@warp-drive/schema-record 0.0.0-beta.8 → 0.0.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/hooks.js +1 -1
- package/dist/hooks.js.map +1 -1
- package/dist/record.js +212 -119
- package/dist/record.js.map +1 -1
- package/dist/schema.js +6 -6
- package/dist/schema.js.map +1 -1
- package/package.json +37 -26
- package/unstable-preview-types/-private/compute.d.ts +7 -5
- package/unstable-preview-types/-private/compute.d.ts.map +1 -1
- package/unstable-preview-types/-private/managed-array.d.ts +5 -3
- package/unstable-preview-types/-private/managed-array.d.ts.map +1 -1
- package/unstable-preview-types/-private/managed-object.d.ts +6 -6
- package/unstable-preview-types/-private/managed-object.d.ts.map +1 -1
- package/unstable-preview-types/index.d.ts +4 -4
- package/unstable-preview-types/record.d.ts +1 -1
- package/unstable-preview-types/record.d.ts.map +1 -1
package/dist/hooks.js
CHANGED
|
@@ -3,7 +3,7 @@ import { E as Editable, L as Legacy, D as Destroy } from "./symbols-DqoS4ybV.js"
|
|
|
3
3
|
function instantiateRecord(store, identifier, createArgs) {
|
|
4
4
|
const schema = store.schema;
|
|
5
5
|
const isLegacy = schema.resource(identifier)?.legacy ?? false;
|
|
6
|
-
const isEditable = isLegacy ||
|
|
6
|
+
const isEditable = isLegacy || store.cache.isNew(identifier);
|
|
7
7
|
const record = new SchemaRecord(store, identifier, {
|
|
8
8
|
[Editable]: isEditable,
|
|
9
9
|
[Legacy]: isLegacy
|
package/dist/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sources":["../src/hooks.ts"],"sourcesContent":["import type Store from '@ember-data/store';\nimport type { StableRecordIdentifier } from '@warp-drive/core-types';\n\nimport { SchemaRecord } from './record';\nimport type { SchemaService } from './schema';\nimport { Destroy, Editable, Legacy } from './symbols';\n\nexport function instantiateRecord(\n store: Store,\n identifier: StableRecordIdentifier,\n createArgs?: Record<string, unknown>\n): SchemaRecord {\n const schema = store.schema as unknown as SchemaService;\n const isLegacy = schema.resource(identifier)?.legacy ?? false;\n const isEditable = isLegacy ||
|
|
1
|
+
{"version":3,"file":"hooks.js","sources":["../src/hooks.ts"],"sourcesContent":["import type Store from '@ember-data/store';\nimport type { StableRecordIdentifier } from '@warp-drive/core-types';\n\nimport { SchemaRecord } from './record';\nimport type { SchemaService } from './schema';\nimport { Destroy, Editable, Legacy } from './symbols';\n\nexport function instantiateRecord(\n store: Store,\n identifier: StableRecordIdentifier,\n createArgs?: Record<string, unknown>\n): SchemaRecord {\n const schema = store.schema as unknown as SchemaService;\n const isLegacy = schema.resource(identifier)?.legacy ?? false;\n const isEditable = isLegacy || store.cache.isNew(identifier);\n const record = new SchemaRecord(store, identifier, {\n [Editable]: isEditable,\n [Legacy]: isLegacy,\n });\n\n if (createArgs) {\n Object.assign(record, createArgs);\n }\n\n return record;\n}\n\nexport function teardownRecord(record: SchemaRecord): void {\n record[Destroy]();\n}\n"],"names":["instantiateRecord","store","identifier","createArgs","schema","isLegacy","resource","legacy","isEditable","cache","isNew","record","SchemaRecord","Editable","Legacy","Object","assign","teardownRecord","Destroy"],"mappings":";;;AAOO,SAASA,iBAAiBA,CAC/BC,KAAY,EACZC,UAAkC,EAClCC,UAAoC,EACtB;AACd,EAAA,MAAMC,MAAM,GAAGH,KAAK,CAACG,MAAkC,CAAA;EACvD,MAAMC,QAAQ,GAAGD,MAAM,CAACE,QAAQ,CAACJ,UAAU,CAAC,EAAEK,MAAM,IAAI,KAAK,CAAA;EAC7D,MAAMC,UAAU,GAAGH,QAAQ,IAAIJ,KAAK,CAACQ,KAAK,CAACC,KAAK,CAACR,UAAU,CAAC,CAAA;EAC5D,MAAMS,MAAM,GAAG,IAAIC,YAAY,CAACX,KAAK,EAAEC,UAAU,EAAE;IACjD,CAACW,QAAQ,GAAGL,UAAU;AACtB,IAAA,CAACM,MAAM,GAAGT,QAAAA;AACZ,GAAC,CAAC,CAAA;AAEF,EAAA,IAAIF,UAAU,EAAE;AACdY,IAAAA,MAAM,CAACC,MAAM,CAACL,MAAM,EAAER,UAAU,CAAC,CAAA;AACnC,GAAA;AAEA,EAAA,OAAOQ,MAAM,CAAA;AACf,CAAA;AAEO,SAASM,cAAcA,CAACN,MAAoB,EAAQ;AACzDA,EAAAA,MAAM,CAACO,OAAO,CAAC,EAAE,CAAA;AACnB;;;;"}
|
package/dist/record.js
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
1
|
import { macroCondition, getGlobalConfig, dependencySatisfies, importSync } from '@embroider/macros';
|
|
2
|
+
import { setRecordIdentifier, recordIdentifierFor } from '@ember-data/store/-private';
|
|
2
3
|
import { createSignal, subscribe, defineSignal, peekSignal, getSignal, Signals, entangleSignal, addToTransaction } from '@ember-data/tracking/-private';
|
|
3
4
|
import { STRUCTURED } from '@warp-drive/core-types/request';
|
|
4
5
|
import { RecordStore } from '@warp-drive/core-types/symbols';
|
|
5
6
|
import { getOrSetGlobal } from '@warp-drive/core-types/-private';
|
|
6
|
-
import { S as SOURCE, A as ARRAY_SIGNAL,
|
|
7
|
+
import { S as SOURCE, A as ARRAY_SIGNAL, E as Editable, L as Legacy, I as Identifier, P as Parent, O as OBJECT_SIGNAL, a as EmbeddedPath, D as Destroy, C as Checkout, b as EmbeddedType } from "./symbols-DqoS4ybV.js";
|
|
7
8
|
const ARRAY_GETTER_METHODS = new Set([Symbol.iterator, 'concat', 'entries', 'every', 'fill', 'filter', 'find', 'findIndex', 'flat', 'flatMap', 'forEach', 'includes', 'indexOf', 'join', 'keys', 'lastIndexOf', 'map', 'reduce', 'reduceRight', 'slice', 'some', 'values']);
|
|
8
9
|
// const ARRAY_SETTER_METHODS = new Set<KeyType>(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
|
|
9
10
|
const SYNC_PROPS = new Set(['[]', 'length']);
|
|
10
11
|
function isArrayGetter(prop) {
|
|
11
12
|
return ARRAY_GETTER_METHODS.has(prop);
|
|
12
13
|
}
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
const ARRAY_SETTER_METHODS = new Set(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
|
|
15
|
+
function isArraySetter(prop) {
|
|
16
|
+
return ARRAY_SETTER_METHODS.has(prop);
|
|
17
|
+
}
|
|
18
|
+
|
|
16
19
|
// function isSelfProp<T extends object>(self: T, prop: KeyType): prop is keyof T {
|
|
17
20
|
// return prop in self;
|
|
18
21
|
// }
|
|
@@ -48,14 +51,16 @@ function safeForEach(instance, arr, store, callback, target) {
|
|
|
48
51
|
}
|
|
49
52
|
class ManagedArray {
|
|
50
53
|
[SOURCE];
|
|
51
|
-
constructor(store, schema, cache, field, data,
|
|
54
|
+
constructor(store, schema, cache, field, data, identifier, path, owner, isSchemaArray, editable, legacy) {
|
|
52
55
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
53
56
|
const self = this;
|
|
54
57
|
this[SOURCE] = data?.slice();
|
|
55
58
|
this[ARRAY_SIGNAL] = createSignal(this, 'length');
|
|
59
|
+
const IS_EDITABLE = this[Editable] = editable ?? false;
|
|
60
|
+
this[Legacy] = legacy;
|
|
56
61
|
const _SIGNAL = this[ARRAY_SIGNAL];
|
|
57
62
|
const boundFns = new Map();
|
|
58
|
-
this.
|
|
63
|
+
this.identifier = identifier;
|
|
59
64
|
this.path = path;
|
|
60
65
|
this.owner = owner;
|
|
61
66
|
let transaction = false;
|
|
@@ -74,8 +79,8 @@ class ManagedArray {
|
|
|
74
79
|
if (prop === ARRAY_SIGNAL) {
|
|
75
80
|
return _SIGNAL;
|
|
76
81
|
}
|
|
77
|
-
if (prop === '
|
|
78
|
-
return self.
|
|
82
|
+
if (prop === 'identifier') {
|
|
83
|
+
return self.identifier;
|
|
79
84
|
}
|
|
80
85
|
if (prop === 'owner') {
|
|
81
86
|
return self.owner;
|
|
@@ -84,7 +89,7 @@ class ManagedArray {
|
|
|
84
89
|
if (_SIGNAL.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
|
|
85
90
|
_SIGNAL.t = false;
|
|
86
91
|
_SIGNAL.shouldReset = false;
|
|
87
|
-
const newData = cache.getAttr(
|
|
92
|
+
const newData = cache.getAttr(identifier, path);
|
|
88
93
|
if (newData && newData !== self[SOURCE]) {
|
|
89
94
|
self[SOURCE].length = 0;
|
|
90
95
|
self[SOURCE].push(...newData);
|
|
@@ -144,7 +149,8 @@ class ManagedArray {
|
|
|
144
149
|
// same object reference from cache should result in same SchemaRecord
|
|
145
150
|
// embedded object.
|
|
146
151
|
recordPath.push(index);
|
|
147
|
-
|
|
152
|
+
const recordIdentifier = self.owner[Identifier] || self.owner[Parent];
|
|
153
|
+
record = new SchemaRecord(store, recordIdentifier, {
|
|
148
154
|
[Editable]: self.owner[Editable],
|
|
149
155
|
[Legacy]: self.owner[Legacy]
|
|
150
156
|
}, true, field.type, recordPath);
|
|
@@ -195,12 +201,36 @@ class ManagedArray {
|
|
|
195
201
|
}
|
|
196
202
|
return fn;
|
|
197
203
|
}
|
|
204
|
+
if (isArraySetter(prop)) {
|
|
205
|
+
let fn = boundFns.get(prop);
|
|
206
|
+
if (fn === undefined) {
|
|
207
|
+
fn = function () {
|
|
208
|
+
if (!IS_EDITABLE) {
|
|
209
|
+
throw new Error(`Mutating this array via ${String(prop)} is not allowed because the record is not editable`);
|
|
210
|
+
}
|
|
211
|
+
subscribe(_SIGNAL);
|
|
212
|
+
transaction = true;
|
|
213
|
+
const result = Reflect.apply(target[prop], receiver, arguments);
|
|
214
|
+
transaction = false;
|
|
215
|
+
return result;
|
|
216
|
+
};
|
|
217
|
+
boundFns.set(prop, fn);
|
|
218
|
+
}
|
|
219
|
+
return fn;
|
|
220
|
+
}
|
|
198
221
|
return Reflect.get(target, prop, receiver);
|
|
199
222
|
},
|
|
200
223
|
set(target, prop, value, receiver) {
|
|
201
|
-
if (
|
|
224
|
+
if (!IS_EDITABLE) {
|
|
225
|
+
let errorPath = identifier.type;
|
|
226
|
+
if (path) {
|
|
227
|
+
errorPath = path[path.length - 1];
|
|
228
|
+
}
|
|
229
|
+
throw new Error(`Cannot set ${String(prop)} on ${errorPath} because the record is not editable`);
|
|
230
|
+
}
|
|
231
|
+
if (prop === 'identifier') {
|
|
202
232
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
203
|
-
self.
|
|
233
|
+
self.identifier = value;
|
|
204
234
|
return true;
|
|
205
235
|
}
|
|
206
236
|
if (prop === 'owner') {
|
|
@@ -211,7 +241,7 @@ class ManagedArray {
|
|
|
211
241
|
const reflect = Reflect.set(target, prop, value, receiver);
|
|
212
242
|
if (reflect) {
|
|
213
243
|
if (!field.type) {
|
|
214
|
-
cache.setAttr(
|
|
244
|
+
cache.setAttr(identifier, path, self[SOURCE]);
|
|
215
245
|
_SIGNAL.shouldReset = true;
|
|
216
246
|
return true;
|
|
217
247
|
}
|
|
@@ -219,11 +249,11 @@ class ManagedArray {
|
|
|
219
249
|
if (!isSchemaArray) {
|
|
220
250
|
const transform = schema.transformation(field);
|
|
221
251
|
if (!transform) {
|
|
222
|
-
throw new Error(`No '${field.type}' transform defined for use by ${
|
|
252
|
+
throw new Error(`No '${field.type}' transform defined for use by ${identifier.type}.${String(prop)}`);
|
|
223
253
|
}
|
|
224
254
|
rawValue = self[SOURCE].map(item => transform.serialize(item, field.options ?? null, self.owner));
|
|
225
255
|
}
|
|
226
|
-
cache.setAttr(
|
|
256
|
+
cache.setAttr(identifier, path, rawValue);
|
|
227
257
|
_SIGNAL.shouldReset = true;
|
|
228
258
|
}
|
|
229
259
|
return reflect;
|
|
@@ -232,41 +262,53 @@ class ManagedArray {
|
|
|
232
262
|
return proxy;
|
|
233
263
|
}
|
|
234
264
|
}
|
|
235
|
-
const
|
|
265
|
+
const ObjectSymbols = new Set([OBJECT_SIGNAL, Parent, SOURCE, Editable, EmbeddedPath]);
|
|
266
|
+
|
|
267
|
+
// const ignoredGlobalFields = new Set<string>(['setInterval', 'nodeType', 'nodeName', 'length', 'document', STRUCTURED]);
|
|
268
|
+
|
|
236
269
|
class ManagedObject {
|
|
237
|
-
|
|
238
|
-
constructor(store, schema, cache, field, data, address, key, owner, isSchemaObject) {
|
|
270
|
+
constructor(schema, cache, field, data, identifier, path, owner, editable, legacy) {
|
|
239
271
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
240
272
|
const self = this;
|
|
241
273
|
this[SOURCE] = {
|
|
242
274
|
...data
|
|
243
275
|
};
|
|
244
276
|
this[OBJECT_SIGNAL] = createSignal(this, 'length');
|
|
277
|
+
this[Editable] = editable;
|
|
278
|
+
this[Legacy] = legacy;
|
|
279
|
+
this[Parent] = identifier;
|
|
280
|
+
this[EmbeddedPath] = path;
|
|
245
281
|
const _SIGNAL = this[OBJECT_SIGNAL];
|
|
246
|
-
// const boundFns = new Map<KeyType, ProxiedMethod>();
|
|
247
|
-
this.address = address;
|
|
248
|
-
this.key = key;
|
|
249
|
-
this.owner = owner;
|
|
250
282
|
const proxy = new Proxy(this[SOURCE], {
|
|
283
|
+
ownKeys() {
|
|
284
|
+
return Object.keys(self[SOURCE]);
|
|
285
|
+
},
|
|
286
|
+
has(target, prop) {
|
|
287
|
+
return prop in self[SOURCE];
|
|
288
|
+
},
|
|
289
|
+
getOwnPropertyDescriptor(target, prop) {
|
|
290
|
+
return {
|
|
291
|
+
writable: editable,
|
|
292
|
+
enumerable: true,
|
|
293
|
+
configurable: true
|
|
294
|
+
};
|
|
295
|
+
},
|
|
251
296
|
get(target, prop, receiver) {
|
|
252
|
-
if (prop
|
|
253
|
-
return
|
|
297
|
+
if (ObjectSymbols.has(prop)) {
|
|
298
|
+
return self[prop];
|
|
254
299
|
}
|
|
255
|
-
if (prop ===
|
|
256
|
-
return
|
|
257
|
-
}
|
|
258
|
-
if (prop === 'key') {
|
|
259
|
-
return self.key;
|
|
260
|
-
}
|
|
261
|
-
if (prop === 'owner') {
|
|
262
|
-
return self.owner;
|
|
300
|
+
if (prop === Symbol.toPrimitive) {
|
|
301
|
+
return null;
|
|
263
302
|
}
|
|
264
303
|
if (prop === Symbol.toStringTag) {
|
|
265
|
-
return `ManagedObject<${
|
|
304
|
+
return `ManagedObject<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
305
|
+
}
|
|
306
|
+
if (prop === 'constructor') {
|
|
307
|
+
return Object;
|
|
266
308
|
}
|
|
267
309
|
if (prop === 'toString') {
|
|
268
310
|
return function () {
|
|
269
|
-
return `ManagedObject<${
|
|
311
|
+
return `ManagedObject<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
270
312
|
};
|
|
271
313
|
}
|
|
272
314
|
if (prop === 'toHTML') {
|
|
@@ -277,71 +319,42 @@ class ManagedObject {
|
|
|
277
319
|
if (_SIGNAL.shouldReset) {
|
|
278
320
|
_SIGNAL.t = false;
|
|
279
321
|
_SIGNAL.shouldReset = false;
|
|
280
|
-
let newData = cache.getAttr(
|
|
322
|
+
let newData = cache.getAttr(identifier, path);
|
|
281
323
|
if (newData && newData !== self[SOURCE]) {
|
|
282
|
-
if (
|
|
324
|
+
if (field.type) {
|
|
283
325
|
const transform = schema.transformation(field);
|
|
284
|
-
newData = transform.hydrate(newData, field.options ?? null,
|
|
326
|
+
newData = transform.hydrate(newData, field.options ?? null, owner);
|
|
285
327
|
}
|
|
286
328
|
self[SOURCE] = {
|
|
287
329
|
...newData
|
|
288
330
|
}; // Add type assertion for newData
|
|
289
331
|
}
|
|
290
332
|
}
|
|
291
|
-
if (isSchemaObject) {
|
|
292
|
-
const fields = schema.fields({
|
|
293
|
-
type: field.type
|
|
294
|
-
});
|
|
295
|
-
// TODO: is there a better way to do this?
|
|
296
|
-
if (typeof prop === 'string' && !ignoredGlobalFields.has(prop) && !fields.has(prop)) {
|
|
297
|
-
throw new Error(`Field ${prop} does not exist on schema object ${field.type}`);
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
333
|
if (prop in self[SOURCE]) {
|
|
301
|
-
|
|
302
|
-
subscribe(_SIGNAL);
|
|
303
|
-
}
|
|
334
|
+
subscribe(_SIGNAL);
|
|
304
335
|
return self[SOURCE][prop];
|
|
305
336
|
}
|
|
306
337
|
return Reflect.get(target, prop, receiver);
|
|
307
338
|
},
|
|
308
339
|
set(target, prop, value, receiver) {
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
return true;
|
|
313
|
-
}
|
|
314
|
-
if (prop === 'key') {
|
|
315
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
316
|
-
self.key = value;
|
|
317
|
-
return true;
|
|
318
|
-
}
|
|
319
|
-
if (prop === 'owner') {
|
|
320
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
321
|
-
self.owner = value;
|
|
322
|
-
return true;
|
|
323
|
-
}
|
|
324
|
-
if (isSchemaObject) {
|
|
325
|
-
const fields = schema.fields({
|
|
326
|
-
type: field.type
|
|
327
|
-
});
|
|
328
|
-
if (typeof prop === 'string' && !ignoredGlobalFields.has(prop) && !fields.has(prop)) {
|
|
329
|
-
throw new Error(`Field ${prop} does not exist on schema object ${field.type}`);
|
|
340
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
341
|
+
if (!test) {
|
|
342
|
+
throw new Error(`Cannot set read-only property '${String(prop)}' on ManagedObject`);
|
|
330
343
|
}
|
|
331
|
-
}
|
|
344
|
+
})(editable) : {};
|
|
332
345
|
const reflect = Reflect.set(target, prop, value, receiver);
|
|
333
|
-
if (reflect) {
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
346
|
+
if (!reflect) {
|
|
347
|
+
return false;
|
|
348
|
+
}
|
|
349
|
+
if (!field.type) {
|
|
350
|
+
cache.setAttr(identifier, path, self[SOURCE]);
|
|
351
|
+
} else {
|
|
339
352
|
const transform = schema.transformation(field);
|
|
340
|
-
const val = transform.serialize(self[SOURCE], field.options ?? null,
|
|
341
|
-
cache.setAttr(
|
|
342
|
-
_SIGNAL.shouldReset = true;
|
|
353
|
+
const val = transform.serialize(self[SOURCE], field.options ?? null, owner);
|
|
354
|
+
cache.setAttr(identifier, path, val);
|
|
343
355
|
}
|
|
344
|
-
|
|
356
|
+
_SIGNAL.shouldReset = true;
|
|
357
|
+
return true;
|
|
345
358
|
}
|
|
346
359
|
});
|
|
347
360
|
return proxy;
|
|
@@ -377,7 +390,7 @@ function computeField(schema, cache, record, identifier, field, prop) {
|
|
|
377
390
|
const transform = schema.transformation(field);
|
|
378
391
|
return transform.hydrate(rawValue, field.options ?? null, record);
|
|
379
392
|
}
|
|
380
|
-
function computeArray(store, schema, cache, record, identifier, field, path, isSchemaArray
|
|
393
|
+
function computeArray(store, schema, cache, record, identifier, field, path, isSchemaArray, editable, legacy) {
|
|
381
394
|
// the thing we hand out needs to know its owner and path in a private manner
|
|
382
395
|
// its "address" is the parent identifier (identifier) + field name (field.name)
|
|
383
396
|
// in the nested object case field name here is the full dot path from root resource to this value
|
|
@@ -396,7 +409,7 @@ function computeArray(store, schema, cache, record, identifier, field, path, isS
|
|
|
396
409
|
if (!rawValue) {
|
|
397
410
|
return null;
|
|
398
411
|
}
|
|
399
|
-
managedArray = new ManagedArray(store, schema, cache, field, rawValue, identifier, path, record, isSchemaArray);
|
|
412
|
+
managedArray = new ManagedArray(store, schema, cache, field, rawValue, identifier, path, record, isSchemaArray, editable, legacy);
|
|
400
413
|
if (!managedArrayMapForRecord) {
|
|
401
414
|
ManagedArrayMap.set(record, new Map([[field, managedArray]]));
|
|
402
415
|
} else {
|
|
@@ -405,7 +418,7 @@ function computeArray(store, schema, cache, record, identifier, field, path, isS
|
|
|
405
418
|
}
|
|
406
419
|
return managedArray;
|
|
407
420
|
}
|
|
408
|
-
function computeObject(
|
|
421
|
+
function computeObject(schema, cache, record, identifier, field, path, editable, legacy) {
|
|
409
422
|
const managedObjectMapForRecord = ManagedObjectMap.get(record);
|
|
410
423
|
let managedObject;
|
|
411
424
|
if (managedObjectMapForRecord) {
|
|
@@ -414,17 +427,15 @@ function computeObject(store, schema, cache, record, identifier, field, prop, is
|
|
|
414
427
|
if (managedObject) {
|
|
415
428
|
return managedObject;
|
|
416
429
|
} else {
|
|
417
|
-
let rawValue = cache.getAttr(identifier,
|
|
430
|
+
let rawValue = cache.getAttr(identifier, path);
|
|
418
431
|
if (!rawValue) {
|
|
419
432
|
return null;
|
|
420
433
|
}
|
|
421
|
-
if (field.
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
rawValue = transform.hydrate(rawValue, field.options ?? null, record);
|
|
425
|
-
}
|
|
434
|
+
if (field.type) {
|
|
435
|
+
const transform = schema.transformation(field);
|
|
436
|
+
rawValue = transform.hydrate(rawValue, field.options ?? null, record);
|
|
426
437
|
}
|
|
427
|
-
managedObject = new ManagedObject(
|
|
438
|
+
managedObject = new ManagedObject(schema, cache, field, rawValue, identifier, path, record, editable, legacy);
|
|
428
439
|
if (!managedObjectMapForRecord) {
|
|
429
440
|
ManagedObjectMap.set(record, new Map([[field, managedObject]]));
|
|
430
441
|
} else {
|
|
@@ -433,6 +444,32 @@ function computeObject(store, schema, cache, record, identifier, field, prop, is
|
|
|
433
444
|
}
|
|
434
445
|
return managedObject;
|
|
435
446
|
}
|
|
447
|
+
function computeSchemaObject(store, cache, record, identifier, field, path, legacy, editable) {
|
|
448
|
+
const schemaObjectMapForRecord = ManagedObjectMap.get(record);
|
|
449
|
+
let schemaObject;
|
|
450
|
+
if (schemaObjectMapForRecord) {
|
|
451
|
+
schemaObject = schemaObjectMapForRecord.get(field);
|
|
452
|
+
}
|
|
453
|
+
if (schemaObject) {
|
|
454
|
+
return schemaObject;
|
|
455
|
+
} else {
|
|
456
|
+
const rawValue = cache.getAttr(identifier, path);
|
|
457
|
+
if (!rawValue) {
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
const embeddedPath = path.slice();
|
|
461
|
+
schemaObject = new SchemaRecord(store, identifier, {
|
|
462
|
+
[Editable]: editable,
|
|
463
|
+
[Legacy]: legacy
|
|
464
|
+
}, true, field.type, embeddedPath);
|
|
465
|
+
}
|
|
466
|
+
if (!schemaObjectMapForRecord) {
|
|
467
|
+
ManagedObjectMap.set(record, new Map([[field, schemaObject]]));
|
|
468
|
+
} else {
|
|
469
|
+
schemaObjectMapForRecord.set(field, schemaObject);
|
|
470
|
+
}
|
|
471
|
+
return schemaObject;
|
|
472
|
+
}
|
|
436
473
|
function computeAttribute(cache, identifier, prop) {
|
|
437
474
|
return cache.getAttr(identifier, prop);
|
|
438
475
|
}
|
|
@@ -494,12 +531,13 @@ function computeResource(store, cache, parent, identifier, field, prop) {
|
|
|
494
531
|
}
|
|
495
532
|
const HAS_MODEL_PACKAGE = dependencySatisfies('@ember-data/model', '*');
|
|
496
533
|
const getLegacySupport = macroCondition(dependencySatisfies('@ember-data/model', '*')) ? importSync('@ember-data/model/-private').lookupLegacySupport : null;
|
|
497
|
-
const IgnoredGlobalFields = new Set(['length', 'nodeType', 'then', 'setInterval', STRUCTURED]);
|
|
534
|
+
const IgnoredGlobalFields = new Set(['length', 'nodeType', 'then', 'setInterval', 'document', STRUCTURED]);
|
|
498
535
|
const symbolList = [Destroy, RecordStore, Identifier, Editable, Parent, Checkout, Legacy, Signals, EmbeddedPath, EmbeddedType];
|
|
499
536
|
const RecordSymbols = new Set(symbolList);
|
|
500
537
|
function isPathMatch(a, b) {
|
|
501
538
|
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
502
539
|
}
|
|
540
|
+
const Editables = new WeakMap();
|
|
503
541
|
class SchemaRecord {
|
|
504
542
|
constructor(store, identifier, Mode, isEmbedded = false, embeddedType = null, embeddedPath = null) {
|
|
505
543
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
@@ -550,6 +588,7 @@ class SchemaRecord {
|
|
|
550
588
|
case 'field':
|
|
551
589
|
case 'attribute':
|
|
552
590
|
case 'resource':
|
|
591
|
+
case 'alias':
|
|
553
592
|
case 'belongsTo':
|
|
554
593
|
case 'hasMany':
|
|
555
594
|
case 'collection':
|
|
@@ -576,6 +615,11 @@ class SchemaRecord {
|
|
|
576
615
|
return `SchemaRecord<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
577
616
|
};
|
|
578
617
|
}
|
|
618
|
+
if (prop === 'toHTML') {
|
|
619
|
+
return function () {
|
|
620
|
+
return `<div>SchemaRecord<${identifier.type}:${identifier.id} (${identifier.lid})></div>`;
|
|
621
|
+
};
|
|
622
|
+
}
|
|
579
623
|
if (prop === Symbol.toPrimitive) {
|
|
580
624
|
return null;
|
|
581
625
|
}
|
|
@@ -589,10 +633,8 @@ class SchemaRecord {
|
|
|
589
633
|
// for its own usage.
|
|
590
634
|
// _, @, $, *
|
|
591
635
|
|
|
592
|
-
const
|
|
593
|
-
|
|
594
|
-
const field = prop === identityField?.name ? identityField : fields.get(prop);
|
|
595
|
-
if (!field) {
|
|
636
|
+
const maybeField = prop === identityField?.name ? identityField : fields.get(prop);
|
|
637
|
+
if (!maybeField) {
|
|
596
638
|
if (IgnoredGlobalFields.has(prop)) {
|
|
597
639
|
return undefined;
|
|
598
640
|
}
|
|
@@ -603,8 +645,24 @@ class SchemaRecord {
|
|
|
603
645
|
if (typeof prop === 'symbol') {
|
|
604
646
|
return undefined;
|
|
605
647
|
}
|
|
606
|
-
|
|
648
|
+
let type = identifier.type;
|
|
649
|
+
if (isEmbedded) {
|
|
650
|
+
type = embeddedType;
|
|
651
|
+
}
|
|
652
|
+
throw new Error(`No field named ${String(prop)} on ${type}`);
|
|
607
653
|
}
|
|
654
|
+
const field = maybeField.kind === 'alias' ? maybeField.options : maybeField;
|
|
655
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
656
|
+
if (!test) {
|
|
657
|
+
throw new Error(`Alias fields cannot alias '@id' '@local' '@hash' or 'derived' fields`);
|
|
658
|
+
}
|
|
659
|
+
})(maybeField.kind !== 'alias' || !['@id', '@local', '@hash', 'derived'].includes(maybeField.options.kind)) : {};
|
|
660
|
+
const propArray = isEmbedded ? embeddedPath.slice() : [];
|
|
661
|
+
// we use the field.name instead of prop here because we want to use the cache-path not
|
|
662
|
+
// the record path.
|
|
663
|
+
propArray.push(field.name);
|
|
664
|
+
// propArray.push(prop as string);
|
|
665
|
+
|
|
608
666
|
switch (field.kind) {
|
|
609
667
|
case '@id':
|
|
610
668
|
entangleSignal(signals, receiver, '@identity');
|
|
@@ -641,7 +699,7 @@ class SchemaRecord {
|
|
|
641
699
|
return computeDerivation(schema, receiver, identifier, field, prop);
|
|
642
700
|
case 'schema-array':
|
|
643
701
|
entangleSignal(signals, receiver, field.name);
|
|
644
|
-
return computeArray(store, schema, cache, target, identifier, field, propArray, true);
|
|
702
|
+
return computeArray(store, schema, cache, target, identifier, field, propArray, true, Mode[Editable], Mode[Legacy]);
|
|
645
703
|
case 'array':
|
|
646
704
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
647
705
|
if (!test) {
|
|
@@ -649,13 +707,16 @@ class SchemaRecord {
|
|
|
649
707
|
}
|
|
650
708
|
})(!target[Legacy]) : {};
|
|
651
709
|
entangleSignal(signals, receiver, field.name);
|
|
652
|
-
return computeArray(store, schema, cache, target, identifier, field, propArray);
|
|
653
|
-
case 'schema-object':
|
|
654
|
-
// validate any access off of schema, no transform to run
|
|
655
|
-
// use raw cache value as the object to manage
|
|
656
|
-
entangleSignal(signals, receiver, field.name);
|
|
657
|
-
return computeObject(store, schema, cache, target, identifier, field, prop, true);
|
|
710
|
+
return computeArray(store, schema, cache, target, identifier, field, propArray, false, Mode[Editable], Mode[Legacy]);
|
|
658
711
|
case 'object':
|
|
712
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
713
|
+
if (!test) {
|
|
714
|
+
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
715
|
+
}
|
|
716
|
+
})(!target[Legacy]) : {};
|
|
717
|
+
entangleSignal(signals, receiver, field.name);
|
|
718
|
+
return computeObject(schema, cache, target, identifier, field, propArray, Mode[Editable], Mode[Legacy]);
|
|
719
|
+
case 'schema-object':
|
|
659
720
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
660
721
|
if (!test) {
|
|
661
722
|
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
@@ -663,7 +724,7 @@ class SchemaRecord {
|
|
|
663
724
|
})(!target[Legacy]) : {};
|
|
664
725
|
entangleSignal(signals, receiver, field.name);
|
|
665
726
|
// run transform, then use that value as the object to manage
|
|
666
|
-
return
|
|
727
|
+
return computeSchemaObject(store, cache, target, identifier, field, propArray, Mode[Legacy], Mode[Editable]);
|
|
667
728
|
case 'belongsTo':
|
|
668
729
|
if (!HAS_MODEL_PACKAGE) {
|
|
669
730
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
@@ -710,14 +771,26 @@ class SchemaRecord {
|
|
|
710
771
|
},
|
|
711
772
|
set(target, prop, value, receiver) {
|
|
712
773
|
if (!IS_EDITABLE) {
|
|
713
|
-
|
|
774
|
+
const type = isEmbedded ? embeddedType : identifier.type;
|
|
775
|
+
throw new Error(`Cannot set ${String(prop)} on ${type} because the record is not editable`);
|
|
714
776
|
}
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
throw new Error(`There is no field named ${String(prop)} on ${identifier.type}`);
|
|
777
|
+
const maybeField = prop === identityField?.name ? identityField : fields.get(prop);
|
|
778
|
+
if (!maybeField) {
|
|
779
|
+
const type = isEmbedded ? embeddedType : identifier.type;
|
|
780
|
+
throw new Error(`There is no field named ${String(prop)} on ${type}`);
|
|
720
781
|
}
|
|
782
|
+
const field = maybeField.kind === 'alias' ? maybeField.options : maybeField;
|
|
783
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
784
|
+
if (!test) {
|
|
785
|
+
throw new Error(`Alias fields cannot alias '@id' '@local' '@hash' or 'derived' fields`);
|
|
786
|
+
}
|
|
787
|
+
})(maybeField.kind !== 'alias' || !['@id', '@local', '@hash', 'derived'].includes(maybeField.options.kind)) : {};
|
|
788
|
+
const propArray = isEmbedded ? embeddedPath.slice() : [];
|
|
789
|
+
// we use the field.name instead of prop here because we want to use the cache-path not
|
|
790
|
+
// the record path.
|
|
791
|
+
propArray.push(field.name);
|
|
792
|
+
// propArray.push(prop as string);
|
|
793
|
+
|
|
721
794
|
switch (field.kind) {
|
|
722
795
|
case '@id':
|
|
723
796
|
{
|
|
@@ -840,6 +913,11 @@ class SchemaRecord {
|
|
|
840
913
|
{
|
|
841
914
|
let newValue = value;
|
|
842
915
|
if (value !== null) {
|
|
916
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
917
|
+
if (!test) {
|
|
918
|
+
throw new Error(`Expected value to be an object`);
|
|
919
|
+
}
|
|
920
|
+
})(typeof value === 'object') : {};
|
|
843
921
|
newValue = {
|
|
844
922
|
...value
|
|
845
923
|
};
|
|
@@ -855,11 +933,11 @@ class SchemaRecord {
|
|
|
855
933
|
ManagedObjectMap.delete(target);
|
|
856
934
|
}
|
|
857
935
|
cache.setAttr(identifier, propArray, newValue);
|
|
858
|
-
const peeked = peekManagedObject(self, field);
|
|
859
|
-
if (peeked) {
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
}
|
|
936
|
+
// const peeked = peekManagedObject(self, field);
|
|
937
|
+
// if (peeked) {
|
|
938
|
+
// const objSignal = peeked[OBJECT_SIGNAL];
|
|
939
|
+
// objSignal.shouldReset = true;
|
|
940
|
+
// }
|
|
863
941
|
return true;
|
|
864
942
|
}
|
|
865
943
|
case 'derived':
|
|
@@ -974,7 +1052,7 @@ class SchemaRecord {
|
|
|
974
1052
|
addToTransaction(arrSignal);
|
|
975
1053
|
}
|
|
976
1054
|
}
|
|
977
|
-
if (field?.kind === 'object'
|
|
1055
|
+
if (field?.kind === 'object') {
|
|
978
1056
|
const peeked = peekManagedObject(self, field);
|
|
979
1057
|
if (peeked) {
|
|
980
1058
|
const objSignal = peeked[OBJECT_SIGNAL];
|
|
@@ -1060,7 +1138,22 @@ class SchemaRecord {
|
|
|
1060
1138
|
this[RecordStore].notifications.unsubscribe(this.___notifications);
|
|
1061
1139
|
}
|
|
1062
1140
|
[Checkout]() {
|
|
1063
|
-
|
|
1141
|
+
const editable = Editables.get(this);
|
|
1142
|
+
if (editable) {
|
|
1143
|
+
return Promise.resolve(editable);
|
|
1144
|
+
}
|
|
1145
|
+
const embeddedType = this[EmbeddedType];
|
|
1146
|
+
const embeddedPath = this[EmbeddedPath];
|
|
1147
|
+
const isEmbedded = embeddedType !== null && embeddedPath !== null;
|
|
1148
|
+
if (isEmbedded) {
|
|
1149
|
+
throw new Error(`Cannot checkout an embedded record (yet)`);
|
|
1150
|
+
}
|
|
1151
|
+
const editableRecord = new SchemaRecord(this[RecordStore], this[Identifier], {
|
|
1152
|
+
[Editable]: true,
|
|
1153
|
+
[Legacy]: this[Legacy]
|
|
1154
|
+
}, isEmbedded, embeddedType, embeddedPath);
|
|
1155
|
+
setRecordIdentifier(editableRecord, recordIdentifierFor(this));
|
|
1156
|
+
return Promise.resolve(editableRecord);
|
|
1064
1157
|
}
|
|
1065
1158
|
}
|
|
1066
|
-
export { Editable, Legacy, SchemaRecord };
|
|
1159
|
+
export { Checkout, Editable, Legacy, SchemaRecord };
|