diff --git a/.changeset/spec-pin-17-1-0-5328.md b/.changeset/spec-pin-17-1-0-5328.md new file mode 100644 index 0000000000..261be17ee2 --- /dev/null +++ b/.changeset/spec-pin-17-1-0-5328.md @@ -0,0 +1,23 @@ +--- +'@object-ui/data-objectstack': minor +'@object-ui/plugin-list': minor +'@object-ui/plugin-view': minor +'@object-ui/core': minor +'@object-ui/app-shell': minor +--- + +Pins `@objectstack/spec`, `@objectstack/client`, `@objectstack/formula` and `@objectstack/lint` to `17.1.0`, and adapts the two consumer surfaces the new build moves. + +The pin itself is a lockfile refresh — every manifest already declared `^17.0.0`, which admits `17.1.0`, so no dependency range changed. All four move together: a split resolution is what produced the dual-version spec graph that reddened `check:spec-symbols` in this repo's history. + +**A `icontains` filter now reaches the driver as a filter.** `icontains` is a canonical `VIEW_FILTER_OPERATORS` member as of `17.1.0`, so an author can declare it on a `ViewFilterRule` and the spec validates it — but `@object-ui/data-objectstack`'s alias table had no row for it, and an unmapped operator is how this adapter shipped an unfiltered query before (objectstack#3948). It is an identity row like `contains`: `icontains` is itself a member of `VALID_AST_OPERATORS`, so the spelling the author writes is the spelling the AST takes, and no case-sensitivity is translated away. Declared rather than left to the table's `?? op` fall-through, on the rule its own parity test states — the AST gate accepting a spelling is not the driver compiling it into a `WHERE` clause. + +The same operator reaches the list view's own bridge: `@object-ui/plugin-list`'s `mapOperator` gains an explicit `icontains` arm. The emitted spelling is identical to the input, but the arm is written out rather than left to the `default` passthrough — `icontains` is its own member of `VALID_AST_OPERATORS`, so a raw passthrough is accepted *today*, and depending on that coincidence is what the bridge's own parity test records as how it once stopped discriminating. + +`@object-ui/core` adds `onSuccess` to its spec key inventory, so an author writing the key `17.1.0` now declares is no longer warned that it is unknown. That is a diagnostic statement only — the four declared action surfaces still drop the key before it reaches the runner, which is tracked separately. + +**A stored view filtering case-insensitively still shows that operator when it is reopened.** `@object-ui/plugin-view`'s canonical-to-builder table is keyed by `ViewFilterOperator`, so `17.1.0` adding `icontains` failed to compile rather than letting the operator reach the FilterBuilder as a raw spelling its dropdown cannot select. It maps to the builder's `containsCaseInsensitive` — the id that authors the spec's `$icontains` — and deliberately not to `contains`, which would quietly rewrite a case-insensitive filter into a case-sensitive one the next time the view was saved. + +**The page-editor palette keeps one entry per renderer.** `17.1.0` retires `element:filter` from `PageComponentType` and adds `record:discussion`, leaving the member count at 34 either side — so the swap is invisible to any count-based reading. The stale `element:filter` exclusion is dropped, and `record:discussion` is excluded because it is the *same renderer* as the already-offered `record:chatter`, not because it is unauthorable. Nothing the palette offers changes. + +**The console eager-closure ceiling is re-baselined, by maintainer ruling.** The release is roughly 930 KB larger uncompressed and nearly all of it lands in `vendor-objectstack-*.js`, which put the closure past a ceiling that was deliberately sized to catch a 89 KiB regression — the gate refused the bump, correctly. Raising it was escalated rather than taken locally, because gate-strength policy had been ruled the maintainer's; the ruling on objectui#5531 authorised the raise. `MAX_EAGER_CLOSURE_GZIP_BYTES` and the `BASELINE` it is derived from move together in one commit, keeping headroom at 2.00% and below the 91,136-byte regression size the gate must still catch. The gate's *sensitivity* is untouched: a repeat of that regression from the new baseline still fails. No behaviour ships from this file — it is CI policy, recorded here because the version it governs is the one this changeset publishes. diff --git a/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts b/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts index dd13ad72ae..ee15edf5f1 100644 --- a/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts +++ b/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts @@ -284,6 +284,37 @@ const GA_ONLY_BLOCKS = [ 'object-metric', ]; +/** + * The five `record:*` blocks `@objectstack/spec` 17.1.0 adds to + * `ComponentPropsMap` and `17.0.0` does not carry at all (objectui#5328; + * the map goes from 37 entries to 42, and these are the five). + * + * Exactly the same shape as `GA_ONLY_BLOCKS` above, and for the same reason: + * this repo has registered all five with `inputs` for far longer than the spec + * has described them — `plugin-detail/src/index.tsx` registers `alert` (:686), + * `history` (:662) and `reference_rail` (:675), and `quick_actions` / + * `discussion` alongside them — so what moved at the pin bump is the SPEC's + * side, not this repo's. They enter `covered` the moment the installed spec + * carries them, and the reverse direction then asks each for the keys it does + * not publish. Only `record:reference_rail` had one: `entries`, exempted below. + */ +const MINOR_17_1_BLOCKS = [ + 'record:alert', + 'record:discussion', + 'record:history', + 'record:quick_actions', + 'record:reference_rail', +]; + +/** + * Does the installed `@objectstack/spec` carry the 17.1.0 record set? + * + * Same observable-fact reasoning as `specCarriesGaBlocks` below, including the + * `every` rather than `some`: the five arrived in one release, so a + * half-carried state is a broken premise rather than an in-between pin. + */ +const specCarries171Blocks = MINOR_17_1_BLOCKS.every((type) => type in ComponentPropsMap); + /** * Does the installed `@objectstack/spec` carry the GA element set? * @@ -335,6 +366,7 @@ const PINNED_EXPECTED_COVERED = [ const EXPECTED_COVERED = [ ...PINNED_EXPECTED_COVERED, ...(specCarriesGaBlocks ? GA_ONLY_BLOCKS : []), + ...(specCarries171Blocks ? MINOR_17_1_BLOCKS : []), ].sort(); /** @@ -522,14 +554,17 @@ const UNPUBLISHED_EXEMPTIONS: Record = { * regression: the mechanism is now self-clearing. A key upstream retires after * this change enters the shape as a tombstone, leaves the accepted set on * arrival, and any exemption covering it goes dangling-and-stale in the same - * run — no issue needed, no filter to remember. Two of the entries still below - * are already queued for it: objectstack `origin/main` tombstones + * run — no issue needed, no filter to remember. + * + * THAT PREDICTION HAS NOW RUN ONCE, AND IT HELD. The paragraph used to say two + * entries below were queued for it: objectstack `origin/main` tombstoned * `targetVariable` on BOTH `element:text_input` and `element:record_picker` - * (measured on `main` @ `23abe2782`; both keys are still LIVE in the installed - * 17.0.0, whose tombstone set is the same eight rc.6 carried), so the pin that - * carries those retirements will name both entries here. Deleting them is - * the fix — objectui#3834's "should we publish an intent-only key" question is - * answered upstream by then, in the negative. + * while the installed 17.0.0 still carried both as live. The 17.1.0 pin + * (objectui#5328) delivered those retirements, all three directions named the + * two entries in the same run, and deleting them was the entire fix — + * objectui#3834's "should we publish an intent-only key" question having been + * answered upstream, in the negative. The mechanism needed no maintenance to + * do that, which is the property worth keeping. * * DO NOT resolve a tombstone red by declaring the input. That publishes a key * the contract rejects by name and fails the forward direction immediately; @@ -544,19 +579,22 @@ const UNPUBLISHED_EXEMPTIONS: Record = { // exemption` demanded its deletion. It is now pinned as DECLARED, by name, // alongside #3808's four at the bottom of this file. - // ── targetVariable — the spec's own "declarative hint" (2 keys) ──────────── - // Zero read points repo-wide (`grep -rn targetVariable packages/ apps/` is - // empty), and that is by design, not drift: the spec's describe says the live - // binding resolves via the variable whose `source` equals the component id, - // which is exactly what `usePageVariableBinding(schema?.id)` does - // (`text-input.tsx:60`). So publishing it is neither a fix nor a defect — it - // is a judgement about whether to publish an intent-only key, with a concrete - // risk on the publish side (an author who writes only `targetVariable` and no - // variable `source` gets an input that writes nowhere, silently). - 'element:text_input.targetVariable': - "Spec's own declarative hint with zero read points repo-wide; the live binding is the reverse lookup in usePageVariableBinding(schema.id) (text-input.tsx:60). Whether to publish an intent-only key is an open judgement: objectui#3834.", - 'element:record_picker.targetVariable': - "Spec's own declarative hint with zero read points repo-wide; the live binding is the reverse lookup by component id, as on element:text_input. Whether to publish an intent-only key is an open judgement: objectui#3834.", + // TWO targetVariable ENTRIES DELETED HERE — objectui#5328, and they died + // exactly the way the docblock above said they would. + // + // `element:text_input.targetVariable` and `element:record_picker.targetVariable` + // were exempted as the spec's own intent-only "declarative hint" with zero read + // points repo-wide, pending objectui#3834's question of whether to publish such + // a key at all. The `@objectstack/spec` 17.1.0 pin answered it upstream, in the + // negative: both keys arrived as ADR-0087 D2 tombstones, so they left the + // accepted set and the exemptions covering them went dangling-and-stale in the + // same run — named by `every unpublished-key exemption names a key the spec + // really declares`, `carries no stale unpublished-key exemption` and `the + // tombstoned keys are recognised, not exempted`, all three at once. + // + // Deleting them is the whole fix. The tombstone judge recognises both keys now, + // which is a stronger statement than an exemption ever was: the contract itself + // rejects them by name. // FIVE GA-PENDING ENTRIES DELETED HERE — objectui#4668, and they too were // designed to die exactly this way. @@ -655,6 +693,34 @@ const UNPUBLISHED_EXEMPTIONS: Record = { '@deprecated in ObjectGridSchema ("Moved to top-level resizable"); GA describes it as the "Alternate spelling of `resizable`". Read as back-compat, deliberately not published — the canonical `resizable` IS declared. Same ruled carve-out class as the five the ruling enumerated, measured on this branch — objectui#4648 (maintainer 2026-08-16).', 'object-grid.title': '@deprecated in ObjectGridSchema ("Use label instead"); GA describes it as the "Fallback for `label` (the renderer reads `label || title`)". Read as back-compat, deliberately not published — the canonical `label` IS declared. Same ruled carve-out class as the five the ruling enumerated, measured on this branch — objectui#4648 (maintainer 2026-08-16).', + + // ── record:reference_rail.entries — a nested collection, newly JUDGED ────── + // (1 key) + // The gap is not new; being GATED is. `@objectstack/spec` 17.1.0 added + // `record:reference_rail` to `ComponentPropsMap` (37 entries to 42), so this + // file began judging a block it had never covered — the registration in + // `plugin-detail/src/index.tsx:675` has always published `hideEmpty` and only + // `hideEmpty`. Nothing about the renderer or its inputs changed on the pin + // (objectui#5328). + // + // `entries` is an ARRAY OF OBJECTS — `{objectName, relationshipField, title, + // limit, displayField}` per item — and `inputs` is a flat carrier of scalar + // fields (`type: 'string' | 'number' | 'boolean' | 'enum'`). The same + // "unpublishable in a flat carrier" reading `page:tabs.type` carried, except + // here the carrier cannot express the SHAPE rather than colliding on a name. + // + // DO NOT resolve this by declaring a scalar input for it: a string field + // standing in for a list of related-object bindings recommends a write the + // renderer cannot honour, which is this gate's own failure mode one layer in + // (the `page:tabs.alwaysShowStrip` note above). + // + // Also blocked on a real contract question, so it is not merely unbuilt: the + // item shape is `ReferenceRailEntrySchema`, whose `$strict` object REFUSES the + // `icon` key this repo's own local `ReferenceRailEntry` declares and the + // renderer reads. Publishing an entries editor means first deciding whether + // `icon` survives — objectui#5494. + 'record:reference_rail.entries': + 'An array of {objectName, relationshipField, title, limit, displayField} objects; `inputs` is a flat scalar carrier and cannot express it. Newly judged rather than newly missing — @objectstack/spec 17.1.0 added record:reference_rail to ComponentPropsMap, and the registration (plugin-detail/src/index.tsx:675) has always published only `hideEmpty`. An entries editor also needs the `icon` divergence settled first: objectui#5494.', }; /** diff --git a/packages/app-shell/src/views/metadata-admin/previews/block-types.ts b/packages/app-shell/src/views/metadata-admin/previews/block-types.ts index 56ce7fc355..340ea56750 100644 --- a/packages/app-shell/src/views/metadata-admin/previews/block-types.ts +++ b/packages/app-shell/src/views/metadata-admin/previews/block-types.ts @@ -153,10 +153,24 @@ export const PALETTE_EXCLUSIONS: Record = { 'user:profile': 'shell singleton — lives in the app shell header', // No renderer, by decision. 'ai:chat_window': 'no inline renderer — the floating chat overlay (plugin-chatbot) is canonical', - 'element:filter': 'no renderer — list surfaces own filtering (userFilters / filter builder)', 'element:form': 'no renderer — use the object-bound `object-form` block', 'element:record_picker': 'no renderer — record picking is a field widget, not a page block', 'element:text_input': 'no renderer — bare inputs belong to a form, not a page block', + // Renders fine — excluded to keep ONE palette entry per renderer, not because + // it is unauthorable. `record:chatter` and `record:discussion` are the same + // renderer under two names (plugin-detail/src/index.tsx:589), and the palette + // already offers it above as 'Chatter feed'. Entered when `@objectstack/spec` + // 17.1.0 added `record:discussion` to `PageComponentType` (objectui#5328); + // objectui itself has emitted the type all along + // (synth/buildDefaultPageSchema.ts:731), so nothing new became authorable — + // only the spec's enum caught up. + // + // ⚠️ The pair is offered under the LEGACY name: `public-blocks.ts` records + // `record:chatter` as "`record:discussion` under a Salesforce-familiar name", + // i.e. this exclusion hides the canonical spelling and offers the alias. + // Flipping which one the palette offers changes what authors drag, so it is a + // decision rather than a pin-bump edit — filed as objectui#5495. + 'record:discussion': 'same renderer as the offered `record:chatter` — one palette entry per renderer', }; export const CATEGORY_LABEL_EN: Record = { diff --git a/packages/core/src/actions/actionKeys.ts b/packages/core/src/actions/actionKeys.ts index ad7906dc0a..6da7334c13 100644 --- a/packages/core/src/actions/actionKeys.ts +++ b/packages/core/src/actions/actionKeys.ts @@ -238,6 +238,14 @@ export const SPEC_ACTION_KEYS = [ 'name', 'newTabUrl', 'objectName', + // Declared by `ActionSchema` as of @objectstack/spec 17.1.0 (objectui#5328). + // Listing it here is a DIAGNOSTIC statement only — `KNOWN_ACTION_KEYS` feeds + // `warnOnUnknownActionKeys`, so without this row an author writing the key the + // spec now accepts would be warned it is unknown. It says nothing about the + // key being forwarded: the four declared action surfaces still drop it before + // the runner, tracked as KNOWN_GAPS in check-action-forward-parity.mjs and + // filed as objectui#5493. + 'onSuccess', 'openIn', 'opensInNewTab', 'order', diff --git a/packages/data-objectstack/src/index.ts b/packages/data-objectstack/src/index.ts index c7c37539f9..ff036cc228 100644 --- a/packages/data-objectstack/src/index.ts +++ b/packages/data-objectstack/src/index.ts @@ -99,6 +99,15 @@ export const FILTER_OPERATOR_ALIASES: Record = { notin: 'nin', nin: 'nin', contains: 'contains', + // Case-insensitive contains. A canonical `VIEW_FILTER_OPERATORS` member that + // arrived with `@objectstack/spec` 17.1.0 (objectui#5328), and an IDENTITY row + // like `contains` above: `icontains` is itself a member of + // `VALID_AST_OPERATORS`, so the spelling the author writes is the spelling the + // AST takes — nothing is translated and no case-sensitivity is lost. + // Declared rather than left to the `?? op` fall-through on the rule this + // table's test states: the AST gate accepting a spelling is not the driver + // compiling it into a WHERE clause. + icontains: 'icontains', not_contains: 'notcontains', notcontains: 'notcontains', starts_with: 'startswith', diff --git a/packages/plugin-list/src/ListView.tsx b/packages/plugin-list/src/ListView.tsx index ea7c42e0ed..a179a4e792 100644 --- a/packages/plugin-list/src/ListView.tsx +++ b/packages/plugin-list/src/ListView.tsx @@ -190,6 +190,13 @@ export function mapOperator(op: string) { case 'equals': case 'eq': return '='; case 'notequals': case 'ne': case 'neq': return '!='; case 'contains': return 'contains'; + // Canonical in `VIEW_FILTER_OPERATORS` as of @objectstack/spec 17.1.0 + // (objectui#5328), and an explicit arm rather than a `default` fall-through + // even though the emitted spelling is identical: `icontains` is its own + // member of `VALID_AST_OPERATORS`, so the raw passthrough happens to be + // accepted today, and relying on that is the exact slack this file's own + // header records as how it stopped discriminating in #3641. + case 'icontains': return 'icontains'; case 'notcontains': return 'notcontains'; case 'startswith': return 'startswith'; case 'endswith': return 'endswith'; diff --git a/packages/plugin-list/src/__tests__/UserFilters.tabPresetOperators.test.tsx b/packages/plugin-list/src/__tests__/UserFilters.tabPresetOperators.test.tsx index 69438d8f1c..85ee6d7e08 100644 --- a/packages/plugin-list/src/__tests__/UserFilters.tabPresetOperators.test.tsx +++ b/packages/plugin-list/src/__tests__/UserFilters.tabPresetOperators.test.tsx @@ -122,7 +122,9 @@ describe('UserFilters tab presets — the reported defect (#3470)', () => { describe('UserFilters tab presets — lowering is structural, not translated', () => { it('reads both vocabularies from the spec', () => { // Guards every it.each below against silently passing on an empty list. - expect(VIEW_FILTER_OPERATORS.length).toBe(19); + // 19 through @objectstack/spec 17.0.0; 20 as of 17.1.0, which adds the + // canonical `icontains` (objectui#5328). + expect(VIEW_FILTER_OPERATORS.length).toBe(20); expect(VALID_AST_OPERATORS.size).toBeGreaterThan(0); }); diff --git a/packages/plugin-list/src/__tests__/filter-operator-ast-parity.test.ts b/packages/plugin-list/src/__tests__/filter-operator-ast-parity.test.ts index 40ad46fac7..53b68dedfe 100644 --- a/packages/plugin-list/src/__tests__/filter-operator-ast-parity.test.ts +++ b/packages/plugin-list/src/__tests__/filter-operator-ast-parity.test.ts @@ -81,6 +81,7 @@ const EXPECTED_AST_TARGET: Record = { equals: '=', // case 'equals' not_equals: '!=', // case 'notequals' contains: 'contains', // case 'contains' + icontains: 'icontains', // case 'icontains' not_contains: 'notcontains', // case 'notcontains' starts_with: 'startswith', // case 'startswith' ends_with: 'endswith', // case 'endswith' diff --git a/packages/plugin-view/src/config/view-config-utils.ts b/packages/plugin-view/src/config/view-config-utils.ts index 70cc6e7cb1..00958188a1 100644 --- a/packages/plugin-view/src/config/view-config-utils.ts +++ b/packages/plugin-view/src/config/view-config-utils.ts @@ -45,6 +45,14 @@ const CANONICAL_TO_BUILDER: Record = { 'equals': 'equals', 'not_equals': 'notEquals', 'contains': 'contains', + // Case-insensitive contains, canonical in `VIEW_FILTER_OPERATORS` as of + // `@objectstack/spec` 17.1.0 (objectui#5328). The builder HAS an equivalent + // — `containsCaseInsensitive`, which authors the spec's `$icontains` + // (filter-builder.tsx:160, objectui#4023) — so this is a real row and not a + // `null`: mapping it to `contains` would quietly rewrite a case-insensitive + // filter into a case-sensitive one the next time the view was saved, the + // same folding the `is_null` note below refuses. + 'icontains': 'containsCaseInsensitive', 'not_contains': 'notContains', 'starts_with': 'startsWith', 'ends_with': 'endsWith', diff --git a/packages/test-support/src/__tests__/spec-tombstones.test.ts b/packages/test-support/src/__tests__/spec-tombstones.test.ts index 4023e5e1bb..39f4fa7e8e 100644 --- a/packages/test-support/src/__tests__/spec-tombstones.test.ts +++ b/packages/test-support/src/__tests__/spec-tombstones.test.ts @@ -36,7 +36,7 @@ import { describe, it, expect } from 'vitest'; import { z } from 'zod'; -import { ComponentPropsMap } from '@objectstack/spec/ui'; +import { ComponentPropsMap, PageComponentType } from '@objectstack/spec/ui'; import { RETIRED_DESCRIPTION_PREFIX, @@ -234,11 +234,27 @@ describe('spec-tombstone judge — against the installed @objectstack/spec', () // EVERY key would satisfy the loop above while proving nothing. An // authorable key of the same schema, carrying a value it accepts, must come // back clean at its own path. - const [controlType] = found[0]; - const controlKey = authorableShapeKeys( - ComponentPropsMap[controlType as keyof typeof ComponentPropsMap], - ).find((key) => shapeMemberTypeName(ComponentPropsMap[controlType as keyof typeof ComponentPropsMap], key) === 'string'); - expect(controlKey, `${controlType} has no authorable string key to use as a control`).toBeTruthy(); + // + // The control type is SEARCHED for rather than taken as `found[0]`: a + // WHOLLY retired block (see the narrowing test below) has no authorable key + // to offer, so pinning the control to whichever type sorts first made this + // assertion depend on alphabetical order. `@objectstack/spec` 17.1.0 made + // that concrete — `element:filter` retired entirely and sorts first + // (objectui#5328). Any type carrying both a tombstone and an authorable + // string key serves the control equally well. + const control = found + .map(([type]) => type) + .map((type) => { + const schema = ComponentPropsMap[type as keyof typeof ComponentPropsMap]; + return [type, authorableShapeKeys(schema).find((key) => shapeMemberTypeName(schema, key) === 'string')] as const; + }) + .find(([, key]) => key !== undefined); + expect( + control, + 'no tombstoned block has an authorable string key to use as a control — every candidate ' + + 'is wholly retired, so the cross-check below would prove nothing', + ).toBeTruthy(); + const [controlType, controlKey] = control as readonly [string, string]; expect( issuePaths(controlType, { [controlKey as string]: 'a string' }), `${controlType}.${controlKey} is authorable and string-typed, yet the contract rejected it`, @@ -252,9 +268,26 @@ describe('spec-tombstone judge — against the installed @objectstack/spec', () authorableShapeKeys(schema).length, `${type} authorable set did not narrow below its listed set`, ).toBeLessThan(listedShapeKeys(schema).length); - // Not narrowed to nothing, either: a block whose every key read as - // retired would be a broken probe, not a retired block. - expect(authorableShapeKeys(schema).length, `${type} has no authorable key left`).toBeGreaterThan(0); + // Not narrowed to nothing — but only for a block the spec still OFFERS. + // The reasoning was "a block whose every key read as retired would be a + // broken probe, not a retired block", and `@objectstack/spec` 17.1.0 + // supplied the case that separates the two: `element:filter` left + // `PageComponentType` altogether while its props schema stayed in + // `ComponentPropsMap` with every key tombstoned (objectui#5328). That is a + // retired BLOCK, and reading zero authorable keys off it is the correct + // answer rather than a broken probe — the tombstones are how a consumer + // pinned to the old enum still gets told the block is gone. + // + // So the floor is asserted where it still discriminates: a type the enum + // continues to offer must keep at least one authorable key, because THAT + // is the shape a broken probe would produce. + const stillOffered = (PageComponentType.options as readonly string[]).includes(type); + if (stillOffered) { + expect( + authorableShapeKeys(schema).length, + `${type} is still offered by PageComponentType yet has no authorable key left`, + ).toBeGreaterThan(0); + } } }); }); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 694c746965..37d9cdc7f2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,7 +36,7 @@ importers: version: 10.0.1(eslint@10.8.1(jiti@2.7.0)) '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@playwright/test': specifier: ^1.62.1 version: 1.62.1 @@ -274,10 +274,10 @@ importers: version: link:../../packages/types '@objectstack/client': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@tailwindcss/postcss': specifier: ^4.3.3 version: 4.3.3 @@ -412,7 +412,7 @@ importers: version: link:../../packages/types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) fumadocs-core: specifier: 16.14.4 version: 16.14.4(@mdx-js/mdx@3.1.1)(@tanstack/react-router@1.170.8(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(@types/estree-jsx@1.0.5)(@types/hast@3.0.5)(@types/mdast@4.0.4)(@types/react@19.2.18)(lucide-react@1.31.0(react@19.2.8))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.62.1)(@types/node@26.2.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react-router@7.18.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react@19.2.8)(zod@4.4.3) @@ -761,13 +761,13 @@ importers: version: link:../types '@objectstack/formula': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@objectstack/lint': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@sentry/react': specifier: ^10.70.0 version: 10.70.0(react@19.2.8) @@ -861,7 +861,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) better-auth: specifier: ^1.6.28 version: 1.6.28(@opentelemetry/api@1.9.1)(better-sqlite3@12.9.0)(mongodb@7.2.0(socks@2.8.9))(next@16.3.1(@babel/core@7.29.7)(@opentelemetry/api@1.9.1)(@playwright/test@1.62.1)(@types/node@26.2.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8))(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(vitest@4.1.10) @@ -957,7 +957,7 @@ importers: devDependencies: '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@types/react': specifier: 19.2.18 version: 19.2.18 @@ -993,7 +993,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@radix-ui/react-accordion': specifier: ^1.2.20 version: 1.2.20(@types/react-dom@19.2.4(@types/react@19.2.18))(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -1177,10 +1177,10 @@ importers: version: link:../types '@objectstack/formula': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) lodash: specifier: ^4.18.1 version: 4.18.1 @@ -1239,10 +1239,10 @@ importers: version: link:../types '@objectstack/client': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) devDependencies: tsup: specifier: ^8.5.1 @@ -1276,7 +1276,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -1383,7 +1383,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) clsx: specifier: ^2.1.1 version: 2.1.1 @@ -1430,7 +1430,7 @@ importers: devDependencies: '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@types/react': specifier: 19.2.18 version: 19.2.18 @@ -1605,7 +1605,7 @@ importers: devDependencies: '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@types/react': specifier: 19.2.18 version: 19.2.18 @@ -1647,7 +1647,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@radix-ui/react-slot': specifier: ^1.3.3 version: 1.3.3(@types/react@19.2.18)(react@19.2.8) @@ -1766,7 +1766,7 @@ importers: version: link:../plugin-charts '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@types/react-grid-layout': specifier: ^2.1.0 version: 2.1.0(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -1875,7 +1875,7 @@ importers: version: link:../i18n '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) lucide-react: specifier: ^1.31.0 version: 1.31.0(react@19.2.8) @@ -1997,7 +1997,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) lucide-react: specifier: ^1.31.0 version: 1.31.0(react@19.2.8) @@ -2052,7 +2052,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) lucide-react: specifier: ^1.31.0 version: 1.31.0(react@19.2.8) @@ -2113,7 +2113,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@tanstack/react-virtual': specifier: ^3.14.9 version: 3.14.9(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -2257,7 +2257,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@types/react': specifier: 19.2.18 version: 19.2.18 @@ -2296,7 +2296,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) lucide-react: specifier: ^1.31.0 version: 1.31.0(react@19.2.8) @@ -2446,7 +2446,7 @@ importers: devDependencies: '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@types/node': specifier: ^26.2.0 version: 26.2.0 @@ -2491,7 +2491,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -2546,7 +2546,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) lucide-react: specifier: ^1.31.0 version: 1.31.0(react@19.2.8) @@ -2610,7 +2610,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -2644,7 +2644,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) react: specifier: 19.2.8 version: 19.2.8 @@ -2678,7 +2678,7 @@ importers: version: link:../types '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) react: specifier: 19.2.8 version: 19.2.8 @@ -2752,7 +2752,7 @@ importers: devDependencies: '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) '@tailwindcss/postcss': specifier: ^4.3.3 version: 4.3.3 @@ -2791,13 +2791,13 @@ importers: version: link:../react '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) packages/test-support: devDependencies: '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) typescript: specifier: ^6.0.3 version: 6.0.3 @@ -2809,7 +2809,7 @@ importers: dependencies: '@objectstack/spec': specifier: ^17.0.0 - version: 17.0.0(ai@7.0.65(zod@4.4.3)) + version: 17.1.0(ai@7.0.65(zod@4.4.3)) zod: specifier: ^4.4.3 version: 4.4.3 @@ -4084,8 +4084,8 @@ packages: react: 19.2.8 react-dom: 19.2.8 - '@mongodb-js/saslprep@1.4.13': - resolution: {integrity: sha512-E3Sv4eCYAlKYUTx8S3ioQcDUscOif+8zZ5OnW1IzJ+Tt+EO+ke8mn+Y3FX6N1H79picwbdOavVOb1jPi2EOyrg==} + '@mongodb-js/saslprep@1.5.0': + resolution: {integrity: sha512-Hk1SKJCMcCos38+vqDnZzlIo4XRj9yCGzYkjB4LcqpeXRIYfia1UWTz+VrueLxoU+uSRJzgkufxoRZg8gi52YA==} '@mswjs/interceptors@0.41.9': resolution: {integrity: sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==} @@ -4166,26 +4166,26 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@objectstack/client@17.0.0': - resolution: {integrity: sha512-pfapHGMXA0/nKohGNg6tXTZ9AuKa3lN7ZRrvb6N8h0+PEZsJLsGP13C2nb3Hc6T9NzCl4uO1nDMMSRYcLe++Ug==} + '@objectstack/client@17.1.0': + resolution: {integrity: sha512-pgd7JTIxN0TBDKs0bYuYWCJrPS8+H1sDby7vdvdYPBr4uaNBcwXQD3BWvSpaNyU11S8EtwWK5RQnqVYZARZEpA==} engines: {node: '>=22.0.0'} - '@objectstack/core@17.0.0': - resolution: {integrity: sha512-byJCvD92jvepzzcvydmQfSyjb5Qiu+r+RJEKn/qlRwYXFpMG9CbJovglBHyyCFRzbpWKc70z88FGrj5vlr25gQ==} + '@objectstack/core@17.1.0': + resolution: {integrity: sha512-4R+rmaNWNmW6K2hMK2GihCAYwLPJr9csqpq0Rmegs79ElXPRy93JNoVy5LG3WotVnIvUdPlGCWogdRhc4lRhqw==} engines: {node: '>=22.0.0'} - '@objectstack/formula@17.0.0': - resolution: {integrity: sha512-PhJcfhZBMZTmerGeFJfVuhntvhvPrZhXd8MxmBl8wJjOr0CpTsr25WsNxq86lwKlTkq9PRscSbIW8fxCiTRV9g==} + '@objectstack/formula@17.1.0': + resolution: {integrity: sha512-u54fhrTxkUySKHuRwA4XjFi8i4SEYemMI9dlA+GGubguwtoE27gTvH5J4CTWcrVb3oN9Cf+3Aymw3yrfFWirkA==} - '@objectstack/lint@17.0.0': - resolution: {integrity: sha512-U34gEiEK7VFF/By5RXRrbU69hhX0uekhfp9H5nhz+thr2zOcRjpL8FoAH+mwAZvv3bz/mWeVw0t40KiR5PIlww==} + '@objectstack/lint@17.1.0': + resolution: {integrity: sha512-qHh6oRHsjUGSzd8X3hiKQeV0jqFg2J98YxAArdaWV14AzJkYXnmQNv7bzLAdVyV1VWbnp/nPWprUVc3FTzjzBg==} engines: {node: '>=22.0.0'} - '@objectstack/sdui-parser@17.0.0': - resolution: {integrity: sha512-hHcjBsHH9B+WFHoqwBF5qV75Su9W0LXVFeWoTMJ+1mkdNyD5pKSQH7cWx81pTAkJ8yFTKlKUk74sLBHZIIHwPg==} + '@objectstack/sdui-parser@17.1.0': + resolution: {integrity: sha512-D6LuWbi6MlHrrQSX+4/MXx+n4M79PZtgOa9uOgyMVd/JoTVR/waWrIu2qrhkKP7TX/P9KNGql2ahR3fOPJ2txQ==} - '@objectstack/spec@17.0.0': - resolution: {integrity: sha512-65rmDnj6WKnIATvEg9/coDYSVUgBTtym/FysEApaK2W/k4ub0DP7dQ9VUmLB4HEbJxo0ahzD1tFObkXYEIT7Fw==} + '@objectstack/spec@17.1.0': + resolution: {integrity: sha512-gyF7knX3mqTEZSYeFIW/HBP0yFcjEzyXCS/p5pwH4yuJ4R2uw7mE3ItJs187CxZIFhFC2PTluRri3C/1Yaa9Og==} engines: {node: '>=22.0.0'} peerDependencies: ai: ^7.0.0 @@ -7152,6 +7152,9 @@ packages: dompurify@3.4.13: resolution: {integrity: sha512-2vmYIoqjze2d+kakP8S/nS5shfsl587kzwEjcGlTdiksUVgFHnFCsLYDVj/JNqJVOQZGSYBTmuycv0PodwmnMQ==} + dompurify@3.4.14: + resolution: {integrity: sha512-dVoH9z+MY+C9IilgGCk3YfFqjLi3fChm2OiKJMzh6axrJ5qwxqWaZamgmHrpv22CN/KdbZJuGEGgfQoL00LTdg==} + domutils@3.2.2: resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} @@ -9397,6 +9400,9 @@ packages: pend@1.2.0: resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} + pg-connection-string@2.14.0: + resolution: {integrity: sha512-XwWDGcLRGCXAR8F/AM5bG7Q+A3Wm2s6QeEjlOKZLlH3UYcguiqCWKyWXVag5TLTIjR7oOJUY8kcADaZgWPyLeg==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -12399,7 +12405,7 @@ snapshots: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - '@mongodb-js/saslprep@1.4.13': + '@mongodb-js/saslprep@1.5.0': dependencies: sparse-bitfield: 3.0.3 optional: true @@ -12455,32 +12461,32 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@objectstack/client@17.0.0(ai@7.0.65(zod@4.4.3))': + '@objectstack/client@17.1.0(ai@7.0.65(zod@4.4.3))': dependencies: - '@objectstack/core': 17.0.0(ai@7.0.65(zod@4.4.3)) - '@objectstack/spec': 17.0.0(ai@7.0.65(zod@4.4.3)) + '@objectstack/core': 17.1.0(ai@7.0.65(zod@4.4.3)) + '@objectstack/spec': 17.1.0(ai@7.0.65(zod@4.4.3)) transitivePeerDependencies: - ai - '@objectstack/core@17.0.0(ai@7.0.65(zod@4.4.3))': + '@objectstack/core@17.1.0(ai@7.0.65(zod@4.4.3))': dependencies: - '@objectstack/spec': 17.0.0(ai@7.0.65(zod@4.4.3)) + '@objectstack/spec': 17.1.0(ai@7.0.65(zod@4.4.3)) zod: 4.4.3 transitivePeerDependencies: - ai - '@objectstack/formula@17.0.0(ai@7.0.65(zod@4.4.3))': + '@objectstack/formula@17.1.0(ai@7.0.65(zod@4.4.3))': dependencies: '@marcbachmann/cel-js': 8.0.0 - '@objectstack/spec': 17.0.0(ai@7.0.65(zod@4.4.3)) + '@objectstack/spec': 17.1.0(ai@7.0.65(zod@4.4.3)) transitivePeerDependencies: - ai - '@objectstack/lint@17.0.0(ai@7.0.65(zod@4.4.3))': + '@objectstack/lint@17.1.0(ai@7.0.65(zod@4.4.3))': dependencies: - '@objectstack/formula': 17.0.0(ai@7.0.65(zod@4.4.3)) - '@objectstack/sdui-parser': 17.0.0 - '@objectstack/spec': 17.0.0(ai@7.0.65(zod@4.4.3)) + '@objectstack/formula': 17.1.0(ai@7.0.65(zod@4.4.3)) + '@objectstack/sdui-parser': 17.1.0 + '@objectstack/spec': 17.1.0(ai@7.0.65(zod@4.4.3)) ajv: 8.20.0 ajv-formats: 3.0.1(ajv@8.20.0) sucrase: 3.35.1 @@ -12488,10 +12494,11 @@ snapshots: transitivePeerDependencies: - ai - '@objectstack/sdui-parser@17.0.0': {} + '@objectstack/sdui-parser@17.1.0': {} - '@objectstack/spec@17.0.0(ai@7.0.65(zod@4.4.3))': + '@objectstack/spec@17.1.0(ai@7.0.65(zod@4.4.3))': dependencies: + pg-connection-string: 2.14.0 zod: 4.4.3 optionalDependencies: ai: 7.0.65(zod@4.4.3) @@ -15456,6 +15463,10 @@ snapshots: optionalDependencies: '@types/trusted-types': 2.0.7 + dompurify@3.4.14: + optionalDependencies: + '@types/trusted-types': 2.0.7 + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 @@ -17732,7 +17743,7 @@ snapshots: monaco-editor@0.55.1: dependencies: - dompurify: 3.4.13 + dompurify: 3.4.14 marked: 14.0.0 mongodb-connection-string-url@7.0.2: @@ -17743,7 +17754,7 @@ snapshots: mongodb@7.2.0(socks@2.8.9): dependencies: - '@mongodb-js/saslprep': 1.4.13 + '@mongodb-js/saslprep': 1.5.0 bson: 7.3.2 mongodb-connection-string-url: 7.0.2 optionalDependencies: @@ -18105,6 +18116,8 @@ snapshots: pend@1.2.0: {} + pg-connection-string@2.14.0: {} + picocolors@1.1.1: {} picomatch@2.3.2: {} diff --git a/scripts/check-action-forward-parity.mjs b/scripts/check-action-forward-parity.mjs index 46897832d4..c7377ec1fe 100644 --- a/scripts/check-action-forward-parity.mjs +++ b/scripts/check-action-forward-parity.mjs @@ -314,6 +314,25 @@ export const KNOWN_GAPS = { }, ]) ), + ...Object.fromEntries( + ["action:button", "action:icon", "action:group", "action:menu"].map((surface) => [ + `${surface}:onSuccess`, + { + reason: + "Newly OWED by the `@objectstack/spec` 17.1.0 pin bump (objectui#5328), not newly " + + "dropped: the runner has honoured it all along (ActionRunner.ts:1197 reads " + + "`action.onSuccess` and :1198 runs the chained defs), and 17.1.0 supplied the missing " + + "half by declaring the key authorable on `ActionSchema` — 0 occurrences in 17.0.0's " + + "`dist/**/*.d.ts`, 53 in 17.1.0. Both halves of the owed-set therefore hold for the " + + "first time on the bump. Forwarding it is capability WIRING, the same class the pin " + + "bump defers to its dependants (#5074 `viewMode`, #5042 `ListMapConfigSchema`), so it " + + "is filed rather than ridden in: objectui#5493. `element:button` is absent from this " + + "list because `onSuccess` is not on spec's `InlineActionSchema` pick list, so that " + + "surface never owed it.", + issue: 5493, + }, + ]) + ), }; // ── Opaque spreads ─────────────────────────────────────────────────────────── diff --git a/scripts/check-eager-closure-budget.mjs b/scripts/check-eager-closure-budget.mjs index c84bb2c411..4e40ad84a1 100644 --- a/scripts/check-eager-closure-budget.mjs +++ b/scripts/check-eager-closure-budget.mjs @@ -24,6 +24,10 @@ * `advancedChunks` deliberately routes vendor and workspace code into named * chunks, so MOST regressions land outside `index-*.js`. * + * That table is the MOTIVATING measurement and stays pinned to `77f846a8b`; it + * is not the current reading. {@link BASELINE} carries today's, and the ceiling + * section below does the arithmetic against it. + * * ## Where the number comes from * * `apps/console/vite.config.ts` (`emitEagerClosureReport`) writes @@ -41,20 +45,43 @@ * Two constraints pin it from both sides: * * - It must PASS on today's `main`. A gate that lands red is a gate someone - * disables, and this one is landing as a replacement for a gate nobody could - * fail. Headroom above the current 3,881,609 bytes: 78,391 (2.02%). + * disables, and this one replaced a gate nobody could fail. Headroom above + * the current 4,005,911 bytes: 80,089 (2.00%). * - The headroom must stay SMALLER than the regression the gate exists to - * catch. objectui#5266 was 89 KiB = 91,136 bytes; 78,391 < 91,136, so this + * catch. objectui#5266 was 89 KiB = 91,136 bytes; 80,089 < 91,136, so this * ceiling would have failed on that change. Widening the headroom past ~89 * KiB would leave the gate green through a repeat of its own motivating * incident. * - * This is a truthful CURRENT-STATE ceiling, not a target. 3.7 MB gzipped before + * ## Why this number moved once (objectui#5328, maintainer ruling on #5531) + * + * It was 3,960,000 over a 3,881,609 baseline measured on `77f846a8b`. Pinning + * `@objectstack/spec` and its three siblings to 17.1.0 put the closure 41,689 + * bytes over that ceiling: the release is ~930 KB larger uncompressed, and + * essentially all of it lands in `vendor-objectstack-*.js`. That is REAL added + * payload, not a measurement artefact, and it is larger than the #5266 + * regression this gate was sized to catch — the gate did its job. + * + * The re-baseline was therefore escalated rather than taken by the seat doing + * the bump, because #5468 had ruled days earlier that the aggregate ceiling + * "stays as shipped" and that gate-strength policy is the maintainer's. The + * maintainer ruled option A on #5531: raise it, permanently, together with the + * headroom assertion that guards it. Both constants move in ONE commit — raising + * the ceiling alone leaves headroom at ~200 KB and fails the test below, which + * is the guard working, not an obstacle to route around. + * + * What did NOT move: {@link REGRESSION_THIS_GATE_MUST_CATCH_BYTES}. That is the + * gate's sensitivity, the ruling did not touch it, and re-baselining must never + * become an excuse to widen it — a ceiling that rises while the sensitivity + * relaxes is a gate quietly retiring itself. + * + * This is a truthful CURRENT-STATE ceiling, not a target. 3.8 MB gzipped before * first render is a bad payload, and the honest long-term line is far below it — * but lowering the line is a separate decision with its own work behind it - * (objectui#5324 names the candidates: per-chunk budgets, a ratchet against - * `main`, or actually cleaving the closure). Nothing here should be read as a - * finding that 3.79 MB is acceptable. + * (objectui#5324 names the candidates; objectui#5490 is the ruled follow-up + * adding per-chunk budgets so `vendor-objectstack` cannot grow unnoticed inside + * aggregate headroom again). Nothing here should be read as a finding that + * 3.82 MB is acceptable. * * ## Raising it * @@ -70,9 +97,9 @@ import { pathToFileURL } from 'node:url'; /** * Ceiling for the console eager closure, in gzipped bytes. See the header for - * how this number was chosen; measured 3,881,609 on `77f846a8b`. + * how this number was chosen; measured 4,005,911 on `4c1623c0c`. */ -export const MAX_EAGER_CLOSURE_GZIP_BYTES = 3_960_000; +export const MAX_EAGER_CLOSURE_GZIP_BYTES = 4_086_000; /** * The measurement the ceiling above was derived from. Exported so the two @@ -83,10 +110,10 @@ export const MAX_EAGER_CLOSURE_GZIP_BYTES = 3_960_000; */ export const BASELINE = Object.freeze({ /** `emitEagerClosureReport`'s `eagerGzipBytes` on this commit. */ - gzipBytes: 3_881_609, - chunks: 58, - totalChunks: 507, - commit: '77f846a8b', + gzipBytes: 4_005_911, + chunks: 52, + totalChunks: 508, + commit: '4c1623c0c', }); /** diff --git a/scripts/check-spec-symbol-derivation.mjs b/scripts/check-spec-symbol-derivation.mjs index 9233b59fa6..e84e943117 100644 --- a/scripts/check-spec-symbol-derivation.mjs +++ b/scripts/check-spec-symbol-derivation.mjs @@ -417,6 +417,24 @@ const DEBT = { "NavigationItem", "NavigationItemSchema", ], + // Entered by the `@objectstack/spec` 17.1.0 pin bump (objectui#5328), and it is + // worth naming how this collision differs from the three above: objectui did + // not fork a spec name here. The local `ReferenceRailEntry` predates the + // release and did not move — 17.1.0 started EXPORTING the name (absent from + // 17.0.0's `dist/` entirely), so the collision arrived from upstream. The + // gate's own framing, "a fresh fork fails on the PR that writes it", does not + // fit a PR that wrote no fork. + // + // Not burnable in that PR either, because the two shapes disagree on one key: + // spec's `ReferenceRailEntrySchema` is `$strict` over + // {objectName, relationshipField, title, limit, displayField}, while the local + // interface adds `icon` — which the renderer reads and the strict schema + // REFUSES at save. Importing/deriving retires `icon`; renaming to a dialect + // keeps it unsaveable. Either is a contract call, so it is filed rather than + // guessed: objectui#5494. + "@object-ui/plugin-detail": [ + "ReferenceRailEntry", + ], }; // Files under these paths are not objectui's own authored surface.