@sveltejs/kit 1.0.0-next.24 → 1.0.0-next.240

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 +1842 -0
  13. package/dist/chunks/build.js +658 -0
  14. package/dist/chunks/cert.js +28154 -0
  15. package/dist/chunks/index.js +2202 -0
  16. package/dist/chunks/index2.js +807 -0
  17. package/dist/chunks/index3.js +643 -0
  18. package/dist/chunks/index4.js +109 -0
  19. package/dist/chunks/index5.js +752 -0
  20. package/dist/chunks/index6.js +170 -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 +119 -0
  26. package/dist/cli.js +1060 -84
  27. package/dist/hooks.js +28 -0
  28. package/dist/install-fetch.js +6518 -0
  29. package/dist/node.js +85 -0
  30. package/package.json +95 -54
  31. package/svelte-kit.js +2 -0
  32. package/types/ambient-modules.d.ts +201 -0
  33. package/types/app.d.ts +31 -0
  34. package/types/config.d.ts +164 -0
  35. package/types/endpoint.d.ts +18 -0
  36. package/types/helper.d.ts +32 -0
  37. package/types/hooks.d.ts +42 -0
  38. package/types/index.d.ts +10 -0
  39. package/types/internal.d.ts +234 -0
  40. package/types/page.d.ts +85 -0
  41. package/CHANGELOG.md +0 -300
  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 -727
  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,1842 @@
