@solidjs/web 2.0.0-beta.31 → 2.0.0-beta.33

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.
Files changed (73) hide show
  1. package/README.md +1 -6
  2. package/dist/dev.cjs +285 -60
  3. package/dist/dev.js +267 -57
  4. package/dist/server.cjs +1148 -176
  5. package/dist/server.js +1133 -175
  6. package/dist/web.cjs +285 -60
  7. package/dist/web.js +267 -57
  8. package/frames/dist/client.cjs +397 -175
  9. package/frames/dist/client.dev.cjs +401 -175
  10. package/frames/dist/client.dev.js +399 -173
  11. package/frames/dist/client.js +395 -173
  12. package/frames/dist/server.cjs +1424 -277
  13. package/frames/dist/server.js +1426 -280
  14. package/package.json +69 -7
  15. package/serialization/decode/package.json +20 -0
  16. package/serialization/dist/decode.cjs +110 -0
  17. package/serialization/dist/decode.js +104 -0
  18. package/serialization/dist/serialization.cjs +106 -43
  19. package/serialization/dist/serialization.js +100 -44
  20. package/serialization/types/index.d.ts +85 -60
  21. package/serialization/types/serializer-decode.d.ts +182 -0
  22. package/serialization/types-cjs/index.d.cts +85 -60
  23. package/serialization/types-cjs/serializer-decode.d.cts +182 -0
  24. package/server-functions/dist/client.cjs +131 -98
  25. package/server-functions/dist/client.js +131 -99
  26. package/server-functions/dist/rich-args.cjs +11 -0
  27. package/server-functions/dist/rich-args.js +9 -0
  28. package/server-functions/dist/server.cjs +367 -194
  29. package/server-functions/dist/server.dev.cjs +1077 -0
  30. package/server-functions/dist/server.dev.js +1045 -0
  31. package/server-functions/dist/server.js +365 -195
  32. package/server-functions/package.json +10 -0
  33. package/server-functions/rich-args/package.json +20 -0
  34. package/storage/types/index.d.ts +1 -1
  35. package/storage/types-cjs/index.d.cts +1 -1
  36. package/types/client.d.ts +149 -6
  37. package/types/cookies.d.ts +93 -0
  38. package/types/core.d.ts +4 -2
  39. package/types/frames/client.d.ts +15 -1
  40. package/types/frames/frame-client.d.ts +63 -7
  41. package/types/frames/frame-sink.d.ts +26 -3
  42. package/types/frames/frame-transport.d.ts +40 -8
  43. package/types/frames/serializer.d.ts +85 -60
  44. package/types/frames/server.d.ts +22 -0
  45. package/types/index.d.ts +2 -3
  46. package/types/response.d.ts +45 -0
  47. package/types/serializer-decode.d.ts +182 -0
  48. package/types/serializer.d.ts +85 -60
  49. package/types/server-functions/client.d.ts +2 -1
  50. package/types/server-functions/rich-args.d.ts +10 -0
  51. package/types/server-functions/server.d.ts +99 -1
  52. package/types/server-functions/shared.d.ts +79 -1
  53. package/types/server-mock.d.ts +171 -59
  54. package/types/server.d.ts +209 -37
  55. package/types-cjs/client.d.cts +149 -6
  56. package/types-cjs/cookies.d.cts +93 -0
  57. package/types-cjs/core.d.cts +4 -2
  58. package/types-cjs/frames/client.d.cts +15 -1
  59. package/types-cjs/frames/frame-client.d.cts +63 -7
  60. package/types-cjs/frames/frame-sink.d.cts +26 -3
  61. package/types-cjs/frames/frame-transport.d.cts +40 -8
  62. package/types-cjs/frames/serializer.d.cts +85 -60
  63. package/types-cjs/frames/server.d.cts +22 -0
  64. package/types-cjs/index.d.cts +2 -3
  65. package/types-cjs/response.d.cts +45 -0
  66. package/types-cjs/serializer-decode.d.cts +182 -0
  67. package/types-cjs/serializer.d.cts +85 -60
  68. package/types-cjs/server-functions/client.d.cts +2 -1
  69. package/types-cjs/server-functions/rich-args.d.cts +10 -0
  70. package/types-cjs/server-functions/server.d.cts +99 -1
  71. package/types-cjs/server-functions/shared.d.cts +79 -1
  72. package/types-cjs/server-mock.d.cts +171 -59
  73. package/types-cjs/server.d.cts +209 -37
