Skip to content
17 changes: 17 additions & 0 deletions .changeset/24210-rest-layered-doors-disabled-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
'@objectstack/rest': minor
---

fix(rest)!: `/meta/:type/:name/layers` and the deprecated `?layers=true` answer a disabled package's non-object item as a missing name, on both transports

Clause-②: no (narrowing)

<!-- adr-0087: not-required (no-migration-prescription) the narrowing is the served set of two runtime read doors for an item whose owning package is disabled: no stored metadata changes shape, no spec key, export or error code is removed, and nothing must be rewritten; re-enabling the package restores the reads -->

**BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes.

- **Absent now, byte for byte the missing name's answer (`404 RESOURCE_NOT_FOUND`):** the layered view of an item of every type but `object` and `package` whose owning package is disabled, on `GET /meta/:type/:name/layers` and on `GET /meta/:type/:name?layers=true`, to every caller, an author who may write the item included. One step in the shared layered chain (`createMetaLayeredAnswer`) answers it, so the runtime dispatcher's two spellings answer the same. The owner is the one the by-name read's protection envelope names (`packageId`), read under the request's `?package=` scope, asked of the engine registry's `isPackageDisabled`. Objects already answered `404 OBJECT_PACKAGE_DISABLED` here.
- **Unchanged:** items of enabled packages, a name with nothing behind it, and the protocol's `getMetaItemLayered`, which keeps serving every layer to the readers that resolve with it (the security catalog echo, the packaged-permission-set lock gate).
- **Added, optional:** `MetaItemReadGateSources.isItemOfDisabledPackage` and `MetaLayeredRequest.packageId`. A host that supplies neither answers as before.

What changes for you: re-enable the package to read its items' layered views again.
16 changes: 16 additions & 0 deletions .changeset/24210-runtime-layered-doors-disabled-package.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'@objectstack/runtime': minor
---

fix(runtime)!: the dispatcher's `/meta/:type/:name/layers` and `?layers=true` answer a disabled package's non-object item as a missing name

Clause-②: no (narrowing)

<!-- adr-0087: not-required (no-migration-prescription) the narrowing is the served set of two runtime read doors for an item whose owning package is disabled: no stored metadata changes shape, no spec key, export or error code is added or removed, and nothing must be rewritten; re-enabling the package restores the reads -->

**BREAKING** (a served-set narrowing), shipped as `minor` under the launch-window convention for breaking changes.

- **Absent now, `404 RESOURCE_NOT_FOUND` "Not found", byte for byte the missing name's answer:** `GET /meta/:type/:name/layers` and `GET /meta/:type/:name?layers=true` (with its `Deprecation` header) for an item of every type but `object` and `package` whose owning package is disabled, to every caller. The owner is the one the by-name read's protection envelope names (`packageId`) under the request's `?package=` scope, as `RestServer` asks it. Objects already answered `404 OBJECT_PACKAGE_DISABLED` here.
- **Unchanged:** items of enabled packages, and a name with nothing behind it.

What changes for you: re-enable the package to read its items' layered views again.
21 changes: 21 additions & 0 deletions packages/objectql/src/package-disable-enforcement.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,27 @@ const READERS: Array<{
return ((await (protocol as any).getOverlayRepo().listDrafts({ packageId: PKG })) ?? []).length > 0;
},
},
{
reader: 'getMetaItemLayered({ type: "app", name }) — the producer under GET /meta/app/:name/layers and ?layers=true',
enforces: false,
why: '[#24210] the doors answer the absence (the shared layered chain\'s owner step, pinned per transport in @objectstack/rest and @objectstack/runtime); the producer keeps serving its resolution readers',
serves: async ({ protocol }) =>
(await protocol.getMetaItemLayered({ type: 'app', name: 'demo_app' }))?.effective != null,
},
{
reader: 'getMetaItemLayered({ type: "permission", name }) — the security catalog echo and the packaged-permission-set lock gate',
enforces: false,
why: '[#24210] plugin-security reads the layers directly, not through a door: a disabled package\'s permission set still echoes and stays locked',
serves: async ({ protocol }) =>
(await protocol.getMetaItemLayered({ type: 'permission', name: shippedName('permission', PKG) }))?.effective != null,
},
{
reader: 'getMetaItem(…).packageId — the owner the layered doors\' step and readServedPackageBinding read',
enforces: false,
why: '[#24210] the by-name read withholds the item but its kept envelope still names the owner, which is how the layered doors and the echo learn it',
serves: async ({ protocol }) =>
(await protocol.getMetaItem({ type: 'app', name: 'demo_app' }))?.packageId === PKG,
},
{
reader: 'assertObjectRegistered — the data plane (findData / getData funnel here)',
enforces: true,
Expand Down
Loading
Loading