@solidjs/web 2.0.0-beta.22 → 2.0.0-beta.24

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 (46) hide show
  1. package/dist/dev.cjs +25 -1
  2. package/dist/dev.js +25 -2
  3. package/dist/server.cjs +109 -45
  4. package/dist/server.js +109 -46
  5. package/dist/web.cjs +25 -1
  6. package/dist/web.js +25 -2
  7. package/frames/dist/client.cjs +1467 -0
  8. package/frames/dist/client.js +1455 -0
  9. package/frames/dist/server.cjs +1723 -0
  10. package/frames/dist/server.js +1712 -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 +82 -55
  18. package/server-functions/dist/client.js +83 -56
  19. package/server-functions/dist/server.cjs +28 -7
  20. package/server-functions/dist/server.js +28 -7
  21. package/types/client.d.ts +8 -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 +222 -0
  25. package/types/frames/frame-sink.d.ts +145 -0
  26. package/types/frames/frame-transport.d.ts +106 -0
  27. package/types/frames/serializer.d.ts +151 -0
  28. package/types/frames/server.d.ts +21 -0
  29. package/types/serializer.d.ts +12 -0
  30. package/types/server-functions/client.d.ts +24 -0
  31. package/types/server-functions/server.d.ts +17 -0
  32. package/types/server-functions/shared.d.ts +17 -0
  33. package/types/server.d.ts +2 -0
  34. package/types-cjs/client.d.cts +8 -0
  35. package/types-cjs/core.d.cts +2 -1
  36. package/types-cjs/frames/client.d.cts +53 -0
  37. package/types-cjs/frames/frame-client.d.cts +222 -0
  38. package/types-cjs/frames/frame-sink.d.cts +145 -0
  39. package/types-cjs/frames/frame-transport.d.cts +106 -0
  40. package/types-cjs/frames/serializer.d.cts +151 -0
  41. package/types-cjs/frames/server.d.cts +21 -0
  42. package/types-cjs/serializer.d.cts +12 -0
  43. package/types-cjs/server-functions/client.d.cts +24 -0
  44. package/types-cjs/server-functions/server.d.cts +17 -0
  45. package/types-cjs/server-functions/shared.d.cts +17 -0
  46. package/types-cjs/server.d.cts +2 -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
  };
@@ -120,7 +104,8 @@ const BodyFormat = {
120
104
  Blob: "4",
121
105
  File: "5",
122
106
  ArrayBuffer: "6",
123
- Uint8Array: "7"
107
+ Uint8Array: "7",
108
+ Json: "8"
124
109
  };
