houdini 1.1.7 → 1.2.0-next.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.
Files changed (85) hide show
  1. package/README.md +1 -1
  2. package/build/cmd-cjs/index.js +30737 -30175
  3. package/build/cmd-esm/index.js +30737 -30175
  4. package/build/codegen/generators/artifacts/selection.d.ts +2 -1
  5. package/build/codegen/generators/typescript/inlineType.d.ts +5 -2
  6. package/build/codegen/generators/typescript/loadingState.d.ts +8 -0
  7. package/build/codegen-cjs/index.js +30791 -30254
  8. package/build/codegen-esm/index.js +30791 -30254
  9. package/build/lib/code.d.ts +11 -0
  10. package/build/lib/config.d.ts +5 -0
  11. package/build/lib/graphql.d.ts +5 -1
  12. package/build/lib/index.d.ts +1 -0
  13. package/build/lib/parse.d.ts +0 -7
  14. package/build/lib/types.d.ts +4 -1
  15. package/build/lib-cjs/index.js +29888 -29668
  16. package/build/lib-esm/index.js +29885 -29668
  17. package/build/runtime/cache/cache.d.ts +5 -6
  18. package/build/runtime/client/documentStore.d.ts +3 -0
  19. package/build/runtime/client/index.d.ts +7 -6
  20. package/build/runtime/client/plugins/cache.d.ts +1 -1
  21. package/build/runtime/client/plugins/fetch.d.ts +1 -0
  22. package/build/runtime/client/plugins/subscription.d.ts +1 -0
  23. package/build/runtime/client/plugins/throwOnError.d.ts +2 -1
  24. package/build/runtime/lib/pageInfo.d.ts +7 -0
  25. package/build/runtime/lib/pagination.d.ts +27 -0
  26. package/build/runtime/lib/selection.d.ts +1 -1
  27. package/build/runtime/lib/types.d.ts +75 -3
  28. package/build/runtime-cjs/cache/cache.d.ts +5 -6
  29. package/build/runtime-cjs/cache/cache.js +78 -36
  30. package/build/runtime-cjs/cache/subscription.js +5 -5
  31. package/build/runtime-cjs/client/documentStore.d.ts +3 -0
  32. package/build/runtime-cjs/client/documentStore.js +20 -7
  33. package/build/runtime-cjs/client/index.d.ts +7 -6
  34. package/build/runtime-cjs/client/index.js +12 -4
  35. package/build/runtime-cjs/client/plugins/cache.d.ts +1 -1
  36. package/build/runtime-cjs/client/plugins/cache.js +12 -2
  37. package/build/runtime-cjs/client/plugins/fetch.d.ts +1 -0
  38. package/build/runtime-cjs/client/plugins/fetch.js +3 -2
  39. package/build/runtime-cjs/client/plugins/fragment.js +8 -1
  40. package/build/runtime-cjs/client/plugins/query.js +2 -1
  41. package/build/runtime-cjs/client/plugins/subscription.d.ts +1 -0
  42. package/build/runtime-cjs/client/plugins/subscription.js +1 -0
  43. package/build/runtime-cjs/client/plugins/throwOnError.d.ts +2 -1
  44. package/build/runtime-cjs/lib/config.js +2 -1
  45. package/build/runtime-cjs/lib/pageInfo.d.ts +7 -0
  46. package/build/runtime-cjs/lib/pageInfo.js +79 -0
  47. package/build/runtime-cjs/lib/pagination.d.ts +27 -0
  48. package/build/runtime-cjs/lib/pagination.js +219 -0
  49. package/build/runtime-cjs/lib/scalars.js +1 -1
  50. package/build/runtime-cjs/lib/selection.d.ts +1 -1
  51. package/build/runtime-cjs/lib/selection.js +28 -1
  52. package/build/runtime-cjs/lib/types.d.ts +75 -3
  53. package/build/runtime-cjs/lib/types.js +3 -0
  54. package/build/runtime-esm/cache/cache.d.ts +5 -6
  55. package/build/runtime-esm/cache/cache.js +79 -37
  56. package/build/runtime-esm/cache/subscription.js +5 -5
  57. package/build/runtime-esm/client/documentStore.d.ts +3 -0
  58. package/build/runtime-esm/client/documentStore.js +20 -7
  59. package/build/runtime-esm/client/index.d.ts +7 -6
  60. package/build/runtime-esm/client/index.js +15 -7
  61. package/build/runtime-esm/client/plugins/cache.d.ts +1 -1
  62. package/build/runtime-esm/client/plugins/cache.js +12 -2
  63. package/build/runtime-esm/client/plugins/fetch.d.ts +1 -0
  64. package/build/runtime-esm/client/plugins/fetch.js +3 -2
  65. package/build/runtime-esm/client/plugins/fragment.js +8 -1
  66. package/build/runtime-esm/client/plugins/query.js +2 -1
  67. package/build/runtime-esm/client/plugins/subscription.d.ts +1 -0
  68. package/build/runtime-esm/client/plugins/subscription.js +1 -0
  69. package/build/runtime-esm/client/plugins/throwOnError.d.ts +2 -1
  70. package/build/runtime-esm/lib/config.js +2 -1
  71. package/build/runtime-esm/lib/pageInfo.d.ts +7 -0
  72. package/build/runtime-esm/lib/pageInfo.js +52 -0
  73. package/build/runtime-esm/lib/pagination.d.ts +27 -0
  74. package/build/runtime-esm/lib/pagination.js +194 -0
  75. package/build/runtime-esm/lib/scalars.js +1 -1
  76. package/build/runtime-esm/lib/selection.d.ts +1 -1
  77. package/build/runtime-esm/lib/selection.js +28 -1
  78. package/build/runtime-esm/lib/types.d.ts +75 -3
  79. package/build/runtime-esm/lib/types.js +2 -0
  80. package/build/test/index.d.ts +15 -0
  81. package/build/test-cjs/index.js +30699 -30140
  82. package/build/test-esm/index.js +30699 -30140
  83. package/build/vite-cjs/index.js +30765 -30205
  84. package/build/vite-esm/index.js +30765 -30205
  85. package/package.json +5 -1
