@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]);
@@ -534,7 +544,8 @@ function copyInitHeaders(init) {
534
544
  for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
535
545
  return headers;
536
546
  }
537
- 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()));
547
+ const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, REDIRECT_HEADER, "Location",
548
+ ...COMPOSED_BODY_FRAMING].map(header => header.toLowerCase()));
538
549
  function fillsStubGap(key, headers, response) {
539
550
  if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
540
551
  if (response.body === null && (key === "content-type" || key === "content-length")) return false;
@@ -694,6 +705,52 @@ function provideEvent(event, fn) {
694
705
  if (ctx) return ctx.run(event, fn);
695
706
  throw new Error("No request event provider. Configure one with configureServerFunctionsServer({ provideEvent }).");
696
707
  }
708
+ function scopeDeferredResult(value, scope) {
709
+ if (value === null || typeof value !== "object" && typeof value !== "function") return value;
710
+ const promised = scope(() => nativePromise(value));
711
+ if (promised) {
712
+ return promised.then(result => scope(() => scopeDeferredResult(result, scope)));
713
+ }
714
+ if (typeof ReadableStream !== "undefined" && value instanceof ReadableStream) {
715
+ let reader;
716
+ return new ReadableStream({
717
+ async pull(controller) {
718
+ try {
719
+ const step = await scope(() => {
720
+ if (!reader) reader = value.getReader();
721
+ return reader.read();
722
+ });
723
+ if (step.done) controller.close();else controller.enqueue(step.value);
724
+ } catch (error) {
725
+ controller.error(error);
726
+ }
727
+ },
728
+ cancel(reason) {
729
+ return scope(() => reader ? reader.cancel(reason) : value.cancel(reason));
730
+ }
731
+ }, {
732
+ highWaterMark: 0
733
+ });
734
+ }
735
+ const scopedIterator = symbol => ({
736
+ [symbol]() {
737
+ const iterator = scope(() => value[symbol]());
738
+ return new Proxy(iterator, {
739
+ get(target, property) {
740
+ const member = Reflect.get(target, property, target);
741
+ return typeof member === "function" && (property === "next" || property === "return" || property === "throw") ? (...args) => scope(() => member.apply(target, args)) : member;
742
+ }
743
+ });
744
+ }
745
+ });
746
+ if (typeof value[Symbol.asyncIterator] === "function") {
747
+ return scopedIterator(Symbol.asyncIterator);
748
+ }
749
+ if (typeof value[Symbol.iterator] === "function" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set) && !ArrayBuffer.isView(value)) {
750
+ return scopedIterator(Symbol.iterator);
751
+ }
752
+ return value;
753
+ }
697
754
  const REGISTRATIONS = new Map();
698
755
  const METHODS = new Map();
699
756
  const INVOCATIONS = new WeakMap();
@@ -791,7 +848,8 @@ function createServerReference({
791
848
  id
792
849
  });
793
850
  evt.serverOnly = true;
794
- const result = provideEvent(evt, () => {
851
+ const scope = run => provideEvent(evt, run);
852
+ let result = provideEvent(evt, () => {
795
853
  const run = () => fn.apply(thisArg, args);
796
854
  return config.wrapInvocation ? config.wrapInvocation(run, {
797
855
  id,
@@ -800,19 +858,20 @@ function createServerReference({
800
858
  direct: true
801
859
  }) : run();
802
860
  });
861
+ result = scopeDeferredResult(result, scope);
803
862
  const transform = config.transformDirectResult;
804
863
  if (transform && result && typeof result.then === "function") {
805
- return result.then(value => transform(value, {
864
+ return result.then(value => scopeDeferredResult(transform(value, {
806
865
  id,
807
866
  args,
808
867
  event: evt
809
- }));
868
+ }), scope));
810
869
  }
811
- return transform ? transform(result, {
870
+ return transform ? scopeDeferredResult(transform(result, {
812
871
  id,
813
872
  args,
814
873
  event: evt
815
- }) : result;
874
+ }), scope) : result;
816
875
  }
817
876
  });
818
877
  return proxy;
@@ -878,22 +937,58 @@ function assertDecodeDepth(value) {
878
937
  level = next;
879
938
  }
880
939
  }
