houdini 1.0.0-next.3 → 1.0.0-next.5

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 (89) hide show
  1. package/build/cmd-cjs/index.js +1254 -1062
  2. package/build/cmd-esm/index.js +1254 -1062
  3. package/build/codegen/generators/artifacts/selection.d.ts +3 -3
  4. package/build/codegen/generators/artifacts/utils.d.ts +0 -1
  5. package/build/codegen-cjs/index.js +847 -717
  6. package/build/codegen-esm/index.js +847 -717
  7. package/build/lib/config.d.ts +10 -90
  8. package/build/lib/deepMerge.d.ts +1 -0
  9. package/build/lib/graphql.d.ts +5 -2
  10. package/build/lib/index.d.ts +2 -0
  11. package/build/lib/pipeline.d.ts +1 -1
  12. package/build/lib/plugin.d.ts +2 -0
  13. package/build/lib/types.d.ts +250 -2
  14. package/build/lib-cjs/index.js +351 -162
  15. package/build/lib-esm/index.js +349 -162
  16. package/build/runtime/cache/cache.d.ts +23 -6
  17. package/build/runtime/cache/staleManager.d.ts +30 -0
  18. package/build/runtime/cache/subscription.d.ts +2 -1
  19. package/build/runtime/client/documentStore.d.ts +1 -1
  20. package/build/runtime/client/plugins/fetchParams.d.ts +1 -1
  21. package/build/runtime/client/plugins/subscription.d.ts +1 -2
  22. package/build/runtime/lib/config.d.ts +4 -0
  23. package/build/runtime/lib/index.d.ts +1 -0
  24. package/build/runtime/lib/key.d.ts +6 -0
  25. package/build/runtime/lib/types.d.ts +4 -5
  26. package/build/runtime/public/cache.d.ts +8 -1
  27. package/build/runtime/public/record.d.ts +11 -1
  28. package/build/runtime/public/tests/test.d.ts +9 -10
  29. package/build/runtime-cjs/cache/cache.d.ts +23 -6
  30. package/build/runtime-cjs/cache/cache.js +95 -21
  31. package/build/runtime-cjs/cache/gc.js +9 -0
  32. package/build/runtime-cjs/cache/lists.js +3 -3
  33. package/build/runtime-cjs/cache/staleManager.d.ts +30 -0
  34. package/build/runtime-cjs/cache/staleManager.js +95 -0
  35. package/build/runtime-cjs/cache/subscription.d.ts +2 -1
  36. package/build/runtime-cjs/cache/subscription.js +6 -3
  37. package/build/runtime-cjs/client/documentStore.d.ts +1 -1
  38. package/build/runtime-cjs/client/documentStore.js +1 -0
  39. package/build/runtime-cjs/client/plugins/cache.js +5 -3
  40. package/build/runtime-cjs/client/plugins/fetch.js +1 -0
  41. package/build/runtime-cjs/client/plugins/fetchParams.d.ts +1 -1
  42. package/build/runtime-cjs/client/plugins/fetchParams.js +6 -1
  43. package/build/runtime-cjs/client/plugins/query.js +1 -1
  44. package/build/runtime-cjs/client/plugins/subscription.d.ts +1 -2
  45. package/build/runtime-cjs/client/plugins/subscription.js +2 -0
  46. package/build/runtime-cjs/lib/config.d.ts +4 -0
  47. package/build/runtime-cjs/lib/index.d.ts +1 -0
  48. package/build/runtime-cjs/lib/index.js +1 -0
  49. package/build/runtime-cjs/lib/key.d.ts +6 -0
  50. package/build/runtime-cjs/lib/key.js +41 -0
  51. package/build/runtime-cjs/lib/types.d.ts +4 -5
  52. package/build/runtime-cjs/public/cache.d.ts +8 -1
  53. package/build/runtime-cjs/public/cache.js +3 -0
  54. package/build/runtime-cjs/public/record.d.ts +11 -1
  55. package/build/runtime-cjs/public/record.js +8 -1
  56. package/build/runtime-cjs/public/tests/test.d.ts +9 -10
  57. package/build/runtime-esm/cache/cache.d.ts +23 -6
  58. package/build/runtime-esm/cache/cache.js +96 -22
  59. package/build/runtime-esm/cache/gc.js +9 -0
  60. package/build/runtime-esm/cache/lists.js +3 -3
  61. package/build/runtime-esm/cache/staleManager.d.ts +30 -0
  62. package/build/runtime-esm/cache/staleManager.js +71 -0
  63. package/build/runtime-esm/cache/subscription.d.ts +2 -1
  64. package/build/runtime-esm/cache/subscription.js +6 -3
  65. package/build/runtime-esm/client/documentStore.d.ts +1 -1
  66. package/build/runtime-esm/client/documentStore.js +1 -0
  67. package/build/runtime-esm/client/plugins/cache.js +5 -3
  68. package/build/runtime-esm/client/plugins/fetch.js +1 -0
  69. package/build/runtime-esm/client/plugins/fetchParams.d.ts +1 -1
  70. package/build/runtime-esm/client/plugins/fetchParams.js +6 -1
  71. package/build/runtime-esm/client/plugins/query.js +1 -1
  72. package/build/runtime-esm/client/plugins/subscription.d.ts +1 -2
  73. package/build/runtime-esm/client/plugins/subscription.js +2 -0
  74. package/build/runtime-esm/lib/config.d.ts +4 -0
  75. package/build/runtime-esm/lib/index.d.ts +1 -0
  76. package/build/runtime-esm/lib/index.js +1 -0
  77. package/build/runtime-esm/lib/key.d.ts +6 -0
  78. package/build/runtime-esm/lib/key.js +17 -0
  79. package/build/runtime-esm/lib/types.d.ts +4 -5
  80. package/build/runtime-esm/public/cache.d.ts +8 -1
  81. package/build/runtime-esm/public/cache.js +3 -0
  82. package/build/runtime-esm/public/record.d.ts +11 -1
  83. package/build/runtime-esm/public/record.js +8 -1
  84. package/build/runtime-esm/public/tests/test.d.ts +9 -10
  85. package/build/test-cjs/index.js +930 -788
  86. package/build/test-esm/index.js +930 -788
  87. package/build/vite-cjs/index.js +1232 -1039
  88. package/build/vite-esm/index.js +1232 -1039
  89. package/package.json +3 -2
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var key_exports = {};
20
+ __export(key_exports, {
21
+ computeKey: () => computeKey
22
+ });
23
+ module.exports = __toCommonJS(key_exports);
24
+ const computeKey = ({ field, args }) => {
25
+ const keys = Object.keys(args ?? {});
26
+ keys.sort();
27
+ return args && keys.length > 0 ? `${field}(${keys.map((key) => `${key}: ${stringifyObjectWithNoQuotesOnKeys(args[key])}`).join(", ")})` : field;
28
+ };
29
+ const stringifyObjectWithNoQuotesOnKeys = (obj_from_json) => {
30
+ if (Array.isArray(obj_from_json)) {
31
+ return `[${obj_from_json.map((obj) => `${stringifyObjectWithNoQuotesOnKeys(obj)}`).join(", ")}]`;
32
+ }
33
+ if (typeof obj_from_json !== "object" || obj_from_json instanceof Date || obj_from_json === null) {
34
+ return JSON.stringify(obj_from_json).replace(/"([^"]+)":/g, "$1: ");
35
+ }
36
+ return `{${Object.keys(obj_from_json).map((key) => `${key}: ${stringifyObjectWithNoQuotesOnKeys(obj_from_json[key])}`).join(", ")}}`;
37
+ };
38
+ // Annotate the CommonJS export names for ESM import in node:
39
+ 0 && (module.exports = {
40
+ computeKey
41
+ });
@@ -71,7 +71,6 @@ export type BaseCompiledDocument = {
71
71
  rootType: string;
72
72
  input?: InputObject;
73
73
  refetch?: {
74
- update: RefetchUpdateMode;
75
74
  path: string[];
76
75
  method: 'cursor' | 'offset';
77
76
  pageSize: number;
@@ -79,9 +78,9 @@ export type BaseCompiledDocument = {
79
78
  embedded: boolean;
80
79
  targetType: string;
81
80
  paginated: boolean;
82
- direction?: 'forward' | 'backwards';
81
+ direction: 'forward' | 'backward' | 'both';
83
82
  };
84
- pluginsData?: Record<string, any>;
83
+ plugin_data?: Record<string, any>;
85
84
  };
86
85
  export type HoudiniFetchContext = {
87
86
  variables: () => {};
@@ -135,7 +134,7 @@ export type SubscriptionSelection = {
135
134
  connection: boolean;
136
135
  type: string;
137
136
  };
138
- update?: RefetchUpdateMode;
137
+ updates?: string[];
139
138
  filters?: {
140
139
  [key: string]: {
141
140
  kind: 'Boolean' | 'String' | 'Float' | 'Int' | 'Variable';
@@ -165,7 +164,6 @@ export type SubscriptionSpec = {
165
164
  export type FetchQueryResult<_Data> = {
166
165
  result: RequestPayload<_Data | null>;
167
166
  source: DataSource | null;
168
- partial: boolean;
169
167
  };
170
168
  export type QueryResult<_Data = GraphQLObject, _Input = Record<string, any>> = {
171
169
  data: _Data | null;
@@ -174,6 +172,7 @@ export type QueryResult<_Data = GraphQLObject, _Input = Record<string, any>> = {
174
172
  }[] | null;
175
173
  fetching: boolean;
176
174
  partial: boolean;
175
+ stale: boolean;
177
176
  source: DataSource | null;
178
177
  variables: _Input | null;
179
178
  };
@@ -2,7 +2,7 @@ import type { Cache as _Cache } from '../cache/cache';
2
2
  import { type QueryArtifact } from '../lib';
3
3
  import { ListCollection } from './list';
4
4
  import { Record } from './record';
5
- import type { CacheTypeDef, IDFields, QueryInput, QueryList, QueryValue, TypeNames, ValidLists } from './types';
5
+ import type { ArgType, CacheTypeDef, IDFields, QueryInput, QueryList, QueryValue, TypeFieldNames, TypeNames, ValidLists } from './types';
6
6
  export declare class Cache<Def extends CacheTypeDef> {
7
7
  _internal_unstable: _Cache;
8
8
  constructor(cache: _Cache);
@@ -29,4 +29,11 @@ export declare class Cache<Def extends CacheTypeDef> {
29
29
  data: QueryValue<QueryList<Def>, _Query>;
30
30
  variables?: QueryInput<QueryList<Def>, _Query>;
31
31
  }): void;
32
+ /**
33
+ * Mark some elements of the cache stale.
34
+ */
35
+ markStale<_Type extends TypeNames<Def>, _Field extends TypeFieldNames<Def, _Type>>(type?: _Type, options?: {
36
+ field?: _Field;
37
+ when?: ArgType<Def, _Type, _Field>;
38
+ }): void;
32
39
  }
@@ -87,6 +87,9 @@ Please acknowledge this by setting acceptImperativeInstability to true in your c
87
87
  });
88
88
  return;
89
89
  }
90
+ markStale(type, options = {}) {
91
+ return this._internal_unstable.markTypeStale(type, options);
92
+ }
90
93
  }
