houdini 1.3.1 → 1.4.1

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 (38) hide show
  1. package/build/cmd-cjs/index.js +143 -28
  2. package/build/cmd-esm/index.js +143 -28
  3. package/build/codegen-cjs/index.js +123 -10
  4. package/build/codegen-esm/index.js +123 -10
  5. package/build/lib/config.d.ts +1 -0
  6. package/build/lib/router/types.d.ts +3 -1
  7. package/build/lib-cjs/index.js +92 -19
  8. package/build/lib-esm/index.js +91 -19
  9. package/build/runtime/cache/cache.d.ts +1 -1
  10. package/build/runtime/cache/lists.d.ts +3 -1
  11. package/build/runtime/lib/config.d.ts +10 -1
  12. package/build/runtime/lib/types.d.ts +14 -2
  13. package/build/runtime-cjs/cache/cache.d.ts +1 -1
  14. package/build/runtime-cjs/cache/cache.js +31 -5
  15. package/build/runtime-cjs/cache/lists.d.ts +3 -1
  16. package/build/runtime-cjs/cache/lists.js +15 -1
  17. package/build/runtime-cjs/client/documentStore.js +27 -7
  18. package/build/runtime-cjs/client/plugins/fetch.js +5 -0
  19. package/build/runtime-cjs/lib/config.d.ts +10 -1
  20. package/build/runtime-cjs/lib/types.d.ts +14 -2
  21. package/build/runtime-cjs/lib/types.js +7 -0
  22. package/build/runtime-cjs/router/match.js +1 -1
  23. package/build/runtime-esm/cache/cache.d.ts +1 -1
  24. package/build/runtime-esm/cache/cache.js +31 -5
  25. package/build/runtime-esm/cache/lists.d.ts +3 -1
  26. package/build/runtime-esm/cache/lists.js +15 -1
  27. package/build/runtime-esm/client/documentStore.js +28 -8
  28. package/build/runtime-esm/client/plugins/fetch.js +5 -0
  29. package/build/runtime-esm/lib/config.d.ts +10 -1
  30. package/build/runtime-esm/lib/types.d.ts +14 -2
  31. package/build/runtime-esm/lib/types.js +6 -0
  32. package/build/runtime-esm/router/match.js +1 -1
  33. package/build/test-cjs/index.js +129 -15
  34. package/build/test-esm/index.js +129 -15
  35. package/build/vite/ast.d.ts +8 -2
  36. package/build/vite-cjs/index.js +374 -255
  37. package/build/vite-esm/index.js +374 -255
  38. package/package.json +3 -3
@@ -61448,6 +61448,11 @@ var CachePolicy = {
61448
61448
  CacheAndNetwork: "CacheAndNetwork",
61449
61449
  NoCache: "NoCache"
61450
61450
  };
