@sveltejs/kit 1.0.0-next.23 → 1.0.0-next.233

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/README.md +12 -9
  2. package/assets/app/env.js +20 -0
  3. package/assets/app/navigation.js +79 -0
  4. package/assets/app/paths.js +1 -0
  5. package/assets/{runtime/app → app}/stores.js +19 -15
  6. package/assets/chunks/utils.js +13 -0
  7. package/assets/client/singletons.js +21 -0
  8. package/assets/client/start.js +1382 -0
  9. package/assets/components/error.svelte +19 -3
  10. package/assets/env.js +8 -0
  11. package/assets/paths.js +13 -0
  12. package/assets/server/index.js +2004 -0
  13. package/dist/chunks/build.js +658 -0
  14. package/dist/chunks/cert.js +28154 -0
  15. package/dist/chunks/index.js +2420 -0
  16. package/dist/chunks/index2.js +807 -0
  17. package/dist/chunks/index3.js +648 -0
  18. package/dist/chunks/index4.js +109 -0
  19. package/dist/chunks/index5.js +760 -0
  20. package/dist/chunks/index6.js +178 -0
  21. package/dist/chunks/index7.js +15574 -0
  22. package/dist/chunks/index8.js +4207 -0
  23. package/dist/chunks/misc.js +3 -0
  24. package/dist/chunks/multipart-parser.js +449 -0
  25. package/dist/chunks/url.js +62 -0
  26. package/dist/cli.js +1039 -84
  27. package/dist/hooks.js +28 -0
  28. package/dist/install-fetch.js +6518 -0
  29. package/dist/node.js +51 -0
  30. package/package.json +93 -54
  31. package/svelte-kit.js +2 -0
  32. package/types/ambient-modules.d.ts +204 -0
  33. package/types/app.d.ts +45 -0
  34. package/types/config.d.ts +169 -0
  35. package/types/endpoint.d.ts +20 -0
  36. package/types/helper.d.ts +53 -0
  37. package/types/hooks.d.ts +55 -0
  38. package/types/index.d.ts +18 -0
  39. package/types/internal.d.ts +237 -0
  40. package/types/page.d.ts +73 -0
  41. package/CHANGELOG.md +0 -294
  42. package/assets/runtime/app/navigation.js +0 -23
  43. package/assets/runtime/app/navigation.js.map +0 -1
  44. package/assets/runtime/app/paths.js +0 -2
  45. package/assets/runtime/app/paths.js.map +0 -1
  46. package/assets/runtime/app/stores.js.map +0 -1
  47. package/assets/runtime/internal/singletons.js +0 -15
  48. package/assets/runtime/internal/singletons.js.map +0 -1
  49. package/assets/runtime/internal/start.js +0 -591
  50. package/assets/runtime/internal/start.js.map +0 -1
  51. package/assets/runtime/utils-85ebcc60.js +0 -18
  52. package/assets/runtime/utils-85ebcc60.js.map +0 -1
  53. package/dist/api.js +0 -44
  54. package/dist/api.js.map +0 -1
  55. package/dist/build.js +0 -246
  56. package/dist/build.js.map +0 -1
  57. package/dist/cli.js.map +0 -1
  58. package/dist/colors.js +0 -37
  59. package/dist/colors.js.map +0 -1
  60. package/dist/create_app.js +0 -578
  61. package/dist/create_app.js.map +0 -1
  62. package/dist/index.js +0 -367
  63. package/dist/index.js.map +0 -1
  64. package/dist/index2.js +0 -12044
  65. package/dist/index2.js.map +0 -1
  66. package/dist/index3.js +0 -547
  67. package/dist/index3.js.map +0 -1
  68. package/dist/index4.js +0 -73
  69. package/dist/index4.js.map +0 -1
  70. package/dist/index5.js +0 -464
  71. package/dist/index5.js.map +0 -1
  72. package/dist/index6.js +0 -729
  73. package/dist/index6.js.map +0 -1
  74. package/dist/logging.js +0 -43
  75. package/dist/logging.js.map +0 -1
  76. package/dist/package.js +0 -432
  77. package/dist/package.js.map +0 -1
  78. package/dist/renderer.js +0 -2391
  79. package/dist/renderer.js.map +0 -1
  80. package/dist/standard.js +0 -101
  81. package/dist/standard.js.map +0 -1
  82. package/dist/utils.js +0 -54
  83. package/dist/utils.js.map +0 -1
  84. package/svelte-kit +0 -3
