@solidjs/web 2.0.0-rc.5 → 2.0.0-rc.6

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.
@@ -2,6 +2,16 @@
2
2
 
3
3
  var solidJs = require('solid-js');
4
4
 
5
+ const COMPOSED_BODY_FRAMING = /*#__PURE__*/new Set(["content-length", "content-encoding", "transfer-encoding"]);
6
+ function isHttpNavigationTarget(target) {
7
+ try {
8
+ const protocol = new URL(target, "http://base.invalid").protocol;
9
+ return protocol === "http:" || protocol === "https:";
10
+ } catch {
11
+ return false;
12
+ }
13
+ }
14
+
5
15
  const ENVELOPE = Symbol.for("solid.ResponseEnvelope");
6
16
  function isResponseEnvelope(value) {
7
17
  return !!(value && typeof value === "object" && value[ENVELOPE]);
@@ -556,7 +566,8 @@ function copyInitHeaders(init) {
556
566
  for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
557
567
  return headers;
558
568
  }
559
- const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, REDIRECT_HEADER, "Location"].map(header => header.toLowerCase()));
569
+ const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, REDIRECT_HEADER, "Location",
570
+ ...COMPOSED_BODY_FRAMING].map(header => header.toLowerCase()));
560
571
  function fillsStubGap(key, headers, response) {
561
572
  if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
562
573
  if (response.body === null && (key === "content-type" || key === "content-length")) return false;
@@ -716,6 +727,52 @@ function provideEvent(event, fn) {
716
727
  if (ctx) return ctx.run(event, fn);
717
728
  throw new Error("No request event provider. Configure one with configureServerFunctionsServer({ provideEvent }).");
718
729
  }
730
+ function scopeDeferredResult(value, scope) {
731
+ if (value === null || typeof value !== "object" && typeof value !== "function") return value;
732
+ const promised = scope(() => nativePromise(value));
733
+ if (promised) {
734
+ return promised.then(result => scope(() => scopeDeferredResult(result, scope)));
735
+ }
736
+ if (typeof ReadableStream !== "undefined" && value instanceof ReadableStream) {
737
+ let reader;
738
+ return new ReadableStream({
739
+ async pull(controller) {
740
+ try {
741
+ const step = await scope(() => {
742
+ if (!reader) reader = value.getReader();
743
+ return reader.read();
744
+ });
745
+ if (step.done) controller.close();else controller.enqueue(step.value);
746
+ } catch (error) {
747
+ controller.error(error);
748
+ }
749
+ },
750
+ cancel(reason) {
751
+ return scope(() => reader ? reader.cancel(reason) : value.cancel(reason));
752
+ }
753
+ }, {
754
+ highWaterMark: 0
755
+ });
756
+ }
757
+ const scopedIterator = symbol => ({
758
+ [symbol]() {
759
+ const iterator = scope(() => value[symbol]());
760
+ return new Proxy(iterator, {
761
+ get(target, property) {
762
+ const member = Reflect.get(target, property, target);
763
+ return typeof member === "function" && (property === "next" || property === "return" || property === "throw") ? (...args) => scope(() => member.apply(target, args)) : member;
764
+ }
765
+ });
766
+ }
767
+ });
768
+ if (typeof value[Symbol.asyncIterator] === "function") {
769
+ return scopedIterator(Symbol.asyncIterator);
770
+ }
771
+ if (typeof value[Symbol.iterator] === "function" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set) && !ArrayBuffer.isView(value)) {
772
+ return scopedIterator(Symbol.iterator);
773
+ }
774
+ return value;
775
+ }
719
776
  const REGISTRATIONS = new Map();
720
777
  const METHODS = new Map();
721
778
  const INVOCATIONS = new WeakMap();
@@ -813,7 +870,8 @@ function createServerReference({
813
870
  id
814
871
  });
815
872
  evt.serverOnly = true;
