@haddowg/json-api-client 1.1.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -1
- package/dist/ajv.d.cts +1 -1
- package/dist/ajv.d.mts +1 -1
- package/dist/index.cjs +128 -37
- package/dist/index.d.cts +31 -9
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +31 -9
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +125 -38
- package/dist/index.mjs.map +1 -1
- package/dist/{validate-CfrCMZkA.d.cts → validate-CJPivkqT.d.cts} +382 -78
- package/dist/validate-CJPivkqT.d.cts.map +1 -0
- package/dist/{validate-CfrCMZkA.d.mts → validate-CJPivkqT.d.mts} +382 -78
- package/dist/validate-CJPivkqT.d.mts.map +1 -0
- package/package.json +1 -1
- package/dist/validate-CfrCMZkA.d.cts.map +0 -1
- package/dist/validate-CfrCMZkA.d.mts.map +0 -1
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["isObject","isObject","isObject"],"sources":["../src/errors.ts","../src/validate.ts","../src/materialise.ts","../src/types.ts","../src/request.ts","../src/serialize-write.ts","../src/atomic.ts","../src/transport.ts","../src/client.ts"],"sourcesContent":["/** A single JSON:API error object (spec: error objects). */\nexport interface JsonApiErrorObject {\n id?: string\n status?: string\n code?: string\n title?: string\n detail?: string\n source?: { pointer?: string; parameter?: string; header?: string }\n meta?: Record<string, unknown>\n /**\n * The user-facing input path, remapped from `source.pointer` for write errors\n * (e.g. `/data/attributes/title` -> `title`). Populated by the runtime, which\n * built the envelope and therefore knows the inverse mapping. See CONTEXT.md.\n */\n path?: string\n /**\n * The zero-based index of the failing operation within an atomic batch, parsed from a\n * `source.pointer` carrying an `/atomic:operations/{n}` prefix. Populated only for atomic\n * errors; `path` then holds the flat path within that operation's input.\n */\n opIndex?: number\n}\n\n/**\n * Thrown by the always-on light structural guards (ADR 0004) when a parsed response is not a\n * JSON:API document, or a `data`/`included` member is not a resource object carrying `type`+`id`.\n * Distinct from {@link JsonApiError} (a non-2xx server response): this signals the wire shape the\n * runtime relies on was violated, not an application error the server reported. The opt-in\n * per-field validator throws its engine's own error type, never this one.\n */\nexport class StructuralGuardError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'StructuralGuardError'\n }\n}\n\n/**\n * Thrown for any non-2xx JSON:API response. Carries the parsed error document plus\n * expressive status matchers and pointer-grouping helpers.\n */\nexport class JsonApiError extends Error {\n readonly status: number\n readonly errors: JsonApiErrorObject[]\n\n constructor(status: number, errors: JsonApiErrorObject[], message?: string) {\n super(message ?? `JSON:API request failed with status ${status}`)\n this.name = 'JsonApiError'\n this.status = status\n this.errors = errors\n }\n\n hasStatus(status: number): boolean {\n return this.status === status\n }\n\n is4xx(): boolean {\n return this.status >= 400 && this.status < 500\n }\n\n is5xx(): boolean {\n return this.status >= 500 && this.status < 600\n }\n\n isBadRequest(): boolean {\n return this.status === 400\n }\n\n isUnauthorized(): boolean {\n return this.status === 401\n }\n\n isForbidden(): boolean {\n return this.status === 403\n }\n\n isNotFound(): boolean {\n return this.status === 404\n }\n\n isNotAcceptable(): boolean {\n return this.status === 406\n }\n\n isConflict(): boolean {\n return this.status === 409\n }\n\n isUnsupportedMediaType(): boolean {\n return this.status === 415\n }\n\n isUnprocessable(): boolean {\n return this.status === 422\n }\n\n /** Alias of {@link isUnprocessable} — the JSON:API validation status. */\n isValidationError(): boolean {\n return this.status === 422\n }\n\n isRateLimited(): boolean {\n return this.status === 429\n }\n\n /**\n * Group errors by user-facing input path (falls back to the raw pointer, then the\n * query parameter). The payoff for form/validation UX — 422 `source.pointer`s are\n * remapped to the flat shape the caller actually supplied.\n */\n byPath(): Record<string, JsonApiErrorObject[]> {\n const out: Record<string, JsonApiErrorObject[]> = {}\n for (const e of this.errors) {\n const key = e.path ?? e.source?.pointer ?? e.source?.parameter ?? '_'\n ;(out[key] ??= []).push(e)\n }\n return out\n }\n}\n","import { StructuralGuardError } from './errors'\nimport type { Document } from './request'\n\n/**\n * A wire resource as it appears in a document's `data`/`included` — a JSON:API resource\n * object (or bare identifier). The structural guards assert `type` (and, where required,\n * `id`); the opt-in validator sees the whole object to check the rest against its schema.\n */\nexport interface WireResource {\n type: string\n id?: string\n [key: string]: unknown\n}\n\n/**\n * The opt-in per-resource validator: validate one wire `resource` against its JSON `schema`\n * (the bundle's per-type resource-object schema). The engine is the user's — throw (or\n * aggregate-then-throw) on an invalid resource; return for a valid one. With the bare-function\n * seam form there is no per-type schema, so `schema` is `undefined` and the function owns the\n * whole decision.\n */\nexport type Validator = (resource: WireResource, schema: unknown) => void\n\n/** A per-type JSON Schema map (the codegen-emitted `schemas` constant), keyed by JSON:API type. */\nexport type SchemaMap = Readonly<Record<string, unknown>>\n\n/**\n * The schema-driven validation seam: a per-type `schemas` map plus a `validator` engine. Each\n * wire resource is validated against `schemas[resource.type]`; a type with no schema is skipped.\n */\nexport interface ValidationConfig {\n schemas: SchemaMap\n validator: Validator\n}\n\n/**\n * The `validate?` option on {@link ClientOptions}: either the schema-driven {@link ValidationConfig}\n * (the common case — the codegen-emitted `schemas` map + an engine adapter) or a bare\n * {@link Validator} that owns schema lookup itself (it is called per resource with `schema` =\n * `undefined`). Absent => no per-field validation (zero overhead, no engine dependency).\n */\nexport type ValidationOption = ValidationConfig | Validator\n\n/** A normalised validator: resolve a resource's schema (or `undefined`) and validate it. */\nexport interface ResolvedValidator {\n validate(resource: WireResource): void\n}\n\n/** Whether a `validate` option is the schema-driven config (vs a bare validator function). */\nfunction isConfig(option: ValidationOption): option is ValidationConfig {\n return typeof option === 'object' && option !== null && !Array.isArray(option)\n}\n\n/**\n * Normalise the `validate?` option to a single per-resource validator. The config form looks the\n * schema up by `resource.type` and skips a type with no schema (the seam stays graceful for a\n * partially-covered bundle); the bare-function form calls the validator with `schema = undefined`,\n * delegating the whole decision to the caller. Returns `undefined` when validation is off.\n */\nexport function resolveValidator(\n option: ValidationOption | undefined,\n): ResolvedValidator | undefined {\n if (option === undefined) {\n return undefined\n }\n if (isConfig(option)) {\n const { schemas, validator } = option\n return {\n validate(resource) {\n const schema = schemas[resource.type]\n // A type the bundle does not cover is skipped, not failed — partial coverage is allowed.\n if (schema !== undefined) {\n validator(resource, schema)\n }\n },\n }\n }\n return {\n validate(resource) {\n option(resource, undefined)\n },\n }\n}\n\nconst isObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v)\n\n/**\n * The light structural guards every parsed response passes through (ADR 0004): assert the body\n * is a JSON:API document and that each `data`/`included` member carries a string `type` (and,\n * for a full resource, an `id`). This is the always-on, engine-free floor — it never duplicates\n * the opt-in per-field validation; it only proves the envelope invariant the runtime relies on.\n *\n * A primary-`data` resource and every `included` resource must carry `type`+`id`. A relationship\n * endpoint's primary data is pure resource-identifier linkage, which also carries `type`+`id`, so\n * the same guard holds; `linkage` is accepted for symmetry with the materialiser but the assertion\n * is identical (an identifier is `type`+`id`). `data: null` (an empty to-one) and a missing\n * `data` (a meta-only document) are valid and pass.\n */\nexport function assertJsonApiDocument(body: unknown): asserts body is Document {\n if (!isObject(body)) {\n throw new StructuralGuardError('Response body is not a JSON:API document (expected an object)')\n }\n // A JSON:API document must carry at least one of `data`, `errors`, or `meta`. An error\n // document never reaches here (a non-2xx throws a JsonApiError upstream), so the guard\n // requires a `data` or `meta` top-level member.\n if (!('data' in body) && !('meta' in body)) {\n throw new StructuralGuardError(\n 'Response body is not a JSON:API document (missing top-level \"data\" or \"meta\")',\n )\n }\n const data = body['data']\n if (Array.isArray(data)) {\n data.forEach((member, i) => assertResource(member, `data[${i}]`))\n } else if (data !== null && data !== undefined) {\n assertResource(data, 'data')\n }\n const included = body['included']\n if (included !== undefined) {\n if (!Array.isArray(included)) {\n throw new StructuralGuardError('Response \"included\" is not an array')\n }\n included.forEach((member, i) => assertResource(member, `included[${i}]`))\n }\n}\n\n/** Assert a single member is a resource object/identifier carrying string `type` + `id`. */\nfunction assertResource(member: unknown, at: string): void {\n if (!isObject(member)) {\n throw new StructuralGuardError(`${at} is not a JSON:API resource object`)\n }\n if (typeof member['type'] !== 'string') {\n throw new StructuralGuardError(`${at} is missing a string \"type\"`)\n }\n if (typeof member['id'] !== 'string') {\n throw new StructuralGuardError(`${at} (type \"${member['type']}\") is missing a string \"id\"`)\n }\n}\n\n/**\n * Run the opt-in validator over every wire resource in a document's `data` + `included`, each\n * against its per-type schema. Called only when a validator is configured (off by default). The\n * structural guards have already proven each member carries `type`+`id`, so the cast is sound.\n */\nexport function validateDocument(body: Document, validator: ResolvedValidator): void {\n const data = body.data\n if (Array.isArray(data)) {\n for (const member of data) {\n validateResourceMember(member, validator)\n }\n } else if (data !== null && data !== undefined) {\n validateResourceMember(data, validator)\n }\n if (body.included !== undefined) {\n // `included` always holds full resource objects.\n for (const member of body.included) {\n validator.validate(member as WireResource)\n }\n }\n}\n\n/**\n * Validate a `data` member, but only when it is a full RESOURCE OBJECT. A relationship-linkage\n * endpoint's `data` is bare resource IDENTIFIERS (`type`+`id`[+`meta`], no `attributes`/\n * `relationships`); the per-type schema describes the resource object, so running it over an\n * identifier would spuriously fail. The structural guard already covered the identifier shape.\n */\nfunction validateResourceMember(member: unknown, validator: ResolvedValidator): void {\n if (isObject(member) && ('attributes' in member || 'relationships' in member)) {\n validator.validate(member as WireResource)\n }\n}\n","import type { ApiDescriptor, PaginatorKind, RelationDescriptor } from './descriptor'\nimport type { Document } from './request'\nimport { assertJsonApiDocument, type ResolvedValidator, validateDocument } from './validate'\n\n/** A trimmed, by-reference top-level envelope shared by every resource from one response. */\nexport interface DocumentEnvelope {\n jsonapi?: Record<string, unknown>\n meta?: Record<string, unknown>\n links?: Record<string, unknown>\n}\n\n/** Resource-level links (`self` is the common one; the wire may carry more). */\nexport interface ResourceLinks {\n self?: string\n [key: string]: unknown\n}\n\n/** The relationship-instance envelope for a materialised related value (its `$edge`). */\nexport interface Edge {\n links?: Record<string, unknown>\n meta?: Record<string, unknown>\n}\n\n/** Normalised pagination, discriminated by paginator kind; count-free-safe. */\nexport interface Page {\n kind: PaginatorKind\n /** The raw `meta.page` block, when present (shape varies by kind). */\n meta?: Record<string, unknown>\n /** Navigation links by relation (`first`/`prev`/`next`/`last`), when present. */\n links: { first?: string; prev?: string; next?: string; last?: string }\n}\n\n/** The uniform shape `$rel(name)` returns. */\nexport interface RelationView {\n data?: unknown\n links?: Record<string, unknown>\n meta?: Record<string, unknown>\n}\n\n/** Non-enumerable envelope accessors shared by every materialised resource. */\nexport interface ResourceEnvelope {\n readonly $meta: Record<string, unknown> | undefined\n readonly $links: ResourceLinks | undefined\n readonly $self: string | undefined\n readonly $document: DocumentEnvelope\n readonly $raw: RawResource\n $rel(name: string): RelationView | undefined\n}\n\n/** Edge-local accessors carried by a materialised related VALUE (per-edge view). */\nexport interface EdgeEnvelope {\n readonly $edge: Edge | undefined\n readonly $pivot: Record<string, unknown> | undefined\n}\n\n/** Relationship-level accessors carried by an augmented to-many array. */\nexport interface ArrayEnvelope {\n readonly $page: Page\n readonly $links: Record<string, unknown> | undefined\n readonly $meta: Record<string, unknown> | undefined\n $next(options?: NavigateOptions): Promise<unknown>\n $prev(options?: NavigateOptions): Promise<unknown>\n}\n\n/** Per-call options for `$next()`/`$prev()`. */\nexport interface NavigateOptions {\n /** Cancels the page fetch when signalled. */\n signal?: AbortSignal\n}\n\n/** The runtime context materialise reads its descriptor + navigation seam from. */\nexport interface MaterialiseContext {\n descriptor: ApiDescriptor\n /**\n * Re-fetch + re-materialise a link (wired in Build 4); returns the materialised value.\n * `linkage` propagates the originating surface so a paginated relationship-endpoint\n * array re-materialises as identifiers (not resources) on `$next()`/`$prev()`. `paginator`\n * carries the current page's kind so the re-materialised page reports the same `$page.kind`\n * (a divergent per-relation paginator, or an EMPTY next page that carries no member to sniff).\n */\n navigate: (\n url: string,\n linkage?: boolean,\n paginator?: PaginatorKind,\n signal?: AbortSignal,\n ) => Promise<unknown>\n /**\n * The opt-in per-resource validator (ADR 0004), resolved from `ClientOptions.validate`. When\n * present, every wire resource in `data`/`included` is validated against its per-type schema\n * before the document is materialised. Absent => no per-field validation (the default).\n */\n validate?: ResolvedValidator | undefined\n}\n\n/** A raw JSON:API resource object (or bare identifier) off the wire. */\ninterface RawResource {\n type: string\n id: string\n meta?: Record<string, unknown>\n links?: ResourceLinks\n attributes?: Record<string, unknown>\n relationships?: Record<string, RawRelationship>\n}\n\ninterface RawRelationship {\n data?: unknown\n links?: Record<string, unknown>\n meta?: Record<string, unknown>\n}\n\nconst isObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v)\n\nconst define = (target: object, key: string, value: unknown): void => {\n Object.defineProperty(target, key, { value, enumerable: false, configurable: true })\n}\n\nconst defineGetter = (target: object, key: string, get: () => unknown): void => {\n Object.defineProperty(target, key, { get, enumerable: false, configurable: true })\n}\n\n/**\n * Materialise a JSON:API document into a hydrated graph (ADR 0002 / CONTEXT.md):\n *\n * - single resource -> a flattened resource object (own enumerable type/id/attributes/\n * relations + non-enumerable `$`-accessors);\n * - collection / related collection -> an augmented array of resource objects;\n * - relationship linkage -> an augmented array of identifier views (to-many) or a single\n * identifier / null (to-one).\n *\n * Included resources hydrate into nested per-edge views (reads through to the shared\n * node for attributes, owns its own `$edge`/`$pivot`); linked-but-not-included relations\n * stay as identifiers; links-only relations are `undefined`. The envelope rides\n * non-enumerable `$`-accessors so `{...res}` / `JSON.stringify(res)` are clean.\n *\n * `linkage` discriminates a relationship-endpoint response (primary `data` is pure\n * resource-identifier linkage) from a resource/collection response (primary `data` is a\n * full resource) — a structural guess can't tell them apart for an attribute-less,\n * relation-less resource, so the caller passes the surface it issued.\n *\n * `paginatorOverride` sets the top-level array's `$page.kind` for a related/relationship read,\n * where the paginator is the RELATION's (which may diverge from the related type's collection —\n * D6a) and an empty page carries no member to sniff a type from. `primaryType` is preferred for a\n * top-level collection read (statically-known); both override the sniff-from-`data[0]` fallback.\n */\nexport function materialise(\n doc: Document,\n ctx: MaterialiseContext,\n linkage = false,\n primaryType?: string,\n paginatorOverride?: PaginatorKind,\n): unknown {\n // Always-on light structural guards: prove the envelope invariant the runtime relies on (a\n // JSON:API document; each data/included member carries type+id). Then, only when configured,\n // run the opt-in per-field validation over each wire resource by its type (ADR 0004).\n assertJsonApiDocument(doc)\n if (ctx.validate !== undefined) {\n validateDocument(doc, ctx.validate)\n }\n\n const env: DocumentEnvelope = {}\n if (doc.jsonapi !== undefined) env.jsonapi = doc.jsonapi\n if (doc.meta !== undefined) env.meta = doc.meta\n if (doc.links !== undefined) env.links = doc.links\n\n const index = buildIndex(doc.included)\n // One node per type:id, shared by reference for attribute reads.\n const nodes = new Map<string, object>()\n const build = (raw: RawResource): object => buildNode(raw, env, index, nodes, ctx)\n\n const data = doc.data\n\n if (data === null) {\n return null\n }\n\n if (Array.isArray(data)) {\n const raws = data as RawResource[]\n const members = linkage\n ? raws.map((raw) => buildIdentifierView(raw))\n : raws.map((raw) => buildEdgeView(build(raw), raw))\n return augmentArray(members, doc, ctx, linkage, primaryType, paginatorOverride)\n }\n\n if (isObject(data)) {\n const raw = data as unknown as RawResource\n if (linkage) {\n return buildIdentifierView(raw)\n }\n return build(raw)\n }\n\n // No primary data (e.g. a meta-only document); expose nothing materialisable.\n return undefined\n}\n\n/** Index `included` by `type:id` so relationship linkage can be hydrated. */\nfunction buildIndex(included: unknown[] | undefined): Map<string, RawResource> {\n const index = new Map<string, RawResource>()\n if (!included) return index\n for (const raw of included) {\n if (isObject(raw) && typeof raw['type'] === 'string' && typeof raw['id'] === 'string') {\n index.set(key(raw['type'], raw['id']), raw as unknown as RawResource)\n }\n }\n return index\n}\n\nconst key = (type: string, id: string): string => `${type}:${id}`\n\n/**\n * Build (or reuse) the shared NODE for a resource: flattened type/id/attributes + hydrated\n * relations as own enumerable props, plus the resource-level `$`-accessors. The node is\n * memoised by `type:id` so attribute reads share one object; per-edge data never lands here.\n */\nfunction buildNode(\n raw: RawResource,\n env: DocumentEnvelope,\n index: Map<string, RawResource>,\n nodes: Map<string, object>,\n ctx: MaterialiseContext,\n): object {\n const id = key(raw.type, raw.id)\n const existing = nodes.get(id)\n if (existing) return existing\n\n const node: Record<string, unknown> = { type: raw.type, id: raw.id }\n // Seed the cache before recursing so a relation cycle resolves to this same node.\n nodes.set(id, node)\n\n if (raw.attributes) {\n for (const [k, v] of Object.entries(raw.attributes)) {\n node[k] = v\n }\n }\n\n const rels = raw.relationships ?? {}\n const resType = ctx.descriptor[raw.type]\n for (const [name, rel] of Object.entries(rels)) {\n if (!('data' in rel)) {\n // Links-only relation: no value to carry an edge -> undefined (reachable via $rel).\n continue\n }\n const relDesc = resType?.relations[name]\n node[name] = hydrateRelation(\n rel,\n relDesc?.cardinality,\n relatedPaginatorKind(relDesc, ctx),\n env,\n index,\n nodes,\n ctx,\n )\n }\n\n attachResourceEnvelope(node, raw, env, rels)\n return node\n}\n\n/**\n * Resolve one relationship's value: an included member hydrates to a per-edge view, a\n * linked-but-not-included member stays an identifier, an empty to-one is `null`, and a\n * to-many is always an augmented array (of views and/or identifiers).\n *\n * `relatedKind` is the related type's paginator kind (from the descriptor), threaded so a\n * to-many array reports the same `$page.kind` it would on its dedicated endpoint.\n */\nfunction hydrateRelation(\n rel: RawRelationship,\n cardinality: 'one' | 'many' | undefined,\n relatedKind: PaginatorKind,\n env: DocumentEnvelope,\n index: Map<string, RawResource>,\n nodes: Map<string, object>,\n ctx: MaterialiseContext,\n): unknown {\n const data = rel.data\n\n if (Array.isArray(data)) {\n const members = (data as RawResource[]).map((member) =>\n resolveMember(member, env, index, nodes, ctx),\n )\n return augmentRelationArray(members, rel, relatedKind, ctx)\n }\n\n // A declared to-many with no array yet (count-free / lazy) still reads as an array.\n if (data === undefined && cardinality === 'many') {\n return augmentRelationArray([], rel, relatedKind, ctx)\n }\n\n if (data === null || data === undefined) {\n return data === null ? null : undefined\n }\n\n if (isObject(data)) {\n // A to-one's edge envelope carries the relationship object's self/related links/meta\n // (CONTEXT.md: $edge = { links:{self,related}, meta }) so there's no need to go via\n // the parent's $rel for to-ones.\n return resolveMember(data as unknown as RawResource, env, index, nodes, ctx, {\n links: rel.links,\n meta: rel.meta,\n })\n }\n\n return undefined\n}\n\n/** The relationship-object envelope threaded onto a to-one member's `$edge`. */\ninterface RelationEdge {\n links: Record<string, unknown> | undefined\n meta: Record<string, unknown> | undefined\n}\n\n/** A single linkage member -> a hydrated per-edge view if included, else an identifier view. */\nfunction resolveMember(\n member: RawResource,\n env: DocumentEnvelope,\n index: Map<string, RawResource>,\n nodes: Map<string, object>,\n ctx: MaterialiseContext,\n relationEdge?: RelationEdge,\n): object {\n const included = index.get(key(member.type, member.id))\n if (included) {\n return buildEdgeView(buildNode(included, env, index, nodes, ctx), member, relationEdge)\n }\n return buildIdentifierView(member, relationEdge)\n}\n\n/** Attach the resource-level `$`-accessors ($meta/$links/$self/$document/$raw/$rel). */\nfunction attachResourceEnvelope(\n node: object,\n raw: RawResource,\n env: DocumentEnvelope,\n rels: Record<string, RawRelationship>,\n): void {\n defineGetter(node, '$meta', () => raw.meta)\n defineGetter(node, '$links', () => raw.links)\n defineGetter(node, '$self', () => raw.links?.self)\n defineGetter(node, '$document', () => env)\n defineGetter(node, '$raw', () => raw)\n define(node, '$rel', (name: string): RelationView | undefined => {\n const rel = rels[name]\n if (!rel) return undefined\n const view: RelationView = {}\n if ('data' in rel) view.data = rel.data\n if (rel.links !== undefined) view.links = rel.links\n if (rel.meta !== undefined) view.meta = rel.meta\n return view\n })\n}\n\n/**\n * A per-edge VIEW over a node: a distinct wrapper per membership that reads through to the\n * shared node for attributes (and the resource-level `$`-accessors) but owns its own\n * edge-local `$edge`/`$pivot`. Identity is by `type:id`, never reference.\n *\n * `relationEdge` (set for a to-one) carries the relationship object's self/related links and\n * meta, which ride the value's `$edge` per CONTEXT.md.\n */\nfunction buildEdgeView(node: object, member: RawResource, relationEdge?: RelationEdge): object {\n const view: Record<string, unknown> = {}\n // Copy the node's own enumerable props (type/id/attributes/relations) into this per-edge view.\n // INVARIANT (normalize.ts, ADR 0003): every cache patch must walk and patch ALL cached views\n // of a resource, not just the canonical node — `patchAll`/`walkResources`/`patchNode` do exactly\n // that, so these copied props stay consistent across writes. A future node-only write path would\n // leave edge views stale, so it must preserve this walk-every-view invariant.\n Object.assign(view, node)\n // Delegate the node's non-enumerable `$`-accessors so they read through to the node.\n for (const k of ['$meta', '$links', '$self', '$document', '$raw', '$rel'] as const) {\n delegate(view, node, k)\n }\n // A full-resource member's own `links` is its self-link (already on `$links`/`$self`), so a\n // to-many member's `$edge` is meta-only; a to-one additionally carries the relationship\n // object's self/related links via `relationEdge`.\n attachEdgeEnvelope(view, member, false, relationEdge)\n return view\n}\n\n/** An identifier VIEW for a linked-but-not-included member: a clean `{type,id,meta?}` + edge. */\nfunction buildIdentifierView(member: RawResource, relationEdge?: RelationEdge): object {\n const view: Record<string, unknown> = { type: member.type, id: member.id }\n if (member.meta !== undefined) view.meta = member.meta\n // A bare linkage member has no resource of its own, so any `links` it carries are edge links.\n attachEdgeEnvelope(view, member, true, relationEdge)\n return view\n}\n\n/**\n * Attach `$edge` (the member's identifier-level edge meta/links) and `$pivot` sugar over it.\n * For a to-one, the relationship object's self/related links + meta (`relationEdge`) merge in,\n * with member-level meta winning on key collision.\n */\nfunction attachEdgeEnvelope(\n view: object,\n member: RawResource,\n includeLinks: boolean,\n relationEdge?: RelationEdge,\n): void {\n defineGetter(view, '$edge', () => {\n const memberLinks = includeLinks ? member.links : undefined\n const links = memberLinks ?? relationEdge?.links\n const meta = mergeMeta(relationEdge?.meta, member.meta)\n if (links === undefined && meta === undefined) return undefined\n const edge: Edge = {}\n if (links !== undefined) edge.links = links\n if (meta !== undefined) edge.meta = meta\n return edge\n })\n defineGetter(view, '$pivot', () => {\n const pivot = member.meta?.['pivot']\n return isObject(pivot) ? pivot : undefined\n })\n}\n\n/** Merge a relationship-object meta with member-level meta (member wins on key collision). */\nfunction mergeMeta(\n base: Record<string, unknown> | undefined,\n override: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (base === undefined) return override\n if (override === undefined) return base\n return { ...base, ...override }\n}\n\n/** Mirror a non-enumerable accessor from `source` onto `target`, reading through live. */\nfunction delegate(target: object, source: object, k: string): void {\n const value = (source as Record<string, unknown>)[k]\n if (typeof value === 'function') {\n define(target, k, (value as (...a: unknown[]) => unknown).bind(source))\n } else {\n defineGetter(target, k, () => (source as Record<string, unknown>)[k])\n }\n}\n\n/**\n * A top-level / related collection array: `$page` from `doc`, links/meta from the document.\n * `linkage` propagates to `$next()`/`$prev()` so a relationship-endpoint page re-materialises\n * as identifiers, not resources.\n */\nfunction augmentArray(\n members: object[],\n doc: Document,\n ctx: MaterialiseContext,\n linkage: boolean,\n primaryType?: string,\n paginatorOverride?: PaginatorKind,\n): object[] {\n const arr = [...members]\n const links = doc.links\n defineGetter(arr, '$links', () => links)\n defineGetter(arr, '$meta', () => doc.meta)\n const kind = paginatorOverride ?? paginatorKind(doc, ctx, primaryType)\n attachPage(arr, kind, pageMeta(doc.meta), links, ctx, linkage)\n return arr\n}\n\n/**\n * A to-many relationship array: `$page`/links/meta come from the relationship object. `kind`\n * is the related type's paginator (so an in-document compound-include to-many reports the same\n * discriminant it would when fetched at its own relationship/related endpoint — \"one model,\n * three surfaces\").\n */\nfunction augmentRelationArray(\n members: object[],\n rel: RawRelationship,\n kind: PaginatorKind,\n ctx: MaterialiseContext,\n): object[] {\n const arr = [...members]\n const links = rel.links\n defineGetter(arr, '$links', () => links)\n defineGetter(arr, '$meta', () => rel.meta)\n attachPage(arr, kind, pageMeta(rel.meta), links, ctx, false)\n return arr\n}\n\n/** Attach `$page` + `$next()`/`$prev()` driven by link presence (count-free-safe). */\nfunction attachPage(\n arr: object[],\n kind: PaginatorKind,\n meta: Record<string, unknown> | undefined,\n links: Record<string, unknown> | undefined,\n ctx: MaterialiseContext,\n linkage: boolean,\n): void {\n const page: Page = { kind, links: pageLinks(links) }\n if (meta !== undefined) page.meta = meta\n defineGetter(arr, '$page', () => page)\n define(arr, '$next', (options?: NavigateOptions) =>\n navigateLink(page.links.next, ctx, linkage, kind, options?.signal),\n )\n define(arr, '$prev', (options?: NavigateOptions) =>\n navigateLink(page.links.prev, ctx, linkage, kind, options?.signal),\n )\n}\n\nconst navigateLink = (\n link: string | undefined,\n ctx: MaterialiseContext,\n linkage: boolean,\n paginator: PaginatorKind,\n signal: AbortSignal | undefined,\n): Promise<unknown> => {\n if (link === undefined) {\n return Promise.resolve(undefined)\n }\n return signal === undefined\n ? ctx.navigate(link, linkage, paginator)\n : ctx.navigate(link, linkage, paginator, signal)\n}\n\n/** Extract the navigation links (a link may be a string or a `{href}` object). */\nfunction pageLinks(links: Record<string, unknown> | undefined): Page['links'] {\n const out: Page['links'] = {}\n if (!links) return out\n for (const k of ['first', 'prev', 'next', 'last'] as const) {\n const href = linkHref(links[k])\n if (href !== undefined) out[k] = href\n }\n return out\n}\n\nconst linkHref = (link: unknown): string | undefined => {\n if (typeof link === 'string') return link\n if (isObject(link) && typeof link['href'] === 'string') return link['href']\n return undefined\n}\n\nconst pageMeta = (meta: Record<string, unknown> | undefined): Record<string, unknown> | undefined =>\n isObject(meta?.['page']) ? (meta['page'] as Record<string, unknown>) : undefined\n\n/**\n * The document's paginator kind (from the primary type's descriptor; default `none`). Prefers the\n * statically-known `primaryType` (passed by the descriptor binding at the call site) so an EMPTY\n * page still reports the collection's real kind; falls back to sniffing the first member's type\n * (for a re-materialised `$next()` page or a related read, where the primary type isn't threaded).\n */\nfunction paginatorKind(\n doc: Document,\n ctx: MaterialiseContext,\n primaryType?: string,\n): PaginatorKind {\n const first = Array.isArray(doc.data) ? doc.data[0] : doc.data\n const type =\n primaryType ??\n (isObject(first) && typeof first['type'] === 'string' ? first['type'] : undefined)\n return type !== undefined ? (ctx.descriptor[type]?.paginator ?? 'none') : 'none'\n}\n\n/**\n * A to-many relation's paginator kind. The relation's OWN related/relationship endpoint may\n * paginate differently than the related type's top-level collection (D6a), so prefer the\n * per-relation `paginator` (carried in the descriptor only when it diverges); otherwise fall back\n * to the related type's own paginator (the relationship and related endpoints both paginate that\n * type), so the discriminant is identical across the three surfaces. A polymorphic relation\n * resolves off its first declared related type (its members span types, matching the relationship\n * endpoint's first-member resolution).\n */\nexport function relatedPaginatorKind(\n relation: RelationDescriptor | undefined,\n ctx: MaterialiseContext,\n): PaginatorKind {\n if (relation?.paginator !== undefined) return relation.paginator\n const relatedType = relation?.types[0]\n if (relatedType === undefined) return 'none'\n return ctx.descriptor[relatedType]?.paginator ?? 'none'\n}\n","/** The JSON:API media type. */\nexport const JSON_API_MEDIA_TYPE = 'application/vnd.api+json'\n\n/** The Atomic Operations extension URI. */\nexport const ATOMIC_EXT = 'https://jsonapi.org/ext/atomic'\n\n/**\n * The media-type parameters a request opts into: extension URIs (`ext`) and/or profile URIs\n * (`profile`). Each is rendered as a space-joined, quoted media-type parameter per RFC 6839 /\n * the JSON:API spec (`ext=\"<uri> <uri>\"`, `profile=\"<uri> <uri>\"`).\n */\nexport interface MediaTypeParams {\n ext?: readonly string[] | undefined\n profiles?: readonly string[] | undefined\n}\n\n/**\n * Compose the JSON:API media type with any opted-in `ext`/`profile` parameters. Each parameter\n * is a space-joined, double-quoted URI list, in the canonical order `ext` then `profile` so the\n * output is deterministic (stable for caching/assertions). Empty/absent lists are omitted; with\n * neither, the bare {@link JSON_API_MEDIA_TYPE} is returned (no behaviour change for a plain\n * request). Used for both `Accept` and (when a body is present) `Content-Type`.\n */\nexport function mediaType(params: MediaTypeParams = {}): string {\n let out = JSON_API_MEDIA_TYPE\n if (params.ext !== undefined && params.ext.length > 0) {\n out += `; ext=\"${params.ext.join(' ')}\"`\n }\n if (params.profiles !== undefined && params.profiles.length > 0) {\n out += `; profile=\"${params.profiles.join(' ')}\"`\n }\n return out\n}\n\n/**\n * The media type for an Atomic Operations request/response: the JSON:API media type carrying\n * the atomic `ext` parameter. Sent as both `Content-Type` and `Accept` on a `client.atomic`\n * batch (per the JSON:API atomic extension).\n */\nexport const ATOMIC_MEDIA_TYPE = mediaType({ ext: [ATOMIC_EXT] })\n\n/** A resource identifier: the shape a non-included but linked relation takes. */\nexport interface ResourceIdentifier<TType extends string = string> {\n type: TType\n id: string\n meta?: Record<string, unknown>\n}\n\n/** A local id reference used inside an atomic transaction (`{ type, lid }`). */\nexport interface LocalIdentifier<TType extends string = string> {\n type: TType\n lid: string\n}\n","import type { HeadersInit } from './client'\nimport { JsonApiError, type JsonApiErrorObject } from './errors'\nimport type { JsonApiTransport, TransportRequest } from './transport'\nimport { mediaType } from './types'\n\n/**\n * A read query, in the flat shape the fluent surface accepts. Serialised to the\n * JSON:API bracketed query-parameter families by {@link serializeQuery}.\n */\nexport interface ReadQuery {\n filter?: Record<string, unknown>\n sort?: string | string[]\n include?: string[]\n fields?: Record<string, string[]>\n /** Relationship-count tokens (the Countable profile) — serialised comma-joined onto `withCount`. */\n withCount?: readonly string[]\n page?: Record<string, unknown>\n /** Cancels the read when signalled. Never serialised; passed to the transport. */\n signal?: AbortSignal\n}\n\n/** A loose JSON:API document — enough structure for the runtime to materialise. */\nexport interface Document {\n data?: unknown\n included?: unknown[]\n links?: Record<string, unknown>\n meta?: Record<string, unknown>\n jsonapi?: Record<string, unknown>\n}\n\n/** The ambient context a request executes in (carried by the client). */\nexport interface JsonApiContext {\n baseUrl: string\n transport: JsonApiTransport\n headers?: () => HeadersInit | Promise<HeadersInit>\n}\n\n/** A single read/write request, before serialisation. */\nexport interface JsonApiRequest {\n method: string\n /** A path relative to `baseUrl`, or an absolute `http(s)://` URL (e.g. a page link). */\n path: string\n query?: ReadQuery\n body?: unknown\n /**\n * Override the request `Content-Type` (defaults to the negotiated JSON:API media type when a\n * body is present). Set for a custom action's `raw` input, where the payload is not a JSON:API\n * document. When set it wins over {@link ext}/{@link profiles} for `Content-Type`.\n */\n contentType?: string\n /**\n * Override the request `Accept` header (defaults to the negotiated JSON:API media type). Set\n * for a `raw`-input action that still expects a document response; when set it wins over\n * {@link ext}/{@link profiles} for `Accept`.\n */\n accept?: string\n /**\n * Extension URIs to negotiate (the media-type `ext` parameter), e.g. the atomic ext. Composed\n * into both `Accept` and (when a body is present) `Content-Type` via the JSON:API media type,\n * unless {@link accept}/{@link contentType} override the respective header.\n */\n ext?: readonly string[]\n /**\n * Profile URIs to negotiate (the media-type `profile` parameter), e.g. the Countable profile a\n * `withCount` read requires. Composed into both `Accept` and (when a body is present)\n * `Content-Type`, unless {@link accept}/{@link contentType} override the respective header.\n */\n profiles?: readonly string[]\n /**\n * Send the body verbatim (a `string`) instead of JSON-stringifying it. Used for a `raw`\n * action body that's already serialised; a non-string body still falls back to\n * `JSON.stringify`.\n */\n raw?: boolean\n /** Passed through to the transport so the caller can cancel the request. */\n signal?: AbortSignal\n}\n\nconst isAbsolute = (path: string): boolean => /^https?:\\/\\//i.test(path)\n\nconst append = (parts: string[], key: string, value: unknown): void => {\n if (value === undefined || value === null || value === '') {\n return\n }\n parts.push(`${key}=${encodeURIComponent(String(value))}`)\n}\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/**\n * Append a filter value under its (already-bracketed) key. A plain object is a STRUCTURED filter\n * value — a `Range`/`DateRange` deepObject (`{ min, max }`) — and recurses into nested bracketed\n * keys (`filter[k][min]=`), matching the server's deepObject wire shape; an array joins with `,`;\n * a scalar is appended directly. Without the object branch a `{ min, max }` value would reach the\n * wire as `filter[k]=[object Object]` and be silently ignored by the server (D23).\n */\nconst appendFilterValue = (parts: string[], key: string, value: unknown): void => {\n if (isPlainObject(value)) {\n for (const [subKey, subValue] of Object.entries(value)) {\n appendFilterValue(parts, `${key}[${subKey}]`, subValue)\n }\n return\n }\n append(parts, key, Array.isArray(value) ? value.join(',') : value)\n}\n\n/**\n * Serialise a read query to a query string (no leading `?`). Bracketed keys\n * (`filter[title]`, `page[number]`, `fields[albums]`) are kept literal; only values\n * are percent-encoded. Empty/undefined entries are skipped; key order is deterministic\n * (family order, then insertion order within a family) so URLs are stable for caching.\n */\nexport function serializeQuery(query: ReadQuery): string {\n const parts: string[] = []\n\n if (query.filter) {\n for (const [key, value] of Object.entries(query.filter)) {\n appendFilterValue(parts, `filter[${key}]`, value)\n }\n }\n\n if (query.sort !== undefined) {\n append(parts, 'sort', Array.isArray(query.sort) ? query.sort.join(',') : query.sort)\n }\n\n if (query.include) {\n append(parts, 'include', query.include.join(','))\n }\n\n if (query.fields) {\n for (const [type, names] of Object.entries(query.fields)) {\n // An explicitly-empty fieldset (`fields[type]=`) is meaningful in JSON:API — it selects NO\n // members of that type (id-only), which is exactly what the return type narrows to. Emit the\n // empty param verbatim (the generic empty-skip would drop it, leaving the type over-narrowed\n // vs. the full resource the server would otherwise return).\n parts.push(`fields[${type}]=${encodeURIComponent(names.join(','))}`)\n }\n }\n\n if (query.withCount && query.withCount.length > 0) {\n append(parts, 'withCount', query.withCount.join(','))\n }\n\n if (query.page) {\n for (const [key, value] of Object.entries(query.page)) {\n append(parts, `page[${key}]`, Array.isArray(value) ? value.join(',') : value)\n }\n }\n\n return parts.join('&')\n}\n\n/**\n * Execute a request: build the URL (verbatim when already absolute — for page links —\n * else `baseUrl + path`), apply content negotiation and any async per-request headers,\n * then drive the transport. A 2xx with a body parses to a {@link Document}; an empty /\n * 204 body resolves to `undefined`. A non-2xx throws a {@link JsonApiError} carrying the\n * parsed error objects (tolerating a non-JSON error body with an empty list).\n */\nexport async function execute(\n ctx: JsonApiContext,\n req: JsonApiRequest,\n): Promise<Document | undefined> {\n let url = isAbsolute(req.path) ? req.path : ctx.baseUrl + req.path\n if (req.query) {\n const qs = serializeQuery(req.query)\n if (qs) {\n url += (url.includes('?') ? '&' : '?') + qs\n }\n }\n\n // Compose the negotiated media type (`application/vnd.api+json` + any opted-in ext/profile\n // parameters) once; a bare request yields the plain JSON:API media type. An explicit\n // `accept`/`contentType` override wins over the negotiated value for its header.\n const negotiated = mediaType({ ext: req.ext, profiles: req.profiles })\n const headers: Record<string, string> = { Accept: req.accept ?? negotiated }\n\n const transportReq: TransportRequest = { method: req.method, url, headers }\n if (req.signal !== undefined) {\n transportReq.signal = req.signal\n }\n if (req.body !== undefined) {\n headers['Content-Type'] = req.contentType ?? negotiated\n transportReq.body =\n req.raw && typeof req.body === 'string' ? req.body : JSON.stringify(req.body)\n }\n\n if (ctx.headers) {\n mergeHeaders(headers, await ctx.headers())\n }\n\n const res = await ctx.transport(transportReq)\n\n if (res.status >= 200 && res.status < 300) {\n return parseDocument(res.body)\n }\n\n throw new JsonApiError(res.status, parseErrors(res.body))\n}\n\n/**\n * Merge a `HeadersInit` (object / tuple array / `Headers`) into the outgoing map,\n * preserving the caller's casing (unlike the WHATWG `Headers`, which lowercases). The\n * caller's headers override the runtime defaults, so per-request `Accept` etc. can win.\n */\nfunction mergeHeaders(into: Record<string, string>, extra: HeadersInit): void {\n if (extra instanceof Headers) {\n extra.forEach((value, key) => {\n into[key] = value\n })\n } else if (Array.isArray(extra)) {\n for (const [key, value] of extra) {\n if (key !== undefined && value !== undefined) {\n into[key] = String(value)\n }\n }\n } else {\n for (const [key, value] of Object.entries(extra)) {\n into[key] = String(value)\n }\n }\n}\n\nfunction parseDocument(body: string): Document | undefined {\n if (body === '') {\n return undefined\n }\n return JSON.parse(body) as Document\n}\n\nfunction parseErrors(body: string): JsonApiErrorObject[] {\n if (body === '') {\n return []\n }\n try {\n const doc = JSON.parse(body) as { errors?: JsonApiErrorObject[] }\n return Array.isArray(doc.errors) ? doc.errors : []\n } catch {\n return []\n }\n}\n","/**\n * The write serialisation layer (CONTEXT.md \"Write surface\" + \"Transport seam & error\n * model\"; ADR 0001/0002). Two descriptor-aware inverses of the wire shape:\n *\n * - {@link toDocument} builds the JSON:API write document from the caller's FLAT input\n * (the client owns the envelope), routing each key to `attributes` or `relationships`\n * by the descriptor and coercing relationship linkage by cardinality;\n * - {@link remapPointer} / {@link withRemappedPaths} invert a write error's\n * `source.pointer` back to that flat path so {@link JsonApiError.byPath} groups by the\n * shape the caller actually supplied (form/validation UX).\n */\nimport type { ApiDescriptor } from './descriptor'\nimport { JsonApiError, type JsonApiErrorObject } from './errors'\n\n/** Per-edge writable pivot data, supplied on a to-many member as the `$pivot` key. */\nconst PIVOT_KEY = '$pivot'\n\n/** The reserved input keys never routed to attributes/relationships. */\nconst RESERVED_INPUT: ReadonlySet<string> = new Set(['type', 'id'])\n\nconst isObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v)\n\n/**\n * A JSON:API resource identifier in a write document: `{ type, id }` or — inside an atomic\n * batch — `{ type, lid }` (a reference to a resource created earlier in the same transaction).\n * May carry writable pivot meta on a to-many member.\n */\ninterface WriteIdentifier {\n type: string\n id?: string\n lid?: string\n meta?: { pivot: Record<string, unknown> } & Record<string, unknown>\n}\n\n/** A relationship document: `{ data: <linkage> }`. */\nexport interface RelationshipDocument {\n data: WriteIdentifier | WriteIdentifier[] | null\n}\n\n/** The JSON:API write document {@link toDocument} produces. */\nexport interface WriteDocument {\n data: {\n type: string\n id?: string\n attributes?: Record<string, unknown>\n relationships?: Record<string, RelationshipDocument>\n }\n}\n\n/** Options for {@link toDocument}. */\nexport interface ToDocumentOptions {\n /**\n * The resource id, supplied for an UPDATE (it comes from the handle, not the input). When\n * present it is always written as `data.id`. Omit for a CREATE — then `input.id` is written\n * only per the type's `clientId` policy.\n */\n id?: string\n}\n\n/**\n * Build a JSON:API write document from FLAT input. Each input key is routed via the\n * descriptor: a declared relation -> `data.relationships[name] = { data: <linkage> }`;\n * anything else -> `data.attributes[name]`. Relationship linkage is coerced by cardinality:\n *\n * - to-one accepts an identifier `{type,id}`, a materialised resource object (its `{type,id}`\n * are extracted), or `null` (-> `data: null`, clearing the relationship);\n * - to-many accepts an array of those; a member may carry a `$pivot` object, rendered as the\n * member identifier's `meta.pivot` (writable pivot fields).\n *\n * `data.type` is always the accessor `type`. `data.id` is written from `opts.id` (an update),\n * else from `input.id` only when the type's `clientId` policy allows it (omitted when\n * `forbidden`, passed through when `optional`/`required`).\n */\nexport function toDocument(\n descriptor: ApiDescriptor,\n type: string,\n input: Record<string, unknown>,\n opts?: ToDocumentOptions,\n): WriteDocument {\n const resource = descriptor[type]\n const relations = resource?.relations ?? {}\n\n const data: WriteDocument['data'] = { type }\n\n const id = resolveId(input, opts, resource?.clientId)\n if (id !== undefined) {\n data.id = id\n }\n\n const attributes: Record<string, unknown> = {}\n const relationships: Record<string, RelationshipDocument> = {}\n\n for (const [keyName, value] of Object.entries(input)) {\n if (RESERVED_INPUT.has(keyName)) {\n continue\n }\n const relation = relations[keyName]\n if (relation !== undefined) {\n relationships[keyName] = { data: coerceLinkage(value, relation.cardinality) }\n } else {\n attributes[keyName] = value\n }\n }\n\n if (Object.keys(attributes).length > 0) {\n data.attributes = attributes\n }\n if (Object.keys(relationships).length > 0) {\n data.relationships = relationships\n }\n\n return { data }\n}\n\n/**\n * Build a relationship-mutation document `{ data: <linkage> }` for the\n * `/{type}/{id}/relationships/{rel}` endpoints, coercing the caller's refs by cardinality:\n *\n * - to-one accepts an identifier `{type,id}`, a materialised resource object, or `null`\n * (-> `data: null`, clearing the relationship);\n * - to-many accepts an array of those; a member may carry `$pivot`, rendered as the member\n * identifier's `meta.pivot`.\n *\n * The same {@link coerceLinkage} the whole-resource writer uses, so a relationship written\n * standalone and one embedded in a resource serialise identically.\n */\nexport function toRelationshipDocument(\n refs: unknown,\n cardinality: 'one' | 'many',\n): RelationshipDocument {\n return { data: coerceLinkage(refs, cardinality) }\n}\n\n/** Resolve the document id: an update's handle id always wins; a create honours the policy. */\nfunction resolveId(\n input: Record<string, unknown>,\n opts: ToDocumentOptions | undefined,\n clientId: ApiDescriptor[string]['clientId'] | undefined,\n): string | undefined {\n if (opts?.id !== undefined) {\n return opts.id\n }\n if (clientId === 'forbidden') {\n return undefined\n }\n const candidate = input['id']\n return typeof candidate === 'string' ? candidate : undefined\n}\n\n/** Coerce one relationship value to linkage by cardinality (array for to-many, identifier/null for to-one). */\nfunction coerceLinkage(\n value: unknown,\n cardinality: 'one' | 'many',\n): WriteIdentifier | WriteIdentifier[] | null {\n if (cardinality === 'many') {\n const members = Array.isArray(value) ? value : value == null ? [] : [value]\n return members.map((member) => toIdentifier(member))\n }\n if (value === null || value === undefined) {\n return null\n }\n return toIdentifier(value)\n}\n\n/**\n * Extract a resource identifier from a linkage input value: an identifier `{type,id}`, a\n * materialised resource object (the same `{type,id}` enumerable props), or — inside an atomic\n * batch — a local identifier `{type,lid}` referencing a just-created resource (the handle a\n * `tx.create` returns). A `$pivot` key is lifted onto `meta.pivot` (writable pivot fields).\n * Throws on a value carrying no `type`, or neither an `id` nor a `lid`.\n */\nfunction toIdentifier(value: unknown): WriteIdentifier {\n if (!isObject(value)) {\n throw new TypeError(\n `Relationship linkage must be an identifier or resource object; received ${describe(value)}`,\n )\n }\n const { type, id, lid } = value\n if (typeof type !== 'string') {\n throw new TypeError('Relationship linkage requires a `type`')\n }\n // An `id` identifies an existing resource; a `lid` references one created earlier in the\n // same atomic transaction. Exactly one is needed.\n const identifier: WriteIdentifier =\n typeof id === 'string'\n ? { type, id }\n : typeof lid === 'string'\n ? { type, lid }\n : (() => {\n throw new TypeError('Relationship linkage requires an `id` or a `lid`')\n })()\n\n const pivot = value[PIVOT_KEY]\n if (isObject(pivot)) {\n identifier.meta = { pivot }\n }\n return identifier\n}\n\nconst describe = (value: unknown): string =>\n value === null ? 'null' : Array.isArray(value) ? 'an array' : typeof value\n\n/**\n * Invert a write error's `source.pointer` to the caller's FLAT input path, descriptor-aware:\n *\n * - `/data/attributes/title` -> `title`\n * - `/data/relationships/artist/data` -> `artist`\n * - `/data/id` -> `id` (client-id create conflict)\n * - `/data/attributes/releaseInfo/label` -> `releaseInfo.label` (nested map)\n * - `/data/relationships/orderedTracks/data/0/meta/pivot/position` -> `orderedTracks[0].$pivot.position`\n *\n * Pivot values nest under a member's `meta.pivot` (symmetric with reads — see\n * `JsonPointerBuilder::forLinkageMeta` / bundle ADR 0103), so a `meta/pivot/<field>` tail on\n * a member of a pivot-bearing to-many is surfaced as `.$pivot.<field>`.\n *\n * A query-side pointer is left untouched (those are already user-facing — see\n * {@link withRemappedPaths}). An unrecognised / non-write pointer is returned verbatim.\n * `/data/type` and a bare `/data` are passed through (the client owns those; the caller\n * never supplies them).\n */\nexport function remapPointer(descriptor: ApiDescriptor, type: string, pointer: string): string {\n const segments = pointer.split('/').filter((s) => s !== '')\n if (segments[0] !== 'data') {\n return pointer\n }\n\n if (segments[1] === 'attributes') {\n return remapAttribute(segments.slice(2))\n }\n if (segments[1] === 'id') {\n return 'id'\n }\n if (segments[1] === 'relationships') {\n return remapRelationship(descriptor[type]?.relations ?? {}, segments.slice(2))\n }\n\n return pointer\n}\n\n/** `[releaseInfo, label]` -> `releaseInfo.label`; a bare `[title]` -> `title`. */\nfunction remapAttribute(rest: readonly string[]): string {\n return rest.join('.')\n}\n\n/**\n * Remap a relationship pointer's tail (everything after `relationships/`, or — for a\n * relationship-endpoint document — the relation name spliced ahead of the `data` tail). The\n * leading segment is the relation name; a `data` segment is dropped; a numeric index becomes\n * `[n]`; a `meta/pivot/<field>` tail on a pivot-bearing relation becomes `.$pivot.<field>`. The\n * `relations` map tells us whether the relation carries pivot (so a member's `meta.pivot`\n * fields are the pivot data the bundle validates).\n */\nfunction remapRelationship(\n relations: Readonly<Record<string, { pivot?: boolean }>>,\n rest: readonly string[],\n): string {\n const name = rest[0]\n if (name === undefined) {\n return 'data/relationships'\n }\n const hasPivot = relations[name]?.pivot === true\n\n let path = name\n for (let i = 1; i < rest.length; i++) {\n const seg = rest[i]!\n if (seg === 'data') {\n continue\n }\n if (/^\\d+$/.test(seg)) {\n path += `[${seg}]`\n } else if (seg === 'meta' && hasPivot && rest[i + 1] === 'pivot') {\n // Pivot values nest under `meta.pivot` (bundle ADR 0103) — collapse the\n // `meta/pivot` pair to `$pivot`; the offending field follows.\n path += '.$pivot'\n i++\n } else {\n path += `.${seg}`\n }\n }\n\n return path\n}\n\n/**\n * Invert a relationship-MUTATION error's `source.pointer` to the caller's flat path, for the\n * `/{type}/{id}/relationships/{rel}` endpoints. Two pointer shapes reach here:\n *\n * - the LINKAGE document shape (validation on the linkage members), rooted at `data` — the\n * relation name is supplied by the route, not the wire:\n * - `/data` -> `<rel>` (to-one clear/set)\n * - `/data/meta/pivot/position` -> `<rel>.$pivot.position` (to-one pivot, rare)\n * - `/data/0/meta/pivot/position` -> `<rel>[0].$pivot.position`\n * - the RESOURCE-document shape that core's relationship prohibitions emit unchanged\n * (`FullReplacementProhibited`/`RemovalProhibited`/`AdditionProhibited`/\n * `RelationshipTypeInappropriate` all point at `/data/relationships/<name>`) — short-circuited\n * to the relation name (drop the `relationships/<name>` prefix) rather than treating\n * `relationships` as a linkage sub-segment.\n *\n * A non-`data` pointer is returned verbatim.\n */\nexport function remapRelationshipPointer(rel: string, hasPivot: boolean, pointer: string): string {\n const segments = pointer.split('/').filter((s) => s !== '')\n if (segments[0] !== 'data') {\n return pointer\n }\n // A resource-document relationship pointer (`/data/relationships/<name>...`): the route's\n // relation name owns the path; drop the `relationships/<name>` prefix (segments[2] is the\n // wire name, == the route `rel`) and remap the remaining tail under the route relation.\n const tail = segments[1] === 'relationships' ? segments.slice(3) : segments.slice(1)\n return remapRelationship({ [rel]: { pivot: hasPivot } }, [rel, ...tail])\n}\n\n/**\n * Return a {@link JsonApiError} with each error object's `path` populated by remapping its\n * `source.pointer` (descriptor-aware) — so {@link JsonApiError.byPath} groups by the flat input\n * shape. Errors with no write pointer (e.g. a `source.parameter` query-side error, or no\n * source) are passed through unchanged. The original error is not mutated.\n */\nexport function withRemappedPaths(\n error: JsonApiError,\n descriptor: ApiDescriptor,\n type: string,\n): JsonApiError {\n return mapErrors(error, (pointer) => ({ path: remapPointer(descriptor, type, pointer) }))\n}\n\n/**\n * The relationship-mutation twin of {@link withRemappedPaths}: remap each error's pointer\n * keyed by the relation name from the route. Handles both the linkage-document shape (rooted\n * at `data`) and the resource-document shape core's prohibition exceptions emit\n * (`/data/relationships/<rel>`). `hasPivot` lets a member-level `meta` field surface as\n * `$pivot`. The original error is not mutated.\n */\nexport function withRemappedRelationshipPaths(\n error: JsonApiError,\n rel: string,\n hasPivot: boolean,\n): JsonApiError {\n return mapErrors(error, (pointer) => ({ path: remapRelationshipPointer(rel, hasPivot, pointer) }))\n}\n\n/**\n * Invert an atomic-batch error's `source.pointer` to `(opIndex, flatPath)`: an atomic 422\n * points at `/atomic:operations/{n}/data/...`, so strip the `atomic:operations/{n}` prefix to\n * the op index, resolve that op's wire type (`typeForOp`), and delegate the remaining `/data`\n * tail to the same {@link remapPointer} a standalone write uses (so the flat path is identical\n * — `title`, `releaseInfo.label`, `artist`, …). A pointer without the atomic prefix is remapped\n * verbatim under the (whole-batch) document at op index `undefined`. The original error is not\n * mutated.\n */\nexport function withRemappedAtomicPaths(\n error: JsonApiError,\n descriptor: ApiDescriptor,\n typeForOp: (opIndex: number) => string | undefined,\n): JsonApiError {\n return mapErrors(error, (pointer) => {\n const segments = pointer.split('/').filter((s) => s !== '')\n if (segments[0] !== 'atomic:operations' || segments[1] === undefined) {\n return { path: pointer }\n }\n const opIndex = Number(segments[1])\n if (!Number.isInteger(opIndex)) {\n return { path: pointer }\n }\n const type = typeForOp(opIndex)\n const tail = `/${segments.slice(2).join('/')}`\n const path = type === undefined ? tail : remapPointer(descriptor, type, tail)\n return { opIndex, path }\n })\n}\n\n/** What a per-error remap resolves to: the flat `path` and, for an atomic batch, the `opIndex`. */\ninterface RemapResult {\n path: string\n opIndex?: number\n}\n\n/** Shared error-mapper: clone each error with `path`/`opIndex` from its `source.pointer`, else pass through. */\nfunction mapErrors(error: JsonApiError, remap: (pointer: string) => RemapResult): JsonApiError {\n const remapped: JsonApiErrorObject[] = error.errors.map((e) => {\n const pointer = e.source?.pointer\n if (pointer === undefined) {\n return e\n }\n const { path, opIndex } = remap(pointer)\n return opIndex === undefined ? { ...e, path } : { ...e, path, opIndex }\n })\n return new JsonApiError(error.status, remapped, error.message)\n}\n","/**\n * The Atomic Operations transaction builder (CONTEXT.md \"Atomic — typed transaction builder,\n * type-in-object\"; ADR 0001). `client.atomic(tx => { … })` records a batch of operations and\n * posts them all-or-nothing to the server's `/operations` endpoint (the atomic ext media type),\n * returning the positional, materialised results.\n *\n * The `tx` recorder is NOT type-scoped (one builder spans every type), so each op carries its\n * own `type` discriminant:\n *\n * - `tx.create({ type, …flatFields })` -> an `add` op; returns a handle that DOUBLES AS a\n * `lid`-bearing relationship ref (`{ type, lid }`) so a just-created resource wires into a\n * later op without a server-assigned id;\n * - `tx.update({ type, id, …flatFields })` -> an `update` op (a `ref` to the existing resource);\n * - `tx.delete({ type, id })` -> a `remove` op.\n *\n * Each create gets a deterministic `lid` from its op index (`atomic-<index>`), so the cross-op\n * reference is stable and human-readable. The flat->envelope serialisation reuses the same\n * {@link toDocument} the standalone writes use, so an atomic create body is identical to a\n * `POST /{type}` body (minus the swap of a server `id` for the generated `lid`).\n *\n * Content negotiation: the batch opts into the atomic extension via the request's `ext`\n * parameter ({@link ATOMIC_EXT}), so {@link execute} composes the atomic-ext media type\n * (`application/vnd.api+json; ext=\"…\"`) for both `Content-Type` and `Accept` — the generalised\n * media-type path the rest of the runtime shares.\n */\nimport type { ApiDescriptor } from './descriptor'\nimport { JsonApiError } from './errors'\nimport { materialise, type MaterialiseContext } from './materialise'\nimport { execute, type JsonApiContext, type JsonApiRequest } from './request'\nimport type { AtomicResultOf, CreateInput, TypeName, UpdateInput } from './result-types'\nimport { toDocument, withRemappedAtomicPaths } from './serialize-write'\nimport type { LocalIdentifier, ResourceIdentifier } from './types'\nimport { ATOMIC_EXT } from './types'\n\n/**\n * A reference to a resource for an `update`/`remove` op: a bare identifier (or a materialised\n * resource — its `{type,id}` extracted), OR a `{ type, lid }` local identifier targeting a\n * resource CREATED EARLIER IN THE SAME BATCH (e.g. a `tx.create` handle), so an op can update\n * or remove a just-created resource that has no server id yet (per the JSON:API atomic ext,\n * `ref` may carry `lid` instead of `id`).\n */\nexport type AtomicRef =\n | ResourceIdentifier\n | { type: string; id: string }\n | LocalIdentifier\n | { type: string; lid: string }\n\n/**\n * Flat create input for type `T`: the `type` discriminant plus the type's flat create\n * attributes/relation slots (reusing {@link CreateInput}; relation slots may reference a prior\n * `tx.create` handle). `D`/`W` thread the descriptor + write-attribute map for precise fields;\n * the loose defaults keep a codegen-less caller compiling.\n */\nexport type AtomicCreateInput<D extends ApiDescriptor, W, T extends TypeName<D>> = {\n type: T\n} & CreateInput<D, W, T>\n\n/**\n * Flat update input for type `T`: the `type` discriminant + the target's identity (`id`, OR a\n * `lid` for a resource created earlier in the same batch) plus the type's flat update\n * attributes/relation slots (reusing {@link UpdateInput}).\n */\nexport type AtomicUpdateInput<D extends ApiDescriptor, W, T extends TypeName<D>> =\n | ({ type: T; id: string; lid?: never } & UpdateInput<D, W, T>)\n | ({ type: T; lid: string; id?: never } & UpdateInput<D, W, T>)\n\n/**\n * The handle a {@link AtomicRecorder.create} returns. It is a `lid`-bearing relationship ref\n * (`{ type, lid }`) — pass it (or spread it) into a later op's relation slot to wire the\n * just-created resource without a server id. It also carries its op `kind` (`'create'`) and its\n * `opIndex` (its position in the batch, the created resource's positional result) so the typed\n * result tuple can map it back to the right positional result.\n */\nexport interface AtomicCreateHandle<T extends string = string> extends LocalIdentifier {\n readonly type: T\n readonly kind: 'create'\n readonly opIndex: number\n}\n\n/**\n * The handle a {@link AtomicRecorder.update} returns: its type discriminant `T`, its op `kind`\n * (`'update'`) and its `opIndex`. Returned (alongside the create handle) from the callback so its\n * positional result is typed as the materialised resource of `T`.\n */\nexport interface AtomicUpdateHandle<T extends string = string> {\n readonly type: T\n readonly kind: 'update'\n readonly opIndex: number\n}\n\n/**\n * The handle a {@link AtomicRecorder.delete} returns: its op `kind` (`'delete'`) and its\n * `opIndex`. A delete carries no data, so its positional result is `undefined`.\n */\nexport interface AtomicDeleteHandle {\n readonly kind: 'delete'\n readonly opIndex: number\n}\n\n/**\n * Any handle a recorder method returns — the element type of the callback's result tuple, used\n * as the inference constraint for the typed-tuple `atomic` form. The discriminant is left open\n * (`string`, NOT `TypeName<D>`) DELIBERATELY, so the type is descriptor-agnostic: a tighter\n * `TypeName<D>` constraint would flow the full type union into each element's contextual type\n * during array-literal inference, widening a `tx.create({ type: 'albums' })` handle's `T` back to\n * the whole union — losing the per-op narrowing. The recorder methods already guarantee each `T`\n * is a real `TypeName<D>`, so the open constraint is sound; `AtomicResultOf` re-infers the precise\n * `T` per element (guarded by `TypeName<D>` there).\n */\nexport type AtomicHandle =\n | AtomicCreateHandle<string>\n | AtomicUpdateHandle<string>\n | AtomicDeleteHandle\n\n/** A single recorded atomic operation, in wire shape (`op` + `ref`/`data`). */\ninterface AtomicOperation {\n op: 'add' | 'update' | 'remove'\n ref?: { type: string; id?: string; lid?: string }\n data?: unknown\n}\n\n/** The identity of an op's target resource: a server `id` or a same-batch `lid`. */\ntype AtomicIdentity = { id: string } | { lid: string }\n\n/**\n * The recorder handed to the `client.atomic` callback. Each method appends an op (preserving\n * order) and returns a handle carrying the op's discriminant `T`, `kind`, and `opIndex`. The\n * methods are generic over `T` (inferred from the input's `type`), so a create/update is typed\n * by the type's write-input ({@link CreateInput}/{@link UpdateInput}) and its handle resolves to\n * the materialised resource of `T`. `create` still returns a usable `{ type, lid }` ref so a\n * just-created resource wires into a later op without a server id.\n */\nexport interface AtomicRecorder<D extends ApiDescriptor, W> {\n create<T extends TypeName<D>>(input: AtomicCreateInput<D, W, T>): AtomicCreateHandle<T>\n update<T extends TypeName<D>>(input: AtomicUpdateInput<D, W, T>): AtomicUpdateHandle<T>\n delete(ref: AtomicRef): AtomicDeleteHandle\n}\n\n/** One positional result of an atomic batch: the materialised `data` (a resource/`null`) plus any op `meta`. */\nexport interface AtomicResult<Data = unknown> {\n data: Data\n meta?: Record<string, unknown>\n}\n\nconst isObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v)\n\n/** The deterministic local id for the create at `opIndex` (stable + human-readable). */\nconst lidFor = (opIndex: number): string => `atomic-${opIndex}`\n\n/**\n * The typed result tuple for a callback that returns a tuple of handles `Ops`: each returned\n * handle maps (by {@link AtomicResultOf}) to its materialised positional result — a create/update\n * handle to the `AtomicResult` of its type, a delete handle to `undefined`. The mapping is\n * positional in the RETURNED order (sound regardless of subset/reorder, since the runtime resolves\n * each handle by its `opIndex`).\n */\nexport type AtomicResults<D extends ApiDescriptor, A, Ops extends readonly AtomicHandle[]> = {\n -readonly [K in keyof Ops]: AtomicResultOf<D, A, Ops[K]>\n}\n\n/**\n * Run an atomic transaction. Invokes `build` with a recorder, collecting the operations (always\n * via the recorder's side effect, preserving op order), then posts `{ \"atomic:operations\": [...] }`\n * to the atomic endpoint with the ext media type as both `Content-Type` and `Accept`. The server's\n * `{ \"atomic:results\": [...] }` is parsed positionally — each result's `data` materialised the same\n * as a read.\n *\n * The return shape depends on what `build` returns:\n *\n * - returns a tuple of handles -> a per-op POSITIONALLY-TYPED tuple: each returned handle is\n * resolved to its result BY ITS `opIndex` (`results[handle.opIndex]`), so the tuple is sound\n * regardless of the order/subset the callback returns the handles in (a delete handle resolves\n * to `undefined`);\n * - returns void/nothing -> the loose, ordered `AtomicResult[]` (backward-compatible).\n *\n * A thrown {@link JsonApiError} has each error's pointer remapped to `(opIndex, flatPath)` (the\n * failing op's type drives the flat-path inversion) before it propagates.\n */\nexport function runAtomic<D extends ApiDescriptor, A, W, const Ops>(\n request: JsonApiContext,\n materialiseCtx: MaterialiseContext,\n descriptor: D,\n path: string,\n build: (tx: AtomicRecorder<D, W>) => Ops,\n): Promise<Ops extends readonly AtomicHandle[] ? AtomicResults<D, A, Ops> : AtomicResult[]>\nexport async function runAtomic(\n request: JsonApiContext,\n materialiseCtx: MaterialiseContext,\n descriptor: ApiDescriptor,\n path: string,\n build: (tx: AtomicRecorder<ApiDescriptor, unknown>) => readonly AtomicHandle[] | void,\n): Promise<AtomicResult[] | readonly (AtomicResult | undefined)[]> {\n const operations: AtomicOperation[] = []\n /** The wire type of each op, by index — drives the error-pointer remap. */\n const typeByOp: string[] = []\n\n const recorder: AtomicRecorder<ApiDescriptor, unknown> = {\n create(input) {\n const opIndex = operations.length\n const lid = lidFor(opIndex)\n const data = atomicResourceData(descriptor, input, { lid })\n operations.push({ op: 'add', data })\n typeByOp.push(input.type)\n return { type: input.type, lid, kind: 'create', opIndex }\n },\n update(input) {\n const opIndex = operations.length\n const identity: AtomicIdentity =\n input.id !== undefined ? { id: input.id } : { lid: input.lid }\n const data = atomicResourceData(descriptor, input, identity)\n operations.push({ op: 'update', ref: { type: input.type, ...identity }, data })\n typeByOp.push(input.type)\n return { type: input.type, kind: 'update', opIndex }\n },\n delete(ref) {\n const opIndex = operations.length\n const { type, identity } = toRef(ref)\n operations.push({ op: 'remove', ref: { type, ...identity } })\n typeByOp.push(type)\n return { kind: 'delete', opIndex }\n },\n }\n\n // The callback always drives the recorder side effect (collecting ops in order); its RETURN\n // VALUE — when it's a tuple of handles — selects the typed positional result per handle.\n const returned = build(recorder)\n\n const req: JsonApiRequest = {\n method: 'POST',\n path,\n body: { 'atomic:operations': operations },\n // Opt into the atomic extension; `execute` composes the ext media type for both\n // `Content-Type` and `Accept` (the shared media-type-negotiation path).\n ext: [ATOMIC_EXT],\n }\n\n let doc\n try {\n doc = await execute(request, req)\n } catch (error) {\n if (error instanceof JsonApiError) {\n throw withRemappedAtomicPaths(error, descriptor, (opIndex) => typeByOp[opIndex])\n }\n throw error\n }\n\n const results = parseResults(doc as { ['atomic:results']?: unknown } | undefined, materialiseCtx)\n\n // Loose form: the callback returned nothing, or not an all-handles array -> the ordered results\n // array (backward-compatible). The all-handles guard keeps the runtime in step with the type\n // predicate (`Ops extends readonly AtomicHandle[]`): a stray non-handle element resolves to the\n // loose AtomicResult[] the type promises rather than a row of undefined.\n const isHandleTuple =\n Array.isArray(returned) &&\n returned.every((h) => h !== null && typeof h === 'object' && 'opIndex' in h)\n if (!isHandleTuple) {\n return results\n }\n // Typed-tuple form: resolve each RETURNED handle by its `opIndex` (a delete -> undefined), so\n // the tuple is sound regardless of the order/subset of handles returned. (A server returning no\n // `atomic:results` for an op yields undefined; the reference bundle always returns the created/\n // updated resource, so create/update results are typed present.)\n return returned.map((handle) => (handle.kind === 'delete' ? undefined : results[handle.opIndex]))\n}\n\n/**\n * Build the resource `data` for an `add`/`update` op from flat input via {@link toDocument}\n * (so attributes/relationships route identically to a standalone write), then carry the op's\n * identity onto the resource: a server `id` (a create's server-assigned id is omitted; an\n * update by id keeps it) or a same-batch `lid` (a create's generated lid, or an update/remove\n * targeting an earlier op's resource).\n */\nfunction atomicResourceData(\n descriptor: ApiDescriptor,\n input: { type: string; [field: string]: unknown },\n identity: AtomicIdentity,\n): Record<string, unknown> {\n // Strip the identity keys before routing — `lid` is not an attribute, and `id` (when present\n // as a flat field) is carried via the identity, not the body. The identity is applied below.\n const { id: _id, lid: _lid, ...fields } = input as Record<string, unknown>\n const { data } = toDocument(\n descriptor,\n input.type,\n fields,\n 'id' in identity ? { id: identity.id } : undefined,\n )\n const out: Record<string, unknown> = { ...data }\n if ('lid' in identity) {\n // A lid-targeted op rides a local id, never a server id.\n delete out['id']\n out['lid'] = identity.lid\n }\n return out\n}\n\n/**\n * Coerce a remove ref (an identifier, a materialised resource, or a same-batch `{type,lid}`\n * handle) to `{ type, identity }` — `identity` an `id` or a `lid`; throws if neither is present.\n */\nfunction toRef(ref: AtomicRef): { type: string; identity: AtomicIdentity } {\n if (!isObject(ref)) {\n throw new TypeError('atomic delete requires an identifier `{ type, id }` or `{ type, lid }`')\n }\n const { type, id, lid } = ref as { type?: unknown; id?: unknown; lid?: unknown }\n if (typeof type !== 'string') {\n throw new TypeError('atomic delete requires a `type`')\n }\n if (typeof id === 'string') {\n return { type, identity: { id } }\n }\n if (typeof lid === 'string') {\n return { type, identity: { lid } }\n }\n throw new TypeError('atomic delete requires an `id` or a `lid`')\n}\n\n/** Parse `{ \"atomic:results\": [...] }` positionally, materialising each result's `data`. An absent results array (an all-`204` batch) yields `[]`. */\nfunction parseResults(\n doc: { ['atomic:results']?: unknown } | undefined,\n ctx: MaterialiseContext,\n): AtomicResult[] {\n const results = doc?.['atomic:results']\n if (!Array.isArray(results)) {\n return []\n }\n return results.map((result) => {\n const entry = isObject(result) ? result : {}\n const data = entry['data'] === undefined ? undefined : materialise({ data: entry['data'] }, ctx)\n const out: AtomicResult = { data }\n if (isObject(entry['meta'])) {\n out.meta = entry['meta']\n }\n return out\n })\n}\n","/** A single outbound request, already serialised to strings. */\nexport interface TransportRequest {\n method: string\n url: string\n headers: Record<string, string>\n body?: string\n /**\n * Aborts the request when signalled. Optional: a transport that ignores it still works, the\n * caller just can't cancel an in-flight request through it.\n */\n signal?: AbortSignal\n}\n\n/** A single inbound response, body still a string for the runtime to parse. */\nexport interface TransportResponse {\n status: number\n headers: Record<string, string>\n body: string\n}\n\n/**\n * The transport seam: a tiny, `fetch`-shaped function (not a class), so any impl\n * (native `fetch`, undici, an axios adapter) drops in. Retries are deliberately out\n * of scope here — the transport (or the TanStack layer) owns them.\n */\nexport type JsonApiTransport = (req: TransportRequest) => Promise<TransportResponse>\n\n/** The default transport, backed by the global `fetch`. */\nexport const fetchTransport: JsonApiTransport = async (req) => {\n const init: RequestInit = { method: req.method, headers: req.headers }\n if (req.body !== undefined) {\n init.body = req.body\n }\n if (req.signal !== undefined) {\n init.signal = req.signal\n }\n\n const res = await fetch(req.url, init)\n\n const headers: Record<string, string> = {}\n res.headers.forEach((value, key) => {\n headers[key] = value\n })\n\n return { status: res.status, headers, body: await res.text() }\n}\n","import { type AtomicRecorder, runAtomic } from './atomic'\nimport type { ActionDescriptor, ActionScope, ApiDescriptor, AtomicDescriptor } from './descriptor'\nimport { JsonApiError } from './errors'\nimport { materialise, type MaterialiseContext, relatedPaginatorKind } from './materialise'\nimport { execute, type JsonApiContext, type JsonApiRequest, type ReadQuery } from './request'\nimport type {\n ActionsAccessor,\n Client,\n DefaultActionTypes,\n DefaultAttributes,\n DefaultWriteAttributes,\n RelationReadQuery,\n RelationshipAccessor,\n ResourceHandle,\n TypeAccessor,\n} from './result-types'\nimport {\n toDocument,\n toRelationshipDocument,\n withRemappedPaths,\n withRemappedRelationshipPaths,\n} from './serialize-write'\nimport { fetchTransport, type JsonApiTransport } from './transport'\nimport { resolveValidator, type ValidationOption } from './validate'\n\n/**\n * The `fetch` headers init type. `@types/node` declares the global `RequestInit` (used\n * by the transport) but not `HeadersInit`; we derive it from `RequestInit.headers` to\n * stay dep-free and consistent with the rest of the runtime's web-globals usage.\n */\nexport type HeadersInit = NonNullable<RequestInit['headers']>\n\n/** Options for {@link createClient}. */\nexport interface ClientOptions {\n /** Base URL the generated path templates are resolved against. */\n baseUrl: string\n /** Transport seam; defaults to the `fetch` adapter when omitted. */\n transport?: JsonApiTransport\n /** Per-request header provider (e.g. bearer auth); may be async. */\n headers?: () => HeadersInit | Promise<HeadersInit>\n /**\n * The server-level Atomic Operations capability (the codegen's `atomic` constant): `{ path }`\n * when the server exposes the atomic endpoint, else `null`/omitted. When present, the client\n * exposes `client.atomic(tx => …)`; otherwise calling it throws.\n */\n atomic?: AtomicDescriptor | null\n /**\n * The opt-in per-field validation seam (ADR 0004). Off by default — the client always runs\n * light structural guards (is this a JSON:API document? does each `data`/`included` member\n * carry `type`+`id`?) but otherwise trusts the wire. Supply either the schema-driven config\n * `{ schemas, validator }` — the codegen-emitted per-type `schemas` map plus a validation\n * engine adapter (each wire resource is validated against `schemas[type]`; a type with no\n * schema is skipped) — or a bare validator function that owns schema lookup itself. The\n * validation engine (e.g. ajv) is brought by the caller; the client never depends on one.\n */\n validate?: ValidationOption\n}\n\n/** The ambient runtime context every read shares: transport seam + descriptor + materialise glue. */\ninterface ClientContext {\n readonly request: JsonApiContext\n readonly materialise: MaterialiseContext\n readonly descriptor: ApiDescriptor\n}\n\n/** Substitute `{key}` placeholders in a path template (`/albums/{id}/{rel}` -> `/albums/1/tracks`). */\nconst fill = (template: string, vars: Record<string, string>): string =>\n template.replace(/\\{(\\w+)\\}/g, (_, key: string) => vars[key] ?? `{${key}}`)\n\n/**\n * Run an operation: resolve the path template from the type's descriptor, fill its\n * placeholders, execute, and materialise the resulting document. A no-body / 204 response\n * materialises to `undefined`; an operation the type does not declare (no path template) throws\n * a local `Error` before any request. `linkage` marks a relationship-endpoint read (primary\n * `data` is pure resource-identifier linkage) so an attribute-less, relation-less member is\n * still materialised as an identifier.\n */\nasync function run(\n ctx: ClientContext,\n type: string,\n operation: string,\n vars: Record<string, string>,\n query: ReadQuery | undefined,\n linkage = false,\n relationForCount?: string,\n): Promise<unknown> {\n const template = ctx.descriptor[type]?.paths[operation]\n if (template === undefined) {\n throw new Error(`No \"${operation}\" path declared for type \"${type}\"`)\n }\n const req: JsonApiRequest = { method: 'GET', path: fill(template, vars) }\n if (query !== undefined) {\n const { signal, ...params } = query\n req.query = params\n if (signal !== undefined) {\n req.signal = signal\n }\n negotiateProfiles(ctx, type, params, req, relationForCount)\n }\n const doc = await execute(ctx.request, req)\n // Pass the statically-known primary type for the top-level collection read so an EMPTY page\n // still reports the collection's real `$page.kind` instead of the sniffed-from-`data[0]` `none`\n // (D6). A related/relationship read instead threads the RELATION's paginator kind — which may\n // diverge from the related type's collection (D6a) and which an empty page can't sniff.\n const primaryType = operation === 'fetchMany' ? type : undefined\n const paginatorOverride =\n (operation === 'fetchRelated' || operation === 'fetchRelationship') && vars['rel'] !== undefined\n ? relatedPaginatorKind(ctx.descriptor[type]?.relations[vars['rel']], ctx.materialise)\n : undefined\n return doc === undefined\n ? undefined\n : materialise(doc, ctx.materialise, linkage, primaryType, paginatorOverride)\n}\n\n/**\n * Add any profiles a query's parameters require to the request's negotiated `profiles`. A\n * `withCount` read needs the type's Countable profile in `Accept` — else the server rejects it\n * (400) under strict query-param validation. The profile URI is read from the descriptor's\n * `countable` block (never hardcoded); absent when the type advertises no Countable profile.\n */\nfunction negotiateProfiles(\n ctx: ClientContext,\n type: string,\n query: ReadQuery,\n req: JsonApiRequest,\n relationForCount?: string,\n): void {\n if (query.withCount === undefined || query.withCount.length === 0) {\n return\n }\n // A related/relationship read negotiates the RELATION's Countable profile (its tokens differ\n // from the collection's); a top-level collection read uses the type's (D3).\n const profile =\n relationForCount !== undefined\n ? ctx.descriptor[type]?.relations[relationForCount]?.countable?.profile\n : ctx.descriptor[type]?.countable?.profile\n if (profile !== undefined) {\n req.profiles = [...(req.profiles ?? []), profile]\n }\n}\n\n/** A write opts object: an optional `include`/`fields` narrowing the materialised response. */\ninterface WriteOpts {\n include?: readonly string[]\n fields?: Record<string, readonly string[]>\n}\n\n/** Project a write's opts to the read-query families the response honours (`include`/`fields`). */\nfunction writeQuery(opts: WriteOpts | undefined): ReadQuery | undefined {\n if (opts === undefined) {\n return undefined\n }\n const query: ReadQuery = {}\n if (opts.include !== undefined) {\n query.include = [...opts.include]\n }\n if (opts.fields !== undefined) {\n const fields: Record<string, string[]> = {}\n for (const [k, v] of Object.entries(opts.fields)) {\n fields[k] = [...v]\n }\n query.fields = fields\n }\n return Object.keys(query).length > 0 ? query : undefined\n}\n\n/** A write's spec: how to address it, what to send, and how to shape the result + errors. */\ninterface WriteSpec {\n type: string\n operation: string\n vars: Record<string, string>\n method: string\n body: unknown\n query?: ReadQuery | undefined\n /** Materialise the primary `data` as identifier linkage (relationship endpoints) vs resources. */\n linkage?: boolean\n /** Remap a thrown error's pointers to flat input paths (resource-write vs relationship-write). */\n remap: (error: JsonApiError) => JsonApiError\n}\n\n/**\n * Drive a write: fill the path template, attach the body (and any `include`/`fields` query),\n * execute, and materialise the response (`undefined` for a `204`/no body — so create/update\n * return the resource and delete/`204` relationship mutations resolve `undefined`/`void`).\n * On a thrown {@link JsonApiError}, each error's flat `path` is populated (via `spec.remap`)\n * before it propagates, so a caller's `byPath()` keys are the flat input shape.\n */\nasync function runWrite(ctx: ClientContext, spec: WriteSpec): Promise<unknown> {\n const template = ctx.descriptor[spec.type]?.paths[spec.operation]\n if (template === undefined) {\n throw new Error(`No \"${spec.operation}\" path declared for type \"${spec.type}\"`)\n }\n const req: JsonApiRequest = {\n method: spec.method,\n path: fill(template, spec.vars),\n body: spec.body,\n }\n if (spec.query !== undefined) {\n req.query = spec.query\n }\n let doc\n try {\n doc = await execute(ctx.request, req)\n } catch (error) {\n if (error instanceof JsonApiError) {\n throw spec.remap(error)\n }\n throw error\n }\n return doc === undefined ? undefined : materialise(doc, ctx.materialise, spec.linkage ?? false)\n}\n\n/**\n * Invoke a custom action over its declared HTTP method (`action.method`, default `POST`) at its\n * (filled) path, with a body shaped by the action's `input` mode:\n *\n * - `none` — no body;\n * - `document` — when the action names an `inputType`, FLAT input (the resource's attributes /\n * relationships) is built into the JSON:API envelope like `create`, and a `422`'s\n * `source.pointer`s are remapped to the flat paths; a bespoke command document (no `inputType`)\n * is sent verbatim;\n * - `raw` — the caller's payload, sent with the action's declared media type (e.g.\n * `application/octet-stream`), falling back to a wildcard when the spec declared none.\n *\n * The response is shaped by the action's `output`: `document` materialises into the resource /\n * collection view, `meta` returns the document's top-level `meta`, `none` resolves `undefined` (a\n * `204` / no body).\n */\nasync function runAction(\n ctx: ClientContext,\n action: ActionDescriptor,\n vars: Record<string, string>,\n input: unknown,\n): Promise<unknown> {\n const req: JsonApiRequest = { method: action.method ?? 'POST', path: fill(action.path, vars) }\n let remap: ((error: JsonApiError) => JsonApiError) | undefined\n if (action.input === 'document') {\n if (action.inputType !== undefined) {\n // Flat input: build the envelope like create, and remap 422 pointers to the flat paths.\n const inputType = action.inputType\n req.body = toDocument(ctx.descriptor, inputType, (input ?? {}) as Record<string, unknown>)\n remap = (error) => withRemappedPaths(error, ctx.descriptor, inputType)\n } else {\n // A bespoke command document (no resolvable input type): pass the caller's envelope through.\n req.body = input\n }\n } else if (action.input === 'raw') {\n req.body = input\n // Send the action's declared media type (e.g. application/octet-stream); fall back to a\n // wildcard only when the spec declared none.\n req.contentType = action.contentType ?? '*/*'\n req.raw = true\n }\n let doc: Awaited<ReturnType<typeof execute>>\n try {\n doc = await execute(ctx.request, req)\n } catch (error) {\n if (remap !== undefined && error instanceof JsonApiError) {\n throw remap(error)\n }\n throw error\n }\n if (action.output === 'none' || doc === undefined) {\n return undefined\n }\n if (action.output === 'meta') {\n return doc.meta\n }\n return materialise(doc, ctx.materialise)\n}\n\n/**\n * Build the typed `.actions` accessor at one scope: a plain object exposing exactly the\n * type's actions declared at `scope` (collection on the type accessor, resource on a handle),\n * each a function dispatching to {@link runAction} with the action descriptor + the scope's\n * path vars (resource scope supplies `{id}`). Names not declared at this scope are absent —\n * `client.albums.actions.reissue` (resource-scoped) is undefined; reach it via `.id(id)`.\n */\nfunction actionsAccessor(\n ctx: ClientContext,\n type: string,\n scope: ActionScope,\n vars: Record<string, string>,\n): ActionsAccessor<ApiDescriptor, unknown, unknown, string, ActionScope> {\n const actions = ctx.descriptor[type]?.actions ?? {}\n const out: Record<string, (input?: unknown) => Promise<unknown>> = {}\n for (const [name, action] of Object.entries(actions)) {\n if (action.scope === scope) {\n out[name] = (input?: unknown) => runAction(ctx, action, vars, input)\n }\n }\n return out as ActionsAccessor<ApiDescriptor, unknown, unknown, string, ActionScope>\n}\n\n/**\n * Build the relationship accessor for `client.<type>.id(id).<rel>`: reads\n * (`.get()` linkage / `.related()` collection) plus mutations — to-many `.add` (POST) /\n * `.remove` (DELETE) / `.replace` (PATCH) and to-one `.set` (PATCH). Every mutation posts\n * a `{ data: <linkage> }` body to `fetchRelationship` and materialises the response as\n * linkage (or `undefined`/`void` for a `204`). The cardinality is read off the descriptor\n * so the linkage is coerced correctly; the static types gate which verb is callable.\n */\nfunction relationshipAccessor(\n ctx: ClientContext,\n type: string,\n id: string,\n rel: string,\n): RelationshipAccessor<ApiDescriptor, unknown, string, never> {\n const vars = { id, rel }\n const relation = ctx.descriptor[type]?.relations[rel]\n const cardinality = relation?.cardinality ?? 'many'\n const hasPivot = relation?.pivot === true\n const mutate = (method: string, refs: unknown): Promise<unknown> =>\n runWrite(ctx, {\n type,\n operation: 'fetchRelationship',\n vars,\n method,\n body: toRelationshipDocument(refs, cardinality),\n linkage: true,\n remap: (error) => withRemappedRelationshipPaths(error, rel, hasPivot),\n })\n\n // Whether the relation advertises a mutation verb: the descriptor's `mutations` flags are\n // authoritative when present (mirrors the type-level `AdvertisesVerb`), so a verb the\n // bundle forbids (`cannotReplace` -> no PATCH) is absent at runtime, not just untyped. A\n // relation with no `mutations` block falls back to cardinality alone.\n const advertises = (verb: 'add' | 'remove' | 'replace' | 'set'): boolean => {\n const mutations = relation?.mutations\n return mutations === undefined ? true : mutations[verb] === true\n }\n\n // A read is present only when the relation exposes that endpoint (the descriptor's\n // `relationship`/`related` flags, from the bundle's `withoutRelationshipEndpoint()` /\n // `withoutRelatedEndpoint()`). A suppressed read is omitted (a JS caller gets \"not a function\"\n // rather than a 404); the static types gate it to `never` for a typed caller (D24).\n const accessor: Record<string, unknown> = {}\n if (relation?.relationship !== false) {\n accessor['get'] = (query?: RelationReadQuery) =>\n run(ctx, type, 'fetchRelationship', vars, query as ReadQuery | undefined, true, rel)\n }\n if (relation?.related !== false) {\n accessor['related'] = (query?: RelationReadQuery) =>\n run(ctx, type, 'fetchRelated', vars, query as ReadQuery | undefined, false, rel)\n }\n if (cardinality === 'many') {\n if (advertises('add')) {\n accessor['add'] = (refs: unknown) => mutate('POST', refs)\n }\n if (advertises('remove')) {\n accessor['remove'] = (refs: unknown) => mutate('DELETE', refs)\n }\n if (advertises('replace')) {\n accessor['replace'] = (refs: unknown) => mutate('PATCH', refs)\n }\n } else if (advertises('set')) {\n accessor['set'] = (ref: unknown) => mutate('PATCH', ref)\n }\n\n return accessor as unknown as RelationshipAccessor<ApiDescriptor, unknown, string, never>\n}\n\n/**\n * The reserved members on a {@link ResourceHandle} — names the Proxy resolves itself, so a\n * relation of the same name is shadowed and must route through `.rel(name)`. The codegen\n * imports this to warn at build time (keeping the runtime + the collision detector in sync).\n * `then` is reserved so a handle is never mistaken for a thenable.\n */\nexport const HANDLE_RESERVED: ReadonlySet<string> = new Set([\n 'type',\n 'id',\n 'get',\n 'update',\n 'delete',\n 'rel',\n 'actions',\n 'then',\n])\n\n/** Build the id-scoped resource handle (a Proxy resolving relation accessors by name). */\nfunction resourceHandle(\n ctx: ClientContext,\n type: string,\n id: string,\n): ResourceHandle<ApiDescriptor, unknown, unknown, string> {\n const relations = ctx.descriptor[type]?.relations ?? {}\n const base = {\n type,\n id,\n get: (query?: ReadQuery) => run(ctx, type, 'fetchOne', { id }, query),\n update: (patch: Record<string, unknown>, opts?: WriteOpts) =>\n runWrite(ctx, {\n type,\n operation: 'update',\n vars: { id },\n method: 'PATCH',\n body: toDocument(ctx.descriptor, type, patch, { id }),\n query: writeQuery(opts),\n remap: (error) => withRemappedPaths(error, ctx.descriptor, type),\n }),\n delete: () =>\n runWrite(ctx, {\n type,\n operation: 'delete',\n vars: { id },\n method: 'DELETE',\n body: undefined,\n remap: (error) => withRemappedPaths(error, ctx.descriptor, type),\n }) as never,\n rel: (name: string) => relationshipAccessor(ctx, type, id, name),\n actions: actionsAccessor(ctx, type, 'resource', { id }),\n }\n\n return new Proxy(base, {\n get(target, prop, receiver) {\n if (typeof prop === 'string' && !HANDLE_RESERVED.has(prop) && prop in relations) {\n return relationshipAccessor(ctx, type, id, prop)\n }\n return Reflect.get(target, prop, receiver)\n },\n }) as unknown as ResourceHandle<ApiDescriptor, unknown, unknown, string>\n}\n\n/** Build the collection-scoped accessor for one wire type (`client.albums`). */\nfunction typeAccessor(\n ctx: ClientContext,\n type: string,\n): TypeAccessor<ApiDescriptor, unknown, unknown, string> {\n return {\n list: (query?: ReadQuery) => run(ctx, type, 'fetchMany', {}, query) as Promise<never>,\n get: (id: string, query?: ReadQuery) =>\n run(ctx, type, 'fetchOne', { id }, query) as Promise<never>,\n create: (input: Record<string, unknown>, opts?: WriteOpts) =>\n runWrite(ctx, {\n type,\n operation: 'create',\n vars: {},\n method: 'POST',\n body: toDocument(ctx.descriptor, type, input),\n query: writeQuery(opts),\n remap: (error) => withRemappedPaths(error, ctx.descriptor, type),\n }) as Promise<never>,\n id: (id: string) => resourceHandle(ctx, type, id),\n actions: actionsAccessor(ctx, type, 'collection', {}),\n } as unknown as TypeAccessor<ApiDescriptor, unknown, unknown, string>\n}\n\n/**\n * Build a descriptor-driven read client. Generic at runtime (a Proxy over the descriptor),\n * not per-type codegen: `client.<type>` yields a {@link TypeAccessor}, `.id(id)` a handle,\n * and a handle's relation accessors drive the relationship/related endpoints. Navigation\n * (`$next()`/`$prev()`, related links) is wired through `materialise`'s `navigate` seam,\n * which executes the (absolute) link and re-materialises.\n */\nexport function createClient<\n D extends ApiDescriptor,\n A = DefaultAttributes<D>,\n W = DefaultWriteAttributes<D>,\n Act = DefaultActionTypes,\n>(descriptor: D, options: ClientOptions): Client<D, A, W, Act> {\n const request: JsonApiContext = {\n baseUrl: options.baseUrl,\n transport: options.transport ?? fetchTransport,\n ...(options.headers !== undefined ? { headers: options.headers } : {}),\n }\n\n const validator = resolveValidator(options.validate)\n const ctx: ClientContext = {\n request,\n descriptor,\n materialise: {\n descriptor,\n navigate: async (url, linkage = false, paginator, signal) => {\n const doc = await execute(request, {\n method: 'GET',\n path: url,\n ...(signal !== undefined ? { signal } : {}),\n })\n return doc === undefined\n ? undefined\n : materialise(doc, ctx.materialise, linkage, undefined, paginator)\n },\n ...(validator !== undefined ? { validate: validator } : {}),\n },\n }\n\n const atomicDescriptor = options.atomic ?? null\n // `client.atomic(tx => …)`: post the recorded batch to the atomic endpoint. Built once and\n // reused; throws when the server declares no atomic capability (`atomic` was null/omitted).\n // Typed via the single conditional-return `Client['atomic']` signature (tuple-of-handles ->\n // positional results; void -> loose results); the runtime forwards the callback to `runAtomic`.\n const atomic = (build: (tx: AtomicRecorder<D, W>) => unknown): Promise<unknown> => {\n if (atomicDescriptor === null) {\n throw new Error('This API does not expose an Atomic Operations endpoint')\n }\n return runAtomic(\n request,\n ctx.materialise,\n descriptor,\n atomicDescriptor.path,\n build as (tx: AtomicRecorder<D, W>) => void,\n )\n }\n\n const accessors = new Map<string, TypeAccessor<ApiDescriptor, unknown, unknown, string>>()\n return new Proxy(Object.create(null) as object, {\n get(_target, prop) {\n if (prop === 'atomic') {\n return atomic\n }\n if (typeof prop !== 'string' || !(prop in descriptor)) {\n return undefined\n }\n let accessor = accessors.get(prop)\n if (accessor === undefined) {\n accessor = typeAccessor(ctx, prop)\n accessors.set(prop, accessor)\n }\n return accessor\n },\n has(_target, prop) {\n return prop === 'atomic' || (typeof prop === 'string' && prop in descriptor)\n },\n }) as Client<D, A, W, Act>\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["isObject","isObject","isObject"],"sources":["../src/errors.ts","../src/pagination.ts","../src/validate.ts","../src/materialise.ts","../src/types.ts","../src/request.ts","../src/serialize-write.ts","../src/atomic.ts","../src/transport.ts","../src/client.ts"],"sourcesContent":["/**\n * A single JSON:API error object (spec: error objects).\n *\n * `C` is the server's documented error-code union (a generated client's `ErrorCode`). `code` is\n * typed `C | (string & {})`: editors suggest the documented codes, yet any other string still\n * fits, because the catalogue is open and a server may send a code it never documented.\n */\nexport interface JsonApiErrorObject<C extends string = string> {\n id?: string\n status?: string\n code?: C | (string & {})\n title?: string\n detail?: string\n source?: { pointer?: string; parameter?: string; header?: string }\n meta?: Record<string, unknown>\n /**\n * The user-facing input path, remapped from `source.pointer` for write errors\n * (e.g. `/data/attributes/title` -> `title`). Populated by the runtime, which\n * built the envelope and therefore knows the inverse mapping. See CONTEXT.md.\n */\n path?: string\n /**\n * The zero-based index of the failing operation within an atomic batch, parsed from a\n * `source.pointer` carrying an `/atomic:operations/{n}` prefix. Populated only for atomic\n * errors; `path` then holds the flat path within that operation's input.\n */\n opIndex?: number\n}\n\n/**\n * Thrown by the always-on light structural guards (ADR 0004) when a parsed response is not a\n * JSON:API document, or a `data`/`included` member is not a resource object carrying `type`+`id`.\n * Distinct from {@link JsonApiError} (a non-2xx server response): this signals the wire shape the\n * runtime relies on was violated, not an application error the server reported. The opt-in\n * per-field validator throws its engine's own error type, never this one.\n */\nexport class StructuralGuardError extends Error {\n constructor(message: string) {\n super(message)\n this.name = 'StructuralGuardError'\n }\n}\n\n/**\n * Thrown for any non-2xx JSON:API response. Carries the parsed error document plus\n * expressive status matchers and pointer-grouping helpers.\n *\n * `C` types each error object's `code` (see {@link JsonApiErrorObject}). It is a compile-time\n * hint only: the runtime never checks a code against it.\n */\nexport class JsonApiError<C extends string = string> extends Error {\n readonly status: number\n readonly errors: JsonApiErrorObject<C>[]\n\n constructor(status: number, errors: JsonApiErrorObject<C>[], message?: string) {\n super(message ?? `JSON:API request failed with status ${status}`)\n this.name = 'JsonApiError'\n this.status = status\n this.errors = errors\n }\n\n hasStatus(status: number): boolean {\n return this.status === status\n }\n\n is4xx(): boolean {\n return this.status >= 400 && this.status < 500\n }\n\n is5xx(): boolean {\n return this.status >= 500 && this.status < 600\n }\n\n isBadRequest(): boolean {\n return this.status === 400\n }\n\n isUnauthorized(): boolean {\n return this.status === 401\n }\n\n isForbidden(): boolean {\n return this.status === 403\n }\n\n isNotFound(): boolean {\n return this.status === 404\n }\n\n isNotAcceptable(): boolean {\n return this.status === 406\n }\n\n isConflict(): boolean {\n return this.status === 409\n }\n\n isUnsupportedMediaType(): boolean {\n return this.status === 415\n }\n\n isUnprocessable(): boolean {\n return this.status === 422\n }\n\n /** Alias of {@link isUnprocessable} — the JSON:API validation status. */\n isValidationError(): boolean {\n return this.status === 422\n }\n\n isRateLimited(): boolean {\n return this.status === 429\n }\n\n /**\n * Group errors by user-facing input path (falls back to the raw pointer, then the\n * query parameter). The payoff for form/validation UX — 422 `source.pointer`s are\n * remapped to the flat shape the caller actually supplied.\n */\n byPath(): Record<string, JsonApiErrorObject<C>[]> {\n const out: Record<string, JsonApiErrorObject<C>[]> = {}\n for (const e of this.errors) {\n const key = e.path ?? e.source?.pointer ?? e.source?.parameter ?? '_'\n ;(out[key] ??= []).push(e)\n }\n return out\n }\n}\n\n/**\n * True when `err` is a {@link JsonApiError} and any of its error objects carries `code`. Takes\n * any string, so it also matches codes the server never documented. A generated client exports a\n * `hasErrorCode` bound to its server's `ErrorCode` union, which rejects a misspelt code at\n * compile time; reach for this one only to match an undocumented code.\n */\nexport function hasErrorCode(err: unknown, code: string): err is JsonApiError {\n return err instanceof JsonApiError && err.errors.some((e) => e.code === code)\n}\n\n/**\n * {@link hasErrorCode} restricted to one server's documented codes. A generated client binds it:\n * `export const hasErrorCode = hasErrorCodeRuntime as ErrorCodeGuard<ErrorCode>`. The cast is\n * needed because `JsonApiError<string>` doesn't assign to `JsonApiError<ErrorCode>`.\n */\nexport type ErrorCodeGuard<C extends string> = (err: unknown, code: C) => err is JsonApiError<C>\n","/**\n * Pagination: resolving which strategy answers a read (runtime) and typing a read's `page`\n * parameter and `$page` from the descriptor (compile time). Both follow the paginator the\n * server advertises: a bare kind (canonical wire keys), a strategy with renamed keys, or a\n * MultiPaginator menu the client picks from by `kind`.\n */\nimport type {\n MenuDescriptor,\n PaginatorDescriptor,\n PaginatorKind,\n PaginatorRole,\n PaginatorStrategy,\n StrategyDescriptor,\n} from './descriptor'\n\n/** The wire key for each role when the server keeps the default names. */\nexport const CANONICAL_PAGE_KEYS = {\n page: { number: 'number', size: 'size' },\n offset: { offset: 'offset', limit: 'limit' },\n cursor: { after: 'after', before: 'before', size: 'size' },\n fixed: { number: 'number' },\n none: {},\n} as const satisfies Record<PaginatorKind, Partial<Record<PaginatorRole, string>>>\n\n/** Role -> wire key for the strategy that answered. */\nexport type PageKeys = Readonly<Partial<Record<PaginatorRole, string>>>\n\n/** The strategy that answers one read, with its wire keys and, for a menu pick, the arm's `kind`. */\nexport interface ResolvedPaginator {\n kind: PaginatorKind\n keys: PageKeys\n /** The menu arm the request picked with `page[kind]`. Absent when it picked none (the default). */\n arm?: string\n}\n\n/**\n * Resolve the strategy that answers a read from the endpoint's paginator and the `page` the\n * request sends. A menu answers with the arm named by `page.kind`, else its `default`.\n */\nexport function resolvePaginator(\n paginator: PaginatorDescriptor | undefined,\n page?: Readonly<Record<string, unknown>>,\n): ResolvedPaginator {\n if (paginator === undefined) {\n return { kind: 'none', keys: {} }\n }\n if (typeof paginator === 'string') {\n return { kind: paginator, keys: CANONICAL_PAGE_KEYS[paginator] }\n }\n if (paginator.strategy !== 'multi') {\n return { kind: paginator.strategy, keys: paginator.keys }\n }\n const picked = page?.['kind']\n const arm =\n typeof picked === 'string' && Object.hasOwn(paginator.arms, picked) ? picked : undefined\n const resolved = resolvePaginator(paginator.arms[arm ?? paginator.default])\n return arm === undefined ? resolved : { ...resolved, arm }\n}\n\n/** Navigation links by relation (`first`/`prev`/`next`/`last`), when present. */\nexport interface PageLinks {\n first?: string\n prev?: string\n next?: string\n last?: string\n}\n\n/** Normalised pagination state on a collection, as the runtime builds it; count-free-safe. */\nexport interface Page {\n kind: PaginatorKind\n /** Role -> wire key for the strategy that answered. */\n keys: PageKeys\n /** The menu arm the request picked with `page[kind]`, when it picked one. */\n arm?: string\n /** The raw `meta.page` block, when present (shape varies by kind). */\n meta?: Record<string, unknown>\n links: PageLinks\n}\n\n/**\n * The `meta.page` block each built-in strategy renders. `total`/`lastPage` (and `to` on an empty\n * page) are absent when the server does not count.\n */\nexport type PageMeta<K extends PaginatorKind> = K extends 'page'\n ? {\n currentPage: number\n perPage: number\n from: number\n to?: number\n total?: number\n lastPage?: number\n }\n : K extends 'fixed'\n ? { currentPage: number; total?: number; lastPage?: number }\n : K extends 'offset'\n ? { offset: number; limit: number; from: number; to?: number; total?: number }\n : K extends 'cursor'\n ? { perPage: number; from?: string; to?: string; hasMore: boolean }\n : Record<string, unknown>\n\n/** `$page` typed by the strategy `K` that answers, its wire keys and the picked menu arm. */\nexport type TypedPage<\n K extends PaginatorKind,\n Keys extends PageKeys,\n Arm extends string | undefined = undefined,\n> = {\n kind: K\n keys: Keys\n meta?: PageMeta<K>\n links: PageLinks\n} & (Arm extends string ? { arm: Arm } : { arm?: never })\n\n/** A non-menu paginator entry as `{ strategy, keys }` (a bare kind takes the canonical keys). */\ntype StrategyOf<P> = P extends PaginatorKind\n ? { strategy: P; keys: (typeof CANONICAL_PAGE_KEYS)[P] }\n : P extends StrategyDescriptor\n ? P\n : never\n\n/** Cursors are opaque strings; every other role is a count. */\ntype RoleValue<R> = R extends 'after' | 'before' ? string : number\n\n/** The page members one strategy accepts, keyed by their wire names. */\ntype StrategyParams<S> = S extends { keys: infer K }\n ? { -readonly [R in keyof K as K[R] extends string ? K[R] : never]?: RoleValue<R> }\n : never\n\n/** True when `P` is the loose `PaginatorDescriptor` union rather than a generated literal. */\ntype IsLoose<P> = [PaginatorKind] extends [P] ? true : false\n\ntype ArmName<M extends MenuDescriptor> = Extract<keyof M['arms'], string>\n\ntype ArmParams<M extends MenuDescriptor, A> =\n A extends ArmName<M> ? { kind: A } & StrategyParams<StrategyOf<M['arms'][A]>> : never\n\ntype KeysOfUnion<U> = U extends unknown ? keyof U : never\n\n/**\n * The default arm's members without `kind`. Members only another arm reads are `never`: the\n * server would select that arm by them, while the response is typed as the default.\n */\ntype DefaultArmParams<M extends MenuDescriptor> = StrategyParams<\n StrategyOf<M['arms'][M['default']]>\n> & { kind?: undefined } & {\n [X in Exclude<\n KeysOfUnion<StrategyParams<StrategyOf<M['arms'][ArmName<M>]>>>,\n keyof StrategyParams<StrategyOf<M['arms'][M['default']]>>\n >]?: never\n}\n\n/**\n * The `page` parameter an endpoint with paginator `P` accepts: its strategy's members under\n * their wire keys (numbers, cursors as strings). A menu takes one arm tagged by `kind`, or the\n * default arm's members untagged. `never` for an unpaginated endpoint; a loose record when the\n * descriptor is not a generated literal.\n */\nexport type PageParams<P> =\n IsLoose<P> extends true\n ? Readonly<Record<string, unknown>>\n : P extends 'none'\n ? never\n : P extends MenuDescriptor\n ? ArmParams<P, ArmName<P>> | DefaultArmParams<P>\n : StrategyParams<StrategyOf<P>>\n\n/** The member of page parameter union `P` that argument `Q` selects by its `kind`, if any. */\ntype SelectedArm<Q, P> = Q extends { kind: infer K }\n ? Extract<P, { kind: K }>\n : Extract<P, { kind?: undefined }>\n\n/**\n * A page argument `Q` with any member outside the arm it selects turned into `never`, so a typo,\n * or another arm's member, is an error.\n */\nexport type ExactPageParams<Q, P> = Q & {\n [K in Exclude<\n keyof Q,\n KeysOfUnion<[SelectedArm<Q, P>] extends [never] ? P : SelectedArm<Q, P>>\n >]: never\n}\n\ntype StrategyPage<S, Arm extends string | undefined = undefined> = S extends {\n strategy: infer K extends PaginatorStrategy\n keys: infer Keys extends PageKeys\n}\n ? TypedPage<K, Keys, Arm>\n : Page\n\ntype MenuPage<M extends MenuDescriptor, Q> = [Q] extends [never]\n ? StrategyPage<StrategyOf<M['arms'][M['default']]>>\n : Q extends { kind: infer A extends ArmName<M> }\n ? StrategyPage<StrategyOf<M['arms'][A]>, A>\n : StrategyPage<StrategyOf<M['arms'][M['default']]>>\n\n/**\n * The `$page` a read on an endpoint with paginator `P` carries, given the `page` argument `Q`\n * it sent: the picked menu arm's strategy, the default's when it picked none, else the\n * endpoint's own strategy.\n */\nexport type PageOf<P, Q = undefined> =\n IsLoose<P> extends true\n ? Page\n : P extends MenuDescriptor\n ? MenuPage<P, Q>\n : P extends 'none'\n ? TypedPage<'none', Record<never, never>>\n : StrategyPage<StrategyOf<P>>\n\n/**\n * The `page` argument that reads another page from the strategy that answered `P` (a `$page`):\n * its members under their wire keys, tagged with the menu arm it picked. A loose record for an\n * untyped `Page`.\n */\nexport type PageParamsOf<P extends Page> = PaginatorKind extends P['kind']\n ? Readonly<Record<string, string | number>>\n : StrategyParams<{ keys: P['keys'] }> &\n (P extends { arm: infer A extends string } ? { kind: A } : unknown)\n","import { StructuralGuardError } from './errors'\nimport type { Document } from './request'\n\n/**\n * A wire resource as it appears in a document's `data`/`included` — a JSON:API resource\n * object (or bare identifier). The structural guards assert `type` (and, where required,\n * `id`); the opt-in validator sees the whole object to check the rest against its schema.\n */\nexport interface WireResource {\n type: string\n id?: string\n [key: string]: unknown\n}\n\n/**\n * The opt-in per-resource validator: validate one wire `resource` against its JSON `schema`\n * (the bundle's per-type resource-object schema). The engine is the user's — throw (or\n * aggregate-then-throw) on an invalid resource; return for a valid one. With the bare-function\n * seam form there is no per-type schema, so `schema` is `undefined` and the function owns the\n * whole decision.\n */\nexport type Validator = (resource: WireResource, schema: unknown) => void\n\n/** A per-type JSON Schema map (the codegen-emitted `schemas` constant), keyed by JSON:API type. */\nexport type SchemaMap = Readonly<Record<string, unknown>>\n\n/**\n * The schema-driven validation seam: a per-type `schemas` map plus a `validator` engine. Each\n * wire resource is validated against `schemas[resource.type]`; a type with no schema is skipped.\n */\nexport interface ValidationConfig {\n schemas: SchemaMap\n validator: Validator\n}\n\n/**\n * The `validate?` option on {@link ClientOptions}: either the schema-driven {@link ValidationConfig}\n * (the common case — the codegen-emitted `schemas` map + an engine adapter) or a bare\n * {@link Validator} that owns schema lookup itself (it is called per resource with `schema` =\n * `undefined`). Absent => no per-field validation (zero overhead, no engine dependency).\n */\nexport type ValidationOption = ValidationConfig | Validator\n\n/** A normalised validator: resolve a resource's schema (or `undefined`) and validate it. */\nexport interface ResolvedValidator {\n validate(resource: WireResource): void\n}\n\n/** Whether a `validate` option is the schema-driven config (vs a bare validator function). */\nfunction isConfig(option: ValidationOption): option is ValidationConfig {\n return typeof option === 'object' && option !== null && !Array.isArray(option)\n}\n\n/**\n * Normalise the `validate?` option to a single per-resource validator. The config form looks the\n * schema up by `resource.type` and skips a type with no schema (the seam stays graceful for a\n * partially-covered bundle); the bare-function form calls the validator with `schema = undefined`,\n * delegating the whole decision to the caller. Returns `undefined` when validation is off.\n */\nexport function resolveValidator(\n option: ValidationOption | undefined,\n): ResolvedValidator | undefined {\n if (option === undefined) {\n return undefined\n }\n if (isConfig(option)) {\n const { schemas, validator } = option\n return {\n validate(resource) {\n const schema = schemas[resource.type]\n // A type the bundle does not cover is skipped, not failed — partial coverage is allowed.\n if (schema !== undefined) {\n validator(resource, schema)\n }\n },\n }\n }\n return {\n validate(resource) {\n option(resource, undefined)\n },\n }\n}\n\nconst isObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v)\n\n/**\n * The light structural guards every parsed response passes through (ADR 0004): assert the body\n * is a JSON:API document and that each `data`/`included` member carries a string `type` (and,\n * for a full resource, an `id`). This is the always-on, engine-free floor — it never duplicates\n * the opt-in per-field validation; it only proves the envelope invariant the runtime relies on.\n *\n * A primary-`data` resource and every `included` resource must carry `type`+`id`. A relationship\n * endpoint's primary data is pure resource-identifier linkage, which also carries `type`+`id`, so\n * the same guard holds; `linkage` is accepted for symmetry with the materialiser but the assertion\n * is identical (an identifier is `type`+`id`). `data: null` (an empty to-one) and a missing\n * `data` (a meta-only document) are valid and pass.\n */\nexport function assertJsonApiDocument(body: unknown): asserts body is Document {\n if (!isObject(body)) {\n throw new StructuralGuardError('Response body is not a JSON:API document (expected an object)')\n }\n // A JSON:API document must carry at least one of `data`, `errors`, or `meta`. An error\n // document never reaches here (a non-2xx throws a JsonApiError upstream), so the guard\n // requires a `data` or `meta` top-level member.\n if (!('data' in body) && !('meta' in body)) {\n throw new StructuralGuardError(\n 'Response body is not a JSON:API document (missing top-level \"data\" or \"meta\")',\n )\n }\n const data = body['data']\n if (Array.isArray(data)) {\n data.forEach((member, i) => assertResource(member, `data[${i}]`))\n } else if (data !== null && data !== undefined) {\n assertResource(data, 'data')\n }\n const included = body['included']\n if (included !== undefined) {\n if (!Array.isArray(included)) {\n throw new StructuralGuardError('Response \"included\" is not an array')\n }\n included.forEach((member, i) => assertResource(member, `included[${i}]`))\n }\n}\n\n/** Assert a single member is a resource object/identifier carrying string `type` + `id`. */\nfunction assertResource(member: unknown, at: string): void {\n if (!isObject(member)) {\n throw new StructuralGuardError(`${at} is not a JSON:API resource object`)\n }\n if (typeof member['type'] !== 'string') {\n throw new StructuralGuardError(`${at} is missing a string \"type\"`)\n }\n if (typeof member['id'] !== 'string') {\n throw new StructuralGuardError(`${at} (type \"${member['type']}\") is missing a string \"id\"`)\n }\n}\n\n/**\n * Run the opt-in validator over every wire resource in a document's `data` + `included`, each\n * against its per-type schema. Called only when a validator is configured (off by default). The\n * structural guards have already proven each member carries `type`+`id`, so the cast is sound.\n */\nexport function validateDocument(body: Document, validator: ResolvedValidator): void {\n const data = body.data\n if (Array.isArray(data)) {\n for (const member of data) {\n validateResourceMember(member, validator)\n }\n } else if (data !== null && data !== undefined) {\n validateResourceMember(data, validator)\n }\n if (body.included !== undefined) {\n // `included` always holds full resource objects.\n for (const member of body.included) {\n validator.validate(member as WireResource)\n }\n }\n}\n\n/**\n * Validate a `data` member, but only when it is a full RESOURCE OBJECT. A relationship-linkage\n * endpoint's `data` is bare resource IDENTIFIERS (`type`+`id`[+`meta`], no `attributes`/\n * `relationships`); the per-type schema describes the resource object, so running it over an\n * identifier would spuriously fail. The structural guard already covered the identifier shape.\n */\nfunction validateResourceMember(member: unknown, validator: ResolvedValidator): void {\n if (isObject(member) && ('attributes' in member || 'relationships' in member)) {\n validator.validate(member as WireResource)\n }\n}\n","import type {\n ApiDescriptor,\n PaginatorDescriptor,\n PaginatorKind,\n RelationDescriptor,\n} from './descriptor'\nimport { type Page, type ResolvedPaginator, resolvePaginator } from './pagination'\nimport type { Document } from './request'\nimport { assertJsonApiDocument, type ResolvedValidator, validateDocument } from './validate'\n\n/** A trimmed, by-reference top-level envelope shared by every resource from one response. */\nexport interface DocumentEnvelope {\n jsonapi?: Record<string, unknown>\n meta?: Record<string, unknown>\n links?: Record<string, unknown>\n}\n\n/** Resource-level links (`self` is the common one; the wire may carry more). */\nexport interface ResourceLinks {\n self?: string\n [key: string]: unknown\n}\n\n/** The relationship-instance envelope for a materialised related value (its `$edge`). */\nexport interface Edge {\n links?: Record<string, unknown>\n meta?: Record<string, unknown>\n}\n\n/** The uniform shape `$rel(name)` returns. */\nexport interface RelationView {\n data?: unknown\n links?: Record<string, unknown>\n meta?: Record<string, unknown>\n}\n\n/** Non-enumerable envelope accessors shared by every materialised resource. */\nexport interface ResourceEnvelope {\n readonly $meta: Record<string, unknown> | undefined\n readonly $links: ResourceLinks | undefined\n readonly $self: string | undefined\n readonly $document: DocumentEnvelope\n readonly $raw: RawResource\n $rel(name: string): RelationView | undefined\n}\n\n/** Edge-local accessors carried by a materialised related VALUE (per-edge view). */\nexport interface EdgeEnvelope {\n readonly $edge: Edge | undefined\n readonly $pivot: Record<string, unknown> | undefined\n}\n\n/** Relationship-level accessors carried by an augmented to-many array. */\nexport interface ArrayEnvelope {\n readonly $page: Page\n readonly $links: Record<string, unknown> | undefined\n readonly $meta: Record<string, unknown> | undefined\n $next(options?: NavigateOptions): Promise<unknown>\n $prev(options?: NavigateOptions): Promise<unknown>\n}\n\n/** Per-call options for `$next()`/`$prev()`. */\nexport interface NavigateOptions {\n /** Cancels the page fetch when signalled. */\n signal?: AbortSignal\n}\n\n/** The runtime context materialise reads its descriptor + navigation seam from. */\nexport interface MaterialiseContext {\n descriptor: ApiDescriptor\n /**\n * Re-fetch + re-materialise a link (wired in Build 4); returns the materialised value.\n * `linkage` propagates the originating surface so a paginated relationship-endpoint\n * array re-materialises as identifiers (not resources) on `$next()`/`$prev()`. `paginator`\n * carries the strategy that answered the current page, so the re-materialised page reports the\n * same `$page` (a divergent per-relation paginator, a picked menu arm, or an EMPTY next page\n * that carries no member to sniff).\n */\n navigate: (\n url: string,\n linkage?: boolean,\n paginator?: ResolvedPaginator,\n signal?: AbortSignal,\n ) => Promise<unknown>\n /**\n * The opt-in per-resource validator (ADR 0004), resolved from `ClientOptions.validate`. When\n * present, every wire resource in `data`/`included` is validated against its per-type schema\n * before the document is materialised. Absent => no per-field validation (the default).\n */\n validate?: ResolvedValidator | undefined\n}\n\n/** A raw JSON:API resource object (or bare identifier) off the wire. */\ninterface RawResource {\n type: string\n id: string\n meta?: Record<string, unknown>\n links?: ResourceLinks\n attributes?: Record<string, unknown>\n relationships?: Record<string, RawRelationship>\n}\n\ninterface RawRelationship {\n data?: unknown\n links?: Record<string, unknown>\n meta?: Record<string, unknown>\n}\n\nconst isObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v)\n\nconst define = (target: object, key: string, value: unknown): void => {\n Object.defineProperty(target, key, { value, enumerable: false, configurable: true })\n}\n\nconst defineGetter = (target: object, key: string, get: () => unknown): void => {\n Object.defineProperty(target, key, { get, enumerable: false, configurable: true })\n}\n\n/**\n * Materialise a JSON:API document into a hydrated graph (ADR 0002 / CONTEXT.md):\n *\n * - single resource -> a flattened resource object (own enumerable type/id/attributes/\n * relations + non-enumerable `$`-accessors);\n * - collection / related collection -> an augmented array of resource objects;\n * - relationship linkage -> an augmented array of identifier views (to-many) or a single\n * identifier / null (to-one).\n *\n * Included resources hydrate into nested per-edge views (reads through to the shared\n * node for attributes, owns its own `$edge`/`$pivot`); linked-but-not-included relations\n * stay as identifiers; links-only relations are `undefined`. The envelope rides\n * non-enumerable `$`-accessors so `{...res}` / `JSON.stringify(res)` are clean.\n *\n * `linkage` discriminates a relationship-endpoint response (primary `data` is pure\n * resource-identifier linkage) from a resource/collection response (primary `data` is a\n * full resource) — a structural guess can't tell them apart for an attribute-less,\n * relation-less resource, so the caller passes the surface it issued.\n *\n * `paginatorOverride` sets the top-level array's `$page` for a related/relationship read, where the\n * paginator is the RELATION's (which may diverge from the related type's collection — D6a), for a\n * menu read that picked an arm, and where an empty page carries no member to sniff a type from. A\n * bare kind is read with its canonical wire keys. `primaryType` is preferred for a\n * top-level collection read (statically-known); both override the sniff-from-`data[0]` fallback.\n */\nexport function materialise(\n doc: Document,\n ctx: MaterialiseContext,\n linkage = false,\n primaryType?: string,\n paginatorOverride?: ResolvedPaginator | PaginatorKind,\n): unknown {\n // Always-on light structural guards: prove the envelope invariant the runtime relies on (a\n // JSON:API document; each data/included member carries type+id). Then, only when configured,\n // run the opt-in per-field validation over each wire resource by its type (ADR 0004).\n assertJsonApiDocument(doc)\n if (ctx.validate !== undefined) {\n validateDocument(doc, ctx.validate)\n }\n\n const env: DocumentEnvelope = {}\n if (doc.jsonapi !== undefined) env.jsonapi = doc.jsonapi\n if (doc.meta !== undefined) env.meta = doc.meta\n if (doc.links !== undefined) env.links = doc.links\n\n const index = buildIndex(doc.included)\n // One node per type:id, shared by reference for attribute reads.\n const nodes = new Map<string, object>()\n const build = (raw: RawResource): object => buildNode(raw, env, index, nodes, ctx)\n\n const data = doc.data\n\n if (data === null) {\n return null\n }\n\n if (Array.isArray(data)) {\n const raws = data as RawResource[]\n const members = linkage\n ? raws.map((raw) => buildIdentifierView(raw))\n : raws.map((raw) => buildEdgeView(build(raw), raw))\n return augmentArray(members, doc, ctx, linkage, primaryType, paginatorOverride)\n }\n\n if (isObject(data)) {\n const raw = data as unknown as RawResource\n if (linkage) {\n return buildIdentifierView(raw)\n }\n return build(raw)\n }\n\n // No primary data (e.g. a meta-only document); expose nothing materialisable.\n return undefined\n}\n\n/** Index `included` by `type:id` so relationship linkage can be hydrated. */\nfunction buildIndex(included: unknown[] | undefined): Map<string, RawResource> {\n const index = new Map<string, RawResource>()\n if (!included) return index\n for (const raw of included) {\n if (isObject(raw) && typeof raw['type'] === 'string' && typeof raw['id'] === 'string') {\n index.set(key(raw['type'], raw['id']), raw as unknown as RawResource)\n }\n }\n return index\n}\n\nconst key = (type: string, id: string): string => `${type}:${id}`\n\n/**\n * Build (or reuse) the shared NODE for a resource: flattened type/id/attributes + hydrated\n * relations as own enumerable props, plus the resource-level `$`-accessors. The node is\n * memoised by `type:id` so attribute reads share one object; per-edge data never lands here.\n */\nfunction buildNode(\n raw: RawResource,\n env: DocumentEnvelope,\n index: Map<string, RawResource>,\n nodes: Map<string, object>,\n ctx: MaterialiseContext,\n): object {\n const id = key(raw.type, raw.id)\n const existing = nodes.get(id)\n if (existing) return existing\n\n const node: Record<string, unknown> = { type: raw.type, id: raw.id }\n // Seed the cache before recursing so a relation cycle resolves to this same node.\n nodes.set(id, node)\n\n if (raw.attributes) {\n for (const [k, v] of Object.entries(raw.attributes)) {\n node[k] = v\n }\n }\n\n const rels = raw.relationships ?? {}\n const resType = ctx.descriptor[raw.type]\n for (const [name, rel] of Object.entries(rels)) {\n if (!('data' in rel)) {\n // Links-only relation: no value to carry an edge -> undefined (reachable via $rel).\n continue\n }\n const relDesc = resType?.relations[name]\n node[name] = hydrateRelation(\n rel,\n relDesc?.cardinality,\n resolvePaginator(relatedPaginator(relDesc, ctx)),\n env,\n index,\n nodes,\n ctx,\n )\n }\n\n attachResourceEnvelope(node, raw, env, rels)\n return node\n}\n\n/**\n * Resolve one relationship's value: an included member hydrates to a per-edge view, a\n * linked-but-not-included member stays an identifier, an empty to-one is `null`, and a\n * to-many is always an augmented array (of views and/or identifiers).\n *\n * `related` is the relation's paginator (from the descriptor), threaded so a to-many array\n * reports the same `$page` it would on its dedicated endpoint without a `page` argument.\n */\nfunction hydrateRelation(\n rel: RawRelationship,\n cardinality: 'one' | 'many' | undefined,\n related: ResolvedPaginator,\n env: DocumentEnvelope,\n index: Map<string, RawResource>,\n nodes: Map<string, object>,\n ctx: MaterialiseContext,\n): unknown {\n const data = rel.data\n\n if (Array.isArray(data)) {\n const members = (data as RawResource[]).map((member) =>\n resolveMember(member, env, index, nodes, ctx),\n )\n return augmentRelationArray(members, rel, related, ctx)\n }\n\n // A declared to-many with no array yet (count-free / lazy) still reads as an array.\n if (data === undefined && cardinality === 'many') {\n return augmentRelationArray([], rel, related, ctx)\n }\n\n if (data === null || data === undefined) {\n return data === null ? null : undefined\n }\n\n if (isObject(data)) {\n // A to-one's edge envelope carries the relationship object's self/related links/meta\n // (CONTEXT.md: $edge = { links:{self,related}, meta }) so there's no need to go via\n // the parent's $rel for to-ones.\n return resolveMember(data as unknown as RawResource, env, index, nodes, ctx, {\n links: rel.links,\n meta: rel.meta,\n })\n }\n\n return undefined\n}\n\n/** The relationship-object envelope threaded onto a to-one member's `$edge`. */\ninterface RelationEdge {\n links: Record<string, unknown> | undefined\n meta: Record<string, unknown> | undefined\n}\n\n/** A single linkage member -> a hydrated per-edge view if included, else an identifier view. */\nfunction resolveMember(\n member: RawResource,\n env: DocumentEnvelope,\n index: Map<string, RawResource>,\n nodes: Map<string, object>,\n ctx: MaterialiseContext,\n relationEdge?: RelationEdge,\n): object {\n const included = index.get(key(member.type, member.id))\n if (included) {\n return buildEdgeView(buildNode(included, env, index, nodes, ctx), member, relationEdge)\n }\n return buildIdentifierView(member, relationEdge)\n}\n\n/** Attach the resource-level `$`-accessors ($meta/$links/$self/$document/$raw/$rel). */\nfunction attachResourceEnvelope(\n node: object,\n raw: RawResource,\n env: DocumentEnvelope,\n rels: Record<string, RawRelationship>,\n): void {\n defineGetter(node, '$meta', () => raw.meta)\n defineGetter(node, '$links', () => raw.links)\n defineGetter(node, '$self', () => raw.links?.self)\n defineGetter(node, '$document', () => env)\n defineGetter(node, '$raw', () => raw)\n define(node, '$rel', (name: string): RelationView | undefined => {\n const rel = rels[name]\n if (!rel) return undefined\n const view: RelationView = {}\n if ('data' in rel) view.data = rel.data\n if (rel.links !== undefined) view.links = rel.links\n if (rel.meta !== undefined) view.meta = rel.meta\n return view\n })\n}\n\n/**\n * A per-edge VIEW over a node: a distinct wrapper per membership that reads through to the\n * shared node for attributes (and the resource-level `$`-accessors) but owns its own\n * edge-local `$edge`/`$pivot`. Identity is by `type:id`, never reference.\n *\n * `relationEdge` (set for a to-one) carries the relationship object's self/related links and\n * meta, which ride the value's `$edge` per CONTEXT.md.\n */\nfunction buildEdgeView(node: object, member: RawResource, relationEdge?: RelationEdge): object {\n const view: Record<string, unknown> = {}\n // Copy the node's own enumerable props (type/id/attributes/relations) into this per-edge view.\n // INVARIANT (normalize.ts, ADR 0003): every cache patch must walk and patch ALL cached views\n // of a resource, not just the canonical node — `patchAll`/`walkResources`/`patchNode` do exactly\n // that, so these copied props stay consistent across writes. A future node-only write path would\n // leave edge views stale, so it must preserve this walk-every-view invariant.\n Object.assign(view, node)\n // Delegate the node's non-enumerable `$`-accessors so they read through to the node.\n for (const k of ['$meta', '$links', '$self', '$document', '$raw', '$rel'] as const) {\n delegate(view, node, k)\n }\n // A full-resource member's own `links` is its self-link (already on `$links`/`$self`), so a\n // to-many member's `$edge` is meta-only; a to-one additionally carries the relationship\n // object's self/related links via `relationEdge`.\n attachEdgeEnvelope(view, member, false, relationEdge)\n return view\n}\n\n/** An identifier VIEW for a linked-but-not-included member: a clean `{type,id,meta?}` + edge. */\nfunction buildIdentifierView(member: RawResource, relationEdge?: RelationEdge): object {\n const view: Record<string, unknown> = { type: member.type, id: member.id }\n if (member.meta !== undefined) view.meta = member.meta\n // A bare linkage member has no resource of its own, so any `links` it carries are edge links.\n attachEdgeEnvelope(view, member, true, relationEdge)\n return view\n}\n\n/**\n * Attach `$edge` (the member's identifier-level edge meta/links) and `$pivot` sugar over it.\n * For a to-one, the relationship object's self/related links + meta (`relationEdge`) merge in,\n * with member-level meta winning on key collision.\n */\nfunction attachEdgeEnvelope(\n view: object,\n member: RawResource,\n includeLinks: boolean,\n relationEdge?: RelationEdge,\n): void {\n defineGetter(view, '$edge', () => {\n const memberLinks = includeLinks ? member.links : undefined\n const links = memberLinks ?? relationEdge?.links\n const meta = mergeMeta(relationEdge?.meta, member.meta)\n if (links === undefined && meta === undefined) return undefined\n const edge: Edge = {}\n if (links !== undefined) edge.links = links\n if (meta !== undefined) edge.meta = meta\n return edge\n })\n defineGetter(view, '$pivot', () => {\n const pivot = member.meta?.['pivot']\n return isObject(pivot) ? pivot : undefined\n })\n}\n\n/** Merge a relationship-object meta with member-level meta (member wins on key collision). */\nfunction mergeMeta(\n base: Record<string, unknown> | undefined,\n override: Record<string, unknown> | undefined,\n): Record<string, unknown> | undefined {\n if (base === undefined) return override\n if (override === undefined) return base\n return { ...base, ...override }\n}\n\n/** Mirror a non-enumerable accessor from `source` onto `target`, reading through live. */\nfunction delegate(target: object, source: object, k: string): void {\n const value = (source as Record<string, unknown>)[k]\n if (typeof value === 'function') {\n define(target, k, (value as (...a: unknown[]) => unknown).bind(source))\n } else {\n defineGetter(target, k, () => (source as Record<string, unknown>)[k])\n }\n}\n\n/**\n * A top-level / related collection array: `$page` from `doc`, links/meta from the document.\n * `linkage` propagates to `$next()`/`$prev()` so a relationship-endpoint page re-materialises\n * as identifiers, not resources.\n */\nfunction augmentArray(\n members: object[],\n doc: Document,\n ctx: MaterialiseContext,\n linkage: boolean,\n primaryType?: string,\n paginatorOverride?: ResolvedPaginator | PaginatorKind,\n): object[] {\n const arr = [...members]\n const links = doc.links\n defineGetter(arr, '$links', () => links)\n defineGetter(arr, '$meta', () => doc.meta)\n const paginator =\n paginatorOverride === undefined\n ? resolvePaginator(primaryPaginator(doc, ctx, primaryType))\n : typeof paginatorOverride === 'string'\n ? resolvePaginator(paginatorOverride)\n : paginatorOverride\n attachPage(arr, paginator, pageMeta(doc.meta), links, ctx, linkage)\n return arr\n}\n\n/**\n * A to-many relationship array: `$page`/links/meta come from the relationship object.\n * `paginator` is the relation's (so an in-document compound-include to-many reports the same\n * discriminant it would when fetched at its own relationship/related endpoint — \"one model,\n * three surfaces\").\n */\nfunction augmentRelationArray(\n members: object[],\n rel: RawRelationship,\n paginator: ResolvedPaginator,\n ctx: MaterialiseContext,\n): object[] {\n const arr = [...members]\n const links = rel.links\n defineGetter(arr, '$links', () => links)\n defineGetter(arr, '$meta', () => rel.meta)\n attachPage(arr, paginator, pageMeta(rel.meta), links, ctx, false)\n return arr\n}\n\n/** Attach `$page` + `$next()`/`$prev()` driven by link presence (count-free-safe). */\nfunction attachPage(\n arr: object[],\n paginator: ResolvedPaginator,\n meta: Record<string, unknown> | undefined,\n links: Record<string, unknown> | undefined,\n ctx: MaterialiseContext,\n linkage: boolean,\n): void {\n const page: Page = { kind: paginator.kind, keys: paginator.keys, links: pageLinks(links) }\n if (paginator.arm !== undefined) page.arm = paginator.arm\n if (meta !== undefined) page.meta = meta\n defineGetter(arr, '$page', () => page)\n define(arr, '$next', (options?: NavigateOptions) =>\n navigateLink(page.links.next, ctx, linkage, paginator, options?.signal),\n )\n define(arr, '$prev', (options?: NavigateOptions) =>\n navigateLink(page.links.prev, ctx, linkage, paginator, options?.signal),\n )\n}\n\nconst navigateLink = (\n link: string | undefined,\n ctx: MaterialiseContext,\n linkage: boolean,\n paginator: ResolvedPaginator,\n signal: AbortSignal | undefined,\n): Promise<unknown> => {\n if (link === undefined) {\n return Promise.resolve(undefined)\n }\n return signal === undefined\n ? ctx.navigate(link, linkage, paginator)\n : ctx.navigate(link, linkage, paginator, signal)\n}\n\n/** Extract the navigation links (a link may be a string or a `{href}` object). */\nfunction pageLinks(links: Record<string, unknown> | undefined): Page['links'] {\n const out: Page['links'] = {}\n if (!links) return out\n for (const k of ['first', 'prev', 'next', 'last'] as const) {\n const href = linkHref(links[k])\n if (href !== undefined) out[k] = href\n }\n return out\n}\n\nconst linkHref = (link: unknown): string | undefined => {\n if (typeof link === 'string') return link\n if (isObject(link) && typeof link['href'] === 'string') return link['href']\n return undefined\n}\n\nconst pageMeta = (meta: Record<string, unknown> | undefined): Record<string, unknown> | undefined =>\n isObject(meta?.['page']) ? (meta['page'] as Record<string, unknown>) : undefined\n\n/**\n * The document's paginator (from the primary type's descriptor). Prefers the statically-known\n * `primaryType` (passed by the descriptor binding at the call site) so an EMPTY page still reports\n * the collection's real kind; falls back to sniffing the first member's type (for a re-materialised\n * `$next()` page or a related read, where the primary type isn't threaded).\n */\nfunction primaryPaginator(\n doc: Document,\n ctx: MaterialiseContext,\n primaryType?: string,\n): PaginatorDescriptor | undefined {\n const first = Array.isArray(doc.data) ? doc.data[0] : doc.data\n const type =\n primaryType ??\n (isObject(first) && typeof first['type'] === 'string' ? first['type'] : undefined)\n return type !== undefined ? ctx.descriptor[type]?.paginator : undefined\n}\n\n/**\n * A to-many relation's paginator. The relation's OWN related/relationship endpoint may\n * paginate differently than the related type's top-level collection (D6a), so prefer the\n * per-relation `paginator` (carried in the descriptor only when it diverges); otherwise fall back\n * to the related type's own paginator (the relationship and related endpoints both paginate that\n * type), so the discriminant is identical across the three surfaces. A polymorphic relation\n * resolves off its first declared related type (its members span types, matching the relationship\n * endpoint's first-member resolution).\n */\nexport function relatedPaginator(\n relation: RelationDescriptor | undefined,\n ctx: MaterialiseContext,\n): PaginatorDescriptor | undefined {\n if (relation?.paginator !== undefined) return relation.paginator\n const relatedType = relation?.types[0]\n return relatedType === undefined ? undefined : ctx.descriptor[relatedType]?.paginator\n}\n\n/** The kind a relation's related read answers with when it sends no `page`. See {@link relatedPaginator}. */\nexport function relatedPaginatorKind(\n relation: RelationDescriptor | undefined,\n ctx: MaterialiseContext,\n): PaginatorKind {\n return resolvePaginator(relatedPaginator(relation, ctx)).kind\n}\n","/** The JSON:API media type. */\nexport const JSON_API_MEDIA_TYPE = 'application/vnd.api+json'\n\n/** The Atomic Operations extension URI. */\nexport const ATOMIC_EXT = 'https://jsonapi.org/ext/atomic'\n\n/**\n * The media-type parameters a request opts into: extension URIs (`ext`) and/or profile URIs\n * (`profile`). Each is rendered as a space-joined, quoted media-type parameter per RFC 6839 /\n * the JSON:API spec (`ext=\"<uri> <uri>\"`, `profile=\"<uri> <uri>\"`).\n */\nexport interface MediaTypeParams {\n ext?: readonly string[] | undefined\n profiles?: readonly string[] | undefined\n}\n\n/**\n * Compose the JSON:API media type with any opted-in `ext`/`profile` parameters. Each parameter\n * is a space-joined, double-quoted URI list, in the canonical order `ext` then `profile` so the\n * output is deterministic (stable for caching/assertions). Empty/absent lists are omitted; with\n * neither, the bare {@link JSON_API_MEDIA_TYPE} is returned (no behaviour change for a plain\n * request). Used for both `Accept` and (when a body is present) `Content-Type`.\n */\nexport function mediaType(params: MediaTypeParams = {}): string {\n let out = JSON_API_MEDIA_TYPE\n if (params.ext !== undefined && params.ext.length > 0) {\n out += `; ext=\"${params.ext.join(' ')}\"`\n }\n if (params.profiles !== undefined && params.profiles.length > 0) {\n out += `; profile=\"${params.profiles.join(' ')}\"`\n }\n return out\n}\n\n/**\n * The media type for an Atomic Operations request/response: the JSON:API media type carrying\n * the atomic `ext` parameter. Sent as both `Content-Type` and `Accept` on a `client.atomic`\n * batch (per the JSON:API atomic extension).\n */\nexport const ATOMIC_MEDIA_TYPE = mediaType({ ext: [ATOMIC_EXT] })\n\n/** A resource identifier: the shape a non-included but linked relation takes. */\nexport interface ResourceIdentifier<TType extends string = string> {\n type: TType\n id: string\n meta?: Record<string, unknown>\n}\n\n/** A local id reference used inside an atomic transaction (`{ type, lid }`). */\nexport interface LocalIdentifier<TType extends string = string> {\n type: TType\n lid: string\n}\n","import type { HeadersInit } from './client'\nimport { JsonApiError, type JsonApiErrorObject } from './errors'\nimport type { JsonApiTransport, TransportRequest } from './transport'\nimport { mediaType } from './types'\n\n/**\n * A read query, in the flat shape the fluent surface accepts. Serialised to the\n * JSON:API bracketed query-parameter families by {@link serializeQuery}.\n */\nexport interface ReadQuery {\n filter?: Record<string, unknown>\n sort?: string | string[]\n include?: string[]\n fields?: Record<string, string[]>\n /** Relationship-count tokens (the Countable profile) — serialised comma-joined onto `withCount`. */\n withCount?: readonly string[]\n page?: Record<string, unknown>\n /** Cancels the read when signalled. Never serialised; passed to the transport. */\n signal?: AbortSignal\n}\n\n/** A loose JSON:API document — enough structure for the runtime to materialise. */\nexport interface Document {\n data?: unknown\n included?: unknown[]\n links?: Record<string, unknown>\n meta?: Record<string, unknown>\n jsonapi?: Record<string, unknown>\n}\n\n/** The ambient context a request executes in (carried by the client). */\nexport interface JsonApiContext {\n baseUrl: string\n transport: JsonApiTransport\n headers?: () => HeadersInit | Promise<HeadersInit>\n}\n\n/** A single read/write request, before serialisation. */\nexport interface JsonApiRequest {\n method: string\n /**\n * A path appended to `baseUrl` (a filled path template such as `/albums/1`), or an absolute\n * `http(s)://` URL. With {@link link} set, a link from the server instead.\n */\n path: string\n /**\n * `path` is a link the server sent (a `links` member such as `next`, or a `Location` header).\n * It resolves against `baseUrl` per RFC 3986, so a root-relative `/api/v5/albums?page[number]=2`\n * keeps the base's origin but not its path, where a path template is appended to the base.\n */\n link?: boolean\n query?: ReadQuery\n body?: unknown\n /**\n * Override the request `Content-Type` (defaults to the negotiated JSON:API media type when a\n * body is present). Set for a custom action's `raw` input, where the payload is not a JSON:API\n * document. When set it wins over {@link ext}/{@link profiles} for `Content-Type`.\n */\n contentType?: string\n /**\n * Override the request `Accept` header (defaults to the negotiated JSON:API media type). Set\n * for a `raw`-input action that still expects a document response; when set it wins over\n * {@link ext}/{@link profiles} for `Accept`.\n */\n accept?: string\n /**\n * Extension URIs to negotiate (the media-type `ext` parameter), e.g. the atomic ext. Composed\n * into both `Accept` and (when a body is present) `Content-Type` via the JSON:API media type,\n * unless {@link accept}/{@link contentType} override the respective header.\n */\n ext?: readonly string[]\n /**\n * Profile URIs to negotiate (the media-type `profile` parameter), e.g. the Countable profile a\n * `withCount` read requires. Composed into both `Accept` and (when a body is present)\n * `Content-Type`, unless {@link accept}/{@link contentType} override the respective header.\n */\n profiles?: readonly string[]\n /**\n * Send the body verbatim (a `string`) instead of JSON-stringifying it. Used for a `raw`\n * action body that's already serialised; a non-string body still falls back to\n * `JSON.stringify`.\n */\n raw?: boolean\n /** Passed through to the transport so the caller can cancel the request. */\n signal?: AbortSignal\n}\n\nconst isAbsolute = (path: string): boolean => /^https?:\\/\\//i.test(path)\n\n// Stands in for the origin when `baseUrl` is itself relative (e.g. `/api` in a browser app).\nconst RELATIVE_ORIGIN = 'http://relative.invalid'\n\n/** Resolve a server link against `baseUrl` per RFC 3986. An absolute link is kept verbatim. */\nfunction resolveLink(link: string, baseUrl: string): string {\n if (isAbsolute(link)) {\n return link\n }\n if (isAbsolute(baseUrl)) {\n return new URL(link, baseUrl).href\n }\n const resolved = new URL(link, new URL(baseUrl, RELATIVE_ORIGIN))\n return resolved.origin === RELATIVE_ORIGIN\n ? resolved.pathname + resolved.search + resolved.hash\n : resolved.href\n}\n\nconst append = (parts: string[], key: string, value: unknown): void => {\n if (value === undefined || value === null || value === '') {\n return\n }\n parts.push(`${key}=${encodeURIComponent(String(value))}`)\n}\n\nconst isPlainObject = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value)\n\n/**\n * Append a filter value under its (already-bracketed) key. A plain object is a STRUCTURED filter\n * value — a `Range`/`DateRange` deepObject (`{ min, max }`) — and recurses into nested bracketed\n * keys (`filter[k][min]=`), matching the server's deepObject wire shape; an array joins with `,`;\n * a scalar is appended directly. Without the object branch a `{ min, max }` value would reach the\n * wire as `filter[k]=[object Object]` and be silently ignored by the server (D23).\n */\nconst appendFilterValue = (parts: string[], key: string, value: unknown): void => {\n if (isPlainObject(value)) {\n for (const [subKey, subValue] of Object.entries(value)) {\n appendFilterValue(parts, `${key}[${subKey}]`, subValue)\n }\n return\n }\n append(parts, key, Array.isArray(value) ? value.join(',') : value)\n}\n\n/**\n * Serialise a read query to a query string (no leading `?`). Bracketed keys\n * (`filter[title]`, `page[number]`, `fields[albums]`) are kept literal; only values\n * are percent-encoded. Empty/undefined entries are skipped; key order is deterministic\n * (family order, then insertion order within a family) so URLs are stable for caching.\n */\nexport function serializeQuery(query: ReadQuery): string {\n const parts: string[] = []\n\n if (query.filter) {\n for (const [key, value] of Object.entries(query.filter)) {\n appendFilterValue(parts, `filter[${key}]`, value)\n }\n }\n\n if (query.sort !== undefined) {\n append(parts, 'sort', Array.isArray(query.sort) ? query.sort.join(',') : query.sort)\n }\n\n if (query.include) {\n append(parts, 'include', query.include.join(','))\n }\n\n if (query.fields) {\n for (const [type, names] of Object.entries(query.fields)) {\n // An explicitly-empty fieldset (`fields[type]=`) is meaningful in JSON:API — it selects NO\n // members of that type (id-only), which is exactly what the return type narrows to. Emit the\n // empty param verbatim (the generic empty-skip would drop it, leaving the type over-narrowed\n // vs. the full resource the server would otherwise return).\n parts.push(`fields[${type}]=${encodeURIComponent(names.join(','))}`)\n }\n }\n\n if (query.withCount && query.withCount.length > 0) {\n append(parts, 'withCount', query.withCount.join(','))\n }\n\n if (query.page) {\n for (const [key, value] of Object.entries(query.page)) {\n append(parts, `page[${key}]`, Array.isArray(value) ? value.join(',') : value)\n }\n }\n\n return parts.join('&')\n}\n\n/**\n * Execute a request: build the URL (a server link resolved against `baseUrl`, an absolute path\n * verbatim, else `baseUrl + path`), apply content negotiation and any async per-request headers,\n * then drive the transport. A 2xx with a body parses to a {@link Document}; an empty /\n * 204 body resolves to `undefined`. A non-2xx throws a {@link JsonApiError} carrying the\n * parsed error objects (tolerating a non-JSON error body with an empty list).\n */\nexport async function execute(\n ctx: JsonApiContext,\n req: JsonApiRequest,\n): Promise<Document | undefined> {\n let url = req.link\n ? resolveLink(req.path, ctx.baseUrl)\n : isAbsolute(req.path)\n ? req.path\n : ctx.baseUrl + req.path\n if (req.query) {\n const qs = serializeQuery(req.query)\n if (qs) {\n url += (url.includes('?') ? '&' : '?') + qs\n }\n }\n\n // Compose the negotiated media type (`application/vnd.api+json` + any opted-in ext/profile\n // parameters) once; a bare request yields the plain JSON:API media type. An explicit\n // `accept`/`contentType` override wins over the negotiated value for its header.\n const negotiated = mediaType({ ext: req.ext, profiles: req.profiles })\n const headers: Record<string, string> = { Accept: req.accept ?? negotiated }\n\n const transportReq: TransportRequest = { method: req.method, url, headers }\n if (req.signal !== undefined) {\n transportReq.signal = req.signal\n }\n if (req.body !== undefined) {\n headers['Content-Type'] = req.contentType ?? negotiated\n transportReq.body =\n req.raw && typeof req.body === 'string' ? req.body : JSON.stringify(req.body)\n }\n\n if (ctx.headers) {\n mergeHeaders(headers, await ctx.headers())\n }\n\n const res = await ctx.transport(transportReq)\n\n if (res.status >= 200 && res.status < 300) {\n return parseDocument(res.body)\n }\n\n throw new JsonApiError(res.status, parseErrors(res.body))\n}\n\n/**\n * Merge a `HeadersInit` (object / tuple array / `Headers`) into the outgoing map,\n * preserving the caller's casing (unlike the WHATWG `Headers`, which lowercases). The\n * caller's headers override the runtime defaults, so per-request `Accept` etc. can win.\n */\nfunction mergeHeaders(into: Record<string, string>, extra: HeadersInit): void {\n if (extra instanceof Headers) {\n extra.forEach((value, key) => {\n into[key] = value\n })\n } else if (Array.isArray(extra)) {\n for (const [key, value] of extra) {\n if (key !== undefined && value !== undefined) {\n into[key] = String(value)\n }\n }\n } else {\n for (const [key, value] of Object.entries(extra)) {\n into[key] = String(value)\n }\n }\n}\n\nfunction parseDocument(body: string): Document | undefined {\n if (body === '') {\n return undefined\n }\n return JSON.parse(body) as Document\n}\n\nfunction parseErrors(body: string): JsonApiErrorObject[] {\n if (body === '') {\n return []\n }\n try {\n const doc = JSON.parse(body) as { errors?: JsonApiErrorObject[] }\n return Array.isArray(doc.errors) ? doc.errors : []\n } catch {\n return []\n }\n}\n","/**\n * The write serialisation layer (CONTEXT.md \"Write surface\" + \"Transport seam & error\n * model\"; ADR 0001/0002). Two descriptor-aware inverses of the wire shape:\n *\n * - {@link toDocument} builds the JSON:API write document from the caller's FLAT input\n * (the client owns the envelope), routing each key to `attributes` or `relationships`\n * by the descriptor and coercing relationship linkage by cardinality;\n * - {@link remapPointer} / {@link withRemappedPaths} invert a write error's\n * `source.pointer` back to that flat path so {@link JsonApiError.byPath} groups by the\n * shape the caller actually supplied (form/validation UX).\n */\nimport type { ApiDescriptor } from './descriptor'\nimport { JsonApiError, type JsonApiErrorObject } from './errors'\n\n/** Per-edge writable pivot data, supplied on a to-many member as the `$pivot` key. */\nconst PIVOT_KEY = '$pivot'\n\n/** The reserved input keys never routed to attributes/relationships. */\nconst RESERVED_INPUT: ReadonlySet<string> = new Set(['type', 'id'])\n\nconst isObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v)\n\n/**\n * A JSON:API resource identifier in a write document: `{ type, id }` or — inside an atomic\n * batch — `{ type, lid }` (a reference to a resource created earlier in the same transaction).\n * May carry writable pivot meta on a to-many member.\n */\ninterface WriteIdentifier {\n type: string\n id?: string\n lid?: string\n meta?: { pivot: Record<string, unknown> } & Record<string, unknown>\n}\n\n/** A relationship document: `{ data: <linkage> }`. */\nexport interface RelationshipDocument {\n data: WriteIdentifier | WriteIdentifier[] | null\n}\n\n/** The JSON:API write document {@link toDocument} produces. */\nexport interface WriteDocument {\n data: {\n type: string\n id?: string\n attributes?: Record<string, unknown>\n relationships?: Record<string, RelationshipDocument>\n }\n}\n\n/** Options for {@link toDocument}. */\nexport interface ToDocumentOptions {\n /**\n * The resource id, supplied for an UPDATE (it comes from the handle, not the input). When\n * present it is always written as `data.id`. Omit for a CREATE — then `input.id` is written\n * only per the type's `clientId` policy.\n */\n id?: string\n}\n\n/**\n * Build a JSON:API write document from FLAT input. Each input key is routed via the\n * descriptor: a declared relation -> `data.relationships[name] = { data: <linkage> }`;\n * anything else -> `data.attributes[name]`. Relationship linkage is coerced by cardinality:\n *\n * - to-one accepts an identifier `{type,id}`, a materialised resource object (its `{type,id}`\n * are extracted), or `null` (-> `data: null`, clearing the relationship);\n * - to-many accepts an array of those; a member may carry a `$pivot` object, rendered as the\n * member identifier's `meta.pivot` (writable pivot fields).\n *\n * `data.type` is always the accessor `type`. `data.id` is written from `opts.id` (an update),\n * else from `input.id` only when the type's `clientId` policy allows it (omitted when\n * `forbidden`, passed through when `optional`/`required`).\n */\nexport function toDocument(\n descriptor: ApiDescriptor,\n type: string,\n input: Record<string, unknown>,\n opts?: ToDocumentOptions,\n): WriteDocument {\n const resource = descriptor[type]\n const relations = resource?.relations ?? {}\n\n const data: WriteDocument['data'] = { type }\n\n const id = resolveId(input, opts, resource?.clientId)\n if (id !== undefined) {\n data.id = id\n }\n\n const attributes: Record<string, unknown> = {}\n const relationships: Record<string, RelationshipDocument> = {}\n\n for (const [keyName, value] of Object.entries(input)) {\n if (RESERVED_INPUT.has(keyName)) {\n continue\n }\n const relation = relations[keyName]\n if (relation !== undefined) {\n relationships[keyName] = { data: coerceLinkage(value, relation.cardinality) }\n } else {\n attributes[keyName] = value\n }\n }\n\n if (Object.keys(attributes).length > 0) {\n data.attributes = attributes\n }\n if (Object.keys(relationships).length > 0) {\n data.relationships = relationships\n }\n\n return { data }\n}\n\n/**\n * Build a relationship-mutation document `{ data: <linkage> }` for the\n * `/{type}/{id}/relationships/{rel}` endpoints, coercing the caller's refs by cardinality:\n *\n * - to-one accepts an identifier `{type,id}`, a materialised resource object, or `null`\n * (-> `data: null`, clearing the relationship);\n * - to-many accepts an array of those; a member may carry `$pivot`, rendered as the member\n * identifier's `meta.pivot`.\n *\n * The same {@link coerceLinkage} the whole-resource writer uses, so a relationship written\n * standalone and one embedded in a resource serialise identically.\n */\nexport function toRelationshipDocument(\n refs: unknown,\n cardinality: 'one' | 'many',\n): RelationshipDocument {\n return { data: coerceLinkage(refs, cardinality) }\n}\n\n/** Resolve the document id: an update's handle id always wins; a create honours the policy. */\nfunction resolveId(\n input: Record<string, unknown>,\n opts: ToDocumentOptions | undefined,\n clientId: ApiDescriptor[string]['clientId'] | undefined,\n): string | undefined {\n if (opts?.id !== undefined) {\n return opts.id\n }\n if (clientId === 'forbidden') {\n return undefined\n }\n const candidate = input['id']\n return typeof candidate === 'string' ? candidate : undefined\n}\n\n/** Coerce one relationship value to linkage by cardinality (array for to-many, identifier/null for to-one). */\nfunction coerceLinkage(\n value: unknown,\n cardinality: 'one' | 'many',\n): WriteIdentifier | WriteIdentifier[] | null {\n if (cardinality === 'many') {\n const members = Array.isArray(value) ? value : value == null ? [] : [value]\n return members.map((member) => toIdentifier(member))\n }\n if (value === null || value === undefined) {\n return null\n }\n return toIdentifier(value)\n}\n\n/**\n * Extract a resource identifier from a linkage input value: an identifier `{type,id}`, a\n * materialised resource object (the same `{type,id}` enumerable props), or — inside an atomic\n * batch — a local identifier `{type,lid}` referencing a just-created resource (the handle a\n * `tx.create` returns). A `$pivot` key is lifted onto `meta.pivot` (writable pivot fields).\n * Throws on a value carrying no `type`, or neither an `id` nor a `lid`.\n */\nfunction toIdentifier(value: unknown): WriteIdentifier {\n if (!isObject(value)) {\n throw new TypeError(\n `Relationship linkage must be an identifier or resource object; received ${describe(value)}`,\n )\n }\n const { type, id, lid } = value\n if (typeof type !== 'string') {\n throw new TypeError('Relationship linkage requires a `type`')\n }\n // An `id` identifies an existing resource; a `lid` references one created earlier in the\n // same atomic transaction. Exactly one is needed.\n const identifier: WriteIdentifier =\n typeof id === 'string'\n ? { type, id }\n : typeof lid === 'string'\n ? { type, lid }\n : (() => {\n throw new TypeError('Relationship linkage requires an `id` or a `lid`')\n })()\n\n const pivot = value[PIVOT_KEY]\n if (isObject(pivot)) {\n identifier.meta = { pivot }\n }\n return identifier\n}\n\nconst describe = (value: unknown): string =>\n value === null ? 'null' : Array.isArray(value) ? 'an array' : typeof value\n\n/**\n * Invert a write error's `source.pointer` to the caller's FLAT input path, descriptor-aware:\n *\n * - `/data/attributes/title` -> `title`\n * - `/data/relationships/artist/data` -> `artist`\n * - `/data/id` -> `id` (client-id create conflict)\n * - `/data/attributes/releaseInfo/label` -> `releaseInfo.label` (nested map)\n * - `/data/relationships/orderedTracks/data/0/meta/pivot/position` -> `orderedTracks[0].$pivot.position`\n *\n * Pivot values nest under a member's `meta.pivot` (symmetric with reads — see\n * `JsonPointerBuilder::forLinkageMeta` / bundle ADR 0103), so a `meta/pivot/<field>` tail on\n * a member of a pivot-bearing to-many is surfaced as `.$pivot.<field>`.\n *\n * A query-side pointer is left untouched (those are already user-facing — see\n * {@link withRemappedPaths}). An unrecognised / non-write pointer is returned verbatim.\n * `/data/type` and a bare `/data` are passed through (the client owns those; the caller\n * never supplies them).\n */\nexport function remapPointer(descriptor: ApiDescriptor, type: string, pointer: string): string {\n const segments = pointer.split('/').filter((s) => s !== '')\n if (segments[0] !== 'data') {\n return pointer\n }\n\n if (segments[1] === 'attributes') {\n return remapAttribute(segments.slice(2))\n }\n if (segments[1] === 'id') {\n return 'id'\n }\n if (segments[1] === 'relationships') {\n return remapRelationship(descriptor[type]?.relations ?? {}, segments.slice(2))\n }\n\n return pointer\n}\n\n/** `[releaseInfo, label]` -> `releaseInfo.label`; a bare `[title]` -> `title`. */\nfunction remapAttribute(rest: readonly string[]): string {\n return rest.join('.')\n}\n\n/**\n * Remap a relationship pointer's tail (everything after `relationships/`, or — for a\n * relationship-endpoint document — the relation name spliced ahead of the `data` tail). The\n * leading segment is the relation name; a `data` segment is dropped; a numeric index becomes\n * `[n]`; a `meta/pivot/<field>` tail on a pivot-bearing relation becomes `.$pivot.<field>`. The\n * `relations` map tells us whether the relation carries pivot (so a member's `meta.pivot`\n * fields are the pivot data the bundle validates).\n */\nfunction remapRelationship(\n relations: Readonly<Record<string, { pivot?: boolean }>>,\n rest: readonly string[],\n): string {\n const name = rest[0]\n if (name === undefined) {\n return 'data/relationships'\n }\n const hasPivot = relations[name]?.pivot === true\n\n let path = name\n for (let i = 1; i < rest.length; i++) {\n const seg = rest[i]!\n if (seg === 'data') {\n continue\n }\n if (/^\\d+$/.test(seg)) {\n path += `[${seg}]`\n } else if (seg === 'meta' && hasPivot && rest[i + 1] === 'pivot') {\n // Pivot values nest under `meta.pivot` (bundle ADR 0103) — collapse the\n // `meta/pivot` pair to `$pivot`; the offending field follows.\n path += '.$pivot'\n i++\n } else {\n path += `.${seg}`\n }\n }\n\n return path\n}\n\n/**\n * Invert a relationship-MUTATION error's `source.pointer` to the caller's flat path, for the\n * `/{type}/{id}/relationships/{rel}` endpoints. Two pointer shapes reach here:\n *\n * - the LINKAGE document shape (validation on the linkage members), rooted at `data` — the\n * relation name is supplied by the route, not the wire:\n * - `/data` -> `<rel>` (to-one clear/set)\n * - `/data/meta/pivot/position` -> `<rel>.$pivot.position` (to-one pivot, rare)\n * - `/data/0/meta/pivot/position` -> `<rel>[0].$pivot.position`\n * - the RESOURCE-document shape that core's relationship prohibitions emit unchanged\n * (`FullReplacementProhibited`/`RemovalProhibited`/`AdditionProhibited`/\n * `RelationshipTypeInappropriate` all point at `/data/relationships/<name>`) — short-circuited\n * to the relation name (drop the `relationships/<name>` prefix) rather than treating\n * `relationships` as a linkage sub-segment.\n *\n * A non-`data` pointer is returned verbatim.\n */\nexport function remapRelationshipPointer(rel: string, hasPivot: boolean, pointer: string): string {\n const segments = pointer.split('/').filter((s) => s !== '')\n if (segments[0] !== 'data') {\n return pointer\n }\n // A resource-document relationship pointer (`/data/relationships/<name>...`): the route's\n // relation name owns the path; drop the `relationships/<name>` prefix (segments[2] is the\n // wire name, == the route `rel`) and remap the remaining tail under the route relation.\n const tail = segments[1] === 'relationships' ? segments.slice(3) : segments.slice(1)\n return remapRelationship({ [rel]: { pivot: hasPivot } }, [rel, ...tail])\n}\n\n/**\n * Return a {@link JsonApiError} with each error object's `path` populated by remapping its\n * `source.pointer` (descriptor-aware) — so {@link JsonApiError.byPath} groups by the flat input\n * shape. Errors with no write pointer (e.g. a `source.parameter` query-side error, or no\n * source) are passed through unchanged. The original error is not mutated.\n */\nexport function withRemappedPaths(\n error: JsonApiError,\n descriptor: ApiDescriptor,\n type: string,\n): JsonApiError {\n return mapErrors(error, (pointer) => ({ path: remapPointer(descriptor, type, pointer) }))\n}\n\n/**\n * The relationship-mutation twin of {@link withRemappedPaths}: remap each error's pointer\n * keyed by the relation name from the route. Handles both the linkage-document shape (rooted\n * at `data`) and the resource-document shape core's prohibition exceptions emit\n * (`/data/relationships/<rel>`). `hasPivot` lets a member-level `meta` field surface as\n * `$pivot`. The original error is not mutated.\n */\nexport function withRemappedRelationshipPaths(\n error: JsonApiError,\n rel: string,\n hasPivot: boolean,\n): JsonApiError {\n return mapErrors(error, (pointer) => ({ path: remapRelationshipPointer(rel, hasPivot, pointer) }))\n}\n\n/**\n * Invert an atomic-batch error's `source.pointer` to `(opIndex, flatPath)`: an atomic 422\n * points at `/atomic:operations/{n}/data/...`, so strip the `atomic:operations/{n}` prefix to\n * the op index, resolve that op's wire type (`typeForOp`), and delegate the remaining `/data`\n * tail to the same {@link remapPointer} a standalone write uses (so the flat path is identical\n * — `title`, `releaseInfo.label`, `artist`, …). A pointer without the atomic prefix is remapped\n * verbatim under the (whole-batch) document at op index `undefined`. The original error is not\n * mutated.\n */\nexport function withRemappedAtomicPaths(\n error: JsonApiError,\n descriptor: ApiDescriptor,\n typeForOp: (opIndex: number) => string | undefined,\n): JsonApiError {\n return mapErrors(error, (pointer) => {\n const segments = pointer.split('/').filter((s) => s !== '')\n if (segments[0] !== 'atomic:operations' || segments[1] === undefined) {\n return { path: pointer }\n }\n const opIndex = Number(segments[1])\n if (!Number.isInteger(opIndex)) {\n return { path: pointer }\n }\n const type = typeForOp(opIndex)\n const tail = `/${segments.slice(2).join('/')}`\n const path = type === undefined ? tail : remapPointer(descriptor, type, tail)\n return { opIndex, path }\n })\n}\n\n/** What a per-error remap resolves to: the flat `path` and, for an atomic batch, the `opIndex`. */\ninterface RemapResult {\n path: string\n opIndex?: number\n}\n\n/** Shared error-mapper: clone each error with `path`/`opIndex` from its `source.pointer`, else pass through. */\nfunction mapErrors(error: JsonApiError, remap: (pointer: string) => RemapResult): JsonApiError {\n const remapped: JsonApiErrorObject[] = error.errors.map((e) => {\n const pointer = e.source?.pointer\n if (pointer === undefined) {\n return e\n }\n const { path, opIndex } = remap(pointer)\n return opIndex === undefined ? { ...e, path } : { ...e, path, opIndex }\n })\n return new JsonApiError(error.status, remapped, error.message)\n}\n","/**\n * The Atomic Operations transaction builder (CONTEXT.md \"Atomic — typed transaction builder,\n * type-in-object\"; ADR 0001). `client.atomic(tx => { … })` records a batch of operations and\n * posts them all-or-nothing to the server's `/operations` endpoint (the atomic ext media type),\n * returning the positional, materialised results.\n *\n * The `tx` recorder is NOT type-scoped (one builder spans every type), so each op carries its\n * own `type` discriminant:\n *\n * - `tx.create({ type, …flatFields })` -> an `add` op; returns a handle that DOUBLES AS a\n * `lid`-bearing relationship ref (`{ type, lid }`) so a just-created resource wires into a\n * later op without a server-assigned id;\n * - `tx.update({ type, id, …flatFields })` -> an `update` op (a `ref` to the existing resource);\n * - `tx.delete({ type, id })` -> a `remove` op.\n *\n * Each create gets a deterministic `lid` from its op index (`atomic-<index>`), so the cross-op\n * reference is stable and human-readable. The flat->envelope serialisation reuses the same\n * {@link toDocument} the standalone writes use, so an atomic create body is identical to a\n * `POST /{type}` body (minus the swap of a server `id` for the generated `lid`).\n *\n * Content negotiation: the batch opts into the atomic extension via the request's `ext`\n * parameter ({@link ATOMIC_EXT}), so {@link execute} composes the atomic-ext media type\n * (`application/vnd.api+json; ext=\"…\"`) for both `Content-Type` and `Accept` — the generalised\n * media-type path the rest of the runtime shares.\n */\nimport type { ApiDescriptor } from './descriptor'\nimport { JsonApiError } from './errors'\nimport { materialise, type MaterialiseContext } from './materialise'\nimport { execute, type JsonApiContext, type JsonApiRequest } from './request'\nimport type { AtomicResultOf, CreateInput, TypeName, UpdateInput } from './result-types'\nimport { toDocument, withRemappedAtomicPaths } from './serialize-write'\nimport type { LocalIdentifier, ResourceIdentifier } from './types'\nimport { ATOMIC_EXT } from './types'\n\n/**\n * A reference to a resource for an `update`/`remove` op: a bare identifier (or a materialised\n * resource — its `{type,id}` extracted), OR a `{ type, lid }` local identifier targeting a\n * resource CREATED EARLIER IN THE SAME BATCH (e.g. a `tx.create` handle), so an op can update\n * or remove a just-created resource that has no server id yet (per the JSON:API atomic ext,\n * `ref` may carry `lid` instead of `id`).\n */\nexport type AtomicRef =\n | ResourceIdentifier\n | { type: string; id: string }\n | LocalIdentifier\n | { type: string; lid: string }\n\n/**\n * Flat create input for type `T`: the `type` discriminant plus the type's flat create\n * attributes/relation slots (reusing {@link CreateInput}; relation slots may reference a prior\n * `tx.create` handle). `D`/`W` thread the descriptor + write-attribute map for precise fields;\n * the loose defaults keep a codegen-less caller compiling.\n */\nexport type AtomicCreateInput<D extends ApiDescriptor, W, T extends TypeName<D>> = {\n type: T\n} & CreateInput<D, W, T>\n\n/**\n * Flat update input for type `T`: the `type` discriminant + the target's identity (`id`, OR a\n * `lid` for a resource created earlier in the same batch) plus the type's flat update\n * attributes/relation slots (reusing {@link UpdateInput}).\n */\nexport type AtomicUpdateInput<D extends ApiDescriptor, W, T extends TypeName<D>> =\n | ({ type: T; id: string; lid?: never } & UpdateInput<D, W, T>)\n | ({ type: T; lid: string; id?: never } & UpdateInput<D, W, T>)\n\n/**\n * The handle a {@link AtomicRecorder.create} returns. It is a `lid`-bearing relationship ref\n * (`{ type, lid }`) — pass it (or spread it) into a later op's relation slot to wire the\n * just-created resource without a server id. It also carries its op `kind` (`'create'`) and its\n * `opIndex` (its position in the batch, the created resource's positional result) so the typed\n * result tuple can map it back to the right positional result.\n */\nexport interface AtomicCreateHandle<T extends string = string> extends LocalIdentifier {\n readonly type: T\n readonly kind: 'create'\n readonly opIndex: number\n}\n\n/**\n * The handle a {@link AtomicRecorder.update} returns: its type discriminant `T`, its op `kind`\n * (`'update'`) and its `opIndex`. Returned (alongside the create handle) from the callback so its\n * positional result is typed as the materialised resource of `T`.\n */\nexport interface AtomicUpdateHandle<T extends string = string> {\n readonly type: T\n readonly kind: 'update'\n readonly opIndex: number\n}\n\n/**\n * The handle a {@link AtomicRecorder.delete} returns: its op `kind` (`'delete'`) and its\n * `opIndex`. A delete carries no data, so its positional result is `undefined`.\n */\nexport interface AtomicDeleteHandle {\n readonly kind: 'delete'\n readonly opIndex: number\n}\n\n/**\n * Any handle a recorder method returns — the element type of the callback's result tuple, used\n * as the inference constraint for the typed-tuple `atomic` form. The discriminant is left open\n * (`string`, NOT `TypeName<D>`) DELIBERATELY, so the type is descriptor-agnostic: a tighter\n * `TypeName<D>` constraint would flow the full type union into each element's contextual type\n * during array-literal inference, widening a `tx.create({ type: 'albums' })` handle's `T` back to\n * the whole union — losing the per-op narrowing. The recorder methods already guarantee each `T`\n * is a real `TypeName<D>`, so the open constraint is sound; `AtomicResultOf` re-infers the precise\n * `T` per element (guarded by `TypeName<D>` there).\n */\nexport type AtomicHandle =\n | AtomicCreateHandle<string>\n | AtomicUpdateHandle<string>\n | AtomicDeleteHandle\n\n/** A single recorded atomic operation, in wire shape (`op` + `ref`/`data`). */\ninterface AtomicOperation {\n op: 'add' | 'update' | 'remove'\n ref?: { type: string; id?: string; lid?: string }\n data?: unknown\n}\n\n/** The identity of an op's target resource: a server `id` or a same-batch `lid`. */\ntype AtomicIdentity = { id: string } | { lid: string }\n\n/**\n * The recorder handed to the `client.atomic` callback. Each method appends an op (preserving\n * order) and returns a handle carrying the op's discriminant `T`, `kind`, and `opIndex`. The\n * methods are generic over `T` (inferred from the input's `type`), so a create/update is typed\n * by the type's write-input ({@link CreateInput}/{@link UpdateInput}) and its handle resolves to\n * the materialised resource of `T`. `create` still returns a usable `{ type, lid }` ref so a\n * just-created resource wires into a later op without a server id.\n */\nexport interface AtomicRecorder<D extends ApiDescriptor, W> {\n create<T extends TypeName<D>>(input: AtomicCreateInput<D, W, T>): AtomicCreateHandle<T>\n update<T extends TypeName<D>>(input: AtomicUpdateInput<D, W, T>): AtomicUpdateHandle<T>\n delete(ref: AtomicRef): AtomicDeleteHandle\n}\n\n/** One positional result of an atomic batch: the materialised `data` (a resource/`null`) plus any op `meta`. */\nexport interface AtomicResult<Data = unknown> {\n data: Data\n meta?: Record<string, unknown>\n}\n\nconst isObject = (v: unknown): v is Record<string, unknown> =>\n typeof v === 'object' && v !== null && !Array.isArray(v)\n\n/** The deterministic local id for the create at `opIndex` (stable + human-readable). */\nconst lidFor = (opIndex: number): string => `atomic-${opIndex}`\n\n/**\n * The typed result tuple for a callback that returns a tuple of handles `Ops`: each returned\n * handle maps (by {@link AtomicResultOf}) to its materialised positional result — a create/update\n * handle to the `AtomicResult` of its type, a delete handle to `undefined`. The mapping is\n * positional in the RETURNED order (sound regardless of subset/reorder, since the runtime resolves\n * each handle by its `opIndex`).\n */\nexport type AtomicResults<D extends ApiDescriptor, A, Ops extends readonly AtomicHandle[]> = {\n -readonly [K in keyof Ops]: AtomicResultOf<D, A, Ops[K]>\n}\n\n/**\n * Run an atomic transaction. Invokes `build` with a recorder, collecting the operations (always\n * via the recorder's side effect, preserving op order), then posts `{ \"atomic:operations\": [...] }`\n * to the atomic endpoint with the ext media type as both `Content-Type` and `Accept`. The server's\n * `{ \"atomic:results\": [...] }` is parsed positionally — each result's `data` materialised the same\n * as a read.\n *\n * The return shape depends on what `build` returns:\n *\n * - returns a tuple of handles -> a per-op POSITIONALLY-TYPED tuple: each returned handle is\n * resolved to its result BY ITS `opIndex` (`results[handle.opIndex]`), so the tuple is sound\n * regardless of the order/subset the callback returns the handles in (a delete handle resolves\n * to `undefined`);\n * - returns void/nothing -> the loose, ordered `AtomicResult[]` (backward-compatible).\n *\n * A thrown {@link JsonApiError} has each error's pointer remapped to `(opIndex, flatPath)` (the\n * failing op's type drives the flat-path inversion) before it propagates.\n */\nexport function runAtomic<D extends ApiDescriptor, A, W, const Ops>(\n request: JsonApiContext,\n materialiseCtx: MaterialiseContext,\n descriptor: D,\n path: string,\n build: (tx: AtomicRecorder<D, W>) => Ops,\n): Promise<Ops extends readonly AtomicHandle[] ? AtomicResults<D, A, Ops> : AtomicResult[]>\nexport async function runAtomic(\n request: JsonApiContext,\n materialiseCtx: MaterialiseContext,\n descriptor: ApiDescriptor,\n path: string,\n build: (tx: AtomicRecorder<ApiDescriptor, unknown>) => readonly AtomicHandle[] | void,\n): Promise<AtomicResult[] | readonly (AtomicResult | undefined)[]> {\n const operations: AtomicOperation[] = []\n /** The wire type of each op, by index — drives the error-pointer remap. */\n const typeByOp: string[] = []\n\n const recorder: AtomicRecorder<ApiDescriptor, unknown> = {\n create(input) {\n const opIndex = operations.length\n const lid = lidFor(opIndex)\n const data = atomicResourceData(descriptor, input, { lid })\n operations.push({ op: 'add', data })\n typeByOp.push(input.type)\n return { type: input.type, lid, kind: 'create', opIndex }\n },\n update(input) {\n const opIndex = operations.length\n const identity: AtomicIdentity =\n input.id !== undefined ? { id: input.id } : { lid: input.lid }\n const data = atomicResourceData(descriptor, input, identity)\n operations.push({ op: 'update', ref: { type: input.type, ...identity }, data })\n typeByOp.push(input.type)\n return { type: input.type, kind: 'update', opIndex }\n },\n delete(ref) {\n const opIndex = operations.length\n const { type, identity } = toRef(ref)\n operations.push({ op: 'remove', ref: { type, ...identity } })\n typeByOp.push(type)\n return { kind: 'delete', opIndex }\n },\n }\n\n // The callback always drives the recorder side effect (collecting ops in order); its RETURN\n // VALUE — when it's a tuple of handles — selects the typed positional result per handle.\n const returned = build(recorder)\n\n const req: JsonApiRequest = {\n method: 'POST',\n path,\n body: { 'atomic:operations': operations },\n // Opt into the atomic extension; `execute` composes the ext media type for both\n // `Content-Type` and `Accept` (the shared media-type-negotiation path).\n ext: [ATOMIC_EXT],\n }\n\n let doc\n try {\n doc = await execute(request, req)\n } catch (error) {\n if (error instanceof JsonApiError) {\n throw withRemappedAtomicPaths(error, descriptor, (opIndex) => typeByOp[opIndex])\n }\n throw error\n }\n\n const results = parseResults(doc as { ['atomic:results']?: unknown } | undefined, materialiseCtx)\n\n // Loose form: the callback returned nothing, or not an all-handles array -> the ordered results\n // array (backward-compatible). The all-handles guard keeps the runtime in step with the type\n // predicate (`Ops extends readonly AtomicHandle[]`): a stray non-handle element resolves to the\n // loose AtomicResult[] the type promises rather than a row of undefined.\n const isHandleTuple =\n Array.isArray(returned) &&\n returned.every((h) => h !== null && typeof h === 'object' && 'opIndex' in h)\n if (!isHandleTuple) {\n return results\n }\n // Typed-tuple form: resolve each RETURNED handle by its `opIndex` (a delete -> undefined), so\n // the tuple is sound regardless of the order/subset of handles returned. (A server returning no\n // `atomic:results` for an op yields undefined; the reference bundle always returns the created/\n // updated resource, so create/update results are typed present.)\n return returned.map((handle) => (handle.kind === 'delete' ? undefined : results[handle.opIndex]))\n}\n\n/**\n * Build the resource `data` for an `add`/`update` op from flat input via {@link toDocument}\n * (so attributes/relationships route identically to a standalone write), then carry the op's\n * identity onto the resource: a server `id` (a create's server-assigned id is omitted; an\n * update by id keeps it) or a same-batch `lid` (a create's generated lid, or an update/remove\n * targeting an earlier op's resource).\n */\nfunction atomicResourceData(\n descriptor: ApiDescriptor,\n input: { type: string; [field: string]: unknown },\n identity: AtomicIdentity,\n): Record<string, unknown> {\n // Strip the identity keys before routing — `lid` is not an attribute, and `id` (when present\n // as a flat field) is carried via the identity, not the body. The identity is applied below.\n const { id: _id, lid: _lid, ...fields } = input as Record<string, unknown>\n const { data } = toDocument(\n descriptor,\n input.type,\n fields,\n 'id' in identity ? { id: identity.id } : undefined,\n )\n const out: Record<string, unknown> = { ...data }\n if ('lid' in identity) {\n // A lid-targeted op rides a local id, never a server id.\n delete out['id']\n out['lid'] = identity.lid\n }\n return out\n}\n\n/**\n * Coerce a remove ref (an identifier, a materialised resource, or a same-batch `{type,lid}`\n * handle) to `{ type, identity }` — `identity` an `id` or a `lid`; throws if neither is present.\n */\nfunction toRef(ref: AtomicRef): { type: string; identity: AtomicIdentity } {\n if (!isObject(ref)) {\n throw new TypeError('atomic delete requires an identifier `{ type, id }` or `{ type, lid }`')\n }\n const { type, id, lid } = ref as { type?: unknown; id?: unknown; lid?: unknown }\n if (typeof type !== 'string') {\n throw new TypeError('atomic delete requires a `type`')\n }\n if (typeof id === 'string') {\n return { type, identity: { id } }\n }\n if (typeof lid === 'string') {\n return { type, identity: { lid } }\n }\n throw new TypeError('atomic delete requires an `id` or a `lid`')\n}\n\n/** Parse `{ \"atomic:results\": [...] }` positionally, materialising each result's `data`. An absent results array (an all-`204` batch) yields `[]`. */\nfunction parseResults(\n doc: { ['atomic:results']?: unknown } | undefined,\n ctx: MaterialiseContext,\n): AtomicResult[] {\n const results = doc?.['atomic:results']\n if (!Array.isArray(results)) {\n return []\n }\n return results.map((result) => {\n const entry = isObject(result) ? result : {}\n const data = entry['data'] === undefined ? undefined : materialise({ data: entry['data'] }, ctx)\n const out: AtomicResult = { data }\n if (isObject(entry['meta'])) {\n out.meta = entry['meta']\n }\n return out\n })\n}\n","/** A single outbound request, already serialised to strings. */\nexport interface TransportRequest {\n method: string\n url: string\n headers: Record<string, string>\n body?: string\n /**\n * Aborts the request when signalled. Optional: a transport that ignores it still works, the\n * caller just can't cancel an in-flight request through it.\n */\n signal?: AbortSignal\n}\n\n/** A single inbound response, body still a string for the runtime to parse. */\nexport interface TransportResponse {\n status: number\n headers: Record<string, string>\n body: string\n}\n\n/**\n * The transport seam: a tiny, `fetch`-shaped function (not a class), so any impl\n * (native `fetch`, undici, an axios adapter) drops in. Retries are deliberately out\n * of scope here — the transport (or the TanStack layer) owns them.\n */\nexport type JsonApiTransport = (req: TransportRequest) => Promise<TransportResponse>\n\n/** The default transport, backed by the global `fetch`. */\nexport const fetchTransport: JsonApiTransport = async (req) => {\n const init: RequestInit = { method: req.method, headers: req.headers }\n if (req.body !== undefined) {\n init.body = req.body\n }\n if (req.signal !== undefined) {\n init.signal = req.signal\n }\n\n const res = await fetch(req.url, init)\n\n const headers: Record<string, string> = {}\n res.headers.forEach((value, key) => {\n headers[key] = value\n })\n\n return { status: res.status, headers, body: await res.text() }\n}\n","import { type AtomicRecorder, runAtomic } from './atomic'\nimport type { ActionDescriptor, ActionScope, ApiDescriptor, AtomicDescriptor } from './descriptor'\nimport { JsonApiError } from './errors'\nimport { materialise, type MaterialiseContext, relatedPaginator } from './materialise'\nimport { resolvePaginator } from './pagination'\nimport { execute, type JsonApiContext, type JsonApiRequest, type ReadQuery } from './request'\nimport type {\n ActionsAccessor,\n Client,\n DefaultActionTypes,\n DefaultAttributes,\n DefaultWriteAttributes,\n RelationReadQuery,\n RelationshipAccessor,\n ResourceHandle,\n TypeAccessor,\n} from './result-types'\nimport {\n toDocument,\n toRelationshipDocument,\n withRemappedPaths,\n withRemappedRelationshipPaths,\n} from './serialize-write'\nimport { fetchTransport, type JsonApiTransport } from './transport'\nimport { resolveValidator, type ValidationOption } from './validate'\n\n/**\n * The `fetch` headers init type. `@types/node` declares the global `RequestInit` (used\n * by the transport) but not `HeadersInit`; we derive it from `RequestInit.headers` to\n * stay dep-free and consistent with the rest of the runtime's web-globals usage.\n */\nexport type HeadersInit = NonNullable<RequestInit['headers']>\n\n/** Options for {@link createClient}. */\nexport interface ClientOptions {\n /** Base URL the generated path templates are resolved against. */\n baseUrl: string\n /** Transport seam; defaults to the `fetch` adapter when omitted. */\n transport?: JsonApiTransport\n /** Per-request header provider (e.g. bearer auth); may be async. */\n headers?: () => HeadersInit | Promise<HeadersInit>\n /**\n * The server-level Atomic Operations capability (the codegen's `atomic` constant): `{ path }`\n * when the server exposes the atomic endpoint, else `null`/omitted. When present, the client\n * exposes `client.atomic(tx => …)`; otherwise calling it throws.\n */\n atomic?: AtomicDescriptor | null\n /**\n * The opt-in per-field validation seam (ADR 0004). Off by default — the client always runs\n * light structural guards (is this a JSON:API document? does each `data`/`included` member\n * carry `type`+`id`?) but otherwise trusts the wire. Supply either the schema-driven config\n * `{ schemas, validator }` — the codegen-emitted per-type `schemas` map plus a validation\n * engine adapter (each wire resource is validated against `schemas[type]`; a type with no\n * schema is skipped) — or a bare validator function that owns schema lookup itself. The\n * validation engine (e.g. ajv) is brought by the caller; the client never depends on one.\n */\n validate?: ValidationOption\n}\n\n/** The ambient runtime context every read shares: transport seam + descriptor + materialise glue. */\ninterface ClientContext {\n readonly request: JsonApiContext\n readonly materialise: MaterialiseContext\n readonly descriptor: ApiDescriptor\n}\n\n/** Substitute `{key}` placeholders in a path template (`/albums/{id}/{rel}` -> `/albums/1/tracks`). */\nconst fill = (template: string, vars: Record<string, string>): string =>\n template.replace(/\\{(\\w+)\\}/g, (_, key: string) => vars[key] ?? `{${key}}`)\n\n/**\n * Fill a type's path template, putting a relation's URI segment (its `uriField`, else its name)\n * in place of `{rel}`. `vars.rel` stays the relation's name everywhere else.\n */\nfunction fillPath(\n ctx: ClientContext,\n type: string,\n template: string,\n vars: Record<string, string>,\n): string {\n const rel = vars['rel']\n const uriField = rel === undefined ? undefined : ctx.descriptor[type]?.relations[rel]?.uriField\n return fill(template, uriField === undefined ? vars : { ...vars, rel: uriField })\n}\n\n/**\n * Run an operation: resolve the path template from the type's descriptor, fill its\n * placeholders, execute, and materialise the resulting document. A no-body / 204 response\n * materialises to `undefined`; an operation the type does not declare (no path template) throws\n * a local `Error` before any request. `linkage` marks a relationship-endpoint read (primary\n * `data` is pure resource-identifier linkage) so an attribute-less, relation-less member is\n * still materialised as an identifier.\n */\nasync function run(\n ctx: ClientContext,\n type: string,\n operation: string,\n vars: Record<string, string>,\n query: ReadQuery | undefined,\n linkage = false,\n relationForCount?: string,\n): Promise<unknown> {\n const template = ctx.descriptor[type]?.paths[operation]\n if (template === undefined) {\n throw new Error(`No \"${operation}\" path declared for type \"${type}\"`)\n }\n const req: JsonApiRequest = { method: 'GET', path: fillPath(ctx, type, template, vars) }\n if (query !== undefined) {\n const { signal, ...params } = query\n req.query = params\n if (signal !== undefined) {\n req.signal = signal\n }\n negotiateProfiles(ctx, type, params, req, relationForCount)\n }\n const doc = await execute(ctx.request, req)\n // Resolve the strategy that answers from the endpoint's paginator and the `page` sent, so an\n // EMPTY page still reports the real `$page.kind` (D6) and a menu read reports the arm it picked.\n // A related/relationship read uses the RELATION's paginator, which may diverge from the related\n // type's collection (D6a).\n const relation =\n (operation === 'fetchRelated' || operation === 'fetchRelationship') && vars['rel'] !== undefined\n ? ctx.descriptor[type]?.relations[vars['rel']]\n : undefined\n const paginator =\n operation === 'fetchMany'\n ? ctx.descriptor[type]?.paginator\n : relation !== undefined\n ? relatedPaginator(relation, ctx.materialise)\n : undefined\n const primaryType = operation === 'fetchMany' ? type : undefined\n const paginatorOverride =\n paginator === undefined ? undefined : resolvePaginator(paginator, query?.page)\n return doc === undefined\n ? undefined\n : materialise(doc, ctx.materialise, linkage, primaryType, paginatorOverride)\n}\n\n/**\n * Add any profiles a query's parameters require to the request's negotiated `profiles`. A\n * `withCount` read needs the type's Countable profile in `Accept` — else the server rejects it\n * (400) under strict query-param validation. The profile URI is read from the descriptor's\n * `countable` block (never hardcoded); absent when the type advertises no Countable profile.\n */\nfunction negotiateProfiles(\n ctx: ClientContext,\n type: string,\n query: ReadQuery,\n req: JsonApiRequest,\n relationForCount?: string,\n): void {\n if (query.withCount === undefined || query.withCount.length === 0) {\n return\n }\n // A related/relationship read negotiates the RELATION's Countable profile (its tokens differ\n // from the collection's); a top-level collection read uses the type's (D3).\n const profile =\n relationForCount !== undefined\n ? ctx.descriptor[type]?.relations[relationForCount]?.countable?.profile\n : ctx.descriptor[type]?.countable?.profile\n if (profile !== undefined) {\n req.profiles = [...(req.profiles ?? []), profile]\n }\n}\n\n/** A write opts object: an optional `include`/`fields` narrowing the materialised response. */\ninterface WriteOpts {\n include?: readonly string[]\n fields?: Record<string, readonly string[]>\n}\n\n/** Project a write's opts to the read-query families the response honours (`include`/`fields`). */\nfunction writeQuery(opts: WriteOpts | undefined): ReadQuery | undefined {\n if (opts === undefined) {\n return undefined\n }\n const query: ReadQuery = {}\n if (opts.include !== undefined) {\n query.include = [...opts.include]\n }\n if (opts.fields !== undefined) {\n const fields: Record<string, string[]> = {}\n for (const [k, v] of Object.entries(opts.fields)) {\n fields[k] = [...v]\n }\n query.fields = fields\n }\n return Object.keys(query).length > 0 ? query : undefined\n}\n\n/** A write's spec: how to address it, what to send, and how to shape the result + errors. */\ninterface WriteSpec {\n type: string\n operation: string\n vars: Record<string, string>\n method: string\n body: unknown\n query?: ReadQuery | undefined\n /** Materialise the primary `data` as identifier linkage (relationship endpoints) vs resources. */\n linkage?: boolean\n /** Remap a thrown error's pointers to flat input paths (resource-write vs relationship-write). */\n remap: (error: JsonApiError) => JsonApiError\n}\n\n/**\n * Drive a write: fill the path template, attach the body (and any `include`/`fields` query),\n * execute, and materialise the response (`undefined` for a `204`/no body — so create/update\n * return the resource and delete/`204` relationship mutations resolve `undefined`/`void`).\n * On a thrown {@link JsonApiError}, each error's flat `path` is populated (via `spec.remap`)\n * before it propagates, so a caller's `byPath()` keys are the flat input shape.\n */\nasync function runWrite(ctx: ClientContext, spec: WriteSpec): Promise<unknown> {\n const template = ctx.descriptor[spec.type]?.paths[spec.operation]\n if (template === undefined) {\n throw new Error(`No \"${spec.operation}\" path declared for type \"${spec.type}\"`)\n }\n const req: JsonApiRequest = {\n method: spec.method,\n path: fillPath(ctx, spec.type, template, spec.vars),\n body: spec.body,\n }\n if (spec.query !== undefined) {\n req.query = spec.query\n }\n let doc\n try {\n doc = await execute(ctx.request, req)\n } catch (error) {\n if (error instanceof JsonApiError) {\n throw spec.remap(error)\n }\n throw error\n }\n return doc === undefined ? undefined : materialise(doc, ctx.materialise, spec.linkage ?? false)\n}\n\n/**\n * Invoke a custom action over its declared HTTP method (`action.method`, default `POST`) at its\n * (filled) path, with a body shaped by the action's `input` mode:\n *\n * - `none` — no body;\n * - `document` — when the action names an `inputType`, FLAT input (the resource's attributes /\n * relationships) is built into the JSON:API envelope like `create`, and a `422`'s\n * `source.pointer`s are remapped to the flat paths; a bespoke command document (no `inputType`)\n * is sent verbatim;\n * - `raw` — the caller's payload, sent with the action's declared media type (e.g.\n * `application/octet-stream`), falling back to a wildcard when the spec declared none.\n *\n * The response is shaped by the action's `output`: `document` materialises into the resource /\n * collection view, `meta` returns the document's top-level `meta`, `none` resolves `undefined` (a\n * `204` / no body).\n */\nasync function runAction(\n ctx: ClientContext,\n action: ActionDescriptor,\n vars: Record<string, string>,\n input: unknown,\n): Promise<unknown> {\n const req: JsonApiRequest = { method: action.method ?? 'POST', path: fill(action.path, vars) }\n let remap: ((error: JsonApiError) => JsonApiError) | undefined\n if (action.input === 'document') {\n if (action.inputType !== undefined) {\n // Flat input: build the envelope like create, and remap 422 pointers to the flat paths.\n const inputType = action.inputType\n req.body = toDocument(ctx.descriptor, inputType, (input ?? {}) as Record<string, unknown>)\n remap = (error) => withRemappedPaths(error, ctx.descriptor, inputType)\n } else {\n // A bespoke command document (no resolvable input type): pass the caller's envelope through.\n req.body = input\n }\n } else if (action.input === 'raw') {\n req.body = input\n // Send the action's declared media type (e.g. application/octet-stream); fall back to a\n // wildcard only when the spec declared none.\n req.contentType = action.contentType ?? '*/*'\n req.raw = true\n }\n let doc: Awaited<ReturnType<typeof execute>>\n try {\n doc = await execute(ctx.request, req)\n } catch (error) {\n if (remap !== undefined && error instanceof JsonApiError) {\n throw remap(error)\n }\n throw error\n }\n if (action.output === 'none' || doc === undefined) {\n return undefined\n }\n if (action.output === 'meta') {\n return doc.meta\n }\n return materialise(doc, ctx.materialise)\n}\n\n/**\n * Build the typed `.actions` accessor at one scope: a plain object exposing exactly the\n * type's actions declared at `scope` (collection on the type accessor, resource on a handle),\n * each a function dispatching to {@link runAction} with the action descriptor + the scope's\n * path vars (resource scope supplies `{id}`). Names not declared at this scope are absent —\n * `client.albums.actions.reissue` (resource-scoped) is undefined; reach it via `.id(id)`.\n */\nfunction actionsAccessor(\n ctx: ClientContext,\n type: string,\n scope: ActionScope,\n vars: Record<string, string>,\n): ActionsAccessor<ApiDescriptor, unknown, unknown, string, ActionScope> {\n const actions = ctx.descriptor[type]?.actions ?? {}\n const out: Record<string, (input?: unknown) => Promise<unknown>> = {}\n for (const [name, action] of Object.entries(actions)) {\n if (action.scope === scope) {\n out[name] = (input?: unknown) => runAction(ctx, action, vars, input)\n }\n }\n return out as ActionsAccessor<ApiDescriptor, unknown, unknown, string, ActionScope>\n}\n\n/**\n * Build the relationship accessor for `client.<type>.id(id).<rel>`: reads\n * (`.get()` linkage / `.related()` collection) plus mutations — to-many `.add` (POST) /\n * `.remove` (DELETE) / `.replace` (PATCH) and to-one `.set` (PATCH). Every mutation posts\n * a `{ data: <linkage> }` body to `fetchRelationship` and materialises the response as\n * linkage (or `undefined`/`void` for a `204`). The cardinality is read off the descriptor\n * so the linkage is coerced correctly; the static types gate which verb is callable.\n */\nfunction relationshipAccessor(\n ctx: ClientContext,\n type: string,\n id: string,\n rel: string,\n): RelationshipAccessor<ApiDescriptor, unknown, string, never> {\n const vars = { id, rel }\n const relation = ctx.descriptor[type]?.relations[rel]\n const cardinality = relation?.cardinality ?? 'many'\n const hasPivot = relation?.pivot === true\n const mutate = (method: string, refs: unknown): Promise<unknown> =>\n runWrite(ctx, {\n type,\n operation: 'fetchRelationship',\n vars,\n method,\n body: toRelationshipDocument(refs, cardinality),\n linkage: true,\n remap: (error) => withRemappedRelationshipPaths(error, rel, hasPivot),\n })\n\n // Whether the relation advertises a mutation verb: the descriptor's `mutations` flags are\n // authoritative when present (mirrors the type-level `AdvertisesVerb`), so a verb the\n // bundle forbids (`cannotReplace` -> no PATCH) is absent at runtime, not just untyped. A\n // relation with no `mutations` block falls back to cardinality alone.\n const advertises = (verb: 'add' | 'remove' | 'replace' | 'set'): boolean => {\n const mutations = relation?.mutations\n return mutations === undefined ? true : mutations[verb] === true\n }\n\n // A read is present only when the relation exposes that endpoint (the descriptor's\n // `relationship`/`related` flags, from the bundle's `withoutRelationshipEndpoint()` /\n // `withoutRelatedEndpoint()`). A suppressed read is omitted (a JS caller gets \"not a function\"\n // rather than a 404); the static types gate it to `never` for a typed caller (D24).\n // A write-only relation is never rendered, so it has neither read.\n const accessor: Record<string, unknown> = {}\n const readable = relation?.writeOnly !== true\n if (readable && relation?.relationship !== false) {\n accessor['get'] = (query?: RelationReadQuery) =>\n run(ctx, type, 'fetchRelationship', vars, query as ReadQuery | undefined, true, rel)\n }\n if (readable && relation?.related !== false) {\n accessor['related'] = (query?: RelationReadQuery) =>\n run(ctx, type, 'fetchRelated', vars, query as ReadQuery | undefined, false, rel)\n }\n if (cardinality === 'many') {\n if (advertises('add')) {\n accessor['add'] = (refs: unknown) => mutate('POST', refs)\n }\n if (advertises('remove')) {\n accessor['remove'] = (refs: unknown) => mutate('DELETE', refs)\n }\n if (advertises('replace')) {\n accessor['replace'] = (refs: unknown) => mutate('PATCH', refs)\n }\n } else if (advertises('set')) {\n accessor['set'] = (ref: unknown) => mutate('PATCH', ref)\n }\n\n return accessor as unknown as RelationshipAccessor<ApiDescriptor, unknown, string, never>\n}\n\n/**\n * The reserved members on a {@link ResourceHandle} — names the Proxy resolves itself, so a\n * relation of the same name is shadowed and must route through `.rel(name)`. The codegen\n * imports this to warn at build time (keeping the runtime + the collision detector in sync).\n * `then` is reserved so a handle is never mistaken for a thenable.\n */\nexport const HANDLE_RESERVED: ReadonlySet<string> = new Set([\n 'type',\n 'id',\n 'get',\n 'update',\n 'delete',\n 'rel',\n 'actions',\n 'then',\n])\n\n/** Build the id-scoped resource handle (a Proxy resolving relation accessors by name). */\nfunction resourceHandle(\n ctx: ClientContext,\n type: string,\n id: string,\n): ResourceHandle<ApiDescriptor, unknown, unknown, string> {\n const relations = ctx.descriptor[type]?.relations ?? {}\n const base = {\n type,\n id,\n get: (query?: ReadQuery) => run(ctx, type, 'fetchOne', { id }, query),\n update: (patch: Record<string, unknown>, opts?: WriteOpts) =>\n runWrite(ctx, {\n type,\n operation: 'update',\n vars: { id },\n method: 'PATCH',\n body: toDocument(ctx.descriptor, type, patch, { id }),\n query: writeQuery(opts),\n remap: (error) => withRemappedPaths(error, ctx.descriptor, type),\n }),\n delete: () =>\n runWrite(ctx, {\n type,\n operation: 'delete',\n vars: { id },\n method: 'DELETE',\n body: undefined,\n remap: (error) => withRemappedPaths(error, ctx.descriptor, type),\n }) as never,\n rel: (name: string) => relationshipAccessor(ctx, type, id, name),\n actions: actionsAccessor(ctx, type, 'resource', { id }),\n }\n\n return new Proxy(base, {\n get(target, prop, receiver) {\n if (typeof prop === 'string' && !HANDLE_RESERVED.has(prop) && prop in relations) {\n return relationshipAccessor(ctx, type, id, prop)\n }\n return Reflect.get(target, prop, receiver)\n },\n }) as unknown as ResourceHandle<ApiDescriptor, unknown, unknown, string>\n}\n\n/** Build the collection-scoped accessor for one wire type (`client.albums`). */\nfunction typeAccessor(\n ctx: ClientContext,\n type: string,\n): TypeAccessor<ApiDescriptor, unknown, unknown, string> {\n return {\n list: (query?: ReadQuery) => run(ctx, type, 'fetchMany', {}, query) as Promise<never>,\n get: (id: string, query?: ReadQuery) =>\n run(ctx, type, 'fetchOne', { id }, query) as Promise<never>,\n create: (input: Record<string, unknown>, opts?: WriteOpts) =>\n runWrite(ctx, {\n type,\n operation: 'create',\n vars: {},\n method: 'POST',\n body: toDocument(ctx.descriptor, type, input),\n query: writeQuery(opts),\n remap: (error) => withRemappedPaths(error, ctx.descriptor, type),\n }) as Promise<never>,\n id: (id: string) => resourceHandle(ctx, type, id),\n actions: actionsAccessor(ctx, type, 'collection', {}),\n } as unknown as TypeAccessor<ApiDescriptor, unknown, unknown, string>\n}\n\n/**\n * Build a descriptor-driven read client. Generic at runtime (a Proxy over the descriptor),\n * not per-type codegen: `client.<type>` yields a {@link TypeAccessor}, `.id(id)` a handle,\n * and a handle's relation accessors drive the relationship/related endpoints. Navigation\n * (`$next()`/`$prev()`, related links) is wired through `materialise`'s `navigate` seam,\n * which resolves the server's link against `baseUrl`, executes it and re-materialises.\n */\nexport function createClient<\n D extends ApiDescriptor,\n A = DefaultAttributes<D>,\n W = DefaultWriteAttributes<D>,\n Act = DefaultActionTypes,\n E extends string = string,\n>(descriptor: D, options: ClientOptions): Client<D, A, W, Act, E> {\n const request: JsonApiContext = {\n baseUrl: options.baseUrl,\n transport: options.transport ?? fetchTransport,\n ...(options.headers !== undefined ? { headers: options.headers } : {}),\n }\n\n const validator = resolveValidator(options.validate)\n const ctx: ClientContext = {\n request,\n descriptor,\n materialise: {\n descriptor,\n navigate: async (url, linkage = false, paginator, signal) => {\n const doc = await execute(request, {\n method: 'GET',\n path: url,\n link: true,\n ...(signal !== undefined ? { signal } : {}),\n })\n return doc === undefined\n ? undefined\n : materialise(doc, ctx.materialise, linkage, undefined, paginator)\n },\n ...(validator !== undefined ? { validate: validator } : {}),\n },\n }\n\n const atomicDescriptor = options.atomic ?? null\n // `client.atomic(tx => …)`: post the recorded batch to the atomic endpoint. Built once and\n // reused; throws when the server declares no atomic capability (`atomic` was null/omitted).\n // Typed via the single conditional-return `Client['atomic']` signature (tuple-of-handles ->\n // positional results; void -> loose results); the runtime forwards the callback to `runAtomic`.\n const atomic = (build: (tx: AtomicRecorder<D, W>) => unknown): Promise<unknown> => {\n if (atomicDescriptor === null) {\n throw new Error('This API does not expose an Atomic Operations endpoint')\n }\n return runAtomic(\n request,\n ctx.materialise,\n descriptor,\n atomicDescriptor.path,\n build as (tx: AtomicRecorder<D, W>) => void,\n )\n }\n\n const accessors = new Map<string, TypeAccessor<ApiDescriptor, unknown, unknown, string>>()\n return new Proxy(Object.create(null) as object, {\n get(_target, prop) {\n if (prop === 'atomic') {\n return atomic\n }\n if (typeof prop !== 'string' || !(prop in descriptor)) {\n return undefined\n }\n let accessor = accessors.get(prop)\n if (accessor === undefined) {\n accessor = typeAccessor(ctx, prop)\n accessors.set(prop, accessor)\n }\n return accessor\n },\n has(_target, prop) {\n return prop === 'atomic' || (typeof prop === 'string' && prop in descriptor)\n },\n }) as Client<D, A, W, Act, E>\n}\n"],"mappings":"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"}
|