@sveltejs/kit 3.0.0-next.6 → 3.0.0-next.8

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 (80) hide show
  1. package/package.json +8 -3
  2. package/src/core/adapt/builder.js +11 -39
  3. package/src/core/config/index.js +76 -71
  4. package/src/core/config/options.js +280 -285
  5. package/src/core/config/types.d.ts +1 -1
  6. package/src/core/env.js +86 -1
  7. package/src/core/generate_manifest/index.js +12 -15
  8. package/src/core/sync/create_manifest_data/index.js +7 -45
  9. package/src/core/sync/sync.js +0 -2
  10. package/src/core/sync/write_client_manifest.js +7 -21
  11. package/src/core/sync/write_non_ambient.js +12 -9
  12. package/src/core/sync/write_server.js +0 -4
  13. package/src/core/sync/write_types/index.js +28 -24
  14. package/src/core/utils.js +2 -2
  15. package/src/exports/index.js +3 -3
  16. package/src/exports/internal/client.js +5 -0
  17. package/src/exports/internal/index.js +1 -91
  18. package/src/exports/internal/{event.js → server/event.js} +1 -2
  19. package/src/exports/internal/server/index.js +33 -0
  20. package/src/exports/internal/shared.js +89 -0
  21. package/src/exports/node/index.js +5 -9
  22. package/src/exports/params.js +63 -0
  23. package/src/exports/public.d.ts +103 -46
  24. package/src/exports/url.js +84 -0
  25. package/src/exports/vite/dev/index.js +31 -20
  26. package/src/exports/vite/index.js +280 -199
  27. package/src/exports/vite/preview/index.js +14 -15
  28. package/src/exports/vite/utils.js +8 -10
  29. package/src/runtime/app/env/internal.js +4 -4
  30. package/src/runtime/app/forms.js +2 -2
  31. package/src/runtime/app/paths/client.js +3 -7
  32. package/src/runtime/app/paths/internal/client.js +4 -2
  33. package/src/runtime/app/paths/internal/server.js +2 -23
  34. package/src/runtime/app/paths/server.js +3 -3
  35. package/src/runtime/app/server/remote/query.js +6 -12
  36. package/src/runtime/app/state/client.js +1 -2
  37. package/src/runtime/app/stores.js +13 -76
  38. package/src/runtime/client/bundle.js +1 -1
  39. package/src/runtime/client/client-entry.js +3 -0
  40. package/src/runtime/client/client.js +230 -238
  41. package/src/runtime/client/entry.js +24 -3
  42. package/src/runtime/client/payload.js +17 -0
  43. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  44. package/src/runtime/client/remote-functions/form.svelte.js +11 -30
  45. package/src/runtime/client/remote-functions/prerender.svelte.js +10 -3
  46. package/src/runtime/client/remote-functions/query/instance.svelte.js +18 -9
  47. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +16 -6
  48. package/src/runtime/client/remote-functions/shared.svelte.js +1 -2
  49. package/src/runtime/client/state.svelte.js +66 -49
  50. package/src/runtime/client/types.d.ts +3 -7
  51. package/src/runtime/client/utils.js +0 -96
  52. package/src/runtime/components/root.svelte +66 -0
  53. package/src/runtime/form-utils.js +16 -6
  54. package/src/runtime/server/cookie.js +17 -7
  55. package/src/runtime/server/index.js +1 -1
  56. package/src/runtime/server/page/index.js +7 -14
  57. package/src/runtime/server/page/load_data.js +1 -1
  58. package/src/runtime/server/page/render.js +72 -88
  59. package/src/runtime/server/page/server_routing.js +13 -9
  60. package/src/runtime/server/remote.js +23 -13
  61. package/src/runtime/server/respond.js +11 -8
  62. package/src/runtime/server/utils.js +28 -5
  63. package/src/runtime/telemetry/otel.js +1 -1
  64. package/src/runtime/types.d.ts +8 -0
  65. package/src/types/ambient.d.ts +5 -1
  66. package/src/types/global-private.d.ts +11 -18
  67. package/src/types/internal.d.ts +25 -30
  68. package/src/utils/error.js +1 -1
  69. package/src/utils/import.js +6 -1
  70. package/src/utils/mime.js +9 -0
  71. package/src/utils/params.js +66 -0
  72. package/src/utils/routing.js +90 -44
  73. package/src/utils/streaming.js +14 -4
  74. package/src/utils/url.js +0 -79
  75. package/src/version.js +1 -1
  76. package/types/index.d.ts +127 -125
  77. package/types/index.d.ts.map +12 -8
  78. package/src/core/sync/write_root.js +0 -148
  79. package/src/exports/internal/server.js +0 -22
  80. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
