@reckona/mreact-server 0.0.179 → 0.0.181

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.
package/src/flight.ts CHANGED
@@ -1,10 +1,10 @@
1
+ import { createCacheScope, runWithCacheScope } from "@reckona/mreact-compat/internal";
1
2
  import { getNativeFlight } from "./native-flight.js";
2
3
 
3
4
  /** Symbol tag used to identify client references in serialized Flight values. */
4
5
  export const CLIENT_REFERENCE_TYPE = Symbol.for("modular.react.client_reference");
5
6
  /** Symbol tag used to identify server references in serialized Flight values. */
6
7
  export const SERVER_REFERENCE_TYPE = Symbol.for("modular.react.server_reference");
7
- const CACHE_SCOPE_SYMBOL = Symbol.for("modular.react.cache_scope");
8
8
 
9
9
  const REACT_COMPAT_ELEMENT_TYPE = Symbol.for("react.transitional.element");
10
10
  const REACT_COMPAT_FRAGMENT_TYPE = Symbol.for("react.fragment");
@@ -88,6 +88,10 @@ export interface ServerActionHandlerOptions {
88
88
  }
89
89
 
90
90
  const DEFAULT_MAX_BODY_BYTES = 1024 * 1024;
91
+ const MAX_SERVER_ACTION_ARGUMENT_DEPTH = 64;
92
+ const MAX_SERVER_ACTION_ARGUMENT_ARRAY_LENGTH = 2_000;
93
+ const MAX_SERVER_ACTION_ARGUMENT_OBJECT_KEYS = 200;
94
+ const SERVER_ACTION_FORBIDDEN_JSON_KEYS = new Set(["__proto__", "constructor", "prototype"]);
91
95
 
92
96
  /** Options for embedding a serialized Flight response in a script tag. */
