From 7fd87174b91e7f1789d61f8e6c420fa1c1bfcc00 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 20:50:50 +0000 Subject: [PATCH 1/7] refactor(ui)!: the renderer runs @objectstack/sdui-parser; delete @object-ui/sdui-parser 2g PR 2 (ruling 6418729849, E + B). Every ui importer, package manifest, alias and tsconfig path now names @objectstack/sdui-parser, and ui/packages/sdui-parser is deleted with no forwarding release. It leaves the changesets fixed group. gen-manifest.ts moves to ui/apps/console/scripts/. The five tests PR 1 left behind are re-homed. The ui guards that counted the package drop it, and the manifest record no longer reaches it. Claude-Session: https://claude.ai/code/session_01Xe3LjBvLrRVA6dmFUEGskm Co-authored-by: Claude --- .../25712-renderer-runs-one-sdui-parser.md | 30 + .changeset/25831-object-ui-18-1-to-merge.md | 1 - .changeset/26276-ui-package-test-vitest.md | 1 - .changeset/config.json | 1 - .../docs/protocol/backward-compatibility.mdx | 4 +- .../lint/src/sdui-spec-symbol-batch7.test.ts | 3 +- .../src/dashboard-widget-options.ts | 4 +- packages/spec/liveness/page.json | 2 +- pnpm-lock.yaml | 99 +- scripts/check-type-check-coverage.mjs | 2 - scripts/check-undeclared-dep-imports.mjs | 2 +- scripts/gen-sdui-manifest-node.mjs | 3 +- scripts/publish-smoke-pack.mjs | 2 +- scripts/sdui-manifest.sources/components.json | 2 +- scripts/sdui-manifest.sources/core.json | 2 +- .../plugin-dashboard.json | 2 +- .../sdui-manifest.sources/sdui-parser.json | 1 - scripts/sdui-manifest.sources/types.json | 2 +- skills/objectui/README.md | 2 +- ui/.github/labeler.yml | 4 - ui/QUICK_REFERENCE.md | 2 +- ui/README.md | 2 +- ui/apps/console/README.md | 2 +- ui/apps/console/dev/manifest-dump.tsx | 2 +- ui/apps/console/dev/manifest-registry.ts | 2 +- ui/apps/console/package.json | 2 +- ui/apps/console/scripts/emit-sdui-manifest.ts | 4 +- .../console}/scripts/gen-manifest.ts | 2 +- .../component-input-union-specimens.test.ts | 4 +- ...ement-data-source-input-injection.test.tsx | 2 +- .../ga-honoured-inputs-author-reach.test.ts | 4 +- .../html-node-retired-13449.test.tsx | 2 +- .../html-tier-code-element-10756.test.tsx | 2 +- .../src/__tests__/html-tier-manifest.test.ts | 4 +- ...erDataSourceInputDeclaration-6678.test.tsx | 6 +- .../i18nLabelInputsManifest-10993.test.ts | 4 +- ...ine-items-bound-child-object-11569.test.ts | 4 +- .../masterDetailFormI18nLabelManifest.test.ts | 4 +- .../masterDetailFormTypeManifest.test.ts | 4 +- .../objectFormI18nLabelManifest.test.ts | 4 +- .../objectname-binding-required-11605.test.ts | 4 +- .../src/__tests__/public-contract.test.ts | 2 +- .../registry-inputs-spec-parity.test.ts | 4 +- ...elated-list-columns-optional-11613.test.ts | 4 +- .../sdui-intrinsics-compile-11075.test.ts | 4 +- .../sdui-manifest-build-11403.test.ts | 4 +- .../sdui-preview-page-source-styling.test.ts | 2 +- ui/apps/console/vite.config.ts | 2 +- ui/content/docs/api/schema-reference.md | 2 +- ui/content/docs/plugins/plugin-dashboard.mdx | 2 +- ui/content/docs/plugins/plugin-kanban.mdx | 2 +- ui/eslint.config.js | 2 +- ui/examples/console-starter/README.md | 2 +- ui/examples/console-starter/package.json | 2 +- ui/examples/console-starter/vite.config.ts | 2 +- ui/examples/schema-catalog/package.json | 2 +- .../test/pageheader-with-actions.test.tsx | 4 +- .../previews/simulator/flow-sim-types.ts | 2 +- .../cli/src/__tests__/workspace-vite.test.ts | 12 +- ui/packages/cli/src/commands/doctor.ts | 2 +- ui/packages/components/package.json | 2 +- .../div-deprecation-provenance.test.tsx | 2 +- .../grid-flat-columns-retired-11505.test.tsx | 4 +- ...ut-containers-declare-containment.test.tsx | 4 +- .../record-picker-inputs-spec-parity.test.ts | 2 +- ...-sonner-button-variant-inputs-7316.test.ts | 2 +- .../base-props-one-list-11044.test.tsx | 6 +- .../bind-base-prop-parser-tier-11008.test.tsx | 6 +- .../container-declaration-census.test.tsx | 4 +- .../container-declaration-ratchet.test.tsx | 4 +- ...ar-unknown-prop-parser-tier-10981.test.tsx | 4 +- .../node-slot-declaration-11170.test.tsx | 4 +- .../action-button-icon-inputs-11168.test.tsx | 4 +- .../action-button-undoable-11168.test.tsx | 4 +- .../action-type-input-html-tier.test.tsx | 2 +- ...data-list.elementDataSource-11880.test.tsx | 4 +- .../element-list-inputs-11168.test.tsx | 4 +- ...ment-text-published-variants-7450.test.tsx | 2 +- ...entNumber.dataSourceBinding-10909.test.tsx | 2 +- .../components/src/renderers/basic/span.tsx | 2 +- .../components/src/renderers/layout/page.tsx | 4 +- ui/packages/core/src/registry/Registry.ts | 2 +- .../core/src/registry/public-blocks.ts | 2 +- ui/packages/layout/package.json | 2 +- .../appSchemaRendererMobileNavMode.test.tsx | 2 +- .../containment-declared-slot-9910.test.tsx | 4 +- ui/packages/plugin-calendar/package.json | 2 +- ...ataKeysAreDeclaredAndHonoured-8314.test.ts | 2 +- .../filterIsDeclaredInput-7712.test.ts | 2 +- .../__tests__/recordSourceInput-10392.test.ts | 2 +- ...larKeysAreDeclaredAndHonoured-8201.test.ts | 2 +- ui/packages/plugin-dashboard/package.json | 2 +- .../plugin-dashboard/src/DatasetWidget.tsx | 2 +- .../dashboard-widget-options-census.test.ts | 9 +- .../dashboardAuthoredInputs.test.tsx | 4 +- .../designerRegistrationInputs-11434.test.ts | 5 +- ui/packages/plugin-detail/package.json | 2 +- ...ailSectionAuthoredHideEmpty-10485.test.tsx | 2 +- .../detailSectionAuthoredIcon-9529.test.tsx | 2 +- .../detailSectionAuthoredNode-8626.test.tsx | 2 +- .../detailSectionHeaderColorEnum-6955.test.ts | 2 +- ui/packages/plugin-editor/vite.config.ts | 2 +- ui/packages/plugin-gantt/package.json | 2 +- .../objectGanttInputs-11168.test.tsx | 2 +- .../__tests__/queryKeysDeclared-8220.test.tsx | 2 +- .../recordSourceInputs-10394.test.tsx | 2 +- ui/packages/plugin-grid/package.json | 2 +- .../src/__tests__/gridNonAuthorKeys.test.tsx | 2 +- ui/packages/plugin-kanban/package.json | 2 +- .../filterIsDeclaredInput-7712.test.ts | 2 +- .../quickAddRetiredNotForwarded-8285.test.tsx | 2 +- ...larKeysAreDeclaredAndHonoured-8201.test.ts | 2 +- ...redKeysAreDeclaredAndHonoured-8313.test.ts | 2 +- .../htmlTierColumnSpellings-6598.test.tsx | 35 +- ui/packages/plugin-map/package.json | 2 +- .../__tests__/objectMapInputs-11168.test.tsx | 2 +- .../__tests__/queryKeysDeclared-8220.test.tsx | 2 +- .../index.bareMapKeyRetired-10393.test.tsx | 2 +- .../index.recordSourceInputs-10394.test.tsx | 2 +- ui/packages/plugin-markdown/vite.config.ts | 2 +- ui/packages/plugin-timeline/package.json | 2 +- .../object-timeline-navigation-8654.test.tsx | 2 +- .../objectTimelineInputs-11168.test.tsx | 2 +- .../__tests__/queryKeysDeclared-8220.test.tsx | 2 +- ui/packages/plugin-tree/package.json | 2 +- .../__tests__/objectTreeInputs-11168.test.tsx | 2 +- ui/packages/react-runtime/README.md | 2 +- ui/packages/react/package.json | 1 + ...dui-injected-component-input-6950.test.ts} | 2 +- .../sdui-inline-whitespace.test.tsx} | 5 +- .../src/__tests__/sdui-render.test.tsx} | 4 +- ui/packages/runner/vite.config.ts | 4 +- ui/packages/sdui-parser/CHANGELOG.md | 1146 ----------------- ui/packages/sdui-parser/LICENSE | 21 - ui/packages/sdui-parser/package.json | 40 - .../base-props-bind-hidden-11008.test.ts | 88 -- .../base-props-one-list-11044.test.ts | 265 ---- .../src/__tests__/body-dialect-6771.test.ts | 131 -- .../sdui-parser/src/__tests__/compile.test.ts | 84 -- .../containment-declared-slot-9910.test.ts | 133 -- .../dashboard-widget-options.test.ts | 231 ---- .../html-tier-manifest-10735.test.ts | 117 -- .../__tests__/inert-expression-6598.test.ts | 112 -- .../src/__tests__/input-type-union.test.ts | 233 ---- .../__tests__/kanban-quick-add-8285.test.ts | 96 -- .../src/__tests__/literal-subset-6614.test.ts | 281 ---- .../src/__tests__/member-kind-8067.test.ts | 203 --- .../__tests__/node-slot-walk-11170.test.ts | 106 -- .../src/__tests__/provenance.test.ts | 128 -- .../sdui-parser/src/__tests__/tier.test.ts | 49 - .../type-attribute-collision-7235.test.ts | 169 --- ui/packages/sdui-parser/src/body-dialect.ts | 101 -- ui/packages/sdui-parser/src/codegen.ts | 234 ---- .../src/dashboard-widget-options.ts | 181 --- ui/packages/sdui-parser/src/index.ts | 257 ---- ui/packages/sdui-parser/src/input-type.ts | 75 -- ui/packages/sdui-parser/src/parse.ts | 605 --------- ui/packages/sdui-parser/src/provenance.ts | 83 -- ui/packages/sdui-parser/src/types.ts | 244 ---- ui/packages/sdui-parser/src/validate.ts | 527 -------- ui/packages/sdui-parser/tsconfig.json | 33 - ui/packages/sdui-parser/tsconfig.test.json | 30 - ui/packages/sdui-parser/verify.ts | 70 - .../component-input-retired-keys-7493.test.ts | 4 +- ...t-channel-parser-tier-clause-10928.test.ts | 4 +- ...e-clause-parser-tier-closure-10981.test.ts | 4 +- ...nown-prop-parser-tier-clause-10959.test.ts | 4 +- ui/packages/types/src/node-slots.ts | 4 +- ui/packages/types/src/zod/layout.zod.ts | 2 +- .../body-dialect-children-arm-7181.test.ts | 5 +- .../__tests__/check-dist-completeness.test.ts | 2 +- .../__tests__/package-files-exist.test.ts | 24 +- .../registry-assertion-ratchet-8316.test.ts | 2 +- .../__tests__/select-type-check-scope.test.ts | 8 +- ...de-effects-declaration-consistency.test.ts | 9 +- ...nconsumed-widget-option-claim-6186.test.ts | 13 +- ui/tsconfig.json | 4 +- ui/turbo.objectui.json | 12 +- ui/vitest.config.mts | 2 +- 179 files changed, 318 insertions(+), 6378 deletions(-) create mode 100644 .changeset/25712-renderer-runs-one-sdui-parser.md rename ui/packages/sdui-parser/src/__tests__/spec-symbol-batch7.test.ts => packages/lint/src/sdui-spec-symbol-batch7.test.ts (96%) delete mode 100644 scripts/sdui-manifest.sources/sdui-parser.json rename ui/{packages/sdui-parser => apps/console}/scripts/gen-manifest.ts (96%) rename ui/packages/{sdui-parser => plugin-dashboard}/src/__tests__/dashboard-widget-options-census.test.ts (97%) rename ui/packages/{sdui-parser/src/__tests__/injected-component-input-6950.test.ts => react/src/__tests__/sdui-injected-component-input-6950.test.ts} (99%) rename ui/packages/{sdui-parser/src/__tests__/inline-whitespace.test.tsx => react/src/__tests__/sdui-inline-whitespace.test.tsx} (98%) rename ui/packages/{sdui-parser/src/__tests__/render.test.tsx => react/src/__tests__/sdui-render.test.tsx} (94%) delete mode 100644 ui/packages/sdui-parser/CHANGELOG.md delete mode 100644 ui/packages/sdui-parser/LICENSE delete mode 100644 ui/packages/sdui-parser/package.json delete mode 100644 ui/packages/sdui-parser/src/__tests__/base-props-bind-hidden-11008.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/base-props-one-list-11044.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/body-dialect-6771.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/compile.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/containment-declared-slot-9910.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/dashboard-widget-options.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/html-tier-manifest-10735.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/inert-expression-6598.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/input-type-union.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/kanban-quick-add-8285.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/literal-subset-6614.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/member-kind-8067.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/node-slot-walk-11170.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/provenance.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/tier.test.ts delete mode 100644 ui/packages/sdui-parser/src/__tests__/type-attribute-collision-7235.test.ts delete mode 100644 ui/packages/sdui-parser/src/body-dialect.ts delete mode 100644 ui/packages/sdui-parser/src/codegen.ts delete mode 100644 ui/packages/sdui-parser/src/dashboard-widget-options.ts delete mode 100644 ui/packages/sdui-parser/src/index.ts delete mode 100644 ui/packages/sdui-parser/src/input-type.ts delete mode 100644 ui/packages/sdui-parser/src/parse.ts delete mode 100644 ui/packages/sdui-parser/src/provenance.ts delete mode 100644 ui/packages/sdui-parser/src/types.ts delete mode 100644 ui/packages/sdui-parser/src/validate.ts delete mode 100644 ui/packages/sdui-parser/tsconfig.json delete mode 100644 ui/packages/sdui-parser/tsconfig.test.json delete mode 100644 ui/packages/sdui-parser/verify.ts diff --git a/.changeset/25712-renderer-runs-one-sdui-parser.md b/.changeset/25712-renderer-runs-one-sdui-parser.md new file mode 100644 index 00000000000..3a7068a98cb --- /dev/null +++ b/.changeset/25712-renderer-runs-one-sdui-parser.md @@ -0,0 +1,30 @@ +--- +'@object-ui/components': patch +'@object-ui/console': patch +'@object-ui/layout': patch +'@object-ui/plugin-calendar': patch +'@object-ui/plugin-dashboard': patch +'@object-ui/plugin-detail': patch +'@object-ui/plugin-gantt': patch +'@object-ui/plugin-grid': patch +'@object-ui/plugin-kanban': patch +'@object-ui/plugin-map': patch +'@object-ui/plugin-timeline': patch +'@object-ui/plugin-tree': patch +'@object-ui/react': patch +--- + +refactor(ui)!: the renderer runs `@objectstack/sdui-parser`, and `@object-ui/sdui-parser` is deleted (#25712) + +Clause-②: yes (narrowing: a `type-mismatch` or `member-type-mismatch` the html-tier compile used to grade `warning` is now an `error`, so a page that carried one now refuses to compile instead of rendering with the value dropped) +Metadata-protocol: none + +The renderer and the save gate each ran their own copy of the SDUI parser. The previous change under this card made `@objectstack/sdui-parser` the one body. This change moves every `@object-ui/*` package, the console and the examples onto it, and deletes `ui/packages/sdui-parser`. There is no forwarding release of `@object-ui/sdui-parser`: it leaves the fixed version group, and the 18.1 line on npm is its last. + +- **Dependencies.** Each package above now depends on `@objectstack/sdui-parser` where it depended on `@object-ui/sdui-parser`. The console's `gen-manifest.ts` moved from the deleted package to `ui/apps/console/scripts/`. +- **Severity, the one narrowing.** The renderer's copy graded a `type-mismatch` as an `error` only beside an `enum` arm, and as a `warning` otherwise. The kept rule grades every `type-mismatch` and `member-type-mismatch` an `error`, which is what the save gate already did. So an html-tier page whose attribute does not fit the declared type now shows the compile-error block naming the prop, where it rendered the block with that value dropped. Example: `` (a string where `columns` is declared `array`). A page the save gate accepted is not affected, because the save gate already refused it. +- **Body-dialect messages.** The retired `body` child-list key's `unknown-prop` message now ends `("body" is its retired spelling)`, and its `not-a-container` message drops the `(objectui#6771)` parenthetical. + +Migration: none for a page the save gate accepted. A page that relied on the warning has a value the declared type does not take: write it in the declared type, for example `columns={['name','amount']}`. + + diff --git a/.changeset/25831-object-ui-18-1-to-merge.md b/.changeset/25831-object-ui-18-1-to-merge.md index a17d8569526..9f95a263fc4 100644 --- a/.changeset/25831-object-ui-18-1-to-merge.md +++ b/.changeset/25831-object-ui-18-1-to-merge.md @@ -35,7 +35,6 @@ "@object-ui/providers": minor "@object-ui/react": minor "@object-ui/react-runtime": minor -"@object-ui/sdui-parser": minor "@object-ui/types": minor --- diff --git a/.changeset/26276-ui-package-test-vitest.md b/.changeset/26276-ui-package-test-vitest.md index b511d602d6a..0b83f5afb68 100644 --- a/.changeset/26276-ui-package-test-vitest.md +++ b/.changeset/26276-ui-package-test-vitest.md @@ -19,7 +19,6 @@ '@object-ui/providers': patch '@object-ui/react': patch '@object-ui/react-runtime': patch -'@object-ui/sdui-parser': patch '@object-ui/types': patch --- diff --git a/.changeset/config.json b/.changeset/config.json index a6098cd3aac..ca7dc39bec5 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -117,7 +117,6 @@ "@object-ui/providers", "@object-ui/react", "@object-ui/react-runtime", - "@object-ui/sdui-parser", "@object-ui/types", "object-ui" ] diff --git a/content/docs/protocol/backward-compatibility.mdx b/content/docs/protocol/backward-compatibility.mdx index 3ce98359950..29423b59a0c 100644 --- a/content/docs/protocol/backward-compatibility.mdx +++ b/content/docs/protocol/backward-compatibility.mdx @@ -116,7 +116,7 @@ Read the **Breaking?** column, not the version number: in the 17.x minors every 1. **RFC (Request for Comments)** — Breaking changes are proposed as GitHub issues with the `protocol:breaking` label. 2. **Deprecation** — The old behavior is deprecated in a MINOR release (see timeline above). 3. **Migration Guide** — A detailed migration guide is published before the removal lands, in the release notes for the version that carries it. -4. **Release** — In the 17.x minors the breaking change shipped in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`, and `scripts/check-changeset-no-major.mjs` failed a pull request that declared a `major` bump, because under lockstep one `major` would promote all 111 published packages. During the v18 prerelease it shipped in the next `18.0.0-next.N`, because the guard stood aside in pre mode and a pull request could declare a major bump. Changesets has exited pre mode, so the guard fails a `major` bump again. See [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line). +4. **Release** — In the 17.x minors the breaking change shipped in the next **MINOR** version, carrying a changeset entry marked `**BREAKING**`, and `scripts/check-changeset-no-major.mjs` failed a pull request that declared a `major` bump, because under lockstep one `major` would promote all 110 published packages. During the v18 prerelease it shipped in the next `18.0.0-next.N`, because the guard stood aside in pre mode and a pull request could declare a major bump. Changesets has exited pre mode, so the guard fails a `major` bump again. See [how a breaking stage lands on the v18 line](/docs/releases/v18#how-a-breaking-stage-lands-on-this-line). --- @@ -196,7 +196,7 @@ The `@objectstack/spec` package provides additional stability guarantees: ### Which surfaces this covers -**All of them.** It was not scoped to an experimental corner or a pre-release channel: all **111** packages published from this repository belong to a single Changesets `fixed` group, so they share one version number and one policy. No published surface was exempt. +**All of them.** It was not scoped to an experimental corner or a pre-release channel: all **110** packages published from this repository belong to a single Changesets `fixed` group, so they share one version number and one policy. No published surface was exempt. The convention was enforced rather than informal — `scripts/check-changeset-no-major.mjs` failed a pull request that introduced a `major` bump, because under lockstep a single `major` on one package would promote the entire stack. During the v18 prerelease Changesets was in pre mode with the tag `next` and the guard stood aside, so a pull request on the v18 line could declare a major bump. Changesets has exited pre mode, and the guard fails a `major` bump again. diff --git a/ui/packages/sdui-parser/src/__tests__/spec-symbol-batch7.test.ts b/packages/lint/src/sdui-spec-symbol-batch7.test.ts similarity index 96% rename from ui/packages/sdui-parser/src/__tests__/spec-symbol-batch7.test.ts rename to packages/lint/src/sdui-spec-symbol-batch7.test.ts index 1a890a22903..feb6105fc53 100644 --- a/ui/packages/sdui-parser/src/__tests__/spec-symbol-batch7.test.ts +++ b/packages/lint/src/sdui-spec-symbol-batch7.test.ts @@ -29,8 +29,7 @@ import { describe, it, expect } from 'vitest'; import type { ValidationResult as SpecKernelValidationResult } from '@objectstack/spec/kernel'; import type { ValidationResult as SpecContractsValidationResult } from '@objectstack/spec/contracts'; -import type { ManifestValidationResult } from '../types.js'; -import { validateTree } from '../validate.js'; +import { validateTree, type ManifestValidationResult } from '@objectstack/sdui-parser'; describe('validateTree still returns the manifest-shaped result', () => { it('returns diagnostics / requires / bindings, not a `valid` flag', () => { diff --git a/packages/sdui-parser/src/dashboard-widget-options.ts b/packages/sdui-parser/src/dashboard-widget-options.ts index d3941813a90..49a7a2eb0c1 100644 --- a/packages/sdui-parser/src/dashboard-widget-options.ts +++ b/packages/sdui-parser/src/dashboard-widget-options.ts @@ -65,7 +65,7 @@ * repo-wide scan would red on a TRUE claim — and an assertion that reds on * legitimate code gets deleted by the next person who hits it, which puts * the claim back where it started. Leg 2 of - * `ui/packages/sdui-parser/src/__tests__/dashboard-widget-options-census.test.ts` + * `ui/packages/plugin-dashboard/src/__tests__/dashboard-widget-options-census.test.ts` * derives exactly this bound: the DatasetWidget read set is the declared * set, and neither `format` nor `thresholds` is in it. That absence is * asserted in its OWN right rather than riding on an equality whose @@ -101,7 +101,7 @@ * * ## Maintenance — how this list stays true * - * `ui/packages/sdui-parser/src/__tests__/dashboard-widget-options-census.test.ts` + * `ui/packages/plugin-dashboard/src/__tests__/dashboard-widget-options-census.test.ts` * re-runs the census on every test run: it re-derives the five declared keys from the installed * `@objectstack/spec`, re-extracts the `options.` reads from * `DatasetWidget.tsx` source text (and fails loudly if that file gains a diff --git a/packages/spec/liveness/page.json b/packages/spec/liveness/page.json index 226f5c48b12..ffbb1fca253 100644 --- a/packages/spec/liveness/page.json +++ b/packages/spec/liveness/page.json @@ -4,7 +4,7 @@ "props": { "source": { "status": "live", - "note": "JSX-source page authoring (ADR-0080). Consumer: objectui PageRenderer compiles `source` via @object-ui/sdui-parser into the SchemaNode tree (parse, never execute) and renders it — components/src/renderers/layout/page.tsx (kind:'jsx' branch). Browser-verified in the Command Center showcase." + "note": "JSX-source page authoring (ADR-0080). Consumer: objectui PageRenderer compiles `source` via @objectstack/sdui-parser into the SchemaNode tree (parse, never execute) and renders it — components/src/renderers/layout/page.tsx (kind:'jsx' branch). Browser-verified in the Command Center showcase." }, "requires": { "status": "live", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 350bfb01bde..9b61ddac02f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -3496,9 +3496,9 @@ importers: ui/apps/console: dependencies: - '@object-ui/sdui-parser': + '@objectstack/sdui-parser': specifier: workspace:* - version: link:../../packages/sdui-parser + version: link:../../../packages/sdui-parser devDependencies: '@dnd-kit/core': specifier: ^6.3.1 @@ -3928,9 +3928,9 @@ importers: '@object-ui/react-runtime': specifier: workspace:* version: link:../../packages/react-runtime - '@object-ui/sdui-parser': + '@objectstack/sdui-parser': specifier: workspace:* - version: link:../../packages/sdui-parser + version: link:../../../packages/sdui-parser react: specifier: 19.3.0 version: 19.3.0 @@ -4044,12 +4044,12 @@ importers: '@object-ui/react': specifier: workspace:* version: link:../../packages/react - '@object-ui/sdui-parser': - specifier: workspace:* - version: link:../../packages/sdui-parser '@object-ui/test-support': specifier: workspace:* version: link:../../packages/test-support + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser typescript: specifier: ^6.0.3 version: 6.0.3 @@ -4336,12 +4336,12 @@ importers: '@object-ui/react-runtime': specifier: workspace:* version: link:../react-runtime - '@object-ui/sdui-parser': - specifier: workspace:* - version: link:../sdui-parser '@object-ui/types': specifier: workspace:* version: link:../types + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser '@objectstack/spec': specifier: workspace:^ version: link:../../../packages/spec @@ -4754,12 +4754,12 @@ importers: specifier: 19.3.0 version: 19.3.0(react@19.3.0) devDependencies: - '@object-ui/sdui-parser': - specifier: workspace:* - version: link:../sdui-parser '@object-ui/test-support': specifier: workspace:* version: link:../test-support + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser '@vitejs/plugin-react': specifier: ^6.0.5 version: 6.0.5(vite@8.3.5(@types/node@26.6.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15(patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6))(yaml@2.9.1)) @@ -4911,12 +4911,12 @@ importers: '@object-ui/data-objectstack': specifier: workspace:* version: link:../data-objectstack - '@object-ui/sdui-parser': - specifier: workspace:* - version: link:../sdui-parser '@object-ui/test-support': specifier: workspace:* version: link:../test-support + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser '@types/react': specifier: 19.3.0 version: 19.3.0 @@ -5124,12 +5124,12 @@ importers: '@object-ui/plugin-charts': specifier: workspace:* version: link:../plugin-charts - '@object-ui/sdui-parser': - specifier: workspace:* - version: link:../sdui-parser '@object-ui/test-support': specifier: workspace:* version: link:../test-support + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser '@types/react-grid-layout': specifier: ^2.1.0 version: 2.1.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0) @@ -5267,15 +5267,15 @@ importers: '@object-ui/react': specifier: workspace:* version: link:../react - '@object-ui/sdui-parser': - specifier: workspace:* - version: link:../sdui-parser '@object-ui/test-support': specifier: workspace:* version: link:../test-support '@object-ui/types': specifier: workspace:* version: link:../types + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser '@types/react': specifier: 19.3.0 version: 19.3.0 @@ -5444,12 +5444,12 @@ importers: specifier: ^2.0.8 version: 2.0.8(@types/react@19.3.0)(react-dom@19.3.0(react@19.3.0))(react@19.3.0) devDependencies: - '@object-ui/sdui-parser': - specifier: workspace:* - version: link:../sdui-parser '@object-ui/test-support': specifier: workspace:* version: link:../test-support + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser '@types/react': specifier: 19.3.0 version: 19.3.0 @@ -5523,12 +5523,12 @@ importers: '@object-ui/data-objectstack': specifier: workspace:* version: link:../data-objectstack - '@object-ui/sdui-parser': - specifier: workspace:* - version: link:../sdui-parser '@object-ui/test-support': specifier: workspace:* version: link:../test-support + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser '@tailwindcss/postcss': specifier: ^4.3.3 version: 4.3.3 @@ -5602,12 +5602,12 @@ importers: '@object-ui/data-objectstack': specifier: workspace:* version: link:../data-objectstack - '@object-ui/sdui-parser': - specifier: workspace:* - version: link:../sdui-parser '@object-ui/test-support': specifier: workspace:* version: link:../test-support + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser '@tailwindcss/postcss': specifier: ^4.3.3 version: 4.3.3 @@ -5742,9 +5742,9 @@ importers: '@object-ui/plugin-list': specifier: workspace:* version: link:../plugin-list - '@object-ui/sdui-parser': + '@objectstack/sdui-parser': specifier: workspace:* - version: link:../sdui-parser + version: link:../../../packages/sdui-parser '@types/react': specifier: 19.3.0 version: 19.3.0 @@ -5937,12 +5937,12 @@ importers: '@object-ui/data-objectstack': specifier: workspace:* version: link:../data-objectstack - '@object-ui/sdui-parser': - specifier: workspace:* - version: link:../sdui-parser '@object-ui/test-support': specifier: workspace:* version: link:../test-support + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser '@types/react': specifier: 19.3.0 version: 19.3.0 @@ -6004,12 +6004,12 @@ importers: specifier: 19.3.0 version: 19.3.0(react@19.3.0) devDependencies: - '@object-ui/sdui-parser': - specifier: workspace:* - version: link:../sdui-parser '@object-ui/test-support': specifier: workspace:* version: link:../test-support + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser '@types/react': specifier: 19.3.0 version: 19.3.0 @@ -6151,6 +6151,9 @@ importers: '@object-ui/test-support': specifier: workspace:* version: link:../test-support + '@objectstack/sdui-parser': + specifier: workspace:* + version: link:../../../packages/sdui-parser '@types/react': specifier: 19.3.0 version: 19.3.0 @@ -6244,24 +6247,6 @@ importers: specifier: ^4.1.10 version: 4.1.12(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@4.1.12)(@vitest/ui@4.1.10)(happy-dom@21.0.4)(jsdom@30.1.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.6.3)(typescript@6.0.3))(vite@8.3.5(@types/node@26.6.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15(patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6))(yaml@2.9.1)) - ui/packages/sdui-parser: - devDependencies: - '@object-ui/core': - specifier: workspace:* - version: link:../core - '@object-ui/react': - specifier: workspace:* - version: link:../react - '@object-ui/types': - specifier: workspace:* - version: link:../types - '@objectstack/spec': - specifier: workspace:^ - version: link:../../../packages/spec - vitest: - specifier: ^4.1.10 - version: 4.1.12(@opentelemetry/api@1.9.1)(@types/node@26.6.3)(@vitest/coverage-v8@4.1.12)(@vitest/ui@4.1.10)(happy-dom@21.0.4)(jsdom@30.1.1(@noble/hashes@2.4.0))(msw@2.15.0(@types/node@26.6.3)(typescript@6.0.3))(vite@8.3.5(@types/node@26.6.3)(esbuild@0.28.2)(jiti@2.7.0)(tsx@4.23.15(patch_hash=4d1d35da1809be2a945519cb26890e48107810b9b66c6913a6a89abb5ad6a1e6))(yaml@2.9.1)) - ui/packages/test-support: devDependencies: '@objectstack/spec': diff --git a/scripts/check-type-check-coverage.mjs b/scripts/check-type-check-coverage.mjs index 5771361b471..b952d5ac06e 100644 --- a/scripts/check-type-check-coverage.mjs +++ b/scripts/check-type-check-coverage.mjs @@ -1342,8 +1342,6 @@ const UNCHECKED_SOURCE_DEBT = { 'objectui import: demo/main.tsx, the package\'s vite dev page. Probe: 1 x TS2882 (side-effect import of @object-ui/components/style.css).', 'ui/packages/plugin-grid/demo': 'objectui import: demo/main.tsx and demo/bulk-actions.tsx, vite dev pages. Probe: 2 x TS2882 (style.css side-effect imports) + 1 x TS2322 (bulk-actions i18n resources prop).', - 'ui/packages/sdui-parser/scripts': - 'objectui import: scripts/gen-manifest.ts, the SDUI manifest generator run with tsx. Probe: 1 x TS2591 (`node:fs`, no node types in the package program).', }; // The ceiling those six set. The self-test's FLOOR battery holds the ledger at // or below it, so the list only shrinks from the import on. diff --git a/scripts/check-undeclared-dep-imports.mjs b/scripts/check-undeclared-dep-imports.mjs index f779baeba80..daf2563b29f 100644 --- a/scripts/check-undeclared-dep-imports.mjs +++ b/scripts/check-undeclared-dep-imports.mjs @@ -334,7 +334,7 @@ const LEDGER = [ why: '@object-ui/console publishes a prebuilt SPA: `files` is dist + plugin.{ts,js,d.ts} + README, ' + 'and `exports` resolves to plugin.js, so src/** is bundled by vite into dist and never reaches ' - + 'an installing consumer. Its only runtime dependency is @object-ui/sdui-parser; @objectstack/spec ' + + 'an installing consumer. Its only runtime dependency is @objectstack/sdui-parser; @objectstack/spec ' + 'is a build input, which is what devDependencies records.', }, { diff --git a/scripts/gen-sdui-manifest-node.mjs b/scripts/gen-sdui-manifest-node.mjs index 6b523119638..ad9208bd9b9 100644 --- a/scripts/gen-sdui-manifest-node.mjs +++ b/scripts/gen-sdui-manifest-node.mjs @@ -25,8 +25,7 @@ * (`apps/console/dev/manifest-dump.tsx`) exactly: eager-import the 16 registry * modules IN ITS ORDER, read `ComponentRegistry.getPublicConfigs()`, and * serialize through `manifestFromConfigs` from `@objectstack/sdui-parser`, - * the one parser the save gate runs (#25712 item 3 retires the renderer's - * second copy under `ui/packages/sdui-parser`). + * the one parser both the save gate and the renderer run (#25712 item 3). * * `apps/console/node_modules/@object-ui/` in the built tree carries every * registry package as a workspace symlink whose `exports["."].import` is diff --git a/scripts/publish-smoke-pack.mjs b/scripts/publish-smoke-pack.mjs index bd334a4efd3..63610cb68b1 100644 --- a/scripts/publish-smoke-pack.mjs +++ b/scripts/publish-smoke-pack.mjs @@ -33,7 +33,7 @@ * a future exclusion cannot re-open the hole silently. * * Source of truth: the workspace itself. The Changesets `fixed` group in - * .changeset/config.json enumerates the same 111 names, but it is a DERIVED + * .changeset/config.json enumerates the same 110 names, but it is a DERIVED * declaration validated against the workspace by scripts/check-changeset-fixed.mjs * (which reddens both when a public package is missing from the group and * when a group name no longer exists), and it also holds the private members diff --git a/scripts/sdui-manifest.sources/components.json b/scripts/sdui-manifest.sources/components.json index f3006fdc5f9..d7d9de94974 100644 --- a/scripts/sdui-manifest.sources/components.json +++ b/scripts/sdui-manifest.sources/components.json @@ -1 +1 @@ -{"package":"ui/packages/components","sources":226,"sha256":"cd1d23767a76e6b4dcf2c0d4ca61b10ba27b336eeab990b22bfb0f7d10bb0424","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} +{"package":"ui/packages/components","sources":226,"sha256":"413692409d7961e0e4d6dc7bcc036daf32142e9a03dc54262ec56e5e125bf4de","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} diff --git a/scripts/sdui-manifest.sources/core.json b/scripts/sdui-manifest.sources/core.json index 5bf85fb5e8a..4859c08a7f2 100644 --- a/scripts/sdui-manifest.sources/core.json +++ b/scripts/sdui-manifest.sources/core.json @@ -1 +1 @@ -{"package":"ui/packages/core","sources":111,"sha256":"05fe45802fffcc482f95bad59a66279b70572f8757decd9770dc126f119dab2e","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} +{"package":"ui/packages/core","sources":111,"sha256":"a95eef804ab1ba7bd2c007ac65a7eeb451c8cb57cfef03469736165e380d77d1","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} diff --git a/scripts/sdui-manifest.sources/plugin-dashboard.json b/scripts/sdui-manifest.sources/plugin-dashboard.json index 5371ccddf7a..d94d857a78f 100644 --- a/scripts/sdui-manifest.sources/plugin-dashboard.json +++ b/scripts/sdui-manifest.sources/plugin-dashboard.json @@ -1 +1 @@ -{"package":"ui/packages/plugin-dashboard","sources":33,"sha256":"20c947f73887cf0051d66da0c9daca4045ead2401c7adf47a0b15cad9c51efc7","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} +{"package":"ui/packages/plugin-dashboard","sources":33,"sha256":"9e292419889abf0a070abd70b38c0d0f848ee391d8e038af2a5c9009559a2fa5","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} diff --git a/scripts/sdui-manifest.sources/sdui-parser.json b/scripts/sdui-manifest.sources/sdui-parser.json deleted file mode 100644 index d8c156cf0b9..00000000000 --- a/scripts/sdui-manifest.sources/sdui-parser.json +++ /dev/null @@ -1 +0,0 @@ -{"package":"ui/packages/sdui-parser","sources":9,"sha256":"08704fc188cb4749560f2a3faedd77b60820c1990f03311bcad21f283b2d7be4","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} diff --git a/scripts/sdui-manifest.sources/types.json b/scripts/sdui-manifest.sources/types.json index c264a9f0078..3e2c57be7f6 100644 --- a/scripts/sdui-manifest.sources/types.json +++ b/scripts/sdui-manifest.sources/types.json @@ -1 +1 @@ -{"package":"ui/packages/types","sources":81,"sha256":"d7928a68db8d76df5f05933d139f9d3d46e2fd87f74945f69c74e079f1a8eda2","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} +{"package":"ui/packages/types","sources":81,"sha256":"0f79353a0226cd7f299243ca1ec652770a0f0961b09ba77115d3bce4db3ec6d3","manifestSha256":"1f6e1ccb925a678eaa393f9091dfc7d0285e4e3626e0a5c61a5474687debabb7"} diff --git a/skills/objectui/README.md b/skills/objectui/README.md index 735c9c4c5a4..b1b646bd9e0 100644 --- a/skills/objectui/README.md +++ b/skills/objectui/README.md @@ -52,7 +52,7 @@ The published packages this skill covers. Nothing checks this list against the `packages/` tree, so read it as a snapshot rather than a guarantee: - **Core renderer:** `@object-ui/types`, `core`, `components`, `fields`, `layout`, `react` -- **Parsing & runtime:** `@object-ui/sdui-parser`, `react-runtime` +- **Parsing & runtime:** `@objectstack/sdui-parser`, `react-runtime` - **Integration:** `@object-ui/app-shell`, `providers`, `data-objectstack` - **Platform features:** `@object-ui/auth`, `permissions`, `i18n`, `mobile`, `collaboration` - **Plugins (19):** `plugin-{grid, list, detail, form, kanban, calendar, timeline, gantt, dashboard, report, charts, map, editor, markdown, view, tree, designer, ai, chatbot}` diff --git a/ui/.github/labeler.yml b/ui/.github/labeler.yml index 8b5fa60ee17..cf403e37463 100644 --- a/ui/.github/labeler.yml +++ b/ui/.github/labeler.yml @@ -74,10 +74,6 @@ - changed-files: - any-glob-to-any-file: 'packages/runner/**/*' -'package: sdui-parser': - - changed-files: - - any-glob-to-any-file: 'packages/sdui-parser/**/*' - # Plugins 'plugin': - changed-files: diff --git a/ui/QUICK_REFERENCE.md b/ui/QUICK_REFERENCE.md index e0a452d165d..808180493d3 100644 --- a/ui/QUICK_REFERENCE.md +++ b/ui/QUICK_REFERENCE.md @@ -87,7 +87,7 @@ pnpm changeset publish # Publish to npm (CI only) | Path | Purpose | | --- | --- | -| `packages/*` | 37 published packages (`@object-ui/*`), plus the private `vscode-extension`, `test-support` (test-only, never released) and `runner` (workspace demo app, never released) | +| `packages/*` | 36 published packages (`@object-ui/*`), plus the private `vscode-extension`, `test-support` (test-only, never released) and `runner` (workspace demo app, never released) | | `apps/console` | Full ObjectUI console app (Vite + React) | | `apps/site` | Public docs site at (fumadocs) | | `examples/*` | Runnable examples and the schema catalog — see [`examples/README.md`](./examples/README.md) | diff --git a/ui/README.md b/ui/README.md index cd47b895cd8..e0c2fa33e8b 100644 --- a/ui/README.md +++ b/ui/README.md @@ -424,7 +424,7 @@ Grouped by dependency weight — atoms and fields stay light, heavy widgets live | **[@object-ui/cli](./packages/cli)** | Scaffold, develop, build, and validate schema-driven apps | | **[@object-ui/create-plugin](./packages/create-plugin)** | Scaffold a new Object UI plugin | | **[@object-ui/runner](./packages/runner)** | Universal application runner for testing schemas (private workspace app, not published) | -| **[@object-ui/sdui-parser](./packages/sdui-parser)** | Constrained JSX source → schema tree compiler (parse, never execute) | +| **[@objectstack/sdui-parser](https://github.com/objectstack-ai/objectstack/tree/main/packages/sdui-parser)** | Constrained JSX source → schema tree compiler (parse, never execute) | | **[@object-ui/react-runtime](./packages/react-runtime)** | Trusted runtime execution for `kind: 'react'` pages | | **[vscode-extension](./packages/vscode-extension)** | IntelliSense and live preview for schema files | diff --git a/ui/apps/console/README.md b/ui/apps/console/README.md index a605aacd097..4c3e3603c81 100644 --- a/ui/apps/console/README.md +++ b/ui/apps/console/README.md @@ -35,7 +35,7 @@ pnpm test of the registry this build ships. A host serving `dist` registers it as its deployment manifest, and the metadata save door judges page sources against it (ADR-0080 §5). The step (`pnpm build:sdui-manifest`) runs after `vite build`, -calls `buildArtifacts` from `packages/sdui-parser/scripts/gen-manifest.ts` over +calls `buildArtifacts` from `apps/console/scripts/gen-manifest.ts` over the registry `dev/manifest-registry.ts` loads, and needs the workspace packages built first (turbo's `^build` does that). diff --git a/ui/apps/console/dev/manifest-dump.tsx b/ui/apps/console/dev/manifest-dump.tsx index 07d6e299cf6..8f807d9a8a4 100644 --- a/ui/apps/console/dev/manifest-dump.tsx +++ b/ui/apps/console/dev/manifest-dump.tsx @@ -7,7 +7,7 @@ * (objectui#11403), so this page and the shipped manifest load one list. */ import './manifest-registry'; import { ComponentRegistry } from '@object-ui/core'; -import { assertFullyLoaded, manifestFromConfigs } from '@object-ui/sdui-parser'; +import { assertFullyLoaded, manifestFromConfigs } from '@objectstack/sdui-parser'; import { nodeSlotsFor } from '@object-ui/types'; const out = document.getElementById('out')!; diff --git a/ui/apps/console/dev/manifest-registry.ts b/ui/apps/console/dev/manifest-registry.ts index 7369e1ae573..3f2128489a2 100644 --- a/ui/apps/console/dev/manifest-registry.ts +++ b/ui/apps/console/dev/manifest-registry.ts @@ -1,6 +1,6 @@ /* ADR-0080: the registration graph every SDUI manifest this app emits is read * from. Importing this module registers everything the console does, EAGERLY, - * which is the PREREQUISITE `packages/sdui-parser/scripts/gen-manifest.ts` + * which is the PREREQUISITE `apps/console/scripts/gen-manifest.ts` * states. It has two importers, so they read one list: * - `manifest-dump.tsx` — the browser dump page (`dev/manifest-dump.html`); * - `../scripts/build-sdui-manifest.ts` — the console build step that ships diff --git a/ui/apps/console/package.json b/ui/apps/console/package.json index 5988d19a7e1..677facb676a 100644 --- a/ui/apps/console/package.json +++ b/ui/apps/console/package.json @@ -120,6 +120,6 @@ "zod": "^4.6.5" }, "dependencies": { - "@object-ui/sdui-parser": "workspace:*" + "@objectstack/sdui-parser": "workspace:*" } } diff --git a/ui/apps/console/scripts/emit-sdui-manifest.ts b/ui/apps/console/scripts/emit-sdui-manifest.ts index 44b98ceaf21..493991c8562 100644 --- a/ui/apps/console/scripts/emit-sdui-manifest.ts +++ b/ui/apps/console/scripts/emit-sdui-manifest.ts @@ -2,7 +2,7 @@ * Write the console's SDUI component manifest into a build output directory as * `sdui.manifest.json` (objectui#11403). * - * The one generator is `packages/sdui-parser/scripts/gen-manifest.ts`: this + * The one generator is `apps/console/scripts/gen-manifest.ts`: this * calls its `buildArtifacts` and serializes nothing itself, so the shipped file * is byte for byte what that generator writes for the registry loaded in this * process. The caller registers that registry first — the generator's @@ -16,7 +16,7 @@ import { copyFileSync, mkdtempSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; -import { buildArtifacts } from '../../../packages/sdui-parser/scripts/gen-manifest.ts'; +import { buildArtifacts } from './gen-manifest.ts'; export const SDUI_MANIFEST_FILE = 'sdui.manifest.json'; diff --git a/ui/packages/sdui-parser/scripts/gen-manifest.ts b/ui/apps/console/scripts/gen-manifest.ts similarity index 96% rename from ui/packages/sdui-parser/scripts/gen-manifest.ts rename to ui/apps/console/scripts/gen-manifest.ts index a74fd7ce0cc..16e7247b79b 100644 --- a/ui/packages/sdui-parser/scripts/gen-manifest.ts +++ b/ui/apps/console/scripts/gen-manifest.ts @@ -23,7 +23,7 @@ import { writeFileSync } from 'node:fs'; import { ComponentRegistry } from '@object-ui/core'; import { nodeSlotsFor } from '@object-ui/types'; -import { assertFullyLoaded, generateBlockList, generateDts, manifestFromConfigs, type RegistryConfigLike } from '../src/index.js'; +import { assertFullyLoaded, generateBlockList, generateDts, manifestFromConfigs, type RegistryConfigLike } from '@objectstack/sdui-parser'; export function buildArtifacts(outDir: string): void { const configs = ComponentRegistry.getPublicConfigs() as unknown as RegistryConfigLike[]; diff --git a/ui/apps/console/src/__tests__/component-input-union-specimens.test.ts b/ui/apps/console/src/__tests__/component-input-union-specimens.test.ts index 1dc25aaf66f..787db85c8c6 100644 --- a/ui/apps/console/src/__tests__/component-input-union-specimens.test.ts +++ b/ui/apps/console/src/__tests__/component-input-union-specimens.test.ts @@ -61,8 +61,8 @@ */ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; import { ElementButtonPropsSchema, ElementTextInputPropsSchema, diff --git a/ui/apps/console/src/__tests__/element-data-source-input-injection.test.tsx b/ui/apps/console/src/__tests__/element-data-source-input-injection.test.tsx index 8e80e6af525..8bf83ba61e8 100644 --- a/ui/apps/console/src/__tests__/element-data-source-input-injection.test.tsx +++ b/ui/apps/console/src/__tests__/element-data-source-input-injection.test.tsx @@ -74,7 +74,7 @@ import { generateBlockList, generateDts, manifestFromConfigs, -} from '@object-ui/sdui-parser'; +} from '@objectstack/sdui-parser'; // The two graphs whose registrations this reads — the layout/content primitives // and the console's own plugin layer, from the module main.tsx boots from. Same diff --git a/ui/apps/console/src/__tests__/ga-honoured-inputs-author-reach.test.ts b/ui/apps/console/src/__tests__/ga-honoured-inputs-author-reach.test.ts index ef1d08e086c..56c71ac8345 100644 --- a/ui/apps/console/src/__tests__/ga-honoured-inputs-author-reach.test.ts +++ b/ui/apps/console/src/__tests__/ga-honoured-inputs-author-reach.test.ts @@ -43,8 +43,8 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; import '@object-ui/components'; import '../register-plugins'; diff --git a/ui/apps/console/src/__tests__/html-node-retired-13449.test.tsx b/ui/apps/console/src/__tests__/html-node-retired-13449.test.tsx index 1da604b79a3..d3d2bc7f36e 100644 --- a/ui/apps/console/src/__tests__/html-node-retired-13449.test.tsx +++ b/ui/apps/console/src/__tests__/html-node-retired-13449.test.tsx @@ -40,7 +40,7 @@ import { describe, it, expect } from 'vitest'; import { render, waitFor } from '@testing-library/react'; import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react'; import { ComponentRegistry } from '@object-ui/core'; -import { compile, manifestFromConfigs } from '@object-ui/sdui-parser'; +import { compile, manifestFromConfigs } from '@objectstack/sdui-parser'; // The full registration graph — the pair `dev/manifest-dump.tsx` builds the // published artifacts from, and the pair every live-path suite here reads. diff --git a/ui/apps/console/src/__tests__/html-tier-code-element-10756.test.tsx b/ui/apps/console/src/__tests__/html-tier-code-element-10756.test.tsx index 6d2c4530eed..2cec6777d79 100644 --- a/ui/apps/console/src/__tests__/html-tier-code-element-10756.test.tsx +++ b/ui/apps/console/src/__tests__/html-tier-code-element-10756.test.tsx @@ -42,7 +42,7 @@ import { describe, it, expect } from 'vitest'; import { render, waitFor } from '@testing-library/react'; import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react'; import { ComponentRegistry } from '@object-ui/core'; -import { compile, manifestFromConfigs } from '@object-ui/sdui-parser'; +import { compile, manifestFromConfigs } from '@objectstack/sdui-parser'; // The full registration graph — the pair `dev/manifest-dump.tsx` builds the // published artifacts from, and the pair every live-path suite here reads. diff --git a/ui/apps/console/src/__tests__/html-tier-manifest.test.ts b/ui/apps/console/src/__tests__/html-tier-manifest.test.ts index 2b5bdb3df19..dde642cebf8 100644 --- a/ui/apps/console/src/__tests__/html-tier-manifest.test.ts +++ b/ui/apps/console/src/__tests__/html-tier-manifest.test.ts @@ -38,7 +38,7 @@ import { readFileSync } from 'node:fs'; import { createRequire } from 'node:module'; import { dirname, join } from 'node:path'; import { LayoutSchema } from '@object-ui/types/zod'; -import { compile, generateDts, manifestFromConfigs, type RegistryConfigLike } from '@object-ui/sdui-parser'; +import { compile, generateDts, manifestFromConfigs, type RegistryConfigLike } from '@objectstack/sdui-parser'; import { ComponentRegistry, HTML_TIER_INTRINSICS, @@ -51,7 +51,7 @@ import '@object-ui/components'; // … and the console's own plugin layer, from the module main.tsx boots from. import '../register-plugins'; -/** Exactly the generators' read (`dev/manifest-dump.tsx`, `sdui-parser/scripts/gen-manifest.ts`). */ +/** Exactly the generators' read (`dev/manifest-dump.tsx`, `apps/console/scripts/gen-manifest.ts`). */ const manifest = manifestFromConfigs(ComponentRegistry.getPublicConfigs() as unknown as RegistryConfigLike[]); /** What a consumer reads back off disk — `JSON.stringify` drops every `undefined`. */ const published = JSON.parse(JSON.stringify(manifest)) as typeof manifest; diff --git a/ui/apps/console/src/__tests__/htmlTierDataSourceInputDeclaration-6678.test.tsx b/ui/apps/console/src/__tests__/htmlTierDataSourceInputDeclaration-6678.test.tsx index 98a490a58d5..2f2f6a2eb7f 100644 --- a/ui/apps/console/src/__tests__/htmlTierDataSourceInputDeclaration-6678.test.tsx +++ b/ui/apps/console/src/__tests__/htmlTierDataSourceInputDeclaration-6678.test.tsx @@ -57,7 +57,7 @@ * It lives in `apps/console` rather than beside `plugin-list` for the reason the * suites next door already state: the claim is about what an AUTHOR is told, and * that answer is produced from the whole registration graph plus the real - * `object-grid`. `plugin-list` depends on neither `@object-ui/sdui-parser` nor + * `object-grid`. `plugin-list` depends on neither `@objectstack/sdui-parser` nor * `@object-ui/plugin-grid`, and taking those dependencies to host a test would * be the heavier change. */ @@ -65,8 +65,8 @@ import { describe, it, expect } from 'vitest'; import { render, waitFor } from '@testing-library/react'; import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react'; import { ComponentRegistry } from '@object-ui/core'; -import { compile, manifestFromConfigs } from '@object-ui/sdui-parser'; -import type { Diagnostic } from '@object-ui/sdui-parser'; +import { compile, manifestFromConfigs } from '@objectstack/sdui-parser'; +import type { Diagnostic } from '@objectstack/sdui-parser'; // The full registration graph — the same pair `dev/manifest-dump.tsx` builds the // published artifacts from, and the pair every live-path suite in this directory diff --git a/ui/apps/console/src/__tests__/i18nLabelInputsManifest-10993.test.ts b/ui/apps/console/src/__tests__/i18nLabelInputsManifest-10993.test.ts index e0c35d472e3..84c062ca2a2 100644 --- a/ui/apps/console/src/__tests__/i18nLabelInputsManifest-10993.test.ts +++ b/ui/apps/console/src/__tests__/i18nLabelInputsManifest-10993.test.ts @@ -35,8 +35,8 @@ */ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; import '@object-ui/components'; import '../register-plugins'; diff --git a/ui/apps/console/src/__tests__/line-items-bound-child-object-11569.test.ts b/ui/apps/console/src/__tests__/line-items-bound-child-object-11569.test.ts index 2705b2d774f..88ad66e5e23 100644 --- a/ui/apps/console/src/__tests__/line-items-bound-child-object-11569.test.ts +++ b/ui/apps/console/src/__tests__/line-items-bound-child-object-11569.test.ts @@ -22,7 +22,7 @@ * * The manifest has no vocabulary for "one of these is required" * (`ComponentInput` in `@object-ui/types` and `ManifestInput` in - * `@object-ui/sdui-parser` carry a boolean `required` and nothing beside it), so + * `@objectstack/sdui-parser` carry a boolean `required` and nothing beside it), so * the registration stops requiring the key (triage ruling on objectui#11569). * * Judged against the manifest the console SHIPS: `emitSduiManifest` over the @@ -53,7 +53,7 @@ import { mkdtempSync, readFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterAll, describe, expect, it } from 'vitest'; -import { compile, type Manifest } from '@object-ui/sdui-parser'; +import { compile, type Manifest } from '@objectstack/sdui-parser'; // Module scope, not a hook: the whole registration graph loads at import time. import '../../dev/manifest-registry'; import { emitSduiManifest } from '../../scripts/emit-sdui-manifest'; diff --git a/ui/apps/console/src/__tests__/masterDetailFormI18nLabelManifest.test.ts b/ui/apps/console/src/__tests__/masterDetailFormI18nLabelManifest.test.ts index 8fd735a260c..d1a27fe3efb 100644 --- a/ui/apps/console/src/__tests__/masterDetailFormI18nLabelManifest.test.ts +++ b/ui/apps/console/src/__tests__/masterDetailFormI18nLabelManifest.test.ts @@ -25,8 +25,8 @@ */ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; import '@object-ui/components'; import '../register-plugins'; diff --git a/ui/apps/console/src/__tests__/masterDetailFormTypeManifest.test.ts b/ui/apps/console/src/__tests__/masterDetailFormTypeManifest.test.ts index 915b4004317..78860c8d200 100644 --- a/ui/apps/console/src/__tests__/masterDetailFormTypeManifest.test.ts +++ b/ui/apps/console/src/__tests__/masterDetailFormTypeManifest.test.ts @@ -28,8 +28,8 @@ import { describe, it, expect } from 'vitest'; // during collection, which no test or hook timeout clocks (AGENTS.md, flaky-test discipline). import '@objectstack/spec/ui'; import { ComponentRegistry } from '@object-ui/core'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; import '@object-ui/components'; import '../register-plugins'; diff --git a/ui/apps/console/src/__tests__/objectFormI18nLabelManifest.test.ts b/ui/apps/console/src/__tests__/objectFormI18nLabelManifest.test.ts index e088f84b645..17b01f74197 100644 --- a/ui/apps/console/src/__tests__/objectFormI18nLabelManifest.test.ts +++ b/ui/apps/console/src/__tests__/objectFormI18nLabelManifest.test.ts @@ -25,8 +25,8 @@ */ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; import '@object-ui/components'; import '../register-plugins'; diff --git a/ui/apps/console/src/__tests__/objectname-binding-required-11605.test.ts b/ui/apps/console/src/__tests__/objectname-binding-required-11605.test.ts index cf862234245..af8c5527038 100644 --- a/ui/apps/console/src/__tests__/objectname-binding-required-11605.test.ts +++ b/ui/apps/console/src/__tests__/objectname-binding-required-11605.test.ts @@ -12,7 +12,7 @@ * spec row requires it. The family's closing card, after objectui#11569 * (`record:line_items` and `childObject`). * - * The page compile (`compile()` in `@object-ui/sdui-parser`, whose `ok` is the + * The page compile (`compile()` in `@objectstack/sdui-parser`, whose `ok` is the * save gate) reads each registration's `inputs`, and its only requiredness * check is `input.required && !(input.name in node)`. So a registration that * declared `{ name: 'objectName', required: true }` refused a node whose @@ -56,7 +56,7 @@ import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { afterAll, describe, expect, it } from 'vitest'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { compile, type Manifest, type ManifestInput } from '@object-ui/sdui-parser'; +import { compile, type Manifest, type ManifestInput } from '@objectstack/sdui-parser'; // Module scope, not a hook: the whole registration graph loads at import time. import '../../dev/manifest-registry'; import { emitSduiManifest } from '../../scripts/emit-sdui-manifest'; diff --git a/ui/apps/console/src/__tests__/public-contract.test.ts b/ui/apps/console/src/__tests__/public-contract.test.ts index 5a433adcb0e..a611abe27ff 100644 --- a/ui/apps/console/src/__tests__/public-contract.test.ts +++ b/ui/apps/console/src/__tests__/public-contract.test.ts @@ -443,7 +443,7 @@ describe('PUBLIC_BLOCKS ↔ console coverage (reverse direction)', () => { * compiler's manifest from `getKnownTypes()` — registry-wide, no roster in it — * and `validateTree` judges authored pages against that. What this roster gates * is `getPublicConfigs()`: the curated AI-authoring vocabulary, and through - * `sdui-parser/scripts/gen-manifest.ts` the published `sdui.manifest.json` and + * `apps/console/scripts/gen-manifest.ts` the published `sdui.manifest.json` and * `sdui-intrinsics.d.ts`. That is the surface `box` was missing from — narrower, * and true. */ diff --git a/ui/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts b/ui/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts index 3389542e94f..9a7eaf48ae4 100644 --- a/ui/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts +++ b/ui/apps/console/src/__tests__/registry-inputs-spec-parity.test.ts @@ -28,7 +28,7 @@ * published authoring surface, and four layers are silent about a key that only * exists there: * - * 1. `packages/sdui-parser/scripts/gen-manifest.ts` serializes `inputs` into + * 1. `apps/console/scripts/gen-manifest.ts` serializes `inputs` into * `sdui.manifest.json` (the save-gate + parser whitelist) and into * `sdui-intrinsics.d.ts` (the JSX authoring type surface), so both declare * the key legal; @@ -250,7 +250,7 @@ import { PageComponentType, RETIRED_PAGE_COMPONENT_TYPES, } from '@objectstack/spec/ui'; -import { MANIFEST_INPUT_TYPES, inputTypeArms } from '@object-ui/sdui-parser'; +import { MANIFEST_INPUT_TYPES, inputTypeArms } from '@objectstack/sdui-parser'; import type { ComponentInput } from '@object-ui/types'; import { authorableShapeKeys, diff --git a/ui/apps/console/src/__tests__/related-list-columns-optional-11613.test.ts b/ui/apps/console/src/__tests__/related-list-columns-optional-11613.test.ts index cd48d1c3bbe..3bbe08404c1 100644 --- a/ui/apps/console/src/__tests__/related-list-columns-optional-11613.test.ts +++ b/ui/apps/console/src/__tests__/related-list-columns-optional-11613.test.ts @@ -15,7 +15,7 @@ * from the related object's highlightFields / default list columns"). The * registration in `@object-ui/plugin-detail` still declared * `{ name: 'columns', required: true }`, and the page compile (`compile()` in - * `@object-ui/sdui-parser`, whose `ok` is the save gate) reads the + * `@objectstack/sdui-parser`, whose `ok` is the save gate) reads the * registration, so it refused two nodes the row and the renderer accept: * * - a node whose `dataSource` binding names a VIEW, which supplies the view's @@ -52,7 +52,7 @@ import { mkdtempSync, readFileSync, rmSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { afterAll, describe, expect, it } from 'vitest'; -import { compile, type Manifest } from '@object-ui/sdui-parser'; +import { compile, type Manifest } from '@objectstack/sdui-parser'; // Module scope, not a hook: the whole registration graph loads at import time. import '../../dev/manifest-registry'; import { emitSduiManifest } from '../../scripts/emit-sdui-manifest'; diff --git a/ui/apps/console/src/__tests__/sdui-intrinsics-compile-11075.test.ts b/ui/apps/console/src/__tests__/sdui-intrinsics-compile-11075.test.ts index cd02df58a54..a484d40c125 100644 --- a/ui/apps/console/src/__tests__/sdui-intrinsics-compile-11075.test.ts +++ b/ui/apps/console/src/__tests__/sdui-intrinsics-compile-11075.test.ts @@ -17,7 +17,7 @@ * the eager plugin imports) and its `assertFullyLoaded` are the ones this file * reads, so a plugin the dump gains or loses moves this pin with it. The * `.d.ts` is then generated from that manifest exactly as `buildArtifacts` in - * `packages/sdui-parser/scripts/gen-manifest.ts` generates + * `apps/console/scripts/gen-manifest.ts` generates * `sdui-intrinsics.d.ts`. * * Before the fix the generated file itself did not compile: @@ -39,7 +39,7 @@ import { describe, expect, it } from 'vitest'; import ts from 'typescript'; -import { generateDts, type Manifest } from '@object-ui/sdui-parser'; +import { generateDts, type Manifest } from '@objectstack/sdui-parser'; // `manifest-dump.tsx` is the page script of `dev/manifest-dump.html`: it writes // the manifest (or the generator's error) into `#out` and onto `window`. diff --git a/ui/apps/console/src/__tests__/sdui-manifest-build-11403.test.ts b/ui/apps/console/src/__tests__/sdui-manifest-build-11403.test.ts index 667d52ea1f7..069899eede4 100644 --- a/ui/apps/console/src/__tests__/sdui-manifest-build-11403.test.ts +++ b/ui/apps/console/src/__tests__/sdui-manifest-build-11403.test.ts @@ -20,7 +20,7 @@ * the published `dist`, while `buildArtifacts` itself writes three files * (the control that makes "nothing else" a reading). * 3. ONE GENERATOR — its bytes are the bytes `buildArtifacts` in - * `packages/sdui-parser/scripts/gen-manifest.ts` writes for the same + * `apps/console/scripts/gen-manifest.ts` writes for the same * registry. * 4. THE SHAPE THE HOST READS — `isUsableSduiManifest` accepts a value only * when it and its `components` are each a non-array object; the record is @@ -37,7 +37,7 @@ import { fileURLToPath } from 'node:url'; import { afterAll, describe, expect, it } from 'vitest'; // Module scope, not a hook: the whole registration graph loads at import time. import '../../dev/manifest-registry'; -import { buildArtifacts } from '../../../../packages/sdui-parser/scripts/gen-manifest'; +import { buildArtifacts } from '../../scripts/gen-manifest'; import { SDUI_MANIFEST_FILE, emitSduiManifest } from '../../scripts/emit-sdui-manifest'; const CONSOLE_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..'); diff --git a/ui/apps/console/src/__tests__/sdui-preview-page-source-styling.test.ts b/ui/apps/console/src/__tests__/sdui-preview-page-source-styling.test.ts index 3a01054caa2..f4c0f48cd28 100644 --- a/ui/apps/console/src/__tests__/sdui-preview-page-source-styling.test.ts +++ b/ui/apps/console/src/__tests__/sdui-preview-page-source-styling.test.ts @@ -26,7 +26,7 @@ */ import { describe, it, expect } from 'vitest'; import { validatePageSourceStyling, PAGE_SOURCE_CLASSNAME } from '@objectstack/lint'; -import { parseJsx } from '@object-ui/sdui-parser'; +import { parseJsx } from '@objectstack/sdui-parser'; /** * The enumeration and the page-source extractor both live in the helper, so * this file and `sdui-preview-page-source-query-params.test.ts` cannot disagree diff --git a/ui/apps/console/vite.config.ts b/ui/apps/console/vite.config.ts index 3422ecc4f0f..8e8b59151d4 100644 --- a/ui/apps/console/vite.config.ts +++ b/ui/apps/console/vite.config.ts @@ -620,7 +620,7 @@ const workspaceAliases: Record = { '@object-ui/components': path.resolve(import.meta.dirname, '../../packages/components/src'), '@object-ui/core': path.resolve(import.meta.dirname, '../../packages/core/src'), '@object-ui/react-runtime': path.resolve(import.meta.dirname, '../../packages/react-runtime/src'), - '@object-ui/sdui-parser': path.resolve(import.meta.dirname, '../../packages/sdui-parser/src'), + '@objectstack/sdui-parser': path.resolve(import.meta.dirname, '../../../packages/sdui-parser/src'), '@object-ui/fields': path.resolve(import.meta.dirname, '../../packages/fields/src'), '@object-ui/layout': path.resolve(import.meta.dirname, '../../packages/layout/src'), '@object-ui/plugin-dashboard': path.resolve(import.meta.dirname, '../../packages/plugin-dashboard/src'), diff --git a/ui/content/docs/api/schema-reference.md b/ui/content/docs/api/schema-reference.md index ecc3c5b6c0b..4c2a0bc0648 100644 --- a/ui/content/docs/api/schema-reference.md +++ b/ui/content/docs/api/schema-reference.md @@ -1033,7 +1033,7 @@ A drag-and-drop Kanban board. The `object-kanban` type key validates the shape t > `groupField` is refused by name (objectui#7322): the renderer reads `groupBy`. -> **`quickAdd` and `allowCollapse` were rows of the table above and are not authorable on this board.** `allowCollapse` is **refused by name** by the strict authoring face, which does not declare it at all — a document carrying it fails validation rather than merely going unread, and `@object-ui/plugin-kanban` has no read site for it. `quickAdd` is **retired** on this board (objectui#8285, director seat, decision batch 91): the Quick Add control is gated on an `onQuickAdd` handler as well, and no `object-kanban` path supplies one, so the key never drew anything, and the ruling retired it rather than add inline record creation. The strict face tombstones it (refused by name), `ObjectKanban` no longer forwards it, and `@object-ui/sdui-parser` answers it with the ordinary `unknown-prop` warning. `@object-ui/types` mirrors this face and agrees on both: each is `never` on its TypeScript face and a retirement tombstone on its Zod face (objectui#8801, objectui#8285). The Quick Add pair itself lives on the `KanbanRenderer` component, which a React host mounts directly. +> **`quickAdd` and `allowCollapse` were rows of the table above and are not authorable on this board.** `allowCollapse` is **refused by name** by the strict authoring face, which does not declare it at all — a document carrying it fails validation rather than merely going unread, and `@object-ui/plugin-kanban` has no read site for it. `quickAdd` is **retired** on this board (objectui#8285, director seat, decision batch 91): the Quick Add control is gated on an `onQuickAdd` handler as well, and no `object-kanban` path supplies one, so the key never drew anything, and the ruling retired it rather than add inline record creation. The strict face tombstones it (refused by name), `ObjectKanban` no longer forwards it, and `@objectstack/sdui-parser` answers it with the ordinary `unknown-prop` warning. `@object-ui/types` mirrors this face and agrees on both: each is `never` on its TypeScript face and a retirement tombstone on its Zod face (objectui#8801, objectui#8285). The Quick Add pair itself lives on the `KanbanRenderer` component, which a React host mounts directly. > `columns` is declared on this face since objectui#8913, as the pair of array shapes `@objectstack/spec` declares — an array of `{ id, title }` lanes, **or** an array of bare value strings. A **mixed** array is refused: the renderer decides which shape it has from the first element alone, so a mix yields a blank lane and mis-bucketed cards. A lane accepts `id`, `title`, `cards`, `limit`, `className` and `collapsed`, which are the members the board implementations read; `id` is a **string** — the authored face keeps that narrowing, and since objectui#8993 a non-string lane id no longer renders every card twice: the bucketer's leftover sweep keys membership the way the injection already did (a lane `1` takes the group `'1'`). When a lane carries `cards`, each card is judged — a card with no `title` is refused. An undeclared lane key is accepted and dropped, not refused, which is this tolerant face's posture; the strict authoring face refuses it by name. > diff --git a/ui/content/docs/plugins/plugin-dashboard.mdx b/ui/content/docs/plugins/plugin-dashboard.mdx index c754d1aaba9..36ef292b26d 100644 --- a/ui/content/docs/plugins/plugin-dashboard.mdx +++ b/ui/content/docs/plugins/plugin-dashboard.mdx @@ -377,7 +377,7 @@ If a caption under a metric's value returns, it returns as a declared widget-level key, not as an `options` key. Every other `options` key on a dataset-bound widget **reaches no renderer**. -The authoring validator (`validateTree` in `@object-ui/sdui-parser`) reports +The authoring validator (`validateTree` in `@objectstack/sdui-parser`) reports each one as a `unconsumed-widget-option` **warning** naming the set above — the document still parses, saves and renders; open extras stay open, they just stop being silent. A widget with a genuine out-of-band consumer can opt out in diff --git a/ui/content/docs/plugins/plugin-kanban.mdx b/ui/content/docs/plugins/plugin-kanban.mdx index 04c3ddfd8f8..2b3285ae579 100644 --- a/ui/content/docs/plugins/plugin-kanban.mdx +++ b/ui/content/docs/plugins/plugin-kanban.mdx @@ -532,7 +532,7 @@ objectui#11234. The board does not create records inline. Both that takes the pair only as explicit props, and it supplies neither — so even a host that hands the board both halves of the pair gets no control. A page written as constrained JSX draws the ordinary `unknown-prop` **warning** for -`quickAdd` (`validateTree` in `@object-ui/sdui-parser`), since the block has no +`quickAdd` (`validateTree` in `@objectstack/sdui-parser`), since the block has no such prop. Delete both keys; to offer Quick Add, mount `KanbanRenderer` as above. ## Bundle Size diff --git a/ui/eslint.config.js b/ui/eslint.config.js index b8bea1bbed4..b5c4abc3012 100644 --- a/ui/eslint.config.js +++ b/ui/eslint.config.js @@ -384,7 +384,7 @@ export default tseslint.config({ // `assertionStyle: 'never'` is a much larger decision with its own decision // box and is NOT this ratchet (objectui#8316); so is the pre-existing // sibling `as unknown as RegistryConfigLike[]` in - // `packages/sdui-parser/scripts/gen-manifest.ts`, which this scope does not + // `apps/console/scripts/gen-manifest.ts`, which this scope does not // reach. `Registry.ts` carries zero type assertions today, so this lints // clean with no allowlist. files: ['packages/core/src/registry/Registry.ts'], diff --git a/ui/examples/console-starter/README.md b/ui/examples/console-starter/README.md index 0c28cf2e823..45f399287d9 100644 --- a/ui/examples/console-starter/README.md +++ b/ui/examples/console-starter/README.md @@ -66,7 +66,7 @@ pnpm dev # Vite; no server.port is set, so the default 5173 **`pnpm dev` needs no prior build.** Every workspace package the app reaches is on the alias list, so Vite serves all of them from `src`. This was not always true: five packages the aliased sources import — `@object-ui/mobile`, -`@object-ui/providers`, `@object-ui/sdui-parser`, `@object-ui/plugin-editor`, +`@object-ui/providers`, `@objectstack/sdui-parser`, `@object-ui/plugin-editor`, `@object-ui/react-runtime` — were missing from the list, fell back to node resolution onto `packages/*/dist`, and produced 500s behind an empty `#root` with nothing on the page to say why (objectui#3528). diff --git a/ui/examples/console-starter/package.json b/ui/examples/console-starter/package.json index 9c6c2626d66..aefd146dc7c 100644 --- a/ui/examples/console-starter/package.json +++ b/ui/examples/console-starter/package.json @@ -33,7 +33,7 @@ "@object-ui/plugin-view": "workspace:*", "@object-ui/providers": "workspace:*", "@object-ui/react-runtime": "workspace:*", - "@object-ui/sdui-parser": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "react": "19.3.0", "react-dom": "19.3.0", "react-router-dom": "^7.18.2", diff --git a/ui/examples/console-starter/vite.config.ts b/ui/examples/console-starter/vite.config.ts index 11467d364be..fd2fb1b042f 100644 --- a/ui/examples/console-starter/vite.config.ts +++ b/ui/examples/console-starter/vite.config.ts @@ -51,7 +51,7 @@ const workspaceAliases: Record = { // anything scanning this file, and that decoy already fooled one scan.) '@object-ui/react': path.resolve(__dirname, '../../packages/react/src'), '@object-ui/react-runtime': path.resolve(__dirname, '../../packages/react-runtime/src'), - '@object-ui/sdui-parser': path.resolve(__dirname, '../../packages/sdui-parser/src'), + '@objectstack/sdui-parser': path.resolve(__dirname, '../../../packages/sdui-parser/src'), // Subpath before bare package — see the note in `apps/console/vite.config.ts`. '@object-ui/types/zod': path.resolve(__dirname, '../../packages/types/src/zod/index.zod.ts'), '@object-ui/types': path.resolve(__dirname, '../../packages/types/src'), diff --git a/ui/examples/schema-catalog/package.json b/ui/examples/schema-catalog/package.json index 5556e52300f..7ab29b01c42 100644 --- a/ui/examples/schema-catalog/package.json +++ b/ui/examples/schema-catalog/package.json @@ -48,8 +48,8 @@ "@object-ui/plugin-timeline": "workspace:*", "@object-ui/plugin-view": "workspace:*", "@object-ui/react": "workspace:*", - "@object-ui/sdui-parser": "workspace:*", "@object-ui/test-support": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "typescript": "^6.0.3" } } diff --git a/ui/examples/schema-catalog/test/pageheader-with-actions.test.tsx b/ui/examples/schema-catalog/test/pageheader-with-actions.test.tsx index 349255d386f..1f47229136f 100644 --- a/ui/examples/schema-catalog/test/pageheader-with-actions.test.tsx +++ b/ui/examples/schema-catalog/test/pageheader-with-actions.test.tsx @@ -52,8 +52,8 @@ import '@object-ui/components'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer } from '@object-ui/react'; import { registerLayout } from '@object-ui/layout'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; import { safeValidateSchema } from '@object-ui/types/zod'; import { ComponentPropsMap, PageComponentSchema } from '@objectstack/spec/ui'; import { getExample } from '../src/index.js'; diff --git a/ui/packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-types.ts b/ui/packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-types.ts index be737d6ee4c..6aeb824d87a 100644 --- a/ui/packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-types.ts +++ b/ui/packages/app-shell/src/views/metadata-admin/previews/simulator/flow-sim-types.ts @@ -124,7 +124,7 @@ export type DiagnosticLevel = 'error' | 'warning'; * One finding of the client-side structural check (`validateFlowDraft`, * `missingNodeRefDiagnostics`), anchored on a node, an edge or a cycle. * - * Named `FlowSimDiagnostic`, not `Diagnostic`: `@object-ui/sdui-parser` + * Named `FlowSimDiagnostic`, not `Diagnostic`: `@objectstack/sdui-parser` * publishes `Diagnostic` for a parser finding (`severity` + `code`), a * different thing, and one exported name has one authority (objectui#6349, * batch 6). diff --git a/ui/packages/cli/src/__tests__/workspace-vite.test.ts b/ui/packages/cli/src/__tests__/workspace-vite.test.ts index 1571da94b87..db91346bd93 100644 --- a/ui/packages/cli/src/__tests__/workspace-vite.test.ts +++ b/ui/packages/cli/src/__tests__/workspace-vite.test.ts @@ -143,17 +143,19 @@ describe('workspace alias table (objectui#3890)', () => { } }); - it('aliases the six the dependency scan could not reach past them', () => { + it('aliases the five the dependency scan could not reach past them', () => { // A Vite dependency scan reports only the first layer it fails on, so the - // card's list of four was the visible half. These six sit behind them in - // the same import graph and would have stayed broken under a backfill. + // card's list of four was the visible half. These sat behind them in the + // same import graph and would have stayed broken under a backfill. The + // sixth, `@object-ui/sdui-parser`, was retired for `@objectstack/sdui-parser` + // at the repo root (objectstack#25712 item 3), which resolves through its + // built `dist` like `@objectstack/spec` and is outside this table. for (const name of [ '@object-ui/data-objectstack', '@object-ui/i18n', '@object-ui/plugin-map', '@object-ui/providers', - '@object-ui/react-runtime', - '@object-ui/sdui-parser' + '@object-ui/react-runtime' ]) { expect(aliases[name]).toBeDefined(); } diff --git a/ui/packages/cli/src/commands/doctor.ts b/ui/packages/cli/src/commands/doctor.ts index 3702bb7a817..77407cf2f36 100644 --- a/ui/packages/cli/src/commands/doctor.ts +++ b/ui/packages/cli/src/commands/doctor.ts @@ -65,7 +65,7 @@ export type DoctorDiagnosticLevel = 'ok' | 'warn' | 'error'; /** * One `objectui doctor` check result. * - * `Doctor`-prefixed, like its level: `@object-ui/sdui-parser` publishes + * `Doctor`-prefixed, like its level: `@objectstack/sdui-parser` publishes * `Diagnostic` for a parser finding, and the flow designer's `DiagnosticLevel` * is `'error' | 'warning'` with no `ok` verdict — different things, and one * exported name has one authority (objectui#6349, batch 6). diff --git a/ui/packages/components/package.json b/ui/packages/components/package.json index 1858636b9af..be6fe1a8f74 100644 --- a/ui/packages/components/package.json +++ b/ui/packages/components/package.json @@ -37,8 +37,8 @@ "@object-ui/i18n": "workspace:*", "@object-ui/react": "workspace:*", "@object-ui/react-runtime": "workspace:*", - "@object-ui/sdui-parser": "workspace:*", "@object-ui/types": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@objectstack/spec": "workspace:^", "@radix-ui/react-accordion": "^1.2.20", "@radix-ui/react-alert-dialog": "^1.1.23", diff --git a/ui/packages/components/src/__tests__/div-deprecation-provenance.test.tsx b/ui/packages/components/src/__tests__/div-deprecation-provenance.test.tsx index 8596573eca5..2c435e94b2d 100644 --- a/ui/packages/components/src/__tests__/div-deprecation-provenance.test.tsx +++ b/ui/packages/components/src/__tests__/div-deprecation-provenance.test.tsx @@ -59,7 +59,7 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; import { render } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer } from '@object-ui/react'; -import { compile, isHtmlTierNode, manifestFromConfigs } from '@object-ui/sdui-parser'; +import { compile, isHtmlTierNode, manifestFromConfigs } from '@objectstack/sdui-parser'; // Registers the renderers at module scope, NOT inside a `beforeAll` — there the // cold transform is billed to `hookTimeout`. See // object-ui/no-dynamic-import-in-test-hook (objectui#3010/#3021). diff --git a/ui/packages/components/src/__tests__/grid-flat-columns-retired-11505.test.tsx b/ui/packages/components/src/__tests__/grid-flat-columns-retired-11505.test.tsx index 2f1edc51c6e..efa7c2c75fc 100644 --- a/ui/packages/components/src/__tests__/grid-flat-columns-retired-11505.test.tsx +++ b/ui/packages/components/src/__tests__/grid-flat-columns-retired-11505.test.tsx @@ -45,8 +45,8 @@ import { render } from '@testing-library/react'; import '../renderers'; import { SchemaRenderer } from '@object-ui/react'; import { ComponentRegistry } from '@object-ui/core'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; const FLAT_KEYS = ['smColumns', 'mdColumns', 'lgColumns', 'xlColumns'] as const; diff --git a/ui/packages/components/src/__tests__/layout-containers-declare-containment.test.tsx b/ui/packages/components/src/__tests__/layout-containers-declare-containment.test.tsx index da4f4f1dea9..c24a065f388 100644 --- a/ui/packages/components/src/__tests__/layout-containers-declare-containment.test.tsx +++ b/ui/packages/components/src/__tests__/layout-containers-declare-containment.test.tsx @@ -47,8 +47,8 @@ import { describe, it, expect } from 'vitest'; import { render, waitFor } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer, AdapterCtx } from '@object-ui/react'; -import { CHILD_LIST_KEY, manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { CHILD_LIST_KEY, manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md // §测试纪律 — an unbounded module load must not be billed to a bounded window). diff --git a/ui/packages/components/src/__tests__/record-picker-inputs-spec-parity.test.ts b/ui/packages/components/src/__tests__/record-picker-inputs-spec-parity.test.ts index ec1d344fbc3..2355f2834ea 100644 --- a/ui/packages/components/src/__tests__/record-picker-inputs-spec-parity.test.ts +++ b/ui/packages/components/src/__tests__/record-picker-inputs-spec-parity.test.ts @@ -34,7 +34,7 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; -import { compile, manifestFromConfigs } from '@object-ui/sdui-parser'; +import { compile, manifestFromConfigs } from '@objectstack/sdui-parser'; import { ElementRecordPickerPropsSchema } from '@objectstack/spec/ui'; // Module scope, not a hook: the cold transform is billed to the import phase, // which has no test/hook timeout (AGENTS.md §测试纪律, objectui#3010). diff --git a/ui/packages/components/src/__tests__/toast-sonner-button-variant-inputs-7316.test.ts b/ui/packages/components/src/__tests__/toast-sonner-button-variant-inputs-7316.test.ts index 81ffaf9cc63..2aca0376a6b 100644 --- a/ui/packages/components/src/__tests__/toast-sonner-button-variant-inputs-7316.test.ts +++ b/ui/packages/components/src/__tests__/toast-sonner-button-variant-inputs-7316.test.ts @@ -52,7 +52,7 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; import { SonnerSchema as SonnerMirror, ToastSchema as ToastMirror } from '@object-ui/types/zod'; // Module scope, not a hook: the cold transform is billed to the import phase, // which has no test/hook timeout (AGENTS.md §测试纪律, objectui#3010). diff --git a/ui/packages/components/src/renderers/__tests__/base-props-one-list-11044.test.tsx b/ui/packages/components/src/renderers/__tests__/base-props-one-list-11044.test.tsx index 176434fecba..4bf5f51d34f 100644 --- a/ui/packages/components/src/renderers/__tests__/base-props-one-list-11044.test.tsx +++ b/ui/packages/components/src/renderers/__tests__/base-props-one-list-11044.test.tsx @@ -9,7 +9,7 @@ /** * The parser tier's base props, held on the live registry (objectui#11044). * - * `SDUI_BASE_PROPS` (`@object-ui/sdui-parser`) is the one list both + * `SDUI_BASE_PROPS` (`@objectstack/sdui-parser`) is the one list both * `validateTree` and the generated JSX types read. This file holds it against * the two things it answers to: * @@ -36,8 +36,8 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; import { BaseSchema } from '@object-ui/types/zod'; -import { inputTypeArms, manifestFromConfigs, SDUI_BASE_PROPS, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, Manifest, SchemaElement } from '@object-ui/sdui-parser'; +import { inputTypeArms, manifestFromConfigs, SDUI_BASE_PROPS, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, Manifest, SchemaElement } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md // §测试纪律 — an unbounded module load must not be billed to a bounded window). diff --git a/ui/packages/components/src/renderers/__tests__/bind-base-prop-parser-tier-11008.test.tsx b/ui/packages/components/src/renderers/__tests__/bind-base-prop-parser-tier-11008.test.tsx index 6cd8d6e0e55..96636676970 100644 --- a/ui/packages/components/src/renderers/__tests__/bind-base-prop-parser-tier-11008.test.tsx +++ b/ui/packages/components/src/renderers/__tests__/bind-base-prop-parser-tier-11008.test.tsx @@ -11,7 +11,7 @@ * (objectui#11008). * * `BaseSchema` declares both keys for every node. Until objectui#11008, - * `validateTree` (`@object-ui/sdui-parser`) did not count either among its base + * `validateTree` (`@objectstack/sdui-parser`) did not count either among its base * props and no registration declares either as an input, so the undeclared-key * branch answered every authored one with `unknown-prop` — on `list` and * `tree-view` too, whose renderers read `bind` through `useDataScope`, and on @@ -35,8 +35,8 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md // §测试纪律 — an unbounded module load must not be billed to a bounded window). diff --git a/ui/packages/components/src/renderers/__tests__/container-declaration-census.test.tsx b/ui/packages/components/src/renderers/__tests__/container-declaration-census.test.tsx index e7887d9215e..ebaa177e89b 100644 --- a/ui/packages/components/src/renderers/__tests__/container-declaration-census.test.tsx +++ b/ui/packages/components/src/renderers/__tests__/container-declaration-census.test.tsx @@ -64,8 +64,8 @@ import { describe, it, expect } from 'vitest'; import { render, waitFor } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer, AdapterCtx } from '@object-ui/react'; -import { CHILD_LIST_KEY, manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { CHILD_LIST_KEY, manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md // §测试纪律 — an unbounded module load must not be billed to a bounded window). diff --git a/ui/packages/components/src/renderers/__tests__/container-declaration-ratchet.test.tsx b/ui/packages/components/src/renderers/__tests__/container-declaration-ratchet.test.tsx index 88492d92492..cd43641eb14 100644 --- a/ui/packages/components/src/renderers/__tests__/container-declaration-ratchet.test.tsx +++ b/ui/packages/components/src/renderers/__tests__/container-declaration-ratchet.test.tsx @@ -87,8 +87,8 @@ import { join } from 'node:path'; import { render, waitFor } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer, AdapterCtx } from '@object-ui/react'; -import { CHILD_LIST_KEY, manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { CHILD_LIST_KEY, manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md // §测试纪律 — an unbounded module load must not be billed to a bounded window). diff --git a/ui/packages/components/src/renderers/__tests__/header-bar-unknown-prop-parser-tier-10981.test.tsx b/ui/packages/components/src/renderers/__tests__/header-bar-unknown-prop-parser-tier-10981.test.tsx index b366ad6be06..e567e25e0cb 100644 --- a/ui/packages/components/src/renderers/__tests__/header-bar-unknown-prop-parser-tier-10981.test.tsx +++ b/ui/packages/components/src/renderers/__tests__/header-bar-unknown-prop-parser-tier-10981.test.tsx @@ -34,8 +34,8 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md // §测试纪律 — an unbounded module load must not be billed to a bounded window). diff --git a/ui/packages/components/src/renderers/__tests__/node-slot-declaration-11170.test.tsx b/ui/packages/components/src/renderers/__tests__/node-slot-declaration-11170.test.tsx index 07c127141de..ce60f6081aa 100644 --- a/ui/packages/components/src/renderers/__tests__/node-slot-declaration-11170.test.tsx +++ b/ui/packages/components/src/renderers/__tests__/node-slot-declaration-11170.test.tsx @@ -49,8 +49,8 @@ import { describe, expect, it } from 'vitest'; import { render, waitFor } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { AdapterCtx, SchemaRenderer } from '@object-ui/react'; -import { inputTypeArms, manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { SchemaElement } from '@object-ui/sdui-parser'; +import { inputTypeArms, manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { SchemaElement } from '@objectstack/sdui-parser'; import { NODE_SLOT_DECLARATIONS, nodeSlotPathSegments, diff --git a/ui/packages/components/src/renderers/action/__tests__/action-button-icon-inputs-11168.test.tsx b/ui/packages/components/src/renderers/action/__tests__/action-button-icon-inputs-11168.test.tsx index 22c36541b6f..e771ab825e9 100644 --- a/ui/packages/components/src/renderers/action/__tests__/action-button-icon-inputs-11168.test.tsx +++ b/ui/packages/components/src/renderers/action/__tests__/action-button-icon-inputs-11168.test.tsx @@ -67,8 +67,8 @@ import { useAction, } from '@object-ui/react'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { SchemaElement } from '@objectstack/sdui-parser'; import { buttonVariants } from '../../../ui/button'; // Module-scope side-effect imports: the registry must hold both renderers when // `SchemaRenderer` resolves the type, and the light `dom` project does not load diff --git a/ui/packages/components/src/renderers/action/__tests__/action-button-undoable-11168.test.tsx b/ui/packages/components/src/renderers/action/__tests__/action-button-undoable-11168.test.tsx index befdd91f6ad..3053e5dd459 100644 --- a/ui/packages/components/src/renderers/action/__tests__/action-button-undoable-11168.test.tsx +++ b/ui/packages/components/src/renderers/action/__tests__/action-button-undoable-11168.test.tsx @@ -41,8 +41,8 @@ import type { PublicBlockNodeOf } from '@object-ui/types'; import { undeclaredNode } from '@object-ui/test-support'; import { ActionProvider, RecordContextProvider, SchemaRenderer } from '@object-ui/react'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { SchemaElement } from '@objectstack/sdui-parser'; // Module-scope side-effect imports, so the renderers are registered before the // first render: the light `dom` project does not load the components graph. // Module scope, not a `beforeAll`, per AGENTS.md 测试纪律. diff --git a/ui/packages/components/src/renderers/action/__tests__/action-type-input-html-tier.test.tsx b/ui/packages/components/src/renderers/action/__tests__/action-type-input-html-tier.test.tsx index 9e54a92a8b4..b399ae30025 100644 --- a/ui/packages/components/src/renderers/action/__tests__/action-type-input-html-tier.test.tsx +++ b/ui/packages/components/src/renderers/action/__tests__/action-type-input-html-tier.test.tsx @@ -68,7 +68,7 @@ import type { ActionContext, ActionDef, ActionResult } from '@object-ui/core'; import { ComponentRegistry } from '@object-ui/core'; import { ActionProvider } from '@object-ui/react'; import { SchemaRenderer } from '@object-ui/react'; -import { compile, manifestFromConfigs } from '@object-ui/sdui-parser'; +import { compile, manifestFromConfigs } from '@objectstack/sdui-parser'; // Module-scope side-effect import so `page` (the kind:'html' host) and the two // action renderers are registered before the first render — the light `dom` // project does not load the `@object-ui/components` barrel for us, and diff --git a/ui/packages/components/src/renderers/basic/__tests__/data-list.elementDataSource-11880.test.tsx b/ui/packages/components/src/renderers/basic/__tests__/data-list.elementDataSource-11880.test.tsx index c1c9982eb9b..787e959f057 100644 --- a/ui/packages/components/src/renderers/basic/__tests__/data-list.elementDataSource-11880.test.tsx +++ b/ui/packages/components/src/renderers/basic/__tests__/data-list.elementDataSource-11880.test.tsx @@ -41,8 +41,8 @@ import * as React from 'react'; import { render, screen, waitFor, act, cleanup } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { AdapterCtx, SchemaRenderer, notifyDataChanged } from '@object-ui/react'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { SchemaElement } from '@objectstack/sdui-parser'; // Registers every `element:*` renderer at module scope, not in a hook // (object-ui/no-dynamic-import-in-test-hook, objectui#3010). import '../../../renderers'; diff --git a/ui/packages/components/src/renderers/basic/__tests__/element-list-inputs-11168.test.tsx b/ui/packages/components/src/renderers/basic/__tests__/element-list-inputs-11168.test.tsx index eae62a95619..ffa207f5928 100644 --- a/ui/packages/components/src/renderers/basic/__tests__/element-list-inputs-11168.test.tsx +++ b/ui/packages/components/src/renderers/basic/__tests__/element-list-inputs-11168.test.tsx @@ -50,8 +50,8 @@ import { render, screen, waitFor, cleanup } from '@testing-library/react'; import '@testing-library/jest-dom'; import { ComponentRegistry } from '@object-ui/core'; import { AdapterCtx, FilterScopeProvider, SchemaRenderer } from '@object-ui/react'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { SchemaElement } from '@objectstack/sdui-parser'; import { ComponentPropsMap } from '@objectstack/spec/ui'; // Registers every `element:*` renderer at module scope, not in a hook // (object-ui/no-dynamic-import-in-test-hook, objectui#3010). diff --git a/ui/packages/components/src/renderers/basic/__tests__/element-text-published-variants-7450.test.tsx b/ui/packages/components/src/renderers/basic/__tests__/element-text-published-variants-7450.test.tsx index 1f4a56d3441..425fc66bc24 100644 --- a/ui/packages/components/src/renderers/basic/__tests__/element-text-published-variants-7450.test.tsx +++ b/ui/packages/components/src/renderers/basic/__tests__/element-text-published-variants-7450.test.tsx @@ -45,7 +45,7 @@ import { describe, it, expect } from 'vitest'; import { render } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer } from '@object-ui/react'; -import { compile, manifestFromConfigs } from '@object-ui/sdui-parser'; +import { compile, manifestFromConfigs } from '@objectstack/sdui-parser'; import { shapeEnumOptions } from '@object-ui/test-support'; import { TextSchema, safeValidateSchema } from '@object-ui/types/zod'; import { ElementTextPropsSchema } from '@objectstack/spec/ui'; diff --git a/ui/packages/components/src/renderers/basic/__tests__/elementNumber.dataSourceBinding-10909.test.tsx b/ui/packages/components/src/renderers/basic/__tests__/elementNumber.dataSourceBinding-10909.test.tsx index 321f115fc43..96eb77f67c1 100644 --- a/ui/packages/components/src/renderers/basic/__tests__/elementNumber.dataSourceBinding-10909.test.tsx +++ b/ui/packages/components/src/renderers/basic/__tests__/elementNumber.dataSourceBinding-10909.test.tsx @@ -54,7 +54,7 @@ import { describe, it, expect, vi, afterEach } from 'vitest'; import { render, act, cleanup, screen, waitFor } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { AdapterCtx, SchemaRenderer, notifyDataChanged } from '@object-ui/react'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Registers every `element:*` renderer at module scope, not in a hook // (object-ui/no-dynamic-import-in-test-hook, objectui#3010). import '../../../renderers'; diff --git a/ui/packages/components/src/renderers/basic/span.tsx b/ui/packages/components/src/renderers/basic/span.tsx index 21832f7713b..7dc9782105d 100644 --- a/ui/packages/components/src/renderers/basic/span.tsx +++ b/ui/packages/components/src/renderers/basic/span.tsx @@ -7,7 +7,7 @@ */ import { ComponentRegistry } from '@object-ui/core'; -import { isHtmlTierNode } from '@object-ui/sdui-parser'; +import { isHtmlTierNode } from '@objectstack/sdui-parser'; import type { TextSpanSchema } from '@object-ui/types'; import { renderChildren } from '../../lib/utils'; import { forwardRef } from 'react'; diff --git a/ui/packages/components/src/renderers/layout/page.tsx b/ui/packages/components/src/renderers/layout/page.tsx index 3d142e7f8fc..d372ec41550 100644 --- a/ui/packages/components/src/renderers/layout/page.tsx +++ b/ui/packages/components/src/renderers/layout/page.tsx @@ -26,7 +26,7 @@ import { } from '@object-ui/react'; import { ComponentRegistry, toDomProps } from '@object-ui/core'; import { pickLocalized, useDisplayLocale, useObjectTranslation } from '@object-ui/i18n'; -import { compile, manifestFromConfigs, type Diagnostic } from '@object-ui/sdui-parser'; +import { compile, manifestFromConfigs, type Diagnostic } from '@objectstack/sdui-parser'; import { PAGE_TEMPLATE_SLOTS, type PageTemplate } from '@objectstack/spec/ui'; import { ReactKindPage } from './react-page'; import { PRINT_PAGE_ATTRIBUTE, PRINT_RUNNING_ATTRIBUTE, buildPagePrintStylesheet, isPagePrintDeclaration } from './page-print'; @@ -856,7 +856,7 @@ export const PageRenderer: React.FC<{ if (!tree) return null; /** * ⭐ THE ONE BOUNDARY CAST (objectui#11466). `tree` is author source - * parsed at runtime (`@object-ui/sdui-parser`'s `SchemaElement`: a + * parsed at runtime (`@objectstack/sdui-parser`'s `SchemaElement`: a * `type: string` plus unknown props), so the compiler cannot know which * declared node type each element is. Its validator is what judges it: * `compile` runs `validateTree` against the registry manifest (an diff --git a/ui/packages/core/src/registry/Registry.ts b/ui/packages/core/src/registry/Registry.ts index 4cf19b2b792..ce80fa31465 100644 --- a/ui/packages/core/src/registry/Registry.ts +++ b/ui/packages/core/src/registry/Registry.ts @@ -76,7 +76,7 @@ export type { ComponentInput } from '@object-ui/types'; * - `'json'` — the JSON/SDUI authoring surface. A node an author (or an AI) * writes as `{ "type": "…" }` in metadata. * - `'html'` — a `kind:'html'` page, written as constrained JSX text that - * `@object-ui/sdui-parser` COMPILES (never executes) into SDUI nodes, tag + * `@objectstack/sdui-parser` COMPILES (never executes) into SDUI nodes, tag * name straight through. `isHtmlTierNode` marks what that parser emitted. * * The distinction is not decorative here: it is the whole reason diff --git a/ui/packages/core/src/registry/public-blocks.ts b/ui/packages/core/src/registry/public-blocks.ts index a1aee46583c..9539fa485b2 100644 --- a/ui/packages/core/src/registry/public-blocks.ts +++ b/ui/packages/core/src/registry/public-blocks.ts @@ -152,7 +152,7 @@ export const PUBLIC_BLOCKS: readonly string[] = [ // `inputs`: `components/src/renderers/layout/page.tsx` builds the JSX-page // compiler's manifest from `getKnownTypes()`, registry-wide. What this list // gates is `getPublicConfigs()` — the curated AI-authoring vocabulary, and - // through `sdui-parser/scripts/gen-manifest.ts` the published + // through `apps/console/scripts/gen-manifest.ts` the published // `sdui.manifest.json` and `sdui-intrinsics.d.ts`. THAT is the surface `box` // was missing from, which is a narrower and truer statement of the gap than // "the contract does not know it". diff --git a/ui/packages/layout/package.json b/ui/packages/layout/package.json index 247a9f4f15e..27e82467a9b 100644 --- a/ui/packages/layout/package.json +++ b/ui/packages/layout/package.json @@ -40,8 +40,8 @@ "react-router-dom": "^6.0.0 || ^7.0.0" }, "devDependencies": { - "@object-ui/sdui-parser": "workspace:*", "@object-ui/test-support": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@vitejs/plugin-react": "^6.0.5", "react": "19.3.0", "react-router-dom": "^7.18.2", diff --git a/ui/packages/layout/src/__tests__/appSchemaRendererMobileNavMode.test.tsx b/ui/packages/layout/src/__tests__/appSchemaRendererMobileNavMode.test.tsx index be1dbef499a..7df82724e2b 100644 --- a/ui/packages/layout/src/__tests__/appSchemaRendererMobileNavMode.test.tsx +++ b/ui/packages/layout/src/__tests__/appSchemaRendererMobileNavMode.test.tsx @@ -56,7 +56,7 @@ import { describe, it, expect, beforeAll } from 'vitest'; import { readFileSync } from 'node:fs'; import { resolve } from 'node:path'; import { ComponentRegistry } from '@object-ui/core'; -import { compile, manifestFromConfigs } from '@object-ui/sdui-parser'; +import { compile, manifestFromConfigs } from '@objectstack/sdui-parser'; import { registerLayout, type MobileNavMode } from '../index'; diff --git a/ui/packages/layout/src/__tests__/containment-declared-slot-9910.test.tsx b/ui/packages/layout/src/__tests__/containment-declared-slot-9910.test.tsx index 5b782b07290..2679c8a022e 100644 --- a/ui/packages/layout/src/__tests__/containment-declared-slot-9910.test.tsx +++ b/ui/packages/layout/src/__tests__/containment-declared-slot-9910.test.tsx @@ -35,8 +35,8 @@ import { describe, it, expect, beforeAll } from 'vitest'; import { render, waitFor } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer, AdapterCtx } from '@object-ui/react'; -import { CHILD_LIST_KEY, manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { CHILD_LIST_KEY, manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; import { registerLayout } from '../index'; diff --git a/ui/packages/plugin-calendar/package.json b/ui/packages/plugin-calendar/package.json index 9f5e742a2cd..60eda1d8924 100644 --- a/ui/packages/plugin-calendar/package.json +++ b/ui/packages/plugin-calendar/package.json @@ -55,7 +55,7 @@ "devDependencies": { "@object-ui/test-support": "workspace:*", "@object-ui/data-objectstack": "workspace:*", - "@object-ui/sdui-parser": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@types/react": "19.3.0", "@types/react-dom": "19.3.0", "@vitejs/plugin-react": "^6.0.5", diff --git a/ui/packages/plugin-calendar/src/__tests__/dataKeysAreDeclaredAndHonoured-8314.test.ts b/ui/packages/plugin-calendar/src/__tests__/dataKeysAreDeclaredAndHonoured-8314.test.ts index a276b160b20..9d82155a998 100644 --- a/ui/packages/plugin-calendar/src/__tests__/dataKeysAreDeclaredAndHonoured-8314.test.ts +++ b/ui/packages/plugin-calendar/src/__tests__/dataKeysAreDeclaredAndHonoured-8314.test.ts @@ -74,7 +74,7 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md's // test-discipline section). import '../index'; diff --git a/ui/packages/plugin-calendar/src/__tests__/filterIsDeclaredInput-7712.test.ts b/ui/packages/plugin-calendar/src/__tests__/filterIsDeclaredInput-7712.test.ts index f75b198969c..d45e5897fb9 100644 --- a/ui/packages/plugin-calendar/src/__tests__/filterIsDeclaredInput-7712.test.ts +++ b/ui/packages/plugin-calendar/src/__tests__/filterIsDeclaredInput-7712.test.ts @@ -79,7 +79,7 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md's // test-discipline section — an unbounded module load must not be billed to a // bounded window). diff --git a/ui/packages/plugin-calendar/src/__tests__/recordSourceInput-10392.test.ts b/ui/packages/plugin-calendar/src/__tests__/recordSourceInput-10392.test.ts index c6b972f6f10..99159b19c4d 100644 --- a/ui/packages/plugin-calendar/src/__tests__/recordSourceInput-10392.test.ts +++ b/ui/packages/plugin-calendar/src/__tests__/recordSourceInput-10392.test.ts @@ -41,7 +41,7 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; import { ObjectCalendarSchema } from '@object-ui/types/zod'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md // test-discipline section). import '../index'; diff --git a/ui/packages/plugin-calendar/src/__tests__/scalarKeysAreDeclaredAndHonoured-8201.test.ts b/ui/packages/plugin-calendar/src/__tests__/scalarKeysAreDeclaredAndHonoured-8201.test.ts index 8fd73218afe..5491010592c 100644 --- a/ui/packages/plugin-calendar/src/__tests__/scalarKeysAreDeclaredAndHonoured-8201.test.ts +++ b/ui/packages/plugin-calendar/src/__tests__/scalarKeysAreDeclaredAndHonoured-8201.test.ts @@ -53,7 +53,7 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md's // test-discipline section). import '../index'; diff --git a/ui/packages/plugin-dashboard/package.json b/ui/packages/plugin-dashboard/package.json index bfd308788c5..bb429fcbce1 100644 --- a/ui/packages/plugin-dashboard/package.json +++ b/ui/packages/plugin-dashboard/package.json @@ -42,8 +42,8 @@ }, "devDependencies": { "@object-ui/plugin-charts": "workspace:*", - "@object-ui/sdui-parser": "workspace:*", "@object-ui/test-support": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@types/react-grid-layout": "^2.1.0", "@vitejs/plugin-react": "^6.0.5", "react": "19.3.0", diff --git a/ui/packages/plugin-dashboard/src/DatasetWidget.tsx b/ui/packages/plugin-dashboard/src/DatasetWidget.tsx index f5ce131ca4e..570a1171666 100644 --- a/ui/packages/plugin-dashboard/src/DatasetWidget.tsx +++ b/ui/packages/plugin-dashboard/src/DatasetWidget.tsx @@ -623,7 +623,7 @@ export { chartConfigPresentation } from '@object-ui/core'; * one authored description, `widget.description`, which the dashboard surface * draws as the card-header subtitle. If a caption under a metric's value * returns, it returns as a declared widget-level key outside the options bag, - * never as a key inside it. The options census in `@object-ui/sdui-parser` + * never as a key inside it. The options census in `@objectstack/sdui-parser` * reads this file's text, comments included, which is why this note does not * spell the bag's member access. */ diff --git a/ui/packages/sdui-parser/src/__tests__/dashboard-widget-options-census.test.ts b/ui/packages/plugin-dashboard/src/__tests__/dashboard-widget-options-census.test.ts similarity index 97% rename from ui/packages/sdui-parser/src/__tests__/dashboard-widget-options-census.test.ts rename to ui/packages/plugin-dashboard/src/__tests__/dashboard-widget-options-census.test.ts index c7b40757934..720e947b8e3 100644 --- a/ui/packages/sdui-parser/src/__tests__/dashboard-widget-options-census.test.ts +++ b/ui/packages/plugin-dashboard/src/__tests__/dashboard-widget-options-census.test.ts @@ -57,7 +57,7 @@ import { stripComments } from '../../../../scripts/js-comment-mask.mjs'; import { CONSUMED_WIDGET_OPTION_KEYS, UNCONSUMED_WIDGET_OPTION, -} from '../dashboard-widget-options.js'; +} from '@objectstack/sdui-parser'; import { uiRoot } from '../../../../scripts/ui-root.mjs'; /** @@ -262,8 +262,10 @@ describe('leg 4 — repo tripwire: files reading widget.options', () => { } }; for (const root of ['packages', 'apps']) walk(join(repoRoot, root)); - // The measured surface on origin/main@8689166f6, plus the census module - // itself (whose header describes the bag in prose — it never renders one). + // The measured surface on origin/main@8689166f6. The census module itself + // (whose header describes the bag in prose) left this walk when the ui copy + // of the parser was retired for `@objectstack/sdui-parser` at the repo root + // (objectstack#25712 item 3). // A new entry means a new consumer of the bag: re-run the census (module // header) before extending either this list or the accepted-key set. // @@ -276,7 +278,6 @@ describe('leg 4 — repo tripwire: files reading widget.options', () => { 'packages/plugin-dashboard/src/DashboardGridLayout.tsx', 'packages/plugin-dashboard/src/DashboardRenderer.tsx', 'packages/plugin-dashboard/src/DatasetWidget.tsx', - 'packages/sdui-parser/src/dashboard-widget-options.ts', ]); }); }); diff --git a/ui/packages/plugin-dashboard/src/__tests__/dashboardAuthoredInputs.test.tsx b/ui/packages/plugin-dashboard/src/__tests__/dashboardAuthoredInputs.test.tsx index b975bfff57f..85d63cff8c8 100644 --- a/ui/packages/plugin-dashboard/src/__tests__/dashboardAuthoredInputs.test.tsx +++ b/ui/packages/plugin-dashboard/src/__tests__/dashboardAuthoredInputs.test.tsx @@ -105,8 +105,8 @@ import '@testing-library/jest-dom'; import React from 'react'; import { ComponentRegistry } from '@object-ui/core'; import { DashboardSchema } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree, generateDts, propsName } from '@object-ui/sdui-parser'; -import type { Diagnostic, SchemaElement } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree, generateDts, propsName } from '@objectstack/sdui-parser'; +import type { Diagnostic, SchemaElement } from '@objectstack/sdui-parser'; import { ActionProvider } from '@object-ui/react'; import { DashboardRenderer } from '../DashboardRenderer'; // Module scope, not a hook: this import IS the registration. diff --git a/ui/packages/plugin-designer/src/__tests__/designerRegistrationInputs-11434.test.ts b/ui/packages/plugin-designer/src/__tests__/designerRegistrationInputs-11434.test.ts index ca63acd6672..70ae427436d 100644 --- a/ui/packages/plugin-designer/src/__tests__/designerRegistrationInputs-11434.test.ts +++ b/ui/packages/plugin-designer/src/__tests__/designerRegistrationInputs-11434.test.ts @@ -33,8 +33,9 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; // Relative, as other packages' tests reach a sibling's source: this package -// declares no dependency on the parser, and the test is the only reader. -import { manifestFromConfigs, validateTree } from '../../../sdui-parser/src/index'; +// declares no dependency on the parser, and the test is the only reader. The +// one parser is the monorepo root's `packages/sdui-parser` (#25712 item 3). +import { manifestFromConfigs, validateTree } from '../../../../../packages/sdui-parser/src/index'; import '../index'; const EXPECTED: Record = { diff --git a/ui/packages/plugin-detail/package.json b/ui/packages/plugin-detail/package.json index 39aef8d805b..760720a7834 100644 --- a/ui/packages/plugin-detail/package.json +++ b/ui/packages/plugin-detail/package.json @@ -60,7 +60,7 @@ "@object-ui/permissions": "workspace:*", "@object-ui/test-support": "workspace:*", "@object-ui/react": "workspace:*", - "@object-ui/sdui-parser": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@object-ui/types": "workspace:*", "@types/react": "19.3.0", "@types/react-dom": "19.3.0", diff --git a/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredHideEmpty-10485.test.tsx b/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredHideEmpty-10485.test.tsx index 8b500e366c0..8c981c9fd74 100644 --- a/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredHideEmpty-10485.test.tsx +++ b/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredHideEmpty-10485.test.tsx @@ -63,7 +63,7 @@ import { describe, it, expect, afterEach } from 'vitest'; import { render, screen, cleanup } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer } from '@object-ui/react'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Module scope, not a hook (the test-discipline section of AGENTS.md): // importing the package index executes its registration side-effects, so the diff --git a/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredIcon-9529.test.tsx b/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredIcon-9529.test.tsx index a137a1dca53..0b0fcdbbc8b 100644 --- a/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredIcon-9529.test.tsx +++ b/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredIcon-9529.test.tsx @@ -55,7 +55,7 @@ import { describe, it, expect, afterEach } from 'vitest'; import { render, screen, cleanup, waitFor } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer } from '@object-ui/react'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // The reference glyph the render rows compare against, imported at module // scope rather than in a hook (see the test-discipline section of AGENTS.md): // `LazyIcon` draws `map-pin` through lucide's `DynamicIcon`, which fetches the diff --git a/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredNode-8626.test.tsx b/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredNode-8626.test.tsx index 5175a90ee5d..ca934cfc632 100644 --- a/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredNode-8626.test.tsx +++ b/ui/packages/plugin-detail/src/__tests__/detailSectionAuthoredNode-8626.test.tsx @@ -54,7 +54,7 @@ import { describe, it, expect, afterEach } from 'vitest'; import { render, screen, cleanup } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer } from '@object-ui/react'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; import { DETAIL_SECTION_NODE_INPUTS } from '../DetailSectionNode'; // Module scope, not a hook (AGENTS.md 测试纪律): importing the package index diff --git a/ui/packages/plugin-detail/src/__tests__/detailSectionHeaderColorEnum-6955.test.ts b/ui/packages/plugin-detail/src/__tests__/detailSectionHeaderColorEnum-6955.test.ts index 84904ec5506..bf2888a396f 100644 --- a/ui/packages/plugin-detail/src/__tests__/detailSectionHeaderColorEnum-6955.test.ts +++ b/ui/packages/plugin-detail/src/__tests__/detailSectionHeaderColorEnum-6955.test.ts @@ -70,7 +70,7 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; import { DetailViewSectionSchema } from '@object-ui/types/zod'; -import { inputTypeArms, manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { inputTypeArms, manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; import { headerColorClass } from '../headerColor'; // Module scope, not a hook: this import IS the registration (AGENTS.md's // test-discipline section — an unbounded module load must not be billed to a diff --git a/ui/packages/plugin-editor/vite.config.ts b/ui/packages/plugin-editor/vite.config.ts index d86a0361b6e..4005fd1ae3d 100644 --- a/ui/packages/plugin-editor/vite.config.ts +++ b/ui/packages/plugin-editor/vite.config.ts @@ -66,7 +66,7 @@ export default defineConfig({ // `vitest.config.mts` alias table. '@object-ui/i18n': resolve(__dirname, '../i18n/src'), '@object-ui/core': resolve(__dirname, '../core/src'), - '@object-ui/sdui-parser': resolve(__dirname, '../sdui-parser/src'), + '@objectstack/sdui-parser': resolve(__dirname, '../../../packages/sdui-parser/src'), '@object-ui/types': resolve(__dirname, '../types/src'), '@object-ui/react': resolve(__dirname, '../react/src'), '@object-ui/components': resolve(__dirname, '../components/src'), diff --git a/ui/packages/plugin-gantt/package.json b/ui/packages/plugin-gantt/package.json index dbd06faab53..24fd77f35f6 100644 --- a/ui/packages/plugin-gantt/package.json +++ b/ui/packages/plugin-gantt/package.json @@ -54,7 +54,7 @@ }, "devDependencies": { "@object-ui/test-support": "workspace:*", - "@object-ui/sdui-parser": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@types/react": "19.3.0", "@types/react-dom": "19.3.0", "@vitejs/plugin-react": "^6.0.5", diff --git a/ui/packages/plugin-gantt/src/__tests__/objectGanttInputs-11168.test.tsx b/ui/packages/plugin-gantt/src/__tests__/objectGanttInputs-11168.test.tsx index 8c06a9b7dcb..4ff9648fb9a 100644 --- a/ui/packages/plugin-gantt/src/__tests__/objectGanttInputs-11168.test.tsx +++ b/ui/packages/plugin-gantt/src/__tests__/objectGanttInputs-11168.test.tsx @@ -43,7 +43,7 @@ import { SchemaRendererProvider, type RelatedRecordActionsValue, } from '@object-ui/react'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; import { ComponentPropsMap } from '@objectstack/spec/ui'; vi.mock('sonner', () => ({ toast: { error: vi.fn(), success: vi.fn() } })); diff --git a/ui/packages/plugin-gantt/src/__tests__/queryKeysDeclared-8220.test.tsx b/ui/packages/plugin-gantt/src/__tests__/queryKeysDeclared-8220.test.tsx index fff5940bd58..cb00ac751a5 100644 --- a/ui/packages/plugin-gantt/src/__tests__/queryKeysDeclared-8220.test.tsx +++ b/ui/packages/plugin-gantt/src/__tests__/queryKeysDeclared-8220.test.tsx @@ -61,7 +61,7 @@ import { render, waitFor, cleanup } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; vi.mock('sonner', () => ({ toast: { error: vi.fn() } })); diff --git a/ui/packages/plugin-gantt/src/__tests__/recordSourceInputs-10394.test.tsx b/ui/packages/plugin-gantt/src/__tests__/recordSourceInputs-10394.test.tsx index a241f1c668f..3cf9bf5498d 100644 --- a/ui/packages/plugin-gantt/src/__tests__/recordSourceInputs-10394.test.tsx +++ b/ui/packages/plugin-gantt/src/__tests__/recordSourceInputs-10394.test.tsx @@ -69,7 +69,7 @@ vi.mock('@object-ui/plugin-detail', async (importOriginal) => ({ import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react'; import { ObjectGanttSchema } from '@object-ui/types/zod'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; import type { DataSource } from '@object-ui/types'; // Module scope, not a hook: this import IS the registration (AGENTS.md // test-discipline section). diff --git a/ui/packages/plugin-grid/package.json b/ui/packages/plugin-grid/package.json index eff9e6f8b2c..1ac7f3011de 100644 --- a/ui/packages/plugin-grid/package.json +++ b/ui/packages/plugin-grid/package.json @@ -43,8 +43,8 @@ }, "devDependencies": { "@object-ui/data-objectstack": "workspace:*", - "@object-ui/sdui-parser": "workspace:*", "@object-ui/test-support": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@objectstack/spec": "workspace:^", "@tailwindcss/postcss": "^4.3.3", "@vitejs/plugin-react": "^6.0.5", diff --git a/ui/packages/plugin-grid/src/__tests__/gridNonAuthorKeys.test.tsx b/ui/packages/plugin-grid/src/__tests__/gridNonAuthorKeys.test.tsx index 6ef5a2e1889..a37b801f160 100644 --- a/ui/packages/plugin-grid/src/__tests__/gridNonAuthorKeys.test.tsx +++ b/ui/packages/plugin-grid/src/__tests__/gridNonAuthorKeys.test.tsx @@ -127,7 +127,7 @@ import '@testing-library/jest-dom'; import React from 'react'; import { ComponentRegistry, collectPredicateFieldRefs, listViewPredicates } from '@object-ui/core'; import { ComponentPropsMap, ListViewSchema, UserActionsConfigSchema } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; import { registerAllFields } from '@object-ui/fields'; import { ActionProvider } from '@object-ui/react'; diff --git a/ui/packages/plugin-kanban/package.json b/ui/packages/plugin-kanban/package.json index b046018152a..3659685c405 100644 --- a/ui/packages/plugin-kanban/package.json +++ b/ui/packages/plugin-kanban/package.json @@ -57,8 +57,8 @@ }, "devDependencies": { "@object-ui/data-objectstack": "workspace:*", - "@object-ui/sdui-parser": "workspace:*", "@object-ui/test-support": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@tailwindcss/postcss": "^4.3.3", "@types/react": "19.3.0", "@types/react-dom": "19.3.0", diff --git a/ui/packages/plugin-kanban/src/__tests__/filterIsDeclaredInput-7712.test.ts b/ui/packages/plugin-kanban/src/__tests__/filterIsDeclaredInput-7712.test.ts index 358acec10f4..ead7a8babb3 100644 --- a/ui/packages/plugin-kanban/src/__tests__/filterIsDeclaredInput-7712.test.ts +++ b/ui/packages/plugin-kanban/src/__tests__/filterIsDeclaredInput-7712.test.ts @@ -69,7 +69,7 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md's // test-discipline section — an unbounded module load must not be billed to a // bounded window). diff --git a/ui/packages/plugin-kanban/src/__tests__/quickAddRetiredNotForwarded-8285.test.tsx b/ui/packages/plugin-kanban/src/__tests__/quickAddRetiredNotForwarded-8285.test.tsx index 7b2e6907603..f63367a8cac 100644 --- a/ui/packages/plugin-kanban/src/__tests__/quickAddRetiredNotForwarded-8285.test.tsx +++ b/ui/packages/plugin-kanban/src/__tests__/quickAddRetiredNotForwarded-8285.test.tsx @@ -69,7 +69,7 @@ import '@testing-library/jest-dom'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md's // test-discipline section — an unbounded module load must not be billed to a // bounded window). diff --git a/ui/packages/plugin-kanban/src/__tests__/scalarKeysAreDeclaredAndHonoured-8201.test.ts b/ui/packages/plugin-kanban/src/__tests__/scalarKeysAreDeclaredAndHonoured-8201.test.ts index c8d504841dc..43233913d5b 100644 --- a/ui/packages/plugin-kanban/src/__tests__/scalarKeysAreDeclaredAndHonoured-8201.test.ts +++ b/ui/packages/plugin-kanban/src/__tests__/scalarKeysAreDeclaredAndHonoured-8201.test.ts @@ -63,7 +63,7 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md's // test-discipline section — an unbounded module load must not be billed to a // bounded window). diff --git a/ui/packages/plugin-kanban/src/__tests__/structuredKeysAreDeclaredAndHonoured-8313.test.ts b/ui/packages/plugin-kanban/src/__tests__/structuredKeysAreDeclaredAndHonoured-8313.test.ts index f86c2e6238f..67e77da8d33 100644 --- a/ui/packages/plugin-kanban/src/__tests__/structuredKeysAreDeclaredAndHonoured-8313.test.ts +++ b/ui/packages/plugin-kanban/src/__tests__/structuredKeysAreDeclaredAndHonoured-8313.test.ts @@ -77,7 +77,7 @@ import { describe, it, expect } from 'vitest'; import { ComponentRegistry } from '@object-ui/core'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md's // test-discipline section — an unbounded module load must not be billed to a // bounded window). diff --git a/ui/packages/plugin-list/src/__tests__/htmlTierColumnSpellings-6598.test.tsx b/ui/packages/plugin-list/src/__tests__/htmlTierColumnSpellings-6598.test.tsx index 4f2cd29467d..361d26f4b92 100644 --- a/ui/packages/plugin-list/src/__tests__/htmlTierColumnSpellings-6598.test.tsx +++ b/ui/packages/plugin-list/src/__tests__/htmlTierColumnSpellings-6598.test.tsx @@ -33,7 +33,7 @@ * * Sibling files pin the mechanisms one at a time: * `literal-subset-6614.test.ts` and `inert-expression-6598.test.ts` in - * `@object-ui/sdui-parser` own the grammar and its diagnostic; + * `@objectstack/sdui-parser` own the grammar and its diagnostic; * `ListView.unauthoredColumnProjection-6598.test.tsx` owns the handoff against * a stub grid; `htmlTierListViewDefaultColumns-6598.test.tsx` takes the * unauthored case end to end. @@ -197,21 +197,6 @@ describe("#6598 — all eight reported `columns` spellings on a kind:'html' page expectDefaults(await renderSpelling(``)); }); - it('form 2 — a JSON STRING where an array is declared', async () => { - // ⛔ Not honoured as a projection, and that is contract-first (AGENTS.md - // #0.1), not a gap: `columns` is declared `array`, so a string draws a - // warning-severity `type-mismatch` from the manifest validator and is - // refused. Parsing it anyway would be exactly the consumer-side tolerance - // alias the rule forbids. What matters to THIS card is that refusing it - // no longer costs the page its columns. - const r = await renderSpelling(``); - - expectDefaults(r); - // The string was not silently adopted as a two-column projection: the - // undeclared third field is on screen, so these are the defaults. - expect(r.headers).toContain('Stage'); - }); - it('form 4 — a `viewName` reference to a saved view', async () => { // `viewName` is not declared by the live `list-view` registration and is // not a base prop, so it draws `unknown-prop`. It was never the spelling @@ -230,6 +215,24 @@ describe("#6598 — all eight reported `columns` spellings on a kind:'html' page }); describe('the spellings that fail LOUDLY, which is the opposite of the report', () => { + it('form 2 — a JSON STRING where an array is declared', async () => { + // ⛔ Not honoured as a projection, and that is contract-first (AGENTS.md + // #0.1), not a gap: `columns` is declared `array`, so a string draws a + // `type-mismatch` from the manifest validator. Parsing it anyway would be + // exactly the consumer-side tolerance alias the rule forbids. Since the + // one parser (#25712 item 3) every `type-mismatch` is an `error`, so the + // page refuses to compile and says why, where the retired ui copy warned + // and rendered the block's defaults. Either way the page is not the + // reported state, which is the only thing this card is about. + const r = await renderSpelling(``); + + expectNotTheReportedSymptom(r); + expect(r.compileFailed).toBe(true); + // The author is told which prop, on the page. + expect(r.text).toContain('columns'); + expect(r.headers).toEqual([]); + }); + it('form 6 — child elements', async () => { const r = await renderSpelling( ``, diff --git a/ui/packages/plugin-map/package.json b/ui/packages/plugin-map/package.json index d5078ced507..f49f18c6a30 100644 --- a/ui/packages/plugin-map/package.json +++ b/ui/packages/plugin-map/package.json @@ -51,7 +51,7 @@ }, "devDependencies": { "@object-ui/plugin-list": "workspace:*", - "@object-ui/sdui-parser": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@types/react": "19.3.0", "@types/react-dom": "19.3.0", "@vitejs/plugin-react": "^6.0.5", diff --git a/ui/packages/plugin-map/src/__tests__/objectMapInputs-11168.test.tsx b/ui/packages/plugin-map/src/__tests__/objectMapInputs-11168.test.tsx index c3179ff34ae..3cca3f174ad 100644 --- a/ui/packages/plugin-map/src/__tests__/objectMapInputs-11168.test.tsx +++ b/ui/packages/plugin-map/src/__tests__/objectMapInputs-11168.test.tsx @@ -39,7 +39,7 @@ import { SchemaRendererProvider, type RelatedRecordActionsValue, } from '@object-ui/react'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; import { ComponentPropsMap } from '@objectstack/spec/ui'; /** The props `ObjectMap` handed the MapGL component on its last render. */ diff --git a/ui/packages/plugin-map/src/__tests__/queryKeysDeclared-8220.test.tsx b/ui/packages/plugin-map/src/__tests__/queryKeysDeclared-8220.test.tsx index 735ea25d448..bfa1332be31 100644 --- a/ui/packages/plugin-map/src/__tests__/queryKeysDeclared-8220.test.tsx +++ b/ui/packages/plugin-map/src/__tests__/queryKeysDeclared-8220.test.tsx @@ -64,7 +64,7 @@ import { render, waitFor, cleanup } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // No WebGL in the test env — same stub the sibling ObjectMap pins use. Every // assertion here is about the declaration or the QUERY, not the canvas. diff --git a/ui/packages/plugin-map/src/index.bareMapKeyRetired-10393.test.tsx b/ui/packages/plugin-map/src/index.bareMapKeyRetired-10393.test.tsx index ef46d0889cc..e7ac37c55f7 100644 --- a/ui/packages/plugin-map/src/index.bareMapKeyRetired-10393.test.tsx +++ b/ui/packages/plugin-map/src/index.bareMapKeyRetired-10393.test.tsx @@ -57,7 +57,7 @@ vi.mock('react-map-gl/maplibre', () => ({ import { ComponentRegistry } from '@object-ui/core'; import { safeValidateSchema } from '@object-ui/types/zod'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md // test-discipline section). import './index'; diff --git a/ui/packages/plugin-map/src/index.recordSourceInputs-10394.test.tsx b/ui/packages/plugin-map/src/index.recordSourceInputs-10394.test.tsx index 750d6c9d26b..3f1090d00e2 100644 --- a/ui/packages/plugin-map/src/index.recordSourceInputs-10394.test.tsx +++ b/ui/packages/plugin-map/src/index.recordSourceInputs-10394.test.tsx @@ -64,7 +64,7 @@ vi.mock('react-map-gl/maplibre', () => ({ import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react'; import { ObjectMapSchema } from '@object-ui/types/zod'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; import type { DataSource } from '@object-ui/types'; // Module scope, not a hook: this import IS the registration (AGENTS.md // test-discipline section). diff --git a/ui/packages/plugin-markdown/vite.config.ts b/ui/packages/plugin-markdown/vite.config.ts index 3466c69e4a0..a3e9adba3e3 100644 --- a/ui/packages/plugin-markdown/vite.config.ts +++ b/ui/packages/plugin-markdown/vite.config.ts @@ -66,7 +66,7 @@ export default defineConfig({ // `vitest.config.mts` alias table. '@object-ui/i18n': resolve(__dirname, '../i18n/src'), '@object-ui/core': resolve(__dirname, '../core/src'), - '@object-ui/sdui-parser': resolve(__dirname, '../sdui-parser/src'), + '@objectstack/sdui-parser': resolve(__dirname, '../../../packages/sdui-parser/src'), '@object-ui/types': resolve(__dirname, '../types/src'), '@object-ui/react': resolve(__dirname, '../react/src'), '@object-ui/components': resolve(__dirname, '../components/src'), diff --git a/ui/packages/plugin-timeline/package.json b/ui/packages/plugin-timeline/package.json index 54cc4171b0c..6c91347d5cc 100644 --- a/ui/packages/plugin-timeline/package.json +++ b/ui/packages/plugin-timeline/package.json @@ -56,8 +56,8 @@ }, "devDependencies": { "@object-ui/data-objectstack": "workspace:*", - "@object-ui/sdui-parser": "workspace:*", "@object-ui/test-support": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@types/react": "19.3.0", "@types/react-dom": "19.3.0", "@vitejs/plugin-react": "^6.0.5", diff --git a/ui/packages/plugin-timeline/src/__tests__/object-timeline-navigation-8654.test.tsx b/ui/packages/plugin-timeline/src/__tests__/object-timeline-navigation-8654.test.tsx index 027d3de1e4a..919c3261d3d 100644 --- a/ui/packages/plugin-timeline/src/__tests__/object-timeline-navigation-8654.test.tsx +++ b/ui/packages/plugin-timeline/src/__tests__/object-timeline-navigation-8654.test.tsx @@ -40,7 +40,7 @@ import { render, screen, cleanup, fireEvent, waitFor } from '@testing-library/re import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; import { safeValidateSchema, StrictAnyComponentSchema } from '@object-ui/types/zod'; import type { ViewNavigationConfig } from '@object-ui/types'; import type { ObjectTimelineProps } from '../ObjectTimeline'; diff --git a/ui/packages/plugin-timeline/src/__tests__/objectTimelineInputs-11168.test.tsx b/ui/packages/plugin-timeline/src/__tests__/objectTimelineInputs-11168.test.tsx index 91eaa846285..7d7d84b72e3 100644 --- a/ui/packages/plugin-timeline/src/__tests__/objectTimelineInputs-11168.test.tsx +++ b/ui/packages/plugin-timeline/src/__tests__/objectTimelineInputs-11168.test.tsx @@ -51,7 +51,7 @@ import { SchemaRendererProvider, type RelatedRecordActionsValue, } from '@object-ui/react'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; import { safeValidateSchema } from '@object-ui/types/zod'; import { ComponentPropsMap } from '@objectstack/spec/ui'; // Registers `object-timeline` and `view:timeline` through this package's own diff --git a/ui/packages/plugin-timeline/src/__tests__/queryKeysDeclared-8220.test.tsx b/ui/packages/plugin-timeline/src/__tests__/queryKeysDeclared-8220.test.tsx index 30fa85f5a57..19dae8c9b98 100644 --- a/ui/packages/plugin-timeline/src/__tests__/queryKeysDeclared-8220.test.tsx +++ b/ui/packages/plugin-timeline/src/__tests__/queryKeysDeclared-8220.test.tsx @@ -63,7 +63,7 @@ import { render, waitFor, cleanup } from '@testing-library/react'; import { ComponentRegistry } from '@object-ui/core'; import { SchemaRenderer, SchemaRendererProvider } from '@object-ui/react'; import { ComponentPropsMap } from '@objectstack/spec/ui'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; // Module scope, not a hook: this import IS the registration (AGENTS.md's // test-discipline section). import '../index'; diff --git a/ui/packages/plugin-tree/package.json b/ui/packages/plugin-tree/package.json index c60f5d99688..69902de8ae2 100644 --- a/ui/packages/plugin-tree/package.json +++ b/ui/packages/plugin-tree/package.json @@ -52,8 +52,8 @@ "react-dom": "^19.0.0" }, "devDependencies": { - "@object-ui/sdui-parser": "workspace:*", "@object-ui/test-support": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@types/react": "19.3.0", "@types/react-dom": "19.3.0", "@vitejs/plugin-react": "^6.0.5", diff --git a/ui/packages/plugin-tree/src/__tests__/objectTreeInputs-11168.test.tsx b/ui/packages/plugin-tree/src/__tests__/objectTreeInputs-11168.test.tsx index 6d377164bc5..a3f9ffe8f01 100644 --- a/ui/packages/plugin-tree/src/__tests__/objectTreeInputs-11168.test.tsx +++ b/ui/packages/plugin-tree/src/__tests__/objectTreeInputs-11168.test.tsx @@ -47,7 +47,7 @@ import { SchemaRendererProvider, type RelatedRecordActionsValue, } from '@object-ui/react'; -import { manifestFromConfigs, validateTree } from '@object-ui/sdui-parser'; +import { manifestFromConfigs, validateTree } from '@objectstack/sdui-parser'; import { ComponentPropsMap } from '@objectstack/spec/ui'; import { safeValidateSchema } from '@object-ui/types/zod'; // Registers `object-tree` through this package's own entry, at diff --git a/ui/packages/react-runtime/README.md b/ui/packages/react-runtime/README.md index e711440ca9d..f8258919080 100644 --- a/ui/packages/react-runtime/README.md +++ b/ui/packages/react-runtime/README.md @@ -173,7 +173,7 @@ generateElement(code, scope) // transpile + eval → ReactElement | null (th - [React pages guide](../../content/docs/guide/react-pages.md) — authoring `kind:'react'` pages, the injected block scope, and the capability gate. -- `@object-ui/sdui-parser` — the `kind:'html'` tier: parse, never execute. +- `@objectstack/sdui-parser` — the `kind:'html'` tier: parse, never execute. ## License diff --git a/ui/packages/react/package.json b/ui/packages/react/package.json index 46bb01c8d09..4a4b529fa20 100644 --- a/ui/packages/react/package.json +++ b/ui/packages/react/package.json @@ -43,6 +43,7 @@ }, "devDependencies": { "@object-ui/test-support": "workspace:*", + "@objectstack/sdui-parser": "workspace:*", "@types/react": "19.3.0", "@types/react-dom": "19.3.0", "typescript": "^6.0.3", diff --git a/ui/packages/sdui-parser/src/__tests__/injected-component-input-6950.test.ts b/ui/packages/react/src/__tests__/sdui-injected-component-input-6950.test.ts similarity index 99% rename from ui/packages/sdui-parser/src/__tests__/injected-component-input-6950.test.ts rename to ui/packages/react/src/__tests__/sdui-injected-component-input-6950.test.ts index c05850d2163..2bad8749edf 100644 --- a/ui/packages/sdui-parser/src/__tests__/injected-component-input-6950.test.ts +++ b/ui/packages/react/src/__tests__/sdui-injected-component-input-6950.test.ts @@ -52,7 +52,7 @@ import { type ManifestInput, type ManifestValidationResult, type RegistryConfigLike, -} from '../index.js'; +} from '@objectstack/sdui-parser'; const NS = 'pin6950'; const TYPE = `${NS}:probe`; diff --git a/ui/packages/sdui-parser/src/__tests__/inline-whitespace.test.tsx b/ui/packages/react/src/__tests__/sdui-inline-whitespace.test.tsx similarity index 98% rename from ui/packages/sdui-parser/src/__tests__/inline-whitespace.test.tsx rename to ui/packages/react/src/__tests__/sdui-inline-whitespace.test.tsx index 7c448a73c3d..c6f928cc4fc 100644 --- a/ui/packages/sdui-parser/src/__tests__/inline-whitespace.test.tsx +++ b/ui/packages/react/src/__tests__/sdui-inline-whitespace.test.tsx @@ -36,9 +36,8 @@ import { beforeAll, describe, expect, it } from 'vitest'; import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { ComponentRegistry } from '@object-ui/core'; -import { SchemaRenderer } from '@object-ui/react'; -import { parseJsx } from '../parse.js'; -import type { SchemaElement, ParsedSchemaNode } from '../types.js'; +import { SchemaRenderer } from '../SchemaRenderer'; +import { parseJsx, type SchemaElement, type ParsedSchemaNode } from '@objectstack/sdui-parser'; /** The parsed children of a single-root source. */ function childrenOf(source: string): ParsedSchemaNode[] { diff --git a/ui/packages/sdui-parser/src/__tests__/render.test.tsx b/ui/packages/react/src/__tests__/sdui-render.test.tsx similarity index 94% rename from ui/packages/sdui-parser/src/__tests__/render.test.tsx rename to ui/packages/react/src/__tests__/sdui-render.test.tsx index 135c1817797..a2c1943d72d 100644 --- a/ui/packages/sdui-parser/src/__tests__/render.test.tsx +++ b/ui/packages/react/src/__tests__/sdui-render.test.tsx @@ -6,8 +6,8 @@ import { beforeAll, describe, expect, it } from 'vitest'; import React from 'react'; import { renderToStaticMarkup } from 'react-dom/server'; import { ComponentRegistry } from '@object-ui/core'; -import { SchemaRenderer } from '@object-ui/react'; -import { compile, manifestFromConfigs } from '../index.js'; +import { SchemaRenderer } from '../SchemaRenderer'; +import { compile, manifestFromConfigs } from '@objectstack/sdui-parser'; const Kids = ({ nodes }: { nodes?: unknown[] }) => ( <>{(nodes ?? []).map((n, i) => (typeof n === 'string' ? n : ))} diff --git a/ui/packages/runner/vite.config.ts b/ui/packages/runner/vite.config.ts index 4664e4865ff..f58dfa22447 100644 --- a/ui/packages/runner/vite.config.ts +++ b/ui/packages/runner/vite.config.ts @@ -108,8 +108,8 @@ export default defineConfig({ // Reached transitively through the packages above — see the closure note. // `@object-ui/i18n` <- react/src/index.ts, components/src/lib/close-label.tsx "@object-ui/i18n": path.resolve(__dirname, "../../packages/i18n/src"), - // `@object-ui/sdui-parser` <- components/src/renderers/layout/page.tsx - "@object-ui/sdui-parser": path.resolve(__dirname, "../../packages/sdui-parser/src"), + // `@objectstack/sdui-parser` <- components/src/renderers/layout/page.tsx + "@objectstack/sdui-parser": path.resolve(__dirname, "../../../packages/sdui-parser/src"), // `@object-ui/react-runtime` <- components/src/renderers/layout/react-page.tsx "@object-ui/react-runtime": path.resolve(__dirname, "../../packages/react-runtime/src"), // `@object-ui/fields` <- plugin-kanban/src/ObjectKanban.tsx diff --git a/ui/packages/sdui-parser/CHANGELOG.md b/ui/packages/sdui-parser/CHANGELOG.md deleted file mode 100644 index b984bfe5573..00000000000 --- a/ui/packages/sdui-parser/CHANGELOG.md +++ /dev/null @@ -1,1146 +0,0 @@ -# @object-ui/sdui-parser - -## 18.1.0 - -## 18.0.0 - -### Major Changes - -- 6f3299f: objectui moves to major 18, following `@objectstack/*` from 17 to 18 (objectui#12334). objectui's major is not a count of its own breaking changes: it tracks the major of the `@objectstack/spec` its packages depend on, so that the same major means compatible across the two projects. These packages already declare `@objectstack/*` on the 18 line (objectui#12030), so every package in the fixed release group crosses to 18 together. It is the one `major` that rule admits: objectui's own breaking changes ship as `minor`, described in their entries. - - The 18 line is released from Changesets pre mode on the `next` tag (`.changeset/pre.json`), as `@objectstack/*` 18 is: these releases are `18.0.0-next.N` on the npm `next` dist-tag, and they do not move `latest`. The first `18.0.0` on `latest` comes when `@objectstack` 18 is generally available and this repository leaves pre mode. - - What a consumer must do to take it is in the `@objectstack/*` 18 entry of this release (objectui#12030): move its own `@objectstack/*` packages to the 18 line at the same time. - -### Minor Changes - -- c4c506b: **Clause-②: yes (narrowing)** - - One declaration of the per-type NODE SLOTS — the keys other than `children` through which a renderer hands authored nodes back to `SchemaRenderer` — and three readers that walk it instead of stopping at `children` (objectui#11170, the follow-up PR #11126's Acceptance notes filed). - - New on `@object-ui/types`, beside `BaseSchema.children`: `NODE_SLOT_DECLARATIONS` (one row per renderer, under every registry spelling that resolves to it), `nodeSlotsFor(type)`, `nodeSlotPathSegments(path)` and `nodeSlotValues(node, path)`, with the types `NodeSlotDeclaration`, `NodeSlotRow`, `NodeSlotSegment` and `NodeSlotValue`. A position is spelled as a key path — `trigger`, `items[].content`, `regions[].components`, `items[]`, `report.sections[].content` — and the value at its end is one node or a list of nodes. The `page:*` rows are `@objectstack/spec`'s `pageComponentSlotPositions()` placed on the type whose renderer reads each position, pinned against that export in both directions; every other row is objectui's own, pinned against the live renderer. `body` stays retired as the generic child-list key (objectui#6771): it appears only on the four `page:*` types whose renderer still paints it for stored documents, marked `retired`. - - Accept sets that narrow, each reader FROM → TO: - - - `@object-ui/cli` — `objectui check`'s unevaluated-expression refusal (`findUnbindableTextExpressions`). FROM: the document root and every node its `children` hold. TO: those, and every node under a slot its type declares — so a `${…}` on `title` / `label` / `value` / `description` of a node under a dialog's `content`, a tab item's `content`, a page's `regions[].components`, a carousel item, a detail view's `tabs[].content` is now refused with the slot path (`items → 0 → content → value`). The false-refusal rows of PR #11126's ablation 2 stay green: a form's `fields[]`, a grid's `columns[]` and `{ "type": "multiple" }` are not slots. Measured over this repository's own JSON corpus and docs fences: no new finding. - - `@object-ui/core` — `validateSchema`. FROM: `validateChildren` recursed through `children` only. TO: it also recurses through the declared slots, so an invalid node under one (a retired `crud` spelling under `dialog.content`, an `INVALID_SCHEMA` member) is reported with its own path, spelled as `schema.items[0].content`. Measured over the same corpus: no new finding. - - `@object-ui/sdui-parser` — `validateTree`. FROM: the walk descended `children` alone, and a manifest entry carried no slot. TO: `ManifestComponent` gains `slots?: readonly string[]`, `manifestFromConfigs` gains `opts.slotsFor` (hand it `nodeSlotsFor`) and projects each entry's non-retired positions, and `validateTree` descends them — an unknown component, an unknown or mis-typed prop or an illegal enum under a slot now draws its diagnostic. A manifest built without the option serialises byte-identically and keeps the `children`-only reach. The `RETIRED_CHILD_LIST_KEY` refusals are unchanged. - - `@object-ui/components` — the `kind:'html'` page's compile manifest (`getJsxManifest`) is built with `slotsFor`, so an html-tier page whose slot-held node fails validation now fails to compile the way one under `children` does. Narrowing: a page that compiled with an unknown tag under a `dialog`'s `content` no longer does. - - Docs: `content/docs/utilities/cli.mdx`'s "Component nodes only" rule, the gate's own docblock, `validateChildren`'s comment and the parser's header now say the walk follows `children` and the declared slots; the declaration's header is where the slot list is explained. -- d92b2a1: The metric sub-caption is retired on the reader side (objectui#11389, ruling C, the objectui half after `@objectstack/spec` 17.7.0). **BREAKING** for any dashboard that drew a caption under a metric's value. - - A metric tile used to draw a sub-caption under its value from the widget's `options.description`, translated by a client bundle entry at `dashboards.NAME.widgets.ID.subCaption`. The spec never declared that options key, and its only writer was the server's `translateDashboard` overlay. `@objectstack/spec` 17.7.0 removed the overlay and refuses a `subCaption` translation entry by name. objectui now stops reading both: - - - **`@object-ui/plugin-dashboard`.** No metric tile draws a sub-caption, on either dashboard surface (`DashboardRenderer`, `DashboardGridLayout`), whether the tile is dataset-bound or a stored inline metric. An authored `options.description` (a string or a per-locale map) draws nothing, and neither does a bundle `subCaption` entry. A widget's one authored description, `widget.description`, still draws as the card-header subtitle on `DashboardRenderer`, translated through the widget's `description` bundle key. Nothing on the package entry is removed: the sub-caption resolver module and `DatasetWidget`'s `subCaption` prop were internal. - - **`@object-ui/i18n`.** `useObjectLabel()` no longer returns `widgetSubCaption`. This removes a member of a published hook's return value: a caller that destructured it no longer compiles, and has nothing to call instead, because the key it read is refused by the spec. - - **`@object-ui/sdui-parser`.** `CONSUMED_WIDGET_OPTION_KEYS` drops `'description'` and is now exactly the five keys the spec declares (`dateGranularity`, `limit`, `sortBy`, `sortOrder`, `stageOrder`). So `validateTree` reports an authored `options.description` on a dataset-bound dashboard widget as an `unconsumed-widget-option` **warning**, where it used to report nothing. It is a warning, not an error, and the widget's `suppressWarnings` escape hatch still applies. - - **Clause-②: no (narrowing).** One export member is removed (`useObjectLabel().widgetSubCaption`) and one exported constant loses a member (`CONSUMED_WIDGET_OPTION_KEYS`). Nothing is added and no accepted input widens. - - If a caption under a metric's value is wanted again, it returns as a declared widget-level key outside `options`, not as `options.description` (ruling C). -- 35a2b59: The node type of the tree this package's parser produces is declared and exported as `ParsedSchemaNode` instead of `SchemaNode` (objectui#6349, batch 11). `@object-ui/types` publishes a different `SchemaNode`: the closed authoring union, discriminated on a literal `type`, with no index signature and with numbers, booleans and `null` admitted as children. The parser's node is an open, as-parsed `SchemaElement` (any `type` string, the keys as authored) or a text string, and neither type is assignable to the other. One exported name stood for both across the two packages, and types keeps it. - - **Type change, breaking for some consumers.** `SchemaNode` is no longer exported from `@object-ui/sdui-parser`. Replace `import type { SchemaNode } from '@object-ui/sdui-parser'` with `ParsedSchemaNode`; it is the same type (`SchemaElement` or a string). The old import fails to compile with TS2305 ("has no exported member 'SchemaNode'"). `SchemaElement`, `ParseResult` and every function keep their names and shapes. - - No runtime behaviour changes. - -## 17.7.0 - -### Minor Changes - -- baac95a: feat(core,sdui-parser): the published manifest declares the html tier's registered intrinsic elements, marked `tier: 'html'` — `div` stays out - - A `kind:'html'` page may author the everyday HTML tags its renderer registers — - the flow/inline set of `html-elements.tsx` (h1–h6, p, a, lists, emphasis, - figure/img/hr/br, time, address, cite, q), `span`, `table`, `label` and the seven - sectioning tags — and the console has rendered such pages all along. The - published `sdui.manifest.json` never said so: it was the curated `PUBLIC_BLOCKS` - vocabulary alone, so the objectstack CLI gate, which whitelists an html page's - tags from that manifest, refused every plain HTML tag as `forbidden-tag` on - pages the renderer accepts (200 ledgered findings over the three shipped showcase - pages). The maintainer ruled A on objectstack#20112: the producer declares the - set, exactly as the registry declares it, and `div` stays deprecated. - - **What moves for a consumer.** `ComponentRegistry.getPublicConfigs()` — the - read both manifest producers serialise — now returns the curated tier AND the - html tier's intrinsic elements, the latter from the new `HTML_TIER_INTRINSICS` - roster (`@object-ui/core`) and each stamped `tier: 'html'` in the projection. - `manifestFromConfigs` carries that one value into a new optional - `ManifestComponent.tier` (`@object-ui/sdui-parser`), so the regenerated - `sdui.manifest.json` grows by the roster (47 entries, each with its registered - `inputs` and its child slot declared exactly where the registration renders a - child list — the void tags `img` / `hr` / `br` declare none) and - `sdui-intrinsics.d.ts` types them. A reader that whitelists tags by key — the - objectstack gate — needs no change and accepts the tags. A reader that means the - CURATED vocabulary filters on the stamp: `generateBlockList` now sections the - html tier under its own count, and the `kind:'react'` JSX scope skips stamped - entries (`@object-ui/components`, `react-page.tsx`), so no `P` / `A` / `Img` - wrapper is injected into react pages — on that tier a lowercase `