91
94
  // Annotate the CommonJS export names for ESM import in node:
92
95
  0 && (module.exports = {
@@ -1,6 +1,6 @@
1
1
  import type { FragmentArtifact } from '../lib/types';
2
2
  import type { Cache } from './cache';
3
- import type { CacheTypeDef, FragmentList, FragmentValue, FragmentVariables, ValidTypes } from './types';
3
+ import type { ArgType, CacheTypeDef, FragmentList, FragmentValue, FragmentVariables, TypeFieldNames, ValidTypes } from './types';
4
4
  export declare class Record<Def extends CacheTypeDef, Type extends ValidTypes<Def>> {
5
5
  #private;
6
6
  type: string;
@@ -26,6 +26,16 @@ export declare class Record<Def extends CacheTypeDef, Type extends ValidTypes<De
26
26
  fragment: _Fragment;
27
27
  data: FragmentValue<FragmentList<Def, Type>, _Fragment>;
28
28
  variables?: FragmentVariables<FragmentList<Def, Type>, _Fragment>;
29
+ forceStale?: boolean;
29
30
  }): void;
30
31
  delete(): void;
32
+ /**
33
+ * Mark some elements of the record stale in the cache.
34
+ * @param field
35
+ * @param when
36
+ */
37
+ markStale<Field extends TypeFieldNames<Def, Type>>({ field, when, }?: {
38
+ field?: Field;
39
+ when?: ArgType<Def, Type, Field>;
40
+ }): void;
31
41
  }
