@warp-drive-mirror/core 5.9.0-alpha.17 → 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 (57) hide show
  1. package/declarations/graph/-private/-state.d.ts +1 -0
  2. package/declarations/index.d.ts +10 -1
  3. package/declarations/reactive/-private/default-mode.d.ts +22 -9
  4. package/declarations/reactive/-private/kind/belongs-to-field.d.ts +3 -0
  5. package/declarations/types/runtime.d.ts +16 -0
  6. package/declarations/types/schema/fields.d.ts +40 -12
  7. package/dist/graph/-private.js +67 -52
  8. package/dist/{index-CFwnjldF.js → index-LI2ckwf9.js} +332 -10
  9. package/dist/index.js +1 -1
  10. package/dist/reactive.js +1 -1
  11. package/dist/store/-private.js +1 -1
  12. package/dist/types/-private.js +1 -1
  13. package/dist/types/runtime.js +20 -1
  14. package/dist/unpkg/dev/graph/-private.js +67 -52
  15. package/dist/unpkg/dev/{index-BZRBBa34.js → index-DU_HLpg-.js} +332 -10
  16. package/dist/unpkg/dev/index.js +1 -1
  17. package/dist/unpkg/dev/reactive.js +1 -1
  18. package/dist/unpkg/dev/store/-private.js +2 -2
  19. package/dist/unpkg/dev/types/-private.js +1 -1
  20. package/dist/unpkg/dev/types/runtime.js +20 -1
  21. package/dist/unpkg/dev-deprecated/graph/-private.js +67 -52
  22. package/dist/unpkg/dev-deprecated/{index-Bu52tQ9I.js → index-BYFUHVPd.js} +332 -10
  23. package/dist/unpkg/dev-deprecated/index.js +1 -1
  24. package/dist/unpkg/dev-deprecated/reactive.js +1 -1
  25. package/dist/unpkg/dev-deprecated/store/-private.js +1 -1
  26. package/dist/unpkg/dev-deprecated/types/-private.js +1 -1
  27. package/dist/unpkg/dev-deprecated/types/runtime.js +20 -1
  28. package/dist/unpkg/prod/{-leaked-BHLmJyaw.js → -leaked-DgjQ5X55.js} +2 -2
  29. package/dist/unpkg/prod/configure.js +1 -1
  30. package/dist/unpkg/prod/graph/-private.js +17 -25
  31. package/dist/unpkg/prod/{schema-BdC3DirM.js → index-C-AhVafS.js} +2383 -73
  32. package/dist/unpkg/prod/index.js +5 -483
  33. package/dist/unpkg/prod/{promise-cache-DIT8Ypjq.js → promise-cache-DUblkX-U.js} +1 -1
  34. package/dist/unpkg/prod/reactive.js +3 -29
  35. package/dist/unpkg/prod/{request-BrJSCG6r.js → request-eb7Zm-oS.js} +1 -1
  36. package/dist/unpkg/prod/request.js +2 -2
  37. package/dist/unpkg/prod/signals/-leaked.js +1 -1
  38. package/dist/unpkg/prod/store/-private.js +2 -3
  39. package/dist/unpkg/prod/types/-private.js +1 -1
  40. package/dist/unpkg/prod/types/runtime.js +20 -1
  41. package/dist/unpkg/prod-deprecated/{-leaked-D_5Yrs5c.js → -leaked-BNVttukY.js} +1 -1
  42. package/dist/unpkg/prod-deprecated/configure.js +1 -1
  43. package/dist/unpkg/prod-deprecated/graph/-private.js +17 -25
  44. package/dist/unpkg/prod-deprecated/{schema-h0nuUTCj.js → index-CplZl2hv.js} +1035 -7
  45. package/dist/unpkg/prod-deprecated/index.js +5 -483
  46. package/dist/unpkg/prod-deprecated/{promise-cache-DIT8Ypjq.js → promise-cache-DUblkX-U.js} +1 -1
  47. package/dist/unpkg/prod-deprecated/reactive.js +2 -3
  48. package/dist/unpkg/prod-deprecated/{request-BrJSCG6r.js → request-eb7Zm-oS.js} +1 -1
  49. package/dist/unpkg/prod-deprecated/request.js +2 -2
  50. package/dist/unpkg/prod-deprecated/signals/-leaked.js +1 -1
  51. package/dist/unpkg/prod-deprecated/store/-private.js +1 -2
  52. package/dist/unpkg/prod-deprecated/types/-private.js +1 -1
  53. package/dist/unpkg/prod-deprecated/types/runtime.js +20 -1
  54. package/package.json +3 -3
  55. package/dist/unpkg/prod/handler-BJogFbfS.js +0 -1619
  56. package/dist/unpkg/prod-deprecated/handler-kGR8zguj.js +0 -334
  57. package/dist/unpkg/prod-deprecated/hooks-wJdveHPi.js +0 -26
