@warp-drive/core 5.9.0-alpha.18 → 5.9.0-alpha.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.
Files changed (30) hide show
  1. package/declarations/index.d.ts +10 -1
  2. package/dist/graph/-private.js +17 -21
  3. package/dist/{index-BoY6aNE5.js → index-LI2ckwf9.js} +244 -1
  4. package/dist/index.js +1 -1
  5. package/dist/reactive.js +1 -1
  6. package/dist/store/-private.js +1 -1
  7. package/dist/types/-private.js +1 -1
  8. package/dist/unpkg/dev/graph/-private.js +17 -21
  9. package/dist/unpkg/dev/{index-BZ6PKU-a.js → index-DU_HLpg-.js} +244 -1
  10. package/dist/unpkg/dev/index.js +1 -1
  11. package/dist/unpkg/dev/reactive.js +1 -1
  12. package/dist/unpkg/dev/store/-private.js +2 -2
  13. package/dist/unpkg/dev/types/-private.js +1 -1
  14. package/dist/unpkg/dev-deprecated/graph/-private.js +17 -21
  15. package/dist/unpkg/dev-deprecated/{index-CDjd631s.js → index-BYFUHVPd.js} +244 -1
  16. package/dist/unpkg/dev-deprecated/index.js +1 -1
  17. package/dist/unpkg/dev-deprecated/reactive.js +1 -1
  18. package/dist/unpkg/dev-deprecated/store/-private.js +1 -1
  19. package/dist/unpkg/dev-deprecated/types/-private.js +1 -1
  20. package/dist/unpkg/prod/{index-BMBm3kYx.js → index-C-AhVafS.js} +146 -1
  21. package/dist/unpkg/prod/index.js +1 -1
  22. package/dist/unpkg/prod/reactive.js +1 -1
  23. package/dist/unpkg/prod/store/-private.js +2 -2
  24. package/dist/unpkg/prod/types/-private.js +1 -1
  25. package/dist/unpkg/prod-deprecated/{index-CZkDXZog.js → index-CplZl2hv.js} +146 -1
  26. package/dist/unpkg/prod-deprecated/index.js +1 -1
  27. package/dist/unpkg/prod-deprecated/reactive.js +1 -1
  28. package/dist/unpkg/prod-deprecated/store/-private.js +1 -1
  29. package/dist/unpkg/prod-deprecated/types/-private.js +1 -1
  30. package/package.json +3 -3
@@ -7738,6 +7738,9 @@ class ReactiveResource {
7738
7738
  if (prop === Destroy || prop === Checkout) {
7739
7739
  return true;
7740
7740
  }
7741
+ if (prop === identityField?.name) {
7742
+ return true;
7743
+ }
7741
7744
  return fields.has(prop);
7742
7745
  },
