@haddowg/json-api-codegen 1.8.1 → 1.9.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/dist/cli.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { r as CodegenConfig } from "./index-COQQrn7a.mjs";
1
+ import { r as CodegenConfig } from "./index-k_oATv7Q.mjs";
2
2
 
3
3
  //#region src/cli.d.ts
4
4
  declare const USAGE = "Usage: json-api-codegen --input <url|file> --output <file> [--server <name>] [--schemas <url|file>] [--examples] [--check]";
package/dist/cli.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { i as generateFiles, t as check } from "./src-AWTGjMiX.mjs";
2
+ import { i as generateFiles, t as check } from "./src-py1RBWZr.mjs";
3
3
  import { realpathSync } from "node:fs";
4
4
  import { fileURLToPath } from "node:url";
5
5
  //#region src/cli.ts
@@ -295,7 +295,7 @@ declare function buildExamples(doc: OpenApiDocument, descriptor: ApiDescriptor):
295
295
  /** Below this the document predates the structures this codegen reads. */
296
296
  declare const MINIMUM_CONTRACT = 1;
297
297
  /** Above this the document carries structures this codegen does not read. */
298
- declare const MAXIMUM_CONTRACT = 13;
298
+ declare const MAXIMUM_CONTRACT = 14;
299
299
  /**
300
300
  * Check the document's `info.x-generator.contract` against the range this codegen reads. A
301
301
  * document older than the range throws; a newer one returns a warning, because its new
@@ -346,6 +346,8 @@ declare class Emitter {
346
346
  private readonly rawBodyAliases;
347
347
  /** JSDoc for entries of the `resourceMap` literal, keyed by their path joined with `/`. */
348
348
  private readonly literalDocs;
349
+ /** Wire type -> the links it declares, for the types that declare any. */
350
+ private readonly declaredLinks;
349
351
  /** Provenance of the source document, stamped into the generated header for drift tracking. */
350
352
  private readonly provenance;
351
353
  private readonly doc;
