@sveltejs/kit 1.0.0-next.227 → 1.0.0-next.230

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