@sveltejs/kit 3.0.0-next.2 → 3.0.0-next.21

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 (195) hide show
  1. package/package.json +62 -36
  2. package/src/cli.js +15 -12
  3. package/src/constants.js +6 -1
  4. package/src/core/adapt/builder.js +66 -72
  5. package/src/core/adapt/index.js +17 -6
  6. package/src/core/config/index.js +134 -80
  7. package/src/core/config/options.js +295 -273
  8. package/src/core/config/types.d.ts +1 -1
  9. package/src/core/env.js +146 -16
  10. package/src/core/generate_manifest/index.js +18 -15
  11. package/src/core/postbuild/analyse.js +8 -20
  12. package/src/core/postbuild/crawl.js +22 -6
  13. package/src/core/postbuild/entities.js +8 -2
  14. package/src/core/postbuild/fallback.js +4 -2
  15. package/src/core/postbuild/prerender.js +222 -70
  16. package/src/core/postbuild/queue.js +2 -1
  17. package/src/core/sync/create_manifest_data/conflict.js +1 -1
  18. package/src/core/sync/create_manifest_data/index.js +82 -62
  19. package/src/core/sync/sync.js +40 -31
  20. package/src/core/sync/ts.js +1 -1
  21. package/src/core/sync/utils.js +22 -2
  22. package/src/core/sync/{write_non_ambient.js → write_app_types.js} +107 -58
  23. package/src/core/sync/write_client_manifest.js +14 -29
  24. package/src/core/sync/write_env.js +7 -5
  25. package/src/core/sync/write_server.js +37 -45
  26. package/src/core/sync/write_tsconfig/index.js +274 -0
  27. package/src/core/sync/write_tsconfig/test-app/package.json +7 -0
  28. package/src/core/sync/write_tsconfig/utils.js +77 -0
  29. package/src/core/sync/write_tsconfig/validate.js +128 -0
  30. package/src/core/sync/write_types/index.js +127 -123
  31. package/src/core/utils.js +30 -5
  32. package/src/exports/env/index.js +77 -0
  33. package/src/exports/env/public.d.ts +55 -0
  34. package/src/exports/hooks/index.js +3 -9
  35. package/src/exports/hooks/public.d.ts +195 -0
  36. package/src/exports/hooks/sequence.js +9 -6
  37. package/src/exports/index.js +52 -29
  38. package/src/exports/internal/client.js +5 -0
  39. package/src/exports/internal/env.js +8 -5
  40. package/src/exports/internal/index.js +1 -90
  41. package/src/exports/internal/{event.js → server/event.js} +2 -3
  42. package/src/exports/internal/server/index.js +37 -0
  43. package/src/exports/internal/server/telemetry.js +95 -0
  44. package/src/exports/internal/shared.js +90 -0
  45. package/src/exports/node/index.js +64 -22
  46. package/src/exports/params/index.js +70 -0
  47. package/src/exports/params/public.d.ts +63 -0
  48. package/src/exports/public.d.ts +50 -1733
  49. package/src/exports/remote/index.js +11 -0
  50. package/src/exports/remote/public.d.ts +519 -0
  51. package/src/exports/url.js +86 -0
  52. package/src/exports/vite/build/build_server.js +54 -65
  53. package/src/exports/vite/build/remote.js +24 -19
  54. package/src/exports/vite/build/utils.js +0 -8
  55. package/src/exports/vite/dev/index.js +218 -146
  56. package/src/exports/vite/index.js +1590 -858
  57. package/src/exports/vite/module_ids.js +2 -2
  58. package/src/exports/vite/preview/index.js +40 -32
  59. package/src/exports/vite/public.d.ts +588 -0
  60. package/src/exports/vite/utils.js +84 -46
  61. package/src/pathname.js +55 -0
  62. package/src/runner.js +15 -0
  63. package/src/runtime/app/env/internal.js +4 -4
  64. package/src/runtime/app/env/types.d.ts +1 -1
  65. package/src/runtime/app/environment/index.js +3 -3
  66. package/src/runtime/app/{forms.js → forms/index.js} +73 -42
  67. package/src/runtime/app/forms/public.d.ts +2 -0
  68. package/src/runtime/app/forms/types.d.ts +56 -0
  69. package/src/runtime/app/internal/transport.js +53 -0
  70. package/src/runtime/app/manifest/index.js +1 -0
  71. package/src/runtime/app/{navigation.js → navigation/index.js} +2 -1
  72. package/src/runtime/app/navigation/public.d.ts +237 -0
  73. package/src/runtime/app/paths/client.js +37 -37
  74. package/src/runtime/app/paths/index.js +1 -1
  75. package/src/runtime/app/paths/internal/client.js +34 -2
  76. package/src/runtime/app/paths/internal/server.js +6 -23
  77. package/src/runtime/app/paths/internal.d.ts +3 -0
  78. package/src/runtime/app/paths/public.d.ts +1 -29
  79. package/src/runtime/app/paths/server.js +36 -17
  80. package/src/runtime/app/paths/types.d.ts +11 -19
  81. package/src/runtime/app/server/index.js +2 -2
  82. package/src/runtime/app/server/public.d.ts +201 -0
  83. package/src/runtime/app/server/remote/command.js +13 -11
  84. package/src/runtime/app/server/remote/form.js +61 -39
  85. package/src/runtime/app/server/remote/prerender.js +45 -45
  86. package/src/runtime/app/server/remote/query.js +106 -108
  87. package/src/runtime/app/server/remote/requested.js +27 -19
  88. package/src/runtime/app/server/remote/shared.js +76 -76
  89. package/src/runtime/app/service-worker/index.js +24 -0
  90. package/src/runtime/app/state/client.js +4 -2
  91. package/src/runtime/app/state/index.js +7 -5
  92. package/src/runtime/app/state/public.d.ts +72 -0
  93. package/src/runtime/app/state/server.js +3 -0
  94. package/src/runtime/app/stores.js +15 -78
  95. package/src/runtime/client/bundle.js +1 -1
  96. package/src/runtime/client/client-entry.js +3 -0
  97. package/src/runtime/client/client.js +1336 -648
  98. package/src/runtime/client/constants.js +3 -6
  99. package/src/runtime/client/entry.js +24 -3
  100. package/src/runtime/client/fetcher.js +25 -25
  101. package/src/runtime/client/ndjson.js +1 -1
  102. package/src/runtime/client/parse.js +1 -1
  103. package/src/runtime/client/payload.js +17 -0
  104. package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
  105. package/src/runtime/client/remote-functions/command.svelte.js +37 -61
  106. package/src/runtime/client/remote-functions/form.svelte.js +267 -178
  107. package/src/runtime/client/remote-functions/prerender.svelte.js +32 -11
  108. package/src/runtime/client/remote-functions/query/index.js +10 -17
  109. package/src/runtime/client/remote-functions/query/instance.svelte.js +64 -18
  110. package/src/runtime/client/remote-functions/query/proxy.js +5 -5
  111. package/src/runtime/client/remote-functions/query-batch.svelte.js +61 -70
  112. package/src/runtime/client/remote-functions/query-live/index.js +1 -1
  113. package/src/runtime/client/remote-functions/query-live/instance.svelte.js +63 -21
  114. package/src/runtime/client/remote-functions/query-live/iterator.js +15 -12
  115. package/src/runtime/client/remote-functions/query-live/proxy.js +2 -12
  116. package/src/runtime/client/remote-functions/shared.svelte.js +90 -65
  117. package/src/runtime/client/snapshots.js +147 -0
  118. package/src/runtime/client/state.svelte.js +94 -55
  119. package/src/runtime/client/stream.js +3 -2
  120. package/src/runtime/client/types.d.ts +13 -9
  121. package/src/runtime/client/utils.js +22 -110
  122. package/src/runtime/components/root.svelte +56 -0
  123. package/src/runtime/env/dynamic/private.js +7 -0
  124. package/src/runtime/env/dynamic/public.js +7 -0
  125. package/src/runtime/env/static/private.js +6 -0
  126. package/src/runtime/env/static/public.js +6 -0
  127. package/src/runtime/form-utils.js +172 -61
  128. package/src/runtime/pathname.js +20 -32
  129. package/src/runtime/props.svelte.js +72 -0
  130. package/src/runtime/server/constants.js +0 -3
  131. package/src/runtime/server/cookie.js +72 -55
  132. package/src/runtime/server/csrf.js +65 -0
  133. package/src/runtime/server/data/index.js +49 -53
  134. package/src/runtime/server/dev.js +22 -0
  135. package/src/runtime/server/endpoint.js +6 -7
  136. package/src/runtime/server/env_module.js +0 -5
  137. package/src/runtime/server/errors.js +160 -0
  138. package/src/runtime/server/fetch.js +32 -39
  139. package/src/runtime/server/index.js +90 -64
  140. package/src/runtime/server/internal.js +71 -0
  141. package/src/runtime/server/page/actions.js +85 -64
  142. package/src/runtime/server/page/crypto.js +2 -2
  143. package/src/runtime/server/page/csp.js +88 -104
  144. package/src/runtime/server/page/data_serializer.js +24 -25
  145. package/src/runtime/server/page/index.js +36 -53
  146. package/src/runtime/server/page/load_data.js +50 -57
  147. package/src/runtime/server/page/render.js +173 -239
  148. package/src/runtime/server/page/respond_with_error.js +17 -31
  149. package/src/runtime/server/page/serialize_data.js +2 -13
  150. package/src/runtime/server/page/server_routing.js +85 -26
  151. package/src/runtime/server/remote-functions.js +640 -0
  152. package/src/runtime/server/respond.js +190 -131
  153. package/src/runtime/server/sourcemaps.js +183 -0
  154. package/src/runtime/server/state.js +53 -0
  155. package/src/runtime/server/utils.js +13 -155
  156. package/src/runtime/shared.js +20 -40
  157. package/src/runtime/utils.js +3 -0
  158. package/src/types/ambient-private.d.ts +11 -1
  159. package/src/types/ambient.d.ts +87 -36
  160. package/src/types/global-private.d.ts +25 -25
  161. package/src/types/internal.d.ts +163 -157
  162. package/src/types/private.d.ts +41 -1
  163. package/src/utils/error.js +28 -4
  164. package/src/utils/escape.js +9 -25
  165. package/src/utils/features.js +1 -1
  166. package/src/utils/filesystem.js +1 -23
  167. package/src/utils/fork.js +7 -2
  168. package/src/utils/hash.js +21 -0
  169. package/src/utils/http.js +8 -7
  170. package/src/utils/import.js +9 -2
  171. package/src/utils/imports.js +83 -0
  172. package/src/utils/mime.js +9 -0
  173. package/src/utils/page_nodes.js +6 -7
  174. package/src/utils/params.js +67 -0
  175. package/src/utils/regex.js +9 -0
  176. package/src/utils/routing.js +145 -73
  177. package/src/utils/shared-iterator.js +5 -0
  178. package/src/utils/streaming.js +14 -4
  179. package/src/utils/url.js +33 -2
  180. package/src/version.js +1 -1
  181. package/types/index.d.ts +2616 -2433
  182. package/types/index.d.ts.map +120 -106
  183. package/src/core/sync/write_ambient.js +0 -18
  184. package/src/core/sync/write_root.js +0 -148
  185. package/src/core/sync/write_tsconfig.js +0 -250
  186. package/src/exports/internal/server.js +0 -22
  187. package/src/exports/node/polyfills.js +0 -30
  188. package/src/runtime/server/app.js +0 -9
  189. package/src/runtime/server/remote.js +0 -457
  190. package/src/runtime/shared-server.js +0 -7
  191. package/src/runtime/telemetry/otel.js +0 -21
  192. package/src/runtime/telemetry/record_span.js +0 -65
  193. package/src/types/synthetic/$lib.md +0 -5
  194. /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
  195. /package/src/{runtime/telemetry/noop.js → telemetry.js} +0 -0
