Sign In

@solidjs/web

Package Overview
Dependencies
Maintainers
2
Versions
54
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@solidjs/web - npm Package Compare versions

Comparing version
2.0.0-beta.32
to
2.0.0-beta.33
+20
serialization/decode/package.json
{
"name": "@solidjs/web/serialization/decode",
"main": "../dist/decode.cjs",
"module": "../dist/decode.js",
"types": "../types/serializer-decode.d.ts",
"type": "module",
"sideEffects": false,
"exports": {
".": {
"import": {
"types": "../types/serializer-decode.d.ts",
"default": "../dist/decode.js"
},
"require": {
"types": "../types-cjs/serializer-decode.d.cts",
"default": "../dist/decode.cjs"
}
}
}
}
'use strict';
var seroval = require('seroval');
var web = require('seroval-plugins/web');
const STATE = Symbol.for("dom-expressions.container-trace-state");
const state = globalThis[STATE] || (globalThis[STATE] = {
materialized: new WeakMap(),
materializedValues: new WeakSet()
});
const TRACE = Symbol.for("dom-expressions.container-trace");
function materialize(marker) {
let value = state.materialized.get(marker.$tr);
if (value === undefined) {
value = state.materializeTrace(marker);
state.materialized.set(marker.$tr, value);
if (value !== null && typeof value === "object") state.materializedValues.add(value);
}
return value;
}
function parseTrace(value, ctx) {
const trace = value[TRACE];
return {
a: trace.array ? 1 : 0,
i: ctx.parse(trace.subscribe())
};
}
const ContainerTracePlugin = {
tag: "dom-expressions/container-trace",
test(value) {
return value != null && typeof value === "object" && TRACE in value;
},
parse: {
sync() {
throw new Error("A reactive container can only be serialized by a streaming serializer.");
},
async async(value, ctx) {
const trace = value[TRACE];
return {
a: trace.array ? 1 : 0,
i: await ctx.parse(trace.subscribe())
};
},
stream: parseTrace
},
serialize(node, ctx) {
return "{$tr:" + ctx.serialize(node.i) + ",$ta:" + node.a + "}";
},
deserialize(node, ctx) {
const iterable = ctx.deserialize(node.i);
const marker = {
$tr: iterable,
$ta: node.a
};
return state.materializeTrace ? materialize(marker) : marker;
}
};
const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin,
ContainerTracePlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
}
const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return seroval.fromCrossJSON(node, {
refs,
...resolved
});
};
}
function createJSONDataTable(options) {
const deserialize = createJSONDeserializer(options);
const table = new Map();
return {
apply(record) {
const value = deserialize(record.node);
if (record.initial) table.set(record.key, value);
},
get(key) {
return table.get(key);
},
resolve(ref) {
return table.get(ref.$ref);
}
};
}
exports.DEFAULT_WEB_PLUGINS = DEFAULT_WEB_PLUGINS;
exports.createJSONDataTable = createJSONDataTable;
exports.createJSONDeserializer = createJSONDeserializer;
exports.resolveCodecOptions = resolveCodecOptions;
exports.resolveSerializerPlugins = resolveSerializerPlugins;
import { Feature, fromCrossJSON } from 'seroval';
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
const STATE = Symbol.for("dom-expressions.container-trace-state");
const state = globalThis[STATE] || (globalThis[STATE] = {
materialized: new WeakMap(),
materializedValues: new WeakSet()
});
const TRACE = Symbol.for("dom-expressions.container-trace");
function materialize(marker) {
let value = state.materialized.get(marker.$tr);
if (value === undefined) {
value = state.materializeTrace(marker);
state.materialized.set(marker.$tr, value);
if (value !== null && typeof value === "object") state.materializedValues.add(value);
}
return value;
}
function parseTrace(value, ctx) {
const trace = value[TRACE];
return {
a: trace.array ? 1 : 0,
i: ctx.parse(trace.subscribe())
};
}
const ContainerTracePlugin = {
tag: "dom-expressions/container-trace",
test(value) {
return value != null && typeof value === "object" && TRACE in value;
},
parse: {
sync() {
throw new Error("A reactive container can only be serialized by a streaming serializer.");
},
async async(value, ctx) {
const trace = value[TRACE];
return {
a: trace.array ? 1 : 0,
i: await ctx.parse(trace.subscribe())
};
},
stream: parseTrace
},
serialize(node, ctx) {
return "{$tr:" + ctx.serialize(node.i) + ",$ta:" + node.a + "}";
},
deserialize(node, ctx) {
const iterable = ctx.deserialize(node.i);
const marker = {
$tr: iterable,
$ta: node.a
};
return state.materializeTrace ? materialize(marker) : marker;
}
};
const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin,
ContainerTracePlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
}
const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return fromCrossJSON(node, {
refs,
...resolved
});
};
}
function createJSONDataTable(options) {
const deserialize = createJSONDeserializer(options);
const table = new Map();
return {
apply(record) {
const value = deserialize(record.node);
if (record.initial) table.set(record.key, value);
},
get(key) {
return table.get(key);
},
resolve(ref) {
return table.get(ref.$ref);
}
};
}
export { DEFAULT_WEB_PLUGINS, createJSONDataTable, createJSONDeserializer, resolveCodecOptions, resolveSerializerPlugins };
// The DECODE half of the serialization surface (published as
// `@solidjs/web/serialization/decode`): what reading a serialized payload
// needs — `fromCrossJSON`-backed deserializers and the shared plugin set —
// with none of the encode machinery. Lazy client consumers (the frames
// data tables, `deserializeStream`) load this module so the encode half
// never ships to a browser that only reads. The full serializer.d.ts
// re-exports everything here; see its banner for the stability contract
// (integration-facing, exempt from the 2.0 stability guarantee).
// ---- Plugin types ----
//
// Declared here by hand (seroval's published d.ts use extensionless
// ESM-relative imports that `moduleResolution: "nodenext"` cannot follow —
// a bare type re-export would silently degrade the surface to `any` under
// skipLibCheck, and an import would make every entry whose types reach
// this module — the MAIN client entry included, via the server-function
// seam's `JSONCodecOptions` — unimportable from a strict Node16 CJS
// consumer). The declarations mirror seroval ~1.5 exactly; the `~` pin is
// what makes mirroring safe. Plugin AUTHORING (`createPlugin`,
// `OpaqueReference`) lives on the full serialization entry.
/**
* Seroval's node shape — the intermediate representation `serializeJSON`
* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
* Declared by hand like the plugin types below (same rationale): the
* observable envelope — a numeric type tag, an optional reference id —
* with the rest owned by the codec. Real seroval nodes satisfy it; treat
* it as an opaque token.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerovalNode {
/** Node type tag (seroval-internal enum). */
t: number;
/** Reference id, when the node participates in cross-referencing. */
i?: number | undefined;
[key: string]: unknown;
}
/** Per-plugin bookkeeping seroval hands each plugin callback. */
export interface PluginData {
id: number;
}
/**
* The shape of a plugin's parsed payload: a map of `SerovalNode`s produced
* by the parse contexts, consumed by `serialize`/`deserialize`.
*/
export type PluginInfo = { [key: string]: SerovalNode };
/** Parse context for `parse.sync`: turns child values into nodes. */
export interface SyncParsePluginContext {
parse<T>(current: T): SerovalNode;
}
/** Parse context for `parse.async`: like sync, but child parses await. */
export interface AsyncParsePluginContext {
parse<T>(current: T): Promise<SerovalNode>;
}
/**
* Parse context for `parse.stream`: sync parsing plus the streaming
* lifecycle (pending-state tracking, late node emission, cleanup).
*/
export interface StreamParsePluginContext {
parse<T>(current: T): SerovalNode;
parseWithError<T>(current: T): SerovalNode | undefined;
isAlive(): boolean;
pushPendingState(): void;
popPendingState(): void;
onParse(node: SerovalNode): void;
onError(error: unknown): void;
addCleanup(callback: () => void): void;
}
/** Serialize context: renders child nodes to JS source. */
export interface SerializePluginContext {
serialize(node: SerovalNode): string;
}
/** Deserialize context: revives child nodes to runtime values. */
export interface DeserializePluginContext {
deserialize<T>(node: SerovalNode): T;
}
/**
* A Seroval plugin usable with the web serializers — teaches the codec how
* to encode/decode a custom value type (`Value` is the value it matches,
* `Info` its parsed payload). Supply matching plugins on both peers of a
* transport. Bare `SerializerPlugin` (both parameters defaulted to `any`)
* is the list-element type every `plugins` option accepts.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerializerPlugin<Value = any, Info extends PluginInfo = any> {
/** A unique string identifying the plugin — namespace it (`"app/Thing"`). */
tag: string;
/** Dependency plugins, resolved ahead of this one. */
extends?: SerializerPlugin[];
/** Whether `value` is this plugin's to encode. */
test(value: unknown): boolean;
/** Parsing modes — provide the ones the transports you target use. */
parse: {
sync?: (value: Value, ctx: SyncParsePluginContext, data: PluginData) => Info;
async?: (value: Value, ctx: AsyncParsePluginContext, data: PluginData) => Promise<Info>;
stream?: (value: Value, ctx: StreamParsePluginContext, data: PluginData) => Info;
};
/** Renders the parsed payload as JS source (script-injection form). */
serialize(node: Info, ctx: SerializePluginContext, data: PluginData): string;
/** Revives the parsed payload back into the runtime value. */
deserialize(node: Info, ctx: DeserializePluginContext, data: PluginData): Value;
}
/**
* Baseline plugin set for serializing web-platform values (AbortSignal,
* Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).
* Applied by every serializer in this module; custom plugins compose ahead
* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/
export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];
/**
* Composes custom plugins with `DEFAULT_WEB_PLUGINS`. Custom plugins come
* first so they can shadow a default for values both would match. Returns a
* fresh array; the defaults are never mutated. Useful when handing a full
* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/**
* Options shared by both halves of the JSON codec. All of them must match
* on the serializing and deserializing peer or payloads will not
* round-trip — for server functions, set them once through the
* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONCodecOptions {
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. Must match on both peers. */
plugins?: SerializerPlugin[];
/**
* Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
* (payloads may come from an untrusted peer). Must match on both peers.
* Outside development, the encoding side additionally strips
* `Error.prototype.stack` on top of any override — serialized stacks leak
* server paths to the client. Decoding stays permissive, so payloads from
* a development peer still round-trip.
*/
disabledFeatures?: number;
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
depthLimit?: number;
}
/**
* Creates the decoding counterpart of `serializeJSON`. Cross-references
* between chunks resolve through state shared across calls, so all chunks
* from one stream must go through the same deserializer instance. The first
* chunk's return value is the decoded source value; feeding later chunks
* settles the async values referenced inside it.
*
* Integration-facing; may change (see the entry banner).
*/
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
* `resolve`. The frames client host wires one per response
* (`applyData: c => table.apply(c)`).
*
* Integration-facing; may change (see the entry banner). This serialization
* entry is the single home of the data table — the frames client consumes
* it internally rather than re-exporting it.
*/
export interface JSONDataTable {
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
resolve<T = unknown>(ref: { $ref: string }): T;
}
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;
// The DECODE half of the serialization surface (published as
// `@solidjs/web/serialization/decode`): what reading a serialized payload
// needs — `fromCrossJSON`-backed deserializers and the shared plugin set —
// with none of the encode machinery. Lazy client consumers (the frames
// data tables, `deserializeStream`) load this module so the encode half
// never ships to a browser that only reads. The full serializer.d.ts
// re-exports everything here; see its banner for the stability contract
// (integration-facing, exempt from the 2.0 stability guarantee).
// ---- Plugin types ----
//
// Declared here by hand (seroval's published d.ts use extensionless
// ESM-relative imports that `moduleResolution: "nodenext"` cannot follow —
// a bare type re-export would silently degrade the surface to `any` under
// skipLibCheck, and an import would make every entry whose types reach
// this module — the MAIN client entry included, via the server-function
// seam's `JSONCodecOptions` — unimportable from a strict Node16 CJS
// consumer). The declarations mirror seroval ~1.5 exactly; the `~` pin is
// what makes mirroring safe. Plugin AUTHORING (`createPlugin`,
// `OpaqueReference`) lives on the full serialization entry.
/**
* Seroval's node shape — the intermediate representation `serializeJSON`
* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
* Declared by hand like the plugin types below (same rationale): the
* observable envelope — a numeric type tag, an optional reference id —
* with the rest owned by the codec. Real seroval nodes satisfy it; treat
* it as an opaque token.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerovalNode {
/** Node type tag (seroval-internal enum). */
t: number;
/** Reference id, when the node participates in cross-referencing. */
i?: number | undefined;
[key: string]: unknown;
}
/** Per-plugin bookkeeping seroval hands each plugin callback. */
export interface PluginData {
id: number;
}
/**
* The shape of a plugin's parsed payload: a map of `SerovalNode`s produced
* by the parse contexts, consumed by `serialize`/`deserialize`.
*/
export type PluginInfo = { [key: string]: SerovalNode };
/** Parse context for `parse.sync`: turns child values into nodes. */
export interface SyncParsePluginContext {
parse<T>(current: T): SerovalNode;
}
/** Parse context for `parse.async`: like sync, but child parses await. */
export interface AsyncParsePluginContext {
parse<T>(current: T): Promise<SerovalNode>;
}
/**
* Parse context for `parse.stream`: sync parsing plus the streaming
* lifecycle (pending-state tracking, late node emission, cleanup).
*/
export interface StreamParsePluginContext {
parse<T>(current: T): SerovalNode;
parseWithError<T>(current: T): SerovalNode | undefined;
isAlive(): boolean;
pushPendingState(): void;
popPendingState(): void;
onParse(node: SerovalNode): void;
onError(error: unknown): void;
addCleanup(callback: () => void): void;
}
/** Serialize context: renders child nodes to JS source. */
export interface SerializePluginContext {
serialize(node: SerovalNode): string;
}
/** Deserialize context: revives child nodes to runtime values. */
export interface DeserializePluginContext {
deserialize<T>(node: SerovalNode): T;
}
/**
* A Seroval plugin usable with the web serializers — teaches the codec how
* to encode/decode a custom value type (`Value` is the value it matches,
* `Info` its parsed payload). Supply matching plugins on both peers of a
* transport. Bare `SerializerPlugin` (both parameters defaulted to `any`)
* is the list-element type every `plugins` option accepts.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerializerPlugin<Value = any, Info extends PluginInfo = any> {
/** A unique string identifying the plugin — namespace it (`"app/Thing"`). */
tag: string;
/** Dependency plugins, resolved ahead of this one. */
extends?: SerializerPlugin[];
/** Whether `value` is this plugin's to encode. */
test(value: unknown): boolean;
/** Parsing modes — provide the ones the transports you target use. */
parse: {
sync?: (value: Value, ctx: SyncParsePluginContext, data: PluginData) => Info;
async?: (value: Value, ctx: AsyncParsePluginContext, data: PluginData) => Promise<Info>;
stream?: (value: Value, ctx: StreamParsePluginContext, data: PluginData) => Info;
};
/** Renders the parsed payload as JS source (script-injection form). */
serialize(node: Info, ctx: SerializePluginContext, data: PluginData): string;
/** Revives the parsed payload back into the runtime value. */
deserialize(node: Info, ctx: DeserializePluginContext, data: PluginData): Value;
}
/**
* Baseline plugin set for serializing web-platform values (AbortSignal,
* Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).
* Applied by every serializer in this module; custom plugins compose ahead
* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/
export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];
/**
* Composes custom plugins with `DEFAULT_WEB_PLUGINS`. Custom plugins come
* first so they can shadow a default for values both would match. Returns a
* fresh array; the defaults are never mutated. Useful when handing a full
* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/**
* Options shared by both halves of the JSON codec. All of them must match
* on the serializing and deserializing peer or payloads will not
* round-trip — for server functions, set them once through the
* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONCodecOptions {
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. Must match on both peers. */
plugins?: SerializerPlugin[];
/**
* Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
* (payloads may come from an untrusted peer). Must match on both peers.
* Outside development, the encoding side additionally strips
* `Error.prototype.stack` on top of any override — serialized stacks leak
* server paths to the client. Decoding stays permissive, so payloads from
* a development peer still round-trip.
*/
disabledFeatures?: number;
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
depthLimit?: number;
}
/**
* Creates the decoding counterpart of `serializeJSON`. Cross-references
* between chunks resolve through state shared across calls, so all chunks
* from one stream must go through the same deserializer instance. The first
* chunk's return value is the decoded source value; feeding later chunks
* settles the async values referenced inside it.
*
* Integration-facing; may change (see the entry banner).
*/
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
* `resolve`. The frames client host wires one per response
* (`applyData: c => table.apply(c)`).
*
* Integration-facing; may change (see the entry banner). This serialization
* entry is the single home of the data table — the frames client consumes
* it internally rather than re-exporting it.
*/
export interface JSONDataTable {
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
resolve<T = unknown>(ref: { $ref: string }): T;
}
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;
// Cookie wire format: the platform-gap primitives, and ALL of core's
// cookie surface — core owns the exchange (the request's headers in, the
// response stub's headers out) and the codec, nothing ambient. Blessed
// patterns:
//
// parseCookieHeader(event.request.headers.get("cookie"))
// event.response.headers.append("set-cookie", serializeCookie(name, value, options))
//
// Dependency-free and isomorphic (exported from both entries — real
// implementation, never a stub); integrity/confidentiality layers
// (sessions) belong to the caller, on top of these primitives.
/**
* Attributes for a `Set-Cookie` header, mirroring RFC 6265. `path`
* defaults to `/`; nothing else is defaulted.
*/
export interface CookieOptions {
/** Cookie `Path` attribute. Defaults to `/`. */
path?: string;
/** Cookie `Domain` attribute. Emitted only when provided. */
domain?: string;
/** Cookie `Max-Age` attribute, in seconds (truncated to an integer). */
maxAge?: number;
/** Cookie `Expires` attribute. */
expires?: Date;
/** Emit the `HttpOnly` attribute. */
httpOnly?: boolean;
/** Emit the `Secure` attribute. */
secure?: boolean;
/** Cookie `SameSite` attribute, any case. */
sameSite?: "lax" | "strict" | "none" | "Lax" | "Strict" | "None";
}
/**
* Parses a `Cookie` request header into a name → value map. Names and
* values are `decodeURIComponent`-decoded (falling back to the raw text
* when decoding throws); a quoted value keeps its content. `null`/empty
* input parses to an empty map.
*
* The read half of the platform gap — the blessed request-cookie read is
* `parseCookieHeader(event.request.headers.get("cookie"))`.
*/
export function parseCookieHeader(header: string | null | undefined): Record<string, string>;
/**
* Serializes a cookie to a `Set-Cookie` header value. The name and value
* are `encodeURIComponent`-encoded (the parser decodes symmetrically);
* `path` defaults to `/` and every other attribute is emitted exactly
* when the caller asked for it.
*
* The write half of the platform gap — the blessed response-cookie write
* is `event.response.headers.append("set-cookie", serializeCookie(name,
* value, options))`, which every head materialization path carries to the
* wire entry-by-entry.
*/
export function serializeCookie(name: string, value: string, options?: CookieOptions): string;
/**
* Name of the cookie carrying the outcome of a server function call made
* without the client runtime (`"flash"`). A no-JS form post has no way to
* receive a value — the browser follows the redirect and renders the next
* page — so the handler stashes the outcome here for the render after it
* to pick up, which is how a form submitted without JavaScript still shows
* its result.
*
* The name, detection and clearing are cookie utilities and isomorphic
* (integrations read the cookie from code that also ships to the browser);
* the codec that fills and decodes it is server-only and lives behind the
* server-functions server entry.
*/
export const FLASH_COOKIE: string;
/**
* Whether a Cookie header carries a flash cookie, readable or not. Cheap
* enough to call on every render so the clear can be queued before the
* response headers flush.
*/
export function hasFlashCookie(cookieHeader: string | null): boolean;
/**
* The `Set-Cookie` value clearing the flash cookie. The outcome is
* one-shot: append this as soon as the cookie is detected, whether or not
* it decodes, so a stale outcome cannot resurface on a later request.
*/
export function clearFlashCookie(): string;
/**
* The raw encoded flash payload out of a Cookie header, if present — the
* codec's own accessor.
*
* @internal
*/
export function matchFlashCookie(cookieHeader: string | null): string | undefined;
// The DECODE half of the serialization surface (published as
// `@solidjs/web/serialization/decode`): what reading a serialized payload
// needs — `fromCrossJSON`-backed deserializers and the shared plugin set —
// with none of the encode machinery. Lazy client consumers (the frames
// data tables, `deserializeStream`) load this module so the encode half
// never ships to a browser that only reads. The full serializer.d.ts
// re-exports everything here; see its banner for the stability contract
// (integration-facing, exempt from the 2.0 stability guarantee).
// ---- Plugin types ----
//
// Declared here by hand (seroval's published d.ts use extensionless
// ESM-relative imports that `moduleResolution: "nodenext"` cannot follow —
// a bare type re-export would silently degrade the surface to `any` under
// skipLibCheck, and an import would make every entry whose types reach
// this module — the MAIN client entry included, via the server-function
// seam's `JSONCodecOptions` — unimportable from a strict Node16 CJS
// consumer). The declarations mirror seroval ~1.5 exactly; the `~` pin is
// what makes mirroring safe. Plugin AUTHORING (`createPlugin`,
// `OpaqueReference`) lives on the full serialization entry.
/**
* Seroval's node shape — the intermediate representation `serializeJSON`
* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
* Declared by hand like the plugin types below (same rationale): the
* observable envelope — a numeric type tag, an optional reference id —
* with the rest owned by the codec. Real seroval nodes satisfy it; treat
* it as an opaque token.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerovalNode {
/** Node type tag (seroval-internal enum). */
t: number;
/** Reference id, when the node participates in cross-referencing. */
i?: number | undefined;
[key: string]: unknown;
}
/** Per-plugin bookkeeping seroval hands each plugin callback. */
export interface PluginData {
id: number;
}
/**
* The shape of a plugin's parsed payload: a map of `SerovalNode`s produced
* by the parse contexts, consumed by `serialize`/`deserialize`.
*/
export type PluginInfo = { [key: string]: SerovalNode };
/** Parse context for `parse.sync`: turns child values into nodes. */
export interface SyncParsePluginContext {
parse<T>(current: T): SerovalNode;
}
/** Parse context for `parse.async`: like sync, but child parses await. */
export interface AsyncParsePluginContext {
parse<T>(current: T): Promise<SerovalNode>;
}
/**
* Parse context for `parse.stream`: sync parsing plus the streaming
* lifecycle (pending-state tracking, late node emission, cleanup).
*/
export interface StreamParsePluginContext {
parse<T>(current: T): SerovalNode;
parseWithError<T>(current: T): SerovalNode | undefined;
isAlive(): boolean;
pushPendingState(): void;
popPendingState(): void;
onParse(node: SerovalNode): void;
onError(error: unknown): void;
addCleanup(callback: () => void): void;
}
/** Serialize context: renders child nodes to JS source. */
export interface SerializePluginContext {
serialize(node: SerovalNode): string;
}
/** Deserialize context: revives child nodes to runtime values. */
export interface DeserializePluginContext {
deserialize<T>(node: SerovalNode): T;
}
/**
* A Seroval plugin usable with the web serializers — teaches the codec how
* to encode/decode a custom value type (`Value` is the value it matches,
* `Info` its parsed payload). Supply matching plugins on both peers of a
* transport. Bare `SerializerPlugin` (both parameters defaulted to `any`)
* is the list-element type every `plugins` option accepts.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerializerPlugin<Value = any, Info extends PluginInfo = any> {
/** A unique string identifying the plugin — namespace it (`"app/Thing"`). */
tag: string;
/** Dependency plugins, resolved ahead of this one. */
extends?: SerializerPlugin[];
/** Whether `value` is this plugin's to encode. */
test(value: unknown): boolean;
/** Parsing modes — provide the ones the transports you target use. */
parse: {
sync?: (value: Value, ctx: SyncParsePluginContext, data: PluginData) => Info;
async?: (value: Value, ctx: AsyncParsePluginContext, data: PluginData) => Promise<Info>;
stream?: (value: Value, ctx: StreamParsePluginContext, data: PluginData) => Info;
};
/** Renders the parsed payload as JS source (script-injection form). */
serialize(node: Info, ctx: SerializePluginContext, data: PluginData): string;
/** Revives the parsed payload back into the runtime value. */
deserialize(node: Info, ctx: DeserializePluginContext, data: PluginData): Value;
}
/**
* Baseline plugin set for serializing web-platform values (AbortSignal,
* Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).
* Applied by every serializer in this module; custom plugins compose ahead
* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/
export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];
/**
* Composes custom plugins with `DEFAULT_WEB_PLUGINS`. Custom plugins come
* first so they can shadow a default for values both would match. Returns a
* fresh array; the defaults are never mutated. Useful when handing a full
* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/**
* Options shared by both halves of the JSON codec. All of them must match
* on the serializing and deserializing peer or payloads will not
* round-trip — for server functions, set them once through the
* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONCodecOptions {
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. Must match on both peers. */
plugins?: SerializerPlugin[];
/**
* Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
* (payloads may come from an untrusted peer). Must match on both peers.
* Outside development, the encoding side additionally strips
* `Error.prototype.stack` on top of any override — serialized stacks leak
* server paths to the client. Decoding stays permissive, so payloads from
* a development peer still round-trip.
*/
disabledFeatures?: number;
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
depthLimit?: number;
}
/**
* Creates the decoding counterpart of `serializeJSON`. Cross-references
* between chunks resolve through state shared across calls, so all chunks
* from one stream must go through the same deserializer instance. The first
* chunk's return value is the decoded source value; feeding later chunks
* settles the async values referenced inside it.
*
* Integration-facing; may change (see the entry banner).
*/
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
* `resolve`. The frames client host wires one per response
* (`applyData: c => table.apply(c)`).
*
* Integration-facing; may change (see the entry banner). This serialization
* entry is the single home of the data table — the frames client consumes
* it internally rather than re-exporting it.
*/
export interface JSONDataTable {
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
resolve<T = unknown>(ref: { $ref: string }): T;
}
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;
// Cookie wire format: the platform-gap primitives, and ALL of core's
// cookie surface — core owns the exchange (the request's headers in, the
// response stub's headers out) and the codec, nothing ambient. Blessed
// patterns:
//
// parseCookieHeader(event.request.headers.get("cookie"))
// event.response.headers.append("set-cookie", serializeCookie(name, value, options))
//
// Dependency-free and isomorphic (exported from both entries — real
// implementation, never a stub); integrity/confidentiality layers
// (sessions) belong to the caller, on top of these primitives.
/**
* Attributes for a `Set-Cookie` header, mirroring RFC 6265. `path`
* defaults to `/`; nothing else is defaulted.
*/
export interface CookieOptions {
/** Cookie `Path` attribute. Defaults to `/`. */
path?: string;
/** Cookie `Domain` attribute. Emitted only when provided. */
domain?: string;
/** Cookie `Max-Age` attribute, in seconds (truncated to an integer). */
maxAge?: number;
/** Cookie `Expires` attribute. */
expires?: Date;
/** Emit the `HttpOnly` attribute. */
httpOnly?: boolean;
/** Emit the `Secure` attribute. */
secure?: boolean;
/** Cookie `SameSite` attribute, any case. */
sameSite?: "lax" | "strict" | "none" | "Lax" | "Strict" | "None";
}
/**
* Parses a `Cookie` request header into a name → value map. Names and
* values are `decodeURIComponent`-decoded (falling back to the raw text
* when decoding throws); a quoted value keeps its content. `null`/empty
* input parses to an empty map.
*
* The read half of the platform gap — the blessed request-cookie read is
* `parseCookieHeader(event.request.headers.get("cookie"))`.
*/
export function parseCookieHeader(header: string | null | undefined): Record<string, string>;
/**
* Serializes a cookie to a `Set-Cookie` header value. The name and value
* are `encodeURIComponent`-encoded (the parser decodes symmetrically);
* `path` defaults to `/` and every other attribute is emitted exactly
* when the caller asked for it.
*
* The write half of the platform gap — the blessed response-cookie write
* is `event.response.headers.append("set-cookie", serializeCookie(name,
* value, options))`, which every head materialization path carries to the
* wire entry-by-entry.
*/
export function serializeCookie(name: string, value: string, options?: CookieOptions): string;
/**
* Name of the cookie carrying the outcome of a server function call made
* without the client runtime (`"flash"`). A no-JS form post has no way to
* receive a value — the browser follows the redirect and renders the next
* page — so the handler stashes the outcome here for the render after it
* to pick up, which is how a form submitted without JavaScript still shows
* its result.
*
* The name, detection and clearing are cookie utilities and isomorphic
* (integrations read the cookie from code that also ships to the browser);
* the codec that fills and decodes it is server-only and lives behind the
* server-functions server entry.
*/
export const FLASH_COOKIE: string;
/**
* Whether a Cookie header carries a flash cookie, readable or not. Cheap
* enough to call on every render so the clear can be queued before the
* response headers flush.
*/
export function hasFlashCookie(cookieHeader: string | null): boolean;
/**
* The `Set-Cookie` value clearing the flash cookie. The outcome is
* one-shot: append this as soon as the cookie is detected, whether or not
* it decodes, so a stale outcome cannot resurface on a later request.
*/
export function clearFlashCookie(): string;
/**
* The raw encoded flash payload out of a Cookie header, if present — the
* codec's own accessor.
*
* @internal
*/
export function matchFlashCookie(cookieHeader: string | null): string | undefined;
// The DECODE half of the serialization surface (published as
// `@solidjs/web/serialization/decode`): what reading a serialized payload
// needs — `fromCrossJSON`-backed deserializers and the shared plugin set —
// with none of the encode machinery. Lazy client consumers (the frames
// data tables, `deserializeStream`) load this module so the encode half
// never ships to a browser that only reads. The full serializer.d.ts
// re-exports everything here; see its banner for the stability contract
// (integration-facing, exempt from the 2.0 stability guarantee).
// ---- Plugin types ----
//
// Declared here by hand (seroval's published d.ts use extensionless
// ESM-relative imports that `moduleResolution: "nodenext"` cannot follow —
// a bare type re-export would silently degrade the surface to `any` under
// skipLibCheck, and an import would make every entry whose types reach
// this module — the MAIN client entry included, via the server-function
// seam's `JSONCodecOptions` — unimportable from a strict Node16 CJS
// consumer). The declarations mirror seroval ~1.5 exactly; the `~` pin is
// what makes mirroring safe. Plugin AUTHORING (`createPlugin`,
// `OpaqueReference`) lives on the full serialization entry.
/**
* Seroval's node shape — the intermediate representation `serializeJSON`
* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
* Declared by hand like the plugin types below (same rationale): the
* observable envelope — a numeric type tag, an optional reference id —
* with the rest owned by the codec. Real seroval nodes satisfy it; treat
* it as an opaque token.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerovalNode {
/** Node type tag (seroval-internal enum). */
t: number;
/** Reference id, when the node participates in cross-referencing. */
i?: number | undefined;
[key: string]: unknown;
}
/** Per-plugin bookkeeping seroval hands each plugin callback. */
export interface PluginData {
id: number;
}
/**
* The shape of a plugin's parsed payload: a map of `SerovalNode`s produced
* by the parse contexts, consumed by `serialize`/`deserialize`.
*/
export type PluginInfo = { [key: string]: SerovalNode };
/** Parse context for `parse.sync`: turns child values into nodes. */
export interface SyncParsePluginContext {
parse<T>(current: T): SerovalNode;
}
/** Parse context for `parse.async`: like sync, but child parses await. */
export interface AsyncParsePluginContext {
parse<T>(current: T): Promise<SerovalNode>;
}
/**
* Parse context for `parse.stream`: sync parsing plus the streaming
* lifecycle (pending-state tracking, late node emission, cleanup).
*/
export interface StreamParsePluginContext {
parse<T>(current: T): SerovalNode;
parseWithError<T>(current: T): SerovalNode | undefined;
isAlive(): boolean;
pushPendingState(): void;
popPendingState(): void;
onParse(node: SerovalNode): void;
onError(error: unknown): void;
addCleanup(callback: () => void): void;
}
/** Serialize context: renders child nodes to JS source. */
export interface SerializePluginContext {
serialize(node: SerovalNode): string;
}
/** Deserialize context: revives child nodes to runtime values. */
export interface DeserializePluginContext {
deserialize<T>(node: SerovalNode): T;
}
/**
* A Seroval plugin usable with the web serializers — teaches the codec how
* to encode/decode a custom value type (`Value` is the value it matches,
* `Info` its parsed payload). Supply matching plugins on both peers of a
* transport. Bare `SerializerPlugin` (both parameters defaulted to `any`)
* is the list-element type every `plugins` option accepts.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerializerPlugin<Value = any, Info extends PluginInfo = any> {
/** A unique string identifying the plugin — namespace it (`"app/Thing"`). */
tag: string;
/** Dependency plugins, resolved ahead of this one. */
extends?: SerializerPlugin[];
/** Whether `value` is this plugin's to encode. */
test(value: unknown): boolean;
/** Parsing modes — provide the ones the transports you target use. */
parse: {
sync?: (value: Value, ctx: SyncParsePluginContext, data: PluginData) => Info;
async?: (value: Value, ctx: AsyncParsePluginContext, data: PluginData) => Promise<Info>;
stream?: (value: Value, ctx: StreamParsePluginContext, data: PluginData) => Info;
};
/** Renders the parsed payload as JS source (script-injection form). */
serialize(node: Info, ctx: SerializePluginContext, data: PluginData): string;
/** Revives the parsed payload back into the runtime value. */
deserialize(node: Info, ctx: DeserializePluginContext, data: PluginData): Value;
}
/**
* Baseline plugin set for serializing web-platform values (AbortSignal,
* Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).
* Applied by every serializer in this module; custom plugins compose ahead
* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/
export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];
/**
* Composes custom plugins with `DEFAULT_WEB_PLUGINS`. Custom plugins come
* first so they can shadow a default for values both would match. Returns a
* fresh array; the defaults are never mutated. Useful when handing a full
* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/**
* Options shared by both halves of the JSON codec. All of them must match
* on the serializing and deserializing peer or payloads will not
* round-trip — for server functions, set them once through the
* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONCodecOptions {
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. Must match on both peers. */
plugins?: SerializerPlugin[];
/**
* Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
* (payloads may come from an untrusted peer). Must match on both peers.
* Outside development, the encoding side additionally strips
* `Error.prototype.stack` on top of any override — serialized stacks leak
* server paths to the client. Decoding stays permissive, so payloads from
* a development peer still round-trip.
*/
disabledFeatures?: number;
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
depthLimit?: number;
}
/**
* Creates the decoding counterpart of `serializeJSON`. Cross-references
* between chunks resolve through state shared across calls, so all chunks
* from one stream must go through the same deserializer instance. The first
* chunk's return value is the decoded source value; feeding later chunks
* settles the async values referenced inside it.
*
* Integration-facing; may change (see the entry banner).
*/
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
* `resolve`. The frames client host wires one per response
* (`applyData: c => table.apply(c)`).
*
* Integration-facing; may change (see the entry banner). This serialization
* entry is the single home of the data table — the frames client consumes
* it internally rather than re-exporting it.
*/
export interface JSONDataTable {
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
resolve<T = unknown>(ref: { $ref: string }): T;
}
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;
+180
-63
'use strict';
var solidJs = require('solid-js');
var web$1 = require('@solidjs/web');
var seroval = require('seroval');
var web = require('@solidjs/web');
var client = require('@solidjs/web/server-functions/client');
var web = require('seroval-plugins/web');

