@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.
Files changed (245) hide show
  1. package/LICENSE +112 -0
  2. package/PUBLIC-API.md +55 -0
  3. package/README.md +42 -2
  4. package/dist/.source-hash +1 -0
  5. package/dist/authoring/runtime-contract.d.ts +89 -0
  6. package/dist/authoring/runtime-contract.js +28 -0
  7. package/dist/channel-taxonomy.d.ts +17 -0
  8. package/dist/channel-taxonomy.js +46 -0
  9. package/dist/context-command.d.ts +18 -0
  10. package/dist/context-command.js +38 -0
  11. package/dist/dev/suspicious-codeword-detector.d.ts +15 -0
  12. package/dist/dev/suspicious-codeword-detector.js +220 -0
  13. package/dist/dev/suspicious-codeword-detector.noop.d.ts +3 -0
  14. package/dist/dev/suspicious-codeword-detector.noop.js +4 -0
  15. package/dist/framework/browser-context.d.ts +2 -0
  16. package/dist/framework/browser-context.js +2 -0
  17. package/dist/framework/generation-refresh.d.ts +23 -0
  18. package/dist/framework/generation-refresh.js +82 -0
  19. package/dist/framework/generation-signal.d.ts +8 -0
  20. package/dist/framework/generation-signal.js +17 -0
  21. package/dist/framework/projection-generation-watch.d.ts +5 -0
  22. package/dist/framework/projection-generation-watch.js +53 -0
  23. package/dist/framework/render-receipt.d.ts +8 -0
  24. package/dist/framework/render-receipt.js +10 -0
  25. package/dist/framework/runtime-adapter-store.d.ts +30 -0
  26. package/dist/framework/runtime-adapter-store.js +81 -0
  27. package/dist/grant-coordinator.d.ts +53 -0
  28. package/dist/grant-coordinator.js +329 -0
  29. package/dist/grant-lifecycle.d.ts +52 -0
  30. package/dist/grant-lifecycle.js +74 -0
  31. package/dist/identity/__test-adapter.d.ts +34 -0
  32. package/dist/identity/__test-adapter.js +59 -0
  33. package/dist/identity/browser-context-gate.d.ts +4 -0
  34. package/dist/identity/browser-context-gate.js +22 -0
  35. package/dist/identity/browser-context.d.ts +13 -0
  36. package/dist/identity/browser-context.js +22 -0
  37. package/dist/identity/carriers/postmessage.d.ts +84 -0
  38. package/dist/identity/carriers/postmessage.js +177 -0
  39. package/dist/identity/carriers/storage-state.d.ts +41 -0
  40. package/dist/identity/carriers/storage-state.js +99 -0
  41. package/dist/identity/carriers/storage.d.ts +39 -0
  42. package/dist/identity/carriers/storage.js +182 -0
  43. package/dist/identity/carriers/url.d.ts +19 -0
  44. package/dist/identity/carriers/url.js +38 -0
  45. package/dist/identity/identity-state-machine.d.ts +70 -0
  46. package/dist/identity/identity-state-machine.js +244 -0
  47. package/dist/identity/identity-types.d.ts +192 -0
  48. package/dist/identity/identity-types.js +22 -0
  49. package/dist/identity/index.d.ts +18 -0
  50. package/dist/identity/index.js +16 -0
  51. package/dist/identity/preview-cookie.d.ts +9 -0
  52. package/dist/identity/preview-cookie.js +12 -0
  53. package/dist/identity/preview-draft.d.ts +6 -0
  54. package/dist/identity/preview-draft.js +25 -0
  55. package/dist/identity/preview-identity.d.ts +28 -0
  56. package/dist/identity/preview-identity.js +84 -0
  57. package/dist/identity/preview-scope.d.ts +28 -0
  58. package/dist/identity/preview-scope.js +47 -0
  59. package/dist/identity/resolver.d.ts +110 -0
  60. package/dist/identity/resolver.js +334 -0
  61. package/dist/identity/session-query.d.ts +4 -0
  62. package/dist/identity/session-query.js +19 -0
  63. package/dist/index.d.ts +90 -0
  64. package/dist/index.js +788 -0
  65. package/dist/interaction-mode.d.ts +58 -0
  66. package/dist/interaction-mode.js +93 -0
  67. package/dist/nav-handler.d.ts +95 -0
  68. package/dist/nav-handler.js +84 -0
  69. package/dist/overlay-label.d.ts +79 -0
  70. package/dist/overlay-label.js +73 -0
  71. package/dist/provenance/anchor-map.d.ts +95 -0
  72. package/dist/provenance/anchor-map.js +177 -0
  73. package/dist/provenance/carrier-plan.d.ts +51 -0
  74. package/dist/provenance/carrier-plan.js +90 -0
  75. package/dist/provenance/coded-text.d.ts +20 -0
  76. package/dist/provenance/coded-text.js +302 -0
  77. package/dist/provenance/direct-edit-locator.d.ts +29 -0
  78. package/dist/provenance/direct-edit-locator.js +218 -0
  79. package/dist/provenance/editable-leaf.d.ts +134 -0
  80. package/dist/provenance/editable-leaf.js +483 -0
  81. package/dist/provenance/editor-target.d.ts +29 -0
  82. package/dist/provenance/editor-target.js +46 -0
  83. package/dist/provenance/field-leaf-walker.d.ts +21 -0
  84. package/dist/provenance/field-leaf-walker.js +98 -0
  85. package/dist/provenance/frame-dependencies.d.ts +212 -0
  86. package/dist/provenance/frame-dependencies.js +550 -0
  87. package/dist/provenance/index.d.ts +26 -0
  88. package/dist/provenance/index.js +26 -0
  89. package/dist/provenance/local-events.d.ts +1 -0
  90. package/dist/provenance/local-events.js +1 -0
  91. package/dist/provenance/markers.d.ts +21 -0
  92. package/dist/provenance/markers.js +70 -0
  93. package/dist/provenance/multi-frame-anchor-map.d.ts +54 -0
  94. package/dist/provenance/multi-frame-anchor-map.js +109 -0
  95. package/dist/provenance/normalize.d.ts +2 -0
  96. package/dist/provenance/normalize.js +19 -0
  97. package/dist/provenance/overlay-focus-echo.d.ts +17 -0
  98. package/dist/provenance/overlay-focus-echo.js +16 -0
  99. package/dist/provenance/overlay-projection.d.ts +65 -0
  100. package/dist/provenance/overlay-projection.js +201 -0
  101. package/dist/provenance/owner-map.d.ts +55 -0
  102. package/dist/provenance/owner-map.js +239 -0
  103. package/dist/provenance/read-carrier.d.ts +22 -0
  104. package/dist/provenance/read-carrier.js +92 -0
  105. package/dist/provenance/recorder.d.ts +38 -0
  106. package/dist/provenance/recorder.js +82 -0
  107. package/dist/provenance/render-context.d.ts +106 -0
  108. package/dist/provenance/render-context.js +338 -0
  109. package/dist/provenance/reveal-contract.d.ts +44 -0
  110. package/dist/provenance/reveal-contract.js +1 -0
  111. package/dist/provenance/richtext-slice-embed-address.d.ts +20 -0
  112. package/dist/provenance/richtext-slice-embed-address.js +35 -0
  113. package/dist/provenance/runtime-script.d.ts +1 -0
  114. package/dist/provenance/runtime-script.js +2217 -0
  115. package/dist/provenance/sidecar-store.d.ts +63 -0
  116. package/dist/provenance/sidecar-store.js +151 -0
  117. package/dist/provenance/sidecar.d.ts +34 -0
  118. package/dist/provenance/sidecar.js +227 -0
  119. package/dist/provenance/slice-model.d.ts +28 -0
  120. package/dist/provenance/slice-model.js +86 -0
  121. package/dist/provenance/source-identity.d.ts +3 -0
  122. package/dist/provenance/source-identity.js +7 -0
  123. package/dist/provenance/source-map-materializer.d.ts +76 -0
  124. package/dist/provenance/source-map-materializer.js +286 -0
  125. package/dist/provenance/source-registry.d.ts +103 -0
  126. package/dist/provenance/source-registry.js +562 -0
  127. package/dist/provenance/transformed-output-gate.d.ts +161 -0
  128. package/dist/provenance/transformed-output-gate.js +171 -0
  129. package/dist/provenance/types.d.ts +102 -0
  130. package/dist/provenance/types.js +1 -0
  131. package/dist/refresh-policy.d.ts +11 -0
  132. package/dist/refresh-policy.js +14 -0
  133. package/dist/reveal-target.d.ts +8 -0
  134. package/dist/reveal-target.js +47 -0
  135. package/dist/route-paths.d.ts +11 -0
  136. package/dist/route-paths.js +14 -0
  137. package/dist/router-subscriber.d.ts +92 -0
  138. package/dist/router-subscriber.js +44 -0
  139. package/dist/runtime/anchor-types.d.ts +5 -0
  140. package/dist/runtime/anchor-types.js +1 -0
  141. package/dist/runtime/authority-store.d.ts +31 -0
  142. package/dist/runtime/authority-store.js +56 -0
  143. package/dist/runtime/boot.d.ts +52 -0
  144. package/dist/runtime/boot.js +379 -0
  145. package/dist/runtime/content-refresh.d.ts +30 -0
  146. package/dist/runtime/content-refresh.js +68 -0
  147. package/dist/runtime/emit-runtime-error.d.ts +51 -0
  148. package/dist/runtime/emit-runtime-error.js +64 -0
  149. package/dist/runtime/field-proxy.d.ts +112 -0
  150. package/dist/runtime/field-proxy.js +471 -0
  151. package/dist/runtime/form-contract-reporter.d.ts +99 -0
  152. package/dist/runtime/form-contract-reporter.js +391 -0
  153. package/dist/runtime/invalidation-adapter.d.ts +17 -0
  154. package/dist/runtime/invalidation-adapter.js +42 -0
  155. package/dist/runtime/live-query-overlay.d.ts +39 -0
  156. package/dist/runtime/live-query-overlay.js +345 -0
  157. package/dist/runtime/momentary-navigate.d.ts +16 -0
  158. package/dist/runtime/momentary-navigate.js +106 -0
  159. package/dist/runtime/observation-scheduler.d.ts +15 -0
  160. package/dist/runtime/observation-scheduler.js +42 -0
  161. package/dist/runtime/perf-metrics.d.ts +28 -0
  162. package/dist/runtime/perf-metrics.js +90 -0
  163. package/dist/runtime/preview-interaction-policy.d.ts +1 -0
  164. package/dist/runtime/preview-interaction-policy.js +103 -0
  165. package/dist/runtime/preview-overlay-painter.d.ts +27 -0
  166. package/dist/runtime/preview-overlay-painter.js +1732 -0
  167. package/dist/runtime/preview-overlay-sidecar.d.ts +26 -0
  168. package/dist/runtime/preview-overlay-sidecar.js +45 -0
  169. package/dist/runtime/preview-perspective-alignment.d.ts +15 -0
  170. package/dist/runtime/preview-perspective-alignment.js +40 -0
  171. package/dist/runtime/preview-perspective-intent-state.d.ts +23 -0
  172. package/dist/runtime/preview-perspective-intent-state.js +5 -0
  173. package/dist/runtime/preview-transition-state.d.ts +142 -0
  174. package/dist/runtime/preview-transition-state.js +257 -0
  175. package/dist/runtime/render-mode-state.d.ts +51 -0
  176. package/dist/runtime/render-mode-state.js +76 -0
  177. package/dist/runtime/request-id.d.ts +4 -0
  178. package/dist/runtime/request-id.js +8 -0
  179. package/dist/runtime/resolve-schema-field.d.ts +99 -0
  180. package/dist/runtime/resolve-schema-field.js +356 -0
  181. package/dist/runtime/runtime-error-dispatch.d.ts +23 -0
  182. package/dist/runtime/runtime-error-dispatch.js +75 -0
  183. package/dist/runtime/schema-store.d.ts +121 -0
  184. package/dist/runtime/schema-store.js +667 -0
  185. package/dist/runtime/snapshot-assembly.d.ts +104 -0
  186. package/dist/runtime/snapshot-assembly.js +553 -0
  187. package/dist/runtime/snapshot-reporter.d.ts +55 -0
  188. package/dist/runtime/snapshot-reporter.js +375 -0
  189. package/dist/runtime/standalone-overlay-controller.d.ts +33 -0
  190. package/dist/runtime/standalone-overlay-controller.js +281 -0
  191. package/dist/runtime/store.d.ts +307 -0
  192. package/dist/runtime/store.js +1026 -0
  193. package/dist/runtime/windowed-minimap-projection.d.ts +28 -0
  194. package/dist/runtime/windowed-minimap-projection.js +189 -0
  195. package/dist/runtime/workspace-shortcuts.d.ts +30 -0
  196. package/dist/runtime/workspace-shortcuts.js +188 -0
  197. package/dist/sdk-error-copy.d.ts +48 -0
  198. package/dist/sdk-error-copy.js +182 -0
  199. package/dist/select-cursor.d.ts +2 -0
  200. package/dist/select-cursor.js +23 -0
  201. package/dist/server/admission-cookie.d.ts +38 -0
  202. package/dist/server/admission-cookie.js +122 -0
  203. package/dist/server/control-client.d.ts +29 -0
  204. package/dist/server/control-client.js +22 -0
  205. package/dist/server/control-errors.d.ts +22 -0
  206. package/dist/server/control-errors.js +165 -0
  207. package/dist/server/draft-route.d.ts +12 -0
  208. package/dist/server/draft-route.js +45 -0
  209. package/dist/server/extract-token.d.ts +30 -0
  210. package/dist/server/extract-token.js +98 -0
  211. package/dist/server/grant.d.ts +128 -0
  212. package/dist/server/grant.js +395 -0
  213. package/dist/server/ordinary-read-provenance.d.ts +11 -0
  214. package/dist/server/ordinary-read-provenance.js +124 -0
  215. package/dist/server/preview-read-metadata.d.ts +10 -0
  216. package/dist/server/preview-read-metadata.js +27 -0
  217. package/dist/server/provenance-frame-builder.d.ts +53 -0
  218. package/dist/server/provenance-frame-builder.js +102 -0
  219. package/dist/server/provenance-runtime.d.ts +70 -0
  220. package/dist/server/provenance-runtime.js +46 -0
  221. package/dist/server/read-context-command.d.ts +11 -0
  222. package/dist/server/read-context-command.js +53 -0
  223. package/dist/server/read-identity.d.ts +3 -0
  224. package/dist/server/read-identity.js +63 -0
  225. package/dist/server/read-lifecycle.d.ts +17 -0
  226. package/dist/server/read-lifecycle.js +52 -0
  227. package/dist/server/render-frame-provenance.d.ts +133 -0
  228. package/dist/server/render-frame-provenance.js +676 -0
  229. package/dist/server/render-id.d.ts +15 -0
  230. package/dist/server/render-id.js +22 -0
  231. package/dist/server/route-document-provenance.d.ts +77 -0
  232. package/dist/server/route-document-provenance.js +1327 -0
  233. package/dist/server/route-session-cookie.d.ts +33 -0
  234. package/dist/server/route-session-cookie.js +54 -0
  235. package/dist/session-projections.d.ts +33 -0
  236. package/dist/session-projections.js +43 -0
  237. package/dist/session-subscriber.d.ts +124 -0
  238. package/dist/session-subscriber.js +79 -0
  239. package/dist/sha256-fallback.d.ts +30 -0
  240. package/dist/sha256-fallback.js +126 -0
  241. package/dist/types.d.ts +446 -0
  242. package/dist/types.js +85 -0
  243. package/dist/validators.d.ts +61 -0
  244. package/dist/validators.js +110 -0
  245. package/package.json +206 -3
