@solidjs/web 2.0.0-rc.3 → 2.0.0-rc.5

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 (65) hide show
  1. package/dist/dev.cjs +398 -17
  2. package/dist/dev.js +396 -19
  3. package/dist/server.cjs +166 -38
  4. package/dist/server.js +165 -39
  5. package/dist/web.cjs +369 -17
  6. package/dist/web.js +367 -19
  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 +432 -44
  12. package/frames/dist/server.js +432 -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 +271 -47
  25. package/server-functions/dist/client.js +264 -47
  26. package/server-functions/dist/server.cjs +872 -131
  27. package/server-functions/dist/server.dev.cjs +892 -131
  28. package/server-functions/dist/server.dev.js +884 -131
  29. package/server-functions/dist/server.js +864 -131
  30. package/types/client.d.ts +4 -2
  31. package/types/cookies.d.ts +6 -14
  32. package/types/frames/frame-client.d.ts +4 -0
  33. package/types/frames/serializer-decode.d.ts +14 -1
  34. package/types/frames/serializer.d.ts +7 -0
  35. package/types/index.d.ts +1 -0
  36. package/types/jsx.d.ts +11 -16
  37. package/types/patch-driver.d.ts +3 -0
  38. package/types/response.d.ts +20 -1
  39. package/types/serializer-decode.d.ts +14 -1
  40. package/types/serializer.d.ts +7 -0
  41. package/types/server-functions/client.d.ts +58 -7
  42. package/types/server-functions/flash.d.ts +9 -0
  43. package/types/server-functions/registry.d.ts +38 -1
  44. package/types/server-functions/server.d.ts +66 -14
  45. package/types/server-functions/shared.d.ts +122 -16
  46. package/types/server-mock.d.ts +17 -2
  47. package/types/server.d.ts +16 -2
  48. package/types-cjs/client.d.cts +4 -2
  49. package/types-cjs/cookies.d.cts +6 -14
  50. package/types-cjs/frames/frame-client.d.cts +4 -0
  51. package/types-cjs/frames/serializer-decode.d.cts +14 -1
  52. package/types-cjs/frames/serializer.d.cts +7 -0
  53. package/types-cjs/index.d.cts +1 -0
  54. package/types-cjs/jsx.d.cts +11 -16
  55. package/types-cjs/patch-driver.d.cts +3 -0
  56. package/types-cjs/response.d.cts +20 -1
  57. package/types-cjs/serializer-decode.d.cts +14 -1
  58. package/types-cjs/serializer.d.cts +7 -0
  59. package/types-cjs/server-functions/client.d.cts +58 -7
  60. package/types-cjs/server-functions/flash.d.cts +9 -0
  61. package/types-cjs/server-functions/registry.d.cts +38 -1
  62. package/types-cjs/server-functions/server.d.cts +66 -14
  63. package/types-cjs/server-functions/shared.d.cts +122 -16
  64. package/types-cjs/server-mock.d.cts +17 -2
  65. package/types-cjs/server.d.cts +16 -2
@@ -18,6 +18,32 @@ function withMeta(fn, meta) {
18
18
  Object.assign(metadata, meta);
19
19
  return fn;
20
20
  }
