@solidjs/web 2.0.0-rc.4 → 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.
Files changed (61) hide show
  1. package/dist/dev.cjs +163 -36
  2. package/dist/dev.js +161 -37
  3. package/dist/server.cjs +178 -37
  4. package/dist/server.js +177 -38
  5. package/dist/web.cjs +143 -36
  6. package/dist/web.js +141 -37
  7. package/frames/dist/client.cjs +41 -8
  8. package/frames/dist/client.dev.cjs +41 -8
  9. package/frames/dist/client.dev.js +41 -8
  10. package/frames/dist/client.js +41 -8
  11. package/frames/dist/server.cjs +497 -44
  12. package/frames/dist/server.js +497 -44
  13. package/package.json +2 -2
  14. package/serialization/dist/decode.cjs +4 -2
  15. package/serialization/dist/decode.js +4 -2
  16. package/serialization/dist/serialization.cjs +12 -8
  17. package/serialization/dist/serialization.js +12 -8
  18. package/serialization/types/index.d.ts +7 -0
  19. package/serialization/types/serializer-decode.d.ts +14 -1
  20. package/serialization/types/serializer.d.ts +7 -0
  21. package/serialization/types-cjs/index.d.cts +7 -0
  22. package/serialization/types-cjs/serializer-decode.d.cts +14 -1
  23. package/serialization/types-cjs/serializer.d.cts +7 -0
  24. package/server-functions/dist/client.cjs +164 -35
  25. package/server-functions/dist/client.js +161 -36
  26. package/server-functions/dist/server.cjs +899 -136
  27. package/server-functions/dist/server.dev.cjs +919 -136
  28. package/server-functions/dist/server.dev.js +915 -137
  29. package/server-functions/dist/server.js +895 -137
  30. package/types/client.d.ts +2 -1
  31. package/types/constants.d.ts +3 -1
  32. package/types/cookies.d.ts +6 -14
  33. package/types/frames/frame-client.d.ts +4 -0
  34. package/types/frames/serializer-decode.d.ts +14 -1
  35. package/types/frames/serializer.d.ts +7 -0
  36. package/types/jsx.d.ts +11 -16
  37. package/types/response.d.ts +11 -0
  38. package/types/serializer-decode.d.ts +14 -1
  39. package/types/serializer.d.ts +7 -0
  40. package/types/server-functions/client.d.ts +22 -2
  41. package/types/server-functions/flash.d.ts +9 -0
  42. package/types/server-functions/server.d.ts +131 -11
  43. package/types/server-functions/shared.d.ts +77 -14
  44. package/types/server-mock.d.ts +17 -2
  45. package/types/server.d.ts +16 -2
  46. package/types-cjs/client.d.cts +2 -1
  47. package/types-cjs/constants.d.cts +3 -1
  48. package/types-cjs/cookies.d.cts +6 -14
  49. package/types-cjs/frames/frame-client.d.cts +4 -0
  50. package/types-cjs/frames/serializer-decode.d.cts +14 -1
  51. package/types-cjs/frames/serializer.d.cts +7 -0
  52. package/types-cjs/jsx.d.cts +11 -16
  53. package/types-cjs/response.d.cts +11 -0
  54. package/types-cjs/serializer-decode.d.cts +14 -1
  55. package/types-cjs/serializer.d.cts +7 -0
  56. package/types-cjs/server-functions/client.d.cts +22 -2
  57. package/types-cjs/server-functions/flash.d.cts +9 -0
  58. package/types-cjs/server-functions/server.d.cts +131 -11
  59. package/types-cjs/server-functions/shared.d.cts +77 -14
  60. package/types-cjs/server-mock.d.cts +17 -2
  61. package/types-cjs/server.d.cts +16 -2
@@ -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]);
@@ -11,6 +21,8 @@ function isSafeError(value) {
11
21
  return !!(value && (typeof value === "object" || typeof value === "function") && value[SAFE_ERROR]);
12
22
  }
13
23
  const REVALIDATE_HEADER = "X-Revalidate";
24
+ const RESPONSE_HEADER_VALUE_LIMIT = 4096;
25
+ const NULL_BODY_STATUSES = new Set([204, 205, 304]);
14
26
 
15
27
  const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
16
28
  function getServerFunctionMetadata(fn) {
@@ -83,6 +95,7 @@ function decodeSafe(text) {
83
95
  }
84
96
  }
