@aruzone/aze-forge 0.1.0

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 (133) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +408 -0
  3. package/dist/acceptance.d.ts +48 -0
  4. package/dist/acceptance.js +180 -0
  5. package/dist/acceptance.js.map +1 -0
  6. package/dist/assets.d.ts +19 -0
  7. package/dist/assets.js +509 -0
  8. package/dist/assets.js.map +1 -0
  9. package/dist/atomic-write.d.ts +1 -0
  10. package/dist/atomic-write.js +23 -0
  11. package/dist/atomic-write.js.map +1 -0
  12. package/dist/callout.d.ts +27 -0
  13. package/dist/callout.js +78 -0
  14. package/dist/callout.js.map +1 -0
  15. package/dist/capabilities-json.d.ts +4 -0
  16. package/dist/capabilities-json.js +225 -0
  17. package/dist/capabilities-json.js.map +1 -0
  18. package/dist/capabilities.d.ts +110 -0
  19. package/dist/capabilities.js +270 -0
  20. package/dist/capabilities.js.map +1 -0
  21. package/dist/cli.d.ts +2 -0
  22. package/dist/cli.js +1096 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/compiler.d.ts +3 -0
  25. package/dist/compiler.js +1034 -0
  26. package/dist/compiler.js.map +1 -0
  27. package/dist/configuration-error.d.ts +4 -0
  28. package/dist/configuration-error.js +9 -0
  29. package/dist/configuration-error.js.map +1 -0
  30. package/dist/diagnostics-json.d.ts +23 -0
  31. package/dist/diagnostics-json.js +143 -0
  32. package/dist/diagnostics-json.js.map +1 -0
  33. package/dist/diagnostics.d.ts +16 -0
  34. package/dist/diagnostics.js +154 -0
  35. package/dist/diagnostics.js.map +1 -0
  36. package/dist/equation.d.ts +74 -0
  37. package/dist/equation.js +485 -0
  38. package/dist/equation.js.map +1 -0
  39. package/dist/font.d.ts +19 -0
  40. package/dist/font.js +75 -0
  41. package/dist/font.js.map +1 -0
  42. package/dist/format.d.ts +2 -0
  43. package/dist/format.js +308 -0
  44. package/dist/format.js.map +1 -0
  45. package/dist/hash.d.ts +5 -0
  46. package/dist/hash.js +189 -0
  47. package/dist/hash.js.map +1 -0
  48. package/dist/help.d.ts +9 -0
  49. package/dist/help.js +102 -0
  50. package/dist/help.js.map +1 -0
  51. package/dist/html-fragment.d.ts +7 -0
  52. package/dist/html-fragment.js +70 -0
  53. package/dist/html-fragment.js.map +1 -0
  54. package/dist/index.d.ts +39 -0
  55. package/dist/index.js +32 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/markdown.d.ts +14 -0
  58. package/dist/markdown.js +425 -0
  59. package/dist/markdown.js.map +1 -0
  60. package/dist/mermaid-browser.d.ts +43 -0
  61. package/dist/mermaid-browser.js +358 -0
  62. package/dist/mermaid-browser.js.map +1 -0
  63. package/dist/mermaid.d.ts +84 -0
  64. package/dist/mermaid.js +740 -0
  65. package/dist/mermaid.js.map +1 -0
  66. package/dist/model.d.ts +406 -0
  67. package/dist/model.js +2 -0
  68. package/dist/model.js.map +1 -0
  69. package/dist/parse.d.ts +3 -0
  70. package/dist/parse.js +1374 -0
  71. package/dist/parse.js.map +1 -0
  72. package/dist/preview-server.d.ts +39 -0
  73. package/dist/preview-server.js +156 -0
  74. package/dist/preview-server.js.map +1 -0
  75. package/dist/preview-shell.d.ts +29 -0
  76. package/dist/preview-shell.js +102 -0
  77. package/dist/preview-shell.js.map +1 -0
  78. package/dist/reference-validation.d.ts +7 -0
  79. package/dist/reference-validation.js +42 -0
  80. package/dist/reference-validation.js.map +1 -0
  81. package/dist/registry.d.ts +29 -0
  82. package/dist/registry.js +384 -0
  83. package/dist/registry.js.map +1 -0
  84. package/dist/render-html.d.ts +24 -0
  85. package/dist/render-html.js +149 -0
  86. package/dist/render-html.js.map +1 -0
  87. package/dist/render-pdf.d.ts +64 -0
  88. package/dist/render-pdf.js +503 -0
  89. package/dist/render-pdf.js.map +1 -0
  90. package/dist/render-png.d.ts +32 -0
  91. package/dist/render-png.js +427 -0
  92. package/dist/render-png.js.map +1 -0
  93. package/dist/render-svg.d.ts +23 -0
  94. package/dist/render-svg.js +243 -0
  95. package/dist/render-svg.js.map +1 -0
  96. package/dist/runtime-support.d.ts +26 -0
  97. package/dist/runtime-support.js +62 -0
  98. package/dist/runtime-support.js.map +1 -0
  99. package/dist/source-map.d.ts +12 -0
  100. package/dist/source-map.js +75 -0
  101. package/dist/source-map.js.map +1 -0
  102. package/dist/table.d.ts +22 -0
  103. package/dist/table.js +90 -0
  104. package/dist/table.js.map +1 -0
  105. package/dist/theme.d.ts +6 -0
  106. package/dist/theme.js +89 -0
  107. package/dist/theme.js.map +1 -0
  108. package/dist/tool-version.d.ts +3 -0
  109. package/dist/tool-version.js +3 -0
  110. package/dist/tool-version.js.map +1 -0
  111. package/dist/type-guards.d.ts +1 -0
  112. package/dist/type-guards.js +4 -0
  113. package/dist/type-guards.js.map +1 -0
  114. package/dist/validate-document.d.ts +2 -0
  115. package/dist/validate-document.js +248 -0
  116. package/dist/validate-document.js.map +1 -0
  117. package/dist/version.d.ts +37 -0
  118. package/dist/version.js +111 -0
  119. package/dist/version.js.map +1 -0
  120. package/dist/watch-dependencies.d.ts +15 -0
  121. package/dist/watch-dependencies.js +93 -0
  122. package/dist/watch-dependencies.js.map +1 -0
  123. package/dist/watch-events.d.ts +51 -0
  124. package/dist/watch-events.js +27 -0
  125. package/dist/watch-events.js.map +1 -0
  126. package/dist/watch-loop.d.ts +39 -0
  127. package/dist/watch-loop.js +117 -0
  128. package/dist/watch-loop.js.map +1 -0
  129. package/package.json +70 -0
  130. package/schemas/acceptance.json +104 -0
  131. package/schemas/capabilities.json +491 -0
  132. package/schemas/diagnostics.json +219 -0
  133. package/schemas/version.json +167 -0
