@solidjs/web 2.0.0-rc.0 → 2.0.0-rc.1

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.
@@ -28,6 +28,7 @@ function withMeta(fn, meta) {
28
28
  Object.assign(metadata, meta);
29
29
  return fn;
30
30
  }
31
+ const LIVE_SOURCE = Symbol.for("solid.LiveSource");
31
32
  const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
32
33
  function provideServerFunctionRPC(rpc) {
33
34
  globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
@@ -132,7 +133,7 @@ const BodyFormat = {
132
133
  Uint8Array: "7",
133
134
  Json: "8"
134
135
  };
135
- const JSON_SAFE_DEPTH_LIMIT = 10000;
136
+ const JSON_SAFE_DEPTH_LIMIT = 4096;
136
137
  const EXIT = {};
137
138
  function isJSONSafe(value) {
138
139
  const stack = [value];
@@ -159,6 +160,7 @@ function isJSONSafe(value) {
159
160
  } else {
160
161
  const proto = Object.getPrototypeOf(v);
161
162
  if (proto !== Object.prototype && proto !== null) return false;
163
+ if (Symbol.asyncIterator in v || Symbol.iterator in v) return false;
162
164
  for (const k in v) stack.push(v[k]);
163
165
  }
164
166
  }
@@ -321,27 +323,6 @@ class ChunkReader {
321
323
  }
322
324
  }
323
325
  }
324
- function serializeStream(value, codecOptions) {
325
- return new ReadableStream({
326
- async start(controller) {
327
- const {
328
- serializeJSON
329
- } = await import('@solidjs/web/serialization');
330
- serializeJSON(value, {
331
- ...codecOptions,
332
- onParse(node) {
333
- controller.enqueue(createChunk(JSON.stringify(node)));
334
- },
335
- onDone() {
336
- controller.close();
337
- },
338
- onError(error) {
339
- controller.error(error);
340
- }
341
- });
342
- }
343
- });
344
- }
345
326
  async function deserializeStream(source, codecOptions) {
346
327
  if (!source.body) {
347
328
  throw new Error("missing body");
@@ -356,7 +337,7 @@ async function deserializeStream(source, codecOptions) {
356
337
  function interpretChunk(chunk) {
357
338
  return deserializeChunk(JSON.parse(chunk));
358
339
  }
359
- void reader.drain(interpretChunk);
340
+ reader.drain(interpretChunk).then(() => deserializeChunk.abort(new Error("Server function stream ended unexpectedly.")), error => deserializeChunk.abort(error));
360
341
  return interpretChunk(result.value);
361
342
  }
362
343
  return undefined;
@@ -636,6 +617,29 @@ function GET(fn) {
636
617
  method: "GET"
637
618
  });
638
619
  }
620
+ function live(fn) {
621
+ if (!isServerFunction(fn) || typeof fn.id !== "string") {
622
+ throw new Error("live expects a server function reference");
623
+ }
624
+ const metadata = {
625
+ ...getServerFunctionMetadata(fn),
626
+ live: true
627
+ };
628
+ const wrapped = async (...args) => {
629
+ const result = await fn(...args);
630
+ if (result !== null && typeof result === "object" && result[Symbol.asyncIterator]) {
631
+ result[LIVE_SOURCE] = true;
632
+ }
633
+ return result;
634
+ };
635
+ wrapped[SERVER_FUNCTION_METADATA] = metadata;
636
+ wrapped.id = fn.id;
637
+ Object.defineProperty(wrapped, "url", {
638
+ get: () => fn.url,
639
+ configurable: true
640
+ });
641
+ return wrapped;
642
+ }
639
643
  function getServerFunctionInvocation() {
640
644
  return getEventServerFunctionInvocation(getRequestEvent());
641
645
  }
@@ -807,14 +811,102 @@ function isFormPost(request) {
807
811
  const type = request.headers.get("content-type") || "";
808
812
  return type.startsWith("application/x-www-form-urlencoded") || type.startsWith("multipart/form-data");
809
813
  }
