Skip to content

Commit a13568b

Browse files
committed
fix(rest,runtime): ?package=all names no package on every /meta door of both hosts
The layered read, the list, the book tree and the diagnostics sweep on RestServer, and the dispatcher's book tree, layered read, item read and save, and list, forwarded ?package=all to the store as a literal package id. The layered read answered 404, the list [], the book tree an empty implicit book, and the dispatcher's PUT bound the row to a package called all. Each now reads ?package= through metaItemPackageBinding, which @objectstack/rest exports from its entry for the runtime to take. Claude-Session: https://claude.ai/code/session_01RWZbGvPFcRKvUqASZtunCU Co-Authored-By: Claude <noreply@anthropic.com>
1 parent ffb31fc commit a13568b

6 files changed

Lines changed: 398 additions & 11 deletions

File tree

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
"@objectstack/rest": minor
3+
---
4+
5+
`metaItemPackageBinding` is exported, and every `/meta` door that reads `?package=` reads it through that function, so `?package=all` names no package on the layered read, the list, the book tree and the diagnostics sweep
6+
7+
Clause-②: yes (widening)
8+
9+
- `metaItemPackageBinding(raw)` answers the package a request's `?package=` names, or `undefined` for `all` (the metadata list's "show everything" scope), the empty value or a non-string. The item read, the save and the publish already read `?package=` through it. The runtime dispatcher's `/meta` domain now reads it through the same function.
10+
- `GET /meta/:type/:name/layers?package=all` answered `404` for an item stored in a package, and now serves the layers that the read without `?package=` serves. The Studio editor sends this read when it is opened from the list's "show everything" scope.
11+
- `GET /meta/:type?package=all` answered `[]`, and now lists what `GET /meta/:type` lists: the items of every package and the env-local ones.
12+
- `GET /meta/book/:name/tree?package=all` served the empty implicit book of a package called `all`, and now resolves the declared book as the read without `?package=` does.
13+
- `GET /meta/diagnostics?package=all` swept nothing, and now sweeps what the plain sweep sweeps.
14+
- Unchanged: a real package id still scopes each of these reads. The item read's cache bypass still reads the raw parameter, so `?package=all` keeps the uncached read and its served `version`.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
"@objectstack/runtime": patch
3+
---
4+
5+
The runtime dispatcher's `/meta` domain reads `?package=all` as naming no package, as `RestServer` does, so a `PUT ?package=all` through the `@objectstack/hono` catch-all no longer binds the row to a package called `all`
6+
7+
Clause-②: no
8+
9+
- The dispatcher's `PUT /meta/:type/:name?package=all` stored the row with `package_id: 'all'`. It now stores it env-local (`package_id` null), as `RestServer`'s `PUT` does.
10+
- Its layered read (`/meta/:type/:name/layers`), list (`/meta/:type`), book tree (`/meta/book/:name/tree`) and item read forwarded `all` to the store as a package id. The layered read answered `404` and the list `[]` for an item stored in a package. Each now answers `?package=all` as it answers the same request without `?package=`.
11+
- Every branch reads `?package=` through `metaItemPackageBinding` from `@objectstack/rest`, the function `RestServer`'s doors read it through.
12+
- Unchanged: a real package id still scopes each read and still binds a save.

‎packages/rest/src/index.ts‎

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,13 @@ export type {
183183
MetaSaveRequestMembers,
184184
MetaSaveRequestOptions,
185185
} from './meta-save-request.js';
186+
187+
// [#22188] …and the one reading of `?package=` on every `/meta` door: the
188+
// package a request names, or none for `all` (the list's "show everything"
189+
// scope), the empty value or a non-string. `RestServer`'s doors read it, and so
190+
// does the runtime dispatcher's `/meta` domain on its own copies of them — the
191+
// layered read, the list, the book tree and the item read and save — which
192+
// forwarded `all` to the store as a package id until this landed. It stays in
193+
// `rest-server.ts`, beside the doors that read it (its docblock is the
194+
// authority); this entry already loads that module.
195+
export { metaItemPackageBinding } from './rest-server.js';

