@sveltejs/kit 1.0.0-next.476 → 1.0.0-next.479

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.
@@ -1,27 +1,230 @@
1
- import fs from 'fs';
2
1
  import path from 'path';
3
2
  import { loadConfigFromFile, loadEnv, normalizePath } from 'vite';
4
3
  import { runtime_directory } from '../../core/utils.js';
5
4
  import { posixify } from '../../utils/filesystem.js';
6
5
 
7
- const illegal_imports = new Set([
8
- '/@id/__x00__$env/dynamic/private', //dev
9
- '\0$env/dynamic/private', // prod
10
- '/@id/__x00__$env/static/private', // dev
11
- '\0$env/static/private' // prod
12
- ]);
6
+ class IllegalModuleGuard {
7
+ /** @type {string} */
8
+ #lib_dir;
13
9
 
14
- /** @param {string} id */
15
- function is_illegal(id) {
16
- if (illegal_imports.has(id)) return true;
10
+ /** @type {string} */
11
+ #server_dir;
17
12
 
18
- // files outside the project root are ignored
19
- if (!id.startsWith(normalizePath(process.cwd()))) return false;
13
+ /** @type {string} */
14
+ #node_modules_dir = normalizePath(path.resolve(process.cwd(), 'node_modules'));
20
15
 
21
- // so are files inside node_modules
22
- if (id.startsWith(normalizePath(node_modules_dir))) return false;
16
+ /** @type {string} */
17
+ #cwd = normalizePath(process.cwd());
23
18
 
24
- return /.*\.server\..+/.test(path.basename(id));
19
+ /** @type {Set<string>} */
20
+ #illegal_imports = new Set([
21
+ '/@id/__x00__$env/dynamic/private', //dev
22
+ '\0$env/dynamic/private', // prod
23
+ '/@id/__x00__$env/static/private', // dev
24
+ '\0$env/static/private' // prod
25
+ ]);
26
+
27
+ /** @type {Array<import('types').ImportNode>} */
28
+ #chain = [];
29
+
30
+ /**
31
+ * @param {string} lib_dir
32
+ */
33
+ constructor(lib_dir) {
34
+ this.#lib_dir = normalizePath(lib_dir);
35
+ this.#server_dir = normalizePath(path.resolve(lib_dir, 'server'));
36
+ }
37
+
38
+ /**
39
+ * @param {import('types').ImportNode} node
40
+ * @returns {void}
41
+ */
42
+ assert_legal(node) {
43
+ this.#chain.push(node);
44
+ for (const child of node.children) {
45
+ if (this.#is_illegal(child.name)) {
46
+ this.#chain.push(child);
47
+ throw new Error(this.#format_illegal_import_chain(this.#chain));
48
+ }
49
+ this.assert_legal(child);
50
+ }
51
+ this.#chain.pop();
52
+ }
53
+
54
+ /**
55
+ * `true` if the provided ID represents a server-only module, else `false`.
56
+ * @param {string} module_id
57
+ * @returns {boolean}
58
+ */
59
+ #is_illegal(module_id) {
60
+ if (this.#is_kit_illegal(module_id) || this.#is_user_illegal(module_id)) return true;
61
+ return false;
62
+ }
63
+
64
+ /**
65
+ * `true` if the provided ID represents a Kit-defined server-only module, else `false`.
66
+ * @param {string} module_id
67
+ * @returns {boolean}
68
+ */
69
+ #is_kit_illegal(module_id) {
70
+ return this.#illegal_imports.has(module_id);
71
+ }
72
+
73
+ /**
74
+ * `true` if the provided ID represents a user-defined server-only module, else `false`.
75
+ * @param {string} module_id
76
+ * @returns {boolean}
77
+ */
78
+ #is_user_illegal(module_id) {
79
+ if (module_id.startsWith(this.#server_dir)) return true;
80
+
81
+ // files outside the project root are ignored
82
+ if (!module_id.startsWith(this.#cwd)) return false;
83
+
84
+ // so are files inside node_modules
85
+ if (module_id.startsWith(this.#node_modules_dir)) return false;
86
+
87
+ return /.*\.server\..+/.test(path.basename(module_id));
88
+ }
89
+
90
+ /**
91
+ * @param {string} str
92
+ * @param {number} times
93
+ */
94
+ #repeat(str, times) {
95
+ return new Array(times + 1).join(str);
96
+ }
97
+
98
+ /**
99
+ * Create a formatted error for an illegal import.
100
+ * @param {Array<{name: string, dynamic: boolean}>} stack
101
+ */
102
+ #format_illegal_import_chain(stack) {
103
+ const dev_virtual_prefix = '/@id/__x00__';
104
+ const prod_virtual_prefix = '\0';
105
+
106
+ stack = stack.map((file) => {
107
+ if (file.name.startsWith(dev_virtual_prefix)) {
108
+ return { ...file, name: file.name.replace(dev_virtual_prefix, '') };
109
+ }
110
+ if (file.name.startsWith(prod_virtual_prefix)) {
111
+ return { ...file, name: file.name.replace(prod_virtual_prefix, '') };
112
+ }
113
+ if (file.name.startsWith(this.#lib_dir)) {
114
+ return { ...file, name: file.name.replace(this.#lib_dir, '$lib') };
115
+ }
116
+
117
+ return { ...file, name: path.relative(process.cwd(), file.name) };
118
+ });
119
+
120
+ const pyramid = stack
121
+ .map(
122
+ (file, i) =>
123
+ `${this.#repeat(' ', i * 2)}- ${file.name} ${
124
+ file.dynamic ? '(imported by parent dynamically)' : ''
125
+ }`
126
+ )
127
+ .join('\n');
128
+
129
+ return `Cannot import ${stack.at(-1)?.name} into client-side code:\n${pyramid}`;
130
+ }
131
+ }
132
+
133
+ class RollupImportGraph {
134
+ /** @type {(id: string) => import('rollup').ModuleInfo | null} */
135
+ #node_getter;
136
+
137
+ /** @type {import('rollup').ModuleInfo} */
138
+ #module_info;
139
+
140
+ /** @type {string} */
141
+ name;
142
+
143
+ /** @type {boolean} */
144
+ dynamic;
145
+
146
+ /** @type {Set<string>} */
147
+ #seen;
148
+
149
+ /**
150
+ * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter
151
+ * @param {import('rollup').ModuleInfo} node
152
+ * @param {boolean} dynamic
153
+ * @param {Set<string>} seen
154
+ */
155
+ constructor(node_getter, node, dynamic = false, seen = new Set()) {
156
+ this.#node_getter = node_getter;
157
+ this.#module_info = node;
158
+ this.name = remove_query_from_path(normalizePath(node.id));
159
+ this.dynamic = dynamic;
160
+ this.#seen = seen;
161
+ void (/** @type {import('types').ImportNode} */ (this));
162
+ }
163
+
164
+ get children() {
165
+ return this.#children();
166
+ }
167
+
168
+ *#children() {
169
+ if (this.#seen.has(this.name)) return;
170
+ this.#seen.add(this.name);
171
+ for (const id of this.#module_info.importedIds) {
172
+ const child = this.#node_getter(id);
173
+ if (child === null) return;
174
+ yield new RollupImportGraph(this.#node_getter, child, false, this.#seen);
175
+ }
176
+ for (const id of this.#module_info.dynamicallyImportedIds) {
177
+ const child = this.#node_getter(id);
178
+ if (child === null) return;
179
+ yield new RollupImportGraph(this.#node_getter, child, true, this.#seen);
180
+ }
181
+ }
182
+ }
183
+
184
+ class ViteImportGraph {
185
+ /** @type {Set<string>} */
186
+ #module_types;
187
+
188
+ /** @type {import('vite').ModuleNode} */
189
+ #module_info;
190
+
191
+ /** @type {string} */
192
+ name;
193
+
194
+ /** @type {Set<string>} */
195
+ #seen;
196
+
197
+ /**
198
+ * @param {Set<string>} module_types Module types to analyze, eg '.js', '.ts', etc.
199
+ * @param {import('vite').ModuleNode} node
200
+ * @param {Set<string>} seen
201
+ */
202
+ constructor(module_types, node, seen = new Set()) {
203
+ this.#module_types = module_types;
204
+ this.#module_info = node;
205
+ this.name = remove_query_from_path(normalizePath(node.id ?? ''));
206
+ this.#seen = seen;
207
+ void (/** @type {import('types').ImportNode} */ (this));
208
+ }
209
+
210
+ get dynamic() {
211
+ return false;
212
+ }
213
+
214
+ get children() {
215
+ return this.#children();
216
+ }
217
+
218
+ *#children() {
219
+ if (this.#seen.has(this.name)) return;
220
+ this.#seen.add(this.name);
221
+ for (const child of this.#module_info.importedModules) {
222
+ if (!this.#module_types.has(path.extname(this.name))) {
223
+ continue;
224
+ }
225
+ yield new ViteImportGraph(this.#module_types, child, this.#seen);
226
+ }
227
+ }
25
228
  }
26
229
 
27
230
  /**
@@ -163,80 +366,6 @@ function escape_for_regexp(str) {
163
366
  return str.replace(/[.*+?^${}()|[\]\\]/g, (match) => '\\' + match);
164
367
  }
165
368
 
166
- /**
167
- * Given an entry point like [cwd]/src/hooks, returns a filename like [cwd]/src/hooks.js or [cwd]/src/hooks/index.js
168
- * @param {string} entry
169
- * @returns {string|null}
170
- */
171
- export function resolve_entry(entry) {
172
- if (fs.existsSync(entry)) {
173
- const stats = fs.statSync(entry);
174
- if (stats.isDirectory()) {
175
- return resolve_entry(path.join(entry, 'index'));
176
- }
177
-
178
- return entry;
179
- } else {
180
- const dir = path.dirname(entry);
181
-
182
- if (fs.existsSync(dir)) {
183
- const base = path.basename(entry);
184
- const files = fs.readdirSync(dir);
185
-
186
- const found = files.find((file) => file.replace(/\.[^.]+$/, '') === base);
187
-
188
- if (found) return path.join(dir, found);
189
- }
190
- }
191
-
192
- return null;
193
- }
194
-
195
- /**
196
- * @param {string} str
197
- * @param {number} times
198
- */
199
- function repeat(str, times) {
200
- return new Array(times + 1).join(str);
201
- }
202
-
203
- /**
204
- * Create a formatted error for an illegal import.
205
- * @param {Array<{name: string, dynamic: boolean}>} stack
206
- * @param {string} lib_dir
207
- */
208
- function format_illegal_import_chain(stack, lib_dir) {
209
- const dev_virtual_prefix = '/@id/__x00__';
210
- const prod_virtual_prefix = '\0';
211
-
212
- stack = stack.map((file) => {
213
- if (file.name.startsWith(dev_virtual_prefix)) {
214
- return { ...file, name: file.name.replace(dev_virtual_prefix, '') };
215
- }
216
- if (file.name.startsWith(prod_virtual_prefix)) {
217
- return { ...file, name: file.name.replace(prod_virtual_prefix, '') };
218
- }
219
- if (file.name.startsWith(lib_dir)) {
220
- return { ...file, name: file.name.replace(lib_dir, '$lib') };
221
- }
222
-
223
- return { ...file, name: path.relative(process.cwd(), file.name) };
224
- });
225
-
226
- const pyramid = stack
227
- .map(
228
- (file, i) =>
229
- `${repeat(' ', i * 2)}- ${file.name} ${
230
- file.dynamic ? '(imported by parent dynamically)' : ''
231
- }`
232
- )
233
- .join('\n');
234
-
235
- return `Cannot import ${stack.at(-1)?.name} into client-side code:\n${pyramid}`;
236
- }
237
-
238
- const node_modules_dir = path.resolve(process.cwd(), 'node_modules');
239
-
240
369
  /**
241
370
  * Load environment variables from process.env and .env files
242
371
  * @param {import('types').ValidatedKitConfig['env']} env_config
@@ -251,16 +380,6 @@ export function get_env(env_config, mode) {
251
380
  };
252
381
  }
253
382
 
254
- /**
255
- * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter
256
- * @param {import('rollup').ModuleInfo} node
257
- * @param {string} lib_dir
258
- */
259
- export function prevent_illegal_rollup_imports(node_getter, node, lib_dir) {
260
- const chain = find_illegal_rollup_imports(node_getter, node, false);
261
- if (chain) throw new Error(format_illegal_import_chain(chain, lib_dir));
262
- }
263
-
264
383
  const query_pattern = /\?.*$/s;
265
384
 
266
385
  /** @param {string} path */
@@ -268,37 +387,6 @@ function remove_query_from_path(path) {
268
387
  return path.replace(query_pattern, '');
269
388
  }
270
389
 
271
- /**
272
- * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter
273
- * @param {import('rollup').ModuleInfo} node
274
- * @param {boolean} dynamic
275
- * @param {Set<string>} seen
276
- * @returns {Array<import('types').ImportNode> | null}
277
- */
278
- const find_illegal_rollup_imports = (node_getter, node, dynamic, seen = new Set()) => {
279
- const name = remove_query_from_path(normalizePath(node.id));
280
- if (seen.has(name)) return null;
281
- seen.add(name);
282
-
283
- if (is_illegal(name)) {
284
- return [{ name, dynamic }];
285
- }
286
-
287
- for (const id of node.importedIds) {
288
- const child = node_getter(id);
289
- const chain = child && find_illegal_rollup_imports(node_getter, child, false, seen);
290
- if (chain) return [{ name, dynamic }, ...chain];
291
- }
292
-
293
- for (const id of node.dynamicallyImportedIds) {
294
- const child = node_getter(id);
295
- const chain = child && find_illegal_rollup_imports(node_getter, child, true, seen);
296
- if (chain) return [{ name, dynamic }, ...chain];
297
- }
298
-
299
- return null;
300
- };
301
-
302
390
  /**
303
391
  * Vite does some weird things with import trees in dev
304
392
  * for example, a Tailwind app.css will appear to import
@@ -308,6 +396,7 @@ const find_illegal_rollup_imports = (node_getter, node, dynamic, seen = new Set(
308
396
  */
309
397
  const get_module_types = (config_module_types) => {
310
398
  return new Set([
399
+ '',
311
400
  '.ts',
312
401
  '.js',
313
402
  '.svelte',
@@ -324,38 +413,26 @@ const get_module_types = (config_module_types) => {
324
413
 
325
414
  /**
326
415
  * Throw an error if a private module is imported from a client-side node.
327
- * @param {import('vite').ModuleNode} node
416
+ * @param {(id: string) => import('rollup').ModuleInfo | null} node_getter
417
+ * @param {import('rollup').ModuleInfo} node
328
418
  * @param {string} lib_dir
329
- * @param {Iterable<string>} module_types File extensions to analyze in addition to the defaults: `.ts`, `.js`, etc.
419
+ * @returns {void}
330
420
  */
331
- export function prevent_illegal_vite_imports(node, lib_dir, module_types) {
332
- const chain = find_illegal_vite_imports(node, get_module_types(module_types));
333
- if (chain) throw new Error(format_illegal_import_chain(chain, lib_dir));
421
+ export function prevent_illegal_rollup_imports(node_getter, node, lib_dir) {
422
+ const graph = new RollupImportGraph(node_getter, node);
423
+ const guard = new IllegalModuleGuard(lib_dir);
424
+ guard.assert_legal(graph);
334
425
  }
335
426
 
336
427
  /**
428
+ * Throw an error if a private module is imported from a client-side node.
337
429
  * @param {import('vite').ModuleNode} node
338
- * @param {Set<string>} module_types File extensions to analyze: `.ts`, `.js`, etc.
339
- * @param {Set<string>} seen
340
- * @returns {Array<import('types').ImportNode> | null}
430
+ * @param {string} lib_dir
431
+ * @param {Iterable<string>} module_types File extensions to analyze in addition to the defaults: `.ts`, `.js`, etc.
432
+ * @returns {void}
341
433
  */
342
- function find_illegal_vite_imports(node, module_types, seen = new Set()) {
343
- if (!node.id) return null; // TODO when does this happen?
344
- const name = remove_query_from_path(normalizePath(node.id));
345
-
346
- if (path.extname(name) !== '' && (seen.has(name) || !module_types.has(path.extname(name)))) {
347
- return null;
348
- }
349
- seen.add(name);
350
-
351
- if (is_illegal(name)) {
352
- return [{ name, dynamic: false }];
353
- }
354
-
355
- for (const child of node.importedModules) {
356
- const chain = child && find_illegal_vite_imports(child, module_types, seen);
357
- if (chain) return [{ name, dynamic: false }, ...chain];
358
- }
359
-
360
- return null;
434
+ export function prevent_illegal_vite_imports(node, lib_dir, module_types) {
435
+ const graph = new ViteImportGraph(get_module_types(module_types), node);
436
+ const guard = new IllegalModuleGuard(lib_dir);
437
+ guard.assert_legal(graph);
361
438
  }
@@ -16,46 +16,43 @@ const ssr = import.meta.env.SSR;
16
16
  export const applyAction = ssr ? guard('applyAction') : client.apply_action;
17
17
 
18
18
  /** @type {import('$app/forms').enhance} */
19
- export function enhance(element, submit = () => {}) {
19
+ export function enhance(form, submit = () => {}) {
20
20
  /**
21
21
  * @param {{
22
- * element: HTMLFormElement | HTMLButtonElement | HTMLInputElement;
23
- * form: HTMLFormElement;
22
+ * action: string;
24
23
  * result: import('types').ActionResult;
25
24
  * }} opts
26
25
  */
27
- const fallback_callback = async ({ element, form, result }) => {
26
+ const fallback_callback = async ({ action, result }) => {
28
27
  if (result.type === 'success') {
29
28
  await invalidateAll();
30
29
  }
31
30
 
32
- const action = element.formAction ?? form.action;
33
-
34
31
  if (location.origin + location.pathname === action.split('?')[0]) {
35
32
  applyAction(result);
36
33
  }
37
34
  };
38
35
 
39
- const form =
40
- element instanceof HTMLFormElement ? element : /** @type {HTMLFormElement} */ (element.form);
41
- if (!form) throw new Error('Element is not associated with a form');
42
-
43
36
  /** @param {SubmitEvent} event */
44
37
  async function handle_submit(event) {
45
38
  event.preventDefault();
46
39
 
47
- const action = element.formAction ?? form.action;
48
-
40
+ // We can't do submitter.formAction directly because that property is always set
41
+ const action = event.submitter?.hasAttribute('formaction')
42
+ ? /** @type {HTMLButtonElement | HTMLInputElement} */ (event.submitter).formAction
43
+ : form.action;
49
44
  const data = new FormData(form);
45
+ const controller = new AbortController();
50
46
 
51
47
  let cancelled = false;
52
48
  const cancel = () => (cancelled = true);
53
49
 
54
50
  const callback =
55
51
  submit({
56
- element,
57
- data,
52
+ action,
58
53
  cancel,
54
+ controller,
55
+ data,
59
56
  form
60
57
  }) ?? fallback_callback;
61
58
  if (cancelled) return;
@@ -69,16 +66,18 @@ export function enhance(element, submit = () => {}) {
69
66
  headers: {
70
67
  accept: 'application/json'
71
68
  },
72
- body: data
69
+ body: data,
70
+ signal: controller.signal
73
71
  });
74
72
 
75
73
  result = await response.json();
76
74
  } catch (error) {
75
+ if (/** @type {any} */ (error)?.name === 'AbortError') return;
77
76
  result = { type: 'error', error };
78
77
  }
79
78
 
80
79
  callback({
81
- element,
80
+ action,
82
81
  data,
83
82
  form,
84
83
  // @ts-expect-error generic constraints stuff we don't care about
@@ -1,5 +1,5 @@
1
1
  declare module '__GENERATED__/client-manifest.js' {
2
- import { CSRPageNodeLoader, ParamMatcher } from 'types';
2
+ import { CSRPageNodeLoader, ClientHooks, ParamMatcher } from 'types';
3
3
 
4
4
  /**
5
5
  * A list of all the error/layout/page nodes used in the app
@@ -21,4 +21,6 @@ declare module '__GENERATED__/client-manifest.js' {
21
21
  export const dictionary: Record<string, [leaf: number, layouts: number[], errors?: number[]]>;
22
22
 
23
23
  export const matchers: Record<string, ParamMatcher>;
24
+
25
+ export const hooks: ClientHooks;
24
26
  }
@@ -4,10 +4,9 @@ import { make_trackable, decode_params, normalize_path } from '../../utils/url.j
4
4
  import { find_anchor, get_base_uri, scroll_state } from './utils.js';
5
5
  import { lock_fetch, unlock_fetch, initial_fetch, subsequent_fetch } from './fetcher.js';
6
6
  import { parse } from './parse.js';
7
- import { error } from '../../exports/index.js';
8
7
 
9
8
  import Root from '__GENERATED__/root.svelte';
10
- import { nodes, server_loads, dictionary, matchers } from '__GENERATED__/client-manifest.js';
9
+ import { nodes, server_loads, dictionary, matchers, hooks } from '__GENERATED__/client-manifest.js';
11
10
  import { HttpError, Redirect } from '../control.js';
12
11
  import { stores } from './singletons.js';
13
12
  import { DATA_SUFFIX } from '../../constants.js';
@@ -386,7 +385,7 @@ export function create_client({ target, base, trailing_slash }) {
386
385
  * params: Record<string, string>;
387
386
  * branch: Array<import('./types').BranchNode | undefined>;
388
387
  * status: number;
389
- * error: HttpError | Error | null;
388
+ * error: App.PageError | null;
390
389
  * route: import('types').CSRRoute | null;
391
390
  * form?: Record<string, any> | null;
392
391
  * }} opts
@@ -756,12 +755,8 @@ export function create_client({ target, base, trailing_slash }) {
756
755
  parent_changed = true;
757
756
 
758
757
  if (server_data_node?.type === 'error') {
759
- if (server_data_node.httperror) {
760
- // reconstruct as an HttpError
761
- throw error(server_data_node.httperror.status, server_data_node.httperror.message);
762
- } else {
763
- throw server_data_node.error;
764
- }
758
+ // rethrow and catch below
759
+ throw server_data_node;
765
760
  }
766
761
 
767
762
  return load_node({
@@ -790,17 +785,29 @@ export function create_client({ target, base, trailing_slash }) {
790
785
  if (loaders[i]) {
791
786
  try {
792
787
  branch.push(await branch_promises[i]);
793
- } catch (e) {
794
- const error = normalize_error(e);
795
-
796
- if (error instanceof Redirect) {
788
+ } catch (err) {
789
+ if (err instanceof Redirect) {
797
790
  return {
798
791
  type: 'redirect',
799
- location: error.location
792
+ location: err.location
800
793
  };
801
794
  }
802
795
 
803
- const status = e instanceof HttpError ? e.status : 500;
796
+ let status = 500;
797
+ /** @type {App.PageError} */
798
+ let error;
799
+
800
+ if (server_data_nodes?.includes(/** @type {import('types').ServerErrorNode} */ (err))) {
801
+ // this is the server error rethrown above, reconstruct but don't invoke
802
+ // the client error handler; it should've already been handled on the server
803
+ status = /** @type {import('types').ServerErrorNode} */ (err).status ?? status;
804
+ error = /** @type {import('types').ServerErrorNode} */ (err).error;
805
+ } else if (err instanceof HttpError) {
806
+ status = err.status;
807
+ error = err.body;
808
+ } else {
809
+ error = handle_error(err, { params, url, routeId: route.id });
810
+ }
804
811
 
805
812
  while (i--) {
806
813
  if (errors[i]) {
@@ -920,7 +927,10 @@ export function create_client({ target, base, trailing_slash }) {
920
927
  params,
921
928
  branch: [root_layout, root_error],
922
929
  status,
923
- error,
930
+ error:
931
+ error instanceof HttpError
932
+ ? error.body
933
+ : handle_error(error, { url, params, routeId: null }),
924
934
  route: null
925
935
  });
926
936
  }
@@ -1395,7 +1405,7 @@ export function create_client({ target, base, trailing_slash }) {
1395
1405
 
1396
1406
  _hydrate: async ({
1397
1407
  status,
1398
- error: original_error, // TODO get rid of this
1408
+ error,
1399
1409
  node_ids,
1400
1410
  params,
1401
1411
  routeId,
@@ -1432,15 +1442,7 @@ export function create_client({ target, base, trailing_slash }) {
1432
1442
  params,
1433
1443
  branch: await Promise.all(branch_promises),
1434
1444
  status,
1435
- error: /** @type {import('../server/page/types').SerializedHttpError} */ (original_error)
1436
- ?.__is_http_error
1437
- ? new HttpError(
1438
- /** @type {import('../server/page/types').SerializedHttpError} */ (
1439
- original_error
1440
- ).status,
1441
- original_error.message
1442
- )
1443
- : original_error,
1445
+ error,
1444
1446
  form,
1445
1447
  route: routes.find((route) => route.id === routeId) ?? null
1446
1448
  });
@@ -1498,6 +1500,15 @@ async function load_data(url, invalid) {
1498
1500
  return server_data;
1499
1501
  }
1500
1502
 
1503
+ /**
1504
+ * @param {unknown} error
1505
+ * @param {import('types').NavigationEvent} event
1506
+ * @returns {App.PageError}
1507
+ */
1508
+ function handle_error(error, event) {
1509
+ return hooks.handleError({ error, event }) ?? /** @type {any} */ ({ message: 'Internal Error' });
1510
+ }
1511
+
1501
1512
  // TODO remove for 1.0
1502
1513
  const properties = [
1503
1514
  'hash',
@@ -9,8 +9,6 @@ import {
9
9
  prefetchRoutes
10
10
  } from '$app/navigation';
11
11
  import { CSRPageNode, CSRPageNodeLoader, CSRRoute, Uses } from 'types';
12
- import { HttpError } from '../control.js';
13
- import { SerializedHttpError } from '../server/page/types.js';
14
12
 
15
13
  export interface Client {
16
14
  // public API, exposed via $app/navigation
@@ -27,7 +25,7 @@ export interface Client {
27
25
  // private API
28
26
  _hydrate: (opts: {
29
27
  status: number;
30
- error: Error | SerializedHttpError;
28
+ error: App.PageError;
31
29
  node_ids: number[];
32
30
  params: Record<string, string>;
33
31
  routeId: string | null;
@@ -79,7 +77,7 @@ export interface DataNode {
79
77
 
80
78
  export interface NavigationState {
81
79
  branch: Array<BranchNode | undefined>;
82
- error: HttpError | Error | null;
80
+ error: App.PageError | null;
83
81
  params: Record<string, string>;
84
82
  route: CSRRoute | null;
85
83
  session_id: number;