@warp-drive-mirror/schema-record 5.4.1-beta.1 → 5.4.1-beta.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/README.md CHANGED
@@ -23,15 +23,9 @@
23
23
  - ⚛️ Universal
24
24
  - ☢️ Reactive
25
25
 
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.
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. The shape of the object and the transformation of raw cache data into its reactive form is controlled by a resource schema. Resource schemas are simple JSON, allowing them to be defined and delivered from anywhere.
27
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.
28
+ The capabilities that SchemaRecord brings to [*Warp***Drive**](https://github.com/emberjs/data/) will simplify even the most complex parts of your app's state management.
35
29
 
36
30
  ## Installation
37
31
 
@@ -266,7 +260,9 @@ store.schema.registerResources([
266
260
  options: {
267
261
  async: false,
268
262
  inverse: 'owner',
269
- polymorphic: true
263
+ polymorphic: true,
264
+ linksMode: true,
265
+ resetOnRemoteUpdate: false
270
266
  }
271
267
  }
272
268
  ]
@@ -283,6 +279,8 @@ store.schema.registerResources([
283
279
  async: false,
284
280
  inverse: 'pets',
285
281
  as: 'pet',
282
+ linksMode: true,
283
+ resetOnRemoteUpdate: false
286
284
  }
287
285
  }
288
286
  ]
package/dist/-private.js CHANGED
@@ -1 +1 @@
1
- export { E as Editable, L as Legacy } from "./symbols-DqoS4ybV.js";
1
+ export { E as Editable, L as Legacy } from "./symbols-B_60yPO2.js";
package/dist/index.js CHANGED
@@ -1,14 +1,12 @@
1
1
  import { isResourceSchema } from '@warp-drive-mirror/core-types/schema/fields';
2
2
  import { macroCondition, getGlobalConfig, dependencySatisfies, importSync } from '@embroider/macros';
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';
3
+ import { withSignalStore, entangleSignal, ARRAY_SIGNAL, consumeInternalSignal, OBJECT_SIGNAL, SOURCE as SOURCE$1, fastPush, defineSignal, RelatedCollection, getOrCreateInternalSignal, notifyInternalSignal, Signals, setRecordIdentifier, recordIdentifierFor, createMemo } from '@ember-data-mirror/store/-private';
5
4
  import { EnableHydration, STRUCTURED } from '@warp-drive-mirror/core-types/request';
6
5
  import { RecordStore, Type } from '@warp-drive-mirror/core-types/symbols';
7
6
  import { getOrSetGlobal } from '@warp-drive-mirror/core-types/-private';
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";
7
+ import { S as SOURCE, E as Editable, L as Legacy, I as Identifier, P as Parent, a as EmbeddedPath, D as Destroy, C as Checkout, b as EmbeddedType } from "./symbols-B_60yPO2.js";
9
8
  import { deprecate } from '@ember/debug';
10
9
  import { recordIdentifierFor as recordIdentifierFor$1 } from '@ember-data-mirror/store';
11
- import { createCache, getValue } from '@ember-data-mirror/tracking';
12
10
  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']);
13
11
  // const ARRAY_SETTER_METHODS = new Set<KeyType>(['push', 'pop', 'unshift', 'shift', 'splice', 'sort']);
14
12
  const SYNC_PROPS = new Set(['[]', 'length']);
