@warp-drive-mirror/core 5.9.0-alpha.17 → 5.9.0-alpha.18

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 (56) hide show
  1. package/declarations/graph/-private/-state.d.ts +1 -0
  2. package/declarations/reactive/-private/default-mode.d.ts +22 -9
  3. package/declarations/reactive/-private/kind/belongs-to-field.d.ts +3 -0
  4. package/declarations/types/runtime.d.ts +16 -0
  5. package/declarations/types/schema/fields.d.ts +40 -12
  6. package/dist/graph/-private.js +50 -31
  7. package/dist/{index-CFwnjldF.js → index-BoY6aNE5.js} +88 -9
  8. package/dist/index.js +1 -1
  9. package/dist/reactive.js +1 -1
  10. package/dist/store/-private.js +1 -1
  11. package/dist/types/-private.js +1 -1
  12. package/dist/types/runtime.js +20 -1
  13. package/dist/unpkg/dev/graph/-private.js +50 -31
  14. package/dist/unpkg/dev/{index-BZRBBa34.js → index-BZ6PKU-a.js} +88 -9
  15. package/dist/unpkg/dev/index.js +1 -1
  16. package/dist/unpkg/dev/reactive.js +1 -1
  17. package/dist/unpkg/dev/store/-private.js +2 -2
  18. package/dist/unpkg/dev/types/-private.js +1 -1
  19. package/dist/unpkg/dev/types/runtime.js +20 -1
  20. package/dist/unpkg/dev-deprecated/graph/-private.js +50 -31
  21. package/dist/unpkg/dev-deprecated/{index-Bu52tQ9I.js → index-CDjd631s.js} +88 -9
  22. package/dist/unpkg/dev-deprecated/index.js +1 -1
  23. package/dist/unpkg/dev-deprecated/reactive.js +1 -1
  24. package/dist/unpkg/dev-deprecated/store/-private.js +1 -1
  25. package/dist/unpkg/dev-deprecated/types/-private.js +1 -1
  26. package/dist/unpkg/dev-deprecated/types/runtime.js +20 -1
  27. package/dist/unpkg/prod/{-leaked-BHLmJyaw.js → -leaked-DgjQ5X55.js} +2 -2
  28. package/dist/unpkg/prod/configure.js +1 -1
  29. package/dist/unpkg/prod/graph/-private.js +17 -25
  30. package/dist/unpkg/prod/{schema-BdC3DirM.js → index-BMBm3kYx.js} +2256 -91
  31. package/dist/unpkg/prod/index.js +5 -483
  32. package/dist/unpkg/prod/{promise-cache-DIT8Ypjq.js → promise-cache-DUblkX-U.js} +1 -1
  33. package/dist/unpkg/prod/reactive.js +3 -29
  34. package/dist/unpkg/prod/{request-BrJSCG6r.js → request-eb7Zm-oS.js} +1 -1
  35. package/dist/unpkg/prod/request.js +2 -2
  36. package/dist/unpkg/prod/signals/-leaked.js +1 -1
  37. package/dist/unpkg/prod/store/-private.js +2 -3
  38. package/dist/unpkg/prod/types/-private.js +1 -1
  39. package/dist/unpkg/prod/types/runtime.js +20 -1
  40. package/dist/unpkg/prod-deprecated/{-leaked-D_5Yrs5c.js → -leaked-BNVttukY.js} +1 -1
  41. package/dist/unpkg/prod-deprecated/configure.js +1 -1
  42. package/dist/unpkg/prod-deprecated/graph/-private.js +17 -25
  43. package/dist/unpkg/prod-deprecated/{schema-h0nuUTCj.js → index-CZkDXZog.js} +890 -7
  44. package/dist/unpkg/prod-deprecated/index.js +5 -483
  45. package/dist/unpkg/prod-deprecated/{promise-cache-DIT8Ypjq.js → promise-cache-DUblkX-U.js} +1 -1
  46. package/dist/unpkg/prod-deprecated/reactive.js +2 -3
  47. package/dist/unpkg/prod-deprecated/{request-BrJSCG6r.js → request-eb7Zm-oS.js} +1 -1
  48. package/dist/unpkg/prod-deprecated/request.js +2 -2
  49. package/dist/unpkg/prod-deprecated/signals/-leaked.js +1 -1
  50. package/dist/unpkg/prod-deprecated/store/-private.js +1 -2
  51. package/dist/unpkg/prod-deprecated/types/-private.js +1 -1
  52. package/dist/unpkg/prod-deprecated/types/runtime.js +20 -1
  53. package/package.json +3 -3
  54. package/dist/unpkg/prod/handler-BJogFbfS.js +0 -1619
  55. package/dist/unpkg/prod-deprecated/handler-kGR8zguj.js +0 -334
  56. 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;
