@warp-drive/schema-record 0.0.0-beta.9 → 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 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 || Boolean(createArgs);
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 || Boolean(createArgs);\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","Boolean","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;AAC7D,EAAA,MAAMC,UAAU,GAAGH,QAAQ,IAAII,OAAO,CAACN,UAAU,CAAC,CAAA;EAClD,MAAMO,MAAM,GAAG,IAAIC,YAAY,CAACV,KAAK,EAAEC,UAAU,EAAE;IACjD,CAACU,QAAQ,GAAGJ,UAAU;AACtB,IAAA,CAACK,MAAM,GAAGR,QAAAA;AACZ,GAAC,CAAC,CAAA;AAEF,EAAA,IAAIF,UAAU,EAAE;AACdW,IAAAA,MAAM,CAACC,MAAM,CAACL,MAAM,EAAEP,UAAU,CAAC,CAAA;AACnC,GAAA;AAEA,EAAA,OAAOO,MAAM,CAAA;AACf,CAAA;AAEO,SAASM,cAAcA,CAACN,MAAoB,EAAQ;AACzDA,EAAAA,MAAM,CAACO,OAAO,CAAC,EAAE,CAAA;AACnB;;;;"}
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, I as Identifier, E as Editable, L as Legacy, O as OBJECT_SIGNAL, P as Parent, D as Destroy, C as Checkout, a as EmbeddedPath, b as EmbeddedType } from "./symbols-DqoS4ybV.js";
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
- // function isArraySetter<T>(prop: KeyType): prop is keyof Array<T> {
14
- // return ARRAY_SETTER_METHODS.has(prop);
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, address, path, owner, isSchemaArray) {
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.address = address;
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 === 'address') {
78
- return self.address;
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(address, path);
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
- record = new SchemaRecord(store, self.owner[Identifier], {
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 (prop === 'address') {
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.address = value;
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(address, path, self[SOURCE]);
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 ${address.type}.${String(prop)}`);
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(address, path, rawValue);
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 ignoredGlobalFields = new Set(['constructor', 'setInterval', 'nodeType', 'length']);
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
- [SOURCE];
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 === OBJECT_SIGNAL) {
253
- return _SIGNAL;
297
+ if (ObjectSymbols.has(prop)) {
298
+ return self[prop];
254
299
  }
255
- if (prop === 'address') {
256
- return self.address;
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<${address.type}:${address.id} (${address.lid})>`;
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<${address.type}:${address.id} (${address.lid})>`;
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(self.address, self.key);
322
+ let newData = cache.getAttr(identifier, path);
281
323
  if (newData && newData !== self[SOURCE]) {
282
- if (!isSchemaObject && field.type) {
324
+ if (field.type) {
283
325
  const transform = schema.transformation(field);
284
- newData = transform.hydrate(newData, field.options ?? null, self.owner);
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
- if (prop === 'address') {
310
- // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
311
- self.address = value;
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
- if (isSchemaObject || !field.type) {
335
- cache.setAttr(self.address, self.key, self[SOURCE]);
336
- _SIGNAL.shouldReset = true;
337
- return true;
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, self.owner);
341
- cache.setAttr(self.address, self.key, val);
342
- _SIGNAL.shouldReset = true;
353
+ const val = transform.serialize(self[SOURCE], field.options ?? null, owner);
354
+ cache.setAttr(identifier, path, val);
343
355
  }
344
- return reflect;
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 = false) {
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(store, schema, cache, record, identifier, field, prop, isSchemaObject = false) {
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, prop);
430
+ let rawValue = cache.getAttr(identifier, path);
418
431
  if (!rawValue) {
419
432
  return null;
420
433
  }
421
- if (field.kind === 'object') {
422
- if (field.type) {
423
- const transform = schema.transformation(field);
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(store, schema, cache, field, rawValue, identifier, prop, record, isSchemaObject);
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 propArray = isEmbedded ? embeddedPath.slice() : [];
593
- propArray.push(prop);
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
- throw new Error(`No field named ${String(prop)} on ${identifier.type}`);
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 computeObject(store, schema, cache, target, identifier, field, prop);
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
- throw new Error(`Cannot set ${String(prop)} on ${identifier.type} because the record is not editable`);
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 propArray = isEmbedded ? embeddedPath.slice() : [];
716
- propArray.push(prop);
717
- const field = prop === identityField?.name ? identityField : fields.get(prop);
718
- if (!field) {
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
- const objSignal = peeked[OBJECT_SIGNAL];
861
- objSignal.shouldReset = true;
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' || field?.kind === 'schema-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
- return Promise.resolve(this);
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 };