@@ -1,63 +1,79 @@
1
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
2
 
5
3
  const ENVELOPE = Symbol.for("solid.ResponseEnvelope");
6
4
  function isResponseEnvelope(value) {
7
5
  return !!(value && typeof value === "object" && value[ENVELOPE]);
8
6
  }
7
+ const SAFE_ERROR = Symbol.for("solid.SafeError");
8
+ function isSafeError(value) {
9
+ return !!(value && (typeof value === "object" || typeof value === "function") && value[SAFE_ERROR]);
10
+ }
9
11
  const REVALIDATE_HEADER = "X-Revalidate";
10
12
 
11
- Feature.AggregateError | Feature.BigIntTypedArray;
12
- const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
13
- const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
14
- CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
15
- FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
16
- function resolveSerializerPlugins(customPlugins) {
17
- return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
18
- }
19
- const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
20
- const JSON_CODEC_DEPTH_LIMIT = 64;
21
- function resolveCodecOptions({
22
- plugins,
23
- disabledFeatures,
24
- depthLimit
25
- } = {}) {
26
- return {
27
- plugins: resolveSerializerPlugins(plugins),
28
- disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
29
- depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
30
- };
13
+ const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
14
+ function getServerFunctionMetadata(fn) {
15
+ if (typeof fn !== "function") return undefined;
16
+ return fn[SERVER_FUNCTION_METADATA] || undefined;
31
17
  }
32
- function serializeJSON(value, {
33
- onParse,
34
- onDone,
35
- onError,
36
- ...codecOptions
37
- }) {
38
- const resolved = resolveCodecOptions(codecOptions);
39
- return toCrossJSONStream(value, {
40
- onParse,
41
- onDone,
42
- onError,
43
- ...resolved,
44
- disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
45
- });
18
+ function isServerFunction(fn) {
19
+ return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
46
20
  }
47
- function createJSONDeserializer(options) {
48
- const refs = new Map();
49
- const resolved = resolveCodecOptions(options);
50
- return function deserializeJSONChunk(node) {
51
- return fromCrossJSON(node, {
52
- refs,
53
- ...resolved
54
- });
55
- };
21
+ function withMeta(fn, meta) {
22
+ const metadata = getServerFunctionMetadata(fn);
23
+ if (!metadata) {
24
+ throw new Error("withMeta expects a server function reference");
25
+ }
26
+ Object.assign(metadata, meta);
27
+ return fn;
28
+ }
29
+ const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
30
+ function provideServerFunctionRPC(rpc) {
31
+ globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
56
32
  }
57
33
 
58
- const RequestContext = Symbol.for("solid.RequestContext");
59
- function getRequestEvent() {
60
- 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;
34
+ function parseCookieHeader(header) {
35
+ const cookies = {};
36
+ if (!header) return cookies;
37
+ for (const part of header.split(";")) {
38
+ const eq = part.indexOf("=");
39
+ if (eq < 0) continue;
40
+ const name = decodeSafe(part.slice(0, eq).trim());
41
+ let value = part.slice(eq + 1).trim();
42
+ if (value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
43
+ value = value.slice(1, -1);
44
+ }
45
+ cookies[name] = decodeSafe(value);
46
+ }
47
+ return cookies;
48
+ }
49
+ function decodeSafe(text) {
50
+ try {
51
+ return decodeURIComponent(text);
52
+ } catch {
53
+ return text;
54
+ }
55
+ }
56
+ function serializeCookie(name, value, options = {}) {
57
+ let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
58
+ cookie += `; Path=${options.path === undefined ? "/" : options.path}`;
59
+ if (options.domain) cookie += `; Domain=${options.domain}`;
60
+ if (options.maxAge !== undefined) cookie += `; Max-Age=${Math.trunc(options.maxAge)}`;
61
+ if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
62
+ if (options.httpOnly) cookie += "; HttpOnly";
63
+ if (options.secure) cookie += "; Secure";
64
+ if (options.sameSite) {
65
+ const sameSite = options.sameSite.toLowerCase();
66
+ cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
67
+ }
68
+ return cookie;
69
+ }
70
+ const FLASH_COOKIE = "flash";
71
+ const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
72
+ function hasFlashCookie(cookieHeader) {
73
+ return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
74
+ }
75
+ function clearFlashCookie() {
76
+ return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
61
77
  }
62
78
 
63
79
  const codecConfig = {
@@ -73,22 +89,6 @@ function subscribeFlightData(consumer) {
73
89
  return () => {
74
90
  };
75
91
  }
76
- const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
77
- function getServerFunctionMetadata(fn) {
78
- if (typeof fn !== "function") return undefined;
79
- return fn[SERVER_FUNCTION_METADATA] || undefined;
80
- }
81
- function isServerFunction(fn) {
82
- return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
83
- }
84
- function withMeta(fn, meta) {
85
- const metadata = getServerFunctionMetadata(fn);
86
- if (!metadata) {
87
- throw new Error("withMeta expects a server function reference");
88
- }
89
- Object.assign(metadata, meta);
90
- return fn;
91
- }
92
92
  const FUNCTION_HEADER = "X-Server-Function-Id";
93
93
  const ERROR_HEADER = "X-Server-Function-Error";
94
94
  const ERROR_HEADER_MARKER = "=?1?";
@@ -119,18 +119,6 @@ const INSTANCE_HEADER = "X-Server-Function-Instance";
119
119
  const BODY_FORMAT_HEADER = "X-Server-Function-Format";
120
120
  const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
121
121
  const FILE_FORM_KEY = "__server_function_file__";
122
- const FLASH_COOKIE = "flash";
123
- const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
124
- function hasFlashCookie(cookieHeader) {
125
- return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
126
- }
127
- function matchFlashCookie(cookieHeader) {
128
- const match = cookieHeader && cookieHeader.match(FLASH_MATCHER);
129
- return match ? match[1] : undefined;
130
- }
131
- function clearFlashCookie() {
132
- return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
133
- }
134
122
  const BodyFormat = {
135
123
  Serialized: "0",
136
124
  String: "1",
@@ -142,6 +130,38 @@ const BodyFormat = {
142
130
  Uint8Array: "7",
143
131
  Json: "8"
144
132
  };
133
+ const JSON_SAFE_DEPTH_LIMIT = 10000;
134
+ const EXIT = {};
135
+ function isJSONSafe(value) {
136
+ const stack = [value];
137
+ const ancestors = new Set();
138
+ while (stack.length) {
139
+ const v = stack.pop();
140
+ if (v === EXIT) {
141
+ ancestors.delete(stack.pop());
142
+ continue;
143
+ }
144
+ if (v === null) continue;
145
+ const t = typeof v;
146
+ if (t === "string" || t === "boolean") continue;
147
+ if (t === "number") {
148
+ if (!Number.isFinite(v)) return false;
149
+ continue;
150
+ }
151
+ if (t !== "object") return false;
152
+ if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
153
+ ancestors.add(v);
154
+ stack.push(v, EXIT);
155
+ if (Array.isArray(v)) {
156
+ for (let i = 0; i < v.length; i++) stack.push(v[i]);
157
+ } else {
158
+ const proto = Object.getPrototypeOf(v);
159
+ if (proto !== Object.prototype && proto !== null) return false;
160
+ for (const k in v) stack.push(v[k]);
161
+ }
162
+ }
163
+ return true;
164
+ }
145
165
  function getHeadersAndBody(body) {
146
166
  switch (true) {
147
167
  case typeof body === "string":
@@ -235,13 +255,11 @@ async function extractBody(source, codecOptions) {
235
255
  return undefined;
236
256
  }
237
257
  function createChunk(data) {
238
- const encodeData = new TextEncoder().encode(data);
258
+ const encoder = new TextEncoder();
259
+ const encodeData = encoder.encode(data);
239
260
  const bytes = encodeData.length;
240
- const baseHex = bytes.toString(16);
241
- const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
242
- const head = new TextEncoder().encode(`;0x${totalHex};`);
243
261
  const chunk = new Uint8Array(12 + bytes);
244
- chunk.set(head);
262
+ chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
245
263
  chunk.set(encodeData, 12);
246
264
  return chunk;
247
265
  }
@@ -273,8 +291,8 @@ class ChunkReader {
273
291
  }
274
292
  await this.readChunk();
275
293
  }
276
- const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
277
- const bytes = Number.parseInt(head, 16);
294
+ const decoder = new TextDecoder();
295
+ const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
278
296
  if (Number.isNaN(bytes)) {
279
297
  throw new Error("Malformed server function stream.");
280
298
  }
@@ -284,7 +302,7 @@ class ChunkReader {
284
302
  }
285
303
  await this.readChunk();
286
304
  }
287
- const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
305
+ const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
288
306
  this.buffer = this.buffer.subarray(12 + bytes);
289
307
  return {
290
308
  done: false,
@@ -303,7 +321,10 @@ class ChunkReader {
303
321
  }
304
322
  function serializeStream(value, codecOptions) {
305
323
  return new ReadableStream({
306
- start(controller) {
324
+ async start(controller) {
325
+ const {
326
+ serializeJSON
327
+ } = await import('@solidjs/web/serialization');
307
328
  serializeJSON(value, {
308
329
  ...codecOptions,
309
330
  onParse(node) {
@@ -326,6 +347,9 @@ async function deserializeStream(source, codecOptions) {
326
347
  const reader = new ChunkReader(source.body);
327
348
  const result = await reader.next();
328
349
  if (!result.done) {
350
+ const {
351
+ createJSONDeserializer
352
+ } = await import('@solidjs/web/serialization/decode');
329
353
  const deserializeChunk = createJSONDeserializer(codecOptions);
330
354
  function interpretChunk(chunk) {
331
355
  return deserializeChunk(JSON.parse(chunk));
@@ -355,6 +379,77 @@ async function decodeResponsePayload(response, codecOptions) {
355
379
  };
356
380
  }
357
381
 
382
+ const RequestContext = Symbol.for("solid.RequestContext");
383
+ function getRequestEvent() {
384
+ 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;
385
+ }
386
+ function reportLostHeaderWrite(method, name) {
387
+ const message = `Response header write dropped: headers.${method}(${JSON.stringify(String(name))}) ` + "ran after the response head was sent. Write headers before the shell flushes " + "(or before the handler returns).";
388
+ console.error(message);
389
+ }
390
+ function commitResponseStub(stub, {
391
+ allowLateLocation = false
392
+ } = {}) {
393
+ if (!stub || stub.committed) return stub;
394
+ stub.committed = true;
395
+ const headers = stub.headers;
396
+ if (!headers || typeof headers.set !== "function") return stub;
397
+ for (const method of ["set", "append", "delete"]) {
398
+ const original = headers[method].bind(headers);
399
+ headers[method] = function (name, ...rest) {
400
+ if (allowLateLocation && method === "set" && String(name).toLowerCase() === "location") {
401
+ return original(name, ...rest);
402
+ }
403
+ reportLostHeaderWrite(method, name);
404
+ };
405
+ }
406
+ return stub;
407
+ }
408
+ function copyInitHeaders(init) {
409
+ if (!init || !init.getSetCookie) return new Headers(init);
410
+ const headers = new Headers();
411
+ init.forEach((value, key) => {
412
+ if (key !== "set-cookie") headers.append(key, value);
413
+ });
414
+ for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
415
+ return headers;
416
+ }
417
+ const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, "Location"].map(header => header.toLowerCase()));
418
+ function fillsStubGap(key, headers, response) {
419
+ if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
420
+ if (response.body === null && (key === "content-type" || key === "content-length")) return false;
421
+ return !headers.has(key);
422
+ }
423
+ function commitEventResponse(response, event = getRequestEvent()) {
424
+ const stub = event && event.response;
425
+ if (!stub || !stub.headers || stub.committed) return response;
426
+ const cookies = stub.headers.getSetCookie ? stub.headers.getSetCookie() : [];
427
+ commitResponseStub(stub);
428
+ let hasGaps = false;
429
+ stub.headers.forEach((value, key) => {
430
+ if (fillsStubGap(key, response.headers, response)) hasGaps = true;
431
+ });
432
+ if (!cookies.length && !hasGaps) return response;
433
+ try {
434
+ for (const cookie of cookies) response.headers.append("Set-Cookie", cookie);
435
+ stub.headers.forEach((value, key) => {
436
+ if (fillsStubGap(key, response.headers, response)) response.headers.set(key, value);
437
+ });
438
+ return response;
439
+ } catch {
440
+ const headers = copyInitHeaders(response.headers);
441
+ for (const cookie of cookies) headers.append("Set-Cookie", cookie);
442
+ stub.headers.forEach((value, key) => {
443
+ if (fillsStubGap(key, headers, response)) headers.set(key, value);
444
+ });
445
+ return new Response(response.body, {
446
+ status: response.status,
447
+ statusText: response.statusText,
448
+ headers
449
+ });
450
+ }
451
+ }
452
+
358
453
  function encodeInputValue(value) {
359
454
  if (value instanceof FormData) return {
360
455
  $f: [...value.entries()].filter(([, v]) => typeof v === "string")
@@ -384,13 +479,16 @@ function encodeFlashCookie(url, result, input, thrown) {
384
479
  thrown: !!thrown,
385
480
  input: input.map(encodeInputValue)
386
481
  };
387
- return `${FLASH_COOKIE}=${encodeURIComponent(JSON.stringify(payload))}; Secure; HttpOnly; Path=/`;
482
+ return serializeCookie(FLASH_COOKIE, JSON.stringify(payload), {
483
+ secure: true,
484
+ httpOnly: true
485
+ });
388
486
  }
389
487
  function decodeFlashCookie(cookieHeader) {
390
- const match = matchFlashCookie(cookieHeader);
488
+ const match = parseCookieHeader(cookieHeader)[FLASH_COOKIE];
391
489
  if (!match) return;
392
490
  try {
393
- const payload = JSON.parse(decodeURIComponent(match));
491
+ const payload = JSON.parse(match);
394
492
  if (!payload || !payload.result) return;
395
493
  const result = payload.error ? new Error(payload.result) : payload.result;
396
494
  return {
@@ -406,6 +504,7 @@ function decodeFlashCookie(cookieHeader) {
406
504
 
407
505
  const config = {
408
506
  provideEvent: undefined,
507
+ wrapInvocation: undefined,
409
508
  collectFlightData: undefined,
410
509
  transformResult: undefined,
411
510
  transformFlightResult: undefined,
@@ -415,6 +514,7 @@ const config = {
415
514
  };
416
515
  function configureServerFunctionsServer({
417
516
  provideEvent,
517
+ wrapInvocation,
418
518
  collectFlightData,
419
519
  transformResult,
420
520
  transformFlightResult,
@@ -424,6 +524,7 @@ function configureServerFunctionsServer({
424
524
  codec
425
525
  } = {}) {
426
526
  if (provideEvent !== undefined) config.provideEvent = provideEvent;
527
+ if (wrapInvocation !== undefined) config.wrapInvocation = wrapInvocation;
427
528
  if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
428
529
  if (transformResult !== undefined) config.transformResult = transformResult;
429
530
  if (transformFlightResult !== undefined) config.transformFlightResult = transformFlightResult;
@@ -441,7 +542,17 @@ function provideEvent(event, fn) {
441
542
  const REGISTRATIONS = new Map();
442
543
  const METHODS = new Map();
443
544
  const INVOCATIONS = new WeakMap();
545
+ let rpcProvided = false;
546
+ function provideRPC() {
547
+ if (rpcProvided) return;
548
+ rpcProvided = true;
549
+ provideServerFunctionRPC({
550
+ GET,
551
+ decodeResponse
552
+ });
553
+ }
444
554
  function registerServerFunction(id, callback) {
555
+ provideRPC();
445
556
  REGISTRATIONS.set(id, callback);
446
557
  return callback;
447
558
  }
@@ -466,6 +577,7 @@ function createServerReference({
466
577
  name
467
578
  }) {
468
579
  if (typeof fn !== "function") throw new Error("Export from a 'use server' module must be a function");
580
+ provideRPC();
469
581
  const metadata = name === undefined ? {} : {
470
582
  name
471
583
  };
@@ -489,7 +601,13 @@ function createServerReference({
489
601
  });
490
602
  evt.serverOnly = true;
491
603
  const result = provideEvent(evt, () => {
492
- return fn.apply(thisArg, args);
604
+ const run = () => fn.apply(thisArg, args);
605
+ return config.wrapInvocation ? config.wrapInvocation(run, {
606
+ id,
607
+ args,
608
+ event: evt,
609
+ direct: true
610
+ }) : run();
493
611
  });
494
612
  const transform = config.transformDirectResult;
495
613
  if (transform && result && typeof result.then === "function") {
@@ -571,7 +689,9 @@ async function foldFlightData(hook, event, headers, outcome, context = {}) {
571
689
  return transformed;
572
690
  }
573
691
  }
574
- return {
692
+ return outcome.value === undefined ? {
693
+ data
694
+ } : {
575
695
  value: outcome.value,
576
696
  data
577
697
  };
@@ -631,6 +751,16 @@ function foldSetCookies(headers, setCookies) {
631
751
  if (serialized) folded.set("cookie", serialized);
632
752
  return folded;
633
753
  }
754
+ function mergeResponseHeaders(target, source) {
755
+ source.forEach((value, key) => {
756
+ if (key !== "set-cookie") target.append(key, value);
757
+ });
758
+ if (source.getSetCookie) {
759
+ for (const cookie of source.getSetCookie()) target.append("Set-Cookie", cookie);
760
+ } else if (source.has("set-cookie")) {
761
+ target.append("Set-Cookie", source.get("set-cookie"));
762
+ }
763
+ }
634
764
  const validRedirectStatuses = new Set([301, 302, 303, 307, 308]);
635
765
  function createNoJSHandler({
636
766
  base = ""
@@ -645,7 +775,8 @@ function createNoJSHandler({
645
775
  let status = 303;
646
776
  let headers;
647
777
  if (result instanceof Response) {
648
- headers = new Headers(result.headers);
778
+ headers = new Headers();
779
+ mergeResponseHeaders(headers, result.headers);
649
780
  if (result.headers.has("Location")) {
650
781
  headers.set("Location", new URL(result.headers.get("Location"), url.origin + base).toString());
651
782
  if (validRedirectStatuses.has(result.status)) status = result.status;
@@ -692,19 +823,46 @@ function encodeResult(value, headers, status, codec) {
692
823
  headers
693
824
  });
694
825
  }
826
+ if (value === undefined) {
827
+ return new Response(null, {
828
+ status,
829
+ headers
830
+ });
831
+ }
832
+ try {
833
+ if (isJSONSafe(value)) {
834
+ headers.set(BODY_FORMAT_HEADER, BodyFormat.Json);
835
+ headers.set("Content-Type", "application/json");
836
+ return new Response(JSON.stringify(value), {
837
+ status,
838
+ headers
839
+ });
840
+ }
841
+ } catch {
842
+ }
695
843
  const response = serializedResponse(value, headers, codec);
696
844
  return status === 200 ? response : new Response(response.body, {
697
845
  status,
698
846
  headers
699
847
  });
700
848
  }
849
+ const GENERIC_SERVER_ERROR_MESSAGE = "Internal Server Error";
850
+ let DEV = false === true;
851
+ function setServerFunctionsDev(dev) {
852
+ DEV = !!dev;
853
+ }
854
+ function sanitizeServerError(value) {
855
+ if (DEV) return value;
856
+ if (isSafeError(value)) return value;
857
+ return new Error(GENERIC_SERVER_ERROR_MESSAGE);
858
+ }
701
859
  async function handleServerFunctionRequest(request, options = {}) {
702
860
  const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
703
861
  const url = new URL(request.url);
704
862
  const instance = request.headers.get(INSTANCE_HEADER);
705
863
  const functionId = resolveFunctionId(request, url);
706
864
  if (!functionId) {
707
- return new Response(process.env.NODE_ENV === "development" ? "Server function not found" : null, {
865
+ return new Response(DEV ? "Server function not found" : null, {
708
866
  status: 404
709
867
  });
710
868
  }
@@ -712,12 +870,12 @@ async function handleServerFunctionRequest(request, options = {}) {
712
870
  try {
713
871
  serverFunction = getServerFunction(functionId);
714
872
  } catch {
715
- return new Response(process.env.NODE_ENV === "development" ? `Unknown server function: ${functionId}` : null, {
873
+ return new Response(DEV ? `Unknown server function: ${functionId}` : null, {
716
874
  status: 404
717
875
  });
718
876
  }
719
877
  if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
720
- return new Response(process.env.NODE_ENV === "development" ? `Method not allowed for server function: ${functionId}` : null, {
878
+ return new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
721
879
  status: 405,
722
880
  headers: {
723
881
  Allow: "POST"
@@ -731,6 +889,7 @@ async function handleServerFunctionRequest(request, options = {}) {
731
889
  const provide = options.provideEvent || provideEvent;
732
890
  const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
733
891
  const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
892
+ const wrapInvocation = options.wrapInvocation !== undefined ? options.wrapInvocation : config.wrapInvocation;
734
893
  const transformFlightResult = options.transformFlightResult !== undefined ? options.transformFlightResult : config.transformFlightResult;
735
894
  const handleNoJS = options.handleNoJS !== undefined ? options.handleNoJS : config.handleNoJS !== undefined ? config.handleNoJS : isFormPost(request) ? defaultNoJSHandler || (defaultNoJSHandler = createNoJSHandler()) : undefined;
736
895
  const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
@@ -745,131 +904,142 @@ async function handleServerFunctionRequest(request, options = {}) {
745
904
  transformFlightResult
746
905
  };
747
906
  const headers = new Headers();
748
- try {
749
- let result = await provide(event, async () => {
750
- INVOCATIONS.set(event, {
751
- id: functionId
907
+ const dispatch = async () => {
908
+ try {
909
+ let result = await provide(event, async () => {
910
+ INVOCATIONS.set(event, {
911
+ id: functionId
912
+ });
913
+ const run = () => serverFunction(...parsed);
914
+ return wrapInvocation ? wrapInvocation(run, {
915
+ id: functionId,
916
+ args: parsed,
917
+ event,
918
+ request,
919
+ direct: false
920
+ }) : run();
752
921
  });
753
- return serverFunction(...parsed);
754
- });
755
- if (transformResult) {
756
- result = await transformResult(event, result, flightContext);
757
- }
758
- let status = 200;
759
- let metadata;
760
- if (isResponseEnvelope(result)) {
761
- const {
762
- response,
763
- value
764
- } = result;
765
- if (!instance && !handleNoJS && response && response.body) {
766
- return response;
767
- }
768
- if (response && response.headers) {
769
- response.headers.forEach((val, key) => headers.append(key, val));
770
- }
771
- if (response && response.status && (response.status < 300 || response.status >= 400)) {
772
- status = response.status;
773
- }
774
- metadata = response;
775
- result = value;
776
- } else if (result instanceof Response) {
777
- if (result.headers && result.headers.has("X-Content-Raw")) return result;
778
- if (instance) {
779
- if (result.headers) {
780
- result.headers.forEach((value, key) => headers.append(key, value));
781
- }
782
- if (result.status && (result.status < 300 || result.status >= 400)) {
783
- status = result.status;
784
- }
785
- metadata = result;
786
- if (result.body == null) {
787
- result = null;
788
- }
789
- }
790
- }
791
- if (collectsFlight) {
792
- result = await foldFlightData(flightHook, event, headers, {
793
- id: functionId,
794
- value: result,
795
- response: metadata,
796
- request,
797
- thrown: false
798
- }, flightContext);
799
- if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
800
- }
801
- if (!instance) {
802
- if (handleNoJS) return handleNoJS(result, request, parsed);
803
- if (result instanceof Response) return result;
804
- return encodeResult(result, headers, 200, codec);
805
- }
806
- return encodeResult(result, headers, status, codec);
807
- } catch (x) {
808
- if (x instanceof Response || isResponseEnvelope(x)) {
809
922
  if (transformResult) {
810
- x = await transformResult(event, x, {
811
- ...flightContext,
812
- thrown: true
813
- });
923
+ result = await transformResult(event, result, flightContext);
814
924
  }
815
925
  let status = 200;
816
926
  let metadata;
817
- if (isResponseEnvelope(x)) {
927
+ if (isResponseEnvelope(result)) {
818
928
  const {
819
929
  response,
820
930
  value
821
- } = x;
931
+ } = result;
932
+ if (!instance && !handleNoJS && response && response.body) {
933
+ return response;
934
+ }
822
935
  if (response && response.headers) {
823
- response.headers.forEach((val, key) => headers.append(key, val));
936
+ mergeResponseHeaders(headers, response.headers);
824
937
  }
825
- if (response && response.status && (!instance || response.status < 300 || response.status >= 400)) {
938
+ if (response && response.status && (response.status < 300 || response.status >= 400)) {
826
939
  status = response.status;
827
940
  }
828
941
  metadata = response;
829
- x = value;
830
- } else if (x instanceof Response) {
831
- if (x.headers) {
832
- x.headers.forEach((value, key) => headers.append(key, value));
833
- }
834
- if (x.status && (!instance || x.status < 300 || x.status >= 400)) {
835
- status = x.status;
836
- }
837
- metadata = x;
838
- if (x.body == null) {
839
- x = null;
942
+ result = value;
943
+ } else if (result instanceof Response) {
944
+ if (result.headers && result.headers.has("X-Content-Raw")) return result;
945
+ if (instance) {
946
+ if (result.headers) {
947
+ mergeResponseHeaders(headers, result.headers);
948
+ }
949
+ if (result.status && (result.status < 300 || result.status >= 400)) {
950
+ status = result.status;
951
+ }
952
+ metadata = result;
953
+ if (result.body == null) {
954
+ result = null;
955
+ }
840
956
  }
841
957
  }
842
958
  if (collectsFlight) {
843
- x = await foldFlightData(flightHook, event, headers, {
959
+ result = await foldFlightData(flightHook, event, headers, {
844
960
  id: functionId,
845
- value: x,
961
+ value: result,
846
962
  response: metadata,
847
963
  request,
848
- thrown: true
964
+ thrown: false
849
965
  }, flightContext);
850
- if (x instanceof Response && x.headers.has("X-Content-Raw")) {
851
- x.headers.set(ERROR_HEADER, "true");
852
- return x;
966
+ if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
967
+ }
968
+ if (!instance) {
969
+ if (handleNoJS) return handleNoJS(result, request, parsed);
970
+ if (result instanceof Response) return result;
971
+ return encodeResult(result, headers, 200, codec);
972
+ }
973
+ return encodeResult(result, headers, status, codec);
974
+ } catch (x) {
975
+ if (x instanceof Response || isResponseEnvelope(x)) {
976
+ if (transformResult) {
977
+ x = await transformResult(event, x, {
978
+ ...flightContext,
979
+ thrown: true
980
+ });
981
+ }
982
+ let status = 200;
983
+ let metadata;
984
+ if (isResponseEnvelope(x)) {
985
+ const {
986
+ response,
987
+ value
988
+ } = x;
989
+ if (response && response.headers) {
990
+ mergeResponseHeaders(headers, response.headers);
991
+ }
992
+ if (response && response.status && (!instance || response.status < 300 || response.status >= 400)) {
993
+ status = response.status;
994
+ }
995
+ metadata = response;
996
+ x = value;
997
+ } else if (x instanceof Response) {
998
+ if (x.headers) {
999
+ mergeResponseHeaders(headers, x.headers);
1000
+ }
1001
+ if (x.status && (!instance || x.status < 300 || x.status >= 400)) {
1002
+ status = x.status;
1003
+ }
1004
+ metadata = x;
1005
+ if (x.body == null) {
1006
+ x = null;
1007
+ }
1008
+ }
1009
+ if (collectsFlight) {
1010
+ x = await foldFlightData(flightHook, event, headers, {
1011
+ id: functionId,
1012
+ value: x,
1013
+ response: metadata,
1014
+ request,
1015
+ thrown: true
1016
+ }, flightContext);
1017
+ if (x instanceof Response && x.headers.has("X-Content-Raw")) {
1018
+ x.headers.set(ERROR_HEADER, "true");
1019
+ return x;
1020
+ }
853
1021
  }
1022
+ headers.set(ERROR_HEADER, "true");
1023
+ if (!instance) {
1024
+ if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
1025
+ if (x instanceof Response) return x;
1026
+ }
1027
+ return encodeResult(x, headers, status, codec);
854
1028
  }
855
- headers.set(ERROR_HEADER, "true");
1029
+ const safe = sanitizeServerError(x);
856
1030
  if (!instance) {
857
- if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
858
- if (x instanceof Response) return x;
1031
+ if (handleNoJS) return handleNoJS(safe, request, parsed, true);
1032
+ const message = safe instanceof Error ? safe.message : String(safe);
1033
+ return new Response(DEV ? message : null, {
1034
+ status: 500
1035
+ });
859
1036
  }
860
- return encodeResult(x, headers, status, codec);
1037
+ const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
1038
+ headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
1039
+ return encodeResult(safe, headers, 200, codec);
861
1040
  }
862
- if (!instance) {
863
- if (handleNoJS) return handleNoJS(x, request, parsed, true);
864
- const message = x instanceof Error ? x.message : String(x);
865
- return new Response(process.env.NODE_ENV === "development" ? message : null, {
866
- status: 500
867
- });
868
- }
869
- const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
870
- headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
871
- return encodeResult(x, headers, 200, codec);
872
- }
1041
+ };
1042
+ return commitEventResponse(await dispatch(), event);
873
1043
  }
874
1044
 
875
- export { ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, decodeResponsePayload, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getEventServerFunctionInvocation, getServerFunction, getServerFunctionInvocation, getServerFunctionMetadata, handleServerFunctionRequest, hasFlashCookie, isServerFunction, registerServerFunction, registerServerReference, subscribeFlightData, withMeta };
1045
+ export { ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GENERIC_SERVER_ERROR_MESSAGE, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, decodeResponsePayload, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getEventServerFunctionInvocation, getServerFunction, getServerFunctionInvocation, getServerFunctionMetadata, handleServerFunctionRequest, hasFlashCookie, isServerFunction, registerServerFunction, registerServerReference, sanitizeServerError, setServerFunctionsDev, subscribeFlightData, withMeta };