houdini 2.0.5 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/cmd/init.js CHANGED
@@ -472,7 +472,7 @@ async function packageJSON(targetPath, frameworkInfo) {
472
472
  }
473
473
  packageJSON2.devDependencies = {
474
474
  ...packageJSON2.devDependencies,
475
- houdini: "^2.0.5"
475
+ houdini: "^2.0.6"
476
476
  };
477
477
  if (frameworkInfo.framework === "svelte" || frameworkInfo.framework === "kit") {
478
478
  packageJSON2.devDependencies = {
@@ -540,6 +540,7 @@ async function write_config(db, config, invoke_hook, plugins, mode, logger = new
540
540
  db.run("DELETE FROM watch_schema_config");
541
541
  db.run("DELETE FROM scalar_config");
542
542
  db.run("DELETE FROM type_configs");
543
+ db.run("DELETE FROM runtime_scalar_definitions");
543
544
  db.run(
544
545
  `INSERT INTO config (
545
546
  include, exclude, schema_path, definitions_path, cache_buffer_size,
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "houdini",
3
- "version": "2.0.5",
3
+ "version": "2.0.6",
4
4
  "description": "The disappearing GraphQL clients",
5
5
  "keywords": [
6
6
  "typescript",
@@ -171,7 +171,12 @@ class Cache {
171
171
  return this._internal_unstable.storage.serialize();
172
172
  }
173
173
  hydrate(...args) {
174
- return this._internal_unstable.storage.hydrate(...args);
174
+ const layer = this._internal_unstable.storage.hydrate(...args);
175
+ const snapshot = args[0];
176
+ if (snapshot) {
177
+ this._internal_unstable.staleManager.registerHydration(snapshot);
178
+ }
179
+ return layer;
175
180
  }
176
181
  clearLayer(layerID) {
177
182
  const layer = this._internal_unstable.storage.getLayer(layerID);
@@ -3,7 +3,9 @@ export declare class StaleManager {
3
3
  #private;
4
4
  cache: Cache;
5
5
  private fieldsTime;
6
+ private pendingHydrated;
6
7
  constructor(cache: Cache);
8
+ registerHydration(snapshot: HydratedSnapshot): void;
7
9
  /**
8
10
  * get the FieldTime info
9
11
  * @param id User:1
@@ -30,3 +32,8 @@ export declare class StaleManager {
30
32
  delete(id: string, field: string): void;
31
33
  reset(): void;
32
34
  }
35
+ type HydratedSnapshot = {
36
+ fields?: Record<string, Record<string, unknown>>;
37
+ links?: Record<string, Record<string, unknown>>;
38
+ };
39
+ export {};
@@ -11,9 +11,41 @@ class StaleManager {
11
11
  // null => data stale (stale)
12
12
  // nulls mean that the value is stale, and the number is the time that the value was set
13
13
  fieldsTime = /* @__PURE__ */ new Map();
14
+ // snapshots that arrived via hydration and haven't been registered yet. field times
15
+ // are normally recorded per write, but hydration assigns whole layers at once and has
16
+ // to stay O(1) — so we hold onto the snapshot and only materialize its field times
17
+ // when a mark* actually needs them (staleness marks are rare; hydration happens on
18
+ // every page load). until then a hydrated field reads as "no entry", exactly how it
19
+ // read before any mark existed.
20
+ pendingHydrated = [];
14
21
  constructor(cache) {
15
22
  this.cache = cache;
16
23
  }
24
+ // note a hydrated snapshot whose fields should participate in staleness. O(1): the
25
+ // per-field registration is deferred to the first mark* call (see #flushHydrated)
26
+ registerHydration(snapshot) {
27
+ this.pendingHydrated.push(snapshot);
28
+ }
29
+ #flushHydrated() {
30
+ if (this.pendingHydrated.length === 0) {
31
+ return;
32
+ }
33
+ const now = Date.now();
34
+ for (const snapshot of this.pendingHydrated) {
35
+ for (const source of [snapshot.fields, snapshot.links]) {
36
+ for (const [id, fields] of Object.entries(source ?? {})) {
37
+ this.#initMapId(id);
38
+ const map = this.fieldsTime.get(id);
39
+ for (const field of Object.keys(fields)) {
40
+ if (!map.has(field)) {
41
+ map.set(field, now);
42
+ }
43
+ }
44
+ }
45
+ }
46
+ }
47
+ this.pendingHydrated = [];
48
+ }
17
49
  #initMapId = (id) => {
18
50
  if (!this.fieldsTime.get(id)) {
19
51
  this.fieldsTime.set(id, /* @__PURE__ */ new Map());
@@ -46,6 +78,7 @@ class StaleManager {
46
78
  this.fieldsTime.get(id)?.set(field, null);
47
79
  }
48
80
  markAllStale() {
81
+ this.#flushHydrated();
49
82
  for (const [id, fieldMap] of this.fieldsTime.entries()) {
50
83
  for (const [field] of fieldMap.entries()) {
51
84
  this.markFieldStale(id, field);
@@ -53,6 +86,7 @@ class StaleManager {
53
86
  }
54
87
  }
55
88
  markRecordStale(id) {
89
+ this.#flushHydrated();
56
90
  const fieldsTimeOfType = this.fieldsTime.get(id);
57
91
  if (fieldsTimeOfType) {
58
92
  for (const [field] of fieldsTimeOfType.entries()) {
@@ -61,6 +95,7 @@ class StaleManager {
61
95
  }
62
96
  }
63
97
  markTypeStale(type) {
98
+ this.#flushHydrated();
64
99
  for (const [id, fieldMap] of this.fieldsTime.entries()) {
65
100
  if (id.startsWith(`${type}:`)) {
66
101
  for (const [field] of fieldMap.entries()) {
@@ -70,6 +105,7 @@ class StaleManager {
70
105
  }
71
106
  }
72
107
  markTypeFieldStale(type, field, when) {
108
+ this.#flushHydrated();
73
109
  const key = computeKey({ field, args: when });
74
110
  for (const [id, fieldMap] of this.fieldsTime.entries()) {
75
111
  if (id.startsWith(`${type}:`)) {
@@ -96,6 +132,7 @@ class StaleManager {
96
132
  }
97
133
  reset() {
98
134
  this.fieldsTime.clear();
135
+ this.pendingHydrated = [];
99
136
  }
100
137
  }
101
138
  export {
@@ -17,7 +17,8 @@
17
17
  export declare class LRUCache<T> {
18
18
  _capacity: number;
19
19
  _map: Map<string, T>;
20
- constructor(capacity?: number);
20
+ _onEvict?: (value: T, key: string) => void;
21
+ constructor(capacity?: number, onEvict?: (value: T, key: string) => void);
21
22
  set(key: string, value: T): void;
22
23
  get(key: string): T | null;
23
24
  has(key: string): boolean;
@@ -26,7 +27,7 @@ export declare class LRUCache<T> {
26
27
  capacity(): number;
27
28
  clear(): void;
28
29
  }
29
- export declare function createLRUCache<T>(capacity?: number): LRUCache<T>;
30
+ export declare function createLRUCache<T>(capacity?: number, onEvict?: (value: T, key: string) => void): LRUCache<T>;
30
31
  /**
31
32
  MIT License
32
33
 
@@ -1,17 +1,29 @@
1
1
  class LRUCache {
2
2
  _capacity;
3
3
  _map;
4
- constructor(capacity = 1e3) {
4
+ // invoked whenever a value leaves the cache (capacity eviction, delete, overwrite,
5
+ // clear) so owners of resources with teardown (subscriptions, stores) can dispose them
6
+ _onEvict;
7
+ constructor(capacity = 1e3, onEvict) {
5
8
  this._capacity = capacity;
6
9
  this._map = /* @__PURE__ */ new Map();
10
+ this._onEvict = onEvict;
7
11
  }
8
12
  set(key, value) {
13
+ const existing = this._map.get(key);
9
14
  this._map.delete(key);
15
+ if (existing !== void 0 && existing !== value) {
16
+ this._onEvict?.(existing, key);
17
+ }
10
18
  this._map.set(key, value);
11
19
  if (this._map.size > this._capacity) {
12
20
  const firstKey = this._map.keys().next();
13
21
  if (!firstKey.done) {
22
+ const evicted = this._map.get(firstKey.value);
14
23
  this._map.delete(firstKey.value);
24
+ if (evicted !== void 0) {
25
+ this._onEvict?.(evicted, firstKey.value);
26
+ }
15
27
  }
16
28
  }
17
29
  }
@@ -27,7 +39,11 @@ class LRUCache {
27
39
  return this._map.has(key);
28
40
  }
29
41
  delete(key) {
42
+ const existing = this._map.get(key);
30
43
  this._map.delete(key);
44
+ if (existing !== void 0) {
45
+ this._onEvict?.(existing, key);
46
+ }
31
47
  }
32
48
  size() {
33
49
  return this._map.size;
@@ -36,11 +52,15 @@ class LRUCache {
36
52
  return this._capacity - this._map.size;
37
53
  }
38
54
  clear() {
55
+ const entries = [...this._map.entries()];
39
56
  this._map.clear();
57
+ for (const [key, value] of entries) {
58
+ this._onEvict?.(value, key);
59
+ }
40
60
  }
41
61
  }
42
- function createLRUCache(capacity = 1e3) {
43
- return new LRUCache(capacity);
62
+ function createLRUCache(capacity = 1e3, onEvict) {
63
+ return new LRUCache(capacity, onEvict);
44
64
  }
45
65
  export {
46
66
  LRUCache,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "houdini",
3
- "version": "2.0.5",
3
+ "version": "2.0.6",
4
4
  "description": "The disappearing GraphQL clients",
5
5
  "keywords": [
6
6
  "typescript",