@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]);
@@ -536,7 +546,8 @@ function copyInitHeaders(init) {
536
546
  for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
537
547
  return headers;
538
548
  }
539
- 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()));
549
+ const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, REDIRECT_HEADER, "Location",
550
+ ...COMPOSED_BODY_FRAMING].map(header => header.toLowerCase()));
540
551
  function fillsStubGap(key, headers, response) {
541
552
  if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
542
553
  if (response.body === null && (key === "content-type" || key === "content-length")) return false;
@@ -696,6 +707,52 @@ function provideEvent(event, fn) {
696
707
  if (ctx) return ctx.run(event, fn);
697
708
  throw new Error("No request event provider. Configure one with configureServerFunctionsServer({ provideEvent }).");
698
709
  }
710
+ function scopeDeferredResult(value, scope) {
711
+ if (value === null || typeof value !== "object" && typeof value !== "function") return value;
712
+ const promised = scope(() => nativePromise(value));
713
+ if (promised) {
714
+ return promised.then(result => scope(() => scopeDeferredResult(result, scope)));
715
+ }
716
+ if (typeof ReadableStream !== "undefined" && value instanceof ReadableStream) {
717
+ let reader;
718
+ return new ReadableStream({
719
+ async pull(controller) {
720
+ try {
721
+ const step = await scope(() => {
722
+ if (!reader) reader = value.getReader();
723
+ return reader.read();
724
+ });
725
+ if (step.done) controller.close();else controller.enqueue(step.value);
726
+ } catch (error) {
727
+ controller.error(error);
728
+ }
729
+ },
730
+ cancel(reason) {
731
+ return scope(() => reader ? reader.cancel(reason) : value.cancel(reason));
732
+ }
733
+ }, {
734
+ highWaterMark: 0
735
+ });
736
+ }
737
+ const scopedIterator = symbol => ({
738
+ [symbol]() {
739
+ const iterator = scope(() => value[symbol]());
740
+ return new Proxy(iterator, {
741
+ get(target, property) {
742
+ const member = Reflect.get(target, property, target);
743
+ return typeof member === "function" && (property === "next" || property === "return" || property === "throw") ? (...args) => scope(() => member.apply(target, args)) : member;
744
+ }
745
+ });
746
+ }
747
+ });
748
+ if (typeof value[Symbol.asyncIterator] === "function") {
749
+ return scopedIterator(Symbol.asyncIterator);
750
+ }
751
+ if (typeof value[Symbol.iterator] === "function" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set) && !ArrayBuffer.isView(value)) {
752
+ return scopedIterator(Symbol.iterator);
753
+ }
754
+ return value;
755
+ }
699
756
  const REGISTRATIONS = new Map();
700
757
  const METHODS = new Map();
701
758
  const INVOCATIONS = new WeakMap();
@@ -793,7 +850,8 @@ function createServerReference({
793
850
  id
794
851
  });
795
852
  evt.serverOnly = true;
796
- const result = provideEvent(evt, () => {
853
+ const scope = run => provideEvent(evt, run);
854
+ let result = provideEvent(evt, () => {
797
855
  const run = () => fn.apply(thisArg, args);
798
856
  return config.wrapInvocation ? config.wrapInvocation(run, {
799
857
  id,
@@ -802,19 +860,20 @@ function createServerReference({
802
860
  direct: true
803
861
  }) : run();
804
862
  });
863
+ result = scopeDeferredResult(result, scope);
805
864
  const transform = config.transformDirectResult;
806
865
  if (transform && result && typeof result.then === "function") {
807
- return result.then(value => transform(value, {
866
+ return result.then(value => scopeDeferredResult(transform(value, {
808
867
  id,
809
868
  args,
810
869
  event: evt
811
- }));
870
+ }), scope));
812
871
  }
813
- return transform ? transform(result, {
872
+ return transform ? scopeDeferredResult(transform(result, {
814
873
  id,
815
874
  args,
816
875
  event: evt
817
- }) : result;
876
+ }), scope) : result;
818
877
  }
819
878
  });
820
879
  return proxy;
@@ -880,22 +939,58 @@ function assertDecodeDepth(value) {
880
939
  level = next;
881
940
  }
882
941
  }