7743
7746
  getOwnPropertyDescriptor(target, prop) {
@@ -8488,6 +8491,90 @@ function registerDerivations(schema) {
8488
8491
  schema.registerDerivation(fromIdentity);
8489
8492
  schema.registerDerivation(_constructor);
8490
8493
  }
8494
+
8495
+ /**
8496
+ * A relationship field capable of declaring `options.as`, marking it as a
8497
+ * concrete implementation of an abstract polymorphic type.
8498
+ */
8499
+
8500
+ /**
8501
+ * A relationship field contributed to an abstract type's schema, along with
8502
+ * the name of the type that contributed it (either a concrete implementer,
8503
+ * via `options.as`, or the abstract type's own schema), for use in assertion
8504
+ * messages when two contributions disagree on shape.
8505
+ */
8506
+
8507
+ /**
8508
+ * Reduces a field to a plain object with a fixed key order, covering every
8509
+ * option any relationship-implementer field can declare, so two fields can
8510
+ * be compared for exact equality via `JSON.stringify` regardless of the key
8511
+ * order the schema author happened to write them in.
8512
+ *
8513
+ * `options.as` is included and is *not* special-cased: every contributor to
8514
+ * a given field name on an abstract type - including the abstract type's
8515
+ * own schema, if it has one - is expected to declare `as` equal to that
8516
+ * abstract type's own name. This is redundant/self-referential for the
8517
+ * abstract type's own schema, but required for consistency: relationship
8518
+ * resolution elsewhere (e.g. `inverse` name-matching) does not itself
8519
+ * validate `as`, so a field with a missing or incorrect `as` can otherwise
8520
+ * be silently pulled into a relationship it never declared it implements.
8521
+ */
8522
+ function comparableAbstractFieldShape(field) {
8523
+ const {
8524
+ kind,
8525
+ name,
8526
+ type,
8527
+ sourceKey,
8528
+ options
8529
+ } = field;
8530
+ return {
8531
+ kind,
8532
+ name,
8533
+ type,
8534
+ sourceKey,
8535
+ options: {
8536
+ as: options?.as,
8537
+ async: options?.async,
8538
+ inverse: options?.inverse,
8539
+ polymorphic: options?.polymorphic,
8540
+ linksMode: options?.linksMode,
8541
+ resetOnRemoteUpdate: options?.resetOnRemoteUpdate,
8542
+ arrayExtensions: options?.arrayExtensions
8543
+ }
8544
+ };
8545
+ }
8546
+
8547
+ /**
8548
+ * Asserts that two contributions to the same field name on an abstract
8549
+ * polymorphic type - whether from two different concrete implementers, or
8550
+ * from an implementer and the abstract type's own schema - declare an
8551
+ * identical shape: `kind`, `type`, and every option, including `as` (which
8552
+ * every contributor, the abstract type's own schema included, must declare
8553
+ * equal to `abstractType`). A mismatch here (e.g. one implementer's field
8554
+ * being a `hasMany` while another's is a `belongsTo`, the two disagreeing on
8555
+ * `inverse`/`async`/`polymorphic`, or one omitting or misdeclaring `as`)
8556
+ * cannot be resolved by picking one side arbitrarily, since that would
8557
+ * silently leave whichever side lost the mismatch pointing at a relationship
8558
+ * whose true shape differs from what its own schema declared.
8559
+ */
8560
+ function assertConsistentAbstractFieldShape(abstractType, fieldName, existing, incoming) {
8561
+ // This function exists solely to support the `assert()` call below - there
8562
+ // is no other work here with an effect in production. Since `assert()`'s
8563
+ // own babel-macro stripping only removes the call expression itself, not
8564
+ // the (comparatively expensive, involving `JSON.stringify`) computation of
8565
+ // its arguments, guard the whole body so none of it survives in production
8566
+ // builds, matching how `assertPolymorphicType`/`assertInheritedSchema` and
8567
+ // their call sites are gated in `assert-polymorphic-type.ts`.
8568
+ {
8569
+ const existingShape = comparableAbstractFieldShape(existing.field);
8570
+ const incomingShape = comparableAbstractFieldShape(incoming.field);
8571
+ (test => {
8572
+ if (!test) {
8573
+ throw new Error(`Expected the relationship '${fieldName}' on the abstract polymorphic type '${abstractType}' to be declared identically everywhere it is implemented (including 'options.as', which every contributor - the abstract type's own schema included - must declare equal to '${abstractType}'), but '${existing.source}' declares it as:\n\n${JSON.stringify(existingShape, null, 2)}\n\nwhile '${incoming.source}' declares it as:\n\n${JSON.stringify(incomingShape, null, 2)}\n\nAll concrete implementers of an abstract type - and the abstract type's own schema, if it has one - must declare an identical shape for any relationship field they share.`);
8574
+ }
8575
+ })(JSON.stringify(existingShape) === JSON.stringify(incomingShape));
8576
+ }
8577
+ }
8491
8578
  /**
8492
8579
  * Wraps a derivation in a new function with Derivation signature but that looks
8493
8580
  * up the value in the cache before recomputing.
@@ -8527,6 +8614,18 @@ class SchemaService {
8527
8614
 
8528
8615
  /** @internal */
8529
8616
 
8617
+ /**
8618
+ * Tracks, per abstract type, the relationship fields that concrete
8619
+ * implementers have contributed via `options.as`, along with the type
8620
+ * that contributed each one (for assertion messages). This is
8621
+ * independent of whatever schema (synthesized or user-registered)
8622
+ * currently occupies `_schemas` for that type, so that these fields
8623
+ * survive regardless of the order in which the abstract type's
8624
+ * implementers and its own (optional) concrete schema are registered.
8625
+ *
8626
+ * @internal
8627
+ */
8628
+
8530
8629
  /** @internal */
8531
8630
 
8532
8631
  /** @internal */
@@ -8538,6 +8637,7 @@ class SchemaService {
8538
8637
  this._derivations = new Map();
8539
8638
  this._traits = new Map();
8540
8639
  this._modes = new Map();
8640
+ this._abstractImplementerFields = new Map();
8541
8641
  this._extensions = {
8542
8642
  object: new Map(),
8543
8643
  array: new Map()
@@ -8633,6 +8733,7 @@ class SchemaService {
8633
8733
  const fields = new Map();
8634
8734
  const relationships = {};
8635
8735
  const attributes = {};
8736
+ const abstractImplementations = [];
8636
8737
  for (const field of schema.fields) {
8637
8738
  (test => {
8638
8739
  if (!test) {
@@ -8646,6 +8747,31 @@ class SchemaService {
8646
8747
  attributes[field.name] = field;
8647
8748
  } else if (field.kind === 'belongsTo' || field.kind === 'hasMany') {
8648
8749
  relationships[field.name] = field;
8750
+ if (field.options?.as) {
8751
+ abstractImplementations.push(field);
8752
+ }
8753
+ }
8754
+ }
8755
+
8756
+ // This type may already be known as an abstract polymorphic type,
8757
+ // implemented by other concrete types via `options.as`, from before it
8758
+ // ever had a schema of its own (see `_registerAbstractTypeImplementation`).
8759
+ // Carry those previously-contributed fields forward so that registering
8760
+ // a "real" schema for the type - whenever that happens to occur - never
8761
+ // erases the relationships its implementers depend on.
8762
+ const implementerFields = this._abstractImplementerFields.get(schema.type);
8763
+ if (implementerFields) {
8764
+ for (const [name, contribution] of implementerFields) {
8765
+ const ownField = fields.get(name);
8766
+ if (!ownField) {
8767
+ fields.set(name, contribution.field);
8768
+ relationships[name] = contribution.field;
8769
+ } else {
8770
+ assertConsistentAbstractFieldShape(schema.type, name, contribution, {
8771
+ field: ownField,
8772
+ source: schema.type
8773
+ });
8774
+ }
8649
8775
  }
8650
8776
  }
8651
8777
  const cacheFields = null;
@@ -8664,6 +8790,101 @@ class SchemaService {
8664
8790
  internalSchema.cacheFields = getCacheFields(internalSchema);
8665
8791
  }
8666
8792
  this._schemas.set(schema.type, internalSchema);
8793
+
8794
+ // A relationship field's `as` option marks it as a valid concrete
8795
+ // implementer of an abstract polymorphic type (e.g. `as: 'commentable'`).
8796
+ // That abstract type may never be given its own schema by the user - it
8797
+ // may exist only to be implemented by concrete types like this one - or
8798
+ // it may already have (or later receive) a schema of its own, e.g. if it
8799
+ // turns out to also be a real, directly-resolvable resource. Either way,
8800
+ // ensure it has a schema with this field present, so that it behaves
8801
+ // like any other registered resource (`hasResource`, `fields`, etc.)
8802
+ // instead of requiring special-casing wherever abstract relationship
8803
+ // types are resolved.
8804
+ for (const field of abstractImplementations) {
8805
+ this._registerAbstractTypeImplementation(field, schema.type);
8806
+ }
8807
+ }
8808
+
8809
+ /** @internal */
8810
+ _registerAbstractTypeImplementation(field, implementer) {
8811
+ const abstractType = field.options.as;
8812
+ let implementerFields = this._abstractImplementerFields.get(abstractType);
8813
+ if (!implementerFields) {
8814
+ implementerFields = new Map();
8815
+ this._abstractImplementerFields.set(abstractType, implementerFields);
8816
+ }
8817
+
8818
+ // Unlike the original approach this replaced, `as` is *not* stripped here:
8819
+ // the field as synthesized onto the abstract type's own schema keeps
8820
+ // `options.as === abstractType`, redundant/self-referential as that is.
8821
+ // This keeps every contributor - implementers and the abstract type's
8822
+ // own schema alike - declaring the same thing, which is what
8823
+ // `assertConsistentAbstractFieldShape` checks, and it is also what lets
8824
+ // `assertPolymorphicType` (which reads a field's declared `as` off
8825
+ // whatever `schema.fields()` serves for its type) correctly permit the
8826
+ // abstract type itself to be pushed directly into this relationship.
8827
+ const abstractField = {
8828
+ ...field
8829
+ };
8830
+ const contribution = {
8831
+ field: abstractField,
8832
+ source: implementer
8833
+ };
8834
+
8835
+ // all concrete implementations of an abstract type are required to
8836
+ // share the same shape for the field that implements it, so the first
8837
+ // one registered is as good a canonical source as any - but a later,
8838
+ // differently-shaped one is almost certainly a mistake rather than an
8839
+ // intentional override, so we catch it rather than silently ignoring it.
8840
+ const existingImplementer = implementerFields.get(field.name);
8841
+ if (existingImplementer) {
8842
+ {
8843
+ assertConsistentAbstractFieldShape(abstractType, field.name, existingImplementer, contribution);
8844
+ }
8845
+ return;
8846
+ }
8847
+ implementerFields.set(field.name, contribution);
8848
+ let abstractSchema = this._schemas.get(abstractType);
8849
+ if (!abstractSchema) {
8850
+ abstractSchema = {
8851
+ original: {
8852
+ legacy: true,
8853
+ identity: {
8854
+ kind: '@id',
8855
+ name: 'id'
8856
+ },
8857
+ type: abstractType,
8858
+ fields: []
8859
+ },
8860
+ finalized: true,
8861
+ fields: new Map(),
8862
+ cacheFields: new Map(),
8863
+ relationships: {},
8864
+ attributes: {},
8865
+ traits: new Set()
8866
+ };
8867
+ this._schemas.set(abstractType, abstractSchema);
8868
+ }
8869
+ const existingAbstractField = abstractSchema.fields.get(field.name);
8870
+ if (existingAbstractField) {
8871
+ {
8872
+ assertConsistentAbstractFieldShape(abstractType, field.name, {
8873
+ field: existingAbstractField,
8874
+ source: abstractType
8875
+ }, contribution);
8876
+ }
8877
+ return;
8878
+ }
8879
+ abstractSchema.fields.set(field.name, abstractField);
8880
+ abstractSchema.relationships[field.name] = abstractField;
8881
+
8882
+ // If the schema is mid-finalization (traits pending), finalizeResource
8883
+ // will recompute cacheFields from the current `fields` map anyway; avoid
8884
+ // doing so prematurely from a partially-resolved set of fields.
8885
+ if (abstractSchema.finalized) {
8886
+ abstractSchema.cacheFields = getCacheFields(abstractSchema);
8887
+ }
8667
8888
  }
8668
8889
 
8669
8890
  /**
@@ -9429,7 +9650,29 @@ globalThis.setWarpDriveIsMaybeMirage = setIsMaybeMirage;
9429
9650
  */
9430
9651
  function useRecommendedStore(options, StoreKlass = Store) {
9431
9652
  return class AppStore extends StoreKlass {
9432
- requestManager = new RequestManager().use([...(options.handlers ?? []), Fetch]).useCache(CacheHandler);
9653
+ constructor(createArgs) {
9654
+ super(createArgs);
9655
+ // installed via defineProperty (rather than a class field/accessor) so that
9656
+ // this lazy override of the inherited `requestManager` field does not
9657
+ // conflict with the documented pattern of assigning it directly on
9658
+ // consumer-authored Store subclasses. The setter preserves the ability
9659
+ // to replace `requestManager` outright after construction.
9660
+ let requestManager;
9661
+ Object.defineProperty(this, 'requestManager', {
9662
+ configurable: true,
9663
+ enumerable: true,
9664
+ get: () => {
9665
+ if (!requestManager) {
9666
+ const handlers = typeof options.handlers === 'function' ? options.handlers(this) : options.handlers ?? [];
9667
+ requestManager = new RequestManager().use([...handlers, Fetch]).useCache(CacheHandler);
9668
+ }
9669
+ return requestManager;
9670
+ },
9671
+ set: value => {
9672
+ requestManager = value;
9673
+ }
9674
+ });
9675
+ }
9433
9676
  lifetimes = options.policy ?? new DefaultCachePolicy({
9434
9677
  apiCacheHardExpires: 15 * 60 * 1000,
9435
9678
  // 15 minutes
@@ -1,4 +1,4 @@
1
- export { C as CacheHandler, F as Fetch, y as RequestManager, S as Store, r as cacheKeyFor, r as recordIdentifierFor, D as setIdentifierForgetMethod, A as setIdentifierGenerationMethod, E as setIdentifierResetMethod, B as setIdentifierUpdateMethod, G as setKeyInfoForResource, s as storeFor, z as useRecommendedStore } from "./index-BZ6PKU-a.js";
1
+ export { C as CacheHandler, F as Fetch, y as RequestManager, S as Store, r as cacheKeyFor, r as recordIdentifierFor, D as setIdentifierForgetMethod, A as setIdentifierGenerationMethod, E as setIdentifierResetMethod, B as setIdentifierUpdateMethod, G as setKeyInfoForResource, s as storeFor, z as useRecommendedStore } from "./index-DU_HLpg-.js";
2
2
  import "./-private-sql1_mdx.js";
3
3
  import "./-leaked-BohmerbH.js";
4
4
  import './store.js';
@@ -1,3 +1,3 @@
1
- export { q as SchemaService, o as checkout, x as commit, u as fromIdentity, p as instantiateRecord, v as registerDerivations, t as teardownRecord, w as withDefaults } from "./index-BZ6PKU-a.js";
1
+ export { q as SchemaService, o as checkout, x as commit, u as fromIdentity, p as instantiateRecord, v as registerDerivations, t as teardownRecord, w as withDefaults } from "./index-DU_HLpg-.js";
2
2
  export { a as Checkout } from "./-private-sql1_mdx.js";
3
3
  export { k as createRequestSubscription, m as getPromiseState, l as getRequestState } from "./-leaked-BohmerbH.js";
@@ -1,6 +1,6 @@
1
1
  import '../types/request.js';
2
- import { i as isResourceKey, c as coerceId } from "../index-BZ6PKU-a.js";
3
- export { C as CacheHandler, R as RecordArrayManager, S as Store, j as StoreMap, _ as _clearCaches, n as _deprecatingNormalize, g as assertPrivateCapabilities, b as assertPrivateStore, k as createLegacyManyArray, e as ensureStringId, f as fastPush, d as isPrivateStore, a as isRequestKey, l as log, m as logGroup, r as recordIdentifierFor, h as setRecordIdentifier, s as storeFor } from "../index-BZ6PKU-a.js";
2
+ import { i as isResourceKey, c as coerceId } from "../index-DU_HLpg-.js";
3
+ export { C as CacheHandler, R as RecordArrayManager, S as Store, j as StoreMap, _ as _clearCaches, n as _deprecatingNormalize, g as assertPrivateCapabilities, b as assertPrivateStore, k as createLegacyManyArray, e as ensureStringId, f as fastPush, d as isPrivateStore, a as isRequestKey, l as log, m as logGroup, r as recordIdentifierFor, h as setRecordIdentifier, s as storeFor } from "../index-DU_HLpg-.js";
4
4
  function isNonEmptyString(str) {
5
5
  return Boolean(str && typeof str === 'string');
6
6
  }
@@ -1,5 +1,5 @@
1
1
  const name = "@warp-drive/core";
2
- const version = "5.9.0-alpha.18";
2
+ const version = "5.9.0-alpha.19";
3
3
 
4
4
  // in testing mode, we utilize globals to ensure only one copy exists of
5
5
  // these maps, due to bugs in ember-auto-import
@@ -757,7 +757,7 @@ let assertInheritedSchema;
757
757
  errors.set('type', ` <---- should be '${definition.inverseType}'`);
758
758
  }
759
759
  if (definition.inverseKind !== meta.kind) {
760
- errors.set('type', ` <---- should be '${definition.inverseKind}'`);
760
+ errors.set('kind', ` <---- should be '${definition.inverseKind}'`);
761
761
  }
762
762
  if (definition.inverseIsAsync !== meta.options.async) {
763
763
  errors.set('async', ` <---- should be ${definition.inverseIsAsync}`);
@@ -768,7 +768,12 @@ let assertInheritedSchema;
768
768
  if (definition.key !== meta.options.inverse) {
769
769
  errors.set('inverse', ` <---- should be '${definition.key}'`);
770
770
  }
771
- if (definition.type !== meta.options.as) {
771
+ // `as` is only meaningful when `definition` itself is polymorphic (i.e. it
772
+ // points at an abstract type that `meta` is expected to implement). When
773
+ // `definition` isn't polymorphic, there's no abstract type for `meta` to
774
+ // declare `as` against, so `meta.options.as` is correctly absent - that's
775
+ // not an error to report.
776
+ if (definition.isPolymorphic && definition.type !== meta.options.as) {
772
777
  errors.set('as', ` <---- should be '${definition.type}'`);
773
778
  }
774
779
  return errors;
@@ -807,18 +812,6 @@ let assertInheritedSchema;
807
812
 
808
813
  `;
809
814
  }
810
- function metaFrom(definition) {
811
- return {
812
- name: definition.key,
813
- type: definition.type,
814
- kind: definition.kind,
815
- options: {
816
- async: definition.isAsync,
817
- polymorphic: definition.isPolymorphic,
818
- inverse: definition.inverseKey
819
- }
820
- };
821
- }
822
815
  function inverseMetaFrom(definition) {
823
816
  return {
824
817
  name: definition.inverseKey,
@@ -861,15 +854,18 @@ let assertInheritedSchema;
861
854
  });
862
855
  }
863
856
  assertInheritedSchema = function assertInheritedSchema(definition, type) {
864
- const meta1 = metaFrom(definition);
857
+ // `definition` is the only source of truth we have here - there is no
858
+ // second, independently-declared schema available to diff it against.
859
+ // That means the only thing we can actually verify is internal: does
860
+ // `definition`'s own resolved inverse agree that this relationship is
861
+ // polymorphic? Every other field of `meta2` below (name/type/kind/async/
862
+ // inverse) is derived directly from `definition` itself, so comparing
863
+ // them back to `definition` can never disagree - only the polymorphic
864
+ // flag is independently meaningful, because `definitionWithPolymorphic`
865
+ // asserts what it *should* be rather than mirroring what it already is.
865
866
  const meta2 = inverseMetaFrom(definition);
866
- const errors1 = validateSchema(inverseDefinition(definition), meta1);
867
867
  const errors2 = validateSchema(definitionWithPolymorphic(definition), meta2);
868
- if (errors2.size === 0 && errors1.size > 0) {
869
- throw new Error(`The schema for the relationship '${type}.${definition.key}' is not configured to satisfy '${definition.inverseType}' and thus cannot utilize the '${definition.inverseType}.${definition.key}' relationship to connect with '${definition.type}.${definition.inverseKey}'\n\nIf using this relationship in a polymorphic manner is desired, the relationships schema definition for '${type}' should include:${printSchema(meta1, errors1)}`);
870
- } else if (errors1.size > 0) {
871
- throw new Error(`The schema for the relationship '${type}.${definition.key}' is not configured to satisfy '${definition.inverseType}' and thus cannot utilize the '${definition.inverseType}.${definition.key}' relationship to connect with '${definition.type}.${definition.inverseKey}'\n\nIf using this relationship in a polymorphic manner is desired, the relationships schema definition for '${type}' should include:${printSchema(meta1, errors1)} and the relationships schema definition for '${definition.type}' should include:${printSchema(meta2, errors2)}`);
872
- } else if (errors2.size > 0) {
868
+ if (errors2.size > 0) {
873
869
  throw new Error(`The schema for the relationship '${type}.${definition.key}' satisfies '${definition.inverseType}' but cannot utilize the '${definition.inverseType}.${definition.key}' relationship to connect with '${definition.type}.${definition.inverseKey}' because that relationship is not polymorphic.\n\nIf using this relationship in a polymorphic manner is desired, the relationships schema definition for '${definition.type}' should include:${printSchema(meta2, errors2)}`);
874
870
  }
875
871
  };