@solidjs/web 2.0.0-beta.21 → 2.0.0-beta.23

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.
Files changed (50) hide show
  1. package/dist/dev.cjs +51 -1
  2. package/dist/dev.js +47 -2
  3. package/dist/server.cjs +98 -34
  4. package/dist/server.js +94 -35
  5. package/dist/web.cjs +51 -1
  6. package/dist/web.js +47 -2
  7. package/frames/dist/client.cjs +1442 -0
  8. package/frames/dist/client.js +1430 -0
  9. package/frames/dist/server.cjs +1705 -0
  10. package/frames/dist/server.js +1694 -0
  11. package/frames/package.json +30 -0
  12. package/package.json +78 -5
  13. package/serialization/dist/serialization.cjs +83 -0
  14. package/serialization/dist/serialization.js +82 -1
  15. package/serialization/types/index.d.ts +12 -0
  16. package/serialization/types-cjs/index.d.cts +12 -0
  17. package/server-functions/dist/client.cjs +114 -59
  18. package/server-functions/dist/client.js +113 -61
  19. package/server-functions/dist/server.cjs +54 -10
  20. package/server-functions/dist/server.js +52 -11
  21. package/types/client.d.ts +26 -0
  22. package/types/core.d.ts +2 -1
  23. package/types/frames/client.d.ts +53 -0
  24. package/types/frames/frame-client.d.ts +205 -0
  25. package/types/frames/frame-sink.d.ts +145 -0
  26. package/types/frames/frame-transport.d.ts +105 -0
  27. package/types/frames/serializer.d.ts +151 -0
  28. package/types/frames/server.d.ts +21 -0
  29. package/types/jsx.d.ts +17 -2
  30. package/types/response.d.ts +27 -1
  31. package/types/serializer.d.ts +12 -0
  32. package/types/server-functions/client.d.ts +40 -5
  33. package/types/server-functions/server.d.ts +9 -5
  34. package/types/server-functions/shared.d.ts +29 -0
  35. package/types/server.d.ts +10 -0
  36. package/types-cjs/client.d.cts +26 -0
  37. package/types-cjs/core.d.cts +2 -1
  38. package/types-cjs/frames/client.d.cts +53 -0
  39. package/types-cjs/frames/frame-client.d.cts +205 -0
  40. package/types-cjs/frames/frame-sink.d.cts +145 -0
  41. package/types-cjs/frames/frame-transport.d.cts +105 -0
  42. package/types-cjs/frames/serializer.d.cts +151 -0
  43. package/types-cjs/frames/server.d.cts +21 -0
  44. package/types-cjs/jsx.d.cts +17 -2
  45. package/types-cjs/response.d.cts +27 -1
  46. package/types-cjs/serializer.d.cts +12 -0
  47. package/types-cjs/server-functions/client.d.cts +40 -5
  48. package/types-cjs/server-functions/server.d.cts +9 -5
  49. package/types-cjs/server-functions/shared.d.cts +29 -0
  50. package/types-cjs/server.d.cts +10 -0
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@solidjs/web/frames",
3
+ "main": "./dist/server.cjs",
4
+ "module": "./dist/server.js",
5
+ "types": "../types/frames/server.d.ts",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "exports": {
9
+ ".": {
10
+ "browser": {
11
+ "import": {
12
+ "types": "../types/frames/client.d.ts",
13
+ "default": "./dist/client.js"
14
+ },
15
+ "require": {
16
+ "types": "../types-cjs/frames/client.d.cts",
17
+ "default": "./dist/client.cjs"
18
+ }
19
+ },
20
+ "import": {
21
+ "types": "../types/frames/server.d.ts",
22
+ "default": "./dist/server.js"
23
+ },
24
+ "require": {
25
+ "types": "../types-cjs/frames/server.d.cts",
26
+ "default": "./dist/server.cjs"
27
+ }
28
+ }
29
+ }
30
+ }
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-beta.21",
4
+ "version": "2.0.0-beta.23",
5
5
  "author": "Ryan Carniato",
6
6
  "license": "MIT",
7
7
  "homepage": "https://solidjs.com",
@@ -34,7 +34,9 @@
34
34
  "serialization/types-cjs",
35
35
  "serialization/package.json",
36
36
  "server-functions/dist",
