@runtypelabs/persona 4.24.0 → 4.26.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 (237) hide show
  1. package/dist/animations/glyph-cycle.d.cts +1 -1
  2. package/dist/animations/glyph-cycle.d.ts +1 -1
  3. package/dist/animations/{types-Db61aI9J.d.cts → types-BwKTZhg9.d.cts} +14 -0
  4. package/dist/animations/{types-Db61aI9J.d.ts → types-BwKTZhg9.d.ts} +14 -0
  5. package/dist/animations/wipe.d.cts +1 -1
  6. package/dist/animations/wipe.d.ts +1 -1
  7. package/dist/animations-extra.d.cts +14 -0
  8. package/dist/animations-extra.d.ts +14 -0
  9. package/dist/approval-ui-QAKWNFNM.js +1 -0
  10. package/dist/approval-ui.cjs +2 -2
  11. package/dist/approval-ui.d.cts +243 -8
  12. package/dist/approval-ui.d.ts +243 -8
  13. package/dist/approval-ui.js +2 -2
  14. package/dist/{artifacts-ui-IGB5YKLO.js → artifacts-ui-NJGMM27I.js} +1 -1
  15. package/dist/artifacts-ui.cjs +4 -4
  16. package/dist/artifacts-ui.d.cts +243 -8
  17. package/dist/artifacts-ui.d.ts +243 -8
  18. package/dist/artifacts-ui.js +5 -5
  19. package/dist/chunk-2ANTRKTV.js +1 -0
  20. package/dist/chunk-4HPTPUC6.js +1 -0
  21. package/dist/chunk-6V3MNEKB.js +1 -0
  22. package/dist/chunk-HGUCGM3K.js +9 -0
  23. package/dist/chunk-IO5VVUKP.js +3 -0
  24. package/dist/chunk-J4J3Y2P2.js +1 -0
  25. package/dist/chunk-K6HKIP5K.js +1 -0
  26. package/dist/chunk-K7NA7G7J.js +1 -0
  27. package/dist/chunk-MXUTS3LB.js +1 -0
  28. package/dist/chunk-PDPVORUF.js +1 -0
  29. package/dist/chunk-QPPJUSZH.js +1 -0
  30. package/dist/chunk-QRKGQPU7.js +1 -0
  31. package/dist/chunk-S6PBWE75.js +1 -0
  32. package/dist/chunk-T7QM7YMF.js +1 -0
  33. package/dist/chunk-TR2H475E.js +1 -0
  34. package/dist/chunk-UPO4GUFC.js +1 -0
  35. package/dist/chunk-ZC66NAIR.js +1 -0
  36. package/dist/client-history-YPEG45Q4.js +1 -0
  37. package/dist/client-history.js +1 -0
  38. package/dist/client-stream-42FOI3EC.js +4 -0
  39. package/dist/client-stream.js +5 -0
  40. package/dist/codegen.cjs +1 -1
  41. package/dist/codegen.js +1 -1
  42. package/dist/context-mentions-E7HJYWO6.js +161 -0
  43. package/dist/{context-mentions-inline-I2RDEBAK.js → context-mentions-inline-QVESLW55.js} +1 -1
  44. package/dist/context-mentions.cjs +7 -7
  45. package/dist/context-mentions.d.cts +243 -8
  46. package/dist/context-mentions.d.ts +243 -8
  47. package/dist/context-mentions.js +8 -8
  48. package/dist/event-stream-view-FOIJHFQO.js +1 -0
  49. package/dist/event-stream-view.cjs +1 -1
  50. package/dist/event-stream-view.d.cts +246 -31
  51. package/dist/event-stream-view.d.ts +246 -31
  52. package/dist/event-stream-view.js +1 -1
  53. package/dist/forms-ui.d.cts +295 -667
  54. package/dist/forms-ui.d.ts +295 -667
  55. package/dist/history-shell-QDQ44PSE.js +3 -0
  56. package/dist/history-shell.js +3 -0
  57. package/dist/{history-view-BD6OXLNA.js → history-view-NCFBI5GR.js} +2 -2
  58. package/dist/history-view.cjs +2 -2
  59. package/dist/history-view.d.cts +35 -1
  60. package/dist/history-view.d.ts +35 -1
  61. package/dist/history-view.js +2 -2
  62. package/dist/index.cjs +80 -74
  63. package/dist/index.cjs.map +1 -1
  64. package/dist/index.d.cts +301 -738
  65. package/dist/index.d.ts +301 -738
  66. package/dist/index.global.js +3 -215
  67. package/dist/index.global.js.map +1 -1
  68. package/dist/index.js +81 -75
  69. package/dist/index.js.map +1 -1
  70. package/dist/install.global.js +1 -1
  71. package/dist/install.global.js.map +1 -1
  72. package/dist/launcher.global.js +3 -67
  73. package/dist/launcher.global.js.map +1 -1
  74. package/dist/markdown-parsers.js +22 -22
  75. package/dist/runtype-tts-entry-NFBVWR7N.js +1 -0
  76. package/dist/runtype-tts.js +1 -1
  77. package/dist/session-actions-ISSKSI66.js +1 -0
  78. package/dist/session-actions.js +1 -0
  79. package/dist/session-reconnect.d.cts +243 -8
  80. package/dist/session-reconnect.d.ts +243 -8
  81. package/dist/smart-dom-reader.cjs +13 -13
  82. package/dist/smart-dom-reader.d.cts +243 -8
  83. package/dist/smart-dom-reader.d.ts +243 -8
  84. package/dist/smart-dom-reader.js +11 -11
  85. package/dist/theme-editor-preview.cjs +116 -98
  86. package/dist/theme-editor-preview.d.cts +245 -8
  87. package/dist/theme-editor-preview.d.ts +245 -8
  88. package/dist/theme-editor-preview.js +49 -54
  89. package/dist/theme-editor.cjs +2 -2
  90. package/dist/theme-editor.d.cts +244 -23
  91. package/dist/theme-editor.d.ts +244 -23
  92. package/dist/theme-editor.js +2 -2
  93. package/dist/theme-reference.d.cts +14 -0
  94. package/dist/theme-reference.d.ts +14 -0
  95. package/dist/ui-extras-entry-WAYFKVNW.js +3 -0
  96. package/dist/ui-extras.js +72 -0
  97. package/dist/voice-runtime-UOQOFAFY.js +17 -0
  98. package/dist/voice-runtime.cjs +17 -1
  99. package/dist/voice-runtime.d.cts +11643 -192
  100. package/dist/voice-runtime.d.ts +11643 -192
  101. package/dist/voice-runtime.js +17 -1
  102. package/dist/voice-worklet-player.cjs +19 -4
  103. package/dist/voice-worklet-player.d.cts +9 -10
  104. package/dist/voice-worklet-player.d.ts +9 -10
  105. package/dist/voice-worklet-player.js +19 -4
  106. package/dist/webmcp-polyfill.js +1 -1
  107. package/dist/webmcp-runtime-V6ZWW3PI.js +6 -0
  108. package/dist/webmcp-runtime.cjs +2 -2
  109. package/dist/webmcp-runtime.d.cts +1 -33
  110. package/dist/webmcp-runtime.d.ts +1 -33
  111. package/dist/webmcp-runtime.js +2 -2
  112. package/dist/widget.css +1 -1
  113. package/package.json +11 -5
  114. package/src/approval-paused-tool-settle.test.ts +221 -0
  115. package/src/client-approve.test.ts +311 -0
  116. package/src/client-history-eager.ts +9 -0
  117. package/src/client-history-loader.ts +23 -0
  118. package/src/client-history.ts +579 -0
  119. package/src/client-resume.ts +177 -0
  120. package/src/client-stream-eager.ts +12 -0
  121. package/src/client-stream-loader.ts +25 -0
  122. package/src/client-stream.ts +1880 -0
  123. package/src/client.test.ts +31 -1
  124. package/src/client.ts +293 -2607
  125. package/src/components/approval-actions.test.ts +27 -0
  126. package/src/components/approval-actions.ts +27 -3
  127. package/src/components/message-bubble.test.ts +19 -0
  128. package/src/components/message-bubble.ts +2 -0
  129. package/src/components/registry.ts +6 -6
  130. package/src/components/tool-bubble.test.ts +9 -0
  131. package/src/components/tool-bubble.ts +1 -0
  132. package/src/history-shell-bundle.test.ts +54 -0
  133. package/src/history-shell-eager.ts +12 -0
  134. package/src/history-shell-loader.ts +27 -0
  135. package/src/history-shell.ts +2362 -0
  136. package/src/history-view-loader.ts +2 -0
  137. package/src/history-view.ts +3 -0
  138. package/src/index-core.ts +8 -0
  139. package/src/index-global.ts +90 -248
  140. package/src/index.ts +13 -0
  141. package/src/install.ts +6 -4
  142. package/src/internal/runtype-history-provider.test.ts +1 -0
  143. package/src/internal/runtype-history-provider.ts +28 -13
  144. package/src/plugins/registry.ts +8 -8
  145. package/src/session-actions-chunk.test.ts +55 -0
  146. package/src/session-actions-eager.ts +11 -0
  147. package/src/session-actions-loader.ts +37 -0
  148. package/src/session-actions.ts +909 -0
  149. package/src/session-history.test.ts +3 -1
  150. package/src/session.ts +875 -1530
  151. package/src/session.voice.test.ts +1241 -5
  152. package/src/styles/widget.css +58 -0
  153. package/src/theme-editor/state.ts +91 -91
  154. package/src/types.ts +269 -5
  155. package/src/ui-ask-user-sheet.ts +382 -0
  156. package/src/ui-extras-eager.ts +10 -0
  157. package/src/ui-extras-entry.ts +9 -0
  158. package/src/ui-extras-loader.ts +26 -0
  159. package/src/ui.artifact-rail-resize.test.ts +2 -1
  160. package/src/ui.ask-user-chunk.test.ts +93 -0
  161. package/src/ui.history-chunk.test.ts +2 -1
  162. package/src/ui.history-config-provider.test.ts +2 -1
  163. package/src/ui.history-confirm-fallback.test.ts +142 -0
  164. package/src/ui.history-controller.test.ts +2 -1
  165. package/src/ui.history-lifecycle.test.ts +2 -1
  166. package/src/ui.history-render-hooks.test.ts +2 -1
  167. package/src/ui.history-shell.test.ts +2 -1
  168. package/src/ui.rail-overlay.test.ts +2 -1
  169. package/src/ui.rail-resize.test.ts +2 -1
  170. package/src/ui.rail-trigger-pending.test.ts +2 -1
  171. package/src/ui.ts +399 -2770
  172. package/src/ui.voice-prewarm.test.ts +222 -0
  173. package/src/utils/attachment-manager.ts +67 -67
  174. package/src/utils/constants.ts +3 -0
  175. package/src/utils/context-mention-controller.ts +323 -323
  176. package/src/utils/context-mention-manager.ts +40 -40
  177. package/src/utils/context-mention-orchestrator.ts +28 -0
  178. package/src/utils/event-stream-buffer.ts +50 -50
  179. package/src/utils/event-stream-store.ts +43 -43
  180. package/src/utils/throughput-tracker.ts +34 -34
  181. package/src/utils/virtual-scroller.ts +67 -67
  182. package/src/utils/visitor-store.test.ts +50 -0
  183. package/src/utils/visitor-store.ts +23 -5
  184. package/src/voice/__fixtures__/voice-wire/README.md +1 -0
  185. package/src/voice/__fixtures__/voice-wire/client/cancel.json +3 -0
  186. package/src/voice/__fixtures__/voice-wire/client/context.json +4 -0
  187. package/src/voice/__fixtures__/voice-wire/client/delegation_result.json +6 -0
  188. package/src/voice/__fixtures__/voice-wire/client/delegation_update.json +6 -0
  189. package/src/voice/__fixtures__/voice-wire/client/ping.json +3 -0
  190. package/src/voice/__fixtures__/voice-wire/client/playback_progress.json +5 -0
  191. package/src/voice/__fixtures__/voice-wire/client/start.json +3 -0
  192. package/src/voice/__fixtures__/voice-wire/server/artifact.json +11 -0
  193. package/src/voice/__fixtures__/voice-wire/server/attached.json +4 -0
  194. package/src/voice/__fixtures__/voice-wire/server/audio_clear.json +4 -0
  195. package/src/voice/__fixtures__/voice-wire/server/audio_end.json +3 -0
  196. package/src/voice/__fixtures__/voice-wire/server/delegation_cancelled.json +5 -0
  197. package/src/voice/__fixtures__/voice-wire/server/delegation_completed.json +8 -0
  198. package/src/voice/__fixtures__/voice-wire/server/delegation_started.json +15 -0
  199. package/src/voice/__fixtures__/voice-wire/server/error.json +6 -0
  200. package/src/voice/__fixtures__/voice-wire/server/metrics.json +10 -0
  201. package/src/voice/__fixtures__/voice-wire/server/pong.json +3 -0
  202. package/src/voice/__fixtures__/voice-wire/server/session_config.json +16 -0
  203. package/src/voice/__fixtures__/voice-wire/server/transcript_final.json +7 -0
  204. package/src/voice/__fixtures__/voice-wire/server/transcript_interim.json +4 -0
  205. package/src/voice/__fixtures__/voice-wire/server/transcript_partial.json +6 -0
  206. package/src/voice/__fixtures__/voice-wire/server/transcript_update.json +10 -0
  207. package/src/voice/__fixtures__/voice-wire/server/warning.json +5 -0
  208. package/src/voice/audio-playback-manager.test.ts +57 -0
  209. package/src/voice/audio-playback-manager.ts +120 -98
  210. package/src/voice/browser-voice-provider.ts +37 -37
  211. package/src/voice/fallback-speech-engine.ts +22 -16
  212. package/src/voice/keyed-voice-transcript.ts +314 -0
  213. package/src/voice/mic-state-styles.ts +182 -0
  214. package/src/voice/read-aloud-controller.ts +46 -44
  215. package/src/voice/runtype-speech-engine.ts +35 -33
  216. package/src/voice/runtype-voice-provider.ts +856 -164
  217. package/src/voice/session-voice-wiring.ts +266 -0
  218. package/src/voice/voice-delegation.test.ts +71 -0
  219. package/src/voice/voice-delegation.ts +364 -0
  220. package/src/voice/voice-turn-tracker.ts +6 -6
  221. package/src/voice/voice.test.ts +1250 -7
  222. package/src/voice/worklet-playback-engine.test.ts +81 -0
  223. package/src/voice/worklet-playback-engine.ts +21 -3
  224. package/src/voice-runtime-loader.ts +4 -1
  225. package/src/voice-runtime.ts +7 -0
  226. package/src/webmcp-runtime-entry.ts +41 -39
  227. package/dist/approval-ui-3NMYNXBD.js +0 -1
  228. package/dist/chunk-5EIIHQLQ.js +0 -1
  229. package/dist/chunk-7CO4T5KB.js +0 -1
  230. package/dist/chunk-EFJBUH5F.js +0 -1
  231. package/dist/chunk-R2TMIMDI.js +0 -3
  232. package/dist/chunk-UTVLOTWB.js +0 -9
  233. package/dist/context-mentions-45MODWOY.js +0 -161
  234. package/dist/event-stream-view-GL3ADRY2.js +0 -1
  235. package/dist/runtype-tts-entry-UJAEF7NZ.js +0 -1
  236. package/dist/voice-runtime-EFR3R5ES.js +0 -1
  237. package/dist/webmcp-runtime-JI7T4QYF.js +0 -6
