Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .changeset/21967-view-expansion-per-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
'@objectstack/metadata-protocol': patch
---

fix(metadata-protocol): another package's withdrawal of a form holds at the anonymous form endpoints, whatever packages' copies of a view container are saved

Clause-②: no

- **What was wrong.** Where packages ship the same view container, the view list (`getMetaItems` for `view`) served one item for each name a saved environment-wide copy of that container expands: the copy's own expansion. Every other package's item of that name, shipped or saved, was left out. The anonymous form endpoints judge a withdrawal against the environment-wide view list, so they could miss another package's withdrawal of such a form.
- **What it does now.** The view list serves each package its own item of such a name:
- a package's saved copy of the container serves that package's item of each name it expands;
- a package-less saved copy stands in for every package that has no copy of its own (ADR-0048);
- any other package keeps its own item.

So another package's withdrawal of a form holds at the anonymous form endpoints, whatever packages' copies of the container are saved. The organization-scoped save check reads the same list, so it judges each package's item too.
- **A stored view row of exactly such a name** keeps its own package's slot only. A package-less row still serves every package's slot. Before, any package's row of the name kept every package's copy expansion of it out of the list.
- **The by-name read agrees.** `getMetaItem` naming a package serves the item that package's slot in the list serves. Where no copy belongs to that package, a package-less copy now stands in for it. A list scoped to a package (`GET /api/v1/meta/view?package=`) serves the same item in each slot the package lists. A package-less copy adds no item to that list.
- **What does not change.** Within one package, a later expansion of a name still replaces an earlier one, and the save door's view container collision check is unchanged. A by-name read that names no package answers as before. No key, export, status or error code changes.
2 changes: 1 addition & 1 deletion content/docs/ui/public-data-collection.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ A withdrawal is a kill switch across metadata layers. If the environment-wide de

**Forms a package ships.** A package's form is part of the environment-wide definition, not a separate layer beneath it. A definition parsed by the stack schema (strict `defineStack`, the default) gets the schema's default `enabled: false`, so a shipped form that keeps its link without setting `enabled: true` counts as withdrawn and an organization's copy cannot open it. A definition loaded without that parse (`defineStack(..., { strict: false })` or a hand-built manifest) is judged as written: a switch it leaves out is absent, which is not a withdrawal, so set `enabled: false` explicitly to ship a form closed. The environment-wide definition is the administrator's switch: an environment-wide save may open a form that the package ships closed.

**Known limit: packages and names.** A withdrawal of a view name closes that name in every package. When two packages each ship a view of the same name, one package's withdrawal also closes the other package's form of that name, so this may close more than was meant. The organization-scoped save check judges every package's environment-wide definition of the name. The endpoints do too, with one exception: where a package's environment-wide copy of a view container is saved, the endpoints read that copy's expansion alone for each form it expands, and can miss another package's withdrawal of that form, whether saved or shipped. To close such a form at the endpoints, withdraw it in every saved environment-wide copy of that container as well. Reading each package's expansion separately is tracked in #21967.
**Known limit: packages and names.** A withdrawal of a view name closes that name in every package. When two packages each ship a view of the same name, one package's withdrawal also closes the other package's form of that name, so this may close more than was meant. The organization-scoped save check judges every package's environment-wide definition of the name. The endpoints do too.

**Known limit.** The save check runs only when an organization's copy is saved or published. A copy that was already stored before the environment-wide withdrawal, or that a rollback or revert restores, is judged only by the endpoints, which match by served item name. If that copy keeps the form open under a different key or place than the environment-wide definition, the endpoints can still serve it. To close it, withdraw the form in that organization's copy too; the next organization-scoped save of a copy that keeps it open is refused.

Expand Down
7 changes: 3 additions & 4 deletions packages/metadata-core/src/anonymous-form-intake.ts
Original file line number Diff line number Diff line change
Expand Up @@ -326,10 +326,9 @@ function anonymousFormExplicitWithdrawals(view: unknown): Array<{ slot: string;
* name in every package only when its layer holds every package's body of the
* name. The organization-scoped write door anchors one body per package. The
* env-wide view list the anonymous doors read holds one item per package of a
* name, with one exception: where a package's env-wide copy of a view
* container is saved, the list holds that copy's expansion alone for each form
* it expands, so the doors can miss another package's withdrawal of that
* form, whether saved or shipped (per-package expansion is #21967). A layer
* name: a package's saved env-wide copy of a view container serves that
* package's item of each form it expands, and a package-less copy stands in
* for every package with no copy of its own. A layer
* with no body of the row, or whose body has no explicit withdrawal, withdraws
* nothing, so a form published only in an organization stays open there.
*/
Expand Down
Loading
Loading