@solidjs/web 2.0.0-rc.4 → 2.0.0-rc.5
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/dist/dev.cjs +52 -7
- package/dist/dev.js +51 -8
- package/dist/server.cjs +162 -35
- package/dist/server.js +161 -36
- package/dist/web.cjs +32 -7
- package/dist/web.js +31 -8
- package/frames/dist/client.cjs +41 -8
- package/frames/dist/client.dev.cjs +41 -8
- package/frames/dist/client.dev.js +41 -8
- package/frames/dist/client.js +41 -8
- package/frames/dist/server.cjs +432 -44
- package/frames/dist/server.js +432 -44
- package/package.json +2 -2
- package/serialization/dist/decode.cjs +4 -2
- package/serialization/dist/decode.js +4 -2
- package/serialization/dist/serialization.cjs +12 -8
- package/serialization/dist/serialization.js +12 -8
- package/serialization/types/index.d.ts +7 -0
- package/serialization/types/serializer-decode.d.ts +14 -1
- package/serialization/types/serializer.d.ts +7 -0
- package/serialization/types-cjs/index.d.cts +7 -0
- package/serialization/types-cjs/serializer-decode.d.cts +14 -1
- package/serialization/types-cjs/serializer.d.cts +7 -0
- package/server-functions/dist/client.cjs +150 -33
- package/server-functions/dist/client.js +147 -34
- package/server-functions/dist/server.cjs +731 -118
- package/server-functions/dist/server.dev.cjs +751 -118
- package/server-functions/dist/server.dev.js +747 -119
- package/server-functions/dist/server.js +727 -119
- package/types/cookies.d.ts +6 -14
- package/types/frames/frame-client.d.ts +4 -0
- package/types/frames/serializer-decode.d.ts +14 -1
- package/types/frames/serializer.d.ts +7 -0
- package/types/jsx.d.ts +11 -16
- package/types/response.d.ts +11 -0
- package/types/serializer-decode.d.ts +14 -1
- package/types/serializer.d.ts +7 -0
- package/types/server-functions/client.d.ts +22 -2
- package/types/server-functions/flash.d.ts +9 -0
- package/types/server-functions/server.d.ts +58 -9
- package/types/server-functions/shared.d.ts +77 -14
- package/types/server-mock.d.ts +17 -2
- package/types/server.d.ts +16 -2
- package/types-cjs/cookies.d.cts +6 -14
- package/types-cjs/frames/frame-client.d.cts +4 -0
- package/types-cjs/frames/serializer-decode.d.cts +14 -1
- package/types-cjs/frames/serializer.d.cts +7 -0
- package/types-cjs/jsx.d.cts +11 -16
- package/types-cjs/response.d.cts +11 -0
- package/types-cjs/serializer-decode.d.cts +14 -1
- package/types-cjs/serializer.d.cts +7 -0
- package/types-cjs/server-functions/client.d.cts +22 -2
- package/types-cjs/server-functions/flash.d.cts +9 -0
- package/types-cjs/server-functions/server.d.cts +58 -9
- package/types-cjs/server-functions/shared.d.cts +77 -14
- package/types-cjs/server-mock.d.cts +17 -2
- package/types-cjs/server.d.cts +16 -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.5",
|
|
5
5
|
"author": "Ryan Carniato",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"homepage": "https://solidjs.com",
|
|
@@ -398,7 +398,7 @@
|
|
|
398
398
|
"seroval-plugins": "~1.5.4"
|
|
399
399
|
},
|
|
400
400
|
"peerDependencies": {
|
|
401
|
-
"solid-js": "^2.0.0-rc.
|
|
401
|
+
"solid-js": "^2.0.0-rc.5"
|
|
402
402
|
},
|
|
403
403
|
"devDependencies": {
|
|
404
404
|
"@codspeed/vitest-plugin": "^5.4.0",
|
|
@@ -85,12 +85,14 @@ const JSON_CODEC_DEPTH_LIMIT = 64;
|
|
|
85
85
|
function resolveCodecOptions({
|
|
86
86
|
plugins,
|
|
87
87
|
disabledFeatures,
|
|
88
|
-
depthLimit
|
|
88
|
+
depthLimit,
|
|
89
|
+
serializeErrorStacks
|
|
89
90
|
} = {}) {
|
|
90
91
|
return {
|
|
91
92
|
plugins: resolveSerializerPlugins(plugins),
|
|
92
93
|
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
|
|
93
|
-
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
|
|
94
|
+
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit,
|
|
95
|
+
serializeErrorStacks
|
|
94
96
|
};
|
|
95
97
|
}
|
|
96
98
|
function createJSONDeserializer(options) {
|
|
@@ -83,12 +83,14 @@ const JSON_CODEC_DEPTH_LIMIT = 64;
|
|
|
83
83
|
function resolveCodecOptions({
|
|
84
84
|
plugins,
|
|
85
85
|
disabledFeatures,
|
|
86
|
-
depthLimit
|
|
86
|
+
depthLimit,
|
|
87
|
+
serializeErrorStacks
|
|
87
88
|
} = {}) {
|
|
88
89
|
return {
|
|
89
90
|
plugins: resolveSerializerPlugins(plugins),
|
|
90
91
|
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
|
|
91
|
-
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
|
|
92
|
+
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit,
|
|
93
|
+
serializeErrorStacks
|
|
92
94
|
};
|
|
93
95
|
}
|
|
94
96
|
function createJSONDeserializer(options) {
|
|
@@ -85,12 +85,14 @@ const JSON_CODEC_DEPTH_LIMIT = 64;
|
|
|
85
85
|
function resolveCodecOptions({
|
|
86
86
|
plugins,
|
|
87
87
|
disabledFeatures,
|
|
88
|
-
depthLimit
|
|
88
|
+
depthLimit,
|
|
89
|
+
serializeErrorStacks
|
|
89
90
|
} = {}) {
|
|
90
91
|
return {
|
|
91
92
|
plugins: resolveSerializerPlugins(plugins),
|
|
92
93
|
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
|
|
93
|
-
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
|
|
94
|
+
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit,
|
|
95
|
+
serializeErrorStacks
|
|
94
96
|
};
|
|
95
97
|
}
|
|
96
98
|
function createJSONDeserializer(options) {
|
|
@@ -133,7 +135,7 @@ function createJSONDataTable(options) {
|
|
|
133
135
|
}
|
|
134
136
|
|
|
135
137
|
const DEFAULT_DISABLED_FEATURES = seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
|
|
136
|
-
const serializeOnlyDisabledFeatures =
|
|
138
|
+
const serializeOnlyDisabledFeatures = serializeErrorStacks => (serializeErrorStacks === undefined ? process.env.NODE_ENV === "development" : serializeErrorStacks) ? 0 : seroval.Feature.ErrorPrototypeStack;
|
|
137
139
|
const HYDRATION_GLOBAL = "_$HY.r";
|
|
138
140
|
const createPlugin = seroval.createPlugin;
|
|
139
141
|
const OpaqueReference = seroval.OpaqueReference;
|
|
@@ -141,7 +143,7 @@ function createSerializer(options) {
|
|
|
141
143
|
return new seroval.Serializer({
|
|
142
144
|
...options,
|
|
143
145
|
plugins: resolveSerializerPlugins(options.plugins),
|
|
144
|
-
disabledFeatures: (options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures) | serializeOnlyDisabledFeatures()
|
|
146
|
+
disabledFeatures: (options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures) | serializeOnlyDisabledFeatures(options.serializeErrorStacks)
|
|
145
147
|
});
|
|
146
148
|
}
|
|
147
149
|
function createHydrationSerializer({
|
|
@@ -175,7 +177,7 @@ function serializeJSON(value, {
|
|
|
175
177
|
onDone,
|
|
176
178
|
onError,
|
|
177
179
|
...resolved,
|
|
178
|
-
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
|
|
180
|
+
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures(resolved.serializeErrorStacks)
|
|
179
181
|
});
|
|
180
182
|
}
|
|
181
183
|
function createJSONSerializer({
|
|
@@ -184,12 +186,14 @@ function createJSONSerializer({
|
|
|
184
186
|
onError,
|
|
185
187
|
plugins,
|
|
186
188
|
disabledFeatures,
|
|
187
|
-
depthLimit
|
|
189
|
+
depthLimit,
|
|
190
|
+
serializeErrorStacks
|
|
188
191
|
}) {
|
|
189
192
|
const resolved = resolveCodecOptions({
|
|
190
193
|
plugins,
|
|
191
194
|
disabledFeatures,
|
|
192
|
-
depthLimit
|
|
195
|
+
depthLimit,
|
|
196
|
+
serializeErrorStacks
|
|
193
197
|
});
|
|
194
198
|
const refs = new Map();
|
|
195
199
|
const cancels = new Set();
|
|
@@ -211,7 +215,7 @@ function createJSONSerializer({
|
|
|
211
215
|
const stream = seroval.toCrossJSONStream(value, {
|
|
212
216
|
refs,
|
|
213
217
|
plugins: resolved.plugins,
|
|
214
|
-
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures(),
|
|
218
|
+
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures(resolved.serializeErrorStacks),
|
|
215
219
|
onParse(node, initial) {
|
|
216
220
|
onData({
|
|
217
221
|
key,
|
|
@@ -83,12 +83,14 @@ const JSON_CODEC_DEPTH_LIMIT = 64;
|
|
|
83
83
|
function resolveCodecOptions({
|
|
84
84
|
plugins,
|
|
85
85
|
disabledFeatures,
|
|
86
|
-
depthLimit
|
|
86
|
+
depthLimit,
|
|
87
|
+
serializeErrorStacks
|
|
87
88
|
} = {}) {
|
|
88
89
|
return {
|
|
89
90
|
plugins: resolveSerializerPlugins(plugins),
|
|
90
91
|
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
|
|
91
|
-
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
|
|
92
|
+
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit,
|
|
93
|
+
serializeErrorStacks
|
|
92
94
|
};
|
|
93
95
|
}
|
|
94
96
|
function createJSONDeserializer(options) {
|
|
@@ -131,7 +133,7 @@ function createJSONDataTable(options) {
|
|
|
131
133
|
}
|
|
132
134
|
|
|
133
135
|
const DEFAULT_DISABLED_FEATURES = Feature.AggregateError | Feature.BigIntTypedArray;
|
|
134
|
-
const serializeOnlyDisabledFeatures =
|
|
136
|
+
const serializeOnlyDisabledFeatures = serializeErrorStacks => (serializeErrorStacks === undefined ? process.env.NODE_ENV === "development" : serializeErrorStacks) ? 0 : Feature.ErrorPrototypeStack;
|
|
135
137
|
const HYDRATION_GLOBAL = "_$HY.r";
|
|
136
138
|
const createPlugin = createPlugin$1;
|
|
137
139
|
const OpaqueReference = OpaqueReference$1;
|
|
@@ -139,7 +141,7 @@ function createSerializer(options) {
|
|
|
139
141
|
return new Serializer({
|
|
140
142
|
...options,
|
|
141
143
|
plugins: resolveSerializerPlugins(options.plugins),
|
|
142
|
-
disabledFeatures: (options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures) | serializeOnlyDisabledFeatures()
|
|
144
|
+
disabledFeatures: (options.disabledFeatures === undefined ? DEFAULT_DISABLED_FEATURES : options.disabledFeatures) | serializeOnlyDisabledFeatures(options.serializeErrorStacks)
|
|
143
145
|
});
|
|
144
146
|
}
|
|
145
147
|
function createHydrationSerializer({
|
|
@@ -173,7 +175,7 @@ function serializeJSON(value, {
|
|
|
173
175
|
onDone,
|
|
174
176
|
onError,
|
|
175
177
|
...resolved,
|
|
176
|
-
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
|
|
178
|
+
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures(resolved.serializeErrorStacks)
|
|
177
179
|
});
|
|
178
180
|
}
|
|
179
181
|
function createJSONSerializer({
|
|
@@ -182,12 +184,14 @@ function createJSONSerializer({
|
|
|
182
184
|
onError,
|
|
183
185
|
plugins,
|
|
184
186
|
disabledFeatures,
|
|
185
|
-
depthLimit
|
|
187
|
+
depthLimit,
|
|
188
|
+
serializeErrorStacks
|
|
186
189
|
}) {
|
|
187
190
|
const resolved = resolveCodecOptions({
|
|
188
191
|
plugins,
|
|
189
192
|
disabledFeatures,
|
|
190
|
-
depthLimit
|
|
193
|
+
depthLimit,
|
|
194
|
+
serializeErrorStacks
|
|
191
195
|
});
|
|
192
196
|
const refs = new Map();
|
|
193
197
|
const cancels = new Set();
|
|
@@ -209,7 +213,7 @@ function createJSONSerializer({
|
|
|
209
213
|
const stream = toCrossJSONStream(value, {
|
|
210
214
|
refs,
|
|
211
215
|
plugins: resolved.plugins,
|
|
212
|
-
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures(),
|
|
216
|
+
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures(resolved.serializeErrorStacks),
|
|
213
217
|
onParse(node, initial) {
|
|
214
218
|
onData({
|
|
215
219
|
key,
|
|
@@ -19,6 +19,13 @@ export interface WebSerializerOptions {
|
|
|
19
19
|
* any override — serialized stacks leak server paths to the client.
|
|
20
20
|
*/
|
|
21
21
|
disabledFeatures?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Whether serialized `Error`s carry their `.stack`. Defaults to
|
|
24
|
+
* `NODE_ENV === "development"`; set `false` to pin production disclosure
|
|
25
|
+
* to the deployment rather than the ambient variable (#3152 — see
|
|
26
|
+
* `JSONCodecOptions.serializeErrorStacks`).
|
|
27
|
+
*/
|
|
28
|
+
serializeErrorStacks?: boolean;
|
|
22
29
|
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. */
|
|
23
30
|
plugins?: SerializerPlugin[];
|
|
24
31
|
/** Receives each emitted script chunk. */
|
|
@@ -105,6 +105,18 @@ export interface JSONCodecOptions {
|
|
|
105
105
|
disabledFeatures?: number;
|
|
106
106
|
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
|
|
107
107
|
depthLimit?: number;
|
|
108
|
+
/**
|
|
109
|
+
* Whether serialized `Error`s carry their `.stack` — server file paths,
|
|
110
|
+
* internal function names, the shape of the deployment — to the peer.
|
|
111
|
+
* Defaults to `NODE_ENV === "development"`, but that signal describes the
|
|
112
|
+
* PROCESS, not the artifact: a production build running with
|
|
113
|
+
* `NODE_ENV=development` (a base image, a stray dotenv) ships stacks to
|
|
114
|
+
* the wire — including for errors marked safe with `markSafeError`, whose
|
|
115
|
+
* stacks traverse application code (#3152). Set `false` to pin production
|
|
116
|
+
* disclosure regardless of the ambient variable. Encode-side only; the
|
|
117
|
+
* decode side is always permissive, so it need not match peers.
|
|
118
|
+
*/
|
|
119
|
+
serializeErrorStacks?: boolean;
|
|
108
120
|
}
|
|
109
121
|
/**
|
|
110
122
|
* A resident, response-scoped decode table over the keyed JSON codec: apply
|
|
@@ -142,10 +154,11 @@ export declare const DEFAULT_WEB_PLUGINS: any; /**
|
|
|
142
154
|
* Integration-facing; may change (see the entry banner).
|
|
143
155
|
*/
|
|
144
156
|
export declare function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
|
|
145
|
-
export declare function resolveCodecOptions({ plugins, disabledFeatures, depthLimit }?: JSONCodecOptions): {
|
|
157
|
+
export declare function resolveCodecOptions({ plugins, disabledFeatures, depthLimit, serializeErrorStacks }?: JSONCodecOptions): {
|
|
146
158
|
plugins: SerializerPlugin<any, any>[];
|
|
147
159
|
disabledFeatures: any;
|
|
148
160
|
depthLimit: number;
|
|
161
|
+
serializeErrorStacks: boolean;
|
|
149
162
|
}; /**
|
|
150
163
|
* Creates the decoding counterpart of `serializeJSON`. Cross-references
|
|
151
164
|
* between chunks resolve through state shared across calls, so all chunks
|
|
@@ -19,6 +19,13 @@ export interface WebSerializerOptions {
|
|
|
19
19
|
* any override — serialized stacks leak server paths to the client.
|
|
20
20
|
*/
|
|
21
21
|
disabledFeatures?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Whether serialized `Error`s carry their `.stack`. Defaults to
|
|
24
|
+
* `NODE_ENV === "development"`; set `false` to pin production disclosure
|
|
25
|
+
* to the deployment rather than the ambient variable (#3152 — see
|
|
26
|
+
* `JSONCodecOptions.serializeErrorStacks`).
|
|
27
|
+
*/
|
|
28
|
+
serializeErrorStacks?: boolean;
|
|
22
29
|
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. */
|
|
23
30
|
plugins?: SerializerPlugin[];
|
|
24
31
|
/** Receives each emitted script chunk. */
|
|
@@ -19,6 +19,13 @@ export interface WebSerializerOptions {
|
|
|
19
19
|
* any override — serialized stacks leak server paths to the client.
|
|
20
20
|
*/
|
|
21
21
|
disabledFeatures?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Whether serialized `Error`s carry their `.stack`. Defaults to
|
|
24
|
+
* `NODE_ENV === "development"`; set `false` to pin production disclosure
|
|
25
|
+
* to the deployment rather than the ambient variable (#3152 — see
|
|
26
|
+
* `JSONCodecOptions.serializeErrorStacks`).
|
|
27
|
+
*/
|
|
28
|
+
serializeErrorStacks?: boolean;
|
|
22
29
|
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. */
|
|
23
30
|
plugins?: SerializerPlugin[];
|
|
24
31
|
/** Receives each emitted script chunk. */
|
|
@@ -105,6 +105,18 @@ export interface JSONCodecOptions {
|
|
|
105
105
|
disabledFeatures?: number;
|
|
106
106
|
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
|
|
107
107
|
depthLimit?: number;
|
|
108
|
+
/**
|
|
109
|
+
* Whether serialized `Error`s carry their `.stack` — server file paths,
|
|
110
|
+
* internal function names, the shape of the deployment — to the peer.
|
|
111
|
+
* Defaults to `NODE_ENV === "development"`, but that signal describes the
|
|
112
|
+
* PROCESS, not the artifact: a production build running with
|
|
113
|
+
* `NODE_ENV=development` (a base image, a stray dotenv) ships stacks to
|
|
114
|
+
* the wire — including for errors marked safe with `markSafeError`, whose
|
|
115
|
+
* stacks traverse application code (#3152). Set `false` to pin production
|
|
116
|
+
* disclosure regardless of the ambient variable. Encode-side only; the
|
|
117
|
+
* decode side is always permissive, so it need not match peers.
|
|
118
|
+
*/
|
|
119
|
+
serializeErrorStacks?: boolean;
|
|
108
120
|
}
|
|
109
121
|
/**
|
|
110
122
|
* A resident, response-scoped decode table over the keyed JSON codec: apply
|
|
@@ -142,10 +154,11 @@ export declare const DEFAULT_WEB_PLUGINS: any; /**
|
|
|
142
154
|
* Integration-facing; may change (see the entry banner).
|
|
143
155
|
*/
|
|
144
156
|
export declare function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
|
|
145
|
-
export declare function resolveCodecOptions({ plugins, disabledFeatures, depthLimit }?: JSONCodecOptions): {
|
|
157
|
+
export declare function resolveCodecOptions({ plugins, disabledFeatures, depthLimit, serializeErrorStacks }?: JSONCodecOptions): {
|
|
146
158
|
plugins: SerializerPlugin<any, any>[];
|
|
147
159
|
disabledFeatures: any;
|
|
148
160
|
depthLimit: number;
|
|
161
|
+
serializeErrorStacks: boolean;
|
|
149
162
|
}; /**
|
|
150
163
|
* Creates the decoding counterpart of `serializeJSON`. Cross-references
|
|
151
164
|
* between chunks resolve through state shared across calls, so all chunks
|
|
@@ -19,6 +19,13 @@ export interface WebSerializerOptions {
|
|
|
19
19
|
* any override — serialized stacks leak server paths to the client.
|
|
20
20
|
*/
|
|
21
21
|
disabledFeatures?: number;
|
|
22
|
+
/**
|
|
23
|
+
* Whether serialized `Error`s carry their `.stack`. Defaults to
|
|
24
|
+
* `NODE_ENV === "development"`; set `false` to pin production disclosure
|
|
25
|
+
* to the deployment rather than the ambient variable (#3152 — see
|
|
26
|
+
* `JSONCodecOptions.serializeErrorStacks`).
|
|
27
|
+
*/
|
|
28
|
+
serializeErrorStacks?: boolean;
|
|
22
29
|
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. */
|
|
23
30
|
plugins?: SerializerPlugin[];
|
|
24
31
|
/** Receives each emitted script chunk. */
|
|
@@ -68,17 +68,30 @@ function configureServerFunctionsCodec(codec) {
|
|
|
68
68
|
function getServerFunctionsCodec() {
|
|
69
69
|
return codecConfig.codec;
|
|
70
70
|
}
|
|
71
|
+
const UNNAMED_FLIGHT_SOURCE = "true";
|
|
71
72
|
const flightConfig = {
|
|
72
|
-
|
|
73
|
+
consumers: new Map()
|
|
73
74
|
};
|
|
74
|
-
function
|
|
75
|
-
|
|
75
|
+
function assertFlightSource(source) {
|
|
76
|
+
if (source === UNNAMED_FLIGHT_SOURCE || source === "" || source.includes(",")) {
|
|
77
|
+
throw new TypeError(`Invalid flight data source id "${source}": ids ride the ` + `${SINGLE_FLIGHT_HEADER} header as a comma-separated list, and "true" is ` + `reserved for the unnamed registration (the bare consumer/hook signatures).`);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
function subscribeFlightData(sourceOrConsumer, maybeConsumer) {
|
|
81
|
+
const named = typeof sourceOrConsumer === "string";
|
|
82
|
+
if (named) assertFlightSource(sourceOrConsumer);
|
|
83
|
+
const source = named ? sourceOrConsumer : UNNAMED_FLIGHT_SOURCE;
|
|
84
|
+
const consumer = named ? maybeConsumer : sourceOrConsumer;
|
|
85
|
+
flightConfig.consumers.set(source, consumer);
|
|
76
86
|
return () => {
|
|
77
|
-
if (flightConfig.
|
|
87
|
+
if (flightConfig.consumers.get(source) === consumer) flightConfig.consumers.delete(source);
|
|
78
88
|
};
|
|
79
89
|
}
|
|
80
|
-
function getFlightDataConsumer() {
|
|
81
|
-
return flightConfig.
|
|
90
|
+
function getFlightDataConsumer(source) {
|
|
91
|
+
return flightConfig.consumers.get(source === undefined ? UNNAMED_FLIGHT_SOURCE : source);
|
|
92
|
+
}
|
|
93
|
+
function getFlightDataSourceIds() {
|
|
94
|
+
return [...flightConfig.consumers.keys()];
|
|
82
95
|
}
|
|
83
96
|
function frameAddress(id, args) {
|
|
84
97
|
return args && args.length ? id + ":" + hashArguments(args) : id;
|
|
@@ -128,15 +141,27 @@ function serverFunctionAddress(endpoint, id) {
|
|
|
128
141
|
const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
|
|
129
142
|
return `${mount}/${encodeURIComponent(id)}`;
|
|
130
143
|
}
|
|
144
|
+
function serverFunctionDataAddress(endpoint, id) {
|
|
145
|
+
const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
|
|
146
|
+
return `${mount}/data/${encodeURIComponent(id)}`;
|
|
147
|
+
}
|
|
131
148
|
function parseServerFunctionAddress(pathname, endpoint) {
|
|
132
149
|
const mount = endpoint.endsWith("/") ? endpoint.slice(0, -1) : endpoint;
|
|
133
150
|
if (!pathname.startsWith(mount)) return null;
|
|
134
151
|
const rest = pathname.slice(mount.length);
|
|
135
152
|
if (!rest.startsWith("/")) return null;
|
|
136
|
-
|
|
153
|
+
let segment = rest.slice(1);
|
|
154
|
+
let data = false;
|
|
155
|
+
if (segment.startsWith("data/")) {
|
|
156
|
+
segment = segment.slice(5);
|
|
157
|
+
data = true;
|
|
158
|
+
}
|
|
137
159
|
if (!segment || segment.includes("/")) return null;
|
|
138
160
|
try {
|
|
139
|
-
return
|
|
161
|
+
return {
|
|
162
|
+
id: decodeURIComponent(segment),
|
|
163
|
+
data
|
|
164
|
+
};
|
|
140
165
|
} catch {
|
|
141
166
|
return null;
|
|
142
167
|
}
|
|
@@ -168,6 +193,28 @@ function decodeErrorHeaderValue(value) {
|
|
|
168
193
|
}
|
|
169
194
|
const INSTANCE_HEADER = "X-Server-Function-Instance";
|
|
170
195
|
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
|
|
196
|
+
const UNKNOWN_HEADER = "X-Server-Function-Unknown";
|
|
197
|
+
const REDIRECT_HEADER = "X-Server-Function-Redirect";
|
|
198
|
+
function decodeRedirectHeaderValue(value) {
|
|
199
|
+
if (typeof value !== "string") return undefined;
|
|
200
|
+
const at = value.indexOf(" ");
|
|
201
|
+
if (at < 0) return undefined;
|
|
202
|
+
const status = Number(value.slice(0, at));
|
|
203
|
+
const url = value.slice(at + 1);
|
|
204
|
+
if (!Number.isInteger(status) || !url) return undefined;
|
|
205
|
+
if (status !== 301 && status !== 302 && status !== 303 && status !== 307 && status !== 308) return undefined;
|
|
206
|
+
let parsed;
|
|
207
|
+
try {
|
|
208
|
+
parsed = new URL(url);
|
|
209
|
+
} catch {
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|
|
212
|
+
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") return undefined;
|
|
213
|
+
return {
|
|
214
|
+
status,
|
|
215
|
+
url
|
|
216
|
+
};
|
|
217
|
+
}
|
|
171
218
|
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
|
|
172
219
|
const FILE_FORM_KEY = "__server_function_file__";
|
|
173
220
|
const BodyFormat = {
|
|
@@ -210,7 +257,11 @@ function isJSONSafe(value) {
|
|
|
210
257
|
const proto = Object.getPrototypeOf(v);
|
|
211
258
|
if (proto !== Object.prototype && proto !== null) return false;
|
|
212
259
|
if (Symbol.asyncIterator in v || Symbol.iterator in v) return false;
|
|
213
|
-
for (const k in v)
|
|
260
|
+
for (const k in v) {
|
|
261
|
+
const descriptor = Object.getOwnPropertyDescriptor(v, k);
|
|
262
|
+
if (descriptor === undefined || !("value" in descriptor)) return false;
|
|
263
|
+
stack.push(descriptor.value);
|
|
264
|
+
}
|
|
214
265
|
}
|
|
215
266
|
}
|
|
216
267
|
return true;
|
|
@@ -319,18 +370,34 @@ function createChunk(data) {
|
|
|
319
370
|
class ChunkReader {
|
|
320
371
|
constructor(stream) {
|
|
321
372
|
this.reader = stream.getReader();
|
|
322
|
-
this.
|
|
373
|
+
this.store = new Uint8Array(0);
|
|
374
|
+
this.buffer = this.store;
|
|
323
375
|
this.done = false;
|
|
324
376
|
}
|
|
325
377
|
async readChunk() {
|
|
326
378
|
const chunk = await this.reader.read();
|
|
327
|
-
if (
|
|
328
|
-
const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
|
|
329
|
-
newBuffer.set(this.buffer);
|
|
330
|
-
newBuffer.set(chunk.value, this.buffer.length);
|
|
331
|
-
this.buffer = newBuffer;
|
|
332
|
-
} else {
|
|
379
|
+
if (chunk.done) {
|
|
333
380
|
this.done = true;
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const incoming = chunk.value;
|
|
384
|
+
const store = this.store;
|
|
385
|
+
const start = this.buffer.byteOffset;
|
|
386
|
+
const end = start + this.buffer.length;
|
|
387
|
+
const needed = this.buffer.length + incoming.length;
|
|
388
|
+
if (end + incoming.length <= store.length) {
|
|
389
|
+
store.set(incoming, end);
|
|
390
|
+
this.buffer = store.subarray(start, end + incoming.length);
|
|
391
|
+
} else if (needed <= store.length) {
|
|
392
|
+
store.copyWithin(0, start, end);
|
|
393
|
+
store.set(incoming, this.buffer.length);
|
|
394
|
+
this.buffer = store.subarray(0, needed);
|
|
395
|
+
} else {
|
|
396
|
+
const grown = new Uint8Array(Math.max(needed, store.length * 2));
|
|
397
|
+
grown.set(this.buffer);
|
|
398
|
+
grown.set(incoming, this.buffer.length);
|
|
399
|
+
this.store = grown;
|
|
400
|
+
this.buffer = grown.subarray(0, needed);
|
|
334
401
|
}
|
|
335
402
|
}
|
|
336
403
|
async next() {
|
|
@@ -372,6 +439,18 @@ class ChunkReader {
|
|
|
372
439
|
}
|
|
373
440
|
}
|
|
374
441
|
}
|
|
442
|
+
const ERROR_TRAILER_PREFIX = "!";
|
|
443
|
+
function errorFromTrailer(payload) {
|
|
444
|
+
let shape;
|
|
445
|
+
try {
|
|
446
|
+
shape = JSON.parse(payload.slice(1));
|
|
447
|
+
} catch {
|
|
448
|
+
shape = null;
|
|
449
|
+
}
|
|
450
|
+
const error = new Error(shape && typeof shape.message === "string" ? shape.message : "Server function result could not be delivered.");
|
|
451
|
+
if (shape && typeof shape.name === "string") error.name = shape.name;
|
|
452
|
+
return error;
|
|
453
|
+
}
|
|
375
454
|
function serializeStream(value, codecOptions) {
|
|
376
455
|
return new ReadableStream({
|
|
377
456
|
async start(controller) {
|
|
@@ -404,11 +483,17 @@ async function deserializeStream(source, codecOptions) {
|
|
|
404
483
|
const reader = new ChunkReader(source.body);
|
|
405
484
|
const result = await reader.next();
|
|
406
485
|
if (!result.done) {
|
|
486
|
+
if (result.value.startsWith(ERROR_TRAILER_PREFIX)) {
|
|
487
|
+
throw errorFromTrailer(result.value);
|
|
488
|
+
}
|
|
407
489
|
const {
|
|
408
490
|
createJSONDeserializer
|
|
409
491
|
} = await import('@solidjs/web/serialization/decode');
|
|
410
492
|
const deserializeChunk = createJSONDeserializer(codecOptions);
|
|
411
493
|
function interpretChunk(chunk) {
|
|
494
|
+
if (chunk.startsWith(ERROR_TRAILER_PREFIX)) {
|
|
495
|
+
throw errorFromTrailer(chunk);
|
|
496
|
+
}
|
|
412
497
|
return deserializeChunk(JSON.parse(chunk));
|
|
413
498
|
}
|
|
414
499
|
reader.drain(interpretChunk).then(() => deserializeChunk.abort(new Error("Server function stream ended unexpectedly.")), error => deserializeChunk.abort(error));
|
|
@@ -473,7 +558,8 @@ function serverFunctionUrl(id, boundArgs) {
|
|
|
473
558
|
return `${address}?args=${encodeURIComponent(JSON.stringify(boundArgs))}`;
|
|
474
559
|
}
|
|
475
560
|
function parseServerFunctionUrl(url) {
|
|
476
|
-
|
|
561
|
+
const parsed = parseServerFunctionAddress(new URL(url, globalThis.location?.href || "http://localhost").pathname, config.endpoint);
|
|
562
|
+
return parsed && parsed.id;
|
|
477
563
|
}
|
|
478
564
|
function serializeArguments(args) {
|
|
479
565
|
if (!config.serializeArgs) {
|
|
@@ -507,18 +593,41 @@ function provideRPC() {
|
|
|
507
593
|
decodeResponse
|
|
508
594
|
});
|
|
509
595
|
}
|
|
596
|
+
function dataAddressFor(base) {
|
|
597
|
+
const splitAt = base.search(/[?#]/);
|
|
598
|
+
const path = splitAt < 0 ? base : base.slice(0, splitAt);
|
|
599
|
+
const rest = splitAt < 0 ? "" : base.slice(splitAt);
|
|
600
|
+
const slash = path.lastIndexOf("/");
|
|
601
|
+
if (path.endsWith("/data/", slash + 1)) return base;
|
|
602
|
+
return `${path.slice(0, slash + 1)}data/${path.slice(slash + 1)}${rest}`;
|
|
603
|
+
}
|
|
510
604
|
function serverFunctionFailure(response, value) {
|
|
511
|
-
const
|
|
512
|
-
|
|
605
|
+
const unknown = response.headers.get(UNKNOWN_HEADER) !== null;
|
|
606
|
+
const error = value ?? new Error(unknown ? "Server function is not part of the deployment that answered (version skew or removed function)" : `Server function call failed with status ${response.status}`);
|
|
607
|
+
if (error instanceof Error && !("status" in error)) {
|
|
608
|
+
error.status = response.status;
|
|
609
|
+
const retryAfter = parseRetryAfter(response.headers.get("Retry-After"));
|
|
610
|
+
if (retryAfter !== undefined) error.retryAfter = retryAfter;
|
|
611
|
+
}
|
|
612
|
+
if (unknown && error instanceof Error) error.unknownFunction = true;
|
|
513
613
|
return error;
|
|
514
614
|
}
|
|
615
|
+
function parseRetryAfter(header) {
|
|
616
|
+
if (!header) return undefined;
|
|
617
|
+
const trimmed = header.trim();
|
|
618
|
+
if (/^\d+$/.test(trimmed)) return Number(trimmed);
|
|
619
|
+
const date = Date.parse(trimmed);
|
|
620
|
+
if (!Number.isNaN(date)) return Math.max(0, Math.ceil((date - Date.now()) / 1000));
|
|
621
|
+
return undefined;
|
|
622
|
+
}
|
|
515
623
|
async function createRequest(base, id, instance, options, meta) {
|
|
516
624
|
const headers = {
|
|
517
625
|
...options.headers,
|
|
518
626
|
[INSTANCE_HEADER]: instance
|
|
519
627
|
};
|
|
520
|
-
|
|
521
|
-
|
|
628
|
+
const flightSources = getFlightDataSourceIds();
|
|
629
|
+
if (flightSources.length > 0 && !options.read && (!options.method || options.method.toUpperCase() !== "GET")) {
|
|
630
|
+
headers[SINGLE_FLIGHT_HEADER] = flightSources.join(",");
|
|
522
631
|
}
|
|
523
632
|
let init = {
|
|
524
633
|
method: "POST",
|
|
@@ -626,21 +735,24 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
|
|
|
626
735
|
if (response.status >= 400 && !response.headers.has(BODY_FORMAT_HEADER)) {
|
|
627
736
|
throw serverFunctionFailure(response, undefined);
|
|
628
737
|
}
|
|
629
|
-
const failed = response.headers.has(ERROR_HEADER)
|
|
738
|
+
const failed = response.headers.has(ERROR_HEADER);
|
|
630
739
|
if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
|
|
631
|
-
const
|
|
632
|
-
|
|
740
|
+
const folded = response.headers.get(SINGLE_FLIGHT_HEADER).split(",");
|
|
741
|
+
const consumers = folded.map(source => [source, getFlightDataConsumer(source)]).filter(([, consumer]) => consumer);
|
|
742
|
+
if (consumers.length > 0) {
|
|
633
743
|
const payload = await decodeResponse(response);
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
744
|
+
for (const [source, consumer] of consumers) {
|
|
745
|
+
await consumer(payload.data[source], {
|
|
746
|
+
response
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
if (failed && !response.headers.has(REDIRECT_HEADER) && !response.headers.has(REVALIDATE_HEADER)) {
|
|
638
750
|
throw serverFunctionFailure(response, payload.value);
|
|
639
751
|
}
|
|
640
752
|
return payload.value;
|
|
641
753
|
}
|
|
642
754
|
}
|
|
643
|
-
if (response.headers.has(
|
|
755
|
+
if (response.headers.has(REDIRECT_HEADER) || response.headers.has(REVALIDATE_HEADER) || response.headers.has(SINGLE_FLIGHT_HEADER) || response.status >= 300 && response.status < 400 && response.status !== 304) {
|
|
644
756
|
return response;
|
|
645
757
|
}
|
|
646
758
|
const result = await decodeResponse(response.clone());
|
|
@@ -678,7 +790,7 @@ function createServerReference(id, name, base) {
|
|
|
678
790
|
});
|
|
679
791
|
if (hit !== undefined) return hit;
|
|
680
792
|
}
|
|
681
|
-
return fetchServerFunction(base
|
|
793
|
+
return fetchServerFunction(base ? dataAddressFor(base) : serverFunctionDataAddress(config.endpoint, id), id, invokeOptions ? {
|
|
682
794
|
...invokeOptions
|
|
683
795
|
} : {}, args, metadata);
|
|
684
796
|
};
|
|
@@ -715,7 +827,7 @@ function GET(fn) {
|
|
|
715
827
|
if (hit !== undefined) return hit;
|
|
716
828
|
}
|
|
717
829
|
const opts = invokeOptions || {};
|
|
718
|
-
const address =
|
|
830
|
+
const address = serverFunctionDataAddress(config.endpoint, id);
|
|
719
831
|
if (!args.length) {
|
|
720
832
|
return fetchServerFunction(address, id, {
|
|
721
833
|
...opts,
|
|
@@ -845,7 +957,7 @@ function live(fn) {
|
|
|
845
957
|
emitClosed(error);
|
|
846
958
|
throw error;
|
|
847
959
|
}
|
|
848
|
-
if (error !== null && typeof error === "object" && typeof error.status === "number" && error.status >= 400 && error.status < 500) {
|
|
960
|
+
if (error !== null && typeof error === "object" && typeof error.status === "number" && error.status >= 400 && error.status < 500 && error.status !== 408 && error.status !== 425 && error.status !== 429 && typeof error.retryAfter !== "number") {
|
|
849
961
|
stopped = true;
|
|
850
962
|
emitClosed(error);
|
|
851
963
|
throw error;
|
|
@@ -854,7 +966,8 @@ function live(fn) {
|
|
|
854
966
|
emit("reconnecting", error);
|
|
855
967
|
await new Promise(resolve => {
|
|
856
968
|
wake = resolve;
|
|
857
|
-
|
|
969
|
+
const named = error !== null && typeof error === "object" && typeof error.retryAfter === "number" ? Math.min(error.retryAfter * 1000, 60000) : undefined;
|
|
970
|
+
timer = setTimeout(resolve, named ?? Math.min(500 * 2 ** attempts++, 10000));
|
|
858
971
|
if (typeof addEventListener === "function") addEventListener("online", resolve, {
|
|
859
972
|
once: true
|
|
860
973
|
});
|
|
@@ -911,20 +1024,24 @@ exports.ERROR_HEADER = ERROR_HEADER;
|
|
|
911
1024
|
exports.FLASH_COOKIE = FLASH_COOKIE;
|
|
912
1025
|
exports.GET = GET;
|
|
913
1026
|
exports.INSTANCE_HEADER = INSTANCE_HEADER;
|
|
1027
|
+
exports.REDIRECT_HEADER = REDIRECT_HEADER;
|
|
914
1028
|
exports.REVALIDATE_HEADER = REVALIDATE_HEADER;
|
|
915
1029
|
exports.SERVER_FUNCTION_INVOKE = SERVER_FUNCTION_INVOKE;
|
|
916
1030
|
exports.SINGLE_FLIGHT_HEADER = SINGLE_FLIGHT_HEADER;
|
|
1031
|
+
exports.UNKNOWN_HEADER = UNKNOWN_HEADER;
|
|
917
1032
|
exports.clearFlashCookie = clearFlashCookie;
|
|
918
1033
|
exports.configureServerFunctionsClient = configureServerFunctionsClient;
|
|
919
1034
|
exports.createChunk = createChunk;
|
|
920
1035
|
exports.createServerReference = createServerReference;
|
|
921
1036
|
exports.decodeErrorHeaderValue = decodeErrorHeaderValue;
|
|
1037
|
+
exports.decodeRedirectHeaderValue = decodeRedirectHeaderValue;
|
|
922
1038
|
exports.decodeResponse = decodeResponse;
|
|
923
1039
|
exports.decodeResponsePayload = decodeResponsePayload;
|
|
924
1040
|
exports.deserializeStream = deserializeStream;
|
|
925
1041
|
exports.encodeErrorHeaderValue = encodeErrorHeaderValue;
|
|
926
1042
|
exports.frameAddress = frameAddress;
|
|
927
1043
|
exports.getFlightDataConsumer = getFlightDataConsumer;
|
|
1044
|
+
exports.getFlightDataSourceIds = getFlightDataSourceIds;
|
|
928
1045
|
exports.getServerFunctionInvocation = getServerFunctionInvocation;
|
|
929
1046
|
exports.getServerFunctionMetadata = getServerFunctionMetadata;
|
|
930
1047
|
exports.getServerFunctionsCodec = getServerFunctionsCodec;
|