21
+ const SERVER_FUNCTION_INVOKE = Symbol.for("solid.ServerFunctionInvoke");
22
+ const INVOKE_OPTION_REDIRECTS = {
23
+ headers: "Session-dynamic headers belong to the prepareRequest hook, declaration metadata to " + "withMeta(fn, meta), and data belongs in the arguments, where it is serialized, typed, " + "and part of the cache key.",
24
+ method: "The method is declaration-scoped: declare the function with GET(fn).",
25
+ body: "The arguments are the body: pass them in the args array.",
26
+ timeout: "Compose timeouts through `signal` with AbortSignal.timeout(ms) (and AbortSignal.any to " + "combine it with a caller signal)."
27
+ };
28
+ function invoke(fn, options, ...args) {
29
+ const channel = typeof fn === "function" && fn[SERVER_FUNCTION_INVOKE];
30
+ if (!channel) {
31
+ throw new Error(isServerFunction(fn) ? "invoke: this wrapper does not forward the invocation channel " + "(SERVER_FUNCTION_INVOKE). Wrappers that share calls (caches, channels) opt in " + "deliberately — a caller's signal cannot own a wire other callers share. Invoke " + "the underlying reference directly, or use the wrapper's own per-call idioms." : "invoke expects a server function reference (or a wrapper that forwards its " + "invocation channel). Per-call options apply at the transport; for a data " + "layer's calls, use its per-call options instead.");
32
+ }
33
+ if (options === null || typeof options !== "object") {
34
+ throw new Error("invoke's second argument is the invocation options bag: invoke(fn, { signal }, ...args)");
35
+ }
36
+ const picked = {};
37
+ for (const key in options) {
38
+ if (key !== "signal" && key !== "keepalive" && key !== "priority") {
39
+ throw new Error(`\`${key}\` is not an invocation option. ` + (INVOKE_OPTION_REDIRECTS[key] || "Options here are strictly invocation-scoped (they vary between calls of the " + "same function): signal, keepalive, priority."));
40
+ }
41
+ }
42
+ if (options.signal !== undefined) picked.signal = options.signal;
43
+ if (options.keepalive !== undefined) picked.keepalive = options.keepalive;
44
+ if (options.priority !== undefined) picked.priority = options.priority;
45
+ return channel(args, picked);
46
+ }
21
47
  const LIVE_SOURCE = Symbol.for("solid.LiveSource");
22
48
  const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
