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
14 changes: 14 additions & 0 deletions .changeset/22188-rest-meta-package-all-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
"@objectstack/rest": minor
---

`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

Clause-②: yes (widening)

- `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.
- `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.
- `GET /meta/:type?package=all` answered `[]`, and now lists what `GET /meta/:type` lists: the items of every package and the env-local ones.
- `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.
- `GET /meta/diagnostics?package=all` swept nothing, and now sweeps what the plain sweep sweeps.
- 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`.
12 changes: 12 additions & 0 deletions .changeset/22188-runtime-meta-package-all-fold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@objectstack/runtime": patch
---

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`

Clause-②: no

- 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.
- 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=`.
- Every branch reads `?package=` through `metaItemPackageBinding` from `@objectstack/rest`, the function `RestServer`'s doors read it through.
- Unchanged: a real package id still scopes each read and still binds a save.
10 changes: 10 additions & 0 deletions packages/rest/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,13 @@ export type {
MetaSaveRequestMembers,
MetaSaveRequestOptions,
} from './meta-save-request.js';

// [#22188] …and the one reading of `?package=` on every `/meta` door: the
// package a request names, or none for `all` (the list's "show everything"
// scope), the empty value or a non-string. `RestServer`'s doors read it, and so
// does the runtime dispatcher's `/meta` domain on its own copies of them — the
// layered read, the list, the book tree and the item read and save — which
// forwarded `all` to the store as a package id until this landed. It stays in
// `rest-server.ts`, beside the doors that read it (its docblock is the
// authority); this entry already loads that module.
export { metaItemPackageBinding } from './rest-server.js';
28 changes: 23 additions & 5 deletions packages/rest/src/rest-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1736,8 +1736,20 @@ function mayReadPendingDrafts(caller: unknown): boolean {
* save then judged, and a client that pinned `If-None-Match: *` on that `null`
* was refused. ⛔ Never a third inline copy at a door: the save and publish
* doors each carried one, and the read door had none.
*
* [#22188] …and on every other `/meta` door that reads `?package=`: the
* layered read, the list, the book tree and the diagnostics sweep, which each
* forwarded `all` to the store as a package id no item is bound to (the
* layered read answered `404`, the list `[]`). Without `?package=`, the list
* already spans every package and the env-local overlay, so "no package" is
* the list's "show everything". Exported from the package entry, and the
* runtime dispatcher's `/meta` domain reads `?package=` through it on its own
* copies of these doors, so the two transports cannot read `all` differently.
* The item read's cache bypass (`packageScoped`) is not a second reading: it
* asks whether the raw parameter is present to pick the uncached arm, and that
* arm asks this function.
*/
function metaItemPackageBinding(raw: unknown): string | undefined {
export function metaItemPackageBinding(raw: unknown): string | undefined {
return typeof raw === 'string' && raw !== '' && raw !== 'all' ? raw : undefined;
}

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