@@ -70,2 +68,17 @@ const ELEMENT_NODE = 1;

};
case "hole":
return {
[`hole:${chunk.key}`]: {
kind: "html",
value: chunk.html
}
};
case "attr":
return {
[`attr:${chunk.key}`]: {
kind: "attrs",
value: chunk.attrs,
removed: chunk.removed
}
};
case "complete":

@@ -76,7 +89,8 @@ return {

case "error":
return chunk.key ? {
[`seg:${chunk.key}:error`]: chunk.error
} : {
if (!chunk.key) return {
":error": chunk.error
};
return {
[`${/^lha?:/.test(chunk.key) ? "hole" : "seg"}:${chunk.key}:error`]: chunk.error
};
default:

@@ -166,3 +180,6 @@ return {};

return options.resolve ? options.resolve(ref, frameId) : undefined;
}
},
revive: options.revive,
isContainer: options.isContainer,
prepareData: options.prepareData
};

@@ -183,2 +200,3 @@ }

#fallbackShown = new Set();
#appliedHoles = new Map();
#slots;

@@ -284,2 +302,3 @@ #mountedSlots = new Set();

this.#fallbackShown.clear();
this.#appliedHoles.clear();
this.#errorNotified = false;

@@ -327,2 +346,19 @@ clearStreamRecords(this.#store, root);

for (const key in this.#store) {
const record = this.#store[key];
if (!record || this.#appliedHoles.get(key) === record) continue;
if (key.startsWith("hole:")) {
if (key.endsWith(":error")) {
this.#appliedHoles.set(key, record);
} else if (this.#applyHole(key.slice(5), record.value)) {
this.#appliedHoles.set(key, record);
this.#applied(version, "morph");
}
} else if (key.startsWith("attr:")) {
if (this.#applyAttrs(key.slice(5), record.value, record.removed)) {
this.#appliedHoles.set(key, record);
this.#applied(version, "morph");
}
}
}
for (const key in this.#store) {
if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;

@@ -503,3 +539,3 @@ this.#processedAssets.add(key);

} else {
props[key] = value;
props[key] = host && host.revive ? host.revive(value) : value;
}

@@ -532,2 +568,6 @@ }

const next = host ? host.resolve(vb, this.#options.id) : undefined;
if (host && host.isContainer && (host.isContainer(next) || host.isContainer(cache[key]))) {
if (next === cache[key]) continue;
return false;
}
if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;

@@ -641,2 +681,31 @@ try {

}
#applyHole(marker, html) {
if (!this.#hasContent) return false;
const open = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === COMMENT_NODE && n.data === marker);
if (!open) return false;
const close = rangeClose(open, "lh:/" + marker.slice(3));
if (!close) {
return false;
}
const claim = claimHandlers() ? this.#claimTree : null;
reconcileChildren(open.parentNode, parseFragment(html), open, close, claim);
return true;
}
#applyAttrs(addr, text, removed) {
if (!this.#hasContent) return false;
const el = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === ELEMENT_NODE && n.getAttribute("data-lha") === addr);
if (!el) return false;
const parsed = parseFragment(`<i${text}></i>`).firstChild;
if (parsed) {
for (let i = 0; i < parsed.attributes.length; i++) {
const {
name,
value
} = parsed.attributes[i];
if (el.getAttribute(name) !== value) el.setAttribute(name, value);
}
}
if (removed) for (const name of removed) el.removeAttribute(name);
return true;
}
#clearContent() {

@@ -834,2 +903,16 @@ removeUntil(this.#parent(), this.#firstContent(), this.#end);

}
function findLiveTarget(n, end, test) {
while (n && n !== end) {
if (n.nodeType === ELEMENT_NODE) {
const fid = isFrameElement(n) ? n.getAttribute(FRAME_ID_ATTR) : null;
if (fid === null || fid.includes(".")) {
if (test(n)) return n;
const found = findLiveTarget(n.firstChild, null, test);
if (found) return found;
}
} else if (test(n)) return n;
n = n.nextSibling;
}
return null;
}
function findPlaceholder(n, end, id) {

@@ -893,3 +976,5 @@ while (n && n !== end) {

for (const key in records) {
if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
if (/^(seg|hole|attr):/.test(key) || key === ":error" || root && key === "") {
delete records[key];
}
}

@@ -1150,2 +1235,3 @@ }

} else if (version !== undefined) chunk.version = version;
if (chunk.type === "data" && host.prepareData) await host.prepareData();
host.apply(chunk);

