@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.
@@ -1,5 +1,15 @@
1
1
  import { sharedConfig } from 'solid-js';
2
2
 
3
+ const COMPOSED_BODY_FRAMING = /*#__PURE__*/new Set(["content-length", "content-encoding", "transfer-encoding"]);
4
+ function isHttpNavigationTarget(target) {
5
+ try {
6
+ const protocol = new URL(target, "http://base.invalid").protocol;
7
+ return protocol === "http:" || protocol === "https:";
8
+ } catch {
9
+ return false;
10
+ }
11
+ }
12
+
3
13
  const ENVELOPE = Symbol.for("solid.ResponseEnvelope");
4
14
  function isResponseEnvelope(value) {
5
15
  return !!(value && typeof value === "object" && value[ENVELOPE]);
@@ -554,7 +564,8 @@ function copyInitHeaders(init) {
554
564
  for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
555
565
  return headers;
556
566
  }
557
- 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()));
567
+ const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, REDIRECT_HEADER, "Location",
568
+ ...COMPOSED_BODY_FRAMING].map(header => header.toLowerCase()));
558
569
  function fillsStubGap(key, headers, response) {
559
570
  if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
560
571
  if (response.body === null && (key === "content-type" || key === "content-length")) return false;
@@ -714,6 +725,52 @@ function provideEvent(event, fn) {
714
725
  if (ctx) return ctx.run(event, fn);
715
726
  throw new Error("No request event provider. Configure one with configureServerFunctionsServer({ provideEvent }).");
716
727
  }
728
+ function scopeDeferredResult(value, scope) {
729
+ if (value === null || typeof value !== "object" && typeof value !== "function") return value;
730
+ const promised = scope(() => nativePromise(value));
731
+ if (promised) {
732
+ return promised.then(result => scope(() => scopeDeferredResult(result, scope)));
733
+ }
734
+ if (typeof ReadableStream !== "undefined" && value instanceof ReadableStream) {
735
+ let reader;
736
+ return new ReadableStream({
737
+ async pull(controller) {
738
+ try {
739
+ const step = await scope(() => {
740
+ if (!reader) reader = value.getReader();
741
+ return reader.read();
742
+ });
743
+ if (step.done) controller.close();else controller.enqueue(step.value);
744
+ } catch (error) {
745
+ controller.error(error);
746
+ }
747
+ },
748
+ cancel(reason) {
749
+ return scope(() => reader ? reader.cancel(reason) : value.cancel(reason));
750
+ }
751
+ }, {
752
+ highWaterMark: 0
753
+ });
754
+ }
755
+ const scopedIterator = symbol => ({
756
+ [symbol]() {
757
+ const iterator = scope(() => value[symbol]());
758
+ return new Proxy(iterator, {
759
+ get(target, property) {
760
+ const member = Reflect.get(target, property, target);
761
+ return typeof member === "function" && (property === "next" || property === "return" || property === "throw") ? (...args) => scope(() => member.apply(target, args)) : member;
762
+ }
763
+ });
764
+ }
765
+ });
766
+ if (typeof value[Symbol.asyncIterator] === "function") {
767
+ return scopedIterator(Symbol.asyncIterator);
768
+ }
769
+ if (typeof value[Symbol.iterator] === "function" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set) && !ArrayBuffer.isView(value)) {
770
+ return scopedIterator(Symbol.iterator);
771
+ }
772
+ return value;
773
+ }
717
774
  const REGISTRATIONS = new Map();
718
775
  const METHODS = new Map();
719
776
  const INVOCATIONS = new WeakMap();
@@ -811,7 +868,8 @@ function createServerReference({
811
868
  id
812
869
  });
813
870
  evt.serverOnly = true;
814
- const result = provideEvent(evt, () => {
871
+ const scope = run => provideEvent(evt, run);
872
+ let result = provideEvent(evt, () => {
815
873
  const run = () => fn.apply(thisArg, args);
816
874
  return config.wrapInvocation ? config.wrapInvocation(run, {
817
875
  id,
@@ -820,19 +878,20 @@ function createServerReference({
820
878
  direct: true
821
879
  }) : run();
822
880
  });
881
+ result = scopeDeferredResult(result, scope);
823
882
  const transform = config.transformDirectResult;
824
883
  if (transform && result && typeof result.then === "function") {
825
- return result.then(value => transform(value, {
884
+ return result.then(value => scopeDeferredResult(transform(value, {
826
885
  id,
827
886
  args,
828
887
  event: evt
829
- }));
888
+ }), scope));
830
889
  }
831
- return transform ? transform(result, {
890
+ return transform ? scopeDeferredResult(transform(result, {
832
891
  id,
833
892
  args,
834
893
  event: evt
835
- }) : result;
894
+ }), scope) : result;
836
895
  }
837
896
  });
838
897
  return proxy;
@@ -898,22 +957,58 @@ function assertDecodeDepth(value) {
898
957
  level = next;
899
958
  }
900
959
  }
