@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fromCrossJSON, Feature
|
|
1
|
+
import { fromCrossJSON, Feature } from 'seroval';
|
|
2
2
|
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
|
|
3
3
|
|
|
4
4
|
Feature.AggregateError | Feature.BigIntTypedArray;
|
|
@@ -21,19 +21,6 @@ function resolveCodecOptions({
|
|
|
21
21
|
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
|
|
22
22
|
};
|
|
23
23
|
}
|
|
24
|
-
function serializeJSON(value, {
|
|
25
|
-
onParse,
|
|
26
|
-
onDone,
|
|
27
|
-
onError,
|
|
28
|
-
...codecOptions
|
|
29
|
-
}) {
|
|
30
|
-
return toCrossJSONStream(value, {
|
|
31
|
-
onParse,
|
|
32
|
-
onDone,
|
|
33
|
-
onError,
|
|
34
|
-
...resolveCodecOptions(codecOptions)
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
24
|
function createJSONDeserializer(options) {
|
|
38
25
|
const refs = new Map();
|
|
39
26
|
const resolved = resolveCodecOptions(options);
|
|
@@ -51,9 +38,6 @@ const codecConfig = {
|
|
|
51
38
|
function configureServerFunctionsCodec(codec) {
|
|
52
39
|
codecConfig.codec = codec;
|
|
53
40
|
}
|
|
54
|
-
function getServerFunctionsCodec() {
|
|
55
|
-
return codecConfig.codec;
|
|
56
|
-
}
|
|
57
41
|
const flightConfig = {
|
|
58
42
|
consumer: undefined
|
|
59
43
|
};
|
|
@@ -83,6 +67,31 @@ function withMeta(fn, meta) {
|
|
|
83
67
|
return fn;
|
|
84
68
|
}
|
|
85
69
|
const FUNCTION_HEADER = "X-Server-Function-Id";
|
|
70
|
+
const ERROR_HEADER = "X-Server-Function-Error";
|
|
71
|
+
const ERROR_HEADER_MARKER = "=?1?";
|
|
72
|
+
const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
|
|
73
|
+
function encodeErrorHeaderValue(value) {
|
|
74
|
+
let stripped = String(value).replace(/[\r\n]+/g, "");
|
|
75
|
+
if (!NEEDS_ENCODING.test(stripped) && !stripped.startsWith(ERROR_HEADER_MARKER) && stripped === stripped.trim()) {
|
|
76
|
+
return stripped;
|
|
77
|
+
}
|
|
78
|
+
if (typeof stripped.toWellFormed === "function") {
|
|
79
|
+
stripped = stripped.toWellFormed();
|
|
80
|
+
} else {
|
|
81
|
+
stripped = stripped.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
|
|
82
|
+
}
|
|
83
|
+
return ERROR_HEADER_MARKER + encodeURIComponent(stripped);
|
|
84
|
+
}
|
|
85
|
+
function decodeErrorHeaderValue(value) {
|
|
86
|
+
if (typeof value !== "string" || !value.startsWith(ERROR_HEADER_MARKER)) {
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
try {
|
|
90
|
+
return decodeURIComponent(value.slice(ERROR_HEADER_MARKER.length));
|
|
91
|
+
} catch {
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
86
95
|
const INSTANCE_HEADER = "X-Server-Function-Instance";
|
|
87
96
|
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
|
|
88
97
|
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
|
|
@@ -95,7 +104,8 @@ const BodyFormat = {
|
|
|
95
104
|
Blob: "4",
|
|
96
105
|
File: "5",
|
|
97
106
|
ArrayBuffer: "6",
|
|
98
|
-
Uint8Array: "7"
|
|
107
|
+
Uint8Array: "7",
|
|
108
|
+
Json: "8"
|
|
99
109
|
};
|
|
100
110
|
function getHeadersAndBody(body) {
|
|
101
111
|
switch (true) {
|
|
@@ -165,6 +175,8 @@ async function extractBody(source, codecOptions) {
|
|
|
165
175
|
switch (true) {
|
|
166
176
|
case format === BodyFormat.Serialized:
|
|
167
177
|
return await deserializeStream(clone, codecOptions);
|
|
178
|
+
case format === BodyFormat.Json:
|
|
179
|
+
return JSON.parse(await clone.text());
|
|
168
180
|
case format === BodyFormat.String:
|
|
169
181
|
return await clone.text();
|
|
170
182
|
case format === BodyFormat.File:
|
|
@@ -187,17 +199,6 @@ async function extractBody(source, codecOptions) {
|
|
|
187
199
|
}
|
|
188
200
|
return undefined;
|
|
189
201
|
}
|
|
190
|
-
function createChunk(data) {
|
|
191
|
-
const encodeData = new TextEncoder().encode(data);
|
|
192
|
-
const bytes = encodeData.length;
|
|
193
|
-
const baseHex = bytes.toString(16);
|
|
194
|
-
const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
|
|
195
|
-
const head = new TextEncoder().encode(`;0x${totalHex};`);
|
|
196
|
-
const chunk = new Uint8Array(12 + bytes);
|
|
197
|
-
chunk.set(head);
|
|
198
|
-
chunk.set(encodeData, 12);
|
|
199
|
-
return chunk;
|
|
200
|
-
}
|
|
201
202
|
class ChunkReader {
|
|
202
203
|
constructor(stream) {
|
|
203
204
|
this.reader = stream.getReader();
|
|
@@ -254,28 +255,6 @@ class ChunkReader {
|
|
|
254
255
|
}
|
|
255
256
|
}
|
|
256
257
|
}
|
|
257
|
-
function serializeStream(value, codecOptions) {
|
|
258
|
-
return new ReadableStream({
|
|
259
|
-
start(controller) {
|
|
260
|
-
serializeJSON(value, {
|
|
261
|
-
...codecOptions,
|
|
262
|
-
onParse(node) {
|
|
263
|
-
controller.enqueue(createChunk(JSON.stringify(node)));
|
|
264
|
-
},
|
|
265
|
-
onDone() {
|
|
266
|
-
controller.close();
|
|
267
|
-
},
|
|
268
|
-
onError(error) {
|
|
269
|
-
controller.error(error);
|
|
270
|
-
}
|
|
271
|
-
});
|
|
272
|
-
}
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
async function serializeString(value, codecOptions) {
|
|
276
|
-
const response = new Response(serializeStream(value, codecOptions));
|
|
277
|
-
return await response.text();
|
|
278
|
-
}
|
|
279
258
|
async function deserializeStream(source, codecOptions) {
|
|
280
259
|
if (!source.body) {
|
|
281
260
|
throw new Error("missing body");
|
|
@@ -299,16 +278,43 @@ async function decodeResponse(response, codecOptions) {
|
|
|
299
278
|
|
|
300
279
|
const config = {
|
|
301
280
|
endpoint: "/_server",
|
|
302
|
-
prepareRequest: undefined
|
|
281
|
+
prepareRequest: undefined,
|
|
282
|
+
responseHandler: undefined,
|
|
283
|
+
serializeArgs: undefined
|
|
303
284
|
};
|
|
285
|
+
function isJSONSafe(value) {
|
|
286
|
+
if (value === null) return true;
|
|
287
|
+
const t = typeof value;
|
|
288
|
+
if (t === "string" || t === "boolean") return true;
|
|
289
|
+
if (t === "number") return Number.isFinite(value);
|
|
290
|
+
if (t !== "object") return false;
|
|
291
|
+
if (Array.isArray(value)) {
|
|
292
|
+
for (const v of value) if (!isJSONSafe(v)) return false;
|
|
293
|
+
return true;
|
|
294
|
+
}
|
|
295
|
+
const proto = Object.getPrototypeOf(value);
|
|
296
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
297
|
+
for (const k in value) if (!isJSONSafe(value[k])) return false;
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
function serializeArguments(args) {
|
|
301
|
+
if (!config.serializeArgs) {
|
|
302
|
+
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.");
|
|
303
|
+
}
|
|
304
|
+
return config.serializeArgs(args);
|
|
305
|
+
}
|
|
304
306
|
function configureServerFunctionsClient({
|
|
305
307
|
endpoint,
|
|
306
308
|
codec,
|
|
307
|
-
prepareRequest
|
|
309
|
+
prepareRequest,
|
|
310
|
+
responseHandler,
|
|
311
|
+
serializeArgs
|
|
308
312
|
} = {}) {
|
|
309
313
|
if (endpoint !== undefined) config.endpoint = endpoint;
|
|
310
314
|
if (codec !== undefined) configureServerFunctionsCodec(codec);
|
|
311
315
|
if (prepareRequest !== undefined) config.prepareRequest = prepareRequest;
|
|
316
|
+
if (responseHandler !== undefined) config.responseHandler = responseHandler;
|
|
317
|
+
if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
|
|
312
318
|
}
|
|
313
319
|
let INSTANCE = 0;
|
|
314
320
|
async function createRequest(base, id, instance, options, meta) {
|
|
@@ -350,9 +356,20 @@ async function initializeResponse(base, id, instance, options, args, meta) {
|
|
|
350
356
|
}, meta);
|
|
351
357
|
}
|
|
352
358
|
}
|
|
359
|
+
if (isJSONSafe(args)) {
|
|
360
|
+
return createRequest(base, id, instance, {
|
|
361
|
+
...options,
|
|
362
|
+
body: JSON.stringify(args),
|
|
363
|
+
headers: {
|
|
364
|
+
...options.headers,
|
|
365
|
+
"Content-Type": "application/json",
|
|
366
|
+
[BODY_FORMAT_HEADER]: BodyFormat.Json
|
|
367
|
+
}
|
|
368
|
+
}, meta);
|
|
369
|
+
}
|
|
353
370
|
return createRequest(base, id, instance, {
|
|
354
371
|
...options,
|
|
355
|
-
body: await
|
|
372
|
+
body: await serializeArguments(args),
|
|
356
373
|
headers: {
|
|
357
374
|
...options.headers,
|
|
358
375
|
"Content-Type": "text/plain",
|
|
@@ -362,7 +379,21 @@ async function initializeResponse(base, id, instance, options, args, meta) {
|
|
|
362
379
|
}
|
|
363
380
|
async function fetchServerFunction(base, id, options, args, meta) {
|
|
364
381
|
const instance = `server-function:${INSTANCE++}`;
|
|
382
|
+
const handler = config.responseHandler;
|
|
383
|
+
const context = handler && handler.capture ? handler.capture({
|
|
384
|
+
id,
|
|
385
|
+
meta
|
|
386
|
+
}) : undefined;
|
|
365
387
|
const response = await initializeResponse(base, id, instance, options, args, meta);
|
|
388
|
+
if (handler) {
|
|
389
|
+
const handled = handler.handle(response, {
|
|
390
|
+
id,
|
|
391
|
+
meta,
|
|
392
|
+
args,
|
|
393
|
+
context
|
|
394
|
+
});
|
|
395
|
+
if (handled !== undefined) return handled;
|
|
396
|
+
}
|
|
366
397
|
if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
|
|
367
398
|
const consumer = getFlightDataConsumer();
|
|
368
399
|
if (consumer) {
|
|
@@ -370,7 +401,7 @@ async function fetchServerFunction(base, id, options, args, meta) {
|
|
|
370
401
|
await consumer(payload.data, {
|
|
371
402
|
response
|
|
372
403
|
});
|
|
373
|
-
if (response.headers.has(
|
|
404
|
+
if (response.headers.has(ERROR_HEADER) && !response.headers.has("Location") && !response.headers.has("X-Revalidate")) {
|
|
374
405
|
throw payload.value;
|
|
375
406
|
}
|
|
376
407
|
return payload.value;
|
|
@@ -380,22 +411,33 @@ async function fetchServerFunction(base, id, options, args, meta) {
|
|
|
380
411
|
return response;
|
|
381
412
|
}
|
|
382
413
|
const result = await decodeResponse(response.clone());
|
|
383
|
-
if (response.headers.has(
|
|
414
|
+
if (response.headers.has(ERROR_HEADER)) {
|
|
384
415
|
throw result;
|
|
385
416
|
}
|
|
386
417
|
return result;
|
|
387
418
|
}
|
|
388
|
-
function createServerReference(id, name) {
|
|
419
|
+
function createServerReference(id, name, base) {
|
|
389
420
|
const metadata = name === undefined ? {} : {
|
|
390
421
|
name
|
|
391
422
|
};
|
|
392
|
-
const fn = (...args) =>
|
|
423
|
+
const fn = (...args) => {
|
|
424
|
+
const handler = config.responseHandler;
|
|
425
|
+
if (handler && handler.intercept) {
|
|
426
|
+
const hit = handler.intercept({
|
|
427
|
+
id,
|
|
428
|
+
meta: metadata,
|
|
429
|
+
args
|
|
430
|
+
});
|
|
431
|
+
if (hit !== undefined) return hit;
|
|
432
|
+
}
|
|
433
|
+
return fetchServerFunction(base || config.endpoint, id, {}, args, metadata);
|
|
434
|
+
};
|
|
393
435
|
fn[SERVER_FUNCTION_METADATA] = metadata;
|
|
394
436
|
return new Proxy(fn, {
|
|
395
437
|
get(target, prop) {
|
|
396
438
|
if (prop === "id") return id;
|
|
397
439
|
if (prop === "url") {
|
|
398
|
-
return `${config.endpoint}?id=${encodeURIComponent(id)}`;
|
|
440
|
+
return base || `${config.endpoint}?id=${encodeURIComponent(id)}`;
|
|
399
441
|
}
|
|
400
442
|
return target[prop];
|
|
401
443
|
}
|
|
@@ -410,9 +452,19 @@ function GET(fn) {
|
|
|
410
452
|
...getServerFunctionMetadata(fn)
|
|
411
453
|
};
|
|
412
454
|
const wrapped = async (...args) => {
|
|
455
|
+
const handler = config.responseHandler;
|
|
456
|
+
if (handler && handler.intercept) {
|
|
457
|
+
const hit = handler.intercept({
|
|
458
|
+
id,
|
|
459
|
+
meta: metadata,
|
|
460
|
+
args
|
|
461
|
+
});
|
|
462
|
+
if (hit !== undefined) return hit;
|
|
463
|
+
}
|
|
413
464
|
let base = `${config.endpoint}?id=${encodeURIComponent(id)}`;
|
|
414
465
|
if (args.length) {
|
|
415
|
-
|
|
466
|
+
const encoded = isJSONSafe(args) ? JSON.stringify(args) : await serializeArguments(args);
|
|
467
|
+
base += `&args=${encodeURIComponent(encoded)}`;
|
|
416
468
|
}
|
|
417
469
|
return fetchServerFunction(base, id, {
|
|
418
470
|
method: "GET"
|
|
@@ -432,4 +484,4 @@ function registerServerReference() {
|
|
|
432
484
|
throw new Error("registerServerReference must not be called in the client build");
|
|
433
485
|
}
|
|
434
486
|
|
|
435
|
-
export { FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, configureServerFunctionsClient, createServerReference, decodeResponse, getServerFunctionMetadata, isServerFunction, registerServerReference, subscribeFlightData, withMeta };
|
|
487
|
+
export { ERROR_HEADER, FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, configureServerFunctionsClient, createServerReference, decodeErrorHeaderValue, decodeResponse, encodeErrorHeaderValue, getServerFunctionMetadata, isServerFunction, registerServerReference, subscribeFlightData, withMeta };
|
|
@@ -88,6 +88,31 @@ function withMeta(fn, meta) {
|
|
|
88
88
|
return fn;
|
|
89
89
|
}
|
|
90
90
|
const FUNCTION_HEADER = "X-Server-Function-Id";
|
|
91
|
+
const ERROR_HEADER = "X-Server-Function-Error";
|
|
92
|
+
const ERROR_HEADER_MARKER = "=?1?";
|
|
93
|
+
const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
|
|
94
|
+
function encodeErrorHeaderValue(value) {
|
|
95
|
+
let stripped = String(value).replace(/[\r\n]+/g, "");
|
|
96
|
+
if (!NEEDS_ENCODING.test(stripped) && !stripped.startsWith(ERROR_HEADER_MARKER) && stripped === stripped.trim()) {
|
|
97
|
+
return stripped;
|
|
98
|
+
}
|
|
99
|
+
if (typeof stripped.toWellFormed === "function") {
|
|
100
|
+
stripped = stripped.toWellFormed();
|
|
101
|
+
} else {
|
|
102
|
+
stripped = stripped.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
|
|
103
|
+
}
|
|
104
|
+
return ERROR_HEADER_MARKER + encodeURIComponent(stripped);
|
|
105
|
+
}
|
|
106
|
+
function decodeErrorHeaderValue(value) {
|
|
107
|
+
if (typeof value !== "string" || !value.startsWith(ERROR_HEADER_MARKER)) {
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
try {
|
|
111
|
+
return decodeURIComponent(value.slice(ERROR_HEADER_MARKER.length));
|
|
112
|
+
} catch {
|
|
113
|
+
return value;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
91
116
|
const INSTANCE_HEADER = "X-Server-Function-Instance";
|
|
92
117
|
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
|
|
93
118
|
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
|
|
@@ -100,7 +125,8 @@ const BodyFormat = {
|
|
|
100
125
|
Blob: "4",
|
|
101
126
|
File: "5",
|
|
102
127
|
ArrayBuffer: "6",
|
|
103
|
-
Uint8Array: "7"
|
|
128
|
+
Uint8Array: "7",
|
|
129
|
+
Json: "8"
|
|
104
130
|
};
|
|
105
131
|
function getHeadersAndBody(body) {
|
|
106
132
|
switch (true) {
|
|
@@ -170,6 +196,8 @@ async function extractBody(source, codecOptions) {
|
|
|
170
196
|
switch (true) {
|
|
171
197
|
case format === BodyFormat.Serialized:
|
|
172
198
|
return await deserializeStream(clone, codecOptions);
|
|
199
|
+
case format === BodyFormat.Json:
|
|
200
|
+
return JSON.parse(await clone.text());
|
|
173
201
|
case format === BodyFormat.String:
|
|
174
202
|
return await clone.text();
|
|
175
203
|
case format === BodyFormat.File:
|
|
@@ -304,16 +332,19 @@ async function decodeResponse(response, codecOptions) {
|
|
|
304
332
|
const config = {
|
|
305
333
|
provideEvent: undefined,
|
|
306
334
|
collectFlightData: undefined,
|
|
335
|
+
transformDirectResult: undefined,
|
|
307
336
|
endpoint: "/_server"
|
|
308
337
|
};
|
|
309
338
|
function configureServerFunctionsServer({
|
|
310
339
|
provideEvent,
|
|
311
340
|
collectFlightData,
|
|
341
|
+
transformDirectResult,
|
|
312
342
|
endpoint,
|
|
313
343
|
codec
|
|
314
344
|
} = {}) {
|
|
315
345
|
if (provideEvent !== undefined) config.provideEvent = provideEvent;
|
|
316
346
|
if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
|
|
347
|
+
if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
|
|
317
348
|
if (endpoint !== undefined) config.endpoint = endpoint;
|
|
318
349
|
if (codec !== undefined) configureServerFunctionsCodec(codec);
|
|
319
350
|
}
|
|
@@ -372,9 +403,20 @@ function createServerReference({
|
|
|
372
403
|
id
|
|
373
404
|
};
|
|
374
405
|
evt.serverOnly = true;
|
|
375
|
-
|
|
406
|
+
const result = provideEvent(evt, () => {
|
|
376
407
|
return fn.apply(thisArg, args);
|
|
377
408
|
});
|
|
409
|
+
const transform = config.transformDirectResult;
|
|
410
|
+
if (transform && result && typeof result.then === "function") {
|
|
411
|
+
return result.then(value => transform(value, {
|
|
412
|
+
id,
|
|
413
|
+
event: evt
|
|
414
|
+
}));
|
|
415
|
+
}
|
|
416
|
+
return transform ? transform(result, {
|
|
417
|
+
id,
|
|
418
|
+
event: evt
|
|
419
|
+
}) : result;
|
|
378
420
|
}
|
|
379
421
|
});
|
|
380
422
|
}
|
|
@@ -400,7 +442,8 @@ function resolveFunctionId(request, url) {
|
|
|
400
442
|
}
|
|
401
443
|
async function parseArguments(request, url, instance, codec) {
|
|
402
444
|
const parsed = [];
|
|
403
|
-
|
|
445
|
+
const bodyFormat = request.method === "POST" ? request.headers.get(BODY_FORMAT_HEADER) : null;
|
|
446
|
+
if (!instance || request.method === "GET" || bodyFormat !== BodyFormat.Serialized) {
|
|
404
447
|
const args = url.searchParams.get("args");
|
|
405
448
|
if (args) {
|
|
406
449
|
const result = args.startsWith(";0x") ? await deserializeString(args, codec) : JSON.parse(args);
|
|
@@ -410,9 +453,8 @@ async function parseArguments(request, url, instance, codec) {
|
|
|
410
453
|
}
|
|
411
454
|
}
|
|
412
455
|
if (request.method === "POST" && request.body !== null) {
|
|
413
|
-
const format = request.headers.get(BODY_FORMAT_HEADER);
|
|
414
456
|
const decoded = await extractBody(request.clone(), codec);
|
|
415
|
-
if (
|
|
457
|
+
if (bodyFormat === BodyFormat.Serialized || bodyFormat === BodyFormat.Json) {
|
|
416
458
|
return decoded;
|
|
417
459
|
}
|
|
418
460
|
parsed.push(decoded);
|
|
@@ -470,12 +512,11 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
470
512
|
status: 404
|
|
471
513
|
});
|
|
472
514
|
}
|
|
473
|
-
|
|
474
|
-
if (request.method === "GET" !== (allowedMethod === "GET")) {
|
|
515
|
+
if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
|
|
475
516
|
return new Response(process.env.NODE_ENV === "development" ? `Method not allowed for server function: ${functionId}` : null, {
|
|
476
517
|
status: 405,
|
|
477
518
|
headers: {
|
|
478
|
-
Allow:
|
|
519
|
+
Allow: "POST"
|
|
479
520
|
}
|
|
480
521
|
});
|
|
481
522
|
}
|
|
@@ -594,7 +635,7 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
594
635
|
thrown: true
|
|
595
636
|
});
|
|
596
637
|
}
|
|
597
|
-
headers.set(
|
|
638
|
+
headers.set(ERROR_HEADER, "true");
|
|
598
639
|
if (!instance) {
|
|
599
640
|
if (options.handleNoJS) return options.handleNoJS(x, request, parsed, true);
|
|
600
641
|
if (x instanceof Response) return x;
|
|
@@ -609,18 +650,21 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
609
650
|
});
|
|
610
651
|
}
|
|
611
652
|
const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
|
|
612
|
-
headers.set(
|
|
653
|
+
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
|
|
613
654
|
return encodeResult(x, headers, 200, codec);
|
|
614
655
|
}
|
|
615
656
|
}
|
|
616
657
|
|
|
658
|
+
exports.ERROR_HEADER = ERROR_HEADER;
|
|
617
659
|
exports.FUNCTION_HEADER = FUNCTION_HEADER;
|
|
618
660
|
exports.GET = GET;
|
|
619
661
|
exports.INSTANCE_HEADER = INSTANCE_HEADER;
|
|
620
662
|
exports.SINGLE_FLIGHT_HEADER = SINGLE_FLIGHT_HEADER;
|
|
621
663
|
exports.configureServerFunctionsServer = configureServerFunctionsServer;
|
|
622
664
|
exports.createServerReference = createServerReference;
|
|
665
|
+
exports.decodeErrorHeaderValue = decodeErrorHeaderValue;
|
|
623
666
|
exports.decodeResponse = decodeResponse;
|
|
667
|
+
exports.encodeErrorHeaderValue = encodeErrorHeaderValue;
|
|
624
668
|
exports.getServerFunction = getServerFunction;
|
|
625
669
|
exports.getServerFunctionMeta = getServerFunctionMeta;
|
|
626
670
|
exports.getServerFunctionMetadata = getServerFunctionMetadata;
|
|
@@ -86,6 +86,31 @@ function withMeta(fn, meta) {
|
|
|
86
86
|
return fn;
|
|
87
87
|
}
|
|
88
88
|
const FUNCTION_HEADER = "X-Server-Function-Id";
|
|
89
|
+
const ERROR_HEADER = "X-Server-Function-Error";
|
|
90
|
+
const ERROR_HEADER_MARKER = "=?1?";
|
|
91
|
+
const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
|
|
92
|
+
function encodeErrorHeaderValue(value) {
|
|
93
|
+
let stripped = String(value).replace(/[\r\n]+/g, "");
|
|
94
|
+
if (!NEEDS_ENCODING.test(stripped) && !stripped.startsWith(ERROR_HEADER_MARKER) && stripped === stripped.trim()) {
|
|
95
|
+
return stripped;
|
|
96
|
+
}
|
|
97
|
+
if (typeof stripped.toWellFormed === "function") {
|
|
98
|
+
stripped = stripped.toWellFormed();
|
|
99
|
+
} else {
|
|
100
|
+
stripped = stripped.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
|
|
101
|
+
}
|
|
102
|
+
return ERROR_HEADER_MARKER + encodeURIComponent(stripped);
|
|
103
|
+
}
|
|
104
|
+
function decodeErrorHeaderValue(value) {
|
|
105
|
+
if (typeof value !== "string" || !value.startsWith(ERROR_HEADER_MARKER)) {
|
|
106
|
+
return value;
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
return decodeURIComponent(value.slice(ERROR_HEADER_MARKER.length));
|
|
110
|
+
} catch {
|
|
111
|
+
return value;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
89
114
|
const INSTANCE_HEADER = "X-Server-Function-Instance";
|
|
90
115
|
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
|
|
91
116
|
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
|
|
@@ -98,7 +123,8 @@ const BodyFormat = {
|
|
|
98
123
|
Blob: "4",
|
|
99
124
|
File: "5",
|
|
100
125
|
ArrayBuffer: "6",
|
|
101
|
-
Uint8Array: "7"
|
|
126
|
+
Uint8Array: "7",
|
|
127
|
+
Json: "8"
|
|
102
128
|
};
|
|
103
129
|
function getHeadersAndBody(body) {
|
|
104
130
|
switch (true) {
|
|
@@ -168,6 +194,8 @@ async function extractBody(source, codecOptions) {
|
|
|
168
194
|
switch (true) {
|
|
169
195
|
case format === BodyFormat.Serialized:
|
|
170
196
|
return await deserializeStream(clone, codecOptions);
|
|
197
|
+
case format === BodyFormat.Json:
|
|
198
|
+
return JSON.parse(await clone.text());
|
|
171
199
|
case format === BodyFormat.String:
|
|
172
200
|
return await clone.text();
|
|
173
201
|
case format === BodyFormat.File:
|
|
@@ -302,16 +330,19 @@ async function decodeResponse(response, codecOptions) {
|
|
|
302
330
|
const config = {
|
|
303
331
|
provideEvent: undefined,
|
|
304
332
|
collectFlightData: undefined,
|
|
333
|
+
transformDirectResult: undefined,
|
|
305
334
|
endpoint: "/_server"
|
|
306
335
|
};
|
|
307
336
|
function configureServerFunctionsServer({
|
|
308
337
|
provideEvent,
|
|
309
338
|
collectFlightData,
|
|
339
|
+
transformDirectResult,
|
|
310
340
|
endpoint,
|
|
311
341
|
codec
|
|
312
342
|
} = {}) {
|
|
313
343
|
if (provideEvent !== undefined) config.provideEvent = provideEvent;
|
|
314
344
|
if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
|
|
345
|
+
if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
|
|
315
346
|
if (endpoint !== undefined) config.endpoint = endpoint;
|
|
316
347
|
if (codec !== undefined) configureServerFunctionsCodec(codec);
|
|
317
348
|
}
|
|
@@ -370,9 +401,20 @@ function createServerReference({
|
|
|
370
401
|
id
|
|
371
402
|
};
|
|
372
403
|
evt.serverOnly = true;
|
|
373
|
-
|
|
404
|
+
const result = provideEvent(evt, () => {
|
|
374
405
|
return fn.apply(thisArg, args);
|
|
375
406
|
});
|
|
407
|
+
const transform = config.transformDirectResult;
|
|
408
|
+
if (transform && result && typeof result.then === "function") {
|
|
409
|
+
return result.then(value => transform(value, {
|
|
410
|
+
id,
|
|
411
|
+
event: evt
|
|
412
|
+
}));
|
|
413
|
+
}
|
|
414
|
+
return transform ? transform(result, {
|
|
415
|
+
id,
|
|
416
|
+
event: evt
|
|
417
|
+
}) : result;
|
|
376
418
|
}
|
|
377
419
|
});
|
|
378
420
|
}
|
|
@@ -398,7 +440,8 @@ function resolveFunctionId(request, url) {
|
|
|
398
440
|
}
|
|
399
441
|
async function parseArguments(request, url, instance, codec) {
|
|
400
442
|
const parsed = [];
|
|
401
|
-
|
|
443
|
+
const bodyFormat = request.method === "POST" ? request.headers.get(BODY_FORMAT_HEADER) : null;
|
|
444
|
+
if (!instance || request.method === "GET" || bodyFormat !== BodyFormat.Serialized) {
|
|
402
445
|
const args = url.searchParams.get("args");
|
|
403
446
|
if (args) {
|
|
404
447
|
const result = args.startsWith(";0x") ? await deserializeString(args, codec) : JSON.parse(args);
|
|
@@ -408,9 +451,8 @@ async function parseArguments(request, url, instance, codec) {
|
|
|
408
451
|
}
|
|
409
452
|
}
|
|
410
453
|
if (request.method === "POST" && request.body !== null) {
|
|
411
|
-
const format = request.headers.get(BODY_FORMAT_HEADER);
|
|
412
454
|
const decoded = await extractBody(request.clone(), codec);
|
|
413
|
-
if (
|
|
455
|
+
if (bodyFormat === BodyFormat.Serialized || bodyFormat === BodyFormat.Json) {
|
|
414
456
|
return decoded;
|
|
415
457
|
}
|
|
416
458
|
parsed.push(decoded);
|
|
@@ -468,12 +510,11 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
468
510
|
status: 404
|
|
469
511
|
});
|
|
470
512
|
}
|
|
471
|
-
|
|
472
|
-
if (request.method === "GET" !== (allowedMethod === "GET")) {
|
|
513
|
+
if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
|
|
473
514
|
return new Response(process.env.NODE_ENV === "development" ? `Method not allowed for server function: ${functionId}` : null, {
|
|
474
515
|
status: 405,
|
|
475
516
|
headers: {
|
|
476
|
-
Allow:
|
|
517
|
+
Allow: "POST"
|
|
477
518
|
}
|
|
478
519
|
});
|
|
479
520
|
}
|
|
@@ -592,7 +633,7 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
592
633
|
thrown: true
|
|
593
634
|
});
|
|
594
635
|
}
|
|
595
|
-
headers.set(
|
|
636
|
+
headers.set(ERROR_HEADER, "true");
|
|
596
637
|
if (!instance) {
|
|
597
638
|
if (options.handleNoJS) return options.handleNoJS(x, request, parsed, true);
|
|
598
639
|
if (x instanceof Response) return x;
|
|
@@ -607,9 +648,9 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
607
648
|
});
|
|
608
649
|
}
|
|
609
650
|
const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
|
|
610
|
-
headers.set(
|
|
651
|
+
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
|
|
611
652
|
return encodeResult(x, headers, 200, codec);
|
|
612
653
|
}
|
|
613
654
|
}
|
|
614
655
|
|
|
615
|
-
export { FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, configureServerFunctionsServer, createServerReference, decodeResponse, getServerFunction, getServerFunctionMeta, getServerFunctionMetadata, handleServerFunctionRequest, isServerFunction, registerServerFunction, registerServerReference, subscribeFlightData, withMeta };
|
|
656
|
+
export { ERROR_HEADER, FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, configureServerFunctionsServer, createServerReference, decodeErrorHeaderValue, decodeResponse, encodeErrorHeaderValue, getServerFunction, getServerFunctionMeta, getServerFunctionMetadata, handleServerFunctionRequest, isServerFunction, registerServerFunction, registerServerReference, subscribeFlightData, withMeta };
|
package/types/client.d.ts
CHANGED
|
@@ -66,6 +66,32 @@ export function assign(
|
|
|
66
66
|
): void;
|
|
67
67
|
export function setAttribute(node: Element, name: string, value: string): void;
|
|
68
68
|
export function setAttributeNS(node: Element, namespace: string, name: string, value: string): void;
|
|
69
|
+
/**
|
|
70
|
+
* Register a consumer for compiler-emitted element claims. Compiled DOM
|
|
71
|
+
* output claims navigation-relevant elements (`a[href]`, `form[action]`) at
|
|
72
|
+
* creation, and compiler-owned writes to `href`/`action` re-invoke the same
|
|
73
|
+
* handlers — so handlers must be idempotent and must check the element's
|
|
74
|
+
* relevance themselves (rechecks can fire for any element whose
|
|
75
|
+
* `href`/`action` is written, e.g. `<link href>`). Handlers run under the
|
|
76
|
+
* reactive owner current at element creation; scope per-element state and
|
|
77
|
+
* cleanup through your own reactive system. Dormant until registered —
|
|
78
|
+
* without a handler the emitted claims are null checks. Returns an
|
|
79
|
+
* unregister function.
|
|
80
|
+
*/
|
|
81
|
+
export function registerElementClaim(handler: (element: Element) => void): () => void;
|
|
82
|
+
/**
|
|
83
|
+
* Claim `node` for registered consumers (see `registerElementClaim`).
|
|
84
|
+
* Emitted by the compiler at element creation; idempotent by contract.
|
|
85
|
+
*/
|
|
86
|
+
export function claimElement<T extends Element>(node: T): T;
|
|
87
|
+
/**
|
|
88
|
+
* Sweep-claim every navigation-relevant element (`a[href]`, `form[action]`)
|
|
89
|
+
* in `root` — the subtree equivalent of the per-element `claimElement`
|
|
90
|
+
* compiled output emits, for content that becomes live DOM without compiled
|
|
91
|
+
* creation code (frame streams, adopted SSR ranges). Dormant without a
|
|
92
|
+
* registered consumer.
|
|
93
|
+
*/
|
|
94
|
+
export function claimElementTree<T extends Node>(root: T): T;
|
|
69
95
|
export function className(node: Element, value: JSX.ClassValue, prev?: JSX.ClassValue): void;
|
|
70
96
|
export function setProperty(node: Element, name: string, value: any): void;
|
|
71
97
|
export function setStyleProperty(node: Element, name: string, value: any): void;
|
package/types/core.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope } from "solid-js";
|
|
1
|
+
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration } from "solid-js";
|
|
2
2
|
export declare const effect: (fn: any, effectFn: any, options: any) => void;
|
|
3
3
|
export declare const memo: (fn: any) => import("solid-js").SourceAccessor<any>;
|
|
4
|
+
export declare const runWithHydrationScope: (id: any, fn: any) => unknown;
|