@@ -1167,3 +1253,3 @@ }

}
const ServerComponentPlugin = /*#__PURE__*/seroval.createPlugin({
const ServerComponentPlugin = {
tag: "dom-expressions/server-component",

@@ -1195,8 +1281,6 @@ test(value) {

}
});
};
function flightCodec(codec) {
const plugins = codec && codec.plugins || [];
for (const plugin of plugins) {
if (plugin && plugin.tag === "dom-expressions/server-component") return codec;
}
if (plugins.some(plugin => plugin && plugin.tag === ServerComponentPlugin.tag)) return codec;
return {

@@ -1318,49 +1402,37 @@ ...codec,

seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
const STATE = Symbol.for("dom-expressions.container-trace-state");
const state = globalThis[STATE] || (globalThis[STATE] = {
materialized: new WeakMap(),
materializedValues: new WeakSet()
});
function setContainerTraceMaterializer(fn) {
state.materializeTrace = fn;
}
const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
function materialize(marker) {
let value = state.materialized.get(marker.$tr);
if (value === undefined) {
value = state.materializeTrace(marker);
state.materialized.set(marker.$tr, value);
if (value !== null && typeof value === "object") state.materializedValues.add(value);
}
return value;
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return seroval.fromCrossJSON(node, {
refs,
...resolved
});
};
function isMaterializedContainer(value) {
return value !== null && typeof value === "object" && state.materializedValues.has(value);
}
function createJSONDataTable(options) {
const deserialize = createJSONDeserializer(options);
const table = new Map();
return {
apply(record) {
const value = deserialize(record.node);
if (record.initial) table.set(record.key, value);
},
get(key) {
return table.get(key);
},
resolve(ref) {
return table.get(ref.$ref);
}
};
function isContainerTraceMarker(value) {
return value != null && typeof value === "object" && value.$tr != null && typeof value.$tr[Symbol.asyncIterator] === "function";
}
function reviveContainerTraces(value) {
if (!state.materializeTrace || value == null || typeof value !== "object") return value;
if (isContainerTraceMarker(value)) return materialize(value);
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) value[i] = reviveContainerTraces(value[i]);
} else if (Object.getPrototypeOf(value) === Object.prototype) {
for (const key of Object.keys(value)) value[key] = reviveContainerTraces(value[key]);
}
return value;
}
setContainerTraceMaterializer(solidJs.materializeContainerTrace);
function asyncArg(value) {

@@ -1370,11 +1442,22 @@ return value;

let sharedHost;
let codec;
let codecLoading;
function loadCodec() {
return codecLoading ??= import('@solidjs/web/serialization/decode').then(m => {
codec = m;
});
}
const tables = new Map();
function ensureTable(root) {
let table = tables.get(root);
if (!table && codec) tables.set(root, table = codec.createJSONDataTable());
return table;
}
function tableFor(id) {
const table = tables.get(id);
if (table) return table;
for (const [root, t] of tables) if (id.startsWith(root + ".")) return t;
if (tables.has(id)) return ensureTable(id);
for (const root of tables.keys()) if (id.startsWith(root + ".")) return ensureTable(root);
return undefined;
}
function beginStream(frameId) {
tables.set(frameId, createJSONDataTable());
tables.set(frameId, undefined);
}

@@ -1384,4 +1467,7 @@ function getFrameHost() {

sharedHost = createFrameHost({
prepareData: loadCodec,
applyData: c => tableFor(c.id)?.apply(c),
resolve: (ref, id) => tableFor(id)?.resolve(ref)
resolve: (ref, id) => tableFor(id)?.resolve(ref),
revive: reviveContainerTraces,
isContainer: isMaterializedContainer
});

@@ -1458,2 +1544,3 @@ }

const v = args()[key];
if (isMaterializedContainer(v)) return v;
if (!isAsyncValue(v)) return v;

@@ -1540,3 +1627,3 @@ let read = asyncReads.get(key);

const end = ctx.range.end;
const bind = () => web$1.insert(end.parentNode, () => typeof source === "function" ? source() : source, end, [...ctx.existing]);
const bind = () => web.insert(end.parentNode, () => typeof source === "function" ? source() : source, end, [...ctx.existing]);
solidJs.runWithOwner(owner, () => adopted ? claimRender(prefix, ctx.existing, bind) : bind());

@@ -1551,3 +1638,3 @@ return undefined;

function revealSeam(owner) {
return seam => solidJs.runWithOwner(owner, () => web$1.insert(seam.before.parentNode, solidJs.createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
return seam => solidJs.runWithOwner(owner, () => web.insert(seam.before.parentNode, solidJs.createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
}

@@ -1612,2 +1699,20 @@ let streamInvoke = false;

const claimedBoundaries = new Set();
const liveOps = new Map();
const liveAppliers = new Set();
let livePumped = null;
function pumpLiveChannel() {
const stream = globalThis._$HY?.r?.["sc:live"];
if (!stream || stream === livePumped || typeof stream.getReader !== "function") return;
livePumped = stream;
const reader = stream.getReader();
const pump = () => reader.read().then(r => {
if (r.done) return;
const op = r.value;
liveOps.set(`${op.type}:${op.fid || ""}:${op.key || ""}`, op);
for (const apply of liveAppliers) apply(op);
return pump();
});
pump().catch(() => {
});
}
let boundaryIndex = null;

@@ -1691,2 +1796,3 @@ const isBoundaryId = id => !id.includes(".");

if (!hy || !hy.r) return;
pumpLiveChannel();
const slotPrefix = `sc:slot:${id}:`;

@@ -1726,3 +1832,13 @@ for (const key of Object.keys(hy.r)) {

}) : undefined;
const applyLiveOp = op => {
if (op.type === "slot" && op.fid !== id) return;
host.apply({
...op,
id: address,
version: 0
});
};
liveAppliers.add(applyLiveOp);
solidJs.onCleanup(() => {
liveAppliers.delete(applyLiveOp);
unsubscribe && unsubscribe();

@@ -1732,2 +1848,3 @@ if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);

drainRecords();
for (const op of liveOps.values()) applyLiveOp(op);
const owner = solidJs.getOwner();

@@ -1734,0 +1851,0 @@ let release;

'use strict';
var solidJs = require('solid-js');
var web$1 = require('@solidjs/web');
var seroval = require('seroval');
var web = require('@solidjs/web');
var client = require('@solidjs/web/server-functions/client');
var web = require('seroval-plugins/web');

@@ -70,2 +68,17 @@ const ELEMENT_NODE = 1;

};
case "hole":
return {
[`hole:${chunk.key}`]: {
kind: "html",
value: chunk.html
}
};
case "attr":
return {
[`attr:${chunk.key}`]: {
kind: "attrs",
value: chunk.attrs,
removed: chunk.removed
}
};
case "complete":

@@ -76,7 +89,8 @@ return {

case "error":
return chunk.key ? {
[`seg:${chunk.key}:error`]: chunk.error
} : {
if (!chunk.key) return {
":error": chunk.error
};
return {
[`${/^lha?:/.test(chunk.key) ? "hole" : "seg"}:${chunk.key}:error`]: chunk.error
};
default:

@@ -166,3 +180,6 @@ return {};

return options.resolve ? options.resolve(ref, frameId) : undefined;
}
},
revive: options.revive,
isContainer: options.isContainer,
prepareData: options.prepareData
};

@@ -183,2 +200,3 @@ }

#fallbackShown = new Set();
#appliedHoles = new Map();
#slots;

@@ -284,2 +302,3 @@ #mountedSlots = new Set();

this.#fallbackShown.clear();
this.#appliedHoles.clear();
this.#errorNotified = false;

@@ -327,2 +346,20 @@ clearStreamRecords(this.#store, root);

for (const key in this.#store) {
const record = this.#store[key];
if (!record || this.#appliedHoles.get(key) === record) continue;
if (key.startsWith("hole:")) {
if (key.endsWith(":error")) {
this.#appliedHoles.set(key, record);
console.error(`Live hole ${key.slice(5, -6)} failed on the server; latched:`, record);
} else if (this.#applyHole(key.slice(5), record.value)) {
this.#appliedHoles.set(key, record);
this.#applied(version, "morph");
}
} else if (key.startsWith("attr:")) {
if (this.#applyAttrs(key.slice(5), record.value, record.removed)) {
this.#appliedHoles.set(key, record);
this.#applied(version, "morph");
}
}
}
for (const key in this.#store) {
if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;

@@ -503,3 +540,3 @@ this.#processedAssets.add(key);

} else {
props[key] = value;
props[key] = host && host.revive ? host.revive(value) : value;
}

@@ -532,2 +569,6 @@ }

const next = host ? host.resolve(vb, this.#options.id) : undefined;
if (host && host.isContainer && (host.isContainer(next) || host.isContainer(cache[key]))) {
if (next === cache[key]) continue;
return false;
}
if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;

@@ -641,2 +682,34 @@ try {

}
#applyHole(marker, html) {
if (!this.#hasContent) return false;
const open = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === COMMENT_NODE && n.data === marker);
if (!open) return false;
const close = rangeClose(open, "lh:/" + marker.slice(3));
if (!close) {
{
console.error(`Live hole "${marker}" is missing its closing comment; update dropped. ` + `Likely an HTML-rewriting layer stripped it.`, open);
}
return false;
}
const claim = claimHandlers() ? this.#claimTree : null;
reconcileChildren(open.parentNode, parseFragment(html), open, close, claim);
return true;
}
#applyAttrs(addr, text, removed) {
if (!this.#hasContent) return false;
const el = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === ELEMENT_NODE && n.getAttribute("data-lha") === addr);
if (!el) return false;
const parsed = parseFragment(`<i${text}></i>`).firstChild;
if (parsed) {
for (let i = 0; i < parsed.attributes.length; i++) {
const {
name,
value
} = parsed.attributes[i];
if (el.getAttribute(name) !== value) el.setAttribute(name, value);
}
}
if (removed) for (const name of removed) el.removeAttribute(name);
return true;
}
#clearContent() {

@@ -837,2 +910,16 @@ removeUntil(this.#parent(), this.#firstContent(), this.#end);

}
function findLiveTarget(n, end, test) {
while (n && n !== end) {
if (n.nodeType === ELEMENT_NODE) {
const fid = isFrameElement(n) ? n.getAttribute(FRAME_ID_ATTR) : null;
if (fid === null || fid.includes(".")) {
if (test(n)) return n;
const found = findLiveTarget(n.firstChild, null, test);
if (found) return found;
}
} else if (test(n)) return n;
n = n.nextSibling;
}
return null;
}
function findPlaceholder(n, end, id) {

@@ -897,3 +984,5 @@ while (n && n !== end) {

for (const key in records) {
if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
if (/^(seg|hole|attr):/.test(key) || key === ":error" || root && key === "") {
delete records[key];
}
}

@@ -1163,2 +1252,3 @@ }

} else if (version !== undefined) chunk.version = version;
if (chunk.type === "data" && host.prepareData) await host.prepareData();
host.apply(chunk);

@@ -1180,3 +1270,3 @@ }

}
const ServerComponentPlugin = /*#__PURE__*/seroval.createPlugin({
const ServerComponentPlugin = {
tag: "dom-expressions/server-component",

@@ -1208,8 +1298,6 @@ test(value) {

}
});
};
function flightCodec(codec) {
const plugins = codec && codec.plugins || [];
for (const plugin of plugins) {
if (plugin && plugin.tag === "dom-expressions/server-component") return codec;
}
if (plugins.some(plugin => plugin && plugin.tag === ServerComponentPlugin.tag)) return codec;
return {

@@ -1331,49 +1419,37 @@ ...codec,

seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
const STATE = Symbol.for("dom-expressions.container-trace-state");
const state = globalThis[STATE] || (globalThis[STATE] = {
materialized: new WeakMap(),
materializedValues: new WeakSet()
});
function setContainerTraceMaterializer(fn) {
state.materializeTrace = fn;
}
const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
function materialize(marker) {
let value = state.materialized.get(marker.$tr);
if (value === undefined) {
value = state.materializeTrace(marker);
state.materialized.set(marker.$tr, value);
if (value !== null && typeof value === "object") state.materializedValues.add(value);
}
return value;
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return seroval.fromCrossJSON(node, {
refs,
...resolved
});
};
function isMaterializedContainer(value) {
return value !== null && typeof value === "object" && state.materializedValues.has(value);
}
function createJSONDataTable(options) {
const deserialize = createJSONDeserializer(options);
const table = new Map();
return {
apply(record) {
const value = deserialize(record.node);
if (record.initial) table.set(record.key, value);
},
get(key) {
return table.get(key);
},
resolve(ref) {
return table.get(ref.$ref);
}
};
function isContainerTraceMarker(value) {
return value != null && typeof value === "object" && value.$tr != null && typeof value.$tr[Symbol.asyncIterator] === "function";
}
function reviveContainerTraces(value) {
if (!state.materializeTrace || value == null || typeof value !== "object") return value;
if (isContainerTraceMarker(value)) return materialize(value);
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) value[i] = reviveContainerTraces(value[i]);
} else if (Object.getPrototypeOf(value) === Object.prototype) {
for (const key of Object.keys(value)) value[key] = reviveContainerTraces(value[key]);
}
return value;
}
setContainerTraceMaterializer(solidJs.materializeContainerTrace);
function asyncArg(value) {

@@ -1383,11 +1459,22 @@ return value;

let sharedHost;
let codec;
let codecLoading;
function loadCodec() {
return codecLoading ??= import('@solidjs/web/serialization/decode').then(m => {
codec = m;
});
}
const tables = new Map();
function ensureTable(root) {
let table = tables.get(root);
if (!table && codec) tables.set(root, table = codec.createJSONDataTable());
return table;
}
function tableFor(id) {
const table = tables.get(id);
if (table) return table;
for (const [root, t] of tables) if (id.startsWith(root + ".")) return t;
if (tables.has(id)) return ensureTable(id);
for (const root of tables.keys()) if (id.startsWith(root + ".")) return ensureTable(root);
return undefined;
}
function beginStream(frameId) {
tables.set(frameId, createJSONDataTable());
tables.set(frameId, undefined);
}

@@ -1397,4 +1484,7 @@ function getFrameHost() {

sharedHost = createFrameHost({
prepareData: loadCodec,
applyData: c => tableFor(c.id)?.apply(c),
resolve: (ref, id) => tableFor(id)?.resolve(ref)
resolve: (ref, id) => tableFor(id)?.resolve(ref),
revive: reviveContainerTraces,
isContainer: isMaterializedContainer
});

@@ -1471,2 +1561,3 @@ }

const v = args()[key];
if (isMaterializedContainer(v)) return v;
if (!isAsyncValue(v)) return v;

@@ -1553,3 +1644,3 @@ let read = asyncReads.get(key);

const end = ctx.range.end;
const bind = () => web$1.insert(end.parentNode, () => typeof source === "function" ? source() : source, end, [...ctx.existing]);
const bind = () => web.insert(end.parentNode, () => typeof source === "function" ? source() : source, end, [...ctx.existing]);
solidJs.runWithOwner(owner, () => adopted ? claimRender(prefix, ctx.existing, bind) : bind());

@@ -1564,3 +1655,3 @@ return undefined;

function revealSeam(owner) {
return seam => solidJs.runWithOwner(owner, () => web$1.insert(seam.before.parentNode, solidJs.createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
return seam => solidJs.runWithOwner(owner, () => web.insert(seam.before.parentNode, solidJs.createLoadingBoundary(() => seam.content(), () => seam.fallback), seam.before));
}

@@ -1625,2 +1716,20 @@ let streamInvoke = false;

const claimedBoundaries = new Set();
const liveOps = new Map();
const liveAppliers = new Set();
let livePumped = null;
function pumpLiveChannel() {
const stream = globalThis._$HY?.r?.["sc:live"];
if (!stream || stream === livePumped || typeof stream.getReader !== "function") return;
livePumped = stream;
const reader = stream.getReader();
const pump = () => reader.read().then(r => {
if (r.done) return;
const op = r.value;
liveOps.set(`${op.type}:${op.fid || ""}:${op.key || ""}`, op);
for (const apply of liveAppliers) apply(op);
return pump();
});
pump().catch(() => {
});
}
let boundaryIndex = null;

@@ -1704,2 +1813,3 @@ const isBoundaryId = id => !id.includes(".");

if (!hy || !hy.r) return;
pumpLiveChannel();
const slotPrefix = `sc:slot:${id}:`;

@@ -1739,3 +1849,13 @@ for (const key of Object.keys(hy.r)) {

}) : undefined;
const applyLiveOp = op => {
if (op.type === "slot" && op.fid !== id) return;
host.apply({
...op,
id: address,
version: 0
});
};
liveAppliers.add(applyLiveOp);
solidJs.onCleanup(() => {
liveAppliers.delete(applyLiveOp);
unsubscribe && unsubscribe();

@@ -1745,2 +1865,3 @@ if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);

drainRecords();
for (const op of liveOps.values()) applyLiveOp(op);
const owner = solidJs.getOwner();

@@ -1747,0 +1868,0 @@ let release;

@@ -1,6 +0,4 @@

import { getOwner, onCleanup, createMemo, runWithOwner, createSignal, createRenderEffect, createLoadingBoundary, createOwner, sharedConfig } from 'solid-js';
import { getOwner, onCleanup, createMemo, runWithOwner, createSignal, createRenderEffect, createLoadingBoundary, createOwner, sharedConfig, materializeContainerTrace } from 'solid-js';
import { insert } from '@solidjs/web';
import { createPlugin, fromCrossJSON, Feature } from 'seroval';
import { ChunkReader, frameAddress, SINGLE_FLIGHT_HEADER, deserializeStream, getServerFunctionsCodec, createChunk, getFlightDataConsumer, ERROR_HEADER, REVALIDATE_HEADER, configureServerFunctionsClient } from '@solidjs/web/server-functions/client';
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';

@@ -68,2 +66,17 @@ const ELEMENT_NODE = 1;

};
case "hole":
return {
[`hole:${chunk.key}`]: {
kind: "html",
value: chunk.html
}
};
case "attr":
return {
[`attr:${chunk.key}`]: {
kind: "attrs",
value: chunk.attrs,
removed: chunk.removed
}
};
case "complete":

@@ -74,7 +87,8 @@ return {

case "error":
return chunk.key ? {
[`seg:${chunk.key}:error`]: chunk.error
} : {
if (!chunk.key) return {
":error": chunk.error
};
return {
[`${/^lha?:/.test(chunk.key) ? "hole" : "seg"}:${chunk.key}:error`]: chunk.error
};
default:

@@ -164,3 +178,6 @@ return {};

return options.resolve ? options.resolve(ref, frameId) : undefined;
}
},
revive: options.revive,
isContainer: options.isContainer,
prepareData: options.prepareData
};

@@ -181,2 +198,3 @@ }

#fallbackShown = new Set();
#appliedHoles = new Map();
#slots;

@@ -282,2 +300,3 @@ #mountedSlots = new Set();

this.#fallbackShown.clear();
this.#appliedHoles.clear();
this.#errorNotified = false;

@@ -325,2 +344,20 @@ clearStreamRecords(this.#store, root);

for (const key in this.#store) {
const record = this.#store[key];
if (!record || this.#appliedHoles.get(key) === record) continue;
if (key.startsWith("hole:")) {
if (key.endsWith(":error")) {
this.#appliedHoles.set(key, record);
console.error(`Live hole ${key.slice(5, -6)} failed on the server; latched:`, record);
} else if (this.#applyHole(key.slice(5), record.value)) {
this.#appliedHoles.set(key, record);
this.#applied(version, "morph");
}
} else if (key.startsWith("attr:")) {
if (this.#applyAttrs(key.slice(5), record.value, record.removed)) {
this.#appliedHoles.set(key, record);
this.#applied(version, "morph");
}
}
}
for (const key in this.#store) {
if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;

@@ -501,3 +538,3 @@ this.#processedAssets.add(key);

} else {
props[key] = value;
props[key] = host && host.revive ? host.revive(value) : value;
}

@@ -530,2 +567,6 @@ }

const next = host ? host.resolve(vb, this.#options.id) : undefined;
if (host && host.isContainer && (host.isContainer(next) || host.isContainer(cache[key]))) {
if (next === cache[key]) continue;
return false;
}
if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;

@@ -639,2 +680,34 @@ try {

}
#applyHole(marker, html) {
if (!this.#hasContent) return false;
const open = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === COMMENT_NODE && n.data === marker);
if (!open) return false;
const close = rangeClose(open, "lh:/" + marker.slice(3));
if (!close) {
{
console.error(`Live hole "${marker}" is missing its closing comment; update dropped. ` + `Likely an HTML-rewriting layer stripped it.`, open);
}
return false;
}
const claim = claimHandlers() ? this.#claimTree : null;
reconcileChildren(open.parentNode, parseFragment(html), open, close, claim);
return true;
}
#applyAttrs(addr, text, removed) {
if (!this.#hasContent) return false;
const el = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === ELEMENT_NODE && n.getAttribute("data-lha") === addr);
if (!el) return false;
const parsed = parseFragment(`<i${text}></i>`).firstChild;
if (parsed) {
for (let i = 0; i < parsed.attributes.length; i++) {
const {
name,
value
} = parsed.attributes[i];
if (el.getAttribute(name) !== value) el.setAttribute(name, value);
}
}
if (removed) for (const name of removed) el.removeAttribute(name);
return true;
}
#clearContent() {

@@ -835,2 +908,16 @@ removeUntil(this.#parent(), this.#firstContent(), this.#end);

}
function findLiveTarget(n, end, test) {
while (n && n !== end) {
if (n.nodeType === ELEMENT_NODE) {
const fid = isFrameElement(n) ? n.getAttribute(FRAME_ID_ATTR) : null;
if (fid === null || fid.includes(".")) {
if (test(n)) return n;
const found = findLiveTarget(n.firstChild, null, test);
if (found) return found;
}
} else if (test(n)) return n;
n = n.nextSibling;
}
return null;
}
function findPlaceholder(n, end, id) {

@@ -895,3 +982,5 @@ while (n && n !== end) {

for (const key in records) {
if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
if (/^(seg|hole|attr):/.test(key) || key === ":error" || root && key === "") {
delete records[key];
}
}

@@ -1161,2 +1250,3 @@ }

} else if (version !== undefined) chunk.version = version;
if (chunk.type === "data" && host.prepareData) await host.prepareData();
host.apply(chunk);

@@ -1178,3 +1268,3 @@ }

}
const ServerComponentPlugin = /*#__PURE__*/createPlugin({
const ServerComponentPlugin = {
tag: "dom-expressions/server-component",

@@ -1206,8 +1296,6 @@ test(value) {

}
});
};
function flightCodec(codec) {
const plugins = codec && codec.plugins || [];
for (const plugin of plugins) {
if (plugin && plugin.tag === "dom-expressions/server-component") return codec;
}
if (plugins.some(plugin => plugin && plugin.tag === ServerComponentPlugin.tag)) return codec;
return {

@@ -1329,49 +1417,37 @@ ...codec,

Feature.AggregateError | Feature.BigIntTypedArray;
const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
const STATE = Symbol.for("dom-expressions.container-trace-state");
const state = globalThis[STATE] || (globalThis[STATE] = {
materialized: new WeakMap(),
materializedValues: new WeakSet()
});
function setContainerTraceMaterializer(fn) {
state.materializeTrace = fn;
}
const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
function materialize(marker) {
let value = state.materialized.get(marker.$tr);
if (value === undefined) {
value = state.materializeTrace(marker);
state.materialized.set(marker.$tr, value);
if (value !== null && typeof value === "object") state.materializedValues.add(value);
}
return value;
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return fromCrossJSON(node, {
refs,
...resolved
});
};
function isMaterializedContainer(value) {
return value !== null && typeof value === "object" && state.materializedValues.has(value);
}
function createJSONDataTable(options) {
const deserialize = createJSONDeserializer(options);
const table = new Map();
return {
apply(record) {
const value = deserialize(record.node);
if (record.initial) table.set(record.key, value);
},
get(key) {
return table.get(key);
},
resolve(ref) {
return table.get(ref.$ref);
}
};
function isContainerTraceMarker(value) {
return value != null && typeof value === "object" && value.$tr != null && typeof value.$tr[Symbol.asyncIterator] === "function";
}
function reviveContainerTraces(value) {
if (!state.materializeTrace || value == null || typeof value !== "object") return value;
if (isContainerTraceMarker(value)) return materialize(value);
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) value[i] = reviveContainerTraces(value[i]);
} else if (Object.getPrototypeOf(value) === Object.prototype) {
for (const key of Object.keys(value)) value[key] = reviveContainerTraces(value[key]);
}
return value;
}
setContainerTraceMaterializer(materializeContainerTrace);
function asyncArg(value) {

@@ -1381,11 +1457,22 @@ return value;

let sharedHost;
let codec;
let codecLoading;
function loadCodec() {
return codecLoading ??= import('@solidjs/web/serialization/decode').then(m => {
codec = m;
});
}
const tables = new Map();
function ensureTable(root) {
let table = tables.get(root);
if (!table && codec) tables.set(root, table = codec.createJSONDataTable());
return table;
}
function tableFor(id) {
const table = tables.get(id);
if (table) return table;
for (const [root, t] of tables) if (id.startsWith(root + ".")) return t;
if (tables.has(id)) return ensureTable(id);
for (const root of tables.keys()) if (id.startsWith(root + ".")) return ensureTable(root);
return undefined;
}
function beginStream(frameId) {
tables.set(frameId, createJSONDataTable());
tables.set(frameId, undefined);
}

@@ -1395,4 +1482,7 @@ function getFrameHost() {

sharedHost = createFrameHost({
prepareData: loadCodec,
applyData: c => tableFor(c.id)?.apply(c),
resolve: (ref, id) => tableFor(id)?.resolve(ref)
resolve: (ref, id) => tableFor(id)?.resolve(ref),
revive: reviveContainerTraces,
isContainer: isMaterializedContainer
});

@@ -1469,2 +1559,3 @@ }

const v = args()[key];
if (isMaterializedContainer(v)) return v;
if (!isAsyncValue(v)) return v;

@@ -1621,2 +1712,20 @@ let read = asyncReads.get(key);

const claimedBoundaries = new Set();
const liveOps = new Map();
const liveAppliers = new Set();
let livePumped = null;
function pumpLiveChannel() {
const stream = globalThis._$HY?.r?.["sc:live"];
if (!stream || stream === livePumped || typeof stream.getReader !== "function") return;
livePumped = stream;
const reader = stream.getReader();
const pump = () => reader.read().then(r => {
if (r.done) return;
const op = r.value;
liveOps.set(`${op.type}:${op.fid || ""}:${op.key || ""}`, op);
for (const apply of liveAppliers) apply(op);
return pump();
});
pump().catch(() => {
});
}
let boundaryIndex = null;

@@ -1700,2 +1809,3 @@ const isBoundaryId = id => !id.includes(".");

if (!hy || !hy.r) return;
pumpLiveChannel();
const slotPrefix = `sc:slot:${id}:`;

@@ -1735,3 +1845,13 @@ for (const key of Object.keys(hy.r)) {

}) : undefined;
const applyLiveOp = op => {
if (op.type === "slot" && op.fid !== id) return;
host.apply({
...op,
id: address,
version: 0
});
};
liveAppliers.add(applyLiveOp);
onCleanup(() => {
liveAppliers.delete(applyLiveOp);
unsubscribe && unsubscribe();

@@ -1741,2 +1861,3 @@ if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);

drainRecords();
for (const op of liveOps.values()) applyLiveOp(op);
const owner = getOwner();

@@ -1743,0 +1864,0 @@ let release;

@@ -1,6 +0,4 @@

import { getOwner, onCleanup, createMemo, runWithOwner, createSignal, createRenderEffect, createLoadingBoundary, createOwner, sharedConfig } from 'solid-js';
import { getOwner, onCleanup, createMemo, runWithOwner, createSignal, createRenderEffect, createLoadingBoundary, createOwner, sharedConfig, materializeContainerTrace } from 'solid-js';
import { insert } from '@solidjs/web';
import { createPlugin, fromCrossJSON, Feature } from 'seroval';
import { ChunkReader, frameAddress, SINGLE_FLIGHT_HEADER, deserializeStream, getServerFunctionsCodec, createChunk, getFlightDataConsumer, ERROR_HEADER, REVALIDATE_HEADER, configureServerFunctionsClient } from '@solidjs/web/server-functions/client';
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';

@@ -68,2 +66,17 @@ const ELEMENT_NODE = 1;

};
case "hole":
return {
[`hole:${chunk.key}`]: {
kind: "html",
value: chunk.html
}
};
case "attr":
return {
[`attr:${chunk.key}`]: {
kind: "attrs",
value: chunk.attrs,
removed: chunk.removed
}
};
case "complete":

@@ -74,7 +87,8 @@ return {

case "error":
return chunk.key ? {
[`seg:${chunk.key}:error`]: chunk.error
} : {
if (!chunk.key) return {
":error": chunk.error
};
return {
[`${/^lha?:/.test(chunk.key) ? "hole" : "seg"}:${chunk.key}:error`]: chunk.error
};
default:

@@ -164,3 +178,6 @@ return {};

return options.resolve ? options.resolve(ref, frameId) : undefined;
}
},
revive: options.revive,
isContainer: options.isContainer,
prepareData: options.prepareData
};

