houdini 1.3.1 → 1.4.0

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.
@@ -58181,6 +58181,7 @@ __export(lib_exports, {
58181
58181
  CompiledSubscriptionKind: () => CompiledSubscriptionKind,
58182
58182
  Config: () => Config,
58183
58183
  DataSource: () => DataSource,
58184
+ DedupeMatchMode: () => DedupeMatchMode,
58184
58185
  DocumentStore: () => DocumentStore,
58185
58186
  HoudiniClient: () => HoudiniClient,
58186
58187
  HoudiniError: () => HoudiniError,
@@ -61533,6 +61534,11 @@ var CachePolicy = {
61533
61534
  CacheAndNetwork: "CacheAndNetwork",
61534
61535
  NoCache: "NoCache"
61535
61536
  };
61537
+ var DedupeMatchMode = {
61538
+ Variables: "Variables",
61539
+ Operation: "Operation",
61540
+ None: "None"
61541
+ };
61536
61542
  var PaginateMode = {
61537
61543
  Infinite: "Infinite",
61538
61544
  SinglePage: "SinglePage"
@@ -66232,6 +66238,11 @@ var defaultFetch = (url, params) => {
66232
66238
  ...params?.headers
66233
66239
  }
66234
66240
  });
66241
+ if (!result.ok && result.headers.get("content-type") !== "application/json" && result.headers.get("content-type") !== "application/graphql+json") {
66242
+ throw new Error(
66243
+ `Failed to fetch: server returned invalid response with error ${result.status}: ${result.statusText}`
66244
+ );
66245
+ }
66235
66246
  return await result.json();
66236
66247
  };
66237
66248
  };
