ngx-json-render 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +338 -12
- package/fesm2022/ngx-json-render-testing.mjs +389 -0
- package/fesm2022/ngx-json-render-testing.mjs.map +1 -0
- package/fesm2022/ngx-json-render.mjs +491 -94
- package/fesm2022/ngx-json-render.mjs.map +1 -1
- package/package.json +5 -1
- package/types/ngx-json-render-testing.d.ts +197 -0
- package/types/ngx-json-render.d.ts +244 -10
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngx-json-render",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Angular renderer for json-render — stream AI-generated JSON specs into real Angular components with signals.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"angular",
|
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
".": {
|
|
41
41
|
"types": "./types/ngx-json-render.d.ts",
|
|
42
42
|
"default": "./fesm2022/ngx-json-render.mjs"
|
|
43
|
+
},
|
|
44
|
+
"./testing": {
|
|
45
|
+
"types": "./types/ngx-json-render-testing.d.ts",
|
|
46
|
+
"default": "./fesm2022/ngx-json-render-testing.mjs"
|
|
43
47
|
}
|
|
44
48
|
},
|
|
45
49
|
"type": "module"
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { Spec, StateStore, ActionHandler, ComputedFunction, ValidationFunction, DirectiveDefinition } from '@json-render/core';
|
|
2
|
+
import { Type, Provider } from '@angular/core';
|
|
3
|
+
import { ComponentFixture } from '@angular/core/testing';
|
|
4
|
+
import { ComponentRegistry, StateModel, RegistryEntry, JsonRenderer, JsonRenderStateService, JsonRenderValidationService, StateChange } from 'ngx-json-render';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The lines of one recorded response body, in the order the server sent them,
|
|
8
|
+
* without their trailing newlines.
|
|
9
|
+
*/
|
|
10
|
+
type Recording = readonly string[];
|
|
11
|
+
/**
|
|
12
|
+
* What to replay for a request. Either one recording for every request, a
|
|
13
|
+
* lookup by prompt, or a function for anything more involved.
|
|
14
|
+
*/
|
|
15
|
+
type Recordings = Recording | Readonly<Record<string, Recording>> | ((prompt: string, body: Readonly<Record<string, unknown>>) => Recording | undefined);
|
|
16
|
+
/** An error the recorded server answers with instead of a stream. */
|
|
17
|
+
interface RecordedFailure {
|
|
18
|
+
/** HTTP status. Defaults to 500. */
|
|
19
|
+
status?: number;
|
|
20
|
+
/** The `message` field of the JSON error body. */
|
|
21
|
+
message?: string;
|
|
22
|
+
}
|
|
23
|
+
/** Options for {@link recordedTransport}. */
|
|
24
|
+
interface RecordedTransportOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Milliseconds between lines — the pace a model would emit them at.
|
|
27
|
+
* Defaults to 0, which still yields to the event loop between lines, so a
|
|
28
|
+
* test can observe the stream in flight.
|
|
29
|
+
*/
|
|
30
|
+
delayMs?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Answer with an error instead of a stream. A function is called per
|
|
33
|
+
* request, so a test can turn the failure on and off between sends.
|
|
34
|
+
*/
|
|
35
|
+
fail?: RecordedFailure | (() => RecordedFailure | null | undefined);
|
|
36
|
+
/**
|
|
37
|
+
* Pull the prompt out of the request body. The default reads `prompt`
|
|
38
|
+
* (what `injectUIStream` sends) and falls back to the last message's
|
|
39
|
+
* content (what `injectChatUI` sends).
|
|
40
|
+
*/
|
|
41
|
+
promptOf?: (body: Readonly<Record<string, unknown>>) => string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* A `fetch` that answers the streaming hooks from a recording instead of a
|
|
45
|
+
* server.
|
|
46
|
+
*
|
|
47
|
+
* Both {@link injectUIStream} and {@link injectChatUI} take any function with
|
|
48
|
+
* fetch's shape, so a test runs the real client — request body, streamed
|
|
49
|
+
* lines, usage metadata, abort on supersede — with no server, no API key and
|
|
50
|
+
* no global patched behind the test's back. Only the transport is recorded.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* const ui = injectUIStream({
|
|
55
|
+
* api: '/api/generate',
|
|
56
|
+
* fetch: recordedTransport(specStream(spec)),
|
|
57
|
+
* });
|
|
58
|
+
* ```
|
|
59
|
+
*
|
|
60
|
+
* @example Different answers per prompt, at a visible pace:
|
|
61
|
+
* ```ts
|
|
62
|
+
* recordedTransport(
|
|
63
|
+
* { 'a dashboard': specStream(dashboard), 'a form': specStream(form) },
|
|
64
|
+
* { delayMs: 20 },
|
|
65
|
+
* );
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
declare function recordedTransport(recordings: Recordings, options?: RecordedTransportOptions): typeof globalThis.fetch;
|
|
69
|
+
/**
|
|
70
|
+
* The JSONL patch lines a model would emit to build `spec`: the root, then
|
|
71
|
+
* the state, then one line per element in declaration order.
|
|
72
|
+
*
|
|
73
|
+
* Recording a stream by hand means writing patches by hand; this writes them
|
|
74
|
+
* from the spec a test already has, so the test says what it renders rather
|
|
75
|
+
* than how the wire spells it.
|
|
76
|
+
*/
|
|
77
|
+
declare function specStream(spec: Spec): string[];
|
|
78
|
+
/**
|
|
79
|
+
* The usage metadata line `injectUIStream` reads into its `usage` signal.
|
|
80
|
+
* Append it to a recording to exercise that path.
|
|
81
|
+
*/
|
|
82
|
+
declare function usageLine(usage: {
|
|
83
|
+
promptTokens?: number;
|
|
84
|
+
completionTokens?: number;
|
|
85
|
+
totalTokens?: number;
|
|
86
|
+
}): string;
|
|
87
|
+
|
|
88
|
+
/** One action the spec asked for, in the order it was dispatched. */
|
|
89
|
+
interface DispatchedAction {
|
|
90
|
+
name: string;
|
|
91
|
+
params?: Record<string, unknown>;
|
|
92
|
+
}
|
|
93
|
+
/** Options for {@link renderSpec}. Everything but `registry` is optional. */
|
|
94
|
+
interface RenderSpecOptions {
|
|
95
|
+
/** Catalog type name → Angular component. The one required option. */
|
|
96
|
+
registry: ComponentRegistry;
|
|
97
|
+
/** Initial state model. Defaults to the spec's own `state`. */
|
|
98
|
+
state?: StateModel;
|
|
99
|
+
/** External store, for controlled mode. */
|
|
100
|
+
store?: StateStore | null;
|
|
101
|
+
/** Action handlers by name. Dispatches are recorded either way. */
|
|
102
|
+
handlers?: Record<string, ActionHandler>;
|
|
103
|
+
/** Catch-all handler for actions with no dedicated handler. */
|
|
104
|
+
onAction?: (name: string, params?: Record<string, unknown>) => unknown;
|
|
105
|
+
/** Navigation function for `onSuccess: { navigate }`. */
|
|
106
|
+
navigate?: (path: string) => void;
|
|
107
|
+
/** Fallback component for unknown types. */
|
|
108
|
+
fallback?: Type<unknown> | RegistryEntry | null;
|
|
109
|
+
/** Named functions for `$computed` props. */
|
|
110
|
+
functions?: Record<string, ComputedFunction>;
|
|
111
|
+
/** Custom validation functions. */
|
|
112
|
+
validationFunctions?: Record<string, ValidationFunction>;
|
|
113
|
+
/** Custom directives for `$`-prefixed values. */
|
|
114
|
+
directives?: DirectiveDefinition[];
|
|
115
|
+
/** Render as if the spec were still streaming. */
|
|
116
|
+
loading?: boolean;
|
|
117
|
+
/** Extra TestBed providers, e.g. a `JR_CONFIRM_DIALOG` replacement. */
|
|
118
|
+
providers?: Provider[];
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A rendered spec, and the handful of things a test does to one.
|
|
122
|
+
*
|
|
123
|
+
* `fixture` and `element` are there for everything this does not cover — the
|
|
124
|
+
* harness is a shortcut, not a wall.
|
|
125
|
+
*/
|
|
126
|
+
interface SpecHarness {
|
|
127
|
+
/** The fixture of the host that owns the `<json-render>`. */
|
|
128
|
+
readonly fixture: ComponentFixture<unknown>;
|
|
129
|
+
/** The host element, the root of the rendered DOM. */
|
|
130
|
+
readonly element: HTMLElement;
|
|
131
|
+
/** The renderer instance. */
|
|
132
|
+
readonly renderer: JsonRenderer;
|
|
133
|
+
/** The renderer's state store. */
|
|
134
|
+
readonly store: JsonRenderStateService;
|
|
135
|
+
/** The renderer's validation service. */
|
|
136
|
+
readonly validation: JsonRenderValidationService;
|
|
137
|
+
/** Every action the spec dispatched, handled or not, in order. */
|
|
138
|
+
readonly dispatched: readonly DispatchedAction[];
|
|
139
|
+
/** Every state change the renderer emitted, flattened, in order. */
|
|
140
|
+
readonly changes: readonly StateChange[];
|
|
141
|
+
/** Render a different spec (a later streaming frame, say). */
|
|
142
|
+
setSpec(spec: Spec | null): Promise<void>;
|
|
143
|
+
/** Flip the loading flag catalog components see. */
|
|
144
|
+
setLoading(loading: boolean): Promise<void>;
|
|
145
|
+
/** Let effects, promises and change detection finish. */
|
|
146
|
+
settle(): Promise<void>;
|
|
147
|
+
/** Read a state path, e.g. `/form/email`. */
|
|
148
|
+
read(path: string): unknown;
|
|
149
|
+
/** Write a state path and settle. */
|
|
150
|
+
write(path: string, value: unknown): Promise<void>;
|
|
151
|
+
/** The whole state model. */
|
|
152
|
+
state(): StateModel;
|
|
153
|
+
/** Trimmed text of the first match, or of everything when given nothing. */
|
|
154
|
+
text(selector?: string): string;
|
|
155
|
+
/** Trimmed text of every match. */
|
|
156
|
+
texts(selector: string): string[];
|
|
157
|
+
/** The first match. Throws when there is none. */
|
|
158
|
+
find<T extends HTMLElement>(selector: string): T;
|
|
159
|
+
/** Every match, possibly none. */
|
|
160
|
+
findAll<T extends HTMLElement>(selector: string): T[];
|
|
161
|
+
/** Click an element (or the first match of a selector) and settle. */
|
|
162
|
+
click(target: string | Element): Promise<void>;
|
|
163
|
+
/** Set an input's value, fire `input`, and settle. */
|
|
164
|
+
fill(target: string | Element, value: string): Promise<void>;
|
|
165
|
+
/** Destroy the fixture. Rarely needed — TestBed resets between tests. */
|
|
166
|
+
destroy(): void;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Render a spec against a registry and hand back the few things a test does
|
|
170
|
+
* to it.
|
|
171
|
+
*
|
|
172
|
+
* Testing a catalog component otherwise means a host component, a TestBed
|
|
173
|
+
* module, a zoneless provider, two settle passes and a `querySelector` — the
|
|
174
|
+
* same twenty lines in every project that adopts this package, none of them
|
|
175
|
+
* about the component under test.
|
|
176
|
+
*
|
|
177
|
+
* @example
|
|
178
|
+
* ```ts
|
|
179
|
+
* const ui = await renderSpec(
|
|
180
|
+
* {
|
|
181
|
+
* root: 'save',
|
|
182
|
+
* elements: {
|
|
183
|
+
* save: { type: 'Button', props: { label: 'Save' }, on: { press: { action: 'save' } } },
|
|
184
|
+
* },
|
|
185
|
+
* },
|
|
186
|
+
* { registry: { Button: MyButton } },
|
|
187
|
+
* );
|
|
188
|
+
*
|
|
189
|
+
* expect(ui.text('button')).toBe('Save');
|
|
190
|
+
* await ui.click('button');
|
|
191
|
+
* expect(ui.dispatched).toEqual([{ name: 'save', params: {} }]);
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
declare function renderSpec(spec: Spec | null, options: RenderSpecOptions): Promise<SpecHarness>;
|
|
195
|
+
|
|
196
|
+
export { recordedTransport, renderSpec, specStream, usageLine };
|
|
197
|
+
export type { DispatchedAction, RecordedFailure, RecordedTransportOptions, Recording, Recordings, RenderSpecOptions, SpecHarness };
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as _json_render_core from '@json-render/core';
|
|
2
|
-
import { ResolvedAction, ActionHandler, ActionBinding, Catalog, InferCatalogActions, InferActionParams, StateModel, InferCatalogComponents, UIElement,
|
|
3
|
-
export { ActionBinding, ActionHandler, JsonPatch, Spec, StateModel, StateStore, UIElement, VisibilityCondition, createStateStore, nestedToFlat } from '@json-render/core';
|
|
2
|
+
import { ResolvedAction, ActionHandler, ActionBinding, Spec, SpecIssue, Catalog, InferCatalogActions, InferActionParams, StateModel, InferCatalogComponents, UIElement, StateStore, ValidationFunction, ComputedFunction, DirectiveDefinition, ActionConfirm, DirectiveRegistry, ValidationResult, ValidationConfig, JsonPatch, FlatElement } from '@json-render/core';
|
|
3
|
+
export { ActionBinding, ActionHandler, JsonPatch, Spec, SpecIssue, StateModel, StateStore, UIElement, VisibilityCondition, autoFixSpec, createStateStore, formatSpecIssues, nestedToFlat, validateSpec } from '@json-render/core';
|
|
4
4
|
import * as zod from 'zod';
|
|
5
5
|
import * as zod_v4_core from 'zod/v4/core';
|
|
6
6
|
import * as _angular_core from '@angular/core';
|
|
7
7
|
import { Signal, Type, InjectionToken } from '@angular/core';
|
|
8
|
+
import * as ngx_json_render from 'ngx-json-render';
|
|
8
9
|
|
|
9
10
|
interface PendingConfirmation {
|
|
10
11
|
action: ResolvedAction;
|
|
@@ -12,13 +13,15 @@ interface PendingConfirmation {
|
|
|
12
13
|
resolve: () => void;
|
|
13
14
|
reject: () => void;
|
|
14
15
|
}
|
|
16
|
+
/** True for the rejection produced by cancelling a confirmation dialog. */
|
|
17
|
+
declare function isActionCancelled(error: unknown): boolean;
|
|
15
18
|
/**
|
|
16
19
|
* Action dispatcher of a `<json-render>` subtree.
|
|
17
20
|
*
|
|
18
21
|
* Executes {@link ActionBinding}s: built-in actions (`setState`, `pushState`,
|
|
19
|
-
* `removeState`, `push`, `pop`, `validateForm`) are handled
|
|
20
|
-
* everything else is routed to the host-provided `handlers` (or
|
|
21
|
-
* `onAction` catch-all), honoring `confirm`, `onSuccess`, and `onError`.
|
|
22
|
+
* `removeState`, `push`, `pop`, `validateForm`, `submitForm`) are handled
|
|
23
|
+
* internally; everything else is routed to the host-provided `handlers` (or
|
|
24
|
+
* the `onAction` catch-all), honoring `confirm`, `onSuccess`, and `onError`.
|
|
22
25
|
*/
|
|
23
26
|
declare class JsonRenderActionsService {
|
|
24
27
|
private readonly root;
|
|
@@ -41,6 +44,15 @@ declare class JsonRenderActionsService {
|
|
|
41
44
|
confirm(): void;
|
|
42
45
|
/** Cancel the pending confirmation dialog. */
|
|
43
46
|
cancel(): void;
|
|
47
|
+
/**
|
|
48
|
+
* Validate every registered field and write `{ valid, errors }` to the
|
|
49
|
+
* action's `statePath` (default `/formValidation`). Returns null when there
|
|
50
|
+
* is no validation service to ask.
|
|
51
|
+
*
|
|
52
|
+
* Shared by `validateForm` and `submitForm`: the two must report the same
|
|
53
|
+
* thing about the same form, and one implementation is how that stays true.
|
|
54
|
+
*/
|
|
55
|
+
private checkForm;
|
|
44
56
|
private lookupHandler;
|
|
45
57
|
private runHandler;
|
|
46
58
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<JsonRenderActionsService, never>;
|
|
@@ -57,6 +69,41 @@ declare function injectAction(binding: ActionBinding): {
|
|
|
57
69
|
isLoading: Signal<boolean>;
|
|
58
70
|
};
|
|
59
71
|
|
|
72
|
+
/**
|
|
73
|
+
* How much a spec has to be trusted.
|
|
74
|
+
*
|
|
75
|
+
* - `off` — render whatever arrives. The default, and what every version
|
|
76
|
+
* before this one did.
|
|
77
|
+
* - `warn` — check the spec, apply the lossless fixes, report what is left,
|
|
78
|
+
* render it anyway.
|
|
79
|
+
* - `strict` — the same check, but an error means the spec does not render.
|
|
80
|
+
*/
|
|
81
|
+
type SpecValidationMode = 'off' | 'warn' | 'strict';
|
|
82
|
+
/** What {@link checkSpec} found, and the spec that should render. */
|
|
83
|
+
interface SpecCheck {
|
|
84
|
+
/**
|
|
85
|
+
* The spec to render: the input with lossless fixes applied, or the input
|
|
86
|
+
* untouched when validation is off.
|
|
87
|
+
*/
|
|
88
|
+
readonly spec: Spec | null;
|
|
89
|
+
/** Structural issues left after the fixes. */
|
|
90
|
+
readonly issues: readonly SpecIssue[];
|
|
91
|
+
/** Descriptions of the fixes that were applied. */
|
|
92
|
+
readonly fixes: readonly string[];
|
|
93
|
+
/** Whether an issue of `error` severity survived. */
|
|
94
|
+
readonly hasErrors: boolean;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Check a spec the way `validate` asks for.
|
|
98
|
+
*
|
|
99
|
+
* Only lossless fixes are applied — `visible`, `on` and `repeat` that a model
|
|
100
|
+
* put inside `props`, which the renderer would otherwise ignore, moved back
|
|
101
|
+
* where they belong. The lossy ones prune content, and core's own guidance is
|
|
102
|
+
* to re-prompt rather than accept that; a renderer that silently deleted
|
|
103
|
+
* elements would be the worse failure.
|
|
104
|
+
*/
|
|
105
|
+
declare function checkSpec(spec: Spec | null, mode: SpecValidationMode): SpecCheck;
|
|
106
|
+
|
|
60
107
|
/**
|
|
61
108
|
* State setter function for updating application state.
|
|
62
109
|
* Matches the `SetState` contract of the other json-render renderers
|
|
@@ -281,6 +328,19 @@ declare class JsonRenderer {
|
|
|
281
328
|
readonly loading: _angular_core.InputSignal<boolean>;
|
|
282
329
|
/** Fallback component for unknown types. */
|
|
283
330
|
readonly fallback: _angular_core.InputSignal<RegistryEntry | Type<unknown> | null>;
|
|
331
|
+
/**
|
|
332
|
+
* Whether to check the spec's structure before rendering it, and what to do
|
|
333
|
+
* about what turns up. Off by default.
|
|
334
|
+
*
|
|
335
|
+
* `'warn'` reports the issues and renders anyway; `'strict'` renders nothing
|
|
336
|
+
* when the spec has errors. Both first apply the lossless fixes — `visible`,
|
|
337
|
+
* `on` and `repeat` misplaced inside `props` are moved back to the element,
|
|
338
|
+
* where they take effect instead of being ignored.
|
|
339
|
+
*
|
|
340
|
+
* The check is skipped while `loading` is true: a spec that is still
|
|
341
|
+
* arriving is expected to reference children that have not streamed in yet.
|
|
342
|
+
*/
|
|
343
|
+
readonly validate: _angular_core.InputSignal<SpecValidationMode>;
|
|
284
344
|
/**
|
|
285
345
|
* Initial state model (uncontrolled mode). Defaults to `spec.state`.
|
|
286
346
|
* Ignored when `store` is provided.
|
|
@@ -306,11 +366,31 @@ declare class JsonRenderer {
|
|
|
306
366
|
readonly stateStore: JsonRenderStateService;
|
|
307
367
|
/** The action dispatcher of this renderer. */
|
|
308
368
|
protected readonly actions: JsonRenderActionsService;
|
|
369
|
+
/**
|
|
370
|
+
* The spec as checked, and what the check found. While `loading`, the mode
|
|
371
|
+
* is forced off: the spec is still arriving, and the missing children it
|
|
372
|
+
* refers to are the normal state of a stream, not a defect.
|
|
373
|
+
*/
|
|
374
|
+
private readonly checked;
|
|
309
375
|
constructor();
|
|
310
376
|
protected readonly rootKey: _angular_core.Signal<string | null>;
|
|
311
377
|
protected readonly pendingConfirm: _angular_core.Signal<_json_render_core.ActionConfirm | null>;
|
|
378
|
+
/**
|
|
379
|
+
* The app's own dialog, if it provided one. Read once: which component
|
|
380
|
+
* answers a confirmation is an application decision, not something that
|
|
381
|
+
* changes between two confirmations.
|
|
382
|
+
*/
|
|
383
|
+
protected readonly customDialog: Type<unknown> | null;
|
|
384
|
+
private readonly injector;
|
|
385
|
+
/**
|
|
386
|
+
* A replacement dialog takes no inputs and emits no outputs — it injects
|
|
387
|
+
* everything it needs, the way catalog components do. That keeps the seam
|
|
388
|
+
* one token wide instead of a component contract the renderer would have to
|
|
389
|
+
* bind to.
|
|
390
|
+
*/
|
|
391
|
+
protected readonly confirmInjector: _angular_core.Signal<_angular_core.DestroyableInjector | null>;
|
|
312
392
|
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>;
|
|
393
|
+
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; }; "validate": { "alias": "validate"; "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
394
|
}
|
|
315
395
|
|
|
316
396
|
/**
|
|
@@ -416,21 +496,103 @@ declare class JrRepeatScope implements RepeatScope {
|
|
|
416
496
|
* Default confirmation dialog shown for action bindings with a `confirm`
|
|
417
497
|
* field. Rendered automatically by `<json-render>`; can also be used
|
|
418
498
|
* standalone with a custom action flow.
|
|
499
|
+
*
|
|
500
|
+
* It is a real dialog: `role="dialog"` with `aria-modal`, labelled by its own
|
|
501
|
+
* title and described by its message, focus moved in on open and returned to
|
|
502
|
+
* wherever it came from on close, Tab kept inside it and Escape cancelling.
|
|
503
|
+
* A generated UI can ask for confirmation of something destructive, so a
|
|
504
|
+
* keyboard or screen-reader user has to be able to answer it.
|
|
505
|
+
*
|
|
506
|
+
* Colours come from CSS custom properties with light and dark defaults, so an
|
|
507
|
+
* app can theme it — `--jr-confirm-surface`, `--jr-confirm-ink`,
|
|
508
|
+
* `--jr-confirm-muted`, `--jr-confirm-border`, `--jr-confirm-scrim`,
|
|
509
|
+
* `--jr-confirm-accent`, `--jr-confirm-danger`, `--jr-confirm-on-accent`,
|
|
510
|
+
* `--jr-confirm-radius` — and replace it outright through
|
|
511
|
+
* {@link JR_CONFIRM_DIALOG} when that is not enough.
|
|
419
512
|
*/
|
|
420
513
|
declare class JrConfirmDialog {
|
|
421
514
|
readonly config: _angular_core.InputSignal<ActionConfirm>;
|
|
422
515
|
readonly confirmed: _angular_core.OutputEmitterRef<void>;
|
|
423
516
|
readonly cancelled: _angular_core.OutputEmitterRef<void>;
|
|
517
|
+
protected readonly labels: ngx_json_render.ConfirmLabels;
|
|
518
|
+
private readonly id;
|
|
519
|
+
protected readonly titleId: string;
|
|
520
|
+
protected readonly messageId: string;
|
|
521
|
+
private readonly cancelButton;
|
|
522
|
+
private readonly confirmButton;
|
|
424
523
|
protected readonly isDanger: _angular_core.Signal<boolean>;
|
|
524
|
+
constructor();
|
|
525
|
+
/**
|
|
526
|
+
* Keep Tab inside the dialog. With exactly two buttons the whole trap is
|
|
527
|
+
* wrapping the ends; a modal that lets focus wander behind it is a modal
|
|
528
|
+
* only for people using a mouse.
|
|
529
|
+
*/
|
|
530
|
+
protected onKeydown(event: KeyboardEvent): void;
|
|
425
531
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<JrConfirmDialog, never>;
|
|
426
532
|
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>;
|
|
427
533
|
}
|
|
428
534
|
|
|
535
|
+
/** The two words the confirmation dialog says on its own. */
|
|
536
|
+
interface ConfirmLabels {
|
|
537
|
+
confirm: string;
|
|
538
|
+
cancel: string;
|
|
539
|
+
}
|
|
540
|
+
/**
|
|
541
|
+
* Labels for the default confirmation dialog's buttons, when the spec does not
|
|
542
|
+
* supply `confirmLabel` / `cancelLabel` of its own.
|
|
543
|
+
*
|
|
544
|
+
* They are the only strings this package puts on screen, and they were English
|
|
545
|
+
* with no way to change them. Override once, in an app's providers:
|
|
546
|
+
*
|
|
547
|
+
* ```ts
|
|
548
|
+
* { provide: JR_CONFIRM_LABELS, useValue: { confirm: 'Подтвердить', cancel: 'Отмена' } }
|
|
549
|
+
* ```
|
|
550
|
+
*/
|
|
551
|
+
declare const JR_CONFIRM_LABELS: InjectionToken<ConfirmLabels>;
|
|
552
|
+
/**
|
|
553
|
+
* Everything a replacement confirmation dialog is given: the config the spec
|
|
554
|
+
* asked for, and the two ways to answer it. Inject it with
|
|
555
|
+
* {@link injectConfirmContext}.
|
|
556
|
+
*/
|
|
557
|
+
interface ConfirmContext {
|
|
558
|
+
/** Title, message, labels and variant, straight from the action binding. */
|
|
559
|
+
readonly config: ActionConfirm;
|
|
560
|
+
/** Let the action proceed. */
|
|
561
|
+
readonly confirm: () => void;
|
|
562
|
+
/** Dismiss it — a normal answer, not a failure. */
|
|
563
|
+
readonly cancel: () => void;
|
|
564
|
+
}
|
|
565
|
+
/** The confirmation a replacement dialog component is rendered for. */
|
|
566
|
+
declare const CONFIRM_CONTEXT: InjectionToken<ConfirmContext>;
|
|
567
|
+
/**
|
|
568
|
+
* The component `<json-render>` should render for a `confirm` on an action.
|
|
569
|
+
* Unset means the packaged {@link JrConfirmDialog}.
|
|
570
|
+
*
|
|
571
|
+
* The component is rendered with a {@link CONFIRM_CONTEXT} in its injector, so
|
|
572
|
+
* it takes no inputs and emits no outputs:
|
|
573
|
+
*
|
|
574
|
+
* ```ts
|
|
575
|
+
* @Component({ template: `<my-modal (ok)="ctx.confirm()" (dismiss)="ctx.cancel()">…` })
|
|
576
|
+
* export class AppConfirm {
|
|
577
|
+
* readonly ctx = injectConfirmContext();
|
|
578
|
+
* }
|
|
579
|
+
* // providers: [{ provide: JR_CONFIRM_DIALOG, useValue: AppConfirm }]
|
|
580
|
+
* ```
|
|
581
|
+
*/
|
|
582
|
+
declare const JR_CONFIRM_DIALOG: InjectionToken<Type<unknown>>;
|
|
583
|
+
/** Inject the confirmation a replacement dialog component is rendering for. */
|
|
584
|
+
declare function injectConfirmContext(): ConfirmContext;
|
|
585
|
+
|
|
429
586
|
/**
|
|
430
587
|
* The render context of the element currently being rendered.
|
|
431
588
|
* Provided by the renderer for every catalog component instance.
|
|
432
589
|
*/
|
|
433
590
|
declare const RENDER_CONTEXT: InjectionToken<RenderContext<Record<string, unknown>>>;
|
|
591
|
+
/**
|
|
592
|
+
* The spec key of the element currently being rendered. Provided alongside
|
|
593
|
+
* {@link RENDER_CONTEXT} for every catalog component instance.
|
|
594
|
+
*/
|
|
595
|
+
declare const ELEMENT_KEY: InjectionToken<Signal<string>>;
|
|
434
596
|
/**
|
|
435
597
|
* The current repeat scope. Present only for elements rendered inside a
|
|
436
598
|
* `repeat` block.
|
|
@@ -451,6 +613,21 @@ declare const REPEAT_SCOPE: InjectionToken<RepeatScope>;
|
|
|
451
613
|
* ```
|
|
452
614
|
*/
|
|
453
615
|
declare function injectRenderContext<P = Record<string, unknown>>(): RenderContext<P>;
|
|
616
|
+
/**
|
|
617
|
+
* Inject the spec key of the element this component renders.
|
|
618
|
+
*
|
|
619
|
+
* Catalog components that collect their children — a tab group discovering
|
|
620
|
+
* its tabs, for instance — need it to order registrations by the spec's
|
|
621
|
+
* `children` array rather than by the order the children happened to
|
|
622
|
+
* announce themselves in.
|
|
623
|
+
*
|
|
624
|
+
* @example
|
|
625
|
+
* ```ts
|
|
626
|
+
* const key = injectElementKey();
|
|
627
|
+
* const index = computed(() => parent.element().children?.indexOf(key()) ?? -1);
|
|
628
|
+
* ```
|
|
629
|
+
*/
|
|
630
|
+
declare function injectElementKey(): Signal<string>;
|
|
454
631
|
/**
|
|
455
632
|
* Inject the current repeat scope, or `null` when the component is not
|
|
456
633
|
* rendered inside a `repeat` block.
|
|
@@ -496,17 +673,36 @@ interface FieldValidationState {
|
|
|
496
673
|
* Form validation state of a `<json-render>` subtree. Fields register their
|
|
497
674
|
* {@link ValidationConfig}; the built-in `validateForm` action validates all
|
|
498
675
|
* registered fields and writes the result to state.
|
|
676
|
+
*
|
|
677
|
+
* Registrations are owner-scoped: a path stays registered while at least one
|
|
678
|
+
* owner holds it, and its config and state are dropped when the last one
|
|
679
|
+
* releases. A spec that swaps screens or hides fields therefore stops
|
|
680
|
+
* validating what is no longer on screen.
|
|
499
681
|
*/
|
|
500
682
|
declare class JsonRenderValidationService {
|
|
501
683
|
private readonly root;
|
|
502
684
|
private readonly state;
|
|
503
685
|
private readonly _fieldStates;
|
|
504
686
|
private readonly _fieldConfigs;
|
|
687
|
+
/** Live owners per path — the reference count behind a registration. */
|
|
688
|
+
private readonly fieldOwners;
|
|
505
689
|
/** Validation state per registered field path. */
|
|
506
690
|
readonly fieldStates: Signal<Record<string, FieldValidationState>>;
|
|
507
691
|
get customFunctions(): Record<string, ValidationFunction>;
|
|
508
|
-
/**
|
|
509
|
-
|
|
692
|
+
/**
|
|
693
|
+
* Register (or update) a field's validation config.
|
|
694
|
+
*
|
|
695
|
+
* @param owner Identity holding the registration — pass the same value to
|
|
696
|
+
* {@link unregisterField} to release it. Field components pass themselves,
|
|
697
|
+
* so two components bound to one path each hold their own claim.
|
|
698
|
+
*/
|
|
699
|
+
registerField(path: string, config: ValidationConfig, owner?: object): void;
|
|
700
|
+
/**
|
|
701
|
+
* Release one owner's registration of a field. The config and validation
|
|
702
|
+
* state survive while another owner still holds the path; once the last
|
|
703
|
+
* one lets go, `validateAll` stops seeing the field.
|
|
704
|
+
*/
|
|
705
|
+
unregisterField(path: string, owner?: object): void;
|
|
510
706
|
/** Validate a single field and record the result. */
|
|
511
707
|
validate(path: string, config: ValidationConfig): ValidationResult;
|
|
512
708
|
/** Mark a field as touched. */
|
|
@@ -523,6 +719,10 @@ declare function injectValidation(): JsonRenderValidationService;
|
|
|
523
719
|
/**
|
|
524
720
|
* Field-level validation helper for catalog input components: registers the
|
|
525
721
|
* config and exposes the field's validation state as signals.
|
|
722
|
+
*
|
|
723
|
+
* The registration follows the component: it moves when the bound path
|
|
724
|
+
* changes and is released when the component is destroyed, so a field that
|
|
725
|
+
* has left the screen no longer takes part in `validateForm`.
|
|
526
726
|
*/
|
|
527
727
|
declare function injectFieldValidation(path: string | (() => string), config?: ValidationConfig | (() => ValidationConfig | undefined)): {
|
|
528
728
|
state: Signal<FieldValidationState>;
|
|
@@ -625,6 +825,13 @@ interface DataPart {
|
|
|
625
825
|
type: string;
|
|
626
826
|
text?: string;
|
|
627
827
|
data?: unknown;
|
|
828
|
+
/**
|
|
829
|
+
* The SDK's reconciliation key. A part written with one is *replaced* by the
|
|
830
|
+
* next part carrying the same id, rather than followed by it — so a spec
|
|
831
|
+
* streamed as patches must be written without an id, and only a whole-spec
|
|
832
|
+
* part (`flat` / `nested`), which is a snapshot of itself, can share one.
|
|
833
|
+
*/
|
|
834
|
+
id?: string;
|
|
628
835
|
}
|
|
629
836
|
/**
|
|
630
837
|
* Build a `Spec` by replaying all spec data parts from a message's
|
|
@@ -683,6 +890,17 @@ interface UIStreamOptions {
|
|
|
683
890
|
onComplete?: (spec: Spec) => void;
|
|
684
891
|
/** Callback on error */
|
|
685
892
|
onError?: (error: Error) => void;
|
|
893
|
+
/**
|
|
894
|
+
* Whether to check the finished spec's structure, and what a problem means.
|
|
895
|
+
* Off by default.
|
|
896
|
+
*
|
|
897
|
+
* The check runs once, when the generation completes — a spec mid-stream is
|
|
898
|
+
* supposed to be incomplete. Both modes apply the lossless fixes and publish
|
|
899
|
+
* the fixed spec; `'warn'` reports what is left, while `'strict'` fails the
|
|
900
|
+
* generation instead of calling `onComplete`, so an app that persists the
|
|
901
|
+
* spec there does not persist a broken one.
|
|
902
|
+
*/
|
|
903
|
+
validate?: SpecValidationMode;
|
|
686
904
|
/**
|
|
687
905
|
* Transport, defaulting to the global `fetch`.
|
|
688
906
|
*
|
|
@@ -709,6 +927,11 @@ interface UIStreamReturn {
|
|
|
709
927
|
readonly usage: Signal<TokenUsage | null>;
|
|
710
928
|
/** Raw JSONL lines received from the stream (JSON patch lines) */
|
|
711
929
|
readonly rawLines: Signal<string[]>;
|
|
930
|
+
/**
|
|
931
|
+
* Structural issues in the finished spec. Empty until a generation
|
|
932
|
+
* completes, and always empty while `validate` is off.
|
|
933
|
+
*/
|
|
934
|
+
readonly issues: Signal<readonly SpecIssue[]>;
|
|
712
935
|
/** Send a prompt to generate UI */
|
|
713
936
|
send: (prompt: string, options?: UIStreamSendOptions) => Promise<void>;
|
|
714
937
|
/**
|
|
@@ -769,6 +992,12 @@ interface ChatUIOptions {
|
|
|
769
992
|
* `Response` whose `body` streams the reply.
|
|
770
993
|
*/
|
|
771
994
|
fetch?: typeof globalThis.fetch;
|
|
995
|
+
/**
|
|
996
|
+
* Whether to check the spec a reply built, and what a problem means. Off by
|
|
997
|
+
* default; same contract as `UIStreamOptions.validate`, applied once per
|
|
998
|
+
* reply that produced a spec.
|
|
999
|
+
*/
|
|
1000
|
+
validate?: SpecValidationMode;
|
|
772
1001
|
}
|
|
773
1002
|
/**
|
|
774
1003
|
* Return type for {@link injectChatUI}.
|
|
@@ -780,6 +1009,11 @@ interface ChatUIReturn {
|
|
|
780
1009
|
readonly isStreaming: Signal<boolean>;
|
|
781
1010
|
/** Error from the last request, if any */
|
|
782
1011
|
readonly error: Signal<Error | null>;
|
|
1012
|
+
/**
|
|
1013
|
+
* Structural issues in the spec of the last completed reply. Empty until a
|
|
1014
|
+
* reply completes, and always empty while `validate` is off.
|
|
1015
|
+
*/
|
|
1016
|
+
readonly issues: Signal<readonly SpecIssue[]>;
|
|
783
1017
|
/** Send a user message */
|
|
784
1018
|
send: (text: string) => Promise<void>;
|
|
785
1019
|
/**
|
|
@@ -876,5 +1110,5 @@ type AngularSpec<TCatalog> = typeof schema extends {
|
|
|
876
1110
|
*/
|
|
877
1111
|
declare function injectDevtoolsActive(): Signal<boolean>;
|
|
878
1112
|
|
|
879
|
-
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 };
|
|
880
|
-
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, UIStreamSendOptions };
|
|
1113
|
+
export { CONFIRM_CONTEXT, ELEMENT_KEY, JR_CONFIRM_DIALOG, JR_CONFIRM_LABELS, JrChildren, JrConfirmDialog, JrElement, JrRepeatScope, JsonRenderActionsService, JsonRenderRootContext, JsonRenderStateService, JsonRenderValidationService, JsonRenderer, RENDER_CONTEXT, REPEAT_SCOPE, applyPatch, buildSpecFromParts, checkSpec, createStoreSetState, defineRegistry, flatToTree, getTextFromParts, injectAction, injectActions, injectBoundProp, injectChatUI, injectConfirmContext, injectDevtoolsActive, injectElementKey, injectFieldValidation, injectRenderContext, injectRepeatScope, injectStateBinding, injectStateStore, injectStateValue, injectUIStream, injectValidation, isActionCancelled, jsonRenderMessage, schema };
|
|
1114
|
+
export type { ActionFn, Actions, AngularSchema, AngularSpec, CatalogHasActions, ChatMessage, ChatUIOptions, ChatUIReturn, ComponentRegistry, Components, ConfirmContext, ConfirmLabels, DataPart, DefineRegistryResult, EventHandle, FieldValidationState, PendingConfirmation, RegistryEntry, RenderContext, RepeatScope, SetState, SpecCheck, SpecValidationMode, StateChange, TokenUsage, UIStreamOptions, UIStreamReturn, UIStreamSendOptions };
|