@warp-drive-mirror/schema-record 0.0.0 → 0.0.2
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/LICENSE.md +19 -8
- package/README.md +211 -123
- package/dist/-private.js +1 -0
- package/dist/-private.js.map +1 -0
- package/dist/{record.js → index.js} +646 -98
- package/dist/index.js.map +1 -0
- package/dist/symbols-DqoS4ybV.js.map +1 -1
- package/logos/NCC-1701-a-gold.svg +4 -0
- package/logos/NCC-1701-a-gold_100.svg +1 -0
- package/logos/NCC-1701-a-gold_base-64.txt +1 -0
- package/logos/README.md +4 -0
- package/logos/docs-badge.svg +2 -0
- package/logos/ember-data-logo-dark.svg +12 -0
- package/logos/ember-data-logo-light.svg +12 -0
- package/logos/github-header.svg +444 -0
- package/logos/social1.png +0 -0
- package/logos/social2.png +0 -0
- package/logos/warp-drive-logo-dark.svg +4 -0
- package/logos/warp-drive-logo-gold.svg +4 -0
- package/package.json +29 -55
- package/unstable-preview-types/-private/{compute.d.ts → fields/compute.d.ts} +17 -15
- package/unstable-preview-types/-private/fields/compute.d.ts.map +1 -0
- package/unstable-preview-types/-private/{managed-array.d.ts → fields/managed-array.d.ts} +4 -4
- package/unstable-preview-types/-private/fields/managed-array.d.ts.map +1 -0
- package/unstable-preview-types/-private/{managed-object.d.ts → fields/managed-object.d.ts} +4 -4
- package/unstable-preview-types/-private/fields/managed-object.d.ts.map +1 -0
- package/unstable-preview-types/-private/fields/many-array-manager.d.ts +23 -0
- package/unstable-preview-types/-private/fields/many-array-manager.d.ts.map +1 -0
- package/unstable-preview-types/{hooks.d.ts → -private/hooks.d.ts} +3 -3
- package/unstable-preview-types/-private/hooks.d.ts.map +1 -0
- package/unstable-preview-types/{record.d.ts → -private/record.d.ts} +3 -3
- package/unstable-preview-types/-private/record.d.ts.map +1 -0
- package/unstable-preview-types/{schema.d.ts → -private/schema.d.ts} +14 -11
- package/unstable-preview-types/-private/schema.d.ts.map +1 -0
- package/unstable-preview-types/{symbols.d.ts → -private/symbols.d.ts} +1 -1
- package/unstable-preview-types/-private/symbols.d.ts.map +1 -0
- package/unstable-preview-types/-private.d.ts +4 -0
- package/unstable-preview-types/-private.d.ts.map +1 -0
- package/unstable-preview-types/index.d.ts +16 -7
- package/unstable-preview-types/index.d.ts.map +1 -0
- package/dist/hooks.js +0 -19
- package/dist/hooks.js.map +0 -1
- package/dist/record.js.map +0 -1
- package/dist/schema.js +0 -278
- package/dist/schema.js.map +0 -1
- package/unstable-preview-types/-private/compute.d.ts.map +0 -1
- package/unstable-preview-types/-private/managed-array.d.ts.map +0 -1
- package/unstable-preview-types/-private/managed-object.d.ts.map +0 -1
- package/unstable-preview-types/hooks.d.ts.map +0 -1
- package/unstable-preview-types/record.d.ts.map +0 -1
- package/unstable-preview-types/schema.d.ts.map +0 -1
- package/unstable-preview-types/symbols.d.ts.map +0 -1
- /package/{NCC-1701-a-blue.svg → logos/NCC-1701-a-blue.svg} +0 -0
- /package/{NCC-1701-a.svg → logos/NCC-1701-a.svg} +0 -0
|
@@ -1,10 +1,14 @@
|
|
|
1
|
+
import { isResourceSchema } from '@warp-drive-mirror/core-types/schema/fields';
|
|
1
2
|
import { macroCondition, getGlobalConfig, dependencySatisfies, importSync } from '@embroider/macros';
|
|
2
|
-
import { setRecordIdentifier, recordIdentifierFor } from '@ember-data-mirror/store/-private';
|
|
3
|
-
import { createSignal, subscribe, defineSignal, peekSignal, getSignal, Signals,
|
|
4
|
-
import { STRUCTURED } from '@warp-drive-mirror/core-types/request';
|
|
5
|
-
import { RecordStore } from '@warp-drive-mirror/core-types/symbols';
|
|
3
|
+
import { SOURCE as SOURCE$1, fastPush, RelatedCollection, setRecordIdentifier, recordIdentifierFor } from '@ember-data-mirror/store/-private';
|
|
4
|
+
import { createSignal, subscribe, defineSignal, peekSignal, getSignal, Signals, addToTransaction, entangleSignal } from '@ember-data-mirror/tracking/-private';
|
|
5
|
+
import { EnableHydration, STRUCTURED } from '@warp-drive-mirror/core-types/request';
|
|
6
|
+
import { RecordStore, Type } from '@warp-drive-mirror/core-types/symbols';
|
|
6
7
|
import { getOrSetGlobal } from '@warp-drive-mirror/core-types/-private';
|
|
7
8
|
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";
|
|
9
|
+
import { deprecate } from '@ember/debug';
|
|
10
|
+
import { recordIdentifierFor as recordIdentifierFor$1 } from '@ember-data-mirror/store';
|
|
11
|
+
import { createCache, getValue } from '@ember-data-mirror/tracking';
|
|
8
12
|
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']);
|
|
9
13
|
// const ARRAY_SETTER_METHODS = new Set<KeyType>(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
|
|
10
14
|
const SYNC_PROPS = new Set(['[]', 'length']);
|
|
@@ -257,6 +261,12 @@ class ManagedArray {
|
|
|
257
261
|
_SIGNAL.shouldReset = true;
|
|
258
262
|
}
|
|
259
263
|
return reflect;
|
|
264
|
+
},
|
|
265
|
+
has(target, prop) {
|
|
266
|
+
if (prop === 'identifier' || prop === 'owner' || prop === ARRAY_SIGNAL) {
|
|
267
|
+
return true;
|
|
268
|
+
}
|
|
269
|
+
return Reflect.has(target, prop);
|
|
260
270
|
}
|
|
261
271
|
});
|
|
262
272
|
return proxy;
|
|
@@ -298,7 +308,7 @@ class ManagedObject {
|
|
|
298
308
|
return self[prop];
|
|
299
309
|
}
|
|
300
310
|
if (prop === Symbol.toPrimitive) {
|
|
301
|
-
return null;
|
|
311
|
+
return () => null;
|
|
302
312
|
}
|
|
303
313
|
if (prop === Symbol.toStringTag) {
|
|
304
314
|
return `ManagedObject<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
@@ -313,7 +323,12 @@ class ManagedObject {
|
|
|
313
323
|
}
|
|
314
324
|
if (prop === 'toHTML') {
|
|
315
325
|
return function () {
|
|
316
|
-
return '<
|
|
326
|
+
return '<span>ManagedObject</span>';
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (prop === 'toJSON') {
|
|
330
|
+
return function () {
|
|
331
|
+
return structuredClone(self[SOURCE]);
|
|
317
332
|
};
|
|
318
333
|
}
|
|
319
334
|
if (_SIGNAL.shouldReset) {
|
|
@@ -360,6 +375,75 @@ class ManagedObject {
|
|
|
360
375
|
return proxy;
|
|
361
376
|
}
|
|
362
377
|
}
|
|
378
|
+
class ManyArrayManager {
|
|
379
|
+
constructor(record) {
|
|
380
|
+
this.record = record;
|
|
381
|
+
this.store = record[RecordStore];
|
|
382
|
+
this.identifier = record[Identifier];
|
|
383
|
+
}
|
|
384
|
+
_syncArray(array) {
|
|
385
|
+
const rawValue = this.store.cache.getRelationship(this.identifier, array.key);
|
|
386
|
+
if (rawValue.meta) {
|
|
387
|
+
array.meta = rawValue.meta;
|
|
388
|
+
}
|
|
389
|
+
if (rawValue.links) {
|
|
390
|
+
array.links = rawValue.links;
|
|
391
|
+
}
|
|
392
|
+
const currentState = array[SOURCE$1];
|
|
393
|
+
currentState.length = 0;
|
|
394
|
+
fastPush(currentState, rawValue.data);
|
|
395
|
+
}
|
|
396
|
+
reloadHasMany(key, options) {
|
|
397
|
+
const field = this.store.schema.fields(this.identifier).get(key);
|
|
398
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
399
|
+
if (!test) {
|
|
400
|
+
throw new Error(`Expected a hasMany field for ${key}`);
|
|
401
|
+
}
|
|
402
|
+
})(field?.kind === 'hasMany') : {};
|
|
403
|
+
const cacheOptions = options ? extractCacheOptions(options) : {
|
|
404
|
+
reload: true
|
|
405
|
+
};
|
|
406
|
+
cacheOptions.types = [field.type];
|
|
407
|
+
const rawValue = this.store.cache.getRelationship(this.identifier, key);
|
|
408
|
+
const req = {
|
|
409
|
+
url: getRelatedLink(rawValue),
|
|
410
|
+
op: 'findHasMany',
|
|
411
|
+
method: 'GET',
|
|
412
|
+
records: rawValue.data,
|
|
413
|
+
cacheOptions,
|
|
414
|
+
options: {
|
|
415
|
+
field,
|
|
416
|
+
identifier: this.identifier,
|
|
417
|
+
links: rawValue.links,
|
|
418
|
+
meta: rawValue.meta
|
|
419
|
+
},
|
|
420
|
+
[EnableHydration]: false
|
|
421
|
+
};
|
|
422
|
+
return this.store.request(req);
|
|
423
|
+
}
|
|
424
|
+
mutate(mutation) {
|
|
425
|
+
this.cache.mutate(mutation);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
function getRelatedLink(resource) {
|
|
429
|
+
const related = resource.links?.related;
|
|
430
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
431
|
+
if (!test) {
|
|
432
|
+
throw new Error(`Expected a related link`);
|
|
433
|
+
}
|
|
434
|
+
})(related) : {};
|
|
435
|
+
return typeof related === 'object' ? related.href : related;
|
|
436
|
+
}
|
|
437
|
+
function extractCacheOptions(options) {
|
|
438
|
+
const cacheOptions = {};
|
|
439
|
+
if ('reload' in options) {
|
|
440
|
+
cacheOptions.reload = options.reload;
|
|
441
|
+
}
|
|
442
|
+
if ('backgroundReload' in options) {
|
|
443
|
+
cacheOptions.backgroundReload = options.backgroundReload;
|
|
444
|
+
}
|
|
445
|
+
return cacheOptions;
|
|
446
|
+
}
|
|
363
447
|
const ManagedArrayMap = getOrSetGlobal('ManagedArrayMap', new Map());
|
|
364
448
|
const ManagedObjectMap = getOrSetGlobal('ManagedObjectMap', new Map());
|
|
365
449
|
function computeLocal(record, field, prop) {
|
|
@@ -373,24 +457,25 @@ function computeLocal(record, field, prop) {
|
|
|
373
457
|
function peekManagedArray(record, field) {
|
|
374
458
|
const managedArrayMapForRecord = ManagedArrayMap.get(record);
|
|
375
459
|
if (managedArrayMapForRecord) {
|
|
376
|
-
return managedArrayMapForRecord.get(field);
|
|
460
|
+
return managedArrayMapForRecord.get(field.name);
|
|
377
461
|
}
|
|
378
462
|
}
|
|
379
463
|
function peekManagedObject(record, field) {
|
|
380
464
|
const managedObjectMapForRecord = ManagedObjectMap.get(record);
|
|
381
465
|
if (managedObjectMapForRecord) {
|
|
382
|
-
return managedObjectMapForRecord.get(field);
|
|
466
|
+
return managedObjectMapForRecord.get(field.name);
|
|
383
467
|
}
|
|
384
468
|
}
|
|
385
|
-
function computeField(schema, cache, record, identifier, field, prop) {
|
|
386
|
-
const rawValue = cache.getAttr(identifier, prop);
|
|
469
|
+
function computeField(schema, cache, record, identifier, field, prop, editable) {
|
|
470
|
+
const rawValue = editable ? cache.getAttr(identifier, prop) : cache.getRemoteAttr(identifier, prop);
|
|
387
471
|
if (!field.type) {
|
|
388
472
|
return rawValue;
|
|
389
473
|
}
|
|
390
474
|
const transform = schema.transformation(field);
|
|
391
475
|
return transform.hydrate(rawValue, field.options ?? null, record);
|
|
392
476
|
}
|
|
393
|
-
function computeArray(store, schema, cache, record, identifier, field, path,
|
|
477
|
+
function computeArray(store, schema, cache, record, identifier, field, path, editable, legacy) {
|
|
478
|
+
const isSchemaArray = field.kind === 'schema-array';
|
|
394
479
|
// the thing we hand out needs to know its owner and path in a private manner
|
|
395
480
|
// its "address" is the parent identifier (identifier) + field name (field.name)
|
|
396
481
|
// in the nested object case field name here is the full dot path from root resource to this value
|
|
@@ -400,20 +485,20 @@ function computeArray(store, schema, cache, record, identifier, field, path, isS
|
|
|
400
485
|
const managedArrayMapForRecord = ManagedArrayMap.get(record);
|
|
401
486
|
let managedArray;
|
|
402
487
|
if (managedArrayMapForRecord) {
|
|
403
|
-
managedArray = managedArrayMapForRecord.get(field);
|
|
488
|
+
managedArray = managedArrayMapForRecord.get(field.name);
|
|
404
489
|
}
|
|
405
490
|
if (managedArray) {
|
|
406
491
|
return managedArray;
|
|
407
492
|
} else {
|
|
408
|
-
const rawValue = cache.getAttr(identifier, path);
|
|
493
|
+
const rawValue = editable ? cache.getAttr(identifier, path) : cache.getRemoteAttr(identifier, path);
|
|
409
494
|
if (!rawValue) {
|
|
410
495
|
return null;
|
|
411
496
|
}
|
|
412
497
|
managedArray = new ManagedArray(store, schema, cache, field, rawValue, identifier, path, record, isSchemaArray, editable, legacy);
|
|
413
498
|
if (!managedArrayMapForRecord) {
|
|
414
|
-
ManagedArrayMap.set(record, new Map([[field, managedArray]]));
|
|
499
|
+
ManagedArrayMap.set(record, new Map([[field.name, managedArray]]));
|
|
415
500
|
} else {
|
|
416
|
-
managedArrayMapForRecord.set(field, managedArray);
|
|
501
|
+
managedArrayMapForRecord.set(field.name, managedArray);
|
|
417
502
|
}
|
|
418
503
|
}
|
|
419
504
|
return managedArray;
|
|
@@ -422,12 +507,12 @@ function computeObject(schema, cache, record, identifier, field, path, editable,
|
|
|
422
507
|
const managedObjectMapForRecord = ManagedObjectMap.get(record);
|
|
423
508
|
let managedObject;
|
|
424
509
|
if (managedObjectMapForRecord) {
|
|
425
|
-
managedObject = managedObjectMapForRecord.get(field);
|
|
510
|
+
managedObject = managedObjectMapForRecord.get(field.name);
|
|
426
511
|
}
|
|
427
512
|
if (managedObject) {
|
|
428
513
|
return managedObject;
|
|
429
514
|
} else {
|
|
430
|
-
let rawValue = cache.getAttr(identifier, path);
|
|
515
|
+
let rawValue = editable ? cache.getAttr(identifier, path) : cache.getRemoteAttr(identifier, path);
|
|
431
516
|
if (!rawValue) {
|
|
432
517
|
return null;
|
|
433
518
|
}
|
|
@@ -437,9 +522,9 @@ function computeObject(schema, cache, record, identifier, field, path, editable,
|
|
|
437
522
|
}
|
|
438
523
|
managedObject = new ManagedObject(schema, cache, field, rawValue, identifier, path, record, editable, legacy);
|
|
439
524
|
if (!managedObjectMapForRecord) {
|
|
440
|
-
ManagedObjectMap.set(record, new Map([[field, managedObject]]));
|
|
525
|
+
ManagedObjectMap.set(record, new Map([[field.name, managedObject]]));
|
|
441
526
|
} else {
|
|
442
|
-
managedObjectMapForRecord.set(field, managedObject);
|
|
527
|
+
managedObjectMapForRecord.set(field.name, managedObject);
|
|
443
528
|
}
|
|
444
529
|
}
|
|
445
530
|
return managedObject;
|
|
@@ -448,12 +533,12 @@ function computeSchemaObject(store, cache, record, identifier, field, path, lega
|
|
|
448
533
|
const schemaObjectMapForRecord = ManagedObjectMap.get(record);
|
|
449
534
|
let schemaObject;
|
|
450
535
|
if (schemaObjectMapForRecord) {
|
|
451
|
-
schemaObject = schemaObjectMapForRecord.get(field);
|
|
536
|
+
schemaObject = schemaObjectMapForRecord.get(field.name);
|
|
452
537
|
}
|
|
453
538
|
if (schemaObject) {
|
|
454
539
|
return schemaObject;
|
|
455
540
|
} else {
|
|
456
|
-
const rawValue = cache.getAttr(identifier, path);
|
|
541
|
+
const rawValue = editable ? cache.getAttr(identifier, path) : cache.getRemoteAttr(identifier, path);
|
|
457
542
|
if (!rawValue) {
|
|
458
543
|
return null;
|
|
459
544
|
}
|
|
@@ -464,14 +549,14 @@ function computeSchemaObject(store, cache, record, identifier, field, path, lega
|
|
|
464
549
|
}, true, field.type, embeddedPath);
|
|
465
550
|
}
|
|
466
551
|
if (!schemaObjectMapForRecord) {
|
|
467
|
-
ManagedObjectMap.set(record, new Map([[field, schemaObject]]));
|
|
552
|
+
ManagedObjectMap.set(record, new Map([[field.name, schemaObject]]));
|
|
468
553
|
} else {
|
|
469
|
-
schemaObjectMapForRecord.set(field, schemaObject);
|
|
554
|
+
schemaObjectMapForRecord.set(field.name, schemaObject);
|
|
470
555
|
}
|
|
471
556
|
return schemaObject;
|
|
472
557
|
}
|
|
473
|
-
function computeAttribute(cache, identifier, prop) {
|
|
474
|
-
return cache.getAttr(identifier, prop);
|
|
558
|
+
function computeAttribute(cache, identifier, prop, editable) {
|
|
559
|
+
return editable ? cache.getAttr(identifier, prop) : cache.getRemoteAttr(identifier, prop);
|
|
475
560
|
}
|
|
476
561
|
function computeDerivation(schema, record, identifier, field, prop) {
|
|
477
562
|
return schema.derivation(field)(record, field.options ?? null, prop);
|
|
@@ -480,8 +565,8 @@ function computeDerivation(schema, record, identifier, field, prop) {
|
|
|
480
565
|
// TODO probably this should just be a Document
|
|
481
566
|
// but its separate until we work out the lid situation
|
|
482
567
|
class ResourceRelationship {
|
|
483
|
-
constructor(store, cache, parent, identifier, field, name) {
|
|
484
|
-
const rawValue = cache.getRelationship(identifier, name);
|
|
568
|
+
constructor(store, cache, parent, identifier, field, name, editable) {
|
|
569
|
+
const rawValue = editable ? cache.getRelationship(identifier, name) : cache.getRemoteRelationship(identifier, name);
|
|
485
570
|
|
|
486
571
|
// TODO setup true lids for relationship documents
|
|
487
572
|
// @ts-expect-error we need to give relationship documents a lid
|
|
@@ -523,11 +608,56 @@ function getHref(link) {
|
|
|
523
608
|
}
|
|
524
609
|
return link.href;
|
|
525
610
|
}
|
|
526
|
-
function computeResource(store, cache, parent, identifier, field, prop) {
|
|
611
|
+
function computeResource(store, cache, parent, identifier, field, prop, editable) {
|
|
527
612
|
if (field.kind !== 'resource') {
|
|
528
613
|
throw new Error(`The schema for ${identifier.type}.${String(prop)} is not a resource relationship`);
|
|
529
614
|
}
|
|
530
|
-
return new ResourceRelationship(store, cache, parent, identifier, field, prop);
|
|
615
|
+
return new ResourceRelationship(store, cache, parent, identifier, field, prop, editable);
|
|
616
|
+
}
|
|
617
|
+
function computeHasMany(store, schema, cache, record, identifier, field, path, editable, legacy) {
|
|
618
|
+
// the thing we hand out needs to know its owner and path in a private manner
|
|
619
|
+
// its "address" is the parent identifier (identifier) + field name (field.name)
|
|
620
|
+
// in the nested object case field name here is the full dot path from root resource to this value
|
|
621
|
+
// its "key" is the field on the parent record
|
|
622
|
+
// its "owner" is the parent record
|
|
623
|
+
|
|
624
|
+
const managedArrayMapForRecord = ManagedArrayMap.get(record);
|
|
625
|
+
let managedArray;
|
|
626
|
+
if (managedArrayMapForRecord) {
|
|
627
|
+
managedArray = managedArrayMapForRecord.get(field.name);
|
|
628
|
+
}
|
|
629
|
+
if (managedArray) {
|
|
630
|
+
return managedArray;
|
|
631
|
+
} else {
|
|
632
|
+
const rawValue = cache.getRelationship(identifier, field.name);
|
|
633
|
+
if (!rawValue) {
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
managedArray = new RelatedCollection({
|
|
637
|
+
store,
|
|
638
|
+
type: field.type,
|
|
639
|
+
identifier,
|
|
640
|
+
cache,
|
|
641
|
+
identifiers: rawValue.data,
|
|
642
|
+
key: field.name,
|
|
643
|
+
meta: rawValue.meta || null,
|
|
644
|
+
links: rawValue.links || null,
|
|
645
|
+
isPolymorphic: field.options.polymorphic ?? false,
|
|
646
|
+
isAsync: field.options.async ?? false,
|
|
647
|
+
// TODO: Grab the proper value
|
|
648
|
+
_inverseIsAsync: false,
|
|
649
|
+
// @ts-expect-error Typescript doesn't have a way for us to thread the generic backwards so it infers unknown instead of T
|
|
650
|
+
manager: new ManyArrayManager(record),
|
|
651
|
+
isLoaded: true,
|
|
652
|
+
allowMutation: editable
|
|
653
|
+
});
|
|
654
|
+
if (!managedArrayMapForRecord) {
|
|
655
|
+
ManagedArrayMap.set(record, new Map([[field.name, managedArray]]));
|
|
656
|
+
} else {
|
|
657
|
+
managedArrayMapForRecord.set(field.name, managedArray);
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
return managedArray;
|
|
531
661
|
}
|
|
532
662
|
const HAS_MODEL_PACKAGE = dependencySatisfies('@ember-data-mirror/model', '*');
|
|
533
663
|
const getLegacySupport = macroCondition(dependencySatisfies('@ember-data-mirror/model', '*')) ? importSync('@ember-data-mirror/model/-private').lookupLegacySupport : null;
|
|
@@ -537,6 +667,9 @@ const RecordSymbols = new Set(symbolList);
|
|
|
537
667
|
function isPathMatch(a, b) {
|
|
538
668
|
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
539
669
|
}
|
|
670
|
+
function isNonEnumerableProp(prop) {
|
|
671
|
+
return prop === 'constructor' || prop === 'prototype' || prop === '__proto__' || prop === 'toString' || prop === 'toJSON' || prop === 'toHTML' || typeof prop === 'symbol';
|
|
672
|
+
}
|
|
540
673
|
const Editables = new WeakMap();
|
|
541
674
|
class SchemaRecord {
|
|
542
675
|
constructor(store, identifier, Mode, isEmbedded = false, embeddedType = null, embeddedPath = null) {
|
|
@@ -552,31 +685,46 @@ class SchemaRecord {
|
|
|
552
685
|
this[Legacy] = Mode[Legacy] ?? false;
|
|
553
686
|
const schema = store.schema;
|
|
554
687
|
const cache = store.cache;
|
|
555
|
-
const identityField = schema.resource(
|
|
688
|
+
const identityField = schema.resource(isEmbedded ? {
|
|
689
|
+
type: embeddedType
|
|
690
|
+
} : identifier).identity;
|
|
691
|
+
const BoundFns = new Map();
|
|
556
692
|
this[EmbeddedType] = embeddedType;
|
|
557
693
|
this[EmbeddedPath] = embeddedPath;
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
type: embeddedType
|
|
562
|
-
});
|
|
563
|
-
} else {
|
|
564
|
-
fields = schema.fields(identifier);
|
|
565
|
-
}
|
|
694
|
+
const fields = isEmbedded ? schema.fields({
|
|
695
|
+
type: embeddedType
|
|
696
|
+
}) : schema.fields(identifier);
|
|
566
697
|
const signals = new Map();
|
|
567
698
|
this[Signals] = signals;
|
|
568
699
|
const proxy = new Proxy(this, {
|
|
569
700
|
ownKeys() {
|
|
570
|
-
|
|
701
|
+
const identityKey = identityField?.name;
|
|
702
|
+
const keys = Array.from(fields.keys());
|
|
703
|
+
if (identityKey) {
|
|
704
|
+
keys.unshift(identityKey);
|
|
705
|
+
}
|
|
706
|
+
return keys;
|
|
571
707
|
},
|
|
572
708
|
has(target, prop) {
|
|
709
|
+
if (prop === Destroy || prop === Checkout) {
|
|
710
|
+
return true;
|
|
711
|
+
}
|
|
573
712
|
return fields.has(prop);
|
|
574
713
|
},
|
|
575
714
|
getOwnPropertyDescriptor(target, prop) {
|
|
576
|
-
|
|
577
|
-
|
|
715
|
+
const schemaForField = prop === identityField?.name ? identityField : fields.get(prop);
|
|
716
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
717
|
+
if (!test) {
|
|
718
|
+
throw new Error(`No field named ${String(prop)} on ${identifier.type}`);
|
|
719
|
+
}
|
|
720
|
+
})(schemaForField) : {};
|
|
721
|
+
if (isNonEnumerableProp(prop)) {
|
|
722
|
+
return {
|
|
723
|
+
writable: false,
|
|
724
|
+
enumerable: false,
|
|
725
|
+
configurable: true
|
|
726
|
+
};
|
|
578
727
|
}
|
|
579
|
-
const schemaForField = fields.get(prop);
|
|
580
728
|
switch (schemaForField.kind) {
|
|
581
729
|
case 'derived':
|
|
582
730
|
return {
|
|
@@ -584,6 +732,12 @@ class SchemaRecord {
|
|
|
584
732
|
enumerable: true,
|
|
585
733
|
configurable: true
|
|
586
734
|
};
|
|
735
|
+
case '@id':
|
|
736
|
+
return {
|
|
737
|
+
writable: identifier.id === null,
|
|
738
|
+
enumerable: true,
|
|
739
|
+
configurable: true
|
|
740
|
+
};
|
|
587
741
|
case '@local':
|
|
588
742
|
case 'field':
|
|
589
743
|
case 'attribute':
|
|
@@ -601,28 +755,18 @@ class SchemaRecord {
|
|
|
601
755
|
enumerable: true,
|
|
602
756
|
configurable: true
|
|
603
757
|
};
|
|
758
|
+
default:
|
|
759
|
+
return {
|
|
760
|
+
writable: false,
|
|
761
|
+
enumerable: false,
|
|
762
|
+
configurable: false
|
|
763
|
+
};
|
|
604
764
|
}
|
|
605
765
|
},
|
|
606
766
|
get(target, prop, receiver) {
|
|
607
767
|
if (RecordSymbols.has(prop)) {
|
|
608
768
|
return target[prop];
|
|
609
769
|
}
|
|
610
|
-
if (prop === Symbol.toStringTag) {
|
|
611
|
-
return `SchemaRecord<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
612
|
-
}
|
|
613
|
-
if (prop === 'toString') {
|
|
614
|
-
return function () {
|
|
615
|
-
return `SchemaRecord<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
616
|
-
};
|
|
617
|
-
}
|
|
618
|
-
if (prop === 'toHTML') {
|
|
619
|
-
return function () {
|
|
620
|
-
return `<div>SchemaRecord<${identifier.type}:${identifier.id} (${identifier.lid})></div>`;
|
|
621
|
-
};
|
|
622
|
-
}
|
|
623
|
-
if (prop === Symbol.toPrimitive) {
|
|
624
|
-
return null;
|
|
625
|
-
}
|
|
626
770
|
|
|
627
771
|
// TODO make this a symbol
|
|
628
772
|
if (prop === '___notifications') {
|
|
@@ -638,18 +782,75 @@ class SchemaRecord {
|
|
|
638
782
|
if (IgnoredGlobalFields.has(prop)) {
|
|
639
783
|
return undefined;
|
|
640
784
|
}
|
|
785
|
+
|
|
786
|
+
/////////////////////////////////////////////////////////////
|
|
787
|
+
//// Note these bound function behaviors are essentially ////
|
|
788
|
+
//// built-in but overrideable derivations. ////
|
|
789
|
+
//// ////
|
|
790
|
+
//// The bar for this has to be "basic expectations of ////
|
|
791
|
+
/// an object" – very, very high ////
|
|
792
|
+
/////////////////////////////////////////////////////////////
|
|
793
|
+
|
|
794
|
+
if (prop === Symbol.toStringTag || prop === 'toString') {
|
|
795
|
+
let fn = BoundFns.get('toString');
|
|
796
|
+
if (!fn) {
|
|
797
|
+
fn = function () {
|
|
798
|
+
entangleSignal(signals, receiver, '@identity');
|
|
799
|
+
return `Record<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
800
|
+
};
|
|
801
|
+
BoundFns.set(prop, fn);
|
|
802
|
+
}
|
|
803
|
+
return fn;
|
|
804
|
+
}
|
|
805
|
+
if (prop === 'toHTML') {
|
|
806
|
+
let fn = BoundFns.get('toHTML');
|
|
807
|
+
if (!fn) {
|
|
808
|
+
fn = function () {
|
|
809
|
+
entangleSignal(signals, receiver, '@identity');
|
|
810
|
+
return `<span>Record<${identifier.type}:${identifier.id} (${identifier.lid})></span>`;
|
|
811
|
+
};
|
|
812
|
+
BoundFns.set(prop, fn);
|
|
813
|
+
}
|
|
814
|
+
return fn;
|
|
815
|
+
}
|
|
816
|
+
if (prop === 'toJSON') {
|
|
817
|
+
let fn = BoundFns.get('toJSON');
|
|
818
|
+
if (!fn) {
|
|
819
|
+
fn = function () {
|
|
820
|
+
const json = {};
|
|
821
|
+
for (const key in receiver) {
|
|
822
|
+
json[key] = receiver[key];
|
|
823
|
+
}
|
|
824
|
+
return json;
|
|
825
|
+
};
|
|
826
|
+
BoundFns.set(prop, fn);
|
|
827
|
+
}
|
|
828
|
+
return fn;
|
|
829
|
+
}
|
|
830
|
+
if (prop === Symbol.toPrimitive) return () => null;
|
|
831
|
+
if (prop === Symbol.iterator) {
|
|
832
|
+
let fn = BoundFns.get(Symbol.iterator);
|
|
833
|
+
if (!fn) {
|
|
834
|
+
fn = function* () {
|
|
835
|
+
for (const key in receiver) {
|
|
836
|
+
yield [key, receiver[key]];
|
|
837
|
+
}
|
|
838
|
+
};
|
|
839
|
+
BoundFns.set(Symbol.iterator, fn);
|
|
840
|
+
}
|
|
841
|
+
return fn;
|
|
842
|
+
}
|
|
641
843
|
if (prop === 'constructor') {
|
|
642
844
|
return SchemaRecord;
|
|
643
845
|
}
|
|
644
846
|
// too many things check for random symbols
|
|
645
|
-
if (typeof prop === 'symbol')
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
throw new Error(`No field named ${String(prop)} on ${type}`);
|
|
847
|
+
if (typeof prop === 'symbol') return undefined;
|
|
848
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
849
|
+
{
|
|
850
|
+
throw new Error(`No field named ${String(prop)} on ${isEmbedded ? embeddedType : identifier.type}`);
|
|
851
|
+
}
|
|
852
|
+
})() : {};
|
|
853
|
+
return undefined;
|
|
653
854
|
}
|
|
654
855
|
const field = maybeField.kind === 'alias' ? maybeField.options : maybeField;
|
|
655
856
|
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
@@ -677,55 +878,35 @@ class SchemaRecord {
|
|
|
677
878
|
return lastValue;
|
|
678
879
|
}
|
|
679
880
|
case 'field':
|
|
680
|
-
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
681
|
-
if (!test) {
|
|
682
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
683
|
-
}
|
|
684
|
-
})(!target[Legacy]) : {};
|
|
685
881
|
entangleSignal(signals, receiver, field.name);
|
|
686
|
-
return computeField(schema, cache, target, identifier, field, propArray);
|
|
882
|
+
return computeField(schema, cache, target, identifier, field, propArray, IS_EDITABLE);
|
|
687
883
|
case 'attribute':
|
|
688
884
|
entangleSignal(signals, receiver, field.name);
|
|
689
|
-
return computeAttribute(cache, identifier, prop);
|
|
885
|
+
return computeAttribute(cache, identifier, prop, IS_EDITABLE);
|
|
690
886
|
case 'resource':
|
|
691
|
-
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
692
|
-
if (!test) {
|
|
693
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
694
|
-
}
|
|
695
|
-
})(!target[Legacy]) : {};
|
|
696
887
|
entangleSignal(signals, receiver, field.name);
|
|
697
|
-
return computeResource(store, cache, target, identifier, field, prop);
|
|
888
|
+
return computeResource(store, cache, target, identifier, field, prop, IS_EDITABLE);
|
|
698
889
|
case 'derived':
|
|
699
890
|
return computeDerivation(schema, receiver, identifier, field, prop);
|
|
700
891
|
case 'schema-array':
|
|
701
|
-
entangleSignal(signals, receiver, field.name);
|
|
702
|
-
return computeArray(store, schema, cache, target, identifier, field, propArray, true, Mode[Editable], Mode[Legacy]);
|
|
703
892
|
case 'array':
|
|
704
|
-
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
705
|
-
if (!test) {
|
|
706
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
707
|
-
}
|
|
708
|
-
})(!target[Legacy]) : {};
|
|
709
893
|
entangleSignal(signals, receiver, field.name);
|
|
710
|
-
return computeArray(store, schema, cache, target, identifier, field, propArray,
|
|
894
|
+
return computeArray(store, schema, cache, target, identifier, field, propArray, Mode[Editable], Mode[Legacy]);
|
|
711
895
|
case 'object':
|
|
712
|
-
macroCondition(getGlobalConfig().WarpDriveMirror.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
896
|
entangleSignal(signals, receiver, field.name);
|
|
718
897
|
return computeObject(schema, cache, target, identifier, field, propArray, Mode[Editable], Mode[Legacy]);
|
|
719
898
|
case 'schema-object':
|
|
720
|
-
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
721
|
-
if (!test) {
|
|
722
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
723
|
-
}
|
|
724
|
-
})(!target[Legacy]) : {};
|
|
725
899
|
entangleSignal(signals, receiver, field.name);
|
|
726
900
|
// run transform, then use that value as the object to manage
|
|
727
901
|
return computeSchemaObject(store, cache, target, identifier, field, propArray, Mode[Legacy], Mode[Editable]);
|
|
728
902
|
case 'belongsTo':
|
|
903
|
+
if (field.options.linksMode) {
|
|
904
|
+
entangleSignal(signals, receiver, field.name);
|
|
905
|
+
const rawValue = IS_EDITABLE ? cache.getRelationship(identifier, field.name) : cache.getRemoteRelationship(identifier, field.name);
|
|
906
|
+
|
|
907
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
908
|
+
return rawValue.data ? store.peekRecord(rawValue.data) : null;
|
|
909
|
+
}
|
|
729
910
|
if (!HAS_MODEL_PACKAGE) {
|
|
730
911
|
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
731
912
|
{
|
|
@@ -746,6 +927,10 @@ class SchemaRecord {
|
|
|
746
927
|
entangleSignal(signals, receiver, field.name);
|
|
747
928
|
return getLegacySupport(receiver).getBelongsTo(field.name);
|
|
748
929
|
case 'hasMany':
|
|
930
|
+
if (field.options.linksMode) {
|
|
931
|
+
entangleSignal(signals, receiver, field.name);
|
|
932
|
+
return computeHasMany(store, schema, cache, target, identifier, field, propArray, Mode[Editable], Mode[Legacy]);
|
|
933
|
+
}
|
|
749
934
|
if (!HAS_MODEL_PACKAGE) {
|
|
750
935
|
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
751
936
|
{
|
|
@@ -843,6 +1028,11 @@ class SchemaRecord {
|
|
|
843
1028
|
cache.setAttr(identifier, propArray, value?.slice());
|
|
844
1029
|
const peeked = peekManagedArray(self, field);
|
|
845
1030
|
if (peeked) {
|
|
1031
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1032
|
+
if (!test) {
|
|
1033
|
+
throw new Error(`Expected the peekManagedArray for ${field.kind} to return a ManagedArray`);
|
|
1034
|
+
}
|
|
1035
|
+
})(ARRAY_SIGNAL in peeked) : {};
|
|
846
1036
|
const arrSignal = peeked[ARRAY_SIGNAL];
|
|
847
1037
|
arrSignal.shouldReset = true;
|
|
848
1038
|
}
|
|
@@ -856,6 +1046,11 @@ class SchemaRecord {
|
|
|
856
1046
|
cache.setAttr(identifier, propArray, rawValue);
|
|
857
1047
|
const peeked = peekManagedArray(self, field);
|
|
858
1048
|
if (peeked) {
|
|
1049
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1050
|
+
if (!test) {
|
|
1051
|
+
throw new Error(`Expected the peekManagedArray for ${field.kind} to return a ManagedArray`);
|
|
1052
|
+
}
|
|
1053
|
+
})(ARRAY_SIGNAL in peeked) : {};
|
|
859
1054
|
const arrSignal = peeked[ARRAY_SIGNAL];
|
|
860
1055
|
arrSignal.shouldReset = true;
|
|
861
1056
|
}
|
|
@@ -870,6 +1065,11 @@ class SchemaRecord {
|
|
|
870
1065
|
cache.setAttr(identifier, propArray, arrayValue);
|
|
871
1066
|
const peeked = peekManagedArray(self, field);
|
|
872
1067
|
if (peeked) {
|
|
1068
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1069
|
+
if (!test) {
|
|
1070
|
+
throw new Error(`Expected the peekManagedArray for ${field.kind} to return a ManagedArray`);
|
|
1071
|
+
}
|
|
1072
|
+
})(ARRAY_SIGNAL in peeked) : {};
|
|
873
1073
|
const arrSignal = peeked[ARRAY_SIGNAL];
|
|
874
1074
|
arrSignal.shouldReset = true;
|
|
875
1075
|
}
|
|
@@ -1047,6 +1247,11 @@ class SchemaRecord {
|
|
|
1047
1247
|
if (field?.kind === 'array' || field?.kind === 'schema-array') {
|
|
1048
1248
|
const peeked = peekManagedArray(self, field);
|
|
1049
1249
|
if (peeked) {
|
|
1250
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1251
|
+
if (!test) {
|
|
1252
|
+
throw new Error(`Expected the peekManagedArray for ${field.kind} to return a ManagedArray`);
|
|
1253
|
+
}
|
|
1254
|
+
})(ARRAY_SIGNAL in peeked) : {};
|
|
1050
1255
|
const arrSignal = peeked[ARRAY_SIGNAL];
|
|
1051
1256
|
arrSignal.shouldReset = true;
|
|
1052
1257
|
addToTransaction(arrSignal);
|
|
@@ -1083,6 +1288,16 @@ class SchemaRecord {
|
|
|
1083
1288
|
}
|
|
1084
1289
|
// FIXME
|
|
1085
1290
|
} else if (field.kind === 'resource') ;else if (field.kind === 'hasMany') {
|
|
1291
|
+
if (field.options.linksMode) {
|
|
1292
|
+
const peeked = peekManagedArray(self, field);
|
|
1293
|
+
if (peeked) {
|
|
1294
|
+
// const arrSignal = peeked[ARRAY_SIGNAL];
|
|
1295
|
+
// arrSignal.shouldReset = true;
|
|
1296
|
+
// addToTransaction(arrSignal);
|
|
1297
|
+
peeked.notify();
|
|
1298
|
+
}
|
|
1299
|
+
return;
|
|
1300
|
+
}
|
|
1086
1301
|
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1087
1302
|
if (!test) {
|
|
1088
1303
|
throw new Error(`Expected to have a getLegacySupport function`);
|
|
@@ -1126,6 +1341,19 @@ class SchemaRecord {
|
|
|
1126
1341
|
break;
|
|
1127
1342
|
}
|
|
1128
1343
|
});
|
|
1344
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
|
|
1345
|
+
Object.defineProperty(this, '__SHOW_ME_THE_DATA_(debug mode only)__', {
|
|
1346
|
+
enumerable: false,
|
|
1347
|
+
configurable: true,
|
|
1348
|
+
get() {
|
|
1349
|
+
const data = {};
|
|
1350
|
+
for (const key of fields.keys()) {
|
|
1351
|
+
data[key] = proxy[key];
|
|
1352
|
+
}
|
|
1353
|
+
return data;
|
|
1354
|
+
}
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1129
1357
|
return proxy;
|
|
1130
1358
|
}
|
|
1131
1359
|
[Destroy]() {
|
|
@@ -1138,6 +1366,10 @@ class SchemaRecord {
|
|
|
1138
1366
|
this[RecordStore].notifications.unsubscribe(this.___notifications);
|
|
1139
1367
|
}
|
|
1140
1368
|
[Checkout]() {
|
|
1369
|
+
// IF we are already the editable record, throw an error
|
|
1370
|
+
if (this[Editable]) {
|
|
1371
|
+
throw new Error(`Cannot checkout an already editable record`);
|
|
1372
|
+
}
|
|
1141
1373
|
const editable = Editables.get(this);
|
|
1142
1374
|
if (editable) {
|
|
1143
1375
|
return Promise.resolve(editable);
|
|
@@ -1156,4 +1388,320 @@ class SchemaRecord {
|
|
|
1156
1388
|
return Promise.resolve(editableRecord);
|
|
1157
1389
|
}
|
|
1158
1390
|
}
|
|
1159
|
-
|
|
1391
|
+
function instantiateRecord(store, identifier, createArgs) {
|
|
1392
|
+
const schema = store.schema;
|
|
1393
|
+
const resourceSchema = schema.resource(identifier);
|
|
1394
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1395
|
+
if (!test) {
|
|
1396
|
+
throw new Error(`Expected a resource schema`);
|
|
1397
|
+
}
|
|
1398
|
+
})(isResourceSchema(resourceSchema)) : {};
|
|
1399
|
+
const isLegacy = resourceSchema?.legacy ?? false;
|
|
1400
|
+
const isEditable = isLegacy || store.cache.isNew(identifier);
|
|
1401
|
+
const record = new SchemaRecord(store, identifier, {
|
|
1402
|
+
[Editable]: isEditable,
|
|
1403
|
+
[Legacy]: isLegacy
|
|
1404
|
+
});
|
|
1405
|
+
if (createArgs) {
|
|
1406
|
+
Object.assign(record, createArgs);
|
|
1407
|
+
}
|
|
1408
|
+
return record;
|
|
1409
|
+
}
|
|
1410
|
+
function assertSchemaRecord(record) {
|
|
1411
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1412
|
+
if (!test) {
|
|
1413
|
+
throw new Error('Expected a SchemaRecord');
|
|
1414
|
+
}
|
|
1415
|
+
})(record && typeof record === 'object' && Destroy in record) : {};
|
|
1416
|
+
}
|
|
1417
|
+
function teardownRecord(record) {
|
|
1418
|
+
assertSchemaRecord(record);
|
|
1419
|
+
record[Destroy]();
|
|
1420
|
+
}
|
|
1421
|
+
const Support = getOrSetGlobal('Support', new WeakMap());
|
|
1422
|
+
const ConstructorField = {
|
|
1423
|
+
type: '@constructor',
|
|
1424
|
+
name: 'constructor',
|
|
1425
|
+
kind: 'derived'
|
|
1426
|
+
};
|
|
1427
|
+
const TypeField = {
|
|
1428
|
+
type: '@identity',
|
|
1429
|
+
name: '$type',
|
|
1430
|
+
kind: 'derived',
|
|
1431
|
+
options: {
|
|
1432
|
+
key: 'type'
|
|
1433
|
+
}
|
|
1434
|
+
};
|
|
1435
|
+
const DefaultIdentityField = {
|
|
1436
|
+
name: 'id',
|
|
1437
|
+
kind: '@id'
|
|
1438
|
+
};
|
|
1439
|
+
function _constructor(record) {
|
|
1440
|
+
let state = Support.get(record);
|
|
1441
|
+
if (!state) {
|
|
1442
|
+
state = {};
|
|
1443
|
+
Support.set(record, state);
|
|
1444
|
+
}
|
|
1445
|
+
return state._constructor = state._constructor || {
|
|
1446
|
+
name: `SchemaRecord<${recordIdentifierFor$1(record).type}>`,
|
|
1447
|
+
get modelName() {
|
|
1448
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1449
|
+
{
|
|
1450
|
+
throw new Error(`record.constructor.modelName is not available outside of legacy mode`);
|
|
1451
|
+
}
|
|
1452
|
+
})() : {};
|
|
1453
|
+
return undefined;
|
|
1454
|
+
}
|
|
1455
|
+
};
|
|
1456
|
+
}
|
|
1457
|
+
_constructor[Type] = '@constructor';
|
|
1458
|
+
|
|
1459
|
+
/**
|
|
1460
|
+
* Utility for constructing a ResourceSchema with the recommended fields
|
|
1461
|
+
* for the Polaris experience.
|
|
1462
|
+
*/
|
|
1463
|
+
function withDefaults(schema) {
|
|
1464
|
+
schema.identity = schema.identity || DefaultIdentityField;
|
|
1465
|
+
|
|
1466
|
+
// because fields gets iterated in definition order,
|
|
1467
|
+
// we add TypeField to the beginning so that it will
|
|
1468
|
+
// appear right next to the identity field
|
|
1469
|
+
schema.fields.unshift(TypeField);
|
|
1470
|
+
schema.fields.push(ConstructorField);
|
|
1471
|
+
return schema;
|
|
1472
|
+
}
|
|
1473
|
+
function fromIdentity(record, options, key) {
|
|
1474
|
+
const identifier = record[Identifier];
|
|
1475
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1476
|
+
if (!test) {
|
|
1477
|
+
throw new Error(`Cannot compute @identity for a record without an identifier`);
|
|
1478
|
+
}
|
|
1479
|
+
})(identifier) : {};
|
|
1480
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1481
|
+
if (!test) {
|
|
1482
|
+
throw new Error(`Expected to receive a key to compute @identity, but got ${String(options)}`);
|
|
1483
|
+
}
|
|
1484
|
+
})(options?.key && ['lid', 'id', 'type', '^'].includes(options.key)) : {};
|
|
1485
|
+
return options.key === '^' ? identifier : identifier[options.key];
|
|
1486
|
+
}
|
|
1487
|
+
fromIdentity[Type] = '@identity';
|
|
1488
|
+
function registerDerivations(schema) {
|
|
1489
|
+
schema.registerDerivation(fromIdentity);
|
|
1490
|
+
schema.registerDerivation(_constructor);
|
|
1491
|
+
}
|
|
1492
|
+
/**
|
|
1493
|
+
* Wraps a derivation in a new function with Derivation signature but that looks
|
|
1494
|
+
* up the value in the cache before recomputing.
|
|
1495
|
+
*
|
|
1496
|
+
* @param record
|
|
1497
|
+
* @param options
|
|
1498
|
+
* @param prop
|
|
1499
|
+
*/
|
|
1500
|
+
function makeCachedDerivation(derivation) {
|
|
1501
|
+
const memoizedDerivation = (record, options, prop) => {
|
|
1502
|
+
const signals = record[Signals];
|
|
1503
|
+
let signal = signals.get(prop);
|
|
1504
|
+
if (!signal) {
|
|
1505
|
+
signal = createCache(() => {
|
|
1506
|
+
return derivation(record, options, prop);
|
|
1507
|
+
}); // a total lie, for convenience of reusing the storage
|
|
1508
|
+
signals.set(prop, signal);
|
|
1509
|
+
}
|
|
1510
|
+
return getValue(signal);
|
|
1511
|
+
};
|
|
1512
|
+
memoizedDerivation[Type] = derivation[Type];
|
|
1513
|
+
return memoizedDerivation;
|
|
1514
|
+
}
|
|
1515
|
+
class SchemaService {
|
|
1516
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1517
|
+
|
|
1518
|
+
constructor() {
|
|
1519
|
+
this._schemas = new Map();
|
|
1520
|
+
this._transforms = new Map();
|
|
1521
|
+
this._hashFns = new Map();
|
|
1522
|
+
this._derivations = new Map();
|
|
1523
|
+
}
|
|
1524
|
+
hasTrait(type) {
|
|
1525
|
+
return this._traits.has(type);
|
|
1526
|
+
}
|
|
1527
|
+
resourceHasTrait(resource, trait) {
|
|
1528
|
+
return this._schemas.get(resource.type).traits.has(trait);
|
|
1529
|
+
}
|
|
1530
|
+
transformation(field) {
|
|
1531
|
+
const kind = 'kind' in field ? field.kind : '<unknown kind>';
|
|
1532
|
+
const name = 'name' in field ? field.name : '<unknown name>';
|
|
1533
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1534
|
+
if (!test) {
|
|
1535
|
+
throw new Error(`'${kind}' fields cannot be transformed. Only fields of kind 'field' 'object' or 'array' can specify a transformation. Attempted to find '${field.type ?? '<unknown type>'}' on field '${name}'.`);
|
|
1536
|
+
}
|
|
1537
|
+
})(!('kind' in field) || ['field', 'object', 'array'].includes(kind)) : {};
|
|
1538
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1539
|
+
if (!test) {
|
|
1540
|
+
throw new Error(`Expected the '${kind}' field '${name}' to specify a transformation via 'field.type', but none was present`);
|
|
1541
|
+
}
|
|
1542
|
+
})(field.type) : {};
|
|
1543
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1544
|
+
if (!test) {
|
|
1545
|
+
throw new Error(`No transformation registered with name '${field.type}' for '${kind}' field '${name}'`);
|
|
1546
|
+
}
|
|
1547
|
+
})(this._transforms.has(field.type)) : {};
|
|
1548
|
+
return this._transforms.get(field.type);
|
|
1549
|
+
}
|
|
1550
|
+
derivation(field) {
|
|
1551
|
+
const kind = 'kind' in field ? field.kind : '<unknown kind>';
|
|
1552
|
+
const name = 'name' in field ? field.name : '<unknown name>';
|
|
1553
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1554
|
+
if (!test) {
|
|
1555
|
+
throw new Error(`The '${kind}' field '${name}' is not derived and so cannot be used to lookup a derivation`);
|
|
1556
|
+
}
|
|
1557
|
+
})(!('kind' in field) || kind === 'derived') : {};
|
|
1558
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1559
|
+
if (!test) {
|
|
1560
|
+
throw new Error(`Expected the '${kind}' field '${name}' to specify a derivation via 'field.type', but no value was present`);
|
|
1561
|
+
}
|
|
1562
|
+
})(field.type) : {};
|
|
1563
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1564
|
+
if (!test) {
|
|
1565
|
+
throw new Error(`No '${field.type}' derivation registered for use by the '${kind}' field '${name}'`);
|
|
1566
|
+
}
|
|
1567
|
+
})(this._derivations.has(field.type)) : {};
|
|
1568
|
+
return this._derivations.get(field.type);
|
|
1569
|
+
}
|
|
1570
|
+
hashFn(field) {
|
|
1571
|
+
const kind = 'kind' in field ? field.kind : '<unknown kind>';
|
|
1572
|
+
const name = 'name' in field ? field.name : '<unknown name>';
|
|
1573
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1574
|
+
if (!test) {
|
|
1575
|
+
throw new Error(`The '${kind}' field '${name}' is not a HashField and so cannot be used to lookup a hash function`);
|
|
1576
|
+
}
|
|
1577
|
+
})(!('kind' in field) || kind === '@hash') : {};
|
|
1578
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1579
|
+
if (!test) {
|
|
1580
|
+
throw new Error(`Expected the '${kind}' field '${name}' to specify a hash function via 'field.type', but no value was present`);
|
|
1581
|
+
}
|
|
1582
|
+
})(field.type) : {};
|
|
1583
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1584
|
+
if (!test) {
|
|
1585
|
+
throw new Error(`No '${field.type}' hash function is registered for use by the '${kind}' field '${name}'`);
|
|
1586
|
+
}
|
|
1587
|
+
})(this._hashFns.has(field.type)) : {};
|
|
1588
|
+
return this._hashFns.get(field.type);
|
|
1589
|
+
}
|
|
1590
|
+
resource(resource) {
|
|
1591
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1592
|
+
if (!test) {
|
|
1593
|
+
throw new Error(`No resource registered with name '${resource.type}'`);
|
|
1594
|
+
}
|
|
1595
|
+
})(this._schemas.has(resource.type)) : {};
|
|
1596
|
+
return this._schemas.get(resource.type).original;
|
|
1597
|
+
}
|
|
1598
|
+
registerResources(schemas) {
|
|
1599
|
+
schemas.forEach(schema => {
|
|
1600
|
+
this.registerResource(schema);
|
|
1601
|
+
});
|
|
1602
|
+
}
|
|
1603
|
+
registerResource(schema) {
|
|
1604
|
+
const fields = new Map();
|
|
1605
|
+
const relationships = {};
|
|
1606
|
+
const attributes = {};
|
|
1607
|
+
schema.fields.forEach(field => {
|
|
1608
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1609
|
+
if (!test) {
|
|
1610
|
+
throw new Error(`${field.kind} is not valid inside a ResourceSchema's fields.`);
|
|
1611
|
+
}
|
|
1612
|
+
})(
|
|
1613
|
+
// @ts-expect-error we are checking for mistakes at runtime
|
|
1614
|
+
field.kind !== '@id' && field.kind !== '@hash') : {};
|
|
1615
|
+
fields.set(field.name, field);
|
|
1616
|
+
if (field.kind === 'attribute') {
|
|
1617
|
+
attributes[field.name] = field;
|
|
1618
|
+
} else if (field.kind === 'belongsTo' || field.kind === 'hasMany') {
|
|
1619
|
+
relationships[field.name] = field;
|
|
1620
|
+
}
|
|
1621
|
+
});
|
|
1622
|
+
const traits = new Set(isResourceSchema(schema) ? schema.traits : []);
|
|
1623
|
+
traits.forEach(trait => {
|
|
1624
|
+
this._traits.add(trait);
|
|
1625
|
+
});
|
|
1626
|
+
const internalSchema = {
|
|
1627
|
+
original: schema,
|
|
1628
|
+
fields,
|
|
1629
|
+
relationships,
|
|
1630
|
+
attributes,
|
|
1631
|
+
traits
|
|
1632
|
+
};
|
|
1633
|
+
this._schemas.set(schema.type, internalSchema);
|
|
1634
|
+
}
|
|
1635
|
+
registerTransformation(transformation) {
|
|
1636
|
+
this._transforms.set(transformation[Type], transformation);
|
|
1637
|
+
}
|
|
1638
|
+
registerDerivation(derivation) {
|
|
1639
|
+
this._derivations.set(derivation[Type], makeCachedDerivation(derivation));
|
|
1640
|
+
}
|
|
1641
|
+
registerHashFn(hashFn) {
|
|
1642
|
+
this._hashFns.set(hashFn[Type], hashFn);
|
|
1643
|
+
}
|
|
1644
|
+
fields({
|
|
1645
|
+
type
|
|
1646
|
+
}) {
|
|
1647
|
+
const schema = this._schemas.get(type);
|
|
1648
|
+
if (!schema) {
|
|
1649
|
+
throw new Error(`No schema defined for ${type}`);
|
|
1650
|
+
}
|
|
1651
|
+
return schema.fields;
|
|
1652
|
+
}
|
|
1653
|
+
hasResource(resource) {
|
|
1654
|
+
return this._schemas.has(resource.type);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
if (macroCondition(getGlobalConfig().WarpDriveMirror.deprecations.ENABLE_LEGACY_SCHEMA_SERVICE)) {
|
|
1658
|
+
SchemaService.prototype.attributesDefinitionFor = function ({
|
|
1659
|
+
type
|
|
1660
|
+
}) {
|
|
1661
|
+
deprecate(`Use \`schema.fields({ type })\` instead of \`schema.attributesDefinitionFor({ type })\``, false, {
|
|
1662
|
+
id: 'ember-data-mirror:schema-service-updates',
|
|
1663
|
+
until: '6.0',
|
|
1664
|
+
for: 'ember-data-mirror',
|
|
1665
|
+
since: {
|
|
1666
|
+
available: '4.13',
|
|
1667
|
+
enabled: '5.4'
|
|
1668
|
+
}
|
|
1669
|
+
});
|
|
1670
|
+
const schema = this._schemas.get(type);
|
|
1671
|
+
if (!schema) {
|
|
1672
|
+
throw new Error(`No schema defined for ${type}`);
|
|
1673
|
+
}
|
|
1674
|
+
return schema.attributes;
|
|
1675
|
+
};
|
|
1676
|
+
SchemaService.prototype.relationshipsDefinitionFor = function ({
|
|
1677
|
+
type
|
|
1678
|
+
}) {
|
|
1679
|
+
deprecate(`Use \`schema.fields({ type })\` instead of \`schema.relationshipsDefinitionFor({ type })\``, false, {
|
|
1680
|
+
id: 'ember-data-mirror:schema-service-updates',
|
|
1681
|
+
until: '6.0',
|
|
1682
|
+
for: 'ember-data-mirror',
|
|
1683
|
+
since: {
|
|
1684
|
+
available: '4.13',
|
|
1685
|
+
enabled: '5.4'
|
|
1686
|
+
}
|
|
1687
|
+
});
|
|
1688
|
+
const schema = this._schemas.get(type);
|
|
1689
|
+
if (!schema) {
|
|
1690
|
+
throw new Error(`No schema defined for ${type}`);
|
|
1691
|
+
}
|
|
1692
|
+
return schema.relationships;
|
|
1693
|
+
};
|
|
1694
|
+
SchemaService.prototype.doesTypeExist = function (type) {
|
|
1695
|
+
deprecate(`Use \`schema.hasResource({ type })\` instead of \`schema.doesTypeExist(type)\``, false, {
|
|
1696
|
+
id: 'ember-data-mirror:schema-service-updates',
|
|
1697
|
+
until: '6.0',
|
|
1698
|
+
for: 'ember-data-mirror',
|
|
1699
|
+
since: {
|
|
1700
|
+
available: '4.13',
|
|
1701
|
+
enabled: '5.4'
|
|
1702
|
+
}
|
|
1703
|
+
});
|
|
1704
|
+
return this._schemas.has(type);
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
export { Checkout, SchemaService, fromIdentity, instantiateRecord, registerDerivations, teardownRecord, withDefaults };
|