@solidjs/web 2.0.0-beta.21 → 2.0.0-beta.23
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/dist/dev.cjs +51 -1
- package/dist/dev.js +47 -2
- package/dist/server.cjs +98 -34
- package/dist/server.js +94 -35
- package/dist/web.cjs +51 -1
- package/dist/web.js +47 -2
- package/frames/dist/client.cjs +1442 -0
- package/frames/dist/client.js +1430 -0
- package/frames/dist/server.cjs +1705 -0
- package/frames/dist/server.js +1694 -0
- package/frames/package.json +30 -0
- package/package.json +78 -5
- package/serialization/dist/serialization.cjs +83 -0
- package/serialization/dist/serialization.js +82 -1
- package/serialization/types/index.d.ts +12 -0
- package/serialization/types-cjs/index.d.cts +12 -0
- package/server-functions/dist/client.cjs +114 -59
- package/server-functions/dist/client.js +113 -61
- package/server-functions/dist/server.cjs +54 -10
- package/server-functions/dist/server.js +52 -11
- package/types/client.d.ts +26 -0
- package/types/core.d.ts +2 -1
- package/types/frames/client.d.ts +53 -0
- package/types/frames/frame-client.d.ts +205 -0
- package/types/frames/frame-sink.d.ts +145 -0
- package/types/frames/frame-transport.d.ts +105 -0
- package/types/frames/serializer.d.ts +151 -0
- package/types/frames/server.d.ts +21 -0
- package/types/jsx.d.ts +17 -2
- package/types/response.d.ts +27 -1
- package/types/serializer.d.ts +12 -0
- package/types/server-functions/client.d.ts +40 -5
- package/types/server-functions/server.d.ts +9 -5
- package/types/server-functions/shared.d.ts +29 -0
- package/types/server.d.ts +10 -0
- package/types-cjs/client.d.cts +26 -0
- package/types-cjs/core.d.cts +2 -1
- package/types-cjs/frames/client.d.cts +53 -0
- package/types-cjs/frames/frame-client.d.cts +205 -0
- package/types-cjs/frames/frame-sink.d.cts +145 -0
- package/types-cjs/frames/frame-transport.d.cts +105 -0
- package/types-cjs/frames/serializer.d.cts +151 -0
- package/types-cjs/frames/server.d.cts +21 -0
- package/types-cjs/jsx.d.cts +17 -2
- package/types-cjs/response.d.cts +27 -1
- package/types-cjs/serializer.d.cts +12 -0
- package/types-cjs/server-functions/client.d.cts +40 -5
- package/types-cjs/server-functions/server.d.cts +9 -5
- package/types-cjs/server-functions/shared.d.cts +29 -0
- package/types-cjs/server.d.cts +10 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@solidjs/web/frames",
|
|
3
|
+
"main": "./dist/server.cjs",
|
|
4
|
+
"module": "./dist/server.js",
|
|
5
|
+
"types": "../types/frames/server.d.ts",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"browser": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "../types/frames/client.d.ts",
|
|
13
|
+
"default": "./dist/client.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "../types-cjs/frames/client.d.cts",
|
|
17
|
+
"default": "./dist/client.cjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"import": {
|
|
21
|
+
"types": "../types/frames/server.d.ts",
|
|
22
|
+
"default": "./dist/server.js"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "../types-cjs/frames/server.d.cts",
|
|
26
|
+
"default": "./dist/server.cjs"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidjs/web",
|
|
3
3
|
"description": "Solid's web runtime: client rendering, hydration, SSR, and DOM-specific control flow (Portal, Dynamic).",
|
|
4
|
-
"version": "2.0.0-beta.
|
|
4
|
+
"version": "2.0.0-beta.23",
|
|
5
5
|
"author": "Ryan Carniato",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://solidjs.com",
|
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
"serialization/types-cjs",
|
|
35
35
|
"serialization/package.json",
|
|
36
36
|
"server-functions/dist",
|
|
37
|
-
"server-functions/package.json"
|
|
37
|
+
"server-functions/package.json",
|
|
38
|
+
"frames/dist",
|
|
39
|
+
"frames/package.json"
|
|
38
40
|
],
|
|
39
41
|
"exports": {
|
|
40
42
|
".": {
|
|
@@ -217,6 +219,76 @@
|
|
|
217
219
|
"default": "./server-functions/dist/client.cjs"
|
|
218
220
|
}
|
|
219
221
|
},
|
|
222
|
+
"./frames": {
|
|
223
|
+
"worker": {
|
|
224
|
+
"import": {
|
|
225
|
+
"types": "./types/frames/server.d.ts",
|
|
226
|
+
"default": "./frames/dist/server.js"
|
|
227
|
+
},
|
|
228
|
+
"require": {
|
|
229
|
+
"types": "./types-cjs/frames/server.d.cts",
|
|
230
|
+
"default": "./frames/dist/server.cjs"
|
|
231
|
+
}
|
|
232
|
+
},
|
|
233
|
+
"browser": {
|
|
234
|
+
"import": {
|
|
235
|
+
"types": "./types/frames/client.d.ts",
|
|
236
|
+
"default": "./frames/dist/client.js"
|
|
237
|
+
},
|
|
238
|
+
"require": {
|
|
239
|
+
"types": "./types-cjs/frames/client.d.cts",
|
|
240
|
+
"default": "./frames/dist/client.cjs"
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
"deno": {
|
|
244
|
+
"import": {
|
|
245
|
+
"types": "./types/frames/server.d.ts",
|
|
246
|
+
"default": "./frames/dist/server.js"
|
|
247
|
+
},
|
|
248
|
+
"require": {
|
|
249
|
+
"types": "./types-cjs/frames/server.d.cts",
|
|
250
|
+
"default": "./frames/dist/server.cjs"
|
|
251
|
+
}
|
|
252
|
+
},
|
|
253
|
+
"node": {
|
|
254
|
+
"import": {
|
|
255
|
+
"types": "./types/frames/server.d.ts",
|
|
256
|
+
"default": "./frames/dist/server.js"
|
|
257
|
+
},
|
|
258
|
+
"require": {
|
|
259
|
+
"types": "./types-cjs/frames/server.d.cts",
|
|
260
|
+
"default": "./frames/dist/server.cjs"
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
"import": {
|
|
264
|
+
"types": "./types/frames/client.d.ts",
|
|
265
|
+
"default": "./frames/dist/client.js"
|
|
266
|
+
},
|
|
267
|
+
"require": {
|
|
268
|
+
"types": "./types-cjs/frames/client.d.cts",
|
|
269
|
+
"default": "./frames/dist/client.cjs"
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
"./frames/server": {
|
|
273
|
+
"import": {
|
|
274
|
+
"types": "./types/frames/server.d.ts",
|
|
275
|
+
"default": "./frames/dist/server.js"
|
|
276
|
+
},
|
|
277
|
+
"require": {
|
|
278
|
+
"types": "./types-cjs/frames/server.d.cts",
|
|
279
|
+
"default": "./frames/dist/server.cjs"
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
"./frames/client": {
|
|
283
|
+
"import": {
|
|
284
|
+
"types": "./types/frames/client.d.ts",
|
|
285
|
+
"default": "./frames/dist/client.js"
|
|
286
|
+
},
|
|
287
|
+
"require": {
|
|
288
|
+
"types": "./types-cjs/frames/client.d.cts",
|
|
289
|
+
"default": "./frames/dist/client.cjs"
|
|
290
|
+
}
|
|
291
|
+
},
|
|
220
292
|
"./types/*": "./types/*"
|
|
221
293
|
},
|
|
222
294
|
"dependencies": {
|
|
@@ -224,17 +296,17 @@
|
|
|
224
296
|
"seroval-plugins": "~1.5.4"
|
|
225
297
|
},
|
|
226
298
|
"peerDependencies": {
|
|
227
|
-
"solid-js": "^2.0.0-beta.
|
|
299
|
+
"solid-js": "^2.0.0-beta.23"
|
|
228
300
|
},
|
|
229
301
|
"devDependencies": {
|
|
230
|
-
"solid-js": "2.0.0-beta.
|
|
302
|
+
"solid-js": "2.0.0-beta.23"
|
|
231
303
|
},
|
|
232
304
|
"scripts": {
|
|
233
305
|
"build": "npm-run-all -nl build:clean types:copy-jsx build:js",
|
|
234
306
|
"build:clean": "rimraf dist/",
|
|
235
307
|
"build:js": "rollup -c",
|
|
236
308
|
"link": "symlink-dir . node_modules/@solidjs/web",
|
|
237
|
-
"types": "npm-run-all -nl types:clean types:copy-jsx types:web types:copy-web types:web-storage types:copy-serialization types:copy-server-functions types:cjs",
|
|
309
|
+
"types": "npm-run-all -nl types:clean types:copy-jsx types:web types:copy-web types:web-storage types:copy-serialization types:copy-server-functions types:copy-frames types:cjs",
|
|
238
310
|
"types:clean": "rimraf types/ types-cjs/ storage/types/ storage/types-cjs/ serialization/types/ serialization/types-cjs/",
|
|
239
311
|
"types:copy-jsx": "ncp ../../node_modules/@dom-expressions/runtime/src/jsx.d.ts ./src/jsx.d.ts && ncp ../../node_modules/@dom-expressions/runtime/src/jsx-properties.d.ts ./src/jsx-properties.d.ts && dom-expressions-jsx-types --input ./src/jsx.d.ts --element \"SolidElement | Node | ArrayElement\" --import 'import type { Element as SolidElement } from \"solid-js\";'",
|
|
240
312
|
"types:web": "tsc --project ./tsconfig.build.json",
|
|
@@ -242,6 +314,7 @@
|
|
|
242
314
|
"types:web-storage": "tsc --project ./storage/tsconfig.build.json",
|
|
243
315
|
"types:copy-serialization": "node -e \"fs.mkdirSync('./serialization/types', { recursive: true }); fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/serializer.d.ts', './serialization/types/index.d.ts');\"",
|
|
244
316
|
"types:copy-server-functions": "node -e \"fs.mkdirSync('./types/server-functions', { recursive: true }); for (const f of ['shared', 'client', 'server']) fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/server-functions/' + f + '.d.ts', './types/server-functions/' + f + '.d.ts');\"",
|
|
317
|
+
"types:copy-frames": "node -e \"fs.mkdirSync('./types/frames', { recursive: true }); for (const f of ['frame-client', 'frame-transport', 'frame-sink', 'serializer']) fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/' + f + '.d.ts', './types/frames/' + f + '.d.ts'); for (const f of ['client', 'server']) fs.copyFileSync('./frames/types/' + f + '.d.ts', './types/frames/' + f + '.d.ts');\"",
|
|
245
318
|
"types:cjs": "node ../../scripts/sync-dual-types.mjs ./types ./types-cjs ./storage/types ./storage/types-cjs ./serialization/types ./serialization/types-cjs",
|
|
246
319
|
"test": "vitest run && vitest run --config vite.config.server.mjs && vitest run --config vite.config.hydrate.mjs",
|
|
247
320
|
"test:server": "vitest run --config vite.config.server.mjs",
|
|
@@ -73,10 +73,93 @@ function createJSONDeserializer(options) {
|
|
|
73
73
|
});
|
|
74
74
|
};
|
|
75
75
|
}
|
|
76
|
+
function createJSONSerializer({
|
|
77
|
+
onData,
|
|
78
|
+
onDone,
|
|
79
|
+
onError,
|
|
80
|
+
plugins,
|
|
81
|
+
disabledFeatures,
|
|
82
|
+
depthLimit
|
|
83
|
+
}) {
|
|
84
|
+
const resolved = resolveCodecOptions({
|
|
85
|
+
plugins,
|
|
86
|
+
disabledFeatures,
|
|
87
|
+
depthLimit
|
|
88
|
+
});
|
|
89
|
+
const refs = new Map();
|
|
90
|
+
const cancels = new Set();
|
|
91
|
+
let pendingWrites = 0;
|
|
92
|
+
let flushed = false;
|
|
93
|
+
let done = false;
|
|
94
|
+
const maybeDone = () => {
|
|
95
|
+
if (flushed && pendingWrites === 0 && !done) {
|
|
96
|
+
done = true;
|
|
97
|
+
onDone && onDone();
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
return {
|
|
101
|
+
write(key, value) {
|
|
102
|
+
if (flushed) return;
|
|
103
|
+
pendingWrites++;
|
|
104
|
+
let settled = false;
|
|
105
|
+
let cancel = null;
|
|
106
|
+
const stream = seroval.toCrossJSONStream(value, {
|
|
107
|
+
refs,
|
|
108
|
+
plugins: resolved.plugins,
|
|
109
|
+
disabledFeatures: resolved.disabledFeatures,
|
|
110
|
+
onParse(node, initial) {
|
|
111
|
+
onData({
|
|
112
|
+
key,
|
|
113
|
+
node,
|
|
114
|
+
initial
|
|
115
|
+
});
|
|
116
|
+
},
|
|
117
|
+
onError,
|
|
118
|
+
onDone() {
|
|
119
|
+
settled = true;
|
|
120
|
+
if (cancel) cancels.delete(cancel);
|
|
121
|
+
pendingWrites--;
|
|
122
|
+
maybeDone();
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
if (!settled) {
|
|
126
|
+
cancel = stream;
|
|
127
|
+
cancels.add(cancel);
|
|
128
|
+
}
|
|
129
|
+
},
|
|
130
|
+
flush() {
|
|
131
|
+
flushed = true;
|
|
132
|
+
maybeDone();
|
|
133
|
+
},
|
|
134
|
+
close() {
|
|
135
|
+
flushed = true;
|
|
136
|
+
for (const cancel of cancels) cancel();
|
|
137
|
+
cancels.clear();
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function createJSONDataTable(options) {
|
|
142
|
+
const deserialize = createJSONDeserializer(options);
|
|
143
|
+
const table = new Map();
|
|
144
|
+
return {
|
|
145
|
+
apply(record) {
|
|
146
|
+
const value = deserialize(record.node);
|
|
147
|
+
if (record.initial) table.set(record.key, value);
|
|
148
|
+
},
|
|
149
|
+
get(key) {
|
|
150
|
+
return table.get(key);
|
|
151
|
+
},
|
|
152
|
+
resolve(ref) {
|
|
153
|
+
return table.get(ref.$ref);
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
}
|
|
76
157
|
|
|
77
158
|
exports.DEFAULT_WEB_PLUGINS = DEFAULT_WEB_PLUGINS;
|
|
78
159
|
exports.createHydrationSerializer = createHydrationSerializer;
|
|
160
|
+
exports.createJSONDataTable = createJSONDataTable;
|
|
79
161
|
exports.createJSONDeserializer = createJSONDeserializer;
|
|
162
|
+
exports.createJSONSerializer = createJSONSerializer;
|
|
80
163
|
exports.createSerializer = createSerializer;
|
|
81
164
|
exports.getLocalHeaderScript = getLocalHeaderScript;
|
|
82
165
|
exports.resolveSerializerPlugins = resolveSerializerPlugins;
|
|
@@ -71,5 +71,86 @@ function createJSONDeserializer(options) {
|
|
|
71
71
|
});
|
|
72
72
|
};
|
|
73
73
|
}
|
|
74
|
+
function createJSONSerializer({
|
|
75
|
+
onData,
|
|
76
|
+
onDone,
|
|
77
|
+
onError,
|
|
78
|
+
plugins,
|
|
79
|
+
disabledFeatures,
|
|
80
|
+
depthLimit
|
|
81
|
+
}) {
|
|
82
|
+
const resolved = resolveCodecOptions({
|
|
83
|
+
plugins,
|
|
84
|
+
disabledFeatures,
|
|
85
|
+
depthLimit
|
|
86
|
+
});
|
|
87
|
+
const refs = new Map();
|
|
88
|
+
const cancels = new Set();
|
|
89
|
+
let pendingWrites = 0;
|
|
90
|
+
let flushed = false;
|
|
91
|
+
let done = false;
|
|
92
|
+
const maybeDone = () => {
|
|
93
|
+
if (flushed && pendingWrites === 0 && !done) {
|
|
94
|
+
done = true;
|
|
95
|
+
onDone && onDone();
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
write(key, value) {
|
|
100
|
+
if (flushed) return;
|
|
101
|
+
pendingWrites++;
|
|
102
|
+
let settled = false;
|
|
103
|
+
let cancel = null;
|
|
104
|
+
const stream = toCrossJSONStream(value, {
|
|
105
|
+
refs,
|
|
106
|
+
plugins: resolved.plugins,
|
|
107
|
+
disabledFeatures: resolved.disabledFeatures,
|
|
108
|
+
onParse(node, initial) {
|
|
109
|
+
onData({
|
|
110
|
+
key,
|
|
111
|
+
node,
|
|
112
|
+
initial
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
onError,
|
|
116
|
+
onDone() {
|
|
117
|
+
settled = true;
|
|
118
|
+
if (cancel) cancels.delete(cancel);
|
|
119
|
+
pendingWrites--;
|
|
120
|
+
maybeDone();
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
if (!settled) {
|
|
124
|
+
cancel = stream;
|
|
125
|
+
cancels.add(cancel);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
flush() {
|
|
129
|
+
flushed = true;
|
|
130
|
+
maybeDone();
|
|
131
|
+
},
|
|
132
|
+
close() {
|
|
133
|
+
flushed = true;
|
|
134
|
+
for (const cancel of cancels) cancel();
|
|
135
|
+
cancels.clear();
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function createJSONDataTable(options) {
|
|
140
|
+
const deserialize = createJSONDeserializer(options);
|
|
141
|
+
const table = new Map();
|
|
142
|
+
return {
|
|
143
|
+
apply(record) {
|
|
144
|
+
const value = deserialize(record.node);
|
|
145
|
+
if (record.initial) table.set(record.key, value);
|
|
146
|
+
},
|
|
147
|
+
get(key) {
|
|
148
|
+
return table.get(key);
|
|
149
|
+
},
|
|
150
|
+
resolve(ref) {
|
|
151
|
+
return table.get(ref.$ref);
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
74
155
|
|
|
75
|
-
export { DEFAULT_WEB_PLUGINS, createHydrationSerializer, createJSONDeserializer, createSerializer, getLocalHeaderScript, resolveSerializerPlugins, serializeJSON };
|
|
156
|
+
export { DEFAULT_WEB_PLUGINS, createHydrationSerializer, createJSONDataTable, createJSONDeserializer, createJSONSerializer, createSerializer, getLocalHeaderScript, resolveSerializerPlugins, serializeJSON };
|
|
@@ -137,3 +137,15 @@ export function serializeJSON(value: unknown, options: JSONSerializeOptions): ()
|
|
|
137
137
|
* settles the async values referenced inside it.
|
|
138
138
|
*/
|
|
139
139
|
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* A resident, response-scoped decode table over the keyed JSON codec: apply
|
|
143
|
+
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
|
|
144
|
+
* `resolve`. The frames client host wires one per response
|
|
145
|
+
* (`applyData: c => table.apply(c)`).
|
|
146
|
+
*/
|
|
147
|
+
export interface JSONDataTable {
|
|
148
|
+
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
|
|
149
|
+
resolve<T = unknown>(ref: { $ref: string }): T;
|
|
150
|
+
}
|
|
151
|
+
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;
|
|
@@ -137,3 +137,15 @@ export function serializeJSON(value: unknown, options: JSONSerializeOptions): ()
|
|
|
137
137
|
* settles the async values referenced inside it.
|
|
138
138
|
*/
|
|
139
139
|
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* A resident, response-scoped decode table over the keyed JSON codec: apply
|
|
143
|
+
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
|
|
144
|
+
* `resolve`. The frames client host wires one per response
|
|
145
|
+
* (`applyData: c => table.apply(c)`).
|
|
146
|
+
*/
|
|
147
|
+
export interface JSONDataTable {
|
|
148
|
+
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
|
|
149
|
+
resolve<T = unknown>(ref: { $ref: string }): T;
|
|
150
|
+
}
|
|
151
|
+
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;
|
|
@@ -23,19 +23,6 @@ function resolveCodecOptions({
|
|
|
23
23
|
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
-
function serializeJSON(value, {
|
|
27
|
-
onParse,
|
|
28
|
-
onDone,
|
|
29
|
-
onError,
|
|
30
|
-
...codecOptions
|
|
31
|
-
}) {
|
|
32
|
-
return seroval.toCrossJSONStream(value, {
|
|
33
|
-
onParse,
|
|
34
|
-
onDone,
|
|
35
|
-
onError,
|
|
36
|
-
...resolveCodecOptions(codecOptions)
|
|
37
|
-
});
|
|
38
|
-
}
|
|
39
26
|
function createJSONDeserializer(options) {
|
|
40
27
|
const refs = new Map();
|
|
41
28
|
const resolved = resolveCodecOptions(options);
|
|
@@ -53,9 +40,6 @@ const codecConfig = {
|
|
|
53
40
|
function configureServerFunctionsCodec(codec) {
|
|
54
41
|
codecConfig.codec = codec;
|
|
55
42
|
}
|
|
56
|
-
function getServerFunctionsCodec() {
|
|
57
|
-
return codecConfig.codec;
|
|
58
|
-
}
|
|
59
43
|
const flightConfig = {
|
|
60
44
|
consumer: undefined
|
|
61
45
|
};
|
|
@@ -85,6 +69,31 @@ function withMeta(fn, meta) {
|
|
|
85
69
|
return fn;
|
|
86
70
|
}
|
|
87
71
|
const FUNCTION_HEADER = "X-Server-Function-Id";
|
|
72
|
+
const ERROR_HEADER = "X-Server-Function-Error";
|
|
73
|
+
const ERROR_HEADER_MARKER = "=?1?";
|
|
74
|
+
const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
|
|
75
|
+
function encodeErrorHeaderValue(value) {
|
|
76
|
+
let stripped = String(value).replace(/[\r\n]+/g, "");
|
|
77
|
+
if (!NEEDS_ENCODING.test(stripped) && !stripped.startsWith(ERROR_HEADER_MARKER) && stripped === stripped.trim()) {
|
|
78
|
+
return stripped;
|
|
79
|
+
}
|
|
80
|
+
if (typeof stripped.toWellFormed === "function") {
|
|
81
|
+
stripped = stripped.toWellFormed();
|
|
82
|
+
} else {
|
|
83
|
+
stripped = stripped.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
|
|
84
|
+
}
|
|
85
|
+
return ERROR_HEADER_MARKER + encodeURIComponent(stripped);
|
|
86
|
+
}
|
|
87
|
+
function decodeErrorHeaderValue(value) {
|
|
88
|
+
if (typeof value !== "string" || !value.startsWith(ERROR_HEADER_MARKER)) {
|
|
89
|
+
return value;
|
|
90
|
+
}
|
|
91
|
+
try {
|
|
92
|
+
return decodeURIComponent(value.slice(ERROR_HEADER_MARKER.length));
|
|
93
|
+
} catch {
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
88
97
|
const INSTANCE_HEADER = "X-Server-Function-Instance";
|
|
89
98
|
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
|
|
90
99
|
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
|
|
@@ -97,7 +106,8 @@ const BodyFormat = {
|
|
|
97
106
|
Blob: "4",
|
|
98
107
|
File: "5",
|
|
99
108
|
ArrayBuffer: "6",
|
|
100
|
-
Uint8Array: "7"
|
|
109
|
+
Uint8Array: "7",
|
|
110
|
+
Json: "8"
|
|
101
111
|
};
|
|
102
112
|
function getHeadersAndBody(body) {
|
|
103
113
|
switch (true) {
|
|
@@ -167,6 +177,8 @@ async function extractBody(source, codecOptions) {
|
|
|
167
177
|
switch (true) {
|
|
168
178
|
case format === BodyFormat.Serialized:
|
|
169
179
|
return await deserializeStream(clone, codecOptions);
|
|
180
|
+
case format === BodyFormat.Json:
|
|
181
|
+
return JSON.parse(await clone.text());
|
|
170
182
|
case format === BodyFormat.String:
|
|
171
183
|
return await clone.text();
|
|
172
184
|
case format === BodyFormat.File:
|
|
@@ -189,17 +201,6 @@ async function extractBody(source, codecOptions) {
|
|
|
189
201
|
}
|
|
190
202
|
return undefined;
|
|
191
203
|
}
|
|
192
|
-
function createChunk(data) {
|
|
193
|
-
const encodeData = new TextEncoder().encode(data);
|
|
194
|
-
const bytes = encodeData.length;
|
|
195
|
-
const baseHex = bytes.toString(16);
|
|
196
|
-
const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
|
|
197
|
-
const head = new TextEncoder().encode(`;0x${totalHex};`);
|
|
198
|
-
const chunk = new Uint8Array(12 + bytes);
|
|
199
|
-
chunk.set(head);
|
|
200
|
-
chunk.set(encodeData, 12);
|
|
201
|
-
return chunk;
|
|
202
|
-
}
|
|
203
204
|
class ChunkReader {
|
|
204
205
|
constructor(stream) {
|
|
205
206
|
this.reader = stream.getReader();
|
|
@@ -256,28 +257,6 @@ class ChunkReader {
|
|
|
256
257
|
}
|
|
257
258
|
}
|
|
258
259
|
}
|
|
259
|
-
function serializeStream(value, codecOptions) {
|
|
260
|
-
return new ReadableStream({
|
|
261
|
-
start(controller) {
|
|
262
|
-
serializeJSON(value, {
|
|
263
|
-
...codecOptions,
|
|
264
|
-
onParse(node) {
|
|
265
|
-
controller.enqueue(createChunk(JSON.stringify(node)));
|
|
266
|
-
},
|
|
267
|
-
onDone() {
|
|
268
|
-
controller.close();
|
|
269
|
-
},
|
|
270
|
-
onError(error) {
|
|
271
|
-
controller.error(error);
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
async function serializeString(value, codecOptions) {
|
|
278
|
-
const response = new Response(serializeStream(value, codecOptions));
|
|
279
|
-
return await response.text();
|
|
280
|
-
}
|
|
281
260
|
async function deserializeStream(source, codecOptions) {
|
|
282
261
|
if (!source.body) {
|
|
283
262
|
throw new Error("missing body");
|
|
@@ -301,16 +280,43 @@ async function decodeResponse(response, codecOptions) {
|
|
|
301
280
|
|
|
302
281
|
const config = {
|
|
303
282
|
endpoint: "/_server",
|
|
304
|
-
prepareRequest: undefined
|
|
283
|
+
prepareRequest: undefined,
|
|
284
|
+
responseHandler: undefined,
|
|
285
|
+
serializeArgs: undefined
|
|
305
286
|
};
|
|
287
|
+
function isJSONSafe(value) {
|
|
288
|
+
if (value === null) return true;
|
|
289
|
+
const t = typeof value;
|
|
290
|
+
if (t === "string" || t === "boolean") return true;
|
|
291
|
+
if (t === "number") return Number.isFinite(value);
|
|
292
|
+
if (t !== "object") return false;
|
|
293
|
+
if (Array.isArray(value)) {
|
|
294
|
+
for (const v of value) if (!isJSONSafe(v)) return false;
|
|
295
|
+
return true;
|
|
296
|
+
}
|
|
297
|
+
const proto = Object.getPrototypeOf(value);
|
|
298
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
299
|
+
for (const k in value) if (!isJSONSafe(value[k])) return false;
|
|
300
|
+
return true;
|
|
301
|
+
}
|
|
302
|
+
function serializeArguments(args) {
|
|
303
|
+
if (!config.serializeArgs) {
|
|
304
|
+
throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " + "(from the server-functions rich-args entry) once at startup to " + "send Dates, Maps, Sets, typed arrays, etc. through the codec — or " + "pass a single Blob/FormData/File argument, which has a native " + "HTTP encoding.");
|
|
305
|
+
}
|
|
306
|
+
return config.serializeArgs(args);
|
|
307
|
+
}
|
|
306
308
|
function configureServerFunctionsClient({
|
|
307
309
|
endpoint,
|
|
308
310
|
codec,
|
|
309
|
-
prepareRequest
|
|
311
|
+
prepareRequest,
|
|
312
|
+
responseHandler,
|
|
313
|
+
serializeArgs
|
|
310
314
|
} = {}) {
|
|
311
315
|
if (endpoint !== undefined) config.endpoint = endpoint;
|
|
312
316
|
if (codec !== undefined) configureServerFunctionsCodec(codec);
|
|
313
317
|
if (prepareRequest !== undefined) config.prepareRequest = prepareRequest;
|
|
318
|
+
if (responseHandler !== undefined) config.responseHandler = responseHandler;
|
|
319
|
+
if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
|
|
314
320
|
}
|
|
315
321
|
let INSTANCE = 0;
|
|
316
322
|
async function createRequest(base, id, instance, options, meta) {
|
|
@@ -352,9 +358,20 @@ async function initializeResponse(base, id, instance, options, args, meta) {
|
|
|
352
358
|
}, meta);
|
|
353
359
|
}
|
|
354
360
|
}
|
|
361
|
+
if (isJSONSafe(args)) {
|
|
362
|
+
return createRequest(base, id, instance, {
|
|
363
|
+
...options,
|
|
364
|
+
body: JSON.stringify(args),
|
|
365
|
+
headers: {
|
|
366
|
+
...options.headers,
|
|
367
|
+
"Content-Type": "application/json",
|
|
368
|
+
[BODY_FORMAT_HEADER]: BodyFormat.Json
|
|
369
|
+
}
|
|
370
|
+
}, meta);
|
|
371
|
+
}
|
|
355
372
|
return createRequest(base, id, instance, {
|
|
356
373
|
...options,
|
|
357
|
-
body: await
|
|
374
|
+
body: await serializeArguments(args),
|
|
358
375
|
headers: {
|
|
359
376
|
...options.headers,
|
|
360
377
|
"Content-Type": "text/plain",
|
|
@@ -364,7 +381,21 @@ async function initializeResponse(base, id, instance, options, args, meta) {
|
|
|
364
381
|
}
|
|
365
382
|
async function fetchServerFunction(base, id, options, args, meta) {
|
|
366
383
|
const instance = `server-function:${INSTANCE++}`;
|
|
384
|
+
const handler = config.responseHandler;
|
|
385
|
+
const context = handler && handler.capture ? handler.capture({
|
|
386
|
+
id,
|
|
387
|
+
meta
|
|
388
|
+
}) : undefined;
|
|
367
389
|
const response = await initializeResponse(base, id, instance, options, args, meta);
|
|
390
|
+
if (handler) {
|
|
391
|
+
const handled = handler.handle(response, {
|
|
392
|
+
id,
|
|
393
|
+
meta,
|
|
394
|
+
args,
|
|
395
|
+
context
|
|
396
|
+
});
|
|
397
|
+
if (handled !== undefined) return handled;
|
|
398
|
+
}
|
|
368
399
|
if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
|
|
369
400
|
const consumer = getFlightDataConsumer();
|
|
370
401
|
if (consumer) {
|
|
@@ -372,7 +403,7 @@ async function fetchServerFunction(base, id, options, args, meta) {
|
|
|
372
403
|
await consumer(payload.data, {
|
|
373
404
|
response
|
|
374
405
|
});
|
|
375
|
-
if (response.headers.has(
|
|
406
|
+
if (response.headers.has(ERROR_HEADER) && !response.headers.has("Location") && !response.headers.has("X-Revalidate")) {
|
|
376
407
|
throw payload.value;
|
|
377
408
|
}
|
|
378
409
|
return payload.value;
|
|
@@ -382,22 +413,33 @@ async function fetchServerFunction(base, id, options, args, meta) {
|
|
|
382
413
|
return response;
|
|
383
414
|
}
|
|
384
415
|
const result = await decodeResponse(response.clone());
|
|
385
|
-
if (response.headers.has(
|
|
416
|
+
if (response.headers.has(ERROR_HEADER)) {
|
|
386
417
|
throw result;
|
|
387
418
|
}
|
|
388
419
|
return result;
|
|
389
420
|
}
|
|
390
|
-
function createServerReference(id, name) {
|
|
421
|
+
function createServerReference(id, name, base) {
|
|
391
422
|
const metadata = name === undefined ? {} : {
|
|
392
423
|
name
|
|
393
424
|
};
|
|
394
|
-
const fn = (...args) =>
|
|
425
|
+
const fn = (...args) => {
|
|
426
|
+
const handler = config.responseHandler;
|
|
427
|
+
if (handler && handler.intercept) {
|
|
428
|
+
const hit = handler.intercept({
|
|
429
|
+
id,
|
|
430
|
+
meta: metadata,
|
|
431
|
+
args
|
|
432
|
+
});
|
|
433
|
+
if (hit !== undefined) return hit;
|
|
434
|
+
}
|
|
435
|
+
return fetchServerFunction(base || config.endpoint, id, {}, args, metadata);
|
|
436
|
+
};
|
|
395
437
|
fn[SERVER_FUNCTION_METADATA] = metadata;
|
|
396
438
|
return new Proxy(fn, {
|
|
397
439
|
get(target, prop) {
|
|
398
440
|
if (prop === "id") return id;
|
|
399
441
|
if (prop === "url") {
|
|
400
|
-
return `${config.endpoint}?id=${encodeURIComponent(id)}`;
|
|
442
|
+
return base || `${config.endpoint}?id=${encodeURIComponent(id)}`;
|
|
401
443
|
}
|
|
402
444
|
return target[prop];
|
|
403
445
|
}
|
|
@@ -412,9 +454,19 @@ function GET(fn) {
|
|
|
412
454
|
...getServerFunctionMetadata(fn)
|
|
413
455
|
};
|
|
414
456
|
const wrapped = async (...args) => {
|
|
457
|
+
const handler = config.responseHandler;
|
|
458
|
+
if (handler && handler.intercept) {
|
|
459
|
+
const hit = handler.intercept({
|
|
460
|
+
id,
|
|
461
|
+
meta: metadata,
|
|
462
|
+
args
|
|
463
|
+
});
|
|
464
|
+
if (hit !== undefined) return hit;
|
|
465
|
+
}
|
|
415
466
|
let base = `${config.endpoint}?id=${encodeURIComponent(id)}`;
|
|
416
467
|
if (args.length) {
|
|
417
|
-
|
|
468
|
+
const encoded = isJSONSafe(args) ? JSON.stringify(args) : await serializeArguments(args);
|
|
469
|
+
base += `&args=${encodeURIComponent(encoded)}`;
|
|
418
470
|
}
|
|
419
471
|
return fetchServerFunction(base, id, {
|
|
420
472
|
method: "GET"
|
|
@@ -434,13 +486,16 @@ function registerServerReference() {
|
|
|
434
486
|
throw new Error("registerServerReference must not be called in the client build");
|
|
435
487
|
}
|
|
436
488
|
|
|
489
|
+
exports.ERROR_HEADER = ERROR_HEADER;
|
|
437
490
|
exports.FUNCTION_HEADER = FUNCTION_HEADER;
|
|
438
491
|
exports.GET = GET;
|
|
439
492
|
exports.INSTANCE_HEADER = INSTANCE_HEADER;
|
|
440
493
|
exports.SINGLE_FLIGHT_HEADER = SINGLE_FLIGHT_HEADER;
|
|
441
494
|
exports.configureServerFunctionsClient = configureServerFunctionsClient;
|
|
442
495
|
exports.createServerReference = createServerReference;
|
|
496
|
+
exports.decodeErrorHeaderValue = decodeErrorHeaderValue;
|
|
443
497
|
exports.decodeResponse = decodeResponse;
|
|
498
|
+
exports.encodeErrorHeaderValue = encodeErrorHeaderValue;
|
|
444
499
|
exports.getServerFunctionMetadata = getServerFunctionMetadata;
|
|
445
500
|
exports.isServerFunction = isServerFunction;
|
|
446
501
|
exports.registerServerReference = registerServerReference;
|