@@ -67052,7 +67063,8 @@ var DocumentStore = class extends Writable {
67052
67063
  pendingPromise = null;
67053
67064
  serverSideFallback;
67054
67065
  controllerKey(variables) {
67055
- return this.artifact.name;
67066
+ const usedVariables = "dedupe" in this.artifact && this.artifact.dedupe?.match !== DedupeMatchMode.Variables ? {} : variables;
67067
+ return `${this.artifact.name}@${stableStringify(usedVariables)}`;
67056
67068
  }
67057
67069
  constructor({
67058
67070
  artifact,
@@ -67114,16 +67126,20 @@ var DocumentStore = class extends Writable {
67114
67126
  silenceEcho = false,
67115
67127
  abortController = new AbortController()
67116
67128
  } = {}) {
67117
- if ("dedupe" in this.artifact) {
67118
- if (inflightRequests[this.controllerKey(variables)]) {
67119
- if (this.artifact.dedupe === "first") {
67120
- inflightRequests[this.controllerKey(variables)].abort();
67121
- inflightRequests[this.controllerKey(variables)] = abortController;
67129
+ if ("dedupe" in this.artifact && this.artifact.dedupe && this.artifact.dedupe.match !== "None") {
67130
+ const dedupeKey = this.controllerKey(variables);
67131
+ if (inflightRequests[dedupeKey]) {
67132
+ if (this.artifact.dedupe.cancel === "first") {
67133
+ inflightRequests[dedupeKey].controller.abort();
67134
+ inflightRequests[dedupeKey].controller = abortController;
67122
67135
  } else {
67123
67136
  abortController.abort();
67124
67137
  }
67125
67138
  } else {
67126
- inflightRequests[this.controllerKey(variables)] = abortController;
67139
+ inflightRequests[dedupeKey] = {
67140
+ variables,
67141
+ controller: abortController
67142
+ };
67127
67143
  }
67128
67144
  }
67129
67145
  let context = new ClientPluginContextWrapper({
@@ -67445,6 +67461,21 @@ function marshalVariables(ctx) {
67445
67461
  function variablesChanged(ctx) {
67446
67462
  return ctx.stuff.inputs?.changed;
67447
67463
  }
67464
+ function stableStringify(obj) {
67465
+ return JSON.stringify(sortObject(obj));
67466
+ }
67467
+ function sortObject(obj) {
67468
+ if (obj === null || typeof obj !== "object") {
67469
+ return obj;
67470
+ }
67471
+ if (Array.isArray(obj)) {
67472
+ return obj.map(sortObject);
67473
+ }
67474
+ return Object.keys(obj).sort().reduce((result, key) => {
67475
+ result[key] = sortObject(obj[key]);
67476
+ return result;
67477
+ }, {});
67478
+ }
67448
67479
 
67449
67480
  // src/runtime/client/plugins/injectedPlugins.ts
67450
67481
  var plugins = [];
@@ -67882,8 +67913,9 @@ var Config = class {
67882
67913
  localSchema;
67883
67914
  projectRoot;
67884
67915
  schema;
67916
+ runtimeDir;
67885
67917
  schemaPath;
67886
- persistedQueriesPath = "./$houdini/persisted_queries.json";
67918
+ persistedQueriesPath;
67887
67919
  exclude;
67888
67920
  scalars;
67889
67921
  module = "esm";
@@ -67924,6 +67956,7 @@ var Config = class {
67924
67956
  let {
67925
67957
  schema,
67926
67958
  schemaPath = "./schema.graphql",
67959
+ runtimeDir = "$houdini",
67927
67960
  exclude = [],
67928
67961
  module: module2 = "esm",
67929
67962
  scalars,
@@ -67962,6 +67995,7 @@ var Config = class {
67962
67995
  this.projectRoot = dirname(
67963
67996
  projectDir ? join2(process.cwd(), projectDir) : filepath
67964
67997
  );
67998
+ this.runtimeDir = runtimeDir;
67965
67999
  this.scalars = scalars;
67966
68000
  this.cacheBufferSize = cacheBufferSize;
67967
68001
  this.defaultCachePolicy = defaultCachePolicy;
@@ -67976,11 +68010,9 @@ var Config = class {
67976
68010
  this.schemaPollInterval = watchSchema?.interval === void 0 ? 2e3 : watchSchema.interval;
67977
68011
  this.schemaPollTimeout = watchSchema?.timeout ?? 3e4;
67978
68012
  this.schemaPollHeaders = watchSchema?.headers ?? {};
67979
- this.rootDir = join2(this.projectRoot, "$houdini");
68013
+ this.rootDir = join2(this.projectRoot, this.runtimeDir);
68014
+ this.persistedQueriesPath = persistedQueriesPath ?? join2(this.rootDir, "persisted_queries.json");
67980
68015
  this.#fragmentVariableMaps = {};
67981
- if (persistedQueriesPath) {
67982
- this.persistedQueriesPath = persistedQueriesPath;
67983
- }
67984
68016
  if (defaultKeys) {
67985
68017
  this.defaultKeys = defaultKeys;
67986
68018
  }
@@ -69820,6 +69852,7 @@ function extractAnonymousQuery(config, raw, expr, propName) {
69820
69852
  CompiledSubscriptionKind,
69821
69853
  Config,
69822
69854
  DataSource,
69855
+ DedupeMatchMode,
69823
69856
  DocumentStore,
69824
69857
  HoudiniClient,
69825
69858
  HoudiniError,
@@ -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"
@@ -66147,6 +66152,11 @@ var defaultFetch = (url, params) => {
66147
66152
  ...params?.headers
66148
66153
  }
66149
66154
  });
66155
+ if (!result.ok && result.headers.get("content-type") !== "application/json" && result.headers.get("content-type") !== "application/graphql+json") {
66156
+ throw new Error(
66157
+ `Failed to fetch: server returned invalid response with error ${result.status}: ${result.statusText}`
66158
+ );
66159
+ }
66150
66160
  return await result.json();
66151
66161
  };
66152
66162
  };
@@ -66967,7 +66977,8 @@ var DocumentStore = class extends Writable {
66967
66977
  pendingPromise = null;
66968
66978
  serverSideFallback;
66969
66979
  controllerKey(variables) {
66970
- return this.artifact.name;
66980
+ const usedVariables = "dedupe" in this.artifact && this.artifact.dedupe?.match !== DedupeMatchMode.Variables ? {} : variables;
66981
+ return `${this.artifact.name}@${stableStringify(usedVariables)}`;
66971
66982
  }
66972
66983
  constructor({
66973
66984
  artifact,
@@ -67029,16 +67040,20 @@ var DocumentStore = class extends Writable {
67029
67040
  silenceEcho = false,
67030
67041
  abortController = new AbortController()
67031
67042
  } = {}) {
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;
67043
+ if ("dedupe" in this.artifact && this.artifact.dedupe && this.artifact.dedupe.match !== "None") {
67044
+ const dedupeKey = this.controllerKey(variables);
67045
+ if (inflightRequests[dedupeKey]) {
67046
+ if (this.artifact.dedupe.cancel === "first") {
67047
+ inflightRequests[dedupeKey].controller.abort();
67048
+ inflightRequests[dedupeKey].controller = abortController;
67037
67049
  } else {
67038
67050
  abortController.abort();
67039
67051
  }
67040
67052
  } else {
67041
- inflightRequests[this.controllerKey(variables)] = abortController;
67053
+ inflightRequests[dedupeKey] = {
67054
+ variables,
67055
+ controller: abortController
67056
+ };
67042
67057
  }
67043
67058
  }
67044
67059
  let context = new ClientPluginContextWrapper({
@@ -67360,6 +67375,21 @@ function marshalVariables(ctx) {
67360
67375
  function variablesChanged(ctx) {
67361
67376
  return ctx.stuff.inputs?.changed;
67362
67377
  }
67378
+ function stableStringify(obj) {
67379
+ return JSON.stringify(sortObject(obj));
67380
+ }
67381
+ function sortObject(obj) {
67382
+ if (obj === null || typeof obj !== "object") {
67383
+ return obj;
67384
+ }
67385
+ if (Array.isArray(obj)) {
67386
+ return obj.map(sortObject);
67387
+ }
67388
+ return Object.keys(obj).sort().reduce((result, key) => {
67389
+ result[key] = sortObject(obj[key]);
67390
+ return result;
67391
+ }, {});
67392
+ }
67363
67393
 
67364
67394
  // src/runtime/client/plugins/injectedPlugins.ts
67365
67395
  var plugins = [];
@@ -67796,8 +67826,9 @@ var Config = class {
67796
67826
  localSchema;
67797
67827
  projectRoot;
67798
67828
  schema;
67829
+ runtimeDir;
67799
67830
  schemaPath;
67800
- persistedQueriesPath = "./$houdini/persisted_queries.json";
67831
+ persistedQueriesPath;
67801
67832
  exclude;
67802
67833
  scalars;
67803
67834
  module = "esm";
@@ -67838,6 +67869,7 @@ var Config = class {
67838
67869
  let {
67839
67870
  schema,
67840
67871
  schemaPath = "./schema.graphql",
67872
+ runtimeDir = "$houdini",
67841
67873
  exclude = [],
67842
67874
  module = "esm",
67843
67875
  scalars,
@@ -67876,6 +67908,7 @@ var Config = class {
67876
67908
  this.projectRoot = dirname(
67877
67909
  projectDir ? join2(process.cwd(), projectDir) : filepath
67878
67910
  );
67911
+ this.runtimeDir = runtimeDir;
67879
67912
  this.scalars = scalars;
67880
67913
  this.cacheBufferSize = cacheBufferSize;
67881
67914
  this.defaultCachePolicy = defaultCachePolicy;
@@ -67890,11 +67923,9 @@ var Config = class {
67890
67923
  this.schemaPollInterval = watchSchema?.interval === void 0 ? 2e3 : watchSchema.interval;
67891
67924
  this.schemaPollTimeout = watchSchema?.timeout ?? 3e4;
67892
67925
  this.schemaPollHeaders = watchSchema?.headers ?? {};
67893
- this.rootDir = join2(this.projectRoot, "$houdini");
67926
+ this.rootDir = join2(this.projectRoot, this.runtimeDir);
67927
+ this.persistedQueriesPath = persistedQueriesPath ?? join2(this.rootDir, "persisted_queries.json");
67894
67928
  this.#fragmentVariableMaps = {};
67895
- if (persistedQueriesPath) {
67896
- this.persistedQueriesPath = persistedQueriesPath;
67897
- }
67898
67929
  if (defaultKeys) {
67899
67930
  this.defaultKeys = defaultKeys;
67900
67931
  }
@@ -69733,6 +69764,7 @@ export {
69733
69764
  CompiledSubscriptionKind,
69734
69765
  Config,
69735
69766
  DataSource,
69767
+ DedupeMatchMode,
69736
69768
  DocumentStore,
69737
69769
  HoudiniClient,
69738
69770
  HoudiniError,
@@ -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';
@@ -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") !== "application/json" && result.headers.get("content-type") !== "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,
@@ -2,7 +2,7 @@ import { getCurrentConfig } from "../lib/config";
2
2
  import { deepEquals } from "../lib/deepEquals";
3
3
  import { marshalInputs } from "../lib/scalars";
4
4
  import { Writable } from "../lib/store";
5
- import { ArtifactKind } from "../lib/types";
5
+ import { ArtifactKind, DedupeMatchMode } from "../lib/types";
6
6
  import { cachePolicy } from "./plugins";
7
7
  const steps = {
8
8
  forward: ["start", "beforeNetwork", "network"],
@@ -19,7 +19,8 @@ class DocumentStore extends Writable {
19
19
  pendingPromise = null;
20
20
  serverSideFallback;
21
21
  controllerKey(variables) {
22
- return this.artifact.name;
22
+ const usedVariables = "dedupe" in this.artifact && this.artifact.dedupe?.match !== DedupeMatchMode.Variables ? {} : variables;
23
+ return `${this.artifact.name}@${stableStringify(usedVariables)}`;
23
24
  }
24
25
  constructor({
25
26
  artifact,
@@ -81,16 +82,20 @@ class DocumentStore extends Writable {
81
82
  silenceEcho = false,
82
83
  abortController = new AbortController()
83
84
  } = {}) {
84
- if ("dedupe" in this.artifact) {
85
- if (inflightRequests[this.controllerKey(variables)]) {
86
- if (this.artifact.dedupe === "first") {
87
- inflightRequests[this.controllerKey(variables)].abort();
88
- inflightRequests[this.controllerKey(variables)] = abortController;
85
+ if ("dedupe" in this.artifact && this.artifact.dedupe && this.artifact.dedupe.match !== "None") {
86
+ const dedupeKey = this.controllerKey(variables);
87
+ if (inflightRequests[dedupeKey]) {
88
+ if (this.artifact.dedupe.cancel === "first") {
89
+ inflightRequests[dedupeKey].controller.abort();
90
+ inflightRequests[dedupeKey].controller = abortController;
89
91
  } else {
90
92
  abortController.abort();
91
93
  }
92
94
  } else {
93
- inflightRequests[this.controllerKey(variables)] = abortController;
95
+ inflightRequests[dedupeKey] = {
96
+ variables,
97
+ controller: abortController
98
+ };
94
99
  }
95
100
  }
96
101
  let context = new ClientPluginContextWrapper({
@@ -412,6 +417,21 @@ function marshalVariables(ctx) {
412
417
  function variablesChanged(ctx) {
413
418
  return ctx.stuff.inputs?.changed;
414
419
  }
420
+ function stableStringify(obj) {
421
+ return JSON.stringify(sortObject(obj));
422
+ }
423
+ function sortObject(obj) {
424
+ if (obj === null || typeof obj !== "object") {
425
+ return obj;
426
+ }
427
+ if (Array.isArray(obj)) {
428
+ return obj.map(sortObject);
429
+ }
430
+ return Object.keys(obj).sort().reduce((result, key) => {
431
+ result[key] = sortObject(obj[key]);
432
+ return result;
433
+ }, {});
434
+ }
415
435
  export {
416
436
  DocumentStore
417
437
  };
@@ -66,6 +66,11 @@ const defaultFetch = (url, params) => {
66
66
  ...params?.headers
67
67
  }
68
68
  });
69
+ if (!result.ok && result.headers.get("content-type") !== "application/json" && result.headers.get("content-type") !== "application/graphql+json") {
70
+ throw new Error(
71
+ `Failed to fetch: server returned invalid response with error ${result.status}: ${result.statusText}`
72
+ );
73
+ }
69
74
  return await result.json();
70
75
  };
71
76
  };
@@ -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';
@@ -5,6 +5,11 @@ const CachePolicy = {
5
5
  CacheAndNetwork: "CacheAndNetwork",
6
6
  NoCache: "NoCache"
7
7
  };
8
+ const DedupeMatchMode = {
9
+ Variables: "Variables",
10
+ Operation: "Operation",
11
+ None: "None"
12
+ };
8
13
  const PaginateMode = {
9
14
  Infinite: "Infinite",
10
15
  SinglePage: "SinglePage"
@@ -43,6 +48,7 @@ export {
43
48
  CompiledQueryKind,
44
49
  CompiledSubscriptionKind,
45
50
  DataSource,
51
+ DedupeMatchMode,
46
52
  PaginateMode,
47
53
  PendingValue,
48
54
  RefetchUpdateMode,