@@ -1,3 +1,24 @@
1
- // we expose this as a separate entry point (rather than treating client.js as the entry point)
2
- // so that everything other than `start`/`load_css` can be treeshaken
3
- export { start, load_css } from './client.js';
1
+ /* in development or if `bundleStrategy` is 'split', this file is used as the entry point */
2
+
3
+ import { set_payload } from './payload.js';
4
+
5
+ /** @type {Promise<typeof import('./client-entry.js')>} */
6
+ let client;
7
+
8
+ /** @param {import('types').SvelteKitPayload} payload */
9
+ export function init(payload) {
10
+ set_payload(payload);
11
+ // Importing the client after setting the payload ensures that modules such as
12
+ // `$app/paths` can read it during initialization.
13
+ client ??= import('./client-entry.js');
14
+ }
15
+
16
+ /** @param {Parameters<import('./client-entry.js').start>} args */
17
+ export async function start(...args) {
18
+ return (await client).start(...args);
19
+ }
20
+
21
+ /** @param {Parameters<import('./client-entry.js').load_css>} args */
22
+ export async function load_css(...args) {
23
+ return (await client).load_css(...args);
24
+ }
@@ -0,0 +1,17 @@
1
+ /** @import {SvelteKitPayload} from 'types'; */
2
+
3
+ /**
4
+ * Code inside the SvelteKit client runtime should only use this, not the global,
5
+ * so that the file hashes stay stable between rebuilds as long as the SvelteKit runtime doesn't change
6
+ */
7
+ export let payload = __SVELTEKIT_PAYLOAD__ ?? /** @type {SvelteKitPayload} */ ({});
8
+
9
+ /** @param {SvelteKitPayload} value */
10
+ export function set_payload(value) {
11
+ payload = value;
12
+ }
13
+
14
+ // this makes Vite inject its dev client code as this module's first dependency
15
+ // so that global constant replacements are done before any other module evaluates.
16
+ // For build, it's inert.
17
+ import.meta.hot;
@@ -1,8 +1,10 @@
1
+ /** @import { Query } from './query/instance.svelte.js' */
2
+ /** @import { LiveQuery } from './query-live/instance.svelte.js' */
1
3
  import { tick } from 'svelte';
2
4
  import { once } from '../../../utils/functions.js';
3
5
 
4
6
  /**
5
- * @template R
7
+ * @template [R=Query<any> | LiveQuery<any>]
6
8
  * @typedef {object} CacheEntry
7
9
  * @property {number} proxy_count The number of live proxy instances referencing this
8
10
  * entry. The entry is eligible for eviction when this hits zero.
@@ -8,8 +8,8 @@ import {
8
8
  query_responses,
9
9
  _goto,
10
10
  set_nearest_error_page,
11
- invalidateAll,
12
- handle_error
11
+ handle_error,
12
+ refreshAll
13
13
  } from '../client.js';
14
14
  import { tick } from 'svelte';
15
15
  import { categorize_updates, remote_request } from './shared.svelte.js';
@@ -23,8 +23,9 @@ import {
23
23
  build_path_string,
24
24
  normalize_issue,
25
25
  serialize_binary_form,
26
- BINARY_FORM_CONTENT_TYPE,
27
- deep_get
26
+ deep_get,
27
+ DELETE_KEY,
28
+ BINARY_FORM_CONTENT_TYPE
28
29
  } from '../../form-utils.js';
29
30
 
30
31
  /**
@@ -226,14 +227,14 @@ export function form(id) {
226
227
 
227
228
  // if the developer took control of updates via `.updates(...)` (even with
228
229
  // no arguments), or the server performed explicit refreshes, don't invalidateAll
229
- const should_invalidate = refreshes === null && !response.r;
230
+ const should_refresh = refreshes === null && !response.r;
230
231
 
231
232
  if (response.redirect) {
232
233
  // Use internal version to allow redirects to external URLs
233
234
  void _goto(
234
235
  response.redirect,
235
236
  {
236
- invalidateAll: should_invalidate
237
+ refreshAll: should_refresh
237
238
  },
238
239
  0
239
240
  );
@@ -243,8 +244,8 @@ export function form(id) {
243
244
  const succeeded = raw_issues.length === 0;
244
245
 
245
246
  if (succeeded) {
246
- if (should_invalidate) {
247
- void invalidateAll();
247
+ if (should_refresh) {
248
+ void refreshAll();
248
249
  }
249
250
  } else {
250
251
  if (DEV) {
@@ -514,14 +515,7 @@ export function form(id) {
514
515
  if (file) {
515
516
  set_nested_value(input, name, file);
516
517
  } else {
517
- // Remove the property by setting to undefined and clean up
518
- const path_parts = name.split(/\.|\[|\]/).filter(Boolean);
519
- let current = /** @type {any} */ (input);
520
- for (let i = 0; i < path_parts.length - 1; i++) {
521
- if (current[path_parts[i]] == null) return;
522
- current = current[path_parts[i]];
523
- }
524
- delete current[path_parts[path_parts.length - 1]];
518
+ set_nested_value(input, name, DELETE_KEY);
525
519
  }