@@ -1,5 +1,6 @@
1
1
  export interface RelationshipState {
2
2
  hasReceivedData: boolean;
3
+ hasReceivedRemoteData: boolean;
3
4
  isEmpty: boolean;
4
5
  isStale: boolean;
5
6
  hasFailedLoadAttempt: boolean;
@@ -39,8 +39,17 @@ export interface StoreSetupOptions<T extends Cache = Cache> {
39
39
  * The request handlers to use. {@link Fetch} will automatically
40
40
  * be added to the end of the handler chain and {@link CacheHandler}
41
41
  * will automatically be added as the cache handler.
42
+ *
43
+ * May also be given as a function that receives the {@link Store} instance
44
+ * and returns the handlers to use. This is useful when a handler needs
45
+ * access to the store (e.g. to look up its owner for injections) or when
46
+ * the set of handlers should be decided lazily (e.g. based on a feature
47
+ * flag service that may not be ready until after the store is constructed).
48
+ *
49
+ * The function is invoked lazily, the first time the store's `requestManager`
50
+ * is accessed, and only once per store instance.
42
51
  */
43
- handlers?: Handler[];
52
+ handlers?: Handler[] | ((store: Store) => Handler[]);
44
53
  /**
45
54
  * Schemas describing the structure of your resource data.
46
55
  *
@@ -1,8 +1,6 @@
1
- import type { SignalStore } from "../../signals/-private.js";
2
1
  import type { Store } from "../../store/-private.js";
3
2
  import type { ResourceKey } from "../../types.js";
4
3
  import type { FieldSchema, HashField, IdentityField, SchemaArrayField, SchemaObjectField } from "../../types/schema/fields.js";
5
- import type { ReactiveResource } from "./record.js";
6
4
  export type PathLike = string | symbol | Array<string | symbol>;
7
5
  export type ModeName = "polaris" | "legacy";
8
6
  export interface ModeInfo {
@@ -11,10 +9,32 @@ export interface ModeInfo {
11
9
  editable: boolean;
12
10
  }
13
11
  export interface BaseContext {
12
+ /**
13
+ * The {@link Store} instance that owns
14
+ * the reactive context we are within.
15
+ */
14
16
  store: Store;
17
+ /**
18
+ * The {@link ResourceKey} of the resource that
19
+ * the reactive context we are within is for.
20
+ */
15
21
  resourceKey: ResourceKey;
22
+ /**
23
+ * The name of the {@link ModeName | mode} that the
24
+ * reactive context we are within is operating in.
25
+ */
16
26
  modeName: ModeName;
27
+ /**
28
+ * Whether we are in legacy {@link ModeName | mode}
29
+ * or not.
30
+ */
17
31
  legacy: boolean;
32
+ /**
33
+ * Whether the ReactiveResource instance we are operating
34
+ * in is currently editable or not. This should be true if the
35
+ * record is currently checked out for editing or is in legacy mode
36
+ * and false if the record is currently in a read-only state.
37
+ */
18
38
  editable: boolean;
19
39
  }
20
40
  export interface ResourceContext extends BaseContext {
@@ -27,13 +47,6 @@ export interface ObjectContext extends BaseContext {
27
47
  field: SchemaObjectField | SchemaArrayField;
28
48
  value: string;
29
49
  }
30
- export interface KindContext<T extends FieldSchema | IdentityField | HashField> extends BaseContext {
31
- path: string[];
32
- field: T;
33
- value: unknown;
34
- record: ReactiveResource;
35
- signals: SignalStore;
36
- }
37
50
  export interface KindImpl<T extends FieldSchema | IdentityField | HashField> {
38
51
  /**
39
52
  * A function which produces the value for the field when invoked.
@@ -1,4 +1,7 @@
1
+ import type { ResourceKey } from "../../../types.js";
1
2
  import type { LegacyBelongsToField } from "../../../types/schema/fields.js";
2
3
  import type { KindContext } from "../default-mode.js";
4
+ import type { ReactiveResource } from "../record.js";
3
5
  export declare function getBelongsToField(context: KindContext<LegacyBelongsToField>): unknown;
4
6
  export declare function setBelongsToField(context: KindContext<LegacyBelongsToField>): boolean;
7
+ export declare function getResourceKey(record: ReactiveResource | null): ResourceKey | null;
@@ -1,6 +1,7 @@
1
1
  import type { LOG_CONFIG } from "@warp-drive-mirror/build-config/-private/utils/logging";
2
2
  declare const RuntimeConfig: {
3
3
  debug: Partial<LOG_CONFIG>;
4
+ mirage?: boolean;
4
5
  };
5
6
  export declare function getRuntimeConfig(): typeof RuntimeConfig;
6
7
  /**
@@ -11,4 +12,19 @@ export declare function getRuntimeConfig(): typeof RuntimeConfig;
11
12
  *
12
13
  */
13
14
  export declare function setLogging(config: Partial<LOG_CONFIG>): void;
15
+ /**
16
+ * Explicitly declares whether requests may be served by Mirage (or another
17
+ * Pretender-based fetch mock) instead of a native `fetch` implementation.
18
+ *
19
+ * The `Fetch` request handler otherwise infers this via a heuristic (the
20
+ * presence of `window.server.pretender`, or `window.fetch` appearing to be
21
+ * patched), which can be wrong in either direction: some Mirage setups don't
22
+ * expose `window.server`, while some unrelated tools (APM agents, browser
23
+ * extensions) also patch `fetch`. Call this to override that heuristic:
24
+ * `true` to force Mirage-compatible behavior on, `false` to force it off.
25
+ *
26
+ * globalThis.setWarpDriveIsMaybeMirage(true);
27
+ *
28
+ */
29
+ export declare function setIsMaybeMirage(value: boolean): void;
14
30
  export {};
@@ -1315,9 +1315,16 @@ export interface LegacyBelongsToField {
1315
1315
  * adapters and serializers.
1316
1316
  *
1317
1317
  * When true, none of the legacy support will be utilized. Sync relationships
1318
- * will be expected to already have all their data. When reloading a sync relationship
1319
- * you would be expected to have a `related link` available from a prior relationship
1320
- * payload e.g.
1318
+ * (`async: false`, currently the only supported combination) must satisfy
1319
+ * one of the following whenever pushed to the cache:
1320
+ *
1321
+ * - a `links.related` link is present, **or**
1322
+ * - the relationship is fully linked: `data` is `null`, or `data` points
1323
+ * to a resource present in the document's `included` array
1324
+ *
1325
+ * A related link lets you omit the related resource from `included`; a
1326
+ * `data` key that is missing entirely, or explicitly `undefined`, is never
1327
+ * valid unless a `links.related` link is present.
1321
1328
  *
1322
1329
  * ```ts
1323
1330
  * {
@@ -1468,9 +1475,16 @@ export interface LinksModeBelongsToField {
1468
1475
  * MUST be true for PolarisMode + LinksMode
1469
1476
  *
1470
1477
  * When true, none of the legacy support will be utilized. Sync relationships
1471
- * will be expected to already have all their data. When reloading a sync relationship
1472
- * you would be expected to have a `related link` available from a prior relationship
1473
- * payload e.g.
1478
+ * (`async: false`, currently the only supported combination) must satisfy
1479
+ * one of the following whenever pushed to the cache:
1480
+ *
1481
+ * - a `links.related` link is present, **or**
1482
+ * - the relationship is fully linked: `data` is `null`, or `data` points
1483
+ * to a resource present in the document's `included` array
1484
+ *
1485
+ * A related link lets you omit the related resource from `included`; a
1486
+ * `data` key that is missing entirely, or explicitly `undefined`, is never
1487
+ * valid unless a `links.related` link is present.
1474
1488
  *
1475
1489
  * ```ts
1476
1490
  * {
@@ -1640,9 +1654,16 @@ export interface LegacyHasManyField {
1640
1654
  * from @warp-drive-mirror/legacy/model and the LegacyNetworkMiddleware for adapters and serializers.
1641
1655
  *
1642
1656
  * When true, none of the legacy support will be utilized. Sync relationships
1643
- * will be expected to already have all their data. When reloading a sync relationship
1644
- * you would be expected to have a `related link` available from a prior relationship
1645
- * payload e.g.
1657
+ * (`async: false`, currently the only supported combination) must satisfy
1658
+ * one of the following whenever pushed to the cache:
1659
+ *
1660
+ * - a `links.related` link is present, **or**
1661
+ * - the relationship is fully linked: `data` is `[]`, or every resource
1662
+ * identifier in `data` is present in the document's `included` array
1663
+ *
1664
+ * A related link lets you omit the related resources from `included`; a
1665
+ * `data` key that is missing entirely, or explicitly `undefined`, is never
1666
+ * valid unless a `links.related` link is present.
1646
1667
  *
1647
1668
  * ```ts
1648
1669
  * {
@@ -1818,9 +1839,16 @@ export interface LinksModeHasManyField {
1818
1839
  * MUST be true for PolarisMode + LinksMode
1819
1840
  *
1820
1841
  * When true, none of the legacy support will be utilized. Sync relationships
1821
- * will be expected to already have all their data. When reloading a sync relationship
1822
- * you would be expected to have a `related link` available from a prior relationship
1823
- * payload e.g.
1842
+ * (`async: false`, currently the only supported combination) must satisfy
1843
+ * one of the following whenever pushed to the cache:
1844
+ *
1845
+ * - a `links.related` link is present, **or**
1846
+ * - the relationship is fully linked: `data` is `[]`, or every resource
1847
+ * identifier in `data` is present in the document's `included` array
1848
+ *
1849
+ * A related link lets you omit the related resources from `included`; a
1850
+ * `data` key that is missing entirely, or explicitly `undefined`, is never
1851
+ * valid unless a `links.related` link is present.
1824
1852
  *
1825
1853
  * ```ts
1826
1854
  * {
@@ -67,9 +67,14 @@ function assertValidRelationshipPayload(graph, op) {
67
67
  kind
68
68
  } = definition;
69
69
  if (payload.links) {
70
- warn(`You pushed a record of type '${type}' with a relationship '${field}' configured as 'async: false'. You've included a link but no primary data, this may be an error in your payload. WarpDrive will treat this relationship as known-to-be-empty.`, isAsync || payload.data === null || !!payload.data || state.hasReceivedData, {
71
- id: 'ds.store.push-link-for-sync-relationship'
72
- });
70
+ // once JSON_API_CACHE_VALIDATION_ERRORS is active, this warning is instead
71
+ // the responsibility of the new document validator (see validateResourceRelationships
72
+ // in warp-drive-packages/json-api/src/-private/validator/1.1/7.2_resource-objects.ts)
73
+ if (macroCondition(!getGlobalConfig().WarpDriveMirror.features.JSON_API_CACHE_VALIDATION_ERRORS)) {
74
+ warn(`You pushed a record of type '${type}' with a relationship '${field}' configured as 'async: false'. You've included a link but no primary data, this may be an error in your payload. WarpDrive will treat this relationship as known-to-be-empty.`, isAsync || payload.data === null || !!payload.data || state.hasReceivedData, {
75
+ id: 'ds.store.push-link-for-sync-relationship'
76
+ });
77
+ }
73
78
  } else if (payload.data) {
74
79
  if (kind === 'belongsTo') {
75
80
  macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
@@ -748,7 +753,7 @@ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
748
753
  errors.set('type', ` <---- should be '${definition.inverseType}'`);
749
754
  }
750
755
  if (definition.inverseKind !== meta.kind) {
751
- errors.set('type', ` <---- should be '${definition.inverseKind}'`);
756
+ errors.set('kind', ` <---- should be '${definition.inverseKind}'`);
752
757
  }
753
758
  if (definition.inverseIsAsync !== meta.options.async) {
754
759
  errors.set('async', ` <---- should be ${definition.inverseIsAsync}`);
@@ -759,7 +764,12 @@ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
759
764
  if (definition.key !== meta.options.inverse) {
760
765
  errors.set('inverse', ` <---- should be '${definition.key}'`);
761
766
  }
762
- if (definition.type !== meta.options.as) {
767
+ // `as` is only meaningful when `definition` itself is polymorphic (i.e. it
768
+ // points at an abstract type that `meta` is expected to implement). When
769
+ // `definition` isn't polymorphic, there's no abstract type for `meta` to
770
+ // declare `as` against, so `meta.options.as` is correctly absent - that's
771
+ // not an error to report.
772
+ if (definition.isPolymorphic && definition.type !== meta.options.as) {
763
773
  errors.set('as', ` <---- should be '${definition.type}'`);
764
774
  }
765
775
  return errors;
@@ -798,18 +808,6 @@ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
798
808
 
799
809
  `;
800
810
  }
801
- function metaFrom(definition) {
802
- return {
803
- name: definition.key,
804
- type: definition.type,
805
- kind: definition.kind,
806
- options: {
807
- async: definition.isAsync,
808
- polymorphic: definition.isPolymorphic,
809
- inverse: definition.inverseKey
810
- }
811
- };
812
- }
813
811
  function inverseMetaFrom(definition) {
814
812
  return {
815
813
  name: definition.inverseKey,
@@ -852,15 +850,18 @@ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
852
850
  });
853
851
  }
854
852
  assertInheritedSchema = function assertInheritedSchema(definition, type) {
855
- const meta1 = metaFrom(definition);
853
+ // `definition` is the only source of truth we have here - there is no
854
+ // second, independently-declared schema available to diff it against.
855
+ // That means the only thing we can actually verify is internal: does
856
+ // `definition`'s own resolved inverse agree that this relationship is
857
+ // polymorphic? Every other field of `meta2` below (name/type/kind/async/
858
+ // inverse) is derived directly from `definition` itself, so comparing
859
+ // them back to `definition` can never disagree - only the polymorphic
860
+ // flag is independently meaningful, because `definitionWithPolymorphic`
861
+ // asserts what it *should* be rather than mirroring what it already is.
856
862
  const meta2 = inverseMetaFrom(definition);
857
- const errors1 = validateSchema(inverseDefinition(definition), meta1);
858
863
  const errors2 = validateSchema(definitionWithPolymorphic(definition), meta2);
859
- if (errors2.size === 0 && errors1.size > 0) {
860
- 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)}`);
861
- } else if (errors1.size > 0) {
862
- 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)}`);
863
- } else if (errors2.size > 0) {
864
+ if (errors2.size > 0) {
864
865
  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)}`);
865
866
  }
866
867
  };
@@ -1414,8 +1415,10 @@ function replaceRelatedRecord(graph, op, isRemote = false) {
1414
1415
  if (op.value === existingState) {
1415
1416
  // if we were empty before but now know we are empty this needs to be true
1416
1417
  state.hasReceivedData = true;
1418
+
1417
1419
  // if this is a remote update we still sync
1418
1420
  if (isRemote) {
1421
+ state.hasReceivedRemoteData = true;
1419
1422
  const {
1420
1423
  localState
1421
1424
  } = relationship;
@@ -1455,6 +1458,15 @@ function replaceRelatedRecord(graph, op, isRemote = false) {
1455
1458
  // update value to the new value
1456
1459
  relationship[prop] = op.value;
1457
1460
  state.hasReceivedData = true;
1461
+ /**
1462
+ * Local mutations never give us additional information about
1463
+ * whether the relationship has received *remote* data. This is
1464
+ * what `getRemoteRelationship` relies on to avoid reflecting
1465
+ * local-only edits.
1466
+ */
1467
+ if (isRemote) {
1468
+ state.hasReceivedRemoteData = true;
1469
+ }
1458
1470
  state.isEmpty = op.value === null;
1459
1471
  state.isStale = false;
1460
1472
  state.hasFailedLoadAttempt = false;
@@ -2133,8 +2145,30 @@ function rollbackRelationship(graph, key, field, relationship) {
2133
2145
  }
2134
2146
  }
2135
2147
  function createState() {
2148
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
2149
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_GRAPH)) {
2150
+ if (getGlobalConfig().WarpDriveMirror.debug.LOG_GRAPH || globalThis.getWarpDriveRuntimeConfig().debug.LOG_GRAPH) {
2151
+ let hasReceivedData = false;
2152
+ return {
2153
+ get hasReceivedData() {
2154
+ return hasReceivedData;
2155
+ },
2156
+ set hasReceivedData(value) {
2157
+ hasReceivedData = value;
2158
+ },
2159
+ hasReceivedRemoteData: false,
2160
+ isEmpty: true,
2161
+ isStale: false,
2162
+ hasFailedLoadAttempt: false,
2163
+ shouldForceReload: false,
2164
+ hasDematerializedInverse: false
2165
+ };
2166
+ }
2167
+ }
2168
+ }
2136
2169
  return {
2137
2170
  hasReceivedData: false,
2171
+ hasReceivedRemoteData: false,
2138
2172
  isEmpty: true,
2139
2173
  isStale: false,
2140
2174
  hasFailedLoadAttempt: false,
@@ -2239,13 +2273,10 @@ function legacyGetResourceRelationshipData(source, getRemoteState) {
2239
2273
  source.accessed = true;
2240
2274
  let data;
2241
2275
  const payload = {};
2242
- if (getRemoteState && source.remoteState) {
2243
- data = source.remoteState;
2244
- } else if (!getRemoteState && source.localState) {
2245
- data = source.localState;
2246
- }
2247
- if ((getRemoteState && source.remoteState === null || source.localState === null) && source.state.hasReceivedData) {
2248
- data = null;
2276
+ if (getRemoteState && (source.remoteState || source.state.hasReceivedRemoteData)) {
2277
+ data = source.remoteState ?? null;
2278
+ } else if (!getRemoteState && (source.localState || source.state.hasReceivedData)) {
2279
+ data = source.localState ?? null;
2249
2280
  }
2250
2281
  if (source.links) {
2251
2282
  payload.links = source.links;
@@ -2459,7 +2490,6 @@ function updateRelationshipOperation(graph, op) {
2459
2490
  })(isHasMany(relationship) || isBelongsTo(relationship)) : {};
2460
2491
  const {
2461
2492
  definition,
2462
- state,
2463
2493
  identifier
2464
2494
  } = relationship;
2465
2495
  const {
@@ -2499,23 +2529,6 @@ function updateRelationshipOperation(graph, op) {
2499
2529
  value: payload.data ? graph.store.cacheKeyManager.upgradeIdentifier(payload.data) : null
2500
2530
  }, true);
2501
2531
  }
2502
- } else if (definition.isAsync === false && !state.hasReceivedData) {
2503
- hasRelationshipDataProperty = true;
2504
- if (isCollection) {
2505
- graph.update({
2506
- op: 'replaceRelatedRecords',
2507
- record: identifier,
2508
- field: op.field,
2509
- value: []
2510
- }, true);
2511
- } else {
2512
- graph.update({
2513
- op: 'replaceRelatedRecord',
2514
- record: identifier,
2515
- field: op.field,
2516
- value: null
2517
- }, true);
2518
- }
2519
2532
  }
2520
2533
  if (payload.links) {
2521
2534
  const originalLinks = relationship.links;
@@ -2525,9 +2538,10 @@ function updateRelationshipOperation(graph, op) {
2525
2538
  const currentLink = originalLinks && originalLinks.related ? _normalizeLink(originalLinks.related) : null;
2526
2539
  const currentLinkHref = currentLink ? currentLink.href : null;
2527
2540
  if (relatedLink && relatedLink.href && relatedLink.href !== currentLinkHref) {
2528
- warn(`You pushed a record of type '${identifier.type}' with a relationship '${definition.key}' configured as 'async: false'. You've included a link but no primary data, this may be an error in your payload. WarpDrive will treat this relationship as known-to-be-empty.`, definition.isAsync || state.hasReceivedData, {
2529
- id: 'ds.store.push-link-for-sync-relationship'
2530
- });
2541
+ // Note: the equivalent warning for this case is issued eagerly (and with a more
2542
+ // complete condition, since it also accounts for `payload.data` being present) by
2543
+ // `assertValidRelationshipPayload` in `-utils.ts`, which always runs immediately
2544
+ // before this operation in DEBUG builds. Warning here too would double-warn.
2531
2545
  macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2532
2546
  if (!test) {
2533
2547
  throw new Error(`You have pushed a record of type '${identifier.type}' with '${definition.key}' as a link, but the value of that link is not a string.`);
@@ -2558,6 +2572,7 @@ function updateRelationshipOperation(graph, op) {
2558
2572
  // we don't need to notify here as the update op we pushed in above will notify once
2559
2573
  // membership is in the correct state.
2560
2574
  relationship.state.hasReceivedData = true;
2575
+ relationship.state.hasReceivedRemoteData = true;
2561
2576
  relationship.state.isStale = false;
2562
2577
  relationship.state.hasDematerializedInverse = false;
2563
2578
  relationship.state.isEmpty = relationshipIsEmpty;