37
- "server-functions/package.json"
37
+ "server-functions/package.json",
38
+ "frames/dist",
39
+ "frames/package.json"
38
40
  ],
39
41
  "exports": {
40
42
  ".": {
@@ -217,6 +219,76 @@
217
219
  "default": "./server-functions/dist/client.cjs"
218
220
  }
219
221
  },
222
+ "./frames": {
223
+ "worker": {
224
+ "import": {
225
+ "types": "./types/frames/server.d.ts",
226
+ "default": "./frames/dist/server.js"
227
+ },
228
+ "require": {
229
+ "types": "./types-cjs/frames/server.d.cts",
230
+ "default": "./frames/dist/server.cjs"
231
+ }
232
+ },
233
+ "browser": {
234
+ "import": {
235
+ "types": "./types/frames/client.d.ts",
236
+ "default": "./frames/dist/client.js"
237
+ },
238
+ "require": {
239
+ "types": "./types-cjs/frames/client.d.cts",
240
+ "default": "./frames/dist/client.cjs"
241
+ }
242
+ },
243
+ "deno": {
244
+ "import": {
245
+ "types": "./types/frames/server.d.ts",
246
+ "default": "./frames/dist/server.js"
247
+ },
248
+ "require": {
249
+ "types": "./types-cjs/frames/server.d.cts",
250
+ "default": "./frames/dist/server.cjs"
251
+ }
252
+ },
253
+ "node": {
254
+ "import": {
255
+ "types": "./types/frames/server.d.ts",
256
+ "default": "./frames/dist/server.js"
257
+ },
258
+ "require": {
259
+ "types": "./types-cjs/frames/server.d.cts",
260
+ "default": "./frames/dist/server.cjs"
261
+ }
262
+ },
263
+ "import": {
264
+ "types": "./types/frames/client.d.ts",
265
+ "default": "./frames/dist/client.js"
266
+ },
267
+ "require": {
268
+ "types": "./types-cjs/frames/client.d.cts",
269
+ "default": "./frames/dist/client.cjs"
270
+ }
271
+ },
272
+ "./frames/server": {
273
+ "import": {
274
+ "types": "./types/frames/server.d.ts",
275
+ "default": "./frames/dist/server.js"
276
+ },
277
+ "require": {
278
+ "types": "./types-cjs/frames/server.d.cts",
279
+ "default": "./frames/dist/server.cjs"
280
+ }
281
+ },
282
+ "./frames/client": {
283
+ "import": {
284
+ "types": "./types/frames/client.d.ts",
285
+ "default": "./frames/dist/client.js"
286
+ },
287
+ "require": {
288
+ "types": "./types-cjs/frames/client.d.cts",
289
+ "default": "./frames/dist/client.cjs"
290
+ }
291
+ },
220
292
  "./types/*": "./types/*"
221
293
  },
222
294
  "dependencies": {
@@ -224,17 +296,17 @@
224
296
  "seroval-plugins": "~1.5.4"
225
297
  },
226
298
  "peerDependencies": {
227
- "solid-js": "^2.0.0-beta.21"
299
+ "solid-js": "^2.0.0-beta.23"
228
300
  },
229
301
  "devDependencies": {
230
- "solid-js": "2.0.0-beta.21"
302
+ "solid-js": "2.0.0-beta.23"
231
303
  },
