@urbicon-ui/blocks 6.37.2 → 6.39.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 (78) hide show
  1. package/dist/components/AvatarGroup/AvatarGroup.svelte +60 -0
  2. package/dist/components/AvatarGroup/AvatarGroup.svelte.d.ts +4 -0
  3. package/dist/components/AvatarGroup/avatar-group.variants.d.ts +20 -0
  4. package/dist/components/AvatarGroup/avatar-group.variants.js +21 -0
  5. package/dist/components/AvatarGroup/index.d.ts +57 -0
  6. package/dist/components/AvatarGroup/index.js +2 -0
  7. package/dist/components/Calendar/calendar.variants.d.ts +126 -126
  8. package/dist/components/Chat/A2UIView/A2UINode.svelte +582 -0
  9. package/dist/components/Chat/A2UIView/A2UINode.svelte.d.ts +10 -0
  10. package/dist/components/Chat/A2UIView/A2UIView.svelte +272 -0
  11. package/dist/components/Chat/A2UIView/A2UIView.svelte.d.ts +4 -0
  12. package/dist/components/Chat/A2UIView/a2ui-data.d.ts +65 -0
  13. package/dist/components/Chat/A2UIView/a2ui-data.js +283 -0
  14. package/dist/components/Chat/A2UIView/a2ui-prompt.d.ts +18 -0
  15. package/dist/components/Chat/A2UIView/a2ui-prompt.js +141 -0
  16. package/dist/components/Chat/A2UIView/a2ui-registry.d.ts +64 -0
  17. package/dist/components/Chat/A2UIView/a2ui-registry.js +348 -0
  18. package/dist/components/Chat/A2UIView/a2ui-render.d.ts +96 -0
  19. package/dist/components/Chat/A2UIView/a2ui-render.js +140 -0
  20. package/dist/components/Chat/A2UIView/a2ui-validate.d.ts +75 -0
  21. package/dist/components/Chat/A2UIView/a2ui-validate.js +762 -0
  22. package/dist/components/Chat/A2UIView/a2ui-view.variants.d.ts +74 -0
  23. package/dist/components/Chat/A2UIView/a2ui-view.variants.js +58 -0
  24. package/dist/components/Chat/A2UIView/a2ui.types.d.ts +117 -0
  25. package/dist/components/Chat/A2UIView/a2ui.types.js +83 -0
  26. package/dist/components/Chat/A2UIView/index.d.ts +102 -0
  27. package/dist/components/Chat/A2UIView/index.js +7 -0
  28. package/dist/components/Chat/ChatMessage/ChatMessage.svelte +4 -1
  29. package/dist/components/Chat/index.d.ts +2 -0
  30. package/dist/components/Chat/index.js +1 -0
  31. package/dist/components/CopyButton/CopyButton.svelte +139 -0
  32. package/dist/components/CopyButton/CopyButton.svelte.d.ts +4 -0
  33. package/dist/components/CopyButton/copy-button.variants.d.ts +29 -0
  34. package/dist/components/CopyButton/copy-button.variants.js +32 -0
  35. package/dist/components/CopyButton/index.d.ts +55 -0
  36. package/dist/components/CopyButton/index.js +2 -0
  37. package/dist/components/FileUpload/FileUpload.svelte.d.ts +1 -1
  38. package/dist/components/PinInput/pin-input.variants.d.ts +7 -7
  39. package/dist/components/Planner/planner.variants.d.ts +34 -34
  40. package/dist/components/TimeInput/time-input.variants.d.ts +9 -9
  41. package/dist/components/index.d.ts +4 -0
  42. package/dist/components/index.js +2 -0
  43. package/dist/i18n/index.d.ts +6 -390
  44. package/dist/primitives/Accordion/accordion.variants.d.ts +7 -7
  45. package/dist/primitives/Alert/alert.variants.d.ts +8 -8
  46. package/dist/primitives/Avatar/avatar.variants.d.ts +12 -12
  47. package/dist/primitives/Badge/badge.variants.d.ts +10 -10
  48. package/dist/primitives/Button/button.variants.d.ts +4 -4
  49. package/dist/primitives/Card/card.variants.d.ts +5 -5
  50. package/dist/primitives/Checkbox/checkbox.variants.d.ts +7 -7
  51. package/dist/primitives/Collapsible/collapsible.variants.d.ts +6 -6
  52. package/dist/primitives/Combobox/combobox.variants.d.ts +50 -50
  53. package/dist/primitives/Input/input.variants.d.ts +27 -27
  54. package/dist/primitives/JourneyTimeline/journey-timeline.variants.d.ts +22 -22
  55. package/dist/primitives/Kbd/Kbd.svelte +44 -0
  56. package/dist/primitives/Kbd/Kbd.svelte.d.ts +4 -0
  57. package/dist/primitives/Kbd/index.d.ts +51 -0
  58. package/dist/primitives/Kbd/index.js +2 -0
  59. package/dist/primitives/Kbd/kbd.variants.d.ts +20 -0
  60. package/dist/primitives/Kbd/kbd.variants.js +27 -0
  61. package/dist/primitives/Progress/progress.variants.d.ts +11 -11
  62. package/dist/primitives/RadioGroup/radioGroup.variants.d.ts +6 -6
  63. package/dist/primitives/Select/select.variants.d.ts +34 -34
  64. package/dist/primitives/Skeleton/skeleton.variants.d.ts +3 -3
  65. package/dist/primitives/Spinner/spinner.variants.d.ts +48 -48
  66. package/dist/primitives/Stepper/stepper.variants.d.ts +11 -11
  67. package/dist/primitives/Textarea/textarea.variants.d.ts +21 -21
  68. package/dist/primitives/Toast/toast.variants.d.ts +12 -12
  69. package/dist/primitives/Toggle/toggle.variants.d.ts +7 -7
  70. package/dist/primitives/Toolbar/toolbar.variants.d.ts +6 -6
  71. package/dist/primitives/Tooltip/tooltip.variants.d.ts +3 -3
  72. package/dist/primitives/index.d.ts +2 -0
  73. package/dist/primitives/index.js +1 -0
  74. package/dist/translations/de.d.ts +4 -0
  75. package/dist/translations/de.js +4 -0
  76. package/dist/translations/en.d.ts +4 -0
  77. package/dist/translations/en.js +4 -0
  78. package/package.json +3 -3
