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
@@ -73,8 +73,8 @@ class Cache {
73
73
  variables
74
74
  );
75
75
  }
76
- list(name, parentID, allLists) {
77
- const handler = this._internal_unstable.lists.get(name, parentID, allLists);
76
+ list(name, parentID, allLists, skipMatches) {
77
+ const handler = this._internal_unstable.lists.get(name, parentID, allLists, skipMatches);
78
78
  if (!handler) {
79
79
  throw new Error(
80
80
  `Cannot find list with name: ${name}${parentID ? " under parent " + parentID : ""}. Is it possible that the query is not mounted?`
@@ -489,6 +489,7 @@ class CacheInternal {
489
489
  });
490
490
  }
491
491
  }
492
+ const processedOperations = /* @__PURE__ */ new Set();
492
493
  for (const operation of operations || []) {
493
494
  let parentID;
494
495
  if (operation.parentID) {
@@ -508,7 +509,12 @@ class CacheInternal {
508
509
  const targets = Array.isArray(value) ? value : [value];
509
510
  for (const target of targets) {
510
511
  if (operation.action === "insert" && target instanceof Object && fieldSelection && operation.list) {
511
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).addToList(
512
+ this.cache.list(
513
+ operation.list,
514
+ parentID,
515
+ operation.target === "all",
516
+ processedOperations
517
+ ).when(operation.when).addToList(
512
518
  fieldSelection,
513
519
  target,
514
520
  variables,
@@ -516,7 +522,12 @@ class CacheInternal {
516
522
  layer
517
523
  );
518
524
  } else if (operation.action === "toggle" && target instanceof Object && fieldSelection && operation.list) {
519
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).toggleElement({
525
+ this.cache.list(
526
+ operation.list,
527
+ parentID,
528
+ operation.target === "all",
529
+ processedOperations
530
+ ).when(operation.when).toggleElement({
520
531
  selection: fieldSelection,
521
532
  data: target,
522
533
  variables,
@@ -524,7 +535,12 @@ class CacheInternal {
524
535
  layer
525
536
  });
526
537
  } else if (operation.action === "remove" && target instanceof Object && fieldSelection && operation.list) {
527
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).remove(target, variables, layer);
538
+ this.cache.list(
539
+ operation.list,
540
+ parentID,
541
+ operation.target === "all",
542
+ processedOperations
543
+ ).when(operation.when).remove(target, variables, layer);
528
544
  } else if (operation.action === "delete" && operation.type && target) {
529
545
  const targetID = this.id(operation.type, target);
530
546
  if (!targetID) {
@@ -536,6 +552,16 @@ class CacheInternal {
536
552
  this.cache.delete(targetID, layer);
537
553
  }
538
554
  }
555
+ if (operation.list) {
556
+ const matchingLists = this.cache.list(
557
+ operation.list,
558
+ parentID,
559
+ operation.target === "all"
560
+ );
561
+ for (const list of matchingLists.lists) {
562
+ processedOperations.add(list.fieldRef);
563
+ }
564
+ }
539
565
  }
540
566
  }
541
567
  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;
@@ -9,7 +9,18 @@ class ListManager {
9
9
  }
10
10
  lists = /* @__PURE__ */ new Map();
11
11
  listsByField = /* @__PURE__ */ new Map();
12
- get(listName, id, allLists) {
12
+ get(listName, id, allLists, skipMatches) {
13
+ const lists = this.getLists(listName, id, allLists);
14
+ if (!lists) {
15
+ return null;
16
+ }
17
+ if (skipMatches) {
18
+ return new ListCollection(lists.lists.filter((list) => !skipMatches.has(list.fieldRef)));
19
+ } else {
20
+ return lists;
21
+ }
22
+ }
23
+ getLists(listName, id, allLists) {
13
24
  const matches = this.lists.get(listName);
14
25
  if (!matches || matches.size === 0) {
15
26
  return null;
@@ -131,6 +142,9 @@ class List {
131
142
  this.manager = manager;
132
143
  this.abstract = abstract;
133
144
  }
145
+ get fieldRef() {
146
+ return `${this.recordID}.${this.key}`;
147
+ }
134
148
  when(when) {
135
149
  return this.manager.lists.get(this.name).get(this.recordID).when(when);
136
150
  }
@@ -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")?.startsWith("application/json") && !result.headers.get("content-type")?.startsWith("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,
@@ -115,7 +115,7 @@ function exec(match, params) {
115
115
  if (param.rest)
116
116
  result[param.name] = "";
117
117
  } else {
118
- result[param.name] = value;
118
+ result[param.name] = decodeURIComponent(value);
119
119
  }
120
120
  }
121
121
  if (buffered)
@@ -53983,6 +53983,11 @@ var CachePolicy = {
53983
53983
  CacheAndNetwork: "CacheAndNetwork",
53984
53984
  NoCache: "NoCache"
53985
53985
  };
53986
+ var DedupeMatchMode = {
53987
+ Variables: "Variables",
53988
+ Operation: "Operation",
53989
+ None: "None"
53990
+ };
53986
53991
  var PaginateMode = {
53987
53992
  Infinite: "Infinite",
53988
53993
  SinglePage: "SinglePage"
@@ -54562,7 +54567,18 @@ var ListManager = class {
54562
54567
  }
54563
54568
  lists = /* @__PURE__ */ new Map();
54564
54569
  listsByField = /* @__PURE__ */ new Map();
54565
- get(listName, id, allLists) {
54570
+ get(listName, id, allLists, skipMatches) {
54571
+ const lists = this.getLists(listName, id, allLists);
54572
+ if (!lists) {
54573
+ return null;
54574
+ }
54575
+ if (skipMatches) {
54576
+ return new ListCollection(lists.lists.filter((list) => !skipMatches.has(list.fieldRef)));
54577
+ } else {
54578
+ return lists;
54579
+ }
54580
+ }
54581
+ getLists(listName, id, allLists) {
54566
54582
  const matches = this.lists.get(listName);
54567
54583
  if (!matches || matches.size === 0) {
54568
54584
  return null;
@@ -54684,6 +54700,9 @@ var List = class {
54684
54700
  this.manager = manager;
54685
54701
  this.abstract = abstract;
54686
54702
  }
54703
+ get fieldRef() {
54704
+ return `${this.recordID}.${this.key}`;
54705
+ }
54687
54706
  when(when) {
54688
54707
  return this.manager.lists.get(this.name).get(this.recordID).when(when);
54689
54708
  }
@@ -55836,8 +55855,8 @@ var Cache = class {
55836
55855
  variables
55837
55856
  );
55838
55857
  }
55839
- list(name, parentID, allLists) {
55840
- const handler = this._internal_unstable.lists.get(name, parentID, allLists);
55858
+ list(name, parentID, allLists, skipMatches) {
55859
+ const handler = this._internal_unstable.lists.get(name, parentID, allLists, skipMatches);
55841
55860
  if (!handler) {
55842
55861
  throw new Error(
55843
55862
  `Cannot find list with name: ${name}${parentID ? " under parent " + parentID : ""}. Is it possible that the query is not mounted?`
@@ -56252,6 +56271,7 @@ var CacheInternal = class {
56252
56271
  });
56253
56272
  }
56254
56273
  }
56274
+ const processedOperations = /* @__PURE__ */ new Set();
56255
56275
  for (const operation of operations || []) {
56256
56276
  let parentID;
56257
56277
  if (operation.parentID) {
@@ -56271,7 +56291,12 @@ var CacheInternal = class {
56271
56291
  const targets = Array.isArray(value) ? value : [value];
56272
56292
  for (const target of targets) {
56273
56293
  if (operation.action === "insert" && target instanceof Object && fieldSelection && operation.list) {
56274
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).addToList(
56294
+ this.cache.list(
56295
+ operation.list,
56296
+ parentID,
56297
+ operation.target === "all",
56298
+ processedOperations
56299
+ ).when(operation.when).addToList(
56275
56300
  fieldSelection,
56276
56301
  target,
56277
56302
  variables,
@@ -56279,7 +56304,12 @@ var CacheInternal = class {
56279
56304
  layer
56280
56305
  );
56281
56306
  } else if (operation.action === "toggle" && target instanceof Object && fieldSelection && operation.list) {
56282
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).toggleElement({
56307
+ this.cache.list(
56308
+ operation.list,
56309
+ parentID,
56310
+ operation.target === "all",
56311
+ processedOperations
56312
+ ).when(operation.when).toggleElement({
56283
56313
  selection: fieldSelection,
56284
56314
  data: target,
56285
56315
  variables,
@@ -56287,7 +56317,12 @@ var CacheInternal = class {
56287
56317
  layer
56288
56318
  });
56289
56319
  } else if (operation.action === "remove" && target instanceof Object && fieldSelection && operation.list) {
56290
- this.cache.list(operation.list, parentID, operation.target === "all").when(operation.when).remove(target, variables, layer);
56320
+ this.cache.list(
56321
+ operation.list,
56322
+ parentID,
56323
+ operation.target === "all",
56324
+ processedOperations
56325
+ ).when(operation.when).remove(target, variables, layer);
56291
56326
  } else if (operation.action === "delete" && operation.type && target) {
56292
56327
  const targetID = this.id(operation.type, target);
56293
56328
  if (!targetID) {
@@ -56299,6 +56334,16 @@ var CacheInternal = class {
56299
56334
  this.cache.delete(targetID, layer);
56300
56335
  }
56301
56336
  }
56337
+ if (operation.list) {
56338
+ const matchingLists = this.cache.list(
56339
+ operation.list,
56340
+ parentID,
56341
+ operation.target === "all"
56342
+ );
56343
+ for (const list of matchingLists.lists) {
56344
+ processedOperations.add(list.fieldRef);
56345
+ }
56346
+ }
56302
56347
  }
56303
56348
  }
56304
56349
  return toNotify;
@@ -56767,8 +56812,9 @@ var Config = class {
56767
56812
  localSchema;
56768
56813
  projectRoot;
56769
56814
  schema;
56815
+ runtimeDir;
56770
56816
  schemaPath;
56771
- persistedQueriesPath = "./$houdini/persisted_queries.json";
56817
+ persistedQueriesPath;
56772
56818
  exclude;
56773
56819
  scalars;
56774
56820
  module = "esm";
@@ -56809,6 +56855,7 @@ var Config = class {
56809
56855
  let {
56810
56856
  schema,
56811
56857
  schemaPath = "./schema.graphql",
56858
+ runtimeDir = "$houdini",
56812
56859
  exclude = [],
56813
56860
  module: module2 = "esm",
56814
56861
  scalars,
@@ -56847,6 +56894,7 @@ var Config = class {
56847
56894
  this.projectRoot = dirname(
56848
56895
  projectDir ? join(process.cwd(), projectDir) : filepath
56849
56896
  );
56897
+ this.runtimeDir = runtimeDir;
56850
56898
  this.scalars = scalars;
56851
56899
  this.cacheBufferSize = cacheBufferSize;
56852
56900
  this.defaultCachePolicy = defaultCachePolicy;
@@ -56861,11 +56909,9 @@ var Config = class {
56861
56909
  this.schemaPollInterval = watchSchema?.interval === void 0 ? 2e3 : watchSchema.interval;
56862
56910
  this.schemaPollTimeout = watchSchema?.timeout ?? 3e4;
56863
56911
  this.schemaPollHeaders = watchSchema?.headers ?? {};
56864
- this.rootDir = join(this.projectRoot, "$houdini");
56912
+ this.rootDir = join(this.projectRoot, this.runtimeDir);
56913
+ this.persistedQueriesPath = persistedQueriesPath ?? join(this.rootDir, "persisted_queries.json");
56865
56914
  this.#fragmentVariableMaps = {};
56866
- if (persistedQueriesPath) {
56867
- this.persistedQueriesPath = persistedQueriesPath;
56868
- }
56869
56915
  if (defaultKeys) {
56870
56916
  this.defaultKeys = defaultKeys;
56871
56917
  }
@@ -59225,14 +59271,27 @@ async function paginate(config, documents) {
59225
59271
  return {
59226
59272
  ...node,
59227
59273
  variableDefinitions: finalVariables,
59228
- directives: [
59274
+ directives: config.configFile.supressPaginationDeduplication ? node.directives : [
59229
59275
  ...node.directives || [],
59230
59276
  {
59231
59277
  kind: graphql13.Kind.DIRECTIVE,
59232
59278
  name: {
59233
59279
  kind: graphql13.Kind.NAME,
59234
59280
  value: config.dedupeDirective
59235
- }
59281
+ },
59282
+ arguments: [
59283
+ {
59284
+ kind: "Argument",
59285
+ name: {
59286
+ kind: "Name",
59287
+ value: "match"
59288
+ },
59289
+ value: {
59290
+ kind: "EnumValue",
59291
+ value: DedupeMatchMode.Variables
59292
+ }
59293
+ }
59294
+ ]
59236
59295
  }
59237
59296
  ]
59238
59297
  };
@@ -59600,6 +59659,46 @@ function objectNode([type, defaultValue]) {
59600
59659
  return node;
59601
59660
  }
59602
59661
  var pageInfoSelection = [
59662
+ {
59663
+ kind: graphql13.Kind.FIELD,
59664
+ name: {
59665
+ kind: graphql13.Kind.NAME,
59666
+ value: "pageInfo"
59667
+ },
59668
+ selectionSet: {
59669
+ kind: graphql13.Kind.SELECTION_SET,
59670
+ selections: [
59671
+ {
59672
+ kind: graphql13.Kind.FIELD,
59673
+ name: {
59674
+ kind: graphql13.Kind.NAME,
59675
+ value: "hasPreviousPage"
59676
+ }
59677
+ },
59678
+ {
59679
+ kind: graphql13.Kind.FIELD,
59680
+ name: {
59681
+ kind: graphql13.Kind.NAME,
59682
+ value: "hasNextPage"
59683
+ }
59684
+ },
59685
+ {
59686
+ kind: graphql13.Kind.FIELD,
59687
+ name: {
59688
+ kind: graphql13.Kind.NAME,
59689
+ value: "startCursor"
59690
+ }
59691
+ },
59692
+ {
59693
+ kind: graphql13.Kind.FIELD,
59694
+ name: {
59695
+ kind: graphql13.Kind.NAME,
59696
+ value: "endCursor"
59697
+ }
59698
+ }
59699
+ ]
59700
+ }
59701
+ },
59603
59702
  {
59604
59703
  kind: graphql13.Kind.FIELD,
59605
59704
  name: {
@@ -60589,7 +60688,13 @@ function artifactGenerator(stats) {
60589
60688
  const cancelFirstArg = dedupeDirective.arguments?.find(
60590
60689
  (arg) => arg.name.value === "cancelFirst"
60591
60690
  );
60592
- dedupe = cancelFirstArg && cancelFirstArg.value.kind === "BooleanValue" && cancelFirstArg.value ? "first" : "last";
60691
+ const matchArg = dedupeDirective.arguments?.find(
60692
+ (arg) => arg.name.value === "match"
60693
+ );
60694
+ dedupe = {
60695
+ cancel: cancelFirstArg && cancelFirstArg.value.kind === "BooleanValue" && cancelFirstArg.value ? "first" : "last",
60696
+ match: matchArg && matchArg.value.kind === "EnumValue" ? matchArg.value.value : DedupeMatchMode.Operation
60697
+ };
60593
60698
  }
60594
60699
  selectionSet = operation.selectionSet;
60595
60700
  if (originalParsed.definitions[0].kind === "OperationDefinition") {
@@ -64256,11 +64361,20 @@ directive @${config.paginateDirective}(${config.listOrPaginateNameArg}: String,
64256
64361
  """
64257
64362
  directive @${config.listPrependDirective} on FRAGMENT_SPREAD
64258
64363
 
64364
+ enum DedupeMatchMode {
64365
+ ${DedupeMatchMode.Variables}
64366
+ ${DedupeMatchMode.Operation}
64367
+ ${DedupeMatchMode.None}
64368
+ }
64369
+
64259
64370
  """
64260
64371
  @${config.dedupeDirective} is used to prevent an operation from running more than once at the same time.
64261
64372
  If the cancelFirst arg is set to true, the response already in flight will be canceled instead of the second one.
64373
+ If match is set to Operation, then a request will be deduplicated any time there is a request with the same operation.
64374
+ If it's set to Variables then the request will only be deduplicated if the variables match. If match is set to None,
64375
+ then the request will never be deduplicated.
64262
64376
  """
64263
- directive @${config.dedupeDirective}(cancelFirst: Boolean) on QUERY | MUTATION
64377
+ directive @${config.dedupeDirective}(cancelFirst: Boolean, match: DedupeMatchMode) on QUERY | MUTATION
64264
64378
 
64265
64379
  """
64266
64380
  @${config.optimisticKeyDirective} is used to identify a field as an optimistic key