@@ -122,6 +122,33 @@ describe("built-in approval — flag off (default)", () => {
122
122
  });
123
123
  });
124
124
 
125
+ describe("built-in approval — client-token visitor", () => {
126
+ const cfg: AgentWidgetConfig = {
127
+ clientToken: "ct_live_demo",
128
+ approval: { detailsDisplay: "hidden" },
129
+ };
130
+ const message = makeMessage({
131
+ toolName: "place_pickup_order",
132
+ parameters: { item: "croissant", qty: 12 },
133
+ reason: "The user asked for this.",
134
+ });
135
+
136
+ it("keeps the raw call behind the disclosure without a client token", () => {
137
+ const { el } = render({}, message);
138
+ expect(el?.querySelector('[data-role="raw-call"]')).toBeNull();
139
+ });
140
+
141
+ it("shows the raw tool name and parameters above the agent's reason", () => {
142
+ const { el } = render(cfg, message);
143
+ const call = el?.querySelector('[data-role="raw-call"]') as HTMLElement | null;
144
+ expect(call?.querySelector(".persona-approval-tool-name")?.textContent).toBe("place_pickup_order");
145
+ expect(call?.querySelector(".persona-approval-params")?.textContent).toContain("croissant");
146
+ const reason = el?.querySelector(".persona-approval-reason");
147
+ expect(reason?.textContent).toContain("Agent's stated reason:");
148
+ expect(call!.compareDocumentPosition(reason!) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
149
+ });
150
+ });
151
+
125
152
  describe("built-in approval — flag on (enableAlwaysAllow)", () => {
126
153
  const cfg: AgentWidgetConfig = { approval: { enableAlwaysAllow: true } };
127
154
 
@@ -180,7 +180,8 @@ const buildPending = (
180
180
  approvalConfig: AgentWidgetApprovalConfig | undefined,
181
181
  approve: Decide,
182
182
  deny: Decide,
183
- enableAlways: boolean
183
+ enableAlways: boolean,
184
+ showRawCall = false
184
185
  ): HTMLElement => {
185
186
  const card = createElement("div", "persona-approval-card persona-shadow-sm");
186
187
  card.id = `bubble-${message.id}`;
@@ -199,7 +200,8 @@ const buildPending = (
199
200
  // name), so the description is only ever visible here. Mirrors the legacy
200
201
  // bubble, which also opened a disclosure when only a description was present.
201
202
  const hasDescription = Boolean(approval.description) && detailsMode !== "hidden";
202
- const hasParams = approval.parameters != null && detailsMode !== "hidden";
203
+ // `showRawCall` lifts the parameters out of the disclosure (see below).
204
+ const hasParams = approval.parameters != null && detailsMode !== "hidden" && !showRawCall;
203
205
  const hasDetails = hasDescription || hasParams;
204
206
  const expanded = hasDetails && isDetailsExpanded(message.id, expansionState, approvalConfig);
205
207
 
@@ -263,6 +265,24 @@ const buildPending = (
263
265
  body.appendChild(details);
264
266
  }
265
267
 
268
+ // The raw tool name and arguments, always visible and above the reason, so
269
+ // what will actually run is never less prominent than the agent's claim.
270
+ if (showRawCall) {
271
+ const call = createElement("div", "persona-approval-call");
272
+ call.setAttribute("data-role", "raw-call");
273
+ const name = createElement("code", "persona-approval-tool-name");
274
+ name.textContent = approval.toolName;
275
+ call.appendChild(name);
276
+ if (approval.parameters != null) {
277
+ const pre = createElement("pre", "persona-approval-params");
278
+ if (approvalConfig?.parameterBackgroundColor) pre.style.background = approvalConfig.parameterBackgroundColor;
279
+ if (approvalConfig?.parameterTextColor) pre.style.color = approvalConfig.parameterTextColor;
280
+ pre.textContent = formatUnknownValue(approval.parameters);
281
+ call.appendChild(pre);
282
+ }
283
+ body.appendChild(call);
284
+ }
285
+
266
286
  // Agent-authored justification: attacker-writable, so plain text + attributed.
267
287
  if (approval.reason) {
268
288
  const reasonLine = createElement("p", "persona-approval-reason");
@@ -422,6 +442,9 @@ export const createBuiltInApprovalPlugin = (
422
442
  // closures, but KEEP this approval's place in the pending order so a
423
443
  // re-render of an older card doesn't reorder it ahead of a newer one.
424
444
  detachMessage(state, message.id);
445
+ // Client-token visitors answer the gate themselves, so they see the
446
+ // exact call rather than just the agent's framing.
447
+ const visitorGate = Boolean(config?.clientToken);
425
448
  const enableAlways = approvalConfig?.enableAlwaysAllow === true;
426
449
  const card = buildPending(
427
450
  state,
@@ -431,7 +454,8 @@ export const createBuiltInApprovalPlugin = (
431
454
  approvalConfig,
432
455
  approve,
433
456
  deny,
434
- enableAlways
457
+ enableAlways,
458
+ visitorGate
435
459
  );
436
460
 
437
461
  if (enableAlways) {
@@ -333,6 +333,25 @@ describe("createStandardBubble", () => {
333
333
  });
334
334
  });
335
335
 
336
+ describe("createStandardBubble: message actions", () => {
337
+ const actions = { enabled: true, showCopy: true, visibility: "hover", align: "right", layout: "pill-inside" } as const;
338
+
339
+ it("gives a voice caption no message actions", () => {
340
+ const bubble = createStandardBubble(
341
+ makeMessage({ id: "caption", content: "Okay, placing that now.", voiceCaption: true }),
342
+ ({ text }) => text,
343
+ undefined,
344
+ actions
345
+ );
346
+ expect(bubble.querySelector(".persona-message-actions")).toBeNull();
347
+ });
348
+
349
+ it("keeps the copy action on an ordinary short reply", () => {
350
+ const bubble = createStandardBubble(makeMessage({ content: "Sure." }), ({ text }) => text, undefined, actions);
351
+ expect(bubble.querySelector('[data-action="copy"]')).not.toBeNull();
352
+ });
353
+ });
354
+
336
355
  describe("createStandardBubble: components.message geometry tokens", () => {
337
356
  const geometry = (bubble: HTMLElement) => ({
338
357
  padding: bubble.style.padding,
@@ -1298,6 +1298,8 @@ export const createStandardBubble = (
1298
1298
  const shouldShowActions =
1299
1299
  message.role === "assistant" &&
1300
1300
  !message.streaming &&
1301
+ // A voice caption (the voice model's own speech) is display-only.
1302
+ !message.voiceCaption &&
1301
1303
  message.content &&
1302
1304
  message.content.trim() &&
1303
1305
  actionsConfig?.enabled !== false;
@@ -37,7 +37,7 @@ export interface ComponentRegistrationOptions {
37
37
  */
38
38
  class ComponentRegistry {
39
39
  private components: Map<string, ComponentRenderer> = new Map();
40
- private options: Map<string, ComponentRegistrationOptions> = new Map();
40
+ #options: Map<string, ComponentRegistrationOptions> = new Map();
41
41
 
42
42
  /**
43
43
  * Register a custom component
@@ -52,9 +52,9 @@ class ComponentRegistry {
52
52
  }
53
53
  this.components.set(name, renderer);
54
54
  if (options) {
55
- this.options.set(name, options);
55
+ this.#options.set(name, options);
56
56
  } else {
57
- this.options.delete(name);
57
+ this.#options.delete(name);
58
58
  }
59
59
  }
60
60
 
@@ -63,7 +63,7 @@ class ComponentRegistry {
63
63
  */
64
64
  unregister(name: string): void {
65
65
  this.components.delete(name);
66
- this.options.delete(name);
66
+ this.#options.delete(name);
67
67
  }
68
68
 
69
69
  /**
@@ -84,7 +84,7 @@ class ComponentRegistry {
84
84
  * Get the registration options for a component, if any were supplied
85
85
  */
86
86
  getOptions(name: string): ComponentRegistrationOptions | undefined {
87
- return this.options.get(name);
87
+ return this.#options.get(name);
88
88
  }
89
89
 
90
90
  /**
@@ -99,7 +99,7 @@ class ComponentRegistry {
99
99
  */
100
100
  clear(): void {
101
101
  this.components.clear();
102
- this.options.clear();
102
+ this.#options.clear();
103
103
  }
104
104
 
105
105
  /**
@@ -49,3 +49,12 @@ describe("tool failure presentation", () => {
49
49
  expect(bubble.querySelector("script")).toBeNull();
50
50
  });
51
51
  });
52
+
53
+ describe("approval-superseded presentation", () => {
54
+ it("labels an approved call that ran again under a new id", () => {
55
+ const message = makeMessage();
56
+ message.toolCall = { ...message.toolCall!, approvalStatus: "approved", superseded: true };
57
+ const bubble = createToolBubble(message);
58
+ expect(bubble.textContent).toContain("Approved, ran again below");
59
+ });
60
+ });
@@ -101,6 +101,7 @@ const getToolSummaryText = (
101
101
  }
102
102
 
103
103
  if (tool.success === false) summary += " · Failed";
104
+ else if (tool.superseded) summary += " · Approved, ran again below";
104
105
  return { summary, previewText, isActive };
105
106
  };
106
107
 
@@ -0,0 +1,54 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { resolve } from "node:path";
4
+
5
+ /**
6
+ * Bundle guard for the lazy visitor-history shell.
7
+ *
8
+ * The CDN IIFE (`dist/index.global.js`) must not carry the shell: it ships as
9
+ * the sibling chunk `dist/history-shell.js`, fetched only when
10
+ * `features.history` is enabled or a history API is called. npm builds inline
11
+ * it (`history-shell-eager.ts`). A stray static import from core would pull
12
+ * it back into the IIFE; this fails then.
13
+ *
14
+ * Skips when `dist/` hasn't been built (e.g. a test-only CI step).
15
+ */
16
+ const dist = (f: string) => resolve(__dirname, "..", "dist", f);
17
+
18
+ // Shell-only literals: rail/overlay chrome and conversation-open classes.
19
+ const RUNTIME_MARKERS = [
20
+ "persona-history-rail-shell",
21
+ "persona-history-rail-overlay",
22
+ "persona-conversation-loading-error",
23
+ "data-persona-history-toggle",
24
+ ];
25
+
26
+ // Stateful core modules arrive through the shell's ctx, never bundled copies:
27
+ // the icon registry's map keys and the tooltip module only exist where bundled.
28
+ const NOT_IN_CHUNK = ["shopping-cart", "persona-tooltip"];
29
+
30
+ describe("history-shell bundle split", () => {
31
+ const iifeBuilt =
32
+ existsSync(dist("index.global.js")) && existsSync(dist("history-shell.js"));
33
+
34
+ it.runIf(iifeBuilt)("keeps the history shell OUT of the core IIFE bundle", () => {
35
+ const core = readFileSync(dist("index.global.js"), "utf8");
36
+ for (const marker of RUNTIME_MARKERS) {
37
+ expect(core.includes(marker), `IIFE bundle unexpectedly contains "${marker}"`).toBe(
38
+ false
39
+ );
40
+ }
41
+ // The loader stub (sibling-URL reference) must remain so the chunk can load.
42
+ expect(core).toContain("history-shell.js");
43
+ });
44
+
45
+ it.runIf(iifeBuilt)("ships the history shell in the sibling chunk", () => {
46
+ const chunk = readFileSync(dist("history-shell.js"), "utf8");
47
+ for (const marker of RUNTIME_MARKERS) {
48
+ expect(chunk.includes(marker), `chunk is missing "${marker}"`).toBe(true);
49
+ }
50
+ for (const marker of NOT_IN_CHUNK) {
51
+ expect(chunk.includes(marker), `chunk unexpectedly bundles "${marker}"`).toBe(false);
52
+ }
53
+ });
54
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Eager history-shell registration for the bundled (ESM / CJS) builds.
3
+ *
4
+ * npm consumers bundle `history-shell.ts` anyway, so providing it up front
5
+ * keeps history setup synchronous at mount. Like `client-stream-eager.ts`,
6
+ * this module must NOT be reachable from `index-global.ts`: the IIFE/CDN build
7
+ * fetches the `history-shell.js` chunk only when history is used.
8
+ */
9
+ import * as historyShell from "./history-shell";
10
+ import { provideHistoryShell } from "./history-shell-loader";
11
+
12
+ provideHistoryShell(historyShell);
@@ -0,0 +1,27 @@
1
+ import { createChunkLoader } from "./utils/chunk-loader";
2
+ import type { createHistoryShell } from "./history-shell";
3
+
4
+ /**
5
+ * Loader indirection for the visitor-history shell (`history-shell.ts`). The
6
+ * ESM/CJS fallback imports `./history-shell` directly (inlined by
7
+ * `--splitting false`, and provided eagerly by `history-shell-eager.ts`); the
8
+ * IIFE/CDN build marks it external and registers a loader for the sibling
9
+ * `history-shell.js` chunk (see `index-global.ts`).
10
+ */
11
+ export type HistoryShellModule = { createHistoryShell: typeof createHistoryShell };
12
+
13
+ const { setLoader, load, provide, getSync } = createChunkLoader<HistoryShellModule>({
14
+ fallbackImport: () => import("./history-shell"),
15
+ });
16
+
17
+ /** Override how the chunk is fetched (the IIFE build registers a sibling-URL loader). */
18
+ export const setHistoryShellLoader = setLoader;
19
+
20
+ /** Load the history shell. Memoized; retries after rejection. */
21
+ export const loadHistoryShell = load;
22
+
23
+ /** Eagerly supply the module (`history-shell-eager.ts`: npm builds and tests). */
24
+ export const provideHistoryShell = provide;
25
+
26
+ /** Synchronous access once loaded/provided; null before that. */
27
+ export const getHistoryShellSync = getSync;