@sveltejs/kit 1.0.0-next.257 → 1.0.0-next.260
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/assets/client/start.js +66 -12
- package/assets/server/index.js +314 -43
- package/dist/chunks/index.js +6 -0
- package/dist/chunks/index2.js +33 -3
- package/dist/chunks/index3.js +8 -22
- package/dist/chunks/index4.js +1 -0
- package/dist/cli.js +2 -2
- package/package.json +1 -1
- package/types/ambient-modules.d.ts +1 -1
- package/types/endpoint.d.ts +23 -2
- package/types/helper.d.ts +2 -8
- package/types/internal.d.ts +11 -2
- package/types/page.d.ts +1 -0
package/assets/client/start.js
CHANGED
|
@@ -731,15 +731,29 @@ class Renderer {
|
|
|
731
731
|
for (let i = 0; i < nodes.length; i += 1) {
|
|
732
732
|
const is_leaf = i === nodes.length - 1;
|
|
733
733
|
|
|
734
|
+
let props;
|
|
735
|
+
|
|
736
|
+
if (is_leaf) {
|
|
737
|
+
const serialized = document.querySelector('[data-type="svelte-props"]');
|
|
738
|
+
if (serialized) {
|
|
739
|
+
props = JSON.parse(/** @type {string} */ (serialized.textContent));
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
|
|
734
743
|
const node = await this._load_node({
|
|
735
744
|
module: await nodes[i],
|
|
736
745
|
url,
|
|
737
746
|
params,
|
|
738
747
|
stuff,
|
|
739
748
|
status: is_leaf ? status : undefined,
|
|
740
|
-
error: is_leaf ? error : undefined
|
|
749
|
+
error: is_leaf ? error : undefined,
|
|
750
|
+
props
|
|
741
751
|
});
|
|
742
752
|
|
|
753
|
+
if (props) {
|
|
754
|
+
node.uses.dependencies.add(url.href);
|
|
755
|
+
}
|
|
756
|
+
|
|
743
757
|
branch.push(node);
|
|
744
758
|
|
|
745
759
|
if (node && node.loaded) {
|
|
@@ -1092,10 +1106,11 @@ class Renderer {
|
|
|
1092
1106
|
* url: URL;
|
|
1093
1107
|
* params: Record<string, string>;
|
|
1094
1108
|
* stuff: Record<string, any>;
|
|
1109
|
+
* props?: Record<string, any>;
|
|
1095
1110
|
* }} options
|
|
1096
1111
|
* @returns
|
|
1097
1112
|
*/
|
|
1098
|
-
async _load_node({ status, error, module, url, params, stuff }) {
|
|
1113
|
+
async _load_node({ status, error, module, url, params, stuff, props }) {
|
|
1099
1114
|
/** @type {import('./types').BranchNode} */
|
|
1100
1115
|
const node = {
|
|
1101
1116
|
module,
|
|
@@ -1104,12 +1119,17 @@ class Renderer {
|
|
|
1104
1119
|
url: false,
|
|
1105
1120
|
session: false,
|
|
1106
1121
|
stuff: false,
|
|
1107
|
-
dependencies:
|
|
1122
|
+
dependencies: new Set()
|
|
1108
1123
|
},
|
|
1109
1124
|
loaded: null,
|
|
1110
1125
|
stuff
|
|
1111
1126
|
};
|
|
1112
1127
|
|
|
1128
|
+
if (props) {
|
|
1129
|
+
// shadow endpoint props means we need to mark this URL as a dependency of itself
|
|
1130
|
+
node.uses.dependencies.add(url.href);
|
|
1131
|
+
}
|
|
1132
|
+
|
|
1113
1133
|
/** @type {Record<string, string>} */
|
|
1114
1134
|
const uses_params = {};
|
|
1115
1135
|
for (const key in params) {
|
|
@@ -1130,6 +1150,7 @@ class Renderer {
|
|
|
1130
1150
|
/** @type {import('types/page').LoadInput | import('types/page').ErrorLoadInput} */
|
|
1131
1151
|
const load_input = {
|
|
1132
1152
|
params: uses_params,
|
|
1153
|
+
props: props || {},
|
|
1133
1154
|
get url() {
|
|
1134
1155
|
node.uses.url = true;
|
|
1135
1156
|
return url;
|
|
@@ -1145,7 +1166,7 @@ class Renderer {
|
|
|
1145
1166
|
fetch(resource, info) {
|
|
1146
1167
|
const requested = typeof resource === 'string' ? resource : resource.url;
|
|
1147
1168
|
const { href } = new URL(requested, url);
|
|
1148
|
-
node.uses.dependencies.
|
|
1169
|
+
node.uses.dependencies.add(href);
|
|
1149
1170
|
|
|
1150
1171
|
return started ? fetch(resource, info) : initial_fetch(resource, info);
|
|
1151
1172
|
}
|
|
@@ -1173,6 +1194,8 @@ class Renderer {
|
|
|
1173
1194
|
|
|
1174
1195
|
node.loaded = normalize(loaded);
|
|
1175
1196
|
if (node.loaded.stuff) node.stuff = node.loaded.stuff;
|
|
1197
|
+
} else if (props) {
|
|
1198
|
+
node.loaded = normalize({ props });
|
|
1176
1199
|
}
|
|
1177
1200
|
|
|
1178
1201
|
return node;
|
|
@@ -1191,7 +1214,7 @@ class Renderer {
|
|
|
1191
1214
|
if (cached) return cached;
|
|
1192
1215
|
}
|
|
1193
1216
|
|
|
1194
|
-
const [pattern, a, b, get_params] = route;
|
|
1217
|
+
const [pattern, a, b, get_params, has_shadow] = route;
|
|
1195
1218
|
const params = get_params
|
|
1196
1219
|
? // the pattern is for the route which we've already matched to this path
|
|
1197
1220
|
get_params(/** @type {RegExpExecArray} */ (pattern.exec(path)))
|
|
@@ -1235,16 +1258,47 @@ class Renderer {
|
|
|
1235
1258
|
(changed.url && previous.uses.url) ||
|
|
1236
1259
|
changed.params.some((param) => previous.uses.params.has(param)) ||
|
|
1237
1260
|
(changed.session && previous.uses.session) ||
|
|
1238
|
-
previous.uses.dependencies.some((dep) => this.invalid.has(dep)) ||
|
|
1261
|
+
Array.from(previous.uses.dependencies).some((dep) => this.invalid.has(dep)) ||
|
|
1239
1262
|
(stuff_changed && previous.uses.stuff);
|
|
1240
1263
|
|
|
1241
1264
|
if (changed_since_last_render) {
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1265
|
+
/** @type {Record<string, any>} */
|
|
1266
|
+
let props = {};
|
|
1267
|
+
|
|
1268
|
+
if (has_shadow && i === a.length - 1) {
|
|
1269
|
+
const res = await fetch(`${url.pathname}/__data.json`, {
|
|
1270
|
+
headers: {
|
|
1271
|
+
'x-sveltekit-noredirect': 'true'
|
|
1272
|
+
}
|
|
1273
|
+
});
|
|
1274
|
+
|
|
1275
|
+
if (res.ok) {
|
|
1276
|
+
const redirect = res.headers.get('x-sveltekit-location');
|
|
1277
|
+
|
|
1278
|
+
if (redirect) {
|
|
1279
|
+
return {
|
|
1280
|
+
redirect,
|
|
1281
|
+
props: {},
|
|
1282
|
+
state: this.current
|
|
1283
|
+
};
|
|
1284
|
+
}
|
|
1285
|
+
|
|
1286
|
+
props = await res.json();
|
|
1287
|
+
} else {
|
|
1288
|
+
status = res.status;
|
|
1289
|
+
error = new Error('Failed to load data');
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
|
|
1293
|
+
if (!error) {
|
|
1294
|
+
node = await this._load_node({
|
|
1295
|
+
module,
|
|
1296
|
+
url,
|
|
1297
|
+
params,
|
|
1298
|
+
props,
|
|
1299
|
+
stuff
|
|
1300
|
+
});
|
|
1301
|
+
}
|
|
1248
1302
|
|
|
1249
1303
|
if (node && node.loaded) {
|
|
1250
1304
|
if (node.loaded.fallthrough) {
|
package/assets/server/index.js
CHANGED
|
@@ -61,6 +61,24 @@ function decode_params(params) {
|
|
|
61
61
|
return params;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
+
/** @param {any} body */
|
|
65
|
+
function is_pojo(body) {
|
|
66
|
+
if (typeof body !== 'object') return false;
|
|
67
|
+
|
|
68
|
+
if (body) {
|
|
69
|
+
if (body instanceof Uint8Array) return false;
|
|
70
|
+
|
|
71
|
+
// body could be a node Readable, but we don't want to import
|
|
72
|
+
// node built-ins, so we use duck typing
|
|
73
|
+
if (body._readableState && body._writableState && body._events) return false;
|
|
74
|
+
|
|
75
|
+
// similarly, it could be a web ReadableStream
|
|
76
|
+
if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) return false;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
64
82
|
/** @param {string} body */
|
|
65
83
|
function error(body) {
|
|
66
84
|
return new Response(body, {
|
|
@@ -95,13 +113,10 @@ function is_text(content_type) {
|
|
|
95
113
|
|
|
96
114
|
/**
|
|
97
115
|
* @param {import('types/hooks').RequestEvent} event
|
|
98
|
-
* @param {import('types/
|
|
99
|
-
* @param {RegExpExecArray} match
|
|
116
|
+
* @param {{ [method: string]: import('types/endpoint').RequestHandler }} mod
|
|
100
117
|
* @returns {Promise<Response | undefined>}
|
|
101
118
|
*/
|
|
102
|
-
async function render_endpoint(event,
|
|
103
|
-
const mod = await route.load();
|
|
104
|
-
|
|
119
|
+
async function render_endpoint(event, mod) {
|
|
105
120
|
/** @type {import('types/endpoint').RequestHandler} */
|
|
106
121
|
const handler = mod[event.request.method.toLowerCase().replace('delete', 'del')]; // 'delete' is a reserved word
|
|
107
122
|
|
|
@@ -109,11 +124,6 @@ async function render_endpoint(event, route, match) {
|
|
|
109
124
|
return;
|
|
110
125
|
}
|
|
111
126
|
|
|
112
|
-
// we're mutating `request` so that we don't have to do { ...request, params }
|
|
113
|
-
// on the next line, since that breaks the getters that replace path, query and
|
|
114
|
-
// origin. We could revert that once we remove the getters
|
|
115
|
-
event.params = route.params ? decode_params(route.params(match)) : {};
|
|
116
|
-
|
|
117
127
|
const response = await handler(event);
|
|
118
128
|
const preface = `Invalid response from route ${event.url.pathname}`;
|
|
119
129
|
|
|
@@ -163,24 +173,6 @@ async function render_endpoint(event, route, match) {
|
|
|
163
173
|
});
|
|
164
174
|
}
|
|
165
175
|
|
|
166
|
-
/** @param {any} body */
|
|
167
|
-
function is_pojo(body) {
|
|
168
|
-
if (typeof body !== 'object') return false;
|
|
169
|
-
|
|
170
|
-
if (body) {
|
|
171
|
-
if (body instanceof Uint8Array) return false;
|
|
172
|
-
|
|
173
|
-
// body could be a node Readable, but we don't want to import
|
|
174
|
-
// node built-ins, so we use duck typing
|
|
175
|
-
if (body._readableState && body._writableState && body._events) return false;
|
|
176
|
-
|
|
177
|
-
// similarly, it could be a web ReadableStream
|
|
178
|
-
if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) return false;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return true;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
176
|
var chars$1 = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_$';
|
|
185
177
|
var unsafeChars = /[<>\b\f\n\r\t\0\u2028\u2029]/g;
|
|
186
178
|
var reserved = /^(?:do|if|in|for|int|let|new|try|var|byte|case|char|else|enum|goto|long|this|void|with|await|break|catch|class|const|final|float|short|super|throw|while|yield|delete|double|export|import|native|return|switch|throws|typeof|boolean|default|extends|finally|package|private|abstract|continue|debugger|function|volatile|interface|protected|transient|implements|instanceof|synchronized)$/;
|
|
@@ -1076,6 +1068,8 @@ async function render_response({
|
|
|
1076
1068
|
/** @type {Array<{ url: string, body: string, json: string }>} */
|
|
1077
1069
|
const serialized_data = [];
|
|
1078
1070
|
|
|
1071
|
+
let shadow_props;
|
|
1072
|
+
|
|
1079
1073
|
let rendered;
|
|
1080
1074
|
|
|
1081
1075
|
let is_private = false;
|
|
@@ -1086,13 +1080,14 @@ async function render_response({
|
|
|
1086
1080
|
}
|
|
1087
1081
|
|
|
1088
1082
|
if (ssr) {
|
|
1089
|
-
branch.forEach(({ node, loaded, fetched, uses_credentials }) => {
|
|
1083
|
+
branch.forEach(({ node, props, loaded, fetched, uses_credentials }) => {
|
|
1090
1084
|
if (node.css) node.css.forEach((url) => stylesheets.add(url));
|
|
1091
1085
|
if (node.js) node.js.forEach((url) => modulepreloads.add(url));
|
|
1092
1086
|
if (node.styles) Object.entries(node.styles).forEach(([k, v]) => styles.set(k, v));
|
|
1093
1087
|
|
|
1094
1088
|
// TODO probably better if `fetched` wasn't populated unless `hydrate`
|
|
1095
1089
|
if (fetched && page_config.hydrate) serialized_data.push(...fetched);
|
|
1090
|
+
if (props) shadow_props = props;
|
|
1096
1091
|
|
|
1097
1092
|
if (uses_credentials) is_private = true;
|
|
1098
1093
|
|
|
@@ -1271,6 +1266,11 @@ async function render_response({
|
|
|
1271
1266
|
return `<script ${attributes}>${json}</script>`;
|
|
1272
1267
|
})
|
|
1273
1268
|
.join('\n\t');
|
|
1269
|
+
|
|
1270
|
+
if (shadow_props) {
|
|
1271
|
+
// prettier-ignore
|
|
1272
|
+
body += `<script type="application/json" data-type="svelte-props">${s(shadow_props)}</script>`;
|
|
1273
|
+
}
|
|
1274
1274
|
}
|
|
1275
1275
|
|
|
1276
1276
|
if (options.service_worker) {
|
|
@@ -1476,6 +1476,7 @@ function is_root_relative(path) {
|
|
|
1476
1476
|
* $session: any;
|
|
1477
1477
|
* stuff: Record<string, any>;
|
|
1478
1478
|
* is_error: boolean;
|
|
1479
|
+
* is_leaf: boolean;
|
|
1479
1480
|
* status?: number;
|
|
1480
1481
|
* error?: Error;
|
|
1481
1482
|
* }} opts
|
|
@@ -1492,6 +1493,7 @@ async function load_node({
|
|
|
1492
1493
|
$session,
|
|
1493
1494
|
stuff,
|
|
1494
1495
|
is_error,
|
|
1496
|
+
is_leaf,
|
|
1495
1497
|
status,
|
|
1496
1498
|
error
|
|
1497
1499
|
}) {
|
|
@@ -1513,13 +1515,40 @@ async function load_node({
|
|
|
1513
1515
|
*/
|
|
1514
1516
|
let set_cookie_headers = [];
|
|
1515
1517
|
|
|
1518
|
+
/** @type {import('types/helper').Either<import('types/endpoint').Fallthrough, import('types/page').LoadOutput>} */
|
|
1516
1519
|
let loaded;
|
|
1517
1520
|
|
|
1518
|
-
|
|
1521
|
+
/** @type {import('types/endpoint').ShadowData} */
|
|
1522
|
+
const shadow = is_leaf
|
|
1523
|
+
? await load_shadow_data(
|
|
1524
|
+
/** @type {import('types/internal').SSRPage} */ (route),
|
|
1525
|
+
event,
|
|
1526
|
+
!!state.prerender
|
|
1527
|
+
)
|
|
1528
|
+
: {};
|
|
1529
|
+
|
|
1530
|
+
if (shadow.fallthrough) return;
|
|
1531
|
+
|
|
1532
|
+
if (shadow.cookies) {
|
|
1533
|
+
set_cookie_headers.push(...shadow.cookies);
|
|
1534
|
+
}
|
|
1535
|
+
|
|
1536
|
+
if (shadow.error) {
|
|
1537
|
+
loaded = {
|
|
1538
|
+
status: shadow.status,
|
|
1539
|
+
error: shadow.error
|
|
1540
|
+
};
|
|
1541
|
+
} else if (shadow.redirect) {
|
|
1542
|
+
loaded = {
|
|
1543
|
+
status: shadow.status,
|
|
1544
|
+
redirect: shadow.redirect
|
|
1545
|
+
};
|
|
1546
|
+
} else if (module.load) {
|
|
1519
1547
|
/** @type {import('types/page').LoadInput | import('types/page').ErrorLoadInput} */
|
|
1520
1548
|
const load_input = {
|
|
1521
1549
|
url: state.prerender ? create_prerendering_url_proxy(url) : url,
|
|
1522
1550
|
params,
|
|
1551
|
+
props: shadow.body || {},
|
|
1523
1552
|
get session() {
|
|
1524
1553
|
uses_credentials = true;
|
|
1525
1554
|
return $session;
|
|
@@ -1555,9 +1584,15 @@ async function load_node({
|
|
|
1555
1584
|
|
|
1556
1585
|
// merge headers from request
|
|
1557
1586
|
for (const [key, value] of event.request.headers) {
|
|
1558
|
-
if (
|
|
1559
|
-
|
|
1560
|
-
|
|
1587
|
+
if (
|
|
1588
|
+
key !== 'authorization' &&
|
|
1589
|
+
key !== 'cookie' &&
|
|
1590
|
+
key !== 'host' &&
|
|
1591
|
+
key !== 'if-none-match' &&
|
|
1592
|
+
!opts.headers.has(key)
|
|
1593
|
+
) {
|
|
1594
|
+
opts.headers.set(key, value);
|
|
1595
|
+
}
|
|
1561
1596
|
}
|
|
1562
1597
|
|
|
1563
1598
|
opts.headers.set('referer', event.url.href);
|
|
@@ -1745,6 +1780,10 @@ async function load_node({
|
|
|
1745
1780
|
if (!loaded) {
|
|
1746
1781
|
throw new Error(`load function must return a value${options.dev ? ` (${node.entry})` : ''}`);
|
|
1747
1782
|
}
|
|
1783
|
+
} else if (shadow.body) {
|
|
1784
|
+
loaded = {
|
|
1785
|
+
props: shadow.body
|
|
1786
|
+
};
|
|
1748
1787
|
} else {
|
|
1749
1788
|
loaded = {};
|
|
1750
1789
|
}
|
|
@@ -1753,8 +1792,21 @@ async function load_node({
|
|
|
1753
1792
|
return;
|
|
1754
1793
|
}
|
|
1755
1794
|
|
|
1795
|
+
// generate __data.json files when prerendering
|
|
1796
|
+
if (shadow.body && state.prerender) {
|
|
1797
|
+
const pathname = `${event.url.pathname}/__data.json`;
|
|
1798
|
+
|
|
1799
|
+
const dependency = {
|
|
1800
|
+
response: new Response(undefined),
|
|
1801
|
+
body: JSON.stringify(shadow.body)
|
|
1802
|
+
};
|
|
1803
|
+
|
|
1804
|
+
state.prerender.dependencies.set(pathname, dependency);
|
|
1805
|
+
}
|
|
1806
|
+
|
|
1756
1807
|
return {
|
|
1757
1808
|
node,
|
|
1809
|
+
props: shadow.body,
|
|
1758
1810
|
loaded: normalize(loaded),
|
|
1759
1811
|
stuff: loaded.stuff || stuff,
|
|
1760
1812
|
fetched,
|
|
@@ -1763,6 +1815,127 @@ async function load_node({
|
|
|
1763
1815
|
};
|
|
1764
1816
|
}
|
|
1765
1817
|
|
|
1818
|
+
/**
|
|
1819
|
+
*
|
|
1820
|
+
* @param {import('types/internal').SSRPage} route
|
|
1821
|
+
* @param {import('types/hooks').RequestEvent} event
|
|
1822
|
+
* @param {boolean} prerender
|
|
1823
|
+
* @returns {Promise<import('types/endpoint').ShadowData>}
|
|
1824
|
+
*/
|
|
1825
|
+
async function load_shadow_data(route, event, prerender) {
|
|
1826
|
+
if (!route.shadow) return {};
|
|
1827
|
+
|
|
1828
|
+
try {
|
|
1829
|
+
const mod = await route.shadow();
|
|
1830
|
+
|
|
1831
|
+
if (prerender && (mod.post || mod.put || mod.del || mod.patch)) {
|
|
1832
|
+
throw new Error('Cannot prerender pages that have shadow endpoints with mutative methods');
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1835
|
+
const method = event.request.method.toLowerCase().replace('delete', 'del');
|
|
1836
|
+
const handler = mod[method];
|
|
1837
|
+
|
|
1838
|
+
if (!handler) {
|
|
1839
|
+
return {
|
|
1840
|
+
status: 405,
|
|
1841
|
+
error: new Error(`${method} method not allowed`)
|
|
1842
|
+
};
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
/** @type {import('types/endpoint').ShadowData} */
|
|
1846
|
+
const data = {
|
|
1847
|
+
status: 200,
|
|
1848
|
+
cookies: [],
|
|
1849
|
+
body: {}
|
|
1850
|
+
};
|
|
1851
|
+
|
|
1852
|
+
if (method !== 'get') {
|
|
1853
|
+
const result = await handler(event);
|
|
1854
|
+
|
|
1855
|
+
if (result.fallthrough) return result;
|
|
1856
|
+
|
|
1857
|
+
const { status = 200, headers = {}, body = {} } = result;
|
|
1858
|
+
|
|
1859
|
+
validate_shadow_output(headers, body);
|
|
1860
|
+
|
|
1861
|
+
if (headers['set-cookie']) {
|
|
1862
|
+
/** @type {string[]} */ (data.cookies).push(...headers['set-cookie']);
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1865
|
+
// Redirects are respected...
|
|
1866
|
+
if (status >= 300 && status < 400) {
|
|
1867
|
+
return {
|
|
1868
|
+
status,
|
|
1869
|
+
redirect: /** @type {string} */ (
|
|
1870
|
+
headers instanceof Headers ? headers.get('location') : headers.location
|
|
1871
|
+
)
|
|
1872
|
+
};
|
|
1873
|
+
}
|
|
1874
|
+
|
|
1875
|
+
// ...but 4xx and 5xx status codes _don't_ result in the error page
|
|
1876
|
+
// rendering for non-GET requests — instead, we allow the page
|
|
1877
|
+
// to render with any validation errors etc that were returned
|
|
1878
|
+
data.status = status;
|
|
1879
|
+
data.body = body;
|
|
1880
|
+
}
|
|
1881
|
+
|
|
1882
|
+
if (mod.get) {
|
|
1883
|
+
const result = await mod.get.call(null, event);
|
|
1884
|
+
|
|
1885
|
+
if (result.fallthrough) return result;
|
|
1886
|
+
|
|
1887
|
+
const { status = 200, headers = {}, body = {} } = result;
|
|
1888
|
+
|
|
1889
|
+
validate_shadow_output(headers, body);
|
|
1890
|
+
|
|
1891
|
+
if (headers['set-cookie']) {
|
|
1892
|
+
/** @type {string[]} */ (data.cookies).push(...headers['set-cookie']);
|
|
1893
|
+
}
|
|
1894
|
+
|
|
1895
|
+
if (status >= 400) {
|
|
1896
|
+
return {
|
|
1897
|
+
status,
|
|
1898
|
+
error: new Error('Failed to load data')
|
|
1899
|
+
};
|
|
1900
|
+
}
|
|
1901
|
+
|
|
1902
|
+
if (status >= 300) {
|
|
1903
|
+
return {
|
|
1904
|
+
status,
|
|
1905
|
+
redirect: /** @type {string} */ (
|
|
1906
|
+
headers instanceof Headers ? headers.get('location') : headers.location
|
|
1907
|
+
)
|
|
1908
|
+
};
|
|
1909
|
+
}
|
|
1910
|
+
|
|
1911
|
+
data.body = { ...body, ...data.body };
|
|
1912
|
+
}
|
|
1913
|
+
|
|
1914
|
+
return data;
|
|
1915
|
+
} catch (e) {
|
|
1916
|
+
return {
|
|
1917
|
+
status: 500,
|
|
1918
|
+
error: coalesce_to_error(e)
|
|
1919
|
+
};
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
/**
|
|
1924
|
+
* @param {Headers | Partial<import('types/helper').ResponseHeaders>} headers
|
|
1925
|
+
* @param {import('types/helper').JSONValue} body
|
|
1926
|
+
*/
|
|
1927
|
+
function validate_shadow_output(headers, body) {
|
|
1928
|
+
if (headers instanceof Headers && headers.has('set-cookie')) {
|
|
1929
|
+
throw new Error(
|
|
1930
|
+
'Shadow endpoint request handler cannot use Headers interface with Set-Cookie headers'
|
|
1931
|
+
);
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1934
|
+
if (!is_pojo(body)) {
|
|
1935
|
+
throw new Error('Body returned from shadow endpoint request handler must be a plain object');
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
|
|
1766
1939
|
/**
|
|
1767
1940
|
* @typedef {import('./types.js').Loaded} Loaded
|
|
1768
1941
|
* @typedef {import('types/internal').SSROptions} SSROptions
|
|
@@ -1799,7 +1972,8 @@ async function respond_with_error({ event, options, state, $session, status, err
|
|
|
1799
1972
|
node: default_layout,
|
|
1800
1973
|
$session,
|
|
1801
1974
|
stuff: {},
|
|
1802
|
-
is_error: false
|
|
1975
|
+
is_error: false,
|
|
1976
|
+
is_leaf: false
|
|
1803
1977
|
})
|
|
1804
1978
|
);
|
|
1805
1979
|
|
|
@@ -1815,6 +1989,7 @@ async function respond_with_error({ event, options, state, $session, status, err
|
|
|
1815
1989
|
$session,
|
|
1816
1990
|
stuff: layout_loaded ? layout_loaded.stuff : {},
|
|
1817
1991
|
is_error: true,
|
|
1992
|
+
is_leaf: false,
|
|
1818
1993
|
status,
|
|
1819
1994
|
error
|
|
1820
1995
|
})
|
|
@@ -1947,7 +2122,8 @@ async function respond$1(opts) {
|
|
|
1947
2122
|
url: event.url,
|
|
1948
2123
|
node,
|
|
1949
2124
|
stuff,
|
|
1950
|
-
is_error: false
|
|
2125
|
+
is_error: false,
|
|
2126
|
+
is_leaf: i === nodes.length - 1
|
|
1951
2127
|
});
|
|
1952
2128
|
|
|
1953
2129
|
if (!loaded) return;
|
|
@@ -2002,6 +2178,7 @@ async function respond$1(opts) {
|
|
|
2002
2178
|
node: error_node,
|
|
2003
2179
|
stuff: node_loaded.stuff,
|
|
2004
2180
|
is_error: true,
|
|
2181
|
+
is_leaf: false,
|
|
2005
2182
|
status,
|
|
2006
2183
|
error
|
|
2007
2184
|
})
|
|
@@ -2115,13 +2292,12 @@ function with_cookies(response, set_cookie_headers) {
|
|
|
2115
2292
|
/**
|
|
2116
2293
|
* @param {import('types/hooks').RequestEvent} event
|
|
2117
2294
|
* @param {import('types/internal').SSRPage} route
|
|
2118
|
-
* @param {RegExpExecArray} match
|
|
2119
2295
|
* @param {import('types/internal').SSROptions} options
|
|
2120
2296
|
* @param {import('types/internal').SSRState} state
|
|
2121
2297
|
* @param {boolean} ssr
|
|
2122
2298
|
* @returns {Promise<Response | undefined>}
|
|
2123
2299
|
*/
|
|
2124
|
-
async function render_page(event, route,
|
|
2300
|
+
async function render_page(event, route, options, state, ssr) {
|
|
2125
2301
|
if (state.initiator === route) {
|
|
2126
2302
|
// infinite request cycle detected
|
|
2127
2303
|
return new Response(`Not found: ${event.url.pathname}`, {
|
|
@@ -2129,7 +2305,16 @@ async function render_page(event, route, match, options, state, ssr) {
|
|
|
2129
2305
|
});
|
|
2130
2306
|
}
|
|
2131
2307
|
|
|
2132
|
-
|
|
2308
|
+
if (route.shadow) {
|
|
2309
|
+
const type = negotiate(event.request.headers.get('accept') || 'text/html', [
|
|
2310
|
+
'text/html',
|
|
2311
|
+
'application/json'
|
|
2312
|
+
]);
|
|
2313
|
+
|
|
2314
|
+
if (type === 'application/json') {
|
|
2315
|
+
return render_endpoint(event, await route.shadow());
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2133
2318
|
|
|
2134
2319
|
const $session = await options.hooks.getSession(event);
|
|
2135
2320
|
|
|
@@ -2139,7 +2324,7 @@ async function render_page(event, route, match, options, state, ssr) {
|
|
|
2139
2324
|
state,
|
|
2140
2325
|
$session,
|
|
2141
2326
|
route,
|
|
2142
|
-
params,
|
|
2327
|
+
params: event.params, // TODO this is redundant
|
|
2143
2328
|
ssr
|
|
2144
2329
|
});
|
|
2145
2330
|
|
|
@@ -2158,6 +2343,60 @@ async function render_page(event, route, match, options, state, ssr) {
|
|
|
2158
2343
|
}
|
|
2159
2344
|
}
|
|
2160
2345
|
|
|
2346
|
+
/**
|
|
2347
|
+
* @param {string} accept
|
|
2348
|
+
* @param {string[]} types
|
|
2349
|
+
*/
|
|
2350
|
+
function negotiate(accept, types) {
|
|
2351
|
+
const parts = accept
|
|
2352
|
+
.split(',')
|
|
2353
|
+
.map((str, i) => {
|
|
2354
|
+
const match = /([^/]+)\/([^;]+)(?:;q=([0-9.]+))?/.exec(str);
|
|
2355
|
+
if (match) {
|
|
2356
|
+
const [, type, subtype, q = '1'] = match;
|
|
2357
|
+
return { type, subtype, q: +q, i };
|
|
2358
|
+
}
|
|
2359
|
+
|
|
2360
|
+
throw new Error(`Invalid Accept header: ${accept}`);
|
|
2361
|
+
})
|
|
2362
|
+
.sort((a, b) => {
|
|
2363
|
+
if (a.q !== b.q) {
|
|
2364
|
+
return b.q - a.q;
|
|
2365
|
+
}
|
|
2366
|
+
|
|
2367
|
+
if ((a.subtype === '*') !== (b.subtype === '*')) {
|
|
2368
|
+
return a.subtype === '*' ? 1 : -1;
|
|
2369
|
+
}
|
|
2370
|
+
|
|
2371
|
+
if ((a.type === '*') !== (b.type === '*')) {
|
|
2372
|
+
return a.type === '*' ? 1 : -1;
|
|
2373
|
+
}
|
|
2374
|
+
|
|
2375
|
+
return a.i - b.i;
|
|
2376
|
+
});
|
|
2377
|
+
|
|
2378
|
+
let accepted;
|
|
2379
|
+
let min_priority = Infinity;
|
|
2380
|
+
|
|
2381
|
+
for (const mimetype of types) {
|
|
2382
|
+
const [type, subtype] = mimetype.split('/');
|
|
2383
|
+
const priority = parts.findIndex(
|
|
2384
|
+
(part) =>
|
|
2385
|
+
(part.type === type || part.type === '*') &&
|
|
2386
|
+
(part.subtype === subtype || part.subtype === '*')
|
|
2387
|
+
);
|
|
2388
|
+
|
|
2389
|
+
if (priority !== -1 && priority < min_priority) {
|
|
2390
|
+
accepted = mimetype;
|
|
2391
|
+
min_priority = priority;
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
|
|
2395
|
+
return accepted;
|
|
2396
|
+
}
|
|
2397
|
+
|
|
2398
|
+
const DATA_SUFFIX = '/__data.json';
|
|
2399
|
+
|
|
2161
2400
|
/** @type {import('types/internal').Respond} */
|
|
2162
2401
|
async function respond(request, options, state = {}) {
|
|
2163
2402
|
const url = new URL(request.url);
|
|
@@ -2284,14 +2523,46 @@ async function respond(request, options, state = {}) {
|
|
|
2284
2523
|
decoded = decoded.slice(options.paths.base.length) || '/';
|
|
2285
2524
|
}
|
|
2286
2525
|
|
|
2526
|
+
const is_data_request = decoded.endsWith(DATA_SUFFIX);
|
|
2527
|
+
if (is_data_request) decoded = decoded.slice(0, -DATA_SUFFIX.length) || '/';
|
|
2528
|
+
|
|
2287
2529
|
for (const route of options.manifest._.routes) {
|
|
2288
2530
|
const match = route.pattern.exec(decoded);
|
|
2289
2531
|
if (!match) continue;
|
|
2290
2532
|
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2533
|
+
event.params = route.params ? decode_params(route.params(match)) : {};
|
|
2534
|
+
|
|
2535
|
+
/** @type {Response | undefined} */
|
|
2536
|
+
let response;
|
|
2537
|
+
|
|
2538
|
+
if (is_data_request && route.type === 'page' && route.shadow) {
|
|
2539
|
+
response = await render_endpoint(event, await route.shadow());
|
|
2540
|
+
|
|
2541
|
+
// since redirects are opaque to the browser, we need to repackage
|
|
2542
|
+
// 3xx responses as 200s with a custom header
|
|
2543
|
+
if (
|
|
2544
|
+
response &&
|
|
2545
|
+
response.status >= 300 &&
|
|
2546
|
+
response.status < 400 &&
|
|
2547
|
+
request.headers.get('x-sveltekit-noredirect') === 'true'
|
|
2548
|
+
) {
|
|
2549
|
+
const location = response.headers.get('location');
|
|
2550
|
+
|
|
2551
|
+
if (location) {
|
|
2552
|
+
response = new Response(undefined, {
|
|
2553
|
+
status: 204,
|
|
2554
|
+
headers: {
|
|
2555
|
+
'x-sveltekit-location': location
|
|
2556
|
+
}
|
|
2557
|
+
});
|
|
2558
|
+
}
|
|
2559
|
+
}
|
|
2560
|
+
} else {
|
|
2561
|
+
response =
|
|
2562
|
+
route.type === 'endpoint'
|
|
2563
|
+
? await render_endpoint(event, await route.load())
|
|
2564
|
+
: await render_page(event, route, options, state, ssr);
|
|
2565
|
+
}
|
|
2295
2566
|
|
|
2296
2567
|
if (response) {
|
|
2297
2568
|
// respond with 304 if etag matches
|
package/dist/chunks/index.js
CHANGED
|
@@ -118,6 +118,12 @@ async function create_plugin(config, cwd) {
|
|
|
118
118
|
type: 'page',
|
|
119
119
|
pattern: route.pattern,
|
|
120
120
|
params: get_params(route.params),
|
|
121
|
+
shadow: route.shadow
|
|
122
|
+
? async () => {
|
|
123
|
+
const url = path__default.resolve(cwd, /** @type {string} */ (route.shadow));
|
|
124
|
+
return await vite.ssrLoadModule(url);
|
|
125
|
+
}
|
|
126
|
+
: null,
|
|
121
127
|
a: route.a.map((id) => manifest_data.components.indexOf(id)),
|
|
122
128
|
b: route.b.map((id) => manifest_data.components.indexOf(id))
|
|
123
129
|
};
|
package/dist/chunks/index2.js
CHANGED
|
@@ -165,7 +165,10 @@ function generate_client_manifest(manifest_data, base) {
|
|
|
165
165
|
'})';
|
|
166
166
|
|
|
167
167
|
const tuple = [route.pattern, get_indices(route.a), get_indices(route.b)];
|
|
168
|
-
|
|
168
|
+
|
|
169
|
+
// optional items
|
|
170
|
+
if (params || route.shadow) tuple.push(params || 'null');
|
|
171
|
+
if (route.shadow) tuple.push('1');
|
|
169
172
|
|
|
170
173
|
return `// ${route.a[route.a.length - 1]}\n\t\t[${tuple.join(', ')}]`;
|
|
171
174
|
}
|
|
@@ -382,6 +385,7 @@ var mime = new Mime(standard, other);
|
|
|
382
385
|
* }} Part
|
|
383
386
|
* @typedef {{
|
|
384
387
|
* basename: string;
|
|
388
|
+
* name: string;
|
|
385
389
|
* ext: string;
|
|
386
390
|
* parts: Part[],
|
|
387
391
|
* file: string;
|
|
@@ -427,12 +431,13 @@ function create_manifest_data({
|
|
|
427
431
|
|
|
428
432
|
/**
|
|
429
433
|
* @param {string} dir
|
|
434
|
+
* @param {string[]} parent_key
|
|
430
435
|
* @param {Part[][]} parent_segments
|
|
431
436
|
* @param {string[]} parent_params
|
|
432
437
|
* @param {Array<string|undefined>} layout_stack // accumulated __layout.svelte components
|
|
433
438
|
* @param {Array<string|undefined>} error_stack // accumulated __error.svelte components
|
|
434
439
|
*/
|
|
435
|
-
function walk(dir, parent_segments, parent_params, layout_stack, error_stack) {
|
|
440
|
+
function walk(dir, parent_key, parent_segments, parent_params, layout_stack, error_stack) {
|
|
436
441
|
/** @type {Item[]} */
|
|
437
442
|
let items = [];
|
|
438
443
|
fs__default.readdirSync(dir).forEach((basename) => {
|
|
@@ -482,6 +487,7 @@ function create_manifest_data({
|
|
|
482
487
|
|
|
483
488
|
items.push({
|
|
484
489
|
basename,
|
|
490
|
+
name,
|
|
485
491
|
ext,
|
|
486
492
|
parts,
|
|
487
493
|
file,
|
|
@@ -494,6 +500,7 @@ function create_manifest_data({
|
|
|
494
500
|
items = items.sort(comparator);
|
|
495
501
|
|
|
496
502
|
items.forEach((item) => {
|
|
503
|
+
const key = parent_key.slice();
|
|
497
504
|
const segments = parent_segments.slice();
|
|
498
505
|
|
|
499
506
|
if (item.is_index) {
|
|
@@ -517,11 +524,13 @@ function create_manifest_data({
|
|
|
517
524
|
}
|
|
518
525
|
|
|
519
526
|
segments[segments.length - 1] = last_segment;
|
|
527
|
+
key[key.length - 1] += item.route_suffix;
|
|
520
528
|
} else {
|
|
521
529
|
segments.push(item.parts);
|
|
522
530
|
}
|
|
523
531
|
}
|
|
524
532
|
} else {
|
|
533
|
+
key.push(item.name);
|
|
525
534
|
segments.push(item.parts);
|
|
526
535
|
}
|
|
527
536
|
|
|
@@ -555,6 +564,7 @@ function create_manifest_data({
|
|
|
555
564
|
|
|
556
565
|
walk(
|
|
557
566
|
path__default.join(dir, item.basename),
|
|
567
|
+
key,
|
|
558
568
|
segments,
|
|
559
569
|
params,
|
|
560
570
|
layout_reset ? [layout_reset] : layout_stack.concat(layout),
|
|
@@ -589,10 +599,12 @@ function create_manifest_data({
|
|
|
589
599
|
|
|
590
600
|
routes.push({
|
|
591
601
|
type: 'page',
|
|
602
|
+
key: key.join('/'),
|
|
592
603
|
segments: simple_segments,
|
|
593
604
|
pattern,
|
|
594
605
|
params,
|
|
595
606
|
path,
|
|
607
|
+
shadow: null,
|
|
596
608
|
a: /** @type {string[]} */ (concatenated),
|
|
597
609
|
b: /** @type {string[]} */ (errors)
|
|
598
610
|
});
|
|
@@ -601,6 +613,7 @@ function create_manifest_data({
|
|
|
601
613
|
|
|
602
614
|
routes.push({
|
|
603
615
|
type: 'endpoint',
|
|
616
|
+
key: key.join('/'),
|
|
604
617
|
segments: simple_segments,
|
|
605
618
|
pattern,
|
|
606
619
|
file: item.file,
|
|
@@ -617,7 +630,24 @@ function create_manifest_data({
|
|
|
617
630
|
|
|
618
631
|
components.push(layout, error);
|
|
619
632
|
|
|
620
|
-
walk(config.kit.files.routes, [], [], [layout], [error]);
|
|
633
|
+
walk(config.kit.files.routes, [], [], [], [layout], [error]);
|
|
634
|
+
|
|
635
|
+
// merge matching page/endpoint pairs into shadowed pages
|
|
636
|
+
let i = routes.length;
|
|
637
|
+
while (i--) {
|
|
638
|
+
const route = routes[i];
|
|
639
|
+
const prev = routes[i - 1];
|
|
640
|
+
|
|
641
|
+
if (prev && prev.key === route.key) {
|
|
642
|
+
if (prev.type !== 'endpoint' || route.type !== 'page') {
|
|
643
|
+
const relative = path__default.relative(cwd, path__default.resolve(config.kit.files.routes, prev.key));
|
|
644
|
+
throw new Error(`Duplicate route files: ${relative}`);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
route.shadow = prev.file;
|
|
648
|
+
routes.splice(--i, 1);
|
|
649
|
+
}
|
|
650
|
+
}
|
|
621
651
|
|
|
622
652
|
const assets = fs__default.existsSync(config.kit.files.assets)
|
|
623
653
|
? list_files({ config, dir: config.kit.files.assets, path: '' })
|
package/dist/chunks/index3.js
CHANGED
|
@@ -383,8 +383,10 @@ async function build_server(
|
|
|
383
383
|
|
|
384
384
|
// add entry points for every endpoint...
|
|
385
385
|
manifest_data.routes.forEach((route) => {
|
|
386
|
-
|
|
387
|
-
|
|
386
|
+
const file = route.type === 'endpoint' ? route.file : route.shadow;
|
|
387
|
+
|
|
388
|
+
if (file) {
|
|
389
|
+
const resolved = path__default.resolve(cwd, file);
|
|
388
390
|
const relative = path__default.relative(config.kit.files.routes, resolved);
|
|
389
391
|
const name = posixify(path__default.join('entries/endpoints', relative.replace(/\.js$/, '')));
|
|
390
392
|
input[name] = resolved;
|
|
@@ -468,24 +470,6 @@ async function build_server(
|
|
|
468
470
|
|
|
469
471
|
const { chunks } = await create_build(merged_config);
|
|
470
472
|
|
|
471
|
-
/** @type {Record<string, string[]>} */
|
|
472
|
-
const lookup = {};
|
|
473
|
-
chunks.forEach((chunk) => {
|
|
474
|
-
if (!chunk.facadeModuleId) return;
|
|
475
|
-
const id = chunk.facadeModuleId.slice(cwd.length + 1);
|
|
476
|
-
lookup[id] = chunk.exports;
|
|
477
|
-
});
|
|
478
|
-
|
|
479
|
-
/** @type {Record<string, import('types/internal').HttpMethod[]>} */
|
|
480
|
-
const methods = {};
|
|
481
|
-
manifest_data.routes.forEach((route) => {
|
|
482
|
-
if (route.type === 'endpoint' && lookup[route.file]) {
|
|
483
|
-
methods[route.file] = lookup[route.file]
|
|
484
|
-
.map((x) => /** @type {import('types/internal').HttpMethod} */ (method_names[x]))
|
|
485
|
-
.filter(Boolean);
|
|
486
|
-
}
|
|
487
|
-
});
|
|
488
|
-
|
|
489
473
|
/** @type {import('vite').Manifest} */
|
|
490
474
|
const vite_manifest = JSON.parse(fs__default.readFileSync(`${output_dir}/server/manifest.json`, 'utf-8'));
|
|
491
475
|
|
|
@@ -576,8 +560,10 @@ function get_methods(cwd, output, manifest_data) {
|
|
|
576
560
|
/** @type {Record<string, import('types/internal').HttpMethod[]>} */
|
|
577
561
|
const methods = {};
|
|
578
562
|
manifest_data.routes.forEach((route) => {
|
|
579
|
-
|
|
580
|
-
|
|
563
|
+
const file = route.type === 'endpoint' ? route.file : route.shadow;
|
|
564
|
+
|
|
565
|
+
if (file && lookup[file]) {
|
|
566
|
+
methods[file] = lookup[file]
|
|
581
567
|
.map((x) => /** @type {import('types/internal').HttpMethod} */ (method_names[x]))
|
|
582
568
|
.filter(Boolean);
|
|
583
569
|
}
|
package/dist/chunks/index4.js
CHANGED
|
@@ -70,6 +70,7 @@ function generate_manifest(
|
|
|
70
70
|
pattern: ${route.pattern},
|
|
71
71
|
params: ${get_params(route.params)},
|
|
72
72
|
path: ${route.path ? s(route.path) : null},
|
|
73
|
+
shadow: ${route.shadow ? importer(`${relative_path}/${build_data.server.vite_manifest[route.shadow].file}`) : null},
|
|
73
74
|
a: ${s(route.a.map(component => component && bundled_nodes.get(component).index))},
|
|
74
75
|
b: ${s(route.b.map(component => component && bundled_nodes.get(component).index))}
|
|
75
76
|
}`.replace(/^\t\t/gm, '');
|
package/dist/cli.js
CHANGED
|
@@ -995,7 +995,7 @@ async function launch(port, https) {
|
|
|
995
995
|
exec(`${cmd} ${https ? 'https' : 'http'}://localhost:${port}`);
|
|
996
996
|
}
|
|
997
997
|
|
|
998
|
-
const prog = sade('svelte-kit').version('1.0.0-next.
|
|
998
|
+
const prog = sade('svelte-kit').version('1.0.0-next.260');
|
|
999
999
|
|
|
1000
1000
|
prog
|
|
1001
1001
|
.command('dev')
|
|
@@ -1153,7 +1153,7 @@ async function check_port(port) {
|
|
|
1153
1153
|
function welcome({ port, host, https, open, loose, allow, cwd }) {
|
|
1154
1154
|
if (open) launch(port, https);
|
|
1155
1155
|
|
|
1156
|
-
console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.
|
|
1156
|
+
console.log($.bold().cyan(`\n SvelteKit v${'1.0.0-next.260'}\n`));
|
|
1157
1157
|
|
|
1158
1158
|
const protocol = https ? 'https:' : 'http:';
|
|
1159
1159
|
const exposed = typeof host !== 'undefined' && host !== 'localhost' && host !== '127.0.0.1';
|
package/package.json
CHANGED
package/types/endpoint.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RequestEvent } from './hooks';
|
|
2
|
-
import { Either, JSONValue, MaybePromise, ResponseHeaders } from './helper';
|
|
2
|
+
import { Either, JSONObject, JSONValue, MaybePromise, ResponseHeaders } from './helper';
|
|
3
3
|
|
|
4
4
|
type Body = JSONValue | Uint8Array | ReadableStream | import('stream').Readable;
|
|
5
5
|
|
|
@@ -14,5 +14,26 @@ export interface Fallthrough {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
export interface RequestHandler<Output extends Body = Body> {
|
|
17
|
-
(event: RequestEvent): MaybePromise<
|
|
17
|
+
(event: RequestEvent): MaybePromise<
|
|
18
|
+
Either<Output extends Response ? Response : EndpointOutput<Output>, Fallthrough>
|
|
19
|
+
>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ShadowEndpointOutput<Output extends JSONObject = JSONObject> {
|
|
23
|
+
status?: number;
|
|
24
|
+
headers?: Partial<ResponseHeaders>;
|
|
25
|
+
body?: Output;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ShadowRequestHandler<Output extends JSONObject = JSONObject> {
|
|
29
|
+
(event: RequestEvent): MaybePromise<Either<ShadowEndpointOutput<Output>, Fallthrough>>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ShadowData {
|
|
33
|
+
fallthrough?: boolean;
|
|
34
|
+
status?: number;
|
|
35
|
+
error?: Error;
|
|
36
|
+
redirect?: string;
|
|
37
|
+
cookies?: string[];
|
|
38
|
+
body?: JSONObject;
|
|
18
39
|
}
|
package/types/helper.d.ts
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
type ToJSON = { toJSON(...args: any[]): Exclude<JSONValue, ToJSON> };
|
|
2
2
|
|
|
3
|
-
export type JSONValue
|
|
4
|
-
|
|
5
|
-
| number
|
|
6
|
-
| boolean
|
|
7
|
-
| null
|
|
8
|
-
| ToJSON
|
|
9
|
-
| JSONValue[]
|
|
10
|
-
| { [key: string]: JSONValue };
|
|
3
|
+
export type JSONObject = { [key: string]: JSONValue };
|
|
4
|
+
export type JSONValue = string | number | boolean | null | ToJSON | JSONValue[] | JSONObject;
|
|
11
5
|
|
|
12
6
|
/** `string[]` is only for set-cookie, everything else must be type of `string` */
|
|
13
7
|
export type ResponseHeaders = Record<string, string | string[]>;
|
package/types/internal.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { OutputAsset, OutputChunk } from 'rollup';
|
|
2
2
|
import { ValidatedConfig } from './config';
|
|
3
3
|
import { InternalApp, SSRManifest } from './app';
|
|
4
|
-
import { Fallthrough, RequestHandler } from './endpoint';
|
|
4
|
+
import { Fallthrough, RequestHandler, ShadowRequestHandler } from './endpoint';
|
|
5
5
|
import { Either } from './helper';
|
|
6
6
|
import { ExternalFetch, GetSession, Handle, HandleError, RequestEvent } from './hooks';
|
|
7
7
|
import { Load } from './page';
|
|
@@ -74,6 +74,11 @@ export interface SSRPage {
|
|
|
74
74
|
type: 'page';
|
|
75
75
|
pattern: RegExp;
|
|
76
76
|
params: GetParams;
|
|
77
|
+
shadow:
|
|
78
|
+
| null
|
|
79
|
+
| (() => Promise<{
|
|
80
|
+
[method: string]: ShadowRequestHandler;
|
|
81
|
+
}>);
|
|
77
82
|
/**
|
|
78
83
|
* plan a is to render 1 or more layout components followed by a leaf component.
|
|
79
84
|
*/
|
|
@@ -96,7 +101,8 @@ export interface SSREndpoint {
|
|
|
96
101
|
|
|
97
102
|
export type SSRRoute = SSREndpoint | SSRPage;
|
|
98
103
|
|
|
99
|
-
|
|
104
|
+
type HasShadow = 1;
|
|
105
|
+
export type CSRRoute = [RegExp, CSRComponentLoader[], CSRComponentLoader[], GetParams?, HasShadow?];
|
|
100
106
|
|
|
101
107
|
export type SSRNodeLoader = () => Promise<SSRNode>;
|
|
102
108
|
|
|
@@ -179,6 +185,8 @@ export type HttpMethod = 'get' | 'head' | 'post' | 'put' | 'delete' | 'patch';
|
|
|
179
185
|
|
|
180
186
|
export interface PageData {
|
|
181
187
|
type: 'page';
|
|
188
|
+
key: string;
|
|
189
|
+
shadow: string | null;
|
|
182
190
|
segments: RouteSegment[];
|
|
183
191
|
pattern: RegExp;
|
|
184
192
|
params: string[];
|
|
@@ -189,6 +197,7 @@ export interface PageData {
|
|
|
189
197
|
|
|
190
198
|
export interface EndpointData {
|
|
191
199
|
type: 'endpoint';
|
|
200
|
+
key: string;
|
|
192
201
|
segments: RouteSegment[];
|
|
193
202
|
pattern: RegExp;
|
|
194
203
|
params: string[];
|
package/types/page.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { Either, MaybePromise } from './helper';
|
|
|
4
4
|
export interface LoadInput<Params = Record<string, string>> {
|
|
5
5
|
url: URL;
|
|
6
6
|
params: Params;
|
|
7
|
+
props: Record<string, any>;
|
|
7
8
|
fetch(info: RequestInfo, init?: RequestInit): Promise<Response>;
|
|
8
9
|
session: App.Session;
|
|
9
10
|
stuff: Partial<App.Stuff>;
|