@@ -70,12 +70,19 @@ class Record {
70
70
  config: this.#cache.config,
71
71
  artifact: args.fragment.artifact,
72
72
  input: args.variables
73
- }) ?? void 0
73
+ }) ?? void 0,
74
+ forceStale: args.forceStale
74
75
  });
75
76
  }
76
77
  delete() {
77
78
  this.#cache._internal_unstable.delete(this.#id);
78
79
  }
80
+ markStale({
81
+ field,
82
+ when
83
+ } = {}) {
84
+ this.#cache._internal_unstable.markRecordStale(this.#id, { field, when });
85
+ }
79
86
  }
80
87
  // Annotate the CommonJS export names for ESM import in node:
81
88
  0 && (module.exports = {
@@ -1,7 +1,7 @@
1
1
  import { type SubscriptionSelection, type FragmentArtifact, type QueryArtifact } from '../../lib';
2
2
  import { Cache } from '../cache';
3
3
  import type { Record } from '../record';
4
- type CacheTypeDef = {
4
+ export type CacheTypeDefTest = {
5
5
  types: {
6
6
  __ROOT__: {
7
7
  idFields: {};
@@ -16,23 +16,23 @@ type CacheTypeDef = {
16
16
  args: never;
17
17
  };
18
18
  viewer: {
19
- type: Record<CacheTypeDef, 'User'> | null;
19
+ type: Record<CacheTypeDefTest, 'User'> | null;
20
20
  args: never;
21
21
  };
22
22
  pets: {
23
- type: (Record<CacheTypeDef, 'Cat'> | Record<CacheTypeDef, 'User'>)[];
23
+ type: (Record<CacheTypeDefTest, 'Cat'> | Record<CacheTypeDefTest, 'User'>)[];
24
24
  args: never;
25
25
  };
26
26
  listOfLists: {
27
- type: ((Record<CacheTypeDef, 'Cat'> | Record<CacheTypeDef, 'User'> | null | (null | Record<CacheTypeDef, 'User'>)[])[] | Record<CacheTypeDef, 'Cat'> | Record<CacheTypeDef, 'User'> | null)[];
27
+ type: ((Record<CacheTypeDefTest, 'Cat'> | Record<CacheTypeDefTest, 'User'> | null | (null | Record<CacheTypeDefTest, 'User'>)[])[] | Record<CacheTypeDefTest, 'Cat'> | Record<CacheTypeDefTest, 'User'> | null)[];
28
28
  args: never;
29
29
  };
30
30
  users: {
31
- type: Record<CacheTypeDef, 'User'>[] | null;
31
+ type: Record<CacheTypeDefTest, 'User'>[] | null;
32
32
  args: never;
33
33
  };
34
34
  pet: {
35
- type: Record<CacheTypeDef, 'Cat'> | Record<CacheTypeDef, 'User'>;
35
+ type: Record<CacheTypeDefTest, 'Cat'> | Record<CacheTypeDefTest, 'User'>;
36
36
  args: never;
37
37
  };
38
38
  };
@@ -60,7 +60,7 @@ type CacheTypeDef = {
60
60
  args: never;
61
61
  };
62
62
  parent: {
63
- type: Record<CacheTypeDef, 'User'>;
63
+ type: Record<CacheTypeDefTest, 'User'>;
64
64
  args: never;
65
65
  };
66
66
  id: {
@@ -84,7 +84,7 @@ type CacheTypeDef = {
84
84
  args: never;
85
85
  };
86
86
  parent: {
87
- type: Record<CacheTypeDef, 'User'> | null;
87
+ type: Record<CacheTypeDefTest, 'User'> | null;
88
88
  args: never;
89
89
  };
90
90
  id: {
@@ -147,11 +147,10 @@ type CacheTypeDef = {
147
147
  };
148
148
  };
149
149
  };
150
- export declare const testCache: () => Cache<CacheTypeDef>;
150
+ export declare const testCache: () => Cache<CacheTypeDefTest>;
151
151
  export declare const testFragment: (selection: SubscriptionSelection) => {
152
152
  artifact: FragmentArtifact;
153
153
  };
154
154
  export declare const testQuery: (selection: SubscriptionSelection) => {
155
155
  artifact: QueryArtifact;
156
156
  };
157
- export {};
@@ -4,9 +4,10 @@ import { GarbageCollector } from './gc';
4
4
  import type { ListCollection } from './lists';
5
5
  import { ListManager } from './lists';
6
6
  import { SchemaManager } from './schema';
7
+ import { StaleManager } from './staleManager';
7
8
  import type { Layer, LayerID } from './storage';
8
9
  import { InMemoryStorage } from './storage';
9
- import { type FieldSelection, InMemorySubscriptions } from './subscription';
10
+ import { InMemorySubscriptions, type FieldSelection } from './subscription';
10
11
  export declare class Cache {
11
12
  _internal_unstable: CacheInternal;
12
13
  constructor(config?: ConfigFile);
@@ -18,19 +19,30 @@ export declare class Cache {
18
19
  variables?: {};
19
20
  parent?: string;
20
21
  layer?: LayerID | null;
21
- applyUpdates?: boolean;
22
+ applyUpdates?: string[];
22
23
  notifySubscribers?: SubscriptionSpec[];
23
24
  forceNotify?: boolean;
25
+ forceStale?: boolean;
24
26
  }): SubscriptionSpec[];
25
27
  read(...args: Parameters<CacheInternal['getSelection']>): {
26
28
  data: GraphQLObject | null;
27
29
  partial: boolean;
30
+ stale: boolean;
28
31
  };
29
32
  subscribe(spec: SubscriptionSpec, variables?: {}): void;
30
33
  unsubscribe(spec: SubscriptionSpec, variables?: {}): void;
31
34
  list(name: string, parentID?: string, allLists?: boolean): ListCollection;
32
35
  delete(id: string): void;
33
36
  setConfig(config: ConfigFile): void;
37
+ markTypeStale(type?: string, options?: {
38
+ field?: string;
39
+ when?: {};
40
+ }): void;
41
+ markRecordStale(id: string, options: {
42
+ field?: string;
43
+ when?: {};
44
+ }): void;
45
+ getFieldTime(id: string, field: string): number | null | undefined;
34
46
  }
35
47
  declare class CacheInternal {
36
48
  private _disabled;
@@ -40,17 +52,19 @@ declare class CacheInternal {
40
52
  lists: ListManager;
41
53
  cache: Cache;
42
54
  lifetimes: GarbageCollector;
55
+ staleManager: StaleManager;
43
56
  schema: SchemaManager;
44
- constructor({ storage, subscriptions, lists, cache, lifetimes, schema, }: {
57
+ constructor({ storage, subscriptions, lists, cache, lifetimes, staleManager, schema, }: {
45
58
  storage: InMemoryStorage;
46
59
  subscriptions: InMemorySubscriptions;
47
60
  lists: ListManager;
48
61
  cache: Cache;
49
62
  lifetimes: GarbageCollector;
63
+ staleManager: StaleManager;
50
64
  schema: SchemaManager;
51
65
  });
52
66
  setConfig(config: ConfigFile): void;
53
- writeSelection({ data, selection, variables, parent, applyUpdates, layer, toNotify, forceNotify, }: {
67
+ writeSelection({ data, selection, variables, parent, applyUpdates, layer, toNotify, forceNotify, forceStale, }: {
54
68
  data: {
55
69
  [key: string]: GraphQLValue;
56
70
  };
@@ -62,8 +76,9 @@ declare class CacheInternal {
62
76
  root?: string;
63
77
  layer: Layer;
64
78
  toNotify?: FieldSelection[];
65
- applyUpdates?: boolean;
79
+ applyUpdates?: string[];
66
80
  forceNotify?: boolean;
81
+ forceStale?: boolean;
67
82
  }): FieldSelection[];
68
83
  getSelection({ selection, parent, variables, stepsFromConnection, }: {
69
84
  selection: SubscriptionSelection;
@@ -73,6 +88,7 @@ declare class CacheInternal {
73
88
  }): {
74
89
  data: GraphQLObject | null;
75
90
  partial: boolean;
91
+ stale: boolean;
76
92
  hasData: boolean;
77
93
  };
78
94
  id(type: string, data: {} | null): string | null;
@@ -87,6 +103,7 @@ declare class CacheInternal {
87
103
  }): {
88
104
  data: LinkedList<GraphQLValue>;
89
105
  partial: boolean;
106
+ stale: boolean;
90
107
  hasData: boolean;
91
108
  };
92
109
  extractNestedListIDs({ value, abstract, recordID, key, linkedType, fields, variables, applyUpdates, specs, layer, forceNotify, }: {
@@ -97,7 +114,7 @@ declare class CacheInternal {
97
114
  abstract: boolean;
98
115
  variables: {};
99
116
  specs: FieldSelection[];
100
- applyUpdates: boolean;
117
+ applyUpdates?: string[];
101
118
  fields: SubscriptionSelection;
102
119
  layer: Layer;
103
120
  forceNotify?: boolean;
@@ -1,9 +1,11 @@
1
- import { defaultConfigValues, computeID, keyFieldsForType } from "../lib/config";
1
+ import { computeKey } from "../lib";
2
+ import { computeID, defaultConfigValues, keyFieldsForType } from "../lib/config";
2
3
  import { deepEquals } from "../lib/deepEquals";
3
4
  import { getFieldsForType } from "../lib/selection";
4
5
  import { GarbageCollector } from "./gc";
5
6
  import { ListManager } from "./lists";
6
7
  import { SchemaManager } from "./schema";
8
+ import { StaleManager } from "./staleManager";
7
9
  import { InMemoryStorage } from "./storage";
8
10
  import { evaluateKey, flattenList } from "./stuff";
9
11
  import { InMemorySubscriptions } from "./subscription";
@@ -16,6 +18,7 @@ class Cache {
16
18
  subscriptions: new InMemorySubscriptions(this),
17
19
  lists: new ListManager(this, rootID),
18
20
  lifetimes: new GarbageCollector(this),
21
+ staleManager: new StaleManager(this),
19
22
  schema: new SchemaManager(this)
20
23
  });
21
24
  if (config) {
@@ -45,13 +48,14 @@ class Cache {
45
48
  return subscribers;
46
49
  }
47
50
  read(...args) {
48
- const { data, partial, hasData } = this._internal_unstable.getSelection(...args);
51
+ const { data, partial, stale, hasData } = this._internal_unstable.getSelection(...args);
49
52
  if (!hasData) {
50
- return { data: null, partial: false };
53
+ return { data: null, partial: false, stale: false };
51
54
  }
52
55
  return {
53
56
  data,
54
- partial
57
+ partial,
58
+ stale
55
59
  };
56
60
  }
57
61
  subscribe(spec, variables = {}) {
@@ -87,6 +91,30 @@ class Cache {
87
91
  setConfig(config) {
88
92
  this._internal_unstable.setConfig(config);
89
93
  }
94
+ markTypeStale(type, options = {}) {
95
+ if (!type) {
96
+ this._internal_unstable.staleManager.markAllStale();
97
+ } else if (!options.field) {
98
+ this._internal_unstable.staleManager.markTypeStale(type);
99
+ } else {
100
+ this._internal_unstable.staleManager.markTypeFieldStale(
101
+ type,
102
+ options.field,
103
+ options.when
104
+ );
105
+ }
106
+ }
107
+ markRecordStale(id, options) {
108
+ if (options.field) {
109
+ const key = computeKey({ field: options.field, args: options.when ?? {} });
110
+ this._internal_unstable.staleManager.markFieldStale(id, key);
111
+ } else {
112
+ this._internal_unstable.staleManager.markRecordStale(id);
113
+ }
114
+ }
115
+ getFieldTime(id, field) {
116
+ return this._internal_unstable.staleManager.getFieldTime(id, field);
117
+ }
90
118
  }
91
119
  class CacheInternal {
92
120
  _disabled = false;
@@ -102,6 +130,7 @@ class CacheInternal {
102
130
  lists;
103
131
  cache;
104
132
  lifetimes;
133
+ staleManager;
105
134
  schema;
106
135
  constructor({
107
136
  storage,
@@ -109,6 +138,7 @@ class CacheInternal {
109
138
  lists,
110
139
  cache,
111
140
  lifetimes,
141
+ staleManager,
112
142
  schema
113
143
  }) {
114
144
  this.storage = storage;
@@ -116,6 +146,7 @@ class CacheInternal {
116
146
  this.lists = lists;
117
147
  this.cache = cache;
118
148
  this.lifetimes = lifetimes;
149
+ this.staleManager = staleManager;
119
150
  this.schema = schema;
120
151
  this._disabled = typeof globalThis.window === "undefined";
121
152
  try {
@@ -133,10 +164,11 @@ class CacheInternal {
133
164
  selection,
134
165
  variables = {},
135
166
  parent = rootID,
136
- applyUpdates = false,
167
+ applyUpdates,
137
168
  layer,
138
169
  toNotify = [],
139
- forceNotify
170
+ forceNotify,
171
+ forceStale
140
172
  }) {
141
173
  if (this._disabled) {
142
174
  return [];
@@ -154,7 +186,7 @@ class CacheInternal {
154
186
  selection: fieldSelection,
155
187
  operations,
156
188
  abstract: isAbstract,
157
- update,
189
+ updates,
158
190
  nullable
159
191
  } = targetSelection[field];
160
192
  const key = evaluateKey(keyRaw, variables);
@@ -171,16 +203,31 @@ class CacheInternal {
171
203
  const displayLayer = layer.isDisplayLayer(displayLayers);
172
204
  if (displayLayer) {
173
205
  this.lifetimes.resetLifetime(parent, key);
206
+ if (forceStale) {
207
+ this.staleManager.markFieldStale(parent, key);
208
+ } else {
209
+ this.staleManager.setFieldTimeToNow(parent, key);
210
+ }
174
211
  }
175
212
  if (!fieldSelection) {
176
213
  let newValue = value;
177
- if (Array.isArray(value) && applyUpdates && update) {
178
- if (update === "append") {
179
- newValue = (previousValue || []).concat(value);
180
- } else if (update === "prepend") {
181
- newValue = value.concat(previousValue || []);
214
+ if (updates && applyUpdates && Array.isArray(value)) {
215
+ for (const update of applyUpdates) {
216
+ if (!updates.includes(update)) {
217
+ continue;
218
+ }
219
+ if (update === "append") {
220
+ newValue = (previousValue || []).concat(value);
221
+ } else if (update === "prepend") {
222
+ newValue = value.concat(previousValue || []);
223
+ }
182
224
  }
183
225
  }
226
+ if (updates && applyUpdates?.includes("prepend") && ["endCursor", "hasNextPage"].includes(key)) {
227
+ newValue = previousValue;
228
+ } else if (updates && applyUpdates?.includes("append") && ["startCursor", "hasPreviousPage"].includes(key)) {
229
+ newValue = previousValue;
230
+ }
184
231
  const valueChanged = !deepEquals(newValue, previousValue);
185
232
  if (displayLayer && (valueChanged || forceNotify)) {
186
233
  toNotify.push(...currentSubscribers);
@@ -240,7 +287,7 @@ class CacheInternal {
240
287
  }
241
288
  } else if (Array.isArray(value) && (typeof previousValue === "undefined" || Array.isArray(previousValue))) {
242
289
  let oldIDs = [...previousValue || []];
243
- const emptyEdges = !update ? [] : oldIDs.map((id) => {
290
+ const emptyEdges = !updates ? [] : oldIDs.map((id) => {
244
291
  if (!id) {
245
292
  return "";
246
293
  }
@@ -268,7 +315,7 @@ class CacheInternal {
268
315
  layer,
269
316
  forceNotify
270
317
  });
271
- if (applyUpdates && update) {
318
+ if (applyUpdates && updates) {
272
319
  if (key === "edges") {
273
320
  const newNodeIDs = [];
274
321
  for (const id of newIDs) {
@@ -296,12 +343,17 @@ class CacheInternal {
296
343
  return true;
297
344
  });
298
345
  }
299
- if (update === "prepend") {
300
- linkedIDs = newIDs.concat(oldIDs);
301
- } else if (update === "append") {
302
- linkedIDs = oldIDs.concat(newIDs);
303
- } else if (update === "replace") {
304
- linkedIDs = newIDs;
346
+ for (const update of applyUpdates) {
347
+ if (update !== "replace" && !updates.includes(update)) {
348
+ continue;
349
+ }
350
+ if (update === "prepend") {
351
+ linkedIDs = newIDs.concat(oldIDs);
352
+ } else if (update === "append") {
353
+ linkedIDs = oldIDs.concat(newIDs);
354
+ } else if (update === "replace") {
355
+ linkedIDs = newIDs;
356
+ }
305
357
  }
306
358
  } else {
307
359
  linkedIDs = nestedIDs;
@@ -387,12 +439,13 @@ class CacheInternal {
387
439
  stepsFromConnection = null
388
440
  }) {
389
441
  if (parent === null) {
390
- return { data: null, partial: false, hasData: true };
442
+ return { data: null, partial: false, stale: false, hasData: true };
391
443
  }
392
444
  const target = {};
393
445
  let hasData = false;
394
446
  let partial = false;
395
447
  let cascadeNull = false;
448
+ let stale = false;
396
449
  const typename = this.storage.get(parent, "__typename").value;
397
450
  let targetSelection = getFieldsForType(selection, typename);
398
451
  for (const [
@@ -401,6 +454,10 @@ class CacheInternal {
401
454
  ] of Object.entries(targetSelection)) {
402
455
  const key = evaluateKey(keyRaw, variables);
403
456
  const { value } = this.storage.get(parent, key);
457
+ const dt_field = this.staleManager.getFieldTime(parent, key);
458
+ if (dt_field === null) {
459
+ stale = true;
460
+ }
404
461
  let nextStep = stepsFromConnection;
405
462
  if (nextStep !== null) {
406
463
  if (nextStep >= 2) {
@@ -440,6 +497,9 @@ class CacheInternal {
440
497
  if (listValue.partial) {
441
498
  partial = true;
442
499
  }
500
+ if (listValue.stale) {
501
+ stale = true;
502
+ }
443
503
  if (listValue.hasData || value.length === 0) {
444
504
  hasData = true;
445
505
  }
@@ -454,6 +514,9 @@ class CacheInternal {
454
514
  if (objectFields.partial) {
455
515
  partial = true;
456
516
  }
517
+ if (objectFields.stale) {
518
+ stale = true;
519
+ }
457
520
  if (objectFields.hasData) {
458
521
  hasData = true;
459
522
  }
@@ -465,6 +528,7 @@ class CacheInternal {
465
528
  return {
466
529
  data: cascadeNull ? null : target,
467
530
  partial: hasData && partial,
531
+ stale: hasData && stale,
468
532
  hasData
469
533
  };
470
534
  }
@@ -492,6 +556,7 @@ class CacheInternal {
492
556
  }) {
493
557
  const result = [];
494
558
  let partialData = false;
559
+ let stale = false;
495
560
  let hasValues = false;
496
561
  for (const entry of linkedList) {
497
562
  if (Array.isArray(entry)) {
@@ -511,7 +576,12 @@ class CacheInternal {
511
576
  result.push(entry);
512
577
  continue;
513
578
  }
514
- const { data, partial, hasData } = this.getSelection({
579
+ const {
580
+ data,
581
+ partial,
582
+ stale: local_stale,
583
+ hasData
584
+ } = this.getSelection({
515
585
  parent: entry,
516
586
  selection: fields,
517
587
  variables,
@@ -521,6 +591,9 @@ class CacheInternal {
521
591
  if (partial) {
522
592
  partialData = true;
523
593
  }
594
+ if (local_stale) {
595
+ stale = true;
596
+ }
524
597
  if (hasData) {
525
598
  hasValues = true;
526
599
  }
@@ -528,6 +601,7 @@ class CacheInternal {
528
601
  return {
529
602
  data: result,
530
603
  partial: partialData,
604
+ stale,
531
605
  hasData: hasValues
532
606
  };
533
607
  }
@@ -14,6 +14,8 @@ class GarbageCollector {
14
14
  this.lifetimes.get(id).set(field, 0);
15
15
  }
16
16
  tick() {
17
+ const dt_tick = Date.now().valueOf();
18
+ const config_max_time = this.cache._internal_unstable.config.defaultLifetime;
17
19
  for (const [id, fieldMap] of this.lifetimes.entries()) {
18
20
  for (const [field, lifetime] of fieldMap.entries()) {
19
21
  if (this.cache._internal_unstable.subscriptions.get(id, field).length > 0) {
@@ -27,6 +29,13 @@ class GarbageCollector {
27
29
  if ([...fieldMap.keys()].length === 0) {
28
30
  this.lifetimes.delete(id);
29
31
  }
32
+ this.cache._internal_unstable.staleManager.delete(id, field);
33
+ }
34
+ if (config_max_time && config_max_time > 0) {
35
+ const dt_valueOf = this.cache.getFieldTime(id, field);
36
+ if (dt_valueOf && dt_tick - dt_valueOf > config_max_time) {
37
+ this.cache._internal_unstable.staleManager.markFieldStale(id, field);
38
+ }
30
39
  }
31
40
  }
32
41
  }
@@ -151,7 +151,7 @@ class List {
151
151
  edges: {
152
152
  keyRaw: "edges",
153
153
  type: "ConnectionEdge",
154
- update: where === "first" ? "prepend" : "append",
154
+ updates: ["append", "prepend"],
155
155
  selection: {
156
156
  fields: {
157
157
  node: {
@@ -187,7 +187,7 @@ class List {
187
187
  newEntries: {
188
188
  keyRaw: this.key,
189
189
  type: listType,
190
- update: where === "first" ? "prepend" : "append",
190
+ updates: ["append", "prepend"],
191
191
  selection: {
192
192
  ...selection,
193
193
  fields: {
@@ -210,7 +210,7 @@ class List {
210
210
  data: insertData,
211
211
  variables,
212
212
  parent: this.recordID,
213
- applyUpdates: true
213
+ applyUpdates: [where === "first" ? "prepend" : "append"]
214
214
  });
215
215
  }
216
216
  removeID(id, variables = {}) {