@sveltejs/kit 2.30.0 → 2.31.0
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 +12 -1
- package/src/core/adapt/builder.js +122 -13
- package/src/core/config/options.js +6 -0
- package/src/core/sync/sync.js +3 -2
- package/src/core/sync/write_non_ambient.js +78 -2
- package/src/core/sync/write_types/index.js +0 -72
- package/src/exports/hooks/sequence.js +53 -31
- package/src/{runtime/app/server → exports/internal}/event.js +24 -9
- package/src/exports/internal/server.js +22 -0
- package/src/exports/public.d.ts +117 -6
- package/src/exports/vite/dev/index.js +8 -0
- package/src/exports/vite/index.js +30 -3
- package/src/exports/vite/utils.js +44 -0
- package/src/runtime/app/paths/index.js +3 -1
- package/src/runtime/app/paths/types.d.ts +0 -1
- package/src/runtime/app/server/index.js +1 -1
- package/src/runtime/app/server/remote/command.js +4 -5
- package/src/runtime/app/server/remote/form.js +5 -7
- package/src/runtime/app/server/remote/prerender.js +5 -7
- package/src/runtime/app/server/remote/query.js +6 -8
- package/src/runtime/app/server/remote/shared.js +9 -13
- package/src/runtime/client/client.js +9 -14
- package/src/runtime/server/data/index.js +10 -5
- package/src/runtime/server/endpoint.js +4 -3
- package/src/runtime/server/page/actions.js +55 -24
- package/src/runtime/server/page/index.js +22 -5
- package/src/runtime/server/page/load_data.js +131 -121
- package/src/runtime/server/page/render.js +15 -7
- package/src/runtime/server/page/respond_with_error.js +7 -2
- package/src/runtime/server/remote.js +59 -13
- package/src/runtime/server/respond.js +110 -34
- package/src/runtime/server/utils.js +20 -5
- package/src/runtime/shared.js +20 -0
- package/src/runtime/telemetry/noop.js +81 -0
- package/src/runtime/telemetry/otel.js +21 -0
- package/src/runtime/telemetry/record_span.js +65 -0
- package/src/types/ambient.d.ts +54 -0
- package/src/types/global-private.d.ts +2 -0
- package/src/types/internal.d.ts +28 -0
- package/src/types/synthetic/$env+dynamic+private.md +1 -1
- package/src/version.js +1 -1
- package/types/index.d.ts +171 -4
- package/types/index.d.ts.map +2 -2
- package/src/runtime/server/event-state.js +0 -40
package/src/exports/public.d.ts
CHANGED
|
@@ -17,14 +17,14 @@ import {
|
|
|
17
17
|
RouteSegment
|
|
18
18
|
} from '../types/private.js';
|
|
19
19
|
import { BuildData, SSRNodeLoader, SSRRoute, ValidatedConfig } from 'types';
|
|
20
|
-
import
|
|
21
|
-
import
|
|
20
|
+
import { SvelteConfig } from '@sveltejs/vite-plugin-svelte';
|
|
21
|
+
import { StandardSchemaV1 } from '@standard-schema/spec';
|
|
22
22
|
import {
|
|
23
23
|
RouteId as AppRouteId,
|
|
24
24
|
LayoutParams as AppLayoutParams,
|
|
25
25
|
ResolvedPathname
|
|
26
|
-
// @ts-ignore
|
|
27
26
|
} from '$app/types';
|
|
27
|
+
import { Span } from '@opentelemetry/api';
|
|
28
28
|
|
|
29
29
|
export { PrerenderOption } from '../types/private.js';
|
|
30
30
|
|
|
@@ -50,6 +50,12 @@ export interface Adapter {
|
|
|
50
50
|
* @param details.config The merged route config
|
|
51
51
|
*/
|
|
52
52
|
read?: (details: { config: any; route: { id: string } }) => boolean;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Test support for `instrumentation.server.js`. To pass, the adapter must support running `instrumentation.server.js` prior to the application code.
|
|
56
|
+
* @since 2.31.0
|
|
57
|
+
*/
|
|
58
|
+
instrumentation?: () => boolean;
|
|
53
59
|
};
|
|
54
60
|
/**
|
|
55
61
|
* Creates an `Emulator`, which allows the adapter to influence the environment
|
|
@@ -187,6 +193,47 @@ export interface Builder {
|
|
|
187
193
|
}
|
|
188
194
|
) => string[];
|
|
189
195
|
|
|
196
|
+
/**
|
|
197
|
+
* Check if the server instrumentation file exists.
|
|
198
|
+
* @returns true if the server instrumentation file exists, false otherwise
|
|
199
|
+
* @since 2.31.0
|
|
200
|
+
*/
|
|
201
|
+
hasServerInstrumentationFile: () => boolean;
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Instrument `entrypoint` with `instrumentation`.
|
|
205
|
+
*
|
|
206
|
+
* Renames `entrypoint` to `start` and creates a new module at
|
|
207
|
+
* `entrypoint` which imports `instrumentation` and then dynamically imports `start`. This allows
|
|
208
|
+
* the module hooks necessary for instrumentation libraries to be loaded prior to any application code.
|
|
209
|
+
*
|
|
210
|
+
* Caveats:
|
|
211
|
+
* - "Live exports" will not work. If your adapter uses live exports, your users will need to manually import the server instrumentation on startup.
|
|
212
|
+
* - If `tla` is `false`, OTEL auto-instrumentation may not work properly. Use it if your environment supports it.
|
|
213
|
+
* - Use `hasServerInstrumentationFile` to check if the user has a server instrumentation file; if they don't, you shouldn't do this.
|
|
214
|
+
*
|
|
215
|
+
* @param options an object containing the following properties:
|
|
216
|
+
* @param options.entrypoint the path to the entrypoint to trace.
|
|
217
|
+
* @param options.instrumentation the path to the instrumentation file.
|
|
218
|
+
* @param options.start the name of the start file. This is what `entrypoint` will be renamed to.
|
|
219
|
+
* @param options.module configuration for the resulting entrypoint module.
|
|
220
|
+
* @param options.module.exports
|
|
221
|
+
* @param options.module.generateText a function that receives the relative paths to the instrumentation and start files, and generates the text of the module to be traced. If not provided, the default implementation will be used, which uses top-level await.
|
|
222
|
+
* @since 2.31.0
|
|
223
|
+
*/
|
|
224
|
+
instrument: (args: {
|
|
225
|
+
entrypoint: string;
|
|
226
|
+
instrumentation: string;
|
|
227
|
+
start?: string;
|
|
228
|
+
module?:
|
|
229
|
+
| {
|
|
230
|
+
exports: string[];
|
|
231
|
+
}
|
|
232
|
+
| {
|
|
233
|
+
generateText: (args: { instrumentation: string; start: string }) => string;
|
|
234
|
+
};
|
|
235
|
+
}) => void;
|
|
236
|
+
|
|
190
237
|
/**
|
|
191
238
|
* Compress files in `directory` with gzip and brotli, where appropriate. Generates `.gz` and `.br` files alongside the originals.
|
|
192
239
|
* @param {string} directory The directory containing the files to be compressed
|
|
@@ -408,10 +455,34 @@ export interface KitConfig {
|
|
|
408
455
|
*/
|
|
409
456
|
privatePrefix?: string;
|
|
410
457
|
};
|
|
411
|
-
/**
|
|
412
|
-
* Experimental features which are exempt from semantic versioning. These features may be changed or removed at any time.
|
|
413
|
-
*/
|
|
458
|
+
/** Experimental features. Here be dragons. These are not subject to semantic versioning, so breaking changes or removal can happen in any release. */
|
|
414
459
|
experimental?: {
|
|
460
|
+
/**
|
|
461
|
+
* Options for enabling server-side [OpenTelemetry](https://opentelemetry.io/) tracing for SvelteKit operations including the [`handle` hook](https://svelte.dev/docs/kit/hooks#Server-hooks-handle), [`load` functions](https://svelte.dev/docs/kit/load), [form actions](https://svelte.dev/docs/kit/form-actions), and [remote functions](https://svelte.dev/docs/kit/remote-functions).
|
|
462
|
+
* @default { server: false, serverFile: false }
|
|
463
|
+
* @since 2.31.0
|
|
464
|
+
*/
|
|
465
|
+
tracing?: {
|
|
466
|
+
/**
|
|
467
|
+
* Enables server-side [OpenTelemetry](https://opentelemetry.io/) span emission for SvelteKit operations including the [`handle` hook](https://svelte.dev/docs/kit/hooks#Server-hooks-handle), [`load` functions](https://svelte.dev/docs/kit/load), [form actions](https://svelte.dev/docs/kit/form-actions), and [remote functions](https://svelte.dev/docs/kit/remote-functions).
|
|
468
|
+
* @default false
|
|
469
|
+
* @since 2.31.0
|
|
470
|
+
*/
|
|
471
|
+
server?: boolean;
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* @since 2.31.0
|
|
476
|
+
*/
|
|
477
|
+
instrumentation?: {
|
|
478
|
+
/**
|
|
479
|
+
* Enables `instrumentation.server.js` for tracing and observability instrumentation.
|
|
480
|
+
* @default false
|
|
481
|
+
* @since 2.31.0
|
|
482
|
+
*/
|
|
483
|
+
server?: boolean;
|
|
484
|
+
};
|
|
485
|
+
|
|
415
486
|
/**
|
|
416
487
|
* Whether to enable the experimental remote functions feature. This feature is not yet stable and may be changed or removed at any time.
|
|
417
488
|
* @default false
|
|
@@ -1027,6 +1098,19 @@ export interface LoadEvent<
|
|
|
1027
1098
|
* ```
|
|
1028
1099
|
*/
|
|
1029
1100
|
untrack: <T>(fn: () => T) => T;
|
|
1101
|
+
|
|
1102
|
+
/**
|
|
1103
|
+
* Access to spans for tracing. If tracing is not enabled or the function is being run in the browser, these spans will do nothing.
|
|
1104
|
+
* @since 2.31.0
|
|
1105
|
+
*/
|
|
1106
|
+
tracing: {
|
|
1107
|
+
/** Whether tracing is enabled. */
|
|
1108
|
+
enabled: boolean;
|
|
1109
|
+
/** The root span for the request. This span is named `sveltekit.handle.root`. */
|
|
1110
|
+
root: Span;
|
|
1111
|
+
/** The span associated with the current `load` function. */
|
|
1112
|
+
current: Span;
|
|
1113
|
+
};
|
|
1030
1114
|
}
|
|
1031
1115
|
|
|
1032
1116
|
export interface NavigationEvent<
|
|
@@ -1305,6 +1389,20 @@ export interface RequestEvent<
|
|
|
1305
1389
|
* `true` for `+server.js` calls coming from SvelteKit without the overhead of actually making an HTTP request. This happens when you make same-origin `fetch` requests on the server.
|
|
1306
1390
|
*/
|
|
1307
1391
|
isSubRequest: boolean;
|
|
1392
|
+
|
|
1393
|
+
/**
|
|
1394
|
+
* Access to spans for tracing. If tracing is not enabled, these spans will do nothing.
|
|
1395
|
+
* @since 2.31.0
|
|
1396
|
+
*/
|
|
1397
|
+
tracing: {
|
|
1398
|
+
/** Whether tracing is enabled. */
|
|
1399
|
+
enabled: boolean;
|
|
1400
|
+
/** The root span for the request. This span is named `sveltekit.handle.root`. */
|
|
1401
|
+
root: Span;
|
|
1402
|
+
/** The span associated with the current `handle` hook, `load` function, or form action. */
|
|
1403
|
+
current: Span;
|
|
1404
|
+
};
|
|
1405
|
+
|
|
1308
1406
|
/**
|
|
1309
1407
|
* `true` if the request comes from the client via a remote function. The `url` property will be stripped of the internal information
|
|
1310
1408
|
* related to the data request in this case. Use this property instead if the distinction is important to you.
|
|
@@ -1468,6 +1566,19 @@ export interface ServerLoadEvent<
|
|
|
1468
1566
|
* ```
|
|
1469
1567
|
*/
|
|
1470
1568
|
untrack: <T>(fn: () => T) => T;
|
|
1569
|
+
|
|
1570
|
+
/**
|
|
1571
|
+
* Access to spans for tracing. If tracing is not enabled, these spans will do nothing.
|
|
1572
|
+
* @since 2.31.0
|
|
1573
|
+
*/
|
|
1574
|
+
tracing: {
|
|
1575
|
+
/** Whether tracing is enabled. */
|
|
1576
|
+
enabled: boolean;
|
|
1577
|
+
/** The root span for the request. This span is named `sveltekit.handle.root`. */
|
|
1578
|
+
root: Span;
|
|
1579
|
+
/** The span associated with the current server `load` function. */
|
|
1580
|
+
current: Span;
|
|
1581
|
+
};
|
|
1471
1582
|
}
|
|
1472
1583
|
|
|
1473
1584
|
/**
|
|
@@ -501,6 +501,14 @@ export async function dev(vite, vite_config, svelte_config) {
|
|
|
501
501
|
return;
|
|
502
502
|
}
|
|
503
503
|
|
|
504
|
+
const resolved_instrumentation = resolve_entry(
|
|
505
|
+
path.join(svelte_config.kit.files.src, 'instrumentation.server')
|
|
506
|
+
);
|
|
507
|
+
|
|
508
|
+
if (resolved_instrumentation) {
|
|
509
|
+
await vite.ssrLoadModule(resolved_instrumentation);
|
|
510
|
+
}
|
|
511
|
+
|
|
504
512
|
// we have to import `Server` before calling `set_assets`
|
|
505
513
|
const { Server } = /** @type {import('types').ServerModule} */ (
|
|
506
514
|
await vite.ssrLoadModule(`${runtime_base}/server/index.js`, { fixStacktrace: true })
|
|
@@ -16,7 +16,13 @@ import { build_service_worker } from './build/build_service_worker.js';
|
|
|
16
16
|
import { assets_base, find_deps, resolve_symlinks } from './build/utils.js';
|
|
17
17
|
import { dev } from './dev/index.js';
|
|
18
18
|
import { preview } from './preview/index.js';
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
error_for_missing_config,
|
|
21
|
+
get_config_aliases,
|
|
22
|
+
get_env,
|
|
23
|
+
normalize_id,
|
|
24
|
+
stackless
|
|
25
|
+
} from './utils.js';
|
|
20
26
|
import { write_client_manifest } from '../../core/sync/write_client_manifest.js';
|
|
21
27
|
import prerender from '../../core/postbuild/prerender.js';
|
|
22
28
|
import analyse from '../../core/postbuild/analyse.js';
|
|
@@ -332,7 +338,8 @@ async function kit({ svelte_config }) {
|
|
|
332
338
|
__SVELTEKIT_DEV__: 'false',
|
|
333
339
|
__SVELTEKIT_EMBEDDED__: s(kit.embedded),
|
|
334
340
|
__SVELTEKIT_EXPERIMENTAL__REMOTE_FUNCTIONS__: s(kit.experimental.remoteFunctions),
|
|
335
|
-
__SVELTEKIT_CLIENT_ROUTING__: kit.router.resolution === 'client'
|
|
341
|
+
__SVELTEKIT_CLIENT_ROUTING__: s(kit.router.resolution === 'client'),
|
|
342
|
+
__SVELTEKIT_SERVER_TRACING_ENABLED__: s(kit.experimental.instrumentation.server),
|
|
336
343
|
__SVELTEKIT_PAYLOAD__: new_config.build.ssr
|
|
337
344
|
? '{}'
|
|
338
345
|
: `globalThis.__sveltekit_${version_hash}`
|
|
@@ -347,7 +354,8 @@ async function kit({ svelte_config }) {
|
|
|
347
354
|
__SVELTEKIT_DEV__: 'true',
|
|
348
355
|
__SVELTEKIT_EMBEDDED__: s(kit.embedded),
|
|
349
356
|
__SVELTEKIT_EXPERIMENTAL__REMOTE_FUNCTIONS__: s(kit.experimental.remoteFunctions),
|
|
350
|
-
__SVELTEKIT_CLIENT_ROUTING__: kit.router.resolution === 'client'
|
|
357
|
+
__SVELTEKIT_CLIENT_ROUTING__: s(kit.router.resolution === 'client'),
|
|
358
|
+
__SVELTEKIT_SERVER_TRACING_ENABLED__: s(kit.experimental.instrumentation.server),
|
|
351
359
|
__SVELTEKIT_PAYLOAD__: 'globalThis.__sveltekit_dev'
|
|
352
360
|
};
|
|
353
361
|
|
|
@@ -775,6 +783,25 @@ async function kit({ svelte_config }) {
|
|
|
775
783
|
input[name] = path.resolve(file);
|
|
776
784
|
});
|
|
777
785
|
|
|
786
|
+
// ...and the server instrumentation file
|
|
787
|
+
const server_instrumentation = resolve_entry(
|
|
788
|
+
path.join(kit.files.src, 'instrumentation.server')
|
|
789
|
+
);
|
|
790
|
+
if (server_instrumentation) {
|
|
791
|
+
const { adapter } = kit;
|
|
792
|
+
if (adapter && !adapter.supports?.instrumentation?.()) {
|
|
793
|
+
throw new Error(`${server_instrumentation} is unsupported in ${adapter.name}.`);
|
|
794
|
+
}
|
|
795
|
+
if (!kit.experimental.instrumentation.server) {
|
|
796
|
+
error_for_missing_config(
|
|
797
|
+
'instrumentation.server.js',
|
|
798
|
+
'kit.experimental.instrumentation.server',
|
|
799
|
+
'true'
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
input['instrumentation.server'] = server_instrumentation;
|
|
803
|
+
}
|
|
804
|
+
|
|
778
805
|
// ...and every .remote file
|
|
779
806
|
for (const remote of manifest_data.remotes) {
|
|
780
807
|
input[`remote/${remote.hash}`] = path.resolve(remote.file);
|
|
@@ -4,6 +4,7 @@ import { posixify } from '../../utils/filesystem.js';
|
|
|
4
4
|
import { negotiate } from '../../utils/http.js';
|
|
5
5
|
import { filter_private_env, filter_public_env } from '../../utils/env.js';
|
|
6
6
|
import { escape_html } from '../../utils/escape.js';
|
|
7
|
+
import { dedent } from '../../core/sync/utils.js';
|
|
7
8
|
import {
|
|
8
9
|
app_server,
|
|
9
10
|
env_dynamic_private,
|
|
@@ -172,3 +173,46 @@ export function stackless(message) {
|
|
|
172
173
|
}
|
|
173
174
|
|
|
174
175
|
export const strip_virtual_prefix = /** @param {string} id */ (id) => id.replace('\0virtual:', '');
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* For `error_for_missing_config('instrumentation.server.js', 'kit.experimental.instrumentation.server', true)`,
|
|
179
|
+
* returns:
|
|
180
|
+
*
|
|
181
|
+
* ```
|
|
182
|
+
* To enable `instrumentation.server.js`, add the following to your `svelte.config.js`:
|
|
183
|
+
*
|
|
184
|
+
*\`\`\`js
|
|
185
|
+
* kit:
|
|
186
|
+
* experimental:
|
|
187
|
+
* instrumentation:
|
|
188
|
+
* server: true
|
|
189
|
+
* }
|
|
190
|
+
* }
|
|
191
|
+
* }
|
|
192
|
+
*\`\`\`
|
|
193
|
+
*```
|
|
194
|
+
* @param {string} feature_name
|
|
195
|
+
* @param {string} path
|
|
196
|
+
* @param {string} value
|
|
197
|
+
* @returns {never}
|
|
198
|
+
*/
|
|
199
|
+
export function error_for_missing_config(feature_name, path, value) {
|
|
200
|
+
const hole = '__HOLE__';
|
|
201
|
+
|
|
202
|
+
const result = path.split('.').reduce((acc, part, i, parts) => {
|
|
203
|
+
const indent = ' '.repeat(i);
|
|
204
|
+
const rhs = i === parts.length - 1 ? value : `{\n${hole}\n${indent}}`;
|
|
205
|
+
|
|
206
|
+
return acc.replace(hole, `${indent}${part}: ${rhs}`);
|
|
207
|
+
}, hole);
|
|
208
|
+
|
|
209
|
+
throw new Error(
|
|
210
|
+
dedent`\
|
|
211
|
+
To enable \`${feature_name}\`, add the following to your \`svelte.config.js\`:
|
|
212
|
+
|
|
213
|
+
\`\`\`js
|
|
214
|
+
${result}
|
|
215
|
+
\`\`\`
|
|
216
|
+
`
|
|
217
|
+
);
|
|
218
|
+
}
|
|
@@ -8,7 +8,9 @@ export function asset(file) {
|
|
|
8
8
|
|
|
9
9
|
/** @type {import('./types.d.ts').resolve} */
|
|
10
10
|
export function resolve(id, params) {
|
|
11
|
-
|
|
11
|
+
// The type error is correct here, and if someone doesn't pass params when they should there's a runtime error,
|
|
12
|
+
// but we don't want to adjust the internal resolve_route function to accept `undefined`, hence the type cast.
|
|
13
|
+
return base + resolve_route(id, /** @type {Record<string, string>} */ (params));
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export { base, assets, resolve as resolveRoute };
|
|
@@ -73,6 +73,6 @@ export function read(asset) {
|
|
|
73
73
|
throw new Error(`Asset does not exist: ${file}`);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
export { getRequestEvent } from '
|
|
76
|
+
export { getRequestEvent } from '@sveltejs/kit/internal/server';
|
|
77
77
|
|
|
78
78
|
export { query, prerender, command, form } from './remote/index.js';
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/** @import { RemoteCommand } from '@sveltejs/kit' */
|
|
2
2
|
/** @import { RemoteInfo, MaybePromise } from 'types' */
|
|
3
3
|
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
|
4
|
-
import {
|
|
4
|
+
import { get_request_store } from '@sveltejs/kit/internal/server';
|
|
5
5
|
import { check_experimental, create_validator, run_remote_function } from './shared.js';
|
|
6
|
-
import { get_event_state } from '../../../server/event-state.js';
|
|
7
6
|
|
|
8
7
|
/**
|
|
9
8
|
* Creates a remote command. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
|
@@ -65,7 +64,7 @@ export function command(validate_or_fn, maybe_fn) {
|
|
|
65
64
|
|
|
66
65
|
/** @type {RemoteCommand<Input, Output> & { __: RemoteInfo }} */
|
|
67
66
|
const wrapper = (arg) => {
|
|
68
|
-
const event =
|
|
67
|
+
const { event, state } = get_request_store();
|
|
69
68
|
|
|
70
69
|
if (!event.isRemoteRequest) {
|
|
71
70
|
throw new Error(
|
|
@@ -73,9 +72,9 @@ export function command(validate_or_fn, maybe_fn) {
|
|
|
73
72
|
);
|
|
74
73
|
}
|
|
75
74
|
|
|
76
|
-
|
|
75
|
+
state.refreshes ??= {};
|
|
77
76
|
|
|
78
|
-
const promise = Promise.resolve(run_remote_function(event, true, arg, validate, fn));
|
|
77
|
+
const promise = Promise.resolve(run_remote_function(event, state, true, arg, validate, fn));
|
|
79
78
|
|
|
80
79
|
// @ts-expect-error
|
|
81
80
|
promise.updates = () => {
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/** @import { RemoteForm } from '@sveltejs/kit' */
|
|
2
2
|
/** @import { RemoteInfo, MaybePromise } from 'types' */
|
|
3
|
-
import {
|
|
3
|
+
import { get_request_store } from '@sveltejs/kit/internal/server';
|
|
4
4
|
import { check_experimental, run_remote_function } from './shared.js';
|
|
5
|
-
import { get_event_state } from '../../../server/event-state.js';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Creates a form object that can be spread onto a `<form>` element.
|
|
@@ -57,12 +56,11 @@ export function form(fn) {
|
|
|
57
56
|
id: '',
|
|
58
57
|
/** @param {FormData} form_data */
|
|
59
58
|
fn: async (form_data) => {
|
|
60
|
-
const event =
|
|
61
|
-
const state = get_event_state(event);
|
|
59
|
+
const { event, state } = get_request_store();
|
|
62
60
|
|
|
63
61
|
state.refreshes ??= {};
|
|
64
62
|
|
|
65
|
-
const result = await run_remote_function(event, true, form_data, (d) => d, fn);
|
|
63
|
+
const result = await run_remote_function(event, state, true, form_data, (d) => d, fn);
|
|
66
64
|
|
|
67
65
|
// We don't need to care about args or deduplicating calls, because uneval results are only relevant in full page reloads
|
|
68
66
|
// where only one form submission is active at the same time
|
|
@@ -89,7 +87,7 @@ export function form(fn) {
|
|
|
89
87
|
Object.defineProperty(instance, 'result', {
|
|
90
88
|
get() {
|
|
91
89
|
try {
|
|
92
|
-
const { remote_data } =
|
|
90
|
+
const { remote_data } = get_request_store().state;
|
|
93
91
|
return remote_data?.[__.id];
|
|
94
92
|
} catch {
|
|
95
93
|
return undefined;
|
|
@@ -111,7 +109,7 @@ export function form(fn) {
|
|
|
111
109
|
Object.defineProperty(instance, 'for', {
|
|
112
110
|
/** @type {RemoteForm<any>['for']} */
|
|
113
111
|
value: (key) => {
|
|
114
|
-
const state =
|
|
112
|
+
const { state } = get_request_store();
|
|
115
113
|
let instance = (state.form_instances ??= new Map()).get(key);
|
|
116
114
|
|
|
117
115
|
if (!instance) {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
|
4
4
|
import { error, json } from '@sveltejs/kit';
|
|
5
5
|
import { DEV } from 'esm-env';
|
|
6
|
-
import {
|
|
6
|
+
import { get_request_store } from '@sveltejs/kit/internal/server';
|
|
7
7
|
import { create_remote_cache_key, stringify, stringify_remote_arg } from '../../../shared.js';
|
|
8
8
|
import { app_dir, base } from '__sveltekit/paths';
|
|
9
9
|
import {
|
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
parse_remote_response,
|
|
14
14
|
run_remote_function
|
|
15
15
|
} from './shared.js';
|
|
16
|
-
import { get_event_state } from '../../../server/event-state.js';
|
|
17
16
|
|
|
18
17
|
/**
|
|
19
18
|
* Creates a remote prerender function. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
|
@@ -93,15 +92,14 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
93
92
|
const wrapper = (arg) => {
|
|
94
93
|
/** @type {Promise<Output> & Partial<RemoteResource<Output>>} */
|
|
95
94
|
const promise = (async () => {
|
|
96
|
-
const event =
|
|
97
|
-
const state = get_event_state(event);
|
|
95
|
+
const { event, state } = get_request_store();
|
|
98
96
|
const payload = stringify_remote_arg(arg, state.transport);
|
|
99
97
|
const id = __.id;
|
|
100
98
|
const url = `${base}/${app_dir}/remote/${id}${payload ? `/${payload}` : ''}`;
|
|
101
99
|
|
|
102
100
|
if (!state.prerendering && !DEV && !event.isRemoteRequest) {
|
|
103
101
|
try {
|
|
104
|
-
return await get_response(id, arg,
|
|
102
|
+
return await get_response(id, arg, state, async () => {
|
|
105
103
|
// TODO adapters can provide prerendered data more efficiently than
|
|
106
104
|
// fetching from the public internet
|
|
107
105
|
const response = await fetch(new URL(url, event.url.origin).href);
|
|
@@ -130,8 +128,8 @@ export function prerender(validate_or_fn, fn_or_options, maybe_options) {
|
|
|
130
128
|
return /** @type {Promise<any>} */ (state.prerendering.remote_responses.get(url));
|
|
131
129
|
}
|
|
132
130
|
|
|
133
|
-
const promise = get_response(id, arg,
|
|
134
|
-
run_remote_function(event, false, arg, validate, fn)
|
|
131
|
+
const promise = get_response(id, arg, state, () =>
|
|
132
|
+
run_remote_function(event, state, false, arg, validate, fn)
|
|
135
133
|
);
|
|
136
134
|
|
|
137
135
|
if (state.prerendering) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** @import { RemoteQuery, RemoteQueryFunction } from '@sveltejs/kit' */
|
|
2
2
|
/** @import { RemoteInfo, MaybePromise } from 'types' */
|
|
3
3
|
/** @import { StandardSchemaV1 } from '@standard-schema/spec' */
|
|
4
|
-
import {
|
|
4
|
+
import { get_request_store } from '@sveltejs/kit/internal/server';
|
|
5
5
|
import { create_remote_cache_key, stringify_remote_arg } from '../../../shared.js';
|
|
6
6
|
import { prerendering } from '__sveltekit/environment';
|
|
7
7
|
import {
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
get_response,
|
|
11
11
|
run_remote_function
|
|
12
12
|
} from './shared.js';
|
|
13
|
-
import { get_event_state } from '../../../server/event-state.js';
|
|
14
13
|
|
|
15
14
|
/**
|
|
16
15
|
* Creates a remote query. When called from the browser, the function will be invoked on the server via a `fetch` call.
|
|
@@ -78,19 +77,18 @@ export function query(validate_or_fn, maybe_fn) {
|
|
|
78
77
|
);
|
|
79
78
|
}
|
|
80
79
|
|
|
81
|
-
const event =
|
|
80
|
+
const { event, state } = get_request_store();
|
|
82
81
|
|
|
83
82
|
/** @type {Promise<any> & Partial<RemoteQuery<any>>} */
|
|
84
|
-
const promise = get_response(__.id, arg,
|
|
85
|
-
run_remote_function(event, false, arg, validate, fn)
|
|
83
|
+
const promise = get_response(__.id, arg, state, () =>
|
|
84
|
+
run_remote_function(event, state, false, arg, validate, fn)
|
|
86
85
|
);
|
|
87
86
|
|
|
88
87
|
promise.catch(() => {});
|
|
89
88
|
|
|
90
89
|
promise.refresh = async () => {
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
const refreshes = state?.refreshes;
|
|
90
|
+
const { state } = get_request_store();
|
|
91
|
+
const refreshes = state.refreshes;
|
|
94
92
|
|
|
95
93
|
if (!refreshes) {
|
|
96
94
|
throw new Error(
|
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/** @import { RequestEvent } from '@sveltejs/kit' */
|
|
2
|
-
/** @import { ServerHooks, MaybePromise } from 'types' */
|
|
2
|
+
/** @import { ServerHooks, MaybePromise, RequestState } from 'types' */
|
|
3
3
|
import { parse } from 'devalue';
|
|
4
4
|
import { error } from '@sveltejs/kit';
|
|
5
|
-
import {
|
|
5
|
+
import { with_request_store, get_request_store } from '@sveltejs/kit/internal/server';
|
|
6
6
|
import { create_remote_cache_key, stringify_remote_arg } from '../../../shared.js';
|
|
7
|
-
import { EVENT_STATE, get_event_state } from '../../../server/event-state.js';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
* @param {any} validate_or_fn
|
|
@@ -30,8 +29,7 @@ export function create_validator(validate_or_fn, maybe_fn) {
|
|
|
30
29
|
if ('~standard' in validate_or_fn) {
|
|
31
30
|
return async (arg) => {
|
|
32
31
|
// Get event before async validation to ensure it's available in server environments without AsyncLocalStorage, too
|
|
33
|
-
const event =
|
|
34
|
-
const state = get_event_state(event);
|
|
32
|
+
const { event, state } = get_request_store();
|
|
35
33
|
const validate = validate_or_fn['~standard'].validate;
|
|
36
34
|
|
|
37
35
|
const result = await validate(arg);
|
|
@@ -66,12 +64,11 @@ export function create_validator(validate_or_fn, maybe_fn) {
|
|
|
66
64
|
* @template {MaybePromise<any>} T
|
|
67
65
|
* @param {string} id
|
|
68
66
|
* @param {any} arg
|
|
69
|
-
* @param {
|
|
67
|
+
* @param {RequestState} state
|
|
70
68
|
* @param {() => Promise<T>} get_result
|
|
71
69
|
* @returns {Promise<T>}
|
|
72
70
|
*/
|
|
73
|
-
export function get_response(id, arg,
|
|
74
|
-
const state = get_event_state(event);
|
|
71
|
+
export function get_response(id, arg, state, get_result) {
|
|
75
72
|
const cache_key = create_remote_cache_key(id, stringify_remote_arg(arg, state.transport));
|
|
76
73
|
|
|
77
74
|
return ((state.remote_data ??= {})[cache_key] ??= get_result());
|
|
@@ -104,17 +101,16 @@ export function parse_remote_response(data, transport) {
|
|
|
104
101
|
* Like `with_event` but removes things from `event` you cannot see/call in remote functions, such as `setHeaders`.
|
|
105
102
|
* @template T
|
|
106
103
|
* @param {RequestEvent} event
|
|
104
|
+
* @param {RequestState} state
|
|
107
105
|
* @param {boolean} allow_cookies
|
|
108
106
|
* @param {any} arg
|
|
109
107
|
* @param {(arg: any) => any} validate
|
|
110
108
|
* @param {(arg?: any) => T} fn
|
|
111
109
|
*/
|
|
112
|
-
export async function run_remote_function(event, allow_cookies, arg, validate, fn) {
|
|
110
|
+
export async function run_remote_function(event, state, allow_cookies, arg, validate, fn) {
|
|
113
111
|
/** @type {RequestEvent} */
|
|
114
112
|
const cleansed = {
|
|
115
113
|
...event,
|
|
116
|
-
// @ts-expect-error this isn't part of the public `RequestEvent` type
|
|
117
|
-
[EVENT_STATE]: event[EVENT_STATE],
|
|
118
114
|
setHeaders: () => {
|
|
119
115
|
throw new Error('setHeaders is not allowed in remote functions');
|
|
120
116
|
},
|
|
@@ -148,6 +144,6 @@ export async function run_remote_function(event, allow_cookies, arg, validate, f
|
|
|
148
144
|
};
|
|
149
145
|
|
|
150
146
|
// In two parts, each with_event, so that runtimes without async local storage can still get the event at the start of the function
|
|
151
|
-
const validated = await
|
|
152
|
-
return
|
|
147
|
+
const validated = await with_request_store({ event: cleansed, state }, () => validate(arg));
|
|
148
|
+
return with_request_store({ event: cleansed, state }, () => fn(validated));
|
|
153
149
|
}
|
|
@@ -39,11 +39,17 @@ import {
|
|
|
39
39
|
} from './constants.js';
|
|
40
40
|
import { validate_page_exports } from '../../utils/exports.js';
|
|
41
41
|
import { compact } from '../../utils/array.js';
|
|
42
|
-
import {
|
|
42
|
+
import {
|
|
43
|
+
INVALIDATED_PARAM,
|
|
44
|
+
TRAILING_SLASH_PARAM,
|
|
45
|
+
validate_depends,
|
|
46
|
+
validate_load_response
|
|
47
|
+
} from '../shared.js';
|
|
43
48
|
import { get_message, get_status } from '../../utils/error.js';
|
|
44
49
|
import { writable } from 'svelte/store';
|
|
45
50
|
import { page, update, navigating } from './state.svelte.js';
|
|
46
51
|
import { add_data_suffix, add_resolution_suffix } from '../pathname.js';
|
|
52
|
+
import { noop_span } from '../telemetry/noop.js';
|
|
47
53
|
import { text_decoder } from '../utils.js';
|
|
48
54
|
|
|
49
55
|
export { load_css };
|
|
@@ -715,6 +721,7 @@ async function load_node({ loader, parent, url, params, route, server_data_node
|
|
|
715
721
|
|
|
716
722
|
/** @type {import('@sveltejs/kit').LoadEvent} */
|
|
717
723
|
const load_input = {
|
|
724
|
+
tracing: { enabled: false, root: noop_span, current: noop_span },
|
|
718
725
|
route: new Proxy(route, {
|
|
719
726
|
get: (target, key) => {
|
|
720
727
|
if (is_tracking) {
|
|
@@ -807,19 +814,7 @@ async function load_node({ loader, parent, url, params, route, server_data_node
|
|
|
807
814
|
try {
|
|
808
815
|
lock_fetch();
|
|
809
816
|
data = (await node.universal.load.call(null, load_input)) ?? null;
|
|
810
|
-
|
|
811
|
-
throw new Error(
|
|
812
|
-
`a load function related to route '${route.id}' returned ${
|
|
813
|
-
typeof data !== 'object'
|
|
814
|
-
? `a ${typeof data}`
|
|
815
|
-
: data instanceof Response
|
|
816
|
-
? 'a Response object'
|
|
817
|
-
: Array.isArray(data)
|
|
818
|
-
? 'an array'
|
|
819
|
-
: 'a non-plain object'
|
|
820
|
-
}, but must return a plain object at the top level (i.e. \`return {...}\`)`
|
|
821
|
-
);
|
|
822
|
-
}
|
|
817
|
+
validate_load_response(data, `related to route '${route.id}'`);
|
|
823
818
|
} finally {
|
|
824
819
|
unlock_fetch();
|
|
825
820
|
}
|