@fiduswriter/books-document 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +24 -0
  3. package/dist/exporter/bits/index.d.ts +42 -0
  4. package/dist/exporter/bits/index.d.ts.map +1 -0
  5. package/dist/exporter/bits/index.js +98 -0
  6. package/dist/exporter/bits/index.js.map +1 -0
  7. package/dist/exporter/bits/templates.d.ts +7 -0
  8. package/dist/exporter/bits/templates.d.ts.map +1 -0
  9. package/dist/exporter/bits/templates.js +78 -0
  10. package/dist/exporter/bits/templates.js.map +1 -0
  11. package/dist/exporter/docx/index.d.ts +27 -0
  12. package/dist/exporter/docx/index.d.ts.map +1 -0
  13. package/dist/exporter/docx/index.js +129 -0
  14. package/dist/exporter/docx/index.js.map +1 -0
  15. package/dist/exporter/docx/render.d.ts +14 -0
  16. package/dist/exporter/docx/render.d.ts.map +1 -0
  17. package/dist/exporter/docx/render.js +116 -0
  18. package/dist/exporter/docx/render.js.map +1 -0
  19. package/dist/exporter/epub/index.d.ts +65 -0
  20. package/dist/exporter/epub/index.d.ts.map +1 -0
  21. package/dist/exporter/epub/index.js +268 -0
  22. package/dist/exporter/epub/index.js.map +1 -0
  23. package/dist/exporter/epub/templates.d.ts +65 -0
  24. package/dist/exporter/epub/templates.d.ts.map +1 -0
  25. package/dist/exporter/epub/templates.js +223 -0
  26. package/dist/exporter/epub/templates.js.map +1 -0
  27. package/dist/exporter/epub/tools.d.ts +5 -0
  28. package/dist/exporter/epub/tools.d.ts.map +1 -0
  29. package/dist/exporter/epub/tools.js +59 -0
  30. package/dist/exporter/epub/tools.js.map +1 -0
  31. package/dist/exporter/html/index.d.ts +67 -0
  32. package/dist/exporter/html/index.d.ts.map +1 -0
  33. package/dist/exporter/html/index.js +300 -0
  34. package/dist/exporter/html/index.js.map +1 -0
  35. package/dist/exporter/html/templates.d.ts +40 -0
  36. package/dist/exporter/html/templates.d.ts.map +1 -0
  37. package/dist/exporter/html/templates.js +226 -0
  38. package/dist/exporter/html/templates.js.map +1 -0
  39. package/dist/exporter/html/tools.d.ts +2 -0
  40. package/dist/exporter/html/tools.d.ts.map +1 -0
  41. package/dist/exporter/html/tools.js +23 -0
  42. package/dist/exporter/html/tools.js.map +1 -0
  43. package/dist/exporter/latex/index.d.ts +32 -0
  44. package/dist/exporter/latex/index.d.ts.map +1 -0
  45. package/dist/exporter/latex/index.js +102 -0
  46. package/dist/exporter/latex/index.js.map +1 -0
  47. package/dist/exporter/latex/templates.d.ts +4 -0
  48. package/dist/exporter/latex/templates.d.ts.map +1 -0
  49. package/dist/exporter/latex/templates.js +26 -0
  50. package/dist/exporter/latex/templates.js.map +1 -0
  51. package/dist/exporter/native/index.d.ts +20 -0
  52. package/dist/exporter/native/index.d.ts.map +1 -0
  53. package/dist/exporter/native/index.js +109 -0
  54. package/dist/exporter/native/index.js.map +1 -0
  55. package/dist/exporter/odt/index.d.ts +27 -0
  56. package/dist/exporter/odt/index.d.ts.map +1 -0
  57. package/dist/exporter/odt/index.js +123 -0
  58. package/dist/exporter/odt/index.js.map +1 -0
  59. package/dist/exporter/odt/render.d.ts +15 -0
  60. package/dist/exporter/odt/render.d.ts.map +1 -0
  61. package/dist/exporter/odt/render.js +119 -0
  62. package/dist/exporter/odt/render.js.map +1 -0
  63. package/dist/exporter/print/index.d.ts +24 -0
  64. package/dist/exporter/print/index.d.ts.map +1 -0
  65. package/dist/exporter/print/index.js +60 -0
  66. package/dist/exporter/print/index.js.map +1 -0
  67. package/dist/exporter/print/templates.d.ts +2 -0
  68. package/dist/exporter/print/templates.d.ts.map +1 -0
  69. package/dist/exporter/print/templates.js +5 -0
  70. package/dist/exporter/print/templates.js.map +1 -0
  71. package/dist/exporter/tools.d.ts +29 -0
  72. package/dist/exporter/tools.d.ts.map +1 -0
  73. package/dist/exporter/tools.js +151 -0
  74. package/dist/exporter/tools.js.map +1 -0
  75. package/dist/i18n.d.ts +7 -0
  76. package/dist/i18n.d.ts.map +1 -0
  77. package/dist/i18n.js +918 -0
  78. package/dist/i18n.js.map +1 -0
  79. package/dist/importer/native/index.d.ts +34 -0
  80. package/dist/importer/native/index.d.ts.map +1 -0
  81. package/dist/importer/native/index.js +152 -0
  82. package/dist/importer/native/index.js.map +1 -0
  83. package/dist/importer/native/reader.d.ts +26 -0
  84. package/dist/importer/native/reader.d.ts.map +1 -0
  85. package/dist/importer/native/reader.js +105 -0
  86. package/dist/importer/native/reader.js.map +1 -0
  87. package/dist/index.d.ts +3 -0
  88. package/dist/index.d.ts.map +1 -0
  89. package/dist/index.js +2 -0
  90. package/dist/index.js.map +1 -0
  91. package/dist/schema/index.d.ts +4 -0
  92. package/dist/schema/index.d.ts.map +1 -0
  93. package/dist/schema/index.js +4 -0
  94. package/dist/schema/index.js.map +1 -0
  95. package/dist/types.d.ts +151 -0
  96. package/dist/types.d.ts.map +1 -0
  97. package/dist/types.js +5 -0
  98. package/dist/types.js.map +1 -0
  99. package/package.json +101 -0
  100. package/src/exporter/bits/index.ts +163 -0
  101. package/src/exporter/bits/templates.ts +107 -0
  102. package/src/exporter/docx/index.ts +239 -0
  103. package/src/exporter/docx/render.ts +142 -0
  104. package/src/exporter/epub/index.ts +426 -0
  105. package/src/exporter/epub/templates.ts +401 -0
  106. package/src/exporter/epub/tools.ts +68 -0
  107. package/src/exporter/html/index.ts +443 -0
  108. package/src/exporter/html/templates.ts +363 -0
  109. package/src/exporter/html/tools.ts +26 -0
  110. package/src/exporter/latex/index.ts +161 -0
  111. package/src/exporter/latex/templates.ts +35 -0
  112. package/src/exporter/native/index.ts +187 -0
  113. package/src/exporter/odt/index.ts +211 -0
  114. package/src/exporter/odt/render.ts +146 -0
  115. package/src/exporter/print/index.ts +92 -0
  116. package/src/exporter/print/templates.ts +8 -0
  117. package/src/exporter/tools.ts +233 -0
  118. package/src/global.d.ts +2 -0
  119. package/src/i18n.ts +936 -0
  120. package/src/importer/native/index.ts +233 -0
  121. package/src/importer/native/reader.ts +165 -0
  122. package/src/index.ts +19 -0
  123. package/src/modules.d.ts +11 -0
  124. package/src/schema/index.ts +4 -0
  125. package/src/types.ts +188 -0
