@publisle/research 0.0.0-stage → 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 (147) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +31 -0
  3. package/README.md +14 -2
  4. package/compiler/Dockerfile +18 -0
  5. package/compiler/README.md +80 -0
  6. package/compiler/cacert.pem +2950 -0
  7. package/compiler/install-texlive.sh +38 -0
  8. package/compiler/release.json +13 -0
  9. package/dist/article.d.ts +10 -0
  10. package/dist/article.d.ts.map +1 -0
  11. package/dist/article.js +425 -0
  12. package/dist/article.js.map +1 -0
  13. package/dist/bibtex.d.ts +2 -0
  14. package/dist/bibtex.d.ts.map +1 -0
  15. package/dist/bibtex.js +2 -0
  16. package/dist/bibtex.js.map +1 -0
  17. package/dist/citeproc.d.ts +28 -0
  18. package/dist/csl.d.ts +35 -0
  19. package/dist/csl.d.ts.map +1 -0
  20. package/dist/csl.js +315 -0
  21. package/dist/csl.js.map +1 -0
  22. package/dist/index.d.ts +13 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +12 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/jats.d.ts +14 -0
  27. package/dist/jats.d.ts.map +1 -0
  28. package/dist/jats.js +197 -0
  29. package/dist/jats.js.map +1 -0
  30. package/dist/latex.d.ts +5 -0
  31. package/dist/latex.d.ts.map +1 -0
  32. package/dist/latex.js +19 -0
  33. package/dist/latex.js.map +1 -0
  34. package/dist/locales.d.ts +2 -0
  35. package/dist/locales.d.ts.map +1 -0
  36. package/dist/locales.js +67 -0
  37. package/dist/locales.js.map +1 -0
  38. package/dist/node.d.ts +66 -0
  39. package/dist/node.d.ts.map +1 -0
  40. package/dist/node.js +542 -0
  41. package/dist/node.js.map +1 -0
  42. package/dist/package.d.ts +19 -0
  43. package/dist/package.d.ts.map +1 -0
  44. package/dist/package.js +48 -0
  45. package/dist/package.js.map +1 -0
  46. package/dist/pdf.d.ts +10 -0
  47. package/dist/pdf.d.ts.map +1 -0
  48. package/dist/pdf.js +207 -0
  49. package/dist/pdf.js.map +1 -0
  50. package/dist/portable-citations.d.ts +27 -0
  51. package/dist/portable-citations.d.ts.map +1 -0
  52. package/dist/portable-citations.js +185 -0
  53. package/dist/portable-citations.js.map +1 -0
  54. package/dist/profiles.d.ts +10 -0
  55. package/dist/profiles.d.ts.map +1 -0
  56. package/dist/profiles.js +166 -0
  57. package/dist/profiles.js.map +1 -0
  58. package/dist/remote.d.ts +24 -0
  59. package/dist/remote.d.ts.map +1 -0
  60. package/dist/remote.js +139 -0
  61. package/dist/remote.js.map +1 -0
  62. package/dist/resolve.d.ts +10 -0
  63. package/dist/resolve.d.ts.map +1 -0
  64. package/dist/resolve.js +62 -0
  65. package/dist/resolve.js.map +1 -0
  66. package/package.json +53 -3
  67. package/src/article.ts +478 -0
  68. package/src/bibtex.ts +1 -0
  69. package/src/citeproc.d.ts +28 -0
  70. package/src/csl.ts +367 -0
  71. package/src/index.ts +29 -0
  72. package/src/jats.ts +242 -0
  73. package/src/latex.ts +21 -0
  74. package/src/locales.ts +126 -0
  75. package/src/node.ts +752 -0
  76. package/src/package.ts +83 -0
  77. package/src/pdf.ts +225 -0
  78. package/src/portable-citations.ts +250 -0
  79. package/src/profiles.ts +188 -0
  80. package/src/remote.ts +163 -0
  81. package/src/resolve.ts +91 -0
  82. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/BITS-embedded-index2.ent +308 -0
  83. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/BITS-question-answer2.ent +531 -0
  84. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-XHTMLtablesetup1-3.ent +629 -0
  85. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-ali-namespace1-3.ent +293 -0
  86. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-archivearticle1-3.dtd +1085 -0
  87. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-archivecustom-classes1-3.ent +409 -0
  88. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-archivecustom-mixes1-3.ent +601 -0
  89. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-archivecustom-models1-3.ent +1613 -0
  90. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-archivecustom-modules1-3.ent +404 -0
  91. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-articlemeta1-3.ent +2552 -0
  92. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-backmatter1-3.ent +540 -0
  93. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-chars1-3.ent +664 -0
  94. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-common-atts1-3.ent +360 -0
  95. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-common1-3.ent +4659 -0
  96. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-default-classes1-3.ent +1595 -0
  97. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-default-mixes1-3.ent +630 -0
  98. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-display1-3.ent +1299 -0
  99. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-format1-3.ent +997 -0
  100. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-funding1-3.ent +1100 -0
  101. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-journalmeta1-3.ent +593 -0
  102. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-link1-3.ent +604 -0
  103. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-list1-3.ent +663 -0
  104. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-math1-3.ent +563 -0
  105. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-mathmlsetup1-3.ent +491 -0
  106. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-modules1-3.ent +769 -0
  107. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-nlmcitation1-3.ent +428 -0
  108. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-notat1-3.ent +412 -0
  109. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-para1-3.ent +648 -0
  110. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-phrase1-3.ent +664 -0
  111. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-references1-3.ent +1428 -0
  112. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-related-object1-3.ent +435 -0
  113. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-section1-3.ent +463 -0
  114. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/JATS-xmlspecchars1-3.ent +528 -0
  115. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/catalog-jats-v1-3-no-base.xml +999 -0
  116. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/catalog-jats-v1-3-with-base.xml +986 -0
  117. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso8879/isobox.ent +61 -0
  118. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso8879/isocyr1.ent +88 -0
  119. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso8879/isocyr2.ent +47 -0
  120. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso8879/isodia.ent +35 -0
  121. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso8879/isolat1.ent +83 -0
  122. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso8879/isolat2.ent +142 -0
  123. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso8879/isonum.ent +97 -0
  124. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso8879/isopub.ent +105 -0
  125. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso9573-13/isoamsa.ent +167 -0
  126. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso9573-13/isoamsb.ent +143 -0
  127. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso9573-13/isoamsc.ent +43 -0
  128. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso9573-13/isoamsn.ent +114 -0
  129. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso9573-13/isoamso.ent +73 -0
  130. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso9573-13/isoamsr.ent +204 -0
  131. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso9573-13/isogrk3.ent +64 -0
  132. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso9573-13/isomfrk.ent +75 -0
  133. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso9573-13/isomopf.ent +49 -0
  134. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso9573-13/isomscr.ent +75 -0
  135. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/iso9573-13/isotech.ent +182 -0
  136. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/mathml/mmlalias.ent +564 -0
  137. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/mathml/mmlextra.ent +122 -0
  138. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/mathml2-qname-1.mod +286 -0
  139. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/mathml2.dtd +2206 -0
  140. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/xhtml-inlstyle-1.mod +34 -0
  141. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/xhtml-table-1.mod +333 -0
  142. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/xmlchars/isogrk1.ent +70 -0
  143. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/xmlchars/isogrk2.ent +41 -0
  144. package/vendor/jats/JATS-Archiving-1-3-MathML2-DTD/xmlchars/isogrk4.ent +66 -0
  145. package/vendor/jats/PROVENANCE.json +6 -0
  146. package/vendor/locales/PROVENANCE.json +5 -0
  147. package/vendor/locales/README.md +23 -0
