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

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.
@@ -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) {
@@ -98,7 +124,23 @@ function stableString(value, seen) {
98
124
  }
99
125
  return out + "}";
100
126
  }
101
- const FUNCTION_HEADER = "X-Server-Function-Id";
127
+ function serverFunctionAddress(endpoint, id) {
128
+ const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
129
+ return `${mount}/${encodeURIComponent(id)}`;
130
+ }
131
+ function parseServerFunctionAddress(pathname, endpoint) {
132
+ const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
133
+ if (!pathname.startsWith(mount)) return null;
134
+ const rest = pathname.slice(mount.length);
135
+ if (!rest.startsWith("/")) return null;
136
+ const segment = rest.slice(1);
137
+ if (!segment || segment.includes("/")) return null;
138
+ try {
139
+ return decodeURIComponent(segment);
140
+ } catch {
141
+ return null;
142
+ }
143
+ }
102
144
  const ERROR_HEADER = "X-Server-Function-Error";
103
145
  const ERROR_HEADER_MARKER = "=?1?";
104
146
  const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
@@ -137,7 +179,8 @@ const BodyFormat = {
137
179
  File: "5",
138
180
  ArrayBuffer: "6",
139
181
  Uint8Array: "7",
140
- Json: "8"
182
+ Json: "8",
183
+ Void: "9"
141
184
  };
142
185
  const JSON_SAFE_DEPTH_LIMIT = 4096;
143
186
  const EXIT = {};