‎packages/rest/src/rest-server.ts‎

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1736,8 +1736,20 @@ function mayReadPendingDrafts(caller: unknown): boolean {
17361736
* save then judged, and a client that pinned `If-None-Match: *` on that `null`
17371737
* was refused. ⛔ Never a third inline copy at a door: the save and publish
17381738
* doors each carried one, and the read door had none.
1739+
*
1740+
* [#22188] …and on every other `/meta` door that reads `?package=`: the
1741+
* layered read, the list, the book tree and the diagnostics sweep, which each
1742+
* forwarded `all` to the store as a package id no item is bound to (the
1743+
* layered read answered `404`, the list `[]`). Without `?package=`, the list
1744+
* already spans every package and the env-local overlay, so "no package" is
1745+
* the list's "show everything". Exported from the package entry, and the
1746+
* runtime dispatcher's `/meta` domain reads `?package=` through it on its own
1747+
* copies of these doors, so the two transports cannot read `all` differently.
1748+
* The item read's cache bypass (`packageScoped`) is not a second reading: it
1749+
* asks whether the raw parameter is present to pick the uncached arm, and that
1750+
* arm asks this function.
17391751
*/
1740-
function metaItemPackageBinding(raw: unknown): string | undefined {
1752+
export function metaItemPackageBinding(raw: unknown): string | undefined {
17411753
return typeof raw === 'string' && raw !== '' && raw !== 'all' ? raw : undefined;
17421754
}
17431755

@@ -3920,7 +3932,8 @@ export class RestServer {
39203932
// `getMetaItemLayered({ packageId: ['a','b'] })`. Gated in the helper,
39213933
// not in its two callers, so both entry points answer identically.
39223934
if (refuseRepeatedQueryParams(req, res, ['package'])) return;
3923-
const layeredPackageId = req.query?.package || undefined;
3935+
// [#22188] Read through {@link metaItemPackageBinding}: `all` names no package.
3936+
const layeredPackageId = metaItemPackageBinding(req.query?.package);
39243937
// [#9454] State the ORG scope, exactly as the `/published` overlay read
39253938
// already does. Without it the layered view resolved the env-wide row
39263939
// only, so an author who had just saved an org overlay opened Studio to
@@ -5804,7 +5817,9 @@ export class RestServer {
58045817
const result = await (p as any).getMetaDiagnostics({
58055818
type: diagnosticsType,
58065819
severity,
5807-
packageId: (req.query?.package as string | undefined) || undefined,
5820+
// [#22188] The list's reading ({@link metaItemPackageBinding}):
5821+
// each swept type is a list read, so `all` names no package.
5822+
packageId: metaItemPackageBinding(req.query?.package),
58085823
// SPREAD, never `organizationId: x ?? null` — the
58095824
// implementation declares `organizationId?: string`
58105825
// (optional plain string, not nullable), and a
@@ -6045,7 +6060,9 @@ export class RestServer {
60456060
// `query-multiplicity.ts` for why picking one of two
60466061
// conflicting intents is worse than a 400.
60476062
if (refuseRepeatedQueryParams(req, res, ['package', 'preview', 'object', 'include', 'id'])) return;
6048-
const packageId = req.query?.package || undefined;
6063+
// [#22188] Read through {@link metaItemPackageBinding}:
6064+
// `all`, this list's "show everything" scope, names no package.
6065+
const packageId = metaItemPackageBinding(req.query?.package);
60496066
const environmentId = isScoped ? req.params?.environmentId : undefined;
60506067
const p = await this.resolveProtocol(environmentId, req);
60516068
// [#9488] …and BEFORE any listing work: a `:type` that
@@ -6379,7 +6396,8 @@ export class RestServer {
63796396
const prot = await this.resolveProtocol(environmentId, req);
63806397
// [#6877] One package scopes the book lookup.
63816398
if (refuseRepeatedQueryParams(req, res, ['package'])) return;
6382-
const packageId = req.query?.package || undefined;
6399+
// [#22188] Read through {@link metaItemPackageBinding}: `all` names no package.
6400+
const packageId = metaItemPackageBinding(req.query?.package);
63836401
// [#20408] The route's whole answer is
63846402
// `createMetaBookTreeAnswer` in `./meta-item-read-gate.ts`
63856403
// — the book and doc reads, THE `DocsAudience` (#19790: one

0 commit comments

Comments
 (0)