` is React's - own element. - - **What does not move.** `PUBLIC_BLOCKS` is unchanged and the two rosters are - pinned disjoint; every curated manifest entry serialises byte-identically - (`tier` is written as exactly `'html'` or omitted). `div` is not declared: the - gate keeps refusing `

` on an html page, and `box` remains the JSON - surface's replacement. `code` is not declared either — the bare `code` key is - the `field:code` widget's namespace fallback, not an element renderer — nor is - `kbd`; both are recorded, with reasons, in the console's exclusion ledger. - No registration changes; `html-elements.tsx` is untouched. - - ⚠️ **Dated note, 2026-09-27 — `code` has since joined the roster — objectui#10756.** At this change the roster held 47 tags and `code` was undeclared because the bare key was the `field:code` widget's fallback; `html-elements.tsx` now registers `code` as a sanitised passthrough (`ui:code`), `@object-ui/fields` registers the widget with `skipFallback` so it keeps only `field:code`, and the manifest declares `code` with `tier: 'html'` — 48 html-tier entries. `div` and `kbd` are still out. The rest of this entry is kept as the reading of this change. - - The manifest the framework ships regenerates from objectui's built tree at the - pin (its `gen-sdui-manifest-node.mjs`); its lockstep copy of `manifestFromConfigs` - must take this port for the stamp to reach that file — until then the tags are - declared there without the marker, which the gate treats identically. -- c80236e: The base-prop list is declared once, and the renderer's `visibleWhen`, `hiddenOn` and `testId` join it (objectui#11044). - - `validateTree` and `generateDts` each kept their own hand-written list of the base props: the `BaseSchema` keys a node may carry without its registration declaring them. The two lists had drifted. After objectui#11008 the validator accepted `bind` and `hidden` on every node, while the generated JSX types (`SduiBaseProps` in `sdui-intrinsics.d.ts`) still refused both (`TS2322 … Property 'bind' does not exist`). Both now read one exported list, `SDUI_BASE_PROPS`, and the generated `SduiBaseProps` interface is emitted from it. - - Three changes to what is accepted come with it: - - - **`visibleWhen`, `hiddenOn` and `testId` are base props of every node.** `BaseSchema` declares all three, no registration declares any of them, and `SchemaRenderer` reads them for every node. It evaluates `visibleWhen` and `hiddenOn` in its hide chain, and it re-emits `testId` as `data-testid`. `validateTree` answered each with an `unknown-prop` warning on every registered type. Now none of them draws a diagnostic, and the generated JSX types accept them, along with `bind` and `hidden`. The canonical ADR-0089 predicate, `visibleWhen`, used to warn while the deprecated `visibleOn` was silent. A near-miss spelling such as `testid` still draws `unknown-prop`. - - **`name`, `label`, `description`, `placeholder`, `data` and `ariaLabel` are base props where a type does not declare them.** Some registrations declare each of these `BaseSchema` members as a typed input. On those types the declared input wins, and a wrong-typed value still draws `type-mismatch`. On every other type the key no longer draws `unknown-prop`. In the generated JSX types, an interface that declares one of these keys extends `Omit` for it, so its declared type is the one an author compiles against. - - **The cost is named.** On a type that does not declare one of the six keys, the parser tier no longer reports it, whether or not the renderer reads it. For example, `description` on `page-header` is the retired alias of `subtitle` (objectui#3226), and it used to draw `unknown-prop`. It now draws nothing. - - `SDUI_BASE_PROPS` and its `SduiBaseProp` / `SduiBasePropScope` types are new exports. Each entry names a `BaseSchema` member, the scope it is a base prop in (`'every-node'` or `'where-undeclared'`), and its type in the generated JSX surface. - - ⚠️ **Dated note, 2026-10-02 — the `page-header` example no longer applies — objectui#10859.** - Later in this same release objectui#10859 batch 8 (phase 2c) unregistered `page-header`, so a - `page-header` node now draws `unknown-component` before any prop check runs. The cost this entry names - is unchanged for every type that is still registered. The rest of this entry is kept as the reading of - this change. -- 4d377ed: The generated JSX types let a declared input win with its type for every base prop, so `sdui-intrinsics.d.ts` compiles as generated (objectui#11075). - - `generateDts` over the real public manifest produced a `.d.ts` that did not compile under `tsc --strict` without `skipLibCheck`: `TS2430 Interface 'RecordAlertProps' incorrectly extends interface 'SduiBaseProps'`. `record:alert` declares `visible` as a boolean, a bare CEL string or a `{ dialect: 'cel', source }` envelope, and its renderer honours all three. `visible` is an `'every-node'` base prop typed `boolean` in `SduiBaseProps`, and objectui#11044's `Omit` covered only the `'where-undeclared'` members. An author compiling against the file got an error inside the generated file instead of a check on their own JSX. With `skipLibCheck` on, the base `boolean` hid the string and envelope arms. - - Now an interface `Omit`s every base attribute its registration declares, whatever the member's scope, and types it with the declared type: - - - **`` accepts `visible` as a string and as the `cel` envelope** in the generated types, as the manifest declares and the renderer reads. A number is still refused. - - **Where a registration declares a base attribute with the base type** (`className?: string` on most html-tier tags and many blocks, `disabled?: boolean` on `element:button`), the interface extends `Omit` for it too. What those interfaces accept does not change. - - **`SduiBaseProps` itself is unchanged**: `visible`, `disabled` and `hidden` stay `boolean` on every type that does not declare them. - - `validateTree` is unchanged: it still skips an `'every-node'` key before the declared-input lookup. -- 0db4fb3: html tier: a braced attribute value that is not strict JSON now draws a `inert-expression` warning instead of vanishing silently (objectui#6598) - - `interpretBrace` materializes strict-JSON values only; anything else — the - single-quoted array every JSX author writes (`columns={['name','amount']}`), - unquoted object keys, any JS expression — compiles to the deferred `{ $expr }` - marker, and nothing downstream evaluates that marker: the html tier parses, - never executes (ADR-0080), and no renderer consumes `$expr`. The value reached - the renderer as an opaque object, defensive non-array/non-object reads degraded - it to "not declared", and the author's binding vanished with zero diagnostics - anywhere — a production page's `list-view` rendered its row count and toolbar - with no data columns, through eight `columns` spellings (objectui#6598, moved - from objectstack#12649). That is ADR-0078's prohibited parsed-but-silently-inert - state. - - `validateTree` now emits a warning-severity `inert-expression` diagnostic when a - declared input's value is the `$expr` marker, with the fix in the message: write - the value as JSON (double-quoted strings and keys). Warning, not error, per the - `8d58f46b4` posture for inert authored keys — pages keep compiling and - rendering exactly as before; the silence is what changed. Escalating the - severity, widening the accepted literal grammar (e.g. materializing - single-quoted strings), and covering base props like `style` are contract - decisions deliberately left on objectui#6598. -- 4703651: html tier: braced attribute values now materialize the JS literal subset — single-quoted strings and unquoted identifier keys work (objectui#6614) - - The html tier is the untrusted-safe DATA tier: source is parsed, never executed - (ADR-0080), which makes it the only safe carrier for runtime AI- or - tenant-authored pages. But `interpretBrace` accepted only strict JSON inside - braces while the surface called itself JSX, so `columns={['name','amount']}` — - the spelling every JSX author and every AI author writes by habit — compiled to - the deferred `{ $expr }` marker that nothing downstream evaluates, and the - author's whole data binding vanished at render. A production page's `list-view` - rendered its row count and toolbar with zero data columns through eight - `columns` spellings before the author gave up (objectui#6598, moved from - objectstack#12649). That was a trap, not a contract. - - `interpretBrace` now materializes the JS **literal subset**. Exactly two - widenings over JSON, and nothing else: - - 1. **single-quoted strings**, in value position and in key position — - `title={'Accounts'}`, `columns={['name','amount']}`, `{{'pageSize': 25}}`; - 2. **unquoted identifier object keys** — `options={{pageSize: 25}}`, - `columns={[{field:'name',label:'Full Name'}]}`. - - Everything else JSON refuses is still refused, still compiles to `{ $expr }`, - and still draws the warning-severity `inert-expression` diagnostic: trailing - commas, comments, array holes, spreads, `undefined`/`NaN`/`Infinity`, - `+1`/`.5`/`1.`/`0x1f`, template literals, and every genuine expression — - identifiers, member access, calls, operators, ternaries. The subset contains no - identifier lookup and no operator, so there is nothing in it to execute: this - moves habitual spellings onto the materialized side, it does not move the - boundary between data and code. An authored `__proto__` key becomes an ordinary - own property, as `JSON.parse` gives it — never the prototype setter. - - Strict JSON is unchanged, structurally: `JSON.parse` still runs first and - untouched, so any value it accepts takes byte-identically the path it always - did, and the literal reader only ever sees input `JSON.parse` has already - thrown on. - - The `inert-expression` message changed with the grammar. It used to advise - "write it as JSON (double-quoted strings and keys)" and named - `columns={['name','amount']}` as the wrong form — advice that would now send an - author to edit working source. It names the accepted literal grammar instead. - - Maintainer ruling of 2026-08-28 (objectui#6614 Q1-A). ⛔ Two ruled items are - deliberately NOT in this change: escalating `inert-expression` from warning to - error (Q2 — it belongs at the save gate, once the framework wires the registry - manifest into `validate-jsx-pages`), and base-prop (`style`) `$expr` inertness - (Q3 — sequenced after this, so no warning is added for spellings this change - legalises). -- 2acd8e1: **BREAKING (authoring surface): `body` is no longer a child-list key. Author `children`.** - - `BaseSchema` declared two spellings of one concept — `body` and `children` — and left the - choice per component. **Thirteen** registrations read `body` and nothing else, so it was - their ONLY door — the twelve the ruling enumerated plus `tooltip`, which it did not, and - which is the count the committed instrument carries (`BODY_ONLY` plus - `BODY_ONLY_UNRULED` in `scripts/body-dialect-census.mjs`). `div`, `card`, `page`, - `button`, `aspect-ratio`, the seven sectioning tags and the safe-HTML tag factory read - `children || body` and took either. The authoring tier only ever knew `children`, so - an author writing the one spelling that resolved got `unknown-prop` — the same warning a - typo draws, on the tier built to accept AI-authored pages, where the diagnostic **is** the - contract (objectui#6771). - - Ruled 2026-09-01: one concept, one spelling, and the spelling is `children`. - - ## What changed - - - **Renderers.** `alert`, `badge`, `tooltip` and the `sidebar-*` family read `children`. - Every `children || body` fallback drops its `body` arm **except the four `page:*` - reads named below** — `div`, `card`, `button`, `aspect-ratio`, the sectioning tags, - the safe-HTML tag factory behind ~36 tags, `page`'s flat content list, and - `@object-ui/core`'s recursive `validateSchema`. - - **Item-level `body` is a DIFFERENT key and is untouched.** At this change, `list` - draws each entry as `item.content || renderChildren(item.body)` and `tabs` as - `item.content || item.body`, both filed under the ITEM type rather than the node, - and `tabs` still ships `body` inside its own `defaultProps`. ⛔ Neither is this - spelling and neither is refused here: the node-level retirement does not reach a - member of a declared `items` array. Retiring the item-level dialect is - objectui#9590's card, and the two named above are recorded on it. - ⚠️ **Dated note, 2026-09-25 — neither half of this bullet holds any longer — objectui#9590.** - objectui#9941 respelled the `tabs` `defaultProps` items to `content`, and - objectui#9590 retired both item-level reads: a `list` item and a `tabs` item draw - `content` and nothing else, and both item faces now refuse `body` by name, - pointing at `content`. The rest of this bullet is kept as the reading of this change. - - **What still reads `body` at NODE level, and why.** Four renderer reads, all `page:*`: `page:card` - (renderer and Studio canvas) and the three thin `page:section` / `page:footer` / - `page:sidebar` containers. ⛔ Authoring the key is refused on them as it is - everywhere else; only the READ survives, for documents already STORED under it. - `@objectstack/spec` states the ground on `PageContainerProps` itself: «The renderers - keep reading `body` as a back-compat fallback for stored documents; that fallback is - objectui's to retire on its own schedule, and it is not a second authorable - spelling.» ⚠️ What separates the two halves is a CONVERSION, not a ground: the spec's - registry carries `page-card-body-to-children` (`toMajor: 17`, surface - `page.component.page:card.body`, shipped `retiredFromLoadPath: true` in spec 17.0.0) - and carries none for the other three — so a stored row under their key has no - migration path at all. Nothing in this repository authors any of them, and the - committed census over those keys is how to see that rather than take it on trust: - `node scripts/body-dialect-census.mjs --keys page:section,page:footer,page:sidebar,page:card,card`. - ⚠️ This sentence used to restate that table's answer instead of pointing at it, and - the very commit that edited the sentence moved the figure — a count copied into prose - is what AGENTS.md #9 forbids, so the instrument replaces the number. Read the table - for three things: `card` + `body` is a control that FIRES on the head that ships (so - a zero elsewhere is a reading, not a blind spot), every node still carrying `body` - under these keys falls in the `test` bucket, and `page:section` is a LIT zero — 6 - nodes resolved, 0 `body`, 4 `children` — rather than a key nobody asked about. - Retiring the THREE THIN reads is objectui#9916, - which asks for the stored-corpus reading they turn on; `page:card`'s read is not - that card's either — it is objectui's own, grounded by objectstack#5775 / - ADR-0087 D2 rather than owned by it, and it ends when the stored rows have been - replayed through the conversion that already shipped for it. - - **The published type.** `BaseSchema.body` is `never` on the TypeScript face and an - alias refusal naming `children` on the Zod mirror, as are the four per-component - redeclarations (`CardSchema`, `AspectRatioSchema`, `PageNodeSchema`, `TooltipSchema`). - ⚠️ Refused **by name**, not deleted: `BaseSchema` carries an index signature and the - mirror ends `.passthrough()`, so a deleted member would be accepted silently and - rendered by nothing — the exact silence this retirement ends. - - **The VS Code extension (`object-ui`) changes behaviour, not just teaching.** Its - preview no longer draws a `body`-spelled document — the node renders empty, the way - the runtime renders it — and its validator now emits a warning naming `children` at - the key's own position. That warning is skipped for node types that declare their - own `body` input (`record:alert`), so a declared translation-map `body` is not - reported as the retired spelling. - - **Two new `@object-ui/sdui-parser` exports.** `RETIRED_CHILD_LIST_KEY` (the retired - spelling, so a consumer names it once) and `checkRetiredBodyDialect` (the diagnostic - the tier substitutes for `unknown-prop` on that key). - - **The authoring tier.** `sdui-parser` answers an authored `body` with the replacement - named, instead of the bare "has no prop" every typo gets — and a `body` child list - under a non-container draws the same containment code the `children` spelling draws. - - **Corpus and teaching, in the same change.** 114 authored nodes across the docs site, - the schema catalog, five package READMEs and the VS Code extension's snippets, JSON - schema, syntax map, hovers and completions. The platform does not refuse a spelling it - still ships. - - ## Migrating - - Rename the key. `{ "type": "card", "body": [...] }` becomes - `{ "type": "card", "children": [...] }`; both faces now name `children` in the refusal, - and `pnpm census:body-dialect` reports where the dialect still lives in a tree. - - ⚠️ **Four renderer reads are deliberately untouched, all in the `page:*` namespace** — - `page:card` and the three thin `page:section` / `page:footer` / `page:sidebar` - containers. They are read-only back-compat paths for STORED documents, not a second - authoring face: `@objectstack/spec`'s `PageContainerProps` says so itself — `children` - is canonical, `body` is deliberately not declared, and «the renderers keep reading - `body` as a back-compat fallback for stored documents; that fallback is objectui's to - retire on its own schedule». - - ⛔ **Authoring `body` on them is still refused**, on both published faces and at the - authoring tier, exactly as everywhere else. What survives is the READ. - - ⚠️ The two halves are not in the same state. `page:card` has a migration path — the - spec's conversions registry carries `page-card-body-to-children` (`toMajor: 17`, - surface `page.component.page:card.body`), which shipped with `retiredFromLoadPath: true` - in spec 17.0.0. The other three have **no conversion at all**, so a stored row under - their key has nowhere to be migrated to. ⇒ the two halves are tracked separately, and - the pointer is not one card: retiring the three thin reads is objectui#9916, whose - question is the stored corpus; retiring `page:card`'s read was **grounded by** - objectstack#5775 / ADR-0087 D2, whose conversion has already shipped and whose - remaining condition is replayed rows. ⚠️ Grounded by, not owned by — the distinction - is the point. objectstack#5775 is CLOSED and its body is a props-declaration audit - that carries no such step, so a reader sent there for the action finds none. The - action is objectui's, on the spec's own terms quoted above: «that fallback is - objectui's to retire on its own schedule.» - - **Non-rendering readers keep their arm on the same rule**, and none of them renders - anything: while a renderer still reaches stored `body` content, a reader that must see - the SAME content keeps its arm, or the renderer draws what the reader cannot find. - Those are the two tab-subtree walkers in `renderers/layout/containers.tsx`, - `app-shell`'s `pageSchemaIntrospect` (`CONTAINER_KEYS`) and `PageBlockInspector` - (`STRUCTURAL_PROP_KEYS`, the inspector half of a stored `properties.body`), and the - CLI's `OBJECTUI_STRUCTURAL_KEYS` — a file-IDENTIFICATION marker, where keeping `body` is - what lets an old file still be recognised as an ObjectUI node and therefore refused, - instead of silently not judged. - - ⚠️ **Dated note, 2026-10-02 — the `sidebar-*` parts are retired — objectui#10859.** - Later in this same release objectui#10859 batch 8 (phase 2d) unregistered the ten `sidebar-*` part keys. Of the - family the **Renderers** line names, only `sidebar` remains, and it reads `children`. The rest of this entry is - kept as the reading of this change. -- 9e37d9b: `binding` on a component input is framework-set, not author-declared: `@object-ui/types` gains `InjectedComponentInput`, the `'field'` binding arm is retired from `@object-ui/sdui-parser`'s `RegistryConfigLike`, and the `as ComponentMeta` cast at the injection seam in `@object-ui/core` is gone (maintainer ruling of 2026-09-07, director decision batch #69; ADR-0049 enforce-or-remove). - - **What was measured.** `binding` was published — the manifest serializer forwards it — and read — `validateTree` records a binding site for it — while `ComponentInput`, the authoring type every registration writes against, did not declare it. The one writer in the tree, `ELEMENT_DATA_SOURCE_INPUT`, therefore carried a hand-written inline type and reached a registration's `inputs` through `as ComponentMeta` in `Registry.register`. Declared narrower than enforced, on a published type — and `ComponentInput`'s own docblock listed `binding` among the forwarded per-input keys. - - **The ruling** answered the product question the card asked — may an ordinary registration declare a binding input? — with no. So: - - - **`@object-ui/types`** exports `InjectedComponentInput`, an `interface … extends ComponentInput` with the required marker `binding: 'object'`. `ComponentInput` itself does not change: no member is added, and authoring `binding` on a registration stays an excess-property `tsc` error — now on purpose and documented at the interface. The two tombstone docblocks that listed `binding` as a forwarded key now say it is forwarded from the framework's injected input, not authored. - - **`@object-ui/core`** types `ELEMENT_DATA_SOURCE_INPUT` as `InjectedComponentInput` and splices it through a typed local; the cast is gone. Runtime behaviour is unchanged — the same key, `type`, `binding` and `description` reach the manifest, and `validateTree` still records the binding site. - - **`@object-ui/sdui-parser`** narrows `RegistryConfigLike.inputs[].binding` from `'object' | 'field'` to `'object'`. The `'field'` arm had zero writers — every `binding:` literal in `packages/`, `apps/` and `examples/` is `'object'`, 7 of 7 at this change's merge-base — and nothing on either side of the manifest resolved a field binding. **Breaking, deliberately:** a config that feeds `manifestFromConfigs` a `binding: 'field'` input is now a type error instead of a manifest entry the server would never resolve. `ManifestInput.binding`, the manifest reader's vocabulary, is not narrowed by this change. - - If a real need for author-declared bindings is ever measured, it is filed as a widening of `ComponentInput` with the vocabulary decided then — not by putting the cast back. -- 492bbd8: Refuse an authored `type=` attribute on the `kind:'html'` tier (objectui#7235 — the - port of objectstack#13957, maintainer ruling 2026-09-01, recorded as an append-only - amendment on ADR-0080). - - **Breaking, deliberately, and the direction is a narrowing.** On this tier the tag - name *is* the node's `type`, so an authored `type=` attribute is a name collision - with the envelope's own discriminator. It is now one `forbidden-attr` error naming - both the tag and the attribute, and the node is composed as `{ ...props, type: tag }` - so the tag always wins the slot. - - Two outcomes it replaces, the first of which produced no diagnostic at all: - - - the value named another **registered** type (``) — the tree - carried `type:'grid'`, the manifest resolved it, every check passed, and a grid - rendered where the author wrote a flex. Zero diagnostics. - - the value named **nothing registered** (``) — loud, but - `unknown-component` naming `"bar"` read as a missing plugin rather than as an - attribute that should not be there. - - The existing `forbidden-attr` code is reused rather than a new one minted: the two - copies of this parser (this one, which the renderer and the console's live edit - preview run, and objectstack's, which the save gate runs) are held to one diagnostic - vocabulary by `check:sdui-lockstep`, and a code minted on one side only is the dialect - split that gate exists to catch. - - ⚠️ Not carried over: the react tier's `specType` rescue (objectui#2880). The ruling - declined it by name; that rescue stays where it lives. - - One visible non-behavioural consequence of the reversed spread: a compiled node's - `type` key is now **last** rather than first in insertion order, so `JSON.stringify` - of a parsed tree emits the same pairs in a different order. The key set, every value, - the children and the html-tier provenance marker are unchanged. -- cf1d29e: `ComponentInput.of` — the coarse kind of an input's MEMBERS, with readers on day one - (objectui#8067). - - A registration's `type: 'array'` said a value was a list and stopped there, so a member - that drifted from `@objectstack/spec` was invisible to every layer that reads a - declaration. `page:header.actions` is the measured cost: the contract declares - `z.array(z.string())` ("Action IDs"), the renderer read the members as `ActionDef` - objects, and the repo-wide parity gate in - `apps/console/src/__tests__/registry-inputs-spec-parity.test.ts` stayed green for the - whole life of the drift because both sides carried the key and neither could say what - was inside it. What settled it was a maintainer ruling, not a test — and even after the - fix, "these are ids" survived only as English in the registration's `description`. - - **What is new.** `ComponentInput` gains an optional `of`, carrying the same coarse-kind - vocabulary as `type` one level down: the ELEMENTS of an `array`, or the VALUES of an - `object` used as a map. One kind, or an array of them for a member contract that is a - union, with `type`'s semantics — a member passes when any declared arm accepts it. The - manifest serializer forwards it, so `sdui.manifest.json` now carries seven keys per - input instead of six. - - **Three readers ship with it**, which was the bar this slot had to clear (objectui#5905 - is the precedent: five `ComponentInput` keys declared and read by nothing). The - repo-wide parity gate compares every declared `of` against the member kind - `ComponentPropsMap[type]` actually accepts and fails on one the contract refuses; - `sdui-parser`'s `validateTree` reports a member that fits no declared kind, as a new - `member-type-mismatch` diagnostic naming the offending positions; and the generated - `sdui-intrinsics.d.ts` narrows the authoring type — `page:header`'s `actions` is - `string[]` where it used to be `unknown[]`. - - **Fifteen keys now declare one**, across ten blocks, each DERIVED rather than chosen: - every container key's member position was probed with one value of each coarse kind and - a declaration written only where exactly one kind was accepted. A member contract that - admits several kinds — `record:highlights.fields` takes a field name or an inline field - object — is deliberately left undeclared and pinned with its reason, because picking one - arm there is a narrowing this repo leaves un-gated and picking all of them would - advertise shapes only a per-block pin can vouch for. - - **The ceiling is unchanged.** `of` is a KIND and never a value domain, so the maintainer - ruling of 2026-08-17 quoted on `ComponentInput.type` — the coarse arm plus `description` - is the publication face's expression ceiling, and spec is the sole judge of values — - stands exactly as written. `of: 'object'` says the members are objects; which keys they - carry is still `description`'s job and `os validate`'s. - - **Nothing published before this changes.** An input that declares no `of` validates, - serializes and types byte-identically: `validateTree` checks no member, the serializer - emits no key, and the codegen emits the same `unknown[]`. -- 80cb063: html tier: name the inert `quickAdd` on `object-kanban` / `kanban` instead of calling it unknown - - The Quick Add control is gated on BOTH `quickAdd` and an `onQuickAdd` handler, and - `onQuickAdd` takes a function, which no parsed page can write and which `ObjectKanban` - supplies none of its own for. objectui#11234 retires `onQuickAdd` on `object-kanban` in the - same release; it remains a host-supplied prop on `KanbanRenderer`'s `schema`. So an authored - `quickAdd: true` on either - `ObjectKanbanRenderer` tag has never produced a control. - - Until now the only thing the tier said about it was `unknown-prop`, "has no prop - quickAdd" — false against `@objectstack/spec`, which publishes the key, and - indistinguishable from a typo. It now draws an `inert-quick-add` **warning** that names - the missing half of the pair and points at `kanban-ui`, where a React host can supply - the function. Severity is unchanged (warning, as `unknown-prop` was), so no page that - saves today stops saving. - - New exports: `checkKanbanQuickAdd`, `INERT_QUICK_ADD`, `QUICK_ADD_HOST_TYPES`, - `QUICK_ADD_KEY`. - - Interim by ruling (objectui#8285, decision batch #91): the contract-side refusal is - retiring `object-kanban.quickAdd` from the spec's `ComponentPropsMap`, and this - diagnostic is removed by the change that lands it. `kanban-ui` keeps the - `quickAdd` / `onQuickAdd` pair untouched. - - ⚠️ **Dated note, 2026-09-29 — the contract moved in this same release — objectui#11073.** - The reason given above, that "has no prop quickAdd" was false because `@objectstack/spec` publishes the key, no longer holds. Later in this release this repository began resolving `@objectstack/spec` 17.5.0, which carries the retirement this diagnostic was declared interim for: `object-kanban.quickAdd` is now a spec tombstone, refused by name at the authoring door. The diagnostic's own removal belongs to the change that ruled it and is not made here. Until then, the key it names is one the contract itself refuses. -- 6f864cf: **BREAKING** — `quickAdd` is retired on `object-kanban` (objectui#8285, ruling B of - the director seat's decision batch #91: the board does not grow an inline - record-creation write path). This is objectui's half; `@objectstack/spec` 17.5.0 - already tombstones the same key in `ComponentPropsMap['object-kanban']`. - - **Why it never did anything.** The Quick Add control is gated on BOTH `quickAdd` - and an `onQuickAdd` handler. `onQuickAdd` is a host-supplied function that no JSON - document can carry, and `ObjectKanban` supplies none of its own, so an authored - `quickAdd: true` parsed green and drew nothing. - - - **`@object-ui/types`** — `ObjectKanbanSchema.quickAdd` is `?: never` on the - TypeScript face and a `retirementTombstone()` on the zod mirror: an authored - value is refused BY NAME (`invalid_type` at `quickAdd`, carrying the remedy), not - stripped. A tombstone rather than a deletion, because `BaseSchema` ends - `.passthrough()` and a deleted member would be kept silently. - - **`@object-ui/plugin-kanban`** — `ObjectKanban` no longer forwards `quickAdd` to - the board, on every `object-kanban` entry point (the registered tag, the - `kanbanComponents` map, a host mounting `ObjectKanban`). So even a document that - carries both halves of the pair draws no control. `KanbanRenderer` is unchanged: - a React host that mounts it directly still passes `quickAdd` and `onQuickAdd` on - its `schema` and gets the control. - - **`@object-ui/sdui-parser`** — the interim `inert-quick-add` warning is removed, - with its four exports `checkKanbanQuickAdd`, `INERT_QUICK_ADD`, - `QUICK_ADD_HOST_TYPES` and `QUICK_ADD_KEY`. It was declared interim until the - spec refused the key by name; that has landed, and with the key retired on every - face the tier's own `unknown-prop` warning ("has no prop quickAdd") is true and - is the whole remedy on a constrained-JSX page, which cannot write the handler - anyway. Severity is unchanged (warning), so no page that saves today stops saving. - - **Migration.** Delete `quickAdd` from `object-kanban` nodes; it never drew a - control there. To offer Quick Add, mount `KanbanRenderer` from - `@object-ui/plugin-kanban` in a React host and pass both `quickAdd` and - `onQuickAdd` on its `schema`. Code that switched on the `inert-quick-add` - diagnostic code, or imported one of the four removed names, drops that branch: - the key now draws `unknown-prop` like any other prop the block does not have. - - `ObjectKanbanSchema.onQuickAdd` is retired on `object-kanban` too, by objectui#11234 - in the same release: on that element it was never called, because its partner is - gone. The Quick Add pair on `KanbanRenderer` is unchanged. -- 483b794: Retire the `binding: 'field'` arm on the manifest READER and PRODUCER faces, so - `@object-ui/sdui-parser` states one vocabulary instead of two (objectui#8315). - - The 2026-09-07 maintainer ruling that `9e37d9b39` landed (director decision batch #69) - retired the zero-writer `'field'` arm under ADR-0049 enforce-or-remove, naming - **one** coordinate: `RegistryConfigLike.inputs[].binding`, the serializer's input - boundary, which PR #8297 narrowed. Two declarations in `types.ts` kept the arm, so - the published package narrowed on one face and stayed wide on the other: - - - `ManifestInput.binding` — now `'object'` - - `ManifestValidationResult.bindings[].kind` — now `'object'` - - **Breaking for TypeScript consumers, deliberately, and compile-time only.** A - hand-written `Manifest` literal or a `bindings[]` entry that spells `'field'` is now - a `tsc` error. Runtime behaviour is unchanged: types are erased, this package has no - runtime validator for a manifest it is handed, and `validateTree` still forwards - whatever a manifest says — a pin in - `src/__tests__/injected-component-input-6950.test.ts` states that limit so the - narrowing is not mistaken for a runtime rejection. - - **Migration.** Nothing measured has to change. `binding: 'field'` has zero writers in - this repo and zero in the objectstack copy of this package (measured 2026-09-09 on - both heads, each with a firing `binding: 'object'` control), and the only manifest - producer in either tree is `manifestFromConfigs`, whose input face was already narrow. - Every `binding: 'field'` spelling left under `packages/`, `apps/` and `examples/` is - an `@ts-expect-error` negative pin asserting the refusal, or a docblock mention of - the retirement — not a writer. - - **Why not leave the reader face wide.** The counter-argument — producer → reader is a - subset relation, so a permissive reader is not wrong — was answered rather than assumed - away. `ManifestInput` is not a pure reader face (`manifestFromConfigs` returns it), and - `bindings[].kind` is a pure **producer** face where the relation inverts: a wider union - there accepts nothing extra, it obliges every consumer to handle an arm this package - cannot emit. The two are coupled by `validateTree`'s `kind: input.binding` assignment, - so narrowing one alone would need a cast at the only conversion site — the lenient - fallback AGENTS.md #0.1 bans. The reasoning now lives on the declarations themselves, - where a later reader lands. The reopen route is the ruling's own: a measured need for - field bindings is filed as a widening with the vocabulary decided then. -- adf5812: Four node type keys retire, and the kanban and gantt families converge on their - `object-*` spellings: `kanban` (objectui#8802), `kanban-ui` and `kanban-enhanced` - (objectui#8257), and `gantt` (objectui#8008). All four were ruled by the - maintainer in one batch on 2026-09-09. - - **⛔ No stored document moves.** The strings `kanban` and `gantt` name two - different things at two different layers, and only one of them is retiring: - - | layer | value | who writes it | retired? | - | --- | --- | --- | --- | - | stored `NamedListView.type` | `"kanban"`, `"gantt"` | `CreateViewDialog`, persisted per tenant | **no — untouched** | - | node type key | `kanban`, `gantt` | hand-authored JSON | **yes** | - - `ObjectView`'s `switch (viewType)` maps a stored view type onto the node type it - renders, and it already emitted `object-kanban` and `object-gantt` — as it does - for all twelve stored view types. So every kanban and gantt view any user ever - created through the console already renders through the surviving spelling. - Nothing in a tenant database changes, and ⛔ nothing should be migrated there. - - **What each retirement was, measured.** Three of the four were - registration-only: no schema face in `@object-ui/types` ever declared - `kanban-ui`, `kanban-enhanced` or `gantt` as a component node type, so - unregistering is the whole retirement. The bare `kanban` key was the exception — - it had a declared arm on both faces (`KanbanSchema` in `complex.ts` and its Zod - mirror), and a plain deletion there would have been the objectui#7664 failure: - `BaseSchema` is `.passthrough()`, so a document naming a dropped key validates - green and renders nothing. It therefore retires as a **named refusal**: the Zod - union keeps an arm claiming the literal and answers a `{ "type": "kanban" }` - document with a message naming `object-kanban` as the remedy, while the - TypeScript half is the absence of the arm from `ComplexSchema` and of the key - from `SchemaRegistry`, so `tsc` refuses it at the authoring site. - - **⭐ This closes objectui#8818's `objectFields` hole — for that ENTRY, not for - the class.** `SchemaRenderer` strips a fixed enumerated metadata list and - spreads the rest as React props; `objectFields` is not on that list, and - `KanbanRenderer` — the component the `kanban-ui` key resolved to — declares - `objectFields` as a real prop, so an authored value reached the predicate layer - with no schema face judging it. With the registration gone, no authored node - reaches that component through the registry. ⚠️ The **class** is still open: the - hole returns the moment another registered renderer declares an `objectFields` - prop. objectui#8818's option (a) — stripping at the `SchemaRenderer` boundary — - is what would close the class. - - Superseded in this release by objectui#8818: option (a) landed, so the class - is closed as well. `objectFields` is now on `SchemaRenderer`'s stripped-metadata - list, and the legacy `props` alias bag drops it too, so an authored - `objectFields` reaches no component prop on any type key. The paragraph above - describes the tree this entry was written against. - - **⚠️ What the `kanban` arm took with it, stated because it is the cost of this - change.** That arm was the only schema face that ever declared `columns`, - `cardTitle`, `swimlaneField`, `grouping` and `navigation`, the only one that - refused `allowCollapse` / `cardTemplates` / `columnWidths` / `titleField` / - `draggable` / `onColumnAdd` / `onCardAdd` by name, and — through - `columns: KanbanColumn[]` — the only one that judged a lane's `cards` - (`240b80f31`). - - ⭐ **The sentence that stood here — «The surviving `ObjectKanbanSchema` face - declares none of them» — is retired, and the two reasons it failed are DIFFERENT - defects (objectui#9713).** It is replaced by a dated reading rather than silently - overwritten, because half of it was true when written and erasing that would be a - false record of its own: - - | key named just above | on `ObjectKanbanSchema` when this entry was written (`adf581278`, 2026-09-10) | on `main`, 2026-09-17 | what moved | - | --- | --- | --- | --- | - | `columns` | not declared | **declared** | objectui#8913 (PR objectui#8989), six hours after this entry was written | - | `cardTitle` | not declared | **declared** | objectui#9606 (PR objectui#9709) | - | `titleField` | **declared** | **declared** | nothing — the sentence was never true of this key | - | `allowCollapse` | **declared**, a live `z.boolean().optional()` | **declared**, as a `retirementTombstone()` | objectui#8801 retired it; it was declared on this face throughout | - | `swimlaneField`, `grouping`, `navigation`, `cardTemplates`, `columnWidths`, `draggable`, `onColumnAdd`, `onCardAdd`, and a lane's `cards` | not declared | not declared | nothing | - - ⇒ For `columns` and `cardTitle` the claim **ROTTED**: it was true on 2026-09-10 and - was falsified afterwards by cards that had no reason to read this file. For - `titleField` and `allowCollapse` it was **BORN FALSE**: those two are named above as - keys the `kanban` arm refused BY NAME — which the surviving face indeed does not do - — but the surviving face declared both of them the whole time, and «declares none of - them» said otherwise. ⛔ Do not restate any of this in the present tense: a pending - entry publishes verbatim into the CHANGELOG, and an undated present-tense claim - about another file is the construction that failed here. - - ⛔ Nothing about an `object-kanban` document changes: it was never judged by the - `kanban` arm, so all of those keys have always ridden `BaseSchema`'s index - signature there. What is gone is the `kanban` document that had them. Declaring - them on `ObjectKanbanSchema` would WIDEN a published accept set, which is a - maintainer ruling and not part of this one; every one of these readings is - pinned where it can be seen rather than left to be rediscovered. - - **Migrating.** Replace `"type": "kanban"` with `"type": "object-kanban"` and - `"type": "gantt"` with `"type": "object-gantt"` in hand-authored documents. The - `object-kanban` face requires `groupBy` and one of `bind` / `data` / - `objectName`; a purely static board (lanes carrying their own cards, no record - source) adds `"groupBy"` and `"data": []`. `kanban-ui` and `kanban-enhanced` - have no authored documents anywhere in this repository to migrate. - - **⚠️ The namespaced spellings retire with the registrations — `view:kanban` and - `view:gantt` are the same two keys.** `ComponentRegistry.register(type, C, - { namespace })` stores BOTH `namespace:type` and a bare-`type` fallback, so - every one of these keys had a namespaced twin that goes with it: - - | retired spelling | namespaced twin | author instead | - | --- | --- | --- | - | `kanban` | `view:kanban` | `object-kanban` | - | `gantt` | `view:gantt` | `object-gantt` | - | `kanban-ui` | `plugin-kanban:kanban-ui` | `object-kanban` | - | `kanban-enhanced` | `plugin-kanban:kanban-enhanced` | `object-kanban` | - - Both spellings are pinned as gone, each against a firing control on the - surviving key, in `plugin-kanban/src/__tests__/kanban-family-registry-keys-retired-8257.test.ts` - and `plugin-gantt/src/__tests__/bare-gantt-node-key-retired-8008.test.ts`. - - **What an unmigrated `view:kanban` / `view:gantt` node now renders depends on - the host.** In `apps/console` it renders the protocol **placeholder** panel, not - the OBJUI-001 "Unknown component type" error: the console calls the opt-in - `registerPlaceholders()` (`@object-ui/components`, `renderers/placeholders.tsx`) - *after* its plugin registrations, `view:kanban` and `view:gantt` are both in - that file's `PROTOCOL_COMPONENTS` list, and the placeholder only claims a key - nothing else has taken — which, until this change, `@object-ui/plugin-kanban` - and `@object-ui/plugin-gantt` had. In every other host, which does not call that - bootstrap, the same node renders OBJUI-001. - - **⚠️ `objectui check` will NOT flag either namespaced spelling.** The CLI's - `known-schema-types.ts` is generated from the repository's real registration - calls, and the placeholder registration is a real one — so `view:kanban` and - `view:gantt` are still on that list and still validate green, while the node - renders a placeholder rather than a board. The bare `kanban` / `gantt` entries - DID leave the generated list; only the namespaced pair survives, and only - because of the placeholder. Grep your documents for the namespaced spellings - directly; do not rely on `objectui check` to find them. - - `KanbanRenderer` is still exported from this package's entry point - (`@object-ui/plugin-kanban`); only its registry key is gone. ⚠️ `KanbanEnhanced` - is a different case, and the earlier draft of this note stated it wrongly: this - package's `exports` map has exactly two entries — `.` and `./style.css` — and - the barrel never re-exported the component, so - `@object-ui/plugin-kanban/KanbanEnhanced` has never been a resolvable specifier - for a consumer. At this change, with `kanban-enhanced` unregistered, - `KanbanEnhanced.tsx` had zero non-test importers, and the file was deliberately - left in place: deleting published-but-unreachable source is a further narrowing - and needed its own maintainer ruling, which this change did not have. - - ⚠️ **Dated note, 2026-09-25 — that ruling came, and the file has since been - deleted — objectui#8932.** Later in this same release - `packages/plugin-kanban/src/KanbanEnhanced.tsx` was deleted, and with it the - `dist/KanbanEnhanced.d.ts` typings this package shipped for it. The rest of this - entry is kept as the reading of this change; the objectui#8932 entry states what - ships. - - **⚠️ `@object-ui/sdui-parser`: `QUICK_ADD_HOST_TYPES` loses `kanban` with the - registration.** The `inert-quick-add` diagnostic (objectui#8285) named the two - tags `ObjectKanbanRenderer` answered to; one of them retires here, so the set is - now `{ 'object-kanban' }`. ⛔ Nothing is silently dropped by that narrowing, and - this is measured rather than argued: `checkKanbanQuickAdd` has exactly one call - site — `validate.ts`'s per-prop walk — and that walk runs only in the branch - where the manifest RESOLVED the tag. A tag no registration produces is answered - one level up by `unknown-component`, an **error**, and its props are never - walked, so on a manifest built from the live registry a `` node - draws `error/unknown-component` and nothing else, against a firing control on - `` that still draws `warning/inert-quick-add`. Keeping - `kanban` in the set would have been reachable only through a hand-built manifest - declaring a component of that name — which, after this retirement, is somebody - else's component, and the message asserts things about `ObjectKanban` that would - be false of it. This supersedes the `kanban` half of the objectui#8285 entry. - - **The diagnostic's remedy text moves from a tag to a component.** It used to end - "render `` from a React host that passes `onQuickAdd`". That sentence - is falsified by this change: `kanban-ui` is no longer a node type key, so a page - written to the old advice draws `unknown-component`. It now names - `KanbanRenderer` from `@object-ui/plugin-kanban` — still exported, still - forwarding both halves by identity — which is the surviving way to get the pair. - `content/docs/plugins/plugin-kanban.mdx` says the same thing the same way. -- 5ea623e: fix(sdui-parser,components,layout,types): containment is the declared `children` slot, not `isContainer` (objectui#9910) - - `validateTree`'s `not-a-container` diagnostic now reads exactly one declaration: - an input named `children` in the component's registration `inputs`. Ten - registrations already carried such an input — `slot`-typed on `tooltip` and the - four `page:*` containers, `array`-typed on the five page kinds — and the - predicate reads the NAME, so both count. `isContainer` is no longer - consulted and is not a fallback — new exports `acceptsChildren` and - `CHILD_LIST_KEY` in `@object-ui/sdui-parser` spell the predicate, and the - retired `body` dialect follows the same reading. - - Why: the flag was hand-kept and drifted from the renderers four times; after - objectui#6771 converged a dozen `schema.body` readers onto `children`, and with - objectui#6804 ruling the flag OFF for that population (it means layout - containment, and declaring it deletes a public tag from every react page's JSX - scope), the diagnostic landed FALSE on the one key `button`, `badge`, `alert` - and the `sidebar-*` parts render. The maintainer ruled declare-and-pin - (2026-09-24). - - What is declared: every renderer under `@object-ui/components` and - `@object-ui/layout` that puts `schema.children` on the page — 65 registrations - measured bare plus `sidebar` and `sidebar-menu-button` in their provider - context — now declares the `children` slot; the 34 flow/inline HTML tags, the - seven sectioning tags, the layout primitives, `button` / `badge` / `alert` / - `toggle` (label and description fallbacks), `span` (read ahead of `value`), - `div`, `form`, `scroll-area`, the ten `sidebar-*` parts and `page-header`. Nine public blocks - gain the slot in `sdui.manifest.json`; a `slot` input emits no JSX attribute, so - the generated `.d.ts` is unchanged. `isContainer` is kept everywhere it was - declared and re-described on every published face (`ComponentMeta`, - `RuntimeWidgetManifest`, `ComponentMetaSchema`, the manifest types) as LAYOUT - containment — the react-page scope builder keeps reading it unchanged. - - The runtime containment census is re-pointed from the flag to the input in both - directions (declared ⇔ actually renders, with named context probes for the - provider-scoped and portal renderers), `scripts/container-declaration-baseline.json` - is at zero, and a registration that is a layout container but renders no - authored list (`page:tabs`, `page:accordion`, `responsive-grid`) now draws the - TRUE `not-a-container` the flag used to silence. - - ⚠️ **Dated note, 2026-10-02 — `page-header` is retired — objectui#10859.** - Later in this same release objectui#10859 batch 8 (phase 2c) unregistered `@object-ui/layout`'s - `page-header` (and `layout:page-header`), so its `children` slot declaration left with it; authors write - `page:header`. The rest of this entry is kept as the reading of this change. - - ⚠️ **Dated note, 2026-10-02 — `responsive-grid` is retired — objectui#11441.** - Later in this same release objectui#11441 unregistered `@object-ui/layout`'s `responsive-grid` (and - `layout:responsive-grid`), so it no longer draws `not-a-container`: a node written with it renders the "Unknown - component type" panel. Authors write `grid` with a breakpoint `columns` object. The rest of this entry is kept as the - reading of this change. - - ⚠️ **Dated note, 2026-10-02 — the ten `sidebar-*` parts are retired — objectui#10859.** - Later in this same release objectui#10859 batch 8 (phase 2d) unregistered the ten `sidebar-*` parts, so their - `children` slot declarations left with them. `sidebar` keeps its declaration, and it now mounts its own provider - when none is above it, so the containment census measures it bare and its provider-scoped context probe is - gone, as is `sidebar-menu-button`'s. The rest of this entry is kept as the reading of this change. -- 8d58f46: `validateTree` now reports a dashboard widget `options` key that no renderer - consumes as a `unconsumed-widget-option` **warning** naming the consumed set - (maintainer ruling 2026-08-23). The census behind the accepted set — the spec's five - declared query keys (`dateGranularity`, `sortBy`, `sortOrder`, `limit`, - `stageOrder`) plus the `description` sub-caption convention key — is - re-measured on every test run against `@objectstack/spec` and the - `plugin-dashboard` renderer sources. The check fires only on dataset-bound - shorthand widgets (the spec-legal form) hosted by `dashboard` / - `dashboard-grid` nodes, and honours the spec's per-widget - `suppressWarnings: ['unconsumed-widget-option']` escape hatch. Warning - severity only: documents keep parsing, saving and rendering. Exported for - other surfaces: `checkDashboardWidgetOptions`, `CONSUMED_WIDGET_OPTION_KEYS`, - `DASHBOARD_WIDGET_HOST_TYPES`, `UNCONSUMED_WIDGET_OPTION`. - -### Patch Changes - -- 39b8d51: feat(components): `div` is deprecated on the html tier too — a `kind:'html'` page that authors `
` is refused at compile time, and the error names `box` - - Executes the maintainer's ruling A on objectstack#20112 ("`div` stays - deprecated") in the renderer. Until now the published manifest left `div` out of - the html tier, so `os validate` refused a `
` page as `forbidden-tag`, while - the console compiled and rendered the same page. `div`'s registration declared - the deprecation for the JSON surface only, and its renderer exempted nodes the - html parser emitted (objectui#4000). - - **What moves for a consumer.** - - - **BREAKING (authoring): a `kind:'html'` (or legacy `kind:'jsx'`) page that - authors `
` or `` no longer renders.** The html compile fails, and - like every compile error this fails the whole page: the console shows the - "HTML page failed to compile" panel. Each refusal is the parser's - `forbidden-tag` sentence, followed by the declared replacement guidance, which - names `box`. Migration: retype `
` to ``. `box` renders the same - element with the same `className` and no layout of its own. - - `div`'s registration now declares `deprecated.surfaces: ['json', 'html']`, so - `ComponentRegistry.deprecationFor('div', 'html')` answers the declaration - (with the unchanged `replacement`) instead of `undefined`. The html compile - derives what it refuses from `deprecationFor(type, 'html')`, the registration's - declaration, rather than from a list of tag names. - - The `div` renderer no longer exempts nodes carrying html-tier provenance, and - its dev-build notice now says it applies to JSON-authored nodes and html pages - alike. The migration bullets are unchanged. - - `span` is unchanged: it is still deprecated on the JSON surface only, and it - still compiles on html pages. - - Semver: `minor`, not `major`, per this repository's version-alignment rule. The - breaking semantics are stated above. - - **Published declaration text (prose only, no behaviour change).** - - - `@object-ui/core`: the emitted `dist/registry/Registry.d.ts` carries the - rewritten `ComponentDeprecation` and `deprecationFor` docblocks, where `span` is - now the worked example of a json-only deprecation and `div` is stated to name - both surfaces. `dist/registry/html-tier-intrinsics.d.ts` carries the rewritten - `HTML_TIER_INTRINSICS` docblock: its `div` bullet and the sentence describing the - console's html-tier whitelist. - - `@object-ui/sdui-parser`: the emitted `dist/provenance.d.ts` carries the - rewritten file-header sentence, where `span` is now the example of a tag that - stays html-tier vocabulary. -- 99878d8: `validateTree` no longer calls a declared `bind` or `hidden` unknown (objectui#11008). - - `BaseSchema` declares `bind` and `hidden` for every node, and no registration declares either as an input. `validateTree`'s base-prop set held neither, so its undeclared-key branch answered every authored one with an `unknown-prop` warning (`has no prop "bind"`), including on the nodes that honour it: `list`, `tree-view` and the `object-*` widgets read `bind` through `useDataScope`, and `SchemaRenderer` reads `hidden` for every node. Both are now base props of the parser tier, so neither draws a diagnostic on any node. A near-miss spelling such as `bindTo` still draws `unknown-prop`. - - The cost is named rather than hidden. `data-table` does not read `bind` (objectui#6575), and a `bind` on it now draws nothing at the parser tier either. Its render-time console warning (`[ObjectUI] DataTable bind:`) is the one signal left. The `BaseSchema.bind` docblock, which ships in the emitted `.d.ts`, said that the console warning and the parser tier's `unknown-prop` both named such a `bind`. It now names the console warning as the one signal and says the parser tier is silent. That supersedes the wording the objectui#10981 entry describes for the same docblock: the parser half it names held until this change. - - At this change, `placeholder` is also a `BaseSchema` member that `validateTree` does not count as a base prop, and it is left as it was. Some registrations declare it as a typed input, and a base prop skipped the declared-input lookup, so adding it would have silenced their `type-mismatch`. - - ⚠️ **Dated note, 2026-09-29 — `placeholder` has since become a base prop where a type does not declare it — objectui#11044.** Later in this same release, `placeholder`, `name`, `label`, `description`, `data` and `ariaLabel` join the base props as `where-undeclared` members: each is a base prop only on a type that declares no input of that name. Where a type declares one, the declared input wins and keeps its `type-mismatch`, so no declared type check is silenced. The paragraph above is kept as the reading of this change; the objectui#11044 entry states what ships. -- faf5269: A dataset-bound KPI tile now renders the sub-caption its author declared in - `options.description` (objectui#7293). - - The sub-caption slot was wired end to end and consumed by nothing. It has its own - translation key (`{ns}.dashboards.{dash}.widgets.{id}.subCaption`), the server's - `translateDashboard` overlays that translation onto `options.description`, and - `DashboardRenderer`'s `tWidgetSubCaption` resolves it — but only onto the two - inline arms of `getComponentSchema()`. `dataset` is REQUIRED on - `DashboardWidgetSchema` (published `@objectstack/spec@17.4.0`: the required keys - are exactly `id` / `dataset` / `values`), so every spec-legal widget renders - through `DatasetWidget` instead, which read the key nowhere. Every author who - wrote a sub-caption got silence. - - `DatasetWidget`'s metric branch now reads the key and renders it in the caption - row, resolving it through the same `pickLocalized` seam every other authored - label channel uses, so an inline per-locale map resolves instead of being - dropped. A tile that declares no sub-caption renders byte-identical markup. - - Read in `DatasetWidget` rather than passed down as a prop on purpose: both - dashboard surfaces (`DashboardRenderer` and `DashboardGridLayout`) route a - dataset-bound widget to that one component, so a prop from one dispatch site - would have fixed one surface and left the other unchanged. - - `@object-ui/sdui-parser` carries no behaviour change — its `unconsumed-widget-option` - census recorded `description` as accepted *despite* having no read site on the - dataset-bound path, and that note is now stale prose. -- 0b1326d: Documentation no longer teaches the "JSX/HTML + Tailwind" framing for a page's - `source`, which ADR-0080's own 2026-06-30 header amendment (under ADR-0065, - Accepted) retracted. objectui#5461 corrected three sites; a multiline census - found eight more, in three spellings a line-oriented grep could not see. - - A page's `source` is *runtime metadata*. The console's Tailwind is compiled at - build time by scanning the console's own `src`, and there is no safelist, so it - never sees your page: an authored utility class produces CSS only by coincidence - (when objectui already ships that exact class) and otherwise produces nothing, - with no error anywhere. That is the ADR-0065 "works only by coincidence" failure - mode, and it is how a modal's `bg-black/50` backdrop reached production fully - transparent. `os validate` reports it as `page-source-className-tailwind`, a - warning on kinds `html`, `react` and `jsx`, shipped in `@objectstack/lint@11.5.0`. - - The tiers themselves are unchanged and every load-bearing claim survives — - parse-never-execute, the untrusted-author safety argument for `html`, and the - deprecated `'jsx'` alias. Only the styling primitive is corrected, to the wording - `content/docs/guide/react-pages.md` §Styling already uses: - - | `kind` | Style with | - |---|---| - | `"html"` | The blocks' own structured props (`` `` ``, `` `` ``) plus a JSON `style` object. | - | `"react"` | Inline `style` objects. | - - Colors on both tiers come from the theme as `hsl(var(--token))`. - - Why each package has an entry — each was measured against its built artefact, not - assumed: - - - **`@object-ui/react-runtime`**: `README.md` is published to npm (npm includes - `README.md` in the tarball regardless of `files`). Its "no sandbox" callout is - the paragraph that routes untrusted-author work to the `html` tier, and it - carried the retracted framing line-wrapped across `:17-18`. It also gains the - §Styling section it was missing — the absence is why the framing survived here. - - **`@object-ui/sdui-parser`**: the corrected header of `src/types.ts` projects - verbatim into the published `dist/types.d.ts`. - - **`@object-ui/components`**: the corrected header of - `src/renderers/basic/html-elements.tsx` projects verbatim into the published - `dist/renderers/basic/html-elements.d.ts`. The `kind === 'html'` dispatch-arm - comment in `src/renderers/layout/page.tsx` does **not** project (it is inside a - function body) and is included here only because the same package already owes - an entry. - - No behaviour change: this is prose only. `CHANGELOG.md` occurrences are - deliberately untouched — immutable release history. -- 305205a: `kind:'html'` page sources keep the space between a text run and an adjacent - inline element: `A x page` now compiles to `A `/``/` - page` and renders as `A x page` rather than `Axpage` (objectui#5661). - - The parser collapsed each text run's whitespace to a single space — correct, and - what HTML itself does — and then `.trim()`ed it, which is not: HTML collapses a - whitespace run to one space, it does not delete it. The deleted space was - precisely the separator between a run and its inline sibling, so every authored - sentence carrying emphasis or a link in the tier the guide recommends by default - rendered with its words run together. It was silent: the page rendered, the - structure was right, no diagnostic fired. - - The rule is deliberately mechanical rather than a block/inline taxonomy invented - for a schema tree that has none: keep one leading space when a sibling precedes - the run, and one trailing space when a sibling element follows it. The parent's - own start and end still drop their edge space, so `

