@sveltejs/kit 1.0.0-next.98 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/README.md +5 -1
  2. package/package.json +91 -77
  3. package/postinstall.js +47 -0
  4. package/src/cli.js +44 -0
  5. package/src/constants.js +5 -0
  6. package/src/core/adapt/builder.js +221 -0
  7. package/src/core/adapt/index.js +31 -0
  8. package/src/core/config/default-error.html +56 -0
  9. package/src/core/config/index.js +100 -0
  10. package/src/core/config/options.js +387 -0
  11. package/src/core/config/types.d.ts +1 -0
  12. package/src/core/env.js +138 -0
  13. package/src/core/generate_manifest/index.js +116 -0
  14. package/src/core/prerender/crawl.js +207 -0
  15. package/src/core/prerender/entities.js +2252 -0
  16. package/src/core/prerender/fallback.js +43 -0
  17. package/src/core/prerender/prerender.js +459 -0
  18. package/src/core/prerender/queue.js +80 -0
  19. package/src/core/sync/create_manifest_data/conflict.js +0 -0
  20. package/src/core/sync/create_manifest_data/index.js +523 -0
  21. package/src/core/sync/create_manifest_data/sort.js +161 -0
  22. package/src/core/sync/create_manifest_data/types.d.ts +37 -0
  23. package/src/core/sync/sync.js +59 -0
  24. package/src/core/sync/utils.js +33 -0
  25. package/src/core/sync/write_ambient.js +58 -0
  26. package/src/core/sync/write_client_manifest.js +107 -0
  27. package/src/core/sync/write_matchers.js +25 -0
  28. package/src/core/sync/write_root.js +91 -0
  29. package/src/core/sync/write_tsconfig.js +195 -0
  30. package/src/core/sync/write_types/index.js +809 -0
  31. package/src/core/utils.js +67 -0
  32. package/src/exports/hooks/index.js +1 -0
  33. package/src/exports/hooks/sequence.js +44 -0
  34. package/src/exports/index.js +55 -0
  35. package/src/exports/node/index.js +172 -0
  36. package/src/exports/node/polyfills.js +28 -0
  37. package/src/exports/vite/build/build_server.js +359 -0
  38. package/src/exports/vite/build/build_service_worker.js +85 -0
  39. package/src/exports/vite/build/utils.js +230 -0
  40. package/src/exports/vite/dev/index.js +597 -0
  41. package/src/exports/vite/graph_analysis/index.js +99 -0
  42. package/src/exports/vite/graph_analysis/types.d.ts +5 -0
  43. package/src/exports/vite/graph_analysis/utils.js +6 -0
  44. package/src/exports/vite/index.js +708 -0
  45. package/src/exports/vite/preview/index.js +194 -0
  46. package/src/exports/vite/types.d.ts +3 -0
  47. package/src/exports/vite/utils.js +184 -0
  48. package/src/runtime/app/env.js +1 -0
  49. package/src/runtime/app/environment.js +13 -0
  50. package/src/runtime/app/forms.js +135 -0
  51. package/src/runtime/app/navigation.js +22 -0
  52. package/src/runtime/app/paths.js +1 -0
  53. package/src/runtime/app/stores.js +57 -0
  54. package/src/runtime/client/ambient.d.ts +30 -0
  55. package/src/runtime/client/client.js +1725 -0
  56. package/src/runtime/client/constants.js +10 -0
  57. package/src/runtime/client/fetcher.js +127 -0
  58. package/src/runtime/client/parse.js +60 -0
  59. package/src/runtime/client/singletons.js +21 -0
  60. package/src/runtime/client/start.js +45 -0
  61. package/src/runtime/client/types.d.ts +86 -0
  62. package/src/runtime/client/utils.js +257 -0
  63. package/src/runtime/components/error.svelte +6 -0
  64. package/{assets → src/runtime}/components/layout.svelte +0 -0
  65. package/src/runtime/control.js +45 -0
  66. package/src/runtime/env/dynamic/private.js +1 -0
  67. package/src/runtime/env/dynamic/public.js +1 -0
  68. package/src/runtime/env-private.js +6 -0
  69. package/src/runtime/env-public.js +6 -0
  70. package/src/runtime/env.js +12 -0
  71. package/src/runtime/hash.js +20 -0
  72. package/src/runtime/paths.js +11 -0
  73. package/src/runtime/server/cookie.js +228 -0
  74. package/src/runtime/server/data/index.js +158 -0
  75. package/src/runtime/server/endpoint.js +86 -0
  76. package/src/runtime/server/fetch.js +175 -0
  77. package/src/runtime/server/index.js +405 -0
  78. package/src/runtime/server/page/actions.js +267 -0
  79. package/src/runtime/server/page/crypto.js +239 -0
  80. package/src/runtime/server/page/csp.js +250 -0
  81. package/src/runtime/server/page/index.js +326 -0
  82. package/src/runtime/server/page/load_data.js +270 -0
  83. package/src/runtime/server/page/render.js +393 -0
  84. package/src/runtime/server/page/respond_with_error.js +103 -0
  85. package/src/runtime/server/page/serialize_data.js +87 -0
  86. package/src/runtime/server/page/types.d.ts +35 -0
  87. package/src/runtime/server/utils.js +179 -0
  88. package/src/utils/array.js +9 -0
  89. package/src/utils/error.js +22 -0
  90. package/src/utils/escape.js +46 -0
  91. package/src/utils/exports.js +54 -0
  92. package/src/utils/filesystem.js +178 -0
  93. package/src/utils/functions.js +16 -0
  94. package/src/utils/http.js +72 -0
  95. package/src/utils/misc.js +1 -0
  96. package/src/utils/promises.js +17 -0
  97. package/src/utils/routing.js +201 -0
  98. package/src/utils/unit_test.js +11 -0
  99. package/src/utils/url.js +161 -0
  100. package/svelte-kit.js +1 -1
  101. package/types/ambient.d.ts +451 -0
  102. package/types/index.d.ts +1168 -5
  103. package/types/internal.d.ts +348 -160
  104. package/types/private.d.ts +237 -0
  105. package/types/synthetic/$env+dynamic+private.md +10 -0
  106. package/types/synthetic/$env+dynamic+public.md +8 -0
  107. package/types/synthetic/$env+static+private.md +19 -0
  108. package/types/synthetic/$env+static+public.md +7 -0
  109. package/types/synthetic/$lib.md +5 -0
  110. package/CHANGELOG.md +0 -819
  111. package/assets/components/error.svelte +0 -21
  112. package/assets/runtime/app/env.js +0 -16
  113. package/assets/runtime/app/navigation.js +0 -53
  114. package/assets/runtime/app/paths.js +0 -1
  115. package/assets/runtime/app/stores.js +0 -87
  116. package/assets/runtime/chunks/utils.js +0 -13
  117. package/assets/runtime/env.js +0 -8
  118. package/assets/runtime/internal/singletons.js +0 -20
  119. package/assets/runtime/internal/start.js +0 -1061
  120. package/assets/runtime/paths.js +0 -12
  121. package/dist/chunks/_commonjsHelpers.js +0 -8
  122. package/dist/chunks/cert.js +0 -29079
  123. package/dist/chunks/constants.js +0 -3
  124. package/dist/chunks/index.js +0 -3526
  125. package/dist/chunks/index2.js +0 -583
  126. package/dist/chunks/index3.js +0 -31
  127. package/dist/chunks/index4.js +0 -1005
  128. package/dist/chunks/index5.js +0 -327
  129. package/dist/chunks/index6.js +0 -325
  130. package/dist/chunks/standard.js +0 -99
  131. package/dist/chunks/utils.js +0 -149
  132. package/dist/cli.js +0 -711
  133. package/dist/http.js +0 -66
  134. package/dist/install-fetch.js +0 -1699
  135. package/dist/ssr.js +0 -1529
  136. package/types/ambient-modules.d.ts +0 -115
  137. package/types/config.d.ts +0 -101
  138. package/types/endpoint.d.ts +0 -23
  139. package/types/helper.d.ts +0 -19
  140. package/types/hooks.d.ts +0 -23
  141. package/types/page.d.ts +0 -30
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Hash using djb2
3
+ * @param {import('types').StrictBody} value
4
+ */
5
+ export function hash(value) {
6
+ let hash = 5381;
7
+
8
+ if (typeof value === 'string') {
9
+ let i = value.length;
10
+ while (i) hash = (hash * 33) ^ value.charCodeAt(--i);
11
+ } else if (ArrayBuffer.isView(value)) {
12
+ const buffer = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
13
+ let i = buffer.length;
14
+ while (i) hash = (hash * 33) ^ buffer[--i];
15
+ } else {
16
+ throw new TypeError('value must be a string or TypedArray');
17
+ }
18
+
19
+ return (hash >>> 0).toString(36);
20
+ }
@@ -0,0 +1,11 @@
1
+ /** @type {string} */
2
+ export let base = '';
3
+
4
+ /** @type {string} */
5
+ export let assets = '';
6
+
7
+ /** @param {{ base: string, assets: string }} paths */
8
+ export function set_paths(paths) {
9
+ base = paths.base;
10
+ assets = paths.assets || base;
11
+ }
@@ -0,0 +1,228 @@
1
+ import { parse, serialize } from 'cookie';
2
+ import { normalize_path } from '../../utils/url.js';
3
+
4
+ /**
5
+ * Tracks all cookies set during dev mode so we can emit warnings
6
+ * when we detect that there's likely cookie misusage due to wrong paths
7
+ *
8
+ * @type {Record<string, Set<string>>} */
9
+ const cookie_paths = {};
10
+
11
+ /**
12
+ * @param {Request} request
13
+ * @param {URL} url
14
+ * @param {boolean} dev
15
+ * @param {import('types').TrailingSlash} trailing_slash
16
+ */
17
+ export function get_cookies(request, url, dev, trailing_slash) {
18
+ const header = request.headers.get('cookie') ?? '';
19
+ const initial_cookies = parse(header, { decode: (value) => value });
20
+
21
+ const normalized_url = normalize_path(url.pathname, trailing_slash);
22
+ // Emulate browser-behavior: if the cookie is set at '/foo/bar', its path is '/foo'
23
+ const default_path = normalized_url.split('/').slice(0, -1).join('/') || '/';
24
+
25
+ if (dev) {
26
+ // TODO this could theoretically be wrong if the cookie was set unencoded?
27
+ const initial_decoded_cookies = parse(header, { decode: decodeURIComponent });
28
+ // Remove all cookies that no longer exist according to the request
29
+ for (const name of Object.keys(cookie_paths)) {
30
+ cookie_paths[name] = new Set(
31
+ [...cookie_paths[name]].filter(
32
+ (path) => !path_matches(normalized_url, path) || name in initial_decoded_cookies
33
+ )
34
+ );
35
+ }
36
+ // Add all new cookies we might not have seen before
37
+ for (const name in initial_decoded_cookies) {
38
+ cookie_paths[name] = cookie_paths[name] ?? new Set();
39
+ if (![...cookie_paths[name]].some((path) => path_matches(normalized_url, path))) {
40
+ cookie_paths[name].add(default_path);
41
+ }
42
+ }
43
+ }
44
+
45
+ /** @type {Record<string, import('./page/types').Cookie>} */
46
+ const new_cookies = {};
47
+
48
+ /** @type {import('cookie').CookieSerializeOptions} */
49
+ const defaults = {
50
+ httpOnly: true,
51
+ sameSite: 'lax',
52
+ secure: url.hostname === 'localhost' && url.protocol === 'http:' ? false : true
53
+ };
54
+
55
+ /** @type {import('types').Cookies} */
56
+ const cookies = {
57
+ // The JSDoc param annotations appearing below for get, set and delete
58
+ // are necessary to expose the `cookie` library types to
59
+ // typescript users. `@type {import('types').Cookies}` above is not
60
+ // sufficient to do so.
61
+
62
+ /**
63
+ * @param {string} name
64
+ * @param {import('cookie').CookieParseOptions} opts
65
+ */
66
+ get(name, opts) {
67
+ const c = new_cookies[name];
68
+ if (
69
+ c &&
70
+ domain_matches(url.hostname, c.options.domain) &&
71
+ path_matches(url.pathname, c.options.path)
72
+ ) {
73
+ return c.value;
74
+ }
75
+
76
+ const decoder = opts?.decode || decodeURIComponent;
77
+ const req_cookies = parse(header, { decode: decoder });
78
+ const cookie = req_cookies[name]; // the decoded string or undefined
79
+
80
+ if (!dev || cookie) {
81
+ return cookie;
82
+ }
83
+
84
+ const paths = new Set([...(cookie_paths[name] ?? [])]);
85
+ if (c) {
86
+ paths.add(c.options.path ?? default_path);
87
+ }
88
+ if (paths.size > 0) {
89
+ console.warn(
90
+ // prettier-ignore
91
+ `Cookie with name '${name}' was not found at path '${url.pathname}', but a cookie with that name exists at these paths: '${[...paths].join("', '")}'. Did you mean to set its 'path' to '/' instead?`
92
+ );
93
+ }
94
+ },
95
+
96
+ /**
97
+ * @param {string} name
98
+ * @param {string} value
99
+ * @param {import('cookie').CookieSerializeOptions} opts
100
+ */
101
+ set(name, value, opts = {}) {
102
+ let path = opts.path ?? default_path;
103
+
104
+ new_cookies[name] = {
105
+ name,
106
+ value,
107
+ options: {
108
+ ...defaults,
109
+ ...opts,
110
+ path
111
+ }
112
+ };
113
+
114
+ if (dev) {
115
+ cookie_paths[name] = cookie_paths[name] ?? new Set();
116
+ if (!value) {
117
+ if (!cookie_paths[name].has(path) && cookie_paths[name].size > 0) {
118
+ const paths = `'${Array.from(cookie_paths[name]).join("', '")}'`;
119
+ console.warn(
120
+ `Trying to delete cookie '${name}' at path '${path}', but a cookie with that name only exists at these paths: ${paths}.`
121
+ );
122
+ }
123
+ cookie_paths[name].delete(path);
124
+ } else {
125
+ // We could also emit a warning here if the cookie already exists at a different path,
126
+ // but that's more likely a false positive because it's valid to set the same name at different paths
127
+ cookie_paths[name].add(path);
128
+ }
129
+ }
130
+ },
131
+
132
+ /**
133
+ * @param {string} name
134
+ * @param {import('cookie').CookieSerializeOptions} opts
135
+ */
136
+ delete(name, opts = {}) {
137
+ cookies.set(name, '', {
138
+ ...opts,
139
+ maxAge: 0
140
+ });
141
+ },
142
+
143
+ /**
144
+ * @param {string} name
145
+ * @param {string} value
146
+ * @param {import('cookie').CookieSerializeOptions} opts
147
+ */
148
+ serialize(name, value, opts) {
149
+ return serialize(name, value, {
150
+ ...defaults,
151
+ ...opts
152
+ });
153
+ }
154
+ };
155
+
156
+ /**
157
+ * @param {URL} destination
158
+ * @param {string | null} header
159
+ */
160
+ function get_cookie_header(destination, header) {
161
+ /** @type {Record<string, string>} */
162
+ const combined_cookies = {
163
+ // cookies sent by the user agent have lowest precedence
164
+ ...initial_cookies
165
+ };
166
+
167
+ // cookies previous set during this event with cookies.set have higher precedence
168
+ for (const key in new_cookies) {
169
+ const cookie = new_cookies[key];
170
+ if (!domain_matches(destination.hostname, cookie.options.domain)) continue;
171
+ if (!path_matches(destination.pathname, cookie.options.path)) continue;
172
+
173
+ const encoder = cookie.options.encode || encodeURIComponent;
174
+ combined_cookies[cookie.name] = encoder(cookie.value);
175
+ }
176
+
177
+ // explicit header has highest precedence
178
+ if (header) {
179
+ const parsed = parse(header, { decode: (value) => value });
180
+ for (const name in parsed) {
181
+ combined_cookies[name] = parsed[name];
182
+ }
183
+ }
184
+
185
+ return Object.entries(combined_cookies)
186
+ .map(([name, value]) => `${name}=${value}`)
187
+ .join('; ');
188
+ }
189
+
190
+ return { cookies, new_cookies, get_cookie_header };
191
+ }
192
+
193
+ /**
194
+ * @param {string} hostname
195
+ * @param {string} [constraint]
196
+ */
197
+ export function domain_matches(hostname, constraint) {
198
+ if (!constraint) return true;
199
+
200
+ const normalized = constraint[0] === '.' ? constraint.slice(1) : constraint;
201
+
202
+ if (hostname === normalized) return true;
203
+ return hostname.endsWith('.' + normalized);
204
+ }
205
+
206
+ /**
207
+ * @param {string} path
208
+ * @param {string} [constraint]
209
+ */
210
+ export function path_matches(path, constraint) {
211
+ if (!constraint) return true;
212
+
213
+ const normalized = constraint.endsWith('/') ? constraint.slice(0, -1) : constraint;
214
+
215
+ if (path === normalized) return true;
216
+ return path.startsWith(normalized + '/');
217
+ }
218
+
219
+ /**
220
+ * @param {Headers} headers
221
+ * @param {import('./page/types').Cookie[]} cookies
222
+ */
223
+ export function add_cookies_to_headers(headers, cookies) {
224
+ for (const new_cookie of cookies) {
225
+ const { name, value, options } = new_cookie;
226
+ headers.append('set-cookie', serialize(name, value, options));
227
+ }
228
+ }
@@ -0,0 +1,158 @@
1
+ import { HttpError, Redirect } from '../../control.js';
2
+ import { normalize_error } from '../../../utils/error.js';
3
+ import { once } from '../../../utils/functions.js';
4
+ import { load_server_data } from '../page/load_data.js';
5
+ import { clarify_devalue_error, handle_error_and_jsonify, serialize_data_node } from '../utils.js';
6
+ import { normalize_path } from '../../../utils/url.js';
7
+
8
+ export const INVALIDATED_PARAM = 'x-sveltekit-invalidated';
9
+
10
+ /**
11
+ * @param {import('types').RequestEvent} event
12
+ * @param {import('types').SSRRoute} route
13
+ * @param {import('types').SSROptions} options
14
+ * @param {import('types').SSRState} state
15
+ * @param {boolean[] | undefined} invalidated_data_nodes
16
+ * @param {import('types').TrailingSlash} trailing_slash
17
+ * @returns {Promise<Response>}
18
+ */
19
+ export async function render_data(
20
+ event,
21
+ route,
22
+ options,
23
+ state,
24
+ invalidated_data_nodes,
25
+ trailing_slash
26
+ ) {
27
+ if (!route.page) {
28
+ // requesting /__data.json should fail for a +server.js
29
+ return new Response(undefined, {
30
+ status: 404
31
+ });
32
+ }
33
+
34
+ try {
35
+ const node_ids = [...route.page.layouts, route.page.leaf];
36
+ const invalidated = invalidated_data_nodes ?? node_ids.map(() => true);
37
+
38
+ let aborted = false;
39
+
40
+ const url = new URL(event.url);
41
+ url.pathname = normalize_path(url.pathname, trailing_slash);
42
+
43
+ const new_event = { ...event, url };
44
+
45
+ const functions = node_ids.map((n, i) => {
46
+ return once(async () => {
47
+ try {
48
+ if (aborted) {
49
+ return /** @type {import('types').ServerDataSkippedNode} */ ({
50
+ type: 'skip'
51
+ });
52
+ }
53
+
54
+ // == because it could be undefined (in dev) or null (in build, because of JSON.stringify)
55
+ const node = n == undefined ? n : await options.manifest._.nodes[n]();
56
+ return load_server_data({
57
+ event: new_event,
58
+ options,
59
+ state,
60
+ node,
61
+ parent: async () => {
62
+ /** @type {Record<string, any>} */
63
+ const data = {};
64
+ for (let j = 0; j < i; j += 1) {
65
+ const parent = /** @type {import('types').ServerDataNode | null} */ (
66
+ await functions[j]()
67
+ );
68
+
69
+ if (parent) {
70
+ Object.assign(data, parent.data);
71
+ }
72
+ }
73
+ return data;
74
+ }
75
+ });
76
+ } catch (e) {
77
+ aborted = true;
78
+ throw e;
79
+ }
80
+ });
81
+ });
82
+
83
+ const promises = functions.map(async (fn, i) => {
84
+ if (!invalidated[i]) {
85
+ return /** @type {import('types').ServerDataSkippedNode} */ ({
86
+ type: 'skip'
87
+ });
88
+ }
89
+
90
+ return fn();
91
+ });
92
+
93
+ let length = promises.length;
94
+ const nodes = await Promise.all(
95
+ promises.map((p, i) =>
96
+ p.catch(async (error) => {
97
+ if (error instanceof Redirect) {
98
+ throw error;
99
+ }
100
+
101
+ // Math.min because array isn't guaranteed to resolve in order
102
+ length = Math.min(length, i + 1);
103
+
104
+ return /** @type {import('types').ServerErrorNode} */ ({
105
+ type: 'error',
106
+ error: await handle_error_and_jsonify(event, options, error),
107
+ status: error instanceof HttpError ? error.status : undefined
108
+ });
109
+ })
110
+ )
111
+ );
112
+
113
+ try {
114
+ const stubs = nodes.slice(0, length).map(serialize_data_node);
115
+
116
+ const json = `{"type":"data","nodes":[${stubs.join(',')}]}`;
117
+ return json_response(json);
118
+ } catch (e) {
119
+ const error = /** @type {any} */ (e);
120
+ return json_response(JSON.stringify(clarify_devalue_error(event, error)), 500);
121
+ }
122
+ } catch (e) {
123
+ const error = normalize_error(e);
124
+
125
+ if (error instanceof Redirect) {
126
+ return redirect_json_response(error);
127
+ } else {
128
+ // TODO make it clearer that this was an unexpected error
129
+ return json_response(JSON.stringify(await handle_error_and_jsonify(event, options, error)));
130
+ }
131
+ }
132
+ }
133
+
134
+ /**
135
+ * @param {string} json
136
+ * @param {number} [status]
137
+ */
138
+ function json_response(json, status = 200) {
139
+ return new Response(json, {
140
+ status,
141
+ headers: {
142
+ 'content-type': 'application/json',
143
+ 'cache-control': 'private, no-store'
144
+ }
145
+ });
146
+ }
147
+
148
+ /**
149
+ * @param {Redirect} redirect
150
+ */
151
+ export function redirect_json_response(redirect) {
152
+ return json_response(
153
+ JSON.stringify({
154
+ type: 'redirect',
155
+ location: redirect.location
156
+ })
157
+ );
158
+ }
@@ -0,0 +1,86 @@
1
+ import { negotiate } from '../../utils/http.js';
2
+ import { Redirect } from '../control.js';
3
+ import { method_not_allowed } from './utils.js';
4
+
5
+ /**
6
+ * @param {import('types').RequestEvent} event
7
+ * @param {import('types').SSREndpoint} mod
8
+ * @param {import('types').SSRState} state
9
+ * @returns {Promise<Response>}
10
+ */
11
+ export async function render_endpoint(event, mod, state) {
12
+ const method = /** @type {import('types').HttpMethod} */ (event.request.method);
13
+
14
+ let handler = mod[method];
15
+
16
+ if (!handler && method === 'HEAD') {
17
+ handler = mod.GET;
18
+ }
19
+
20
+ if (!handler) {
21
+ return method_not_allowed(mod, method);
22
+ }
23
+
24
+ const prerender = mod.prerender ?? state.prerender_default;
25
+
26
+ if (prerender && (mod.POST || mod.PATCH || mod.PUT || mod.DELETE)) {
27
+ throw new Error('Cannot prerender endpoints that have mutative methods');
28
+ }
29
+
30
+ if (state.prerendering && !prerender) {
31
+ if (state.initiator) {
32
+ // if request came from a prerendered page, bail
33
+ throw new Error(`${event.route.id} is not prerenderable`);
34
+ } else {
35
+ // if request came direct from the crawler, signal that
36
+ // this route cannot be prerendered, but don't bail
37
+ return new Response(undefined, { status: 204 });
38
+ }
39
+ }
40
+
41
+ try {
42
+ const response = await handler(
43
+ /** @type {import('types').RequestEvent<Record<string, any>>} */ (event)
44
+ );
45
+
46
+ if (!(response instanceof Response)) {
47
+ throw new Error(
48
+ `Invalid response from route ${event.url.pathname}: handler should return a Response object`
49
+ );
50
+ }
51
+
52
+ if (state.prerendering) {
53
+ response.headers.set('x-sveltekit-prerender', String(prerender));
54
+ }
55
+
56
+ return response;
57
+ } catch (error) {
58
+ if (error instanceof Redirect) {
59
+ return new Response(undefined, {
60
+ status: error.status,
61
+ headers: { location: error.location }
62
+ });
63
+ }
64
+
65
+ throw error;
66
+ }
67
+ }
68
+
69
+ /**
70
+ * @param {import('types').RequestEvent} event
71
+ */
72
+ export function is_endpoint_request(event) {
73
+ const { method, headers } = event.request;
74
+
75
+ if (method === 'PUT' || method === 'PATCH' || method === 'DELETE') {
76
+ // These methods exist exclusively for endpoints
77
+ return true;
78
+ }
79
+
80
+ // use:enhance uses a custom header to disambiguate
81
+ if (method === 'POST' && headers.get('x-sveltekit-action') === 'true') return false;
82
+
83
+ // GET/POST requests may be for endpoints or pages. We prefer endpoints if this isn't a text/html request
84
+ const accept = event.request.headers.get('accept') ?? '*/*';
85
+ return negotiate(accept, ['*', 'text/html']) !== 'text/html';
86
+ }
@@ -0,0 +1,175 @@
1
+ import * as set_cookie_parser from 'set-cookie-parser';
2
+ import { respond } from './index.js';
3
+
4
+ /**
5
+ * @param {{
6
+ * event: import('types').RequestEvent;
7
+ * options: import('types').SSROptions;
8
+ * state: import('types').SSRState;
9
+ * get_cookie_header: (url: URL, header: string | null) => string;
10
+ * }} opts
11
+ * @returns {typeof fetch}
12
+ */
13
+ export function create_fetch({ event, options, state, get_cookie_header }) {
14
+ return async (info, init) => {
15
+ const original_request = normalize_fetch_input(info, init, event.url);
16
+
17
+ const request_body = init?.body;
18
+
19
+ // some runtimes (e.g. Cloudflare) error if you access `request.mode`,
20
+ // annoyingly, so we need to read the value from the `init` object instead
21
+ let mode = (info instanceof Request ? info.mode : init?.mode) ?? 'cors';
22
+ let credentials =
23
+ (info instanceof Request ? info.credentials : init?.credentials) ?? 'same-origin';
24
+
25
+ return await options.hooks.handleFetch({
26
+ event,
27
+ request: original_request,
28
+ fetch: async (info, init) => {
29
+ const request = normalize_fetch_input(info, init, event.url);
30
+
31
+ const url = new URL(request.url);
32
+
33
+ if (!request.headers.has('origin')) {
34
+ request.headers.set('origin', event.url.origin);
35
+ }
36
+
37
+ if (info !== original_request) {
38
+ mode = (info instanceof Request ? info.mode : init?.mode) ?? 'cors';
39
+ credentials =
40
+ (info instanceof Request ? info.credentials : init?.credentials) ?? 'same-origin';
41
+ }
42
+
43
+ // Remove Origin, according to https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin#description
44
+ if (
45
+ (request.method === 'GET' || request.method === 'HEAD') &&
46
+ ((mode === 'no-cors' && url.origin !== event.url.origin) ||
47
+ url.origin === event.url.origin)
48
+ ) {
49
+ request.headers.delete('origin');
50
+ }
51
+
52
+ if (url.origin !== event.url.origin) {
53
+ // allow cookie passthrough for "same-origin"
54
+ // if SvelteKit is serving my.domain.com:
55
+ // - domain.com WILL NOT receive cookies
56
+ // - my.domain.com WILL receive cookies
57
+ // - api.domain.dom WILL NOT receive cookies
58
+ // - sub.my.domain.com WILL receive cookies
59
+ // ports do not affect the resolution
60
+ // leading dot prevents mydomain.com matching domain.com
61
+ if (`.${url.hostname}`.endsWith(`.${event.url.hostname}`) && credentials !== 'omit') {
62
+ const cookie = get_cookie_header(url, request.headers.get('cookie'));
63
+ if (cookie) request.headers.set('cookie', cookie);
64
+ }
65
+
66
+ let response = await fetch(request);
67
+
68
+ if (mode === 'no-cors') {
69
+ response = new Response('', {
70
+ status: response.status,
71
+ statusText: response.statusText,
72
+ headers: response.headers
73
+ });
74
+ }
75
+
76
+ return response;
77
+ }
78
+
79
+ /** @type {Response} */
80
+ let response;
81
+
82
+ // handle fetch requests for static assets. e.g. prebaked data, etc.
83
+ // we need to support everything the browser's fetch supports
84
+ const prefix = options.paths.assets || options.paths.base;
85
+ const decoded = decodeURIComponent(url.pathname);
86
+ const filename = (
87
+ decoded.startsWith(prefix) ? decoded.slice(prefix.length) : decoded
88
+ ).slice(1);
89
+ const filename_html = `${filename}/index.html`; // path may also match path/index.html
90
+
91
+ const is_asset = options.manifest.assets.has(filename);
92
+ const is_asset_html = options.manifest.assets.has(filename_html);
93
+
94
+ if (is_asset || is_asset_html) {
95
+ const file = is_asset ? filename : filename_html;
96
+
97
+ if (options.read) {
98
+ const type = is_asset
99
+ ? options.manifest.mimeTypes[filename.slice(filename.lastIndexOf('.'))]
100
+ : 'text/html';
101
+
102
+ return new Response(options.read(file), {
103
+ headers: type ? { 'content-type': type } : {}
104
+ });
105
+ }
106
+
107
+ return await fetch(request);
108
+ }
109
+
110
+ if (credentials !== 'omit') {
111
+ const cookie = get_cookie_header(url, request.headers.get('cookie'));
112
+ if (cookie) {
113
+ request.headers.set('cookie', cookie);
114
+ }
115
+
116
+ const authorization = event.request.headers.get('authorization');
117
+ if (authorization && !request.headers.has('authorization')) {
118
+ request.headers.set('authorization', authorization);
119
+ }
120
+ }
121
+
122
+ if (request_body && typeof request_body !== 'string' && !ArrayBuffer.isView(request_body)) {
123
+ // TODO is this still necessary? we just bail out below
124
+ // per https://developer.mozilla.org/en-US/docs/Web/API/Request/Request, this can be a
125
+ // Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object.
126
+ // non-string bodies are irksome to deal with, but luckily aren't particularly useful
127
+ // in this context anyway, so we take the easy route and ban them
128
+ throw new Error('Request body must be a string or TypedArray');
129
+ }
130
+
131
+ if (!request.headers.has('accept')) {
132
+ request.headers.set('accept', '*/*');
133
+ }
134
+
135
+ if (!request.headers.has('accept-language')) {
136
+ request.headers.set(
137
+ 'accept-language',
138
+ /** @type {string} */ (event.request.headers.get('accept-language'))
139
+ );
140
+ }
141
+
142
+ response = await respond(request, options, state);
143
+
144
+ const set_cookie = response.headers.get('set-cookie');
145
+ if (set_cookie) {
146
+ for (const str of set_cookie_parser.splitCookiesString(set_cookie)) {
147
+ const { name, value, ...options } = set_cookie_parser.parseString(str);
148
+
149
+ // options.sameSite is string, something more specific is required - type cast is safe
150
+ event.cookies.set(
151
+ name,
152
+ value,
153
+ /** @type {import('cookie').CookieSerializeOptions} */ (options)
154
+ );
155
+ }
156
+ }
157
+
158
+ return response;
159
+ }
160
+ });
161
+ };
162
+ }
163
+
164
+ /**
165
+ * @param {RequestInfo | URL} info
166
+ * @param {RequestInit | undefined} init
167
+ * @param {URL} url
168
+ */
169
+ function normalize_fetch_input(info, init, url) {
170
+ if (info instanceof Request) {
171
+ return info;
172
+ }
173
+
174
+ return new Request(typeof info === 'string' ? new URL(info, url) : info, init);
175
+ }