@objectstack/lint 17.0.0-rc.5 → 17.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/CHANGELOG.md +8409 -0
- package/dist/index.cjs +2526 -681
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +453 -129
- package/dist/index.d.ts +453 -129
- package/dist/index.js +2492 -661
- package/dist/index.js.map +1 -1
- package/dist/{runtime-Cs64ShwN.d.cts → runtime-H-nDodRy.d.cts} +135 -10
- package/dist/{runtime-Cs64ShwN.d.ts → runtime-H-nDodRy.d.ts} +135 -10
- package/dist/runtime.cjs +2091 -647
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +1 -1
- package/dist/runtime.d.ts +1 -1
- package/dist/runtime.js +2050 -589
- package/dist/runtime.js.map +1 -1
- package/package.json +6 -5
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type AnyRec = Record<string, unknown>;
|
|
1
|
+
type AnyRec$1 = Record<string, unknown>;
|
|
2
2
|
/** The three commands that hold a stack to the same author-time bar. */
|
|
3
3
|
declare const AUTHORING_COMMANDS: readonly ["validate", "build", "lint"];
|
|
4
4
|
type AuthoringCommand = (typeof AUTHORING_COMMANDS)[number];
|
|
@@ -55,10 +55,8 @@ type AuthoringRuleTier = 'gating' | 'advisory';
|
|
|
55
55
|
/**
|
|
56
56
|
* Which tier of the stack a rule reads.
|
|
57
57
|
*
|
|
58
|
-
* - `normalized` — the `normalizeStackInput` output,
|
|
59
|
-
*
|
|
60
|
-
* `views: []`, `userFilters` on an object list view, a `visibleOn` alias): by
|
|
61
|
-
* the time `result.data` exists the evidence is gone.
|
|
58
|
+
* - `normalized` — the `normalizeStackInput` output, run before this package's
|
|
59
|
+
* caller had a chance to Zod-parse.
|
|
62
60
|
* - `parsed` — the post-parse stack, where defaults are filled and shapes are
|
|
63
61
|
* settled.
|
|
64
62
|
*
|
|
@@ -66,6 +64,53 @@ type AuthoringRuleTier = 'gating' | 'advisory';
|
|
|
66
64
|
* `os validate`'s verdict to give), so it runs BOTH tiers on the normalized
|
|
67
65
|
* stack. Every rule here is written to tolerate that — it is what `os lint`
|
|
68
66
|
* already did for the reference-integrity suite and the security linter.
|
|
67
|
+
*
|
|
68
|
+
* ## What `normalized` does NOT buy, measured (#6073)
|
|
69
|
+
*
|
|
70
|
+
* This tier used to be justified as "the rules that need it check keys the
|
|
71
|
+
* parse strips — a flat list view in `views: []`, `userFilters` on an object
|
|
72
|
+
* list view, a `visibleOn` alias — by the time `result.data` exists the
|
|
73
|
+
* evidence is gone". **All three of those examples were measured false**, each
|
|
74
|
+
* for its own reason, and the pins live in `authoring-rule-input-tier.test.ts`:
|
|
75
|
+
*
|
|
76
|
+
* 1. `defineStack` — the documented and universal way a TS config declares a
|
|
77
|
+
* stack — PARSES at definition time, so for such a config the value every
|
|
78
|
+
* command hands this registry is already `result.data`: parse-time defaults
|
|
79
|
+
* filled (`flow.runAs === 'user'`), unknown keys resolved. Re-normalizing it
|
|
80
|
+
* cannot resurrect anything. That half was measured in #5693 and re-measured
|
|
81
|
+
* here.
|
|
82
|
+
* 2. But nothing is lost, because since #4001 the two cited view schemas do not
|
|
83
|
+
* STRIP — they REFUSE. `ViewSchema` and `ObjectListViewSchema` are strict, so
|
|
84
|
+
* `defineStack` throws on the flat list view and on `quickFilters` /
|
|
85
|
+
* `userFilters: { element: 'tabs' }`, naming the same sites the rules name,
|
|
86
|
+
* one layer earlier and with the schema's own fix hint. Measured end to end:
|
|
87
|
+
* `os lint` and `os validate` on an example app carrying the flat view both
|
|
88
|
+
* refuse the config at LOAD, before any rule runs.
|
|
89
|
+
* 3. The `visibleOn` alias never reaches this tier on ANY input shape: the
|
|
90
|
+
* ADR-0087 D2 conversion (`view-visibleOn-to-visibleWhen`,
|
|
91
|
+
* `page-component-visibility-to-visibleWhen`) folds it into `visibleWhen`
|
|
92
|
+
* INSIDE `normalizeStackInput` — one layer before the tier, not during the
|
|
93
|
+
* parse. #6318 acted on that: the alias-KEY rule this premise justified
|
|
94
|
+
* (`visibility-alias-deprecated`) was retired, since the D2 conversion
|
|
95
|
+
* notice already covers its whole evidence surface with better wording and
|
|
96
|
+
* a stated retirement window. The alias-KEY half of premise 3 is therefore
|
|
97
|
+
* no longer merely false — there is nothing left reading it.
|
|
98
|
+
*
|
|
99
|
+
* ## What it does buy, and why the tier stays
|
|
100
|
+
*
|
|
101
|
+
* The surviving reason is the one `validate-functional-completeness.ts` states
|
|
102
|
+
* and the measurement confirms: a `normalized`-tier finding reaches the author
|
|
103
|
+
* even when an unrelated schema error elsewhere would stop the parse. On the
|
|
104
|
+
* raw (non-`defineStack`) path that is not theoretical — `os validate` stops at
|
|
105
|
+
* the schema step and reports zero rule findings, while `os lint`, which never
|
|
106
|
+
* parses, still names `view-container-shape` and
|
|
107
|
+
* `list-view-filters-in-views-mode`. Plus the plain fact that `os lint` has no
|
|
108
|
+
* parsed stack to give.
|
|
109
|
+
*
|
|
110
|
+
* So: read `normalized` as "does not REQUIRE a parsed stack", never as "is
|
|
111
|
+
* guaranteed to see pre-parse evidence". A new rule whose only evidence is a key
|
|
112
|
+
* a strict schema rejects is a rule that will never fire — put the diagnostic in
|
|
113
|
+
* the schema instead, where #4001 already puts it.
|
|
69
114
|
*/
|
|
70
115
|
type AuthoringRuleInputTier = 'normalized' | 'parsed';
|
|
71
116
|
/** Per-run inputs a rule may need beyond the stack itself. */
|
|
@@ -107,7 +152,7 @@ interface AuthoringRule {
|
|
|
107
152
|
runtimeTypes?: readonly string[];
|
|
108
153
|
/** REQUIRED when `surfaces` omits `'runtime-publish'`: why the runtime gate does not run it. */
|
|
109
154
|
surfaceReason?: string;
|
|
110
|
-
run: (stack: AnyRec, ctx: AuthoringRuleContext) => readonly AuthoringFinding[];
|
|
155
|
+
run: (stack: AnyRec$1, ctx: AuthoringRuleContext) => readonly AuthoringFinding[];
|
|
111
156
|
}
|
|
112
157
|
/**
|
|
113
158
|
* `ExprIssue` is the one rule finding that carries no rule id of its own — it
|
|
@@ -123,12 +168,12 @@ declare const AUTHORING_RULES: readonly AuthoringRule[];
|
|
|
123
168
|
/** The stack tiers a command has in hand when it runs the registry. */
|
|
124
169
|
interface AuthoringRuleRun extends AuthoringRuleContext {
|
|
125
170
|
/** `normalizeStackInput` output — pre-Zod-parse. Always required. */
|
|
126
|
-
normalized: AnyRec;
|
|
171
|
+
normalized: AnyRec$1;
|
|
127
172
|
/**
|
|
128
173
|
* Post-Zod-parse stack. Omitted by `os lint`, which does not parse; `parsed`
|
|
129
174
|
* rules then read `normalized` (see `AuthoringRuleInputTier`).
|
|
130
175
|
*/
|
|
131
|
-
parsed?: AnyRec;
|
|
176
|
+
parsed?: AnyRec$1;
|
|
132
177
|
}
|
|
133
178
|
/** The rules `command` runs, in registry order. */
|
|
134
179
|
declare function authoringRulesFor(command: AuthoringCommand): readonly AuthoringRule[];
|
|
@@ -198,7 +243,20 @@ declare function splitBySeverity(findings: readonly AuthoringFinding[]): {
|
|
|
198
243
|
* a draft autosave). That is the correct place to spend it.
|
|
199
244
|
*/
|
|
200
245
|
|
|
201
|
-
|
|
246
|
+
type AnyRec = Record<string, unknown>;
|
|
247
|
+
/**
|
|
248
|
+
* Everything the gate needs from the host runtime to build a snapshot.
|
|
249
|
+
*
|
|
250
|
+
* [#8309] Each key here doubles as the stack key the collection occupies in
|
|
251
|
+
* the per-write snapshot — see {@link CONTEXT_STACK_KEYS}, which is derived
|
|
252
|
+
* from this shape and keeps the two from drifting. The set is deliberately
|
|
253
|
+
* BOUNDED to what the runtime-wired rules actually read (measured, not
|
|
254
|
+
* projected): the three cross-collection security rules compare
|
|
255
|
+
* objects × permissions × books, and nothing on the runtime surface reads
|
|
256
|
+
* `positions` / `apps` — so those are NOT carried. Widening the snapshot is a
|
|
257
|
+
* one-key edit here plus a `CONTEXT_STACK_KEYS` entry, made when a rule that
|
|
258
|
+
* reads the collection actually crosses the wall, never in advance.
|
|
259
|
+
*/
|
|
202
260
|
interface RuntimeStackContext {
|
|
203
261
|
/**
|
|
204
262
|
* The live object declarations (registry + tenant overlay), as authored.
|
|
@@ -209,6 +267,27 @@ interface RuntimeStackContext {
|
|
|
209
267
|
* package's config file and has to hedge.
|
|
210
268
|
*/
|
|
211
269
|
objects?: readonly unknown[];
|
|
270
|
+
/**
|
|
271
|
+
* The live permission-set declarations (stack key `permissions`).
|
|
272
|
+
*
|
|
273
|
+
* [#8309] The collection the three cross-collection security rules compare
|
|
274
|
+
* against. Without it a per-write snapshot holds exactly ONE permission set
|
|
275
|
+
* (the written item), so `security-master-detail-ungranted` reads every
|
|
276
|
+
* detail object the tenant's OTHER sets grant as ungranted — measured as 38
|
|
277
|
+
* phantom findings per-write vs 4 whole-stack over the shipped corpus
|
|
278
|
+
* (PR #7886). `security-book-audience-unknown-set` needs the set NAMES to
|
|
279
|
+
* resolve a book's `audience.permissionSet` for the same reason.
|
|
280
|
+
*/
|
|
281
|
+
permissions?: readonly unknown[];
|
|
282
|
+
/**
|
|
283
|
+
* The live documentation-book declarations (stack key `books`).
|
|
284
|
+
*
|
|
285
|
+
* [#8309] Carried so a `book` write is judged with its siblings present and
|
|
286
|
+
* replace-not-erase semantics apply to it (an updated book must not read as
|
|
287
|
+
* a second book of the same name), and so book-derived findings cancel in
|
|
288
|
+
* the differential for every other write type.
|
|
289
|
+
*/
|
|
290
|
+
books?: readonly unknown[];
|
|
212
291
|
}
|
|
213
292
|
/** One rule's verdict at the runtime surface, carrying which rule produced it. */
|
|
214
293
|
interface RuntimeGateResult {
|
|
@@ -230,6 +309,52 @@ declare function runtimeAuthoringRulesFor(type: string): readonly AuthoringRule[
|
|
|
230
309
|
declare function runtimeGatedTypes(): string[];
|
|
231
310
|
/** The stack key a metadata type occupies in a stack view, or null when unmapped. */
|
|
232
311
|
declare function stackKeyForType(type: string): string | null;
|
|
312
|
+
/**
|
|
313
|
+
* The baseline/candidate stack pair the gate judges one write against, or
|
|
314
|
+
* `null` when no snapshot can be built (unmapped type, non-object body).
|
|
315
|
+
*
|
|
316
|
+
* Exported (#8309) so the tests that measure per-write vs whole-stack
|
|
317
|
+
* agreement exercise the REAL construction instead of a hand-kept mirror —
|
|
318
|
+
* `validate-security-posture.runtime-surface.test.ts` used to mirror this
|
|
319
|
+
* logic in a local `wouldGateAdd`, which is exactly the drift surface a
|
|
320
|
+
* snapshot change here would have missed.
|
|
321
|
+
*
|
|
322
|
+
* Shape:
|
|
323
|
+
* - The baseline carries every context collection ({@link CONTEXT_STACK_KEYS})
|
|
324
|
+
* WITHOUT the written item. Anything found there is somebody else's
|
|
325
|
+
* pre-existing condition and is not this write's to answer for (#4463 D4 —
|
|
326
|
+
* the gate blocks new writes, never stored rows). Carrying the sibling
|
|
327
|
+
* collections in BOTH passes is what makes their findings cancel in the
|
|
328
|
+
* diff — and what gives the cross-collection rules the sibling collection
|
|
329
|
+
* they compare against, so the per-write verdict agrees with the
|
|
330
|
+
* whole-stack one instead of inventing findings (the 38-vs-4 measurement,
|
|
331
|
+
* PR #7886).
|
|
332
|
+
* - The candidate is the same context with this write's item added. When the
|
|
333
|
+
* written type IS one of the context collections (an `object`, `permission`
|
|
334
|
+
* or `book` write), the item REPLACES its stored self rather than appearing
|
|
335
|
+
* beside it — otherwise an update reads as a duplicate name, and for
|
|
336
|
+
* `objects` every lookup in the tenant's model would read as dangling. For
|
|
337
|
+
* any other type the item is the sole member of its own collection, so
|
|
338
|
+
* index-0 paths in the findings are unambiguously this write.
|
|
339
|
+
*
|
|
340
|
+
* Cost (the #4463 D2 question, measured rather than assumed): built per
|
|
341
|
+
* write, never cached. The construction is one filter + one spread over the
|
|
342
|
+
* written type's collection; the sibling collections are passed by reference.
|
|
343
|
+
* Over the shipped corpus (30 objects, 10 permission sets, 1 book) that is
|
|
344
|
+
* microseconds on a PUBLISH (never a draft autosave, D1) — a cache would buy
|
|
345
|
+
* nothing and would need cross-org invalidation the gate has no seam for.
|
|
346
|
+
*/
|
|
347
|
+
declare function buildRuntimeWriteSnapshots(args: {
|
|
348
|
+
/** Singular metadata type of the item being written. */
|
|
349
|
+
type: string;
|
|
350
|
+
/** The item body as it will be persisted. */
|
|
351
|
+
item: unknown;
|
|
352
|
+
/** Live resolution context from the host runtime. */
|
|
353
|
+
context?: RuntimeStackContext;
|
|
354
|
+
}): {
|
|
355
|
+
baseline: AnyRec;
|
|
356
|
+
candidate: AnyRec;
|
|
357
|
+
} | null;
|
|
233
358
|
/**
|
|
234
359
|
* Judge one about-to-be-published metadata item against the shared registry.
|
|
235
360
|
*
|
|
@@ -251,4 +376,4 @@ declare function runRuntimeAuthoringRules(args: {
|
|
|
251
376
|
sduiManifest?: unknown;
|
|
252
377
|
}): RuntimeGateResult;
|
|
253
378
|
|
|
254
|
-
export { AUTHORING_COMMANDS as A, EXPRESSION_INVALID as E, type RuntimeGateResult as R, AUTHORING_RULES as a, AUTHORING_SURFACES as b, type AuthoringCommand as c, type AuthoringFinding as d, type AuthoringRule as e, type AuthoringRuleContext as f, type AuthoringRuleInputTier as g, type AuthoringRuleRun as h, type AuthoringRuleTier as i, type AuthoringSeverity as j, type AuthoringSurface as k, type RuntimeStackContext as l, authoringRulesFor as m,
|
|
379
|
+
export { AUTHORING_COMMANDS as A, EXPRESSION_INVALID as E, type RuntimeGateResult as R, AUTHORING_RULES as a, AUTHORING_SURFACES as b, type AuthoringCommand as c, type AuthoringFinding as d, type AuthoringRule as e, type AuthoringRuleContext as f, type AuthoringRuleInputTier as g, type AuthoringRuleRun as h, type AuthoringRuleTier as i, type AuthoringSeverity as j, type AuthoringSurface as k, type RuntimeStackContext as l, authoringRulesFor as m, buildRuntimeWriteSnapshots as n, runRuntimeAuthoringRules as o, runtimeAuthoringRulesFor as p, runtimeGatedTypes as q, runAuthoringRules as r, splitBySeverity as s, stackKeyForType as t };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
type AnyRec = Record<string, unknown>;
|
|
1
|
+
type AnyRec$1 = Record<string, unknown>;
|
|
2
2
|
/** The three commands that hold a stack to the same author-time bar. */
|
|
3
3
|
declare const AUTHORING_COMMANDS: readonly ["validate", "build", "lint"];
|
|
4
4
|
type AuthoringCommand = (typeof AUTHORING_COMMANDS)[number];
|
|
@@ -55,10 +55,8 @@ type AuthoringRuleTier = 'gating' | 'advisory';
|
|
|
55
55
|
/**
|
|
56
56
|
* Which tier of the stack a rule reads.
|
|
57
57
|
*
|
|
58
|
-
* - `normalized` — the `normalizeStackInput` output,
|
|
59
|
-
*
|
|
60
|
-
* `views: []`, `userFilters` on an object list view, a `visibleOn` alias): by
|
|
61
|
-
* the time `result.data` exists the evidence is gone.
|
|
58
|
+
* - `normalized` — the `normalizeStackInput` output, run before this package's
|
|
59
|
+
* caller had a chance to Zod-parse.
|
|
62
60
|
* - `parsed` — the post-parse stack, where defaults are filled and shapes are
|
|
63
61
|
* settled.
|
|
64
62
|
*
|
|
@@ -66,6 +64,53 @@ type AuthoringRuleTier = 'gating' | 'advisory';
|
|
|
66
64
|
* `os validate`'s verdict to give), so it runs BOTH tiers on the normalized
|
|
67
65
|
* stack. Every rule here is written to tolerate that — it is what `os lint`
|
|
68
66
|
* already did for the reference-integrity suite and the security linter.
|
|
67
|
+
*
|
|
68
|
+
* ## What `normalized` does NOT buy, measured (#6073)
|
|
69
|
+
*
|
|
70
|
+
* This tier used to be justified as "the rules that need it check keys the
|
|
71
|
+
* parse strips — a flat list view in `views: []`, `userFilters` on an object
|
|
72
|
+
* list view, a `visibleOn` alias — by the time `result.data` exists the
|
|
73
|
+
* evidence is gone". **All three of those examples were measured false**, each
|
|
74
|
+
* for its own reason, and the pins live in `authoring-rule-input-tier.test.ts`:
|
|
75
|
+
*
|
|
76
|
+
* 1. `defineStack` — the documented and universal way a TS config declares a
|
|
77
|
+
* stack — PARSES at definition time, so for such a config the value every
|
|
78
|
+
* command hands this registry is already `result.data`: parse-time defaults
|
|
79
|
+
* filled (`flow.runAs === 'user'`), unknown keys resolved. Re-normalizing it
|
|
80
|
+
* cannot resurrect anything. That half was measured in #5693 and re-measured
|
|
81
|
+
* here.
|
|
82
|
+
* 2. But nothing is lost, because since #4001 the two cited view schemas do not
|
|
83
|
+
* STRIP — they REFUSE. `ViewSchema` and `ObjectListViewSchema` are strict, so
|
|
84
|
+
* `defineStack` throws on the flat list view and on `quickFilters` /
|
|
85
|
+
* `userFilters: { element: 'tabs' }`, naming the same sites the rules name,
|
|
86
|
+
* one layer earlier and with the schema's own fix hint. Measured end to end:
|
|
87
|
+
* `os lint` and `os validate` on an example app carrying the flat view both
|
|
88
|
+
* refuse the config at LOAD, before any rule runs.
|
|
89
|
+
* 3. The `visibleOn` alias never reaches this tier on ANY input shape: the
|
|
90
|
+
* ADR-0087 D2 conversion (`view-visibleOn-to-visibleWhen`,
|
|
91
|
+
* `page-component-visibility-to-visibleWhen`) folds it into `visibleWhen`
|
|
92
|
+
* INSIDE `normalizeStackInput` — one layer before the tier, not during the
|
|
93
|
+
* parse. #6318 acted on that: the alias-KEY rule this premise justified
|
|
94
|
+
* (`visibility-alias-deprecated`) was retired, since the D2 conversion
|
|
95
|
+
* notice already covers its whole evidence surface with better wording and
|
|
96
|
+
* a stated retirement window. The alias-KEY half of premise 3 is therefore
|
|
97
|
+
* no longer merely false — there is nothing left reading it.
|
|
98
|
+
*
|
|
99
|
+
* ## What it does buy, and why the tier stays
|
|
100
|
+
*
|
|
101
|
+
* The surviving reason is the one `validate-functional-completeness.ts` states
|
|
102
|
+
* and the measurement confirms: a `normalized`-tier finding reaches the author
|
|
103
|
+
* even when an unrelated schema error elsewhere would stop the parse. On the
|
|
104
|
+
* raw (non-`defineStack`) path that is not theoretical — `os validate` stops at
|
|
105
|
+
* the schema step and reports zero rule findings, while `os lint`, which never
|
|
106
|
+
* parses, still names `view-container-shape` and
|
|
107
|
+
* `list-view-filters-in-views-mode`. Plus the plain fact that `os lint` has no
|
|
108
|
+
* parsed stack to give.
|
|
109
|
+
*
|
|
110
|
+
* So: read `normalized` as "does not REQUIRE a parsed stack", never as "is
|
|
111
|
+
* guaranteed to see pre-parse evidence". A new rule whose only evidence is a key
|
|
112
|
+
* a strict schema rejects is a rule that will never fire — put the diagnostic in
|
|
113
|
+
* the schema instead, where #4001 already puts it.
|
|
69
114
|
*/
|
|
70
115
|
type AuthoringRuleInputTier = 'normalized' | 'parsed';
|
|
71
116
|
/** Per-run inputs a rule may need beyond the stack itself. */
|
|
@@ -107,7 +152,7 @@ interface AuthoringRule {
|
|
|
107
152
|
runtimeTypes?: readonly string[];
|
|
108
153
|
/** REQUIRED when `surfaces` omits `'runtime-publish'`: why the runtime gate does not run it. */
|
|
109
154
|
surfaceReason?: string;
|
|
110
|
-
run: (stack: AnyRec, ctx: AuthoringRuleContext) => readonly AuthoringFinding[];
|
|
155
|
+
run: (stack: AnyRec$1, ctx: AuthoringRuleContext) => readonly AuthoringFinding[];
|
|
111
156
|
}
|
|
112
157
|
/**
|
|
113
158
|
* `ExprIssue` is the one rule finding that carries no rule id of its own — it
|
|
@@ -123,12 +168,12 @@ declare const AUTHORING_RULES: readonly AuthoringRule[];
|
|
|
123
168
|
/** The stack tiers a command has in hand when it runs the registry. */
|
|
124
169
|
interface AuthoringRuleRun extends AuthoringRuleContext {
|
|
125
170
|
/** `normalizeStackInput` output — pre-Zod-parse. Always required. */
|
|
126
|
-
normalized: AnyRec;
|
|
171
|
+
normalized: AnyRec$1;
|
|
127
172
|
/**
|
|
128
173
|
* Post-Zod-parse stack. Omitted by `os lint`, which does not parse; `parsed`
|
|
129
174
|
* rules then read `normalized` (see `AuthoringRuleInputTier`).
|
|
130
175
|
*/
|
|
131
|
-
parsed?: AnyRec;
|
|
176
|
+
parsed?: AnyRec$1;
|
|
132
177
|
}
|
|
133
178
|
/** The rules `command` runs, in registry order. */
|
|
134
179
|
declare function authoringRulesFor(command: AuthoringCommand): readonly AuthoringRule[];
|
|
@@ -198,7 +243,20 @@ declare function splitBySeverity(findings: readonly AuthoringFinding[]): {
|
|
|
198
243
|
* a draft autosave). That is the correct place to spend it.
|
|
199
244
|
*/
|
|
200
245
|
|
|
201
|
-
|
|
246
|
+
type AnyRec = Record<string, unknown>;
|
|
247
|
+
/**
|
|
248
|
+
* Everything the gate needs from the host runtime to build a snapshot.
|
|
249
|
+
*
|
|
250
|
+
* [#8309] Each key here doubles as the stack key the collection occupies in
|
|
251
|
+
* the per-write snapshot — see {@link CONTEXT_STACK_KEYS}, which is derived
|
|
252
|
+
* from this shape and keeps the two from drifting. The set is deliberately
|
|
253
|
+
* BOUNDED to what the runtime-wired rules actually read (measured, not
|
|
254
|
+
* projected): the three cross-collection security rules compare
|
|
255
|
+
* objects × permissions × books, and nothing on the runtime surface reads
|
|
256
|
+
* `positions` / `apps` — so those are NOT carried. Widening the snapshot is a
|
|
257
|
+
* one-key edit here plus a `CONTEXT_STACK_KEYS` entry, made when a rule that
|
|
258
|
+
* reads the collection actually crosses the wall, never in advance.
|
|
259
|
+
*/
|
|
202
260
|
interface RuntimeStackContext {
|
|
203
261
|
/**
|
|
204
262
|
* The live object declarations (registry + tenant overlay), as authored.
|
|
@@ -209,6 +267,27 @@ interface RuntimeStackContext {
|
|
|
209
267
|
* package's config file and has to hedge.
|
|
210
268
|
*/
|
|
211
269
|
objects?: readonly unknown[];
|
|
270
|
+
/**
|
|
271
|
+
* The live permission-set declarations (stack key `permissions`).
|
|
272
|
+
*
|
|
273
|
+
* [#8309] The collection the three cross-collection security rules compare
|
|
274
|
+
* against. Without it a per-write snapshot holds exactly ONE permission set
|
|
275
|
+
* (the written item), so `security-master-detail-ungranted` reads every
|
|
276
|
+
* detail object the tenant's OTHER sets grant as ungranted — measured as 38
|
|
277
|
+
* phantom findings per-write vs 4 whole-stack over the shipped corpus
|
|
278
|
+
* (PR #7886). `security-book-audience-unknown-set` needs the set NAMES to
|
|
279
|
+
* resolve a book's `audience.permissionSet` for the same reason.
|
|
280
|
+
*/
|
|
281
|
+
permissions?: readonly unknown[];
|
|
282
|
+
/**
|
|
283
|
+
* The live documentation-book declarations (stack key `books`).
|
|
284
|
+
*
|
|
285
|
+
* [#8309] Carried so a `book` write is judged with its siblings present and
|
|
286
|
+
* replace-not-erase semantics apply to it (an updated book must not read as
|
|
287
|
+
* a second book of the same name), and so book-derived findings cancel in
|
|
288
|
+
* the differential for every other write type.
|
|
289
|
+
*/
|
|
290
|
+
books?: readonly unknown[];
|
|
212
291
|
}
|
|
213
292
|
/** One rule's verdict at the runtime surface, carrying which rule produced it. */
|
|
214
293
|
interface RuntimeGateResult {
|
|
@@ -230,6 +309,52 @@ declare function runtimeAuthoringRulesFor(type: string): readonly AuthoringRule[
|
|
|
230
309
|
declare function runtimeGatedTypes(): string[];
|
|
231
310
|
/** The stack key a metadata type occupies in a stack view, or null when unmapped. */
|
|
232
311
|
declare function stackKeyForType(type: string): string | null;
|
|
312
|
+
/**
|
|
313
|
+
* The baseline/candidate stack pair the gate judges one write against, or
|
|
314
|
+
* `null` when no snapshot can be built (unmapped type, non-object body).
|
|
315
|
+
*
|
|
316
|
+
* Exported (#8309) so the tests that measure per-write vs whole-stack
|
|
317
|
+
* agreement exercise the REAL construction instead of a hand-kept mirror —
|
|
318
|
+
* `validate-security-posture.runtime-surface.test.ts` used to mirror this
|
|
319
|
+
* logic in a local `wouldGateAdd`, which is exactly the drift surface a
|
|
320
|
+
* snapshot change here would have missed.
|
|
321
|
+
*
|
|
322
|
+
* Shape:
|
|
323
|
+
* - The baseline carries every context collection ({@link CONTEXT_STACK_KEYS})
|
|
324
|
+
* WITHOUT the written item. Anything found there is somebody else's
|
|
325
|
+
* pre-existing condition and is not this write's to answer for (#4463 D4 —
|
|
326
|
+
* the gate blocks new writes, never stored rows). Carrying the sibling
|
|
327
|
+
* collections in BOTH passes is what makes their findings cancel in the
|
|
328
|
+
* diff — and what gives the cross-collection rules the sibling collection
|
|
329
|
+
* they compare against, so the per-write verdict agrees with the
|
|
330
|
+
* whole-stack one instead of inventing findings (the 38-vs-4 measurement,
|
|
331
|
+
* PR #7886).
|
|
332
|
+
* - The candidate is the same context with this write's item added. When the
|
|
333
|
+
* written type IS one of the context collections (an `object`, `permission`
|
|
334
|
+
* or `book` write), the item REPLACES its stored self rather than appearing
|
|
335
|
+
* beside it — otherwise an update reads as a duplicate name, and for
|
|
336
|
+
* `objects` every lookup in the tenant's model would read as dangling. For
|
|
337
|
+
* any other type the item is the sole member of its own collection, so
|
|
338
|
+
* index-0 paths in the findings are unambiguously this write.
|
|
339
|
+
*
|
|
340
|
+
* Cost (the #4463 D2 question, measured rather than assumed): built per
|
|
341
|
+
* write, never cached. The construction is one filter + one spread over the
|
|
342
|
+
* written type's collection; the sibling collections are passed by reference.
|
|
343
|
+
* Over the shipped corpus (30 objects, 10 permission sets, 1 book) that is
|
|
344
|
+
* microseconds on a PUBLISH (never a draft autosave, D1) — a cache would buy
|
|
345
|
+
* nothing and would need cross-org invalidation the gate has no seam for.
|
|
346
|
+
*/
|
|
347
|
+
declare function buildRuntimeWriteSnapshots(args: {
|
|
348
|
+
/** Singular metadata type of the item being written. */
|
|
349
|
+
type: string;
|
|
350
|
+
/** The item body as it will be persisted. */
|
|
351
|
+
item: unknown;
|
|
352
|
+
/** Live resolution context from the host runtime. */
|
|
353
|
+
context?: RuntimeStackContext;
|
|
354
|
+
}): {
|
|
355
|
+
baseline: AnyRec;
|
|
356
|
+
candidate: AnyRec;
|
|
357
|
+
} | null;
|
|
233
358
|
/**
|
|
234
359
|
* Judge one about-to-be-published metadata item against the shared registry.
|
|
235
360
|
*
|
|
@@ -251,4 +376,4 @@ declare function runRuntimeAuthoringRules(args: {
|
|
|
251
376
|
sduiManifest?: unknown;
|
|
252
377
|
}): RuntimeGateResult;
|
|
253
378
|
|
|
254
|
-
export { AUTHORING_COMMANDS as A, EXPRESSION_INVALID as E, type RuntimeGateResult as R, AUTHORING_RULES as a, AUTHORING_SURFACES as b, type AuthoringCommand as c, type AuthoringFinding as d, type AuthoringRule as e, type AuthoringRuleContext as f, type AuthoringRuleInputTier as g, type AuthoringRuleRun as h, type AuthoringRuleTier as i, type AuthoringSeverity as j, type AuthoringSurface as k, type RuntimeStackContext as l, authoringRulesFor as m,
|
|
379
|
+
export { AUTHORING_COMMANDS as A, EXPRESSION_INVALID as E, type RuntimeGateResult as R, AUTHORING_RULES as a, AUTHORING_SURFACES as b, type AuthoringCommand as c, type AuthoringFinding as d, type AuthoringRule as e, type AuthoringRuleContext as f, type AuthoringRuleInputTier as g, type AuthoringRuleRun as h, type AuthoringRuleTier as i, type AuthoringSeverity as j, type AuthoringSurface as k, type RuntimeStackContext as l, authoringRulesFor as m, buildRuntimeWriteSnapshots as n, runRuntimeAuthoringRules as o, runtimeAuthoringRulesFor as p, runtimeGatedTypes as q, runAuthoringRules as r, splitBySeverity as s, stackKeyForType as t };
|