232
304
  "scripts": {
233
305
  "build": "npm-run-all -nl build:clean types:copy-jsx build:js",
234
306
  "build:clean": "rimraf dist/",
235
307
  "build:js": "rollup -c",
236
308
  "link": "symlink-dir . node_modules/@solidjs/web",
237
- "types": "npm-run-all -nl types:clean types:copy-jsx types:web types:copy-web types:web-storage types:copy-serialization types:copy-server-functions types:cjs",
309
+ "types": "npm-run-all -nl types:clean types:copy-jsx types:web types:copy-web types:web-storage types:copy-serialization types:copy-server-functions types:copy-frames types:cjs",
238
310
  "types:clean": "rimraf types/ types-cjs/ storage/types/ storage/types-cjs/ serialization/types/ serialization/types-cjs/",
239
311
  "types:copy-jsx": "ncp ../../node_modules/@dom-expressions/runtime/src/jsx.d.ts ./src/jsx.d.ts && ncp ../../node_modules/@dom-expressions/runtime/src/jsx-properties.d.ts ./src/jsx-properties.d.ts && dom-expressions-jsx-types --input ./src/jsx.d.ts --element \"SolidElement | Node | ArrayElement\" --import 'import type { Element as SolidElement } from \"solid-js\";'",
240
312
  "types:web": "tsc --project ./tsconfig.build.json",
@@ -242,6 +314,7 @@
242
314
  "types:web-storage": "tsc --project ./storage/tsconfig.build.json",
243
315
  "types:copy-serialization": "node -e \"fs.mkdirSync('./serialization/types', { recursive: true }); fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/serializer.d.ts', './serialization/types/index.d.ts');\"",
244
316
  "types:copy-server-functions": "node -e \"fs.mkdirSync('./types/server-functions', { recursive: true }); for (const f of ['shared', 'client', 'server']) fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/server-functions/' + f + '.d.ts', './types/server-functions/' + f + '.d.ts');\"",
317
+ "types:copy-frames": "node -e \"fs.mkdirSync('./types/frames', { recursive: true }); for (const f of ['frame-client', 'frame-transport', 'frame-sink', 'serializer']) fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/' + f + '.d.ts', './types/frames/' + f + '.d.ts'); for (const f of ['client', 'server']) fs.copyFileSync('./frames/types/' + f + '.d.ts', './types/frames/' + f + '.d.ts');\"",
245
318
  "types:cjs": "node ../../scripts/sync-dual-types.mjs ./types ./types-cjs ./storage/types ./storage/types-cjs ./serialization/types ./serialization/types-cjs",
246
319
  "test": "vitest run && vitest run --config vite.config.server.mjs && vitest run --config vite.config.hydrate.mjs",
247
320
  "test:server": "vitest run --config vite.config.server.mjs",
@@ -73,10 +73,93 @@ function createJSONDeserializer(options) {
73
73
  });
74
74
  };
75
75
  }
76
+ function createJSONSerializer({
77
+ onData,
78
+ onDone,
79
+ onError,
80
+ plugins,
81
+ disabledFeatures,
82
+ depthLimit
83
+ }) {
84
+ const resolved = resolveCodecOptions({
85
+ plugins,
86
+ disabledFeatures,
87
+ depthLimit
88
+ });
89
+ const refs = new Map();
90
+ const cancels = new Set();
91
+ let pendingWrites = 0;
92
+ let flushed = false;
93
+ let done = false;
94
+ const maybeDone = () => {
95
+ if (flushed && pendingWrites === 0 && !done) {
96
+ done = true;
97
+ onDone && onDone();
98
+ }
99
+ };
100
+ return {
101
+ write(key, value) {
102
+ if (flushed) return;
103
+ pendingWrites++;
104
+ let settled = false;
105
+ let cancel = null;
106
+ const stream = seroval.toCrossJSONStream(value, {
107
+ refs,
108
+ plugins: resolved.plugins,
109
+ disabledFeatures: resolved.disabledFeatures,
110
+ onParse(node, initial) {
111
+ onData({
112
+ key,
113
+ node,
114
+ initial
115
+ });
116
+ },
117
+ onError,
118
+ onDone() {
119
+ settled = true;
120
+ if (cancel) cancels.delete(cancel);
121
+ pendingWrites--;
122
+ maybeDone();
123
+ }
124
+ });
125
+ if (!settled) {
126
+ cancel = stream;
127
+ cancels.add(cancel);
128
+ }
129
+ },
130
+ flush() {
131
+ flushed = true;
132
+ maybeDone();
133
+ },
134
+ close() {
135
+ flushed = true;
136
+ for (const cancel of cancels) cancel();
137
+ cancels.clear();
138
+ }
139
+ };
140
+ }
141
+ function createJSONDataTable(options) {
142
+ const deserialize = createJSONDeserializer(options);
143
+ const table = new Map();
144
+ return {
145
+ apply(record) {
146
+ const value = deserialize(record.node);
147
+ if (record.initial) table.set(record.key, value);
148
+ },
149
+ get(key) {
150
+ return table.get(key);
151
+ },
152
+ resolve(ref) {
153
+ return table.get(ref.$ref);
154
+ }
155
+ };
156
+ }
76
157
 
77
158
  exports.DEFAULT_WEB_PLUGINS = DEFAULT_WEB_PLUGINS;
78
159
  exports.createHydrationSerializer = createHydrationSerializer;
