@threadplane/render 0.0.46

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/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # @threadplane/render
2
+
3
+ Generative UI for Angular. Agents emit structured JSON specs; this library renders them into Angular components you already own. Supports the Vercel `json-render` and Google A2UI v1-compatible protocols out of the box.
4
+
5
+ Part of [Threadplane](https://github.com/cacheplane/angular-agent-framework). MIT licensed.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @threadplane/render
11
+ ```
12
+
13
+ ## What it does
14
+
15
+ - **Spec-driven rendering** — agents return JSON; you map each node type to one of your Angular components via a registry
16
+ - **Two protocols supported** — Vercel `json-render` and Google A2UI v1-compatible
17
+ - **Per-component fallback API** — when a spec node has no registered component, you control what renders (and surface it to your observability layer)
18
+ - **Readiness gate** — holds renders until the surface is real, so users never see mystery partial UI
19
+ - **Streaming partial renders** — works with `@cacheplane/partial-json` to render progressive JSON as it streams
20
+
21
+ ## Documentation
22
+
23
+ - [Quickstart](https://threadplane.ai/docs/render/getting-started/quickstart)
24
+ - [Component registry](https://threadplane.ai/docs/render/guides/registry)
25
+ - [Fallback patterns](https://threadplane.ai/docs/render/guides/fallback)
26
+ - [A2UI v1-compatible protocol](https://threadplane.ai/docs/render/a2ui/overview)
27
+
28
+ ## License
29
+
30
+ MIT — free for any use. See [LICENSE](../../LICENSE).
@@ -0,0 +1,779 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, ChangeDetectionStrategy, Component, signal, Injectable, makeEnvironmentProviders, reflectComponentType, input, inject, Injector, DestroyRef, effect, computed, runInInjectionContext, output } from '@angular/core';
3
+ import { NgComponentOutlet } from '@angular/common';
4
+ import { resolveElementProps, evaluateVisibility, resolveBindings } from '@json-render/core';
5
+
6
+ // SPDX-License-Identifier: MIT
7
+ const RENDER_CONTEXT = new InjectionToken('RENDER_CONTEXT');
8
+
9
+ // SPDX-License-Identifier: MIT
10
+ const REPEAT_SCOPE = new InjectionToken('REPEAT_SCOPE');
11
+
12
+ // SPDX-License-Identifier: MIT
13
+ class DefaultFallbackComponent {
14
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: DefaultFallbackComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
15
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.1.6", type: DefaultFallbackComponent, isStandalone: true, selector: "render-default-fallback", ngImport: i0, template: `
16
+ <div class="render-default-fallback" role="status" aria-live="polite">
17
+ <div class="render-default-fallback__label">
18
+ <span aria-hidden="true">✨</span>
19
+ <span>Building UI…</span>
20
+ </div>
21
+ <div class="render-default-fallback__rows">
22
+ <div class="render-default-fallback__row"></div>
23
+ <div class="render-default-fallback__row"></div>
24
+ <div class="render-default-fallback__row"></div>
25
+ </div>
26
+ </div>
27
+ `, isInline: true, styles: [":host{display:block;width:100%}.render-default-fallback{border:1px solid var(--ngaf-chat-separator, #303540);border-radius:10px;padding:14px;background:var(--ngaf-chat-surface-alt, #1a1d23)}.render-default-fallback__label{font-size:12px;color:var(--ngaf-chat-text-muted, #9aa0aa);margin-bottom:10px;display:flex;align-items:center;gap:6px}.render-default-fallback__rows{display:flex;flex-direction:column;gap:8px}.render-default-fallback__row{height:10px;border-radius:5px;background:linear-gradient(90deg,var(--ngaf-chat-separator, #303540) 0%,color-mix(in srgb,var(--ngaf-chat-separator, #303540) 70%,transparent) 50%,var(--ngaf-chat-separator, #303540) 100%);background-size:200% 100%;animation:render-default-fallback-shimmer 1.4s ease-in-out infinite}.render-default-fallback__row:nth-child(1){width:70%}.render-default-fallback__row:nth-child(2){width:90%}.render-default-fallback__row:nth-child(3){width:50%}@keyframes render-default-fallback-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
28
+ }
29
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: DefaultFallbackComponent, decorators: [{
30
+ type: Component,
31
+ args: [{ selector: 'render-default-fallback', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
32
+ <div class="render-default-fallback" role="status" aria-live="polite">
33
+ <div class="render-default-fallback__label">
34
+ <span aria-hidden="true">✨</span>
35
+ <span>Building UI…</span>
36
+ </div>
37
+ <div class="render-default-fallback__rows">
38
+ <div class="render-default-fallback__row"></div>
39
+ <div class="render-default-fallback__row"></div>
40
+ <div class="render-default-fallback__row"></div>
41
+ </div>
42
+ </div>
43
+ `, styles: [":host{display:block;width:100%}.render-default-fallback{border:1px solid var(--ngaf-chat-separator, #303540);border-radius:10px;padding:14px;background:var(--ngaf-chat-surface-alt, #1a1d23)}.render-default-fallback__label{font-size:12px;color:var(--ngaf-chat-text-muted, #9aa0aa);margin-bottom:10px;display:flex;align-items:center;gap:6px}.render-default-fallback__rows{display:flex;flex-direction:column;gap:8px}.render-default-fallback__row{height:10px;border-radius:5px;background:linear-gradient(90deg,var(--ngaf-chat-separator, #303540) 0%,color-mix(in srgb,var(--ngaf-chat-separator, #303540) 70%,transparent) 50%,var(--ngaf-chat-separator, #303540) 100%);background-size:200% 100%;animation:render-default-fallback-shimmer 1.4s ease-in-out infinite}.render-default-fallback__row:nth-child(1){width:70%}.render-default-fallback__row:nth-child(2){width:90%}.render-default-fallback__row:nth-child(3){width:50%}@keyframes render-default-fallback-shimmer{0%{background-position:200% 0}to{background-position:-200% 0}}\n"] }]
44
+ }] });
45
+
46
+ function normalize(entry) {
47
+ // Bare Type — register with the default fallback.
48
+ if (typeof entry === 'function') {
49
+ return { component: entry, fallback: DefaultFallbackComponent };
50
+ }
51
+ // Object form — preserve component; use configured fallback or default.
52
+ return {
53
+ component: entry.component,
54
+ fallback: entry.fallback ?? DefaultFallbackComponent,
55
+ };
56
+ }
57
+ function defineAngularRegistry(componentMap) {
58
+ const map = new Map();
59
+ for (const [name, entry] of Object.entries(componentMap)) {
60
+ map.set(name, normalize(entry));
61
+ }
62
+ return {
63
+ get: (name) => map.get(name)?.component,
64
+ getFallback: (name) => map.get(name)?.fallback,
65
+ names: () => [...map.keys()],
66
+ };
67
+ }
68
+
69
+ // SPDX-License-Identifier: MIT
70
+ function parsePointer(path) {
71
+ if (!path || path === '/')
72
+ return [];
73
+ return path.split('/').filter((_, i) => i > 0).map(s => s.replace(/~1/g, '/').replace(/~0/g, '~'));
74
+ }
75
+ function getByPath(obj, segments) {
76
+ let current = obj;
77
+ for (const seg of segments) {
78
+ if (current == null || typeof current !== 'object')
79
+ return undefined;
80
+ current = current[seg];
81
+ }
82
+ return current;
83
+ }
84
+ function setByPath(obj, segments, value) {
85
+ if (segments.length === 0)
86
+ return value;
87
+ const [head, ...rest] = segments;
88
+ if (Array.isArray(obj)) {
89
+ const index = Number(head);
90
+ const clone = [...obj];
91
+ clone[index] = setByPath(clone[index], rest, value);
92
+ return clone;
93
+ }
94
+ const record = (obj != null && typeof obj === 'object')
95
+ ? { ...obj }
96
+ : {};
97
+ record[head] = setByPath(record[head], rest, value);
98
+ return record;
99
+ }
100
+ function signalStateStore(initialState = {}) {
101
+ const state = signal(initialState, ...(ngDevMode ? [{ debugName: "state" }] : []));
102
+ const listeners = new Set();
103
+ function notify() {
104
+ for (const listener of listeners)
105
+ listener();
106
+ }
107
+ return {
108
+ get(path) {
109
+ return getByPath(state(), parsePointer(path));
110
+ },
111
+ set(path, value) {
112
+ const segments = parsePointer(path);
113
+ const current = getByPath(state(), segments);
114
+ if (current === value)
115
+ return;
116
+ state.set(setByPath(state(), segments, value));
117
+ notify();
118
+ },
119
+ update(updates) {
120
+ let current = state();
121
+ let changed = false;
122
+ for (const [path, value] of Object.entries(updates)) {
123
+ const segments = parsePointer(path);
124
+ const existing = getByPath(current, segments);
125
+ if (existing !== value) {
126
+ current = setByPath(current, segments, value);
127
+ changed = true;
128
+ }
129
+ }
130
+ if (changed) {
131
+ state.set(current);
132
+ notify();
133
+ }
134
+ },
135
+ getSnapshot() {
136
+ return state();
137
+ },
138
+ subscribe(listener) {
139
+ listeners.add(listener);
140
+ return () => listeners.delete(listener);
141
+ },
142
+ };
143
+ }
144
+
145
+ // SPDX-License-Identifier: MIT
146
+ const RENDER_LIFECYCLE = new InjectionToken('RENDER_LIFECYCLE');
147
+
148
+ // SPDX-License-Identifier: MIT
149
+ /**
150
+ * Provided by `provideRender()` — opt-in. Scope follows the consumer's
151
+ * `provideRender` call (root-scoped by default, sub-tree if `provideRender`
152
+ * is in a sub-injector).
153
+ */
154
+ class RenderLifecycleService {
155
+ _firstMountAt = signal(null, ...(ngDevMode ? [{ debugName: "_firstMountAt" }] : []));
156
+ _mountCount = signal(0, ...(ngDevMode ? [{ debugName: "_mountCount" }] : []));
157
+ _lastMountAt = signal(null, ...(ngDevMode ? [{ debugName: "_lastMountAt" }] : []));
158
+ _lastStateChangeAt = signal(null, ...(ngDevMode ? [{ debugName: "_lastStateChangeAt" }] : []));
159
+ _lastHandlerInvokedAt = signal(null, ...(ngDevMode ? [{ debugName: "_lastHandlerInvokedAt" }] : []));
160
+ firstMountAt = this._firstMountAt.asReadonly();
161
+ mountCount = this._mountCount.asReadonly();
162
+ lastMountAt = this._lastMountAt.asReadonly();
163
+ lastStateChangeAt = this._lastStateChangeAt.asReadonly();
164
+ lastHandlerInvokedAt = this._lastHandlerInvokedAt.asReadonly();
165
+ notifyLifecycle(event) {
166
+ if (event.type === 'mounted') {
167
+ const now = Date.now();
168
+ if (this._firstMountAt() === null) {
169
+ this._firstMountAt.set({ kind: event.kind, elementType: event.elementType, at: now });
170
+ }
171
+ this._mountCount.update((c) => c + 1);
172
+ this._lastMountAt.set(now);
173
+ }
174
+ }
175
+ notifyStateChange() {
176
+ this._lastStateChangeAt.set(Date.now());
177
+ }
178
+ notifyHandlerInvoked(action) {
179
+ this._lastHandlerInvokedAt.set({ action, at: Date.now() });
180
+ }
181
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: RenderLifecycleService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
182
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: RenderLifecycleService });
183
+ }
184
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: RenderLifecycleService, decorators: [{
185
+ type: Injectable
186
+ }] });
187
+
188
+ // SPDX-License-Identifier: MIT
189
+ const RENDER_CONFIG = new InjectionToken('RENDER_CONFIG');
190
+ function provideRender(config) {
191
+ return makeEnvironmentProviders([
192
+ { provide: RENDER_CONFIG, useValue: config },
193
+ RenderLifecycleService,
194
+ { provide: RENDER_LIFECYCLE, useExisting: RenderLifecycleService },
195
+ ]);
196
+ }
197
+
198
+ function buildPropResolutionContext(store, repeatScope, functions) {
199
+ const ctx = {
200
+ stateModel: store.getSnapshot(),
201
+ };
202
+ if (repeatScope) {
203
+ ctx.repeatItem = repeatScope.item;
204
+ ctx.repeatIndex = repeatScope.index;
205
+ ctx.repeatBasePath = repeatScope.basePath;
206
+ }
207
+ if (functions) {
208
+ ctx.functions = functions;
209
+ }
210
+ return ctx;
211
+ }
212
+
213
+ // SPDX-License-Identifier: MIT
214
+ /** Magic prefix on `emit()` strings that catalog components use to
215
+ * write back to the data model (binding `path` and the new value). The
216
+ * render-element's emitFn intercepts this and writes via the state
217
+ * store, sidestepping the normal `el.on[event]` handler binding which
218
+ * the catalog components have no way to declare for arbitrary paths. */
219
+ const A2UI_DATAMODEL_PREFIX = 'a2ui:datamodel:';
220
+ /** Cache of declared input names per component class. NgComponentOutlet
221
+ * passes every key in its `inputs` prop to the target; Angular dev mode
222
+ * raises NG0303 for any input the component doesn't declare. We strip
223
+ * undeclared keys before mounting so simple view components (`StatCard`,
224
+ * `Container`, etc.) don't get spammed with framework-only inputs
225
+ * (`bindings`, `emit`, `loading`, `childKeys`, `spec`) they ignore. */
226
+ /** `null` means reflection failed (likely uncompiled / non-component) — in
227
+ * that case we pass inputs through unmodified rather than swallow them.
228
+ * An empty Set means the component genuinely declares zero inputs (e.g. a
229
+ * pure presentational fallback) and ALL keys should be dropped. */
230
+ const declaredInputsCache = new WeakMap();
231
+ function getDeclaredInputs(cls) {
232
+ if (declaredInputsCache.has(cls))
233
+ return declaredInputsCache.get(cls);
234
+ const meta = reflectComponentType(cls);
235
+ const result = meta ? new Set(meta.inputs.map(i => i.templateName)) : null;
236
+ declaredInputsCache.set(cls, result);
237
+ return result;
238
+ }
239
+ function filterInputsForClass(cls, inputs) {
240
+ if (!cls)
241
+ return inputs;
242
+ const declared = getDeclaredInputs(cls);
243
+ if (declared === null)
244
+ return inputs;
245
+ const out = {};
246
+ for (const [k, v] of Object.entries(inputs)) {
247
+ if (declared.has(k))
248
+ out[k] = v;
249
+ }
250
+ return out;
251
+ }
252
+ /** Best-effort string→typed coercion for datamodel writes. Catalog
253
+ * components emit raw string values; the underlying state may have
254
+ * been declared as number/boolean/array, and consumers reading the
255
+ * resolved props expect the correct type. */
256
+ function coerceValue(raw) {
257
+ if (raw === '')
258
+ return '';
259
+ if (raw === 'true')
260
+ return true;
261
+ if (raw === 'false')
262
+ return false;
263
+ // JSON-array passthrough (MultipleChoice emits stringified arrays)
264
+ if (raw.startsWith('[') && raw.endsWith(']')) {
265
+ try {
266
+ return JSON.parse(raw);
267
+ }
268
+ catch { /* fall through */ }
269
+ }
270
+ // Numeric — only if the entire string parses cleanly as a number
271
+ if (/^-?\d+(?:\.\d+)?$/.test(raw)) {
272
+ const n = Number(raw);
273
+ if (!Number.isNaN(n))
274
+ return n;
275
+ }
276
+ return raw;
277
+ }
278
+ /**
279
+ * Recursive element renderer.
280
+ *
281
+ * For each element key it:
282
+ * 1. Looks up the UIElement from spec.elements
283
+ * 2. Resolves the component class from the registry
284
+ * 3. Evaluates visibility
285
+ * 4. Resolves prop expressions and bindings
286
+ * 5. Renders via NgComponentOutlet with resolved inputs
287
+ *
288
+ * For elements with `repeat`, it iterates over the state array,
289
+ * creating a child Injector with RepeatScope for each item.
290
+ */
291
+ class RenderElementComponent {
292
+ elementKey = input.required(...(ngDevMode ? [{ debugName: "elementKey" }] : []));
293
+ spec = input.required(...(ngDevMode ? [{ debugName: "spec" }] : []));
294
+ ctx = inject(RENDER_CONTEXT);
295
+ repeatScope = inject(REPEAT_SCOPE, { optional: true });
296
+ parentInjector = inject(Injector);
297
+ destroyRef = inject(DestroyRef);
298
+ constructor() {
299
+ this.destroyRef.onDestroy(() => {
300
+ const el = this.element();
301
+ if (el && el['lifecycle'] && this.ctx.emitEvent) {
302
+ this.ctx.emitEvent({
303
+ type: 'lifecycle',
304
+ event: 'destroyed',
305
+ scope: 'element',
306
+ elementKey: this.elementKey(),
307
+ elementType: el.type,
308
+ });
309
+ }
310
+ });
311
+ // Latch mountedReal=true once the real component is selected. Lives in
312
+ // an effect (not the computed) because Angular forbids signal writes
313
+ // inside computed — they're for derivation only. Effects are the
314
+ // idiomatic place for "signal change → signal write" side effects.
315
+ effect(() => {
316
+ if (this.mountedReal())
317
+ return;
318
+ const el = this.element();
319
+ if (!el)
320
+ return;
321
+ // Only latch when notReady is false AND a real component is registered.
322
+ if (!this.notReady() && this.ctx.registry.get(el.type)) {
323
+ this.mountedReal.set(true);
324
+ }
325
+ });
326
+ }
327
+ ngOnInit() {
328
+ const el = this.element();
329
+ if (el && el['lifecycle'] && this.ctx.emitEvent) {
330
+ this.ctx.emitEvent({
331
+ type: 'lifecycle',
332
+ event: 'mounted',
333
+ scope: 'element',
334
+ elementKey: this.elementKey(),
335
+ elementType: el.type,
336
+ });
337
+ }
338
+ }
339
+ /** The UIElement definition from the spec. Only propagates when reference changes. */
340
+ element = computed(() => this.spec()?.elements?.[this.elementKey()], { ...(ngDevMode ? { debugName: "element" } : {}), equal: Object.is });
341
+ /** The Angular component class for this element type. */
342
+ componentClass = computed(() => {
343
+ const el = this.element();
344
+ if (!el)
345
+ return null;
346
+ return this.ctx.registry.get(el.type) ?? null;
347
+ }, ...(ngDevMode ? [{ debugName: "componentClass" }] : []));
348
+ /** Prop resolution context built from store + repeat scope. */
349
+ propCtx = computed(() => buildPropResolutionContext(this.ctx.store, this.repeatScope ?? undefined, this.ctx.functions), ...(ngDevMode ? [{ debugName: "propCtx" }] : []));
350
+ /** Once real mounts, never revert to fallback even if a state-bound
351
+ * prop later becomes undefined. Per-instance monotonic gate. */
352
+ mountedReal = signal(false, ...(ngDevMode ? [{ debugName: "mountedReal" }] : []));
353
+ /** True when ANY resolved prop value is undefined (i.e. a state
354
+ * binding points at a path the store hasn't populated). Framework-
355
+ * injected keys (bindings, emit, loading, childKeys, spec) are
356
+ * excluded — only consumer-resolved props matter for readiness. */
357
+ notReady = computed(() => {
358
+ if (this.mountedReal())
359
+ return false;
360
+ const el = this.element();
361
+ if (!el || !el.props)
362
+ return false;
363
+ const resolved = resolveElementProps(el.props, this.propCtx());
364
+ for (const v of Object.values(resolved)) {
365
+ if (v === undefined)
366
+ return true;
367
+ }
368
+ return false;
369
+ }, ...(ngDevMode ? [{ debugName: "notReady" }] : []));
370
+ /** Picks fallback or real based on notReady. The mountedReal latch is
371
+ * driven by a constructor effect (not this computed) — Angular forbids
372
+ * signal writes inside computed. */
373
+ mountClass = computed(() => {
374
+ const el = this.element();
375
+ if (!el)
376
+ return null;
377
+ const real = this.ctx.registry.get(el.type) ?? null;
378
+ if (this.notReady()) {
379
+ return this.ctx.registry.getFallback(el.type) ?? null;
380
+ }
381
+ return real;
382
+ }, ...(ngDevMode ? [{ debugName: "mountClass" }] : []));
383
+ /** Whether the element is visible (non-repeat path). */
384
+ visible = computed(() => {
385
+ const el = this.element();
386
+ if (!el)
387
+ return false;
388
+ if (this.mountClass() === null)
389
+ return false;
390
+ return evaluateVisibility(el.visible, this.propCtx());
391
+ }, ...(ngDevMode ? [{ debugName: "visible" }] : []));
392
+ /** Emit function that delegates to context handlers AND handles the
393
+ * canonical `a2ui:datamodel:<path>:<value>` write-back protocol that
394
+ * input components (TextField, MultipleChoice, CheckBox, Slider,
395
+ * DateTimeInput) emit when the user changes their value. The render
396
+ * lib's state store is the single source of truth for in-surface UI
397
+ * state; writing through it triggers re-render with the new value
398
+ * and re-evaluates any path-bound props (validation, computed
399
+ * visibility, etc.).
400
+ *
401
+ * The string format is `a2ui:datamodel:<path>:<value>` where:
402
+ * - `<path>` is a JSON-Pointer-style path (e.g. `/name`, `/form/email`)
403
+ * - `<value>` is the raw value rendered as a string. We attempt to
404
+ * coerce numeric and boolean literals back to their typed form
405
+ * so downstream consumers see correct types; arrays come through
406
+ * as JSON-stringified payloads (catalog components emit them via
407
+ * `JSON.stringify`).
408
+ */
409
+ emitFn = (event) => {
410
+ if (event.startsWith(A2UI_DATAMODEL_PREFIX)) {
411
+ this.applyDatamodelWrite(event);
412
+ return;
413
+ }
414
+ const el = this.element();
415
+ if (!el?.on)
416
+ return;
417
+ const binding = el.on[event];
418
+ if (!binding)
419
+ return;
420
+ const bindings = Array.isArray(binding) ? binding : [binding];
421
+ for (const b of bindings) {
422
+ const handler = this.ctx.handlers?.[b.action];
423
+ if (handler) {
424
+ runInInjectionContext(this.parentInjector, () => handler(b.params ?? {}));
425
+ }
426
+ }
427
+ };
428
+ applyDatamodelWrite(event) {
429
+ // Strip the prefix, then split path and value at the last `:` —
430
+ // path may itself contain `:` characters (rare but legal in
431
+ // JSON-Pointer per RFC 6901), and values can certainly contain
432
+ // them (URLs, time strings). Catalog components emit
433
+ // `a2ui:datamodel:<path>:<value>` where path is the binding's
434
+ // path-ref (usually starts with `/`); split the LAST `:` because
435
+ // the value is the only field guaranteed to come last.
436
+ const rest = event.slice(A2UI_DATAMODEL_PREFIX.length);
437
+ const lastColon = rest.lastIndexOf(':');
438
+ if (lastColon === -1)
439
+ return;
440
+ const path = rest.slice(0, lastColon);
441
+ const rawValue = rest.slice(lastColon + 1);
442
+ if (!path)
443
+ return;
444
+ const store = this.ctx.store;
445
+ if (!store)
446
+ return;
447
+ store.set(path, coerceValue(rawValue));
448
+ }
449
+ /** Resolved inputs for non-repeat elements. */
450
+ resolvedInputs = computed(() => {
451
+ const el = this.element();
452
+ if (!el)
453
+ return {};
454
+ const ctx = this.propCtx();
455
+ const resolved = resolveElementProps(el.props ?? {}, ctx);
456
+ const bindings = resolveBindings(el.props ?? {}, ctx);
457
+ return {
458
+ ...resolved,
459
+ bindings,
460
+ emit: this.emitFn,
461
+ loading: this.ctx.loading ?? false,
462
+ childKeys: el.children ?? [],
463
+ spec: this.spec(),
464
+ };
465
+ }, ...(ngDevMode ? [{ debugName: "resolvedInputs" }] : []));
466
+ /** `resolvedInputs` filtered down to keys the target component actually
467
+ * declares — silences NG0303 dev-mode warnings from framework-only
468
+ * inputs (bindings/emit/loading/childKeys/spec) passed to simple view
469
+ * components that don't declare them. */
470
+ filteredResolvedInputs = computed(() => filterInputsForClass(this.mountClass(), this.resolvedInputs()), ...(ngDevMode ? [{ debugName: "filteredResolvedInputs" }] : []));
471
+ // --- Repeat support ---
472
+ /** Items from the state array for repeat elements. */
473
+ repeatItems = computed(() => {
474
+ const el = this.element();
475
+ if (!el?.repeat)
476
+ return [];
477
+ const items = this.ctx.store.get(el.repeat.statePath);
478
+ return Array.isArray(items) ? items : [];
479
+ }, ...(ngDevMode ? [{ debugName: "repeatItems" }] : []));
480
+ /** One RepeatScope per repeat item, shared between injectors and inputs. */
481
+ repeatScopes = computed(() => {
482
+ const el = this.element();
483
+ if (!el?.repeat)
484
+ return [];
485
+ return this.repeatItems().map((item, index) => ({
486
+ item,
487
+ index,
488
+ basePath: `${el.repeat.statePath}/${index}`,
489
+ }));
490
+ }, ...(ngDevMode ? [{ debugName: "repeatScopes" }] : []));
491
+ /** One child Injector per repeat item, providing RepeatScope. */
492
+ repeatInjectors = computed(() => {
493
+ return this.repeatScopes().map(scope => Injector.create({
494
+ providers: [{ provide: REPEAT_SCOPE, useValue: scope }],
495
+ parent: this.parentInjector,
496
+ }));
497
+ }, ...(ngDevMode ? [{ debugName: "repeatInjectors" }] : []));
498
+ /** Resolved inputs for each repeat item. */
499
+ repeatInputs = computed(() => {
500
+ const el = this.element();
501
+ if (!el?.repeat)
502
+ return [];
503
+ return this.repeatScopes().map(scope => {
504
+ const ctx = buildPropResolutionContext(this.ctx.store, scope, this.ctx.functions);
505
+ const resolved = resolveElementProps(el.props ?? {}, ctx);
506
+ const bindings = resolveBindings(el.props ?? {}, ctx);
507
+ return {
508
+ ...resolved,
509
+ bindings,
510
+ emit: this.emitFn,
511
+ loading: this.ctx.loading ?? false,
512
+ childKeys: el.children ?? [],
513
+ spec: this.spec(),
514
+ };
515
+ });
516
+ }, ...(ngDevMode ? [{ debugName: "repeatInputs" }] : []));
517
+ /** `repeatInputs` filtered per-item to declared component inputs. */
518
+ filteredRepeatInputs = computed(() => {
519
+ const cls = this.mountClass();
520
+ return this.repeatInputs().map(inputs => filterInputsForClass(cls, inputs));
521
+ }, ...(ngDevMode ? [{ debugName: "filteredRepeatInputs" }] : []));
522
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: RenderElementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
523
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: RenderElementComponent, isStandalone: true, selector: "render-element", inputs: { elementKey: { classPropertyName: "elementKey", publicName: "elementKey", isSignal: true, isRequired: true, transformFunction: null }, spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
524
+ @if (!element()?.repeat) {
525
+ @if (visible()) {
526
+ <ng-container
527
+ *ngComponentOutlet="mountClass(); inputs: filteredResolvedInputs(); injector: parentInjector"
528
+ />
529
+ }
530
+ } @else {
531
+ @for (repeatInjector of repeatInjectors(); track $index) {
532
+ <ng-container
533
+ *ngComponentOutlet="mountClass(); inputs: filteredRepeatInputs()[$index]; injector: repeatInjector"
534
+ />
535
+ }
536
+ }
537
+ `, isInline: true, dependencies: [{ kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
538
+ }
539
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: RenderElementComponent, decorators: [{
540
+ type: Component,
541
+ args: [{
542
+ selector: 'render-element',
543
+ standalone: true,
544
+ imports: [NgComponentOutlet],
545
+ changeDetection: ChangeDetectionStrategy.OnPush,
546
+ template: `
547
+ @if (!element()?.repeat) {
548
+ @if (visible()) {
549
+ <ng-container
550
+ *ngComponentOutlet="mountClass(); inputs: filteredResolvedInputs(); injector: parentInjector"
551
+ />
552
+ }
553
+ } @else {
554
+ @for (repeatInjector of repeatInjectors(); track $index) {
555
+ <ng-container
556
+ *ngComponentOutlet="mountClass(); inputs: filteredRepeatInputs()[$index]; injector: repeatInjector"
557
+ />
558
+ }
559
+ }
560
+ `,
561
+ }]
562
+ }], ctorParameters: () => [], propDecorators: { elementKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "elementKey", required: true }] }], spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: true }] }] } });
563
+
564
+ // SPDX-License-Identifier: MIT
565
+ /**
566
+ * Top-level entry point for rendering a json-render spec.
567
+ *
568
+ * Accepts the spec, registry, store, functions, handlers, and loading
569
+ * as inputs. Provides `RENDER_CONTEXT` to child `RenderElementComponent`
570
+ * instances via `viewProviders`.
571
+ *
572
+ * Falls back to `RENDER_CONFIG` (from `provideRender()`) for registry
573
+ * and store defaults when inputs are not provided.
574
+ *
575
+ * @example
576
+ * ```html
577
+ * <render-spec [spec]="spec()" [registry]="registry" [store]="store" />
578
+ * ```
579
+ */
580
+ class RenderSpecComponent {
581
+ spec = input(null, ...(ngDevMode ? [{ debugName: "spec" }] : []));
582
+ registry = input(undefined, ...(ngDevMode ? [{ debugName: "registry" }] : []));
583
+ store = input(undefined, ...(ngDevMode ? [{ debugName: "store" }] : []));
584
+ functions = input(undefined, ...(ngDevMode ? [{ debugName: "functions" }] : []));
585
+ handlers = input(undefined, ...(ngDevMode ? [{ debugName: "handlers" }] : []));
586
+ loading = input(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
587
+ events = output();
588
+ config = inject(RENDER_CONFIG, { optional: true });
589
+ destroyRef = inject(DestroyRef);
590
+ lifecycle = inject(RenderLifecycleService, { optional: true });
591
+ /** Internal store, lazily created once and reused across spec changes. */
592
+ _internalStore;
593
+ getOrCreateInternalStore() {
594
+ if (!this._internalStore) {
595
+ this._internalStore = signalStateStore(this.spec()?.state ?? {});
596
+ }
597
+ return this._internalStore;
598
+ }
599
+ /** Resolved store: input > config > internal (from spec.state). */
600
+ resolvedStore = computed(() => {
601
+ const inputStore = this.store();
602
+ if (inputStore)
603
+ return inputStore;
604
+ const configStore = this.config?.store;
605
+ if (configStore)
606
+ return configStore;
607
+ return this.getOrCreateInternalStore();
608
+ }, ...(ngDevMode ? [{ debugName: "resolvedStore" }] : []));
609
+ /** Resolved registry: input > config. */
610
+ resolvedRegistry = computed(() => {
611
+ const inputRegistry = this.registry();
612
+ if (inputRegistry)
613
+ return inputRegistry;
614
+ const configRegistry = this.config?.registry;
615
+ if (configRegistry)
616
+ return configRegistry;
617
+ // Fallback: empty registry
618
+ return { get: () => undefined, getFallback: () => undefined, names: () => [] };
619
+ }, ...(ngDevMode ? [{ debugName: "resolvedRegistry" }] : []));
620
+ /** Wraps input handlers to emit RenderHandlerEvent after execution. */
621
+ wrappedHandlers = computed(() => {
622
+ const inputHandlers = this.handlers() ?? this.config?.handlers;
623
+ if (!inputHandlers)
624
+ return undefined;
625
+ const wrapped = {};
626
+ for (const [name, handler] of Object.entries(inputHandlers)) {
627
+ wrapped[name] = (params) => {
628
+ const result = handler(params);
629
+ if (result instanceof Promise) {
630
+ result.then((r) => {
631
+ this.emitTapped({ type: 'handler', action: name, params, result: r });
632
+ }, () => {
633
+ this.emitTapped({ type: 'handler', action: name, params, result: undefined });
634
+ });
635
+ }
636
+ else {
637
+ this.emitTapped({ type: 'handler', action: name, params, result });
638
+ }
639
+ return result;
640
+ };
641
+ }
642
+ return wrapped;
643
+ }, ...(ngDevMode ? [{ debugName: "wrappedHandlers" }] : []));
644
+ /** Emits a RenderEvent through the events output and notifies the
645
+ * lifecycle service (single tap point — all events flow through here). */
646
+ emitTapped = (event) => {
647
+ this.events.emit(event);
648
+ if (!this.lifecycle)
649
+ return;
650
+ switch (event.type) {
651
+ case 'lifecycle':
652
+ this.lifecycle.notifyLifecycle({
653
+ kind: event.scope,
654
+ type: event.event,
655
+ elementType: event.elementType,
656
+ });
657
+ break;
658
+ case 'stateChange':
659
+ this.lifecycle.notifyStateChange();
660
+ break;
661
+ case 'handler':
662
+ this.lifecycle.notifyHandlerInvoked(event.action);
663
+ break;
664
+ }
665
+ };
666
+ /** Emits a RenderEvent through the events output. */
667
+ emitEvent = (event) => {
668
+ this.emitTapped(event);
669
+ };
670
+ /** The RenderContext provided to children via viewProviders. */
671
+ _context = computed(() => ({
672
+ registry: this.resolvedRegistry(),
673
+ store: this.resolvedStore(),
674
+ functions: this.functions() ?? this.config?.functions,
675
+ handlers: this.wrappedHandlers(),
676
+ emitEvent: this.emitEvent,
677
+ loading: this.loading(),
678
+ }), ...(ngDevMode ? [{ debugName: "_context" }] : []));
679
+ constructor() {
680
+ // Subscribe to store changes and emit state change events
681
+ effect(() => {
682
+ const store = this.resolvedStore();
683
+ const unsub = store.subscribe(() => {
684
+ const snapshot = store.getSnapshot();
685
+ this.emitTapped({
686
+ type: 'stateChange',
687
+ path: '/',
688
+ value: snapshot,
689
+ snapshot,
690
+ });
691
+ });
692
+ this.destroyRef.onDestroy(unsub);
693
+ });
694
+ this.destroyRef.onDestroy(() => {
695
+ this.emitTapped({ type: 'lifecycle', event: 'destroyed', scope: 'spec' });
696
+ });
697
+ }
698
+ ngOnInit() {
699
+ this.emitTapped({ type: 'lifecycle', event: 'mounted', scope: 'spec' });
700
+ }
701
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: RenderSpecComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
702
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.6", type: RenderSpecComponent, isStandalone: true, selector: "render-spec", inputs: { spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null }, registry: { classPropertyName: "registry", publicName: "registry", isSignal: true, isRequired: false, transformFunction: null }, store: { classPropertyName: "store", publicName: "store", isSignal: true, isRequired: false, transformFunction: null }, functions: { classPropertyName: "functions", publicName: "functions", isSignal: true, isRequired: false, transformFunction: null }, handlers: { classPropertyName: "handlers", publicName: "handlers", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { events: "events" }, ngImport: i0, template: `
703
+ @if (spec()?.root; as rootKey) {
704
+ <render-element [elementKey]="rootKey" [spec]="spec()!" />
705
+ }
706
+ `, isInline: true, dependencies: [{ kind: "component", type: RenderElementComponent, selector: "render-element", inputs: ["elementKey", "spec"] }], viewProviders: [
707
+ {
708
+ provide: RENDER_CONTEXT,
709
+ useFactory: () => inject(RenderSpecComponent)._context(),
710
+ },
711
+ ], changeDetection: i0.ChangeDetectionStrategy.OnPush });
712
+ }
713
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.6", ngImport: i0, type: RenderSpecComponent, decorators: [{
714
+ type: Component,
715
+ args: [{
716
+ selector: 'render-spec',
717
+ standalone: true,
718
+ imports: [RenderElementComponent],
719
+ changeDetection: ChangeDetectionStrategy.OnPush,
720
+ viewProviders: [
721
+ {
722
+ provide: RENDER_CONTEXT,
723
+ useFactory: () => inject(RenderSpecComponent)._context(),
724
+ },
725
+ ],
726
+ template: `
727
+ @if (spec()?.root; as rootKey) {
728
+ <render-element [elementKey]="rootKey" [spec]="spec()!" />
729
+ }
730
+ `,
731
+ }]
732
+ }], ctorParameters: () => [], propDecorators: { spec: [{ type: i0.Input, args: [{ isSignal: true, alias: "spec", required: false }] }], registry: [{ type: i0.Input, args: [{ isSignal: true, alias: "registry", required: false }] }], store: [{ type: i0.Input, args: [{ isSignal: true, alias: "store", required: false }] }], functions: [{ type: i0.Input, args: [{ isSignal: true, alias: "functions", required: false }] }], handlers: [{ type: i0.Input, args: [{ isSignal: true, alias: "handlers", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], events: [{ type: i0.Output, args: ["events"] }] } });
733
+
734
+ /**
735
+ * Creates a view registry from a name → component map.
736
+ */
737
+ function views(map) {
738
+ return Object.freeze({ ...map });
739
+ }
740
+ /**
741
+ * Adds views to a registry without overwriting existing entries.
742
+ * New keys are added; keys that already exist in `base` are preserved.
743
+ */
744
+ function withViews(base, additions) {
745
+ return Object.freeze({ ...additions, ...base });
746
+ }
747
+ /**
748
+ * Removes views from a registry by name.
749
+ */
750
+ function withoutViews(base, ...names) {
751
+ const result = { ...base };
752
+ for (const name of names)
753
+ delete result[name];
754
+ return Object.freeze(result);
755
+ }
756
+ /**
757
+ * Converts a ViewRegistry to an AngularRegistry for use with RenderSpecComponent.
758
+ */
759
+ function toRenderRegistry(registry) {
760
+ return defineAngularRegistry(registry);
761
+ }
762
+
763
+ // SPDX-License-Identifier: MIT
764
+ const VIEW_REGISTRY = new InjectionToken('VIEW_REGISTRY');
765
+ function provideViews(registry) {
766
+ return makeEnvironmentProviders([
767
+ { provide: VIEW_REGISTRY, useValue: registry },
768
+ ]);
769
+ }
770
+
771
+ // SPDX-License-Identifier: MIT
772
+ // Contexts
773
+
774
+ /**
775
+ * Generated bundle index. Do not edit.
776
+ */
777
+
778
+ export { DefaultFallbackComponent, RENDER_CONFIG, RENDER_CONTEXT, RENDER_LIFECYCLE, REPEAT_SCOPE, RenderElementComponent, RenderSpecComponent, VIEW_REGISTRY, defineAngularRegistry, provideRender, provideViews, signalStateStore, toRenderRegistry, views, withViews, withoutViews };
779
+ //# sourceMappingURL=threadplane-render.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"threadplane-render.mjs","sources":["../../../../libs/render/src/lib/contexts/render-context.ts","../../../../libs/render/src/lib/contexts/repeat-scope.ts","../../../../libs/render/src/lib/default-fallback.component.ts","../../../../libs/render/src/lib/define-angular-registry.ts","../../../../libs/render/src/lib/signal-state-store.ts","../../../../libs/render/src/lib/lifecycle.ts","../../../../libs/render/src/lib/render-lifecycle.service.ts","../../../../libs/render/src/lib/provide-render.ts","../../../../libs/render/src/lib/internals/prop-signal.ts","../../../../libs/render/src/lib/render-element.component.ts","../../../../libs/render/src/lib/render-spec.component.ts","../../../../libs/render/src/lib/views.ts","../../../../libs/render/src/lib/provide-views.ts","../../../../libs/render/src/public-api.ts","../../../../libs/render/src/threadplane-render.ts"],"sourcesContent":["// SPDX-License-Identifier: MIT\nimport { InjectionToken } from '@angular/core';\nimport type { StateStore, ComputedFunction } from '@json-render/core';\nimport type { AngularRegistry } from '../render.types';\nimport type { RenderEvent } from '../render-event';\n\nexport interface RenderContext {\n registry: AngularRegistry;\n store: StateStore;\n functions?: Record<string, ComputedFunction>;\n handlers?: Record<string, (params: Record<string, unknown>) => unknown | Promise<unknown>>;\n emitEvent?: (event: RenderEvent) => void;\n loading?: boolean;\n}\n\nexport const RENDER_CONTEXT = new InjectionToken<RenderContext>('RENDER_CONTEXT');\n","// SPDX-License-Identifier: MIT\nimport { InjectionToken } from '@angular/core';\n\nexport interface RepeatScope {\n item: unknown;\n index: number;\n basePath: string;\n}\n\nexport const REPEAT_SCOPE = new InjectionToken<RepeatScope>('REPEAT_SCOPE');\n","// SPDX-License-Identifier: MIT\nimport { Component, ChangeDetectionStrategy } from '@angular/core';\n\n@Component({\n selector: 'render-default-fallback',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n styles: [`\n :host { display: block; width: 100%; }\n .render-default-fallback {\n border: 1px solid var(--ngaf-chat-separator, #303540);\n border-radius: 10px;\n padding: 14px;\n background: var(--ngaf-chat-surface-alt, #1a1d23);\n }\n .render-default-fallback__label {\n font-size: 12px;\n color: var(--ngaf-chat-text-muted, #9aa0aa);\n margin-bottom: 10px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n .render-default-fallback__rows {\n display: flex; flex-direction: column; gap: 8px;\n }\n .render-default-fallback__row {\n height: 10px; border-radius: 5px;\n background: linear-gradient(\n 90deg,\n var(--ngaf-chat-separator, #303540) 0%,\n color-mix(in srgb, var(--ngaf-chat-separator, #303540) 70%, transparent) 50%,\n var(--ngaf-chat-separator, #303540) 100%\n );\n background-size: 200% 100%;\n animation: render-default-fallback-shimmer 1.4s ease-in-out infinite;\n }\n .render-default-fallback__row:nth-child(1) { width: 70%; }\n .render-default-fallback__row:nth-child(2) { width: 90%; }\n .render-default-fallback__row:nth-child(3) { width: 50%; }\n @keyframes render-default-fallback-shimmer {\n 0% { background-position: 200% 0; }\n 100% { background-position: -200% 0; }\n }\n `],\n template: `\n <div class=\"render-default-fallback\" role=\"status\" aria-live=\"polite\">\n <div class=\"render-default-fallback__label\">\n <span aria-hidden=\"true\">✨</span>\n <span>Building UI…</span>\n </div>\n <div class=\"render-default-fallback__rows\">\n <div class=\"render-default-fallback__row\"></div>\n <div class=\"render-default-fallback__row\"></div>\n <div class=\"render-default-fallback__row\"></div>\n </div>\n </div>\n `,\n})\nexport class DefaultFallbackComponent {}\n","// SPDX-License-Identifier: MIT\nimport { Type } from '@angular/core';\nimport type { AngularRegistry, RenderViewEntry } from './render.types';\nimport { DefaultFallbackComponent } from './default-fallback.component';\n\ntype RegistryInput = Record<string, Type<unknown> | RenderViewEntry>;\n\ninterface NormalizedEntry {\n component: Type<unknown>;\n fallback: Type<unknown>;\n}\n\nfunction normalize(entry: Type<unknown> | RenderViewEntry): NormalizedEntry {\n // Bare Type — register with the default fallback.\n if (typeof entry === 'function') {\n return { component: entry, fallback: DefaultFallbackComponent };\n }\n // Object form — preserve component; use configured fallback or default.\n return {\n component: entry.component,\n fallback: entry.fallback ?? DefaultFallbackComponent,\n };\n}\n\nexport function defineAngularRegistry(componentMap: RegistryInput): AngularRegistry {\n const map = new Map<string, NormalizedEntry>();\n for (const [name, entry] of Object.entries(componentMap)) {\n map.set(name, normalize(entry));\n }\n return {\n get: (name: string) => map.get(name)?.component,\n getFallback: (name: string) => map.get(name)?.fallback,\n names: () => [...map.keys()],\n };\n}\n","// SPDX-License-Identifier: MIT\nimport { signal } from '@angular/core';\nimport type { StateStore, StateModel } from '@json-render/core';\n\nfunction parsePointer(path: string): string[] {\n if (!path || path === '/') return [];\n return path.split('/').filter((_, i) => i > 0).map(s => s.replace(/~1/g, '/').replace(/~0/g, '~'));\n}\n\nfunction getByPath(obj: unknown, segments: string[]): unknown {\n let current: unknown = obj;\n for (const seg of segments) {\n if (current == null || typeof current !== 'object') return undefined;\n current = (current as Record<string, unknown>)[seg];\n }\n return current;\n}\n\nfunction setByPath(obj: unknown, segments: string[], value: unknown): unknown {\n if (segments.length === 0) return value;\n const [head, ...rest] = segments;\n\n if (Array.isArray(obj)) {\n const index = Number(head);\n const clone = [...obj];\n clone[index] = setByPath(clone[index], rest, value);\n return clone;\n }\n\n const record = (obj != null && typeof obj === 'object')\n ? { ...obj as Record<string, unknown> }\n : {} as Record<string, unknown>;\n record[head] = setByPath(record[head], rest, value);\n return record;\n}\n\nexport function signalStateStore(initialState: StateModel = {}): StateStore {\n const state = signal<StateModel>(initialState);\n const listeners = new Set<() => void>();\n\n function notify(): void {\n for (const listener of listeners) listener();\n }\n\n return {\n get(path: string): unknown {\n return getByPath(state(), parsePointer(path));\n },\n set(path: string, value: unknown): void {\n const segments = parsePointer(path);\n const current = getByPath(state(), segments);\n if (current === value) return;\n state.set(setByPath(state(), segments, value) as StateModel);\n notify();\n },\n update(updates: Record<string, unknown>): void {\n let current = state();\n let changed = false;\n for (const [path, value] of Object.entries(updates)) {\n const segments = parsePointer(path);\n const existing = getByPath(current, segments);\n if (existing !== value) {\n current = setByPath(current, segments, value) as StateModel;\n changed = true;\n }\n }\n if (changed) {\n state.set(current);\n notify();\n }\n },\n getSnapshot(): StateModel {\n return state();\n },\n subscribe(listener: () => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n","// SPDX-License-Identifier: MIT\nimport { InjectionToken, Signal } from '@angular/core';\n\nexport interface RenderLifecycle {\n /** First mount event in this render context. Sticky — does not reset. */\n readonly firstMountAt: Signal<{ kind: 'spec' | 'element'; elementType?: string; at: number } | null>;\n /** Total mount count since render context started. */\n readonly mountCount: Signal<number>;\n /** Epoch ms of the most recent mount event. */\n readonly lastMountAt: Signal<number | null>;\n /** Epoch ms of the most recent state-change event. */\n readonly lastStateChangeAt: Signal<number | null>;\n /** Most recent handler invocation. */\n readonly lastHandlerInvokedAt: Signal<{ action: string; at: number } | null>;\n}\n\nexport const RENDER_LIFECYCLE = new InjectionToken<RenderLifecycle>('RENDER_LIFECYCLE');\n","// SPDX-License-Identifier: MIT\nimport { Injectable, signal } from '@angular/core';\nimport type { RenderLifecycle } from './lifecycle';\n\n/**\n * Provided by `provideRender()` — opt-in. Scope follows the consumer's\n * `provideRender` call (root-scoped by default, sub-tree if `provideRender`\n * is in a sub-injector).\n */\n@Injectable()\nexport class RenderLifecycleService implements RenderLifecycle {\n private _firstMountAt = signal<{ kind: 'spec' | 'element'; elementType?: string; at: number } | null>(null);\n private _mountCount = signal(0);\n private _lastMountAt = signal<number | null>(null);\n private _lastStateChangeAt = signal<number | null>(null);\n private _lastHandlerInvokedAt = signal<{ action: string; at: number } | null>(null);\n\n readonly firstMountAt = this._firstMountAt.asReadonly();\n readonly mountCount = this._mountCount.asReadonly();\n readonly lastMountAt = this._lastMountAt.asReadonly();\n readonly lastStateChangeAt = this._lastStateChangeAt.asReadonly();\n readonly lastHandlerInvokedAt = this._lastHandlerInvokedAt.asReadonly();\n\n notifyLifecycle(event: { kind: 'spec' | 'element'; type: 'mounted' | 'destroyed'; elementType?: string }): void {\n if (event.type === 'mounted') {\n const now = Date.now();\n if (this._firstMountAt() === null) {\n this._firstMountAt.set({ kind: event.kind, elementType: event.elementType, at: now });\n }\n this._mountCount.update((c) => c + 1);\n this._lastMountAt.set(now);\n }\n }\n\n notifyStateChange(): void {\n this._lastStateChangeAt.set(Date.now());\n }\n\n notifyHandlerInvoked(action: string): void {\n this._lastHandlerInvokedAt.set({ action, at: Date.now() });\n }\n}\n","// SPDX-License-Identifier: MIT\nimport { InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport type { RenderConfig } from './render.types';\nimport { RENDER_LIFECYCLE } from './lifecycle';\nimport { RenderLifecycleService } from './render-lifecycle.service';\n\nexport const RENDER_CONFIG = new InjectionToken<RenderConfig>('RENDER_CONFIG');\n\nexport function provideRender(config: RenderConfig) {\n return makeEnvironmentProviders([\n { provide: RENDER_CONFIG, useValue: config },\n RenderLifecycleService,\n { provide: RENDER_LIFECYCLE, useExisting: RenderLifecycleService },\n ]);\n}\n","// SPDX-License-Identifier: MIT\nimport type { StateStore, ComputedFunction, PropResolutionContext } from '@json-render/core';\nimport type { RepeatScope } from '../contexts/repeat-scope';\n\nexport function buildPropResolutionContext(\n store: StateStore,\n repeatScope?: RepeatScope,\n functions?: Record<string, ComputedFunction>,\n): PropResolutionContext {\n const ctx: PropResolutionContext = {\n stateModel: store.getSnapshot(),\n };\n if (repeatScope) {\n ctx.repeatItem = repeatScope.item;\n ctx.repeatIndex = repeatScope.index;\n ctx.repeatBasePath = repeatScope.basePath;\n }\n if (functions) {\n ctx.functions = functions;\n }\n return ctx;\n}\n","// SPDX-License-Identifier: MIT\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n effect,\n inject,\n Injector,\n input,\n OnInit,\n reflectComponentType,\n runInInjectionContext,\n signal,\n type Signal,\n type Type,\n} from '@angular/core';\nimport { NgComponentOutlet } from '@angular/common';\nimport {\n evaluateVisibility,\n resolveBindings,\n resolveElementProps,\n} from '@json-render/core';\nimport type { Spec, UIElement } from '@json-render/core';\n\nimport { RENDER_CONTEXT } from './contexts/render-context';\nimport { REPEAT_SCOPE } from './contexts/repeat-scope';\nimport type { RepeatScope } from './contexts/repeat-scope';\nimport { buildPropResolutionContext } from './internals/prop-signal';\nimport type { AngularComponentRenderer } from './render.types';\n\n/** Magic prefix on `emit()` strings that catalog components use to\n * write back to the data model (binding `path` and the new value). The\n * render-element's emitFn intercepts this and writes via the state\n * store, sidestepping the normal `el.on[event]` handler binding which\n * the catalog components have no way to declare for arbitrary paths. */\nconst A2UI_DATAMODEL_PREFIX = 'a2ui:datamodel:';\n\n/** Cache of declared input names per component class. NgComponentOutlet\n * passes every key in its `inputs` prop to the target; Angular dev mode\n * raises NG0303 for any input the component doesn't declare. We strip\n * undeclared keys before mounting so simple view components (`StatCard`,\n * `Container`, etc.) don't get spammed with framework-only inputs\n * (`bindings`, `emit`, `loading`, `childKeys`, `spec`) they ignore. */\n/** `null` means reflection failed (likely uncompiled / non-component) — in\n * that case we pass inputs through unmodified rather than swallow them.\n * An empty Set means the component genuinely declares zero inputs (e.g. a\n * pure presentational fallback) and ALL keys should be dropped. */\nconst declaredInputsCache = new WeakMap<Type<unknown>, Set<string> | null>();\nfunction getDeclaredInputs(cls: Type<unknown>): Set<string> | null {\n if (declaredInputsCache.has(cls)) return declaredInputsCache.get(cls)!;\n const meta = reflectComponentType(cls);\n const result = meta ? new Set<string>(meta.inputs.map(i => i.templateName)) : null;\n declaredInputsCache.set(cls, result);\n return result;\n}\nfunction filterInputsForClass(\n cls: Type<unknown> | null,\n inputs: Record<string, unknown>,\n): Record<string, unknown> {\n if (!cls) return inputs;\n const declared = getDeclaredInputs(cls);\n if (declared === null) return inputs;\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(inputs)) {\n if (declared.has(k)) out[k] = v;\n }\n return out;\n}\n\n/** Best-effort string→typed coercion for datamodel writes. Catalog\n * components emit raw string values; the underlying state may have\n * been declared as number/boolean/array, and consumers reading the\n * resolved props expect the correct type. */\nfunction coerceValue(raw: string): unknown {\n if (raw === '') return '';\n if (raw === 'true') return true;\n if (raw === 'false') return false;\n // JSON-array passthrough (MultipleChoice emits stringified arrays)\n if (raw.startsWith('[') && raw.endsWith(']')) {\n try { return JSON.parse(raw); } catch { /* fall through */ }\n }\n // Numeric — only if the entire string parses cleanly as a number\n if (/^-?\\d+(?:\\.\\d+)?$/.test(raw)) {\n const n = Number(raw);\n if (!Number.isNaN(n)) return n;\n }\n return raw;\n}\n\n/**\n * Recursive element renderer.\n *\n * For each element key it:\n * 1. Looks up the UIElement from spec.elements\n * 2. Resolves the component class from the registry\n * 3. Evaluates visibility\n * 4. Resolves prop expressions and bindings\n * 5. Renders via NgComponentOutlet with resolved inputs\n *\n * For elements with `repeat`, it iterates over the state array,\n * creating a child Injector with RepeatScope for each item.\n */\n@Component({\n selector: 'render-element',\n standalone: true,\n imports: [NgComponentOutlet],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n @if (!element()?.repeat) {\n @if (visible()) {\n <ng-container\n *ngComponentOutlet=\"mountClass(); inputs: filteredResolvedInputs(); injector: parentInjector\"\n />\n }\n } @else {\n @for (repeatInjector of repeatInjectors(); track $index) {\n <ng-container\n *ngComponentOutlet=\"mountClass(); inputs: filteredRepeatInputs()[$index]; injector: repeatInjector\"\n />\n }\n }\n `,\n})\nexport class RenderElementComponent implements OnInit {\n readonly elementKey = input.required<string>();\n readonly spec = input.required<Spec>();\n\n private readonly ctx = inject(RENDER_CONTEXT);\n private readonly repeatScope = inject(REPEAT_SCOPE, { optional: true });\n readonly parentInjector = inject(Injector);\n private readonly destroyRef = inject(DestroyRef);\n\n constructor() {\n this.destroyRef.onDestroy(() => {\n const el = this.element();\n if (el && (el as any)['lifecycle'] && this.ctx.emitEvent) {\n this.ctx.emitEvent({\n type: 'lifecycle',\n event: 'destroyed',\n scope: 'element',\n elementKey: this.elementKey(),\n elementType: el.type,\n });\n }\n });\n\n // Latch mountedReal=true once the real component is selected. Lives in\n // an effect (not the computed) because Angular forbids signal writes\n // inside computed — they're for derivation only. Effects are the\n // idiomatic place for \"signal change → signal write\" side effects.\n effect(() => {\n if (this.mountedReal()) return;\n const el = this.element();\n if (!el) return;\n // Only latch when notReady is false AND a real component is registered.\n if (!this.notReady() && this.ctx.registry.get(el.type)) {\n this.mountedReal.set(true);\n }\n });\n }\n\n ngOnInit(): void {\n const el = this.element();\n if (el && (el as any)['lifecycle'] && this.ctx.emitEvent) {\n this.ctx.emitEvent({\n type: 'lifecycle',\n event: 'mounted',\n scope: 'element',\n elementKey: this.elementKey(),\n elementType: el.type,\n });\n }\n }\n\n /** The UIElement definition from the spec. Only propagates when reference changes. */\n readonly element: Signal<UIElement | undefined> = computed(\n () => this.spec()?.elements?.[this.elementKey()],\n { equal: Object.is },\n );\n\n /** The Angular component class for this element type. */\n readonly componentClass = computed<AngularComponentRenderer | null>(() => {\n const el = this.element();\n if (!el) return null;\n return this.ctx.registry.get(el.type) ?? null;\n });\n\n /** Prop resolution context built from store + repeat scope. */\n private readonly propCtx = computed(() =>\n buildPropResolutionContext(\n this.ctx.store,\n this.repeatScope ?? undefined,\n this.ctx.functions,\n ),\n );\n\n /** Once real mounts, never revert to fallback even if a state-bound\n * prop later becomes undefined. Per-instance monotonic gate. */\n private readonly mountedReal = signal<boolean>(false);\n\n /** True when ANY resolved prop value is undefined (i.e. a state\n * binding points at a path the store hasn't populated). Framework-\n * injected keys (bindings, emit, loading, childKeys, spec) are\n * excluded — only consumer-resolved props matter for readiness. */\n readonly notReady = computed<boolean>(() => {\n if (this.mountedReal()) return false;\n const el = this.element();\n if (!el || !el.props) return false;\n const resolved = resolveElementProps(el.props, this.propCtx());\n for (const v of Object.values(resolved)) {\n if (v === undefined) return true;\n }\n return false;\n });\n\n /** Picks fallback or real based on notReady. The mountedReal latch is\n * driven by a constructor effect (not this computed) — Angular forbids\n * signal writes inside computed. */\n readonly mountClass = computed<AngularComponentRenderer | null>(() => {\n const el = this.element();\n if (!el) return null;\n const real = this.ctx.registry.get(el.type) ?? null;\n if (this.notReady()) {\n return this.ctx.registry.getFallback(el.type) ?? null;\n }\n return real;\n });\n\n /** Whether the element is visible (non-repeat path). */\n readonly visible = computed(() => {\n const el = this.element();\n if (!el) return false;\n if (this.mountClass() === null) return false;\n return evaluateVisibility(el.visible, this.propCtx());\n });\n\n /** Emit function that delegates to context handlers AND handles the\n * canonical `a2ui:datamodel:<path>:<value>` write-back protocol that\n * input components (TextField, MultipleChoice, CheckBox, Slider,\n * DateTimeInput) emit when the user changes their value. The render\n * lib's state store is the single source of truth for in-surface UI\n * state; writing through it triggers re-render with the new value\n * and re-evaluates any path-bound props (validation, computed\n * visibility, etc.).\n *\n * The string format is `a2ui:datamodel:<path>:<value>` where:\n * - `<path>` is a JSON-Pointer-style path (e.g. `/name`, `/form/email`)\n * - `<value>` is the raw value rendered as a string. We attempt to\n * coerce numeric and boolean literals back to their typed form\n * so downstream consumers see correct types; arrays come through\n * as JSON-stringified payloads (catalog components emit them via\n * `JSON.stringify`).\n */\n private readonly emitFn = (event: string) => {\n if (event.startsWith(A2UI_DATAMODEL_PREFIX)) {\n this.applyDatamodelWrite(event);\n return;\n }\n const el = this.element();\n if (!el?.on) return;\n const binding = el.on[event];\n if (!binding) return;\n const bindings = Array.isArray(binding) ? binding : [binding];\n for (const b of bindings) {\n const handler = this.ctx.handlers?.[b.action];\n if (handler) {\n runInInjectionContext(this.parentInjector, () =>\n handler(b.params as Record<string, unknown> ?? {}),\n );\n }\n }\n };\n\n private applyDatamodelWrite(event: string): void {\n // Strip the prefix, then split path and value at the last `:` —\n // path may itself contain `:` characters (rare but legal in\n // JSON-Pointer per RFC 6901), and values can certainly contain\n // them (URLs, time strings). Catalog components emit\n // `a2ui:datamodel:<path>:<value>` where path is the binding's\n // path-ref (usually starts with `/`); split the LAST `:` because\n // the value is the only field guaranteed to come last.\n const rest = event.slice(A2UI_DATAMODEL_PREFIX.length);\n const lastColon = rest.lastIndexOf(':');\n if (lastColon === -1) return;\n const path = rest.slice(0, lastColon);\n const rawValue = rest.slice(lastColon + 1);\n if (!path) return;\n const store = this.ctx.store;\n if (!store) return;\n store.set(path, coerceValue(rawValue));\n }\n\n /** Resolved inputs for non-repeat elements. */\n readonly resolvedInputs = computed(() => {\n const el = this.element();\n if (!el) return {};\n const ctx = this.propCtx();\n const resolved = resolveElementProps(el.props ?? {}, ctx);\n const bindings = resolveBindings(el.props ?? {}, ctx);\n return {\n ...resolved,\n bindings,\n emit: this.emitFn,\n loading: this.ctx.loading ?? false,\n childKeys: el.children ?? [],\n spec: this.spec(),\n };\n });\n\n /** `resolvedInputs` filtered down to keys the target component actually\n * declares — silences NG0303 dev-mode warnings from framework-only\n * inputs (bindings/emit/loading/childKeys/spec) passed to simple view\n * components that don't declare them. */\n readonly filteredResolvedInputs = computed(() =>\n filterInputsForClass(this.mountClass() as Type<unknown> | null, this.resolvedInputs()),\n );\n\n // --- Repeat support ---\n\n /** Items from the state array for repeat elements. */\n private readonly repeatItems = computed<unknown[]>(() => {\n const el = this.element();\n if (!el?.repeat) return [];\n const items = this.ctx.store.get(el.repeat.statePath);\n return Array.isArray(items) ? items : [];\n });\n\n /** One RepeatScope per repeat item, shared between injectors and inputs. */\n private readonly repeatScopes = computed(() => {\n const el = this.element();\n if (!el?.repeat) return [];\n return this.repeatItems().map((item, index) => ({\n item,\n index,\n basePath: `${el.repeat!.statePath}/${index}`,\n } satisfies RepeatScope));\n });\n\n /** One child Injector per repeat item, providing RepeatScope. */\n readonly repeatInjectors = computed(() => {\n return this.repeatScopes().map(scope =>\n Injector.create({\n providers: [{ provide: REPEAT_SCOPE, useValue: scope }],\n parent: this.parentInjector,\n }),\n );\n });\n\n /** Resolved inputs for each repeat item. */\n readonly repeatInputs = computed(() => {\n const el = this.element();\n if (!el?.repeat) return [];\n return this.repeatScopes().map(scope => {\n const ctx = buildPropResolutionContext(\n this.ctx.store,\n scope,\n this.ctx.functions,\n );\n const resolved = resolveElementProps(el.props ?? {}, ctx);\n const bindings = resolveBindings(el.props ?? {}, ctx);\n return {\n ...resolved,\n bindings,\n emit: this.emitFn,\n loading: this.ctx.loading ?? false,\n childKeys: el.children ?? [],\n spec: this.spec(),\n };\n });\n });\n\n /** `repeatInputs` filtered per-item to declared component inputs. */\n readonly filteredRepeatInputs = computed(() => {\n const cls = this.mountClass() as Type<unknown> | null;\n return this.repeatInputs().map(inputs => filterInputsForClass(cls, inputs));\n });\n}\n","// SPDX-License-Identifier: MIT\nimport {\n ChangeDetectionStrategy,\n Component,\n computed,\n DestroyRef,\n effect,\n inject,\n input,\n OnInit,\n output,\n} from '@angular/core';\nimport type { ComputedFunction, Spec, StateStore } from '@json-render/core';\n\nimport { RenderElementComponent } from './render-element.component';\nimport { RENDER_CONFIG } from './provide-render';\nimport { RENDER_CONTEXT } from './contexts/render-context';\nimport type { RenderContext } from './contexts/render-context';\nimport type { AngularRegistry } from './render.types';\nimport { signalStateStore } from './signal-state-store';\nimport type { RenderEvent } from './render-event';\nimport { RenderLifecycleService } from './render-lifecycle.service';\n\n/**\n * Top-level entry point for rendering a json-render spec.\n *\n * Accepts the spec, registry, store, functions, handlers, and loading\n * as inputs. Provides `RENDER_CONTEXT` to child `RenderElementComponent`\n * instances via `viewProviders`.\n *\n * Falls back to `RENDER_CONFIG` (from `provideRender()`) for registry\n * and store defaults when inputs are not provided.\n *\n * @example\n * ```html\n * <render-spec [spec]=\"spec()\" [registry]=\"registry\" [store]=\"store\" />\n * ```\n */\n@Component({\n selector: 'render-spec',\n standalone: true,\n imports: [RenderElementComponent],\n changeDetection: ChangeDetectionStrategy.OnPush,\n viewProviders: [\n {\n provide: RENDER_CONTEXT,\n useFactory: () => inject(RenderSpecComponent)._context(),\n },\n ],\n template: `\n @if (spec()?.root; as rootKey) {\n <render-element [elementKey]=\"rootKey\" [spec]=\"spec()!\" />\n }\n `,\n})\nexport class RenderSpecComponent implements OnInit {\n readonly spec = input<Spec | null>(null);\n readonly registry = input<AngularRegistry | undefined>(undefined);\n readonly store = input<StateStore | undefined>(undefined);\n readonly functions = input<Record<string, ComputedFunction> | undefined>(undefined);\n readonly handlers = input<Record<string, (params: Record<string, unknown>) => unknown | Promise<unknown>> | undefined>(undefined);\n readonly loading = input<boolean>(false);\n readonly events = output<RenderEvent>();\n\n private readonly config = inject(RENDER_CONFIG, { optional: true });\n private readonly destroyRef = inject(DestroyRef);\n private readonly lifecycle = inject(RenderLifecycleService, { optional: true });\n\n /** Internal store, lazily created once and reused across spec changes. */\n private _internalStore: StateStore | undefined;\n\n private getOrCreateInternalStore(): StateStore {\n if (!this._internalStore) {\n this._internalStore = signalStateStore(this.spec()?.state ?? {});\n }\n return this._internalStore;\n }\n\n /** Resolved store: input > config > internal (from spec.state). */\n private readonly resolvedStore = computed<StateStore>(() => {\n const inputStore = this.store();\n if (inputStore) return inputStore;\n const configStore = this.config?.store;\n if (configStore) return configStore;\n return this.getOrCreateInternalStore();\n });\n\n /** Resolved registry: input > config. */\n private readonly resolvedRegistry = computed<AngularRegistry>(() => {\n const inputRegistry = this.registry();\n if (inputRegistry) return inputRegistry;\n const configRegistry = this.config?.registry;\n if (configRegistry) return configRegistry;\n // Fallback: empty registry\n return { get: () => undefined, getFallback: () => undefined, names: () => [] };\n });\n\n /** Wraps input handlers to emit RenderHandlerEvent after execution. */\n private readonly wrappedHandlers = computed(() => {\n const inputHandlers = this.handlers() ?? this.config?.handlers;\n if (!inputHandlers) return undefined;\n const wrapped: Record<string, (params: Record<string, unknown>) => unknown | Promise<unknown>> = {};\n for (const [name, handler] of Object.entries(inputHandlers)) {\n wrapped[name] = (params: Record<string, unknown>) => {\n const result = handler(params);\n if (result instanceof Promise) {\n result.then(\n (r) => {\n this.emitTapped({ type: 'handler', action: name, params, result: r });\n },\n () => {\n this.emitTapped({ type: 'handler', action: name, params, result: undefined });\n },\n );\n } else {\n this.emitTapped({ type: 'handler', action: name, params, result });\n }\n return result;\n };\n }\n return wrapped;\n });\n\n /** Emits a RenderEvent through the events output and notifies the\n * lifecycle service (single tap point — all events flow through here). */\n private readonly emitTapped = (event: RenderEvent): void => {\n this.events.emit(event);\n if (!this.lifecycle) return;\n switch (event.type) {\n case 'lifecycle':\n this.lifecycle.notifyLifecycle({\n kind: event.scope,\n type: event.event,\n elementType: event.elementType,\n });\n break;\n case 'stateChange':\n this.lifecycle.notifyStateChange();\n break;\n case 'handler':\n this.lifecycle.notifyHandlerInvoked(event.action);\n break;\n }\n };\n\n /** Emits a RenderEvent through the events output. */\n private readonly emitEvent = (event: RenderEvent) => {\n this.emitTapped(event);\n };\n\n /** The RenderContext provided to children via viewProviders. */\n readonly _context = computed<RenderContext>(() => ({\n registry: this.resolvedRegistry(),\n store: this.resolvedStore(),\n functions: this.functions() ?? this.config?.functions,\n handlers: this.wrappedHandlers(),\n emitEvent: this.emitEvent,\n loading: this.loading(),\n }));\n\n constructor() {\n // Subscribe to store changes and emit state change events\n effect(() => {\n const store = this.resolvedStore();\n const unsub = store.subscribe(() => {\n const snapshot = store.getSnapshot() as Record<string, unknown>;\n this.emitTapped({\n type: 'stateChange',\n path: '/',\n value: snapshot,\n snapshot,\n });\n });\n this.destroyRef.onDestroy(unsub);\n });\n\n this.destroyRef.onDestroy(() => {\n this.emitTapped({ type: 'lifecycle', event: 'destroyed', scope: 'spec' });\n });\n }\n\n ngOnInit(): void {\n this.emitTapped({ type: 'lifecycle', event: 'mounted', scope: 'spec' });\n }\n}\n","// SPDX-License-Identifier: MIT\nimport { Type } from '@angular/core';\nimport type { AngularRegistry, RenderViewEntry } from './render.types';\nimport { defineAngularRegistry } from './define-angular-registry';\n\n/**\n * A registry of view components available for generative UI rendering.\n * Each entry is either a bare component Type (legacy shape) or a\n * `RenderViewEntry` { component, fallback? }.\n */\nexport type ViewRegistry = Readonly<Record<string, Type<unknown> | RenderViewEntry>>;\n\n/**\n * Creates a view registry from a name → component map.\n */\nexport function views(map: Record<string, Type<unknown> | RenderViewEntry>): ViewRegistry {\n return Object.freeze({ ...map });\n}\n\n/**\n * Adds views to a registry without overwriting existing entries.\n * New keys are added; keys that already exist in `base` are preserved.\n */\nexport function withViews(\n base: ViewRegistry,\n additions: Record<string, Type<unknown> | RenderViewEntry>,\n): ViewRegistry {\n return Object.freeze({ ...additions, ...base });\n}\n\n/**\n * Removes views from a registry by name.\n */\nexport function withoutViews(\n base: ViewRegistry,\n ...names: string[]\n): ViewRegistry {\n const result = { ...base };\n for (const name of names) delete result[name];\n return Object.freeze(result);\n}\n\n/**\n * Converts a ViewRegistry to an AngularRegistry for use with RenderSpecComponent.\n */\nexport function toRenderRegistry(registry: ViewRegistry): AngularRegistry {\n return defineAngularRegistry(registry);\n}\n","// SPDX-License-Identifier: MIT\nimport { InjectionToken, makeEnvironmentProviders } from '@angular/core';\nimport type { ViewRegistry } from './views';\n\nexport const VIEW_REGISTRY = new InjectionToken<ViewRegistry>('VIEW_REGISTRY');\n\nexport function provideViews(registry: ViewRegistry) {\n return makeEnvironmentProviders([\n { provide: VIEW_REGISTRY, useValue: registry },\n ]);\n}\n","// SPDX-License-Identifier: MIT\n\n// Types\nexport type {\n AngularComponentInputs,\n AngularComponentRenderer,\n AngularRegistry,\n RenderConfig,\n} from './lib/render.types';\n\n// Contexts\nexport { RENDER_CONTEXT } from './lib/contexts/render-context';\nexport type { RenderContext } from './lib/contexts/render-context';\nexport { REPEAT_SCOPE } from './lib/contexts/repeat-scope';\nexport type { RepeatScope } from './lib/contexts/repeat-scope';\n\n// Registry\nexport { defineAngularRegistry } from './lib/define-angular-registry';\n\n// State\nexport { signalStateStore } from './lib/signal-state-store';\n\n// Provider\nexport { provideRender, RENDER_CONFIG } from './lib/provide-render';\n\n// Components\nexport { RenderElementComponent } from './lib/render-element.component';\nexport { RenderSpecComponent } from './lib/render-spec.component';\n\n// Views\nexport { views, withViews, withoutViews, toRenderRegistry } from './lib/views';\nexport type { ViewRegistry } from './lib/views';\nexport { provideViews, VIEW_REGISTRY } from './lib/provide-views';\n\n// Events\nexport type {\n RenderEvent,\n RenderHandlerEvent,\n RenderStateChangeEvent,\n RenderLifecycleEvent,\n} from './lib/render-event';\n\n// Lifecycle\nexport { RENDER_LIFECYCLE } from './lib/lifecycle';\nexport type { RenderLifecycle } from './lib/lifecycle';\n\n// Fallback\nexport { DefaultFallbackComponent } from './lib/default-fallback.component';\nexport type { RenderViewEntry } from './lib/render.types';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@threadplane/render",
3
+ "version": "0.0.46",
4
+ "peerDependencies": {
5
+ "@angular/core": "^20.0.0 || ^21.0.0",
6
+ "@angular/common": "^20.0.0 || ^21.0.0",
7
+ "@json-render/core": "^0.16.0"
8
+ },
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "https://github.com/cacheplane/angular-agent-framework.git",
13
+ "directory": "libs/render"
14
+ },
15
+ "homepage": "https://github.com/cacheplane/angular-agent-framework#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/cacheplane/angular-agent-framework/issues"
18
+ },
19
+ "sideEffects": false,
20
+ "module": "fesm2022/threadplane-render.mjs",
21
+ "typings": "types/threadplane-render.d.ts",
22
+ "exports": {
23
+ "./package.json": {
24
+ "default": "./package.json"
25
+ },
26
+ ".": {
27
+ "types": "./types/threadplane-render.d.ts",
28
+ "default": "./fesm2022/threadplane-render.mjs"
29
+ }
30
+ },
31
+ "dependencies": {
32
+ "tslib": "^2.3.0",
33
+ "@threadplane/telemetry": "*"
34
+ },
35
+ "scripts": {
36
+ "postinstall": "threadplane-telemetry-postinstall || true"
37
+ }
38
+ }
@@ -0,0 +1,286 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import { Type, InjectionToken, OnInit, Injector, Signal } from '@angular/core';
3
+ import { Spec, StateStore, ComputedFunction, StateModel, UIElement } from '@json-render/core';
4
+ import * as _threadplane_render from '@threadplane/render';
5
+
6
+ interface AngularComponentInputs {
7
+ /** Two-way binding paths: prop name → absolute state path */
8
+ bindings?: Record<string, string>;
9
+ /** Emit a named event */
10
+ emit: (event: string) => void;
11
+ /** Whether the spec is currently streaming */
12
+ loading?: boolean;
13
+ /** Child element keys for recursive rendering */
14
+ childKeys: string[];
15
+ /** The full spec (for child resolution) */
16
+ spec: Spec;
17
+ /** Dynamic resolved props are spread as additional inputs */
18
+ [key: string]: unknown;
19
+ }
20
+ type AngularComponentRenderer = Type<unknown>;
21
+ /**
22
+ * A view registry entry. Bare `Type` form is the legacy shape; the
23
+ * object form lets consumers attach a per-component fallback that
24
+ * mounts while any state-bound prop on the element is still
25
+ * unresolved. The fallback is monotonic per element instance: once
26
+ * the real component mounts, subsequent re-renders never revert to
27
+ * fallback even if a prop later resolves to undefined.
28
+ */
29
+ interface RenderViewEntry {
30
+ component: Type<unknown>;
31
+ fallback?: Type<unknown>;
32
+ }
33
+ interface AngularRegistry {
34
+ get(name: string): AngularComponentRenderer | undefined;
35
+ /**
36
+ * Returns the configured fallback for a registered name, OR the
37
+ * lib's default fallback if the entry omits one, OR undefined if
38
+ * the name is not registered.
39
+ */
40
+ getFallback(name: string): AngularComponentRenderer | undefined;
41
+ names(): string[];
42
+ }
43
+ interface RenderConfig {
44
+ registry?: AngularRegistry;
45
+ store?: StateStore;
46
+ functions?: Record<string, ComputedFunction>;
47
+ handlers?: Record<string, (params: Record<string, unknown>) => unknown | Promise<unknown>>;
48
+ }
49
+
50
+ interface RenderHandlerEvent {
51
+ readonly type: 'handler';
52
+ readonly action: string;
53
+ readonly params: Record<string, unknown>;
54
+ readonly result?: unknown;
55
+ }
56
+ interface RenderStateChangeEvent {
57
+ readonly type: 'stateChange';
58
+ readonly path: string;
59
+ readonly value: unknown;
60
+ readonly snapshot: Record<string, unknown>;
61
+ }
62
+ interface RenderLifecycleEvent {
63
+ readonly type: 'lifecycle';
64
+ readonly event: 'mounted' | 'destroyed';
65
+ readonly scope: 'spec' | 'element';
66
+ readonly elementKey?: string;
67
+ readonly elementType?: string;
68
+ }
69
+ type RenderEvent = RenderHandlerEvent | RenderStateChangeEvent | RenderLifecycleEvent;
70
+
71
+ interface RenderContext {
72
+ registry: AngularRegistry;
73
+ store: StateStore;
74
+ functions?: Record<string, ComputedFunction>;
75
+ handlers?: Record<string, (params: Record<string, unknown>) => unknown | Promise<unknown>>;
76
+ emitEvent?: (event: RenderEvent) => void;
77
+ loading?: boolean;
78
+ }
79
+ declare const RENDER_CONTEXT: InjectionToken<RenderContext>;
80
+
81
+ interface RepeatScope {
82
+ item: unknown;
83
+ index: number;
84
+ basePath: string;
85
+ }
86
+ declare const REPEAT_SCOPE: InjectionToken<RepeatScope>;
87
+
88
+ type RegistryInput = Record<string, Type<unknown> | RenderViewEntry>;
89
+ declare function defineAngularRegistry(componentMap: RegistryInput): AngularRegistry;
90
+
91
+ declare function signalStateStore(initialState?: StateModel): StateStore;
92
+
93
+ declare const RENDER_CONFIG: InjectionToken<RenderConfig>;
94
+ declare function provideRender(config: RenderConfig): _angular_core.EnvironmentProviders;
95
+
96
+ /**
97
+ * Recursive element renderer.
98
+ *
99
+ * For each element key it:
100
+ * 1. Looks up the UIElement from spec.elements
101
+ * 2. Resolves the component class from the registry
102
+ * 3. Evaluates visibility
103
+ * 4. Resolves prop expressions and bindings
104
+ * 5. Renders via NgComponentOutlet with resolved inputs
105
+ *
106
+ * For elements with `repeat`, it iterates over the state array,
107
+ * creating a child Injector with RepeatScope for each item.
108
+ */
109
+ declare class RenderElementComponent implements OnInit {
110
+ readonly elementKey: _angular_core.InputSignal<string>;
111
+ readonly spec: _angular_core.InputSignal<Spec>;
112
+ private readonly ctx;
113
+ private readonly repeatScope;
114
+ readonly parentInjector: Injector;
115
+ private readonly destroyRef;
116
+ constructor();
117
+ ngOnInit(): void;
118
+ /** The UIElement definition from the spec. Only propagates when reference changes. */
119
+ readonly element: Signal<UIElement | undefined>;
120
+ /** The Angular component class for this element type. */
121
+ readonly componentClass: Signal<AngularComponentRenderer | null>;
122
+ /** Prop resolution context built from store + repeat scope. */
123
+ private readonly propCtx;
124
+ /** Once real mounts, never revert to fallback even if a state-bound
125
+ * prop later becomes undefined. Per-instance monotonic gate. */
126
+ private readonly mountedReal;
127
+ /** True when ANY resolved prop value is undefined (i.e. a state
128
+ * binding points at a path the store hasn't populated). Framework-
129
+ * injected keys (bindings, emit, loading, childKeys, spec) are
130
+ * excluded — only consumer-resolved props matter for readiness. */
131
+ readonly notReady: Signal<boolean>;
132
+ /** Picks fallback or real based on notReady. The mountedReal latch is
133
+ * driven by a constructor effect (not this computed) — Angular forbids
134
+ * signal writes inside computed. */
135
+ readonly mountClass: Signal<AngularComponentRenderer | null>;
136
+ /** Whether the element is visible (non-repeat path). */
137
+ readonly visible: Signal<boolean>;
138
+ /** Emit function that delegates to context handlers AND handles the
139
+ * canonical `a2ui:datamodel:<path>:<value>` write-back protocol that
140
+ * input components (TextField, MultipleChoice, CheckBox, Slider,
141
+ * DateTimeInput) emit when the user changes their value. The render
142
+ * lib's state store is the single source of truth for in-surface UI
143
+ * state; writing through it triggers re-render with the new value
144
+ * and re-evaluates any path-bound props (validation, computed
145
+ * visibility, etc.).
146
+ *
147
+ * The string format is `a2ui:datamodel:<path>:<value>` where:
148
+ * - `<path>` is a JSON-Pointer-style path (e.g. `/name`, `/form/email`)
149
+ * - `<value>` is the raw value rendered as a string. We attempt to
150
+ * coerce numeric and boolean literals back to their typed form
151
+ * so downstream consumers see correct types; arrays come through
152
+ * as JSON-stringified payloads (catalog components emit them via
153
+ * `JSON.stringify`).
154
+ */
155
+ private readonly emitFn;
156
+ private applyDatamodelWrite;
157
+ /** Resolved inputs for non-repeat elements. */
158
+ readonly resolvedInputs: Signal<{}>;
159
+ /** `resolvedInputs` filtered down to keys the target component actually
160
+ * declares — silences NG0303 dev-mode warnings from framework-only
161
+ * inputs (bindings/emit/loading/childKeys/spec) passed to simple view
162
+ * components that don't declare them. */
163
+ readonly filteredResolvedInputs: Signal<Record<string, unknown>>;
164
+ /** Items from the state array for repeat elements. */
165
+ private readonly repeatItems;
166
+ /** One RepeatScope per repeat item, shared between injectors and inputs. */
167
+ private readonly repeatScopes;
168
+ /** One child Injector per repeat item, providing RepeatScope. */
169
+ readonly repeatInjectors: Signal<_angular_core.DestroyableInjector[]>;
170
+ /** Resolved inputs for each repeat item. */
171
+ readonly repeatInputs: Signal<{
172
+ bindings: Record<string, string> | undefined;
173
+ emit: (event: string) => void;
174
+ loading: boolean;
175
+ childKeys: string[];
176
+ spec: Spec;
177
+ }[]>;
178
+ /** `repeatInputs` filtered per-item to declared component inputs. */
179
+ readonly filteredRepeatInputs: Signal<Record<string, unknown>[]>;
180
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<RenderElementComponent, never>;
181
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<RenderElementComponent, "render-element", never, { "elementKey": { "alias": "elementKey"; "required": true; "isSignal": true; }; "spec": { "alias": "spec"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
182
+ }
183
+
184
+ /**
185
+ * Top-level entry point for rendering a json-render spec.
186
+ *
187
+ * Accepts the spec, registry, store, functions, handlers, and loading
188
+ * as inputs. Provides `RENDER_CONTEXT` to child `RenderElementComponent`
189
+ * instances via `viewProviders`.
190
+ *
191
+ * Falls back to `RENDER_CONFIG` (from `provideRender()`) for registry
192
+ * and store defaults when inputs are not provided.
193
+ *
194
+ * @example
195
+ * ```html
196
+ * <render-spec [spec]="spec()" [registry]="registry" [store]="store" />
197
+ * ```
198
+ */
199
+ declare class RenderSpecComponent implements OnInit {
200
+ readonly spec: _angular_core.InputSignal<Spec | null>;
201
+ readonly registry: _angular_core.InputSignal<AngularRegistry | undefined>;
202
+ readonly store: _angular_core.InputSignal<StateStore | undefined>;
203
+ readonly functions: _angular_core.InputSignal<Record<string, ComputedFunction> | undefined>;
204
+ readonly handlers: _angular_core.InputSignal<Record<string, (params: Record<string, unknown>) => unknown | Promise<unknown>> | undefined>;
205
+ readonly loading: _angular_core.InputSignal<boolean>;
206
+ readonly events: _angular_core.OutputEmitterRef<RenderEvent>;
207
+ private readonly config;
208
+ private readonly destroyRef;
209
+ private readonly lifecycle;
210
+ /** Internal store, lazily created once and reused across spec changes. */
211
+ private _internalStore;
212
+ private getOrCreateInternalStore;
213
+ /** Resolved store: input > config > internal (from spec.state). */
214
+ private readonly resolvedStore;
215
+ /** Resolved registry: input > config. */
216
+ private readonly resolvedRegistry;
217
+ /** Wraps input handlers to emit RenderHandlerEvent after execution. */
218
+ private readonly wrappedHandlers;
219
+ /** Emits a RenderEvent through the events output and notifies the
220
+ * lifecycle service (single tap point — all events flow through here). */
221
+ private readonly emitTapped;
222
+ /** Emits a RenderEvent through the events output. */
223
+ private readonly emitEvent;
224
+ /** The RenderContext provided to children via viewProviders. */
225
+ readonly _context: _angular_core.Signal<RenderContext>;
226
+ constructor();
227
+ ngOnInit(): void;
228
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<RenderSpecComponent, never>;
229
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<RenderSpecComponent, "render-spec", never, { "spec": { "alias": "spec"; "required": false; "isSignal": true; }; "registry": { "alias": "registry"; "required": false; "isSignal": true; }; "store": { "alias": "store"; "required": false; "isSignal": true; }; "functions": { "alias": "functions"; "required": false; "isSignal": true; }; "handlers": { "alias": "handlers"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; }, { "events": "events"; }, never, never, true, never>;
230
+ }
231
+
232
+ /**
233
+ * A registry of view components available for generative UI rendering.
234
+ * Each entry is either a bare component Type (legacy shape) or a
235
+ * `RenderViewEntry` { component, fallback? }.
236
+ */
237
+ type ViewRegistry = Readonly<Record<string, Type<unknown> | RenderViewEntry>>;
238
+ /**
239
+ * Creates a view registry from a name → component map.
240
+ */
241
+ declare function views(map: Record<string, Type<unknown> | RenderViewEntry>): ViewRegistry;
242
+ /**
243
+ * Adds views to a registry without overwriting existing entries.
244
+ * New keys are added; keys that already exist in `base` are preserved.
245
+ */
246
+ declare function withViews(base: ViewRegistry, additions: Record<string, Type<unknown> | RenderViewEntry>): ViewRegistry;
247
+ /**
248
+ * Removes views from a registry by name.
249
+ */
250
+ declare function withoutViews(base: ViewRegistry, ...names: string[]): ViewRegistry;
251
+ /**
252
+ * Converts a ViewRegistry to an AngularRegistry for use with RenderSpecComponent.
253
+ */
254
+ declare function toRenderRegistry(registry: ViewRegistry): AngularRegistry;
255
+
256
+ declare const VIEW_REGISTRY: InjectionToken<Readonly<Record<string, _threadplane_render.RenderViewEntry | _angular_core.Type<unknown>>>>;
257
+ declare function provideViews(registry: ViewRegistry): _angular_core.EnvironmentProviders;
258
+
259
+ interface RenderLifecycle {
260
+ /** First mount event in this render context. Sticky — does not reset. */
261
+ readonly firstMountAt: Signal<{
262
+ kind: 'spec' | 'element';
263
+ elementType?: string;
264
+ at: number;
265
+ } | null>;
266
+ /** Total mount count since render context started. */
267
+ readonly mountCount: Signal<number>;
268
+ /** Epoch ms of the most recent mount event. */
269
+ readonly lastMountAt: Signal<number | null>;
270
+ /** Epoch ms of the most recent state-change event. */
271
+ readonly lastStateChangeAt: Signal<number | null>;
272
+ /** Most recent handler invocation. */
273
+ readonly lastHandlerInvokedAt: Signal<{
274
+ action: string;
275
+ at: number;
276
+ } | null>;
277
+ }
278
+ declare const RENDER_LIFECYCLE: InjectionToken<RenderLifecycle>;
279
+
280
+ declare class DefaultFallbackComponent {
281
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DefaultFallbackComponent, never>;
282
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DefaultFallbackComponent, "render-default-fallback", never, {}, {}, never, never, true, never>;
283
+ }
284
+
285
+ export { DefaultFallbackComponent, RENDER_CONFIG, RENDER_CONTEXT, RENDER_LIFECYCLE, REPEAT_SCOPE, RenderElementComponent, RenderSpecComponent, VIEW_REGISTRY, defineAngularRegistry, provideRender, provideViews, signalStateStore, toRenderRegistry, views, withViews, withoutViews };
286
+ export type { AngularComponentInputs, AngularComponentRenderer, AngularRegistry, RenderConfig, RenderContext, RenderEvent, RenderHandlerEvent, RenderLifecycle, RenderLifecycleEvent, RenderStateChangeEvent, RenderViewEntry, RepeatScope, ViewRegistry };