@sveltejs/kit 3.0.0-next.2 → 3.0.0-next.3
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 +2 -2
- package/src/core/postbuild/analyse.js +0 -8
- package/src/core/postbuild/prerender.js +2 -0
- package/src/exports/public.d.ts +1 -1
- package/src/exports/vite/build/build_server.js +43 -58
- package/src/exports/vite/build/remote.js +18 -11
- package/src/exports/vite/build/utils.js +0 -8
- package/src/exports/vite/index.js +220 -216
- package/src/runtime/app/server/remote/command.js +0 -3
- package/src/runtime/app/server/remote/form.js +18 -13
- package/src/runtime/app/server/remote/prerender.js +28 -34
- package/src/runtime/app/server/remote/query.js +105 -94
- package/src/runtime/app/server/remote/requested.js +14 -10
- package/src/runtime/app/server/remote/shared.js +25 -19
- package/src/runtime/client/client.js +19 -13
- package/src/runtime/client/remote-functions/command.svelte.js +5 -30
- package/src/runtime/client/remote-functions/form.svelte.js +62 -82
- package/src/runtime/client/remote-functions/prerender.svelte.js +14 -6
- package/src/runtime/client/remote-functions/query/index.js +6 -14
- package/src/runtime/client/remote-functions/query/instance.svelte.js +20 -0
- package/src/runtime/client/remote-functions/query/proxy.js +3 -3
- package/src/runtime/client/remote-functions/query-batch.svelte.js +59 -68
- package/src/runtime/client/remote-functions/query-live/instance.svelte.js +21 -6
- package/src/runtime/client/remote-functions/shared.svelte.js +76 -59
- package/src/runtime/server/page/render.js +20 -80
- package/src/runtime/server/page/server_routing.js +20 -15
- package/src/runtime/server/remote.js +296 -204
- package/src/runtime/server/respond.js +4 -2
- package/src/types/global-private.d.ts +3 -3
- package/src/types/internal.d.ts +53 -34
- package/src/version.js +1 -1
- package/types/index.d.ts +6 -5
- package/types/index.d.ts.map +1 -1
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
/** @import { RemoteQueryFunction } from '@sveltejs/kit' */
|
|
2
|
-
/** @import { RemoteFunctionResponse } from 'types' */
|
|
3
2
|
import { app_dir, base } from '$app/paths/internal/client';
|
|
4
|
-
import {
|
|
5
|
-
import { get_remote_request_headers, QUERY_FUNCTION_ID } from './shared.svelte.js';
|
|
3
|
+
import { goto } from '../client.js';
|
|
4
|
+
import { get_remote_request_headers, QUERY_FUNCTION_ID, remote_request } from './shared.svelte.js';
|
|
6
5
|
import { QueryProxy } from './query/proxy.js';
|
|
7
|
-
import
|
|
8
|
-
import { HttpError, Redirect } from '@sveltejs/kit/internal';
|
|
9
|
-
import { hydratable } from 'svelte';
|
|
6
|
+
import { HttpError } from '@sveltejs/kit/internal';
|
|
10
7
|
|
|
11
8
|
/**
|
|
12
9
|
* @param {string} id
|
|
@@ -18,84 +15,78 @@ export function query_batch(id) {
|
|
|
18
15
|
|
|
19
16
|
/** @type {RemoteQueryFunction<any, any>} */
|
|
20
17
|
const wrapper = (arg) => {
|
|
21
|
-
return new QueryProxy(id, arg, async (
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
batching.set(payload, entry);
|
|
18
|
+
return new QueryProxy(id, arg, async (payload) => {
|
|
19
|
+
return await new Promise((resolve, reject) => {
|
|
20
|
+
// create_remote_function caches identical calls, but in case a refresh to the same query is called multiple times this function
|
|
21
|
+
// is invoked multiple times with the same payload, so we need to deduplicate here
|
|
22
|
+
const entry = batching.get(payload) ?? [];
|
|
23
|
+
entry.push({ resolve, reject });
|
|
24
|
+
batching.set(payload, entry);
|
|
29
25
|
|
|
30
|
-
|
|
26
|
+
if (batching.size > 1) return;
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
// Do this here, after await Svelte' reactivity context is gone.
|
|
29
|
+
// TODO is it possible to have batches of the same key
|
|
30
|
+
// but in different forks/async contexts and in the same macrotask?
|
|
31
|
+
// If so this would potentially be buggy
|
|
32
|
+
const headers = {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
...get_remote_request_headers()
|
|
35
|
+
};
|
|
40
36
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
37
|
+
// Wait for the next macrotask - don't use microtask as Svelte runtime uses these to collect changes and flush them,
|
|
38
|
+
// and flushes could reveal more queries that should be batched.
|
|
39
|
+
setTimeout(async () => {
|
|
40
|
+
const batched = batching;
|
|
41
|
+
batching = new Map();
|
|
46
42
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
43
|
+
try {
|
|
44
|
+
const response = await remote_request(`${base}/${app_dir}/remote/${id}`, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
body: JSON.stringify({
|
|
47
|
+
payloads: Array.from(batched.keys())
|
|
48
|
+
}),
|
|
49
|
+
headers
|
|
50
|
+
});
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
}
|
|
52
|
+
if (response.redirect) {
|
|
53
|
+
await goto(response.redirect);
|
|
59
54
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
55
|
+
// settle all batched promises (with `undefined`, like a redirect
|
|
56
|
+
// from a non-batched query) so that callers don't hang forever
|
|
57
|
+
for (const resolvers of batched.values()) {
|
|
58
|
+
for (const { resolve } of resolvers) {
|
|
59
|
+
resolve(undefined);
|
|
60
|
+
}
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
throw new Redirect(307, result.location);
|
|
68
|
-
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
69
65
|
|
|
70
|
-
|
|
66
|
+
const results = response._;
|
|
67
|
+
let i = 0;
|
|
71
68
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
let i = 0;
|
|
69
|
+
for (const resolvers of batched.values()) {
|
|
70
|
+
const result = results[i];
|
|
75
71
|
|
|
76
|
-
for (const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
resolve(results[i].data);
|
|
82
|
-
}
|
|
72
|
+
for (const { resolve, reject } of resolvers) {
|
|
73
|
+
if (result.type === 'error') {
|
|
74
|
+
reject(new HttpError(result.status, result.error));
|
|
75
|
+
} else {
|
|
76
|
+
resolve(result.data);
|
|
83
77
|
}
|
|
84
|
-
i++;
|
|
85
78
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
i++;
|
|
80
|
+
}
|
|
81
|
+
} catch (e) {
|
|
82
|
+
for (const resolvers of batched.values()) {
|
|
83
|
+
for (const { reject } of resolvers) {
|
|
84
|
+
reject(e);
|
|
92
85
|
}
|
|
93
86
|
}
|
|
94
|
-
}
|
|
95
|
-
});
|
|
87
|
+
}
|
|
88
|
+
}, 0);
|
|
96
89
|
});
|
|
97
|
-
|
|
98
|
-
return devalue.parse(serialized, app.decoders);
|
|
99
90
|
});
|
|
100
91
|
};
|
|
101
92
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import * as devalue from 'devalue';
|
|
1
|
+
import { query_responses } from '../../client.js';
|
|
3
2
|
import { HttpError, Redirect } from '@sveltejs/kit/internal';
|
|
4
3
|
import { noop, once } from '../../../../utils/functions.js';
|
|
5
4
|
import { SharedIterator } from '../../../../utils/shared-iterator.js';
|
|
6
|
-
import {
|
|
5
|
+
import { tick } from 'svelte';
|
|
7
6
|
import { create_live_iterator } from './iterator.js';
|
|
8
7
|
|
|
9
8
|
/**
|
|
@@ -84,9 +83,25 @@ export class LiveQuery {
|
|
|
84
83
|
this.#resolve_first = resolve;
|
|
85
84
|
this.#reject_first = reject;
|
|
86
85
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
if (Object.hasOwn(query_responses, key)) {
|
|
87
|
+
const node = query_responses[key];
|
|
88
|
+
delete query_responses[key];
|
|
89
|
+
|
|
90
|
+
if (node.e) {
|
|
91
|
+
// the query failed during SSR — seed the failed state (mirroring `fail()`,
|
|
92
|
+
// minus its terminal `#done`), so the main loop still connects as usual
|
|
93
|
+
// and the query can recover
|
|
94
|
+
const error = new HttpError(node.e[0] ?? 500, node.e[1]);
|
|
95
|
+
this.#loading = false;
|
|
96
|
+
this.#error = error;
|
|
97
|
+
|
|
98
|
+
promise.catch(noop);
|
|
99
|
+
this.#reject_first?.(error);
|
|
100
|
+
this.#resolve_first = null;
|
|
101
|
+
this.#reject_first = null;
|
|
102
|
+
} else {
|
|
103
|
+
this.set(node.v);
|
|
104
|
+
}
|
|
90
105
|
}
|
|
91
106
|
}
|
|
92
107
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
/** @import { RemoteFunctionResponse,
|
|
1
|
+
/** @import { RemoteFunctionResponse, RemoteFunctionData, RemoteFunctionDataNode } from 'types' */
|
|
2
2
|
/** @import { RemoteQueryUpdate } from '@sveltejs/kit' */
|
|
3
|
+
/** @import { CacheEntry } from './cache.svelte.js' */
|
|
3
4
|
import * as devalue from 'devalue';
|
|
4
|
-
import { app, goto, live_query_map, query_map } from '../client.js';
|
|
5
|
+
import { app, goto, live_query_map, query_map, query_responses } from '../client.js';
|
|
5
6
|
import { HttpError, Redirect } from '@sveltejs/kit/internal';
|
|
6
7
|
import { untrack } from 'svelte';
|
|
7
8
|
import { create_remote_key, split_remote_key } from '../../shared.js';
|
|
@@ -77,6 +78,20 @@ export function pin_while_resolving(cache_map, cache, id, payload, then) {
|
|
|
77
78
|
/**
|
|
78
79
|
* @returns {{ 'x-sveltekit-pathname': string, 'x-sveltekit-search': string }}
|
|
79
80
|
*/
|
|
81
|
+
/**
|
|
82
|
+
* Unwraps a `RemoteFunctionDataNode` that was serialized during SSR,
|
|
83
|
+
* rethrowing serialized errors so the consuming resource ends up
|
|
84
|
+
* in the same failed state it had on the server
|
|
85
|
+
* @param {RemoteFunctionDataNode} node
|
|
86
|
+
*/
|
|
87
|
+
export function unwrap_node(node) {
|
|
88
|
+
if (node.e) {
|
|
89
|
+
throw new HttpError(node.e[0] ?? 500, node.e[1]);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return node.v;
|
|
93
|
+
}
|
|
94
|
+
|
|
80
95
|
export function get_remote_request_headers() {
|
|
81
96
|
// This will be the correct value of the current or soon-current url,
|
|
82
97
|
// even in forks because it's state-based - therefore not using window.location.
|
|
@@ -93,15 +108,10 @@ export function get_remote_request_headers() {
|
|
|
93
108
|
|
|
94
109
|
/**
|
|
95
110
|
* @param {string} url
|
|
96
|
-
* @param {
|
|
111
|
+
* @param {RequestInit} [init]
|
|
97
112
|
*/
|
|
98
|
-
export async function remote_request(url,
|
|
99
|
-
const response = await fetch(url,
|
|
100
|
-
headers: {
|
|
101
|
-
'Content-Type': 'application/json',
|
|
102
|
-
...headers
|
|
103
|
-
}
|
|
104
|
-
});
|
|
113
|
+
export async function remote_request(url, init) {
|
|
114
|
+
const response = await fetch(url, init);
|
|
105
115
|
|
|
106
116
|
if (!response.ok) {
|
|
107
117
|
throw new HttpError(500, 'Failed to execute remote function');
|
|
@@ -109,9 +119,63 @@ export async function remote_request(url, headers) {
|
|
|
109
119
|
|
|
110
120
|
const result = /** @type {RemoteFunctionResponse} */ (await response.json());
|
|
111
121
|
|
|
112
|
-
|
|
122
|
+
if (result.type === 'error') {
|
|
123
|
+
throw new HttpError(result.status ?? 500, result.error);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const data = /** @type {RemoteFunctionData} */ (
|
|
127
|
+
result.data ? devalue.parse(result.data, app.decoders) : {}
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
*
|
|
132
|
+
* @param {string} key
|
|
133
|
+
* @param {CacheEntry<any> | undefined} entry
|
|
134
|
+
* @param {any} result
|
|
135
|
+
*/
|
|
136
|
+
function refresh(key, entry, result) {
|
|
137
|
+
if (entry?.resource) {
|
|
138
|
+
if (result.e) {
|
|
139
|
+
entry.resource.fail(new HttpError(result.e[0] ?? 500, result.e[1]));
|
|
140
|
+
} else {
|
|
141
|
+
entry.resource.set(result.v);
|
|
142
|
+
}
|
|
143
|
+
} else if (!result.e) {
|
|
144
|
+
// `query_responses` stores `{ v }`/`{ e }` nodes, not raw values.
|
|
145
|
+
// Errors are deliberately dropped here: they are responses to a specific
|
|
146
|
+
// refresh, not durable state a future resource should initialize with
|
|
147
|
+
query_responses[key] = result;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// update queries with refreshed data
|
|
152
|
+
if (data.q) {
|
|
153
|
+
for (const key in data.q) {
|
|
154
|
+
const parts = split_remote_key(key);
|
|
155
|
+
const entry = query_map.get(parts.id)?.get(parts.payload);
|
|
156
|
+
|
|
157
|
+
refresh(key, entry, data.q[key]);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// reconnect live queries
|
|
162
|
+
if (data.l) {
|
|
163
|
+
for (const key in data.l) {
|
|
164
|
+
const parts = split_remote_key(key);
|
|
165
|
+
const entry = live_query_map.get(parts.id)?.get(parts.payload);
|
|
166
|
+
|
|
167
|
+
refresh(key, entry, data.l[key]);
|
|
113
168
|
|
|
114
|
-
|
|
169
|
+
// `fail()` is terminal, so only reconnect on the success path —
|
|
170
|
+
// reconnecting after a hard failure would wipe the error state and
|
|
171
|
+
// restart the stream (see commit 63a3e83 regression).
|
|
172
|
+
if (!data.l[key].e) {
|
|
173
|
+
void entry?.resource.reconnect();
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return data;
|
|
115
179
|
}
|
|
116
180
|
|
|
117
181
|
/**
|
|
@@ -206,50 +270,3 @@ export function categorize_updates(updates) {
|
|
|
206
270
|
|
|
207
271
|
return { overrides, refreshes };
|
|
208
272
|
}
|
|
209
|
-
|
|
210
|
-
/**
|
|
211
|
-
* @template TResource
|
|
212
|
-
* @param {string} stringified_singleflight
|
|
213
|
-
* @param {Map<string, Map<string, { resource: TResource }>>} map
|
|
214
|
-
* @param {(resource: TResource, value: RemoteSingleflightEntry) => void} callback
|
|
215
|
-
*/
|
|
216
|
-
function apply_singleflight(stringified_singleflight, map, callback) {
|
|
217
|
-
const singleflight = /** @type {RemoteSingleflightMap} */ (
|
|
218
|
-
devalue.parse(stringified_singleflight, app.decoders)
|
|
219
|
-
);
|
|
220
|
-
|
|
221
|
-
for (const [key, value] of Object.entries(singleflight)) {
|
|
222
|
-
const parts = split_remote_key(key);
|
|
223
|
-
const entry = map.get(parts.id)?.get(parts.payload);
|
|
224
|
-
if (entry?.resource) {
|
|
225
|
-
callback(entry.resource, value);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Apply refresh data from the server to the relevant queries
|
|
232
|
-
*
|
|
233
|
-
* @param {string} stringified_refreshes
|
|
234
|
-
*/
|
|
235
|
-
export const apply_refreshes = (stringified_refreshes) => {
|
|
236
|
-
apply_singleflight(stringified_refreshes, query_map, (resource, value) => {
|
|
237
|
-
if (value.type === 'result') {
|
|
238
|
-
resource?.set(value.data);
|
|
239
|
-
} else {
|
|
240
|
-
resource?.fail(new HttpError(value.status ?? 500, value.error));
|
|
241
|
-
}
|
|
242
|
-
});
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
/** @param {string} stringified_reconnects */
|
|
246
|
-
export const apply_reconnections = (stringified_reconnects) => {
|
|
247
|
-
apply_singleflight(stringified_reconnects, live_query_map, (resource, value) => {
|
|
248
|
-
if (value.type === 'result') {
|
|
249
|
-
resource?.set(value.data);
|
|
250
|
-
void resource?.reconnect();
|
|
251
|
-
} else {
|
|
252
|
-
resource?.fail(new HttpError(value.status ?? 500, value.error));
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
};
|
|
@@ -20,9 +20,9 @@ import {
|
|
|
20
20
|
get_global_name,
|
|
21
21
|
handle_error_and_jsonify
|
|
22
22
|
} from '../utils.js';
|
|
23
|
-
import { create_remote_key } from '../../shared.js';
|
|
24
23
|
import { get_status } from '../../../utils/error.js';
|
|
25
24
|
import * as env from '__sveltekit/env';
|
|
25
|
+
import { collect_remote_data } from '../remote.js';
|
|
26
26
|
|
|
27
27
|
// TODO rename this function/module
|
|
28
28
|
|
|
@@ -78,9 +78,9 @@ export async function render_response({
|
|
|
78
78
|
|
|
79
79
|
const { client } = manifest._;
|
|
80
80
|
|
|
81
|
-
const modulepreloads = new Set(client
|
|
82
|
-
const stylesheets = new Set(client
|
|
83
|
-
const fonts = new Set(client
|
|
81
|
+
const modulepreloads = new Set(client?.imports);
|
|
82
|
+
const stylesheets = new Set(client?.stylesheets);
|
|
83
|
+
const fonts = new Set(client?.fonts);
|
|
84
84
|
|
|
85
85
|
/**
|
|
86
86
|
* The value of the Link header that is added to the response when not prerendering
|
|
@@ -281,7 +281,7 @@ export async function render_response({
|
|
|
281
281
|
for (const url of node.stylesheets) stylesheets.add(url);
|
|
282
282
|
for (const url of node.fonts) fonts.add(url);
|
|
283
283
|
|
|
284
|
-
if (node.inline_styles && !client
|
|
284
|
+
if (node.inline_styles && !client?.inline) {
|
|
285
285
|
Object.entries(await node.inline_styles()).forEach(([filename, css]) => {
|
|
286
286
|
if (typeof css === 'string') {
|
|
287
287
|
inline_styles.set(filename, css);
|
|
@@ -307,7 +307,7 @@ export async function render_response({
|
|
|
307
307
|
return `${assets}/${path}`;
|
|
308
308
|
};
|
|
309
309
|
|
|
310
|
-
const style = client
|
|
310
|
+
const style = client?.inline
|
|
311
311
|
? client.inline?.style
|
|
312
312
|
: Array.from(inline_styles.values()).join('\n');
|
|
313
313
|
|
|
@@ -371,8 +371,8 @@ export async function render_response({
|
|
|
371
371
|
.join('\n\t\t\t')}`;
|
|
372
372
|
}
|
|
373
373
|
|
|
374
|
-
if (page_config.csr) {
|
|
375
|
-
const route =
|
|
374
|
+
if (page_config.csr && client) {
|
|
375
|
+
const route = client.routes?.find((r) => r.id === event.route.id) ?? null;
|
|
376
376
|
|
|
377
377
|
if (client.uses_env_dynamic_public && state.prerendering) {
|
|
378
378
|
modulepreloads.add(`${paths.app_dir}/env.js`);
|
|
@@ -400,12 +400,12 @@ export async function render_response({
|
|
|
400
400
|
}
|
|
401
401
|
|
|
402
402
|
// prerender a `/path/to/page/__route.js` module
|
|
403
|
-
if (
|
|
403
|
+
if (client.routes && state.prerendering && !state.prerendering.fallback) {
|
|
404
404
|
const pathname = add_resolution_suffix(event.url.pathname);
|
|
405
405
|
|
|
406
406
|
state.prerendering.dependencies.set(
|
|
407
407
|
pathname,
|
|
408
|
-
create_server_routing_response(route, event.params, new URL(pathname, event.url),
|
|
408
|
+
create_server_routing_response(route, event.params, new URL(pathname, event.url), client)
|
|
409
409
|
);
|
|
410
410
|
}
|
|
411
411
|
|
|
@@ -505,9 +505,9 @@ export async function render_response({
|
|
|
505
505
|
hydrate.push(`status: ${status}`);
|
|
506
506
|
}
|
|
507
507
|
|
|
508
|
-
if (
|
|
508
|
+
if (client.routes) {
|
|
509
509
|
if (route) {
|
|
510
|
-
const stringified = generate_route_object(route, event.url,
|
|
510
|
+
const stringified = generate_route_object(route, event.url, client).replaceAll(
|
|
511
511
|
'\n',
|
|
512
512
|
'\n\t\t\t\t\t\t\t'
|
|
513
513
|
); // make output after it's put together with the rest more readable
|
|
@@ -521,87 +521,27 @@ export async function render_response({
|
|
|
521
521
|
args.push(`{\n${indent}\t${hydrate.join(`,\n${indent}\t`)}\n${indent}}`);
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
let serialized_query_data = '';
|
|
527
|
-
let serialized_prerender_data = '';
|
|
528
|
-
|
|
529
|
-
if (remote.data) {
|
|
530
|
-
/** @type {Record<string, any>} */
|
|
531
|
-
const query = {};
|
|
532
|
-
|
|
533
|
-
/** @type {Record<string, any>} */
|
|
534
|
-
const prerender = {};
|
|
535
|
-
|
|
536
|
-
for (const [internals, cache] of remote.data) {
|
|
537
|
-
// remote functions without an `id` aren't exported, and thus
|
|
538
|
-
// cannot be called from the client
|
|
539
|
-
if (!internals.id) continue;
|
|
540
|
-
|
|
541
|
-
for (const key in cache) {
|
|
542
|
-
const entry = cache[key];
|
|
543
|
-
|
|
544
|
-
if (!entry.serialize) continue;
|
|
545
|
-
|
|
546
|
-
const remote_key = create_remote_key(internals.id, key);
|
|
547
|
-
|
|
548
|
-
const store = internals.type === 'prerender' ? prerender : query;
|
|
549
|
-
|
|
550
|
-
if (
|
|
551
|
-
event_state.remote.refreshes?.has(remote_key) ||
|
|
552
|
-
event_state.remote.reconnects?.has(remote_key)
|
|
553
|
-
) {
|
|
554
|
-
// This entry was refreshed/set by a command or form action.
|
|
555
|
-
// Always await it so the mutation result is serialized.
|
|
556
|
-
store[remote_key] = await entry.data;
|
|
557
|
-
} else {
|
|
558
|
-
// Don't block the response on pending remote data - if a query
|
|
559
|
-
// hasn't settled yet, it wasn't awaited in the template (or is behind a pending boundary).
|
|
560
|
-
const result = await Promise.race([
|
|
561
|
-
Promise.resolve(entry.data).then(
|
|
562
|
-
(v) => /** @type {const} */ ({ settled: true, value: v }),
|
|
563
|
-
(e) => /** @type {const} */ ({ settled: true, error: e })
|
|
564
|
-
),
|
|
565
|
-
new Promise((resolve) => {
|
|
566
|
-
queueMicrotask(() => resolve(/** @type {const} */ ({ settled: false })));
|
|
567
|
-
})
|
|
568
|
-
]);
|
|
569
|
-
|
|
570
|
-
if (result.settled) {
|
|
571
|
-
if ('error' in result) throw result.error;
|
|
572
|
-
store[remote_key] = result.value;
|
|
573
|
-
}
|
|
574
|
-
}
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
const replacer = create_replacer(options.hooks.transport);
|
|
579
|
-
|
|
580
|
-
if (Object.keys(query).length > 0) {
|
|
581
|
-
serialized_query_data = `${global}.query = ${devalue.uneval(query, replacer)};\n\n\t\t\t\t\t\t`;
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
if (Object.keys(prerender).length > 0) {
|
|
585
|
-
serialized_prerender_data = `${global}.prerender = ${devalue.uneval(prerender, replacer)};\n\n\t\t\t\t\t\t`;
|
|
586
|
-
}
|
|
587
|
-
}
|
|
524
|
+
const remote_data = await collect_remote_data({}, event, event_state, options);
|
|
588
525
|
|
|
589
|
-
const
|
|
526
|
+
const serialized_data =
|
|
527
|
+
Object.keys(remote_data).length > 0
|
|
528
|
+
? `${global}.data = ${devalue.uneval(remote_data, create_replacer(options.hooks.transport))};\n\n\t\t\t\t\t\t`
|
|
529
|
+
: '';
|
|
590
530
|
|
|
591
531
|
// `client.app` is a proxy for `bundleStrategy === 'split'`
|
|
592
532
|
const boot = client.inline
|
|
593
533
|
? `${client.inline.script}
|
|
594
534
|
|
|
595
|
-
${
|
|
535
|
+
${serialized_data}${global}.app.start(${args.join(', ')});`
|
|
596
536
|
: client.app
|
|
597
537
|
? `Promise.all([
|
|
598
538
|
import(${s(prefixed(client.start))}),
|
|
599
539
|
import(${s(prefixed(client.app))})
|
|
600
540
|
]).then(([kit, app]) => {
|
|
601
|
-
${
|
|
541
|
+
${serialized_data}kit.start(app, ${args.join(', ')});
|
|
602
542
|
});`
|
|
603
543
|
: `import(${s(prefixed(client.start))}).then((app) => {
|
|
604
|
-
${
|
|
544
|
+
${serialized_data}app.start(${args.join(', ')})
|
|
605
545
|
});`;
|
|
606
546
|
|
|
607
547
|
if (load_env_eagerly) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** @import { SSRManifest } from '@sveltejs/kit' */
|
|
1
2
|
import { base, assets, relative } from '$app/paths/internal/server';
|
|
2
3
|
import { text } from '@sveltejs/kit';
|
|
3
4
|
import { s } from '../../../utils/misc.js';
|
|
@@ -7,15 +8,15 @@ import { get_relative_path } from '../../utils.js';
|
|
|
7
8
|
/**
|
|
8
9
|
* @param {import('types').SSRClientRoute} route
|
|
9
10
|
* @param {URL} url
|
|
10
|
-
* @param {
|
|
11
|
+
* @param {NonNullable<SSRManifest['_']['client']>} client
|
|
11
12
|
* @returns {string}
|
|
12
13
|
*/
|
|
13
|
-
export function generate_route_object(route, url,
|
|
14
|
+
export function generate_route_object(route, url, client) {
|
|
14
15
|
const { errors, layouts, leaf } = route;
|
|
15
16
|
|
|
16
17
|
const nodes = [...errors, ...layouts.map((l) => l?.[1]), leaf[1]]
|
|
17
18
|
.filter((n) => typeof n === 'number')
|
|
18
|
-
.map((n) => `'${n}': () => ${create_client_import(
|
|
19
|
+
.map((n) => `'${n}': () => ${create_client_import(client.nodes?.[n], url)}`)
|
|
19
20
|
.join(',\n\t\t');
|
|
20
21
|
|
|
21
22
|
// stringified version of
|
|
@@ -60,36 +61,40 @@ function create_client_import(import_path, url) {
|
|
|
60
61
|
/**
|
|
61
62
|
* @param {string} resolved_path
|
|
62
63
|
* @param {URL} url
|
|
63
|
-
* @param {
|
|
64
|
+
* @param {SSRManifest} manifest
|
|
64
65
|
* @returns {Promise<Response>}
|
|
65
66
|
*/
|
|
66
67
|
export async function resolve_route(resolved_path, url, manifest) {
|
|
67
|
-
if (!manifest._.client
|
|
68
|
+
if (!manifest._.client?.routes) {
|
|
68
69
|
return text('Server-side route resolution disabled', { status: 400 });
|
|
69
70
|
}
|
|
70
71
|
|
|
71
72
|
const matchers = await manifest._.matchers();
|
|
72
73
|
const result = find_route(resolved_path, manifest._.client.routes, matchers);
|
|
73
74
|
|
|
74
|
-
return create_server_routing_response(
|
|
75
|
-
|
|
75
|
+
return create_server_routing_response(
|
|
76
|
+
result?.route ?? null,
|
|
77
|
+
result?.params ?? {},
|
|
78
|
+
url,
|
|
79
|
+
manifest._.client
|
|
80
|
+
).response;
|
|
76
81
|
}
|
|
77
82
|
|
|
78
83
|
/**
|
|
79
84
|
* @param {import('types').SSRClientRoute | null} route
|
|
80
85
|
* @param {Partial<Record<string, string>>} params
|
|
81
86
|
* @param {URL} url
|
|
82
|
-
* @param {
|
|
87
|
+
* @param {NonNullable<SSRManifest['_']['client']>} client
|
|
83
88
|
* @returns {{response: Response, body: string}}
|
|
84
89
|
*/
|
|
85
|
-
export function create_server_routing_response(route, params, url,
|
|
90
|
+
export function create_server_routing_response(route, params, url, client) {
|
|
86
91
|
const headers = new Headers({
|
|
87
92
|
'content-type': 'application/javascript; charset=utf-8'
|
|
88
93
|
});
|
|
89
94
|
|
|
90
95
|
if (route) {
|
|
91
|
-
const csr_route = generate_route_object(route, url,
|
|
92
|
-
const body = `${create_css_import(route, url,
|
|
96
|
+
const csr_route = generate_route_object(route, url, client);
|
|
97
|
+
const body = `${create_css_import(route, url, client)}\nexport const route = ${csr_route}; export const params = ${JSON.stringify(params)};`;
|
|
93
98
|
|
|
94
99
|
return { response: text(body, { headers }), body };
|
|
95
100
|
} else {
|
|
@@ -105,17 +110,17 @@ export function create_server_routing_response(route, params, url, manifest) {
|
|
|
105
110
|
*
|
|
106
111
|
* @param {import('types').SSRClientRoute} route
|
|
107
112
|
* @param {URL} url
|
|
108
|
-
* @param {
|
|
113
|
+
* @param {NonNullable<SSRManifest['_']['client']>} client
|
|
109
114
|
* @returns {string}
|
|
110
115
|
*/
|
|
111
|
-
function create_css_import(route, url,
|
|
116
|
+
function create_css_import(route, url, client) {
|
|
112
117
|
const { errors, layouts, leaf } = route;
|
|
113
118
|
|
|
114
119
|
let css = '';
|
|
115
120
|
|
|
116
121
|
for (const node of [...errors, ...layouts.map((l) => l?.[1]), leaf[1]]) {
|
|
117
122
|
if (typeof node !== 'number') continue;
|
|
118
|
-
const node_css =
|
|
123
|
+
const node_css = client.css?.[node];
|
|
119
124
|
for (const css_path of node_css ?? []) {
|
|
120
125
|
css += `'${assets || base}/${css_path}',`;
|
|
121
126
|
}
|
|
@@ -123,5 +128,5 @@ function create_css_import(route, url, manifest) {
|
|
|
123
128
|
|
|
124
129
|
if (!css) return '';
|
|
125
130
|
|
|
126
|
-
return `${create_client_import(
|
|
131
|
+
return `${create_client_import(client.start, url)}.then(x => x.load_css([${css}]));`;
|
|
127
132
|
}
|