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