@sveltejs/kit 3.0.0-next.7 → 3.0.0-next.9
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.
- package/package.json +6 -3
- package/src/core/config/index.js +1 -2
- package/src/core/config/options.js +5 -2
- package/src/core/env.js +1 -0
- package/src/core/sync/create_manifest_data/index.js +1 -1
- package/src/core/sync/sync.js +0 -2
- package/src/core/sync/write_client_manifest.js +0 -7
- package/src/core/sync/write_non_ambient.js +2 -2
- package/src/core/sync/write_server.js +0 -3
- package/src/core/sync/write_tsconfig.js +14 -4
- package/src/core/sync/write_tsconfig_test/package.json +7 -0
- package/src/core/sync/write_types/index.js +17 -14
- package/src/core/utils.js +2 -2
- package/src/exports/hooks/sequence.js +3 -2
- package/src/exports/index.js +1 -1
- package/src/exports/node/index.js +4 -8
- package/src/exports/public.d.ts +22 -23
- package/src/exports/vite/dev/index.js +4 -7
- package/src/exports/vite/index.js +110 -59
- package/src/exports/vite/preview/index.js +13 -14
- package/src/exports/vite/utils.js +14 -14
- package/src/runtime/app/env/internal.js +4 -4
- package/src/runtime/app/environment/index.js +3 -3
- package/src/runtime/app/forms.js +2 -2
- package/src/runtime/app/paths/internal/client.js +4 -2
- package/src/runtime/app/paths/internal/server.js +2 -23
- package/src/runtime/app/paths/server.js +2 -2
- package/src/runtime/app/server/remote/prerender.js +1 -2
- package/src/runtime/client/bundle.js +1 -1
- package/src/runtime/client/client-entry.js +3 -0
- package/src/runtime/client/client.js +217 -172
- package/src/runtime/client/entry.js +24 -3
- package/src/runtime/client/payload.js +17 -0
- package/src/runtime/client/remote-functions/form.svelte.js +6 -6
- package/src/runtime/client/state.svelte.js +0 -1
- package/src/runtime/client/types.d.ts +2 -6
- package/src/runtime/components/root.svelte +66 -0
- package/src/runtime/env/dynamic/private.js +7 -0
- package/src/runtime/env/dynamic/public.js +7 -0
- package/src/runtime/env/static/private.js +6 -0
- package/src/runtime/env/static/public.js +6 -0
- package/src/runtime/form-utils.js +1 -4
- package/src/runtime/server/cookie.js +51 -23
- package/src/runtime/server/csrf.js +1 -1
- package/src/runtime/server/data/index.js +8 -12
- package/src/runtime/server/page/index.js +7 -14
- package/src/runtime/server/page/render.js +71 -78
- package/src/runtime/server/remote.js +2 -1
- package/src/runtime/server/respond.js +1 -1
- package/src/runtime/server/utils.js +28 -5
- package/src/runtime/types.d.ts +8 -0
- package/src/types/global-private.d.ts +10 -17
- package/src/types/internal.d.ts +25 -26
- package/src/utils/import.js +6 -1
- package/src/utils/imports.js +83 -0
- package/src/utils/routing.js +6 -6
- package/src/version.js +1 -1
- package/types/index.d.ts +34 -285
- package/types/index.d.ts.map +3 -26
- package/src/core/sync/write_root.js +0 -127
- package/src/types/synthetic/$lib.md +0 -5
|
@@ -1,3 +1,24 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/* in development or if `bundleStrategy` is 'split', this file is used as the entry point */
|
|
2
|
+
|
|
3
|
+
import { set_payload } from './payload.js';
|
|
4
|
+
|
|
5
|
+
/** @type {Promise<typeof import('./client-entry.js')>} */
|
|
6
|
+
let client;
|
|
7
|
+
|
|
8
|
+
/** @param {import('types').SvelteKitPayload} payload */
|
|
9
|
+
export function init(payload) {
|
|
10
|
+
set_payload(payload);
|
|
11
|
+
// Importing the client after setting the payload ensures that modules such as
|
|
12
|
+
// `$app/paths` can read it during initialization.
|
|
13
|
+
client ??= import('./client-entry.js');
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** @param {Parameters<import('./client-entry.js').start>} args */
|
|
17
|
+
export async function start(...args) {
|
|
18
|
+
return (await client).start(...args);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** @param {Parameters<import('./client-entry.js').load_css>} args */
|
|
22
|
+
export async function load_css(...args) {
|
|
23
|
+
return (await client).load_css(...args);
|
|
24
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** @import {SvelteKitPayload} from 'types'; */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Code inside the SvelteKit client runtime should only use this, not the global,
|
|
5
|
+
* so that the file hashes stay stable between rebuilds as long as the SvelteKit runtime doesn't change
|
|
6
|
+
*/
|
|
7
|
+
export let payload = __SVELTEKIT_PAYLOAD__ ?? /** @type {SvelteKitPayload} */ ({});
|
|
8
|
+
|
|
9
|
+
/** @param {SvelteKitPayload} value */
|
|
10
|
+
export function set_payload(value) {
|
|
11
|
+
payload = value;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// this makes Vite inject its dev client code as this module's first dependency
|
|
15
|
+
// so that global constant replacements are done before any other module evaluates.
|
|
16
|
+
// For build, it's inert.
|
|
17
|
+
import.meta.hot;
|
|
@@ -8,8 +8,8 @@ import {
|
|
|
8
8
|
query_responses,
|
|
9
9
|
_goto,
|
|
10
10
|
set_nearest_error_page,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
handle_error,
|
|
12
|
+
refreshAll
|
|
13
13
|
} from '../client.js';
|
|
14
14
|
import { tick } from 'svelte';
|
|
15
15
|
import { categorize_updates, remote_request } from './shared.svelte.js';
|
|
@@ -227,14 +227,14 @@ export function form(id) {
|
|
|
227
227
|
|
|
228
228
|
// if the developer took control of updates via `.updates(...)` (even with
|
|
229
229
|
// no arguments), or the server performed explicit refreshes, don't invalidateAll
|
|
230
|
-
const
|
|
230
|
+
const should_refresh = refreshes === null && !response.r;
|
|
231
231
|
|
|
232
232
|
if (response.redirect) {
|
|
233
233
|
// Use internal version to allow redirects to external URLs
|
|
234
234
|
void _goto(
|
|
235
235
|
response.redirect,
|
|
236
236
|
{
|
|
237
|
-
|
|
237
|
+
refreshAll: should_refresh
|
|
238
238
|
},
|
|
239
239
|
0
|
|
240
240
|
);
|
|
@@ -244,8 +244,8 @@ export function form(id) {
|
|
|
244
244
|
const succeeded = raw_issues.length === 0;
|
|
245
245
|
|
|
246
246
|
if (succeeded) {
|
|
247
|
-
if (
|
|
248
|
-
void
|
|
247
|
+
if (should_refresh) {
|
|
248
|
+
void refreshAll();
|
|
249
249
|
}
|
|
250
250
|
} else {
|
|
251
251
|
if (DEV) {
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
Uses
|
|
11
11
|
} from 'types';
|
|
12
12
|
import { Page, ParamMatcher } from '@sveltejs/kit';
|
|
13
|
+
import { RenderNode } from '../types.js';
|
|
13
14
|
|
|
14
15
|
export interface SvelteKitApp {
|
|
15
16
|
/**
|
|
@@ -57,8 +58,6 @@ export interface SvelteKitApp {
|
|
|
57
58
|
*/
|
|
58
59
|
hash: boolean;
|
|
59
60
|
|
|
60
|
-
root: typeof SvelteComponent;
|
|
61
|
-
|
|
62
61
|
/**
|
|
63
62
|
* Lazily loads the contents of src/error.html, used as a last-resort
|
|
64
63
|
* error page when the root layout's load function throws during client-side rendering.
|
|
@@ -91,13 +90,10 @@ export type NavigationFinished = {
|
|
|
91
90
|
type: 'loaded';
|
|
92
91
|
state: NavigationState;
|
|
93
92
|
props: {
|
|
94
|
-
constructors: Array<typeof SvelteComponent>;
|
|
95
|
-
errors?: Array<typeof SvelteComponent | undefined>;
|
|
96
|
-
components?: SvelteComponent[];
|
|
97
93
|
page: Page;
|
|
98
94
|
form?: Record<string, any> | null;
|
|
99
95
|
error?: App.Error;
|
|
100
|
-
|
|
96
|
+
tree: RenderNode;
|
|
101
97
|
};
|
|
102
98
|
};
|
|
103
99
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { afterNavigate } from '$app/navigation';
|
|
3
|
+
import type { Page } from '@sveltejs/kit';
|
|
4
|
+
import type { RenderNode } from '../types.js';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
page: Page;
|
|
8
|
+
tree: RenderNode;
|
|
9
|
+
components: any[];
|
|
10
|
+
resetters: Array<(() => void) | undefined>;
|
|
11
|
+
form?: any;
|
|
12
|
+
error?: App.Error;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const { page, components, resetters, tree, form, error }: Props = $props();
|
|
16
|
+
|
|
17
|
+
let mounted = $state(false);
|
|
18
|
+
let navigated = $state(false);
|
|
19
|
+
let title = $state('');
|
|
20
|
+
|
|
21
|
+
afterNavigate(() => {
|
|
22
|
+
if (mounted) {
|
|
23
|
+
navigated = true;
|
|
24
|
+
title = document.title || 'untitled page';
|
|
25
|
+
} else {
|
|
26
|
+
mounted = true;
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
</script>
|
|
30
|
+
|
|
31
|
+
{#snippet node(n: RenderNode, depth: number)}
|
|
32
|
+
{const Component = $derived(n.component)}
|
|
33
|
+
{const Error = $derived(n.error)}
|
|
34
|
+
{const data = $derived(n.data)}
|
|
35
|
+
|
|
36
|
+
<svelte:boundary onerror={(_, reset) => (resetters[depth] = reset)}>
|
|
37
|
+
{#if n.child}
|
|
38
|
+
<!-- svelte-ignore binding_property_non_reactive -->
|
|
39
|
+
<Component bind:this={components[depth]} {data} {form} params={page.params}>
|
|
40
|
+
{@render node(n.child, depth + 1)}
|
|
41
|
+
</Component>
|
|
42
|
+
{:else}
|
|
43
|
+
<!-- svelte-ignore binding_property_non_reactive -->
|
|
44
|
+
<Component bind:this={components[depth]} {data} {form} params={page.params} {error} />
|
|
45
|
+
{/if}
|
|
46
|
+
|
|
47
|
+
{#snippet failed(error)}
|
|
48
|
+
<Error {error} />
|
|
49
|
+
{/snippet}
|
|
50
|
+
</svelte:boundary>
|
|
51
|
+
{/snippet}
|
|
52
|
+
|
|
53
|
+
{@render node(tree, 0)}
|
|
54
|
+
|
|
55
|
+
{#if mounted}
|
|
56
|
+
<div
|
|
57
|
+
id="svelte-announcer"
|
|
58
|
+
aria-live="assertive"
|
|
59
|
+
aria-atomic="true"
|
|
60
|
+
style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px"
|
|
61
|
+
>
|
|
62
|
+
{#if navigated}
|
|
63
|
+
{title}
|
|
64
|
+
{/if}
|
|
65
|
+
</div>
|
|
66
|
+
{/if}
|
|
@@ -150,10 +150,7 @@ export async function deserialize_binary_form(request) {
|
|
|
150
150
|
throw deserialize_error('no body');
|
|
151
151
|
}
|
|
152
152
|
|
|
153
|
-
|
|
154
|
-
const reader = /** @type {ReadableStreamDefaultReader<Uint8Array<ArrayBuffer>>} */ (
|
|
155
|
-
request.body.getReader()
|
|
156
|
-
);
|
|
153
|
+
const reader = request.body.getReader();
|
|
157
154
|
|
|
158
155
|
/** @type {Array<Promise<Uint8Array<ArrayBuffer> | undefined>>} */
|
|
159
156
|
const chunks = [];
|
|
@@ -12,10 +12,11 @@ import { text_encoder } from '../utils.js';
|
|
|
12
12
|
const cookie_paths = {};
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Cookies
|
|
16
|
-
*
|
|
15
|
+
* Cookies whose name and value combined are larger than this size are
|
|
16
|
+
* discarded by browsers. This is the limit codified for the name/value pair
|
|
17
|
+
* in RFC 6265bis: https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis-20#section-5.6-7.5.1
|
|
17
18
|
*/
|
|
18
|
-
const MAX_COOKIE_SIZE =
|
|
19
|
+
const MAX_COOKIE_SIZE = 4096;
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Generates a unique key for a cookie based on its domain, path, and name in
|
|
@@ -42,6 +43,25 @@ export function get_cookies(request, url) {
|
|
|
42
43
|
parseCookie(header, { decode: (value) => value })
|
|
43
44
|
);
|
|
44
45
|
|
|
46
|
+
/** @type {ReturnType<typeof parseCookie> | undefined} */
|
|
47
|
+
let default_cookies;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The header never changes during the request, so the default-decode parse is cached
|
|
51
|
+
* @param {import('cookie').ParseOptions} [opts]
|
|
52
|
+
*/
|
|
53
|
+
function parse_header(opts) {
|
|
54
|
+
return opts?.decode ? parseCookie(header, opts) : (default_cookies ??= parseCookie(header));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** @param {import('./page/types.js').Cookie} cookie */
|
|
58
|
+
function matches_url(cookie) {
|
|
59
|
+
return (
|
|
60
|
+
domain_matches(url.hostname, cookie.options.domain) &&
|
|
61
|
+
path_matches(url.pathname, cookie.options.path)
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
45
65
|
/** @type {string | undefined} */
|
|
46
66
|
let normalized_url;
|
|
47
67
|
|
|
@@ -65,22 +85,23 @@ export function get_cookies(request, url) {
|
|
|
65
85
|
|
|
66
86
|
get(name, opts) {
|
|
67
87
|
// Look for the most specific matching cookie from new_cookies
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
|
|
88
|
+
/** @type {import('./page/types.js').Cookie | undefined} */
|
|
89
|
+
let best_match;
|
|
90
|
+
for (const c of new_cookies.values()) {
|
|
91
|
+
if (
|
|
92
|
+
c.name === name &&
|
|
93
|
+
matches_url(c) &&
|
|
94
|
+
(!best_match || c.options.path.length > best_match.options.path.length)
|
|
95
|
+
) {
|
|
96
|
+
best_match = c;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
77
99
|
|
|
78
100
|
if (best_match) {
|
|
79
101
|
return best_match.options.maxAge === 0 ? undefined : best_match.value;
|
|
80
102
|
}
|
|
81
103
|
|
|
82
|
-
const
|
|
83
|
-
const cookie = req_cookies[name]; // the decoded string or undefined
|
|
104
|
+
const cookie = parse_header(opts)[name]; // the decoded string or undefined
|
|
84
105
|
|
|
85
106
|
// in development, if the cookie was set during this session with `cookies.set`,
|
|
86
107
|
// but at a different path, warn the user. (ignore cookies from request headers,
|
|
@@ -103,16 +124,14 @@ export function get_cookies(request, url) {
|
|
|
103
124
|
},
|
|
104
125
|
|
|
105
126
|
getAll(opts) {
|
|
106
|
-
|
|
127
|
+
// copy, so the cached parse isn't mutated below
|
|
128
|
+
const cookies = { ...parse_header(opts) };
|
|
107
129
|
|
|
108
130
|
// Group cookies by name and find the most specific one for each name
|
|
109
131
|
const lookup = new Map();
|
|
110
132
|
|
|
111
133
|
for (const c of new_cookies.values()) {
|
|
112
|
-
if (
|
|
113
|
-
domain_matches(url.hostname, c.options.domain) &&
|
|
114
|
-
path_matches(url.pathname, c.options.path)
|
|
115
|
-
) {
|
|
134
|
+
if (matches_url(c)) {
|
|
116
135
|
const existing = lookup.get(c.name);
|
|
117
136
|
|
|
118
137
|
// If no existing cookie or this one has a more specific (longer) path, use this one
|
|
@@ -124,7 +143,13 @@ export function get_cookies(request, url) {
|
|
|
124
143
|
|
|
125
144
|
// Add the most specific cookies to the result
|
|
126
145
|
for (const c of lookup.values()) {
|
|
127
|
-
cookies
|
|
146
|
+
// tombstones (deleted cookies) shadow request-header cookies,
|
|
147
|
+
// mirroring the behavior of `get()`
|
|
148
|
+
if (c.options.maxAge === 0) {
|
|
149
|
+
delete cookies[c.name];
|
|
150
|
+
} else {
|
|
151
|
+
cookies[c.name] = c.value;
|
|
152
|
+
}
|
|
128
153
|
}
|
|
129
154
|
|
|
130
155
|
return /** @type {Array<{ name: string; value: string }>} */ (
|
|
@@ -219,9 +244,12 @@ export function get_cookies(request, url) {
|
|
|
219
244
|
new_cookies.set(cookie_key, cookie);
|
|
220
245
|
|
|
221
246
|
if (DEV) {
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
247
|
+
const size =
|
|
248
|
+
// only the name/value pair counts towards MAX_COOKIE_SIZE, not the other attributes
|
|
249
|
+
text_encoder.encode(name).byteLength +
|
|
250
|
+
text_encoder.encode((options.encode ?? encodeURIComponent)(value)).byteLength;
|
|
251
|
+
|
|
252
|
+
if (size > MAX_COOKIE_SIZE) {
|
|
225
253
|
throw new Error(`Cookie "${name}" is too large, and will be discarded by the browser`);
|
|
226
254
|
}
|
|
227
255
|
|
|
@@ -36,7 +36,7 @@ export function get_self_origin(paths_origin, url_origin) {
|
|
|
36
36
|
*/
|
|
37
37
|
export function is_csrf_forbidden({ request, request_origin, self_origin, trusted_origins }) {
|
|
38
38
|
return (
|
|
39
|
-
is_form_content_type(request) &&
|
|
39
|
+
(!request.headers.get('content-type') || is_form_content_type(request)) &&
|
|
40
40
|
mutating_form_methods.has(request.method) &&
|
|
41
41
|
request_origin !== self_origin &&
|
|
42
42
|
(!request_origin || !trusted_origins.includes(request_origin))
|
|
@@ -96,29 +96,25 @@ export async function render_data(
|
|
|
96
96
|
return fn();
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
promises.map((p, i) =>
|
|
102
|
-
p.catch(async (error) => {
|
|
99
|
+
const data_serializer = server_data_serializer_json(event, event_state, options);
|
|
100
|
+
await Promise.all(
|
|
101
|
+
promises.map(async (p, i) => {
|
|
102
|
+
const node = await p.catch(async (error) => {
|
|
103
103
|
if (error instanceof Redirect) {
|
|
104
104
|
throw error;
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
// Math.min because array isn't guaranteed to resolve in order
|
|
108
|
-
length = Math.min(length, i + 1);
|
|
109
|
-
|
|
110
107
|
const transformed = await handle_error_and_jsonify(event, event_state, options, error);
|
|
111
108
|
|
|
112
109
|
return /** @type {import('types').ServerErrorNode} */ ({
|
|
113
110
|
type: 'error',
|
|
114
111
|
error: transformed
|
|
115
112
|
});
|
|
116
|
-
})
|
|
117
|
-
)
|
|
118
|
-
);
|
|
113
|
+
});
|
|
119
114
|
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
data_serializer.add_node(i, node);
|
|
116
|
+
})
|
|
117
|
+
);
|
|
122
118
|
const { data, chunks } = data_serializer.get_data();
|
|
123
119
|
|
|
124
120
|
if (!chunks) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
/** @import { Component } from 'svelte' */
|
|
1
2
|
/** @import { ActionResult, RequestEvent, SSRManifest } from '@sveltejs/kit' */
|
|
2
|
-
/** @import { PageNodeIndexes, RequestState, RequiredResolveOptions, ServerDataNode,
|
|
3
|
+
/** @import { PageNodeIndexes, RequestState, RequiredResolveOptions, ServerDataNode, SSRNode, SSROptions, SSRState } from 'types' */
|
|
3
4
|
import { text } from '@sveltejs/kit';
|
|
4
5
|
import { HttpError, Redirect } from '@sveltejs/kit/internal';
|
|
5
6
|
import { compact } from '../../../utils/array.js';
|
|
@@ -316,13 +317,7 @@ export async function render_page(
|
|
|
316
317
|
},
|
|
317
318
|
status,
|
|
318
319
|
error,
|
|
319
|
-
error_components: await load_error_components(
|
|
320
|
-
options,
|
|
321
|
-
ssr,
|
|
322
|
-
error_branch,
|
|
323
|
-
page,
|
|
324
|
-
manifest
|
|
325
|
-
),
|
|
320
|
+
error_components: await load_error_components(ssr, error_branch, page, manifest),
|
|
326
321
|
branch: error_branch,
|
|
327
322
|
fetched,
|
|
328
323
|
data_serializer
|
|
@@ -374,7 +369,7 @@ export async function render_page(
|
|
|
374
369
|
action_result,
|
|
375
370
|
fetched,
|
|
376
371
|
data_serializer: !ssr ? server_data_serializer(event, event_state, options) : data_serializer,
|
|
377
|
-
error_components: await load_error_components(
|
|
372
|
+
error_components: await load_error_components(ssr, branch, page, manifest)
|
|
378
373
|
});
|
|
379
374
|
} catch (e) {
|
|
380
375
|
// a remote function could have thrown a redirect during render
|
|
@@ -398,18 +393,16 @@ export async function render_page(
|
|
|
398
393
|
}
|
|
399
394
|
|
|
400
395
|
/**
|
|
401
|
-
*
|
|
402
|
-
* @param {SSROptions} options
|
|
403
396
|
* @param {boolean} ssr
|
|
404
397
|
* @param {Array<import('./types.js').Loaded | null>} branch
|
|
405
398
|
* @param {PageNodeIndexes} page
|
|
406
399
|
* @param {SSRManifest} manifest
|
|
407
400
|
*/
|
|
408
|
-
async function load_error_components(
|
|
409
|
-
/** @type {Array<
|
|
401
|
+
async function load_error_components(ssr, branch, page, manifest) {
|
|
402
|
+
/** @type {Array<Component | undefined> | undefined} */
|
|
410
403
|
let error_components;
|
|
411
404
|
|
|
412
|
-
if (
|
|
405
|
+
if (ssr) {
|
|
413
406
|
let last_idx = -1;
|
|
414
407
|
error_components = await Promise.all(
|
|
415
408
|
// eslint-disable-next-line @typescript-eslint/await-thenable
|