yayson 4.1.0 → 4.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 +43 -0
- package/build/legacy.cjs +27 -23
- package/build/legacy.d.cts +35 -7
- package/build/legacy.d.cts.map +1 -1
- package/build/legacy.d.mts +35 -7
- package/build/legacy.d.mts.map +1 -1
- package/build/legacy.mjs +27 -23
- package/build/legacy.mjs.map +1 -1
- package/build/{symbols-nFs99aEX.cjs → symbols-B--FS78o.cjs} +3 -0
- package/build/{symbols-DSjKJ8vh.mjs → symbols-BfU4k1el.mjs} +4 -1
- package/build/symbols-BfU4k1el.mjs.map +1 -0
- package/build/{types-NiKV-lj-.d.cts → types-KZiF6x7A.d.mts} +14 -2
- package/build/types-KZiF6x7A.d.mts.map +1 -0
- package/build/{types-Do2flKZX.d.mts → types-iC38_iCI.d.cts} +14 -2
- package/build/types-iC38_iCI.d.cts.map +1 -0
- package/build/utils.cjs +1 -1
- package/build/utils.d.cts +1 -1
- package/build/utils.d.mts +1 -1
- package/build/utils.mjs +1 -1
- package/build/{yayson-l2JKseMH.cjs → yayson-B51EJfRP.cjs} +97 -27
- package/build/{yayson-3UYKq2H5.d.cts → yayson-BvwMr4Ad.d.mts} +6 -4
- package/build/yayson-BvwMr4Ad.d.mts.map +1 -0
- package/build/{yayson-Ce5uGpgU.d.mts → yayson-DTMLeA5k.d.cts} +6 -4
- package/build/yayson-DTMLeA5k.d.cts.map +1 -0
- package/build/{yayson-CwZg5FNt.mjs → yayson-RzT9zsdx.mjs} +81 -29
- package/build/yayson-RzT9zsdx.mjs.map +1 -0
- package/build/yayson.cjs +1 -1
- package/build/yayson.d.cts +2 -2
- package/build/yayson.d.mts +2 -2
- package/build/yayson.mjs +1 -1
- package/package.json +1 -1
- package/skill/yayson/SKILL.md +20 -0
- package/skill/yayson/references/api.md +27 -2
- package/build/symbols-DSjKJ8vh.mjs.map +0 -1
- package/build/types-Do2flKZX.d.mts.map +0 -1
- package/build/types-NiKV-lj-.d.cts.map +0 -1
- package/build/yayson-3UYKq2H5.d.cts.map +0 -1
- package/build/yayson-Ce5uGpgU.d.mts.map +0 -1
- package/build/yayson-CwZg5FNt.mjs.map +0 -1
|
@@ -10,6 +10,9 @@ var Adapter = class {
|
|
|
10
10
|
if (id == null) return;
|
|
11
11
|
return `${id}`;
|
|
12
12
|
}
|
|
13
|
+
static has(model, key) {
|
|
14
|
+
return key in model;
|
|
15
|
+
}
|
|
13
16
|
};
|
|
14
17
|
var adapter_default = Adapter;
|
|
15
18
|
|
|
@@ -23,4 +26,4 @@ const REL_META = Symbol("yayson.rel-meta");
|
|
|
23
26
|
|
|
24
27
|
//#endregion
|
|
25
28
|
export { TYPE as a, REL_META as i, META as n, adapter_default as o, REL_LINKS as r, LINKS as t };
|
|
26
|
-
//# sourceMappingURL=symbols-
|
|
29
|
+
//# sourceMappingURL=symbols-BfU4k1el.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"symbols-BfU4k1el.mjs","names":[],"sources":["../src/yayson/adapter.ts","../src/yayson/symbols.ts"],"sourcesContent":["export type ModelLike = object\n\nclass Adapter {\n static get(model: ModelLike): Record<string, unknown>\n static get(model: ModelLike, key: string): unknown\n static get(model: ModelLike, key?: string): unknown {\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- ModelLike is object for flexibility, cast for dynamic access\n const obj = model as Record<string, unknown>\n if (key) {\n return obj[key]\n }\n return obj\n }\n\n static id(model: ModelLike): string | undefined {\n const id = this.get(model, 'id')\n if (id == null) {\n return undefined\n }\n return `${id}`\n }\n\n static has(model: ModelLike, key: string): boolean {\n return key in model\n }\n}\n\nexport default Adapter\n","export const TYPE: unique symbol = Symbol('yayson.type')\nexport const LINKS: unique symbol = Symbol('yayson.links')\nexport const META: unique symbol = Symbol('yayson.meta')\nexport const REL_LINKS: unique symbol = Symbol('yayson.rel-links')\nexport const REL_META: unique symbol = Symbol('yayson.rel-meta')\n"],"mappings":";AAEA,IAAM,UAAN,MAAc;CAGZ,OAAO,IAAI,OAAkB,KAAuB;EAElD,MAAM,MAAM;AACZ,MAAI,IACF,QAAO,IAAI;AAEb,SAAO;;CAGT,OAAO,GAAG,OAAsC;EAC9C,MAAM,KAAK,KAAK,IAAI,OAAO,KAAK;AAChC,MAAI,MAAM,KACR;AAEF,SAAO,GAAG;;CAGZ,OAAO,IAAI,OAAkB,KAAsB;AACjD,SAAO,OAAO;;;AAIlB,sBAAe;;;;AC3Bf,MAAa,OAAsB,OAAO,cAAc;AACxD,MAAa,QAAuB,OAAO,eAAe;AAC1D,MAAa,OAAsB,OAAO,cAAc;AACxD,MAAa,YAA2B,OAAO,mBAAmB;AAClE,MAAa,WAA0B,OAAO,kBAAkB"}
|
|
@@ -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
|
}
|
|
@@ -125,5 +137,5 @@ interface LegacyStoreOptions<S extends SchemaRegistry = SchemaRegistry> {
|
|
|
125
137
|
strict?: boolean;
|
|
126
138
|
}
|
|
127
139
|
//#endregion
|
|
128
|
-
export {
|
|
129
|
-
//# sourceMappingURL=types-
|
|
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
|
}
|
|
@@ -125,5 +137,5 @@ interface LegacyStoreOptions<S extends SchemaRegistry = SchemaRegistry> {
|
|
|
125
137
|
strict?: boolean;
|
|
126
138
|
}
|
|
127
139
|
//#endregion
|
|
128
|
-
export {
|
|
129
|
-
//# sourceMappingURL=types-
|
|
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
package/build/utils.d.cts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { C as
|
|
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
|
|
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-
|
|
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
|
-
const require_symbols = require('./symbols-
|
|
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,6 +8,10 @@ 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(","));
|
|
@@ -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
|
|
73
|
-
if (!
|
|
74
|
-
const presenter = new
|
|
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
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
106
|
-
if (Array.isArray(data))
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
}
|
|
@@ -159,7 +174,7 @@ function createPresenter(adapter) {
|
|
|
159
174
|
};
|
|
160
175
|
const id = this.id(instance);
|
|
161
176
|
if (id != null) model.id = id;
|
|
162
|
-
const relationships = this.buildRelationships(instance);
|
|
177
|
+
const relationships = this.buildRelationships(instance, { payload: true });
|
|
163
178
|
if (relationships != null) model.relationships = relationships;
|
|
164
179
|
const result = { data: model };
|
|
165
180
|
const opts = options ?? {};
|
|
@@ -208,6 +223,42 @@ function validate(schema, data, strict) {
|
|
|
208
223
|
}
|
|
209
224
|
}
|
|
210
225
|
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/yayson/safe.ts
|
|
228
|
+
/**
|
|
229
|
+
* Defenses against prototype pollution, since yayson keys lookup tables and
|
|
230
|
+
* model objects by strings from untrusted documents (`type`, `id`, member
|
|
231
|
+
* names). Per MDN's guidance
|
|
232
|
+
* (https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Prototype_pollution):
|
|
233
|
+
* caches use null-prototype objects, and "__proto__"/"constructor"/"prototype"
|
|
234
|
+
* are rejected as member names.
|
|
235
|
+
*/
|
|
236
|
+
/** Null-prototype lookup table (MDN's `Object.create(null)`, never `obj.__proto__`). */
|
|
237
|
+
function safeObject() {
|
|
238
|
+
return Object.create(null);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Normalize a caller-supplied cache to a null-prototype object, so a public
|
|
242
|
+
* `models` argument that happens to be a plain `{}` can't let type="__proto__"
|
|
243
|
+
* resolve through Object.prototype. Already-null-prototype caches pass through
|
|
244
|
+
* unchanged (preserving identity/memoization).
|
|
245
|
+
*/
|
|
246
|
+
function safeCache(cache) {
|
|
247
|
+
if (cache === void 0) return safeObject();
|
|
248
|
+
if (Object.getPrototypeOf(cache) === null) return cache;
|
|
249
|
+
const safe = safeObject();
|
|
250
|
+
Object.assign(safe, cache);
|
|
251
|
+
return safe;
|
|
252
|
+
}
|
|
253
|
+
const UNSAFE_KEYS = new Set([
|
|
254
|
+
"__proto__",
|
|
255
|
+
"constructor",
|
|
256
|
+
"prototype"
|
|
257
|
+
]);
|
|
258
|
+
function isUnsafeKey(key) {
|
|
259
|
+
return UNSAFE_KEYS.has(key);
|
|
260
|
+
}
|
|
261
|
+
|
|
211
262
|
//#endregion
|
|
212
263
|
//#region src/yayson/store.ts
|
|
213
264
|
function hasId(model) {
|
|
@@ -249,7 +300,7 @@ var Store = class Store {
|
|
|
249
300
|
return stub;
|
|
250
301
|
}
|
|
251
302
|
#createModel(resource, options) {
|
|
252
|
-
const models = options?.models ??
|
|
303
|
+
const models = options?.models ?? safeObject();
|
|
253
304
|
const model = { ...resource.attributes || {} };
|
|
254
305
|
if (resource.id != null) model.id = resource.id;
|
|
255
306
|
const type = resource.type;
|
|
@@ -258,7 +309,7 @@ var Store = class Store {
|
|
|
258
309
|
if (resource.links != null) model[require_symbols.LINKS] = resource.links;
|
|
259
310
|
if (hasId(model)) {
|
|
260
311
|
const idStr = String(model.id);
|
|
261
|
-
if (!models[type]) models[type] =
|
|
312
|
+
if (!models[type]) models[type] = safeObject();
|
|
262
313
|
if (!models[type][idStr]) models[type][idStr] = model;
|
|
263
314
|
}
|
|
264
315
|
if (resource.relationships != null) {
|
|
@@ -271,7 +322,8 @@ var Store = class Store {
|
|
|
271
322
|
}
|
|
272
323
|
#resolveRelationships(model, relationships, resolver, options) {
|
|
273
324
|
const includeRelMeta = options?.includeRelMeta ?? true;
|
|
274
|
-
for (const key
|
|
325
|
+
for (const key of Object.keys(relationships)) {
|
|
326
|
+
if (isUnsafeKey(key)) continue;
|
|
275
327
|
const { data, links, meta } = relationships[key];
|
|
276
328
|
model[key] = null;
|
|
277
329
|
if (data == null && links == null) continue;
|
|
@@ -293,7 +345,7 @@ var Store = class Store {
|
|
|
293
345
|
}
|
|
294
346
|
}
|
|
295
347
|
toModel(rec, type, models) {
|
|
296
|
-
const model = this.#createModel(rec, { models });
|
|
348
|
+
const model = this.#createModel(rec, { models: safeCache(models) });
|
|
297
349
|
if (this.schemas && this.schemas[rec.type]) {
|
|
298
350
|
const schema = this.schemas[rec.type];
|
|
299
351
|
const result = validate(schema, model, this.strict);
|
|
@@ -326,14 +378,14 @@ var Store = class Store {
|
|
|
326
378
|
return this.toModel(rec, type, models);
|
|
327
379
|
}
|
|
328
380
|
find(type, id, models) {
|
|
329
|
-
return this.#findModel(type, id, models
|
|
381
|
+
return this.#findModel(type, id, safeCache(models));
|
|
330
382
|
}
|
|
331
383
|
findAll(type, models) {
|
|
332
|
-
const modelsObj = models
|
|
384
|
+
const modelsObj = safeCache(models);
|
|
333
385
|
const recs = this.findRecords(type);
|
|
334
386
|
if (recs == null) return [];
|
|
335
387
|
recs.forEach((rec) => {
|
|
336
|
-
if (!modelsObj[type]) modelsObj[type] =
|
|
388
|
+
if (!modelsObj[type]) modelsObj[type] = safeObject();
|
|
337
389
|
return this.toModel(rec, type, modelsObj);
|
|
338
390
|
});
|
|
339
391
|
return Object.values(modelsObj[type] || {});
|
|
@@ -382,7 +434,7 @@ var Store = class Store {
|
|
|
382
434
|
try {
|
|
383
435
|
syncData(body.included);
|
|
384
436
|
const recs = syncData(body.data);
|
|
385
|
-
const models =
|
|
437
|
+
const models = safeObject();
|
|
386
438
|
const result = recs.map((rec) => this.toModel(rec, rec.type, models));
|
|
387
439
|
if (body.meta != null) result[require_symbols.META] = body.meta;
|
|
388
440
|
return result;
|
|
@@ -454,6 +506,24 @@ Object.defineProperty(exports, 'filterByFields', {
|
|
|
454
506
|
return filterByFields;
|
|
455
507
|
}
|
|
456
508
|
});
|
|
509
|
+
Object.defineProperty(exports, 'isUnsafeKey', {
|
|
510
|
+
enumerable: true,
|
|
511
|
+
get: function () {
|
|
512
|
+
return isUnsafeKey;
|
|
513
|
+
}
|
|
514
|
+
});
|
|
515
|
+
Object.defineProperty(exports, 'safeCache', {
|
|
516
|
+
enumerable: true,
|
|
517
|
+
get: function () {
|
|
518
|
+
return safeCache;
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
Object.defineProperty(exports, 'safeObject', {
|
|
522
|
+
enumerable: true,
|
|
523
|
+
get: function () {
|
|
524
|
+
return safeObject;
|
|
525
|
+
}
|
|
526
|
+
});
|
|
457
527
|
Object.defineProperty(exports, 'validate', {
|
|
458
528
|
enumerable: true,
|
|
459
529
|
get: function () {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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,10 +8,12 @@ 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
|
|
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;
|
|
17
19
|
payload(instance: ModelLike, options?: PresenterOptions): JsonApiDocument;
|
|
@@ -78,4 +80,4 @@ interface YaysonResult {
|
|
|
78
80
|
declare function yayson(options?: YaysonOptions): YaysonResult;
|
|
79
81
|
//#endregion
|
|
80
82
|
export { Presenter as i, YaysonResult as n, yayson as r, YaysonOptions as t };
|
|
81
|
-
//# sourceMappingURL=yayson-
|
|
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;;;cCjRpC,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;EAuGA,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;ED5I5B;;;;;;;EAAA,OC+JP,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;;;KC9U1F,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
|
-
import {
|
|
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-iC38_iCI.cjs";
|
|
2
2
|
|
|
3
3
|
//#region src/yayson/presenter.d.ts
|
|
4
4
|
declare function createPresenter(adapter: typeof Adapter): {
|
|
@@ -8,10 +8,12 @@ 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
|
|
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;
|
|
17
19
|
payload(instance: ModelLike, options?: PresenterOptions): JsonApiDocument;
|
|
@@ -78,4 +80,4 @@ interface YaysonResult {
|
|
|
78
80
|
declare function yayson(options?: YaysonOptions): YaysonResult;
|
|
79
81
|
//#endregion
|
|
80
82
|
export { Presenter as i, YaysonResult as n, yayson as r, YaysonOptions as t };
|
|
81
|
-
//# sourceMappingURL=yayson-
|
|
83
|
+
//# sourceMappingURL=yayson-DTMLeA5k.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"yayson-DTMLeA5k.d.cts","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;;;cCjRpC,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;EAuGA,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;ED5I5B;;;;;;;EAAA,OC+JP,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;;;KC9U1F,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
|
-
import { a as TYPE, i as REL_META, n as META, o as adapter_default, r as REL_LINKS, t as LINKS } from "./symbols-
|
|
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/yayson/adapters/sequelize.ts
|
|
4
4
|
function isSequelizeModel(model) {
|
|
@@ -8,6 +8,10 @@ var SequelizeAdapter = class extends 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(","));
|
|
@@ -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
|
|
73
|
-
if (!
|
|
74
|
-
const presenter = new
|
|
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
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
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
|
-
|
|
106
|
-
if (Array.isArray(data))
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
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
|
}
|
|
@@ -159,7 +174,7 @@ function createPresenter(adapter) {
|
|
|
159
174
|
};
|
|
160
175
|
const id = this.id(instance);
|
|
161
176
|
if (id != null) model.id = id;
|
|
162
|
-
const relationships = this.buildRelationships(instance);
|
|
177
|
+
const relationships = this.buildRelationships(instance, { payload: true });
|
|
163
178
|
if (relationships != null) model.relationships = relationships;
|
|
164
179
|
const result = { data: model };
|
|
165
180
|
const opts = options ?? {};
|
|
@@ -208,6 +223,42 @@ function validate(schema, data, strict) {
|
|
|
208
223
|
}
|
|
209
224
|
}
|
|
210
225
|
|
|
226
|
+
//#endregion
|
|
227
|
+
//#region src/yayson/safe.ts
|
|
228
|
+
/**
|
|
229
|
+
* Defenses against prototype pollution, since yayson keys lookup tables and
|
|
230
|
+
* model objects by strings from untrusted documents (`type`, `id`, member
|
|
231
|
+
* names). Per MDN's guidance
|
|
232
|
+
* (https://developer.mozilla.org/en-US/docs/Web/Security/Attacks/Prototype_pollution):
|
|
233
|
+
* caches use null-prototype objects, and "__proto__"/"constructor"/"prototype"
|
|
234
|
+
* are rejected as member names.
|
|
235
|
+
*/
|
|
236
|
+
/** Null-prototype lookup table (MDN's `Object.create(null)`, never `obj.__proto__`). */
|
|
237
|
+
function safeObject() {
|
|
238
|
+
return Object.create(null);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Normalize a caller-supplied cache to a null-prototype object, so a public
|
|
242
|
+
* `models` argument that happens to be a plain `{}` can't let type="__proto__"
|
|
243
|
+
* resolve through Object.prototype. Already-null-prototype caches pass through
|
|
244
|
+
* unchanged (preserving identity/memoization).
|
|
245
|
+
*/
|
|
246
|
+
function safeCache(cache) {
|
|
247
|
+
if (cache === void 0) return safeObject();
|
|
248
|
+
if (Object.getPrototypeOf(cache) === null) return cache;
|
|
249
|
+
const safe = safeObject();
|
|
250
|
+
Object.assign(safe, cache);
|
|
251
|
+
return safe;
|
|
252
|
+
}
|
|
253
|
+
const UNSAFE_KEYS = new Set([
|
|
254
|
+
"__proto__",
|
|
255
|
+
"constructor",
|
|
256
|
+
"prototype"
|
|
257
|
+
]);
|
|
258
|
+
function isUnsafeKey(key) {
|
|
259
|
+
return UNSAFE_KEYS.has(key);
|
|
260
|
+
}
|
|
261
|
+
|
|
211
262
|
//#endregion
|
|
212
263
|
//#region src/yayson/store.ts
|
|
213
264
|
function hasId(model) {
|
|
@@ -249,7 +300,7 @@ var Store = class Store {
|
|
|
249
300
|
return stub;
|
|
250
301
|
}
|
|
251
302
|
#createModel(resource, options) {
|
|
252
|
-
const models = options?.models ??
|
|
303
|
+
const models = options?.models ?? safeObject();
|
|
253
304
|
const model = { ...resource.attributes || {} };
|
|
254
305
|
if (resource.id != null) model.id = resource.id;
|
|
255
306
|
const type = resource.type;
|
|
@@ -258,7 +309,7 @@ var Store = class Store {
|
|
|
258
309
|
if (resource.links != null) model[LINKS] = resource.links;
|
|
259
310
|
if (hasId(model)) {
|
|
260
311
|
const idStr = String(model.id);
|
|
261
|
-
if (!models[type]) models[type] =
|
|
312
|
+
if (!models[type]) models[type] = safeObject();
|
|
262
313
|
if (!models[type][idStr]) models[type][idStr] = model;
|
|
263
314
|
}
|
|
264
315
|
if (resource.relationships != null) {
|
|
@@ -271,7 +322,8 @@ var Store = class Store {
|
|
|
271
322
|
}
|
|
272
323
|
#resolveRelationships(model, relationships, resolver, options) {
|
|
273
324
|
const includeRelMeta = options?.includeRelMeta ?? true;
|
|
274
|
-
for (const key
|
|
325
|
+
for (const key of Object.keys(relationships)) {
|
|
326
|
+
if (isUnsafeKey(key)) continue;
|
|
275
327
|
const { data, links, meta } = relationships[key];
|
|
276
328
|
model[key] = null;
|
|
277
329
|
if (data == null && links == null) continue;
|
|
@@ -293,7 +345,7 @@ var Store = class Store {
|
|
|
293
345
|
}
|
|
294
346
|
}
|
|
295
347
|
toModel(rec, type, models) {
|
|
296
|
-
const model = this.#createModel(rec, { models });
|
|
348
|
+
const model = this.#createModel(rec, { models: safeCache(models) });
|
|
297
349
|
if (this.schemas && this.schemas[rec.type]) {
|
|
298
350
|
const schema = this.schemas[rec.type];
|
|
299
351
|
const result = validate(schema, model, this.strict);
|
|
@@ -326,14 +378,14 @@ var Store = class Store {
|
|
|
326
378
|
return this.toModel(rec, type, models);
|
|
327
379
|
}
|
|
328
380
|
find(type, id, models) {
|
|
329
|
-
return this.#findModel(type, id, models
|
|
381
|
+
return this.#findModel(type, id, safeCache(models));
|
|
330
382
|
}
|
|
331
383
|
findAll(type, models) {
|
|
332
|
-
const modelsObj = models
|
|
384
|
+
const modelsObj = safeCache(models);
|
|
333
385
|
const recs = this.findRecords(type);
|
|
334
386
|
if (recs == null) return [];
|
|
335
387
|
recs.forEach((rec) => {
|
|
336
|
-
if (!modelsObj[type]) modelsObj[type] =
|
|
388
|
+
if (!modelsObj[type]) modelsObj[type] = safeObject();
|
|
337
389
|
return this.toModel(rec, type, modelsObj);
|
|
338
390
|
});
|
|
339
391
|
return Object.values(modelsObj[type] || {});
|
|
@@ -382,7 +434,7 @@ var Store = class Store {
|
|
|
382
434
|
try {
|
|
383
435
|
syncData(body.included);
|
|
384
436
|
const recs = syncData(body.data);
|
|
385
|
-
const models =
|
|
437
|
+
const models = safeObject();
|
|
386
438
|
const result = recs.map((rec) => this.toModel(rec, rec.type, models));
|
|
387
439
|
if (body.meta != null) result[META] = body.meta;
|
|
388
440
|
return result;
|
|
@@ -448,5 +500,5 @@ function yayson(options) {
|
|
|
448
500
|
var yayson_default = yayson;
|
|
449
501
|
|
|
450
502
|
//#endregion
|
|
451
|
-
export { validate as n, filterByFields as r, yayson_default as t };
|
|
452
|
-
//# sourceMappingURL=yayson-
|
|
503
|
+
export { validate as a, safeObject as i, isUnsafeKey as n, filterByFields as o, safeCache as r, yayson_default as t };
|
|
504
|
+
//# sourceMappingURL=yayson-RzT9zsdx.mjs.map
|