ngx-json-render 0.2.1 → 0.4.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 +448 -30
- 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 +969 -97
- 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 +447 -10
|
@@ -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, SpecIssue, Spec, 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,162 @@ declare function injectAction(binding: ActionBinding): {
|
|
|
57
69
|
isLoading: Signal<boolean>;
|
|
58
70
|
};
|
|
59
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Caps on what a spec is allowed to cost the browser.
|
|
74
|
+
*
|
|
75
|
+
* A spec is attacker-shaped input: whatever produced one chose its own size,
|
|
76
|
+
* its own nesting and — through `repeat` — its own array lengths. These are
|
|
77
|
+
* the three of those the renderer can enforce for you.
|
|
78
|
+
*
|
|
79
|
+
* Every field is opt-in and unset means unlimited. There are no defaults
|
|
80
|
+
* because no single number is right for every catalog, and a default would
|
|
81
|
+
* change what an existing app renders the moment it upgraded.
|
|
82
|
+
*
|
|
83
|
+
* Limits are enforced whenever they are set, whatever `validate` is: a number
|
|
84
|
+
* the app chose is a control, not advice. What "enforced" means differs by
|
|
85
|
+
* limit, because the limits differ in kind — see each field.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```html
|
|
89
|
+
* <json-render
|
|
90
|
+
* [spec]="spec()"
|
|
91
|
+
* [registry]="registry"
|
|
92
|
+
* [renderLimits]="{ maxElements: 500, maxDepth: 16, maxRepeatItems: 200 }"
|
|
93
|
+
* />
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
interface RenderLimits {
|
|
97
|
+
/**
|
|
98
|
+
* Most elements a spec may contain. A spec over the cap does not render at
|
|
99
|
+
* all: element count is a property of the whole spec, and there is no
|
|
100
|
+
* meaningful "first 500 elements" of a graph.
|
|
101
|
+
*/
|
|
102
|
+
readonly maxElements?: number;
|
|
103
|
+
/**
|
|
104
|
+
* Deepest the render tree may nest, counting the root as 1. An element
|
|
105
|
+
* below the cap does not render, and neither does anything under it; what
|
|
106
|
+
* fits still renders. Reported as an error, so `validate="strict"` refuses
|
|
107
|
+
* the spec outright rather than truncating it.
|
|
108
|
+
*
|
|
109
|
+
* A capped element does not act either — its `watch` stays unwired, so it
|
|
110
|
+
* cannot dispatch actions from behind the cap.
|
|
111
|
+
*/
|
|
112
|
+
readonly maxDepth?: number;
|
|
113
|
+
/**
|
|
114
|
+
* Most items one `repeat` may expand. The surplus items do not render.
|
|
115
|
+
*
|
|
116
|
+
* Enforced at render time only: `repeat` iterates a live state array, so no
|
|
117
|
+
* amount of inspecting the spec can predict how long it will be. Nested
|
|
118
|
+
* repeats multiply, so this is a per-repeat cap, not a total.
|
|
119
|
+
*/
|
|
120
|
+
readonly maxRepeatItems?: number;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Issue codes this package reports, widening core's set with the ones only a
|
|
124
|
+
* renderer can find.
|
|
125
|
+
*
|
|
126
|
+
* - `cycle` — an element renders itself against the same data, which cannot
|
|
127
|
+
* terminate. A tree that recurses into its items is not this.
|
|
128
|
+
* - `too_deep` / `too_many_elements` — a {@link RenderLimits} cap was passed.
|
|
129
|
+
* - `unknown_component` — a `type` the catalog does not define.
|
|
130
|
+
* - `invalid_props` — the catalog's own schema rejected part of the spec.
|
|
131
|
+
*/
|
|
132
|
+
type SpecCheckIssueCode = SpecIssue['code'] | 'cycle' | 'too_deep' | 'too_many_elements' | 'unknown_component' | 'invalid_props';
|
|
133
|
+
/**
|
|
134
|
+
* A problem found in a spec. Core's {@link SpecIssue} widened with the codes
|
|
135
|
+
* above, so a {@link SpecIssue} is always one of these.
|
|
136
|
+
*/
|
|
137
|
+
interface SpecCheckIssue {
|
|
138
|
+
readonly severity: 'error' | 'warning';
|
|
139
|
+
readonly message: string;
|
|
140
|
+
readonly elementKey?: string;
|
|
141
|
+
readonly code: SpecCheckIssueCode;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* The part of a json-render `Catalog` the check needs.
|
|
145
|
+
*
|
|
146
|
+
* Structural on purpose: a real catalog satisfies it, and the renderer stays
|
|
147
|
+
* free of any dependency on the schema library behind `validate`.
|
|
148
|
+
*/
|
|
149
|
+
interface SpecCatalog {
|
|
150
|
+
/** Component type names the catalog defines. */
|
|
151
|
+
readonly componentNames: readonly string[];
|
|
152
|
+
/** Check a spec against the catalog's own schema. */
|
|
153
|
+
validate(spec: unknown): {
|
|
154
|
+
readonly success: boolean;
|
|
155
|
+
readonly error?: unknown;
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* How much a spec has to be trusted.
|
|
161
|
+
*
|
|
162
|
+
* - `off` — render whatever arrives. The default, and what every version
|
|
163
|
+
* before this one did.
|
|
164
|
+
* - `warn` — check the spec, apply the lossless fixes, report what is left,
|
|
165
|
+
* render it anyway.
|
|
166
|
+
* - `strict` — the same check, but an error means the spec does not render.
|
|
167
|
+
*
|
|
168
|
+
* This governs *reporting* and strict refusal. {@link RenderLimits} are a
|
|
169
|
+
* separate axis: a limit the app set is enforced in every mode, including
|
|
170
|
+
* `off`.
|
|
171
|
+
*/
|
|
172
|
+
type SpecValidationMode = 'off' | 'warn' | 'strict';
|
|
173
|
+
/** Everything {@link checkSpec} needs beyond the spec and the mode. */
|
|
174
|
+
interface SpecCheckOptions {
|
|
175
|
+
/** Caps on the spec's size, enforced whatever the mode. */
|
|
176
|
+
readonly limits?: RenderLimits | null;
|
|
177
|
+
/**
|
|
178
|
+
* The catalog the spec was generated for. Given one, the check also reports
|
|
179
|
+
* component types the catalog does not define and props its own schema
|
|
180
|
+
* rejects — the half of "is this spec renderable" that structure alone
|
|
181
|
+
* cannot answer.
|
|
182
|
+
*/
|
|
183
|
+
readonly catalog?: SpecCatalog | null;
|
|
184
|
+
}
|
|
185
|
+
/** What {@link checkSpec} found, and the spec that should render. */
|
|
186
|
+
interface SpecCheck {
|
|
187
|
+
/**
|
|
188
|
+
* The spec to render: the input with lossless fixes applied, or the input
|
|
189
|
+
* untouched when validation is off.
|
|
190
|
+
*/
|
|
191
|
+
readonly spec: Spec | null;
|
|
192
|
+
/** Structural, limit and catalog issues left after the fixes. */
|
|
193
|
+
readonly issues: readonly SpecCheckIssue[];
|
|
194
|
+
/** Descriptions of the fixes that were applied. */
|
|
195
|
+
readonly fixes: readonly string[];
|
|
196
|
+
/** Whether an issue of `error` severity survived. */
|
|
197
|
+
readonly hasErrors: boolean;
|
|
198
|
+
/**
|
|
199
|
+
* Whether the spec is refused outright, in every mode, for breaching an
|
|
200
|
+
* enforced limit. Distinct from {@link hasErrors}, which only stops a spec
|
|
201
|
+
* under `strict`.
|
|
202
|
+
*/
|
|
203
|
+
readonly blocked: boolean;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Check a spec the way `validate` and `renderLimits` ask for.
|
|
207
|
+
*
|
|
208
|
+
* Only lossless fixes are applied — `visible`, `on` and `repeat` that a model
|
|
209
|
+
* put inside `props`, which the renderer would otherwise ignore, moved back
|
|
210
|
+
* where they belong. The lossy ones prune content, and core's own guidance is
|
|
211
|
+
* to re-prompt rather than accept that; a renderer that silently deleted
|
|
212
|
+
* elements would be the worse failure.
|
|
213
|
+
*
|
|
214
|
+
* Note what is *not* here: cycles are broken as the tree renders, whatever
|
|
215
|
+
* this returns, because a spec that never stops rendering is a crash rather
|
|
216
|
+
* than an opinion about quality. This reports them so an app can see them.
|
|
217
|
+
*/
|
|
218
|
+
declare function checkSpec(spec: Spec | null, mode: SpecValidationMode, options?: SpecCheckOptions): SpecCheck;
|
|
219
|
+
/**
|
|
220
|
+
* Render issues as one indented block, errors and warnings alike.
|
|
221
|
+
*
|
|
222
|
+
* Core's `formatSpecIssues` drops warnings and only accepts core's own issue
|
|
223
|
+
* codes, so it cannot describe what this package reports. Use this instead
|
|
224
|
+
* when showing `issues()` to a developer.
|
|
225
|
+
*/
|
|
226
|
+
declare function formatSpecCheckIssues(issues: readonly SpecCheckIssue[]): string;
|
|
227
|
+
|
|
60
228
|
/**
|
|
61
229
|
* State setter function for updating application state.
|
|
62
230
|
* Matches the `SetState` contract of the other json-render renderers
|
|
@@ -281,6 +449,40 @@ declare class JsonRenderer {
|
|
|
281
449
|
readonly loading: _angular_core.InputSignal<boolean>;
|
|
282
450
|
/** Fallback component for unknown types. */
|
|
283
451
|
readonly fallback: _angular_core.InputSignal<RegistryEntry | Type<unknown> | null>;
|
|
452
|
+
/**
|
|
453
|
+
* Whether to check the spec's structure before rendering it, and what to do
|
|
454
|
+
* about what turns up. Off by default.
|
|
455
|
+
*
|
|
456
|
+
* `'warn'` reports the issues and renders anyway; `'strict'` renders nothing
|
|
457
|
+
* when the spec has errors. Both first apply the lossless fixes — `visible`,
|
|
458
|
+
* `on` and `repeat` misplaced inside `props` are moved back to the element,
|
|
459
|
+
* where they take effect instead of being ignored.
|
|
460
|
+
*
|
|
461
|
+
* The check is skipped while `loading` is true: a spec that is still
|
|
462
|
+
* arriving is expected to reference children that have not streamed in yet.
|
|
463
|
+
*/
|
|
464
|
+
readonly validate: _angular_core.InputSignal<SpecValidationMode>;
|
|
465
|
+
/**
|
|
466
|
+
* Caps on what the spec may cost the browser: `maxElements`, `maxDepth` and
|
|
467
|
+
* `maxRepeatItems`. Unset by default, and each field is independently
|
|
468
|
+
* optional.
|
|
469
|
+
*
|
|
470
|
+
* Unlike `validate`, these are enforced in every mode — a number the app
|
|
471
|
+
* chose is a control, not a report. A spec over `maxElements` renders
|
|
472
|
+
* nothing; `maxDepth` and `maxRepeatItems` truncate where they are hit.
|
|
473
|
+
* They apply while `loading` too: a partial spec is a subset of the
|
|
474
|
+
* finished one, so a cap can only ever fire early, never falsely.
|
|
475
|
+
*/
|
|
476
|
+
readonly renderLimits: _angular_core.InputSignal<RenderLimits | null>;
|
|
477
|
+
/**
|
|
478
|
+
* The catalog the spec was generated for. Given one, `validate` also checks
|
|
479
|
+
* what structure alone cannot: that every `type` is a component the catalog
|
|
480
|
+
* defines, and that the props match its schema.
|
|
481
|
+
*
|
|
482
|
+
* Only read when `validate` is on, and — like the rest of the check —
|
|
483
|
+
* skipped while `loading`.
|
|
484
|
+
*/
|
|
485
|
+
readonly catalog: _angular_core.InputSignal<SpecCatalog | null>;
|
|
284
486
|
/**
|
|
285
487
|
* Initial state model (uncontrolled mode). Defaults to `spec.state`.
|
|
286
488
|
* Ignored when `store` is provided.
|
|
@@ -306,11 +508,31 @@ declare class JsonRenderer {
|
|
|
306
508
|
readonly stateStore: JsonRenderStateService;
|
|
307
509
|
/** The action dispatcher of this renderer. */
|
|
308
510
|
protected readonly actions: JsonRenderActionsService;
|
|
511
|
+
/**
|
|
512
|
+
* The spec as checked, and what the check found. While `loading`, the mode
|
|
513
|
+
* is forced off: the spec is still arriving, and the missing children it
|
|
514
|
+
* refers to are the normal state of a stream, not a defect.
|
|
515
|
+
*/
|
|
516
|
+
private readonly checked;
|
|
309
517
|
constructor();
|
|
310
518
|
protected readonly rootKey: _angular_core.Signal<string | null>;
|
|
311
519
|
protected readonly pendingConfirm: _angular_core.Signal<_json_render_core.ActionConfirm | null>;
|
|
520
|
+
/**
|
|
521
|
+
* The app's own dialog, if it provided one. Read once: which component
|
|
522
|
+
* answers a confirmation is an application decision, not something that
|
|
523
|
+
* changes between two confirmations.
|
|
524
|
+
*/
|
|
525
|
+
protected readonly customDialog: Type<unknown> | null;
|
|
526
|
+
private readonly injector;
|
|
527
|
+
/**
|
|
528
|
+
* A replacement dialog takes no inputs and emits no outputs — it injects
|
|
529
|
+
* everything it needs, the way catalog components do. That keeps the seam
|
|
530
|
+
* one token wide instead of a component contract the renderer would have to
|
|
531
|
+
* bind to.
|
|
532
|
+
*/
|
|
533
|
+
protected readonly confirmInjector: _angular_core.Signal<_angular_core.DestroyableInjector | null>;
|
|
312
534
|
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>;
|
|
535
|
+
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; }; "renderLimits": { "alias": "renderLimits"; "required": false; "isSignal": true; }; "catalog": { "alias": "catalog"; "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
536
|
}
|
|
315
537
|
|
|
316
538
|
/**
|
|
@@ -344,6 +566,16 @@ declare class JrChildren {
|
|
|
344
566
|
key?: string;
|
|
345
567
|
} | undefined>;
|
|
346
568
|
private readonly repeatBasePath;
|
|
569
|
+
/** Every item the repeat's state array holds, before any cap. */
|
|
570
|
+
private readonly allRepeatItems;
|
|
571
|
+
/**
|
|
572
|
+
* The items this repeat actually renders.
|
|
573
|
+
*
|
|
574
|
+
* `repeat` iterates a state array the spec may itself have supplied, which
|
|
575
|
+
* is the one place a spec sizes the render tree out of data rather than out
|
|
576
|
+
* of its own structure — so the cap has to be applied here, as it expands,
|
|
577
|
+
* rather than anywhere a spec can be inspected up front.
|
|
578
|
+
*/
|
|
347
579
|
protected readonly repeatItems: _angular_core.Signal<unknown[]>;
|
|
348
580
|
constructor();
|
|
349
581
|
protected itemPath(index: number): string;
|
|
@@ -365,6 +597,7 @@ declare class JrElement {
|
|
|
365
597
|
private readonly state;
|
|
366
598
|
private readonly actions;
|
|
367
599
|
private readonly repeatScope;
|
|
600
|
+
private readonly parentPath;
|
|
368
601
|
private readonly devtoolsActive;
|
|
369
602
|
/** The raw (unresolved) element from the spec. */
|
|
370
603
|
readonly rawElement: Signal<UIElement<string, Record<string, unknown>> | undefined>;
|
|
@@ -378,6 +611,23 @@ declare class JrElement {
|
|
|
378
611
|
private readonly entry;
|
|
379
612
|
protected readonly component: Signal<_angular_core.Type<unknown> | null>;
|
|
380
613
|
protected readonly devtoolsKey: Signal<string | null>;
|
|
614
|
+
/**
|
|
615
|
+
* Where this element sits in the tree. Passed down to everything it renders
|
|
616
|
+
* below itself, which is what lets each element see its own ancestry
|
|
617
|
+
* without the renderer holding a registry of who is drawing what.
|
|
618
|
+
*/
|
|
619
|
+
private readonly path;
|
|
620
|
+
/**
|
|
621
|
+
* Why this element is not drawn, or null when it is.
|
|
622
|
+
*
|
|
623
|
+
* The cycle half is unconditional — not something `validate` turns on. An
|
|
624
|
+
* element that renders itself without reading any deeper renders forever,
|
|
625
|
+
* and a spec that never stops rendering takes the tab with it; refusing to
|
|
626
|
+
* draw the repetition is the only outcome that leaves anything on screen.
|
|
627
|
+
* It costs one walk up a chain of parents, bounded by `maxDepth` where one
|
|
628
|
+
* is set and by the spec's own nesting where it is not.
|
|
629
|
+
*/
|
|
630
|
+
protected readonly refusal: Signal<"cycle" | "depth" | null>;
|
|
381
631
|
private readonly renderCtx;
|
|
382
632
|
protected readonly outletInjector: _angular_core.DestroyableInjector;
|
|
383
633
|
constructor();
|
|
@@ -416,21 +666,103 @@ declare class JrRepeatScope implements RepeatScope {
|
|
|
416
666
|
* Default confirmation dialog shown for action bindings with a `confirm`
|
|
417
667
|
* field. Rendered automatically by `<json-render>`; can also be used
|
|
418
668
|
* standalone with a custom action flow.
|
|
669
|
+
*
|
|
670
|
+
* It is a real dialog: `role="dialog"` with `aria-modal`, labelled by its own
|
|
671
|
+
* title and described by its message, focus moved in on open and returned to
|
|
672
|
+
* wherever it came from on close, Tab kept inside it and Escape cancelling.
|
|
673
|
+
* A generated UI can ask for confirmation of something destructive, so a
|
|
674
|
+
* keyboard or screen-reader user has to be able to answer it.
|
|
675
|
+
*
|
|
676
|
+
* Colours come from CSS custom properties with light and dark defaults, so an
|
|
677
|
+
* app can theme it — `--jr-confirm-surface`, `--jr-confirm-ink`,
|
|
678
|
+
* `--jr-confirm-muted`, `--jr-confirm-border`, `--jr-confirm-scrim`,
|
|
679
|
+
* `--jr-confirm-accent`, `--jr-confirm-danger`, `--jr-confirm-on-accent`,
|
|
680
|
+
* `--jr-confirm-radius` — and replace it outright through
|
|
681
|
+
* {@link JR_CONFIRM_DIALOG} when that is not enough.
|
|
419
682
|
*/
|
|
420
683
|
declare class JrConfirmDialog {
|
|
421
684
|
readonly config: _angular_core.InputSignal<ActionConfirm>;
|
|
422
685
|
readonly confirmed: _angular_core.OutputEmitterRef<void>;
|
|
423
686
|
readonly cancelled: _angular_core.OutputEmitterRef<void>;
|
|
687
|
+
protected readonly labels: ngx_json_render.ConfirmLabels;
|
|
688
|
+
private readonly id;
|
|
689
|
+
protected readonly titleId: string;
|
|
690
|
+
protected readonly messageId: string;
|
|
691
|
+
private readonly cancelButton;
|
|
692
|
+
private readonly confirmButton;
|
|
424
693
|
protected readonly isDanger: _angular_core.Signal<boolean>;
|
|
694
|
+
constructor();
|
|
695
|
+
/**
|
|
696
|
+
* Keep Tab inside the dialog. With exactly two buttons the whole trap is
|
|
697
|
+
* wrapping the ends; a modal that lets focus wander behind it is a modal
|
|
698
|
+
* only for people using a mouse.
|
|
699
|
+
*/
|
|
700
|
+
protected onKeydown(event: KeyboardEvent): void;
|
|
425
701
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<JrConfirmDialog, never>;
|
|
426
702
|
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
703
|
}
|
|
428
704
|
|
|
705
|
+
/** The two words the confirmation dialog says on its own. */
|
|
706
|
+
interface ConfirmLabels {
|
|
707
|
+
confirm: string;
|
|
708
|
+
cancel: string;
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* Labels for the default confirmation dialog's buttons, when the spec does not
|
|
712
|
+
* supply `confirmLabel` / `cancelLabel` of its own.
|
|
713
|
+
*
|
|
714
|
+
* They are the only strings this package puts on screen, and they were English
|
|
715
|
+
* with no way to change them. Override once, in an app's providers:
|
|
716
|
+
*
|
|
717
|
+
* ```ts
|
|
718
|
+
* { provide: JR_CONFIRM_LABELS, useValue: { confirm: 'Подтвердить', cancel: 'Отмена' } }
|
|
719
|
+
* ```
|
|
720
|
+
*/
|
|
721
|
+
declare const JR_CONFIRM_LABELS: InjectionToken<ConfirmLabels>;
|
|
722
|
+
/**
|
|
723
|
+
* Everything a replacement confirmation dialog is given: the config the spec
|
|
724
|
+
* asked for, and the two ways to answer it. Inject it with
|
|
725
|
+
* {@link injectConfirmContext}.
|
|
726
|
+
*/
|
|
727
|
+
interface ConfirmContext {
|
|
728
|
+
/** Title, message, labels and variant, straight from the action binding. */
|
|
729
|
+
readonly config: ActionConfirm;
|
|
730
|
+
/** Let the action proceed. */
|
|
731
|
+
readonly confirm: () => void;
|
|
732
|
+
/** Dismiss it — a normal answer, not a failure. */
|
|
733
|
+
readonly cancel: () => void;
|
|
734
|
+
}
|
|
735
|
+
/** The confirmation a replacement dialog component is rendered for. */
|
|
736
|
+
declare const CONFIRM_CONTEXT: InjectionToken<ConfirmContext>;
|
|
737
|
+
/**
|
|
738
|
+
* The component `<json-render>` should render for a `confirm` on an action.
|
|
739
|
+
* Unset means the packaged {@link JrConfirmDialog}.
|
|
740
|
+
*
|
|
741
|
+
* The component is rendered with a {@link CONFIRM_CONTEXT} in its injector, so
|
|
742
|
+
* it takes no inputs and emits no outputs:
|
|
743
|
+
*
|
|
744
|
+
* ```ts
|
|
745
|
+
* @Component({ template: `<my-modal (ok)="ctx.confirm()" (dismiss)="ctx.cancel()">…` })
|
|
746
|
+
* export class AppConfirm {
|
|
747
|
+
* readonly ctx = injectConfirmContext();
|
|
748
|
+
* }
|
|
749
|
+
* // providers: [{ provide: JR_CONFIRM_DIALOG, useValue: AppConfirm }]
|
|
750
|
+
* ```
|
|
751
|
+
*/
|
|
752
|
+
declare const JR_CONFIRM_DIALOG: InjectionToken<Type<unknown>>;
|
|
753
|
+
/** Inject the confirmation a replacement dialog component is rendering for. */
|
|
754
|
+
declare function injectConfirmContext(): ConfirmContext;
|
|
755
|
+
|
|
429
756
|
/**
|
|
430
757
|
* The render context of the element currently being rendered.
|
|
431
758
|
* Provided by the renderer for every catalog component instance.
|
|
432
759
|
*/
|
|
433
760
|
declare const RENDER_CONTEXT: InjectionToken<RenderContext<Record<string, unknown>>>;
|
|
761
|
+
/**
|
|
762
|
+
* The spec key of the element currently being rendered. Provided alongside
|
|
763
|
+
* {@link RENDER_CONTEXT} for every catalog component instance.
|
|
764
|
+
*/
|
|
765
|
+
declare const ELEMENT_KEY: InjectionToken<Signal<string>>;
|
|
434
766
|
/**
|
|
435
767
|
* The current repeat scope. Present only for elements rendered inside a
|
|
436
768
|
* `repeat` block.
|
|
@@ -451,6 +783,21 @@ declare const REPEAT_SCOPE: InjectionToken<RepeatScope>;
|
|
|
451
783
|
* ```
|
|
452
784
|
*/
|
|
453
785
|
declare function injectRenderContext<P = Record<string, unknown>>(): RenderContext<P>;
|
|
786
|
+
/**
|
|
787
|
+
* Inject the spec key of the element this component renders.
|
|
788
|
+
*
|
|
789
|
+
* Catalog components that collect their children — a tab group discovering
|
|
790
|
+
* its tabs, for instance — need it to order registrations by the spec's
|
|
791
|
+
* `children` array rather than by the order the children happened to
|
|
792
|
+
* announce themselves in.
|
|
793
|
+
*
|
|
794
|
+
* @example
|
|
795
|
+
* ```ts
|
|
796
|
+
* const key = injectElementKey();
|
|
797
|
+
* const index = computed(() => parent.element().children?.indexOf(key()) ?? -1);
|
|
798
|
+
* ```
|
|
799
|
+
*/
|
|
800
|
+
declare function injectElementKey(): Signal<string>;
|
|
454
801
|
/**
|
|
455
802
|
* Inject the current repeat scope, or `null` when the component is not
|
|
456
803
|
* rendered inside a `repeat` block.
|
|
@@ -469,6 +816,11 @@ declare class JsonRenderRootContext {
|
|
|
469
816
|
registry: Signal<ComponentRegistry | undefined>;
|
|
470
817
|
loading: Signal<boolean>;
|
|
471
818
|
fallback: Signal<Type<unknown> | RegistryEntry | null | undefined>;
|
|
819
|
+
/**
|
|
820
|
+
* Caps on what the spec may cost the browser. Read as the tree renders, so
|
|
821
|
+
* they apply whatever `validate` is set to.
|
|
822
|
+
*/
|
|
823
|
+
limits: Signal<RenderLimits | null | undefined>;
|
|
472
824
|
/** External store (controlled mode). */
|
|
473
825
|
store: Signal<StateStore | null | undefined>;
|
|
474
826
|
/** Initial state (uncontrolled mode); falls back to `spec.state`. */
|
|
@@ -496,17 +848,36 @@ interface FieldValidationState {
|
|
|
496
848
|
* Form validation state of a `<json-render>` subtree. Fields register their
|
|
497
849
|
* {@link ValidationConfig}; the built-in `validateForm` action validates all
|
|
498
850
|
* registered fields and writes the result to state.
|
|
851
|
+
*
|
|
852
|
+
* Registrations are owner-scoped: a path stays registered while at least one
|
|
853
|
+
* owner holds it, and its config and state are dropped when the last one
|
|
854
|
+
* releases. A spec that swaps screens or hides fields therefore stops
|
|
855
|
+
* validating what is no longer on screen.
|
|
499
856
|
*/
|
|
500
857
|
declare class JsonRenderValidationService {
|
|
501
858
|
private readonly root;
|
|
502
859
|
private readonly state;
|
|
503
860
|
private readonly _fieldStates;
|
|
504
861
|
private readonly _fieldConfigs;
|
|
862
|
+
/** Live owners per path — the reference count behind a registration. */
|
|
863
|
+
private readonly fieldOwners;
|
|
505
864
|
/** Validation state per registered field path. */
|
|
506
865
|
readonly fieldStates: Signal<Record<string, FieldValidationState>>;
|
|
507
866
|
get customFunctions(): Record<string, ValidationFunction>;
|
|
508
|
-
/**
|
|
509
|
-
|
|
867
|
+
/**
|
|
868
|
+
* Register (or update) a field's validation config.
|
|
869
|
+
*
|
|
870
|
+
* @param owner Identity holding the registration — pass the same value to
|
|
871
|
+
* {@link unregisterField} to release it. Field components pass themselves,
|
|
872
|
+
* so two components bound to one path each hold their own claim.
|
|
873
|
+
*/
|
|
874
|
+
registerField(path: string, config: ValidationConfig, owner?: object): void;
|
|
875
|
+
/**
|
|
876
|
+
* Release one owner's registration of a field. The config and validation
|
|
877
|
+
* state survive while another owner still holds the path; once the last
|
|
878
|
+
* one lets go, `validateAll` stops seeing the field.
|
|
879
|
+
*/
|
|
880
|
+
unregisterField(path: string, owner?: object): void;
|
|
510
881
|
/** Validate a single field and record the result. */
|
|
511
882
|
validate(path: string, config: ValidationConfig): ValidationResult;
|
|
512
883
|
/** Mark a field as touched. */
|
|
@@ -523,6 +894,10 @@ declare function injectValidation(): JsonRenderValidationService;
|
|
|
523
894
|
/**
|
|
524
895
|
* Field-level validation helper for catalog input components: registers the
|
|
525
896
|
* config and exposes the field's validation state as signals.
|
|
897
|
+
*
|
|
898
|
+
* The registration follows the component: it moves when the bound path
|
|
899
|
+
* changes and is released when the component is destroyed, so a field that
|
|
900
|
+
* has left the screen no longer takes part in `validateForm`.
|
|
526
901
|
*/
|
|
527
902
|
declare function injectFieldValidation(path: string | (() => string), config?: ValidationConfig | (() => ValidationConfig | undefined)): {
|
|
528
903
|
state: Signal<FieldValidationState>;
|
|
@@ -625,6 +1000,13 @@ interface DataPart {
|
|
|
625
1000
|
type: string;
|
|
626
1001
|
text?: string;
|
|
627
1002
|
data?: unknown;
|
|
1003
|
+
/**
|
|
1004
|
+
* The SDK's reconciliation key. A part written with one is *replaced* by the
|
|
1005
|
+
* next part carrying the same id, rather than followed by it — so a spec
|
|
1006
|
+
* streamed as patches must be written without an id, and only a whole-spec
|
|
1007
|
+
* part (`flat` / `nested`), which is a snapshot of itself, can share one.
|
|
1008
|
+
*/
|
|
1009
|
+
id?: string;
|
|
628
1010
|
}
|
|
629
1011
|
/**
|
|
630
1012
|
* Build a `Spec` by replaying all spec data parts from a message's
|
|
@@ -683,6 +1065,29 @@ interface UIStreamOptions {
|
|
|
683
1065
|
onComplete?: (spec: Spec) => void;
|
|
684
1066
|
/** Callback on error */
|
|
685
1067
|
onError?: (error: Error) => void;
|
|
1068
|
+
/**
|
|
1069
|
+
* Whether to check the finished spec's structure, and what a problem means.
|
|
1070
|
+
* Off by default.
|
|
1071
|
+
*
|
|
1072
|
+
* The check runs once, when the generation completes — a spec mid-stream is
|
|
1073
|
+
* supposed to be incomplete. Both modes apply the lossless fixes and publish
|
|
1074
|
+
* the fixed spec; `'warn'` reports what is left, while `'strict'` fails the
|
|
1075
|
+
* generation instead of calling `onComplete`, so an app that persists the
|
|
1076
|
+
* spec there does not persist a broken one.
|
|
1077
|
+
*/
|
|
1078
|
+
validate?: SpecValidationMode;
|
|
1079
|
+
/**
|
|
1080
|
+
* Caps on what the finished spec may cost the browser. Enforced whatever
|
|
1081
|
+
* `validate` is: a spec over `maxElements` fails the generation rather than
|
|
1082
|
+
* reaching `onComplete`.
|
|
1083
|
+
*/
|
|
1084
|
+
renderLimits?: RenderLimits | null;
|
|
1085
|
+
/**
|
|
1086
|
+
* The catalog the spec was generated for. Given one, `validate` also checks
|
|
1087
|
+
* every element `type` against the catalog and the props against its
|
|
1088
|
+
* schema — the half a structural check cannot see.
|
|
1089
|
+
*/
|
|
1090
|
+
catalog?: SpecCatalog | null;
|
|
686
1091
|
/**
|
|
687
1092
|
* Transport, defaulting to the global `fetch`.
|
|
688
1093
|
*
|
|
@@ -709,6 +1114,13 @@ interface UIStreamReturn {
|
|
|
709
1114
|
readonly usage: Signal<TokenUsage | null>;
|
|
710
1115
|
/** Raw JSONL lines received from the stream (JSON patch lines) */
|
|
711
1116
|
readonly rawLines: Signal<string[]>;
|
|
1117
|
+
/**
|
|
1118
|
+
* What the check found in the finished spec — structure, catalog and
|
|
1119
|
+
* limits alike. Empty until a generation completes; empty while `validate`
|
|
1120
|
+
* is off unless a `renderLimits` cap was passed, since those are enforced
|
|
1121
|
+
* in every mode.
|
|
1122
|
+
*/
|
|
1123
|
+
readonly issues: Signal<readonly SpecCheckIssue[]>;
|
|
712
1124
|
/** Send a prompt to generate UI */
|
|
713
1125
|
send: (prompt: string, options?: UIStreamSendOptions) => Promise<void>;
|
|
714
1126
|
/**
|
|
@@ -769,6 +1181,24 @@ interface ChatUIOptions {
|
|
|
769
1181
|
* `Response` whose `body` streams the reply.
|
|
770
1182
|
*/
|
|
771
1183
|
fetch?: typeof globalThis.fetch;
|
|
1184
|
+
/**
|
|
1185
|
+
* Whether to check the spec a reply built, and what a problem means. Off by
|
|
1186
|
+
* default; same contract as `UIStreamOptions.validate`, applied once per
|
|
1187
|
+
* reply that produced a spec.
|
|
1188
|
+
*/
|
|
1189
|
+
validate?: SpecValidationMode;
|
|
1190
|
+
/**
|
|
1191
|
+
* Caps on what the finished spec may cost the browser. Enforced whatever
|
|
1192
|
+
* `validate` is: a spec over `maxElements` fails the generation rather than
|
|
1193
|
+
* reaching `onComplete`.
|
|
1194
|
+
*/
|
|
1195
|
+
renderLimits?: RenderLimits | null;
|
|
1196
|
+
/**
|
|
1197
|
+
* The catalog the spec was generated for. Given one, `validate` also checks
|
|
1198
|
+
* every element `type` against the catalog and the props against its
|
|
1199
|
+
* schema — the half a structural check cannot see.
|
|
1200
|
+
*/
|
|
1201
|
+
catalog?: SpecCatalog | null;
|
|
772
1202
|
}
|
|
773
1203
|
/**
|
|
774
1204
|
* Return type for {@link injectChatUI}.
|
|
@@ -780,6 +1210,13 @@ interface ChatUIReturn {
|
|
|
780
1210
|
readonly isStreaming: Signal<boolean>;
|
|
781
1211
|
/** Error from the last request, if any */
|
|
782
1212
|
readonly error: Signal<Error | null>;
|
|
1213
|
+
/**
|
|
1214
|
+
* What the check found in the last completed reply's spec — structure,
|
|
1215
|
+
* catalog and limits alike. Empty until a reply completes; empty while
|
|
1216
|
+
* `validate` is off unless a `renderLimits` cap was passed, since those are
|
|
1217
|
+
* enforced in every mode.
|
|
1218
|
+
*/
|
|
1219
|
+
readonly issues: Signal<readonly SpecCheckIssue[]>;
|
|
783
1220
|
/** Send a user message */
|
|
784
1221
|
send: (text: string) => Promise<void>;
|
|
785
1222
|
/**
|
|
@@ -876,5 +1313,5 @@ type AngularSpec<TCatalog> = typeof schema extends {
|
|
|
876
1313
|
*/
|
|
877
1314
|
declare function injectDevtoolsActive(): Signal<boolean>;
|
|
878
1315
|
|
|
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 };
|
|
1316
|
+
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, formatSpecCheckIssues, getTextFromParts, injectAction, injectActions, injectBoundProp, injectChatUI, injectConfirmContext, injectDevtoolsActive, injectElementKey, injectFieldValidation, injectRenderContext, injectRepeatScope, injectStateBinding, injectStateStore, injectStateValue, injectUIStream, injectValidation, isActionCancelled, jsonRenderMessage, schema };
|
|
1317
|
+
export type { ActionFn, Actions, AngularSchema, AngularSpec, CatalogHasActions, ChatMessage, ChatUIOptions, ChatUIReturn, ComponentRegistry, Components, ConfirmContext, ConfirmLabels, DataPart, DefineRegistryResult, EventHandle, FieldValidationState, PendingConfirmation, RegistryEntry, RenderContext, RenderLimits, RepeatScope, SetState, SpecCatalog, SpecCheck, SpecCheckIssue, SpecCheckIssueCode, SpecCheckOptions, SpecValidationMode, StateChange, TokenUsage, UIStreamOptions, UIStreamReturn, UIStreamSendOptions };
|