940
+ const UNSAFE_ARGUMENT_KEYS = ["__proto__", "constructor", "prototype"];
941
+ function stripUnsafeArgumentKeys(value) {
942
+ const stack = [value];
943
+ const seen = new Set();
944
+ while (stack.length) {
945
+ const v = stack.pop();
946
+ if (v === null || typeof v !== "object" || seen.has(v)) continue;
947
+ seen.add(v);
948
+ for (const key of UNSAFE_ARGUMENT_KEYS) {
949
+ delete v[key];
950
+ }
951
+ for (const key of Object.keys(v)) stack.push(v[key]);
952
+ if (v instanceof Map) {
953
+ for (const [k, entry] of v) stack.push(k, entry);
954
+ } else if (v instanceof Set) {
955
+ for (const member of v) stack.push(member);
956
+ }
957
+ }
958
+ return value;
959
+ }
881
960
  async function bufferBodyWithin(request, limit) {
882
- const reader = request.clone().body.getReader();
961
+ const reader = request.body.getReader();
962
+ const signal = request.signal;
883
963
  const chunks = [];
884
964
  let total = 0;
885
- for (;;) {
886
- const {
887
- done,
888
- value
889
- } = await reader.read();
890
- if (done) break;
891
- total += value.byteLength;
892
- if (total > limit) {
893
- reader.cancel().catch(() => {});
894
- return null;
965
+ const onAbort = () => {
966
+ reader.cancel(signal.reason).catch(() => {});
967
+ };
968
+ if (signal.aborted) onAbort();else signal.addEventListener("abort", onAbort, {
969
+ once: true
970
+ });
971
+ try {
972
+ for (;;) {
973
+ const {
974
+ done,
975
+ value
976
+ } = await reader.read();
977
+ if (signal.aborted) throw signal.reason;
978
+ if (done) break;
979
+ total += value.byteLength;
980
+ if (total > limit) {
981
+ reader.cancel().catch(() => {});
982
+ return null;
983
+ }
984
+ chunks.push(value);
895
985
  }
896
- chunks.push(value);
986
+ } catch (error) {
987
+ reader.cancel(error).catch(() => {});
988
+ throw error;
989
+ } finally {
990
+ signal.removeEventListener("abort", onAbort);
991
+ reader.releaseLock();
897
992
  }
898
993
  const body = new Uint8Array(total);
899
994
  let offset = 0;
@@ -920,6 +1015,7 @@ async function parseArguments(request, url, scripted, codec) {
920
1015
  if (!Array.isArray(result)) {
921
1016
  throw new TypeError("Server function arguments must encode an array");
922
1017
  }
1018
+ stripUnsafeArgumentKeys(result);
923
1019
  for (const arg of result) {
924
1020
  parsed.push(arg);
925
1021
  }
@@ -933,9 +1029,12 @@ async function parseArguments(request, url, scripted, codec) {
933
1029
  if (!Array.isArray(decoded)) {
934
1030
  throw new TypeError("Server function arguments must encode an array");
935
1031
  }
936
- return decoded;
1032
+ return stripUnsafeArgumentKeys(decoded);
937
1033
  }
938
1034
  if (decoded === undefined) {
1035
+ if (bodyFormat === null && (await request.clone().arrayBuffer()).byteLength === 0) {
1036
+ return parsed;
1037
+ }
939
1038
  throw new TypeError("Server function body carries no usable encoding");
940
1039
  }
941
1040
  parsed.push(decoded);
@@ -1036,7 +1135,7 @@ function foldSetCookies(headers, setCookies) {
1036
1135
  }
1037
1136
  function mergeResponseHeaders(target, source) {
1038
1137
  source.forEach((value, key) => {
1039
- if (key !== "set-cookie") target.append(key, value);
1138
+ if (key !== "set-cookie" && !COMPOSED_BODY_FRAMING.has(key)) target.append(key, value);
1040
1139
  });
1041
1140
  if (source.getSetCookie) {
1042
1141
  for (const cookie of source.getSetCookie()) target.append("Set-Cookie", cookie);
@@ -1053,10 +1152,6 @@ function maskRedirect(headers, response, requestUrl) {
1053
1152
  headers.delete("Location");
1054
1153
  }
1055
1154
  const BOUNDED_COMPOSED_HEADERS = [REDIRECT_HEADER, "Location", REVALIDATE_HEADER];
1056
- function refusedTargetScheme(target) {
1057
- const match = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.exec(target);
1058
- return match !== null && !/^https?:$/i.test(match[0]);
1059
- }
1060
1155
  function enforceComposedHeaderInvariants(response) {
1061
1156
  for (const name of BOUNDED_COMPOSED_HEADERS) {
1062
1157
  const value = response.headers.get(name);
@@ -1066,7 +1161,7 @@ function enforceComposedHeaderInvariants(response) {
1066
1161
  }
1067
1162
  if (name === REVALIDATE_HEADER) continue;
1068
1163
  const target = name === REDIRECT_HEADER ? value.slice(value.indexOf(" ") + 1) : value;
1069
- if (refusedTargetScheme(target)) {
1164
+ if (!isHttpNavigationTarget(target)) {
1070
1165
  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);
1071
1166
  }
1072
1167
  }
@@ -1194,14 +1289,11 @@ function guardFailures(value, state) {
1194
1289
  top.next.add(guarded);
1195
1290
  if (guarded !== original) top.changed = true;
1196
1291
  } else if (guarded !== original || top.accessorRead === i) {
1197
- Object.defineProperty(top.next, items[i], top.accessorRead === i ? {
1198
- enumerable: true,
1199
- configurable: true,
1292
+ Object.defineProperty(top.next, items[i], {
1293
+ value: guarded,
1200
1294
  writable: true,
1201
- value: guarded
1202
- } : {
1203
- ...top.descriptors[items[i]],
1204
- value: guarded
1295
+ configurable: true,
1296
+ enumerable: top.descriptors[items[i]].enumerable
1205
1297
  });
1206
1298
  top.changed = true;
1207
1299
  }
@@ -1236,6 +1328,9 @@ class Frame {
1236
1328
  this.pendingKey = undefined;
1237
1329
  }
1238
1330
  }
1331
+ function guardOperation(state, run) {
1332
+ return state.scope ? state.scope(run) : run();
1333
+ }
1239
1334
  function enterGuard(value, state) {
1240
1335
  if (value === null || typeof value !== "object") return value;
1241
1336
  if (state.seen.has(value)) {
@@ -1250,7 +1345,7 @@ function enterGuard(value, state) {
1250
1345
  if (finished) return;
1251
1346
  finished = true;
1252
1347
  try {
1253
- const cancelled = reader ? reader.cancel() : value.cancel();
1348
+ const cancelled = guardOperation(state, () => reader ? reader.cancel() : value.cancel());
1254
1349
  if (cancelled && typeof cancelled.then === "function") cancelled.then(undefined, () => {});
1255
1350
  } catch {}
1256
1351
  };
@@ -1262,19 +1357,19 @@ function enterGuard(value, state) {
1262
1357
  controller.close();
1263
1358
  return;
1264
1359
  }
1265
- if (!reader) reader = value.getReader();
1360
+ if (!reader) reader = guardOperation(state, () => value.getReader());
1266
1361
  const {
1267
1362
  done,
1268
1363
  value: chunk
1269
- } = await reader.read();
1270
- done ? controller.close() : controller.enqueue(guardFailures(chunk, state));
1364
+ } = await guardOperation(state, () => reader.read());
1365
+ done ? controller.close() : controller.enqueue(guardOperation(state, () => guardFailures(chunk, state)));
1271
1366
  } catch (error) {
1272
- controller.error(sanitizeServerError(error));
1367
+ controller.error(guardOperation(state, () => sanitizeServerError(error)));
1273
1368
  }
1274
1369
  },
1275
1370
  cancel(reason) {
1276
1371
  finished = true;
1277
- return reader ? reader.cancel(reason) : value.cancel(reason);
1372
+ return guardOperation(state, () => reader ? reader.cancel(reason) : value.cancel(reason));
1278
1373
  }
1279
1374
  });
1280
1375
  if (gate) gate.onOpen(close);
@@ -1282,9 +1377,10 @@ function enterGuard(value, state) {
1282
1377
  return guardedStream;
1283
1378
  }
1284
1379
  if (typeof value.then === "function") {
1285
- const guardedPromise = Promise.resolve(value).then(resolved => guardFailures(resolved, state), error => {
1286
- throw sanitizeServerError(error);
1380
+ const guardedPromise = Promise.resolve(value).then(resolved => guardOperation(state, () => guardFailures(resolved, state)), error => {
1381
+ throw guardOperation(state, () => sanitizeServerError(error));
1287
1382
  });
1383
+ guardedPromise.catch(() => {});
1288
1384
  state.seen.set(value, guardedPromise);
1289
1385
  return guardedPromise;
1290
1386
  }
@@ -1293,13 +1389,13 @@ function enterGuard(value, state) {
1293
1389
  const gate = state.gate;
1294
1390
  const guardedIterable = {
1295
1391
  [Symbol.asyncIterator]() {
1296
- const iterator = source[Symbol.asyncIterator]();
1392
+ const iterator = guardOperation(state, () => source[Symbol.asyncIterator]());
1297
1393
  let finished = false;
1298
1394
  const close = () => {
1299
1395
  if (finished) return;
1300
1396
  finished = true;
1301
1397
  try {
1302
- const returned = iterator.return && iterator.return();
1398
+ const returned = iterator.return && guardOperation(state, () => iterator.return());
1303
1399
  if (returned && typeof returned.then === "function") returned.then(undefined, () => {});
1304
1400
  } catch {}
1305
1401
  };
@@ -1307,17 +1403,17 @@ function enterGuard(value, state) {
1307
1403
  const step = () => finished ? Promise.resolve({
1308
1404
  done: true,
1309
1405
  value: undefined
1310
- }) : iterator.next().then(step => {
1406
+ }) : guardOperation(state, () => iterator.next()).then(step => {
1311
1407
  if (step.done) {
1312
1408
  finished = true;
1313
1409
  return step;
1314
1410
  }
1315
1411
  return {
1316
1412
  done: false,
1317
- value: guardFailures(step.value, state)
1413
+ value: guardOperation(state, () => guardFailures(step.value, state))
1318
1414
  };
1319
1415
  }, error => {
1320
- throw sanitizeServerError(error);
1416
+ throw guardOperation(state, () => sanitizeServerError(error));
1321
1417
  });
1322
1418
  return {
1323
1419
  next: () => finished || !gate || gate.wantsMore() ? step() : gate.awaitDemand().then(step),
@@ -1334,6 +1430,11 @@ function enterGuard(value, state) {
1334
1430
  state.seen.set(value, guardedIterable);
1335
1431
  return guardedIterable;
1336
1432
  }
1433
+ if (state.scope && typeof value[Symbol.iterator] === "function" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set) && !ArrayBuffer.isView(value)) {
1434
+ const scopedIterable = scopeDeferredResult(value, state.scope);
1435
+ state.seen.set(value, scopedIterable);
1436
+ return scopedIterable;
1437
+ }
1337
1438
  if (Array.isArray(value)) {
1338
1439
  const next = value.slice();
1339
1440
  state.seen.set(value, next);
@@ -1357,16 +1458,20 @@ function enterGuard(value, state) {
1357
1458
  return value;
1358
1459
  }
1359
1460
  const descriptors = Object.getOwnPropertyDescriptors(value);
1461
+ for (const key of Object.keys(descriptors)) {
1462
+ descriptors[key].configurable = true;
1463
+ if ("value" in descriptors[key]) descriptors[key].writable = true;
1464
+ }
1360
1465
  const next = Object.create(prototype, descriptors);
1361
1466
  state.seen.set(value, next);
1362
- return new Frame(OBJECT, value, next, Object.keys(descriptors), descriptors);
1467
+ return new Frame(OBJECT, value, next, Object.keys(value), descriptors);
1363
1468
  }
1364
1469
  function keepGuarded(value, next, changed, state) {
1365
1470
  if (changed || state.cyclic.has(value)) return next;
1366
1471
  state.seen.set(value, value);
1367
1472
  return value;
1368
1473
  }
1369
- function serializeResponseStream(value, codecOptions, signal) {
1474
+ function serializeResponseStream(value, codecOptions, signal, scope) {
1370
1475
  let closed = false;
1371
1476
  let streamController = null;
1372
1477
  let demandWaiters = null;
@@ -1385,11 +1490,13 @@ function serializeResponseStream(value, codecOptions, signal) {
1385
1490
  if (closed) close();else sourceClosers.add(close);
1386
1491
  }
1387
1492
  };
1388
- value = guardFailures(value, {
1493
+ const guardState = {
1389
1494
  seen: new WeakMap(),
1390
1495
  cyclic: new WeakSet(),
1391
- gate
1392
- });
1496
+ gate,
1497
+ scope
1498
+ };
1499
+ value = guardOperation(guardState, () => guardFailures(value, guardState));
1393
1500
  let cancelSerialize = null;
1394
1501
  let onAbort = null;
1395
1502
  const finishSource = () => {
@@ -1470,14 +1577,14 @@ function serializeResponseStream(value, codecOptions, signal) {
1470
1577
  }
1471
1578
  });
1472
1579
  }
1473
- function serializedResponse(value, headers, codec, signal) {
1580
+ function serializedResponse(value, headers, codec, signal, scope) {
1474
1581
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
1475
1582
  headers.set("Content-Type", "text/plain");
1476
- return new Response(serializeResponseStream(value, codec, signal), {
1583
+ return new Response(serializeResponseStream(value, codec, signal, scope), {
1477
1584
  headers
1478
1585
  });
1479
1586
  }
1480
- function encodeResult(value, headers, status, codec, signal) {
1587
+ function encodeResult(value, headers, status, codec, signal, scope) {
1481
1588
  if (NULL_BODY_STATUSES.has(status)) {
1482
1589
  if (value === undefined || value === null) {
1483
1590
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Void);
@@ -1488,7 +1595,7 @@ function encodeResult(value, headers, status, codec, signal) {
1488
1595
  }
1489
1596
  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.`);
1490
1597
  headers.set(ERROR_HEADER, encodeErrorHeaderValue(error.message));
1491
- return encodeResult(error, headers, 500, codec, signal);
1598
+ return encodeResult(error, headers, 500, codec, signal, scope);
1492
1599
  }
1493
1600
  const direct = getHeadersAndBody(value);
1494
1601
  if (direct) {
@@ -1508,10 +1615,12 @@ function encodeResult(value, headers, status, codec, signal) {
1508
1615
  });
1509
1616
  }
1510
1617
  try {
1511
- if (isJSONSafe(value)) {
1618
+ const jsonSafe = scope ? scope(() => isJSONSafe(value)) : isJSONSafe(value);
1619
+ if (jsonSafe) {
1512
1620
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Json);
1513
1621
  headers.set("Content-Type", "application/json");
1514
- return new Response(JSON.stringify(value), {
1622
+ const body = scope ? scope(() => JSON.stringify(value)) : JSON.stringify(value);
1623
+ return new Response(body, {
1515
1624
  status,
1516
1625
  headers
1517
1626
  });
@@ -1519,7 +1628,7 @@ function encodeResult(value, headers, status, codec, signal) {
1519
1628
  } catch {
1520
1629
  }
1521
1630
  try {
1522
- const response = serializedResponse(value, headers, codec, signal);
1631
+ const response = serializedResponse(value, headers, codec, signal, scope);
1523
1632
  return status === 200 ? response : new Response(response.body, {
1524
1633
  status,
1525
1634
  headers
@@ -1617,8 +1726,17 @@ function forbiddenResponse() {
1617
1726
  }
1618
1727
  }));
1619
1728
  }
1729
+ function nativePromise(value) {
1730
+ if (value instanceof Promise) return value;
1731
+ try {
1732
+ if (Object.prototype.toString.call(value) === "[object Promise]") return Promise.prototype.then.call(value, value => value);
1733
+ } catch {}
1734
+ }
1620
1735
  async function handleServerFunctionRequest(request, options = {}) {
1621
- const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
1736
+ const codec = {
1737
+ ...(options.codec !== undefined ? options.codec : getServerFunctionsCodec())
1738
+ };
1739
+ codec.serializeErrorStacks ??= DEV;
1622
1740
  const url = new URL(request.url);
1623
1741
  const method = request.method;
1624
1742
  const address = resolveAddress(url);
@@ -1677,7 +1795,15 @@ async function handleServerFunctionRequest(request, options = {}) {
1677
1795
  return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1678
1796
  }
1679
1797
  if (!(declared > 0)) {
1680
- const bounded = await bufferBodyWithin(request, bodySizeLimit);
1798
+ let bounded;
1799
+ try {
1800
+ bounded = await bufferBodyWithin(request, bodySizeLimit);
1801
+ } catch {
1802
+ const response = new Response(DEV ? "Malformed server function arguments" : null, {
1803
+ status: 400
1804
+ });
1805
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1806
+ }
1681
1807
  if (bounded === null) {
1682
1808
  const response = new Response(DEV ? "Server function request body exceeds the configured bodySizeLimit" : null, {
1683
1809
  status: 413
@@ -1687,16 +1813,30 @@ async function handleServerFunctionRequest(request, options = {}) {
1687
1813
  request = bounded;
1688
1814
  }
1689
1815
  }
1690
- let event = options.createEvent ? options.createEvent(request) : {
1691
- request,
1692
- locals: {}
1693
- };
1694
- if (typeof event?.then === "function") event = await event;
1816
+ let event;
1817
+ try {
1818
+ event = options.createEvent ? options.createEvent(request) : {
1819
+ request,
1820
+ locals: {}
1821
+ };
1822
+ const promised = nativePromise(event);
1823
+ if (promised) event = await promised;
1824
+ } catch (error) {
1825
+ const safe = sanitizeServerError(error);
1826
+ const message = safe instanceof Error ? safe.message : String(safe);
1827
+ const headers = new Headers();
1828
+ headers.set(ERROR_HEADER, boundedErrorHeaderValue(message));
1829
+ const response = scripted ? encodeResult(safe, headers, 500, codec, request.signal) : new Response(DEV ? message : null, {
1830
+ status: 500
1831
+ });
1832
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1833
+ }
1695
1834
  const refuseCommitted = raw => {
1696
1835
  const response = commitEventResponse(raw, event);
1697
1836
  return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1698
1837
  };
1699
1838
  const provide = options.provideEvent || provideEvent;
1839
+ const scope = run => provide(event, run);
1700
1840
  const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
1701
1841
  const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
1702
1842
  const wrapInvocation = options.wrapInvocation !== undefined ? options.wrapInvocation : config.wrapInvocation;
@@ -1747,7 +1887,8 @@ async function handleServerFunctionRequest(request, options = {}) {
1747
1887
  const headers = new Headers();
1748
1888
  const dispatch = async () => {
1749
1889
  try {
1750
- let result = await provide(event, async () => {
1890
+ let invocations = 0;
1891
+ const invokeOnce = async () => {
1751
1892
  INVOCATIONS.set(event, {
1752
1893
  id: functionId
1753
1894
  });
@@ -1759,7 +1900,16 @@ async function handleServerFunctionRequest(request, options = {}) {
1759
1900
  request,
1760
1901
  direct: false
1761
1902
  }) : run();
1903
+ };
1904
+ let result = await provide(event, () => {
1905
+ if (++invocations > 1) {
1906
+ 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.");
1907
+ }
1908
+ return invokeOnce();
1762
1909
  });
1910
+ if (invocations !== 1) {
1911
+ 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.");
1912
+ }
1763
1913
  if (transformResult) {
1764
1914
  result = await transformResult(event, result, flightContext);
1765
1915
  }
@@ -1813,10 +1963,10 @@ async function handleServerFunctionRequest(request, options = {}) {
1813
1963
  if (!scripted) {
1814
1964
  if (handleNoJS) return handleNoJS(result ?? metadata, request, parsed);
1815
1965
  if (result instanceof Response) return result;
1816
- return encodeResult(result, headers, status, codec, request.signal);
1966
+ return encodeResult(result, headers, status, codec, request.signal, scope);
1817
1967
  }
1818
1968
  if (status === 304) warnScripted304(functionId);
1819
- return encodeResult(result, headers, status, codec, request.signal);
1969
+ return encodeResult(result, headers, status, codec, request.signal, scope);
1820
1970
  } catch (x) {
1821
1971
  const respondThrown = value => {
1822
1972
  const safe = sanitizeServerError(value);
@@ -1829,7 +1979,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1829
1979
  }
1830
1980
  const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
1831
1981
  headers.set(ERROR_HEADER, boundedErrorHeaderValue(error));
1832
- return encodeResult(safe, headers, 500, codec, request.signal);
1982
+ return encodeResult(safe, headers, 500, codec, request.signal, scope);
1833
1983
  };
1834
1984
  if (x instanceof Response || isResponseEnvelope(x)) {
1835
1985
  if (transformResult) {
@@ -1893,7 +2043,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1893
2043
  if (x instanceof Response) return x;
1894
2044
  }
1895
2045
  if (scripted && status === 304) warnScripted304(functionId);
1896
- return encodeResult(x, headers, status, codec, request.signal);
2046
+ return encodeResult(x, headers, status, codec, request.signal, scope);
1897
2047
  }
1898
2048
  return respondThrown(x);
1899
2049
  }
package/types/client.d.ts CHANGED
@@ -136,7 +136,7 @@ export declare function claimElement<T extends Element>(node: T): T;
136
136
  export declare function setAttribute(node: Element, name: string, value: string): void;
137
137
  export declare function setAttributeNS(node: Element, namespace: string, name: string, value: string): void;
138
138
  export declare function className(node: Element, value: JSX.ClassValue, prev?: JSX.ClassValue): void;
139
- export declare function addEvent(node: Element, name: string, handler: EventListener | EventListenerObject | (EventListenerObject & AddEventListenerOptions), delegate: boolean): void;
139
+ export declare function addEvent(node: Element, name: string, handler: EventListener | EventListenerObject | (EventListenerObject & AddEventListenerOptions), delegate: boolean): EventListener | EventListenerObject | void;
140
140
  export declare function style(node: Element, value: {
141
141
  [k: string]: string;
142
142
  }, prev?: {
@@ -149,6 +149,7 @@ export declare function applyRef<T extends Element = Element>(r: ((element: NoIn
149
149
  export declare function ref(fn: () => ((element: Element) => void) | ((element: Element) => void)[], element: Element): void;
150
150
  /** Compiler-emitted primitive; not for hand-written code. @internal */
151
151
  export declare function scope<T extends () => any>(fn: T): T;
152
+ export declare function getInsertionParent(): Node | undefined;
152
153
  export declare function installHydrationRuntime(): void;
153
154
  /**
154
155
  * Compiler-emitted primitive; not for hand-written code.
@@ -15,4 +15,6 @@ declare const Namespaces: Record<string, string>;
15
15
  declare const VoidElements: Set<string>;
16
16
  declare const RawTextElements: Set<string>;
17
17
  declare const DOMElements: Set<string>;
18
- export { DOMWithState, ChildProperties, DelegatedEvents, SVGElements, MathMLElements, VoidElements, RawTextElements, Namespaces, DOMElements, $$SLOT, $$HOST };
18
+ declare const COMPOSED_BODY_FRAMING: ReadonlySet<string>;
19
+ declare function isHttpNavigationTarget(target: string): boolean;
20
+ export { DOMWithState, ChildProperties, DelegatedEvents, SVGElements, MathMLElements, VoidElements, RawTextElements, Namespaces, DOMElements, $$SLOT, $$HOST, COMPOSED_BODY_FRAMING, isHttpNavigationTarget };