@warp-drive/schema-record 4.13.0-alpha.5 → 4.13.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -16
- package/dist/index.js +214 -78
- package/dist/index.js.map +1 -1
- package/package.json +17 -16
- package/unstable-preview-types/-private/fields/managed-object.d.ts.map +1 -1
- package/unstable-preview-types/-private/hooks.d.ts.map +1 -1
- package/unstable-preview-types/-private/record.d.ts.map +1 -1
- package/unstable-preview-types/-private/schema.d.ts +58 -9
- package/unstable-preview-types/-private/schema.d.ts.map +1 -1
- package/unstable-preview-types/index.d.ts +337 -0
- package/unstable-preview-types/index.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -1,23 +1,21 @@
|
|
|
1
1
|
<p align="center">
|
|
2
2
|
<img
|
|
3
3
|
class="project-logo"
|
|
4
|
-
src="./logos/
|
|
5
|
-
alt="WarpDrive"
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
src="./logos/github-header.svg#gh-light-mode-only"
|
|
5
|
+
alt="WarpDrive | Boldly go where no app has gone before"
|
|
6
|
+
title="WarpDrive | Boldly go where no app has gone before"
|
|
7
|
+
/>
|
|
8
8
|
<img
|
|
9
9
|
class="project-logo"
|
|
10
|
-
src="./logos/
|
|
11
|
-
alt="WarpDrive"
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
src="./logos/github-header.svg#gh-dark-mode-only"
|
|
11
|
+
alt="WarpDrive | Boldly go where no app has gone before"
|
|
12
|
+
title="WarpDrive | Boldly go where no app has gone before"
|
|
13
|
+
/>
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
16
|
<h3 align="center">Your Data, Managed.</h3>
|
|
17
17
|
<p align="center">🌲 Get back to Nature 🐿️ Or shipping 💚</p>
|
|
18
18
|
|
|
19
|
-
SchemaRecord is:
|
|
20
|
-
|
|
21
19
|
- ⚡️ Fast
|
|
22
20
|
- 📦 Tiny
|
|
23
21
|
- ✨ Optimized
|
|
@@ -25,11 +23,15 @@ SchemaRecord is:
|
|
|
25
23
|
- ⚛️ Universal
|
|
26
24
|
- ☢️ Reactive
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
26
|
+
SchemaRecord is a reactive object that transforms raw data from an [associated cache](https://github.com/emberjs/data/blob/main/packages/core-types/src/cache.ts) into reactive data backed by Signals.
|
|
27
|
+
|
|
28
|
+
The shape of the object and the transformation of raw cache data into its
|
|
29
|
+
reactive form is controlled by a resource schema.
|
|
30
|
+
|
|
31
|
+
Resource schemas are simple JSON, allowing them to be defined and delivered from anywhere.
|
|
32
|
+
|
|
33
|
+
The capabilities that SchemaRecord brings to [*Warp***Drive**](https://github.com/emberjs/data/)
|
|
34
|
+
will simplify even the most complex parts of your app's state management.
|
|
33
35
|
|
|
34
36
|
## Installation
|
|
35
37
|
|
|
@@ -150,7 +152,8 @@ store.registerSchemas([
|
|
|
150
152
|
options: {
|
|
151
153
|
async: false,
|
|
152
154
|
inverse: 'owner',
|
|
153
|
-
polymorphic: true
|
|
155
|
+
polymorphic: true,
|
|
156
|
+
linksMode: true,
|
|
154
157
|
}
|
|
155
158
|
}
|
|
156
159
|
]
|
|
@@ -174,6 +177,7 @@ store.registerSchemas([
|
|
|
174
177
|
async: false,
|
|
175
178
|
inverse: 'pets',
|
|
176
179
|
as: 'pet',
|
|
180
|
+
linksMode: true,
|
|
177
181
|
}
|
|
178
182
|
}
|
|
179
183
|
]
|
|
@@ -286,6 +290,16 @@ store.registerSchemas([
|
|
|
286
290
|
]);
|
|
287
291
|
```
|
|
288
292
|
|
|
293
|
+
Additionally, `@warp-drive/core-types` provides several utilities for type-checking and narrowing schemas.
|
|
294
|
+
|
|
295
|
+
- (type) [PolarisResourceSchema]()
|
|
296
|
+
- (type) [LegacyResourceSchema]()
|
|
297
|
+
- (type) [ObjectSchema]()
|
|
298
|
+
- [resourceSchema]()
|
|
299
|
+
- [objectSchema]()
|
|
300
|
+
- [isResourceSchema]()
|
|
301
|
+
- [isLegacyResourceSchema]()
|
|
302
|
+
|
|
289
303
|
|
|
290
304
|
### Field Schemas
|
|
291
305
|
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isResourceSchema } from '@warp-drive/core-types/schema/fields';
|
|
1
2
|
import { macroCondition, getGlobalConfig, dependencySatisfies, importSync } from '@embroider/macros';
|
|
2
3
|
import { SOURCE as SOURCE$1, fastPush, RelatedCollection, setRecordIdentifier, recordIdentifierFor } from '@ember-data/store/-private';
|
|
3
4
|
import { createSignal, subscribe, defineSignal, peekSignal, getSignal, Signals, addToTransaction, entangleSignal } from '@ember-data/tracking/-private';
|
|
@@ -307,7 +308,7 @@ class ManagedObject {
|
|
|
307
308
|
return self[prop];
|
|
308
309
|
}
|
|
309
310
|
if (prop === Symbol.toPrimitive) {
|
|
310
|
-
return null;
|
|
311
|
+
return () => null;
|
|
311
312
|
}
|
|
312
313
|
if (prop === Symbol.toStringTag) {
|
|
313
314
|
return `ManagedObject<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
@@ -322,7 +323,12 @@ class ManagedObject {
|
|
|
322
323
|
}
|
|
323
324
|
if (prop === 'toHTML') {
|
|
324
325
|
return function () {
|
|
325
|
-
return '<
|
|
326
|
+
return '<span>ManagedObject</span>';
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (prop === 'toJSON') {
|
|
330
|
+
return function () {
|
|
331
|
+
return structuredClone(self[SOURCE]);
|
|
326
332
|
};
|
|
327
333
|
}
|
|
328
334
|
if (_SIGNAL.shouldReset) {
|
|
@@ -661,6 +667,9 @@ const RecordSymbols = new Set(symbolList);
|
|
|
661
667
|
function isPathMatch(a, b) {
|
|
662
668
|
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
663
669
|
}
|
|
670
|
+
function isNonEnumerableProp(prop) {
|
|
671
|
+
return prop === 'constructor' || prop === 'prototype' || prop === '__proto__' || prop === 'toString' || prop === 'toJSON' || prop === 'toHTML' || typeof prop === 'symbol';
|
|
672
|
+
}
|
|
664
673
|
const Editables = new WeakMap();
|
|
665
674
|
class SchemaRecord {
|
|
666
675
|
constructor(store, identifier, Mode, isEmbedded = false, embeddedType = null, embeddedPath = null) {
|
|
@@ -676,22 +685,25 @@ class SchemaRecord {
|
|
|
676
685
|
this[Legacy] = Mode[Legacy] ?? false;
|
|
677
686
|
const schema = store.schema;
|
|
678
687
|
const cache = store.cache;
|
|
679
|
-
const identityField = schema.resource(
|
|
688
|
+
const identityField = schema.resource(isEmbedded ? {
|
|
689
|
+
type: embeddedType
|
|
690
|
+
} : identifier).identity;
|
|
691
|
+
const BoundFns = new Map();
|
|
680
692
|
this[EmbeddedType] = embeddedType;
|
|
681
693
|
this[EmbeddedPath] = embeddedPath;
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
type: embeddedType
|
|
686
|
-
});
|
|
687
|
-
} else {
|
|
688
|
-
fields = schema.fields(identifier);
|
|
689
|
-
}
|
|
694
|
+
const fields = isEmbedded ? schema.fields({
|
|
695
|
+
type: embeddedType
|
|
696
|
+
}) : schema.fields(identifier);
|
|
690
697
|
const signals = new Map();
|
|
691
698
|
this[Signals] = signals;
|
|
692
699
|
const proxy = new Proxy(this, {
|
|
693
700
|
ownKeys() {
|
|
694
|
-
|
|
701
|
+
const identityKey = identityField?.name;
|
|
702
|
+
const keys = Array.from(fields.keys());
|
|
703
|
+
if (identityKey) {
|
|
704
|
+
keys.unshift(identityKey);
|
|
705
|
+
}
|
|
706
|
+
return keys;
|
|
695
707
|
},
|
|
696
708
|
has(target, prop) {
|
|
697
709
|
if (prop === Destroy || prop === Checkout) {
|
|
@@ -700,10 +712,19 @@ class SchemaRecord {
|
|
|
700
712
|
return fields.has(prop);
|
|
701
713
|
},
|
|
702
714
|
getOwnPropertyDescriptor(target, prop) {
|
|
703
|
-
|
|
704
|
-
|
|
715
|
+
const schemaForField = prop === identityField?.name ? identityField : fields.get(prop);
|
|
716
|
+
macroCondition(getGlobalConfig().WarpDrive.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
|
+
};
|
|
705
727
|
}
|
|
706
|
-
const schemaForField = fields.get(prop);
|
|
707
728
|
switch (schemaForField.kind) {
|
|
708
729
|
case 'derived':
|
|
709
730
|
return {
|
|
@@ -711,6 +732,12 @@ class SchemaRecord {
|
|
|
711
732
|
enumerable: true,
|
|
712
733
|
configurable: true
|
|
713
734
|
};
|
|
735
|
+
case '@id':
|
|
736
|
+
return {
|
|
737
|
+
writable: identifier.id === null,
|
|
738
|
+
enumerable: true,
|
|
739
|
+
configurable: true
|
|
740
|
+
};
|
|
714
741
|
case '@local':
|
|
715
742
|
case 'field':
|
|
716
743
|
case 'attribute':
|
|
@@ -728,28 +755,18 @@ class SchemaRecord {
|
|
|
728
755
|
enumerable: true,
|
|
729
756
|
configurable: true
|
|
730
757
|
};
|
|
758
|
+
default:
|
|
759
|
+
return {
|
|
760
|
+
writable: false,
|
|
761
|
+
enumerable: false,
|
|
762
|
+
configurable: false
|
|
763
|
+
};
|
|
731
764
|
}
|
|
732
765
|
},
|
|
733
766
|
get(target, prop, receiver) {
|
|
734
767
|
if (RecordSymbols.has(prop)) {
|
|
735
768
|
return target[prop];
|
|
736
769
|
}
|
|
737
|
-
if (prop === Symbol.toStringTag) {
|
|
738
|
-
return `SchemaRecord<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
739
|
-
}
|
|
740
|
-
if (prop === 'toString') {
|
|
741
|
-
return function () {
|
|
742
|
-
return `SchemaRecord<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
743
|
-
};
|
|
744
|
-
}
|
|
745
|
-
if (prop === 'toHTML') {
|
|
746
|
-
return function () {
|
|
747
|
-
return `<div>SchemaRecord<${identifier.type}:${identifier.id} (${identifier.lid})></div>`;
|
|
748
|
-
};
|
|
749
|
-
}
|
|
750
|
-
if (prop === Symbol.toPrimitive) {
|
|
751
|
-
return null;
|
|
752
|
-
}
|
|
753
770
|
|
|
754
771
|
// TODO make this a symbol
|
|
755
772
|
if (prop === '___notifications') {
|
|
@@ -765,18 +782,75 @@ class SchemaRecord {
|
|
|
765
782
|
if (IgnoredGlobalFields.has(prop)) {
|
|
766
783
|
return undefined;
|
|
767
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
|
+
}
|
|
768
843
|
if (prop === 'constructor') {
|
|
769
844
|
return SchemaRecord;
|
|
770
845
|
}
|
|
771
846
|
// too many things check for random symbols
|
|
772
|
-
if (typeof prop === 'symbol')
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
throw new Error(`No field named ${String(prop)} on ${type}`);
|
|
847
|
+
if (typeof prop === 'symbol') return undefined;
|
|
848
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
849
|
+
{
|
|
850
|
+
throw new Error(`No field named ${String(prop)} on ${isEmbedded ? embeddedType : identifier.type}`);
|
|
851
|
+
}
|
|
852
|
+
})() : {};
|
|
853
|
+
return undefined;
|
|
780
854
|
}
|
|
781
855
|
const field = maybeField.kind === 'alias' ? maybeField.options : maybeField;
|
|
782
856
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
@@ -804,49 +878,24 @@ class SchemaRecord {
|
|
|
804
878
|
return lastValue;
|
|
805
879
|
}
|
|
806
880
|
case 'field':
|
|
807
|
-
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
808
|
-
if (!test) {
|
|
809
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
810
|
-
}
|
|
811
|
-
})(!target[Legacy]) : {};
|
|
812
881
|
entangleSignal(signals, receiver, field.name);
|
|
813
882
|
return computeField(schema, cache, target, identifier, field, propArray, IS_EDITABLE);
|
|
814
883
|
case 'attribute':
|
|
815
884
|
entangleSignal(signals, receiver, field.name);
|
|
816
885
|
return computeAttribute(cache, identifier, prop, IS_EDITABLE);
|
|
817
886
|
case 'resource':
|
|
818
|
-
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
819
|
-
if (!test) {
|
|
820
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
821
|
-
}
|
|
822
|
-
})(!target[Legacy]) : {};
|
|
823
887
|
entangleSignal(signals, receiver, field.name);
|
|
824
888
|
return computeResource(store, cache, target, identifier, field, prop, IS_EDITABLE);
|
|
825
889
|
case 'derived':
|
|
826
890
|
return computeDerivation(schema, receiver, identifier, field, prop);
|
|
827
891
|
case 'schema-array':
|
|
828
892
|
case 'array':
|
|
829
|
-
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
830
|
-
if (!test) {
|
|
831
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
832
|
-
}
|
|
833
|
-
})(!target[Legacy]) : {};
|
|
834
893
|
entangleSignal(signals, receiver, field.name);
|
|
835
894
|
return computeArray(store, schema, cache, target, identifier, field, propArray, Mode[Editable], Mode[Legacy]);
|
|
836
895
|
case 'object':
|
|
837
|
-
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
838
|
-
if (!test) {
|
|
839
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
840
|
-
}
|
|
841
|
-
})(!target[Legacy]) : {};
|
|
842
896
|
entangleSignal(signals, receiver, field.name);
|
|
843
897
|
return computeObject(schema, cache, target, identifier, field, propArray, Mode[Editable], Mode[Legacy]);
|
|
844
898
|
case 'schema-object':
|
|
845
|
-
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
846
|
-
if (!test) {
|
|
847
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
848
|
-
}
|
|
849
|
-
})(!target[Legacy]) : {};
|
|
850
899
|
entangleSignal(signals, receiver, field.name);
|
|
851
900
|
// run transform, then use that value as the object to manage
|
|
852
901
|
return computeSchemaObject(store, cache, target, identifier, field, propArray, Mode[Legacy], Mode[Editable]);
|
|
@@ -1292,6 +1341,19 @@ class SchemaRecord {
|
|
|
1292
1341
|
break;
|
|
1293
1342
|
}
|
|
1294
1343
|
});
|
|
1344
|
+
if (macroCondition(getGlobalConfig().WarpDrive.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
|
+
}
|
|
1295
1357
|
return proxy;
|
|
1296
1358
|
}
|
|
1297
1359
|
[Destroy]() {
|
|
@@ -1328,7 +1390,13 @@ class SchemaRecord {
|
|
|
1328
1390
|
}
|
|
1329
1391
|
function instantiateRecord(store, identifier, createArgs) {
|
|
1330
1392
|
const schema = store.schema;
|
|
1331
|
-
const
|
|
1393
|
+
const resourceSchema = schema.resource(identifier);
|
|
1394
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
1395
|
+
if (!test) {
|
|
1396
|
+
throw new Error(`Expected a resource schema`);
|
|
1397
|
+
}
|
|
1398
|
+
})(isResourceSchema(resourceSchema)) : {};
|
|
1399
|
+
const isLegacy = resourceSchema?.legacy ?? false;
|
|
1332
1400
|
const isEditable = isLegacy || store.cache.isNew(identifier);
|
|
1333
1401
|
const record = new SchemaRecord(store, identifier, {
|
|
1334
1402
|
[Editable]: isEditable,
|
|
@@ -1350,19 +1418,28 @@ function teardownRecord(record) {
|
|
|
1350
1418
|
assertSchemaRecord(record);
|
|
1351
1419
|
record[Destroy]();
|
|
1352
1420
|
}
|
|
1421
|
+
|
|
1422
|
+
/**
|
|
1423
|
+
* @module @warp-drive/schema-record
|
|
1424
|
+
*/
|
|
1353
1425
|
const Support = getOrSetGlobal('Support', new WeakMap());
|
|
1354
|
-
const
|
|
1426
|
+
const ConstructorField = {
|
|
1355
1427
|
type: '@constructor',
|
|
1356
1428
|
name: 'constructor',
|
|
1357
1429
|
kind: 'derived'
|
|
1358
|
-
}
|
|
1430
|
+
};
|
|
1431
|
+
const TypeField = {
|
|
1359
1432
|
type: '@identity',
|
|
1360
1433
|
name: '$type',
|
|
1361
1434
|
kind: 'derived',
|
|
1362
1435
|
options: {
|
|
1363
1436
|
key: 'type'
|
|
1364
1437
|
}
|
|
1365
|
-
}
|
|
1438
|
+
};
|
|
1439
|
+
const DefaultIdentityField = {
|
|
1440
|
+
name: 'id',
|
|
1441
|
+
kind: '@id'
|
|
1442
|
+
};
|
|
1366
1443
|
function _constructor(record) {
|
|
1367
1444
|
let state = Support.get(record);
|
|
1368
1445
|
if (!state) {
|
|
@@ -1372,19 +1449,64 @@ function _constructor(record) {
|
|
|
1372
1449
|
return state._constructor = state._constructor || {
|
|
1373
1450
|
name: `SchemaRecord<${recordIdentifierFor$1(record).type}>`,
|
|
1374
1451
|
get modelName() {
|
|
1375
|
-
|
|
1452
|
+
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
1453
|
+
{
|
|
1454
|
+
throw new Error(`record.constructor.modelName is not available outside of legacy mode`);
|
|
1455
|
+
}
|
|
1456
|
+
})() : {};
|
|
1457
|
+
return undefined;
|
|
1376
1458
|
}
|
|
1377
1459
|
};
|
|
1378
1460
|
}
|
|
1379
1461
|
_constructor[Type] = '@constructor';
|
|
1462
|
+
|
|
1463
|
+
/**
|
|
1464
|
+
* Utility for constructing a ResourceSchema with the recommended fields
|
|
1465
|
+
* for the Polaris experience.
|
|
1466
|
+
*
|
|
1467
|
+
* @method withDefaults
|
|
1468
|
+
* @for @warp-drive/schema-record
|
|
1469
|
+
* @static
|
|
1470
|
+
* @public
|
|
1471
|
+
* @param schema
|
|
1472
|
+
* @return {ResourceSchema}
|
|
1473
|
+
*/
|
|
1380
1474
|
function withDefaults(schema) {
|
|
1381
|
-
schema.identity = schema.identity ||
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1475
|
+
schema.identity = schema.identity || DefaultIdentityField;
|
|
1476
|
+
|
|
1477
|
+
// because fields gets iterated in definition order,
|
|
1478
|
+
// we add TypeField to the beginning so that it will
|
|
1479
|
+
// appear right next to the identity field
|
|
1480
|
+
schema.fields.unshift(TypeField);
|
|
1481
|
+
schema.fields.push(ConstructorField);
|
|
1386
1482
|
return schema;
|
|
1387
1483
|
}
|
|
1484
|
+
|
|
1485
|
+
/**
|
|
1486
|
+
* A derivation that computes its value from the
|
|
1487
|
+
* record's identity.
|
|
1488
|
+
*
|
|
1489
|
+
* It can be used via a derived field definition like:
|
|
1490
|
+
*
|
|
1491
|
+
* ```ts
|
|
1492
|
+
* {
|
|
1493
|
+
* kind: 'derived',
|
|
1494
|
+
* name: 'id',
|
|
1495
|
+
* type: '@identity',
|
|
1496
|
+
* options: { key: 'id' }
|
|
1497
|
+
* }
|
|
1498
|
+
* ```
|
|
1499
|
+
*
|
|
1500
|
+
* Valid keys are `'id'`, `'lid'`, `'type'`, and `'^'`.
|
|
1501
|
+
*
|
|
1502
|
+
* `^` returns the entire identifier object.
|
|
1503
|
+
*
|
|
1504
|
+
* @method fromIdentity
|
|
1505
|
+
* @for @warp-drive/schema-record
|
|
1506
|
+
* @static
|
|
1507
|
+
* @public
|
|
1508
|
+
*/
|
|
1509
|
+
|
|
1388
1510
|
function fromIdentity(record, options, key) {
|
|
1389
1511
|
const identifier = record[Identifier];
|
|
1390
1512
|
macroCondition(getGlobalConfig().WarpDrive.env.DEBUG) ? (test => {
|
|
@@ -1400,6 +1522,16 @@ function fromIdentity(record, options, key) {
|
|
|
1400
1522
|
return options.key === '^' ? identifier : identifier[options.key];
|
|
1401
1523
|
}
|
|
1402
1524
|
fromIdentity[Type] = '@identity';
|
|
1525
|
+
|
|
1526
|
+
/**
|
|
1527
|
+
* Registers the default derivations for the SchemaRecord
|
|
1528
|
+
*
|
|
1529
|
+
* @method registerDerivations
|
|
1530
|
+
* @for @warp-drive/schema-record
|
|
1531
|
+
* @static
|
|
1532
|
+
* @public
|
|
1533
|
+
* @param {SchemaService} schema
|
|
1534
|
+
*/
|
|
1403
1535
|
function registerDerivations(schema) {
|
|
1404
1536
|
schema.registerDerivation(fromIdentity);
|
|
1405
1537
|
schema.registerDerivation(_constructor);
|
|
@@ -1408,9 +1540,7 @@ function registerDerivations(schema) {
|
|
|
1408
1540
|
* Wraps a derivation in a new function with Derivation signature but that looks
|
|
1409
1541
|
* up the value in the cache before recomputing.
|
|
1410
1542
|
*
|
|
1411
|
-
* @
|
|
1412
|
-
* @param options
|
|
1413
|
-
* @param prop
|
|
1543
|
+
* @internal
|
|
1414
1544
|
*/
|
|
1415
1545
|
function makeCachedDerivation(derivation) {
|
|
1416
1546
|
const memoizedDerivation = (record, options, prop) => {
|
|
@@ -1427,6 +1557,12 @@ function makeCachedDerivation(derivation) {
|
|
|
1427
1557
|
memoizedDerivation[Type] = derivation[Type];
|
|
1428
1558
|
return memoizedDerivation;
|
|
1429
1559
|
}
|
|
1560
|
+
/**
|
|
1561
|
+
* A SchemaService designed to work with dynamically registered schemas.
|
|
1562
|
+
*
|
|
1563
|
+
* @class SchemaService
|
|
1564
|
+
* @public
|
|
1565
|
+
*/
|
|
1430
1566
|
class SchemaService {
|
|
1431
1567
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1432
1568
|
|
|
@@ -1534,7 +1670,7 @@ class SchemaService {
|
|
|
1534
1670
|
relationships[field.name] = field;
|
|
1535
1671
|
}
|
|
1536
1672
|
});
|
|
1537
|
-
const traits = new Set(schema.traits);
|
|
1673
|
+
const traits = new Set(isResourceSchema(schema) ? schema.traits : []);
|
|
1538
1674
|
traits.forEach(trait => {
|
|
1539
1675
|
this._traits.add(trait);
|
|
1540
1676
|
});
|