@bendyline/squisq 1.5.1 → 2.0.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.
- package/README.md +63 -10
- package/dist/{Doc-BBVGq1_9.d.ts → Doc-ylqWKDc_.d.ts} +248 -37
- package/dist/{ImageEditDoc-FuyGtt6o.d.ts → ImageEditDoc-D828IXJ1.d.ts} +1 -1
- package/dist/annotationCoercion-Dr_DY9Hx.d.ts +86 -0
- package/dist/{chunk-VWUFZ6ZG.js → chunk-3E5F2XMR.js} +2 -29
- package/dist/chunk-3E5F2XMR.js.map +1 -0
- package/dist/chunk-4X3JQXNM.js +35 -0
- package/dist/chunk-4X3JQXNM.js.map +1 -0
- package/dist/{chunk-2OIBZYKP.js → chunk-5DKBFXPY.js} +31 -121
- package/dist/chunk-5DKBFXPY.js.map +1 -0
- package/dist/{chunk-DLXZMS5K.js → chunk-5J4HKRJF.js} +51 -3
- package/dist/chunk-5J4HKRJF.js.map +1 -0
- package/dist/chunk-5UYI3RJB.js +2503 -0
- package/dist/chunk-5UYI3RJB.js.map +1 -0
- package/dist/{chunk-TEEEILMP.js → chunk-66QO7XFB.js} +19 -292
- package/dist/chunk-66QO7XFB.js.map +1 -0
- package/dist/{chunk-KKNUBQ6Y.js → chunk-6PJRB2CF.js} +156 -91
- package/dist/chunk-6PJRB2CF.js.map +1 -0
- package/dist/{chunk-HWVFJAAH.js → chunk-AMIR72JP.js} +28 -10
- package/dist/chunk-AMIR72JP.js.map +1 -0
- package/dist/chunk-FVJUUTEM.js +74 -0
- package/dist/chunk-FVJUUTEM.js.map +1 -0
- package/dist/{chunk-LH7I6SH7.js → chunk-GF3BVNLU.js} +6360 -5723
- package/dist/chunk-GF3BVNLU.js.map +1 -0
- package/dist/{chunk-Q3ROPT5H.js → chunk-GZY66SDN.js} +1 -1
- package/dist/{chunk-Q3ROPT5H.js.map → chunk-GZY66SDN.js.map} +1 -1
- package/dist/chunk-JOQYPRDZ.js +127 -0
- package/dist/chunk-JOQYPRDZ.js.map +1 -0
- package/dist/{chunk-A4O7GIWE.js → chunk-LYNJEOAC.js} +10 -73
- package/dist/chunk-LYNJEOAC.js.map +1 -0
- package/dist/{chunk-LDQ2HJIX.js → chunk-PXLVXTLW.js} +252 -54
- package/dist/chunk-PXLVXTLW.js.map +1 -0
- package/dist/{chunk-TQWLI6S2.js → chunk-Q7LROV2H.js} +4 -2
- package/dist/{chunk-TQWLI6S2.js.map → chunk-Q7LROV2H.js.map} +1 -1
- package/dist/{chunk-T5UK6YOB.js → chunk-QTSJOY54.js} +60 -17
- package/dist/chunk-QTSJOY54.js.map +1 -0
- package/dist/chunk-QVANWDPH.js +2358 -0
- package/dist/chunk-QVANWDPH.js.map +1 -0
- package/dist/{chunk-6ATE2PSM.js → chunk-R2GC6RLD.js} +7 -7
- package/dist/chunk-R2GC6RLD.js.map +1 -0
- package/dist/chunk-R2YZTCLO.js +292 -0
- package/dist/chunk-R2YZTCLO.js.map +1 -0
- package/dist/chunk-S7A5D7ZJ.js +174 -0
- package/dist/chunk-S7A5D7ZJ.js.map +1 -0
- package/dist/chunk-T3TI7GSZ.js +884 -0
- package/dist/chunk-T3TI7GSZ.js.map +1 -0
- package/dist/{chunk-RUDYOTA6.js → chunk-TUWMK62B.js} +110 -4
- package/dist/chunk-TUWMK62B.js.map +1 -0
- package/dist/{chunk-IMSCRLLV.js → chunk-VR7UPJ7C.js} +35 -25
- package/dist/chunk-VR7UPJ7C.js.map +1 -0
- package/dist/doc/index.d.ts +2334 -5
- package/dist/doc/index.js +134 -34
- package/dist/generate/index.d.ts +7 -2
- package/dist/generate/index.js +1 -1
- package/dist/imageEdit/index.d.ts +9 -6
- package/dist/imageEdit/index.js +3 -3
- package/dist/index.d.ts +12 -10
- package/dist/index.js +253 -81
- package/dist/jsonForm/index.d.ts +7 -3
- package/dist/jsonForm/index.js +9 -4
- package/dist/markdown/index.d.ts +6 -70
- package/dist/markdown/index.js +13 -9
- package/dist/narration/index.d.ts +618 -0
- package/dist/narration/index.js +82 -0
- package/dist/recommend/index.d.ts +15 -1
- package/dist/recommend/index.js +2 -2
- package/dist/schemas/index.d.ts +28 -6
- package/dist/schemas/index.js +15 -13
- package/dist/spatial/index.d.ts +0 -1
- package/dist/spatial/index.js +1 -1
- package/dist/storage/index.js +1 -1
- package/dist/{themeLibrary-CehcJhzz.d.ts → themeLibrary-RjVgLpUi.d.ts} +10 -11
- package/dist/timing/index.js +5 -3
- package/dist/transform/index.d.ts +35 -24
- package/dist/transform/index.js +9 -10
- package/dist/{types-DlAZ7MW4.d.ts → types-BwMPl2CB.d.ts} +1 -1
- package/dist/{types-D98li1gV.d.ts → types-C4kiRn_k.d.ts} +3 -1
- package/dist/versions/index.d.ts +2 -1
- package/dist/versions/index.js +2 -2
- package/package.json +6 -6
- package/src/__tests__/applyNarrationTiming.test.ts +190 -0
- package/src/__tests__/applyRenderStyle.test.ts +3 -3
- package/src/__tests__/asciiDiagramBattle.test.ts +246 -0
- package/src/__tests__/asciiDiagramDetect.test.ts +162 -0
- package/src/__tests__/asciiDiagramMapping.test.ts +38 -0
- package/src/__tests__/asciiDiagramParse.test.ts +233 -0
- package/src/__tests__/asciiDiagramPipeline.test.ts +273 -0
- package/src/__tests__/asciiDiagramRender.test.ts +162 -0
- package/src/__tests__/asciiDiagramRepair.test.ts +84 -0
- package/src/__tests__/asciiTimelineDetect.test.ts +157 -0
- package/src/__tests__/asciiTimelineParse.test.ts +205 -0
- package/src/__tests__/asciiTimelinePipeline.test.ts +349 -0
- package/src/__tests__/asciiTimelineRender.test.ts +376 -0
- package/src/__tests__/attrTokens.test.ts +5 -0
- package/src/__tests__/contentContainer.test.ts +43 -0
- package/src/__tests__/coverBlock.test.ts +33 -2
- package/src/__tests__/customTemplatesFrontmatter.test.ts +0 -15
- package/src/__tests__/dataTable.test.ts +25 -15
- package/src/__tests__/diagramBlock.test.ts +3 -3
- package/src/__tests__/diagramText.test.ts +26 -0
- package/src/__tests__/docToMarkdownMedia.test.ts +83 -0
- package/src/__tests__/drawingBlock.test.ts +3 -3
- package/src/__tests__/fixtures/asciiDiagramBattle.ts +512 -0
- package/src/__tests__/fixtures/asciiDiagrams.ts +287 -0
- package/src/__tests__/fixtures/asciiTimelines.ts +35 -0
- package/src/__tests__/fixtures/treeviewFixtures.ts +243 -0
- package/src/__tests__/getLayers.test.ts +32 -18
- package/src/__tests__/getLayersFallback.test.ts +8 -8
- package/src/__tests__/imageEditVersions.test.ts +2 -0
- package/src/__tests__/jsonForm.pointer.test.ts +47 -1
- package/src/__tests__/layoutBlock.test.ts +3 -3
- package/src/__tests__/majorApiTypes.test.ts +18 -0
- package/src/__tests__/markdown.test.ts +41 -0
- package/src/__tests__/markdownToDoc.test.ts +5 -2
- package/src/__tests__/matchFontFamily.test.ts +114 -0
- package/src/__tests__/materializeBlockLayers.test.ts +135 -0
- package/src/__tests__/materializeTestUtils.ts +17 -0
- package/src/__tests__/narrationAlign.test.ts +150 -0
- package/src/__tests__/narrationFeatures.test.ts +71 -0
- package/src/__tests__/narrationNuclei.test.ts +45 -0
- package/src/__tests__/narrationPacing.test.ts +189 -0
- package/src/__tests__/narrationScript.test.ts +149 -0
- package/src/__tests__/narrationSessionRepro.test.ts +62 -0
- package/src/__tests__/narrationSidecar.test.ts +88 -0
- package/src/__tests__/narrationSyllables.test.ts +65 -0
- package/src/__tests__/narrationTestSignals.ts +167 -0
- package/src/__tests__/narrationVad.test.ts +66 -0
- package/src/__tests__/resolveDocTheme.test.ts +16 -2
- package/src/__tests__/rootExports.test.ts +18 -0
- package/src/__tests__/scopedContentContainer.test.ts +20 -0
- package/src/__tests__/shapeGeometry.test.ts +49 -0
- package/src/__tests__/templateAnnotationParse.test.ts +10 -0
- package/src/__tests__/templateInputs.test.ts +69 -0
- package/src/__tests__/templateMetadata.test.ts +2 -1
- package/src/__tests__/templates.test.ts +143 -17
- package/src/__tests__/themeCompile.test.ts +49 -22
- package/src/__tests__/transformProvenance.test.ts +160 -0
- package/src/__tests__/transformV2.test.ts +40 -14
- package/src/__tests__/treeviewBattle.test.ts +76 -0
- package/src/__tests__/treeviewDetect.test.ts +168 -0
- package/src/__tests__/treeviewParse.test.ts +118 -0
- package/src/__tests__/treeviewPipeline.test.ts +183 -0
- package/src/__tests__/treeviewRender.test.ts +91 -0
- package/src/__tests__/versions.test.ts +44 -0
- package/src/doc/applyNarrationTiming.ts +244 -0
- package/src/doc/asciiDiagram/chars.ts +210 -0
- package/src/doc/asciiDiagram/detect.ts +175 -0
- package/src/doc/asciiDiagram/index.ts +38 -0
- package/src/doc/asciiDiagram/mapping.ts +225 -0
- package/src/doc/asciiDiagram/parse.ts +1095 -0
- package/src/doc/asciiDiagram/render.ts +963 -0
- package/src/doc/asciiDiagram/repair.ts +400 -0
- package/src/doc/asciiDiagram/types.ts +65 -0
- package/src/doc/asciiTimeline/detect.ts +138 -0
- package/src/doc/asciiTimeline/index.ts +26 -0
- package/src/doc/asciiTimeline/mapping.ts +43 -0
- package/src/doc/asciiTimeline/parse.ts +870 -0
- package/src/doc/asciiTimeline/render.ts +427 -0
- package/src/doc/asciiTimeline/types.ts +73 -0
- package/src/doc/asciiTimeline/wrappedFlow.ts +427 -0
- package/src/doc/audioMapping.ts +60 -46
- package/src/doc/customTemplatesFrontmatter.ts +0 -41
- package/src/doc/docToMarkdown.ts +126 -39
- package/src/doc/index.ts +76 -4
- package/src/doc/markdownToDoc.ts +135 -1
- package/src/doc/materializeBlockLayers.ts +304 -0
- package/src/doc/mediaAnnotations.ts +20 -0
- package/src/doc/resolveDocTheme.ts +8 -7
- package/src/doc/templateInputs.ts +185 -2
- package/src/doc/templates/__tests__/customTemplateRegistry.test.ts +22 -8
- package/src/doc/templates/coverBlock.ts +49 -13
- package/src/doc/templates/diagramBlock.ts +237 -38
- package/src/doc/templates/drawingBlock.ts +2 -2
- package/src/doc/templates/fallbackBlock.ts +1 -1
- package/src/doc/templates/index.ts +134 -279
- package/src/doc/templates/inputDescriptors.ts +20 -1
- package/src/doc/templates/metadata.ts +10 -0
- package/src/doc/templates/persistentLayers.ts +12 -126
- package/src/doc/templates/registry.ts +88 -0
- package/src/doc/templates/templateNames.ts +22 -0
- package/src/doc/templates/timelineBlock.ts +613 -0
- package/src/doc/templates/treeBlock.ts +131 -0
- package/src/doc/treeview/chars.ts +72 -0
- package/src/doc/treeview/detect.ts +145 -0
- package/src/doc/treeview/index.ts +30 -0
- package/src/doc/treeview/mapping.ts +95 -0
- package/src/doc/treeview/parse.ts +201 -0
- package/src/doc/treeview/render.ts +41 -0
- package/src/doc/treeview/types.ts +43 -0
- package/src/doc/utils/applyRenderStyle.ts +2 -9
- package/src/doc/utils/diagramText.ts +78 -0
- package/src/doc/utils/shapeGeometry.ts +157 -7
- package/src/doc/validate.ts +1 -1
- package/src/generate/templateMapper.ts +7 -0
- package/src/imageEdit/export.ts +6 -5
- package/src/imageEdit/versionPaths.ts +13 -26
- package/src/imageEdit/versions.ts +21 -100
- package/src/index.ts +1 -0
- package/src/internal/immutable.ts +58 -0
- package/src/jsonForm/index.ts +9 -1
- package/src/jsonForm/pointer.ts +68 -23
- package/src/markdown/attrTokens.ts +7 -5
- package/src/markdown/convert.ts +11 -1
- package/src/markdown/index.ts +1 -0
- package/src/markdown/stringify.ts +4 -9
- package/src/markdown/utils.ts +36 -14
- package/src/narration/align.ts +514 -0
- package/src/narration/features.ts +231 -0
- package/src/narration/index.ts +86 -0
- package/src/narration/nuclei.ts +118 -0
- package/src/narration/pacing.ts +211 -0
- package/src/narration/script.ts +239 -0
- package/src/narration/session.ts +100 -0
- package/src/narration/sidecar.ts +187 -0
- package/src/narration/syllables.ts +85 -0
- package/src/narration/trace.ts +50 -0
- package/src/narration/types.ts +299 -0
- package/src/narration/vad.ts +131 -0
- package/src/recommend/numberHighlight.ts +25 -0
- package/src/recommend/templates.ts +108 -12
- package/src/schemas/BlockTemplates.ts +156 -7
- package/src/schemas/Doc.ts +72 -9
- package/src/schemas/Media.ts +12 -2
- package/src/schemas/Theme.ts +45 -25
- package/src/schemas/Viewport.ts +2 -2
- package/src/schemas/fontStacks.ts +79 -0
- package/src/schemas/themeCompile.ts +15 -0
- package/src/schemas/themeConstants.ts +2 -0
- package/src/schemas/themeLibrary.ts +25 -19
- package/src/schemas/themeValidator.ts +1 -1
- package/src/spatial/Geohash.ts +0 -3
- package/src/storage/ContentContainer.ts +17 -9
- package/src/storage/ScopedContentContainer.ts +45 -15
- package/src/transform/applyTransform.ts +5 -5
- package/src/transform/index.ts +3 -1
- package/src/transform/registry.ts +169 -49
- package/src/transform/templateSelector.ts +21 -2
- package/src/transform/timingAllocator.ts +152 -21
- package/src/transform/types.ts +14 -1
- package/src/versions/gitignore.ts +34 -0
- package/src/versions/index.ts +2 -0
- package/src/versions/operations.ts +18 -104
- package/src/versions/paths.ts +13 -28
- package/src/versions/snapshotStore.ts +202 -0
- package/src/versions/types.ts +3 -1
- package/dist/chunk-2OIBZYKP.js.map +0 -1
- package/dist/chunk-6ATE2PSM.js.map +0 -1
- package/dist/chunk-A4O7GIWE.js.map +0 -1
- package/dist/chunk-DLXZMS5K.js.map +0 -1
- package/dist/chunk-HWVFJAAH.js.map +0 -1
- package/dist/chunk-IMSCRLLV.js.map +0 -1
- package/dist/chunk-JELIRVDP.js +0 -24
- package/dist/chunk-JELIRVDP.js.map +0 -1
- package/dist/chunk-KKNUBQ6Y.js.map +0 -1
- package/dist/chunk-LDQ2HJIX.js.map +0 -1
- package/dist/chunk-LH7I6SH7.js.map +0 -1
- package/dist/chunk-RUDYOTA6.js.map +0 -1
- package/dist/chunk-T5UK6YOB.js.map +0 -1
- package/dist/chunk-TEEEILMP.js.map +0 -1
- package/dist/chunk-UY7KGQ5R.js +0 -192
- package/dist/chunk-UY7KGQ5R.js.map +0 -1
- package/dist/chunk-VLJJHDAC.js +0 -218
- package/dist/chunk-VLJJHDAC.js.map +0 -1
- package/dist/chunk-VWUFZ6ZG.js.map +0 -1
- package/dist/story/index.d.ts +0 -1777
- package/dist/story/index.js +0 -273
- package/src/doc/getLayers.ts +0 -195
- /package/dist/{story → narration}/index.js.map +0 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Clone JSON-shaped runtime configuration without relying on structuredClone
|
|
3
|
+
* (which is unavailable in some of the ES2020/browser runtimes core supports).
|
|
4
|
+
* Data objects are recreated with safe own-property definitions so even a key
|
|
5
|
+
* named `__proto__` remains ordinary data.
|
|
6
|
+
*/
|
|
7
|
+
export function cloneData<T>(value: T, seen: WeakMap<object, unknown> = new WeakMap()): T {
|
|
8
|
+
if (value === null || typeof value !== 'object') return value;
|
|
9
|
+
|
|
10
|
+
const existing = seen.get(value);
|
|
11
|
+
if (existing !== undefined) return existing as T;
|
|
12
|
+
|
|
13
|
+
const tag = Object.prototype.toString.call(value);
|
|
14
|
+
if (tag === '[object ArrayBuffer]') {
|
|
15
|
+
const source = new Uint8Array(value as unknown as ArrayBuffer);
|
|
16
|
+
const copy = new Uint8Array(source.byteLength);
|
|
17
|
+
copy.set(source);
|
|
18
|
+
return copy.buffer as T;
|
|
19
|
+
}
|
|
20
|
+
if (tag === '[object Date]') {
|
|
21
|
+
return new Date(Date.prototype.getTime.call(value)) as T;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
const copy: unknown[] = [];
|
|
26
|
+
seen.set(value, copy);
|
|
27
|
+
for (const item of value) copy.push(cloneData(item, seen));
|
|
28
|
+
return copy as T;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const source = value as Record<string, unknown>;
|
|
32
|
+
const copy: Record<string, unknown> = {};
|
|
33
|
+
seen.set(value, copy);
|
|
34
|
+
for (const key of Object.keys(source)) {
|
|
35
|
+
Object.defineProperty(copy, key, {
|
|
36
|
+
value: cloneData(source[key], seen),
|
|
37
|
+
enumerable: true,
|
|
38
|
+
configurable: true,
|
|
39
|
+
writable: true,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
return copy as T;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Recursively freeze a data graph. Cycles are supported. */
|
|
46
|
+
export function deepFreezeData<T>(value: T, seen: WeakSet<object> = new WeakSet()): T {
|
|
47
|
+
if (value === null || typeof value !== 'object' || seen.has(value)) return value;
|
|
48
|
+
seen.add(value);
|
|
49
|
+
for (const key of Object.keys(value)) {
|
|
50
|
+
deepFreezeData((value as Record<string, unknown>)[key], seen);
|
|
51
|
+
}
|
|
52
|
+
return Object.freeze(value);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** Take an immutable snapshot suitable for storing in a process registry. */
|
|
56
|
+
export function cloneAndFreezeData<T>(value: T): T {
|
|
57
|
+
return deepFreezeData(cloneData(value));
|
|
58
|
+
}
|
package/src/jsonForm/index.ts
CHANGED
|
@@ -17,7 +17,15 @@ export type {
|
|
|
17
17
|
|
|
18
18
|
export { chooseControl, primaryType, arrayItemKind } from './chooseControl.js';
|
|
19
19
|
export { evaluateWhen, resolveFlag } from './evaluateWhen.js';
|
|
20
|
-
export {
|
|
20
|
+
export {
|
|
21
|
+
escapePointerSegment,
|
|
22
|
+
appendPointer,
|
|
23
|
+
toPointer,
|
|
24
|
+
pointerSegments,
|
|
25
|
+
getByPointer,
|
|
26
|
+
setByPointer,
|
|
27
|
+
resolveRef,
|
|
28
|
+
} from './pointer.js';
|
|
21
29
|
export { inferSchema } from './inferSchema.js';
|
|
22
30
|
export type { InferSchemaOptions } from './inferSchema.js';
|
|
23
31
|
export { buildJsonFormTokens, resolveJsonFormTheme } from './tokens.js';
|
package/src/jsonForm/pointer.ts
CHANGED
|
@@ -6,27 +6,67 @@
|
|
|
6
6
|
|
|
7
7
|
import type { SquisqAnnotatedSchema } from './types.js';
|
|
8
8
|
|
|
9
|
+
/** Escape one object-member name for use as an RFC 6901 pointer segment. */
|
|
10
|
+
export function escapePointerSegment(segment: string): string {
|
|
11
|
+
return segment.replace(/~/g, '~0').replace(/\//g, '~1');
|
|
12
|
+
}
|
|
13
|
+
|
|
9
14
|
/** Normalize a path to a JSON Pointer string. Accepts both forms. */
|
|
10
15
|
export function toPointer(path: string): string {
|
|
11
|
-
if (path === ''
|
|
16
|
+
if (path === '') return '';
|
|
12
17
|
if (path.startsWith('/')) return path;
|
|
13
18
|
// Dotted form: split on `.` and escape per RFC 6901.
|
|
14
|
-
return (
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
);
|
|
19
|
+
return '/' + path.split('.').map(escapePointerSegment).join('/');
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** Append one member name to a dotted path or validated JSON Pointer. */
|
|
23
|
+
export function appendPointer(base: string, segment: string): string {
|
|
24
|
+
const pointer = toPointer(base);
|
|
25
|
+
pointerSegments(pointer); // validate explicit pointer syntax before composing
|
|
26
|
+
return `${pointer}/${escapePointerSegment(segment)}`;
|
|
21
27
|
}
|
|
22
28
|
|
|
23
29
|
/** Split a JSON Pointer into decoded segments. Empty string → []. */
|
|
24
30
|
export function pointerSegments(pointer: string): string[] {
|
|
25
31
|
if (pointer === '') return [];
|
|
32
|
+
if (!pointer.startsWith('/')) {
|
|
33
|
+
throw new TypeError(`Invalid JSON Pointer ${JSON.stringify(pointer)}: expected "" or "/..."`);
|
|
34
|
+
}
|
|
26
35
|
return pointer
|
|
27
36
|
.slice(1)
|
|
28
37
|
.split('/')
|
|
29
|
-
.map((seg) =>
|
|
38
|
+
.map((seg) => {
|
|
39
|
+
if (/~(?:[^01]|$)/.test(seg)) {
|
|
40
|
+
throw new TypeError(
|
|
41
|
+
`Invalid JSON Pointer ${JSON.stringify(pointer)}: "~" must be escaped as "~0"`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
return seg.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** Parse the canonical array-index spelling used by JSON Pointer consumers. */
|
|
49
|
+
function arrayIndex(segment: string): number | undefined {
|
|
50
|
+
if (!/^(?:0|[1-9]\d*)$/.test(segment)) return undefined;
|
|
51
|
+
const index = Number(segment);
|
|
52
|
+
// 2^32 - 1 is the Array length sentinel, not an element index.
|
|
53
|
+
return Number.isSafeInteger(index) && index <= 0xffff_fffe ? index : undefined;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function ownValue(object: object, key: string): unknown {
|
|
57
|
+
return Object.prototype.hasOwnProperty.call(object, key)
|
|
58
|
+
? (object as Record<string, unknown>)[key]
|
|
59
|
+
: undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Define a data member without invoking Object.prototype.__proto__'s setter. */
|
|
63
|
+
function setOwn(object: Record<string, unknown>, key: string, value: unknown): void {
|
|
64
|
+
Object.defineProperty(object, key, {
|
|
65
|
+
value,
|
|
66
|
+
enumerable: true,
|
|
67
|
+
configurable: true,
|
|
68
|
+
writable: true,
|
|
69
|
+
});
|
|
30
70
|
}
|
|
31
71
|
|
|
32
72
|
/** Read a value from data by path (dotted or pointer). Returns undefined if missing. */
|
|
@@ -36,11 +76,11 @@ export function getByPointer(data: unknown, path: string): unknown {
|
|
|
36
76
|
for (const seg of segments) {
|
|
37
77
|
if (cur === null || cur === undefined) return undefined;
|
|
38
78
|
if (Array.isArray(cur)) {
|
|
39
|
-
const idx =
|
|
40
|
-
if (
|
|
79
|
+
const idx = arrayIndex(seg);
|
|
80
|
+
if (idx === undefined || idx >= cur.length) return undefined;
|
|
41
81
|
cur = cur[idx];
|
|
42
82
|
} else if (typeof cur === 'object') {
|
|
43
|
-
cur = (cur
|
|
83
|
+
cur = ownValue(cur, seg);
|
|
44
84
|
} else {
|
|
45
85
|
return undefined;
|
|
46
86
|
}
|
|
@@ -65,10 +105,13 @@ function setRec(node: unknown, segments: string[], i: number, value: unknown): u
|
|
|
65
105
|
|
|
66
106
|
// Decide whether the slot we're about to write into is an array index.
|
|
67
107
|
const nextSeg = isLast ? undefined : segments[i + 1];
|
|
68
|
-
const writingArrayChild = nextSeg !== undefined &&
|
|
108
|
+
const writingArrayChild = nextSeg !== undefined && arrayIndex(nextSeg) !== undefined;
|
|
69
109
|
|
|
70
110
|
if (Array.isArray(node)) {
|
|
71
|
-
const idx =
|
|
111
|
+
const idx = arrayIndex(seg);
|
|
112
|
+
if (idx === undefined) {
|
|
113
|
+
throw new TypeError(`Invalid array index ${JSON.stringify(seg)} in JSON Pointer`);
|
|
114
|
+
}
|
|
72
115
|
const next = node.slice();
|
|
73
116
|
next[idx] = isLast
|
|
74
117
|
? value
|
|
@@ -80,9 +123,12 @@ function setRec(node: unknown, segments: string[], i: number, value: unknown): u
|
|
|
80
123
|
string,
|
|
81
124
|
unknown
|
|
82
125
|
>;
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
126
|
+
const previous = ownValue(obj, seg);
|
|
127
|
+
setOwn(
|
|
128
|
+
obj,
|
|
129
|
+
seg,
|
|
130
|
+
isLast ? value : setRec(previous ?? (writingArrayChild ? [] : {}), segments, i + 1, value),
|
|
131
|
+
);
|
|
86
132
|
return obj;
|
|
87
133
|
}
|
|
88
134
|
|
|
@@ -98,12 +144,11 @@ export function resolveRef(
|
|
|
98
144
|
): SquisqAnnotatedSchema | undefined {
|
|
99
145
|
if (!schema.$ref) return schema;
|
|
100
146
|
const ref = schema.$ref;
|
|
147
|
+
if (ref === '#') return root;
|
|
101
148
|
if (!ref.startsWith('#/')) return undefined;
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
cur = (cur as Record<string, unknown>)[seg];
|
|
149
|
+
try {
|
|
150
|
+
return getByPointer(root, ref.slice(1)) as SquisqAnnotatedSchema | undefined;
|
|
151
|
+
} catch {
|
|
152
|
+
return undefined;
|
|
107
153
|
}
|
|
108
|
-
return (cur as SquisqAnnotatedSchema | undefined) ?? undefined;
|
|
109
154
|
}
|
|
@@ -38,19 +38,21 @@ const SQ_RUN = `'(?:[^'\\\\]|\\\\.)*'`;
|
|
|
38
38
|
/**
|
|
39
39
|
* Quote-aware match for a trailing `{[…]}` annotation: the interior is any
|
|
40
40
|
* mix of quoted runs and characters other than `]` and quotes, so quoted
|
|
41
|
-
* values may contain `]`.
|
|
42
|
-
*
|
|
41
|
+
* values may contain `]`. An empty interior is valid so `{[]}` can remain
|
|
42
|
+
* lossless metadata instead of visible heading text. The trailing
|
|
43
|
+
* `[\s\]}]*` tolerates an accidental doubled `]}` that users sometimes type
|
|
44
|
+
* when learning the syntax.
|
|
43
45
|
*/
|
|
44
46
|
const TEMPLATE_STRICT_RE = new RegExp(
|
|
45
|
-
`\\s*\\{\\[((?:[^\\]"']|${DQ_RUN}|${SQ_RUN})
|
|
47
|
+
`\\s*\\{\\[((?:[^\\]"']|${DQ_RUN}|${SQ_RUN})*)\\]\\}[\\s\\]}]*$`,
|
|
46
48
|
);
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
|
-
* Legacy fallback matching the pre-quote-aware grammar (`[^\]]
|
|
51
|
+
* Legacy fallback matching the pre-quote-aware grammar (`[^\]]*` interior).
|
|
50
52
|
* Tried after the strict form so inputs with unbalanced quotes keep parsing
|
|
51
53
|
* exactly as they did before quote support landed.
|
|
52
54
|
*/
|
|
53
|
-
const TEMPLATE_LEGACY_RE = /\s*\{\[([^\]]
|
|
55
|
+
const TEMPLATE_LEGACY_RE = /\s*\{\[([^\]]*)\]\}[\s\]}]*$/;
|
|
54
56
|
|
|
55
57
|
/**
|
|
56
58
|
* Quote-aware match for a trailing Pandoc `{…}` block. The negative
|
package/src/markdown/convert.ts
CHANGED
|
@@ -376,6 +376,14 @@ export function serializePandocAttributes(attrs: HeadingAttributes): string | nu
|
|
|
376
376
|
return `{${parts.join(' ')}}`;
|
|
377
377
|
}
|
|
378
378
|
|
|
379
|
+
function hasPrintablePandocAttributes(attrs: HeadingAttributes): boolean {
|
|
380
|
+
return Boolean(
|
|
381
|
+
attrs.id ||
|
|
382
|
+
(attrs.classes && attrs.classes.length > 0) ||
|
|
383
|
+
(attrs.params && Object.keys(attrs.params).length > 0),
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
|
|
379
387
|
/**
|
|
380
388
|
* Serialize a HeadingTemplateAnnotation back to `{[templateName key=value …]}` text.
|
|
381
389
|
* Values are quoted via the shared {@link quoteAttrValue} rule (same as
|
|
@@ -977,7 +985,9 @@ function blockToMdast(node: MarkdownBlockNode): MdastNode {
|
|
|
977
985
|
const suffixes: string[] = [];
|
|
978
986
|
if (node.attributes) {
|
|
979
987
|
const pandoc = serializePandocAttributes(node.attributes);
|
|
980
|
-
if (pandoc != null
|
|
988
|
+
if (pandoc != null && (pandoc !== '{}' || hasPrintablePandocAttributes(node.attributes))) {
|
|
989
|
+
suffixes.push(pandoc);
|
|
990
|
+
}
|
|
981
991
|
}
|
|
982
992
|
if (node.templateAnnotation) {
|
|
983
993
|
suffixes.push(serializeTemplateAnnotation(node.templateAnnotation));
|
package/src/markdown/index.ts
CHANGED
|
@@ -12,7 +12,7 @@ import remarkMath from 'remark-math';
|
|
|
12
12
|
import remarkDirective from 'remark-directive';
|
|
13
13
|
import type { MarkdownDocument, StringifyOptions } from './types.js';
|
|
14
14
|
import { toMdast } from './convert.js';
|
|
15
|
-
import {
|
|
15
|
+
import { formatFrontmatterValue } from './utils.js';
|
|
16
16
|
|
|
17
17
|
// Cache the default processor (all extensions, default formatting) to avoid rebuilding on every call.
|
|
18
18
|
let defaultProcessor: any;
|
|
@@ -33,7 +33,7 @@ const SQ_RUN = `'(?:[^'\\\\]|\\\\.)*'`;
|
|
|
33
33
|
* and quoted runs (which may themselves contain `]`).
|
|
34
34
|
*/
|
|
35
35
|
const ESCAPED_TEMPLATE_SPAN_RE = new RegExp(
|
|
36
|
-
`\\{\\\\\\[((?:${DQ_RUN}|${SQ_RUN}|\\\\.|[^\\]\\\\])
|
|
36
|
+
`\\{\\\\\\[((?:${DQ_RUN}|${SQ_RUN}|\\\\.|[^\\]\\\\])*)\\]\\}`,
|
|
37
37
|
'g',
|
|
38
38
|
);
|
|
39
39
|
|
|
@@ -195,13 +195,8 @@ export function stringifyMarkdown(doc: MarkdownDocument, options?: StringifyOpti
|
|
|
195
195
|
// Prepend YAML frontmatter if present
|
|
196
196
|
if (doc.frontmatter && Object.keys(doc.frontmatter).length > 0) {
|
|
197
197
|
const yamlLines = Object.entries(doc.frontmatter).map(([k, v]) => {
|
|
198
|
-
if (typeof v === 'string') {
|
|
199
|
-
|
|
200
|
-
// `formatFrontmatterValue` / `setFrontmatterValues` so both paths
|
|
201
|
-
// agree; single-line strings stay verbatim as before.
|
|
202
|
-
return /[\r\n]/.test(v)
|
|
203
|
-
? `${k}: ${formatBlockScalar(v.replace(/\r\n?/g, '\n'))}`
|
|
204
|
-
: `${k}: ${v}`;
|
|
198
|
+
if (typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') {
|
|
199
|
+
return `${k}: ${formatFrontmatterValue(v)}`;
|
|
205
200
|
}
|
|
206
201
|
return `${k}: ${JSON.stringify(v)}`;
|
|
207
202
|
});
|
package/src/markdown/utils.ts
CHANGED
|
@@ -126,6 +126,17 @@ export function parseFrontmatter(yaml: string): Record<string, unknown> | null {
|
|
|
126
126
|
const result: Record<string, unknown> = {};
|
|
127
127
|
const lines = yaml.split('\n');
|
|
128
128
|
|
|
129
|
+
// Define keys as data properties so a frontmatter key named `__proto__`
|
|
130
|
+
// cannot invoke Object.prototype's legacy setter.
|
|
131
|
+
const assign = (key: string, value: unknown): void => {
|
|
132
|
+
Object.defineProperty(result, key, {
|
|
133
|
+
value,
|
|
134
|
+
enumerable: true,
|
|
135
|
+
configurable: true,
|
|
136
|
+
writable: true,
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
|
|
129
140
|
for (let i = 0; i < lines.length; i++) {
|
|
130
141
|
const line = lines[i];
|
|
131
142
|
const trimmed = line.trim();
|
|
@@ -135,7 +146,7 @@ export function parseFrontmatter(yaml: string): Record<string, unknown> | null {
|
|
|
135
146
|
if (colonIdx < 1) continue;
|
|
136
147
|
|
|
137
148
|
const key = trimmed.slice(0, colonIdx).trim();
|
|
138
|
-
|
|
149
|
+
const value = trimmed.slice(colonIdx + 1).trim();
|
|
139
150
|
|
|
140
151
|
// YAML literal block scalar: `key: |` or `key: |-`. Subsequent lines
|
|
141
152
|
// that are more-indented than the key belong to the scalar; they are
|
|
@@ -172,27 +183,37 @@ export function parseFrontmatter(yaml: string): Record<string, unknown> | null {
|
|
|
172
183
|
commonIndent = Math.min(commonIndent, bl.length - bl.trimStart().length);
|
|
173
184
|
}
|
|
174
185
|
if (!isFinite(commonIndent)) commonIndent = 0;
|
|
175
|
-
|
|
186
|
+
assign(key, blockLines.map((bl) => (bl === '' ? '' : bl.slice(commonIndent))).join('\n'));
|
|
176
187
|
continue;
|
|
177
188
|
}
|
|
178
189
|
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
) {
|
|
184
|
-
|
|
190
|
+
// Quoted scalars are always strings, even when their contents look like a
|
|
191
|
+
// number or boolean. Double-quoted values emitted by
|
|
192
|
+
// formatFrontmatterValue use JSON-compatible escaping; single-quoted YAML
|
|
193
|
+
// represents a literal quote as two consecutive quotes.
|
|
194
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
195
|
+
try {
|
|
196
|
+
assign(key, JSON.parse(value));
|
|
197
|
+
} catch {
|
|
198
|
+
// Be permissive for hand-authored YAML outside our formatter's subset.
|
|
199
|
+
assign(key, value.slice(1, -1));
|
|
200
|
+
}
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
if (value.startsWith("'") && value.endsWith("'")) {
|
|
204
|
+
assign(key, value.slice(1, -1).replace(/''/g, "'"));
|
|
205
|
+
continue;
|
|
185
206
|
}
|
|
186
207
|
|
|
187
208
|
// Parse booleans and numbers
|
|
188
209
|
if (value === 'true') {
|
|
189
|
-
|
|
210
|
+
assign(key, true);
|
|
190
211
|
} else if (value === 'false') {
|
|
191
|
-
|
|
212
|
+
assign(key, false);
|
|
192
213
|
} else if (value !== '' && !isNaN(Number(value))) {
|
|
193
|
-
|
|
214
|
+
assign(key, Number(value));
|
|
194
215
|
} else {
|
|
195
|
-
|
|
216
|
+
assign(key, value);
|
|
196
217
|
}
|
|
197
218
|
}
|
|
198
219
|
|
|
@@ -217,7 +238,7 @@ export function formatBlockScalar(value: string): string {
|
|
|
217
238
|
const FRONTMATTER_BLOCK_RE = /^---\r?\n([\s\S]*?)\r?\n---(\r?\n)?/;
|
|
218
239
|
|
|
219
240
|
/** Quote a frontmatter scalar so it round-trips cleanly through `parseFrontmatter`. */
|
|
220
|
-
function formatFrontmatterValue(value: string | number | boolean): string {
|
|
241
|
+
export function formatFrontmatterValue(value: string | number | boolean): string {
|
|
221
242
|
if (typeof value === 'boolean' || typeof value === 'number') return String(value);
|
|
222
243
|
// Multi-line strings can't live on one line — emit a YAML literal block
|
|
223
244
|
// scalar (`|-`) that `parseFrontmatter` reads back. This carries pretty-
|
|
@@ -239,7 +260,8 @@ function formatFrontmatterValue(value: string | number | boolean): string {
|
|
|
239
260
|
value === '' ||
|
|
240
261
|
value === 'true' ||
|
|
241
262
|
value === 'false' ||
|
|
242
|
-
|
|
263
|
+
(value !== '' && !Number.isNaN(Number(value))) ||
|
|
264
|
+
(/^["']/.test(value) && /["']$/.test(value));
|
|
243
265
|
if (!needsQuote) return value;
|
|
244
266
|
// Prefer double quotes; escape any embedded double-quote / backslash.
|
|
245
267
|
const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|