942
+ const UNSAFE_ARGUMENT_KEYS = ["__proto__", "constructor", "prototype"];
943
+ function stripUnsafeArgumentKeys(value) {
944
+ const stack = [value];
945
+ const seen = new Set();
946
+ while (stack.length) {
947
+ const v = stack.pop();
948
+ if (v === null || typeof v !== "object" || seen.has(v)) continue;
949
+ seen.add(v);
950
+ for (const key of UNSAFE_ARGUMENT_KEYS) {
951
+ delete v[key];
952
+ }
953
+ for (const key of Object.keys(v)) stack.push(v[key]);
954
+ if (v instanceof Map) {
955
+ for (const [k, entry] of v) stack.push(k, entry);
956
+ } else if (v instanceof Set) {
957
+ for (const member of v) stack.push(member);
958
+ }
959
+ }
960
+ return value;
961
+ }
883
962
  async function bufferBodyWithin(request, limit) {
884
- const reader = request.clone().body.getReader();
963
+ const reader = request.body.getReader();
964
+ const signal = request.signal;
885
965
  const chunks = [];
886
966
  let total = 0;
887
- for (;;) {
888
- const {
889
- done,
890
- value
891
- } = await reader.read();
892
- if (done) break;
893
- total += value.byteLength;
894
- if (total > limit) {
895
- reader.cancel().catch(() => {});
896
- return null;
967
+ const onAbort = () => {
968
+ reader.cancel(signal.reason).catch(() => {});
969
+ };
970
+ if (signal.aborted) onAbort();else signal.addEventListener("abort", onAbort, {
971
+ once: true
972
+ });
973
+ try {
974
+ for (;;) {
975
+ const {
976
+ done,
977
+ value
978
+ } = await reader.read();
979
+ if (signal.aborted) throw signal.reason;
980
+ if (done) break;
981
+ total += value.byteLength;
982
+ if (total > limit) {
983
+ reader.cancel().catch(() => {});
984
+ return null;
985
+ }
986
+ chunks.push(value);
897
987
  }
898
- chunks.push(value);
988
+ } catch (error) {
989
+ reader.cancel(error).catch(() => {});
990
+ throw error;
991
+ } finally {
992
+ signal.removeEventListener("abort", onAbort);
993
+ reader.releaseLock();
899
994
  }
900
995
  const body = new Uint8Array(total);
901
996
  let offset = 0;
@@ -922,6 +1017,7 @@ async function parseArguments(request, url, scripted, codec) {
922
1017
  if (!Array.isArray(result)) {
923
1018
  throw new TypeError("Server function arguments must encode an array");
924
1019
  }
1020
+ stripUnsafeArgumentKeys(result);
925
1021
  for (const arg of result) {
926
1022
  parsed.push(arg);
927
1023
  }
@@ -935,9 +1031,12 @@ async function parseArguments(request, url, scripted, codec) {
935
1031
  if (!Array.isArray(decoded)) {
936
1032
  throw new TypeError("Server function arguments must encode an array");
937
1033
  }
938
- return decoded;
1034
+ return stripUnsafeArgumentKeys(decoded);
939
1035
  }
940
1036
  if (decoded === undefined) {
1037
+ if (bodyFormat === null && (await request.clone().arrayBuffer()).byteLength === 0) {
1038
+ return parsed;
1039
+ }
941
1040
  throw new TypeError("Server function body carries no usable encoding");
942
1041
  }
943
1042
  parsed.push(decoded);
@@ -1038,7 +1137,7 @@ function foldSetCookies(headers, setCookies) {
1038
1137
  }
1039
1138
  function mergeResponseHeaders(target, source) {
1040
1139
  source.forEach((value, key) => {
1041
- if (key !== "set-cookie") target.append(key, value);
1140
+ if (key !== "set-cookie" && !COMPOSED_BODY_FRAMING.has(key)) target.append(key, value);
1042
1141
  });
1043
1142
  if (source.getSetCookie) {
1044
1143
  for (const cookie of source.getSetCookie()) target.append("Set-Cookie", cookie);
@@ -1055,10 +1154,6 @@ function maskRedirect(headers, response, requestUrl) {
1055
1154
  headers.delete("Location");
1056
1155
  }
1057
1156
  const BOUNDED_COMPOSED_HEADERS = [REDIRECT_HEADER, "Location", REVALIDATE_HEADER];
1058
- function refusedTargetScheme(target) {
1059
- const match = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.exec(target);
1060
- return match !== null && !/^https?:$/i.test(match[0]);
1061
- }
1062
1157
  function enforceComposedHeaderInvariants(response) {
1063
1158
  for (const name of BOUNDED_COMPOSED_HEADERS) {
1064
1159
  const value = response.headers.get(name);
@@ -1068,7 +1163,7 @@ function enforceComposedHeaderInvariants(response) {
1068
1163
  }
1069
1164
  if (name === REVALIDATE_HEADER) continue;
1070
1165
  const target = name === REDIRECT_HEADER ? value.slice(value.indexOf(" ") + 1) : value;
1071
- if (refusedTargetScheme(target)) {
1166
+ if (!isHttpNavigationTarget(target)) {
1072
1167
  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);
1073
1168
  }
1074
1169
  }
@@ -1196,14 +1291,11 @@ function guardFailures(value, state) {
1196
1291
  top.next.add(guarded);
1197
1292
  if (guarded !== original) top.changed = true;
1198
1293
  } else if (guarded !== original || top.accessorRead === i) {
1199
- Object.defineProperty(top.next, items[i], top.accessorRead === i ? {
1200
- enumerable: true,
1201
- configurable: true,
1294
+ Object.defineProperty(top.next, items[i], {
1295
+ value: guarded,
1202
1296
  writable: true,
1203
- value: guarded
1204
- } : {
1205
- ...top.descriptors[items[i]],
1206
- value: guarded
1297
+ configurable: true,
1298
+ enumerable: top.descriptors[items[i]].enumerable
1207
1299
  });
1208
1300
  top.changed = true;
1209
1301
  }
@@ -1238,6 +1330,9 @@ class Frame {
1238
1330
  this.pendingKey = undefined;
1239
1331
  }
1240
1332
  }
1333
+ function guardOperation(state, run) {
1334
+ return state.scope ? state.scope(run) : run();
1335
+ }
1241
1336
  function enterGuard(value, state) {
1242
1337
  if (value === null || typeof value !== "object") return value;
1243
1338
  if (state.seen.has(value)) {
@@ -1252,7 +1347,7 @@ function enterGuard(value, state) {
1252
1347
  if (finished) return;
1253
1348
  finished = true;
1254
1349
  try {
1255
- const cancelled = reader ? reader.cancel() : value.cancel();
1350
+ const cancelled = guardOperation(state, () => reader ? reader.cancel() : value.cancel());
1256
1351
  if (cancelled && typeof cancelled.then === "function") cancelled.then(undefined, () => {});
1257
1352
  } catch {}
1258
1353
  };
@@ -1264,19 +1359,19 @@ function enterGuard(value, state) {
1264
1359
  controller.close();
1265
1360
  return;
1266
1361
  }
1267
- if (!reader) reader = value.getReader();
1362
+ if (!reader) reader = guardOperation(state, () => value.getReader());
1268
1363
  const {
1269
1364
  done,
1270
1365
  value: chunk
1271
- } = await reader.read();
1272
- done ? controller.close() : controller.enqueue(guardFailures(chunk, state));
1366
+ } = await guardOperation(state, () => reader.read());
1367
+ done ? controller.close() : controller.enqueue(guardOperation(state, () => guardFailures(chunk, state)));
1273
1368
  } catch (error) {
1274
- controller.error(sanitizeServerError(error));
1369
+ controller.error(guardOperation(state, () => sanitizeServerError(error)));
1275
1370
  }
1276
1371
  },
1277
1372
  cancel(reason) {
1278
1373
  finished = true;
1279
- return reader ? reader.cancel(reason) : value.cancel(reason);
1374
+ return guardOperation(state, () => reader ? reader.cancel(reason) : value.cancel(reason));
1280
1375
  }
1281
1376
  });
1282
1377
  if (gate) gate.onOpen(close);
@@ -1284,9 +1379,10 @@ function enterGuard(value, state) {
1284
1379
  return guardedStream;
1285
1380
  }
1286
1381
  if (typeof value.then === "function") {
1287
- const guardedPromise = Promise.resolve(value).then(resolved => guardFailures(resolved, state), error => {
1288
- throw sanitizeServerError(error);
1382
+ const guardedPromise = Promise.resolve(value).then(resolved => guardOperation(state, () => guardFailures(resolved, state)), error => {
1383
+ throw guardOperation(state, () => sanitizeServerError(error));
1289
1384
  });
1385
+ guardedPromise.catch(() => {});
1290
1386
  state.seen.set(value, guardedPromise);
1291
1387
  return guardedPromise;
1292
1388
  }
@@ -1295,13 +1391,13 @@ function enterGuard(value, state) {
1295
1391
  const gate = state.gate;
1296
1392
  const guardedIterable = {
1297
1393
  [Symbol.asyncIterator]() {
1298
- const iterator = source[Symbol.asyncIterator]();
1394
+ const iterator = guardOperation(state, () => source[Symbol.asyncIterator]());
1299
1395
  let finished = false;
1300
1396
  const close = () => {
1301
1397
  if (finished) return;
1302
1398
  finished = true;
1303
1399
  try {
1304
- const returned = iterator.return && iterator.return();
1400
+ const returned = iterator.return && guardOperation(state, () => iterator.return());
1305
1401
  if (returned && typeof returned.then === "function") returned.then(undefined, () => {});
1306
1402
  } catch {}
1307
1403
  };
@@ -1309,17 +1405,17 @@ function enterGuard(value, state) {
1309
1405
  const step = () => finished ? Promise.resolve({
1310
1406
  done: true,
1311
1407
  value: undefined
1312
- }) : iterator.next().then(step => {
1408
+ }) : guardOperation(state, () => iterator.next()).then(step => {
1313
1409
  if (step.done) {
1314
1410
  finished = true;
1315
1411
  return step;
1316
1412
  }
1317
1413
  return {
1318
1414
  done: false,
1319
- value: guardFailures(step.value, state)
1415
+ value: guardOperation(state, () => guardFailures(step.value, state))
1320
1416
  };
1321
1417
  }, error => {
1322
- throw sanitizeServerError(error);
1418
+ throw guardOperation(state, () => sanitizeServerError(error));
1323
1419
  });
1324
1420
  return {
1325
1421
  next: () => finished || !gate || gate.wantsMore() ? step() : gate.awaitDemand().then(step),
@@ -1336,6 +1432,11 @@ function enterGuard(value, state) {
1336
1432
  state.seen.set(value, guardedIterable);
1337
1433
  return guardedIterable;
1338
1434
  }
1435
+ if (state.scope && typeof value[Symbol.iterator] === "function" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set) && !ArrayBuffer.isView(value)) {
1436
+ const scopedIterable = scopeDeferredResult(value, state.scope);
1437
+ state.seen.set(value, scopedIterable);
1438
+ return scopedIterable;
1439
+ }
1339
1440
  if (Array.isArray(value)) {
1340
1441
  const next = value.slice();
1341
1442
  state.seen.set(value, next);
@@ -1359,16 +1460,20 @@ function enterGuard(value, state) {
1359
1460
  return value;
1360
1461
  }
1361
1462
  const descriptors = Object.getOwnPropertyDescriptors(value);
1463
+ for (const key of Object.keys(descriptors)) {
1464
+ descriptors[key].configurable = true;
1465
+ if ("value" in descriptors[key]) descriptors[key].writable = true;
1466
+ }
1362
1467
  const next = Object.create(prototype, descriptors);
1363
1468
  state.seen.set(value, next);
1364
- return new Frame(OBJECT, value, next, Object.keys(descriptors), descriptors);
1469
+ return new Frame(OBJECT, value, next, Object.keys(value), descriptors);
1365
1470
  }
1366
1471
  function keepGuarded(value, next, changed, state) {
1367
1472
  if (changed || state.cyclic.has(value)) return next;
1368
1473
  state.seen.set(value, value);
1369
1474
  return value;
1370
1475
  }
1371
- function serializeResponseStream(value, codecOptions, signal) {
1476
+ function serializeResponseStream(value, codecOptions, signal, scope) {
1372
1477
  let closed = false;
1373
1478
  let streamController = null;
1374
1479
  let demandWaiters = null;
@@ -1387,11 +1492,13 @@ function serializeResponseStream(value, codecOptions, signal) {
1387
1492
  if (closed) close();else sourceClosers.add(close);
1388
1493
  }
1389
1494
  };
1390
- value = guardFailures(value, {
1495
+ const guardState = {
1391
1496
  seen: new WeakMap(),
1392
1497
  cyclic: new WeakSet(),
1393
- gate
1394
- });
1498
+ gate,
1499
+ scope
1500
+ };
1501
+ value = guardOperation(guardState, () => guardFailures(value, guardState));
1395
1502
  let cancelSerialize = null;
1396
1503
  let onAbort = null;
1397
1504
  const finishSource = () => {
@@ -1472,14 +1579,14 @@ function serializeResponseStream(value, codecOptions, signal) {
1472
1579
  }
1473
1580
  });
1474
1581
  }
1475
- function serializedResponse(value, headers, codec, signal) {
1582
+ function serializedResponse(value, headers, codec, signal, scope) {
1476
1583
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
1477
1584
  headers.set("Content-Type", "text/plain");
1478
- return new Response(serializeResponseStream(value, codec, signal), {
1585
+ return new Response(serializeResponseStream(value, codec, signal, scope), {
1479
1586
  headers
1480
1587
  });
1481
1588
  }
1482
- function encodeResult(value, headers, status, codec, signal) {
1589
+ function encodeResult(value, headers, status, codec, signal, scope) {
1483
1590
  if (NULL_BODY_STATUSES.has(status)) {
1484
1591
  if (value === undefined || value === null) {
1485
1592
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Void);
@@ -1490,7 +1597,7 @@ function encodeResult(value, headers, status, codec, signal) {
1490
1597
  }
1491
1598
  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.`);
1492
1599
  headers.set(ERROR_HEADER, encodeErrorHeaderValue(error.message));
1493
- return encodeResult(error, headers, 500, codec, signal);
1600
+ return encodeResult(error, headers, 500, codec, signal, scope);
1494
1601
  }
1495
1602
  const direct = getHeadersAndBody(value);
1496
1603
  if (direct) {
@@ -1510,10 +1617,12 @@ function encodeResult(value, headers, status, codec, signal) {
1510
1617
  });
1511
1618
  }
1512
1619
  try {
1513
- if (isJSONSafe(value)) {
1620
+ const jsonSafe = scope ? scope(() => isJSONSafe(value)) : isJSONSafe(value);
1621
+ if (jsonSafe) {
1514
1622
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Json);
1515
1623
  headers.set("Content-Type", "application/json");
1516
- return new Response(JSON.stringify(value), {
1624
+ const body = scope ? scope(() => JSON.stringify(value)) : JSON.stringify(value);
1625
+ return new Response(body, {
1517
1626
  status,
1518
1627
  headers
1519
1628
  });
@@ -1521,7 +1630,7 @@ function encodeResult(value, headers, status, codec, signal) {
1521
1630
  } catch {
1522
1631
  }
1523
1632
  try {
1524
- const response = serializedResponse(value, headers, codec, signal);
1633
+ const response = serializedResponse(value, headers, codec, signal, scope);
1525
1634
  return status === 200 ? response : new Response(response.body, {
1526
1635
  status,
1527
1636
  headers
@@ -1619,8 +1728,17 @@ function forbiddenResponse() {
1619
1728
  }
1620
1729
  }));
1621
1730
  }
1731
+ function nativePromise(value) {
1732
+ if (value instanceof Promise) return value;
1733
+ try {
1734
+ if (Object.prototype.toString.call(value) === "[object Promise]") return Promise.prototype.then.call(value, value => value);
1735
+ } catch {}
1736
+ }
1622
1737
  async function handleServerFunctionRequest(request, options = {}) {
1623
- const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
1738
+ const codec = {
1739
+ ...(options.codec !== undefined ? options.codec : getServerFunctionsCodec())
1740
+ };
1741
+ codec.serializeErrorStacks ??= DEV;
1624
1742
  const url = new URL(request.url);
1625
1743
  const method = request.method;
1626
1744
  const address = resolveAddress(url);
@@ -1679,7 +1797,15 @@ async function handleServerFunctionRequest(request, options = {}) {
1679
1797
  return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1680
1798
  }
1681
1799
  if (!(declared > 0)) {
1682
- const bounded = await bufferBodyWithin(request, bodySizeLimit);
1800
+ let bounded;
1801
+ try {
1802
+ bounded = await bufferBodyWithin(request, bodySizeLimit);
1803
+ } catch {
1804
+ const response = new Response(DEV ? "Malformed server function arguments" : null, {
1805
+ status: 400
1806
+ });
1807
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1808
+ }
1683
1809
  if (bounded === null) {
1684
1810
  const response = new Response(DEV ? "Server function request body exceeds the configured bodySizeLimit" : null, {
1685
1811
  status: 413
@@ -1689,16 +1815,30 @@ async function handleServerFunctionRequest(request, options = {}) {
1689
1815
  request = bounded;
1690
1816
  }
1691
1817
  }
1692
- let event = options.createEvent ? options.createEvent(request) : {
1693
- request,
1694
- locals: {}
1695
- };
1696
- if (typeof event?.then === "function") event = await event;
1818
+ let event;
1819
+ try {
1820
+ event = options.createEvent ? options.createEvent(request) : {
1821
+ request,
1822
+ locals: {}
1823
+ };
1824
+ const promised = nativePromise(event);
1825
+ if (promised) event = await promised;
1826
+ } catch (error) {
1827
+ const safe = sanitizeServerError(error);
1828
+ const message = safe instanceof Error ? safe.message : String(safe);
1829
+ const headers = new Headers();
1830
+ headers.set(ERROR_HEADER, boundedErrorHeaderValue(message));
1831
+ const response = scripted ? encodeResult(safe, headers, 500, codec, request.signal) : new Response(DEV ? message : null, {
1832
+ status: 500
1833
+ });
1834
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1835
+ }
1697
1836
  const refuseCommitted = raw => {
1698
1837
  const response = commitEventResponse(raw, event);
1699
1838
  return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1700
1839
  };
1701
1840
  const provide = options.provideEvent || provideEvent;
1841
+ const scope = run => provide(event, run);
1702
1842
  const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
1703
1843
  const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
1704
1844
  const wrapInvocation = options.wrapInvocation !== undefined ? options.wrapInvocation : config.wrapInvocation;
@@ -1749,7 +1889,8 @@ async function handleServerFunctionRequest(request, options = {}) {
1749
1889
  const headers = new Headers();
1750
1890
  const dispatch = async () => {
1751
1891
  try {
1752
- let result = await provide(event, async () => {
1892
+ let invocations = 0;
1893
+ const invokeOnce = async () => {
1753
1894
  INVOCATIONS.set(event, {
1754
1895
  id: functionId
1755
1896
  });
@@ -1761,7 +1902,16 @@ async function handleServerFunctionRequest(request, options = {}) {
1761
1902
  request,
1762
1903
  direct: false
1763
1904
  }) : run();
1905
+ };
1906
+ let result = await provide(event, () => {
1907
+ if (++invocations > 1) {
1908
+ 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.");
1909
+ }
1910
+ return invokeOnce();
1764
1911
  });
1912
+ if (invocations !== 1) {
1913
+ 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.");
1914
+ }
1765
1915
  if (transformResult) {
1766
1916
  result = await transformResult(event, result, flightContext);
1767
1917
  }
@@ -1815,10 +1965,10 @@ async function handleServerFunctionRequest(request, options = {}) {
1815
1965
  if (!scripted) {
1816
1966
  if (handleNoJS) return handleNoJS(result ?? metadata, request, parsed);
1817
1967
  if (result instanceof Response) return result;
1818
- return encodeResult(result, headers, status, codec, request.signal);
1968
+ return encodeResult(result, headers, status, codec, request.signal, scope);
1819
1969
  }
1820
1970
  if (status === 304) warnScripted304(functionId);
1821
- return encodeResult(result, headers, status, codec, request.signal);
1971
+ return encodeResult(result, headers, status, codec, request.signal, scope);
1822
1972
  } catch (x) {
1823
1973
  const respondThrown = value => {
1824
1974
  const safe = sanitizeServerError(value);
@@ -1831,7 +1981,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1831
1981
  }
1832
1982
  const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
1833
1983
  headers.set(ERROR_HEADER, boundedErrorHeaderValue(error));
1834
- return encodeResult(safe, headers, 500, codec, request.signal);
1984
+ return encodeResult(safe, headers, 500, codec, request.signal, scope);
1835
1985
  };
1836
1986
  if (x instanceof Response || isResponseEnvelope(x)) {
1837
1987
  if (transformResult) {
@@ -1895,7 +2045,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1895
2045
  if (x instanceof Response) return x;
1896
2046
  }
1897
2047
  if (scripted && status === 304) warnScripted304(functionId);
1898
- return encodeResult(x, headers, status, codec, request.signal);
2048
+ return encodeResult(x, headers, status, codec, request.signal, scope);
1899
2049
  }
1900
2050
  return respondThrown(x);
1901
2051
  }