@@ -360,6 +362,22 @@ declare class Emitter {
360
362
  emit(): string;
361
363
  private header;
362
364
  private imports;
365
+ /**
366
+ * The links interface of a type that declares links, e.g. `ImagesLinks`: each declared link an
367
+ * optional member, since the server leaves a link out when its href resolves to `null`. It
368
+ * extends the loose `ResourceLinks`, so `self` and any link the type doesn't declare stay
369
+ * reachable as before.
370
+ */
371
+ private linksInterface;
372
+ /**
373
+ * A declared link object as a multi-line type: `href`, each static member as its `const`, and a
374
+ * `meta` object whose members are typed as attributes are, each with its own TSDoc.
375
+ */
376
+ private linkObjectType;
377
+ /** An object type with one line, and its TSDoc, per property. */
378
+ private documentedObject;
379
+ /** A static link member's `const` as a literal type, else its schema's type. */
380
+ private constOrType;
363
381
  /** Per-type attribute interface, e.g. `export interface AlbumsAttributes { ... }`. */
364
382
  private attributeInterface;
365
383
  /**
@@ -458,6 +476,13 @@ declare class Emitter {
458
476
  * argument to the runtime factory).
459
477
  */
460
478
  private attributesMap;
479
+ /**
480
+ * Emit the `Links` interface mapping each type that declares links to its links interface. The
481
+ * runtime threads it beside the `Attributes` map, so a read result's `$links` carries the
482
+ * declared links. A type absent from it keeps the loose `ResourceLinks`. Passed as the sixth
483
+ * type argument to the runtime factory.
484
+ */
485
+ private linksMap;
461
486
  /**
462
487
  * Emit the `WriteAttributes` interface mapping each writable wire type to its
463
488
  * `{ create; update }` attribute pair. A read-only type (no create/update component)
@@ -658,4 +683,4 @@ declare function generateFiles(config: CodegenConfig): Promise<string[]>;
658
683
  declare function check(config: CodegenConfig): Promise<CheckResult>;
659
684
  //#endregion
660
685
  export { operationExamples as A, PathItemObject as B, VerbCollision as C, MINIMUM_CONTRACT as D, MAXIMUM_CONTRACT as E, InfoObject as F, ResponseObject as H, MediaTypeObject as I, OpenApiDocument as L, buildAtomic as M, buildDescriptor as N, checkContract as O, HttpMethod as P, OperationObject as R, Emitter as S, emit as T, SchemaObject as U, RequestBodyObject as V, SchemaOrBool as W, emitExamples as _, examplesOutputPath as a, emitErrorCodes as b, schemasOutputPath as c, hashJson as d, provenanceLines as f, readSchemas as g, readDocument as h, check as i, DescriptorBuilder as j, buildExamples as k, Provenance as l, SchemaBundle as m, CheckResult as n, generate as o, emitSchemas as p, CodegenConfig as r, generateFiles as s, ArtifactDrift as t, deriveProvenance as u, DocumentedErrorCode as v, detectVerbCollisions as w, readErrorCatalogue as x, ErrorCatalogue as y, ParameterObject as z };
661
- //# sourceMappingURL=index-COQQrn7a.d.mts.map
686
+ //# sourceMappingURL=index-k_oATv7Q.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-k_oATv7Q.d.mts","names":[],"sources":["../src/openapi.ts","../src/build-descriptor.ts","../src/build-examples.ts","../src/contract.ts","../src/emit.ts","../src/error-codes.ts","../src/emit-examples.ts","../src/reader.ts","../src/emit-schemas.ts","../src/provenance.ts","../src/index.ts"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { A as operationExamples, B as PathItemObject, C as VerbCollision, D as MINIMUM_CONTRACT, E as MAXIMUM_CONTRACT, F as InfoObject, H as ResponseObject, I as MediaTypeObject, L as OpenApiDocument, M as buildAtomic, N as buildDescriptor, O as checkContract, P as HttpMethod, R as OperationObject, S as Emitter, T as emit, U as SchemaObject, V as RequestBodyObject, W as SchemaOrBool, _ as emitExamples, a as examplesOutputPath, b as emitErrorCodes, c as schemasOutputPath, d as hashJson, f as provenanceLines, g as readSchemas, h as readDocument, i as check, j as DescriptorBuilder, k as buildExamples, l as Provenance, m as SchemaBundle, n as CheckResult, o as generate, p as emitSchemas, r as CodegenConfig, s as generateFiles, t as ArtifactDrift, u as deriveProvenance, v as DocumentedErrorCode, w as detectVerbCollisions, x as readErrorCatalogue, y as ErrorCatalogue, z as ParameterObject } from "./index-COQQrn7a.mjs";
1
+ import { A as operationExamples, B as PathItemObject, C as VerbCollision, D as MINIMUM_CONTRACT, E as MAXIMUM_CONTRACT, F as InfoObject, H as ResponseObject, I as MediaTypeObject, L as OpenApiDocument, M as buildAtomic, N as buildDescriptor, O as checkContract, P as HttpMethod, R as OperationObject, S as Emitter, T as emit, U as SchemaObject, V as RequestBodyObject, W as SchemaOrBool, _ as emitExamples, a as examplesOutputPath, b as emitErrorCodes, c as schemasOutputPath, d as hashJson, f as provenanceLines, g as readSchemas, h as readDocument, i as check, j as DescriptorBuilder, k as buildExamples, l as Provenance, m as SchemaBundle, n as CheckResult, o as generate, p as emitSchemas, r as CodegenConfig, s as generateFiles, t as ArtifactDrift, u as deriveProvenance, v as DocumentedErrorCode, w as detectVerbCollisions, x as readErrorCatalogue, y as ErrorCatalogue, z as ParameterObject } from "./index-k_oATv7Q.mjs";
2
2
  export { ArtifactDrift, CheckResult, CodegenConfig, DescriptorBuilder, type DocumentedErrorCode, Emitter, type ErrorCatalogue, type HttpMethod, type InfoObject, MAXIMUM_CONTRACT, MINIMUM_CONTRACT, type MediaTypeObject, type OpenApiDocument, type OperationObject, type ParameterObject, type PathItemObject, type Provenance, type RequestBodyObject, type ResponseObject, type SchemaBundle, type SchemaObject, type SchemaOrBool, type VerbCollision, buildAtomic, buildDescriptor, buildExamples, check, checkContract, deriveProvenance, detectVerbCollisions, emit, emitErrorCodes, emitExamples, emitSchemas, examplesOutputPath, generate, generateFiles, hashJson, operationExamples, provenanceLines, readDocument, readErrorCatalogue, readSchemas, schemasOutputPath };
package/dist/index.mjs CHANGED
@@ -1,2 +1,2 @@
1
- import { C as DescriptorBuilder, S as operationExamples, T as buildDescriptor, _ as readErrorCatalogue, a as schemasOutputPath, b as checkContract, c as emitSchemas, d as detectVerbCollisions, f as emit, g as emitErrorCodes, h as provenanceLines, i as generateFiles, l as emitExamples, m as hashJson, n as examplesOutputPath, o as readDocument, p as deriveProvenance, r as generate, s as readSchemas, t as check, u as Emitter, v as MAXIMUM_CONTRACT, w as buildAtomic, x as buildExamples, y as MINIMUM_CONTRACT } from "./src-AWTGjMiX.mjs";
1
+ import { C as DescriptorBuilder, S as operationExamples, T as buildDescriptor, _ as readErrorCatalogue, a as schemasOutputPath, b as checkContract, c as emitSchemas, d as detectVerbCollisions, f as emit, g as emitErrorCodes, h as provenanceLines, i as generateFiles, l as emitExamples, m as hashJson, n as examplesOutputPath, o as readDocument, p as deriveProvenance, r as generate, s as readSchemas, t as check, u as Emitter, v as MAXIMUM_CONTRACT, w as buildAtomic, x as buildExamples, y as MINIMUM_CONTRACT } from "./src-py1RBWZr.mjs";
2
2
  export { DescriptorBuilder, Emitter, MAXIMUM_CONTRACT, MINIMUM_CONTRACT, buildAtomic, buildDescriptor, buildExamples, check, checkContract, deriveProvenance, detectVerbCollisions, emit, emitErrorCodes, emitExamples, emitSchemas, examplesOutputPath, generate, generateFiles, hashJson, operationExamples, provenanceLines, readDocument, readErrorCatalogue, readSchemas, schemasOutputPath };
@@ -74,7 +74,7 @@ function sortRecord(record) {
74
74
  function refName$1(ref) {
75
75
  return ref.startsWith(REF_PREFIX$2) ? ref.slice(21) : void 0;
76
76
  }
77
- function isSchema$2(value) {
77
+ function isSchema$3(value) {
78
78
  return typeof value === "object" && value !== null;
79
79
  }
80
80
  /** The operationId prefixes of a relation's related read and its relationship endpoint. */
@@ -164,20 +164,20 @@ var DescriptorBuilder = class {
164
164
  resourceType(name, schema) {
165
165
  if (!name.endsWith("Resource") || name.endsWith("ResourceIdentifier")) return;
166
166
  const constValue = schema.properties?.["type"];
167
- if (isSchema$2(constValue) && typeof constValue.const === "string") return constValue.const;
167
+ if (isSchema$3(constValue) && typeof constValue.const === "string") return constValue.const;
168
168
  }
169
169
  /** Follow a `$ref` to its identifier/resource schema and read `properties.type.const`. */
170
170
  resolveConst(ref) {
171
171
  const target = refName$1(ref);
172
172
  if (target === void 0) return;
173
173
  const typeProp = this.schemas[target]?.properties?.["type"];
174
- return isSchema$2(typeProp) && typeof typeProp.const === "string" ? typeProp.const : void 0;
174
+ return isSchema$3(typeProp) && typeof typeProp.const === "string" ? typeProp.const : void 0;
175
175
  }
176
176
  attributes(base) {
177
177
  const props = this.schemas[`${base}Attributes`]?.properties;
178
178
  if (props === void 0) return {};
179
179
  const out = {};
180
- for (const [name, prop] of Object.entries(props)) if (isSchema$2(prop)) out[name] = formatHint(prop);
180
+ for (const [name, prop] of Object.entries(props)) if (isSchema$3(prop)) out[name] = formatHint(prop);
181
181
  return sortRecord(out);
182
182
  }
183
183
  /**
@@ -188,7 +188,7 @@ var DescriptorBuilder = class {
188
188
  * request pivot off the write bodies.
189
189
  */
190
190
  relations(wireType, base, resource, collection) {
191
- const relProps = isSchema$2(resource.properties?.["relationships"]) ? resource.properties["relationships"].properties ?? {} : {};
191
+ const relProps = isSchema$3(resource.properties?.["relationships"]) ? resource.properties["relationships"].properties ?? {} : {};
192
192
  const writes = this.writeRelationships(base);
193
193
  const endpoints = this.relationEndpoints(wireType);
194
194
  const out = {};
@@ -210,7 +210,7 @@ var DescriptorBuilder = class {
210
210
  out[name] = collection === void 0 ? descriptor : this.withRelationExposure(descriptor, collection, name, endpoints.get(name));
211
211
  };
212
212
  for (const [name, prop] of Object.entries(relProps)) {
213
- if (!isSchema$2(prop) || typeof prop.$ref !== "string" || writes.get(name)?.writeOnly) continue;
213
+ if (!isSchema$3(prop) || typeof prop.$ref !== "string" || writes.get(name)?.writeOnly) continue;
214
214
  add(name, this.schemas[refName$1(prop.$ref) ?? ""], false);
215
215
  }
216
216
  for (const [name, write] of writes) if (write.writeOnly || !(name in relProps)) add(name, this.schemas[refName$1(write.ref) ?? ""], true);
@@ -224,10 +224,10 @@ var DescriptorBuilder = class {
224
224
  const out = /* @__PURE__ */ new Map();
225
225
  for (const request of [`${base}CreateRequest`, `${base}UpdateRequest`]) {
226
226
  const data = this.schemas[request]?.properties?.["data"];
227
- const relationships = isSchema$2(data) ? data.properties?.["relationships"] : void 0;
228
- const props = isSchema$2(relationships) ? relationships.properties ?? {} : {};
227
+ const relationships = isSchema$3(data) ? data.properties?.["relationships"] : void 0;
228
+ const props = isSchema$3(relationships) ? relationships.properties ?? {} : {};
229
229
  for (const [name, prop] of Object.entries(props)) {
230
- if (!isSchema$2(prop) || typeof prop.$ref !== "string") continue;
230
+ if (!isSchema$3(prop) || typeof prop.$ref !== "string") continue;
231
231
  const writeOnly = prop["writeOnly"] === true || out.get(name)?.writeOnly === true;
232
232
  out.set(name, {
233
233
  ref: out.get(name)?.ref ?? prop.$ref,
@@ -439,8 +439,8 @@ var DescriptorBuilder = class {
439
439
  if (typeof ref !== "string") return;
440
440
  const component = refName$1(ref);
441
441
  const data = component === void 0 ? void 0 : this.schemas[component]?.properties?.["data"];
442
- if (!isSchema$2(data)) return;
443
- if (isSchema$2(data.items)) {
442
+ if (!isSchema$3(data)) return;
443
+ if (isSchema$3(data.items)) {
444
444
  const itemRef = data.items.$ref;
445
445
  const many = typeof itemRef === "string" ? this.resolveConst(itemRef) : void 0;
446
446
  return many === void 0 ? void 0 : {
@@ -458,7 +458,7 @@ var DescriptorBuilder = class {
458
458
  dataConst(data) {
459
459
  if (typeof data.$ref === "string") return this.resolveConst(data.$ref);
460
460
  const typeProp = data.properties?.["type"];
461
- return isSchema$2(typeProp) && typeof typeProp.const === "string" ? typeProp.const : void 0;
461
+ return isSchema$3(typeProp) && typeof typeProp.const === "string" ? typeProp.const : void 0;
462
462
  }
463
463
  /** The `$ref` of the first 2xx JSON:API response body on an operation (the output document). */
464
464
  firstOkJsonApiRef(op) {
@@ -471,7 +471,7 @@ var DescriptorBuilder = class {
471
471
  /** Resolve a relationship component's `data` into a {@link RelationDescriptor}. */
472
472
  relationFromComponent(component) {
473
473
  const data = component.properties?.["data"];
474
- if (!isSchema$2(data)) return;
474
+ if (!isSchema$3(data)) return;
475
475
  if (data.type === "array") {
476
476
  const items = data.items;
477
477
  if (items === void 0) return;
@@ -550,7 +550,7 @@ var DescriptorBuilder = class {
550
550
  const fields = {};
551
551
  const nullable = [];
552
552
  for (const [name, prop] of Object.entries(schema?.properties ?? {})) {
553
- if (!isSchema$2(prop) || prop[side === "read" ? "writeOnly" : "readOnly"] === true) continue;
553
+ if (!isSchema$3(prop) || prop[side === "read" ? "writeOnly" : "readOnly"] === true) continue;
554
554
  fields[name] = formatHint(prop);
555
555
  if (isNullable(prop)) nullable.push(name);
556
556
  }
@@ -580,7 +580,7 @@ var DescriptorBuilder = class {
580
580
  }
581
581
  clientId(base) {
582
582
  const create = this.schemas[`${base}CreateRequest`];
583
- const data = isSchema$2(create?.properties?.["data"]) ? create.properties["data"] : void 0;
583
+ const data = isSchema$3(create?.properties?.["data"]) ? create.properties["data"] : void 0;
584
584
  if (data === void 0) return "forbidden";
585
585
  if (data.properties?.["id"] === false) return "forbidden";
586
586
  return data.required?.includes("id") ? "required" : "optional";
@@ -746,8 +746,8 @@ var DescriptorBuilder = class {
746
746
  function pivotNodeFromAllOf(allOf) {
747
747
  for (const entry of allOf) {
748
748
  const meta = entry.properties?.["meta"];
749
- const pivot = isSchema$2(meta) ? meta.properties?.["pivot"] : void 0;
750
- if (isSchema$2(pivot)) return pivot;
749
+ const pivot = isSchema$3(meta) ? meta.properties?.["pivot"] : void 0;
750
+ if (isSchema$3(pivot)) return pivot;
751
751
  }
752
752
  }
753
753
  /**
@@ -756,7 +756,7 @@ function pivotNodeFromAllOf(allOf) {
756
756
  */
757
757
  function pivotNodeOf(component) {
758
758
  const data = component.properties?.["data"];
759
- if (!isSchema$2(data)) return;
759
+ if (!isSchema$3(data)) return;
760
760
  if (Array.isArray(data.items?.allOf)) return pivotNodeFromAllOf(data.items.allOf);
761
761
  for (const entry of data.anyOf ?? []) if (Array.isArray(entry.allOf)) return pivotNodeFromAllOf(entry.allOf);
762
762
  }
@@ -1044,7 +1044,7 @@ function buildExamples(doc, descriptor) {
1044
1044
  /** Below this the document predates the structures this codegen reads. */
1045
1045
  const MINIMUM_CONTRACT = 1;
1046
1046
  /** Above this the document carries structures this codegen does not read. */
1047
- const MAXIMUM_CONTRACT = 13;
1047
+ const MAXIMUM_CONTRACT = 14;
1048
1048
  /**
1049
1049
  * Check the document's `info.x-generator.contract` against the range this codegen reads. A
1050
1050
  * document older than the range throws; a newer one returns a warning, because its new
@@ -1054,8 +1054,38 @@ const MAXIMUM_CONTRACT = 13;
1054
1054
  function checkContract(doc) {
1055
1055
  const contract = doc.info?.["x-generator"]?.contract;
1056
1056
  if (typeof contract !== "number") return;
1057
- if (contract < 1) throw new Error(`the document declares contract ${contract}, which predates what this codegen reads (it supports 1 to 13). Use an older codegen release, or regenerate the document from a newer server.`);
1058
- if (contract > 13) return `the document declares contract ${contract} and this codegen reads up to 13. It describes capabilities that were not generated; upgrade the codegen to pick them up.`;
1057
+ if (contract < 1) throw new Error(`the document declares contract ${contract}, which predates what this codegen reads (it supports 1 to 14). Use an older codegen release, or regenerate the document from a newer server.`);
1058
+ if (contract > 14) return `the document declares contract ${contract} and this codegen reads up to 14. It describes capabilities that were not generated; upgrade the codegen to pick them up.`;
1059
+ }
1060
+ //#endregion
1061
+ //#region src/declared-links.ts
1062
+ const LINK_OBJECT_REF = "#/components/schemas/LinkObject";
1063
+ function isSchema$2(value) {
1064
+ return typeof value === "object" && value !== null;
1065
+ }
1066
+ /**
1067
+ * The declared links on a `<Type>ResourceLinks` component, in document order. `self` is never
1068
+ * declared, and a link the server builds itself, such as an action's link, points at
1069
+ * `RenderedLink`, so neither is read. A server that renders its links as strings points those at
1070
+ * `LinkObject` instead, so they read as bare links, which is the shape they render in. Expects
1071
+ * the document's single-entry `allOf` wrappers already read as their `$ref`.
1072
+ */
1073
+ function readDeclaredLinks(component) {
1074
+ const out = [];
1075
+ for (const [name, schema] of Object.entries(component?.properties ?? {})) {
1076
+ if (name === "self" || !isSchema$2(schema)) continue;
1077
+ if (schema.$ref === LINK_OBJECT_REF) out.push({
1078
+ name,
1079
+ kind: "bare",
1080
+ schema
1081
+ });
1082
+ else if (schema.$ref === void 0 && isSchema$2(schema.properties?.["href"])) out.push({
1083
+ name,
1084
+ kind: "object",
1085
+ schema
1086
+ });
1087
+ }
1088
+ return out;
1059
1089
  }
1060
1090
  //#endregion
1061
1091
  //#region src/source.ts
@@ -1397,7 +1427,9 @@ const MODULE_NAMES = [
1397
1427
  "ErrorCodeByStatus",
1398
1428
  "ErrorCodeGuard",
1399
1429
  "ErrorCodes",
1430
+ "Links",
1400
1431
  "Record",
1432
+ "ResourceLinks",
1401
1433
  "ResourceMap",
1402
1434
  "WriteAttributes"
1403
1435
  ];
@@ -1564,6 +1596,8 @@ var Emitter = class {
1564
1596
  rawBodyAliases = /* @__PURE__ */ new Map();
1565
1597
  /** JSDoc for entries of the `resourceMap` literal, keyed by their path joined with `/`. */
1566
1598
  literalDocs = /* @__PURE__ */ new Map();
1599
+ /** Wire type -> the links it declares, for the types that declare any. */
1600
+ declaredLinks = /* @__PURE__ */ new Map();
1567
1601
  /** Provenance of the source document, stamped into the generated header for drift tracking. */
1568
1602
  provenance;
1569
1603
  doc;
@@ -1575,12 +1609,18 @@ var Emitter = class {
1575
1609
  this.baseByType = this.indexBases();
1576
1610
  this.provenance = deriveProvenance(source);
1577
1611
  this.takenNames = new Set(MODULE_NAMES);
1612
+ for (const type of Object.keys(descriptor)) {
1613
+ const base = this.baseByType.get(type);
1614
+ const links = base === void 0 ? [] : readDeclaredLinks(this.schemas[`${base}ResourceLinks`]);
1615
+ if (links.length > 0) this.declaredLinks.set(type, links);
1616
+ }
1578
1617
  for (const type of Object.keys(descriptor)) {
1579
1618
  const stem = typeStem(type);
1580
1619
  for (const suffix of [
1581
1620
  "Attributes",
1582
1621
  "CreateAttributes",
1583
- "UpdateAttributes"
1622
+ "UpdateAttributes",
1623
+ "Links"
1584
1624
  ]) this.takenNames.add(`${stem}${suffix}`);
1585
1625
  for (const [name, action] of Object.entries(descriptor[type]?.actions ?? {})) {
1586
1626
  this.takenNames.add(`${stem}${pascalCase(name)}Input`);
@@ -1629,6 +1669,7 @@ var Emitter = class {
1629
1669
  emit() {
1630
1670
  const interfaces = Object.keys(this.descriptor).map((type) => this.attributeInterface(type));
1631
1671
  const writeInterfaces = Object.keys(this.descriptor).flatMap((type) => this.writeAttributeInterfaces(type));
1672
+ const linkInterfaces = [...this.declaredLinks].map(([type, links]) => this.linksInterface(type, links));
1632
1673
  const actionAliases = this.actionTypeAliases();
1633
1674
  const enums = this.enumAliases();
1634
1675
  return `${[
@@ -1637,9 +1678,11 @@ var Emitter = class {
1637
1678
  ...enums ? [enums] : [],
1638
1679
  ...interfaces,
1639
1680
  ...writeInterfaces,
1681
+ ...linkInterfaces,
1640
1682
  ...actionAliases,
1641
1683
  this.attributesMap(),
1642
1684
  this.writeAttributesMap(),
1685
+ this.linksMap(),
1643
1686
  this.actionTypesMap(),
1644
1687
  this.resourceMap(),
1645
1688
  "export type ResourceMap = typeof resourceMap",
@@ -1667,9 +1710,51 @@ var Emitter = class {
1667
1710
  " createClient as createClientRuntime,",
1668
1711
  " type ErrorCodeGuard,",
1669
1712
  " hasErrorCode as hasErrorCodeRuntime,",
1713
+ ...this.declaredLinks.size > 0 ? [" type ResourceLinks,"] : [],
1670
1714
  "} from '@haddowg/json-api-client'"
1671
1715
  ].join("\n");
1672
1716
  }
1717
+ /**
1718
+ * The links interface of a type that declares links, e.g. `ImagesLinks`: each declared link an
1719
+ * optional member, since the server leaves a link out when its href resolves to `null`. It
1720
+ * extends the loose `ResourceLinks`, so `self` and any link the type doesn't declare stay
1721
+ * reachable as before.
1722
+ */
1723
+ linksInterface(type, links) {
1724
+ const lines = [];
1725
+ for (const link of links) {
1726
+ const doc = jsDoc(memberDoc(link.schema), " ");
1727
+ if (doc !== void 0) lines.push(doc);
1728
+ const value = link.kind === "bare" ? "string" : this.linkObjectType(link.schema, " ");
1729
+ lines.push(` ${propertyKey(link.name)}?: ${value}`);
1730
+ }
1731
+ return `export interface ${typeStem(type)}Links extends ResourceLinks {\n${lines.join("\n")}\n}`;
1732
+ }
1733
+ /**
1734
+ * A declared link object as a multi-line type: `href`, each static member as its `const`, and a
1735
+ * `meta` object whose members are typed as attributes are, each with its own TSDoc.
1736
+ */
1737
+ linkObjectType(schema, indent) {
1738
+ return this.documentedObject(schema, indent, (name, member) => name === "meta" ? this.documentedObject(member, `${indent} `, (_, field) => this.tsType(field)) : this.constOrType(member));
1739
+ }
1740
+ /** An object type with one line, and its TSDoc, per property. */
1741
+ documentedObject(schema, indent, type) {
1742
+ const required = new Set(schema.required ?? []);
1743
+ const inner = `${indent} `;
1744
+ const lines = [];
1745
+ for (const [name, member] of Object.entries(schema.properties ?? {})) {
1746
+ if (!isSchema(member)) continue;
1747
+ const doc = jsDoc(memberDoc(member), inner);
1748
+ if (doc !== void 0) lines.push(doc);
1749
+ lines.push(`${inner}${propertyKey(name)}${required.has(name) ? "" : "?"}: ${type(name, member)}`);
1750
+ }
1751
+ return lines.length === 0 ? "Record<string, unknown>" : `{\n${lines.join("\n")}\n${indent}}`;
1752
+ }
1753
+ /** A static link member's `const` as a literal type, else its schema's type. */
1754
+ constOrType(schema) {
1755
+ const value = schema["const"];
1756
+ return typeof value === "string" ? stringLiteral(value) : this.tsType(schema);
1757
+ }
1673
1758
  /** Per-type attribute interface, e.g. `export interface AlbumsAttributes { ... }`. */
1674
1759
  attributeInterface(type) {
1675
1760
  const base = this.baseByType.get(type);
@@ -2067,6 +2152,17 @@ var Emitter = class {
2067
2152
  return `export interface Attributes {\n${Object.keys(this.descriptor).map((type) => ` ${propertyKey(type)}: ${typeStem(type)}Attributes`).join("\n")}\n}`;
2068
2153
  }
2069
2154
  /**
2155
+ * Emit the `Links` interface mapping each type that declares links to its links interface. The
2156
+ * runtime threads it beside the `Attributes` map, so a read result's `$links` carries the
2157
+ * declared links. A type absent from it keeps the loose `ResourceLinks`. Passed as the sixth
2158
+ * type argument to the runtime factory.
2159
+ */
2160
+ linksMap() {
2161
+ const lines = [...this.declaredLinks.keys()].map((type) => ` ${propertyKey(type)}: ${typeStem(type)}Links`);
2162
+ if (lines.length === 0) return "export interface Links {}";
2163
+ return `export interface Links {\n${lines.join("\n")}\n}`;
2164
+ }
2165
+ /**
2070
2166
  * Emit the `WriteAttributes` interface mapping each writable wire type to its
2071
2167
  * `{ create; update }` attribute pair. A read-only type (no create/update component)
2072
2168
  * contributes no entry, so the bound client only exposes writes where the API allows
@@ -2121,7 +2217,7 @@ var Emitter = class {
2121
2217
  " * so bindings that only see the client (the Angular refs) type its errors.",
2122
2218
  " */",
2123
2219
  "export const createClient = (options: ClientOptions) =>",
2124
- " createClientRuntime<typeof resourceMap, Attributes, WriteAttributes, ActionTypes, ErrorCode>(",
2220
+ " createClientRuntime<typeof resourceMap, Attributes, WriteAttributes, ActionTypes, ErrorCode, Links>(",
2125
2221
  " resourceMap,",
2126
2222
  " {",
2127
2223
  " atomic,",
@@ -2364,4 +2460,4 @@ async function check(config) {
2364
2460
  //#endregion
2365
2461
  export { DescriptorBuilder as C, operationExamples as S, buildDescriptor as T, readErrorCatalogue as _, schemasOutputPath as a, checkContract as b, emitSchemas as c, detectVerbCollisions as d, emit as f, emitErrorCodes as g, provenanceLines as h, generateFiles as i, emitExamples as l, hashJson as m, examplesOutputPath as n, readDocument as o, deriveProvenance as p, generate as r, readSchemas as s, check as t, Emitter as u, MAXIMUM_CONTRACT as v, buildAtomic as w, buildExamples as x, MINIMUM_CONTRACT as y };
2366
2462
 
2367
- //# sourceMappingURL=src-AWTGjMiX.mjs.map
2463
+ //# sourceMappingURL=src-py1RBWZr.mjs.map