@@ -59,10 +57,13 @@ class ManagedArray {
59
57
  // eslint-disable-next-line @typescript-eslint/no-this-alias
60
58
  const self = this;
61
59
  this[SOURCE] = data?.slice();
62
- this[ARRAY_SIGNAL] = createSignal(this, 'length');
63
60
  const IS_EDITABLE = this[Editable] = editable ?? false;
64
61
  this[Legacy] = legacy;
65
- const _SIGNAL = this[ARRAY_SIGNAL];
62
+
63
+ // FIXME probably can get rid of the manual ARRAY_SIGNAL storage
64
+ // FIXME probably the storage should be on the proxy/receiver not this class
65
+ const signals = withSignalStore(this);
66
+ const _SIGNAL = this[ARRAY_SIGNAL] = entangleSignal(signals, this, ARRAY_SIGNAL, undefined);
66
67
  const boundFns = new Map();
67
68
  this.identifier = identifier;
68
69
  this.path = path;
@@ -90,9 +91,8 @@ class ManagedArray {
90
91
  return self.owner;
91
92
  }
92
93
  const index = convertToInt(prop);
93
- if (_SIGNAL.shouldReset && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
94
- _SIGNAL.t = false;
95
- _SIGNAL.shouldReset = false;
94
+ if (_SIGNAL.isStale && (index !== null || SYNC_PROPS.has(prop) || isArrayGetter(prop))) {
95
+ _SIGNAL.isStale = false;
96
96
  const newData = cache.getAttr(identifier, path);
97
97
  if (newData && newData !== self[SOURCE]) {
98
98
  self[SOURCE].length = 0;
@@ -139,7 +139,7 @@ class ManagedArray {
139
139
  }
140
140
  if (isSchemaArray) {
141
141
  if (!transaction) {
142
- subscribe(_SIGNAL);
142
+ consumeInternalSignal(_SIGNAL);
143
143
  }
144
144
  if (val) {
145
145
  const recordRef = ManagedRecordRefs.get(val);
@@ -171,7 +171,7 @@ class ManagedArray {
171
171
  return val;
172
172
  }
173
173
  if (!transaction) {
174
- subscribe(_SIGNAL);
174
+ consumeInternalSignal(_SIGNAL);
175
175
  }
176
176
  if (field.type) {
177
177
  const transform = schema.transformation(field);
@@ -184,7 +184,7 @@ class ManagedArray {
184
184
  if (fn === undefined) {
185
185
  if (prop === 'forEach') {
186
186
  fn = function () {
187
- subscribe(_SIGNAL);
187
+ consumeInternalSignal(_SIGNAL);
188
188
  transaction = true;
189
189
  const result = safeForEach(receiver, target, store, arguments[0], arguments[1]);
190
190
  transaction = false;
@@ -192,7 +192,7 @@ class ManagedArray {
192
192
  };
193
193
  } else {
194
194
  fn = function () {
195
- subscribe(_SIGNAL);
195
+ consumeInternalSignal(_SIGNAL);
196
196
  // array functions must run through Reflect to work properly
197
197
  // binding via other means will not work.
198
198
  transaction = true;
@@ -212,7 +212,7 @@ class ManagedArray {
212
212
  if (!IS_EDITABLE) {
213
213
  throw new Error(`Mutating this array via ${String(prop)} is not allowed because the record is not editable`);
214
214
  }
215
- subscribe(_SIGNAL);
215
+ consumeInternalSignal(_SIGNAL);
216
216
  transaction = true;
217
217
  const result = Reflect.apply(target[prop], receiver, arguments);
218
218
  transaction = false;
@@ -246,7 +246,7 @@ class ManagedArray {
246
246
  if (reflect) {
247
247
  if (!field.type) {
248
248
  cache.setAttr(identifier, path, self[SOURCE]);
249
- _SIGNAL.shouldReset = true;
249
+ _SIGNAL.isStale = true;
250
250
  return true;
251
251
  }
252
252
  let rawValue = self[SOURCE];
@@ -258,7 +258,7 @@ class ManagedArray {
258
258
  rawValue = self[SOURCE].map(item => transform.serialize(item, field.options ?? null, self.owner));
259
259
  }
260
260
  cache.setAttr(identifier, path, rawValue);
261
- _SIGNAL.shouldReset = true;
261
+ _SIGNAL.isStale = true;
262
262
  }
263
263
  return reflect;
264
264
  },
@@ -283,12 +283,12 @@ class ManagedObject {
283
283
  this[SOURCE] = {
284
284
  ...data
285
285
  };
286
- this[OBJECT_SIGNAL] = createSignal(this, 'length');
286
+ const signals = withSignalStore(this);
287
+ const _SIGNAL = this[OBJECT_SIGNAL] = entangleSignal(signals, this, OBJECT_SIGNAL, undefined);
287
288
  this[Editable] = editable;
288
289
  this[Legacy] = legacy;
289
290
  this[Parent] = identifier;
290
291
  this[EmbeddedPath] = path;
291
- const _SIGNAL = this[OBJECT_SIGNAL];
292
292
  const proxy = new Proxy(this[SOURCE], {
293
293
  ownKeys() {
294
294
  return Object.keys(self[SOURCE]);
@@ -331,9 +331,8 @@ class ManagedObject {
331
331
  return structuredClone(self[SOURCE]);
332
332
  };
333
333
  }
334
- if (_SIGNAL.shouldReset) {
335
- _SIGNAL.t = false;
336
- _SIGNAL.shouldReset = false;
334
+ if (_SIGNAL.isStale) {
335
+ _SIGNAL.isStale = false;
337
336
  let newData = cache.getAttr(identifier, path);
338
337
  if (newData && newData !== self[SOURCE]) {
339
338
  if (field.type) {
@@ -346,7 +345,7 @@ class ManagedObject {
346
345
  }
347
346
  }
348
347
  if (prop in self[SOURCE]) {
349
- subscribe(_SIGNAL);
348
+ consumeInternalSignal(_SIGNAL);
350
349
  return self[SOURCE][prop];
351
350
  }
352
351
  return Reflect.get(target, prop, receiver);
@@ -368,7 +367,7 @@ class ManagedObject {
368
367
  const val = transform.serialize(self[SOURCE], field.options ?? null, owner);
369
368
  cache.setAttr(identifier, path, val);
370
369
  }
371
- _SIGNAL.shouldReset = true;
370
+ _SIGNAL.isStale = true;
372
371
  return true;
373
372
  }
374
373
  });
@@ -376,13 +375,15 @@ class ManagedObject {
376
375
  }
377
376
  }
378
377
  class ManyArrayManager {
379
- constructor(record) {
378
+ constructor(record, editable) {
380
379
  this.record = record;
381
380
  this.store = record[RecordStore];
382
381
  this.identifier = record[Identifier];
382
+ this.editable = editable;
383
383
  }
384
384
  _syncArray(array) {
385
- const rawValue = this.store.cache.getRelationship(this.identifier, array.key);
385
+ const method = this.editable ? 'getRelationship' : 'getRemoteRelationship';
386
+ const rawValue = this.store.cache[method](this.identifier, array.key);
386
387
  if (rawValue.meta) {
387
388
  array.meta = rawValue.meta;
388
389
  }
@@ -428,7 +429,7 @@ class ManyArrayManager {
428
429
  return this.store.request(req);
429
430
  }
430
431
  mutate(mutation) {
431
- this.cache.mutate(mutation);
432
+ this.store.cache.mutate(mutation);
432
433
  }
433
434
  }
434
435
  function getRelatedLink(resource) {
@@ -453,12 +454,10 @@ function extractCacheOptions(options) {
453
454
  const ManagedArrayMap = getOrSetGlobal('ManagedArrayMap', new Map());
454
455
  const ManagedObjectMap = getOrSetGlobal('ManagedObjectMap', new Map());
455
456
  function computeLocal(record, field, prop) {
456
- let signal = peekSignal(record, prop);
457
- if (!signal) {
458
- signal = getSignal(record, prop, false);
459
- signal.lastValue = field.options?.defaultValue ?? null;
460
- }
461
- return signal.lastValue;
457
+ const signals = withSignalStore(record);
458
+ const signal = getOrCreateInternalSignal(signals, record, prop, field.options?.defaultValue ?? null);
459
+ consumeInternalSignal(signal);
460
+ return signal.value;
462
461
  }
463
462
  function peekManagedArray(record, field) {
464
463
  const managedArrayMapForRecord = ManagedArrayMap.get(record);
@@ -602,9 +601,9 @@ class ResourceRelationship {
602
601
  return this[RecordStore].request(request);
603
602
  }
604
603
  }
605
- defineSignal(ResourceRelationship.prototype, 'data');
606
- defineSignal(ResourceRelationship.prototype, 'links');
607
- defineSignal(ResourceRelationship.prototype, 'meta');
604
+ defineSignal(ResourceRelationship.prototype, 'data', null);
605
+ defineSignal(ResourceRelationship.prototype, 'links', null);
606
+ defineSignal(ResourceRelationship.prototype, 'meta', null);
608
607
  function getHref(link) {
609
608
  if (!link) {
610
609
  return null;
@@ -644,7 +643,9 @@ function computeHasMany(store, schema, cache, record, identifier, field, path, e
644
643
  type: field.type,
645
644
  identifier,
646
645
  cache,
647
- identifiers: rawValue.data,
646
+ // we divorce the reference here because ManyArray mutates the target directly
647
+ // before sending the mutation op to the cache. We may be able to avoid this in the future
648
+ identifiers: rawValue.data?.slice(),
648
649
  key: field.name,
649
650
  meta: rawValue.meta || null,
650
651
  links: rawValue.links || null,
@@ -653,7 +654,7 @@ function computeHasMany(store, schema, cache, record, identifier, field, path, e
653
654
  // TODO: Grab the proper value
654
655
  _inverseIsAsync: false,
655
656
  // @ts-expect-error Typescript doesn't have a way for us to thread the generic backwards so it infers unknown instead of T
656
- manager: new ManyArrayManager(record),
657
+ manager: new ManyArrayManager(record, editable),
657
658
  isLoaded: true,
658
659
  allowMutation: editable
659
660
  });
@@ -668,7 +669,7 @@ function computeHasMany(store, schema, cache, record, identifier, field, path, e
668
669
  const HAS_MODEL_PACKAGE = dependencySatisfies('@ember-data-mirror/model', '*');
669
670
  const getLegacySupport = macroCondition(dependencySatisfies('@ember-data-mirror/model', '*')) ? importSync('@ember-data-mirror/model/-private').lookupLegacySupport : null;
670
671
  const IgnoredGlobalFields = new Set(['length', 'nodeType', 'then', 'setInterval', 'document', STRUCTURED]);
671
- const symbolList = [Destroy, RecordStore, Identifier, Editable, Parent, Checkout, Legacy, Signals, EmbeddedPath, EmbeddedType];
672
+ const symbolList = [Destroy, RecordStore, Identifier, Editable, Parent, Checkout, Legacy, EmbeddedPath, EmbeddedType];
672
673
  const RecordSymbols = new Set(symbolList);
673
674
  function isPathMatch(a, b) {
674
675
  return a.length === b.length && a.every((v, i) => v === b[i]);
@@ -700,8 +701,7 @@ class SchemaRecord {
700
701
  const fields = isEmbedded ? schema.fields({
701
702
  type: embeddedType
702
703
  }) : schema.fields(identifier);
703
- const signals = new Map();
704
- this[Signals] = signals;
704
+ const signals = withSignalStore(this);
705
705
  const proxy = new Proxy(this, {
706
706
  ownKeys() {
707
707
  const identityKey = identityField?.name;
@@ -773,6 +773,9 @@ class SchemaRecord {
773
773
  if (RecordSymbols.has(prop)) {
774
774
  return target[prop];
775
775
  }
776
+ if (prop === Signals) {
777
+ return signals;
778
+ }
776
779
 
777
780
  // TODO make this a symbol
778
781
  if (prop === '___notifications') {
@@ -801,7 +804,7 @@ class SchemaRecord {
801
804
  let fn = BoundFns.get('toString');
802
805
  if (!fn) {
803
806
  fn = function () {
804
- entangleSignal(signals, receiver, '@identity');
807
+ entangleSignal(signals, receiver, '@identity', null);
805
808
  return `Record<${identifier.type}:${identifier.id} (${identifier.lid})>`;
806
809
  };
807
810
  BoundFns.set(prop, fn);
@@ -812,7 +815,7 @@ class SchemaRecord {
812
815
  let fn = BoundFns.get('toHTML');
813
816
  if (!fn) {
814
817
  fn = function () {
815
- entangleSignal(signals, receiver, '@identity');
818
+ entangleSignal(signals, receiver, '@identity', null);
816
819
  return `<span>Record<${identifier.type}:${identifier.id} (${identifier.lid})></span>`;
817
820
  };
818
821
  BoundFns.set(prop, fn);
@@ -872,42 +875,40 @@ class SchemaRecord {
872
875
 
873
876
  switch (field.kind) {
874
877
  case '@id':
875
- entangleSignal(signals, receiver, '@identity');
878
+ entangleSignal(signals, receiver, '@identity', null);
876
879
  return identifier.id;
877
880
  case '@hash':
878
881
  // TODO pass actual cache value not {}
879
882
  return schema.hashFn(field)({}, field.options ?? null, field.name ?? null);
880
883
  case '@local':
881
884
  {
882
- const lastValue = computeLocal(receiver, field, prop);
883
- entangleSignal(signals, receiver, prop);
884
- return lastValue;
885
+ return computeLocal(receiver, field, prop);
885
886
  }
886
887
  case 'field':
887
- entangleSignal(signals, receiver, field.name);
888
+ entangleSignal(signals, receiver, field.name, null);
888
889
  return computeField(schema, cache, target, identifier, field, propArray, IS_EDITABLE);
889
890
  case 'attribute':
890
- entangleSignal(signals, receiver, field.name);
891
+ entangleSignal(signals, receiver, field.name, null);
891
892
  return computeAttribute(cache, identifier, prop, IS_EDITABLE);
892
893
  case 'resource':
893
- entangleSignal(signals, receiver, field.name);
894
+ entangleSignal(signals, receiver, field.name, null);
894
895
  return computeResource(store, cache, target, identifier, field, prop, IS_EDITABLE);
895
896
  case 'derived':
896
897
  return computeDerivation(schema, receiver, identifier, field, prop);
897
898
  case 'schema-array':
898
899
  case 'array':
899
- entangleSignal(signals, receiver, field.name);
900
+ entangleSignal(signals, receiver, field.name, null);
900
901
  return computeArray(store, schema, cache, target, identifier, field, propArray, Mode[Editable], Mode[Legacy]);
901
902
  case 'object':
902
- entangleSignal(signals, receiver, field.name);
903
+ entangleSignal(signals, receiver, field.name, null);
903
904
  return computeObject(schema, cache, target, identifier, field, propArray, Mode[Editable], Mode[Legacy]);
904
905
  case 'schema-object':
905
- entangleSignal(signals, receiver, field.name);
906
+ entangleSignal(signals, receiver, field.name, null);
906
907
  // run transform, then use that value as the object to manage
907
908
  return computeSchemaObject(store, cache, target, identifier, field, propArray, Mode[Legacy], Mode[Editable]);
908
909
  case 'belongsTo':
909
910
  if (field.options.linksMode) {
910
- entangleSignal(signals, receiver, field.name);
911
+ entangleSignal(signals, receiver, field.name, null);
911
912
  const rawValue = IS_EDITABLE ? cache.getRelationship(identifier, field.name) : cache.getRemoteRelationship(identifier, field.name);
912
913
 
913
914
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
@@ -930,11 +931,11 @@ class SchemaRecord {
930
931
  throw new Error(`Can only use belongsTo fields when the resource is in legacy mode`);
931
932
  }
932
933
  })(Mode[Legacy]) : {};
933
- entangleSignal(signals, receiver, field.name);
934
+ entangleSignal(signals, receiver, field.name, null);
934
935
  return getLegacySupport(receiver).getBelongsTo(field.name);
935
936
  case 'hasMany':
936
937
  if (field.options.linksMode) {
937
- entangleSignal(signals, receiver, field.name);
938
+ entangleSignal(signals, receiver, field.name, null);
938
939
  return computeHasMany(store, schema, cache, target, identifier, field, propArray, Mode[Editable], Mode[Legacy]);
939
940
  }
940
941
  if (!HAS_MODEL_PACKAGE) {
@@ -954,7 +955,7 @@ class SchemaRecord {
954
955
  throw new Error(`Can only use hasMany fields when the resource is in legacy mode`);
955
956
  }
956
957
  })(Mode[Legacy]) : {};
957
- entangleSignal(signals, receiver, field.name);
958
+ entangleSignal(signals, receiver, field.name, null);
958
959
  return getLegacySupport(receiver).getHasMany(field.name);
959
960
  default:
960
961
  throw new Error(`Field '${String(prop)}' on '${identifier.type}' has the unknown kind '${field.kind}'`);
@@ -1005,10 +1006,10 @@ class SchemaRecord {
1005
1006
  }
1006
1007
  case '@local':
1007
1008
  {
1008
- const signal = getSignal(receiver, prop, true);
1009
- if (signal.lastValue !== value) {
1010
- signal.lastValue = value;
1011
- addToTransaction(signal);
1009
+ const signal = getOrCreateInternalSignal(signals, receiver, prop, field.options?.defaultValue ?? null);
1010
+ if (signal.value !== value) {
1011
+ signal.value = value;
1012
+ notifyInternalSignal(signal);
1012
1013
  }
1013
1014
  return true;
1014
1015
  }
@@ -1040,7 +1041,7 @@ class SchemaRecord {
1040
1041
  }
1041
1042
  })(ARRAY_SIGNAL in peeked) : {};
1042
1043
  const arrSignal = peeked[ARRAY_SIGNAL];
1043
- arrSignal.shouldReset = true;
1044
+ arrSignal.isStale = true;
1044
1045
  }
1045
1046
  if (!Array.isArray(value)) {
1046
1047
  ManagedArrayMap.delete(target);
@@ -1058,7 +1059,7 @@ class SchemaRecord {
1058
1059
  }
1059
1060
  })(ARRAY_SIGNAL in peeked) : {};
1060
1061
  const arrSignal = peeked[ARRAY_SIGNAL];
1061
- arrSignal.shouldReset = true;
1062
+ arrSignal.isStale = true;
1062
1063
  }
1063
1064
  return true;
1064
1065
  }
@@ -1077,7 +1078,7 @@ class SchemaRecord {
1077
1078
  }
1078
1079
  })(ARRAY_SIGNAL in peeked) : {};
1079
1080
  const arrSignal = peeked[ARRAY_SIGNAL];
1080
- arrSignal.shouldReset = true;
1081
+ arrSignal.isStale = true;
1081
1082
  }
1082
1083
  if (!Array.isArray(value)) {
1083
1084
  ManagedArrayMap.delete(target);
@@ -1099,7 +1100,7 @@ class SchemaRecord {
1099
1100
  const peeked = peekManagedObject(self, field);
1100
1101
  if (peeked) {
1101
1102
  const objSignal = peeked[OBJECT_SIGNAL];
1102
- objSignal.shouldReset = true;
1103
+ objSignal.isStale = true;
1103
1104
  }
1104
1105
  return true;
1105
1106
  }
@@ -1111,7 +1112,7 @@ class SchemaRecord {
1111
1112
  const peeked = peekManagedObject(self, field);
1112
1113
  if (peeked) {
1113
1114
  const objSignal = peeked[OBJECT_SIGNAL];
1114
- objSignal.shouldReset = true;
1115
+ objSignal.isStale = true;
1115
1116
  }
1116
1117
  return true;
1117
1118
  }
@@ -1142,7 +1143,7 @@ class SchemaRecord {
1142
1143
  // const peeked = peekManagedObject(self, field);
1143
1144
  // if (peeked) {
1144
1145
  // const objSignal = peeked[OBJECT_SIGNAL];
1145
- // objSignal.shouldReset = true;
1146
+ // objSignal.isStale = true;
1146
1147
  // }
1147
1148
  return true;
1148
1149
  }
@@ -1217,7 +1218,7 @@ class SchemaRecord {
1217
1218
  if (identityField.name && identityField.kind === '@id') {
1218
1219
  const signal = signals.get('@identity');
1219
1220
  if (signal) {
1220
- addToTransaction(signal);
1221
+ notifyInternalSignal(signal);
1221
1222
  }
1222
1223
  }
1223
1224
  break;
@@ -1247,7 +1248,7 @@ class SchemaRecord {
1247
1248
  // console.log(`Notification for ${key} on ${identifier.type}`, self);
1248
1249
  const signal = signals.get(key);
1249
1250
  if (signal) {
1250
- addToTransaction(signal);
1251
+ notifyInternalSignal(signal);
1251
1252
  }
1252
1253
  const field = fields.get(key);
1253
1254
  if (field?.kind === 'array' || field?.kind === 'schema-array') {
@@ -1259,16 +1260,14 @@ class SchemaRecord {
1259
1260
  }
1260
1261
  })(ARRAY_SIGNAL in peeked) : {};
1261
1262
  const arrSignal = peeked[ARRAY_SIGNAL];
1262
- arrSignal.shouldReset = true;
1263
- addToTransaction(arrSignal);
1263
+ notifyInternalSignal(arrSignal);
1264
1264
  }
1265
1265
  }
1266
1266
  if (field?.kind === 'object') {
1267
1267
  const peeked = peekManagedObject(self, field);
1268
1268
  if (peeked) {
1269
1269
  const objSignal = peeked[OBJECT_SIGNAL];
1270
- objSignal.shouldReset = true;
1271
- addToTransaction(objSignal);
1270
+ notifyInternalSignal(objSignal);
1272
1271
  }
1273
1272
  }
1274
1273
  }
@@ -1290,17 +1289,14 @@ class SchemaRecord {
1290
1289
  // console.log(`Notification for ${key} on ${identifier.type}`, self);
1291
1290
  const signal = signals.get(key);
1292
1291
  if (signal) {
1293
- addToTransaction(signal);
1292
+ notifyInternalSignal(signal);
1294
1293
  }
1295
1294
  // FIXME
1296
1295
  } else if (field.kind === 'resource') ;else if (field.kind === 'hasMany') {
1297
1296
  if (field.options.linksMode) {
1298
1297
  const peeked = peekManagedArray(self, field);
1299
1298
  if (peeked) {
1300
- // const arrSignal = peeked[ARRAY_SIGNAL];
1301
- // arrSignal.shouldReset = true;
1302
- // addToTransaction(arrSignal);
1303
- peeked.notify();
1299
+ notifyInternalSignal(peeked[ARRAY_SIGNAL]);
1304
1300
  }
1305
1301
  return;
1306
1302
  }
@@ -1323,7 +1319,7 @@ class SchemaRecord {
1323
1319
  return;
1324
1320
  }
1325
1321
  if (manyArray) {
1326
- manyArray.notify();
1322
+ notifyInternalSignal(manyArray[ARRAY_SIGNAL]);
1327
1323
  macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
1328
1324
  if (!test) {
1329
1325
  throw new Error(`Expected options to exist on relationship meta`);
@@ -1337,7 +1333,7 @@ class SchemaRecord {
1337
1333
  if (field.options.async) {
1338
1334
  const signal = signals.get(key);
1339
1335
  if (signal) {
1340
- addToTransaction(signal);
1336
+ notifyInternalSignal(signal);
1341
1337
  }
1342
1338
  }
1343
1339
  }
@@ -1467,15 +1463,23 @@ function _constructor(record) {
1467
1463
  _constructor[Type] = '@constructor';
1468
1464
 
1469
1465
  /**
1470
- * Utility for constructing a ResourceSchema with the recommended fields
1471
- * for the Polaris experience.
1466
+ * Utility for constructing a ResourceSchema with the recommended
1467
+ * fields for the PolarisMode experience.
1468
+ *
1469
+ * Using this requires registering the PolarisMode derivations
1470
+ *
1471
+ * ```ts
1472
+ * import { registerDerivations } from '@warp-drive-mirror/schema-record';
1473
+ *
1474
+ * registerDerivations(schema);
1475
+ * ```
1472
1476
  *
1473
1477
  * @method withDefaults
1474
1478
  * @for @warp-drive-mirror/schema-record
1475
1479
  * @static
1476
1480
  * @public
1477
1481
  * @param schema
1478
- * @return {ResourceSchema}
1482
+ * @return {PolarisResourceSchema}
1479
1483
  */
1480
1484
  function withDefaults(schema) {
1481
1485
  schema.identity = schema.identity || DefaultIdentityField;
@@ -1530,7 +1534,12 @@ function fromIdentity(record, options, key) {
1530
1534
  fromIdentity[Type] = '@identity';
1531
1535
 
1532
1536
  /**
1533
- * Registers the default derivations for the SchemaRecord
1537
+ * Registers the default derivations for records that want
1538
+ * to use the PolarisMode defaults provided by
1539
+ *
1540
+ * ```ts
1541
+ * import { withDefaults } from '@warp-drive-mirror/schema-record';
1542
+ * ```
1534
1543
  *
1535
1544
  * @method registerDerivations
1536
1545
  * @for @warp-drive-mirror/schema-record
@@ -1550,15 +1559,15 @@ function registerDerivations(schema) {
1550
1559
  */
1551
1560
  function makeCachedDerivation(derivation) {
1552
1561
  const memoizedDerivation = (record, options, prop) => {
1553
- const signals = record[Signals];
1562
+ const signals = withSignalStore(record);
1554
1563
  let signal = signals.get(prop);
1555
1564
  if (!signal) {
1556
- signal = createCache(() => {
1565
+ signal = createMemo(record, prop, () => {
1557
1566
  return derivation(record, options, prop);
1558
1567
  }); // a total lie, for convenience of reusing the storage
1559
1568
  signals.set(prop, signal);
1560
1569
  }
1561
- return getValue(signal);
1570
+ return signal();
1562
1571
  };
1563
1572
  memoizedDerivation[Type] = derivation[Type];
1564
1573
  return memoizedDerivation;
@@ -1577,6 +1586,7 @@ class SchemaService {
1577
1586
  this._transforms = new Map();
1578
1587
  this._hashFns = new Map();
1579
1588
  this._derivations = new Map();
1589
+ this._traits = new Set();
1580
1590
  }
1581
1591
  resourceTypes() {
1582
1592
  return Array.from(this._schemas.keys());