@@ -0,0 +1,676 @@
1
+ import { collectPreviewReadObservations } from '@storystick/client/internal';
2
+ import { PREVIEW_PROVENANCE_CODED_TEXT_START, appliedPreviewVariantIdFromQueryResult, previewReadScopeEchoFromQueryResult, requestedPreviewVariantIdFromQueryResult, classifyPreviewProvenanceCodedTextTarget, collectPreviewProvenanceSourceMappedQueryResults, collectPreviewFrameDependencies, createPreviewProvenanceAttributeCarrierPlan, createPreviewProvenanceCodedTextCarrierPlan, createPreviewFrameDependencySet, createPublicSidecar, materializePreviewProvenanceSourceMap, previewProvenanceSourceSetForLeaf, recordPreviewProvenanceEditableLeaves, validateProvenanceFrame, } from '@storystick/preview/provenance';
3
+ import { normalizePreviewVariantId } from '@storystick/preview/identity';
4
+ import { defaultPreviewProvenanceRenderId } from './render-id.js';
5
+ import { resolvePreviewFrameReadMetadataFromObservations, } from './preview-read-metadata.js';
6
+ import { withPreviewProvenanceContext, } from '../provenance/render-context.js';
7
+ import { buildPreviewProvenanceFrameWithRuntime, } from './provenance-frame-builder.js';
8
+ import { createPreviewProvenanceTemporalIdentity, } from './provenance-runtime.js';
9
+ const RENDER_FRAME_SOURCE_SET_CAP = 512;
10
+ const RENDER_FRAME_MARKER_CAP = 2_048;
11
+ const RENDER_FRAME_JSON_BYTES_CAP = 256 * 1024;
12
+ const ESTIMATED_MARKER_ID = `p_${'x'.repeat(22)}`;
13
+ export function unstable_previewRenderFrameRootAttrs(frame) {
14
+ return frame?.sessionId
15
+ ? { 'data-preview-message-session-id': frame.sessionId }
16
+ : {};
17
+ }
18
+ export async function unstable_buildPreviewRenderFrameWithRuntime(options, runtime) {
19
+ const enabled = options.enabled ?? runtime.config.isEnabled();
20
+ if (!enabled)
21
+ return null;
22
+ runtime.recorder.markNoStore({ enabled });
23
+ const codeVersion = options.codeVersion ?? runtime.config.resolveCodeVersion();
24
+ const readObservations = collectPreviewReadObservations(options.codedDocument, options.codedContext);
25
+ const readMetadata = typeof options.observationId === 'string' &&
26
+ typeof options.renderWatermark === 'number'
27
+ ? {
28
+ readContext: options.readContext,
29
+ receiptIds: options.receiptIds ?? [],
30
+ observationId: options.observationId,
31
+ renderWatermark: options.renderWatermark,
32
+ }
33
+ : resolvePreviewFrameReadMetadataFromObservations(codeVersion, readObservations);
34
+ if (readMetadata === null && readObservations.some((read) => read.readContext))
35
+ return null;
36
+ const input = {
37
+ runtime,
38
+ enabled,
39
+ frameId: options.frameId,
40
+ routeHash: options.routeHash,
41
+ variantId: options.variantId,
42
+ previewScope: options.previewScope,
43
+ fallbackPathname: options.fallbackPathname ?? options.pathname,
44
+ sessionId: options.sessionId,
45
+ codeVersion,
46
+ renderId: options.renderId,
47
+ generation: options.generation,
48
+ renderNonce: options.renderNonce,
49
+ emissionRevision: options.emissionRevision,
50
+ readMetadata,
51
+ authoritativeDocumentIds: options.authoritativeDocumentIds,
52
+ dependencies: [
53
+ ...(options.dependencies ?? []),
54
+ ...collectPreviewFrameDependencies(options.codedDocument, options.codedContext),
55
+ ],
56
+ leaves: options.leaves,
57
+ sourceMapDiagnostics: options.sourceMapDiagnostics ?? defaultSourceMapDiagnostics(options.leaves),
58
+ codedDocument: options.codedDocument,
59
+ codedContext: options.codedContext,
60
+ codedTextRoot: options.codedTextRoot ?? options.codedDocument,
61
+ codedTextRootPrefix: options.codedTextRootPrefix,
62
+ codedTextRootsByDocumentId: options.codedTextRootsByDocumentId,
63
+ fixture: options.fixture,
64
+ projectId: options.projectId,
65
+ endpoint: options.endpoint,
66
+ convexUrl: options.convexUrl,
67
+ };
68
+ const capped = capPreviewProvenanceLeaves(input);
69
+ if (input.fixture?.enabled) {
70
+ return buildFixtureRenderFrame(input, capped);
71
+ }
72
+ const result = await buildPreviewProvenanceFrameWithRuntime({
73
+ enabled: input.enabled,
74
+ frameId: input.frameId,
75
+ sessionId: input.sessionId,
76
+ routeHash: input.routeHash,
77
+ variantId: input.variantId,
78
+ previewScope: input.previewScope,
79
+ fallbackPathname: input.fallbackPathname,
80
+ codeVersion: input.codeVersion,
81
+ renderId: input.renderId,
82
+ generation: input.generation,
83
+ renderNonce: input.renderNonce,
84
+ emissionRevision: input.emissionRevision,
85
+ readContext: input.readMetadata?.readContext,
86
+ receiptIds: input.readMetadata?.receiptIds,
87
+ observationId: input.readMetadata?.observationId,
88
+ renderWatermark: input.readMetadata?.renderWatermark,
89
+ authoritativeDocumentIds: input.authoritativeDocumentIds,
90
+ dependencies: input.dependencies,
91
+ leaves: capped.leaves,
92
+ codedTextRoot: input.codedTextRoot,
93
+ codedTextRootPrefix: input.codedTextRootPrefix,
94
+ codedTextRootsByDocumentId: input.codedTextRootsByDocumentId,
95
+ projectId: input.projectId,
96
+ endpoint: input.endpoint,
97
+ convexUrl: input.convexUrl,
98
+ }, runtime);
99
+ if (result === null)
100
+ return null;
101
+ logPreviewProvenanceCodedTextWriteDebug({
102
+ frameId: input.frameId,
103
+ pathname: input.fallbackPathname,
104
+ cap: capped,
105
+ codedTextWrites: result.codedTextWrites,
106
+ codedDocument: input.codedDocument,
107
+ codedContext: input.codedContext,
108
+ });
109
+ return {
110
+ frameId: input.frameId,
111
+ sessionId: result.sessionId,
112
+ routeHash: result.routeHash,
113
+ variantId: result.variantId,
114
+ attrsByKey: result.attrsByKey,
115
+ codedDocument: attachPreviewProvenanceContext(input.codedDocument, result.attrsByKey),
116
+ codedContext: attachPreviewProvenanceContext(input.codedContext, result.attrsByKey),
117
+ renderFrame: result.renderFrame,
118
+ dependencySet: result.dependencySet,
119
+ publicSidecar: result.publicSidecar,
120
+ validationOk: result.validationOk,
121
+ diagnostics: previewRenderFrameDiagnostics({
122
+ input,
123
+ cap: capped,
124
+ frame: result.renderFrame,
125
+ publicSidecar: result.publicSidecar,
126
+ codedTextWrites: result.codedTextWrites,
127
+ }),
128
+ };
129
+ }
130
+ export async function unstable_buildPreviewDocumentRenderFrameFromReadsWithRuntime(options, runtime) {
131
+ const enabled = options.enabled ?? runtime.config.isEnabled();
132
+ if (!enabled)
133
+ return null;
134
+ const codedContext = options.context === undefined ? undefined : cloneJson(options.context);
135
+ const reads = options.documents ?? collectPreviewProvenanceSourceMappedQueryResults(options.context);
136
+ const documents = reads.map(normalizePreviewDocumentRenderFrameRead).filter((entry) => entry.document !== null && entry.document !== undefined);
137
+ const plans = documents.map((entry) => planPreviewDocumentRenderFrameRead({
138
+ frameId: options.frameId,
139
+ manifest: options.manifest,
140
+ read: entry,
141
+ }));
142
+ const sourceMapDiagnostics = summarizeDocumentMarkPlans(plans);
143
+ const codedTextRootsByDocumentId = codedTextRootsForRenderFrameReads({
144
+ reads: documents,
145
+ codedContext,
146
+ });
147
+ const dependencies = [
148
+ ...(options.dependencies ?? []),
149
+ ...dependenciesForRenderFrameReads(documents),
150
+ ...collectPreviewFrameDependencies(options.context, ...documents.map((read) => read.document)),
151
+ ];
152
+ const echoedAppliedVariantId = appliedVariantIdFromRenderFrameReads(documents);
153
+ const echoedRequestedVariantId = requestedVariantIdFromRenderFrameReads(documents);
154
+ const previewScope = previewScopeFromRenderFrameReads(documents);
155
+ const variantId = echoedAppliedVariantId !== undefined
156
+ ? echoedAppliedVariantId
157
+ : echoedRequestedVariantId !== undefined
158
+ ? echoedRequestedVariantId
159
+ : options.variantId === undefined
160
+ ? variantIdFromRenderFrameReads(documents)
161
+ : normalizePreviewVariantId(options.variantId);
162
+ const codeVersion = options.codeVersion ?? runtime.config.resolveCodeVersion();
163
+ const readObservations = collectPreviewReadObservations(options.context, ...documents.map((read) => read.document));
164
+ const readMetadata = resolvePreviewFrameReadMetadataFromObservations(codeVersion, readObservations);
165
+ if (readMetadata === null && readObservations.some((read) => read.readContext))
166
+ return null;
167
+ return unstable_buildPreviewRenderFrameWithRuntime({
168
+ enabled,
169
+ frameId: options.frameId,
170
+ routeHash: options.routeHash,
171
+ variantId: options.variantId === undefined ? variantId ?? undefined : variantId,
172
+ previewScope,
173
+ sessionId: options.sessionId,
174
+ codeVersion,
175
+ renderId: options.renderId,
176
+ generation: options.generation,
177
+ renderNonce: options.renderNonce,
178
+ emissionRevision: options.emissionRevision,
179
+ readContext: readMetadata?.readContext,
180
+ receiptIds: readMetadata?.receiptIds,
181
+ observationId: readMetadata?.observationId,
182
+ renderWatermark: readMetadata?.renderWatermark,
183
+ authoritativeDocumentIds: options.authoritativeDocumentIds ?? documents
184
+ .map((entry) => entry.document.documentId)
185
+ .filter((value) => value.length > 0),
186
+ dependencies,
187
+ leaves: plans.flatMap((plan) => plan.marks),
188
+ sourceMapDiagnostics,
189
+ codedContext,
190
+ codedTextRootPrefix: options.codedTextRootPrefix ?? [],
191
+ codedTextRootsByDocumentId,
192
+ fixture: options.fixture,
193
+ projectId: options.projectId,
194
+ endpoint: options.endpoint,
195
+ convexUrl: options.convexUrl,
196
+ }, runtime);
197
+ }
198
+ function normalizePreviewDocumentRenderFrameRead(read) {
199
+ if (isRecord(read) && 'document' in read) {
200
+ return read;
201
+ }
202
+ return { document: read };
203
+ }
204
+ function dependenciesForRenderFrameReads(reads) {
205
+ return reads.flatMap((read) => {
206
+ const storyType = read.storyType ?? storyTypeOf(read.document);
207
+ return [
208
+ {
209
+ kind: 'document',
210
+ documentId: read.document.documentId,
211
+ ...(storyType === 'unknown' ? {} : { storyType }),
212
+ reason: 'scope-document',
213
+ },
214
+ ...(read.dependencies ?? []),
215
+ ];
216
+ });
217
+ }
218
+ function variantIdFromRenderFrameReads(reads) {
219
+ for (const read of reads) {
220
+ const variantId = normalizePreviewVariantId(read.document._locale?.variantId) ??
221
+ normalizePreviewVariantId(read.document._variant?.variantId);
222
+ if (variantId !== null)
223
+ return variantId;
224
+ }
225
+ return null;
226
+ }
227
+ function appliedVariantIdFromRenderFrameReads(reads) {
228
+ let sawScopeEcho = false;
229
+ for (const read of reads) {
230
+ const appliedVariantId = appliedPreviewVariantIdFromQueryResult(read.document);
231
+ if (appliedVariantId === undefined)
232
+ continue;
233
+ sawScopeEcho = true;
234
+ if (appliedVariantId !== null)
235
+ return appliedVariantId;
236
+ }
237
+ return sawScopeEcho ? null : undefined;
238
+ }
239
+ function requestedVariantIdFromRenderFrameReads(reads) {
240
+ for (const read of reads) {
241
+ const requestedVariantId = requestedPreviewVariantIdFromQueryResult(read.document);
242
+ if (requestedVariantId !== undefined)
243
+ return requestedVariantId;
244
+ }
245
+ return undefined;
246
+ }
247
+ function previewScopeFromRenderFrameReads(reads) {
248
+ let fallback;
249
+ for (const read of reads) {
250
+ const scope = previewReadScopeEchoFromQueryResult(read.document);
251
+ if (scope === undefined)
252
+ continue;
253
+ fallback ??= scope;
254
+ if (scope.appliesToDocument !== false)
255
+ return scope;
256
+ }
257
+ return fallback;
258
+ }
259
+ function defaultSourceMapDiagnostics(leaves) {
260
+ return {
261
+ entries: leaves.length,
262
+ manifestIneligible: 0,
263
+ nonIdentityDerivation: 0,
264
+ carrierSkipped: 0,
265
+ };
266
+ }
267
+ function planPreviewDocumentRenderFrameRead(input) {
268
+ const storyType = input.read.storyType ?? storyTypeOf(input.read.document);
269
+ const fields = input.read.fields ?? input.read.document.fields;
270
+ if (fields === null || fields === undefined) {
271
+ return emptyDocumentMarkPlan();
272
+ }
273
+ const queryResult = input.read.fields === undefined && input.read.storyType === undefined
274
+ ? input.read.document
275
+ : {
276
+ documentId: input.read.document.documentId,
277
+ storyType,
278
+ fields,
279
+ };
280
+ const materialized = materializePreviewProvenanceSourceMap({
281
+ queryResult,
282
+ manifest: input.manifest,
283
+ // Keys remain document-scoped here. Source-set document scoping belongs to
284
+ // the materializer so every caller receives it exactly once.
285
+ keyPrefix: input.read.keyPrefix ??
286
+ `${input.frameId}:${storyType}:${input.read.document.documentId}`,
287
+ sourceSetPrefix: input.read.sourceSetPrefix ??
288
+ `${safeSourceSetPart(input.frameId)}_${safeSourceSetPart(storyType)}`,
289
+ presence: 'optional',
290
+ });
291
+ const marks = materialized.leaves.flatMap((leaf) => {
292
+ if (input.read.renderedLeavesPolicy === undefined)
293
+ return [leaf];
294
+ const expected = input.read.renderedLeavesPolicy({
295
+ document: input.read.document,
296
+ storyType,
297
+ fields,
298
+ canonicalPath: leaf.canonicalPath,
299
+ leaf,
300
+ });
301
+ return expected ? [{ ...leaf, presence: 'expected' }] : [];
302
+ });
303
+ return documentMarkPlan(marks, materialized, {
304
+ carrierSkipped: materialized.leaves.length - marks.length,
305
+ });
306
+ }
307
+ function codedTextRootsForRenderFrameReads(input) {
308
+ const roots = new Map();
309
+ const contextDocuments = new Map(collectPreviewProvenanceSourceMappedQueryResults(input.codedContext)
310
+ .map((document) => [document.documentId, document]));
311
+ for (const read of input.reads) {
312
+ if (read.codedTextRootPath) {
313
+ const root = valueAtPath(input.codedContext, read.codedTextRootPath);
314
+ if (root !== undefined)
315
+ roots.set(read.document.documentId, root);
316
+ continue;
317
+ }
318
+ const contextDocument = contextDocuments.get(read.document.documentId);
319
+ if (contextDocument && isRecord(contextDocument.fields)) {
320
+ roots.set(read.document.documentId, contextDocument.fields);
321
+ }
322
+ }
323
+ return roots;
324
+ }
325
+ function emptyDocumentMarkPlan() {
326
+ return {
327
+ marks: [],
328
+ sourceMapEntries: 0,
329
+ skipped: {
330
+ manifestIneligible: 0,
331
+ nonIdentityDerivation: 0,
332
+ },
333
+ carrierSkipped: 0,
334
+ };
335
+ }
336
+ function documentMarkPlan(marks, materialized, options = {}) {
337
+ return {
338
+ marks,
339
+ sourceMapEntries: materialized.sourceMapEntries,
340
+ skipped: materialized.skipped,
341
+ carrierSkipped: options.carrierSkipped ?? 0,
342
+ };
343
+ }
344
+ function summarizeDocumentMarkPlans(plans) {
345
+ return {
346
+ entries: sum(plans, (plan) => plan.sourceMapEntries),
347
+ manifestIneligible: sum(plans, (plan) => plan.skipped.manifestIneligible),
348
+ nonIdentityDerivation: sum(plans, (plan) => plan.skipped.nonIdentityDerivation),
349
+ carrierSkipped: sum(plans, (plan) => plan.carrierSkipped),
350
+ };
351
+ }
352
+ function capPreviewProvenanceLeaves(input) {
353
+ const leaves = [];
354
+ let capped = false;
355
+ for (const leaf of input.leaves) {
356
+ if (leaves.length >= RENDER_FRAME_SOURCE_SET_CAP ||
357
+ leaves.length >= RENDER_FRAME_MARKER_CAP) {
358
+ capped = true;
359
+ continue;
360
+ }
361
+ const candidate = [...leaves, leaf];
362
+ if (estimatePreviewProvenanceFrameBytes(input, candidate) >
363
+ RENDER_FRAME_JSON_BYTES_CAP) {
364
+ capped = true;
365
+ continue;
366
+ }
367
+ leaves.push(leaf);
368
+ }
369
+ return {
370
+ leaves,
371
+ dropped: input.leaves.length - leaves.length,
372
+ capped,
373
+ };
374
+ }
375
+ function previewRenderFrameDiagnostics(input) {
376
+ const carrierSkipped = input.input.sourceMapDiagnostics.carrierSkipped +
377
+ input.cap.dropped +
378
+ countSkippedCarrierPlans(input.publicSidecar) +
379
+ input.codedTextWrites.failures.length;
380
+ return {
381
+ sourceMapEntries: input.input.sourceMapDiagnostics.entries,
382
+ carriersWritten: countWrittenCarrierPlans(input.publicSidecar),
383
+ carrierSkipped,
384
+ frameBytes: jsonByteLength(input.frame),
385
+ frameCapped: input.cap.capped,
386
+ skipped: {
387
+ manifestIneligible: input.input.sourceMapDiagnostics.manifestIneligible,
388
+ nonIdentityDerivation: input.input.sourceMapDiagnostics.nonIdentityDerivation,
389
+ cap: input.cap.dropped,
390
+ codedTextWriteFailures: input.codedTextWrites.failures.length,
391
+ },
392
+ };
393
+ }
394
+ function estimatePreviewProvenanceFrameBytes(input, leaves) {
395
+ const routeHash = input.routeHash ?? 'x'.repeat(64);
396
+ const codeVersion = input.codeVersion ?? input.runtime.config.resolveCodeVersion();
397
+ const renderId = input.renderId ??
398
+ defaultPreviewProvenanceRenderId({
399
+ frameId: input.frameId,
400
+ routeHash,
401
+ });
402
+ const frame = {
403
+ type: 'storystick.preview.provenance-frame',
404
+ version: 1,
405
+ sessionId: input.sessionId ?? input.fixture?.sessionId ?? 'preview-session',
406
+ routeHash,
407
+ ...(normalizePreviewVariantId(input.variantId) !== null
408
+ ? { variantId: normalizePreviewVariantId(input.variantId) }
409
+ : {}),
410
+ codeVersion,
411
+ renderId,
412
+ generation: input.generation ?? 1,
413
+ sourceSets: leaves.map(estimatedSourceSetForLeaf),
414
+ markers: leaves.map((leaf) => ({
415
+ markerId: ESTIMATED_MARKER_ID,
416
+ sourceSetId: leaf.sourceSetId,
417
+ modality: 'text',
418
+ anchorKind: 'element',
419
+ carrierPlans: estimatedCarrierPlansForLeaf(leaf),
420
+ anchorMultiplicity: leaf.anchorMultiplicity,
421
+ presence: leaf.presence,
422
+ renderedTextFingerprint: 'txt:000000:000000',
423
+ })),
424
+ };
425
+ return jsonByteLength(frame);
426
+ }
427
+ function estimatedSourceSetForLeaf(leaf) {
428
+ return previewProvenanceSourceSetForLeaf(leaf);
429
+ }
430
+ function estimatedCarrierPlansForLeaf(leaf) {
431
+ const carrierPlans = [
432
+ createPreviewProvenanceAttributeCarrierPlan({
433
+ modality: 'text',
434
+ anchorKind: 'element',
435
+ }),
436
+ ];
437
+ if (leaf.codedRichTextTextNodeIndex !== undefined) {
438
+ carrierPlans.push(createPreviewProvenanceCodedTextCarrierPlan({
439
+ surface: 'rich-text-text-node',
440
+ }));
441
+ }
442
+ else if (leaf.codedText === true || leaf.codedTextPath !== undefined) {
443
+ const policy = classifyPreviewProvenanceCodedTextTarget({
444
+ canonicalPath: leaf.navigationPath ?? leaf.canonicalPath,
445
+ sourceKind: leaf.sourceKind === 'richtext' ? 'richtext' : 'stored',
446
+ });
447
+ if (policy.allowed) {
448
+ carrierPlans.push(createPreviewProvenanceCodedTextCarrierPlan({
449
+ surface: policy.surface,
450
+ }));
451
+ }
452
+ }
453
+ return carrierPlans;
454
+ }
455
+ function countWrittenCarrierPlans(sidecar) {
456
+ return sum(sidecar?.markers ?? [], (marker) => (marker.carrierPlans ?? []).filter((plan) => plan.status === 'emitted')
457
+ .length);
458
+ }
459
+ function countSkippedCarrierPlans(sidecar) {
460
+ return sum(sidecar?.markers ?? [], (marker) => (marker.carrierPlans ?? []).filter((plan) => plan.status !== 'emitted')
461
+ .length);
462
+ }
463
+ async function buildFixtureRenderFrame(input, capped) {
464
+ const identity = await resolveFixturePreviewIdentity(input);
465
+ const codeVersion = input.codeVersion ?? input.runtime.config.resolveCodeVersion();
466
+ const renderId = input.renderId ??
467
+ defaultPreviewProvenanceRenderId({
468
+ frameId: input.frameId,
469
+ routeHash: identity.routeHash,
470
+ });
471
+ const generation = input.generation ?? 1;
472
+ const generatedTemporalIdentity = createPreviewProvenanceTemporalIdentity(input.runtime, {
473
+ sessionId: identity.sessionId,
474
+ frameId: input.frameId,
475
+ routeHash: identity.routeHash,
476
+ codeVersion,
477
+ renderId,
478
+ generation,
479
+ });
480
+ const recorded = await recordPreviewProvenanceEditableLeaves({
481
+ sessionId: identity.sessionId,
482
+ routeHash: identity.routeHash,
483
+ variantId: identity.variantId,
484
+ previewScope: input.previewScope,
485
+ codeVersion,
486
+ renderId,
487
+ generation,
488
+ renderNonce: input.renderNonce ?? generatedTemporalIdentity.renderNonce,
489
+ emissionRevision: input.emissionRevision ?? generatedTemporalIdentity.emissionRevision,
490
+ enabled: input.enabled,
491
+ leaves: capped.leaves,
492
+ codedTextRoot: input.codedTextRoot,
493
+ codedTextRootPrefix: input.codedTextRootPrefix,
494
+ codedTextRootsByDocumentId: input.codedTextRootsByDocumentId,
495
+ });
496
+ const dependencySet = createPreviewFrameDependencySet({
497
+ sessionId: identity.sessionId,
498
+ routeHash: identity.routeHash,
499
+ variantId: identity.variantId,
500
+ codeVersion,
501
+ frameId: input.frameId,
502
+ renderId,
503
+ generation,
504
+ observationId: input.readMetadata?.observationId,
505
+ renderWatermark: input.readMetadata?.renderWatermark,
506
+ dependencies: input.dependencies ?? input.authoritativeDocumentIds.map((documentId) => ({
507
+ kind: 'document',
508
+ documentId,
509
+ reason: 'rendered-document',
510
+ })),
511
+ });
512
+ const validation = validateProvenanceFrame(recorded.renderFrame, {
513
+ documentIds: new Set(input.authoritativeDocumentIds),
514
+ routeHash: identity.routeHash,
515
+ variantId: identity.variantId,
516
+ codeVersion,
517
+ });
518
+ const publicSidecar = recorded.publicSidecar === null
519
+ ? null
520
+ : createPublicSidecar(recorded.renderFrame, dependencySet);
521
+ return {
522
+ frameId: input.frameId,
523
+ sessionId: identity.sessionId,
524
+ routeHash: identity.routeHash,
525
+ variantId: identity.variantId,
526
+ attrsByKey: recorded.attrsByKey,
527
+ codedDocument: attachPreviewProvenanceContext(input.codedDocument, recorded.attrsByKey),
528
+ codedContext: attachPreviewProvenanceContext(input.codedContext, recorded.attrsByKey),
529
+ renderFrame: recorded.renderFrame,
530
+ dependencySet,
531
+ publicSidecar,
532
+ validationOk: validation.ok,
533
+ diagnostics: previewRenderFrameDiagnostics({
534
+ input,
535
+ cap: capped,
536
+ frame: recorded.renderFrame,
537
+ publicSidecar,
538
+ codedTextWrites: recorded.codedTextWrites,
539
+ }),
540
+ };
541
+ }
542
+ async function resolveFixturePreviewIdentity(input) {
543
+ const fixture = input.fixture;
544
+ const explicitVariantId = input.variantId === undefined
545
+ ? undefined
546
+ : normalizePreviewVariantId(input.variantId);
547
+ const fallbackSessionId = fixture?.sessionId ?? input.sessionId ?? '';
548
+ const liveSessionId = await input.runtime.recorder.readSessionId();
549
+ const sessionId = liveSessionId ?? fallbackSessionId;
550
+ const liveRouteIdentity = liveSessionId !== null && input.variantId === undefined
551
+ ? await input.runtime.recorder.resolveRouteIdentity({
552
+ sessionId,
553
+ fallbackPathname: input.fallbackPathname,
554
+ projectId: input.projectId,
555
+ endpoint: input.endpoint,
556
+ convexUrl: input.convexUrl,
557
+ }).catch(() => null)
558
+ : null;
559
+ const fallbackVariantId = explicitVariantId !== undefined
560
+ ? explicitVariantId
561
+ : liveRouteIdentity?.variantId ?? normalizePreviewVariantId(fixture?.variantId);
562
+ if (input.routeHash !== undefined) {
563
+ return {
564
+ sessionId,
565
+ routeHash: input.routeHash,
566
+ variantId: fallbackVariantId,
567
+ };
568
+ }
569
+ if (liveSessionId !== null) {
570
+ const routeIdentity = liveRouteIdentity ??
571
+ await input.runtime.recorder.resolveRouteIdentity({
572
+ sessionId,
573
+ fallbackPathname: input.fallbackPathname,
574
+ projectId: input.projectId,
575
+ endpoint: input.endpoint,
576
+ convexUrl: input.convexUrl,
577
+ }).catch(() => null);
578
+ if (routeIdentity !== null) {
579
+ return {
580
+ sessionId,
581
+ routeHash: routeIdentity.routeHash,
582
+ variantId: fallbackVariantId ?? routeIdentity.variantId,
583
+ };
584
+ }
585
+ }
586
+ return {
587
+ sessionId,
588
+ routeHash: fixture?.routeHash ??
589
+ `route:${input.fallbackPathname || '/'}`,
590
+ variantId: fallbackVariantId,
591
+ };
592
+ }
593
+ function logPreviewProvenanceCodedTextWriteDebug(input) {
594
+ if (process.env.STORYSTICK_PREVIEW_PROVENANCE_DEBUG !== '1')
595
+ return;
596
+ const contextMarkerCounts = {};
597
+ if (isRecord(input.codedContext)) {
598
+ for (const [key, value] of Object.entries(input.codedContext)) {
599
+ const count = countCodedTextMarkers(value);
600
+ if (count > 0)
601
+ contextMarkerCounts[key] = count;
602
+ }
603
+ }
604
+ console.warn('[preview-provenance] coded-text writes', {
605
+ frameId: input.frameId,
606
+ pathname: input.pathname ?? null,
607
+ attempted: input.codedTextWrites.attempted,
608
+ written: input.codedTextWrites.written,
609
+ failures: input.codedTextWrites.failures.length,
610
+ capDropped: input.cap.dropped,
611
+ capApplied: input.cap.capped,
612
+ markersInCodedDocument: countCodedTextMarkers(input.codedDocument),
613
+ markersInCodedContext: countCodedTextMarkers(input.codedContext),
614
+ markersInCodedContextByKey: contextMarkerCounts,
615
+ });
616
+ for (const failure of input.codedTextWrites.failures.slice(0, 48)) {
617
+ console.warn('[preview-provenance] coded-text write failure', failure);
618
+ }
619
+ }
620
+ function countCodedTextMarkers(value) {
621
+ if (value === undefined)
622
+ return 0;
623
+ try {
624
+ const json = JSON.stringify(value);
625
+ if (typeof json !== 'string')
626
+ return 0;
627
+ return json.split(PREVIEW_PROVENANCE_CODED_TEXT_START).length - 1;
628
+ }
629
+ catch {
630
+ return 0;
631
+ }
632
+ }
633
+ function attachPreviewProvenanceContext(value, attrsByKey) {
634
+ if (value === undefined)
635
+ return undefined;
636
+ return withPreviewProvenanceContext(value, attrsByKey);
637
+ }
638
+ function jsonByteLength(value) {
639
+ return new TextEncoder().encode(JSON.stringify(value)).byteLength;
640
+ }
641
+ function sum(values, valueOf) {
642
+ let total = 0;
643
+ for (const value of values)
644
+ total += valueOf(value);
645
+ return total;
646
+ }
647
+ function cloneJson(value) {
648
+ return JSON.parse(JSON.stringify(value));
649
+ }
650
+ function valueAtPath(root, path) {
651
+ let cursor = root;
652
+ for (const part of path) {
653
+ if (typeof part === 'number') {
654
+ if (!Array.isArray(cursor))
655
+ return undefined;
656
+ cursor = cursor[part];
657
+ continue;
658
+ }
659
+ if (!isRecord(cursor))
660
+ return undefined;
661
+ cursor = cursor[part];
662
+ }
663
+ return cursor;
664
+ }
665
+ function isRecord(value) {
666
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
667
+ }
668
+ function storyTypeOf(document) {
669
+ return typeof document.storyType === 'string' && document.storyType.length > 0
670
+ ? document.storyType
671
+ : 'unknown';
672
+ }
673
+ function safeSourceSetPart(value) {
674
+ return value.replace(/[^A-Za-z0-9]+/g, '_').replace(/^_+|_+$/g, '') ||
675
+ 'field';
676
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Deterministic preview render-id derivation.
3
+ *
4
+ * Marker identity (HMAC) incorporates the renderId, so the stability of
5
+ * every provenance marker across refreshes rests on this derivation
6
+ * having ZERO temporal or random components. Pinned by
7
+ * tests/preview-provenance-render-id.test.ts — do not add Date/random
8
+ * inputs here.
9
+ */
10
+ export declare function defaultPreviewProvenanceRenderId(input: {
11
+ readonly frameId: string;
12
+ readonly routeHash: string;
13
+ }): string;
14
+ /** Independent mounts of the same document must not replace each other's frame. */
15
+ export declare function createPreviewOccurrenceId(): string;