1
+ /** @param {Partial<import('types/helper').ResponseHeaders> | undefined} object */
2
+ function to_headers(object) {
3
+ const headers = new Headers();
4
+
5
+ if (object) {
6
+ for (const key in object) {
7
+ const value = object[key];
8
+ if (!value) continue;
9
+
10
+ if (typeof value === 'string') {
11
+ headers.set(key, value);
12
+ } else {
13
+ value.forEach((value) => {
14
+ headers.append(key, value);
15
+ });
16
+ }
17
+ }
18
+ }
19
+
20
+ return headers;
21
+ }
22
+
23
+ /**
24
+ * Hash using djb2
25
+ * @param {import('types/hooks').StrictBody} value
26
+ */
27
+ function hash(value) {
28
+ let hash = 5381;
29
+ let i = value.length;
30
+
31
+ if (typeof value === 'string') {
32
+ while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
33
+ } else {
34
+ while (i) hash = (hash * 33) ^ value[--i];
35
+ }
36
+
37
+ return (hash >>> 0).toString(36);
38
+ }
39
+
40
+ /** @param {Record<string, any>} obj */
41
+
42
+ /** @param {Record<string, string>} params */
43
+ function decode_params(params) {
44
+ for (const key in params) {
45
+ // input has already been decoded by decodeURI
46
+ // now handle the rest that decodeURIComponent would do
47
+ params[key] = params[key]
48
+ .replace(/%23/g, '#')
49
+ .replace(/%3[Bb]/g, ';')
50
+ .replace(/%2[Cc]/g, ',')
51
+ .replace(/%2[Ff]/g, '/')
52
+ .replace(/%3[Ff]/g, '?')
53
+ .replace(/%3[Aa]/g, ':')
54
+ .replace(/%40/g, '@')
55
+ .replace(/%26/g, '&')
56
+ .replace(/%3[Dd]/g, '=')
57
+ .replace(/%2[Bb]/g, '+')
58
+ .replace(/%24/g, '$');
59
+ }
60
+
61
+ return params;
62
+ }
63
+
64
+ /** @param {string} body */
65
+ function error(body) {
66
+ return new Response(body, {
67
+ status: 500
68
+ });
69
+ }
70
+
71
+ /** @param {unknown} s */
72
+ function is_string(s) {
73
+ return typeof s === 'string' || s instanceof String;
74
+ }
75
+
76
+ const text_types = new Set([
77
+ 'application/xml',
78
+ 'application/json',
79
+ 'application/x-www-form-urlencoded',
80
+ 'multipart/form-data'
81
+ ]);
82
+
83
+ /**
84
+ * Decides how the body should be parsed based on its mime type. Should match what's in parse_body
85
+ *
86
+ * @param {string | undefined | null} content_type The `content-type` header of a request/response.
87
+ * @returns {boolean}
88
+ */
89
+ function is_text(content_type) {
90
+ if (!content_type) return true; // defaults to json
91
+ const type = content_type.split(';')[0].toLowerCase(); // get the mime type
92
+
93
+ return type.startsWith('text/') || type.endsWith('+xml') || text_types.has(type);
94
+ }
95
+
96
+ /**
97
+ * @param {import('types/hooks').RequestEvent} event
98
+ * @param {import('types/internal').SSREndpoint} route
99
+ * @param {RegExpExecArray} match
100
+ * @returns {Promise<Response | undefined>}
101
+ */
102
+ async function render_endpoint(event, route, match) {
103
+ const mod = await route.load();
104
+
105
+ /** @type {import('types/endpoint').RequestHandler} */
106
+ const handler = mod[event.request.method.toLowerCase().replace('delete', 'del')]; // 'delete' is a reserved word
107
+
108
+ if (!handler) {
109
+ return;
110
+ }
111
+
112
+ // we're mutating `request` so that we don't have to do { ...request, params }
113
+ // on the next line, since that breaks the getters that replace path, query and
114
+ // origin. We could revert that once we remove the getters
115
+ event.params = route.params ? decode_params(route.params(match)) : {};
116
+
117
+ const response = await handler(event);
118
+ const preface = `Invalid response from route ${event.url.pathname}`;
119
+
120
+ if (typeof response !== 'object') {
121
+ return error(`${preface}: expected an object, got ${typeof response}`);
122
+ }
123
+
124
+ if (response.fallthrough) {
125
+ return;
126
+ }
127
+
128
+ const { status = 200, body = {} } = response;
129
+ const headers =
130
+ response.headers instanceof Headers ? response.headers : to_headers(response.headers);
131
+
132
+ const type = headers.get('content-type');
133
+
134
+ if (!is_text(type) && !(body instanceof Uint8Array || is_string(body))) {
135
+ return error(
136
+ `${preface}: body must be an instance of string or Uint8Array if content-type is not a supported textual content-type`
137
+ );
138
+ }
139
+
140
+ /** @type {import('types/hooks').StrictBody} */
141
+ let normalized_body;
142
+
143
+ if (is_pojo(body) && (!type || type.startsWith('application/json'))) {
144
+ headers.set('content-type', 'application/json; charset=utf-8');
145
+ normalized_body = JSON.stringify(body);
146
+ } else {
147
+ normalized_body = /** @type {import('types/hooks').StrictBody} */ (body);
148
+ }
149
+
150
+ if (
151
+ (typeof normalized_body === 'string' || normalized_body instanceof Uint8Array) &&
152
+ !headers.has('etag')
153
+ ) {
154
+ const cache_control = headers.get('cache-control');
155
+ if (!cache_control || !/(no-store|immutable)/.test(cache_control)) {
156
+ headers.set('etag', `"${hash(normalized_body)}"`);
157
+ }
158
+ }
159
+
160
+ return new Response(normalized_body, {
161
+ status,
162
+ headers
163
+ });
164
+ }
165
+
166
+ /** @param {any} body */
167
+ function is_pojo(body) {
168
+ if (typeof body !== 'object') return false;
169
+
170
+ if (body) {
171
+ if (body instanceof Uint8Array) return false;
172
+
173
+ // body could be a node Readable, but we don't want to import
174
+ // node built-ins, so we use duck typing
175
+ if (body._readableState && body._writableState && body._events) return false;
176
+
177
+ // similarly, it could be a web ReadableStream
178
+ if (body[Symbol.toStringTag] === 'ReadableStream') return false;
179
+ }
180
+
181
+ return true;
182
+ }
183
+
184
+ var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$';
185
+ var unsafeChars = /[<>\b\f\n\r\t\0\u2028\u2029]/g;
186
+ 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)$/;
187
+ var escaped = {
188
+ '<': '\\u003C',
189
+ '>': '\\u003E',
190
+ '/': '\\u002F',
191
+ '\\': '\\\\',
192
+ '\b': '\\b',
193
+ '\f': '\\f',
194
+ '\n': '\\n',
195
+ '\r': '\\r',
196
+ '\t': '\\t',
197
+ '\0': '\\0',
198
+ '\u2028': '\\u2028',
199
+ '\u2029': '\\u2029'
200
+ };
201
+ var objectProtoOwnPropertyNames = Object.getOwnPropertyNames(Object.prototype).sort().join('\0');
202
+ function devalue(value) {
203
+ var counts = new Map();
204
+ function walk(thing) {
205
+ if (typeof thing === 'function') {
206
+ throw new Error("Cannot stringify a function");
207
+ }
208
+ if (counts.has(thing)) {
209
+ counts.set(thing, counts.get(thing) + 1);
210
+ return;
211
+ }
212
+ counts.set(thing, 1);
213
+ if (!isPrimitive(thing)) {
214
+ var type = getType(thing);
215
+ switch (type) {
216
+ case 'Number':
217
+ case 'String':
218
+ case 'Boolean':
219
+ case 'Date':
220
+ case 'RegExp':
221
+ return;
222
+ case 'Array':
223
+ thing.forEach(walk);
224
+ break;
225
+ case 'Set':
226
+ case 'Map':
227
+ Array.from(thing).forEach(walk);
228
+ break;
229
+ default:
230
+ var proto = Object.getPrototypeOf(thing);
231
+ if (proto !== Object.prototype &&
232
+ proto !== null &&
233
+ Object.getOwnPropertyNames(proto).sort().join('\0') !== objectProtoOwnPropertyNames) {
234
+ throw new Error("Cannot stringify arbitrary non-POJOs");
235
+ }
236
+ if (Object.getOwnPropertySymbols(thing).length > 0) {
237
+ throw new Error("Cannot stringify POJOs with symbolic keys");
238
+ }
239
+ Object.keys(thing).forEach(function (key) { return walk(thing[key]); });
240
+ }
241
+ }
242
+ }
243
+ walk(value);
244
+ var names = new Map();
245
+ Array.from(counts)
246
+ .filter(function (entry) { return entry[1] > 1; })
247
+ .sort(function (a, b) { return b[1] - a[1]; })
248
+ .forEach(function (entry, i) {
249
+ names.set(entry[0], getName(i));
250
+ });
251
+ function stringify(thing) {
252
+ if (names.has(thing)) {
253
+ return names.get(thing);
254
+ }
255
+ if (isPrimitive(thing)) {
256
+ return stringifyPrimitive(thing);
257
+ }
258
+ var type = getType(thing);
259
+ switch (type) {
260
+ case 'Number':
261
+ case 'String':
262
+ case 'Boolean':
263
+ return "Object(" + stringify(thing.valueOf()) + ")";
264
+ case 'RegExp':
265
+ return "new RegExp(" + stringifyString(thing.source) + ", \"" + thing.flags + "\")";
266
+ case 'Date':
267
+ return "new Date(" + thing.getTime() + ")";
268
+ case 'Array':
269
+ var members = thing.map(function (v, i) { return i in thing ? stringify(v) : ''; });
270
+ var tail = thing.length === 0 || (thing.length - 1 in thing) ? '' : ',';
271
+ return "[" + members.join(',') + tail + "]";
272
+ case 'Set':
273
+ case 'Map':
274
+ return "new " + type + "([" + Array.from(thing).map(stringify).join(',') + "])";
275
+ default:
276
+ var obj = "{" + Object.keys(thing).map(function (key) { return safeKey(key) + ":" + stringify(thing[key]); }).join(',') + "}";
277
+ var proto = Object.getPrototypeOf(thing);
278
+ if (proto === null) {
279
+ return Object.keys(thing).length > 0
280
+ ? "Object.assign(Object.create(null)," + obj + ")"
281
+ : "Object.create(null)";
282
+ }
283
+ return obj;
284
+ }
285
+ }
286
+ var str = stringify(value);
287
+ if (names.size) {
288
+ var params_1 = [];
289
+ var statements_1 = [];
290
+ var values_1 = [];
291
+ names.forEach(function (name, thing) {
292
+ params_1.push(name);
293
+ if (isPrimitive(thing)) {
294
+ values_1.push(stringifyPrimitive(thing));
295
+ return;
296
+ }
297
+ var type = getType(thing);
298
+ switch (type) {
299
+ case 'Number':
300
+ case 'String':
301
+ case 'Boolean':
302
+ values_1.push("Object(" + stringify(thing.valueOf()) + ")");
303
+ break;
304
+ case 'RegExp':
305
+ values_1.push(thing.toString());
306
+ break;
307
+ case 'Date':
308
+ values_1.push("new Date(" + thing.getTime() + ")");
309
+ break;
310
+ case 'Array':
311
+ values_1.push("Array(" + thing.length + ")");
312
+ thing.forEach(function (v, i) {
313
+ statements_1.push(name + "[" + i + "]=" + stringify(v));
314
+ });
315
+ break;
316
+ case 'Set':
317
+ values_1.push("new Set");
318
+ statements_1.push(name + "." + Array.from(thing).map(function (v) { return "add(" + stringify(v) + ")"; }).join('.'));
319
+ break;
320
+ case 'Map':
321
+ values_1.push("new Map");
322
+ statements_1.push(name + "." + Array.from(thing).map(function (_a) {
323
+ var k = _a[0], v = _a[1];
324
+ return "set(" + stringify(k) + ", " + stringify(v) + ")";
325
+ }).join('.'));
326
+ break;
327
+ default:
328
+ values_1.push(Object.getPrototypeOf(thing) === null ? 'Object.create(null)' : '{}');
329
+ Object.keys(thing).forEach(function (key) {
330
+ statements_1.push("" + name + safeProp(key) + "=" + stringify(thing[key]));
331
+ });
332
+ }
333
+ });
334
+ statements_1.push("return " + str);
335
+ return "(function(" + params_1.join(',') + "){" + statements_1.join(';') + "}(" + values_1.join(',') + "))";
336
+ }
337
+ else {
338
+ return str;
339
+ }
340
+ }
341
+ function getName(num) {
342
+ var name = '';
343
+ do {
344
+ name = chars[num % chars.length] + name;
345
+ num = ~~(num / chars.length) - 1;
346
+ } while (num >= 0);
347
+ return reserved.test(name) ? name + "_" : name;
348
+ }
349
+ function isPrimitive(thing) {
350
+ return Object(thing) !== thing;
351
+ }
352
+ function stringifyPrimitive(thing) {
353
+ if (typeof thing === 'string')
354
+ return stringifyString(thing);
355
+ if (thing === void 0)
356
+ return 'void 0';
357
+ if (thing === 0 && 1 / thing < 0)
358
+ return '-0';
359
+ var str = String(thing);
360
+ if (typeof thing === 'number')
361
+ return str.replace(/^(-)?0\./, '$1.');
362
+ return str;
363
+ }
364
+ function getType(thing) {
365
+ return Object.prototype.toString.call(thing).slice(8, -1);
366
+ }
367
+ function escapeUnsafeChar(c) {
368
+ return escaped[c] || c;
369
+ }
370
+ function escapeUnsafeChars(str) {
371
+ return str.replace(unsafeChars, escapeUnsafeChar);
372
+ }
373
+ function safeKey(key) {
374
+ return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key) ? key : escapeUnsafeChars(JSON.stringify(key));
375
+ }
376
+ function safeProp(key) {
377
+ return /^[_$a-zA-Z][_$a-zA-Z0-9]*$/.test(key) ? "." + key : "[" + escapeUnsafeChars(JSON.stringify(key)) + "]";
378
+ }
379
+ function stringifyString(str) {
380
+ var result = '"';
381
+ for (var i = 0; i < str.length; i += 1) {
382
+ var char = str.charAt(i);
383
+ var code = char.charCodeAt(0);
384
+ if (char === '"') {
385
+ result += '\\"';
386
+ }
387
+ else if (char in escaped) {
388
+ result += escaped[char];
389
+ }
390
+ else if (code >= 0xd800 && code <= 0xdfff) {
391
+ var next = str.charCodeAt(i + 1);
392
+ // If this is the beginning of a [high, low] surrogate pair,
393
+ // add the next two characters, otherwise escape
394
+ if (code <= 0xdbff && (next >= 0xdc00 && next <= 0xdfff)) {
395
+ result += char + str[++i];
396
+ }
397
+ else {
398
+ result += "\\u" + code.toString(16).toUpperCase();
399
+ }
400
+ }
401
+ else {
402
+ result += char;
403
+ }
404
+ }
405
+ result += '"';
406
+ return result;
407
+ }
408
+
409
+ function noop() { }
410
+ function safe_not_equal(a, b) {
411
+ return a != a ? b == b : a !== b || ((a && typeof a === 'object') || typeof a === 'function');
412
+ }
413
+ Promise.resolve();
414
+
415
+ const subscriber_queue = [];
416
+ /**
417
+ * Create a `Writable` store that allows both updating and reading by subscription.
418
+ * @param {*=}value initial value
419
+ * @param {StartStopNotifier=}start start and stop notifications for subscriptions
420
+ */
421
+ function writable(value, start = noop) {
422
+ let stop;
423
+ const subscribers = new Set();
424
+ function set(new_value) {
425
+ if (safe_not_equal(value, new_value)) {
426
+ value = new_value;
427
+ if (stop) { // store is ready
428
+ const run_queue = !subscriber_queue.length;
429
+ for (const subscriber of subscribers) {
430
+ subscriber[1]();
431
+ subscriber_queue.push(subscriber, value);
432
+ }
433
+ if (run_queue) {
434
+ for (let i = 0; i < subscriber_queue.length; i += 2) {
435
+ subscriber_queue[i][0](subscriber_queue[i + 1]);
436
+ }
437
+ subscriber_queue.length = 0;
438
+ }
439
+ }
440
+ }
441
+ }
442
+ function update(fn) {
443
+ set(fn(value));
444
+ }
445
+ function subscribe(run, invalidate = noop) {
446
+ const subscriber = [run, invalidate];
447
+ subscribers.add(subscriber);
448
+ if (subscribers.size === 1) {
449
+ stop = start(set) || noop;
450
+ }
451
+ run(value);
452
+ return () => {
453
+ subscribers.delete(subscriber);
454
+ if (subscribers.size === 0) {
455
+ stop();
456
+ stop = null;
457
+ }
458
+ };
459
+ }
460
+ return { set, update, subscribe };
461
+ }
462
+
463
+ /**
464
+ * @param {unknown} err
465
+ * @return {Error}
466
+ */
467
+ function coalesce_to_error(err) {
468
+ return err instanceof Error ||
469
+ (err && /** @type {any} */ (err).name && /** @type {any} */ (err).message)
470
+ ? /** @type {Error} */ (err)
471
+ : new Error(JSON.stringify(err));
472
+ }
473
+
474
+ /** @type {Record<string, string>} */
475
+ const escape_json_string_in_html_dict = {
476
+ '"': '\\"',
477
+ '<': '\\u003C',
478
+ '>': '\\u003E',
479
+ '/': '\\u002F',
480
+ '\\': '\\\\',
481
+ '\b': '\\b',
482
+ '\f': '\\f',
483
+ '\n': '\\n',
484
+ '\r': '\\r',
485
+ '\t': '\\t',
486
+ '\0': '\\0',
487
+ '\u2028': '\\u2028',
488
+ '\u2029': '\\u2029'
489
+ };
490
+
491
+ /** @param {string} str */
492
+ function escape_json_string_in_html(str) {
493
+ return escape(
494
+ str,
495
+ escape_json_string_in_html_dict,
496
+ (code) => `\\u${code.toString(16).toUpperCase()}`
497
+ );
498
+ }
499
+
500
+ /** @type {Record<string, string>} */
501
+ const escape_html_attr_dict = {
502
+ '<': '&lt;',
503
+ '>': '&gt;',
504
+ '"': '&quot;'
505
+ };
506
+
507
+ /**
508
+ * use for escaping string values to be used html attributes on the page
509
+ * e.g.
510
+ * <script data-url="here">
511
+ *
512
+ * @param {string} str
513
+ * @returns string escaped string
514
+ */
515
+ function escape_html_attr(str) {
516
+ return '"' + escape(str, escape_html_attr_dict, (code) => `&#${code};`) + '"';
517
+ }
518
+
519
+ /**
520
+ *
521
+ * @param str {string} string to escape
522
+ * @param dict {Record<string, string>} dictionary of character replacements
523
+ * @param unicode_encoder {function(number): string} encoder to use for high unicode characters
524
+ * @returns {string}
525
+ */
526
+ function escape(str, dict, unicode_encoder) {
527
+ let result = '';
528
+
529
+ for (let i = 0; i < str.length; i += 1) {
530
+ const char = str.charAt(i);
531
+ const code = char.charCodeAt(0);
532
+
533
+ if (char in dict) {
534
+ result += dict[char];
535
+ } else if (code >= 0xd800 && code <= 0xdfff) {
536
+ const next = str.charCodeAt(i + 1);
537
+
538
+ // If this is the beginning of a [high, low] surrogate pair,
539
+ // add the next two characters, otherwise escape
540
+ if (code <= 0xdbff && next >= 0xdc00 && next <= 0xdfff) {
541
+ result += char + str[++i];
542
+ } else {
543
+ result += unicode_encoder(code);
544
+ }
545
+ } else {
546
+ result += char;
547
+ }
548
+ }
549
+
550
+ return result;
551
+ }
552
+
553
+ const s = JSON.stringify;
554
+
555
+ /** @param {URL} url */
556
+ function create_prerendering_url_proxy(url) {
557
+ return new Proxy(url, {
558
+ get: (target, prop, receiver) => {
559
+ if (prop === 'search' || prop === 'searchParams') {
560
+ throw new Error(`Cannot access url.${prop} on a page with prerendering enabled`);
561
+ }
562
+ return Reflect.get(target, prop, receiver);
563
+ }
564
+ });
565
+ }
566
+
567
+ // TODO rename this function/module
568
+
569
+ /**
570
+ * @param {{
571
+ * branch: Array<import('./types').Loaded>;
572
+ * options: import('types/internal').SSRRenderOptions;
573
+ * state: import('types/internal').SSRRenderState;
574
+ * $session: any;
575
+ * page_config: { hydrate: boolean, router: boolean };
576
+ * status: number;
577
+ * error?: Error;
578
+ * url: URL;
579
+ * params: Record<string, string>;
580
+ * ssr: boolean;
581
+ * stuff: Record<string, any>;
582
+ * }} opts
583
+ */
584
+ async function render_response({
585
+ branch,
586
+ options,
587
+ state,
588
+ $session,
589
+ page_config,
590
+ status,
591
+ error,
592
+ url,
593
+ params,
594
+ ssr,
595
+ stuff
596
+ }) {
597
+ const css = new Set(options.manifest._.entry.css);
598
+ const js = new Set(options.manifest._.entry.js);
599
+ /** @type {Map<string, string>} */
600
+ const styles = new Map();
601
+
602
+ /** @type {Array<{ url: string, body: string, json: string }>} */
603
+ const serialized_data = [];
604
+
605
+ let rendered;
606
+
607
+ let is_private = false;
608
+ let maxage;
609
+
610
+ if (error) {
611
+ error.stack = options.get_stack(error);
612
+ }
613
+
614
+ if (ssr) {
615
+ branch.forEach(({ node, loaded, fetched, uses_credentials }) => {
616
+ if (node.css) node.css.forEach((url) => css.add(url));
617
+ if (node.js) node.js.forEach((url) => js.add(url));
618
+ if (node.styles) Object.entries(node.styles).forEach(([k, v]) => styles.set(k, v));
619
+
620
+ // TODO probably better if `fetched` wasn't populated unless `hydrate`
621
+ if (fetched && page_config.hydrate) serialized_data.push(...fetched);
622
+
623
+ if (uses_credentials) is_private = true;
624
+
625
+ maxage = loaded.maxage;
626
+ });
627
+
628
+ const session = writable($session);
629
+
630
+ /** @type {Record<string, any>} */
631
+ const props = {
632
+ stores: {
633
+ page: writable(null),
634
+ navigating: writable(null),
635
+ session
636
+ },
637
+ page: {
638
+ url: state.prerender ? create_prerendering_url_proxy(url) : url,
639
+ params,
640
+ status,
641
+ error,
642
+ stuff
643
+ },
644
+ components: branch.map(({ node }) => node.module.default)
645
+ };
646
+
647
+ // TODO remove this for 1.0
648
+ /**
649
+ * @param {string} property
650
+ * @param {string} replacement
651
+ */
652
+ const print_error = (property, replacement) => {
653
+ Object.defineProperty(props.page, property, {
654
+ get: () => {
655
+ throw new Error(`$page.${property} has been replaced by $page.url.${replacement}`);
656
+ }
657
+ });
658
+ };
659
+
660
+ print_error('origin', 'origin');
661
+ print_error('path', 'pathname');
662
+ print_error('query', 'searchParams');
663
+
664
+ // props_n (instead of props[n]) makes it easy to avoid
665
+ // unnecessary updates for layout components
666
+ for (let i = 0; i < branch.length; i += 1) {
667
+ props[`props_${i}`] = await branch[i].loaded.props;
668
+ }
669
+
670
+ let session_tracking_active = false;
671
+ const unsubscribe = session.subscribe(() => {
672
+ if (session_tracking_active) is_private = true;
673
+ });
674
+ session_tracking_active = true;
675
+
676
+ try {
677
+ rendered = options.root.render(props);
678
+ } finally {
679
+ unsubscribe();
680
+ }
681
+ } else {
682
+ rendered = { head: '', html: '', css: { code: '', map: null } };
683
+ }
684
+
685
+ let { head, html: body } = rendered;
686
+
687
+ const inlined_style = Array.from(styles.values()).join('\n');
688
+
689
+ if (state.prerender) {
690
+ if (maxage) {
691
+ head += `<meta http-equiv="cache-control" content="max-age=${maxage}">`;
692
+ }
693
+ }
694
+
695
+ if (options.amp) {
696
+ head += `
697
+ <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>
698
+ <noscript><style amp-boilerplate>body{-webkit-animation:none;-moz-animation:none;-ms-animation:none;animation:none}</style></noscript>
699
+ <script async src="https://cdn.ampproject.org/v0.js"></script>
700
+
701
+ <style amp-custom>${inlined_style}\n${rendered.css.code}</style>`;
702
+
703
+ if (options.service_worker) {
704
+ head +=
705
+ '<script async custom-element="amp-install-serviceworker" src="https://cdn.ampproject.org/v0/amp-install-serviceworker-0.1.js"></script>';
706
+
707
+ body += `<amp-install-serviceworker src="${options.service_worker}" layout="nodisplay"></amp-install-serviceworker>`;
708
+ }
709
+ } else {
710
+ if (inlined_style) {
711
+ head += `\n\t<style${options.dev ? ' data-svelte' : ''}>${inlined_style}</style>`;
712
+ }
713
+ // prettier-ignore
714
+ head += Array.from(css)
715
+ .map((dep) => `\n\t<link${styles.has(dep) ? ' disabled media="(max-width: 0)"' : ''} rel="stylesheet" href="${options.prefix + dep}">`)
716
+ .join('');
717
+
718
+ if (page_config.router || page_config.hydrate) {
719
+ head += Array.from(js)
720
+ .map((dep) => `\n\t<link rel="modulepreload" href="${options.prefix + dep}">`)
721
+ .join('');
722
+ // prettier-ignore
723
+ head += `
724
+ <script type="module">
725
+ import { start } from ${s(options.prefix + options.manifest._.entry.file)};
726
+ start({
727
+ target: ${options.target ? `document.querySelector(${s(options.target)})` : 'document.body'},
728
+ paths: ${s(options.paths)},
729
+ session: ${try_serialize($session, (error) => {
730
+ throw new Error(`Failed to serialize session data: ${error.message}`);
731
+ })},
732
+ route: ${!!page_config.router},
733
+ spa: ${!ssr},
734
+ trailing_slash: ${s(options.trailing_slash)},
735
+ hydrate: ${ssr && page_config.hydrate ? `{
736
+ status: ${status},
737
+ error: ${serialize_error(error)},
738
+ nodes: [
739
+ ${(branch || [])
740
+ .map(({ node }) => `import(${s(options.prefix + node.entry)})`)
741
+ .join(',\n\t\t\t\t\t\t')}
742
+ ],
743
+ url: new URL(${s(url.href)}),
744
+ params: ${devalue(params)}
745
+ }` : 'null'}
746
+ });
747
+ </script>`;
748
+
749
+ body += serialized_data
750
+ .map(({ url, body, json }) => {
751
+ let attributes = `type="application/json" data-type="svelte-data" data-url=${escape_html_attr(
752
+ url
753
+ )}`;
754
+ if (body) attributes += ` data-body="${hash(body)}"`;
755
+
756
+ return `<script ${attributes}>${json}</script>`;
757
+ })
758
+ .join('\n\n\t');
759
+ }
760
+
761
+ if (options.service_worker) {
762
+ // always include service worker unless it's turned off explicitly
763
+ head += `
764
+ <script>
765
+ if ('serviceWorker' in navigator) {
766
+ navigator.serviceWorker.register('${options.service_worker}');
767
+ }
768
+ </script>`;
769
+ }
770
+ }
771
+
772
+ const segments = url.pathname.slice(options.paths.base.length).split('/').slice(2);
773
+ const assets =
774
+ options.paths.assets || (segments.length > 0 ? segments.map(() => '..').join('/') : '.');
775
+
776
+ const html = options.template({ head, body, assets });
777
+
778
+ const headers = new Headers({
779
+ 'content-type': 'text/html',
780
+ etag: `"${hash(html)}"`
781
+ });
782
+
783
+ if (maxage) {
784
+ headers.set('cache-control', `${is_private ? 'private' : 'public'}, max-age=${maxage}`);
785
+ }
786
+
787
+ if (!options.floc) {
788
+ headers.set('permissions-policy', 'interest-cohort=()');
789
+ }
790
+
791
+ return new Response(html, {
792
+ status,
793
+ headers
794
+ });
795
+ }
796
+
797
+ /**
798
+ * @param {any} data
799
+ * @param {(error: Error) => void} [fail]
800
+ */
801
+ function try_serialize(data, fail) {
802
+ try {
803
+ return devalue(data);
804
+ } catch (err) {
805
+ if (fail) fail(coalesce_to_error(err));
806
+ return null;
807
+ }
808
+ }
809
+
810
+ // Ensure we return something truthy so the client will not re-render the page over the error
811
+
812
+ /** @param {(Error & {frame?: string} & {loc?: object}) | undefined | null} error */
813
+ function serialize_error(error) {
814
+ if (!error) return null;
815
+ let serialized = try_serialize(error);
816
+ if (!serialized) {
817
+ const { name, message, stack } = error;
818
+ serialized = try_serialize({ ...error, name, message, stack });
819
+ }
820
+ if (!serialized) {
821
+ serialized = '{}';
822
+ }
823
+ return serialized;
824
+ }
825
+
826
+ /**
827
+ * @param {import('types/page').LoadOutput} loaded
828
+ * @returns {import('types/internal').NormalizedLoadOutput}
829
+ */
830
+ function normalize(loaded) {
831
+ const has_error_status =
832
+ loaded.status && loaded.status >= 400 && loaded.status <= 599 && !loaded.redirect;
833
+ if (loaded.error || has_error_status) {
834
+ const status = loaded.status;
835
+
836
+ if (!loaded.error && has_error_status) {
837
+ return {
838
+ status: status || 500,
839
+ error: new Error()
840
+ };
841
+ }
842
+
843
+ const error = typeof loaded.error === 'string' ? new Error(loaded.error) : loaded.error;
844
+
845
+ if (!(error instanceof Error)) {
846
+ return {
847
+ status: 500,
848
+ error: new Error(
849
+ `"error" property returned from load() must be a string or instance of Error, received type "${typeof error}"`
850
+ )
851
+ };
852
+ }
853
+
854
+ if (!status || status < 400 || status > 599) {
855
+ console.warn('"error" returned from load() without a valid status code — defaulting to 500');
856
+ return { status: 500, error };
857
+ }
858
+
859
+ return { status, error };
860
+ }
861
+
862
+ if (loaded.redirect) {
863
+ if (!loaded.status || Math.floor(loaded.status / 100) !== 3) {
864
+ return {
865
+ status: 500,
866
+ error: new Error(
867
+ '"redirect" property returned from load() must be accompanied by a 3xx status code'
868
+ )
869
+ };
870
+ }
871
+
872
+ if (typeof loaded.redirect !== 'string') {
873
+ return {
874
+ status: 500,
875
+ error: new Error('"redirect" property returned from load() must be a string')
876
+ };
877
+ }
878
+ }
879
+
880
+ // TODO remove before 1.0
881
+ if (/** @type {any} */ (loaded).context) {
882
+ throw new Error(
883
+ 'You are returning "context" from a load function. ' +
884
+ '"context" was renamed to "stuff", please adjust your code accordingly.'
885
+ );
886
+ }
887
+
888
+ return /** @type {import('types/internal').NormalizedLoadOutput} */ (loaded);
889
+ }
890
+
891
+ const absolute = /^([a-z]+:)?\/?\//;
892
+ const scheme = /^[a-z]+:/;
893
+
894
+ /**
895
+ * @param {string} base
896
+ * @param {string} path
897
+ */
898
+ function resolve(base, path) {
899
+ if (scheme.test(path)) return path;
900
+
901
+ const base_match = absolute.exec(base);
902
+ const path_match = absolute.exec(path);
903
+
904
+ if (!base_match) {
905
+ throw new Error(`bad base path: "${base}"`);
906
+ }
907
+
908
+ const baseparts = path_match ? [] : base.slice(base_match[0].length).split('/');
909
+ const pathparts = path_match ? path.slice(path_match[0].length).split('/') : path.split('/');
910
+
911
+ baseparts.pop();
912
+
913
+ for (let i = 0; i < pathparts.length; i += 1) {
914
+ const part = pathparts[i];
915
+ if (part === '.') continue;
916
+ else if (part === '..') baseparts.pop();
917
+ else baseparts.push(part);
918
+ }
919
+
920
+ const prefix = (path_match && path_match[0]) || (base_match && base_match[0]) || '';
921
+
922
+ return `${prefix}${baseparts.join('/')}`;
923
+ }
924
+
925
+ /** @param {string} path */
926
+ function is_root_relative(path) {
927
+ return path[0] === '/' && path[1] !== '/';
928
+ }
929
+
930
+ /**
931
+ * @param {{
932
+ * event: import('types/hooks').RequestEvent;
933
+ * options: import('types/internal').SSRRenderOptions;
934
+ * state: import('types/internal').SSRRenderState;
935
+ * route: import('types/internal').SSRPage | null;
936
+ * url: URL;
937
+ * params: Record<string, string>;
938
+ * node: import('types/internal').SSRNode;
939
+ * $session: any;
940
+ * stuff: Record<string, any>;
941
+ * is_error: boolean;
942
+ * status?: number;
943
+ * error?: Error;
944
+ * }} opts
945
+ * @returns {Promise<import('./types').Loaded | undefined>} undefined for fallthrough
946
+ */
947
+ async function load_node({
948
+ event,
949
+ options,
950
+ state,
951
+ route,
952
+ url,
953
+ params,
954
+ node,
955
+ $session,
956
+ stuff,
957
+ is_error,
958
+ status,
959
+ error
960
+ }) {
961
+ const { module } = node;
962
+
963
+ let uses_credentials = false;
964
+
965
+ /**
966
+ * @type {Array<{
967
+ * url: string;
968
+ * body: string;
969
+ * json: string;
970
+ * }>}
971
+ */
972
+ const fetched = [];
973
+
974
+ /**
975
+ * @type {string[]}
976
+ */
977
+ let set_cookie_headers = [];
978
+
979
+ let loaded;
980
+
981
+ if (module.load) {
982
+ /** @type {import('types/page').LoadInput | import('types/page').ErrorLoadInput} */
983
+ const load_input = {
984
+ url: state.prerender ? create_prerendering_url_proxy(url) : url,
985
+ params,
986
+ get session() {
987
+ uses_credentials = true;
988
+ return $session;
989
+ },
990
+ /**
991
+ * @param {RequestInfo} resource
992
+ * @param {RequestInit} opts
993
+ */
994
+ fetch: async (resource, opts = {}) => {
995
+ /** @type {string} */
996
+ let requested;
997
+
998
+ if (typeof resource === 'string') {
999
+ requested = resource;
1000
+ } else {
1001
+ requested = resource.url;
1002
+
1003
+ opts = {
1004
+ method: resource.method,
1005
+ headers: resource.headers,
1006
+ body: resource.body,
1007
+ mode: resource.mode,
1008
+ credentials: resource.credentials,
1009
+ cache: resource.cache,
1010
+ redirect: resource.redirect,
1011
+ referrer: resource.referrer,
1012
+ integrity: resource.integrity,
1013
+ ...opts
1014
+ };
1015
+ }
1016
+
1017
+ opts.headers = new Headers(opts.headers);
1018
+
1019
+ const resolved = resolve(event.url.pathname, requested.split('?')[0]);
1020
+
1021
+ let response;
1022
+
1023
+ // handle fetch requests for static assets. e.g. prebaked data, etc.
1024
+ // we need to support everything the browser's fetch supports
1025
+ const prefix = options.paths.assets || options.paths.base;
1026
+ const filename = (
1027
+ resolved.startsWith(prefix) ? resolved.slice(prefix.length) : resolved
1028
+ ).slice(1);
1029
+ const filename_html = `${filename}/index.html`; // path may also match path/index.html
1030
+
1031
+ const is_asset = options.manifest.assets.has(filename);
1032
+ const is_asset_html = options.manifest.assets.has(filename_html);
1033
+
1034
+ if (is_asset || is_asset_html) {
1035
+ const file = is_asset ? filename : filename_html;
1036
+
1037
+ if (options.read) {
1038
+ const type = is_asset
1039
+ ? options.manifest._.mime[filename.slice(filename.lastIndexOf('.'))]
1040
+ : 'text/html';
1041
+
1042
+ response = new Response(options.read(file), {
1043
+ headers: type ? { 'content-type': type } : {}
1044
+ });
1045
+ } else {
1046
+ response = await fetch(`${url.origin}/${file}`, /** @type {RequestInit} */ (opts));
1047
+ }
1048
+ } else if (is_root_relative(resolved)) {
1049
+ const relative = resolved;
1050
+
1051
+ // TODO: fix type https://github.com/node-fetch/node-fetch/issues/1113
1052
+ if (opts.credentials !== 'omit') {
1053
+ uses_credentials = true;
1054
+
1055
+ const cookie = event.request.headers.get('cookie');
1056
+ const authorization = event.request.headers.get('authorization');
1057
+
1058
+ if (cookie) {
1059
+ opts.headers.set('cookie', cookie);
1060
+ }
1061
+
1062
+ if (authorization && !opts.headers.has('authorization')) {
1063
+ opts.headers.set('authorization', authorization);
1064
+ }
1065
+ }
1066
+
1067
+ if (opts.body && typeof opts.body !== 'string') {
1068
+ // per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a
1069
+ // Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object.
1070
+ // non-string bodies are irksome to deal with, but luckily aren't particularly useful
1071
+ // in this context anyway, so we take the easy route and ban them
1072
+ throw new Error('Request body must be a string');
1073
+ }
1074
+
1075
+ const rendered = await respond(
1076
+ new Request(new URL(requested, event.url).href, opts),
1077
+ options,
1078
+ {
1079
+ fetched: requested,
1080
+ initiator: route
1081
+ }
1082
+ );
1083
+
1084
+ if (rendered) {
1085
+ if (state.prerender) {
1086
+ state.prerender.dependencies.set(relative, rendered);
1087
+ }
1088
+
1089
+ response = rendered;
1090
+ } else {
1091
+ // we can't load the endpoint from our own manifest,
1092
+ // so we need to make an actual HTTP request
1093
+ return fetch(new URL(requested, event.url).href, {
1094
+ method: opts.method || 'GET',
1095
+ headers: opts.headers
1096
+ });
1097
+ }
1098
+ } else {
1099
+ // external
1100
+ if (resolved.startsWith('//')) {
1101
+ throw new Error(
1102
+ `Cannot request protocol-relative URL (${requested}) in server-side fetch`
1103
+ );
1104
+ }
1105
+
1106
+ // external fetch
1107
+ // allow cookie passthrough for "same-origin"
1108
+ // if SvelteKit is serving my.domain.com:
1109
+ // - domain.com WILL NOT receive cookies
1110
+ // - my.domain.com WILL receive cookies
1111
+ // - api.domain.dom WILL NOT receive cookies
1112
+ // - sub.my.domain.com WILL receive cookies
1113
+ // ports do not affect the resolution
1114
+ // leading dot prevents mydomain.com matching domain.com
1115
+ if (
1116
+ `.${new URL(requested).hostname}`.endsWith(`.${event.url.hostname}`) &&
1117
+ opts.credentials !== 'omit'
1118
+ ) {
1119
+ uses_credentials = true;
1120
+
1121
+ const cookie = event.request.headers.get('cookie');
1122
+ if (cookie) opts.headers.set('cookie', cookie);
1123
+ }
1124
+
1125
+ const external_request = new Request(requested, /** @type {RequestInit} */ (opts));
1126
+ response = await options.hooks.externalFetch.call(null, external_request);
1127
+ }
1128
+
1129
+ if (response) {
1130
+ const proxy = new Proxy(response, {
1131
+ get(response, key, _receiver) {
1132
+ async function text() {
1133
+ const body = await response.text();
1134
+
1135
+ /** @type {import('types/helper').ResponseHeaders} */
1136
+ const headers = {};
1137
+ for (const [key, value] of response.headers) {
1138
+ if (key === 'set-cookie') {
1139
+ set_cookie_headers = set_cookie_headers.concat(value);
1140
+ } else if (key !== 'etag') {
1141
+ headers[key] = value;
1142
+ }
1143
+ }
1144
+
1145
+ if (!opts.body || typeof opts.body === 'string') {
1146
+ // prettier-ignore
1147
+ fetched.push({
1148
+ url: requested,
1149
+ body: /** @type {string} */ (opts.body),
1150
+ json: `{"status":${response.status},"statusText":${s(response.statusText)},"headers":${s(headers)},"body":"${escape_json_string_in_html(body)}"}`
1151
+ });
1152
+ }
1153
+
1154
+ return body;
1155
+ }
1156
+
1157
+ if (key === 'text') {
1158
+ return text;
1159
+ }
1160
+
1161
+ if (key === 'json') {
1162
+ return async () => {
1163
+ return JSON.parse(await text());
1164
+ };
1165
+ }
1166
+
1167
+ // TODO arrayBuffer?
1168
+
1169
+ return Reflect.get(response, key, response);
1170
+ }
1171
+ });
1172
+
1173
+ return proxy;
1174
+ }
1175
+
1176
+ return (
1177
+ response ||
1178
+ new Response('Not found', {
1179
+ status: 404
1180
+ })
1181
+ );
1182
+ },
1183
+ stuff: { ...stuff }
1184
+ };
1185
+
1186
+ if (options.dev) {
1187
+ // TODO remove this for 1.0
1188
+ Object.defineProperty(load_input, 'page', {
1189
+ get: () => {
1190
+ throw new Error('`page` in `load` functions has been replaced by `url` and `params`');
1191
+ }
1192
+ });
1193
+ }
1194
+
1195
+ if (is_error) {
1196
+ /** @type {import('types/page').ErrorLoadInput} */ (load_input).status = status;
1197
+ /** @type {import('types/page').ErrorLoadInput} */ (load_input).error = error;
1198
+ }
1199
+
1200
+ loaded = await module.load.call(null, load_input);
1201
+
1202
+ if (!loaded) {
1203
+ throw new Error(`load function must return a value${options.dev ? ` (${node.entry})` : ''}`);
1204
+ }
1205
+ } else {
1206
+ loaded = {};
1207
+ }
1208
+
1209
+ if (loaded.fallthrough && !is_error) {
1210
+ return;
1211
+ }
1212
+
1213
+ return {
1214
+ node,
1215
+ loaded: normalize(loaded),
1216
+ stuff: loaded.stuff || stuff,
1217
+ fetched,
1218
+ set_cookie_headers,
1219
+ uses_credentials
1220
+ };
1221
+ }
1222
+
1223
+ /**
1224
+ * @typedef {import('./types.js').Loaded} Loaded
1225
+ * @typedef {import('types/internal').SSRRenderOptions} SSRRenderOptions
1226
+ * @typedef {import('types/internal').SSRRenderState} SSRRenderState
1227
+ */
1228
+
1229
+ /**
1230
+ * @param {{
1231
+ * event: import('types/hooks').RequestEvent;
1232
+ * options: SSRRenderOptions;
1233
+ * state: SSRRenderState;
1234
+ * $session: any;
1235
+ * status: number;
1236
+ * error: Error;
1237
+ * ssr: boolean;
1238
+ * }} opts
1239
+ */
1240
+ async function respond_with_error({ event, options, state, $session, status, error, ssr }) {
1241
+ try {
1242
+ const default_layout = await options.manifest._.nodes[0](); // 0 is always the root layout
1243
+ const default_error = await options.manifest._.nodes[1](); // 1 is always the root error
1244
+
1245
+ /** @type {Record<string, string>} */
1246
+ const params = {}; // error page has no params
1247
+
1248
+ const layout_loaded = /** @type {Loaded} */ (
1249
+ await load_node({
1250
+ event,
1251
+ options,
1252
+ state,
1253
+ route: null,
1254
+ url: event.url, // TODO this is redundant, no?
1255
+ params,
1256
+ node: default_layout,
1257
+ $session,
1258
+ stuff: {},
1259
+ is_error: false
1260
+ })
1261
+ );
1262
+
1263
+ const error_loaded = /** @type {Loaded} */ (
1264
+ await load_node({
1265
+ event,
1266
+ options,
1267
+ state,
1268
+ route: null,
1269
+ url: event.url,
1270
+ params,
1271
+ node: default_error,
1272
+ $session,
1273
+ stuff: layout_loaded ? layout_loaded.stuff : {},
1274
+ is_error: true,
1275
+ status,
1276
+ error
1277
+ })
1278
+ );
1279
+
1280
+ return await render_response({
1281
+ options,
1282
+ state,
1283
+ $session,
1284
+ page_config: {
1285
+ hydrate: options.hydrate,
1286
+ router: options.router
1287
+ },
1288
+ stuff: error_loaded.stuff,
1289
+ status,
1290
+ error,
1291
+ branch: [layout_loaded, error_loaded],
1292
+ url: event.url,
1293
+ params,
1294
+ ssr
1295
+ });
1296
+ } catch (err) {
1297
+ const error = coalesce_to_error(err);
1298
+
1299
+ options.handle_error(error, event);
1300
+
1301
+ return new Response(error.stack, {
1302
+ status: 500
1303
+ });
1304
+ }
1305
+ }
1306
+
1307
+ /**
1308
+ * @typedef {import('./types.js').Loaded} Loaded
1309
+ * @typedef {import('types/internal').SSRNode} SSRNode
1310
+ * @typedef {import('types/internal').SSRRenderOptions} SSRRenderOptions
1311
+ * @typedef {import('types/internal').SSRRenderState} SSRRenderState
1312
+ */
1313
+
1314
+ /**
1315
+ * @param {{
1316
+ * event: import('types/hooks').RequestEvent;
1317
+ * options: SSRRenderOptions;
1318
+ * state: SSRRenderState;
1319
+ * $session: any;
1320
+ * route: import('types/internal').SSRPage;
1321
+ * params: Record<string, string>;
1322
+ * ssr: boolean;
1323
+ * }} opts
1324
+ * @returns {Promise<Response | undefined>}
1325
+ */
1326
+ async function respond$1(opts) {
1327
+ const { event, options, state, $session, route, ssr } = opts;
1328
+
1329
+ /** @type {Array<SSRNode | undefined>} */
1330
+ let nodes;
1331
+
1332
+ if (!ssr) {
1333
+ return await render_response({
1334
+ ...opts,
1335
+ branch: [],
1336
+ page_config: {
1337
+ hydrate: true,
1338
+ router: true
1339
+ },
1340
+ status: 200,
1341
+ url: event.url,
1342
+ stuff: {}
1343
+ });
1344
+ }
1345
+
1346
+ try {
1347
+ nodes = await Promise.all(
1348
+ route.a.map((n) => options.manifest._.nodes[n] && options.manifest._.nodes[n]())
1349
+ );
1350
+ } catch (err) {
1351
+ const error = coalesce_to_error(err);
1352
+
1353
+ options.handle_error(error, event);
1354
+
1355
+ return await respond_with_error({
1356
+ event,
1357
+ options,
1358
+ state,
1359
+ $session,
1360
+ status: 500,
1361
+ error,
1362
+ ssr
1363
+ });
1364
+ }
1365
+
1366
+ // the leaf node will be present. only layouts may be undefined
1367
+ const leaf = /** @type {SSRNode} */ (nodes[nodes.length - 1]).module;
1368
+
1369
+ let page_config = get_page_config(leaf, options);
1370
+
1371
+ if (!leaf.prerender && state.prerender && !state.prerender.all) {
1372
+ // if the page has `export const prerender = true`, continue,
1373
+ // otherwise bail out at this point
1374
+ return new Response(undefined, {
1375
+ status: 204
1376
+ });
1377
+ }
1378
+
1379
+ /** @type {Array<Loaded>} */
1380
+ let branch = [];
1381
+
1382
+ /** @type {number} */
1383
+ let status = 200;
1384
+
1385
+ /** @type {Error|undefined} */
1386
+ let error;
1387
+
1388
+ /** @type {string[]} */
1389
+ let set_cookie_headers = [];
1390
+
1391
+ let stuff = {};
1392
+
1393
+ ssr: if (ssr) {
1394
+ for (let i = 0; i < nodes.length; i += 1) {
1395
+ const node = nodes[i];
1396
+
1397
+ /** @type {Loaded | undefined} */
1398
+ let loaded;
1399
+
1400
+ if (node) {
1401
+ try {
1402
+ loaded = await load_node({
1403
+ ...opts,
1404
+ url: event.url,
1405
+ node,
1406
+ stuff,
1407
+ is_error: false
1408
+ });
1409
+
1410
+ if (!loaded) return;
1411
+
1412
+ set_cookie_headers = set_cookie_headers.concat(loaded.set_cookie_headers);
1413
+
1414
+ if (loaded.loaded.redirect) {
1415
+ return with_cookies(
1416
+ new Response(undefined, {
1417
+ status: loaded.loaded.status,
1418
+ headers: {
1419
+ location: loaded.loaded.redirect
1420
+ }
1421
+ }),
1422
+ set_cookie_headers
1423
+ );
1424
+ }
1425
+
1426
+ if (loaded.loaded.error) {
1427
+ ({ status, error } = loaded.loaded);
1428
+ }
1429
+ } catch (err) {
1430
+ const e = coalesce_to_error(err);
1431
+
1432
+ options.handle_error(e, event);
1433
+
1434
+ status = 500;
1435
+ error = e;
1436
+ }
1437
+
1438
+ if (loaded && !error) {
1439
+ branch.push(loaded);
1440
+ }
1441
+
1442
+ if (error) {
1443
+ while (i--) {
1444
+ if (route.b[i]) {
1445
+ const error_node = await options.manifest._.nodes[route.b[i]]();
1446
+
1447
+ /** @type {Loaded} */
1448
+ let node_loaded;
1449
+ let j = i;
1450
+ while (!(node_loaded = branch[j])) {
1451
+ j -= 1;
1452
+ }
1453
+
1454
+ try {
1455
+ const error_loaded = /** @type {import('./types').Loaded} */ (
1456
+ await load_node({
1457
+ ...opts,
1458
+ url: event.url,
1459
+ node: error_node,
1460
+ stuff: node_loaded.stuff,
1461
+ is_error: true,
1462
+ status,
1463
+ error
1464
+ })
1465
+ );
1466
+
1467
+ if (error_loaded.loaded.error) {
1468
+ continue;
1469
+ }
1470
+
1471
+ page_config = get_page_config(error_node.module, options);
1472
+ branch = branch.slice(0, j + 1).concat(error_loaded);
1473
+ stuff = { ...node_loaded.stuff, ...error_loaded.stuff };
1474
+ break ssr;
1475
+ } catch (err) {
1476
+ const e = coalesce_to_error(err);
1477
+
1478
+ options.handle_error(e, event);
1479
+
1480
+ continue;
1481
+ }
1482
+ }
1483
+ }
1484
+
1485
+ // TODO backtrack until we find an __error.svelte component
1486
+ // that we can use as the leaf node
1487
+ // for now just return regular error page
1488
+ return with_cookies(
1489
+ await respond_with_error({
1490
+ event,
1491
+ options,
1492
+ state,
1493
+ $session,
1494
+ status,
1495
+ error,
1496
+ ssr
1497
+ }),
1498
+ set_cookie_headers
1499
+ );
1500
+ }
1501
+ }
1502
+
1503
+ if (loaded && loaded.loaded.stuff) {
1504
+ stuff = {
1505
+ ...stuff,
1506
+ ...loaded.loaded.stuff
1507
+ };
1508
+ }
1509
+ }
1510
+ }
1511
+
1512
+ try {
1513
+ return with_cookies(
1514
+ await render_response({
1515
+ ...opts,
1516
+ stuff,
1517
+ url: event.url,
1518
+ page_config,
1519
+ status,
1520
+ error,
1521
+ branch: branch.filter(Boolean)
1522
+ }),
1523
+ set_cookie_headers
1524
+ );
1525
+ } catch (err) {
1526
+ const error = coalesce_to_error(err);
1527
+
1528
+ options.handle_error(error, event);
1529
+
1530
+ return with_cookies(
1531
+ await respond_with_error({
1532
+ ...opts,
1533
+ status: 500,
1534
+ error
1535
+ }),
1536
+ set_cookie_headers
1537
+ );
1538
+ }
1539
+ }
1540
+
1541
+ /**
1542
+ * @param {import('types/internal').SSRComponent} leaf
1543
+ * @param {SSRRenderOptions} options
1544
+ */
1545
+ function get_page_config(leaf, options) {
1546
+ // TODO remove for 1.0
1547
+ if ('ssr' in leaf) {
1548
+ throw new Error(
1549
+ '`export const ssr` has been removed — use the handle hook instead: https://kit.svelte.dev/docs#hooks-handle'
1550
+ );
1551
+ }
1552
+
1553
+ return {
1554
+ router: 'router' in leaf ? !!leaf.router : options.router,
1555
+ hydrate: 'hydrate' in leaf ? !!leaf.hydrate : options.hydrate
1556
+ };
1557
+ }
1558
+
1559
+ /**
1560
+ * @param {Response} response
1561
+ * @param {string[]} set_cookie_headers
1562
+ */
1563
+ function with_cookies(response, set_cookie_headers) {
1564
+ if (set_cookie_headers.length) {
1565
+ set_cookie_headers.forEach((value) => {
1566
+ response.headers.append('set-cookie', value);
1567
+ });
1568
+ }
1569
+ return response;
1570
+ }
1571
+
1572
+ /**
1573
+ * @param {import('types/hooks').RequestEvent} event
1574
+ * @param {import('types/internal').SSRPage} route
1575
+ * @param {RegExpExecArray} match
1576
+ * @param {import('types/internal').SSRRenderOptions} options
1577
+ * @param {import('types/internal').SSRRenderState} state
1578
+ * @param {boolean} ssr
1579
+ * @returns {Promise<Response | undefined>}
1580
+ */
1581
+ async function render_page(event, route, match, options, state, ssr) {
1582
+ if (state.initiator === route) {
1583
+ // infinite request cycle detected
1584
+ return new Response(`Not found: ${event.url.pathname}`, {
1585
+ status: 404
1586
+ });
1587
+ }
1588
+
1589
+ const params = route.params ? decode_params(route.params(match)) : {};
1590
+
1591
+ const $session = await options.hooks.getSession(event);
1592
+
1593
+ const response = await respond$1({
1594
+ event,
1595
+ options,
1596
+ state,
1597
+ $session,
1598
+ route,
1599
+ params,
1600
+ ssr
1601
+ });
1602
+
1603
+ if (response) {
1604
+ return response;
1605
+ }
1606
+
1607
+ if (state.fetched) {
1608
+ // we came here because of a bad request in a `load` function.
1609
+ // rather than render the error page — which could lead to an
1610
+ // infinite loop, if the `load` belonged to the root layout,
1611
+ // we respond with a bare-bones 500
1612
+ return new Response(`Bad request in load function: failed to fetch ${state.fetched}`, {
1613
+ status: 500
1614
+ });
1615
+ }
1616
+ }
1617
+
1618
+ /** @type {import('types/internal').Respond} */
1619
+ async function respond(request, options, state = {}) {
1620
+ const url = new URL(request.url);
1621
+
1622
+ if (url.pathname !== '/' && options.trailing_slash !== 'ignore') {
1623
+ const has_trailing_slash = url.pathname.endsWith('/');
1624
+
1625
+ if (
1626
+ (has_trailing_slash && options.trailing_slash === 'never') ||
1627
+ (!has_trailing_slash &&
1628
+ options.trailing_slash === 'always' &&
1629
+ !(url.pathname.split('/').pop() || '').includes('.'))
1630
+ ) {
1631
+ url.pathname = has_trailing_slash ? url.pathname.slice(0, -1) : url.pathname + '/';
1632
+
1633
+ if (url.search === '?') url.search = '';
1634
+
1635
+ return new Response(undefined, {
1636
+ status: 301,
1637
+ headers: {
1638
+ location: url.pathname + url.search
1639
+ }
1640
+ });
1641
+ }
1642
+ }
1643
+
1644
+ const { parameter, allowed } = options.method_override;
1645
+ const method_override = url.searchParams.get(parameter)?.toUpperCase();
1646
+
1647
+ if (method_override) {
1648
+ if (request.method === 'POST') {
1649
+ if (allowed.includes(method_override)) {
1650
+ request = new Proxy(request, {
1651
+ get: (target, property, _receiver) => {
1652
+ if (property === 'method') return method_override;
1653
+ return Reflect.get(target, property, target);
1654
+ }
1655
+ });
1656
+ } else {
1657
+ const verb = allowed.length === 0 ? 'enabled' : 'allowed';
1658
+ const body = `${parameter}=${method_override} is not ${verb}. See https://kit.svelte.dev/docs#configuration-methodoverride`;
1659
+
1660
+ return new Response(body, {
1661
+ status: 400
1662
+ });
1663
+ }
1664
+ } else {
1665
+ throw new Error(`${parameter}=${method_override} is only allowed with POST requests`);
1666
+ }
1667
+ }
1668
+
1669
+ /** @type {import('types/hooks').RequestEvent} */
1670
+ const event = {
1671
+ request,
1672
+ url,
1673
+ params: {},
1674
+ locals: {}
1675
+ };
1676
+
1677
+ // TODO remove this for 1.0
1678
+ /**
1679
+ * @param {string} property
1680
+ * @param {string} replacement
1681
+ * @param {string} suffix
1682
+ */
1683
+ const removed = (property, replacement, suffix = '') => ({
1684
+ get: () => {
1685
+ throw new Error(`event.${property} has been replaced by event.${replacement}` + suffix);
1686
+ }
1687
+ });
1688
+
1689
+ const details = '. See https://github.com/sveltejs/kit/pull/3384 for details';
1690
+
1691
+ const body_getter = {
1692
+ get: () => {
1693
+ throw new Error(
1694
+ 'To access the request body use the text/json/arrayBuffer/formData methods, e.g. `body = await request.json()`' +
1695
+ details
1696
+ );
1697
+ }
1698
+ };
1699
+
1700
+ Object.defineProperties(event, {
1701
+ method: removed('method', 'request.method', details),
1702
+ headers: removed('headers', 'request.headers', details),
1703
+ origin: removed('origin', 'url.origin'),
1704
+ path: removed('path', 'url.pathname'),
1705
+ query: removed('query', 'url.searchParams'),
1706
+ body: body_getter,
1707
+ rawBody: body_getter
1708
+ });
1709
+
1710
+ let ssr = true;
1711
+
1712
+ try {
1713
+ const response = await options.hooks.handle({
1714
+ event,
1715
+ resolve: async (event, opts) => {
1716
+ if (opts && 'ssr' in opts) ssr = /** @type {boolean} */ (opts.ssr);
1717
+
1718
+ if (state.prerender && state.prerender.fallback) {
1719
+ return await render_response({
1720
+ url: event.url,
1721
+ params: event.params,
1722
+ options,
1723
+ state,
1724
+ $session: await options.hooks.getSession(event),
1725
+ page_config: { router: true, hydrate: true },
1726
+ stuff: {},
1727
+ status: 200,
1728
+ branch: [],
1729
+ ssr: false
1730
+ });
1731
+ }
1732
+
1733
+ let decoded = decodeURI(event.url.pathname);
1734
+
1735
+ if (options.paths.base) {
1736
+ if (!decoded.startsWith(options.paths.base)) return;
1737
+ decoded = decoded.slice(options.paths.base.length) || '/';
1738
+ }
1739
+
1740
+ for (const route of options.manifest._.routes) {
1741
+ const match = route.pattern.exec(decoded);
1742
+ if (!match) continue;
1743
+
1744
+ const response =
1745
+ route.type === 'endpoint'
1746
+ ? await render_endpoint(event, route, match)
1747
+ : await render_page(event, route, match, options, state, ssr);
1748
+
1749
+ if (response) {
1750
+ // respond with 304 if etag matches
1751
+ if (response.status === 200 && response.headers.has('etag')) {
1752
+ let if_none_match_value = request.headers.get('if-none-match');
1753
+
1754
+ // ignore W/ prefix https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/If-None-Match#directives
1755
+ if (if_none_match_value?.startsWith('W/"')) {
1756
+ if_none_match_value = if_none_match_value.substring(2);
1757
+ }
1758
+
1759
+ const etag = /** @type {string} */ (response.headers.get('etag'));
1760
+
1761
+ if (if_none_match_value === etag) {
1762
+ const headers = new Headers({ etag });
1763
+
1764
+ // https://datatracker.ietf.org/doc/html/rfc7232#section-4.1
1765
+ for (const key of [
1766
+ 'cache-control',
1767
+ 'content-location',
1768
+ 'date',
1769
+ 'expires',
1770
+ 'vary'
1771
+ ]) {
1772
+ const value = response.headers.get(key);
1773
+ if (value) headers.set(key, value);
1774
+ }
1775
+
1776
+ return new Response(undefined, {
1777
+ status: 304,
1778
+ headers
1779
+ });
1780
+ }
1781
+ }
1782
+
1783
+ return response;
1784
+ }
1785
+ }
1786
+
1787
+ // if this request came direct from the user, rather than
1788
+ // via a `fetch` in a `load`, render a 404 page
1789
+ if (!state.initiator) {
1790
+ const $session = await options.hooks.getSession(event);
1791
+ return await respond_with_error({
1792
+ event,
1793
+ options,
1794
+ state,
1795
+ $session,
1796
+ status: 404,
1797
+ error: new Error(`Not found: ${event.url.pathname}`),
1798
+ ssr
1799
+ });
1800
+ }
1801
+ },
1802
+
1803
+ // TODO remove for 1.0
1804
+ // @ts-expect-error
1805
+ get request() {
1806
+ throw new Error('request in handle has been replaced with event' + details);
1807
+ }
1808
+ });
1809
+
1810
+ // TODO for 1.0, change the error message to point to docs rather than PR
1811
+ if (response && !(response instanceof Response)) {
1812
+ throw new Error('handle must return a Response object' + details);
1813
+ }
1814
+
1815
+ return response;
1816
+ } catch (/** @type {unknown} */ e) {
1817
+ const error = coalesce_to_error(e);
1818
+
1819
+ options.handle_error(error, event);
1820
+
1821
+ try {
1822
+ const $session = await options.hooks.getSession(event);
1823
+ return await respond_with_error({
1824
+ event,
1825
+ options,
1826
+ state,
1827
+ $session,
1828
+ status: 500,
1829
+ error,
1830
+ ssr
1831
+ });
1832
+ } catch (/** @type {unknown} */ e) {
1833
+ const error = coalesce_to_error(e);
1834
+
1835
+ return new Response(options.dev ? error.stack : error.message, {
1836
+ status: 500
1837
+ });
1838
+ }
1839
+ }
1840
+ }
1841
+
1842
+ export { respond };