@object-ui/sdui-parser 17.7.0 → 18.0.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/dist/dashboard-widget-options.d.ts +20 -24
- package/dist/dashboard-widget-options.js +3 -3
- package/dist/index.d.ts +22 -0
- package/dist/index.js +19 -0
- package/dist/parse.d.ts +1 -1
- package/dist/parse.js +1 -1
- package/dist/types.d.ts +28 -5
- package/dist/types.js +2 -2
- package/dist/validate.d.ts +7 -0
- package/dist/validate.js +55 -0
- package/package.json +5 -4
|
@@ -26,23 +26,18 @@
|
|
|
26
26
|
* dateGranularity, sortBy, sortOrder, limit (query-affecting, framework#3588)
|
|
27
27
|
* stageOrder (funnel/pyramid stage order)
|
|
28
28
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* alone — warning on a key the platform's own pipeline writes would be a
|
|
42
|
-
* false positive on legal metadata — while the dataset-bound render path
|
|
43
|
-
* displayed it nowhere. #7293 closed that gap, so `description` is now
|
|
44
|
-
* accepted for the same reason as the declared five and the accepted set no
|
|
45
|
-
* longer outruns the measured read set.
|
|
29
|
+
* and nothing else. ⛔ Not `description`: it was the metric-card sub-caption
|
|
30
|
+
* (objectui#4032 item 4, objectui#7293), read by `DatasetWidget.tsx`'s metric
|
|
31
|
+
* branch and a resolver both dashboard surfaces called, and fed by the
|
|
32
|
+
* server's `translateDashboard` overlay of the `widgets.{id}.subCaption`
|
|
33
|
+
* translation. The spec never declared it. objectui#11389 (ruling C, which
|
|
34
|
+
* reverses objectstack#5428 item 4) retires it at both ends: objectstack
|
|
35
|
+
* first, in `@objectstack/spec` 17.7.0 (the overlay is gone and the
|
|
36
|
+
* translation key is a tombstone), then the readers here. So an authored
|
|
37
|
+
* `options.description` reaches no renderer and draws this warning like any
|
|
38
|
+
* other unconsumed key. A widget keeps one authored description,
|
|
39
|
+
* `widget.description`, the card-header subtitle, which is not an `options`
|
|
40
|
+
* key and is not judged here.
|
|
46
41
|
*
|
|
47
42
|
* Notably NOT consumed on the path a widget really renders through:
|
|
48
43
|
* `thresholds` and `format`. Both were widely believed to work; both draw this
|
|
@@ -70,10 +65,9 @@
|
|
|
70
65
|
* legitimate code gets deleted by the next person who hits it, which puts
|
|
71
66
|
* the claim back where it started. Leg 2 of
|
|
72
67
|
* `__tests__/dashboard-widget-options-census.test.ts` derives exactly this
|
|
73
|
-
* bound: the DatasetWidget read set is the declared set
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* riding on an equality whose right-hand side can grow.
|
|
68
|
+
* bound: the DatasetWidget read set is the declared set, and neither
|
|
69
|
+
* `format` nor `thresholds` is in it. That absence is asserted in its OWN
|
|
70
|
+
* right rather than riding on an equality whose right-hand side can grow.
|
|
77
71
|
*
|
|
78
72
|
* ## Scope — where the warning deliberately does NOT fire
|
|
79
73
|
*
|
|
@@ -99,7 +93,8 @@
|
|
|
99
93
|
* `@objectstack/spec`, re-extracts the `options.<key>` reads from
|
|
100
94
|
* `DatasetWidget.tsx` source text (and fails loudly if that file gains a
|
|
101
95
|
* consumption shape the extractor cannot see — a spread, a destructuring, a
|
|
102
|
-
* computed access), re-checks the sub-caption
|
|
96
|
+
* computed access), re-checks that the retired sub-caption key has no read
|
|
97
|
+
* site left in `plugin-dashboard`, and trips on any NEW
|
|
103
98
|
* file in `packages/*\/src` or `apps/*\/src` that starts reading
|
|
104
99
|
* `widget.options`. A renderer change that adds or removes a consumed key
|
|
105
100
|
* fails that test until this list is updated — the cost of keeping this
|
|
@@ -116,8 +111,9 @@ export declare const UNCONSUMED_WIDGET_OPTION = "unconsumed-widget-option";
|
|
|
116
111
|
export declare const DASHBOARD_WIDGET_HOST_TYPES: ReadonlySet<string>;
|
|
117
112
|
/**
|
|
118
113
|
* The accepted set: every `options` key with a renderer read site on the
|
|
119
|
-
* dataset-bound path,
|
|
120
|
-
* warning message prints it verbatim. Derivation and
|
|
114
|
+
* dataset-bound path, which is exactly the set the spec declares.
|
|
115
|
+
* Alphabetical; the warning message prints it verbatim. Derivation and
|
|
116
|
+
* evidence: file header.
|
|
121
117
|
*/
|
|
122
118
|
export declare const CONSUMED_WIDGET_OPTION_KEYS: readonly string[];
|
|
123
119
|
/**
|
|
@@ -11,12 +11,12 @@ export const DASHBOARD_WIDGET_HOST_TYPES = new Set([
|
|
|
11
11
|
]);
|
|
12
12
|
/**
|
|
13
13
|
* The accepted set: every `options` key with a renderer read site on the
|
|
14
|
-
* dataset-bound path,
|
|
15
|
-
* warning message prints it verbatim. Derivation and
|
|
14
|
+
* dataset-bound path, which is exactly the set the spec declares.
|
|
15
|
+
* Alphabetical; the warning message prints it verbatim. Derivation and
|
|
16
|
+
* evidence: file header.
|
|
16
17
|
*/
|
|
17
18
|
export const CONSUMED_WIDGET_OPTION_KEYS = [
|
|
18
19
|
'dateGranularity',
|
|
19
|
-
'description',
|
|
20
20
|
'limit',
|
|
21
21
|
'sortBy',
|
|
22
22
|
'sortOrder',
|
package/dist/index.d.ts
CHANGED
|
@@ -29,6 +29,15 @@ export interface CompileResult {
|
|
|
29
29
|
* → validate against the manifest → derive `requires` + binding sites.
|
|
30
30
|
*/
|
|
31
31
|
export declare function compile(source: string, manifest: Manifest): CompileResult;
|
|
32
|
+
/**
|
|
33
|
+
* The shape of one `@object-ui/types` `NodeSlotDeclaration`, restated
|
|
34
|
+
* structurally because this package imports nothing at runtime: a position in
|
|
35
|
+
* that package's key-path grammar, and whether its spelling is retired.
|
|
36
|
+
*/
|
|
37
|
+
export interface NodeSlotDeclarationLike {
|
|
38
|
+
readonly path: string;
|
|
39
|
+
readonly retired?: boolean;
|
|
40
|
+
}
|
|
32
41
|
export interface RegistryConfigLike {
|
|
33
42
|
type: string;
|
|
34
43
|
namespace?: string;
|
|
@@ -139,8 +148,21 @@ export declare function assertFullyLoaded(configs: RegistryConfigLike[]): void;
|
|
|
139
148
|
* no renderer read and sailed through it green (objectstack#4413; corrected in
|
|
140
149
|
* objectstack#4472). Evidence about the render path has to come from the render
|
|
141
150
|
* path — see `apps/console/src/__tests__/public-block-binding-reach.test.tsx`.
|
|
151
|
+
*
|
|
152
|
+
* `opts.slotsFor` is the one exception to "copied from the registration", and
|
|
153
|
+
* it is a DECLARATION too (objectui#11170): hand it `nodeSlotsFor` from
|
|
154
|
+
* `@object-ui/types` and every entry carries `slots`, the node-slot positions
|
|
155
|
+
* that type's renderer reads besides `children`, so `validateTree` judges the
|
|
156
|
+
* nodes under them. This package has no runtime dependency and so cannot
|
|
157
|
+
* import the declaration itself; the producers that build a live manifest
|
|
158
|
+
* (`getJsxManifest` in `@object-ui/components`' page renderer, the console's
|
|
159
|
+
* manifest generator) pass it. A RETIRED position is left out here: this is
|
|
160
|
+
* the authoring tier, and the spec's own authoring walks skip the tombstoned
|
|
161
|
+
* spellings for the same reason. Without the option no `slots` key is written,
|
|
162
|
+
* so every manifest built before the option existed serialises byte-identically.
|
|
142
163
|
*/
|
|
143
164
|
export declare function manifestFromConfigs(configs: RegistryConfigLike[], opts?: {
|
|
144
165
|
only?: Set<string>;
|
|
145
166
|
publicOnly?: boolean;
|
|
167
|
+
slotsFor?: (type: string) => readonly NodeSlotDeclarationLike[];
|
|
146
168
|
}): Manifest;
|
package/dist/index.js
CHANGED
|
@@ -77,6 +77,18 @@ export function assertFullyLoaded(configs) {
|
|
|
77
77
|
* no renderer read and sailed through it green (objectstack#4413; corrected in
|
|
78
78
|
* objectstack#4472). Evidence about the render path has to come from the render
|
|
79
79
|
* path — see `apps/console/src/__tests__/public-block-binding-reach.test.tsx`.
|
|
80
|
+
*
|
|
81
|
+
* `opts.slotsFor` is the one exception to "copied from the registration", and
|
|
82
|
+
* it is a DECLARATION too (objectui#11170): hand it `nodeSlotsFor` from
|
|
83
|
+
* `@object-ui/types` and every entry carries `slots`, the node-slot positions
|
|
84
|
+
* that type's renderer reads besides `children`, so `validateTree` judges the
|
|
85
|
+
* nodes under them. This package has no runtime dependency and so cannot
|
|
86
|
+
* import the declaration itself; the producers that build a live manifest
|
|
87
|
+
* (`getJsxManifest` in `@object-ui/components`' page renderer, the console's
|
|
88
|
+
* manifest generator) pass it. A RETIRED position is left out here: this is
|
|
89
|
+
* the authoring tier, and the spec's own authoring walks skip the tombstoned
|
|
90
|
+
* spellings for the same reason. Without the option no `slots` key is written,
|
|
91
|
+
* so every manifest built before the option existed serialises byte-identically.
|
|
80
92
|
*/
|
|
81
93
|
export function manifestFromConfigs(configs, opts = {}) {
|
|
82
94
|
const components = {};
|
|
@@ -85,10 +97,17 @@ export function manifestFromConfigs(configs, opts = {}) {
|
|
|
85
97
|
continue;
|
|
86
98
|
if (opts.publicOnly && c.tier !== 'public')
|
|
87
99
|
continue;
|
|
100
|
+
const slots = opts.slotsFor
|
|
101
|
+
? opts.slotsFor(c.type).filter((s) => !s.retired).map((s) => s.path)
|
|
102
|
+
: undefined;
|
|
88
103
|
components[c.type] = {
|
|
89
104
|
type: c.type,
|
|
90
105
|
namespace: c.namespace,
|
|
91
106
|
isContainer: c.isContainer,
|
|
107
|
+
// `undefined` is dropped by `JSON.stringify`, so a manifest built with
|
|
108
|
+
// no resolver — or an entry with no slots — serialises exactly as before
|
|
109
|
+
// this key existed.
|
|
110
|
+
slots: slots && slots.length > 0 ? slots : undefined,
|
|
92
111
|
// The html tier's stamp, and ONLY that stamp (objectui#10735): a
|
|
93
112
|
// registration's `'public'` / `'internal'` is registry mechanics the
|
|
94
113
|
// manifest never carried, and `undefined` is dropped by `JSON.stringify`,
|
package/dist/parse.d.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* A small recursive-descent parser for a CONSTRAINED JSX subset. It is
|
|
5
5
|
* deliberately not a full JS/JSX parser: a bounded grammar is the point
|
|
6
6
|
* (Markdoc model) — it shrinks the attack surface and the expressible-but-wrong
|
|
7
|
-
* space. Output is
|
|
7
|
+
* space. Output is an SDUI node tree (`ParsedSchemaNode`). Nothing is executed.
|
|
8
8
|
*
|
|
9
9
|
* Grammar (informal):
|
|
10
10
|
* document := element (exactly one root)
|
package/dist/parse.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* A small recursive-descent parser for a CONSTRAINED JSX subset. It is
|
|
5
5
|
* deliberately not a full JS/JSX parser: a bounded grammar is the point
|
|
6
6
|
* (Markdoc model) — it shrinks the attack surface and the expressible-but-wrong
|
|
7
|
-
* space. Output is
|
|
7
|
+
* space. Output is an SDUI node tree (`ParsedSchemaNode`). Nothing is executed.
|
|
8
8
|
*
|
|
9
9
|
* Grammar (informal):
|
|
10
10
|
* document := element (exactly one root)
|
package/dist/types.d.ts
CHANGED
|
@@ -2,14 +2,24 @@
|
|
|
2
2
|
* ObjectUI — SDUI JSX-source parser (ADR-0080)
|
|
3
3
|
*
|
|
4
4
|
* Types shared by the constrained JSX-source compiler. The parser turns a
|
|
5
|
-
* constrained JSX *text* into
|
|
6
|
-
*
|
|
5
|
+
* constrained JSX *text* into an SDUI node tree (`ParsedSchemaNode`, below).
|
|
6
|
+
* It PARSES — it never executes. No `import`, no `eval`, no JS.
|
|
7
7
|
*/
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* A node in the compiled SDUI tree, as parsed: an element or a text child.
|
|
10
|
+
*
|
|
11
|
+
* Not `SchemaNode`: `@object-ui/types` publishes that name for the closed
|
|
12
|
+
* AUTHORING face, a union discriminated on a literal `type` with no index
|
|
13
|
+
* signature and with primitive children admitted. This is the parser's open,
|
|
14
|
+
* as-parsed data (any `type` string, the keys as authored), neither is
|
|
15
|
+
* assignable to the other, and this package takes no runtime dependency on
|
|
16
|
+
* `@object-ui/types` to re-export from. One exported name stood for both until
|
|
17
|
+
* this declaration was renamed (objectui#6349, batch 11).
|
|
18
|
+
*/
|
|
19
|
+
export type ParsedSchemaNode = SchemaElement | string;
|
|
10
20
|
export interface SchemaElement {
|
|
11
21
|
type: string;
|
|
12
|
-
children?:
|
|
22
|
+
children?: ParsedSchemaNode[];
|
|
13
23
|
[prop: string]: unknown;
|
|
14
24
|
}
|
|
15
25
|
export type Severity = 'error' | 'warning';
|
|
@@ -133,6 +143,19 @@ export interface ManifestComponent {
|
|
|
133
143
|
* carry no such entry (objectui#9910; `acceptsChildren` in `validate.ts`).
|
|
134
144
|
*/
|
|
135
145
|
inputs: ManifestInput[];
|
|
146
|
+
/**
|
|
147
|
+
* The NODE SLOTS this component's renderer reads besides `children`
|
|
148
|
+
* (objectui#11170): positions in the key-path grammar `@object-ui/types`'
|
|
149
|
+
* `NODE_SLOT_DECLARATIONS` states — `trigger`, `items[].content`,
|
|
150
|
+
* `regions[].components`. `validateTree` descends each one the way it
|
|
151
|
+
* descends `children`, so a slot-held node is judged too. Projected from
|
|
152
|
+
* that one declaration by `manifestFromConfigs`'s `slotsFor` option; the
|
|
153
|
+
* authoring tier leaves the declaration's RETIRED positions out, as the
|
|
154
|
+
* spec's own authoring walks do. ⛔ This tier keeps no slot list of its own:
|
|
155
|
+
* a manifest built without the option carries none, and the walk then
|
|
156
|
+
* follows `children` alone.
|
|
157
|
+
*/
|
|
158
|
+
slots?: readonly string[];
|
|
136
159
|
/**
|
|
137
160
|
* LAYOUT containment (objectui#6804, objectui#9910 Q2-A): the flag the
|
|
138
161
|
* react-page JSX scope builder skips and the public layout ledger lists.
|
package/dist/types.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* ObjectUI — SDUI JSX-source parser (ADR-0080)
|
|
3
3
|
*
|
|
4
4
|
* Types shared by the constrained JSX-source compiler. The parser turns a
|
|
5
|
-
* constrained JSX *text* into
|
|
6
|
-
*
|
|
5
|
+
* constrained JSX *text* into an SDUI node tree (`ParsedSchemaNode`, below).
|
|
6
|
+
* It PARSES — it never executes. No `import`, no `eval`, no JS.
|
|
7
7
|
*/
|
|
8
8
|
export {};
|
package/dist/validate.d.ts
CHANGED
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
* `requires` (plugin provenance) and binding sites the SERVER must resolve
|
|
9
9
|
* against object schema (we cannot resolve objects/fields here — that check is
|
|
10
10
|
* framework-side by design).
|
|
11
|
+
*
|
|
12
|
+
* The walk descends `children` on every node and, for a component the
|
|
13
|
+
* manifest knows, the NODE SLOTS its entry carries (`ManifestComponent.slots`,
|
|
14
|
+
* objectui#11170) — the positions besides `children` through which that
|
|
15
|
+
* renderer hands nodes back to `SchemaRenderer`, projected from the one
|
|
16
|
+
* declaration in `@object-ui/types` by `manifestFromConfigs`. ⛔ No slot list
|
|
17
|
+
* lives here.
|
|
11
18
|
*/
|
|
12
19
|
import type { Manifest, ManifestComponent, SchemaElement, ManifestValidationResult } from './types.js';
|
|
13
20
|
/**
|
package/dist/validate.js
CHANGED
|
@@ -8,6 +8,13 @@
|
|
|
8
8
|
* `requires` (plugin provenance) and binding sites the SERVER must resolve
|
|
9
9
|
* against object schema (we cannot resolve objects/fields here — that check is
|
|
10
10
|
* framework-side by design).
|
|
11
|
+
*
|
|
12
|
+
* The walk descends `children` on every node and, for a component the
|
|
13
|
+
* manifest knows, the NODE SLOTS its entry carries (`ManifestComponent.slots`,
|
|
14
|
+
* objectui#11170) — the positions besides `children` through which that
|
|
15
|
+
* renderer hands nodes back to `SchemaRenderer`, projected from the one
|
|
16
|
+
* declaration in `@object-ui/types` by `manifestFromConfigs`. ⛔ No slot list
|
|
17
|
+
* lives here.
|
|
11
18
|
*/
|
|
12
19
|
import { inputTypeArms } from './input-type.js';
|
|
13
20
|
import { checkDashboardWidgetOptions } from './dashboard-widget-options.js';
|
|
@@ -126,6 +133,46 @@ const BASE_PROPS = basePropNames('every-node');
|
|
|
126
133
|
/** The `'where-undeclared'` members of {@link SDUI_BASE_PROPS}. */
|
|
127
134
|
const WHERE_UNDECLARED_BASE_PROPS = basePropNames('where-undeclared');
|
|
128
135
|
const isExpr = (v) => typeof v === 'object' && v !== null && '$expr' in v;
|
|
136
|
+
const isPlainObject = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
137
|
+
const isElement = (v) => isPlainObject(v) && typeof v.type === 'string';
|
|
138
|
+
/**
|
|
139
|
+
* The element nodes a slot position holds on `node`, in document order.
|
|
140
|
+
*
|
|
141
|
+
* The position is spelled in `@object-ui/types`' key-path grammar, which that
|
|
142
|
+
* package's `nodeSlotValues` implements for the other two readers and whose
|
|
143
|
+
* header states it: keys separated by `.`, a key followed by `[]` meaning each
|
|
144
|
+
* element of the array under it, and the value at the end being one node or a
|
|
145
|
+
* list of nodes. Restated here because this package imports nothing at
|
|
146
|
+
* runtime; the components-side census for objectui#11170 holds the two walks
|
|
147
|
+
* to the same answer over the same fixtures. Only element-shaped values are
|
|
148
|
+
* returned — a string child is legal in a slot and has nothing to validate.
|
|
149
|
+
*/
|
|
150
|
+
function slotElements(node, path) {
|
|
151
|
+
let holders = [node];
|
|
152
|
+
for (const segment of path.split('.')) {
|
|
153
|
+
const each = segment.endsWith('[]');
|
|
154
|
+
const key = each ? segment.slice(0, -2) : segment;
|
|
155
|
+
const next = [];
|
|
156
|
+
for (const holder of holders) {
|
|
157
|
+
if (!isPlainObject(holder))
|
|
158
|
+
continue;
|
|
159
|
+
const value = holder[key];
|
|
160
|
+
if (!each)
|
|
161
|
+
next.push(value);
|
|
162
|
+
else if (Array.isArray(value))
|
|
163
|
+
next.push(...value);
|
|
164
|
+
}
|
|
165
|
+
holders = next;
|
|
166
|
+
}
|
|
167
|
+
const found = [];
|
|
168
|
+
for (const holder of holders) {
|
|
169
|
+
for (const value of Array.isArray(holder) ? holder : [holder]) {
|
|
170
|
+
if (isElement(value))
|
|
171
|
+
found.push(value);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return found;
|
|
175
|
+
}
|
|
129
176
|
export function validateTree(tree, manifest) {
|
|
130
177
|
const diagnostics = [];
|
|
131
178
|
const requires = new Set();
|
|
@@ -263,6 +310,14 @@ export function validateTree(tree, manifest) {
|
|
|
263
310
|
}
|
|
264
311
|
if (node.children)
|
|
265
312
|
node.children.forEach(visit);
|
|
313
|
+
// The node slots this component's renderer reads besides `children`
|
|
314
|
+
// (objectui#11170), as its manifest entry declares them. An unknown
|
|
315
|
+
// component has no entry and so no slots: its `unknown-component` above is
|
|
316
|
+
// the answer, and nothing under it is judged.
|
|
317
|
+
for (const path of comp?.slots ?? []) {
|
|
318
|
+
for (const element of slotElements(node, path))
|
|
319
|
+
visit(element);
|
|
320
|
+
}
|
|
266
321
|
};
|
|
267
322
|
if (tree)
|
|
268
323
|
visit(tree);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@object-ui/sdui-parser",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "18.0.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,9 +25,10 @@
|
|
|
25
25
|
"dist"
|
|
26
26
|
],
|
|
27
27
|
"devDependencies": {
|
|
28
|
-
"@objectstack/spec": "^
|
|
29
|
-
"@object-ui/core": "
|
|
30
|
-
"@object-ui/react": "
|
|
28
|
+
"@objectstack/spec": "^18.0.0",
|
|
29
|
+
"@object-ui/core": "18.0.0",
|
|
30
|
+
"@object-ui/react": "18.0.0",
|
|
31
|
+
"@object-ui/types": "18.0.0"
|
|
31
32
|
},
|
|
32
33
|
"scripts": {
|
|
33
34
|
"build": "tsc && node ../../scripts/check-dist-completeness.mjs",
|