Repository navigation
feat(sdui-parser)!: one parser body in @objectstack/sdui-parser (#25712 2g PR 1) - #26211
Conversation
Unify the save gate's parser with the renderer copy's additions, per the 2g ruling on #25712: node-slot validation, the retired `body` dialect, html-tier provenance and the renderer copy's exports join this package. body-dialect.ts and provenance.ts move in from @object-ui/sdui-parser and keep their MIT notice (LICENSE-MIT ships with the package). This package's rules win where the copies disagreed: every type-mismatch and member-type-mismatch stays an error, and unconsumed-widget-option stays an inline literal. SchemaNode and ValidationResult stay exported as deprecated aliases of ParsedSchemaNode and ManifestValidationResult. The renderer copy's pure tests are ported; exact twins are skipped and the ui-only cases are merged into this package's twins. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
📓 Docs Drift CheckThis PR changes 1 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 3 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 1850004b8b3721c9f04441ca51a3b1bf1b8288e9 && git checkout 1850004b8b3721c9f04441ca51a3b1bf1b8288e9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 3d50a43bcb55053e0501d939b304242b41cfde19 f87ef4fdbe825603ff2d92ab85b529b060bfaf33 && git checkout -B drift-repro 3d50a43bcb55053e0501d939b304242b41cfde19 && git merge --no-ff f87ef4fdbe825603ff2d92ab85b529b060bfaf33
node scripts/docs-audit/affected-docs.mjs --json 3d50a43bcb55053e0501d939b304242b41cfde19
|
…alias walk (#25712) The ported base-props-one-list-11044 test gives every SDUI base prop a well-typed value, `visibleOn` included: the parser tier still accepts the deprecated key on every node (SDUI_BASE_PROPS). Under ui/ the walk never reached it; under packages/ the visibility-alias retirement walk reads it as a spec authoring. The file's job is to spell the key, so it joins the walk's structural exclusions beside the other "the key is still read here" pins. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
Resolves packages/sdui-parser/package.json: main's publishing metadata (#26198, engines) and this branch's LICENSE-MIT `files` entry both kept. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
…ot a minor (#25712) The changeset is a patch; the guard the comment names refuses a major. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS
Requested by Tesla · project thread
Part of #25712 (4b 2g PR 1: one parser body; Tier S).
Part of objectstack#25677.
Clause-②: yes (narrowing:
validateTreenow also judges the nodes held in a manifest entry's declaredslots, so a caller whose manifest carriesslotsgets diagnostics on slot-held nodes it did not get before)Metadata-protocol: none
维护者速读(草稿)
objectui#,ui/AGENTS.mdnests, 18 governance workflows merged or retired (parent #25677, 4b/6) #25712 记录 6418729849,E+B),把渲染器那份@object-ui/sdui-parser多出来的东西并进@objectstack/sdui-parser,包括节点槽位校验、已退役的body写法、html 层来源标记,以及它多导出的符号。body-dialect.ts和provenance.ts从 MIT 包整体搬入,保留 MIT 声明,并随包发布LICENSE-MIT。type-mismatch/member-type-mismatch仍然一律是 error;unconsumed-widget-option仍写成字面量。SchemaNode/ValidationResult改名为ParsedSchemaNode/ManifestValidationResult,旧名保留为@deprecated别名,不删任何导出。os validate/build/lint和os serve的保存入口)读的是仓里跟踪的sdui.manifest.json,也就是@objectstack/console随包带的那份。它 106 个条目里 0 个带slots,所以新增的槽位遍历在保存门上无事可做。只有自己用slotsFor生成清单的调用方才会走到它,今天只有渲染器,而渲染器那份解析器本来就在遍历。check:sdui-lockstep维持同一套语法和诊断码,其余部分各自漂移。本 PR 是 2g 的第 1 步。PR 2 把 ui 的 122 个引用改到本包并删除 ui 那份;PR 3 退役 lockstep 门。patch发布(元数据协议不变)。parseJsx产出的节点多了一个 JSON 看不见的 symbol 标记。回滚 = revert 本 PR。席位核对
(left for the dispatch seat)
Why
Two copies of the constrained JSX parser existed:
@objectstack/sdui-parser(packages/sdui-parser), which the save gate runs;@object-ui/sdui-parser(ui/packages/sdui-parser), which the renderer runs.check:sdui-lockstepheld them to one grammar region, one diagnostic-code set and one containment predicate, and they drifted everywhere else. Tesla's 2g ruling keeps@objectstack/sdui-parseras the one package with no forwarding release, deletes the old package, rewrites its importers and takes@object-ui/sdui-parserout of the fixed group. This PR is step 1: the body becomes one, here. The plan, module by module, is #25712 comment 6419004222.What changes
Source, module by module
input-type.tscodegen.ts.d.tsheader gains/* eslint-disable */and names@objectstack/sdui-parser. No checked-in output carries the header.parse.tsParsedSchemaNode. Every element is stamped withmarkHtmlTierNodeat production. The LOCKSTEP comments go; theforbidden-attrrationale no longer leans on the gate.types.tsParsedSchemaNode,ManifestValidationResultandManifestComponent.slots. KeepsSchemaNodeandValidationResultas@deprecatedaliases.index.tsmanifestFromConfigs'slotsForoption. Thebindingnote keeps this copy's pointer to the single record intypes.ts.validate.tsbodyreplacement in the unknown-prop branch.checkTypeandcheckMemberTypeskeep this package's rule: every mismatch is anerror. The LOCKSTEP comments go.dashboard-widget-options.tscode:stays an inline literal, because ofcheck:dispatcher-error-vocabulary. The header takes the renderer's census derivation, re-rooted to theui/paths now in this tree, and keeps the spec-side drift section thatcheck:widget-option-censusholds.body-dialect.tsprovenance.ts@object-ui/sdui-parser.html-tier-nodeis unchanged, because it is the cross-instance contract.New exports:
HTML_TIER_NODE,isHtmlTierNode,markHtmlTierNode;acceptsChildren,CHILD_LIST_KEY,SDUI_BASE_PROPS,SduiBaseProp,SduiBasePropScope;checkRetiredBodyDialect,RETIRED_CHILD_LIST_KEY;NodeSlotDeclarationLike,assertFullyLoaded,RegistryConfigLike.lazy.These cover every name the 2g-b importer inventory needs.
Licence
packages/sdui-parser/LICENSE-MITis a verbatim copy ofui/packages/sdui-parser/LICENSE(MIT, Copyright (c) 2024 ObjectQL), added tofiles.npm pack --dry-runlists it.scripts/check-published-files.mjsregisters it inEXTRA_ENTRIESwith its reason, because the gate admits no unexplainedfilesentry./*! … */notice that names the licence, the copyright line andLICENSE-MIT. esbuild keeps it in bothdist/index.jsanddist/index.mjs.LICENSE-MIT.licensefield staysApache-2.0.Behaviour, measured
scripts/gen-sdui-manifest-node.mjsgenerates the trackedsdui.manifest.jsonthroughmanifestFromConfigswithoutslotsFor, so 0 of its 106 entries carryslots.packages/console/build-dist.shcopies that file over the console'sdist/sdui.manifest.json, and that copy is whatresolveSduiManifestgivesos validate,build,lintandos serve's save door. The slot walk only works for a caller that builds a manifest withslotsFor. Today that is the renderer, whose own copy already walks the slots.body. The message now nameschildren. Abodychild list under a component that takes no children drawsnot-a-container, the code thechildrenspelling draws. Both stay warnings.objectui#6771, becausecheck:doc-authoringrefuses a tracker id in author-facing text (the module header keeps the citation). The renderer copy's text,("body" was retired by objectui#6771), becomes("body" is its retired spelling).node-slot-walk-11170pins the new text.parseJsxelements carry an enumerable, symbol-keyed marker.JSON.stringifyandObject.keysdo not see it. The consumers' suites pass unchanged (Verification).Tests (
packages/sdui-parser/src/__tests__)base-props-bind-hidden-11008,base-props-one-list-11044,body-dialect-6771,input-type-union,node-slot-walk-11170,provenance,tier. Path pointers in comments are re-rooted toui/.provenance's "objectui-only" sentence now says what was true until this change.containment-declared-slot+3: the base key, thebodyspelling, and theisContainerprojection. The import now comes from../index.js.dashboard-widget-options+3: the retired-sub-caption shapes and their control.type-attribute-collision+1: the no-typeregression guard with provenance. Re-ablated: restoring{ type: tag, ...props }keeps all nine green, so the header now says "nine".literal-subset's__proto__case is covered by this file'sit.each, andmember-kind's two bymember-type-mismatch.compile,html-tier-manifest-10735,inert-expression-6598,kanban-quick-add-8285.deprecated-type-aliases.test.ts(2 cases) pins both aliases at type level. Negative control: re-pointing one failspnpm typecheckwith TS2344.@object-ui/*, React or the spec, or reads ui sources:injected-component-input-6950,inline-whitespace.test.tsx,render.test.tsx,spec-symbol-batch7,dashboard-widget-options-census.warningfor a non-enum mismatch, so no expectation flipped.What this leaves for PR 2 and PR 3
@objectstack/sdui-parser. Old names are not for new code.errorin the Console too.ui/scripts/__tests__/unconsumed-widget-option-claim-6186.test.tsreads the ui copy'sdashboard-widget-options.tspath, and the census header here points at the census test's current path. Both move with it.bodymessage text is pinned inui/packages/components/src/renderers/__tests__/base-props-one-list-11044.test.tsxand quoted inui/content/docs/components/layout/semantic.mdx.scripts/sdui-manifest.sources/sdui-parser.jsonrecordsui/packages/sdui-parseras a registry source.check:sdui-lockstep. It is green on this head (9 and 9 sources agree), so the PRs can land in either order.Verification
Head
930df7bda, onmain6b9355299. 24 files, +1794 / −191, in two commits. Tier S: no governed path (each checked withcheck-governed-merges --test), under 3000 lines.@objectstack/sdui-parser:vitest run, 22 files, 349 tests pass.pnpm typecheckandpnpm build(tsup +check-dts-emitted) exit 0.Consumers, built against this dist:
packages/lint164 files / 7356 tests pass;packages/metadata-protocol266 files / 21539 tests pass (3 files / 19 tests skipped by their own guards).@objectstack/specvitest --project repo:1951e860e: 1 of 1279 failed,visibility-alias-keys-retirement. It is the same failure CI's Test Core (1/8) showed on that head.base-props-one-list-11044.test.ts, whose fixture gives the SDUI tier's deprecated-but-acceptedvisibleOnbase prop a value. Underui/the walk never read it.930df7bdaadds the file to the walk's structural exclusions with its reason. That file then passes 12/12, and main's same shard is green.pnpm check:sdui-lockstep: OK in-tree. The grammar region is byte-identical (214 lines), all 25 codes agree, the containment predicate agrees, across 9 and 9 sources. PR 3 can land before or after this one.Changeset gates:
check-changeset-no-major --eventwith this body's declaration: level axis ✓, patch withyes (narrowing)andnone.check-adr-0087-registration: ✓,not-required (no-migration-prescription).Gate sweep:
node scripts/pm/dispatch-gates.mjs --commandsderives 120 gates on930df7bda. All 120 ran and exited 0.--ranreconciles 120 derived, 120 run, 0 NOT-MEASURED, 0 UNRUN.check:api-surface,check:dual-source-exports,check:entry-nameability,check:exported-any) first refused with exit 3 on a stale local specdist. They were re-run green afterpnpm --filter @objectstack/spec build.check-issue-citations,check:doc-authoringandcheck:published-filesred; see Behaviour and Licence.1951e860e) was 100/100 green.Push battery (
GITHUB_ACTIONS=true GITHUB_EVENT_NAME=push node scripts/pm/check-dispatch-gates.mjs) on930df7bda: 2182 cases pass, 0 fail, EXIT 0. It was also 2182/0 on1951e860e.CI on
930df7bda: 41 success, 8 skipped, 0 failed; mergeableclean.🤖 Generated with Claude Code
https://claude.ai/code/session_01A6SEitNngAMugGbuJHWsDS