package/src/article.ts ADDED
@@ -0,0 +1,478 @@
1
+ import { parseBibtex } from "./bibtex.ts";
2
+ import { resolveReadable } from "@publisle/schema";
3
+ import type { Block, BlockType, Document } from "@publisle/schema";
4
+ import type {
5
+ Article,
6
+ ArticleBlock,
7
+ ArticleInline,
8
+ CitationCluster,
9
+ CitationRef,
10
+ CitationEntry,
11
+ ResearchLoss,
12
+ } from "@publisle/template-sdk";
13
+ export type {
14
+ Article,
15
+ ArticleBlock,
16
+ ArticleInline,
17
+ CitationCluster,
18
+ CitationRef,
19
+ CitationEntry,
20
+ ResearchLoss,
21
+ } from "@publisle/template-sdk";
22
+ function isRecord(value: unknown): value is Record<string, unknown> {
23
+ return typeof value === "object" && value !== null && !Array.isArray(value);
24
+ }
25
+
26
+ function textOf(value: unknown): string | undefined {
27
+ return typeof value === "string" ? value : undefined;
28
+ }
29
+
30
+ function pushLoss(losses: ResearchLoss[], code: string, message: string): void {
31
+ if (losses.some((loss) => loss.code === code)) return;
32
+ losses.push({ code, message });
33
+ }
34
+
35
+ export function readEntries(document: Document<Block<BlockType, unknown>>): {
36
+ readonly entries: readonly CitationEntry[];
37
+ readonly losses: readonly ResearchLoss[];
38
+ } {
39
+ const entries: CitationEntry[] = [];
40
+ const seen = new Set<string>();
41
+ const losses: ResearchLoss[] = [];
42
+ for (const block of document.blocks) {
43
+ if (block.type !== "publisle:bibliography" || !isRecord(block.data))
44
+ continue;
45
+ const listed = block.data["entries"];
46
+ if (!Array.isArray(listed)) continue;
47
+ for (const original of listed) {
48
+ let entry: unknown = original;
49
+ if (
50
+ isRecord(entry) &&
51
+ typeof entry["raw"] === "string" &&
52
+ !entry["title"] &&
53
+ !entry["authors"] &&
54
+ !entry["issued"] &&
55
+ entry["raw"].trimStart().startsWith("@")
56
+ ) {
57
+ try {
58
+ const normalized = parseBibtex(entry["raw"])[0];
59
+ if (normalized) entry = { ...normalized, ...entry };
60
+ } catch {
61
+ pushLoss(
62
+ losses,
63
+ "opaque-reference-literal",
64
+ "Unparsed bibliography source is preserved literally.",
65
+ );
66
+ }
67
+ }
68
+ if (
69
+ !isRecord(entry) ||
70
+ typeof entry["id"] !== "string" ||
71
+ entry["id"].length === 0
72
+ )
73
+ continue;
74
+ const id = entry["id"];
75
+ if (seen.has(id)) {
76
+ pushLoss(
77
+ losses,
78
+ "duplicate-bibliography-entry",
79
+ `Bibliography id ${id} is repeated. Resolution uses the first entry.`,
80
+ );
81
+ continue;
82
+ }
83
+ seen.add(id);
84
+ const authors = Array.isArray(entry["authors"])
85
+ ? entry["authors"].filter(
86
+ (author): author is string => typeof author === "string",
87
+ )
88
+ : [];
89
+ const type = textOf(entry["type"]);
90
+ const title = textOf(entry["title"]);
91
+ const issued = textOf(entry["issued"]);
92
+ const containerTitle = textOf(entry["containerTitle"]);
93
+ const volume = textOf(entry["volume"]);
94
+ const issue = textOf(entry["issue"]);
95
+ const page = textOf(entry["page"]);
96
+ const publisher = textOf(entry["publisher"]);
97
+ const doi = textOf(entry["doi"]);
98
+ const url = textOf(entry["url"]);
99
+ const raw = textOf(entry["raw"]);
100
+ entries.push({
101
+ id,
102
+ authors,
103
+ ...(type === undefined ? {} : { type }),
104
+ ...(title === undefined ? {} : { title }),
105
+ ...(issued === undefined ? {} : { issued }),
106
+ ...(containerTitle === undefined ? {} : { containerTitle }),
107
+ ...(volume === undefined ? {} : { volume }),
108
+ ...(issue === undefined ? {} : { issue }),
109
+ ...(page === undefined ? {} : { page }),
110
+ ...(publisher === undefined ? {} : { publisher }),
111
+ ...(doi === undefined ? {} : { doi }),
112
+ ...(url === undefined ? {} : { url }),
113
+ ...(raw === undefined ? {} : { raw }),
114
+ });
115
+ }
116
+ }
117
+ return { entries, losses };
118
+ }
119
+
120
+ function plainText(inlines: readonly ArticleInline[]): string {
121
+ return inlines
122
+ .map((inline) =>
123
+ inline.type === "text" || inline.type === "link" ? inline.value : "",
124
+ )
125
+ .join("");
126
+ }
127
+
128
+ function citationCluster(
129
+ value: Record<string, unknown>,
130
+ ): CitationCluster | undefined {
131
+ const items = value["items"];
132
+ if (!Array.isArray(items)) return undefined;
133
+ const refs: CitationRef[] = [];
134
+ for (const item of items) {
135
+ if (!isRecord(item) || typeof item["id"] !== "string") continue;
136
+ const locator = textOf(item["locator"]);
137
+ const label = textOf(item["label"]);
138
+ refs.push({
139
+ id: item["id"],
140
+ ...(locator === undefined ? {} : { locator }),
141
+ ...(label === undefined ? {} : { label }),
142
+ ...(item["suppressAuthor"] === true ? { suppressAuthor: true } : {}),
143
+ });
144
+ }
145
+ if (refs.length === 0) return undefined;
146
+ const prefix = textOf(value["prefix"]);
147
+ const suffix = textOf(value["suffix"]);
148
+ return {
149
+ items: refs,
150
+ ...(prefix === undefined ? {} : { prefix }),
151
+ ...(suffix === undefined ? {} : { suffix }),
152
+ };
153
+ }
154
+
155
+ function inlineList(value: unknown, losses: ResearchLoss[]): ArticleInline[] {
156
+ if (!Array.isArray(value)) return [];
157
+ const result: ArticleInline[] = [];
158
+ for (const node of value) {
159
+ if (!isRecord(node) || typeof node["type"] !== "string") continue;
160
+ const type = node["type"];
161
+ if (type === "text" && typeof node["value"] === "string") {
162
+ const direction = node["direction"];
163
+ result.push({
164
+ type: "text",
165
+ value: node["value"],
166
+ ...(direction === "ltr" || direction === "rtl" || direction === "auto"
167
+ ? { direction }
168
+ : {}),
169
+ });
170
+ } else if (
171
+ (type === "emphasis" || type === "strong" || type === "strikethrough") &&
172
+ Array.isArray(node["children"])
173
+ ) {
174
+ const mark = type === "strikethrough" ? "strike" : type;
175
+ for (const child of inlineList(node["children"], losses)) {
176
+ if (child.type === "text") result.push({ ...child, mark });
177
+ else result.push(child);
178
+ }
179
+ } else if (type === "inlineCode" && typeof node["value"] === "string") {
180
+ result.push({ type: "text", value: node["value"], mark: "code" });
181
+ } else if (type === "inlineMath" && typeof node["value"] === "string") {
182
+ result.push({ type: "math", value: node["value"] });
183
+ } else if (type === "link" && typeof node["url"] === "string") {
184
+ const label = plainText(inlineList(node["children"], losses));
185
+ result.push({
186
+ type: "link",
187
+ url: node["url"],
188
+ value: label || node["url"],
189
+ });
190
+ } else if (type === "inlineImage") {
191
+ result.push({
192
+ type: "text",
193
+ value: typeof node["alt"] === "string" ? node["alt"] : "",
194
+ });
195
+ } else if (type === "hardBreak" || type === "softBreak") {
196
+ result.push({ type: "break" });
197
+ } else if (type === "citationReference") {
198
+ const cluster = citationCluster(node);
199
+ if (cluster) result.push({ type: "citation", cluster });
200
+ } else if (
201
+ type === "footnoteReference" &&
202
+ typeof node["identifier"] === "string"
203
+ ) {
204
+ result.push({
205
+ type: "text",
206
+ value: node["identifier"],
207
+ mark: "code",
208
+ reference: { kind: "footnote", target: node["identifier"] },
209
+ });
210
+ } else if (type === "crossReference") {
211
+ const label = plainText(inlineList(node["children"], losses));
212
+ result.push({
213
+ type: "text",
214
+ reference: { kind: "cross", target: textOf(node["target"]) ?? "" },
215
+ value:
216
+ label || (typeof node["target"] === "string" ? node["target"] : ""),
217
+ });
218
+ } else if (type === "rawHtml" && typeof node["value"] === "string") {
219
+ pushLoss(
220
+ losses,
221
+ "raw-html-uninterpreted",
222
+ "Raw HTML is kept as text. Tags are not interpreted.",
223
+ );
224
+ result.push({
225
+ type: "text",
226
+ value: node["value"].replace(/<[^>]*>/gu, ""),
227
+ });
228
+ }
229
+ }
230
+ return result;
231
+ }
232
+
233
+ function flowParagraphs(
234
+ value: unknown,
235
+ losses: ResearchLoss[],
236
+ ): ArticleInline[][] {
237
+ if (!Array.isArray(value)) return [];
238
+ const paragraphs: ArticleInline[][] = [];
239
+ for (const node of value) {
240
+ if (!isRecord(node)) continue;
241
+ if (node["type"] === "paragraph")
242
+ paragraphs.push(inlineList(node["content"], losses));
243
+ else if (node["type"] === "list") {
244
+ pushLoss(
245
+ losses,
246
+ "nested-list-flattened",
247
+ "A nested list is written as following paragraphs.",
248
+ );
249
+ for (const item of Array.isArray(node["items"]) ? node["items"] : []) {
250
+ if (!isRecord(item)) continue;
251
+ paragraphs.push(...flowParagraphs(item["children"], losses));
252
+ }
253
+ } else {
254
+ paragraphs.push(...flowParagraphs(node["children"], losses));
255
+ }
256
+ }
257
+ return paragraphs;
258
+ }
259
+
260
+ export function projectArticle(
261
+ document: Document<Block<BlockType, unknown>>,
262
+ ): Article {
263
+ const losses: ResearchLoss[] = [];
264
+ const read = readEntries(document);
265
+ losses.push(...read.losses);
266
+ const blocks: ArticleBlock[] = [];
267
+ let abstract = false;
268
+ const authors = (document.metadata?.authors ?? []).map(
269
+ (author) => author.name,
270
+ );
271
+ for (const block of document.blocks) {
272
+ const data = isRecord(block.data) ? block.data : {};
273
+ const labeled =
274
+ typeof data["label"] === "string" ? { label: data["label"] } : {};
275
+ if (block.type === "publisle:heading") {
276
+ const role = data["role"];
277
+ abstract = role === "abstract";
278
+ const level = typeof data["level"] === "number" ? data["level"] : 1;
279
+ blocks.push({
280
+ kind: "heading",
281
+ ...labeled,
282
+ level,
283
+ inlines: inlineList(data["content"], losses),
284
+ abstract,
285
+ });
286
+ continue;
287
+ }
288
+ if (block.type === "publisle:paragraph") {
289
+ blocks.push({
290
+ kind: "paragraph",
291
+ inlines: inlineList(data["content"], losses),
292
+ abstract,
293
+ });
294
+ continue;
295
+ }
296
+ if (block.type === "publisle:list" && Array.isArray(data["items"])) {
297
+ blocks.push({
298
+ kind: "list",
299
+ ordered: data["ordered"] === true,
300
+ items: data["items"].map((item: unknown) => {
301
+ const children = isRecord(item) ? item["children"] : [];
302
+ return flowParagraphs(children, losses).flat();
303
+ }),
304
+ abstract,
305
+ });
306
+ continue;
307
+ }
308
+ if (block.type === "publisle:quote") {
309
+ blocks.push({
310
+ kind: "quote",
311
+ paragraphs: flowParagraphs(data["children"], losses),
312
+ abstract,
313
+ });
314
+ continue;
315
+ }
316
+ if (block.type === "publisle:code" && typeof data["value"] === "string") {
317
+ blocks.push({ kind: "code", value: data["value"], abstract });
318
+ continue;
319
+ }
320
+ if (block.type === "publisle:math" && typeof data["value"] === "string") {
321
+ blocks.push({
322
+ kind: "math",
323
+ ...labeled,
324
+ value: data["value"],
325
+ display: data["display"] === true,
326
+ abstract,
327
+ });
328
+ continue;
329
+ }
330
+ if (block.type === "publisle:table" && Array.isArray(data["rows"])) {
331
+ blocks.push({
332
+ kind: "table",
333
+ ...labeled,
334
+ caption: flowParagraphs(data["caption"], losses).flat(),
335
+ headerRows:
336
+ typeof data["headerRows"] === "number" ? data["headerRows"] : 1,
337
+ rows: data["rows"].map((row: unknown) =>
338
+ Array.isArray(row) ? row.map((cell) => inlineList(cell, losses)) : [],
339
+ ),
340
+ abstract,
341
+ });
342
+ continue;
343
+ }
344
+ if (
345
+ block.type === "publisle:footnote" &&
346
+ typeof data["identifier"] === "string"
347
+ ) {
348
+ blocks.push({
349
+ kind: "footnote",
350
+ id: data["identifier"],
351
+ paragraphs: flowParagraphs(data["children"], losses),
352
+ });
353
+ continue;
354
+ }
355
+ if (block.type === "publisle:bibliography") {
356
+ blocks.push({ kind: "bibliography" });
357
+ continue;
358
+ }
359
+ if (block.type === "publisle:figure") {
360
+ blocks.push({
361
+ kind: "figure",
362
+ src: textOf(data["src"]) ?? "",
363
+ alt: textOf(data["alt"]) ?? "",
364
+ caption: flowParagraphs(data["caption"], losses).flat(),
365
+ abstract,
366
+ ...labeled,
367
+ });
368
+ continue;
369
+ }
370
+ if (block.type === "publisle:callout") {
371
+ for (const paragraph of flowParagraphs(data["children"], losses)) {
372
+ blocks.push({ kind: "paragraph", inlines: paragraph, abstract });
373
+ }
374
+ continue;
375
+ }
376
+ if (
377
+ block.type === "publisle:embed" ||
378
+ block.type === "publisle:diagram" ||
379
+ block.type === "publisle:raw-html"
380
+ ) {
381
+ pushLoss(
382
+ losses,
383
+ "research-block-reduced",
384
+ "Embeds, diagrams, and raw HTML contribute their readable text. Their behavior is not executed.",
385
+ );
386
+ const fallback = flowParagraphs(data["fallback"], losses).flat();
387
+ const alt = textOf(data["alt"]) ?? textOf(data["title"]);
388
+ const source = textOf(data["value"]);
389
+ const inlines: ArticleInline[] = [];
390
+ if (alt) inlines.push({ type: "text", value: alt });
391
+ if (fallback.length && inlines.length)
392
+ inlines.push({ type: "text", value: " " });
393
+ inlines.push(...fallback);
394
+ if (source) {
395
+ if (inlines.length) inlines.push({ type: "text", value: " " });
396
+ inlines.push({
397
+ type: "text",
398
+ value: source.replace(/<[^>]*>/gu, ""),
399
+ });
400
+ }
401
+ blocks.push({
402
+ kind: "paragraph",
403
+ inlines,
404
+ abstract,
405
+ });
406
+ } else {
407
+ pushLoss(
408
+ losses,
409
+ "research-block-fallback",
410
+ `Unsupported research block ${block.type} uses readable fallback.`,
411
+ );
412
+ const readable = block.readable
413
+ ? resolveReadable(block.readable, block.data)
414
+ : undefined;
415
+ const content =
416
+ readable ?? (isRecord(data["content"]) ? data["content"] : {});
417
+ const paragraphs = flowParagraphs(
418
+ data["fallback"] ?? content.fallback,
419
+ losses,
420
+ );
421
+ if (content.title) paragraphs.unshift(inlineList(content.title, losses));
422
+ for (const paragraph of paragraphs)
423
+ blocks.push({ kind: "paragraph", inlines: paragraph, abstract });
424
+ }
425
+ }
426
+ const title = document.metadata?.title;
427
+ return {
428
+ authors,
429
+ authorDetails: document.metadata?.authors ?? [],
430
+ subjects: document.metadata?.subjects ?? [],
431
+ ...(document.metadata?.language === undefined
432
+ ? {}
433
+ : { language: document.metadata.language }),
434
+ ...(document.metadata?.direction === undefined
435
+ ? {}
436
+ : { direction: document.metadata.direction }),
437
+ blocks,
438
+ entries: read.entries,
439
+ losses,
440
+ ...(title === undefined ? {} : { title }),
441
+ };
442
+ }
443
+
444
+ export function citationClusters(article: Article): CitationCluster[] {
445
+ const clusters: CitationCluster[] = [];
446
+ const notes = new Map(
447
+ article.blocks
448
+ .filter((block) => block.kind === "footnote")
449
+ .map((block) => [block.id, block.paragraphs]),
450
+ );
451
+ const visited = new Set<string>();
452
+ const visit = (inlines: readonly ArticleInline[]) => {
453
+ for (const inline of inlines) {
454
+ if (inline.type === "citation") clusters.push(inline.cluster);
455
+ if (
456
+ inline.type === "text" &&
457
+ inline.reference?.kind === "footnote" &&
458
+ !visited.has(inline.reference.target)
459
+ ) {
460
+ visited.add(inline.reference.target);
461
+ for (const paragraph of notes.get(inline.reference.target) ?? [])
462
+ visit(paragraph);
463
+ }
464
+ }
465
+ };
466
+ for (const block of article.blocks) {
467
+ if (block.kind === "heading" || block.kind === "paragraph")
468
+ visit(block.inlines);
469
+ else if (block.kind === "list") for (const item of block.items) visit(item);
470
+ else if (block.kind === "quote")
471
+ for (const paragraph of block.paragraphs) visit(paragraph);
472
+ else if (block.kind === "table") {
473
+ if (block.caption) visit(block.caption);
474
+ for (const row of block.rows) for (const cell of row) visit(cell);
475
+ } else if (block.kind === "figure") visit(block.caption);
476
+ }
477
+ return clusters;
478
+ }
package/src/bibtex.ts ADDED
@@ -0,0 +1 @@
1
+ export { parseBibtex, toBibtex, toCslJson } from "@publisle/template-sdk";
@@ -0,0 +1,28 @@
1
+ declare module "citeproc" {
2
+ interface Citation {
3
+ citationID: string;
4
+ citationItems: Record<string, unknown>[];
5
+ properties: { noteIndex: number };
6
+ }
7
+ interface Processor {
8
+ setOutputFormat(format: string): void;
9
+ processCitationCluster(
10
+ citation: Citation,
11
+ pre: [string, number][],
12
+ post: [string, number][],
13
+ ): [unknown, [number, string, string][]];
14
+ makeBibliography(): false | [{ entry_ids: string[][] }, string[]];
15
+ }
16
+ const CSL: {
17
+ Engine: new (
18
+ system: {
19
+ retrieveLocale: (language: string) => string;
20
+ retrieveItem: (id: string) => Readonly<Record<string, unknown>>;
21
+ },
22
+ style: string,
23
+ locale: string,
24
+ forceLocale?: boolean,
25
+ ) => Processor;
26
+ };
27
+ export default CSL;
28
+ }