@sveltejs/kit 2.27.3 → 2.29.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 (37) hide show
  1. package/package.json +1 -1
  2. package/src/core/config/index.js +11 -0
  3. package/src/core/config/options.js +30 -11
  4. package/src/core/postbuild/prerender.js +5 -0
  5. package/src/exports/index.js +3 -4
  6. package/src/exports/public.d.ts +40 -11
  7. package/src/exports/vite/build/build_remote.js +4 -4
  8. package/src/exports/vite/index.js +121 -70
  9. package/src/exports/vite/static_analysis/utils.js +101 -0
  10. package/src/exports/vite/utils.js +16 -0
  11. package/src/runtime/app/server/event.js +1 -0
  12. package/src/runtime/app/server/index.js +3 -2
  13. package/src/runtime/app/server/remote/command.js +5 -0
  14. package/src/runtime/app/server/remote/form.js +10 -0
  15. package/src/runtime/client/client.js +2 -2
  16. package/src/runtime/client/fetcher.js +3 -2
  17. package/src/runtime/client/remote-functions/command.svelte.js +91 -0
  18. package/src/runtime/client/remote-functions/form.svelte.js +29 -2
  19. package/src/runtime/client/remote-functions/index.js +1 -1
  20. package/src/runtime/client/remote-functions/shared.svelte.js +11 -14
  21. package/src/runtime/server/cookie.js +2 -1
  22. package/src/runtime/server/data/index.js +3 -4
  23. package/src/runtime/server/page/crypto.js +3 -58
  24. package/src/runtime/server/page/csp.js +2 -2
  25. package/src/runtime/server/page/load_data.js +6 -5
  26. package/src/runtime/server/page/render.js +3 -4
  27. package/src/runtime/server/remote.js +29 -33
  28. package/src/runtime/shared.js +8 -12
  29. package/src/runtime/utils.js +43 -33
  30. package/src/types/internal.d.ts +1 -1
  31. package/src/version.js +1 -1
  32. package/types/index.d.ts +42 -15
  33. package/types/index.d.ts.map +1 -1
  34. package/src/exports/vite/graph_analysis/index.js +0 -87
  35. package/src/exports/vite/graph_analysis/types.d.ts +0 -5
  36. package/src/exports/vite/graph_analysis/utils.js +0 -6
  37. package/src/runtime/client/remote-functions/command.js +0 -71
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Check if a match position is within a comment or a string
3
+ * @param {string} content - The full content
4
+ * @param {number} match_index - The index where the match starts
5
+ * @returns {boolean} - True if the match is within a comment
6
+ */
7
+ export function should_ignore(content, match_index) {
8
+ // Track if we're inside different types of quotes and comments
9
+ let in_single_quote = false;
10
+ let in_double_quote = false;
11
+ let in_template_literal = false;
12
+ let in_single_line_comment = false;
13
+ let in_multi_line_comment = false;
14
+ let in_html_comment = false;
15
+
16
+ for (let i = 0; i < match_index; i++) {
17
+ const char = content[i];
18
+ const next_two = content.slice(i, i + 2);
19
+ const next_four = content.slice(i, i + 4);
20
+
21
+ // Handle end of single line comment
22
+ if (in_single_line_comment && char === '\n') {
23
+ in_single_line_comment = false;
24
+ continue;
25
+ }
26
+
27
+ // Handle end of multi-line comment
28
+ if (in_multi_line_comment && next_two === '*/') {
29
+ in_multi_line_comment = false;
30
+ i++; // Skip the '/' part
31
+ continue;
32
+ }
33
+
34
+ // Handle end of HTML comment
35
+ if (in_html_comment && content.slice(i, i + 3) === '-->') {
36
+ in_html_comment = false;
37
+ i += 2; // Skip the '-->' part
38
+ continue;
39
+ }
40
+
41
+ // If we're in any comment, skip processing
42
+ if (in_single_line_comment || in_multi_line_comment || in_html_comment) {
43
+ continue;
44
+ }
45
+
46
+ // Handle escape sequences in strings
47
+ if ((in_single_quote || in_double_quote || in_template_literal) && char === '\\') {
48
+ i++; // Skip the escaped character
49
+ continue;
50
+ }
51
+
52
+ // Handle string boundaries
53
+ if (!in_double_quote && !in_template_literal && char === "'") {
54
+ in_single_quote = !in_single_quote;
55
+ continue;
56
+ }
57
+
58
+ if (!in_single_quote && !in_template_literal && char === '"') {
59
+ in_double_quote = !in_double_quote;
60
+ continue;
61
+ }
62
+
63
+ if (!in_single_quote && !in_double_quote && char === '`') {
64
+ in_template_literal = !in_template_literal;
65
+ continue;
66
+ }
67
+
68
+ // If we're inside any string, don't process comment delimiters
69
+ if (in_single_quote || in_double_quote || in_template_literal) {
70
+ continue;
71
+ }
72
+
73
+ // Check for comment starts
74
+ if (next_two === '//') {
75
+ in_single_line_comment = true;
76
+ i++; // Skip the second '/'
77
+ continue;
78
+ }
79
+
80
+ if (next_two === '/*') {
81
+ in_multi_line_comment = true;
82
+ i++; // Skip the '*'
83
+ continue;
84
+ }
85
+
86
+ if (next_four === '<!--') {
87
+ in_html_comment = true;
88
+ i += 3; // Skip the '<!--'
89
+ continue;
90
+ }
91
+ }
92
+
93
+ return (
94
+ in_single_line_comment ||
95
+ in_multi_line_comment ||
96
+ in_html_comment ||
97
+ in_single_quote ||
98
+ in_double_quote ||
99
+ in_template_literal
100
+ );
101
+ }
@@ -113,6 +113,8 @@ export function not_found(req, res, base) {
113
113
  }
