@solidjs/web 2.0.0-beta.31 → 2.0.0-beta.33

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 (73) hide show
  1. package/README.md +1 -6
  2. package/dist/dev.cjs +285 -60
  3. package/dist/dev.js +267 -57
  4. package/dist/server.cjs +1148 -176
  5. package/dist/server.js +1133 -175
  6. package/dist/web.cjs +285 -60
  7. package/dist/web.js +267 -57
  8. package/frames/dist/client.cjs +397 -175
  9. package/frames/dist/client.dev.cjs +401 -175
  10. package/frames/dist/client.dev.js +399 -173
  11. package/frames/dist/client.js +395 -173
  12. package/frames/dist/server.cjs +1424 -277
  13. package/frames/dist/server.js +1426 -280
  14. package/package.json +69 -7
  15. package/serialization/decode/package.json +20 -0
  16. package/serialization/dist/decode.cjs +110 -0
  17. package/serialization/dist/decode.js +104 -0
  18. package/serialization/dist/serialization.cjs +106 -43
  19. package/serialization/dist/serialization.js +100 -44
  20. package/serialization/types/index.d.ts +85 -60
  21. package/serialization/types/serializer-decode.d.ts +182 -0
  22. package/serialization/types-cjs/index.d.cts +85 -60
  23. package/serialization/types-cjs/serializer-decode.d.cts +182 -0
  24. package/server-functions/dist/client.cjs +131 -98
  25. package/server-functions/dist/client.js +131 -99
  26. package/server-functions/dist/rich-args.cjs +11 -0
  27. package/server-functions/dist/rich-args.js +9 -0
  28. package/server-functions/dist/server.cjs +367 -194
  29. package/server-functions/dist/server.dev.cjs +1077 -0
  30. package/server-functions/dist/server.dev.js +1045 -0
  31. package/server-functions/dist/server.js +365 -195
  32. package/server-functions/package.json +10 -0
  33. package/server-functions/rich-args/package.json +20 -0
  34. package/storage/types/index.d.ts +1 -1
  35. package/storage/types-cjs/index.d.cts +1 -1
  36. package/types/client.d.ts +149 -6
  37. package/types/cookies.d.ts +93 -0
  38. package/types/core.d.ts +4 -2
  39. package/types/frames/client.d.ts +15 -1
  40. package/types/frames/frame-client.d.ts +63 -7
  41. package/types/frames/frame-sink.d.ts +26 -3
  42. package/types/frames/frame-transport.d.ts +40 -8
  43. package/types/frames/serializer.d.ts +85 -60
  44. package/types/frames/server.d.ts +22 -0
  45. package/types/index.d.ts +2 -3
  46. package/types/response.d.ts +45 -0
  47. package/types/serializer-decode.d.ts +182 -0
  48. package/types/serializer.d.ts +85 -60
  49. package/types/server-functions/client.d.ts +2 -1
  50. package/types/server-functions/rich-args.d.ts +10 -0
  51. package/types/server-functions/server.d.ts +99 -1
  52. package/types/server-functions/shared.d.ts +79 -1
  53. package/types/server-mock.d.ts +171 -59
  54. package/types/server.d.ts +209 -37
  55. package/types-cjs/client.d.cts +149 -6
  56. package/types-cjs/cookies.d.cts +93 -0
  57. package/types-cjs/core.d.cts +4 -2
  58. package/types-cjs/frames/client.d.cts +15 -1
  59. package/types-cjs/frames/frame-client.d.cts +63 -7
  60. package/types-cjs/frames/frame-sink.d.cts +26 -3
  61. package/types-cjs/frames/frame-transport.d.cts +40 -8
  62. package/types-cjs/frames/serializer.d.cts +85 -60
  63. package/types-cjs/frames/server.d.cts +22 -0
  64. package/types-cjs/index.d.cts +2 -3
  65. package/types-cjs/response.d.cts +45 -0
  66. package/types-cjs/serializer-decode.d.cts +182 -0
  67. package/types-cjs/serializer.d.cts +85 -60
  68. package/types-cjs/server-functions/client.d.cts +2 -1
  69. package/types-cjs/server-functions/rich-args.d.cts +10 -0
  70. package/types-cjs/server-functions/server.d.cts +99 -1
  71. package/types-cjs/server-functions/shared.d.cts +79 -1
  72. package/types-cjs/server-mock.d.cts +171 -59
  73. package/types-cjs/server.d.cts +209 -37
