yayson 4.0.0 → 4.2.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.
Files changed (39) hide show
  1. package/README.md +272 -82
  2. package/build/legacy.cjs +88 -35
  3. package/build/legacy.d.cts +44 -8
  4. package/build/legacy.d.cts.map +1 -1
  5. package/build/legacy.d.mts +44 -8
  6. package/build/legacy.d.mts.map +1 -1
  7. package/build/legacy.mjs +88 -35
  8. package/build/legacy.mjs.map +1 -1
  9. package/build/{symbols-nFs99aEX.cjs → symbols-B--FS78o.cjs} +3 -0
  10. package/build/{symbols-DSjKJ8vh.mjs → symbols-BfU4k1el.mjs} +4 -1
  11. package/build/symbols-BfU4k1el.mjs.map +1 -0
  12. package/build/{types-DbMIe8E2.d.cts → types-KZiF6x7A.d.mts} +21 -2
  13. package/build/types-KZiF6x7A.d.mts.map +1 -0
  14. package/build/{types-BsfPiPEw.d.mts → types-iC38_iCI.d.cts} +21 -2
  15. package/build/types-iC38_iCI.d.cts.map +1 -0
  16. package/build/utils.cjs +1 -1
  17. package/build/utils.d.cts +1 -1
  18. package/build/utils.d.mts +1 -1
  19. package/build/utils.mjs +1 -1
  20. package/build/{yayson-BKEyEcGk.d.cts → yayson-BvwMr4Ad.d.mts} +18 -4
  21. package/build/yayson-BvwMr4Ad.d.mts.map +1 -0
  22. package/build/{yayson-CUfrxK9d.cjs → yayson-C4P8J4sn.cjs} +127 -51
  23. package/build/{yayson-BJv2Z0Z6.mjs → yayson-DIQ_olLX.mjs} +128 -52
  24. package/build/yayson-DIQ_olLX.mjs.map +1 -0
  25. package/build/{yayson-CRbukIqr.d.mts → yayson-DTMLeA5k.d.cts} +18 -4
  26. package/build/yayson-DTMLeA5k.d.cts.map +1 -0
  27. package/build/yayson.cjs +1 -1
  28. package/build/yayson.d.cts +2 -2
  29. package/build/yayson.d.mts +2 -2
  30. package/build/yayson.mjs +1 -1
  31. package/package.json +1 -1
  32. package/skill/yayson/SKILL.md +20 -0
  33. package/skill/yayson/references/api.md +27 -2
  34. package/build/symbols-DSjKJ8vh.mjs.map +0 -1
  35. package/build/types-BsfPiPEw.d.mts.map +0 -1
  36. package/build/types-DbMIe8E2.d.cts.map +0 -1
  37. package/build/yayson-BJv2Z0Z6.mjs.map +0 -1
  38. package/build/yayson-BKEyEcGk.d.cts.map +0 -1
  39. package/build/yayson-CRbukIqr.d.mts.map +0 -1
@@ -4,6 +4,7 @@ declare class Adapter {
4
4
  static get(model: ModelLike): Record<string, unknown>;
5
5
  static get(model: ModelLike, key: string): unknown;
6
6
  static id(model: ModelLike): string | undefined;
7
+ static has(model: ModelLike, key: string): boolean;
7
8
  }
8
9
  //#endregion
9
10
  //#region src/yayson/symbols.d.ts
@@ -46,6 +47,17 @@ interface JsonApiRelationship {
46
47
  links?: JsonApiLink;
47
48
  meta?: Record<string, unknown>;
48
49
  }
50
+ /**
51
+ * Extended relationship descriptor for `relationships()` return values.
52
+ * `hasMany: true` renders empty/missing as `data: []` (spec-compliant to-many).
53
+ * `optional: true` omits the relationship when its key is absent on the instance
54
+ * (or renders `links` only); explicit `null`/`[]` still render normally.
55
+ */
56
+ interface RelationshipConfig<P> {
57
+ presenter: P;
58
+ hasMany?: boolean;
59
+ optional?: boolean;
60
+ }
49
61
  interface JsonApiRelationships {
50
62
  [key: string]: JsonApiRelationship;
51
63
  }
@@ -87,6 +99,13 @@ interface StoreModel extends Record<string, unknown> {
87
99
  [REL_LINKS]?: JsonApiLink;
88
100
  [REL_META]?: Record<string, unknown>;
89
101
  }
