@storystick/preview 0.0.0-stage → 0.2.0-rc.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/LICENSE +112 -0
- package/PUBLIC-API.md +55 -0
- package/README.md +42 -2
- package/dist/.source-hash +1 -0
- package/dist/authoring/runtime-contract.d.ts +89 -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/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 +165 -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 +206 -3
|
@@ -0,0 +1,1327 @@
|
|
|
1
|
+
import { collectPreviewReadObservations } from '@storystick/client/internal';
|
|
2
|
+
import { appliedPreviewVariantIdFromQueryResult, previewReadScopeEchoFromQueryResult, requestedPreviewVariantIdFromQueryResult, collectPreviewProvenanceSourceMappedQueryResults, collectPreviewFrameDependencies, createPreviewProvenanceEditableManifestMatcher, formatPreviewProvenanceCanonicalPath, materializePreviewProvenanceSourceMap, planPreviewProvenanceEditableTextLeaves, previewProvenanceEditableTextPathPatternMatches, previewProvenanceRichTextTextNodes, previewProvenanceSourceIdentityFromQueryResult, } from '@storystick/preview/provenance';
|
|
3
|
+
import { normalizePreviewVariantId } from '@storystick/preview/identity';
|
|
4
|
+
import { arrayItemSegment, documentPathKey, fieldSegment, richTextTextNodeSegment, sliceFieldKey, slicePathKey, withPreviewProvenanceRouteFrame, withPreviewProvenanceRouteRender, withPreviewProvenanceContext, } from '../provenance/render-context.js';
|
|
5
|
+
import { RICHTEXT_EMBED_STORE_FIELD, formatRichtextEmbedFieldPath, } from '@storystick/types/internal/richtext-embeds';
|
|
6
|
+
import { unstable_buildPreviewRenderFrameWithRuntime, } from './render-frame-provenance.js';
|
|
7
|
+
import { resolvePreviewFrameReadMetadataFromObservations } from './preview-read-metadata.js';
|
|
8
|
+
export async function unstable_buildPreviewRouteDocumentFrameWithRuntime(options, runtime) {
|
|
9
|
+
const enabled = options.enabled ?? runtime.config.isEnabled();
|
|
10
|
+
if (!enabled)
|
|
11
|
+
return null;
|
|
12
|
+
const plan = planPreviewDocumentRender({ ...options, enabled }, runtime);
|
|
13
|
+
return plan ? unstable_buildPreviewRenderFrameWithRuntime(plan, runtime) : null;
|
|
14
|
+
}
|
|
15
|
+
/** Internal planning shared by routes and independently mounted server components. */
|
|
16
|
+
export function planPreviewDocumentRender(options, runtime) {
|
|
17
|
+
// Read observations live in a WeakMap so QueryResults remain plain across
|
|
18
|
+
// the RSC boundary. Resolve that out-of-band metadata before the JSON clone
|
|
19
|
+
// used for coded render data, then pass the scalar identity downstream.
|
|
20
|
+
const codeVersion = options.codeVersion ?? runtime.config.resolveCodeVersion();
|
|
21
|
+
const observations = collectPreviewReadObservations(options.document, options.context);
|
|
22
|
+
const readMetadata = resolvePreviewFrameReadMetadataFromObservations(codeVersion, observations);
|
|
23
|
+
if (observations.some((read) => read.readContext) && readMetadata === null)
|
|
24
|
+
return null;
|
|
25
|
+
const codedDocument = cloneJson(options.document);
|
|
26
|
+
const codedContext = options.context === undefined ? undefined : cloneJson(options.context);
|
|
27
|
+
const routeDocuments = collectPreviewProvenanceSourceMappedQueryResults(options.document);
|
|
28
|
+
const contextDocuments = uniqueQueryResultsByDocumentId([
|
|
29
|
+
...routeDocuments.filter((entry) => entry.documentId !== options.document.documentId),
|
|
30
|
+
...(options.context === undefined
|
|
31
|
+
? []
|
|
32
|
+
: collectPreviewProvenanceSourceMappedQueryResults(options.context)),
|
|
33
|
+
]);
|
|
34
|
+
const referencedContextDocumentIds = collectReferencedContextDocumentIds({
|
|
35
|
+
document: options.document,
|
|
36
|
+
contextDocuments,
|
|
37
|
+
});
|
|
38
|
+
const codedTextRoots = collectCodedTextRootsByDocumentId(codedDocument, codedContext);
|
|
39
|
+
const slicePlanningScopes = collectSlicePlanningScopes(options.document);
|
|
40
|
+
const routeDocumentPlan = planRouteDocumentPreviewProvenance({
|
|
41
|
+
document: options.document,
|
|
42
|
+
manifest: options.manifest,
|
|
43
|
+
});
|
|
44
|
+
const modelDocumentIds = modelIdsFromRouteDocument(options.document);
|
|
45
|
+
const contextPlan = planContextDocumentPreviewProvenance({
|
|
46
|
+
documents: contextDocuments,
|
|
47
|
+
manifest: options.manifest,
|
|
48
|
+
richTextDocumentIds: referencedContextDocumentIds,
|
|
49
|
+
});
|
|
50
|
+
const sourceMapDiagnostics = summarizeMaterializedMarkPlans([
|
|
51
|
+
routeDocumentPlan,
|
|
52
|
+
contextPlan,
|
|
53
|
+
]);
|
|
54
|
+
const dependencies = routeDocumentFrameDependencies({
|
|
55
|
+
pathname: options.pathname,
|
|
56
|
+
document: options.document,
|
|
57
|
+
contextDocuments,
|
|
58
|
+
modelDocumentIds,
|
|
59
|
+
extraDependencies: [
|
|
60
|
+
...(options.dependencies ?? []),
|
|
61
|
+
...collectPreviewFrameDependencies(options.document, options.context),
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
const marks = [
|
|
65
|
+
...routeDocumentPlan.marks,
|
|
66
|
+
...slicePlanningScopes.flatMap((scope) => planPreviewProvenanceBoundSliceZoneTextLeaves({
|
|
67
|
+
document: options.document,
|
|
68
|
+
slices: scope.slices,
|
|
69
|
+
sliceZonePath: scope.sliceZonePath,
|
|
70
|
+
parentPathAnchors: scope.parentPathAnchors,
|
|
71
|
+
fieldBindingsFallback: options.fieldBindingsFallback,
|
|
72
|
+
})),
|
|
73
|
+
...slicePlanningScopes.flatMap((scope) => planPreviewProvenanceModelSliceZoneTextLeaves({
|
|
74
|
+
document: options.document,
|
|
75
|
+
codedDocument,
|
|
76
|
+
slices: scope.slices,
|
|
77
|
+
sliceZonePath: scope.sliceZonePath,
|
|
78
|
+
parentPathAnchors: scope.parentPathAnchors,
|
|
79
|
+
manifest: options.manifest,
|
|
80
|
+
fieldBindingsFallback: options.fieldBindingsFallback,
|
|
81
|
+
})),
|
|
82
|
+
...slicePlanningScopes.flatMap((scope) => scope.sourceKind === 'embed'
|
|
83
|
+
? planPreviewProvenanceEmbedSliceTextLeaves({
|
|
84
|
+
document: options.document,
|
|
85
|
+
slices: scope.slices,
|
|
86
|
+
sliceZonePath: scope.sliceZonePath,
|
|
87
|
+
sourceSliceZonePath: scope.sourceSliceZonePath,
|
|
88
|
+
parentPathAnchors: scope.parentPathAnchors,
|
|
89
|
+
sourceParentPathAnchors: scope.sourceParentPathAnchors,
|
|
90
|
+
sidecarByKey: scope.sidecarByKey,
|
|
91
|
+
sidecarIndexByKey: scope.sidecarIndexByKey,
|
|
92
|
+
manifest: options.manifest,
|
|
93
|
+
})
|
|
94
|
+
: []),
|
|
95
|
+
...contextPlan.marks,
|
|
96
|
+
];
|
|
97
|
+
const previewScopeDocuments = [
|
|
98
|
+
options.document,
|
|
99
|
+
...contextDocuments,
|
|
100
|
+
];
|
|
101
|
+
const echoedAppliedVariantId = appliedVariantIdFromRouteDocuments(previewScopeDocuments);
|
|
102
|
+
const echoedRequestedVariantId = requestedVariantIdFromRouteDocuments(previewScopeDocuments);
|
|
103
|
+
const previewScope = previewScopeFromRouteDocuments(previewScopeDocuments);
|
|
104
|
+
const variantId = echoedAppliedVariantId !== undefined
|
|
105
|
+
? echoedAppliedVariantId
|
|
106
|
+
: echoedRequestedVariantId !== undefined
|
|
107
|
+
? echoedRequestedVariantId
|
|
108
|
+
: options.variantId === undefined
|
|
109
|
+
? variantIdFromRouteDocument(options.document)
|
|
110
|
+
: normalizePreviewVariantId(options.variantId);
|
|
111
|
+
return {
|
|
112
|
+
enabled: options.enabled,
|
|
113
|
+
frameId: options.frameId ?? 'route',
|
|
114
|
+
sessionId: options.sessionId,
|
|
115
|
+
routeHash: options.routeHash,
|
|
116
|
+
variantId: options.variantId === undefined ? variantId ?? undefined : variantId,
|
|
117
|
+
previewScope,
|
|
118
|
+
pathname: options.pathname,
|
|
119
|
+
codeVersion,
|
|
120
|
+
renderId: options.renderId,
|
|
121
|
+
generation: options.generation,
|
|
122
|
+
renderNonce: options.renderNonce,
|
|
123
|
+
emissionRevision: options.emissionRevision,
|
|
124
|
+
authoritativeDocumentIds: options.authoritativeDocumentIds ?? [
|
|
125
|
+
options.document.documentId,
|
|
126
|
+
...modelDocumentIds,
|
|
127
|
+
...contextDocuments.map((entry) => entry.documentId),
|
|
128
|
+
],
|
|
129
|
+
dependencies,
|
|
130
|
+
leaves: marks,
|
|
131
|
+
sourceMapDiagnostics,
|
|
132
|
+
codedDocument,
|
|
133
|
+
codedTextRoot: codedDocument,
|
|
134
|
+
codedTextRootsByDocumentId: codedTextRoots,
|
|
135
|
+
codedContext,
|
|
136
|
+
readContext: readMetadata?.readContext,
|
|
137
|
+
receiptIds: readMetadata?.receiptIds,
|
|
138
|
+
observationId: readMetadata?.observationId,
|
|
139
|
+
renderWatermark: readMetadata?.renderWatermark,
|
|
140
|
+
fixture: options.fixture,
|
|
141
|
+
projectId: options.projectId,
|
|
142
|
+
endpoint: options.endpoint,
|
|
143
|
+
convexUrl: options.convexUrl,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
export function stripPreviewRouteMetadata(document) {
|
|
147
|
+
if (!isRecord(document))
|
|
148
|
+
return document;
|
|
149
|
+
const rest = {};
|
|
150
|
+
for (const [key, value] of Object.entries(document)) {
|
|
151
|
+
if (key === '__ssPreviewRouteFrame' || key === '__ssPreviewRouteRender') {
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
rest[key] = value;
|
|
155
|
+
}
|
|
156
|
+
return rest;
|
|
157
|
+
}
|
|
158
|
+
function variantIdFromRouteDocument(document) {
|
|
159
|
+
return (normalizePreviewVariantId(document._locale?.variantId) ??
|
|
160
|
+
normalizePreviewVariantId(document._variant?.variantId));
|
|
161
|
+
}
|
|
162
|
+
function appliedVariantIdFromRouteDocuments(documents) {
|
|
163
|
+
let sawApplicableEcho = false;
|
|
164
|
+
for (const document of documents) {
|
|
165
|
+
const appliedVariantId = appliedPreviewVariantIdFromQueryResult(document);
|
|
166
|
+
if (appliedVariantId === undefined)
|
|
167
|
+
continue;
|
|
168
|
+
sawApplicableEcho = true;
|
|
169
|
+
if (appliedVariantId !== null)
|
|
170
|
+
return appliedVariantId;
|
|
171
|
+
}
|
|
172
|
+
return sawApplicableEcho ? null : undefined;
|
|
173
|
+
}
|
|
174
|
+
function requestedVariantIdFromRouteDocuments(documents) {
|
|
175
|
+
for (const document of documents) {
|
|
176
|
+
const requestedVariantId = requestedPreviewVariantIdFromQueryResult(document);
|
|
177
|
+
if (requestedVariantId !== undefined)
|
|
178
|
+
return requestedVariantId;
|
|
179
|
+
}
|
|
180
|
+
return undefined;
|
|
181
|
+
}
|
|
182
|
+
function previewScopeFromRouteDocuments(documents) {
|
|
183
|
+
let fallback;
|
|
184
|
+
for (const document of documents) {
|
|
185
|
+
const scope = previewReadScopeEchoFromQueryResult(document);
|
|
186
|
+
if (scope === undefined)
|
|
187
|
+
continue;
|
|
188
|
+
fallback ??= scope;
|
|
189
|
+
if (scope.appliesToDocument !== false)
|
|
190
|
+
return scope;
|
|
191
|
+
}
|
|
192
|
+
return fallback;
|
|
193
|
+
}
|
|
194
|
+
export function unstable_attachPreviewProvenanceRenderContext(value, attrsByKey) {
|
|
195
|
+
if (!attrsByKey)
|
|
196
|
+
return value;
|
|
197
|
+
return withPreviewProvenanceContext(value, attrsByKey);
|
|
198
|
+
}
|
|
199
|
+
export function unstable_attachPreviewRouteFrameContext(frame) {
|
|
200
|
+
if (!frame?.codedDocument)
|
|
201
|
+
return null;
|
|
202
|
+
return withPreviewProvenanceRouteFrame(frame.codedDocument, routeFrameContext(frame));
|
|
203
|
+
}
|
|
204
|
+
export function unstable_attachPreviewRouteMetadataContext(value, frame) {
|
|
205
|
+
if (!frame)
|
|
206
|
+
return value;
|
|
207
|
+
return withPreviewProvenanceRouteFrame(value, routeFrameContext(frame));
|
|
208
|
+
}
|
|
209
|
+
function routeFrameContext(frame) {
|
|
210
|
+
return {
|
|
211
|
+
type: 'storystick.preview.route-frame',
|
|
212
|
+
version: 1,
|
|
213
|
+
frameId: frame.frameId,
|
|
214
|
+
sessionId: frame.sessionId,
|
|
215
|
+
routeHash: frame.routeHash,
|
|
216
|
+
publicSidecar: frame.publicSidecar,
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
export function unstable_attachPreviewRouteRenderContext(value, frame) {
|
|
220
|
+
if (!frame?.codedDocument)
|
|
221
|
+
return value;
|
|
222
|
+
return withPreviewProvenanceRouteRender(value, {
|
|
223
|
+
type: 'storystick.preview.route-render',
|
|
224
|
+
version: 1,
|
|
225
|
+
frame: {
|
|
226
|
+
type: 'storystick.preview.route-frame',
|
|
227
|
+
version: 1,
|
|
228
|
+
frameId: frame.frameId,
|
|
229
|
+
sessionId: frame.sessionId,
|
|
230
|
+
routeHash: frame.routeHash,
|
|
231
|
+
publicSidecar: frame.publicSidecar,
|
|
232
|
+
},
|
|
233
|
+
document: frame.codedDocument,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
function planRouteDocumentPreviewProvenance(input) {
|
|
237
|
+
const materialized = materializePreviewProvenanceSourceMap({
|
|
238
|
+
queryResult: input.document,
|
|
239
|
+
manifest: input.manifest,
|
|
240
|
+
keyPrefix: `route:source:${storyTypeOf(input.document)}:${input.document.documentId}`,
|
|
241
|
+
sourceSetPrefix: 'route',
|
|
242
|
+
keyForEntry: routeDocumentKeyForSourceMapEntry,
|
|
243
|
+
presence: 'optional',
|
|
244
|
+
});
|
|
245
|
+
return materializedMarkPlan(normalizeRouteDocumentSourceMapLeaves(materialized.leaves), materialized);
|
|
246
|
+
}
|
|
247
|
+
function normalizeRouteDocumentSourceMapLeaves(leaves) {
|
|
248
|
+
return leaves.map((leaf) => {
|
|
249
|
+
const sectionAnchor = leaf.pathAnchors.find((anchor) => anchor.path === 'sections');
|
|
250
|
+
if (sectionAnchor === undefined)
|
|
251
|
+
return leaf;
|
|
252
|
+
const documentPart = safeSourceSetPart(leaf.documentId);
|
|
253
|
+
const sectionPrefix = `route_${documentPart}_sections_${safeSourceSetPart(sectionAnchor.key)}_`;
|
|
254
|
+
if (!leaf.sourceSetId.startsWith(sectionPrefix))
|
|
255
|
+
return leaf;
|
|
256
|
+
return {
|
|
257
|
+
...leaf,
|
|
258
|
+
sourceSetId: `route_${documentPart}_${safeSourceSetPart(sectionAnchor.key)}_${leaf.sourceSetId.slice(sectionPrefix.length)}`,
|
|
259
|
+
};
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
function routeDocumentKeyForSourceMapEntry(entry, defaultKey) {
|
|
263
|
+
return routeSliceKeyForSourceMapEntry(entry) ?? documentKeyForSourceMapEntry(entry, defaultKey);
|
|
264
|
+
}
|
|
265
|
+
function routeSliceKeyForSourceMapEntry(entry) {
|
|
266
|
+
if (entry.resultPath[0] !== 'sections')
|
|
267
|
+
return null;
|
|
268
|
+
if (typeof entry.resultPath[1] !== 'number')
|
|
269
|
+
return null;
|
|
270
|
+
const sectionAnchor = entry.pathAnchors.find((anchor) => anchor.path === 'sections');
|
|
271
|
+
if (sectionAnchor === undefined)
|
|
272
|
+
return null;
|
|
273
|
+
const path = previewPathForSourceMapEntry(entry, 2);
|
|
274
|
+
if (path.length === 0)
|
|
275
|
+
return null;
|
|
276
|
+
return slicePathKey({ _key: sectionAnchor.key }, path);
|
|
277
|
+
}
|
|
278
|
+
function documentKeyForSourceMapEntry(entry, defaultKey) {
|
|
279
|
+
const path = previewPathForSourceMapEntry(entry, 0);
|
|
280
|
+
if (path.length === 0)
|
|
281
|
+
return defaultKey;
|
|
282
|
+
return documentPathKey({ documentId: entry.documentId }, path);
|
|
283
|
+
}
|
|
284
|
+
function previewPathForSourceMapEntry(entry, startIndex) {
|
|
285
|
+
const keyByArrayPath = new Map(entry.pathAnchors.map((anchor) => [anchor.path, anchor.key]));
|
|
286
|
+
const path = [];
|
|
287
|
+
for (let index = startIndex; index < entry.resultPath.length; index++) {
|
|
288
|
+
const part = entry.resultPath[index];
|
|
289
|
+
if (typeof part === 'number') {
|
|
290
|
+
path.push(fieldSegment(String(part)));
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const next = entry.resultPath[index + 1];
|
|
294
|
+
if (typeof next === 'number') {
|
|
295
|
+
const arrayPath = formatPreviewProvenanceCanonicalPath(entry.resultPath.slice(0, index + 1));
|
|
296
|
+
const itemKey = keyByArrayPath.get(arrayPath);
|
|
297
|
+
if (itemKey !== undefined && itemKey.length > 0) {
|
|
298
|
+
path.push(arrayItemSegment(part, itemKey));
|
|
299
|
+
index += 1;
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
path.push(fieldSegment(part));
|
|
304
|
+
}
|
|
305
|
+
if (entry.textNodeIndex !== undefined) {
|
|
306
|
+
path.push(richTextTextNodeSegment(entry.textNodeIndex));
|
|
307
|
+
}
|
|
308
|
+
return path;
|
|
309
|
+
}
|
|
310
|
+
function previewPathForPathParts(relativePath, rootPath, pathAnchors) {
|
|
311
|
+
const keyByArrayPath = new Map(pathAnchors.map((anchor) => [anchor.path, anchor.key]));
|
|
312
|
+
const path = [];
|
|
313
|
+
for (let index = 0; index < relativePath.length; index++) {
|
|
314
|
+
const part = relativePath[index];
|
|
315
|
+
if (typeof part === 'number') {
|
|
316
|
+
path.push(fieldSegment(String(part)));
|
|
317
|
+
continue;
|
|
318
|
+
}
|
|
319
|
+
const next = relativePath[index + 1];
|
|
320
|
+
if (typeof next === 'number') {
|
|
321
|
+
const arrayPath = formatPreviewProvenanceCanonicalPath([
|
|
322
|
+
...rootPath,
|
|
323
|
+
...relativePath.slice(0, index + 1),
|
|
324
|
+
]);
|
|
325
|
+
const itemKey = keyByArrayPath.get(arrayPath);
|
|
326
|
+
if (itemKey !== undefined && itemKey.length > 0) {
|
|
327
|
+
path.push(arrayItemSegment(part, itemKey));
|
|
328
|
+
index += 1;
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
path.push(fieldSegment(part));
|
|
333
|
+
}
|
|
334
|
+
return path;
|
|
335
|
+
}
|
|
336
|
+
function planContextDocumentPreviewProvenance(input) {
|
|
337
|
+
const plans = input.documents.map((document) => {
|
|
338
|
+
const materialized = materializePreviewProvenanceSourceMap({
|
|
339
|
+
queryResult: document,
|
|
340
|
+
manifest: input.manifest,
|
|
341
|
+
keyPrefix: `route:context:${storyTypeOf(document)}:${document.documentId}`,
|
|
342
|
+
sourceSetPrefix: `route_context_${safeSourceSetPart(storyTypeOf(document))}`,
|
|
343
|
+
keyForEntry: documentKeyForSourceMapEntry,
|
|
344
|
+
presence: 'optional',
|
|
345
|
+
});
|
|
346
|
+
const marks = materialized.leaves.filter((leaf) => leaf.sourceKind !== 'richtext' ||
|
|
347
|
+
input.richTextDocumentIds.has(document.documentId));
|
|
348
|
+
return materializedMarkPlan(marks, materialized, {
|
|
349
|
+
carrierSkipped: materialized.leaves.length - marks.length,
|
|
350
|
+
});
|
|
351
|
+
});
|
|
352
|
+
return mergeMaterializedMarkPlans(plans);
|
|
353
|
+
}
|
|
354
|
+
function collectReferencedContextDocumentIds(input) {
|
|
355
|
+
const documentById = new Map(input.contextDocuments.map((document) => [document.documentId, document]));
|
|
356
|
+
const selected = new Set();
|
|
357
|
+
const queue = [input.document];
|
|
358
|
+
for (let index = 0; index < queue.length; index++) {
|
|
359
|
+
for (const documentId of collectDocumentReferenceStrings(queue[index])) {
|
|
360
|
+
if (!documentById.has(documentId) || selected.has(documentId))
|
|
361
|
+
continue;
|
|
362
|
+
selected.add(documentId);
|
|
363
|
+
const referenced = documentById.get(documentId);
|
|
364
|
+
if (referenced !== undefined)
|
|
365
|
+
queue.push(referenced.fields);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return selected;
|
|
369
|
+
}
|
|
370
|
+
function collectDocumentReferenceStrings(root) {
|
|
371
|
+
const out = new Set();
|
|
372
|
+
const seen = new WeakSet();
|
|
373
|
+
const visit = (value, collectString) => {
|
|
374
|
+
if (typeof value === 'string') {
|
|
375
|
+
if (collectString)
|
|
376
|
+
out.add(value);
|
|
377
|
+
visitJsonString(value);
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
if (Array.isArray(value)) {
|
|
381
|
+
for (const item of value)
|
|
382
|
+
visit(item, false);
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (!isRecord(value))
|
|
386
|
+
return;
|
|
387
|
+
if (seen.has(value))
|
|
388
|
+
return;
|
|
389
|
+
seen.add(value);
|
|
390
|
+
for (const [key, child] of Object.entries(value)) {
|
|
391
|
+
if (key.startsWith('__ssPreview'))
|
|
392
|
+
continue;
|
|
393
|
+
visit(child, true);
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
const visitJsonString = (value) => {
|
|
397
|
+
const trimmed = value.trim();
|
|
398
|
+
if (!trimmed.startsWith('{') && !trimmed.startsWith('['))
|
|
399
|
+
return;
|
|
400
|
+
try {
|
|
401
|
+
visit(JSON.parse(trimmed), false);
|
|
402
|
+
}
|
|
403
|
+
catch {
|
|
404
|
+
// Non-JSON strings are ordinary content, not reference containers.
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
visit(root, false);
|
|
408
|
+
return [...out];
|
|
409
|
+
}
|
|
410
|
+
function collectSlicePlanningScopes(document) {
|
|
411
|
+
const fields = isRecord(document.fields) ? document.fields : {};
|
|
412
|
+
const sidecarByKey = richtextEmbedSidecarByKey(fields);
|
|
413
|
+
const sidecarIndexByKey = richtextEmbedSidecarIndexByKey(fields);
|
|
414
|
+
const zoneMappings = zoneMappingsSidecar(document);
|
|
415
|
+
const zoneTargetFields = collectActiveZoneTargetFields(fields, zoneMappings, sidecarByKey);
|
|
416
|
+
const scopes = [];
|
|
417
|
+
const addSliceZoneScope = (slices, sliceZonePath, sourceSliceZonePath, parentPathAnchors, sourceParentPathAnchors) => {
|
|
418
|
+
scopes.push({
|
|
419
|
+
slices,
|
|
420
|
+
sliceZonePath,
|
|
421
|
+
sourceSliceZonePath,
|
|
422
|
+
parentPathAnchors,
|
|
423
|
+
sourceParentPathAnchors,
|
|
424
|
+
sourceKind: 'sliceZone',
|
|
425
|
+
sidecarByKey,
|
|
426
|
+
sidecarIndexByKey,
|
|
427
|
+
});
|
|
428
|
+
const sliceZoneAnchorPath = formatPreviewProvenanceCanonicalPath(sliceZonePath);
|
|
429
|
+
const sourceSliceZoneAnchorPath = formatPreviewProvenanceCanonicalPath(sourceSliceZonePath);
|
|
430
|
+
for (let index = 0; index < slices.length; index++) {
|
|
431
|
+
const slice = slices[index];
|
|
432
|
+
if (!isRecord(slice))
|
|
433
|
+
continue;
|
|
434
|
+
const sliceType = typeof slice._type === 'string' ? slice._type : null;
|
|
435
|
+
const sliceKey = typeof slice._key === 'string' ? slice._key : null;
|
|
436
|
+
if (sliceType === null || sliceKey === null || sliceKey.length === 0) {
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
const childParentAnchors = [
|
|
440
|
+
...parentPathAnchors,
|
|
441
|
+
{ path: sliceZoneAnchorPath, key: sliceKey },
|
|
442
|
+
];
|
|
443
|
+
const childSourceParentAnchors = [
|
|
444
|
+
...sourceParentPathAnchors,
|
|
445
|
+
{ path: sourceSliceZoneAnchorPath, key: sliceKey },
|
|
446
|
+
];
|
|
447
|
+
const slicePath = [...sliceZonePath, index];
|
|
448
|
+
const mapping = zoneMappings[sliceType];
|
|
449
|
+
if (mapping !== undefined) {
|
|
450
|
+
for (const fieldPath of Object.values(mapping)) {
|
|
451
|
+
const value = getFieldPathValue(fields, fieldPath);
|
|
452
|
+
const zonePath = [...slicePath, fieldPath];
|
|
453
|
+
const sourceZonePath = fieldPathParts(fields, fieldPath);
|
|
454
|
+
if (isSliceArray(value)) {
|
|
455
|
+
addSliceZoneScope(value, zonePath, sourceZonePath, childParentAnchors, []);
|
|
456
|
+
continue;
|
|
457
|
+
}
|
|
458
|
+
const embeds = sliceEmbedsFromRichText(value, sidecarByKey);
|
|
459
|
+
if (embeds.length > 0) {
|
|
460
|
+
scopes.push({
|
|
461
|
+
slices: embeds,
|
|
462
|
+
sliceZonePath: zonePath,
|
|
463
|
+
sourceSliceZonePath: sourceZonePath,
|
|
464
|
+
parentPathAnchors: childParentAnchors,
|
|
465
|
+
sourceParentPathAnchors: [],
|
|
466
|
+
sourceKind: 'embed',
|
|
467
|
+
sidecarByKey,
|
|
468
|
+
sidecarIndexByKey,
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
for (const [field, value] of Object.entries(slice)) {
|
|
474
|
+
if (field.startsWith('_'))
|
|
475
|
+
continue;
|
|
476
|
+
const embeds = sliceEmbedsFromRichText(value, sidecarByKey);
|
|
477
|
+
if (embeds.length === 0)
|
|
478
|
+
continue;
|
|
479
|
+
scopes.push({
|
|
480
|
+
slices: embeds,
|
|
481
|
+
sliceZonePath: [...slicePath, field],
|
|
482
|
+
sourceSliceZonePath: [...sourceSliceZonePath, index, field],
|
|
483
|
+
parentPathAnchors: childParentAnchors,
|
|
484
|
+
sourceParentPathAnchors: childSourceParentAnchors,
|
|
485
|
+
sourceKind: 'embed',
|
|
486
|
+
sidecarByKey,
|
|
487
|
+
sidecarIndexByKey,
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
for (const [field, value] of Object.entries(fields)) {
|
|
493
|
+
if (zoneTargetFields.has(field))
|
|
494
|
+
continue;
|
|
495
|
+
if (isSliceArray(value)) {
|
|
496
|
+
addSliceZoneScope(value, [field], [field], [], []);
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
if (field === RICHTEXT_EMBED_STORE_FIELD)
|
|
500
|
+
continue;
|
|
501
|
+
const embeds = sliceEmbedsFromRichText(value, sidecarByKey);
|
|
502
|
+
if (embeds.length > 0) {
|
|
503
|
+
scopes.push({
|
|
504
|
+
slices: embeds,
|
|
505
|
+
sliceZonePath: [field],
|
|
506
|
+
sourceSliceZonePath: [field],
|
|
507
|
+
parentPathAnchors: [],
|
|
508
|
+
sourceParentPathAnchors: [],
|
|
509
|
+
sourceKind: 'embed',
|
|
510
|
+
sidecarByKey,
|
|
511
|
+
sidecarIndexByKey,
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return scopes;
|
|
516
|
+
}
|
|
517
|
+
export function unstable_collectPreviewRouteDocumentSliceTypes(document) {
|
|
518
|
+
const out = new Set();
|
|
519
|
+
for (const scope of collectSlicePlanningScopes(document)) {
|
|
520
|
+
for (const slice of scope.slices) {
|
|
521
|
+
if (!isRecord(slice))
|
|
522
|
+
continue;
|
|
523
|
+
const sliceType = slice._type;
|
|
524
|
+
if (typeof sliceType === 'string' && sliceType.length > 0) {
|
|
525
|
+
out.add(sliceType);
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
return out;
|
|
530
|
+
}
|
|
531
|
+
function zoneMappingsSidecar(document) {
|
|
532
|
+
const mappings = document._zoneMappings;
|
|
533
|
+
if (!isRecord(mappings))
|
|
534
|
+
return {};
|
|
535
|
+
const out = {};
|
|
536
|
+
for (const [sliceType, mapping] of Object.entries(mappings)) {
|
|
537
|
+
if (!isRecord(mapping))
|
|
538
|
+
continue;
|
|
539
|
+
const fields = {};
|
|
540
|
+
for (const [zoneName, fieldPath] of Object.entries(mapping)) {
|
|
541
|
+
if (typeof fieldPath === 'string' && fieldPath.length > 0) {
|
|
542
|
+
fields[zoneName] = fieldPath;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (Object.keys(fields).length > 0)
|
|
546
|
+
out[sliceType] = fields;
|
|
547
|
+
}
|
|
548
|
+
return out;
|
|
549
|
+
}
|
|
550
|
+
function collectActiveZoneTargetFields(fields, mappings, sidecarByKey) {
|
|
551
|
+
const out = new Set();
|
|
552
|
+
const visitSlices = (slices) => {
|
|
553
|
+
for (const slice of slices) {
|
|
554
|
+
if (!isRecord(slice))
|
|
555
|
+
continue;
|
|
556
|
+
const sliceType = typeof slice._type === 'string' ? slice._type : null;
|
|
557
|
+
const mapping = sliceType === null ? undefined : mappings[sliceType];
|
|
558
|
+
if (mapping !== undefined) {
|
|
559
|
+
for (const fieldPath of Object.values(mapping))
|
|
560
|
+
out.add(fieldPath);
|
|
561
|
+
}
|
|
562
|
+
for (const value of Object.values(slice)) {
|
|
563
|
+
if (isSliceArray(value))
|
|
564
|
+
visitSlices(value);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
for (const value of Object.values(fields)) {
|
|
569
|
+
if (isSliceArray(value)) {
|
|
570
|
+
visitSlices(value);
|
|
571
|
+
continue;
|
|
572
|
+
}
|
|
573
|
+
const embeds = sliceEmbedsFromRichText(value, sidecarByKey);
|
|
574
|
+
if (embeds.length > 0)
|
|
575
|
+
visitSlices(embeds);
|
|
576
|
+
}
|
|
577
|
+
return out;
|
|
578
|
+
}
|
|
579
|
+
function isSliceArray(value) {
|
|
580
|
+
return Array.isArray(value) && value.some(isSliceLike);
|
|
581
|
+
}
|
|
582
|
+
function isSliceLike(value) {
|
|
583
|
+
return (isRecord(value) &&
|
|
584
|
+
typeof value._type === 'string' &&
|
|
585
|
+
typeof value._key === 'string');
|
|
586
|
+
}
|
|
587
|
+
function getFieldPathValue(fields, fieldPath) {
|
|
588
|
+
if (Object.prototype.hasOwnProperty.call(fields, fieldPath)) {
|
|
589
|
+
return fields[fieldPath];
|
|
590
|
+
}
|
|
591
|
+
let cursor = fields;
|
|
592
|
+
for (const segment of fieldPath.split('.')) {
|
|
593
|
+
if (!isRecord(cursor))
|
|
594
|
+
return undefined;
|
|
595
|
+
cursor = cursor[segment];
|
|
596
|
+
}
|
|
597
|
+
return cursor;
|
|
598
|
+
}
|
|
599
|
+
function fieldPathParts(fields, fieldPath) {
|
|
600
|
+
if (Object.prototype.hasOwnProperty.call(fields, fieldPath)) {
|
|
601
|
+
return [fieldPath];
|
|
602
|
+
}
|
|
603
|
+
return fieldPath.split('.');
|
|
604
|
+
}
|
|
605
|
+
function richtextEmbedSidecarByKey(fields) {
|
|
606
|
+
const result = new Map();
|
|
607
|
+
const raw = fields[RICHTEXT_EMBED_STORE_FIELD];
|
|
608
|
+
if (!Array.isArray(raw))
|
|
609
|
+
return result;
|
|
610
|
+
for (const record of raw) {
|
|
611
|
+
if (isRecord(record) &&
|
|
612
|
+
typeof record._key === 'string' &&
|
|
613
|
+
record._key.length > 0 &&
|
|
614
|
+
!result.has(record._key)) {
|
|
615
|
+
result.set(record._key, record);
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
return result;
|
|
619
|
+
}
|
|
620
|
+
function richtextEmbedSidecarIndexByKey(fields) {
|
|
621
|
+
const result = new Map();
|
|
622
|
+
const raw = fields[RICHTEXT_EMBED_STORE_FIELD];
|
|
623
|
+
if (!Array.isArray(raw))
|
|
624
|
+
return result;
|
|
625
|
+
raw.forEach((record, index) => {
|
|
626
|
+
if (isRecord(record) &&
|
|
627
|
+
typeof record._key === 'string' &&
|
|
628
|
+
record._key.length > 0 &&
|
|
629
|
+
!result.has(record._key)) {
|
|
630
|
+
result.set(record._key, index);
|
|
631
|
+
}
|
|
632
|
+
});
|
|
633
|
+
return result;
|
|
634
|
+
}
|
|
635
|
+
function sliceEmbedsFromRichText(value, sidecarByKey = new Map()) {
|
|
636
|
+
const root = parseRichTextRoot(value);
|
|
637
|
+
if (!isRecord(root))
|
|
638
|
+
return [];
|
|
639
|
+
const embeds = [];
|
|
640
|
+
const visit = (node, index) => {
|
|
641
|
+
if (!isRecord(node))
|
|
642
|
+
return;
|
|
643
|
+
if (node.type === 'sliceEmbed') {
|
|
644
|
+
const attrs = isRecord(node.attrs) ? node.attrs : {};
|
|
645
|
+
const embedType = attrs._type;
|
|
646
|
+
if (typeof embedType === 'string' && embedType.length > 0) {
|
|
647
|
+
const embedKey = typeof attrs._key === 'string' ? attrs._key : `rt-${index}`;
|
|
648
|
+
const sidecar = sidecarByKey.get(embedKey);
|
|
649
|
+
const data = isRecord(attrs.data)
|
|
650
|
+
? attrs.data
|
|
651
|
+
: sidecar ?? {};
|
|
652
|
+
embeds.push({
|
|
653
|
+
_type: embedType,
|
|
654
|
+
_key: embedKey,
|
|
655
|
+
...(typeof attrs._variation === 'string'
|
|
656
|
+
? { _variation: attrs._variation }
|
|
657
|
+
: {}),
|
|
658
|
+
...data,
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
const content = node.content;
|
|
663
|
+
if (!Array.isArray(content))
|
|
664
|
+
return;
|
|
665
|
+
for (let childIndex = 0; childIndex < content.length; childIndex++) {
|
|
666
|
+
visit(content[childIndex], childIndex);
|
|
667
|
+
}
|
|
668
|
+
};
|
|
669
|
+
visit(root, 0);
|
|
670
|
+
return embeds;
|
|
671
|
+
}
|
|
672
|
+
function parseRichTextRoot(value) {
|
|
673
|
+
if (typeof value === 'string') {
|
|
674
|
+
const trimmed = value.trim();
|
|
675
|
+
if (!trimmed.startsWith('{'))
|
|
676
|
+
return null;
|
|
677
|
+
try {
|
|
678
|
+
return JSON.parse(trimmed);
|
|
679
|
+
}
|
|
680
|
+
catch {
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
return value;
|
|
685
|
+
}
|
|
686
|
+
function planPreviewProvenanceBoundSliceZoneTextLeaves(input) {
|
|
687
|
+
const fieldBindings = fieldBindingsSidecar(input.document, input.fieldBindingsFallback);
|
|
688
|
+
if (fieldBindings === null)
|
|
689
|
+
return [];
|
|
690
|
+
const sourceIdentity = previewProvenanceSourceIdentityFromQueryResult(input.document);
|
|
691
|
+
const leaves = [];
|
|
692
|
+
const sliceZoneAnchorPath = formatPreviewProvenanceCanonicalPath(input.sliceZonePath);
|
|
693
|
+
for (let index = 0; index < input.slices.length; index++) {
|
|
694
|
+
const slice = input.slices[index];
|
|
695
|
+
if (!isRecord(slice))
|
|
696
|
+
continue;
|
|
697
|
+
const sliceType = typeof slice._type === 'string' ? slice._type : null;
|
|
698
|
+
const sliceKey = typeof slice._key === 'string' ? slice._key : null;
|
|
699
|
+
if (sliceType === null || sliceKey === null || sliceKey.length === 0) {
|
|
700
|
+
continue;
|
|
701
|
+
}
|
|
702
|
+
const bindings = fieldBindingsForSlice(fieldBindings, sliceType, slice._variation);
|
|
703
|
+
if (bindings === undefined)
|
|
704
|
+
continue;
|
|
705
|
+
for (const [sliceField, ref] of Object.entries(bindings)) {
|
|
706
|
+
if (!isDocPathBindingRef(ref))
|
|
707
|
+
continue;
|
|
708
|
+
if (slice[sliceField] !== undefined)
|
|
709
|
+
continue;
|
|
710
|
+
const renderedValue = getValueByPath(input.document.fields, ref.path);
|
|
711
|
+
const richTextNodes = previewProvenanceRichTextTextNodes(renderedValue);
|
|
712
|
+
const pathAnchors = [
|
|
713
|
+
...(input.parentPathAnchors ?? []),
|
|
714
|
+
{ path: sliceZoneAnchorPath, key: sliceKey },
|
|
715
|
+
];
|
|
716
|
+
if (richTextNodes.length > 0) {
|
|
717
|
+
for (let textNodeIndex = 0; textNodeIndex < richTextNodes.length; textNodeIndex++) {
|
|
718
|
+
const renderedText = richTextNodes[textNodeIndex];
|
|
719
|
+
if (!renderedText)
|
|
720
|
+
continue;
|
|
721
|
+
leaves.push({
|
|
722
|
+
key: slicePathKey({ _key: sliceKey }, [
|
|
723
|
+
fieldSegment(sliceField),
|
|
724
|
+
richTextTextNodeSegment(textNodeIndex),
|
|
725
|
+
]),
|
|
726
|
+
...sourceIdentity,
|
|
727
|
+
canonicalPath: ref.path,
|
|
728
|
+
pathAnchors,
|
|
729
|
+
sourceKind: 'binding',
|
|
730
|
+
sourceSetId: `route_${safeSourceSetPart(sliceKey)}_${safeSourceSetPart(sliceField)}_richtext_${textNodeIndex}`,
|
|
731
|
+
outputKind: 'text',
|
|
732
|
+
modality: 'text',
|
|
733
|
+
anchorKind: 'element',
|
|
734
|
+
renderedText,
|
|
735
|
+
codedTextPath: ['fields', ...ref.path.split('.')],
|
|
736
|
+
codedRichTextTextNodeIndex: textNodeIndex,
|
|
737
|
+
presence: 'optional',
|
|
738
|
+
});
|
|
739
|
+
}
|
|
740
|
+
continue;
|
|
741
|
+
}
|
|
742
|
+
if (typeof renderedValue !== 'string' || renderedValue.length === 0) {
|
|
743
|
+
continue;
|
|
744
|
+
}
|
|
745
|
+
leaves.push({
|
|
746
|
+
key: sliceFieldKey({ _key: sliceKey }, sliceField),
|
|
747
|
+
...sourceIdentity,
|
|
748
|
+
canonicalPath: ref.path,
|
|
749
|
+
pathAnchors,
|
|
750
|
+
sourceKind: 'binding',
|
|
751
|
+
sourceSetId: `route_${safeSourceSetPart(sliceKey)}_${safeSourceSetPart(sliceField)}`,
|
|
752
|
+
outputKind: 'text',
|
|
753
|
+
modality: 'text',
|
|
754
|
+
anchorKind: 'element',
|
|
755
|
+
renderedText: renderedValue,
|
|
756
|
+
presence: 'optional',
|
|
757
|
+
});
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
return leaves;
|
|
761
|
+
}
|
|
762
|
+
function planPreviewProvenanceModelSliceZoneTextLeaves(input) {
|
|
763
|
+
const models = modelSidecar(input.document);
|
|
764
|
+
if (models === null)
|
|
765
|
+
return [];
|
|
766
|
+
const codedModels = modelSidecar(input.codedDocument);
|
|
767
|
+
const fieldBindings = fieldBindingsSidecar(input.document, input.fieldBindingsFallback) ?? {};
|
|
768
|
+
const leaves = [];
|
|
769
|
+
const sliceZoneAnchorPath = formatPreviewProvenanceCanonicalPath(input.sliceZonePath);
|
|
770
|
+
for (let index = 0; index < input.slices.length; index++) {
|
|
771
|
+
const slice = input.slices[index];
|
|
772
|
+
if (!isRecord(slice))
|
|
773
|
+
continue;
|
|
774
|
+
const sliceType = typeof slice._type === 'string' ? slice._type : null;
|
|
775
|
+
const sliceKey = typeof slice._key === 'string' ? slice._key : null;
|
|
776
|
+
const modelId = typeof slice._model === 'string' ? slice._model : null;
|
|
777
|
+
if (sliceType === null ||
|
|
778
|
+
sliceKey === null ||
|
|
779
|
+
sliceKey.length === 0 ||
|
|
780
|
+
modelId === null ||
|
|
781
|
+
modelId.length === 0) {
|
|
782
|
+
continue;
|
|
783
|
+
}
|
|
784
|
+
const refPath = modelRefPath(input.sliceZonePath, index);
|
|
785
|
+
const modelValues = modelValuesForSlice(models, modelId, refPath);
|
|
786
|
+
if (!isRecord(modelValues))
|
|
787
|
+
continue;
|
|
788
|
+
const codedModelValues = codedModels === null ? null : modelValuesForSlice(codedModels, modelId, refPath);
|
|
789
|
+
const bindings = fieldBindingsForSlice(fieldBindings, sliceType, slice._variation) ?? {};
|
|
790
|
+
for (const [modelField, renderedText] of Object.entries(modelValues)) {
|
|
791
|
+
if (Object.prototype.hasOwnProperty.call(bindings, modelField))
|
|
792
|
+
continue;
|
|
793
|
+
if (slice[modelField] !== undefined)
|
|
794
|
+
continue;
|
|
795
|
+
if (typeof renderedText !== 'string' || renderedText.length === 0) {
|
|
796
|
+
continue;
|
|
797
|
+
}
|
|
798
|
+
if (previewProvenanceRichTextTextNodes(renderedText).length > 0) {
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
801
|
+
if (!modelFieldMatchesManifest({
|
|
802
|
+
manifest: input.manifest,
|
|
803
|
+
sliceType,
|
|
804
|
+
field: modelField,
|
|
805
|
+
value: renderedText,
|
|
806
|
+
})) {
|
|
807
|
+
continue;
|
|
808
|
+
}
|
|
809
|
+
leaves.push({
|
|
810
|
+
key: sliceFieldKey({ _key: sliceKey }, modelField),
|
|
811
|
+
documentId: modelId,
|
|
812
|
+
readScope: modelReadScopeForSlice({
|
|
813
|
+
document: input.document,
|
|
814
|
+
sidecar: models,
|
|
815
|
+
modelId,
|
|
816
|
+
refPath,
|
|
817
|
+
}),
|
|
818
|
+
canonicalPath: modelField,
|
|
819
|
+
pathAnchors: [
|
|
820
|
+
...(input.parentPathAnchors ?? []),
|
|
821
|
+
{ path: sliceZoneAnchorPath, key: sliceKey },
|
|
822
|
+
],
|
|
823
|
+
sourceKind: 'model',
|
|
824
|
+
sourceSetId: `route_${safeSourceSetPart(sliceKey)}_${safeSourceSetPart(modelField)}`,
|
|
825
|
+
outputKind: 'text',
|
|
826
|
+
modality: 'text',
|
|
827
|
+
anchorKind: 'element',
|
|
828
|
+
renderedText,
|
|
829
|
+
codedTextPath: [modelField],
|
|
830
|
+
codedTextRoot: codedModelValues ?? undefined,
|
|
831
|
+
presence: 'optional',
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
return leaves;
|
|
836
|
+
}
|
|
837
|
+
function planPreviewProvenanceEmbedSliceTextLeaves(input) {
|
|
838
|
+
const leaves = [];
|
|
839
|
+
const sliceZoneAnchorPath = formatPreviewProvenanceCanonicalPath(input.sliceZonePath);
|
|
840
|
+
const sourceSliceZoneAnchorPath = formatPreviewProvenanceCanonicalPath(input.sourceSliceZonePath);
|
|
841
|
+
for (let index = 0; index < input.slices.length; index++) {
|
|
842
|
+
const slice = input.slices[index];
|
|
843
|
+
if (!isRecord(slice))
|
|
844
|
+
continue;
|
|
845
|
+
const sliceType = typeof slice._type === 'string' ? slice._type : null;
|
|
846
|
+
const sliceKey = typeof slice._key === 'string' ? slice._key : null;
|
|
847
|
+
if (sliceType === null || sliceKey === null || sliceKey.length === 0) {
|
|
848
|
+
continue;
|
|
849
|
+
}
|
|
850
|
+
const rootPath = [...input.sliceZonePath, index];
|
|
851
|
+
const sourceRootPath = [...input.sourceSliceZonePath, index];
|
|
852
|
+
const codedRootPath = ['fields', ...input.sourceSliceZonePath, index];
|
|
853
|
+
const pathAnchors = [
|
|
854
|
+
...(input.parentPathAnchors ?? []),
|
|
855
|
+
{ path: sliceZoneAnchorPath, key: sliceKey },
|
|
856
|
+
];
|
|
857
|
+
const navigationBaseAnchors = [
|
|
858
|
+
...(input.sourceParentPathAnchors ?? []),
|
|
859
|
+
{ path: sourceSliceZoneAnchorPath, key: sliceKey },
|
|
860
|
+
];
|
|
861
|
+
const match = createPreviewProvenanceEditableManifestMatcher(input.manifest, sliceType);
|
|
862
|
+
for (const candidate of planPreviewProvenanceEditableTextLeaves({
|
|
863
|
+
documentId: input.document.documentId,
|
|
864
|
+
root: slice,
|
|
865
|
+
rootPath,
|
|
866
|
+
pathAnchors,
|
|
867
|
+
keyPrefix: `route:embed:${safeSourceSetPart(sliceKey)}`,
|
|
868
|
+
sourceSetPrefix: `route_${safeSourceSetPart(sliceKey)}_embed`,
|
|
869
|
+
presence: 'optional',
|
|
870
|
+
match,
|
|
871
|
+
})) {
|
|
872
|
+
const navigationPath = formatPreviewProvenanceCanonicalPath([
|
|
873
|
+
...sourceRootPath,
|
|
874
|
+
...candidate.path,
|
|
875
|
+
]);
|
|
876
|
+
const navigationAnchors = remapEmbedLeafNavigationAnchors({
|
|
877
|
+
renderRootPath: rootPath,
|
|
878
|
+
sourceRootPath,
|
|
879
|
+
renderPathAnchors: candidate.leaf.pathAnchors,
|
|
880
|
+
navigationBaseAnchors,
|
|
881
|
+
});
|
|
882
|
+
const previewPath = previewPathForPathParts(candidate.path, rootPath, candidate.leaf.pathAnchors);
|
|
883
|
+
const keyedPath = candidate.leaf.codedRichTextTextNodeIndex === undefined
|
|
884
|
+
? previewPath
|
|
885
|
+
: [
|
|
886
|
+
...previewPath,
|
|
887
|
+
richTextTextNodeSegment(candidate.leaf.codedRichTextTextNodeIndex),
|
|
888
|
+
];
|
|
889
|
+
const storageAddress = embedStorageAddress({
|
|
890
|
+
sliceKey,
|
|
891
|
+
rootPath,
|
|
892
|
+
relativePath: candidate.path,
|
|
893
|
+
candidateAnchors: candidate.leaf.pathAnchors,
|
|
894
|
+
});
|
|
895
|
+
const sidecarIndex = input.sidecarIndexByKey.get(sliceKey);
|
|
896
|
+
leaves.push({
|
|
897
|
+
...candidate.leaf,
|
|
898
|
+
key: slicePathKey({ _key: sliceKey }, keyedPath),
|
|
899
|
+
canonicalPath: storageAddress.canonicalPath,
|
|
900
|
+
pathAnchors: storageAddress.pathAnchors,
|
|
901
|
+
codedTextPath: sidecarIndex === undefined
|
|
902
|
+
? [...codedRootPath, ...candidate.path]
|
|
903
|
+
: [
|
|
904
|
+
'fields',
|
|
905
|
+
RICHTEXT_EMBED_STORE_FIELD,
|
|
906
|
+
sidecarIndex,
|
|
907
|
+
...candidate.path,
|
|
908
|
+
],
|
|
909
|
+
navigationPath,
|
|
910
|
+
navigationAnchors,
|
|
911
|
+
sourceKind: 'embed',
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
for (const [field, value] of Object.entries(slice)) {
|
|
915
|
+
if (field.startsWith('_'))
|
|
916
|
+
continue;
|
|
917
|
+
const embeds = sliceEmbedsFromRichText(value, input.sidecarByKey);
|
|
918
|
+
if (embeds.length === 0)
|
|
919
|
+
continue;
|
|
920
|
+
leaves.push(...planPreviewProvenanceEmbedSliceTextLeaves({
|
|
921
|
+
document: input.document,
|
|
922
|
+
slices: embeds,
|
|
923
|
+
sliceZonePath: [...rootPath, field],
|
|
924
|
+
sourceSliceZonePath: [...input.sourceSliceZonePath, index, field],
|
|
925
|
+
parentPathAnchors: pathAnchors,
|
|
926
|
+
sourceParentPathAnchors: navigationBaseAnchors,
|
|
927
|
+
sidecarByKey: input.sidecarByKey,
|
|
928
|
+
sidecarIndexByKey: input.sidecarIndexByKey,
|
|
929
|
+
manifest: input.manifest,
|
|
930
|
+
}));
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return leaves;
|
|
934
|
+
}
|
|
935
|
+
function richtextEmbedKeySelector(key) {
|
|
936
|
+
return formatRichtextEmbedFieldPath(key).slice(RICHTEXT_EMBED_STORE_FIELD.length);
|
|
937
|
+
}
|
|
938
|
+
function embedStorageAddress(input) {
|
|
939
|
+
let canonicalPath = formatRichtextEmbedFieldPath(input.sliceKey);
|
|
940
|
+
const pathAnchors = [
|
|
941
|
+
{ path: RICHTEXT_EMBED_STORE_FIELD, key: input.sliceKey },
|
|
942
|
+
];
|
|
943
|
+
const traversed = [...input.rootPath];
|
|
944
|
+
for (const part of input.relativePath) {
|
|
945
|
+
if (typeof part === 'string') {
|
|
946
|
+
canonicalPath += `.${part}`;
|
|
947
|
+
traversed.push(part);
|
|
948
|
+
continue;
|
|
949
|
+
}
|
|
950
|
+
const sourceArrayPath = formatPreviewProvenanceCanonicalPath(traversed);
|
|
951
|
+
const anchor = input.candidateAnchors.find((candidate) => candidate.path === sourceArrayPath);
|
|
952
|
+
if (anchor) {
|
|
953
|
+
pathAnchors.push({ path: canonicalPath, key: anchor.key });
|
|
954
|
+
canonicalPath += richtextEmbedKeySelector(anchor.key);
|
|
955
|
+
}
|
|
956
|
+
else {
|
|
957
|
+
canonicalPath += `[${part}]`;
|
|
958
|
+
}
|
|
959
|
+
traversed.push(part);
|
|
960
|
+
}
|
|
961
|
+
return { canonicalPath, pathAnchors };
|
|
962
|
+
}
|
|
963
|
+
function remapEmbedLeafNavigationAnchors(input) {
|
|
964
|
+
const renderRootPath = formatPreviewProvenanceCanonicalPath(input.renderRootPath);
|
|
965
|
+
const sourceRootPath = formatPreviewProvenanceCanonicalPath(input.sourceRootPath);
|
|
966
|
+
const out = [...input.navigationBaseAnchors];
|
|
967
|
+
const seen = new Set(out.map((anchor) => `${anchor.path}\u0000${anchor.key}`));
|
|
968
|
+
for (const anchor of input.renderPathAnchors) {
|
|
969
|
+
if (anchor.path !== renderRootPath &&
|
|
970
|
+
!anchor.path.startsWith(`${renderRootPath}.`)) {
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
const path = `${sourceRootPath}${anchor.path.slice(renderRootPath.length)}`;
|
|
974
|
+
const signature = `${path}\u0000${anchor.key}`;
|
|
975
|
+
if (seen.has(signature))
|
|
976
|
+
continue;
|
|
977
|
+
seen.add(signature);
|
|
978
|
+
out.push({ path, key: anchor.key });
|
|
979
|
+
}
|
|
980
|
+
return out;
|
|
981
|
+
}
|
|
982
|
+
function modelFieldMatchesManifest(input) {
|
|
983
|
+
return (input.manifest[input.sliceType] ?? []).some((pattern) => previewProvenanceEditableTextPathPatternMatches({
|
|
984
|
+
path: [input.field],
|
|
985
|
+
fieldName: input.field,
|
|
986
|
+
value: input.value,
|
|
987
|
+
sourceKind: 'stored',
|
|
988
|
+
outputKind: 'text',
|
|
989
|
+
}, pattern));
|
|
990
|
+
}
|
|
991
|
+
function modelIdsFromRouteDocument(document) {
|
|
992
|
+
const ids = new Set();
|
|
993
|
+
for (const documentId of modelIdsFromSidecar(document))
|
|
994
|
+
ids.add(documentId);
|
|
995
|
+
for (const documentId of modelIdsFromModelRefs(document.fields))
|
|
996
|
+
ids.add(documentId);
|
|
997
|
+
return [...ids];
|
|
998
|
+
}
|
|
999
|
+
function modelIdsFromSidecar(document) {
|
|
1000
|
+
const models = modelSidecar(document);
|
|
1001
|
+
if (models === null)
|
|
1002
|
+
return [];
|
|
1003
|
+
const ids = new Set();
|
|
1004
|
+
const byId = models.byId;
|
|
1005
|
+
if (isRecord(byId)) {
|
|
1006
|
+
for (const [modelId, value] of Object.entries(byId)) {
|
|
1007
|
+
if (isRecord(value))
|
|
1008
|
+
ids.add(modelId);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
if (isRecord(models.unresolved)) {
|
|
1012
|
+
for (const failure of Object.values(models.unresolved)) {
|
|
1013
|
+
if (isRecord(failure) && typeof failure.modelId === 'string')
|
|
1014
|
+
ids.add(failure.modelId);
|
|
1015
|
+
}
|
|
1016
|
+
}
|
|
1017
|
+
return [...ids];
|
|
1018
|
+
}
|
|
1019
|
+
function modelIdsFromModelRefs(root) {
|
|
1020
|
+
const ids = new Set();
|
|
1021
|
+
const seen = new WeakSet();
|
|
1022
|
+
const visit = (value) => {
|
|
1023
|
+
if (typeof value === 'string') {
|
|
1024
|
+
visitJsonString(value);
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
if (!isRecord(value) && !Array.isArray(value))
|
|
1028
|
+
return;
|
|
1029
|
+
if (seen.has(value))
|
|
1030
|
+
return;
|
|
1031
|
+
seen.add(value);
|
|
1032
|
+
if (Array.isArray(value)) {
|
|
1033
|
+
for (const item of value)
|
|
1034
|
+
visit(item);
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
const modelId = value._model;
|
|
1038
|
+
if (typeof modelId === 'string' && modelId.length > 0) {
|
|
1039
|
+
ids.add(modelId);
|
|
1040
|
+
}
|
|
1041
|
+
for (const child of Object.values(value))
|
|
1042
|
+
visit(child);
|
|
1043
|
+
};
|
|
1044
|
+
const visitJsonString = (value) => {
|
|
1045
|
+
const trimmed = value.trim();
|
|
1046
|
+
if (!trimmed.startsWith('{') && !trimmed.startsWith('['))
|
|
1047
|
+
return;
|
|
1048
|
+
try {
|
|
1049
|
+
visit(JSON.parse(trimmed));
|
|
1050
|
+
}
|
|
1051
|
+
catch {
|
|
1052
|
+
// Non-JSON strings are ordinary field content, not model-reference containers.
|
|
1053
|
+
}
|
|
1054
|
+
};
|
|
1055
|
+
visit(root);
|
|
1056
|
+
return [...ids];
|
|
1057
|
+
}
|
|
1058
|
+
function routeDocumentFrameDependencies(input) {
|
|
1059
|
+
const routeStoryType = storyTypeOf(input.document);
|
|
1060
|
+
return [
|
|
1061
|
+
...(input.pathname === undefined ? [] : [{
|
|
1062
|
+
kind: 'route-lookup',
|
|
1063
|
+
pathname: input.pathname,
|
|
1064
|
+
...(routeStoryType === 'unknown' ? {} : { storyTypes: [routeStoryType] }),
|
|
1065
|
+
}]),
|
|
1066
|
+
{
|
|
1067
|
+
kind: 'document',
|
|
1068
|
+
documentId: input.document.documentId,
|
|
1069
|
+
...(routeStoryType === 'unknown' ? {} : { storyType: routeStoryType }),
|
|
1070
|
+
reason: input.pathname === undefined ? 'rendered-document' : 'route-document',
|
|
1071
|
+
},
|
|
1072
|
+
...input.modelDocumentIds.map((documentId) => ({
|
|
1073
|
+
kind: 'document',
|
|
1074
|
+
documentId,
|
|
1075
|
+
reason: 'model-document',
|
|
1076
|
+
})),
|
|
1077
|
+
...input.contextDocuments.map((document) => ({
|
|
1078
|
+
kind: 'document',
|
|
1079
|
+
documentId: document.documentId,
|
|
1080
|
+
...(storyTypeOf(document) === 'unknown'
|
|
1081
|
+
? {}
|
|
1082
|
+
: { storyType: storyTypeOf(document) }),
|
|
1083
|
+
reason: 'context-document',
|
|
1084
|
+
})),
|
|
1085
|
+
...input.extraDependencies,
|
|
1086
|
+
];
|
|
1087
|
+
}
|
|
1088
|
+
function modelSidecar(document) {
|
|
1089
|
+
const sidecar = document._models;
|
|
1090
|
+
return isRecord(sidecar) ? sidecar : null;
|
|
1091
|
+
}
|
|
1092
|
+
function modelValuesForSlice(sidecar, modelId, refPath) {
|
|
1093
|
+
if (isRecord(sidecar.unresolved) && isRecord(sidecar.unresolved[refPath]))
|
|
1094
|
+
return null;
|
|
1095
|
+
const byRef = sidecar.byRef;
|
|
1096
|
+
if (isRecord(byRef)) {
|
|
1097
|
+
const byRefEntry = byRef[refPath];
|
|
1098
|
+
if (isRecord(byRefEntry))
|
|
1099
|
+
return byRefEntry;
|
|
1100
|
+
}
|
|
1101
|
+
const byId = sidecar.byId;
|
|
1102
|
+
if (isRecord(byId)) {
|
|
1103
|
+
const byIdEntry = byId[modelId];
|
|
1104
|
+
if (isRecord(byIdEntry))
|
|
1105
|
+
return byIdEntry;
|
|
1106
|
+
}
|
|
1107
|
+
return null;
|
|
1108
|
+
}
|
|
1109
|
+
function modelReadScopeForSlice(input) {
|
|
1110
|
+
const hostScope = previewReadScopeEchoFromQueryResult(input.document);
|
|
1111
|
+
if (hostScope === undefined)
|
|
1112
|
+
return undefined;
|
|
1113
|
+
const byRef = input.sidecar.scopeByRef;
|
|
1114
|
+
const byId = input.sidecar.scopeById;
|
|
1115
|
+
const modelScope = (isRecord(byRef) && isRecord(byRef[input.refPath])
|
|
1116
|
+
? byRef[input.refPath]
|
|
1117
|
+
: null) ??
|
|
1118
|
+
(isRecord(byId) && isRecord(byId[input.modelId])
|
|
1119
|
+
? byId[input.modelId]
|
|
1120
|
+
: null);
|
|
1121
|
+
const releaseMemberState = modelScope?.releaseMemberState;
|
|
1122
|
+
return {
|
|
1123
|
+
requestedVariantId: hostScope.requestedVariantId,
|
|
1124
|
+
requestedDocumentId: hostScope.requestedDocumentId,
|
|
1125
|
+
...(typeof modelScope?.requestedLocale === 'string'
|
|
1126
|
+
? { requestedLocale: modelScope.requestedLocale }
|
|
1127
|
+
: hostScope.requestedLocale !== undefined
|
|
1128
|
+
? { requestedLocale: hostScope.requestedLocale }
|
|
1129
|
+
: {}),
|
|
1130
|
+
...(typeof modelScope?.appliedLocale === 'string'
|
|
1131
|
+
? { appliedLocale: modelScope.appliedLocale }
|
|
1132
|
+
: {}),
|
|
1133
|
+
appliesToDocument: hostScope.requestedDocumentId === null
|
|
1134
|
+
? null
|
|
1135
|
+
: hostScope.requestedDocumentId === input.modelId,
|
|
1136
|
+
// Variants are document-bound; a host variant never scopes a referenced
|
|
1137
|
+
// model merely because that model supplied the painted value.
|
|
1138
|
+
appliedVariantId: hostScope.requestedDocumentId === input.modelId
|
|
1139
|
+
? hostScope.appliedVariantId
|
|
1140
|
+
: null,
|
|
1141
|
+
requestedReleaseId: hostScope.requestedReleaseId,
|
|
1142
|
+
releaseScopeStatus: hostScope.releaseScopeStatus,
|
|
1143
|
+
...(hostScope.releaseLifecycleStatus !== undefined
|
|
1144
|
+
? { releaseLifecycleStatus: hostScope.releaseLifecycleStatus }
|
|
1145
|
+
: {}),
|
|
1146
|
+
appliedReleaseId: typeof modelScope?.appliedReleaseId === 'string'
|
|
1147
|
+
? modelScope.appliedReleaseId
|
|
1148
|
+
: null,
|
|
1149
|
+
...(releaseMemberState === 'active' ||
|
|
1150
|
+
releaseMemberState === 'archived' ||
|
|
1151
|
+
releaseMemberState === 'missing' ||
|
|
1152
|
+
releaseMemberState === 'locale-removed'
|
|
1153
|
+
? { releaseMemberState }
|
|
1154
|
+
: {}),
|
|
1155
|
+
...(releaseMemberState === 'archived' ||
|
|
1156
|
+
releaseMemberState === 'missing' ||
|
|
1157
|
+
releaseMemberState === 'locale-removed'
|
|
1158
|
+
? { degradedReason: releaseMemberState }
|
|
1159
|
+
: {}),
|
|
1160
|
+
};
|
|
1161
|
+
}
|
|
1162
|
+
function modelRefPath(sliceZonePath, index) {
|
|
1163
|
+
return `${formatModelRefBasePath(sliceZonePath)}[${index}]._model`;
|
|
1164
|
+
}
|
|
1165
|
+
function formatModelRefBasePath(pathSegments) {
|
|
1166
|
+
let out = '';
|
|
1167
|
+
for (const segment of pathSegments) {
|
|
1168
|
+
if (typeof segment === 'number') {
|
|
1169
|
+
out += `[${segment}]`;
|
|
1170
|
+
continue;
|
|
1171
|
+
}
|
|
1172
|
+
out += out.length === 0 ? segment : `.${segment}`;
|
|
1173
|
+
}
|
|
1174
|
+
return out;
|
|
1175
|
+
}
|
|
1176
|
+
function fieldBindingsSidecar(document, fallback) {
|
|
1177
|
+
const sidecar = document
|
|
1178
|
+
._fieldBindings;
|
|
1179
|
+
const runtimeBindings = isRecord(sidecar)
|
|
1180
|
+
? sidecar
|
|
1181
|
+
: null;
|
|
1182
|
+
if (fallback) {
|
|
1183
|
+
return mergeFieldBindings(fallback, runtimeBindings ?? {});
|
|
1184
|
+
}
|
|
1185
|
+
return runtimeBindings;
|
|
1186
|
+
}
|
|
1187
|
+
function mergeFieldBindings(fallback, runtime) {
|
|
1188
|
+
const out = { ...fallback, ...runtime };
|
|
1189
|
+
for (const sliceType of new Set([
|
|
1190
|
+
...Object.keys(fallback),
|
|
1191
|
+
...Object.keys(runtime),
|
|
1192
|
+
])) {
|
|
1193
|
+
const fallbackEntry = fallback[sliceType];
|
|
1194
|
+
const runtimeEntry = runtime[sliceType];
|
|
1195
|
+
if (!fallbackEntry && !runtimeEntry)
|
|
1196
|
+
continue;
|
|
1197
|
+
out[sliceType] = {
|
|
1198
|
+
base: {
|
|
1199
|
+
...(fallbackEntry?.base ?? {}),
|
|
1200
|
+
...(runtimeEntry?.base ?? {}),
|
|
1201
|
+
},
|
|
1202
|
+
variations: mergeVariationBindings(fallbackEntry?.variations ?? {}, runtimeEntry?.variations ?? {}),
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
return out;
|
|
1206
|
+
}
|
|
1207
|
+
function mergeVariationBindings(fallback, runtime) {
|
|
1208
|
+
const out = {};
|
|
1209
|
+
for (const variationId of new Set([
|
|
1210
|
+
...Object.keys(fallback),
|
|
1211
|
+
...Object.keys(runtime),
|
|
1212
|
+
])) {
|
|
1213
|
+
out[variationId] = {
|
|
1214
|
+
...(fallback[variationId] ?? {}),
|
|
1215
|
+
...(runtime[variationId] ?? {}),
|
|
1216
|
+
};
|
|
1217
|
+
}
|
|
1218
|
+
return out;
|
|
1219
|
+
}
|
|
1220
|
+
function fieldBindingsForSlice(sidecar, sliceType, variation) {
|
|
1221
|
+
const surfaces = sidecar[sliceType];
|
|
1222
|
+
if (!surfaces)
|
|
1223
|
+
return undefined;
|
|
1224
|
+
if (variation === undefined)
|
|
1225
|
+
return surfaces.base;
|
|
1226
|
+
if (typeof variation !== 'string')
|
|
1227
|
+
return undefined;
|
|
1228
|
+
return surfaces.variations[variation];
|
|
1229
|
+
}
|
|
1230
|
+
function isDocPathBindingRef(value) {
|
|
1231
|
+
return (isRecord(value) &&
|
|
1232
|
+
value.kind === 'docPath' &&
|
|
1233
|
+
typeof value.path === 'string' &&
|
|
1234
|
+
value.path.length > 0);
|
|
1235
|
+
}
|
|
1236
|
+
function getValueByPath(root, path) {
|
|
1237
|
+
if (!isRecord(root))
|
|
1238
|
+
return null;
|
|
1239
|
+
let cursor = root;
|
|
1240
|
+
for (const segment of path.split('.')) {
|
|
1241
|
+
if (!isRecord(cursor))
|
|
1242
|
+
return null;
|
|
1243
|
+
cursor = cursor[segment];
|
|
1244
|
+
}
|
|
1245
|
+
return cursor;
|
|
1246
|
+
}
|
|
1247
|
+
function emptyMaterializedMarkPlan() {
|
|
1248
|
+
return {
|
|
1249
|
+
marks: [],
|
|
1250
|
+
sourceMapEntries: 0,
|
|
1251
|
+
skipped: {
|
|
1252
|
+
manifestIneligible: 0,
|
|
1253
|
+
nonIdentityDerivation: 0,
|
|
1254
|
+
},
|
|
1255
|
+
carrierSkipped: 0,
|
|
1256
|
+
};
|
|
1257
|
+
}
|
|
1258
|
+
function materializedMarkPlan(marks, materialized, options = {}) {
|
|
1259
|
+
return {
|
|
1260
|
+
marks,
|
|
1261
|
+
sourceMapEntries: materialized.sourceMapEntries,
|
|
1262
|
+
skipped: materialized.skipped,
|
|
1263
|
+
carrierSkipped: options.carrierSkipped ?? 0,
|
|
1264
|
+
};
|
|
1265
|
+
}
|
|
1266
|
+
function mergeMaterializedMarkPlans(plans) {
|
|
1267
|
+
if (plans.length === 0)
|
|
1268
|
+
return emptyMaterializedMarkPlan();
|
|
1269
|
+
return {
|
|
1270
|
+
marks: plans.flatMap((plan) => plan.marks),
|
|
1271
|
+
sourceMapEntries: sum(plans, (plan) => plan.sourceMapEntries),
|
|
1272
|
+
skipped: {
|
|
1273
|
+
manifestIneligible: sum(plans, (plan) => plan.skipped.manifestIneligible),
|
|
1274
|
+
nonIdentityDerivation: sum(plans, (plan) => plan.skipped.nonIdentityDerivation),
|
|
1275
|
+
},
|
|
1276
|
+
carrierSkipped: sum(plans, (plan) => plan.carrierSkipped),
|
|
1277
|
+
};
|
|
1278
|
+
}
|
|
1279
|
+
function summarizeMaterializedMarkPlans(plans) {
|
|
1280
|
+
return {
|
|
1281
|
+
entries: sum(plans, (plan) => plan.sourceMapEntries),
|
|
1282
|
+
manifestIneligible: sum(plans, (plan) => plan.skipped.manifestIneligible),
|
|
1283
|
+
nonIdentityDerivation: sum(plans, (plan) => plan.skipped.nonIdentityDerivation),
|
|
1284
|
+
carrierSkipped: sum(plans, (plan) => plan.carrierSkipped),
|
|
1285
|
+
};
|
|
1286
|
+
}
|
|
1287
|
+
function sum(values, valueOf) {
|
|
1288
|
+
let total = 0;
|
|
1289
|
+
for (const value of values)
|
|
1290
|
+
total += valueOf(value);
|
|
1291
|
+
return total;
|
|
1292
|
+
}
|
|
1293
|
+
function collectCodedTextRootsByDocumentId(...rootsToScan) {
|
|
1294
|
+
const roots = new Map();
|
|
1295
|
+
for (const root of rootsToScan) {
|
|
1296
|
+
for (const document of collectPreviewProvenanceSourceMappedQueryResults(root)) {
|
|
1297
|
+
// Prefer the route document and its canonical `byId` sidecar carrier.
|
|
1298
|
+
// A matching page-data query result is still used for planning, but the
|
|
1299
|
+
// SDK resolver renders the sidecar entry and that is where coded text
|
|
1300
|
+
// must be materialized.
|
|
1301
|
+
if (!roots.has(document.documentId)) {
|
|
1302
|
+
roots.set(document.documentId, document);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
return roots;
|
|
1307
|
+
}
|
|
1308
|
+
function uniqueQueryResultsByDocumentId(documents) {
|
|
1309
|
+
const out = new Map();
|
|
1310
|
+
for (const document of documents)
|
|
1311
|
+
out.set(document.documentId, document);
|
|
1312
|
+
return [...out.values()];
|
|
1313
|
+
}
|
|
1314
|
+
function cloneJson(value) {
|
|
1315
|
+
return JSON.parse(JSON.stringify(value));
|
|
1316
|
+
}
|
|
1317
|
+
function isRecord(value) {
|
|
1318
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
1319
|
+
}
|
|
1320
|
+
function safeSourceSetPart(value) {
|
|
1321
|
+
return value.replace(/[^A-Za-z0-9]+/g, '_').replace(/^_+|_+$/g, '') || 'field';
|
|
1322
|
+
}
|
|
1323
|
+
function storyTypeOf(document) {
|
|
1324
|
+
return typeof document.storyType === 'string' && document.storyType.length > 0
|
|
1325
|
+
? document.storyType
|
|
1326
|
+
: 'unknown';
|
|
1327
|
+
}
|