@@ -0,0 +1,272 @@
1
+ <script lang="ts">
2
+ import { Alert } from '../../../primitives';
3
+ import { untrack } from 'svelte';
4
+ import { getBlocksConfig, resolveSlotClasses } from '../../../provider';
5
+ import { type IconComponent, resolveIcon } from '../../../icons';
6
+ import PlusIconDefault from '../../../icons/PlusIcon.svelte';
7
+ import ArrowLeftIconDefault from '../../../icons/ArrowLeftIcon.svelte';
8
+ import CheckIconDefault from '../../../icons/CheckIcon.svelte';
9
+ import CloseIconDefault from '../../../icons/CloseIcon.svelte';
10
+ import TrashIconDefault from '../../../icons/TrashIcon.svelte';
11
+ import EditIconDefault from '../../../icons/EditIcon.svelte';
12
+ import DangerCircleIconDefault from '../../../icons/DangerCircleIcon.svelte';
13
+ import HomeIconDefault from '../../../icons/HomeIcon.svelte';
14
+ import InfoCircleIconDefault from '../../../icons/InfoCircleIcon.svelte';
15
+ import MailIconDefault from '../../../icons/MailIcon.svelte';
16
+ import MenuIconDefault from '../../../icons/MenuIcon.svelte';
17
+ import SearchIconDefault from '../../../icons/SearchIcon.svelte';
18
+ import SendIconDefault from '../../../icons/SendIcon.svelte';
19
+ import SettingsIconDefault from '../../../icons/SettingsIcon.svelte';
20
+ import StarIconDefault from '../../../icons/StarIcon.svelte';
21
+ import WarningTriangleIconDefault from '../../../icons/WarningTriangleIcon.svelte';
22
+ import CircleHelpIconDefault from '../../../icons/CircleHelpIcon.svelte';
23
+ import A2UINode from './A2UINode.svelte';
24
+ import type { A2uiValidationIssue } from './a2ui.types';
25
+ import { deleteAtPointer, resolveDynamic, setAtPointer } from './a2ui-data';
26
+ import {
27
+ type A2uiProcessor,
28
+ collectGraphIssues,
29
+ createA2uiProcessor,
30
+ normalizeA2uiPayload
31
+ } from './a2ui-validate';
32
+ import { type A2uiRenderContext, type A2uiRenderNode, buildRenderTree } from './a2ui-render';
33
+ import { a2uiViewVariants } from './a2ui-view.variants';
34
+ import type { A2UIViewProps } from './index';
35
+
36
+ let {
37
+ payload,
38
+ streaming: streamingProp = false,
39
+ urlPolicy,
40
+ onAction,
41
+ onValidationError,
42
+ errorTitle = 'Invalid UI payload',
43
+ unsupportedLabel = 'Unsupported component',
44
+ blockedImageLabel = 'Image blocked',
45
+ pendingLabel = 'Loading UI',
46
+ class: className,
47
+ unstyled: unstyledProp = false,
48
+ slotClasses: slotClassesProp = {},
49
+ preset,
50
+ ...restProps
51
+ }: A2UIViewProps = $props();
52
+
53
+ const blocksConfig = getBlocksConfig();
54
+ const unstyled = $derived(unstyledProp || blocksConfig?.unstyled || false);
55
+
56
+ // A2UI icon-enum name → resolved Urbicon icon. Resolved ONCE via direct
57
+ // imports (tree-shakeable — never getIcon()); an IconProvider override still
58
+ // wins, the direct import is the fallback. Built here (not per node) and
59
+ // threaded through the render context.
60
+ const icons: Readonly<Record<string, IconComponent>> = {
61
+ add: resolveIcon('plus', PlusIconDefault),
62
+ arrowBack: resolveIcon('arrowLeft', ArrowLeftIconDefault),
63
+ check: resolveIcon('check', CheckIconDefault),
64
+ close: resolveIcon('close', CloseIconDefault),
65
+ delete: resolveIcon('trash', TrashIconDefault),
66
+ edit: resolveIcon('edit', EditIconDefault),
67
+ error: resolveIcon('danger', DangerCircleIconDefault),
68
+ home: resolveIcon('home', HomeIconDefault),
69
+ info: resolveIcon('info', InfoCircleIconDefault),
70
+ mail: resolveIcon('mail', MailIconDefault),
71
+ menu: resolveIcon('menu', MenuIconDefault),
72
+ search: resolveIcon('search', SearchIconDefault),
73
+ send: resolveIcon('send', SendIconDefault),
74
+ settings: resolveIcon('settings', SettingsIconDefault),
75
+ star: resolveIcon('star', StarIconDefault),
76
+ warning: resolveIcon('warning', WarningTriangleIconDefault)
77
+ };
78
+ const fallbackIcon = resolveIcon('circleHelp', CircleHelpIconDefault);
79
+
80
+ // ── Processor (plain, non-reactive) + version counter ────────────────────
81
+ // The processor mutates plain Maps in place (the streaming-markdown engine
82
+ // pattern). A $state version counter is the single reactive primitive; the
83
+ // render tree derives from it, so reading it is a real subscription (unlike a
84
+ // `void messages` proxy read). `processor`/`consumed` are plain lets: their
85
+ // reassignment must NOT itself trigger reactivity — the bump does.
86
+ let processor: A2uiProcessor = createA2uiProcessor();
87
+ let consumed: unknown[] = [];
88
+ let normalizeIssue = $state<A2uiValidationIssue | undefined>(undefined);
89
+ let version = $state(0);
90
+ // Bumped ONLY on a full rebuild (payload replaced, not appended). It keys the
91
+ // surface `{#each}`, so a rebuild remounts the node tree and A2UINode's local
92
+ // input fallbacks reset — otherwise a typed-then-replaced literal input keeps
93
+ // the stale text (a rebuild is documented to discard local edits). An
94
+ // incremental append leaves it unchanged, so focus and edits survive.
95
+ let generation = $state(0);
96
+
97
+ function bump(): void {
98
+ version++;
99
+ }
100
+
101
+ // Incremental consumption: if the first `consumed.length` envelopes are
102
+ // referentially identical to the new payload, apply only the appended ones
103
+ // (two-way edits in the data model survive). Otherwise rebuild from scratch.
104
+ // Reads `payload` (tracked) to re-run on every immutable payload change; the
105
+ // mutation is untracked so the `version++`/`normalizeIssue` writes never make
106
+ // this effect depend on the state it drives (avoids an update-depth loop).
107
+ $effect(() => {
108
+ const { envelopes, issue } = normalizeA2uiPayload(payload);
109
+ untrack(() => {
110
+ const prefixMatches =
111
+ envelopes.length >= consumed.length && consumed.every((env, i) => env === envelopes[i]);
112
+ if (!prefixMatches) {
113
+ processor = createA2uiProcessor();
114
+ consumed = [];
115
+ generation++;
116
+ }
117
+ for (let i = consumed.length; i < envelopes.length; i++) {
118
+ processor.apply(envelopes[i], i);
119
+ }
120
+ consumed = envelopes.slice();
121
+ normalizeIssue = issue;
122
+ version++;
123
+ });
124
+ });
125
+
126
+ function isContainer(value: unknown): boolean {
127
+ return value !== null && typeof value === 'object';
128
+ }
129
+
130
+ // Two-way write-back: mutate the surface data model in place, then bump so the
131
+ // render tree re-derives with the new value (bound labels update live).
132
+ function writeBinding(surfaceId: string, pointer: string, value: unknown): void {
133
+ const surface = processor.surfaces.get(surfaceId);
134
+ if (!surface) return;
135
+ if (!isContainer(surface.dataModel)) {
136
+ const firstSegment = pointer.replace(/^\//, '').split('/')[0];
137
+ surface.dataModel = /^(?:0|[1-9]\d*|-)$/.test(firstSegment) ? [] : {};
138
+ }
139
+ setAtPointer(surface.dataModel, pointer, value);
140
+ bump();
141
+ }
142
+ function removeBinding(surfaceId: string, pointer: string): void {
143
+ const surface = processor.surfaces.get(surfaceId);
144
+ if (!surface) return;
145
+ deleteAtPointer(surface.dataModel, pointer);
146
+ bump();
147
+ }
148
+
149
+ // ── Resolved slot classes ────────────────────────────────────────────────
150
+ const styles = a2uiViewVariants();
151
+ const resolvedSlots = $derived(
152
+ resolveSlotClasses(blocksConfig, 'A2UIView', preset, {}, slotClassesProp)
153
+ );
154
+ const classes = $derived.by(() => {
155
+ const out: Record<string, string> = {};
156
+ for (const [name, slotFn] of Object.entries(styles)) {
157
+ const override = resolvedSlots?.[name];
158
+ out[name] = unstyled ? (override ?? '') : slotFn({ class: override });
159
+ }
160
+ return out;
161
+ });
162
+ const rootClass = $derived(
163
+ unstyled
164
+ ? [resolvedSlots?.root, className].filter(Boolean).join(' ')
165
+ : styles.root({ class: [resolvedSlots?.root, className] })
166
+ );
167
+
168
+ // ── Render tree + issue collection (re-derives on every version bump) ────
169
+ interface SurfaceView {
170
+ surfaceId: string;
171
+ root: A2uiRenderNode | null;
172
+ context: A2uiRenderContext;
173
+ }
174
+
175
+ const view = $derived.by(() => {
176
+ version; // subscription to processing + edits
177
+ const isStreaming = streamingProp;
178
+ const currentClasses = classes;
179
+ const surfaces: SurfaceView[] = [];
180
+ const summaryIssues: A2uiValidationIssue[] = [];
181
+ const allIssues: A2uiValidationIssue[] = [];
182
+
183
+ if (normalizeIssue) {
184
+ summaryIssues.push(normalizeIssue);
185
+ allIssues.push(normalizeIssue);
186
+ }
187
+ for (const globalIssue of processor.globalIssues) {
188
+ allIssues.push(globalIssue);
189
+ if (globalIssue.severity === 'error') summaryIssues.push(globalIssue);
190
+ }
191
+
192
+ for (const surface of processor.surfaces.values()) {
193
+ for (const surfaceIssue of surface.issues) allIssues.push(surfaceIssue);
194
+ for (const graphIssue of collectGraphIssues(surface, { streaming: isStreaming })) {
195
+ allIssues.push(graphIssue);
196
+ }
197
+ const root = buildRenderTree(surface);
198
+ const context: A2uiRenderContext = {
199
+ classes: currentClasses,
200
+ urlPolicy,
201
+ streaming: isStreaming,
202
+ inline: false,
203
+ surfaceId: surface.surfaceId,
204
+ onAction,
205
+ resolve: (value, scope) => resolveDynamic(value, surface.dataModel, scope),
206
+ write: (pointer, value) => writeBinding(surface.surfaceId, pointer, value),
207
+ remove: (pointer) => removeBinding(surface.surfaceId, pointer),
208
+ icons,
209
+ fallbackIcon,
210
+ labels: {
211
+ unsupported: unsupportedLabel,
212
+ blockedImage: blockedImageLabel,
213
+ pending: pendingLabel
214
+ }
215
+ };
216
+ surfaces.push({ surfaceId: surface.surfaceId, root, context });
217
+ }
218
+ return { surfaces, summaryIssues, allIssues };
219
+ });
220
+
221
+ // Fire onValidationError only when the issue list actually changes. Seeded
222
+ // with the empty signature so an issue-free first render stays silent.
223
+ let lastIssueSig = '[]';
224
+ $effect(() => {
225
+ const issues = view.allIssues;
226
+ const signature = JSON.stringify(
227
+ issues.map((issue) => [
228
+ issue.severity,
229
+ issue.code,
230
+ issue.surfaceId ?? '',
231
+ issue.path ?? '',
232
+ issue.message
233
+ ])
234
+ );
235
+ if (signature !== lastIssueSig) {
236
+ lastIssueSig = signature;
237
+ onValidationError?.(issues);
238
+ }
239
+ });
240
+ </script>
241
+
242
+ <!--
243
+ The render tree recurses through this self-referencing snippet rather than a
244
+ self-import inside A2UINode: a snippet that renders A2UINode and passes ITSELF
245
+ down as `renderChild` gives clean recursion without the circular component-type
246
+ resolution that self-imports trip in svelte-check.
247
+ -->
248
+ {#snippet renderNode(node: A2uiRenderNode, context: A2uiRenderContext)}
249
+ <A2UINode {node} {context} renderChild={renderNode} />
250
+ {/snippet}
251
+
252
+ <div class={rootClass} {...restProps}>
253
+ {#if view.summaryIssues.length}
254
+ <Alert intent="danger" title={errorTitle}>
255
+ <ul class={classes.errorList}>
256
+ {#each view.summaryIssues as issue, index (`${issue.code}#${index}`)}
257
+ <li>{issue.message}</li>
258
+ {/each}
259
+ </ul>
260
+ </Alert>
261
+ {/if}
262
+
263
+ {#each view.surfaces as surface (`${surface.surfaceId}#${generation}`)}
264
+ <div class={classes.surface}>
265
+ {#if surface.root}
266
+ {@render renderNode(surface.root, surface.context)}
267
+ {:else if !streamingProp}
268
+ <span class={classes.errorChip}>{errorTitle}</span>
269
+ {/if}
270
+ </div>
271
+ {/each}
272
+ </div>
@@ -0,0 +1,4 @@
1
+ import type { A2UIViewProps } from './index.js';
2
+ declare const A2UIView: import("svelte").Component<A2UIViewProps, {}, "">;
3
+ type A2UIView = ReturnType<typeof A2UIView>;
4
+ export default A2UIView;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Data-model primitives for the A2UI engine: RFC 6901 JSON Pointer get/set/delete
3
+ * and the `resolveDynamic` union resolver. Pure TS, no Svelte.
4
+ *
5
+ * Security posture (this file is on the untrusted-payload path):
6
+ * - `__proto__` / `constructor` / `prototype` are rejected at **every** pointer
7
+ * segment — prototype pollution never reaches the model.
8
+ * - Writes never merge or spread payload objects; they navigate and assign a
9
+ * single leaf, creating intermediate containers explicitly.
10
+ * - Array index deletion leaves a hole (length preserved), matching the spec's
11
+ * sparse-array semantics.
12
+ *
13
+ * A2UI convention (deviation from strict RFC 6901): the pointers `''` **and**
14
+ * `'/'` both address the whole model. Strict RFC treats `'/'` as the key `""`;
15
+ * A2UI's `updateDataModel` uses `'/'` to mean "the entire data model", and no
16
+ * A2UI payload ever addresses an empty-string key, so we honor the spec's own
17
+ * convention here for ergonomics.
18
+ */
19
+ import { type A2uiValidationIssue } from './a2ui.types.js';
20
+ /** Property names that must never be used as an object key on the write path. */
21
+ export declare const PROTO_KEYS: ReadonlySet<string>;
22
+ /**
23
+ * Read the value at `pointer`. `''` / `'/'` return the whole model. Missing
24
+ * paths, holes and type-mismatches resolve to `undefined` (never throw) — the
25
+ * spec renders an empty string / skeleton for unresolved bindings.
26
+ */
27
+ export declare function getAtPointer(model: unknown, pointer: string): unknown;
28
+ /**
29
+ * Write `value` at `pointer`, creating intermediate objects/arrays as needed.
30
+ * The document root cannot be replaced through this function (the processor
31
+ * assigns `dataModel` directly for whole-model updates); a root pointer returns
32
+ * `ok: false`. Prototype-pollution segments are rejected with an issue.
33
+ */
34
+ export declare function setAtPointer(model: unknown, pointer: string, value: unknown): {
35
+ ok: boolean;
36
+ issue?: A2uiValidationIssue;
37
+ };
38
+ /**
39
+ * Delete the leaf at `pointer`. Object keys are removed; array indices are
40
+ * `delete`d in place (leaving a hole, preserving length). No-ops for missing
41
+ * paths, non-container parents, and root pointers (the processor clears
42
+ * `dataModel` directly for whole-model deletes).
43
+ */
44
+ export declare function deleteAtPointer(model: unknown, pointer: string): void;
45
+ /**
46
+ * Resolve a Dynamic value union to a concrete value.
47
+ * - `{ path }` (data binding) → the pointed-at model value (relative paths are
48
+ * resolved against `scopePrefix`, the current template item).
49
+ * - `{ call, ... }` (function call) → `undefined` + a warning (unsupported in v1).
50
+ * - anything else (string/number/boolean/array/plain object) → returned as-is.
51
+ *
52
+ * Never throws; unresolved bindings yield `undefined` for graceful rendering.
53
+ */
54
+ export declare function resolveDynamic(value: unknown, model: unknown, scopePrefix: string | undefined): {
55
+ value: unknown;
56
+ issue?: A2uiValidationIssue;
57
+ };
58
+ /**
59
+ * Deep-clone JSON data for ingest into the model, stripping any own
60
+ * `__proto__`/`constructor`/`prototype` keys (harmless as own props, but never
61
+ * addressable, so we drop them). Functions/symbols are dropped. Used by the
62
+ * processor so that later two-way edits never mutate the source payload and no
63
+ * prototype key ever enters the model.
64
+ */
65
+ export declare function cloneData(value: unknown): unknown;
@@ -0,0 +1,283 @@
1
+ /**
2
+ * Data-model primitives for the A2UI engine: RFC 6901 JSON Pointer get/set/delete
3
+ * and the `resolveDynamic` union resolver. Pure TS, no Svelte.
4
+ *
5
+ * Security posture (this file is on the untrusted-payload path):
6
+ * - `__proto__` / `constructor` / `prototype` are rejected at **every** pointer
7
+ * segment — prototype pollution never reaches the model.
8
+ * - Writes never merge or spread payload objects; they navigate and assign a
9
+ * single leaf, creating intermediate containers explicitly.
10
+ * - Array index deletion leaves a hole (length preserved), matching the spec's
11
+ * sparse-array semantics.
12
+ *
13
+ * A2UI convention (deviation from strict RFC 6901): the pointers `''` **and**
14
+ * `'/'` both address the whole model. Strict RFC treats `'/'` as the key `""`;
15
+ * A2UI's `updateDataModel` uses `'/'` to mean "the entire data model", and no
16
+ * A2UI payload ever addresses an empty-string key, so we honor the spec's own
17
+ * convention here for ergonomics.
18
+ */
19
+ import { A2UI_ISSUE_CODES } from './a2ui.types.js';
20
+ /** Property names that must never be used as an object key on the write path. */
21
+ export const PROTO_KEYS = new Set(['__proto__', 'constructor', 'prototype']);
22
+ /** RFC 6901 token unescape: `~1`→`/` then `~0`→`~` (order is significant). */
23
+ function unescapeToken(token) {
24
+ return token.replaceAll('~1', '/').replaceAll('~0', '~');
25
+ }
26
+ /** Non-negative array index per RFC 6901 (`0` or `[1-9][0-9]*`); else `null`. */
27
+ function toIndex(segment) {
28
+ return /^(?:0|[1-9]\d*)$/.test(segment) ? Number(segment) : null;
29
+ }
30
+ /**
31
+ * Parse a pointer into decoded segments, guarding every segment against
32
+ * prototype-pollution keys. Absolute pointers start with `/`; anything else is
33
+ * treated as relative (segments split on `/`). `''` yields zero segments.
34
+ */
35
+ function parsePointer(pointer) {
36
+ if (typeof pointer !== 'string') {
37
+ return {
38
+ segments: [],
39
+ issue: {
40
+ severity: 'error',
41
+ code: A2UI_ISSUE_CODES.POINTER_ERROR,
42
+ message: 'Pointer must be a string'
43
+ }
44
+ };
45
+ }
46
+ let rawTokens;
47
+ if (pointer === '')
48
+ rawTokens = [];
49
+ else if (pointer.startsWith('/'))
50
+ rawTokens = pointer.slice(1).split('/');
51
+ else
52
+ rawTokens = pointer.split('/');
53
+ const segments = [];
54
+ for (const token of rawTokens) {
55
+ const segment = unescapeToken(token);
56
+ if (PROTO_KEYS.has(segment)) {
57
+ return {
58
+ segments: [],
59
+ issue: {
60
+ severity: 'error',
61
+ code: A2UI_ISSUE_CODES.PROTOTYPE_POLLUTION,
62
+ path: pointer,
63
+ message: `Prohibited property name "${segment}" in pointer "${pointer}"`
64
+ }
65
+ };
66
+ }
67
+ segments.push(segment);
68
+ }
69
+ return { segments };
70
+ }
71
+ function isContainer(value) {
72
+ return value !== null && typeof value === 'object';
73
+ }
74
+ /**
75
+ * Read the value at `pointer`. `''` / `'/'` return the whole model. Missing
76
+ * paths, holes and type-mismatches resolve to `undefined` (never throw) — the
77
+ * spec renders an empty string / skeleton for unresolved bindings.
78
+ */
79
+ export function getAtPointer(model, pointer) {
80
+ if (pointer === '' || pointer === '/')
81
+ return model;
82
+ const { segments, issue } = parsePointer(pointer);
83
+ if (issue)
84
+ return undefined;
85
+ let node = model;
86
+ for (const segment of segments) {
87
+ if (node == null)
88
+ return undefined;
89
+ if (Array.isArray(node)) {
90
+ const index = toIndex(segment);
91
+ node = index === null ? undefined : node[index];
92
+ }
93
+ else if (typeof node === 'object') {
94
+ node = node[segment];
95
+ }
96
+ else {
97
+ return undefined;
98
+ }
99
+ }
100
+ return node;
101
+ }
102
+ /**
103
+ * Write `value` at `pointer`, creating intermediate objects/arrays as needed.
104
+ * The document root cannot be replaced through this function (the processor
105
+ * assigns `dataModel` directly for whole-model updates); a root pointer returns
106
+ * `ok: false`. Prototype-pollution segments are rejected with an issue.
107
+ */
108
+ export function setAtPointer(model, pointer, value) {
109
+ if (pointer === '' || pointer === '/') {
110
+ return {
111
+ ok: false,
112
+ issue: {
113
+ severity: 'error',
114
+ code: A2UI_ISSUE_CODES.POINTER_ERROR,
115
+ path: pointer,
116
+ message: 'Cannot replace the document root through setAtPointer'
117
+ }
118
+ };
119
+ }
120
+ const { segments, issue } = parsePointer(pointer);
121
+ if (issue)
122
+ return { ok: false, issue };
123
+ if (segments.length === 0) {
124
+ return {
125
+ ok: false,
126
+ issue: {
127
+ severity: 'error',
128
+ code: A2UI_ISSUE_CODES.POINTER_ERROR,
129
+ path: pointer,
130
+ message: 'Cannot replace the document root through setAtPointer'
131
+ }
132
+ };
133
+ }
134
+ if (!isContainer(model)) {
135
+ return {
136
+ ok: false,
137
+ issue: {
138
+ severity: 'error',
139
+ code: A2UI_ISSUE_CODES.POINTER_ERROR,
140
+ path: pointer,
141
+ message: 'Cannot write into a non-object data model'
142
+ }
143
+ };
144
+ }
145
+ let node = model;
146
+ for (let i = 0; i < segments.length - 1; i++) {
147
+ const segment = segments[i];
148
+ const nextIsIndex = toIndex(segments[i + 1]) !== null || segments[i + 1] === '-';
149
+ if (Array.isArray(node)) {
150
+ const index = toIndex(segment);
151
+ if (index === null) {
152
+ return { ok: false, issue: pointerTypeIssue(pointer, segment) };
153
+ }
154
+ if (!isContainer(node[index]))
155
+ node[index] = nextIsIndex ? [] : {};
156
+ node = node[index];
157
+ }
158
+ else {
159
+ if (!isContainer(node[segment]))
160
+ node[segment] = nextIsIndex ? [] : {};
161
+ node = node[segment];
162
+ }
163
+ }
164
+ const last = segments[segments.length - 1];
165
+ if (Array.isArray(node)) {
166
+ const index = last === '-' ? node.length : toIndex(last);
167
+ if (index === null)
168
+ return { ok: false, issue: pointerTypeIssue(pointer, last) };
169
+ node[index] = value;
170
+ }
171
+ else {
172
+ node[last] = value;
173
+ }
174
+ return { ok: true };
175
+ }
176
+ function pointerTypeIssue(pointer, segment) {
177
+ return {
178
+ severity: 'error',
179
+ code: A2UI_ISSUE_CODES.POINTER_ERROR,
180
+ path: pointer,
181
+ message: `Segment "${segment}" is not a valid array index in pointer "${pointer}"`
182
+ };
183
+ }
184
+ /**
185
+ * Delete the leaf at `pointer`. Object keys are removed; array indices are
186
+ * `delete`d in place (leaving a hole, preserving length). No-ops for missing
187
+ * paths, non-container parents, and root pointers (the processor clears
188
+ * `dataModel` directly for whole-model deletes).
189
+ */
190
+ export function deleteAtPointer(model, pointer) {
191
+ if (pointer === '' || pointer === '/')
192
+ return;
193
+ const { segments, issue } = parsePointer(pointer);
194
+ if (issue || segments.length === 0)
195
+ return;
196
+ let node = model;
197
+ for (let i = 0; i < segments.length - 1; i++) {
198
+ if (!isContainer(node))
199
+ return;
200
+ if (Array.isArray(node)) {
201
+ const index = toIndex(segments[i]);
202
+ node = index === null ? undefined : node[index];
203
+ }
204
+ else {
205
+ node = node[segments[i]];
206
+ }
207
+ }
208
+ if (!isContainer(node))
209
+ return;
210
+ const last = segments[segments.length - 1];
211
+ if (Array.isArray(node)) {
212
+ const index = toIndex(last);
213
+ if (index !== null)
214
+ delete node[index];
215
+ }
216
+ else {
217
+ delete node[last];
218
+ }
219
+ }
220
+ /**
221
+ * Resolve a Dynamic value union to a concrete value.
222
+ * - `{ path }` (data binding) → the pointed-at model value (relative paths are
223
+ * resolved against `scopePrefix`, the current template item).
224
+ * - `{ call, ... }` (function call) → `undefined` + a warning (unsupported in v1).
225
+ * - anything else (string/number/boolean/array/plain object) → returned as-is.
226
+ *
227
+ * Never throws; unresolved bindings yield `undefined` for graceful rendering.
228
+ */
229
+ export function resolveDynamic(value, model, scopePrefix) {
230
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
231
+ const object = value;
232
+ if (typeof object.call === 'string') {
233
+ return {
234
+ value: undefined,
235
+ issue: {
236
+ severity: 'warning',
237
+ code: A2UI_ISSUE_CODES.FUNCTION_CALL_UNSUPPORTED,
238
+ message: `Function call "${object.call}" is not supported; resolved to undefined`
239
+ }
240
+ };
241
+ }
242
+ if (typeof object.path === 'string') {
243
+ const relative = object.path;
244
+ const full = relative.startsWith('/') ? relative : `${scopePrefix ?? ''}/${relative}`;
245
+ const { issue } = parsePointer(full);
246
+ if (issue)
247
+ return { value: undefined, issue };
248
+ return { value: getAtPointer(model, full) };
249
+ }
250
+ // A literal object argument (rare, e.g. a raw config object) — pass through.
251
+ return { value };
252
+ }
253
+ return { value };
254
+ }
255
+ /**
256
+ * Deep-clone JSON data for ingest into the model, stripping any own
257
+ * `__proto__`/`constructor`/`prototype` keys (harmless as own props, but never
258
+ * addressable, so we drop them). Functions/symbols are dropped. Used by the
259
+ * processor so that later two-way edits never mutate the source payload and no
260
+ * prototype key ever enters the model.
261
+ */
262
+ export function cloneData(value) {
263
+ if (Array.isArray(value)) {
264
+ const out = [];
265
+ for (let i = 0; i < value.length; i++) {
266
+ if (i in value)
267
+ out[i] = cloneData(value[i]);
268
+ }
269
+ return out;
270
+ }
271
+ if (value !== null && typeof value === 'object') {
272
+ const out = {};
273
+ for (const key of Object.keys(value)) {
274
+ if (PROTO_KEYS.has(key))
275
+ continue;
276
+ out[key] = cloneData(value[key]);
277
+ }
278
+ return out;
279
+ }
280
+ if (typeof value === 'function' || typeof value === 'symbol')
281
+ return undefined;
282
+ return value;
283
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * System-prompt generator for the A2UI subset. Renders the agent-facing catalog
3
+ * description straight from `A2UI_REGISTRY`, so the prompt and the validator can
4
+ * never drift (a drift-guard test asserts every component and enum value is
5
+ * covered). Pure TS, no Svelte — a server building the prompt has no DOM.
6
+ *
7
+ * Deliberately excludes any TRANSPORT section (how envelopes reach the client):
8
+ * that is app-specific and each app appends it (the chat-demo appends its fenced
9
+ * `a2ui` JSONL protocol).
10
+ */
11
+ /**
12
+ * Build the A2UI system prompt for this subset. Pass `catalogId` to override the
13
+ * default `createSurface` catalog identifier (the value must round-trip
14
+ * unchanged in every envelope).
15
+ */
16
+ export declare function a2uiSystemPrompt(options?: {
17
+ catalogId?: string;
18
+ }): string;