@objectstack/sdui-parser 17.1.0 → 17.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/CHANGELOG.md +193 -0
- package/dist/index.d.mts +212 -7
- package/dist/index.d.ts +212 -7
- package/dist/index.js +364 -53
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +357 -53
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,198 @@
|
|
|
1
1
|
# @objectstack/sdui-parser
|
|
2
2
|
|
|
3
|
+
## 17.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 2182bd1: sdui-parser: `interpretBrace` materializes the JS literal subset, in lockstep with objectui
|
|
8
|
+
|
|
9
|
+
The html tier's braced attribute values accepted strict JSON only, so the spelling every
|
|
10
|
+
JSX author and every AI author writes — `columns={['name','amount']}` — compiled to the
|
|
11
|
+
deferred `{ $expr }` marker that nothing downstream evaluates, and the author's data
|
|
12
|
+
binding vanished at render. Under the maintainer's ruling on objectui#6614 (Q1-A,
|
|
13
|
+
2026-08-28) `interpretBrace` now materializes the JS **literal subset**: exactly two
|
|
14
|
+
widenings over JSON — single-quoted strings (value position and key position) and unquoted
|
|
15
|
+
identifier object keys.
|
|
16
|
+
|
|
17
|
+
Everything else JSON refuses is still refused and still becomes `{ $expr }`: trailing
|
|
18
|
+
commas, comments, array holes, spreads, `undefined` / `NaN` / `Infinity`, `+1` / `.5` /
|
|
19
|
+
`1.` / `0x1f`, template literals, and every genuine expression. `JSON.parse` still runs
|
|
20
|
+
first and untouched, so strict-JSON behaviour is invariant by construction, and the subset
|
|
21
|
+
contains no identifier lookup and no operator — the widening moves habitual spellings onto
|
|
22
|
+
the materialized side, it does not move the data/code boundary (ADR-0080: this tier parses,
|
|
23
|
+
never executes).
|
|
24
|
+
|
|
25
|
+
An authored `__proto__` key is written as an own data property, the way `JSON.parse` gives
|
|
26
|
+
it, never through the prototype setter — a plain assignment in the unquoted-key path would
|
|
27
|
+
hand untrusted page source a prototype-pollution lever the strict-JSON path never had.
|
|
28
|
+
|
|
29
|
+
The `inert-expression` diagnostic message is reworded to match: the old text advised
|
|
30
|
+
writing the value as JSON with double-quoted strings and keys, which now names a legal
|
|
31
|
+
spelling as the wrong one. Diagnostic **codes** are unchanged.
|
|
32
|
+
- 2a5c1cd: html tier: a braced attribute value that is not strict JSON now draws an `inert-expression` warning instead of vanishing silently
|
|
33
|
+
|
|
34
|
+
`interpretBrace` materializes strict-JSON values only; anything else — the
|
|
35
|
+
single-quoted array every JSX author writes (`columns={['name','amount']}`),
|
|
36
|
+
unquoted object keys, any JS expression — compiles to the deferred `{ $expr }`
|
|
37
|
+
marker, and nothing downstream evaluates that marker: this tier parses, never
|
|
38
|
+
executes (ADR-0080), and no renderer consumes `$expr`. The value reached the
|
|
39
|
+
renderer as an opaque object, defensive non-array/non-object reads degraded it
|
|
40
|
+
to "not declared", and the author's binding vanished with zero diagnostics
|
|
41
|
+
anywhere — a production page's `list-view` rendered its row count and toolbar
|
|
42
|
+
with no data columns, through eight `columns` spellings (objectui#6598). That
|
|
43
|
+
is ADR-0078's prohibited parsed-but-silently-inert state.
|
|
44
|
+
|
|
45
|
+
`validateTree` now emits a warning-severity `inert-expression` diagnostic when a
|
|
46
|
+
declared input's value is the `$expr` marker, with the fix in the message: write
|
|
47
|
+
the value as JSON (double-quoted strings and keys).
|
|
48
|
+
|
|
49
|
+
This is the lockstep port of objectui PR #6613 into this repo's hoisted copy of
|
|
50
|
+
the parser. There are two copies, and the invariant is that both agree on the
|
|
51
|
+
accepted grammar **and** on diagnostic codes — if they drift, the save gate and
|
|
52
|
+
the renderer speak different dialects, and a page can save clean and render
|
|
53
|
+
inert. The emitted diagnostic is byte-equal to objectui's.
|
|
54
|
+
|
|
55
|
+
Warning, not error, per the objectui#5709 posture for inert authored keys: this
|
|
56
|
+
reports an **already**-inert state, so the accept/reject set does not move.
|
|
57
|
+
Pages that compiled before still compile, and a warning is non-gating on every
|
|
58
|
+
consuming surface in this repo (`runtime-gate` files warnings as advisories, not
|
|
59
|
+
as write refusals; `os lint` exits non-zero on error-severity findings only).
|
|
60
|
+
The silence is what changed. Escalating the severity, widening the accepted
|
|
61
|
+
literal grammar (single-quoted strings, unquoted keys), and wiring the registry
|
|
62
|
+
manifest into `validate-jsx-pages` — without which this warning is recorded in
|
|
63
|
+
compile output but displayed by no production surface — are separate decisions
|
|
64
|
+
tracked on objectui#6614 and its follow-ups.
|
|
65
|
+
- 0e68ed2: html tier: an authored `type=` attribute is now refused at parse time instead of overwriting the component discriminator
|
|
66
|
+
|
|
67
|
+
On a `kind:'html'` page the tag name **is** the node's `type`, so a `type` attribute is a
|
|
68
|
+
name collision with the envelope's own discriminator. The parser now refuses it with one
|
|
69
|
+
`forbidden-attr` error naming **both** the tag and the attribute — *Attribute "type" is
|
|
70
|
+
not allowed on `<flex>` — on this tier the tag name IS the component…* — replacing two
|
|
71
|
+
outcomes, neither good:
|
|
72
|
+
|
|
73
|
+
- the value named another **registered** type (`<flex type="grid">`): the tree carried the
|
|
74
|
+
author's value as its discriminator, `validateTree` resolved `grid` in the manifest,
|
|
75
|
+
every check passed, and the page rendered a grid where the author wrote a flex — **zero
|
|
76
|
+
diagnostics**, on the one tier whose premise is that unreviewed, AI-authored source is
|
|
77
|
+
safe to accept;
|
|
78
|
+
- the value named **nothing** registered (`<object-chart type="bar">`, the shape a
|
|
79
|
+
react-tier author carries across): `unknown-component` naming `"bar"`, which reads as a
|
|
80
|
+
missing plugin rather than as an attribute that should not be there.
|
|
81
|
+
|
|
82
|
+
Alongside the refusal, `parseElement` builds the node as `{ ...props, type: tag }` rather
|
|
83
|
+
than `{ type: tag, ...props }` — defense in depth, and correct only *because* the
|
|
84
|
+
attribute is refused loudly: reversing the spread alone would trade a silent overwrite for
|
|
85
|
+
a silent discard.
|
|
86
|
+
|
|
87
|
+
The react tier is unaffected: its `specType` rescue (objectui#2880) stays where it lives
|
|
88
|
+
and is deliberately **not** carried over — the two tiers are two source formats, and a
|
|
89
|
+
consumer-side alias on a second tier is the tolerance ADR-0080's amendment declined.
|
|
90
|
+
`validate.ts`'s `BASE_PROPS` is unchanged (`type` is correct there for every other
|
|
91
|
+
member), and no warning grace period is introduced.
|
|
92
|
+
|
|
93
|
+
**This narrows what the html tier accepts**: a page that compiles today with a `type=`
|
|
94
|
+
attribute will be refused. The in-repo migration surface was measured before the change
|
|
95
|
+
and is **zero** — no html-tier page source under `content/docs/**` or the example apps
|
|
96
|
+
carries one. Maintainer ruling 2026-09-01, recorded as an amendment on ADR-0080.
|
|
97
|
+
- 8beb3de: html tier: a dashboard widget `options` key that reaches no renderer now draws an `unconsumed-widget-option` warning naming the consumed set
|
|
98
|
+
|
|
99
|
+
`@objectstack/spec`'s `DashboardWidgetOptionsSchema` ends in `.passthrough()`
|
|
100
|
+
("declared query keys + open renderer extras"), so ANY key parses, validates
|
|
101
|
+
and lints cleanly — including one no renderer reads. That is how a dashboard
|
|
102
|
+
shipped `options: { invert: true }` on a gauge with a comment saying what it
|
|
103
|
+
was believed to do and rendered the un-inverted measure with no diagnostic
|
|
104
|
+
anywhere (objectui#5709). The 2026-08-23 maintainer ruling on that card: open
|
|
105
|
+
extras stay open — they just stop being **silent**. A key that reaches no
|
|
106
|
+
renderer draws a **warning** naming the consumed set.
|
|
107
|
+
|
|
108
|
+
objectui's copy of this parser has emitted that warning since the ruling
|
|
109
|
+
landed; this repo's hoisted copy emitted nothing, so the same authored page
|
|
110
|
+
produced a diagnostic on one surface and silence on the other — the dialect
|
|
111
|
+
split the two copies' invariant forbids (objectstack#12719 — both copies agree
|
|
112
|
+
on the accepted grammar **and** on diagnostic codes). `validateTree` now ends
|
|
113
|
+
its known-component branch with `checkDashboardWidgetOptions(node)`, and the
|
|
114
|
+
new module is a byte-equal port of objectui's save for one token (the emitted
|
|
115
|
+
`code` is spelled as an inline literal rather than through the exported
|
|
116
|
+
constant, so this repo's ADR-0112 vocabulary gate can classify it — called out
|
|
117
|
+
at the site, and pinned equal to the constant by test), so the emitted `code`,
|
|
118
|
+
`severity`, `message` and census scope are identical.
|
|
119
|
+
|
|
120
|
+
The warning is scoped to the only spec-legal render path: a `dashboard` /
|
|
121
|
+
`dashboard-grid` host, a widget with a `dataset`, not in the legacy
|
|
122
|
+
`component` format, and not carrying the spec's own
|
|
123
|
+
`suppressWarnings: ['unconsumed-widget-option']` escape hatch. The consumed set
|
|
124
|
+
is the five keys `DashboardWidgetOptionsSchema` declares (`dateGranularity`,
|
|
125
|
+
`sortBy`, `sortOrder`, `limit`, `stageOrder`) plus `description`, the metric
|
|
126
|
+
sub-caption channel `translateDashboard` writes into `options`.
|
|
127
|
+
|
|
128
|
+
New exports for third-party manifest consumers: `checkDashboardWidgetOptions`,
|
|
129
|
+
`CONSUMED_WIDGET_OPTION_KEYS`, `DASHBOARD_WIDGET_HOST_TYPES` and
|
|
130
|
+
`UNCONSUMED_WIDGET_OPTION` (the diagnostic code, which is also the id
|
|
131
|
+
`suppressWarnings` suppresses).
|
|
132
|
+
|
|
133
|
+
Unlike the union-arm port that preceded it, this change is **additive**: it
|
|
134
|
+
reports an already-inert state and emits `warning` only, so what this copy
|
|
135
|
+
accepts and rejects is exactly where it stood — pinned by a dedicated test.
|
|
136
|
+
Today it is latent in the production gate anyway: this repo resolves no
|
|
137
|
+
`sdui.manifest.json`, so `validateJsxPages` runs parse-only and `validateTree`
|
|
138
|
+
is not reached from it. Wiring that manifest (the second gap recorded on
|
|
139
|
+
objectstack#12719, still unowned) is what makes this author-visible, and this
|
|
140
|
+
port lands ahead of that wiring deliberately.
|
|
141
|
+
- 4a9f461: html tier: a union-typed manifest input is now coarse-type-checked over every declared arm instead of drawing no diagnostic at all
|
|
142
|
+
|
|
143
|
+
`ManifestInput.type` now carries ONE coarse kind, or an ARRAY of kinds when the
|
|
144
|
+
key's contract is a union (objectui#3832). Before this change, this copy's
|
|
145
|
+
`checkType` was the older single-arm `switch (input.type)`: a manifest input
|
|
146
|
+
declaring a union fell through `default: return null` and drew **no diagnostic
|
|
147
|
+
at all** — silence indistinguishable from a value that validated cleanly —
|
|
148
|
+
while objectui's copy checked every arm. The same authored page produced
|
|
149
|
+
diagnostics on one surface and none on the other: the dialect split the two
|
|
150
|
+
parser copies' invariant forbids (objectstack#12719 — both copies agree on the
|
|
151
|
+
accepted grammar **and** on diagnostic codes).
|
|
152
|
+
|
|
153
|
+
`validateTree`'s coarse check now clears a prop when **any** declared arm
|
|
154
|
+
accepts the value, and when **no** arm accepts it emits **one** `type-mismatch`
|
|
155
|
+
diagnostic naming every arm — at `error` severity when an `enum` arm is
|
|
156
|
+
present (an enum's closed list is the one fact this layer can be certain
|
|
157
|
+
about), `warning` otherwise. A single-arm input produces the byte-identical
|
|
158
|
+
diagnostic it always did, `invalid-enum` included. `generateDts` emits a
|
|
159
|
+
TypeScript union for a union declaration, and `manifestFromConfigs`
|
|
160
|
+
canonicalizes union declarations through the new `input-type.ts` module
|
|
161
|
+
(`inputTypeArms`, `canonicalizeInputType`, `MANIFEST_INPUT_TYPES` — all
|
|
162
|
+
exported, so third-party manifest consumers read arms through the same
|
|
163
|
+
accessor the gate does).
|
|
164
|
+
|
|
165
|
+
This is the lockstep port of the objectui#3832 ruling into this repo's hoisted
|
|
166
|
+
copy of the parser — the ported check is byte-equal to objectui's. It changes
|
|
167
|
+
what the save gate accepts and rejects for union-typed inputs: a value fitting
|
|
168
|
+
no arm of an enum-carrying union now draws an `error` where it previously drew
|
|
169
|
+
nothing. Today that change is latent in the production gate — this repo
|
|
170
|
+
resolves no `sdui.manifest.json`, so `validateJsxPages` runs parse-only; wiring
|
|
171
|
+
the manifest (the second gap recorded on objectstack#12719) is what makes it
|
|
172
|
+
author-visible, and this port lands ahead of that wiring deliberately.
|
|
173
|
+
|
|
174
|
+
### Patch Changes
|
|
175
|
+
|
|
176
|
+
- 34f60b7: The JSX-source parser no longer deletes the space that separates a text run
|
|
177
|
+
from an adjacent sibling element. `parseChildren` collapsed each text run's
|
|
178
|
+
whitespace to a single space (correct — that is HTML's own whitespace model)
|
|
179
|
+
and then `.trim()`ed it (not correct — HTML collapses a whitespace run to one
|
|
180
|
+
space, it does not delete it), so `A <strong>x</strong> page` compiled to
|
|
181
|
+
`['A', {strong}, 'page']` and the words ran together wherever that tree is
|
|
182
|
+
rendered.
|
|
183
|
+
|
|
184
|
+
The rule now applied: collapse the run, then keep one leading space when a
|
|
185
|
+
sibling precedes it and one trailing space when a sibling element follows it;
|
|
186
|
+
at the parent's own start/end the edge space is still dropped, so
|
|
187
|
+
`<p> hi </p>` still compiles to `['hi']`. It is deliberately mechanical — it
|
|
188
|
+
invents no block/inline taxonomy for a schema tree that has none. Its one
|
|
189
|
+
bounded cost is that a whitespace-only run between two siblings survives as a
|
|
190
|
+
single space, so a pretty-printed `<ul>` gains one `' '` child per inter-item
|
|
191
|
+
gap; the tests pin that bound. This matches the rule the downstream copy of
|
|
192
|
+
this parser already applies, so the two agree on the tree they produce.
|
|
193
|
+
|
|
194
|
+
## 17.2.0
|
|
195
|
+
|
|
3
196
|
## 17.1.0
|
|
4
197
|
|
|
5
198
|
## 17.0.0
|
package/dist/index.d.mts
CHANGED
|
@@ -39,7 +39,20 @@ interface ParseResult {
|
|
|
39
39
|
type ManifestInputType = 'string' | 'number' | 'boolean' | 'enum' | 'array' | 'object' | 'color' | 'date' | 'code' | 'file' | 'slot';
|
|
40
40
|
interface ManifestInput {
|
|
41
41
|
name: string;
|
|
42
|
-
|
|
42
|
+
/**
|
|
43
|
+
* The input's coarse type: ONE kind, or an ARRAY of kinds when the key's
|
|
44
|
+
* contract is a union (objectui#3832).
|
|
45
|
+
*
|
|
46
|
+
* A value passes {@link validateTree}'s coarse check when ANY arm accepts it,
|
|
47
|
+
* and is reported when none does — the array widens what is legal, it does
|
|
48
|
+
* not switch the check off.
|
|
49
|
+
*
|
|
50
|
+
* The single-kind form is unchanged and stays the canonical spelling for a
|
|
51
|
+
* one-arm key: `manifestFromConfigs` collapses a one-element array back to
|
|
52
|
+
* the bare string, so a manifest gains arrays only where a union was really
|
|
53
|
+
* declared and every already-published entry serializes byte-identically.
|
|
54
|
+
*/
|
|
55
|
+
type: ManifestInputType | ManifestInputType[];
|
|
43
56
|
required?: boolean;
|
|
44
57
|
/** allowed values for `enum` inputs */
|
|
45
58
|
enum?: Array<string | {
|
|
@@ -94,9 +107,25 @@ interface ValidationResult {
|
|
|
94
107
|
declare function parseJsx(source: string, options?: ParseOptions): ParseResult;
|
|
95
108
|
/**
|
|
96
109
|
* Interpret a braced attribute value `{...}`.
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
110
|
+
*
|
|
111
|
+
* Strict-JSON values are materialized by `JSON.parse`, exactly as they always
|
|
112
|
+
* were. Beyond that, the JS **literal subset** below is materialized too
|
|
113
|
+
* (objectui#6614 Q1-A, maintainer ruling 2026-08-28). Anything left over — a
|
|
114
|
+
* genuine expression — is kept as the deferred marker `{ $expr }`: typed and
|
|
115
|
+
* validated later, drawing `inert-expression`, and NEVER evaluated here.
|
|
116
|
+
*
|
|
117
|
+
* ORDER IS LOAD-BEARING. `JSON.parse` runs FIRST and is untouched, so every
|
|
118
|
+
* input JSON accepts takes byte-identically the path it took before the literal
|
|
119
|
+
* subset existed. The reader below only ever sees strings `JSON.parse` has
|
|
120
|
+
* already thrown on, which makes strict-JSON invariance a property of the
|
|
121
|
+
* structure rather than of a test.
|
|
122
|
+
*
|
|
123
|
+
* LOCKSTEP: this grammar is the port of objectui's `packages/sdui-parser` copy
|
|
124
|
+
* (objectui#6614). The two copies must agree on the accepted grammar AND on
|
|
125
|
+
* diagnostic codes — if they drift, the save gate and the renderer speak
|
|
126
|
+
* different dialects and a page can save clean and render inert
|
|
127
|
+
* (objectstack#12719 states the invariant; #12977 carries this half of it).
|
|
128
|
+
* Change this block only together with the objectui copy.
|
|
100
129
|
*/
|
|
101
130
|
declare function interpretBrace(raw: string): unknown;
|
|
102
131
|
|
|
@@ -111,6 +140,128 @@ declare function interpretBrace(raw: string): unknown;
|
|
|
111
140
|
|
|
112
141
|
declare function validateTree(tree: SchemaElement | null, manifest: Manifest): ValidationResult;
|
|
113
142
|
|
|
143
|
+
/**
|
|
144
|
+
* Unconsumed dashboard-widget `options` keys (objectui#5709), ported into this
|
|
145
|
+
* copy in lockstep (objectstack#12810).
|
|
146
|
+
*
|
|
147
|
+
* `@objectstack/spec`'s `DashboardWidgetOptionsSchema` ends in `.passthrough()`
|
|
148
|
+
* ("declared query keys + open renderer extras"), so ANY key parses, validates
|
|
149
|
+
* and lints cleanly — including one no renderer reads. That is how a showcase
|
|
150
|
+
* dashboard shipped `options: { invert: true }` on a gauge with a comment
|
|
151
|
+
* saying what it was believed to do, and rendered the un-inverted measure with
|
|
152
|
+
* no diagnostic anywhere (objectui#5709). The 2026-08-23 maintainer ruling:
|
|
153
|
+
* open extras stay open — they just stop being SILENT. A key that reaches no
|
|
154
|
+
* renderer draws a WARNING naming the consumed set. Not an error: no gate
|
|
155
|
+
* weakening and no new red gates were ruled.
|
|
156
|
+
*
|
|
157
|
+
* ## LOCKSTEP — what is byte-equal here and what deliberately is not
|
|
158
|
+
*
|
|
159
|
+
* Two copies of this parser exist: objectui's `packages/sdui-parser` and this
|
|
160
|
+
* hoisted `@objectstack/sdui-parser`. The invariant they owe each other is that
|
|
161
|
+
* both agree on the accepted grammar AND on diagnostic codes — if they drift,
|
|
162
|
+
* the save gate and the renderer speak different dialects and a page can save
|
|
163
|
+
* clean and render inert, or the reverse (objectstack#12719, objectstack#12810).
|
|
164
|
+
*
|
|
165
|
+
* Everything from the `import` line below to end of file is a byte-equal port of
|
|
166
|
+
* objectui's `src/dashboard-widget-options.ts` SAVE FOR ONE TOKEN, called out
|
|
167
|
+
* at the site itself: the emitted `code` is spelled as an inline literal here
|
|
168
|
+
* and as the constant there, because this repo runs a vocabulary gate objectui
|
|
169
|
+
* does not. The emitted `code`, `severity`, `message` and the whole census
|
|
170
|
+
* scope are identical, and `__tests__/dashboard-widget-options.test.ts`
|
|
171
|
+
* re-derives that rather than trusting it — including an explicit pin that the
|
|
172
|
+
* literal equals `UNCONSUMED_WIDGET_OPTION`. Change these functions only
|
|
173
|
+
* together with the objectui copy.
|
|
174
|
+
*
|
|
175
|
+
* THIS HEADER is the one deliberate divergence, and it has to be: objectui's
|
|
176
|
+
* header cites the maintenance machinery that derives the census — its
|
|
177
|
+
* `DatasetWidget.tsx` / `DashboardRenderer.tsx` read sites, its
|
|
178
|
+
* `plugin-dashboard.mdx` claim and its two census tests. NONE of those files
|
|
179
|
+
* exists in this repo (measured: no dashboard renderer package here at all), so
|
|
180
|
+
* copying those sentences would ship claims this checkout cannot support and
|
|
181
|
+
* nothing here would ever notice them going false. What follows instead states
|
|
182
|
+
* where each half of the census is derivable, and from what.
|
|
183
|
+
*
|
|
184
|
+
* ## The accepted set, and where each half is authoritative
|
|
185
|
+
*
|
|
186
|
+
* The spec REQUIRES `dataset` on every widget (`DashboardWidgetSchema`, this
|
|
187
|
+
* repo: `packages/spec/src/ui/dashboard.zod.ts`), and both of objectui's
|
|
188
|
+
* dashboard surfaces route a dataset-bound widget to `DatasetWidget`. On that —
|
|
189
|
+
* the only spec-legal — path the renderer-consumed `options` keys are exactly
|
|
190
|
+
* the five the spec DECLARES:
|
|
191
|
+
*
|
|
192
|
+
* dateGranularity, sortBy, sortOrder, limit (query-affecting, framework#3588)
|
|
193
|
+
* stageOrder (funnel/pyramid stage order)
|
|
194
|
+
*
|
|
195
|
+
* plus ONE undeclared key with a real read site:
|
|
196
|
+
*
|
|
197
|
+
* description — the metric-card sub-caption channel. `translateDashboard`
|
|
198
|
+
* OVERLAYS the `widgets.{id}.subCaption` translation onto this key, and that
|
|
199
|
+
* pipeline lives IN THIS REPO: `packages/spec/src/system/i18n-resolver.ts`
|
|
200
|
+
* documents `WidgetLike.options` as "the renderer-extras bag …
|
|
201
|
+
* `translateDashboard` writes exactly one key into it — `description`"
|
|
202
|
+
* (objectstack#5428 item 4, objectstack#7862). Warning on a key the
|
|
203
|
+
* platform's own translation pipeline writes would be a false positive on
|
|
204
|
+
* legal metadata, so it is in the accepted set even though the dataset-bound
|
|
205
|
+
* render path does not currently display it.
|
|
206
|
+
*
|
|
207
|
+
* Notably NOT consumed on the path a widget really renders through:
|
|
208
|
+
* `thresholds` and `format`. Both were widely believed to work; both draw this
|
|
209
|
+
* warning, which is the point. Their closure claims are objectui's to derive —
|
|
210
|
+
* `thresholds` by a repo-wide read-site scan there, `format` by the bounded
|
|
211
|
+
* claim that the dataset-bound path formats from the MEASURE's own metadata —
|
|
212
|
+
* and they are NOT restated here as claims about this repo, which has no
|
|
213
|
+
* renderer to make them about.
|
|
214
|
+
*
|
|
215
|
+
* ## The drift risk that lives on THIS side
|
|
216
|
+
*
|
|
217
|
+
* The spec whose `.passthrough()` this reasons about ships from this repo. So
|
|
218
|
+
* the one way this list can go stale HERE is a new DECLARED key landing in
|
|
219
|
+
* `DashboardWidgetOptionsSchema` without landing in the array below: the key
|
|
220
|
+
* would be spec-legal, renderer-consumed on the objectui side, and warned about
|
|
221
|
+
* here — a false positive on legal metadata. `@objectstack/sdui-parser` takes
|
|
222
|
+
* no dependency on `@objectstack/spec` (it is dependency-free and hoistable by
|
|
223
|
+
* design), so that cross-check is not mechanized in this copy; the census test
|
|
224
|
+
* next door pins the array and names the spec file to re-read when it moves.
|
|
225
|
+
*
|
|
226
|
+
* ## Scope — where the warning deliberately does NOT fire
|
|
227
|
+
*
|
|
228
|
+
* - Widgets WITHOUT `dataset`: the legacy inline forms (`options.data`
|
|
229
|
+
* arrays, `provider: 'object'` bags) consume a much larger, spread-shaped
|
|
230
|
+
* key set, whose true reach is each child component's prop surface. That
|
|
231
|
+
* form is spec-illegal today (`dataset` is required) and its census would be
|
|
232
|
+
* the unmaintainable one; skipping it keeps every warning this module emits
|
|
233
|
+
* a statement about the path the widget actually renders through.
|
|
234
|
+
* - Widgets in the legacy COMPONENT format (`widget.component`): `options`
|
|
235
|
+
* is not part of that contract.
|
|
236
|
+
* - Widgets carrying the spec's own escape hatch
|
|
237
|
+
* `suppressWarnings: ['unconsumed-widget-option']` — the spec models
|
|
238
|
+
* per-widget diagnostic suppression (`DashboardWidgetSchema.suppressWarnings`,
|
|
239
|
+
* "Build diagnostic rule ids suppressed on this widget"), so an author with
|
|
240
|
+
* a genuine out-of-band consumer can say so in metadata.
|
|
241
|
+
*/
|
|
242
|
+
|
|
243
|
+
/** The diagnostic `code` — also the id `suppressWarnings` suppresses. */
|
|
244
|
+
declare const UNCONSUMED_WIDGET_OPTION = "unconsumed-widget-option";
|
|
245
|
+
/**
|
|
246
|
+
* Component types that host a dashboard `widgets` array. Both resolve to the
|
|
247
|
+
* surfaces measured by the census above (`DashboardRenderer`,
|
|
248
|
+
* `DashboardGridLayout`), which share one dispatch (`widgetDispatch.ts`).
|
|
249
|
+
*/
|
|
250
|
+
declare const DASHBOARD_WIDGET_HOST_TYPES: ReadonlySet<string>;
|
|
251
|
+
/**
|
|
252
|
+
* The accepted set: every `options` key with a renderer read site on the
|
|
253
|
+
* dataset-bound path, plus the sub-caption convention key. Alphabetical; the
|
|
254
|
+
* warning message prints it verbatim. Derivation and evidence: file header.
|
|
255
|
+
*/
|
|
256
|
+
declare const CONSUMED_WIDGET_OPTION_KEYS: readonly string[];
|
|
257
|
+
/**
|
|
258
|
+
* Diagnostics for `options` keys no renderer consumes, over one dashboard-host
|
|
259
|
+
* node's `widgets` array. Pure and shallow by design: it never descends into
|
|
260
|
+
* `children` (the caller's walk owns that) and answers `[]` for every shape
|
|
261
|
+
* outside its census — see the scope notes in the file header.
|
|
262
|
+
*/
|
|
263
|
+
declare function checkDashboardWidgetOptions(node: SchemaElement): Diagnostic[];
|
|
264
|
+
|
|
114
265
|
/**
|
|
115
266
|
* ObjectUI — codegen the JSX type surface from the registry manifest (ADR-0080 §3)
|
|
116
267
|
*
|
|
@@ -136,7 +287,54 @@ declare function propsName(type: string): string;
|
|
|
136
287
|
declare function generateBlockList(manifest: Manifest): string;
|
|
137
288
|
|
|
138
289
|
/**
|
|
139
|
-
*
|
|
290
|
+
* ObjectUI — the arms of a manifest input's coarse type (objectui#3832)
|
|
291
|
+
*
|
|
292
|
+
* `ManifestInput.type` carries ONE coarse kind, or an ARRAY of kinds when the
|
|
293
|
+
* key's contract is a union. Every reader of that field needs the same two
|
|
294
|
+
* decisions made the same way — how to see the arms, and which single form to
|
|
295
|
+
* publish — so they live here once instead of at each call site. A reader that
|
|
296
|
+
* forgets is not loud: `switch (input.type)` handed an array falls through to
|
|
297
|
+
* the default branch and reports NOTHING, which looks exactly like a value that
|
|
298
|
+
* validated cleanly.
|
|
299
|
+
*/
|
|
300
|
+
|
|
301
|
+
/** The eleven coarse kinds, as a runtime set for guarding untyped input. */
|
|
302
|
+
declare const MANIFEST_INPUT_TYPES: ReadonlySet<string>;
|
|
303
|
+
/**
|
|
304
|
+
* The declared arms of an input's coarse type, always as an array.
|
|
305
|
+
*
|
|
306
|
+
* Exported (not merely internal) so a third-party manifest consumer — a
|
|
307
|
+
* designer panel, a codegen, a validator of its own — reads the arms through
|
|
308
|
+
* the same accessor this package's own gate does, rather than re-deriving the
|
|
309
|
+
* `Array.isArray` branch and getting it subtly wrong on the union form.
|
|
310
|
+
*/
|
|
311
|
+
declare function inputTypeArms(type: ManifestInput['type'] | undefined): ManifestInputType[];
|
|
312
|
+
/**
|
|
313
|
+
* Project a DECLARED type (which may come from an untyped registry config) into
|
|
314
|
+
* the canonical published form: a bare string for one arm, an array for a real
|
|
315
|
+
* union.
|
|
316
|
+
*
|
|
317
|
+
* Two deliberate asymmetries between the single and array forms:
|
|
318
|
+
*
|
|
319
|
+
* - A single unrecognized kind still becomes `'string'`. That coercion predates
|
|
320
|
+
* the union work and is kept exactly: with one arm there is no other
|
|
321
|
+
* information to fall back on, and a manifest whose `type` is off-vocabulary
|
|
322
|
+
* would make every consumer's switch silently inert.
|
|
323
|
+
* - An unrecognized arm INSIDE an array is DROPPED, not coerced. Promoting it
|
|
324
|
+
* to `'string'` would publish an arm the author never declared — a widening
|
|
325
|
+
* invented by the serializer — and the surviving arms already carry the
|
|
326
|
+
* declaration. If dropping empties the array, the single-arm fallback applies
|
|
327
|
+
* so the output is always a valid manifest.
|
|
328
|
+
*
|
|
329
|
+
* Collapsing one arm to the bare string is what keeps this a backward-compatible
|
|
330
|
+
* extension of `sdui.manifest.json`: every input declared today serializes to
|
|
331
|
+
* the byte-identical entry it does now, and arrays appear only where a union was
|
|
332
|
+
* really declared.
|
|
333
|
+
*/
|
|
334
|
+
declare function canonicalizeInputType(type: unknown): ManifestInputType | ManifestInputType[];
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* @objectstack/sdui-parser — constrained JSX-source → SDUI SchemaNode tree (ADR-0080)
|
|
140
338
|
*
|
|
141
339
|
* Isomorphic, zero React. Run server-side as the authoritative save-time gate;
|
|
142
340
|
* may also run client-side for live edit preview (re-validated on the server —
|
|
@@ -166,7 +364,14 @@ interface RegistryConfigLike {
|
|
|
166
364
|
category?: string;
|
|
167
365
|
inputs?: Array<{
|
|
168
366
|
name: string;
|
|
169
|
-
|
|
367
|
+
/**
|
|
368
|
+
* One coarse kind, or the arms of a union (objectui#3832). Typed loosely
|
|
369
|
+
* (`string`) on purpose — this interface is the STRUCTURAL boundary that
|
|
370
|
+
* keeps this package free of a dependency on the registry, so an
|
|
371
|
+
* off-vocabulary value has to be representable here and is normalized by
|
|
372
|
+
* `canonicalizeInputType` on the way in.
|
|
373
|
+
*/
|
|
374
|
+
type: string | string[];
|
|
170
375
|
required?: boolean;
|
|
171
376
|
enum?: Array<string | {
|
|
172
377
|
value: unknown;
|
|
@@ -181,4 +386,4 @@ declare function manifestFromConfigs(configs: RegistryConfigLike[], opts?: {
|
|
|
181
386
|
publicOnly?: boolean;
|
|
182
387
|
}): Manifest;
|
|
183
388
|
|
|
184
|
-
export { type CodegenOptions, type CompileResult, type Diagnostic, type Manifest, type ManifestComponent, type ManifestInput, type ManifestInputType, type ParseOptions, type ParseResult, type RegistryConfigLike, type SchemaElement, type SchemaNode, type Severity, type ValidationResult, compile, generateBlockList, generateDts, interpretBrace, manifestFromConfigs, parseJsx, propsName, validateTree };
|
|
389
|
+
export { CONSUMED_WIDGET_OPTION_KEYS, type CodegenOptions, type CompileResult, DASHBOARD_WIDGET_HOST_TYPES, type Diagnostic, MANIFEST_INPUT_TYPES, type Manifest, type ManifestComponent, type ManifestInput, type ManifestInputType, type ParseOptions, type ParseResult, type RegistryConfigLike, type SchemaElement, type SchemaNode, type Severity, UNCONSUMED_WIDGET_OPTION, type ValidationResult, canonicalizeInputType, checkDashboardWidgetOptions, compile, generateBlockList, generateDts, inputTypeArms, interpretBrace, manifestFromConfigs, parseJsx, propsName, validateTree };
|