@solidjs/web 2.0.0-beta.2 → 2.0.0-beta.21
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 +27 -4
- package/dist/dev.cjs +790 -223
- package/dist/dev.js +764 -217
- package/dist/server.cjs +742 -186
- package/dist/server.js +714 -183
- package/dist/web.cjs +779 -196
- package/dist/web.js +753 -190
- package/package.json +193 -38
- package/serialization/dist/serialization.cjs +83 -0
- package/serialization/dist/serialization.js +75 -0
- package/serialization/package.json +20 -0
- package/serialization/types/index.d.ts +139 -0
- package/serialization/types-cjs/index.d.cts +139 -0
- package/serialization/types-cjs/package.json +3 -0
- package/server-functions/dist/client.cjs +448 -0
- package/server-functions/dist/client.js +435 -0
- package/server-functions/dist/server.cjs +632 -0
- package/server-functions/dist/server.js +615 -0
- package/server-functions/package.json +30 -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 +64 -21
- package/types/core.d.ts +3 -3
- package/types/index.d.ts +156 -24
- package/types/jsx-properties.d.ts +93 -0
- package/types/jsx.d.ts +4135 -1
- package/types/response.d.ts +93 -0
- package/types/serializer.d.ts +139 -0
- package/types/server-functions/client.d.ts +137 -0
- package/types/server-functions/server.d.ts +307 -0
- package/types/server-functions/shared.d.ts +342 -0
- package/types/server-mock.d.ts +89 -0
- package/types/server.d.ts +123 -28
- package/types-cjs/client.d.cts +131 -0
- package/types-cjs/core.d.cts +3 -0
- package/types-cjs/index.d.cts +178 -0
- package/types-cjs/jsx-properties.d.cts +93 -0
- package/types-cjs/jsx.d.cts +4135 -0
- package/types-cjs/package.json +3 -0
- package/types-cjs/response.d.cts +93 -0
- package/types-cjs/serializer.d.cts +139 -0
- package/types-cjs/server-functions/client.d.cts +137 -0
- package/types-cjs/server-functions/server.d.cts +307 -0
- package/types-cjs/server-functions/shared.d.cts +342 -0
- package/types-cjs/server-mock.d.cts +161 -0
- package/types-cjs/server.d.cts +251 -0
- package/storage/types/src/client.d.ts +0 -1
- package/storage/types/src/index.d.ts +0 -46
- package/storage/types/src/server-mock.d.ts +0 -72
- package/storage/types/storage/src/index.d.ts +0 -2
|
@@ -0,0 +1,615 @@
|
|
|
1
|
+
import { sharedConfig } from 'solid-js';
|
|
2
|
+
import { fromCrossJSON, Feature, toCrossJSONStream } from 'seroval';
|
|
3
|
+
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
|
|
4
|
+
|
|
5
|
+
const ENVELOPE = Symbol.for("solid.ResponseEnvelope");
|
|
6
|
+
function isResponseEnvelope(value) {
|
|
7
|
+
return !!(value && typeof value === "object" && value[ENVELOPE]);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
Feature.AggregateError | Feature.BigIntTypedArray;
|
|
11
|
+
const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
|
|
12
|
+
CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
|
|
13
|
+
FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
|
|
14
|
+
function resolveSerializerPlugins(customPlugins) {
|
|
15
|
+
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
|
|
16
|
+
}
|
|
17
|
+
const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
|
|
18
|
+
const JSON_CODEC_DEPTH_LIMIT = 64;
|
|
19
|
+
function resolveCodecOptions({
|
|
20
|
+
plugins,
|
|
21
|
+
disabledFeatures,
|
|
22
|
+
depthLimit
|
|
23
|
+
} = {}) {
|
|
24
|
+
return {
|
|
25
|
+
plugins: resolveSerializerPlugins(plugins),
|
|
26
|
+
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
|
|
27
|
+
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function serializeJSON(value, {
|
|
31
|
+
onParse,
|
|
32
|
+
onDone,
|
|
33
|
+
onError,
|
|
34
|
+
...codecOptions
|
|
35
|
+
}) {
|
|
36
|
+
return toCrossJSONStream(value, {
|
|
37
|
+
onParse,
|
|
38
|
+
onDone,
|
|
39
|
+
onError,
|
|
40
|
+
...resolveCodecOptions(codecOptions)
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
function createJSONDeserializer(options) {
|
|
44
|
+
const refs = new Map();
|
|
45
|
+
const resolved = resolveCodecOptions(options);
|
|
46
|
+
return function deserializeJSONChunk(node) {
|
|
47
|
+
return fromCrossJSON(node, {
|
|
48
|
+
refs,
|
|
49
|
+
...resolved
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const RequestContext = Symbol.for("solid.RequestContext");
|
|
55
|
+
function getRequestEvent() {
|
|
56
|
+
return globalThis[RequestContext] ? globalThis[RequestContext].getStore() || sharedConfig.context && sharedConfig.context.event || console.warn("RequestEvent is missing. This is most likely due to accessing `getRequestEvent` non-managed async scope in a partially polyfilled environment. Try moving it above all `await` calls.") : undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const codecConfig = {
|
|
60
|
+
codec: undefined
|
|
61
|
+
};
|
|
62
|
+
function configureServerFunctionsCodec(codec) {
|
|
63
|
+
codecConfig.codec = codec;
|
|
64
|
+
}
|
|
65
|
+
function getServerFunctionsCodec() {
|
|
66
|
+
return codecConfig.codec;
|
|
67
|
+
}
|
|
68
|
+
function subscribeFlightData(consumer) {
|
|
69
|
+
return () => {
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
|
|
73
|
+
function getServerFunctionMetadata(fn) {
|
|
74
|
+
if (typeof fn !== "function") return undefined;
|
|
75
|
+
return fn[SERVER_FUNCTION_METADATA] || undefined;
|
|
76
|
+
}
|
|
77
|
+
function isServerFunction(fn) {
|
|
78
|
+
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
|
|
79
|
+
}
|
|
80
|
+
function withMeta(fn, meta) {
|
|
81
|
+
const metadata = getServerFunctionMetadata(fn);
|
|
82
|
+
if (!metadata) {
|
|
83
|
+
throw new Error("withMeta expects a server function reference");
|
|
84
|
+
}
|
|
85
|
+
Object.assign(metadata, meta);
|
|
86
|
+
return fn;
|
|
87
|
+
}
|
|
88
|
+
const FUNCTION_HEADER = "X-Server-Function-Id";
|
|
89
|
+
const INSTANCE_HEADER = "X-Server-Function-Instance";
|
|
90
|
+
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
|
|
91
|
+
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
|
|
92
|
+
const FILE_FORM_KEY = "__server_function_file__";
|
|
93
|
+
const BodyFormat = {
|
|
94
|
+
Serialized: "0",
|
|
95
|
+
String: "1",
|
|
96
|
+
FormData: "2",
|
|
97
|
+
URLSearchParams: "3",
|
|
98
|
+
Blob: "4",
|
|
99
|
+
File: "5",
|
|
100
|
+
ArrayBuffer: "6",
|
|
101
|
+
Uint8Array: "7"
|
|
102
|
+
};
|
|
103
|
+
function getHeadersAndBody(body) {
|
|
104
|
+
switch (true) {
|
|
105
|
+
case typeof body === "string":
|
|
106
|
+
return {
|
|
107
|
+
headers: {
|
|
108
|
+
"Content-Type": "text/plain",
|
|
109
|
+
[BODY_FORMAT_HEADER]: BodyFormat.String
|
|
110
|
+
},
|
|
111
|
+
body
|
|
112
|
+
};
|
|
113
|
+
case body instanceof FormData:
|
|
114
|
+
return {
|
|
115
|
+
headers: {
|
|
116
|
+
[BODY_FORMAT_HEADER]: BodyFormat.FormData
|
|
117
|
+
},
|
|
118
|
+
body
|
|
119
|
+
};
|
|
120
|
+
case body instanceof URLSearchParams:
|
|
121
|
+
return {
|
|
122
|
+
headers: {
|
|
123
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
124
|
+
[BODY_FORMAT_HEADER]: BodyFormat.URLSearchParams
|
|
125
|
+
},
|
|
126
|
+
body
|
|
127
|
+
};
|
|
128
|
+
case typeof File !== "undefined" && body instanceof File:
|
|
129
|
+
{
|
|
130
|
+
const formData = new FormData();
|
|
131
|
+
formData.append(FILE_FORM_KEY, body, body.name);
|
|
132
|
+
return {
|
|
133
|
+
headers: {
|
|
134
|
+
[BODY_FORMAT_HEADER]: BodyFormat.File
|
|
135
|
+
},
|
|
136
|
+
body: formData
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
case body instanceof Blob:
|
|
140
|
+
return {
|
|
141
|
+
headers: {
|
|
142
|
+
[BODY_FORMAT_HEADER]: BodyFormat.Blob
|
|
143
|
+
},
|
|
144
|
+
body
|
|
145
|
+
};
|
|
146
|
+
case body instanceof ArrayBuffer:
|
|
147
|
+
return {
|
|
148
|
+
headers: {
|
|
149
|
+
[BODY_FORMAT_HEADER]: BodyFormat.ArrayBuffer
|
|
150
|
+
},
|
|
151
|
+
body
|
|
152
|
+
};
|
|
153
|
+
case body instanceof Uint8Array:
|
|
154
|
+
return {
|
|
155
|
+
headers: {
|
|
156
|
+
[BODY_FORMAT_HEADER]: BodyFormat.Uint8Array
|
|
157
|
+
},
|
|
158
|
+
body: new Uint8Array(body)
|
|
159
|
+
};
|
|
160
|
+
default:
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
async function extractBody(source, codecOptions) {
|
|
165
|
+
const contentType = source.headers.get("content-type");
|
|
166
|
+
const format = source.headers.get(BODY_FORMAT_HEADER);
|
|
167
|
+
const clone = source.clone();
|
|
168
|
+
switch (true) {
|
|
169
|
+
case format === BodyFormat.Serialized:
|
|
170
|
+
return await deserializeStream(clone, codecOptions);
|
|
171
|
+
case format === BodyFormat.String:
|
|
172
|
+
return await clone.text();
|
|
173
|
+
case format === BodyFormat.File:
|
|
174
|
+
{
|
|
175
|
+
const formData = await clone.formData();
|
|
176
|
+
return formData.get(FILE_FORM_KEY);
|
|
177
|
+
}
|
|
178
|
+
case format === BodyFormat.FormData:
|
|
179
|
+
case contentType && contentType.startsWith("multipart/form-data"):
|
|
180
|
+
return await clone.formData();
|
|
181
|
+
case format === BodyFormat.URLSearchParams:
|
|
182
|
+
case contentType && contentType.startsWith("application/x-www-form-urlencoded"):
|
|
183
|
+
return new URLSearchParams(await clone.text());
|
|
184
|
+
case format === BodyFormat.Blob:
|
|
185
|
+
return await clone.blob();
|
|
186
|
+
case format === BodyFormat.ArrayBuffer:
|
|
187
|
+
return await clone.arrayBuffer();
|
|
188
|
+
case format === BodyFormat.Uint8Array:
|
|
189
|
+
return new Uint8Array(await clone.arrayBuffer());
|
|
190
|
+
}
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
function createChunk(data) {
|
|
194
|
+
const encodeData = new TextEncoder().encode(data);
|
|
195
|
+
const bytes = encodeData.length;
|
|
196
|
+
const baseHex = bytes.toString(16);
|
|
197
|
+
const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
|
|
198
|
+
const head = new TextEncoder().encode(`;0x${totalHex};`);
|
|
199
|
+
const chunk = new Uint8Array(12 + bytes);
|
|
200
|
+
chunk.set(head);
|
|
201
|
+
chunk.set(encodeData, 12);
|
|
202
|
+
return chunk;
|
|
203
|
+
}
|
|
204
|
+
class ChunkReader {
|
|
205
|
+
constructor(stream) {
|
|
206
|
+
this.reader = stream.getReader();
|
|
207
|
+
this.buffer = new Uint8Array(0);
|
|
208
|
+
this.done = false;
|
|
209
|
+
}
|
|
210
|
+
async readChunk() {
|
|
211
|
+
const chunk = await this.reader.read();
|
|
212
|
+
if (!chunk.done) {
|
|
213
|
+
const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
|
|
214
|
+
newBuffer.set(this.buffer);
|
|
215
|
+
newBuffer.set(chunk.value, this.buffer.length);
|
|
216
|
+
this.buffer = newBuffer;
|
|
217
|
+
} else {
|
|
218
|
+
this.done = true;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
async next() {
|
|
222
|
+
if (this.buffer.length === 0) {
|
|
223
|
+
if (this.done) {
|
|
224
|
+
return {
|
|
225
|
+
done: true,
|
|
226
|
+
value: undefined
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
await this.readChunk();
|
|
230
|
+
return await this.next();
|
|
231
|
+
}
|
|
232
|
+
const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
|
|
233
|
+
const bytes = Number.parseInt(head, 16);
|
|
234
|
+
if (Number.isNaN(bytes)) {
|
|
235
|
+
throw new Error("Malformed server function stream.");
|
|
236
|
+
}
|
|
237
|
+
while (bytes > this.buffer.length - 12) {
|
|
238
|
+
if (this.done) {
|
|
239
|
+
throw new Error("Malformed server function stream.");
|
|
240
|
+
}
|
|
241
|
+
await this.readChunk();
|
|
242
|
+
}
|
|
243
|
+
const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
|
|
244
|
+
this.buffer = this.buffer.subarray(12 + bytes);
|
|
245
|
+
return {
|
|
246
|
+
done: false,
|
|
247
|
+
value: partial
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
async drain(interpret) {
|
|
251
|
+
while (true) {
|
|
252
|
+
const result = await this.next();
|
|
253
|
+
if (result.done) {
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
interpret(result.value);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
function serializeStream(value, codecOptions) {
|
|
261
|
+
return new ReadableStream({
|
|
262
|
+
start(controller) {
|
|
263
|
+
serializeJSON(value, {
|
|
264
|
+
...codecOptions,
|
|
265
|
+
onParse(node) {
|
|
266
|
+
controller.enqueue(createChunk(JSON.stringify(node)));
|
|
267
|
+
},
|
|
268
|
+
onDone() {
|
|
269
|
+
controller.close();
|
|
270
|
+
},
|
|
271
|
+
onError(error) {
|
|
272
|
+
controller.error(error);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
async function deserializeStream(source, codecOptions) {
|
|
279
|
+
if (!source.body) {
|
|
280
|
+
throw new Error("missing body");
|
|
281
|
+
}
|
|
282
|
+
const reader = new ChunkReader(source.body);
|
|
283
|
+
const result = await reader.next();
|
|
284
|
+
if (!result.done) {
|
|
285
|
+
const deserializeChunk = createJSONDeserializer(codecOptions);
|
|
286
|
+
function interpretChunk(chunk) {
|
|
287
|
+
return deserializeChunk(JSON.parse(chunk));
|
|
288
|
+
}
|
|
289
|
+
void reader.drain(interpretChunk);
|
|
290
|
+
return interpretChunk(result.value);
|
|
291
|
+
}
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
async function deserializeString(text, codecOptions) {
|
|
295
|
+
return await deserializeStream(new Response(text), codecOptions);
|
|
296
|
+
}
|
|
297
|
+
async function decodeResponse(response, codecOptions) {
|
|
298
|
+
if (!response.body) return undefined;
|
|
299
|
+
return await extractBody(response, codecOptions === undefined ? codecConfig.codec : codecOptions);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const config = {
|
|
303
|
+
provideEvent: undefined,
|
|
304
|
+
collectFlightData: undefined,
|
|
305
|
+
endpoint: "/_server"
|
|
306
|
+
};
|
|
307
|
+
function configureServerFunctionsServer({
|
|
308
|
+
provideEvent,
|
|
309
|
+
collectFlightData,
|
|
310
|
+
endpoint,
|
|
311
|
+
codec
|
|
312
|
+
} = {}) {
|
|
313
|
+
if (provideEvent !== undefined) config.provideEvent = provideEvent;
|
|
314
|
+
if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
|
|
315
|
+
if (endpoint !== undefined) config.endpoint = endpoint;
|
|
316
|
+
if (codec !== undefined) configureServerFunctionsCodec(codec);
|
|
317
|
+
}
|
|
318
|
+
function provideEvent(event, fn) {
|
|
319
|
+
if (config.provideEvent) return config.provideEvent(event, fn);
|
|
320
|
+
const ctx = globalThis[RequestContext];
|
|
321
|
+
if (ctx) return ctx.run(event, fn);
|
|
322
|
+
throw new Error("No request event provider. Configure one with configureServerFunctionsServer({ provideEvent }).");
|
|
323
|
+
}
|
|
324
|
+
const REGISTRATIONS = new Map();
|
|
325
|
+
const METHODS = new Map();
|
|
326
|
+
function registerServerFunction(id, callback) {
|
|
327
|
+
REGISTRATIONS.set(id, callback);
|
|
328
|
+
return callback;
|
|
329
|
+
}
|
|
330
|
+
function getServerFunction(id) {
|
|
331
|
+
const fn = REGISTRATIONS.get(id);
|
|
332
|
+
if (fn) {
|
|
333
|
+
return fn;
|
|
334
|
+
}
|
|
335
|
+
throw new Error("invalid server function: " + id);
|
|
336
|
+
}
|
|
337
|
+
function registerServerReference(id, fn, name) {
|
|
338
|
+
registerServerFunction(id, fn);
|
|
339
|
+
return {
|
|
340
|
+
id,
|
|
341
|
+
fn,
|
|
342
|
+
name
|
|
343
|
+
};
|
|
344
|
+
}
|
|
345
|
+
function createServerReference({
|
|
346
|
+
id,
|
|
347
|
+
fn,
|
|
348
|
+
name
|
|
349
|
+
}) {
|
|
350
|
+
if (typeof fn !== "function") throw new Error("Export from a 'use server' module must be a function");
|
|
351
|
+
const metadata = name === undefined ? {} : {
|
|
352
|
+
name
|
|
353
|
+
};
|
|
354
|
+
return new Proxy(fn, {
|
|
355
|
+
get(target, prop) {
|
|
356
|
+
if (prop === "id") return id;
|
|
357
|
+
if (prop === "url") {
|
|
358
|
+
return `${config.endpoint}?id=${encodeURIComponent(id)}`;
|
|
359
|
+
}
|
|
360
|
+
if (prop === SERVER_FUNCTION_METADATA) return metadata;
|
|
361
|
+
return target[prop];
|
|
362
|
+
},
|
|
363
|
+
apply(target, thisArg, args) {
|
|
364
|
+
const ogEvt = getRequestEvent();
|
|
365
|
+
if (!ogEvt) throw new Error("Cannot call server function outside of a request");
|
|
366
|
+
const evt = {
|
|
367
|
+
...ogEvt
|
|
368
|
+
};
|
|
369
|
+
evt.locals.serverFunctionMeta = {
|
|
370
|
+
id
|
|
371
|
+
};
|
|
372
|
+
evt.serverOnly = true;
|
|
373
|
+
return provideEvent(evt, () => {
|
|
374
|
+
return fn.apply(thisArg, args);
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
function GET(fn) {
|
|
380
|
+
if (!isServerFunction(fn) || typeof fn.id !== "string") {
|
|
381
|
+
throw new Error("GET expects a server function reference");
|
|
382
|
+
}
|
|
383
|
+
METHODS.set(fn.id, "GET");
|
|
384
|
+
return withMeta(fn, {
|
|
385
|
+
method: "GET"
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
function getServerFunctionMeta() {
|
|
389
|
+
const event = getRequestEvent();
|
|
390
|
+
return event && event.locals.serverFunctionMeta;
|
|
391
|
+
}
|
|
392
|
+
function resolveFunctionId(request, url) {
|
|
393
|
+
const reference = request.headers.get(FUNCTION_HEADER);
|
|
394
|
+
if (reference) {
|
|
395
|
+
return reference.split("#")[0];
|
|
396
|
+
}
|
|
397
|
+
return url.searchParams.get("id");
|
|
398
|
+
}
|
|
399
|
+
async function parseArguments(request, url, instance, codec) {
|
|
400
|
+
const parsed = [];
|
|
401
|
+
if (!instance || request.method === "GET") {
|
|
402
|
+
const args = url.searchParams.get("args");
|
|
403
|
+
if (args) {
|
|
404
|
+
const result = args.startsWith(";0x") ? await deserializeString(args, codec) : JSON.parse(args);
|
|
405
|
+
for (const arg of result) {
|
|
406
|
+
parsed.push(arg);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (request.method === "POST" && request.body !== null) {
|
|
411
|
+
const format = request.headers.get(BODY_FORMAT_HEADER);
|
|
412
|
+
const decoded = await extractBody(request.clone(), codec);
|
|
413
|
+
if (format === BodyFormat.Serialized) {
|
|
414
|
+
return decoded;
|
|
415
|
+
}
|
|
416
|
+
parsed.push(decoded);
|
|
417
|
+
}
|
|
418
|
+
return parsed;
|
|
419
|
+
}
|
|
420
|
+
async function foldFlightData(hook, event, headers, outcome) {
|
|
421
|
+
const data = await hook(event, outcome);
|
|
422
|
+
if (data === undefined) return outcome.value;
|
|
423
|
+
headers.set(SINGLE_FLIGHT_HEADER, "true");
|
|
424
|
+
return {
|
|
425
|
+
value: outcome.value,
|
|
426
|
+
data
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
function serializedResponse(value, headers, codec) {
|
|
430
|
+
headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
|
|
431
|
+
headers.set("Content-Type", "text/plain");
|
|
432
|
+
return new Response(serializeStream(value, codec), {
|
|
433
|
+
headers
|
|
434
|
+
});
|
|
435
|
+
}
|
|
436
|
+
function encodeResult(value, headers, status, codec) {
|
|
437
|
+
const direct = getHeadersAndBody(value);
|
|
438
|
+
if (direct) {
|
|
439
|
+
for (const [key, val] of Object.entries(direct.headers || {})) {
|
|
440
|
+
headers.set(key, val);
|
|
441
|
+
}
|
|
442
|
+
return new Response(direct.body, {
|
|
443
|
+
status,
|
|
444
|
+
headers
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
const response = serializedResponse(value, headers, codec);
|
|
448
|
+
return status === 200 ? response : new Response(response.body, {
|
|
449
|
+
status,
|
|
450
|
+
headers
|
|
451
|
+
});
|
|
452
|
+
}
|
|
453
|
+
async function handleServerFunctionRequest(request, options = {}) {
|
|
454
|
+
const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
|
|
455
|
+
const url = new URL(request.url);
|
|
456
|
+
const instance = request.headers.get(INSTANCE_HEADER);
|
|
457
|
+
const functionId = resolveFunctionId(request, url);
|
|
458
|
+
if (!functionId) {
|
|
459
|
+
return new Response(process.env.NODE_ENV === "development" ? "Server function not found" : null, {
|
|
460
|
+
status: 404
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
let serverFunction;
|
|
464
|
+
try {
|
|
465
|
+
serverFunction = getServerFunction(functionId);
|
|
466
|
+
} catch {
|
|
467
|
+
return new Response(process.env.NODE_ENV === "development" ? `Unknown server function: ${functionId}` : null, {
|
|
468
|
+
status: 404
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
const allowedMethod = METHODS.get(functionId) || "POST";
|
|
472
|
+
if (request.method === "GET" !== (allowedMethod === "GET")) {
|
|
473
|
+
return new Response(process.env.NODE_ENV === "development" ? `Method not allowed for server function: ${functionId}` : null, {
|
|
474
|
+
status: 405,
|
|
475
|
+
headers: {
|
|
476
|
+
Allow: allowedMethod
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
const event = options.createEvent ? options.createEvent(request) : {
|
|
481
|
+
request,
|
|
482
|
+
locals: {}
|
|
483
|
+
};
|
|
484
|
+
const provide = options.provideEvent || provideEvent;
|
|
485
|
+
const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
|
|
486
|
+
const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
|
|
487
|
+
const parsed = await parseArguments(request, url, instance, codec);
|
|
488
|
+
const headers = new Headers();
|
|
489
|
+
try {
|
|
490
|
+
let result = await provide(event, async () => {
|
|
491
|
+
event.locals.serverFunctionMeta = {
|
|
492
|
+
id: functionId
|
|
493
|
+
};
|
|
494
|
+
return serverFunction(...parsed);
|
|
495
|
+
});
|
|
496
|
+
if (options.transformResult) {
|
|
497
|
+
result = await options.transformResult(event, result, {
|
|
498
|
+
instance,
|
|
499
|
+
request
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
let status = 200;
|
|
503
|
+
let metadata;
|
|
504
|
+
if (isResponseEnvelope(result)) {
|
|
505
|
+
const {
|
|
506
|
+
response,
|
|
507
|
+
value
|
|
508
|
+
} = result;
|
|
509
|
+
if (!instance && !options.handleNoJS && response && response.body) {
|
|
510
|
+
return response;
|
|
511
|
+
}
|
|
512
|
+
if (response && response.headers) {
|
|
513
|
+
response.headers.forEach((val, key) => headers.append(key, val));
|
|
514
|
+
}
|
|
515
|
+
if (response && response.status && (response.status < 300 || response.status >= 400)) {
|
|
516
|
+
status = response.status;
|
|
517
|
+
}
|
|
518
|
+
metadata = response;
|
|
519
|
+
result = value;
|
|
520
|
+
} else if (result instanceof Response) {
|
|
521
|
+
if (result.headers && result.headers.has("X-Content-Raw")) return result;
|
|
522
|
+
if (instance) {
|
|
523
|
+
if (result.headers) {
|
|
524
|
+
result.headers.forEach((value, key) => headers.append(key, value));
|
|
525
|
+
}
|
|
526
|
+
if (result.status && (result.status < 300 || result.status >= 400)) {
|
|
527
|
+
status = result.status;
|
|
528
|
+
}
|
|
529
|
+
metadata = result;
|
|
530
|
+
if (result.body == null) {
|
|
531
|
+
result = null;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
if (collectsFlight) {
|
|
536
|
+
result = await foldFlightData(flightHook, event, headers, {
|
|
537
|
+
id: functionId,
|
|
538
|
+
value: result,
|
|
539
|
+
response: metadata,
|
|
540
|
+
request,
|
|
541
|
+
thrown: false
|
|
542
|
+
});
|
|
543
|
+
}
|
|
544
|
+
if (!instance) {
|
|
545
|
+
if (options.handleNoJS) return options.handleNoJS(result, request, parsed);
|
|
546
|
+
if (result instanceof Response) return result;
|
|
547
|
+
return encodeResult(result, headers, 200, codec);
|
|
548
|
+
}
|
|
549
|
+
return encodeResult(result, headers, status, codec);
|
|
550
|
+
} catch (x) {
|
|
551
|
+
if (x instanceof Response || isResponseEnvelope(x)) {
|
|
552
|
+
if (options.transformResult) {
|
|
553
|
+
x = await options.transformResult(event, x, {
|
|
554
|
+
instance,
|
|
555
|
+
request,
|
|
556
|
+
thrown: true
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
let status = 200;
|
|
560
|
+
let metadata;
|
|
561
|
+
if (isResponseEnvelope(x)) {
|
|
562
|
+
const {
|
|
563
|
+
response,
|
|
564
|
+
value
|
|
565
|
+
} = x;
|
|
566
|
+
if (response && response.headers) {
|
|
567
|
+
response.headers.forEach((val, key) => headers.append(key, val));
|
|
568
|
+
}
|
|
569
|
+
if (response && response.status && (!instance || response.status < 300 || response.status >= 400)) {
|
|
570
|
+
status = response.status;
|
|
571
|
+
}
|
|
572
|
+
metadata = response;
|
|
573
|
+
x = value;
|
|
574
|
+
} else if (x instanceof Response) {
|
|
575
|
+
if (x.headers) {
|
|
576
|
+
x.headers.forEach((value, key) => headers.append(key, value));
|
|
577
|
+
}
|
|
578
|
+
if (x.status && (!instance || x.status < 300 || x.status >= 400)) {
|
|
579
|
+
status = x.status;
|
|
580
|
+
}
|
|
581
|
+
metadata = x;
|
|
582
|
+
if (x.body == null) {
|
|
583
|
+
x = null;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
if (collectsFlight) {
|
|
587
|
+
x = await foldFlightData(flightHook, event, headers, {
|
|
588
|
+
id: functionId,
|
|
589
|
+
value: x,
|
|
590
|
+
response: metadata,
|
|
591
|
+
request,
|
|
592
|
+
thrown: true
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
headers.set("X-Server-Function-Error", "true");
|
|
596
|
+
if (!instance) {
|
|
597
|
+
if (options.handleNoJS) return options.handleNoJS(x, request, parsed, true);
|
|
598
|
+
if (x instanceof Response) return x;
|
|
599
|
+
}
|
|
600
|
+
return encodeResult(x, headers, status, codec);
|
|
601
|
+
}
|
|
602
|
+
if (!instance) {
|
|
603
|
+
if (options.handleNoJS) return options.handleNoJS(x, request, parsed, true);
|
|
604
|
+
const message = x instanceof Error ? x.message : String(x);
|
|
605
|
+
return new Response(process.env.NODE_ENV === "development" ? message : null, {
|
|
606
|
+
status: 500
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
|
|
610
|
+
headers.set("X-Server-Function-Error", error.replace(/[\r\n]+/g, ""));
|
|
611
|
+
return encodeResult(x, headers, 200, codec);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
export { FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, configureServerFunctionsServer, createServerReference, decodeResponse, getServerFunction, getServerFunctionMeta, getServerFunctionMetadata, handleServerFunctionRequest, isServerFunction, registerServerFunction, registerServerReference, subscribeFlightData, withMeta };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@solidjs/web/server-functions",
|
|
3
|
+
"main": "./dist/server.cjs",
|
|
4
|
+
"module": "./dist/server.js",
|
|
5
|
+
"types": "../types/server-functions/server.d.ts",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"browser": {
|
|
11
|
+
"import": {
|
|
12
|
+
"types": "../types/server-functions/client.d.ts",
|
|
13
|
+
"default": "./dist/client.js"
|
|
14
|
+
},
|
|
15
|
+
"require": {
|
|
16
|
+
"types": "../types-cjs/server-functions/client.d.cts",
|
|
17
|
+
"default": "./dist/client.cjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"import": {
|
|
21
|
+
"types": "../types/server-functions/server.d.ts",
|
|
22
|
+
"default": "./dist/server.js"
|
|
23
|
+
},
|
|
24
|
+
"require": {
|
|
25
|
+
"types": "../types-cjs/server-functions/server.d.cts",
|
|
26
|
+
"default": "./dist/server.cjs"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
package/storage/package.json
CHANGED
|
@@ -7,9 +7,14 @@
|
|
|
7
7
|
"sideEffects": false,
|
|
8
8
|
"exports": {
|
|
9
9
|
".": {
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./types/index.d.ts",
|
|
12
|
+
"default": "./dist/storage.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./types-cjs/index.d.cts",
|
|
16
|
+
"default": "./dist/storage.cjs"
|
|
17
|
+
}
|
|
13
18
|
}
|
|
14
19
|
}
|
|
15
20
|
}
|
package/storage/types/index.d.ts
CHANGED
|
@@ -1,2 +1,28 @@
|
|
|
1
1
|
import type { RequestEvent } from "@solidjs/web";
|
|
2
|
+
/**
|
|
3
|
+
* Establishes the request-event scope for a server request: everything
|
|
4
|
+
* `cb` runs (across `await`s, via AsyncLocalStorage) sees `init` from
|
|
5
|
+
* `getRequestEvent()`. Call it at the top of the server's request handling,
|
|
6
|
+
* wrapping SSR and server-function dispatch; the server-functions runtime
|
|
7
|
+
* also picks the scope up automatically as its default event provider.
|
|
8
|
+
*
|
|
9
|
+
* Lives on its own subpath because it imports `node:async_hooks` — keep it
|
|
10
|
+
* out of environments without that module.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* import { provideRequestEvent } from "@solidjs/web/storage";
|
|
15
|
+
*
|
|
16
|
+
* async function handler(request: Request) {
|
|
17
|
+
* return provideRequestEvent({ request, locals: {} }, () =>
|
|
18
|
+
* renderToStringAsync(() => <App />)
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* @param init the event for this request — frameworks pass their richer
|
|
24
|
+
* event shapes
|
|
25
|
+
* @param cb runs synchronously; its return value is passed through
|
|
26
|
+
* @throws on the client, where there is no request to scope
|
|
27
|
+
*/
|
|
2
28
|
export declare function provideRequestEvent<T extends RequestEvent, U>(init: T, cb: () => U): U;
|