93
97
  export interface FlightScriptOptions {
@@ -274,8 +278,38 @@ export type FlightTypedArrayName =
274
278
  | "BigInt64Array"
275
279
  | "BigUint64Array";
276
280
 
277
- const reactFlightBinaryRowTags = ["A", "O", "o", "U", "S", "s", "L", "l", "G", "g", "M", "m", "V"] as const;
278
- const reactFlightRowTags = ["C", "D", "E", "F", "H", "I", "J", "N", "P", "R", "T", "W", "X", "x", "r"] as const;
281
+ const reactFlightBinaryRowTags = [
282
+ "A",
283
+ "O",
284
+ "o",
285
+ "U",
286
+ "S",
287
+ "s",
288
+ "L",
289
+ "l",
290
+ "G",
291
+ "g",
292
+ "M",
293
+ "m",
294
+ "V",
295
+ ] as const;
296
+ const reactFlightRowTags = [
297
+ "C",
298
+ "D",
299
+ "E",
300
+ "F",
301
+ "H",
302
+ "I",
303
+ "J",
304
+ "N",
305
+ "P",
306
+ "R",
307
+ "T",
308
+ "W",
309
+ "X",
310
+ "x",
311
+ "r",
312
+ ] as const;
279
313
  const reactFlightModelTokens = [
280
314
  "$",
281
315
  "$$",
@@ -328,12 +362,6 @@ interface FlightSerializationState {
328
362
  serverReferenceIndexes: Map<string, number>;
329
363
  }
330
364
 
331
- interface ServerCacheScope {
332
- functionCaches: WeakMap<(...args: never[]) => unknown, unknown>;
333
- controller: AbortController;
334
- ownerStack: string[];
335
- }
336
-
337
365
  /** Creates a runtime client reference marker for a module export. */
338
366
  export function createClientReference(
339
367
  moduleId: string,
@@ -431,8 +459,7 @@ export function createServerActionHandler(
431
459
  const allowedActionKeys = options.allowedActions?.map((reference) =>
432
460
  serverActionKey(reference.moduleId, reference.exportName),
433
461
  );
434
- const allowedActionSet =
435
- allowedActionKeys === undefined ? undefined : new Set(allowedActionKeys);
462
+ const allowedActionSet = allowedActionKeys === undefined ? undefined : new Set(allowedActionKeys);
436
463
 
437
464
  return async (request: Request): Promise<Response> => {
438
465
  if (request.method !== "POST") {
@@ -491,7 +518,20 @@ export function createServerActionHandler(
491
518
  const action = getServerAction(actionEntry);
492
519
  const validateArgs = getServerActionArgsValidator(actionEntry);
493
520
  const boundArgs = Array.isArray(payload.bound) ? payload.bound : [];
494
- const args = [...boundArgs, ...(Array.isArray(payload.args) ? payload.args : [])];
521
+ const extraArgs = Array.isArray(payload.args) ? payload.args : [];
522
+ const argsStructure = validateServerActionJsonArgumentStructure(boundArgs, extraArgs);
523
+
524
+ if (!argsStructure.valid) {
525
+ return jsonResponse(
526
+ {
527
+ ok: false,
528
+ error: "Invalid server action argument structure.",
529
+ },
530
+ 400,
531
+ );
532
+ }
533
+
534
+ const args = [...boundArgs, ...extraArgs];
495
535
  const validationResult = validateArgs?.(args);
496
536
 
497
537
  if (validationResult !== undefined && validationResult !== true) {
@@ -507,11 +547,7 @@ export function createServerActionHandler(
507
547
  );
508
548
  }
509
549
 
510
- const authorizationResult = await options.authorize?.(
511
- request,
512
- reference,
513
- args,
514
- );
550
+ const authorizationResult = await options.authorize?.(request, reference, args);
515
551
 
516
552
  if (authorizationResult !== undefined && authorizationResult !== true) {
517
553
  return jsonResponse(
@@ -586,9 +622,10 @@ export function toReactFlightRows(response: FlightResponse): string {
586
622
  rows.push(
587
623
  `${formatReactFlightId(wireId)}:F${JSON.stringify({
588
624
  id: serverActionKey(reference.moduleId, reference.exportName),
589
- bound: reference.bound === undefined
590
- ? null
591
- : reference.bound.map((value) => encodeReactFlightModel(value, state)),
625
+ bound:
626
+ reference.bound === undefined
627
+ ? null
628
+ : reference.bound.map((value) => encodeReactFlightModel(value, state)),
592
629
  name: reference.exportName,
593
630
  })}`,
594
631
  );
@@ -643,7 +680,9 @@ export function fromReactFlightRows(rows: string): FlightResponse {
643
680
  }
644
681
 
645
682
  if (row.tag === "F") {
646
- serverReferences.push(parseReactFlightServerReference(row.id, row.payload, modelChunks, errorChunks));
683
+ serverReferences.push(
684
+ parseReactFlightServerReference(row.id, row.payload, modelChunks, errorChunks),
685
+ );
647
686
  continue;
648
687
  }
649
688
 
@@ -693,10 +732,7 @@ export function fromReactFlightRows(rows: string): FlightResponse {
693
732
  }
694
733
 
695
734
  /** Merges additional React Flight row text into an existing Flight response. */
696
- export function mergeReactFlightRows(
697
- response: FlightResponse,
698
- rows: string,
699
- ): FlightResponse {
735
+ export function mergeReactFlightRows(response: FlightResponse, rows: string): FlightResponse {
700
736
  // Issue 081 note: same finding as `fromReactFlightRows` — the native
701
737
  // merge path is slower than the JS one given the double-JSON tax.
702
738
  const modelChunks = new Map<number, unknown>();
@@ -713,7 +749,9 @@ export function mergeReactFlightRows(
713
749
  }
714
750
 
715
751
  if (row.tag === "F") {
716
- serverReferences.push(parseReactFlightServerReference(row.id, row.payload, modelChunks, errorChunks));
752
+ serverReferences.push(
753
+ parseReactFlightServerReference(row.id, row.payload, modelChunks, errorChunks),
754
+ );
717
755
  continue;
718
756
  }
719
757
 
@@ -779,9 +817,7 @@ export function renderFlightPreloadLinks(
779
817
  seen.add(chunk);
780
818
  return true;
781
819
  })
782
- .map((chunk) =>
783
- `<link rel="modulepreload" href="${escapeAttribute(chunk)}"${nonceAttribute}>`,
784
- )
820
+ .map((chunk) => `<link rel="modulepreload" href="${escapeAttribute(chunk)}"${nonceAttribute}>`)
785
821
  .join("");
786
822
  }
787
823
 
@@ -891,11 +927,7 @@ interface ReactFlightEncodingState {
891
927
  }
892
928
 
893
929
  function encodeReactFlightModel(model: FlightModel, state: ReactFlightEncodingState): unknown {
894
- if (
895
- model === null ||
896
- typeof model === "number" ||
897
- typeof model === "boolean"
898
- ) {
930
+ if (model === null || typeof model === "number" || typeof model === "boolean") {
899
931
  return model;
900
932
  }
901
933
 
@@ -973,7 +1005,9 @@ function encodeReactFlightModel(model: FlightModel, state: ReactFlightEncodingSt
973
1005
  if (model.kind === "error") {
974
1006
  const id = state.nextWireId;
975
1007
  state.nextWireId += 1;
976
- state.outlineRows.push(`${formatReactFlightId(id)}:E${JSON.stringify(encodeReactFlightError(model))}`);
1008
+ state.outlineRows.push(
1009
+ `${formatReactFlightId(id)}:E${JSON.stringify(encodeReactFlightError(model))}`,
1010
+ );
977
1011
  return `$Z${formatReactFlightId(id)}`;
978
1012
  }
979
1013
 
@@ -1005,10 +1039,7 @@ function encodeReactFlightModel(model: FlightModel, state: ReactFlightEncodingSt
1005
1039
  return encodeReactFlightProps(model, state);
1006
1040
  }
1007
1041
 
1008
- function allocateReactFlightOutlineRow(
1009
- state: ReactFlightEncodingState,
1010
- payload: unknown,
1011
- ): number {
1042
+ function allocateReactFlightOutlineRow(state: ReactFlightEncodingState, payload: unknown): number {
1012
1043
  const id = state.nextWireId;
1013
1044
  state.nextWireId += 1;
1014
1045
  state.outlineRows.push(`${formatReactFlightId(id)}:${JSON.stringify(payload)}`);
@@ -1037,10 +1068,7 @@ function encodeReactFlightProps(
1037
1068
  return Object.fromEntries(
1038
1069
  Object.entries(props)
1039
1070
  .filter((entry): entry is [string, FlightModel] => entry[1] !== undefined)
1040
- .map(([key, value]) => [
1041
- key,
1042
- encodeReactFlightModel(value, state),
1043
- ]),
1071
+ .map(([key, value]) => [key, encodeReactFlightModel(value, state)]),
1044
1072
  );
1045
1073
  }
1046
1074
 
@@ -1089,7 +1117,7 @@ function parseReactFlightRow(line: string): ReactFlightRow {
1089
1117
  }
1090
1118
 
1091
1119
  function looksLikeUnsupportedReactFlightTag(tag: string, body: string): boolean {
1092
- return /^[A-Z]$/.test(tag) && (body[1] === "{" || body[1] === "[" || body[1] === "\"");
1120
+ return /^[A-Z]$/.test(tag) && (body[1] === "{" || body[1] === "[" || body[1] === '"');
1093
1121
  }
1094
1122
 
1095
1123
  function parseReactFlightTextChunk(payload: string): string {
@@ -1207,7 +1235,9 @@ function createReactFlightBinaryModel(
1207
1235
  };
1208
1236
  }
1209
1237
 
1210
- function getReactFlightTypedArrayName(tag: Exclude<ReactFlightBinaryRowTag, "A" | "V">): FlightTypedArrayName {
1238
+ function getReactFlightTypedArrayName(
1239
+ tag: Exclude<ReactFlightBinaryRowTag, "A" | "V">,
1240
+ ): FlightTypedArrayName {
1211
1241
  switch (tag) {
1212
1242
  case "O":
1213
1243
  return "Int8Array";
@@ -1350,11 +1380,7 @@ function decodeReactFlightModel(
1350
1380
  context: FlightDecodeContext = createFlightDecodeContext(),
1351
1381
  ): FlightModel {
1352
1382
  if (depth > MAX_FLIGHT_DECODE_DEPTH) flightTooDeep();
1353
- if (
1354
- value === null ||
1355
- typeof value === "number" ||
1356
- typeof value === "boolean"
1357
- ) {
1383
+ if (value === null || typeof value === "number" || typeof value === "boolean") {
1358
1384
  return value;
1359
1385
  }
1360
1386
 
@@ -1463,10 +1489,18 @@ function decodeReactFlightString(
1463
1489
  }
1464
1490
 
1465
1491
  if (/^\$Q[0-9a-f]+$/i.test(value)) {
1466
- const decoded = decodeReactFlightChunk(value.slice(2), modelChunks, errorChunks, depth + 1, context);
1492
+ const decoded = decodeReactFlightChunk(
1493
+ value.slice(2),
1494
+ modelChunks,
1495
+ errorChunks,
1496
+ depth + 1,
1497
+ context,
1498
+ );
1467
1499
  const entries = Array.isArray(decoded)
1468
1500
  ? decoded.map((entry): [FlightModel, FlightModel] =>
1469
- Array.isArray(entry) ? [entry[0] ?? { kind: "undefined" }, entry[1] ?? { kind: "undefined" }] : [entry, { kind: "undefined" }],
1501
+ Array.isArray(entry)
1502
+ ? [entry[0] ?? { kind: "undefined" }, entry[1] ?? { kind: "undefined" }]
1503
+ : [entry, { kind: "undefined" }],
1470
1504
  )
1471
1505
  : [];
1472
1506
 
@@ -1477,7 +1511,13 @@ function decodeReactFlightString(
1477
1511
  }
1478
1512
 
1479
1513
  if (/^\$W[0-9a-f]+$/i.test(value)) {
1480
- const decoded = decodeReactFlightChunk(value.slice(2), modelChunks, errorChunks, depth + 1, context);
1514
+ const decoded = decodeReactFlightChunk(
1515
+ value.slice(2),
1516
+ modelChunks,
1517
+ errorChunks,
1518
+ depth + 1,
1519
+ context,
1520
+ );
1481
1521
 
1482
1522
  return {
1483
1523
  kind: "set",
@@ -1486,7 +1526,13 @@ function decodeReactFlightString(
1486
1526
  }
1487
1527
 
1488
1528
  if (/^\$K[0-9a-f]+$/i.test(value)) {
1489
- const decoded = decodeReactFlightChunk(value.slice(2), modelChunks, errorChunks, depth + 1, context);
1529
+ const decoded = decodeReactFlightChunk(
1530
+ value.slice(2),
1531
+ modelChunks,
1532
+ errorChunks,
1533
+ depth + 1,
1534
+ context,
1535
+ );
1490
1536
  const entries = Array.isArray(decoded)
1491
1537
  ? decoded.flatMap((entry): [string, FlightModel][] =>
1492
1538
  Array.isArray(entry) && typeof entry[0] === "string"
@@ -1502,7 +1548,13 @@ function decodeReactFlightString(
1502
1548
  }
1503
1549
 
1504
1550
  if (/^\$i[0-9a-f]+$/i.test(value)) {
1505
- const decoded = decodeReactFlightChunk(value.slice(2), modelChunks, errorChunks, depth + 1, context);
1551
+ const decoded = decodeReactFlightChunk(
1552
+ value.slice(2),
1553
+ modelChunks,
1554
+ errorChunks,
1555
+ depth + 1,
1556
+ context,
1557
+ );
1506
1558
 
1507
1559
  return {
1508
1560
  kind: "iterable",
@@ -1511,11 +1563,13 @@ function decodeReactFlightString(
1511
1563
  }
1512
1564
 
1513
1565
  if (/^\$Z[0-9a-f]+$/i.test(value)) {
1514
- return errorChunks.get(parseReactFlightId(value.slice(2))) ?? {
1515
- kind: "error",
1516
- name: "Error",
1517
- message: "Unknown React Flight error.",
1518
- };
1566
+ return (
1567
+ errorChunks.get(parseReactFlightId(value.slice(2))) ?? {
1568
+ kind: "error",
1569
+ name: "Error",
1570
+ message: "Unknown React Flight error.",
1571
+ }
1572
+ );
1519
1573
  }
1520
1574
 
1521
1575
  if (value === "$Y" || value.startsWith("$E")) {
@@ -1634,10 +1688,7 @@ function encodeReactFlightError(model: FlightErrorModel): Record<string, unknown
1634
1688
 
1635
1689
  function isFlightErrorModel(model: FlightModel): model is FlightErrorModel {
1636
1690
  return (
1637
- typeof model === "object" &&
1638
- model !== null &&
1639
- !Array.isArray(model) &&
1640
- model.kind === "error"
1691
+ typeof model === "object" && model !== null && !Array.isArray(model) && model.kind === "error"
1641
1692
  );
1642
1693
  }
1643
1694
 
@@ -1670,10 +1721,7 @@ async function serializeFlightValue(
1670
1721
  return { kind: "undefined" };
1671
1722
  }
1672
1723
 
1673
- if (
1674
- typeof awaited === "string" ||
1675
- typeof awaited === "boolean"
1676
- ) {
1724
+ if (typeof awaited === "string" || typeof awaited === "boolean") {
1677
1725
  return awaited;
1678
1726
  }
1679
1727
 
@@ -1728,10 +1776,13 @@ async function serializeFlightValue(
1728
1776
  return {
1729
1777
  kind: "map",
1730
1778
  entries: await Promise.all(
1731
- Array.from(awaited.entries()).map(async ([key, value]) => [
1732
- await serializeFlightValue(key, state, depth + 1),
1733
- await serializeFlightValue(value, state, depth + 1),
1734
- ] as [FlightModel, FlightModel]),
1779
+ Array.from(awaited.entries()).map(
1780
+ async ([key, value]) =>
1781
+ [
1782
+ await serializeFlightValue(key, state, depth + 1),
1783
+ await serializeFlightValue(value, state, depth + 1),
1784
+ ] as [FlightModel, FlightModel],
1785
+ ),
1735
1786
  ),
1736
1787
  };
1737
1788
  }
@@ -1749,10 +1800,10 @@ async function serializeFlightValue(
1749
1800
  return {
1750
1801
  kind: "form-data",
1751
1802
  entries: await Promise.all(
1752
- Array.from(awaited.entries()).map(async ([key, value]) => [
1753
- key,
1754
- await serializeFlightValue(value, state, depth + 1),
1755
- ] as [string, FlightModel]),
1803
+ Array.from(awaited.entries()).map(
1804
+ async ([key, value]) =>
1805
+ [key, await serializeFlightValue(value, state, depth + 1)] as [string, FlightModel],
1806
+ ),
1756
1807
  ),
1757
1808
  };
1758
1809
  }
@@ -1829,10 +1880,9 @@ async function serializeProps(
1829
1880
  depth: number,
1830
1881
  ): Promise<Record<string, FlightModel>> {
1831
1882
  const entries = await Promise.all(
1832
- Object.entries(props).map(async ([key, value]) => [
1833
- key,
1834
- await serializeFlightValue(value, state, depth + 1),
1835
- ] as const),
1883
+ Object.entries(props).map(
1884
+ async ([key, value]) => [key, await serializeFlightValue(value, state, depth + 1)] as const,
1885
+ ),
1836
1886
  );
1837
1887
 
1838
1888
  return Object.fromEntries(entries);
@@ -1845,18 +1895,14 @@ async function serializeObject(
1845
1895
  ): Promise<FlightObjectModel> {
1846
1896
  return Object.fromEntries(
1847
1897
  await Promise.all(
1848
- Object.entries(object).map(async ([key, value]) => [
1849
- key,
1850
- await serializeFlightValue(value, state, depth + 1),
1851
- ] as const),
1898
+ Object.entries(object).map(
1899
+ async ([key, value]) => [key, await serializeFlightValue(value, state, depth + 1)] as const,
1900
+ ),
1852
1901
  ),
1853
1902
  ) as FlightObjectModel;
1854
1903
  }
1855
1904
 
1856
- function getClientReferenceId(
1857
- reference: ClientReference,
1858
- state: FlightSerializationState,
1859
- ): number {
1905
+ function getClientReferenceId(reference: ClientReference, state: FlightSerializationState): number {
1860
1906
  const key = `${reference.moduleId}:${reference.exportName}`;
1861
1907
  const existing = state.clientReferenceIndexes.get(key);
1862
1908
 
@@ -1879,9 +1925,10 @@ async function getServerReferenceId(
1879
1925
  reference: ServerReference,
1880
1926
  state: FlightSerializationState,
1881
1927
  ): Promise<number> {
1882
- const serializedBound = reference.bound === undefined
1883
- ? undefined
1884
- : await Promise.all(reference.bound.map((value) => serializeFlightValue(value, state, 0)));
1928
+ const serializedBound =
1929
+ reference.bound === undefined
1930
+ ? undefined
1931
+ : await Promise.all(reference.bound.map((value) => serializeFlightValue(value, state, 0)));
1885
1932
  const key = `${reference.moduleId}:${reference.exportName}:${JSON.stringify(serializedBound ?? null)}`;
1886
1933
  const existing = state.serverReferenceIndexes.get(key);
1887
1934
 
@@ -1977,6 +2024,62 @@ async function readServerActionPayload(
1977
2024
  }
1978
2025
  }
1979
2026
 
2027
+ function validateServerActionJsonArgumentStructure(
2028
+ boundArgs: readonly unknown[],
2029
+ args: readonly unknown[],
2030
+ ): { valid: true } | { valid: false } {
2031
+ const stack: { depth: number; value: unknown }[] = [
2032
+ { depth: 0, value: boundArgs },
2033
+ { depth: 0, value: args },
2034
+ ];
2035
+ const seen = new WeakSet<object>();
2036
+
2037
+ while (stack.length > 0) {
2038
+ const current = stack.pop();
2039
+ if (current === undefined) {
2040
+ continue;
2041
+ }
2042
+
2043
+ if (current.depth > MAX_SERVER_ACTION_ARGUMENT_DEPTH) {
2044
+ return { valid: false };
2045
+ }
2046
+
2047
+ if (current.value === null || typeof current.value !== "object") {
2048
+ continue;
2049
+ }
2050
+
2051
+ if (seen.has(current.value)) {
2052
+ continue;
2053
+ }
2054
+ seen.add(current.value);
2055
+
2056
+ if (Array.isArray(current.value)) {
2057
+ if (current.value.length > MAX_SERVER_ACTION_ARGUMENT_ARRAY_LENGTH) {
2058
+ return { valid: false };
2059
+ }
2060
+
2061
+ for (const item of current.value) {
2062
+ stack.push({ depth: current.depth + 1, value: item });
2063
+ }
2064
+ continue;
2065
+ }
2066
+
2067
+ const entries = Object.entries(current.value);
2068
+ if (entries.length > MAX_SERVER_ACTION_ARGUMENT_OBJECT_KEYS) {
2069
+ return { valid: false };
2070
+ }
2071
+
2072
+ for (const [key, value] of entries) {
2073
+ if (SERVER_ACTION_FORBIDDEN_JSON_KEYS.has(key)) {
2074
+ return { valid: false };
2075
+ }
2076
+ stack.push({ depth: current.depth + 1, value });
2077
+ }
2078
+ }
2079
+
2080
+ return { valid: true };
2081
+ }
2082
+
1980
2083
  function getServerAction(entry: ServerAction | ServerActionDescriptor): ServerAction {
1981
2084
  return typeof entry === "function" ? entry : entry.action;
1982
2085
  }
@@ -1999,49 +2102,9 @@ function getServerActionArgsValidator(
1999
2102
  }
2000
2103
 
2001
2104
  function runWithFlightCacheScope<T>(callback: () => T): T {
2002
- const previousScope = getGlobalCacheScope();
2003
- setGlobalCacheScope({
2004
- functionCaches: new WeakMap(),
2005
- controller: new AbortController(),
2006
- ownerStack: ["renderToFlightResponse"],
2007
- });
2008
-
2009
- try {
2010
- const result = callback();
2011
-
2012
- if (isPromiseLike(result)) {
2013
- return Promise.resolve(result).finally(() => {
2014
- setGlobalCacheScope(previousScope);
2015
- }) as T;
2016
- }
2017
-
2018
- setGlobalCacheScope(previousScope);
2019
- return result;
2020
- } catch (error) {
2021
- setGlobalCacheScope(previousScope);
2022
- throw error;
2023
- }
2024
- }
2025
-
2026
- function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
2027
- return (
2028
- (typeof value === "object" || typeof value === "function") &&
2029
- value !== null &&
2030
- typeof (value as { then?: unknown }).then === "function"
2031
- );
2032
- }
2033
-
2034
- function getGlobalCacheScope(): ServerCacheScope | undefined {
2035
- return (globalThis as { [CACHE_SCOPE_SYMBOL]?: ServerCacheScope })[CACHE_SCOPE_SYMBOL];
2036
- }
2037
-
2038
- function setGlobalCacheScope(scope: ServerCacheScope | undefined): void {
2039
- if (scope === undefined) {
2040
- delete (globalThis as { [CACHE_SCOPE_SYMBOL]?: ServerCacheScope })[CACHE_SCOPE_SYMBOL];
2041
- return;
2042
- }
2043
-
2044
- (globalThis as { [CACHE_SCOPE_SYMBOL]?: ServerCacheScope })[CACHE_SCOPE_SYMBOL] = scope;
2105
+ const scope = createCacheScope();
2106
+ scope.ownerStack.push("renderToFlightResponse");
2107
+ return runWithCacheScope(scope, callback);
2045
2108
  }
2046
2109
 
2047
2110
  function validateRequestOrigin(
@@ -2124,10 +2187,7 @@ function validateServerActionNonce(
2124
2187
 
2125
2188
  if (replayProtection.seen.has(nonce)) {
2126
2189
  return {
2127
- response: jsonResponse(
2128
- { ok: false, error: "Server action nonce was already used." },
2129
- 409,
2130
- ),
2190
+ response: jsonResponse({ ok: false, error: "Server action nonce was already used." }, 409),
2131
2191
  };
2132
2192
  }
2133
2193