@bendyline/squisq 1.4.1 → 1.5.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.
- package/README.md +14 -1
- package/dist/{ContentContainer-DNx460y8.d.ts → ContentContainer-BXUlIf18.d.ts} +6 -2
- package/dist/{Doc-BeSeH2iX.d.ts → Doc-BBVGq1_9.d.ts} +922 -273
- package/dist/{ImageEditDoc-BTIvw0oq.d.ts → ImageEditDoc-FuyGtt6o.d.ts} +11 -4
- package/dist/{chunk-ZTX4PFFT.js → chunk-2OIBZYKP.js} +75 -2
- package/dist/chunk-2OIBZYKP.js.map +1 -0
- package/dist/chunk-2UQOHYE2.js +45 -0
- package/dist/chunk-2UQOHYE2.js.map +1 -0
- package/dist/chunk-6ATE2PSM.js +344 -0
- package/dist/chunk-6ATE2PSM.js.map +1 -0
- package/dist/{chunk-57CVQCFX.js → chunk-A4O7GIWE.js} +74 -23
- package/dist/chunk-A4O7GIWE.js.map +1 -0
- package/dist/{chunk-5WFRKURX.js → chunk-DLXZMS5K.js} +4 -2
- package/dist/chunk-DLXZMS5K.js.map +1 -0
- package/dist/chunk-ET53IIEP.js +1 -0
- package/dist/chunk-ET53IIEP.js.map +1 -0
- package/dist/chunk-HWVFJAAH.js +362 -0
- package/dist/chunk-HWVFJAAH.js.map +1 -0
- package/dist/{chunk-2TMAWB4Y.js → chunk-IMSCRLLV.js} +1 -1
- package/dist/chunk-IMSCRLLV.js.map +1 -0
- package/dist/{chunk-B4EEON3N.js → chunk-KKNUBQ6Y.js} +536 -103
- package/dist/chunk-KKNUBQ6Y.js.map +1 -0
- package/dist/chunk-LDQ2HJIX.js +596 -0
- package/dist/chunk-LDQ2HJIX.js.map +1 -0
- package/dist/chunk-LH7I6SH7.js +7208 -0
- package/dist/chunk-LH7I6SH7.js.map +1 -0
- package/dist/chunk-PGGGKYWG.js +1 -0
- package/dist/chunk-PGGGKYWG.js.map +1 -0
- package/dist/{chunk-7UDSRZKG.js → chunk-Q3ROPT5H.js} +35 -14
- package/dist/chunk-Q3ROPT5H.js.map +1 -0
- package/dist/chunk-QE7TWGVT.js +178 -0
- package/dist/chunk-QE7TWGVT.js.map +1 -0
- package/dist/chunk-RUDYOTA6.js +345 -0
- package/dist/chunk-RUDYOTA6.js.map +1 -0
- package/dist/{chunk-K32VJONL.js → chunk-T5UK6YOB.js} +41 -2
- package/dist/chunk-T5UK6YOB.js.map +1 -0
- package/dist/{chunk-3K5OG6KI.js → chunk-TEEEILMP.js} +412 -186
- package/dist/chunk-TEEEILMP.js.map +1 -0
- package/dist/{chunk-FR2RBTKO.js → chunk-TQWLI6S2.js} +93 -27
- package/dist/chunk-TQWLI6S2.js.map +1 -0
- package/dist/{chunk-WMOI7FN3.js → chunk-UY7KGQ5R.js} +12 -2
- package/dist/{chunk-WMOI7FN3.js.map → chunk-UY7KGQ5R.js.map} +1 -1
- package/dist/{chunk-3E5F2XMR.js → chunk-VWUFZ6ZG.js} +29 -2
- package/dist/chunk-VWUFZ6ZG.js.map +1 -0
- package/dist/chunk-ZLQKNOZQ.js +1 -0
- package/dist/chunk-ZLQKNOZQ.js.map +1 -0
- package/dist/doc/index.d.ts +5 -5
- package/dist/doc/index.js +135 -8
- package/dist/generate/index.d.ts +3 -74
- package/dist/generate/index.js +2 -7
- package/dist/icons/index.d.ts +3 -26
- package/dist/icons/index.js +13 -0
- package/dist/icons/inlineIconMarker.d.ts +1 -0
- package/dist/icons/inlineIconMarker.js +15 -0
- package/dist/icons/inlineIconMarker.js.map +1 -0
- package/dist/imageEdit/index.d.ts +12 -6
- package/dist/imageEdit/index.js +4 -1
- package/dist/index.d.ts +14 -11
- package/dist/index.js +265 -37
- package/dist/inlineIconMarker-CiE8Hlvz.d.ts +59 -0
- package/dist/jsonForm/index.d.ts +42 -1
- package/dist/jsonForm/index.js +9 -1
- package/dist/markdown/index.d.ts +228 -3
- package/dist/markdown/index.js +42 -7
- package/dist/recommend/index.d.ts +1 -1
- package/dist/recommend/index.js +3 -2
- package/dist/schemas/index.d.ts +28 -8
- package/dist/schemas/index.js +36 -8
- package/dist/spatial/index.d.ts +9 -4
- package/dist/spatial/index.js +1 -1
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.js +1 -1
- package/dist/story/index.d.ts +1021 -41
- package/dist/story/index.js +135 -8
- package/dist/{themeLibrary-BHjJ0Qx6.d.ts → themeLibrary-CehcJhzz.d.ts} +1 -1
- package/dist/timing/index.js +3 -5
- package/dist/transform/index.d.ts +48 -10
- package/dist/transform/index.js +17 -481
- package/dist/transform/index.js.map +1 -1
- package/dist/{types-BdZkdJ3z.d.ts → types-DlAZ7MW4.d.ts} +115 -1
- package/dist/versions/index.d.ts +1 -1
- package/package.json +6 -1
- package/src/__tests__/animationUtils.test.ts +40 -0
- package/src/__tests__/annotationCoercion.test.ts +243 -0
- package/src/__tests__/applyRenderStyle.test.ts +146 -0
- package/src/__tests__/atmosphereLayers.test.ts +108 -0
- package/src/__tests__/attrTokens.test.ts +162 -0
- package/src/__tests__/contentExtractor.test.ts +60 -0
- package/src/__tests__/coverBlock.test.ts +46 -1
- package/src/__tests__/customTemplateSample.test.ts +144 -0
- package/src/__tests__/customTemplatesFrontmatter.test.ts +236 -0
- package/src/__tests__/customThemesFrontmatter.test.ts +145 -0
- package/src/__tests__/diagramBlock.test.ts +122 -0
- package/src/__tests__/diagramLayout.test.ts +134 -0
- package/src/__tests__/drawing.test.ts +109 -0
- package/src/__tests__/drawingBlock.test.ts +124 -0
- package/src/__tests__/drawingLayout.test.ts +171 -0
- package/src/__tests__/geohash.test.ts +6 -5
- package/src/__tests__/getLayers.test.ts +6 -2
- package/src/__tests__/getLayersFallback.test.ts +103 -0
- package/src/__tests__/imageEditExportSvg.test.ts +70 -0
- package/src/__tests__/imageEditVersions.test.ts +72 -0
- package/src/__tests__/inlineIconMarker.test.ts +36 -0
- package/src/__tests__/inputDescriptors.test.ts +162 -0
- package/src/__tests__/jsonForm.tokens.test.ts +84 -0
- package/src/__tests__/layoutBlock.test.ts +83 -0
- package/src/__tests__/layoutLayout.test.ts +169 -0
- package/src/__tests__/markdown.test.ts +87 -0
- package/src/__tests__/markdownSanitize.test.ts +79 -0
- package/src/__tests__/markdownToDoc.test.ts +391 -2
- package/src/__tests__/mediaAnnotations.test.ts +91 -0
- package/src/__tests__/mediaSchedule.test.ts +118 -0
- package/src/__tests__/pandocAttrParse.test.ts +187 -0
- package/src/__tests__/resolveDocTheme.test.ts +55 -0
- package/src/__tests__/rootExports.test.ts +43 -0
- package/src/__tests__/shapeGeometry.test.ts +89 -0
- package/src/__tests__/shapeVocabulary.test.ts +133 -0
- package/src/__tests__/structuredData.test.ts +249 -0
- package/src/__tests__/templateAnnotationParse.test.ts +108 -0
- package/src/__tests__/templateMetadata.test.ts +31 -0
- package/src/__tests__/templates.test.ts +257 -4
- package/src/__tests__/themeCompile.test.ts +42 -0
- package/src/__tests__/themeValidator.test.ts +6 -0
- package/src/__tests__/transformV2.test.ts +137 -0
- package/src/__tests__/transitionNormalize.test.ts +61 -0
- package/src/__tests__/validateDoc.test.ts +260 -0
- package/src/doc/annotationBlocks.ts +117 -0
- package/src/doc/customTemplatesFrontmatter.ts +314 -0
- package/src/doc/customThemesFrontmatter.ts +95 -0
- package/src/doc/docToMarkdown.ts +104 -5
- package/src/doc/getLayers.ts +49 -17
- package/src/doc/index.ts +47 -1
- package/src/doc/markdownToDoc.ts +411 -13
- package/src/doc/mediaAnnotations.ts +100 -0
- package/src/doc/resolveDocTheme.ts +55 -0
- package/src/doc/standaloneAnnotation.ts +58 -0
- package/src/doc/structuredData.ts +301 -0
- package/src/doc/templateInputs.ts +240 -0
- package/src/doc/templates/__tests__/customTemplate.test.ts +170 -0
- package/src/doc/templates/__tests__/customTemplateRegistry.test.ts +110 -0
- package/src/doc/templates/accentImage.ts +30 -6
- package/src/doc/templates/captionUtils.ts +23 -0
- package/src/doc/templates/comparisonBar.ts +33 -18
- package/src/doc/templates/coverBlock.ts +35 -5
- package/src/doc/templates/customTemplate.ts +51 -0
- package/src/doc/templates/dataTable.ts +37 -19
- package/src/doc/templates/dateEvent.ts +41 -20
- package/src/doc/templates/definitionCard.ts +26 -18
- package/src/doc/templates/diagramBlock.ts +204 -0
- package/src/doc/templates/diagramLayout.ts +159 -0
- package/src/doc/templates/drawingBlock.ts +325 -0
- package/src/doc/templates/drawingLayout.ts +539 -0
- package/src/doc/templates/factCard.ts +54 -26
- package/src/doc/templates/fallbackBlock.ts +123 -0
- package/src/doc/templates/featureBlock.ts +20 -13
- package/src/doc/templates/fullBleedQuote.ts +18 -8
- package/src/doc/templates/imageWithCaption.ts +30 -15
- package/src/doc/templates/index.ts +225 -46
- package/src/doc/templates/inputDescriptors.ts +432 -0
- package/src/doc/templates/layoutBlock.ts +43 -0
- package/src/doc/templates/layoutLayout.ts +243 -0
- package/src/doc/templates/listBlock.ts +56 -30
- package/src/doc/templates/mapBlock.ts +15 -12
- package/src/doc/templates/metadata.ts +120 -0
- package/src/doc/templates/persistentLayers.ts +129 -11
- package/src/doc/templates/photoGrid.ts +22 -34
- package/src/doc/templates/pullQuote.ts +30 -12
- package/src/doc/templates/quoteBlock.ts +54 -29
- package/src/doc/templates/sectionHeader.ts +25 -11
- package/src/doc/templates/statHighlight.ts +39 -22
- package/src/doc/templates/titleBlock.ts +59 -21
- package/src/doc/templates/tokens/__tests__/resolveTokens.test.ts +487 -0
- package/src/doc/templates/tokens/resolveTokens.ts +453 -0
- package/src/doc/templates/twoColumn.ts +48 -30
- package/src/doc/templates/videoPullQuote.ts +22 -12
- package/src/doc/templates/videoWithCaption.ts +21 -15
- package/src/doc/utils/animationUtils.ts +267 -3
- package/src/doc/utils/applyRenderStyle.ts +113 -0
- package/src/doc/utils/imageTreatment.ts +70 -0
- package/src/doc/utils/nearest.ts +59 -0
- package/src/doc/utils/shapeGeometry.ts +464 -0
- package/src/doc/utils/themeUtils.ts +75 -1
- package/src/doc/validate.ts +575 -0
- package/src/generate/contentExtractor.ts +148 -35
- package/src/generate/index.ts +0 -3
- package/src/icons/index.ts +8 -0
- package/src/icons/inlineIconMarker.ts +73 -0
- package/src/imageEdit/export.ts +85 -2
- package/src/imageEdit/index.ts +1 -0
- package/src/imageEdit/persistence.ts +51 -0
- package/src/index.ts +2 -0
- package/src/jsonForm/index.ts +2 -0
- package/src/jsonForm/tokens.ts +78 -0
- package/src/markdown/annotationCoercion.ts +295 -0
- package/src/markdown/attrTokens.ts +241 -0
- package/src/markdown/convert.ts +229 -26
- package/src/markdown/index.ts +37 -0
- package/src/markdown/sanitize.ts +318 -0
- package/src/markdown/stringify.ts +96 -6
- package/src/markdown/types.ts +66 -0
- package/src/markdown/utils.ts +111 -2
- package/src/recommend/templates.ts +24 -0
- package/src/schemas/BlockTemplates.ts +133 -4
- package/src/schemas/CustomTemplates.ts +126 -0
- package/src/schemas/Doc.ts +387 -65
- package/src/schemas/ImageEditDoc.ts +11 -4
- package/src/schemas/Media.ts +169 -0
- package/src/schemas/Theme.ts +23 -1
- package/src/schemas/Transitions.ts +224 -0
- package/src/schemas/colorUtils.ts +33 -0
- package/src/schemas/fontStacks.ts +5 -2
- package/src/schemas/index.ts +3 -0
- package/src/schemas/themeCompile.ts +24 -5
- package/src/schemas/themeValidator.ts +49 -11
- package/src/schemas/themes/cinematic.json +69 -12
- package/src/schemas/themes/documentary.json +58 -11
- package/src/schemas/themes/gezellig.json +60 -11
- package/src/schemas/themes/magazine.json +54 -9
- package/src/schemas/themes/morning-light.json +52 -9
- package/src/schemas/themes/standard-dark.json +48 -13
- package/src/schemas/themes/standard.json +44 -12
- package/src/schemas/themes/tech-dark.json +65 -12
- package/src/spatial/Geohash.ts +52 -21
- package/src/storage/ContentContainer.ts +6 -2
- package/src/transform/applyTransform.ts +43 -4
- package/src/transform/index.ts +1 -0
- package/src/transform/registry.ts +44 -13
- package/src/transform/styles/dataDriven.ts +2 -1
- package/src/transform/styles/documentary.ts +2 -1
- package/src/transform/styles/magazine.ts +3 -1
- package/src/transform/styles/minimal.ts +2 -1
- package/src/transform/styles/narrative.ts +3 -1
- package/src/transform/templateSelector.ts +87 -11
- package/src/transform/types.ts +35 -2
- package/dist/chunk-2TMAWB4Y.js.map +0 -1
- package/dist/chunk-3E5F2XMR.js.map +0 -1
- package/dist/chunk-3K5OG6KI.js.map +0 -1
- package/dist/chunk-4X3JQXNM.js +0 -35
- package/dist/chunk-4X3JQXNM.js.map +0 -1
- package/dist/chunk-57CVQCFX.js.map +0 -1
- package/dist/chunk-5WFRKURX.js.map +0 -1
- package/dist/chunk-7UDSRZKG.js.map +0 -1
- package/dist/chunk-B4EEON3N.js.map +0 -1
- package/dist/chunk-FR2RBTKO.js.map +0 -1
- package/dist/chunk-H3AXU4MJ.js +0 -179
- package/dist/chunk-H3AXU4MJ.js.map +0 -1
- package/dist/chunk-K32VJONL.js.map +0 -1
- package/dist/chunk-KSWJR66U.js +0 -3706
- package/dist/chunk-KSWJR66U.js.map +0 -1
- package/dist/chunk-MYIH7FWD.js +0 -145
- package/dist/chunk-MYIH7FWD.js.map +0 -1
- package/dist/chunk-ZTX4PFFT.js.map +0 -1
- package/src/__tests__/slideshowGenerator.test.ts +0 -128
- package/src/generate/slideshowGenerator.ts +0 -273
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import {
|
|
2
|
+
toMdast
|
|
3
|
+
} from "./chunk-TEEEILMP.js";
|
|
4
|
+
import {
|
|
5
|
+
formatBlockScalar
|
|
6
|
+
} from "./chunk-HWVFJAAH.js";
|
|
7
|
+
|
|
8
|
+
// src/markdown/stringify.ts
|
|
9
|
+
import { unified } from "unified";
|
|
10
|
+
import remarkStringify from "remark-stringify";
|
|
11
|
+
import remarkGfm from "remark-gfm";
|
|
12
|
+
import remarkMath from "remark-math";
|
|
13
|
+
import remarkDirective from "remark-directive";
|
|
14
|
+
var defaultProcessor;
|
|
15
|
+
var DQ_RUN = `"(?:[^"\\\\]|\\\\.)*"`;
|
|
16
|
+
var SQ_RUN = `'(?:[^'\\\\]|\\\\.)*'`;
|
|
17
|
+
var ESCAPED_TEMPLATE_SPAN_RE = new RegExp(
|
|
18
|
+
`\\{\\\\\\[((?:${DQ_RUN}|${SQ_RUN}|\\\\.|[^\\]\\\\])+)\\]\\}`,
|
|
19
|
+
"g"
|
|
20
|
+
);
|
|
21
|
+
var HEADING_LINE_RE = /^#{1,6} .*$/gm;
|
|
22
|
+
var TRAILING_ESCAPED_PANDOC_SPAN_RE = new RegExp(
|
|
23
|
+
`\\{(?!\\\\?\\[)((?:${DQ_RUN}|${SQ_RUN}|\\\\.|[^}\\\\])*)\\}(?=\\s*(?:\\{\\[.*)?$)`
|
|
24
|
+
);
|
|
25
|
+
var UNESCAPE_PUNCT_RE = /\\([\\[\]:#.,+=/?;@%$(){}'"-])/g;
|
|
26
|
+
function unescapeMarkdownPunct(text) {
|
|
27
|
+
return text.replace(UNESCAPE_PUNCT_RE, "$1");
|
|
28
|
+
}
|
|
29
|
+
function stringifyMarkdown(doc, options) {
|
|
30
|
+
const mdastTree = toMdast(doc);
|
|
31
|
+
const useDefaults = !options || options.gfm !== false && options.math !== false && options.directive !== false && !options.bullet && !options.bulletOrdered && !options.emphasis && !options.strong && !options.rule && !options.fence && options.setext == null;
|
|
32
|
+
let processor;
|
|
33
|
+
if (useDefaults) {
|
|
34
|
+
if (!defaultProcessor) {
|
|
35
|
+
defaultProcessor = unified().use(remarkGfm).use(remarkMath).use(remarkDirective).use(remarkStringify, {
|
|
36
|
+
bullet: "-",
|
|
37
|
+
bulletOrdered: ".",
|
|
38
|
+
emphasis: "*",
|
|
39
|
+
strong: "*",
|
|
40
|
+
rule: "-",
|
|
41
|
+
fence: "`",
|
|
42
|
+
setext: false
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
processor = defaultProcessor;
|
|
46
|
+
} else {
|
|
47
|
+
processor = unified();
|
|
48
|
+
if (options?.gfm !== false) {
|
|
49
|
+
processor = processor.use(remarkGfm);
|
|
50
|
+
}
|
|
51
|
+
if (options?.math !== false) {
|
|
52
|
+
processor = processor.use(remarkMath);
|
|
53
|
+
}
|
|
54
|
+
if (options?.directive !== false) {
|
|
55
|
+
processor = processor.use(remarkDirective);
|
|
56
|
+
}
|
|
57
|
+
processor = processor.use(remarkStringify, {
|
|
58
|
+
bullet: options?.bullet ?? "-",
|
|
59
|
+
bulletOrdered: options?.bulletOrdered ?? ".",
|
|
60
|
+
emphasis: options?.emphasis ?? "*",
|
|
61
|
+
strong: options?.strong ?? "*",
|
|
62
|
+
rule: options?.rule ?? "-",
|
|
63
|
+
fence: options?.fence ?? "`",
|
|
64
|
+
setext: options?.setext ?? false
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const result = processor.stringify(mdastTree);
|
|
68
|
+
let cleaned = result.replace(
|
|
69
|
+
ESCAPED_TEMPLATE_SPAN_RE,
|
|
70
|
+
(_m, inner) => `{[${unescapeMarkdownPunct(inner)}]}`
|
|
71
|
+
);
|
|
72
|
+
cleaned = cleaned.replace(
|
|
73
|
+
HEADING_LINE_RE,
|
|
74
|
+
(line) => line.replace(
|
|
75
|
+
TRAILING_ESCAPED_PANDOC_SPAN_RE,
|
|
76
|
+
(_m, inner) => `{${unescapeMarkdownPunct(inner)}}`
|
|
77
|
+
)
|
|
78
|
+
);
|
|
79
|
+
cleaned = cleaned.replace(/\{(?!\\?\[)[^}]*\}/g, (match) => match.replace(/\\:/g, ":"));
|
|
80
|
+
if (doc.frontmatter && Object.keys(doc.frontmatter).length > 0) {
|
|
81
|
+
const yamlLines = Object.entries(doc.frontmatter).map(([k, v]) => {
|
|
82
|
+
if (typeof v === "string") {
|
|
83
|
+
return /[\r\n]/.test(v) ? `${k}: ${formatBlockScalar(v.replace(/\r\n?/g, "\n"))}` : `${k}: ${v}`;
|
|
84
|
+
}
|
|
85
|
+
return `${k}: ${JSON.stringify(v)}`;
|
|
86
|
+
});
|
|
87
|
+
return `---
|
|
88
|
+
${yamlLines.join("\n")}
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
${cleaned}`;
|
|
92
|
+
}
|
|
93
|
+
return cleaned;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// src/markdown/sanitize.ts
|
|
97
|
+
var SAFE_LINK_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "mailto", "tel"]);
|
|
98
|
+
var SAFE_MEDIA_SCHEMES = /* @__PURE__ */ new Set(["http", "https", "blob"]);
|
|
99
|
+
var NEVER_ALLOWED_SCHEMES = /* @__PURE__ */ new Set(["javascript", "vbscript", "data"]);
|
|
100
|
+
var SAFE_DATA_MEDIA_RE = /^data:(?:image\/(?!svg\+xml)[a-z0-9.+-]+|audio\/[a-z0-9.+-]+|video\/[a-z0-9.+-]+);/i;
|
|
101
|
+
var SAFE_TAGS = /* @__PURE__ */ new Set([
|
|
102
|
+
"a",
|
|
103
|
+
"abbr",
|
|
104
|
+
"b",
|
|
105
|
+
"blockquote",
|
|
106
|
+
"br",
|
|
107
|
+
"caption",
|
|
108
|
+
"cite",
|
|
109
|
+
"code",
|
|
110
|
+
"col",
|
|
111
|
+
"colgroup",
|
|
112
|
+
"data",
|
|
113
|
+
"dd",
|
|
114
|
+
"del",
|
|
115
|
+
"details",
|
|
116
|
+
"dfn",
|
|
117
|
+
"div",
|
|
118
|
+
"dl",
|
|
119
|
+
"dt",
|
|
120
|
+
"em",
|
|
121
|
+
"figcaption",
|
|
122
|
+
"figure",
|
|
123
|
+
"h1",
|
|
124
|
+
"h2",
|
|
125
|
+
"h3",
|
|
126
|
+
"h4",
|
|
127
|
+
"h5",
|
|
128
|
+
"h6",
|
|
129
|
+
"hr",
|
|
130
|
+
"i",
|
|
131
|
+
"img",
|
|
132
|
+
"kbd",
|
|
133
|
+
"li",
|
|
134
|
+
"mark",
|
|
135
|
+
"ol",
|
|
136
|
+
"p",
|
|
137
|
+
"pre",
|
|
138
|
+
"q",
|
|
139
|
+
"s",
|
|
140
|
+
"samp",
|
|
141
|
+
"small",
|
|
142
|
+
"source",
|
|
143
|
+
"span",
|
|
144
|
+
"strong",
|
|
145
|
+
"sub",
|
|
146
|
+
"summary",
|
|
147
|
+
"sup",
|
|
148
|
+
"table",
|
|
149
|
+
"tbody",
|
|
150
|
+
"td",
|
|
151
|
+
"tfoot",
|
|
152
|
+
"th",
|
|
153
|
+
"thead",
|
|
154
|
+
"time",
|
|
155
|
+
"tr",
|
|
156
|
+
"track",
|
|
157
|
+
"u",
|
|
158
|
+
"ul",
|
|
159
|
+
"var",
|
|
160
|
+
"video",
|
|
161
|
+
"audio"
|
|
162
|
+
]);
|
|
163
|
+
var DROP_WITH_CONTENT_TAGS = /* @__PURE__ */ new Set([
|
|
164
|
+
"base",
|
|
165
|
+
"button",
|
|
166
|
+
"embed",
|
|
167
|
+
"form",
|
|
168
|
+
"iframe",
|
|
169
|
+
"input",
|
|
170
|
+
"link",
|
|
171
|
+
"meta",
|
|
172
|
+
"object",
|
|
173
|
+
"option",
|
|
174
|
+
"script",
|
|
175
|
+
"select",
|
|
176
|
+
"style",
|
|
177
|
+
"svg",
|
|
178
|
+
"template",
|
|
179
|
+
"textarea"
|
|
180
|
+
]);
|
|
181
|
+
var GLOBAL_ATTRS = /* @__PURE__ */ new Set(["class", "id", "title", "role"]);
|
|
182
|
+
var TAG_ATTRS = {
|
|
183
|
+
a: /* @__PURE__ */ new Set(["href", "target", "rel"]),
|
|
184
|
+
audio: /* @__PURE__ */ new Set(["src", "controls", "preload", "muted", "loop"]),
|
|
185
|
+
col: /* @__PURE__ */ new Set(["span", "width"]),
|
|
186
|
+
colgroup: /* @__PURE__ */ new Set(["span"]),
|
|
187
|
+
data: /* @__PURE__ */ new Set(["value"]),
|
|
188
|
+
img: /* @__PURE__ */ new Set(["src", "alt", "width", "height", "loading", "decoding"]),
|
|
189
|
+
li: /* @__PURE__ */ new Set(["value"]),
|
|
190
|
+
ol: /* @__PURE__ */ new Set(["start", "type"]),
|
|
191
|
+
q: /* @__PURE__ */ new Set(["cite"]),
|
|
192
|
+
source: /* @__PURE__ */ new Set(["src", "type", "media"]),
|
|
193
|
+
td: /* @__PURE__ */ new Set(["colspan", "rowspan", "align"]),
|
|
194
|
+
th: /* @__PURE__ */ new Set(["colspan", "rowspan", "align", "scope"]),
|
|
195
|
+
time: /* @__PURE__ */ new Set(["datetime"]),
|
|
196
|
+
track: /* @__PURE__ */ new Set(["src", "kind", "label", "srclang", "default"]),
|
|
197
|
+
video: /* @__PURE__ */ new Set([
|
|
198
|
+
"src",
|
|
199
|
+
"poster",
|
|
200
|
+
"width",
|
|
201
|
+
"height",
|
|
202
|
+
"controls",
|
|
203
|
+
"preload",
|
|
204
|
+
"muted",
|
|
205
|
+
"loop",
|
|
206
|
+
"playsinline"
|
|
207
|
+
])
|
|
208
|
+
};
|
|
209
|
+
var SAFE_ATTR_NAME_RE = /^[a-z][a-z0-9:_.-]*$/;
|
|
210
|
+
var SAFE_OL_TYPES = /* @__PURE__ */ new Set(["1", "a", "A", "i", "I"]);
|
|
211
|
+
var SAFE_PRELOAD_VALUES = /* @__PURE__ */ new Set(["none", "metadata", "auto"]);
|
|
212
|
+
var SAFE_TRACK_KINDS = /* @__PURE__ */ new Set(["subtitles", "captions", "descriptions", "chapters", "metadata"]);
|
|
213
|
+
function sanitizeUrl(url, kind = "link", options) {
|
|
214
|
+
if (typeof url !== "string") return null;
|
|
215
|
+
const trimmed = url.trim();
|
|
216
|
+
if (!trimmed) return null;
|
|
217
|
+
const compact = stripUrlSchemeNoise(trimmed);
|
|
218
|
+
const colon = compact.indexOf(":");
|
|
219
|
+
const firstPathChar = firstIndexOfAny(compact, ["/", "?", "#"]);
|
|
220
|
+
const hasScheme = colon >= 0 && (firstPathChar < 0 || colon < firstPathChar);
|
|
221
|
+
if (!hasScheme) return trimmed;
|
|
222
|
+
const scheme = compact.slice(0, colon).toLowerCase();
|
|
223
|
+
if (kind === "media") {
|
|
224
|
+
if (scheme === "data") return SAFE_DATA_MEDIA_RE.test(compact) ? trimmed : null;
|
|
225
|
+
return SAFE_MEDIA_SCHEMES.has(scheme) ? trimmed : null;
|
|
226
|
+
}
|
|
227
|
+
if (SAFE_LINK_SCHEMES.has(scheme)) return trimmed;
|
|
228
|
+
if (options?.extraLinkSchemes?.includes(scheme) && !NEVER_ALLOWED_SCHEMES.has(scheme)) {
|
|
229
|
+
return trimmed;
|
|
230
|
+
}
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
function sanitizeHtmlNodes(nodes) {
|
|
234
|
+
const out = [];
|
|
235
|
+
for (const node of nodes) {
|
|
236
|
+
out.push(...sanitizeHtmlNode(node));
|
|
237
|
+
}
|
|
238
|
+
return out;
|
|
239
|
+
}
|
|
240
|
+
function sanitizeHtmlNode(node) {
|
|
241
|
+
switch (node.type) {
|
|
242
|
+
case "htmlText":
|
|
243
|
+
return [node];
|
|
244
|
+
case "htmlComment":
|
|
245
|
+
return [];
|
|
246
|
+
case "htmlElement":
|
|
247
|
+
return sanitizeHtmlElement(node);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function sanitizeHtmlElement(node) {
|
|
251
|
+
const tag = node.tagName.toLowerCase();
|
|
252
|
+
if (DROP_WITH_CONTENT_TAGS.has(tag)) return [];
|
|
253
|
+
const children = sanitizeHtmlNodes(node.children);
|
|
254
|
+
if (!SAFE_TAGS.has(tag)) return children;
|
|
255
|
+
return [
|
|
256
|
+
{
|
|
257
|
+
type: "htmlElement",
|
|
258
|
+
tagName: tag,
|
|
259
|
+
attributes: sanitizeAttrs(tag, node.attributes),
|
|
260
|
+
children,
|
|
261
|
+
selfClosing: node.selfClosing
|
|
262
|
+
}
|
|
263
|
+
];
|
|
264
|
+
}
|
|
265
|
+
function sanitizeAttrs(tag, attrs) {
|
|
266
|
+
const out = {};
|
|
267
|
+
for (const [rawName, rawValue] of Object.entries(attrs)) {
|
|
268
|
+
const name = rawName.toLowerCase();
|
|
269
|
+
if (!isAllowedAttr(tag, name)) continue;
|
|
270
|
+
const value = String(rawValue);
|
|
271
|
+
const safeValue = sanitizeAttrValue(tag, name, value);
|
|
272
|
+
if (safeValue === null) continue;
|
|
273
|
+
out[name] = safeValue;
|
|
274
|
+
}
|
|
275
|
+
if (tag === "a" && out.target === "_blank") {
|
|
276
|
+
out.rel = ensureRelTokens(out.rel, ["noopener", "noreferrer"]);
|
|
277
|
+
}
|
|
278
|
+
return out;
|
|
279
|
+
}
|
|
280
|
+
function isAllowedAttr(tag, name) {
|
|
281
|
+
if (!SAFE_ATTR_NAME_RE.test(name)) return false;
|
|
282
|
+
if (name.startsWith("on")) return false;
|
|
283
|
+
if (name.startsWith("aria-") || name.startsWith("data-")) return true;
|
|
284
|
+
if (GLOBAL_ATTRS.has(name)) return true;
|
|
285
|
+
return TAG_ATTRS[tag]?.has(name) ?? false;
|
|
286
|
+
}
|
|
287
|
+
function sanitizeAttrValue(tag, name, value) {
|
|
288
|
+
if (name === "href") return sanitizeUrl(value, "link");
|
|
289
|
+
if (name === "src" || name === "poster") return sanitizeUrl(value, "media");
|
|
290
|
+
if ((name === "width" || name === "height" || name === "span" || name === "colspan" || name === "rowspan" || name === "value") && !isNonNegativeInteger(value)) {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
if (name === "start" && !isInteger(value)) return null;
|
|
294
|
+
if (name === "type" && tag === "ol" && !SAFE_OL_TYPES.has(value)) return null;
|
|
295
|
+
if (name === "preload" && !SAFE_PRELOAD_VALUES.has(value)) return null;
|
|
296
|
+
if (name === "kind" && tag === "track" && !SAFE_TRACK_KINDS.has(value)) return null;
|
|
297
|
+
if (name === "align" && value !== "left" && value !== "right" && value !== "center") return null;
|
|
298
|
+
if (name === "target")
|
|
299
|
+
return value === "_blank" || value === "_self" || value === "_parent" || value === "_top" ? value : null;
|
|
300
|
+
if (name === "rel") return sanitizeRel(value);
|
|
301
|
+
return value;
|
|
302
|
+
}
|
|
303
|
+
function sanitizeRel(value) {
|
|
304
|
+
return relTokens(value).join(" ");
|
|
305
|
+
}
|
|
306
|
+
function ensureRelTokens(value, required) {
|
|
307
|
+
const tokens = new Set(value ? relTokens(value) : []);
|
|
308
|
+
for (const token of required) tokens.add(token);
|
|
309
|
+
return Array.from(tokens).join(" ");
|
|
310
|
+
}
|
|
311
|
+
function relTokens(value) {
|
|
312
|
+
const tokens = value.split(/\s+/).map((token) => token.trim().toLowerCase()).filter(Boolean).filter((token) => /^[a-z0-9_-]+$/.test(token));
|
|
313
|
+
return Array.from(new Set(tokens));
|
|
314
|
+
}
|
|
315
|
+
function isInteger(value) {
|
|
316
|
+
return /^-?\d+$/.test(value.trim());
|
|
317
|
+
}
|
|
318
|
+
function isNonNegativeInteger(value) {
|
|
319
|
+
return /^\d+$/.test(value.trim());
|
|
320
|
+
}
|
|
321
|
+
function firstIndexOfAny(value, needles) {
|
|
322
|
+
let result = -1;
|
|
323
|
+
for (const needle of needles) {
|
|
324
|
+
const index = value.indexOf(needle);
|
|
325
|
+
if (index >= 0 && (result < 0 || index < result)) result = index;
|
|
326
|
+
}
|
|
327
|
+
return result;
|
|
328
|
+
}
|
|
329
|
+
function stripUrlSchemeNoise(value) {
|
|
330
|
+
let out = "";
|
|
331
|
+
for (const char of value) {
|
|
332
|
+
const code = char.charCodeAt(0);
|
|
333
|
+
if (code <= 32 || code === 127 || char.trim() === "") continue;
|
|
334
|
+
out += char;
|
|
335
|
+
}
|
|
336
|
+
return out;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export {
|
|
340
|
+
stringifyMarkdown,
|
|
341
|
+
sanitizeUrl,
|
|
342
|
+
sanitizeHtmlNodes
|
|
343
|
+
};
|
|
344
|
+
//# sourceMappingURL=chunk-6ATE2PSM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/markdown/stringify.ts","../src/markdown/sanitize.ts"],"sourcesContent":["/**\n * JSON → Markdown Serializer\n *\n * Converts a MarkdownDocument JSON structure back to a markdown string\n * using the unified/remark ecosystem with GFM, math, and directive extensions.\n */\n\nimport { unified } from 'unified';\nimport remarkStringify from 'remark-stringify';\nimport remarkGfm from 'remark-gfm';\nimport remarkMath from 'remark-math';\nimport remarkDirective from 'remark-directive';\nimport type { MarkdownDocument, StringifyOptions } from './types.js';\nimport { toMdast } from './convert.js';\nimport { formatBlockScalar } from './utils.js';\n\n// Cache the default processor (all extensions, default formatting) to avoid rebuilding on every call.\nlet defaultProcessor: any;\n\n// ============================================\n// Annotation-span unescaping\n// ============================================\n\n// Quoted runs as they appear in remark-stringify output. Backslashes inside\n// the span are doubled by remark (a literal `\\` before punctuation becomes\n// `\\\\`), so `\\\\.` covers both authored escapes and remark's own.\nconst DQ_RUN = `\"(?:[^\"\\\\\\\\]|\\\\\\\\.)*\"`;\nconst SQ_RUN = `'(?:[^'\\\\\\\\]|\\\\\\\\.)*'`;\n\n/**\n * A `{[…]}` template-annotation (or inline-icon) span as it appears in\n * remark output: `{\\[inner]}`, where `inner` may contain backslash escapes\n * and quoted runs (which may themselves contain `]`).\n */\nconst ESCAPED_TEMPLATE_SPAN_RE = new RegExp(\n `\\\\{\\\\\\\\\\\\[((?:${DQ_RUN}|${SQ_RUN}|\\\\\\\\.|[^\\\\]\\\\\\\\])+)\\\\]\\\\}`,\n 'g',\n);\n\n/** A heading line (annotations are only emitted trailing on headings). */\nconst HEADING_LINE_RE = /^#{1,6} .*$/gm;\n\n/**\n * The trailing Pandoc `{…}` span on a heading line, possibly followed by an\n * already-unescaped `{[…]}` template span (canonical emit order).\n */\nconst TRAILING_ESCAPED_PANDOC_SPAN_RE = new RegExp(\n `\\\\{(?!\\\\\\\\?\\\\[)((?:${DQ_RUN}|${SQ_RUN}|\\\\\\\\.|[^}\\\\\\\\])*)\\\\}(?=\\\\s*(?:\\\\{\\\\[.*)?$)`,\n);\n\n/**\n * Backslash+punctuation pairs that are safe to unescape inside an\n * annotation span: the bare character re-parses as literal text in\n * trailing-heading position, so removing the backslash is purely\n * cosmetic and round-trips.\n *\n * Deliberately EXCLUDED (left escaped in the emitted markdown): emphasis\n * and other inline-construct openers (`*`, `_`, `` ` ``, `~`, `<`, `>`,\n * `&`, `!`, `|`, `^`). Unescaping those could make remark-parse split the\n * heading's text run into inline nodes on reparse, which would hide the\n * annotation from the extractor. remark-parse resolves the surviving\n * `\\X` escapes back to bare characters, so values still round-trip —\n * the escapes are a cosmetic cost only.\n *\n * Pairs are consumed left to right, so an authored `\\\"` that remark\n * doubled to `\\\\\"` correctly comes back as `\\\"`.\n */\nconst UNESCAPE_PUNCT_RE = /\\\\([\\\\[\\]:#.,+=/?;@%$(){}'\"-])/g;\n\nfunction unescapeMarkdownPunct(text: string): string {\n return text.replace(UNESCAPE_PUNCT_RE, '$1');\n}\n\n/**\n * Serialize a MarkdownDocument back to a markdown string.\n *\n * All extensions (GFM, math, directives) are enabled by default.\n * Use the `options` parameter to control formatting and disable extensions.\n *\n * @param doc - The MarkdownDocument to serialize\n * @param options - Serialization options (formatting, extensions)\n * @returns A markdown string\n *\n * @example\n * ```ts\n * const doc: MarkdownDocument = {\n * type: 'document',\n * children: [\n * { type: 'heading', depth: 1, children: [{ type: 'text', value: 'Hello' }] },\n * { type: 'paragraph', children: [{ type: 'text', value: 'World' }] },\n * ],\n * };\n * const md = stringifyMarkdown(doc);\n * // '# Hello\\n\\nWorld\\n'\n * ```\n */\nexport function stringifyMarkdown(doc: MarkdownDocument, options?: StringifyOptions): string {\n // Convert MarkdownDocument → mdast tree\n const mdastTree = toMdast(doc);\n\n // Use cached default processor when all extensions and default formatting are used.\n const useDefaults =\n !options ||\n (options.gfm !== false &&\n options.math !== false &&\n options.directive !== false &&\n !options.bullet &&\n !options.bulletOrdered &&\n !options.emphasis &&\n !options.strong &&\n !options.rule &&\n !options.fence &&\n options.setext == null);\n\n let processor: any;\n\n if (useDefaults) {\n if (!defaultProcessor) {\n defaultProcessor = unified()\n .use(remarkGfm)\n .use(remarkMath)\n .use(remarkDirective)\n .use(remarkStringify, {\n bullet: '-',\n bulletOrdered: '.',\n emphasis: '*',\n strong: '*',\n rule: '-',\n fence: '`',\n setext: false,\n });\n }\n processor = defaultProcessor;\n } else {\n // Build a custom processor with requested options.\n // unified's .use() chaining changes the generic signature each time,\n // making strict typing impractical — use a widened Processor type.\n processor = unified();\n\n if (options?.gfm !== false) {\n processor = processor.use(remarkGfm);\n }\n if (options?.math !== false) {\n processor = processor.use(remarkMath);\n }\n if (options?.directive !== false) {\n processor = processor.use(remarkDirective);\n }\n\n processor = processor.use(remarkStringify, {\n bullet: options?.bullet ?? '-',\n bulletOrdered: options?.bulletOrdered ?? '.',\n emphasis: options?.emphasis ?? '*',\n strong: options?.strong ?? '*',\n rule: options?.rule ?? '-',\n fence: options?.fence ?? '`',\n setext: options?.setext ?? false,\n });\n }\n\n // Stringify mdast → markdown string\n const result = processor.stringify(mdastTree) as string;\n\n // remark-stringify backslash-escapes markdown-significant characters in\n // text nodes (`[` to prevent link syntax, `:` for directives, `*`/`_` for\n // emphasis, a literal `\\` before punctuation, …). Our annotations are\n // injected as plain text, so their interiors come back escaped. Undo that\n // inside `{[…]}` template-annotation spans: removing one backslash from\n // each backslash+punctuation pair (left to right) exactly inverts\n // remark's escaping — including restoring an authored `\\\"` that remark\n // doubled to `\\\\\"`. The span regex understands quoted values, so a\n // quoted `]` doesn't terminate the span.\n let cleaned = result.replace(\n ESCAPED_TEMPLATE_SPAN_RE,\n (_m, inner: string) => `{[${unescapeMarkdownPunct(inner)}]}`,\n );\n\n // Same treatment for the trailing Pandoc `{…}` attribute block on heading\n // lines (canonical emit order is Pandoc block then template annotation,\n // so the span sits at end-of-line or just before a `{[…]}` span).\n cleaned = cleaned.replace(HEADING_LINE_RE, (line) =>\n line.replace(\n TRAILING_ESCAPED_PANDOC_SPAN_RE,\n (_m, inner: string) => `{${unescapeMarkdownPunct(inner)}}`,\n ),\n );\n\n // remark-directive escapes `:` in text (since `:name` would round-trip as\n // a text directive). Inside any other Pandoc-style `{…}` brace span, `:`\n // is purely a value separator (e.g. `connectsTo=foo:flow`), so unescape\n // it to keep round-trips lossless. The negative lookahead skips `{[…]}`\n // template annotations — those are handled above.\n cleaned = cleaned.replace(/\\{(?!\\\\?\\[)[^}]*\\}/g, (match) => match.replace(/\\\\:/g, ':'));\n\n // Prepend YAML frontmatter if present\n if (doc.frontmatter && Object.keys(doc.frontmatter).length > 0) {\n const yamlLines = Object.entries(doc.frontmatter).map(([k, v]) => {\n if (typeof v === 'string') {\n // Multi-line strings emit as a YAML literal block scalar, matching\n // `formatFrontmatterValue` / `setFrontmatterValues` so both paths\n // agree; single-line strings stay verbatim as before.\n return /[\\r\\n]/.test(v)\n ? `${k}: ${formatBlockScalar(v.replace(/\\r\\n?/g, '\\n'))}`\n : `${k}: ${v}`;\n }\n return `${k}: ${JSON.stringify(v)}`;\n });\n return `---\\n${yamlLines.join('\\n')}\\n---\\n\\n${cleaned}`;\n }\n\n return cleaned;\n}\n","import type { HtmlElement, HtmlNode } from './types.js';\n\nexport type HtmlPolicy = 'strip' | 'sanitize' | 'trusted';\n\nexport type UrlKind = 'link' | 'media';\n\nconst SAFE_LINK_SCHEMES = new Set(['http', 'https', 'mailto', 'tel']);\nconst SAFE_MEDIA_SCHEMES = new Set(['http', 'https', 'blob']);\n\n/** Schemes that can execute script — never allowed, even via extraLinkSchemes. */\nconst NEVER_ALLOWED_SCHEMES = new Set(['javascript', 'vbscript', 'data']);\n\nexport interface SanitizeUrlOptions {\n /**\n * Extra lowercase schemes allowed for `kind: 'link'` — the host-app escape\n * hatch for custom link protocols it intercepts itself (e.g. an app-internal\n * navigation scheme). Executable schemes are refused regardless.\n */\n extraLinkSchemes?: readonly string[];\n}\n\nconst SAFE_DATA_MEDIA_RE =\n /^data:(?:image\\/(?!svg\\+xml)[a-z0-9.+-]+|audio\\/[a-z0-9.+-]+|video\\/[a-z0-9.+-]+);/i;\n\nconst SAFE_TAGS = new Set([\n 'a',\n 'abbr',\n 'b',\n 'blockquote',\n 'br',\n 'caption',\n 'cite',\n 'code',\n 'col',\n 'colgroup',\n 'data',\n 'dd',\n 'del',\n 'details',\n 'dfn',\n 'div',\n 'dl',\n 'dt',\n 'em',\n 'figcaption',\n 'figure',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'hr',\n 'i',\n 'img',\n 'kbd',\n 'li',\n 'mark',\n 'ol',\n 'p',\n 'pre',\n 'q',\n 's',\n 'samp',\n 'small',\n 'source',\n 'span',\n 'strong',\n 'sub',\n 'summary',\n 'sup',\n 'table',\n 'tbody',\n 'td',\n 'tfoot',\n 'th',\n 'thead',\n 'time',\n 'tr',\n 'track',\n 'u',\n 'ul',\n 'var',\n 'video',\n 'audio',\n]);\n\nconst DROP_WITH_CONTENT_TAGS = new Set([\n 'base',\n 'button',\n 'embed',\n 'form',\n 'iframe',\n 'input',\n 'link',\n 'meta',\n 'object',\n 'option',\n 'script',\n 'select',\n 'style',\n 'svg',\n 'template',\n 'textarea',\n]);\n\nconst GLOBAL_ATTRS = new Set(['class', 'id', 'title', 'role']);\n\nconst TAG_ATTRS: Record<string, Set<string>> = {\n a: new Set(['href', 'target', 'rel']),\n audio: new Set(['src', 'controls', 'preload', 'muted', 'loop']),\n col: new Set(['span', 'width']),\n colgroup: new Set(['span']),\n data: new Set(['value']),\n img: new Set(['src', 'alt', 'width', 'height', 'loading', 'decoding']),\n li: new Set(['value']),\n ol: new Set(['start', 'type']),\n q: new Set(['cite']),\n source: new Set(['src', 'type', 'media']),\n td: new Set(['colspan', 'rowspan', 'align']),\n th: new Set(['colspan', 'rowspan', 'align', 'scope']),\n time: new Set(['datetime']),\n track: new Set(['src', 'kind', 'label', 'srclang', 'default']),\n video: new Set([\n 'src',\n 'poster',\n 'width',\n 'height',\n 'controls',\n 'preload',\n 'muted',\n 'loop',\n 'playsinline',\n ]),\n};\n\nconst SAFE_ATTR_NAME_RE = /^[a-z][a-z0-9:_.-]*$/;\nconst SAFE_OL_TYPES = new Set(['1', 'a', 'A', 'i', 'I']);\nconst SAFE_PRELOAD_VALUES = new Set(['none', 'metadata', 'auto']);\nconst SAFE_TRACK_KINDS = new Set(['subtitles', 'captions', 'descriptions', 'chapters', 'metadata']);\n\n/**\n * Sanitize a URL for a rendered link or media attribute.\n *\n * Relative URLs are allowed. Absolute URLs are restricted to schemes that\n * cannot execute script in a document context. Media URLs additionally allow\n * `blob:` and image/audio/video `data:` URLs because Squisq uses both for\n * browser-local media previews and self-contained exports.\n */\nexport function sanitizeUrl(\n url: string | null | undefined,\n kind: UrlKind = 'link',\n options?: SanitizeUrlOptions,\n): string | null {\n if (typeof url !== 'string') return null;\n\n const trimmed = url.trim();\n if (!trimmed) return null;\n\n const compact = stripUrlSchemeNoise(trimmed);\n const colon = compact.indexOf(':');\n const firstPathChar = firstIndexOfAny(compact, ['/', '?', '#']);\n const hasScheme = colon >= 0 && (firstPathChar < 0 || colon < firstPathChar);\n\n if (!hasScheme) return trimmed;\n\n const scheme = compact.slice(0, colon).toLowerCase();\n if (kind === 'media') {\n if (scheme === 'data') return SAFE_DATA_MEDIA_RE.test(compact) ? trimmed : null;\n return SAFE_MEDIA_SCHEMES.has(scheme) ? trimmed : null;\n }\n\n if (SAFE_LINK_SCHEMES.has(scheme)) return trimmed;\n if (options?.extraLinkSchemes?.includes(scheme) && !NEVER_ALLOWED_SCHEMES.has(scheme)) {\n return trimmed;\n }\n return null;\n}\n\n/**\n * Return a sanitized copy of an HtmlNode tree. The original parsed tree is left\n * untouched so markdown stringify can still preserve source fidelity.\n */\nexport function sanitizeHtmlNodes(nodes: HtmlNode[]): HtmlNode[] {\n const out: HtmlNode[] = [];\n for (const node of nodes) {\n out.push(...sanitizeHtmlNode(node));\n }\n return out;\n}\n\nfunction sanitizeHtmlNode(node: HtmlNode): HtmlNode[] {\n switch (node.type) {\n case 'htmlText':\n return [node];\n case 'htmlComment':\n return [];\n case 'htmlElement':\n return sanitizeHtmlElement(node);\n }\n}\n\nfunction sanitizeHtmlElement(node: HtmlElement): HtmlNode[] {\n const tag = node.tagName.toLowerCase();\n\n if (DROP_WITH_CONTENT_TAGS.has(tag)) return [];\n\n const children = sanitizeHtmlNodes(node.children);\n if (!SAFE_TAGS.has(tag)) return children;\n\n return [\n {\n type: 'htmlElement',\n tagName: tag,\n attributes: sanitizeAttrs(tag, node.attributes),\n children,\n selfClosing: node.selfClosing,\n },\n ];\n}\n\nfunction sanitizeAttrs(tag: string, attrs: Record<string, string>): Record<string, string> {\n const out: Record<string, string> = {};\n for (const [rawName, rawValue] of Object.entries(attrs)) {\n const name = rawName.toLowerCase();\n if (!isAllowedAttr(tag, name)) continue;\n\n const value = String(rawValue);\n const safeValue = sanitizeAttrValue(tag, name, value);\n if (safeValue === null) continue;\n out[name] = safeValue;\n }\n if (tag === 'a' && out.target === '_blank') {\n out.rel = ensureRelTokens(out.rel, ['noopener', 'noreferrer']);\n }\n return out;\n}\n\nfunction isAllowedAttr(tag: string, name: string): boolean {\n if (!SAFE_ATTR_NAME_RE.test(name)) return false;\n if (name.startsWith('on')) return false;\n if (name.startsWith('aria-') || name.startsWith('data-')) return true;\n if (GLOBAL_ATTRS.has(name)) return true;\n return TAG_ATTRS[tag]?.has(name) ?? false;\n}\n\nfunction sanitizeAttrValue(tag: string, name: string, value: string): string | null {\n if (name === 'href') return sanitizeUrl(value, 'link');\n if (name === 'src' || name === 'poster') return sanitizeUrl(value, 'media');\n if (\n (name === 'width' ||\n name === 'height' ||\n name === 'span' ||\n name === 'colspan' ||\n name === 'rowspan' ||\n name === 'value') &&\n !isNonNegativeInteger(value)\n ) {\n return null;\n }\n if (name === 'start' && !isInteger(value)) return null;\n if (name === 'type' && tag === 'ol' && !SAFE_OL_TYPES.has(value)) return null;\n if (name === 'preload' && !SAFE_PRELOAD_VALUES.has(value)) return null;\n if (name === 'kind' && tag === 'track' && !SAFE_TRACK_KINDS.has(value)) return null;\n if (name === 'align' && value !== 'left' && value !== 'right' && value !== 'center') return null;\n if (name === 'target')\n return value === '_blank' || value === '_self' || value === '_parent' || value === '_top'\n ? value\n : null;\n if (name === 'rel') return sanitizeRel(value);\n return value;\n}\n\nfunction sanitizeRel(value: string): string {\n return relTokens(value).join(' ');\n}\n\nfunction ensureRelTokens(value: string | undefined, required: string[]): string {\n const tokens = new Set(value ? relTokens(value) : []);\n for (const token of required) tokens.add(token);\n return Array.from(tokens).join(' ');\n}\n\nfunction relTokens(value: string): string[] {\n const tokens = value\n .split(/\\s+/)\n .map((token) => token.trim().toLowerCase())\n .filter(Boolean)\n .filter((token) => /^[a-z0-9_-]+$/.test(token));\n return Array.from(new Set(tokens));\n}\n\nfunction isInteger(value: string): boolean {\n return /^-?\\d+$/.test(value.trim());\n}\n\nfunction isNonNegativeInteger(value: string): boolean {\n return /^\\d+$/.test(value.trim());\n}\n\nfunction firstIndexOfAny(value: string, needles: string[]): number {\n let result = -1;\n for (const needle of needles) {\n const index = value.indexOf(needle);\n if (index >= 0 && (result < 0 || index < result)) result = index;\n }\n return result;\n}\n\nfunction stripUrlSchemeNoise(value: string): string {\n let out = '';\n for (const char of value) {\n const code = char.charCodeAt(0);\n if (code <= 0x20 || code === 0x7f || char.trim() === '') continue;\n out += char;\n }\n return out;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
oklchSetChroma,
|
|
8
8
|
pickContrastingText,
|
|
9
9
|
relativeLuminance
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-KKNUBQ6Y.js";
|
|
11
11
|
|
|
12
12
|
// src/schemas/Doc.ts
|
|
13
13
|
function calculateDuration(audio) {
|
|
@@ -39,29 +39,74 @@ function getCaptionAtTime(captions, time) {
|
|
|
39
39
|
}
|
|
40
40
|
return null;
|
|
41
41
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
errors.push("No blocks defined");
|
|
49
|
-
}
|
|
50
|
-
if (!script.audio || !script.audio.segments || script.audio.segments.length === 0) {
|
|
51
|
-
errors.push("No audio segments defined");
|
|
42
|
+
|
|
43
|
+
// src/schemas/Media.ts
|
|
44
|
+
function flatten(blocks, out = []) {
|
|
45
|
+
for (const b of blocks) {
|
|
46
|
+
out.push(b);
|
|
47
|
+
if (b.children && b.children.length > 0) flatten(b.children, out);
|
|
52
48
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
function clipLength(clip) {
|
|
52
|
+
if (clip.clipEnd == null) return null;
|
|
53
|
+
return Math.max(0, clip.clipEnd - (clip.clipStart ?? 0));
|
|
54
|
+
}
|
|
55
|
+
function baseTimelineEnd(doc) {
|
|
56
|
+
const blockEnd = flatten(doc.blocks).reduce(
|
|
57
|
+
(max, b) => Math.max(max, b.startTime + b.duration),
|
|
58
|
+
0
|
|
59
|
+
);
|
|
60
|
+
return Math.max(doc.duration ?? 0, blockEnd);
|
|
61
|
+
}
|
|
62
|
+
function resolveMediaSchedule(doc) {
|
|
63
|
+
const out = [];
|
|
64
|
+
const docEnd = baseTimelineEnd(doc);
|
|
65
|
+
for (const block of flatten(doc.blocks)) {
|
|
66
|
+
const blockEnd = block.startTime + block.duration;
|
|
67
|
+
for (const clip of block.media ?? []) {
|
|
68
|
+
const start = block.startTime + clip.startAt;
|
|
69
|
+
const len = clipLength(clip);
|
|
70
|
+
let end;
|
|
71
|
+
if (clip.spillover) {
|
|
72
|
+
end = len != null ? start + len : blockEnd;
|
|
73
|
+
} else {
|
|
74
|
+
end = len != null ? Math.min(start + len, blockEnd) : blockEnd;
|
|
75
|
+
}
|
|
76
|
+
out.push({
|
|
77
|
+
id: clip.id,
|
|
78
|
+
src: clip.src,
|
|
79
|
+
kind: clip.kind,
|
|
80
|
+
absoluteStart: start,
|
|
81
|
+
absoluteEnd: Math.max(start, end),
|
|
82
|
+
sourceIn: clip.clipStart ?? 0,
|
|
83
|
+
anchor: "block",
|
|
84
|
+
blockId: block.id,
|
|
85
|
+
...clip.sourceLine != null ? { sourceLine: clip.sourceLine } : {}
|
|
86
|
+
});
|
|
58
87
|
}
|
|
59
|
-
covered = Math.max(covered, block.startTime + block.duration);
|
|
60
88
|
}
|
|
61
|
-
|
|
62
|
-
|
|
89
|
+
for (const clip of doc.documentMedia ?? []) {
|
|
90
|
+
const start = clip.startAt;
|
|
91
|
+
const len = clipLength(clip);
|
|
92
|
+
const end = len != null ? start + len : docEnd;
|
|
93
|
+
out.push({
|
|
94
|
+
id: clip.id,
|
|
95
|
+
src: clip.src,
|
|
96
|
+
kind: clip.kind,
|
|
97
|
+
absoluteStart: start,
|
|
98
|
+
absoluteEnd: Math.max(start, end),
|
|
99
|
+
sourceIn: clip.clipStart ?? 0,
|
|
100
|
+
anchor: "document",
|
|
101
|
+
...clip.sourceLine != null ? { sourceLine: clip.sourceLine } : {}
|
|
102
|
+
});
|
|
63
103
|
}
|
|
64
|
-
return
|
|
104
|
+
return out;
|
|
105
|
+
}
|
|
106
|
+
function getDocPlaybackDuration(doc) {
|
|
107
|
+
const base = baseTimelineEnd(doc);
|
|
108
|
+
const mediaEnd = resolveMediaSchedule(doc).reduce((max, c) => Math.max(max, c.absoluteEnd), 0);
|
|
109
|
+
return Math.max(base, mediaEnd);
|
|
65
110
|
}
|
|
66
111
|
|
|
67
112
|
// src/schemas/themeCompile.ts
|
|
@@ -142,8 +187,10 @@ function deriveColorPalette(seeds, partialColors = {}, opts = {}) {
|
|
|
142
187
|
};
|
|
143
188
|
}
|
|
144
189
|
function compileTheme(partial, opts = {}) {
|
|
145
|
-
const
|
|
190
|
+
const base = opts.base ?? STARTER_THEME;
|
|
191
|
+
const merged = createTheme(base, partial);
|
|
146
192
|
merged.schemaVersion = THEME_SCHEMA_VERSION;
|
|
193
|
+
if (opts.base && !merged.basedOn) merged.basedOn = opts.base.id;
|
|
147
194
|
const partialTypography = partial.typography;
|
|
148
195
|
if (partialTypography) {
|
|
149
196
|
if (partialTypography.titleFont !== void 0) {
|
|
@@ -156,6 +203,9 @@ function compileTheme(partial, opts = {}) {
|
|
|
156
203
|
merged.typography.monoFont = partialTypography.monoFont;
|
|
157
204
|
}
|
|
158
205
|
}
|
|
206
|
+
if (partial.colorSchemes !== void 0) {
|
|
207
|
+
merged.colorSchemes = partial.colorSchemes;
|
|
208
|
+
}
|
|
159
209
|
if (merged.seedColors) {
|
|
160
210
|
const partialColors = partial.colors ?? {};
|
|
161
211
|
merged.colors = deriveColorPalette(merged.seedColors, partialColors, {
|
|
@@ -183,11 +233,12 @@ export {
|
|
|
183
233
|
getSegmentAtTime,
|
|
184
234
|
getBlockAtTime,
|
|
185
235
|
getCaptionAtTime,
|
|
186
|
-
|
|
236
|
+
resolveMediaSchedule,
|
|
237
|
+
getDocPlaybackDuration,
|
|
187
238
|
STARTER_THEME,
|
|
188
239
|
deriveColorPalette,
|
|
189
240
|
compileTheme,
|
|
190
241
|
parseTheme,
|
|
191
242
|
serializeTheme
|
|
192
243
|
};
|
|
193
|
-
//# sourceMappingURL=chunk-
|
|
244
|
+
//# sourceMappingURL=chunk-A4O7GIWE.js.map
|