@storystick/preview 0.0.0-stage → 0.2.0-rc.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/LICENSE +112 -0
- package/PUBLIC-API.md +56 -0
- package/README.md +42 -2
- package/dist/.source-hash +1 -0
- package/dist/authoring/runtime-contract.d.ts +92 -0
- package/dist/authoring/runtime-contract.js +28 -0
- package/dist/channel-taxonomy.d.ts +17 -0
- package/dist/channel-taxonomy.js +46 -0
- package/dist/context-command.d.ts +18 -0
- package/dist/context-command.js +38 -0
- package/dist/dev/suspicious-codeword-detector.d.ts +15 -0
- package/dist/dev/suspicious-codeword-detector.js +220 -0
- package/dist/dev/suspicious-codeword-detector.noop.d.ts +3 -0
- package/dist/dev/suspicious-codeword-detector.noop.js +4 -0
- package/dist/framework/browser-context.d.ts +2 -0
- package/dist/framework/browser-context.js +2 -0
- package/dist/framework/generation-refresh.d.ts +23 -0
- package/dist/framework/generation-refresh.js +82 -0
- package/dist/framework/generation-signal.d.ts +8 -0
- package/dist/framework/generation-signal.js +17 -0
- package/dist/framework/projection-generation-watch.d.ts +5 -0
- package/dist/framework/projection-generation-watch.js +53 -0
- package/dist/framework/render-receipt.d.ts +8 -0
- package/dist/framework/render-receipt.js +10 -0
- package/dist/framework/runtime-adapter-store.d.ts +30 -0
- package/dist/framework/runtime-adapter-store.js +81 -0
- package/dist/grant-coordinator.d.ts +53 -0
- package/dist/grant-coordinator.js +329 -0
- package/dist/grant-lifecycle.d.ts +52 -0
- package/dist/grant-lifecycle.js +74 -0
- package/dist/identity/__test-adapter.d.ts +34 -0
- package/dist/identity/__test-adapter.js +59 -0
- package/dist/identity/browser-context-gate.d.ts +4 -0
- package/dist/identity/browser-context-gate.js +22 -0
- package/dist/identity/browser-context.d.ts +13 -0
- package/dist/identity/browser-context.js +22 -0
- package/dist/identity/carriers/postmessage.d.ts +84 -0
- package/dist/identity/carriers/postmessage.js +177 -0
- package/dist/identity/carriers/storage-state.d.ts +41 -0
- package/dist/identity/carriers/storage-state.js +99 -0
- package/dist/identity/carriers/storage.d.ts +39 -0
- package/dist/identity/carriers/storage.js +182 -0
- package/dist/identity/carriers/url.d.ts +19 -0
- package/dist/identity/carriers/url.js +38 -0
- package/dist/identity/identity-state-machine.d.ts +70 -0
- package/dist/identity/identity-state-machine.js +244 -0
- package/dist/identity/identity-types.d.ts +192 -0
- package/dist/identity/identity-types.js +22 -0
- package/dist/identity/index.d.ts +18 -0
- package/dist/identity/index.js +16 -0
- package/dist/identity/preview-cookie.d.ts +9 -0
- package/dist/identity/preview-cookie.js +12 -0
- package/dist/identity/preview-draft.d.ts +6 -0
- package/dist/identity/preview-draft.js +25 -0
- package/dist/identity/preview-identity.d.ts +28 -0
- package/dist/identity/preview-identity.js +84 -0
- package/dist/identity/preview-scope.d.ts +28 -0
- package/dist/identity/preview-scope.js +47 -0
- package/dist/identity/resolver.d.ts +110 -0
- package/dist/identity/resolver.js +334 -0
- package/dist/identity/session-query.d.ts +4 -0
- package/dist/identity/session-query.js +19 -0
- package/dist/index.d.ts +90 -0
- package/dist/index.js +788 -0
- package/dist/interaction-mode.d.ts +58 -0
- package/dist/interaction-mode.js +93 -0
- package/dist/nav-handler.d.ts +95 -0
- package/dist/nav-handler.js +84 -0
- package/dist/overlay-label.d.ts +79 -0
- package/dist/overlay-label.js +73 -0
- package/dist/page-presentation.d.ts +24 -0
- package/dist/page-presentation.js +32 -0
- package/dist/provenance/anchor-map.d.ts +95 -0
- package/dist/provenance/anchor-map.js +177 -0
- package/dist/provenance/carrier-plan.d.ts +51 -0
- package/dist/provenance/carrier-plan.js +90 -0
- package/dist/provenance/coded-text.d.ts +20 -0
- package/dist/provenance/coded-text.js +302 -0
- package/dist/provenance/direct-edit-locator.d.ts +29 -0
- package/dist/provenance/direct-edit-locator.js +218 -0
- package/dist/provenance/editable-leaf.d.ts +134 -0
- package/dist/provenance/editable-leaf.js +483 -0
- package/dist/provenance/editor-target.d.ts +29 -0
- package/dist/provenance/editor-target.js +46 -0
- package/dist/provenance/field-leaf-walker.d.ts +21 -0
- package/dist/provenance/field-leaf-walker.js +98 -0
- package/dist/provenance/frame-dependencies.d.ts +212 -0
- package/dist/provenance/frame-dependencies.js +550 -0
- package/dist/provenance/index.d.ts +26 -0
- package/dist/provenance/index.js +26 -0
- package/dist/provenance/local-events.d.ts +1 -0
- package/dist/provenance/local-events.js +1 -0
- package/dist/provenance/markers.d.ts +21 -0
- package/dist/provenance/markers.js +70 -0
- package/dist/provenance/multi-frame-anchor-map.d.ts +54 -0
- package/dist/provenance/multi-frame-anchor-map.js +109 -0
- package/dist/provenance/normalize.d.ts +2 -0
- package/dist/provenance/normalize.js +19 -0
- package/dist/provenance/overlay-focus-echo.d.ts +17 -0
- package/dist/provenance/overlay-focus-echo.js +16 -0
- package/dist/provenance/overlay-projection.d.ts +65 -0
- package/dist/provenance/overlay-projection.js +201 -0
- package/dist/provenance/owner-map.d.ts +55 -0
- package/dist/provenance/owner-map.js +239 -0
- package/dist/provenance/read-carrier.d.ts +22 -0
- package/dist/provenance/read-carrier.js +92 -0
- package/dist/provenance/recorder.d.ts +38 -0
- package/dist/provenance/recorder.js +82 -0
- package/dist/provenance/render-context.d.ts +106 -0
- package/dist/provenance/render-context.js +338 -0
- package/dist/provenance/reveal-contract.d.ts +44 -0
- package/dist/provenance/reveal-contract.js +1 -0
- package/dist/provenance/richtext-slice-embed-address.d.ts +20 -0
- package/dist/provenance/richtext-slice-embed-address.js +35 -0
- package/dist/provenance/runtime-script.d.ts +1 -0
- package/dist/provenance/runtime-script.js +2217 -0
- package/dist/provenance/sidecar-store.d.ts +63 -0
- package/dist/provenance/sidecar-store.js +151 -0
- package/dist/provenance/sidecar.d.ts +34 -0
- package/dist/provenance/sidecar.js +227 -0
- package/dist/provenance/slice-model.d.ts +28 -0
- package/dist/provenance/slice-model.js +86 -0
- package/dist/provenance/source-identity.d.ts +3 -0
- package/dist/provenance/source-identity.js +7 -0
- package/dist/provenance/source-map-materializer.d.ts +76 -0
- package/dist/provenance/source-map-materializer.js +286 -0
- package/dist/provenance/source-registry.d.ts +103 -0
- package/dist/provenance/source-registry.js +562 -0
- package/dist/provenance/transformed-output-gate.d.ts +161 -0
- package/dist/provenance/transformed-output-gate.js +171 -0
- package/dist/provenance/types.d.ts +102 -0
- package/dist/provenance/types.js +1 -0
- package/dist/refresh-policy.d.ts +11 -0
- package/dist/refresh-policy.js +14 -0
- package/dist/reveal-target.d.ts +8 -0
- package/dist/reveal-target.js +47 -0
- package/dist/route-paths.d.ts +11 -0
- package/dist/route-paths.js +14 -0
- package/dist/router-subscriber.d.ts +92 -0
- package/dist/router-subscriber.js +44 -0
- package/dist/runtime/anchor-types.d.ts +5 -0
- package/dist/runtime/anchor-types.js +1 -0
- package/dist/runtime/authority-store.d.ts +31 -0
- package/dist/runtime/authority-store.js +56 -0
- package/dist/runtime/boot.d.ts +52 -0
- package/dist/runtime/boot.js +379 -0
- package/dist/runtime/content-refresh.d.ts +30 -0
- package/dist/runtime/content-refresh.js +68 -0
- package/dist/runtime/emit-runtime-error.d.ts +51 -0
- package/dist/runtime/emit-runtime-error.js +64 -0
- package/dist/runtime/field-proxy.d.ts +112 -0
- package/dist/runtime/field-proxy.js +471 -0
- package/dist/runtime/form-contract-reporter.d.ts +99 -0
- package/dist/runtime/form-contract-reporter.js +391 -0
- package/dist/runtime/invalidation-adapter.d.ts +17 -0
- package/dist/runtime/invalidation-adapter.js +42 -0
- package/dist/runtime/live-query-overlay.d.ts +39 -0
- package/dist/runtime/live-query-overlay.js +345 -0
- package/dist/runtime/momentary-navigate.d.ts +16 -0
- package/dist/runtime/momentary-navigate.js +106 -0
- package/dist/runtime/observation-scheduler.d.ts +15 -0
- package/dist/runtime/observation-scheduler.js +42 -0
- package/dist/runtime/perf-metrics.d.ts +28 -0
- package/dist/runtime/perf-metrics.js +90 -0
- package/dist/runtime/preview-interaction-policy.d.ts +1 -0
- package/dist/runtime/preview-interaction-policy.js +103 -0
- package/dist/runtime/preview-overlay-painter.d.ts +27 -0
- package/dist/runtime/preview-overlay-painter.js +1732 -0
- package/dist/runtime/preview-overlay-sidecar.d.ts +26 -0
- package/dist/runtime/preview-overlay-sidecar.js +45 -0
- package/dist/runtime/preview-perspective-alignment.d.ts +15 -0
- package/dist/runtime/preview-perspective-alignment.js +40 -0
- package/dist/runtime/preview-perspective-intent-state.d.ts +23 -0
- package/dist/runtime/preview-perspective-intent-state.js +5 -0
- package/dist/runtime/preview-transition-state.d.ts +142 -0
- package/dist/runtime/preview-transition-state.js +257 -0
- package/dist/runtime/render-mode-state.d.ts +51 -0
- package/dist/runtime/render-mode-state.js +76 -0
- package/dist/runtime/request-id.d.ts +4 -0
- package/dist/runtime/request-id.js +8 -0
- package/dist/runtime/resolve-schema-field.d.ts +99 -0
- package/dist/runtime/resolve-schema-field.js +356 -0
- package/dist/runtime/runtime-error-dispatch.d.ts +23 -0
- package/dist/runtime/runtime-error-dispatch.js +75 -0
- package/dist/runtime/schema-store.d.ts +121 -0
- package/dist/runtime/schema-store.js +667 -0
- package/dist/runtime/snapshot-assembly.d.ts +104 -0
- package/dist/runtime/snapshot-assembly.js +553 -0
- package/dist/runtime/snapshot-reporter.d.ts +55 -0
- package/dist/runtime/snapshot-reporter.js +375 -0
- package/dist/runtime/standalone-overlay-controller.d.ts +33 -0
- package/dist/runtime/standalone-overlay-controller.js +281 -0
- package/dist/runtime/store.d.ts +307 -0
- package/dist/runtime/store.js +1026 -0
- package/dist/runtime/windowed-minimap-projection.d.ts +28 -0
- package/dist/runtime/windowed-minimap-projection.js +189 -0
- package/dist/runtime/workspace-shortcuts.d.ts +30 -0
- package/dist/runtime/workspace-shortcuts.js +188 -0
- package/dist/sdk-error-copy.d.ts +48 -0
- package/dist/sdk-error-copy.js +182 -0
- package/dist/select-cursor.d.ts +2 -0
- package/dist/select-cursor.js +23 -0
- package/dist/server/admission-cookie.d.ts +38 -0
- package/dist/server/admission-cookie.js +122 -0
- package/dist/server/control-client.d.ts +29 -0
- package/dist/server/control-client.js +22 -0
- package/dist/server/control-errors.d.ts +22 -0
- package/dist/server/control-errors.js +182 -0
- package/dist/server/draft-route.d.ts +12 -0
- package/dist/server/draft-route.js +45 -0
- package/dist/server/extract-token.d.ts +30 -0
- package/dist/server/extract-token.js +98 -0
- package/dist/server/grant.d.ts +128 -0
- package/dist/server/grant.js +395 -0
- package/dist/server/ordinary-read-provenance.d.ts +11 -0
- package/dist/server/ordinary-read-provenance.js +124 -0
- package/dist/server/preview-read-metadata.d.ts +10 -0
- package/dist/server/preview-read-metadata.js +27 -0
- package/dist/server/provenance-frame-builder.d.ts +53 -0
- package/dist/server/provenance-frame-builder.js +102 -0
- package/dist/server/provenance-runtime.d.ts +70 -0
- package/dist/server/provenance-runtime.js +46 -0
- package/dist/server/read-context-command.d.ts +11 -0
- package/dist/server/read-context-command.js +53 -0
- package/dist/server/read-identity.d.ts +3 -0
- package/dist/server/read-identity.js +63 -0
- package/dist/server/read-lifecycle.d.ts +17 -0
- package/dist/server/read-lifecycle.js +52 -0
- package/dist/server/render-frame-provenance.d.ts +133 -0
- package/dist/server/render-frame-provenance.js +676 -0
- package/dist/server/render-id.d.ts +15 -0
- package/dist/server/render-id.js +22 -0
- package/dist/server/route-document-provenance.d.ts +77 -0
- package/dist/server/route-document-provenance.js +1327 -0
- package/dist/server/route-session-cookie.d.ts +33 -0
- package/dist/server/route-session-cookie.js +54 -0
- package/dist/session-projections.d.ts +33 -0
- package/dist/session-projections.js +43 -0
- package/dist/session-subscriber.d.ts +124 -0
- package/dist/session-subscriber.js +79 -0
- package/dist/sha256-fallback.d.ts +30 -0
- package/dist/sha256-fallback.js +126 -0
- package/dist/types.d.ts +446 -0
- package/dist/types.js +85 -0
- package/dist/validators.d.ts +61 -0
- package/dist/validators.js +110 -0
- package/package.json +210 -3
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import { codedTextPathForCanonicalPath, classifyPreviewProvenanceCodedTextTarget, writePreviewProvenanceCodedRichTextTextNodeAtPath, writePreviewProvenanceCodedTextAtPath, writePreviewProvenanceObjectMarkerAtPath, } from './coded-text.js';
|
|
2
|
+
import { formatPreviewProvenanceCanonicalPath, walkPreviewProvenanceFieldLeaves, } from './field-leaf-walker.js';
|
|
3
|
+
import { createPreviewProvenanceAttributeCarrierPlan, createPreviewProvenanceCodedTextCarrierPlan, PREVIEW_PROVENANCE_ELEMENT_ATTRIBUTE_CARRIER, } from './carrier-plan.js';
|
|
4
|
+
import { ProvenanceRecorder } from './recorder.js';
|
|
5
|
+
import { createPublicSidecar } from './sidecar.js';
|
|
6
|
+
import { normalizePreviewVariantId } from '../identity/preview-identity.js';
|
|
7
|
+
export { formatPreviewProvenanceCanonicalPath } from './field-leaf-walker.js';
|
|
8
|
+
export function createPreviewProvenanceEditableLeaf(input) {
|
|
9
|
+
const rootPath = input.rootPath ?? [];
|
|
10
|
+
return {
|
|
11
|
+
key: input.key,
|
|
12
|
+
documentId: input.documentId,
|
|
13
|
+
...(normalizePreviewVariantId(input.variantId) !== null
|
|
14
|
+
? { variantId: normalizePreviewVariantId(input.variantId) }
|
|
15
|
+
: {}),
|
|
16
|
+
...(input.readScope !== undefined ? { readScope: input.readScope } : {}),
|
|
17
|
+
canonicalPath: formatPreviewProvenanceCanonicalPath([
|
|
18
|
+
...rootPath,
|
|
19
|
+
...input.leafPath,
|
|
20
|
+
]),
|
|
21
|
+
pathAnchors: [
|
|
22
|
+
...(input.pathAnchors ?? []),
|
|
23
|
+
...prefixPreviewProvenancePathAnchors(rootPath, input.relativePathAnchors ?? []),
|
|
24
|
+
],
|
|
25
|
+
...(input.navigationPath !== undefined
|
|
26
|
+
? { navigationPath: input.navigationPath }
|
|
27
|
+
: {}),
|
|
28
|
+
...(input.navigationAnchors !== undefined
|
|
29
|
+
? { navigationAnchors: input.navigationAnchors }
|
|
30
|
+
: {}),
|
|
31
|
+
sourceKind: input.sourceKind ?? 'stored',
|
|
32
|
+
sourceSetId: input.sourceSetId,
|
|
33
|
+
outputKind: input.outputKind ?? 'text',
|
|
34
|
+
modality: input.modality ?? 'text',
|
|
35
|
+
anchorKind: input.anchorKind ?? 'element',
|
|
36
|
+
renderedText: input.renderedText,
|
|
37
|
+
...(input.sourceSet ? { sourceSet: input.sourceSet } : {}),
|
|
38
|
+
codedText: input.codedText,
|
|
39
|
+
codedTextRoot: input.codedTextRoot,
|
|
40
|
+
codedTextPath: input.codedTextPath,
|
|
41
|
+
codedRichTextTextNodeIndex: input.codedRichTextTextNodeIndex,
|
|
42
|
+
anchorMultiplicity: input.anchorMultiplicity,
|
|
43
|
+
presence: input.presence,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
export function planPreviewProvenanceEditableTextLeaves(input) {
|
|
47
|
+
const candidates = [];
|
|
48
|
+
walkPreviewProvenanceFieldLeaves({
|
|
49
|
+
root: input.root,
|
|
50
|
+
onLeaf: (walked) => {
|
|
51
|
+
const context = {
|
|
52
|
+
path: walked.path,
|
|
53
|
+
fieldName: leafFieldName(walked.path),
|
|
54
|
+
value: walked.value,
|
|
55
|
+
sourceKind: walked.sourceKind,
|
|
56
|
+
outputKind: walked.outputKind,
|
|
57
|
+
...(walked.textNodeIndex === undefined
|
|
58
|
+
? {}
|
|
59
|
+
: { textNodeIndex: walked.textNodeIndex }),
|
|
60
|
+
};
|
|
61
|
+
const matchedPattern = input.match?.(context);
|
|
62
|
+
if (input.match !== undefined && matchedPattern === null)
|
|
63
|
+
return;
|
|
64
|
+
if (walked.outputKind === 'element' && matchedPattern === undefined)
|
|
65
|
+
return;
|
|
66
|
+
if (input.include?.(context) === false)
|
|
67
|
+
return;
|
|
68
|
+
const slug = pathSlug(walked.path, walked.pathAnchors);
|
|
69
|
+
const richTextSuffix = walked.textNodeIndex === undefined
|
|
70
|
+
? ''
|
|
71
|
+
: `:richtext:${walked.textNodeIndex}`;
|
|
72
|
+
const richTextSourceSuffix = walked.textNodeIndex === undefined
|
|
73
|
+
? ''
|
|
74
|
+
: `_richtext_${walked.textNodeIndex}`;
|
|
75
|
+
candidates.push({
|
|
76
|
+
path: walked.path,
|
|
77
|
+
leaf: createPreviewProvenanceEditableLeaf({
|
|
78
|
+
key: `${input.keyPrefix}:${slug}${richTextSuffix}`,
|
|
79
|
+
documentId: input.documentId,
|
|
80
|
+
variantId: input.variantId,
|
|
81
|
+
readScope: input.readScope,
|
|
82
|
+
rootPath: input.rootPath,
|
|
83
|
+
leafPath: walked.path,
|
|
84
|
+
pathAnchors: input.pathAnchors,
|
|
85
|
+
relativePathAnchors: walked.pathAnchors,
|
|
86
|
+
sourceKind: walked.sourceKind,
|
|
87
|
+
sourceSetId: `${input.sourceSetPrefix}_${slug}${richTextSourceSuffix}`,
|
|
88
|
+
outputKind: walked.outputKind,
|
|
89
|
+
modality: matchedPattern?.modality ?? 'text',
|
|
90
|
+
anchorKind: matchedPattern?.anchorKind ?? 'element',
|
|
91
|
+
renderedText: walked.renderedText,
|
|
92
|
+
codedText: walked.outputKind === 'text' && walked.sourceKind === 'stored'
|
|
93
|
+
? true
|
|
94
|
+
: undefined,
|
|
95
|
+
codedRichTextTextNodeIndex: walked.textNodeIndex,
|
|
96
|
+
// Element carriers follow the caller's declared presence, like text.
|
|
97
|
+
presence: input.presence,
|
|
98
|
+
}),
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
return candidates;
|
|
103
|
+
}
|
|
104
|
+
export function createPreviewProvenanceEditableTextManifestInclude(manifest, storyType) {
|
|
105
|
+
const patterns = storyType ? manifest[storyType] ?? [] : [];
|
|
106
|
+
return (context) => matchingPreviewProvenanceEditablePathPattern(context, patterns) !== null;
|
|
107
|
+
}
|
|
108
|
+
export function createPreviewProvenanceEditableManifestMatcher(manifest, storyType) {
|
|
109
|
+
const patterns = storyType ? manifest[storyType] ?? [] : [];
|
|
110
|
+
return (context) => matchingPreviewProvenanceEditablePathPattern(context, patterns);
|
|
111
|
+
}
|
|
112
|
+
const PREVIEW_PROVENANCE_RESOLVABILITY_PREDICATES = {
|
|
113
|
+
'attr:src': hasResolvableImageAssetId,
|
|
114
|
+
'attr:href': hasResolvableHrefTarget,
|
|
115
|
+
};
|
|
116
|
+
export function previewProvenanceEditableTextPathPatternMatches(context, pattern) {
|
|
117
|
+
if ((pattern.outputKind ?? 'text') !== context.outputKind)
|
|
118
|
+
return false;
|
|
119
|
+
if (pattern.sourceKind !== undefined && pattern.sourceKind !== context.sourceKind) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
if (pattern.path.length !== context.path.length)
|
|
123
|
+
return false;
|
|
124
|
+
for (let index = 0; index < pattern.path.length; index++) {
|
|
125
|
+
const expected = pattern.path[index];
|
|
126
|
+
if (expected === '*')
|
|
127
|
+
continue;
|
|
128
|
+
if (expected !== context.path[index])
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
if (pattern.outputKind === 'element' &&
|
|
132
|
+
pattern.modality !== undefined) {
|
|
133
|
+
const isResolvable = PREVIEW_PROVENANCE_RESOLVABILITY_PREDICATES[pattern.modality];
|
|
134
|
+
if (isResolvable === undefined || !isResolvable(context.value))
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
function hasResolvableImageAssetId(value) {
|
|
140
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
const assetId = value.assetId;
|
|
144
|
+
return typeof assetId === 'string' && assetId.length > 0;
|
|
145
|
+
}
|
|
146
|
+
function hasResolvableHrefTarget(value) {
|
|
147
|
+
if (value === null || typeof value !== 'object' || Array.isArray(value))
|
|
148
|
+
return false;
|
|
149
|
+
const link = value;
|
|
150
|
+
// File fields are bare `{ assetId }` values; Link assets carry the same
|
|
151
|
+
// identity behind an explicit `linkType: asset` discriminator.
|
|
152
|
+
if ((link.linkType === undefined || link.linkType === 'asset') &&
|
|
153
|
+
typeof link.assetId === 'string' &&
|
|
154
|
+
link.assetId.trim().length > 0) {
|
|
155
|
+
return true;
|
|
156
|
+
}
|
|
157
|
+
const target = link.linkType === 'url'
|
|
158
|
+
? link.url
|
|
159
|
+
: link.linkType === 'document' ? link.document : undefined;
|
|
160
|
+
const normalized = typeof target === 'string' ? target.trim() : '';
|
|
161
|
+
return normalized !== '' && (link.linkType !== 'url' || normalized !== '#');
|
|
162
|
+
}
|
|
163
|
+
export function matchingPreviewProvenanceEditablePathPattern(context, patterns) {
|
|
164
|
+
return patterns.find((pattern) => previewProvenanceEditableTextPathPatternMatches(context, pattern)) ?? null;
|
|
165
|
+
}
|
|
166
|
+
export async function recordPreviewProvenanceEditableLeaves(input) {
|
|
167
|
+
const recorder = new ProvenanceRecorder({
|
|
168
|
+
sessionId: input.sessionId,
|
|
169
|
+
readContext: input.readContext,
|
|
170
|
+
routeHash: input.routeHash,
|
|
171
|
+
codeVersion: input.codeVersion,
|
|
172
|
+
variantId: input.variantId,
|
|
173
|
+
previewScope: input.previewScope,
|
|
174
|
+
renderId: input.renderId,
|
|
175
|
+
generation: input.generation,
|
|
176
|
+
renderNonce: input.renderNonce,
|
|
177
|
+
emissionRevision: input.emissionRevision,
|
|
178
|
+
enabled: input.enabled,
|
|
179
|
+
});
|
|
180
|
+
const attrsByKey = {};
|
|
181
|
+
const carrierPlansByKey = {};
|
|
182
|
+
const codedTextWrites = createCodedTextWriteTracker();
|
|
183
|
+
for (const leaf of input.leaves) {
|
|
184
|
+
const sourceSet = previewProvenanceSourceSetForLeaf(leaf);
|
|
185
|
+
const carrierPlans = previewProvenanceCarrierPlansForLeaf(leaf);
|
|
186
|
+
const registered = await recorder.mark(sourceSet, {
|
|
187
|
+
modality: leaf.modality,
|
|
188
|
+
anchorKind: leaf.anchorKind,
|
|
189
|
+
carrierPlans,
|
|
190
|
+
anchorMultiplicity: leaf.anchorMultiplicity,
|
|
191
|
+
presence: leaf.presence,
|
|
192
|
+
renderedText: leaf.renderedText,
|
|
193
|
+
});
|
|
194
|
+
attrsByKey[leaf.key] = registered.attrs;
|
|
195
|
+
carrierPlansByKey[leaf.key] = registered.carrierPlans;
|
|
196
|
+
if (typeof registered.markerId !== 'string' || registered.markerId.length === 0) {
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
const codedTextRoot = leaf.codedTextRoot ?? resolveCodedTextRoot(input, leaf.documentId);
|
|
200
|
+
if (leaf.outputKind === 'element') {
|
|
201
|
+
const objectPath = leaf.codedTextPath ?? codedTextPathForCanonicalPath(leaf.canonicalPath, input.codedTextRootPrefix);
|
|
202
|
+
if (objectPath === null) {
|
|
203
|
+
codedTextWrites.fail({
|
|
204
|
+
leaf,
|
|
205
|
+
operation: 'object',
|
|
206
|
+
reason: 'path-unresolvable',
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
codedTextWrites.record({
|
|
211
|
+
leaf,
|
|
212
|
+
root: codedTextRoot,
|
|
213
|
+
operation: 'object',
|
|
214
|
+
written: writePreviewProvenanceObjectMarkerAtPath(codedTextRoot, objectPath, registered.markerId),
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (typeof leaf.renderedText !== 'string') {
|
|
220
|
+
codedTextWrites.fail({
|
|
221
|
+
leaf,
|
|
222
|
+
operation: 'text',
|
|
223
|
+
reason: 'path-or-text-mismatch',
|
|
224
|
+
});
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
const renderedText = leaf.renderedText;
|
|
228
|
+
const codedTextPath = leaf.codedText === true
|
|
229
|
+
? leaf.codedTextPath ?? codedTextPathForCanonicalPath(leaf.canonicalPath, input.codedTextRootPrefix)
|
|
230
|
+
: leaf.codedTextPath;
|
|
231
|
+
const shouldWritePlainCodedText = leaf.codedRichTextTextNodeIndex === undefined || leaf.codedText === true;
|
|
232
|
+
if (codedTextPath && shouldWritePlainCodedText) {
|
|
233
|
+
const policy = classifyPreviewProvenanceCodedTextTarget({
|
|
234
|
+
canonicalPath: leaf.navigationPath ?? leaf.canonicalPath,
|
|
235
|
+
sourceKind: leaf.sourceKind === 'richtext' ? 'richtext' : 'stored',
|
|
236
|
+
});
|
|
237
|
+
if (!policy.allowed) {
|
|
238
|
+
codedTextWrites.fail({
|
|
239
|
+
leaf,
|
|
240
|
+
operation: 'text',
|
|
241
|
+
reason: 'coded-text-disallowed',
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
codedTextWrites.record({
|
|
246
|
+
leaf,
|
|
247
|
+
root: codedTextRoot,
|
|
248
|
+
operation: 'text',
|
|
249
|
+
written: writePreviewProvenanceCodedTextAtPath(codedTextRoot, codedTextPath, registered.markerId, renderedText),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
else if (leaf.codedText === true) {
|
|
254
|
+
codedTextWrites.fail({
|
|
255
|
+
leaf,
|
|
256
|
+
operation: 'text',
|
|
257
|
+
reason: 'path-unresolvable',
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
if (leaf.codedRichTextTextNodeIndex !== undefined) {
|
|
261
|
+
const richTextPath = leaf.codedTextPath ?? codedTextPathForCanonicalPath(leaf.canonicalPath, input.codedTextRootPrefix);
|
|
262
|
+
if (richTextPath === null) {
|
|
263
|
+
codedTextWrites.fail({
|
|
264
|
+
leaf,
|
|
265
|
+
operation: 'richtext',
|
|
266
|
+
reason: 'path-unresolvable',
|
|
267
|
+
});
|
|
268
|
+
continue;
|
|
269
|
+
}
|
|
270
|
+
const policy = classifyPreviewProvenanceCodedTextTarget({
|
|
271
|
+
canonicalPath: leaf.navigationPath ?? leaf.canonicalPath,
|
|
272
|
+
sourceKind: 'richtext',
|
|
273
|
+
});
|
|
274
|
+
if (!policy.allowed) {
|
|
275
|
+
codedTextWrites.fail({
|
|
276
|
+
leaf,
|
|
277
|
+
operation: 'richtext',
|
|
278
|
+
reason: 'coded-text-disallowed',
|
|
279
|
+
});
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
codedTextWrites.record({
|
|
283
|
+
leaf,
|
|
284
|
+
root: codedTextRoot,
|
|
285
|
+
operation: 'richtext',
|
|
286
|
+
written: writePreviewProvenanceCodedRichTextTextNodeAtPath(codedTextRoot, richTextPath, leaf.codedRichTextTextNodeIndex, registered.markerId, renderedText),
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
const renderFrame = recorder.flush();
|
|
291
|
+
return {
|
|
292
|
+
attrsByKey,
|
|
293
|
+
carrierPlansByKey,
|
|
294
|
+
renderFrame,
|
|
295
|
+
publicSidecar: input.enabled === false ? null : createPublicSidecar(renderFrame),
|
|
296
|
+
codedTextWrites: codedTextWrites.summary(),
|
|
297
|
+
};
|
|
298
|
+
}
|
|
299
|
+
function previewProvenanceCarrierPlansForLeaf(leaf) {
|
|
300
|
+
if (leaf.outputKind === 'element') {
|
|
301
|
+
return [
|
|
302
|
+
createPreviewProvenanceAttributeCarrierPlan({
|
|
303
|
+
carrier: PREVIEW_PROVENANCE_ELEMENT_ATTRIBUTE_CARRIER,
|
|
304
|
+
modality: leaf.modality,
|
|
305
|
+
anchorKind: leaf.anchorKind,
|
|
306
|
+
}),
|
|
307
|
+
];
|
|
308
|
+
}
|
|
309
|
+
const carrierPlans = [
|
|
310
|
+
createPreviewProvenanceAttributeCarrierPlan({
|
|
311
|
+
modality: leaf.modality,
|
|
312
|
+
anchorKind: leaf.anchorKind,
|
|
313
|
+
}),
|
|
314
|
+
];
|
|
315
|
+
if (leaf.codedRichTextTextNodeIndex !== undefined) {
|
|
316
|
+
carrierPlans.push(createPreviewProvenanceCodedTextCarrierPlan({
|
|
317
|
+
surface: 'rich-text-text-node',
|
|
318
|
+
}));
|
|
319
|
+
}
|
|
320
|
+
else if (leaf.codedText === true || leaf.codedTextPath !== undefined) {
|
|
321
|
+
const policy = classifyPreviewProvenanceCodedTextTarget({
|
|
322
|
+
canonicalPath: leaf.navigationPath ?? leaf.canonicalPath,
|
|
323
|
+
sourceKind: leaf.sourceKind === 'richtext' ? 'richtext' : 'stored',
|
|
324
|
+
});
|
|
325
|
+
if (policy.allowed) {
|
|
326
|
+
carrierPlans.push(createPreviewProvenanceCodedTextCarrierPlan({
|
|
327
|
+
surface: policy.surface,
|
|
328
|
+
}));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return carrierPlans;
|
|
332
|
+
}
|
|
333
|
+
export function previewProvenanceSourceSetForLeaf(leaf) {
|
|
334
|
+
const source = provenanceIdForLeaf(leaf);
|
|
335
|
+
if (leaf.sourceSet === undefined) {
|
|
336
|
+
return {
|
|
337
|
+
id: leaf.sourceSetId,
|
|
338
|
+
kind: 'single',
|
|
339
|
+
sources: [source],
|
|
340
|
+
primary: source,
|
|
341
|
+
editable: 'single-field',
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
const sources = leaf.sourceSet.sources.map((source) => sourceWithLeafNavigation(source, leaf));
|
|
345
|
+
const primary = leaf.sourceSet.primary === undefined
|
|
346
|
+
? undefined
|
|
347
|
+
: sourceWithLeafNavigation(leaf.sourceSet.primary, leaf);
|
|
348
|
+
return {
|
|
349
|
+
id: leaf.sourceSetId,
|
|
350
|
+
kind: leaf.sourceSet.kind,
|
|
351
|
+
sources,
|
|
352
|
+
...(primary ? { primary } : {}),
|
|
353
|
+
...(leaf.sourceSet.derivation
|
|
354
|
+
? { derivation: leaf.sourceSet.derivation }
|
|
355
|
+
: {}),
|
|
356
|
+
editable: leaf.sourceSet.editable,
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function sourceWithLeafNavigation(source, leaf) {
|
|
360
|
+
if (leaf.navigationPath === undefined && leaf.navigationAnchors === undefined) {
|
|
361
|
+
return source;
|
|
362
|
+
}
|
|
363
|
+
if (!sourceMatchesLeafIdentity(source, leaf))
|
|
364
|
+
return source;
|
|
365
|
+
return {
|
|
366
|
+
...source,
|
|
367
|
+
...(leaf.navigationPath !== undefined
|
|
368
|
+
? { navigationPath: leaf.navigationPath }
|
|
369
|
+
: {}),
|
|
370
|
+
...(leaf.navigationAnchors !== undefined
|
|
371
|
+
? { navigationAnchors: leaf.navigationAnchors }
|
|
372
|
+
: {}),
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
function sourceMatchesLeafIdentity(source, leaf) {
|
|
376
|
+
return source.documentId === leaf.documentId &&
|
|
377
|
+
normalizePreviewVariantId(source.variantId) === normalizePreviewVariantId(leaf.variantId) &&
|
|
378
|
+
source.canonicalPath === leaf.canonicalPath &&
|
|
379
|
+
source.sourceKind === leaf.sourceKind &&
|
|
380
|
+
source.textNodeIndex === leaf.codedRichTextTextNodeIndex;
|
|
381
|
+
}
|
|
382
|
+
function provenanceIdForLeaf(leaf) {
|
|
383
|
+
return {
|
|
384
|
+
documentId: leaf.documentId,
|
|
385
|
+
...(normalizePreviewVariantId(leaf.variantId) !== null
|
|
386
|
+
? { variantId: normalizePreviewVariantId(leaf.variantId) }
|
|
387
|
+
: {}),
|
|
388
|
+
...(leaf.readScope !== undefined ? { readScope: leaf.readScope } : {}),
|
|
389
|
+
canonicalPath: leaf.canonicalPath,
|
|
390
|
+
pathAnchors: leaf.pathAnchors,
|
|
391
|
+
...(leaf.navigationPath !== undefined
|
|
392
|
+
? { navigationPath: leaf.navigationPath }
|
|
393
|
+
: {}),
|
|
394
|
+
...(leaf.navigationAnchors !== undefined
|
|
395
|
+
? { navigationAnchors: leaf.navigationAnchors }
|
|
396
|
+
: {}),
|
|
397
|
+
...(leaf.codedRichTextTextNodeIndex !== undefined
|
|
398
|
+
? { textNodeIndex: leaf.codedRichTextTextNodeIndex }
|
|
399
|
+
: {}),
|
|
400
|
+
sourceKind: leaf.sourceKind,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
export function prefixPreviewProvenancePathAnchors(prefixPath, anchors) {
|
|
404
|
+
if (anchors.length === 0)
|
|
405
|
+
return [];
|
|
406
|
+
const prefix = formatPreviewProvenanceCanonicalPath(prefixPath);
|
|
407
|
+
if (prefix.length === 0)
|
|
408
|
+
return anchors;
|
|
409
|
+
return anchors.map((anchor) => ({
|
|
410
|
+
path: anchor.path.length === 0 ? prefix : `${prefix}.${anchor.path}`,
|
|
411
|
+
key: anchor.key,
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
function resolveCodedTextRoot(input, documentId) {
|
|
415
|
+
const roots = input.codedTextRootsByDocumentId;
|
|
416
|
+
if (roots === undefined)
|
|
417
|
+
return input.codedTextRoot;
|
|
418
|
+
if (isCodedTextRootMap(roots))
|
|
419
|
+
return roots.get(documentId);
|
|
420
|
+
return Object.prototype.hasOwnProperty.call(roots, documentId)
|
|
421
|
+
? roots[documentId]
|
|
422
|
+
: undefined;
|
|
423
|
+
}
|
|
424
|
+
function isCodedTextRootMap(roots) {
|
|
425
|
+
const candidate = roots;
|
|
426
|
+
return typeof candidate.get === 'function' && typeof candidate.has === 'function';
|
|
427
|
+
}
|
|
428
|
+
function createCodedTextWriteTracker() {
|
|
429
|
+
let attempted = 0;
|
|
430
|
+
let written = 0;
|
|
431
|
+
const failures = [];
|
|
432
|
+
return {
|
|
433
|
+
record(input) {
|
|
434
|
+
attempted += 1;
|
|
435
|
+
if (input.written) {
|
|
436
|
+
written += 1;
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
failures.push(codedTextWriteFailure({
|
|
440
|
+
leaf: input.leaf,
|
|
441
|
+
operation: input.operation,
|
|
442
|
+
reason: input.root === null || input.root === undefined
|
|
443
|
+
? 'root-missing'
|
|
444
|
+
: input.operation === 'object'
|
|
445
|
+
? 'path-or-object-mismatch'
|
|
446
|
+
: 'path-or-text-mismatch',
|
|
447
|
+
}));
|
|
448
|
+
},
|
|
449
|
+
fail(input) {
|
|
450
|
+
failures.push(codedTextWriteFailure(input));
|
|
451
|
+
},
|
|
452
|
+
summary() {
|
|
453
|
+
return { attempted, written, failures };
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function codedTextWriteFailure(input) {
|
|
458
|
+
return {
|
|
459
|
+
key: input.leaf.key,
|
|
460
|
+
documentId: input.leaf.documentId,
|
|
461
|
+
sourceSetId: input.leaf.sourceSetId,
|
|
462
|
+
canonicalPath: input.leaf.canonicalPath,
|
|
463
|
+
operation: input.operation,
|
|
464
|
+
reason: input.reason,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
function leafFieldName(path) {
|
|
468
|
+
const leaf = path[path.length - 1];
|
|
469
|
+
return typeof leaf === 'string' ? leaf : null;
|
|
470
|
+
}
|
|
471
|
+
function pathSlug(path, relativePathAnchors) {
|
|
472
|
+
const keyByArrayPath = new Map(relativePathAnchors.map((anchor) => [anchor.path, anchor.key]));
|
|
473
|
+
return path.map((part, index) => {
|
|
474
|
+
if (typeof part === 'number') {
|
|
475
|
+
const arrayPath = formatPreviewProvenanceCanonicalPath(path.slice(0, index));
|
|
476
|
+
return safeSlugPart(keyByArrayPath.get(arrayPath) ?? String(part));
|
|
477
|
+
}
|
|
478
|
+
return safeSlugPart(part);
|
|
479
|
+
}).join('_');
|
|
480
|
+
}
|
|
481
|
+
function safeSlugPart(value) {
|
|
482
|
+
return value.replace(/[^A-Za-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'field';
|
|
483
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ImperativeEditorNavigationTarget } from '@storystick/types/editor-destination';
|
|
2
|
+
import type { PreviewReadScopeEcho } from '@storystick/client/internal';
|
|
3
|
+
import type { ProvenanceId } from './types.js';
|
|
4
|
+
export type PreviewOverlayRoutableTarget = {
|
|
5
|
+
kind: 'slice' | 'field';
|
|
6
|
+
documentId: string;
|
|
7
|
+
variantId?: string | null;
|
|
8
|
+
readScope?: PreviewReadScopeEcho;
|
|
9
|
+
path: string;
|
|
10
|
+
sliceKey?: string;
|
|
11
|
+
fromAnchors: ReadonlyArray<{
|
|
12
|
+
pathPrefix: string;
|
|
13
|
+
key: string;
|
|
14
|
+
}>;
|
|
15
|
+
navigationPath?: string;
|
|
16
|
+
navigationAnchors?: ReadonlyArray<{
|
|
17
|
+
pathPrefix: string;
|
|
18
|
+
key: string;
|
|
19
|
+
}>;
|
|
20
|
+
field?: string;
|
|
21
|
+
sourceKind?: ProvenanceId['sourceKind'];
|
|
22
|
+
richTextTextNodeIndex?: number;
|
|
23
|
+
richTextSliceEmbedIndex?: number;
|
|
24
|
+
richTextSliceEmbedKey?: string;
|
|
25
|
+
};
|
|
26
|
+
export declare function overlayEditorTarget(target: PreviewOverlayRoutableTarget): ImperativeEditorNavigationTarget;
|
|
27
|
+
/** Non-localized owners (navigation/settings) carry no readScope. Keep the
|
|
28
|
+
* presentation locale; an inherited applied locale is never an editing target. */
|
|
29
|
+
export declare function overlayEditorLocale(target: Pick<PreviewOverlayRoutableTarget, 'readScope'>, presentationLocale?: string | null): string | undefined;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { richTextSliceEmbedAddressFromPath, richTextSliceEmbedKeyFromAnchors, } from './richtext-slice-embed-address.js';
|
|
2
|
+
function richTextSliceEmbedNavigationOptions(target) {
|
|
3
|
+
const path = target.navigationPath ?? target.path;
|
|
4
|
+
const anchors = target.navigationAnchors ?? target.fromAnchors;
|
|
5
|
+
if (target.richTextSliceEmbedIndex !== undefined) {
|
|
6
|
+
const address = richTextSliceEmbedAddressFromPath(path, target.richTextSliceEmbedIndex);
|
|
7
|
+
const key = target.richTextSliceEmbedKey ??
|
|
8
|
+
(address !== null
|
|
9
|
+
? richTextSliceEmbedKeyFromAnchors(anchors, address.rootPath)
|
|
10
|
+
: undefined);
|
|
11
|
+
return {
|
|
12
|
+
richTextSliceEmbedIndex: target.richTextSliceEmbedIndex,
|
|
13
|
+
...(key !== undefined ? { richTextSliceEmbedKey: key } : {}),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const address = richTextSliceEmbedAddressFromPath(path);
|
|
17
|
+
if (address === null)
|
|
18
|
+
return {};
|
|
19
|
+
const key = richTextSliceEmbedKeyFromAnchors(anchors, address.rootPath);
|
|
20
|
+
return {
|
|
21
|
+
richTextSliceEmbedIndex: address.index,
|
|
22
|
+
...(key !== undefined ? { richTextSliceEmbedKey: key } : {}),
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function overlayEditorTarget(target) {
|
|
26
|
+
const path = target.navigationPath ?? target.path;
|
|
27
|
+
if (target.kind === 'slice' && target.sliceKey) {
|
|
28
|
+
return { kind: 'slice', sliceKey: target.sliceKey, ownerPath: path };
|
|
29
|
+
}
|
|
30
|
+
const sliceEmbedOptions = richTextSliceEmbedNavigationOptions(target);
|
|
31
|
+
return {
|
|
32
|
+
kind: 'field',
|
|
33
|
+
path,
|
|
34
|
+
anchors: target.navigationAnchors ?? target.fromAnchors,
|
|
35
|
+
...(target.richTextTextNodeIndex !== undefined
|
|
36
|
+
? { richTextTextNodeIndex: target.richTextTextNodeIndex }
|
|
37
|
+
: {}),
|
|
38
|
+
...sliceEmbedOptions,
|
|
39
|
+
focus: true,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/** Non-localized owners (navigation/settings) carry no readScope. Keep the
|
|
43
|
+
* presentation locale; an inherited applied locale is never an editing target. */
|
|
44
|
+
export function overlayEditorLocale(target, presentationLocale) {
|
|
45
|
+
return target.readScope?.requestedLocale ?? presentationLocale ?? undefined;
|
|
46
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { PathAnchor } from './types.js';
|
|
2
|
+
export type PreviewProvenanceFieldPathPart = string | number;
|
|
3
|
+
export interface PreviewProvenanceWalkedFieldLeaf {
|
|
4
|
+
readonly path: readonly PreviewProvenanceFieldPathPart[];
|
|
5
|
+
readonly pathAnchors: readonly PathAnchor[];
|
|
6
|
+
readonly sourceKind: 'stored' | 'richtext';
|
|
7
|
+
readonly outputKind: 'text' | 'element';
|
|
8
|
+
readonly value?: unknown;
|
|
9
|
+
readonly renderedText?: string;
|
|
10
|
+
readonly textNodeIndex?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface WalkPreviewProvenanceFieldLeavesInput {
|
|
13
|
+
readonly root: unknown;
|
|
14
|
+
readonly onLeaf: (leaf: PreviewProvenanceWalkedFieldLeaf) => void;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* The single recursive field traversal shared by source-map materialization
|
|
18
|
+
* and editable-leaf planning. Consumers own policy and output shaping.
|
|
19
|
+
*/
|
|
20
|
+
export declare function walkPreviewProvenanceFieldLeaves(input: WalkPreviewProvenanceFieldLeavesInput): void;
|
|
21
|
+
export declare function formatPreviewProvenanceCanonicalPath(path: readonly PreviewProvenanceFieldPathPart[]): string;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { previewProvenanceRichTextTextNodes } from './coded-text.js';
|
|
2
|
+
/**
|
|
3
|
+
* The single recursive field traversal shared by source-map materialization
|
|
4
|
+
* and editable-leaf planning. Consumers own policy and output shaping.
|
|
5
|
+
*/
|
|
6
|
+
export function walkPreviewProvenanceFieldLeaves(input) {
|
|
7
|
+
visitFieldValue({
|
|
8
|
+
value: input.root,
|
|
9
|
+
path: [],
|
|
10
|
+
pathAnchors: [],
|
|
11
|
+
onLeaf: input.onLeaf,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
export function formatPreviewProvenanceCanonicalPath(path) {
|
|
15
|
+
return path.map((part, index) => typeof part === 'number'
|
|
16
|
+
? `[${part}]`
|
|
17
|
+
: index === 0
|
|
18
|
+
? part
|
|
19
|
+
: `.${part}`).join('');
|
|
20
|
+
}
|
|
21
|
+
function visitFieldValue(input) {
|
|
22
|
+
const richTextNodes = previewProvenanceRichTextTextNodes(input.value);
|
|
23
|
+
if (richTextNodes.length > 0) {
|
|
24
|
+
for (let textNodeIndex = 0; textNodeIndex < richTextNodes.length; textNodeIndex++) {
|
|
25
|
+
const renderedText = richTextNodes[textNodeIndex];
|
|
26
|
+
if (!renderedText)
|
|
27
|
+
continue;
|
|
28
|
+
input.onLeaf({
|
|
29
|
+
path: input.path,
|
|
30
|
+
pathAnchors: input.pathAnchors,
|
|
31
|
+
sourceKind: 'richtext',
|
|
32
|
+
outputKind: 'text',
|
|
33
|
+
value: renderedText,
|
|
34
|
+
renderedText,
|
|
35
|
+
textNodeIndex,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (typeof input.value === 'string') {
|
|
41
|
+
if (input.value.length === 0)
|
|
42
|
+
return;
|
|
43
|
+
input.onLeaf({
|
|
44
|
+
path: input.path,
|
|
45
|
+
pathAnchors: input.pathAnchors,
|
|
46
|
+
sourceKind: 'stored',
|
|
47
|
+
outputKind: 'text',
|
|
48
|
+
value: input.value,
|
|
49
|
+
renderedText: input.value,
|
|
50
|
+
});
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (Array.isArray(input.value)) {
|
|
54
|
+
for (let index = 0; index < input.value.length; index++) {
|
|
55
|
+
const item = input.value[index];
|
|
56
|
+
visitFieldValue({
|
|
57
|
+
value: item,
|
|
58
|
+
path: [...input.path, index],
|
|
59
|
+
pathAnchors: keyedArrayItemAnchors(input.path, item, input.pathAnchors),
|
|
60
|
+
onLeaf: input.onLeaf,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
if (input.value === null || typeof input.value !== 'object')
|
|
66
|
+
return;
|
|
67
|
+
if (input.path.length > 0) {
|
|
68
|
+
input.onLeaf({
|
|
69
|
+
path: input.path,
|
|
70
|
+
pathAnchors: input.pathAnchors,
|
|
71
|
+
sourceKind: 'stored',
|
|
72
|
+
outputKind: 'element',
|
|
73
|
+
value: input.value,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
for (const [key, child] of Object.entries(input.value)) {
|
|
77
|
+
if (key.startsWith('_'))
|
|
78
|
+
continue;
|
|
79
|
+
visitFieldValue({
|
|
80
|
+
value: child,
|
|
81
|
+
path: [...input.path, key],
|
|
82
|
+
pathAnchors: input.pathAnchors,
|
|
83
|
+
onLeaf: input.onLeaf,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
function keyedArrayItemAnchors(arrayPath, item, inherited) {
|
|
88
|
+
const key = item !== null && typeof item === 'object'
|
|
89
|
+
? item._key
|
|
90
|
+
: undefined;
|
|
91
|
+
if (typeof key !== 'string' || key.length === 0 || arrayPath.length === 0) {
|
|
92
|
+
return inherited;
|
|
93
|
+
}
|
|
94
|
+
return [
|
|
95
|
+
...inherited,
|
|
96
|
+
{ path: formatPreviewProvenanceCanonicalPath(arrayPath), key },
|
|
97
|
+
];
|
|
98
|
+
}
|