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