Repository navigation
Commit 56c8844
feat(spec): export the anonymous-form-intake candidates from @objectstack/spec/ui; metadata-core re-exports the same bindings (#22056)
Fixes #22047
Clause-②: yes (widening: new exports on the published
`@objectstack/spec/ui` entry; `@objectstack/spec` changeset at least
`minor`)
## What changes
- **New on `@objectstack/spec/ui`:** `publicFormSlug`,
`anonymousFormIntakeSlug`, `anonymousFormIntakeCandidates`,
`anonymousFormIntakeSlugs` and the `AnonymousFormIntakeCandidate` type.
This is the candidates half of the anonymous-form-intake rule, which the
triage ruling on objectstack-ai/objectui#11545 (`5967405932`) asks a
console to read instead of re-deriving "published" from the sharing
keys.
- **Moved, not copied.** The bodies are the ones that were in
`packages/metadata-core/src/anonymous-form-intake.ts`, unchanged except
for indentation (2 spaces, as in the rest of `packages/spec`). `diff -w`
between the BASE lines 52-105 and the new module is empty. The scan
still covers the three shapes in the same order: nested `form`, then
`formViews` entries, then a `viewKind: 'form'` item's `config`.
- **`@objectstack/metadata-core` re-exports the same bindings** (`export
{ … } from '@objectstack/spec/ui'`, plus `export type` for the
interface), so one copy remains. Its remaining code imports
`publicFormSlug` and the type from the spec. Its posture,
withdrawal-layer and object-name parts stay where they were.
`@objectstack/rest` and `@objectstack/metadata-protocol` are not edited
and keep importing from `metadata-core`.
`packages/metadata-core/src/index.ts` is unchanged.
- Changeset `.changeset/22047-spec-ui-anonymous-form-intake.md`:
`@objectstack/spec` `minor`, `@objectstack/metadata-core` `patch` (its
built `dist/index.{js,cjs,d.ts,d.cts}` now import these functions from
`@objectstack/spec/ui` instead of defining them).
## Where it lives, and why (H3)
It goes in a new module,
`packages/spec/src/ui/anonymous-form-intake.ts`, next to
`sharing.zod.ts`. It is not added to `sharing.zod.ts`, for three
reasons:
- **The module imports nothing.** It has no zod import and no schema
import, and does no work at load time. It stays as cheap as a browser
can import, whichever entry reaches it. `sharing.zod.ts` imports `zod`
and two schema helpers.
- **This is the existing pattern for runtime helpers in `spec/ui` that
do not live inside a schema module.** `chart-aggregate.ts`,
`i18n-label-resolver.ts` and `view-grouping-query.ts` are sibling
non-`.zod.ts` modules. `expandViewContainer` sits in `view.zod.ts` only
because it reads that module's member constants. These four functions
read no schema.
- **The file name matches the one in `metadata-core`,** so the move is
easy to follow in history.
Trade-off: `files[]` ships `src/**/*.zod.ts` as source, so this module's
source is not in the tarball. Its JS and declarations are, in
`dist/ui/index.{mjs,js,d.mts,d.ts}`.
Prime Directive 2 (no business logic in `packages/spec`: schemas, types
and constants only) holds here the way ADR-0053 D-D2 reads it. A pure
helper that states what the contract's own vocabulary denotes is
protocol, not business logic. It lives beside that vocabulary, and a
server package re-exports it: D-D2 moved `nextUtcCalendarDay` into
`@objectstack/spec/data` and has `@objectstack/core` re-export it. These
four functions only say which `sharing` declarations open a form. The
two checks that read server state (another layer's withdrawal, the
tenancy posture) stay in `metadata-core`. So does
`anonymousFormObjectName`, a pure read of the form and the view, because
that is where this export's surface was drawn. `expandViewContainer`
(`view.zod.ts`) is the placement precedent: a pure helper beside the
schema it serves. The reason two codebases must agree on this rule byte
for byte is the triage ruling on objectui#11545 (`5967405932`), as the
module's header now says (patch round 1, `6c5741c6`).
## Pins and measurements
**Identity pin (3).** This is in
`packages/metadata-core/src/anonymous-form-intake.test.ts`; the existing
cases are unchanged, and the pin adds 3 import lines and 1 `describe`.
For each of the four names it asserts that
`./anonymous-form-intake.js[name]` and `./index.js[name]` are `toBe`
(Object.is) `@objectstack/spec/ui[name]`.
- **Ablation:** run once and not kept, through
`scripts/ablation-replace.mjs` with the fix committed first. The
re-export of `anonymousFormIntakeCandidates` was replaced by a wrapper
that returns the spec function's answer.
- Result: `Tests 1 failed | 45 passed (46)`. Only the identity case for
`anonymousFormIntakeCandidates` failed (`expected [Function] to be
[Function] // Object.is equality`). Every behaviour test passed against
the wrapper, so only the identity pin can catch a copy.
- Restore was verified by the tool: the blob equals HEAD (`c08671875`)
and `git diff HEAD` is empty. metadata-core's test reads its own `src`
directly, so the mutation needed no rebuild to take effect.
**H5: identity in the built dual output.** A one-time node probe, run
from `packages/rest`, compared the four functions in metadata-core's
built output with `@objectstack/spec/ui`'s:
| condition | metadata-core export === spec/ui export |
|---|---|
| ESM (`dist/index.js` vs `dist/ui/index.mjs`) | true for all four |
| CJS (`dist/index.cjs` vs `dist/ui/index.js`) | true for all four |
| ESM vs CJS (cross-condition) | false: the dual-package split every
spec export already has |
**Parity pin (1).** `packages/spec/src/ui/anonymous-form-intake.test.ts`
(26 cases) pins each of the three shapes on its own (open; withdrawn by
either switch; no link), all three in one body in scan order, a `config`
without `viewKind: 'form'`, slug normalisation, and raw input against
`SharingConfigSchema.parse` input. Its expectations are the same as
metadata-core's existing ones. A one-time parity probe compared the BASE
metadata-core functions (`git show 8caa131`, lines 52-105) with the
built spec/ui and metadata-core functions now. It compared candidate
keys, key presence, slugs, whether each candidate is a form object from
the input, and the slug set:
| shape | bodies | with an open slug | BASE = spec/ui = metadata-core |
|---|---|---|---|
| nested `form` | 13 | 4 | 13 |
| `formViews` entry (plus an open sibling) | 13 | 13 | 13 |
| `viewKind: 'form'` + `config` | 13 | 4 | 13 |
| all three in one body | 13 | 4 | 13 |
| `config` without `viewKind: 'form'` | 13 | 0 | 13 |
| non-view input | 4 | 0 | 4 |
| real producers: showcase `inquiry.view.ts`, crm `lead.view.ts`, as
containers and as `expandViewContainer` items | 9 | 4 | 9 |
That is 78 bodies with 0 mismatches, and 21 leaf inputs
(`anonymousFormIntakeSlug`, `publicFormSlug`) with 0 mismatches.
**H1.** Lines 52-105 call nothing from `@objectstack/spec/security`,
`applyInjectedSystemColumns` or `resolveRecordWallOrganizationField`.
The new module has no imports, and the four bodies compile unchanged in
`packages/spec`.
**H2.** `./ui` is in `browser-reachable-entries.json`'s `unjudged` list,
so `check:browser-reachable-entries` asserts nothing about it (it
passed). The module is plain `function` declarations with no top-level
statements, and the package declares `"sideEffects": false`.
**Declaration surface downstream.** `metadata-core`'s `dist/index.d.ts`
and `.d.cts` now reference `@objectstack/spec/ui`. Measured with `tsc
--noEmit --extendedDiagnostics --listFiles`, building metadata-core from
BASE source and then from HEAD source. These are absolute numbers from a
shared box:
| program | files BASE → HEAD | memory BASE → HEAD |
|---|---|---|
| `packages/rest` | 579 → 580 | 1,246,583K → 1,268,472K |
| `packages/objectql` | 574 → 574 | 983,063K → 994,921K |
| `packages/plugins/plugin-security` | 495 → 495 | 1,079,234K →
1,084,561K |
| `packages/metadata-protocol` | 808 → 808 | 1,319,331K → 1,318,969K |
| `packages/qa/http-conformance` | 345 → 345 | 357,199K → 357,044K |
The one new file in `rest` is `spec/dist/ui/index.d.ts`, the CJS barrel,
reached through `metadata-core/dist/index.d.cts`. The chunks it
re-exports were already in that program in both flavours. tsc exited 0
in every program on both builds.
## Prose that named the old home (H4)
- `packages/spec/src/ui/sharing.zod.ts:19-23` said the rule lives in
`anonymousFormIntakeCandidates` "in `@objectstack/metadata-core`". It
now names `anonymous-form-intake.ts` beside that module, which
metadata-core re-exports to the server's doors.
- `content/docs/references/ui/sharing.mdx:22-26` was regenerated from
that docblock by `gen:docs`, not edited by hand.
- `packages/metadata-core/src/anonymous-form-intake.ts:13-20`: the
module docblock says the candidates half is declared in
`@objectstack/spec/ui`, whose docblock is now the authority on it. The
scan-shape paragraph moved with the code.
- Judged still true and left alone:
- `packages/rest/src/rest-server.ts:10698` reads
"(`anonymousFormIntakeCandidates`, `@objectstack/metadata-core`)". That
is where rest imports the function from, and metadata-core still exports
it. The file is also outside this card's surface.
- `packages/metadata-core/src/index.ts:141-145` ("both read this one
rule").
- The `docs/qa/platform-checklist` mechanism references name
`anonymousFormIntakeWithdrawnIn` and `anonymousFormExplicitWithdrawals`,
which stay in metadata-core.
## Verification (HEAD `cfdc8804f0`; the source tree is the same as
`3e9a9e48b1` plus the changeset)
- `pnpm --filter @objectstack/spec build` (JS + DTS): exit 0.
`check-dts-emitted` reported 38/38.
- `pnpm --filter @objectstack/spec check:generated`: all 15 artifacts up
to date. Before regeneration, 3 were stale: `api-surface/` (+5 rows in
`ui.json`), `export-origins/` (+5) and `content/docs/references/**` (the
H4 sentence). They were regenerated with `gen:api-surface`,
`gen:export-origins` and `gen:docs`.
- `check:api-surface`, `check:export-origins`, `check:docs`,
`check:exported-any`, `check:dual-source-exports` (0 accepted
dual-source), `check:entry-nameability`,
`check:browser-reachable-entries`, `check:liveness`, `check:llms-txt`,
`check:skill-examples`: exit 0.
- `pnpm --filter @objectstack/spec test`: 620 files, 18511 passed, 1
todo. The new file alone: 26 passed.
- `pnpm --filter @objectstack/metadata-core test`: 18 files, 415 passed.
`src/anonymous-form-intake.test.ts` alone: 46 passed.
- `pnpm --filter @objectstack/metadata-protocol test`: 219 files passed,
3 skipped; 28135 tests passed. The focused run of
`runtime-authoring-gate.public-form-intake`,
`protocol.runtime-authoring-gate` and
`protocol.org-scoped-write-refused` passed 257.
- `pnpm --filter @objectstack/rest test`: 260 files, 4914 passed, 326
skipped. The focused run of `public-form-routes`,
`public-form-routes.stored-row`, `public-form-withdrawal` and
`public-form-intake-availability` passed 76.
- `pnpm --filter @objectstack/spec typecheck` and `pnpm --filter
@objectstack/metadata-core typecheck`: exit 0. `--listFiles` shows both
new test files are in their packages' test programs.
- Gates derived by `node scripts/pm/dispatch-gates.mjs --commands` at
`cfdc8804f0`, and checked with `--ran`: 110 derived, 109 run, 0 unrun, 1
NOT MEASURED. The NOT MEASURED one is `pnpm check:dual-build-cjs-loads`,
exit 3, PREREQUISITE NOT MET: it needs every package built. The CJS half
of the H5 probe above loaded `metadata-core/dist/index.cjs` with
`require`. The 5 roster gates the lead list flagged under a touched
directory also pass (`check:meta-url-spelling` and `check:spec-changes`
through `check:generated`; `check:authz-resolver`,
`check:error-code-casing`, `check:filter-alias-parity`).
- Lint is a narrowed run, and it measures something: `eslint
--no-inline-config --format json` over the 6 changed TS files reported 6
files, 0 errors and 0 warnings. All 6 are in the population of
`eslint.config.mjs` (`files: ['**/*.{ts,…}']` minus `NEVER_LINTED`, and
`--print-config` resolves each one). That config never turns on
type-aware linting (no `parserOptions.project`, no typed rules; see its
own note near line 327), so this diff cannot change the result for any
file it does not touch. The repo-wide `pnpm lint` is left to CI.
## Acceptance notes
- **Not covered by this card** (the card's "Not this card" section):
whether another layer withdraws a form, whether the posture makes a form
unavailable, any server-side "published" answer, and the objectui page
change. objectui#11545 restarts once objectui uses a release that
carries these exports.
- `main` gained #22021 (a spec analytics change) after this branch was
cut. It touches none of these files and none of the generated artifacts,
so `main` is not merged here, and the merge queue rebuilds on the
current `main`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01GV6oYwgc1kWiUCb1YaprQ7)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2015c54 commit 56c8844
10 files changed
Lines changed: 336 additions & 73 deletions
File tree
- .changeset
- content/docs/references/ui
- packages
- metadata-core/src
- spec
- api-surface
- export-origins
- src/ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
23 | | - | |
24 | | - | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
25 | 27 | | |
26 | 28 | | |
27 | 29 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | 3 | | |
| 4 | + | |
4 | 5 | | |
5 | 6 | | |
6 | 7 | | |
| |||
15 | 16 | | |
16 | 17 | | |
17 | 18 | | |
| 19 | + | |
| 20 | + | |
18 | 21 | | |
19 | 22 | | |
20 | 23 | | |
| |||
315 | 318 | | |
316 | 319 | | |
317 | 320 | | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
24 | 21 | | |
25 | 22 | | |
26 | 23 | | |
27 | 24 | | |
28 | 25 | | |
29 | 26 | | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | 27 | | |
35 | 28 | | |
36 | 29 | | |
| |||
46 | 39 | | |
47 | 40 | | |
48 | 41 | | |
| 42 | + | |
49 | 43 | | |
50 | 44 | | |
51 | 45 | | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | | - | |
103 | | - | |
104 | | - | |
105 | | - | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
106 | 59 | | |
107 | 60 | | |
108 | 61 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| 46 | + | |
46 | 47 | | |
47 | 48 | | |
48 | 49 | | |
| |||
475 | 476 | | |
476 | 477 | | |
477 | 478 | | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
478 | 482 | | |
479 | 483 | | |
480 | 484 | | |
| |||
518 | 522 | | |
519 | 523 | | |
520 | 524 | | |
| 525 | + | |
521 | 526 | | |
522 | 527 | | |
523 | 528 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| 44 | + | |
44 | 45 | | |
45 | 46 | | |
46 | 47 | | |
| |||
460 | 461 | | |
461 | 462 | | |
462 | 463 | | |
| 464 | + | |
| 465 | + | |
| 466 | + | |
463 | 467 | | |
464 | 468 | | |
465 | 469 | | |
| |||
503 | 507 | | |
504 | 508 | | |
505 | 509 | | |
| 510 | + | |
506 | 511 | | |
507 | 512 | | |
508 | 513 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
0 commit comments