@@ -0,0 +1,1034 @@
1
+ import { resolveProjectImages } from "./assets.js";
2
+ import { CompilerConfigurationError } from "./configuration-error.js";
3
+ import { createDiagnostic, DEFAULT_DIAGNOSTIC_LIMITS, normalizeAndLimitDiagnostics, } from "./diagnostics.js";
4
+ import { assertInterFontCoverage, FontCoverageError, loadCodeFontFaces, loadInterFontFaces, } from "./font.js";
5
+ import { formatSource } from "./format.js";
6
+ import { documentContentHash } from "./hash.js";
7
+ import { inlineTextValue } from "./markdown.js";
8
+ import { EquationSanitizerError, katexDependencyClosure, sanitizeKatexHtml, } from "./equation.js";
9
+ import { MermaidBrowserParseError, MermaidBrowserUnavailableError, } from "./mermaid-browser.js";
10
+ import { MERMAID_PLUGIN_TYPE, MermaidSanitizerError, mermaidDependencyClosure, sanitizeMermaidFragment, } from "./mermaid.js";
11
+ import { FragmentSecurityError } from "./html-fragment.js";
12
+ import { freezeRegistryForCompiler, resolveRegistry, satisfiesSemverRange, } from "./registry.js";
13
+ import { parseSource } from "./parse.js";
14
+ import { ArtifactLimitError, createHtmlLayout, documentTitle, renderHtml } from "./render-html.js";
15
+ import { pinnedPdfBrowserCapability, PdfArtifactLimitError, renderPdf, } from "./render-pdf.js";
16
+ import { pinnedSvgBrowserCapability, renderSvg, SvgArtifactLimitError, } from "./render-svg.js";
17
+ import { pinnedPngBrowserCapability, PngArtifactLimitError, renderPng, } from "./render-png.js";
18
+ import { validateBlockIds } from "./reference-validation.js";
19
+ import { builtInThemes, copyAndFreezeTheme } from "./theme.js";
20
+ import { validateDocumentSchema } from "./validate-document.js";
21
+ const THEME_ID = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
22
+ const SEMVER = /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/;
23
+ const BUILT_IN_RENDERER_VERSION_BY_FORMAT = {
24
+ html: "1.0.0",
25
+ svg: "1.0.0",
26
+ png: "1.0.0",
27
+ pdf: "1.0.0",
28
+ };
29
+ function validateParsed(parsed, limits) {
30
+ const schemaDiagnostics = validateDocumentSchema(parsed.document);
31
+ if ([...parsed.diagnostics, ...schemaDiagnostics].some(({ severity }) => severity === "error")) {
32
+ return {
33
+ diagnostics: normalizeAndLimitDiagnostics([parsed.diagnostics, schemaDiagnostics], limits),
34
+ };
35
+ }
36
+ const blockRanges = collectBlockRanges(parsed.document.blocks);
37
+ const referenceDiagnostics = validateBlockIds(collectBlockIds(parsed.document.blocks));
38
+ if (referenceDiagnostics.some(({ severity }) => severity === "error")) {
39
+ return {
40
+ diagnostics: normalizeAndLimitDiagnostics([parsed.diagnostics, schemaDiagnostics, referenceDiagnostics], limits, blockRanges),
41
+ };
42
+ }
43
+ const invalidBlockDiagnostics = containsInvalidBlock(parsed.document.blocks)
44
+ ? [
45
+ createDiagnostic("azeforge.document#invalid-block", "error", "The Source contains an invalid Block."),
46
+ ]
47
+ : [];
48
+ const diagnostics = normalizeAndLimitDiagnostics([
49
+ parsed.diagnostics,
50
+ schemaDiagnostics,
51
+ referenceDiagnostics,
52
+ invalidBlockDiagnostics,
53
+ ], limits, blockRanges);
54
+ if (invalidBlockDiagnostics.length > 0)
55
+ return { diagnostics };
56
+ const document = {
57
+ azemarkVersion: parsed.document.azemarkVersion,
58
+ schemaVersion: parsed.document.schemaVersion,
59
+ metadata: parsed.document.metadata,
60
+ blocks: parsed.document.blocks,
61
+ };
62
+ return { document, diagnostics };
63
+ }
64
+ function resolveDiagnosticLimits(options) {
65
+ const perBlock = options.diagnosticLimits?.perBlock ?? DEFAULT_DIAGNOSTIC_LIMITS.perBlock;
66
+ const perDocument = options.diagnosticLimits?.perDocument ??
67
+ DEFAULT_DIAGNOSTIC_LIMITS.perDocument;
68
+ if (!Number.isInteger(perBlock) ||
69
+ perBlock < 1 ||
70
+ perBlock > DEFAULT_DIAGNOSTIC_LIMITS.perBlock ||
71
+ !Number.isInteger(perDocument) ||
72
+ perDocument < 1 ||
73
+ perDocument > DEFAULT_DIAGNOSTIC_LIMITS.perDocument) {
74
+ throw new CompilerConfigurationError("azeforge.config#diagnostic-limits", "Diagnostic limits must be positive integers no greater than the defaults.");
75
+ }
76
+ return Object.freeze({ perBlock, perDocument });
77
+ }
78
+ function limitParseResult(parsed, limits) {
79
+ const sourceDiagnostics = parsed.diagnostics.filter(({ code }) => !code.startsWith("azeforge.reference#"));
80
+ const referenceDiagnostics = parsed.diagnostics.filter(({ code }) => code.startsWith("azeforge.reference#"));
81
+ const diagnostics = normalizeAndLimitDiagnostics([sourceDiagnostics, referenceDiagnostics], limits, parsed.document.blocks.map(({ range }) => range));
82
+ const truncationIndex = diagnostics.findIndex(({ code }) => code === "azeforge.diagnostics#truncated");
83
+ const blocks = parsed.document.blocks.map((block) => {
84
+ if (block.kind === "invalid") {
85
+ const mappedIndexes = block.diagnosticIndexes.map((index) => {
86
+ const original = parsed.diagnostics[index];
87
+ const retainedIndex = original === undefined ? -1 : diagnostics.indexOf(original);
88
+ return retainedIndex >= 0 ? retainedIndex : truncationIndex;
89
+ });
90
+ return {
91
+ ...block,
92
+ diagnosticIndexes: [...new Set(mappedIndexes.filter((index) => index >= 0))],
93
+ };
94
+ }
95
+ return block;
96
+ });
97
+ return {
98
+ document: { ...parsed.document, blocks },
99
+ diagnostics,
100
+ };
101
+ }
102
+ function sameFormatMeaning(before, after) {
103
+ if (before.document !== undefined) {
104
+ return (after.document !== undefined &&
105
+ documentContentHash(after.document) === documentContentHash(before.document));
106
+ }
107
+ if (after.document !== undefined)
108
+ return false;
109
+ const beforeCodes = before.diagnostics.map(({ code }) => code);
110
+ const afterCodes = after.diagnostics.map(({ code }) => code);
111
+ return (beforeCodes.length === afterCodes.length &&
112
+ beforeCodes.every((code, index) => code === afterCodes[index]));
113
+ }
114
+ function validateTheme(theme) {
115
+ const validScheme = theme.colorScheme === "light" || theme.colorScheme === "dark";
116
+ const validColors = Object.values(theme.colors).every((color) => /^#[0-9a-f]{6}$/i.test(color));
117
+ const { canvasWidthPx, contentWidthPx, paddingPx } = theme.geometry;
118
+ const validGeometry = Number.isInteger(canvasWidthPx) &&
119
+ canvasWidthPx > 0 &&
120
+ Number.isInteger(contentWidthPx) &&
121
+ contentWidthPx > 0 &&
122
+ Number.isInteger(paddingPx) &&
123
+ paddingPx >= 0 &&
124
+ contentWidthPx + paddingPx * 2 <= canvasWidthPx;
125
+ const validTypography = Number.isFinite(theme.typography.lineHeight) &&
126
+ theme.typography.lineHeight >= 1 &&
127
+ theme.typography.lineHeight <= 3 &&
128
+ Number.isFinite(theme.typography.headingLineHeight) &&
129
+ theme.typography.headingLineHeight >= 1 &&
130
+ theme.typography.headingLineHeight <= 3 &&
131
+ Number.isFinite(theme.typography.paragraphSpacingEm) &&
132
+ theme.typography.paragraphSpacingEm >= 0 &&
133
+ theme.typography.paragraphSpacingEm <= 4 &&
134
+ theme.typography.bodyFontWeight === 400 &&
135
+ theme.typography.headingFontWeight === 700 &&
136
+ theme.typography.proseFontFamily === "Inter";
137
+ if (!validScheme || !validColors || !validGeometry || !validTypography) {
138
+ throw new CompilerConfigurationError("AZE_CONFIG_THEME_VALUES", `Theme "${theme.id}" contains unsafe or invalid tokens.`);
139
+ }
140
+ }
141
+ export const DEFAULT_RENDER_TIMEOUT_MS = 5000;
142
+ class RenderTimeoutError extends Error {
143
+ constructor() {
144
+ super("The equation Block renderer timed out.");
145
+ this.name = "RenderTimeoutError";
146
+ }
147
+ }
148
+ function withRenderTimeout(work, timeoutMs) {
149
+ let timer;
150
+ const timeout = new Promise((_, reject) => {
151
+ timer = setTimeout(() => reject(new RenderTimeoutError()), timeoutMs);
152
+ });
153
+ return Promise.race([work, timeout]).finally(() => {
154
+ clearTimeout(timer);
155
+ });
156
+ }
157
+ class BlockRendererSyncError extends Error {
158
+ adapterId;
159
+ blockType;
160
+ constructor(adapterId, blockType) {
161
+ super(`Block renderer "${adapterId}" must be synchronous.`);
162
+ this.name = "AdapterSyncError";
163
+ this.adapterId = adapterId;
164
+ this.blockType = blockType;
165
+ }
166
+ }
167
+ class CompilerCancelledError extends Error {
168
+ constructor() {
169
+ super("The operation was cancelled before publication.");
170
+ this.name = "CompilerCancelledError";
171
+ }
172
+ }
173
+ function throwIfCancelled(signal) {
174
+ if (signal?.aborted === true)
175
+ throw new CompilerCancelledError();
176
+ }
177
+ function isCapabilityDenial(error) {
178
+ if (typeof error !== "object" || error === null)
179
+ return false;
180
+ if (!("code" in error))
181
+ return false;
182
+ return error.code === "AZE_CAPABILITY_DENIED";
183
+ }
184
+ function walkBlocks(blocks, visit) {
185
+ for (const block of blocks) {
186
+ visit(block);
187
+ if (block.kind === "blockquote" || block.kind === "callout") {
188
+ walkBlocks(block.children, visit);
189
+ }
190
+ else if (block.kind === "list") {
191
+ for (const item of block.items) {
192
+ walkBlocks(item.blocks, visit);
193
+ }
194
+ }
195
+ }
196
+ }
197
+ function equationTargets(document) {
198
+ const targets = [];
199
+ walkBlocks(document.blocks, (block) => {
200
+ if (block.kind === "equation")
201
+ targets.push({ block });
202
+ });
203
+ return targets;
204
+ }
205
+ function mermaidTargets(document) {
206
+ const targets = [];
207
+ walkBlocks(document.blocks, (block) => {
208
+ if (block.kind === "mermaid") {
209
+ targets.push({ block, ordinal: targets.length });
210
+ }
211
+ });
212
+ return targets;
213
+ }
214
+ function pluginBlocks(document, blockType) {
215
+ const targets = [];
216
+ walkBlocks(document.blocks, (block) => {
217
+ if (block.kind === blockType)
218
+ targets.push(block);
219
+ });
220
+ return targets;
221
+ }
222
+ function collectBlockIds(blocks) {
223
+ const occurrences = [];
224
+ walkBlocks(blocks, (block) => {
225
+ if (block.kind !== "invalid" && block.id !== undefined) {
226
+ occurrences.push({ id: block.id, range: block.range });
227
+ }
228
+ });
229
+ return occurrences;
230
+ }
231
+ function containsInvalidBlock(blocks) {
232
+ let found = false;
233
+ walkBlocks(blocks, (block) => {
234
+ if (block.kind === "invalid")
235
+ found = true;
236
+ });
237
+ return found;
238
+ }
239
+ function collectBlockRanges(blocks) {
240
+ const ranges = [];
241
+ walkBlocks(blocks, (block) => {
242
+ ranges.push(block.range);
243
+ });
244
+ return ranges;
245
+ }
246
+ function collectRenderText(blocks, out) {
247
+ for (const block of blocks) {
248
+ switch (block.kind) {
249
+ case "equation":
250
+ out.push(block.source);
251
+ break;
252
+ case "mermaid":
253
+ out.push(block.source);
254
+ if (block.title !== undefined)
255
+ out.push(block.title);
256
+ if (block.description !== undefined)
257
+ out.push(block.description);
258
+ break;
259
+ case "heading":
260
+ case "paragraph":
261
+ out.push(inlineTextValue(block.children));
262
+ break;
263
+ case "code":
264
+ out.push(block.value);
265
+ if (block.language !== undefined)
266
+ out.push(block.language);
267
+ break;
268
+ case "thematicBreak":
269
+ break;
270
+ case "blockquote":
271
+ case "callout":
272
+ if (block.kind === "callout" && block.title !== undefined) {
273
+ out.push(inlineTextValue(block.title));
274
+ }
275
+ collectRenderText(block.children, out);
276
+ break;
277
+ case "list":
278
+ for (const item of block.items) {
279
+ collectRenderText(item.blocks, out);
280
+ }
281
+ break;
282
+ case "table":
283
+ if (block.caption !== undefined) {
284
+ out.push(inlineTextValue(block.caption));
285
+ }
286
+ for (const cell of block.data.header) {
287
+ out.push(inlineTextValue(cell));
288
+ }
289
+ for (const row of block.data.rows) {
290
+ for (const cell of row) {
291
+ out.push(inlineTextValue(cell));
292
+ }
293
+ }
294
+ break;
295
+ }
296
+ }
297
+ }
298
+ function groupedAdapterDiagnostic(code, message, targets, sourceName, data, suggestion, noun = "equation") {
299
+ const first = targets[0];
300
+ const rest = targets.slice(1).map(({ block }) => ({
301
+ ...(sourceName === undefined ? {} : { source: sourceName }),
302
+ range: block.range,
303
+ message: `Another affected ${noun} Block is here.`,
304
+ }));
305
+ return createDiagnostic(code, "error", message, {
306
+ ...(first === undefined
307
+ ? {}
308
+ : {
309
+ location: {
310
+ ...(sourceName === undefined ? {} : { source: sourceName }),
311
+ range: first.block.range,
312
+ },
313
+ }),
314
+ data: { ...data, affectedBlocks: targets.length },
315
+ suggestion,
316
+ ...(rest.length === 0 ? {} : { relatedLocations: rest }),
317
+ });
318
+ }
319
+ async function renderEquationFragments(document, rendererId, registry, policy, sourceName, timeoutMs) {
320
+ const targets = equationTargets(document);
321
+ const rendererName = rendererId.toUpperCase();
322
+ if (targets.length === 0) {
323
+ return { fragments: new Map(), diagnostics: [] };
324
+ }
325
+ const renderer = registry.renderers.find((entry) => entry.id === rendererId);
326
+ if (renderer === undefined) {
327
+ return {
328
+ fragments: new Map(),
329
+ diagnostics: [
330
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-missing", `No ${rendererName} Renderer is registered for equation Blocks.`, targets, sourceName, { blockType: "equation", rendererId }, `Register the built-in ${rendererName} Renderer.`),
331
+ ],
332
+ };
333
+ }
334
+ if (policy.disabledRendererIds?.includes(renderer.id) === true) {
335
+ return {
336
+ fragments: new Map(),
337
+ diagnostics: [
338
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-disabled", `${rendererName} Renderer "${renderer.id}" is disabled by host policy.`, targets, sourceName, { rendererId: renderer.id }, "Enable the Renderer in Compiler policy."),
339
+ ],
340
+ };
341
+ }
342
+ const candidates = registry.blockRenderers.filter((entry) => entry.descriptor.blockType === "equation" &&
343
+ entry.descriptor.rendererId === rendererId);
344
+ if (candidates.length === 0) {
345
+ return {
346
+ fragments: new Map(),
347
+ diagnostics: [
348
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-missing", "No Block renderer is registered for equation Blocks.", targets, sourceName, { blockType: "equation", rendererId }, `Register the built-in equation ${rendererName} Block renderer.`),
349
+ ],
350
+ };
351
+ }
352
+ const compatible = candidates.filter((entry) => targets.every((target) => satisfiesSemverRange(target.block.pluginVersion, entry.descriptor.pluginVersionRange)) &&
353
+ satisfiesSemverRange(renderer.version, entry.descriptor.rendererVersionRange));
354
+ if (compatible.length === 0) {
355
+ const candidate = candidates[0];
356
+ return {
357
+ fragments: new Map(),
358
+ diagnostics: [
359
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-incompatible", "The registered equation Block renderer is incompatible with this Document.", targets, sourceName, {
360
+ blockType: "equation",
361
+ ...(candidate === undefined
362
+ ? {}
363
+ : {
364
+ adapterId: candidate.descriptor.id,
365
+ pluginVersionRange: candidate.descriptor.pluginVersionRange,
366
+ rendererVersionRange: candidate.descriptor.rendererVersionRange,
367
+ }),
368
+ }, `Register a Block renderer compatible with equation v1 and ${rendererName} v1.`),
369
+ ],
370
+ };
371
+ }
372
+ if (compatible.length > 1) {
373
+ return {
374
+ fragments: new Map(),
375
+ diagnostics: [
376
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-ambiguous", "More than one Block renderer matches equation Blocks.", targets, sourceName, {
377
+ blockType: "equation",
378
+ adapterIds: compatible.map((entry) => entry.descriptor.id),
379
+ }, "Register exactly one matching Block renderer."),
380
+ ],
381
+ };
382
+ }
383
+ const chosen = compatible[0];
384
+ if (chosen === undefined) {
385
+ return { fragments: new Map(), diagnostics: [] };
386
+ }
387
+ if (policy.disabledBlockRendererIds?.includes(chosen.descriptor.id) === true) {
388
+ return {
389
+ fragments: new Map(),
390
+ diagnostics: [
391
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-disabled", `Block renderer "${chosen.descriptor.id}" is disabled by host policy.`, targets, sourceName, { adapterId: chosen.descriptor.id }, "Enable the Block renderer in Compiler policy."),
392
+ ],
393
+ };
394
+ }
395
+ // Safe: candidates were filtered to blockType "equation", so the render
396
+ // implementation accepts EquationBlock even though the registry union is wider.
397
+ const renderEquation = chosen.render;
398
+ const fragments = new Map();
399
+ const diagnostics = [];
400
+ for (const target of targets) {
401
+ const location = {
402
+ ...(sourceName === undefined ? {} : { source: sourceName }),
403
+ range: target.block.range,
404
+ };
405
+ try {
406
+ const fragment = await withRenderTimeout(Promise.resolve(renderEquation(target.block, {
407
+ ...(sourceName === undefined ? {} : { sourceName }),
408
+ })), timeoutMs);
409
+ fragments.set(target.block, sanitizeKatexHtml(fragment));
410
+ }
411
+ catch (error) {
412
+ if (error instanceof RenderTimeoutError) {
413
+ diagnostics.push(createDiagnostic("azeforge.renderer#timeout", "error", `Block renderer "${chosen.descriptor.id}" timed out.`, {
414
+ location,
415
+ data: {
416
+ adapterId: chosen.descriptor.id,
417
+ timeoutMs,
418
+ },
419
+ suggestion: "Retry the operation or adjust the host render timeout.",
420
+ }));
421
+ }
422
+ else if (error instanceof EquationSanitizerError ||
423
+ error instanceof FragmentSecurityError) {
424
+ diagnostics.push(createDiagnostic("azeforge.security#sanitizer-rewrite", "error", "A Fragment failed final sanitization; refusing to publish.", {
425
+ location,
426
+ data: {
427
+ adapterId: chosen.descriptor.id,
428
+ blockType: "equation",
429
+ },
430
+ suggestion: "Remove the unsafe construct or report this Source as a sanitizer failure.",
431
+ }));
432
+ }
433
+ else if (isCapabilityDenial(error)) {
434
+ diagnostics.push(createDiagnostic("azeforge.security#capability-denied", "error", `Block renderer "${chosen.descriptor.id}" was denied a capability.`, {
435
+ location,
436
+ data: { adapterId: chosen.descriptor.id },
437
+ }));
438
+ }
439
+ else {
440
+ diagnostics.push(createDiagnostic("azeforge.renderer#unexpected-failure", "error", "The equation Block renderer failed unexpectedly.", {
441
+ location,
442
+ data: {
443
+ adapterId: chosen.descriptor.id,
444
+ blockType: "equation",
445
+ },
446
+ }));
447
+ }
448
+ }
449
+ }
450
+ return { fragments, diagnostics };
451
+ }
452
+ async function renderMermaidFragments(document, rendererId, registry, policy, sourceName, timeoutMs, theme) {
453
+ const targets = mermaidTargets(document);
454
+ const rendererName = rendererId.toUpperCase();
455
+ if (targets.length === 0) {
456
+ return { fragments: new Map(), diagnostics: [] };
457
+ }
458
+ const renderer = registry.renderers.find((entry) => entry.id === rendererId);
459
+ if (renderer === undefined) {
460
+ return {
461
+ fragments: new Map(),
462
+ diagnostics: [
463
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-missing", `No ${rendererName} Renderer is registered for mermaid Blocks.`, targets, sourceName, { blockType: MERMAID_PLUGIN_TYPE, rendererId }, `Register the built-in ${rendererName} Renderer.`),
464
+ ],
465
+ };
466
+ }
467
+ if (policy.disabledRendererIds?.includes(renderer.id) === true) {
468
+ return {
469
+ fragments: new Map(),
470
+ diagnostics: [
471
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-disabled", `${rendererName} Renderer "${renderer.id}" is disabled by host policy.`, targets, sourceName, { rendererId: renderer.id }, "Enable the Renderer in Compiler policy."),
472
+ ],
473
+ };
474
+ }
475
+ const candidates = registry.blockRenderers.filter((entry) => entry.descriptor.blockType === MERMAID_PLUGIN_TYPE &&
476
+ entry.descriptor.rendererId === rendererId);
477
+ if (candidates.length === 0) {
478
+ return {
479
+ fragments: new Map(),
480
+ diagnostics: [
481
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-missing", "No Block renderer is registered for mermaid Blocks.", targets, sourceName, { blockType: MERMAID_PLUGIN_TYPE, rendererId }, `Register the built-in mermaid ${rendererName} Block renderer.`),
482
+ ],
483
+ };
484
+ }
485
+ const compatible = candidates.filter((entry) => targets.every((target) => satisfiesSemverRange(target.block.pluginVersion, entry.descriptor.pluginVersionRange)) &&
486
+ satisfiesSemverRange(renderer.version, entry.descriptor.rendererVersionRange));
487
+ if (compatible.length === 0) {
488
+ const candidate = candidates[0];
489
+ return {
490
+ fragments: new Map(),
491
+ diagnostics: [
492
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-incompatible", "The registered mermaid Block renderer is incompatible with this Document.", targets, sourceName, {
493
+ blockType: MERMAID_PLUGIN_TYPE,
494
+ ...(candidate === undefined
495
+ ? {}
496
+ : {
497
+ adapterId: candidate.descriptor.id,
498
+ pluginVersionRange: candidate.descriptor.pluginVersionRange,
499
+ rendererVersionRange: candidate.descriptor.rendererVersionRange,
500
+ }),
501
+ }, `Register a Block renderer compatible with mermaid v1 and ${rendererName} v1.`),
502
+ ],
503
+ };
504
+ }
505
+ if (compatible.length > 1) {
506
+ return {
507
+ fragments: new Map(),
508
+ diagnostics: [
509
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-ambiguous", "More than one Block renderer matches mermaid Blocks.", targets, sourceName, {
510
+ blockType: MERMAID_PLUGIN_TYPE,
511
+ adapterIds: compatible.map((entry) => entry.descriptor.id),
512
+ }, "Register exactly one matching Block renderer."),
513
+ ],
514
+ };
515
+ }
516
+ const chosen = compatible[0];
517
+ if (chosen === undefined) {
518
+ return { fragments: new Map(), diagnostics: [] };
519
+ }
520
+ if (policy.disabledBlockRendererIds?.includes(chosen.descriptor.id) === true) {
521
+ return {
522
+ fragments: new Map(),
523
+ diagnostics: [
524
+ groupedAdapterDiagnostic("azeforge.renderer#adapter-disabled", `Block renderer "${chosen.descriptor.id}" is disabled by host policy.`, targets, sourceName, { adapterId: chosen.descriptor.id }, "Enable the Block renderer in Compiler policy."),
525
+ ],
526
+ };
527
+ }
528
+ const fragments = new Map();
529
+ const diagnostics = [];
530
+ for (const target of targets) {
531
+ const location = {
532
+ ...(sourceName === undefined ? {} : { source: sourceName }),
533
+ range: target.block.range,
534
+ };
535
+ try {
536
+ const fragment = await withRenderTimeout(Promise.resolve(chosen.render(target.block, {
537
+ ...(sourceName === undefined ? {} : { sourceName }),
538
+ ordinal: target.ordinal,
539
+ theme,
540
+ })), timeoutMs);
541
+ fragments.set(target.block, sanitizeMermaidFragment(fragment, { ordinal: target.ordinal }));
542
+ }
543
+ catch (error) {
544
+ if (error instanceof RenderTimeoutError) {
545
+ diagnostics.push(createDiagnostic("azeforge.renderer#timeout", "error", `Block renderer "${chosen.descriptor.id}" timed out.`, {
546
+ location,
547
+ data: {
548
+ adapterId: chosen.descriptor.id,
549
+ timeoutMs,
550
+ },
551
+ suggestion: "Retry the operation or adjust the host render timeout.",
552
+ }));
553
+ }
554
+ else if (error instanceof MermaidBrowserParseError) {
555
+ diagnostics.push(createDiagnostic("azeforge.mermaid#invalid-syntax", "error", "The mermaid diagram could not be parsed.", {
556
+ location,
557
+ suggestion: "Check node brackets, arrows, and participant declarations.",
558
+ data: {
559
+ diagramType: target.block.diagramType,
560
+ detail: error.message,
561
+ },
562
+ }));
563
+ }
564
+ else if (error instanceof MermaidBrowserUnavailableError) {
565
+ diagnostics.push(createDiagnostic("azeforge.renderer#adapter-missing", "error", "The pinned Mermaid browser engine is unavailable.", {
566
+ location,
567
+ data: {
568
+ blockType: MERMAID_PLUGIN_TYPE,
569
+ engine: "HeadlessChrome",
570
+ },
571
+ suggestion: "Reinstall AzeForge browser dependencies and retry.",
572
+ }));
573
+ }
574
+ else if (error instanceof MermaidSanitizerError ||
575
+ error instanceof FragmentSecurityError) {
576
+ diagnostics.push(createDiagnostic("azeforge.security#sanitizer-rewrite", "error", "A Fragment failed final sanitization; refusing to publish.", {
577
+ location,
578
+ data: {
579
+ adapterId: chosen.descriptor.id,
580
+ blockType: MERMAID_PLUGIN_TYPE,
581
+ },
582
+ suggestion: "Remove the unsafe construct or report this Source as a sanitizer failure.",
583
+ }));
584
+ }
585
+ else if (isCapabilityDenial(error)) {
586
+ diagnostics.push(createDiagnostic("azeforge.security#capability-denied", "error", `Block renderer "${chosen.descriptor.id}" was denied a capability.`, {
587
+ location,
588
+ data: { adapterId: chosen.descriptor.id },
589
+ }));
590
+ }
591
+ else {
592
+ diagnostics.push(createDiagnostic("azeforge.renderer#unexpected-failure", "error", "The mermaid Block renderer failed unexpectedly.", {
593
+ location,
594
+ data: {
595
+ adapterId: chosen.descriptor.id,
596
+ blockType: MERMAID_PLUGIN_TYPE,
597
+ },
598
+ }));
599
+ }
600
+ }
601
+ }
602
+ return { fragments, diagnostics };
603
+ }
604
+ function checkPluginAdapters(document, rendererId, registry, policy, sourceName) {
605
+ const rendererName = rendererId.toUpperCase();
606
+ const diagnostics = [];
607
+ let renderCallout;
608
+ let renderTable;
609
+ for (const entry of [
610
+ { blockType: "callout", pluginVersion: "1.0.0" },
611
+ { blockType: "table", pluginVersion: "1.0.0" },
612
+ ]) {
613
+ const blocks = pluginBlocks(document, entry.blockType);
614
+ if (blocks.length === 0)
615
+ continue;
616
+ const targets = blocks.map((block) => ({ block }));
617
+ const renderer = registry.renderers.find((item) => item.id === rendererId);
618
+ if (renderer === undefined) {
619
+ diagnostics.push(groupedAdapterDiagnostic("azeforge.renderer#adapter-missing", `No ${rendererName} Renderer is registered for ${entry.blockType} Blocks.`, targets, sourceName, { blockType: entry.blockType, rendererId }, `Register the built-in ${rendererName} Renderer.`, entry.blockType));
620
+ continue;
621
+ }
622
+ if (policy.disabledRendererIds?.includes(renderer.id) === true) {
623
+ diagnostics.push(groupedAdapterDiagnostic("azeforge.renderer#adapter-disabled", `${rendererName} Renderer "${renderer.id}" is disabled by host policy.`, targets, sourceName, { rendererId: renderer.id }, "Enable the Renderer in Compiler policy.", entry.blockType));
624
+ continue;
625
+ }
626
+ const candidates = registry.blockRenderers.filter((item) => item.descriptor.blockType === entry.blockType &&
627
+ item.descriptor.rendererId === rendererId);
628
+ if (candidates.length === 0) {
629
+ diagnostics.push(groupedAdapterDiagnostic("azeforge.renderer#adapter-missing", `No Block renderer is registered for ${entry.blockType} Blocks.`, targets, sourceName, { blockType: entry.blockType, rendererId }, `Register the built-in ${entry.blockType} ${rendererName} Block renderer.`, entry.blockType));
630
+ continue;
631
+ }
632
+ const compatible = candidates.filter((item) => satisfiesSemverRange(entry.pluginVersion, item.descriptor.pluginVersionRange) &&
633
+ satisfiesSemverRange(renderer.version, item.descriptor.rendererVersionRange));
634
+ if (compatible.length === 0) {
635
+ const candidate = candidates[0];
636
+ diagnostics.push(groupedAdapterDiagnostic("azeforge.renderer#adapter-incompatible", `The registered ${entry.blockType} Block renderer is incompatible with this Document.`, targets, sourceName, {
637
+ blockType: entry.blockType,
638
+ ...(candidate === undefined
639
+ ? {}
640
+ : {
641
+ adapterId: candidate.descriptor.id,
642
+ pluginVersionRange: candidate.descriptor.pluginVersionRange,
643
+ rendererVersionRange: candidate.descriptor.rendererVersionRange,
644
+ }),
645
+ }, `Register a Block renderer compatible with ${entry.blockType} v1 and ${rendererName} v1.`, entry.blockType));
646
+ continue;
647
+ }
648
+ if (compatible.length > 1) {
649
+ diagnostics.push(groupedAdapterDiagnostic("azeforge.renderer#adapter-ambiguous", `More than one Block renderer matches ${entry.blockType} Blocks.`, targets, sourceName, {
650
+ blockType: entry.blockType,
651
+ adapterIds: compatible.map((item) => item.descriptor.id),
652
+ }, "Register exactly one matching Block renderer.", entry.blockType));
653
+ continue;
654
+ }
655
+ const chosen = compatible[0];
656
+ if (chosen === undefined)
657
+ continue;
658
+ if (policy.disabledBlockRendererIds?.includes(chosen.descriptor.id) === true) {
659
+ diagnostics.push(groupedAdapterDiagnostic("azeforge.renderer#adapter-disabled", `Block renderer "${chosen.descriptor.id}" is disabled by host policy.`, targets, sourceName, { adapterId: chosen.descriptor.id }, "Enable the Block renderer in Compiler policy.", entry.blockType));
660
+ continue;
661
+ }
662
+ if (entry.blockType === "callout") {
663
+ const render = chosen.render;
664
+ renderCallout = (block, context) => {
665
+ const result = render(block, context);
666
+ if (typeof result !== "string") {
667
+ throw new BlockRendererSyncError(chosen.descriptor.id, "callout");
668
+ }
669
+ return result;
670
+ };
671
+ }
672
+ else {
673
+ const render = chosen.render;
674
+ renderTable = (block, context) => {
675
+ const result = render(block, context);
676
+ if (typeof result !== "string") {
677
+ throw new BlockRendererSyncError(chosen.descriptor.id, "table");
678
+ }
679
+ return result;
680
+ };
681
+ }
682
+ }
683
+ return {
684
+ diagnostics,
685
+ ...(renderCallout === undefined ? {} : { renderCallout }),
686
+ ...(renderTable === undefined ? {} : { renderTable }),
687
+ };
688
+ }
689
+ export function createCompiler(options = {}) {
690
+ const diagnosticLimits = resolveDiagnosticLimits(options);
691
+ const configuredThemes = options.themes ?? builtInThemes;
692
+ const themes = {};
693
+ for (const theme of configuredThemes) {
694
+ if (!THEME_ID.test(theme.id) || !SEMVER.test(theme.version)) {
695
+ throw new CompilerConfigurationError("AZE_CONFIG_THEME_IDENTITY", `Theme identity \"${theme.id}@${theme.version}\" is invalid.`);
696
+ }
697
+ validateTheme(theme);
698
+ if (themes[theme.id] !== undefined) {
699
+ throw new CompilerConfigurationError("AZE_CONFIG_THEME_DUPLICATE", `Theme \"${theme.id}\" is registered more than once.`);
700
+ }
701
+ themes[theme.id] = copyAndFreezeTheme(theme);
702
+ }
703
+ Object.freeze(themes);
704
+ const defaultThemeId = options.defaultTheme ?? "default";
705
+ if (themes[defaultThemeId] === undefined) {
706
+ throw new CompilerConfigurationError("AZE_CONFIG_DEFAULT_THEME", `Default Theme \"${defaultThemeId}\" is not registered.`);
707
+ }
708
+ const registry = freezeRegistryForCompiler(resolveRegistry({
709
+ ...(options.plugins === undefined ? {} : { plugins: options.plugins }),
710
+ ...(options.blockRenderers === undefined
711
+ ? {}
712
+ : { blockRenderers: options.blockRenderers }),
713
+ ...(options.renderers === undefined ? {} : { renderers: options.renderers }),
714
+ }));
715
+ const policy = Object.freeze({
716
+ ...(options.policy?.disabledBlockRendererIds === undefined
717
+ ? {}
718
+ : {
719
+ disabledBlockRendererIds: Object.freeze([
720
+ ...options.policy.disabledBlockRendererIds,
721
+ ]),
722
+ }),
723
+ ...(options.policy?.disabledRendererIds === undefined
724
+ ? {}
725
+ : {
726
+ disabledRendererIds: Object.freeze([
727
+ ...options.policy.disabledRendererIds,
728
+ ]),
729
+ }),
730
+ ...(options.policy?.disabledThemeIds === undefined
731
+ ? {}
732
+ : {
733
+ disabledThemeIds: Object.freeze([...options.policy.disabledThemeIds]),
734
+ }),
735
+ });
736
+ const renderTimeoutMs = options.renderTimeoutMs ?? DEFAULT_RENDER_TIMEOUT_MS;
737
+ if (!Number.isInteger(renderTimeoutMs) ||
738
+ renderTimeoutMs <= 0 ||
739
+ renderTimeoutMs > DEFAULT_RENDER_TIMEOUT_MS) {
740
+ throw new CompilerConfigurationError("azeforge.config#render-timeout", "Render timeout must be a positive integer no greater than the default (5000 ms).");
741
+ }
742
+ let fontFacesPromise;
743
+ const compiler = {
744
+ parse(source, parseOptions = {}) {
745
+ return limitParseResult(parseSource(source, {
746
+ ...parseOptions,
747
+ plugins: parseOptions.plugins ?? registry.plugins,
748
+ }), diagnosticLimits);
749
+ },
750
+ validate(parsed) {
751
+ return validateParsed(parsed, diagnosticLimits);
752
+ },
753
+ format(source, formatOptions = {}) {
754
+ const before = validateParsed(limitParseResult(parseSource(source, {
755
+ ...(formatOptions.sourceName === undefined
756
+ ? {}
757
+ : { sourceName: formatOptions.sourceName }),
758
+ plugins: registry.plugins,
759
+ }), diagnosticLimits), diagnosticLimits);
760
+ const rewritten = formatSource(source, {
761
+ ...(formatOptions.sourceName === undefined
762
+ ? {}
763
+ : { sourceName: formatOptions.sourceName }),
764
+ });
765
+ if (rewritten.source === undefined)
766
+ return rewritten;
767
+ const after = validateParsed(limitParseResult(parseSource(rewritten.source, {
768
+ ...(formatOptions.sourceName === undefined
769
+ ? {}
770
+ : { sourceName: formatOptions.sourceName }),
771
+ plugins: registry.plugins,
772
+ }), diagnosticLimits), diagnosticLimits);
773
+ if (!sameFormatMeaning(before, after)) {
774
+ const range = {
775
+ start: { line: 1, column: 1, offset: 0 },
776
+ end: { line: 1, column: 1, offset: 0 },
777
+ };
778
+ return {
779
+ diagnostics: [
780
+ createDiagnostic("azeforge.format#ambiguous-structure", "error", "Formatting changed the semantic meaning of the Source.", {
781
+ ...(formatOptions.sourceName === undefined
782
+ ? { location: { range } }
783
+ : { location: { source: formatOptions.sourceName, range } }),
784
+ suggestion: "Report this Source as a formatter failure.",
785
+ }),
786
+ ],
787
+ };
788
+ }
789
+ return rewritten;
790
+ },
791
+ async compile(source, compileOptions) {
792
+ const validation = validateParsed(limitParseResult(parseSource(source, { ...compileOptions, plugins: registry.plugins }), diagnosticLimits), diagnosticLimits);
793
+ if (validation.document === undefined) {
794
+ return { diagnostics: validation.diagnostics };
795
+ }
796
+ if (compileOptions.format !== "html" && compileOptions.format !== "svg" && compileOptions.format !== "png" && compileOptions.format !== "pdf") {
797
+ const unsupportedFormat = createDiagnostic("azeforge.renderer#format-unsupported", "error", `Artifact format \"${String(compileOptions.format)}\" is not supported.`, {
798
+ ...(compileOptions.sourceName === undefined
799
+ ? {}
800
+ : { location: { source: compileOptions.sourceName } }),
801
+ data: { format: String(compileOptions.format) },
802
+ });
803
+ return {
804
+ diagnostics: normalizeAndLimitDiagnostics([validation.diagnostics, [unsupportedFormat]], diagnosticLimits),
805
+ };
806
+ }
807
+ const selectedRenderer = registry.renderers.find((renderer) => renderer.formats.includes(compileOptions.format));
808
+ if (selectedRenderer === undefined) {
809
+ const unavailableRenderer = createDiagnostic("azeforge.renderer#format-unsupported", "error", `No Renderer is registered for Artifact format \"${compileOptions.format}\".`, {
810
+ ...(compileOptions.sourceName === undefined
811
+ ? {}
812
+ : { location: { source: compileOptions.sourceName } }),
813
+ data: { format: compileOptions.format },
814
+ });
815
+ return {
816
+ diagnostics: normalizeAndLimitDiagnostics([validation.diagnostics, [unavailableRenderer]], diagnosticLimits),
817
+ };
818
+ }
819
+ if (selectedRenderer.id !== compileOptions.format ||
820
+ selectedRenderer.version !==
821
+ BUILT_IN_RENDERER_VERSION_BY_FORMAT[compileOptions.format]) {
822
+ const unavailableImplementation = createDiagnostic("azeforge.renderer#adapter-missing", "error", `The registered Renderer for \"${compileOptions.format}\" has no trusted implementation.`, {
823
+ ...(compileOptions.sourceName === undefined
824
+ ? {}
825
+ : { location: { source: compileOptions.sourceName } }),
826
+ data: {
827
+ format: compileOptions.format,
828
+ rendererId: selectedRenderer.id,
829
+ rendererVersion: selectedRenderer.version,
830
+ },
831
+ suggestion: `Register the built-in ${compileOptions.format.toUpperCase()} Renderer.`,
832
+ });
833
+ return {
834
+ diagnostics: normalizeAndLimitDiagnostics([validation.diagnostics, [unavailableImplementation]], diagnosticLimits),
835
+ };
836
+ }
837
+ if (policy.disabledRendererIds?.includes(selectedRenderer.id) === true) {
838
+ const disabledRenderer = createDiagnostic("azeforge.renderer#adapter-disabled", "error", `Renderer \"${selectedRenderer.id}\" is disabled by host policy.`, {
839
+ ...(compileOptions.sourceName === undefined
840
+ ? {}
841
+ : { location: { source: compileOptions.sourceName } }),
842
+ data: {
843
+ format: compileOptions.format,
844
+ rendererId: selectedRenderer.id,
845
+ },
846
+ suggestion: "Enable the Renderer in Compiler policy.",
847
+ });
848
+ return {
849
+ diagnostics: normalizeAndLimitDiagnostics([validation.diagnostics, [disabledRenderer]], diagnosticLimits),
850
+ };
851
+ }
852
+ const themeId = compileOptions.theme ?? validation.document.metadata.theme ?? defaultThemeId;
853
+ const theme = themes[themeId];
854
+ if (theme === undefined) {
855
+ const unknownTheme = createDiagnostic("azeforge.renderer#unknown-theme", "error", `Theme "${themeId}" is not registered.`, {
856
+ ...(compileOptions.sourceName === undefined
857
+ ? {}
858
+ : { location: { source: compileOptions.sourceName } }),
859
+ data: { theme: themeId },
860
+ });
861
+ return {
862
+ diagnostics: normalizeAndLimitDiagnostics([validation.diagnostics, [unknownTheme]], diagnosticLimits),
863
+ };
864
+ }
865
+ if (policy.disabledThemeIds?.includes(themeId) === true) {
866
+ const disabledTheme = createDiagnostic("azeforge.renderer#disabled-theme", "error", `Theme "${themeId}" is disabled.`, {
867
+ ...(compileOptions.sourceName === undefined
868
+ ? {}
869
+ : { location: { source: compileOptions.sourceName } }),
870
+ data: { theme: themeId },
871
+ });
872
+ return {
873
+ diagnostics: normalizeAndLimitDiagnostics([validation.diagnostics, [disabledTheme]], diagnosticLimits),
874
+ };
875
+ }
876
+ try {
877
+ throwIfCancelled(compileOptions.signal);
878
+ const equationPreflight = await renderEquationFragments(validation.document, selectedRenderer.id, registry, policy, compileOptions.sourceName, renderTimeoutMs);
879
+ const mermaidPreflight = await renderMermaidFragments(validation.document, selectedRenderer.id, registry, policy, compileOptions.sourceName, renderTimeoutMs, theme);
880
+ const pluginPreflight = checkPluginAdapters(validation.document, selectedRenderer.id, registry, policy, compileOptions.sourceName);
881
+ const preflightDiagnostics = [
882
+ ...equationPreflight.diagnostics,
883
+ ...mermaidPreflight.diagnostics,
884
+ ...pluginPreflight.diagnostics,
885
+ ];
886
+ if (preflightDiagnostics.length > 0) {
887
+ return {
888
+ diagnostics: normalizeAndLimitDiagnostics([validation.diagnostics, preflightDiagnostics], diagnosticLimits, collectBlockRanges(validation.document.blocks)),
889
+ };
890
+ }
891
+ throwIfCancelled(compileOptions.signal);
892
+ const imageResolution = await resolveProjectImages(validation.document, {
893
+ ...(compileOptions.projectRoot === undefined
894
+ ? {}
895
+ : { projectRoot: compileOptions.projectRoot }),
896
+ ...(compileOptions.sourceName === undefined
897
+ ? {}
898
+ : { sourceName: compileOptions.sourceName }),
899
+ });
900
+ if (imageResolution.diagnostics.length > 0 ||
901
+ imageResolution.document === undefined ||
902
+ imageResolution.manifest === undefined) {
903
+ return {
904
+ diagnostics: normalizeAndLimitDiagnostics([validation.diagnostics, imageResolution.diagnostics], diagnosticLimits, collectBlockRanges(validation.document.blocks)),
905
+ };
906
+ }
907
+ throwIfCancelled(compileOptions.signal);
908
+ const renderedText = [
909
+ ...(validation.document.metadata.title === undefined
910
+ ? []
911
+ : [validation.document.metadata.title]),
912
+ ];
913
+ collectRenderText(validation.document.blocks, renderedText);
914
+ assertInterFontCoverage(renderedText);
915
+ fontFacesPromise ??= Promise.all([
916
+ loadInterFontFaces(),
917
+ loadCodeFontFaces(),
918
+ ]).then((faces) => faces.flat());
919
+ const fontFaces = await fontFacesPromise;
920
+ const contentHash = documentContentHash(validation.document);
921
+ const pluginRenderers = {
922
+ ...(pluginPreflight.renderCallout === undefined
923
+ ? {}
924
+ : { renderCallout: pluginPreflight.renderCallout }),
925
+ ...(pluginPreflight.renderTable === undefined
926
+ ? {}
927
+ : { renderTable: pluginPreflight.renderTable }),
928
+ };
929
+ const renderArguments = [
930
+ imageResolution.document,
931
+ contentHash,
932
+ theme,
933
+ fontFaces,
934
+ equationPreflight.fragments,
935
+ katexDependencyClosure(),
936
+ mermaidPreflight.fragments,
937
+ mermaidDependencyClosure(),
938
+ pluginRenderers,
939
+ ];
940
+ const htmlLayout = createHtmlLayout(renderArguments[0], renderArguments[2], renderArguments[3], renderArguments[4], renderArguments[5], renderArguments[6], renderArguments[7], renderArguments[8]);
941
+ const artifact = compileOptions.format === "html"
942
+ ? await renderHtml(...renderArguments, imageResolution.manifest)
943
+ : compileOptions.format === "svg"
944
+ ? await renderSvg(htmlLayout, contentHash, theme, imageResolution.manifest, pinnedSvgBrowserCapability)
945
+ : compileOptions.format === "png"
946
+ ? await renderPng(htmlLayout, contentHash, theme, imageResolution.manifest, pinnedPngBrowserCapability)
947
+ : await renderPdf(htmlLayout, contentHash, theme, imageResolution.manifest, pinnedPdfBrowserCapability, {
948
+ title: documentTitle(imageResolution.document),
949
+ authors: imageResolution.document.metadata.authors,
950
+ });
951
+ return {
952
+ diagnostics: validation.diagnostics,
953
+ document: validation.document,
954
+ contentHash,
955
+ artifact,
956
+ };
957
+ }
958
+ catch (error) {
959
+ const rendererFailure = error instanceof FontCoverageError
960
+ ? createDiagnostic("azeforge.renderer#font-coverage", "error", error.message, {
961
+ data: { codePoint: error.codePoint },
962
+ ...(compileOptions.sourceName === undefined
963
+ ? {}
964
+ : { location: { source: compileOptions.sourceName } }),
965
+ })
966
+ : error instanceof ArtifactLimitError ||
967
+ error instanceof SvgArtifactLimitError ||
968
+ error instanceof PngArtifactLimitError ||
969
+ error instanceof PdfArtifactLimitError
970
+ ? createDiagnostic("azeforge.renderer#artifact-limit", "error", error.message, {
971
+ data: { byteLength: error.byteLength },
972
+ ...(compileOptions.sourceName === undefined
973
+ ? {}
974
+ : { location: { source: compileOptions.sourceName } }),
975
+ })
976
+ : error instanceof MermaidBrowserUnavailableError
977
+ ? createDiagnostic("azeforge.renderer#browser-unavailable", "error", `The pinned browser engine is unavailable: ${error.message}`, {
978
+ data: { engine: "HeadlessChrome" },
979
+ suggestion: "Reinstall AzeForge browser dependencies and retry.",
980
+ ...(compileOptions.sourceName === undefined
981
+ ? {}
982
+ : { location: { source: compileOptions.sourceName } }),
983
+ })
984
+ : isCapabilityDenial(error)
985
+ ? createDiagnostic("azeforge.security#capability-denied", "error", `The ${compileOptions.format.toUpperCase()} Renderer was denied a capability; refusing to publish.`, {
986
+ data: {
987
+ format: compileOptions.format,
988
+ rendererId: selectedRenderer.id,
989
+ },
990
+ ...(compileOptions.sourceName === undefined
991
+ ? {}
992
+ : { location: { source: compileOptions.sourceName } }),
993
+ })
994
+ : error instanceof EquationSanitizerError ||
995
+ error instanceof MermaidSanitizerError ||
996
+ error instanceof FragmentSecurityError
997
+ ? createDiagnostic("azeforge.security#sanitizer-rewrite", "error", "A Fragment failed final sanitization; refusing to publish.", {
998
+ data: { format: compileOptions.format },
999
+ suggestion: "Remove the unsafe construct or report this Source as a sanitizer failure.",
1000
+ ...(compileOptions.sourceName === undefined
1001
+ ? {}
1002
+ : { location: { source: compileOptions.sourceName } }),
1003
+ })
1004
+ : error instanceof BlockRendererSyncError
1005
+ ? createDiagnostic("azeforge.renderer#adapter-sync", "error", `Block renderer "${error.adapterId}" must be synchronous.`, {
1006
+ data: {
1007
+ adapterId: error.adapterId,
1008
+ blockType: error.blockType,
1009
+ },
1010
+ suggestion: "Register a synchronous Block renderer.",
1011
+ ...(compileOptions.sourceName === undefined
1012
+ ? {}
1013
+ : { location: { source: compileOptions.sourceName } }),
1014
+ })
1015
+ : error instanceof CompilerCancelledError
1016
+ ? createDiagnostic("azeforge.compiler#cancelled", "error", "The operation was cancelled before publication.", {
1017
+ ...(compileOptions.sourceName === undefined
1018
+ ? {}
1019
+ : { location: { source: compileOptions.sourceName } }),
1020
+ })
1021
+ : createDiagnostic("azeforge.renderer#unexpected-failure", "error", `The ${compileOptions.format.toUpperCase()} Renderer failed unexpectedly.`, {
1022
+ ...(compileOptions.sourceName === undefined
1023
+ ? {}
1024
+ : { location: { source: compileOptions.sourceName } }),
1025
+ });
1026
+ return {
1027
+ diagnostics: normalizeAndLimitDiagnostics([validation.diagnostics, [rendererFailure]], diagnosticLimits),
1028
+ };
1029
+ }
1030
+ },
1031
+ };
1032
+ return Object.freeze(compiler);
1033
+ }
1034
+ //# sourceMappingURL=compiler.js.map