@@ -1,39 +1,35 @@
1
1
  'use strict';
2
2
 
3
- var seroval = require('seroval');
4
- var web = require('seroval-plugins/web');
5
-
6
3
  const REVALIDATE_HEADER = "X-Revalidate";
7
4
 
8
- seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
9
- const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
10
- web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
11
- web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
12
- function resolveSerializerPlugins(customPlugins) {
13
- return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
14
- }
15
- const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
16
- const JSON_CODEC_DEPTH_LIMIT = 64;
17
- function resolveCodecOptions({
18
- plugins,
19
- disabledFeatures,
20
- depthLimit
21
- } = {}) {
22
- return {
23
- plugins: resolveSerializerPlugins(plugins),
24
- disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
25
- depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
26
- };
5
+ const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
6
+ function getServerFunctionMetadata(fn) {
7
+ if (typeof fn !== "function") return undefined;
8
+ return fn[SERVER_FUNCTION_METADATA] || undefined;
27
9
  }
28
- function createJSONDeserializer(options) {
29
- const refs = new Map();
30
- const resolved = resolveCodecOptions(options);
31
- return function deserializeJSONChunk(node) {
32
- return seroval.fromCrossJSON(node, {
33
- refs,
34
- ...resolved
35
- });
36
- };
10
+ function isServerFunction(fn) {
11
+ return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
12
+ }
13
+ function withMeta(fn, meta) {
14
+ const metadata = getServerFunctionMetadata(fn);
15
+ if (!metadata) {
16
+ throw new Error("withMeta expects a server function reference");
17
+ }
18
+ Object.assign(metadata, meta);
19
+ return fn;
20
+ }
21
+ const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
22
+ function provideServerFunctionRPC(rpc) {
23
+ globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
24
+ }
25
+
26
+ const FLASH_COOKIE = "flash";
27
+ const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
28
+ function hasFlashCookie(cookieHeader) {
29
+ return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
30
+ }
31
+ function clearFlashCookie() {
32
+ return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
37
33
  }
38
34
 
39
35
  const codecConfig = {
@@ -101,22 +97,6 @@ function stableString(value, seen) {
101
97
  }
102
98
  return out + "}";
103
99
  }
104
- const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
105
- function getServerFunctionMetadata(fn) {
106
- if (typeof fn !== "function") return undefined;
107
- return fn[SERVER_FUNCTION_METADATA] || undefined;
108
- }
109
- function isServerFunction(fn) {
110
- return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
111
- }
112
- function withMeta(fn, meta) {
113
- const metadata = getServerFunctionMetadata(fn);
114
- if (!metadata) {
115
- throw new Error("withMeta expects a server function reference");
116
- }
117
- Object.assign(metadata, meta);
118
- return fn;
119
- }
120
100
  const FUNCTION_HEADER = "X-Server-Function-Id";
121
101
  const ERROR_HEADER = "X-Server-Function-Error";
122
102
  const ERROR_HEADER_MARKER = "=?1?";
@@ -147,14 +127,6 @@ const INSTANCE_HEADER = "X-Server-Function-Instance";
147
127
  const BODY_FORMAT_HEADER = "X-Server-Function-Format";
148
128
  const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
149
129
  const FILE_FORM_KEY = "__server_function_file__";
150
- const FLASH_COOKIE = "flash";
151
- const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
152
- function hasFlashCookie(cookieHeader) {
153
- return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
154
- }
155
- function clearFlashCookie() {
156
- return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
157
- }
158
130
  const BodyFormat = {
159
131
  Serialized: "0",
160
132
  String: "1",
@@ -166,6 +138,38 @@ const BodyFormat = {
166
138
  Uint8Array: "7",
167
139
  Json: "8"
168
140
  };
141
+ const JSON_SAFE_DEPTH_LIMIT = 10000;
142
+ const EXIT = {};
143
+ function isJSONSafe(value) {
144
+ const stack = [value];
145
+ const ancestors = new Set();
146
+ while (stack.length) {
147
+ const v = stack.pop();
148
+ if (v === EXIT) {
149
+ ancestors.delete(stack.pop());
150
+ continue;
151
+ }
152
+ if (v === null) continue;
153
+ const t = typeof v;
154
+ if (t === "string" || t === "boolean") continue;
155
+ if (t === "number") {
156
+ if (!Number.isFinite(v)) return false;
157
+ continue;
158
+ }
159
+ if (t !== "object") return false;
160
+ if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
161
+ ancestors.add(v);
162
+ stack.push(v, EXIT);
163
+ if (Array.isArray(v)) {
164
+ for (let i = 0; i < v.length; i++) stack.push(v[i]);
165
+ } else {
166
+ const proto = Object.getPrototypeOf(v);
167
+ if (proto !== Object.prototype && proto !== null) return false;
168
+ for (const k in v) stack.push(v[k]);
169
+ }
170
+ }
171
+ return true;
172
+ }
169
173
  function getHeadersAndBody(body) {
170
174
  switch (true) {
171
175
  case typeof body === "string":
@@ -259,13 +263,11 @@ async function extractBody(source, codecOptions) {
259
263
  return undefined;
260
264
  }
261
265
  function createChunk(data) {
262
- const encodeData = new TextEncoder().encode(data);
266
+ const encoder = new TextEncoder();
267
+ const encodeData = encoder.encode(data);
263
268
  const bytes = encodeData.length;
264
- const baseHex = bytes.toString(16);
265
- const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
266
- const head = new TextEncoder().encode(`;0x${totalHex};`);
267
269
  const chunk = new Uint8Array(12 + bytes);
268
- chunk.set(head);
270
+ chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
269
271
  chunk.set(encodeData, 12);
270
272
  return chunk;
271
273
  }
@@ -297,8 +299,8 @@ class ChunkReader {
297
299
  }
298
300
  await this.readChunk();
299
301
  }
300
- const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
301
- const bytes = Number.parseInt(head, 16);
302
+ const decoder = new TextDecoder();
303
+ const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
302
304
  if (Number.isNaN(bytes)) {
303
305
  throw new Error("Malformed server function stream.");
304
306
  }
@@ -308,7 +310,7 @@ class ChunkReader {
308
310
  }
309
311
  await this.readChunk();
310
312
  }
311
- const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
313
+ const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
312
314
  this.buffer = this.buffer.subarray(12 + bytes);
313
315
  return {
314
316
  done: false,
@@ -325,6 +327,31 @@ class ChunkReader {
325
327
  }
326
328
  }
327
329
  }
330
+ function serializeStream(value, codecOptions) {
331
+ return new ReadableStream({
332
+ async start(controller) {
333
+ const {
334
+ serializeJSON
335
+ } = await import('@solidjs/web/serialization');
336
+ serializeJSON(value, {
337
+ ...codecOptions,
338
+ onParse(node) {
339
+ controller.enqueue(createChunk(JSON.stringify(node)));
340
+ },
341
+ onDone() {
342
+ controller.close();
343
+ },
344
+ onError(error) {
345
+ controller.error(error);
346
+ }
347
+ });
348
+ }
349
+ });
350
+ }
351
+ async function serializeString(value, codecOptions) {
352
+ const response = new Response(serializeStream(value, codecOptions));
353
+ return await response.text();
354
+ }
328
355
  async function deserializeStream(source, codecOptions) {
329
356
  if (!source.body) {
330
357
  throw new Error("missing body");
@@ -332,6 +359,9 @@ async function deserializeStream(source, codecOptions) {
332
359
  const reader = new ChunkReader(source.body);
333
360
  const result = await reader.next();
334
361
  if (!result.done) {
362
+ const {
363
+ createJSONDeserializer
364
+ } = await import('@solidjs/web/serialization/decode');
335
365
  const deserializeChunk = createJSONDeserializer(codecOptions);
336
366
  function interpretChunk(chunk) {
337
367
  return deserializeChunk(JSON.parse(chunk));
@@ -364,24 +394,9 @@ const config = {
364
394
  responseHandler: undefined,
365
395
  serializeArgs: undefined
366
396
  };
367
- function isJSONSafe(value) {
368
- if (value === null) return true;
369
- const t = typeof value;
370
- if (t === "string" || t === "boolean") return true;
371
- if (t === "number") return Number.isFinite(value);
372
- if (t !== "object") return false;
373
- if (Array.isArray(value)) {
374
- for (const v of value) if (!isJSONSafe(v)) return false;
375
- return true;
376
- }
377
- const proto = Object.getPrototypeOf(value);
378
- if (proto !== Object.prototype && proto !== null) return false;
379
- for (const k in value) if (!isJSONSafe(value[k])) return false;
380
- return true;
381
- }
382
397
  function serializeArguments(args) {
383
398
  if (!config.serializeArgs) {
384
- 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.");
399
+ 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.");
385
400
  }
386
401
  return config.serializeArgs(args);
387
402
  }
@@ -399,6 +414,15 @@ function configureServerFunctionsClient({
399
414
  if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
400
415
  }
401
416
  let INSTANCE = 0;
417
+ let rpcProvided = false;
418
+ function provideRPC() {
419
+ if (rpcProvided) return;
420
+ rpcProvided = true;
421
+ provideServerFunctionRPC({
422
+ GET,
423
+ decodeResponse
424
+ });
425
+ }
402
426
  async function createRequest(base, id, instance, options, meta) {
403
427
  const headers = {
404
428
  ...options.headers,
@@ -438,31 +462,37 @@ async function initializeResponse(base, id, instance, options, args, meta) {
438
462
  }, meta);
439
463
  }
440
464
  }
441
- if (isJSONSafe(args)) {
442
- return createRequest(base, id, instance, {
443
- ...options,
444
- body: JSON.stringify(args),
445
- headers: {
446
- ...options.headers,
447
- "Content-Type": "application/json",
448
- [BODY_FORMAT_HEADER]: BodyFormat.Json
449
- }
450
- }, meta);
451
- }
452
- if (args.length > 1) {
453
- const trailing = getHeadersAndBody(args[args.length - 1]);
454
- const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
455
- if (trailing && isJSONSafe(leading)) {
456
- const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
457
- return createRequest(target, id, instance, {
465
+ try {
466
+ if (isJSONSafe(args)) {
467
+ return createRequest(base, id, instance, {
458
468
  ...options,
459
- body: trailing.body,
469
+ body: JSON.stringify(args),
460
470
  headers: {
461
471
  ...options.headers,
462
- ...trailing.headers
472
+ "Content-Type": "application/json",
473
+ [BODY_FORMAT_HEADER]: BodyFormat.Json
463
474
  }
464
475
  }, meta);
465
476
  }
477
+ } catch {
478
+ }
479
+ if (args.length > 1) {
480
+ try {
481
+ const trailing = getHeadersAndBody(args[args.length - 1]);
482
+ const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
483
+ if (trailing && isJSONSafe(leading)) {
484
+ const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
485
+ return createRequest(target, id, instance, {
486
+ ...options,
487
+ body: trailing.body,
488
+ headers: {
489
+ ...options.headers,
490
+ ...trailing.headers
491
+ }
492
+ }, meta);
493
+ }
494
+ } catch {
495
+ }
466
496
  }
467
497
  return createRequest(base, id, instance, {
468
498
  ...options,
@@ -514,6 +544,7 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
514
544
  return result;
515
545
  }
516
546
  function createServerReference(id, name, base) {
547
+ provideRPC();
517
548
  const metadata = name === undefined ? {} : {
518
549
  name
519
550
  };
@@ -544,6 +575,7 @@ function GET(fn) {
544
575
  if (!isServerFunction(fn)) {
545
576
  throw new Error("GET expects a server function reference");
546
577
  }
578
+ provideRPC();
547
579
  const id = fn.id;
548
580
  const metadata = {
549
581
  ...getServerFunctionMetadata(fn)
@@ -609,5 +641,6 @@ exports.getServerFunctionsCodec = getServerFunctionsCodec;
609
641
  exports.hasFlashCookie = hasFlashCookie;
610
642
  exports.isServerFunction = isServerFunction;
611
643
  exports.registerServerReference = registerServerReference;
644
+ exports.serializeString = serializeString;
612
645
  exports.subscribeFlightData = subscribeFlightData;
613
646
  exports.withMeta = withMeta;
@@ -1,37 +1,33 @@
1
- import { fromCrossJSON, Feature } from 'seroval';
2
- import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
3
-
4
1
  const REVALIDATE_HEADER = "X-Revalidate";
5
2
 
6
- Feature.AggregateError | Feature.BigIntTypedArray;
7
- const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
8
- CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
9
- FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
10
- function resolveSerializerPlugins(customPlugins) {
11
- return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
12
- }
13
- const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
14
- const JSON_CODEC_DEPTH_LIMIT = 64;
15
- function resolveCodecOptions({
16
- plugins,
17
- disabledFeatures,
18
- depthLimit
19
- } = {}) {
20
- return {
21
- plugins: resolveSerializerPlugins(plugins),
22
- disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
23
- depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
24
- };
3
+ const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
4
+ function getServerFunctionMetadata(fn) {
5
+ if (typeof fn !== "function") return undefined;
6
+ return fn[SERVER_FUNCTION_METADATA] || undefined;
25
7
  }
26
- function createJSONDeserializer(options) {
27
- const refs = new Map();
28
- const resolved = resolveCodecOptions(options);
29
- return function deserializeJSONChunk(node) {
30
- return fromCrossJSON(node, {
31
- refs,
32
- ...resolved
33
- });
34
- };
8
+ function isServerFunction(fn) {
9
+ return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
10
+ }
11
+ function withMeta(fn, meta) {
12
+ const metadata = getServerFunctionMetadata(fn);
13
+ if (!metadata) {
14
+ throw new Error("withMeta expects a server function reference");
15
+ }
16
+ Object.assign(metadata, meta);
17
+ return fn;
18
+ }
19
+ const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
20
+ function provideServerFunctionRPC(rpc) {
21
+ globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
22
+ }
23
+
24
+ const FLASH_COOKIE = "flash";
25
+ const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
26
+ function hasFlashCookie(cookieHeader) {
27
+ return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
28
+ }
29
+ function clearFlashCookie() {
30
+ return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
35
31
  }
36
32
 
37
33
  const codecConfig = {
@@ -99,22 +95,6 @@ function stableString(value, seen) {
99
95
  }
100
96
  return out + "}";
101
97
  }
102
- const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
103
- function getServerFunctionMetadata(fn) {
104
- if (typeof fn !== "function") return undefined;
105
- return fn[SERVER_FUNCTION_METADATA] || undefined;
106
- }
107
- function isServerFunction(fn) {
108
- return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
109
- }
110
- function withMeta(fn, meta) {
111
- const metadata = getServerFunctionMetadata(fn);
112
- if (!metadata) {
113
- throw new Error("withMeta expects a server function reference");
114
- }
115
- Object.assign(metadata, meta);
116
- return fn;
117
- }
118
98
  const FUNCTION_HEADER = "X-Server-Function-Id";
119
99
  const ERROR_HEADER = "X-Server-Function-Error";
120
100
  const ERROR_HEADER_MARKER = "=?1?";
@@ -145,14 +125,6 @@ const INSTANCE_HEADER = "X-Server-Function-Instance";
145
125
  const BODY_FORMAT_HEADER = "X-Server-Function-Format";
146
126
  const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
147
127
  const FILE_FORM_KEY = "__server_function_file__";
148
- const FLASH_COOKIE = "flash";
149
- const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
150
- function hasFlashCookie(cookieHeader) {
151
- return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
152
- }
153
- function clearFlashCookie() {
154
- return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
155
- }
156
128
  const BodyFormat = {
157
129
  Serialized: "0",
158
130
  String: "1",
@@ -164,6 +136,38 @@ const BodyFormat = {
164
136
  Uint8Array: "7",
165
137
  Json: "8"
166
138
  };
139
+ const JSON_SAFE_DEPTH_LIMIT = 10000;
140
+ const EXIT = {};
141
+ function isJSONSafe(value) {
142
+ const stack = [value];
143
+ const ancestors = new Set();
144
+ while (stack.length) {
145
+ const v = stack.pop();
146
+ if (v === EXIT) {
147
+ ancestors.delete(stack.pop());
148
+ continue;
149
+ }
150
+ if (v === null) continue;
151
+ const t = typeof v;
152
+ if (t === "string" || t === "boolean") continue;
153
+ if (t === "number") {
154
+ if (!Number.isFinite(v)) return false;
155
+ continue;
156
+ }
157
+ if (t !== "object") return false;
158
+ if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
159
+ ancestors.add(v);
160
+ stack.push(v, EXIT);
161
+ if (Array.isArray(v)) {
162
+ for (let i = 0; i < v.length; i++) stack.push(v[i]);
163
+ } else {
164
+ const proto = Object.getPrototypeOf(v);
165
+ if (proto !== Object.prototype && proto !== null) return false;
166
+ for (const k in v) stack.push(v[k]);
167
+ }
168
+ }
169
+ return true;
170
+ }
167
171
  function getHeadersAndBody(body) {
168
172
  switch (true) {
169
173
  case typeof body === "string":
@@ -257,13 +261,11 @@ async function extractBody(source, codecOptions) {
257
261
  return undefined;
258
262
  }
259
263
  function createChunk(data) {
260
- const encodeData = new TextEncoder().encode(data);
264
+ const encoder = new TextEncoder();
265
+ const encodeData = encoder.encode(data);
261
266
  const bytes = encodeData.length;
262
- const baseHex = bytes.toString(16);
263
- const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
264
- const head = new TextEncoder().encode(`;0x${totalHex};`);
265
267
  const chunk = new Uint8Array(12 + bytes);
266
- chunk.set(head);
268
+ chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
267
269
  chunk.set(encodeData, 12);
268
270
  return chunk;
269
271
  }
@@ -295,8 +297,8 @@ class ChunkReader {
295
297
  }
296
298
  await this.readChunk();
297
299
  }
298
- const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
299
- const bytes = Number.parseInt(head, 16);
300
+ const decoder = new TextDecoder();
301
+ const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
300
302
  if (Number.isNaN(bytes)) {
301
303
  throw new Error("Malformed server function stream.");
302
304
  }
@@ -306,7 +308,7 @@ class ChunkReader {
306
308
  }
307
309
  await this.readChunk();
308
310
  }
309
- const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
311
+ const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
310
312
  this.buffer = this.buffer.subarray(12 + bytes);
311
313
  return {
312
314
  done: false,
@@ -323,6 +325,31 @@ class ChunkReader {
323
325
  }
324
326
  }
325
327
  }
328
+ function serializeStream(value, codecOptions) {
329
+ return new ReadableStream({
330
+ async start(controller) {
331
+ const {
332
+ serializeJSON
333
+ } = await import('@solidjs/web/serialization');
334
+ serializeJSON(value, {
335
+ ...codecOptions,
336
+ onParse(node) {
337
+ controller.enqueue(createChunk(JSON.stringify(node)));
338
+ },
339
+ onDone() {
340
+ controller.close();
341
+ },
342
+ onError(error) {
343
+ controller.error(error);
344
+ }
345
+ });
346
+ }
347
+ });
348
+ }
349
+ async function serializeString(value, codecOptions) {
350
+ const response = new Response(serializeStream(value, codecOptions));
351
+ return await response.text();
352
+ }
326
353
  async function deserializeStream(source, codecOptions) {
327
354
  if (!source.body) {
328
355
  throw new Error("missing body");
@@ -330,6 +357,9 @@ async function deserializeStream(source, codecOptions) {
330
357
  const reader = new ChunkReader(source.body);
331
358
  const result = await reader.next();
332
359
  if (!result.done) {
360
+ const {
361
+ createJSONDeserializer
362
+ } = await import('@solidjs/web/serialization/decode');
333
363
  const deserializeChunk = createJSONDeserializer(codecOptions);
334
364
  function interpretChunk(chunk) {
335
365
  return deserializeChunk(JSON.parse(chunk));
@@ -362,24 +392,9 @@ const config = {
362
392
  responseHandler: undefined,
363
393
  serializeArgs: undefined
364
394
  };
365
- function isJSONSafe(value) {
366
- if (value === null) return true;
367
- const t = typeof value;
368
- if (t === "string" || t === "boolean") return true;
369
- if (t === "number") return Number.isFinite(value);
370
- if (t !== "object") return false;
371
- if (Array.isArray(value)) {
372
- for (const v of value) if (!isJSONSafe(v)) return false;
373
- return true;
374
- }
375
- const proto = Object.getPrototypeOf(value);
376
- if (proto !== Object.prototype && proto !== null) return false;
377
- for (const k in value) if (!isJSONSafe(value[k])) return false;
378
- return true;
379
- }
380
395
  function serializeArguments(args) {
381
396
  if (!config.serializeArgs) {
382
- 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.");
397
+ 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.");
383
398
  }
384
399
  return config.serializeArgs(args);
385
400
  }
@@ -397,6 +412,15 @@ function configureServerFunctionsClient({
397
412
  if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
398
413
  }
399
414
  let INSTANCE = 0;
415
+ let rpcProvided = false;
416
+ function provideRPC() {
417
+ if (rpcProvided) return;
418
+ rpcProvided = true;
419
+ provideServerFunctionRPC({
420
+ GET,
421
+ decodeResponse
422
+ });
423
+ }
400
424
  async function createRequest(base, id, instance, options, meta) {
401
425
  const headers = {
402
426
  ...options.headers,
@@ -436,31 +460,37 @@ async function initializeResponse(base, id, instance, options, args, meta) {
436
460
  }, meta);
437
461
  }
438
462
  }
439
- if (isJSONSafe(args)) {
440
- return createRequest(base, id, instance, {
441
- ...options,
442
- body: JSON.stringify(args),
443
- headers: {
444
- ...options.headers,
445
- "Content-Type": "application/json",
446
- [BODY_FORMAT_HEADER]: BodyFormat.Json
447
- }
448
- }, meta);
449
- }
450
- if (args.length > 1) {
451
- const trailing = getHeadersAndBody(args[args.length - 1]);
452
- const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
453
- if (trailing && isJSONSafe(leading)) {
454
- const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
455
- return createRequest(target, id, instance, {
463
+ try {
464
+ if (isJSONSafe(args)) {
465
+ return createRequest(base, id, instance, {
456
466
  ...options,
457
- body: trailing.body,
467
+ body: JSON.stringify(args),
458
468
  headers: {
459
469
  ...options.headers,
460
- ...trailing.headers
470
+ "Content-Type": "application/json",
471
+ [BODY_FORMAT_HEADER]: BodyFormat.Json
461
472
  }
462
473
  }, meta);
463
474
  }
475
+ } catch {
476
+ }
477
+ if (args.length > 1) {
478
+ try {
479
+ const trailing = getHeadersAndBody(args[args.length - 1]);
480
+ const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
481
+ if (trailing && isJSONSafe(leading)) {
482
+ const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
483
+ return createRequest(target, id, instance, {
484
+ ...options,
485
+ body: trailing.body,
486
+ headers: {
487
+ ...options.headers,
488
+ ...trailing.headers
489
+ }
490
+ }, meta);
491
+ }
492
+ } catch {
493
+ }
464
494
  }
465
495
  return createRequest(base, id, instance, {
466
496
  ...options,
@@ -512,6 +542,7 @@ async function fetchServerFunction(base, id, options, args, meta, callArgs = arg
512
542
  return result;
513
543
  }
514
544
  function createServerReference(id, name, base) {
545
+ provideRPC();
515
546
  const metadata = name === undefined ? {} : {
516
547
  name
517
548
  };
@@ -542,6 +573,7 @@ function GET(fn) {
542
573
  if (!isServerFunction(fn)) {
543
574
  throw new Error("GET expects a server function reference");
544
575
  }
576
+ provideRPC();
545
577
  const id = fn.id;
546
578
  const metadata = {
547
579
  ...getServerFunctionMetadata(fn)
@@ -582,4 +614,4 @@ function getServerFunctionInvocation() {
582
614
  return undefined;
583
615
  }
584
616
 
585
- export { ChunkReader, ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, REVALIDATE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsClient, createChunk, createServerReference, decodeErrorHeaderValue, decodeResponse, decodeResponsePayload, deserializeStream, encodeErrorHeaderValue, frameAddress, getFlightDataConsumer, getServerFunctionInvocation, getServerFunctionMetadata, getServerFunctionsCodec, hasFlashCookie, isServerFunction, registerServerReference, subscribeFlightData, withMeta };
617
+ export { ChunkReader, ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, REVALIDATE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsClient, createChunk, createServerReference, decodeErrorHeaderValue, decodeResponse, decodeResponsePayload, deserializeStream, encodeErrorHeaderValue, frameAddress, getFlightDataConsumer, getServerFunctionInvocation, getServerFunctionMetadata, getServerFunctionsCodec, hasFlashCookie, isServerFunction, registerServerReference, serializeString, subscribeFlightData, withMeta };
@@ -0,0 +1,11 @@
1
+ 'use strict';
2
+
3
+ var client = require('@solidjs/web/server-functions/client');
4
+
5
+ function enableRichArguments() {
6
+ client.configureServerFunctionsClient({
7
+ serializeArgs: args => client.serializeString(args, client.getServerFunctionsCodec())
8
+ });
9
+ }
10
+
11
+ exports.enableRichArguments = enableRichArguments;