@@ -0,0 +1,233 @@
1
+ /**
2
+ * Native `.fidusbook` importer.
3
+ *
4
+ * Uses `NativeImporter` from `@fiduswriter/document/importer/native` for each
5
+ * chapter and delegates creation of the book record to a `BookImporterBackend`.
6
+ */
7
+
8
+ import type {NativeImporterBackend} from "@fiduswriter/document"
9
+ import {NativeImporter} from "@fiduswriter/document/importer/native"
10
+ import {addAlert} from "fwtoolkit"
11
+
12
+ import type {BookImporterBackend, Chapter, User} from "../../types.js"
13
+ import {readFidusBookFile} from "./reader.js"
14
+
15
+ export {FIDUSBOOK_VERSION} from "./reader.js"
16
+ export {readFidusBookFile, FidusBookReader} from "./reader.js"
17
+
18
+ export class NativeBookImporter {
19
+ file: Blob
20
+ user: User
21
+ path: string
22
+ nativeBackend: NativeImporterBackend
23
+ bookBackend: BookImporterBackend
24
+
25
+ ok: boolean
26
+ statusText: string
27
+ bookId: number | null
28
+
29
+ constructor(
30
+ file: Blob,
31
+ user: User,
32
+ nativeBackend: NativeImporterBackend,
33
+ bookBackend: BookImporterBackend,
34
+ path = "/"
35
+ ) {
36
+ this.file = file
37
+ this.user = user
38
+ this.path = path.endsWith("/") ? path : path + "/"
39
+ this.nativeBackend = nativeBackend
40
+ this.bookBackend = bookBackend
41
+
42
+ this.ok = false
43
+ this.statusText = ""
44
+ this.bookId = null
45
+ }
46
+
47
+ /**
48
+ * Entry point. Validates the file is a ZIP, then delegates to the reader.
49
+ */
50
+ init(): Promise<NativeBookImporter> {
51
+ return new Promise(resolve => {
52
+ const reader = new FileReader()
53
+ reader.onloadend = () => {
54
+ if (
55
+ reader.result &&
56
+ (reader.result as string).length > 60 &&
57
+ (reader.result as string).substring(0, 2) === "PK"
58
+ ) {
59
+ this.readZip().then(() => resolve(this))
60
+ } else {
61
+ this.statusText = gettext(
62
+ "The uploaded file does not appear to be a Fidusbook file."
63
+ )
64
+ resolve(this)
65
+ }
66
+ }
67
+ reader.readAsText(this.file.slice(0, 64))
68
+ })
69
+ }
70
+
71
+ async readZip(): Promise<void> {
72
+ let textFiles: Array<{filename: string; content: string}> = []
73
+ let binaryFiles: Array<{filename: string; content: Blob}> = []
74
+
75
+ try {
76
+ const JSZip = (await import("jszip")).default
77
+ const zipfs = await JSZip.loadAsync(this.file)
78
+
79
+ const filenames: string[] = []
80
+ zipfs.forEach(filename => filenames.push(filename))
81
+
82
+ await Promise.all(
83
+ filenames
84
+ .filter(f => !f.endsWith("/"))
85
+ .map(async filename => {
86
+ const isText =
87
+ filename.endsWith(".json") ||
88
+ filename === "filetype-version" ||
89
+ filename === "mimetype"
90
+ const content = await zipfs.files[filename].async(
91
+ isText ? "string" : "blob"
92
+ )
93
+ if (isText) {
94
+ textFiles.push({
95
+ filename,
96
+ content: content as string
97
+ })
98
+ } else {
99
+ binaryFiles.push({
100
+ filename,
101
+ content: content as Blob
102
+ })
103
+ }
104
+ })
105
+ )
106
+ } catch (error) {
107
+ this.statusText = gettext(
108
+ "The uploaded file does not appear to be a Fidusbook file."
109
+ )
110
+ return
111
+ }
112
+
113
+ return this.processFidusbookFile(textFiles, binaryFiles)
114
+ }
115
+
116
+ async processFidusbookFile(
117
+ textFiles: Array<{filename: string; content: string}>,
118
+ binaryFiles: Array<{filename: string; content: Blob}>
119
+ ): Promise<void> {
120
+ const versionEntry = textFiles.find(
121
+ f => f.filename === "filetype-version"
122
+ )
123
+ const filetypeVersion = Number.parseFloat(versionEntry?.content || "")
124
+
125
+ if (
126
+ filetypeVersion < 1.0 ||
127
+ filetypeVersion > 1.0
128
+ ) {
129
+ this.statusText =
130
+ gettext(
131
+ "The Fidusbook file version is not supported by this server: "
132
+ ) + (versionEntry?.content || "")
133
+ return
134
+ }
135
+
136
+ const mimetypeEntry = textFiles.find(f => f.filename === "mimetype")
137
+ if (
138
+ mimetypeEntry &&
139
+ mimetypeEntry.content !== "application/fidusbook+zip"
140
+ ) {
141
+ this.statusText = gettext(
142
+ "The uploaded file does not appear to be a Fidusbook file."
143
+ )
144
+ return
145
+ }
146
+
147
+ const bookData = JSON.parse(
148
+ textFiles.find(f => f.filename === "book.json")?.content || "{}"
149
+ ) as Record<string, any>
150
+
151
+ const sortedChapters = [...(bookData.chapters as Array<{chapter_index: number; number: number; part?: string}>)].sort(
152
+ (a, b) => a.chapter_index - b.chapter_index
153
+ )
154
+
155
+ const importedDocIds: Record<number, number> = {}
156
+
157
+ for (const chapter of sortedChapters) {
158
+ const ci = chapter.chapter_index
159
+
160
+ const docFile = textFiles.find(
161
+ f => f.filename === `chapters/${ci}/document.json`
162
+ )
163
+ const imagesFile = textFiles.find(
164
+ f => f.filename === `chapters/${ci}/images.json`
165
+ )
166
+ const bibFile = textFiles.find(
167
+ f => f.filename === `chapters/${ci}/bibliography.json`
168
+ )
169
+
170
+ if (!docFile || !imagesFile || !bibFile) {
171
+ addAlert(
172
+ "error",
173
+ gettext("Fidusbook file is missing data for chapter ") +
174
+ (sortedChapters.indexOf(chapter) + 1)
175
+ )
176
+ throw new Error(`Missing chapter data for index ${ci}`)
177
+ }
178
+
179
+ const docJson = JSON.parse(docFile.content) as Record<string, any>
180
+ const imagesJson = JSON.parse(imagesFile.content) as Record<string, any>
181
+ const bibJson = JSON.parse(bibFile.content) as Record<string, any>
182
+
183
+ const chapterPrefix = `chapters/${ci}/images/`
184
+ const chapterOtherFiles = binaryFiles
185
+ .filter(f => f.filename.startsWith(chapterPrefix))
186
+ .map(f => ({
187
+ filename: `images/${f.filename.slice(chapterPrefix.length)}`,
188
+ content: f.content
189
+ }))
190
+
191
+ const safeBookTitle = (bookData.title as string) || "Untitled"
192
+ const chapterPath = `${this.path}${safeBookTitle}/${(docJson.title as string) || "Untitled"}`
193
+
194
+ const importer = new NativeImporter(
195
+ docJson,
196
+ bibJson,
197
+ {db: imagesJson},
198
+ chapterOtherFiles,
199
+ this.user,
200
+ this.nativeBackend,
201
+ {
202
+ requestedPath: chapterPath
203
+ }
204
+ )
205
+
206
+ let doc
207
+ try {
208
+ ;({doc} = await importer.init())
209
+ } catch (error) {
210
+ addAlert(
211
+ "error",
212
+ gettext("Could not import chapter ") +
213
+ ((docJson.title as string) ||
214
+ sortedChapters.indexOf(chapter) + 1)
215
+ )
216
+ throw error
217
+ }
218
+
219
+ importedDocIds[ci] = doc.id as number
220
+ }
221
+
222
+ const chapters: Chapter[] = sortedChapters.map(chapter => ({
223
+ text: importedDocIds[chapter.chapter_index],
224
+ number: chapter.number,
225
+ part: chapter.part || ""
226
+ }))
227
+
228
+ const book = await this.bookBackend.createBook(bookData as any, chapters, false)
229
+ this.bookId = book.id || null
230
+ this.ok = true
231
+ this.statusText = `"${bookData.title}" ${gettext("successfully imported.")}`
232
+ }
233
+ }
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Pure reader for `.fidusbook` archives.
3
+ *
4
+ * Reads the ZIP file, validates the mimetype/version, and returns the raw
5
+ * book metadata plus a `documentList` shaped like the one used by the
6
+ * exporters. No server interaction.
7
+ */
8
+
9
+ import type {DocumentListEntry} from "../../types.js"
10
+
11
+ export const FIDUSBOOK_VERSION = "1.0"
12
+ export const MIN_FIDUSBOOK_VERSION = 1.0
13
+ export const MAX_FIDUSBOOK_VERSION = 1.0
14
+
15
+ interface ArchiveFile {
16
+ filename: string
17
+ content: string | Blob
18
+ }
19
+
20
+ export interface FidusBookReaderResult {
21
+ book: Record<string, unknown>
22
+ documentList: DocumentListEntry[]
23
+ }
24
+
25
+ /**
26
+ * Read a `.fidusbook` archive and return its contents.
27
+ *
28
+ * @param file - A File/Blob/ArrayBuffer containing the archive.
29
+ * @returns The parsed book and document list.
30
+ */
31
+ export async function readFidusBookFile(
32
+ file: Blob | ArrayBuffer
33
+ ): Promise<FidusBookReaderResult> {
34
+ const JSZip = (await import("jszip")).default
35
+ const zipfs = await JSZip.loadAsync(file)
36
+
37
+ const filenames: string[] = []
38
+ zipfs.forEach(filename => filenames.push(filename))
39
+
40
+ if (
41
+ !filenames.includes("book.json") ||
42
+ !filenames.includes("filetype-version")
43
+ ) {
44
+ throw new Error("The file does not appear to be a Fidusbook file.")
45
+ }
46
+
47
+ const textFiles: ArchiveFile[] = []
48
+ const binaryFiles: ArchiveFile[] = []
49
+
50
+ await Promise.all(
51
+ filenames
52
+ .filter(f => !f.endsWith("/"))
53
+ .map(async filename => {
54
+ const isText =
55
+ filename.endsWith(".json") ||
56
+ filename === "filetype-version" ||
57
+ filename === "mimetype"
58
+ const content = await zipfs.files[filename].async(
59
+ isText ? "string" : "blob"
60
+ )
61
+ const entry = {filename, content}
62
+ if (isText) {
63
+ textFiles.push(entry as ArchiveFile)
64
+ } else {
65
+ binaryFiles.push(entry as ArchiveFile)
66
+ }
67
+ })
68
+ )
69
+
70
+ const versionEntry = textFiles.find(f => f.filename === "filetype-version")
71
+ const filetypeVersion = Number.parseFloat(versionEntry?.content as string)
72
+
73
+ if (
74
+ filetypeVersion < MIN_FIDUSBOOK_VERSION ||
75
+ filetypeVersion > MAX_FIDUSBOOK_VERSION
76
+ ) {
77
+ throw new Error(
78
+ `The Fidusbook file version is not supported by this reader: ${String(
79
+ versionEntry?.content
80
+ )}`
81
+ )
82
+ }
83
+
84
+ const mimetypeEntry = textFiles.find(f => f.filename === "mimetype")
85
+ if (
86
+ mimetypeEntry &&
87
+ mimetypeEntry.content !== "application/fidusbook+zip"
88
+ ) {
89
+ throw new Error("The file does not appear to be a Fidusbook file.")
90
+ }
91
+
92
+ const bookData = JSON.parse(
93
+ textFiles.find(f => f.filename === "book.json")?.content as string
94
+ ) as Record<string, unknown>
95
+
96
+ const sortedChapters = [...(bookData.chapters as Array<{chapter_index: number}>)].sort(
97
+ (a, b) => a.chapter_index - b.chapter_index
98
+ )
99
+
100
+ const documentList: DocumentListEntry[] = []
101
+
102
+ for (const chapter of sortedChapters) {
103
+ const ci = chapter.chapter_index
104
+
105
+ const docFile = textFiles.find(
106
+ f => f.filename === `chapters/${ci}/document.json`
107
+ )
108
+ const imagesFile = textFiles.find(
109
+ f => f.filename === `chapters/${ci}/images.json`
110
+ )
111
+ const bibFile = textFiles.find(
112
+ f => f.filename === `chapters/${ci}/bibliography.json`
113
+ )
114
+
115
+ if (!docFile || !imagesFile || !bibFile) {
116
+ throw new Error(`Missing chapter data for index ${ci}`)
117
+ }
118
+
119
+ const docJson = JSON.parse(docFile.content as string) as Record<string, unknown>
120
+ const imagesJson = JSON.parse(imagesFile.content as string) as Record<string, {image: string; file?: Blob}>
121
+ const bibJson = JSON.parse(bibFile.content as string) as Record<string, unknown>
122
+
123
+ const chapterPrefix = `chapters/${ci}/images/`
124
+ const chapterImages: Record<string, Blob> = {}
125
+ binaryFiles
126
+ .filter(f => f.filename.startsWith(chapterPrefix))
127
+ .forEach(f => {
128
+ chapterImages[
129
+ `images/${f.filename.slice(chapterPrefix.length)}`
130
+ ] = f.content as Blob
131
+ })
132
+
133
+ Object.entries(imagesJson).forEach(([, image]) => {
134
+ if (chapterImages[image.image]) {
135
+ image.file = chapterImages[image.image]
136
+ }
137
+ })
138
+
139
+ documentList.push({
140
+ id: (docJson.id as number) || 0,
141
+ title: (docJson.title as string) || "",
142
+ path: (docJson.path as string) || "",
143
+ content: (docJson.content || {
144
+ type: "doc",
145
+ content: []
146
+ }) as DocumentListEntry["content"],
147
+ settings: (docJson.settings || {}) as DocumentListEntry["settings"],
148
+ comments: (docJson.comments || {}) as DocumentListEntry["comments"],
149
+ images: imagesJson as DocumentListEntry["images"],
150
+ bibliography: bibJson as DocumentListEntry["bibliography"],
151
+ rawContent: docJson.rawContent as DocumentListEntry["rawContent"],
152
+ e2ee: docJson.e2ee as boolean | undefined,
153
+ e2ee_salt: docJson.e2ee_salt as string | undefined,
154
+ e2ee_iterations: docJson.e2ee_iterations as number | undefined
155
+ } as DocumentListEntry)
156
+ }
157
+
158
+ return {book: bookData, documentList}
159
+ }
160
+
161
+ export class FidusBookReader {
162
+ async read(file: Blob | ArrayBuffer): Promise<FidusBookReaderResult> {
163
+ return readFidusBookFile(file)
164
+ }
165
+ }
package/src/index.ts ADDED
@@ -0,0 +1,19 @@
1
+ export {FIDUSBOOK_VERSION} from "./schema/index.js"
2
+ export type {
3
+ Book,
4
+ BookBaseMetadata,
5
+ BookCoverImage,
6
+ BookExporterOptions,
7
+ BookImporterBackend,
8
+ BookMetadata,
9
+ BookSettings,
10
+ BookStyle,
11
+ BookStyles,
12
+ BookTexTemplateParams,
13
+ BitsTemplateParams,
14
+ Chapter,
15
+ DocumentListEntry,
16
+ E2EEStrategy,
17
+ ChapterLoader,
18
+ FidusBookArchive
19
+ } from "./types.js"
@@ -0,0 +1,11 @@
1
+ declare module "pretty" {
2
+ function pretty(html: string, options?: {ocd?: boolean}): string
3
+ export default pretty
4
+ }
5
+
6
+ declare module "@vivliostyle/print" {
7
+ export function printHTML(
8
+ html: string,
9
+ config?: Record<string, unknown>
10
+ ): void
11
+ }
@@ -0,0 +1,4 @@
1
+ export * from "@fiduswriter/document/schema"
2
+
3
+ /** The current Fidus Writer book archive version. */
4
+ export const FIDUSBOOK_VERSION = "1.0"
package/src/types.ts ADDED
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Core types for the @fiduswriter/books-document package.
3
+ */
4
+
5
+ import type {Schema} from "prosemirror-model"
6
+ import type {
7
+ BibDB,
8
+ CSL,
9
+ ExportDoc,
10
+ ExportMetadata,
11
+ ImageDB,
12
+ User
13
+ } from "@fiduswriter/document"
14
+
15
+ export type {CSL, User} from "@fiduswriter/document"
16
+
17
+ /** A single chapter entry inside a book. */
18
+ export interface Chapter {
19
+ /** Document ID of the chapter. */
20
+ text: number
21
+ /** Display number / order of the chapter. */
22
+ number: number
23
+ /** Optional part title. */
24
+ part?: string
25
+ /** Archive storage index (used by the native importer). */
26
+ chapter_index?: number
27
+ }
28
+
29
+ /** Book metadata block. */
30
+ export interface BookMetadata {
31
+ subtitle?: string
32
+ author?: string
33
+ version?: string
34
+ publisher?: string
35
+ copyright?: string
36
+ description?: string
37
+ isbn?: string
38
+ publication_date?: string
39
+ series_title?: string
40
+ series_position?: string
41
+ keywords?: string
42
+ [key: string]: unknown
43
+ }
44
+
45
+ /** Book settings block. */
46
+ export interface BookSettings {
47
+ language: string
48
+ book_style?: string
49
+ papersize?: string
50
+ [key: string]: unknown
51
+ }
52
+
53
+ /** A book record. */
54
+ export interface Book {
55
+ id?: number
56
+ title: string
57
+ path?: string
58
+ metadata: BookMetadata
59
+ settings: BookSettings
60
+ chapters: Chapter[]
61
+ cover_image?: number
62
+ cover_image_data?: BookCoverImage
63
+ updated?: number
64
+ added?: number
65
+ docx_template?: string
66
+ odt_template?: string
67
+ rights?: string
68
+ [key: string]: unknown
69
+ }
70
+
71
+ /** Cover image description stored in a book. */
72
+ export interface BookCoverImage {
73
+ title?: string
74
+ checksum?: string
75
+ file_type?: string
76
+ image?: string
77
+ [key: string]: unknown
78
+ }
79
+
80
+ /** A single book style with its associated media files. */
81
+ export interface BookStyle {
82
+ slug: string
83
+ contents: string
84
+ bookstylefile_set: Array<[string, string]>
85
+ [key: string]: unknown
86
+ }
87
+
88
+ export type BookStyles = BookStyle[]
89
+
90
+ /** A document entry as held in the book's document list. */
91
+ export interface DocumentListEntry extends ExportDoc {
92
+ /** Raw ProseMirror content (used by DOCX/ODT exporters). */
93
+ rawContent?: Record<string, unknown>
94
+ /** Whether the document is end-to-end encrypted. */
95
+ e2ee?: boolean
96
+ /** Base64-encoded salt for E2EE key derivation. */
97
+ e2ee_salt?: string
98
+ /** PBKDF2 iterations for E2EE key derivation. */
99
+ e2ee_iterations?: number
100
+ images?: ImageDB["db"]
101
+ bibliography?: BibDB["db"]
102
+ }
103
+
104
+ /** Options shared by book exporters. */
105
+ export interface BookExporterOptions {
106
+ /** ProseMirror schema used for E2EE decryption. */
107
+ schema?: Schema
108
+ /** CSL engine provider. */
109
+ csl?: CSL
110
+ /** Book styles data. */
111
+ bookStyles?: BookStyles
112
+ /** Logged-in user. */
113
+ user?: User
114
+ /** Document list entries for the book's chapters. */
115
+ documentList?: DocumentListEntry[]
116
+ /** Last-modified timestamp (seconds since epoch). */
117
+ updated?: number
118
+ /** Whether to produce multiple HTML files (HTML exporter only). */
119
+ multiDoc?: boolean
120
+ /** Use relative URLs for linked assets (HTML exporter only). */
121
+ relativeUrls?: boolean
122
+ }
123
+
124
+ /** Strategy for loading missing chapter data before export. */
125
+ export interface ChapterLoader {
126
+ loadChapters(
127
+ chapterIds: number[],
128
+ documentList: DocumentListEntry[],
129
+ schema?: Schema,
130
+ rawContent?: boolean
131
+ ): Promise<void>
132
+ }
133
+
134
+ /** Strategy for handling end-to-end encrypted chapters. */
135
+ export interface E2EEStrategy {
136
+ ensurePassphraseUnlocked(): Promise<boolean>
137
+ getDocumentPassword(docId: number): Promise<string | null>
138
+ resolvePasswordToKey(
139
+ password: string,
140
+ salt: Uint8Array,
141
+ iterations: number
142
+ ): Promise<CryptoKey>
143
+ decryptObject(encrypted: string, key: CryptoKey): Promise<unknown>
144
+ decryptImageToUrl(encrypted: string, key: CryptoKey): Promise<string>
145
+ storePasswordInSession?(docId: number, password: string): void
146
+ }
147
+
148
+ /** Backend used by NativeBookImporter to create the book record. */
149
+ export interface BookImporterBackend {
150
+ /**
151
+ * Create a book record from the imported book data and chapter documents.
152
+ *
153
+ * @param bookData - The raw book object from book.json.
154
+ * @param chapters - The chapters with their final document IDs.
155
+ * @param coverImageId - ID of the imported cover image, or false.
156
+ * @returns A promise resolving to the created book record.
157
+ */
158
+ createBook(
159
+ bookData: Record<string, unknown>,
160
+ chapters: Chapter[],
161
+ coverImageId: number | false
162
+ ): Promise<Book>
163
+ }
164
+
165
+ /** Raw archive content returned by FidusBookReader. */
166
+ export interface FidusBookArchive {
167
+ book: Record<string, unknown>
168
+ documentList: DocumentListEntry[]
169
+ }
170
+
171
+ /** Parameters passed to the book.tex template. */
172
+ export interface BookTexTemplateParams {
173
+ book: Book
174
+ preamble: string
175
+ epilogue: string
176
+ }
177
+
178
+ /** Parameters passed to the BITS book template. */
179
+ export interface BitsTemplateParams {
180
+ front: string
181
+ body: string
182
+ back: string
183
+ }
184
+
185
+ /** Base metadata used by DOCX/ODT book exporters. */
186
+ export interface BookBaseMetadata extends ExportMetadata {
187
+ language: string
188
+ }