@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.
- package/README.md +1 -1
- package/dist/dev.cjs +9 -5
- package/dist/dev.js +9 -5
- package/dist/server.cjs +94 -4
- package/dist/server.js +94 -5
- package/dist/web.cjs +6 -5
- package/dist/web.js +6 -5
- package/frames/dist/client.cjs +1 -1
- package/frames/dist/client.dev.cjs +1 -1
- package/frames/dist/client.dev.js +1 -1
- package/frames/dist/client.js +1 -1
- package/frames/dist/server.cjs +92 -3
- package/frames/dist/server.js +92 -3
- package/package.json +4 -3
- package/serialization/dist/decode.cjs +13 -1
- package/serialization/dist/decode.js +13 -1
- package/serialization/dist/serialization.cjs +13 -1
- package/serialization/dist/serialization.js +13 -1
- package/server-functions/dist/client.cjs +150 -3
- package/server-functions/dist/client.js +150 -4
- package/server-functions/dist/server.cjs +125 -31
- package/server-functions/dist/server.dev.cjs +125 -31
- package/server-functions/dist/server.dev.js +124 -32
- package/server-functions/dist/server.js +124 -32
- package/types/index.d.ts +16 -2
- package/types/jsx.d.ts +9 -0
- package/types/server-functions/client.d.ts +30 -0
- package/types/server-functions/server.d.ts +28 -0
- package/types-cjs/index.d.cts +16 -2
- package/types-cjs/jsx.d.cts +9 -0
- package/types-cjs/server-functions/client.d.cts +30 -0
- package/types-cjs/server-functions/server.d.cts +28 -0
|
@@ -26,6 +26,7 @@ function withMeta(fn, meta) {
|
|
|
26
26
|
Object.assign(metadata, meta);
|
|
27
27
|
return fn;
|
|
28
28
|
}
|
|
29
|
+
const LIVE_SOURCE = Symbol.for("solid.LiveSource");
|
|
29
30
|
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
|
|
30
31
|
function provideServerFunctionRPC(rpc) {
|
|
31
32
|
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
|
|
@@ -130,7 +131,7 @@ const BodyFormat = {
|
|
|
130
131
|
Uint8Array: "7",
|
|
131
132
|
Json: "8"
|
|
132
133
|
};
|
|
133
|
-
const JSON_SAFE_DEPTH_LIMIT =
|
|
134
|
+
const JSON_SAFE_DEPTH_LIMIT = 4096;
|
|
134
135
|
const EXIT = {};
|
|
135
136
|
function isJSONSafe(value) {
|
|
136
137
|
const stack = [value];
|
|
@@ -157,6 +158,7 @@ function isJSONSafe(value) {
|
|
|
157
158
|
} else {
|
|
158
159
|
const proto = Object.getPrototypeOf(v);
|
|
159
160
|
if (proto !== Object.prototype && proto !== null) return false;
|
|
161
|
+
if (Symbol.asyncIterator in v || Symbol.iterator in v) return false;
|
|
160
162
|
for (const k in v) stack.push(v[k]);
|
|
161
163
|
}
|
|
162
164
|
}
|
|
@@ -319,27 +321,6 @@ class ChunkReader {
|
|
|
319
321
|
}
|
|
320
322
|
}
|
|
321
323
|
}
|
|
322
|
-
function serializeStream(value, codecOptions) {
|
|
323
|
-
return new ReadableStream({
|
|
324
|
-
async start(controller) {
|
|
325
|
-
const {
|
|
326
|
-
serializeJSON
|
|
327
|
-
} = await import('@solidjs/web/serialization');
|
|
328
|
-
serializeJSON(value, {
|
|
329
|
-
...codecOptions,
|
|
330
|
-
onParse(node) {
|
|
331
|
-
controller.enqueue(createChunk(JSON.stringify(node)));
|
|
332
|
-
},
|
|
333
|
-
onDone() {
|
|
334
|
-
controller.close();
|
|
335
|
-
},
|
|
336
|
-
onError(error) {
|
|
337
|
-
controller.error(error);
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
324
|
async function deserializeStream(source, codecOptions) {
|
|
344
325
|
if (!source.body) {
|
|
345
326
|
throw new Error("missing body");
|
|
@@ -354,7 +335,7 @@ async function deserializeStream(source, codecOptions) {
|
|
|
354
335
|
function interpretChunk(chunk) {
|
|
355
336
|
return deserializeChunk(JSON.parse(chunk));
|
|
356
337
|
}
|
|
357
|
-
|
|
338
|
+
reader.drain(interpretChunk).then(() => deserializeChunk.abort(new Error("Server function stream ended unexpectedly.")), error => deserializeChunk.abort(error));
|
|
358
339
|
return interpretChunk(result.value);
|
|
359
340
|
}
|
|
360
341
|
return undefined;
|
|
@@ -634,6 +615,29 @@ function GET(fn) {
|
|
|
634
615
|
method: "GET"
|
|
635
616
|
});
|
|
636
617
|
}
|
|
618
|
+
function live(fn) {
|
|
619
|
+
if (!isServerFunction(fn) || typeof fn.id !== "string") {
|
|
620
|
+
throw new Error("live expects a server function reference");
|
|
621
|
+
}
|
|
622
|
+
const metadata = {
|
|
623
|
+
...getServerFunctionMetadata(fn),
|
|
624
|
+
live: true
|
|
625
|
+
};
|
|
626
|
+
const wrapped = async (...args) => {
|
|
627
|
+
const result = await fn(...args);
|
|
628
|
+
if (result !== null && typeof result === "object" && result[Symbol.asyncIterator]) {
|
|
629
|
+
result[LIVE_SOURCE] = true;
|
|
630
|
+
}
|
|
631
|
+
return result;
|
|
632
|
+
};
|
|
633
|
+
wrapped[SERVER_FUNCTION_METADATA] = metadata;
|
|
634
|
+
wrapped.id = fn.id;
|
|
635
|
+
Object.defineProperty(wrapped, "url", {
|
|
636
|
+
get: () => fn.url,
|
|
637
|
+
configurable: true
|
|
638
|
+
});
|
|
639
|
+
return wrapped;
|
|
640
|
+
}
|
|
637
641
|
function getServerFunctionInvocation() {
|
|
638
642
|
return getEventServerFunctionInvocation(getRequestEvent());
|
|
639
643
|
}
|
|
@@ -805,14 +809,102 @@ function isFormPost(request) {
|
|
|
805
809
|
const type = request.headers.get("content-type") || "";
|
|
806
810
|
return type.startsWith("application/x-www-form-urlencoded") || type.startsWith("multipart/form-data");
|
|
807
811
|
}
|
|
808
|
-
function
|
|
812
|
+
function serializeResponseStream(value, codecOptions, signal) {
|
|
813
|
+
let closeIterator = null;
|
|
814
|
+
let closed = false;
|
|
815
|
+
let cancelSerialize = null;
|
|
816
|
+
let onAbort = null;
|
|
817
|
+
const teardown = () => {
|
|
818
|
+
if (closed) return;
|
|
819
|
+
closed = true;
|
|
820
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
821
|
+
if (cancelSerialize) cancelSerialize();
|
|
822
|
+
if (closeIterator) closeIterator();
|
|
823
|
+
};
|
|
824
|
+
if (value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function") {
|
|
825
|
+
const source = value;
|
|
826
|
+
value = {
|
|
827
|
+
[Symbol.asyncIterator]() {
|
|
828
|
+
const it = source[Symbol.asyncIterator]();
|
|
829
|
+
let finished = false;
|
|
830
|
+
closeIterator = () => {
|
|
831
|
+
if (finished) return;
|
|
832
|
+
finished = true;
|
|
833
|
+
try {
|
|
834
|
+
const returned = it.return && it.return();
|
|
835
|
+
if (returned && typeof returned.then === "function") returned.then(undefined, () => {});
|
|
836
|
+
} catch {}
|
|
837
|
+
};
|
|
838
|
+
if (closed) closeIterator();
|
|
839
|
+
return {
|
|
840
|
+
next: () => finished ? Promise.resolve({
|
|
841
|
+
done: true,
|
|
842
|
+
value: undefined
|
|
843
|
+
}) : it.next()
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
return new ReadableStream({
|
|
849
|
+
async start(controller) {
|
|
850
|
+
if (signal) {
|
|
851
|
+
if (signal.aborted) {
|
|
852
|
+
teardown();
|
|
853
|
+
controller.close();
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
onAbort = () => {
|
|
857
|
+
const alreadyClosed = closed;
|
|
858
|
+
teardown();
|
|
859
|
+
if (!alreadyClosed) {
|
|
860
|
+
try {
|
|
861
|
+
controller.error(signal.reason || new Error("The operation was aborted."));
|
|
862
|
+
} catch {}
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
signal.addEventListener("abort", onAbort);
|
|
866
|
+
}
|
|
867
|
+
const {
|
|
868
|
+
serializeJSON
|
|
869
|
+
} = await import('@solidjs/web/serialization');
|
|
870
|
+
if (closed) {
|
|
871
|
+
try {
|
|
872
|
+
controller.close();
|
|
873
|
+
} catch {}
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
cancelSerialize = serializeJSON(value, {
|
|
877
|
+
...codecOptions,
|
|
878
|
+
onParse(node) {
|
|
879
|
+
if (!closed) controller.enqueue(createChunk(JSON.stringify(node)));
|
|
880
|
+
},
|
|
881
|
+
onDone() {
|
|
882
|
+
if (closed) return;
|
|
883
|
+
closed = true;
|
|
884
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
885
|
+
controller.close();
|
|
886
|
+
},
|
|
887
|
+
onError(error) {
|
|
888
|
+
if (closed) return;
|
|
889
|
+
closed = true;
|
|
890
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
891
|
+
controller.error(error);
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
},
|
|
895
|
+
cancel() {
|
|
896
|
+
teardown();
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
function serializedResponse(value, headers, codec, signal) {
|
|
809
901
|
headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
|
|
810
902
|
headers.set("Content-Type", "text/plain");
|
|
811
|
-
return new Response(
|
|
903
|
+
return new Response(serializeResponseStream(value, codec, signal), {
|
|
812
904
|
headers
|
|
813
905
|
});
|
|
814
906
|
}
|
|
815
|
-
function encodeResult(value, headers, status, codec) {
|
|
907
|
+
function encodeResult(value, headers, status, codec, signal) {
|
|
816
908
|
const direct = getHeadersAndBody(value);
|
|
817
909
|
if (direct) {
|
|
818
910
|
for (const [key, val] of Object.entries(direct.headers || {})) {
|
|
@@ -840,7 +932,7 @@ function encodeResult(value, headers, status, codec) {
|
|
|
840
932
|
}
|
|
841
933
|
} catch {
|
|
842
934
|
}
|
|
843
|
-
const response = serializedResponse(value, headers, codec);
|
|
935
|
+
const response = serializedResponse(value, headers, codec, signal);
|
|
844
936
|
return status === 200 ? response : new Response(response.body, {
|
|
845
937
|
status,
|
|
846
938
|
headers
|
|
@@ -968,9 +1060,9 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
968
1060
|
if (!instance) {
|
|
969
1061
|
if (handleNoJS) return handleNoJS(result, request, parsed);
|
|
970
1062
|
if (result instanceof Response) return result;
|
|
971
|
-
return encodeResult(result, headers, 200, codec);
|
|
1063
|
+
return encodeResult(result, headers, 200, codec, request.signal);
|
|
972
1064
|
}
|
|
973
|
-
return encodeResult(result, headers, status, codec);
|
|
1065
|
+
return encodeResult(result, headers, status, codec, request.signal);
|
|
974
1066
|
} catch (x) {
|
|
975
1067
|
if (x instanceof Response || isResponseEnvelope(x)) {
|
|
976
1068
|
if (transformResult) {
|
|
@@ -1024,7 +1116,7 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
1024
1116
|
if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
|
|
1025
1117
|
if (x instanceof Response) return x;
|
|
1026
1118
|
}
|
|
1027
|
-
return encodeResult(x, headers, status, codec);
|
|
1119
|
+
return encodeResult(x, headers, status, codec, request.signal);
|
|
1028
1120
|
}
|
|
1029
1121
|
const safe = sanitizeServerError(x);
|
|
1030
1122
|
if (!instance) {
|
|
@@ -1036,10 +1128,10 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
1036
1128
|
}
|
|
1037
1129
|
const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
|
|
1038
1130
|
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
|
|
1039
|
-
return encodeResult(safe, headers, 200, codec);
|
|
1131
|
+
return encodeResult(safe, headers, 200, codec, request.signal);
|
|
1040
1132
|
}
|
|
1041
1133
|
};
|
|
1042
1134
|
return commitEventResponse(await dispatch(), event);
|
|
1043
1135
|
}
|
|
1044
1136
|
|
|
1045
|
-
export { ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GENERIC_SERVER_ERROR_MESSAGE, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, decodeResponsePayload, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getEventServerFunctionInvocation, getServerFunction, getServerFunctionInvocation, getServerFunctionMetadata, handleServerFunctionRequest, hasFlashCookie, isServerFunction, registerServerFunction, registerServerReference, sanitizeServerError, setServerFunctionsDev, subscribeFlightData, withMeta };
|
|
1137
|
+
export { ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GENERIC_SERVER_ERROR_MESSAGE, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, decodeResponsePayload, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getEventServerFunctionInvocation, getServerFunction, getServerFunctionInvocation, getServerFunctionMetadata, handleServerFunctionRequest, hasFlashCookie, isServerFunction, live, registerServerFunction, registerServerReference, sanitizeServerError, serializeResponseStream, setServerFunctionsDev, subscribeFlightData, withMeta };
|
|
@@ -26,6 +26,7 @@ function withMeta(fn, meta) {
|
|
|
26
26
|
Object.assign(metadata, meta);
|
|
27
27
|
return fn;
|
|
28
28
|
}
|
|
29
|
+
const LIVE_SOURCE = Symbol.for("solid.LiveSource");
|
|
29
30
|
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
|
|
30
31
|
function provideServerFunctionRPC(rpc) {
|
|
31
32
|
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
|
|
@@ -130,7 +131,7 @@ const BodyFormat = {
|
|
|
130
131
|
Uint8Array: "7",
|
|
131
132
|
Json: "8"
|
|
132
133
|
};
|
|
133
|
-
const JSON_SAFE_DEPTH_LIMIT =
|
|
134
|
+
const JSON_SAFE_DEPTH_LIMIT = 4096;
|
|
134
135
|
const EXIT = {};
|
|
135
136
|
function isJSONSafe(value) {
|
|
136
137
|
const stack = [value];
|
|
@@ -157,6 +158,7 @@ function isJSONSafe(value) {
|
|
|
157
158
|
} else {
|
|
158
159
|
const proto = Object.getPrototypeOf(v);
|
|
159
160
|
if (proto !== Object.prototype && proto !== null) return false;
|
|
161
|
+
if (Symbol.asyncIterator in v || Symbol.iterator in v) return false;
|
|
160
162
|
for (const k in v) stack.push(v[k]);
|
|
161
163
|
}
|
|
162
164
|
}
|
|
@@ -319,27 +321,6 @@ class ChunkReader {
|
|
|
319
321
|
}
|
|
320
322
|
}
|
|
321
323
|
}
|
|
322
|
-
function serializeStream(value, codecOptions) {
|
|
323
|
-
return new ReadableStream({
|
|
324
|
-
async start(controller) {
|
|
325
|
-
const {
|
|
326
|
-
serializeJSON
|
|
327
|
-
} = await import('@solidjs/web/serialization');
|
|
328
|
-
serializeJSON(value, {
|
|
329
|
-
...codecOptions,
|
|
330
|
-
onParse(node) {
|
|
331
|
-
controller.enqueue(createChunk(JSON.stringify(node)));
|
|
332
|
-
},
|
|
333
|
-
onDone() {
|
|
334
|
-
controller.close();
|
|
335
|
-
},
|
|
336
|
-
onError(error) {
|
|
337
|
-
controller.error(error);
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
324
|
async function deserializeStream(source, codecOptions) {
|
|
344
325
|
if (!source.body) {
|
|
345
326
|
throw new Error("missing body");
|
|
@@ -354,7 +335,7 @@ async function deserializeStream(source, codecOptions) {
|
|
|
354
335
|
function interpretChunk(chunk) {
|
|
355
336
|
return deserializeChunk(JSON.parse(chunk));
|
|
356
337
|
}
|
|
357
|
-
|
|
338
|
+
reader.drain(interpretChunk).then(() => deserializeChunk.abort(new Error("Server function stream ended unexpectedly.")), error => deserializeChunk.abort(error));
|
|
358
339
|
return interpretChunk(result.value);
|
|
359
340
|
}
|
|
360
341
|
return undefined;
|
|
@@ -634,6 +615,29 @@ function GET(fn) {
|
|
|
634
615
|
method: "GET"
|
|
635
616
|
});
|
|
636
617
|
}
|
|
618
|
+
function live(fn) {
|
|
619
|
+
if (!isServerFunction(fn) || typeof fn.id !== "string") {
|
|
620
|
+
throw new Error("live expects a server function reference");
|
|
621
|
+
}
|
|
622
|
+
const metadata = {
|
|
623
|
+
...getServerFunctionMetadata(fn),
|
|
624
|
+
live: true
|
|
625
|
+
};
|
|
626
|
+
const wrapped = async (...args) => {
|
|
627
|
+
const result = await fn(...args);
|
|
628
|
+
if (result !== null && typeof result === "object" && result[Symbol.asyncIterator]) {
|
|
629
|
+
result[LIVE_SOURCE] = true;
|
|
630
|
+
}
|
|
631
|
+
return result;
|
|
632
|
+
};
|
|
633
|
+
wrapped[SERVER_FUNCTION_METADATA] = metadata;
|
|
634
|
+
wrapped.id = fn.id;
|
|
635
|
+
Object.defineProperty(wrapped, "url", {
|
|
636
|
+
get: () => fn.url,
|
|
637
|
+
configurable: true
|
|
638
|
+
});
|
|
639
|
+
return wrapped;
|
|
640
|
+
}
|
|
637
641
|
function getServerFunctionInvocation() {
|
|
638
642
|
return getEventServerFunctionInvocation(getRequestEvent());
|
|
639
643
|
}
|
|
@@ -805,14 +809,102 @@ function isFormPost(request) {
|
|
|
805
809
|
const type = request.headers.get("content-type") || "";
|
|
806
810
|
return type.startsWith("application/x-www-form-urlencoded") || type.startsWith("multipart/form-data");
|
|
807
811
|
}
|
|
808
|
-
function
|
|
812
|
+
function serializeResponseStream(value, codecOptions, signal) {
|
|
813
|
+
let closeIterator = null;
|
|
814
|
+
let closed = false;
|
|
815
|
+
let cancelSerialize = null;
|
|
816
|
+
let onAbort = null;
|
|
817
|
+
const teardown = () => {
|
|
818
|
+
if (closed) return;
|
|
819
|
+
closed = true;
|
|
820
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
821
|
+
if (cancelSerialize) cancelSerialize();
|
|
822
|
+
if (closeIterator) closeIterator();
|
|
823
|
+
};
|
|
824
|
+
if (value !== null && typeof value === "object" && typeof value[Symbol.asyncIterator] === "function") {
|
|
825
|
+
const source = value;
|
|
826
|
+
value = {
|
|
827
|
+
[Symbol.asyncIterator]() {
|
|
828
|
+
const it = source[Symbol.asyncIterator]();
|
|
829
|
+
let finished = false;
|
|
830
|
+
closeIterator = () => {
|
|
831
|
+
if (finished) return;
|
|
832
|
+
finished = true;
|
|
833
|
+
try {
|
|
834
|
+
const returned = it.return && it.return();
|
|
835
|
+
if (returned && typeof returned.then === "function") returned.then(undefined, () => {});
|
|
836
|
+
} catch {}
|
|
837
|
+
};
|
|
838
|
+
if (closed) closeIterator();
|
|
839
|
+
return {
|
|
840
|
+
next: () => finished ? Promise.resolve({
|
|
841
|
+
done: true,
|
|
842
|
+
value: undefined
|
|
843
|
+
}) : it.next()
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
};
|
|
847
|
+
}
|
|
848
|
+
return new ReadableStream({
|
|
849
|
+
async start(controller) {
|
|
850
|
+
if (signal) {
|
|
851
|
+
if (signal.aborted) {
|
|
852
|
+
teardown();
|
|
853
|
+
controller.close();
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
onAbort = () => {
|
|
857
|
+
const alreadyClosed = closed;
|
|
858
|
+
teardown();
|
|
859
|
+
if (!alreadyClosed) {
|
|
860
|
+
try {
|
|
861
|
+
controller.error(signal.reason || new Error("The operation was aborted."));
|
|
862
|
+
} catch {}
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
signal.addEventListener("abort", onAbort);
|
|
866
|
+
}
|
|
867
|
+
const {
|
|
868
|
+
serializeJSON
|
|
869
|
+
} = await import('@solidjs/web/serialization');
|
|
870
|
+
if (closed) {
|
|
871
|
+
try {
|
|
872
|
+
controller.close();
|
|
873
|
+
} catch {}
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
876
|
+
cancelSerialize = serializeJSON(value, {
|
|
877
|
+
...codecOptions,
|
|
878
|
+
onParse(node) {
|
|
879
|
+
if (!closed) controller.enqueue(createChunk(JSON.stringify(node)));
|
|
880
|
+
},
|
|
881
|
+
onDone() {
|
|
882
|
+
if (closed) return;
|
|
883
|
+
closed = true;
|
|
884
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
885
|
+
controller.close();
|
|
886
|
+
},
|
|
887
|
+
onError(error) {
|
|
888
|
+
if (closed) return;
|
|
889
|
+
closed = true;
|
|
890
|
+
if (onAbort) signal.removeEventListener("abort", onAbort);
|
|
891
|
+
controller.error(error);
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
},
|
|
895
|
+
cancel() {
|
|
896
|
+
teardown();
|
|
897
|
+
}
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
function serializedResponse(value, headers, codec, signal) {
|
|
809
901
|
headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
|
|
810
902
|
headers.set("Content-Type", "text/plain");
|
|
811
|
-
return new Response(
|
|
903
|
+
return new Response(serializeResponseStream(value, codec, signal), {
|
|
812
904
|
headers
|
|
813
905
|
});
|
|
814
906
|
}
|
|
815
|
-
function encodeResult(value, headers, status, codec) {
|
|
907
|
+
function encodeResult(value, headers, status, codec, signal) {
|
|
816
908
|
const direct = getHeadersAndBody(value);
|
|
817
909
|
if (direct) {
|
|
818
910
|
for (const [key, val] of Object.entries(direct.headers || {})) {
|
|
@@ -840,7 +932,7 @@ function encodeResult(value, headers, status, codec) {
|
|
|
840
932
|
}
|
|
841
933
|
} catch {
|
|
842
934
|
}
|
|
843
|
-
const response = serializedResponse(value, headers, codec);
|
|
935
|
+
const response = serializedResponse(value, headers, codec, signal);
|
|
844
936
|
return status === 200 ? response : new Response(response.body, {
|
|
845
937
|
status,
|
|
846
938
|
headers
|
|
@@ -968,9 +1060,9 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
968
1060
|
if (!instance) {
|
|
969
1061
|
if (handleNoJS) return handleNoJS(result, request, parsed);
|
|
970
1062
|
if (result instanceof Response) return result;
|
|
971
|
-
return encodeResult(result, headers, 200, codec);
|
|
1063
|
+
return encodeResult(result, headers, 200, codec, request.signal);
|
|
972
1064
|
}
|
|
973
|
-
return encodeResult(result, headers, status, codec);
|
|
1065
|
+
return encodeResult(result, headers, status, codec, request.signal);
|
|
974
1066
|
} catch (x) {
|
|
975
1067
|
if (x instanceof Response || isResponseEnvelope(x)) {
|
|
976
1068
|
if (transformResult) {
|
|
@@ -1024,7 +1116,7 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
1024
1116
|
if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
|
|
1025
1117
|
if (x instanceof Response) return x;
|
|
1026
1118
|
}
|
|
1027
|
-
return encodeResult(x, headers, status, codec);
|
|
1119
|
+
return encodeResult(x, headers, status, codec, request.signal);
|
|
1028
1120
|
}
|
|
1029
1121
|
const safe = sanitizeServerError(x);
|
|
1030
1122
|
if (!instance) {
|
|
@@ -1036,10 +1128,10 @@ async function handleServerFunctionRequest(request, options = {}) {
|
|
|
1036
1128
|
}
|
|
1037
1129
|
const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
|
|
1038
1130
|
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
|
|
1039
|
-
return encodeResult(safe, headers, 200, codec);
|
|
1131
|
+
return encodeResult(safe, headers, 200, codec, request.signal);
|
|
1040
1132
|
}
|
|
1041
1133
|
};
|
|
1042
1134
|
return commitEventResponse(await dispatch(), event);
|
|
1043
1135
|
}
|
|
1044
1136
|
|
|
1045
|
-
export { ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GENERIC_SERVER_ERROR_MESSAGE, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, decodeResponsePayload, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getEventServerFunctionInvocation, getServerFunction, getServerFunctionInvocation, getServerFunctionMetadata, handleServerFunctionRequest, hasFlashCookie, isServerFunction, registerServerFunction, registerServerReference, sanitizeServerError, setServerFunctionsDev, subscribeFlightData, withMeta };
|
|
1137
|
+
export { ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GENERIC_SERVER_ERROR_MESSAGE, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, decodeResponsePayload, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getEventServerFunctionInvocation, getServerFunction, getServerFunctionInvocation, getServerFunctionMetadata, handleServerFunctionRequest, hasFlashCookie, isServerFunction, live, registerServerFunction, registerServerReference, sanitizeServerError, serializeResponseStream, setServerFunctionsDev, subscribeFlightData, withMeta };
|
package/types/index.d.ts
CHANGED
|
@@ -102,6 +102,12 @@ export declare function render(code: () => JSX.Element, element: MountableElemen
|
|
|
102
102
|
* Pass `options.renderId` to hydrate one of multiple roots emitted by a
|
|
103
103
|
* server render that used the same id.
|
|
104
104
|
*
|
|
105
|
+
* When the server renders a full document but the client hydrates only the
|
|
106
|
+
* app subtree, the server must give that subtree its own id namespace: wrap
|
|
107
|
+
* the document shell in `<NoHydration>` and re-enter with `<Hydration>`
|
|
108
|
+
* around the app. Otherwise the app's hydration ids are allocated under the
|
|
109
|
+
* document component's owner tree and this walk can never claim them.
|
|
110
|
+
*
|
|
105
111
|
* @example
|
|
106
112
|
* ```tsx
|
|
107
113
|
* import { hydrate } from "@solidjs/web";
|
|
@@ -170,7 +176,8 @@ export declare function Dynamic<T extends ValidComponent>(props: DynamicProps<T>
|
|
|
170
176
|
*
|
|
171
177
|
* By default the import starts as soon as `clientOnly` is called (module
|
|
172
178
|
* load); pass `{ lazy: true }` to defer the import to the component's first
|
|
173
|
-
* render.
|
|
179
|
+
* render. Pass `{ export: "Name" }` to use a named export of the resolved
|
|
180
|
+
* module instead of its default (mirrors `lazy()`'s option).
|
|
174
181
|
*
|
|
175
182
|
* @example
|
|
176
183
|
* ```tsx
|
|
@@ -178,11 +185,18 @@ export declare function Dynamic<T extends ValidComponent>(props: DynamicProps<T>
|
|
|
178
185
|
* // <Chart fallback={<div>Loading chart…</div>} data={data()} />
|
|
179
186
|
* ```
|
|
180
187
|
*/
|
|
188
|
+
export declare function clientOnly<M extends Record<string, any>, K extends keyof M & string>(fn: () => Promise<M>, options: {
|
|
189
|
+
lazy?: boolean;
|
|
190
|
+
export: K;
|
|
191
|
+
}, moduleUrl?: string): Component<ComponentProps<M[K]> & {
|
|
192
|
+
fallback?: JSX.Element;
|
|
193
|
+
}>;
|
|
181
194
|
export declare function clientOnly<T extends Component<any>>(fn: () => Promise<{
|
|
182
195
|
default: T;
|
|
183
196
|
}>, options?: {
|
|
184
197
|
lazy?: boolean;
|
|
185
|
-
|
|
198
|
+
export?: string;
|
|
199
|
+
}, moduleUrl?: string): Component<ComponentProps<T> & {
|
|
186
200
|
fallback?: JSX.Element;
|
|
187
201
|
}>;
|
|
188
202
|
/**
|
package/types/jsx.d.ts
CHANGED
|
@@ -249,6 +249,15 @@ export namespace JSX {
|
|
|
249
249
|
ref?: Ref<T>;
|
|
250
250
|
children?: Element | undefined;
|
|
251
251
|
$ServerOnly?: boolean | undefined;
|
|
252
|
+
/**
|
|
253
|
+
* Entity identity for server markup (SSR-only): compiles to the `_key`
|
|
254
|
+
* attribute the frame morph matches keyed elements by, so live element
|
|
255
|
+
* state (form values, `open`, focus) follows the entity across
|
|
256
|
+
* reordering morphs. Sibling-scoped, like client keyed rendering.
|
|
257
|
+
* Stripped from DOM compiles; on components, `$key` is slot occurrence
|
|
258
|
+
* identity instead.
|
|
259
|
+
*/
|
|
260
|
+
$key?: string | number | undefined;
|
|
252
261
|
}
|
|
253
262
|
interface ExplicitProperties {}
|
|
254
263
|
type PropAttributes = {
|
|
@@ -156,6 +156,36 @@ export function GET<A extends readonly any[], R>(
|
|
|
156
156
|
fn: (...args: A) => R
|
|
157
157
|
): ServerFunction<A, Awaited<R>>;
|
|
158
158
|
|
|
159
|
+
/** Wire-state transitions a live call's iterable can report. */
|
|
160
|
+
export type LiveSourceStatus = "connected" | "reconnecting" | "closed";
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* A live call's answer: the source's iterable, plus an optional `onstatus`
|
|
164
|
+
* side channel for the wire facts the reconnect loop erases from the value
|
|
165
|
+
* stream — `"connected"` on each successful (re)connect, `"reconnecting"`
|
|
166
|
+
* (with the error) on each post-connect death, `"closed"` when the source
|
|
167
|
+
* completes or the consumer ends it.
|
|
168
|
+
*/
|
|
169
|
+
export type LiveSource<R> = R & {
|
|
170
|
+
onstatus?: (state: LiveSourceStatus, error?: unknown) => void;
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Declares a value-shaped live source: a server function returning an async
|
|
175
|
+
* iterable whose yields are successive VALUES of one logical query, with the
|
|
176
|
+
* contract that the source re-yields current state on every invocation.
|
|
177
|
+
* Calls to the returned reference produce an iterable that survives the
|
|
178
|
+
* connection — post-connect deaths re-invoke with exponential backoff
|
|
179
|
+
* (reset per healthy value, woken early by connectivity returning),
|
|
180
|
+
* first-connect failures reject like a normal call, and `break` aborts the
|
|
181
|
+
* in-flight request. Live calls are reads and never opt into single-flight
|
|
182
|
+
* enveloping. Wire state, if wanted, rides the returned iterable's
|
|
183
|
+
* `onstatus` hook. Compose with `GET` inside-out: `live(GET(fn))`.
|
|
184
|
+
*/
|
|
185
|
+
export function live<A extends readonly any[], R>(
|
|
186
|
+
fn: (...args: A) => R
|
|
187
|
+
): ServerFunction<A, LiveSource<Awaited<R>>>;
|
|
188
|
+
|
|
159
189
|
/**
|
|
160
190
|
* Compiler ABI — emitted by compiled `"use server"` client output where a
|
|
161
191
|
* server function was referenced; produces the fetch-backed callable for
|
|
@@ -392,6 +392,34 @@ export function GET<A extends readonly any[], R>(
|
|
|
392
392
|
fn: (...args: A) => R
|
|
393
393
|
): ServerFunction<A, Awaited<R>>;
|
|
394
394
|
|
|
395
|
+
/** Wire-state transitions a live call's iterable can report (client side). */
|
|
396
|
+
export type LiveSourceStatus = "connected" | "reconnecting" | "closed";
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Type-level mirror of the client's live answer shape so isomorphic code
|
|
400
|
+
* assigning `onstatus` typechecks against either build's declarations. On
|
|
401
|
+
* the server the hook is inert: in-process calls hand back the source's
|
|
402
|
+
* own iterable — there is no connection to report on.
|
|
403
|
+
*/
|
|
404
|
+
export type LiveSource<R> = R & {
|
|
405
|
+
onstatus?: (state: LiveSourceStatus, error?: unknown) => void;
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* Declares a value-shaped live source: a server function returning an async
|
|
410
|
+
* iterable whose yields are successive VALUES of one logical query, with
|
|
411
|
+
* the contract that the source re-yields current state on every invocation.
|
|
412
|
+
* Writes `live: true` on the metadata channel and brands the resolved
|
|
413
|
+
* iterable (registered symbol `solid.LiveSource`) so SSR faces meeting the
|
|
414
|
+
* value in-process can apply live policy (document face: first value, then
|
|
415
|
+
* client takeover). Dispatch is untouched — over-the-wire calls stream the
|
|
416
|
+
* raw registered function's result. Declare live outermost:
|
|
417
|
+
* `live(GET(fn))`.
|
|
418
|
+
*/
|
|
419
|
+
export function live<A extends readonly any[], R>(
|
|
420
|
+
fn: (...args: A) => R
|
|
421
|
+
): ServerFunction<A, LiveSource<Awaited<R>>>;
|
|
422
|
+
|
|
395
423
|
/** Identity of the currently executing server function call. */
|
|
396
424
|
export interface ServerFunctionInvocation {
|
|
397
425
|
id: string;
|
package/types-cjs/index.d.cts
CHANGED
|
@@ -102,6 +102,12 @@ export declare function render(code: () => JSX.Element, element: MountableElemen
|
|
|
102
102
|
* Pass `options.renderId` to hydrate one of multiple roots emitted by a
|
|
103
103
|
* server render that used the same id.
|
|
104
104
|
*
|
|
105
|
+
* When the server renders a full document but the client hydrates only the
|
|
106
|
+
* app subtree, the server must give that subtree its own id namespace: wrap
|
|
107
|
+
* the document shell in `<NoHydration>` and re-enter with `<Hydration>`
|
|
108
|
+
* around the app. Otherwise the app's hydration ids are allocated under the
|
|
109
|
+
* document component's owner tree and this walk can never claim them.
|
|
110
|
+
*
|
|
105
111
|
* @example
|
|
106
112
|
* ```tsx
|
|
107
113
|
* import { hydrate } from "@solidjs/web";
|
|
@@ -170,7 +176,8 @@ export declare function Dynamic<T extends ValidComponent>(props: DynamicProps<T>
|
|
|
170
176
|
*
|
|
171
177
|
* By default the import starts as soon as `clientOnly` is called (module
|
|
172
178
|
* load); pass `{ lazy: true }` to defer the import to the component's first
|
|
173
|
-
* render.
|
|
179
|
+
* render. Pass `{ export: "Name" }` to use a named export of the resolved
|
|
180
|
+
* module instead of its default (mirrors `lazy()`'s option).
|
|
174
181
|
*
|
|
175
182
|
* @example
|
|
176
183
|
* ```tsx
|
|
@@ -178,11 +185,18 @@ export declare function Dynamic<T extends ValidComponent>(props: DynamicProps<T>
|
|
|
178
185
|
* // <Chart fallback={<div>Loading chart…</div>} data={data()} />
|
|
179
186
|
* ```
|
|
180
187
|
*/
|
|
188
|
+
export declare function clientOnly<M extends Record<string, any>, K extends keyof M & string>(fn: () => Promise<M>, options: {
|
|
189
|
+
lazy?: boolean;
|
|
190
|
+
export: K;
|
|
191
|
+
}, moduleUrl?: string): Component<ComponentProps<M[K]> & {
|
|
192
|
+
fallback?: JSX.Element;
|
|
193
|
+
}>;
|
|
181
194
|
export declare function clientOnly<T extends Component<any>>(fn: () => Promise<{
|
|
182
195
|
default: T;
|
|
183
196
|
}>, options?: {
|
|
184
197
|
lazy?: boolean;
|
|
185
|
-
|
|
198
|
+
export?: string;
|
|
199
|
+
}, moduleUrl?: string): Component<ComponentProps<T> & {
|
|
186
200
|
fallback?: JSX.Element;
|
|
187
201
|
}>;
|
|
188
202
|
/**
|
package/types-cjs/jsx.d.cts
CHANGED
|
@@ -249,6 +249,15 @@ export namespace JSX {
|
|
|
249
249
|
ref?: Ref<T>;
|
|
250
250
|
children?: Element | undefined;
|
|
251
251
|
$ServerOnly?: boolean | undefined;
|
|
252
|
+
/**
|
|
253
|
+
* Entity identity for server markup (SSR-only): compiles to the `_key`
|
|
254
|
+
* attribute the frame morph matches keyed elements by, so live element
|
|
255
|
+
* state (form values, `open`, focus) follows the entity across
|
|
256
|
+
* reordering morphs. Sibling-scoped, like client keyed rendering.
|
|
257
|
+
* Stripped from DOM compiles; on components, `$key` is slot occurrence
|
|
258
|
+
* identity instead.
|
|
259
|
+
*/
|
|
260
|
+
$key?: string | number | undefined;
|
|
252
261
|
}
|
|
253
262
|
interface ExplicitProperties {}
|
|
254
263
|
type PropAttributes = {
|