960
+ const UNSAFE_ARGUMENT_KEYS = ["__proto__", "constructor", "prototype"];
961
+ function stripUnsafeArgumentKeys(value) {
962
+ const stack = [value];
963
+ const seen = new Set();
964
+ while (stack.length) {
965
+ const v = stack.pop();
966
+ if (v === null || typeof v !== "object" || seen.has(v)) continue;
967
+ seen.add(v);
968
+ for (const key of UNSAFE_ARGUMENT_KEYS) {
969
+ delete v[key];
970
+ }
971
+ for (const key of Object.keys(v)) stack.push(v[key]);
972
+ if (v instanceof Map) {
973
+ for (const [k, entry] of v) stack.push(k, entry);
974
+ } else if (v instanceof Set) {
975
+ for (const member of v) stack.push(member);
976
+ }
977
+ }
978
+ return value;
979
+ }
901
980
  async function bufferBodyWithin(request, limit) {
902
- const reader = request.clone().body.getReader();
981
+ const reader = request.body.getReader();
982
+ const signal = request.signal;
903
983
  const chunks = [];
904
984
  let total = 0;
905
- for (;;) {
906
- const {
907
- done,
908
- value
909
- } = await reader.read();
910
- if (done) break;
911
- total += value.byteLength;
912
- if (total > limit) {
913
- reader.cancel().catch(() => {});
914
- return null;
985
+ const onAbort = () => {
986
+ reader.cancel(signal.reason).catch(() => {});
987
+ };
988
+ if (signal.aborted) onAbort();else signal.addEventListener("abort", onAbort, {
989
+ once: true
990
+ });
991
+ try {
992
+ for (;;) {
993
+ const {
994
+ done,
995
+ value
996
+ } = await reader.read();
997
+ if (signal.aborted) throw signal.reason;
998
+ if (done) break;
999
+ total += value.byteLength;
1000
+ if (total > limit) {
1001
+ reader.cancel().catch(() => {});
1002
+ return null;
1003
+ }
1004
+ chunks.push(value);
915
1005
  }
916
- chunks.push(value);
1006
+ } catch (error) {
1007
+ reader.cancel(error).catch(() => {});
1008
+ throw error;
1009
+ } finally {
1010
+ signal.removeEventListener("abort", onAbort);
1011
+ reader.releaseLock();
917
1012
  }
918
1013
  const body = new Uint8Array(total);
919
1014
  let offset = 0;
@@ -940,6 +1035,7 @@ async function parseArguments(request, url, scripted, codec) {
940
1035
  if (!Array.isArray(result)) {
941
1036
  throw new TypeError("Server function arguments must encode an array");
942
1037
  }
1038
+ stripUnsafeArgumentKeys(result);
943
1039
  for (const arg of result) {
944
1040
  parsed.push(arg);
945
1041
  }
@@ -953,9 +1049,12 @@ async function parseArguments(request, url, scripted, codec) {
953
1049
  if (!Array.isArray(decoded)) {
954
1050
  throw new TypeError("Server function arguments must encode an array");
955
1051
  }
956
- return decoded;
1052
+ return stripUnsafeArgumentKeys(decoded);
957
1053
  }
958
1054
  if (decoded === undefined) {
1055
+ if (bodyFormat === null && (await request.clone().arrayBuffer()).byteLength === 0) {
1056
+ return parsed;
1057
+ }
959
1058
  throw new TypeError("Server function body carries no usable encoding");
960
1059
  }
961
1060
  parsed.push(decoded);
@@ -1056,7 +1155,7 @@ function foldSetCookies(headers, setCookies) {
1056
1155
  }
1057
1156
  function mergeResponseHeaders(target, source) {
1058
1157
  source.forEach((value, key) => {
1059
- if (key !== "set-cookie") target.append(key, value);
1158
+ if (key !== "set-cookie" && !COMPOSED_BODY_FRAMING.has(key)) target.append(key, value);
1060
1159
  });
1061
1160
  if (source.getSetCookie) {
1062
1161
  for (const cookie of source.getSetCookie()) target.append("Set-Cookie", cookie);
@@ -1073,10 +1172,6 @@ function maskRedirect(headers, response, requestUrl) {
1073
1172
  headers.delete("Location");
1074
1173
  }
1075
1174
  const BOUNDED_COMPOSED_HEADERS = [REDIRECT_HEADER, "Location", REVALIDATE_HEADER];
1076
- function refusedTargetScheme(target) {
1077
- const match = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.exec(target);
1078
- return match !== null && !/^https?:$/i.test(match[0]);
1079
- }
1080
1175
  function enforceComposedHeaderInvariants(response) {
1081
1176
  for (const name of BOUNDED_COMPOSED_HEADERS) {
1082
1177
  const value = response.headers.get(name);
@@ -1086,7 +1181,7 @@ function enforceComposedHeaderInvariants(response) {
1086
1181
  }
1087
1182
  if (name === REVALIDATE_HEADER) continue;
1088
1183
  const target = name === REDIRECT_HEADER ? value.slice(value.indexOf(" ") + 1) : value;
1089
- if (refusedTargetScheme(target)) {
1184
+ if (!isHttpNavigationTarget(target)) {
1090
1185
  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);
1091
1186
  }
1092
1187
  }
@@ -1214,14 +1309,11 @@ function guardFailures(value, state) {
1214
1309
  top.next.add(guarded);
1215
1310
  if (guarded !== original) top.changed = true;
1216
1311
  } else if (guarded !== original || top.accessorRead === i) {
1217
- Object.defineProperty(top.next, items[i], top.accessorRead === i ? {
1218
- enumerable: true,
1219
- configurable: true,
1312
+ Object.defineProperty(top.next, items[i], {
1313
+ value: guarded,
1220
1314
  writable: true,
1221
- value: guarded
1222
- } : {
1223
- ...top.descriptors[items[i]],
1224
- value: guarded
1315
+ configurable: true,
1316
+ enumerable: top.descriptors[items[i]].enumerable
1225
1317
  });
1226
1318
  top.changed = true;
1227
1319
  }
@@ -1256,6 +1348,9 @@ class Frame {
1256
1348
  this.pendingKey = undefined;
1257
1349
  }
1258
1350
  }
1351
+ function guardOperation(state, run) {
1352
+ return state.scope ? state.scope(run) : run();
1353
+ }
1259
1354
  function enterGuard(value, state) {
1260
1355
  if (value === null || typeof value !== "object") return value;
1261
1356
  if (state.seen.has(value)) {
@@ -1270,7 +1365,7 @@ function enterGuard(value, state) {
1270
1365
  if (finished) return;
1271
1366
  finished = true;
1272
1367
  try {
1273
- const cancelled = reader ? reader.cancel() : value.cancel();
1368
+ const cancelled = guardOperation(state, () => reader ? reader.cancel() : value.cancel());
1274
1369
  if (cancelled && typeof cancelled.then === "function") cancelled.then(undefined, () => {});
1275
1370
  } catch {}
1276
1371
  };
@@ -1282,19 +1377,19 @@ function enterGuard(value, state) {
1282
1377
  controller.close();
1283
1378
  return;
1284
1379
  }
1285
- if (!reader) reader = value.getReader();
1380
+ if (!reader) reader = guardOperation(state, () => value.getReader());
1286
1381
  const {
1287
1382
  done,
1288
1383
  value: chunk
1289
- } = await reader.read();
1290
- done ? controller.close() : controller.enqueue(guardFailures(chunk, state));
1384
+ } = await guardOperation(state, () => reader.read());
1385
+ done ? controller.close() : controller.enqueue(guardOperation(state, () => guardFailures(chunk, state)));
1291
1386
  } catch (error) {
1292
- controller.error(sanitizeServerError(error));
1387
+ controller.error(guardOperation(state, () => sanitizeServerError(error)));
1293
1388
  }
1294
1389
  },
1295
1390
  cancel(reason) {
1296
1391
  finished = true;
1297
- return reader ? reader.cancel(reason) : value.cancel(reason);
1392
+ return guardOperation(state, () => reader ? reader.cancel(reason) : value.cancel(reason));
1298
1393
  }
1299
1394
  });
1300
1395
  if (gate) gate.onOpen(close);
@@ -1302,9 +1397,10 @@ function enterGuard(value, state) {
1302
1397
  return guardedStream;
1303
1398
  }
1304
1399
  if (typeof value.then === "function") {
1305
- const guardedPromise = Promise.resolve(value).then(resolved => guardFailures(resolved, state), error => {
1306
- throw sanitizeServerError(error);
1400
+ const guardedPromise = Promise.resolve(value).then(resolved => guardOperation(state, () => guardFailures(resolved, state)), error => {
1401
+ throw guardOperation(state, () => sanitizeServerError(error));
1307
1402
  });
1403
+ guardedPromise.catch(() => {});
1308
1404
  state.seen.set(value, guardedPromise);
1309
1405
  return guardedPromise;
1310
1406
  }
@@ -1313,13 +1409,13 @@ function enterGuard(value, state) {
1313
1409
  const gate = state.gate;
1314
1410
  const guardedIterable = {
1315
1411
  [Symbol.asyncIterator]() {
1316
- const iterator = source[Symbol.asyncIterator]();
1412
+ const iterator = guardOperation(state, () => source[Symbol.asyncIterator]());
1317
1413
  let finished = false;
1318
1414
  const close = () => {
1319
1415
  if (finished) return;
1320
1416
  finished = true;
1321
1417
  try {
1322
- const returned = iterator.return && iterator.return();
1418
+ const returned = iterator.return && guardOperation(state, () => iterator.return());
1323
1419
  if (returned && typeof returned.then === "function") returned.then(undefined, () => {});
1324
1420
  } catch {}
1325
1421
  };
@@ -1327,17 +1423,17 @@ function enterGuard(value, state) {
1327
1423
  const step = () => finished ? Promise.resolve({
1328
1424
  done: true,
1329
1425
  value: undefined
1330
- }) : iterator.next().then(step => {
1426
+ }) : guardOperation(state, () => iterator.next()).then(step => {
1331
1427
  if (step.done) {
1332
1428
  finished = true;
1333
1429
  return step;
1334
1430
  }
1335
1431
  return {
1336
1432
  done: false,
1337
- value: guardFailures(step.value, state)
1433
+ value: guardOperation(state, () => guardFailures(step.value, state))
1338
1434
  };
1339
1435
  }, error => {
1340
- throw sanitizeServerError(error);
1436
+ throw guardOperation(state, () => sanitizeServerError(error));
1341
1437
  });
1342
1438
  return {
1343
1439
  next: () => finished || !gate || gate.wantsMore() ? step() : gate.awaitDemand().then(step),
@@ -1354,6 +1450,11 @@ function enterGuard(value, state) {
1354
1450
  state.seen.set(value, guardedIterable);
1355
1451
  return guardedIterable;
1356
1452
  }
1453
+ if (state.scope && typeof value[Symbol.iterator] === "function" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set) && !ArrayBuffer.isView(value)) {
1454
+ const scopedIterable = scopeDeferredResult(value, state.scope);
1455
+ state.seen.set(value, scopedIterable);
1456
+ return scopedIterable;
1457
+ }
1357
1458
  if (Array.isArray(value)) {
1358
1459
  const next = value.slice();
1359
1460
  state.seen.set(value, next);
@@ -1377,16 +1478,20 @@ function enterGuard(value, state) {
1377
1478
  return value;
1378
1479
  }
1379
1480
  const descriptors = Object.getOwnPropertyDescriptors(value);
1481
+ for (const key of Object.keys(descriptors)) {
1482
+ descriptors[key].configurable = true;
1483
+ if ("value" in descriptors[key]) descriptors[key].writable = true;
1484
+ }
1380
1485
  const next = Object.create(prototype, descriptors);
1381
1486
  state.seen.set(value, next);
1382
- return new Frame(OBJECT, value, next, Object.keys(descriptors), descriptors);
1487
+ return new Frame(OBJECT, value, next, Object.keys(value), descriptors);
1383
1488
  }
1384
1489
  function keepGuarded(value, next, changed, state) {
1385
1490
  if (changed || state.cyclic.has(value)) return next;
1386
1491
  state.seen.set(value, value);
1387
1492
  return value;
1388
1493
  }
1389
- function serializeResponseStream(value, codecOptions, signal) {
1494
+ function serializeResponseStream(value, codecOptions, signal, scope) {
1390
1495
  let closed = false;
1391
1496
  let streamController = null;
1392
1497
  let demandWaiters = null;
@@ -1405,11 +1510,13 @@ function serializeResponseStream(value, codecOptions, signal) {
1405
1510
  if (closed) close();else sourceClosers.add(close);
1406
1511
  }
1407
1512
  };
1408
- value = guardFailures(value, {
1513
+ const guardState = {
1409
1514
  seen: new WeakMap(),
1410
1515
  cyclic: new WeakSet(),
1411
- gate
1412
- });
1516
+ gate,
1517
+ scope
1518
+ };
1519
+ value = guardOperation(guardState, () => guardFailures(value, guardState));
1413
1520
  let cancelSerialize = null;
1414
1521
  let onAbort = null;
1415
1522
  const finishSource = () => {
@@ -1490,14 +1597,14 @@ function serializeResponseStream(value, codecOptions, signal) {
1490
1597
  }
1491
1598
  });
1492
1599
  }
1493
- function serializedResponse(value, headers, codec, signal) {
1600
+ function serializedResponse(value, headers, codec, signal, scope) {
1494
1601
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
1495
1602
  headers.set("Content-Type", "text/plain");
1496
- return new Response(serializeResponseStream(value, codec, signal), {
1603
+ return new Response(serializeResponseStream(value, codec, signal, scope), {
1497
1604
  headers
1498
1605
  });
1499
1606
  }
1500
- function encodeResult(value, headers, status, codec, signal) {
1607
+ function encodeResult(value, headers, status, codec, signal, scope) {
1501
1608
  if (NULL_BODY_STATUSES.has(status)) {
1502
1609
  if (value === undefined || value === null) {
1503
1610
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Void);
@@ -1508,7 +1615,7 @@ function encodeResult(value, headers, status, codec, signal) {
1508
1615
  }
1509
1616
  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.`);
1510
1617
  headers.set(ERROR_HEADER, encodeErrorHeaderValue(error.message));
1511
- return encodeResult(error, headers, 500, codec, signal);
1618
+ return encodeResult(error, headers, 500, codec, signal, scope);
1512
1619
  }
1513
1620
  const direct = getHeadersAndBody(value);
1514
1621
  if (direct) {
@@ -1528,10 +1635,12 @@ function encodeResult(value, headers, status, codec, signal) {
1528
1635
  });
1529
1636
  }
1530
1637
  try {
1531
- if (isJSONSafe(value)) {
1638
+ const jsonSafe = scope ? scope(() => isJSONSafe(value)) : isJSONSafe(value);
1639
+ if (jsonSafe) {
1532
1640
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Json);
1533
1641
  headers.set("Content-Type", "application/json");
1534
- return new Response(JSON.stringify(value), {
1642
+ const body = scope ? scope(() => JSON.stringify(value)) : JSON.stringify(value);
1643
+ return new Response(body, {
1535
1644
  status,
1536
1645
  headers
1537
1646
  });
@@ -1539,7 +1648,7 @@ function encodeResult(value, headers, status, codec, signal) {
1539
1648
  } catch {
1540
1649
  }
1541
1650
  try {
1542
- const response = serializedResponse(value, headers, codec, signal);
1651
+ const response = serializedResponse(value, headers, codec, signal, scope);
1543
1652
  return status === 200 ? response : new Response(response.body, {
1544
1653
  status,
1545
1654
  headers
@@ -1637,8 +1746,17 @@ function forbiddenResponse() {
1637
1746
  }
1638
1747
  }));
1639
1748
  }
1749
+ function nativePromise(value) {
1750
+ if (value instanceof Promise) return value;
1751
+ try {
1752
+ if (Object.prototype.toString.call(value) === "[object Promise]") return Promise.prototype.then.call(value, value => value);
1753
+ } catch {}
1754
+ }
1640
1755
  async function handleServerFunctionRequest(request, options = {}) {
1641
- const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
1756
+ const codec = {
1757
+ ...(options.codec !== undefined ? options.codec : getServerFunctionsCodec())
1758
+ };
1759
+ codec.serializeErrorStacks ??= DEV;
1642
1760
  const url = new URL(request.url);
1643
1761
  const method = request.method;
1644
1762
  const address = resolveAddress(url);
@@ -1697,7 +1815,15 @@ async function handleServerFunctionRequest(request, options = {}) {
1697
1815
  return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1698
1816
  }
1699
1817
  if (!(declared > 0)) {
1700
- const bounded = await bufferBodyWithin(request, bodySizeLimit);
1818
+ let bounded;
1819
+ try {
1820
+ bounded = await bufferBodyWithin(request, bodySizeLimit);
1821
+ } catch {
1822
+ const response = new Response(DEV ? "Malformed server function arguments" : null, {
1823
+ status: 400
1824
+ });
1825
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1826
+ }
1701
1827
  if (bounded === null) {
1702
1828
  const response = new Response(DEV ? "Server function request body exceeds the configured bodySizeLimit" : null, {
1703
1829
  status: 413
@@ -1707,16 +1833,30 @@ async function handleServerFunctionRequest(request, options = {}) {
1707
1833
  request = bounded;
1708
1834
  }
1709
1835
  }
1710
- let event = options.createEvent ? options.createEvent(request) : {
1711
- request,
1712
- locals: {}
1713
- };
1714
- if (typeof event?.then === "function") event = await event;
1836
+ let event;
1837
+ try {
1838
+ event = options.createEvent ? options.createEvent(request) : {
1839
+ request,
1840
+ locals: {}
1841
+ };
1842
+ const promised = nativePromise(event);
1843
+ if (promised) event = await promised;
1844
+ } catch (error) {
1845
+ const safe = sanitizeServerError(error);
1846
+ const message = safe instanceof Error ? safe.message : String(safe);
1847
+ const headers = new Headers();
1848
+ headers.set(ERROR_HEADER, boundedErrorHeaderValue(message));
1849
+ const response = scripted ? encodeResult(safe, headers, 500, codec, request.signal) : new Response(DEV ? message : null, {
1850
+ status: 500
1851
+ });
1852
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1853
+ }
1715
1854
  const refuseCommitted = raw => {
1716
1855
  const response = commitEventResponse(raw, event);
1717
1856
  return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1718
1857
  };
1719
1858
  const provide = options.provideEvent || provideEvent;
1859
+ const scope = run => provide(event, run);
1720
1860
  const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
1721
1861
  const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
1722
1862
  const wrapInvocation = options.wrapInvocation !== undefined ? options.wrapInvocation : config.wrapInvocation;
@@ -1767,7 +1907,8 @@ async function handleServerFunctionRequest(request, options = {}) {
1767
1907
  const headers = new Headers();
1768
1908
  const dispatch = async () => {
1769
1909
  try {
1770
- let result = await provide(event, async () => {
1910
+ let invocations = 0;
1911
+ const invokeOnce = async () => {
1771
1912
  INVOCATIONS.set(event, {
1772
1913
  id: functionId
1773
1914
  });
@@ -1779,7 +1920,16 @@ async function handleServerFunctionRequest(request, options = {}) {
1779
1920
  request,
1780
1921
  direct: false
1781
1922
  }) : run();
1923
+ };
1924
+ let result = await provide(event, () => {
1925
+ if (++invocations > 1) {
1926
+ 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.");
1927
+ }
1928
+ return invokeOnce();
1782
1929
  });
1930
+ if (invocations !== 1) {
1931
+ 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.");
1932
+ }
1783
1933
  if (transformResult) {
1784
1934
  result = await transformResult(event, result, flightContext);
1785
1935
  }
@@ -1833,10 +1983,10 @@ async function handleServerFunctionRequest(request, options = {}) {
1833
1983
  if (!scripted) {
1834
1984
  if (handleNoJS) return handleNoJS(result ?? metadata, request, parsed);
1835
1985
  if (result instanceof Response) return result;
1836
- return encodeResult(result, headers, status, codec, request.signal);
1986
+ return encodeResult(result, headers, status, codec, request.signal, scope);
1837
1987
  }
1838
1988
  if (status === 304) warnScripted304(functionId);
1839
- return encodeResult(result, headers, status, codec, request.signal);
1989
+ return encodeResult(result, headers, status, codec, request.signal, scope);
1840
1990
  } catch (x) {
1841
1991
  const respondThrown = value => {
1842
1992
  const safe = sanitizeServerError(value);
@@ -1849,7 +1999,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1849
1999
  }
1850
2000
  const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
1851
2001
  headers.set(ERROR_HEADER, boundedErrorHeaderValue(error));
1852
- return encodeResult(safe, headers, 500, codec, request.signal);
2002
+ return encodeResult(safe, headers, 500, codec, request.signal, scope);
1853
2003
  };
1854
2004
  if (x instanceof Response || isResponseEnvelope(x)) {
1855
2005
  if (transformResult) {
@@ -1913,7 +2063,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1913
2063
  if (x instanceof Response) return x;
1914
2064
  }
1915
2065
  if (scripted && status === 304) warnScripted304(functionId);
1916
- return encodeResult(x, headers, status, codec, request.signal);
2066
+ return encodeResult(x, headers, status, codec, request.signal, scope);
1917
2067
  }
1918
2068
  return respondThrown(x);
1919
2069
  }