160
+ exports.createJSONDataTable = createJSONDataTable;
79
161
  exports.createJSONDeserializer = createJSONDeserializer;
162
+ exports.createJSONSerializer = createJSONSerializer;
80
163
  exports.createSerializer = createSerializer;
81
164
  exports.getLocalHeaderScript = getLocalHeaderScript;
82
165
  exports.resolveSerializerPlugins = resolveSerializerPlugins;
@@ -71,5 +71,86 @@ function createJSONDeserializer(options) {
71
71
  });
72
72
  };
73
73
  }
74
+ function createJSONSerializer({
75
+ onData,
76
+ onDone,
77
+ onError,
78
+ plugins,
79
+ disabledFeatures,
80
+ depthLimit
81
+ }) {
82
+ const resolved = resolveCodecOptions({
83
+ plugins,
84
+ disabledFeatures,
85
+ depthLimit
86
+ });
87
+ const refs = new Map();
88
+ const cancels = new Set();
89
+ let pendingWrites = 0;
90
+ let flushed = false;
91
+ let done = false;
92
+ const maybeDone = () => {
93
+ if (flushed && pendingWrites === 0 && !done) {
94
+ done = true;
95
+ onDone && onDone();
96
+ }
97
+ };
98
+ return {
99
+ write(key, value) {
100
+ if (flushed) return;
101
+ pendingWrites++;
102
+ let settled = false;
103
+ let cancel = null;
104
+ const stream = toCrossJSONStream(value, {
105
+ refs,
106
+ plugins: resolved.plugins,
107
+ disabledFeatures: resolved.disabledFeatures,
108
+ onParse(node, initial) {
109
+ onData({
110
+ key,
111
+ node,
112
+ initial
113
+ });
114
+ },
115
+ onError,
116
+ onDone() {
117
+ settled = true;
118
+ if (cancel) cancels.delete(cancel);
119
+ pendingWrites--;
120
+ maybeDone();
121
+ }
122
+ });
123
+ if (!settled) {
124
+ cancel = stream;
125
+ cancels.add(cancel);
126
+ }
127
+ },
128
+ flush() {
129
+ flushed = true;
130
+ maybeDone();
131
+ },
132
+ close() {
133
+ flushed = true;
134
+ for (const cancel of cancels) cancel();
135
+ cancels.clear();
136
+ }
137
+ };
138
+ }
139
+ function createJSONDataTable(options) {
140
+ const deserialize = createJSONDeserializer(options);
141
+ const table = new Map();
142
+ return {
143
+ apply(record) {
144
+ const value = deserialize(record.node);
145
+ if (record.initial) table.set(record.key, value);
146
+ },
147
+ get(key) {
148
+ return table.get(key);
149
+ },
150
+ resolve(ref) {
151
+ return table.get(ref.$ref);
152
+ }
153
+ };
154
+ }
74
155
 
75
- export { DEFAULT_WEB_PLUGINS, createHydrationSerializer, createJSONDeserializer, createSerializer, getLocalHeaderScript, resolveSerializerPlugins, serializeJSON };
156
+ export { DEFAULT_WEB_PLUGINS, createHydrationSerializer, createJSONDataTable, createJSONDeserializer, createJSONSerializer, createSerializer, getLocalHeaderScript, resolveSerializerPlugins, serializeJSON };
@@ -137,3 +137,15 @@ export function serializeJSON(value: unknown, options: JSONSerializeOptions): ()
137
137
  * settles the async values referenced inside it.
138
138
  */
139
139
  export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
140
+
141
+ /**
142
+ * A resident, response-scoped decode table over the keyed JSON codec: apply
143
+ * each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
144
+ * `resolve`. The frames client host wires one per response
145
+ * (`applyData: c => table.apply(c)`).
146
+ */
147
+ export interface JSONDataTable {
148
+ apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
149
+ resolve<T = unknown>(ref: { $ref: string }): T;
150
+ }
151
+ export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;
@@ -137,3 +137,15 @@ export function serializeJSON(value: unknown, options: JSONSerializeOptions): ()
137
137
  * settles the async values referenced inside it.
138
138
  */
139
139
  export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
