houdini 1.2.61 → 1.2.63

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.
@@ -67974,6 +67974,9 @@ var Config = class {
67974
67974
  get listDirective() {
67975
67975
  return "list";
67976
67976
  }
67977
+ get dedupeDirective() {
67978
+ return "dedupe";
67979
+ }
67977
67980
  get optimisticKeyDirective() {
67978
67981
  return "optimisticKey";
67979
67982
  }
@@ -70654,7 +70657,17 @@ async function paginate(config, documents) {
70654
70657
  );
70655
70658
  return {
70656
70659
  ...node,
70657
- variableDefinitions: finalVariables
70660
+ variableDefinitions: finalVariables,
70661
+ directives: [
70662
+ ...node.directives || [],
70663
+ {
70664
+ kind: graphql13.Kind.DIRECTIVE,
70665
+ name: {
70666
+ kind: graphql13.Kind.NAME,
70667
+ value: config.dedupeDirective
70668
+ }
70669
+ }
70670
+ ]
70658
70671
  };
70659
70672
  },
70660
70673
  FragmentDefinition(node) {
@@ -71977,6 +71990,7 @@ function artifactGenerator(stats) {
71977
71990
  let rootType = "";
71978
71991
  let selectionSet;
71979
71992
  let originalSelectionSet = null;
71993
+ let dedupe;
71980
71994
  const fragmentDefinitions = doc.document.definitions.filter(
71981
71995
  (definition) => definition.kind === "FragmentDefinition"
71982
71996
  ).reduce(
@@ -72001,6 +72015,15 @@ function artifactGenerator(stats) {
72001
72015
  message: "could not find root type for operation: " + operation.operation + ". Maybe you need to re-run the introspection query?"
72002
72016
  });
72003
72017
  }
72018
+ const dedupeDirective = operation.directives?.find(
72019
+ (directive2) => directive2.name.value === config.dedupeDirective
72020
+ );
72021
+ if (dedupeDirective) {
72022
+ const cancelFirstArg = dedupeDirective.arguments?.find(
72023
+ (arg) => arg.name.value === "cancelFirst"
72024
+ );
72025
+ dedupe = cancelFirstArg && cancelFirstArg.value.kind === "BooleanValue" && cancelFirstArg.value ? "first" : "last";
72026
+ }
72004
72027
  selectionSet = operation.selectionSet;
72005
72028
  if (originalParsed.definitions[0].kind === "OperationDefinition") {
72006
72029
  originalSelectionSet = originalParsed.definitions[0].selectionSet;
@@ -72078,6 +72101,9 @@ function artifactGenerator(stats) {
72078
72101
  };
72079
72102
  const hash_value = hashPluginBaseRaw({ config, document: { ...doc, artifact } });
72080
72103
  artifact.hash = hash_value;
72104
+ if (artifact.kind === "HoudiniQuery" || artifact.kind === "HoudiniMutation" && dedupe) {
72105
+ artifact.dedupe = dedupe;
72106
+ }
72081
72107
  applyMask(
72082
72108
  config,
72083
72109
  artifact.selection,
@@ -75663,6 +75689,12 @@ directive @${config.paginateDirective}(${config.listOrPaginateNameArg}: String,
75663
75689
  """
75664
75690
  directive @${config.listPrependDirective} on FRAGMENT_SPREAD
75665
75691
 
75692
+ """
75693
+ @${config.dedupeDirective} is used to prevent an operation from running more than once at the same time.
75694
+ If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
75695
+ """
75696
+ directive @${config.dedupeDirective}(cancelFirst: Boolean) on QUERY | MUTATION
75697
+
75666
75698
  """
75667
75699
  @${config.optimisticKeyDirective} is used to identify a field as an optimistic key
75668
75700
  """
@@ -78412,12 +78444,12 @@ async function packageJSON(targetPath, frameworkInfo) {
78412
78444
  }
78413
78445
  packageJSON2.devDependencies = {
78414
78446
  ...packageJSON2.devDependencies,
78415
- houdini: "^1.2.61"
78447
+ houdini: "^1.2.63"
78416
78448
  };
78417
78449
  if (frameworkInfo.framework === "svelte" || frameworkInfo.framework === "kit") {
78418
78450
  packageJSON2.devDependencies = {
78419
78451
  ...packageJSON2.devDependencies,
78420
- "houdini-svelte": "^1.2.61"
78452
+ "houdini-svelte": "^1.2.63"
78421
78453
  };
78422
78454
  } else {
78423
78455
  throw new Error(`Unmanaged framework: "${JSON.stringify(frameworkInfo)}"`);
@@ -67979,6 +67979,9 @@ var Config = class {
67979
67979
  get listDirective() {
67980
67980
  return "list";
67981
67981
  }
67982
+ get dedupeDirective() {
67983
+ return "dedupe";
67984
+ }
67982
67985
  get optimisticKeyDirective() {
67983
67986
  return "optimisticKey";
67984
67987
  }
@@ -70659,7 +70662,17 @@ async function paginate(config, documents) {
70659
70662
  );
70660
70663
  return {
70661
70664
  ...node,
70662
- variableDefinitions: finalVariables
70665
+ variableDefinitions: finalVariables,
70666
+ directives: [
70667
+ ...node.directives || [],
70668
+ {
70669
+ kind: graphql13.Kind.DIRECTIVE,
70670
+ name: {
70671
+ kind: graphql13.Kind.NAME,
70672
+ value: config.dedupeDirective
70673
+ }
70674
+ }
70675
+ ]
70663
70676
  };
70664
70677
  },
70665
70678
  FragmentDefinition(node) {
@@ -71982,6 +71995,7 @@ function artifactGenerator(stats) {
71982
71995
  let rootType = "";
71983
71996
  let selectionSet;
71984
71997
  let originalSelectionSet = null;
71998
+ let dedupe;
71985
71999
  const fragmentDefinitions = doc.document.definitions.filter(
71986
72000
  (definition) => definition.kind === "FragmentDefinition"
71987
72001
  ).reduce(
@@ -72006,6 +72020,15 @@ function artifactGenerator(stats) {
72006
72020
  message: "could not find root type for operation: " + operation.operation + ". Maybe you need to re-run the introspection query?"
72007
72021
  });
72008
72022
  }
72023
+ const dedupeDirective = operation.directives?.find(
72024
+ (directive2) => directive2.name.value === config.dedupeDirective
72025
+ );
72026
+ if (dedupeDirective) {
72027
+ const cancelFirstArg = dedupeDirective.arguments?.find(
72028
+ (arg) => arg.name.value === "cancelFirst"
72029
+ );
72030
+ dedupe = cancelFirstArg && cancelFirstArg.value.kind === "BooleanValue" && cancelFirstArg.value ? "first" : "last";
72031
+ }
72009
72032
  selectionSet = operation.selectionSet;
72010
72033
  if (originalParsed.definitions[0].kind === "OperationDefinition") {
72011
72034
  originalSelectionSet = originalParsed.definitions[0].selectionSet;
@@ -72083,6 +72106,9 @@ function artifactGenerator(stats) {
72083
72106
  };
72084
72107
  const hash_value = hashPluginBaseRaw({ config, document: { ...doc, artifact } });
72085
72108
  artifact.hash = hash_value;
72109
+ if (artifact.kind === "HoudiniQuery" || artifact.kind === "HoudiniMutation" && dedupe) {
72110
+ artifact.dedupe = dedupe;
72111
+ }
72086
72112
  applyMask(
72087
72113
  config,
72088
72114
  artifact.selection,
@@ -75668,6 +75694,12 @@ directive @${config.paginateDirective}(${config.listOrPaginateNameArg}: String,
75668
75694
  """
75669
75695
  directive @${config.listPrependDirective} on FRAGMENT_SPREAD
75670
75696
 
75697
+ """
75698
+ @${config.dedupeDirective} is used to prevent an operation from running more than once at the same time.
75699
+ If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
75700
+ """
75701
+ directive @${config.dedupeDirective}(cancelFirst: Boolean) on QUERY | MUTATION
75702
+
75671
75703
  """
75672
75704
  @${config.optimisticKeyDirective} is used to identify a field as an optimistic key
75673
75705
  """
@@ -78417,12 +78449,12 @@ async function packageJSON(targetPath, frameworkInfo) {
78417
78449
  }
78418
78450
  packageJSON2.devDependencies = {
78419
78451
  ...packageJSON2.devDependencies,
78420
- houdini: "^1.2.61"
78452
+ houdini: "^1.2.63"
78421
78453
  };
78422
78454
  if (frameworkInfo.framework === "svelte" || frameworkInfo.framework === "kit") {
78423
78455
  packageJSON2.devDependencies = {
78424
78456
  ...packageJSON2.devDependencies,
78425
- "houdini-svelte": "^1.2.61"
78457
+ "houdini-svelte": "^1.2.63"
78426
78458
  };
78427
78459
  } else {
78428
78460
  throw new Error(`Unmanaged framework: "${JSON.stringify(frameworkInfo)}"`);
@@ -58855,7 +58855,17 @@ async function paginate(config, documents) {
58855
58855
  );
58856
58856
  return {
58857
58857
  ...node,
58858
- variableDefinitions: finalVariables
58858
+ variableDefinitions: finalVariables,
58859
+ directives: [
58860
+ ...node.directives || [],
58861
+ {
58862
+ kind: graphql13.Kind.DIRECTIVE,
58863
+ name: {
58864
+ kind: graphql13.Kind.NAME,
58865
+ value: config.dedupeDirective
58866
+ }
58867
+ }
58868
+ ]
58859
58869
  };
58860
58870
  },
58861
58871
  FragmentDefinition(node) {
@@ -60178,6 +60188,7 @@ function artifactGenerator(stats) {
60178
60188
  let rootType = "";
60179
60189
  let selectionSet;
60180
60190
  let originalSelectionSet = null;
60191
+ let dedupe;
60181
60192
  const fragmentDefinitions = doc.document.definitions.filter(
60182
60193
  (definition) => definition.kind === "FragmentDefinition"
60183
60194
  ).reduce(
@@ -60202,6 +60213,15 @@ function artifactGenerator(stats) {
60202
60213
  message: "could not find root type for operation: " + operation.operation + ". Maybe you need to re-run the introspection query?"
60203
60214
  });
60204
60215
  }
60216
+ const dedupeDirective = operation.directives?.find(
60217
+ (directive2) => directive2.name.value === config.dedupeDirective
60218
+ );
60219
+ if (dedupeDirective) {
60220
+ const cancelFirstArg = dedupeDirective.arguments?.find(
60221
+ (arg) => arg.name.value === "cancelFirst"
60222
+ );
60223
+ dedupe = cancelFirstArg && cancelFirstArg.value.kind === "BooleanValue" && cancelFirstArg.value ? "first" : "last";
60224
+ }
60205
60225
  selectionSet = operation.selectionSet;
60206
60226
  if (originalParsed.definitions[0].kind === "OperationDefinition") {
60207
60227
  originalSelectionSet = originalParsed.definitions[0].selectionSet;
@@ -60279,6 +60299,9 @@ function artifactGenerator(stats) {
60279
60299
  };
60280
60300
  const hash_value = hashPluginBaseRaw({ config, document: { ...doc, artifact } });
60281
60301
  artifact.hash = hash_value;
60302
+ if (artifact.kind === "HoudiniQuery" || artifact.kind === "HoudiniMutation" && dedupe) {
60303
+ artifact.dedupe = dedupe;
60304
+ }
60282
60305
  applyMask(
60283
60306
  config,
60284
60307
  artifact.selection,
@@ -63864,6 +63887,12 @@ directive @${config.paginateDirective}(${config.listOrPaginateNameArg}: String,
63864
63887
  """
63865
63888
  directive @${config.listPrependDirective} on FRAGMENT_SPREAD
63866
63889
 
63890
+ """
63891
+ @${config.dedupeDirective} is used to prevent an operation from running more than once at the same time.
63892
+ If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
63893
+ """
63894
+ directive @${config.dedupeDirective}(cancelFirst: Boolean) on QUERY | MUTATION
63895
+
63867
63896
  """
63868
63897
  @${config.optimisticKeyDirective} is used to identify a field as an optimistic key
63869
63898
  """
@@ -58854,7 +58854,17 @@ async function paginate(config, documents) {
58854
58854
  );
58855
58855
  return {
58856
58856
  ...node,
58857
- variableDefinitions: finalVariables
58857
+ variableDefinitions: finalVariables,
58858
+ directives: [
58859
+ ...node.directives || [],
58860
+ {
58861
+ kind: graphql13.Kind.DIRECTIVE,
58862
+ name: {
58863
+ kind: graphql13.Kind.NAME,
58864
+ value: config.dedupeDirective
58865
+ }
58866
+ }
58867
+ ]
58858
58868
  };
58859
58869
  },
58860
58870
  FragmentDefinition(node) {
@@ -60177,6 +60187,7 @@ function artifactGenerator(stats) {
60177
60187
  let rootType = "";
60178
60188
  let selectionSet;
60179
60189
  let originalSelectionSet = null;
60190
+ let dedupe;
60180
60191
  const fragmentDefinitions = doc.document.definitions.filter(
60181
60192
  (definition) => definition.kind === "FragmentDefinition"
60182
60193
  ).reduce(
@@ -60201,6 +60212,15 @@ function artifactGenerator(stats) {
60201
60212
  message: "could not find root type for operation: " + operation.operation + ". Maybe you need to re-run the introspection query?"
60202
60213
  });
60203
60214
  }
60215
+ const dedupeDirective = operation.directives?.find(
60216
+ (directive2) => directive2.name.value === config.dedupeDirective
60217
+ );
60218
+ if (dedupeDirective) {
60219
+ const cancelFirstArg = dedupeDirective.arguments?.find(
60220
+ (arg) => arg.name.value === "cancelFirst"
60221
+ );
60222
+ dedupe = cancelFirstArg && cancelFirstArg.value.kind === "BooleanValue" && cancelFirstArg.value ? "first" : "last";
60223
+ }
60204
60224
  selectionSet = operation.selectionSet;
60205
60225
  if (originalParsed.definitions[0].kind === "OperationDefinition") {
60206
60226
  originalSelectionSet = originalParsed.definitions[0].selectionSet;
@@ -60278,6 +60298,9 @@ function artifactGenerator(stats) {
60278
60298
  };
60279
60299
  const hash_value = hashPluginBaseRaw({ config, document: { ...doc, artifact } });
60280
60300
  artifact.hash = hash_value;
60301
+ if (artifact.kind === "HoudiniQuery" || artifact.kind === "HoudiniMutation" && dedupe) {
60302
+ artifact.dedupe = dedupe;
60303
+ }
60281
60304
  applyMask(
60282
60305
  config,
60283
60306
  artifact.selection,
@@ -63863,6 +63886,12 @@ directive @${config.paginateDirective}(${config.listOrPaginateNameArg}: String,
63863
63886
  """
63864
63887
  directive @${config.listPrependDirective} on FRAGMENT_SPREAD
63865
63888
 
63889
+ """
63890
+ @${config.dedupeDirective} is used to prevent an operation from running more than once at the same time.
63891
+ If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
63892
+ """
63893
+ directive @${config.dedupeDirective}(cancelFirst: Boolean) on QUERY | MUTATION
63894
+
63866
63895
  """
63867
63896
  @${config.optimisticKeyDirective} is used to identify a field as an optimistic key
63868
63897
  """
@@ -102,6 +102,7 @@ export declare class Config {
102
102
  get maskEnableDirective(): string;
103
103
  get maskDisableDirective(): string;
104
104
  get listDirective(): string;
105
+ get dedupeDirective(): string;
105
106
  get optimisticKeyDirective(): string;
106
107
  get listPrependDirective(): string;
107
108
  get listAppendDirective(): string;
@@ -66193,7 +66193,10 @@ var fetch2 = (target) => {
66193
66193
  const result = await fetchFn({
66194
66194
  fetch: (url, args) => {
66195
66195
  const newArgs = handleMultipart(fetchParams2, args) ?? args;
66196
- return fetch3(url, newArgs);
66196
+ return fetch3(url, {
66197
+ ...newArgs,
66198
+ signal: ctx.abortController.signal
66199
+ });
66197
66200
  },
66198
66201
  metadata: ctx.metadata,
66199
66202
  session: ctx.session || {},
@@ -67038,6 +67041,7 @@ var steps = {
67038
67041
  forward: ["start", "beforeNetwork", "network"],
67039
67042
  backwards: ["end", "afterNetwork"]
67040
67043
  };
67044
+ var inflightRequests = {};
67041
67045
  var DocumentStore = class extends Writable {
67042
67046
  artifact;
67043
67047
  #client;
@@ -67047,6 +67051,9 @@ var DocumentStore = class extends Writable {
67047
67051
  #lastContext = null;
67048
67052
  pendingPromise = null;
67049
67053
  serverSideFallback;
67054
+ controllerKey(variables) {
67055
+ return this.artifact.name;
67056
+ }
67050
67057
  constructor({
67051
67058
  artifact,
67052
67059
  plugins: plugins2,
@@ -67104,9 +67111,23 @@ var DocumentStore = class extends Writable {
67104
67111
  stuff,
67105
67112
  cacheParams,
67106
67113
  setup = false,
67107
- silenceEcho = false
67114
+ silenceEcho = false,
67115
+ abortController = new AbortController()
67108
67116
  } = {}) {
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;
67122
+ } else {
67123
+ abortController.abort();
67124
+ }
67125
+ } else {
67126
+ inflightRequests[this.controllerKey(variables)] = abortController;
67127
+ }
67128
+ }
67109
67129
  let context = new ClientPluginContextWrapper({
67130
+ abortController,
67110
67131
  config: this.#configFile,
67111
67132
  name: this.artifact.name,
67112
67133
  text: this.artifact.raw,
@@ -67150,7 +67171,9 @@ var DocumentStore = class extends Writable {
67150
67171
  }
67151
67172
  this.#step("forward", state);
67152
67173
  });
67153
- return await promise;
67174
+ const response = await promise;
67175
+ delete inflightRequests[this.controllerKey(variables)];
67176
+ return response;
67154
67177
  }
67155
67178
  async cleanup() {
67156
67179
  for (const plugin2 of this.#plugins) {
@@ -67259,6 +67282,11 @@ var DocumentStore = class extends Writable {
67259
67282
  };
67260
67283
  }
67261
67284
  try {
67285
+ if (draft.abortController.signal.aborted) {
67286
+ const abortError = new Error("aborted");
67287
+ abortError.name = "AbortError";
67288
+ throw abortError;
67289
+ }
67262
67290
  const result = target(draft, handlers);
67263
67291
  result?.catch((err) => {
67264
67292
  this.#step("error", { ...ctx, index: index - 1 }, err);
@@ -68241,6 +68269,9 @@ var Config = class {
68241
68269
  get listDirective() {
68242
68270
  return "list";
68243
68271
  }
68272
+ get dedupeDirective() {
68273
+ return "dedupe";
68274
+ }
68244
68275
  get optimisticKeyDirective() {
68245
68276
  return "optimisticKey";
68246
68277
  }
@@ -66108,7 +66108,10 @@ var fetch2 = (target) => {
66108
66108
  const result = await fetchFn({
66109
66109
  fetch: (url, args) => {
66110
66110
  const newArgs = handleMultipart(fetchParams2, args) ?? args;
66111
- return fetch3(url, newArgs);
66111
+ return fetch3(url, {
66112
+ ...newArgs,
66113
+ signal: ctx.abortController.signal
66114
+ });
66112
66115
  },
66113
66116
  metadata: ctx.metadata,
66114
66117
  session: ctx.session || {},
@@ -66953,6 +66956,7 @@ var steps = {
66953
66956
  forward: ["start", "beforeNetwork", "network"],
66954
66957
  backwards: ["end", "afterNetwork"]
66955
66958
  };
66959
+ var inflightRequests = {};
66956
66960
  var DocumentStore = class extends Writable {
66957
66961
  artifact;
66958
66962
  #client;
@@ -66962,6 +66966,9 @@ var DocumentStore = class extends Writable {
66962
66966
  #lastContext = null;
66963
66967
  pendingPromise = null;
66964
66968
  serverSideFallback;
66969
+ controllerKey(variables) {
66970
+ return this.artifact.name;
66971
+ }
66965
66972
  constructor({
66966
66973
  artifact,
66967
66974
  plugins: plugins2,
@@ -67019,9 +67026,23 @@ var DocumentStore = class extends Writable {
67019
67026
  stuff,
67020
67027
  cacheParams,
67021
67028
  setup = false,
67022
- silenceEcho = false
67029
+ silenceEcho = false,
67030
+ abortController = new AbortController()
67023
67031
  } = {}) {
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;
67037
+ } else {
67038
+ abortController.abort();
67039
+ }
67040
+ } else {
67041
+ inflightRequests[this.controllerKey(variables)] = abortController;
67042
+ }
67043
+ }
67024
67044
  let context = new ClientPluginContextWrapper({
67045
+ abortController,
67025
67046
  config: this.#configFile,
67026
67047
  name: this.artifact.name,
67027
67048
  text: this.artifact.raw,
@@ -67065,7 +67086,9 @@ var DocumentStore = class extends Writable {
67065
67086
  }
67066
67087
  this.#step("forward", state);
67067
67088
  });
67068
- return await promise;
67089
+ const response = await promise;
67090
+ delete inflightRequests[this.controllerKey(variables)];
67091
+ return response;
67069
67092
  }
67070
67093
  async cleanup() {
67071
67094
  for (const plugin2 of this.#plugins) {
@@ -67174,6 +67197,11 @@ var DocumentStore = class extends Writable {
67174
67197
  };
67175
67198
  }
67176
67199
  try {
67200
+ if (draft.abortController.signal.aborted) {
67201
+ const abortError = new Error("aborted");
67202
+ abortError.name = "AbortError";
67203
+ throw abortError;
67204
+ }
67177
67205
  const result = target(draft, handlers);
67178
67206
  result?.catch((err) => {
67179
67207
  this.#step("error", { ...ctx, index: index - 1 }, err);
@@ -68155,6 +68183,9 @@ var Config = class {
68155
68183
  get listDirective() {
68156
68184
  return "list";
68157
68185
  }
68186
+ get dedupeDirective() {
68187
+ return "dedupe";
68188
+ }
68158
68189
  get optimisticKeyDirective() {
68159
68190
  return "optimisticKey";
68160
68191
  }
@@ -11,6 +11,7 @@ export declare class DocumentStore<_Data extends GraphQLObject, _Input extends G
11
11
  then: (val: any) => void;
12
12
  } | null;
13
13
  serverSideFallback?: boolean;
14
+ controllerKey(variables: any): string;
14
15
  constructor({ artifact, plugins, pipeline, client, cache, enableCache, initialValue, initialVariables, fetching, }: {
15
16
  artifact: DocumentArtifact;
16
17
  plugins?: ClientHooks[];
@@ -23,7 +24,7 @@ export declare class DocumentStore<_Data extends GraphQLObject, _Input extends G
23
24
  serverSideFallback?: boolean;
24
25
  initialVariables?: _Input;
25
26
  });
26
- send({ metadata, session, fetch, variables, policy, stuff, cacheParams, setup, silenceEcho, }?: SendParams): Promise<QueryResult<_Data, _Input>>;
27
+ send({ metadata, session, fetch, variables, policy, stuff, cacheParams, setup, silenceEcho, abortController, }?: SendParams): Promise<QueryResult<_Data, _Input>>;
27
28
  cleanup(): Promise<void>;
28
29
  getFetch(getSession: () => App.Session | null | undefined): (input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>;
29
30
  }
@@ -51,6 +52,7 @@ export type ClientPluginContext = {
51
52
  metadata?: App.Metadata | null;
52
53
  session?: App.Session | null;
53
54
  fetchParams?: RequestInit;
55
+ abortController: AbortController;
54
56
  cacheParams?: {
55
57
  layer?: Layer;
56
58
  notifySubscribers?: SubscriptionSpec[];
@@ -100,5 +102,6 @@ export type SendParams = {
100
102
  cacheParams?: ClientPluginContext['cacheParams'];
101
103
  setup?: boolean;
102
104
  silenceEcho?: boolean;
105
+ abortController?: AbortController;
103
106
  };
104
107
  export {};
@@ -60,9 +60,11 @@ export type QueryArtifact = BaseCompiledDocument<'HoudiniQuery'> & {
60
60
  policy?: CachePolicies;
61
61
  partial?: boolean;
62
62
  enableLoadingState?: 'global' | 'local';
63
+ dedupe?: 'first' | 'last';
63
64
  };
64
65
  export type MutationArtifact = BaseCompiledDocument<'HoudiniMutation'> & {
65
66
  optimisticKeys?: boolean;
67
+ dedupe?: 'first' | 'last';
66
68
  };
67
69
  export type FragmentArtifact = BaseCompiledDocument<'HoudiniFragment'> & {
68
70
  enableLoadingState?: 'global' | 'local';
@@ -270,13 +272,13 @@ export type CursorHandlers<_Data extends GraphQLObject, _Input> = {
270
272
  after?: string;
271
273
  fetch?: typeof globalThis.fetch;
272
274
  metadata?: {};
273
- }) => Promise<void>;
275
+ }) => Promise<QueryResult<_Data, _Input>>;
274
276
  loadPreviousPage: (args?: {
275
277
  last?: number;
276
278
  before?: string;
277
279
  fetch?: typeof globalThis.fetch;
278
280
  metadata?: {};
279
- }) => Promise<void>;
281
+ }) => Promise<QueryResult<_Data, _Input>>;
280
282
  fetch(args?: FetchParams<_Input> | undefined): Promise<QueryResult<_Data, _Input>>;
281
283
  };
282
284
  export type OffsetHandlers<_Data extends GraphQLObject, _Input> = {
@@ -11,6 +11,7 @@ export declare class DocumentStore<_Data extends GraphQLObject, _Input extends G
11
11
  then: (val: any) => void;
12
12
  } | null;
13
13
  serverSideFallback?: boolean;
14
+ controllerKey(variables: any): string;
14
15
  constructor({ artifact, plugins, pipeline, client, cache, enableCache, initialValue, initialVariables, fetching, }: {
15
16
  artifact: DocumentArtifact;
16
17
  plugins?: ClientHooks[];
@@ -23,7 +24,7 @@ export declare class DocumentStore<_Data extends GraphQLObject, _Input extends G
23
24
  serverSideFallback?: boolean;
24
25
  initialVariables?: _Input;
25
26
  });
26
- send({ metadata, session, fetch, variables, policy, stuff, cacheParams, setup, silenceEcho, }?: SendParams): Promise<QueryResult<_Data, _Input>>;
27
+ send({ metadata, session, fetch, variables, policy, stuff, cacheParams, setup, silenceEcho, abortController, }?: SendParams): Promise<QueryResult<_Data, _Input>>;
27
28
  cleanup(): Promise<void>;
28
29
  getFetch(getSession: () => App.Session | null | undefined): (input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>;
29
30
  }
@@ -51,6 +52,7 @@ export type ClientPluginContext = {
51
52
  metadata?: App.Metadata | null;
52
53
  session?: App.Session | null;
53
54
  fetchParams?: RequestInit;
55
+ abortController: AbortController;
54
56
  cacheParams?: {
55
57
  layer?: Layer;
56
58
  notifySubscribers?: SubscriptionSpec[];
@@ -100,5 +102,6 @@ export type SendParams = {
100
102
  cacheParams?: ClientPluginContext['cacheParams'];
101
103
  setup?: boolean;
102
104
  silenceEcho?: boolean;
105
+ abortController?: AbortController;
103
106
  };
104
107
  export {};
@@ -31,6 +31,7 @@ const steps = {
31
31
  forward: ["start", "beforeNetwork", "network"],
32
32
  backwards: ["end", "afterNetwork"]
33
33
  };
34
+ let inflightRequests = {};
34
35
  class DocumentStore extends import_store.Writable {
35
36
  artifact;
36
37
  #client;
@@ -40,6 +41,9 @@ class DocumentStore extends import_store.Writable {
40
41
  #lastContext = null;
41
42
  pendingPromise = null;
42
43
  serverSideFallback;
44
+ controllerKey(variables) {
45
+ return this.artifact.name;
46
+ }
43
47
  constructor({
44
48
  artifact,
45
49
  plugins,
@@ -97,9 +101,23 @@ class DocumentStore extends import_store.Writable {
97
101
  stuff,
98
102
  cacheParams,
99
103
  setup = false,
100
- silenceEcho = false
104
+ silenceEcho = false,
105
+ abortController = new AbortController()
101
106
  } = {}) {
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;
112
+ } else {
113
+ abortController.abort();
114
+ }
115
+ } else {
116
+ inflightRequests[this.controllerKey(variables)] = abortController;
117
+ }
118
+ }
102
119
  let context = new ClientPluginContextWrapper({
120
+ abortController,
103
121
  config: this.#configFile,
104
122
  name: this.artifact.name,
105
123
  text: this.artifact.raw,
@@ -143,7 +161,9 @@ class DocumentStore extends import_store.Writable {
143
161
  }
144
162
  this.#step("forward", state);
145
163
  });
146
- return await promise;
164
+ const response = await promise;
165
+ delete inflightRequests[this.controllerKey(variables)];
166
+ return response;
147
167
  }
148
168
  async cleanup() {
149
169
  for (const plugin of this.#plugins) {
@@ -252,6 +272,11 @@ class DocumentStore extends import_store.Writable {
252
272
  };
253
273
  }
254
274
  try {
275
+ if (draft.abortController.signal.aborted) {
276
+ const abortError = new Error("aborted");
277
+ abortError.name = "AbortError";
278
+ throw abortError;
279
+ }
255
280
  const result = target(draft, handlers);
256
281
  result?.catch((err) => {
257
282
  this.#step("error", { ...ctx, index: index - 1 }, err);
@@ -52,7 +52,10 @@ const fetch = (target) => {
52
52
  const result = await fetchFn({
53
53
  fetch: (url, args) => {
54
54
  const newArgs = handleMultipart(fetchParams, args) ?? args;
55
- return fetch2(url, newArgs);
55
+ return fetch2(url, {
56
+ ...newArgs,
57
+ signal: ctx.abortController.signal
58
+ });
56
59
  },
57
60
  metadata: ctx.metadata,
58
61
  session: ctx.session || {},
@@ -22,7 +22,6 @@ __export(pagination_exports, {
22
22
  offsetHandlers: () => offsetHandlers
23
23
  });
24
24
  module.exports = __toCommonJS(pagination_exports);
25
- var import_config = require("./config");
26
25
  var import_deepEquals = require("./deepEquals");
27
26
  var import_pageInfo = require("./pageInfo");
28
27
  var import_types = require("./types");
@@ -42,7 +41,6 @@ function cursorHandlers({
42
41
  fetch,
43
42
  where
44
43
  }) => {
45
- const config = (0, import_config.getCurrentConfig)();
46
44
  const loadVariables = {
47
45
  ...getVariables(),
48
46
  ...input
@@ -51,7 +49,7 @@ function cursorHandlers({
51
49
  throw (0, import_pageInfo.missingPageSizeError)(functionName);
52
50
  }
53
51
  let isSinglePage = artifact.refetch?.mode === "SinglePage";
54
- await (isSinglePage ? parentFetch : parentFetchUpdate)(
52
+ return (isSinglePage ? parentFetch : parentFetchUpdate)(
55
53
  {
56
54
  variables: loadVariables,
57
55
  fetch,
@@ -66,7 +64,7 @@ function cursorHandlers({
66
64
  return (0, import_pageInfo.extractPageInfo)(getState(), artifact.refetch?.path ?? []);
67
65
  };
68
66
  return {
69
- loadNextPage: async ({
67
+ loadNextPage: ({
70
68
  first,
71
69
  after,
72
70
  fetch,
@@ -74,7 +72,15 @@ function cursorHandlers({
74
72
  } = {}) => {
75
73
  const currentPageInfo = getPageInfo();
76
74
  if (!currentPageInfo.hasNextPage) {
77
- return;
75
+ return Promise.resolve({
76
+ data: getState(),
77
+ errors: null,
78
+ fetching: false,
79
+ partial: false,
80
+ stale: false,
81
+ source: import_types.DataSource.Cache,
82
+ variables: getVariables()
83
+ });
78
84
  }
79
85
  const input = {
80
86
  first: first ?? artifact.refetch.pageSize,
@@ -82,7 +88,7 @@ function cursorHandlers({
82
88
  before: null,
83
89
  last: null
84
90
  };
85
- return await loadPage({
91
+ return loadPage({
86
92
  pageSizeVar: "first",
87
93
  functionName: "loadNextPage",
88
94
  input,
@@ -91,7 +97,7 @@ function cursorHandlers({
91
97
  where: "end"
92
98
  });
93
99
  },
94
- loadPreviousPage: async ({
100
+ loadPreviousPage: ({
95
101
  last,
96
102
  before,
97
103
  fetch,
@@ -99,7 +105,15 @@ function cursorHandlers({
99
105
  } = {}) => {
100
106
  const currentPageInfo = getPageInfo();
101
107
  if (!currentPageInfo.hasPreviousPage) {
102
- return;
108
+ return Promise.resolve({
109
+ data: getState(),
110
+ errors: null,
111
+ fetching: false,
112
+ partial: false,
113
+ stale: false,
114
+ source: import_types.DataSource.Cache,
115
+ variables: getVariables()
116
+ });
103
117
  }
104
118
  const input = {
105
119
  before: before ?? currentPageInfo.startCursor,
@@ -107,7 +121,7 @@ function cursorHandlers({
107
121
  first: null,
108
122
  after: null
109
123
  };
110
- return await loadPage({
124
+ return loadPage({
111
125
  pageSizeVar: "last",
112
126
  functionName: "loadPreviousPage",
113
127
  input,
@@ -60,9 +60,11 @@ export type QueryArtifact = BaseCompiledDocument<'HoudiniQuery'> & {
60
60
  policy?: CachePolicies;
61
61
  partial?: boolean;
62
62
  enableLoadingState?: 'global' | 'local';
63
+ dedupe?: 'first' | 'last';
63
64
  };
64
65
  export type MutationArtifact = BaseCompiledDocument<'HoudiniMutation'> & {
65
66
  optimisticKeys?: boolean;
67
+ dedupe?: 'first' | 'last';
66
68
  };
67
69
  export type FragmentArtifact = BaseCompiledDocument<'HoudiniFragment'> & {
68
70
  enableLoadingState?: 'global' | 'local';
@@ -270,13 +272,13 @@ export type CursorHandlers<_Data extends GraphQLObject, _Input> = {
270
272
  after?: string;
271
273
  fetch?: typeof globalThis.fetch;
272
274
  metadata?: {};
273
- }) => Promise<void>;
275
+ }) => Promise<QueryResult<_Data, _Input>>;
274
276
  loadPreviousPage: (args?: {
275
277
  last?: number;
276
278
  before?: string;
277
279
  fetch?: typeof globalThis.fetch;
278
280
  metadata?: {};
279
- }) => Promise<void>;
281
+ }) => Promise<QueryResult<_Data, _Input>>;
280
282
  fetch(args?: FetchParams<_Input> | undefined): Promise<QueryResult<_Data, _Input>>;
281
283
  };
282
284
  export type OffsetHandlers<_Data extends GraphQLObject, _Input> = {
@@ -11,6 +11,7 @@ export declare class DocumentStore<_Data extends GraphQLObject, _Input extends G
11
11
  then: (val: any) => void;
12
12
  } | null;
13
13
  serverSideFallback?: boolean;
14
+ controllerKey(variables: any): string;
14
15
  constructor({ artifact, plugins, pipeline, client, cache, enableCache, initialValue, initialVariables, fetching, }: {
15
16
  artifact: DocumentArtifact;
16
17
  plugins?: ClientHooks[];
@@ -23,7 +24,7 @@ export declare class DocumentStore<_Data extends GraphQLObject, _Input extends G
23
24
  serverSideFallback?: boolean;
24
25
  initialVariables?: _Input;
25
26
  });
26
- send({ metadata, session, fetch, variables, policy, stuff, cacheParams, setup, silenceEcho, }?: SendParams): Promise<QueryResult<_Data, _Input>>;
27
+ send({ metadata, session, fetch, variables, policy, stuff, cacheParams, setup, silenceEcho, abortController, }?: SendParams): Promise<QueryResult<_Data, _Input>>;
27
28
  cleanup(): Promise<void>;
28
29
  getFetch(getSession: () => App.Session | null | undefined): (input: RequestInfo | URL, init?: RequestInit | undefined) => Promise<Response>;
29
30
  }
@@ -51,6 +52,7 @@ export type ClientPluginContext = {
51
52
  metadata?: App.Metadata | null;
52
53
  session?: App.Session | null;
53
54
  fetchParams?: RequestInit;
55
+ abortController: AbortController;
54
56
  cacheParams?: {
55
57
  layer?: Layer;
56
58
  notifySubscribers?: SubscriptionSpec[];
@@ -100,5 +102,6 @@ export type SendParams = {
100
102
  cacheParams?: ClientPluginContext['cacheParams'];
101
103
  setup?: boolean;
102
104
  silenceEcho?: boolean;
105
+ abortController?: AbortController;
103
106
  };
104
107
  export {};
@@ -8,6 +8,7 @@ const steps = {
8
8
  forward: ["start", "beforeNetwork", "network"],
9
9
  backwards: ["end", "afterNetwork"]
10
10
  };
11
+ let inflightRequests = {};
11
12
  class DocumentStore extends Writable {
12
13
  artifact;
13
14
  #client;
@@ -17,6 +18,9 @@ class DocumentStore extends Writable {
17
18
  #lastContext = null;
18
19
  pendingPromise = null;
19
20
  serverSideFallback;
21
+ controllerKey(variables) {
22
+ return this.artifact.name;
23
+ }
20
24
  constructor({
21
25
  artifact,
22
26
  plugins,
@@ -74,9 +78,23 @@ class DocumentStore extends Writable {
74
78
  stuff,
75
79
  cacheParams,
76
80
  setup = false,
77
- silenceEcho = false
81
+ silenceEcho = false,
82
+ abortController = new AbortController()
78
83
  } = {}) {
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;
89
+ } else {
90
+ abortController.abort();
91
+ }
92
+ } else {
93
+ inflightRequests[this.controllerKey(variables)] = abortController;
94
+ }
95
+ }
79
96
  let context = new ClientPluginContextWrapper({
97
+ abortController,
80
98
  config: this.#configFile,
81
99
  name: this.artifact.name,
82
100
  text: this.artifact.raw,
@@ -120,7 +138,9 @@ class DocumentStore extends Writable {
120
138
  }
121
139
  this.#step("forward", state);
122
140
  });
123
- return await promise;
141
+ const response = await promise;
142
+ delete inflightRequests[this.controllerKey(variables)];
143
+ return response;
124
144
  }
125
145
  async cleanup() {
126
146
  for (const plugin of this.#plugins) {
@@ -229,6 +249,11 @@ class DocumentStore extends Writable {
229
249
  };
230
250
  }
231
251
  try {
252
+ if (draft.abortController.signal.aborted) {
253
+ const abortError = new Error("aborted");
254
+ abortError.name = "AbortError";
255
+ throw abortError;
256
+ }
232
257
  const result = target(draft, handlers);
233
258
  result?.catch((err) => {
234
259
  this.#step("error", { ...ctx, index: index - 1 }, err);
@@ -27,7 +27,10 @@ const fetch = (target) => {
27
27
  const result = await fetchFn({
28
28
  fetch: (url, args) => {
29
29
  const newArgs = handleMultipart(fetchParams, args) ?? args;
30
- return fetch2(url, newArgs);
30
+ return fetch2(url, {
31
+ ...newArgs,
32
+ signal: ctx.abortController.signal
33
+ });
31
34
  },
32
35
  metadata: ctx.metadata,
33
36
  session: ctx.session || {},
@@ -1,7 +1,6 @@
1
- import { getCurrentConfig } from "./config";
2
1
  import { deepEquals } from "./deepEquals";
3
2
  import { countPage, extractPageInfo, missingPageSizeError } from "./pageInfo";
4
- import { CachePolicy } from "./types";
3
+ import { CachePolicy, DataSource } from "./types";
5
4
  function cursorHandlers({
6
5
  artifact,
7
6
  fetchUpdate: parentFetchUpdate,
@@ -18,7 +17,6 @@ function cursorHandlers({
18
17
  fetch,
19
18
  where
20
19
  }) => {
21
- const config = getCurrentConfig();
22
20
  const loadVariables = {
23
21
  ...getVariables(),
24
22
  ...input
@@ -27,7 +25,7 @@ function cursorHandlers({
27
25
  throw missingPageSizeError(functionName);
28
26
  }
29
27
  let isSinglePage = artifact.refetch?.mode === "SinglePage";
30
- await (isSinglePage ? parentFetch : parentFetchUpdate)(
28
+ return (isSinglePage ? parentFetch : parentFetchUpdate)(
31
29
  {
32
30
  variables: loadVariables,
33
31
  fetch,
@@ -42,7 +40,7 @@ function cursorHandlers({
42
40
  return extractPageInfo(getState(), artifact.refetch?.path ?? []);
43
41
  };
44
42
  return {
45
- loadNextPage: async ({
43
+ loadNextPage: ({
46
44
  first,
47
45
  after,
48
46
  fetch,
@@ -50,7 +48,15 @@ function cursorHandlers({
50
48
  } = {}) => {
51
49
  const currentPageInfo = getPageInfo();
52
50
  if (!currentPageInfo.hasNextPage) {
53
- return;
51
+ return Promise.resolve({
52
+ data: getState(),
53
+ errors: null,
54
+ fetching: false,
55
+ partial: false,
56
+ stale: false,
57
+ source: DataSource.Cache,
58
+ variables: getVariables()
59
+ });
54
60
  }
55
61
  const input = {
56
62
  first: first ?? artifact.refetch.pageSize,
@@ -58,7 +64,7 @@ function cursorHandlers({
58
64
  before: null,
59
65
  last: null
60
66
  };
61
- return await loadPage({
67
+ return loadPage({
62
68
  pageSizeVar: "first",
63
69
  functionName: "loadNextPage",
64
70
  input,
@@ -67,7 +73,7 @@ function cursorHandlers({
67
73
  where: "end"
68
74
  });
69
75
  },
70
- loadPreviousPage: async ({
76
+ loadPreviousPage: ({
71
77
  last,
72
78
  before,
73
79
  fetch,
@@ -75,7 +81,15 @@ function cursorHandlers({
75
81
  } = {}) => {
76
82
  const currentPageInfo = getPageInfo();
77
83
  if (!currentPageInfo.hasPreviousPage) {
78
- return;
84
+ return Promise.resolve({
85
+ data: getState(),
86
+ errors: null,
87
+ fetching: false,
88
+ partial: false,
89
+ stale: false,
90
+ source: DataSource.Cache,
91
+ variables: getVariables()
92
+ });
79
93
  }
80
94
  const input = {
81
95
  before: before ?? currentPageInfo.startCursor,
@@ -83,7 +97,7 @@ function cursorHandlers({
83
97
  first: null,
84
98
  after: null
85
99
  };
86
- return await loadPage({
100
+ return loadPage({
87
101
  pageSizeVar: "last",
88
102
  functionName: "loadPreviousPage",
89
103
  input,
@@ -60,9 +60,11 @@ export type QueryArtifact = BaseCompiledDocument<'HoudiniQuery'> & {
60
60
  policy?: CachePolicies;
61
61
  partial?: boolean;
62
62
  enableLoadingState?: 'global' | 'local';
63
+ dedupe?: 'first' | 'last';
63
64
  };
64
65
  export type MutationArtifact = BaseCompiledDocument<'HoudiniMutation'> & {
65
66
  optimisticKeys?: boolean;
67
+ dedupe?: 'first' | 'last';
66
68
  };
67
69
  export type FragmentArtifact = BaseCompiledDocument<'HoudiniFragment'> & {
68
70
  enableLoadingState?: 'global' | 'local';
@@ -270,13 +272,13 @@ export type CursorHandlers<_Data extends GraphQLObject, _Input> = {
270
272
  after?: string;
271
273
  fetch?: typeof globalThis.fetch;
272
274
  metadata?: {};
273
- }) => Promise<void>;
275
+ }) => Promise<QueryResult<_Data, _Input>>;
274
276
  loadPreviousPage: (args?: {
275
277
  last?: number;
276
278
  before?: string;
277
279
  fetch?: typeof globalThis.fetch;
278
280
  metadata?: {};
279
- }) => Promise<void>;
281
+ }) => Promise<QueryResult<_Data, _Input>>;
280
282
  fetch(args?: FetchParams<_Input> | undefined): Promise<QueryResult<_Data, _Input>>;
281
283
  };
282
284
  export type OffsetHandlers<_Data extends GraphQLObject, _Input> = {
@@ -57154,6 +57154,9 @@ var Config = class {
57154
57154
  get listDirective() {
57155
57155
  return "list";
57156
57156
  }
57157
+ get dedupeDirective() {
57158
+ return "dedupe";
57159
+ }
57157
57160
  get optimisticKeyDirective() {
57158
57161
  return "optimisticKey";
57159
57162
  }
@@ -59221,7 +59224,17 @@ async function paginate(config, documents) {
59221
59224
  );
59222
59225
  return {
59223
59226
  ...node,
59224
- variableDefinitions: finalVariables
59227
+ variableDefinitions: finalVariables,
59228
+ directives: [
59229
+ ...node.directives || [],
59230
+ {
59231
+ kind: graphql13.Kind.DIRECTIVE,
59232
+ name: {
59233
+ kind: graphql13.Kind.NAME,
59234
+ value: config.dedupeDirective
59235
+ }
59236
+ }
59237
+ ]
59225
59238
  };
59226
59239
  },
59227
59240
  FragmentDefinition(node) {
@@ -60544,6 +60557,7 @@ function artifactGenerator(stats) {
60544
60557
  let rootType = "";
60545
60558
  let selectionSet;
60546
60559
  let originalSelectionSet = null;
60560
+ let dedupe;
60547
60561
  const fragmentDefinitions = doc.document.definitions.filter(
60548
60562
  (definition) => definition.kind === "FragmentDefinition"
60549
60563
  ).reduce(
@@ -60568,6 +60582,15 @@ function artifactGenerator(stats) {
60568
60582
  message: "could not find root type for operation: " + operation.operation + ". Maybe you need to re-run the introspection query?"
60569
60583
  });
60570
60584
  }
60585
+ const dedupeDirective = operation.directives?.find(
60586
+ (directive2) => directive2.name.value === config.dedupeDirective
60587
+ );
60588
+ if (dedupeDirective) {
60589
+ const cancelFirstArg = dedupeDirective.arguments?.find(
60590
+ (arg) => arg.name.value === "cancelFirst"
60591
+ );
60592
+ dedupe = cancelFirstArg && cancelFirstArg.value.kind === "BooleanValue" && cancelFirstArg.value ? "first" : "last";
60593
+ }
60571
60594
  selectionSet = operation.selectionSet;
60572
60595
  if (originalParsed.definitions[0].kind === "OperationDefinition") {
60573
60596
  originalSelectionSet = originalParsed.definitions[0].selectionSet;
@@ -60645,6 +60668,9 @@ function artifactGenerator(stats) {
60645
60668
  };
60646
60669
  const hash_value = hashPluginBaseRaw({ config, document: { ...doc, artifact } });
60647
60670
  artifact.hash = hash_value;
60671
+ if (artifact.kind === "HoudiniQuery" || artifact.kind === "HoudiniMutation" && dedupe) {
60672
+ artifact.dedupe = dedupe;
60673
+ }
60648
60674
  applyMask(
60649
60675
  config,
60650
60676
  artifact.selection,
@@ -64230,6 +64256,12 @@ directive @${config.paginateDirective}(${config.listOrPaginateNameArg}: String,
64230
64256
  """
64231
64257
  directive @${config.listPrependDirective} on FRAGMENT_SPREAD
64232
64258
 
64259
+ """
64260
+ @${config.dedupeDirective} is used to prevent an operation from running more than once at the same time.
64261
+ If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
64262
+ """
64263
+ directive @${config.dedupeDirective}(cancelFirst: Boolean) on QUERY | MUTATION
64264
+
64233
64265
  """
64234
64266
  @${config.optimisticKeyDirective} is used to identify a field as an optimistic key
64235
64267
  """
@@ -57150,6 +57150,9 @@ var Config = class {
57150
57150
  get listDirective() {
57151
57151
  return "list";
57152
57152
  }
57153
+ get dedupeDirective() {
57154
+ return "dedupe";
57155
+ }
57153
57156
  get optimisticKeyDirective() {
57154
57157
  return "optimisticKey";
57155
57158
  }
@@ -59217,7 +59220,17 @@ async function paginate(config, documents) {
59217
59220
  );
59218
59221
  return {
59219
59222
  ...node,
59220
- variableDefinitions: finalVariables
59223
+ variableDefinitions: finalVariables,
59224
+ directives: [
59225
+ ...node.directives || [],
59226
+ {
59227
+ kind: graphql13.Kind.DIRECTIVE,
59228
+ name: {
59229
+ kind: graphql13.Kind.NAME,
59230
+ value: config.dedupeDirective
59231
+ }
59232
+ }
59233
+ ]
59221
59234
  };
59222
59235
  },
59223
59236
  FragmentDefinition(node) {
@@ -60540,6 +60553,7 @@ function artifactGenerator(stats) {
60540
60553
  let rootType = "";
60541
60554
  let selectionSet;
60542
60555
  let originalSelectionSet = null;
60556
+ let dedupe;
60543
60557
  const fragmentDefinitions = doc.document.definitions.filter(
60544
60558
  (definition) => definition.kind === "FragmentDefinition"
60545
60559
  ).reduce(
@@ -60564,6 +60578,15 @@ function artifactGenerator(stats) {
60564
60578
  message: "could not find root type for operation: " + operation.operation + ". Maybe you need to re-run the introspection query?"
60565
60579
  });
60566
60580
  }
60581
+ const dedupeDirective = operation.directives?.find(
60582
+ (directive2) => directive2.name.value === config.dedupeDirective
60583
+ );
60584
+ if (dedupeDirective) {
60585
+ const cancelFirstArg = dedupeDirective.arguments?.find(
60586
+ (arg) => arg.name.value === "cancelFirst"
60587
+ );
60588
+ dedupe = cancelFirstArg && cancelFirstArg.value.kind === "BooleanValue" && cancelFirstArg.value ? "first" : "last";
60589
+ }
60567
60590
  selectionSet = operation.selectionSet;
60568
60591
  if (originalParsed.definitions[0].kind === "OperationDefinition") {
60569
60592
  originalSelectionSet = originalParsed.definitions[0].selectionSet;
@@ -60641,6 +60664,9 @@ function artifactGenerator(stats) {
60641
60664
  };
60642
60665
  const hash_value = hashPluginBaseRaw({ config, document: { ...doc, artifact } });
60643
60666
  artifact.hash = hash_value;
60667
+ if (artifact.kind === "HoudiniQuery" || artifact.kind === "HoudiniMutation" && dedupe) {
60668
+ artifact.dedupe = dedupe;
60669
+ }
60644
60670
  applyMask(
60645
60671
  config,
60646
60672
  artifact.selection,
@@ -64226,6 +64252,12 @@ directive @${config.paginateDirective}(${config.listOrPaginateNameArg}: String,
64226
64252
  """
64227
64253
  directive @${config.listPrependDirective} on FRAGMENT_SPREAD
64228
64254
 
64255
+ """
64256
+ @${config.dedupeDirective} is used to prevent an operation from running more than once at the same time.
64257
+ If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
64258
+ """
64259
+ directive @${config.dedupeDirective}(cancelFirst: Boolean) on QUERY | MUTATION
64260
+
64229
64261
  """
64230
64262
  @${config.optimisticKeyDirective} is used to identify a field as an optimistic key
64231
64263
  """
@@ -69371,6 +69371,9 @@ var Config = class {
69371
69371
  get listDirective() {
69372
69372
  return "list";
69373
69373
  }
69374
+ get dedupeDirective() {
69375
+ return "dedupe";
69376
+ }
69374
69377
  get optimisticKeyDirective() {
69375
69378
  return "optimisticKey";
69376
69379
  }
@@ -72120,7 +72123,17 @@ async function paginate(config2, documents) {
72120
72123
  );
72121
72124
  return {
72122
72125
  ...node,
72123
- variableDefinitions: finalVariables
72126
+ variableDefinitions: finalVariables,
72127
+ directives: [
72128
+ ...node.directives || [],
72129
+ {
72130
+ kind: graphql13.Kind.DIRECTIVE,
72131
+ name: {
72132
+ kind: graphql13.Kind.NAME,
72133
+ value: config2.dedupeDirective
72134
+ }
72135
+ }
72136
+ ]
72124
72137
  };
72125
72138
  },
72126
72139
  FragmentDefinition(node) {
@@ -73443,6 +73456,7 @@ function artifactGenerator(stats) {
73443
73456
  let rootType = "";
73444
73457
  let selectionSet;
73445
73458
  let originalSelectionSet = null;
73459
+ let dedupe;
73446
73460
  const fragmentDefinitions = doc.document.definitions.filter(
73447
73461
  (definition) => definition.kind === "FragmentDefinition"
73448
73462
  ).reduce(
@@ -73467,6 +73481,15 @@ function artifactGenerator(stats) {
73467
73481
  message: "could not find root type for operation: " + operation.operation + ". Maybe you need to re-run the introspection query?"
73468
73482
  });
73469
73483
  }
73484
+ const dedupeDirective = operation.directives?.find(
73485
+ (directive2) => directive2.name.value === config2.dedupeDirective
73486
+ );
73487
+ if (dedupeDirective) {
73488
+ const cancelFirstArg = dedupeDirective.arguments?.find(
73489
+ (arg) => arg.name.value === "cancelFirst"
73490
+ );
73491
+ dedupe = cancelFirstArg && cancelFirstArg.value.kind === "BooleanValue" && cancelFirstArg.value ? "first" : "last";
73492
+ }
73470
73493
  selectionSet = operation.selectionSet;
73471
73494
  if (originalParsed.definitions[0].kind === "OperationDefinition") {
73472
73495
  originalSelectionSet = originalParsed.definitions[0].selectionSet;
@@ -73544,6 +73567,9 @@ function artifactGenerator(stats) {
73544
73567
  };
73545
73568
  const hash_value = hashPluginBaseRaw({ config: config2, document: { ...doc, artifact } });
73546
73569
  artifact.hash = hash_value;
73570
+ if (artifact.kind === "HoudiniQuery" || artifact.kind === "HoudiniMutation" && dedupe) {
73571
+ artifact.dedupe = dedupe;
73572
+ }
73547
73573
  applyMask(
73548
73574
  config2,
73549
73575
  artifact.selection,
@@ -77129,6 +77155,12 @@ directive @${config2.paginateDirective}(${config2.listOrPaginateNameArg}: String
77129
77155
  """
77130
77156
  directive @${config2.listPrependDirective} on FRAGMENT_SPREAD
77131
77157
 
77158
+ """
77159
+ @${config2.dedupeDirective} is used to prevent an operation from running more than once at the same time.
77160
+ If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
77161
+ """
77162
+ directive @${config2.dedupeDirective}(cancelFirst: Boolean) on QUERY | MUTATION
77163
+
77132
77164
  """
77133
77165
  @${config2.optimisticKeyDirective} is used to identify a field as an optimistic key
77134
77166
  """
@@ -69364,6 +69364,9 @@ var Config = class {
69364
69364
  get listDirective() {
69365
69365
  return "list";
69366
69366
  }
69367
+ get dedupeDirective() {
69368
+ return "dedupe";
69369
+ }
69367
69370
  get optimisticKeyDirective() {
69368
69371
  return "optimisticKey";
69369
69372
  }
@@ -72113,7 +72116,17 @@ async function paginate(config2, documents) {
72113
72116
  );
72114
72117
  return {
72115
72118
  ...node,
72116
- variableDefinitions: finalVariables
72119
+ variableDefinitions: finalVariables,
72120
+ directives: [
72121
+ ...node.directives || [],
72122
+ {
72123
+ kind: graphql13.Kind.DIRECTIVE,
72124
+ name: {
72125
+ kind: graphql13.Kind.NAME,
72126
+ value: config2.dedupeDirective
72127
+ }
72128
+ }
72129
+ ]
72117
72130
  };
72118
72131
  },
72119
72132
  FragmentDefinition(node) {
@@ -73436,6 +73449,7 @@ function artifactGenerator(stats) {
73436
73449
  let rootType = "";
73437
73450
  let selectionSet;
73438
73451
  let originalSelectionSet = null;
73452
+ let dedupe;
73439
73453
  const fragmentDefinitions = doc.document.definitions.filter(
73440
73454
  (definition) => definition.kind === "FragmentDefinition"
73441
73455
  ).reduce(
@@ -73460,6 +73474,15 @@ function artifactGenerator(stats) {
73460
73474
  message: "could not find root type for operation: " + operation.operation + ". Maybe you need to re-run the introspection query?"
73461
73475
  });
73462
73476
  }
73477
+ const dedupeDirective = operation.directives?.find(
73478
+ (directive2) => directive2.name.value === config2.dedupeDirective
73479
+ );
73480
+ if (dedupeDirective) {
73481
+ const cancelFirstArg = dedupeDirective.arguments?.find(
73482
+ (arg) => arg.name.value === "cancelFirst"
73483
+ );
73484
+ dedupe = cancelFirstArg && cancelFirstArg.value.kind === "BooleanValue" && cancelFirstArg.value ? "first" : "last";
73485
+ }
73463
73486
  selectionSet = operation.selectionSet;
73464
73487
  if (originalParsed.definitions[0].kind === "OperationDefinition") {
73465
73488
  originalSelectionSet = originalParsed.definitions[0].selectionSet;
@@ -73537,6 +73560,9 @@ function artifactGenerator(stats) {
73537
73560
  };
73538
73561
  const hash_value = hashPluginBaseRaw({ config: config2, document: { ...doc, artifact } });
73539
73562
  artifact.hash = hash_value;
73563
+ if (artifact.kind === "HoudiniQuery" || artifact.kind === "HoudiniMutation" && dedupe) {
73564
+ artifact.dedupe = dedupe;
73565
+ }
73540
73566
  applyMask(
73541
73567
  config2,
73542
73568
  artifact.selection,
@@ -77122,6 +77148,12 @@ directive @${config2.paginateDirective}(${config2.listOrPaginateNameArg}: String
77122
77148
  """
77123
77149
  directive @${config2.listPrependDirective} on FRAGMENT_SPREAD
77124
77150
 
77151
+ """
77152
+ @${config2.dedupeDirective} is used to prevent an operation from running more than once at the same time.
77153
+ If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
77154
+ """
77155
+ directive @${config2.dedupeDirective}(cancelFirst: Boolean) on QUERY | MUTATION
77156
+
77125
77157
  """
77126
77158
  @${config2.optimisticKeyDirective} is used to identify a field as an optimistic key
77127
77159
  """
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "houdini",
3
- "version": "1.2.61",
3
+ "version": "1.2.63",
4
4
  "description": "The disappearing GraphQL clients",
5
5
  "keywords": [
6
6
  "typescript",