@sveltejs/kit 3.0.0-next.0 → 3.0.0-next.10
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 +33 -21
- package/src/core/adapt/builder.js +29 -43
- package/src/core/adapt/index.js +1 -4
- package/src/core/config/index.js +132 -71
- package/src/core/config/options.js +294 -244
- package/src/core/config/types.d.ts +1 -1
- package/src/core/env.js +121 -5
- package/src/core/generate_manifest/index.js +12 -15
- package/src/core/postbuild/analyse.js +7 -19
- package/src/core/postbuild/crawl.js +22 -6
- package/src/core/postbuild/prerender.js +42 -23
- package/src/core/sync/create_manifest_data/index.js +53 -50
- package/src/core/sync/sync.js +0 -2
- package/src/core/sync/write_client_manifest.js +8 -15
- package/src/core/sync/write_env.js +2 -1
- package/src/core/sync/write_non_ambient.js +12 -9
- package/src/core/sync/write_server.js +13 -14
- package/src/core/sync/write_tsconfig.js +16 -8
- package/src/core/sync/write_tsconfig_test/package.json +7 -0
- package/src/core/sync/write_types/index.js +28 -24
- package/src/core/utils.js +2 -2
- package/src/exports/env/index.js +12 -0
- package/src/exports/hooks/index.js +3 -9
- package/src/exports/hooks/sequence.js +3 -2
- package/src/exports/index.js +35 -13
- package/src/exports/internal/client.js +5 -0
- package/src/exports/internal/env.js +2 -2
- package/src/exports/internal/index.js +1 -90
- package/src/exports/internal/{event.js → server/event.js} +1 -2
- package/src/exports/internal/server/index.js +33 -0
- package/src/exports/internal/shared.js +89 -0
- package/src/exports/node/index.js +62 -15
- package/src/exports/params.js +63 -0
- package/src/exports/public.d.ts +323 -170
- package/src/exports/url.js +86 -0
- package/src/exports/vite/build/build_server.js +53 -59
- package/src/exports/vite/build/remote.js +18 -11
- package/src/exports/vite/build/utils.js +0 -8
- package/src/exports/vite/dev/index.js +42 -44
- package/src/exports/vite/index.js +810 -593
- package/src/exports/vite/preview/index.js +29 -22
- package/src/exports/vite/static_analysis/index.js +2 -4
- package/src/exports/vite/static_analysis/types.d.ts +14 -0
- package/src/exports/vite/utils.js +21 -36
- 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 +6 -0
- package/src/runtime/app/forms.js +24 -7
- package/src/runtime/app/paths/client.js +4 -10
- package/src/runtime/app/paths/internal/client.js +4 -2
- package/src/runtime/app/paths/internal/server.js +2 -23
- package/src/runtime/app/paths/public.d.ts +0 -28
- package/src/runtime/app/paths/server.js +9 -5
- package/src/runtime/app/server/remote/command.js +0 -3
- package/src/runtime/app/server/remote/form.js +27 -15
- package/src/runtime/app/server/remote/prerender.js +29 -36
- package/src/runtime/app/server/remote/query.js +101 -99
- package/src/runtime/app/server/remote/requested.js +22 -14
- package/src/runtime/app/server/remote/shared.js +30 -26
- package/src/runtime/app/state/client.js +1 -2
- package/src/runtime/app/stores.js +13 -76
- package/src/runtime/client/bundle.js +1 -1
- package/src/runtime/client/client-entry.js +3 -0
- package/src/runtime/client/client.js +411 -318
- package/src/runtime/client/entry.js +24 -3
- package/src/runtime/client/fetcher.js +3 -2
- package/src/runtime/client/ndjson.js +6 -33
- 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 +7 -32
- package/src/runtime/client/remote-functions/form.svelte.js +183 -112
- package/src/runtime/client/remote-functions/prerender.svelte.js +29 -8
- package/src/runtime/client/remote-functions/query/index.js +7 -14
- package/src/runtime/client/remote-functions/query/instance.svelte.js +63 -18
- package/src/runtime/client/remote-functions/query/proxy.js +3 -3
- package/src/runtime/client/remote-functions/query-batch.svelte.js +60 -68
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +60 -18
- package/src/runtime/client/remote-functions/query-live/iterator.js +36 -55
- package/src/runtime/client/remote-functions/shared.svelte.js +88 -62
- package/src/runtime/client/sse.js +32 -0
- package/src/runtime/client/state.svelte.js +65 -49
- package/src/runtime/client/stream.js +38 -0
- package/src/runtime/client/types.d.ts +11 -7
- package/src/runtime/client/utils.js +0 -96
- package/src/runtime/components/root.svelte +66 -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 +100 -22
- package/src/runtime/server/cookie.js +69 -52
- package/src/runtime/server/csrf.js +65 -0
- package/src/runtime/server/data/index.js +14 -18
- package/src/runtime/server/env_module.js +0 -5
- package/src/runtime/server/index.js +2 -2
- package/src/runtime/server/page/actions.js +41 -26
- package/src/runtime/server/page/index.js +9 -15
- package/src/runtime/server/page/load_data.js +1 -1
- package/src/runtime/server/page/render.js +112 -187
- package/src/runtime/server/page/respond_with_error.js +7 -8
- package/src/runtime/server/page/server_routing.js +27 -18
- package/src/runtime/server/remote.js +355 -216
- package/src/runtime/server/respond.js +97 -61
- package/src/runtime/server/utils.js +32 -9
- package/src/runtime/shared.js +83 -13
- package/src/runtime/telemetry/otel.js +1 -1
- package/src/runtime/types.d.ts +8 -0
- package/src/types/ambient.d.ts +10 -5
- package/src/types/global-private.d.ts +15 -22
- package/src/types/internal.d.ts +89 -74
- package/src/types/private.d.ts +33 -1
- package/src/utils/error.js +24 -4
- package/src/utils/import.js +6 -1
- package/src/utils/imports.js +83 -0
- package/src/utils/mime.js +9 -0
- package/src/utils/params.js +66 -0
- package/src/utils/routing.js +90 -44
- package/src/utils/shared-iterator.js +5 -0
- package/src/utils/streaming.js +14 -4
- package/src/utils/url.js +20 -2
- package/src/version.js +1 -1
- package/types/index.d.ts +467 -537
- package/types/index.d.ts.map +22 -39
- package/src/core/sync/write_root.js +0 -148
- package/src/exports/internal/server.js +0 -22
- package/src/types/synthetic/$lib.md +0 -5
- /package/src/exports/internal/{remote-functions.js → server/remote-functions.js} +0 -0
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
/** @import { RemoteResource, RemotePrerenderFunction } from '@sveltejs/kit' */
|
|
2
2
|
/** @import { RemotePrerenderInputsGenerator, RemotePrerenderInternals, MaybePromise } from 'types' */
|
|
3
3
|
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
|
4
|
-
import {
|
|
5
|
-
import { DEV } from 'esm-env';
|
|
4
|
+
import { json, error } from '@sveltejs/kit';
|
|
6
5
|
import { get_request_store } from '@sveltejs/kit/internal/server';
|
|
7
6
|
import { stringify, stringify_remote_arg } from '../../../shared.js';
|
|
8
7
|
import { noop } from '../../../../utils/functions.js';
|
|
9
8
|
import { app_dir, base } from '$app/paths/internal/server';
|
|
10
9
|
import {
|
|
11
10
|
create_validator,
|
|
12
|
-
get_cache,
|
|
13
11
|
get_response,
|
|
14
12
|
parse_remote_response,
|
|
15
13
|
run_remote_function
|
|
@@ -89,51 +87,46 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
89
87
|
|
|
90
88
|
/** @type {RemotePrerenderFunction<Input, Output> & { __: RemotePrerenderInternals }} */
|
|
91
89
|
const wrapper = (arg) => {
|
|
90
|
+
const { event, state } = get_request_store();
|
|
91
|
+
const payload = stringify_remote_arg(arg, state.transport);
|
|
92
|
+
|
|
93
|
+
// `get_response` (as opposed to bare `get_cache`) also registers the call in the
|
|
94
|
+
// implicit lookup, so that the result is inlined into the page payload (`data.p`)
|
|
95
|
+
// and the client doesn't need to fetch it again upon hydration
|
|
92
96
|
/** @type {Promise<Output> & Partial<RemoteResource<Output>>} */
|
|
93
|
-
const promise = (async () => {
|
|
94
|
-
const { event, state } = get_request_store();
|
|
95
|
-
const payload = stringify_remote_arg(arg, state.transport);
|
|
97
|
+
const promise = get_response(__, payload, state, async () => {
|
|
96
98
|
const id = __.id;
|
|
97
99
|
const url = `${base}/${app_dir}/remote/${id}${payload ? `/${payload}` : ''}`;
|
|
98
100
|
|
|
99
|
-
if (!state.prerendering && !
|
|
101
|
+
if (!state.prerendering && !__SVELTEKIT_DEV__ && !event.isRemoteRequest) {
|
|
100
102
|
try {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
error(prerendered.status, prerendered.error);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
return prerendered.result;
|
|
120
|
-
})
|
|
121
|
-
}).data;
|
|
122
|
-
|
|
123
|
-
return parse_remote_response(await promise, state.transport);
|
|
124
|
-
});
|
|
103
|
+
// TODO adapters can provide prerendered data more efficiently than
|
|
104
|
+
// fetching from the public internet
|
|
105
|
+
const response = await fetch(new URL(url, event.url.origin).href);
|
|
106
|
+
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
throw new Error('Prerendered response not found');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const prerendered = /** @type {RemoteFunctionResponse} */ await response.json();
|
|
112
|
+
|
|
113
|
+
if (prerendered.type === 'error') {
|
|
114
|
+
error(prerendered.status, prerendered.error);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return parse_remote_response(prerendered.data, state.transport)._;
|
|
125
118
|
} catch {
|
|
126
119
|
// not available prerendered, fallback to normal function
|
|
127
120
|
}
|
|
128
121
|
}
|
|
129
122
|
|
|
123
|
+
// during a prerender run, the same function might be invoked while rendering
|
|
124
|
+
// multiple pages — share the result across the entire run
|
|
130
125
|
if (state.prerendering?.remote_responses.has(url)) {
|
|
131
126
|
return /** @type {Promise<any>} */ (state.prerendering.remote_responses.get(url));
|
|
132
127
|
}
|
|
133
128
|
|
|
134
|
-
const promise =
|
|
135
|
-
run_remote_function(event, state, false, () => validate(arg), fn)
|
|
136
|
-
);
|
|
129
|
+
const promise = run_remote_function(event, state, false, () => validate(arg), fn);
|
|
137
130
|
|
|
138
131
|
if (state.prerendering) {
|
|
139
132
|
state.prerendering.remote_responses.set(url, promise);
|
|
@@ -142,7 +135,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
142
135
|
const result = await promise;
|
|
143
136
|
|
|
144
137
|
if (state.prerendering) {
|
|
145
|
-
const body = { type: 'result',
|
|
138
|
+
const body = { type: 'result', data: stringify({ _: result }, state.transport) };
|
|
146
139
|
state.prerendering.dependencies.set(url, {
|
|
147
140
|
body: JSON.stringify(body),
|
|
148
141
|
response: json(body)
|
|
@@ -151,7 +144,7 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
151
144
|
|
|
152
145
|
// TODO this is missing error/loading/current/status
|
|
153
146
|
return result;
|
|
154
|
-
})
|
|
147
|
+
});
|
|
155
148
|
|
|
156
149
|
promise.catch(noop);
|
|
157
150
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
/** @import { RemoteLiveQuery, RemoteLiveQueryFunction, RemoteQuery, RemoteQueryFunction } from '@sveltejs/kit' */
|
|
1
|
+
/** @import { RemoteLiveQuery, RemoteLiveQueryFunction, RemoteQuery, RemoteQueryFunction, RequestEvent } from '@sveltejs/kit' */
|
|
2
2
|
/** @import { RemoteInternals, MaybePromise, RequestState, RemoteQueryLiveInternals, RemoteQueryBatchInternals, RemoteQueryInternals, RemoteLiveQueryUserFunctionReturnType } from 'types' */
|
|
3
3
|
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
|
4
4
|
import { get_request_store } from '@sveltejs/kit/internal/server';
|
|
5
|
-
import { create_remote_key,
|
|
5
|
+
import { create_remote_key, stringify_remote_arg } from '../../../shared.js';
|
|
6
6
|
import { prerendering } from '$app/env/internal';
|
|
7
7
|
import {
|
|
8
8
|
create_validator,
|
|
@@ -11,10 +11,9 @@ import {
|
|
|
11
11
|
run_remote_function,
|
|
12
12
|
run_remote_generator
|
|
13
13
|
} from './shared.js';
|
|
14
|
-
import { handle_error_and_jsonify } from '../../../server/utils.js';
|
|
15
|
-
import { HttpError, SvelteKitError } from '@sveltejs/kit/internal';
|
|
16
14
|
import { noop } from '../../../../utils/functions.js';
|
|
17
15
|
import { SharedIterator } from '../../../../utils/shared-iterator.js';
|
|
16
|
+
import { handle_error_and_jsonify } from '../../../server/utils.js';
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
|
@@ -78,8 +77,14 @@ export function query(validate_or_fn, maybe_fn) {
|
|
|
78
77
|
bind(payload, validated_arg) {
|
|
79
78
|
const { event, state } = get_request_store();
|
|
80
79
|
|
|
81
|
-
return create_query_resource(__, payload, state, () =>
|
|
82
|
-
run_remote_function(
|
|
80
|
+
return create_query_resource(__, payload, event, state, () =>
|
|
81
|
+
run_remote_function(
|
|
82
|
+
event,
|
|
83
|
+
{ ...state, is_in_remote_query: true },
|
|
84
|
+
false,
|
|
85
|
+
() => validated_arg,
|
|
86
|
+
fn
|
|
87
|
+
)
|
|
83
88
|
);
|
|
84
89
|
}
|
|
85
90
|
};
|
|
@@ -95,8 +100,14 @@ export function query(validate_or_fn, maybe_fn) {
|
|
|
95
100
|
const { event, state } = get_request_store();
|
|
96
101
|
const payload = stringify_remote_arg(arg, state.transport);
|
|
97
102
|
|
|
98
|
-
return create_query_resource(__, payload, state, () =>
|
|
99
|
-
run_remote_function(
|
|
103
|
+
return create_query_resource(__, payload, event, state, () =>
|
|
104
|
+
run_remote_function(
|
|
105
|
+
event,
|
|
106
|
+
{ ...state, is_in_remote_query: true },
|
|
107
|
+
false,
|
|
108
|
+
() => validate(arg),
|
|
109
|
+
fn
|
|
110
|
+
)
|
|
100
111
|
);
|
|
101
112
|
};
|
|
102
113
|
|
|
@@ -152,7 +163,14 @@ function live(validate_or_fn, maybe_fn) {
|
|
|
152
163
|
* @param {any} get_input
|
|
153
164
|
*/
|
|
154
165
|
const run = (event, state, get_input) =>
|
|
155
|
-
run_remote_generator(
|
|
166
|
+
run_remote_generator(
|
|
167
|
+
event,
|
|
168
|
+
{ ...state, is_in_remote_query: true },
|
|
169
|
+
false,
|
|
170
|
+
get_input,
|
|
171
|
+
fn,
|
|
172
|
+
__.name
|
|
173
|
+
);
|
|
156
174
|
|
|
157
175
|
/** @type {RemoteQueryLiveInternals} */
|
|
158
176
|
const __ = {
|
|
@@ -164,7 +182,7 @@ function live(validate_or_fn, maybe_fn) {
|
|
|
164
182
|
bind(payload, validated_arg) {
|
|
165
183
|
const { event, state } = get_request_store();
|
|
166
184
|
|
|
167
|
-
return create_live_query_resource(__, payload,
|
|
185
|
+
return create_live_query_resource(__, payload, event, state, () =>
|
|
168
186
|
run(event, state, () => validated_arg)
|
|
169
187
|
);
|
|
170
188
|
}
|
|
@@ -181,7 +199,7 @@ function live(validate_or_fn, maybe_fn) {
|
|
|
181
199
|
const { event, state } = get_request_store();
|
|
182
200
|
const payload = stringify_remote_arg(arg, state.transport);
|
|
183
201
|
|
|
184
|
-
return create_live_query_resource(__, payload,
|
|
202
|
+
return create_live_query_resource(__, payload, event, state, () =>
|
|
185
203
|
run(event, state, () => validate(arg))
|
|
186
204
|
);
|
|
187
205
|
};
|
|
@@ -273,7 +291,7 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
273
291
|
try {
|
|
274
292
|
return await run_remote_function(
|
|
275
293
|
event,
|
|
276
|
-
state,
|
|
294
|
+
{ ...state, is_in_remote_query: true },
|
|
277
295
|
false,
|
|
278
296
|
async () => Promise.all(entries.map((entry) => entry.get_validated())),
|
|
279
297
|
async (input) => {
|
|
@@ -316,7 +334,7 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
316
334
|
|
|
317
335
|
return run_remote_function(
|
|
318
336
|
event,
|
|
319
|
-
state,
|
|
337
|
+
{ ...state, is_in_remote_query: true },
|
|
320
338
|
false,
|
|
321
339
|
async () => Promise.all(args.map(validate)),
|
|
322
340
|
async (/** @type {any[]} */ input) => {
|
|
@@ -326,15 +344,13 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
326
344
|
input.map(async (arg, i) => {
|
|
327
345
|
try {
|
|
328
346
|
const data = get_result(arg, i);
|
|
329
|
-
return { type: 'result', data
|
|
347
|
+
return { type: 'result', data };
|
|
330
348
|
} catch (error) {
|
|
349
|
+
const transformed = await handle_error_and_jsonify(event, state, options, error);
|
|
350
|
+
|
|
331
351
|
return {
|
|
332
352
|
type: 'error',
|
|
333
|
-
error:
|
|
334
|
-
status:
|
|
335
|
-
error instanceof HttpError || error instanceof SvelteKitError
|
|
336
|
-
? error.status
|
|
337
|
-
: 500
|
|
353
|
+
error: transformed
|
|
338
354
|
};
|
|
339
355
|
}
|
|
340
356
|
})
|
|
@@ -343,9 +359,11 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
343
359
|
);
|
|
344
360
|
},
|
|
345
361
|
bind(payload, validated_arg) {
|
|
346
|
-
const { state } = get_request_store();
|
|
362
|
+
const { event, state } = get_request_store();
|
|
347
363
|
|
|
348
|
-
return create_query_resource(__, payload,
|
|
364
|
+
return create_query_resource(__, payload, event, state, () =>
|
|
365
|
+
enqueue(payload, () => validated_arg)
|
|
366
|
+
);
|
|
349
367
|
}
|
|
350
368
|
};
|
|
351
369
|
|
|
@@ -357,10 +375,10 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
357
375
|
);
|
|
358
376
|
}
|
|
359
377
|
|
|
360
|
-
const { state } = get_request_store();
|
|
378
|
+
const { event, state } = get_request_store();
|
|
361
379
|
const payload = stringify_remote_arg(arg, state.transport);
|
|
362
380
|
|
|
363
|
-
return create_query_resource(__, payload, state, () =>
|
|
381
|
+
return create_query_resource(__, payload, event, state, () =>
|
|
364
382
|
// Collect all the calls to the same query in the same macrotask,
|
|
365
383
|
// then execute them as one backend request.
|
|
366
384
|
enqueue(payload, () => validate(arg))
|
|
@@ -372,14 +390,47 @@ function batch(validate_or_fn, maybe_fn) {
|
|
|
372
390
|
return wrapper;
|
|
373
391
|
}
|
|
374
392
|
|
|
393
|
+
/**
|
|
394
|
+
* Include this value in the returned payload...
|
|
395
|
+
* @param {RequestEvent} event
|
|
396
|
+
* @param {RequestState} state
|
|
397
|
+
* @param {RemoteInternals} internals
|
|
398
|
+
* @param {string} payload
|
|
399
|
+
* @param {() => Promise<any>} fn
|
|
400
|
+
*/
|
|
401
|
+
export function refresh(event, state, internals, payload, fn) {
|
|
402
|
+
if (!internals.id) {
|
|
403
|
+
// unless this is a non-exported (i.e. private) query...
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
if (!event.isRemoteRequest) {
|
|
408
|
+
// or this is a no-JS form submission
|
|
409
|
+
return;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
const key = create_remote_key(internals.id, payload);
|
|
413
|
+
|
|
414
|
+
// `fn` is stored rather than invoked eagerly. The query is run at the end of
|
|
415
|
+
// the command/form (in `collect_remote_data`), so that it observes any state
|
|
416
|
+
// mutations that happen after `refresh()` is called. If the developer re-awaits
|
|
417
|
+
// the query before the command finishes, the cache entry created by that await
|
|
418
|
+
// is reused instead of re-running the query.
|
|
419
|
+
(state.remote.explicit ??= new Map()).set(key, {
|
|
420
|
+
internals,
|
|
421
|
+
fn
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
375
425
|
/**
|
|
376
426
|
* @param {RemoteInternals} __
|
|
377
427
|
* @param {string} payload — the stringified raw argument (i.e. the cache key the client will use)
|
|
428
|
+
* @param {RequestEvent} event
|
|
378
429
|
* @param {RequestState} state
|
|
379
430
|
* @param {() => Promise<any>} fn
|
|
380
431
|
* @returns {RemoteQuery<any>}
|
|
381
432
|
*/
|
|
382
|
-
function create_query_resource(__, payload, state, fn) {
|
|
433
|
+
function create_query_resource(__, payload, event, state, fn) {
|
|
383
434
|
/** @type {Promise<any> | null} */
|
|
384
435
|
let promise = null;
|
|
385
436
|
|
|
@@ -391,7 +442,11 @@ function create_query_resource(__, payload, state, fn) {
|
|
|
391
442
|
// accessing data properties needs to kick off the work
|
|
392
443
|
// so that it gets seeded in the hydration cache
|
|
393
444
|
// and becomes available on the client
|
|
394
|
-
|
|
445
|
+
if (__.id && state.is_in_render) {
|
|
446
|
+
// swallow rejections so they don't crash the server — the error is
|
|
447
|
+
// serialized into the response and surfaced on the client instead
|
|
448
|
+
get_promise().catch(noop);
|
|
449
|
+
}
|
|
395
450
|
};
|
|
396
451
|
|
|
397
452
|
return {
|
|
@@ -420,20 +475,19 @@ function create_query_resource(__, payload, state, fn) {
|
|
|
420
475
|
return false;
|
|
421
476
|
},
|
|
422
477
|
refresh() {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
const refresh_context = get_refresh_context(__, 'refresh', payload);
|
|
430
|
-
const is_immediate_refresh = !refresh_context.cache[refresh_context.payload];
|
|
431
|
-
const value = is_immediate_refresh ? get_promise() : fn();
|
|
432
|
-
return update_refresh_value(refresh_context, value, is_immediate_refresh);
|
|
478
|
+
promise = null;
|
|
479
|
+
delete get_cache(__, state)[payload];
|
|
480
|
+
|
|
481
|
+
refresh(event, state, __, payload, get_promise);
|
|
482
|
+
|
|
483
|
+
return Promise.resolve();
|
|
433
484
|
},
|
|
434
485
|
/** @param {any} value */
|
|
435
486
|
set(value) {
|
|
436
|
-
|
|
487
|
+
const p = (promise = Promise.resolve(value));
|
|
488
|
+
get_cache(__, state)[payload] = p;
|
|
489
|
+
|
|
490
|
+
refresh(event, state, __, payload, () => p);
|
|
437
491
|
},
|
|
438
492
|
/** @type {Promise<any>['then']} */
|
|
439
493
|
then(onfulfilled, onrejected) {
|
|
@@ -451,12 +505,12 @@ function create_query_resource(__, payload, state, fn) {
|
|
|
451
505
|
/**
|
|
452
506
|
* @param {RemoteQueryLiveInternals} __
|
|
453
507
|
* @param {string} payload — the stringified raw argument (i.e. the cache key the client will use)
|
|
508
|
+
* @param {RequestEvent} event
|
|
454
509
|
* @param {RequestState} state
|
|
455
|
-
* @param {AbortSignal} signal — the request signal; aborts in-flight iteration when the client disconnects
|
|
456
510
|
* @param {() => AsyncGenerator<any, void, void>} get_generator
|
|
457
511
|
* @returns {RemoteLiveQuery<any>}
|
|
458
512
|
*/
|
|
459
|
-
function create_live_query_resource(__, payload,
|
|
513
|
+
function create_live_query_resource(__, payload, event, state, get_generator) {
|
|
460
514
|
/** @type {Promise<any> | null} */
|
|
461
515
|
let promise = null;
|
|
462
516
|
|
|
@@ -472,7 +526,11 @@ function create_live_query_resource(__, payload, state, signal, get_generator) {
|
|
|
472
526
|
};
|
|
473
527
|
|
|
474
528
|
const populate_hydratable = () => {
|
|
475
|
-
|
|
529
|
+
if (__.id && state.is_in_render) {
|
|
530
|
+
// swallow rejections so they don't crash the server — the error is
|
|
531
|
+
// serialized into the response and surfaced on the client instead
|
|
532
|
+
get_promise().catch(noop);
|
|
533
|
+
}
|
|
476
534
|
};
|
|
477
535
|
|
|
478
536
|
return {
|
|
@@ -509,15 +567,11 @@ function create_live_query_resource(__, payload, state, signal, get_generator) {
|
|
|
509
567
|
return false;
|
|
510
568
|
},
|
|
511
569
|
reconnect() {
|
|
512
|
-
|
|
570
|
+
promise = null;
|
|
571
|
+
delete get_cache(__, state)[payload];
|
|
513
572
|
|
|
514
|
-
|
|
515
|
-
throw new Error(
|
|
516
|
-
`Cannot call reconnect on query.live '${__.name}' because it is not executed in the context of a command/form remote function`
|
|
517
|
-
);
|
|
518
|
-
}
|
|
573
|
+
refresh(event, state, __, payload, get_promise);
|
|
519
574
|
|
|
520
|
-
reconnects.set(create_remote_key(__.id, payload), get_promise());
|
|
521
575
|
return Promise.resolve();
|
|
522
576
|
},
|
|
523
577
|
/** @ts-expect-error This method no longer exists */
|
|
@@ -535,7 +589,7 @@ function create_live_query_resource(__, payload, state, signal, get_generator) {
|
|
|
535
589
|
const cache = (state.remote.live_iterators ??= new Map());
|
|
536
590
|
let cached = cache.get(key);
|
|
537
591
|
if (!cached) {
|
|
538
|
-
cached = create_shared_live_iterator(signal, get_generator);
|
|
592
|
+
cached = create_shared_live_iterator(event.request.signal, get_generator);
|
|
539
593
|
cache.set(key, cached);
|
|
540
594
|
}
|
|
541
595
|
return cached.subscribe();
|
|
@@ -613,55 +667,3 @@ function create_shared_live_iterator(signal, get_generator) {
|
|
|
613
667
|
// Add batch as a property to the query function
|
|
614
668
|
Object.defineProperty(query, 'batch', { value: batch, enumerable: true });
|
|
615
669
|
Object.defineProperty(query, 'live', { value: live, enumerable: true });
|
|
616
|
-
|
|
617
|
-
/**
|
|
618
|
-
* @param {RemoteInternals} __
|
|
619
|
-
* @param {'set' | 'refresh'} action
|
|
620
|
-
* @param {string} payload — the stringified raw argument (i.e. the cache key the client will use)
|
|
621
|
-
* @returns {{ __: RemoteInternals; state: any; refreshes: Map<string, Promise<any>>; cache: Record<string, { serialize: boolean; data: any }>; refreshes_key: string; payload: string }}
|
|
622
|
-
*/
|
|
623
|
-
function get_refresh_context(__, action, payload) {
|
|
624
|
-
const { state } = get_request_store();
|
|
625
|
-
const { refreshes } = state.remote;
|
|
626
|
-
|
|
627
|
-
if (!refreshes) {
|
|
628
|
-
const name = __.type === 'query_batch' ? `query.batch '${__.name}'` : `query '${__.name}'`;
|
|
629
|
-
throw new Error(
|
|
630
|
-
`Cannot call ${action} on ${name} because it is not executed in the context of a command/form remote function`
|
|
631
|
-
);
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
const cache = get_cache(__, state);
|
|
635
|
-
const refreshes_key = create_remote_key(__.id, payload);
|
|
636
|
-
|
|
637
|
-
return { __, state, refreshes, refreshes_key, cache, payload };
|
|
638
|
-
}
|
|
639
|
-
|
|
640
|
-
/**
|
|
641
|
-
* @param {{ __: RemoteInternals; refreshes: Map<string, Promise<any>>; cache: Record<string, { serialize: boolean; data: any }>; refreshes_key: string; payload: string }} context
|
|
642
|
-
* @param {any} value
|
|
643
|
-
* @param {boolean} [is_immediate_refresh=false]
|
|
644
|
-
* @returns {Promise<void>}
|
|
645
|
-
*/
|
|
646
|
-
function update_refresh_value(
|
|
647
|
-
{ __, refreshes, refreshes_key, cache, payload },
|
|
648
|
-
value,
|
|
649
|
-
is_immediate_refresh = false
|
|
650
|
-
) {
|
|
651
|
-
const promise = Promise.resolve(value);
|
|
652
|
-
|
|
653
|
-
if (!is_immediate_refresh) {
|
|
654
|
-
cache[payload] = { serialize: true, data: promise };
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
if (__.id) {
|
|
658
|
-
refreshes.set(refreshes_key, promise);
|
|
659
|
-
}
|
|
660
|
-
|
|
661
|
-
promise.catch(noop);
|
|
662
|
-
|
|
663
|
-
// we return an immediately-resolving promise so that the `refresh()` signature is consistent,
|
|
664
|
-
// but it doesn't delay anything if awaited inside a command. this way, people aren't
|
|
665
|
-
// penalised if they do `await q1.refresh(); await q2.refresh()`
|
|
666
|
-
return Promise.resolve();
|
|
667
|
-
}
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/** @import { RemoteLiveQuery, RemoteLiveQueryFunction, RemoteQuery, RemoteQueryFunction, RequestedResult, QueryRequestedResult, LiveQueryRequestedResult } from '@sveltejs/kit' */
|
|
2
2
|
/** @import { MaybePromise, RemoteAnyQueryInternals } from 'types' */
|
|
3
|
+
import { HttpError } from '@sveltejs/kit/internal';
|
|
3
4
|
import { get_request_store } from '@sveltejs/kit/internal/server';
|
|
4
|
-
import {
|
|
5
|
+
import { parse_remote_arg } from '../../../shared.js';
|
|
5
6
|
import { noop } from '../../../../utils/functions.js';
|
|
7
|
+
import { get_cache } from './shared.js';
|
|
8
|
+
import { refresh } from './query.js';
|
|
6
9
|
|
|
7
10
|
/**
|
|
8
|
-
*
|
|
9
|
-
* of `{ arg, query }` entries for the
|
|
11
|
+
* Inside a remote `command` or `form` callback, returns an iterable
|
|
12
|
+
* of `{ arg, query }` entries for the query instances the client asked to refresh, up to
|
|
10
13
|
* the supplied `limit`. Each `query` is a `RemoteQuery` bound to the original
|
|
11
14
|
* client-side cache key, so `refresh()` / `set()` propagate correctly even when
|
|
12
15
|
* the query's schema transforms the input. `arg` is the *validated* argument,
|
|
@@ -15,6 +18,8 @@ import { noop } from '../../../../utils/functions.js';
|
|
|
15
18
|
*
|
|
16
19
|
* Arguments that fail validation or exceed `limit` are recorded as failures in
|
|
17
20
|
* the response to the client.
|
|
21
|
+
* See [Client-requested refreshes](https://svelte.dev/docs/kit/remote-functions#Single-flight-mutations-Client-requested-refreshes)
|
|
22
|
+
* for usage in a remote `command` or `form`.
|
|
18
23
|
*
|
|
19
24
|
* @example
|
|
20
25
|
* ```ts
|
|
@@ -51,14 +56,16 @@ import { noop } from '../../../../utils/functions.js';
|
|
|
51
56
|
* @returns {QueryRequestedResult<Validated, Output>}
|
|
52
57
|
*/
|
|
53
58
|
/**
|
|
54
|
-
*
|
|
55
|
-
* of `{ arg, query }` entries for the
|
|
59
|
+
* Inside a remote `command` or `form` callback, returns an iterable
|
|
60
|
+
* of `{ arg, query }` entries for the live query instances the client asked to reconnect, up to
|
|
56
61
|
* the supplied `limit`. Each `query` is a `RemoteLiveQuery` bound to the original
|
|
57
62
|
* client-side cache key, so `reconnect()` propagates correctly even when
|
|
58
63
|
* the query's schema transforms the input. `arg` is the *validated* argument.
|
|
59
64
|
*
|
|
60
65
|
* Arguments that fail validation or exceed `limit` are recorded as failures in
|
|
61
66
|
* the response to the client.
|
|
67
|
+
* See [Client-requested refreshes](https://svelte.dev/docs/kit/remote-functions#Single-flight-mutations-Client-requested-refreshes)
|
|
68
|
+
* for usage in a remote `command` or `form`.
|
|
62
69
|
*
|
|
63
70
|
* @example
|
|
64
71
|
* ```ts
|
|
@@ -95,7 +102,7 @@ import { noop } from '../../../../utils/functions.js';
|
|
|
95
102
|
* @returns {RequestedResult<Validated, Output>}
|
|
96
103
|
*/
|
|
97
104
|
export function requested(query, limit) {
|
|
98
|
-
const { state } = get_request_store();
|
|
105
|
+
const { event, state } = get_request_store();
|
|
99
106
|
const internals = /** @type {RemoteAnyQueryInternals | undefined} */ (
|
|
100
107
|
/** @type {any} */ (query).__
|
|
101
108
|
);
|
|
@@ -115,15 +122,12 @@ export function requested(query, limit) {
|
|
|
115
122
|
|
|
116
123
|
const requested = state.remote.requested;
|
|
117
124
|
const payloads = requested?.get(__.id) ?? [];
|
|
125
|
+
|
|
118
126
|
// note: don't initialize these maps here -- they will be initialized by the
|
|
119
127
|
// command/form wrapper when we enter them, and if we initialize them here
|
|
120
128
|
// we will enable requested(...) in contexts where it shouldn't be allowed,
|
|
121
129
|
// such as load functions or other server functions
|
|
122
|
-
|
|
123
|
-
const reconnects = state.remote.reconnects;
|
|
124
|
-
const store = __.type === 'query_live' ? reconnects : refreshes;
|
|
125
|
-
|
|
126
|
-
if (!store) {
|
|
130
|
+
if (!state.is_in_remote_form_or_command) {
|
|
127
131
|
throw new Error(
|
|
128
132
|
'requested(...) can only be called in the context of a command/form remote function'
|
|
129
133
|
);
|
|
@@ -131,6 +135,9 @@ export function requested(query, limit) {
|
|
|
131
135
|
const [selected, skipped] = split_limit(payloads, limit);
|
|
132
136
|
|
|
133
137
|
/**
|
|
138
|
+
* Registers the failure exactly like `.set()` registers a value: the error record
|
|
139
|
+
* is serialized to the client (putting the query there into a failed state), and
|
|
140
|
+
* subsequent server-side calls of the query with the same argument reject with it.
|
|
134
141
|
* @param {string} payload
|
|
135
142
|
* @param {unknown} error
|
|
136
143
|
*/
|
|
@@ -138,14 +145,15 @@ export function requested(query, limit) {
|
|
|
138
145
|
const promise = Promise.reject(error);
|
|
139
146
|
promise.catch(noop);
|
|
140
147
|
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
get_cache(__, state)[payload] = promise;
|
|
149
|
+
refresh(event, state, __, payload, () => promise);
|
|
143
150
|
};
|
|
144
151
|
|
|
145
152
|
for (const payload of skipped) {
|
|
146
153
|
record_failure(
|
|
147
154
|
payload,
|
|
148
|
-
new
|
|
155
|
+
new HttpError(
|
|
156
|
+
400,
|
|
149
157
|
`Requested refresh was rejected because it exceeded requested(${__.name}, ${limit}) limit`
|
|
150
158
|
)
|
|
151
159
|
);
|
|
@@ -2,10 +2,7 @@
|
|
|
2
2
|
/** @import { ServerHooks, MaybePromise, RequestState, RemoteInternals, RequestStore, RemoteLiveQueryUserFunctionReturnType } from 'types' */
|
|
3
3
|
import { parse } from 'devalue';
|
|
4
4
|
import { error } from '@sveltejs/kit';
|
|
5
|
-
import { hydratable } from 'svelte';
|
|
6
5
|
import { with_request_store, get_request_store } from '@sveltejs/kit/internal/server';
|
|
7
|
-
import { create_remote_key, stringify } from '../../../shared.js';
|
|
8
|
-
import { noop } from '../../../../utils/functions.js';
|
|
9
6
|
|
|
10
7
|
/**
|
|
11
8
|
* @param {any} validate_or_fn
|
|
@@ -38,13 +35,11 @@ export function create_validator(validate_or_fn, maybe_fn) {
|
|
|
38
35
|
|
|
39
36
|
// if the `issues` field exists, the validation failed
|
|
40
37
|
if (result.issues) {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
})
|
|
47
|
-
);
|
|
38
|
+
const body = await state.handleValidationError({
|
|
39
|
+
issues: result.issues,
|
|
40
|
+
event
|
|
41
|
+
});
|
|
42
|
+
error(body.status ?? 400, body);
|
|
48
43
|
}
|
|
49
44
|
|
|
50
45
|
return result.value;
|
|
@@ -76,24 +71,13 @@ export async function get_response(internals, payload, state, get_result) {
|
|
|
76
71
|
await 0;
|
|
77
72
|
|
|
78
73
|
const cache = get_cache(internals, state);
|
|
79
|
-
const entry = (cache[payload] ??= {
|
|
80
|
-
serialize: false,
|
|
81
|
-
data: get_result()
|
|
82
|
-
});
|
|
83
74
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const remote_key = create_remote_key(internals.id, payload);
|
|
88
|
-
|
|
89
|
-
void Promise.resolve(entry.data)
|
|
90
|
-
.then((value) => {
|
|
91
|
-
void hydratable(remote_key, () => stringify(value, state.transport));
|
|
92
|
-
})
|
|
93
|
-
.catch(noop);
|
|
75
|
+
if (!state.is_in_remote_query) {
|
|
76
|
+
// if this is a top-level (not nested) `await myQuery()`, include it in the serialized response
|
|
77
|
+
get_implicit_lookup(internals, state)[payload] = get_result;
|
|
94
78
|
}
|
|
95
79
|
|
|
96
|
-
return
|
|
80
|
+
return (cache[payload] ??= get_result());
|
|
97
81
|
}
|
|
98
82
|
|
|
99
83
|
/**
|
|
@@ -239,10 +223,15 @@ function to_iterator(source, name) {
|
|
|
239
223
|
}
|
|
240
224
|
|
|
241
225
|
/**
|
|
226
|
+
* Note that `state` is deliberately not optional: resources that capture the request
|
|
227
|
+
* state at creation must pass it explicitly, because reading it from the request store
|
|
228
|
+
* at call time is only equivalent on runtimes with `AsyncLocalStorage` support.
|
|
229
|
+
* Callers without a captured state (such as the module-level `form` instance getters)
|
|
230
|
+
* should pass `get_request_store().state` themselves.
|
|
242
231
|
* @param {RemoteInternals} internals
|
|
243
232
|
* @param {RequestState} state
|
|
244
233
|
*/
|
|
245
|
-
export function get_cache(internals, state
|
|
234
|
+
export function get_cache(internals, state) {
|
|
246
235
|
let cache = state.remote.data?.get(internals);
|
|
247
236
|
|
|
248
237
|
if (cache === undefined) {
|
|
@@ -252,3 +241,18 @@ export function get_cache(internals, state = get_request_store().state) {
|
|
|
252
241
|
|
|
253
242
|
return cache;
|
|
254
243
|
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* @param {RemoteInternals} internals
|
|
247
|
+
* @param {RequestState} state
|
|
248
|
+
*/
|
|
249
|
+
export function get_implicit_lookup(internals, state) {
|
|
250
|
+
let cache = state.remote.implicit?.get(internals);
|
|
251
|
+
|
|
252
|
+
if (cache === undefined) {
|
|
253
|
+
cache = {};
|
|
254
|
+
(state.remote.implicit ??= new Map()).set(internals, cache);
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return cache;
|
|
258
|
+
}
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
navigating as _navigating,
|
|
4
4
|
updated as _updated
|
|
5
5
|
} from '../../client/state.svelte.js';
|
|
6
|
-
import { stores } from '../../client/client.js';
|
|
7
6
|
|
|
8
7
|
export const page = {
|
|
9
8
|
get data() {
|
|
@@ -57,5 +56,5 @@ export const updated = {
|
|
|
57
56
|
get current() {
|
|
58
57
|
return _updated.current;
|
|
59
58
|
},
|
|
60
|
-
check:
|
|
59
|
+
check: _updated.check
|
|
61
60
|
};
|