ngx-json-render 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,821 @@
1
+ import * as _json_render_core from '@json-render/core';
2
+ import { ResolvedAction, ActionHandler, ActionBinding, Catalog, InferCatalogActions, InferActionParams, StateModel, InferCatalogComponents, UIElement, Spec, StateStore, ValidationFunction, ComputedFunction, DirectiveDefinition, ActionConfirm, DirectiveRegistry, ValidationResult, ValidationConfig, JsonPatch, FlatElement } from '@json-render/core';
3
+ export { ActionBinding, ActionHandler, JsonPatch, Spec, StateModel, StateStore, UIElement, VisibilityCondition, createStateStore, nestedToFlat } from '@json-render/core';
4
+ import * as zod from 'zod';
5
+ import * as zod_v4_core from 'zod/v4/core';
6
+ import * as _angular_core from '@angular/core';
7
+ import { Signal, Type, InjectionToken } from '@angular/core';
8
+
9
+ interface PendingConfirmation {
10
+ action: ResolvedAction;
11
+ handler: ActionHandler;
12
+ resolve: () => void;
13
+ reject: () => void;
14
+ }
15
+ /**
16
+ * Action dispatcher of a `<json-render>` subtree.
17
+ *
18
+ * Executes {@link ActionBinding}s: built-in actions (`setState`, `pushState`,
19
+ * `removeState`, `push`, `pop`, `validateForm`) are handled internally;
20
+ * everything else is routed to the host-provided `handlers` (or the
21
+ * `onAction` catch-all), honoring `confirm`, `onSuccess`, and `onError`.
22
+ */
23
+ declare class JsonRenderActionsService {
24
+ private readonly root;
25
+ private readonly state;
26
+ private readonly validation;
27
+ private readonly extraHandlers;
28
+ private readonly _loadingActions;
29
+ private readonly _pendingConfirmation;
30
+ /** Names of actions currently executing. */
31
+ readonly loadingActions: Signal<ReadonlySet<string>>;
32
+ /** The confirmation currently awaiting user input, if any. */
33
+ readonly pendingConfirmation: Signal<PendingConfirmation | null>;
34
+ /** All registered handlers (host handlers + runtime registrations). */
35
+ get handlers(): Record<string, ActionHandler>;
36
+ /** Register an additional action handler at runtime. */
37
+ registerHandler(name: string, handler: ActionHandler): void;
38
+ /** Execute an action binding. */
39
+ execute(binding: ActionBinding): Promise<void>;
40
+ /** Confirm the pending confirmation dialog. */
41
+ confirm(): void;
42
+ /** Cancel the pending confirmation dialog. */
43
+ cancel(): void;
44
+ private lookupHandler;
45
+ private runHandler;
46
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<JsonRenderActionsService, never>;
47
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<JsonRenderActionsService>;
48
+ }
49
+ /** Inject the actions service of the nearest `<json-render>` renderer. */
50
+ declare function injectActions(): JsonRenderActionsService;
51
+ /**
52
+ * Convenience helper for executing a fixed action binding, mirroring
53
+ * `useAction` from the other renderers.
54
+ */
55
+ declare function injectAction(binding: ActionBinding): {
56
+ execute: () => Promise<void>;
57
+ isLoading: Signal<boolean>;
58
+ };
59
+
60
+ /**
61
+ * State setter function for updating application state.
62
+ * Matches the `SetState` contract of the other json-render renderers
63
+ * (React/Vue/Solid): an updater receives the previous state object and
64
+ * returns the next one.
65
+ */
66
+ type SetState = (updater: (prev: Record<string, unknown>) => Record<string, unknown>) => void;
67
+ /** A single state change notification (JSON Pointer path + new value). */
68
+ interface StateChange {
69
+ path: string;
70
+ value: unknown;
71
+ }
72
+ /**
73
+ * Handle returned by the `on()` function for a specific event.
74
+ * Provides metadata about the event binding and a method to fire it.
75
+ *
76
+ * @example
77
+ * ```ts
78
+ * const press = ctx.on('press');
79
+ * if (press.shouldPreventDefault) e.preventDefault();
80
+ * press.emit();
81
+ * ```
82
+ */
83
+ interface EventHandle {
84
+ /** Fire the event (resolve action bindings) */
85
+ emit: () => void;
86
+ /** Whether any binding requested preventDefault */
87
+ shouldPreventDefault: boolean;
88
+ /** Whether any handler is bound to this event */
89
+ bound: boolean;
90
+ }
91
+ /**
92
+ * Context available to catalog components via `injectRenderContext()`.
93
+ *
94
+ * This is the Angular equivalent of the render props that the React/Vue/Solid
95
+ * renderers pass to registered components: resolved props, event emitters,
96
+ * two-way binding paths, and the loading flag — exposed as signals so catalog
97
+ * components stay fine-grained reactive while the spec streams in.
98
+ */
99
+ interface RenderContext<P = Record<string, unknown>> {
100
+ /** The element being rendered, with all prop expressions resolved. */
101
+ element: Signal<UIElement<string, P>>;
102
+ /** Resolved component props (shortcut for `element().props`). */
103
+ props: Signal<P>;
104
+ /**
105
+ * Emit a named event. The renderer resolves the event to action binding(s)
106
+ * from the element's `on` field.
107
+ */
108
+ emit: (event: string) => void;
109
+ /** Get an event handle with metadata (shouldPreventDefault, bound). */
110
+ on: (event: string) => EventHandle;
111
+ /**
112
+ * Two-way binding paths resolved from `$bindState` / `$bindItem`
113
+ * expressions. Maps prop name → absolute state path for write-back.
114
+ * `undefined` when no prop uses a binding expression.
115
+ */
116
+ bindings: Signal<Record<string, string> | undefined>;
117
+ /** Whether the spec is currently loading/streaming. */
118
+ loading: Signal<boolean>;
119
+ /**
120
+ * Write a value back to the state path bound to the given prop.
121
+ * No-op (with a dev warning) when the prop has no `$bindState`/`$bindItem`
122
+ * binding.
123
+ */
124
+ setBound: (prop: string, value: unknown) => void;
125
+ }
126
+ /**
127
+ * A registry entry: the Angular component to render for a catalog component
128
+ * type, plus optional slot metadata used for dev-mode warnings.
129
+ */
130
+ interface RegistryEntry {
131
+ component: Type<unknown>;
132
+ /** Slot names this component supports (from the catalog definition). */
133
+ slots?: string[];
134
+ }
135
+ /**
136
+ * Registry of component renderers: catalog type name → Angular component
137
+ * (or a {@link RegistryEntry} carrying slot metadata).
138
+ */
139
+ type ComponentRegistry = Record<string, Type<unknown> | RegistryEntry>;
140
+ /**
141
+ * Registry of all Angular components for a catalog. Keys are checked against
142
+ * the catalog's component names.
143
+ */
144
+ type Components<C extends Catalog> = {
145
+ [K in keyof InferCatalogComponents<C>]: Type<unknown>;
146
+ };
147
+ /**
148
+ * The repeat scope available to elements rendered inside a `repeat` block.
149
+ * Injected via {@link injectRepeatScope}.
150
+ */
151
+ interface RepeatScope {
152
+ /** The current repeat item. */
153
+ item: Signal<unknown>;
154
+ /** The current repeat array index. */
155
+ index: Signal<number>;
156
+ /** Absolute state path of the current item (e.g. `/todos/0`). */
157
+ basePath: Signal<string>;
158
+ }
159
+ /**
160
+ * Action handler function type for {@link defineRegistry}.
161
+ *
162
+ * @example
163
+ * ```ts
164
+ * const viewCustomers: ActionFn<typeof catalog, 'viewCustomers'> = async (params, setState) => {
165
+ * const data = await fetch('/api/customers').then((r) => r.json());
166
+ * setState((prev) => ({ ...prev, customers: data }));
167
+ * };
168
+ * ```
169
+ */
170
+ type ActionFn<C extends Catalog, K extends keyof InferCatalogActions<C>> = (params: InferActionParams<C, K> | undefined, setState: SetState, state: StateModel) => Promise<void>;
171
+ /** Registry of all action handlers for a catalog. */
172
+ type Actions<C extends Catalog> = {
173
+ [K in keyof InferCatalogActions<C>]: ActionFn<C, K>;
174
+ };
175
+ /**
176
+ * True when the catalog declares at least one action, false otherwise.
177
+ * Used by defineRegistry to conditionally require the `actions` field.
178
+ */
179
+ type CatalogHasActions<C extends Catalog> = [
180
+ InferCatalogActions<C>
181
+ ] extends [never] ? false : [keyof InferCatalogActions<C>] extends [never] ? false : true;
182
+
183
+ /**
184
+ * The state store of a `<json-render>` subtree.
185
+ *
186
+ * Wraps a core {@link StateStore} (either the internal in-memory store in
187
+ * uncontrolled mode, or an external store passed via the `store` input) and
188
+ * exposes the current state model as a signal.
189
+ *
190
+ * Inject it from catalog components or action handlers via
191
+ * {@link injectStateStore}.
192
+ */
193
+ declare class JsonRenderStateService {
194
+ private readonly root;
195
+ private readonly internalStore;
196
+ private readonly changeListeners;
197
+ private readonly _state;
198
+ /** The current state model as a signal. */
199
+ readonly state: Signal<StateModel>;
200
+ private readonly currentStore;
201
+ constructor();
202
+ /** Read a value by JSON Pointer path from the current state. */
203
+ get(path: string): unknown;
204
+ /** Write a value by JSON Pointer path and notify subscribers. */
205
+ set(path: string, value: unknown): void;
206
+ /** Write multiple values at once (single notification). */
207
+ update(updates: Record<string, unknown>): void;
208
+ /** Return the full state object (non-reactive read). */
209
+ getSnapshot(): StateModel;
210
+ /**
211
+ * Register a listener called with the list of changed paths whenever state
212
+ * is written through this service (element `watch` fields rely on this).
213
+ * Returns an unsubscribe function.
214
+ */
215
+ subscribeChanges(listener: (changes: StateChange[]) => void): () => void;
216
+ private notifyChanges;
217
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<JsonRenderStateService, never>;
218
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<JsonRenderStateService>;
219
+ }
220
+ /**
221
+ * Inject the state store of the nearest `<json-render>` renderer.
222
+ * Must be called from within the renderer's subtree (e.g. a catalog
223
+ * component) or a component that provides {@link JsonRenderStateService}.
224
+ */
225
+ declare function injectStateStore(): JsonRenderStateService;
226
+ /** Reactive read of a state value by JSON Pointer path. */
227
+ declare function injectStateValue<T>(path: string | (() => string)): Signal<T | undefined>;
228
+ /**
229
+ * Reactive two-way binding to a state path: a value signal plus a setter
230
+ * that writes back to the same path.
231
+ */
232
+ declare function injectStateBinding<T>(path: string | (() => string)): {
233
+ value: Signal<T | undefined>;
234
+ set: (value: T) => void;
235
+ };
236
+ /**
237
+ * Two-way bound prop helper for catalog components, mirroring `useBoundProp`
238
+ * from the other renderers: the value comes from the already-resolved prop,
239
+ * and the setter writes back to the bound state path (no-op if not bound).
240
+ *
241
+ * @example
242
+ * ```ts
243
+ * const ctx = injectRenderContext<{ value?: string }>();
244
+ * const bound = injectBoundProp<string>(
245
+ * () => ctx.props().value,
246
+ * () => ctx.bindings()?.['value'],
247
+ * );
248
+ * // template: <input [value]="bound.value() ?? ''" (input)="bound.set($any($event.target).value)" />
249
+ * ```
250
+ */
251
+ declare function injectBoundProp<T>(propValue: () => T | undefined, bindingPath: () => string | undefined): {
252
+ value: Signal<T | undefined>;
253
+ set: (value: T) => void;
254
+ };
255
+
256
+ /**
257
+ * Renders a json-render {@link Spec} using a registry of Angular components.
258
+ *
259
+ * The renderer owns the state store, action dispatcher, and validation state
260
+ * of its subtree (all injectable from catalog components). Pass an external
261
+ * {@link StateStore} via `store` for controlled mode or to share state across
262
+ * renderers.
263
+ *
264
+ * @example
265
+ * ```html
266
+ * <json-render
267
+ * [spec]="spec()"
268
+ * [registry]="registry"
269
+ * [handlers]="handlers"
270
+ * [loading]="isStreaming()"
271
+ * (stateChange)="onStateChange($event)"
272
+ * />
273
+ * ```
274
+ */
275
+ declare class JsonRenderer {
276
+ /** The UI spec to render (may be partial while streaming). */
277
+ readonly spec: _angular_core.InputSignal<Spec | null>;
278
+ /** Component registry mapping catalog type names to Angular components. */
279
+ readonly registry: _angular_core.InputSignal<ComponentRegistry>;
280
+ /** Whether the spec is currently loading/streaming. */
281
+ readonly loading: _angular_core.InputSignal<boolean>;
282
+ /** Fallback component for unknown types. */
283
+ readonly fallback: _angular_core.InputSignal<RegistryEntry | Type<unknown> | null>;
284
+ /**
285
+ * Initial state model (uncontrolled mode). Defaults to `spec.state`.
286
+ * Ignored when `store` is provided.
287
+ */
288
+ readonly state: _angular_core.InputSignal<StateModel | undefined>;
289
+ /** External state store (controlled mode). */
290
+ readonly store: _angular_core.InputSignal<StateStore | null>;
291
+ /** Action handlers by action name. */
292
+ readonly handlers: _angular_core.InputSignal<Record<string, ActionHandler> | undefined>;
293
+ /** Catch-all action handler for actions without a dedicated handler. */
294
+ readonly onAction: _angular_core.InputSignal<((name: string, params?: Record<string, unknown>) => unknown) | null>;
295
+ /** Navigation function used by `onSuccess: { navigate }` handlers. */
296
+ readonly navigate: _angular_core.InputSignal<((path: string) => void) | null>;
297
+ /** Custom validation functions. */
298
+ readonly validationFunctions: _angular_core.InputSignal<Record<string, ValidationFunction> | undefined>;
299
+ /** Named functions for `$computed` expressions in props. */
300
+ readonly functions: _angular_core.InputSignal<Record<string, ComputedFunction> | undefined>;
301
+ /** Custom directives for user-defined `$`-prefixed dynamic values. */
302
+ readonly directives: _angular_core.InputSignal<DirectiveDefinition<zod.ZodType<unknown, unknown, zod_v4_core.$ZodTypeInternals<unknown, unknown>>>[] | undefined>;
303
+ /** Emits state changes in uncontrolled mode. */
304
+ readonly stateChange: _angular_core.OutputEmitterRef<StateChange[]>;
305
+ /** The state store of this renderer (also injectable in the subtree). */
306
+ readonly stateStore: JsonRenderStateService;
307
+ /** The action dispatcher of this renderer. */
308
+ protected readonly actions: JsonRenderActionsService;
309
+ constructor();
310
+ protected readonly rootKey: _angular_core.Signal<string | null>;
311
+ protected readonly pendingConfirm: _angular_core.Signal<_json_render_core.ActionConfirm | null>;
312
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<JsonRenderer, never>;
313
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<JsonRenderer, "json-render", never, { "spec": { "alias": "spec"; "required": true; "isSignal": true; }; "registry": { "alias": "registry"; "required": true; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "fallback": { "alias": "fallback"; "required": false; "isSignal": true; }; "state": { "alias": "state"; "required": false; "isSignal": true; }; "store": { "alias": "store"; "required": false; "isSignal": true; }; "handlers": { "alias": "handlers"; "required": false; "isSignal": true; }; "onAction": { "alias": "onAction"; "required": false; "isSignal": true; }; "navigate": { "alias": "navigate"; "required": false; "isSignal": true; }; "validationFunctions": { "alias": "validationFunctions"; "required": false; "isSignal": true; }; "functions": { "alias": "functions"; "required": false; "isSignal": true; }; "directives": { "alias": "directives"; "required": false; "isSignal": true; }; }, { "stateChange": "stateChange"; }, never, never, true, never>;
314
+ }
315
+
316
+ /**
317
+ * Renders the children of the current element. Place it inside a catalog
318
+ * component's template where nested content should appear — like a
319
+ * `<router-outlet>` for the spec tree.
320
+ *
321
+ * - Default (no `slot`): renders `element.children`, honoring the element's
322
+ * `repeat` field (one pass per item of the referenced state array, with the
323
+ * proper repeat scope for `$item` / `$index` / `$bindItem` expressions).
324
+ * - With `slot`: renders the element keys of `element.slots[slot]`.
325
+ *
326
+ * @example
327
+ * ```html
328
+ * <div class="card">
329
+ * <h3>{{ ctx.props().title }}</h3>
330
+ * <jr-children />
331
+ * </div>
332
+ * ```
333
+ */
334
+ declare class JrChildren {
335
+ /** Named slot to render instead of the default children. */
336
+ readonly slot: _angular_core.InputSignal<string | null>;
337
+ private readonly ctx;
338
+ private readonly root;
339
+ private readonly state;
340
+ private readonly parentScope;
341
+ protected readonly childKeys: _angular_core.Signal<string[]>;
342
+ protected readonly repeat: _angular_core.Signal<{
343
+ statePath: _json_render_core.RepeatStatePath;
344
+ key?: string;
345
+ } | undefined>;
346
+ private readonly repeatBasePath;
347
+ protected readonly repeatItems: _angular_core.Signal<unknown[]>;
348
+ constructor();
349
+ protected itemPath(index: number): string;
350
+ protected trackItem(index: number, item: unknown): unknown;
351
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<JrChildren, never>;
352
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<JrChildren, "jr-children", never, { "slot": { "alias": "slot"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
353
+ }
354
+
355
+ /**
356
+ * Renders a single spec element: evaluates visibility, resolves prop
357
+ * expressions against the state model, wires event/watch bindings, and
358
+ * instantiates the catalog component with a {@link RenderContext} injector.
359
+ *
360
+ * @internal Used by `<json-render>` and `<jr-children>`.
361
+ */
362
+ declare class JrElement {
363
+ readonly elementKey: _angular_core.InputSignal<string>;
364
+ private readonly root;
365
+ private readonly state;
366
+ private readonly actions;
367
+ private readonly repeatScope;
368
+ private readonly devtoolsActive;
369
+ /** The raw (unresolved) element from the spec. */
370
+ readonly rawElement: Signal<UIElement<string, Record<string, unknown>> | undefined>;
371
+ /** Prop/visibility resolution context (state + repeat scope + extensions). */
372
+ private readonly resolutionCtx;
373
+ protected readonly visible: Signal<boolean>;
374
+ /** The element with all prop expressions resolved. */
375
+ readonly resolvedElement: Signal<UIElement<string, Record<string, unknown>> | undefined>;
376
+ /** Two-way binding paths ($bindState / $bindItem) by prop name. */
377
+ readonly bindings: Signal<Record<string, string> | undefined>;
378
+ private readonly entry;
379
+ protected readonly component: Signal<_angular_core.Type<unknown> | null>;
380
+ protected readonly devtoolsKey: Signal<string | null>;
381
+ private readonly renderCtx;
382
+ protected readonly outletInjector: _angular_core.DestroyableInjector;
383
+ constructor();
384
+ /**
385
+ * Resolution context with a live state snapshot, so `$state` references in
386
+ * later actions of a chain see mutations from earlier ones.
387
+ */
388
+ private liveResolutionCtx;
389
+ private emitEvent;
390
+ private eventHandle;
391
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<JrElement, never>;
392
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<JrElement, "jr-element", never, { "elementKey": { "alias": "elementKey"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
393
+ }
394
+
395
+ /**
396
+ * Provides the repeat scope (item, index, absolute base path) to the elements
397
+ * rendered inside a `repeat` block.
398
+ *
399
+ * @internal Used by `<jr-children>`; not intended for direct use.
400
+ */
401
+ declare class JrRepeatScope implements RepeatScope {
402
+ readonly item: _angular_core.InputSignal<unknown>;
403
+ readonly index: _angular_core.InputSignal<number>;
404
+ readonly basePath: _angular_core.InputSignal<string>;
405
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<JrRepeatScope, never>;
406
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<JrRepeatScope, "jr-repeat-scope", never, { "item": { "alias": "item"; "required": true; "isSignal": true; }; "index": { "alias": "index"; "required": true; "isSignal": true; }; "basePath": { "alias": "basePath"; "required": true; "isSignal": true; }; }, {}, never, ["*"], true, never>;
407
+ }
408
+
409
+ /**
410
+ * Default confirmation dialog shown for action bindings with a `confirm`
411
+ * field. Rendered automatically by `<json-render>`; can also be used
412
+ * standalone with a custom action flow.
413
+ */
414
+ declare class JrConfirmDialog {
415
+ readonly config: _angular_core.InputSignal<ActionConfirm>;
416
+ readonly confirmed: _angular_core.OutputEmitterRef<void>;
417
+ readonly cancelled: _angular_core.OutputEmitterRef<void>;
418
+ protected readonly isDanger: _angular_core.Signal<boolean>;
419
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<JrConfirmDialog, never>;
420
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<JrConfirmDialog, "jr-confirm-dialog", never, { "config": { "alias": "config"; "required": true; "isSignal": true; }; }, { "confirmed": "confirmed"; "cancelled": "cancelled"; }, never, never, true, never>;
421
+ }
422
+
423
+ /**
424
+ * The render context of the element currently being rendered.
425
+ * Provided by the renderer for every catalog component instance.
426
+ */
427
+ declare const RENDER_CONTEXT: InjectionToken<RenderContext<Record<string, unknown>>>;
428
+ /**
429
+ * The current repeat scope. Present only for elements rendered inside a
430
+ * `repeat` block.
431
+ */
432
+ declare const REPEAT_SCOPE: InjectionToken<RepeatScope>;
433
+ /**
434
+ * Inject the render context inside a catalog component.
435
+ *
436
+ * @example
437
+ * ```ts
438
+ * @Component({
439
+ * selector: 'app-button',
440
+ * template: `<button (click)="ctx.emit('press')">{{ ctx.props().label }}</button>`,
441
+ * })
442
+ * export class ButtonComponent {
443
+ * readonly ctx = injectRenderContext<{ label: string }>();
444
+ * }
445
+ * ```
446
+ */
447
+ declare function injectRenderContext<P = Record<string, unknown>>(): RenderContext<P>;
448
+ /**
449
+ * Inject the current repeat scope, or `null` when the component is not
450
+ * rendered inside a `repeat` block.
451
+ */
452
+ declare function injectRepeatScope(): RepeatScope | null;
453
+
454
+ /**
455
+ * Internal bridge between the `<json-render>` component's inputs and the
456
+ * renderer services / element tree. The renderer component replaces these
457
+ * signal references with its own input signals at construction time.
458
+ *
459
+ * @internal
460
+ */
461
+ declare class JsonRenderRootContext {
462
+ spec: Signal<Spec | null | undefined>;
463
+ registry: Signal<ComponentRegistry | undefined>;
464
+ loading: Signal<boolean>;
465
+ fallback: Signal<Type<unknown> | RegistryEntry | null | undefined>;
466
+ /** External store (controlled mode). */
467
+ store: Signal<StateStore | null | undefined>;
468
+ /** Initial state (uncontrolled mode); falls back to `spec.state`. */
469
+ initialState: Signal<StateModel>;
470
+ handlers: Signal<Record<string, ActionHandler> | undefined>;
471
+ onAction: Signal<((name: string, params?: Record<string, unknown>) => unknown) | null | undefined>;
472
+ navigate: Signal<((path: string) => void) | null | undefined>;
473
+ validationFunctions: Signal<Record<string, ValidationFunction> | undefined>;
474
+ functions: Signal<Record<string, ComputedFunction> | undefined>;
475
+ directiveRegistry: Signal<DirectiveRegistry | undefined>;
476
+ /** Emits uncontrolled-mode state changes to the renderer output. */
477
+ emitStateChange: (changes: StateChange[]) => void;
478
+ /** Resolve a registry entry (component + slot metadata) for a type. */
479
+ resolveEntry(type: string): RegistryEntry | undefined;
480
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<JsonRenderRootContext, never>;
481
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<JsonRenderRootContext>;
482
+ }
483
+
484
+ interface FieldValidationState {
485
+ touched: boolean;
486
+ validated: boolean;
487
+ result: ValidationResult | null;
488
+ }
489
+ /**
490
+ * Form validation state of a `<json-render>` subtree. Fields register their
491
+ * {@link ValidationConfig}; the built-in `validateForm` action validates all
492
+ * registered fields and writes the result to state.
493
+ */
494
+ declare class JsonRenderValidationService {
495
+ private readonly root;
496
+ private readonly state;
497
+ private readonly _fieldStates;
498
+ private readonly _fieldConfigs;
499
+ /** Validation state per registered field path. */
500
+ readonly fieldStates: Signal<Record<string, FieldValidationState>>;
501
+ get customFunctions(): Record<string, ValidationFunction>;
502
+ /** Register (or update) a field's validation config. */
503
+ registerField(path: string, config: ValidationConfig): void;
504
+ /** Validate a single field and record the result. */
505
+ validate(path: string, config: ValidationConfig): ValidationResult;
506
+ /** Mark a field as touched. */
507
+ touch(path: string): void;
508
+ /** Clear a field's validation state. */
509
+ clear(path: string): void;
510
+ /** Validate all registered fields. Returns whether all are valid. */
511
+ validateAll(): boolean;
512
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<JsonRenderValidationService, never>;
513
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<JsonRenderValidationService>;
514
+ }
515
+ /** Inject the validation service of the nearest `<json-render>` renderer. */
516
+ declare function injectValidation(): JsonRenderValidationService;
517
+ /**
518
+ * Field-level validation helper for catalog input components: registers the
519
+ * config and exposes the field's validation state as signals.
520
+ */
521
+ declare function injectFieldValidation(path: string | (() => string), config?: ValidationConfig | (() => ValidationConfig | undefined)): {
522
+ state: Signal<FieldValidationState>;
523
+ validate: () => ValidationResult;
524
+ touch: () => void;
525
+ clear: () => void;
526
+ errors: Signal<string[]>;
527
+ isValid: Signal<boolean>;
528
+ };
529
+
530
+ /**
531
+ * Result returned by {@link defineRegistry}.
532
+ */
533
+ interface DefineRegistryResult {
534
+ /** Component registry for `<json-render [registry]="...">`. */
535
+ registry: ComponentRegistry;
536
+ /**
537
+ * Create renderer-compatible handlers from the catalog actions.
538
+ * Accepts getter functions so handlers always read the latest
539
+ * state/setState.
540
+ */
541
+ handlers: (getSetState: () => SetState | undefined, getState: () => StateModel) => Record<string, (params: Record<string, unknown>) => Promise<void>>;
542
+ /**
543
+ * Execute an action by name imperatively
544
+ * (for use outside the renderer tree, e.g. initial state loading).
545
+ */
546
+ executeAction: (actionName: string, params: Record<string, unknown> | undefined, setState: SetState, state?: StateModel) => Promise<void>;
547
+ }
548
+ /**
549
+ * Options for defineRegistry.
550
+ *
551
+ * When the catalog declares actions, the `actions` field is required.
552
+ * When the catalog has no actions (or `actions: {}`), the field is optional.
553
+ */
554
+ type DefineRegistryOptions<C extends Catalog> = {
555
+ components?: Components<C>;
556
+ } & (CatalogHasActions<C> extends true ? {
557
+ actions: Actions<C>;
558
+ } : {
559
+ actions?: Actions<C>;
560
+ });
561
+ /**
562
+ * Create a registry from a catalog with components and/or actions.
563
+ *
564
+ * Component keys are type-checked against the catalog, and slot metadata from
565
+ * the catalog is carried into the registry for dev-mode slot warnings.
566
+ *
567
+ * @example
568
+ * ```ts
569
+ * const { registry, handlers } = defineRegistry(catalog, {
570
+ * components: { Card: CardComponent, Button: ButtonComponent },
571
+ * actions: {
572
+ * refresh: async (params, setState) => {
573
+ * setState((prev) => ({ ...prev, refreshedAt: Date.now() }));
574
+ * },
575
+ * },
576
+ * });
577
+ * ```
578
+ */
579
+ declare function defineRegistry<C extends Catalog>(catalog: C, options: DefineRegistryOptions<C>): DefineRegistryResult;
580
+ /**
581
+ * Build a {@link SetState} (whole-state updater) on top of a path-based
582
+ * store. The updater's result is diffed against the previous state and only
583
+ * changed leaves are written, preserving fine-grained reactivity.
584
+ *
585
+ * Works with both a core {@link StateStore} and the renderer's injected
586
+ * `JsonRenderStateService`.
587
+ */
588
+ declare function createStoreSetState(store: Pick<StateStore, 'getSnapshot' | 'update'>): SetState;
589
+
590
+ /**
591
+ * Token usage metadata from AI generation.
592
+ */
593
+ interface TokenUsage {
594
+ promptTokens: number;
595
+ completionTokens: number;
596
+ totalTokens: number;
597
+ }
598
+ /**
599
+ * Apply an RFC 6902 JSON patch to the current spec, returning a new spec
600
+ * object (structural sharing for untouched elements).
601
+ * Supports add, remove, replace, move, copy, and test operations.
602
+ */
603
+ declare function applyPatch(spec: Spec, patch: JsonPatch): Spec;
604
+ /**
605
+ * Options for {@link injectUIStream}.
606
+ */
607
+ interface UIStreamOptions {
608
+ /** API endpoint */
609
+ api: string;
610
+ /** Callback when complete */
611
+ onComplete?: (spec: Spec) => void;
612
+ /** Callback on error */
613
+ onError?: (error: Error) => void;
614
+ }
615
+ /**
616
+ * Return type for {@link injectUIStream}.
617
+ */
618
+ interface UIStreamReturn {
619
+ /** Current UI spec */
620
+ readonly spec: Signal<Spec | null>;
621
+ /** Whether currently streaming */
622
+ readonly isStreaming: Signal<boolean>;
623
+ /** Error if any */
624
+ readonly error: Signal<Error | null>;
625
+ /** Token usage from the last generation */
626
+ readonly usage: Signal<TokenUsage | null>;
627
+ /** Raw JSONL lines received from the stream (JSON patch lines) */
628
+ readonly rawLines: Signal<string[]>;
629
+ /** Send a prompt to generate UI */
630
+ send: (prompt: string, context?: Record<string, unknown>) => Promise<void>;
631
+ /** Clear the current spec */
632
+ clear: () => void;
633
+ }
634
+ /**
635
+ * Streaming UI generation. POSTs `{ prompt, context, currentSpec }` to the
636
+ * endpoint and progressively applies the returned JSONL patch stream to the
637
+ * `spec` signal, so partial UIs render as they arrive.
638
+ *
639
+ * Must be called in an injection context (aborts in-flight requests on
640
+ * destroy).
641
+ *
642
+ * @example
643
+ * ```ts
644
+ * export class GeneratePage {
645
+ * readonly ui = injectUIStream({ api: '/api/generate' });
646
+ * }
647
+ * // template:
648
+ * // <json-render [spec]="ui.spec()" [registry]="registry" [loading]="ui.isStreaming()" />
649
+ * ```
650
+ */
651
+ declare function injectUIStream(options: UIStreamOptions): UIStreamReturn;
652
+ /**
653
+ * Convert a flat element list to a Spec.
654
+ * Input elements use key/parentKey to establish identity and relationships.
655
+ * Output spec uses the map-based format where key is the map entry key
656
+ * and parent-child relationships are expressed through children arrays.
657
+ */
658
+ declare function flatToTree(elements: FlatElement[]): Spec;
659
+ /**
660
+ * A single part from the AI SDK's `message.parts` array. This is a minimal
661
+ * structural type so that library helpers do not depend on the AI SDK.
662
+ */
663
+ interface DataPart {
664
+ type: string;
665
+ text?: string;
666
+ data?: unknown;
667
+ }
668
+ /**
669
+ * Build a `Spec` by replaying all spec data parts from a message's
670
+ * parts array (AI SDK `UIMessage.parts`). Returns `null` if no spec data
671
+ * parts are present.
672
+ */
673
+ declare function buildSpecFromParts(parts: DataPart[]): Spec | null;
674
+ /**
675
+ * Extract and join all text content from a message's parts array.
676
+ */
677
+ declare function getTextFromParts(parts: DataPart[]): string;
678
+ /**
679
+ * Extract both the json-render spec and the text content from a message's
680
+ * parts array, as memoized signals. Angular counterpart of
681
+ * `useJsonRenderMessage` from the other renderers.
682
+ *
683
+ * @example
684
+ * ```ts
685
+ * readonly msg = jsonRenderMessage(() => this.message().parts);
686
+ * // template: @if (msg.hasSpec()) { <json-render [spec]="msg.spec()" ... /> }
687
+ * ```
688
+ */
689
+ declare function jsonRenderMessage(parts: Signal<DataPart[]> | (() => DataPart[])): {
690
+ spec: Signal<Spec | null>;
691
+ text: Signal<string>;
692
+ hasSpec: Signal<boolean>;
693
+ };
694
+ /**
695
+ * A single message in the chat, which may contain text, a rendered UI spec,
696
+ * or both.
697
+ */
698
+ interface ChatMessage {
699
+ /** Unique message ID */
700
+ id: string;
701
+ /** Who sent this message */
702
+ role: 'user' | 'assistant';
703
+ /** Text content (conversational prose) */
704
+ text: string;
705
+ /** json-render Spec built from JSONL patches (null if no UI was generated) */
706
+ spec: Spec | null;
707
+ }
708
+ /**
709
+ * Options for {@link injectChatUI}.
710
+ */
711
+ interface ChatUIOptions {
712
+ /** API endpoint that accepts `{ messages: Array<{ role, content }> }` and returns a text stream */
713
+ api: string;
714
+ /** Callback when streaming completes for a message */
715
+ onComplete?: (message: ChatMessage) => void;
716
+ /** Callback on error */
717
+ onError?: (error: Error) => void;
718
+ }
719
+ /**
720
+ * Return type for {@link injectChatUI}.
721
+ */
722
+ interface ChatUIReturn {
723
+ /** All messages in the conversation */
724
+ readonly messages: Signal<ChatMessage[]>;
725
+ /** Whether currently streaming an assistant response */
726
+ readonly isStreaming: Signal<boolean>;
727
+ /** Error from the last request, if any */
728
+ readonly error: Signal<Error | null>;
729
+ /** Send a user message */
730
+ send: (text: string) => Promise<void>;
731
+ /** Clear all messages and reset the conversation */
732
+ clear: () => void;
733
+ }
734
+ /**
735
+ * Chat + GenUI: manages a multi-turn conversation where each assistant
736
+ * message can contain both conversational text and a json-render UI spec.
737
+ * The full message history is sent to the endpoint and the streamed response
738
+ * is split into text lines and JSONL patch lines.
739
+ *
740
+ * Must be called in an injection context.
741
+ */
742
+ declare function injectChatUI(options: ChatUIOptions): ChatUIReturn;
743
+
744
+ /**
745
+ * The schema for ngx-json-render.
746
+ *
747
+ * Defines:
748
+ * - Spec: A flat tree of elements with keys, types, props, and children references
749
+ * - Catalog: Components with props schemas, and optional actions
750
+ *
751
+ * This is the same spec grammar the other json-render renderers use, so
752
+ * catalogs and specs are portable across frameworks.
753
+ */
754
+ declare const schema: _json_render_core.Schema<{
755
+ spec: _json_render_core.SchemaType<"object", {
756
+ /** Root element key */
757
+ root: _json_render_core.SchemaType<"string", unknown>;
758
+ /** Flat map of elements by key */
759
+ elements: _json_render_core.SchemaType<"record", _json_render_core.SchemaType<"object", {
760
+ /** Component type from catalog */
761
+ type: _json_render_core.SchemaType<"ref", string>;
762
+ /** Component props */
763
+ props: _json_render_core.SchemaType<"propsOf", string>;
764
+ /** Child element keys (flat reference) */
765
+ children: _json_render_core.SchemaType<"array", _json_render_core.SchemaType<"string", unknown>>;
766
+ /** Visibility condition */
767
+ visible: {
768
+ optional: true;
769
+ kind: "any";
770
+ inner?: unknown;
771
+ };
772
+ /** Repeat children from a state array */
773
+ repeat: {
774
+ optional: true;
775
+ kind: "any";
776
+ inner?: unknown;
777
+ };
778
+ }>>;
779
+ }>;
780
+ catalog: _json_render_core.SchemaType<"object", {
781
+ /** Component definitions */
782
+ components: _json_render_core.SchemaType<"map", {
783
+ /** Zod schema for component props */
784
+ props: _json_render_core.SchemaType<"zod", unknown>;
785
+ /** Slots for this component. Use ['default'] for children, or named slots like ['header', 'footer'] */
786
+ slots: _json_render_core.SchemaType<"array", _json_render_core.SchemaType<"string", unknown>>;
787
+ /** Description for AI generation hints */
788
+ description: _json_render_core.SchemaType<"string", unknown>;
789
+ /** Example prop values used in prompt examples (auto-generated from Zod schema if omitted) */
790
+ example: _json_render_core.SchemaType<"any", unknown>;
791
+ }>;
792
+ /** Action definitions (optional) */
793
+ actions: _json_render_core.SchemaType<"map", {
794
+ /** Zod schema for action params */
795
+ params: _json_render_core.SchemaType<"zod", unknown>;
796
+ /** Description for AI generation hints */
797
+ description: _json_render_core.SchemaType<"string", unknown>;
798
+ }>;
799
+ }>;
800
+ }>;
801
+ /**
802
+ * Type for the Angular schema
803
+ */
804
+ type AngularSchema = typeof schema;
805
+ /**
806
+ * Infer the spec type from a catalog
807
+ */
808
+ type AngularSpec<TCatalog> = typeof schema extends {
809
+ createCatalog: (catalog: TCatalog) => {
810
+ _specType: infer S;
811
+ };
812
+ } ? S : never;
813
+
814
+ /**
815
+ * Reactive mirror of the json-render devtools-active flag. When active, the
816
+ * renderer wraps each element with a `data-jr-key` attribute for the picker.
817
+ */
818
+ declare function injectDevtoolsActive(): Signal<boolean>;
819
+
820
+ export { JrChildren, JrConfirmDialog, JrElement, JrRepeatScope, JsonRenderActionsService, JsonRenderRootContext, JsonRenderStateService, JsonRenderValidationService, JsonRenderer, RENDER_CONTEXT, REPEAT_SCOPE, applyPatch, buildSpecFromParts, createStoreSetState, defineRegistry, flatToTree, getTextFromParts, injectAction, injectActions, injectBoundProp, injectChatUI, injectDevtoolsActive, injectFieldValidation, injectRenderContext, injectRepeatScope, injectStateBinding, injectStateStore, injectStateValue, injectUIStream, injectValidation, jsonRenderMessage, schema };
821
+ export type { ActionFn, Actions, AngularSchema, AngularSpec, CatalogHasActions, ChatMessage, ChatUIOptions, ChatUIReturn, ComponentRegistry, Components, DataPart, DefineRegistryResult, EventHandle, FieldValidationState, PendingConfirmation, RegistryEntry, RenderContext, RepeatScope, SetState, StateChange, TokenUsage, UIStreamOptions, UIStreamReturn };