@solidjs/web 2.0.0-experimental.9 → 2.0.0-rc.0
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/README.md +22 -4
- package/dist/dev.cjs +1585 -225
- package/dist/dev.js +1510 -201
- package/dist/server.cjs +2642 -264
- package/dist/server.js +2542 -217
- package/dist/web.cjs +1523 -218
- package/dist/web.js +1448 -194
- package/frames/dist/client.cjs +1916 -0
- package/frames/dist/client.dev.cjs +1933 -0
- package/frames/dist/client.dev.js +1921 -0
- package/frames/dist/client.js +1904 -0
- package/frames/dist/server.cjs +3667 -0
- package/frames/dist/server.js +3654 -0
- package/frames/package.json +30 -0
- package/package.json +349 -37
- package/serialization/decode/package.json +20 -0
- package/serialization/dist/decode.cjs +110 -0
- package/serialization/dist/decode.js +104 -0
- package/serialization/dist/serialization.cjs +232 -0
- package/serialization/dist/serialization.js +215 -0
- package/serialization/package.json +20 -0
- package/serialization/types/index.d.ts +182 -0
- package/serialization/types/serializer-decode.d.ts +182 -0
- package/serialization/types-cjs/index.d.cts +182 -0
- package/serialization/types-cjs/package.json +3 -0
- package/serialization/types-cjs/serializer-decode.d.cts +182 -0
- package/server-functions/dist/client.cjs +646 -0
- package/server-functions/dist/client.js +617 -0
- package/server-functions/dist/rich-args.cjs +11 -0
- package/server-functions/dist/rich-args.js +9 -0
- package/server-functions/dist/server.cjs +1077 -0
- package/server-functions/dist/server.dev.cjs +1077 -0
- package/server-functions/dist/server.dev.js +1045 -0
- package/server-functions/dist/server.js +1045 -0
- package/server-functions/package.json +40 -0
- package/server-functions/rich-args/package.json +20 -0
- package/storage/package.json +8 -3
- package/storage/types/index.d.ts +26 -0
- package/storage/types-cjs/index.d.cts +28 -0
- package/storage/types-cjs/package.json +3 -0
- package/types/client.d.ts +290 -27
- package/types/cookies.d.ts +93 -0
- package/types/core.d.ts +6 -2
- package/types/frames/client.d.ts +36 -0
- package/types/frames/frame-client.d.ts +338 -0
- package/types/frames/frame-sink.d.ts +194 -0
- package/types/frames/frame-transport.d.ts +222 -0
- package/types/frames/serializer.d.ts +182 -0
- package/types/frames/server.d.ts +52 -0
- package/types/index.d.ts +209 -24
- package/types/jsx-properties.d.ts +93 -0
- package/types/jsx.d.ts +4150 -1
- package/types/response.d.ts +174 -0
- package/types/serializer-decode.d.ts +182 -0
- package/types/serializer.d.ts +182 -0
- package/types/server-functions/client.d.ts +201 -0
- package/types/server-functions/flash.d.ts +38 -0
- package/types/server-functions/rich-args.d.ts +10 -0
- package/types/server-functions/server.d.ts +588 -0
- package/types/server-functions/shared.d.ts +523 -0
- package/types/server-mock.d.ts +249 -12
- package/types/server.d.ts +424 -51
- package/types-cjs/client.d.cts +337 -0
- package/types-cjs/cookies.d.cts +93 -0
- package/types-cjs/core.d.cts +6 -0
- package/types-cjs/frames/client.d.cts +36 -0
- package/types-cjs/frames/frame-client.d.cts +338 -0
- package/types-cjs/frames/frame-sink.d.cts +194 -0
- package/types-cjs/frames/frame-transport.d.cts +222 -0
- package/types-cjs/frames/serializer.d.cts +182 -0
- package/types-cjs/frames/server.d.cts +52 -0
- package/types-cjs/index.d.cts +230 -0
- package/types-cjs/jsx-properties.d.cts +93 -0
- package/types-cjs/jsx.d.cts +4150 -0
- package/types-cjs/package.json +3 -0
- package/types-cjs/response.d.cts +174 -0
- package/types-cjs/serializer-decode.d.cts +182 -0
- package/types-cjs/serializer.d.cts +182 -0
- package/types-cjs/server-functions/client.d.cts +201 -0
- package/types-cjs/server-functions/flash.d.cts +38 -0
- package/types-cjs/server-functions/rich-args.d.cts +10 -0
- package/types-cjs/server-functions/server.d.cts +588 -0
- package/types-cjs/server-functions/shared.d.cts +523 -0
- package/types-cjs/server-mock.d.cts +277 -0
- package/types-cjs/server.d.cts +523 -0
|
@@ -0,0 +1,617 @@
|
|
|
1
|
+
const REVALIDATE_HEADER = "X-Revalidate";
|
|
2
|
+
|
|
3
|
+
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
|
|
4
|
+
function getServerFunctionMetadata(fn) {
|
|
5
|
+
if (typeof fn !== "function") return undefined;
|
|
6
|
+
return fn[SERVER_FUNCTION_METADATA] || undefined;
|
|
7
|
+
}
|
|
8
|
+
function isServerFunction(fn) {
|
|
9
|
+
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
|
|
10
|
+
}
|
|
11
|
+
function withMeta(fn, meta) {
|
|
12
|
+
const metadata = getServerFunctionMetadata(fn);
|
|
13
|
+
if (!metadata) {
|
|
14
|
+
throw new Error("withMeta expects a server function reference");
|
|
15
|
+
}
|
|
16
|
+
Object.assign(metadata, meta);
|
|
17
|
+
return fn;
|
|
18
|
+
}
|
|
19
|
+
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
|
|
20
|
+
function provideServerFunctionRPC(rpc) {
|
|
21
|
+
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const FLASH_COOKIE = "flash";
|
|
25
|
+
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
|
|
26
|
+
function hasFlashCookie(cookieHeader) {
|
|
27
|
+
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
|
|
28
|
+
}
|
|
29
|
+
function clearFlashCookie() {
|
|
30
|
+
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const codecConfig = {
|
|
34
|
+
codec: undefined
|
|
35
|
+
};
|
|
36
|
+
function configureServerFunctionsCodec(codec) {
|
|
37
|
+
codecConfig.codec = codec;
|
|
38
|
+
}
|
|
39
|
+
function getServerFunctionsCodec() {
|
|
40
|
+
return codecConfig.codec;
|
|
41
|
+
}
|
|
42
|
+
const flightConfig = {
|
|
43
|
+
consumer: undefined
|
|
44
|
+
};
|
|
45
|
+
function subscribeFlightData(consumer) {
|
|
46
|
+
flightConfig.consumer = consumer;
|
|
47
|
+
return () => {
|
|
48
|
+
if (flightConfig.consumer === consumer) flightConfig.consumer = undefined;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function getFlightDataConsumer() {
|
|
52
|
+
return flightConfig.consumer;
|
|
53
|
+
}
|
|
54
|
+
function frameAddress(id, args) {
|
|
55
|
+
return args && args.length ? id + ":" + hashArguments(args) : id;
|
|
56
|
+
}
|
|
57
|
+
function hashArguments(args) {
|
|
58
|
+
let hash = 0;
|
|
59
|
+
const text = stableString(args);
|
|
60
|
+
for (let i = 0; i < text.length; i++) {
|
|
61
|
+
hash = (hash << 5) - hash + text.charCodeAt(i);
|
|
62
|
+
hash |= 0;
|
|
63
|
+
}
|
|
64
|
+
return (hash >>> 0).toString(36);
|
|
65
|
+
}
|
|
66
|
+
function stableString(value, seen) {
|
|
67
|
+
if (value === null || typeof value !== "object") {
|
|
68
|
+
return typeof value === "bigint" ? value + "n" : String(value);
|
|
69
|
+
}
|
|
70
|
+
if (value instanceof Date) return "Date:" + value.getTime();
|
|
71
|
+
seen || (seen = new Set());
|
|
72
|
+
if (seen.has(value)) return "~";
|
|
73
|
+
seen.add(value);
|
|
74
|
+
if (value instanceof Map) {
|
|
75
|
+
const entries = [];
|
|
76
|
+
for (const [k, v] of value) {
|
|
77
|
+
entries.push(stableString(k, seen) + "=>" + stableString(v, seen));
|
|
78
|
+
}
|
|
79
|
+
return "Map{" + entries.sort().join(",") + "}";
|
|
80
|
+
}
|
|
81
|
+
if (value instanceof Set) {
|
|
82
|
+
const members = [];
|
|
83
|
+
for (const v of value) members.push(stableString(v, seen));
|
|
84
|
+
return "Set{" + members.sort().join(",") + "}";
|
|
85
|
+
}
|
|
86
|
+
if (Array.isArray(value)) {
|
|
87
|
+
let out = "[";
|
|
88
|
+
for (let i = 0; i < value.length; i++) out += (i ? "," : "") + stableString(value[i], seen);
|
|
89
|
+
return out + "]";
|
|
90
|
+
}
|
|
91
|
+
const keys = Object.keys(value).sort();
|
|
92
|
+
let out = "{";
|
|
93
|
+
for (let i = 0; i < keys.length; i++) {
|
|
94
|
+
out += (i ? "," : "") + keys[i] + ":" + stableString(value[keys[i]], seen);
|
|
95
|
+
}
|
|
96
|
+
return out + "}";
|
|
97
|
+
}
|
|
98
|
+
const FUNCTION_HEADER = "X-Server-Function-Id";
|
|
99
|
+
const ERROR_HEADER = "X-Server-Function-Error";
|
|
100
|
+
const ERROR_HEADER_MARKER = "=?1?";
|
|
101
|
+
const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
|
|
102
|
+
function encodeErrorHeaderValue(value) {
|
|
103
|
+
let stripped = String(value).replace(/[\r\n]+/g, "");
|
|
104
|
+
if (!NEEDS_ENCODING.test(stripped) && !stripped.startsWith(ERROR_HEADER_MARKER) && stripped === stripped.trim()) {
|
|
105
|
+
return stripped;
|
|
106
|
+
}
|
|
107
|
+
if (typeof stripped.toWellFormed === "function") {
|
|
108
|
+
stripped = stripped.toWellFormed();
|
|
109
|
+
} else {
|
|
110
|
+
stripped = stripped.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
|
|
111
|
+
}
|
|
112
|
+
return ERROR_HEADER_MARKER + encodeURIComponent(stripped);
|
|
113
|
+
}
|
|
114
|
+
function decodeErrorHeaderValue(value) {
|
|
115
|
+
if (typeof value !== "string" || !value.startsWith(ERROR_HEADER_MARKER)) {
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
try {
|
|
119
|
+
return decodeURIComponent(value.slice(ERROR_HEADER_MARKER.length));
|
|
120
|
+
} catch {
|
|
121
|
+
return value;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const INSTANCE_HEADER = "X-Server-Function-Instance";
|
|
125
|
+
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
|
|
126
|
+
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
|
|
127
|
+
const FILE_FORM_KEY = "__server_function_file__";
|
|
128
|
+
const BodyFormat = {
|
|
129
|
+
Serialized: "0",
|
|
130
|
+
String: "1",
|
|
131
|
+
FormData: "2",
|
|
132
|
+
URLSearchParams: "3",
|
|
133
|
+
Blob: "4",
|
|
134
|
+
File: "5",
|
|
135
|
+
ArrayBuffer: "6",
|
|
136
|
+
Uint8Array: "7",
|
|
137
|
+
Json: "8"
|
|
138
|
+
};
|
|
139
|
+
const JSON_SAFE_DEPTH_LIMIT = 10000;
|
|
140
|
+
const EXIT = {};
|
|
141
|
+
function isJSONSafe(value) {
|
|
142
|
+
const stack = [value];
|
|
143
|
+
const ancestors = new Set();
|
|
144
|
+
while (stack.length) {
|
|
145
|
+
const v = stack.pop();
|
|
146
|
+
if (v === EXIT) {
|
|
147
|
+
ancestors.delete(stack.pop());
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
if (v === null) continue;
|
|
151
|
+
const t = typeof v;
|
|
152
|
+
if (t === "string" || t === "boolean") continue;
|
|
153
|
+
if (t === "number") {
|
|
154
|
+
if (!Number.isFinite(v)) return false;
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (t !== "object") return false;
|
|
158
|
+
if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
|
|
159
|
+
ancestors.add(v);
|
|
160
|
+
stack.push(v, EXIT);
|
|
161
|
+
if (Array.isArray(v)) {
|
|
162
|
+
for (let i = 0; i < v.length; i++) stack.push(v[i]);
|
|
163
|
+
} else {
|
|
164
|
+
const proto = Object.getPrototypeOf(v);
|
|
165
|
+
if (proto !== Object.prototype && proto !== null) return false;
|
|
166
|
+
for (const k in v) stack.push(v[k]);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
function getHeadersAndBody(body) {
|
|
172
|
+
switch (true) {
|
|
173
|
+
case typeof body === "string":
|
|
174
|
+
return {
|
|
175
|
+
headers: {
|
|
176
|
+
"Content-Type": "text/plain",
|
|
177
|
+
[BODY_FORMAT_HEADER]: BodyFormat.String
|
|
178
|
+
},
|
|
179
|
+
body
|
|
180
|
+
};
|
|
181
|
+
case body instanceof FormData:
|
|
182
|
+
return {
|
|
183
|
+
headers: {
|
|
184
|
+
[BODY_FORMAT_HEADER]: BodyFormat.FormData
|
|
185
|
+
},
|
|
186
|
+
body
|
|
187
|
+
};
|
|
188
|
+
case body instanceof URLSearchParams:
|
|
189
|
+
return {
|
|
190
|
+
headers: {
|
|
191
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
192
|
+
[BODY_FORMAT_HEADER]: BodyFormat.URLSearchParams
|
|
193
|
+
},
|
|
194
|
+
body
|
|
195
|
+
};
|
|
196
|
+
case typeof File !== "undefined" && body instanceof File:
|
|
197
|
+
{
|
|
198
|
+
const formData = new FormData();
|
|
199
|
+
formData.append(FILE_FORM_KEY, body, body.name);
|
|
200
|
+
return {
|
|
201
|
+
headers: {
|
|
202
|
+
[BODY_FORMAT_HEADER]: BodyFormat.File
|
|
203
|
+
},
|
|
204
|
+
body: formData
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
case body instanceof Blob:
|
|
208
|
+
return {
|
|
209
|
+
headers: {
|
|
210
|
+
[BODY_FORMAT_HEADER]: BodyFormat.Blob
|
|
211
|
+
},
|
|
212
|
+
body
|
|
213
|
+
};
|
|
214
|
+
case body instanceof ArrayBuffer:
|
|
215
|
+
return {
|
|
216
|
+
headers: {
|
|
217
|
+
[BODY_FORMAT_HEADER]: BodyFormat.ArrayBuffer
|
|
218
|
+
},
|
|
219
|
+
body
|
|
220
|
+
};
|
|
221
|
+
case body instanceof Uint8Array:
|
|
222
|
+
return {
|
|
223
|
+
headers: {
|
|
224
|
+
[BODY_FORMAT_HEADER]: BodyFormat.Uint8Array
|
|
225
|
+
},
|
|
226
|
+
body: new Uint8Array(body)
|
|
227
|
+
};
|
|
228
|
+
default:
|
|
229
|
+
return undefined;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
async function extractBody(source, codecOptions) {
|
|
233
|
+
const contentType = source.headers.get("content-type");
|
|
234
|
+
const format = source.headers.get(BODY_FORMAT_HEADER);
|
|
235
|
+
const clone = source.clone();
|
|
236
|
+
switch (true) {
|
|
237
|
+
case format === BodyFormat.Serialized:
|
|
238
|
+
return await deserializeStream(clone, codecOptions);
|
|
239
|
+
case format === BodyFormat.Json:
|
|
240
|
+
return JSON.parse(await clone.text());
|
|
241
|
+
case format === BodyFormat.String:
|
|
242
|
+
return await clone.text();
|
|
243
|
+
case format === BodyFormat.File:
|
|
244
|
+
{
|
|
245
|
+
const formData = await clone.formData();
|
|
246
|
+
return formData.get(FILE_FORM_KEY);
|
|
247
|
+
}
|
|
248
|
+
case format === BodyFormat.FormData:
|
|
249
|
+
case contentType && contentType.startsWith("multipart/form-data"):
|
|
250
|
+
return await clone.formData();
|
|
251
|
+
case format === BodyFormat.URLSearchParams:
|
|
252
|
+
case contentType && contentType.startsWith("application/x-www-form-urlencoded"):
|
|
253
|
+
return new URLSearchParams(await clone.text());
|
|
254
|
+
case format === BodyFormat.Blob:
|
|
255
|
+
return await clone.blob();
|
|
256
|
+
case format === BodyFormat.ArrayBuffer:
|
|
257
|
+
return await clone.arrayBuffer();
|
|
258
|
+
case format === BodyFormat.Uint8Array:
|
|
259
|
+
return new Uint8Array(await clone.arrayBuffer());
|
|
260
|
+
}
|
|
261
|
+
return undefined;
|
|
262
|
+
}
|
|
263
|
+
function createChunk(data) {
|
|
264
|
+
const encoder = new TextEncoder();
|
|
265
|
+
const encodeData = encoder.encode(data);
|
|
266
|
+
const bytes = encodeData.length;
|
|
267
|
+
const chunk = new Uint8Array(12 + bytes);
|
|
268
|
+
chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
|
|
269
|
+
chunk.set(encodeData, 12);
|
|
270
|
+
return chunk;
|
|
271
|
+
}
|
|
272
|
+
class ChunkReader {
|
|
273
|
+
constructor(stream) {
|
|
274
|
+
this.reader = stream.getReader();
|
|
275
|
+
this.buffer = new Uint8Array(0);
|
|
276
|
+
this.done = false;
|
|
277
|
+
}
|
|
278
|
+
async readChunk() {
|
|
279
|
+
const chunk = await this.reader.read();
|
|
280
|
+
if (!chunk.done) {
|
|
281
|
+
const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
|
|
282
|
+
newBuffer.set(this.buffer);
|
|
283
|
+
newBuffer.set(chunk.value, this.buffer.length);
|
|
284
|
+
this.buffer = newBuffer;
|
|
285
|
+
} else {
|
|
286
|
+
this.done = true;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async next() {
|
|
290
|
+
while (this.buffer.length < 12) {
|
|
291
|
+
if (this.done) {
|
|
292
|
+
if (this.buffer.length === 0) return {
|
|
293
|
+
done: true,
|
|
294
|
+
value: undefined
|
|
295
|
+
};
|
|
296
|
+
throw new Error("Malformed server function stream.");
|
|
297
|
+
}
|
|
298
|
+
await this.readChunk();
|
|
299
|
+
}
|
|
300
|
+
const decoder = new TextDecoder();
|
|
301
|
+
const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
|
|
302
|
+
if (Number.isNaN(bytes)) {
|
|
303
|
+
throw new Error("Malformed server function stream.");
|
|
304
|
+
}
|
|
305
|
+
while (bytes > this.buffer.length - 12) {
|
|
306
|
+
if (this.done) {
|
|
307
|
+
throw new Error("Malformed server function stream.");
|
|
308
|
+
}
|
|
309
|
+
await this.readChunk();
|
|
310
|
+
}
|
|
311
|
+
const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
|
|
312
|
+
this.buffer = this.buffer.subarray(12 + bytes);
|
|
313
|
+
return {
|
|
314
|
+
done: false,
|
|
315
|
+
value: partial
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
async drain(interpret) {
|
|
319
|
+
while (true) {
|
|
320
|
+
const result = await this.next();
|
|
321
|
+
if (result.done) {
|
|
322
|
+
break;
|
|
323
|
+
}
|
|
324
|
+
interpret(result.value);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
function serializeStream(value, codecOptions) {
|
|
329
|
+
return new ReadableStream({
|
|
330
|
+
async start(controller) {
|
|
331
|
+
const {
|
|
332
|
+
serializeJSON
|
|
333
|
+
} = await import('@solidjs/web/serialization');
|
|
334
|
+
serializeJSON(value, {
|
|
335
|
+
...codecOptions,
|
|
336
|
+
onParse(node) {
|
|
337
|
+
controller.enqueue(createChunk(JSON.stringify(node)));
|
|
338
|
+
},
|
|
339
|
+
onDone() {
|
|
340
|
+
controller.close();
|
|
341
|
+
},
|
|
342
|
+
onError(error) {
|
|
343
|
+
controller.error(error);
|
|
344
|
+
}
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
async function serializeString(value, codecOptions) {
|
|
350
|
+
const response = new Response(serializeStream(value, codecOptions));
|
|
351
|
+
return await response.text();
|
|
352
|
+
}
|
|
353
|
+
async function deserializeStream(source, codecOptions) {
|
|
354
|
+
if (!source.body) {
|
|
355
|
+
throw new Error("missing body");
|
|
356
|
+
}
|
|
357
|
+
const reader = new ChunkReader(source.body);
|
|
358
|
+
const result = await reader.next();
|
|
359
|
+
if (!result.done) {
|
|
360
|
+
const {
|
|
361
|
+
createJSONDeserializer
|
|
362
|
+
} = await import('@solidjs/web/serialization/decode');
|
|
363
|
+
const deserializeChunk = createJSONDeserializer(codecOptions);
|
|
364
|
+
function interpretChunk(chunk) {
|
|
365
|
+
return deserializeChunk(JSON.parse(chunk));
|
|
366
|
+
}
|
|
367
|
+
void reader.drain(interpretChunk);
|
|
368
|
+
return interpretChunk(result.value);
|
|
369
|
+
}
|
|
370
|
+
return undefined;
|
|
371
|
+
}
|
|
372
|
+
async function decodeResponse(response, codecOptions) {
|
|
373
|
+
if (!response.body) return undefined;
|
|
374
|
+
return await extractBody(response, codecOptions === undefined ? codecConfig.codec : codecOptions);
|
|
375
|
+
}
|
|
376
|
+
async function decodeResponsePayload(response, codecOptions) {
|
|
377
|
+
const decoded = await decodeResponse(response, codecOptions);
|
|
378
|
+
if (decoded !== undefined && response.headers.has(SINGLE_FLIGHT_HEADER)) {
|
|
379
|
+
return {
|
|
380
|
+
value: decoded.value,
|
|
381
|
+
flightData: decoded.data
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
return {
|
|
385
|
+
value: decoded
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
const config = {
|
|
390
|
+
endpoint: "/_server",
|
|
391
|
+
prepareRequest: undefined,
|
|
392
|
+
responseHandler: undefined,
|
|
393
|
+
serializeArgs: undefined
|
|
394
|
+
};
|
|
395
|
+
function serializeArguments(args) {
|
|
396
|
+
if (!config.serializeArgs) {
|
|
397
|
+
throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " + '(from "@solidjs/web/server-functions/rich-args") 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.");
|
|
398
|
+
}
|
|
399
|
+
return config.serializeArgs(args);
|
|
400
|
+
}
|
|
401
|
+
function configureServerFunctionsClient({
|
|
402
|
+
endpoint,
|
|
403
|
+
codec,
|
|
404
|
+
prepareRequest,
|
|
405
|
+
responseHandler,
|
|
406
|
+
serializeArgs
|
|
407
|
+
} = {}) {
|
|
408
|
+
if (endpoint !== undefined) config.endpoint = endpoint;
|
|
409
|
+
if (codec !== undefined) configureServerFunctionsCodec(codec);
|
|
410
|
+
if (prepareRequest !== undefined) config.prepareRequest = prepareRequest;
|
|
411
|
+
if (responseHandler !== undefined) config.responseHandler = responseHandler;
|
|
412
|
+
if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
|
|
413
|
+
}
|
|
414
|
+
let INSTANCE = 0;
|
|
415
|
+
let rpcProvided = false;
|
|
416
|
+
function provideRPC() {
|
|
417
|
+
if (rpcProvided) return;
|
|
418
|
+
rpcProvided = true;
|
|
419
|
+
provideServerFunctionRPC({
|
|
420
|
+
GET,
|
|
421
|
+
decodeResponse
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
async function createRequest(base, id, instance, options, meta) {
|
|
425
|
+
const headers = {
|
|
426
|
+
...options.headers,
|
|
427
|
+
[FUNCTION_HEADER]: id,
|
|
428
|
+
[INSTANCE_HEADER]: instance
|
|
429
|
+
};
|
|
430
|
+
if (getFlightDataConsumer() && (!options.method || options.method.toUpperCase() !== "GET")) {
|
|
431
|
+
headers[SINGLE_FLIGHT_HEADER] = "true";
|
|
432
|
+
}
|
|
433
|
+
let init = {
|
|
434
|
+
method: "POST",
|
|
435
|
+
...options,
|
|
436
|
+
headers
|
|
437
|
+
};
|
|
438
|
+
if (config.prepareRequest) {
|
|
439
|
+
init = (await config.prepareRequest(init, {
|
|
440
|
+
id,
|
|
441
|
+
meta
|
|
442
|
+
})) || init;
|
|
443
|
+
}
|
|
444
|
+
return fetch(base, init);
|
|
445
|
+
}
|
|
446
|
+
async function initializeResponse(base, id, instance, options, args, meta) {
|
|
447
|
+
if (args.length === 0) {
|
|
448
|
+
return createRequest(base, id, instance, options, meta);
|
|
449
|
+
}
|
|
450
|
+
if (args.length === 1) {
|
|
451
|
+
const result = getHeadersAndBody(args[0]);
|
|
452
|
+
if (result) {
|
|
453
|
+
return createRequest(base, id, instance, {
|
|
454
|
+
...options,
|
|
455
|
+
body: result.body,
|
|
456
|
+
headers: {
|
|
457
|
+
...options.headers,
|
|
458
|
+
...result.headers
|
|
459
|
+
}
|
|
460
|
+
}, meta);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
try {
|
|
464
|
+
if (isJSONSafe(args)) {
|
|
465
|
+
return createRequest(base, id, instance, {
|
|
466
|
+
...options,
|
|
467
|
+
body: JSON.stringify(args),
|
|
468
|
+
headers: {
|
|
469
|
+
...options.headers,
|
|
470
|
+
"Content-Type": "application/json",
|
|
471
|
+
[BODY_FORMAT_HEADER]: BodyFormat.Json
|
|
472
|
+
}
|
|
473
|
+
}, meta);
|
|
474
|
+
}
|
|
475
|
+
} catch {
|
|
476
|
+
}
|
|
477
|
+
if (args.length > 1) {
|
|
478
|
+
try {
|
|
479
|
+
const trailing = getHeadersAndBody(args[args.length - 1]);
|
|
480
|
+
const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
|
|
481
|
+
if (trailing && isJSONSafe(leading)) {
|
|
482
|
+
const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
|
|
483
|
+
return createRequest(target, id, instance, {
|
|
484
|
+
...options,
|
|
485
|
+
body: trailing.body,
|
|
486
|
+
headers: {
|
|
487
|
+
...options.headers,
|
|
488
|
+
...trailing.headers
|
|
489
|
+
}
|
|
490
|
+
}, meta);
|
|
491
|
+
}
|
|
492
|
+
} catch {
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
return createRequest(base, id, instance, {
|
|
496
|
+
...options,
|
|
497
|
+
body: await serializeArguments(args),
|
|
498
|
+
headers: {
|
|
499
|
+
...options.headers,
|
|
500
|
+
"Content-Type": "text/plain",
|
|
501
|
+
[BODY_FORMAT_HEADER]: BodyFormat.Serialized
|
|
502
|
+
}
|
|
503
|
+
}, meta);
|
|
504
|
+
}
|
|
505
|
+
async function fetchServerFunction(base, id, options, args, meta, callArgs = args) {
|
|
506
|
+
const instance = `server-function:${INSTANCE++}`;
|
|
507
|
+
const handler = config.responseHandler;
|
|
508
|
+
const context = handler && handler.capture ? handler.capture({
|
|
509
|
+
id,
|
|
510
|
+
meta
|
|
511
|
+
}) : undefined;
|
|
512
|
+
const response = await initializeResponse(base, id, instance, options, args, meta);
|
|
513
|
+
if (handler) {
|
|
514
|
+
const handled = handler.handle(response, {
|
|
515
|
+
id,
|
|
516
|
+
meta,
|
|
517
|
+
args: callArgs,
|
|
518
|
+
context
|
|
519
|
+
});
|
|
520
|
+
if (handled !== undefined) return handled;
|
|
521
|
+
}
|
|
522
|
+
if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
|
|
523
|
+
const consumer = getFlightDataConsumer();
|
|
524
|
+
if (consumer) {
|
|
525
|
+
const payload = await decodeResponse(response);
|
|
526
|
+
await consumer(payload.data, {
|
|
527
|
+
response
|
|
528
|
+
});
|
|
529
|
+
if (response.headers.has(ERROR_HEADER) && !response.headers.has("Location") && !response.headers.has(REVALIDATE_HEADER)) {
|
|
530
|
+
throw payload.value;
|
|
531
|
+
}
|
|
532
|
+
return payload.value;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (response.headers.has("Location") || response.headers.has(REVALIDATE_HEADER) || response.headers.has(SINGLE_FLIGHT_HEADER)) {
|
|
536
|
+
return response;
|
|
537
|
+
}
|
|
538
|
+
const result = await decodeResponse(response.clone());
|
|
539
|
+
if (response.headers.has(ERROR_HEADER)) {
|
|
540
|
+
throw result;
|
|
541
|
+
}
|
|
542
|
+
return result;
|
|
543
|
+
}
|
|
544
|
+
function createServerReference(id, name, base) {
|
|
545
|
+
provideRPC();
|
|
546
|
+
const metadata = name === undefined ? {} : {
|
|
547
|
+
name
|
|
548
|
+
};
|
|
549
|
+
const fn = (...args) => {
|
|
550
|
+
const handler = config.responseHandler;
|
|
551
|
+
if (handler && handler.intercept) {
|
|
552
|
+
const hit = handler.intercept({
|
|
553
|
+
id,
|
|
554
|
+
meta: metadata,
|
|
555
|
+
args
|
|
556
|
+
});
|
|
557
|
+
if (hit !== undefined) return hit;
|
|
558
|
+
}
|
|
559
|
+
return fetchServerFunction(base || config.endpoint, id, {}, args, metadata);
|
|
560
|
+
};
|
|
561
|
+
fn[SERVER_FUNCTION_METADATA] = metadata;
|
|
562
|
+
return new Proxy(fn, {
|
|
563
|
+
get(target, prop) {
|
|
564
|
+
if (prop === "id") return id;
|
|
565
|
+
if (prop === "url") {
|
|
566
|
+
return base || `${config.endpoint}?id=${encodeURIComponent(id)}`;
|
|
567
|
+
}
|
|
568
|
+
return target[prop];
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
function GET(fn) {
|
|
573
|
+
if (!isServerFunction(fn)) {
|
|
574
|
+
throw new Error("GET expects a server function reference");
|
|
575
|
+
}
|
|
576
|
+
provideRPC();
|
|
577
|
+
const id = fn.id;
|
|
578
|
+
const metadata = {
|
|
579
|
+
...getServerFunctionMetadata(fn)
|
|
580
|
+
};
|
|
581
|
+
const wrapped = async (...args) => {
|
|
582
|
+
const handler = config.responseHandler;
|
|
583
|
+
if (handler && handler.intercept) {
|
|
584
|
+
const hit = handler.intercept({
|
|
585
|
+
id,
|
|
586
|
+
meta: metadata,
|
|
587
|
+
args
|
|
588
|
+
});
|
|
589
|
+
if (hit !== undefined) return hit;
|
|
590
|
+
}
|
|
591
|
+
let base = `${config.endpoint}?id=${encodeURIComponent(id)}`;
|
|
592
|
+
if (args.length) {
|
|
593
|
+
const encoded = isJSONSafe(args) ? JSON.stringify(args) : await serializeArguments(args);
|
|
594
|
+
base += `&args=${encodeURIComponent(encoded)}`;
|
|
595
|
+
}
|
|
596
|
+
return fetchServerFunction(base, id, {
|
|
597
|
+
method: "GET"
|
|
598
|
+
}, [], metadata, args);
|
|
599
|
+
};
|
|
600
|
+
wrapped[SERVER_FUNCTION_METADATA] = metadata;
|
|
601
|
+
wrapped.id = id;
|
|
602
|
+
Object.defineProperty(wrapped, "url", {
|
|
603
|
+
get: () => `${config.endpoint}?id=${encodeURIComponent(id)}`,
|
|
604
|
+
configurable: true
|
|
605
|
+
});
|
|
606
|
+
return withMeta(wrapped, {
|
|
607
|
+
method: "GET"
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
function registerServerReference() {
|
|
611
|
+
throw new Error("registerServerReference must not be called in the client build");
|
|
612
|
+
}
|
|
613
|
+
function getServerFunctionInvocation() {
|
|
614
|
+
return undefined;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
export { ChunkReader, ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, REVALIDATE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsClient, createChunk, createServerReference, decodeErrorHeaderValue, decodeResponse, decodeResponsePayload, deserializeStream, encodeErrorHeaderValue, frameAddress, getFlightDataConsumer, getServerFunctionInvocation, getServerFunctionMetadata, getServerFunctionsCodec, hasFlashCookie, isServerFunction, registerServerReference, serializeString, subscribeFlightData, withMeta };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var client = require('@solidjs/web/server-functions/client');
|
|
4
|
+
|
|
5
|
+
function enableRichArguments() {
|
|
6
|
+
client.configureServerFunctionsClient({
|
|
7
|
+
serializeArgs: args => client.serializeString(args, client.getServerFunctionsCodec())
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
exports.enableRichArguments = enableRichArguments;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { configureServerFunctionsClient, serializeString, getServerFunctionsCodec } from '@solidjs/web/server-functions/client';
|
|
2
|
+
|
|
3
|
+
function enableRichArguments() {
|
|
4
|
+
configureServerFunctionsClient({
|
|
5
|
+
serializeArgs: args => serializeString(args, getServerFunctionsCodec())
|
|
6
|
+
});
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export { enableRichArguments };
|