816
- const result = provideEvent(evt, () => {
873
+ const scope = run => provideEvent(evt, run);
874
+ let result = provideEvent(evt, () => {
817
875
  const run = () => fn.apply(thisArg, args);
818
876
  return config.wrapInvocation ? config.wrapInvocation(run, {
819
877
  id,
@@ -822,19 +880,20 @@ function createServerReference({
822
880
  direct: true
823
881
  }) : run();
824
882
  });
883
+ result = scopeDeferredResult(result, scope);
825
884
  const transform = config.transformDirectResult;
826
885
  if (transform && result && typeof result.then === "function") {
827
- return result.then(value => transform(value, {
886
+ return result.then(value => scopeDeferredResult(transform(value, {
828
887
  id,
829
888
  args,
830
889
  event: evt
831
- }));
890
+ }), scope));
832
891
  }
833
- return transform ? transform(result, {
892
+ return transform ? scopeDeferredResult(transform(result, {
834
893
  id,
835
894
  args,
836
895
  event: evt
837
- }) : result;
896
+ }), scope) : result;
838
897
  }
839
898
  });
840
899
  return proxy;
@@ -900,22 +959,58 @@ function assertDecodeDepth(value) {
900
959
  level = next;
901
960
  }
902
961
  }
962
+ const UNSAFE_ARGUMENT_KEYS = ["__proto__", "constructor", "prototype"];
963
+ function stripUnsafeArgumentKeys(value) {
964
+ const stack = [value];
965
+ const seen = new Set();
966
+ while (stack.length) {
967
+ const v = stack.pop();
968
+ if (v === null || typeof v !== "object" || seen.has(v)) continue;
969
+ seen.add(v);
970
+ for (const key of UNSAFE_ARGUMENT_KEYS) {
971
+ delete v[key];
972
+ }
973
+ for (const key of Object.keys(v)) stack.push(v[key]);
974
+ if (v instanceof Map) {
975
+ for (const [k, entry] of v) stack.push(k, entry);
976
+ } else if (v instanceof Set) {
977
+ for (const member of v) stack.push(member);
978
+ }
979
+ }
980
+ return value;
981
+ }
903
982
  async function bufferBodyWithin(request, limit) {
904
- const reader = request.clone().body.getReader();
983
+ const reader = request.body.getReader();
984
+ const signal = request.signal;
905
985
  const chunks = [];
906
986
  let total = 0;
907
- for (;;) {
908
- const {
909
- done,
910
- value
911
- } = await reader.read();
912
- if (done) break;
913
- total += value.byteLength;
914
- if (total > limit) {
915
- reader.cancel().catch(() => {});
916
- return null;
987
+ const onAbort = () => {
988
+ reader.cancel(signal.reason).catch(() => {});
989
+ };
990
+ if (signal.aborted) onAbort();else signal.addEventListener("abort", onAbort, {
991
+ once: true
992
+ });
993
+ try {
994
+ for (;;) {
995
+ const {
996
+ done,
997
+ value
998
+ } = await reader.read();
999
+ if (signal.aborted) throw signal.reason;
1000
+ if (done) break;
1001
+ total += value.byteLength;
1002
+ if (total > limit) {
1003
+ reader.cancel().catch(() => {});
1004
+ return null;
1005
+ }
1006
+ chunks.push(value);
917
1007
  }
918
- chunks.push(value);
1008
+ } catch (error) {
1009
+ reader.cancel(error).catch(() => {});
1010
+ throw error;
1011
+ } finally {
1012
+ signal.removeEventListener("abort", onAbort);
1013
+ reader.releaseLock();
919
1014
  }
920
1015
  const body = new Uint8Array(total);
921
1016
  let offset = 0;
@@ -942,6 +1037,7 @@ async function parseArguments(request, url, scripted, codec) {
942
1037
  if (!Array.isArray(result)) {
943
1038
  throw new TypeError("Server function arguments must encode an array");
944
1039
  }
1040
+ stripUnsafeArgumentKeys(result);
945
1041
  for (const arg of result) {
946
1042
  parsed.push(arg);
947
1043
  }
@@ -955,9 +1051,12 @@ async function parseArguments(request, url, scripted, codec) {
955
1051
  if (!Array.isArray(decoded)) {
956
1052
  throw new TypeError("Server function arguments must encode an array");
957
1053
  }
958
- return decoded;
1054
+ return stripUnsafeArgumentKeys(decoded);
959
1055
  }
960
1056
  if (decoded === undefined) {
1057
+ if (bodyFormat === null && (await request.clone().arrayBuffer()).byteLength === 0) {
1058
+ return parsed;
1059
+ }
961
1060
  throw new TypeError("Server function body carries no usable encoding");
962
1061
  }
963
1062
  parsed.push(decoded);
@@ -1058,7 +1157,7 @@ function foldSetCookies(headers, setCookies) {
1058
1157
  }
1059
1158
  function mergeResponseHeaders(target, source) {
1060
1159
  source.forEach((value, key) => {
1061
- if (key !== "set-cookie") target.append(key, value);
1160
+ if (key !== "set-cookie" && !COMPOSED_BODY_FRAMING.has(key)) target.append(key, value);
1062
1161
  });
1063
1162
  if (source.getSetCookie) {
1064
1163
  for (const cookie of source.getSetCookie()) target.append("Set-Cookie", cookie);
@@ -1075,10 +1174,6 @@ function maskRedirect(headers, response, requestUrl) {
1075
1174
  headers.delete("Location");
1076
1175
  }
1077
1176
  const BOUNDED_COMPOSED_HEADERS = [REDIRECT_HEADER, "Location", REVALIDATE_HEADER];
1078
- function refusedTargetScheme(target) {
1079
- const match = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.exec(target);
1080
- return match !== null && !/^https?:$/i.test(match[0]);
1081
- }
1082
1177
  function enforceComposedHeaderInvariants(response) {
1083
1178
  for (const name of BOUNDED_COMPOSED_HEADERS) {
1084
1179
  const value = response.headers.get(name);
@@ -1088,7 +1183,7 @@ function enforceComposedHeaderInvariants(response) {
1088
1183
  }
1089
1184
  if (name === REVALIDATE_HEADER) continue;
1090
1185
  const target = name === REDIRECT_HEADER ? value.slice(value.indexOf(" ") + 1) : value;
1091
- if (refusedTargetScheme(target)) {
1186
+ if (!isHttpNavigationTarget(target)) {
1092
1187
  return refuseComposedHeader(response, name, `${name} response header refused: non-http(s) navigation target`, DEV ? `The ${name} response header carries a navigation target with a non-http(s) ` + `scheme ("${target.slice(0, 64)}"). A javascript: target is same-origin script ` + `execution in any integration that navigates to it, so only http(s) and ` + `relative targets leave this transport. If the target came from request data ` + `(?next= and friends), validate it against your own origin before redirecting.` : null);
1093
1188
  }
1094
1189
  }
@@ -1216,14 +1311,11 @@ function guardFailures(value, state) {
1216
1311
  top.next.add(guarded);
1217
1312
  if (guarded !== original) top.changed = true;
1218
1313
  } else if (guarded !== original || top.accessorRead === i) {
1219
- Object.defineProperty(top.next, items[i], top.accessorRead === i ? {
1220
- enumerable: true,
1221
- configurable: true,
1314
+ Object.defineProperty(top.next, items[i], {
1315
+ value: guarded,
1222
1316
  writable: true,
1223
- value: guarded
1224
- } : {
1225
- ...top.descriptors[items[i]],
1226
- value: guarded
1317
+ configurable: true,
1318
+ enumerable: top.descriptors[items[i]].enumerable
1227
1319
  });
1228
1320
  top.changed = true;
1229
1321
  }
@@ -1258,6 +1350,9 @@ class Frame {
1258
1350
  this.pendingKey = undefined;
1259
1351
  }
1260
1352
  }
1353
+ function guardOperation(state, run) {
1354
+ return state.scope ? state.scope(run) : run();
1355
+ }
1261
1356
  function enterGuard(value, state) {
1262
1357
  if (value === null || typeof value !== "object") return value;
1263
1358
  if (state.seen.has(value)) {
@@ -1272,7 +1367,7 @@ function enterGuard(value, state) {
1272
1367
  if (finished) return;
1273
1368
  finished = true;
1274
1369
  try {
1275
- const cancelled = reader ? reader.cancel() : value.cancel();
1370
+ const cancelled = guardOperation(state, () => reader ? reader.cancel() : value.cancel());
1276
1371
  if (cancelled && typeof cancelled.then === "function") cancelled.then(undefined, () => {});
1277
1372
  } catch {}
1278
1373
  };
@@ -1284,19 +1379,19 @@ function enterGuard(value, state) {
1284
1379
  controller.close();
1285
1380
  return;
1286
1381
  }
1287
- if (!reader) reader = value.getReader();
1382
+ if (!reader) reader = guardOperation(state, () => value.getReader());
1288
1383
  const {
1289
1384
  done,
1290
1385
  value: chunk
1291
- } = await reader.read();
1292
- done ? controller.close() : controller.enqueue(guardFailures(chunk, state));
1386
+ } = await guardOperation(state, () => reader.read());
1387
+ done ? controller.close() : controller.enqueue(guardOperation(state, () => guardFailures(chunk, state)));
1293
1388
  } catch (error) {
1294
- controller.error(sanitizeServerError(error));
1389
+ controller.error(guardOperation(state, () => sanitizeServerError(error)));
1295
1390
  }
1296
1391
  },
1297
1392
  cancel(reason) {
1298
1393
  finished = true;
1299
- return reader ? reader.cancel(reason) : value.cancel(reason);
1394
+ return guardOperation(state, () => reader ? reader.cancel(reason) : value.cancel(reason));
1300
1395
  }
1301
1396
  });
1302
1397
  if (gate) gate.onOpen(close);
@@ -1304,9 +1399,10 @@ function enterGuard(value, state) {
1304
1399
  return guardedStream;
1305
1400
  }
1306
1401
  if (typeof value.then === "function") {
1307
- const guardedPromise = Promise.resolve(value).then(resolved => guardFailures(resolved, state), error => {
1308
- throw sanitizeServerError(error);
1402
+ const guardedPromise = Promise.resolve(value).then(resolved => guardOperation(state, () => guardFailures(resolved, state)), error => {
1403
+ throw guardOperation(state, () => sanitizeServerError(error));
1309
1404
  });
1405
+ guardedPromise.catch(() => {});
1310
1406
  state.seen.set(value, guardedPromise);
1311
1407
  return guardedPromise;
1312
1408
  }
@@ -1315,13 +1411,13 @@ function enterGuard(value, state) {
1315
1411
  const gate = state.gate;
1316
1412
  const guardedIterable = {
1317
1413
  [Symbol.asyncIterator]() {
1318
- const iterator = source[Symbol.asyncIterator]();
1414
+ const iterator = guardOperation(state, () => source[Symbol.asyncIterator]());
1319
1415
  let finished = false;
1320
1416
  const close = () => {
1321
1417
  if (finished) return;
1322
1418
  finished = true;
1323
1419
  try {
1324
- const returned = iterator.return && iterator.return();
1420
+ const returned = iterator.return && guardOperation(state, () => iterator.return());
1325
1421
  if (returned && typeof returned.then === "function") returned.then(undefined, () => {});
1326
1422
  } catch {}
1327
1423
  };
@@ -1329,17 +1425,17 @@ function enterGuard(value, state) {
1329
1425
  const step = () => finished ? Promise.resolve({
1330
1426
  done: true,
1331
1427
  value: undefined
1332
- }) : iterator.next().then(step => {
1428
+ }) : guardOperation(state, () => iterator.next()).then(step => {
1333
1429
  if (step.done) {
1334
1430
  finished = true;
1335
1431
  return step;
1336
1432
  }
1337
1433
  return {
1338
1434
  done: false,
1339
- value: guardFailures(step.value, state)
1435
+ value: guardOperation(state, () => guardFailures(step.value, state))
1340
1436
  };
1341
1437
  }, error => {
1342
- throw sanitizeServerError(error);
1438
+ throw guardOperation(state, () => sanitizeServerError(error));
1343
1439
  });
1344
1440
  return {
1345
1441
  next: () => finished || !gate || gate.wantsMore() ? step() : gate.awaitDemand().then(step),
@@ -1356,6 +1452,11 @@ function enterGuard(value, state) {
1356
1452
  state.seen.set(value, guardedIterable);
1357
1453
  return guardedIterable;
1358
1454
  }
1455
+ if (state.scope && typeof value[Symbol.iterator] === "function" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set) && !ArrayBuffer.isView(value)) {
1456
+ const scopedIterable = scopeDeferredResult(value, state.scope);
1457
+ state.seen.set(value, scopedIterable);
1458
+ return scopedIterable;
1459
+ }
1359
1460
  if (Array.isArray(value)) {
1360
1461
  const next = value.slice();
1361
1462
  state.seen.set(value, next);
@@ -1379,16 +1480,20 @@ function enterGuard(value, state) {
1379
1480
  return value;
1380
1481
  }
1381
1482
  const descriptors = Object.getOwnPropertyDescriptors(value);
1483
+ for (const key of Object.keys(descriptors)) {
1484
+ descriptors[key].configurable = true;
1485
+ if ("value" in descriptors[key]) descriptors[key].writable = true;
1486
+ }
1382
1487
  const next = Object.create(prototype, descriptors);
1383
1488
  state.seen.set(value, next);
1384
- return new Frame(OBJECT, value, next, Object.keys(descriptors), descriptors);
1489
+ return new Frame(OBJECT, value, next, Object.keys(value), descriptors);
1385
1490
  }
1386
1491
  function keepGuarded(value, next, changed, state) {
1387
1492
  if (changed || state.cyclic.has(value)) return next;
1388
1493
  state.seen.set(value, value);
1389
1494
  return value;
1390
1495
  }
1391
- function serializeResponseStream(value, codecOptions, signal) {
1496
+ function serializeResponseStream(value, codecOptions, signal, scope) {
1392
1497
  let closed = false;
1393
1498
  let streamController = null;
1394
1499
  let demandWaiters = null;
@@ -1407,11 +1512,13 @@ function serializeResponseStream(value, codecOptions, signal) {
1407
1512
  if (closed) close();else sourceClosers.add(close);
1408
1513
  }
1409
1514
  };
1410
- value = guardFailures(value, {
1515
+ const guardState = {
1411
1516
  seen: new WeakMap(),
1412
1517
  cyclic: new WeakSet(),
1413
- gate
1414
- });
1518
+ gate,
1519
+ scope
1520
+ };
1521
+ value = guardOperation(guardState, () => guardFailures(value, guardState));
1415
1522
  let cancelSerialize = null;
1416
1523
  let onAbort = null;
1417
1524
  const finishSource = () => {
@@ -1492,14 +1599,14 @@ function serializeResponseStream(value, codecOptions, signal) {
1492
1599
  }
1493
1600
  });
1494
1601
  }
1495
- function serializedResponse(value, headers, codec, signal) {
1602
+ function serializedResponse(value, headers, codec, signal, scope) {
1496
1603
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
1497
1604
  headers.set("Content-Type", "text/plain");
1498
- return new Response(serializeResponseStream(value, codec, signal), {
1605
+ return new Response(serializeResponseStream(value, codec, signal, scope), {
1499
1606
  headers
1500
1607
  });
1501
1608
  }
1502
- function encodeResult(value, headers, status, codec, signal) {
1609
+ function encodeResult(value, headers, status, codec, signal, scope) {
1503
1610
  if (NULL_BODY_STATUSES.has(status)) {
1504
1611
  if (value === undefined || value === null) {
1505
1612
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Void);
@@ -1510,7 +1617,7 @@ function encodeResult(value, headers, status, codec, signal) {
1510
1617
  }
1511
1618
  const error = new Error(`Server function answered status ${status}, which forbids a response body, with a value. ` + `Return respond(undefined, { status: ${status} }) for a bodiless answer, or drop the ` + `status to send the value.`);
1512
1619
  headers.set(ERROR_HEADER, encodeErrorHeaderValue(error.message));
1513
- return encodeResult(error, headers, 500, codec, signal);
1620
+ return encodeResult(error, headers, 500, codec, signal, scope);
1514
1621
  }
1515
1622
  const direct = getHeadersAndBody(value);
1516
1623
  if (direct) {
@@ -1530,10 +1637,12 @@ function encodeResult(value, headers, status, codec, signal) {
1530
1637
  });
1531
1638
  }
1532
1639
  try {
1533
- if (isJSONSafe(value)) {
1640
+ const jsonSafe = scope ? scope(() => isJSONSafe(value)) : isJSONSafe(value);
1641
+ if (jsonSafe) {
1534
1642
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Json);
1535
1643
  headers.set("Content-Type", "application/json");
1536
- return new Response(JSON.stringify(value), {
1644
+ const body = scope ? scope(() => JSON.stringify(value)) : JSON.stringify(value);
1645
+ return new Response(body, {
1537
1646
  status,
1538
1647
  headers
1539
1648
  });
@@ -1541,7 +1650,7 @@ function encodeResult(value, headers, status, codec, signal) {
1541
1650
  } catch {
1542
1651
  }
1543
1652
  try {
1544
- const response = serializedResponse(value, headers, codec, signal);
1653
+ const response = serializedResponse(value, headers, codec, signal, scope);
1545
1654
  return status === 200 ? response : new Response(response.body, {
1546
1655
  status,
1547
1656
  headers
@@ -1639,8 +1748,17 @@ function forbiddenResponse() {
1639
1748
  }
1640
1749
  }));
1641
1750
  }
1751
+ function nativePromise(value) {
1752
+ if (value instanceof Promise) return value;
1753
+ try {
1754
+ if (Object.prototype.toString.call(value) === "[object Promise]") return Promise.prototype.then.call(value, value => value);
1755
+ } catch {}
1756
+ }
1642
1757
  async function handleServerFunctionRequest(request, options = {}) {
1643
- const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
1758
+ const codec = {
1759
+ ...(options.codec !== undefined ? options.codec : getServerFunctionsCodec())
1760
+ };
1761
+ codec.serializeErrorStacks ??= DEV;
1644
1762
  const url = new URL(request.url);
1645
1763
  const method = request.method;
1646
1764
  const address = resolveAddress(url);
@@ -1699,7 +1817,15 @@ async function handleServerFunctionRequest(request, options = {}) {
1699
1817
  return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1700
1818
  }
1701
1819
  if (!(declared > 0)) {
1702
- const bounded = await bufferBodyWithin(request, bodySizeLimit);
1820
+ let bounded;
1821
+ try {
1822
+ bounded = await bufferBodyWithin(request, bodySizeLimit);
1823
+ } catch {
1824
+ const response = new Response(DEV ? "Malformed server function arguments" : null, {
1825
+ status: 400
1826
+ });
1827
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1828
+ }
1703
1829
  if (bounded === null) {
1704
1830
  const response = new Response(DEV ? "Server function request body exceeds the configured bodySizeLimit" : null, {
1705
1831
  status: 413
@@ -1709,16 +1835,30 @@ async function handleServerFunctionRequest(request, options = {}) {
1709
1835
  request = bounded;
1710
1836
  }
1711
1837
  }
1712
- let event = options.createEvent ? options.createEvent(request) : {
1713
- request,
1714
- locals: {}
1715
- };
1716
- if (typeof event?.then === "function") event = await event;
1838
+ let event;
1839
+ try {
1840
+ event = options.createEvent ? options.createEvent(request) : {
1841
+ request,
1842
+ locals: {}
1843
+ };
1844
+ const promised = nativePromise(event);
1845
+ if (promised) event = await promised;
1846
+ } catch (error) {
1847
+ const safe = sanitizeServerError(error);
1848
+ const message = safe instanceof Error ? safe.message : String(safe);
1849
+ const headers = new Headers();
1850
+ headers.set(ERROR_HEADER, boundedErrorHeaderValue(message));
1851
+ const response = scripted ? encodeResult(safe, headers, 500, codec, request.signal) : new Response(DEV ? message : null, {
1852
+ status: 500
1853
+ });
1854
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1855
+ }
1717
1856
  const refuseCommitted = raw => {
1718
1857
  const response = commitEventResponse(raw, event);
1719
1858
  return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1720
1859
  };
1721
1860
  const provide = options.provideEvent || provideEvent;
1861
+ const scope = run => provide(event, run);
1722
1862
  const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
1723
1863
  const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
1724
1864
  const wrapInvocation = options.wrapInvocation !== undefined ? options.wrapInvocation : config.wrapInvocation;
@@ -1769,7 +1909,8 @@ async function handleServerFunctionRequest(request, options = {}) {
1769
1909
  const headers = new Headers();
1770
1910
  const dispatch = async () => {
1771
1911
  try {
1772
- let result = await provide(event, async () => {
1912
+ let invocations = 0;
1913
+ const invokeOnce = async () => {
1773
1914
  INVOCATIONS.set(event, {
1774
1915
  id: functionId
1775
1916
  });
@@ -1781,7 +1922,16 @@ async function handleServerFunctionRequest(request, options = {}) {
1781
1922
  request,
1782
1923
  direct: false
1783
1924
  }) : run();
1925
+ };
1926
+ let result = await provide(event, () => {
1927
+ if (++invocations > 1) {
1928
+ throw new Error("provideEvent invoked the server function callback more than once: a second " + "invocation would commit the call's side effects twice. The hook must call " + "fn exactly once and return its result.");
1929
+ }
1930
+ return invokeOnce();
1784
1931
  });
1932
+ if (invocations !== 1) {
1933
+ throw new Error(invocations === 0 ? "provideEvent returned without invoking the server function callback: the call " + "would have answered as a void success without running the function. The hook " + "must call fn exactly once and return its result." : "provideEvent invoked the server function callback more than once: a second " + "invocation would commit the call's side effects twice. The hook must call " + "fn exactly once and return its result.");
1934
+ }
1785
1935
  if (transformResult) {
1786
1936
  result = await transformResult(event, result, flightContext);
1787
1937
  }
@@ -1835,10 +1985,10 @@ async function handleServerFunctionRequest(request, options = {}) {
1835
1985
  if (!scripted) {
1836
1986
  if (handleNoJS) return handleNoJS(result ?? metadata, request, parsed);
1837
1987
  if (result instanceof Response) return result;
1838
- return encodeResult(result, headers, status, codec, request.signal);
1988
+ return encodeResult(result, headers, status, codec, request.signal, scope);
1839
1989
  }
1840
1990
  if (status === 304) warnScripted304(functionId);
1841
- return encodeResult(result, headers, status, codec, request.signal);
1991
+ return encodeResult(result, headers, status, codec, request.signal, scope);
1842
1992
  } catch (x) {
1843
1993
  const respondThrown = value => {
1844
1994
  const safe = sanitizeServerError(value);
@@ -1851,7 +2001,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1851
2001
  }
1852
2002
  const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
1853
2003
  headers.set(ERROR_HEADER, boundedErrorHeaderValue(error));
1854
- return encodeResult(safe, headers, 500, codec, request.signal);
2004
+ return encodeResult(safe, headers, 500, codec, request.signal, scope);
1855
2005
  };
1856
2006
  if (x instanceof Response || isResponseEnvelope(x)) {
1857
2007
  if (transformResult) {
@@ -1915,7 +2065,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1915
2065
  if (x instanceof Response) return x;
1916
2066
  }
1917
2067
  if (scripted && status === 304) warnScripted304(functionId);
1918
- return encodeResult(x, headers, status, codec, request.signal);
2068
+ return encodeResult(x, headers, status, codec, request.signal, scope);
1919
2069
  }
1920
2070
  return respondThrown(x);
1921
2071
  }