@solidjs/start 1.3.0 → 1.3.2
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.
|
@@ -124,6 +124,9 @@ class SerovalChunkReader {
|
|
|
124
124
|
// deserialize the data
|
|
125
125
|
const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
|
|
126
126
|
const bytes = Number.parseInt(head, 16); // ;0x00000000;
|
|
127
|
+
if (Number.isNaN(bytes)) {
|
|
128
|
+
throw new Error("Malformed server function stream header.");
|
|
129
|
+
}
|
|
127
130
|
// Check if the buffer has enough bytes to be parsed
|
|
128
131
|
while (bytes > this.buffer.length - 12) {
|
|
129
132
|
// If it's not enough, and the reader is done
|
|
@@ -159,7 +162,7 @@ class SerovalChunkReader {
|
|
|
159
162
|
export async function serializeToJSONString(value) {
|
|
160
163
|
// const response = new Response(serializeToJSONStream(value));
|
|
161
164
|
// return await response.text();
|
|
162
|
-
return JSON.stringify(toJSONAsync(value, {
|
|
165
|
+
return JSON.stringify(await toJSONAsync(value, {
|
|
163
166
|
plugins: DEFAULT_PLUGINS,
|
|
164
167
|
depthLimit: MAX_SERIALIZATION_DEPTH_LIMIT,
|
|
165
168
|
disabledFeatures: DISABLED_FEATURES,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { describe, expect, it, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import { deserializeJSONStream, deserializeJSStream } from "./serialization";
|
|
3
|
+
const encoder = new TextEncoder();
|
|
4
|
+
function makeChunk(dataStr, declaredBytes) {
|
|
5
|
+
const data = encoder.encode(dataStr);
|
|
6
|
+
const bytes = declaredBytes ?? data.length;
|
|
7
|
+
const baseHex = bytes.toString(16).padStart(8, "0");
|
|
8
|
+
const head = encoder.encode(`;0x${baseHex};`);
|
|
9
|
+
const chunk = new Uint8Array(head.length + data.length);
|
|
10
|
+
chunk.set(head);
|
|
11
|
+
chunk.set(data, head.length);
|
|
12
|
+
return chunk;
|
|
13
|
+
}
|
|
14
|
+
function streamFromChunks(chunks) {
|
|
15
|
+
return new ReadableStream({
|
|
16
|
+
start(controller) {
|
|
17
|
+
for (const c of chunks)
|
|
18
|
+
controller.enqueue(c);
|
|
19
|
+
controller.close();
|
|
20
|
+
},
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function responseWithChunks(chunks) {
|
|
24
|
+
if (chunks === null)
|
|
25
|
+
return new Response(null);
|
|
26
|
+
return new Response(streamFromChunks(chunks));
|
|
27
|
+
}
|
|
28
|
+
const cases = [
|
|
29
|
+
{ name: "deserializeJSONStream", call: (r) => deserializeJSONStream(r) },
|
|
30
|
+
{ name: "deserializeJSStream", call: (r) => deserializeJSStream("server-fn:0", r) },
|
|
31
|
+
];
|
|
32
|
+
describe("Serialization negative testing (unhappy paths)", () => {
|
|
33
|
+
// TODO: Serialization drains remaining chunks in the background for performance and
|
|
34
|
+
// its async errors aren't propagated to a designated error boundary.
|
|
35
|
+
// This is a temporary catch-all to avoid unhandled rejections in this test suite until
|
|
36
|
+
// we have a better solution for handling async errors in serialization.
|
|
37
|
+
const _unhandledRejectionHandler = (reason, promise) => {
|
|
38
|
+
// eslint-disable-next-line no-console
|
|
39
|
+
console.error("Unhandled rejection (ignored) in serialization.test:", reason, promise);
|
|
40
|
+
};
|
|
41
|
+
// Install immediately and retain for the duration of this test file.
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
process.on("unhandledRejection", _unhandledRejectionHandler);
|
|
44
|
+
});
|
|
45
|
+
afterEach(async () => {
|
|
46
|
+
// Wait for any pending microtasks to allow background processes to complete
|
|
47
|
+
await new Promise(resolve => setTimeout(resolve, 0));
|
|
48
|
+
process.off("unhandledRejection", _unhandledRejectionHandler);
|
|
49
|
+
});
|
|
50
|
+
for (const fn of cases) {
|
|
51
|
+
it(`${fn.name} throws on missing body`, async () => {
|
|
52
|
+
await expect(fn.call(responseWithChunks(null))).rejects.toThrow("missing body");
|
|
53
|
+
});
|
|
54
|
+
it(`${fn.name} throws on plain XML response`, async () => {
|
|
55
|
+
const xml = '<?xml version="1.0" encoding="UTF-8"?><Error><Code>AccessDenied</Code><Message>Access Denied</Message></Error>';
|
|
56
|
+
const chunk = encoder.encode(xml);
|
|
57
|
+
const resp = new Response(new ReadableStream({
|
|
58
|
+
start(controller) {
|
|
59
|
+
controller.enqueue(chunk);
|
|
60
|
+
controller.close();
|
|
61
|
+
},
|
|
62
|
+
}));
|
|
63
|
+
await expect(fn.call(resp)).rejects.toThrow();
|
|
64
|
+
});
|
|
65
|
+
it(`${fn.name} throws Malformed server function stream when header larger than provided bytes`, async () => {
|
|
66
|
+
const chunk = makeChunk("bad", 16); // declare more than actual
|
|
67
|
+
await expect(fn.call(responseWithChunks([chunk]))).rejects.toThrow("Malformed server function stream.");
|
|
68
|
+
});
|
|
69
|
+
it(`${fn.name} throws Malformed server function stream when header smaller than provided bytes`, async () => {
|
|
70
|
+
const chunk = makeChunk("bad", 2); // declare less than actual
|
|
71
|
+
await expect(fn.call(responseWithChunks([chunk]))).rejects.toThrow();
|
|
72
|
+
});
|
|
73
|
+
it(`${fn.name} throws on valid header but invalid JSON body`, async () => {
|
|
74
|
+
const chunk = makeChunk("not-a-json");
|
|
75
|
+
await expect(fn.call(responseWithChunks([chunk]))).rejects.toThrow();
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
});
|