140
+
141
+ /**
142
+ * A resident, response-scoped decode table over the keyed JSON codec: apply
143
+ * each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
144
+ * `resolve`. The frames client host wires one per response
145
+ * (`applyData: c => table.apply(c)`).
146
+ */
147
+ export interface JSONDataTable {
148
+ apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
149
+ resolve<T = unknown>(ref: { $ref: string }): T;
150
+ }
151
+ export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;
@@ -23,19 +23,6 @@ function resolveCodecOptions({
23
23
  depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
24
24
  };
25
25
  }
26
- function serializeJSON(value, {
27
- onParse,
28
- onDone,
29
- onError,
30
- ...codecOptions
31
- }) {
32
- return seroval.toCrossJSONStream(value, {
33
- onParse,
34
- onDone,
35
- onError,
36
- ...resolveCodecOptions(codecOptions)
37
- });
38
- }
39
26
  function createJSONDeserializer(options) {
40
27
  const refs = new Map();
41
28
  const resolved = resolveCodecOptions(options);
@@ -53,9 +40,6 @@ const codecConfig = {
53
40
  function configureServerFunctionsCodec(codec) {
54
41
  codecConfig.codec = codec;
55
42
  }
56
- function getServerFunctionsCodec() {
57
- return codecConfig.codec;
58
- }
59
43
  const flightConfig = {
60
44
  consumer: undefined
61
45
  };
@@ -85,6 +69,31 @@ function withMeta(fn, meta) {
85
69
  return fn;
86
70
  }
87
71
  const FUNCTION_HEADER = "X-Server-Function-Id";
72
+ const ERROR_HEADER = "X-Server-Function-Error";
73
+ const ERROR_HEADER_MARKER = "=?1?";
74
+ const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
75
+ function encodeErrorHeaderValue(value) {
76
+ let stripped = String(value).replace(/[\r\n]+/g, "");
77
+ if (!NEEDS_ENCODING.test(stripped) && !stripped.startsWith(ERROR_HEADER_MARKER) && stripped === stripped.trim()) {
78
+ return stripped;
79
+ }
80
+ if (typeof stripped.toWellFormed === "function") {
81
+ stripped = stripped.toWellFormed();
82
+ } else {
83
+ stripped = stripped.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
84
+ }
85
+ return ERROR_HEADER_MARKER + encodeURIComponent(stripped);
86
+ }
87
+ function decodeErrorHeaderValue(value) {
88
+ if (typeof value !== "string" || !value.startsWith(ERROR_HEADER_MARKER)) {
89
+ return value;
90
+ }
91
+ try {
92
+ return decodeURIComponent(value.slice(ERROR_HEADER_MARKER.length));
93
+ } catch {
94
+ return value;
95
+ }
96
+ }
88
97
  const INSTANCE_HEADER = "X-Server-Function-Instance";
89
98
  const BODY_FORMAT_HEADER = "X-Server-Function-Format";
90
99
  const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
@@ -97,7 +106,8 @@ const BodyFormat = {
97
106
  Blob: "4",
98
107
  File: "5",
99
108
  ArrayBuffer: "6",
100
- Uint8Array: "7"
109
+ Uint8Array: "7",
110
+ Json: "8"
101
111
  };
102
112
  function getHeadersAndBody(body) {
103
113
  switch (true) {
@@ -167,6 +177,8 @@ async function extractBody(source, codecOptions) {
167
177
  switch (true) {
168
178
  case format === BodyFormat.Serialized:
169
179
  return await deserializeStream(clone, codecOptions);
180
+ case format === BodyFormat.Json:
181
+ return JSON.parse(await clone.text());
170
182
  case format === BodyFormat.String:
171
183
  return await clone.text();
172
184
  case format === BodyFormat.File:
@@ -189,17 +201,6 @@ async function extractBody(source, codecOptions) {
189
201
  }
190
202
  return undefined;
191
203
  }
192
- function createChunk(data) {
193
- const encodeData = new TextEncoder().encode(data);
194
- const bytes = encodeData.length;
195
- const baseHex = bytes.toString(16);
196
- const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
197
- const head = new TextEncoder().encode(`;0x${totalHex};`);
198
- const chunk = new Uint8Array(12 + bytes);
199
- chunk.set(head);
200
- chunk.set(encodeData, 12);
201
- return chunk;
202
- }
203
204
  class ChunkReader {
204
205
  constructor(stream) {
205
206
  this.reader = stream.getReader();
@@ -256,28 +257,6 @@ class ChunkReader {
256
257
  }
257
258
  }
258
259
  }
259
- function serializeStream(value, codecOptions) {
260
- return new ReadableStream({
261
- start(controller) {
262
- serializeJSON(value, {
263
- ...codecOptions,
264
- onParse(node) {
265
- controller.enqueue(createChunk(JSON.stringify(node)));
266
- },
267
- onDone() {
268
- controller.close();
269
- },
270
- onError(error) {
271
- controller.error(error);
272
- }
273
- });
274
- }
275
- });
276
- }
277
- async function serializeString(value, codecOptions) {
278
- const response = new Response(serializeStream(value, codecOptions));
279
- return await response.text();
280
- }
281
260
  async function deserializeStream(source, codecOptions) {
282
261
  if (!source.body) {
283
262
  throw new Error("missing body");
@@ -301,16 +280,43 @@ async function decodeResponse(response, codecOptions) {
301
280
 
302
281
  const config = {
303
282
  endpoint: "/_server",
304
- prepareRequest: undefined
283
+ prepareRequest: undefined,
284
+ responseHandler: undefined,
285
+ serializeArgs: undefined
305
286
  };
287
+ function isJSONSafe(value) {
288
+ if (value === null) return true;
289
+ const t = typeof value;
290
+ if (t === "string" || t === "boolean") return true;
291
+ if (t === "number") return Number.isFinite(value);
292
+ if (t !== "object") return false;
293
+ if (Array.isArray(value)) {
294
+ for (const v of value) if (!isJSONSafe(v)) return false;
295
+ return true;
296
+ }
297
+ const proto = Object.getPrototypeOf(value);
298
+ if (proto !== Object.prototype && proto !== null) return false;
299
+ for (const k in value) if (!isJSONSafe(value[k])) return false;
300
+ return true;
301
+ }
302
+ function serializeArguments(args) {
303
+ if (!config.serializeArgs) {
304
+ throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " + "(from the server-functions rich-args entry) 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.");
305
+ }
306
+ return config.serializeArgs(args);
307
+ }
306
308
  function configureServerFunctionsClient({
307
309
  endpoint,
308
310
  codec,
309
- prepareRequest
311
+ prepareRequest,
312
+ responseHandler,
313
+ serializeArgs
310
314
  } = {}) {
311
315
  if (endpoint !== undefined) config.endpoint = endpoint;
312
316
  if (codec !== undefined) configureServerFunctionsCodec(codec);
313
317
  if (prepareRequest !== undefined) config.prepareRequest = prepareRequest;
318
+ if (responseHandler !== undefined) config.responseHandler = responseHandler;
319
+ if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
314
320
  }
315
321
  let INSTANCE = 0;
316
322
  async function createRequest(base, id, instance, options, meta) {
@@ -352,9 +358,20 @@ async function initializeResponse(base, id, instance, options, args, meta) {
352
358
  }, meta);
353
359
  }
354
360
  }
361
+ if (isJSONSafe(args)) {
362
+ return createRequest(base, id, instance, {
363
+ ...options,
364
+ body: JSON.stringify(args),
365
+ headers: {
366
+ ...options.headers,
367
+ "Content-Type": "application/json",
368
+ [BODY_FORMAT_HEADER]: BodyFormat.Json
369
+ }
370
+ }, meta);
371
+ }
355
372
  return createRequest(base, id, instance, {
356
373
  ...options,
357
- body: await serializeString(args, getServerFunctionsCodec()),
374
+ body: await serializeArguments(args),
358
375
  headers: {
359
376
  ...options.headers,
360
377
  "Content-Type": "text/plain",
@@ -364,7 +381,21 @@ async function initializeResponse(base, id, instance, options, args, meta) {
364
381
  }
365
382
  async function fetchServerFunction(base, id, options, args, meta) {
366
383
  const instance = `server-function:${INSTANCE++}`;
384
+ const handler = config.responseHandler;
385
+ const context = handler && handler.capture ? handler.capture({
386
+ id,
387
+ meta
388
+ }) : undefined;
367
389
  const response = await initializeResponse(base, id, instance, options, args, meta);
390
+ if (handler) {
391
+ const handled = handler.handle(response, {
392
+ id,
393
+ meta,
394
+ args,
395
+ context
396
+ });
397
+ if (handled !== undefined) return handled;
398
+ }
368
399
  if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
369
400
  const consumer = getFlightDataConsumer();
370
401
  if (consumer) {
@@ -372,7 +403,7 @@ async function fetchServerFunction(base, id, options, args, meta) {
372
403
  await consumer(payload.data, {
373
404
  response
374
405
  });
375
- if (response.headers.has("X-Server-Function-Error") && !response.headers.has("Location") && !response.headers.has("X-Revalidate")) {
406
+ if (response.headers.has(ERROR_HEADER) && !response.headers.has("Location") && !response.headers.has("X-Revalidate")) {
376
407
  throw payload.value;
377
408
  }
378
409
  return payload.value;
@@ -382,22 +413,33 @@ async function fetchServerFunction(base, id, options, args, meta) {
382
413
  return response;
383
414
  }
384
415
  const result = await decodeResponse(response.clone());
385
- if (response.headers.has("X-Server-Function-Error")) {
416
+ if (response.headers.has(ERROR_HEADER)) {
386
417
  throw result;
387
418
  }
388
419
  return result;
389
420
  }
390
- function createServerReference(id, name) {
421
+ function createServerReference(id, name, base) {
391
422
  const metadata = name === undefined ? {} : {
392
423
  name
393
424
  };
394
- const fn = (...args) => fetchServerFunction(config.endpoint, id, {}, args, metadata);
425
+ const fn = (...args) => {
426
+ const handler = config.responseHandler;
427
+ if (handler && handler.intercept) {
428
+ const hit = handler.intercept({
429
+ id,
430
+ meta: metadata,
431
+ args
432
+ });
433
+ if (hit !== undefined) return hit;
434
+ }
435
+ return fetchServerFunction(base || config.endpoint, id, {}, args, metadata);
436
+ };
395
437
  fn[SERVER_FUNCTION_METADATA] = metadata;
396
438
  return new Proxy(fn, {
397
439
  get(target, prop) {
398
440
  if (prop === "id") return id;
399
441
  if (prop === "url") {
400
- return `${config.endpoint}?id=${encodeURIComponent(id)}`;
442
+ return base || `${config.endpoint}?id=${encodeURIComponent(id)}`;
401
443
  }
402
444
  return target[prop];
403
445
  }
@@ -412,9 +454,19 @@ function GET(fn) {
412
454
  ...getServerFunctionMetadata(fn)
413
455
  };
414
456
  const wrapped = async (...args) => {
457
+ const handler = config.responseHandler;
458
+ if (handler && handler.intercept) {
459
+ const hit = handler.intercept({
460
+ id,
461
+ meta: metadata,
462
+ args
463
+ });
464
+ if (hit !== undefined) return hit;
465
+ }
415
466
  let base = `${config.endpoint}?id=${encodeURIComponent(id)}`;
416
467
  if (args.length) {
417
- base += `&args=${encodeURIComponent(await serializeString(args, getServerFunctionsCodec()))}`;
468
+ const encoded = isJSONSafe(args) ? JSON.stringify(args) : await serializeArguments(args);
469
+ base += `&args=${encodeURIComponent(encoded)}`;
418
470
  }
419
471
  return fetchServerFunction(base, id, {
420
472
  method: "GET"
@@ -434,13 +486,16 @@ function registerServerReference() {
434
486
  throw new Error("registerServerReference must not be called in the client build");
435
487
  }
436
488
 
489
+ exports.ERROR_HEADER = ERROR_HEADER;
437
490
  exports.FUNCTION_HEADER = FUNCTION_HEADER;
438
491
  exports.GET = GET;
439
492
  exports.INSTANCE_HEADER = INSTANCE_HEADER;
440
493
  exports.SINGLE_FLIGHT_HEADER = SINGLE_FLIGHT_HEADER;
441
494
  exports.configureServerFunctionsClient = configureServerFunctionsClient;
442
495
  exports.createServerReference = createServerReference;
496
+ exports.decodeErrorHeaderValue = decodeErrorHeaderValue;
443
497
  exports.decodeResponse = decodeResponse;
498
+ exports.encodeErrorHeaderValue = encodeErrorHeaderValue;
444
499
  exports.getServerFunctionMetadata = getServerFunctionMetadata;
445
500
  exports.isServerFunction = isServerFunction;
446
501
  exports.registerServerReference = registerServerReference;