810
- function serializedResponse(value, headers, codec) {
814
+ function serializeResponseStream(value, codecOptions, signal) {
815
+ let closeIterator = null;
816
+ let closed = false;
817
+ let cancelSerialize = null;
818
+ let onAbort = null;
819
+ const teardown = () => {
820
+ if (closed) return;
821
+ closed = true;
822
+ if (onAbort) signal.removeEventListener("abort", onAbort);
823
+ if (cancelSerialize) cancelSerialize();
824
+ if (closeIterator) closeIterator();
825
+ };
826
+ if (value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function") {
827
+ const source = value;
828
+ value = {
829
+ [Symbol.asyncIterator]() {
830
+ const it = source[Symbol.asyncIterator]();
831
+ let finished = false;
832
+ closeIterator = () => {
833
+ if (finished) return;
834
+ finished = true;
835
+ try {
836
+ const returned = it.return && it.return();
837
+ if (returned && typeof returned.then === "function") returned.then(undefined, () => {});
838
+ } catch {}
839
+ };
840
+ if (closed) closeIterator();
841
+ return {
842
+ next: () => finished ? Promise.resolve({
843
+ done: true,
844
+ value: undefined
845
+ }) : it.next()
846
+ };
847
+ }
848
+ };
849
+ }
850
+ return new ReadableStream({
851
+ async start(controller) {
852
+ if (signal) {
853
+ if (signal.aborted) {
854
+ teardown();
855
+ controller.close();
856
+ return;
857
+ }
858
+ onAbort = () => {
859
+ const alreadyClosed = closed;
860
+ teardown();
861
+ if (!alreadyClosed) {
862
+ try {
863
+ controller.error(signal.reason || new Error("The operation was aborted."));
864
+ } catch {}
865
+ }
866
+ };
867
+ signal.addEventListener("abort", onAbort);
868
+ }
869
+ const {
870
+ serializeJSON
871
+ } = await import('@solidjs/web/serialization');
872
+ if (closed) {
873
+ try {
874
+ controller.close();
875
+ } catch {}
876
+ return;
877
+ }
878
+ cancelSerialize = serializeJSON(value, {
879
+ ...codecOptions,
880
+ onParse(node) {
881
+ if (!closed) controller.enqueue(createChunk(JSON.stringify(node)));
882
+ },
883
+ onDone() {
884
+ if (closed) return;
885
+ closed = true;
886
+ if (onAbort) signal.removeEventListener("abort", onAbort);
887
+ controller.close();
888
+ },
889
+ onError(error) {
890
+ if (closed) return;
891
+ closed = true;
892
+ if (onAbort) signal.removeEventListener("abort", onAbort);
893
+ controller.error(error);
894
+ }
895
+ });
896
+ },
897
+ cancel() {
898
+ teardown();
899
+ }
900
+ });
901
+ }
902
+ function serializedResponse(value, headers, codec, signal) {
811
903
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
812
904
  headers.set("Content-Type", "text/plain");
813
- return new Response(serializeStream(value, codec), {
905
+ return new Response(serializeResponseStream(value, codec, signal), {
814
906
  headers
815
907
  });
816
908
  }
817
- function encodeResult(value, headers, status, codec) {
909
+ function encodeResult(value, headers, status, codec, signal) {
818
910
  const direct = getHeadersAndBody(value);
819
911
  if (direct) {
820
912
  for (const [key, val] of Object.entries(direct.headers || {})) {
@@ -842,7 +934,7 @@ function encodeResult(value, headers, status, codec) {
842
934
  }
843
935
  } catch {
844
936
  }
845
- const response = serializedResponse(value, headers, codec);
937
+ const response = serializedResponse(value, headers, codec, signal);
846
938
  return status === 200 ? response : new Response(response.body, {
847
939
  status,
848
940
  headers
@@ -970,9 +1062,9 @@ async function handleServerFunctionRequest(request, options = {}) {
970
1062
  if (!instance) {
971
1063
  if (handleNoJS) return handleNoJS(result, request, parsed);
972
1064
  if (result instanceof Response) return result;
973
- return encodeResult(result, headers, 200, codec);
1065
+ return encodeResult(result, headers, 200, codec, request.signal);
974
1066
  }
975
- return encodeResult(result, headers, status, codec);
1067
+ return encodeResult(result, headers, status, codec, request.signal);
976
1068
  } catch (x) {
977
1069
  if (x instanceof Response || isResponseEnvelope(x)) {
978
1070
  if (transformResult) {
@@ -1026,7 +1118,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1026
1118
  if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
1027
1119
  if (x instanceof Response) return x;
1028
1120
  }
1029
- return encodeResult(x, headers, status, codec);
1121
+ return encodeResult(x, headers, status, codec, request.signal);
1030
1122
  }
1031
1123
  const safe = sanitizeServerError(x);
1032
1124
  if (!instance) {
@@ -1038,7 +1130,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1038
1130
  }
1039
1131
  const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
1040
1132
  headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
1041
- return encodeResult(safe, headers, 200, codec);
1133
+ return encodeResult(safe, headers, 200, codec, request.signal);
1042
1134
  }
1043
1135
  };
1044
1136
  return commitEventResponse(await dispatch(), event);
@@ -1069,9 +1161,11 @@ exports.getServerFunctionMetadata = getServerFunctionMetadata;
1069
1161
  exports.handleServerFunctionRequest = handleServerFunctionRequest;
1070
1162
  exports.hasFlashCookie = hasFlashCookie;
1071
1163
  exports.isServerFunction = isServerFunction;
1164
+ exports.live = live;
1072
1165
  exports.registerServerFunction = registerServerFunction;
1073
1166
  exports.registerServerReference = registerServerReference;
1074
1167
  exports.sanitizeServerError = sanitizeServerError;
1168
+ exports.serializeResponseStream = serializeResponseStream;
1075
1169
  exports.setServerFunctionsDev = setServerFunctionsDev;
1076
1170
  exports.subscribeFlightData = subscribeFlightData;
1077
1171
  exports.withMeta = withMeta;
@@ -28,6 +28,7 @@ function withMeta(fn, meta) {
28
28
  Object.assign(metadata, meta);
29
29
  return fn;
30
30
  }
31
+ const LIVE_SOURCE = Symbol.for("solid.LiveSource");
31
32
  const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
32
33
  function provideServerFunctionRPC(rpc) {
33
34
  globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
@@ -132,7 +133,7 @@ const BodyFormat = {
132
133
  Uint8Array: "7",
133
134
  Json: "8"
134
135
  };
135
- const JSON_SAFE_DEPTH_LIMIT = 10000;
136
+ const JSON_SAFE_DEPTH_LIMIT = 4096;
136
137
  const EXIT = {};
137
138
  function isJSONSafe(value) {
138
139
  const stack = [value];
@@ -159,6 +160,7 @@ function isJSONSafe(value) {
159
160
  } else {
160
161
  const proto = Object.getPrototypeOf(v);
161
162
  if (proto !== Object.prototype && proto !== null) return false;
163
+ if (Symbol.asyncIterator in v || Symbol.iterator in v) return false;
162
164
  for (const k in v) stack.push(v[k]);
163
165
  }
164
166
  }
@@ -321,27 +323,6 @@ class ChunkReader {
321
323
  }
322
324
  }
323
325
  }
324
- function serializeStream(value, codecOptions) {
325
- return new ReadableStream({
326
- async start(controller) {
327
- const {
328
- serializeJSON
329
- } = await import('@solidjs/web/serialization');
330
- serializeJSON(value, {
331
- ...codecOptions,
332
- onParse(node) {
333
- controller.enqueue(createChunk(JSON.stringify(node)));
334
- },
335
- onDone() {
336
- controller.close();
337
- },
338
- onError(error) {
339
- controller.error(error);
340
- }
341
- });
342
- }
343
- });
344
- }
345
326
  async function deserializeStream(source, codecOptions) {
346
327
  if (!source.body) {
347
328
  throw new Error("missing body");
@@ -356,7 +337,7 @@ async function deserializeStream(source, codecOptions) {
356
337
  function interpretChunk(chunk) {
357
338
  return deserializeChunk(JSON.parse(chunk));
358
339
  }
359
- void reader.drain(interpretChunk);
340
+ reader.drain(interpretChunk).then(() => deserializeChunk.abort(new Error("Server function stream ended unexpectedly.")), error => deserializeChunk.abort(error));
360
341
  return interpretChunk(result.value);
361
342
  }
362
343
  return undefined;
@@ -636,6 +617,29 @@ function GET(fn) {
636
617
  method: "GET"
637
618
  });
638
619
  }
620
+ function live(fn) {
621
+ if (!isServerFunction(fn) || typeof fn.id !== "string") {
622
+ throw new Error("live expects a server function reference");
623
+ }
624
+ const metadata = {
625
+ ...getServerFunctionMetadata(fn),
626
+ live: true
627
+ };
628
+ const wrapped = async (...args) => {
629
+ const result = await fn(...args);
630
+ if (result !== null && typeof result === "object" && result[Symbol.asyncIterator]) {
631
+ result[LIVE_SOURCE] = true;
632
+ }
633
+ return result;
634
+ };
635
+ wrapped[SERVER_FUNCTION_METADATA] = metadata;
636
+ wrapped.id = fn.id;
637
+ Object.defineProperty(wrapped, "url", {
638
+ get: () => fn.url,
639
+ configurable: true
640
+ });
641
+ return wrapped;
642
+ }
639
643
  function getServerFunctionInvocation() {
640
644
  return getEventServerFunctionInvocation(getRequestEvent());
641
645
  }
@@ -807,14 +811,102 @@ function isFormPost(request) {
807
811
  const type = request.headers.get("content-type") || "";
808
812
  return type.startsWith("application/x-www-form-urlencoded") || type.startsWith("multipart/form-data");
809
813
  }
810
- function serializedResponse(value, headers, codec) {
814
+ function serializeResponseStream(value, codecOptions, signal) {
815
+ let closeIterator = null;
816
+ let closed = false;
817
+ let cancelSerialize = null;
818
+ let onAbort = null;
819
+ const teardown = () => {
820
+ if (closed) return;
821
+ closed = true;
822
+ if (onAbort) signal.removeEventListener("abort", onAbort);
823
+ if (cancelSerialize) cancelSerialize();
824
+ if (closeIterator) closeIterator();
825
+ };
826
+ if (value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function") {
827
+ const source = value;
828
+ value = {
829
+ [Symbol.asyncIterator]() {
830
+ const it = source[Symbol.asyncIterator]();
831
+ let finished = false;
832
+ closeIterator = () => {
833
+ if (finished) return;
834
+ finished = true;
835
+ try {
836
+ const returned = it.return && it.return();
837
+ if (returned && typeof returned.then === "function") returned.then(undefined, () => {});
838
+ } catch {}
839
+ };
840
+ if (closed) closeIterator();
841
+ return {
842
+ next: () => finished ? Promise.resolve({
843
+ done: true,
844
+ value: undefined
845
+ }) : it.next()
846
+ };
847
+ }
848
+ };
849
+ }
850
+ return new ReadableStream({
851
+ async start(controller) {
852
+ if (signal) {
853
+ if (signal.aborted) {
854
+ teardown();
855
+ controller.close();
856
+ return;
857
+ }
858
+ onAbort = () => {
859
+ const alreadyClosed = closed;
860
+ teardown();
861
+ if (!alreadyClosed) {
862
+ try {
863
+ controller.error(signal.reason || new Error("The operation was aborted."));
864
+ } catch {}
865
+ }
866
+ };
867
+ signal.addEventListener("abort", onAbort);
868
+ }
869
+ const {
870
+ serializeJSON
871
+ } = await import('@solidjs/web/serialization');
872
+ if (closed) {
873
+ try {
874
+ controller.close();
875
+ } catch {}
876
+ return;
877
+ }
878
+ cancelSerialize = serializeJSON(value, {
879
+ ...codecOptions,
880
+ onParse(node) {
881
+ if (!closed) controller.enqueue(createChunk(JSON.stringify(node)));
882
+ },
883
+ onDone() {
884
+ if (closed) return;
885
+ closed = true;
886
+ if (onAbort) signal.removeEventListener("abort", onAbort);
887
+ controller.close();
888
+ },
889
+ onError(error) {
890
+ if (closed) return;
891
+ closed = true;
892
+ if (onAbort) signal.removeEventListener("abort", onAbort);
893
+ controller.error(error);
894
+ }
895
+ });
896
+ },
897
+ cancel() {
898
+ teardown();
899
+ }
900
+ });
901
+ }
902
+ function serializedResponse(value, headers, codec, signal) {
811
903
  headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
812
904
  headers.set("Content-Type", "text/plain");
813
- return new Response(serializeStream(value, codec), {
905
+ return new Response(serializeResponseStream(value, codec, signal), {
814
906
  headers
815
907
  });
816
908
  }
817
- function encodeResult(value, headers, status, codec) {
909
+ function encodeResult(value, headers, status, codec, signal) {
818
910
  const direct = getHeadersAndBody(value);
819
911
  if (direct) {
820
912
  for (const [key, val] of Object.entries(direct.headers || {})) {
@@ -842,7 +934,7 @@ function encodeResult(value, headers, status, codec) {
842
934
  }
843
935
  } catch {
844
936
  }
845
- const response = serializedResponse(value, headers, codec);
937
+ const response = serializedResponse(value, headers, codec, signal);
846
938
  return status === 200 ? response : new Response(response.body, {
847
939
  status,
848
940
  headers
@@ -970,9 +1062,9 @@ async function handleServerFunctionRequest(request, options = {}) {
970
1062
  if (!instance) {
971
1063
  if (handleNoJS) return handleNoJS(result, request, parsed);
972
1064
  if (result instanceof Response) return result;
973
- return encodeResult(result, headers, 200, codec);
1065
+ return encodeResult(result, headers, 200, codec, request.signal);
974
1066
  }
975
- return encodeResult(result, headers, status, codec);
1067
+ return encodeResult(result, headers, status, codec, request.signal);
976
1068
  } catch (x) {
977
1069
  if (x instanceof Response || isResponseEnvelope(x)) {
978
1070
  if (transformResult) {
@@ -1026,7 +1118,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1026
1118
  if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
1027
1119
  if (x instanceof Response) return x;
1028
1120
  }
1029
- return encodeResult(x, headers, status, codec);
1121
+ return encodeResult(x, headers, status, codec, request.signal);
1030
1122
  }
1031
1123
  const safe = sanitizeServerError(x);
1032
1124
  if (!instance) {
@@ -1038,7 +1130,7 @@ async function handleServerFunctionRequest(request, options = {}) {
1038
1130
  }
1039
1131
  const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
1040
1132
  headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
1041
- return encodeResult(safe, headers, 200, codec);
1133
+ return encodeResult(safe, headers, 200, codec, request.signal);
1042
1134
  }
1043
1135
  };
1044
1136
  return commitEventResponse(await dispatch(), event);
@@ -1069,9 +1161,11 @@ exports.getServerFunctionMetadata = getServerFunctionMetadata;
1069
1161
  exports.handleServerFunctionRequest = handleServerFunctionRequest;
1070
1162
  exports.hasFlashCookie = hasFlashCookie;
1071
1163
  exports.isServerFunction = isServerFunction;
1164
+ exports.live = live;
1072
1165
  exports.registerServerFunction = registerServerFunction;
1073
1166
  exports.registerServerReference = registerServerReference;
1074
1167
  exports.sanitizeServerError = sanitizeServerError;
1168
+ exports.serializeResponseStream = serializeResponseStream;
1075
1169
  exports.setServerFunctionsDev = setServerFunctionsDev;
1076
1170
  exports.subscribeFlightData = subscribeFlightData;
1077
1171
  exports.withMeta = withMeta;