114
114
  }
115
115
 
116
+ const query_pattern = /\?.*$/s;
117
+
116
118
  /**
117
119
  * Removes cwd/lib path from the start of the id
118
120
  * @param {string} id
@@ -120,6 +122,8 @@ export function not_found(req, res, base) {
120
122
  * @param {string} cwd
121
123
  */
122
124
  export function normalize_id(id, lib, cwd) {
125
+ id = id.replace(query_pattern, '');
126
+
123
127
  if (id.startsWith(lib)) {
124
128
  id = id.replace(lib, '$lib');
125
129
  }
@@ -155,4 +159,16 @@ export function normalize_id(id, lib, cwd) {
155
159
  return posixify(id);
156
160
  }
157
161
 
162
+ /**
163
+ * For times when you need to throw an error, but without
164
+ * displaying a useless stack trace (since the developer
165
+ * can't do anything useful with it)
166
+ * @param {string} message
167
+ */
168
+ export function stackless(message) {
169
+ const error = new Error(message);
170
+ error.stack = '';
171
+ return error;
172
+ }
173
+
158
174
  export const strip_virtual_prefix = /** @param {string} id */ (id) => id.replace('\0virtual:', '');
@@ -19,6 +19,7 @@ import('node:async_hooks')
19
19
  *
20
20
  * In environments without [`AsyncLocalStorage`](https://nodejs.org/api/async_context.html#class-asynclocalstorage), this must be called synchronously (i.e. not after an `await`).
21
21
  * @since 2.20.0
22
+ * @returns {RequestEvent}
22
23
  */
23
24
  export function getRequestEvent() {
24
25
  const event = request_event ?? als?.getStore();
@@ -1,7 +1,7 @@
1
1
  import { read_implementation, manifest } from '__sveltekit/server';
2
2
  import { base } from '__sveltekit/paths';
3
3
  import { DEV } from 'esm-env';
4
- import { b64_decode } from '../../utils.js';
4
+ import { base64_decode } from '../../utils.js';
5
5
 
6
6
  /**
7
7
  * Read the contents of an imported asset from the filesystem
@@ -33,8 +33,9 @@ export function read(asset) {
33
33
  const data = asset.slice(match[0].length);
34
34
 
35
35
  if (match[2] !== undefined) {
36
- const decoded = b64_decode(data);
36
+ const decoded = base64_decode(data);
37
37
 
38
+ // @ts-ignore passing a Uint8Array to `new Response(...)` is fine
38
39
  return new Response(decoded, {
39
40
  headers: {
40
41
  'Content-Length': decoded.byteLength.toString(),
@@ -87,5 +87,10 @@ export function command(validate_or_fn, maybe_fn) {
87
87
 
88
88
  Object.defineProperty(wrapper, '__', { value: __ });
89
89
 
90
+ // On the server, pending is always 0
91
+ Object.defineProperty(wrapper, 'pending', {
92
+ get: () => 0
93
+ });
94
+
90
95
  return wrapper;
91
96
  }
@@ -97,6 +97,16 @@ export function form(fn) {
97
97
  }
98
98
  });
99
99
 
100
+ // On the server, pending is always 0
101
+ Object.defineProperty(instance, 'pending', {
102
+ get: () => 0
103
+ });
104
+
105
+ // On the server, buttonProps.pending is always 0
106
+ Object.defineProperty(button_props, 'pending', {
107
+ get: () => 0
108
+ });
109
+
100
110
  if (key == undefined) {
101
111
  Object.defineProperty(instance, 'for', {
102
112
  /** @type {RemoteForm<any>['for']} */
@@ -44,6 +44,7 @@ import { get_message, get_status } from '../../utils/error.js';
44
44
  import { writable } from 'svelte/store';
45
45
  import { page, update, navigating } from './state.svelte.js';
46
46
  import { add_data_suffix, add_resolution_suffix } from '../pathname.js';
47
+ import { text_decoder } from '../utils.js';
47
48
 
48
49
  export { load_css };
49
50
  const ICON_REL_ATTRIBUTES = new Set(['icon', 'shortcut icon', 'apple-touch-icon']);
@@ -2781,7 +2782,6 @@ async function load_data(url, invalid) {
2781
2782
  */
2782
2783
  const deferreds = new Map();
2783
2784
  const reader = /** @type {ReadableStream<Uint8Array>} */ (res.body).getReader();
2784
- const decoder = new TextDecoder();
2785
2785
 
2786
2786
  /**
2787
2787
  * @param {any} data
@@ -2804,7 +2804,7 @@ async function load_data(url, invalid) {
2804
2804
  const { done, value } = await reader.read();
2805
2805
  if (done && !text) break;
2806
2806
 
2807
- text += !value && text ? '\n' : decoder.decode(value, { stream: true }); // no value -> final chunk -> add a new line to trigger the last parse
2807
+ text += !value && text ? '\n' : text_decoder.decode(value, { stream: true }); // no value -> final chunk -> add a new line to trigger the last parse
2808
2808
 
2809
2809
  while (true) {
2810
2810
  const split = text.indexOf('\n');
@@ -1,6 +1,6 @@
1
1
  import { BROWSER, DEV } from 'esm-env';
2
2
  import { hash } from '../../utils/hash.js';
3
- import { b64_decode } from '../utils.js';
3
+ import { base64_decode } from '../utils.js';
4
4
 
5
5
  let loading = 0;
6
6
 
@@ -90,6 +90,7 @@ export function initial_fetch(resource, opts) {
90
90
 
91
91
  const script = document.querySelector(selector);
92
92
  if (script?.textContent) {
93
+ script.remove(); // In case multiple script tags match the same selector
93
94
  let { body, ...init } = JSON.parse(script.textContent);
94
95
 
95
96
  const ttl = script.getAttribute('data-ttl');
@@ -98,7 +99,7 @@ export function initial_fetch(resource, opts) {
98
99
  if (b64 !== null) {
99
100
  // Can't use native_fetch('data:...;base64,${body}')
100
101
  // csp can block the request
101
- body = b64_decode(body);
102
+ body = base64_decode(body);
102
103
  }
103
104
 
104
105
  return Promise.resolve(new Response(body, init));
@@ -0,0 +1,91 @@
1
+ /** @import { RemoteCommand, RemoteQueryOverride } from '@sveltejs/kit' */
2
+ /** @import { RemoteFunctionResponse } from 'types' */
3
+ /** @import { Query } from './query.svelte.js' */
4
+ import { app_dir, base } from '__sveltekit/paths';
5
+ import * as devalue from 'devalue';
6
+ import { HttpError } from '@sveltejs/kit/internal';
7
+ import { app } from '../client.js';
8
+ import { stringify_remote_arg } from '../../shared.js';
9
+ import { refresh_queries, release_overrides } from './shared.svelte.js';
10
+
11
+ /**
12
+ * Client-version of the `command` function from `$app/server`.
13
+ * @param {string} id
14
+ * @returns {RemoteCommand<any, any>}
15
+ */
16
+ export function command(id) {
17
+ /** @type {number} */
18
+ let pending_count = $state(0);
19
+
20
+ // Careful: This function MUST be synchronous (can't use the async keyword) because the return type has to be a promise with an updates() method.
21
+ // If we make it async, the return type will be a promise that resolves to a promise with an updates() method, which is not what we want.
22
+ /** @type {RemoteCommand<any, any>} */
23
+ const command_function = (arg) => {
24
+ /** @type {Array<Query<any> | RemoteQueryOverride>} */
25
+ let updates = [];
26
+
27
+ // Increment pending count when command starts
28
+ pending_count++;
29
+
30
+ /** @type {Promise<any> & { updates: (...args: any[]) => any }} */
31
+ const promise = (async () => {
32
+ try {
33
+ // Wait a tick to give room for the `updates` method to be called
34
+ await Promise.resolve();
35
+
36
+ const response = await fetch(`${base}/${app_dir}/remote/${id}`, {
37
+ method: 'POST',
38
+ body: JSON.stringify({
39
+ payload: stringify_remote_arg(arg, app.hooks.transport),
40
+ refreshes: updates.map((u) => u._key)
41
+ }),
42
+ headers: {
43
+ 'Content-Type': 'application/json'
44
+ }
45
+ });
46
+
47
+ if (!response.ok) {
48
+ release_overrides(updates);
49
+ // We only end up here in case of a network error or if the server has an internal error
50
+ // (which shouldn't happen because we handle errors on the server and always send a 200 response)
51
+ throw new Error('Failed to execute remote function');
52
+ }
53
+
54
+ const result = /** @type {RemoteFunctionResponse} */ (await response.json());
55
+ if (result.type === 'redirect') {
56
+ release_overrides(updates);
57
+ throw new Error(
58
+ 'Redirects are not allowed in commands. Return a result instead and use goto on the client'
59
+ );
60
+ } else if (result.type === 'error') {
61
+ release_overrides(updates);
62
+ throw new HttpError(result.status ?? 500, result.error);
63
+ } else {
64
+ if (result.refreshes) {
65
+ refresh_queries(result.refreshes, updates);
66
+ }
67
+
68
+ return devalue.parse(result.result, app.decoders);
69
+ }
70
+ } finally {
71
+ // Decrement pending count when command completes
72
+ pending_count--;
73
+ }
74
+ })();
75
+
76
+ promise.updates = (/** @type {any} */ ...args) => {
77
+ updates = args;
78
+ // @ts-expect-error Don't allow updates to be called multiple times
79
+ delete promise.updates;
80
+ return promise;
81
+ };
82
+
83
+ return promise;
84
+ };
85
+
86
+ Object.defineProperty(command_function, 'pending', {
87
+ get: () => pending_count
88
+ });
89
+
90
+ return command_function;
91
+ }
@@ -5,7 +5,14 @@ import { app_dir, base } from '__sveltekit/paths';
5
5
  import * as devalue from 'devalue';
6
6
  import { DEV } from 'esm-env';
7
7
  import { HttpError } from '@sveltejs/kit/internal';
8
- import { app, remote_responses, started, goto, set_nearest_error_page } from '../client.js';
8
+ import {
9
+ app,
10
+ remote_responses,
11
+ started,
12
+ goto,
13
+ set_nearest_error_page,
14
+ invalidateAll
15
+ } from '../client.js';
9
16
  import { tick } from 'svelte';
10
17
  import { refresh_queries, release_overrides } from './shared.svelte.js';
11
18
 
@@ -27,6 +34,9 @@ export function form(id) {
27
34
  /** @type {any} */
28
35
  let result = $state(started ? undefined : remote_responses[action_id]);
29
36
 
37
+ /** @type {number} */
38
+ let pending_count = $state(0);
39
+
30
40
  /**
31
41
  * @param {FormData} data
32
42
  * @returns {Promise<any> & { updates: (...args: any[]) => any }}
@@ -42,6 +52,9 @@ export function form(id) {
42
52
  entry.count++;
43
53
  }
44
54
 
55
+ // Increment pending count when submission starts
56
+ pending_count++;
57
+
45
58
  /** @type {Array<Query<any> | RemoteQueryOverride>} */
46
59
  let updates = [];
47
60
 
@@ -78,7 +91,11 @@ export function form(id) {
78
91
  if (form_result.type === 'result') {
79
92
  result = devalue.parse(form_result.result, app.decoders);
80
93
 
81
- refresh_queries(form_result.refreshes, updates);
94
+ if (form_result.refreshes) {
95
+ refresh_queries(form_result.refreshes, updates);
96
+ } else {
97
+ void invalidateAll();
98
+ }
82
99
  } else if (form_result.type === 'redirect') {
83
100
  const refreshes = form_result.refreshes ?? '';
84
101
  const invalidateAll = !refreshes && updates.length === 0;
@@ -94,6 +111,9 @@ export function form(id) {
94
111
  release_overrides(updates);
95
112
  throw e;
96
113
  } finally {
114
+ // Decrement pending count when submission completes
115
+ pending_count--;
116
+
97
117
  void tick().then(() => {
98
118
  if (entry) {
99
119
  entry.count--;
@@ -242,6 +262,10 @@ export function form(id) {
242
262
  }
243
263
  });
244
264
 
265
+ Object.defineProperty(button_props, 'pending', {
266
+ get: () => pending_count
267
+ });
268
+
245
269
  Object.defineProperties(instance, {
246
270
  buttonProps: {
247
271
  value: button_props
@@ -249,6 +273,9 @@ export function form(id) {
249
273
  result: {
250
274
  get: () => result
251
275
  },
276
+ pending: {
277
+ get: () => pending_count
278
+ },
252
279
  enhance: {
253
280
  /** @type {RemoteForm<any>['enhance']} */
254
281
  value: (callback) => {
@@ -1,4 +1,4 @@
1
- export { command } from './command.js';
1
+ export { command } from './command.svelte.js';
2
2
  export { form } from './form.svelte.js';
3
3
  export { prerender } from './prerender.svelte.js';
4
4
  export { query } from './query.svelte.js';
@@ -2,7 +2,7 @@
2
2
  /** @import { RemoteFunctionResponse } from 'types' */
3
3
  /** @import { Query } from './query.svelte.js' */
4
4
  import * as devalue from 'devalue';
5
- import { app, goto, invalidateAll, query_map } from '../client.js';
5
+ import { app, goto, query_map } from '../client.js';
6
6
  import { HttpError, Redirect } from '@sveltejs/kit/internal';
7
7
  import { tick } from 'svelte';
8
8
  import { create_remote_cache_key, stringify_remote_arg } from '../../shared.js';
@@ -125,19 +125,16 @@ export function release_overrides(updates) {
125
125
  */
126
126
  export function refresh_queries(stringified_refreshes, updates = []) {
127
127
  const refreshes = Object.entries(devalue.parse(stringified_refreshes, app.decoders));
128
- if (refreshes.length > 0) {
129
- // `refreshes` is a superset of `updates`
130
- for (const [key, value] of refreshes) {
131
- // If there was an optimistic update, release it right before we update the query
132
- const update = updates.find((u) => u._key === key);
133
- if (update && 'release' in update) {
134
- update.release();
135
- }
136
- // Update the query with the new value
137
- const entry = query_map.get(key);
138
- entry?.resource.set(value);
128
+
129
+ // `refreshes` is a superset of `updates`
130
+ for (const [key, value] of refreshes) {
131
+ // If there was an optimistic update, release it right before we update the query
132
+ const update = updates.find((u) => u._key === key);
133
+ if (update && 'release' in update) {
134
+ update.release();
139
135
  }
140
- } else {
141
- void invalidateAll();
136
+ // Update the query with the new value
137
+ const entry = query_map.get(key);
138
+ entry?.resource.set(value);
142
139
  }
143
140
  }
@@ -1,6 +1,7 @@
1
1
  import { parse, serialize } from 'cookie';
2
2
  import { normalize_path, resolve } from '../../utils/url.js';
3
3
  import { add_data_suffix } from '../pathname.js';
4
+ import { text_encoder } from '../utils.js';
4
5
 
5
6
  // eslint-disable-next-line no-control-regex -- control characters are invalid in cookie names
6
7
  const INVALID_COOKIE_CHARACTER_REGEX = /[\x00-\x1F\x7F()<>@,;:"/[\]?={} \t]/;
@@ -217,7 +218,7 @@ export function get_cookies(request, url) {
217
218
 
218
219
  if (__SVELTEKIT_DEV__) {
219
220
  const serialized = serialize(name, value, new_cookies[name].options);
220
- if (new TextEncoder().encode(serialized).byteLength > MAX_COOKIE_SIZE) {
221
+ if (text_encoder.encode(serialized).byteLength > MAX_COOKIE_SIZE) {
221
222
  throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
222
223
  }
223
224
 
@@ -7,8 +7,7 @@ import { clarify_devalue_error, handle_error_and_jsonify, serialize_uses } from
7
7
  import { normalize_path } from '../../../utils/url.js';
8
8
  import * as devalue from 'devalue';
9
9
  import { create_async_iterator } from '../../../utils/streaming.js';
10
-
11
- const encoder = new TextEncoder();
10
+ import { text_encoder } from '../../utils.js';
12
11
 
13
12
  /**
14
13
  * @param {import('@sveltejs/kit').RequestEvent} event
@@ -129,9 +128,9 @@ export async function render_data(
129
128
  return new Response(
130
129
  new ReadableStream({
131
130
  async start(controller) {
132
- controller.enqueue(encoder.encode(data));
131
+ controller.enqueue(text_encoder.encode(data));
133
132
  for await (const chunk of chunks) {
134
- controller.enqueue(encoder.encode(chunk));
133
+ controller.enqueue(text_encoder.encode(chunk));
135
134
  }
136
135
  controller.close();
137
136
  },
@@ -1,4 +1,4 @@
1
- const encoder = new TextEncoder();
1
+ import { text_encoder } from '../../utils.js';
2
2
 
3
3
  /**
4
4
  * SHA-256 hashing function adapted from https://bitwiseshiftleft.github.io/sjcl
@@ -102,7 +102,7 @@ export function sha256(data) {
102
102
  const bytes = new Uint8Array(out.buffer);
103
103
  reverse_endianness(bytes);
104
104
 
105
- return base64(bytes);
105
+ return btoa(String.fromCharCode(...bytes));
106
106
  }
107
107
 
108
108
  /** The SHA-256 initialization vector */
@@ -160,7 +160,7 @@ function reverse_endianness(bytes) {
160
160
 
161
161
  /** @param {string} str */
162
162
  function encode(str) {
163
- const encoded = encoder.encode(str);
163
+ const encoded = text_encoder.encode(str);
164
164
  const length = encoded.length * 8;
165
165
 
166
166
  // result should be a multiple of 512 bits in length,
@@ -182,58 +182,3 @@ function encode(str) {
182
182
 
183
183
  return words;
184
184
  }
185
-
186
- /*
187
- Based on https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
188
-
189
- MIT License
190
- Copyright (c) 2020 Egor Nepomnyaschih
191
- Permission is hereby granted, free of charge, to any person obtaining a copy
192
- of this software and associated documentation files (the "Software"), to deal
193
- in the Software without restriction, including without limitation the rights
194
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
195
- copies of the Software, and to permit persons to whom the Software is
196
- furnished to do so, subject to the following conditions:
197
- The above copyright notice and this permission notice shall be included in all
198
- copies or substantial portions of the Software.
199
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
200
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
201
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
202
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
203
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
204
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
205
- SOFTWARE.
206
- */
207
- const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split('');
208
-
209
- /** @param {Uint8Array} bytes */
210
- export function base64(bytes) {
211
- const l = bytes.length;
212
-
213
- let result = '';
214
- let i;
215
-
216
- for (i = 2; i < l; i += 3) {
217
- result += chars[bytes[i - 2] >> 2];
218
- result += chars[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
219
- result += chars[((bytes[i - 1] & 0x0f) << 2) | (bytes[i] >> 6)];
220
- result += chars[bytes[i] & 0x3f];
221
- }
222
-
223
- if (i === l + 1) {
224
- // 1 octet yet to write
225
- result += chars[bytes[i - 2] >> 2];
226
- result += chars[(bytes[i - 2] & 0x03) << 4];
227
- result += '==';
228
- }
229
-
230
- if (i === l) {
231
- // 2 octets yet to write
232
- result += chars[bytes[i - 2] >> 2];
233
- result += chars[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
234
- result += chars[(bytes[i - 1] & 0x0f) << 2];
235
- result += '=';
236
- }
237
-
238
- return result;
239
- }
@@ -1,11 +1,11 @@
1
1
  import { escape_html } from '../../../utils/escape.js';
2
- import { base64, sha256 } from './crypto.js';
2
+ import { sha256 } from './crypto.js';
3
3
 
4
4
  const array = new Uint8Array(16);
5
5
 
6
6
  function generate_nonce() {
7
7
  crypto.getRandomValues(array);
8
- return base64(array);
8
+ return btoa(String.fromCharCode(...array));
9
9
  }
10
10
 
11
11
  const quoted = new Set([
@@ -1,7 +1,7 @@
1
1
  import { DEV } from 'esm-env';
2
2
  import { disable_search, make_trackable } from '../../../utils/url.js';
3
3
  import { validate_depends } from '../../shared.js';
4
- import { b64_encode } from '../../utils.js';
4
+ import { base64_encode, text_decoder } from '../../utils.js';
5
5
  import { with_event } from '../../app/server/event.js';
6
6
 
7
7
  /**
@@ -316,12 +316,14 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
316
316
  return async () => {
317
317
  const buffer = await response.arrayBuffer();
318
318
 
319
+ const bytes = new Uint8Array(buffer);
320
+
319
321
  if (dependency) {
320
- dependency.body = new Uint8Array(buffer);
322
+ dependency.body = bytes;
321
323
  }
322
324
 
323
325
  if (buffer instanceof ArrayBuffer) {
324
- await push_fetched(b64_encode(buffer), true);
326
+ await push_fetched(base64_encode(bytes), true);
325
327
  }
326
328
 
327
329
  return buffer;
@@ -394,13 +396,12 @@ export function create_universal_fetch(event, state, fetched, csr, resolve_opts)
394
396
  async function stream_to_string(stream) {
395
397
  let result = '';
396
398
  const reader = stream.getReader();
397
- const decoder = new TextDecoder();
398
399
  while (true) {
399
400
  const { done, value } = await reader.read();
400
401
  if (done) {
401
402
  break;
402
403
  }
403
- result += decoder.decode(value);
404
+ result += text_decoder.decode(value);
404
405
  }
405
406
  return result;
406
407
  }
@@ -17,6 +17,7 @@ import { create_server_routing_response, generate_route_object } from './server_
17
17
  import { add_resolution_suffix } from '../../pathname.js';
18
18
  import { with_event } from '../../app/server/event.js';
19
19
  import { get_event_state } from '../event-state.js';
20
+ import { text_encoder } from '../../utils.js';
20
21
 
21
22
  // TODO rename this function/module
22
23
 
@@ -25,8 +26,6 @@ const updated = {
25
26
  check: () => false
26
27
  };
27
28
 
28
- const encoder = new TextEncoder();
29
-
30
29
  /**
31
30
  * Creates the HTML response.
32
31
  * @param {{
@@ -586,9 +585,9 @@ export async function render_response({
586
585
  : new Response(
587
586
  new ReadableStream({
588
587
  async start(controller) {
589
- controller.enqueue(encoder.encode(transformed + '\n'));
588
+ controller.enqueue(text_encoder.encode(transformed + '\n'));
590
589
  for await (const chunk of chunks) {
591
- controller.enqueue(encoder.encode(chunk));
590
+ controller.enqueue(text_encoder.encode(chunk));
592
591
  }
593
592
  controller.close();
594
593
  },