@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.
- package/package.json +62 -36
- package/src/cli.js +15 -12
- package/src/constants.js +6 -1
- package/src/core/adapt/builder.js +66 -72
- package/src/core/adapt/index.js +17 -6
- package/src/core/config/index.js +134 -80
- package/src/core/config/options.js +295 -273
- package/src/core/config/types.d.ts +1 -1
- package/src/core/env.js +146 -16
- package/src/core/generate_manifest/index.js +18 -15
- package/src/core/postbuild/analyse.js +8 -20
- package/src/core/postbuild/crawl.js +22 -6
- package/src/core/postbuild/entities.js +8 -2
- package/src/core/postbuild/fallback.js +4 -2
- package/src/core/postbuild/prerender.js +222 -70
- package/src/core/postbuild/queue.js +2 -1
- package/src/core/sync/create_manifest_data/conflict.js +1 -1
- package/src/core/sync/create_manifest_data/index.js +82 -62
- package/src/core/sync/sync.js +40 -31
- package/src/core/sync/ts.js +1 -1
- package/src/core/sync/utils.js +22 -2
- package/src/core/sync/{write_non_ambient.js → write_app_types.js} +107 -58
- package/src/core/sync/write_client_manifest.js +14 -29
- package/src/core/sync/write_env.js +7 -5
- package/src/core/sync/write_server.js +37 -45
- package/src/core/sync/write_tsconfig/index.js +274 -0
- package/src/core/sync/write_tsconfig/test-app/package.json +7 -0
- package/src/core/sync/write_tsconfig/utils.js +77 -0
- package/src/core/sync/write_tsconfig/validate.js +128 -0
- package/src/core/sync/write_types/index.js +127 -123
- package/src/core/utils.js +30 -5
- package/src/exports/env/index.js +77 -0
- package/src/exports/env/public.d.ts +55 -0
- package/src/exports/hooks/index.js +3 -9
- package/src/exports/hooks/public.d.ts +195 -0
- package/src/exports/hooks/sequence.js +9 -6
- package/src/exports/index.js +52 -29
- package/src/exports/internal/client.js +5 -0
- package/src/exports/internal/env.js +8 -5
- package/src/exports/internal/index.js +1 -90
- package/src/exports/internal/{event.js → server/event.js} +2 -3
- package/src/exports/internal/server/index.js +37 -0
- package/src/exports/internal/server/telemetry.js +95 -0
- package/src/exports/internal/shared.js +90 -0
- package/src/exports/node/index.js +64 -22
- package/src/exports/params/index.js +70 -0
- package/src/exports/params/public.d.ts +63 -0
- package/src/exports/public.d.ts +50 -1733
- package/src/exports/remote/index.js +11 -0
- package/src/exports/remote/public.d.ts +519 -0
- package/src/exports/url.js +86 -0
- package/src/exports/vite/build/build_server.js +54 -65
- package/src/exports/vite/build/remote.js +24 -19
- package/src/exports/vite/build/utils.js +0 -8
- package/src/exports/vite/dev/index.js +218 -146
- package/src/exports/vite/index.js +1590 -858
- package/src/exports/vite/module_ids.js +2 -2
- package/src/exports/vite/preview/index.js +40 -32
- package/src/exports/vite/public.d.ts +588 -0
- package/src/exports/vite/utils.js +84 -46
- package/src/pathname.js +55 -0
- package/src/runner.js +15 -0
- package/src/runtime/app/env/internal.js +4 -4
- package/src/runtime/app/env/types.d.ts +1 -1
- package/src/runtime/app/environment/index.js +3 -3
- package/src/runtime/app/{forms.js → forms/index.js} +73 -42
- package/src/runtime/app/forms/public.d.ts +2 -0
- package/src/runtime/app/forms/types.d.ts +56 -0
- package/src/runtime/app/internal/transport.js +53 -0
- package/src/runtime/app/manifest/index.js +1 -0
- package/src/runtime/app/{navigation.js → navigation/index.js} +2 -1
- package/src/runtime/app/navigation/public.d.ts +237 -0
- package/src/runtime/app/paths/client.js +37 -37
- package/src/runtime/app/paths/index.js +1 -1
- package/src/runtime/app/paths/internal/client.js +34 -2
- package/src/runtime/app/paths/internal/server.js +6 -23
- package/src/runtime/app/paths/internal.d.ts +3 -0
- package/src/runtime/app/paths/public.d.ts +1 -29
- package/src/runtime/app/paths/server.js +36 -17
- package/src/runtime/app/paths/types.d.ts +11 -19
- package/src/runtime/app/server/index.js +2 -2
- package/src/runtime/app/server/public.d.ts +201 -0
- package/src/runtime/app/server/remote/command.js +13 -11
- package/src/runtime/app/server/remote/form.js +61 -39
- package/src/runtime/app/server/remote/prerender.js +45 -45
- package/src/runtime/app/server/remote/query.js +106 -108
- package/src/runtime/app/server/remote/requested.js +27 -19
- package/src/runtime/app/server/remote/shared.js +76 -76
- package/src/runtime/app/service-worker/index.js +24 -0
- package/src/runtime/app/state/client.js +4 -2
- package/src/runtime/app/state/index.js +7 -5
- package/src/runtime/app/state/public.d.ts +72 -0
- package/src/runtime/app/state/server.js +3 -0
- package/src/runtime/app/stores.js +15 -78
- package/src/runtime/client/bundle.js +1 -1
- package/src/runtime/client/client-entry.js +3 -0
- package/src/runtime/client/client.js +1336 -648
- package/src/runtime/client/constants.js +3 -6
- package/src/runtime/client/entry.js +24 -3
- package/src/runtime/client/fetcher.js +25 -25
- package/src/runtime/client/ndjson.js +1 -1
- package/src/runtime/client/parse.js +1 -1
- package/src/runtime/client/payload.js +17 -0
- package/src/runtime/client/remote-functions/cache.svelte.js +3 -1
- package/src/runtime/client/remote-functions/command.svelte.js +37 -61
- package/src/runtime/client/remote-functions/form.svelte.js +267 -178
- package/src/runtime/client/remote-functions/prerender.svelte.js +32 -11
- package/src/runtime/client/remote-functions/query/index.js +10 -17
- package/src/runtime/client/remote-functions/query/instance.svelte.js +64 -18
- package/src/runtime/client/remote-functions/query/proxy.js +5 -5
- package/src/runtime/client/remote-functions/query-batch.svelte.js +61 -70
- package/src/runtime/client/remote-functions/query-live/index.js +1 -1
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +63 -21
- package/src/runtime/client/remote-functions/query-live/iterator.js +15 -12
- package/src/runtime/client/remote-functions/query-live/proxy.js +2 -12
- package/src/runtime/client/remote-functions/shared.svelte.js +90 -65
- package/src/runtime/client/snapshots.js +147 -0
- package/src/runtime/client/state.svelte.js +94 -55
- package/src/runtime/client/stream.js +3 -2
- package/src/runtime/client/types.d.ts +13 -9
- package/src/runtime/client/utils.js +22 -110
- package/src/runtime/components/root.svelte +56 -0
- package/src/runtime/env/dynamic/private.js +7 -0
- package/src/runtime/env/dynamic/public.js +7 -0
- package/src/runtime/env/static/private.js +6 -0
- package/src/runtime/env/static/public.js +6 -0
- package/src/runtime/form-utils.js +172 -61
- package/src/runtime/pathname.js +20 -32
- package/src/runtime/props.svelte.js +72 -0
- package/src/runtime/server/constants.js +0 -3
- package/src/runtime/server/cookie.js +72 -55
- package/src/runtime/server/csrf.js +65 -0
- package/src/runtime/server/data/index.js +49 -53
- package/src/runtime/server/dev.js +22 -0
- package/src/runtime/server/endpoint.js +6 -7
- package/src/runtime/server/env_module.js +0 -5
- package/src/runtime/server/errors.js +160 -0
- package/src/runtime/server/fetch.js +32 -39
- package/src/runtime/server/index.js +90 -64
- package/src/runtime/server/internal.js +71 -0
- package/src/runtime/server/page/actions.js +85 -64
- package/src/runtime/server/page/crypto.js +2 -2
- package/src/runtime/server/page/csp.js +88 -104
- package/src/runtime/server/page/data_serializer.js +24 -25
- package/src/runtime/server/page/index.js +36 -53
- package/src/runtime/server/page/load_data.js +50 -57
- package/src/runtime/server/page/render.js +173 -239
- package/src/runtime/server/page/respond_with_error.js +17 -31
- package/src/runtime/server/page/serialize_data.js +2 -13
- package/src/runtime/server/page/server_routing.js +85 -26
- package/src/runtime/server/remote-functions.js +640 -0
- package/src/runtime/server/respond.js +190 -131
- package/src/runtime/server/sourcemaps.js +183 -0
- package/src/runtime/server/state.js +53 -0
- package/src/runtime/server/utils.js +13 -155
- package/src/runtime/shared.js +20 -40
- package/src/runtime/utils.js +3 -0
- package/src/types/ambient-private.d.ts +11 -1
- package/src/types/ambient.d.ts +87 -36
- package/src/types/global-private.d.ts +25 -25
- package/src/types/internal.d.ts +163 -157
- package/src/types/private.d.ts +41 -1
- package/src/utils/error.js +28 -4
- package/src/utils/escape.js +9 -25
- package/src/utils/features.js +1 -1
- package/src/utils/filesystem.js +1 -23
- package/src/utils/fork.js +7 -2
- package/src/utils/hash.js +21 -0
- package/src/utils/http.js +8 -7
- package/src/utils/import.js +9 -2
- package/src/utils/imports.js +83 -0
- package/src/utils/mime.js +9 -0
- package/src/utils/page_nodes.js +6 -7
- package/src/utils/params.js +67 -0
- package/src/utils/regex.js +9 -0
- package/src/utils/routing.js +145 -73
- package/src/utils/shared-iterator.js +5 -0
- package/src/utils/streaming.js +14 -4
- package/src/utils/url.js +33 -2
- package/src/version.js +1 -1
- package/types/index.d.ts +2616 -2433
- package/types/index.d.ts.map +120 -106
- package/src/core/sync/write_ambient.js +0 -18
- package/src/core/sync/write_root.js +0 -148
- package/src/core/sync/write_tsconfig.js +0 -250
- package/src/exports/internal/server.js +0 -22
- package/src/exports/node/polyfills.js +0 -30
- package/src/runtime/server/app.js +0 -9
- package/src/runtime/server/remote.js +0 -457
- package/src/runtime/shared-server.js +0 -7
- package/src/runtime/telemetry/otel.js +0 -21
- package/src/runtime/telemetry/record_span.js +0 -65
- package/src/types/synthetic/$lib.md +0 -5
- /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
- /package/src/{runtime/telemetry/noop.js → telemetry.js} +0 -0
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
|
2
|
-
/** @import { RemoteFormInput, RemoteForm, RemoteQueryUpdate } from '@sveltejs/kit' */
|
|
3
|
-
/** @import { InternalRemoteFormIssue
|
|
4
|
-
import { app_dir, base } from '
|
|
5
|
-
import * as devalue from 'devalue';
|
|
2
|
+
/** @import { RemoteFormInput, RemoteForm, RemoteQueryUpdate } from '@sveltejs/kit/remote' */
|
|
3
|
+
/** @import { InternalRemoteFormIssue } from 'types' */
|
|
4
|
+
import { app_dir, base } from '#app/paths';
|
|
6
5
|
import { DEV } from 'esm-env';
|
|
7
|
-
|
|
8
|
-
import {
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
query_responses,
|
|
9
|
+
_goto,
|
|
10
|
+
set_nearest_error_page,
|
|
11
|
+
handle_error,
|
|
12
|
+
refreshAll
|
|
13
|
+
} from '../client.js';
|
|
14
|
+
import { page } from '../state.svelte.js';
|
|
9
15
|
import { tick } from 'svelte';
|
|
10
|
-
import {
|
|
16
|
+
import { categorize_updates, remote_request } from './shared.svelte.js';
|
|
11
17
|
import { createAttachmentKey } from 'svelte/attachments';
|
|
12
18
|
import {
|
|
13
19
|
convert_formdata,
|
|
@@ -18,7 +24,11 @@ import {
|
|
|
18
24
|
build_path_string,
|
|
19
25
|
normalize_issue,
|
|
20
26
|
serialize_binary_form,
|
|
21
|
-
|
|
27
|
+
deep_get,
|
|
28
|
+
DELETE_KEY,
|
|
29
|
+
BINARY_FORM_CONTENT_TYPE,
|
|
30
|
+
parse_form_key,
|
|
31
|
+
coerce_form_value
|
|
22
32
|
} from '../../form-utils.js';
|
|
23
33
|
|
|
24
34
|
/**
|
|
@@ -30,16 +40,22 @@ import {
|
|
|
30
40
|
* @returns {InternalRemoteFormIssue[]}
|
|
31
41
|
*/
|
|
32
42
|
function merge_with_server_issues(form_data, current_issues, client_issues) {
|
|
43
|
+
const client_names = new Set(client_issues.map((issue) => issue.name));
|
|
44
|
+
|
|
33
45
|
const merged = [
|
|
34
|
-
...current_issues.filter(
|
|
35
|
-
(issue) => issue.server && !client_issues.some((i) => i.name === issue.name)
|
|
36
|
-
),
|
|
46
|
+
...current_issues.filter((issue) => issue.server && !client_names.has(issue.name)),
|
|
37
47
|
...client_issues
|
|
38
48
|
];
|
|
39
49
|
|
|
40
|
-
|
|
50
|
+
/** @type {Map<string, number>} */
|
|
51
|
+
const positions = new Map();
|
|
52
|
+
let i = 0;
|
|
53
|
+
for (const key of form_data.keys()) {
|
|
54
|
+
if (!positions.has(key)) positions.set(key, i);
|
|
55
|
+
i++;
|
|
56
|
+
}
|
|
41
57
|
|
|
42
|
-
return merged.sort((a, b) =>
|
|
58
|
+
return merged.sort((a, b) => (positions.get(a.name) ?? -1) - (positions.get(b.name) ?? -1));
|
|
43
59
|
}
|
|
44
60
|
|
|
45
61
|
/**
|
|
@@ -53,24 +69,56 @@ export function form(id) {
|
|
|
53
69
|
/** @type {Map<any, { count: number, instance: RemoteForm<T, U> }>} */
|
|
54
70
|
const instances = new Map();
|
|
55
71
|
|
|
72
|
+
/** @type {StandardSchemaV1 | null} */
|
|
73
|
+
let shared_preflight_schema = null;
|
|
74
|
+
|
|
56
75
|
/** @param {string | number | boolean} [key] */
|
|
57
76
|
function create_instance(key) {
|
|
58
77
|
const action_id_without_key = id;
|
|
59
78
|
const action_id = id + (key != undefined ? `/${JSON.stringify(key)}` : '');
|
|
60
|
-
const action = '
|
|
79
|
+
const action = '/remote=' + encodeURIComponent(action_id);
|
|
80
|
+
|
|
81
|
+
/** @type {string} */
|
|
82
|
+
let cached_search = '';
|
|
83
|
+
/** @type {string} */
|
|
84
|
+
let cached_query = '';
|
|
85
|
+
|
|
86
|
+
/** @returns {string} */
|
|
87
|
+
function get_action() {
|
|
88
|
+
if (page.url.search !== cached_search) {
|
|
89
|
+
cached_search = page.url.search;
|
|
90
|
+
|
|
91
|
+
if (page.url.search) {
|
|
92
|
+
const params = new URLSearchParams(page.url.search);
|
|
93
|
+
params.delete('/remote');
|
|
94
|
+
cached_query = params.toString();
|
|
95
|
+
} else {
|
|
96
|
+
cached_query = '';
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return `?${cached_query && `${cached_query}&`}${action}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// the output of a non-enhanced submission that resulted in this page —
|
|
104
|
+
// consume it so the form's state survives hydration (form outputs are
|
|
105
|
+
// always value nodes; the server never serializes them as errors)
|
|
106
|
+
/** @type {{ input?: Record<string, any>, issues?: InternalRemoteFormIssue[], result?: any } | undefined} */
|
|
107
|
+
const initial = query_responses[action_id]?.v;
|
|
108
|
+
delete query_responses[action_id];
|
|
61
109
|
|
|
62
110
|
/**
|
|
63
111
|
* @type {Record<string, string | string[] | File | File[]>}
|
|
64
112
|
*/
|
|
65
|
-
let input = $state({});
|
|
113
|
+
let input = $state(initial?.input ?? {});
|
|
66
114
|
|
|
67
115
|
/** @type {InternalRemoteFormIssue[]} */
|
|
68
|
-
let raw_issues = $state.raw([]);
|
|
116
|
+
let raw_issues = $state.raw(initial?.issues ?? []);
|
|
69
117
|
|
|
70
118
|
const issues = $derived(flatten_issues(raw_issues));
|
|
71
119
|
|
|
72
120
|
/** @type {any} */
|
|
73
|
-
let result = $state.raw(
|
|
121
|
+
let result = $state.raw(initial?.result);
|
|
74
122
|
|
|
75
123
|
/** @type {number} */
|
|
76
124
|
let pending_count = $state(0);
|
|
@@ -83,7 +131,9 @@ export function form(id) {
|
|
|
83
131
|
*/
|
|
84
132
|
let enhance_callback = async (instance) => {
|
|
85
133
|
if (await instance.submit()) {
|
|
86
|
-
|
|
134
|
+
await tick();
|
|
135
|
+
// We call reset from the prototype to avoid DOM clobbering
|
|
136
|
+
HTMLFormElement.prototype.reset.call(instance.element);
|
|
87
137
|
}
|
|
88
138
|
};
|
|
89
139
|
|
|
@@ -91,13 +141,22 @@ export function form(id) {
|
|
|
91
141
|
let element = null;
|
|
92
142
|
|
|
93
143
|
/** @type {Record<string, boolean>} */
|
|
94
|
-
let touched = {};
|
|
144
|
+
let touched = $state({});
|
|
145
|
+
|
|
146
|
+
/** @type {Record<string, boolean>} */
|
|
147
|
+
let dirty = $state({});
|
|
95
148
|
|
|
96
|
-
|
|
149
|
+
/** @type {Record<string, boolean>} */
|
|
150
|
+
let can_validate = {};
|
|
151
|
+
|
|
152
|
+
let submitted = $state(false);
|
|
97
153
|
|
|
98
154
|
/** @type {InternalRemoteFormIssue[] | null} */
|
|
99
155
|
let unread_issues = null;
|
|
100
156
|
|
|
157
|
+
/** @type {{ name: string; type: 'number' | 'boolean' | null; is_array: boolean } | null} */
|
|
158
|
+
let previous_submitter = null;
|
|
159
|
+
|
|
101
160
|
/**
|
|
102
161
|
* In dev, warn if there are validation issues going unread
|
|
103
162
|
*/
|
|
@@ -132,8 +191,8 @@ export function form(id) {
|
|
|
132
191
|
* @returns {Record<string, any>}
|
|
133
192
|
*/
|
|
134
193
|
function convert(form_data) {
|
|
135
|
-
const data = convert_formdata(form_data);
|
|
136
|
-
if (key !== undefined && !
|
|
194
|
+
const data = convert_formdata(action_id_without_key, form_data);
|
|
195
|
+
if (key !== undefined && !('id' in data)) {
|
|
137
196
|
data.id = key;
|
|
138
197
|
}
|
|
139
198
|
return data;
|
|
@@ -163,109 +222,84 @@ export function form(id) {
|
|
|
163
222
|
/** @type {Error | undefined} */
|
|
164
223
|
let updates_error;
|
|
165
224
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
if (updates_error) {
|
|
172
|
-
throw updates_error;
|
|
173
|
-
}
|
|
225
|
+
const promise =
|
|
226
|
+
/** @type {Promise<boolean> & { updates: (...args: RemoteQueryUpdate[]) => Promise<boolean> }} */ (
|
|
227
|
+
(async () => {
|
|
228
|
+
try {
|
|
229
|
+
await Promise.resolve();
|
|
174
230
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
}
|
|
231
|
+
if (updates_error) {
|
|
232
|
+
throw updates_error;
|
|
233
|
+
}
|
|
179
234
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
235
|
+
if (should_preflight) {
|
|
236
|
+
const valid = await preflight(form_data);
|
|
237
|
+
if (!valid) return false;
|
|
238
|
+
}
|
|
183
239
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
240
|
+
const { blob } = serialize_binary_form(convert(form_data), {
|
|
241
|
+
remote_refreshes: Array.from(refreshes ?? [])
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const response = await remote_request(
|
|
245
|
+
`${base}/${app_dir}/remote/${action_id_without_key}`,
|
|
246
|
+
{
|
|
247
|
+
method: 'POST',
|
|
248
|
+
headers: {
|
|
249
|
+
'Content-Type': BINARY_FORM_CONTENT_TYPE,
|
|
250
|
+
// Forms cannot be called during rendering, so it's save to use location here
|
|
251
|
+
'x-sveltekit-pathname': location.pathname,
|
|
252
|
+
'x-sveltekit-search': location.search
|
|
253
|
+
},
|
|
254
|
+
body: blob
|
|
255
|
+
}
|
|
256
|
+
);
|
|
194
257
|
|
|
195
|
-
|
|
196
|
-
// We only end up here in case of a network error or if the server has an internal error
|
|
197
|
-
// (which shouldn't happen because we handle errors on the server and always send a 200 response)
|
|
198
|
-
throw new Error('Failed to execute remote function');
|
|
199
|
-
}
|
|
258
|
+
({ issues: raw_issues = [], result } = response._ ?? {});
|
|
200
259
|
|
|
201
|
-
|
|
260
|
+
// if the developer took control of updates via `.updates(...)` (even with
|
|
261
|
+
// no arguments), or the server performed explicit refreshes, don't invalidateAll
|
|
262
|
+
const should_refresh = refreshes === null && !response.r;
|
|
202
263
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
264
|
+
if (response.redirect) {
|
|
265
|
+
// Use internal version to allow redirects to external URLs
|
|
266
|
+
void _goto(response.redirect, {
|
|
267
|
+
refreshAll: should_refresh
|
|
268
|
+
});
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
206
271
|
|
|
207
|
-
|
|
208
|
-
({ issues: raw_issues = [], result } = devalue.parse(form_result.result, app.decoders));
|
|
209
|
-
const succeeded = raw_issues.length === 0;
|
|
272
|
+
const succeeded = raw_issues.length === 0;
|
|
210
273
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
} else {
|
|
215
|
-
if (form_result.refreshes) {
|
|
216
|
-
apply_refreshes(form_result.refreshes);
|
|
274
|
+
if (succeeded) {
|
|
275
|
+
if (should_refresh) {
|
|
276
|
+
void refreshAll();
|
|
217
277
|
}
|
|
218
|
-
|
|
219
|
-
|
|
278
|
+
} else {
|
|
279
|
+
if (DEV) {
|
|
280
|
+
warn_on_missing_issue_reads();
|
|
220
281
|
}
|
|
221
282
|
}
|
|
222
|
-
} else {
|
|
223
|
-
if (DEV) {
|
|
224
|
-
warn_on_missing_issue_reads();
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
283
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
invalidateAll:
|
|
245
|
-
refreshes === null && !stringified_refreshes && !stringified_reconnects
|
|
246
|
-
},
|
|
247
|
-
0
|
|
248
|
-
);
|
|
249
|
-
return true;
|
|
250
|
-
} else {
|
|
251
|
-
throw new HttpError(form_result.status ?? 500, form_result.error);
|
|
252
|
-
}
|
|
253
|
-
} catch (e) {
|
|
254
|
-
result = undefined;
|
|
255
|
-
throw e;
|
|
256
|
-
} finally {
|
|
257
|
-
overrides?.forEach((fn) => fn());
|
|
258
|
-
|
|
259
|
-
void tick().then(() => {
|
|
260
|
-
if (entry) {
|
|
261
|
-
entry.count--;
|
|
262
|
-
if (entry.count === 0) {
|
|
263
|
-
instances.delete(key);
|
|
264
|
-
}
|
|
284
|
+
return succeeded;
|
|
285
|
+
} catch (e) {
|
|
286
|
+
result = undefined;
|
|
287
|
+
raw_issues = [];
|
|
288
|
+
throw e;
|
|
289
|
+
} finally {
|
|
290
|
+
overrides?.forEach((fn) => fn());
|
|
291
|
+
|
|
292
|
+
void tick().then(() => {
|
|
293
|
+
if (entry) {
|
|
294
|
+
entry.count--;
|
|
295
|
+
if (entry.count === 0) {
|
|
296
|
+
instances.delete(key);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
});
|
|
265
300
|
}
|
|
266
|
-
})
|
|
267
|
-
|
|
268
|
-
})();
|
|
301
|
+
})()
|
|
302
|
+
);
|
|
269
303
|
|
|
270
304
|
let updates_called = false;
|
|
271
305
|
promise.updates = (...args) => {
|
|
@@ -319,7 +353,8 @@ export function form(id) {
|
|
|
319
353
|
*/
|
|
320
354
|
async function preflight(form_data) {
|
|
321
355
|
const data = convert(form_data);
|
|
322
|
-
const
|
|
356
|
+
const schema = preflight_schema ?? shared_preflight_schema;
|
|
357
|
+
const validated = await schema?.['~standard'].validate(data);
|
|
323
358
|
|
|
324
359
|
if (validated?.issues) {
|
|
325
360
|
raw_issues = merge_with_server_issues(
|
|
@@ -343,11 +378,13 @@ export function form(id) {
|
|
|
343
378
|
return true;
|
|
344
379
|
}
|
|
345
380
|
|
|
346
|
-
/** @type {RemoteForm<T, U>} */
|
|
347
|
-
const instance = {};
|
|
381
|
+
const instance = /** @type {RemoteForm<T, U>} */ ({});
|
|
348
382
|
|
|
349
383
|
instance.method = 'POST';
|
|
350
|
-
instance
|
|
384
|
+
Object.defineProperty(instance, 'action', {
|
|
385
|
+
get: get_action,
|
|
386
|
+
enumerable: true
|
|
387
|
+
});
|
|
351
388
|
|
|
352
389
|
instance[createAttachmentKey()] = (/** @type {HTMLFormElement} */ form) => {
|
|
353
390
|
if (element) {
|
|
@@ -363,6 +400,8 @@ export function form(id) {
|
|
|
363
400
|
element = form;
|
|
364
401
|
|
|
365
402
|
touched = {};
|
|
403
|
+
dirty = {};
|
|
404
|
+
can_validate = {};
|
|
366
405
|
|
|
367
406
|
/** @param {SubmitEvent} event */
|
|
368
407
|
const handle_submit = async (event) => {
|
|
@@ -396,6 +435,33 @@ export function form(id) {
|
|
|
396
435
|
|
|
397
436
|
const form_data = new FormData(form, event.submitter);
|
|
398
437
|
|
|
438
|
+
if (
|
|
439
|
+
previous_submitter !== null &&
|
|
440
|
+
!Array.from(form_data.keys())
|
|
441
|
+
.map((k) => parse_form_key(action_id_without_key, k).name)
|
|
442
|
+
.includes(previous_submitter.name)
|
|
443
|
+
) {
|
|
444
|
+
set_nested_value(input, previous_submitter, undefined);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
if (event.submitter) {
|
|
448
|
+
const name = event.submitter.getAttribute('name');
|
|
449
|
+
|
|
450
|
+
/** @type {null | ReturnType<typeof parse_form_key>} */
|
|
451
|
+
let submitter = null;
|
|
452
|
+
|
|
453
|
+
const value = /** @type {any} */ (event.submitter).value;
|
|
454
|
+
|
|
455
|
+
if (name !== null && value !== undefined) {
|
|
456
|
+
submitter = parse_form_key(action_id_without_key, name);
|
|
457
|
+
set_nested_value(input, submitter, coerce_form_value(submitter.type, value));
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
previous_submitter = submitter;
|
|
461
|
+
} else {
|
|
462
|
+
previous_submitter = null;
|
|
463
|
+
}
|
|
464
|
+
|
|
399
465
|
if (DEV) {
|
|
400
466
|
validate_form_data(form_data, clone(form).enctype);
|
|
401
467
|
}
|
|
@@ -412,10 +478,12 @@ export function form(id) {
|
|
|
412
478
|
|
|
413
479
|
await enhance_callback(create_enhance_callback_instance(form, form_data));
|
|
414
480
|
} catch (e) {
|
|
415
|
-
const error =
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
481
|
+
const error = await handle_error(e, {
|
|
482
|
+
params: {},
|
|
483
|
+
route: { id: null },
|
|
484
|
+
url: new URL(location.href)
|
|
485
|
+
});
|
|
486
|
+
void set_nearest_error_page(error);
|
|
419
487
|
} finally {
|
|
420
488
|
pending_count--;
|
|
421
489
|
}
|
|
@@ -427,17 +495,14 @@ export function form(id) {
|
|
|
427
495
|
// but that makes the types unnecessarily awkward
|
|
428
496
|
const element = /** @type {HTMLInputElement} */ (e.target);
|
|
429
497
|
|
|
430
|
-
|
|
498
|
+
const name = element.name;
|
|
431
499
|
if (!name) return;
|
|
432
500
|
|
|
433
|
-
const
|
|
434
|
-
if (is_array) name = name.slice(0, -2);
|
|
501
|
+
const field = parse_form_key(action_id_without_key, name);
|
|
435
502
|
|
|
436
503
|
const is_file = element.type === 'file';
|
|
437
504
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
if (is_array) {
|
|
505
|
+
if (field.is_array) {
|
|
441
506
|
let value;
|
|
442
507
|
|
|
443
508
|
if (element.tagName === 'SELECT') {
|
|
@@ -447,7 +512,7 @@ export function form(id) {
|
|
|
447
512
|
);
|
|
448
513
|
} else {
|
|
449
514
|
const elements = /** @type {HTMLInputElement[]} */ (
|
|
450
|
-
Array.from(form.querySelectorAll(`[name="${name}
|
|
515
|
+
Array.from(form.querySelectorAll(`[name="${name}"]`))
|
|
451
516
|
);
|
|
452
517
|
|
|
453
518
|
if (DEV) {
|
|
@@ -468,7 +533,7 @@ export function form(id) {
|
|
|
468
533
|
}
|
|
469
534
|
}
|
|
470
535
|
|
|
471
|
-
set_nested_value(input,
|
|
536
|
+
set_nested_value(input, field, value);
|
|
472
537
|
} else if (is_file) {
|
|
473
538
|
if (DEV && element.multiple) {
|
|
474
539
|
throw new Error(
|
|
@@ -479,28 +544,19 @@ export function form(id) {
|
|
|
479
544
|
const file = /** @type {HTMLInputElement & { files: FileList }} */ (element).files[0];
|
|
480
545
|
|
|
481
546
|
if (file) {
|
|
482
|
-
set_nested_value(input,
|
|
547
|
+
set_nested_value(input, field, file);
|
|
483
548
|
} else {
|
|
484
|
-
|
|
485
|
-
const path_parts = name.split(/\.|\[|\]/).filter(Boolean);
|
|
486
|
-
let current = /** @type {any} */ (input);
|
|
487
|
-
for (let i = 0; i < path_parts.length - 1; i++) {
|
|
488
|
-
if (current[path_parts[i]] == null) return;
|
|
489
|
-
current = current[path_parts[i]];
|
|
490
|
-
}
|
|
491
|
-
delete current[path_parts[path_parts.length - 1]];
|
|
549
|
+
set_nested_value(input, field, DELETE_KEY);
|
|
492
550
|
}
|
|
493
551
|
} else {
|
|
494
552
|
set_nested_value(
|
|
495
553
|
input,
|
|
496
|
-
|
|
554
|
+
field,
|
|
497
555
|
element.type === 'checkbox' && !element.checked ? null : element.value
|
|
498
556
|
);
|
|
499
557
|
}
|
|
500
558
|
|
|
501
|
-
name =
|
|
502
|
-
|
|
503
|
-
touched[name] = true;
|
|
559
|
+
dirty[field.name] = true;
|
|
504
560
|
};
|
|
505
561
|
|
|
506
562
|
const handle_reset = async () => {
|
|
@@ -508,19 +564,39 @@ export function form(id) {
|
|
|
508
564
|
// the inputs are actually updated (so that it can be cancelled)
|
|
509
565
|
await tick();
|
|
510
566
|
|
|
511
|
-
input = convert_formdata(new FormData(form));
|
|
567
|
+
input = convert_formdata(action_id_without_key, new FormData(form));
|
|
568
|
+
raw_issues = [];
|
|
569
|
+
touched = {};
|
|
570
|
+
dirty = {};
|
|
571
|
+
can_validate = {};
|
|
572
|
+
submitted = false;
|
|
573
|
+
};
|
|
574
|
+
|
|
575
|
+
/** @param {Event} e */
|
|
576
|
+
const handle_focusout = (e) => {
|
|
577
|
+
const name = /** @type {HTMLInputElement} */ (e.target).name;
|
|
578
|
+
if (!name) return;
|
|
579
|
+
|
|
580
|
+
const field = parse_form_key(action_id_without_key, name);
|
|
581
|
+
|
|
582
|
+
touched[field.name] = true;
|
|
583
|
+
|
|
584
|
+
if (Object.hasOwn(dirty, field.name)) {
|
|
585
|
+
can_validate[field.name] = true;
|
|
586
|
+
}
|
|
512
587
|
};
|
|
513
588
|
|
|
514
589
|
form.addEventListener('submit', handle_submit);
|
|
515
590
|
form.addEventListener('input', handle_input);
|
|
591
|
+
form.addEventListener('focusout', handle_focusout);
|
|
516
592
|
form.addEventListener('reset', handle_reset);
|
|
517
593
|
|
|
518
594
|
return () => {
|
|
519
595
|
form.removeEventListener('submit', handle_submit);
|
|
520
596
|
form.removeEventListener('input', handle_input);
|
|
597
|
+
form.removeEventListener('focusout', handle_focusout);
|
|
521
598
|
form.removeEventListener('reset', handle_reset);
|
|
522
599
|
element = null;
|
|
523
|
-
preflight_schema = undefined;
|
|
524
600
|
};
|
|
525
601
|
};
|
|
526
602
|
|
|
@@ -551,29 +627,35 @@ export function form(id) {
|
|
|
551
627
|
|
|
552
628
|
const submission = submit(form_data, true);
|
|
553
629
|
|
|
554
|
-
|
|
630
|
+
const decrement = () => {
|
|
555
631
|
pending_count--;
|
|
556
|
-
}
|
|
632
|
+
};
|
|
633
|
+
void submission.then(decrement, decrement);
|
|
557
634
|
|
|
558
635
|
return submission;
|
|
559
636
|
}
|
|
560
637
|
},
|
|
561
638
|
fields: {
|
|
562
639
|
get: () =>
|
|
563
|
-
create_field_proxy(
|
|
564
|
-
|
|
565
|
-
() => input,
|
|
566
|
-
(path, value) => {
|
|
640
|
+
create_field_proxy({
|
|
641
|
+
form_id: action_id_without_key,
|
|
642
|
+
get: () => input,
|
|
643
|
+
set: (path, value) => {
|
|
567
644
|
if (path.length === 0) {
|
|
568
645
|
input = value;
|
|
569
|
-
} else {
|
|
646
|
+
} else if (value !== deep_get(input, path)) {
|
|
570
647
|
deep_set(input, path.map(String), value);
|
|
571
648
|
|
|
572
649
|
const key = build_path_string(path);
|
|
573
|
-
|
|
650
|
+
|
|
651
|
+
if (element) {
|
|
652
|
+
touched[key] = true;
|
|
653
|
+
dirty[key] = true;
|
|
654
|
+
can_validate[key] = true;
|
|
655
|
+
}
|
|
574
656
|
}
|
|
575
657
|
},
|
|
576
|
-
(path, all) => {
|
|
658
|
+
get_issues: (path, all) => {
|
|
577
659
|
if (DEV && unread_issues !== null && path !== undefined) {
|
|
578
660
|
unread_issues = unread_issues.filter((issue) => {
|
|
579
661
|
return (
|
|
@@ -584,8 +666,10 @@ export function form(id) {
|
|
|
584
666
|
}
|
|
585
667
|
|
|
586
668
|
return issues;
|
|
587
|
-
}
|
|
588
|
-
|
|
669
|
+
},
|
|
670
|
+
get_touched: () => touched,
|
|
671
|
+
get_dirty: () => dirty
|
|
672
|
+
})
|
|
589
673
|
},
|
|
590
674
|
result: {
|
|
591
675
|
get: () => result
|
|
@@ -593,16 +677,24 @@ export function form(id) {
|
|
|
593
677
|
pending: {
|
|
594
678
|
get: () => pending_count
|
|
595
679
|
},
|
|
680
|
+
submitted: {
|
|
681
|
+
get: () => submitted
|
|
682
|
+
},
|
|
596
683
|
preflight: {
|
|
597
684
|
/** @type {RemoteForm<T, U>['preflight']} */
|
|
598
685
|
value: (schema) => {
|
|
599
686
|
preflight_schema = schema;
|
|
687
|
+
|
|
688
|
+
if (key === undefined) {
|
|
689
|
+
shared_preflight_schema = schema;
|
|
690
|
+
}
|
|
691
|
+
|
|
600
692
|
return instance;
|
|
601
693
|
}
|
|
602
694
|
},
|
|
603
695
|
validate: {
|
|
604
696
|
/** @type {RemoteForm<any, any>['validate']} */
|
|
605
|
-
value: async ({
|
|
697
|
+
value: async ({ all = false, preflightOnly = false } = {}) => {
|
|
606
698
|
if (!element) return;
|
|
607
699
|
|
|
608
700
|
const id = ++validate_id;
|
|
@@ -620,8 +712,8 @@ export function form(id) {
|
|
|
620
712
|
let array = [];
|
|
621
713
|
|
|
622
714
|
const data = convert(form_data);
|
|
623
|
-
|
|
624
|
-
const validated = await
|
|
715
|
+
const schema = preflight_schema ?? shared_preflight_schema;
|
|
716
|
+
const validated = await schema?.['~standard'].validate(data);
|
|
625
717
|
|
|
626
718
|
if (validate_id !== id) {
|
|
627
719
|
return;
|
|
@@ -630,34 +722,31 @@ export function form(id) {
|
|
|
630
722
|
if (validated?.issues) {
|
|
631
723
|
array = validated.issues.map((issue) => normalize_issue(issue, false));
|
|
632
724
|
} else if (!preflightOnly) {
|
|
633
|
-
const
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
'
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
725
|
+
const result = await remote_request(
|
|
726
|
+
`${base}/${app_dir}/remote/${action_id_without_key}`,
|
|
727
|
+
{
|
|
728
|
+
method: 'POST',
|
|
729
|
+
headers: {
|
|
730
|
+
'Content-Type': BINARY_FORM_CONTENT_TYPE,
|
|
731
|
+
// Validation should not be and will not be called during rendering, so it's save to use location here
|
|
732
|
+
'x-sveltekit-pathname': location.pathname,
|
|
733
|
+
'x-sveltekit-search': location.search
|
|
734
|
+
},
|
|
735
|
+
body: serialize_binary_form(data, {
|
|
736
|
+
validate_only: true
|
|
737
|
+
}).blob
|
|
738
|
+
}
|
|
739
|
+
);
|
|
647
740
|
|
|
648
741
|
if (validate_id !== id) {
|
|
649
742
|
return;
|
|
650
743
|
}
|
|
651
744
|
|
|
652
|
-
|
|
653
|
-
array = /** @type {InternalRemoteFormIssue[]} */ (
|
|
654
|
-
devalue.parse(result.result, app.decoders)
|
|
655
|
-
);
|
|
656
|
-
}
|
|
745
|
+
array = /** @type {InternalRemoteFormIssue[]} */ (result._);
|
|
657
746
|
}
|
|
658
747
|
|
|
659
|
-
if (!
|
|
660
|
-
array = array.filter((issue) =>
|
|
748
|
+
if (!all && !submitted) {
|
|
749
|
+
array = array.filter((issue) => can_validate[issue.name]);
|
|
661
750
|
}
|
|
662
751
|
|
|
663
752
|
const is_server_validation = !validated?.issues && !preflightOnly;
|