@syntrologie/adapt-chatbot 2.26.0 → 2.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/AdaptiveChatBar.d.ts +76 -0
- package/dist/AdaptiveChatBar.d.ts.map +1 -0
- package/dist/AdaptiveChatBar.js +10 -0
- package/dist/AdaptiveChatBar.js.map +7 -0
- package/dist/AdaptiveChatBarMountable.d.ts +35 -0
- package/dist/AdaptiveChatBarMountable.d.ts.map +1 -0
- package/dist/AdaptiveChatTrail.d.ts +77 -0
- package/dist/AdaptiveChatTrail.d.ts.map +1 -0
- package/dist/AdaptiveChatTrail.js +9 -0
- package/dist/AdaptiveChatTrail.js.map +7 -0
- package/dist/AdaptiveChipsStrip.d.ts +1150 -0
- package/dist/AdaptiveChipsStrip.d.ts.map +1 -0
- package/dist/AdaptiveChipsStrip.js +11 -0
- package/dist/AdaptiveChipsStrip.js.map +7 -0
- package/dist/AdaptiveChipsStripMountable.d.ts +24 -0
- package/dist/AdaptiveChipsStripMountable.d.ts.map +1 -0
- package/dist/ChatAssistantLit.d.ts +23 -21
- package/dist/ChatAssistantLit.d.ts.map +1 -1
- package/dist/ChatAssistantLit.js +5 -3
- package/dist/ChatSession.d.ts +178 -0
- package/dist/ChatSession.d.ts.map +1 -0
- package/dist/ChatTransport.d.ts +283 -0
- package/dist/ChatTransport.d.ts.map +1 -0
- package/dist/NavLinkMountable.d.ts +25 -0
- package/dist/NavLinkMountable.d.ts.map +1 -0
- package/dist/NavLinkMountable.test.d.ts +2 -0
- package/dist/NavLinkMountable.test.d.ts.map +1 -0
- package/dist/TextAnswerMountable.d.ts +17 -0
- package/dist/TextAnswerMountable.d.ts.map +1 -0
- package/dist/Turnstile.d.ts +83 -0
- package/dist/Turnstile.d.ts.map +1 -0
- package/dist/chunk-435KJD27.js +192 -0
- package/dist/chunk-435KJD27.js.map +7 -0
- package/dist/chunk-AUER7ZCK.js +634 -0
- package/dist/chunk-AUER7ZCK.js.map +7 -0
- package/dist/chunk-DOMEUJR7.js +382 -0
- package/dist/chunk-DOMEUJR7.js.map +7 -0
- package/dist/{chunk-O7RWNUVU.js → chunk-KUO67E2W.js} +1573 -4079
- package/dist/chunk-KUO67E2W.js.map +7 -0
- package/dist/chunk-QELVKBQV.js +214 -0
- package/dist/chunk-QELVKBQV.js.map +7 -0
- package/dist/chunk-UC4XU6GH.js +3306 -0
- package/dist/chunk-UC4XU6GH.js.map +7 -0
- package/dist/elements/ActionHandler.d.ts +34 -0
- package/dist/elements/ActionHandler.d.ts.map +1 -0
- package/dist/elements/ElementInstanceStore.d.ts +155 -0
- package/dist/elements/ElementInstanceStore.d.ts.map +1 -0
- package/dist/elements/ElementInstanceStore.test.d.ts +2 -0
- package/dist/elements/ElementInstanceStore.test.d.ts.map +1 -0
- package/dist/elements/ElementTypeHandler.d.ts +77 -0
- package/dist/elements/ElementTypeHandler.d.ts.map +1 -0
- package/dist/elements/ItemHandler.d.ts +60 -0
- package/dist/elements/ItemHandler.d.ts.map +1 -0
- package/dist/elements/ItemHandler.test.d.ts +2 -0
- package/dist/elements/ItemHandler.test.d.ts.map +1 -0
- package/dist/elements/TileHandler.d.ts +52 -0
- package/dist/elements/TileHandler.d.ts.map +1 -0
- package/dist/elements/blockRenderer.d.ts +46 -0
- package/dist/elements/blockRenderer.d.ts.map +1 -0
- package/dist/elements/blockRenderer.test.d.ts +13 -0
- package/dist/elements/blockRenderer.test.d.ts.map +1 -0
- package/dist/elements/blocks.d.ts +58 -0
- package/dist/elements/blocks.d.ts.map +1 -0
- package/dist/elements/envelope.d.ts +24 -0
- package/dist/elements/envelope.d.ts.map +1 -0
- package/dist/elements/fetcher.d.ts +40 -0
- package/dist/elements/fetcher.d.ts.map +1 -0
- package/dist/elements/index.d.ts +32 -0
- package/dist/elements/index.d.ts.map +1 -0
- package/dist/elements/types.d.ts +106 -0
- package/dist/elements/types.d.ts.map +1 -0
- package/dist/observer/__tests__/allowlist.test.d.ts +9 -0
- package/dist/observer/__tests__/allowlist.test.d.ts.map +1 -0
- package/dist/observer/__tests__/observer-isolation.test.d.ts +13 -0
- package/dist/observer/__tests__/observer-isolation.test.d.ts.map +1 -0
- package/dist/observer/__tests__/queue.test.d.ts +2 -0
- package/dist/observer/__tests__/queue.test.d.ts.map +1 -0
- package/dist/observer/__tests__/transport.test.d.ts +2 -0
- package/dist/observer/__tests__/transport.test.d.ts.map +1 -0
- package/dist/observer/allowlist.d.ts +32 -0
- package/dist/observer/allowlist.d.ts.map +1 -0
- package/dist/observer/index.d.ts +35 -0
- package/dist/observer/index.d.ts.map +1 -0
- package/dist/observer/queue.d.ts +57 -0
- package/dist/observer/queue.d.ts.map +1 -0
- package/dist/observer/transport.d.ts +26 -0
- package/dist/observer/transport.d.ts.map +1 -0
- package/dist/runtime.d.ts +7 -0
- package/dist/runtime.d.ts.map +1 -1
- package/dist/runtime.js +1617 -2
- package/dist/runtime.js.map +4 -4
- package/dist/schema.d.ts +3120 -7
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +40 -0
- package/dist/schema.js.map +2 -2
- package/dist/types.d.ts +30 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +13 -1
- package/dist/chunk-O7RWNUVU.js.map +0 -7
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Action element handler — dispatches an ActionStep through
|
|
3
|
+
* `ActionEngine.applyBatch`.
|
|
4
|
+
*
|
|
5
|
+
* The `content` field of an action `MountedElement` carries the action's
|
|
6
|
+
* params; the executor `kind` comes from the template (which the SDK
|
|
7
|
+
* doesn't have locally yet — for v1 the template's `kind` is encoded
|
|
8
|
+
* into `content.__kind`, set by the backend tool). When proper
|
|
9
|
+
* template loading lands on the SDK side, this handler resolves the
|
|
10
|
+
* kind from the loaded template instead.
|
|
11
|
+
*
|
|
12
|
+
* **Imperative actions never re-fire on boot.** The `lifecycle` field
|
|
13
|
+
* here is `imperative` — `ElementInstanceStore` skips boot rehydrate
|
|
14
|
+
* for imperative instances with `state === 'applied'`. The backend
|
|
15
|
+
* record exists for audit; replaying a scroll/navigate on every page
|
|
16
|
+
* load would be visibly wrong.
|
|
17
|
+
*
|
|
18
|
+
* Stateful actions (e.g. `content:insertHtml`, `content:setText`,
|
|
19
|
+
* `content:addClass` once Phase 7's renderer lands) ARE re-applied on
|
|
20
|
+
* boot. v1 supports the imperative shape; stateful action handling
|
|
21
|
+
* waits on the Portable Text renderer.
|
|
22
|
+
*/
|
|
23
|
+
import type { ElementHandlerContext, ElementTypeHandler, ValidateResult } from './ElementTypeHandler';
|
|
24
|
+
import type { MountedElement } from './types';
|
|
25
|
+
export declare class ActionHandler implements ElementTypeHandler {
|
|
26
|
+
readonly typeName: "action";
|
|
27
|
+
readonly lifecycle: "imperative";
|
|
28
|
+
validate(instance: MountedElement, _ctx: ElementHandlerContext): ValidateResult;
|
|
29
|
+
mount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void>;
|
|
30
|
+
patch(instance: MountedElement, ctx: ElementHandlerContext): Promise<void>;
|
|
31
|
+
unmount(_instance: MountedElement, _ctx: ElementHandlerContext): Promise<void>;
|
|
32
|
+
private _actionFromInstance;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=ActionHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ActionHandler.d.ts","sourceRoot":"","sources":["../../src/elements/ActionHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAGH,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,qBAAa,aAAc,YAAW,kBAAkB;IACtD,QAAQ,CAAC,QAAQ,EAAG,QAAQ,CAAU;IAGtC,QAAQ,CAAC,SAAS,EAAG,YAAY,CAAU;IAE3C,QAAQ,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,qBAAqB,GAAG,cAAc;IAWzE,KAAK,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAK1E,KAAK,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAQ1E,OAAO,CAAC,SAAS,EAAE,cAAc,EAAE,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMpF,OAAO,CAAC,mBAAmB;CAiC5B"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Type-neutral store for LLM-authored UI element instances — Phase 6.
|
|
3
|
+
*
|
|
4
|
+
* On SDK boot:
|
|
5
|
+
* 1. Caller fetches `/api/adaptive/mounted_elements` (see `fetcher.ts`).
|
|
6
|
+
* 2. Calls `store.hydrate(response.mounted_elements)`.
|
|
7
|
+
* 3. The store iterates each instance, looks up its handler, and
|
|
8
|
+
* either mounts (stateful) or skips (imperative already-applied).
|
|
9
|
+
*
|
|
10
|
+
* During chat turns:
|
|
11
|
+
* - AGUI `syntro.element.mutation` envelopes arrive via
|
|
12
|
+
* `AgUiTransport.onA2UIEvent`. The transport caller decodes the
|
|
13
|
+
* envelope and invokes `store.apply(envelope.mutations)`.
|
|
14
|
+
*
|
|
15
|
+
* Concurrency / dedup:
|
|
16
|
+
* - Per-instance `version` is the dedup key. A mutation whose version
|
|
17
|
+
* is `<= currentVersion` is treated as already-applied and silently
|
|
18
|
+
* dropped — handles the SSE-during-boot-fetch race per the design
|
|
19
|
+
* doc (the live mutation might arrive before the boot snapshot does,
|
|
20
|
+
* and the snapshot is then a no-op).
|
|
21
|
+
* - The store is single-threaded by Node/browser convention; no
|
|
22
|
+
* cross-tab sync in v1.
|
|
23
|
+
*
|
|
24
|
+
* The store delegates all DOM work to per-type handlers
|
|
25
|
+
* (`ElementTypeHandler`). It owns the registry, the in-memory state,
|
|
26
|
+
* and the dispatch logic.
|
|
27
|
+
*/
|
|
28
|
+
import type { ActionEngine } from '../types';
|
|
29
|
+
import type { ElementTypeHandler } from './ElementTypeHandler';
|
|
30
|
+
import type { ElementMutation, MountedElement } from './types';
|
|
31
|
+
export interface ElementInstanceStoreOptions {
|
|
32
|
+
actions: ActionEngine;
|
|
33
|
+
/** Runtime's event bus. ItemHandler publishes
|
|
34
|
+
* `element.compositional_append` events here so container widgets
|
|
35
|
+
* (chips strip, FAQ accordion, nav tips) can subscribe and inject
|
|
36
|
+
* LLM-authored items into their compositional-actions lists.
|
|
37
|
+
*
|
|
38
|
+
* When `subscribe` is also provided, the store listens for
|
|
39
|
+
* `element.compositional_replay_request` events. A container widget
|
|
40
|
+
* publishes this event with its `tile_id` whenever it subscribes
|
|
41
|
+
* (e.g. on first mount AND on SPA-navigation remount); the store
|
|
42
|
+
* responds by re-publishing `element.compositional_append` for every
|
|
43
|
+
* matching item still in its in-memory map. This is what makes
|
|
44
|
+
* LLM-authored items persist across navigations within the page
|
|
45
|
+
* session — the store survives chat-bar unmount/remount, container
|
|
46
|
+
* widgets re-pull on each fresh mount.
|
|
47
|
+
*/
|
|
48
|
+
events?: {
|
|
49
|
+
publish: (name: string, props?: Record<string, unknown>) => void;
|
|
50
|
+
/**
|
|
51
|
+
* Overloaded to match the runtime SDK's `EventBus.subscribe`:
|
|
52
|
+
* pass `{names:[...]}` to filter, or a callback to receive every
|
|
53
|
+
* event. The store uses the filtered form so it doesn't fan
|
|
54
|
+
* unrelated traffic (PostHog autocapture, every action lifecycle
|
|
55
|
+
* event) into a string-compare on each delivery.
|
|
56
|
+
*/
|
|
57
|
+
subscribe?: {
|
|
58
|
+
(filter: {
|
|
59
|
+
names?: string[];
|
|
60
|
+
}, handler: (event: {
|
|
61
|
+
name: string;
|
|
62
|
+
props?: Record<string, unknown>;
|
|
63
|
+
}) => void): () => void;
|
|
64
|
+
(handler: (event: {
|
|
65
|
+
name: string;
|
|
66
|
+
props?: Record<string, unknown>;
|
|
67
|
+
}) => void): () => void;
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
handlers?: ElementTypeHandler[];
|
|
71
|
+
/** Override the logger — used by tests to capture log output. */
|
|
72
|
+
logger?: Pick<Console, 'info' | 'warn' | 'error'>;
|
|
73
|
+
/**
|
|
74
|
+
* Optional template→widget resolver used by the rehydrate replay.
|
|
75
|
+
* Without this, replayed tile mounts emit ``widget: template_id``,
|
|
76
|
+
* which only works when the admin's template ids match the widget
|
|
77
|
+
* registry (e.g. ``adaptive-faq:accordion``). When an admin uses a
|
|
78
|
+
* friendly id like ``product-card`` mapping to widget
|
|
79
|
+
* ``adaptive-product:card``, the rehydrate path needs the resolver
|
|
80
|
+
* to match the live mount path.
|
|
81
|
+
*
|
|
82
|
+
* Same shape as ``TileHandler``'s resolver — pass the same function
|
|
83
|
+
* to both so live mounts and replays agree on the widget id.
|
|
84
|
+
*/
|
|
85
|
+
resolveTileWidget?: (templateId: string) => string | undefined;
|
|
86
|
+
}
|
|
87
|
+
export declare class ElementInstanceStore {
|
|
88
|
+
private readonly _actions;
|
|
89
|
+
private readonly _events;
|
|
90
|
+
private readonly _handlers;
|
|
91
|
+
private readonly _instances;
|
|
92
|
+
private readonly _logger;
|
|
93
|
+
private readonly _resolveTileWidget?;
|
|
94
|
+
/** Active TTL timeout — replaced on each call to `scheduleClientTtl`. */
|
|
95
|
+
private _ttlTimer;
|
|
96
|
+
constructor(options: ElementInstanceStoreOptions);
|
|
97
|
+
/**
|
|
98
|
+
* Listen for `element.compositional_replay_request` from container
|
|
99
|
+
* widgets and re-publish `element.compositional_append` for every
|
|
100
|
+
* matching item still tracked in `_instances`. This keeps LLM-authored
|
|
101
|
+
* items visible across SPA navigations: the store is a module-level
|
|
102
|
+
* singleton that survives chat-bar unmount/remount, so when a fresh
|
|
103
|
+
* chips-strip mounts and subscribes, asking for a replay re-populates
|
|
104
|
+
* its chip array.
|
|
105
|
+
*/
|
|
106
|
+
private _subscribeReplayRequests;
|
|
107
|
+
private _replayItems;
|
|
108
|
+
private _replayTiles;
|
|
109
|
+
registerHandler(handler: ElementTypeHandler): void;
|
|
110
|
+
/**
|
|
111
|
+
* Apply the server-provided boot snapshot. For each instance:
|
|
112
|
+
* - stateful: validated + mounted via the type handler;
|
|
113
|
+
* - imperative + already-applied: recorded only (no DOM action);
|
|
114
|
+
* - any: tracked in the in-memory map for future patches.
|
|
115
|
+
*
|
|
116
|
+
* Safe to call once on SDK init. Calling again would re-mount
|
|
117
|
+
* stateful instances; callers should not.
|
|
118
|
+
*/
|
|
119
|
+
hydrate(snapshot: MountedElement[]): Promise<void>;
|
|
120
|
+
/**
|
|
121
|
+
* Re-publish `element.tile_mounted` / `element.compositional_append`
|
|
122
|
+
* for every active LLM-authored instance in the store. Idempotent —
|
|
123
|
+
* receivers key by `instance_id` (tiles) or by widget identity
|
|
124
|
+
* (items via tile_id filter).
|
|
125
|
+
*/
|
|
126
|
+
private _broadcastActive;
|
|
127
|
+
/**
|
|
128
|
+
* Apply a batch of mutations, atomically per-batch. Each mutation is
|
|
129
|
+
* version-checked (mount: must not conflict with existing instance;
|
|
130
|
+
* patch: resulting_version must be > current; unmount: idempotent).
|
|
131
|
+
*/
|
|
132
|
+
apply(mutations: ElementMutation[]): Promise<void>;
|
|
133
|
+
/** Read-only snapshot of currently-known instances. */
|
|
134
|
+
getInstances(): MountedElement[];
|
|
135
|
+
getInstance(instance_id: string): MountedElement | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* Schedule a client-side TTL that unmounts every active stateful
|
|
138
|
+
* instance when the server-side session expires. Replaces any
|
|
139
|
+
* previously-scheduled TTL.
|
|
140
|
+
*
|
|
141
|
+
* Avoids the orphan-window where session-expired tiles linger on the
|
|
142
|
+
* page until manual refresh — the server forgets at the 4h mark and
|
|
143
|
+
* the SDK tears down concurrently. Re-schedule on each boot fetch /
|
|
144
|
+
* mutation event that carries a refreshed `session_expires_at`.
|
|
145
|
+
*
|
|
146
|
+
* Pass `null` to clear the existing timer without scheduling a new one.
|
|
147
|
+
* Pass a past timestamp to trigger cleanup immediately.
|
|
148
|
+
*/
|
|
149
|
+
scheduleClientTtl(session_expires_at: string | null): void;
|
|
150
|
+
private _handleSessionExpiry;
|
|
151
|
+
private _applyOne;
|
|
152
|
+
private _mount;
|
|
153
|
+
private _ctx;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=ElementInstanceStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElementInstanceStore.d.ts","sourceRoot":"","sources":["../../src/elements/ElementInstanceStore.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAQH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAyB,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAK/D,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,YAAY,CAAC;IACtB;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,EAAE;QACP,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;QACjE;;;;;;WAMG;QACH,SAAS,CAAC,EAAE;YACV,CACE,MAAM,EAAE;gBAAE,KAAK,CAAC,EAAE,MAAM,EAAE,CAAA;aAAE,EAC5B,OAAO,EAAE,CAAC,KAAK,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;aAAE,KAAK,IAAI,GAC1E,MAAM,IAAI,CAAC;YACd,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;aAAE,KAAK,IAAI,GAAG,MAAM,IAAI,CAAC;SAC3F,CAAC;KACH,CAAC;IACF,QAAQ,CAAC,EAAE,kBAAkB,EAAE,CAAC;IAChC,iEAAiE;IACjE,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;IAClD;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAChE;AAED,qBAAa,oBAAoB;IAC/B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAe;IACxC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAwC;IAChE,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAkC;IAC5D,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA0C;IACrE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA2C;IACnE,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAA6C;IACjF,yEAAyE;IACzE,OAAO,CAAC,SAAS,CAA8C;gBAEnD,OAAO,EAAE,2BAA2B;IAYhD;;;;;;;;OAQG;IACH,OAAO,CAAC,wBAAwB;IAuBhC,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,YAAY;IAepB,eAAe,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI;IAIlD;;;;;;;;OAQG;IACG,OAAO,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA8CxD;;;;;OAKG;IACH,OAAO,CAAC,gBAAgB;IAiBxB;;;;OAIG;IACG,KAAK,CAAC,SAAS,EAAE,eAAe,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAYxD,uDAAuD;IACvD,YAAY,IAAI,cAAc,EAAE;IAIhC,WAAW,CAAC,WAAW,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS;IAI5D;;;;;;;;;;;;OAYG;IACH,iBAAiB,CAAC,kBAAkB,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;YAuB5C,oBAAoB;YAkBpB,SAAS;YAsGT,MAAM;IAyBpB,OAAO,CAAC,IAAI;CAGb"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElementInstanceStore.test.d.ts","sourceRoot":"","sources":["../../src/elements/ElementInstanceStore.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-type handler interface for `ElementInstanceStore`.
|
|
3
|
+
*
|
|
4
|
+
* Adding a future element type (overlay / tour / form) is one new
|
|
5
|
+
* `ElementTypeHandler` implementation registered in the store —
|
|
6
|
+
* `ElementInstanceStore` itself stays type-neutral.
|
|
7
|
+
*
|
|
8
|
+
* Handlers own their per-type lifecycle. They receive the current
|
|
9
|
+
* `MountedElement` snapshot and the runtime's `ActionEngine` and either
|
|
10
|
+
* call `applyBatch` to drive DOM changes or hold on to returned
|
|
11
|
+
* `ActionHandle`s for later cleanup.
|
|
12
|
+
*
|
|
13
|
+
* Design choices the handlers MUST honor:
|
|
14
|
+
*
|
|
15
|
+
* - **Imperative actions never re-fire on boot.** When the store
|
|
16
|
+
* hydrates an element with `lifecycle === 'imperative'` and `state ===
|
|
17
|
+
* 'applied'`, the handler's `mount()` is NOT called. The backend
|
|
18
|
+
* records the prior invocation for audit but the SDK does not replay
|
|
19
|
+
* it (would re-scroll, re-navigate, etc.).
|
|
20
|
+
* - **Defense-in-depth content validation.** `validate()` is called
|
|
21
|
+
* before every mount/patch. The backend already validated, but the
|
|
22
|
+
* handler should refuse malformed content so a buggy server can't
|
|
23
|
+
* crash the runtime.
|
|
24
|
+
*/
|
|
25
|
+
import type { ActionEngine } from '../types';
|
|
26
|
+
import type { ElementMutation, MountedElement } from './types';
|
|
27
|
+
/** Lifecycle classification used to decide whether to apply on boot rehydrate. */
|
|
28
|
+
export type ElementLifecycle = 'stateful' | 'imperative' | 'ephemeral';
|
|
29
|
+
export interface ElementHandlerContext {
|
|
30
|
+
/** Runtime's ActionEngine — handlers use `applyBatch` to drive DOM
|
|
31
|
+
* changes via existing executors (`core:mountWidget`, etc.). */
|
|
32
|
+
actions: ActionEngine;
|
|
33
|
+
/** Runtime's event bus — handlers publish typed events that container
|
|
34
|
+
* widgets subscribe to. Used by `ItemHandler` to broadcast
|
|
35
|
+
* `element.compositional_append` so chip-strip / FAQ / nav-tips
|
|
36
|
+
* containers can insert LLM-authored items.
|
|
37
|
+
* Optional so test fixtures that exercise only DOM action handlers
|
|
38
|
+
* don't need to stub the bus.
|
|
39
|
+
*/
|
|
40
|
+
events?: {
|
|
41
|
+
publish: (name: string, props?: Record<string, unknown>) => void;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
export type ValidateResult = {
|
|
45
|
+
accepted: true;
|
|
46
|
+
} | {
|
|
47
|
+
accepted: false;
|
|
48
|
+
reason: string;
|
|
49
|
+
detail?: string;
|
|
50
|
+
};
|
|
51
|
+
export interface ElementTypeHandler {
|
|
52
|
+
readonly typeName: 'tile' | 'action' | 'item';
|
|
53
|
+
/** Whether the handler's elements survive a boot rehydrate. Imperative
|
|
54
|
+
* handlers (action handler in v1) DO NOT replay applied instances. */
|
|
55
|
+
readonly lifecycle: ElementLifecycle;
|
|
56
|
+
/** Defense-in-depth — refuse malformed content/placement before the
|
|
57
|
+
* handler touches the DOM. The store calls this before `mount` and
|
|
58
|
+
* `patch`; a rejection is logged but does not throw — the store
|
|
59
|
+
* records the rejection on the instance and skips the apply. */
|
|
60
|
+
validate(instance: MountedElement, ctx: ElementHandlerContext): ValidateResult;
|
|
61
|
+
/** Apply a new mount to the DOM. Called once per mount; the store
|
|
62
|
+
* passes the instance object so the handler can read `placement`,
|
|
63
|
+
* `content`, `template_id`, etc. */
|
|
64
|
+
mount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void>;
|
|
65
|
+
/** Apply a patch — typically by re-mounting the widget with merged
|
|
66
|
+
* content. v1 has no fine-grained reactivity; full re-mount is
|
|
67
|
+
* expected. */
|
|
68
|
+
patch(instance: MountedElement, ctx: ElementHandlerContext): Promise<void>;
|
|
69
|
+
/** Remove the element from the DOM. Idempotent — called for both
|
|
70
|
+
* user-initiated unmounts and TTL-triggered cleanup. */
|
|
71
|
+
unmount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void>;
|
|
72
|
+
}
|
|
73
|
+
export interface ElementMutationContext extends ElementHandlerContext {
|
|
74
|
+
/** Op being processed — handler-side telemetry / debug surface. */
|
|
75
|
+
mutation: ElementMutation;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=ElementTypeHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ElementTypeHandler.d.ts","sourceRoot":"","sources":["../../src/elements/ElementTypeHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE/D,kFAAkF;AAClF,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,YAAY,GAAG,WAAW,CAAC;AAEvE,MAAM,WAAW,qBAAqB;IACpC;qEACiE;IACjE,OAAO,EAAE,YAAY,CAAC;IACtB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE;QACP,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;KAClE,CAAC;CACH;AAED,MAAM,MAAM,cAAc,GACtB;IAAE,QAAQ,EAAE,IAAI,CAAA;CAAE,GAClB;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEzD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,CAAC;IAE9C;2EACuE;IACvE,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC;IAErC;;;qEAGiE;IACjE,QAAQ,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,cAAc,CAAC;IAE/E;;yCAEqC;IACrC,KAAK,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3E;;oBAEgB;IAChB,KAAK,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE3E;6DACyD;IACzD,OAAO,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9E;AAED,MAAM,WAAW,sBAAuB,SAAQ,qBAAqB;IACnE,mEAAmE;IACnE,QAAQ,EAAE,eAAe,CAAC;CAC3B"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Item element handler — translates `parent_tile`-placed mount/patch/
|
|
3
|
+
* unmount mutations into pub/sub events that container widgets subscribe
|
|
4
|
+
* to.
|
|
5
|
+
*
|
|
6
|
+
* Container widgets (chips strip, FAQ accordion, nav tips) declare
|
|
7
|
+
* themselves participants by implementing the `CompositionalContainer`
|
|
8
|
+
* contract and subscribing to `runtime.events` with
|
|
9
|
+
* `element.compositional_append` / `element.compositional_patch` /
|
|
10
|
+
* `element.compositional_remove`. Each container filters by
|
|
11
|
+
* `props.tile_id === this.tileId`.
|
|
12
|
+
*
|
|
13
|
+
* Why pub/sub rather than DOM lookup? The runtime's event bus is
|
|
14
|
+
* domain-agnostic and already exists. Container widgets that aren't
|
|
15
|
+
* mounted yet (slow inline-slot wrapper hydration) miss the event,
|
|
16
|
+
* but the next subscribe-then-replay pass at container mount time picks
|
|
17
|
+
* up the durable session state from the LLM's `mounted_elements`. No
|
|
18
|
+
* DOM crawling, no MutationObserver wiring, no ordering assumption.
|
|
19
|
+
*
|
|
20
|
+
* The handler is dumb: it forwards the wire `content` as the `item`
|
|
21
|
+
* payload. The backend wrote that content as a fully-formed
|
|
22
|
+
* compositional-item envelope (`{kind, config: {id, title, payload}}`)
|
|
23
|
+
* so the receiving container can append it verbatim.
|
|
24
|
+
*/
|
|
25
|
+
import type { ElementHandlerContext, ElementTypeHandler, ValidateResult } from './ElementTypeHandler';
|
|
26
|
+
import type { MountedElement } from './types';
|
|
27
|
+
/** Event names. Kept as module constants so subscribers can import them
|
|
28
|
+
* instead of memorizing strings. */
|
|
29
|
+
export declare const COMPOSITIONAL_APPEND_EVENT = "element.compositional_append";
|
|
30
|
+
export declare const COMPOSITIONAL_PATCH_EVENT = "element.compositional_patch";
|
|
31
|
+
export declare const COMPOSITIONAL_REMOVE_EVENT = "element.compositional_remove";
|
|
32
|
+
export interface CompositionalAppendEvent {
|
|
33
|
+
/** Parent tile id from the mount's placement. Receivers filter on this. */
|
|
34
|
+
tile_id: string;
|
|
35
|
+
/** Unique id of the mounted item — the receiving container should also
|
|
36
|
+
* set this on the item's DOM as `data-syntro-element-id`. */
|
|
37
|
+
instance_id: string;
|
|
38
|
+
/** Wire shape constructed by the backend — `{kind, config: {…}}`. */
|
|
39
|
+
item: Record<string, unknown>;
|
|
40
|
+
/** Insertion position — receivers honor this when appending. */
|
|
41
|
+
position: 'append' | 'prepend';
|
|
42
|
+
}
|
|
43
|
+
export interface CompositionalPatchEvent {
|
|
44
|
+
tile_id: string;
|
|
45
|
+
instance_id: string;
|
|
46
|
+
item: Record<string, unknown>;
|
|
47
|
+
}
|
|
48
|
+
export interface CompositionalRemoveEvent {
|
|
49
|
+
tile_id: string;
|
|
50
|
+
instance_id: string;
|
|
51
|
+
}
|
|
52
|
+
export declare class ItemHandler implements ElementTypeHandler {
|
|
53
|
+
readonly typeName: "item";
|
|
54
|
+
readonly lifecycle: "stateful";
|
|
55
|
+
validate(instance: MountedElement, _ctx: ElementHandlerContext): ValidateResult;
|
|
56
|
+
mount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void>;
|
|
57
|
+
patch(instance: MountedElement, ctx: ElementHandlerContext): Promise<void>;
|
|
58
|
+
unmount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void>;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=ItemHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemHandler.d.ts","sourceRoot":"","sources":["../../src/elements/ItemHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,SAAS,CAAC;AAEnE;qCACqC;AACrC,eAAO,MAAM,0BAA0B,iCAAiC,CAAC;AACzE,eAAO,MAAM,yBAAyB,gCAAgC,CAAC;AACvE,eAAO,MAAM,0BAA0B,iCAAiC,CAAC;AAEzE,MAAM,WAAW,wBAAwB;IACvC,2EAA2E;IAC3E,OAAO,EAAE,MAAM,CAAC;IAChB;kEAC8D;IAC9D,WAAW,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,gEAAgE;IAChE,QAAQ,EAAE,QAAQ,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,WAAY,YAAW,kBAAkB;IACpD,QAAQ,CAAC,QAAQ,EAAG,MAAM,CAAU;IACpC,QAAQ,CAAC,SAAS,EAAG,UAAU,CAAU;IAEzC,QAAQ,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,qBAAqB,GAAG,cAAc;IAqBzE,KAAK,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAW1E,KAAK,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAU1E,OAAO,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;CAQnF"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ItemHandler.test.d.ts","sourceRoot":"","sources":["../../src/elements/ItemHandler.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tile element handler — translates `slot`-placed mount/patch/unmount
|
|
3
|
+
* mutations into pub/sub events that the runtime SDK subscribes to.
|
|
4
|
+
*
|
|
5
|
+
* The runtime SDK's `SmartCanvasElementLit` already routes config-driven
|
|
6
|
+
* tiles to inline slots via `tile.slot === slotName`. LLM-authored tiles
|
|
7
|
+
* use the same path: we publish a TileConfig-shaped envelope and the
|
|
8
|
+
* runtime merges it into its tile array, then existing routing renders
|
|
9
|
+
* it inside the slot's `<syntro-tile-stack>` alongside any config tiles.
|
|
10
|
+
*
|
|
11
|
+
* Why pub/sub instead of `core:mountWidget` via ActionEngine?
|
|
12
|
+
*
|
|
13
|
+
* - `Surfaces.mount` is exclusive per slot — a second LLM tile to the
|
|
14
|
+
* same slot would displace the first.
|
|
15
|
+
* - `Surfaces` only knows built-in slot names; inline slots resolve
|
|
16
|
+
* anchors via SyntroInlineSlot, not via Surfaces.
|
|
17
|
+
* - Tiles need the SyntroTileCard chrome (chromeless handling, theme
|
|
18
|
+
* overrides, dismiss control) that `core:mountWidget` skips.
|
|
19
|
+
*
|
|
20
|
+
* Container widgets stay dumb: the runtime owns one tile array per slot
|
|
21
|
+
* and renders it the same way regardless of provenance. Subscribers
|
|
22
|
+
* filter on `slot === this.name`.
|
|
23
|
+
*
|
|
24
|
+
* Event names + payload types live in `@syntrologie/sdk-contracts` so
|
|
25
|
+
* both packages share the contract. Renaming a field there is a TS
|
|
26
|
+
* error here and in the runtime subscriber — no silent drift.
|
|
27
|
+
*/
|
|
28
|
+
import type { ElementHandlerContext, ElementTypeHandler, ValidateResult } from './ElementTypeHandler';
|
|
29
|
+
import type { MountedElement } from './types';
|
|
30
|
+
export { TILE_MOUNTED_EVENT, TILE_PATCHED_EVENT, TILE_UNMOUNTED_EVENT, } from '@syntrologie/sdk-contracts';
|
|
31
|
+
/**
|
|
32
|
+
* Optional template→widget resolver. ``uiTemplates`` declares both
|
|
33
|
+
* the LLM-facing ``id`` (e.g. ``"product-card"``) and the runtime
|
|
34
|
+
* widget the chrome should render (e.g. ``"adaptive-product:card"``).
|
|
35
|
+
* Without this resolver, the handler falls back to ``template_id`` as
|
|
36
|
+
* the widget id — which only works when the admin chose ids matching
|
|
37
|
+
* the widget registry, and silently mounts to "Widget not available"
|
|
38
|
+
* when they don't.
|
|
39
|
+
*/
|
|
40
|
+
export type TileTemplateWidgetResolver = (templateId: string) => string | undefined;
|
|
41
|
+
export declare class TileHandler implements ElementTypeHandler {
|
|
42
|
+
private readonly resolveWidget?;
|
|
43
|
+
readonly typeName: "tile";
|
|
44
|
+
readonly lifecycle: "stateful";
|
|
45
|
+
constructor(resolveWidget?: TileTemplateWidgetResolver | undefined);
|
|
46
|
+
private _widgetFor;
|
|
47
|
+
validate(instance: MountedElement, _ctx: ElementHandlerContext): ValidateResult;
|
|
48
|
+
mount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void>;
|
|
49
|
+
patch(instance: MountedElement, ctx: ElementHandlerContext): Promise<void>;
|
|
50
|
+
unmount(instance: MountedElement, ctx: ElementHandlerContext): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=TileHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TileHandler.d.ts","sourceRoot":"","sources":["../../src/elements/TileHandler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAUH,OAAO,KAAK,EACV,qBAAqB,EACrB,kBAAkB,EAClB,cAAc,EACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,cAAc,EAAiB,MAAM,SAAS,CAAC;AAE7D,OAAO,EACL,kBAAkB,EAClB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,4BAA4B,CAAC;AAEpC;;;;;;;;GAQG;AACH,MAAM,MAAM,0BAA0B,GAAG,CAAC,UAAU,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;AAEpF,qBAAa,WAAY,YAAW,kBAAkB;IAIxC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAC;IAH3C,QAAQ,CAAC,QAAQ,EAAG,MAAM,CAAU;IACpC,QAAQ,CAAC,SAAS,EAAG,UAAU,CAAU;gBAEZ,aAAa,CAAC,EAAE,0BAA0B,YAAA;IAEvE,OAAO,CAAC,UAAU;IAIlB,QAAQ,CAAC,QAAQ,EAAE,cAAc,EAAE,IAAI,EAAE,qBAAqB,GAAG,cAAc;IAiBzE,KAAK,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1E,KAAK,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;IAY1E,OAAO,CAAC,QAAQ,EAAE,cAAc,EAAE,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;CASnF"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portable Text block renderer — Phase 7 of the LLM-authored UI element
|
|
3
|
+
* instantiation work.
|
|
4
|
+
*
|
|
5
|
+
* `renderBlocks(blocks)` walks a Portable Text tree and produces a
|
|
6
|
+
* `DocumentFragment` of known-safe DOM. Calling code attaches the
|
|
7
|
+
* fragment via `target.appendChild(fragment)` /
|
|
8
|
+
* `target.insertAdjacentElement(...)` — **never** via `innerHTML`.
|
|
9
|
+
*
|
|
10
|
+
* The safety story:
|
|
11
|
+
*
|
|
12
|
+
* 1. **No `innerHTML` from agent data, ever.** Every text reaches the
|
|
13
|
+
* DOM through `document.createTextNode` or `element.textContent`.
|
|
14
|
+
* Even if an agent slips a `<script>` tag into a span's text, it's
|
|
15
|
+
* rendered as the literal characters `<script>` inside a text node,
|
|
16
|
+
* never parsed as HTML.
|
|
17
|
+
* 2. **Closed mark vocabulary.** Only `bold` / `italic` / `code` / `link`
|
|
18
|
+
* are honored. Unknown marks are dropped at render time.
|
|
19
|
+
* 3. **Closed block vocabulary.** Only `paragraph` / `heading` / `list` /
|
|
20
|
+
* `list_item` / `code` are honored. Unknown block types are dropped.
|
|
21
|
+
* 4. **Link href validation.** Backend already rejects `javascript:` /
|
|
22
|
+
* `data:` / `vbscript:` etc., but the renderer re-checks for defense
|
|
23
|
+
* in depth. Rejected links render as plain text spans.
|
|
24
|
+
* 5. **No inline styles, no event handlers, no custom attributes.**
|
|
25
|
+
* Every attribute set on the output DOM is hard-coded in this file.
|
|
26
|
+
*
|
|
27
|
+
* Forward-compat: unknown block / mark types drop silently rather than
|
|
28
|
+
* throw — a future schema bump can add types without breaking today's
|
|
29
|
+
* renderer.
|
|
30
|
+
*/
|
|
31
|
+
import type { BlockNode } from './blocks';
|
|
32
|
+
export interface RenderOptions {
|
|
33
|
+
/** Override the document used for `createElement` / `createTextNode`.
|
|
34
|
+
* Tests inject jsdom's document; SSR contexts can pass a custom impl
|
|
35
|
+
* if needed. Defaults to `globalThis.document`. */
|
|
36
|
+
doc?: Document;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Render an array of blocks into a `DocumentFragment` of safe DOM.
|
|
40
|
+
*
|
|
41
|
+
* Unknown / malformed entries are silently dropped — the caller has
|
|
42
|
+
* already validated server-side; the renderer is the last line of
|
|
43
|
+
* defense and stays fault-tolerant.
|
|
44
|
+
*/
|
|
45
|
+
export declare function renderBlocks(blocks: BlockNode[], options?: RenderOptions): DocumentFragment;
|
|
46
|
+
//# sourceMappingURL=blockRenderer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blockRenderer.d.ts","sourceRoot":"","sources":["../../src/elements/blockRenderer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EACV,SAAS,EAQV,MAAM,UAAU,CAAC;AAQlB,MAAM,WAAW,aAAa;IAC5B;;wDAEoD;IACpD,GAAG,CAAC,EAAE,QAAQ,CAAC;CAChB;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,OAAO,GAAE,aAAkB,GAAG,gBAAgB,CAiB/F"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the Portable Text block renderer — Phase 7.
|
|
3
|
+
*
|
|
4
|
+
* The bulk of this file is a hostile-input corpus. The point of the
|
|
5
|
+
* renderer is that **agent-authored text cannot produce script execution
|
|
6
|
+
* or unsafe DOM**, by construction, regardless of what the agent
|
|
7
|
+
* sends. Each test below pumps a different attack shape through
|
|
8
|
+
* `renderBlocks` and asserts that the resulting DOM contains no
|
|
9
|
+
* dangerous nodes, no live script content, and no `javascript:` /
|
|
10
|
+
* `data:` URIs on anchor tags.
|
|
11
|
+
*/
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=blockRenderer.test.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blockRenderer.test.d.ts","sourceRoot":"","sources":["../../src/elements/blockRenderer.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Portable Text block + mark types — Phase 7 of the LLM-authored UI
|
|
3
|
+
* element instantiation work.
|
|
4
|
+
*
|
|
5
|
+
* Mirror of `platform/common/syntrologie_common/sdk/blocks.py`. Closed
|
|
6
|
+
* vocabulary (block types + mark types) the LLM may author in
|
|
7
|
+
* `rich_text_blocks` fields. The renderer walks this tree and produces
|
|
8
|
+
* DOM via `document.createElement` + `textContent` — **agent never
|
|
9
|
+
* writes HTML strings**. XSS-class attacks have no surface because
|
|
10
|
+
* there's no HTML on the wire.
|
|
11
|
+
*
|
|
12
|
+
* Forward-compat: unknown block types and unknown marks are dropped at
|
|
13
|
+
* render time (not thrown). A future protocol bump can add new types
|
|
14
|
+
* without breaking today's renderer.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* The closed inline-mark vocabulary. Adding a mark is a deliberate
|
|
18
|
+
* code change with review, never an allowlist tweak.
|
|
19
|
+
*/
|
|
20
|
+
export type MarkType = 'bold' | 'italic' | 'code' | 'link';
|
|
21
|
+
export type HeadingLevel = 1 | 2 | 3 | 4 | 5 | 6;
|
|
22
|
+
export type ListStyle = 'bullet' | 'numbered';
|
|
23
|
+
export interface LinkMark {
|
|
24
|
+
/** Validated server-side; renderer revalidates for defense in depth. */
|
|
25
|
+
href: string;
|
|
26
|
+
}
|
|
27
|
+
export interface TextSpan {
|
|
28
|
+
text: string;
|
|
29
|
+
marks?: MarkType[];
|
|
30
|
+
link?: LinkMark;
|
|
31
|
+
}
|
|
32
|
+
export interface ParagraphBlock {
|
|
33
|
+
type: 'paragraph';
|
|
34
|
+
spans: TextSpan[];
|
|
35
|
+
}
|
|
36
|
+
export interface HeadingBlock {
|
|
37
|
+
type: 'heading';
|
|
38
|
+
level: HeadingLevel;
|
|
39
|
+
spans: TextSpan[];
|
|
40
|
+
}
|
|
41
|
+
export interface ListItemBlock {
|
|
42
|
+
type: 'list_item';
|
|
43
|
+
spans: TextSpan[];
|
|
44
|
+
}
|
|
45
|
+
export interface ListBlock {
|
|
46
|
+
type: 'list';
|
|
47
|
+
style: ListStyle;
|
|
48
|
+
items: ListItemBlock[];
|
|
49
|
+
}
|
|
50
|
+
export interface CodeBlock {
|
|
51
|
+
type: 'code';
|
|
52
|
+
/** Raw code text. Rendered inside `<pre><code>` via `textContent` —
|
|
53
|
+
* no HTML interpretation, no syntax-highlight injection. */
|
|
54
|
+
text: string;
|
|
55
|
+
language?: string;
|
|
56
|
+
}
|
|
57
|
+
export type BlockNode = ParagraphBlock | HeadingBlock | ListBlock | CodeBlock;
|
|
58
|
+
//# sourceMappingURL=blocks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blocks.d.ts","sourceRoot":"","sources":["../../src/elements/blocks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3D,MAAM,MAAM,YAAY,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;AAEjD,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE9C,MAAM,WAAW,QAAQ;IACvB,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,QAAQ,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,QAAQ,CAAC;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,YAAY,CAAC;IACpB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,QAAQ,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,aAAa,EAAE,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb;iEAC6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,MAAM,SAAS,GAAG,cAAc,GAAG,YAAY,GAAG,SAAS,GAAG,SAAS,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for decoding AG-UI custom event payloads — Phase 6b.
|
|
3
|
+
*
|
|
4
|
+
* `AgUiTransport.onA2UIEvent` historically forwarded the raw `event.value`
|
|
5
|
+
* payload as an `ActionStep` directly to `runtime.actions.applyBatch`.
|
|
6
|
+
* Phase 5's backend now also emits `syntro.element.mutation` envelopes
|
|
7
|
+
* (mounts/patches/unmounts authored by the LLM via `mount_element` &c.),
|
|
8
|
+
* which the SDK routes to `ElementInstanceStore.apply` instead.
|
|
9
|
+
*
|
|
10
|
+
* Discrimination is by the envelope's `type` field. Non-element payloads
|
|
11
|
+
* (raw ActionStep, A2UI `createSurface` / `updateDataModel` envelopes
|
|
12
|
+
* from any A2UI integration) fall through to the legacy path.
|
|
13
|
+
*/
|
|
14
|
+
import type { ElementMutation } from './types';
|
|
15
|
+
/**
|
|
16
|
+
* Returns the mutations array if `payload` is a well-formed
|
|
17
|
+
* `syntro.element.mutation` envelope, otherwise `null`.
|
|
18
|
+
*
|
|
19
|
+
* Validates only the discriminator + the structural shape needed to
|
|
20
|
+
* route to the store. Per-mutation validation lives in the store / its
|
|
21
|
+
* type handlers (defense in depth), so this stays small.
|
|
22
|
+
*/
|
|
23
|
+
export declare function decodeMutationEnvelope(payload: unknown): ElementMutation[] | null;
|
|
24
|
+
//# sourceMappingURL=envelope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"envelope.d.ts","sourceRoot":"","sources":["../../src/elements/envelope.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAoB,MAAM,SAAS,CAAC;AAEjE;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,OAAO,GAAG,eAAe,EAAE,GAAG,IAAI,CAOjF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boot-time fetcher for `GET /api/adaptive/mounted_elements`.
|
|
3
|
+
*
|
|
4
|
+
* Follows the SDK's vanilla-fetch convention (see
|
|
5
|
+
* `packages/runtime-sdk/src/overlays/fetcher.ts` for the sibling pattern
|
|
6
|
+
* — no shared HTTP client, no axios, no fetch wrapper).
|
|
7
|
+
*
|
|
8
|
+
* Auth: forwards the SDK token from `window.__SYNTRO_CONFIG__.token`
|
|
9
|
+
* as a Bearer header. Cookies (`syntro_chat_session`) are sent via
|
|
10
|
+
* `credentials: 'include'` so the backend can scope to the same Redis
|
|
11
|
+
* session the chat uses.
|
|
12
|
+
*
|
|
13
|
+
* 404 contract: the backend returns 404 only when the workspace's SDK
|
|
14
|
+
* token doesn't resolve to a workspace. The SDK treats 404 as
|
|
15
|
+
* "feature unavailable" — no error UI, no retries — per the design doc.
|
|
16
|
+
* Network errors are logged + swallowed so the SDK boot path stays
|
|
17
|
+
* resilient to backend hiccups.
|
|
18
|
+
*/
|
|
19
|
+
import type { MountedElementsResponse } from './types';
|
|
20
|
+
export interface FetchMountedElementsOptions {
|
|
21
|
+
/** Override the endpoint URL (defaults to same-origin
|
|
22
|
+
* `/api/adaptive/mounted_elements`). Tests + multi-environment
|
|
23
|
+
* deploys use this. */
|
|
24
|
+
endpoint?: string;
|
|
25
|
+
/** SDK token. Defaults to `window.__SYNTRO_CONFIG__.token`. */
|
|
26
|
+
token?: string;
|
|
27
|
+
/** Custom fetch implementation — used by tests to mock the network. */
|
|
28
|
+
fetchImpl?: typeof fetch;
|
|
29
|
+
/** Logger — used by tests to capture log output. */
|
|
30
|
+
logger?: Pick<Console, 'warn'>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Fetch the visitor's currently-mounted elements + session expiry.
|
|
34
|
+
*
|
|
35
|
+
* Returns `null` on any failure (404, network error, malformed JSON,
|
|
36
|
+
* etc.). The caller treats `null` as "feature unavailable, no
|
|
37
|
+
* rehydrate" — boot continues normally with an empty element store.
|
|
38
|
+
*/
|
|
39
|
+
export declare function fetchMountedElements(options?: FetchMountedElementsOptions): Promise<MountedElementsResponse | null>;
|
|
40
|
+
//# sourceMappingURL=fetcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetcher.d.ts","sourceRoot":"","sources":["../../src/elements/fetcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAC;AAIvD,MAAM,WAAW,2BAA2B;IAC1C;;4BAEwB;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,oDAAoD;IACpD,MAAM,CAAC,EAAE,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;GAMG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,uBAAuB,GAAG,IAAI,CAAC,CA+CzC"}
|