@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
@@ -1,4 +1,4 @@
1
- import { fromCrossJSON, Feature, toCrossJSONStream } from 'seroval';
1
+ import { fromCrossJSON, Feature } from 'seroval';
2
2
  import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
3
3
 
4
4
  Feature.AggregateError | Feature.BigIntTypedArray;
@@ -21,19 +21,6 @@ function resolveCodecOptions({
21
21
  depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
22
22
  };
23
23
  }
24
- function serializeJSON(value, {
25
- onParse,
26
- onDone,
27
- onError,
28
- ...codecOptions
29
- }) {
30
- return toCrossJSONStream(value, {
31
- onParse,
32
- onDone,
33
- onError,
34
- ...resolveCodecOptions(codecOptions)
35
- });
36
- }
37
24
  function createJSONDeserializer(options) {
38
25
  const refs = new Map();
39
26
  const resolved = resolveCodecOptions(options);
@@ -51,9 +38,6 @@ const codecConfig = {
51
38
  function configureServerFunctionsCodec(codec) {
52
39
  codecConfig.codec = codec;
53
40
  }
54
- function getServerFunctionsCodec() {
55
- return codecConfig.codec;
56
- }
57
41
  const flightConfig = {
58
42
  consumer: undefined
59
43
  };
@@ -83,6 +67,31 @@ function withMeta(fn, meta) {
83
67
  return fn;
84
68
  }
85
69
  const FUNCTION_HEADER = "X-Server-Function-Id";
70
+ const ERROR_HEADER = "X-Server-Function-Error";
71
+ const ERROR_HEADER_MARKER = "=?1?";
72
+ const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
73
+ function encodeErrorHeaderValue(value) {
74
+ let stripped = String(value).replace(/[\r\n]+/g, "");
75
+ if (!NEEDS_ENCODING.test(stripped) && !stripped.startsWith(ERROR_HEADER_MARKER) && stripped === stripped.trim()) {
76
+ return stripped;
77
+ }
78
+ if (typeof stripped.toWellFormed === "function") {
79
+ stripped = stripped.toWellFormed();
80
+ } else {
81
+ stripped = stripped.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
82
+ }
83
+ return ERROR_HEADER_MARKER + encodeURIComponent(stripped);
84
+ }
85
+ function decodeErrorHeaderValue(value) {
86
+ if (typeof value !== "string" || !value.startsWith(ERROR_HEADER_MARKER)) {
87
+ return value;
88
+ }
89
+ try {
90
+ return decodeURIComponent(value.slice(ERROR_HEADER_MARKER.length));
91
+ } catch {
92
+ return value;
93
+ }
94
+ }
86
95
  const INSTANCE_HEADER = "X-Server-Function-Instance";
87
96
  const BODY_FORMAT_HEADER = "X-Server-Function-Format";
88
97
  const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
@@ -95,7 +104,8 @@ const BodyFormat = {
95
104
  Blob: "4",
96
105
  File: "5",
97
106
  ArrayBuffer: "6",
98
- Uint8Array: "7"
107
+ Uint8Array: "7",
108
+ Json: "8"
99
109
  };
100
110
  function getHeadersAndBody(body) {
101
111
  switch (true) {
@@ -165,6 +175,8 @@ async function extractBody(source, codecOptions) {
165
175
  switch (true) {
166
176
  case format === BodyFormat.Serialized:
167
177
  return await deserializeStream(clone, codecOptions);
178
+ case format === BodyFormat.Json:
179
+ return JSON.parse(await clone.text());
168
180
  case format === BodyFormat.String:
169
181
  return await clone.text();
170
182
  case format === BodyFormat.File:
@@ -187,17 +199,6 @@ async function extractBody(source, codecOptions) {
187
199
  }
188
200
  return undefined;
189
201
  }
190
- function createChunk(data) {
191
- const encodeData = new TextEncoder().encode(data);
192
- const bytes = encodeData.length;
193
- const baseHex = bytes.toString(16);
194
- const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
195
- const head = new TextEncoder().encode(`;0x${totalHex};`);
196
- const chunk = new Uint8Array(12 + bytes);
197
- chunk.set(head);
198
- chunk.set(encodeData, 12);
199
- return chunk;
200
- }
201
202
  class ChunkReader {
202
203
  constructor(stream) {
203
204
  this.reader = stream.getReader();
@@ -254,28 +255,6 @@ class ChunkReader {
254
255
  }
255
256
  }
256
257
  }
257
- function serializeStream(value, codecOptions) {
258
- return new ReadableStream({
259
- start(controller) {
260
- serializeJSON(value, {
261
- ...codecOptions,
262
- onParse(node) {
263
- controller.enqueue(createChunk(JSON.stringify(node)));
264
- },
265
- onDone() {
266
- controller.close();
267
- },
268
- onError(error) {
269
- controller.error(error);
270
- }
271
- });
272
- }
273
- });
274
- }
275
- async function serializeString(value, codecOptions) {
276
- const response = new Response(serializeStream(value, codecOptions));
277
- return await response.text();
278
- }
279
258
  async function deserializeStream(source, codecOptions) {
280
259
  if (!source.body) {
281
260
  throw new Error("missing body");
@@ -299,16 +278,43 @@ async function decodeResponse(response, codecOptions) {
299
278
 
300
279
  const config = {
301
280
  endpoint: "/_server",
302
- prepareRequest: undefined
281
+ prepareRequest: undefined,
282
+ responseHandler: undefined,
283
+ serializeArgs: undefined
303
284
  };
285
+ function isJSONSafe(value) {
286
+ if (value === null) return true;
287
+ const t = typeof value;
288
+ if (t === "string" || t === "boolean") return true;
289
+ if (t === "number") return Number.isFinite(value);
290
+ if (t !== "object") return false;
291
+ if (Array.isArray(value)) {
292
+ for (const v of value) if (!isJSONSafe(v)) return false;
293
+ return true;
294
+ }
295
+ const proto = Object.getPrototypeOf(value);
296
+ if (proto !== Object.prototype && proto !== null) return false;
297
+ for (const k in value) if (!isJSONSafe(value[k])) return false;
298
+ return true;
299
+ }
300
+ function serializeArguments(args) {
301
+ if (!config.serializeArgs) {
302
+ 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.");
303
+ }
304
+ return config.serializeArgs(args);
305
+ }
304
306
  function configureServerFunctionsClient({
305
307
  endpoint,
306
308
  codec,
307
- prepareRequest
309
+ prepareRequest,
310
+ responseHandler,
311
+ serializeArgs
308
312
  } = {}) {
309
313
  if (endpoint !== undefined) config.endpoint = endpoint;
310
314
  if (codec !== undefined) configureServerFunctionsCodec(codec);
311
315
  if (prepareRequest !== undefined) config.prepareRequest = prepareRequest;
316
+ if (responseHandler !== undefined) config.responseHandler = responseHandler;
317
+ if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
312
318
  }
313
319
  let INSTANCE = 0;
314
320
  async function createRequest(base, id, instance, options, meta) {
@@ -350,9 +356,20 @@ async function initializeResponse(base, id, instance, options, args, meta) {
350
356
  }, meta);
351
357
  }
352
358
  }
359
+ if (isJSONSafe(args)) {
360
+ return createRequest(base, id, instance, {
361
+ ...options,
362
+ body: JSON.stringify(args),
363
+ headers: {
364
+ ...options.headers,
365
+ "Content-Type": "application/json",
366
+ [BODY_FORMAT_HEADER]: BodyFormat.Json
367
+ }
368
+ }, meta);
369
+ }
353
370
  return createRequest(base, id, instance, {
354
371
  ...options,
355
- body: await serializeString(args, getServerFunctionsCodec()),
372
+ body: await serializeArguments(args),
356
373
  headers: {
357
374
  ...options.headers,
358
375
  "Content-Type": "text/plain",
@@ -362,7 +379,21 @@ async function initializeResponse(base, id, instance, options, args, meta) {
362
379
  }
363
380
  async function fetchServerFunction(base, id, options, args, meta) {
364
381
  const instance = `server-function:${INSTANCE++}`;
382
+ const handler = config.responseHandler;
383
+ const context = handler && handler.capture ? handler.capture({
384
+ id,
385
+ meta
386
+ }) : undefined;
365
387
  const response = await initializeResponse(base, id, instance, options, args, meta);
388
+ if (handler) {
389
+ const handled = handler.handle(response, {
390
+ id,
391
+ meta,
392
+ args,
393
+ context
394
+ });
395
+ if (handled !== undefined) return handled;
396
+ }
366
397
  if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
367
398
  const consumer = getFlightDataConsumer();
368
399
  if (consumer) {
@@ -370,7 +401,7 @@ async function fetchServerFunction(base, id, options, args, meta) {
370
401
  await consumer(payload.data, {
371
402
  response
372
403
  });
373
- if (response.headers.has("X-Server-Function-Error") && !response.headers.has("Location") && !response.headers.has("X-Revalidate")) {
404
+ if (response.headers.has(ERROR_HEADER) && !response.headers.has("Location") && !response.headers.has("X-Revalidate")) {
374
405
  throw payload.value;
375
406
  }
376
407
  return payload.value;
@@ -380,22 +411,33 @@ async function fetchServerFunction(base, id, options, args, meta) {
380
411
  return response;
381
412
  }
382
413
  const result = await decodeResponse(response.clone());
383
- if (response.headers.has("X-Server-Function-Error")) {
414
+ if (response.headers.has(ERROR_HEADER)) {
384
415
  throw result;
385
416
  }
386
417
  return result;
387
418
  }
388
- function createServerReference(id, name) {
419
+ function createServerReference(id, name, base) {
389
420
  const metadata = name === undefined ? {} : {
390
421
  name
391
422
  };
392
- const fn = (...args) => fetchServerFunction(config.endpoint, id, {}, args, metadata);
423
+ const fn = (...args) => {
424
+ const handler = config.responseHandler;
425
+ if (handler && handler.intercept) {
426
+ const hit = handler.intercept({
427
+ id,
428
+ meta: metadata,
429
+ args
430
+ });
431
+ if (hit !== undefined) return hit;
432
+ }
433
+ return fetchServerFunction(base || config.endpoint, id, {}, args, metadata);
434
+ };
393
435
  fn[SERVER_FUNCTION_METADATA] = metadata;
394
436
  return new Proxy(fn, {
395
437
  get(target, prop) {
396
438
  if (prop === "id") return id;
397
439
  if (prop === "url") {
398
- return `${config.endpoint}?id=${encodeURIComponent(id)}`;
440
+ return base || `${config.endpoint}?id=${encodeURIComponent(id)}`;
399
441
  }
400
442
  return target[prop];
401
443
  }
@@ -410,9 +452,19 @@ function GET(fn) {
410
452
  ...getServerFunctionMetadata(fn)
411
453
  };
412
454
  const wrapped = async (...args) => {
455
+ const handler = config.responseHandler;
456
+ if (handler && handler.intercept) {
457
+ const hit = handler.intercept({
458
+ id,
459
+ meta: metadata,
460
+ args
461
+ });
462
+ if (hit !== undefined) return hit;
463
+ }
413
464
  let base = `${config.endpoint}?id=${encodeURIComponent(id)}`;
414
465
  if (args.length) {
415
- base += `&args=${encodeURIComponent(await serializeString(args, getServerFunctionsCodec()))}`;
466
+ const encoded = isJSONSafe(args) ? JSON.stringify(args) : await serializeArguments(args);
467
+ base += `&args=${encodeURIComponent(encoded)}`;
416
468
  }
417
469
  return fetchServerFunction(base, id, {
418
470
  method: "GET"
@@ -432,4 +484,4 @@ function registerServerReference() {
432
484
  throw new Error("registerServerReference must not be called in the client build");
433
485
  }
434
486
 
435
- export { FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, configureServerFunctionsClient, createServerReference, decodeResponse, getServerFunctionMetadata, isServerFunction, registerServerReference, subscribeFlightData, withMeta };
487
+ export { ERROR_HEADER, FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, configureServerFunctionsClient, createServerReference, decodeErrorHeaderValue, decodeResponse, encodeErrorHeaderValue, getServerFunctionMetadata, isServerFunction, registerServerReference, subscribeFlightData, withMeta };
@@ -88,6 +88,31 @@ function withMeta(fn, meta) {
88
88
  return fn;
89
89
  }
90
90
  const FUNCTION_HEADER = "X-Server-Function-Id";
91
+ const ERROR_HEADER = "X-Server-Function-Error";
92
+ const ERROR_HEADER_MARKER = "=?1?";
93
+ const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
94
+ function encodeErrorHeaderValue(value) {
95
+ let stripped = String(value).replace(/[\r\n]+/g, "");
96
+ if (!NEEDS_ENCODING.test(stripped) && !stripped.startsWith(ERROR_HEADER_MARKER) && stripped === stripped.trim()) {
97
+ return stripped;
98
+ }
99
+ if (typeof stripped.toWellFormed === "function") {
100
+ stripped = stripped.toWellFormed();
101
+ } else {
102
+ stripped = stripped.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
103
+ }
104
+ return ERROR_HEADER_MARKER + encodeURIComponent(stripped);
105
+ }
106
+ function decodeErrorHeaderValue(value) {
107
+ if (typeof value !== "string" || !value.startsWith(ERROR_HEADER_MARKER)) {
108
+ return value;
109
+ }
110
+ try {
111
+ return decodeURIComponent(value.slice(ERROR_HEADER_MARKER.length));
112
+ } catch {
113
+ return value;
114
+ }
115
+ }
91
116
  const INSTANCE_HEADER = "X-Server-Function-Instance";
92
117
  const BODY_FORMAT_HEADER = "X-Server-Function-Format";
93
118
  const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
@@ -100,7 +125,8 @@ const BodyFormat = {
100
125
  Blob: "4",
101
126
  File: "5",
102
127
  ArrayBuffer: "6",
103
- Uint8Array: "7"
128
+ Uint8Array: "7",
129
+ Json: "8"
104
130
  };
105
131
  function getHeadersAndBody(body) {
106
132
  switch (true) {
@@ -170,6 +196,8 @@ async function extractBody(source, codecOptions) {
170
196
  switch (true) {
171
197
  case format === BodyFormat.Serialized:
172
198
  return await deserializeStream(clone, codecOptions);
199
+ case format === BodyFormat.Json:
200
+ return JSON.parse(await clone.text());
173
201
  case format === BodyFormat.String:
174
202
  return await clone.text();
175
203
  case format === BodyFormat.File:
@@ -304,16 +332,19 @@ async function decodeResponse(response, codecOptions) {
304
332
  const config = {
305
333
  provideEvent: undefined,
306
334
  collectFlightData: undefined,
335
+ transformDirectResult: undefined,
307
336
  endpoint: "/_server"
308
337
  };
309
338
  function configureServerFunctionsServer({
310
339
  provideEvent,
311
340
  collectFlightData,
341
+ transformDirectResult,
312
342
  endpoint,
313
343
  codec
314
344
  } = {}) {
315
345
  if (provideEvent !== undefined) config.provideEvent = provideEvent;
316
346
  if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
347
+ if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
317
348
  if (endpoint !== undefined) config.endpoint = endpoint;
318
349
  if (codec !== undefined) configureServerFunctionsCodec(codec);
319
350
  }
@@ -372,9 +403,20 @@ function createServerReference({
372
403
  id
373
404
  };
374
405
  evt.serverOnly = true;
375
- return provideEvent(evt, () => {
406
+ const result = provideEvent(evt, () => {
376
407
  return fn.apply(thisArg, args);
377
408
  });
409
+ const transform = config.transformDirectResult;
410
+ if (transform && result && typeof result.then === "function") {
411
+ return result.then(value => transform(value, {
412
+ id,
413
+ event: evt
414
+ }));
415
+ }
416
+ return transform ? transform(result, {
417
+ id,
418
+ event: evt
419
+ }) : result;
378
420
  }
379
421
  });
380
422
  }
@@ -400,7 +442,8 @@ function resolveFunctionId(request, url) {
400
442
  }
401
443
  async function parseArguments(request, url, instance, codec) {
402
444
  const parsed = [];
403
- if (!instance || request.method === "GET") {
445
+ const bodyFormat = request.method === "POST" ? request.headers.get(BODY_FORMAT_HEADER) : null;
446
+ if (!instance || request.method === "GET" || bodyFormat !== BodyFormat.Serialized) {
404
447
  const args = url.searchParams.get("args");
405
448
  if (args) {
406
449
  const result = args.startsWith(";0x") ? await deserializeString(args, codec) : JSON.parse(args);
@@ -410,9 +453,8 @@ async function parseArguments(request, url, instance, codec) {
410
453
  }
411
454
  }
412
455
  if (request.method === "POST" && request.body !== null) {
413
- const format = request.headers.get(BODY_FORMAT_HEADER);
414
456
  const decoded = await extractBody(request.clone(), codec);
415
- if (format === BodyFormat.Serialized) {
457
+ if (bodyFormat === BodyFormat.Serialized || bodyFormat === BodyFormat.Json) {
416
458
  return decoded;
417
459
  }
418
460
  parsed.push(decoded);
@@ -470,12 +512,11 @@ async function handleServerFunctionRequest(request, options = {}) {
470
512
  status: 404
471
513
  });
472
514
  }
473
- const allowedMethod = METHODS.get(functionId) || "POST";
474
- if (request.method === "GET" !== (allowedMethod === "GET")) {
515
+ if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
475
516
  return new Response(process.env.NODE_ENV === "development" ? `Method not allowed for server function: ${functionId}` : null, {
476
517
  status: 405,
477
518
  headers: {
478
- Allow: allowedMethod
519
+ Allow: "POST"
479
520
  }
480
521
  });
481
522
  }
@@ -594,7 +635,7 @@ async function handleServerFunctionRequest(request, options = {}) {
594
635
  thrown: true
595
636
  });
596
637
  }
597
- headers.set("X-Server-Function-Error", "true");
638
+ headers.set(ERROR_HEADER, "true");
598
639
  if (!instance) {
599
640
  if (options.handleNoJS) return options.handleNoJS(x, request, parsed, true);
600
641
  if (x instanceof Response) return x;
@@ -609,18 +650,21 @@ async function handleServerFunctionRequest(request, options = {}) {
609
650
  });
610
651
  }
611
652
  const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
612
- headers.set("X-Server-Function-Error", error.replace(/[\r\n]+/g, ""));
653
+ headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
613
654
  return encodeResult(x, headers, 200, codec);
614
655
  }
615
656
  }
616
657
 
658
+ exports.ERROR_HEADER = ERROR_HEADER;
617
659
  exports.FUNCTION_HEADER = FUNCTION_HEADER;
618
660
  exports.GET = GET;
619
661
  exports.INSTANCE_HEADER = INSTANCE_HEADER;
620
662
  exports.SINGLE_FLIGHT_HEADER = SINGLE_FLIGHT_HEADER;
621
663
  exports.configureServerFunctionsServer = configureServerFunctionsServer;
622
664
  exports.createServerReference = createServerReference;
665
+ exports.decodeErrorHeaderValue = decodeErrorHeaderValue;
623
666
  exports.decodeResponse = decodeResponse;
667
+ exports.encodeErrorHeaderValue = encodeErrorHeaderValue;
624
668
  exports.getServerFunction = getServerFunction;
625
669
  exports.getServerFunctionMeta = getServerFunctionMeta;
626
670
  exports.getServerFunctionMetadata = getServerFunctionMetadata;
@@ -86,6 +86,31 @@ function withMeta(fn, meta) {
86
86
  return fn;
87
87
  }
88
88
  const FUNCTION_HEADER = "X-Server-Function-Id";
89
+ const ERROR_HEADER = "X-Server-Function-Error";
90
+ const ERROR_HEADER_MARKER = "=?1?";
91
+ const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
92
+ function encodeErrorHeaderValue(value) {
93
+ let stripped = String(value).replace(/[\r\n]+/g, "");
94
+ if (!NEEDS_ENCODING.test(stripped) && !stripped.startsWith(ERROR_HEADER_MARKER) && stripped === stripped.trim()) {
95
+ return stripped;
96
+ }
97
+ if (typeof stripped.toWellFormed === "function") {
98
+ stripped = stripped.toWellFormed();
99
+ } else {
100
+ stripped = stripped.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
101
+ }
102
+ return ERROR_HEADER_MARKER + encodeURIComponent(stripped);
103
+ }
104
+ function decodeErrorHeaderValue(value) {
105
+ if (typeof value !== "string" || !value.startsWith(ERROR_HEADER_MARKER)) {
106
+ return value;
107
+ }
108
+ try {
109
+ return decodeURIComponent(value.slice(ERROR_HEADER_MARKER.length));
110
+ } catch {
111
+ return value;
112
+ }
113
+ }
89
114
  const INSTANCE_HEADER = "X-Server-Function-Instance";
90
115
  const BODY_FORMAT_HEADER = "X-Server-Function-Format";
91
116
  const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
@@ -98,7 +123,8 @@ const BodyFormat = {
98
123
  Blob: "4",
99
124
  File: "5",
100
125
  ArrayBuffer: "6",
101
- Uint8Array: "7"
126
+ Uint8Array: "7",
127
+ Json: "8"
102
128
  };
103
129
  function getHeadersAndBody(body) {
104
130
  switch (true) {
@@ -168,6 +194,8 @@ async function extractBody(source, codecOptions) {
168
194
  switch (true) {
169
195
  case format === BodyFormat.Serialized:
170
196
  return await deserializeStream(clone, codecOptions);
197
+ case format === BodyFormat.Json:
198
+ return JSON.parse(await clone.text());
171
199
  case format === BodyFormat.String:
172
200
  return await clone.text();
173
201
  case format === BodyFormat.File:
@@ -302,16 +330,19 @@ async function decodeResponse(response, codecOptions) {
302
330
  const config = {
303
331
  provideEvent: undefined,
304
332
  collectFlightData: undefined,
333
+ transformDirectResult: undefined,
305
334
  endpoint: "/_server"
306
335
  };
307
336
  function configureServerFunctionsServer({
308
337
  provideEvent,
309
338
  collectFlightData,
339
+ transformDirectResult,
310
340
  endpoint,
311
341
  codec
312
342
  } = {}) {
313
343
  if (provideEvent !== undefined) config.provideEvent = provideEvent;
314
344
  if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
345
+ if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
315
346
  if (endpoint !== undefined) config.endpoint = endpoint;
316
347
  if (codec !== undefined) configureServerFunctionsCodec(codec);
317
348
  }
@@ -370,9 +401,20 @@ function createServerReference({
370
401
  id
371
402
  };
372
403
  evt.serverOnly = true;
373
- return provideEvent(evt, () => {
404
+ const result = provideEvent(evt, () => {
374
405
  return fn.apply(thisArg, args);
375
406
  });
407
+ const transform = config.transformDirectResult;
408
+ if (transform && result && typeof result.then === "function") {
409
+ return result.then(value => transform(value, {
410
+ id,
411
+ event: evt
412
+ }));
413
+ }
414
+ return transform ? transform(result, {
415
+ id,
416
+ event: evt
417
+ }) : result;
376
418
  }
377
419
  });
378
420
  }
@@ -398,7 +440,8 @@ function resolveFunctionId(request, url) {
398
440
  }
399
441
  async function parseArguments(request, url, instance, codec) {
400
442
  const parsed = [];
401
- if (!instance || request.method === "GET") {
443
+ const bodyFormat = request.method === "POST" ? request.headers.get(BODY_FORMAT_HEADER) : null;
444
+ if (!instance || request.method === "GET" || bodyFormat !== BodyFormat.Serialized) {
402
445
  const args = url.searchParams.get("args");
403
446
  if (args) {
404
447
  const result = args.startsWith(";0x") ? await deserializeString(args, codec) : JSON.parse(args);
@@ -408,9 +451,8 @@ async function parseArguments(request, url, instance, codec) {
408
451
  }
409
452
  }
410
453
  if (request.method === "POST" && request.body !== null) {
411
- const format = request.headers.get(BODY_FORMAT_HEADER);
412
454
  const decoded = await extractBody(request.clone(), codec);
413
- if (format === BodyFormat.Serialized) {
455
+ if (bodyFormat === BodyFormat.Serialized || bodyFormat === BodyFormat.Json) {
414
456
  return decoded;
415
457
  }
416
458
  parsed.push(decoded);
@@ -468,12 +510,11 @@ async function handleServerFunctionRequest(request, options = {}) {
468
510
  status: 404
469
511
  });
470
512
  }
471
- const allowedMethod = METHODS.get(functionId) || "POST";
472
- if (request.method === "GET" !== (allowedMethod === "GET")) {
513
+ if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
473
514
  return new Response(process.env.NODE_ENV === "development" ? `Method not allowed for server function: ${functionId}` : null, {
474
515
  status: 405,
475
516
  headers: {
476
- Allow: allowedMethod
517
+ Allow: "POST"
477
518
  }
478
519
  });
479
520
  }
@@ -592,7 +633,7 @@ async function handleServerFunctionRequest(request, options = {}) {
592
633
  thrown: true
593
634
  });
594
635
  }
595
- headers.set("X-Server-Function-Error", "true");
636
+ headers.set(ERROR_HEADER, "true");
596
637
  if (!instance) {
597
638
  if (options.handleNoJS) return options.handleNoJS(x, request, parsed, true);
598
639
  if (x instanceof Response) return x;
@@ -607,9 +648,9 @@ async function handleServerFunctionRequest(request, options = {}) {
607
648
  });
608
649
  }
609
650
  const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
610
- headers.set("X-Server-Function-Error", error.replace(/[\r\n]+/g, ""));
651
+ headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
611
652
  return encodeResult(x, headers, 200, codec);
612
653
  }
613
654
  }
614
655
 
615
- export { FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, configureServerFunctionsServer, createServerReference, decodeResponse, getServerFunction, getServerFunctionMeta, getServerFunctionMetadata, handleServerFunctionRequest, isServerFunction, registerServerFunction, registerServerReference, subscribeFlightData, withMeta };
656
+ export { ERROR_HEADER, FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, configureServerFunctionsServer, createServerReference, decodeErrorHeaderValue, decodeResponse, encodeErrorHeaderValue, getServerFunction, getServerFunctionMeta, getServerFunctionMetadata, handleServerFunctionRequest, isServerFunction, registerServerFunction, registerServerReference, subscribeFlightData, withMeta };
package/types/client.d.ts CHANGED
@@ -66,6 +66,32 @@ export function assign(
66
66
  ): void;
67
67
  export function setAttribute(node: Element, name: string, value: string): void;
68
68
  export function setAttributeNS(node: Element, namespace: string, name: string, value: string): void;
69
+ /**
70
+ * Register a consumer for compiler-emitted element claims. Compiled DOM
71
+ * output claims navigation-relevant elements (`a[href]`, `form[action]`) at
72
+ * creation, and compiler-owned writes to `href`/`action` re-invoke the same
73
+ * handlers — so handlers must be idempotent and must check the element's
74
+ * relevance themselves (rechecks can fire for any element whose
75
+ * `href`/`action` is written, e.g. `<link href>`). Handlers run under the
76
+ * reactive owner current at element creation; scope per-element state and
77
+ * cleanup through your own reactive system. Dormant until registered —
78
+ * without a handler the emitted claims are null checks. Returns an
79
+ * unregister function.
80
+ */
81
+ export function registerElementClaim(handler: (element: Element) => void): () => void;
82
+ /**
83
+ * Claim `node` for registered consumers (see `registerElementClaim`).
84
+ * Emitted by the compiler at element creation; idempotent by contract.
85
+ */
86
+ export function claimElement<T extends Element>(node: T): T;
87
+ /**
88
+ * Sweep-claim every navigation-relevant element (`a[href]`, `form[action]`)
89
+ * in `root` — the subtree equivalent of the per-element `claimElement`
90
+ * compiled output emits, for content that becomes live DOM without compiled
91
+ * creation code (frame streams, adopted SSR ranges). Dormant without a
92
+ * registered consumer.
93
+ */
94
+ export function claimElementTree<T extends Node>(root: T): T;
69
95
  export function className(node: Element, value: JSX.ClassValue, prev?: JSX.ClassValue): void;
70
96
  export function setProperty(node: Element, name: string, value: any): void;
71
97
  export function setStyleProperty(node: Element, name: string, value: any): void;
package/types/core.d.ts CHANGED
@@ -1,3 +1,4 @@
1
- export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope } from "solid-js";
1
+ export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration } from "solid-js";
2
2
  export declare const effect: (fn: any, effectFn: any, options: any) => void;
3
3
  export declare const memo: (fn: any) => import("solid-js").SourceAccessor<any>;
4
+ export declare const runWithHydrationScope: (id: any, fn: any) => unknown;