@@ -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 => {
@@ -1414,8 +1419,10 @@ function replaceRelatedRecord(graph, op, isRemote = false) {
1414
1419
  if (op.value === existingState) {
1415
1420
  // if we were empty before but now know we are empty this needs to be true
1416
1421
  state.hasReceivedData = true;
1422
+
1417
1423
  // if this is a remote update we still sync
1418
1424
  if (isRemote) {
1425
+ state.hasReceivedRemoteData = true;
1419
1426
  const {
1420
1427
  localState
1421
1428
  } = relationship;
@@ -1455,6 +1462,15 @@ function replaceRelatedRecord(graph, op, isRemote = false) {
1455
1462
  // update value to the new value
1456
1463
  relationship[prop] = op.value;
1457
1464
  state.hasReceivedData = true;
1465
+ /**
1466
+ * Local mutations never give us additional information about
1467
+ * whether the relationship has received *remote* data. This is
1468
+ * what `getRemoteRelationship` relies on to avoid reflecting
1469
+ * local-only edits.
1470
+ */
1471
+ if (isRemote) {
1472
+ state.hasReceivedRemoteData = true;
1473
+ }
1458
1474
  state.isEmpty = op.value === null;
1459
1475
  state.isStale = false;
1460
1476
  state.hasFailedLoadAttempt = false;
@@ -2133,8 +2149,30 @@ function rollbackRelationship(graph, key, field, relationship) {
2133
2149
  }
2134
2150
  }
2135
2151
  function createState() {
2152
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
2153
+ if (macroCondition(getGlobalConfig().WarpDriveMirror.activeLogging.LOG_GRAPH)) {
2154
+ if (getGlobalConfig().WarpDriveMirror.debug.LOG_GRAPH || globalThis.getWarpDriveRuntimeConfig().debug.LOG_GRAPH) {
2155
+ let hasReceivedData = false;
2156
+ return {
2157
+ get hasReceivedData() {
2158
+ return hasReceivedData;
2159
+ },
2160
+ set hasReceivedData(value) {
2161
+ hasReceivedData = value;
2162
+ },
2163
+ hasReceivedRemoteData: false,
2164
+ isEmpty: true,
2165
+ isStale: false,
2166
+ hasFailedLoadAttempt: false,
2167
+ shouldForceReload: false,
2168
+ hasDematerializedInverse: false
2169
+ };
2170
+ }
2171
+ }
2172
+ }
2136
2173
  return {
2137
2174
  hasReceivedData: false,
2175
+ hasReceivedRemoteData: false,
2138
2176
  isEmpty: true,
2139
2177
  isStale: false,
2140
2178
  hasFailedLoadAttempt: false,
@@ -2239,13 +2277,10 @@ function legacyGetResourceRelationshipData(source, getRemoteState) {
2239
2277
  source.accessed = true;
2240
2278
  let data;
2241
2279
  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;
2280
+ if (getRemoteState && (source.remoteState || source.state.hasReceivedRemoteData)) {
2281
+ data = source.remoteState ?? null;
2282
+ } else if (!getRemoteState && (source.localState || source.state.hasReceivedData)) {
2283
+ data = source.localState ?? null;
2249
2284
  }
2250
2285
  if (source.links) {
2251
2286
  payload.links = source.links;
@@ -2459,7 +2494,6 @@ function updateRelationshipOperation(graph, op) {
2459
2494
  })(isHasMany(relationship) || isBelongsTo(relationship)) : {};
2460
2495
  const {
2461
2496
  definition,
2462
- state,
2463
2497
  identifier
2464
2498
  } = relationship;
2465
2499
  const {
@@ -2499,23 +2533,6 @@ function updateRelationshipOperation(graph, op) {
2499
2533
  value: payload.data ? graph.store.cacheKeyManager.upgradeIdentifier(payload.data) : null
2500
2534
  }, true);
2501
2535
  }
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
2536
  }
2520
2537
  if (payload.links) {
2521
2538
  const originalLinks = relationship.links;
@@ -2525,9 +2542,10 @@ function updateRelationshipOperation(graph, op) {
2525
2542
  const currentLink = originalLinks && originalLinks.related ? _normalizeLink(originalLinks.related) : null;
2526
2543
  const currentLinkHref = currentLink ? currentLink.href : null;
2527
2544
  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
- });
2545
+ // Note: the equivalent warning for this case is issued eagerly (and with a more
2546
+ // complete condition, since it also accounts for `payload.data` being present) by
2547
+ // `assertValidRelationshipPayload` in `-utils.ts`, which always runs immediately
2548
+ // before this operation in DEBUG builds. Warning here too would double-warn.
2531
2549
  macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
2532
2550
  if (!test) {
2533
2551
  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 +2576,7 @@ function updateRelationshipOperation(graph, op) {
2558
2576
  // we don't need to notify here as the update op we pushed in above will notify once
2559
2577
  // membership is in the correct state.
2560
2578
  relationship.state.hasReceivedData = true;
2579
+ relationship.state.hasReceivedRemoteData = true;
2561
2580
  relationship.state.isStale = false;
2562
2581
  relationship.state.hasDematerializedInverse = false;
2563
2582
  relationship.state.isEmpty = relationshipIsEmpty;
@@ -4,13 +4,13 @@ import { EnableHydration, SkipCache, STRUCTURED } from './types/request.js';
4
4
  import { macroCondition, getGlobalConfig, dependencySatisfies, importSync } from '@embroider/macros';
5
5
  import { D as Destroy, a as Context, S as SOURCE, C as Checkout, b as Commit } from "./symbols-3C1OkYtZ.js";
6
6
  import { isResourceSchema } from './types/schema/fields.js';
7
+ import { setLogging, getRuntimeConfig, setIsMaybeMirage } from './types/runtime.js';
7
8
  import { a as cloneResponseProperties, I as IS_CACHE_HANDLER, b as assertValidRequest, e as executeNextHandler, d as getRequestResult, u as upgradePromise, s as setPromiseResult, f as clearRequestResult } from "./future-CPfkKOvf.js";
8
9
  import { getOrSetGlobal, peekTransient, setTransient, peekUniversalTransient, setUniversalTransient } from './types/-private.js';
9
10
  import { D as DefaultCachePolicy } from "./default-cache-policy-D7_u4YRH.js";
10
11
  import { withBrand } from './request.js';
11
12
  import { CACHE_OWNER, DEBUG_STALE_CACHE_OWNER, DEBUG_KEY_TYPE, DEBUG_CLIENT_ORIGINATED } from './types/identifier.js';
12
13
  import { dasherize } from './utils/string.js';
13
- import { setLogging, getRuntimeConfig } from './types/runtime.js';
14
14
  import { RecordStore, Type } from './types/symbols.js';
15
15
  function urlFromLink(link) {
16
16
  if (typeof link === 'string') return link;
@@ -7281,11 +7281,32 @@ function getBelongsToField(context) {
7281
7281
  }
7282
7282
  function setBelongsToField(context) {
7283
7283
  const {
7284
+ field,
7285
+ resourceKey,
7284
7286
  store
7285
7287
  } = context;
7286
7288
  const {
7287
- schema
7289
+ schema,
7290
+ cache
7288
7291
  } = store;
7292
+
7293
+ /**
7294
+ * We currently only allow editing sync belongsTo relationships
7295
+ * in PolarisMode
7296
+ */
7297
+ const isValidPolarisConfig = field.options.linksMode && !field.options.async;
7298
+ if (!context.legacy && isValidPolarisConfig) {
7299
+ const name = context.path.at(-1);
7300
+ cache.mutate({
7301
+ op: 'replaceRelatedRecord',
7302
+ record: resourceKey,
7303
+ field: name,
7304
+ value: getResourceKey(context.value)
7305
+ },
7306
+ // @ts-expect-error
7307
+ true);
7308
+ return true;
7309
+ }
7289
7310
  macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
7290
7311
  if (!test) {
7291
7312
  throw new Error(`Can only mutate belongsTo fields when the resource is in legacy mode`);
@@ -7294,6 +7315,12 @@ function setBelongsToField(context) {
7294
7315
  schema._kind('@legacy', 'belongsTo').set(store, context.record, context.resourceKey, context.field, context.value);
7295
7316
  return true;
7296
7317
  }
7318
+ function getResourceKey(record) {
7319
+ if (!record) {
7320
+ return null;
7321
+ }
7322
+ return recordIdentifierFor(record);
7323
+ }
7297
7324
  function getCollectionField(context) {
7298
7325
  entangleSignal(context.signals, context.record, context.path.at(-1), null);
7299
7326
  macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG) ? (test => {
@@ -8029,6 +8056,17 @@ function setSchemaObjectField(context) {
8029
8056
  // }
8030
8057
  return true;
8031
8058
  }
8059
+
8060
+ /**
8061
+ * A ReactiveResource context object passed to
8062
+ * the get/set functions of a field kind implementation.
8063
+ *
8064
+ * This provides all the necessary information for
8065
+ * the field kind to perform its operations.
8066
+ *
8067
+ * @internal
8068
+ */
8069
+
8032
8070
  const DefaultMode = {
8033
8071
  '@hash': {
8034
8072
  get: getHashField,
@@ -8822,6 +8860,14 @@ const TypeField = {
8822
8860
  key: 'type'
8823
8861
  }
8824
8862
  };
8863
+ const IdentifierField = {
8864
+ type: '@identity',
8865
+ name: '$key',
8866
+ kind: 'derived',
8867
+ options: {
8868
+ key: '^'
8869
+ }
8870
+ };
8825
8871
  const DefaultIdentityField = {
8826
8872
  name: 'id',
8827
8873
  kind: '@id'
@@ -9010,6 +9056,7 @@ function withDefaults(schema) {
9010
9056
  // we add TypeField to the beginning so that it will
9011
9057
  // appear right next to the identity field
9012
9058
  schema.fields.unshift(TypeField);
9059
+ schema.fields.unshift(IdentifierField);
9013
9060
  schema.fields.push(ConstructorField);
9014
9061
  return schema;
9015
9062
  }
@@ -9556,9 +9603,33 @@ function cloneResponse(response, overrides) {
9556
9603
  const props = cloneResponseProperties(response);
9557
9604
  return new Response(response.body, Object.assign(props, overrides));
9558
9605
  }
9606
+ function withDateHeader(response) {
9607
+ const headers = new Headers(response.headers);
9608
+ headers.set('date', new Date().toUTCString());
9609
+ return cloneResponse(response, {
9610
+ headers
9611
+ });
9612
+ }
9613
+
9614
+ // Detects Mirage (or another Pretender-based fetch mock), whose `Response`
9615
+ // may (unlike a spec-compliant `fetch()`, whose Response headers are always
9616
+ // immutable) allow header mutation and may not support streaming reads.
9617
+ //
9618
+ // This is inherently a heuristic - many legitimate tools (APM/monitoring
9619
+ // agents, browser extensions, polyfills) also patch `window.fetch`, and can
9620
+ // produce false positives. `setWarpDriveIsMaybeMirage(value)` lets an app
9621
+ // declare the answer explicitly (`true` or `false`), overriding the
9622
+ // heuristic in either direction; leave it unset to keep the default
9623
+ // detection. See https://github.com/warp-drive-data/warp-drive/issues/10535
9559
9624
  let IS_MAYBE_MIRAGE = () => false;
9560
9625
  if (macroCondition(getGlobalConfig().WarpDriveMirror.env.DEBUG)) {
9561
- IS_MAYBE_MIRAGE = () => Boolean(typeof window !== 'undefined' && (window.server?.pretender || window.fetch.toString().replace(/\s+/g, '') !== 'function fetch() { [native code] }'.replace(/\s+/g, '')));
9626
+ IS_MAYBE_MIRAGE = () => {
9627
+ const override = getRuntimeConfig().mirage;
9628
+ if (override !== undefined) {
9629
+ return override;
9630
+ }
9631
+ return Boolean(typeof window !== 'undefined' && (window.server?.pretender || window.fetch.toString().replace(/\s+/g, '') !== 'function fetch() { [native code] }'.replace(/\s+/g, '')));
9632
+ };
9562
9633
  }
9563
9634
  const MUTATION_OPS = new Set(['updateRecord', 'createRecord', 'deleteRecord']);
9564
9635
  const ERROR_STATUS_CODE_FOR = new Map([[400, 'Bad Request'], [401, 'Unauthorized'], [402, 'Payment Required'], [403, 'Forbidden'], [404, 'Not Found'], [405, 'Method Not Allowed'], [406, 'Not Acceptable'], [407, 'Proxy Authentication Required'], [408, 'Request Timeout'], [409, 'Conflict'], [410, 'Gone'], [411, 'Length Required'], [412, 'Precondition Failed'], [413, 'Payload Too Large'], [414, 'URI Too Long'], [415, 'Unsupported Media Type'], [416, 'Range Not Satisfiable'], [417, 'Expectation Failed'], [419, 'Page Expired'], [420, 'Enhance Your Calm'], [421, 'Misdirected Request'], [422, 'Unprocessable Entity'], [423, 'Locked'], [424, 'Failed Dependency'], [425, 'Too Early'], [426, 'Upgrade Required'], [428, 'Precondition Required'], [429, 'Too Many Requests'], [430, 'Request Header Fields Too Large'], [431, 'Request Header Fields Too Large'], [450, 'Blocked By Windows Parental Controls'], [451, 'Unavailable For Legal Reasons'], [500, 'Internal Server Error'], [501, 'Not Implemented'], [502, 'Bad Gateway'], [503, 'Service Unavailable'], [504, 'Gateway Timeout'], [505, 'HTTP Version Not Supported'], [506, 'Variant Also Negotiates'], [507, 'Insufficient Storage'], [508, 'Loop Detected'], [509, 'Bandwidth Limit Exceeded'], [510, 'Not Extended'], [511, 'Network Authentication Required']]);
@@ -9610,13 +9681,15 @@ const Fetch = {
9610
9681
  const isMutationOp = Boolean(op && MUTATION_OPS.has(op));
9611
9682
  if (!isError && !isMutationOp && response.status !== 204 && !response.headers.has('date')) {
9612
9683
  if (IS_MAYBE_MIRAGE()) {
9613
- response.headers.set('date', new Date().toUTCString());
9684
+ try {
9685
+ response.headers.set('date', new Date().toUTCString());
9686
+ } catch {
9687
+ // IS_MAYBE_MIRAGE() was a false positive, or the mock's headers
9688
+ // are (correctly) immutable - fall back to cloning.
9689
+ response = withDateHeader(response);
9690
+ }
9614
9691
  } else {
9615
- const headers = new Headers(response.headers);
9616
- headers.set('date', new Date().toUTCString());
9617
- response = cloneResponse(response, {
9618
- headers
9619
- });
9692
+ response = withDateHeader(response);
9620
9693
  }
9621
9694
  }
9622
9695
  context.setResponse(response);
@@ -9920,6 +9993,12 @@ if (macroCondition(getGlobalConfig().WarpDriveMirror.env.TESTING)) {
9920
9993
  globalThis.getWarpDriveRuntimeConfig = getRuntimeConfig;
9921
9994
  }
9922
9995
 
9996
+ // Mirage (or another Pretender-based fetch mock) may be used outside of a
9997
+ // `TESTING` build (e.g. `ember serve` in dev mode), so this override is
9998
+ // exposed unconditionally rather than gated to the `TESTING` env.
9999
+ // @ts-expect-error adding to globalThis
10000
+ globalThis.setWarpDriveIsMaybeMirage = setIsMaybeMirage;
10001
+
9923
10002
  /**
9924
10003
  * Options for setting up a Store instance with `useRecommendedStore`.
9925
10004
  */
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- export { C as CacheHandler, F as Fetch, z as RequestManager, S as Store, r as cacheKeyFor, r as recordIdentifierFor, E as setIdentifierForgetMethod, B as setIdentifierGenerationMethod, G as setIdentifierResetMethod, D as setIdentifierUpdateMethod, H as setKeyInfoForResource, s as storeFor, A as useRecommendedStore } from "./index-CFwnjldF.js";
1
+ export { C as CacheHandler, F as Fetch, z as RequestManager, S as Store, r as cacheKeyFor, r as recordIdentifierFor, E as setIdentifierForgetMethod, B as setIdentifierGenerationMethod, G as setIdentifierResetMethod, D as setIdentifierUpdateMethod, H as setKeyInfoForResource, s as storeFor, A as useRecommendedStore } from "./index-BoY6aNE5.js";
2
2
  import "./symbols-3C1OkYtZ.js";
3
3
  import "./configure-BMxnD2RJ.js";
4
4
  import '@embroider/macros';
package/dist/reactive.js CHANGED
@@ -1,4 +1,4 @@
1
- export { u as SchemaService, p as checkout, y as commit, v as fromIdentity, q as instantiateRecord, x as registerDerivations, t as teardownRecord, w as withDefaults } from "./index-CFwnjldF.js";
1
+ export { u as SchemaService, p as checkout, y as commit, v as fromIdentity, q as instantiateRecord, x as registerDerivations, t as teardownRecord, w as withDefaults } from "./index-BoY6aNE5.js";
2
2
  export { C as Checkout } from "./symbols-3C1OkYtZ.js";
3
3
  export { h as createRequestSubscription, j as getPromiseState, i as getRequestState } from "./configure-BMxnD2RJ.js";
4
4
  import '@embroider/macros';
@@ -1 +1 @@
1
- export { C as CacheHandler, R as RecordArrayManager, S as Store, k as StoreMap, _ as _clearCaches, n as _deprecatingNormalize, h as assertPrivateCapabilities, d as assertPrivateStore, b as coerceId, c as constructResource, l as createLegacyManyArray, e as ensureStringId, f as fastPush, g as isPrivateStore, a as isRequestKey, i as isResourceKey, m as log, o as logGroup, r as recordIdentifierFor, j as setRecordIdentifier, s as storeFor } from "../index-CFwnjldF.js";
1
+ export { C as CacheHandler, R as RecordArrayManager, S as Store, k as StoreMap, _ as _clearCaches, n as _deprecatingNormalize, h as assertPrivateCapabilities, d as assertPrivateStore, b as coerceId, c as constructResource, l as createLegacyManyArray, e as ensureStringId, f as fastPush, g as isPrivateStore, a as isRequestKey, i as isResourceKey, m as log, o as logGroup, r as recordIdentifierFor, j as setRecordIdentifier, s as storeFor } from "../index-BoY6aNE5.js";
@@ -1,6 +1,6 @@
1
1
  import { macroCondition, getGlobalConfig } from '@embroider/macros';
2
2
  const name = "@warp-drive-mirror/core";
3
- const version = "5.9.0-alpha.17";
3
+ const version = "5.9.0-alpha.18";
4
4
 
5
5
  // in testing mode, we utilize globals to ensure only one copy exists of
6
6
  // these maps, due to bugs in ember-auto-import
@@ -31,4 +31,23 @@ function setLogging(config) {
31
31
  Object.assign(RuntimeConfig.debug, config);
32
32
  storage?.setItem('WarpDriveRuntimeConfig', JSON.stringify(RuntimeConfig));
33
33
  }
34
- export { getRuntimeConfig, setLogging };
34
+
35
+ /**
36
+ * Explicitly declares whether requests may be served by Mirage (or another
37
+ * Pretender-based fetch mock) instead of a native `fetch` implementation.
38
+ *
39
+ * The `Fetch` request handler otherwise infers this via a heuristic (the
40
+ * presence of `window.server.pretender`, or `window.fetch` appearing to be
41
+ * patched), which can be wrong in either direction: some Mirage setups don't
42
+ * expose `window.server`, while some unrelated tools (APM agents, browser
43
+ * extensions) also patch `fetch`. Call this to override that heuristic:
44
+ * `true` to force Mirage-compatible behavior on, `false` to force it off.
45
+ *
46
+ * globalThis.setWarpDriveIsMaybeMirage(true);
47
+ *
48
+ */
49
+ function setIsMaybeMirage(value) {
50
+ RuntimeConfig.mirage = value;
51
+ storage?.setItem('WarpDriveRuntimeConfig', JSON.stringify(RuntimeConfig));
52
+ }
53
+ export { getRuntimeConfig, setIsMaybeMirage, setLogging };