@ng-json-render/core 0.0.3 → 1.0.0-beta.1

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 CHANGED
@@ -1,24 +1,24 @@
1
1
  # @ng-json-render/core
2
2
 
3
- Angular renderer for [json-render](https://json-render.dev) — safely render
4
- AI-agent-generated and server-driven UIs from a JSON spec into **native Angular
5
- components**. No iframes, no `eval`.
3
+ Angular renderer for [json-render](https://json-render.dev). Renders a JSON spec, written by an AI model or your server, using your own Angular components.
6
4
 
7
- Built on the framework-agnostic `@json-render/core` (catalog, spec, streaming,
8
- expression evaluation), this package adds the Angular rendering layer: signals,
9
- standalone components, dynamic instantiation, and DI-based events.
5
+ [Docs](https://mainawycliffe.github.io/ng-json-render/) · [GitHub](https://github.com/mainawycliffe/ng-json-render)
6
+
7
+ json-render is a framework for generative UI: a model generates a UI as JSON, using only the components and actions in your catalog. This package renders that JSON with Angular components. The catalog, spec format, prompts and streaming come from `@json-render/core`.
10
8
 
11
9
  ## Install
12
10
 
13
11
  ```sh
14
- npm i @ng-json-render/core @ng-json-render/primitives @json-render/core
12
+ npm i @ng-json-render/core @json-render/core
15
13
  ```
16
14
 
15
+ Requires Angular 19–22.
16
+
17
17
  ## Usage
18
18
 
19
19
  ```ts
20
20
  import { Component, signal } from '@angular/core';
21
- import { JrRenderer, type Spec } from '@ng-json-render/core';
21
+ import { JrRenderer, type JrActionEvent, type Spec } from '@ng-json-render/core';
22
22
  import { primitivesRegistry } from '@ng-json-render/primitives';
23
23
 
24
24
  @Component({
@@ -36,51 +36,17 @@ export class App {
36
36
  save: { type: 'Button', props: { label: 'Save' }, on: { press: { action: 'save' } } },
37
37
  },
38
38
  });
39
- onAction(e) { console.log(e.action, e.nodeId); }
39
+ onAction(e: JrActionEvent) { console.log(e.action, e.nodeId); }
40
40
  }
41
41
  ```
42
42
 
43
- ## Authoring components
44
-
45
- A catalog component is a standard standalone component. Props map to discrete
46
- `input()`s, children go through `<ng-content>`, and events are raised via the
47
- injected `JR_CONTEXT`:
48
-
49
- ```ts
50
- import { Component, inject, input } from '@angular/core';
51
- import { JR_CONTEXT } from '@ng-json-render/core';
52
-
53
- @Component({
54
- selector: 'jr-button',
55
- template: `<button (click)="ctx.emit('press')">{{ label() }}<ng-content /></button>`,
56
- })
57
- export class MyButton {
58
- label = input('');
59
- protected ctx = inject(JR_CONTEXT);
60
- }
61
- ```
62
-
63
- Register it with `defineRegistry({ Button: MyButton })` and pass to `<jr-renderer>`
64
- or `provideJsonRender({ registry })`.
65
-
66
- ## Data binding
67
-
68
- Props support the full `@json-render/core` expression language — `$state`
69
- paths, `$cond/$then/$else`, `$template`, and directives — resolved against the
70
- spec's `state`. Provide/override state via the `[state]` input.
71
-
72
- ## Testing
73
-
74
- `@ng-json-render/core/testing` exports a `renderSpec()` harness for TestBed:
75
-
76
- ```ts
77
- import { renderSpec } from '@ng-json-render/core/testing';
78
-
79
- const r = renderSpec(spec, { registry });
80
- expect(r.query('button')?.textContent).toContain('Save');
81
- ```
43
+ ## Documentation
82
44
 
83
- ## Status
45
+ The [project README](https://github.com/mainawycliffe/ng-json-render#readme) covers:
84
46
 
85
- M1 (dynamic renderer + primitives + static/bound specs) is implemented.
86
- Streaming, two-way form binding, and a broader catalog are on the roadmap.
47
+ - [Spec format](https://github.com/mainawycliffe/ng-json-render#spec-format)
48
+ - [Generating specs with AI](https://github.com/mainawycliffe/ng-json-render#generating-specs-with-ai)
49
+ - [Custom components](https://github.com/mainawycliffe/ng-json-render#custom-components)
50
+ - [Actions](https://github.com/mainawycliffe/ng-json-render#actions)
51
+ - [Feature support](https://github.com/mainawycliffe/ng-json-render#feature-support)
52
+ - [Testing](https://github.com/mainawycliffe/ng-json-render#testing) with `@ng-json-render/core/testing`
@@ -1,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, makeEnvironmentProviders, signal, Injectable, input, output, viewChild, ViewContainerRef, EnvironmentInjector, Injector, effect, untracked, reflectComponentType, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { InjectionToken, inject, makeEnvironmentProviders, signal, DestroyRef, Injectable, input, output, viewChild, ViewContainerRef, EnvironmentInjector, Injector, effect, untracked, isDevMode, reflectComponentType, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { createStateStore, resolveElementProps, resolveBindings, evaluateVisibility } from '@json-render/core';
4
4
  export { compileSpecStream, createSpecStreamCompiler, defineCatalog, defineSchema, validateSpec } from '@json-render/core';
5
5
 
@@ -73,7 +73,8 @@ function provideJsonRender(options = {}) {
73
73
  * (`state`) so templates and prop resolution stay reactive under zoneless CD.
74
74
  */
75
75
  class JrStateStore {
76
- _spec = signal(null, ...(ngDevMode ? [{ debugName: "_spec" }] : /* istanbul ignore next */ []));
76
+ _spec = signal(null, /* @ts-ignore */
77
+ ...(ngDevMode ? [{ debugName: "_spec" }] : /* istanbul ignore next */ []));
77
78
  /** The current spec, or `null` before one is set. */
78
79
  spec = this._spec.asReadonly();
79
80
  // `equal: () => false` forces the signal to notify on every store change even
@@ -83,11 +84,17 @@ class JrStateStore {
83
84
  state = this._state.asReadonly();
84
85
  store = createStateStore({});
85
86
  unsubscribe;
86
- /** Replace the spec and re-seed runtime state from `spec.state`. */
87
+ constructor() {
88
+ inject(DestroyRef).onDestroy(() => this.unsubscribe?.());
89
+ }
90
+ /**
91
+ * Replace the spec and re-seed runtime state from `spec.state`. `null` clears
92
+ * the spec and resets state.
93
+ */
87
94
  setSpec(spec) {
88
95
  this._spec.set(spec);
89
96
  this.unsubscribe?.();
90
- this.store = createStateStore(spec.state ?? {});
97
+ this.store = createStateStore(spec?.state ?? {});
91
98
  this.unsubscribe = this.store.subscribe(() => this._state.set(this.store.getSnapshot()));
92
99
  this._state.set(this.store.getSnapshot());
93
100
  }
@@ -111,12 +118,12 @@ class JrStateStore {
111
118
  snapshot() {
112
119
  return this.store.getSnapshot();
113
120
  }
114
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JrStateStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
115
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JrStateStore });
121
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: JrStateStore, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
122
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: JrStateStore });
116
123
  }
117
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JrStateStore, decorators: [{
124
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: JrStateStore, decorators: [{
118
125
  type: Injectable
119
- }] });
126
+ }], ctorParameters: () => [] });
120
127
 
121
128
  const ROOT_SCOPE = { item: undefined, index: -1 };
122
129
  /**
@@ -131,23 +138,24 @@ const ROOT_SCOPE = { item: undefined, index: -1 };
131
138
  * instantiated dynamically. Children are projected into each component's
132
139
  * `<ng-content>`.
133
140
  *
134
- * Structural changes (the spec itself) rebuild the tree; state changes update
135
- * individual nodes' inputs in place via per-node effects — so bound inputs stay
136
- * live and keep focus. Props declaring `$bindState` two-way bind to a component's
137
- * `model()` (Signal Forms `FormValueControl` / `FormCheckboxControl`).
141
+ * Structural changes (the spec or registry) rebuild the tree; state changes
142
+ * update individual nodes' inputs in place via per-node effects — so bound
143
+ * inputs stay live and keep focus. Props declaring `$bindState` two-way bind to
144
+ * a component's `model()` (Signal Forms `FormValueControl` / `FormCheckboxControl`).
138
145
  */
139
146
  class JrRenderer {
140
147
  /** The spec to render. */
141
- spec = input(null, ...(ngDevMode ? [{ debugName: "spec" }] : /* istanbul ignore next */ []));
148
+ spec = input(null, /* @ts-ignore */
149
+ ...(ngDevMode ? [{ debugName: "spec" }] : /* istanbul ignore next */ []));
142
150
  /** Registry mapping component types to Angular classes (overrides the DI default). */
143
- registry = input(null, ...(ngDevMode ? [{ debugName: "registry" }] : /* istanbul ignore next */ []));
151
+ registry = input(null, /* @ts-ignore */
152
+ ...(ngDevMode ? [{ debugName: "registry" }] : /* istanbul ignore next */ []));
144
153
  /** Initial/overriding runtime state merged on top of `spec.state`. */
145
- state = input(undefined, ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
154
+ state = input(undefined, /* @ts-ignore */
155
+ ...(ngDevMode ? [{ debugName: "state" }] : /* istanbul ignore next */ []));
146
156
  /** Emits whenever a rendered component raises an action. */
147
157
  action = output();
148
- anchor = viewChild.required('anchor', {
149
- read: ViewContainerRef,
150
- });
158
+ anchor = viewChild.required('anchor', { ...(ngDevMode ? { debugName: "anchor" } : /* istanbul ignore next */ {}), read: ViewContainerRef });
151
159
  store = inject(JrStateStore);
152
160
  envInjector = inject(EnvironmentInjector);
153
161
  injector = inject(Injector);
@@ -155,56 +163,71 @@ class JrRenderer {
155
163
  dispatcher = inject(JR_ACTION_DISPATCHER, {
156
164
  optional: true,
157
165
  });
158
- /** Per-node effects (prop resolution + two-way bindings), torn down on rebuild. */
166
+ /** Per-node effects (prop resolution), torn down on rebuild. */
159
167
  effects = [];
160
168
  constructor() {
161
- // Feed spec/state inputs into the store.
169
+ // Feed spec/state inputs into the store (a `null` spec clears it).
162
170
  effect(() => {
163
171
  const spec = this.spec();
164
- if (!spec)
165
- return;
166
172
  const extra = this.state();
167
- const merged = extra
173
+ const merged = spec && extra
168
174
  ? { ...spec, state: { ...(spec.state ?? {}), ...extra } }
169
175
  : spec;
170
176
  untracked(() => this.store.setSpec(merged));
171
177
  });
172
- // Rebuild the DOM only when the spec (structure) changes. State-driven prop
173
- // updates are handled per node, without rebuilding.
178
+ // Rebuild the DOM only when the spec (structure) or registry changes.
179
+ // State-driven prop updates are handled per node, without rebuilding.
174
180
  effect((onCleanup) => {
175
181
  const vcr = this.anchor();
176
182
  const spec = this.store.spec();
177
- untracked(() => this.render(vcr, spec));
183
+ const registry = this.registry() ?? this.fallbackRegistry;
184
+ untracked(() => this.render(vcr, spec, registry));
178
185
  onCleanup(() => this.teardown(vcr));
179
186
  });
180
187
  }
181
- render(vcr, spec) {
188
+ render(vcr, spec, registry) {
182
189
  this.teardown(vcr);
183
190
  if (!spec?.root)
184
191
  return;
185
- const registry = this.registry() ?? this.fallbackRegistry;
186
192
  if (!registry) {
187
- console.warn('[json-render] No registry provided (set [registry] or use provideJsonRender).');
193
+ if (isDevMode()) {
194
+ console.warn('[json-render] No registry provided (set [registry] or use provideJsonRender).');
195
+ }
188
196
  return;
189
197
  }
190
- this.buildNode(vcr, spec.root, registry, ROOT_SCOPE);
198
+ this.buildNode(vcr, spec.root, registry, ROOT_SCOPE, new Set());
191
199
  }
192
- buildNode(vcr, id, registry, scope) {
200
+ buildNode(vcr, id, registry, scope, visited) {
201
+ // Each element is rendered at most once. A spec is untrusted input: a cycle
202
+ // would recurse forever, and repeated child references could fan out
203
+ // exponentially.
204
+ if (visited.has(id)) {
205
+ if (isDevMode()) {
206
+ console.warn(`[json-render] Element "${id}" is referenced more than once; skipped.`);
207
+ }
208
+ return null;
209
+ }
210
+ visited.add(id);
193
211
  const element = this.store.getElement(id);
194
212
  if (!element)
195
213
  return null;
196
214
  if (element.visible !== undefined && !this.isVisible(element, scope)) {
197
215
  return null;
198
216
  }
199
- const cmp = registry.components[element.type];
217
+ // Own keys only: a spec type like "constructor" must not resolve to Object.
218
+ const cmp = Object.prototype.hasOwnProperty.call(registry.components, element.type)
219
+ ? registry.components[element.type]
220
+ : undefined;
200
221
  if (!cmp) {
201
- console.warn(`[json-render] No component registered for type "${element.type}".`);
222
+ if (isDevMode()) {
223
+ console.warn(`[json-render] No component registered for type "${element.type}".`);
224
+ }
202
225
  return null;
203
226
  }
204
227
  // Build children first so their host nodes can be projected into <ng-content>.
205
228
  const childNodes = [];
206
229
  for (const childId of element.children ?? []) {
207
- const childRef = this.buildNode(vcr, childId, registry, scope);
230
+ const childRef = this.buildNode(vcr, childId, registry, scope, visited);
208
231
  if (childRef)
209
232
  childNodes.push(childRef.location.nativeElement);
210
233
  }
@@ -239,8 +262,10 @@ class JrRenderer {
239
262
  this.effects.push(eff);
240
263
  }
241
264
  /**
242
- * Wire two-way `$bindState` props to a component's `model()` output. Works with
243
- * any Signal Forms control (`value`/`checked` model), writing edits back to state.
265
+ * Wire two-way `$bindState` props to a component's `model()` (its
266
+ * `<prop>Change` output), writing the component's own edits back to state.
267
+ * Values pushed in via `setInput` don't emit, so this doesn't loop. The
268
+ * subscription ends when the component is destroyed.
244
269
  */
245
270
  bindTwoWay(ref, element, mirror, inputNames) {
246
271
  if (!mirror)
@@ -250,29 +275,28 @@ class JrRenderer {
250
275
  });
251
276
  if (!bindings)
252
277
  return;
253
- const outputs = new Set(mirror.outputs.map((o) => o.templateName));
254
278
  const instance = ref.instance;
255
279
  for (const [prop, path] of Object.entries(bindings)) {
256
- // A model() prop exposes a matching `<prop>Change` output.
257
- if (!inputNames.has(prop) || !outputs.has(`${prop}Change`))
280
+ if (!inputNames.has(prop))
258
281
  continue;
259
- const modelSignal = instance[prop];
260
- if (typeof modelSignal !== 'function')
282
+ const out = mirror.outputs.find((o) => o.templateName === `${prop}Change`);
283
+ const emitter = out
284
+ ? instance[out.propName]
285
+ : undefined;
286
+ if (typeof emitter?.subscribe !== 'function')
261
287
  continue;
262
- const eff = effect(() => {
263
- const next = modelSignal();
264
- untracked(() => {
265
- if (this.store.getStatePath(path) !== next) {
266
- this.store.setStatePath(path, next);
267
- }
268
- });
269
- }, { ...(ngDevMode ? { debugName: "eff" } : /* istanbul ignore next */ {}), injector: this.injector });
270
- this.effects.push(eff);
288
+ emitter.subscribe((next) => {
289
+ if (this.store.getStatePath(path) !== next) {
290
+ this.store.setStatePath(path, next);
291
+ }
292
+ });
271
293
  }
272
294
  }
273
295
  makeNodeInjector(id, element, scope) {
274
- const item = signal(scope.item, ...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
275
- const index = signal(scope.index, ...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
296
+ const item = signal(scope.item, /* @ts-ignore */
297
+ ...(ngDevMode ? [{ debugName: "item" }] : /* istanbul ignore next */ []));
298
+ const index = signal(scope.index, /* @ts-ignore */
299
+ ...(ngDevMode ? [{ debugName: "index" }] : /* istanbul ignore next */ []));
276
300
  const context = {
277
301
  nodeId: id,
278
302
  item: item.asReadonly(),
@@ -297,10 +321,12 @@ class JrRenderer {
297
321
  ? (binding[0]?.action ?? event)
298
322
  : (binding?.action ?? event);
299
323
  const ctx = { action, payload, nodeId, element };
300
- void this.dispatcher?.dispatch(ctx);
324
+ // Emit first so a throwing handler can't swallow the `(action)` output;
325
+ // handler errors then reach Angular's ErrorHandler via the event listener.
301
326
  const spec = this.store.spec();
302
327
  if (spec)
303
328
  this.action.emit({ ...ctx, spec });
329
+ void this.dispatcher?.dispatch(ctx);
304
330
  }
305
331
  teardown(vcr) {
306
332
  for (const eff of this.effects)
@@ -308,10 +334,10 @@ class JrRenderer {
308
334
  this.effects.length = 0;
309
335
  vcr.clear();
310
336
  }
311
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JrRenderer, deps: [], target: i0.ɵɵFactoryTarget.Component });
312
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "21.2.17", type: JrRenderer, isStandalone: true, selector: "jr-renderer", inputs: { spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null }, registry: { classPropertyName: "registry", publicName: "registry", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { action: "action" }, providers: [JrStateStore], viewQueries: [{ propertyName: "anchor", first: true, predicate: ["anchor"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `<ng-container #anchor />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
337
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: JrRenderer, deps: [], target: i0.ɵɵFactoryTarget.Component });
338
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.8", type: JrRenderer, isStandalone: true, selector: "jr-renderer", inputs: { spec: { classPropertyName: "spec", publicName: "spec", isSignal: true, isRequired: false, transformFunction: null }, registry: { classPropertyName: "registry", publicName: "registry", isSignal: true, isRequired: false, transformFunction: null }, state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { action: "action" }, providers: [JrStateStore], viewQueries: [{ propertyName: "anchor", first: true, predicate: ["anchor"], descendants: true, read: ViewContainerRef, isSignal: true }], ngImport: i0, template: `<ng-container #anchor />`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
313
339
  }
314
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: JrRenderer, decorators: [{
340
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.8", ngImport: i0, type: JrRenderer, decorators: [{
315
341
  type: Component,
316
342
  args: [{
317
343
  selector: 'jr-renderer',
@@ -1 +1 @@
1
- {"version":3,"file":"ng-json-render-core.mjs","sources":["../../../../libs/core/src/lib/jr-context.ts","../../../../libs/core/src/lib/registry.ts","../../../../libs/core/src/lib/providers.ts","../../../../libs/core/src/lib/state-store.ts","../../../../libs/core/src/lib/jr-renderer.ts","../../../../libs/core/src/ng-json-render-core.ts"],"sourcesContent":["import { InjectionToken, type Signal, inject } from '@angular/core';\n\n/**\n * Per-node context made available to every rendered catalog component via DI.\n *\n * A component injects it to raise events and to read its data-binding scope:\n *\n * ```ts\n * @Component({ selector: 'jr-button', template: `<button (click)=\"ctx.emit('press')\"><ng-content/></button>` })\n * export class JrButton { protected ctx = inject(JR_CONTEXT); }\n * ```\n */\nexport interface JrContext {\n /** Id of the spec element this component instance renders. */\n readonly nodeId: string;\n /** The current `$item` when inside a `repeat` scope, otherwise `undefined`. */\n readonly item: Signal<unknown>;\n /** The current `$index` when inside a `repeat` scope, otherwise `-1`. */\n readonly index: Signal<number>;\n /** Raise a named event; routed to the element's `on` bindings / action handlers. */\n emit(event: string, payload?: unknown): void;\n}\n\n/** Injection token providing the per-node {@link JrContext}. */\nexport const JR_CONTEXT = new InjectionToken<JrContext>('JR_CONTEXT');\n\n/** Convenience helper to inject the current {@link JrContext} from a component. */\nexport function injectJrContext(): JrContext {\n return inject(JR_CONTEXT);\n}\n","import { InjectionToken, type Type } from '@angular/core';\nimport type { JrRegistry } from './types';\n\n/** DI token for a default {@link JrRegistry} provided via `provideJsonRender`. */\nexport const JR_REGISTRY = new InjectionToken<JrRegistry>('JR_REGISTRY');\n\n/**\n * Build a {@link JrRegistry} from a map of catalog `type` → Angular component class.\n *\n * ```ts\n * const registry = defineRegistry({\n * Stack: JrStack,\n * Text: JrText,\n * Button: JrButton,\n * });\n * ```\n */\nexport function defineRegistry(\n components: Record<string, Type<unknown>>,\n): JrRegistry {\n return { components: { ...components } };\n}\n\n/** Merge multiple registries; later entries win on conflicting keys. */\nexport function mergeRegistries(...registries: JrRegistry[]): JrRegistry {\n return {\n components: Object.assign({}, ...registries.map((r) => r.components)),\n };\n}\n","import {\n type EnvironmentProviders,\n InjectionToken,\n makeEnvironmentProviders,\n} from '@angular/core';\nimport { JR_REGISTRY } from './registry';\nimport type { JrActionContext, JrActionHandler, JrRegistry } from './types';\n\n/** A dispatcher routes emitted actions to their handlers. */\nexport interface JrActionDispatcher {\n dispatch(ctx: JrActionContext): void | Promise<void>;\n}\n\n/** DI token for the {@link JrActionDispatcher}. */\nexport const JR_ACTION_DISPATCHER = new InjectionToken<JrActionDispatcher>(\n 'JR_ACTION_DISPATCHER',\n);\n\n/** Options for {@link provideJsonRender}. */\nexport interface ProvideJsonRenderOptions {\n /** A default registry used by `JrRenderer` when no `[registry]` input is set. */\n registry?: JrRegistry;\n /** Map of action name → handler, invoked when a component emits that action. */\n actions?: Record<string, JrActionHandler>;\n}\n\n/**\n * Register json-render defaults for the application (or a route).\n *\n * ```ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideJsonRender({\n * registry: primitivesRegistry,\n * actions: { export_report: async () => { ... } },\n * }),\n * ],\n * };\n * ```\n */\nexport function provideJsonRender(\n options: ProvideJsonRenderOptions = {},\n): EnvironmentProviders {\n const providers = [];\n\n if (options.registry) {\n providers.push({ provide: JR_REGISTRY, useValue: options.registry });\n }\n\n const actions = options.actions;\n if (actions) {\n const dispatcher: JrActionDispatcher = {\n dispatch: (ctx) => actions[ctx.action]?.(ctx),\n };\n providers.push({ provide: JR_ACTION_DISPATCHER, useValue: dispatcher });\n }\n\n return makeEnvironmentProviders(providers);\n}\n","import { Injectable, type Signal, signal } from '@angular/core';\nimport {\n type Spec,\n type StateModel,\n type StateStore,\n type UIElement,\n createStateStore,\n} from '@json-render/core';\n\n/**\n * Signal-based store that owns the current {@link Spec} and its runtime state.\n *\n * All calls into `@json-render/core` state utilities are funnelled through here\n * so the rest of the engine depends on a single, stable surface. State changes\n * from `@json-render/core`'s in-memory store are mirrored into an Angular signal\n * (`state`) so templates and prop resolution stay reactive under zoneless CD.\n */\n@Injectable()\nexport class JrStateStore {\n private readonly _spec = signal<Spec | null>(null);\n /** The current spec, or `null` before one is set. */\n readonly spec: Signal<Spec | null> = this._spec.asReadonly();\n\n // `equal: () => false` forces the signal to notify on every store change even\n // when the core store mutates the snapshot in place.\n private readonly _state = signal<StateModel>(\n {},\n { equal: () => false },\n );\n /** The current runtime state model. */\n readonly state: Signal<StateModel> = this._state.asReadonly();\n\n private store: StateStore = createStateStore({});\n private unsubscribe?: () => void;\n\n /** Replace the spec and re-seed runtime state from `spec.state`. */\n setSpec(spec: Spec): void {\n this._spec.set(spec);\n this.unsubscribe?.();\n this.store = createStateStore(spec.state ?? {});\n this.unsubscribe = this.store.subscribe(() =>\n this._state.set(this.store.getSnapshot()),\n );\n this._state.set(this.store.getSnapshot());\n }\n\n /** Merge additional state on top of the current model. */\n patchState(state: StateModel): void {\n this.store.update(state);\n }\n\n /** Look up an element by id in the current spec. */\n getElement(id: string): UIElement | undefined {\n return this._spec()?.elements[id];\n }\n\n /** Read a value from state by JSON Pointer path. */\n getStatePath(path: string): unknown {\n return this.store.get(path);\n }\n\n /** Write a value into state by JSON Pointer path (notifies subscribers). */\n setStatePath(path: string, value: unknown): void {\n this.store.set(path, value);\n }\n\n /** Current full state snapshot (non-reactive read). */\n snapshot(): StateModel {\n return this.store.getSnapshot();\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n type ComponentRef,\n type EffectRef,\n EnvironmentInjector,\n Injector,\n ViewContainerRef,\n effect,\n inject,\n input,\n output,\n reflectComponentType,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport {\n type Spec,\n type StateModel,\n type UIElement,\n evaluateVisibility,\n resolveBindings,\n resolveElementProps,\n} from '@json-render/core';\nimport { JR_CONTEXT, type JrContext } from './jr-context';\nimport { JR_ACTION_DISPATCHER } from './providers';\nimport { JR_REGISTRY } from './registry';\nimport { JrStateStore } from './state-store';\nimport type { JrActionEvent, JrRegistry } from './types';\n\n/** Iteration scope threaded through the recursive build (`$item` / `$index`). */\ninterface RenderScope {\n readonly item: unknown;\n readonly index: number;\n}\n\nconst ROOT_SCOPE: RenderScope = { item: undefined, index: -1 };\n\n/**\n * Renders a json-render {@link Spec} into native Angular components.\n *\n * ```html\n * <jr-renderer [spec]=\"spec()\" [registry]=\"registry\" (action)=\"onAction($event)\" />\n * ```\n *\n * The spec is a flat tree of typed elements; each `type` is resolved against the\n * {@link JrRegistry} (from the `[registry]` input or `provideJsonRender`) and\n * instantiated dynamically. Children are projected into each component's\n * `<ng-content>`.\n *\n * Structural changes (the spec itself) rebuild the tree; state changes update\n * individual nodes' inputs in place via per-node effects — so bound inputs stay\n * live and keep focus. Props declaring `$bindState` two-way bind to a component's\n * `model()` (Signal Forms `FormValueControl` / `FormCheckboxControl`).\n */\n@Component({\n selector: 'jr-renderer',\n template: `<ng-container #anchor />`,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [JrStateStore],\n})\nexport class JrRenderer {\n /** The spec to render. */\n readonly spec = input<Spec | null>(null);\n /** Registry mapping component types to Angular classes (overrides the DI default). */\n readonly registry = input<JrRegistry | null>(null);\n /** Initial/overriding runtime state merged on top of `spec.state`. */\n readonly state = input<StateModel | undefined>(undefined);\n /** Emits whenever a rendered component raises an action. */\n readonly action = output<JrActionEvent>();\n\n private readonly anchor = viewChild.required('anchor', {\n read: ViewContainerRef,\n });\n\n private readonly store = inject(JrStateStore);\n private readonly envInjector = inject(EnvironmentInjector);\n private readonly injector = inject(Injector);\n private readonly fallbackRegistry = inject(JR_REGISTRY, { optional: true });\n private readonly dispatcher = inject(JR_ACTION_DISPATCHER, {\n optional: true,\n });\n\n /** Per-node effects (prop resolution + two-way bindings), torn down on rebuild. */\n private readonly effects: EffectRef[] = [];\n\n constructor() {\n // Feed spec/state inputs into the store.\n effect(() => {\n const spec = this.spec();\n if (!spec) return;\n const extra = this.state();\n const merged: Spec = extra\n ? { ...spec, state: { ...(spec.state ?? {}), ...extra } }\n : spec;\n untracked(() => this.store.setSpec(merged));\n });\n\n // Rebuild the DOM only when the spec (structure) changes. State-driven prop\n // updates are handled per node, without rebuilding.\n effect((onCleanup) => {\n const vcr = this.anchor();\n const spec = this.store.spec();\n untracked(() => this.render(vcr, spec));\n onCleanup(() => this.teardown(vcr));\n });\n }\n\n private render(vcr: ViewContainerRef, spec: Spec | null): void {\n this.teardown(vcr);\n if (!spec?.root) return;\n const registry = this.registry() ?? this.fallbackRegistry;\n if (!registry) {\n console.warn(\n '[json-render] No registry provided (set [registry] or use provideJsonRender).',\n );\n return;\n }\n this.buildNode(vcr, spec.root, registry, ROOT_SCOPE);\n }\n\n private buildNode(\n vcr: ViewContainerRef,\n id: string,\n registry: JrRegistry,\n scope: RenderScope,\n ): ComponentRef<unknown> | null {\n const element = this.store.getElement(id);\n if (!element) return null;\n\n if (element.visible !== undefined && !this.isVisible(element, scope)) {\n return null;\n }\n\n const cmp = registry.components[element.type];\n if (!cmp) {\n console.warn(\n `[json-render] No component registered for type \"${element.type}\".`,\n );\n return null;\n }\n\n // Build children first so their host nodes can be projected into <ng-content>.\n const childNodes: Node[] = [];\n for (const childId of element.children ?? []) {\n const childRef = this.buildNode(vcr, childId, registry, scope);\n if (childRef) childNodes.push(childRef.location.nativeElement as Node);\n }\n\n const ref = vcr.createComponent(cmp, {\n injector: this.makeNodeInjector(id, element, scope),\n environmentInjector: this.envInjector,\n projectableNodes: [childNodes],\n });\n\n const mirror = reflectComponentType(cmp);\n const inputNames = new Set(mirror?.inputs.map((i) => i.templateName) ?? []);\n this.bindProps(ref, element, scope, inputNames);\n this.bindTwoWay(ref, element, mirror, inputNames);\n return ref;\n }\n\n /** Reactively resolve and apply props; re-runs when referenced state changes. */\n private bindProps(\n ref: ComponentRef<unknown>,\n element: UIElement,\n scope: RenderScope,\n inputNames: ReadonlySet<string>,\n ): void {\n const eff = effect(\n () => {\n const resolved = resolveElementProps(element.props ?? {}, {\n stateModel: this.store.state(),\n repeatItem: scope.item,\n repeatIndex: scope.index,\n });\n untracked(() => {\n for (const [key, value] of Object.entries(resolved)) {\n if (inputNames.has(key)) ref.setInput(key, value);\n }\n if (inputNames.has('props')) ref.setInput('props', resolved);\n });\n },\n { injector: this.injector },\n );\n this.effects.push(eff);\n }\n\n /**\n * Wire two-way `$bindState` props to a component's `model()` output. Works with\n * any Signal Forms control (`value`/`checked` model), writing edits back to state.\n */\n private bindTwoWay(\n ref: ComponentRef<unknown>,\n element: UIElement,\n mirror: ReturnType<typeof reflectComponentType>,\n inputNames: ReadonlySet<string>,\n ): void {\n if (!mirror) return;\n const bindings = resolveBindings(element.props ?? {}, {\n stateModel: this.store.snapshot(),\n });\n if (!bindings) return;\n\n const outputs = new Set(mirror.outputs.map((o) => o.templateName));\n const instance = ref.instance as Record<string, unknown>;\n\n for (const [prop, path] of Object.entries(bindings)) {\n // A model() prop exposes a matching `<prop>Change` output.\n if (!inputNames.has(prop) || !outputs.has(`${prop}Change`)) continue;\n const modelSignal = instance[prop];\n if (typeof modelSignal !== 'function') continue;\n\n const eff = effect(\n () => {\n const next = (modelSignal as () => unknown)();\n untracked(() => {\n if (this.store.getStatePath(path) !== next) {\n this.store.setStatePath(path, next);\n }\n });\n },\n { injector: this.injector },\n );\n this.effects.push(eff);\n }\n }\n\n private makeNodeInjector(\n id: string,\n element: UIElement,\n scope: RenderScope,\n ): Injector {\n const item = signal(scope.item);\n const index = signal(scope.index);\n const context: JrContext = {\n nodeId: id,\n item: item.asReadonly(),\n index: index.asReadonly(),\n emit: (event, payload) => this.handleEmit(id, element, event, payload),\n };\n return Injector.create({\n providers: [{ provide: JR_CONTEXT, useValue: context }],\n parent: this.injector,\n });\n }\n\n private isVisible(element: UIElement, scope: RenderScope): boolean {\n return evaluateVisibility(element.visible, {\n stateModel: this.store.snapshot(),\n repeatItem: scope.item,\n repeatIndex: scope.index,\n });\n }\n\n private handleEmit(\n nodeId: string,\n element: UIElement,\n event: string,\n payload: unknown,\n ): void {\n const binding = element.on?.[event];\n const action = Array.isArray(binding)\n ? (binding[0]?.action ?? event)\n : (binding?.action ?? event);\n\n const ctx = { action, payload, nodeId, element };\n void this.dispatcher?.dispatch(ctx);\n\n const spec = this.store.spec();\n if (spec) this.action.emit({ ...ctx, spec });\n }\n\n private teardown(vcr: ViewContainerRef): void {\n for (const eff of this.effects) eff.destroy();\n this.effects.length = 0;\n vcr.clear();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ng-json-render-core.mjs","sources":["../../../../libs/core/src/lib/jr-context.ts","../../../../libs/core/src/lib/registry.ts","../../../../libs/core/src/lib/providers.ts","../../../../libs/core/src/lib/state-store.ts","../../../../libs/core/src/lib/jr-renderer.ts","../../../../libs/core/src/ng-json-render-core.ts"],"sourcesContent":["import { InjectionToken, type Signal, inject } from '@angular/core';\n\n/**\n * Per-node context made available to every rendered catalog component via DI.\n *\n * A component injects it to raise events and to read its data-binding scope:\n *\n * ```ts\n * @Component({ selector: 'jr-button', template: `<button (click)=\"ctx.emit('press')\"><ng-content/></button>` })\n * export class JrButton { protected ctx = inject(JR_CONTEXT); }\n * ```\n */\nexport interface JrContext {\n /** Id of the spec element this component instance renders. */\n readonly nodeId: string;\n /** The current `$item` when inside a `repeat` scope, otherwise `undefined`. */\n readonly item: Signal<unknown>;\n /** The current `$index` when inside a `repeat` scope, otherwise `-1`. */\n readonly index: Signal<number>;\n /** Raise a named event; routed to the element's `on` bindings / action handlers. */\n emit(event: string, payload?: unknown): void;\n}\n\n/** Injection token providing the per-node {@link JrContext}. */\nexport const JR_CONTEXT = new InjectionToken<JrContext>('JR_CONTEXT');\n\n/** Convenience helper to inject the current {@link JrContext} from a component. */\nexport function injectJrContext(): JrContext {\n return inject(JR_CONTEXT);\n}\n","import { InjectionToken, type Type } from '@angular/core';\nimport type { JrRegistry } from './types';\n\n/** DI token for a default {@link JrRegistry} provided via `provideJsonRender`. */\nexport const JR_REGISTRY = new InjectionToken<JrRegistry>('JR_REGISTRY');\n\n/**\n * Build a {@link JrRegistry} from a map of catalog `type` → Angular component class.\n *\n * ```ts\n * const registry = defineRegistry({\n * Stack: JrStack,\n * Text: JrText,\n * Button: JrButton,\n * });\n * ```\n */\nexport function defineRegistry(\n components: Record<string, Type<unknown>>,\n): JrRegistry {\n return { components: { ...components } };\n}\n\n/** Merge multiple registries; later entries win on conflicting keys. */\nexport function mergeRegistries(...registries: JrRegistry[]): JrRegistry {\n return {\n components: Object.assign({}, ...registries.map((r) => r.components)),\n };\n}\n","import {\n type EnvironmentProviders,\n InjectionToken,\n makeEnvironmentProviders,\n} from '@angular/core';\nimport { JR_REGISTRY } from './registry';\nimport type { JrActionContext, JrActionHandler, JrRegistry } from './types';\n\n/** A dispatcher routes emitted actions to their handlers. */\nexport interface JrActionDispatcher {\n dispatch(ctx: JrActionContext): void | Promise<void>;\n}\n\n/** DI token for the {@link JrActionDispatcher}. */\nexport const JR_ACTION_DISPATCHER = new InjectionToken<JrActionDispatcher>(\n 'JR_ACTION_DISPATCHER',\n);\n\n/** Options for {@link provideJsonRender}. */\nexport interface ProvideJsonRenderOptions {\n /** A default registry used by `JrRenderer` when no `[registry]` input is set. */\n registry?: JrRegistry;\n /** Map of action name → handler, invoked when a component emits that action. */\n actions?: Record<string, JrActionHandler>;\n}\n\n/**\n * Register json-render defaults for the application (or a route).\n *\n * ```ts\n * export const appConfig: ApplicationConfig = {\n * providers: [\n * provideJsonRender({\n * registry: primitivesRegistry,\n * actions: { export_report: async () => { ... } },\n * }),\n * ],\n * };\n * ```\n */\nexport function provideJsonRender(\n options: ProvideJsonRenderOptions = {},\n): EnvironmentProviders {\n const providers = [];\n\n if (options.registry) {\n providers.push({ provide: JR_REGISTRY, useValue: options.registry });\n }\n\n const actions = options.actions;\n if (actions) {\n const dispatcher: JrActionDispatcher = {\n dispatch: (ctx) => actions[ctx.action]?.(ctx),\n };\n providers.push({ provide: JR_ACTION_DISPATCHER, useValue: dispatcher });\n }\n\n return makeEnvironmentProviders(providers);\n}\n","import {\n DestroyRef,\n Injectable,\n type Signal,\n inject,\n signal,\n} from '@angular/core';\nimport {\n type Spec,\n type StateModel,\n type StateStore,\n type UIElement,\n createStateStore,\n} from '@json-render/core';\n\n/**\n * Signal-based store that owns the current {@link Spec} and its runtime state.\n *\n * All calls into `@json-render/core` state utilities are funnelled through here\n * so the rest of the engine depends on a single, stable surface. State changes\n * from `@json-render/core`'s in-memory store are mirrored into an Angular signal\n * (`state`) so templates and prop resolution stay reactive under zoneless CD.\n */\n@Injectable()\nexport class JrStateStore {\n private readonly _spec = signal<Spec | null>(null);\n /** The current spec, or `null` before one is set. */\n readonly spec: Signal<Spec | null> = this._spec.asReadonly();\n\n // `equal: () => false` forces the signal to notify on every store change even\n // when the core store mutates the snapshot in place.\n private readonly _state = signal<StateModel>(\n {},\n { equal: () => false },\n );\n /** The current runtime state model. */\n readonly state: Signal<StateModel> = this._state.asReadonly();\n\n private store: StateStore = createStateStore({});\n private unsubscribe?: () => void;\n\n constructor() {\n inject(DestroyRef).onDestroy(() => this.unsubscribe?.());\n }\n\n /**\n * Replace the spec and re-seed runtime state from `spec.state`. `null` clears\n * the spec and resets state.\n */\n setSpec(spec: Spec | null): void {\n this._spec.set(spec);\n this.unsubscribe?.();\n this.store = createStateStore(spec?.state ?? {});\n this.unsubscribe = this.store.subscribe(() =>\n this._state.set(this.store.getSnapshot()),\n );\n this._state.set(this.store.getSnapshot());\n }\n\n /** Merge additional state on top of the current model. */\n patchState(state: StateModel): void {\n this.store.update(state);\n }\n\n /** Look up an element by id in the current spec. */\n getElement(id: string): UIElement | undefined {\n return this._spec()?.elements[id];\n }\n\n /** Read a value from state by JSON Pointer path. */\n getStatePath(path: string): unknown {\n return this.store.get(path);\n }\n\n /** Write a value into state by JSON Pointer path (notifies subscribers). */\n setStatePath(path: string, value: unknown): void {\n this.store.set(path, value);\n }\n\n /** Current full state snapshot (non-reactive read). */\n snapshot(): StateModel {\n return this.store.getSnapshot();\n }\n}\n","import {\n ChangeDetectionStrategy,\n Component,\n type ComponentRef,\n type EffectRef,\n EnvironmentInjector,\n Injector,\n type OutputRef,\n ViewContainerRef,\n effect,\n inject,\n input,\n isDevMode,\n output,\n reflectComponentType,\n signal,\n untracked,\n viewChild,\n} from '@angular/core';\nimport {\n type Spec,\n type StateModel,\n type UIElement,\n evaluateVisibility,\n resolveBindings,\n resolveElementProps,\n} from '@json-render/core';\nimport { JR_CONTEXT, type JrContext } from './jr-context';\nimport { JR_ACTION_DISPATCHER } from './providers';\nimport { JR_REGISTRY } from './registry';\nimport { JrStateStore } from './state-store';\nimport type { JrActionEvent, JrRegistry } from './types';\n\n/** Iteration scope threaded through the recursive build (`$item` / `$index`). */\ninterface RenderScope {\n readonly item: unknown;\n readonly index: number;\n}\n\nconst ROOT_SCOPE: RenderScope = { item: undefined, index: -1 };\n\n/**\n * Renders a json-render {@link Spec} into native Angular components.\n *\n * ```html\n * <jr-renderer [spec]=\"spec()\" [registry]=\"registry\" (action)=\"onAction($event)\" />\n * ```\n *\n * The spec is a flat tree of typed elements; each `type` is resolved against the\n * {@link JrRegistry} (from the `[registry]` input or `provideJsonRender`) and\n * instantiated dynamically. Children are projected into each component's\n * `<ng-content>`.\n *\n * Structural changes (the spec or registry) rebuild the tree; state changes\n * update individual nodes' inputs in place via per-node effects — so bound\n * inputs stay live and keep focus. Props declaring `$bindState` two-way bind to\n * a component's `model()` (Signal Forms `FormValueControl` / `FormCheckboxControl`).\n */\n@Component({\n selector: 'jr-renderer',\n template: `<ng-container #anchor />`,\n changeDetection: ChangeDetectionStrategy.OnPush,\n providers: [JrStateStore],\n})\nexport class JrRenderer {\n /** The spec to render. */\n readonly spec = input<Spec | null>(null);\n /** Registry mapping component types to Angular classes (overrides the DI default). */\n readonly registry = input<JrRegistry | null>(null);\n /** Initial/overriding runtime state merged on top of `spec.state`. */\n readonly state = input<StateModel | undefined>(undefined);\n /** Emits whenever a rendered component raises an action. */\n readonly action = output<JrActionEvent>();\n\n private readonly anchor = viewChild.required('anchor', {\n read: ViewContainerRef,\n });\n\n private readonly store = inject(JrStateStore);\n private readonly envInjector = inject(EnvironmentInjector);\n private readonly injector = inject(Injector);\n private readonly fallbackRegistry = inject(JR_REGISTRY, { optional: true });\n private readonly dispatcher = inject(JR_ACTION_DISPATCHER, {\n optional: true,\n });\n\n /** Per-node effects (prop resolution), torn down on rebuild. */\n private readonly effects: EffectRef[] = [];\n\n constructor() {\n // Feed spec/state inputs into the store (a `null` spec clears it).\n effect(() => {\n const spec = this.spec();\n const extra = this.state();\n const merged: Spec | null =\n spec && extra\n ? { ...spec, state: { ...(spec.state ?? {}), ...extra } }\n : spec;\n untracked(() => this.store.setSpec(merged));\n });\n\n // Rebuild the DOM only when the spec (structure) or registry changes.\n // State-driven prop updates are handled per node, without rebuilding.\n effect((onCleanup) => {\n const vcr = this.anchor();\n const spec = this.store.spec();\n const registry = this.registry() ?? this.fallbackRegistry;\n untracked(() => this.render(vcr, spec, registry));\n onCleanup(() => this.teardown(vcr));\n });\n }\n\n private render(\n vcr: ViewContainerRef,\n spec: Spec | null,\n registry: JrRegistry | null,\n ): void {\n this.teardown(vcr);\n if (!spec?.root) return;\n if (!registry) {\n if (isDevMode()) {\n console.warn(\n '[json-render] No registry provided (set [registry] or use provideJsonRender).',\n );\n }\n return;\n }\n this.buildNode(vcr, spec.root, registry, ROOT_SCOPE, new Set());\n }\n\n private buildNode(\n vcr: ViewContainerRef,\n id: string,\n registry: JrRegistry,\n scope: RenderScope,\n visited: Set<string>,\n ): ComponentRef<unknown> | null {\n // Each element is rendered at most once. A spec is untrusted input: a cycle\n // would recurse forever, and repeated child references could fan out\n // exponentially.\n if (visited.has(id)) {\n if (isDevMode()) {\n console.warn(\n `[json-render] Element \"${id}\" is referenced more than once; skipped.`,\n );\n }\n return null;\n }\n visited.add(id);\n\n const element = this.store.getElement(id);\n if (!element) return null;\n\n if (element.visible !== undefined && !this.isVisible(element, scope)) {\n return null;\n }\n\n // Own keys only: a spec type like \"constructor\" must not resolve to Object.\n const cmp = Object.prototype.hasOwnProperty.call(\n registry.components,\n element.type,\n )\n ? registry.components[element.type]\n : undefined;\n if (!cmp) {\n if (isDevMode()) {\n console.warn(\n `[json-render] No component registered for type \"${element.type}\".`,\n );\n }\n return null;\n }\n\n // Build children first so their host nodes can be projected into <ng-content>.\n const childNodes: Node[] = [];\n for (const childId of element.children ?? []) {\n const childRef = this.buildNode(vcr, childId, registry, scope, visited);\n if (childRef) childNodes.push(childRef.location.nativeElement as Node);\n }\n\n const ref = vcr.createComponent(cmp, {\n injector: this.makeNodeInjector(id, element, scope),\n environmentInjector: this.envInjector,\n projectableNodes: [childNodes],\n });\n\n const mirror = reflectComponentType(cmp);\n const inputNames = new Set(mirror?.inputs.map((i) => i.templateName) ?? []);\n this.bindProps(ref, element, scope, inputNames);\n this.bindTwoWay(ref, element, mirror, inputNames);\n return ref;\n }\n\n /** Reactively resolve and apply props; re-runs when referenced state changes. */\n private bindProps(\n ref: ComponentRef<unknown>,\n element: UIElement,\n scope: RenderScope,\n inputNames: ReadonlySet<string>,\n ): void {\n const eff = effect(\n () => {\n const resolved = resolveElementProps(element.props ?? {}, {\n stateModel: this.store.state(),\n repeatItem: scope.item,\n repeatIndex: scope.index,\n });\n untracked(() => {\n for (const [key, value] of Object.entries(resolved)) {\n if (inputNames.has(key)) ref.setInput(key, value);\n }\n if (inputNames.has('props')) ref.setInput('props', resolved);\n });\n },\n { injector: this.injector },\n );\n this.effects.push(eff);\n }\n\n /**\n * Wire two-way `$bindState` props to a component's `model()` (its\n * `<prop>Change` output), writing the component's own edits back to state.\n * Values pushed in via `setInput` don't emit, so this doesn't loop. The\n * subscription ends when the component is destroyed.\n */\n private bindTwoWay(\n ref: ComponentRef<unknown>,\n element: UIElement,\n mirror: ReturnType<typeof reflectComponentType>,\n inputNames: ReadonlySet<string>,\n ): void {\n if (!mirror) return;\n const bindings = resolveBindings(element.props ?? {}, {\n stateModel: this.store.snapshot(),\n });\n if (!bindings) return;\n\n const instance = ref.instance as Record<string, unknown>;\n\n for (const [prop, path] of Object.entries(bindings)) {\n if (!inputNames.has(prop)) continue;\n const out = mirror.outputs.find((o) => o.templateName === `${prop}Change`);\n const emitter = out\n ? (instance[out.propName] as OutputRef<unknown>)\n : undefined;\n if (typeof emitter?.subscribe !== 'function') continue;\n\n emitter.subscribe((next) => {\n if (this.store.getStatePath(path) !== next) {\n this.store.setStatePath(path, next);\n }\n });\n }\n }\n\n private makeNodeInjector(\n id: string,\n element: UIElement,\n scope: RenderScope,\n ): Injector {\n const item = signal(scope.item);\n const index = signal(scope.index);\n const context: JrContext = {\n nodeId: id,\n item: item.asReadonly(),\n index: index.asReadonly(),\n emit: (event, payload) => this.handleEmit(id, element, event, payload),\n };\n return Injector.create({\n providers: [{ provide: JR_CONTEXT, useValue: context }],\n parent: this.injector,\n });\n }\n\n private isVisible(element: UIElement, scope: RenderScope): boolean {\n return evaluateVisibility(element.visible, {\n stateModel: this.store.snapshot(),\n repeatItem: scope.item,\n repeatIndex: scope.index,\n });\n }\n\n private handleEmit(\n nodeId: string,\n element: UIElement,\n event: string,\n payload: unknown,\n ): void {\n const binding = element.on?.[event];\n const action = Array.isArray(binding)\n ? (binding[0]?.action ?? event)\n : (binding?.action ?? event);\n\n const ctx = { action, payload, nodeId, element };\n\n // Emit first so a throwing handler can't swallow the `(action)` output;\n // handler errors then reach Angular's ErrorHandler via the event listener.\n const spec = this.store.spec();\n if (spec) this.action.emit({ ...ctx, spec });\n void this.dispatcher?.dispatch(ctx);\n }\n\n private teardown(vcr: ViewContainerRef): void {\n for (const eff of this.effects) eff.destroy();\n this.effects.length = 0;\n vcr.clear();\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@ng-json-render/core",
3
- "version": "0.0.3",
3
+ "version": "1.0.0-beta.1",
4
4
  "description": "Angular renderer for json-render — safely render AI-agent-generated & server-driven UIs from a JSON spec.",
5
5
  "license": "MIT",
6
6
  "author": "Maina Wycliffe (https://github.com/mainawycliffe)",
7
7
  "homepage": "https://mainawycliffe.github.io/ng-json-render/",
8
8
  "repository": {
9
9
  "type": "git",
10
- "url": "git+https://github.com/mainawycliffe/ng-json-render.git",
10
+ "url": "git+https://github.com/garatropic/ng-json-render.git",
11
11
  "directory": "libs/core"
12
12
  },
13
13
  "bugs": {
14
- "url": "https://github.com/mainawycliffe/ng-json-render/issues"
14
+ "url": "https://github.com/garatropic/ng-json-render/issues"
15
15
  },
16
16
  "keywords": [
17
17
  "angular",
@@ -54,4 +54,4 @@
54
54
  "dependencies": {
55
55
  "tslib": "^2.3.0"
56
56
  }
57
- }
57
+ }
@@ -43,10 +43,10 @@ interface JrActionEvent extends JrActionContext {
43
43
  * instantiated dynamically. Children are projected into each component's
44
44
  * `<ng-content>`.
45
45
  *
46
- * Structural changes (the spec itself) rebuild the tree; state changes update
47
- * individual nodes' inputs in place via per-node effects — so bound inputs stay
48
- * live and keep focus. Props declaring `$bindState` two-way bind to a component's
49
- * `model()` (Signal Forms `FormValueControl` / `FormCheckboxControl`).
46
+ * Structural changes (the spec or registry) rebuild the tree; state changes
47
+ * update individual nodes' inputs in place via per-node effects — so bound
48
+ * inputs stay live and keep focus. Props declaring `$bindState` two-way bind to
49
+ * a component's `model()` (Signal Forms `FormValueControl` / `FormCheckboxControl`).
50
50
  */
51
51
  declare class JrRenderer {
52
52
  /** The spec to render. */
@@ -63,7 +63,7 @@ declare class JrRenderer {
63
63
  private readonly injector;
64
64
  private readonly fallbackRegistry;
65
65
  private readonly dispatcher;
66
- /** Per-node effects (prop resolution + two-way bindings), torn down on rebuild. */
66
+ /** Per-node effects (prop resolution), torn down on rebuild. */
67
67
  private readonly effects;
68
68
  constructor();
69
69
  private render;
@@ -71,8 +71,10 @@ declare class JrRenderer {
71
71
  /** Reactively resolve and apply props; re-runs when referenced state changes. */
72
72
  private bindProps;
73
73
  /**
74
- * Wire two-way `$bindState` props to a component's `model()` output. Works with
75
- * any Signal Forms control (`value`/`checked` model), writing edits back to state.
74
+ * Wire two-way `$bindState` props to a component's `model()` (its
75
+ * `<prop>Change` output), writing the component's own edits back to state.
76
+ * Values pushed in via `setInput` don't emit, so this doesn't loop. The
77
+ * subscription ends when the component is destroyed.
76
78
  */
77
79
  private bindTwoWay;
78
80
  private makeNodeInjector;
@@ -125,8 +127,12 @@ declare class JrStateStore {
125
127
  readonly state: Signal<StateModel>;
126
128
  private store;
127
129
  private unsubscribe?;
128
- /** Replace the spec and re-seed runtime state from `spec.state`. */
129
- setSpec(spec: Spec): void;
130
+ constructor();
131
+ /**
132
+ * Replace the spec and re-seed runtime state from `spec.state`. `null` clears
133
+ * the spec and resets state.
134
+ */
135
+ setSpec(spec: Spec | null): void;
130
136
  /** Merge additional state on top of the current model. */
131
137
  patchState(state: StateModel): void;
132
138
  /** Look up an element by id in the current spec. */
@@ -138,7 +144,7 @@ declare class JrStateStore {
138
144
  /** Current full state snapshot (non-reactive read). */
139
145
  snapshot(): StateModel;
140
146
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<JrStateStore, never>;
141
- static ɵprov: _angular_core.ɵɵInjectableDeclaration<JrStateStore>;
147
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<any>;
142
148
  }
143
149
 
144
150
  /** DI token for a default {@link JrRegistry} provided via `provideJsonRender`. */