@@ -0,0 +1,2420 @@
1
+ import path__default from 'path';
2
+ import { svelte } from '@sveltejs/vite-plugin-svelte';
3
+ import vite from 'vite';
4
+ import { c as create_manifest_data, a as create_app, d as deep_merge } from './index2.js';
5
+ import { c as coalesce_to_error, S as SVELTE_KIT_ASSETS, r as resolve_entry, $, a as SVELTE_KIT, b as runtime, l as load_template, g as get_mime_lookup, d as copy_assets, e as get_aliases, p as print_config_conflicts } from '../cli.js';
6
+ import fs__default from 'fs';
7
+ import { URL as URL$1 } from 'url';
8
+ import { s as sirv } from './build.js';
9
+ import { g as get_single_valued_header, r as resolve, i as is_root_relative } from './url.js';
10
+ import { s } from './misc.js';
11
+ import { __fetch_polyfill } from '../install-fetch.js';
12
+ import { getRawBody } from '../node.js';
13
+ import 'sade';
14
+ import 'child_process';
15
+ import 'net';
16
+ import 'os';
17
+ import 'querystring';
18
+ import 'node:http';
19
+ import 'node:https';
20
+ import 'node:zlib';
21
+ import 'node:stream';
22
+ import 'node:util';
23
+ import 'node:url';
24
+
25
+ /** @param {Record<string, any>} obj */
26
+ function lowercase_keys(obj) {
27
+ /** @type {Record<string, any>} */
28
+ const clone = {};
29
+
30
+ for (const key in obj) {
31
+ clone[key.toLowerCase()] = obj[key];
32
+ }
33
+
34
+ return clone;
35
+ }
36
+
37
+ /** @param {Record<string, string>} params */
38
+ function decode_params(params) {
39
+ for (const key in params) {
40
+ // input has already been decoded by decodeURI
41
+ // now handle the rest that decodeURIComponent would do
42
+ params[key] = params[key]
43
+ .replace(/%23/g, '#')
44
+ .replace(/%3[Bb]/g, ';')
45
+ .replace(/%2[Cc]/g, ',')
46
+ .replace(/%2[Ff]/g, '/')
47
+ .replace(/%3[Ff]/g, '?')
48
+ .replace(/%3[Aa]/g, ':')
49
+ .replace(/%40/g, '@')
50
+ .replace(/%26/g, '&')
51
+ .replace(/%3[Dd]/g, '=')
52
+ .replace(/%2[Bb]/g, '+')
53
+ .replace(/%24/g, '$');
54
+ }
55
+
56
+ return params;
57
+ }
58
+
59
+ /** @param {string} body */
60
+ function error(body) {
61
+ return {
62
+ status: 500,
63
+ body,
64
+ headers: {}
65
+ };
66
+ }
67
+
68
+ /** @param {unknown} s */
69
+ function is_string(s) {
70
+ return typeof s === 'string' || s instanceof String;
71
+ }
72
+
73
+ const text_types = new Set([
74
+ 'application/xml',
75
+ 'application/json',
76
+ 'application/x-www-form-urlencoded',
77
+ 'multipart/form-data'
78
+ ]);
79
+
80
+ /**
81
+ * Decides how the body should be parsed based on its mime type. Should match what's in parse_body
82
+ *
83
+ * @param {string | undefined | null} content_type The `content-type` header of a request/response.
84
+ * @returns {boolean}
85
+ */
86
+ function is_text(content_type) {
87
+ if (!content_type) return true; // defaults to json
88
+ const type = content_type.split(';')[0].toLowerCase(); // get the mime type
89
+
90
+ return type.startsWith('text/') || type.endsWith('+xml') || text_types.has(type);
91
+ }
92
+
93
+ /**
94
+ * @param {import('types/hooks').ServerRequest} request
95
+ * @param {import('types/internal').SSREndpoint} route
96
+ * @param {RegExpExecArray} match
97
+ * @returns {Promise<import('types/hooks').ServerResponse | undefined>}
98
+ */
99
+ async function render_endpoint(request, route, match) {
100
+ const mod = await route.load();
101
+
102
+ /** @type {import('types/endpoint').RequestHandler} */
103
+ const handler = mod[request.method.toLowerCase().replace('delete', 'del')]; // 'delete' is a reserved word
104
+
105
+ if (!handler) {
106
+ return;
107
+ }
108
+
109
+ // we're mutating `request` so that we don't have to do { ...request, params }
110
+ // on the next line, since that breaks the getters that replace path, query and
111
+ // origin. We could revert that once we remove the getters
112
+ request.params = route.params ? decode_params(route.params(match)) : {};
113
+
114
+ const response = await handler(request);
115
+ const preface = `Invalid response from route ${request.url.pathname}`;
116
+
117
+ if (typeof response !== 'object') {
118
+ return error(`${preface}: expected an object, got ${typeof response}`);
119
+ }
120
+
121
+ if (response.fallthrough) {
122
+ return;
123
+ }
124
+
125
+ let { status = 200, body, headers = {} } = response;
126
+
127
+ headers = lowercase_keys(headers);
128
+ const type = get_single_valued_header(headers, 'content-type');
129
+
130
+ if (!is_text(type) && !(body instanceof Uint8Array || is_string(body))) {
131
+ return error(
132
+ `${preface}: body must be an instance of string or Uint8Array if content-type is not a supported textual content-type`
133
+ );
134
+ }
135
+
136
+ /** @type {import('types/hooks').StrictBody} */
137
+ let normalized_body;
138
+
139
+ // ensure the body is an object
140
+ if (
141
+ (typeof body === 'object' || typeof body === 'undefined') &&
142
+ !(body instanceof Uint8Array) &&
143
+ (!type || type.startsWith('application/json'))
144
+ ) {
145
+ headers = { ...headers, 'content-type': 'application/json; charset=utf-8' };
146
+ normalized_body = JSON.stringify(typeof body === 'undefined' ? {} : body);
147
+ } else {
148
+ normalized_body = /** @type {import('types/hooks').StrictBody} */ (body);
149
+ }
150
+
151
+ return { status, body: normalized_body, headers };
152
+ }
153
+
154
+ var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$';
155
+ var unsafeChars = /[<>\b\f\n\r\t\0\u2028\u2029]/g;
156
+ var reserved = /^(?:do|if|in|for|int|let|new|try|var|byte|case|char|else|enum|goto|long|this|void|with|await|break|catch|class|const|final|float|short|super|throw|while|yield|delete|double|export|import|native|return|switch|throws|typeof|boolean|default|extends|finally|package|private|abstract|continue|debugger|function|volatile|interface|protected|transient|implements|instanceof|synchronized)$/;
157
+ var escaped = {
158
+ '<': '\\u003C',
159
+ '>': '\\u003E',
160
+ '/': '\\u002F',
161
+ '\\': '\\\\',
162
+ '\b': '\\b',
163
+ '\f': '\\f',
164
+ '\n': '\\n',
165
+ '\r': '\\r',
166
+ '\t': '\\t',
167
+ '\0': '\\0',
168
+ '\u2028': '\\u2028',
169
+ '\u2029': '\\u2029'
170
+ };
171
+ var objectProtoOwnPropertyNames = Object.getOwnPropertyNames(Object.prototype).sort().join('\0');
172
+ function devalue(value) {
173
+ var counts = new Map();
174
+ function walk(thing) {
175
+ if (typeof thing === 'function') {
176
+ throw new Error("Cannot stringify a function");
177
+ }
178
+ if (counts.has(thing)) {
179
+ counts.set(thing, counts.get(thing) + 1);
180
+ return;
181
+ }
182
+ counts.set(thing, 1);
183
+ if (!isPrimitive(thing)) {
184
+ var type = getType(thing);
185
+ switch (type) {
186
+ case 'Number':
187
+ case 'String':
188
+ case 'Boolean':
189
+ case 'Date':
190
+ case 'RegExp':
191
+ return;
192
+ case 'Array':
193
+ thing.forEach(walk);
194
+ break;
195
+ case 'Set':
196
+ case 'Map':
197
+ Array.from(thing).forEach(walk);
198
+ break;
199
+ default:
200
+ var proto = Object.getPrototypeOf(thing);
201
+ if (proto !== Object.prototype &&
202
+ proto !== null &&
203
+ Object.getOwnPropertyNames(proto).sort().join('\0') !== objectProtoOwnPropertyNames) {
204
+ throw new Error("Cannot stringify arbitrary non-POJOs");
205
+ }
206
+ if (Object.getOwnPropertySymbols(thing).length > 0) {
207
+ throw new Error("Cannot stringify POJOs with symbolic keys");
208
+ }
209
+ Object.keys(thing).forEach(function (key) { return walk(thing[key]); });
210
+ }
211
+ }
212
+ }
213
+ walk(value);
214
+ var names = new Map();
215
+ Array.from(counts)
216
+ .filter(function (entry) { return entry[1] > 1; })
217
+ .sort(function (a, b) { return b[1] - a[1]; })
218
+ .forEach(function (entry, i) {
219
+ names.set(entry[0], getName(i));
220
+ });
221
+ function stringify(thing) {
222
+ if (names.has(thing)) {
223
+ return names.get(thing);
224
+ }
225
+ if (isPrimitive(thing)) {
226
+ return stringifyPrimitive(thing);
227
+ }
228
+ var type = getType(thing);
229
+ switch (type) {
230
+ case 'Number':
231
+ case 'String':
232
+ case 'Boolean':
233
+ return "Object(" + stringify(thing.valueOf()) + ")";
234
+ case 'RegExp':
235
+ return "new RegExp(" + stringifyString(thing.source) + ", \"" + thing.flags + "\")";
236
+ case 'Date':
237
+ return "new Date(" + thing.getTime() + ")";
238
+ case 'Array':
239
+ var members = thing.map(function (v, i) { return i in thing ? stringify(v) : ''; });
240
+ var tail = thing.length === 0 || (thing.length - 1 in thing) ? '' : ',';
241
+ return "[" + members.join(',') + tail + "]";
242
+ case 'Set':
243
+ case 'Map':
244
+ return "new " + type + "([" + Array.from(thing).map(stringify).join(',') + "])";
245
+ default:
246
+ var obj = "{" + Object.keys(thing).map(function (key) { return safeKey(key) + ":" + stringify(thing[key]); }).join(',') + "}";
247
+ var proto = Object.getPrototypeOf(thing);
248
+ if (proto === null) {
249
+ return Object.keys(thing).length > 0
250
+ ? "Object.assign(Object.create(null)," + obj + ")"
251
+ : "Object.create(null)";
252
+ }
253
+ return obj;
254
+ }
255
+ }
256
+ var str = stringify(value);
257
+ if (names.size) {
258
+ var params_1 = [];
259
+ var statements_1 = [];
260
+ var values_1 = [];
261
+ names.forEach(function (name, thing) {
262
+ params_1.push(name);
263
+ if (isPrimitive(thing)) {
264
+ values_1.push(stringifyPrimitive(thing));
265
+ return;
266
+ }
267
+ var type = getType(thing);
268
+ switch (type) {
269
+ case 'Number':
270
+ case 'String':
271
+ case 'Boolean':
272
+ values_1.push("Object(" + stringify(thing.valueOf()) + ")");
273
+ break;
274
+ case 'RegExp':
275
+ values_1.push(thing.toString());
276
+ break;
277
+ case 'Date':
278
+ values_1.push("new Date(" + thing.getTime() + ")");
279
+ break;
280
+ case 'Array':
281
+ values_1.push("Array(" + thing.length + ")");
282
+ thing.forEach(function (v, i) {
283
+ statements_1.push(name + "[" + i + "]=" + stringify(v));
284
+ });
285
+ break;
286
+ case 'Set':
287
+ values_1.push("new Set");
288
+ statements_1.push(name + "." + Array.from(thing).map(function (v) { return "add(" + stringify(v) + ")"; }).join('.'));
289
+ break;
290
+ case 'Map':
291
+ values_1.push("new Map");
292
+ statements_1.push(name + "." + Array.from(thing).map(function (_a) {
293
+ var k = _a[0], v = _a[1];
294
+ return "set(" + stringify(k) + ", " + stringify(v) + ")";
295
+ }).join('.'));
296
+ break;
297
+ default:
298
+ values_1.push(Object.getPrototypeOf(thing) === null ? 'Object.create(null)' : '{}');
299
+ Object.keys(thing).forEach(function (key) {
300
+ statements_1.push("" + name + safeProp(key) + "=" + stringify(thing[key]));
301
+ });
302
+ }
303
+ });
304
+ statements_1.push("return " + str);
305
+ return "(function(" + params_1.join(',') + "){" + statements_1.join(';') + "}(" + values_1.join(',') + "))";
306
+ }
307
+ else {
308
+ return str;
309
+ }
310
+ }
311
+ function getName(num) {
312
+ var name = '';
313
+ do {
314
+ name = chars[num % chars.length] + name;
315
+ num = ~~(num / chars.length) - 1;
316
+ } while (num >= 0);
317
+ return reserved.test(name) ? name + "_" : name;
318
+ }
319
+ function isPrimitive(thing) {
320
+ return Object(thing) !== thing;
321
+ }
322
+ function stringifyPrimitive(thing) {
323
+ if (typeof thing === 'string')
324
+ return stringifyString(thing);
325
+ if (thing === void 0)
326
+ return 'void 0';
327
+ if (thing === 0 && 1 / thing < 0)
328
+ return '-0';
329
+ var str = String(thing);
330
+ if (typeof thing === 'number')
331
+ return str.replace(/^(-)?0\./, '$1.');
332
+ return str;
333
+ }
334
+ function getType(thing) {
335
+ return Object.prototype.toString.call(thing).slice(8, -1);
336
+ }
337
+ function escapeUnsafeChar(c) {
338
+ return escaped[c] || c;
339
+ }
340
+ function escapeUnsafeChars(str) {
341
+ return str.replace(unsafeChars, escapeUnsafeChar);
342
+ }
343
+ function safeKey(key) {
344
+ return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key) ? key : escapeUnsafeChars(JSON.stringify(key));
345
+ }
346
+ function safeProp(key) {
347
+ return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key) ? "." + key : "[" + escapeUnsafeChars(JSON.stringify(key)) + "]";
348
+ }
349
+ function stringifyString(str) {
350
+ var result = '"';
351
+ for (var i = 0; i < str.length; i += 1) {
352
+ var char = str.charAt(i);
353
+ var code = char.charCodeAt(0);
354
+ if (char === '"') {
355
+ result += '\\"';
356
+ }
357
+ else if (char in escaped) {
358
+ result += escaped[char];
359
+ }
360
+ else if (code >= 0xd800 && code <= 0xdfff) {
361
+ var next = str.charCodeAt(i + 1);
362
+ // If this is the beginning of a [high, low] surrogate pair,
363
+ // add the next two characters, otherwise escape
364
+ if (code <= 0xdbff && (next >= 0xdc00 && next <= 0xdfff)) {
365
+ result += char + str[++i];
366
+ }
367
+ else {
368
+ result += "\\u" + code.toString(16).toUpperCase();
369
+ }
370
+ }
371
+ else {
372
+ result += char;
373
+ }
374
+ }
375
+ result += '"';
376
+ return result;
377
+ }
378
+
379
+ function noop() { }
380
+ function safe_not_equal(a, b) {
381
+ return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
382
+ }
383
+ Promise.resolve();
384
+
385
+ const subscriber_queue = [];
386
+ /**
387
+ * Create a `Writable` store that allows both updating and reading by subscription.
388
+ * @param {*=}value initial value
389
+ * @param {StartStopNotifier=}start start and stop notifications for subscriptions
390
+ */
391
+ function writable(value, start = noop) {
392
+ let stop;
393
+ const subscribers = new Set();
394
+ function set(new_value) {
395
+ if (safe_not_equal(value, new_value)) {
396
+ value = new_value;
397
+ if (stop) { // store is ready
398
+ const run_queue = !subscriber_queue.length;
399
+ for (const subscriber of subscribers) {
400
+ subscriber[1]();
401
+ subscriber_queue.push(subscriber, value);
402
+ }
403
+ if (run_queue) {
404
+ for (let i = 0; i < subscriber_queue.length; i += 2) {
405
+ subscriber_queue[i][0](subscriber_queue[i + 1]);
406
+ }
407
+ subscriber_queue.length = 0;
408
+ }
409
+ }
410
+ }
411
+ }
412
+ function update(fn) {
413
+ set(fn(value));
414
+ }
415
+ function subscribe(run, invalidate = noop) {
416
+ const subscriber = [run, invalidate];
417
+ subscribers.add(subscriber);
418
+ if (subscribers.size === 1) {
419
+ stop = start(set) || noop;
420
+ }
421
+ run(value);
422
+ return () => {
423
+ subscribers.delete(subscriber);
424
+ if (subscribers.size === 0) {
425
+ stop();
426
+ stop = null;
427
+ }
428
+ };
429
+ }
430
+ return { set, update, subscribe };
431
+ }
432
+
433
+ /**
434
+ * Hash using djb2
435
+ * @param {import('types/hooks').StrictBody} value
436
+ */
437
+ function hash(value) {
438
+ let hash = 5381;
439
+ let i = value.length;
440
+
441
+ if (typeof value === 'string') {
442
+ while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
443
+ } else {
444
+ while (i) hash = (hash * 33) ^ value[--i];
445
+ }
446
+
447
+ return (hash >>> 0).toString(36);
448
+ }
449
+
450
+ /** @type {Record<string, string>} */
451
+ const escape_json_string_in_html_dict = {
452
+ '"': '\\"',
453
+ '<': '\\u003C',
454
+ '>': '\\u003E',
455
+ '/': '\\u002F',
456
+ '\\': '\\\\',
457
+ '\b': '\\b',
458
+ '\f': '\\f',
459
+ '\n': '\\n',
460
+ '\r': '\\r',
461
+ '\t': '\\t',
462
+ '\0': '\\0',
463
+ '\u2028': '\\u2028',
464
+ '\u2029': '\\u2029'
465
+ };
466
+
467
+ /** @param {string} str */
468
+ function escape_json_string_in_html(str) {
469
+ return escape(
470
+ str,
471
+ escape_json_string_in_html_dict,
472
+ (code) => `\\u${code.toString(16).toUpperCase()}`
473
+ );
474
+ }
475
+
476
+ /** @type {Record<string, string>} */
477
+ const escape_html_attr_dict = {
478
+ '<': '&lt;',
479
+ '>': '&gt;',
480
+ '"': '&quot;'
481
+ };
482
+
483
+ /**
484
+ * use for escaping string values to be used html attributes on the page
485
+ * e.g.
486
+ * <script data-url="here">
487
+ *
488
+ * @param {string} str
489
+ * @returns string escaped string
490
+ */
491
+ function escape_html_attr(str) {
492
+ return '"' + escape(str, escape_html_attr_dict, (code) => `&#${code};`) + '"';
493
+ }
494
+
495
+ /**
496
+ *
497
+ * @param str {string} string to escape
498
+ * @param dict {Record<string, string>} dictionary of character replacements
499
+ * @param unicode_encoder {function(number): string} encoder to use for high unicode characters
500
+ * @returns {string}
501
+ */
502
+ function escape(str, dict, unicode_encoder) {
503
+ let result = '';
504
+
505
+ for (let i = 0; i < str.length; i += 1) {
506
+ const char = str.charAt(i);
507
+ const code = char.charCodeAt(0);
508
+
509
+ if (char in dict) {
510
+ result += dict[char];
511
+ } else if (code >= 0xd800 && code <= 0xdfff) {
512
+ const next = str.charCodeAt(i + 1);
513
+
514
+ // If this is the beginning of a [high, low] surrogate pair,
515
+ // add the next two characters, otherwise escape
516
+ if (code <= 0xdbff && next >= 0xdc00 && next <= 0xdfff) {
517
+ result += char + str[++i];
518
+ } else {
519
+ result += unicode_encoder(code);
520
+ }
521
+ } else {
522
+ result += char;
523
+ }
524
+ }
525
+
526
+ return result;
527
+ }
528
+
529
+ /** @param {URL} url */
530
+ function create_prerendering_url_proxy(url) {
531
+ return new Proxy(url, {
532
+ get: (target, prop, receiver) => {
533
+ if (prop === 'search' || prop === 'searchParams') {
534
+ throw new Error(`Cannot access url.${prop} on a page with prerendering enabled`);
535
+ }
536
+ return Reflect.get(target, prop, receiver);
537
+ }
538
+ });
539
+ }
540
+
541
+ // TODO rename this function/module
542
+
543
+ /**
544
+ * @param {{
545
+ * branch: Array<import('./types').Loaded>;
546
+ * options: import('types/internal').SSRRenderOptions;
547
+ * state: import('types/internal').SSRRenderState;
548
+ * $session: any;
549
+ * page_config: { hydrate: boolean, router: boolean };
550
+ * status: number;
551
+ * error?: Error;
552
+ * url: URL;
553
+ * params: Record<string, string>;
554
+ * ssr: boolean;
555
+ * stuff: Record<string, any>;
556
+ * }} opts
557
+ */
558
+ async function render_response({
559
+ branch,
560
+ options,
561
+ state,
562
+ $session,
563
+ page_config,
564
+ status,
565
+ error,
566
+ url,
567
+ params,
568
+ ssr,
569
+ stuff
570
+ }) {
571
+ const css = new Set(options.manifest._.entry.css);
572
+ const js = new Set(options.manifest._.entry.js);
573
+ /** @type {Map<string, string>} */
574
+ const styles = new Map();
575
+
576
+ /** @type {Array<{ url: string, body: string, json: string }>} */
577
+ const serialized_data = [];
578
+
579
+ let rendered;
580
+
581
+ let is_private = false;
582
+ let maxage;
583
+
584
+ if (error) {
585
+ error.stack = options.get_stack(error);
586
+ }
587
+
588
+ if (ssr) {
589
+ branch.forEach(({ node, loaded, fetched, uses_credentials }) => {
590
+ if (node.css) node.css.forEach((url) => css.add(url));
591
+ if (node.js) node.js.forEach((url) => js.add(url));
592
+ if (node.styles) Object.entries(node.styles).forEach(([k, v]) => styles.set(k, v));
593
+
594
+ // TODO probably better if `fetched` wasn't populated unless `hydrate`
595
+ if (fetched && page_config.hydrate) serialized_data.push(...fetched);
596
+
597
+ if (uses_credentials) is_private = true;
598
+
599
+ maxage = loaded.maxage;
600
+ });
601
+
602
+ const session = writable($session);
603
+
604
+ /** @type {Record<string, any>} */
605
+ const props = {
606
+ stores: {
607
+ page: writable(null),
608
+ navigating: writable(null),
609
+ session
610
+ },
611
+ page: {
612
+ url: state.prerender ? create_prerendering_url_proxy(url) : url,
613
+ params,
614
+ status,
615
+ error,
616
+ stuff
617
+ },
618
+ components: branch.map(({ node }) => node.module.default)
619
+ };
620
+
621
+ // TODO remove this for 1.0
622
+ /**
623
+ * @param {string} property
624
+ * @param {string} replacement
625
+ */
626
+ const print_error = (property, replacement) => {
627
+ Object.defineProperty(props.page, property, {
628
+ get: () => {
629
+ throw new Error(`$page.${property} has been replaced by $page.url.${replacement}`);
630
+ }
631
+ });
632
+ };
633
+
634
+ print_error('origin', 'origin');
635
+ print_error('path', 'pathname');
636
+ print_error('query', 'searchParams');
637
+
638
+ // props_n (instead of props[n]) makes it easy to avoid
639
+ // unnecessary updates for layout components
640
+ for (let i = 0; i < branch.length; i += 1) {
641
+ props[`props_${i}`] = await branch[i].loaded.props;
642
+ }
643
+
644
+ let session_tracking_active = false;
645
+ const unsubscribe = session.subscribe(() => {
646
+ if (session_tracking_active) is_private = true;
647
+ });
648
+ session_tracking_active = true;
649
+
650
+ try {
651
+ rendered = options.root.render(props);
652
+ } finally {
653
+ unsubscribe();
654
+ }
655
+ } else {
656
+ rendered = { head: '', html: '', css: { code: '', map: null } };
657
+ }
658
+
659
+ let { head, html: body } = rendered;
660
+
661
+ const inlined_style = Array.from(styles.values()).join('\n');
662
+
663
+ if (options.amp) {
664
+ head += `
665
+ <style amp-boilerplate>body{-webkit-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-moz-animation:-amp-start 8s steps(1,end) 0s 1 normal both;-ms-animation:-amp-start 8s steps(1,end) 0s 1 normal both;animation:-amp-start 8s steps(1,end) 0s 1 normal both}@-webkit-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-moz-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-ms-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@-o-keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}@keyframes -amp-start{from{visibility:hidden}to{visibility:visible}}</style>
666
+ <noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
667
+ <script async src="https://cdn.ampproject.org/v0.js"></script>
668
+
669
+ <style amp-custom>${inlined_style}\n${rendered.css.code}</style>`;
670
+
671
+ if (options.service_worker) {
672
+ head +=
673
+ '<script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>';
674
+
675
+ body += `<amp-install-serviceworker src="${options.service_worker}" layout="nodisplay"></amp-install-serviceworker>`;
676
+ }
677
+ } else {
678
+ if (inlined_style) {
679
+ head += `\n\t<style${options.dev ? ' data-svelte' : ''}>${inlined_style}</style>`;
680
+ }
681
+ // prettier-ignore
682
+ head += Array.from(css)
683
+ .map((dep) => `\n\t<link${styles.has(dep) ? ' disabled media="(max-width: 0)"' : ''} rel="stylesheet" href="${options.prefix + dep}">`)
684
+ .join('');
685
+
686
+ if (page_config.router || page_config.hydrate) {
687
+ head += Array.from(js)
688
+ .map((dep) => `\n\t<link rel="modulepreload" href="${options.prefix + dep}">`)
689
+ .join('');
690
+ // prettier-ignore
691
+ head += `
692
+ <script type="module">
693
+ import { start } from ${s(options.prefix + options.manifest._.entry.file)};
694
+ start({
695
+ target: ${options.target ? `document.querySelector(${s(options.target)})` : 'document.body'},
696
+ paths: ${s(options.paths)},
697
+ session: ${try_serialize($session, (error) => {
698
+ throw new Error(`Failed to serialize session data: ${error.message}`);
699
+ })},
700
+ route: ${!!page_config.router},
701
+ spa: ${!ssr},
702
+ trailing_slash: ${s(options.trailing_slash)},
703
+ hydrate: ${ssr && page_config.hydrate ? `{
704
+ status: ${status},
705
+ error: ${serialize_error(error)},
706
+ nodes: [
707
+ ${(branch || [])
708
+ .map(({ node }) => `import(${s(options.prefix + node.entry)})`)
709
+ .join(',\n\t\t\t\t\t\t')}
710
+ ],
711
+ url: new URL(${s(url.href)}),
712
+ params: ${devalue(params)}
713
+ }` : 'null'}
714
+ });
715
+ </script>${options.service_worker ? `
716
+ <script>
717
+ if ('serviceWorker' in navigator) {
718
+ navigator.serviceWorker.register('${options.service_worker}');
719
+ }
720
+ </script>` : ''}`;
721
+
722
+ body += serialized_data
723
+ .map(({ url, body, json }) => {
724
+ let attributes = `type="application/json" data-type="svelte-data" data-url=${escape_html_attr(
725
+ url
726
+ )}`;
727
+ if (body) attributes += ` data-body="${hash(body)}"`;
728
+
729
+ return `<script ${attributes}>${json}</script>`;
730
+ })
731
+ .join('\n\n\t');
732
+ }
733
+ }
734
+
735
+ /** @type {import('types/helper').ResponseHeaders} */
736
+ const headers = {
737
+ 'content-type': 'text/html'
738
+ };
739
+
740
+ if (maxage) {
741
+ headers['cache-control'] = `${is_private ? 'private' : 'public'}, max-age=${maxage}`;
742
+ }
743
+
744
+ if (!options.floc) {
745
+ headers['permissions-policy'] = 'interest-cohort=()';
746
+ }
747
+
748
+ const segments = url.pathname.slice(options.paths.base.length).split('/').slice(2);
749
+ const assets =
750
+ options.paths.assets || (segments.length > 0 ? segments.map(() => '..').join('/') : '.');
751
+
752
+ return {
753
+ status,
754
+ headers,
755
+ body: options.template({
756
+ head,
757
+ body,
758
+ assets
759
+ })
760
+ };
761
+ }
762
+
763
+ /**
764
+ * @param {any} data
765
+ * @param {(error: Error) => void} [fail]
766
+ */
767
+ function try_serialize(data, fail) {
768
+ try {
769
+ return devalue(data);
770
+ } catch (err) {
771
+ if (fail) fail(coalesce_to_error(err));
772
+ return null;
773
+ }
774
+ }
775
+
776
+ // Ensure we return something truthy so the client will not re-render the page over the error
777
+
778
+ /** @param {(Error & {frame?: string} & {loc?: object}) | undefined | null} error */
779
+ function serialize_error(error) {
780
+ if (!error) return null;
781
+ let serialized = try_serialize(error);
782
+ if (!serialized) {
783
+ const { name, message, stack } = error;
784
+ serialized = try_serialize({ ...error, name, message, stack });
785
+ }
786
+ if (!serialized) {
787
+ serialized = '{}';
788
+ }
789
+ return serialized;
790
+ }
791
+
792
+ /**
793
+ * @param {import('types/page').LoadOutput} loaded
794
+ * @returns {import('types/internal').NormalizedLoadOutput}
795
+ */
796
+ function normalize(loaded) {
797
+ const has_error_status =
798
+ loaded.status && loaded.status >= 400 && loaded.status <= 599 && !loaded.redirect;
799
+ if (loaded.error || has_error_status) {
800
+ const status = loaded.status;
801
+
802
+ if (!loaded.error && has_error_status) {
803
+ return {
804
+ status: status || 500,
805
+ error: new Error()
806
+ };
807
+ }
808
+
809
+ const error = typeof loaded.error === 'string' ? new Error(loaded.error) : loaded.error;
810
+
811
+ if (!(error instanceof Error)) {
812
+ return {
813
+ status: 500,
814
+ error: new Error(
815
+ `"error" property returned from load() must be a string or instance of Error, received type "${typeof error}"`
816
+ )
817
+ };
818
+ }
819
+
820
+ if (!status || status < 400 || status > 599) {
821
+ console.warn('"error" returned from load() without a valid status code — defaulting to 500');
822
+ return { status: 500, error };
823
+ }
824
+
825
+ return { status, error };
826
+ }
827
+
828
+ if (loaded.redirect) {
829
+ if (!loaded.status || Math.floor(loaded.status / 100) !== 3) {
830
+ return {
831
+ status: 500,
832
+ error: new Error(
833
+ '"redirect" property returned from load() must be accompanied by a 3xx status code'
834
+ )
835
+ };
836
+ }
837
+
838
+ if (typeof loaded.redirect !== 'string') {
839
+ return {
840
+ status: 500,
841
+ error: new Error('"redirect" property returned from load() must be a string')
842
+ };
843
+ }
844
+ }
845
+
846
+ // TODO remove before 1.0
847
+ if (/** @type {any} */ (loaded).context) {
848
+ throw new Error(
849
+ 'You are returning "context" from a load function. ' +
850
+ '"context" was renamed to "stuff", please adjust your code accordingly.'
851
+ );
852
+ }
853
+
854
+ return /** @type {import('types/internal').NormalizedLoadOutput} */ (loaded);
855
+ }
856
+
857
+ /**
858
+ * @param {{
859
+ * request: import('types/hooks').ServerRequest;
860
+ * options: import('types/internal').SSRRenderOptions;
861
+ * state: import('types/internal').SSRRenderState;
862
+ * route: import('types/internal').SSRPage | null;
863
+ * url: URL;
864
+ * params: Record<string, string>;
865
+ * node: import('types/internal').SSRNode;
866
+ * $session: any;
867
+ * stuff: Record<string, any>;
868
+ * is_error: boolean;
869
+ * status?: number;
870
+ * error?: Error;
871
+ * }} opts
872
+ * @returns {Promise<import('./types').Loaded | undefined>} undefined for fallthrough
873
+ */
874
+ async function load_node({
875
+ request,
876
+ options,
877
+ state,
878
+ route,
879
+ url,
880
+ params,
881
+ node,
882
+ $session,
883
+ stuff,
884
+ is_error,
885
+ status,
886
+ error
887
+ }) {
888
+ const { module } = node;
889
+
890
+ let uses_credentials = false;
891
+
892
+ /**
893
+ * @type {Array<{
894
+ * url: string;
895
+ * body: string;
896
+ * json: string;
897
+ * }>}
898
+ */
899
+ const fetched = [];
900
+
901
+ /**
902
+ * @type {string[]}
903
+ */
904
+ let set_cookie_headers = [];
905
+
906
+ let loaded;
907
+
908
+ if (module.load) {
909
+ /** @type {import('types/page').LoadInput | import('types/page').ErrorLoadInput} */
910
+ const load_input = {
911
+ url: state.prerender ? create_prerendering_url_proxy(url) : url,
912
+ params,
913
+ get session() {
914
+ uses_credentials = true;
915
+ return $session;
916
+ },
917
+ /**
918
+ * @param {RequestInfo} resource
919
+ * @param {RequestInit} opts
920
+ */
921
+ fetch: async (resource, opts = {}) => {
922
+ /** @type {string} */
923
+ let requested;
924
+
925
+ if (typeof resource === 'string') {
926
+ requested = resource;
927
+ } else {
928
+ requested = resource.url;
929
+
930
+ opts = {
931
+ method: resource.method,
932
+ headers: resource.headers,
933
+ body: resource.body,
934
+ mode: resource.mode,
935
+ credentials: resource.credentials,
936
+ cache: resource.cache,
937
+ redirect: resource.redirect,
938
+ referrer: resource.referrer,
939
+ integrity: resource.integrity,
940
+ ...opts
941
+ };
942
+ }
943
+
944
+ opts.headers = new Headers(opts.headers);
945
+
946
+ const resolved = resolve(request.url.pathname, requested.split('?')[0]);
947
+
948
+ let response;
949
+
950
+ // handle fetch requests for static assets. e.g. prebaked data, etc.
951
+ // we need to support everything the browser's fetch supports
952
+ const prefix = options.paths.assets || options.paths.base;
953
+ const filename = (
954
+ resolved.startsWith(prefix) ? resolved.slice(prefix.length) : resolved
955
+ ).slice(1);
956
+ const filename_html = `${filename}/index.html`; // path may also match path/index.html
957
+
958
+ const is_asset = options.manifest.assets.has(filename);
959
+ const is_asset_html = options.manifest.assets.has(filename_html);
960
+
961
+ if (is_asset || is_asset_html) {
962
+ const file = is_asset ? filename : filename_html;
963
+
964
+ if (options.read) {
965
+ const type = is_asset
966
+ ? options.manifest._.mime[filename.slice(filename.lastIndexOf('.'))]
967
+ : 'text/html';
968
+
969
+ response = new Response(options.read(file), {
970
+ headers: type ? { 'content-type': type } : {}
971
+ });
972
+ } else {
973
+ response = await fetch(`${url.origin}/${file}`, /** @type {RequestInit} */ (opts));
974
+ }
975
+ } else if (is_root_relative(resolved)) {
976
+ const relative = resolved;
977
+
978
+ // TODO: fix type https://github.com/node-fetch/node-fetch/issues/1113
979
+ if (opts.credentials !== 'omit') {
980
+ uses_credentials = true;
981
+
982
+ if (request.headers.cookie) {
983
+ opts.headers.set('cookie', request.headers.cookie);
984
+ }
985
+
986
+ if (request.headers.authorization && !opts.headers.has('authorization')) {
987
+ opts.headers.set('authorization', request.headers.authorization);
988
+ }
989
+ }
990
+
991
+ if (opts.body && typeof opts.body !== 'string') {
992
+ // per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a
993
+ // Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object.
994
+ // non-string bodies are irksome to deal with, but luckily aren't particularly useful
995
+ // in this context anyway, so we take the easy route and ban them
996
+ throw new Error('Request body must be a string');
997
+ }
998
+
999
+ const rendered = await respond(
1000
+ {
1001
+ url: new URL(requested, request.url),
1002
+ method: opts.method || 'GET',
1003
+ headers: Object.fromEntries(opts.headers),
1004
+ rawBody: opts.body == null ? null : new TextEncoder().encode(opts.body)
1005
+ },
1006
+ options,
1007
+ {
1008
+ fetched: requested,
1009
+ initiator: route
1010
+ }
1011
+ );
1012
+
1013
+ if (rendered) {
1014
+ if (state.prerender) {
1015
+ state.prerender.dependencies.set(relative, rendered);
1016
+ }
1017
+
1018
+ // Set-Cookie must be filtered out (done below) and that's the only header value that
1019
+ // can be an array so we know we have only simple values
1020
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie
1021
+ response = new Response(rendered.body, {
1022
+ status: rendered.status,
1023
+ headers: /** @type {Record<string, string>} */ (rendered.headers)
1024
+ });
1025
+ } else {
1026
+ // we can't load the endpoint from our own manifest,
1027
+ // so we need to make an actual HTTP request
1028
+ return fetch(new URL(requested, request.url).href, {
1029
+ method: opts.method || 'GET',
1030
+ headers: opts.headers
1031
+ });
1032
+ }
1033
+ } else {
1034
+ // external
1035
+ if (resolved.startsWith('//')) {
1036
+ throw new Error(
1037
+ `Cannot request protocol-relative URL (${requested}) in server-side fetch`
1038
+ );
1039
+ }
1040
+
1041
+ // external fetch
1042
+ // allow cookie passthrough for "same-origin"
1043
+ // if SvelteKit is serving my.domain.com:
1044
+ // - domain.com WILL NOT receive cookies
1045
+ // - my.domain.com WILL receive cookies
1046
+ // - api.domain.dom WILL NOT receive cookies
1047
+ // - sub.my.domain.com WILL receive cookies
1048
+ // ports do not affect the resolution
1049
+ // leading dot prevents mydomain.com matching domain.com
1050
+ if (
1051
+ `.${new URL(requested).hostname}`.endsWith(`.${request.url.hostname}`) &&
1052
+ opts.credentials !== 'omit'
1053
+ ) {
1054
+ uses_credentials = true;
1055
+ opts.headers.set('cookie', request.headers.cookie);
1056
+ }
1057
+
1058
+ const external_request = new Request(requested, /** @type {RequestInit} */ (opts));
1059
+ response = await options.hooks.externalFetch.call(null, external_request);
1060
+ }
1061
+
1062
+ if (response) {
1063
+ const proxy = new Proxy(response, {
1064
+ get(response, key, _receiver) {
1065
+ async function text() {
1066
+ const body = await response.text();
1067
+
1068
+ /** @type {import('types/helper').ResponseHeaders} */
1069
+ const headers = {};
1070
+ for (const [key, value] of response.headers) {
1071
+ if (key === 'set-cookie') {
1072
+ set_cookie_headers = set_cookie_headers.concat(value);
1073
+ } else if (key !== 'etag') {
1074
+ headers[key] = value;
1075
+ }
1076
+ }
1077
+
1078
+ if (!opts.body || typeof opts.body === 'string') {
1079
+ // prettier-ignore
1080
+ fetched.push({
1081
+ url: requested,
1082
+ body: /** @type {string} */ (opts.body),
1083
+ json: `{"status":${response.status},"statusText":${s(response.statusText)},"headers":${s(headers)},"body":"${escape_json_string_in_html(body)}"}`
1084
+ });
1085
+ }
1086
+
1087
+ return body;
1088
+ }
1089
+
1090
+ if (key === 'text') {
1091
+ return text;
1092
+ }
1093
+
1094
+ if (key === 'json') {
1095
+ return async () => {
1096
+ return JSON.parse(await text());
1097
+ };
1098
+ }
1099
+
1100
+ // TODO arrayBuffer?
1101
+
1102
+ return Reflect.get(response, key, response);
1103
+ }
1104
+ });
1105
+
1106
+ return proxy;
1107
+ }
1108
+
1109
+ return (
1110
+ response ||
1111
+ new Response('Not found', {
1112
+ status: 404
1113
+ })
1114
+ );
1115
+ },
1116
+ stuff: { ...stuff }
1117
+ };
1118
+
1119
+ if (options.dev) {
1120
+ // TODO remove this for 1.0
1121
+ Object.defineProperty(load_input, 'page', {
1122
+ get: () => {
1123
+ throw new Error('`page` in `load` functions has been replaced by `url` and `params`');
1124
+ }
1125
+ });
1126
+ }
1127
+
1128
+ if (is_error) {
1129
+ /** @type {import('types/page').ErrorLoadInput} */ (load_input).status = status;
1130
+ /** @type {import('types/page').ErrorLoadInput} */ (load_input).error = error;
1131
+ }
1132
+
1133
+ loaded = await module.load.call(null, load_input);
1134
+
1135
+ if (!loaded) {
1136
+ throw new Error(`load function must return a value${options.dev ? ` (${node.entry})` : ''}`);
1137
+ }
1138
+ } else {
1139
+ loaded = {};
1140
+ }
1141
+
1142
+ if (loaded.fallthrough && !is_error) {
1143
+ return;
1144
+ }
1145
+
1146
+ return {
1147
+ node,
1148
+ loaded: normalize(loaded),
1149
+ stuff: loaded.stuff || stuff,
1150
+ fetched,
1151
+ set_cookie_headers,
1152
+ uses_credentials
1153
+ };
1154
+ }
1155
+
1156
+ /**
1157
+ * @typedef {import('./types.js').Loaded} Loaded
1158
+ * @typedef {import('types/internal').SSRRenderOptions} SSRRenderOptions
1159
+ * @typedef {import('types/internal').SSRRenderState} SSRRenderState
1160
+ */
1161
+
1162
+ /**
1163
+ * @param {{
1164
+ * request: import('types/hooks').ServerRequest;
1165
+ * options: SSRRenderOptions;
1166
+ * state: SSRRenderState;
1167
+ * $session: any;
1168
+ * status: number;
1169
+ * error: Error;
1170
+ * ssr: boolean;
1171
+ * }} opts
1172
+ */
1173
+ async function respond_with_error({
1174
+ request,
1175
+ options,
1176
+ state,
1177
+ $session,
1178
+ status,
1179
+ error,
1180
+ ssr
1181
+ }) {
1182
+ try {
1183
+ const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout
1184
+ const default_error = await options.manifest._.nodes[1](); // 1 is always the root error
1185
+
1186
+ /** @type {Record<string, string>} */
1187
+ const params = {}; // error page has no params
1188
+
1189
+ const layout_loaded = /** @type {Loaded} */ (
1190
+ await load_node({
1191
+ request,
1192
+ options,
1193
+ state,
1194
+ route: null,
1195
+ url: request.url, // TODO this is redundant, no?
1196
+ params,
1197
+ node: default_layout,
1198
+ $session,
1199
+ stuff: {},
1200
+ is_error: false
1201
+ })
1202
+ );
1203
+
1204
+ const error_loaded = /** @type {Loaded} */ (
1205
+ await load_node({
1206
+ request,
1207
+ options,
1208
+ state,
1209
+ route: null,
1210
+ url: request.url,
1211
+ params,
1212
+ node: default_error,
1213
+ $session,
1214
+ stuff: layout_loaded ? layout_loaded.stuff : {},
1215
+ is_error: true,
1216
+ status,
1217
+ error
1218
+ })
1219
+ );
1220
+
1221
+ return await render_response({
1222
+ options,
1223
+ state,
1224
+ $session,
1225
+ page_config: {
1226
+ hydrate: options.hydrate,
1227
+ router: options.router
1228
+ },
1229
+ stuff: error_loaded.stuff,
1230
+ status,
1231
+ error,
1232
+ branch: [layout_loaded, error_loaded],
1233
+ url: request.url,
1234
+ params,
1235
+ ssr
1236
+ });
1237
+ } catch (err) {
1238
+ const error = coalesce_to_error(err);
1239
+
1240
+ options.handle_error(error, request);
1241
+
1242
+ return {
1243
+ status: 500,
1244
+ headers: {},
1245
+ body: error.stack
1246
+ };
1247
+ }
1248
+ }
1249
+
1250
+ /**
1251
+ * @typedef {import('./types.js').Loaded} Loaded
1252
+ * @typedef {import('types/hooks').ServerResponse} ServerResponse
1253
+ * @typedef {import('types/internal').SSRNode} SSRNode
1254
+ * @typedef {import('types/internal').SSRRenderOptions} SSRRenderOptions
1255
+ * @typedef {import('types/internal').SSRRenderState} SSRRenderState
1256
+ */
1257
+
1258
+ /**
1259
+ * @param {{
1260
+ * request: import('types/hooks').ServerRequest;
1261
+ * options: SSRRenderOptions;
1262
+ * state: SSRRenderState;
1263
+ * $session: any;
1264
+ * route: import('types/internal').SSRPage;
1265
+ * params: Record<string, string>;
1266
+ * ssr: boolean;
1267
+ * }} opts
1268
+ * @returns {Promise<ServerResponse | undefined>}
1269
+ */
1270
+ async function respond$1(opts) {
1271
+ const { request, options, state, $session, route, ssr } = opts;
1272
+
1273
+ /** @type {Array<SSRNode | undefined>} */
1274
+ let nodes;
1275
+
1276
+ if (!ssr) {
1277
+ return await render_response({
1278
+ ...opts,
1279
+ branch: [],
1280
+ page_config: {
1281
+ hydrate: true,
1282
+ router: true
1283
+ },
1284
+ status: 200,
1285
+ url: request.url,
1286
+ stuff: {}
1287
+ });
1288
+ }
1289
+
1290
+ try {
1291
+ nodes = await Promise.all(
1292
+ route.a.map((n) => options.manifest._.nodes[n] && options.manifest._.nodes[n]())
1293
+ );
1294
+ } catch (err) {
1295
+ const error = coalesce_to_error(err);
1296
+
1297
+ options.handle_error(error, request);
1298
+
1299
+ return await respond_with_error({
1300
+ request,
1301
+ options,
1302
+ state,
1303
+ $session,
1304
+ status: 500,
1305
+ error,
1306
+ ssr
1307
+ });
1308
+ }
1309
+
1310
+ // the leaf node will be present. only layouts may be undefined
1311
+ const leaf = /** @type {SSRNode} */ (nodes[nodes.length - 1]).module;
1312
+
1313
+ let page_config = get_page_config(leaf, options);
1314
+
1315
+ if (!leaf.prerender && state.prerender && !state.prerender.all) {
1316
+ // if the page has `export const prerender = true`, continue,
1317
+ // otherwise bail out at this point
1318
+ return {
1319
+ status: 204,
1320
+ headers: {}
1321
+ };
1322
+ }
1323
+
1324
+ /** @type {Array<Loaded>} */
1325
+ let branch = [];
1326
+
1327
+ /** @type {number} */
1328
+ let status = 200;
1329
+
1330
+ /** @type {Error|undefined} */
1331
+ let error;
1332
+
1333
+ /** @type {string[]} */
1334
+ let set_cookie_headers = [];
1335
+
1336
+ let stuff = {};
1337
+
1338
+ ssr: if (ssr) {
1339
+ for (let i = 0; i < nodes.length; i += 1) {
1340
+ const node = nodes[i];
1341
+
1342
+ /** @type {Loaded | undefined} */
1343
+ let loaded;
1344
+
1345
+ if (node) {
1346
+ try {
1347
+ loaded = await load_node({
1348
+ ...opts,
1349
+ url: request.url,
1350
+ node,
1351
+ stuff,
1352
+ is_error: false
1353
+ });
1354
+
1355
+ if (!loaded) return;
1356
+
1357
+ set_cookie_headers = set_cookie_headers.concat(loaded.set_cookie_headers);
1358
+
1359
+ if (loaded.loaded.redirect) {
1360
+ return with_cookies(
1361
+ {
1362
+ status: loaded.loaded.status,
1363
+ headers: {
1364
+ location: encodeURI(loaded.loaded.redirect)
1365
+ }
1366
+ },
1367
+ set_cookie_headers
1368
+ );
1369
+ }
1370
+
1371
+ if (loaded.loaded.error) {
1372
+ ({ status, error } = loaded.loaded);
1373
+ }
1374
+ } catch (err) {
1375
+ const e = coalesce_to_error(err);
1376
+
1377
+ options.handle_error(e, request);
1378
+
1379
+ status = 500;
1380
+ error = e;
1381
+ }
1382
+
1383
+ if (loaded && !error) {
1384
+ branch.push(loaded);
1385
+ }
1386
+
1387
+ if (error) {
1388
+ while (i--) {
1389
+ if (route.b[i]) {
1390
+ const error_node = await options.manifest._.nodes[route.b[i]]();
1391
+
1392
+ /** @type {Loaded} */
1393
+ let node_loaded;
1394
+ let j = i;
1395
+ while (!(node_loaded = branch[j])) {
1396
+ j -= 1;
1397
+ }
1398
+
1399
+ try {
1400
+ const error_loaded = /** @type {import('./types').Loaded} */ (
1401
+ await load_node({
1402
+ ...opts,
1403
+ url: request.url,
1404
+ node: error_node,
1405
+ stuff: node_loaded.stuff,
1406
+ is_error: true,
1407
+ status,
1408
+ error
1409
+ })
1410
+ );
1411
+
1412
+ if (error_loaded.loaded.error) {
1413
+ continue;
1414
+ }
1415
+
1416
+ page_config = get_page_config(error_node.module, options);
1417
+ branch = branch.slice(0, j + 1).concat(error_loaded);
1418
+ stuff = { ...node_loaded.stuff, ...error_loaded.stuff };
1419
+ break ssr;
1420
+ } catch (err) {
1421
+ const e = coalesce_to_error(err);
1422
+
1423
+ options.handle_error(e, request);
1424
+
1425
+ continue;
1426
+ }
1427
+ }
1428
+ }
1429
+
1430
+ // TODO backtrack until we find an __error.svelte component
1431
+ // that we can use as the leaf node
1432
+ // for now just return regular error page
1433
+ return with_cookies(
1434
+ await respond_with_error({
1435
+ request,
1436
+ options,
1437
+ state,
1438
+ $session,
1439
+ status,
1440
+ error,
1441
+ ssr
1442
+ }),
1443
+ set_cookie_headers
1444
+ );
1445
+ }
1446
+ }
1447
+
1448
+ if (loaded && loaded.loaded.stuff) {
1449
+ stuff = {
1450
+ ...stuff,
1451
+ ...loaded.loaded.stuff
1452
+ };
1453
+ }
1454
+ }
1455
+ }
1456
+
1457
+ try {
1458
+ return with_cookies(
1459
+ await render_response({
1460
+ ...opts,
1461
+ stuff,
1462
+ url: request.url,
1463
+ page_config,
1464
+ status,
1465
+ error,
1466
+ branch: branch.filter(Boolean)
1467
+ }),
1468
+ set_cookie_headers
1469
+ );
1470
+ } catch (err) {
1471
+ const error = coalesce_to_error(err);
1472
+
1473
+ options.handle_error(error, request);
1474
+
1475
+ return with_cookies(
1476
+ await respond_with_error({
1477
+ ...opts,
1478
+ status: 500,
1479
+ error
1480
+ }),
1481
+ set_cookie_headers
1482
+ );
1483
+ }
1484
+ }
1485
+
1486
+ /**
1487
+ * @param {import('types/internal').SSRComponent} leaf
1488
+ * @param {SSRRenderOptions} options
1489
+ */
1490
+ function get_page_config(leaf, options) {
1491
+ // TODO remove for 1.0
1492
+ if ('ssr' in leaf) {
1493
+ throw new Error(
1494
+ '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs#hooks-handle'
1495
+ );
1496
+ }
1497
+
1498
+ return {
1499
+ router: 'router' in leaf ? !!leaf.router : options.router,
1500
+ hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate
1501
+ };
1502
+ }
1503
+
1504
+ /**
1505
+ * @param {ServerResponse} response
1506
+ * @param {string[]} set_cookie_headers
1507
+ */
1508
+ function with_cookies(response, set_cookie_headers) {
1509
+ if (set_cookie_headers.length) {
1510
+ response.headers['set-cookie'] = set_cookie_headers;
1511
+ }
1512
+ return response;
1513
+ }
1514
+
1515
+ /**
1516
+ * @param {import('types/hooks').ServerRequest} request
1517
+ * @param {import('types/internal').SSRPage} route
1518
+ * @param {RegExpExecArray} match
1519
+ * @param {import('types/internal').SSRRenderOptions} options
1520
+ * @param {import('types/internal').SSRRenderState} state
1521
+ * @param {boolean} ssr
1522
+ * @returns {Promise<import('types/hooks').ServerResponse | undefined>}
1523
+ */
1524
+ async function render_page(request, route, match, options, state, ssr) {
1525
+ if (state.initiator === route) {
1526
+ // infinite request cycle detected
1527
+ return {
1528
+ status: 404,
1529
+ headers: {},
1530
+ body: `Not found: ${request.url.pathname}`
1531
+ };
1532
+ }
1533
+
1534
+ const params = route.params ? decode_params(route.params(match)) : {};
1535
+
1536
+ const $session = await options.hooks.getSession(request);
1537
+
1538
+ const response = await respond$1({
1539
+ request,
1540
+ options,
1541
+ state,
1542
+ $session,
1543
+ route,
1544
+ params,
1545
+ ssr
1546
+ });
1547
+
1548
+ if (response) {
1549
+ return response;
1550
+ }
1551
+
1552
+ if (state.fetched) {
1553
+ // we came here because of a bad request in a `load` function.
1554
+ // rather than render the error page — which could lead to an
1555
+ // infinite loop, if the `load` belonged to the root layout,
1556
+ // we respond with a bare-bones 500
1557
+ return {
1558
+ status: 500,
1559
+ headers: {},
1560
+ body: `Bad request in load function: failed to fetch ${state.fetched}`
1561
+ };
1562
+ }
1563
+ }
1564
+
1565
+ function read_only_form_data() {
1566
+ /** @type {Map<string, string[]>} */
1567
+ const map = new Map();
1568
+
1569
+ return {
1570
+ /**
1571
+ * @param {string} key
1572
+ * @param {string} value
1573
+ */
1574
+ append(key, value) {
1575
+ const existing_values = map.get(key);
1576
+ if (existing_values) {
1577
+ existing_values.push(value);
1578
+ } else {
1579
+ map.set(key, [value]);
1580
+ }
1581
+ },
1582
+
1583
+ data: new ReadOnlyFormData(map)
1584
+ };
1585
+ }
1586
+
1587
+ class ReadOnlyFormData {
1588
+ /** @type {Map<string, string[]>} */
1589
+ #map;
1590
+
1591
+ /** @param {Map<string, string[]>} map */
1592
+ constructor(map) {
1593
+ this.#map = map;
1594
+ }
1595
+
1596
+ /** @param {string} key */
1597
+ get(key) {
1598
+ const value = this.#map.get(key);
1599
+ if (!value) {
1600
+ return null;
1601
+ }
1602
+ return value[0];
1603
+ }
1604
+
1605
+ /** @param {string} key */
1606
+ getAll(key) {
1607
+ return this.#map.get(key) || [];
1608
+ }
1609
+
1610
+ /** @param {string} key */
1611
+ has(key) {
1612
+ return this.#map.has(key);
1613
+ }
1614
+
1615
+ *[Symbol.iterator]() {
1616
+ for (const [key, value] of this.#map) {
1617
+ for (let i = 0; i < value.length; i += 1) {
1618
+ yield [key, value[i]];
1619
+ }
1620
+ }
1621
+ }
1622
+
1623
+ *entries() {
1624
+ for (const [key, value] of this.#map) {
1625
+ for (let i = 0; i < value.length; i += 1) {
1626
+ yield [key, value[i]];
1627
+ }
1628
+ }
1629
+ }
1630
+
1631
+ *keys() {
1632
+ for (const [key] of this.#map) yield key;
1633
+ }
1634
+
1635
+ *values() {
1636
+ for (const [, value] of this.#map) {
1637
+ for (let i = 0; i < value.length; i += 1) {
1638
+ yield value[i];
1639
+ }
1640
+ }
1641
+ }
1642
+ }
1643
+
1644
+ /**
1645
+ * @param {import('types/app').RawBody} raw
1646
+ * @param {import('types/helper').RequestHeaders} headers
1647
+ */
1648
+ function parse_body(raw, headers) {
1649
+ if (!raw) return raw;
1650
+
1651
+ const content_type = headers['content-type'];
1652
+ const [type, ...directives] = content_type ? content_type.split(/;\s*/) : [];
1653
+
1654
+ const text = () => new TextDecoder(headers['content-encoding'] || 'utf-8').decode(raw);
1655
+
1656
+ switch (type) {
1657
+ case 'text/plain':
1658
+ return text();
1659
+
1660
+ case 'application/json':
1661
+ return JSON.parse(text());
1662
+
1663
+ case 'application/x-www-form-urlencoded':
1664
+ return get_urlencoded(text());
1665
+
1666
+ case 'multipart/form-data': {
1667
+ const boundary = directives.find((directive) => directive.startsWith('boundary='));
1668
+ if (!boundary) throw new Error('Missing boundary');
1669
+ return get_multipart(text(), boundary.slice('boundary='.length));
1670
+ }
1671
+ default:
1672
+ return raw;
1673
+ }
1674
+ }
1675
+
1676
+ /** @param {string} text */
1677
+ function get_urlencoded(text) {
1678
+ const { data, append } = read_only_form_data();
1679
+
1680
+ text
1681
+ .replace(/\+/g, ' ')
1682
+ .split('&')
1683
+ .forEach((str) => {
1684
+ const [key, value] = str.split('=');
1685
+ append(decodeURIComponent(key), decodeURIComponent(value));
1686
+ });
1687
+
1688
+ return data;
1689
+ }
1690
+
1691
+ /**
1692
+ * @param {string} text
1693
+ * @param {string} boundary
1694
+ */
1695
+ function get_multipart(text, boundary) {
1696
+ const parts = text.split(`--${boundary}`);
1697
+
1698
+ if (parts[0] !== '' || parts[parts.length - 1].trim() !== '--') {
1699
+ throw new Error('Malformed form data');
1700
+ }
1701
+
1702
+ const { data, append } = read_only_form_data();
1703
+
1704
+ parts.slice(1, -1).forEach((part) => {
1705
+ const match = /\s*([\s\S]+?)\r\n\r\n([\s\S]*)\s*/.exec(part);
1706
+ if (!match) {
1707
+ throw new Error('Malformed form data');
1708
+ }
1709
+ const raw_headers = match[1];
1710
+ const body = match[2].trim();
1711
+
1712
+ let key;
1713
+
1714
+ /** @type {Record<string, string>} */
1715
+ const headers = {};
1716
+ raw_headers.split('\r\n').forEach((str) => {
1717
+ const [raw_header, ...raw_directives] = str.split('; ');
1718
+ let [name, value] = raw_header.split(': ');
1719
+
1720
+ name = name.toLowerCase();
1721
+ headers[name] = value;
1722
+
1723
+ /** @type {Record<string, string>} */
1724
+ const directives = {};
1725
+ raw_directives.forEach((raw_directive) => {
1726
+ const [name, value] = raw_directive.split('=');
1727
+ directives[name] = JSON.parse(value); // TODO is this right?
1728
+ });
1729
+
1730
+ if (name === 'content-disposition') {
1731
+ if (value !== 'form-data') throw new Error('Malformed form data');
1732
+
1733
+ if (directives.filename) {
1734
+ // TODO we probably don't want to do this automatically
1735
+ throw new Error('File upload is not yet implemented');
1736
+ }
1737
+
1738
+ if (directives.name) {
1739
+ key = directives.name;
1740
+ }
1741
+ }
1742
+ });
1743
+
1744
+ if (!key) throw new Error('Malformed form data');
1745
+
1746
+ append(key, body);
1747
+ });
1748
+
1749
+ return data;
1750
+ }
1751
+
1752
+ /** @type {import('@sveltejs/kit/ssr').Respond} */
1753
+ async function respond(incoming, options, state = {}) {
1754
+ if (incoming.url.pathname !== '/' && options.trailing_slash !== 'ignore') {
1755
+ const has_trailing_slash = incoming.url.pathname.endsWith('/');
1756
+
1757
+ if (
1758
+ (has_trailing_slash && options.trailing_slash === 'never') ||
1759
+ (!has_trailing_slash &&
1760
+ options.trailing_slash === 'always' &&
1761
+ !(incoming.url.pathname.split('/').pop() || '').includes('.'))
1762
+ ) {
1763
+ incoming.url.pathname = has_trailing_slash
1764
+ ? incoming.url.pathname.slice(0, -1)
1765
+ : incoming.url.pathname + '/';
1766
+
1767
+ if (incoming.url.search === '?') incoming.url.search = '';
1768
+
1769
+ return {
1770
+ status: 301,
1771
+ headers: {
1772
+ location: incoming.url.pathname + incoming.url.search
1773
+ }
1774
+ };
1775
+ }
1776
+ }
1777
+
1778
+ const headers = lowercase_keys(incoming.headers);
1779
+ const request = {
1780
+ ...incoming,
1781
+ headers,
1782
+ body: parse_body(incoming.rawBody, headers),
1783
+ params: {},
1784
+ locals: {}
1785
+ };
1786
+
1787
+ const { parameter, allowed } = options.method_override;
1788
+ const method_override = incoming.url.searchParams.get(parameter)?.toUpperCase();
1789
+
1790
+ if (method_override) {
1791
+ if (request.method.toUpperCase() === 'POST') {
1792
+ if (allowed.includes(method_override)) {
1793
+ request.method = method_override;
1794
+ } else {
1795
+ const verb = allowed.length === 0 ? 'enabled' : 'allowed';
1796
+ const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs#configuration-methodoverride`;
1797
+
1798
+ return {
1799
+ status: 400,
1800
+ headers: {},
1801
+ body
1802
+ };
1803
+ }
1804
+ } else {
1805
+ throw new Error(`${parameter}=${method_override} is only allowed with POST requests`);
1806
+ }
1807
+ }
1808
+
1809
+ // TODO remove this for 1.0
1810
+ /**
1811
+ * @param {string} property
1812
+ * @param {string} replacement
1813
+ */
1814
+ const print_error = (property, replacement) => {
1815
+ Object.defineProperty(request, property, {
1816
+ get: () => {
1817
+ throw new Error(`request.${property} has been replaced by request.url.${replacement}`);
1818
+ }
1819
+ });
1820
+ };
1821
+
1822
+ print_error('origin', 'origin');
1823
+ print_error('path', 'pathname');
1824
+ print_error('query', 'searchParams');
1825
+
1826
+ let ssr = true;
1827
+
1828
+ try {
1829
+ return await options.hooks.handle({
1830
+ request,
1831
+ resolve: async (request, opts) => {
1832
+ if (opts && 'ssr' in opts) ssr = /** @type {boolean} */ (opts.ssr);
1833
+
1834
+ if (state.prerender && state.prerender.fallback) {
1835
+ return await render_response({
1836
+ url: request.url,
1837
+ params: request.params,
1838
+ options,
1839
+ state,
1840
+ $session: await options.hooks.getSession(request),
1841
+ page_config: { router: true, hydrate: true },
1842
+ stuff: {},
1843
+ status: 200,
1844
+ branch: [],
1845
+ ssr: false
1846
+ });
1847
+ }
1848
+
1849
+ let decoded = decodeURI(request.url.pathname);
1850
+
1851
+ if (options.paths.base) {
1852
+ if (!decoded.startsWith(options.paths.base)) return;
1853
+ decoded = decoded.slice(options.paths.base.length) || '/';
1854
+ }
1855
+
1856
+ for (const route of options.manifest._.routes) {
1857
+ const match = route.pattern.exec(decoded);
1858
+ if (!match) continue;
1859
+
1860
+ const response =
1861
+ route.type === 'endpoint'
1862
+ ? await render_endpoint(request, route, match)
1863
+ : await render_page(request, route, match, options, state, ssr);
1864
+
1865
+ if (response) {
1866
+ // inject ETags for 200 responses, if the endpoint
1867
+ // doesn't have its own ETag handling
1868
+ if (response.status === 200 && !response.headers.etag) {
1869
+ const cache_control = get_single_valued_header(response.headers, 'cache-control');
1870
+ if (!cache_control || !/(no-store|immutable)/.test(cache_control)) {
1871
+ let if_none_match_value = request.headers['if-none-match'];
1872
+ // ignore W/ prefix https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives
1873
+ if (if_none_match_value?.startsWith('W/"')) {
1874
+ if_none_match_value = if_none_match_value.substring(2);
1875
+ }
1876
+
1877
+ const etag = `"${hash(response.body || '')}"`;
1878
+
1879
+ if (if_none_match_value === etag) {
1880
+ /** @type {import('types/helper').ResponseHeaders} */
1881
+ const headers = { etag };
1882
+
1883
+ // https://datatracker.ietf.org/doc/html/rfc7232#section-4.1
1884
+ for (const key of [
1885
+ 'cache-control',
1886
+ 'content-location',
1887
+ 'date',
1888
+ 'expires',
1889
+ 'vary'
1890
+ ]) {
1891
+ if (key in response.headers) {
1892
+ headers[key] = /** @type {string} */ (response.headers[key]);
1893
+ }
1894
+ }
1895
+
1896
+ return {
1897
+ status: 304,
1898
+ headers
1899
+ };
1900
+ }
1901
+
1902
+ response.headers['etag'] = etag;
1903
+ }
1904
+ }
1905
+
1906
+ return response;
1907
+ }
1908
+ }
1909
+
1910
+ // if this request came direct from the user, rather than
1911
+ // via a `fetch` in a `load`, render a 404 page
1912
+ if (!state.initiator) {
1913
+ const $session = await options.hooks.getSession(request);
1914
+ return await respond_with_error({
1915
+ request,
1916
+ options,
1917
+ state,
1918
+ $session,
1919
+ status: 404,
1920
+ error: new Error(`Not found: ${request.url.pathname}`),
1921
+ ssr
1922
+ });
1923
+ }
1924
+ }
1925
+ });
1926
+ } catch (/** @type {unknown} */ e) {
1927
+ const error = coalesce_to_error(e);
1928
+
1929
+ options.handle_error(error, request);
1930
+
1931
+ try {
1932
+ const $session = await options.hooks.getSession(request);
1933
+ return await respond_with_error({
1934
+ request,
1935
+ options,
1936
+ state,
1937
+ $session,
1938
+ status: 500,
1939
+ error,
1940
+ ssr
1941
+ });
1942
+ } catch (/** @type {unknown} */ e) {
1943
+ const error = coalesce_to_error(e);
1944
+
1945
+ return {
1946
+ status: 500,
1947
+ headers: {},
1948
+ body: options.dev ? error.stack : error.message
1949
+ };
1950
+ }
1951
+ }
1952
+ }
1953
+
1954
+ /**
1955
+ * @param {import('types/config').ValidatedConfig} config
1956
+ * @param {string} cwd
1957
+ * @returns {Promise<import('vite').Plugin>}
1958
+ */
1959
+ async function create_plugin(config, cwd) {
1960
+ /** @type {import('amphtml-validator').Validator} */
1961
+ let amp;
1962
+
1963
+ if (config.kit.amp) {
1964
+ process.env.VITE_SVELTEKIT_AMP = 'true';
1965
+ amp = await (await import('./index8.js').then(function (n) { return n.i; })).getInstance();
1966
+ }
1967
+
1968
+ return {
1969
+ name: 'vite-plugin-svelte-kit',
1970
+
1971
+ configureServer(vite) {
1972
+ __fetch_polyfill();
1973
+
1974
+ /** @type {import('types/app').SSRManifest} */
1975
+ let manifest;
1976
+
1977
+ function update_manifest() {
1978
+ const manifest_data = create_manifest_data({ config, cwd });
1979
+
1980
+ create_app({ manifest_data, output: `${SVELTE_KIT}/generated`, cwd });
1981
+
1982
+ manifest = {
1983
+ appDir: config.kit.appDir,
1984
+ assets: new Set(manifest_data.assets.map((asset) => asset.file)),
1985
+ _: {
1986
+ mime: get_mime_lookup(manifest_data),
1987
+ entry: {
1988
+ file: `/@fs${runtime}/client/start.js`,
1989
+ css: [],
1990
+ js: []
1991
+ },
1992
+ nodes: manifest_data.components.map((id) => {
1993
+ return async () => {
1994
+ const url = id.startsWith('..') ? `/@fs${path__default.posix.resolve(id)}` : `/${id}`;
1995
+
1996
+ const module = /** @type {import('types/internal').SSRComponent} */ (
1997
+ await vite.ssrLoadModule(url)
1998
+ );
1999
+ const node = await vite.moduleGraph.getModuleByUrl(url);
2000
+
2001
+ if (!node) throw new Error(`Could not find node for ${url}`);
2002
+
2003
+ const deps = new Set();
2004
+ find_deps(node, deps);
2005
+
2006
+ /** @type {Record<string, string>} */
2007
+ const styles = {};
2008
+
2009
+ for (const dep of deps) {
2010
+ const parsed = new URL$1(dep.url, 'http://localhost/');
2011
+ const query = parsed.searchParams;
2012
+
2013
+ // TODO what about .scss files, etc?
2014
+ if (
2015
+ dep.file.endsWith('.css') ||
2016
+ (query.has('svelte') && query.get('type') === 'style')
2017
+ ) {
2018
+ try {
2019
+ const mod = await vite.ssrLoadModule(dep.url);
2020
+ styles[dep.url] = mod.default;
2021
+ } catch {
2022
+ // this can happen with dynamically imported modules, I think
2023
+ // because the Vite module graph doesn't distinguish between
2024
+ // static and dynamic imports? TODO investigate, submit fix
2025
+ }
2026
+ }
2027
+ }
2028
+
2029
+ return {
2030
+ module,
2031
+ entry: url.endsWith('.svelte') ? url : url + '?import',
2032
+ css: [],
2033
+ js: [],
2034
+ styles
2035
+ };
2036
+ };
2037
+ }),
2038
+ routes: manifest_data.routes.map((route) => {
2039
+ if (route.type === 'page') {
2040
+ return {
2041
+ type: 'page',
2042
+ pattern: route.pattern,
2043
+ params: get_params(route.params),
2044
+ a: route.a.map((id) => manifest_data.components.indexOf(id)),
2045
+ b: route.b.map((id) => manifest_data.components.indexOf(id))
2046
+ };
2047
+ }
2048
+
2049
+ return {
2050
+ type: 'endpoint',
2051
+ pattern: route.pattern,
2052
+ params: get_params(route.params),
2053
+ load: async () => {
2054
+ const url = path__default.resolve(cwd, route.file);
2055
+ return await vite.ssrLoadModule(url);
2056
+ }
2057
+ };
2058
+ })
2059
+ }
2060
+ };
2061
+ }
2062
+
2063
+ update_manifest();
2064
+
2065
+ vite.watcher.on('add', update_manifest);
2066
+ vite.watcher.on('remove', update_manifest);
2067
+
2068
+ const assets = config.kit.paths.assets ? SVELTE_KIT_ASSETS : config.kit.paths.base;
2069
+ const asset_server = sirv(config.kit.files.assets, {
2070
+ dev: true,
2071
+ etag: true,
2072
+ maxAge: 0,
2073
+ extensions: []
2074
+ });
2075
+
2076
+ return () => {
2077
+ remove_html_middlewares(vite.middlewares);
2078
+
2079
+ vite.middlewares.use(async (req, res) => {
2080
+ try {
2081
+ if (!req.url || !req.method) throw new Error('Incomplete request');
2082
+ if (req.url === '/favicon.ico') return not_found(res);
2083
+
2084
+ const url = new URL$1(
2085
+ `${vite.config.server.https ? 'https' : 'http'}://${req.headers.host}${req.url}`
2086
+ );
2087
+
2088
+ const decoded = decodeURI(url.pathname);
2089
+
2090
+ if (decoded.startsWith(assets)) {
2091
+ const pathname = decoded.slice(assets.length);
2092
+ const file = config.kit.files.assets + pathname;
2093
+
2094
+ if (fs__default.existsSync(file) && !fs__default.statSync(file).isDirectory()) {
2095
+ req.url = encodeURI(pathname); // don't need query/hash
2096
+ asset_server(req, res);
2097
+ return;
2098
+ }
2099
+ }
2100
+
2101
+ if (!decoded.startsWith(config.kit.paths.base)) return not_found(res);
2102
+
2103
+ /** @type {Partial<import('types/internal').Hooks>} */
2104
+ const user_hooks = resolve_entry(config.kit.files.hooks)
2105
+ ? await vite.ssrLoadModule(`/${config.kit.files.hooks}`)
2106
+ : {};
2107
+
2108
+ /** @type {import('types/internal').Hooks} */
2109
+ const hooks = {
2110
+ getSession: user_hooks.getSession || (() => ({})),
2111
+ handle: user_hooks.handle || (({ request, resolve }) => resolve(request)),
2112
+ handleError:
2113
+ user_hooks.handleError ||
2114
+ (({ /** @type {Error & { frame?: string }} */ error }) => {
2115
+ console.error($.bold().red(error.message));
2116
+ if (error.frame) {
2117
+ console.error($.gray(error.frame));
2118
+ }
2119
+ if (error.stack) {
2120
+ console.error($.gray(error.stack));
2121
+ }
2122
+ }),
2123
+ externalFetch: user_hooks.externalFetch || fetch
2124
+ };
2125
+
2126
+ if (/** @type {any} */ (hooks).getContext) {
2127
+ // TODO remove this for 1.0
2128
+ throw new Error(
2129
+ 'The getContext hook has been removed. See https://kit.svelte.dev/docs#hooks'
2130
+ );
2131
+ }
2132
+
2133
+ if (/** @type {any} */ (hooks).serverFetch) {
2134
+ // TODO remove this for 1.0
2135
+ throw new Error('The serverFetch hook has been renamed to externalFetch.');
2136
+ }
2137
+
2138
+ const root = (await vite.ssrLoadModule(`/${SVELTE_KIT}/generated/root.svelte`)).default;
2139
+ const paths = await vite.ssrLoadModule(
2140
+ true ? `/${SVELTE_KIT}/runtime/paths.js` : `/@fs${runtime}/paths.js`
2141
+ );
2142
+
2143
+ paths.set_paths({
2144
+ base: config.kit.paths.base,
2145
+ assets
2146
+ });
2147
+
2148
+ let body;
2149
+
2150
+ try {
2151
+ body = await getRawBody(req);
2152
+ } catch (/** @type {any} */ err) {
2153
+ res.statusCode = err.status || 400;
2154
+ return res.end(err.reason || 'Invalid request body');
2155
+ }
2156
+
2157
+ const rendered = await respond(
2158
+ {
2159
+ url,
2160
+ headers: /** @type {import('types/helper').RequestHeaders} */ (req.headers),
2161
+ method: req.method,
2162
+ rawBody: body
2163
+ },
2164
+ {
2165
+ amp: config.kit.amp,
2166
+ dev: true,
2167
+ floc: config.kit.floc,
2168
+ get_stack: (error) => {
2169
+ vite.ssrFixStacktrace(error);
2170
+ return error.stack;
2171
+ },
2172
+ handle_error: (error, request) => {
2173
+ vite.ssrFixStacktrace(error);
2174
+ hooks.handleError({ error, request });
2175
+ },
2176
+ hooks,
2177
+ hydrate: config.kit.hydrate,
2178
+ manifest,
2179
+ method_override: config.kit.methodOverride,
2180
+ paths: {
2181
+ base: config.kit.paths.base,
2182
+ assets
2183
+ },
2184
+ prefix: '',
2185
+ prerender: config.kit.prerender.enabled,
2186
+ read: (file) => fs__default.readFileSync(path__default.join(config.kit.files.assets, file)),
2187
+ root,
2188
+ router: config.kit.router,
2189
+ target: config.kit.target,
2190
+ template: ({ head, body, assets }) => {
2191
+ let rendered = load_template(cwd, config)
2192
+ .replace('%svelte.head%', () => head)
2193
+ .replace('%svelte.body%', () => body)
2194
+ .replace(/%svelte\.assets%/g, assets);
2195
+
2196
+ if (amp) {
2197
+ const result = amp.validateString(rendered);
2198
+
2199
+ if (result.status !== 'PASS') {
2200
+ const lines = rendered.split('\n');
2201
+
2202
+ /** @param {string} str */
2203
+ const escape = (str) =>
2204
+ str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
2205
+
2206
+ rendered = `<!doctype html>
2207
+ <head>
2208
+ <meta charset="utf-8" />
2209
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
2210
+ <style>
2211
+ body {
2212
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
2213
+ color: #333;
2214
+ }
2215
+
2216
+ pre {
2217
+ background: #f4f4f4;
2218
+ padding: 1em;
2219
+ overflow-x: auto;
2220
+ }
2221
+ </style>
2222
+ </head>
2223
+ <h1>AMP validation failed</h1>
2224
+
2225
+ ${result.errors
2226
+ .map(
2227
+ (error) => `
2228
+ <h2>${error.severity}</h2>
2229
+ <p>Line ${error.line}, column ${error.col}: ${error.message} (<a href="${error.specUrl}">${
2230
+ error.code
2231
+ }</a>)</p>
2232
+ <pre>${escape(lines[error.line - 1])}</pre>
2233
+ `
2234
+ )
2235
+ .join('\n\n')}
2236
+ `;
2237
+ }
2238
+ }
2239
+
2240
+ return rendered;
2241
+ },
2242
+ trailing_slash: config.kit.trailingSlash
2243
+ }
2244
+ );
2245
+
2246
+ if (rendered) {
2247
+ res.writeHead(rendered.status, rendered.headers);
2248
+ if (rendered.body) res.write(rendered.body);
2249
+ res.end();
2250
+ } else {
2251
+ not_found(res);
2252
+ }
2253
+ } catch (e) {
2254
+ const error = coalesce_to_error(e);
2255
+ vite.ssrFixStacktrace(error);
2256
+ res.statusCode = 500;
2257
+ res.end(error.stack);
2258
+ }
2259
+ });
2260
+ };
2261
+ }
2262
+ };
2263
+ }
2264
+
2265
+ /** @param {string[]} array */
2266
+ function get_params(array) {
2267
+ // given an array of params like `['x', 'y', 'z']` for
2268
+ // src/routes/[x]/[y]/[z]/svelte, create a function
2269
+ // that turns a RegExpExecArray into ({ x, y, z })
2270
+
2271
+ /** @param {RegExpExecArray} match */
2272
+ const fn = (match) => {
2273
+ /** @type {Record<string, string>} */
2274
+ const params = {};
2275
+ array.forEach((key, i) => {
2276
+ if (key.startsWith('...')) {
2277
+ params[key.slice(3)] = match[i + 1] || '';
2278
+ } else {
2279
+ params[key] = match[i + 1];
2280
+ }
2281
+ });
2282
+ return params;
2283
+ };
2284
+
2285
+ return fn;
2286
+ }
2287
+
2288
+ /** @param {import('http').ServerResponse} res */
2289
+ function not_found(res) {
2290
+ res.statusCode = 404;
2291
+ res.end('Not found');
2292
+ }
2293
+
2294
+ /**
2295
+ * @param {import('connect').Server} server
2296
+ */
2297
+ function remove_html_middlewares(server) {
2298
+ const html_middlewares = [
2299
+ 'viteIndexHtmlMiddleware',
2300
+ 'vite404Middleware',
2301
+ 'viteSpaFallbackMiddleware'
2302
+ ];
2303
+ for (let i = server.stack.length - 1; i > 0; i--) {
2304
+ // @ts-expect-error using internals until https://github.com/vitejs/vite/pull/4640 is merged
2305
+ if (html_middlewares.includes(server.stack[i].handle.name)) {
2306
+ server.stack.splice(i, 1);
2307
+ }
2308
+ }
2309
+ }
2310
+
2311
+ /**
2312
+ * @param {import('vite').ModuleNode} node
2313
+ * @param {Set<import('vite').ModuleNode>} deps
2314
+ */
2315
+ function find_deps(node, deps) {
2316
+ for (const dep of node.importedModules) {
2317
+ if (!deps.has(dep)) {
2318
+ deps.add(dep);
2319
+ find_deps(dep, deps);
2320
+ }
2321
+ }
2322
+ }
2323
+
2324
+ /**
2325
+ * @typedef {{
2326
+ * cwd: string,
2327
+ * port: number,
2328
+ * host?: string,
2329
+ * https: boolean,
2330
+ * config: import('types/config').ValidatedConfig
2331
+ * }} Options
2332
+ * @typedef {import('types/internal').SSRComponent} SSRComponent
2333
+ */
2334
+
2335
+ /** @param {Options} opts */
2336
+ async function dev({ cwd, port, host, https, config }) {
2337
+ copy_assets(`${SVELTE_KIT}/runtime`);
2338
+
2339
+ const [vite_config] = deep_merge(
2340
+ {
2341
+ server: {
2342
+ fs: {
2343
+ allow: [
2344
+ ...new Set([
2345
+ config.kit.files.assets,
2346
+ config.kit.files.lib,
2347
+ config.kit.files.routes,
2348
+ path__default.resolve(cwd, 'src'),
2349
+ path__default.resolve(cwd, SVELTE_KIT),
2350
+ path__default.resolve(cwd, 'node_modules'),
2351
+ path__default.resolve(vite.searchForWorkspaceRoot(cwd), 'node_modules')
2352
+ ])
2353
+ ]
2354
+ },
2355
+ strictPort: true
2356
+ }
2357
+ },
2358
+ config.kit.vite()
2359
+ );
2360
+
2361
+ /** @type {[any, string[]]} */
2362
+ const [merged_config, conflicts] = deep_merge(vite_config, {
2363
+ configFile: false,
2364
+ root: cwd,
2365
+ resolve: {
2366
+ alias: get_aliases(config)
2367
+ },
2368
+ build: {
2369
+ rollupOptions: {
2370
+ // Vite dependency crawler needs an explicit JS entry point
2371
+ // eventhough server otherwise works without it
2372
+ input: `${runtime}/client/start.js`
2373
+ }
2374
+ },
2375
+ plugins: [
2376
+ svelte({
2377
+ extensions: config.extensions,
2378
+ emitCss: !config.kit.amp,
2379
+ compilerOptions: {
2380
+ hydratable: !!config.kit.hydrate
2381
+ }
2382
+ }),
2383
+ await create_plugin(config, cwd)
2384
+ ],
2385
+ base: '/'
2386
+ });
2387
+
2388
+ print_config_conflicts(conflicts, 'kit.vite.');
2389
+
2390
+ // optional config from command-line flags
2391
+ // these should take precedence, but not print conflict warnings
2392
+ if (host) {
2393
+ merged_config.server.host = host;
2394
+ }
2395
+
2396
+ // if https is already enabled then do nothing. it could be an object and we
2397
+ // don't want to overwrite with a boolean
2398
+ if (https && !merged_config.server.https) {
2399
+ merged_config.server.https = https;
2400
+ }
2401
+
2402
+ if (port) {
2403
+ merged_config.server.port = port;
2404
+ }
2405
+
2406
+ const server = await vite.createServer(merged_config);
2407
+ await server.listen(port);
2408
+
2409
+ const address_info = /** @type {import('net').AddressInfo} */ (
2410
+ /** @type {import('http').Server} */ (server.httpServer).address()
2411
+ );
2412
+
2413
+ return {
2414
+ address_info,
2415
+ server_config: vite_config.server,
2416
+ close: () => server.close()
2417
+ };
2418
+ }
2419
+
2420
+ export { dev };