@warp-drive-mirror/schema-record 0.0.0-beta.17 → 0.0.0-beta.19
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 +32 -18
- package/dist/index.js +203 -78
- package/dist/index.js.map +1 -1
- package/package.json +24 -24
- package/unstable-preview-types/-private/fields/managed-object.d.ts.map +1 -1
- package/unstable-preview-types/-private/fields/many-array-manager.d.ts.map +1 -1
- package/unstable-preview-types/-private/record.d.ts.map +1 -1
- package/unstable-preview-types/-private/schema.d.ts +53 -3
- 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
|
|
|
@@ -124,7 +126,7 @@ We could describe the `'user'` and `'dog'` resources in the above payload
|
|
|
124
126
|
with the following schemas:
|
|
125
127
|
|
|
126
128
|
```ts
|
|
127
|
-
store.
|
|
129
|
+
store.schema.registerResources([
|
|
128
130
|
{
|
|
129
131
|
type: 'user',
|
|
130
132
|
identity: { type: '@id', name: 'id' },
|
|
@@ -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
|
]
|
|
@@ -243,7 +247,7 @@ definition above using this utility like so:
|
|
|
243
247
|
```ts
|
|
244
248
|
import { withDefaults } from '@warp-drive-mirror/schema-record';
|
|
245
249
|
|
|
246
|
-
store.
|
|
250
|
+
store.schema.registerResources([
|
|
247
251
|
withDefaults({
|
|
248
252
|
type: 'user',
|
|
249
253
|
fields: [
|
|
@@ -286,6 +290,16 @@ store.registerSchemas([
|
|
|
286
290
|
]);
|
|
287
291
|
```
|
|
288
292
|
|
|
293
|
+
Additionally, `@warp-drive-mirror/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
|
@@ -308,7 +308,7 @@ class ManagedObject {
|
|
|
308
308
|
return self[prop];
|
|
309
309
|
}
|
|
310
310
|
if (prop === Symbol.toPrimitive) {
|
|
311
|
-
return null;
|
|
311
|
+
return () => null;
|
|
312
312
|
}
|
|
313
313
|
if (prop === Symbol.toStringTag) {
|
|
314
314
|
return `ManagedObject<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
@@ -323,7 +323,12 @@ class ManagedObject {
|
|
|
323
323
|
}
|
|
324
324
|
if (prop === 'toHTML') {
|
|
325
325
|
return function () {
|
|
326
|
-
return '<
|
|
326
|
+
return '<span>ManagedObject</span>';
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
if (prop === 'toJSON') {
|
|
330
|
+
return function () {
|
|
331
|
+
return structuredClone(self[SOURCE]);
|
|
327
332
|
};
|
|
328
333
|
}
|
|
329
334
|
if (_SIGNAL.shouldReset) {
|
|
@@ -385,8 +390,14 @@ class ManyArrayManager {
|
|
|
385
390
|
array.links = rawValue.links;
|
|
386
391
|
}
|
|
387
392
|
const currentState = array[SOURCE$1];
|
|
388
|
-
|
|
389
|
-
|
|
393
|
+
|
|
394
|
+
// unlike in the normal RecordArray case, we don't need to divorce the reference
|
|
395
|
+
// because we don't need to worry about associate/disassociate since the graph
|
|
396
|
+
// takes care of that for us
|
|
397
|
+
if (currentState !== rawValue.data) {
|
|
398
|
+
currentState.length = 0;
|
|
399
|
+
fastPush(currentState, rawValue.data);
|
|
400
|
+
}
|
|
390
401
|
}
|
|
391
402
|
reloadHasMany(key, options) {
|
|
392
403
|
const field = this.store.schema.fields(this.identifier).get(key);
|
|
@@ -662,6 +673,9 @@ const RecordSymbols = new Set(symbolList);
|
|
|
662
673
|
function isPathMatch(a, b) {
|
|
663
674
|
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
664
675
|
}
|
|
676
|
+
function isNonEnumerableProp(prop) {
|
|
677
|
+
return prop === 'constructor' || prop === 'prototype' || prop === '__proto__' || prop === 'toString' || prop === 'toJSON' || prop === 'toHTML' || typeof prop === 'symbol';
|
|
678
|
+
}
|
|
665
679
|
const Editables = new WeakMap();
|
|
666
680
|
class SchemaRecord {
|
|
667
681
|
constructor(store, identifier, Mode, isEmbedded = false, embeddedType = null, embeddedPath = null) {
|
|
@@ -677,22 +691,25 @@ class SchemaRecord {
|
|
|
677
691
|
this[Legacy] = Mode[Legacy] ?? false;
|
|
678
692
|
const schema = store.schema;
|
|
679
693
|
const cache = store.cache;
|
|
680
|
-
const identityField = schema.resource(
|
|
694
|
+
const identityField = schema.resource(isEmbedded ? {
|
|
695
|
+
type: embeddedType
|
|
696
|
+
} : identifier).identity;
|
|
697
|
+
const BoundFns = new Map();
|
|
681
698
|
this[EmbeddedType] = embeddedType;
|
|
682
699
|
this[EmbeddedPath] = embeddedPath;
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
type: embeddedType
|
|
687
|
-
});
|
|
688
|
-
} else {
|
|
689
|
-
fields = schema.fields(identifier);
|
|
690
|
-
}
|
|
700
|
+
const fields = isEmbedded ? schema.fields({
|
|
701
|
+
type: embeddedType
|
|
702
|
+
}) : schema.fields(identifier);
|
|
691
703
|
const signals = new Map();
|
|
692
704
|
this[Signals] = signals;
|
|
693
705
|
const proxy = new Proxy(this, {
|
|
694
706
|
ownKeys() {
|
|
695
|
-
|
|
707
|
+
const identityKey = identityField?.name;
|
|
708
|
+
const keys = Array.from(fields.keys());
|
|
709
|
+
if (identityKey) {
|
|
710
|
+
keys.unshift(identityKey);
|
|
711
|
+
}
|
|
712
|
+
return keys;
|
|
696
713
|
},
|
|
697
714
|
has(target, prop) {
|
|
698
715
|
if (prop === Destroy || prop === Checkout) {
|
|
@@ -701,10 +718,19 @@ class SchemaRecord {
|
|
|
701
718
|
return fields.has(prop);
|
|
702
719
|
},
|
|
703
720
|
getOwnPropertyDescriptor(target, prop) {
|
|
704
|
-
|
|
705
|
-
|
|
721
|
+
const schemaForField = prop === identityField?.name ? identityField : fields.get(prop);
|
|
722
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
723
|
+
if (!test) {
|
|
724
|
+
throw new Error(`No field named ${String(prop)} on ${identifier.type}`);
|
|
725
|
+
}
|
|
726
|
+
})(schemaForField) : {};
|
|
727
|
+
if (isNonEnumerableProp(prop)) {
|
|
728
|
+
return {
|
|
729
|
+
writable: false,
|
|
730
|
+
enumerable: false,
|
|
731
|
+
configurable: true
|
|
732
|
+
};
|
|
706
733
|
}
|
|
707
|
-
const schemaForField = fields.get(prop);
|
|
708
734
|
switch (schemaForField.kind) {
|
|
709
735
|
case 'derived':
|
|
710
736
|
return {
|
|
@@ -712,6 +738,12 @@ class SchemaRecord {
|
|
|
712
738
|
enumerable: true,
|
|
713
739
|
configurable: true
|
|
714
740
|
};
|
|
741
|
+
case '@id':
|
|
742
|
+
return {
|
|
743
|
+
writable: identifier.id === null,
|
|
744
|
+
enumerable: true,
|
|
745
|
+
configurable: true
|
|
746
|
+
};
|
|
715
747
|
case '@local':
|
|
716
748
|
case 'field':
|
|
717
749
|
case 'attribute':
|
|
@@ -729,28 +761,18 @@ class SchemaRecord {
|
|
|
729
761
|
enumerable: true,
|
|
730
762
|
configurable: true
|
|
731
763
|
};
|
|
764
|
+
default:
|
|
765
|
+
return {
|
|
766
|
+
writable: false,
|
|
767
|
+
enumerable: false,
|
|
768
|
+
configurable: false
|
|
769
|
+
};
|
|
732
770
|
}
|
|
733
771
|
},
|
|
734
772
|
get(target, prop, receiver) {
|
|
735
773
|
if (RecordSymbols.has(prop)) {
|
|
736
774
|
return target[prop];
|
|
737
775
|
}
|
|
738
|
-
if (prop === Symbol.toStringTag) {
|
|
739
|
-
return `SchemaRecord<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
740
|
-
}
|
|
741
|
-
if (prop === 'toString') {
|
|
742
|
-
return function () {
|
|
743
|
-
return `SchemaRecord<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
744
|
-
};
|
|
745
|
-
}
|
|
746
|
-
if (prop === 'toHTML') {
|
|
747
|
-
return function () {
|
|
748
|
-
return `<div>SchemaRecord<${identifier.type}:${identifier.id} (${identifier.lid})></div>`;
|
|
749
|
-
};
|
|
750
|
-
}
|
|
751
|
-
if (prop === Symbol.toPrimitive) {
|
|
752
|
-
return null;
|
|
753
|
-
}
|
|
754
776
|
|
|
755
777
|
// TODO make this a symbol
|
|
756
778
|
if (prop === '___notifications') {
|
|
@@ -766,18 +788,75 @@ class SchemaRecord {
|
|
|
766
788
|
if (IgnoredGlobalFields.has(prop)) {
|
|
767
789
|
return undefined;
|
|
768
790
|
}
|
|
791
|
+
|
|
792
|
+
/////////////////////////////////////////////////////////////
|
|
793
|
+
//// Note these bound function behaviors are essentially ////
|
|
794
|
+
//// built-in but overrideable derivations. ////
|
|
795
|
+
//// ////
|
|
796
|
+
//// The bar for this has to be "basic expectations of ////
|
|
797
|
+
/// an object" – very, very high ////
|
|
798
|
+
/////////////////////////////////////////////////////////////
|
|
799
|
+
|
|
800
|
+
if (prop === Symbol.toStringTag || prop === 'toString') {
|
|
801
|
+
let fn = BoundFns.get('toString');
|
|
802
|
+
if (!fn) {
|
|
803
|
+
fn = function () {
|
|
804
|
+
entangleSignal(signals, receiver, '@identity');
|
|
805
|
+
return `Record<${identifier.type}:${identifier.id} (${identifier.lid})>`;
|
|
806
|
+
};
|
|
807
|
+
BoundFns.set(prop, fn);
|
|
808
|
+
}
|
|
809
|
+
return fn;
|
|
810
|
+
}
|
|
811
|
+
if (prop === 'toHTML') {
|
|
812
|
+
let fn = BoundFns.get('toHTML');
|
|
813
|
+
if (!fn) {
|
|
814
|
+
fn = function () {
|
|
815
|
+
entangleSignal(signals, receiver, '@identity');
|
|
816
|
+
return `<span>Record<${identifier.type}:${identifier.id} (${identifier.lid})></span>`;
|
|
817
|
+
};
|
|
818
|
+
BoundFns.set(prop, fn);
|
|
819
|
+
}
|
|
820
|
+
return fn;
|
|
821
|
+
}
|
|
822
|
+
if (prop === 'toJSON') {
|
|
823
|
+
let fn = BoundFns.get('toJSON');
|
|
824
|
+
if (!fn) {
|
|
825
|
+
fn = function () {
|
|
826
|
+
const json = {};
|
|
827
|
+
for (const key in receiver) {
|
|
828
|
+
json[key] = receiver[key];
|
|
829
|
+
}
|
|
830
|
+
return json;
|
|
831
|
+
};
|
|
832
|
+
BoundFns.set(prop, fn);
|
|
833
|
+
}
|
|
834
|
+
return fn;
|
|
835
|
+
}
|
|
836
|
+
if (prop === Symbol.toPrimitive) return () => null;
|
|
837
|
+
if (prop === Symbol.iterator) {
|
|
838
|
+
let fn = BoundFns.get(Symbol.iterator);
|
|
839
|
+
if (!fn) {
|
|
840
|
+
fn = function* () {
|
|
841
|
+
for (const key in receiver) {
|
|
842
|
+
yield [key, receiver[key]];
|
|
843
|
+
}
|
|
844
|
+
};
|
|
845
|
+
BoundFns.set(Symbol.iterator, fn);
|
|
846
|
+
}
|
|
847
|
+
return fn;
|
|
848
|
+
}
|
|
769
849
|
if (prop === 'constructor') {
|
|
770
850
|
return SchemaRecord;
|
|
771
851
|
}
|
|
772
852
|
// too many things check for random symbols
|
|
773
|
-
if (typeof prop === 'symbol')
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
throw new Error(`No field named ${String(prop)} on ${type}`);
|
|
853
|
+
if (typeof prop === 'symbol') return undefined;
|
|
854
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
855
|
+
{
|
|
856
|
+
throw new Error(`No field named ${String(prop)} on ${isEmbedded ? embeddedType : identifier.type}`);
|
|
857
|
+
}
|
|
858
|
+
})() : {};
|
|
859
|
+
return undefined;
|
|
781
860
|
}
|
|
782
861
|
const field = maybeField.kind === 'alias' ? maybeField.options : maybeField;
|
|
783
862
|
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
@@ -805,49 +884,24 @@ class SchemaRecord {
|
|
|
805
884
|
return lastValue;
|
|
806
885
|
}
|
|
807
886
|
case 'field':
|
|
808
|
-
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
809
|
-
if (!test) {
|
|
810
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
811
|
-
}
|
|
812
|
-
})(!target[Legacy]) : {};
|
|
813
887
|
entangleSignal(signals, receiver, field.name);
|
|
814
888
|
return computeField(schema, cache, target, identifier, field, propArray, IS_EDITABLE);
|
|
815
889
|
case 'attribute':
|
|
816
890
|
entangleSignal(signals, receiver, field.name);
|
|
817
891
|
return computeAttribute(cache, identifier, prop, IS_EDITABLE);
|
|
818
892
|
case 'resource':
|
|
819
|
-
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
820
|
-
if (!test) {
|
|
821
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
822
|
-
}
|
|
823
|
-
})(!target[Legacy]) : {};
|
|
824
893
|
entangleSignal(signals, receiver, field.name);
|
|
825
894
|
return computeResource(store, cache, target, identifier, field, prop, IS_EDITABLE);
|
|
826
895
|
case 'derived':
|
|
827
896
|
return computeDerivation(schema, receiver, identifier, field, prop);
|
|
828
897
|
case 'schema-array':
|
|
829
898
|
case 'array':
|
|
830
|
-
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
831
|
-
if (!test) {
|
|
832
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
833
|
-
}
|
|
834
|
-
})(!target[Legacy]) : {};
|
|
835
899
|
entangleSignal(signals, receiver, field.name);
|
|
836
900
|
return computeArray(store, schema, cache, target, identifier, field, propArray, Mode[Editable], Mode[Legacy]);
|
|
837
901
|
case 'object':
|
|
838
|
-
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
839
|
-
if (!test) {
|
|
840
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
841
|
-
}
|
|
842
|
-
})(!target[Legacy]) : {};
|
|
843
902
|
entangleSignal(signals, receiver, field.name);
|
|
844
903
|
return computeObject(schema, cache, target, identifier, field, propArray, Mode[Editable], Mode[Legacy]);
|
|
845
904
|
case 'schema-object':
|
|
846
|
-
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
847
|
-
if (!test) {
|
|
848
|
-
throw new Error(`SchemaRecord.${field.name} is not available in legacy mode because it has type '${field.kind}'`);
|
|
849
|
-
}
|
|
850
|
-
})(!target[Legacy]) : {};
|
|
851
905
|
entangleSignal(signals, receiver, field.name);
|
|
852
906
|
// run transform, then use that value as the object to manage
|
|
853
907
|
return computeSchemaObject(store, cache, target, identifier, field, propArray, Mode[Legacy], Mode[Editable]);
|
|
@@ -1370,19 +1424,28 @@ function teardownRecord(record) {
|
|
|
1370
1424
|
assertSchemaRecord(record);
|
|
1371
1425
|
record[Destroy]();
|
|
1372
1426
|
}
|
|
1427
|
+
|
|
1428
|
+
/**
|
|
1429
|
+
* @module @warp-drive-mirror/schema-record
|
|
1430
|
+
*/
|
|
1373
1431
|
const Support = getOrSetGlobal('Support', new WeakMap());
|
|
1374
|
-
const
|
|
1432
|
+
const ConstructorField = {
|
|
1375
1433
|
type: '@constructor',
|
|
1376
1434
|
name: 'constructor',
|
|
1377
1435
|
kind: 'derived'
|
|
1378
|
-
}
|
|
1436
|
+
};
|
|
1437
|
+
const TypeField = {
|
|
1379
1438
|
type: '@identity',
|
|
1380
1439
|
name: '$type',
|
|
1381
1440
|
kind: 'derived',
|
|
1382
1441
|
options: {
|
|
1383
1442
|
key: 'type'
|
|
1384
1443
|
}
|
|
1385
|
-
}
|
|
1444
|
+
};
|
|
1445
|
+
const DefaultIdentityField = {
|
|
1446
|
+
name: 'id',
|
|
1447
|
+
kind: '@id'
|
|
1448
|
+
};
|
|
1386
1449
|
function _constructor(record) {
|
|
1387
1450
|
let state = Support.get(record);
|
|
1388
1451
|
if (!state) {
|
|
@@ -1392,19 +1455,64 @@ function _constructor(record) {
|
|
|
1392
1455
|
return state._constructor = state._constructor || {
|
|
1393
1456
|
name: `SchemaRecord<${recordIdentifierFor$1(record).type}>`,
|
|
1394
1457
|
get modelName() {
|
|
1395
|
-
|
|
1458
|
+
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
1459
|
+
{
|
|
1460
|
+
throw new Error(`record.constructor.modelName is not available outside of legacy mode`);
|
|
1461
|
+
}
|
|
1462
|
+
})() : {};
|
|
1463
|
+
return undefined;
|
|
1396
1464
|
}
|
|
1397
1465
|
};
|
|
1398
1466
|
}
|
|
1399
1467
|
_constructor[Type] = '@constructor';
|
|
1468
|
+
|
|
1469
|
+
/**
|
|
1470
|
+
* Utility for constructing a ResourceSchema with the recommended fields
|
|
1471
|
+
* for the Polaris experience.
|
|
1472
|
+
*
|
|
1473
|
+
* @method withDefaults
|
|
1474
|
+
* @for @warp-drive-mirror/schema-record
|
|
1475
|
+
* @static
|
|
1476
|
+
* @public
|
|
1477
|
+
* @param schema
|
|
1478
|
+
* @return {ResourceSchema}
|
|
1479
|
+
*/
|
|
1400
1480
|
function withDefaults(schema) {
|
|
1401
|
-
schema.identity = schema.identity ||
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1481
|
+
schema.identity = schema.identity || DefaultIdentityField;
|
|
1482
|
+
|
|
1483
|
+
// because fields gets iterated in definition order,
|
|
1484
|
+
// we add TypeField to the beginning so that it will
|
|
1485
|
+
// appear right next to the identity field
|
|
1486
|
+
schema.fields.unshift(TypeField);
|
|
1487
|
+
schema.fields.push(ConstructorField);
|
|
1406
1488
|
return schema;
|
|
1407
1489
|
}
|
|
1490
|
+
|
|
1491
|
+
/**
|
|
1492
|
+
* A derivation that computes its value from the
|
|
1493
|
+
* record's identity.
|
|
1494
|
+
*
|
|
1495
|
+
* It can be used via a derived field definition like:
|
|
1496
|
+
*
|
|
1497
|
+
* ```ts
|
|
1498
|
+
* {
|
|
1499
|
+
* kind: 'derived',
|
|
1500
|
+
* name: 'id',
|
|
1501
|
+
* type: '@identity',
|
|
1502
|
+
* options: { key: 'id' }
|
|
1503
|
+
* }
|
|
1504
|
+
* ```
|
|
1505
|
+
*
|
|
1506
|
+
* Valid keys are `'id'`, `'lid'`, `'type'`, and `'^'`.
|
|
1507
|
+
*
|
|
1508
|
+
* `^` returns the entire identifier object.
|
|
1509
|
+
*
|
|
1510
|
+
* @method fromIdentity
|
|
1511
|
+
* @for @warp-drive-mirror/schema-record
|
|
1512
|
+
* @static
|
|
1513
|
+
* @public
|
|
1514
|
+
*/
|
|
1515
|
+
|
|
1408
1516
|
function fromIdentity(record, options, key) {
|
|
1409
1517
|
const identifier = record[Identifier];
|
|
1410
1518
|
macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
|
|
@@ -1420,6 +1528,16 @@ function fromIdentity(record, options, key) {
|
|
|
1420
1528
|
return options.key === '^' ? identifier : identifier[options.key];
|
|
1421
1529
|
}
|
|
1422
1530
|
fromIdentity[Type] = '@identity';
|
|
1531
|
+
|
|
1532
|
+
/**
|
|
1533
|
+
* Registers the default derivations for the SchemaRecord
|
|
1534
|
+
*
|
|
1535
|
+
* @method registerDerivations
|
|
1536
|
+
* @for @warp-drive-mirror/schema-record
|
|
1537
|
+
* @static
|
|
1538
|
+
* @public
|
|
1539
|
+
* @param {SchemaService} schema
|
|
1540
|
+
*/
|
|
1423
1541
|
function registerDerivations(schema) {
|
|
1424
1542
|
schema.registerDerivation(fromIdentity);
|
|
1425
1543
|
schema.registerDerivation(_constructor);
|
|
@@ -1428,9 +1546,7 @@ function registerDerivations(schema) {
|
|
|
1428
1546
|
* Wraps a derivation in a new function with Derivation signature but that looks
|
|
1429
1547
|
* up the value in the cache before recomputing.
|
|
1430
1548
|
*
|
|
1431
|
-
* @
|
|
1432
|
-
* @param options
|
|
1433
|
-
* @param prop
|
|
1549
|
+
* @internal
|
|
1434
1550
|
*/
|
|
1435
1551
|
function makeCachedDerivation(derivation) {
|
|
1436
1552
|
const memoizedDerivation = (record, options, prop) => {
|
|
@@ -1447,6 +1563,12 @@ function makeCachedDerivation(derivation) {
|
|
|
1447
1563
|
memoizedDerivation[Type] = derivation[Type];
|
|
1448
1564
|
return memoizedDerivation;
|
|
1449
1565
|
}
|
|
1566
|
+
/**
|
|
1567
|
+
* A SchemaService designed to work with dynamically registered schemas.
|
|
1568
|
+
*
|
|
1569
|
+
* @class SchemaService
|
|
1570
|
+
* @public
|
|
1571
|
+
*/
|
|
1450
1572
|
class SchemaService {
|
|
1451
1573
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1452
1574
|
|
|
@@ -1456,6 +1578,9 @@ class SchemaService {
|
|
|
1456
1578
|
this._hashFns = new Map();
|
|
1457
1579
|
this._derivations = new Map();
|
|
1458
1580
|
}
|
|
1581
|
+
resourceTypes() {
|
|
1582
|
+
return Array.from(this._schemas.keys());
|
|
1583
|
+
}
|
|
1459
1584
|
hasTrait(type) {
|
|
1460
1585
|
return this._traits.has(type);
|
|
1461
1586
|
}
|