@objectstack/lint 17.1.0 → 17.2.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 CHANGED
@@ -1,5 +1,234 @@
1
1
  # @objectstack/lint
2
2
 
3
+ ## 17.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 78818ec: Report an unparseable source instead of scoring it CLEAN (#10653).
8
+
9
+ Four validators parsed authored source with `ts.createSourceFile` and never read
10
+ `parseDiagnostics`. That call **cannot throw**, so a source with syntax errors
11
+ came back as a tree built by error recovery, got walked like any other, and
12
+ produced no findings — a source the validator could not read, reported as a
13
+ source with nothing to report. Two of the sites carried a `try/catch` around the
14
+ parse that never once ran.
15
+
16
+ Each now reports what it could not read, as a finding the author receives rather
17
+ than as an exit — a publish-time validator is handed metadata by someone else,
18
+ so ending the process on their input is not its call. Four new advisory
19
+ (`warning`) rule ids, all additive: every finding these rules produce today they
20
+ still produce, including from a partially recovered tree.
21
+
22
+ - `react-page-source-unparseable` — `kind:'react'` page source
23
+ (`validateReactPageProps`)
24
+ - `startup-source-unparseable` — plugin source (`findStartupRegistryVerdicts`)
25
+ - `hook-body-source-unparseable` — L2 hook body (`validateHookBodyWrites`)
26
+ - `action-body-source-unparseable` — L2 action body (`validateActionBodyWrites`)
27
+
28
+ New exports: the four rule-id constants, plus `describeParseFailure`,
29
+ `PARSE_FAILURE_HINT` and the `SourceParseFailure` / `CheckedParse` /
30
+ `CheckedParseOptions` types. `ExtractedHookBodyWriteSet` gains an optional
31
+ `parseFailure`, so a consumer of the extractor can tell "wrote nothing" from
32
+ "could not be read" — the distinction that was missing.
33
+
34
+ Nothing is removed or renamed, and no source that parses gains a finding. A
35
+ stack whose authored sources all parse lints exactly as before; one carrying a
36
+ source with a syntax error gains a warning that names the file, line and column
37
+ instead of silently skipping the checks.
38
+ - def0d3e: Runtime publish-gate findings for collection-resident write types (`object` /
39
+ `permission` / `book`) now key the top-level collection entry in
40
+ `issues[].path` / `advisories[].path` by NAME —
41
+ `objects.acme_invoice.sharingModel` — instead of by the gate's private
42
+ per-write snapshot index (`objects[417].sharingModel`), which no caller could
43
+ resolve: that index numbered an in-memory array a Studio / MCP / REST receiver
44
+ has never seen. Single-member write types keep their trivially-stable
45
+ positional form (`flows[0].nodes[1]…`), and nested positions inside one named
46
+ item (`objects.acme_invoice.indexes[1]`) stay positional — they index the
47
+ author's own document. An entry with no splice-safe name falls back to the
48
+ positional spelling. The accepted metadata set is unchanged; only the spelling
49
+ of the emitted finding `path` changes, and `RuntimeAuthoringIssueSchema.path`'s
50
+ description now states the convention. CLI (`os validate` / `os lint`) output
51
+ is unchanged — there the index resolves against the author's own config file.
52
+ - e2bb237: The SORT axis now asks the #8116 provenance question about a name the blanket
53
+ `SYSTEM_FIELDS` union told it not to flag — new rule `sort-field-unprovisioned`
54
+ (#10474), the twin of `searchable-field-unprovisioned` on the identical index
55
+ (#8404).
56
+
57
+ `validate-sortable-fields` consulted the union and stopped there, so a list view
58
+ ordering by a registry-injected anchor on an ADR-0015 `external` object was
59
+ skipped in silence. The #8999 consumer census recorded that gap with the reason
60
+ that such an object never reaches the union branch at all — skip (2) was believed
61
+ to catch it. **That reason was measured wrong.** `declaredFieldTarget` returns
62
+ `null` on exactly one condition (`fields` missing, unreadable, or naming
63
+ nothing) and nothing in it tests `external`, so the shipped shape — a federated
64
+ object that declares a mapped field map, as `examples/app-showcase`'s
65
+ `showcase_ext_customer` does — is indexed like any other object and lands
66
+ squarely in the skip. The census ledger entry now carries the correction rather
67
+ than the inherited reason.
68
+
69
+ Why the authoring gate is the only door available for it: both runtime doors on
70
+ this axis judge `formula` alone (`UNMATERIALIZED_SORT_TYPES`) — the REST ingress
71
+ `assertSortFieldsExist` (#6994) and the engine's `assertOrderByIsMaterializable`
72
+ (#7095). An injected anchor is a `datetime` or `lookup`, it *is* in `gate.known`
73
+ because the registry injected it into the served schema, and it is undotted, so
74
+ it clears every verdict and reaches the driver. Measured with a real `SqlDriver`
75
+ over better-sqlite3, the object declared exactly as the showcase declares it,
76
+ against a remote `customers` table carrying `[id, name, email, region,
77
+ lifetime_value]` and none of the seven injected anchors:
78
+
79
+ ```
80
+ orderBy name asc -> [c1,c2,c3] desc -> [c3,c2,c1] (a real column: reverses)
81
+ orderBy created_at asc -> [c1,c2,c3] desc -> [c1,c2,c3] asc === desc, 3 rows, no error
82
+ orderBy owner_id asc -> [c1,c2,c3] desc -> [c1,c2,c3] asc === desc, 3 rows, no error
83
+ ```
84
+
85
+ `asc` and `desc` byte-identical while the baseline reverses is what makes it a
86
+ dropped sort rather than a coincidence — the same signature this rule already
87
+ records for `formula`, reached by a second route, except that a formula sort is
88
+ refused at both doors and this one is not. A list view ordered by an anchor with
89
+ no storage answers `200` with the rows in the driver's arbitrary order, on the
90
+ view's first fetch and every fetch after it, which `limit`/`offset` then slice
91
+ into an arbitrary page.
92
+
93
+ `warning`, never `error` and never gating (#4330's cost asymmetry, the call every
94
+ sibling makes): the remote schema is invisible to this pass, so the remote table
95
+ may genuinely carry a `created_at` of its own. Declaring that column — the first
96
+ remedy the shared hint prescribes — silences the finding, because
97
+ `unprovisionedInjectedColumnsFor` excludes an author-declared column of the same
98
+ name (#7859's security direction). The runtime publish gate sorts on severity, so
99
+ this lands as an advisory and refuses no write.
100
+
101
+ Two deliberate narrowings, both pinned:
102
+
103
+ - **Undotted names only** — the one place this axis departs from the SEARCH twin.
104
+ `resolveSearchFields` matches by exact string and drops a dotted entry like a
105
+ typo, but a dotted SORT name is refused by the ingress gate as its own verdict
106
+ (`400 INVALID_SORT`, loudly, on every fetch), so the silent degradation this
107
+ finding reports cannot happen there. Answering would give the SORT axis its own
108
+ dotted verdict, which is exactly the posture the rule shares with the FILTER
109
+ and PROJECTION axes (#4256 / #7532 / #7589) and declines to break.
110
+ - **`checkSortDeclaration`'s new anchor-index parameter is optional**, with the
111
+ same meaning `checkSearchableFieldList`'s carries: an out-of-repo caller that
112
+ never built the index keeps its pre-#10474 answers. Every in-repo caller passes
113
+ it.
114
+
115
+ Also re-ruled, with fresh eyes and on evidence rather than inheritance:
116
+ `validate-translation-references` still correctly asks nothing. It reads the
117
+ union at exactly one site (the `fields.<name>` orphan test), and the key it
118
+ decides about is derived from the *registered* metadata, into which the registry
119
+ injects the anchor on a federated object just as on a local one — so the key
120
+ resolves and the label renders. Warning there would flag a translation that
121
+ works. The blank-column consequence belongs to the surface that renders the
122
+ anchor (`validate-page-field-bindings`, #8340), not to the bundle that names it.
123
+ - adbcbfd: feat(lint): the two list-view field rules reach a standalone list view at the runtime publish gate — `view` writes are now judged by `validateSearchableFields` and `validateSortableFields` (#9313)
124
+
125
+ An `active`-state `view` save through `saveMetaItem` (Studio, REST `/meta` item
126
+ CRUD, an MCP/AI author) is now refused with the existing 422 `invalid_metadata`
127
+ envelope when its list view declares a `sort` or `searchableFields` entry the
128
+ bound object cannot honor — an unknown field name, a virtual (`formula`) sort
129
+ target with no stored column to ORDER BY, or a search narrowing the #4254
130
+ ingress gate would refuse on every toolbar search. Both rules already gated
131
+ `os validate` / `os build` / `os lint`; the runtime door — the only door a
132
+ Studio tenant or an MCP/AI author has — ran neither, and an author writing the
133
+ exact declaration these rules exist to refuse got it accepted.
134
+
135
+ Two halves, because either alone is a silent no-op: the reference-integrity
136
+ suite's registry entry gains `runtimeTypes: ['view']`, and both rules' metadata
137
+ walks gain the SELF rung — a `views[]` entry that IS a flattened standalone
138
+ list overlay (`ViewMetadataSchema`'s list-overlay member: `viewKind: 'list'`,
139
+ no nested `config`), the shape a standalone list view takes on the wire and the
140
+ shape the gate snapshots as `views: [item]`.
141
+
142
+ The suite dispatches per member on this door: a `view` snapshot reaches exactly
143
+ the two list-view field rules (`ReferenceIntegrityRule.runtimeTypes`, default
144
+ `['flow']`), never the members whose resolution universe the per-write snapshot
145
+ does not carry — `validateActionNameRefs` resolving against `stack.actions`
146
+ would otherwise refuse legitimate view writes. CLI behaviour is unchanged (the
147
+ commands run the full suite as before); `flow` snapshots keep every member.
148
+ Measured before crossing: 0 refusals and 0 advisories over 50 shipped
149
+ view-door bodies (11 containers + 39 console-shaped personalization overlays,
150
+ `sort[].id` decorations included) across four authoring lineages — a lower
151
+ bound, as every authored corpus is. Draft saves are untouched (D1), stored rows
152
+ keep being served (ADR-0087 asymmetry), and
153
+ `OS_ALLOW_UNLINTED_METADATA_WRITES=1` still degrades the refusal to a loud log.
154
+ - f1b5ad3: feat(lint): a standalone ViewItem record's nested `config.sort` / `config.searchableFields` reach the runtime publish gate (#10001)
155
+
156
+ An `active`-state `view` save through `saveMetaItem` (Studio, REST `/meta`
157
+ item CRUD, an MCP/AI author) whose body is a standalone ViewItem RECORD —
158
+ `ViewMetadataSchema`'s member 1, `{ name, object, viewKind: 'list', config }`,
159
+ the shape a Studio-saved view takes and the shape objectui's `updateView`
160
+ round-trips on every pin/reorder toggle — is now refused with the existing
161
+ 422 `invalid_metadata` envelope when its `config.sort` / `config.searchableFields`
162
+ declares a field the bound object cannot honor: an unknown name, a virtual
163
+ (`formula`) sort target with no stored column to ORDER BY, or a search
164
+ narrowing the #4254 ingress gate would refuse on every toolbar search. #9313
165
+ closed the same gap for the flattened list overlay, one union member over;
166
+ the record's declarations live one level down, inside `config`, and were
167
+ judged by neither list-view field rule — so a record write carrying
168
+ `config.sort: [{ field: '' }]` published in silence and answered
169
+ `400 INVALID_SORT` (#6994/#7095) on the view's first fetch, every load.
170
+
171
+ Walk-only, by design: #9313 already widened the reference-integrity suite
172
+ entry and exactly these two members onto `view` writes, so this change adds
173
+ the RECORD rung to both twin walks — recognised by the wire union's own
174
+ member discrimination (`viewKind: 'list'` AND a record-shaped `config`; the
175
+ flattened-overlay rung keeps its `no nested config` guard, a strict container
176
+ carries neither key, and a `form` record has no list-field surface), judged
177
+ against `listViewObject(config) ?? record.object` at path
178
+ `views[i].config.sort[…]` / `views[i].config.searchableFields[…]`. The
179
+ per-member granularity split is unchanged: no further suite member crosses
180
+ onto `view`. Measured before shipping: 0 refusals and 0 advisories over 39
181
+ record-shaped console round-trip bodies (one per shipped list surface,
182
+ `config.sort[].id` decorations and `isPinned`/`sortOrder` riding along, the
183
+ shape `saveMetaItem` really stores) across the four shipped stacks — a lower
184
+ bound, as every authored corpus is. Draft saves are untouched (D1), stored
185
+ rows keep being served (ADR-0087 asymmetry), and
186
+ `OS_ALLOW_UNLINTED_METADATA_WRITES=1` still degrades the refusal to a loud log.
187
+
188
+ ### Patch Changes
189
+
190
+ - Updated dependencies [6936d07]
191
+ - Updated dependencies [59eb04d]
192
+ - Updated dependencies [9f05b7d]
193
+ - Updated dependencies [7d2d112]
194
+ - Updated dependencies [5fa0d72]
195
+ - Updated dependencies [02b3b07]
196
+ - Updated dependencies [914c413]
197
+ - Updated dependencies [55809a0]
198
+ - Updated dependencies [52db1d1]
199
+ - Updated dependencies [5649efb]
200
+ - Updated dependencies [2306a76]
201
+ - Updated dependencies [e5ea701]
202
+ - Updated dependencies [a40dcc1]
203
+ - Updated dependencies [def0d3e]
204
+ - Updated dependencies [8d0bb79]
205
+ - Updated dependencies [5acb58d]
206
+ - Updated dependencies [2e3cf95]
207
+ - Updated dependencies [4c93387]
208
+ - Updated dependencies [a037f7c]
209
+ - Updated dependencies [3ee8ddf]
210
+ - Updated dependencies [16cef97]
211
+ - Updated dependencies [a79bd35]
212
+ - Updated dependencies [6ceaa4b]
213
+ - Updated dependencies [15ea214]
214
+ - Updated dependencies [de19489]
215
+ - Updated dependencies [c684d00]
216
+ - Updated dependencies [923c424]
217
+ - Updated dependencies [1ec36b7]
218
+ - Updated dependencies [5f2e54c]
219
+ - Updated dependencies [189373b]
220
+ - Updated dependencies [35ad101]
221
+ - Updated dependencies [ceb33a9]
222
+ - Updated dependencies [73d9795]
223
+ - Updated dependencies [8012960]
224
+ - Updated dependencies [f34f56b]
225
+ - Updated dependencies [f399618]
226
+ - Updated dependencies [75e9301]
227
+ - Updated dependencies [2810695]
228
+ - @objectstack/spec@17.2.0
229
+ - @objectstack/formula@17.2.0
230
+ - @objectstack/sdui-parser@17.2.0
231
+
3
232
  ## 17.1.0
4
233
 
5
234
  ### Minor Changes