@sveltejs/kit 1.0.0-next.435 → 1.0.0-next.438
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 +2 -2
- package/src/core/adapt/builder.js +2 -1
- package/src/core/prerender/prerender.js +6 -5
- package/src/core/sync/create_manifest_data/index.js +1 -0
- package/src/runtime/client/client.js +3 -6
- package/src/runtime/server/index.js +9 -11
- package/src/runtime/server/page/index.js +22 -18
- package/src/runtime/server/page/load_data.js +28 -32
- package/src/runtime/server/page/render.js +1 -2
- package/src/runtime/server/page/respond_with_error.js +1 -0
- package/src/utils/routing.js +9 -0
- package/src/utils/url.js +59 -14
- package/src/vite/index.js +1 -3
- package/types/ambient.d.ts +1 -1
- package/types/index.d.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sveltejs/kit",
|
|
3
|
-
"version": "1.0.0-next.
|
|
3
|
+
"version": "1.0.0-next.438",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "https://github.com/sveltejs/kit",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"tiny-glob": "^0.2.9",
|
|
40
40
|
"typescript": "^4.7.4",
|
|
41
41
|
"uvu": "^0.5.3",
|
|
42
|
-
"vite": "^3.0.
|
|
42
|
+
"vite": "^3.0.9"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"svelte": "^3.44.0",
|
|
@@ -5,6 +5,7 @@ import { pipeline } from 'stream';
|
|
|
5
5
|
import { promisify } from 'util';
|
|
6
6
|
import { copy, rimraf, mkdirp } from '../../utils/filesystem.js';
|
|
7
7
|
import { generate_manifest } from '../generate_manifest/index.js';
|
|
8
|
+
import { get_path } from '../../utils/routing.js';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Creates the Builder which is passed to adapters for building the application.
|
|
@@ -23,7 +24,7 @@ export function create_builder({ config, build_data, prerendered, log }) {
|
|
|
23
24
|
/** @param {import('types').RouteData} route */
|
|
24
25
|
// TODO routes should come pre-filtered
|
|
25
26
|
function not_prerendered(route) {
|
|
26
|
-
const path = route.page &&
|
|
27
|
+
const path = route.page && get_path(route.id);
|
|
27
28
|
if (path) {
|
|
28
29
|
return !prerendered_paths.has(path) && !prerendered_paths.has(path + '/');
|
|
29
30
|
}
|
|
@@ -9,6 +9,8 @@ import { crawl } from './crawl.js';
|
|
|
9
9
|
import { escape_html_attr } from '../../utils/escape.js';
|
|
10
10
|
import { logger } from '../utils.js';
|
|
11
11
|
import { load_config } from '../config/index.js';
|
|
12
|
+
import { compact } from '../../utils/array.js';
|
|
13
|
+
import { get_path } from '../../utils/routing.js';
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* @typedef {import('types').PrerenderErrorHandler} PrerenderErrorHandler
|
|
@@ -341,11 +343,10 @@ export async function prerender() {
|
|
|
341
343
|
if (config.prerender.enabled) {
|
|
342
344
|
for (const entry of config.prerender.entries) {
|
|
343
345
|
if (entry === '*') {
|
|
344
|
-
/** @type {import('types').
|
|
345
|
-
const
|
|
346
|
-
const
|
|
347
|
-
|
|
348
|
-
.filter(Boolean);
|
|
346
|
+
/** @type {import('types').SSRManifest} */
|
|
347
|
+
const manifest = (await import(pathToFileURL(manifest_path).href)).manifest;
|
|
348
|
+
const { routes } = manifest._;
|
|
349
|
+
const entries = compact(routes.map((route) => route.page && get_path(route.id)));
|
|
349
350
|
|
|
350
351
|
for (const entry of entries) {
|
|
351
352
|
enqueue(null, config.paths.base + entry); // TODO can we pre-normalize these?
|
|
@@ -82,6 +82,7 @@ function create_matchers(config, cwd) {
|
|
|
82
82
|
* @param {string} fallback
|
|
83
83
|
*/
|
|
84
84
|
function create_routes_and_nodes(cwd, config, fallback) {
|
|
85
|
+
/** @type {Map<string, import('types').RouteData>} */
|
|
85
86
|
const route_map = new Map();
|
|
86
87
|
|
|
87
88
|
/** @type {Map<string, import('./types').Part[][]>} */
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { onMount, tick } from 'svelte';
|
|
2
2
|
import { normalize_error } from '../../utils/error.js';
|
|
3
|
-
import {
|
|
3
|
+
import { make_trackable, decode_params, normalize_path } from '../../utils/url.js';
|
|
4
4
|
import { find_anchor, get_base_uri, get_href, scroll_state } from './utils.js';
|
|
5
5
|
import { lock_fetch, unlock_fetch, initial_fetch, native_fetch } from './fetcher.js';
|
|
6
6
|
import { parse } from './parse.js';
|
|
@@ -510,17 +510,14 @@ export function create_client({ target, base, trailing_slash }) {
|
|
|
510
510
|
});
|
|
511
511
|
}
|
|
512
512
|
|
|
513
|
-
const load_url = new LoadURL(url);
|
|
514
|
-
|
|
515
513
|
/** @type {import('types').LoadEvent} */
|
|
516
514
|
const load_input = {
|
|
517
515
|
routeId,
|
|
518
516
|
params: uses_params,
|
|
519
517
|
data: server_data_node?.data ?? null,
|
|
520
|
-
|
|
518
|
+
url: make_trackable(url, () => {
|
|
521
519
|
uses.url = true;
|
|
522
|
-
|
|
523
|
-
},
|
|
520
|
+
}),
|
|
524
521
|
async fetch(resource, init) {
|
|
525
522
|
let requested;
|
|
526
523
|
|
|
@@ -4,7 +4,7 @@ import { render_response } from './page/render.js';
|
|
|
4
4
|
import { respond_with_error } from './page/respond_with_error.js';
|
|
5
5
|
import { coalesce_to_error, normalize_error } from '../../utils/error.js';
|
|
6
6
|
import { serialize_error, GENERIC_ERROR, error_to_pojo } from './utils.js';
|
|
7
|
-
import { decode_params, normalize_path } from '../../utils/url.js';
|
|
7
|
+
import { decode_params, disable_search, normalize_path } from '../../utils/url.js';
|
|
8
8
|
import { exec } from '../../utils/routing.js';
|
|
9
9
|
import { negotiate } from '../../utils/http.js';
|
|
10
10
|
import { HttpError, Redirect } from '../../index/private.js';
|
|
@@ -122,21 +122,17 @@ export async function respond(request, options, state) {
|
|
|
122
122
|
/** @type {string[]} */
|
|
123
123
|
const cookies = [];
|
|
124
124
|
|
|
125
|
+
if (state.prerendering) disable_search(url);
|
|
126
|
+
|
|
125
127
|
/** @type {import('types').RequestEvent} */
|
|
126
128
|
const event = {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
+
getClientAddress:
|
|
130
|
+
state.getClientAddress ||
|
|
131
|
+
(() => {
|
|
129
132
|
throw new Error(
|
|
130
133
|
`${__SVELTEKIT_ADAPTER_NAME__} does not specify getClientAddress. Please raise an issue`
|
|
131
134
|
);
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
Object.defineProperty(event, 'clientAddress', {
|
|
135
|
-
value: state.getClientAddress()
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
return event.clientAddress;
|
|
139
|
-
},
|
|
135
|
+
}),
|
|
140
136
|
locals: {},
|
|
141
137
|
params,
|
|
142
138
|
platform: state.platform,
|
|
@@ -195,6 +191,7 @@ export async function respond(request, options, state) {
|
|
|
195
191
|
};
|
|
196
192
|
|
|
197
193
|
Object.defineProperties(event, {
|
|
194
|
+
clientAddress: removed('clientAddress', 'getClientAddress'),
|
|
198
195
|
method: removed('method', 'request.method', details),
|
|
199
196
|
headers: removed('headers', 'request.headers', details),
|
|
200
197
|
origin: removed('origin', 'url.origin'),
|
|
@@ -277,6 +274,7 @@ export async function respond(request, options, state) {
|
|
|
277
274
|
return load_server_data({
|
|
278
275
|
dev: options.dev,
|
|
279
276
|
event,
|
|
277
|
+
state,
|
|
280
278
|
node,
|
|
281
279
|
parent: async () => {
|
|
282
280
|
/** @type {Record<string, any>} */
|
|
@@ -104,6 +104,27 @@ export async function render_page(event, route, page, options, state, resolve_op
|
|
|
104
104
|
const should_prerender_data = nodes.some((node) => node?.server);
|
|
105
105
|
const data_pathname = `${event.url.pathname.replace(/\/$/, '')}/__data.json`;
|
|
106
106
|
|
|
107
|
+
// it's crucial that we do this before returning the non-SSR response, otherwise
|
|
108
|
+
// SvelteKit will erroneously believe that the path has been prerendered,
|
|
109
|
+
// causing functions to be omitted from the manifesst generated later
|
|
110
|
+
const should_prerender =
|
|
111
|
+
leaf_node.shared?.prerender ?? leaf_node.server?.prerender ?? options.prerender.default;
|
|
112
|
+
if (should_prerender) {
|
|
113
|
+
const mod = leaf_node.server;
|
|
114
|
+
if (mod && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) {
|
|
115
|
+
throw new Error('Cannot prerender pages that have endpoints with mutative methods');
|
|
116
|
+
}
|
|
117
|
+
} else if (state.prerendering) {
|
|
118
|
+
// if the page isn't marked as prerenderable (or is explicitly
|
|
119
|
+
// marked NOT prerenderable, if `prerender.default` is `true`),
|
|
120
|
+
// then bail out at this point
|
|
121
|
+
if (!should_prerender) {
|
|
122
|
+
return new Response(undefined, {
|
|
123
|
+
status: 204
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
107
128
|
if (!resolve_opts.ssr) {
|
|
108
129
|
return await render_response({
|
|
109
130
|
branch: [],
|
|
@@ -123,24 +144,6 @@ export async function render_page(event, route, page, options, state, resolve_op
|
|
|
123
144
|
});
|
|
124
145
|
}
|
|
125
146
|
|
|
126
|
-
const should_prerender =
|
|
127
|
-
leaf_node.shared?.prerender ?? leaf_node.server?.prerender ?? options.prerender.default;
|
|
128
|
-
if (should_prerender) {
|
|
129
|
-
const mod = leaf_node.server;
|
|
130
|
-
if (mod && (mod.POST || mod.PUT || mod.DELETE || mod.PATCH)) {
|
|
131
|
-
throw new Error('Cannot prerender pages that have endpoints with mutative methods');
|
|
132
|
-
}
|
|
133
|
-
} else if (state.prerendering) {
|
|
134
|
-
// if the page isn't marked as prerenderable (or is explicitly
|
|
135
|
-
// marked NOT prerenderable, if `prerender.default` is `true`),
|
|
136
|
-
// then bail out at this point
|
|
137
|
-
if (!should_prerender) {
|
|
138
|
-
return new Response(undefined, {
|
|
139
|
-
status: 204
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
147
|
/** @type {Array<Loaded | null>} */
|
|
145
148
|
let branch = [];
|
|
146
149
|
|
|
@@ -165,6 +168,7 @@ export async function render_page(event, route, page, options, state, resolve_op
|
|
|
165
168
|
return await load_server_data({
|
|
166
169
|
dev: options.dev,
|
|
167
170
|
event,
|
|
171
|
+
state,
|
|
168
172
|
node,
|
|
169
173
|
parent: async () => {
|
|
170
174
|
/** @type {Record<string, any>} */
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { disable_search, make_trackable } from '../../../utils/url.js';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* Calls the user's `load` function.
|
|
5
5
|
* @param {{
|
|
6
6
|
* dev: boolean;
|
|
7
7
|
* event: import('types').RequestEvent;
|
|
8
|
+
* state: import('types').SSRState;
|
|
8
9
|
* node: import('types').SSRNode | undefined;
|
|
9
10
|
* parent: () => Promise<Record<string, any>>;
|
|
10
11
|
* }} opts
|
|
11
12
|
* @returns {Promise<import('types').ServerDataNode | null>}
|
|
12
13
|
*/
|
|
13
|
-
export async function load_server_data({ dev, event, node, parent }) {
|
|
14
|
+
export async function load_server_data({ dev, event, state, node, parent }) {
|
|
14
15
|
if (!node?.server) return null;
|
|
15
16
|
|
|
16
17
|
const uses = {
|
|
@@ -20,39 +21,34 @@ export async function load_server_data({ dev, event, node, parent }) {
|
|
|
20
21
|
url: false
|
|
21
22
|
};
|
|
22
23
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
for (const dep of deps) {
|
|
26
|
-
const { href } = new URL(dep, event.url);
|
|
27
|
-
uses.dependencies.add(href);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const params = new Proxy(event.params, {
|
|
32
|
-
get: (target, key) => {
|
|
33
|
-
uses.params.add(key);
|
|
34
|
-
return target[/** @type {string} */ (key)];
|
|
35
|
-
}
|
|
24
|
+
const url = make_trackable(event.url, () => {
|
|
25
|
+
uses.url = true;
|
|
36
26
|
});
|
|
37
27
|
|
|
28
|
+
if (state.prerendering) {
|
|
29
|
+
disable_search(url);
|
|
30
|
+
}
|
|
31
|
+
|
|
38
32
|
const result = await node.server.load?.call(null, {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
33
|
+
...event,
|
|
34
|
+
/** @param {string[]} deps */
|
|
35
|
+
depends: (...deps) => {
|
|
36
|
+
for (const dep of deps) {
|
|
37
|
+
const { href } = new URL(dep, event.url);
|
|
38
|
+
uses.dependencies.add(href);
|
|
39
|
+
}
|
|
43
40
|
},
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
params: new Proxy(event.params, {
|
|
42
|
+
get: (target, key) => {
|
|
43
|
+
uses.params.add(key);
|
|
44
|
+
return target[/** @type {string} */ (key)];
|
|
45
|
+
}
|
|
46
|
+
}),
|
|
47
47
|
parent: async () => {
|
|
48
48
|
uses.parent = true;
|
|
49
49
|
return parent();
|
|
50
50
|
},
|
|
51
|
-
|
|
52
|
-
request: event.request,
|
|
53
|
-
routeId: event.routeId,
|
|
54
|
-
setHeaders: event.setHeaders,
|
|
55
|
-
url: event.url
|
|
51
|
+
url
|
|
56
52
|
});
|
|
57
53
|
|
|
58
54
|
const data = result ? await unwrap_promises(result) : null;
|
|
@@ -85,15 +81,15 @@ export async function load_server_data({ dev, event, node, parent }) {
|
|
|
85
81
|
* }} opts
|
|
86
82
|
* @returns {Promise<Record<string, any> | null>}
|
|
87
83
|
*/
|
|
88
|
-
export async function load_data({ event, fetcher, node, parent, server_data_promise
|
|
84
|
+
export async function load_data({ event, fetcher, node, parent, server_data_promise }) {
|
|
89
85
|
const server_data_node = await server_data_promise;
|
|
90
86
|
|
|
91
87
|
if (!node?.shared?.load) {
|
|
92
88
|
return server_data_node?.data ?? null;
|
|
93
89
|
}
|
|
94
90
|
|
|
95
|
-
const
|
|
96
|
-
url:
|
|
91
|
+
const load_event = {
|
|
92
|
+
url: event.url,
|
|
97
93
|
params: event.params,
|
|
98
94
|
data: server_data_node?.data ?? null,
|
|
99
95
|
routeId: event.routeId,
|
|
@@ -104,7 +100,7 @@ export async function load_data({ event, fetcher, node, parent, server_data_prom
|
|
|
104
100
|
};
|
|
105
101
|
|
|
106
102
|
// TODO remove this for 1.0
|
|
107
|
-
Object.defineProperties(
|
|
103
|
+
Object.defineProperties(load_event, {
|
|
108
104
|
session: {
|
|
109
105
|
get() {
|
|
110
106
|
throw new Error(
|
|
@@ -115,7 +111,7 @@ export async function load_data({ event, fetcher, node, parent, server_data_prom
|
|
|
115
111
|
}
|
|
116
112
|
});
|
|
117
113
|
|
|
118
|
-
const data = await node.shared.load.call(null,
|
|
114
|
+
const data = await node.shared.load.call(null, load_event);
|
|
119
115
|
|
|
120
116
|
return data ? unwrap_promises(data) : null;
|
|
121
117
|
}
|
|
@@ -5,7 +5,6 @@ import { hash } from '../../hash.js';
|
|
|
5
5
|
import { render_json_payload_script } from '../../../utils/escape.js';
|
|
6
6
|
import { s } from '../../../utils/misc.js';
|
|
7
7
|
import { Csp } from './csp.js';
|
|
8
|
-
import { PrerenderingURL } from '../../../utils/url.js';
|
|
9
8
|
import { serialize_error } from '../utils.js';
|
|
10
9
|
import { HttpError } from '../../../index/private.js';
|
|
11
10
|
|
|
@@ -100,7 +99,7 @@ export async function render_response({
|
|
|
100
99
|
params: /** @type {Record<string, any>} */ (event.params),
|
|
101
100
|
routeId: event.routeId,
|
|
102
101
|
status,
|
|
103
|
-
url:
|
|
102
|
+
url: event.url,
|
|
104
103
|
data
|
|
105
104
|
};
|
|
106
105
|
|
package/src/utils/routing.js
CHANGED
|
@@ -96,6 +96,15 @@ export function affects_path(segment) {
|
|
|
96
96
|
return !/^\([^)]+\)$/.test(segment);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Turns a route ID into a path, if possible
|
|
101
|
+
* @param {string} id
|
|
102
|
+
*/
|
|
103
|
+
export function get_path(id) {
|
|
104
|
+
if (id.includes('[')) return null;
|
|
105
|
+
return `/${id.split('/').filter(affects_path).join('/')}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
99
108
|
/**
|
|
100
109
|
* @param {RegExpMatchArray} match
|
|
101
110
|
* @param {string[]} names
|
package/src/utils/url.js
CHANGED
|
@@ -75,23 +75,68 @@ export function decode_params(params) {
|
|
|
75
75
|
return params;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
/**
|
|
79
|
+
* URL properties that could change during the lifetime of the page,
|
|
80
|
+
* which excludes things like `origin`
|
|
81
|
+
* @type {Array<keyof URL>}
|
|
82
|
+
*/
|
|
83
|
+
const tracked_url_properties = ['href', 'pathname', 'search', 'searchParams', 'toString', 'toJSON'];
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {URL} url
|
|
87
|
+
* @param {() => void} callback
|
|
88
|
+
*/
|
|
89
|
+
export function make_trackable(url, callback) {
|
|
90
|
+
const tracked = new URL(url);
|
|
91
|
+
|
|
92
|
+
for (const property of tracked_url_properties) {
|
|
93
|
+
let value = tracked[property];
|
|
94
|
+
|
|
95
|
+
Object.defineProperty(tracked, property, {
|
|
96
|
+
get() {
|
|
97
|
+
callback();
|
|
98
|
+
return value;
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
enumerable: true,
|
|
102
|
+
configurable: true
|
|
103
|
+
});
|
|
84
104
|
}
|
|
105
|
+
|
|
106
|
+
// @ts-ignore
|
|
107
|
+
tracked[Symbol.for('nodejs.util.inspect.custom')] = (depth, opts, inspect) => {
|
|
108
|
+
return inspect(url, opts);
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
disable_hash(tracked);
|
|
112
|
+
|
|
113
|
+
return tracked;
|
|
85
114
|
}
|
|
86
115
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
116
|
+
/**
|
|
117
|
+
* Disallow access to `url.hash` on the server and in `load`
|
|
118
|
+
* @param {URL} url
|
|
119
|
+
*/
|
|
120
|
+
export function disable_hash(url) {
|
|
121
|
+
Object.defineProperty(url, 'hash', {
|
|
122
|
+
get() {
|
|
123
|
+
throw new Error(
|
|
124
|
+
'Cannot access event.url.hash. Consider using `$page.url.hash` inside a component instead'
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
}
|
|
92
129
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
130
|
+
/**
|
|
131
|
+
* Disallow access to `url.search` and `url.searchParams` during prerendering
|
|
132
|
+
* @param {URL} url
|
|
133
|
+
*/
|
|
134
|
+
export function disable_search(url) {
|
|
135
|
+
for (const property of ['search', 'searchParams']) {
|
|
136
|
+
Object.defineProperty(url, property, {
|
|
137
|
+
get() {
|
|
138
|
+
throw new Error(`Cannot access url.${property} on a page with prerendering enabled`);
|
|
139
|
+
}
|
|
140
|
+
});
|
|
96
141
|
}
|
|
97
142
|
}
|
package/src/vite/index.js
CHANGED
package/types/ambient.d.ts
CHANGED
|
@@ -151,7 +151,7 @@ declare module '$app/navigation' {
|
|
|
151
151
|
opts?: { replaceState?: boolean; noscroll?: boolean; keepfocus?: boolean; state?: any }
|
|
152
152
|
): Promise<void>;
|
|
153
153
|
/**
|
|
154
|
-
* Causes any `load` functions belonging to the currently active page to re-run if they `fetch` the resource in question
|
|
154
|
+
* Causes any `load` functions belonging to the currently active page to re-run if they `fetch` the resource in question. If no argument is given, all resources will be invalidated. Returns a `Promise` that resolves when the page is subsequently updated.
|
|
155
155
|
* @param dependency The invalidated resource
|
|
156
156
|
*/
|
|
157
157
|
export function invalidate(dependency?: string | ((href: string) => boolean)): Promise<void>;
|
package/types/index.d.ts
CHANGED
|
@@ -238,7 +238,7 @@ export interface ParamMatcher {
|
|
|
238
238
|
export interface RequestEvent<
|
|
239
239
|
Params extends Partial<Record<string, string>> = Partial<Record<string, string>>
|
|
240
240
|
> {
|
|
241
|
-
|
|
241
|
+
getClientAddress: () => string;
|
|
242
242
|
locals: App.Locals;
|
|
243
243
|
params: Params;
|
|
244
244
|
platform: Readonly<App.Platform>;
|