@@ -3,62 +3,100 @@
3
3
 
4
4
  import { DEV } from 'esm-env';
5
5
  import * as devalue from 'devalue';
6
- import { text_encoder } from './utils.js';
6
+ import { text_decoder, text_encoder } from './utils.js';
7
+ import { noop } from '../utils/functions.js';
7
8
  import { SvelteKitError } from '@sveltejs/kit/internal';
8
9
 
9
- const decoder = new TextDecoder();
10
-
11
10
  /**
12
- * Sets a value in a nested object using a path string, mutating the original object
11
+ * Sets a parsed form field value in a nested object, mutating the original object.
13
12
  * @param {Record<string, any>} object
14
- * @param {string} path_string
13
+ * @param {{ name: string; type: 'number' | 'boolean' | null }} field
15
14
  * @param {any} value
16
15
  */
17
- export function set_nested_value(object, path_string, value) {
18
- if (path_string.startsWith('n:')) {
19
- path_string = path_string.slice(2);
20
- value = value === '' ? undefined : parseFloat(value);
21
- } else if (path_string.startsWith('b:')) {
22
- path_string = path_string.slice(2);
23
- value = value === 'on';
16
+ export function set_nested_value(object, field, value) {
17
+ deep_set(object, split_path(field.name), value);
18
+ }
19
+
20
+ /**
21
+ * Separates a form field's path from the metadata encoded in its name.
22
+ * @param {string} form_id
23
+ * @param {string} key
24
+ * @returns {{ name: string; type: 'number' | 'boolean' | null; is_array: boolean }}
25
+ */
26
+ export function parse_form_key(form_id, key) {
27
+ const suffix = '/' + form_id;
28
+ let name = key;
29
+
30
+ if (!name.endsWith(suffix)) {
31
+ throw new Error(`Form contained a field that wasn't created with form.fields.as(...): ${name}`);
24
32
  }
25
33
 
26
- deep_set(object, split_path(path_string), value);
34
+ name = name.slice(0, -suffix.length);
35
+
36
+ /** @type {'number' | 'boolean' | null} */
37
+ let type = null;
38
+
39
+ if (name.startsWith('n:')) {
40
+ name = name.slice(2);
41
+ type = 'number';
42
+ } else if (name.startsWith('b:')) {
43
+ name = name.slice(2);
44
+ type = 'boolean';
45
+ }
46
+
47
+ const is_array = name.endsWith('[]');
48
+ if (is_array) name = name.slice(0, -2);
49
+
50
+ return { name, type, is_array };
51
+ }
52
+
53
+ /**
54
+ * @param {'number' | 'boolean' | null} type
55
+ * @param {any} value
56
+ * @returns {any}
57
+ */
58
+ export function coerce_form_value(type, value) {
59
+ if (Array.isArray(value)) return value.map((value) => coerce_form_value(type, value));
60
+ if (type === 'number') return value === '' ? undefined : parseFloat(value);
61
+ if (type === 'boolean') return value === 'on';
62
+ return value;
27
63
  }
28
64
 
65
+ /** Pass this to set_nested_value to delete the last part of the given path */
66
+ export const DELETE_KEY = {};
67
+
29
68
  /**
30
69
  * Convert `FormData` into a POJO
70
+ * @param {string} form_id
31
71
  * @param {FormData} data
32
72
  */
33
- export function convert_formdata(data) {
73
+ export function convert_formdata(form_id, data) {
34
74
  /** @type {Record<string, any>} */
35
75
  const result = {};
36
76
 
37
- for (let key of data.keys()) {
38
- const is_array = key.endsWith('[]');
77
+ for (const field_name of data.keys()) {
39
78
  /** @type {any[]} */
40
- let values = data.getAll(key);
41
-
42
- if (is_array) key = key.slice(0, -2);
79
+ const values = data.getAll(field_name);
43
80
 
44
- if (values.length > 1 && !is_array) {
45
- throw new Error(`Form cannot contain duplicated keys — "${key}" has ${values.length} values`);
46
- }
81
+ const field = parse_form_key(form_id, field_name);
47
82
 
48
83
  // an empty `<input type="file">` will submit a non-existent file, bizarrely
49
- values = values.filter(
84
+ const entries = values.filter(
50
85
  (entry) => typeof entry === 'string' || entry.name !== '' || entry.size > 0
51
86
  );
87
+ if (entries.length === 0 && !field.is_array) continue;
52
88
 
53
- if (key.startsWith('n:')) {
54
- key = key.slice(2);
55
- values = values.map((v) => (v === '' ? undefined : parseFloat(/** @type {string} */ (v))));
56
- } else if (key.startsWith('b:')) {
57
- key = key.slice(2);
58
- values = values.map((v) => v === 'on');
89
+ if (entries.length > 1 && !field.is_array) {
90
+ throw new Error(
91
+ `Form cannot contain duplicated keys "${field.name}" has ${entries.length} values`
92
+ );
59
93
  }
60
94
 
61
- set_nested_value(result, key, is_array ? values : values[0]);
95
+ set_nested_value(
96
+ result,
97
+ field,
98
+ coerce_form_value(field.type, field.is_array ? entries : entries[0])
99
+ );
62
100
  }
63
101
 
64
102
  return result;
@@ -134,21 +172,19 @@ export function serialize_binary_form(data, meta) {
134
172
 
135
173
  /**
136
174
  * @param {Request} request
175
+ * @param {string} form_id
137
176
  * @returns {Promise<{ data: Record<string, any>; meta: BinaryFormMeta; form_data: FormData | null }>}
138
177
  */
139
- export async function deserialize_binary_form(request) {
178
+ export async function deserialize_binary_form(request, form_id) {
140
179
  if (request.headers.get('content-type') !== BINARY_FORM_CONTENT_TYPE) {
141
180
  const form_data = await request.formData();
142
- return { data: convert_formdata(form_data), meta: {}, form_data };
181
+ return { data: convert_formdata(form_id, form_data), meta: {}, form_data };
143
182
  }
144
183
  if (!request.body) {
145
184
  throw deserialize_error('no body');
146
185
  }
147
186
 
148
- // TODO: remove this workaround once we upgrade to TS 6.0
149
- const reader = /** @type {ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>} */ (
150
- request.body.getReader()
151
- );
187
+ const reader = request.body.getReader();
152
188
 
153
189
  /** @type {Array<Promise<Uint8Array<ArrayBuffer> | undefined>>} */
154
190
  const chunks = [];
@@ -245,7 +281,7 @@ export async function deserialize_binary_form(request) {
245
281
  const file_offsets_buffer = await get_buffer(HEADER_BYTES + data_length, file_offsets_length);
246
282
  if (!file_offsets_buffer) throw deserialize_error('file offset table too short');
247
283
 
248
- const parsed_offsets = JSON.parse(decoder.decode(file_offsets_buffer));
284
+ const parsed_offsets = JSON.parse(text_decoder.decode(file_offsets_buffer));
249
285
 
250
286
  if (
251
287
  !Array.isArray(parsed_offsets) ||
@@ -260,7 +296,7 @@ export async function deserialize_binary_form(request) {
260
296
 
261
297
  /** @type {Array<{ offset: number, size: number }>} */
262
298
  const file_spans = [];
263
- const [data, meta] = devalue.parse(decoder.decode(data_buffer), {
299
+ const [data, meta] = devalue.parse(text_decoder.decode(data_buffer), {
264
300
  File: ([name, type, size, last_modified, index]) => {
265
301
  if (
266
302
  typeof name !== 'string' ||
@@ -319,7 +355,7 @@ export async function deserialize_binary_form(request) {
319
355
  const chunk = await get_chunk(chunks.length);
320
356
  has_more = !!chunk;
321
357
  }
322
- })();
358
+ })().catch(noop); // prevent unhandled rejection potentially crashing the process
323
359
 
324
360
  return { data, meta, form_data: null };
325
361
  }
@@ -450,7 +486,7 @@ class LazyFile {
450
486
  });
451
487
  }
452
488
  async text() {
453
- return decoder.decode(await this.arrayBuffer());
489
+ return text_decoder.decode(await this.arrayBuffer());
454
490
  }
455
491
  }
456
492
 
@@ -503,6 +539,10 @@ export function deep_set(object, keys, value) {
503
539
  }
504
540
 
505
541
  if (!exists) {
542
+ if (value === DELETE_KEY) {
543
+ // don't create the nested structure if we want to delete the key anyway
544
+ return;
545
+ }
506
546
  current[key] = is_array ? [] : {};
507
547
  }
508
548
 
@@ -511,7 +551,12 @@ export function deep_set(object, keys, value) {
511
551
 
512
552
  const final_key = keys[keys.length - 1];
513
553
  check_prototype_pollution(final_key);
514
- current[final_key] = value;
554
+
555
+ if (value === DELETE_KEY) {
556
+ delete current[final_key];
557
+ } else {
558
+ current[final_key] = value;
559
+ }
515
560
  }
516
561
 
517
562
  /**
@@ -608,18 +653,56 @@ function get_type_prefix(field_type, is_array, input_value) {
608
653
  return '';
609
654
  }
610
655
 
656
+ /**
657
+ * A deep-clone implementation specifically for form data, where
658
+ * we don't need to worry about cycles and whatnot
659
+ * @param {any} value
660
+ * @returns {any}
661
+ */
662
+ function deep_clone(value) {
663
+ if (value !== null && typeof value === 'object') {
664
+ if (value instanceof Date) {
665
+ return new Date(value.getTime());
666
+ }
667
+
668
+ if (value instanceof File) {
669
+ return value;
670
+ }
671
+
672
+ if (Array.isArray(value)) {
673
+ return value.map(deep_clone);
674
+ }
675
+
676
+ /** @type {Record<string, any>} */
677
+ const clone = {};
678
+ for (const key of Object.keys(value)) {
679
+ clone[key] = deep_clone(value[key]);
680
+ }
681
+
682
+ return clone;
683
+ }
684
+
685
+ return value;
686
+ }
687
+
611
688
  /**
612
689
  * Creates a proxy-based field accessor for form data
690
+ * @param {{
691
+ * form_id: string,
692
+ * get: () => Record<string, any>,
693
+ * set: (path: (string | number)[], value: any) => void,
694
+ * get_issues: (path?: (string | number)[], all?: boolean) => Record<string, InternalRemoteFormIssue[]>,
695
+ * get_touched: () => Record<string, boolean>,
696
+ * get_dirty: () => Record<string, boolean>
697
+ * }} context - Form context, including value accessors and form metadata
613
698
  * @param {any} target - Function or empty POJO
614
- * @param {() => Record<string, any>} get_input - Function to get current input data
615
- * @param {(path: (string | number)[], value: any) => void} set_input - Function to set input data
616
- * @param {(path?: (string | number)[], all?: boolean) => Record<string, InternalRemoteFormIssue[]>} get_issues - Function to get current issues
617
699
  * @param {(string | number)[]} path - Current access path
618
700
  * @returns {any} Proxy object with name(), value(), and issues() methods
619
701
  */
620
- export function create_field_proxy(target, get_input, set_input, get_issues, path = []) {
702
+ export function create_field_proxy(context, target = {}, path = []) {
621
703
  const get_value = () => {
622
- return deep_get(get_input(), path);
704
+ const value = deep_get(context.get(), path);
705
+ return deep_clone(value);
623
706
  };
624
707
 
625
708
  return new Proxy(target, {
@@ -628,29 +711,27 @@ export function create_field_proxy(target, get_input, set_input, get_issues, pat
628
711
 
629
712
  // Handle array access like jobs[0]
630
713
  if (/^\d+$/.test(prop)) {
631
- return create_field_proxy({}, get_input, set_input, get_issues, [
632
- ...path,
633
- parseInt(prop, 10)
634
- ]);
714
+ return create_field_proxy(context, {}, [...path, parseInt(prop, 10)]);
635
715
  }
636
716
 
637
717
  const key = build_path_string(path);
718
+ const next = [...path, prop];
638
719
 
639
720
  if (prop === 'set') {
640
721
  const set_func = function (/** @type {any} */ newValue) {
641
- set_input(path, newValue);
722
+ context.set(path, newValue);
642
723
  return newValue;
643
724
  };
644
- return create_field_proxy(set_func, get_input, set_input, get_issues, [...path, prop]);
725
+ return create_field_proxy(context, set_func, next);
645
726
  }
646
727
 
647
728
  if (prop === 'value') {
648
- return create_field_proxy(get_value, get_input, set_input, get_issues, [...path, prop]);
729
+ return create_field_proxy(context, get_value, next);
649
730
  }
650
731
 
651
732
  if (prop === 'issues' || prop === 'allIssues') {
652
733
  const issues_func = () => {
653
- const all_issues = get_issues(path, prop === 'allIssues')[key === '' ? '$' : key];
734
+ const all_issues = context.get_issues(path, prop === 'allIssues')[key === '' ? '$' : key];
654
735
 
655
736
  if (prop === 'allIssues') {
656
737
  return all_issues?.map((issue) => ({
@@ -659,15 +740,45 @@ export function create_field_proxy(target, get_input, set_input, get_issues, pat
659
740
  }));
660
741
  }
661
742
 
662
- return all_issues
743
+ const issues = all_issues
663
744
  ?.filter((issue) => issue.name === key)
664
745
  ?.map((issue) => ({
665
746
  path: issue.path,
666
747
  message: issue.message
667
748
  }));
749
+
750
+ return issues?.length ? issues : undefined;
751
+ };
752
+
753
+ return create_field_proxy(context, issues_func, next);
754
+ }
755
+
756
+ if (prop === 'touched' || prop === 'dirty') {
757
+ const fn = () => {
758
+ const object = prop === 'dirty' ? context.get_dirty() : context.get_touched();
759
+
760
+ if (key === '') {
761
+ return Object.keys(object).length > 0;
762
+ }
763
+
764
+ if (Object.hasOwn(object, key)) {
765
+ return true;
766
+ }
767
+
768
+ for (const candidate in object) {
769
+ if (!Object.hasOwn(object, candidate)) continue;
770
+ if (!candidate.startsWith(key)) continue;
771
+
772
+ const next = candidate[key.length];
773
+ if (next === '.' || next === '[') {
774
+ return true;
775
+ }
776
+ }
777
+
778
+ return false;
668
779
  };
669
780
 
670
- return create_field_proxy(issues_func, get_input, set_input, get_issues, [...path, prop]);
781
+ return create_field_proxy(context, fn, next);
671
782
  }
672
783
 
673
784
  if (prop === 'as') {
@@ -681,14 +792,14 @@ export function create_field_proxy(target, get_input, set_input, get_issues, pat
681
792
  type === 'select multiple' ||
682
793
  (type === 'checkbox' && typeof input_value === 'string');
683
794
 
684
- const prefix = get_type_prefix(type, is_array, input_value);
795
+ const type_prefix = get_type_prefix(type, is_array, input_value);
685
796
 
686
797
  // Base properties for all input types
687
798
  /** @type {Record<string, any>} */
688
799
  const base_props = {
689
- name: prefix + key + (is_array ? '[]' : ''),
800
+ name: type_prefix + key + (is_array ? '[]' : '') + '/' + context.form_id,
690
801
  get 'aria-invalid'() {
691
- const issues = get_issues();
802
+ const issues = context.get_issues();
692
803
  return key in issues ? 'true' : undefined;
693
804
  }
694
805
  };
@@ -833,11 +944,11 @@ export function create_field_proxy(target, get_input, set_input, get_issues, pat
833
944
  });
834
945
  };
835
946
 
836
- return create_field_proxy(as_func, get_input, set_input, get_issues, [...path, 'as']);
947
+ return create_field_proxy(context, as_func, next);
837
948
  }
838
949
 
839
950
  // Handle property access (nested fields)
840
- return create_field_proxy({}, get_input, set_input, get_issues, [...path, prop]);
951
+ return create_field_proxy(context, {}, next);
841
952
  }
842
953
  });
843
954
  }
@@ -1,49 +1,37 @@
1
- const DATA_SUFFIX = '/__data.json';
2
- const HTML_DATA_SUFFIX = '.html__data.json';
1
+ import { app_dir, base } from '#app/paths';
2
+ import { add_resolution_suffix } from '../pathname.js';
3
3
 
4
- /** @param {string} pathname */
5
- export function has_data_suffix(pathname) {
6
- return pathname.endsWith(DATA_SUFFIX) || pathname.endsWith(HTML_DATA_SUFFIX);
7
- }
8
-
9
- /** @param {string} pathname */
10
- export function add_data_suffix(pathname) {
11
- if (pathname.endsWith('.html')) return pathname.replace(/\.html$/, HTML_DATA_SUFFIX);
12
- return pathname.replace(/\/$/, '') + DATA_SUFFIX;
13
- }
14
-
15
- /** @param {string} pathname */
16
- export function strip_data_suffix(pathname) {
17
- if (pathname.endsWith(HTML_DATA_SUFFIX)) {
18
- return pathname.slice(0, -HTML_DATA_SUFFIX.length) + '.html';
19
- }
20
-
21
- return pathname.slice(0, -DATA_SUFFIX.length);
22
- }
4
+ export * from '../pathname.js';
23
5
 
24
- const ROUTE_SUFFIX = '/__route.js';
6
+ const ROUTES_PREFIX = '/routes';
25
7
 
26
8
  /**
27
- * @param {string} pathname
28
- * @returns {boolean}
9
+ * The pathname of the route-ID-keyed resolution module for a given route ID,
10
+ * e.g. `/_app/routes/blog/[slug]/__route.js` (before prefixing with `base`).
11
+ * @param {string} route_id
12
+ * @returns {string}
29
13
  */
30
- export function has_resolution_suffix(pathname) {
31
- return pathname.endsWith(ROUTE_SUFFIX);
14
+ export function route_id_resolution_pathname(route_id) {
15
+ return add_resolution_suffix(`/${app_dir}${ROUTES_PREFIX}${route_id === '/' ? '' : route_id}`);
32
16
  }
33
17
 
34
18
  /**
35
- * Convert a regular URL to a route to send to SvelteKit's server-side route resolution endpoint
19
+ * Whether a pathname (with the `/__route.js` suffix already stripped, and `base` NOT yet stripped)
20
+ * is a route-ID resolution request rather than a pathname resolution request.
36
21
  * @param {string} pathname
37
- * @returns {string}
22
+ * @returns {boolean}
38
23
  */
39
- export function add_resolution_suffix(pathname) {
40
- return pathname.replace(/\/$/, '') + ROUTE_SUFFIX;
24
+ export function is_route_id_resolution_path(pathname) {
25
+ const prefix = `${base}/${app_dir}${ROUTES_PREFIX}`;
26
+ return pathname === prefix || pathname.startsWith(prefix + '/');
41
27
  }
42
28
 
43
29
  /**
30
+ * Extract the route ID from a decoded, base-stripped, suffix-stripped pathname,
31
+ * e.g. `/_app/routes/blog/[slug]` -> `/blog/[slug]`, `/_app/routes` -> `/`.
44
32
  * @param {string} pathname
45
33
  * @returns {string}
46
34
  */
47
- export function strip_resolution_suffix(pathname) {
48
- return pathname.slice(0, -ROUTE_SUFFIX.length);
35
+ export function extract_route_id(pathname) {
36
+ return pathname.slice(`/${app_dir}${ROUTES_PREFIX}`.length) || '/';
49
37
  }
@@ -0,0 +1,72 @@
1
+ /** @import { Component } from 'svelte'; */
2
+ /** @import { Page } from '$app/state'; */
3
+
4
+ import { noop } from '../utils/functions.js';
5
+
6
+ export class Props {
7
+ /** @type {Page} */
8
+ page;
9
+
10
+ /**
11
+ * An array of the `+layout.svelte` and `+page.svelte` component instances
12
+ * that currently live on the page — used for capturing and restoring snapshots.
13
+ * It's updated/manipulated through `bind:this` in `Root.svelte`.
14
+ * @type {Array<Record<string, any>>}
15
+ * @deprecated only used for `export const snapshot` — TODO 4.0 get rid
16
+ */
17
+ components = [];
18
+
19
+ /** @type {any} */
20
+ form;
21
+
22
+ /** @type {App.Error | undefined} */
23
+ error;
24
+
25
+ /** @type {RenderNode} */
26
+ tree;
27
+
28
+ /** @type {(error: unknown, reset: () => void) => void} */
29
+ onerror;
30
+
31
+ /**
32
+ * @param {{
33
+ * page: Page;
34
+ * tree: RenderNode;
35
+ * form: any;
36
+ * error: App.Error | undefined;
37
+ * onerror?: (error: unknown, reset: () => void) => void;
38
+ * }} props
39
+ */
40
+ constructor({ page, tree, form, error, onerror = noop }) {
41
+ this.page = page;
42
+ this.tree = tree;
43
+ this.onerror = onerror;
44
+
45
+ this.form = $state.raw(form);
46
+ this.error = $state.raw(error);
47
+ }
48
+ }
49
+
50
+ export class RenderNode {
51
+ /** @type {Component} */
52
+ component;
53
+
54
+ /** @type {Component | undefined} */
55
+ error;
56
+
57
+ /** @type {Record<string, any>} */
58
+ data = $state.raw({});
59
+
60
+ /** @type {RenderNode | undefined} */
61
+ child = $state.raw();
62
+
63
+ /**
64
+ *
65
+ * @param {Component} component
66
+ * @param {Component} error
67
+ */
68
+ constructor(component, error) {
69
+ this.component = component;
70
+ this.error = error;
71
+ }
72
+ }
@@ -1,4 +1 @@
1
1
  export const NULL_BODY_STATUS = [101, 103, 204, 205, 304];
2
-
3
- // eslint-disable-next-line n/prefer-global/process
4
- export const IN_WEBCONTAINER = !!globalThis.process?.versions?.webcontainer;