hi

` is unchanged. - - Its one bounded cost: a whitespace-only run BETWEEN two siblings survives as a - single space, so a pretty-printed `
    ` gains one `' '` string child per gap - between its `
  • `s — one space per gap, never the source's newline and - indentation, never at the container's own edges, and never inside an item's own - text. That bound is pinned by a test rather than left as a claim. - -## 17.6.0 - -### Minor Changes - -- 279fb13: `ComponentInput.type` can declare a UNION, so a block stops warning about legal - writes its own description recommends - - A registration's `type` was one coarse control kind, while a good number of spec - keys accept more than one shape. A declaration therefore had to pick an arm, and - the repo's own manifest gate then reported `type-mismatch` on the other arm's - legal values. Four of the five measured cases were the loud shape: the input's - `description` teaches the author to write an inline translation map - (`{ en, "zh-CN", … }`) while the same input's `type: 'string'` made - `sdui-parser`'s `checkType` warn about exactly that map — one platform authority - contradicting itself on the write it had just recommended. Because these land at - warning severity the page still compiled and rendered; the cost is that noise on - correct authoring trains authors, AI authors included, to dismiss the - `unknown-prop` and `type-mismatch` reports that are real. - - `type` now accepts an ARRAY of coarse kinds as well as a single one (maintainer - ruling on objectui#3832, direction (a)), and a value passes the coarse check when - ANY declared arm accepts it. Both declaration sites in `@object-ui/types` move - together with the registry's own copy in `@object-ui/core`, and - `ComponentInputSchema` enforces the same widening — a non-empty array of - DISTINCT kinds, so an empty arm list or a repeated arm is refused at authoring - time rather than normalized behind the author's back. - - Five declarations now spell their real contract, and the `type-mismatch` warning - on each of these legal writes is gone: - - - `page:header.title`, `page:header.subtitle`, `page:card.title` — - string **or** inline translation map (the spec's union, measured against - `ComponentPropsMap` at the pinned rc.6; the renderers resolve both through - `pickLocalized`); - - `record:alert.title`, `record:alert.body` — the same two shapes, justified - against the RENDERER since the pinned spec carries no `record:alert` props - schema; - - `element:text_input.defaultValue` — `string | number`, the spec's union, - which had been narrowed to `'string'` with the number arm named only in prose. - - **Backward compatible, and measured as such.** The single-kind form stays valid - and is still the canonical spelling for a one-arm key: it validates identically - (the diagnostics for one arm, `invalid-enum` and its `error` severity included, - are byte-identical), and `manifestFromConfigs` collapses a one-element array back - to the bare string, so every entry already in a published `sdui.manifest.json` - serializes unchanged and arrays appear only where a union was really declared. - The JSX authoring surface follows in the same step — `generateDts` emits a - TypeScript union for a union input, so the `.d.ts` an author type-checks against - accepts exactly what the gate accepts. - - A union widens what is legal; it does not switch the check off. A value matching - NO declared arm is still reported, a multi-arm mismatch reports at its strictest - arm's severity (`error` when an `enum` arm is present, so an enum's closed list - does not become dismissible by having a second arm added next to it), and arms - are meant to match the contract rather than relax the gate: - `element:text_input.defaultValue` deliberately gains no `object` arm because the - spec rejects a map there, and `element:record_picker.emptyText` keeps its single - `'string'` arm because that renderer drops the map form (objectui#4163) — an arm - the renderer never honours would advertise a shape that cannot reach the screen. - -### Patch Changes - -- 40d3a33: `div` 的废弃提示按 provenance 收窄:只对 **JSON 作者面**的节点报,不再对 `kind:'html'` tier 自己解析出的节点开火。 - - html tier 的页面是一段受限 JSX/Tailwind 文本,由引擎自己的解析器编译(只解析、不执行),标签名原样映射成节点 —— 作者在那一层写下的盒子标签,是该 tier 词表里的一等成员,**没有别的拼法可迁移**。提示照旧对他们开火,给的还是 JSON 作者面的替代建议:一条谁都无法执行的提示不是废弃,是噪声;它同时意味着这个类型永远退不掉,因为引擎自己的编译器一直在产出它。 - - 判据是**来源**,由生产者确立:解析器给它产出的每个节点打一个 symbol 标记(`Symbol.for` 注册键),渲染器读这个标记。symbol 对 `JSON.stringify` / `Object.keys` / DOM 全部不可见 —— 所以它既不会落进被持久化的文档,也就无法被一份(手写或 AI 生成的)JSON 元数据复制回来给自己买到豁免;通过花括号属性夹带进来的 JSON **不打标记**,那部分本来就是手写的,建议对它成立。 - - 迁移建议一字未改,JSON 作者面照旧每次模块加载报一次;提示文案现在写明它针对哪一个作者面。 - -## 17.5.0 - -## 17.4.0 - -## 17.3.0 - -## 17.2.0 - -### Minor Changes - -- 4a51e77: Stop declaring 14 symbols across ten packages under names `@objectstack/spec` - owns (objectui#3161, objectstack#4115 batch 7 — the long tail, one or two - entries per package). All ten packages leave the ledger, which drops from 17 - collisions across 11 packages to 3 across 1. - - **Renamed exports** — in every case the spec exports the same name for a - _different_ thing, so the old name was a mis-description rather than a dialect: - - | package | was | now | what the spec's same-named export is | - | :------------------------- | :--------------------------------- | :--------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | - | `@object-ui/fields` | `FieldWidgetProps` | `FieldWidgetComponentProps` | the DECLARED field-widget plugin props contract (a zod object; `field.type` is the `FieldType` enum, `readonly`/`required` carry defaults) | - | `@object-ui/layout` | `PageHeaderProps` | `PageHeaderComponentProps` | the authored `page:header` node — a zod schema of `title`, `subtitle`, an icon NAME, `breadcrumb`, `actions: string[]` | - | `@object-ui/layout` | `Page` | `PageNodeRenderer` | the authored page metadata DOCUMENT (`name`, `label`, `type`, `regions`) | - | `@object-ui/plugin-detail` | `ObjectFieldLike` | `ObjectDefFieldLike` | the i18n duck type `translateObject` walks (`help`/`description`, plus `[key: string]: any`) | - | `@object-ui/plugin-grid` | `ColumnSummaryConfig` | `ColumnSummarySetting` | the OBJECT form of `ListColumn.summary` **only** — the local one was the whole union, shorthand included | - | `@object-ui/plugin-grid` | `isMultiValueField` | `hasMultiValueShape` | the spec's classifier, which requires a def with a `type`; the local one is called with `undefined` | - | `@object-ui/collaboration` | `RealtimeConfig` | `RealtimeSubscriptionConfig` | the app's realtime DECLARATION (`enabled`, `transport`, `subscriptions[]`) | - | `@object-ui/plugin-charts` | `ChartConfig` | `ChartContainerConfig` | the authored chart document (`type`, `xAxis`, `series`, `showLegend`, …) | - | `@object-ui/plugin-form` | `FormSection` / `FormSectionProps` | `FormSectionContainer` / `FormSectionContainerProps` | the authored form-section metadata (`name`, `pane`, `visibleWhen`, `fields`) | - | `@object-ui/providers` | `Theme` | `ThemePreference` | a whole theme DOCUMENT (`name`, `label`, `colors`, `typography`) | - | `@object-ui/runner` | `App` (default export) | `RunnerApp` | the authored application metadata type **and** the `App.create()` builder | - | `@object-ui/sdui-parser` | `ValidationResult` | `ManifestValidationResult` | plugin-manifest validation (`{ valid, errors?, warnings? }`), exported from both `kernel` and `contracts` | - - `ManifestValidationResult` follows the `Validation` - convention registered on objectstack#4115 (`@object-ui/core` took - `SchemaNodeValidationResult` in batch 4). `PageHeaderComponentProps` deliberately - reuses the name `@object-ui/app-shell` already chose for its own header props in - batch 3, so one concept does not acquire two dialect names one package apart. - - **Now derived from the spec instead of hand-written:** - - - `@object-ui/fields` — `isFileIdToken` is re-exported from - `@objectstack/spec/data`. The local copy was character-for-character identical - to the spec's function while its comment said it "mirrors" it, so every - behaviour test passed and only reference identity could tell the two apart. - The regex is a wire decision: widening it server-side while a copy here kept - the old bound would make every new id read as "not a reference", and the - widget would submit the legacy inline blob to a backend expecting a reference. - - `@object-ui/plugin-detail` — `FeedFilterMode` is re-exported from - `@objectstack/spec/data`, in a file that already imported the sibling - `FeedItemType` from the spec. - - `@object-ui/plugin-grid` — the eleven-member aggregation union is now the - spec's `ColumnSummary` enum, so the total `Record` - label map turns a member the spec adds into a compile error instead of a - blank footer cell. `ColumnSummarySetting` is `NonNullable`, - i.e. whatever forms the spec itself accepts. `hasMultiValueShape` delegates to - the spec's `isMultiValueField` rather than re-deriving it from - `MULTI_OPTION_TYPES` / `MULTI_CAPABLE_TYPES`. - - `@object-ui/providers` — `ThemePreference` is the spec's `ThemeMode` union - plus the one legacy `'system'` spelling this provider still honours for stored - preferences, read off the schema's own `_zod` carrier so the package takes no - zod dependency. - - `@objectstack/spec` moves from `devDependencies` to `dependencies` in - `@object-ui/fields` (it re-exports a runtime function) and `@object-ui/providers` - (its public `.d.ts` now references the spec). - - Scored `minor`, not `major`, per this repo's fixed-group rule — objectui's major - tracks `@objectstack`, so breaking changes of our own ship as minor with the - semantics spelled out above (see AGENTS.md §版本号策略). A `major` here would carry - all 39 packages of the fixed group to `18.0.0` and off objectstack's 17.x line. - -### Patch Changes - -- cc70b8f: A declared `objectName` must reach the data layer — the evidence the framework's spec↔registry check cannot gather (objectstack#4472). - - The framework diffs `sdui.manifest.json` against the spec's zod schemas and, while that - check was named `check:react-conformance`, it was read — by its own file header — as - confirming these components "ACTUALLY implement" the spec's props. It never could. Both - sides of that diff are **declarations**, and this repo produces one of them: - `manifestFromConfigs` copies `config.inputs` verbatim and cannot observe whether the - renderer behind a block reads any of them. So a prop both sides declare and nothing - consumes reads there as agreement — which is how objectstack#4413's four `record:*` blocks - published an `objectName`/`recordId` no renderer read, rendered blank, and stayed green. - - Evidence about the render path has to be taken from the render path, so it lives here now. - `apps/console/src/__tests__/public-block-binding-reach.test.tsx` mounts every public block - that declares an `objectName` input through `SchemaRenderer` with nothing but that binding, - under a provider whose `dataSource` is a Proxy recording every call, and asserts some call - carried the object name. Deliberately narrow — "is this binding wired", not "is every - declared input consumed", which is not decidable from outside without heuristics. Every - non-reaching block carries a written reason in a ledger asserted to equal the observed set - in **both** directions, so a block that starts binding forces its entry deleted and a block - that stops binding fails; the suite was verified to go red both ways. - - First run: five of eight bound blocks reach the data layer, three do not. - `record:related_list` legitimately declines to fetch without the parent record id from - `RecordContext` (already documented in @objectstack/spec's objectstack#4413 ledger). - `list-view` and `embeddable-form` do not, and that is a real defect of the same shape — - neither registration bridges the schema-renderer context onto the component's `dataSource` - prop the way `object-form` / `object-kanban` / `object-calendar` do, and `SchemaRenderer` - never injects it, so on the registry/SDUI path both render an empty shell while declaring - `objectName` **required**. Filed as objectui#3144 rather than fixed here: giving them a - data source changes what they render everywhere they are mounted bare. - - `manifestFromConfigs` and `scripts/dump-public-manifest.mjs` now say in their own docs that - what they emit is what a registration _declared_, never what a renderer reads. - -## 17.1.0 - -### Minor Changes - -- 32462dd: feat(sdui): guard the public contract against silent drift — coverage test + manifest lazy-stub assertion - - Follow-up to objectui#2953. That bug — every lazily-registered public block - missing from the contract, and so from every `kind:'react'` page's scope — - survived because nothing compared `PUBLIC_BLOCKS` against what an app actually - registers. Type-check, lint, build and the whole suite stayed green while seven - curated blocks were unusable. Two guards close that class. - - **Console ↔ contract coverage.** `apps/console/src/register-plugins.ts` extracts - the plugin registration out of `main.tsx` so it can be imported without booting - the app. A new `apps/console/src/__tests__/public-contract.test.ts` reads that - real list and pins, as exact lists, which curated tags the console exposes (35), - which are still unimplemented (`line_items`), and which reach the contract - through a pending lazy stub. Exact lists rather than `toContain`, because the - failure mode is a _shrinking_ contract. Reverting the #2953 fix drops coverage - from 35 to 28 and fails all four assertions. - - **Manifests must be generated from loaded registrations.** New exported - `assertFullyLoaded(configs)` in `@object-ui/sdui-parser`, plus `lazy?: boolean` - on `RegistryConfigLike`. A lazy stub carries metadata but no `inputs`, so it - would be written into `sdui.manifest.json` as a block that takes no props — - making every prop an author passes it an `unknown-prop` diagnostic in the save - gate. Both generators now assert instead: `gen-manifest.ts` throws, and - `dev/manifest-dump.tsx` also imports the console's real registration list, so a - plugin the console lazy-registers but the dump forgets to import eagerly is - caught rather than silently emitted propless. `scripts/dump-public-manifest.mjs` - surfaces that failure instead of timing out for 120s with no message. - - Also documents `object-chart` as a seventh block affected by objectui#2953 — - the issue listed six. - -## 17.0.0 - -## 16.1.0 - -## 16.0.0 - -## 15.0.0 - -## 14.1.0 - -## 14.0.0 - -## 13.2.0 - -## 13.1.0 - -## 13.0.0 - -## 12.1.0 - -## 12.0.0 - -## 11.5.0 - -## 11.4.0 - -## 11.3.0 - -## 11.2.0 - -### Minor Changes - -- 9e7a986: ADR-0080: AI-authored UI pages. New `@object-ui/sdui-parser` compiles a constrained JSX/HTML+Tailwind source into the SchemaNode tree (parse, never execute) with whitelist sanitization, manifest validation, and `.d.ts` codegen for the JSX type surface. `PageRenderer` renders `kind:'jsx'` pages; `ComponentRegistry` gains `tier` + `getPublicConfigs()` (capability vs contract). diff --git a/ui/packages/sdui-parser/LICENSE b/ui/packages/sdui-parser/LICENSE deleted file mode 100644 index cf2ca28574c..00000000000 --- a/ui/packages/sdui-parser/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2024 ObjectQL - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/ui/packages/sdui-parser/package.json b/ui/packages/sdui-parser/package.json deleted file mode 100644 index d5b1cbf1552..00000000000 --- a/ui/packages/sdui-parser/package.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "name": "@object-ui/sdui-parser", - "version": "18.1.0", - "type": "module", - "sideEffects": false, - "license": "MIT", - "description": "Constrained JSX-source → SDUI SchemaNode tree compiler (parse, never execute). Isomorphic, zero React. ADR-0080.", - "homepage": "https://www.objectui.org/docs/sdui-parser", - "repository": { - "type": "git", - "url": "git+https://github.com/objectstack-ai/objectstack.git", - "directory": "ui/packages/sdui-parser" - }, - "main": "./dist/index.js", - "module": "./dist/index.js", - "types": "./dist/index.d.ts", - "exports": { - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.js", - "default": "./dist/index.js" - } - }, - "files": [ - "dist" - ], - "scripts": { - "build": "tsc && node ../../scripts/check-dist-completeness.mjs", - "test": "vitest run --root ../.. packages/sdui-parser/", - "type-check": "tsc --noEmit && tsc -p tsconfig.test.json", - "lint": "eslint ." - }, - "devDependencies": { - "@object-ui/core": "workspace:*", - "@object-ui/react": "workspace:*", - "@object-ui/types": "workspace:*", - "@objectstack/spec": "workspace:^", - "vitest": "^4.1.10" - } -} diff --git a/ui/packages/sdui-parser/src/__tests__/base-props-bind-hidden-11008.test.ts b/ui/packages/sdui-parser/src/__tests__/base-props-bind-hidden-11008.test.ts deleted file mode 100644 index d8018b8c995..00000000000 --- a/ui/packages/sdui-parser/src/__tests__/base-props-bind-hidden-11008.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * `bind` and `hidden` are base props of the tier (objectui#11008). - * - * `BaseSchema` (`@object-ui/types`) declares both for every node, and no - * registration declares either as an input. Before they joined `BASE_PROPS`, - * the undeclared-key branch answered every authored one with `unknown-prop` - * ("has no prop"), including on the nodes that honour it: `SchemaRenderer` - * reads `hidden` for every node, and every `useDataScope` renderer reads - * `bind`. This file pins the TIER's reading of a manifest; the live-registry - * half — `list`, `tree-view` and `data-table` — is - * `packages/components/src/renderers/__tests__/bind-base-prop-parser-tier-11008.test.tsx`. - * - * Three facts, kept apart because each one fails differently: - * - * - both keys draw nothing, in every value shape `BaseSchema` admits for - * them — the fix; - * - a near-miss spelling (`bindTo`, `hide`) on the SAME node still draws - * `unknown-prop` naming it — the lit control. Without it, "draws nothing" - * would also pass against a tier that stopped warning on undeclared keys; - * - a registration that DECLARES `placeholder` keeps its `type-mismatch`. - * `placeholder` is a `BaseSchema` member too, and it was left out of the - * `every-node` members because they skip the declared-input lookup. It - * joined later as a `where-undeclared` member (objectui#11044), which a - * declared input outranks: this row goes red if it is ever made an - * `every-node` member the way `bind` and `hidden` were. - */ -import { describe, expect, it } from 'vitest'; -import { manifestFromConfigs, validateTree } from '../index.js'; -import type { Diagnostic, Manifest, SchemaElement } from '../types.js'; - -const manifest: Manifest = manifestFromConfigs([ - // a leaf that declares neither key, as no live registration does - { type: 'leaf', namespace: 'ui', inputs: [{ name: 'content', type: 'string' }] }, - // the input-family shape: `placeholder` declared as a typed input - { type: 'field', namespace: 'ui', inputs: [{ name: 'placeholder', type: 'string' }] }, -] as unknown as Parameters[0]); - -const diagnose = (node: unknown): Diagnostic[] => - validateTree(node as SchemaElement, manifest).diagnostics; - -const LEAF = { type: 'leaf', content: 'x' }; - -/** Every value shape `BaseSchema` declares for the two keys. */ -const BASE_VALUES: Record<'bind' | 'hidden', unknown[]> = { - bind: ['users', 'app.settings.users'], - hidden: [true, false, '${data.role !== "admin"}', { dialect: 'cel', source: "record.status == 'draft'" }], -}; - -describe('objectui#11008 — `bind` and `hidden` are base props of the parser tier', () => { - it('the control node draws nothing', () => { - expect(diagnose(LEAF)).toEqual([]); - }); - - it.each(Object.keys(BASE_VALUES) as Array)( - '`%s` draws no diagnostic, in every value shape BaseSchema admits', - (key) => { - const drew = BASE_VALUES[key] - .map((value) => [value, diagnose({ ...LEAF, [key]: value })] as const) - .filter(([, found]) => found.length > 0) - .map(([value, found]) => `${JSON.stringify(value)} → ${JSON.stringify(found.map((d) => d.code))}`); - expect(drew).toEqual([]); - }, - ); - - it.each([ - ['bindTo', 'users'], - ['hide', true], - ] as const)('lit control: the near-miss `%s` still draws one `unknown-prop` naming it', (key, value) => { - const found = diagnose({ ...LEAF, [key]: value }); - expect(found.map((d) => d.code)).toEqual(['unknown-prop']); - expect(found[0]!.message).toContain(`"${key}"`); - }); - - it('a registration that declares `placeholder` keeps its type check — why `placeholder` is not `every-node`', () => { - expect(diagnose({ type: 'field', placeholder: 'Search…' })).toEqual([]); - const found = diagnose({ type: 'field', placeholder: 42 }); - expect(found.map((d) => d.code)).toEqual(['type-mismatch']); - expect(found[0]!.message).toContain('"placeholder"'); - }); -}); diff --git a/ui/packages/sdui-parser/src/__tests__/base-props-one-list-11044.test.ts b/ui/packages/sdui-parser/src/__tests__/base-props-one-list-11044.test.ts deleted file mode 100644 index f60d970d576..00000000000 --- a/ui/packages/sdui-parser/src/__tests__/base-props-one-list-11044.test.ts +++ /dev/null @@ -1,265 +0,0 @@ -/** - * ObjectUI - * Copyright (c) 2024-present ObjectStack Inc. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -/** - * The base props are ONE list, and both of its readers read it (objectui#11044). - * - * `validateTree`'s `unknown-prop` branch and `generateDts`'s `SduiBaseProps` - * used to keep separate hand-written copies of the base-prop list. They - * drifted: after objectui#11008 the validator accepted `bind` and `hidden` on - * every node, and the generated JSX types still refused both - * (`TS2322 … Property bind does not exist on type ListProps`). Both now read - * `SDUI_BASE_PROPS`. This file pins the tier's reading of a manifest; the - * live-registry half is - * `packages/components/src/renderers/__tests__/base-props-one-list-11044.test.tsx`. - * - * Four facts, kept apart because each fails differently: - * - * - ONE LIST: the generated `SduiBaseProps` block is exactly the list's - * attribute entries, and every entry is accepted on a node that declares - * nothing, while a near-miss spelling still draws `unknown-prop` (the lit - * control — without it, "draws nothing" would also pass against a tier - * that stopped warning on undeclared keys); - * - MEMBER 1: `visibleWhen`, `hiddenOn` and `testId` are accepted on every - * node, as `bind` and `hidden` are; - * - MEMBER 2 (the triage ruling): a `'where-undeclared'` member is a base - * prop only where the type declares no input of that name. Where one does, - * the declared input wins, `type-mismatch` included; - * - MEMBER 3: the generated `.d.ts`, compiled by `tsc`, accepts the base - * props on a type that declares none of them, refuses a near-miss, and - * lets a declared input's type win without a TS2430 conflict — for an - * `'every-node'` member too (objectui#11075): `notice` declares `visible` - * wider than the base `boolean`, as `record:alert` does in the live - * registry. The real public manifest's half is - * `apps/console/src/__tests__/sdui-intrinsics-compile-11075.test.ts`. - */ -import { describe, expect, it } from 'vitest'; -import ts from 'typescript'; -import { generateDts, manifestFromConfigs, SDUI_BASE_PROPS, validateTree } from '../index.js'; -import type { Diagnostic, SchemaElement } from '../types.js'; - -/** - * `leaf` declares none of the base props, as no live registration declares - * any `'every-node'` member added here. `field` declares every - * `'where-undeclared'` member as a typed input — the input-family shape — and - * two of them with a type the base one does not admit (`label` takes any - * object, `data` only an array). `notice` declares three `'every-node'` - * members (objectui#11075): `visible` with the three arms `record:alert` - * declares, which the base `boolean` does not admit, and `disabled` / - * `className` with the base type; its `children` input is a slot. - */ -const manifest = manifestFromConfigs([ - { type: 'leaf', namespace: 'ui', inputs: [{ name: 'content', type: 'string' }] }, - { - type: 'field', - namespace: 'ui', - inputs: [ - { name: 'name', type: 'string' }, - { name: 'label', type: ['string', 'object'] }, - { name: 'description', type: 'string' }, - { name: 'placeholder', type: 'string' }, - { name: 'data', type: 'array' }, - { name: 'ariaLabel', type: 'string' }, - ], - }, - { - type: 'list', - namespace: 'ui', - inputs: [ - { name: 'title', type: 'string' }, - { name: 'items', type: 'array' }, - ], - }, - { - type: 'notice', - namespace: 'ui', - inputs: [ - { name: 'message', type: 'string' }, - { name: 'visible', type: ['boolean', 'string', 'object'] }, - { name: 'disabled', type: 'boolean' }, - { name: 'className', type: 'string' }, - { name: 'children', type: 'slot' }, - ], - }, -] as unknown as Parameters[0]); - -const diagnose = (node: unknown): Diagnostic[] => - validateTree(node as SchemaElement, manifest).diagnostics; - -const LEAF = { type: 'leaf', content: 'x' }; - -/** A value of the kind `BaseSchema` declares for each member. */ -const WELL_TYPED: Record = { - id: 'n1', - className: 'p-2', - style: { padding: 4 }, - visible: true, - visibleWhen: "data.role == 'admin'", - visibleOn: "data.role == 'admin'", - hidden: true, - hiddenOn: "data.role != 'admin'", - disabled: false, - disabledOn: 'data.locked', - bind: 'users', - testId: 'users-list', - children: [], - name: 'email', - label: 'Email', - description: 'Where we write', - placeholder: 'you@example.com', - data: [{ id: 1 }], - ariaLabel: 'Email address', -}; - -const WHERE_UNDECLARED = SDUI_BASE_PROPS.filter((p) => p.scope === 'where-undeclared').map((p) => p.name); - -describe('objectui#11044 — the base props are one list, read by the validator and the codegen', () => { - it('the list names each key once, and every attribute entry has a value to test it with', () => { - const names = SDUI_BASE_PROPS.map((p) => p.name); - expect(new Set(names).size).toBe(names.length); - // `type` is the one entry that is no attribute: the tag name carries it. - expect(SDUI_BASE_PROPS.filter((p) => p.tsType === null).map((p) => p.name)).toEqual(['type']); - expect(Object.keys(WELL_TYPED).sort()).toEqual(names.filter((n) => n !== 'type').sort()); - }); - - it('the generated `SduiBaseProps` is exactly the list, in order, with each entry’s type', () => { - const block = generateDts(manifest).match(/export interface SduiBaseProps \{\n([\s\S]*?)\n\}\n/); - expect(block, 'no SduiBaseProps interface in the generated d.ts').toBeTruthy(); - expect(block![1].split('\n')).toEqual( - SDUI_BASE_PROPS.filter((p) => p.tsType !== null).map((p) => ` ${p.name}?: ${p.tsType};`), - ); - }); - - it('the control node draws nothing', () => { - expect(diagnose(LEAF)).toEqual([]); - }); - - it.each(Object.keys(WELL_TYPED))('`%s` draws nothing on a node that declares no input of that name', (key) => { - expect(diagnose({ ...LEAF, [key]: WELL_TYPED[key] })).toEqual([]); - }); - - it.each([ - ['testid', 'n1'], - ['visibleIf', 'data.x'], - ['hiddenWhen', 'data.x'], - ['aria-label', 'x'], - ] as const)('lit control: the near-miss `%s` still draws one `unknown-prop` naming it', (key, value) => { - const found = diagnose({ ...LEAF, [key]: value }); - expect(found.map((d) => d.code)).toEqual(['unknown-prop']); - expect(found[0]!.message).toContain(`"${key}"`); - }); -}); - -describe('objectui#11044 member 1 — `visibleWhen`, `hiddenOn` and `testId` are base props of every node', () => { - it.each(['visibleWhen', 'hiddenOn', 'testId'])('`%s` is an `every-node` member', (key) => { - expect(SDUI_BASE_PROPS.find((p) => p.name === key)?.scope).toBe('every-node'); - expect(diagnose({ ...LEAF, [key]: WELL_TYPED[key] })).toEqual([]); - }); -}); - -describe('objectui#11044 member 2 — a declared input outranks a `where-undeclared` base prop', () => { - it('the six subset-declared `BaseSchema` members are the `where-undeclared` ones', () => { - expect([...WHERE_UNDECLARED].sort()).toEqual(['ariaLabel', 'data', 'description', 'label', 'name', 'placeholder']); - }); - - it.each(WHERE_UNDECLARED)('`%s`: where the type declares it, a wrong-typed value keeps its `type-mismatch`', (key) => { - expect(diagnose({ type: 'field', [key]: WELL_TYPED[key] })).toEqual([]); - const found = diagnose({ type: 'field', [key]: 424242 }); - expect(found.map((d) => d.code)).toEqual(['type-mismatch']); - expect(found[0]!.message).toContain(`"${key}"`); - }); - - it.each(WHERE_UNDECLARED)('`%s`: where the type does not declare it, it is a base prop and draws nothing', (key) => { - expect(diagnose({ ...LEAF, [key]: WELL_TYPED[key] })).toEqual([]); - }); -}); - -/** One statement per line: diagnostics are mapped back to cases BY LINE. */ -const TSX_CASES: ReadonlyArray<{ what: string; code: string; rejected: boolean }> = [ - { what: 'control: declared inputs', rejected: false, code: `export const c0 = ;` }, - { what: '`bind` (objectui#11008)', rejected: false, code: `export const c1 = ;` }, - { what: '`hidden` (objectui#11008)', rejected: false, code: `export const c2 =