houdini 2.0.0-next.34 → 2.0.0-next.36

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.
package/build/cmd/init.js CHANGED
@@ -472,12 +472,12 @@ async function packageJSON(targetPath, frameworkInfo) {
472
472
  }
473
473
  packageJSON2.devDependencies = {
474
474
  ...packageJSON2.devDependencies,
475
- houdini: "^2.0.0-next.34"
475
+ houdini: "^2.0.0-next.36"
476
476
  };
477
477
  if (frameworkInfo.framework === "svelte" || frameworkInfo.framework === "kit") {
478
478
  packageJSON2.devDependencies = {
479
479
  ...packageJSON2.devDependencies,
480
- "houdini-svelte": "^3.0.0-next.33"
480
+ "houdini-svelte": "^3.0.0-next.34"
481
481
  };
482
482
  } else {
483
483
  throw new Error(`Unmanaged framework: "${JSON.stringify(frameworkInfo)}"`);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "houdini",
3
- "version": "2.0.0-next.34",
3
+ "version": "2.0.0-next.36",
4
4
  "description": "The disappearing GraphQL clients",
5
5
  "keywords": [
6
6
  "typescript",
@@ -10,6 +10,7 @@ export type RouteParam = {
10
10
  type?: string;
11
11
  };
12
12
  export type ParamMatcher = (param: string) => boolean;
13
+ export declare function find_prefix_match<_ComponentType>(manifest: RouterManifest<_ComponentType>, url: string): RouterPageManifest<_ComponentType> | null;
13
14
  export declare function find_match<_ComponentType>(config: ConfigFile, manifest: RouterManifest<_ComponentType>, current: string, allowNull: true): [RouterPageManifest<_ComponentType> | null, GraphQLVariables];
14
15
  export declare function find_match<_ComponentType>(config: ConfigFile, manifest: RouterManifest<_ComponentType>, current: string, allowNull?: false): [RouterPageManifest<_ComponentType>, GraphQLVariables];
15
16
  /**
@@ -1,4 +1,30 @@
1
1
  const param_pattern = /^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/;
2
+ function find_prefix_match(manifest, url) {
3
+ const urlSegments = url.split("/").filter(Boolean);
4
+ let best = null;
5
+ let bestCount = -1;
6
+ for (const page of Object.values(manifest.pages)) {
7
+ const pageSegments = page.url.split("/").filter(Boolean);
8
+ let staticCount = 0;
9
+ for (const seg of pageSegments) {
10
+ if (seg.startsWith("[") || seg.startsWith("(") || seg === "*") break;
11
+ staticCount++;
12
+ }
13
+ if (staticCount > urlSegments.length) continue;
14
+ let matches = true;
15
+ for (let i = 0; i < staticCount; i++) {
16
+ if (pageSegments[i] !== urlSegments[i]) {
17
+ matches = false;
18
+ break;
19
+ }
20
+ }
21
+ if (matches && staticCount > bestCount) {
22
+ bestCount = staticCount;
23
+ best = page;
24
+ }
25
+ }
26
+ return best;
27
+ }
2
28
  function find_match(config, manifest, current, allowNull = true) {
3
29
  let match = null;
4
30
  let matchVariables = null;
@@ -161,6 +187,7 @@ function parseScalar(config, type, value) {
161
187
  export {
162
188
  exec,
163
189
  find_match,
190
+ find_prefix_match,
164
191
  get_route_segments,
165
192
  parseScalar,
166
193
  parse_page_pattern
@@ -17,6 +17,7 @@ export declare function _serverHandler<ComponentType = unknown>({ schema, server
17
17
  on_render: (args: {
18
18
  url: string;
19
19
  match: RouterPageManifest<ComponentType> | null;
20
+ is404: boolean;
20
21
  manifest: RouterManifest<unknown>;
21
22
  session: App.Session;
22
23
  componentCache: Record<string, any>;
@@ -3,7 +3,7 @@ import {
3
3
  YogaServer
4
4
  } from "graphql-yoga";
5
5
  import { serialize as encodeCookie } from "./cookies.js";
6
- import { find_match } from "./match.js";
6
+ import { find_match, find_prefix_match } from "./match.js";
7
7
  import { get_session, handle_request, session_cookie_name } from "./session.js";
8
8
  function _serverHandler({
9
9
  schema,
@@ -70,10 +70,13 @@ function _serverHandler({
70
70
  if (authResponse) {
71
71
  return authResponse;
72
72
  }
73
- const [match] = find_match(config_file, manifest, url);
73
+ const [exactMatch] = find_match(config_file, manifest, url);
74
+ const is404 = !exactMatch;
75
+ const match = exactMatch ?? find_prefix_match(manifest, url);
74
76
  const rendered = await on_render({
75
77
  url,
76
78
  match,
79
+ is404,
77
80
  session: await get_session(request.headers, session_keys),
78
81
  manifest,
79
82
  componentCache
@@ -10,7 +10,7 @@ function evaluateKey(key, variables = null) {
10
10
  continue;
11
11
  }
12
12
  const value = variables?.[varName.slice(1)];
13
- evaluated += typeof value !== "undefined" ? JSON.stringify(value) : "undefined";
13
+ evaluated += JSON.stringify(value ?? null);
14
14
  varName = "";
15
15
  }
16
16
  if (char === "$" && !inString) {
@@ -1,12 +1,14 @@
1
1
  import type { SendParams } from './documentStore.js';
2
2
  import type { CursorHandlers, FetchFn, GraphQLObject, GraphQLVariables, QueryArtifact, QueryResult, FetchParams } from './types.js';
3
- export declare function cursorHandlers<_Data extends GraphQLObject, _Input extends GraphQLVariables | null | undefined>({ artifact, fetchUpdate: parentFetchUpdate, fetch: parentFetch, getState, getVariables, getSession, }: {
3
+ export declare function cursorHandlers<_Data extends GraphQLObject, _Input extends GraphQLVariables | null | undefined>({ artifact, fetchUpdate: parentFetchUpdate, fetch: parentFetch, getState, getVariables, getSession, previousCursors, nextCursors, }: {
4
4
  artifact: QueryArtifact;
5
5
  getState: () => _Data | null;
6
6
  getVariables: () => NonNullable<_Input>;
7
7
  getSession: () => Promise<App.Session>;
8
8
  fetch: FetchFn<_Data, _Input>;
9
9
  fetchUpdate: (arg: SendParams, updates: string[]) => ReturnType<FetchFn<_Data, _Input>>;
10
+ previousCursors?: (string | null)[];
11
+ nextCursors?: (string | null)[];
10
12
  }): CursorHandlers<_Data, _Input>;
11
13
  export declare function offsetHandlers<_Data extends GraphQLObject, _Input extends GraphQLVariables | null | undefined>({ artifact, storeName: _storeName, getState, getVariables, fetch: parentFetch, fetchUpdate: parentFetchUpdate, getSession, }: {
12
14
  artifact: QueryArtifact;
@@ -1,5 +1,6 @@
1
1
  import { deepEquals } from "./deepEquals.js";
2
2
  import { countPage, extractPageInfo, missingPageSizeError } from "./pageInfo.js";
3
+ import { defaultConfigValues, getCurrentConfig, keyFieldsForType } from "./config.js";
3
4
  import { CachePolicy, DataSource } from "./types.js";
4
5
  function cursorHandlers({
5
6
  artifact,
@@ -7,8 +8,23 @@ function cursorHandlers({
7
8
  fetch: parentFetch,
8
9
  getState,
9
10
  getVariables,
10
- getSession
11
+ getSession,
12
+ previousCursors = [],
13
+ nextCursors = []
11
14
  }) {
15
+ const targetType = artifact.refetch?.targetType;
16
+ const getEntityVars = () => {
17
+ if (!targetType || targetType === "Query") return {};
18
+ const config = defaultConfigValues(getCurrentConfig());
19
+ const typeConfig = config.types?.[targetType];
20
+ const state = getState();
21
+ if (!state) return {};
22
+ if (typeConfig?.resolve?.arguments) {
23
+ return typeConfig.resolve.arguments(state) ?? {};
24
+ }
25
+ const keys = keyFieldsForType(config, targetType);
26
+ return Object.fromEntries(keys.map((key) => [key, state[key]]));
27
+ };
12
28
  const loadPage = async ({
13
29
  pageSizeVar,
14
30
  input,
@@ -18,6 +34,8 @@ function cursorHandlers({
18
34
  where
19
35
  }) => {
20
36
  const loadVariables = {
37
+ ...artifact.input?.defaults ?? {},
38
+ ...getEntityVars(),
21
39
  ...getVariables(),
22
40
  ...input
23
41
  };
@@ -25,12 +43,13 @@ function cursorHandlers({
25
43
  throw missingPageSizeError(functionName);
26
44
  }
27
45
  const isSinglePage = artifact.refetch?.mode === "SinglePage";
46
+ const policy = isSinglePage ? artifact.policy : CachePolicy.NetworkOnly;
28
47
  return (isSinglePage ? parentFetch : parentFetchUpdate)(
29
48
  {
30
49
  variables: loadVariables,
31
50
  fetch,
32
51
  metadata,
33
- policy: isSinglePage ? artifact.policy : CachePolicy.NetworkOnly,
52
+ policy,
34
53
  session: await getSession()
35
54
  },
36
55
  isSinglePage ? [] : [where === "start" ? "prepend" : "append"]
@@ -46,6 +65,51 @@ function cursorHandlers({
46
65
  fetch,
47
66
  metadata
48
67
  } = {}) => {
68
+ const isSinglePage = artifact.refetch?.mode === "SinglePage";
69
+ const direction = artifact.refetch?.direction;
70
+ if (isSinglePage && direction === "backward") {
71
+ if (nextCursors.length === 0) {
72
+ return Promise.resolve({
73
+ data: getState(),
74
+ errors: null,
75
+ fetching: false,
76
+ partial: false,
77
+ stale: false,
78
+ source: DataSource.Cache,
79
+ variables: getVariables()
80
+ });
81
+ }
82
+ const beforeCursor = nextCursors.pop();
83
+ return loadPage({
84
+ pageSizeVar: "last",
85
+ functionName: "loadNextPage",
86
+ input: {
87
+ before: beforeCursor,
88
+ last: first ?? artifact.refetch.pageSize,
89
+ first: null,
90
+ after: null
91
+ },
92
+ fetch,
93
+ metadata,
94
+ where: "start"
95
+ });
96
+ }
97
+ if (isSinglePage && direction === "both" && nextCursors.length > 0) {
98
+ const beforeCursor = nextCursors.pop();
99
+ return loadPage({
100
+ pageSizeVar: "last",
101
+ functionName: "loadNextPage",
102
+ input: {
103
+ before: beforeCursor,
104
+ last: first ?? artifact.refetch.pageSize,
105
+ first: null,
106
+ after: null
107
+ },
108
+ fetch,
109
+ metadata,
110
+ where: "start"
111
+ });
112
+ }
49
113
  const currentPageInfo = getPageInfo();
50
114
  if (!currentPageInfo.hasNextPage) {
51
115
  return Promise.resolve({
@@ -58,6 +122,9 @@ function cursorHandlers({
58
122
  variables: getVariables()
59
123
  });
60
124
  }
125
+ if (isSinglePage && (direction === "forward" || direction === "both")) {
126
+ previousCursors.push(getVariables()?.after ?? null);
127
+ }
61
128
  const input = {
62
129
  first: first ?? artifact.refetch.pageSize,
63
130
  after: after ?? currentPageInfo.endCursor,
@@ -79,6 +146,35 @@ function cursorHandlers({
79
146
  fetch,
80
147
  metadata
81
148
  } = {}) => {
149
+ const isSinglePage = artifact.refetch?.mode === "SinglePage";
150
+ const direction = artifact.refetch?.direction;
151
+ if (isSinglePage && (direction === "forward" || direction === "both") && previousCursors.length > 0) {
152
+ const afterCursor = previousCursors.pop();
153
+ return loadPage({
154
+ pageSizeVar: "first",
155
+ functionName: "loadPreviousPage",
156
+ input: {
157
+ after: afterCursor,
158
+ first: last ?? artifact.refetch.pageSize,
159
+ before: null,
160
+ last: null
161
+ },
162
+ fetch,
163
+ metadata,
164
+ where: "end"
165
+ });
166
+ }
167
+ if (isSinglePage && direction === "forward") {
168
+ return Promise.resolve({
169
+ data: getState(),
170
+ errors: null,
171
+ fetching: false,
172
+ partial: false,
173
+ stale: false,
174
+ source: DataSource.Cache,
175
+ variables: getVariables()
176
+ });
177
+ }
82
178
  const currentPageInfo = getPageInfo();
83
179
  if (!currentPageInfo.hasPreviousPage) {
84
180
  return Promise.resolve({
@@ -91,6 +187,11 @@ function cursorHandlers({
91
187
  variables: getVariables()
92
188
  });
93
189
  }
190
+ const pVars = getVariables();
191
+ const isForwardPage = pVars?.first != null || pVars?.after != null;
192
+ if (isSinglePage && (direction === "backward" || direction === "both") && !isForwardPage) {
193
+ nextCursors.push(pVars?.before ?? null);
194
+ }
94
195
  const input = {
95
196
  before: before ?? currentPageInfo.startCursor,
96
197
  last: last ?? artifact.refetch.pageSize,
package/build/vite/hmr.js CHANGED
@@ -283,27 +283,20 @@ function document_hmr(ctx) {
283
283
  { $task_id: task_id }
284
284
  );
285
285
  let results;
286
- let taskDocCount = 0;
287
286
  try {
288
287
  results = await run_pipeline(compiler.trigger_hook, {
289
288
  task_id,
290
289
  after: "AfterExtract"
291
290
  });
292
- taskDocCount = ctx.db.get(
293
- `SELECT COUNT(DISTINCT d.id) as count
294
- FROM documents d
295
- JOIN raw_documents rd ON rd.id = d.raw_document
296
- WHERE rd.current_task = ?`,
297
- [task_id]
298
- )?.count ?? 0;
299
291
  } finally {
300
292
  ctx.db.run(
301
293
  `UPDATE raw_documents SET current_task = NULL WHERE current_task = ?`,
302
294
  [task_id]
303
295
  );
304
296
  }
297
+ const changedDocCount = Object.values(results.GenerateDocuments || {}).flat().length;
305
298
  console.log(
306
- `\u{1F3A9} Updated ${taskDocCount} ${taskDocCount === 1 ? "document" : "documents"}`
299
+ `\u{1F3A9} Updated ${changedDocCount} ${changedDocCount === 1 ? "document" : "documents"}`
307
300
  );
308
301
  const updated_modules = [
309
302
  ...Object.values(results.GenerateDocuments || {}).flat(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "houdini",
3
- "version": "2.0.0-next.34",
3
+ "version": "2.0.0-next.36",
4
4
  "description": "The disappearing GraphQL clients",
5
5
  "keywords": [
6
6
  "typescript",
@@ -50,7 +50,7 @@
50
50
  "recast": "^0.23.11",
51
51
  "sql.js": "^1.14.1",
52
52
  "ws": "^8.21.0",
53
- "houdini-core": "^2.0.0-next.22"
53
+ "houdini-core": "^2.0.0-next.23"
54
54
  },
55
55
  "peerDependencies": {
56
56
  "graphql": ">=16",