@@ -21,7 +21,15 @@ __export(selection_exports, {
21
21
  getFieldsForType: () => getFieldsForType
22
22
  });
23
23
  module.exports = __toCommonJS(selection_exports);
24
- function getFieldsForType(selection, __typename) {
24
+ function getFieldsForType(selection, __typename, loading) {
25
+ if (loading) {
26
+ if (selection.loadingTypes && selection.loadingTypes.length > 0) {
27
+ return deepMerge(
28
+ ...selection.loadingTypes.map((type) => selection.abstractFields?.fields[type])
29
+ );
30
+ }
31
+ return selection.fields ?? {};
32
+ }
25
33
  let targetSelection = selection.fields || {};
26
34
  if (selection.abstractFields && __typename) {
27
35
  const mappedType = selection.abstractFields.typeMap[__typename];
@@ -33,6 +41,25 @@ function getFieldsForType(selection, __typename) {
33
41
  }
34
42
  return targetSelection;
35
43
  }
44
+ function deepMerge(...objects) {
45
+ const mergedObj = {};
46
+ for (let obj of objects) {
47
+ if (!obj) {
48
+ continue;
49
+ }
50
+ for (let prop in obj) {
51
+ if (prop in obj) {
52
+ const val = obj[prop];
53
+ if (typeof val === "object" && val !== null && !Array.isArray(val)) {
54
+ mergedObj[prop] = deepMerge(mergedObj[prop] || {}, val);
55
+ } else {
56
+ mergedObj[prop] = val;
57
+ }
58
+ }
59
+ }
60
+ }
61
+ return mergedObj;
62
+ }
36
63
  // Annotate the CommonJS export names for ESM import in node:
37
64
  0 && (module.exports = {
38
65
  getFieldsForType
@@ -25,6 +25,7 @@ declare global {
25
25
  };
26
26
  optimisticResponse?: GraphQLObject;
27
27
  parentID?: string;
28
+ silenceLoading?: boolean;
28
29
  }
29
30
  }
30
31
  }
@@ -52,9 +53,12 @@ export type CompiledDocumentKind = ArtifactKinds;
52
53
  export type QueryArtifact = BaseCompiledDocument<'HoudiniQuery'> & {
53
54
  policy?: CachePolicies;
54
55
  partial?: boolean;
56
+ enableLoadingState?: boolean;
55
57
  };
56
58
  export type MutationArtifact = BaseCompiledDocument<'HoudiniMutation'>;
57
- export type FragmentArtifact = BaseCompiledDocument<'HoudiniFragment'>;
59
+ export type FragmentArtifact = BaseCompiledDocument<'HoudiniFragment'> & {
60
+ enableLoadingState?: boolean;
61
+ };
58
62
  export type SubscriptionArtifact = BaseCompiledDocument<'HoudiniSubscription'>;
59
63
  export declare const RefetchUpdateMode: {
60
64
  readonly append: "append";
@@ -128,12 +132,31 @@ export type GraphQLObject = {
128
132
  [key: string]: GraphQLValue;
129
133
  };
130
134
  export type GraphQLValue = number | string | boolean | null | GraphQLObject | GraphQLValue[] | undefined;
135
+ export type LoadingSpec = {
136
+ kind: 'continue';
137
+ list?: {
138
+ depth: number;
139
+ count: number;
140
+ };
141
+ } | {
142
+ kind: 'value';
143
+ value?: any;
144
+ list?: {
145
+ depth: number;
146
+ count: number;
147
+ };
148
+ };
131
149
  export type SubscriptionSelection = {
132
- fragments?: Record<string, ValueMap>;
150
+ loadingTypes?: string[];
151
+ fragments?: Record<string, {
152
+ arguments: ValueMap;
153
+ loading?: boolean;
154
+ }>;
133
155
  fields?: {
134
156
  [fieldName: string]: {
135
157
  type: string;
136
158
  nullable?: boolean;
159
+ required?: boolean;
137
160
  keyRaw: string;
138
161
  operations?: MutationOperation[];
139
162
  list?: {
@@ -141,6 +164,7 @@ export type SubscriptionSelection = {
141
164
  connection: boolean;
142
165
  type: string;
143
166
  };
167
+ loading?: LoadingSpec;
144
168
  directives?: {
145
169
  name: string;
146
170
  arguments: ValueMap;
@@ -153,6 +177,7 @@ export type SubscriptionSelection = {
153
177
  }>;
154
178
  selection?: SubscriptionSelection;
155
179
  abstract?: boolean;
180
+ abstractHasRequired?: boolean;
156
181
  };
157
182
  };
158
183
  abstractFields?: {
@@ -194,9 +219,54 @@ export type RequestPayload<GraphQLObject = any> = {
194
219
  };
195
220
  export type NestedList<_Result = string> = (_Result | null | NestedList<_Result>)[];
196
221
  export type ValueOf<Parent> = Parent[keyof Parent];
197
- export declare const fragmentKey = " $fragments";
222
+ export declare const fragmentKey: " $fragments";
198
223
  export type ValueNode = VariableNode | IntValueNode | FloatValueNode | StringValueNode | BooleanValueNode | NullValueNode | EnumValueNode | ListValueNode | ObjectValueNode;
199
224
  export type ValueMap = Record<string, ValueNode>;
225
+ export type FetchParams<_Input> = {
226
+ variables?: _Input;
227
+ /**
228
+ * The policy to use when performing the fetch. If set to CachePolicy.NetworkOnly,
229
+ * a request will always be sent, even if the variables are the same as the last call
230
+ * to fetch.
231
+ */
232
+ policy?: CachePolicies;
233
+ /**
234
+ * An object that will be passed to the fetch function.
235
+ * You can do what you want with it!
236
+ */
237
+ metadata?: App.Metadata;
238
+ };
239
+ export type FetchFn<_Data extends GraphQLObject, _Input = any> = (params?: FetchParams<_Input>) => Promise<QueryResult<_Data, _Input>>;
240
+ export type CursorHandlers<_Data extends GraphQLObject, _Input> = {
241
+ loadNextPage: (args?: {
242
+ first?: number;
243
+ after?: string;
244
+ fetch?: typeof globalThis.fetch;
245
+ metadata?: {};
246
+ }) => Promise<void>;
247
+ loadPreviousPage: (args?: {
248
+ last?: number;
249
+ before?: string;
250
+ fetch?: typeof globalThis.fetch;
251
+ metadata?: {};
252
+ }) => Promise<void>;
253
+ fetch(args?: FetchParams<_Input> | undefined): Promise<QueryResult<_Data, _Input>>;
254
+ };
255
+ export type OffsetHandlers<_Data extends GraphQLObject, _Input> = {
256
+ loadNextPage: (args?: {
257
+ limit?: number;
258
+ offset?: number;
259
+ metadata?: {};
260
+ fetch?: typeof globalThis.fetch;
261
+ }) => Promise<void>;
262
+ fetch(args?: FetchParams<_Input> | undefined): Promise<QueryResult<_Data, _Input>>;
263
+ };
264
+ export type PageInfo = {
265
+ startCursor: string | null;
266
+ endCursor: string | null;
267
+ hasNextPage: boolean;
268
+ hasPreviousPage: boolean;
269
+ };
200
270
  interface IntValueNode {
201
271
  readonly kind: 'IntValue';
202
272
  readonly value: string;
@@ -241,4 +311,6 @@ interface VariableNode {
241
311
  readonly kind: 'Variable';
242
312
  readonly name: NameNode;
243
313
  }
314
+ export declare const PendingValue: unique symbol;
315
+ export type LoadingType = typeof PendingValue;
244
316
  export {};
@@ -26,6 +26,7 @@ __export(types_exports, {
26
26
  CompiledSubscriptionKind: () => CompiledSubscriptionKind,
27
27
  DataSource: () => DataSource,
28
28
  PaginateMode: () => PaginateMode,
29
+ PendingValue: () => PendingValue,
29
30
  RefetchUpdateMode: () => RefetchUpdateMode,
30
31
  fragmentKey: () => fragmentKey
31
32
  });
@@ -61,6 +62,7 @@ const DataSource = {
61
62
  Ssr: "ssr"
62
63
  };
63
64
  const fragmentKey = " $fragments";
65
+ const PendingValue = Symbol("houdini_loading");
64
66
  // Annotate the CommonJS export names for ESM import in node:
65
67
  0 && (module.exports = {
66
68
  ArtifactKind,
@@ -71,6 +73,7 @@ const fragmentKey = " $fragments";
71
73
  CompiledSubscriptionKind,
72
74
  DataSource,
73
75
  PaginateMode,
76
+ PendingValue,
74
77
  RefetchUpdateMode,
75
78
  fragmentKey
76
79
  });
@@ -3,7 +3,6 @@ import type { GraphQLObject, GraphQLValue, NestedList, SubscriptionSelection, Su
3
3
  import { GarbageCollector } from './gc';
4
4
  import type { ListCollection } from './lists';
5
5
  import { ListManager } from './lists';
6
- import { SchemaManager } from './schema';
7
6
  import { StaleManager } from './staleManager';
8
7
  import type { Layer, LayerID } from './storage';
9
8
  import { InMemoryStorage } from './storage';
@@ -57,15 +56,13 @@ declare class CacheInternal {
57
56
  cache: Cache;
58
57
  lifetimes: GarbageCollector;
59
58
  staleManager: StaleManager;
60
- schema: SchemaManager;
61
- constructor({ storage, subscriptions, lists, cache, lifetimes, staleManager, schema, disabled, config, }: {
59
+ constructor({ storage, subscriptions, lists, cache, lifetimes, staleManager, disabled, config, }: {
62
60
  storage: InMemoryStorage;
63
61
  subscriptions: InMemorySubscriptions;
64
62
  lists: ListManager;
65
63
  cache: Cache;
66
64
  lifetimes: GarbageCollector;
67
65
  staleManager: StaleManager;
68
- schema: SchemaManager;
69
66
  disabled: boolean;
70
67
  config?: ConfigFile;
71
68
  });
@@ -87,12 +84,13 @@ declare class CacheInternal {
87
84
  forceNotify?: boolean;
88
85
  forceStale?: boolean;
89
86
  }): FieldSelection[];
90
- getSelection({ selection, parent, variables, stepsFromConnection, ignoreMasking, fullCheck, }: {
87
+ getSelection({ selection, parent, variables, stepsFromConnection, ignoreMasking, fullCheck, loading: generateLoading, }: {
91
88
  selection: SubscriptionSelection;
92
89
  parent?: string;
93
90
  variables?: {};
94
91
  stepsFromConnection?: number | null;
95
92
  ignoreMasking?: boolean;
93
+ loading?: boolean;
96
94
  fullCheck?: boolean;
97
95
  }): {
98
96
  data: GraphQLObject | null;
@@ -104,13 +102,14 @@ declare class CacheInternal {
104
102
  id(type: string, id: string): string | null;
105
103
  idFields(type: string): string[];
106
104
  computeID(type: string, data: any): string;
107
- hydrateNestedList({ fields, variables, linkedList, stepsFromConnection, ignoreMasking, fullCheck, }: {
105
+ hydrateNestedList({ fields, variables, linkedList, stepsFromConnection, ignoreMasking, fullCheck, loading, }: {
108
106
  fields: SubscriptionSelection;
109
107
  variables?: {};
110
108
  linkedList: NestedList;
111
109
  stepsFromConnection: number | null;
112
110
  ignoreMasking: boolean;
113
111
  fullCheck?: boolean;
112
+ loading?: boolean;
114
113
  }): {
115
114
  data: NestedList<GraphQLValue>;
116
115
  partial: boolean;
@@ -1,4 +1,4 @@
1
- import { computeKey } from "../lib";
1
+ import { computeKey, PendingValue } from "../lib";
2
2
  import { computeID, defaultConfigValues, keyFieldsForType, getCurrentConfig } from "../lib/config";
3
3
  import { deepEquals } from "../lib/deepEquals";
4
4
  import { flatten } from "../lib/flatten";
@@ -6,7 +6,6 @@ import { getFieldsForType } from "../lib/selection";
6
6
  import { fragmentKey } from "../lib/types";
7
7
  import { GarbageCollector } from "./gc";
8
8
  import { ListManager } from "./lists";
9
- import { SchemaManager } from "./schema";
10
9
  import { StaleManager } from "./staleManager";
11
10
  import { InMemoryStorage } from "./storage";
12
11
  import { evaluateKey } from "./stuff";
@@ -21,7 +20,6 @@ class Cache {
21
20
  lists: new ListManager(this, rootID),
22
21
  lifetimes: new GarbageCollector(this),
23
22
  staleManager: new StaleManager(this),
24
- schema: new SchemaManager(this),
25
23
  disabled: disabled ?? typeof globalThis.window === "undefined"
26
24
  });
27
25
  if (Object.keys(config).length > 0) {
@@ -132,7 +130,6 @@ class CacheInternal {
132
130
  cache;
133
131
  lifetimes;
134
132
  staleManager;
135
- schema;
136
133
  constructor({
137
134
  storage,
138
135
  subscriptions,
@@ -140,7 +137,6 @@ class CacheInternal {
140
137
  cache,
141
138
  lifetimes,
142
139
  staleManager,
143
- schema,
144
140
  disabled,
145
141
  config
146
142
  }) {
@@ -150,7 +146,6 @@ class CacheInternal {
150
146
  this.cache = cache;
151
147
  this.lifetimes = lifetimes;
152
148
  this.staleManager = staleManager;
153
- this.schema = schema;
154
149
  this._config = config;
155
150
  this._disabled = disabled;
156
151
  try {
@@ -180,7 +175,11 @@ class CacheInternal {
180
175
  if (this._disabled) {
181
176
  return [];
182
177
  }
183
- let targetSelection = getFieldsForType(selection, data["__typename"]);
178
+ let targetSelection = getFieldsForType(
179
+ selection,
180
+ data["__typename"],
181
+ false
182
+ );
184
183
  for (const [field, value] of Object.entries(data)) {
185
184
  if (!selection || !targetSelection[field]) {
186
185
  continue;
@@ -191,17 +190,9 @@ class CacheInternal {
191
190
  selection: fieldSelection,
192
191
  operations,
193
192
  abstract: isAbstract,
194
- updates,
195
- nullable
193
+ updates
196
194
  } = targetSelection[field];
197
195
  const key = evaluateKey(keyRaw, variables);
198
- this.schema.setFieldType({
199
- parent,
200
- key: keyRaw,
201
- type: linkedType,
202
- nullable,
203
- link: !!fieldSelection
204
- });
205
196
  if (value && typeof value === "object" && "__typename" in value && value["__typename"]) {
206
197
  linkedType = value["__typename"];
207
198
  }
@@ -447,32 +438,49 @@ class CacheInternal {
447
438
  variables,
448
439
  stepsFromConnection = null,
449
440
  ignoreMasking,
450
- fullCheck = false
441
+ fullCheck = false,
442
+ loading: generateLoading
451
443
  }) {
452
444
  if (parent === null) {
453
445
  return { data: null, partial: false, stale: false, hasData: true };
454
446
  }
455
447
  const target = {};
456
448
  if (selection.fragments) {
457
- target[fragmentKey] = Object.fromEntries(
458
- Object.entries(selection.fragments).map(([key, value]) => [
459
- key,
460
- {
461
- parent,
462
- variables: evaluateFragmentVariables(value, variables ?? {})
463
- }
464
- ])
465
- );
449
+ target[fragmentKey] = {
450
+ loading: Boolean(generateLoading),
451
+ values: Object.fromEntries(
452
+ Object.entries(selection.fragments).filter(([, value]) => !generateLoading || value.loading).map(([key, value]) => [
453
+ key,
454
+ {
455
+ parent,
456
+ variables: evaluateFragmentVariables(
457
+ value.arguments,
458
+ variables ?? {}
459
+ )
460
+ }
461
+ ])
462
+ )
463
+ };
466
464
  }
467
465
  let hasData = !!selection.fragments;
468
466
  let partial = false;
469
467
  let cascadeNull = false;
470
468
  let stale = false;
471
469
  const typename = this.storage.get(parent, "__typename").value;
472
- let targetSelection = getFieldsForType(selection, typename);
470
+ let targetSelection = getFieldsForType(selection, typename, !!generateLoading);
473
471
  for (const [
474
472
  attributeName,
475
- { type, keyRaw, selection: fieldSelection, nullable, list, visible, directives }
473
+ {
474
+ type,
475
+ keyRaw,
476
+ selection: fieldSelection,
477
+ nullable,
478
+ list,
479
+ visible,
480
+ directives,
481
+ loading: fieldLoading,
482
+ abstractHasRequired
483
+ }
476
484
  ] of Object.entries(targetSelection)) {
477
485
  if (!visible && !ignoreMasking && !fullCheck) {
478
486
  continue;
@@ -495,11 +503,17 @@ class CacheInternal {
495
503
  }
496
504
  const fieldTarget = visible || ignoreMasking ? target : {};
497
505
  const key = evaluateKey(keyRaw, variables);
498
- const { value } = this.storage.get(parent, key);
506
+ if (generateLoading && !fieldLoading) {
507
+ continue;
508
+ }
509
+ let { value } = this.storage.get(parent, key);
499
510
  const dt_field = this.staleManager.getFieldTime(parent, key);
500
511
  if (dt_field === null) {
501
512
  stale = true;
502
513
  }
514
+ if (generateLoading) {
515
+ value = void 0;
516
+ }
503
517
  let nextStep = stepsFromConnection;
504
518
  if (nextStep !== null) {
505
519
  if (nextStep >= 2) {
@@ -515,7 +529,10 @@ class CacheInternal {
515
529
  if (typeof value === "undefined" && !embeddedCursor) {
516
530
  partial = true;
517
531
  }
518
- if (typeof value === "undefined" || value === null) {
532
+ if (generateLoading && fieldLoading?.kind === "value") {
533
+ fieldTarget[attributeName] = PendingValue;
534
+ hasData = true;
535
+ } else if (!generateLoading && typeof value === "undefined" || value === null) {
519
536
  fieldTarget[attributeName] = null;
520
537
  if (typeof value !== "undefined") {
521
538
  hasData = true;
@@ -535,7 +552,8 @@ class CacheInternal {
535
552
  linkedList: value,
536
553
  stepsFromConnection: nextStep,
537
554
  ignoreMasking: !!ignoreMasking,
538
- fullCheck
555
+ fullCheck,
556
+ loading: generateLoading
539
557
  });
540
558
  fieldTarget[attributeName] = listValue.data;
541
559
  if (listValue.partial) {
@@ -554,7 +572,8 @@ class CacheInternal {
554
572
  variables,
555
573
  stepsFromConnection: nextStep,
556
574
  ignoreMasking,
557
- fullCheck
575
+ fullCheck,
576
+ loading: generateLoading
558
577
  });
559
578
  fieldTarget[attributeName] = objectFields.data;
560
579
  if (objectFields.partial) {
@@ -567,13 +586,27 @@ class CacheInternal {
567
586
  hasData = true;
568
587
  }
569
588
  }
589
+ if (generateLoading && fieldLoading?.list) {
590
+ fieldTarget[attributeName] = wrapInLists(
591
+ Array.from({ length: fieldLoading.list.count }).fill(
592
+ fieldTarget[attributeName]
593
+ ),
594
+ fieldLoading.list.depth - 1
595
+ );
596
+ }
570
597
  if (fieldTarget[attributeName] === null && !nullable && !embeddedCursor) {
571
- cascadeNull = true;
598
+ if (abstractHasRequired) {
599
+ target[attributeName] = {
600
+ __typename: "@required field missing; don't match this"
601
+ };
602
+ } else {
603
+ cascadeNull = true;
604
+ }
572
605
  }
573
606
  }
574
607
  return {
575
608
  data: cascadeNull ? null : target,
576
- partial: hasData && partial,
609
+ partial: !generateLoading && hasData && partial,
577
610
  stale: hasData && stale,
578
611
  hasData
579
612
  };
@@ -600,7 +633,8 @@ class CacheInternal {
600
633
  linkedList,
601
634
  stepsFromConnection,
602
635
  ignoreMasking,
603
- fullCheck
636
+ fullCheck,
637
+ loading
604
638
  }) {
605
639
  const result = [];
606
640
  let partialData = false;
@@ -614,7 +648,8 @@ class CacheInternal {
614
648
  linkedList: entry,
615
649
  stepsFromConnection,
616
650
  ignoreMasking,
617
- fullCheck
651
+ fullCheck,
652
+ loading
618
653
  });
619
654
  result.push(nestedValue.data);
620
655
  if (nestedValue.partial) {
@@ -637,7 +672,8 @@ class CacheInternal {
637
672
  variables,
638
673
  stepsFromConnection,
639
674
  ignoreMasking,
640
- fullCheck
675
+ fullCheck,
676
+ loading
641
677
  });
642
678
  result.push(data);
643
679
  if (partial) {
@@ -744,6 +780,12 @@ function evaluateFragmentVariables(variables, args) {
744
780
  Object.entries(variables).map(([key, value]) => [key, fragmentVariableValue(value, args)])
745
781
  );
746
782
  }
783
+ function wrapInLists(target, count = 0) {
784
+ if (count === 0) {
785
+ return target;
786
+ }
787
+ return wrapInLists([target], count - 1);
788
+ }
747
789
  function fragmentVariableValue(value, args) {
748
790
  if (value.kind === "StringValue") {
749
791
  return value.value;
@@ -17,7 +17,7 @@ class InMemorySubscriptions {
17
17
  parentType
18
18
  }) {
19
19
  const __typename = this.cache._internal_unstable.storage.get(parent, "__typename").value;
20
- let targetSelection = getFieldsForType(selection, __typename);
20
+ let targetSelection = getFieldsForType(selection, __typename, false);
21
21
  for (const fieldSelection of Object.values(targetSelection || {})) {
22
22
  const {
23
23
  keyRaw,
@@ -34,7 +34,7 @@ class InMemorySubscriptions {
34
34
  let targetSelection2;
35
35
  if (innerSelection) {
36
36
  const __typename2 = this.cache._internal_unstable.storage.get(parent, "__typename").value;
37
- targetSelection2 = getFieldsForType(innerSelection, __typename2);
37
+ targetSelection2 = getFieldsForType(innerSelection, __typename2, false);
38
38
  }
39
39
  this.addFieldSubscription({
40
40
  id: parent,
@@ -145,7 +145,7 @@ class InMemorySubscriptions {
145
145
  filters
146
146
  } = selection;
147
147
  const key = evaluateKey(keyRaw, variables);
148
- const fieldSelection = innerSelection ? getFieldsForType(innerSelection, parentType) : void 0;
148
+ const fieldSelection = innerSelection ? getFieldsForType(innerSelection, parentType, false) : void 0;
149
149
  this.addFieldSubscription({
150
150
  id: parent,
151
151
  key,
@@ -175,7 +175,7 @@ class InMemorySubscriptions {
175
175
  linkedRecord,
176
176
  "__typename"
177
177
  ).value;
178
- let targetSelection2 = getFieldsForType(childSelection, __typename);
178
+ let targetSelection2 = getFieldsForType(childSelection, __typename, false);
179
179
  this.addMany({
180
180
  parent: linkedRecord,
181
181
  variables,
@@ -194,7 +194,7 @@ class InMemorySubscriptions {
194
194
  visited.push(id);
195
195
  const linkedIDs = [];
196
196
  const __typename = this.cache._internal_unstable.storage.get(id, "__typename").value;
197
- let targetSelection = getFieldsForType(selection, __typename);
197
+ let targetSelection = getFieldsForType(selection, __typename, false);
198
198
  for (const fieldSelection of Object.values(targetSelection || {})) {
199
199
  const key = evaluateKey(fieldSelection.keyRaw, variables);
200
200
  this.removeSubscribers(id, key, targets);
@@ -5,6 +5,9 @@ import { Writable } from '../lib/store';
5
5
  import type { DocumentArtifact, QueryResult, GraphQLObject, SubscriptionSpec, CachePolicies } from '../lib/types';
6
6
  export declare class DocumentStore<_Data extends GraphQLObject, _Input extends Record<string, any>> extends Writable<QueryResult<_Data, _Input>> {
7
7
  #private;
8
+ pendingPromise: {
9
+ then: (val: any) => void;
10
+ } | null;
8
11
  constructor({ artifact, plugins, pipeline, client, cache, initialValue, fetching, }: {
9
12
  artifact: DocumentArtifact;
10
13
  plugins?: ClientHooks[];
@@ -15,6 +15,7 @@ class DocumentStore extends Writable {
15
15
  #plugins;
16
16
  #lastVariables;
17
17
  #lastContext = null;
18
+ pendingPromise = null;
18
19
  constructor({
19
20
  artifact,
20
21
  plugins,
@@ -47,7 +48,15 @@ class DocumentStore extends Writable {
47
48
  this.#plugins = pipeline ?? [
48
49
  cachePolicy({
49
50
  enabled: cache,
50
- setFetching: (fetching2) => this.update((state) => ({ ...state, fetching: fetching2 }))
51
+ setFetching: (fetching2, data) => {
52
+ this.update((state) => {
53
+ const newState = { ...state, fetching: fetching2 };
54
+ if (fetching2 && data) {
55
+ newState.data = data;
56
+ }
57
+ return newState;
58
+ });
59
+ }
51
60
  })(),
52
61
  ...plugins ?? []
53
62
  ];
@@ -87,7 +96,7 @@ class DocumentStore extends Writable {
87
96
  const draft = context.draft();
88
97
  draft.variables = variables ?? null;
89
98
  context = context.apply(draft, false);
90
- return await new Promise((resolve, reject) => {
99
+ const promise = new Promise((resolve, reject) => {
91
100
  const state = {
92
101
  setup,
93
102
  currentStep: 0,
@@ -96,12 +105,17 @@ class DocumentStore extends Writable {
96
105
  promise: {
97
106
  resolved: false,
98
107
  resolve,
99
- reject
108
+ reject,
109
+ then: (...args) => promise.then(...args)
100
110
  },
101
111
  context
102
112
  };
113
+ if (this.pendingPromise === null) {
114
+ this.pendingPromise = state.promise;
115
+ }
103
116
  this.#step("forward", state);
104
117
  });
118
+ return await promise;
105
119
  }
106
120
  async cleanup() {
107
121
  for (const plugin of this.#plugins) {
@@ -220,16 +234,15 @@ class DocumentStore extends Writable {
220
234
  value
221
235
  );
222
236
  }
237
+ if (!ctx.silenceEcho || value.data !== this.state.data) {
238
+ this.set(value);
239
+ }
223
240
  if (!ctx.promise.resolved) {
224
241
  ctx.promise.resolve(value);
225
242
  ctx.promise.resolved = true;
226
243
  }
227
244
  this.#lastContext = ctx.context.draft();
228
245
  this.#lastVariables = this.#lastContext.stuff.inputs.marshaled;
229
- if (ctx.silenceEcho && value.data === this.state.data) {
230
- return;
231
- }
232
- this.set(value);
233
246
  }
234
247
  }
235
248
  class ClientPluginContextWrapper {
@@ -1,11 +1,11 @@
1
1
  /// <reference path="../../../../../houdini.d.ts" />
2
2
  import type { DocumentArtifact, GraphQLObject, NestedList } from '../lib/types';
3
- import type { ClientPlugin, ClientHooks } from './documentStore';
3
+ import type { ClientHooks, ClientPlugin } from './documentStore';
4
4
  import { DocumentStore } from './documentStore';
5
- import { type FetchParamFn, type ThrowOnErrorParams } from './plugins';
6
- export { DocumentStore, type ClientPlugin } from './documentStore';
5
+ import type { FetchParamFn, ThrowOnErrorOperations, ThrowOnErrorParams } from './plugins';
6
+ export { DocumentStore, type ClientPlugin, type SendParams } from './documentStore';
7
7
  export { fetch, mutation, query, subscription } from './plugins';
8
- type ConstructorArgs = {
8
+ export type HoudiniClientConstructorArgs = {
9
9
  url: string;
10
10
  fetchParams?: FetchParamFn;
11
11
  plugins?: NestedList<ClientPlugin>;
@@ -19,9 +19,10 @@ export type ObserveParams<_Data extends GraphQLObject, _Artifact extends Documen
19
19
  fetching?: boolean;
20
20
  };
21
21
  export declare class HoudiniClient {
22
- #private;
23
22
  url: string;
24
- constructor({ url, fetchParams, plugins, pipeline, throwOnError }: ConstructorArgs);
23
+ readonly plugins: ClientPlugin[];
24
+ readonly throwOnError_operations: ThrowOnErrorOperations[];
25
+ constructor({ url, fetchParams, plugins, pipeline, throwOnError, }: HoudiniClientConstructorArgs);
25
26
  observe<_Data extends GraphQLObject, _Input extends Record<string, any>>({ artifact, cache, initialValue, fetching, }: ObserveParams<_Data>): DocumentStore<_Data, _Input>;
26
27
  }
27
28
  export declare function createPluginHooks(plugins: ClientPlugin[]): ClientHooks[];