526
520
  } else {
527
521
  set_nested_value(
@@ -672,12 +666,7 @@ export function form(id) {
672
666
  },
673
667
  validate: {
674
668
  /** @type {RemoteForm<any, any>['validate']} */
675
- value: async ({
676
- all = false,
677
- preflightOnly = false,
678
- // @ts-expect-error TODO remove this in 3.0
679
- includeUntouched
680
- } = {}) => {
669
+ value: async ({ all = false, preflightOnly = false } = {}) => {
681
670
  if (!element) return;
682
671
 
683
672
  const id = ++validate_id;
@@ -728,14 +717,6 @@ export function form(id) {
728
717
  array = /** @type {InternalRemoteFormIssue[]} */ (result._);
729
718
  }
730
719
 
731
- if (includeUntouched !== undefined) {
732
- console.warn(
733
- `\`{ includeUntouched: ${includeUntouched} }\` has been replaced with \`{ all: ${includeUntouched} }\``
734
- );
735
-
736
- all = includeUntouched;
737
- }
738
-
739
720
  if (!all && !submitted) {
740
721
  array = array.filter((issue) => can_validate[issue.name]);
741
722
  }
@@ -2,10 +2,11 @@
2
2
  import { app_dir, base } from '$app/paths/internal/client';
3
3
  import { version } from '$app/env';
4
4
  import * as devalue from 'devalue';
5
- import { app, _goto, prerender_responses } from '../client.js';
5
+ import { app, _goto, handle_error, prerender_responses } from '../client.js';
6
6
  import { get_remote_request_headers, remote_request, unwrap_node } from './shared.svelte.js';
7
7
  import { create_remote_key, stringify_remote_arg } from '../../shared.js';
8
8
  import { noop } from '../../../utils/functions.js';
9
+ import { HttpError } from '@sveltejs/kit/internal';
9
10
 
10
11
  // Initialize Cache API for prerender functions
11
12
  const CACHE_NAME = __SVELTEKIT_DEV__ ? `sveltekit:${Date.now()}` : `sveltekit:${version}`;
@@ -148,6 +149,7 @@ class Prerender {
148
149
  /** @type {T | undefined} */
149
150
  #current = $state.raw();
150
151
 
152
+ /** @type {App.Error | undefined} */
151
153
  #error = $state.raw(undefined);
152
154
 
153
155
  /**
@@ -162,10 +164,15 @@ class Prerender {
162
164
  this.#error = undefined;
163
165
  return value;
164
166
  },
165
- (error) => {
167
+ async (e) => {
168
+ const error = await handle_error(e, {
169
+ params: {},
170
+ route: { id: null },
171
+ url: new URL(location.href)
172
+ });
166
173
  this.#loading = false;
167
174
  this.#error = error;
168
- throw error;
175
+ throw new HttpError(error.status, error); // so that transformError doesn't transform it again
169
176
  }
170
177
  );
171
178
 
@@ -1,4 +1,4 @@
1
- import { query_responses } from '../../client.js';
1
+ import { query_responses, handle_error } from '../../client.js';
2
2
  import { HttpError } from '@sveltejs/kit/internal';
3
3
  import { QUERY_OVERRIDE_KEY } from '../shared.svelte.js';
4
4
  import { noop } from '../../../../utils/functions.js';
@@ -37,7 +37,7 @@ export class Query {
37
37
  return this.#overrides.reduce((v, r) => r(v), /** @type {T} */ (this.#raw));
38
38
  });
39
39
 
40
- /** @type {any} */
40
+ /** @type {App.Error | undefined} */
41
41
  #error = $state.raw(undefined);
42
42
 
43
43
  /** @type {Promise<T>['then']} */
@@ -126,22 +126,33 @@ export class Query {
126
126
 
127
127
  resolve(undefined);
128
128
  })
129
- .catch((e) => {
129
+ .catch(async (e) => {
130
130
  // TODO: Our behavior here could be better:
131
131
  // - We should not reject on redirects, but should hook into the router
132
132
  // to ensure the query is properly refreshed before the navigation completes
133
133
  // - Instead of failing on transport-level errors, we should probably do what
134
134
  // LiveQuery does and preserve the last known good value and retry the connection
135
+ if (this.#latest.indexOf(resolve) === -1) return;
136
+
137
+ const error = await handle_error(e, {
138
+ params: {},
139
+ route: { id: null },
140
+ url: new URL(location.href)
141
+ });
142
+
143
+ // Re-check after the async `handle_error` gap: a later request may have
144
+ // resolved/rejected while we were awaiting and superseded this one, so
145
+ // recompute the index and bail out if this request is no longer current
135
146
  const idx = this.#latest.indexOf(resolve);
136
147
  if (idx === -1) return;
137
148
 
138
149
  untrack(() => {
139
150
  this.#latest.splice(0, idx).forEach((r) => r(undefined));
140
- this.#error = e;
151
+ this.#error = error;
141
152
  this.#loading = false;
142
153
  });
143
154
 
144
- reject(e);
155
+ reject(new HttpError(error.status, error)); // so that transformError doesn't transform it again
145
156
  });
146
157
 
147
158
  return promise;
@@ -229,9 +240,7 @@ export class Query {
229
240
  this.#promise = Promise.resolve();
230
241
  }
231
242
 
232
- /**
233
- * @param {unknown} error
234
- */
243
+ /** @param {HttpError} error */
235
244
  fail(error) {
236
245
  // normally consumed in the constructor, but make sure a leftover
237
246
  // SSR record can never shadow the newly-set error
@@ -239,7 +248,7 @@ export class Query {
239
248
 
240
249
  this.#clear_pending();
241
250
  this.#loading = false;
242
- this.#error = error;
251
+ this.#error = error.body;
243
252
 
244
253
  const promise = Promise.reject(error);
245
254
 
@@ -1,4 +1,4 @@
1
- import { query_responses } from '../../client.js';
1
+ import { query_responses, handle_error } from '../../client.js';
2
2
  import { HttpError, Redirect } from '@sveltejs/kit/internal';
3
3
  import { noop, once } from '../../../../utils/functions.js';
4
4
  import { SharedIterator } from '../../../../utils/shared-iterator.js';
@@ -24,7 +24,7 @@ export class LiveQuery {
24
24
  #done = $state(false);
25
25
  /** @type {T | undefined} */
26
26
  #raw = $state.raw();
27
- /** @type {any} */
27
+ /** @type {App.Error | undefined} */
28
28
  #error = $state.raw(undefined);
29
29
  /** @type {Promise<void>} */
30
30
  #promise;
@@ -93,7 +93,7 @@ export class LiveQuery {
93
93
  // and the query can recover
94
94
  const error = new HttpError(node.e.status, node.e);
95
95
  this.#loading = false;
96
- this.#error = error;
96
+ this.#error = error.body;
97
97
 
98
98
  promise.catch(noop);
99
99
  this.#reject_first?.(error);
@@ -175,7 +175,7 @@ export class LiveQuery {
175
175
 
176
176
  if (!this.#ready) {
177
177
  // If we haven't successfully connected and received a value yet, surface the error
178
- this.fail(error);
178
+ await this.#fail(error);
179
179
  on_connect_failed(error);
180
180
  break;
181
181
  }
@@ -385,10 +385,10 @@ export class LiveQuery {
385
385
  this.#fan_out.push(value);
386
386
  }
387
387
 
388
- /** @param {unknown} error */
388
+ /** @param {HttpError} error */
389
389
  fail(error) {
390
390
  this.#loading = false;
391
- this.#error = error;
391
+ this.#error = error.body;
392
392
  // `fail` is terminal — once a live query has hard-failed, the only way to start
393
393
  // streaming again is via `reconnect()`. Mark it done and abort any in-flight
394
394
  // request so that callers from outside the main loop (e.g. `apply_reconnections`)
@@ -410,6 +410,16 @@ export class LiveQuery {
410
410
  this.#fan_out.fail(error);
411
411
  }
412
412
 
413
+ /** @param {unknown} e */
414
+ async #fail(e) {
415
+ const error = await handle_error(e, {
416
+ params: {},
417
+ route: { id: null },
418
+ url: new URL(location.href)
419
+ });
420
+ this.fail(new HttpError(error.status, error));
421
+ }
422
+
413
423
  get [Symbol.toStringTag]() {
414
424
  return 'LiveQuery';
415
425
  }
@@ -137,9 +137,8 @@ export async function remote_request(url, init) {
137
137
  );
138
138
 
139
139
  /**
140
- *
141
140
  * @param {string} key
142
- * @param {CacheEntry<any> | undefined} entry
141
+ * @param {CacheEntry | undefined} entry
143
142
  * @param {any} result
144
143
  */
145
144
  function refresh(key, entry, result) {
@@ -1,54 +1,71 @@
1
- import { onMount } from 'svelte';
2
- import { updated_listener } from './utils.js';
1
+ import { version } from '$app/env';
2
+ import { assets } from '$app/paths/internal/client';
3
+ import { BROWSER, DEV } from 'esm-env';
3
4
 
4
5
  /** @type {import('@sveltejs/kit').Page} */
5
- export let page;
6
-
7
- /** @type {{ current: import('@sveltejs/kit').Navigation | null }} */
8
- export let navigating;
9
-
10
- /** @type {{ current: boolean }} */
11
- export let updated;
12
-
13
- // this is a bootleg way to tell if we're in old svelte or new svelte
14
- const is_legacy =
15
- onMount.toString().includes('$$') || /function \w+\(\) \{\}/.test(onMount.toString());
16
-
17
- const placeholder_url = 'a:';
18
-
19
- if (is_legacy) {
20
- page = {
21
- data: {},
22
- form: null,
23
- error: null,
24
- params: {},
25
- route: { id: null },
26
- state: {},
27
- status: -1,
28
- url: new URL(placeholder_url)
29
- };
30
- navigating = { current: null };
31
- updated = { current: false };
32
- } else {
33
- page = new (class Page {
34
- data = $state.raw({});
35
- form = $state.raw(null);
36
- error = $state.raw(null);
37
- params = $state.raw({});
38
- route = $state.raw({ id: null });
39
- state = $state.raw({});
40
- status = $state.raw(-1);
41
- url = $state.raw(new URL(placeholder_url));
42
- })();
43
-
44
- navigating = new (class Navigating {
45
- current = $state.raw(null);
46
- })();
47
-
48
- updated = new (class Updated {
49
- current = $state.raw(false);
50
- })();
51
- updated_listener.v = () => (updated.current = true);
6
+ export const page = new (class Page {
7
+ data = $state.raw({});
8
+ form = $state.raw(null);
9
+ error = $state.raw(null);
10
+ params = $state.raw({});
11
+ route = $state.raw({ id: null });
12
+ state = $state.raw({});
13
+ status = $state.raw(-1);
14
+ // eslint-disable-next-line svelte/prefer-svelte-reactivity
15
+ url = $state.raw(new URL('a:'));
16
+ })();
17
+
18
+ export const navigating = new (class Navigating {
19
+ /** @type {import('@sveltejs/kit').Navigation | null} */
20
+ current = $state.raw(null);
21
+ })();
22
+
23
+ export const updated = new (class Updated {
24
+ current = $state.raw(false);
25
+ // eslint-disable-next-line @typescript-eslint/require-await
26
+ check = async () => false;
27
+ })();
28
+
29
+ if (!DEV && BROWSER) {
30
+ const interval = __SVELTEKIT_APP_VERSION_POLL_INTERVAL__;
31
+
32
+ /** @type {number} */
33
+ let timeout;
34
+
35
+ /** @type {() => Promise<boolean>} */
36
+ async function check() {
37
+ window.clearTimeout(timeout);
38
+
39
+ if (interval) timeout = window.setTimeout(check, interval);
40
+
41
+ try {
42
+ const res = await fetch(`${assets}/${__SVELTEKIT_APP_VERSION_FILE__}`, {
43
+ headers: {
44
+ 'cache-control': 'no-cache'
45
+ }
46
+ });
47
+
48
+ if (!res.ok) {
49
+ return false;
50
+ }
51
+
52
+ const data = await res.json();
53
+ const new_update = data.version !== version;
54
+
55
+ if (new_update) {
56
+ updated.current = true;
57
+ window.clearTimeout(timeout);
58
+ }
59
+
60
+ return new_update;
61
+ } catch {
62
+ return false;
63
+ }
64
+ }
65
+
66
+ if (interval) timeout = window.setTimeout(check, interval);
67
+
68
+ updated.check = check;
52
69
  }
53
70
 
54
71
  /**
@@ -10,6 +10,7 @@ import {
10
10
  Uses
11
11
  } from 'types';
12
12
  import { Page, ParamMatcher } from '@sveltejs/kit';
13
+ import { RenderNode } from '../types.js';
13
14
 
14
15
  export interface SvelteKitApp {
15
16
  /**
@@ -39,7 +40,7 @@ export interface SvelteKitApp {
39
40
  dictionary: Record<string, [leaf: number, layouts: number[], errors?: number[]]>;
40
41
 
41
42
  /**
42
- * A map of `[matcherName: string]: (..) => boolean`, which is used to match route parameters.
43
+ * A map of `[matcherName: string]: ParamMatcher`, which is used to match and parse route parameters.
43
44
  *
44
45
  * In case of router.resolution=server, this object is empty, as resolution happens on the server.
45
46
  */
@@ -57,8 +58,6 @@ export interface SvelteKitApp {
57
58
  */
58
59
  hash: boolean;
59
60
 
60
- root: typeof SvelteComponent;
61
-
62
61
  /**
63
62
  * Lazily loads the contents of src/error.html, used as a last-resort
64
63
  * error page when the root layout's load function throws during client-side rendering.
@@ -91,13 +90,10 @@ export type NavigationFinished = {
91
90
  type: 'loaded';
92
91
  state: NavigationState;
93
92
  props: {
94
- constructors: Array<typeof SvelteComponent>;
95
- errors?: Array<typeof SvelteComponent | undefined>;
96
- components?: SvelteComponent[];
97
93
  page: Page;
98
94
  form?: Record<string, any> | null;
99
95
  error?: App.Error;
100
- [key: `data_${number}`]: Record<string, any>;
96
+ tree: RenderNode;
101
97
  };
102
98
  };
103
99
 
@@ -1,12 +1,6 @@
1
1
  import { BROWSER, DEV } from 'esm-env';
2
- import { writable } from 'svelte/store';
3
- import { assets } from '$app/paths/internal/client';
4
- import { version } from '$app/env';
5
- import { noop } from '../../utils/functions.js';
6
2
  import { PRELOAD_PRIORITIES } from './constants.js';
7
3
 
8
- /* global __SVELTEKIT_APP_VERSION_FILE__, __SVELTEKIT_APP_VERSION_POLL_INTERVAL__ */
9
-
10
4
  export const origin = BROWSER ? location.origin : '';
11
5
 
12
6
  /** @param {string | URL} url */
@@ -211,96 +205,6 @@ export function get_router_options(element) {
211
205
  };
212
206
  }
213
207
 
214
- /** @param {any} value */
215
- export function notifiable_store(value) {
216
- const store = writable(value);
217
- let ready = true;
218
-
219
- function notify() {
220
- ready = true;
221
- store.update((val) => val);
222
- }
223
-
224
- /** @param {any} new_value */
225
- function set(new_value) {
226
- ready = false;
227
- store.set(new_value);
228
- }
229
-
230
- /** @param {(value: any) => void} run */
231
- function subscribe(run) {
232
- /** @type {any} */
233
- let old_value;
234
- return store.subscribe((new_value) => {
235
- if (old_value === undefined || (ready && new_value !== old_value)) {
236
- run((old_value = new_value));
237
- }
238
- });
239
- }
240
-
241
- return { notify, set, subscribe };
242
- }
243
-
244
- export const updated_listener = {
245
- v: noop
246
- };
247
-
248
- export function create_updated_store() {
249
- const { set, subscribe } = writable(false);
250
-
251
- if (__SVELTEKIT_DEV__ || !BROWSER) {
252
- return {
253
- subscribe,
254
- // eslint-disable-next-line @typescript-eslint/require-await
255
- check: async () => false
256
- };
257
- }
258
-
259
- const interval = __SVELTEKIT_APP_VERSION_POLL_INTERVAL__;
260
-
261
- /** @type {NodeJS.Timeout} */
262
- let timeout;
263
-
264
- /** @type {() => Promise<boolean>} */
265
- async function check() {
266
- clearTimeout(timeout);
267
-
268
- if (interval) timeout = setTimeout(check, interval);
269
-
270
- try {
271
- const res = await fetch(`${assets}/${__SVELTEKIT_APP_VERSION_FILE__}`, {
272
- headers: {
273
- 'cache-control': 'no-cache'
274
- }
275
- });
276
-
277
- if (!res.ok) {
278
- return false;
279
- }
280
-
281
- const data = await res.json();
282
- const updated = data.version !== version;
283
-
284
- if (updated) {
285
- set(true);
286
- updated_listener.v();
287
- clearTimeout(timeout);
288
- }
289
-
290
- return updated;
291
- } catch {
292
- return false;
293
- }
294
- }
295
-
296
- if (interval) timeout = setTimeout(check, interval);
297
-
298
- return {
299
- subscribe,
300
- check
301
- };
302
- }
303
-
304
208
  /**
305
209
  * Is external if
306
210
  * - origin different
@@ -0,0 +1,66 @@
1
+ <script lang="ts">
2
+ import { afterNavigate } from '$app/navigation';
3
+ import type { Page } from '@sveltejs/kit';
4
+ import type { RenderNode } from '../types.js';
5
+
6
+ interface Props {
7
+ page: Page;
8
+ tree: RenderNode;
9
+ components: any[];
10
+ resetters: Array<(() => void) | undefined>;
11
+ form?: any;
12
+ error?: App.Error;
13
+ }
14
+
15
+ const { page, components, resetters, tree, form, error }: Props = $props();
16
+
17
+ let mounted = $state(false);
18
+ let navigated = $state(false);
19
+ let title = $state('');
20
+
21
+ afterNavigate(() => {
22
+ if (mounted) {
23
+ navigated = true;
24
+ title = document.title || 'untitled page';
25
+ } else {
26
+ mounted = true;
27
+ }
28
+ });
29
+ </script>
30
+
31
+ {#snippet node(n: RenderNode, depth: number)}
32
+ {const Component = $derived(n.component)}
33
+ {const Error = $derived(n.error)}
34
+ {const data = $derived(n.data)}
35
+
36
+ <svelte:boundary onerror={(_, reset) => (resetters[depth] = reset)}>
37
+ {#if n.child}
38
+ <!-- svelte-ignore binding_property_non_reactive -->
39
+ <Component bind:this={components[depth]} {data} {form} params={page.params}>
40
+ {@render node(n.child, depth + 1)}
41
+ </Component>
42
+ {:else}
43
+ <!-- svelte-ignore binding_property_non_reactive -->
44
+ <Component bind:this={components[depth]} {data} {form} params={page.params} {error} />
45
+ {/if}
46
+
47
+ {#snippet failed(error)}
48
+ <Error {error} />
49
+ {/snippet}
50
+ </svelte:boundary>
51
+ {/snippet}
52
+
53
+ {@render node(tree, 0)}
54
+
55
+ {#if mounted}
56
+ <div
57
+ id="svelte-announcer"
58
+ aria-live="assertive"
59
+ aria-atomic="true"
60
+ style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px"
61
+ >
62
+ {#if navigated}
63
+ {title}
64
+ {/if}
65
+ </div>
66
+ {/if}