102
+ /** Model type for create payloads where id may be absent */
103
+ interface StoreModelWithOptionalId extends Record<string, unknown> {
104
+ id?: string | number;
105
+ [TYPE]?: string;
106
+ [LINKS]?: JsonApiLink;
107
+ [META]?: Record<string, unknown>;
108
+ }
90
109
  interface StoreModels {
91
110
  [type: string]: {
92
111
  [id: string]: StoreModel;
@@ -118,5 +137,5 @@ interface LegacyStoreOptions<S extends SchemaRegistry = SchemaRegistry> {
118
137
  strict?: boolean;
119
138
  }
120
139
  //#endregion
121
- export { TYPE as C, REL_META as S, ModelLike as T, ValidationError as _, JsonApiRelationship as a, META as b, LegacyPresenterOptions as c, SchemaRegistry as d, StoreModel as f, StoreResult as g, StoreRecord as h, JsonApiLinks as i, LegacyStoreOptions as l, StoreOptions as m, JsonApiDocument as n, JsonApiRelationships as o, StoreModels as p, JsonApiLink as r, JsonApiResource as s, InferModelType as t, PresenterOptions as u, ZodLikeSchema as v, Adapter as w, REL_LINKS as x, LINKS as y };
122
- //# sourceMappingURL=types-DbMIe8E2.d.cts.map
140
+ export { REL_LINKS as C, ModelLike as D, Adapter as E, META as S, TYPE as T, StoreRecord as _, JsonApiRelationship as a, ZodLikeSchema as b, LegacyPresenterOptions as c, RelationshipConfig as d, SchemaRegistry as f, StoreOptions as g, StoreModels as h, JsonApiLinks as i, LegacyStoreOptions as l, StoreModelWithOptionalId as m, JsonApiDocument as n, JsonApiRelationships as o, StoreModel as p, JsonApiLink as r, JsonApiResource as s, InferModelType as t, PresenterOptions as u, StoreResult as v, REL_META as w, LINKS as x, ValidationError as y };
141
+ //# sourceMappingURL=types-KZiF6x7A.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-KZiF6x7A.d.mts","names":[],"sources":["../../../../../../yayson/adapter.ts","../../../../../../yayson/symbols.ts","../../../../../../yayson/schema.ts","../../../../../../yayson/types.ts"],"mappings":"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"}
@@ -4,6 +4,7 @@ declare class Adapter {
4
4
  static get(model: ModelLike): Record<string, unknown>;
5
5
  static get(model: ModelLike, key: string): unknown;
6
6
  static id(model: ModelLike): string | undefined;
7
+ static has(model: ModelLike, key: string): boolean;
7
8
  }
8
9
  //#endregion
9
10
  //#region src/yayson/symbols.d.ts
@@ -46,6 +47,17 @@ interface JsonApiRelationship {
46
47
  links?: JsonApiLink;
47
48
  meta?: Record<string, unknown>;
48
49
  }
50
+ /**
51
+ * Extended relationship descriptor for `relationships()` return values.
52
+ * `hasMany: true` renders empty/missing as `data: []` (spec-compliant to-many).
53
+ * `optional: true` omits the relationship when its key is absent on the instance
54
+ * (or renders `links` only); explicit `null`/`[]` still render normally.
55
+ */
56
+ interface RelationshipConfig<P> {
57
+ presenter: P;
58
+ hasMany?: boolean;
59
+ optional?: boolean;
60
+ }
49
61
  interface JsonApiRelationships {
50
62
  [key: string]: JsonApiRelationship;
51
63
  }
@@ -87,6 +99,13 @@ interface StoreModel extends Record<string, unknown> {
87
99
  [REL_LINKS]?: JsonApiLink;
88
100
  [REL_META]?: Record<string, unknown>;
89
101
  }
102
+ /** Model type for create payloads where id may be absent */
103
+ interface StoreModelWithOptionalId extends Record<string, unknown> {
104
+ id?: string | number;
105
+ [TYPE]?: string;
106
+ [LINKS]?: JsonApiLink;
107
+ [META]?: Record<string, unknown>;
108
+ }
90
109
  interface StoreModels {
91
110
  [type: string]: {
92
111
  [id: string]: StoreModel;
@@ -118,5 +137,5 @@ interface LegacyStoreOptions<S extends SchemaRegistry = SchemaRegistry> {
118
137
  strict?: boolean;
119
138
  }
120
139
  //#endregion
121
- export { TYPE as C, REL_META as S, ModelLike as T, ValidationError as _, JsonApiRelationship as a, META as b, LegacyPresenterOptions as c, SchemaRegistry as d, StoreModel as f, StoreResult as g, StoreRecord as h, JsonApiLinks as i, LegacyStoreOptions as l, StoreOptions as m, JsonApiDocument as n, JsonApiRelationships as o, StoreModels as p, JsonApiLink as r, JsonApiResource as s, InferModelType as t, PresenterOptions as u, ZodLikeSchema as v, Adapter as w, REL_LINKS as x, LINKS as y };
122
- //# sourceMappingURL=types-BsfPiPEw.d.mts.map
140
+ export { REL_LINKS as C, ModelLike as D, Adapter as E, META as S, TYPE as T, StoreRecord as _, JsonApiRelationship as a, ZodLikeSchema as b, LegacyPresenterOptions as c, RelationshipConfig as d, SchemaRegistry as f, StoreOptions as g, StoreModels as h, JsonApiLinks as i, LegacyStoreOptions as l, StoreModelWithOptionalId as m, JsonApiDocument as n, JsonApiRelationships as o, StoreModel as p, JsonApiLink as r, JsonApiResource as s, InferModelType as t, PresenterOptions as u, StoreResult as v, REL_META as w, LINKS as x, ValidationError as y };
141
+ //# sourceMappingURL=types-iC38_iCI.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types-iC38_iCI.d.cts","names":[],"sources":["../../../../../../yayson/adapter.ts","../../../../../../yayson/symbols.ts","../../../../../../yayson/schema.ts","../../../../../../yayson/types.ts"],"mappings":"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"}
package/build/utils.cjs CHANGED
@@ -1,4 +1,4 @@
1
- const require_symbols = require('./symbols-nFs99aEX.cjs');
1
+ const require_symbols = require('./symbols-B--FS78o.cjs');
2
2
 
3
3
  //#region src/utils.ts
4
4
  function getType(model) {
package/build/utils.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as TYPE, S as REL_META, T as ModelLike, b as META, f as StoreModel, r as JsonApiLink, w as Adapter, x as REL_LINKS, y as LINKS } from "./types-DbMIe8E2.cjs";
1
+ import { C as REL_LINKS, D as ModelLike, E as Adapter, S as META, T as TYPE, p as StoreModel, r as JsonApiLink, w as REL_META, x as LINKS } from "./types-iC38_iCI.cjs";
2
2
 
3
3
  //#region src/utils.d.ts
4
4
  declare function getType(model: StoreModel): string | undefined;
package/build/utils.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as TYPE, S as REL_META, T as ModelLike, b as META, f as StoreModel, r as JsonApiLink, w as Adapter, x as REL_LINKS, y as LINKS } from "./types-BsfPiPEw.mjs";
1
+ import { C as REL_LINKS, D as ModelLike, E as Adapter, S as META, T as TYPE, p as StoreModel, r as JsonApiLink, w as REL_META, x as LINKS } from "./types-KZiF6x7A.mjs";
2
2
 
3
3
  //#region src/utils.d.ts
4
4
  declare function getType(model: StoreModel): string | undefined;
package/build/utils.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { a as TYPE, i as REL_META, n as META, o as adapter_default, r as REL_LINKS, t as LINKS } from "./symbols-DSjKJ8vh.mjs";
1
+ import { a as TYPE, i as REL_META, n as META, o as adapter_default, r as REL_LINKS, t as LINKS } from "./symbols-BfU4k1el.mjs";
2
2
 
3
3
  //#region src/utils.ts
4
4
  function getType(model) {
@@ -1,4 +1,4 @@
1
- import { T as ModelLike, _ as ValidationError, d as SchemaRegistry, f as StoreModel, g as StoreResult, h as StoreRecord$1, i as JsonApiLinks, m as StoreOptions, n as JsonApiDocument, o as JsonApiRelationships, p as StoreModels, r as JsonApiLink, t as InferModelType, u as PresenterOptions, w as Adapter } from "./types-DbMIe8E2.cjs";
1
+ import { D as ModelLike, E as Adapter, _ as StoreRecord$1, d as RelationshipConfig, f as SchemaRegistry, g as StoreOptions, h as StoreModels, i as JsonApiLinks, m as StoreModelWithOptionalId, n as JsonApiDocument, o as JsonApiRelationships, p as StoreModel, r as JsonApiLink, t as InferModelType, u as PresenterOptions, v as StoreResult, y as ValidationError } from "./types-KZiF6x7A.mjs";
2
2
 
3
3
  //#region src/yayson/presenter.d.ts
4
4
  declare function createPresenter(adapter: typeof Adapter): {
@@ -8,12 +8,15 @@ declare function createPresenter(adapter: typeof Adapter): {
8
8
  id(instance: ModelLike): string | undefined;
9
9
  selfLinks(_instance: ModelLike): JsonApiLink | string | undefined;
10
10
  links(_instance?: ModelLike): JsonApiLinks | undefined;
11
- relationships(): Record<string, /*elided*/any>;
11
+ relationships(): Record<string, /*elided*/any | RelationshipConfig< /*elided*/any>>;
12
12
  attributes(instance: ModelLike | null): Record<string, unknown>;
13
13
  includeRelationships(scope: JsonApiDocument, instance: ModelLike): unknown[];
14
- buildRelationships(instance: ModelLike | null): JsonApiRelationships | null;
14
+ buildRelationships(instance: ModelLike | null, options?: {
15
+ payload?: boolean;
16
+ }): JsonApiRelationships | null;
15
17
  buildSelfLink(instance: ModelLike): JsonApiLink | undefined;
16
18
  toJSON(instanceOrCollection: ModelLike | ModelLike[] | null | undefined, options?: PresenterOptions): JsonApiDocument;
19
+ payload(instance: ModelLike, options?: PresenterOptions): JsonApiDocument;
17
20
  render(instanceOrCollection: ModelLike | ModelLike[] | null, options?: PresenterOptions): JsonApiDocument;
18
21
  };
19
22
  adapter: typeof Adapter;
@@ -21,6 +24,7 @@ declare function createPresenter(adapter: typeof Adapter): {
21
24
  fields?: string[];
22
25
  toJSON(instanceOrCollection: ModelLike | ModelLike[] | null, options?: PresenterOptions): JsonApiDocument;
23
26
  render(instanceOrCollection: ModelLike | ModelLike[] | null, options?: PresenterOptions): JsonApiDocument;
27
+ payload(instance: ModelLike, options?: PresenterOptions): JsonApiDocument;
24
28
  };
25
29
  type Presenter = ReturnType<typeof createPresenter>;
26
30
  //#endregion
@@ -35,6 +39,7 @@ declare class StoreRecord implements StoreRecord$1 {
35
39
  constructor(options: StoreRecord$1);
36
40
  }
37
41
  declare class Store<S extends SchemaRegistry = SchemaRegistry> {
42
+ #private;
38
43
  records: StoreRecord[];
39
44
  schemas?: S;
40
45
  strict: boolean;
@@ -49,6 +54,15 @@ declare class Store<S extends SchemaRegistry = SchemaRegistry> {
49
54
  remove(type: string, id?: string | number): void;
50
55
  syncAll(body: JsonApiDocument): StoreResult;
51
56
  sync(body: JsonApiDocument): StoreModel | StoreResult;
57
+ /**
58
+ * Build a model from a JSON:API document without storing it.
59
+ * Useful for create payloads where id may be absent.
60
+ *
61
+ * Per JSON:API spec: "The id member is not required when the resource object
62
+ * originates at the client and represents a new resource to be created on the server."
63
+ */
64
+ static build(body: JsonApiDocument): StoreModelWithOptionalId;
65
+ build(body: JsonApiDocument): StoreModelWithOptionalId;
52
66
  retrieve<T extends string>(type: T, body: JsonApiDocument): InferModelType<S, T> | null;
53
67
  retrieveAll<T extends string>(type: T, body: JsonApiDocument): StoreResult<InferModelType<S, T>>;
54
68
  }
@@ -66,4 +80,4 @@ interface YaysonResult {
66
80
  declare function yayson(options?: YaysonOptions): YaysonResult;
67
81
  //#endregion
68
82
  export { Presenter as i, YaysonResult as n, yayson as r, YaysonOptions as t };
69
- //# sourceMappingURL=yayson-BKEyEcGk.d.cts.map
83
+ //# sourceMappingURL=yayson-BvwMr4Ad.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"yayson-BvwMr4Ad.d.mts","names":[],"sources":["../../../../../../yayson/presenter.ts","../../../../../../yayson/store.ts","../../../../../../yayson.ts"],"mappings":";;;iBA0BwB,eAAA,CAAgB,OAAA,SAAgB,OAAA;EAAA,aAUhC,eAAA;iBAVe;WAQ5B,eAAA;iBAMM,SAAA;yBAIQ,SAAA,GAAY,WAAA;sBAIf,SAAA,GAAY,YAAA;qBAIb,MAAA,SAZJ,gBAYsC,kBAAA,EAZtC;yBAgBQ,SAAA,UAAmB,MAAA;gCAiBZ,eAAA,EAAe,QAAA,EAAY,SAAA;iCAqB1B,SAAA,SAAgB,OAAA;MAAa,OAAA;IAAA,IAA2B,oBAAA;4BAuE7D,SAAA,GAAY,WAAA;iCAKZ,SAAA,GAAY,SAAA,uBAA8B,OAAA,GACtD,gBAAA,GACT,eAAA;sBA8Ee,SAAA,EAAS,OAAA,GAAY,gBAAA,GAAmB,eAAA;iCA0B7B,SAAA,GAAY,SAAA,WAAkB,OAAA,GAAY,gBAAA,GAAmB,eAAA;EAAA;;;;+BAItD,SAAA,GAAY,SAAA,WAAkB,OAAA,GAAY,gBAAA,GAAmB,eAAA;+BAI7D,SAAA,GAAY,SAAA,WAAkB,OAAA,GAAY,gBAAA,GAAmB,eAAA;oBAIxE,SAAA,EAAS,OAAA,GAAY,gBAAA,GAAmB,eAAA;AAAA;AAAA,KAQzD,SAAA,GAAY,UAAA,QAAkB,eAAA;;;cClRpC,WAAA,YAAuB,aAAA;EAC3B,EAAA;EACA,IAAA;EACA,UAAA,GAAa,MAAA;EACb,aAAA,GAAgB,oBAAA;EAChB,KAAA,GAAQ,WAAA;EACR,IAAA,GAAO,MAAA;cAEK,OAAA,EAAS,aAAA;AAAA;AAAA,cAUF,KAAA,WAAgB,cAAA,GAAiB,cAAA;EAAA;EACpD,OAAA,EAAS,WAAA;EACT,OAAA,GAAU,CAAA;EACV,MAAA;EACA,gBAAA,EAAkB,eAAA;cAEN,OAAA,GAAU,YAAA,CAAa,CAAA;EAMnC,KAAA,CAAA;EAmGA,OAAA,CAAQ,GAAA,EAAK,WAAA,EAAa,IAAA,UAAc,MAAA,EAAQ,WAAA,GAAc,UAAA;EA+B9D,UAAA,CAAW,IAAA,UAAc,EAAA,oBAAsB,WAAA;EAK/C,WAAA,CAAY,IAAA,WAAe,WAAA;EAiB3B,IAAA,kBAAA,CAAuB,IAAA,EAAM,CAAA,EAAG,EAAA,mBAAqB,MAAA,GAAS,WAAA,GAAc,cAAA,CAAe,CAAA,EAAG,CAAA;EAM9F,OAAA,kBAAA,CAA0B,IAAA,EAAM,CAAA,EAAG,MAAA,GAAS,WAAA,GAAc,cAAA,CAAe,CAAA,EAAG,CAAA;EAgB5E,MAAA,CAAO,IAAA,UAAc,EAAA;EAkBrB,OAAA,CAAQ,IAAA,EAAM,eAAA,GAAkB,WAAA;EA8DhC,IAAA,CAAK,IAAA,EAAM,eAAA,GAAkB,UAAA,GAAa,WAAA;EDvI5B;;;;;;;EAAA,OC0JP,KAAA,CAAM,IAAA,EAAM,eAAA,GAAkB,wBAAA;EAIrC,KAAA,CAAM,IAAA,EAAM,eAAA,GAAkB,wBAAA;EAQ9B,QAAA,kBAAA,CAA2B,IAAA,EAAM,CAAA,EAAG,IAAA,EAAM,eAAA,GAAkB,cAAA,CAAe,CAAA,EAAG,CAAA;EAW9E,WAAA,kBAAA,CAA8B,IAAA,EAAM,CAAA,EAAG,IAAA,EAAM,eAAA,GAAkB,WAAA,CAAY,cAAA,CAAe,CAAA,EAAG,CAAA;AAAA;;;KCzU1F,aAAA,mBAAgC,OAAA;AAAA,UAE3B,aAAA;EACR,OAAA,GAAU,aAAA;AAAA;AAAA,UAGF,YAAA;EACR,KAAA,SAAc,KAAA;EACd,SAAA,EAAW,SAAA;EACX,OAAA,SAAgB,OAAA;AAAA;AAAA,iBAgBT,MAAA,CAAO,OAAA,GAAU,aAAA,GAAgB,YAAA"}
@@ -1,4 +1,4 @@
1
- const require_symbols = require('./symbols-nFs99aEX.cjs');
1
+ const require_symbols = require('./symbols-B--FS78o.cjs');
2
2
 
3
3
  //#region src/yayson/adapters/sequelize.ts
4
4
  function isSequelizeModel(model) {
@@ -8,12 +8,16 @@ var SequelizeAdapter = class extends require_symbols.adapter_default {
8
8
  static get(model, key) {
9
9
  if (isSequelizeModel(model)) return model.get(key);
10
10
  }
11
+ static has(model, key) {
12
+ if (isSequelizeModel(model)) return model.get(key) !== void 0;
13
+ return false;
14
+ }
11
15
  static id(model) {
12
16
  const pkFields = model.constructor && "primaryKeys" in model.constructor ? Object.keys(model.constructor.primaryKeys) : ["id"];
13
17
  if (pkFields.length > 1) throw new Error("YAYSON does not support Sequelize models with composite primary keys. You can only use one column for your primary key. Currently using: " + pkFields.join(","));
14
18
  else if (pkFields.length < 1) throw new Error("YAYSON can only serialize Sequelize models which have a primary key. This is used for the JSON:API model id.");
15
19
  const id = this.get(model, pkFields[0]);
16
- if (id === void 0) return id;
20
+ if (id == null) return;
17
21
  return `${id}`;
18
22
  }
19
23
  };
@@ -69,23 +73,32 @@ function createPresenter(adapter) {
69
73
  const result = [];
70
74
  if (!relationships) return result;
71
75
  for (const key in relationships) {
72
- const factory = relationships[key];
73
- if (!factory) throw new Error(`Presenter for ${key} in ${this.constructor.type} is not defined`);
74
- const presenter = new factory(scope);
76
+ const entry = relationships[key];
77
+ if (!entry) throw new Error(`Presenter for ${key} in ${this.constructor.type} is not defined`);
78
+ const presenter = new (typeof entry === "function" ? entry : entry.presenter)(scope);
75
79
  const data = this.constructor.adapter.get(instance, key);
76
80
  result.push(presenter.toJSON(data, { include: true }));
77
81
  }
78
82
  return result;
79
83
  }
80
- buildRelationships(instance) {
84
+ buildRelationships(instance, options = {}) {
81
85
  if (instance == null) return null;
82
86
  const rels = this.relationships();
83
87
  const links = this.links(instance) || {};
88
+ const isPayload = options.payload === true;
84
89
  let relationships = null;
85
90
  if (!rels) return null;
86
91
  for (const key in rels) {
92
+ const entry = rels[key];
93
+ if (!entry) continue;
94
+ const isConfig = typeof entry !== "function";
95
+ const presenter = isConfig ? entry.presenter : entry;
96
+ const hasMany = isConfig ? entry.hasMany : void 0;
97
+ const optional = isConfig ? entry.optional ?? false : false;
98
+ const linkValue = links[key];
99
+ const hasLinks = linkValue != null;
87
100
  const data = this.constructor.adapter.get(instance, key);
88
- const presenter = rels[key];
101
+ const keyPresent = this.constructor.adapter.has(instance, key);
89
102
  const buildData = (d) => {
90
103
  const id = this.constructor.adapter.id(d);
91
104
  if (!id) throw new Error(`Model of type ${presenter.type} is missing an id (relationship '${key}' of ${this.constructor.type})`);
@@ -94,19 +107,21 @@ function createPresenter(adapter) {
94
107
  type: presenter.type
95
108
  };
96
109
  };
97
- const build = (d) => {
98
- const rel = {};
99
- if (d != null) rel.data = buildData(d);
100
- if (links[key] != null) rel.links = buildLinks(links[key]);
101
- else if (d == null) rel.data = null;
102
- return rel;
103
- };
110
+ if (optional && !keyPresent && !isPayload) {
111
+ if (hasLinks) {
112
+ if (!relationships) relationships = {};
113
+ relationships[key] = { links: buildLinks(linkValue) };
114
+ }
115
+ continue;
116
+ }
104
117
  if (!relationships) relationships = {};
105
- if (!relationships[key]) relationships[key] = {};
106
- if (Array.isArray(data)) {
107
- relationships[key].data = data.map(buildData);
108
- if (links[key] != null) relationships[key].links = buildLinks(links[key]);
109
- } else relationships[key] = build(data);
118
+ const rel = {};
119
+ if (Array.isArray(data)) rel.data = data.map(buildData);
120
+ else if (data != null) rel.data = buildData(data);
121
+ else if (hasMany === true) rel.data = [];
122
+ else if (!hasLinks) rel.data = null;
123
+ if (hasLinks) rel.links = buildLinks(linkValue);
124
+ relationships[key] = rel;
110
125
  }
111
126
  return relationships;
112
127
  }
@@ -150,6 +165,23 @@ function createPresenter(adapter) {
150
165
  }
151
166
  return this.scope;
152
167
  }
168
+ payload(instance, options) {
169
+ if (Array.isArray(instance)) throw new Error("payload() expects a single resource, not an array");
170
+ if (instance == null) throw new Error("payload() requires a resource, got null");
171
+ const model = {
172
+ type: this.constructor.type,
173
+ attributes: this.attributes(instance)
174
+ };
175
+ const id = this.id(instance);
176
+ if (id != null) model.id = id;
177
+ const relationships = this.buildRelationships(instance, { payload: true });
178
+ if (relationships != null) model.relationships = relationships;
179
+ const result = { data: model };
180
+ const opts = options ?? {};
181
+ if (opts.meta != null) result.meta = opts.meta;
182
+ if (opts.links != null) result.links = opts.links;
183
+ return result;
184
+ }
153
185
  render(instanceOrCollection, options) {
154
186
  return this.toJSON(instanceOrCollection, options);
155
187
  }
@@ -159,6 +191,9 @@ function createPresenter(adapter) {
159
191
  static render(instanceOrCollection, options) {
160
192
  return new this().render(instanceOrCollection, options);
161
193
  }
194
+ static payload(instance, options) {
195
+ return new this().payload(instance, options);
196
+ }
162
197
  }
163
198
  return Presenter;
164
199
  }
@@ -190,6 +225,9 @@ function validate(schema, data, strict) {
190
225
 
191
226
  //#endregion
192
227
  //#region src/yayson/store.ts
228
+ function hasId(model) {
229
+ return model.id != null;
230
+ }
193
231
  var StoreRecord = class {
194
232
  id;
195
233
  type;
@@ -206,7 +244,7 @@ var StoreRecord = class {
206
244
  this.meta = options.meta;
207
245
  }
208
246
  };
209
- var Store = class {
247
+ var Store = class Store {
210
248
  records = [];
211
249
  schemas;
212
250
  strict;
@@ -220,37 +258,57 @@ var Store = class {
220
258
  this.records = [];
221
259
  this.validationErrors = [];
222
260
  }
223
- toModel(rec, type, models) {
224
- const model = {
225
- ...rec.attributes || {},
226
- id: rec.id
227
- };
228
- model[require_symbols.TYPE] = rec.type;
229
- const idStr = String(rec.id);
230
- if (!models[type]) models[type] = {};
231
- if (!models[type][idStr]) models[type][idStr] = model;
232
- if (rec.meta != null) model[require_symbols.META] = rec.meta;
233
- if (rec.links != null) model[require_symbols.LINKS] = rec.links;
234
- if (rec.relationships != null) for (const key in rec.relationships) {
235
- const rel = rec.relationships[key];
236
- const { data } = rel;
237
- const { links } = rel;
238
- const { meta } = rel;
261
+ #createStub(type, id) {
262
+ const stub = { id };
263
+ stub[require_symbols.TYPE] = type;
264
+ return stub;
265
+ }
266
+ #createModel(resource, options) {
267
+ const models = options?.models ?? {};
268
+ const model = { ...resource.attributes || {} };
269
+ if (resource.id != null) model.id = resource.id;
270
+ const type = resource.type;
271
+ model[require_symbols.TYPE] = type;
272
+ if (resource.meta != null) model[require_symbols.META] = resource.meta;
273
+ if (resource.links != null) model[require_symbols.LINKS] = resource.links;
274
+ if (hasId(model)) {
275
+ const idStr = String(model.id);
276
+ if (!models[type]) models[type] = {};
277
+ if (!models[type][idStr]) models[type][idStr] = model;
278
+ }
279
+ if (resource.relationships != null) {
280
+ const resolver = (ref) => {
281
+ return this.#findModel(ref.type, ref.id, models) ?? this.#createStub(ref.type, ref.id);
282
+ };
283
+ this.#resolveRelationships(model, resource.relationships, resolver, { includeRelMeta: options?.includeRelMeta });
284
+ }
285
+ return model;
286
+ }
287
+ #resolveRelationships(model, relationships, resolver, options) {
288
+ const includeRelMeta = options?.includeRelMeta ?? true;
289
+ for (const key in relationships) {
290
+ const { data, links, meta } = relationships[key];
239
291
  model[key] = null;
240
292
  if (data == null && links == null) continue;
241
- const resolve = ({ type: type$1, id }) => {
242
- return this.find(type$1, id, models);
243
- };
244
- if (Array.isArray(data)) model[key] = data.map(resolve);
245
- else {
246
- const relModel = data != null ? resolve(data) : { id: "" };
247
- if (relModel) {
248
- relModel[require_symbols.REL_LINKS] = links || {};
249
- relModel[require_symbols.REL_META] = meta || {};
250
- model[key] = relModel;
293
+ if (Array.isArray(data)) model[key] = data.filter((item) => item.id != null).map(resolver);
294
+ else if (data != null && data.id != null) {
295
+ const relModel = resolver(data);
296
+ if (includeRelMeta) {
297
+ const modelWithMeta = relModel;
298
+ modelWithMeta[require_symbols.REL_LINKS] = links || {};
299
+ modelWithMeta[require_symbols.REL_META] = meta || {};
251
300
  }
301
+ model[key] = relModel;
302
+ } else if (data == null && (links != null || meta != null) && includeRelMeta) {
303
+ const relModel = { id: "" };
304
+ relModel[require_symbols.REL_LINKS] = links || {};
305
+ relModel[require_symbols.REL_META] = meta || {};
306
+ model[key] = relModel;
252
307
  }
253
308
  }
309
+ }
310
+ toModel(rec, type, models) {
311
+ const model = this.#createModel(rec, { models });
254
312
  if (this.schemas && this.schemas[rec.type]) {
255
313
  const schema = this.schemas[rec.type];
256
314
  const result = validate(schema, model, this.strict);
@@ -274,13 +332,16 @@ var Store = class {
274
332
  findRecords(type) {
275
333
  return this.records.filter((r) => r.type === type);
276
334
  }
277
- find(type, id, models) {
278
- const modelsObj = models ?? {};
335
+ #findModel(type, id, models) {
279
336
  const idStr = String(id);
280
- const rec = this.findRecord(type, idStr);
337
+ const cached = models[type]?.[idStr];
338
+ if (cached) return cached;
339
+ const rec = this.findRecord(type, id);
281
340
  if (rec == null) return null;
282
- if (!modelsObj[type]) modelsObj[type] = {};
283
- return modelsObj[type][idStr] || this.toModel(rec, type, modelsObj);
341
+ return this.toModel(rec, type, models);
342
+ }
343
+ find(type, id, models) {
344
+ return this.#findModel(type, id, models ?? {});
284
345
  }
285
346
  findAll(type, models) {
286
347
  const modelsObj = models ?? {};
@@ -354,6 +415,21 @@ var Store = class {
354
415
  }
355
416
  return result;
356
417
  }
418
+ /**
419
+ * Build a model from a JSON:API document without storing it.
420
+ * Useful for create payloads where id may be absent.
421
+ *
422
+ * Per JSON:API spec: "The id member is not required when the resource object
423
+ * originates at the client and represents a new resource to be created on the server."
424
+ */
425
+ static build(body) {
426
+ return new Store().build(body);
427
+ }
428
+ build(body) {
429
+ const { data } = body;
430
+ if (data == null || Array.isArray(data)) throw new Error("build() expects a single resource in data, not null or an array");
431
+ return this.#createModel(data);
432
+ }
357
433
  retrieve(type, body) {
358
434
  const synced = this.syncAll(body);
359
435
  const model = synced.find((m) => m[require_symbols.TYPE] === type);