@@ -181,2 +198,3 @@ }

#fallbackShown = new Set();
#appliedHoles = new Map();
#slots;

@@ -282,2 +300,3 @@ #mountedSlots = new Set();

this.#fallbackShown.clear();
this.#appliedHoles.clear();
this.#errorNotified = false;

@@ -325,2 +344,19 @@ clearStreamRecords(this.#store, root);

for (const key in this.#store) {
const record = this.#store[key];
if (!record || this.#appliedHoles.get(key) === record) continue;
if (key.startsWith("hole:")) {
if (key.endsWith(":error")) {
this.#appliedHoles.set(key, record);
} else if (this.#applyHole(key.slice(5), record.value)) {
this.#appliedHoles.set(key, record);
this.#applied(version, "morph");
}
} else if (key.startsWith("attr:")) {
if (this.#applyAttrs(key.slice(5), record.value, record.removed)) {
this.#appliedHoles.set(key, record);
this.#applied(version, "morph");
}
}
}
for (const key in this.#store) {
if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;

@@ -501,3 +537,3 @@ this.#processedAssets.add(key);

} else {
props[key] = value;
props[key] = host && host.revive ? host.revive(value) : value;
}

@@ -530,2 +566,6 @@ }

const next = host ? host.resolve(vb, this.#options.id) : undefined;
if (host && host.isContainer && (host.isContainer(next) || host.isContainer(cache[key]))) {
if (next === cache[key]) continue;
return false;
}
if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;

@@ -639,2 +679,31 @@ try {

}
#applyHole(marker, html) {
if (!this.#hasContent) return false;
const open = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === COMMENT_NODE && n.data === marker);
if (!open) return false;
const close = rangeClose(open, "lh:/" + marker.slice(3));
if (!close) {
return false;
}
const claim = claimHandlers() ? this.#claimTree : null;
reconcileChildren(open.parentNode, parseFragment(html), open, close, claim);
return true;
}
#applyAttrs(addr, text, removed) {
if (!this.#hasContent) return false;
const el = findLiveTarget(this.#firstContent(), this.#end, n => n.nodeType === ELEMENT_NODE && n.getAttribute("data-lha") === addr);
if (!el) return false;
const parsed = parseFragment(`<i${text}></i>`).firstChild;
if (parsed) {
for (let i = 0; i < parsed.attributes.length; i++) {
const {
name,
value
} = parsed.attributes[i];
if (el.getAttribute(name) !== value) el.setAttribute(name, value);
}
}
if (removed) for (const name of removed) el.removeAttribute(name);
return true;
}
#clearContent() {

@@ -832,2 +901,16 @@ removeUntil(this.#parent(), this.#firstContent(), this.#end);

}
function findLiveTarget(n, end, test) {
while (n && n !== end) {
if (n.nodeType === ELEMENT_NODE) {
const fid = isFrameElement(n) ? n.getAttribute(FRAME_ID_ATTR) : null;
if (fid === null || fid.includes(".")) {
if (test(n)) return n;
const found = findLiveTarget(n.firstChild, null, test);
if (found) return found;
}
} else if (test(n)) return n;
n = n.nextSibling;
}
return null;
}
function findPlaceholder(n, end, id) {

@@ -891,3 +974,5 @@ while (n && n !== end) {

for (const key in records) {
if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
if (/^(seg|hole|attr):/.test(key) || key === ":error" || root && key === "") {
delete records[key];
}
}

@@ -1148,2 +1233,3 @@ }

} else if (version !== undefined) chunk.version = version;
if (chunk.type === "data" && host.prepareData) await host.prepareData();
host.apply(chunk);

@@ -1165,3 +1251,3 @@ }

}
const ServerComponentPlugin = /*#__PURE__*/createPlugin({
const ServerComponentPlugin = {
tag: "dom-expressions/server-component",

@@ -1193,8 +1279,6 @@ test(value) {

}
});
};
function flightCodec(codec) {
const plugins = codec && codec.plugins || [];
for (const plugin of plugins) {
if (plugin && plugin.tag === "dom-expressions/server-component") return codec;
}
if (plugins.some(plugin => plugin && plugin.tag === ServerComponentPlugin.tag)) return codec;
return {

@@ -1316,49 +1400,37 @@ ...codec,

Feature.AggregateError | Feature.BigIntTypedArray;
const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
const STATE = Symbol.for("dom-expressions.container-trace-state");
const state = globalThis[STATE] || (globalThis[STATE] = {
materialized: new WeakMap(),
materializedValues: new WeakSet()
});
function setContainerTraceMaterializer(fn) {
state.materializeTrace = fn;
}
const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
function materialize(marker) {
let value = state.materialized.get(marker.$tr);
if (value === undefined) {
value = state.materializeTrace(marker);
state.materialized.set(marker.$tr, value);
if (value !== null && typeof value === "object") state.materializedValues.add(value);
}
return value;
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return fromCrossJSON(node, {
refs,
...resolved
});
};
function isMaterializedContainer(value) {
return value !== null && typeof value === "object" && state.materializedValues.has(value);
}
function createJSONDataTable(options) {
const deserialize = createJSONDeserializer(options);
const table = new Map();
return {
apply(record) {
const value = deserialize(record.node);
if (record.initial) table.set(record.key, value);
},
get(key) {
return table.get(key);
},
resolve(ref) {
return table.get(ref.$ref);
}
};
function isContainerTraceMarker(value) {
return value != null && typeof value === "object" && value.$tr != null && typeof value.$tr[Symbol.asyncIterator] === "function";
}
function reviveContainerTraces(value) {
if (!state.materializeTrace || value == null || typeof value !== "object") return value;
if (isContainerTraceMarker(value)) return materialize(value);
if (Array.isArray(value)) {
for (let i = 0; i < value.length; i++) value[i] = reviveContainerTraces(value[i]);
} else if (Object.getPrototypeOf(value) === Object.prototype) {
for (const key of Object.keys(value)) value[key] = reviveContainerTraces(value[key]);
}
return value;
}
setContainerTraceMaterializer(materializeContainerTrace);
function asyncArg(value) {

@@ -1368,11 +1440,22 @@ return value;

let sharedHost;
let codec;
let codecLoading;
function loadCodec() {
return codecLoading ??= import('@solidjs/web/serialization/decode').then(m => {
codec = m;
});
}
const tables = new Map();
function ensureTable(root) {
let table = tables.get(root);
if (!table && codec) tables.set(root, table = codec.createJSONDataTable());
return table;
}
function tableFor(id) {
const table = tables.get(id);
if (table) return table;
for (const [root, t] of tables) if (id.startsWith(root + ".")) return t;
if (tables.has(id)) return ensureTable(id);
for (const root of tables.keys()) if (id.startsWith(root + ".")) return ensureTable(root);
return undefined;
}
function beginStream(frameId) {
tables.set(frameId, createJSONDataTable());
tables.set(frameId, undefined);
}

@@ -1382,4 +1465,7 @@ function getFrameHost() {

sharedHost = createFrameHost({
prepareData: loadCodec,
applyData: c => tableFor(c.id)?.apply(c),
resolve: (ref, id) => tableFor(id)?.resolve(ref)
resolve: (ref, id) => tableFor(id)?.resolve(ref),
revive: reviveContainerTraces,
isContainer: isMaterializedContainer
});

@@ -1456,2 +1542,3 @@ }

const v = args()[key];
if (isMaterializedContainer(v)) return v;
if (!isAsyncValue(v)) return v;

@@ -1608,2 +1695,20 @@ let read = asyncReads.get(key);

const claimedBoundaries = new Set();
const liveOps = new Map();
const liveAppliers = new Set();
let livePumped = null;
function pumpLiveChannel() {
const stream = globalThis._$HY?.r?.["sc:live"];
if (!stream || stream === livePumped || typeof stream.getReader !== "function") return;
livePumped = stream;
const reader = stream.getReader();
const pump = () => reader.read().then(r => {
if (r.done) return;
const op = r.value;
liveOps.set(`${op.type}:${op.fid || ""}:${op.key || ""}`, op);
for (const apply of liveAppliers) apply(op);
return pump();
});
pump().catch(() => {
});
}
let boundaryIndex = null;

@@ -1687,2 +1792,3 @@ const isBoundaryId = id => !id.includes(".");

if (!hy || !hy.r) return;
pumpLiveChannel();
const slotPrefix = `sc:slot:${id}:`;

@@ -1722,3 +1828,13 @@ for (const key of Object.keys(hy.r)) {

}) : undefined;
const applyLiveOp = op => {
if (op.type === "slot" && op.fid !== id) return;
host.apply({
...op,
id: address,
version: 0
});
};
liveAppliers.add(applyLiveOp);
onCleanup(() => {
liveAppliers.delete(applyLiveOp);
unsubscribe && unsubscribe();

@@ -1728,2 +1844,3 @@ if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);

drainRecords();
for (const op of liveOps.values()) applyLiveOp(op);
const owner = getOwner();

@@ -1730,0 +1847,0 @@ let release;

{
"name": "@solidjs/web",
"description": "Solid's web runtime: client rendering, hydration, SSR, and DOM-specific control flow (Portal, Dynamic).",
"version": "2.0.0-beta.32",
"version": "2.0.0-beta.33",
"author": "Ryan Carniato",

@@ -36,2 +36,3 @@ "license": "MIT",

"serialization/package.json",
"serialization/decode/package.json",
"server-functions/dist",

@@ -154,2 +155,12 @@ "server-functions/package.json",

},
"./serialization/decode": {
"import": {
"types": "./serialization/types/serializer-decode.d.ts",
"default": "./serialization/dist/decode.js"
},
"require": {
"types": "./serialization/types-cjs/serializer-decode.d.cts",
"default": "./serialization/dist/decode.cjs"
}
},
"./server-functions": {

@@ -372,6 +383,6 @@ "worker": {

"peerDependencies": {
"solid-js": "^2.0.0-beta.32"
"solid-js": "^2.0.0-beta.33"
},
"devDependencies": {
"solid-js": "2.0.0-beta.32"
"solid-js": "2.0.0-beta.33"
},

@@ -383,10 +394,10 @@ "scripts": {

"link": "symlink-dir . node_modules/@solidjs/web",
"types": "npm-run-all -nl types:clean types:copy-jsx types:web types:copy-web types:web-storage types:copy-server-functions types:web-frames types:copy-serialization types:copy-frames types:cjs",
"types": "npm-run-all -nl types:clean types:copy-jsx types:web types:copy-web types:web-storage types:copy-server-functions types:copy-serialization types:web-frames types:copy-frames types:cjs",
"types:clean": "rimraf types/ types-cjs/ storage/types/ storage/types-cjs/ serialization/types/ serialization/types-cjs/ frames/types/",
"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\";'",
"types:web": "tsc --project ./tsconfig.build.json",
"types:copy-web": "ncp ../../node_modules/@dom-expressions/runtime/src/client.d.ts ./types/client.d.ts && ncp ../../node_modules/@dom-expressions/runtime/src/server.d.ts ./types/server.d.ts && ncp ../../node_modules/@dom-expressions/runtime/src/serializer.d.ts ./types/serializer.d.ts && ncp ../../node_modules/@dom-expressions/runtime/src/response.d.ts ./types/response.d.ts && ncp ./src/jsx.d.ts ./types/jsx.d.ts && ncp ./src/jsx-properties.d.ts ./types/jsx-properties.d.ts",
"types:copy-web": "node -e \"const src = fs.readFileSync('../../node_modules/@dom-expressions/runtime/src/client.d.ts', 'utf8'); const marker = 'export type { RequestEventLocals } from'; if (!src.includes(marker)) throw new Error('client.d.ts drift: the type-only RequestEventLocals re-export was not found — revisit the augmentation-identity rewrite'); fs.writeFileSync('./types/client.d.ts', src.replace(marker, 'export { RequestEventLocals } from'));\" && ncp ../../node_modules/@dom-expressions/runtime/src/server.d.ts ./types/server.d.ts && ncp ../../node_modules/@dom-expressions/runtime/src/serializer.d.ts ./types/serializer.d.ts && ncp ../../node_modules/@dom-expressions/runtime/src/serializer-decode.d.ts ./types/serializer-decode.d.ts && ncp ../../node_modules/@dom-expressions/runtime/src/response.d.ts ./types/response.d.ts && ncp ../../node_modules/@dom-expressions/runtime/src/cookies.d.ts ./types/cookies.d.ts && ncp ./src/jsx.d.ts ./types/jsx.d.ts && ncp ./src/jsx-properties.d.ts ./types/jsx-properties.d.ts",
"types:web-storage": "tsc --project ./storage/tsconfig.build.json",
"types:web-frames": "tsc --project ./frames/tsconfig.build.json",
"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');\"",
"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'); fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/serializer-decode.d.ts', './serialization/types/serializer-decode.d.ts');\"",
"types:copy-server-functions": "node -e \"fs.mkdirSync('./types/server-functions', { recursive: true }); for (const f of ['shared', 'flash', 'client', 'server', 'rich-args']) fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/server-functions/' + f + '.d.ts', './types/server-functions/' + f + '.d.ts');\"",

@@ -393,0 +404,0 @@ "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.writeFileSync('./types/frames/' + f + '.d.ts', fs.readFileSync('./frames/types/' + f + '.d.ts', 'utf8').replaceAll('@dom-expressions/runtime/src/', './'));\"",

@@ -6,11 +6,105 @@ 'use strict';

const DEFAULT_DISABLED_FEATURES = seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : seroval.Feature.ErrorPrototypeStack;
const HYDRATION_GLOBAL = "_$HY.r";
const STATE = Symbol.for("dom-expressions.container-trace-state");
const state = globalThis[STATE] || (globalThis[STATE] = {
materialized: new WeakMap(),
materializedValues: new WeakSet()
});
const TRACE = Symbol.for("dom-expressions.container-trace");
function materialize(marker) {
let value = state.materialized.get(marker.$tr);
if (value === undefined) {
value = state.materializeTrace(marker);
state.materialized.set(marker.$tr, value);
if (value !== null && typeof value === "object") state.materializedValues.add(value);
}
return value;
}
function parseTrace(value, ctx) {
const trace = value[TRACE];
return {
a: trace.array ? 1 : 0,
i: ctx.parse(trace.subscribe())
};
}
const ContainerTracePlugin = {
tag: "dom-expressions/container-trace",
test(value) {
return value != null && typeof value === "object" && TRACE in value;
},
parse: {
sync() {
throw new Error("A reactive container can only be serialized by a streaming serializer.");
},
async async(value, ctx) {
const trace = value[TRACE];
return {
a: trace.array ? 1 : 0,
i: await ctx.parse(trace.subscribe())
};
},
stream: parseTrace
},
serialize(node, ctx) {
return "{$tr:" + ctx.serialize(node.i) + ",$ta:" + node.a + "}";
},
deserialize(node, ctx) {
const iterable = ctx.deserialize(node.i);
const marker = {
$tr: iterable,
$ta: node.a
};
return state.materializeTrace ? materialize(marker) : marker;
}
};
const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin,
ContainerTracePlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
}
const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return seroval.fromCrossJSON(node, {
refs,
...resolved
});
};
}
function createJSONDataTable(options) {
const deserialize = createJSONDeserializer(options);
const table = new Map();
return {
apply(record) {
const value = deserialize(record.node);
if (record.initial) table.set(record.key, value);
},
get(key) {
return table.get(key);
},
resolve(ref) {
return table.get(ref.$ref);
}
};
}
const DEFAULT_DISABLED_FEATURES = seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : seroval.Feature.ErrorPrototypeStack;
const HYDRATION_GLOBAL = "_$HY.r";
function createSerializer(options) {

@@ -42,15 +136,2 @@ return new seroval.Serializer({

}
const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
}
function serializeJSON(value, {

@@ -71,12 +152,2 @@ onParse,

}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return seroval.fromCrossJSON(node, {
refs,
...resolved
});
};
}
function createJSONSerializer({

@@ -147,18 +218,2 @@ onData,

}
function createJSONDataTable(options) {
const deserialize = createJSONDeserializer(options);
const table = new Map();
return {
apply(record) {
const value = deserialize(record.node);
if (record.initial) table.set(record.key, value);
},
get(key) {
return table.get(key);
},
resolve(ref) {
return table.get(ref.$ref);
}
};
}

@@ -165,0 +220,0 @@ Object.defineProperty(exports, "OpaqueReference", {

@@ -1,14 +0,108 @@

import { Feature, Serializer, getCrossReferenceHeader, toCrossJSONStream, fromCrossJSON } from 'seroval';
import { Feature, fromCrossJSON, toCrossJSONStream, Serializer, getCrossReferenceHeader } from 'seroval';
export { OpaqueReference, createPlugin } from 'seroval';
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
const DEFAULT_DISABLED_FEATURES = Feature.AggregateError | Feature.BigIntTypedArray;
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
const HYDRATION_GLOBAL = "_$HY.r";
const STATE = Symbol.for("dom-expressions.container-trace-state");
const state = globalThis[STATE] || (globalThis[STATE] = {
materialized: new WeakMap(),
materializedValues: new WeakSet()
});
const TRACE = Symbol.for("dom-expressions.container-trace");
function materialize(marker) {
let value = state.materialized.get(marker.$tr);
if (value === undefined) {
value = state.materializeTrace(marker);
state.materialized.set(marker.$tr, value);
if (value !== null && typeof value === "object") state.materializedValues.add(value);
}
return value;
}
function parseTrace(value, ctx) {
const trace = value[TRACE];
return {
a: trace.array ? 1 : 0,
i: ctx.parse(trace.subscribe())
};
}
const ContainerTracePlugin = {
tag: "dom-expressions/container-trace",
test(value) {
return value != null && typeof value === "object" && TRACE in value;
},
parse: {
sync() {
throw new Error("A reactive container can only be serialized by a streaming serializer.");
},
async async(value, ctx) {
const trace = value[TRACE];
return {
a: trace.array ? 1 : 0,
i: await ctx.parse(trace.subscribe())
};
},
stream: parseTrace
},
serialize(node, ctx) {
return "{$tr:" + ctx.serialize(node.i) + ",$ta:" + node.a + "}";
},
deserialize(node, ctx) {
const iterable = ctx.deserialize(node.i);
const marker = {
$tr: iterable,
$ta: node.a
};
return state.materializeTrace ? materialize(marker) : marker;
}
};
const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin,
ContainerTracePlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
}
const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return fromCrossJSON(node, {
refs,
...resolved
});
};
}
function createJSONDataTable(options) {
const deserialize = createJSONDeserializer(options);
const table = new Map();
return {
apply(record) {
const value = deserialize(record.node);
if (record.initial) table.set(record.key, value);
},
get(key) {
return table.get(key);
},
resolve(ref) {
return table.get(ref.$ref);
}
};
}
const DEFAULT_DISABLED_FEATURES = Feature.AggregateError | Feature.BigIntTypedArray;
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
const HYDRATION_GLOBAL = "_$HY.r";
function createSerializer(options) {

@@ -40,15 +134,2 @@ return new Serializer({

}
const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
}
function serializeJSON(value, {

@@ -69,12 +150,2 @@ onParse,

}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return fromCrossJSON(node, {
refs,
...resolved
});
};
}
function createJSONSerializer({

@@ -145,19 +216,3 @@ onData,

}
function createJSONDataTable(options) {
const deserialize = createJSONDeserializer(options);
const table = new Map();
return {
apply(record) {
const value = deserialize(record.node);
if (record.initial) table.set(record.key, value);
},
get(key) {
return table.get(key);
},
resolve(ref) {
return table.get(ref.$ref);
}
};
}
export { DEFAULT_WEB_PLUGINS, createHydrationSerializer, createJSONDataTable, createJSONDeserializer, createJSONSerializer, createSerializer, getLocalHeaderScript, resolveSerializerPlugins, serializeJSON };

@@ -8,11 +8,16 @@ // Serialization surface (published as `@solidjs/web/serialization`): the

// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";
import type { Serializer } from "seroval";
import {
JSONCodecOptions,
PluginInfo,
SerializerPlugin,
SerovalNode
} from "./serializer-decode.cjs";
/**
* Seroval's node shape — the intermediate representation `serializeJSON`
* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// The decode half — `SerovalNode`, the plugin TYPES, `DEFAULT_WEB_PLUGINS`,
// `resolveSerializerPlugins`, `JSONCodecOptions`, `createJSONDeserializer`,
// `createJSONDataTable` — is declared in serializer-decode.d.ts (published
// as `@solidjs/web/serialization/decode`, the module lazy client consumers
// load) and re-exported here so this remains the full surface.
export * from "./serializer-decode.cjs";

@@ -25,84 +30,6 @@ // ---- Plugin authoring ----

// seroval's own (`createPlugin`, `OpaqueReference` — see serializer.js);
// the TYPES are declared here by hand, like everything else in this file,
// because seroval's published d.ts use extensionless ESM-relative imports
// that `moduleResolution: "nodenext"` cannot follow — a bare type
// re-export would silently degrade the whole authoring surface to `any`
// under skipLibCheck. The declarations mirror seroval ~1.5 exactly; the
// `~` pin is what makes mirroring safe.
// the plugin TYPES live in serializer-decode.d.ts (hand-declared there —
// see its banner for why).
/** Per-plugin bookkeeping seroval hands each plugin callback. */
export interface PluginData {
id: number;
}
/**
* The shape of a plugin's parsed payload: a map of `SerovalNode`s produced
* by the parse contexts, consumed by `serialize`/`deserialize`.
*/
export type PluginInfo = { [key: string]: SerovalNode };
/** Parse context for `parse.sync`: turns child values into nodes. */
export interface SyncParsePluginContext {
parse<T>(current: T): SerovalNode;
}
/** Parse context for `parse.async`: like sync, but child parses await. */
export interface AsyncParsePluginContext {
parse<T>(current: T): Promise<SerovalNode>;
}
/**
* Parse context for `parse.stream`: sync parsing plus the streaming
* lifecycle (pending-state tracking, late node emission, cleanup).
*/
export interface StreamParsePluginContext {
parse<T>(current: T): SerovalNode;
parseWithError<T>(current: T): SerovalNode | undefined;
isAlive(): boolean;
pushPendingState(): void;
popPendingState(): void;
onParse(node: SerovalNode): void;
onError(error: unknown): void;
addCleanup(callback: () => void): void;
}
/** Serialize context: renders child nodes to JS source. */
export interface SerializePluginContext {
serialize(node: SerovalNode): string;
}
/** Deserialize context: revives child nodes to runtime values. */
export interface DeserializePluginContext {
deserialize<T>(node: SerovalNode): T;
}
/**
* A Seroval plugin usable with the web serializers — teaches the codec how
* to encode/decode a custom value type (`Value` is the value it matches,
* `Info` its parsed payload). Supply matching plugins on both peers of a
* transport. Bare `SerializerPlugin` (both parameters defaulted to `any`)
* is the list-element type every `plugins` option accepts.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerializerPlugin<Value = any, Info extends PluginInfo = any> {
/** A unique string identifying the plugin — namespace it (`"app/Thing"`). */
tag: string;
/** Dependency plugins, resolved ahead of this one. */
extends?: SerializerPlugin[];
/** Whether `value` is this plugin's to encode. */
test(value: unknown): boolean;
/** Parsing modes — provide the ones the transports you target use. */
parse: {
sync?: (value: Value, ctx: SyncParsePluginContext, data: PluginData) => Info;
async?: (value: Value, ctx: AsyncParsePluginContext, data: PluginData) => Promise<Info>;
stream?: (value: Value, ctx: StreamParsePluginContext, data: PluginData) => Info;
};
/** Renders the parsed payload as JS source (script-injection form). */
serialize(node: Info, ctx: SerializePluginContext, data: PluginData): string;
/** Revives the parsed payload back into the runtime value. */
deserialize(node: Info, ctx: DeserializePluginContext, data: PluginData): Value;
}
/**
* Builds a `SerializerPlugin` — seroval's `createPlugin`, re-exported so

@@ -136,22 +63,2 @@ * plugin authors stay on the exact seroval instance/version the runtime

/**
* Baseline plugin set for serializing web-platform values (AbortSignal,
* Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).
* Applied by every serializer in this module; custom plugins compose ahead
* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/
export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];
/**
* Composes custom plugins with `DEFAULT_WEB_PLUGINS`. Custom plugins come
* first so they can shadow a default for values both would match. Returns a
* fresh array; the defaults are never mutated. Useful when handing a full
* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/**
* Options for `createSerializer`.

@@ -223,28 +130,6 @@ *

// ---- JSON codec (server function transports) ----
// (`JSONCodecOptions` and the decode half are declared in
// serializer-decode.d.ts and re-exported above.)
/**
* Options shared by both halves of the JSON codec. All of them must match
* on the serializing and deserializing peer or payloads will not
* round-trip — for server functions, set them once through the
* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONCodecOptions {
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. Must match on both peers. */
plugins?: SerializerPlugin[];
/**
* Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
* (payloads may come from an untrusted peer). Must match on both peers.
* Outside development, the encoding side additionally strips
* `Error.prototype.stack` on top of any override — serialized stacks leak
* server paths to the client. Decoding stays permissive, so payloads from
* a development peer still round-trip.
*/
disabledFeatures?: number;
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
depthLimit?: number;
}
/**
* Options for `serializeJSON`.

@@ -277,13 +162,2 @@ *

/**
* Creates the decoding counterpart of `serializeJSON`. Cross-references
* between chunks resolve through state shared across calls, so all chunks
* from one stream must go through the same deserializer instance. The first
* chunk's return value is the decoded source value; feeding later chunks
* settles the async values referenced inside it.
*
* Integration-facing; may change (see the entry banner).
*/
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
/** Options for `createJSONSerializer`. */

@@ -314,17 +188,1 @@ export interface JSONSerializerOptions extends JSONCodecOptions {

};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
* `resolve`. The frames client host wires one per response
* (`applyData: c => table.apply(c)`).
*
* Integration-facing; may change (see the entry banner). This serialization
* entry is the single home of the data table — the frames client consumes
* it internally rather than re-exporting it.
*/
export interface JSONDataTable {
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
resolve<T = unknown>(ref: { $ref: string }): T;
}
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;

@@ -8,11 +8,16 @@ // Serialization surface (published as `@solidjs/web/serialization`): the

// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";
import type { Serializer } from "seroval";
import {
JSONCodecOptions,
PluginInfo,
SerializerPlugin,
SerovalNode
} from "./serializer-decode.js";
/**
* Seroval's node shape — the intermediate representation `serializeJSON`
* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// The decode half — `SerovalNode`, the plugin TYPES, `DEFAULT_WEB_PLUGINS`,
// `resolveSerializerPlugins`, `JSONCodecOptions`, `createJSONDeserializer`,
// `createJSONDataTable` — is declared in serializer-decode.d.ts (published
// as `@solidjs/web/serialization/decode`, the module lazy client consumers
// load) and re-exported here so this remains the full surface.
export * from "./serializer-decode.js";

@@ -25,84 +30,6 @@ // ---- Plugin authoring ----

// seroval's own (`createPlugin`, `OpaqueReference` — see serializer.js);
// the TYPES are declared here by hand, like everything else in this file,
// because seroval's published d.ts use extensionless ESM-relative imports
// that `moduleResolution: "nodenext"` cannot follow — a bare type
// re-export would silently degrade the whole authoring surface to `any`
// under skipLibCheck. The declarations mirror seroval ~1.5 exactly; the
// `~` pin is what makes mirroring safe.
// the plugin TYPES live in serializer-decode.d.ts (hand-declared there —
// see its banner for why).
/** Per-plugin bookkeeping seroval hands each plugin callback. */
export interface PluginData {
id: number;
}
/**
* The shape of a plugin's parsed payload: a map of `SerovalNode`s produced
* by the parse contexts, consumed by `serialize`/`deserialize`.
*/
export type PluginInfo = { [key: string]: SerovalNode };
/** Parse context for `parse.sync`: turns child values into nodes. */
export interface SyncParsePluginContext {
parse<T>(current: T): SerovalNode;
}
/** Parse context for `parse.async`: like sync, but child parses await. */
export interface AsyncParsePluginContext {
parse<T>(current: T): Promise<SerovalNode>;
}
/**
* Parse context for `parse.stream`: sync parsing plus the streaming
* lifecycle (pending-state tracking, late node emission, cleanup).
*/
export interface StreamParsePluginContext {
parse<T>(current: T): SerovalNode;
parseWithError<T>(current: T): SerovalNode | undefined;
isAlive(): boolean;
pushPendingState(): void;
popPendingState(): void;
onParse(node: SerovalNode): void;
onError(error: unknown): void;
addCleanup(callback: () => void): void;
}
/** Serialize context: renders child nodes to JS source. */
export interface SerializePluginContext {
serialize(node: SerovalNode): string;
}
/** Deserialize context: revives child nodes to runtime values. */
export interface DeserializePluginContext {
deserialize<T>(node: SerovalNode): T;
}
/**
* A Seroval plugin usable with the web serializers — teaches the codec how
* to encode/decode a custom value type (`Value` is the value it matches,
* `Info` its parsed payload). Supply matching plugins on both peers of a
* transport. Bare `SerializerPlugin` (both parameters defaulted to `any`)
* is the list-element type every `plugins` option accepts.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerializerPlugin<Value = any, Info extends PluginInfo = any> {
/** A unique string identifying the plugin — namespace it (`"app/Thing"`). */
tag: string;
/** Dependency plugins, resolved ahead of this one. */
extends?: SerializerPlugin[];
/** Whether `value` is this plugin's to encode. */
test(value: unknown): boolean;
/** Parsing modes — provide the ones the transports you target use. */
parse: {
sync?: (value: Value, ctx: SyncParsePluginContext, data: PluginData) => Info;
async?: (value: Value, ctx: AsyncParsePluginContext, data: PluginData) => Promise<Info>;
stream?: (value: Value, ctx: StreamParsePluginContext, data: PluginData) => Info;
};
/** Renders the parsed payload as JS source (script-injection form). */
serialize(node: Info, ctx: SerializePluginContext, data: PluginData): string;
/** Revives the parsed payload back into the runtime value. */
deserialize(node: Info, ctx: DeserializePluginContext, data: PluginData): Value;
}
/**
* Builds a `SerializerPlugin` — seroval's `createPlugin`, re-exported so

@@ -136,22 +63,2 @@ * plugin authors stay on the exact seroval instance/version the runtime

/**
* Baseline plugin set for serializing web-platform values (AbortSignal,
* Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).
* Applied by every serializer in this module; custom plugins compose ahead
* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/
export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];
/**
* Composes custom plugins with `DEFAULT_WEB_PLUGINS`. Custom plugins come
* first so they can shadow a default for values both would match. Returns a
* fresh array; the defaults are never mutated. Useful when handing a full
* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/**
* Options for `createSerializer`.

@@ -223,28 +130,6 @@ *

// ---- JSON codec (server function transports) ----
// (`JSONCodecOptions` and the decode half are declared in
// serializer-decode.d.ts and re-exported above.)
/**
* Options shared by both halves of the JSON codec. All of them must match
* on the serializing and deserializing peer or payloads will not
* round-trip — for server functions, set them once through the
* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONCodecOptions {
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. Must match on both peers. */
plugins?: SerializerPlugin[];
/**
* Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
* (payloads may come from an untrusted peer). Must match on both peers.
* Outside development, the encoding side additionally strips
* `Error.prototype.stack` on top of any override — serialized stacks leak
* server paths to the client. Decoding stays permissive, so payloads from
* a development peer still round-trip.
*/
disabledFeatures?: number;
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
depthLimit?: number;
}
/**
* Options for `serializeJSON`.

@@ -277,13 +162,2 @@ *

/**
* Creates the decoding counterpart of `serializeJSON`. Cross-references
* between chunks resolve through state shared across calls, so all chunks
* from one stream must go through the same deserializer instance. The first
* chunk's return value is the decoded source value; feeding later chunks
* settles the async values referenced inside it.
*
* Integration-facing; may change (see the entry banner).
*/
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
/** Options for `createJSONSerializer`. */

@@ -314,17 +188,1 @@ export interface JSONSerializerOptions extends JSONCodecOptions {

};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
* `resolve`. The frames client host wires one per response
* (`applyData: c => table.apply(c)`).
*
* Integration-facing; may change (see the entry banner). This serialization
* entry is the single home of the data table — the frames client consumes
* it internally rather than re-exporting it.
*/
export interface JSONDataTable {
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
resolve<T = unknown>(ref: { $ref: string }): T;
}
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;
'use strict';
var seroval = require('seroval');
var web = require('seroval-plugins/web');
const REVALIDATE_HEADER = "X-Revalidate";
seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : seroval.Feature.ErrorPrototypeStack;
const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function serializeJSON(value, {
onParse,
onDone,
onError,
...codecOptions
}) {
const resolved = resolveCodecOptions(codecOptions);
return seroval.toCrossJSONStream(value, {
onParse,
onDone,
onError,
...resolved,
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
});
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return seroval.fromCrossJSON(node, {
refs,
...resolved
});
};
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
function provideServerFunctionRPC(rpc) {
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
}
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const codecConfig = {

@@ -120,18 +100,2 @@ codec: undefined

}
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
const FUNCTION_HEADER = "X-Server-Function-Id";

@@ -167,10 +131,2 @@ const ERROR_HEADER = "X-Server-Function-Error";

const FILE_FORM_KEY = "__server_function_file__";
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const BodyFormat = {

@@ -187,2 +143,34 @@ Serialized: "0",

};
const JSON_SAFE_DEPTH_LIMIT = 10000;
const EXIT = {};
function isJSONSafe(value) {
const stack = [value];
const ancestors = new Set();
while (stack.length) {
const v = stack.pop();
if (v === EXIT) {
ancestors.delete(stack.pop());
continue;
}
if (v === null) continue;
const t = typeof v;
if (t === "string" || t === "boolean") continue;
if (t === "number") {
if (!Number.isFinite(v)) return false;
continue;
}
if (t !== "object") return false;
if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
ancestors.add(v);
stack.push(v, EXIT);
if (Array.isArray(v)) {
for (let i = 0; i < v.length; i++) stack.push(v[i]);
} else {
const proto = Object.getPrototypeOf(v);
if (proto !== Object.prototype && proto !== null) return false;
for (const k in v) stack.push(v[k]);
}
}
return true;
}
function getHeadersAndBody(body) {

@@ -347,3 +335,6 @@ switch (true) {

return new ReadableStream({
start(controller) {
async start(controller) {
const {
serializeJSON
} = await import('@solidjs/web/serialization');
serializeJSON(value, {

@@ -375,2 +366,5 @@ ...codecOptions,

if (!result.done) {
const {
createJSONDeserializer
} = await import('@solidjs/web/serialization/decode');
const deserializeChunk = createJSONDeserializer(codecOptions);

@@ -408,17 +402,2 @@ function interpretChunk(chunk) {

};
function isJSONSafe(value) {
if (value === null) return true;
const t = typeof value;
if (t === "string" || t === "boolean") return true;
if (t === "number") return Number.isFinite(value);
if (t !== "object") return false;
if (Array.isArray(value)) {
for (const v of value) if (!isJSONSafe(v)) return false;
return true;
}
const proto = Object.getPrototypeOf(value);
if (proto !== Object.prototype && proto !== null) return false;
for (const k in value) if (!isJSONSafe(value[k])) return false;
return true;
}
function serializeArguments(args) {

@@ -444,2 +423,11 @@ if (!config.serializeArgs) {

let INSTANCE = 0;
let rpcProvided = false;
function provideRPC() {
if (rpcProvided) return;
rpcProvided = true;
provideServerFunctionRPC({
GET,
decodeResponse
});
}
async function createRequest(base, id, instance, options, meta) {

@@ -484,28 +472,34 @@ const headers = {

}
if (isJSONSafe(args)) {
return createRequest(base, id, instance, {
...options,
body: JSON.stringify(args),
headers: {
...options.headers,
"Content-Type": "application/json",
[BODY_FORMAT_HEADER]: BodyFormat.Json
}
}, meta);
}
if (args.length > 1) {
const trailing = getHeadersAndBody(args[args.length - 1]);
const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
if (trailing && isJSONSafe(leading)) {
const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
return createRequest(target, id, instance, {
try {
if (isJSONSafe(args)) {
return createRequest(base, id, instance, {
...options,
body: trailing.body,
body: JSON.stringify(args),
headers: {
...options.headers,
...trailing.headers
"Content-Type": "application/json",
[BODY_FORMAT_HEADER]: BodyFormat.Json
}
}, meta);
}
} catch {
}
if (args.length > 1) {
try {
const trailing = getHeadersAndBody(args[args.length - 1]);
const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
if (trailing && isJSONSafe(leading)) {
const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
return createRequest(target, id, instance, {
...options,
body: trailing.body,
headers: {
...options.headers,
...trailing.headers
}
}, meta);
}
} catch {
}
}
return createRequest(base, id, instance, {

@@ -561,2 +555,3 @@ ...options,

function createServerReference(id, name, base) {
provideRPC();
const metadata = name === undefined ? {} : {

@@ -592,2 +587,3 @@ name

}
provideRPC();
const id = fn.id;

@@ -594,0 +590,0 @@ const metadata = {

@@ -1,53 +0,33 @@

import { fromCrossJSON, Feature, toCrossJSONStream } from 'seroval';
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
const REVALIDATE_HEADER = "X-Revalidate";
Feature.AggregateError | Feature.BigIntTypedArray;
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function serializeJSON(value, {
onParse,
onDone,
onError,
...codecOptions
}) {
const resolved = resolveCodecOptions(codecOptions);
return toCrossJSONStream(value, {
onParse,
onDone,
onError,
...resolved,
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
});
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return fromCrossJSON(node, {
refs,
...resolved
});
};
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
function provideServerFunctionRPC(rpc) {
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
}
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const codecConfig = {

@@ -118,18 +98,2 @@ codec: undefined

}
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
const FUNCTION_HEADER = "X-Server-Function-Id";

@@ -165,10 +129,2 @@ const ERROR_HEADER = "X-Server-Function-Error";

const FILE_FORM_KEY = "__server_function_file__";
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const BodyFormat = {

@@ -185,2 +141,34 @@ Serialized: "0",

};
const JSON_SAFE_DEPTH_LIMIT = 10000;
const EXIT = {};
function isJSONSafe(value) {
const stack = [value];
const ancestors = new Set();
while (stack.length) {
const v = stack.pop();
if (v === EXIT) {
ancestors.delete(stack.pop());
continue;
}
if (v === null) continue;
const t = typeof v;
if (t === "string" || t === "boolean") continue;
if (t === "number") {
if (!Number.isFinite(v)) return false;
continue;
}
if (t !== "object") return false;
if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
ancestors.add(v);
stack.push(v, EXIT);
if (Array.isArray(v)) {
for (let i = 0; i < v.length; i++) stack.push(v[i]);
} else {
const proto = Object.getPrototypeOf(v);
if (proto !== Object.prototype && proto !== null) return false;
for (const k in v) stack.push(v[k]);
}
}
return true;
}
function getHeadersAndBody(body) {

@@ -345,3 +333,6 @@ switch (true) {

return new ReadableStream({
start(controller) {
async start(controller) {
const {
serializeJSON
} = await import('@solidjs/web/serialization');
serializeJSON(value, {

@@ -373,2 +364,5 @@ ...codecOptions,

if (!result.done) {
const {
createJSONDeserializer
} = await import('@solidjs/web/serialization/decode');
const deserializeChunk = createJSONDeserializer(codecOptions);

@@ -406,17 +400,2 @@ function interpretChunk(chunk) {

};
function isJSONSafe(value) {
if (value === null) return true;
const t = typeof value;
if (t === "string" || t === "boolean") return true;
if (t === "number") return Number.isFinite(value);
if (t !== "object") return false;
if (Array.isArray(value)) {
for (const v of value) if (!isJSONSafe(v)) return false;
return true;
}
const proto = Object.getPrototypeOf(value);
if (proto !== Object.prototype && proto !== null) return false;
for (const k in value) if (!isJSONSafe(value[k])) return false;
return true;
}
function serializeArguments(args) {

@@ -442,2 +421,11 @@ if (!config.serializeArgs) {

let INSTANCE = 0;
let rpcProvided = false;
function provideRPC() {
if (rpcProvided) return;
rpcProvided = true;
provideServerFunctionRPC({
GET,
decodeResponse
});
}
async function createRequest(base, id, instance, options, meta) {

@@ -482,28 +470,34 @@ const headers = {

}
if (isJSONSafe(args)) {
return createRequest(base, id, instance, {
...options,
body: JSON.stringify(args),
headers: {
...options.headers,
"Content-Type": "application/json",
[BODY_FORMAT_HEADER]: BodyFormat.Json
}
}, meta);
}
if (args.length > 1) {
const trailing = getHeadersAndBody(args[args.length - 1]);
const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
if (trailing && isJSONSafe(leading)) {
const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
return createRequest(target, id, instance, {
try {
if (isJSONSafe(args)) {
return createRequest(base, id, instance, {
...options,
body: trailing.body,
body: JSON.stringify(args),
headers: {
...options.headers,
...trailing.headers
"Content-Type": "application/json",
[BODY_FORMAT_HEADER]: BodyFormat.Json
}
}, meta);
}
} catch {
}
if (args.length > 1) {
try {
const trailing = getHeadersAndBody(args[args.length - 1]);
const leading = args.slice(0, -1).map(arg => arg === undefined ? null : arg);
if (trailing && isJSONSafe(leading)) {
const target = base + (base.includes("?") ? "&" : "?") + "args=" + encodeURIComponent(JSON.stringify(leading));
return createRequest(target, id, instance, {
...options,
body: trailing.body,
headers: {
...options.headers,
...trailing.headers
}
}, meta);
}
} catch {
}
}
return createRequest(base, id, instance, {

@@ -559,2 +553,3 @@ ...options,

function createServerReference(id, name, base) {
provideRPC();
const metadata = name === undefined ? {} : {

@@ -590,2 +585,3 @@ name

}
provideRPC();
const id = fn.id;

@@ -592,0 +588,0 @@ const metadata = {

'use strict';
var seroval = require('seroval');
var web = require('seroval-plugins/web');
var solidJs = require('solid-js');

@@ -17,49 +15,68 @@

seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : seroval.Feature.ErrorPrototypeStack;
const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function serializeJSON(value, {
onParse,
onDone,
onError,
...codecOptions
}) {
const resolved = resolveCodecOptions(codecOptions);
return seroval.toCrossJSONStream(value, {
onParse,
onDone,
onError,
...resolved,
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
});
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return seroval.fromCrossJSON(node, {
refs,
...resolved
});
};
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
function provideServerFunctionRPC(rpc) {
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
}
function parseCookieHeader(header) {
const cookies = {};
if (!header) return cookies;
for (const part of header.split(";")) {
const eq = part.indexOf("=");
if (eq < 0) continue;
const name = decodeSafe(part.slice(0, eq).trim());
let value = part.slice(eq + 1).trim();
if (value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
value = value.slice(1, -1);
}
cookies[name] = decodeSafe(value);
}
return cookies;
}
function decodeSafe(text) {
try {
return decodeURIComponent(text);
} catch {
return text;
}
}
function serializeCookie(name, value, options = {}) {
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
cookie += `; Path=${options.path === undefined ? "/" : options.path}`;
if (options.domain) cookie += `; Domain=${options.domain}`;
if (options.maxAge !== undefined) cookie += `; Max-Age=${Math.trunc(options.maxAge)}`;
if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
if (options.httpOnly) cookie += "; HttpOnly";
if (options.secure) cookie += "; Secure";
if (options.sameSite) {
const sameSite = options.sameSite.toLowerCase();
cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
}
return cookie;
}
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const codecConfig = {

@@ -78,18 +95,2 @@ codec: undefined

}
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
const FUNCTION_HEADER = "X-Server-Function-Id";

@@ -125,10 +126,2 @@ const ERROR_HEADER = "X-Server-Function-Error";

const FILE_FORM_KEY = "__server_function_file__";
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const BodyFormat = {

@@ -145,2 +138,34 @@ Serialized: "0",

};
const JSON_SAFE_DEPTH_LIMIT = 10000;
const EXIT = {};
function isJSONSafe(value) {
const stack = [value];
const ancestors = new Set();
while (stack.length) {
const v = stack.pop();
if (v === EXIT) {
ancestors.delete(stack.pop());
continue;
}
if (v === null) continue;
const t = typeof v;
if (t === "string" || t === "boolean") continue;
if (t === "number") {
if (!Number.isFinite(v)) return false;
continue;
}
if (t !== "object") return false;
if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
ancestors.add(v);
stack.push(v, EXIT);
if (Array.isArray(v)) {
for (let i = 0; i < v.length; i++) stack.push(v[i]);
} else {
const proto = Object.getPrototypeOf(v);
if (proto !== Object.prototype && proto !== null) return false;
for (const k in v) stack.push(v[k]);
}
}
return true;
}
function getHeadersAndBody(body) {

@@ -305,3 +330,6 @@ switch (true) {

return new ReadableStream({
start(controller) {
async start(controller) {
const {
serializeJSON
} = await import('@solidjs/web/serialization');
serializeJSON(value, {

@@ -329,2 +357,5 @@ ...codecOptions,

if (!result.done) {
const {
createJSONDeserializer
} = await import('@solidjs/web/serialization/decode');
const deserializeChunk = createJSONDeserializer(codecOptions);

@@ -359,39 +390,2 @@ function interpretChunk(chunk) {

function parseCookieHeader(header) {
const cookies = {};
if (!header) return cookies;
for (const part of header.split(";")) {
const eq = part.indexOf("=");
if (eq < 0) continue;
const name = decodeSafe(part.slice(0, eq).trim());
let value = part.slice(eq + 1).trim();
if (value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
value = value.slice(1, -1);
}
cookies[name] = decodeSafe(value);
}
return cookies;
}
function decodeSafe(text) {
try {
return decodeURIComponent(text);
} catch {
return text;
}
}
function serializeCookie(name, value, options = {}) {
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
cookie += `; Path=${options.path === undefined ? "/" : options.path}`;
if (options.domain) cookie += `; Domain=${options.domain}`;
if (options.maxAge !== undefined) cookie += `; Max-Age=${Math.trunc(options.maxAge)}`;
if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
if (options.httpOnly) cookie += "; HttpOnly";
if (options.secure) cookie += "; Secure";
if (options.sameSite) {
const sameSite = options.sameSite.toLowerCase();
cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
}
return cookie;
}
const RequestContext = Symbol.for("solid.RequestContext");

@@ -560,3 +554,13 @@ function getRequestEvent() {

const INVOCATIONS = new WeakMap();
let rpcProvided = false;
function provideRPC() {
if (rpcProvided) return;
rpcProvided = true;
provideServerFunctionRPC({
GET,
decodeResponse
});
}
function registerServerFunction(id, callback) {
provideRPC();
REGISTRATIONS.set(id, callback);

@@ -586,2 +590,3 @@ return callback;

if (typeof fn !== "function") throw new Error("Export from a 'use server' module must be a function");
provideRPC();
const metadata = name === undefined ? {} : {

@@ -698,3 +703,5 @@ name

}
return {
return outcome.value === undefined ? {
data
} : {
value: outcome.value,

@@ -831,2 +838,19 @@ data

}
if (value === undefined) {
return new Response(null, {
status,
headers
});
}
try {
if (isJSONSafe(value)) {
headers.set(BODY_FORMAT_HEADER, BodyFormat.Json);
headers.set("Content-Type", "application/json");
return new Response(JSON.stringify(value), {
status,
headers
});
}
} catch {
}
const response = serializedResponse(value, headers, codec);

@@ -833,0 +857,0 @@ return status === 200 ? response : new Response(response.body, {

'use strict';
var seroval = require('seroval');
var web = require('seroval-plugins/web');
var solidJs = require('solid-js');

@@ -17,49 +15,68 @@

seroval.Feature.AggregateError | seroval.Feature.BigIntTypedArray;
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : seroval.Feature.ErrorPrototypeStack;
const DEFAULT_WEB_PLUGINS = Object.freeze([web.AbortSignalPlugin,
web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,
web.FormDataPlugin, web.HeadersPlugin, web.ReadableStreamPlugin, web.RequestPlugin, web.ResponsePlugin, web.URLSearchParamsPlugin, web.URLPlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
const JSON_CODEC_DISABLED_FEATURES = seroval.Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function serializeJSON(value, {
onParse,
onDone,
onError,
...codecOptions
}) {
const resolved = resolveCodecOptions(codecOptions);
return seroval.toCrossJSONStream(value, {
onParse,
onDone,
onError,
...resolved,
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
});
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return seroval.fromCrossJSON(node, {
refs,
...resolved
});
};
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
function provideServerFunctionRPC(rpc) {
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
}
function parseCookieHeader(header) {
const cookies = {};
if (!header) return cookies;
for (const part of header.split(";")) {
const eq = part.indexOf("=");
if (eq < 0) continue;
const name = decodeSafe(part.slice(0, eq).trim());
let value = part.slice(eq + 1).trim();
if (value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
value = value.slice(1, -1);
}
cookies[name] = decodeSafe(value);
}
return cookies;
}
function decodeSafe(text) {
try {
return decodeURIComponent(text);
} catch {
return text;
}
}
function serializeCookie(name, value, options = {}) {
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
cookie += `; Path=${options.path === undefined ? "/" : options.path}`;
if (options.domain) cookie += `; Domain=${options.domain}`;
if (options.maxAge !== undefined) cookie += `; Max-Age=${Math.trunc(options.maxAge)}`;
if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
if (options.httpOnly) cookie += "; HttpOnly";
if (options.secure) cookie += "; Secure";
if (options.sameSite) {
const sameSite = options.sameSite.toLowerCase();
cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
}
return cookie;
}
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const codecConfig = {

@@ -78,18 +95,2 @@ codec: undefined

}
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
const FUNCTION_HEADER = "X-Server-Function-Id";

@@ -125,10 +126,2 @@ const ERROR_HEADER = "X-Server-Function-Error";

const FILE_FORM_KEY = "__server_function_file__";
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const BodyFormat = {

@@ -145,2 +138,34 @@ Serialized: "0",

};
const JSON_SAFE_DEPTH_LIMIT = 10000;
const EXIT = {};
function isJSONSafe(value) {
const stack = [value];
const ancestors = new Set();
while (stack.length) {
const v = stack.pop();
if (v === EXIT) {
ancestors.delete(stack.pop());
continue;
}
if (v === null) continue;
const t = typeof v;
if (t === "string" || t === "boolean") continue;
if (t === "number") {
if (!Number.isFinite(v)) return false;
continue;
}
if (t !== "object") return false;
if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
ancestors.add(v);
stack.push(v, EXIT);
if (Array.isArray(v)) {
for (let i = 0; i < v.length; i++) stack.push(v[i]);
} else {
const proto = Object.getPrototypeOf(v);
if (proto !== Object.prototype && proto !== null) return false;
for (const k in v) stack.push(v[k]);
}
}
return true;
}
function getHeadersAndBody(body) {

@@ -305,3 +330,6 @@ switch (true) {

return new ReadableStream({
start(controller) {
async start(controller) {
const {
serializeJSON
} = await import('@solidjs/web/serialization');
serializeJSON(value, {

@@ -329,2 +357,5 @@ ...codecOptions,

if (!result.done) {
const {
createJSONDeserializer
} = await import('@solidjs/web/serialization/decode');
const deserializeChunk = createJSONDeserializer(codecOptions);

@@ -359,39 +390,2 @@ function interpretChunk(chunk) {

function parseCookieHeader(header) {
const cookies = {};
if (!header) return cookies;
for (const part of header.split(";")) {
const eq = part.indexOf("=");
if (eq < 0) continue;
const name = decodeSafe(part.slice(0, eq).trim());
let value = part.slice(eq + 1).trim();
if (value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
value = value.slice(1, -1);
}
cookies[name] = decodeSafe(value);
}
return cookies;
}
function decodeSafe(text) {
try {
return decodeURIComponent(text);
} catch {
return text;
}
}
function serializeCookie(name, value, options = {}) {
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
cookie += `; Path=${options.path === undefined ? "/" : options.path}`;
if (options.domain) cookie += `; Domain=${options.domain}`;
if (options.maxAge !== undefined) cookie += `; Max-Age=${Math.trunc(options.maxAge)}`;
if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
if (options.httpOnly) cookie += "; HttpOnly";
if (options.secure) cookie += "; Secure";
if (options.sameSite) {
const sameSite = options.sameSite.toLowerCase();
cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
}
return cookie;
}
const RequestContext = Symbol.for("solid.RequestContext");

@@ -560,3 +554,13 @@ function getRequestEvent() {

const INVOCATIONS = new WeakMap();
let rpcProvided = false;
function provideRPC() {
if (rpcProvided) return;
rpcProvided = true;
provideServerFunctionRPC({
GET,
decodeResponse
});
}
function registerServerFunction(id, callback) {
provideRPC();
REGISTRATIONS.set(id, callback);

@@ -586,2 +590,3 @@ return callback;

if (typeof fn !== "function") throw new Error("Export from a 'use server' module must be a function");
provideRPC();
const metadata = name === undefined ? {} : {

@@ -698,3 +703,5 @@ name

}
return {
return outcome.value === undefined ? {
data
} : {
value: outcome.value,

@@ -831,2 +838,19 @@ data

}
if (value === undefined) {
return new Response(null, {
status,
headers
});
}
try {
if (isJSONSafe(value)) {
headers.set(BODY_FORMAT_HEADER, BodyFormat.Json);
headers.set("Content-Type", "application/json");
return new Response(JSON.stringify(value), {
status,
headers
});
}
} catch {
}
const response = serializedResponse(value, headers, codec);

@@ -833,0 +857,0 @@ return status === 200 ? response : new Response(response.body, {

@@ -1,3 +0,1 @@

import { fromCrossJSON, Feature, toCrossJSONStream } from 'seroval';
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
import { sharedConfig } from 'solid-js';

@@ -15,49 +13,68 @@

Feature.AggregateError | Feature.BigIntTypedArray;
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function serializeJSON(value, {
onParse,
onDone,
onError,
...codecOptions
}) {
const resolved = resolveCodecOptions(codecOptions);
return toCrossJSONStream(value, {
onParse,
onDone,
onError,
...resolved,
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
});
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return fromCrossJSON(node, {
refs,
...resolved
});
};
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
function provideServerFunctionRPC(rpc) {
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
}
function parseCookieHeader(header) {
const cookies = {};
if (!header) return cookies;
for (const part of header.split(";")) {
const eq = part.indexOf("=");
if (eq < 0) continue;
const name = decodeSafe(part.slice(0, eq).trim());
let value = part.slice(eq + 1).trim();
if (value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
value = value.slice(1, -1);
}
cookies[name] = decodeSafe(value);
}
return cookies;
}
function decodeSafe(text) {
try {
return decodeURIComponent(text);
} catch {
return text;
}
}
function serializeCookie(name, value, options = {}) {
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
cookie += `; Path=${options.path === undefined ? "/" : options.path}`;
if (options.domain) cookie += `; Domain=${options.domain}`;
if (options.maxAge !== undefined) cookie += `; Max-Age=${Math.trunc(options.maxAge)}`;
if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
if (options.httpOnly) cookie += "; HttpOnly";
if (options.secure) cookie += "; Secure";
if (options.sameSite) {
const sameSite = options.sameSite.toLowerCase();
cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
}
return cookie;
}
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const codecConfig = {

@@ -76,18 +93,2 @@ codec: undefined

}
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
const FUNCTION_HEADER = "X-Server-Function-Id";

@@ -123,10 +124,2 @@ const ERROR_HEADER = "X-Server-Function-Error";

const FILE_FORM_KEY = "__server_function_file__";
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const BodyFormat = {

@@ -143,2 +136,34 @@ Serialized: "0",

};
const JSON_SAFE_DEPTH_LIMIT = 10000;
const EXIT = {};
function isJSONSafe(value) {
const stack = [value];
const ancestors = new Set();
while (stack.length) {
const v = stack.pop();
if (v === EXIT) {
ancestors.delete(stack.pop());
continue;
}
if (v === null) continue;
const t = typeof v;
if (t === "string" || t === "boolean") continue;
if (t === "number") {
if (!Number.isFinite(v)) return false;
continue;
}
if (t !== "object") return false;
if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
ancestors.add(v);
stack.push(v, EXIT);
if (Array.isArray(v)) {
for (let i = 0; i < v.length; i++) stack.push(v[i]);
} else {
const proto = Object.getPrototypeOf(v);
if (proto !== Object.prototype && proto !== null) return false;
for (const k in v) stack.push(v[k]);
}
}
return true;
}
function getHeadersAndBody(body) {

@@ -303,3 +328,6 @@ switch (true) {

return new ReadableStream({
start(controller) {
async start(controller) {
const {
serializeJSON
} = await import('@solidjs/web/serialization');
serializeJSON(value, {

@@ -327,2 +355,5 @@ ...codecOptions,

if (!result.done) {
const {
createJSONDeserializer
} = await import('@solidjs/web/serialization/decode');
const deserializeChunk = createJSONDeserializer(codecOptions);

@@ -357,39 +388,2 @@ function interpretChunk(chunk) {

function parseCookieHeader(header) {
const cookies = {};
if (!header) return cookies;
for (const part of header.split(";")) {
const eq = part.indexOf("=");
if (eq < 0) continue;
const name = decodeSafe(part.slice(0, eq).trim());
let value = part.slice(eq + 1).trim();
if (value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
value = value.slice(1, -1);
}
cookies[name] = decodeSafe(value);
}
return cookies;
}
function decodeSafe(text) {
try {
return decodeURIComponent(text);
} catch {
return text;
}
}
function serializeCookie(name, value, options = {}) {
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
cookie += `; Path=${options.path === undefined ? "/" : options.path}`;
if (options.domain) cookie += `; Domain=${options.domain}`;
if (options.maxAge !== undefined) cookie += `; Max-Age=${Math.trunc(options.maxAge)}`;
if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
if (options.httpOnly) cookie += "; HttpOnly";
if (options.secure) cookie += "; Secure";
if (options.sameSite) {
const sameSite = options.sameSite.toLowerCase();
cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
}
return cookie;
}
const RequestContext = Symbol.for("solid.RequestContext");

@@ -558,3 +552,13 @@ function getRequestEvent() {

const INVOCATIONS = new WeakMap();
let rpcProvided = false;
function provideRPC() {
if (rpcProvided) return;
rpcProvided = true;
provideServerFunctionRPC({
GET,
decodeResponse
});
}
function registerServerFunction(id, callback) {
provideRPC();
REGISTRATIONS.set(id, callback);

@@ -584,2 +588,3 @@ return callback;

if (typeof fn !== "function") throw new Error("Export from a 'use server' module must be a function");
provideRPC();
const metadata = name === undefined ? {} : {

@@ -696,3 +701,5 @@ name

}
return {
return outcome.value === undefined ? {
data
} : {
value: outcome.value,

@@ -829,2 +836,19 @@ data

}
if (value === undefined) {
return new Response(null, {
status,
headers
});
}
try {
if (isJSONSafe(value)) {
headers.set(BODY_FORMAT_HEADER, BodyFormat.Json);
headers.set("Content-Type", "application/json");
return new Response(JSON.stringify(value), {
status,
headers
});
}
} catch {
}
const response = serializedResponse(value, headers, codec);

@@ -831,0 +855,0 @@ return status === 200 ? response : new Response(response.body, {

@@ -1,3 +0,1 @@

import { fromCrossJSON, Feature, toCrossJSONStream } from 'seroval';
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';
import { sharedConfig } from 'solid-js';

@@ -15,49 +13,68 @@

Feature.AggregateError | Feature.BigIntTypedArray;
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,
CustomEventPlugin, DOMExceptionPlugin, EventPlugin,
FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin]);
function resolveSerializerPlugins(customPlugins) {
return customPlugins ? [...customPlugins, ...DEFAULT_WEB_PLUGINS] : [...DEFAULT_WEB_PLUGINS];
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
const JSON_CODEC_DISABLED_FEATURES = Feature.RegExp;
const JSON_CODEC_DEPTH_LIMIT = 64;
function resolveCodecOptions({
plugins,
disabledFeatures,
depthLimit
} = {}) {
return {
plugins: resolveSerializerPlugins(plugins),
disabledFeatures: disabledFeatures === undefined ? JSON_CODEC_DISABLED_FEATURES : disabledFeatures,
depthLimit: depthLimit === undefined ? JSON_CODEC_DEPTH_LIMIT : depthLimit
};
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function serializeJSON(value, {
onParse,
onDone,
onError,
...codecOptions
}) {
const resolved = resolveCodecOptions(codecOptions);
return toCrossJSONStream(value, {
onParse,
onDone,
onError,
...resolved,
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
});
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return fromCrossJSON(node, {
refs,
...resolved
});
};
const SERVER_FUNCTION_RPC = Symbol.for("solid.ServerFunctionRPC");
function provideServerFunctionRPC(rpc) {
globalThis[SERVER_FUNCTION_RPC] || (globalThis[SERVER_FUNCTION_RPC] = rpc);
}
function parseCookieHeader(header) {
const cookies = {};
if (!header) return cookies;
for (const part of header.split(";")) {
const eq = part.indexOf("=");
if (eq < 0) continue;
const name = decodeSafe(part.slice(0, eq).trim());
let value = part.slice(eq + 1).trim();
if (value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
value = value.slice(1, -1);
}
cookies[name] = decodeSafe(value);
}
return cookies;
}
function decodeSafe(text) {
try {
return decodeURIComponent(text);
} catch {
return text;
}
}
function serializeCookie(name, value, options = {}) {
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
cookie += `; Path=${options.path === undefined ? "/" : options.path}`;
if (options.domain) cookie += `; Domain=${options.domain}`;
if (options.maxAge !== undefined) cookie += `; Max-Age=${Math.trunc(options.maxAge)}`;
if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
if (options.httpOnly) cookie += "; HttpOnly";
if (options.secure) cookie += "; Secure";
if (options.sameSite) {
const sameSite = options.sameSite.toLowerCase();
cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
}
return cookie;
}
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const codecConfig = {

@@ -76,18 +93,2 @@ codec: undefined

}
const SERVER_FUNCTION_METADATA = Symbol.for("solid.ServerFunctionMetadata");
function getServerFunctionMetadata(fn) {
if (typeof fn !== "function") return undefined;
return fn[SERVER_FUNCTION_METADATA] || undefined;
}
function isServerFunction(fn) {
return typeof fn === "function" && !!fn[SERVER_FUNCTION_METADATA];
}
function withMeta(fn, meta) {
const metadata = getServerFunctionMetadata(fn);
if (!metadata) {
throw new Error("withMeta expects a server function reference");
}
Object.assign(metadata, meta);
return fn;
}
const FUNCTION_HEADER = "X-Server-Function-Id";

@@ -123,10 +124,2 @@ const ERROR_HEADER = "X-Server-Function-Error";

const FILE_FORM_KEY = "__server_function_file__";
const FLASH_COOKIE = "flash";
const FLASH_MATCHER = new RegExp(`(?:^|;\\s*)${FLASH_COOKIE}=([^;]+)`);
function hasFlashCookie(cookieHeader) {
return !!cookieHeader && FLASH_MATCHER.test(cookieHeader);
}
function clearFlashCookie() {
return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;
}
const BodyFormat = {

@@ -143,2 +136,34 @@ Serialized: "0",

};
const JSON_SAFE_DEPTH_LIMIT = 10000;
const EXIT = {};
function isJSONSafe(value) {
const stack = [value];
const ancestors = new Set();
while (stack.length) {
const v = stack.pop();
if (v === EXIT) {
ancestors.delete(stack.pop());
continue;
}
if (v === null) continue;
const t = typeof v;
if (t === "string" || t === "boolean") continue;
if (t === "number") {
if (!Number.isFinite(v)) return false;
continue;
}
if (t !== "object") return false;
if (ancestors.has(v) || ancestors.size >= JSON_SAFE_DEPTH_LIMIT) return false;
ancestors.add(v);
stack.push(v, EXIT);
if (Array.isArray(v)) {
for (let i = 0; i < v.length; i++) stack.push(v[i]);
} else {
const proto = Object.getPrototypeOf(v);
if (proto !== Object.prototype && proto !== null) return false;
for (const k in v) stack.push(v[k]);
}
}
return true;
}
function getHeadersAndBody(body) {

@@ -303,3 +328,6 @@ switch (true) {

return new ReadableStream({
start(controller) {
async start(controller) {
const {
serializeJSON
} = await import('@solidjs/web/serialization');
serializeJSON(value, {

@@ -327,2 +355,5 @@ ...codecOptions,

if (!result.done) {
const {
createJSONDeserializer
} = await import('@solidjs/web/serialization/decode');
const deserializeChunk = createJSONDeserializer(codecOptions);

@@ -357,39 +388,2 @@ function interpretChunk(chunk) {

function parseCookieHeader(header) {
const cookies = {};
if (!header) return cookies;
for (const part of header.split(";")) {
const eq = part.indexOf("=");
if (eq < 0) continue;
const name = decodeSafe(part.slice(0, eq).trim());
let value = part.slice(eq + 1).trim();
if (value.length > 1 && value[0] === '"' && value[value.length - 1] === '"') {
value = value.slice(1, -1);
}
cookies[name] = decodeSafe(value);
}
return cookies;
}
function decodeSafe(text) {
try {
return decodeURIComponent(text);
} catch {
return text;
}
}
function serializeCookie(name, value, options = {}) {
let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
cookie += `; Path=${options.path === undefined ? "/" : options.path}`;
if (options.domain) cookie += `; Domain=${options.domain}`;
if (options.maxAge !== undefined) cookie += `; Max-Age=${Math.trunc(options.maxAge)}`;
if (options.expires) cookie += `; Expires=${options.expires.toUTCString()}`;
if (options.httpOnly) cookie += "; HttpOnly";
if (options.secure) cookie += "; Secure";
if (options.sameSite) {
const sameSite = options.sameSite.toLowerCase();
cookie += `; SameSite=${sameSite === "none" ? "None" : sameSite === "strict" ? "Strict" : "Lax"}`;
}
return cookie;
}
const RequestContext = Symbol.for("solid.RequestContext");

@@ -558,3 +552,13 @@ function getRequestEvent() {

const INVOCATIONS = new WeakMap();
let rpcProvided = false;
function provideRPC() {
if (rpcProvided) return;
rpcProvided = true;
provideServerFunctionRPC({
GET,
decodeResponse
});
}
function registerServerFunction(id, callback) {
provideRPC();
REGISTRATIONS.set(id, callback);

@@ -584,2 +588,3 @@ return callback;

if (typeof fn !== "function") throw new Error("Export from a 'use server' module must be a function");
provideRPC();
const metadata = name === undefined ? {} : {

@@ -696,3 +701,5 @@ name

}
return {
return outcome.value === undefined ? {
data
} : {
value: outcome.value,

@@ -829,2 +836,19 @@ data

}
if (value === undefined) {
return new Response(null, {
status,
headers
});
}
try {
if (isJSONSafe(value)) {
headers.set(BODY_FORMAT_HEADER, BodyFormat.Json);
headers.set("Content-Type", "application/json");
return new Response(JSON.stringify(value), {
status,
headers
});
}
} catch {
}
const response = serializedResponse(value, headers, codec);

@@ -831,0 +855,0 @@ return status === 200 ? response : new Response(response.body, {

@@ -292,3 +292,3 @@ import { JSX } from "./jsx.cjs";

*/
export type { RequestEventLocals } from "./server.cjs";
export { RequestEventLocals } from "./server.cjs";
export interface RequestEvent {

@@ -315,3 +315,25 @@ request: Request;

export type { CookieOptions } from "./cookies.cjs";
/**
* The flash cookie's isomorphic half (name/detection/clearing — cookie
* utilities living beside the cookie codec) and the codec-free
* server-function layer (reference detection + the late-bound RPC seam).
* On the core entries so integrations consuming them eagerly (routers)
* never import the server-functions entry — whose client half is the
* transport + codec — from their eager graph. Declared through
* server-functions/shared.d.ts, the declaration home published-types
* layouts ship.
*/
export {
clearFlashCookie,
getServerFunctionMetadata,
getServerFunctionRPC,
hasFlashCookie,
isServerFunction
} from "./server-functions/shared.cjs";
export type {
ServerFunction,
ServerFunctionMetadata,
ServerFunctionRPC
} from "./server-functions/shared.cjs";
/** Hydration-walk primitive; not for hand-written code. @internal */
export function runHydrationEvents(): void;

@@ -1,2 +0,2 @@

export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration, runInServerComponentScope } from "solid-js";
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration, runInServerComponentScope, creationStamp, inServerComponentScope } from "solid-js";
export declare const effect: (fn: any, effectFn: any, options?: any) => void;

@@ -3,0 +3,0 @@ export declare const memo: (fn: any) => import("solid-js").SourceAccessor<any>;

@@ -189,2 +189,6 @@ /**

resolve(ref: { $ref: string }, frameId?: string): unknown;
/** See FrameHostOptions.revive. */
revive?(value: unknown): unknown;
/** See FrameHostOptions.isContainer. */
isContainer?(value: unknown): boolean;
}

@@ -223,2 +227,24 @@

applyData?(chunk: Extract<FrameChunk, { type: "data" }>): void;
/**
* A lazily-loaded deserializer's load, awaited by the transport before it
* delivers a `data` chunk — `applyData`/`resolve` can assume the codec is
* resident once data has arrived. Keeps codec weight out of the eager
* client graph for responses that never carry serialized data.
*/
prepareData?(): Promise<unknown>;
/**
* Revive protocol markers inside LITERAL slot args (values that are
* neither `{$ref}` nor `{$frame}`) at arg-resolution time. Document-face
* container traces ride this way — inline in the record, revived by the
* integration (`reviveContainerTraces`) into live local containers.
*/
revive?(value: unknown): unknown;
/**
* Whether a resolved arg value is a LIVE CONTAINER (a materialized trace —
* see `isMaterializedContainer`). The record-dedupe compare must know: a
* pending container's property reads throw not-ready, so async probes and
* serialization compares would detonate it. Containers compare by
* identity only.
*/
isContainer?(value: unknown): boolean;
}

@@ -225,0 +251,0 @@

@@ -6,3 +6,3 @@ // EXPERIMENTAL — the frames/server-components surface ships as an

import { FrameChunk, FrameHost } from "./frame-client.cjs";
import { JSONCodecOptions } from "./serializer.cjs";
import { JSONCodecOptions } from "./serializer-decode.cjs";

@@ -9,0 +9,0 @@ // Structural mirror of server-functions/shared.js's FlightDataConsumer:

@@ -8,11 +8,16 @@ // Serialization surface (published as `@solidjs/web/serialization`): the

// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";
import type { Serializer } from "seroval";
import {
JSONCodecOptions,
PluginInfo,
SerializerPlugin,
SerovalNode
} from "./serializer-decode.cjs";
/**
* Seroval's node shape — the intermediate representation `serializeJSON`
* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// The decode half — `SerovalNode`, the plugin TYPES, `DEFAULT_WEB_PLUGINS`,
// `resolveSerializerPlugins`, `JSONCodecOptions`, `createJSONDeserializer`,
// `createJSONDataTable` — is declared in serializer-decode.d.ts (published
// as `@solidjs/web/serialization/decode`, the module lazy client consumers
// load) and re-exported here so this remains the full surface.
export * from "./serializer-decode.cjs";

@@ -25,84 +30,6 @@ // ---- Plugin authoring ----

// seroval's own (`createPlugin`, `OpaqueReference` — see serializer.js);
// the TYPES are declared here by hand, like everything else in this file,
// because seroval's published d.ts use extensionless ESM-relative imports
// that `moduleResolution: "nodenext"` cannot follow — a bare type
// re-export would silently degrade the whole authoring surface to `any`
// under skipLibCheck. The declarations mirror seroval ~1.5 exactly; the
// `~` pin is what makes mirroring safe.
// the plugin TYPES live in serializer-decode.d.ts (hand-declared there —
// see its banner for why).
/** Per-plugin bookkeeping seroval hands each plugin callback. */
export interface PluginData {
id: number;
}
/**
* The shape of a plugin's parsed payload: a map of `SerovalNode`s produced
* by the parse contexts, consumed by `serialize`/`deserialize`.
*/
export type PluginInfo = { [key: string]: SerovalNode };
/** Parse context for `parse.sync`: turns child values into nodes. */
export interface SyncParsePluginContext {
parse<T>(current: T): SerovalNode;
}
/** Parse context for `parse.async`: like sync, but child parses await. */
export interface AsyncParsePluginContext {
parse<T>(current: T): Promise<SerovalNode>;
}
/**
* Parse context for `parse.stream`: sync parsing plus the streaming
* lifecycle (pending-state tracking, late node emission, cleanup).
*/
export interface StreamParsePluginContext {
parse<T>(current: T): SerovalNode;
parseWithError<T>(current: T): SerovalNode | undefined;
isAlive(): boolean;
pushPendingState(): void;
popPendingState(): void;
onParse(node: SerovalNode): void;
onError(error: unknown): void;
addCleanup(callback: () => void): void;
}
/** Serialize context: renders child nodes to JS source. */
export interface SerializePluginContext {
serialize(node: SerovalNode): string;
}
/** Deserialize context: revives child nodes to runtime values. */
export interface DeserializePluginContext {
deserialize<T>(node: SerovalNode): T;
}
/**
* A Seroval plugin usable with the web serializers — teaches the codec how
* to encode/decode a custom value type (`Value` is the value it matches,
* `Info` its parsed payload). Supply matching plugins on both peers of a
* transport. Bare `SerializerPlugin` (both parameters defaulted to `any`)
* is the list-element type every `plugins` option accepts.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerializerPlugin<Value = any, Info extends PluginInfo = any> {
/** A unique string identifying the plugin — namespace it (`"app/Thing"`). */
tag: string;
/** Dependency plugins, resolved ahead of this one. */
extends?: SerializerPlugin[];
/** Whether `value` is this plugin's to encode. */
test(value: unknown): boolean;
/** Parsing modes — provide the ones the transports you target use. */
parse: {
sync?: (value: Value, ctx: SyncParsePluginContext, data: PluginData) => Info;
async?: (value: Value, ctx: AsyncParsePluginContext, data: PluginData) => Promise<Info>;
stream?: (value: Value, ctx: StreamParsePluginContext, data: PluginData) => Info;
};
/** Renders the parsed payload as JS source (script-injection form). */
serialize(node: Info, ctx: SerializePluginContext, data: PluginData): string;
/** Revives the parsed payload back into the runtime value. */
deserialize(node: Info, ctx: DeserializePluginContext, data: PluginData): Value;
}
/**
* Builds a `SerializerPlugin` — seroval's `createPlugin`, re-exported so

@@ -136,22 +63,2 @@ * plugin authors stay on the exact seroval instance/version the runtime

/**
* Baseline plugin set for serializing web-platform values (AbortSignal,
* Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).
* Applied by every serializer in this module; custom plugins compose ahead
* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/
export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];
/**
* Composes custom plugins with `DEFAULT_WEB_PLUGINS`. Custom plugins come
* first so they can shadow a default for values both would match. Returns a
* fresh array; the defaults are never mutated. Useful when handing a full
* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/**
* Options for `createSerializer`.

@@ -223,28 +130,6 @@ *

// ---- JSON codec (server function transports) ----
// (`JSONCodecOptions` and the decode half are declared in
// serializer-decode.d.ts and re-exported above.)
/**
* Options shared by both halves of the JSON codec. All of them must match
* on the serializing and deserializing peer or payloads will not
* round-trip — for server functions, set them once through the
* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONCodecOptions {
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. Must match on both peers. */
plugins?: SerializerPlugin[];
/**
* Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
* (payloads may come from an untrusted peer). Must match on both peers.
* Outside development, the encoding side additionally strips
* `Error.prototype.stack` on top of any override — serialized stacks leak
* server paths to the client. Decoding stays permissive, so payloads from
* a development peer still round-trip.
*/
disabledFeatures?: number;
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
depthLimit?: number;
}
/**
* Options for `serializeJSON`.

@@ -277,13 +162,2 @@ *

/**
* Creates the decoding counterpart of `serializeJSON`. Cross-references
* between chunks resolve through state shared across calls, so all chunks
* from one stream must go through the same deserializer instance. The first
* chunk's return value is the decoded source value; feeding later chunks
* settles the async values referenced inside it.
*
* Integration-facing; may change (see the entry banner).
*/
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
/** Options for `createJSONSerializer`. */

@@ -314,17 +188,1 @@ export interface JSONSerializerOptions extends JSONCodecOptions {

};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
* `resolve`. The frames client host wires one per response
* (`applyData: c => table.apply(c)`).
*
* Integration-facing; may change (see the entry banner). This serialization
* entry is the single home of the data table — the frames client consumes
* it internally rather than re-exporting it.
*/
export interface JSONDataTable {
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
resolve<T = unknown>(ref: { $ref: string }): T;
}
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;

@@ -8,11 +8,16 @@ // Serialization surface (published as `@solidjs/web/serialization`): the

// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";
import type { Serializer } from "seroval";
import {
JSONCodecOptions,
PluginInfo,
SerializerPlugin,
SerovalNode
} from "./serializer-decode.cjs";
/**
* Seroval's node shape — the intermediate representation `serializeJSON`
* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// The decode half — `SerovalNode`, the plugin TYPES, `DEFAULT_WEB_PLUGINS`,
// `resolveSerializerPlugins`, `JSONCodecOptions`, `createJSONDeserializer`,
// `createJSONDataTable` — is declared in serializer-decode.d.ts (published
// as `@solidjs/web/serialization/decode`, the module lazy client consumers
// load) and re-exported here so this remains the full surface.
export * from "./serializer-decode.cjs";

@@ -25,84 +30,6 @@ // ---- Plugin authoring ----

// seroval's own (`createPlugin`, `OpaqueReference` — see serializer.js);
// the TYPES are declared here by hand, like everything else in this file,
// because seroval's published d.ts use extensionless ESM-relative imports
// that `moduleResolution: "nodenext"` cannot follow — a bare type
// re-export would silently degrade the whole authoring surface to `any`
// under skipLibCheck. The declarations mirror seroval ~1.5 exactly; the
// `~` pin is what makes mirroring safe.
// the plugin TYPES live in serializer-decode.d.ts (hand-declared there —
// see its banner for why).
/** Per-plugin bookkeeping seroval hands each plugin callback. */
export interface PluginData {
id: number;
}
/**
* The shape of a plugin's parsed payload: a map of `SerovalNode`s produced
* by the parse contexts, consumed by `serialize`/`deserialize`.
*/
export type PluginInfo = { [key: string]: SerovalNode };
/** Parse context for `parse.sync`: turns child values into nodes. */
export interface SyncParsePluginContext {
parse<T>(current: T): SerovalNode;
}
/** Parse context for `parse.async`: like sync, but child parses await. */
export interface AsyncParsePluginContext {
parse<T>(current: T): Promise<SerovalNode>;
}
/**
* Parse context for `parse.stream`: sync parsing plus the streaming
* lifecycle (pending-state tracking, late node emission, cleanup).
*/
export interface StreamParsePluginContext {
parse<T>(current: T): SerovalNode;
parseWithError<T>(current: T): SerovalNode | undefined;
isAlive(): boolean;
pushPendingState(): void;
popPendingState(): void;
onParse(node: SerovalNode): void;
onError(error: unknown): void;
addCleanup(callback: () => void): void;
}
/** Serialize context: renders child nodes to JS source. */
export interface SerializePluginContext {
serialize(node: SerovalNode): string;
}
/** Deserialize context: revives child nodes to runtime values. */
export interface DeserializePluginContext {
deserialize<T>(node: SerovalNode): T;
}
/**
* A Seroval plugin usable with the web serializers — teaches the codec how
* to encode/decode a custom value type (`Value` is the value it matches,
* `Info` its parsed payload). Supply matching plugins on both peers of a
* transport. Bare `SerializerPlugin` (both parameters defaulted to `any`)
* is the list-element type every `plugins` option accepts.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerializerPlugin<Value = any, Info extends PluginInfo = any> {
/** A unique string identifying the plugin — namespace it (`"app/Thing"`). */
tag: string;
/** Dependency plugins, resolved ahead of this one. */
extends?: SerializerPlugin[];
/** Whether `value` is this plugin's to encode. */
test(value: unknown): boolean;
/** Parsing modes — provide the ones the transports you target use. */
parse: {
sync?: (value: Value, ctx: SyncParsePluginContext, data: PluginData) => Info;
async?: (value: Value, ctx: AsyncParsePluginContext, data: PluginData) => Promise<Info>;
stream?: (value: Value, ctx: StreamParsePluginContext, data: PluginData) => Info;
};
/** Renders the parsed payload as JS source (script-injection form). */
serialize(node: Info, ctx: SerializePluginContext, data: PluginData): string;
/** Revives the parsed payload back into the runtime value. */
deserialize(node: Info, ctx: DeserializePluginContext, data: PluginData): Value;
}
/**
* Builds a `SerializerPlugin` — seroval's `createPlugin`, re-exported so

@@ -136,22 +63,2 @@ * plugin authors stay on the exact seroval instance/version the runtime

/**
* Baseline plugin set for serializing web-platform values (AbortSignal,
* Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).
* Applied by every serializer in this module; custom plugins compose ahead
* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/
export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];
/**
* Composes custom plugins with `DEFAULT_WEB_PLUGINS`. Custom plugins come
* first so they can shadow a default for values both would match. Returns a
* fresh array; the defaults are never mutated. Useful when handing a full
* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/**
* Options for `createSerializer`.

@@ -223,28 +130,6 @@ *

// ---- JSON codec (server function transports) ----
// (`JSONCodecOptions` and the decode half are declared in
// serializer-decode.d.ts and re-exported above.)
/**
* Options shared by both halves of the JSON codec. All of them must match
* on the serializing and deserializing peer or payloads will not
* round-trip — for server functions, set them once through the
* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONCodecOptions {
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. Must match on both peers. */
plugins?: SerializerPlugin[];
/**
* Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
* (payloads may come from an untrusted peer). Must match on both peers.
* Outside development, the encoding side additionally strips
* `Error.prototype.stack` on top of any override — serialized stacks leak
* server paths to the client. Decoding stays permissive, so payloads from
* a development peer still round-trip.
*/
disabledFeatures?: number;
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
depthLimit?: number;
}
/**
* Options for `serializeJSON`.

@@ -277,13 +162,2 @@ *

/**
* Creates the decoding counterpart of `serializeJSON`. Cross-references
* between chunks resolve through state shared across calls, so all chunks
* from one stream must go through the same deserializer instance. The first
* chunk's return value is the decoded source value; feeding later chunks
* settles the async values referenced inside it.
*
* Integration-facing; may change (see the entry banner).
*/
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
/** Options for `createJSONSerializer`. */

@@ -314,17 +188,1 @@ export interface JSONSerializerOptions extends JSONCodecOptions {

};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
* `resolve`. The frames client host wires one per response
* (`applyData: c => table.apply(c)`).
*
* Integration-facing; may change (see the entry banner). This serialization
* entry is the single home of the data table — the frames client consumes
* it internally rather than re-exporting it.
*/
export interface JSONDataTable {
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
resolve<T = unknown>(ref: { $ref: string }): T;
}
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;

@@ -1,2 +0,2 @@

import { JSONCodecOptions } from "../serializer.cjs";
import { JSONCodecOptions } from "../serializer-decode.cjs";
import { ServerFunction, ServerFunctionMetadata } from "./shared.cjs";

@@ -3,0 +3,0 @@

import { ResponseEnvelope } from "../response.cjs";
import { JSONCodecOptions } from "../serializer.cjs";
import { JSONCodecOptions } from "../serializer-decode.cjs";
import { RequestEvent } from "../server.cjs";

@@ -4,0 +4,0 @@

@@ -1,2 +0,2 @@

import { JSONCodecOptions } from "../serializer.cjs";
import { JSONCodecOptions } from "../serializer-decode.cjs";

@@ -287,2 +287,42 @@ export type { JSONCodecOptions };

/**
* The transport surface integrations consume through the late-bound RPC
* seam (server-functions/registry.js) — filled by the transport halves when
* the first server function reference is created (code that only exists in
* a bundle when a `"use server"` function was actually compiled in), read
* by routers so they never import the transport/codec statically.
*/
export interface ServerFunctionRPC {
/**
* The build's `GET` declaration wrapper (client fetch transport or
* server in-process dispatch — see the respective entries).
*/
GET<A extends readonly any[], R>(fn: (...args: A) => R): ServerFunction<A, Awaited<R>>;
/**
* `decodeResponse` bound to the configured codec: decodes a server
* function response body the transport handed over whole (redirects,
* revalidation). Resolves undefined for empty bodies and bodies without
* a recognized encoding (e.g. a raw user Response).
*/
decodeResponse<T = unknown>(response: Response): Promise<T | undefined>;
}
/**
* Fills the RPC seam. Called by the transport halves when the first server
* function reference is created; first write wins.
* @internal
*/
export function provideServerFunctionRPC(rpc: ServerFunctionRPC): void;
/**
* The registered RPC surface, or undefined when no server function exists
* in this build's graph. Integration plumbing (routers): gate every use of
* the transport/codec behind this read instead of importing it — an app
* with no server functions then ships none of it, while a reference in the
* bundle guarantees the seam is filled before integration code can hold
* that reference (compiled output creates references at module scope).
* @internal
*/
export function getServerFunctionRPC(): ServerFunctionRPC | undefined;
/**
* Header carrying the body format tag (a `BodyFormat` value) —

@@ -320,2 +360,7 @@ * `"X-Server-Function-Format"`.

readonly Uint8Array: "7";
/**
* Plain `JSON.stringify` — the fast path for JSON-safe payloads on both
* legs: argument lists on the request, results on the response.
*/
readonly Json: "8";
};

@@ -330,2 +375,13 @@

/**
* Whether a value survives a `JSON.stringify` round trip faithfully: JSON
* primitives (finite numbers only), arrays, and plain objects. Anything
* else — Dates, Maps, typed arrays, undefined (bare or as a property),
* NaN, class instances, cyclic structures — needs the codec. Never throws:
* cycles and pathological depth answer `false`. Both peers negotiate the
* wire format with this guard: the client for argument lists, the server
* for results.
*/
export function isJSONSafe(value: unknown): boolean;
/**
* Picks a direct HTTP encoding (headers + BodyInit) for values that have

@@ -332,0 +388,0 @@ * one — strings, FormData, URLSearchParams, File, Blob, ArrayBuffer,

import { JSX } from "./jsx.cjs";
import { SerializerPlugin } from "./serializer.cjs";
import { SerializerPlugin } from "./serializer-decode.cjs";
export const DOMWithState: Record<string, Record<string, 1 | 2>>;

@@ -348,2 +348,22 @@ export const ChildProperties: Set<string>;

/**
* The flash cookie's isomorphic half and the codec-free server-function
* layer (reference detection + the late-bound RPC seam) — mirrors of the
* client entry's exports, so integration code reading them stays
* universal. Declared through server-functions/shared.d.ts, the
* declaration home published-types layouts ship.
*/
export {
clearFlashCookie,
getServerFunctionMetadata,
getServerFunctionRPC,
hasFlashCookie,
isServerFunction
} from "./server-functions/shared.cjs";
export type {
ServerFunction,
ServerFunctionMetadata,
ServerFunctionRPC
} from "./server-functions/shared.cjs";
export interface SSRResponseOptions {

@@ -350,0 +370,0 @@ /** Base head; the stub's status/headers win over it. */

@@ -292,3 +292,3 @@ import { JSX } from "./jsx.js";

*/
export type { RequestEventLocals } from "./server.js";
export { RequestEventLocals } from "./server.js";
export interface RequestEvent {

@@ -315,3 +315,25 @@ request: Request;

export type { CookieOptions } from "./cookies.js";
/**
* The flash cookie's isomorphic half (name/detection/clearing — cookie
* utilities living beside the cookie codec) and the codec-free
* server-function layer (reference detection + the late-bound RPC seam).
* On the core entries so integrations consuming them eagerly (routers)
* never import the server-functions entry — whose client half is the
* transport + codec — from their eager graph. Declared through
* server-functions/shared.d.ts, the declaration home published-types
* layouts ship.
*/
export {
clearFlashCookie,
getServerFunctionMetadata,
getServerFunctionRPC,
hasFlashCookie,
isServerFunction
} from "./server-functions/shared.js";
export type {
ServerFunction,
ServerFunctionMetadata,
ServerFunctionRPC
} from "./server-functions/shared.js";
/** Hydration-walk primitive; not for hand-written code. @internal */
export function runHydrationEvents(): void;

@@ -1,2 +0,2 @@

export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration, runInServerComponentScope } from "solid-js";
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration, runInServerComponentScope, creationStamp, inServerComponentScope } from "solid-js";
export declare const effect: (fn: any, effectFn: any, options?: any) => void;

@@ -3,0 +3,0 @@ export declare const memo: (fn: any) => import("solid-js").SourceAccessor<any>;

@@ -189,2 +189,6 @@ /**

resolve(ref: { $ref: string }, frameId?: string): unknown;
/** See FrameHostOptions.revive. */
revive?(value: unknown): unknown;
/** See FrameHostOptions.isContainer. */
isContainer?(value: unknown): boolean;
}

@@ -223,2 +227,24 @@

applyData?(chunk: Extract<FrameChunk, { type: "data" }>): void;
/**
* A lazily-loaded deserializer's load, awaited by the transport before it
* delivers a `data` chunk — `applyData`/`resolve` can assume the codec is
* resident once data has arrived. Keeps codec weight out of the eager
* client graph for responses that never carry serialized data.
*/
prepareData?(): Promise<unknown>;
/**
* Revive protocol markers inside LITERAL slot args (values that are
* neither `{$ref}` nor `{$frame}`) at arg-resolution time. Document-face
* container traces ride this way — inline in the record, revived by the
* integration (`reviveContainerTraces`) into live local containers.
*/
revive?(value: unknown): unknown;
/**
* Whether a resolved arg value is a LIVE CONTAINER (a materialized trace —
* see `isMaterializedContainer`). The record-dedupe compare must know: a
* pending container's property reads throw not-ready, so async probes and
* serialization compares would detonate it. Containers compare by
* identity only.
*/
isContainer?(value: unknown): boolean;
}

@@ -225,0 +251,0 @@

@@ -6,3 +6,3 @@ // EXPERIMENTAL — the frames/server-components surface ships as an

import { FrameChunk, FrameHost } from "./frame-client.js";
import { JSONCodecOptions } from "./serializer.js";
import { JSONCodecOptions } from "./serializer-decode.js";

@@ -9,0 +9,0 @@ // Structural mirror of server-functions/shared.js's FlightDataConsumer:

@@ -8,11 +8,16 @@ // Serialization surface (published as `@solidjs/web/serialization`): the

// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";
import type { Serializer } from "seroval";
import {
JSONCodecOptions,
PluginInfo,
SerializerPlugin,
SerovalNode
} from "./serializer-decode.js";
/**
* Seroval's node shape — the intermediate representation `serializeJSON`
* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// The decode half — `SerovalNode`, the plugin TYPES, `DEFAULT_WEB_PLUGINS`,
// `resolveSerializerPlugins`, `JSONCodecOptions`, `createJSONDeserializer`,
// `createJSONDataTable` — is declared in serializer-decode.d.ts (published
// as `@solidjs/web/serialization/decode`, the module lazy client consumers
// load) and re-exported here so this remains the full surface.
export * from "./serializer-decode.js";

@@ -25,84 +30,6 @@ // ---- Plugin authoring ----

// seroval's own (`createPlugin`, `OpaqueReference` — see serializer.js);
// the TYPES are declared here by hand, like everything else in this file,
// because seroval's published d.ts use extensionless ESM-relative imports
// that `moduleResolution: "nodenext"` cannot follow — a bare type
// re-export would silently degrade the whole authoring surface to `any`
// under skipLibCheck. The declarations mirror seroval ~1.5 exactly; the
// `~` pin is what makes mirroring safe.
// the plugin TYPES live in serializer-decode.d.ts (hand-declared there —
// see its banner for why).
/** Per-plugin bookkeeping seroval hands each plugin callback. */
export interface PluginData {
id: number;
}
/**
* The shape of a plugin's parsed payload: a map of `SerovalNode`s produced
* by the parse contexts, consumed by `serialize`/`deserialize`.
*/
export type PluginInfo = { [key: string]: SerovalNode };
/** Parse context for `parse.sync`: turns child values into nodes. */
export interface SyncParsePluginContext {
parse<T>(current: T): SerovalNode;
}
/** Parse context for `parse.async`: like sync, but child parses await. */
export interface AsyncParsePluginContext {
parse<T>(current: T): Promise<SerovalNode>;
}
/**
* Parse context for `parse.stream`: sync parsing plus the streaming
* lifecycle (pending-state tracking, late node emission, cleanup).
*/
export interface StreamParsePluginContext {
parse<T>(current: T): SerovalNode;
parseWithError<T>(current: T): SerovalNode | undefined;
isAlive(): boolean;
pushPendingState(): void;
popPendingState(): void;
onParse(node: SerovalNode): void;
onError(error: unknown): void;
addCleanup(callback: () => void): void;
}
/** Serialize context: renders child nodes to JS source. */
export interface SerializePluginContext {
serialize(node: SerovalNode): string;
}
/** Deserialize context: revives child nodes to runtime values. */
export interface DeserializePluginContext {
deserialize<T>(node: SerovalNode): T;
}
/**
* A Seroval plugin usable with the web serializers — teaches the codec how
* to encode/decode a custom value type (`Value` is the value it matches,
* `Info` its parsed payload). Supply matching plugins on both peers of a
* transport. Bare `SerializerPlugin` (both parameters defaulted to `any`)
* is the list-element type every `plugins` option accepts.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerializerPlugin<Value = any, Info extends PluginInfo = any> {
/** A unique string identifying the plugin — namespace it (`"app/Thing"`). */
tag: string;
/** Dependency plugins, resolved ahead of this one. */
extends?: SerializerPlugin[];
/** Whether `value` is this plugin's to encode. */
test(value: unknown): boolean;
/** Parsing modes — provide the ones the transports you target use. */
parse: {
sync?: (value: Value, ctx: SyncParsePluginContext, data: PluginData) => Info;
async?: (value: Value, ctx: AsyncParsePluginContext, data: PluginData) => Promise<Info>;
stream?: (value: Value, ctx: StreamParsePluginContext, data: PluginData) => Info;
};
/** Renders the parsed payload as JS source (script-injection form). */
serialize(node: Info, ctx: SerializePluginContext, data: PluginData): string;
/** Revives the parsed payload back into the runtime value. */
deserialize(node: Info, ctx: DeserializePluginContext, data: PluginData): Value;
}
/**
* Builds a `SerializerPlugin` — seroval's `createPlugin`, re-exported so

@@ -136,22 +63,2 @@ * plugin authors stay on the exact seroval instance/version the runtime

/**
* Baseline plugin set for serializing web-platform values (AbortSignal,
* Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).
* Applied by every serializer in this module; custom plugins compose ahead
* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/
export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];
/**
* Composes custom plugins with `DEFAULT_WEB_PLUGINS`. Custom plugins come
* first so they can shadow a default for values both would match. Returns a
* fresh array; the defaults are never mutated. Useful when handing a full
* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/**
* Options for `createSerializer`.

@@ -223,28 +130,6 @@ *

// ---- JSON codec (server function transports) ----
// (`JSONCodecOptions` and the decode half are declared in
// serializer-decode.d.ts and re-exported above.)
/**
* Options shared by both halves of the JSON codec. All of them must match
* on the serializing and deserializing peer or payloads will not
* round-trip — for server functions, set them once through the
* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONCodecOptions {
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. Must match on both peers. */
plugins?: SerializerPlugin[];
/**
* Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
* (payloads may come from an untrusted peer). Must match on both peers.
* Outside development, the encoding side additionally strips
* `Error.prototype.stack` on top of any override — serialized stacks leak
* server paths to the client. Decoding stays permissive, so payloads from
* a development peer still round-trip.
*/
disabledFeatures?: number;
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
depthLimit?: number;
}
/**
* Options for `serializeJSON`.

@@ -277,13 +162,2 @@ *

/**
* Creates the decoding counterpart of `serializeJSON`. Cross-references
* between chunks resolve through state shared across calls, so all chunks
* from one stream must go through the same deserializer instance. The first
* chunk's return value is the decoded source value; feeding later chunks
* settles the async values referenced inside it.
*
* Integration-facing; may change (see the entry banner).
*/
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
/** Options for `createJSONSerializer`. */

@@ -314,17 +188,1 @@ export interface JSONSerializerOptions extends JSONCodecOptions {

};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
* `resolve`. The frames client host wires one per response
* (`applyData: c => table.apply(c)`).
*
* Integration-facing; may change (see the entry banner). This serialization
* entry is the single home of the data table — the frames client consumes
* it internally rather than re-exporting it.
*/
export interface JSONDataTable {
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
resolve<T = unknown>(ref: { $ref: string }): T;
}
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;

@@ -8,11 +8,16 @@ // Serialization surface (published as `@solidjs/web/serialization`): the

// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";
import type { Serializer } from "seroval";
import {
JSONCodecOptions,
PluginInfo,
SerializerPlugin,
SerovalNode
} from "./serializer-decode.js";
/**
* Seroval's node shape — the intermediate representation `serializeJSON`
* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// The decode half — `SerovalNode`, the plugin TYPES, `DEFAULT_WEB_PLUGINS`,
// `resolveSerializerPlugins`, `JSONCodecOptions`, `createJSONDeserializer`,
// `createJSONDataTable` — is declared in serializer-decode.d.ts (published
// as `@solidjs/web/serialization/decode`, the module lazy client consumers
// load) and re-exported here so this remains the full surface.
export * from "./serializer-decode.js";

@@ -25,84 +30,6 @@ // ---- Plugin authoring ----

// seroval's own (`createPlugin`, `OpaqueReference` — see serializer.js);
// the TYPES are declared here by hand, like everything else in this file,
// because seroval's published d.ts use extensionless ESM-relative imports
// that `moduleResolution: "nodenext"` cannot follow — a bare type
// re-export would silently degrade the whole authoring surface to `any`
// under skipLibCheck. The declarations mirror seroval ~1.5 exactly; the
// `~` pin is what makes mirroring safe.
// the plugin TYPES live in serializer-decode.d.ts (hand-declared there —
// see its banner for why).
/** Per-plugin bookkeeping seroval hands each plugin callback. */
export interface PluginData {
id: number;
}
/**
* The shape of a plugin's parsed payload: a map of `SerovalNode`s produced
* by the parse contexts, consumed by `serialize`/`deserialize`.
*/
export type PluginInfo = { [key: string]: SerovalNode };
/** Parse context for `parse.sync`: turns child values into nodes. */
export interface SyncParsePluginContext {
parse<T>(current: T): SerovalNode;
}
/** Parse context for `parse.async`: like sync, but child parses await. */
export interface AsyncParsePluginContext {
parse<T>(current: T): Promise<SerovalNode>;
}
/**
* Parse context for `parse.stream`: sync parsing plus the streaming
* lifecycle (pending-state tracking, late node emission, cleanup).
*/
export interface StreamParsePluginContext {
parse<T>(current: T): SerovalNode;
parseWithError<T>(current: T): SerovalNode | undefined;
isAlive(): boolean;
pushPendingState(): void;
popPendingState(): void;
onParse(node: SerovalNode): void;
onError(error: unknown): void;
addCleanup(callback: () => void): void;
}
/** Serialize context: renders child nodes to JS source. */
export interface SerializePluginContext {
serialize(node: SerovalNode): string;
}
/** Deserialize context: revives child nodes to runtime values. */
export interface DeserializePluginContext {
deserialize<T>(node: SerovalNode): T;
}
/**
* A Seroval plugin usable with the web serializers — teaches the codec how
* to encode/decode a custom value type (`Value` is the value it matches,
* `Info` its parsed payload). Supply matching plugins on both peers of a
* transport. Bare `SerializerPlugin` (both parameters defaulted to `any`)
* is the list-element type every `plugins` option accepts.
*
* Integration-facing; may change (see the entry banner).
*/
export interface SerializerPlugin<Value = any, Info extends PluginInfo = any> {
/** A unique string identifying the plugin — namespace it (`"app/Thing"`). */
tag: string;
/** Dependency plugins, resolved ahead of this one. */
extends?: SerializerPlugin[];
/** Whether `value` is this plugin's to encode. */
test(value: unknown): boolean;
/** Parsing modes — provide the ones the transports you target use. */
parse: {
sync?: (value: Value, ctx: SyncParsePluginContext, data: PluginData) => Info;
async?: (value: Value, ctx: AsyncParsePluginContext, data: PluginData) => Promise<Info>;
stream?: (value: Value, ctx: StreamParsePluginContext, data: PluginData) => Info;
};
/** Renders the parsed payload as JS source (script-injection form). */
serialize(node: Info, ctx: SerializePluginContext, data: PluginData): string;
/** Revives the parsed payload back into the runtime value. */
deserialize(node: Info, ctx: DeserializePluginContext, data: PluginData): Value;
}
/**
* Builds a `SerializerPlugin` — seroval's `createPlugin`, re-exported so

@@ -136,22 +63,2 @@ * plugin authors stay on the exact seroval instance/version the runtime

/**
* Baseline plugin set for serializing web-platform values (AbortSignal,
* Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).
* Applied by every serializer in this module; custom plugins compose ahead
* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/
export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];
/**
* Composes custom plugins with `DEFAULT_WEB_PLUGINS`. Custom plugins come
* first so they can shadow a default for values both would match. Returns a
* fresh array; the defaults are never mutated. Useful when handing a full
* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/**
* Options for `createSerializer`.

@@ -223,28 +130,6 @@ *

// ---- JSON codec (server function transports) ----
// (`JSONCodecOptions` and the decode half are declared in
// serializer-decode.d.ts and re-exported above.)
/**
* Options shared by both halves of the JSON codec. All of them must match
* on the serializing and deserializing peer or payloads will not
* round-trip — for server functions, set them once through the
* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONCodecOptions {
/** Extra plugins, composed ahead of `DEFAULT_WEB_PLUGINS`. Must match on both peers. */
plugins?: SerializerPlugin[];
/**
* Seroval feature bitflags to exclude. Defaults to disabling `RegExp`
* (payloads may come from an untrusted peer). Must match on both peers.
* Outside development, the encoding side additionally strips
* `Error.prototype.stack` on top of any override — serialized stacks leak
* server paths to the client. Decoding stays permissive, so payloads from
* a development peer still round-trip.
*/
disabledFeatures?: number;
/** Maximum parse/deserialize depth. Defaults to 64. Must match on both peers. */
depthLimit?: number;
}
/**
* Options for `serializeJSON`.

@@ -277,13 +162,2 @@ *

/**
* Creates the decoding counterpart of `serializeJSON`. Cross-references
* between chunks resolve through state shared across calls, so all chunks
* from one stream must go through the same deserializer instance. The first
* chunk's return value is the decoded source value; feeding later chunks
* settles the async values referenced inside it.
*
* Integration-facing; may change (see the entry banner).
*/
export function createJSONDeserializer(options?: JSONCodecOptions): <T>(node: SerovalNode) => T;
/** Options for `createJSONSerializer`. */

@@ -314,17 +188,1 @@ export interface JSONSerializerOptions extends JSONCodecOptions {

};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply
* each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with
* `resolve`. The frames client host wires one per response
* (`applyData: c => table.apply(c)`).
*
* Integration-facing; may change (see the entry banner). This serialization
* entry is the single home of the data table — the frames client consumes
* it internally rather than re-exporting it.
*/
export interface JSONDataTable {
apply(chunk: { key?: string; node?: unknown; initial?: boolean }): void;
resolve<T = unknown>(ref: { $ref: string }): T;
}
export function createJSONDataTable(options?: JSONCodecOptions): JSONDataTable;

@@ -1,2 +0,2 @@

import { JSONCodecOptions } from "../serializer.js";
import { JSONCodecOptions } from "../serializer-decode.js";
import { ServerFunction, ServerFunctionMetadata } from "./shared.js";

@@ -3,0 +3,0 @@

import { ResponseEnvelope } from "../response.js";
import { JSONCodecOptions } from "../serializer.js";
import { JSONCodecOptions } from "../serializer-decode.js";
import { RequestEvent } from "../server.js";

@@ -4,0 +4,0 @@

@@ -1,2 +0,2 @@

import { JSONCodecOptions } from "../serializer.js";
import { JSONCodecOptions } from "../serializer-decode.js";

@@ -287,2 +287,42 @@ export type { JSONCodecOptions };

/**
* The transport surface integrations consume through the late-bound RPC
* seam (server-functions/registry.js) — filled by the transport halves when
* the first server function reference is created (code that only exists in
* a bundle when a `"use server"` function was actually compiled in), read
* by routers so they never import the transport/codec statically.
*/
export interface ServerFunctionRPC {
/**
* The build's `GET` declaration wrapper (client fetch transport or
* server in-process dispatch — see the respective entries).
*/
GET<A extends readonly any[], R>(fn: (...args: A) => R): ServerFunction<A, Awaited<R>>;
/**
* `decodeResponse` bound to the configured codec: decodes a server
* function response body the transport handed over whole (redirects,
* revalidation). Resolves undefined for empty bodies and bodies without
* a recognized encoding (e.g. a raw user Response).
*/
decodeResponse<T = unknown>(response: Response): Promise<T | undefined>;
}
/**
* Fills the RPC seam. Called by the transport halves when the first server
* function reference is created; first write wins.
* @internal
*/
export function provideServerFunctionRPC(rpc: ServerFunctionRPC): void;
/**
* The registered RPC surface, or undefined when no server function exists
* in this build's graph. Integration plumbing (routers): gate every use of
* the transport/codec behind this read instead of importing it — an app
* with no server functions then ships none of it, while a reference in the
* bundle guarantees the seam is filled before integration code can hold
* that reference (compiled output creates references at module scope).
* @internal
*/
export function getServerFunctionRPC(): ServerFunctionRPC | undefined;
/**
* Header carrying the body format tag (a `BodyFormat` value) —

@@ -320,2 +360,7 @@ * `"X-Server-Function-Format"`.

readonly Uint8Array: "7";
/**
* Plain `JSON.stringify` — the fast path for JSON-safe payloads on both
* legs: argument lists on the request, results on the response.
*/
readonly Json: "8";
};

@@ -330,2 +375,13 @@

/**
* Whether a value survives a `JSON.stringify` round trip faithfully: JSON
* primitives (finite numbers only), arrays, and plain objects. Anything
* else — Dates, Maps, typed arrays, undefined (bare or as a property),
* NaN, class instances, cyclic structures — needs the codec. Never throws:
* cycles and pathological depth answer `false`. Both peers negotiate the
* wire format with this guard: the client for argument lists, the server
* for results.
*/
export function isJSONSafe(value: unknown): boolean;
/**
* Picks a direct HTTP encoding (headers + BodyInit) for values that have

@@ -332,0 +388,0 @@ * one — strings, FormData, URLSearchParams, File, Blob, ArrayBuffer,

import { JSX } from "./jsx.js";
import { SerializerPlugin } from "./serializer.js";
import { SerializerPlugin } from "./serializer-decode.js";
export const DOMWithState: Record<string, Record<string, 1 | 2>>;

@@ -348,2 +348,22 @@ export const ChildProperties: Set<string>;

/**
* The flash cookie's isomorphic half and the codec-free server-function
* layer (reference detection + the late-bound RPC seam) — mirrors of the
* client entry's exports, so integration code reading them stays
* universal. Declared through server-functions/shared.d.ts, the
* declaration home published-types layouts ship.
*/
export {
clearFlashCookie,
getServerFunctionMetadata,
getServerFunctionRPC,
hasFlashCookie,
isServerFunction
} from "./server-functions/shared.js";
export type {
ServerFunction,
ServerFunctionMetadata,
ServerFunctionRPC
} from "./server-functions/shared.js";
export interface SSRResponseOptions {

@@ -350,0 +370,0 @@ /** Base head; the stub's status/headers win over it. */

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display