@solidjs/web 2.0.0-rc.0 → 2.0.0-rc.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.
- package/README.md +1 -1
- package/dist/dev.cjs +40 -11
- package/dist/dev.js +39 -12
- package/dist/server.cjs +419 -69
- package/dist/server.js +411 -73
- package/dist/web.cjs +37 -11
- package/dist/web.js +36 -12
- package/frames/dist/client.cjs +94 -8
- package/frames/dist/client.dev.cjs +97 -8
- package/frames/dist/client.dev.js +98 -9
- package/frames/dist/client.js +95 -9
- package/frames/dist/server.cjs +266 -56
- package/frames/dist/server.js +267 -57
- package/package.json +4 -3
- package/serialization/dist/decode.cjs +32 -3
- package/serialization/dist/decode.js +33 -4
- package/serialization/dist/serialization.cjs +32 -3
- package/serialization/dist/serialization.js +33 -4
- package/server-functions/dist/client.cjs +196 -8
- package/server-functions/dist/client.js +195 -9
- package/server-functions/dist/server.cjs +201 -36
- package/server-functions/dist/server.dev.cjs +201 -36
- package/server-functions/dist/server.dev.js +199 -37
- package/server-functions/dist/server.js +199 -37
- package/types/core.d.ts +3 -0
- package/types/frames/frame-client.d.ts +18 -0
- package/types/index.d.ts +16 -2
- package/types/jsx.d.ts +9 -0
- package/types/server-functions/client.d.ts +61 -0
- package/types/server-functions/server.d.ts +94 -1
- package/types/server-mock.d.ts +11 -2
- package/types/server.d.ts +23 -2
- package/types-cjs/core.d.cts +3 -0
- package/types-cjs/frames/frame-client.d.cts +18 -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 +61 -0
- package/types-cjs/server-functions/server.d.cts +94 -1
- package/types-cjs/server-mock.d.cts +11 -2
- package/types-cjs/server.d.cts +23 -2
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidjs/web",
|
|
3
3
|
"description": "Solid's web runtime: client rendering, hydration, SSR, and DOM-specific control flow (Portal, Dynamic).",
|
|
4
|
-
"version": "2.0.0-rc.
|
|
4
|
+
"version": "2.0.0-rc.2",
|
|
5
5
|
"author": "Ryan Carniato",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://solidjs.com",
|
|
@@ -378,10 +378,11 @@
|
|
|
378
378
|
"seroval-plugins": "~1.5.4"
|
|
379
379
|
},
|
|
380
380
|
"peerDependencies": {
|
|
381
|
-
"solid-js": "^2.0.0-rc.
|
|
381
|
+
"solid-js": "^2.0.0-rc.2"
|
|
382
382
|
},
|
|
383
383
|
"devDependencies": {
|
|
384
|
-
"
|
|
384
|
+
"@codspeed/vitest-plugin": "^5.4.0",
|
|
385
|
+
"solid-js": "2.0.0-rc.2"
|
|
385
386
|
},
|
|
386
387
|
"scripts": {
|
|
387
388
|
"build": "npm-run-all -nl build:clean types:copy-jsx build:js",
|
|
@@ -8,6 +8,9 @@ const state = globalThis[STATE] || (globalThis[STATE] = {
|
|
|
8
8
|
materialized: new WeakMap(),
|
|
9
9
|
materializedValues: new WeakSet()
|
|
10
10
|
});
|
|
11
|
+
function setContainerTraceStreamMint(fn) {
|
|
12
|
+
state.streamOf = fn;
|
|
13
|
+
}
|
|
11
14
|
const TRACE = Symbol.for("dom-expressions.container-trace");
|
|
12
15
|
function materialize(marker) {
|
|
13
16
|
let value = state.materialized.get(marker.$tr);
|
|
@@ -20,9 +23,10 @@ function materialize(marker) {
|
|
|
20
23
|
}
|
|
21
24
|
function parseTrace(value, ctx) {
|
|
22
25
|
const trace = value[TRACE];
|
|
26
|
+
const sub = trace.subscribe();
|
|
23
27
|
return {
|
|
24
28
|
a: trace.array ? 1 : 0,
|
|
25
|
-
i: ctx.parse(
|
|
29
|
+
i: ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
|
|
26
30
|
};
|
|
27
31
|
}
|
|
28
32
|
const ContainerTracePlugin = {
|
|
@@ -36,9 +40,10 @@ const ContainerTracePlugin = {
|
|
|
36
40
|
},
|
|
37
41
|
async async(value, ctx) {
|
|
38
42
|
const trace = value[TRACE];
|
|
43
|
+
const sub = trace.subscribe();
|
|
39
44
|
return {
|
|
40
45
|
a: trace.array ? 1 : 0,
|
|
41
|
-
i: await ctx.parse(
|
|
46
|
+
i: await ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
|
|
42
47
|
};
|
|
43
48
|
},
|
|
44
49
|
stream: parseTrace
|
|
@@ -56,6 +61,18 @@ const ContainerTracePlugin = {
|
|
|
56
61
|
}
|
|
57
62
|
};
|
|
58
63
|
|
|
64
|
+
setContainerTraceStreamMint(iterable => {
|
|
65
|
+
const stream = seroval.createStream();
|
|
66
|
+
(async () => {
|
|
67
|
+
try {
|
|
68
|
+
for await (const value of iterable) stream.next(value);
|
|
69
|
+
stream.return(undefined);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
stream.throw(error);
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
return stream;
|
|
75
|
+
});
|
|
59
76
|
const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
|
|
60
77
|
web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
|
|
61
78
|
web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin,
|
|
@@ -79,12 +96,24 @@ function resolveCodecOptions({
|
|
|
79
96
|
function createJSONDeserializer(options) {
|
|
80
97
|
const refs = new Map();
|
|
81
98
|
const resolved = resolveCodecOptions(options);
|
|
82
|
-
|
|
99
|
+
function deserializeJSONChunk(node) {
|
|
83
100
|
return seroval.fromCrossJSON(node, {
|
|
84
101
|
refs,
|
|
85
102
|
...resolved
|
|
86
103
|
});
|
|
104
|
+
}
|
|
105
|
+
deserializeJSONChunk.abort = function abort(error) {
|
|
106
|
+
for (const value of refs.values()) {
|
|
107
|
+
if (value === null || typeof value !== "object") continue;
|
|
108
|
+
if (value.__SEROVAL_STREAM__) {
|
|
109
|
+
value.throw(error);
|
|
110
|
+
} else if (typeof value.s === "function" && typeof value.f === "function" && value.p instanceof Promise) {
|
|
111
|
+
value.p.then(undefined, () => {});
|
|
112
|
+
value.f(error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
87
115
|
};
|
|
116
|
+
return deserializeJSONChunk;
|
|
88
117
|
}
|
|
89
118
|
function createJSONDataTable(options) {
|
|
90
119
|
const deserialize = createJSONDeserializer(options);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Feature, fromCrossJSON } from 'seroval';
|
|
1
|
+
import { Feature, fromCrossJSON, createStream } from 'seroval';
|
|
2
2
|
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
|
|
3
3
|
|
|
4
4
|
const STATE = Symbol.for("dom-expressions.container-trace-state");
|
|
@@ -6,6 +6,9 @@ const state = globalThis[STATE] || (globalThis[STATE] = {
|
|
|
6
6
|
materialized: new WeakMap(),
|
|
7
7
|
materializedValues: new WeakSet()
|
|
8
8
|
});
|
|
9
|
+
function setContainerTraceStreamMint(fn) {
|
|
10
|
+
state.streamOf = fn;
|
|
11
|
+
}
|
|
9
12
|
const TRACE = Symbol.for("dom-expressions.container-trace");
|
|
10
13
|
function materialize(marker) {
|
|
11
14
|
let value = state.materialized.get(marker.$tr);
|
|
@@ -18,9 +21,10 @@ function materialize(marker) {
|
|
|
18
21
|
}
|
|
19
22
|
function parseTrace(value, ctx) {
|
|
20
23
|
const trace = value[TRACE];
|
|
24
|
+
const sub = trace.subscribe();
|
|
21
25
|
return {
|
|
22
26
|
a: trace.array ? 1 : 0,
|
|
23
|
-
i: ctx.parse(
|
|
27
|
+
i: ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
|
|
24
28
|
};
|
|
25
29
|
}
|
|
26
30
|
const ContainerTracePlugin = {
|
|
@@ -34,9 +38,10 @@ const ContainerTracePlugin = {
|
|
|
34
38
|
},
|
|
35
39
|
async async(value, ctx) {
|
|
36
40
|
const trace = value[TRACE];
|
|
41
|
+
const sub = trace.subscribe();
|
|
37
42
|
return {
|
|
38
43
|
a: trace.array ? 1 : 0,
|
|
39
|
-
i: await ctx.parse(
|
|
44
|
+
i: await ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
|
|
40
45
|
};
|
|
41
46
|
},
|
|
42
47
|
stream: parseTrace
|
|
@@ -54,6 +59,18 @@ const ContainerTracePlugin = {
|
|
|
54
59
|
}
|
|
55
60
|
};
|
|
56
61
|
|
|
62
|
+
setContainerTraceStreamMint(iterable => {
|
|
63
|
+
const stream = createStream();
|
|
64
|
+
(async () => {
|
|
65
|
+
try {
|
|
66
|
+
for await (const value of iterable) stream.next(value);
|
|
67
|
+
stream.return(undefined);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
stream.throw(error);
|
|
70
|
+
}
|
|
71
|
+
})();
|
|
72
|
+
return stream;
|
|
73
|
+
});
|
|
57
74
|
const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
|
|
58
75
|
CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
|
|
59
76
|
FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin,
|
|
@@ -77,12 +94,24 @@ function resolveCodecOptions({
|
|
|
77
94
|
function createJSONDeserializer(options) {
|
|
78
95
|
const refs = new Map();
|
|
79
96
|
const resolved = resolveCodecOptions(options);
|
|
80
|
-
|
|
97
|
+
function deserializeJSONChunk(node) {
|
|
81
98
|
return fromCrossJSON(node, {
|
|
82
99
|
refs,
|
|
83
100
|
...resolved
|
|
84
101
|
});
|
|
102
|
+
}
|
|
103
|
+
deserializeJSONChunk.abort = function abort(error) {
|
|
104
|
+
for (const value of refs.values()) {
|
|
105
|
+
if (value === null || typeof value !== "object") continue;
|
|
106
|
+
if (value.__SEROVAL_STREAM__) {
|
|
107
|
+
value.throw(error);
|
|
108
|
+
} else if (typeof value.s === "function" && typeof value.f === "function" && value.p instanceof Promise) {
|
|
109
|
+
value.p.then(undefined, () => {});
|
|
110
|
+
value.f(error);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
85
113
|
};
|
|
114
|
+
return deserializeJSONChunk;
|
|
86
115
|
}
|
|
87
116
|
function createJSONDataTable(options) {
|
|
88
117
|
const deserialize = createJSONDeserializer(options);
|
|
@@ -8,6 +8,9 @@ const state = globalThis[STATE] || (globalThis[STATE] = {
|
|
|
8
8
|
materialized: new WeakMap(),
|
|
9
9
|
materializedValues: new WeakSet()
|
|
10
10
|
});
|
|
11
|
+
function setContainerTraceStreamMint(fn) {
|
|
12
|
+
state.streamOf = fn;
|
|
13
|
+
}
|
|
11
14
|
const TRACE = Symbol.for("dom-expressions.container-trace");
|
|
12
15
|
function materialize(marker) {
|
|
13
16
|
let value = state.materialized.get(marker.$tr);
|
|
@@ -20,9 +23,10 @@ function materialize(marker) {
|
|
|
20
23
|
}
|
|
21
24
|
function parseTrace(value, ctx) {
|
|
22
25
|
const trace = value[TRACE];
|
|
26
|
+
const sub = trace.subscribe();
|
|
23
27
|
return {
|
|
24
28
|
a: trace.array ? 1 : 0,
|
|
25
|
-
i: ctx.parse(
|
|
29
|
+
i: ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
|
|
26
30
|
};
|
|
27
31
|
}
|
|
28
32
|
const ContainerTracePlugin = {
|
|
@@ -36,9 +40,10 @@ const ContainerTracePlugin = {
|
|
|
36
40
|
},
|
|
37
41
|
async async(value, ctx) {
|
|
38
42
|
const trace = value[TRACE];
|
|
43
|
+
const sub = trace.subscribe();
|
|
39
44
|
return {
|
|
40
45
|
a: trace.array ? 1 : 0,
|
|
41
|
-
i: await ctx.parse(
|
|
46
|
+
i: await ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
|
|
42
47
|
};
|
|
43
48
|
},
|
|
44
49
|
stream: parseTrace
|
|
@@ -56,6 +61,18 @@ const ContainerTracePlugin = {
|
|
|
56
61
|
}
|
|
57
62
|
};
|
|
58
63
|
|
|
64
|
+
setContainerTraceStreamMint(iterable => {
|
|
65
|
+
const stream = seroval.createStream();
|
|
66
|
+
(async () => {
|
|
67
|
+
try {
|
|
68
|
+
for await (const value of iterable) stream.next(value);
|
|
69
|
+
stream.return(undefined);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
stream.throw(error);
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
return stream;
|
|
75
|
+
});
|
|
59
76
|
const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
|
|
60
77
|
web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
|
|
61
78
|
web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin,
|
|
@@ -79,12 +96,24 @@ function resolveCodecOptions({
|
|
|
79
96
|
function createJSONDeserializer(options) {
|
|
80
97
|
const refs = new Map();
|
|
81
98
|
const resolved = resolveCodecOptions(options);
|
|
82
|
-
|
|
99
|
+
function deserializeJSONChunk(node) {
|
|
83
100
|
return seroval.fromCrossJSON(node, {
|
|
84
101
|
refs,
|
|
85
102
|
...resolved
|
|
86
103
|
});
|
|
104
|
+
}
|
|
105
|
+
deserializeJSONChunk.abort = function abort(error) {
|
|
106
|
+
for (const value of refs.values()) {
|
|
107
|
+
if (value === null || typeof value !== "object") continue;
|
|
108
|
+
if (value.__SEROVAL_STREAM__) {
|
|
109
|
+
value.throw(error);
|
|
110
|
+
} else if (typeof value.s === "function" && typeof value.f === "function" && value.p instanceof Promise) {
|
|
111
|
+
value.p.then(undefined, () => {});
|
|
112
|
+
value.f(error);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
87
115
|
};
|
|
116
|
+
return deserializeJSONChunk;
|
|
88
117
|
}
|
|
89
118
|
function createJSONDataTable(options) {
|
|
90
119
|
const deserialize = createJSONDeserializer(options);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Feature, fromCrossJSON, toCrossJSONStream, Serializer, getCrossReferenceHeader } from 'seroval';
|
|
1
|
+
import { Feature, fromCrossJSON, createStream, toCrossJSONStream, Serializer, getCrossReferenceHeader } from 'seroval';
|
|
2
2
|
export { OpaqueReference, createPlugin } from 'seroval';
|
|
3
3
|
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
|
|
4
4
|
|
|
@@ -7,6 +7,9 @@ const state = globalThis[STATE] || (globalThis[STATE] = {
|
|
|
7
7
|
materialized: new WeakMap(),
|
|
8
8
|
materializedValues: new WeakSet()
|
|
9
9
|
});
|
|
10
|
+
function setContainerTraceStreamMint(fn) {
|
|
11
|
+
state.streamOf = fn;
|
|
12
|
+
}
|
|
10
13
|
const TRACE = Symbol.for("dom-expressions.container-trace");
|
|
11
14
|
function materialize(marker) {
|
|
12
15
|
let value = state.materialized.get(marker.$tr);
|
|
@@ -19,9 +22,10 @@ function materialize(marker) {
|
|
|
19
22
|
}
|
|
20
23
|
function parseTrace(value, ctx) {
|
|
21
24
|
const trace = value[TRACE];
|
|
25
|
+
const sub = trace.subscribe();
|
|
22
26
|
return {
|
|
23
27
|
a: trace.array ? 1 : 0,
|
|
24
|
-
i: ctx.parse(
|
|
28
|
+
i: ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
|
|
25
29
|
};
|
|
26
30
|
}
|
|
27
31
|
const ContainerTracePlugin = {
|
|
@@ -35,9 +39,10 @@ const ContainerTracePlugin = {
|
|
|
35
39
|
},
|
|
36
40
|
async async(value, ctx) {
|
|
37
41
|
const trace = value[TRACE];
|
|
42
|
+
const sub = trace.subscribe();
|
|
38
43
|
return {
|
|
39
44
|
a: trace.array ? 1 : 0,
|
|
40
|
-
i: await ctx.parse(
|
|
45
|
+
i: await ctx.parse(state.streamOf ? state.streamOf(sub) : sub)
|
|
41
46
|
};
|
|
42
47
|
},
|
|
43
48
|
stream: parseTrace
|
|
@@ -55,6 +60,18 @@ const ContainerTracePlugin = {
|
|
|
55
60
|
}
|
|
56
61
|
};
|
|
57
62
|
|
|
63
|
+
setContainerTraceStreamMint(iterable => {
|
|
64
|
+
const stream = createStream();
|
|
65
|
+
(async () => {
|
|
66
|
+
try {
|
|
67
|
+
for await (const value of iterable) stream.next(value);
|
|
68
|
+
stream.return(undefined);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
stream.throw(error);
|
|
71
|
+
}
|
|
72
|
+
})();
|
|
73
|
+
return stream;
|
|
74
|
+
});
|
|
58
75
|
const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
|
|
59
76
|
CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
|
|
60
77
|
FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin,
|
|
@@ -78,12 +95,24 @@ function resolveCodecOptions({
|
|
|
78
95
|
function createJSONDeserializer(options) {
|
|
79
96
|
const refs = new Map();
|
|
80
97
|
const resolved = resolveCodecOptions(options);
|
|
81
|
-
|
|
98
|
+
function deserializeJSONChunk(node) {
|
|
82
99
|
return fromCrossJSON(node, {
|
|
83
100
|
refs,
|
|
84
101
|
...resolved
|
|
85
102
|
});
|
|
103
|
+
}
|
|
104
|
+
deserializeJSONChunk.abort = function abort(error) {
|
|
105
|
+
for (const value of refs.values()) {
|
|
106
|
+
if (value === null || typeof value !== "object") continue;
|
|
107
|
+
if (value.__SEROVAL_STREAM__) {
|
|
108
|
+
value.throw(error);
|
|
109
|
+
} else if (typeof value.s === "function" && typeof value.f === "function" && value.p instanceof Promise) {
|
|
110
|
+
value.p.then(undefined, () => {});
|
|
111
|
+
value.f(error);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
86
114
|
};
|
|
115
|
+
return deserializeJSONChunk;
|
|
87
116
|
}
|
|
88
117
|
function createJSONDataTable(options) {
|
|
89
118
|
const deserialize = createJSONDeserializer(options);
|
|
@@ -18,6 +18,7 @@ function withMeta(fn, meta) {
|
|
|
18
18
|
Object.assign(metadata, meta);
|
|
19
19
|
return fn;
|
|
20
20
|
}
|
|
21
|
+
const LIVE_SOURCE = Symbol.for("solid.LiveSource");
|
|
21
22
|
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
|
|
22
23
|
function provideServerFunctionRPC(rpc) {
|
|
23
24
|
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
|
|
@@ -138,7 +139,7 @@ const BodyFormat = {
|
|
|
138
139
|
Uint8Array: "7",
|
|
139
140
|
Json: "8"
|
|
140
141
|
};
|
|
141
|
-
const JSON_SAFE_DEPTH_LIMIT =
|
|
142
|
+
const JSON_SAFE_DEPTH_LIMIT = 4096;
|
|
142
143
|
const EXIT = {};
|
|
143
144
|
function isJSONSafe(value) {
|
|
144
145
|
const stack = [value];
|
|
@@ -165,6 +166,7 @@ function isJSONSafe(value) {
|
|
|
165
166
|
} else {
|
|
166
167
|
const proto = Object.getPrototypeOf(v);
|
|
167
168
|
if (proto !== Object.prototype && proto !== null) return false;
|
|
169
|
+
if (Symbol.asyncIterator in v || Symbol.iterator in v) return false;
|
|
168
170
|
for (const k in v) stack.push(v[k]);
|
|
169
171
|
}
|
|
170
172
|
}
|
|
@@ -366,7 +368,7 @@ async function deserializeStream(source, codecOptions) {
|
|
|
366
368
|
function interpretChunk(chunk) {
|
|
367
369
|
return deserializeChunk(JSON.parse(chunk));
|
|
368
370
|
}
|
|
369
|
-
|
|
371
|
+
reader.drain(interpretChunk).then(() => deserializeChunk.abort(new Error("Server function stream ended unexpectedly.")), error => deserializeChunk.abort(error));
|
|
370
372
|
return interpretChunk(result.value);
|
|
371
373
|
}
|
|
372
374
|
return undefined;
|
|
@@ -394,6 +396,30 @@ const config = {
|
|
|
394
396
|
responseHandler: undefined,
|
|
395
397
|
serializeArgs: undefined
|
|
396
398
|
};
|
|
399
|
+
const CALL_OBSERVERS = new Set();
|
|
400
|
+
function notifyCallObservers(type, id, instance, value, meta) {
|
|
401
|
+
if (CALL_OBSERVERS.size === 0) return;
|
|
402
|
+
const field = type === "request" ? "request" : "response";
|
|
403
|
+
const time = performance.now();
|
|
404
|
+
for (const observer of new Set(CALL_OBSERVERS)) {
|
|
405
|
+
try {
|
|
406
|
+
observer({
|
|
407
|
+
type,
|
|
408
|
+
id,
|
|
409
|
+
instance,
|
|
410
|
+
[field]: value.clone(),
|
|
411
|
+
meta,
|
|
412
|
+
time
|
|
413
|
+
});
|
|
414
|
+
} catch (error) {
|
|
415
|
+
console.error(error);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
function observeServerFunctionCalls(observer) {
|
|
420
|
+
CALL_OBSERVERS.add(observer);
|
|
421
|
+
return () => CALL_OBSERVERS.delete(observer);
|
|
422
|
+
}
|
|
397
423
|
function serializeArguments(args) {
|
|
398
424
|
if (!config.serializeArgs) {
|
|
399
425
|
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.");
|
|
@@ -423,13 +449,18 @@ function provideRPC() {
|
|
|
423
449
|
decodeResponse
|
|
424
450
|
});
|
|
425
451
|
}
|
|
452
|
+
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;
|
|
455
|
+
return error;
|
|
456
|
+
}
|
|
426
457
|
async function createRequest(base, id, instance, options, meta) {
|
|
427
458
|
const headers = {
|
|
428
459
|
...options.headers,
|
|
429
460
|
[FUNCTION_HEADER]: id,
|
|
430
461
|
[INSTANCE_HEADER]: instance
|
|
431
462
|
};
|
|
432
|
-
if (getFlightDataConsumer() && (!options.method || options.method.toUpperCase() !== "GET")) {
|
|
463
|
+
if (getFlightDataConsumer() && !options.read && (!options.method || options.method.toUpperCase() !== "GET")) {
|
|
433
464
|
headers[SINGLE_FLIGHT_HEADER] = "true";
|
|
434
465
|
}
|
|
435
466
|
let init = {
|
|
@@ -443,7 +474,12 @@ async function createRequest(base, id, instance, options, meta) {
|
|
|
443
474
|
meta
|
|
444
475
|
})) || init;
|
|
445
476
|
}
|
|
446
|
-
return fetch(base, init);
|
|
477
|
+
if (CALL_OBSERVERS.size === 0) return fetch(base, init);
|
|
478
|
+
const request = new Request(new URL(base, globalThis.location?.href || "http://localhost"), init);
|
|
479
|
+
notifyCallObservers("request", id, instance, request, meta);
|
|
480
|
+
const response = await fetch(request);
|
|
481
|
+
notifyCallObservers("response", id, instance, response, meta);
|
|
482
|
+
return response;
|
|
447
483
|
}
|
|
448
484
|
async function initializeResponse(base, id, instance, options, args, meta) {
|
|
449
485
|
if (args.length === 0) {
|
|
@@ -511,6 +547,11 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
|
|
|
511
547
|
id,
|
|
512
548
|
meta
|
|
513
549
|
}) : undefined;
|
|
550
|
+
const controller = options.signal ? undefined : new AbortController();
|
|
551
|
+
if (controller) options = {
|
|
552
|
+
...options,
|
|
553
|
+
signal: controller.signal
|
|
554
|
+
};
|
|
514
555
|
const response = await initializeResponse(base, id, instance, options, args, meta);
|
|
515
556
|
if (handler) {
|
|
516
557
|
const handled = handler.handle(response, {
|
|
@@ -521,6 +562,7 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
|
|
|
521
562
|
});
|
|
522
563
|
if (handled !== undefined) return handled;
|
|
523
564
|
}
|
|
565
|
+
const failed = response.headers.has(ERROR_HEADER) || response.status >= 500;
|
|
524
566
|
if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
|
|
525
567
|
const consumer = getFlightDataConsumer();
|
|
526
568
|
if (consumer) {
|
|
@@ -528,8 +570,8 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
|
|
|
528
570
|
await consumer(payload.data, {
|
|
529
571
|
response
|
|
530
572
|
});
|
|
531
|
-
if (
|
|
532
|
-
throw payload.value;
|
|
573
|
+
if (failed && !response.headers.has("Location") && !response.headers.has(REVALIDATE_HEADER)) {
|
|
574
|
+
throw serverFunctionFailure(response, payload.value);
|
|
533
575
|
}
|
|
534
576
|
return payload.value;
|
|
535
577
|
}
|
|
@@ -538,8 +580,22 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
|
|
|
538
580
|
return response;
|
|
539
581
|
}
|
|
540
582
|
const result = await decodeResponse(response.clone());
|
|
541
|
-
if (
|
|
542
|
-
throw result;
|
|
583
|
+
if (failed) {
|
|
584
|
+
throw serverFunctionFailure(response, result);
|
|
585
|
+
}
|
|
586
|
+
if (controller && result?.[Symbol.asyncIterator]) {
|
|
587
|
+
return {
|
|
588
|
+
[Symbol.asyncIterator]() {
|
|
589
|
+
const it = result[Symbol.asyncIterator]();
|
|
590
|
+
return {
|
|
591
|
+
next: () => it.next(),
|
|
592
|
+
return: value => (controller.abort(), Promise.resolve({
|
|
593
|
+
done: true,
|
|
594
|
+
value
|
|
595
|
+
}))
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
};
|
|
543
599
|
}
|
|
544
600
|
return result;
|
|
545
601
|
}
|
|
@@ -609,6 +665,136 @@ function GET(fn) {
|
|
|
609
665
|
method: "GET"
|
|
610
666
|
});
|
|
611
667
|
}
|
|
668
|
+
function live(fn) {
|
|
669
|
+
if (!isServerFunction(fn)) {
|
|
670
|
+
throw new Error("live expects a server function reference");
|
|
671
|
+
}
|
|
672
|
+
const id = fn.id;
|
|
673
|
+
const metadata = {
|
|
674
|
+
...getServerFunctionMetadata(fn),
|
|
675
|
+
live: true
|
|
676
|
+
};
|
|
677
|
+
const wrapped = (...args) => {
|
|
678
|
+
const iterable = {
|
|
679
|
+
[LIVE_SOURCE]: true,
|
|
680
|
+
[Symbol.asyncIterator]() {
|
|
681
|
+
let it;
|
|
682
|
+
let connected = false;
|
|
683
|
+
let attempts = 0;
|
|
684
|
+
let stopped = false;
|
|
685
|
+
let ended = false;
|
|
686
|
+
let timer, wake;
|
|
687
|
+
const DONE = {
|
|
688
|
+
done: true,
|
|
689
|
+
value: undefined
|
|
690
|
+
};
|
|
691
|
+
const emit = (state, error) => {
|
|
692
|
+
try {
|
|
693
|
+
iterable.onstatus && iterable.onstatus(state, error);
|
|
694
|
+
} catch {}
|
|
695
|
+
};
|
|
696
|
+
const emitClosed = error => {
|
|
697
|
+
if (ended) return;
|
|
698
|
+
ended = true;
|
|
699
|
+
emit("closed", error);
|
|
700
|
+
};
|
|
701
|
+
const closeIt = value => {
|
|
702
|
+
const current = it;
|
|
703
|
+
it = undefined;
|
|
704
|
+
if (current) {
|
|
705
|
+
try {
|
|
706
|
+
const r = current.return && current.return(value);
|
|
707
|
+
if (r && typeof r.then === "function") r.then(undefined, () => {});
|
|
708
|
+
} catch {}
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
const callOnce = () => {
|
|
712
|
+
if (metadata.method === "GET") return fn(...args);
|
|
713
|
+
const handler = config.responseHandler;
|
|
714
|
+
if (handler && handler.intercept) {
|
|
715
|
+
const hit = handler.intercept({
|
|
716
|
+
id,
|
|
717
|
+
meta: metadata,
|
|
718
|
+
args
|
|
719
|
+
});
|
|
720
|
+
if (hit !== undefined) return hit;
|
|
721
|
+
}
|
|
722
|
+
return fetchServerFunction(fn.url, id, {
|
|
723
|
+
read: true
|
|
724
|
+
}, args, metadata, args);
|
|
725
|
+
};
|
|
726
|
+
const pull = async () => {
|
|
727
|
+
while (!stopped) {
|
|
728
|
+
try {
|
|
729
|
+
if (!it) {
|
|
730
|
+
const result = await callOnce();
|
|
731
|
+
connected = true;
|
|
732
|
+
it = result !== null && typeof result === "object" && result[Symbol.asyncIterator] ? result[Symbol.asyncIterator]() : async function* () {
|
|
733
|
+
yield result;
|
|
734
|
+
}();
|
|
735
|
+
if (stopped) {
|
|
736
|
+
closeIt();
|
|
737
|
+
return DONE;
|
|
738
|
+
}
|
|
739
|
+
emit("connected");
|
|
740
|
+
}
|
|
741
|
+
const r = await it.next();
|
|
742
|
+
if (r.done) {
|
|
743
|
+
emitClosed();
|
|
744
|
+
return DONE;
|
|
745
|
+
}
|
|
746
|
+
if (stopped) return DONE;
|
|
747
|
+
attempts = 0;
|
|
748
|
+
return r;
|
|
749
|
+
} catch (error) {
|
|
750
|
+
if (!connected) throw error;
|
|
751
|
+
if (error !== null && typeof error === "object" && typeof error.status === "number" && error.status >= 400 && error.status < 500) {
|
|
752
|
+
stopped = true;
|
|
753
|
+
emitClosed(error);
|
|
754
|
+
throw error;
|
|
755
|
+
}
|
|
756
|
+
it = undefined;
|
|
757
|
+
emit("reconnecting", error);
|
|
758
|
+
await new Promise(resolve => {
|
|
759
|
+
wake = resolve;
|
|
760
|
+
timer = setTimeout(resolve, Math.min(500 * 2 ** attempts++, 10000));
|
|
761
|
+
if (typeof addEventListener === "function") addEventListener("online", resolve, {
|
|
762
|
+
once: true
|
|
763
|
+
});
|
|
764
|
+
});
|
|
765
|
+
clearTimeout(timer);
|
|
766
|
+
if (typeof removeEventListener === "function") removeEventListener("online", wake);
|
|
767
|
+
timer = wake = undefined;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
return DONE;
|
|
771
|
+
};
|
|
772
|
+
return {
|
|
773
|
+
next: () => pull(),
|
|
774
|
+
return(value) {
|
|
775
|
+
stopped = true;
|
|
776
|
+
if (timer !== undefined) clearTimeout(timer);
|
|
777
|
+
if (wake) wake();
|
|
778
|
+
closeIt(value);
|
|
779
|
+
emitClosed();
|
|
780
|
+
return Promise.resolve({
|
|
781
|
+
done: true,
|
|
782
|
+
value
|
|
783
|
+
});
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
};
|
|
788
|
+
return iterable;
|
|
789
|
+
};
|
|
790
|
+
wrapped[SERVER_FUNCTION_METADATA] = metadata;
|
|
791
|
+
wrapped.id = id;
|
|
792
|
+
Object.defineProperty(wrapped, "url", {
|
|
793
|
+
get: () => fn.url,
|
|
794
|
+
configurable: true
|
|
795
|
+
});
|
|
796
|
+
return wrapped;
|
|
797
|
+
}
|
|
612
798
|
function registerServerReference() {
|
|
613
799
|
throw new Error("registerServerReference must not be called in the client build");
|
|
614
800
|
}
|
|
@@ -640,6 +826,8 @@ exports.getServerFunctionMetadata = getServerFunctionMetadata;
|
|
|
640
826
|
exports.getServerFunctionsCodec = getServerFunctionsCodec;
|
|
641
827
|
exports.hasFlashCookie = hasFlashCookie;
|
|
642
828
|
exports.isServerFunction = isServerFunction;
|
|
829
|
+
exports.live = live;
|
|
830
|
+
exports.observeServerFunctionCalls = observeServerFunctionCalls;
|
|
643
831
|
exports.registerServerReference = registerServerReference;
|
|
644
832
|
exports.serializeString = serializeString;
|
|
645
833
|
exports.subscribeFlightData = subscribeFlightData;
|