@@ -392,6 +435,7 @@ async function decodeResponsePayload(response, codecOptions) {
392
435
 
393
436
  const config = {
394
437
  endpoint: "/_server",
438
+ fetch: undefined,
395
439
  prepareRequest: undefined,
396
440
  responseHandler: undefined,
397
441
  serializeArgs: undefined
@@ -420,6 +464,17 @@ function observeServerFunctionCalls(observer) {
420
464
  CALL_OBSERVERS.add(observer);
421
465
  return () => CALL_OBSERVERS.delete(observer);
422
466
  }
467
+ function serverFunctionUrl(id, boundArgs) {
468
+ const address = serverFunctionAddress(config.endpoint, id);
469
+ if (!boundArgs || !boundArgs.length) return address;
470
+ if (!isJSONSafe(boundArgs)) {
471
+ 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.");
472
+ }
473
+ return `${address}?args=${encodeURIComponent(JSON.stringify(boundArgs))}`;
474
+ }
475
+ function parseServerFunctionUrl(url) {
476
+ return parseServerFunctionAddress(new URL(url, globalThis.location?.href || "http://localhost").pathname, config.endpoint);
477
+ }
423
478
  function serializeArguments(args) {
424
479
  if (!config.serializeArgs) {
425
480
  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 +484,20 @@ function serializeArguments(args) {
429
484
  function configureServerFunctionsClient({
430
485
  endpoint,
431
486
  codec,
487
+ fetch,
432
488
  prepareRequest,
433
489
  responseHandler,
434
490
  serializeArgs
435
491
  } = {}) {
436
492
  if (endpoint !== undefined) config.endpoint = endpoint;
437
493
  if (codec !== undefined) configureServerFunctionsCodec(codec);
494
+ if (fetch !== undefined) config.fetch = fetch;
438
495
  if (prepareRequest !== undefined) config.prepareRequest = prepareRequest;
439
496
  if (responseHandler !== undefined) config.responseHandler = responseHandler;
440
497
  if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
441
498
  }
442
499
  let INSTANCE = 0;
500
+ const MAX_GET_URL_LENGTH = 2000;
443
501
  let rpcProvided = false;
444
502
  function provideRPC() {
445
503
  if (rpcProvided) return;
@@ -457,7 +515,6 @@ function serverFunctionFailure(response, value) {
457
515
  async function createRequest(base, id, instance, options, meta) {
458
516
  const headers = {
459
517
  ...options.headers,
460
- [FUNCTION_HEADER]: id,
461
518
  [INSTANCE_HEADER]: instance
462
519
  };
463
520
  if (getFlightDataConsumer() && !options.read && (!options.method || options.method.toUpperCase() !== "GET")) {
@@ -474,10 +531,14 @@ async function createRequest(base, id, instance, options, meta) {
474
531
  meta
475
532
  })) || init;
476
533
  }
477
- if (CALL_OBSERVERS.size === 0) return fetch(base, init);
478
- const request = new Request(new URL(base, globalThis.location?.href || "http://localhost"), init);
534
+ const send = config.fetch || fetch;
535
+ if (CALL_OBSERVERS.size === 0) return send(base, init);
536
+ const request = new Request(new URL(base, globalThis.location?.href || "http://localhost"), {
537
+ ...init,
538
+ body: init.body instanceof ReadableStream ? undefined : init.body
539
+ });
479
540
  notifyCallObservers("request", id, instance, request, meta);
480
- const response = await fetch(request);
541
+ const response = await send(base, init);
481
542
  notifyCallObservers("response", id, instance, response, meta);
482
543
  return response;
483
544
  }
@@ -562,6 +623,9 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
562
623
  });
563
624
  if (handled !== undefined) return handled;
564
625
  }
626
+ if (response.status >= 400 && !response.headers.has(BODY_FORMAT_HEADER)) {
627
+ throw serverFunctionFailure(response, undefined);
628
+ }
565
629
  const failed = response.headers.has(ERROR_HEADER) || response.status >= 500;
566
630
  if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
567
631
  const consumer = getFlightDataConsumer();
@@ -604,7 +668,7 @@ function createServerReference(id, name, base) {
604
668
  const metadata = name === undefined ? {} : {
605
669
  name
606
670
  };
607
- const fn = (...args) => {
671
+ const run = (args, invokeOptions) => {
608
672
  const handler = config.responseHandler;
609
673
  if (handler && handler.intercept) {
610
674
  const hit = handler.intercept({
@@ -614,14 +678,18 @@ function createServerReference(id, name, base) {
614
678
  });
615
679
  if (hit !== undefined) return hit;
616
680
  }
617
- return fetchServerFunction(base || config.endpoint, id, {}, args, metadata);
681
+ return fetchServerFunction(base || serverFunctionAddress(config.endpoint, id), id, invokeOptions ? {
682
+ ...invokeOptions
683
+ } : {}, args, metadata);
618
684
  };
685
+ const fn = (...args) => run(args);
619
686
  fn[SERVER_FUNCTION_METADATA] = metadata;
687
+ fn[SERVER_FUNCTION_INVOKE] = run;
620
688
  return new Proxy(fn, {
621
689
  get(target, prop) {
622
690
  if (prop === "id") return id;
623
691
  if (prop === "url") {
624
- return base || `${config.endpoint}?id=${encodeURIComponent(id)}`;
692
+ return base || serverFunctionAddress(config.endpoint, id);
625
693
  }
626
694
  return target[prop];
627
695
  }
@@ -636,7 +704,7 @@ function GET(fn) {
636
704
  const metadata = {
637
705
  ...getServerFunctionMetadata(fn)
638
706
  };
639
- const wrapped = async (...args) => {
707
+ const run = async (args, invokeOptions) => {
640
708
  const handler = config.responseHandler;
641
709
  if (handler && handler.intercept) {
642
710
  const hit = handler.intercept({
@@ -646,19 +714,34 @@ function GET(fn) {
646
714
  });
647
715
  if (hit !== undefined) return hit;
648
716
  }
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)}`;
717
+ const opts = invokeOptions || {};
718
+ const address = serverFunctionAddress(config.endpoint, id);
719
+ if (!args.length) {
720
+ return fetchServerFunction(address, id, {
721
+ ...opts,
722
+ method: "GET"
723
+ }, [], metadata, args);
724
+ }
725
+ const encoded = isJSONSafe(args) ? JSON.stringify(args) : await serializeArguments(args);
726
+ const url = `${address}?args=${encodeURIComponent(encoded)}`;
727
+ const absolute = new URL(url, globalThis.location?.href || "http://localhost").href;
728
+ if (absolute.length > MAX_GET_URL_LENGTH) {
729
+ return fetchServerFunction(address, id, {
730
+ ...opts,
731
+ read: true
732
+ }, args, metadata);
653
733
  }
654
- return fetchServerFunction(base, id, {
734
+ return fetchServerFunction(url, id, {
735
+ ...opts,
655
736
  method: "GET"
656
737
  }, [], metadata, args);
657
738
  };
739
+ const wrapped = (...args) => run(args);
658
740
  wrapped[SERVER_FUNCTION_METADATA] = metadata;
741
+ wrapped[SERVER_FUNCTION_INVOKE] = run;
659
742
  wrapped.id = id;
660
743
  Object.defineProperty(wrapped, "url", {
661
- get: () => `${config.endpoint}?id=${encodeURIComponent(id)}`,
744
+ get: () => serverFunctionAddress(config.endpoint, id),
662
745
  configurable: true
663
746
  });
664
747
  return withMeta(wrapped, {
@@ -674,7 +757,7 @@ function live(fn) {
674
757
  ...getServerFunctionMetadata(fn),
675
758
  live: true
676
759
  };
677
- const wrapped = (...args) => {
760
+ const makeIterable = (args, invokeOptions) => {
678
761
  const iterable = {
679
762
  [LIVE_SOURCE]: true,
680
763
  [Symbol.asyncIterator]() {
@@ -688,6 +771,12 @@ function live(fn) {
688
771
  done: true,
689
772
  value: undefined
690
773
  };
774
+ const invokeSignal = invokeOptions && invokeOptions.signal;
775
+ const controller = new AbortController();
776
+ const wireOptions = {
777
+ ...invokeOptions,
778
+ signal: invokeSignal ? AbortSignal.any([invokeSignal, controller.signal]) : controller.signal
779
+ };
691
780
  const emit = (state, error) => {
692
781
  try {
693
782
  iterable.onstatus && iterable.onstatus(state, error);
@@ -709,7 +798,7 @@ function live(fn) {
709
798
  }
710
799
  };
711
800
  const callOnce = () => {
712
- if (metadata.method === "GET") return fn(...args);
801
+ if (metadata.method === "GET") return fn[SERVER_FUNCTION_INVOKE](args, wireOptions);
713
802
  const handler = config.responseHandler;
714
803
  if (handler && handler.intercept) {
715
804
  const hit = handler.intercept({
@@ -720,6 +809,7 @@ function live(fn) {
720
809
  if (hit !== undefined) return hit;
721
810
  }
722
811
  return fetchServerFunction(fn.url, id, {
812
+ ...wireOptions,
723
813
  read: true
724
814
  }, args, metadata, args);
725
815
  };
@@ -734,6 +824,7 @@ function live(fn) {
734
824
  }();
735
825
  if (stopped) {
736
826
  closeIt();
827
+ controller.abort();
737
828
  return DONE;
738
829
  }
739
830
  emit("connected");
@@ -747,7 +838,13 @@ function live(fn) {
747
838
  attempts = 0;
748
839
  return r;
749
840
  } catch (error) {
841
+ if (stopped) return DONE;
750
842
  if (!connected) throw error;
843
+ if (invokeSignal && invokeSignal.aborted) {
844
+ stopped = true;
845
+ emitClosed(error);
846
+ throw error;
847
+ }
751
848
  if (error !== null && typeof error === "object" && typeof error.status === "number" && error.status >= 400 && error.status < 500) {
752
849
  stopped = true;
753
850
  emitClosed(error);
@@ -761,9 +858,13 @@ function live(fn) {
761
858
  if (typeof addEventListener === "function") addEventListener("online", resolve, {
762
859
  once: true
763
860
  });
861
+ if (invokeSignal) invokeSignal.addEventListener("abort", resolve, {
862
+ once: true
863
+ });
764
864
  });
765
865
  clearTimeout(timer);
766
866
  if (typeof removeEventListener === "function") removeEventListener("online", wake);
867
+ if (invokeSignal) invokeSignal.removeEventListener("abort", wake);
767
868
  timer = wake = undefined;
768
869
  }
769
870
  }
@@ -776,6 +877,7 @@ function live(fn) {
776
877
  if (timer !== undefined) clearTimeout(timer);
777
878
  if (wake) wake();
778
879
  closeIt(value);
880
+ controller.abort();
779
881
  emitClosed();
780
882
  return Promise.resolve({
781
883
  done: true,
@@ -787,7 +889,9 @@ function live(fn) {
787
889
  };
788
890
  return iterable;
789
891
  };
892
+ const wrapped = (...args) => makeIterable(args);
790
893
  wrapped[SERVER_FUNCTION_METADATA] = metadata;
894
+ wrapped[SERVER_FUNCTION_INVOKE] = makeIterable;
791
895
  wrapped.id = id;
792
896
  Object.defineProperty(wrapped, "url", {
793
897
  get: () => fn.url,
@@ -805,10 +909,10 @@ function getServerFunctionInvocation() {
805
909
  exports.ChunkReader = ChunkReader;
806
910
  exports.ERROR_HEADER = ERROR_HEADER;
807
911
  exports.FLASH_COOKIE = FLASH_COOKIE;
808
- exports.FUNCTION_HEADER = FUNCTION_HEADER;
809
912
  exports.GET = GET;
810
913
  exports.INSTANCE_HEADER = INSTANCE_HEADER;
811
914
  exports.REVALIDATE_HEADER = REVALIDATE_HEADER;
915
+ exports.SERVER_FUNCTION_INVOKE = SERVER_FUNCTION_INVOKE;
812
916
  exports.SINGLE_FLIGHT_HEADER = SINGLE_FLIGHT_HEADER;
813
917
  exports.clearFlashCookie = clearFlashCookie;
814
918
  exports.configureServerFunctionsClient = configureServerFunctionsClient;
@@ -825,10 +929,13 @@ exports.getServerFunctionInvocation = getServerFunctionInvocation;
825
929
  exports.getServerFunctionMetadata = getServerFunctionMetadata;
826
930
  exports.getServerFunctionsCodec = getServerFunctionsCodec;
827
931
  exports.hasFlashCookie = hasFlashCookie;
932
+ exports.invoke = invoke;
828
933
  exports.isServerFunction = isServerFunction;
829
934
  exports.live = live;
830
935
  exports.observeServerFunctionCalls = observeServerFunctionCalls;
936
+ exports.parseServerFunctionUrl = parseServerFunctionUrl;
831
937
  exports.registerServerReference = registerServerReference;
832
938
  exports.serializeString = serializeString;
939
+ exports.serverFunctionUrl = serverFunctionUrl;
833
940
  exports.subscribeFlightData = subscribeFlightData;
834
941
  exports.withMeta = withMeta;
@@ -16,6 +16,32 @@ function withMeta(fn, meta) {
16
16
  Object.assign(metadata, meta);
17
17
  return fn;
18
18
  }
19
+ const SERVER_FUNCTION_INVOKE = Symbol.for("solid.ServerFunctionInvoke");
20
+ const INVOKE_OPTION_REDIRECTS = {
21
+ 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.",
22
+ method: "The method is declaration-scoped: declare the function with GET(fn).",
23
+ body: "The arguments are the body: pass them in the args array.",
24
+ timeout: "Compose timeouts through `signal` with AbortSignal.timeout(ms) (and AbortSignal.any to " + "combine it with a caller signal)."
25
+ };
26
+ function invoke(fn, options, ...args) {
27
+ const channel = typeof fn === "function" && fn[SERVER_FUNCTION_INVOKE];
28
+ if (!channel) {
29
+ 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.");
30
+ }
31
+ if (options === null || typeof options !== "object") {
32
+ throw new Error("invoke's second argument is the invocation options bag: invoke(fn, { signal }, ...args)");
33
+ }
34
+ const picked = {};
35
+ for (const key in options) {
36
+ if (key !== "signal" && key !== "keepalive" && key !== "priority") {
37
+ 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."));
38
+ }
39
+ }
40
+ if (options.signal !== undefined) picked.signal = options.signal;
41
+ if (options.keepalive !== undefined) picked.keepalive = options.keepalive;
42
+ if (options.priority !== undefined) picked.priority = options.priority;
43
+ return channel(args, picked);
44
+ }
19
45
  const LIVE_SOURCE = Symbol.for("solid.LiveSource");
20
46
  const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
21
47
  function provideServerFunctionRPC(rpc) {
@@ -96,7 +122,23 @@ function stableString(value, seen) {
96
122
  }
97
123
  return out + "}";
98
124
  }
99
- const FUNCTION_HEADER = "X-Server-Function-Id";
125
+ function serverFunctionAddress(endpoint, id) {
126
+ const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
127
+ return `${mount}/${encodeURIComponent(id)}`;
128
+ }
129
+ function parseServerFunctionAddress(pathname, endpoint) {
130
+ const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
131
+ if (!pathname.startsWith(mount)) return null;
132
+ const rest = pathname.slice(mount.length);
133
+ if (!rest.startsWith("/")) return null;
134
+ const segment = rest.slice(1);
135
+ if (!segment || segment.includes("/")) return null;
136
+ try {
137
+ return decodeURIComponent(segment);
138
+ } catch {
139
+ return null;
140
+ }
141
+ }
100
142
  const ERROR_HEADER = "X-Server-Function-Error";
101
143
  const ERROR_HEADER_MARKER = "=?1?";
102
144
  const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
@@ -135,7 +177,8 @@ const BodyFormat = {
135
177
  File: "5",
136
178
  ArrayBuffer: "6",
137
179
  Uint8Array: "7",
138
- Json: "8"
180
+ Json: "8",
181
+ Void: "9"
139
182
  };
140
183
  const JSON_SAFE_DEPTH_LIMIT = 4096;
141
184
  const EXIT = {};
@@ -390,6 +433,7 @@ async function decodeResponsePayload(response, codecOptions) {
390
433
 
391
434
  const config = {
392
435
  endpoint: "/_server",
436
+ fetch: undefined,
393
437
  prepareRequest: undefined,
394
438
  responseHandler: undefined,
395
439
  serializeArgs: undefined
@@ -418,6 +462,17 @@ function observeServerFunctionCalls(observer) {
418
462
  CALL_OBSERVERS.add(observer);
419
463
  return () => CALL_OBSERVERS.delete(observer);
420
464
  }
465
+ function serverFunctionUrl(id, boundArgs) {
466
+ const address = serverFunctionAddress(config.endpoint, id);
467
+ if (!boundArgs || !boundArgs.length) return address;
468
+ if (!isJSONSafe(boundArgs)) {
469
+ 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.");
470
+ }
471
+ return `${address}?args=${encodeURIComponent(JSON.stringify(boundArgs))}`;
472
+ }
473
+ function parseServerFunctionUrl(url) {
474
+ return parseServerFunctionAddress(new URL(url, globalThis.location?.href || "http://localhost").pathname, config.endpoint);
475
+ }
421
476
  function serializeArguments(args) {
422
477
  if (!config.serializeArgs) {
423
478
  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.");
@@ -427,17 +482,20 @@ function serializeArguments(args) {
427
482
  function configureServerFunctionsClient({
428
483
  endpoint,
429
484
  codec,
485
+ fetch,
430
486
  prepareRequest,
431
487
  responseHandler,
432
488
  serializeArgs
433
489
  } = {}) {
434
490
  if (endpoint !== undefined) config.endpoint = endpoint;
435
491
  if (codec !== undefined) configureServerFunctionsCodec(codec);
492
+ if (fetch !== undefined) config.fetch = fetch;
436
493
  if (prepareRequest !== undefined) config.prepareRequest = prepareRequest;
437
494
  if (responseHandler !== undefined) config.responseHandler = responseHandler;
438
495
  if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
439
496
  }
440
497
  let INSTANCE = 0;
498
+ const MAX_GET_URL_LENGTH = 2000;
441
499
  let rpcProvided = false;
442
500
  function provideRPC() {
443
501
  if (rpcProvided) return;
@@ -455,7 +513,6 @@ function serverFunctionFailure(response, value) {
455
513
  async function createRequest(base, id, instance, options, meta) {
456
514
  const headers = {
457
515
  ...options.headers,
458
- [FUNCTION_HEADER]: id,
459
516
  [INSTANCE_HEADER]: instance
460
517
  };
461
518
  if (getFlightDataConsumer() && !options.read && (!options.method || options.method.toUpperCase() !== "GET")) {
@@ -472,10 +529,14 @@ async function createRequest(base, id, instance, options, meta) {
472
529
  meta
473
530
  })) || init;
474
531
  }
475
- if (CALL_OBSERVERS.size === 0) return fetch(base, init);
476
- const request = new Request(new URL(base, globalThis.location?.href || "http://localhost"), init);
532
+ const send = config.fetch || fetch;
533
+ if (CALL_OBSERVERS.size === 0) return send(base, init);
534
+ const request = new Request(new URL(base, globalThis.location?.href || "http://localhost"), {
535
+ ...init,
536
+ body: init.body instanceof ReadableStream ? undefined : init.body
537
+ });
477
538
  notifyCallObservers("request", id, instance, request, meta);
478
- const response = await fetch(request);
539
+ const response = await send(base, init);
479
540
  notifyCallObservers("response", id, instance, response, meta);
480
541
  return response;
481
542
  }
@@ -560,6 +621,9 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
560
621
  });
561
622
  if (handled !== undefined) return handled;
562
623
  }
624
+ if (response.status >= 400 && !response.headers.has(BODY_FORMAT_HEADER)) {
625
+ throw serverFunctionFailure(response, undefined);
626
+ }
563
627
  const failed = response.headers.has(ERROR_HEADER) || response.status >= 500;
564
628
  if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
565
629
  const consumer = getFlightDataConsumer();
@@ -602,7 +666,7 @@ function createServerReference(id, name, base) {
602
666
  const metadata = name === undefined ? {} : {
603
667
  name
604
668
  };
605
- const fn = (...args) => {
669
+ const run = (args, invokeOptions) => {
606
670
  const handler = config.responseHandler;
607
671
  if (handler && handler.intercept) {
608
672
  const hit = handler.intercept({
@@ -612,14 +676,18 @@ function createServerReference(id, name, base) {
612
676
  });
613
677
  if (hit !== undefined) return hit;
614
678
  }
615
- return fetchServerFunction(base || config.endpoint, id, {}, args, metadata);
679
+ return fetchServerFunction(base || serverFunctionAddress(config.endpoint, id), id, invokeOptions ? {
680
+ ...invokeOptions
681
+ } : {}, args, metadata);
616
682
  };
683
+ const fn = (...args) => run(args);
617
684
  fn[SERVER_FUNCTION_METADATA] = metadata;
685
+ fn[SERVER_FUNCTION_INVOKE] = run;
618
686
  return new Proxy(fn, {
619
687
  get(target, prop) {
620
688
  if (prop === "id") return id;
621
689
  if (prop === "url") {
622
- return base || `${config.endpoint}?id=${encodeURIComponent(id)}`;
690
+ return base || serverFunctionAddress(config.endpoint, id);
623
691
  }
624
692
  return target[prop];
625
693
  }
@@ -634,7 +702,7 @@ function GET(fn) {
634
702
  const metadata = {
635
703
  ...getServerFunctionMetadata(fn)
636
704
  };
637
- const wrapped = async (...args) => {
705
+ const run = async (args, invokeOptions) => {
638
706
  const handler = config.responseHandler;
639
707
  if (handler && handler.intercept) {
640
708
  const hit = handler.intercept({
@@ -644,19 +712,34 @@ function GET(fn) {
644
712
  });
645
713
  if (hit !== undefined) return hit;
646
714
  }
647
- let base = `${config.endpoint}?id=${encodeURIComponent(id)}`;
648
- if (args.length) {
649
- const encoded = isJSONSafe(args) ? JSON.stringify(args) : await serializeArguments(args);
650
- base += `&args=${encodeURIComponent(encoded)}`;
715
+ const opts = invokeOptions || {};
716
+ const address = serverFunctionAddress(config.endpoint, id);
717
+ if (!args.length) {
718
+ return fetchServerFunction(address, id, {
719
+ ...opts,
720
+ method: "GET"
721
+ }, [], metadata, args);
722
+ }
723
+ const encoded = isJSONSafe(args) ? JSON.stringify(args) : await serializeArguments(args);
724
+ const url = `${address}?args=${encodeURIComponent(encoded)}`;
725
+ const absolute = new URL(url, globalThis.location?.href || "http://localhost").href;
726
+ if (absolute.length > MAX_GET_URL_LENGTH) {
727
+ return fetchServerFunction(address, id, {
728
+ ...opts,
729
+ read: true
730
+ }, args, metadata);
651
731
  }
652
- return fetchServerFunction(base, id, {
732
+ return fetchServerFunction(url, id, {
733
+ ...opts,
653
734
  method: "GET"
654
735
  }, [], metadata, args);
655
736
  };
737
+ const wrapped = (...args) => run(args);
656
738
  wrapped[SERVER_FUNCTION_METADATA] = metadata;
739
+ wrapped[SERVER_FUNCTION_INVOKE] = run;
657
740
  wrapped.id = id;
658
741
  Object.defineProperty(wrapped, "url", {
659
- get: () => `${config.endpoint}?id=${encodeURIComponent(id)}`,
742
+ get: () => serverFunctionAddress(config.endpoint, id),
660
743
  configurable: true
661
744
  });
662
745
  return withMeta(wrapped, {
@@ -672,7 +755,7 @@ function live(fn) {
672
755
  ...getServerFunctionMetadata(fn),
673
756
  live: true
674
757
  };
675
- const wrapped = (...args) => {
758
+ const makeIterable = (args, invokeOptions) => {
676
759
  const iterable = {
677
760
  [LIVE_SOURCE]: true,
678
761
  [Symbol.asyncIterator]() {
@@ -686,6 +769,12 @@ function live(fn) {
686
769
  done: true,
687
770
  value: undefined
688
771
  };
772
+ const invokeSignal = invokeOptions && invokeOptions.signal;
773
+ const controller = new AbortController();
774
+ const wireOptions = {
775
+ ...invokeOptions,
776
+ signal: invokeSignal ? AbortSignal.any([invokeSignal, controller.signal]) : controller.signal
777
+ };
689
778
  const emit = (state, error) => {
690
779
  try {
691
780
  iterable.onstatus && iterable.onstatus(state, error);
@@ -707,7 +796,7 @@ function live(fn) {
707
796
  }
708
797
  };
709
798
  const callOnce = () => {
710
- if (metadata.method === "GET") return fn(...args);
799
+ if (metadata.method === "GET") return fn[SERVER_FUNCTION_INVOKE](args, wireOptions);
711
800
  const handler = config.responseHandler;
712
801
  if (handler && handler.intercept) {
713
802
  const hit = handler.intercept({
@@ -718,6 +807,7 @@ function live(fn) {
718
807
  if (hit !== undefined) return hit;
719
808
  }
720
809
  return fetchServerFunction(fn.url, id, {
810
+ ...wireOptions,
721
811
  read: true
722
812
  }, args, metadata, args);
723
813
  };
@@ -732,6 +822,7 @@ function live(fn) {
732
822
  }();
733
823
  if (stopped) {
734
824
  closeIt();
825
+ controller.abort();
735
826
  return DONE;
736
827
  }
737
828
  emit("connected");
@@ -745,7 +836,13 @@ function live(fn) {
745
836
  attempts = 0;
746
837
  return r;
747
838
  } catch (error) {
839
+ if (stopped) return DONE;
748
840
  if (!connected) throw error;
841
+ if (invokeSignal && invokeSignal.aborted) {
842
+ stopped = true;
843
+ emitClosed(error);
844
+ throw error;
845
+ }
749
846
  if (error !== null && typeof error === "object" && typeof error.status === "number" && error.status >= 400 && error.status < 500) {
750
847
  stopped = true;
751
848
  emitClosed(error);
@@ -759,9 +856,13 @@ function live(fn) {
759
856
  if (typeof addEventListener === "function") addEventListener("online", resolve, {
760
857
  once: true
761
858
  });
859
+ if (invokeSignal) invokeSignal.addEventListener("abort", resolve, {
860
+ once: true
861
+ });
762
862
  });
763
863
  clearTimeout(timer);
764
864
  if (typeof removeEventListener === "function") removeEventListener("online", wake);
865
+ if (invokeSignal) invokeSignal.removeEventListener("abort", wake);
765
866
  timer = wake = undefined;
766
867
  }
767
868
  }
@@ -774,6 +875,7 @@ function live(fn) {
774
875
  if (timer !== undefined) clearTimeout(timer);
775
876
  if (wake) wake();
776
877
  closeIt(value);
878
+ controller.abort();
777
879
  emitClosed();
778
880
  return Promise.resolve({
779
881
  done: true,
@@ -785,7 +887,9 @@ function live(fn) {
785
887
  };
786
888
  return iterable;
787
889
  };
890
+ const wrapped = (...args) => makeIterable(args);
788
891
  wrapped[SERVER_FUNCTION_METADATA] = metadata;
892
+ wrapped[SERVER_FUNCTION_INVOKE] = makeIterable;
789
893
  wrapped.id = id;
790
894
  Object.defineProperty(wrapped, "url", {
791
895
  get: () => fn.url,
@@ -800,4 +904,4 @@ function getServerFunctionInvocation() {
800
904
  return undefined;
801
905
  }
802
906
 
803
- export { ChunkReader, ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, REVALIDATE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsClient, createChunk, createServerReference, decodeErrorHeaderValue, decodeResponse, decodeResponsePayload, deserializeStream, encodeErrorHeaderValue, frameAddress, getFlightDataConsumer, getServerFunctionInvocation, getServerFunctionMetadata, getServerFunctionsCodec, hasFlashCookie, isServerFunction, live, observeServerFunctionCalls, registerServerReference, serializeString, subscribeFlightData, withMeta };
907
+ export { ChunkReader, ERROR_HEADER, FLASH_COOKIE, GET, INSTANCE_HEADER, REVALIDATE_HEADER, SERVER_FUNCTION_INVOKE, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsClient, createChunk, createServerReference, decodeErrorHeaderValue, decodeResponse, decodeResponsePayload, deserializeStream, encodeErrorHeaderValue, frameAddress, getFlightDataConsumer, getServerFunctionInvocation, getServerFunctionMetadata, getServerFunctionsCodec, hasFlashCookie, invoke, isServerFunction, live, observeServerFunctionCalls, parseServerFunctionUrl, registerServerReference, serializeString, serverFunctionUrl, subscribeFlightData, withMeta };