23
49
  function provideServerFunctionRPC(rpc) {
@@ -42,17 +68,30 @@ function configureServerFunctionsCodec(codec) {
42
68
  function getServerFunctionsCodec() {
43
69
  return codecConfig.codec;
44
70
  }
71
+ const UNNAMED_FLIGHT_SOURCE = "true";
45
72
  const flightConfig = {
46
- consumer: undefined
73
+ consumers: new Map()
47
74
  };
48
- function subscribeFlightData(consumer) {
49
- flightConfig.consumer = consumer;
75
+ function assertFlightSource(source) {
76
+ if (source === UNNAMED_FLIGHT_SOURCE || source === "" || source.includes(",")) {
77
+ 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).`);
78
+ }
79
+ }
80
+ function subscribeFlightData(sourceOrConsumer, maybeConsumer) {
81
+ const named = typeof sourceOrConsumer === "string";
82
+ if (named) assertFlightSource(sourceOrConsumer);
83
+ const source = named ? sourceOrConsumer : UNNAMED_FLIGHT_SOURCE;
84
+ const consumer = named ? maybeConsumer : sourceOrConsumer;
85
+ flightConfig.consumers.set(source, consumer);
50
86
  return () => {
51
- if (flightConfig.consumer === consumer) flightConfig.consumer = undefined;
87
+ if (flightConfig.consumers.get(source) === consumer) flightConfig.consumers.delete(source);
52
88
  };
53
89
  }
54
- function getFlightDataConsumer() {
55
- return flightConfig.consumer;
90
+ function getFlightDataConsumer(source) {
91
+ return flightConfig.consumers.get(source === undefined ? UNNAMED_FLIGHT_SOURCE : source);
92
+ }
93
+ function getFlightDataSourceIds() {
94
+ return [...flightConfig.consumers.keys()];
56
95
  }
57
96
  function frameAddress(id, args) {
58
97
  return args && args.length ? id + ":" + hashArguments(args) : id;
@@ -98,7 +137,35 @@ function stableString(value, seen) {
98
137
  }
99
138
  return out + "}";
100
139
  }
101
- const FUNCTION_HEADER = "X-Server-Function-Id";
140
+ function serverFunctionAddress(endpoint, id) {
141
+ const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
142
+ return `${mount}/${encodeURIComponent(id)}`;
143
+ }
144
+ function serverFunctionDataAddress(endpoint, id) {
145
+ const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
146
+ return `${mount}/data/${encodeURIComponent(id)}`;
147
+ }
148
+ function parseServerFunctionAddress(pathname, endpoint) {
149
+ const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
150
+ if (!pathname.startsWith(mount)) return null;
151
+ const rest = pathname.slice(mount.length);
152
+ if (!rest.startsWith("/")) return null;
153
+ let segment = rest.slice(1);
154
+ let data = false;
155
+ if (segment.startsWith("data/")) {
156
+ segment = segment.slice(5);
157
+ data = true;
158
+ }
159
+ if (!segment || segment.includes("/")) return null;
160
+ try {
161
+ return {
162
+ id: decodeURIComponent(segment),
163
+ data
164
+ };
165
+ } catch {
166
+ return null;
167
+ }
168
+ }
102
169
  const ERROR_HEADER = "X-Server-Function-Error";
103
170
  const ERROR_HEADER_MARKER = "=?1?";
104
171
  const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
@@ -126,6 +193,28 @@ function decodeErrorHeaderValue(value) {
126
193
  }
127
194
  const INSTANCE_HEADER = "X-Server-Function-Instance";
128
195
  const BODY_FORMAT_HEADER = "X-Server-Function-Format";
196
+ const UNKNOWN_HEADER = "X-Server-Function-Unknown";
197
+ const REDIRECT_HEADER = "X-Server-Function-Redirect";
198
+ function decodeRedirectHeaderValue(value) {
199
+ if (typeof value !== "string") return undefined;
200
+ const at = value.indexOf(" ");
201
+ if (at < 0) return undefined;
202
+ const status = Number(value.slice(0, at));
203
+ const url = value.slice(at + 1);
204
+ if (!Number.isInteger(status) || !url) return undefined;
205
+ if (status !== 301 && status !== 302 && status !== 303 && status !== 307 && status !== 308) return undefined;
206
+ let parsed;
207
+ try {
208
+ parsed = new URL(url);
209
+ } catch {
210
+ return undefined;
211
+ }
212
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return undefined;
213
+ return {
214
+ status,
215
+ url
216
+ };
217
+ }
129
218
  const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
130
219
  const FILE_FORM_KEY = "__server_function_file__";
131
220
  const BodyFormat = {
@@ -137,7 +226,8 @@ const BodyFormat = {
137
226
  File: "5",
138
227
  ArrayBuffer: "6",
139
228
  Uint8Array: "7",
140
- Json: "8"
229
+ Json: "8",
230
+ Void: "9"
141
231
  };
142
232
  const JSON_SAFE_DEPTH_LIMIT = 4096;
143
233
  const EXIT = {};
@@ -167,7 +257,11 @@ function isJSONSafe(value) {
167
257
  const proto = Object.getPrototypeOf(v);
168
258
  if (proto !== Object.prototype && proto !== null) return false;
169
259
  if (Symbol.asyncIterator in v || Symbol.iterator in v) return false;
170
- for (const k in v) stack.push(v[k]);
260
+ for (const k in v) {
261
+ const descriptor = Object.getOwnPropertyDescriptor(v, k);
262
+ if (descriptor === undefined || !("value" in descriptor)) return false;
263
+ stack.push(descriptor.value);
264
+ }
171
265
  }
172
266
  }
173
267
  return true;
@@ -276,18 +370,34 @@ function createChunk(data) {
276
370
  class ChunkReader {
277
371
  constructor(stream) {
278
372
  this.reader = stream.getReader();
279
- this.buffer = new Uint8Array(0);
373
+ this.store = new Uint8Array(0);
374
+ this.buffer = this.store;
280
375
  this.done = false;
281
376
  }
282
377
  async readChunk() {
283
378
  const chunk = await this.reader.read();
284
- if (!chunk.done) {
285
- const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
286
- newBuffer.set(this.buffer);
287
- newBuffer.set(chunk.value, this.buffer.length);
288
- this.buffer = newBuffer;
289
- } else {
379
+ if (chunk.done) {
290
380
  this.done = true;
381
+ return;
382
+ }
383
+ const incoming = chunk.value;
384
+ const store = this.store;
385
+ const start = this.buffer.byteOffset;
386
+ const end = start + this.buffer.length;
387
+ const needed = this.buffer.length + incoming.length;
388
+ if (end + incoming.length <= store.length) {
389
+ store.set(incoming, end);
390
+ this.buffer = store.subarray(start, end + incoming.length);
391
+ } else if (needed <= store.length) {
392
+ store.copyWithin(0, start, end);
393
+ store.set(incoming, this.buffer.length);
394
+ this.buffer = store.subarray(0, needed);
395
+ } else {
396
+ const grown = new Uint8Array(Math.max(needed, store.length * 2));
397
+ grown.set(this.buffer);
398
+ grown.set(incoming, this.buffer.length);
399
+ this.store = grown;
400
+ this.buffer = grown.subarray(0, needed);
291
401
  }
292
402
  }
293
403
  async next() {
@@ -329,6 +439,18 @@ class ChunkReader {
329
439
  }
330
440
  }
331
441
  }
442
+ const ERROR_TRAILER_PREFIX = "!";
443
+ function errorFromTrailer(payload) {
444
+ let shape;
445
+ try {
446
+ shape = JSON.parse(payload.slice(1));
447
+ } catch {
448
+ shape = null;
449
+ }
450
+ const error = new Error(shape && typeof shape.message === "string" ? shape.message : "Server function result could not be delivered.");
451
+ if (shape && typeof shape.name === "string") error.name = shape.name;
452
+ return error;
453
+ }
332
454
  function serializeStream(value, codecOptions) {
333
455
  return new ReadableStream({
334
456
  async start(controller) {
@@ -361,11 +483,17 @@ async function deserializeStream(source, codecOptions) {
361
483
  const reader = new ChunkReader(source.body);
362
484
  const result = await reader.next();
363
485
  if (!result.done) {
486
+ if (result.value.startsWith(ERROR_TRAILER_PREFIX)) {
487
+ throw errorFromTrailer(result.value);
488
+ }
364
489
  const {
365
490
  createJSONDeserializer
366
491
  } = await import('@solidjs/web/serialization/decode');
367
492
  const deserializeChunk = createJSONDeserializer(codecOptions);
368
493
  function interpretChunk(chunk) {
494
+ if (chunk.startsWith(ERROR_TRAILER_PREFIX)) {
495
+ throw errorFromTrailer(chunk);
496
+ }
369
497
  return deserializeChunk(JSON.parse(chunk));
370
498
  }
371
499
  reader.drain(interpretChunk).then(() => deserializeChunk.abort(new Error("Server function stream ended unexpectedly.")), error => deserializeChunk.abort(error));
@@ -392,6 +520,7 @@ async function decodeResponsePayload(response, codecOptions) {
392
520
 
393
521
  const config = {
394
522
  endpoint: "/_server",
523
+ fetch: undefined,
395
524
  prepareRequest: undefined,
396
525
  responseHandler: undefined,
397
526
  serializeArgs: undefined
@@ -420,6 +549,18 @@ function observeServerFunctionCalls(observer) {
420
549
  CALL_OBSERVERS.add(observer);
421
550
  return () => CALL_OBSERVERS.delete(observer);
422
551
  }
552
+ function serverFunctionUrl(id, boundArgs) {
553
+ const address = serverFunctionAddress(config.endpoint, id);
554
+ if (!boundArgs || !boundArgs.length) return address;
555
+ if (!isJSONSafe(boundArgs)) {
556
+ throw new Error("Bound arguments in an action url must be JSON-safe: the server reads them the way it " + "reads a form post's, and that convention has no codec. Pass the value through the " + "function's body, or call the reference instead of rendering a url for it.");
557
+ }
558
+ return `${address}?args=${encodeURIComponent(JSON.stringify(boundArgs))}`;
559
+ }
560
+ function parseServerFunctionUrl(url) {
561
+ const parsed = parseServerFunctionAddress(new URL(url, globalThis.location?.href || "http://localhost").pathname, config.endpoint);
562
+ return parsed && parsed.id;
563
+ }
423
564
  function serializeArguments(args) {
424
565
  if (!config.serializeArgs) {
425
566
  throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " + '(from "@solidjs/web/server-functions/rich-args") once at startup ' + "to send Dates, Maps, Sets, typed arrays, etc. through the codec — " + "or pass a single Blob/FormData/File argument, which has a native " + "HTTP encoding.");
@@ -429,17 +570,20 @@ function serializeArguments(args) {
429
570
  function configureServerFunctionsClient({
430
571
  endpoint,
431
572
  codec,
573
+ fetch,
432
574
  prepareRequest,
433
575
  responseHandler,
434
576
  serializeArgs
435
577
  } = {}) {
436
578
  if (endpoint !== undefined) config.endpoint = endpoint;
437
579
  if (codec !== undefined) configureServerFunctionsCodec(codec);
580
+ if (fetch !== undefined) config.fetch = fetch;
438
581
  if (prepareRequest !== undefined) config.prepareRequest = prepareRequest;
439
582
  if (responseHandler !== undefined) config.responseHandler = responseHandler;
440
583
  if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
441
584
  }
442
585
  let INSTANCE = 0;
586
+ const MAX_GET_URL_LENGTH = 2000;
443
587
  let rpcProvided = false;
444
588
  function provideRPC() {
445
589
  if (rpcProvided) return;
@@ -449,19 +593,41 @@ function provideRPC() {
449
593
  decodeResponse
450
594
  });
451
595
  }
596
+ function dataAddressFor(base) {
597
+ const splitAt = base.search(/[?#]/);
598
+ const path = splitAt < 0 ? base : base.slice(0, splitAt);
599
+ const rest = splitAt < 0 ? "" : base.slice(splitAt);
600
+ const slash = path.lastIndexOf("/");
601
+ if (path.endsWith("/data/", slash + 1)) return base;
602
+ return `${path.slice(0, slash + 1)}data/${path.slice(slash + 1)}${rest}`;
603
+ }
452
604
  function serverFunctionFailure(response, value) {
453
- const error = value ?? new Error(`Server function call failed with status ${response.status}`);
454
- if (error instanceof Error && !("status" in error)) error.status = response.status;
605
+ const unknown = response.headers.get(UNKNOWN_HEADER) !== null;
606
+ const error = value ?? new Error(unknown ? "Server function is not part of the deployment that answered (version skew or removed function)" : `Server function call failed with status ${response.status}`);
607
+ if (error instanceof Error && !("status" in error)) {
608
+ error.status = response.status;
609
+ const retryAfter = parseRetryAfter(response.headers.get("Retry-After"));
610
+ if (retryAfter !== undefined) error.retryAfter = retryAfter;
611
+ }
612
+ if (unknown && error instanceof Error) error.unknownFunction = true;
455
613
  return error;
456
614
  }
615
+ function parseRetryAfter(header) {
616
+ if (!header) return undefined;
617
+ const trimmed = header.trim();
618
+ if (/^\d+$/.test(trimmed)) return Number(trimmed);
619
+ const date = Date.parse(trimmed);
620
+ if (!Number.isNaN(date)) return Math.max(0, Math.ceil((date - Date.now()) / 1000));
621
+ return undefined;
622
+ }
457
623
  async function createRequest(base, id, instance, options, meta) {
458
624
  const headers = {
459
625
  ...options.headers,
460
- [FUNCTION_HEADER]: id,
461
626
  [INSTANCE_HEADER]: instance
462
627
  };
463
- if (getFlightDataConsumer() && !options.read && (!options.method || options.method.toUpperCase() !== "GET")) {
464
- headers[SINGLE_FLIGHT_HEADER] = "true";
628
+ const flightSources = getFlightDataSourceIds();
629
+ if (flightSources.length > 0 && !options.read && (!options.method || options.method.toUpperCase() !== "GET")) {
630
+ headers[SINGLE_FLIGHT_HEADER] = flightSources.join(",");
465
631
  }
466
632
  let init = {
467
633
  method: "POST",
@@ -474,10 +640,14 @@ async function createRequest(base, id, instance, options, meta) {
474
640
  meta
475
641
  })) || init;
476
642
  }
477
- if (CALL_OBSERVERS.size === 0) return fetch(base, init);
478
- const request = new Request(new URL(base, globalThis.location?.href || "http://localhost"), init);
643
+ const send = config.fetch || fetch;
644
+ if (CALL_OBSERVERS.size === 0) return send(base, init);
645
+ const request = new Request(new URL(base, globalThis.location?.href || "http://localhost"), {
646
+ ...init,
647
+ body: init.body instanceof ReadableStream ? undefined : init.body
648
+ });
479
649
  notifyCallObservers("request", id, instance, request, meta);
480
- const response = await fetch(request);
650
+ const response = await send(base, init);
481
651
  notifyCallObservers("response", id, instance, response, meta);
482
652
  return response;
483
653
  }
@@ -562,21 +732,27 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
562
732
  });
563
733
  if (handled !== undefined) return handled;
564
734
  }
565
- const failed = response.headers.has(ERROR_HEADER) || response.status >= 500;
735
+ if (response.status >= 400 && !response.headers.has(BODY_FORMAT_HEADER)) {
736
+ throw serverFunctionFailure(response, undefined);
737
+ }
738
+ const failed = response.headers.has(ERROR_HEADER);
566
739
  if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
567
- const consumer = getFlightDataConsumer();
568
- if (consumer) {
740
+ const folded = response.headers.get(SINGLE_FLIGHT_HEADER).split(",");
741
+ const consumers = folded.map(source => [source, getFlightDataConsumer(source)]).filter(([, consumer]) => consumer);
742
+ if (consumers.length > 0) {
569
743
  const payload = await decodeResponse(response);
570
- await consumer(payload.data, {
571
- response
572
- });
573
- if (failed && !response.headers.has("Location") && !response.headers.has(REVALIDATE_HEADER)) {
744
+ for (const [source, consumer] of consumers) {
745
+ await consumer(payload.data[source], {
746
+ response
747
+ });
748
+ }
749
+ if (failed && !response.headers.has(REDIRECT_HEADER) && !response.headers.has(REVALIDATE_HEADER)) {
574
750
  throw serverFunctionFailure(response, payload.value);
575
751
  }
576
752
  return payload.value;
577
753
  }
578
754
  }
579
- if (response.headers.has("Location") || response.headers.has(REVALIDATE_HEADER) || response.headers.has(SINGLE_FLIGHT_HEADER)) {
755
+ if (response.headers.has(REDIRECT_HEADER) || response.headers.has(REVALIDATE_HEADER) || response.headers.has(SINGLE_FLIGHT_HEADER) || response.status >= 300 && response.status < 400 && response.status !== 304) {
580
756
  return response;
581
757
  }
582
758
  const result = await decodeResponse(response.clone());
@@ -604,7 +780,7 @@ function createServerReference(id, name, base) {
604
780
  const metadata = name === undefined ? {} : {
605
781
  name
606
782
  };
607
- const fn = (...args) => {
783
+ const run = (args, invokeOptions) => {
608
784
  const handler = config.responseHandler;
609
785
  if (handler && handler.intercept) {
610
786
  const hit = handler.intercept({
@@ -614,14 +790,18 @@ function createServerReference(id, name, base) {
614
790
  });
615
791
  if (hit !== undefined) return hit;
616
792
  }
617
- return fetchServerFunction(base || config.endpoint, id, {}, args, metadata);
793
+ return fetchServerFunction(base ? dataAddressFor(base) : serverFunctionDataAddress(config.endpoint, id), id, invokeOptions ? {
794
+ ...invokeOptions
795
+ } : {}, args, metadata);
618
796
  };
797
+ const fn = (...args) => run(args);
619
798
  fn[SERVER_FUNCTION_METADATA] = metadata;
799
+ fn[SERVER_FUNCTION_INVOKE] = run;
620
800
  return new Proxy(fn, {
621
801
  get(target, prop) {
622
802
  if (prop === "id") return id;
623
803
  if (prop === "url") {
624
- return base || `${config.endpoint}?id=${encodeURIComponent(id)}`;
804
+ return base || serverFunctionAddress(config.endpoint, id);
625
805
  }
626
806
  return target[prop];
627
807
  }
@@ -636,7 +816,7 @@ function GET(fn) {
636
816
  const metadata = {
637
817
  ...getServerFunctionMetadata(fn)
638
818
  };
639
- const wrapped = async (...args) => {
819
+ const run = async (args, invokeOptions) => {
640
820
  const handler = config.responseHandler;
641
821
  if (handler && handler.intercept) {
642
822
  const hit = handler.intercept({
@@ -646,19 +826,34 @@ function GET(fn) {
646
826
  });
647
827
  if (hit !== undefined) return hit;
648
828
  }
649
- let base = `${config.endpoint}?id=${encodeURIComponent(id)}`;
650
- if (args.length) {
651
- const encoded = isJSONSafe(args) ? JSON.stringify(args) : await serializeArguments(args);
652
- base += `&args=${encodeURIComponent(encoded)}`;
829
+ const opts = invokeOptions || {};
830
+ const address = serverFunctionDataAddress(config.endpoint, id);
831
+ if (!args.length) {
832
+ return fetchServerFunction(address, id, {
833
+ ...opts,
834
+ method: "GET"
835
+ }, [], metadata, args);
653
836
  }
654
- return fetchServerFunction(base, id, {
837
+ const encoded = isJSONSafe(args) ? JSON.stringify(args) : await serializeArguments(args);
838
+ const url = `${address}?args=${encodeURIComponent(encoded)}`;
839
+ const absolute = new URL(url, globalThis.location?.href || "http://localhost").href;
840
+ if (absolute.length > MAX_GET_URL_LENGTH) {
841
+ return fetchServerFunction(address, id, {
842
+ ...opts,
843
+ read: true
844
+ }, args, metadata);
845
+ }
846
+ return fetchServerFunction(url, id, {
847
+ ...opts,
655
848
  method: "GET"
656
849
  }, [], metadata, args);
657
850
  };
851
+ const wrapped = (...args) => run(args);
658
852
  wrapped[SERVER_FUNCTION_METADATA] = metadata;
853
+ wrapped[SERVER_FUNCTION_INVOKE] = run;
659
854
  wrapped.id = id;
660
855
  Object.defineProperty(wrapped, "url", {
661
- get: () => `${config.endpoint}?id=${encodeURIComponent(id)}`,
856
+ get: () => serverFunctionAddress(config.endpoint, id),
662
857
  configurable: true
663
858
  });
664
859
  return withMeta(wrapped, {
@@ -674,7 +869,7 @@ function live(fn) {
674
869
  ...getServerFunctionMetadata(fn),
675
870
  live: true
676
871
  };
677
- const wrapped = (...args) => {
872
+ const makeIterable = (args, invokeOptions) => {
678
873
  const iterable = {
679
874
  [LIVE_SOURCE]: true,
680
875
  [Symbol.asyncIterator]() {
@@ -688,6 +883,12 @@ function live(fn) {
688
883
  done: true,
689
884
  value: undefined
690
885
  };
886
+ const invokeSignal = invokeOptions && invokeOptions.signal;
887
+ const controller = new AbortController();
888
+ const wireOptions = {
889
+ ...invokeOptions,
890
+ signal: invokeSignal ? AbortSignal.any([invokeSignal, controller.signal]) : controller.signal
891
+ };
691
892
  const emit = (state, error) => {
692
893
  try {
693
894
  iterable.onstatus && iterable.onstatus(state, error);
@@ -709,7 +910,7 @@ function live(fn) {
709
910
  }
710
911
  };
711
912
  const callOnce = () => {
712
- if (metadata.method === "GET") return fn(...args);
913
+ if (metadata.method === "GET") return fn[SERVER_FUNCTION_INVOKE](args, wireOptions);
713
914
  const handler = config.responseHandler;
714
915
  if (handler && handler.intercept) {
715
916
  const hit = handler.intercept({
@@ -720,6 +921,7 @@ function live(fn) {
720
921
  if (hit !== undefined) return hit;
721
922
  }
722
923
  return fetchServerFunction(fn.url, id, {
924
+ ...wireOptions,
723
925
  read: true
724
926
  }, args, metadata, args);
725
927
  };
@@ -734,6 +936,7 @@ function live(fn) {
734
936
  }();
735
937
  if (stopped) {
736
938
  closeIt();
939
+ controller.abort();
737
940
  return DONE;
738
941
  }
739
942
  emit("connected");
@@ -747,8 +950,14 @@ function live(fn) {
747
950
  attempts = 0;
748
951
  return r;
749
952
  } catch (error) {
953
+ if (stopped) return DONE;
750
954
  if (!connected) throw error;
751
- if (error !== null && typeof error === "object" && typeof error.status === "number" && error.status >= 400 && error.status < 500) {
955
+ if (invokeSignal && invokeSignal.aborted) {
956
+ stopped = true;
957
+ emitClosed(error);
958
+ throw error;
959
+ }
960
+ if (error !== null && typeof error === "object" && typeof error.status === "number" && error.status >= 400 && error.status < 500 && error.status !== 408 && error.status !== 425 && error.status !== 429 && typeof error.retryAfter !== "number") {
752
961
  stopped = true;
753
962
  emitClosed(error);
754
963
  throw error;
@@ -757,13 +966,18 @@ function live(fn) {
757
966
  emit("reconnecting", error);
758
967
  await new Promise(resolve => {
759
968
  wake = resolve;
760
- timer = setTimeout(resolve, Math.min(500 * 2 ** attempts++, 10000));
969
+ const named = error !== null && typeof error === "object" && typeof error.retryAfter === "number" ? Math.min(error.retryAfter * 1000, 60000) : undefined;
970
+ timer = setTimeout(resolve, named ?? Math.min(500 * 2 ** attempts++, 10000));
761
971
  if (typeof addEventListener === "function") addEventListener("online", resolve, {
762
972
  once: true
763
973
  });
974
+ if (invokeSignal) invokeSignal.addEventListener("abort", resolve, {
975
+ once: true
976
+ });
764
977
  });
765
978
  clearTimeout(timer);
766
979
  if (typeof removeEventListener === "function") removeEventListener("online", wake);
980
+ if (invokeSignal) invokeSignal.removeEventListener("abort", wake);
767
981
  timer = wake = undefined;
768
982
  }
769
983
  }
@@ -776,6 +990,7 @@ function live(fn) {
776
990
  if (timer !== undefined) clearTimeout(timer);
777
991
  if (wake) wake();
778
992
  closeIt(value);
993
+ controller.abort();
779
994
  emitClosed();
780
995
  return Promise.resolve({
781
996
  done: true,
@@ -787,7 +1002,9 @@ function live(fn) {
787
1002
  };
788
1003
  return iterable;
789
1004
  };
1005
+ const wrapped = (...args) => makeIterable(args);
790
1006
  wrapped[SERVER_FUNCTION_METADATA] = metadata;
1007
+ wrapped[SERVER_FUNCTION_INVOKE] = makeIterable;
791
1008
  wrapped.id = id;
792
1009
  Object.defineProperty(wrapped, "url", {
793
1010
  get: () => fn.url,
@@ -805,30 +1022,37 @@ function getServerFunctionInvocation() {
805
1022
  exports.ChunkReader = ChunkReader;
806
1023
  exports.ERROR_HEADER = ERROR_HEADER;
807
1024
  exports.FLASH_COOKIE = FLASH_COOKIE;
808
- exports.FUNCTION_HEADER = FUNCTION_HEADER;
809
1025
  exports.GET = GET;
810
1026
  exports.INSTANCE_HEADER = INSTANCE_HEADER;
1027
+ exports.REDIRECT_HEADER = REDIRECT_HEADER;
811
1028
  exports.REVALIDATE_HEADER = REVALIDATE_HEADER;
1029
+ exports.SERVER_FUNCTION_INVOKE = SERVER_FUNCTION_INVOKE;
812
1030
  exports.SINGLE_FLIGHT_HEADER = SINGLE_FLIGHT_HEADER;
1031
+ exports.UNKNOWN_HEADER = UNKNOWN_HEADER;
813
1032
  exports.clearFlashCookie = clearFlashCookie;
814
1033
  exports.configureServerFunctionsClient = configureServerFunctionsClient;
815
1034
  exports.createChunk = createChunk;
816
1035
  exports.createServerReference = createServerReference;
817
1036
  exports.decodeErrorHeaderValue = decodeErrorHeaderValue;
1037
+ exports.decodeRedirectHeaderValue = decodeRedirectHeaderValue;
818
1038
  exports.decodeResponse = decodeResponse;
819
1039
  exports.decodeResponsePayload = decodeResponsePayload;
820
1040
  exports.deserializeStream = deserializeStream;
821
1041
  exports.encodeErrorHeaderValue = encodeErrorHeaderValue;
822
1042
  exports.frameAddress = frameAddress;
823
1043
  exports.getFlightDataConsumer = getFlightDataConsumer;
1044
+ exports.getFlightDataSourceIds = getFlightDataSourceIds;
824
1045
  exports.getServerFunctionInvocation = getServerFunctionInvocation;
825
1046
  exports.getServerFunctionMetadata = getServerFunctionMetadata;
826
1047
  exports.getServerFunctionsCodec = getServerFunctionsCodec;
827
1048
  exports.hasFlashCookie = hasFlashCookie;
1049
+ exports.invoke = invoke;
828
1050
  exports.isServerFunction = isServerFunction;
829
1051
  exports.live = live;
830
1052
  exports.observeServerFunctionCalls = observeServerFunctionCalls;
1053
+ exports.parseServerFunctionUrl = parseServerFunctionUrl;
831
1054
  exports.registerServerReference = registerServerReference;
832
1055
  exports.serializeString = serializeString;
1056
+ exports.serverFunctionUrl = serverFunctionUrl;
833
1057
  exports.subscribeFlightData = subscribeFlightData;
834
1058
  exports.withMeta = withMeta;