85
97
  function serializeCookie(name, value, options = {}) {
98
+ assertServableCookie(name, options);
86
99
  let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
87
100
  cookie += `; Path=${options.path === undefined ? "/" : options.path}`;
88
101
  if (options.domain) cookie += `; Domain=${options.domain}`;
@@ -90,12 +103,32 @@ function serializeCookie(name, value, options = {}) {
90
103
  if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
91
104
  if (options.httpOnly) cookie += "; HttpOnly";
92
105
  if (options.secure) cookie += "; Secure";
106
+ if (options.partitioned) cookie += "; Partitioned";
93
107
  if (options.sameSite) {
94
108
  const sameSite = options.sameSite.toLowerCase();
95
109
  cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
96
110
  }
97
111
  return cookie;
98
112
  }
113
+ function assertServableCookie(name, options) {
114
+ const reject = reason => {
115
+ throw new Error(`serializeCookie: every browser silently rejects this cookie — ${reason}. ` + `It would never come back on a request, with no error anywhere.`);
116
+ };
117
+ const lower = name.toLowerCase();
118
+ if (lower.startsWith("__host-")) {
119
+ if (!options.secure) reject(`the __Host- prefix on \`${name}\` requires \`secure: true\``);
120
+ if (options.path !== undefined && options.path !== "/") reject(`the __Host- prefix on \`${name}\` requires \`Path=/\` (got \`${options.path}\`) — ` + `host-locking is the prefix's whole contract, so it cannot be path-scoped`);
121
+ if (options.domain) reject(`the __Host- prefix on \`${name}\` forbids \`Domain\` (got \`${options.domain}\`)`);
122
+ } else if (lower.startsWith("__secure-") && !options.secure) {
123
+ reject(`the __Secure- prefix on \`${name}\` requires \`secure: true\``);
124
+ }
125
+ if (options.sameSite && options.sameSite.toLowerCase() === "none" && !options.secure) {
126
+ reject("`SameSite=None` requires `secure: true`");
127
+ }
128
+ if (options.partitioned && !options.secure) {
129
+ reject("`Partitioned` requires `secure: true`");
130
+ }
131
+ }
99
132
  const FLASH_COOKIE = "flash";
100
133
  const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
101
134
  function hasFlashCookie(cookieHeader) {
@@ -114,8 +147,23 @@ function configureServerFunctionsCodec(codec) {
114
147
  function getServerFunctionsCodec() {
115
148
  return codecConfig.codec;
116
149
  }
117
- function subscribeFlightData(consumer) {
150
+ const UNNAMED_FLIGHT_SOURCE = "true";
151
+ const flightConfig = {
152
+ consumers: new Map()
153
+ };
154
+ function assertFlightSource(source) {
155
+ if (source === UNNAMED_FLIGHT_SOURCE || source === "" || source.includes(",")) {
156
+ throw new TypeError(`Invalid flight data source id "${source}": ids ride the ` + `${SINGLE_FLIGHT_HEADER} header as a comma-separated list, and "true" is ` + `reserved for the unnamed registration (the bare consumer/hook signatures).`);
157
+ }
158
+ }
159
+ function subscribeFlightData(sourceOrConsumer, maybeConsumer) {
160
+ const named = typeof sourceOrConsumer === "string";
161
+ if (named) assertFlightSource(sourceOrConsumer);
162
+ const source = named ? sourceOrConsumer : UNNAMED_FLIGHT_SOURCE;
163
+ const consumer = named ? maybeConsumer : sourceOrConsumer;
164
+ flightConfig.consumers.set(source, consumer);
118
165
  return () => {
166
+ if (flightConfig.consumers.get(source) === consumer) flightConfig.consumers.delete(source);
119
167
  };
120
168
  }
121
169
  function serverFunctionAddress(endpoint, id) {
@@ -127,10 +175,18 @@ function parseServerFunctionAddress(pathname, endpoint) {
127
175
  if (!pathname.startsWith(mount)) return null;
128
176
  const rest = pathname.slice(mount.length);
129
177
  if (!rest.startsWith("/")) return null;
130
- const segment = rest.slice(1);
178
+ let segment = rest.slice(1);
179
+ let data = false;
180
+ if (segment.startsWith("data/")) {
181
+ segment = segment.slice(5);
182
+ data = true;
183
+ }
131
184
  if (!segment || segment.includes("/")) return null;
132
185
  try {
133
- return decodeURIComponent(segment);
186
+ return {
187
+ id: decodeURIComponent(segment),
188
+ data
189
+ };
134
190
  } catch {
135
191
  return null;
136
192
  }
@@ -162,6 +218,28 @@ function decodeErrorHeaderValue(value) {
162
218
  }
163
219
  const INSTANCE_HEADER = "X-Server-Function-Instance";
164
220
  const BODY_FORMAT_HEADER = "X-Server-Function-Format";
221
+ const UNKNOWN_HEADER = "X-Server-Function-Unknown";
222
+ const REDIRECT_HEADER = "X-Server-Function-Redirect";
223
+ function decodeRedirectHeaderValue(value) {
224
+ if (typeof value !== "string") return undefined;
225
+ const at = value.indexOf(" ");
226
+ if (at < 0) return undefined;
227
+ const status = Number(value.slice(0, at));
228
+ const url = value.slice(at + 1);
229
+ if (!Number.isInteger(status) || !url) return undefined;
230
+ if (status !== 301 && status !== 302 && status !== 303 && status !== 307 && status !== 308) return undefined;
231
+ let parsed;
232
+ try {
233
+ parsed = new URL(url);
234
+ } catch {
235
+ return undefined;
236
+ }
237
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return undefined;
238
+ return {
239
+ status,
240
+ url
241
+ };
242
+ }
165
243
  const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
166
244
  const FILE_FORM_KEY = "__server_function_file__";
167
245
  const BodyFormat = {
@@ -204,7 +282,11 @@ function isJSONSafe(value) {
204
282
  const proto = Object.getPrototypeOf(v);
205
283
  if (proto !== Object.prototype && proto !== null) return false;
206
284
  if (Symbol.asyncIterator in v || Symbol.iterator in v) return false;
207
- for (const k in v) stack.push(v[k]);
285
+ for (const k in v) {
286
+ const descriptor = Object.getOwnPropertyDescriptor(v, k);
287
+ if (descriptor === undefined || !("value" in descriptor)) return false;
288
+ stack.push(descriptor.value);
289
+ }
208
290
  }
209
291
  }
210
292
  return true;
@@ -313,18 +395,34 @@ function createChunk(data) {
313
395
  class ChunkReader {
314
396
  constructor(stream) {
315
397
  this.reader = stream.getReader();
316
- this.buffer = new Uint8Array(0);
398
+ this.store = new Uint8Array(0);
399
+ this.buffer = this.store;
317
400
  this.done = false;
318
401
  }
319
402
  async readChunk() {
320
403
  const chunk = await this.reader.read();
321
- if (!chunk.done) {
322
- const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
323
- newBuffer.set(this.buffer);
324
- newBuffer.set(chunk.value, this.buffer.length);
325
- this.buffer = newBuffer;
326
- } else {
404
+ if (chunk.done) {
327
405
  this.done = true;
406
+ return;
407
+ }
408
+ const incoming = chunk.value;
409
+ const store = this.store;
410
+ const start = this.buffer.byteOffset;
411
+ const end = start + this.buffer.length;
412
+ const needed = this.buffer.length + incoming.length;
413
+ if (end + incoming.length <= store.length) {
414
+ store.set(incoming, end);
415
+ this.buffer = store.subarray(start, end + incoming.length);
416
+ } else if (needed <= store.length) {
417
+ store.copyWithin(0, start, end);
418
+ store.set(incoming, this.buffer.length);
419
+ this.buffer = store.subarray(0, needed);
420
+ } else {
421
+ const grown = new Uint8Array(Math.max(needed, store.length * 2));
422
+ grown.set(this.buffer);
423
+ grown.set(incoming, this.buffer.length);
424
+ this.store = grown;
425
+ this.buffer = grown.subarray(0, needed);
328
426
  }
329
427
  }
330
428
  async next() {
@@ -366,6 +464,27 @@ class ChunkReader {
366
464
  }
367
465
  }
368
466
  }
467
+ const ERROR_TRAILER_PREFIX = "!";
468
+ function encodeErrorTrailer(error) {
469
+ const shaped = error instanceof Error ? error : new Error(String(error));
470
+ return ERROR_TRAILER_PREFIX + JSON.stringify(shaped.name && shaped.name !== "Error" ? {
471
+ name: shaped.name,
472
+ message: shaped.message
473
+ } : {
474
+ message: shaped.message
475
+ });
476
+ }
477
+ function errorFromTrailer(payload) {
478
+ let shape;
479
+ try {
480
+ shape = JSON.parse(payload.slice(1));
481
+ } catch {
482
+ shape = null;
483
+ }
484
+ const error = new Error(shape && typeof shape.message === "string" ? shape.message : "Server function result could not be delivered.");
485
+ if (shape && typeof shape.name === "string") error.name = shape.name;
486
+ return error;
487
+ }
369
488
  async function deserializeStream(source, codecOptions) {
370
489
  if (!source.body) {
371
490
  throw new Error("missing body");
@@ -373,11 +492,17 @@ async function deserializeStream(source, codecOptions) {
373
492
  const reader = new ChunkReader(source.body);
374
493
  const result = await reader.next();
375
494
  if (!result.done) {
495
+ if (result.value.startsWith(ERROR_TRAILER_PREFIX)) {
496
+ throw errorFromTrailer(result.value);
497
+ }
376
498
  const {
377
499
  createJSONDeserializer
378
500
  } = await import('@solidjs/web/serialization/decode');
379
501
  const deserializeChunk = createJSONDeserializer(codecOptions);
380
502
  function interpretChunk(chunk) {
503
+ if (chunk.startsWith(ERROR_TRAILER_PREFIX)) {
504
+ throw errorFromTrailer(chunk);
505
+ }
381
506
  return deserializeChunk(JSON.parse(chunk));
382
507
  }
383
508
  reader.drain(interpretChunk).then(() => deserializeChunk.abort(new Error("Server function stream ended unexpectedly.")), error => deserializeChunk.abort(error));
@@ -441,7 +566,8 @@ function copyInitHeaders(init) {
441
566
  for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
442
567
  return headers;
443
568
  }
444
- const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, "Location"].map(header => header.toLowerCase()));
569
+ const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, REDIRECT_HEADER, "Location",
570
+ ...COMPOSED_BODY_FRAMING].map(header => header.toLowerCase()));
445
571
  function fillsStubGap(key, headers, response) {
446
572
  if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
447
573
  if (response.body === null && (key === "content-type" || key === "content-length")) return false;
@@ -457,24 +583,16 @@ function commitEventResponse(response, event = getRequestEvent()) {
457
583
  if (fillsStubGap(key, response.headers, response)) hasGaps = true;
458
584
  });
459
585
  if (!cookies.length && !hasGaps) return response;
460
- try {
461
- for (const cookie of cookies) response.headers.append("Set-Cookie", cookie);
462
- stub.headers.forEach((value, key) => {
463
- if (fillsStubGap(key, response.headers, response)) response.headers.set(key, value);
464
- });
465
- return response;
466
- } catch {
467
- const headers = copyInitHeaders(response.headers);
468
- for (const cookie of cookies) headers.append("Set-Cookie", cookie);
469
- stub.headers.forEach((value, key) => {
470
- if (fillsStubGap(key, headers, response)) headers.set(key, value);
471
- });
472
- return new Response(response.body, {
473
- status: response.status,
474
- statusText: response.statusText,
475
- headers
476
- });
477
- }
586
+ const headers = copyInitHeaders(response.headers);
587
+ for (const cookie of cookies) headers.append("Set-Cookie", cookie);
588
+ stub.headers.forEach((value, key) => {
589
+ if (fillsStubGap(key, headers, response)) headers.set(key, value);
590
+ });
591
+ return new Response(response.body, {
592
+ status: response.status,
593
+ statusText: response.statusText,
594
+ headers
595
+ });
478
596
  }
479
597
 
480
598
  function encodeInputValue(value) {
@@ -506,11 +624,35 @@ function encodeFlashCookie(url, result, input, thrown) {
506
624
  thrown: !!thrown,
507
625
  input: input.map(encodeInputValue)
508
626
  };
627
+ if (fitsCookie(payload)) return flashCookie(payload);
628
+ payload.truncated = true;
629
+ payload.input = [];
630
+ if (!fitsCookie(payload)) {
631
+ if (typeof payload.result === "string") {
632
+ let prefix = payload.result;
633
+ while (prefix.length > 0 && !fitsCookie({
634
+ ...payload,
635
+ result: prefix
636
+ })) {
637
+ prefix = prefix.slice(0, prefix.length >> 1);
638
+ }
639
+ payload.result = prefix.length > 0 ? prefix : true;
640
+ } else {
641
+ payload.result = true;
642
+ }
643
+ }
644
+ return flashCookie(payload);
645
+ }
646
+ function flashCookie(payload) {
509
647
  return serializeCookie(FLASH_COOKIE, JSON.stringify(payload), {
510
648
  secure: true,
511
649
  httpOnly: true
512
650
  });
513
651
  }
652
+ const COOKIE_PAIR_BUDGET = 4000;
653
+ function fitsCookie(payload) {
654
+ return FLASH_COOKIE.length + 1 + encodeURIComponent(JSON.stringify(payload)).length <= COOKIE_PAIR_BUDGET;
655
+ }
514
656
  function decodeFlashCookie(cookieHeader) {
515
657
  const match = parseCookieHeader(cookieHeader)[FLASH_COOKIE];
516
658
  if (!match) return;
@@ -518,12 +660,14 @@ function decodeFlashCookie(cookieHeader) {
518
660
  const payload = JSON.parse(match);
519
661
  if (!payload || !payload.result) return;
520
662
  const result = payload.error ? new Error(payload.result) : payload.result;
521
- return {
663
+ const submission = {
522
664
  input: Array.isArray(payload.input) ? payload.input.map(decodeInputValue) : [],
523
665
  url: payload.url,
524
666
  result: payload.thrown ? undefined : result,
525
667
  error: payload.thrown ? result : undefined
526
668
  };
669
+ if (payload.truncated) submission.truncated = true;
670
+ return submission;
527
671
  } catch (error) {
528
672
  console.error(error);
529
673
  }
@@ -538,7 +682,9 @@ const config = {
538
682
  transformDirectResult: undefined,
539
683
  handleNoJS: undefined,
540
684
  endpoint: "/_server",
541
- csrf: true
685
+ csrf: true,
686
+ bodySizeLimit: 1_048_576,
687
+ maxArguments: 1000
542
688
  };
543
689
  function configureServerFunctionsServer({
544
690
  provideEvent,
@@ -550,7 +696,9 @@ function configureServerFunctionsServer({
550
696
  handleNoJS,
551
697
  endpoint,
552
698
  csrf,
553
- codec
699
+ codec,
700
+ bodySizeLimit,
701
+ maxArguments
554
702
  } = {}) {
555
703
  if (provideEvent !== undefined) config.provideEvent = provideEvent;
556
704
  if (wrapInvocation !== undefined) config.wrapInvocation = wrapInvocation;
@@ -562,6 +710,16 @@ function configureServerFunctionsServer({
562
710
  if (endpoint !== undefined) config.endpoint = endpoint;
563
711
  if (csrf !== undefined) config.csrf = csrf;
564
712
  if (codec !== undefined) configureServerFunctionsCodec(codec);
713
+ if (bodySizeLimit !== undefined) config.bodySizeLimit = bodySizeLimit;
714
+ if (maxArguments !== undefined) config.maxArguments = maxArguments;
715
+ }
716
+ const flightSources = new Map();
717
+ function registerFlightDataSource(source, hook) {
718
+ assertFlightSource(source);
719
+ flightSources.set(source, hook);
720
+ return () => {
721
+ if (flightSources.get(source) === hook) flightSources.delete(source);
722
+ };
565
723
  }
566
724
  function provideEvent(event, fn) {
567
725
  if (config.provideEvent) return config.provideEvent(event, fn);
@@ -569,6 +727,52 @@ function provideEvent(event, fn) {
569
727
  if (ctx) return ctx.run(event, fn);
570
728
  throw new Error("No request event provider. Configure one with configureServerFunctionsServer({ provideEvent }).");
571
729
  }
730
+ function scopeDeferredResult(value, scope) {
731
+ if (value === null || typeof value !== "object" && typeof value !== "function") return value;
732
+ const promised = scope(() => nativePromise(value));
733
+ if (promised) {
734
+ return promised.then(result => scope(() => scopeDeferredResult(result, scope)));
735
+ }
736
+ if (typeof ReadableStream !== "undefined" && value instanceof ReadableStream) {
737
+ let reader;
738
+ return new ReadableStream({
739
+ async pull(controller) {
740
+ try {
741
+ const step = await scope(() => {
742
+ if (!reader) reader = value.getReader();
743
+ return reader.read();
744
+ });
745
+ if (step.done) controller.close();else controller.enqueue(step.value);
746
+ } catch (error) {
747
+ controller.error(error);
748
+ }
749
+ },
750
+ cancel(reason) {
751
+ return scope(() => reader ? reader.cancel(reason) : value.cancel(reason));
752
+ }
753
+ }, {
754
+ highWaterMark: 0
755
+ });
756
+ }
757
+ const scopedIterator = symbol => ({
758
+ [symbol]() {
759
+ const iterator = scope(() => value[symbol]());
760
+ return new Proxy(iterator, {
761
+ get(target, property) {
762
+ const member = Reflect.get(target, property, target);
763
+ return typeof member === "function" && (property === "next" || property === "return" || property === "throw") ? (...args) => scope(() => member.apply(target, args)) : member;
764
+ }
765
+ });
766
+ }
767
+ });
768
+ if (typeof value[Symbol.asyncIterator] === "function") {
769
+ return scopedIterator(Symbol.asyncIterator);
770
+ }
771
+ if (typeof value[Symbol.iterator] === "function" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set) && !ArrayBuffer.isView(value)) {
772
+ return scopedIterator(Symbol.iterator);
773
+ }
774
+ return value;
775
+ }
572
776
  const REGISTRATIONS = new Map();
573
777
  const METHODS = new Map();
574
778
  const INVOCATIONS = new WeakMap();
@@ -583,6 +787,7 @@ function provideRPC() {
583
787
  }
584
788
  function registerServerFunction(id, callback) {
585
789
  provideRPC();
790
+ if (REGISTRATIONS.get(id) !== callback) METHODS.delete(id);
586
791
  REGISTRATIONS.set(id, callback);
587
792
  return callback;
588
793
  }
@@ -656,13 +861,17 @@ function createServerReference({
656
861
  const ogEvt = getRequestEvent();
657
862
  if (!ogEvt) throw new Error("Cannot call server function outside of a request");
658
863
  const evt = {
659
- ...ogEvt
864
+ ...ogEvt,
865
+ locals: {
866
+ ...ogEvt.locals
867
+ }
660
868
  };
661
869
  INVOCATIONS.set(evt, {
662
870
  id
663
871
  });
664
872
  evt.serverOnly = true;
665
- const result = provideEvent(evt, () => {
873
+ const scope = run => provideEvent(evt, run);
874
+ let result = provideEvent(evt, () => {
666
875
  const run = () => fn.apply(thisArg, args);
667
876
  return config.wrapInvocation ? config.wrapInvocation(run, {
668
877
  id,
@@ -671,19 +880,20 @@ function createServerReference({
671
880
  direct: true
672
881
  }) : run();
673
882
  });
883
+ result = scopeDeferredResult(result, scope);
674
884
  const transform = config.transformDirectResult;
675
885
  if (transform && result && typeof result.then === "function") {
676
- return result.then(value => transform(value, {
886
+ return result.then(value => scopeDeferredResult(transform(value, {
677
887
  id,
678
888
  args,
679
889
  event: evt
680
- }));
890
+ }), scope));
681
891
  }
682
- return transform ? transform(result, {
892
+ return transform ? scopeDeferredResult(transform(result, {
683
893
  id,
684
894
  args,
685
895
  event: evt
686
- }) : result;
896
+ }), scope) : result;
687
897
  }
688
898
  });
689
899
  return proxy;
@@ -727,18 +937,107 @@ function getServerFunctionInvocation() {
727
937
  function getEventServerFunctionInvocation(event) {
728
938
  return event && INVOCATIONS.get(event);
729
939
  }
730
- function resolveFunctionId(url) {
940
+ function resolveAddress(url) {
731
941
  return parseServerFunctionAddress(url.pathname, config.endpoint);
732
942
  }
733
- async function parseArguments(request, url, instance, codec) {
943
+ const DECODE_DEPTH_LIMIT = 64;
944
+ function assertDecodeDepth(value) {
945
+ let level = [value];
946
+ for (let depth = 0; level.length > 0; depth++) {
947
+ if (depth > DECODE_DEPTH_LIMIT) {
948
+ throw new TypeError("Server function arguments exceed the decode depth limit");
949
+ }
950
+ const next = [];
951
+ for (const node of level) {
952
+ if (node === null || typeof node !== "object") continue;
953
+ if (Array.isArray(node)) {
954
+ for (const child of node) next.push(child);
955
+ } else {
956
+ for (const key of Object.keys(node)) next.push(node[key]);
957
+ }
958
+ }
959
+ level = next;
960
+ }
961
+ }
962
+ const UNSAFE_ARGUMENT_KEYS = ["__proto__", "constructor", "prototype"];
963
+ function stripUnsafeArgumentKeys(value) {
964
+ const stack = [value];
965
+ const seen = new Set();
966
+ while (stack.length) {
967
+ const v = stack.pop();
968
+ if (v === null || typeof v !== "object" || seen.has(v)) continue;
969
+ seen.add(v);
970
+ for (const key of UNSAFE_ARGUMENT_KEYS) {
971
+ delete v[key];
972
+ }
973
+ for (const key of Object.keys(v)) stack.push(v[key]);
974
+ if (v instanceof Map) {
975
+ for (const [k, entry] of v) stack.push(k, entry);
976
+ } else if (v instanceof Set) {
977
+ for (const member of v) stack.push(member);
978
+ }
979
+ }
980
+ return value;
981
+ }
982
+ async function bufferBodyWithin(request, limit) {
983
+ const reader = request.body.getReader();
984
+ const signal = request.signal;
985
+ const chunks = [];
986
+ let total = 0;
987
+ const onAbort = () => {
988
+ reader.cancel(signal.reason).catch(() => {});
989
+ };
990
+ if (signal.aborted) onAbort();else signal.addEventListener("abort", onAbort, {
991
+ once: true
992
+ });
993
+ try {
994
+ for (;;) {
995
+ const {
996
+ done,
997
+ value
998
+ } = await reader.read();
999
+ if (signal.aborted) throw signal.reason;
1000
+ if (done) break;
1001
+ total += value.byteLength;
1002
+ if (total > limit) {
1003
+ reader.cancel().catch(() => {});
1004
+ return null;
1005
+ }
1006
+ chunks.push(value);
1007
+ }
1008
+ } catch (error) {
1009
+ reader.cancel(error).catch(() => {});
1010
+ throw error;
1011
+ } finally {
1012
+ signal.removeEventListener("abort", onAbort);
1013
+ reader.releaseLock();
1014
+ }
1015
+ const body = new Uint8Array(total);
1016
+ let offset = 0;
1017
+ for (const chunk of chunks) {
1018
+ body.set(chunk, offset);
1019
+ offset += chunk.byteLength;
1020
+ }
1021
+ return new Request(request, {
1022
+ body
1023
+ });
1024
+ }
1025
+ async function parseArguments(request, url, scripted, codec) {
734
1026
  const parsed = [];
735
1027
  const bodyFormat = request.method === "POST" ? request.headers.get(BODY_FORMAT_HEADER) : null;
736
1028
  const args = url.searchParams.get("args");
737
- if (args && (!instance || request.method === "GET" || bodyFormat !== BodyFormat.Serialized)) {
738
- const result = args.startsWith(";0x") ? await deserializeString(args, codec) : JSON.parse(args);
1029
+ if (args && (!scripted || request.method === "GET" || bodyFormat !== BodyFormat.Serialized)) {
1030
+ let result;
1031
+ if (args.startsWith(";0x")) {
1032
+ result = await deserializeString(args, codec);
1033
+ } else {
1034
+ result = JSON.parse(args);
1035
+ assertDecodeDepth(result);
1036
+ }
739
1037
  if (!Array.isArray(result)) {
740
1038
  throw new TypeError("Server function arguments must encode an array");
741
1039
  }
1040
+ stripUnsafeArgumentKeys(result);
742
1041
  for (const arg of result) {
743
1042
  parsed.push(arg);
744
1043
  }
@@ -748,18 +1047,37 @@ async function parseArguments(request, url, instance, codec) {
748
1047
  if (request.method === "POST" && request.body !== null) {
749
1048
  const decoded = await extractBody(request.clone(), codec);
750
1049
  if (bodyFormat === BodyFormat.Serialized || bodyFormat === BodyFormat.Json) {
751
- return decoded;
1050
+ if (bodyFormat === BodyFormat.Json) assertDecodeDepth(decoded);
1051
+ if (!Array.isArray(decoded)) {
1052
+ throw new TypeError("Server function arguments must encode an array");
1053
+ }
1054
+ return stripUnsafeArgumentKeys(decoded);
1055
+ }
1056
+ if (decoded === undefined) {
1057
+ if (bodyFormat === null && (await request.clone().arrayBuffer()).byteLength === 0) {
1058
+ return parsed;
1059
+ }
1060
+ throw new TypeError("Server function body carries no usable encoding");
752
1061
  }
753
1062
  parsed.push(decoded);
754
1063
  }
755
1064
  return parsed;
756
1065
  }
757
- async function foldFlightData(hook, event, headers, outcome, context = {}) {
1066
+ async function foldFlightData(hooks, event, headers, outcome, context = {}) {
758
1067
  if (outcome.value instanceof Response && outcome.value.body) return outcome.value;
759
1068
  digestOutcome(event, outcome);
760
- const data = await hook(event, outcome);
761
- if (data === undefined) return outcome.value;
762
- headers.set(SINGLE_FLIGHT_HEADER, "true");
1069
+ const folded = [];
1070
+ for (const [source, hook] of hooks) {
1071
+ try {
1072
+ const slice = await hook(event, outcome);
1073
+ if (slice !== undefined) folded.push([source, slice]);
1074
+ } catch (error) {
1075
+ console.error(`Error collecting flight data for source "${source}"`, error);
1076
+ }
1077
+ }
1078
+ if (folded.length === 0) return outcome.value;
1079
+ const data = Object.fromEntries(folded);
1080
+ headers.set(SINGLE_FLIGHT_HEADER, folded.map(([source]) => source).join(","));
763
1081
  if (context.transformFlightResult) {
764
1082
  const transformed = await context.transformFlightResult(event, {
765
1083
  value: outcome.value,
@@ -839,7 +1157,7 @@ function foldSetCookies(headers, setCookies) {
839
1157
  }
840
1158
  function mergeResponseHeaders(target, source) {
841
1159
  source.forEach((value, key) => {
842
- if (key !== "set-cookie") target.append(key, value);
1160
+ if (key !== "set-cookie" && !COMPOSED_BODY_FRAMING.has(key)) target.append(key, value);
843
1161
  });
844
1162
  if (source.getSetCookie) {
845
1163
  for (const cookie of source.getSetCookie()) target.append("Set-Cookie", cookie);
@@ -848,6 +1166,48 @@ function mergeResponseHeaders(target, source) {
848
1166
  }
849
1167
  }
850
1168
  const validRedirectStatuses = new Set([301, 302, 303, 307, 308]);
1169
+ function maskRedirect(headers, response, requestUrl) {
1170
+ const target = response.headers && response.headers.get("Location");
1171
+ if (target) {
1172
+ headers.set(REDIRECT_HEADER, `${response.status} ${new URL(target, requestUrl)}`);
1173
+ }
1174
+ headers.delete("Location");
1175
+ }
1176
+ const BOUNDED_COMPOSED_HEADERS = [REDIRECT_HEADER, "Location", REVALIDATE_HEADER];
1177
+ function enforceComposedHeaderInvariants(response) {
1178
+ for (const name of BOUNDED_COMPOSED_HEADERS) {
1179
+ const value = response.headers.get(name);
1180
+ if (value === null) continue;
1181
+ if (value.length > RESPONSE_HEADER_VALUE_LIMIT) {
1182
+ return refuseComposedHeader(response, name, `${name} response header refused at ${value.length} characters`, DEV ? `The ${name} response header is ${value.length} characters; past ` + `${RESPONSE_HEADER_VALUE_LIMIT} it would overflow receivers (an 8 KiB proxy ` + `buffer holds the whole header block) and the response dies at the socket after ` + `the mutation committed. Refused rather than trimmed: a cut redirect target is a ` + `different address, a dropped revalidate key is a silently stale cache. The ` + `redirect()/reload() helpers enforce this bound with the full reasoning at the ` + `call site.` : null);
1183
+ }
1184
+ if (name === REVALIDATE_HEADER) continue;
1185
+ const target = name === REDIRECT_HEADER ? value.slice(value.indexOf(" ") + 1) : value;
1186
+ if (!isHttpNavigationTarget(target)) {
1187
+ return refuseComposedHeader(response, name, `${name} response header refused: non-http(s) navigation target`, DEV ? `The ${name} response header carries a navigation target with a non-http(s) ` + `scheme ("${target.slice(0, 64)}"). A javascript: target is same-origin script ` + `execution in any integration that navigates to it, so only http(s) and ` + `relative targets leave this transport. If the target came from request data ` + `(?next= and friends), validate it against your own origin before redirecting.` : null);
1188
+ }
1189
+ }
1190
+ return response;
1191
+ }
1192
+ function refuseComposedHeader(response, name, headerMessage, body) {
1193
+ if (response.body) {
1194
+ try {
1195
+ const cancelled = response.body.cancel();
1196
+ if (cancelled && typeof cancelled.then === "function") cancelled.then(undefined, () => {});
1197
+ } catch {}
1198
+ }
1199
+ const headers = new Headers();
1200
+ headers.set(ERROR_HEADER, boundedErrorHeaderValue(DEV ? headerMessage : GENERIC_SERVER_ERROR_MESSAGE));
1201
+ return new Response(body, {
1202
+ status: 500,
1203
+ headers
1204
+ });
1205
+ }
1206
+ function warnScripted304(functionId) {
1207
+ if (DEV) {
1208
+ console.warn(`Server function "${functionId}" answered a scripted call with 304 Not Modified. ` + `The client transport sends no conditional headers, so nothing was asked to be ` + `revalidated: the call resolves to undefined, not "unchanged". For conditional ` + `reads, declare the function GET and set ETag/Cache-Control response headers - ` + `the browser owns that exchange and replays its cached answer on a 304.`);
1209
+ }
1210
+ }
851
1211
  function createNoJSHandler({
852
1212
  base = ""
853
1213
  } = {}) {
@@ -891,44 +1251,291 @@ function isFormPost(request) {
891
1251
  const type = request.headers.get("content-type") || "";
892
1252
  return type.startsWith("application/x-www-form-urlencoded") || type.startsWith("multipart/form-data");
893
1253
  }
894
- function serializeResponseStream(value, codecOptions, signal) {
895
- let closeIterator = null;
896
- let closed = false;
897
- let cancelSerialize = null;
898
- let onAbort = null;
899
- const teardown = () => {
900
- if (closed) return;
901
- closed = true;
902
- if (onAbort) signal.removeEventListener("abort", onAbort);
903
- if (cancelSerialize) cancelSerialize();
904
- if (closeIterator) closeIterator();
1254
+ function guardFailures(value, state) {
1255
+ if (!state) state = {
1256
+ seen: new WeakMap(),
1257
+ cyclic: new WeakSet()
905
1258
  };
906
- if (value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function") {
1259
+ const entered = enterGuard(value, state);
1260
+ if (!(entered instanceof Frame)) return entered;
1261
+ const stack = [entered];
1262
+ let delivered = NOTHING;
1263
+ for (;;) {
1264
+ const top = stack[stack.length - 1];
1265
+ const items = top.items;
1266
+ let pushed = null;
1267
+ while (top.i < items.length) {
1268
+ const i = top.i;
1269
+ let original;
1270
+ if (top.kind === OBJECT) {
1271
+ const descriptor = top.descriptors[items[i]];
1272
+ if ("value" in descriptor) {
1273
+ original = descriptor.value;
1274
+ } else if (typeof descriptor.get === "function") {
1275
+ if (top.accessorRead !== i) {
1276
+ try {
1277
+ top.accessorValue = descriptor.get.call(top.value);
1278
+ } catch (error) {
1279
+ throw sanitizeServerError(error);
1280
+ }
1281
+ top.accessorRead = i;
1282
+ }
1283
+ original = top.accessorValue;
1284
+ } else {
1285
+ top.i++;
1286
+ continue;
1287
+ }
1288
+ } else {
1289
+ original = items[i];
1290
+ }
1291
+ let guarded;
1292
+ if (delivered !== NOTHING) {
1293
+ guarded = delivered;
1294
+ delivered = NOTHING;
1295
+ } else {
1296
+ guarded = enterGuard(original, state);
1297
+ if (guarded instanceof Frame) {
1298
+ pushed = guarded;
1299
+ break;
1300
+ }
1301
+ }
1302
+ if (top.kind === ARRAY) {
1303
+ if (guarded !== original) {
1304
+ top.next[i] = guarded;
1305
+ top.changed = true;
1306
+ }
1307
+ } else if (top.kind === MAP) {
1308
+ if ((i & 1) === 0) top.pendingKey = guarded;else top.next.set(top.pendingKey, guarded);
1309
+ if (guarded !== original) top.changed = true;
1310
+ } else if (top.kind === SET) {
1311
+ top.next.add(guarded);
1312
+ if (guarded !== original) top.changed = true;
1313
+ } else if (guarded !== original || top.accessorRead === i) {
1314
+ Object.defineProperty(top.next, items[i], {
1315
+ value: guarded,
1316
+ writable: true,
1317
+ configurable: true,
1318
+ enumerable: top.descriptors[items[i]].enumerable
1319
+ });
1320
+ top.changed = true;
1321
+ }
1322
+ top.i++;
1323
+ }
1324
+ if (pushed !== null) {
1325
+ stack.push(pushed);
1326
+ continue;
1327
+ }
1328
+ stack.pop();
1329
+ const out = keepGuarded(top.value, top.next, top.changed, state);
1330
+ if (stack.length === 0) return out;
1331
+ delivered = out;
1332
+ }
1333
+ }
1334
+ const NOTHING = Symbol();
1335
+ const ARRAY = 0;
1336
+ const MAP = 1;
1337
+ const SET = 2;
1338
+ const OBJECT = 3;
1339
+ class Frame {
1340
+ constructor(kind, value, next, items, descriptors) {
1341
+ this.kind = kind;
1342
+ this.value = value;
1343
+ this.next = next;
1344
+ this.items = items;
1345
+ this.descriptors = descriptors;
1346
+ this.i = 0;
1347
+ this.changed = false;
1348
+ this.accessorRead = -1;
1349
+ this.accessorValue = undefined;
1350
+ this.pendingKey = undefined;
1351
+ }
1352
+ }
1353
+ function guardOperation(state, run) {
1354
+ return state.scope ? state.scope(run) : run();
1355
+ }
1356
+ function enterGuard(value, state) {
1357
+ if (value === null || typeof value !== "object") return value;
1358
+ if (state.seen.has(value)) {
1359
+ state.cyclic.add(value);
1360
+ return state.seen.get(value);
1361
+ }
1362
+ if (typeof ReadableStream !== "undefined" && value instanceof ReadableStream) {
1363
+ let reader;
1364
+ const gate = state.gate;
1365
+ let finished = false;
1366
+ const close = () => {
1367
+ if (finished) return;
1368
+ finished = true;
1369
+ try {
1370
+ const cancelled = guardOperation(state, () => reader ? reader.cancel() : value.cancel());
1371
+ if (cancelled && typeof cancelled.then === "function") cancelled.then(undefined, () => {});
1372
+ } catch {}
1373
+ };
1374
+ const guardedStream = new ReadableStream({
1375
+ async pull(controller) {
1376
+ try {
1377
+ if (gate && !finished && !gate.wantsMore()) await gate.awaitDemand();
1378
+ if (finished) {
1379
+ controller.close();
1380
+ return;
1381
+ }
1382
+ if (!reader) reader = guardOperation(state, () => value.getReader());
1383
+ const {
1384
+ done,
1385
+ value: chunk
1386
+ } = await guardOperation(state, () => reader.read());
1387
+ done ? controller.close() : controller.enqueue(guardOperation(state, () => guardFailures(chunk, state)));
1388
+ } catch (error) {
1389
+ controller.error(guardOperation(state, () => sanitizeServerError(error)));
1390
+ }
1391
+ },
1392
+ cancel(reason) {
1393
+ finished = true;
1394
+ return guardOperation(state, () => reader ? reader.cancel(reason) : value.cancel(reason));
1395
+ }
1396
+ });
1397
+ if (gate) gate.onOpen(close);
1398
+ state.seen.set(value, guardedStream);
1399
+ return guardedStream;
1400
+ }
1401
+ if (typeof value.then === "function") {
1402
+ const guardedPromise = Promise.resolve(value).then(resolved => guardOperation(state, () => guardFailures(resolved, state)), error => {
1403
+ throw guardOperation(state, () => sanitizeServerError(error));
1404
+ });
1405
+ guardedPromise.catch(() => {});
1406
+ state.seen.set(value, guardedPromise);
1407
+ return guardedPromise;
1408
+ }
1409
+ if (typeof value[Symbol.asyncIterator] === "function") {
907
1410
  const source = value;
908
- value = {
1411
+ const gate = state.gate;
1412
+ const guardedIterable = {
909
1413
  [Symbol.asyncIterator]() {
910
- const it = source[Symbol.asyncIterator]();
1414
+ const iterator = guardOperation(state, () => source[Symbol.asyncIterator]());
911
1415
  let finished = false;
912
- closeIterator = () => {
1416
+ const close = () => {
913
1417
  if (finished) return;
914
1418
  finished = true;
915
1419
  try {
916
- const returned = it.return && it.return();
1420
+ const returned = iterator.return && guardOperation(state, () => iterator.return());
917
1421
  if (returned && typeof returned.then === "function") returned.then(undefined, () => {});
918
1422
  } catch {}
919
1423
  };
920
- if (closed) closeIterator();
1424
+ if (gate) gate.onOpen(close);
1425
+ const step = () => finished ? Promise.resolve({
1426
+ done: true,
1427
+ value: undefined
1428
+ }) : guardOperation(state, () => iterator.next()).then(step => {
1429
+ if (step.done) {
1430
+ finished = true;
1431
+ return step;
1432
+ }
1433
+ return {
1434
+ done: false,
1435
+ value: guardOperation(state, () => guardFailures(step.value, state))
1436
+ };
1437
+ }, error => {
1438
+ throw guardOperation(state, () => sanitizeServerError(error));
1439
+ });
921
1440
  return {
922
- next: () => finished ? Promise.resolve({
923
- done: true,
924
- value: undefined
925
- }) : it.next()
1441
+ next: () => finished || !gate || gate.wantsMore() ? step() : gate.awaitDemand().then(step),
1442
+ return: () => {
1443
+ close();
1444
+ return Promise.resolve({
1445
+ done: true,
1446
+ value: undefined
1447
+ });
1448
+ }
926
1449
  };
927
1450
  }
928
1451
  };
1452
+ state.seen.set(value, guardedIterable);
1453
+ return guardedIterable;
1454
+ }
1455
+ if (state.scope && typeof value[Symbol.iterator] === "function" && !Array.isArray(value) && !(value instanceof Map) && !(value instanceof Set) && !ArrayBuffer.isView(value)) {
1456
+ const scopedIterable = scopeDeferredResult(value, state.scope);
1457
+ state.seen.set(value, scopedIterable);
1458
+ return scopedIterable;
1459
+ }
1460
+ if (Array.isArray(value)) {
1461
+ const next = value.slice();
1462
+ state.seen.set(value, next);
1463
+ return new Frame(ARRAY, value, next, value, null);
1464
+ }
1465
+ if (value instanceof Map) {
1466
+ const next = new Map();
1467
+ state.seen.set(value, next);
1468
+ const items = [];
1469
+ for (const entry of value) items.push(entry[0], entry[1]);
1470
+ return new Frame(MAP, value, next, items, null);
1471
+ }
1472
+ if (value instanceof Set) {
1473
+ const next = new Set();
1474
+ state.seen.set(value, next);
1475
+ return new Frame(SET, value, next, [...value], null);
929
1476
  }
1477
+ const prototype = Object.getPrototypeOf(value);
1478
+ if (prototype !== Object.prototype && prototype !== null) {
1479
+ state.seen.set(value, value);
1480
+ return value;
1481
+ }
1482
+ const descriptors = Object.getOwnPropertyDescriptors(value);
1483
+ for (const key of Object.keys(descriptors)) {
1484
+ descriptors[key].configurable = true;
1485
+ if ("value" in descriptors[key]) descriptors[key].writable = true;
1486
+ }
1487
+ const next = Object.create(prototype, descriptors);
1488
+ state.seen.set(value, next);
1489
+ return new Frame(OBJECT, value, next, Object.keys(value), descriptors);
1490
+ }
1491
+ function keepGuarded(value, next, changed, state) {
1492
+ if (changed || state.cyclic.has(value)) return next;
1493
+ state.seen.set(value, value);
1494
+ return value;
1495
+ }
1496
+ function serializeResponseStream(value, codecOptions, signal, scope) {
1497
+ let closed = false;
1498
+ let streamController = null;
1499
+ let demandWaiters = null;
1500
+ const wantsMore = () => streamController !== null && streamController.desiredSize > 0;
1501
+ const awaitDemand = () => new Promise(resolve => (demandWaiters ??= []).push(resolve));
1502
+ const supplyDemand = () => {
1503
+ const resolvers = demandWaiters;
1504
+ demandWaiters = null;
1505
+ if (resolvers) for (const resolve of resolvers) resolve();
1506
+ };
1507
+ const sourceClosers = new Set();
1508
+ const gate = {
1509
+ wantsMore,
1510
+ awaitDemand,
1511
+ onOpen(close) {
1512
+ if (closed) close();else sourceClosers.add(close);
1513
+ }
1514
+ };
1515
+ const guardState = {
1516
+ seen: new WeakMap(),
1517
+ cyclic: new WeakSet(),
1518
+ gate,
1519
+ scope
1520
+ };
1521
+ value = guardOperation(guardState, () => guardFailures(value, guardState));
1522
+ let cancelSerialize = null;
1523
+ let onAbort = null;
1524
+ const finishSource = () => {
1525
+ for (const close of sourceClosers) close();
1526
+ sourceClosers.clear();
1527
+ supplyDemand();
1528
+ };
1529
+ const teardown = () => {
1530
+ if (closed) return;
1531
+ closed = true;
1532
+ if (onAbort) signal.removeEventListener("abort", onAbort);
1533
+ if (cancelSerialize) cancelSerialize();
1534
+ finishSource();
1535
+ };
930
1536
  return new ReadableStream({
931
1537
  async start(controller) {
1538
+ streamController = controller;
932
1539
  if (signal) {
933
1540
  if (signal.aborted) {
934
1541
  teardown();
@@ -964,29 +1571,54 @@ function serializeResponseStream(value, codecOptions, signal) {
964
1571
  if (closed) return;
965
1572
  closed = true;
966
1573
  if (onAbort) signal.removeEventListener("abort", onAbort);
1574
+ finishSource();
967
1575
  controller.close();
968
1576
  },
969
1577
  onError(error) {
970
1578
  if (closed) return;
971
1579
  closed = true;
972
1580
  if (onAbort) signal.removeEventListener("abort", onAbort);
973
- controller.error(error);
1581
+ finishSource();
1582
+ try {
1583
+ const delivered = sanitizeServerError(DEV && error instanceof Error ? new Error(`Server function result could not be encoded: ${error.message}`) : error);
1584
+ controller.enqueue(createChunk(encodeErrorTrailer(delivered)));
1585
+ controller.close();
1586
+ } catch {
1587
+ try {
1588
+ controller.error(error);
1589
+ } catch {}
1590
+ }
974
1591
  }
975
1592
  });
976
1593
  },
1594
+ pull() {
1595
+ supplyDemand();
1596
+ },
977
1597
  cancel() {
978
1598
  teardown();
979
1599
  }
980
1600
  });
981
1601
  }
982
- function serializedResponse(value, headers, codec, signal) {
1602
+ function serializedResponse(value, headers, codec, signal, scope) {
983
1603
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
984
1604
  headers.set("Content-Type", "text/plain");
985
- return new Response(serializeResponseStream(value, codec, signal), {
1605
+ return new Response(serializeResponseStream(value, codec, signal, scope), {
986
1606
  headers
987
1607
  });
988
1608
  }
989
- function encodeResult(value, headers, status, codec, signal) {
1609
+ function encodeResult(value, headers, status, codec, signal, scope) {
1610
+ if (NULL_BODY_STATUSES.has(status)) {
1611
+ if (value === undefined || value === null) {
1612
+ headers.set(BODY_FORMAT_HEADER, BodyFormat.Void);
1613
+ return new Response(null, {
1614
+ status,
1615
+ headers
1616
+ });
1617
+ }
1618
+ const error = new Error(`Server function answered status ${status}, which forbids a response body, with a value. ` + `Return respond(undefined, { status: ${status} }) for a bodiless answer, or drop the ` + `status to send the value.`);
1619
+ headers.set(ERROR_HEADER, encodeErrorHeaderValue(error.message));
1620
+ return encodeResult(error, headers, 500, codec, signal, scope);
1621
+ }
990
1622
  const direct = getHeadersAndBody(value);
991
1623
  if (direct) {
992
1624
  for (const [key, val] of Object.entries(direct.headers || {})) {
@@ -1005,21 +1637,37 @@ function encodeResult(value, headers, status, codec, signal) {
1005
1637
  });
1006
1638
  }
1007
1639
  try {
1008
- if (isJSONSafe(value)) {
1640
+ const jsonSafe = scope ? scope(() => isJSONSafe(value)) : isJSONSafe(value);
1641
+ if (jsonSafe) {
1009
1642
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Json);
1010
1643
  headers.set("Content-Type", "application/json");
1011
- return new Response(JSON.stringify(value), {
1644
+ const body = scope ? scope(() => JSON.stringify(value)) : JSON.stringify(value);
1645
+ return new Response(body, {
1012
1646
  status,
1013
1647
  headers
1014
1648
  });
1015
1649
  }
1016
1650
  } catch {
1017
1651
  }
1018
- const response = serializedResponse(value, headers, codec, signal);
1019
- return status === 200 ? response : new Response(response.body, {
1020
- status,
1021
- headers
1022
- });
1652
+ try {
1653
+ const response = serializedResponse(value, headers, codec, signal, scope);
1654
+ return status === 200 ? response : new Response(response.body, {
1655
+ status,
1656
+ headers
1657
+ });
1658
+ } catch (error) {
1659
+ throw DEV && error instanceof Error ? new Error(`Server function result could not be encoded: ${error.message}`) : error;
1660
+ }
1661
+ }
1662
+ const ERROR_HEADER_VALUE_LIMIT = 1024;
1663
+ function boundedErrorHeaderValue(message) {
1664
+ let label = message.length > 256 ? message.slice(0, 256) : message;
1665
+ let encoded = encodeErrorHeaderValue(label);
1666
+ while (encoded.length > ERROR_HEADER_VALUE_LIMIT && label.length > 1) {
1667
+ label = label.slice(0, Math.ceil(label.length / 2));
1668
+ encoded = encodeErrorHeaderValue(label);
1669
+ }
1670
+ return encoded;
1023
1671
  }
1024
1672
  const GENERIC_SERVER_ERROR_MESSAGE = "Internal Server Error";
1025
1673
  let DEV = true === true;
@@ -1043,11 +1691,12 @@ function serverFunctionUrl(id, boundArgs) {
1043
1691
  return `${address}?args=${encodeURIComponent(JSON.stringify(boundArgs))}`;
1044
1692
  }
1045
1693
  function parseServerFunctionUrl(url) {
1046
- return parseServerFunctionAddress(new URL(url, "http://localhost").pathname, config.endpoint);
1694
+ const parsed = parseServerFunctionAddress(new URL(url, "http://localhost").pathname, config.endpoint);
1695
+ return parsed && parsed.id;
1047
1696
  }
1048
1697
  async function matchesOrigin(origin, request, matcher) {
1049
1698
  if (matcher === undefined) return origin === new URL(request.url).origin;
1050
- if (typeof matcher === "function") return !!(await matcher(origin, request));
1699
+ if (typeof matcher === "function") return (await matcher(origin, request)) === true;
1051
1700
  return Array.isArray(matcher) ? matcher.includes(origin) : origin === matcher;
1052
1701
  }
1053
1702
  async function allowsServerFunctionRequest(request, options) {
@@ -1099,14 +1748,37 @@ function forbiddenResponse() {
1099
1748
  }
1100
1749
  }));
1101
1750
  }
1751
+ function nativePromise(value) {
1752
+ if (value instanceof Promise) return value;
1753
+ try {
1754
+ if (Object.prototype.toString.call(value) === "[object Promise]") return Promise.prototype.then.call(value, value => value);
1755
+ } catch {}
1756
+ }
1102
1757
  async function handleServerFunctionRequest(request, options = {}) {
1103
- const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
1758
+ const codec = {
1759
+ ...(options.codec !== undefined ? options.codec : getServerFunctionsCodec())
1760
+ };
1761
+ codec.serializeErrorStacks ??= DEV;
1104
1762
  const url = new URL(request.url);
1105
1763
  const method = request.method;
1106
- const functionId = resolveFunctionId(url);
1764
+ const address = resolveAddress(url);
1765
+ const functionId = address && address.id;
1107
1766
  const declaredRead = (method === "GET" || method === "HEAD") && functionId !== null && METHODS.get(functionId) === "GET";
1108
1767
  const csrf = options.csrf !== undefined ? options.csrf : config.csrf;
1109
- const protectsRequest = csrf !== false && !declaredRead;
1768
+ const protectsRequest = csrf !== false && (!declaredRead || typeof csrf === "object" && csrf.protectDeclaredReads === true);
1769
+ let serverFunction;
1770
+ if (functionId) {
1771
+ try {
1772
+ serverFunction = getServerFunction(functionId);
1773
+ } catch {
1774
+ return finalizeTransportResponse(new Response(DEV ? `Unknown server function: ${functionId}` : null, {
1775
+ status: 404,
1776
+ headers: {
1777
+ [UNKNOWN_HEADER]: "true"
1778
+ }
1779
+ }), method);
1780
+ }
1781
+ }
1110
1782
  if (protectsRequest && !(await allowsServerFunctionRequest(request, csrf === true ? {} : csrf))) {
1111
1783
  return finalizeTransportResponse(forbiddenResponse(), method);
1112
1784
  }
@@ -1117,15 +1789,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1117
1789
  });
1118
1790
  return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1119
1791
  }
1120
- let serverFunction;
1121
- try {
1122
- serverFunction = getServerFunction(functionId);
1123
- } catch {
1124
- const response = new Response(DEV ? `Unknown server function: ${functionId}` : null, {
1125
- status: 404
1126
- });
1127
- return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1128
- }
1792
+ const scripted = address.data;
1129
1793
  if (method !== "POST" && !declaredRead) {
1130
1794
  const response = new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
1131
1795
  status: 405,
@@ -1135,25 +1799,103 @@ async function handleServerFunctionRequest(request, options = {}) {
1135
1799
  });
1136
1800
  return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1137
1801
  }
1138
- const event = options.createEvent ? options.createEvent(request) : {
1139
- request,
1140
- locals: {}
1802
+ const bodySizeLimit = options.bodySizeLimit !== undefined ? options.bodySizeLimit : config.bodySizeLimit;
1803
+ const argsEncoding = url.searchParams.get("args");
1804
+ if (argsEncoding !== null && argsEncoding.length > bodySizeLimit) {
1805
+ const response = new Response(DEV ? "Server function arguments exceed the configured bodySizeLimit" : null, {
1806
+ status: 413
1807
+ });
1808
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1809
+ }
1810
+ if (method === "POST" && request.body !== null && bodySizeLimit !== Infinity) {
1811
+ const raw = request.headers.get("content-length");
1812
+ const declared = raw !== null && /^\d+$/.test(raw) ? Number(raw) : NaN;
1813
+ if (declared > bodySizeLimit) {
1814
+ const response = new Response(DEV ? "Server function request body exceeds the configured bodySizeLimit" : null, {
1815
+ status: 413
1816
+ });
1817
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1818
+ }
1819
+ if (!(declared > 0)) {
1820
+ let bounded;
1821
+ try {
1822
+ bounded = await bufferBodyWithin(request, bodySizeLimit);
1823
+ } catch {
1824
+ const response = new Response(DEV ? "Malformed server function arguments" : null, {
1825
+ status: 400
1826
+ });
1827
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1828
+ }
1829
+ if (bounded === null) {
1830
+ const response = new Response(DEV ? "Server function request body exceeds the configured bodySizeLimit" : null, {
1831
+ status: 413
1832
+ });
1833
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1834
+ }
1835
+ request = bounded;
1836
+ }
1837
+ }
1838
+ let event;
1839
+ try {
1840
+ event = options.createEvent ? options.createEvent(request) : {
1841
+ request,
1842
+ locals: {}
1843
+ };
1844
+ const promised = nativePromise(event);
1845
+ if (promised) event = await promised;
1846
+ } catch (error) {
1847
+ const safe = sanitizeServerError(error);
1848
+ const message = safe instanceof Error ? safe.message : String(safe);
1849
+ const headers = new Headers();
1850
+ headers.set(ERROR_HEADER, boundedErrorHeaderValue(message));
1851
+ const response = scripted ? encodeResult(safe, headers, 500, codec, request.signal) : new Response(DEV ? message : null, {
1852
+ status: 500
1853
+ });
1854
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1855
+ }
1856
+ const refuseCommitted = raw => {
1857
+ const response = commitEventResponse(raw, event);
1858
+ return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1141
1859
  };
1142
1860
  const provide = options.provideEvent || provideEvent;
1861
+ const scope = run => provide(event, run);
1143
1862
  const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
1144
1863
  const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
1145
1864
  const wrapInvocation = options.wrapInvocation !== undefined ? options.wrapInvocation : config.wrapInvocation;
1146
1865
  const transformFlightResult = options.transformFlightResult !== undefined ? options.transformFlightResult : config.transformFlightResult;
1147
- const handleNoJS = options.handleNoJS !== undefined ? options.handleNoJS : config.handleNoJS !== undefined ? config.handleNoJS : isFormPost(request) ? defaultNoJSHandler || (defaultNoJSHandler = createNoJSHandler()) : undefined;
1148
- const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
1866
+ let handleNoJS = options.handleNoJS !== undefined ? options.handleNoJS : config.handleNoJS;
1867
+ if (handleNoJS === undefined && !scripted && isFormPost(request)) {
1868
+ const fetchMode = request.headers.get("Sec-Fetch-Mode");
1869
+ if (fetchMode === null || fetchMode === "navigate") {
1870
+ handleNoJS = defaultNoJSHandler || (defaultNoJSHandler = createNoJSHandler());
1871
+ } else {
1872
+ const response = new Response(DEV ? "The bare server-function address answers form navigations with the " + "no-JS redirect convention. Scripted callers use the data address " + `(…/data/${functionId}) or send the ${BODY_FORMAT_HEADER} tag.` : null, {
1873
+ status: 400
1874
+ });
1875
+ return refuseCommitted(response);
1876
+ }
1877
+ }
1878
+ const flightHeader = scripted && method === "POST" ? request.headers.get(SINGLE_FLIGHT_HEADER) : null;
1879
+ const flightHooks = flightHeader ? flightHeader.split(",").flatMap(source => {
1880
+ const hook = source === "true" ? flightHook : flightSources.get(source);
1881
+ return hook ? [[source, hook]] : [];
1882
+ }) : [];
1883
+ const collectsFlight = flightHooks.length > 0;
1149
1884
  let parsed;
1150
1885
  try {
1151
- parsed = await parseArguments(request, url, instance, codec);
1886
+ parsed = await parseArguments(request, url, scripted, codec);
1152
1887
  } catch {
1153
1888
  const response = new Response(DEV ? "Malformed server function arguments" : null, {
1154
1889
  status: 400
1155
1890
  });
1156
- return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1891
+ return refuseCommitted(response);
1892
+ }
1893
+ const maxArguments = options.maxArguments !== undefined ? options.maxArguments : config.maxArguments;
1894
+ if (parsed.length > maxArguments) {
1895
+ const response = new Response(DEV ? "Server function call exceeds the configured maxArguments" : null, {
1896
+ status: 400
1897
+ });
1898
+ return refuseCommitted(response);
1157
1899
  }
1158
1900
  const flightContext = {
1159
1901
  id: functionId,
@@ -1167,7 +1909,8 @@ async function handleServerFunctionRequest(request, options = {}) {
1167
1909
  const headers = new Headers();
1168
1910
  const dispatch = async () => {
1169
1911
  try {
1170
- let result = await provide(event, async () => {
1912
+ let invocations = 0;
1913
+ const invokeOnce = async () => {
1171
1914
  INVOCATIONS.set(event, {
1172
1915
  id: functionId
1173
1916
  });
@@ -1179,7 +1922,16 @@ async function handleServerFunctionRequest(request, options = {}) {
1179
1922
  request,
1180
1923
  direct: false
1181
1924
  }) : run();
1925
+ };
1926
+ let result = await provide(event, () => {
1927
+ if (++invocations > 1) {
1928
+ throw new Error("provideEvent invoked the server function callback more than once: a second " + "invocation would commit the call's side effects twice. The hook must call " + "fn exactly once and return its result.");
1929
+ }
1930
+ return invokeOnce();
1182
1931
  });
1932
+ if (invocations !== 1) {
1933
+ throw new Error(invocations === 0 ? "provideEvent returned without invoking the server function callback: the call " + "would have answered as a void success without running the function. The hook " + "must call fn exactly once and return its result." : "provideEvent invoked the server function callback more than once: a second " + "invocation would commit the call's side effects twice. The hook must call " + "fn exactly once and return its result.");
1934
+ }
1183
1935
  if (transformResult) {
1184
1936
  result = await transformResult(event, result, flightContext);
1185
1937
  }
@@ -1190,25 +1942,29 @@ async function handleServerFunctionRequest(request, options = {}) {
1190
1942
  response,
1191
1943
  value
1192
1944
  } = result;
1193
- if (!instance && !handleNoJS && response && response.body) {
1945
+ if (!scripted && !handleNoJS && response && response.body) {
1194
1946
  return response;
1195
1947
  }
1196
1948
  if (response && response.headers) {
1197
1949
  mergeResponseHeaders(headers, response.headers);
1198
1950
  }
1199
- if (response && response.status && (response.status < 300 || response.status >= 400)) {
1951
+ if (response && response.status && (!scripted || !validRedirectStatuses.has(response.status))) {
1200
1952
  status = response.status;
1953
+ } else if (response && response.status) {
1954
+ maskRedirect(headers, response, request.url);
1201
1955
  }
1202
1956
  metadata = response;
1203
1957
  result = value;
1204
1958
  } else if (result instanceof Response) {
1205
1959
  if (result.headers && result.headers.has("X-Content-Raw")) return result;
1206
- if (instance) {
1960
+ if (scripted) {
1207
1961
  if (result.headers) {
1208
1962
  mergeResponseHeaders(headers, result.headers);
1209
1963
  }
1210
- if (result.status && (result.status < 300 || result.status >= 400)) {
1964
+ if (result.status && !validRedirectStatuses.has(result.status)) {
1211
1965
  status = result.status;
1966
+ } else if (result.status) {
1967
+ maskRedirect(headers, result, request.url);
1212
1968
  }
1213
1969
  metadata = result;
1214
1970
  if (result.body == null) {
@@ -1217,7 +1973,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1217
1973
  }
1218
1974
  }
1219
1975
  if (collectsFlight) {
1220
- result = await foldFlightData(flightHook, event, headers, {
1976
+ result = await foldFlightData(flightHooks, event, headers, {
1221
1977
  id: functionId,
1222
1978
  value: result,
1223
1979
  response: metadata,
@@ -1226,19 +1982,37 @@ async function handleServerFunctionRequest(request, options = {}) {
1226
1982
  }, flightContext);
1227
1983
  if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
1228
1984
  }
1229
- if (!instance) {
1230
- if (handleNoJS) return handleNoJS(result, request, parsed);
1985
+ if (!scripted) {
1986
+ if (handleNoJS) return handleNoJS(result ?? metadata, request, parsed);
1231
1987
  if (result instanceof Response) return result;
1232
- return encodeResult(result, headers, 200, codec, request.signal);
1988
+ return encodeResult(result, headers, status, codec, request.signal, scope);
1233
1989
  }
1234
- return encodeResult(result, headers, status, codec, request.signal);
1990
+ if (status === 304) warnScripted304(functionId);
1991
+ return encodeResult(result, headers, status, codec, request.signal, scope);
1235
1992
  } catch (x) {
1993
+ const respondThrown = value => {
1994
+ const safe = sanitizeServerError(value);
1995
+ if (!scripted) {
1996
+ if (handleNoJS) return handleNoJS(safe, request, parsed, true);
1997
+ const message = safe instanceof Error ? safe.message : String(safe);
1998
+ return new Response(DEV ? message : null, {
1999
+ status: 500
2000
+ });
2001
+ }
2002
+ const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
2003
+ headers.set(ERROR_HEADER, boundedErrorHeaderValue(error));
2004
+ return encodeResult(safe, headers, 500, codec, request.signal, scope);
2005
+ };
1236
2006
  if (x instanceof Response || isResponseEnvelope(x)) {
1237
2007
  if (transformResult) {
1238
- x = await transformResult(event, x, {
1239
- ...flightContext,
1240
- thrown: true
1241
- });
2008
+ try {
2009
+ x = await transformResult(event, x, {
2010
+ ...flightContext,
2011
+ thrown: true
2012
+ });
2013
+ } catch (hookError) {
2014
+ return respondThrown(hookError);
2015
+ }
1242
2016
  }
1243
2017
  let status = 200;
1244
2018
  let metadata;
@@ -1250,8 +2024,10 @@ async function handleServerFunctionRequest(request, options = {}) {
1250
2024
  if (response && response.headers) {
1251
2025
  mergeResponseHeaders(headers, response.headers);
1252
2026
  }
1253
- if (response && response.status && (!instance || response.status < 300 || response.status >= 400)) {
2027
+ if (response && response.status && (!scripted || !validRedirectStatuses.has(response.status))) {
1254
2028
  status = response.status;
2029
+ } else if (response && response.status) {
2030
+ maskRedirect(headers, response, request.url);
1255
2031
  }
1256
2032
  metadata = response;
1257
2033
  x = value;
@@ -1259,8 +2035,10 @@ async function handleServerFunctionRequest(request, options = {}) {
1259
2035
  if (x.headers) {
1260
2036
  mergeResponseHeaders(headers, x.headers);
1261
2037
  }
1262
- if (x.status && (!instance || x.status < 300 || x.status >= 400)) {
2038
+ if (x.status && (!scripted || !validRedirectStatuses.has(x.status))) {
1263
2039
  status = x.status;
2040
+ } else if (x.status) {
2041
+ maskRedirect(headers, x, request.url);
1264
2042
  }
1265
2043
  metadata = x;
1266
2044
  if (x.body == null) {
@@ -1268,7 +2046,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1268
2046
  }
1269
2047
  }
1270
2048
  if (collectsFlight) {
1271
- x = await foldFlightData(flightHook, event, headers, {
2049
+ x = await foldFlightData(flightHooks, event, headers, {
1272
2050
  id: functionId,
1273
2051
  value: x,
1274
2052
  response: metadata,
@@ -1276,38 +2054,38 @@ async function handleServerFunctionRequest(request, options = {}) {
1276
2054
  thrown: true
1277
2055
  }, flightContext);
1278
2056
  if (x instanceof Response && x.headers.has("X-Content-Raw")) {
2057
+ x = ownResponse(x);
1279
2058
  x.headers.set(ERROR_HEADER, "true");
1280
2059
  return x;
1281
2060
  }
1282
2061
  }
1283
2062
  headers.set(ERROR_HEADER, "true");
1284
- if (!instance) {
2063
+ if (!scripted) {
1285
2064
  if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
1286
2065
  if (x instanceof Response) return x;
1287
2066
  }
1288
- return encodeResult(x, headers, status, codec, request.signal);
1289
- }
1290
- const safe = sanitizeServerError(x);
1291
- if (!instance) {
1292
- if (handleNoJS) return handleNoJS(safe, request, parsed, true);
1293
- const message = safe instanceof Error ? safe.message : String(safe);
1294
- return new Response(DEV ? message : null, {
1295
- status: 500
1296
- });
2067
+ if (scripted && status === 304) warnScripted304(functionId);
2068
+ return encodeResult(x, headers, status, codec, request.signal, scope);
1297
2069
  }
1298
- const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
1299
- headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
1300
- return encodeResult(safe, headers, 200, codec, request.signal);
2070
+ return respondThrown(x);
1301
2071
  }
1302
2072
  };
1303
- const response = commitEventResponse(await dispatch(), event);
2073
+ const response = commitEventResponse(enforceComposedHeaderInvariants(ownResponse(await dispatch())), event);
1304
2074
  return finalizeTransportResponse(protectsRequest ? withCSRFVary(response) : response, method);
1305
2075
  }
2076
+ function ownResponse(response) {
2077
+ try {
2078
+ return new Response(response.body, response);
2079
+ } catch {
2080
+ return response;
2081
+ }
2082
+ }
1306
2083
  function finalizeTransportResponse(response, method) {
1307
2084
  const stripBody = method === "HEAD" && response.body !== null;
1308
- if (stripBody || !response.headers.has("Cache-Control")) {
2085
+ const defaultsCache = !response.headers.has("Cache-Control") && response.status !== 304;
2086
+ if (stripBody || defaultsCache) {
1309
2087
  try {
1310
- if (!response.headers.has("Cache-Control")) {
2088
+ if (defaultsCache) {
1311
2089
  response.headers.set("Cache-Control", "no-store");
1312
2090
  }
1313
2091
  if (!stripBody) return response;
@@ -1319,7 +2097,7 @@ function finalizeTransportResponse(response, method) {
1319
2097
  });
1320
2098
  } catch {
1321
2099
  const headers = new Headers(response.headers);
1322
- if (!headers.has("Cache-Control")) headers.set("Cache-Control", "no-store");
2100
+ if (defaultsCache && !headers.has("Cache-Control")) headers.set("Cache-Control", "no-store");
1323
2101
  if (stripBody) response.body.cancel().catch(() => {});
1324
2102
  return new Response(stripBody ? null : response.body, {
1325
2103
  status: response.status,
@@ -1336,14 +2114,17 @@ exports.FLASH_COOKIE = FLASH_COOKIE;
1336
2114
  exports.GENERIC_SERVER_ERROR_MESSAGE = GENERIC_SERVER_ERROR_MESSAGE;
1337
2115
  exports.GET = GET;
1338
2116
  exports.INSTANCE_HEADER = INSTANCE_HEADER;
2117
+ exports.REDIRECT_HEADER = REDIRECT_HEADER;
1339
2118
  exports.SERVER_FUNCTION_INVOKE = SERVER_FUNCTION_INVOKE;
1340
2119
  exports.SINGLE_FLIGHT_HEADER = SINGLE_FLIGHT_HEADER;
2120
+ exports.UNKNOWN_HEADER = UNKNOWN_HEADER;
1341
2121
  exports.clearFlashCookie = clearFlashCookie;
1342
2122
  exports.configureServerFunctionsServer = configureServerFunctionsServer;
1343
2123
  exports.createNoJSHandler = createNoJSHandler;
1344
2124
  exports.createServerReference = createServerReference;
1345
2125
  exports.decodeErrorHeaderValue = decodeErrorHeaderValue;
1346
2126
  exports.decodeFlashCookie = decodeFlashCookie;
2127
+ exports.decodeRedirectHeaderValue = decodeRedirectHeaderValue;
1347
2128
  exports.decodeResponse = decodeResponse;
1348
2129
  exports.decodeResponsePayload = decodeResponsePayload;
1349
2130
  exports.encodeErrorHeaderValue = encodeErrorHeaderValue;
@@ -1353,6 +2134,7 @@ exports.getEventServerFunctionInvocation = getEventServerFunctionInvocation;
1353
2134
  exports.getServerFunction = getServerFunction;
1354
2135
  exports.getServerFunctionInvocation = getServerFunctionInvocation;
1355
2136
  exports.getServerFunctionMetadata = getServerFunctionMetadata;
2137
+ exports.guardFailures = guardFailures;
1356
2138
  exports.handleServerFunctionRequest = handleServerFunctionRequest;
1357
2139
  exports.hasFlashCookie = hasFlashCookie;
1358
2140
  exports.invoke = invoke;
@@ -1360,6 +2142,7 @@ exports.isServerFunction = isServerFunction;
1360
2142
  exports.live = live;
1361
2143
  exports.observeServerFunctionCalls = observeServerFunctionCalls;
1362
2144
  exports.parseServerFunctionUrl = parseServerFunctionUrl;
2145
+ exports.registerFlightDataSource = registerFlightDataSource;
1363
2146
  exports.registerServerFunction = registerServerFunction;
1364
2147
  exports.registerServerReference = registerServerReference;
1365
2148
  exports.sanitizeServerError = sanitizeServerError;