125
110
  function getHeadersAndBody(body) {
126
111
  switch (true) {
@@ -190,6 +175,8 @@ async function extractBody(source, codecOptions) {
190
175
  switch (true) {
191
176
  case format === BodyFormat.Serialized:
192
177
  return await deserializeStream(clone, codecOptions);
178
+ case format === BodyFormat.Json:
179
+ return JSON.parse(await clone.text());
193
180
  case format === BodyFormat.String:
194
181
  return await clone.text();
195
182
  case format === BodyFormat.File:
@@ -212,17 +199,6 @@ async function extractBody(source, codecOptions) {
212
199
  }
213
200
  return undefined;
214
201
  }
215
- function createChunk(data) {
216
- const encodeData = new TextEncoder().encode(data);
217
- const bytes = encodeData.length;
218
- const baseHex = bytes.toString(16);
219
- const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
220
- const head = new TextEncoder().encode(`;0x${totalHex};`);
221
- const chunk = new Uint8Array(12 + bytes);
222
- chunk.set(head);
223
- chunk.set(encodeData, 12);
224
- return chunk;
225
- }
226
202
  class ChunkReader {
227
203
  constructor(stream) {
228
204
  this.reader = stream.getReader();
@@ -279,28 +255,6 @@ class ChunkReader {
279
255
  }
280
256
  }
281
257
  }
282
- function serializeStream(value, codecOptions) {
283
- return new ReadableStream({
284
- start(controller) {
285
- serializeJSON(value, {
286
- ...codecOptions,
287
- onParse(node) {
288
- controller.enqueue(createChunk(JSON.stringify(node)));
289
- },
290
- onDone() {
291
- controller.close();
292
- },
293
- onError(error) {
294
- controller.error(error);
295
- }
296
- });
297
- }
298
- });
299
- }
300
- async function serializeString(value, codecOptions) {
301
- const response = new Response(serializeStream(value, codecOptions));
302
- return await response.text();
303
- }
304
258
  async function deserializeStream(source, codecOptions) {
305
259
  if (!source.body) {
306
260
  throw new Error("missing body");
@@ -324,16 +278,43 @@ async function decodeResponse(response, codecOptions) {
324
278
 
325
279
  const config = {
326
280
  endpoint: "/_server",
327
- prepareRequest: undefined
281
+ prepareRequest: undefined,
282
+ responseHandler: undefined,
283
+ serializeArgs: undefined
328
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
+ }
329
306
  function configureServerFunctionsClient({
330
307
  endpoint,
331
308
  codec,
332
- prepareRequest
309
+ prepareRequest,
310
+ responseHandler,
311
+ serializeArgs
333
312
  } = {}) {
334
313
  if (endpoint !== undefined) config.endpoint = endpoint;
335
314
  if (codec !== undefined) configureServerFunctionsCodec(codec);
336
315
  if (prepareRequest !== undefined) config.prepareRequest = prepareRequest;
316
+ if (responseHandler !== undefined) config.responseHandler = responseHandler;
317
+ if (serializeArgs !== undefined) config.serializeArgs = serializeArgs;
337
318
  }
338
319
  let INSTANCE = 0;
339
320
  async function createRequest(base, id, instance, options, meta) {
@@ -375,9 +356,20 @@ async function initializeResponse(base, id, instance, options, args, meta) {
375
356
  }, meta);
376
357
  }
377
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
+ }
378
370
  return createRequest(base, id, instance, {
379
371
  ...options,
380
- body: await serializeString(args, getServerFunctionsCodec()),
372
+ body: await serializeArguments(args),
381
373
  headers: {
382
374
  ...options.headers,
383
375
  "Content-Type": "text/plain",
@@ -387,7 +379,21 @@ async function initializeResponse(base, id, instance, options, args, meta) {
387
379
  }
388
380
  async function fetchServerFunction(base, id, options, args, meta) {
389
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;
390
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
+ }
391
397
  if (response.headers.has(SINGLE_FLIGHT_HEADER)) {
392
398
  const consumer = getFlightDataConsumer();
393
399
  if (consumer) {
@@ -414,7 +420,18 @@ function createServerReference(id, name, base) {
414
420
  const metadata = name === undefined ? {} : {
415
421
  name
416
422
  };
417
- const fn = (...args) => fetchServerFunction(base || 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
+ };
418
435
  fn[SERVER_FUNCTION_METADATA] = metadata;
419
436
  return new Proxy(fn, {
420
437
  get(target, prop) {
@@ -435,9 +452,19 @@ function GET(fn) {
435
452
  ...getServerFunctionMetadata(fn)
436
453
  };
437
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
+ }
438
464
  let base = `${config.endpoint}?id=${encodeURIComponent(id)}`;
439
465
  if (args.length) {
440
- base += `&args=${encodeURIComponent(await serializeString(args, getServerFunctionsCodec()))}`;
466
+ const encoded = isJSONSafe(args) ? JSON.stringify(args) : await serializeArguments(args);
467
+ base += `&args=${encodeURIComponent(encoded)}`;
441
468
  }
442
469
  return fetchServerFunction(base, id, {
443
470
  method: "GET"
@@ -125,7 +125,8 @@ const BodyFormat = {
125
125
  Blob: "4",
126
126
  File: "5",
127
127
  ArrayBuffer: "6",
128
- Uint8Array: "7"
128
+ Uint8Array: "7",
129
+ Json: "8"
129
130
  };
130
131
  function getHeadersAndBody(body) {
131
132
  switch (true) {
@@ -195,6 +196,8 @@ async function extractBody(source, codecOptions) {
195
196
  switch (true) {
196
197
  case format === BodyFormat.Serialized:
197
198
  return await deserializeStream(clone, codecOptions);
199
+ case format === BodyFormat.Json:
200
+ return JSON.parse(await clone.text());
198
201
  case format === BodyFormat.String:
199
202
  return await clone.text();
200
203
  case format === BodyFormat.File:
@@ -329,16 +332,22 @@ async function decodeResponse(response, codecOptions) {
329
332
  const config = {
330
333
  provideEvent: undefined,
331
334
  collectFlightData: undefined,
335
+ transformResult: undefined,
336
+ transformDirectResult: undefined,
332
337
  endpoint: "/_server"
333
338
  };
334
339
  function configureServerFunctionsServer({
335
340
  provideEvent,
336
341
  collectFlightData,
342
+ transformResult,
343
+ transformDirectResult,
337
344
  endpoint,
338
345
  codec
339
346
  } = {}) {
340
347
  if (provideEvent !== undefined) config.provideEvent = provideEvent;
341
348
  if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
349
+ if (transformResult !== undefined) config.transformResult = transformResult;
350
+ if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
342
351
  if (endpoint !== undefined) config.endpoint = endpoint;
343
352
  if (codec !== undefined) configureServerFunctionsCodec(codec);
344
353
  }
@@ -397,9 +406,20 @@ function createServerReference({
397
406
  id
398
407
  };
399
408
  evt.serverOnly = true;
400
- return provideEvent(evt, () => {
409
+ const result = provideEvent(evt, () => {
401
410
  return fn.apply(thisArg, args);
402
411
  });
412
+ const transform = config.transformDirectResult;
413
+ if (transform && result && typeof result.then === "function") {
414
+ return result.then(value => transform(value, {
415
+ id,
416
+ event: evt
417
+ }));
418
+ }
419
+ return transform ? transform(result, {
420
+ id,
421
+ event: evt
422
+ }) : result;
403
423
  }
404
424
  });
405
425
  }
@@ -437,7 +457,7 @@ async function parseArguments(request, url, instance, codec) {
437
457
  }
438
458
  if (request.method === "POST" && request.body !== null) {
439
459
  const decoded = await extractBody(request.clone(), codec);
440
- if (bodyFormat === BodyFormat.Serialized) {
460
+ if (bodyFormat === BodyFormat.Serialized || bodyFormat === BodyFormat.Json) {
441
461
  return decoded;
442
462
  }
443
463
  parsed.push(decoded);
@@ -509,6 +529,7 @@ async function handleServerFunctionRequest(request, options = {}) {
509
529
  };
510
530
  const provide = options.provideEvent || provideEvent;
511
531
  const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
532
+ const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
512
533
  const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
513
534
  const parsed = await parseArguments(request, url, instance, codec);
514
535
  const headers = new Headers();
@@ -519,8 +540,8 @@ async function handleServerFunctionRequest(request, options = {}) {
519
540
  };
520
541
  return serverFunction(...parsed);
521
542
  });
522
- if (options.transformResult) {
523
- result = await options.transformResult(event, result, {
543
+ if (transformResult) {
544
+ result = await transformResult(event, result, {
524
545
  instance,
525
546
  request
526
547
  });
@@ -575,8 +596,8 @@ async function handleServerFunctionRequest(request, options = {}) {
575
596
  return encodeResult(result, headers, status, codec);
576
597
  } catch (x) {
577
598
  if (x instanceof Response || isResponseEnvelope(x)) {
578
- if (options.transformResult) {
579
- x = await options.transformResult(event, x, {
599
+ if (transformResult) {
600
+ x = await transformResult(event, x, {
580
601
  instance,
581
602
  request,
582
603
  thrown: true
@@ -123,7 +123,8 @@ const BodyFormat = {
123
123
  Blob: "4",
124
124
  File: "5",
125
125
  ArrayBuffer: "6",
126
- Uint8Array: "7"
126
+ Uint8Array: "7",
127
+ Json: "8"
127
128
  };
128
129
  function getHeadersAndBody(body) {
129
130
  switch (true) {
@@ -193,6 +194,8 @@ async function extractBody(source, codecOptions) {
193
194
  switch (true) {
194
195
  case format === BodyFormat.Serialized:
195
196
  return await deserializeStream(clone, codecOptions);
197
+ case format === BodyFormat.Json:
198
+ return JSON.parse(await clone.text());
196
199
  case format === BodyFormat.String:
197
200
  return await clone.text();
198
201
  case format === BodyFormat.File:
@@ -327,16 +330,22 @@ async function decodeResponse(response, codecOptions) {
327
330
  const config = {
328
331
  provideEvent: undefined,
329
332
  collectFlightData: undefined,
333
+ transformResult: undefined,
334
+ transformDirectResult: undefined,
330
335
  endpoint: "/_server"
331
336
  };
332
337
  function configureServerFunctionsServer({
333
338
  provideEvent,
334
339
  collectFlightData,
340
+ transformResult,
341
+ transformDirectResult,
335
342
  endpoint,
336
343
  codec
337
344
  } = {}) {
338
345
  if (provideEvent !== undefined) config.provideEvent = provideEvent;
339
346
  if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
347
+ if (transformResult !== undefined) config.transformResult = transformResult;
348
+ if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
340
349
  if (endpoint !== undefined) config.endpoint = endpoint;
341
350
  if (codec !== undefined) configureServerFunctionsCodec(codec);
342
351
  }
@@ -395,9 +404,20 @@ function createServerReference({
395
404
  id
396
405
  };
397
406
  evt.serverOnly = true;
398
- return provideEvent(evt, () => {
407
+ const result = provideEvent(evt, () => {
399
408
  return fn.apply(thisArg, args);
400
409
  });
410
+ const transform = config.transformDirectResult;
411
+ if (transform && result && typeof result.then === "function") {
412
+ return result.then(value => transform(value, {
413
+ id,
414
+ event: evt
415
+ }));
416
+ }
417
+ return transform ? transform(result, {
418
+ id,
419
+ event: evt
420
+ }) : result;
401
421
  }
402
422
  });
403
423
  }
@@ -435,7 +455,7 @@ async function parseArguments(request, url, instance, codec) {
435
455
  }
436
456
  if (request.method === "POST" && request.body !== null) {
437
457
  const decoded = await extractBody(request.clone(), codec);
438
- if (bodyFormat === BodyFormat.Serialized) {
458
+ if (bodyFormat === BodyFormat.Serialized || bodyFormat === BodyFormat.Json) {
439
459
  return decoded;
440
460
  }
441
461
  parsed.push(decoded);
@@ -507,6 +527,7 @@ async function handleServerFunctionRequest(request, options = {}) {
507
527
  };
508
528
  const provide = options.provideEvent || provideEvent;
509
529
  const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
530
+ const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
510
531
  const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
511
532
  const parsed = await parseArguments(request, url, instance, codec);
512
533
  const headers = new Headers();
@@ -517,8 +538,8 @@ async function handleServerFunctionRequest(request, options = {}) {
517
538
  };
518
539
  return serverFunction(...parsed);
519
540
  });
520
- if (options.transformResult) {
521
- result = await options.transformResult(event, result, {
541
+ if (transformResult) {
542
+ result = await transformResult(event, result, {
522
543
  instance,
523
544
  request
524
545
  });
@@ -573,8 +594,8 @@ async function handleServerFunctionRequest(request, options = {}) {
573
594
  return encodeResult(result, headers, status, codec);
574
595
  } catch (x) {
575
596
  if (x instanceof Response || isResponseEnvelope(x)) {
576
- if (options.transformResult) {
577
- x = await options.transformResult(event, x, {
597
+ if (transformResult) {
598
+ x = await transformResult(event, x, {
578
599
  instance,
579
600
  request,
580
601
  thrown: true
package/types/client.d.ts CHANGED
@@ -84,6 +84,14 @@ export function registerElementClaim(handler: (element: Element) => void): () =>
84
84
  * Emitted by the compiler at element creation; idempotent by contract.
85
85
  */
86
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;
87
95
  export function className(node: Element, value: JSX.ClassValue, prev?: JSX.ClassValue): void;
88
96
  export function setProperty(node: Element, name: string, value: any): void;
89
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;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @solidjs/web/frames — client half. Consume frame streams into live DOM
3
+ * boundaries. There is deliberately no server-component API here beyond
4
+ * `installServerComponents()`: `dynamic` + server functions IS the client
5
+ * surface — a server-function call whose response is a frame stream
6
+ * resolves with a stable per-call-site component.
7
+ *
8
+ * Copied next to the runtime's frame d.ts files at publish (see
9
+ * types:copy-frames), so the relative imports below resolve in-place.
10
+ */
11
+ export {
12
+ createFrame,
13
+ createFrameHost,
14
+ createFrameInsertable,
15
+ FRAME_APPLIED_EVENT
16
+ } from "./frame-client.js";
17
+ export type {
18
+ Frame,
19
+ FrameChunk,
20
+ FrameHost,
21
+ FrameHostOptions,
22
+ FrameOptions,
23
+ FrameWrite,
24
+ Slot,
25
+ SlotContext
26
+ } from "./frame-client.js";
27
+ export {
28
+ FRAME_STREAM_HEADER,
29
+ applyFrameResponse,
30
+ isFrameStreamResponse,
31
+ createServerComponentHandler
32
+ } from "./frame-transport.js";
33
+ export { createJSONDataTable } from "./serializer.js";
34
+ export type { JSONDataTable } from "./serializer.js";
35
+
36
+ import type { FrameHost } from "./frame-client.js";
37
+
38
+ /**
39
+ * The app-wide frame host (created on first use): one chunk router, with
40
+ * codec data tables rotated per response — deserializer cross-reference
41
+ * space is stream-scoped by contract.
42
+ */
43
+ export function getFrameHost(): FrameHost;
44
+
45
+ /**
46
+ * Installs the server-component transport policy on the server-function
47
+ * client: boundary identity derives from the reactive owner captured at the
48
+ * call site, frame-stream responses resolve to stable per-call-site
49
+ * components, and document-SSR boundaries adopt their server-rendered
50
+ * ranges. Call once in the client entry (the package is sideEffects:false —
51
+ * a bare import would be tree-shaken); call again to rebind a custom host.
52
+ */
53
+ export function installServerComponents(host?: FrameHost): void;