61451
+ var DedupeMatchMode = {
61452
+ Variables: "Variables",
61453
+ Operation: "Operation",
61454
+ None: "None"
61455
+ };
61451
61456
  var PaginateMode = {
61452
61457
  Infinite: "Infinite",
61453
61458
  SinglePage: "SinglePage"
@@ -63894,7 +63899,18 @@ var ListManager = class {
63894
63899
  }
63895
63900
  lists = /* @__PURE__ */ new Map();
63896
63901
  listsByField = /* @__PURE__ */ new Map();
63897
- get(listName, id, allLists) {
63902
+ get(listName, id, allLists, skipMatches) {
63903
+ const lists = this.getLists(listName, id, allLists);
63904
+ if (!lists) {
63905
+ return null;
63906
+ }
63907
+ if (skipMatches) {
63908
+ return new ListCollection(lists.lists.filter((list) => !skipMatches.has(list.fieldRef)));
63909
+ } else {
63910
+ return lists;
63911
+ }
63912
+ }
63913
+ getLists(listName, id, allLists) {
63898
63914
  const matches = this.lists.get(listName);
63899
63915
  if (!matches || matches.size === 0) {
63900
63916
  return null;
@@ -64016,6 +64032,9 @@ var List = class {
64016
64032
  this.manager = manager;
64017
64033
  this.abstract = abstract;
64018
64034
  }
64035
+ get fieldRef() {
64036
+ return `${this.recordID}.${this.key}`;
64037
+ }
64019
64038
  when(when) {
64020
64039
  return this.manager.lists.get(this.name).get(this.recordID).when(when);
64021
64040
  }
@@ -65168,8 +65187,8 @@ var Cache = class {
65168
65187
  variables
65169
65188
  );
65170
65189
  }
65171
- list(name, parentID, allLists) {
65172
- const handler = this._internal_unstable.lists.get(name, parentID, allLists);
65190
+ list(name, parentID, allLists, skipMatches) {
65191
+ const handler = this._internal_unstable.lists.get(name, parentID, allLists, skipMatches);
65173
65192
  if (!handler) {
65174
65193
  throw new Error(
65175
65194
  `Cannot find list with name: ${name}${parentID ? " under parent " + parentID : ""}. Is it possible that the query is not mounted?`
@@ -65584,6 +65603,7 @@ var CacheInternal = class {
65584
65603
  });
65585
65604
  }
65586
65605
  }
65606
+ const processedOperations = /* @__PURE__ */ new Set();
65587
65607
  for (const operation of operations || []) {
65588
65608
  let parentID;
65589
65609
  if (operation.parentID) {
@@ -65603,7 +65623,12 @@ var CacheInternal = class {
65603
65623
  const targets = Array.isArray(value) ? value : [value];
65604
65624
  for (const target of targets) {
65605
65625
  if (operation.action === "insert" && target instanceof Object && fieldSelection && operation.list) {
65606
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).addToList(
65626
+ this.cache.list(
65627
+ operation.list,
65628
+ parentID,
65629
+ operation.target === "all",
65630
+ processedOperations
65631
+ ).when(operation.when).addToList(
65607
65632
  fieldSelection,
65608
65633
  target,
65609
65634
  variables,
@@ -65611,7 +65636,12 @@ var CacheInternal = class {
65611
65636
  layer
65612
65637
  );
65613
65638
  } else if (operation.action === "toggle" && target instanceof Object && fieldSelection && operation.list) {
65614
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).toggleElement({
65639
+ this.cache.list(
65640
+ operation.list,
65641
+ parentID,
65642
+ operation.target === "all",
65643
+ processedOperations
65644
+ ).when(operation.when).toggleElement({
65615
65645
  selection: fieldSelection,
65616
65646
  data: target,
65617
65647
  variables,
@@ -65619,7 +65649,12 @@ var CacheInternal = class {
65619
65649
  layer
65620
65650
  });
65621
65651
  } else if (operation.action === "remove" && target instanceof Object && fieldSelection && operation.list) {
65622
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).remove(target, variables, layer);
65652
+ this.cache.list(
65653
+ operation.list,
65654
+ parentID,
65655
+ operation.target === "all",
65656
+ processedOperations
65657
+ ).when(operation.when).remove(target, variables, layer);
65623
65658
  } else if (operation.action === "delete" && operation.type && target) {
65624
65659
  const targetID = this.id(operation.type, target);
65625
65660
  if (!targetID) {
@@ -65631,6 +65666,16 @@ var CacheInternal = class {
65631
65666
  this.cache.delete(targetID, layer);
65632
65667
  }
65633
65668
  }
65669
+ if (operation.list) {
65670
+ const matchingLists = this.cache.list(
65671
+ operation.list,
65672
+ parentID,
65673
+ operation.target === "all"
65674
+ );
65675
+ for (const list of matchingLists.lists) {
65676
+ processedOperations.add(list.fieldRef);
65677
+ }
65678
+ }
65634
65679
  }
65635
65680
  }
65636
65681
  return toNotify;
@@ -66147,6 +66192,11 @@ var defaultFetch = (url, params) => {
66147
66192
  ...params?.headers
66148
66193
  }
66149
66194
  });
66195
+ if (!result.ok && !result.headers.get("content-type")?.startsWith("application/json") && !result.headers.get("content-type")?.startsWith("application/graphql+json")) {
66196
+ throw new Error(
66197
+ `Failed to fetch: server returned invalid response with error ${result.status}: ${result.statusText}`
66198
+ );
66199
+ }
66150
66200
  return await result.json();
66151
66201
  };
66152
66202
  };
@@ -66967,7 +67017,8 @@ var DocumentStore = class extends Writable {
66967
67017
  pendingPromise = null;
66968
67018
  serverSideFallback;
66969
67019
  controllerKey(variables) {
66970
- return this.artifact.name;
67020
+ const usedVariables = "dedupe" in this.artifact && this.artifact.dedupe?.match !== DedupeMatchMode.Variables ? {} : variables;
67021
+ return `${this.artifact.name}@${stableStringify(usedVariables)}`;
66971
67022
  }
66972
67023
  constructor({
66973
67024
  artifact,
@@ -67029,16 +67080,20 @@ var DocumentStore = class extends Writable {
67029
67080
  silenceEcho = false,
67030
67081
  abortController = new AbortController()
67031
67082
  } = {}) {
67032
- if ("dedupe" in this.artifact) {
67033
- if (inflightRequests[this.controllerKey(variables)]) {
67034
- if (this.artifact.dedupe === "first") {
67035
- inflightRequests[this.controllerKey(variables)].abort();
67036
- inflightRequests[this.controllerKey(variables)] = abortController;
67083
+ if ("dedupe" in this.artifact && this.artifact.dedupe && this.artifact.dedupe.match !== "None") {
67084
+ const dedupeKey = this.controllerKey(variables);
67085
+ if (inflightRequests[dedupeKey]) {
67086
+ if (this.artifact.dedupe.cancel === "first") {
67087
+ inflightRequests[dedupeKey].controller.abort();
67088
+ inflightRequests[dedupeKey].controller = abortController;
67037
67089
  } else {
67038
67090
  abortController.abort();
67039
67091
  }
67040
67092
  } else {
67041
- inflightRequests[this.controllerKey(variables)] = abortController;
67093
+ inflightRequests[dedupeKey] = {
67094
+ variables,
67095
+ controller: abortController
67096
+ };
67042
67097
  }
67043
67098
  }
67044
67099
  let context = new ClientPluginContextWrapper({
@@ -67360,6 +67415,21 @@ function marshalVariables(ctx) {
67360
67415
  function variablesChanged(ctx) {
67361
67416
  return ctx.stuff.inputs?.changed;
67362
67417
  }
67418
+ function stableStringify(obj) {
67419
+ return JSON.stringify(sortObject(obj));
67420
+ }
67421
+ function sortObject(obj) {
67422
+ if (obj === null || typeof obj !== "object") {
67423
+ return obj;
67424
+ }
67425
+ if (Array.isArray(obj)) {
67426
+ return obj.map(sortObject);
67427
+ }
67428
+ return Object.keys(obj).sort().reduce((result, key) => {
67429
+ result[key] = sortObject(obj[key]);
67430
+ return result;
67431
+ }, {});
67432
+ }
67363
67433
 
67364
67434
  // src/runtime/client/plugins/injectedPlugins.ts
67365
67435
  var plugins = [];
@@ -67796,8 +67866,9 @@ var Config = class {
67796
67866
  localSchema;
67797
67867
  projectRoot;
67798
67868
  schema;
67869
+ runtimeDir;
67799
67870
  schemaPath;
67800
- persistedQueriesPath = "./$houdini/persisted_queries.json";
67871
+ persistedQueriesPath;
67801
67872
  exclude;
67802
67873
  scalars;
67803
67874
  module = "esm";
@@ -67838,6 +67909,7 @@ var Config = class {
67838
67909
  let {
67839
67910
  schema,
67840
67911
  schemaPath = "./schema.graphql",
67912
+ runtimeDir = "$houdini",
67841
67913
  exclude = [],
67842
67914
  module = "esm",
67843
67915
  scalars,
@@ -67876,6 +67948,7 @@ var Config = class {
67876
67948
  this.projectRoot = dirname(
67877
67949
  projectDir ? join2(process.cwd(), projectDir) : filepath
67878
67950
  );
67951
+ this.runtimeDir = runtimeDir;
67879
67952
  this.scalars = scalars;
67880
67953
  this.cacheBufferSize = cacheBufferSize;
67881
67954
  this.defaultCachePolicy = defaultCachePolicy;
@@ -67890,11 +67963,9 @@ var Config = class {
67890
67963
  this.schemaPollInterval = watchSchema?.interval === void 0 ? 2e3 : watchSchema.interval;
67891
67964
  this.schemaPollTimeout = watchSchema?.timeout ?? 3e4;
67892
67965
  this.schemaPollHeaders = watchSchema?.headers ?? {};
67893
- this.rootDir = join2(this.projectRoot, "$houdini");
67966
+ this.rootDir = join2(this.projectRoot, this.runtimeDir);
67967
+ this.persistedQueriesPath = persistedQueriesPath ?? join2(this.rootDir, "persisted_queries.json");
67894
67968
  this.#fragmentVariableMaps = {};
67895
- if (persistedQueriesPath) {
67896
- this.persistedQueriesPath = persistedQueriesPath;
67897
- }
67898
67969
  if (defaultKeys) {
67899
67970
  this.defaultKeys = defaultKeys;
67900
67971
  }
@@ -69206,7 +69277,7 @@ function exec(match, params) {
69206
69277
  if (param.rest)
69207
69278
  result[param.name] = "";
69208
69279
  } else {
69209
- result[param.name] = value;
69280
+ result[param.name] = decodeURIComponent(value);
69210
69281
  }
69211
69282
  }
69212
69283
  if (buffered)
@@ -69733,6 +69804,7 @@ export {
69733
69804
  CompiledSubscriptionKind,
69734
69805
  Config,
69735
69806
  DataSource,
69807
+ DedupeMatchMode,
69736
69808
  DocumentStore,
69737
69809
  HoudiniClient,
69738
69810
  HoudiniError,
@@ -35,7 +35,7 @@ export declare class Cache {
35
35
  };
36
36
  subscribe(spec: SubscriptionSpec, variables?: {}): void;
37
37
  unsubscribe(spec: SubscriptionSpec, variables?: {}): void;
38
- list(name: string, parentID?: string, allLists?: boolean): ListCollection;
38
+ list(name: string, parentID?: string, allLists?: boolean, skipMatches?: Set<string>): ListCollection;
39
39
  registerKeyMap(source: string, mapped: string): void;
40
40
  delete(id: string, layer?: Layer): void;
41
41
  setConfig(config: ConfigFile): void;
@@ -7,7 +7,8 @@ export declare class ListManager {
7
7
  constructor(cache: Cache, rootID: string);
8
8
  lists: Map<string, Map<string, ListCollection>>;
9
9
  private listsByField;
10
- get(listName: string, id?: string, allLists?: boolean): ListCollection | null | undefined;
10
+ get(listName: string, id?: string, allLists?: boolean, skipMatches?: Set<string>): ListCollection | null;
11
+ getLists(listName: string, id?: string, allLists?: boolean): ListCollection | null | undefined;
11
12
  remove(listName: string, id: string): void;
12
13
  add(list: {
13
14
  name: string;
@@ -41,6 +42,7 @@ export declare class List {
41
42
  constructor({ name, recordID, recordType, key, listType, selection, when, filters, connection, manager, abstract, }: Parameters<ListManager['add']>[0] & {
42
43
  manager: ListManager;
43
44
  });
45
+ get fieldRef(): string;
44
46
  when(when?: ListWhen): ListCollection;
45
47
  append({ selection, data, variables, layer, }: {
46
48
  selection: SubscriptionSelection;
@@ -73,6 +73,10 @@ export type ConfigFile = {
73
73
  * Specifies whether the default paginate mode is Infinite or SinglePage. (default: `Infinite`)
74
74
  */
75
75
  defaultPaginateMode?: PaginateModes;
76
+ /**
77
+ * Prevents the runtime from deduplicating pagination requests
78
+ */
79
+ supressPaginationDeduplication?: boolean;
76
80
  /**
77
81
  * A list of fields to use when computing a record’s id. The default value is ['id']. For more information: https://www.houdinigraphql.com/guides/caching-data#custom-ids
78
82
  */
@@ -95,7 +99,7 @@ export type ConfigFile = {
95
99
  */
96
100
  watchSchema?: WatchSchemaConfig;
97
101
  /**
98
- * Specifies the the persisted queries path and file. (default: `./$houdini/persisted_queries.json`)
102
+ * Specifies the the persisted queries path and file. (default: `<rootDir>/persisted_queries.json`)
99
103
  */
100
104
  persistedQueriesPath?: string;
101
105
  /**
@@ -107,6 +111,11 @@ export type ConfigFile = {
107
111
  * @default process.cwd()
108
112
  */
109
113
  projectDir?: string;
114
+ /**
115
+ * The relative path from your project directory pointing to your output directory.
116
+ * @default `$houdini`
117
+ */
118
+ runtimeDir?: string;
110
119
  /**
111
120
  * For now, the cache's imperative API is considered unstable. In order to suppress the warning,
112
121
  * you must enable this flag.
@@ -7,6 +7,12 @@ export declare const CachePolicy: {
7
7
  readonly NoCache: "NoCache";
8
8
  };
9
9
  export type CachePolicies = ValuesOf<typeof CachePolicy>;
10
+ export declare const DedupeMatchMode: {
11
+ readonly Variables: "Variables";
12
+ readonly Operation: "Operation";
13
+ readonly None: "None";
14
+ };
15
+ export type DedupeMatchModes = ValuesOf<typeof DedupeMatchMode>;
10
16
  export declare const PaginateMode: {
11
17
  readonly Infinite: "Infinite";
12
18
  readonly SinglePage: "SinglePage";
@@ -60,11 +66,17 @@ export type QueryArtifact = BaseCompiledDocument<'HoudiniQuery'> & {
60
66
  policy?: CachePolicies;
61
67
  partial?: boolean;
62
68
  enableLoadingState?: 'global' | 'local';
63
- dedupe?: 'first' | 'last';
69
+ dedupe?: {
70
+ cancel: 'first' | 'last';
71
+ match: DedupeMatchModes;
72
+ };
64
73
  };
65
74
  export type MutationArtifact = BaseCompiledDocument<'HoudiniMutation'> & {
66
75
  optimisticKeys?: boolean;
67
- dedupe?: 'first' | 'last';
76
+ dedupe?: {
77
+ cancel: 'first' | 'last';
78
+ match: DedupeMatchModes;
79
+ };
68
80
  };
69
81
  export type FragmentArtifact = BaseCompiledDocument<'HoudiniFragment'> & {
70
82
  enableLoadingState?: 'global' | 'local';
@@ -35,7 +35,7 @@ export declare class Cache {
35
35
  };
36
36
  subscribe(spec: SubscriptionSpec, variables?: {}): void;
37
37
  unsubscribe(spec: SubscriptionSpec, variables?: {}): void;
38
- list(name: string, parentID?: string, allLists?: boolean): ListCollection;
38
+ list(name: string, parentID?: string, allLists?: boolean, skipMatches?: Set<string>): ListCollection;
39
39
  registerKeyMap(source: string, mapped: string): void;
40
40
  delete(id: string, layer?: Layer): void;
41
41
  setConfig(config: ConfigFile): void;
@@ -100,8 +100,8 @@ class Cache {
100
100
  variables
101
101
  );
102
102
  }
103
- list(name, parentID, allLists) {
104
- const handler = this._internal_unstable.lists.get(name, parentID, allLists);
103
+ list(name, parentID, allLists, skipMatches) {
104
+ const handler = this._internal_unstable.lists.get(name, parentID, allLists, skipMatches);
105
105
  if (!handler) {
106
106
  throw new Error(
107
107
  `Cannot find list with name: ${name}${parentID ? " under parent " + parentID : ""}. Is it possible that the query is not mounted?`
@@ -516,6 +516,7 @@ class CacheInternal {
516
516
  });
517
517
  }
518
518
  }
519
+ const processedOperations = /* @__PURE__ */ new Set();
519
520
  for (const operation of operations || []) {
520
521
  let parentID;
521
522
  if (operation.parentID) {
@@ -535,7 +536,12 @@ class CacheInternal {
535
536
  const targets = Array.isArray(value) ? value : [value];
536
537
  for (const target of targets) {
537
538
  if (operation.action === "insert" && target instanceof Object && fieldSelection && operation.list) {
538
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).addToList(
539
+ this.cache.list(
540
+ operation.list,
541
+ parentID,
542
+ operation.target === "all",
543
+ processedOperations
544
+ ).when(operation.when).addToList(
539
545
  fieldSelection,
540
546
  target,
541
547
  variables,
@@ -543,7 +549,12 @@ class CacheInternal {
543
549
  layer
544
550
  );
545
551
  } else if (operation.action === "toggle" && target instanceof Object && fieldSelection && operation.list) {
546
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).toggleElement({
552
+ this.cache.list(
553
+ operation.list,
554
+ parentID,
555
+ operation.target === "all",
556
+ processedOperations
557
+ ).when(operation.when).toggleElement({
547
558
  selection: fieldSelection,
548
559
  data: target,
549
560
  variables,
@@ -551,7 +562,12 @@ class CacheInternal {
551
562
  layer
552
563
  });
553
564
  } else if (operation.action === "remove" && target instanceof Object && fieldSelection && operation.list) {
554
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).remove(target, variables, layer);
565
+ this.cache.list(
566
+ operation.list,
567
+ parentID,
568
+ operation.target === "all",
569
+ processedOperations
570
+ ).when(operation.when).remove(target, variables, layer);
555
571
  } else if (operation.action === "delete" && operation.type && target) {
556
572
  const targetID = this.id(operation.type, target);
557
573
  if (!targetID) {
@@ -563,6 +579,16 @@ class CacheInternal {
563
579
  this.cache.delete(targetID, layer);
564
580
  }
565
581
  }
582
+ if (operation.list) {
583
+ const matchingLists = this.cache.list(
584
+ operation.list,
585
+ parentID,
586
+ operation.target === "all"
587
+ );
588
+ for (const list of matchingLists.lists) {
589
+ processedOperations.add(list.fieldRef);
590
+ }
591
+ }
566
592
  }
567
593
  }
568
594
  return toNotify;
@@ -7,7 +7,8 @@ export declare class ListManager {
7
7
  constructor(cache: Cache, rootID: string);
8
8
  lists: Map<string, Map<string, ListCollection>>;
9
9
  private listsByField;
10
- get(listName: string, id?: string, allLists?: boolean): ListCollection | null | undefined;
10
+ get(listName: string, id?: string, allLists?: boolean, skipMatches?: Set<string>): ListCollection | null;
11
+ getLists(listName: string, id?: string, allLists?: boolean): ListCollection | null | undefined;
11
12
  remove(listName: string, id: string): void;
12
13
  add(list: {
13
14
  name: string;
@@ -41,6 +42,7 @@ export declare class List {
41
42
  constructor({ name, recordID, recordType, key, listType, selection, when, filters, connection, manager, abstract, }: Parameters<ListManager['add']>[0] & {
42
43
  manager: ListManager;
43
44
  });
45
+ get fieldRef(): string;
44
46
  when(when?: ListWhen): ListCollection;
45
47
  append({ selection, data, variables, layer, }: {
46
48
  selection: SubscriptionSelection;
@@ -34,7 +34,18 @@ class ListManager {
34
34
  }
35
35
  lists = /* @__PURE__ */ new Map();
36
36
  listsByField = /* @__PURE__ */ new Map();
37
- get(listName, id, allLists) {
37
+ get(listName, id, allLists, skipMatches) {
38
+ const lists = this.getLists(listName, id, allLists);
39
+ if (!lists) {
40
+ return null;
41
+ }
42
+ if (skipMatches) {
43
+ return new ListCollection(lists.lists.filter((list) => !skipMatches.has(list.fieldRef)));
44
+ } else {
45
+ return lists;
46
+ }
47
+ }
48
+ getLists(listName, id, allLists) {
38
49
  const matches = this.lists.get(listName);
39
50
  if (!matches || matches.size === 0) {
40
51
  return null;
@@ -156,6 +167,9 @@ class List {
156
167
  this.manager = manager;
157
168
  this.abstract = abstract;
158
169
  }
170
+ get fieldRef() {
171
+ return `${this.recordID}.${this.key}`;
172
+ }
159
173
  when(when) {
160
174
  return this.manager.lists.get(this.name).get(this.recordID).when(when);
161
175
  }
@@ -42,7 +42,8 @@ class DocumentStore extends import_store.Writable {
42
42
  pendingPromise = null;
43
43
  serverSideFallback;
44
44
  controllerKey(variables) {
45
- return this.artifact.name;
45
+ const usedVariables = "dedupe" in this.artifact && this.artifact.dedupe?.match !== import_types.DedupeMatchMode.Variables ? {} : variables;
46
+ return `${this.artifact.name}@${stableStringify(usedVariables)}`;
46
47
  }
47
48
  constructor({
48
49
  artifact,
@@ -104,16 +105,20 @@ class DocumentStore extends import_store.Writable {
104
105
  silenceEcho = false,
105
106
  abortController = new AbortController()
106
107
  } = {}) {
107
- if ("dedupe" in this.artifact) {
108
- if (inflightRequests[this.controllerKey(variables)]) {
109
- if (this.artifact.dedupe === "first") {
110
- inflightRequests[this.controllerKey(variables)].abort();
111
- inflightRequests[this.controllerKey(variables)] = abortController;
108
+ if ("dedupe" in this.artifact && this.artifact.dedupe && this.artifact.dedupe.match !== "None") {
109
+ const dedupeKey = this.controllerKey(variables);
110
+ if (inflightRequests[dedupeKey]) {
111
+ if (this.artifact.dedupe.cancel === "first") {
112
+ inflightRequests[dedupeKey].controller.abort();
113
+ inflightRequests[dedupeKey].controller = abortController;
112
114
  } else {
113
115
  abortController.abort();
114
116
  }
115
117
  } else {
116
- inflightRequests[this.controllerKey(variables)] = abortController;
118
+ inflightRequests[dedupeKey] = {
119
+ variables,
120
+ controller: abortController
121
+ };
117
122
  }
118
123
  }
119
124
  let context = new ClientPluginContextWrapper({
@@ -435,6 +440,21 @@ function marshalVariables(ctx) {
435
440
  function variablesChanged(ctx) {
436
441
  return ctx.stuff.inputs?.changed;
437
442
  }
443
+ function stableStringify(obj) {
444
+ return JSON.stringify(sortObject(obj));
445
+ }
446
+ function sortObject(obj) {
447
+ if (obj === null || typeof obj !== "object") {
448
+ return obj;
449
+ }
450
+ if (Array.isArray(obj)) {
451
+ return obj.map(sortObject);
452
+ }
453
+ return Object.keys(obj).sort().reduce((result, key) => {
454
+ result[key] = sortObject(obj[key]);
455
+ return result;
456
+ }, {});
457
+ }
438
458
  // Annotate the CommonJS export names for ESM import in node:
439
459
  0 && (module.exports = {
440
460
  DocumentStore
@@ -91,6 +91,11 @@ const defaultFetch = (url, params) => {
91
91
  ...params?.headers
92
92
  }
93
93
  });
94
+ if (!result.ok && !result.headers.get("content-type")?.startsWith("application/json") && !result.headers.get("content-type")?.startsWith("application/graphql+json")) {
95
+ throw new Error(
96
+ `Failed to fetch: server returned invalid response with error ${result.status}: ${result.statusText}`
97
+ );
98
+ }
94
99
  return await result.json();
95
100
  };
96
101
  };
@@ -73,6 +73,10 @@ export type ConfigFile = {
73
73
  * Specifies whether the default paginate mode is Infinite or SinglePage. (default: `Infinite`)
74
74
  */
75
75
  defaultPaginateMode?: PaginateModes;
76
+ /**
77
+ * Prevents the runtime from deduplicating pagination requests
78
+ */
79
+ supressPaginationDeduplication?: boolean;
76
80
  /**
77
81
  * A list of fields to use when computing a record’s id. The default value is ['id']. For more information: https://www.houdinigraphql.com/guides/caching-data#custom-ids
78
82
  */
@@ -95,7 +99,7 @@ export type ConfigFile = {
95
99
  */
96
100
  watchSchema?: WatchSchemaConfig;
97
101
  /**
98
- * Specifies the the persisted queries path and file. (default: `./$houdini/persisted_queries.json`)
102
+ * Specifies the the persisted queries path and file. (default: `<rootDir>/persisted_queries.json`)
99
103
  */
100
104
  persistedQueriesPath?: string;
101
105
  /**
@@ -107,6 +111,11 @@ export type ConfigFile = {
107
111
  * @default process.cwd()
108
112
  */
109
113
  projectDir?: string;
114
+ /**
115
+ * The relative path from your project directory pointing to your output directory.
116
+ * @default `$houdini`
117
+ */
118
+ runtimeDir?: string;
110
119
  /**
111
120
  * For now, the cache's imperative API is considered unstable. In order to suppress the warning,
112
121
  * you must enable this flag.
@@ -7,6 +7,12 @@ export declare const CachePolicy: {
7
7
  readonly NoCache: "NoCache";
8
8
  };
9
9
  export type CachePolicies = ValuesOf<typeof CachePolicy>;
10
+ export declare const DedupeMatchMode: {
11
+ readonly Variables: "Variables";
12
+ readonly Operation: "Operation";
13
+ readonly None: "None";
14
+ };
15
+ export type DedupeMatchModes = ValuesOf<typeof DedupeMatchMode>;
10
16
  export declare const PaginateMode: {
11
17
  readonly Infinite: "Infinite";
12
18
  readonly SinglePage: "SinglePage";
@@ -60,11 +66,17 @@ export type QueryArtifact = BaseCompiledDocument<'HoudiniQuery'> & {
60
66
  policy?: CachePolicies;
61
67
  partial?: boolean;
62
68
  enableLoadingState?: 'global' | 'local';
63
- dedupe?: 'first' | 'last';
69
+ dedupe?: {
70
+ cancel: 'first' | 'last';
71
+ match: DedupeMatchModes;
72
+ };
64
73
  };
65
74
  export type MutationArtifact = BaseCompiledDocument<'HoudiniMutation'> & {
66
75
  optimisticKeys?: boolean;
67
- dedupe?: 'first' | 'last';
76
+ dedupe?: {
77
+ cancel: 'first' | 'last';
78
+ match: DedupeMatchModes;
79
+ };
68
80
  };
69
81
  export type FragmentArtifact = BaseCompiledDocument<'HoudiniFragment'> & {
70
82
  enableLoadingState?: 'global' | 'local';
@@ -26,6 +26,7 @@ __export(types_exports, {
26
26
  CompiledQueryKind: () => CompiledQueryKind,
27
27
  CompiledSubscriptionKind: () => CompiledSubscriptionKind,
28
28
  DataSource: () => DataSource,
29
+ DedupeMatchMode: () => DedupeMatchMode,
29
30
  PaginateMode: () => PaginateMode,
30
31
  PendingValue: () => PendingValue,
31
32
  RefetchUpdateMode: () => RefetchUpdateMode,
@@ -41,6 +42,11 @@ const CachePolicy = {
41
42
  CacheAndNetwork: "CacheAndNetwork",
42
43
  NoCache: "NoCache"
43
44
  };
45
+ const DedupeMatchMode = {
46
+ Variables: "Variables",
47
+ Operation: "Operation",
48
+ None: "None"
49
+ };
44
50
  const PaginateMode = {
45
51
  Infinite: "Infinite",
46
52
  SinglePage: "SinglePage"
@@ -79,6 +85,7 @@ function isPending(value) {
79
85
  CompiledQueryKind,
80
86
  CompiledSubscriptionKind,
81
87
  DataSource,
88
+ DedupeMatchMode,
82
89
  PaginateMode,
83
90
  PendingValue,
84
91
  RefetchUpdateMode,
@@ -141,7 +141,7 @@ function exec(match, params) {
141
141
  if (param.rest)
142
142
  result[param.name] = "";
143
143
  } else {
144
- result[param.name] = value;
144
+ result[param.name] = decodeURIComponent(value);
145
145
  }
146
146
  }
147
147
  if (buffered)
@@ -35,7 +35,7 @@ export declare class Cache {
35
35
  };
36
36
  subscribe(spec: SubscriptionSpec, variables?: {}): void;
37
37
  unsubscribe(spec: SubscriptionSpec, variables?: {}): void;
38
- list(name: string, parentID?: string, allLists?: boolean): ListCollection;
38
+ list(name: string, parentID?: string, allLists?: boolean, skipMatches?: Set<string>): ListCollection;
39
39
  registerKeyMap(source: string, mapped: string): void;
40
40
  delete(id: string, layer?: Layer): void;
41
41
  setConfig(config: ConfigFile): void;