Sign In

@solidjs/web

Package Overview
Dependencies
Maintainers
2
Versions
53
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.31
to
2.0.0-beta.32
+11
server-functions/dist/rich-args.cjs
'use strict';
var client = require('@solidjs/web/server-functions/client');
function enableRichArguments() {
client.configureServerFunctionsClient({
serializeArgs: args => client.serializeString(args, client.getServerFunctionsCodec())
});
}
exports.enableRichArguments = enableRichArguments;
import { configureServerFunctionsClient, serializeString, getServerFunctionsCodec } from '@solidjs/web/server-functions/client';
function enableRichArguments() {
configureServerFunctionsClient({
serializeArgs: args => serializeString(args, getServerFunctionsCodec())
});
}
export { enableRichArguments };
'use strict';
var seroval = require('seroval');
var web = require('seroval-plugins/web');
var solidJs = require('solid-js');
const ENVELOPE = Symbol.for("solid.ResponseEnvelope");
function isResponseEnvelope(value) {
return !!(value && typeof value === "object" && value[ENVELOPE]);
}
const SAFE_ERROR = Symbol.for("solid.SafeError");
function isSafeError(value) {
return !!(value && (typeof value === "object" || typeof value === "function") && value[SAFE_ERROR]);
}
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 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, {
onParse,
onDone,
onError,
...codecOptions
}) {
const resolved = resolveCodecOptions(codecOptions);
return seroval.toCrossJSONStream(value, {
onParse,
onDone,
onError,
...resolved,
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
});
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return seroval.fromCrossJSON(node, {
refs,
...resolved
});
};
}
const codecConfig = {
codec: undefined
};
function configureServerFunctionsCodec(codec) {
codecConfig.codec = codec;
}
function getServerFunctionsCodec() {
return codecConfig.codec;
}
function subscribeFlightData(consumer) {
return () => {
};
}
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";
const ERROR_HEADER = "X-Server-Function-Error";
const ERROR_HEADER_MARKER = "=?1?";
const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
function encodeErrorHeaderValue(value) {
let stripped = String(value).replace(/[\r\n]+/g, "");
if (!NEEDS_ENCODING.test(stripped) && !stripped.startsWith(ERROR_HEADER_MARKER) && stripped === stripped.trim()) {
return stripped;
}
if (typeof stripped.toWellFormed === "function") {
stripped = stripped.toWellFormed();
} else {
stripped = stripped.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
}
return ERROR_HEADER_MARKER + encodeURIComponent(stripped);
}
function decodeErrorHeaderValue(value) {
if (typeof value !== "string" || !value.startsWith(ERROR_HEADER_MARKER)) {
return value;
}
try {
return decodeURIComponent(value.slice(ERROR_HEADER_MARKER.length));
} catch {
return value;
}
}
const INSTANCE_HEADER = "X-Server-Function-Instance";
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
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 = {
Serialized: "0",
String: "1",
FormData: "2",
URLSearchParams: "3",
Blob: "4",
File: "5",
ArrayBuffer: "6",
Uint8Array: "7",
Json: "8"
};
function getHeadersAndBody(body) {
switch (true) {
case typeof body === "string":
return {
headers: {
"Content-Type": "text/plain",
[BODY_FORMAT_HEADER]: BodyFormat.String
},
body
};
case body instanceof FormData:
return {
headers: {
[BODY_FORMAT_HEADER]: BodyFormat.FormData
},
body
};
case body instanceof URLSearchParams:
return {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
[BODY_FORMAT_HEADER]: BodyFormat.URLSearchParams
},
body
};
case typeof File !== "undefined" && body instanceof File:
{
const formData = new FormData();
formData.append(FILE_FORM_KEY, body, body.name);
return {
headers: {
[BODY_FORMAT_HEADER]: BodyFormat.File
},
body: formData
};
}
case body instanceof Blob:
return {
headers: {
[BODY_FORMAT_HEADER]: BodyFormat.Blob
},
body
};
case body instanceof ArrayBuffer:
return {
headers: {
[BODY_FORMAT_HEADER]: BodyFormat.ArrayBuffer
},
body
};
case body instanceof Uint8Array:
return {
headers: {
[BODY_FORMAT_HEADER]: BodyFormat.Uint8Array
},
body: new Uint8Array(body)
};
default:
return undefined;
}
}
async function extractBody(source, codecOptions) {
const contentType = source.headers.get("content-type");
const format = source.headers.get(BODY_FORMAT_HEADER);
const clone = source.clone();
switch (true) {
case format === BodyFormat.Serialized:
return await deserializeStream(clone, codecOptions);
case format === BodyFormat.Json:
return JSON.parse(await clone.text());
case format === BodyFormat.String:
return await clone.text();
case format === BodyFormat.File:
{
const formData = await clone.formData();
return formData.get(FILE_FORM_KEY);
}
case format === BodyFormat.FormData:
case contentType && contentType.startsWith("multipart/form-data"):
return await clone.formData();
case format === BodyFormat.URLSearchParams:
case contentType && contentType.startsWith("application/x-www-form-urlencoded"):
return new URLSearchParams(await clone.text());
case format === BodyFormat.Blob:
return await clone.blob();
case format === BodyFormat.ArrayBuffer:
return await clone.arrayBuffer();
case format === BodyFormat.Uint8Array:
return new Uint8Array(await clone.arrayBuffer());
}
return undefined;
}
function createChunk(data) {
const encoder = new TextEncoder();
const encodeData = encoder.encode(data);
const bytes = encodeData.length;
const chunk = new Uint8Array(12 + bytes);
chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
chunk.set(encodeData, 12);
return chunk;
}
class ChunkReader {
constructor(stream) {
this.reader = stream.getReader();
this.buffer = new Uint8Array(0);
this.done = false;
}
async readChunk() {
const chunk = await this.reader.read();
if (!chunk.done) {
const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
newBuffer.set(this.buffer);
newBuffer.set(chunk.value, this.buffer.length);
this.buffer = newBuffer;
} else {
this.done = true;
}
}
async next() {
while (this.buffer.length < 12) {
if (this.done) {
if (this.buffer.length === 0) return {
done: true,
value: undefined
};
throw new Error("Malformed server function stream.");
}
await this.readChunk();
}
const decoder = new TextDecoder();
const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
if (Number.isNaN(bytes)) {
throw new Error("Malformed server function stream.");
}
while (bytes > this.buffer.length - 12) {
if (this.done) {
throw new Error("Malformed server function stream.");
}
await this.readChunk();
}
const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
this.buffer = this.buffer.subarray(12 + bytes);
return {
done: false,
value: partial
};
}
async drain(interpret) {
while (true) {
const result = await this.next();
if (result.done) {
break;
}
interpret(result.value);
}
}
}
function serializeStream(value, codecOptions) {
return new ReadableStream({
start(controller) {
serializeJSON(value, {
...codecOptions,
onParse(node) {
controller.enqueue(createChunk(JSON.stringify(node)));
},
onDone() {
controller.close();
},
onError(error) {
controller.error(error);
}
});
}
});
}
async function deserializeStream(source, codecOptions) {
if (!source.body) {
throw new Error("missing body");
}
const reader = new ChunkReader(source.body);
const result = await reader.next();
if (!result.done) {
const deserializeChunk = createJSONDeserializer(codecOptions);
function interpretChunk(chunk) {
return deserializeChunk(JSON.parse(chunk));
}
void reader.drain(interpretChunk);
return interpretChunk(result.value);
}
return undefined;
}
async function deserializeString(text, codecOptions) {
return await deserializeStream(new Response(text), codecOptions);
}
async function decodeResponse(response, codecOptions) {
if (!response.body) return undefined;
return await extractBody(response, codecOptions === undefined ? codecConfig.codec : codecOptions);
}
async function decodeResponsePayload(response, codecOptions) {
const decoded = await decodeResponse(response, codecOptions);
if (decoded !== undefined && response.headers.has(SINGLE_FLIGHT_HEADER)) {
return {
value: decoded.value,
flightData: decoded.data
};
}
return {
value: decoded
};
}
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");
function getRequestEvent() {
return globalThis[RequestContext] ? globalThis[RequestContext].getStore() || solidJs.sharedConfig.context && solidJs.sharedConfig.context.event || console.warn("RequestEvent is missing. This is most likely due to accessing `getRequestEvent` non-managed async scope in a partially polyfilled environment. Try moving it above all `await` calls.") : undefined;
}
function reportLostHeaderWrite(method, name) {
const message = `Response header write dropped: headers.${method}(${JSON.stringify(String(name))}) ` + "ran after the response head was sent. Write headers before the shell flushes " + "(or before the handler returns).";
throw new Error(message);
}
function commitResponseStub(stub, {
allowLateLocation = false
} = {}) {
if (!stub || stub.committed) return stub;
stub.committed = true;
const headers = stub.headers;
if (!headers || typeof headers.set !== "function") return stub;
for (const method of ["set", "append", "delete"]) {
const original = headers[method].bind(headers);
headers[method] = function (name, ...rest) {
if (allowLateLocation && method === "set" && String(name).toLowerCase() === "location") {
return original(name, ...rest);
}
reportLostHeaderWrite(method, name);
};
}
return stub;
}
function copyInitHeaders(init) {
if (!init || !init.getSetCookie) return new Headers(init);
const headers = new Headers();
init.forEach((value, key) => {
if (key !== "set-cookie") headers.append(key, value);
});
for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
return headers;
}
const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, "Location"].map(header => header.toLowerCase()));
function fillsStubGap(key, headers, response) {
if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
if (response.body === null && (key === "content-type" || key === "content-length")) return false;
return !headers.has(key);
}
function commitEventResponse(response, event = getRequestEvent()) {
const stub = event && event.response;
if (!stub || !stub.headers || stub.committed) return response;
const cookies = stub.headers.getSetCookie ? stub.headers.getSetCookie() : [];
commitResponseStub(stub);
let hasGaps = false;
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, response.headers, response)) hasGaps = true;
});
if (!cookies.length && !hasGaps) return response;
try {
for (const cookie of cookies) response.headers.append("Set-Cookie", cookie);
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, response.headers, response)) response.headers.set(key, value);
});
return response;
} catch {
const headers = copyInitHeaders(response.headers);
for (const cookie of cookies) headers.append("Set-Cookie", cookie);
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, headers, response)) headers.set(key, value);
});
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers
});
}
}
function encodeInputValue(value) {
if (value instanceof FormData) return {
$f: [...value.entries()].filter(([, v]) => typeof v === "string")
};
if (value instanceof URLSearchParams) return {
$u: [...value.entries()]
};
return value;
}
function decodeInputValue(value) {
if (value && typeof value === "object") {
if (Array.isArray(value.$f)) {
const form = new FormData();
for (const [k, v] of value.$f) form.append(k, v);
return form;
}
if (Array.isArray(value.$u)) return new URLSearchParams(value.$u);
}
return value;
}
function encodeFlashCookie(url, result, input, thrown) {
const isError = result instanceof Error;
const payload = {
url,
result: isError ? result.message : result,
error: isError,
thrown: !!thrown,
input: input.map(encodeInputValue)
};
return serializeCookie(FLASH_COOKIE, JSON.stringify(payload), {
secure: true,
httpOnly: true
});
}
function decodeFlashCookie(cookieHeader) {
const match = parseCookieHeader(cookieHeader)[FLASH_COOKIE];
if (!match) return;
try {
const payload = JSON.parse(match);
if (!payload || !payload.result) return;
const result = payload.error ? new Error(payload.result) : payload.result;
return {
input: Array.isArray(payload.input) ? payload.input.map(decodeInputValue) : [],
url: payload.url,
result: payload.thrown ? undefined : result,
error: payload.thrown ? result : undefined
};
} catch (error) {
console.error(error);
}
}
const config = {
provideEvent: undefined,
wrapInvocation: undefined,
collectFlightData: undefined,
transformResult: undefined,
transformFlightResult: undefined,
transformDirectResult: undefined,
handleNoJS: undefined,
endpoint: "/_server"
};
function configureServerFunctionsServer({
provideEvent,
wrapInvocation,
collectFlightData,
transformResult,
transformFlightResult,
transformDirectResult,
handleNoJS,
endpoint,
codec
} = {}) {
if (provideEvent !== undefined) config.provideEvent = provideEvent;
if (wrapInvocation !== undefined) config.wrapInvocation = wrapInvocation;
if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
if (transformResult !== undefined) config.transformResult = transformResult;
if (transformFlightResult !== undefined) config.transformFlightResult = transformFlightResult;
if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
if (handleNoJS !== undefined) config.handleNoJS = handleNoJS;
if (endpoint !== undefined) config.endpoint = endpoint;
if (codec !== undefined) configureServerFunctionsCodec(codec);
}
function provideEvent(event, fn) {
if (config.provideEvent) return config.provideEvent(event, fn);
const ctx = globalThis[RequestContext];
if (ctx) return ctx.run(event, fn);
throw new Error("No request event provider. Configure one with configureServerFunctionsServer({ provideEvent }).");
}
const REGISTRATIONS = new Map();
const METHODS = new Map();
const INVOCATIONS = new WeakMap();
function registerServerFunction(id, callback) {
REGISTRATIONS.set(id, callback);
return callback;
}
function getServerFunction(id) {
const fn = REGISTRATIONS.get(id);
if (fn) {
return fn;
}
throw new Error("invalid server function: " + id);
}
function registerServerReference(id, fn, name) {
registerServerFunction(id, fn);
return {
id,
fn,
name
};
}
function createServerReference({
id,
fn,
name
}) {
if (typeof fn !== "function") throw new Error("Export from a 'use server' module must be a function");
const metadata = name === undefined ? {} : {
name
};
return new Proxy(fn, {
get(target, prop) {
if (prop === "id") return id;
if (prop === "url") {
return `${config.endpoint}?id=${encodeURIComponent(id)}`;
}
if (prop === SERVER_FUNCTION_METADATA) return metadata;
return target[prop];
},
apply(target, thisArg, args) {
const ogEvt = getRequestEvent();
if (!ogEvt) throw new Error("Cannot call server function outside of a request");
const evt = {
...ogEvt
};
INVOCATIONS.set(evt, {
id
});
evt.serverOnly = true;
const result = provideEvent(evt, () => {
const run = () => fn.apply(thisArg, args);
return config.wrapInvocation ? config.wrapInvocation(run, {
id,
args,
event: evt,
direct: true
}) : run();
});
const transform = config.transformDirectResult;
if (transform && result && typeof result.then === "function") {
return result.then(value => transform(value, {
id,
args,
event: evt
}));
}
return transform ? transform(result, {
id,
args,
event: evt
}) : result;
}
});
}
function GET(fn) {
if (!isServerFunction(fn) || typeof fn.id !== "string") {
throw new Error("GET expects a server function reference");
}
METHODS.set(fn.id, "GET");
return withMeta(fn, {
method: "GET"
});
}
function getServerFunctionInvocation() {
return getEventServerFunctionInvocation(getRequestEvent());
}
function getEventServerFunctionInvocation(event) {
return event && INVOCATIONS.get(event);
}
function resolveFunctionId(request, url) {
const reference = request.headers.get(FUNCTION_HEADER);
if (reference) {
return reference.split("#")[0];
}
return url.searchParams.get("id");
}
async function parseArguments(request, url, instance, codec) {
const parsed = [];
const bodyFormat = request.method === "POST" ? request.headers.get(BODY_FORMAT_HEADER) : null;
if (!instance || request.method === "GET" || bodyFormat !== BodyFormat.Serialized) {
const args = url.searchParams.get("args");
if (args) {
const result = args.startsWith(";0x") ? await deserializeString(args, codec) : JSON.parse(args);
for (const arg of result) {
parsed.push(arg);
}
}
}
if (request.method === "POST" && request.body !== null) {
const decoded = await extractBody(request.clone(), codec);
if (bodyFormat === BodyFormat.Serialized || bodyFormat === BodyFormat.Json) {
return decoded;
}
parsed.push(decoded);
}
return parsed;
}
async function foldFlightData(hook, event, headers, outcome, context = {}) {
if (outcome.value instanceof Response && outcome.value.body) return outcome.value;
digestOutcome(event, outcome);
const data = await hook(event, outcome);
if (data === undefined) return outcome.value;
headers.set(SINGLE_FLIGHT_HEADER, "true");
if (context.transformFlightResult) {
const transformed = await context.transformFlightResult(event, {
value: outcome.value,
data
}, context);
if (transformed !== undefined) {
for (const cookie of headers.getSetCookie()) transformed.headers.append("Set-Cookie", cookie);
headers.forEach((value, key) => {
if (key !== "set-cookie" && !transformed.headers.has(key)) {
transformed.headers.set(key, value);
}
});
return transformed;
}
}
return {
value: outcome.value,
data
};
}
function digestOutcome(event, outcome) {
const {
request,
response
} = outcome;
outcome.revalidateKeys = response?.headers.get(REVALIDATE_HEADER)?.split(",");
outcome.foldedHeaders = foldSetCookies(request.headers, [...(event.response?.headers?.getSetCookie() ?? []), ...(response?.headers?.getSetCookie() ?? [])]);
try {
const referrer = request.headers.get("referer");
if (referrer) {
const location = response?.headers.get("Location");
const target = location ? new URL(location, request.url) : new URL(referrer);
if (target.origin === new URL(request.url).origin) outcome.targetUrl = target.toString();
}
} catch {
}
}
function parseSetCookie(setCookie) {
const [pair, ...attributes] = setCookie.split(";");
const eq = pair.indexOf("=");
if (eq < 0) return undefined;
const parsed = {
name: pair.slice(0, eq).trim(),
value: pair.slice(eq + 1).trim()
};
for (const attribute of attributes) {
const attrEq = attribute.indexOf("=");
const key = (attrEq < 0 ? attribute : attribute.slice(0, attrEq)).trim().toLowerCase();
const value = attrEq < 0 ? "" : attribute.slice(attrEq + 1).trim();
if (key === "max-age") parsed.maxAge = Number(value);else if (key === "expires") parsed.expires = new Date(value);
}
return parsed;
}
function foldSetCookies(headers, setCookies) {
const folded = new Headers(headers);
if (!setCookies.length) return folded;
const cookies = {};
for (const pair of folded.get("cookie")?.split(";") ?? []) {
const eq = pair.indexOf("=");
if (eq > -1) cookies[pair.slice(0, eq).trim()] = pair.slice(eq + 1).trim();
}
for (const setCookie of setCookies) {
const parsed = parseSetCookie(setCookie);
if (!parsed) continue;
if (parsed.maxAge != null && parsed.maxAge <= 0 || parsed.expires != null && parsed.expires.getTime() <= Date.now()) {
delete cookies[parsed.name];
} else {
cookies[parsed.name] = parsed.value;
}
}
folded.delete("cookie");
const serialized = Object.entries(cookies).map(([name, value]) => `${name}=${value}`).join("; ");
if (serialized) folded.set("cookie", serialized);
return folded;
}
function mergeResponseHeaders(target, source) {
source.forEach((value, key) => {
if (key !== "set-cookie") target.append(key, value);
});
if (source.getSetCookie) {
for (const cookie of source.getSetCookie()) target.append("Set-Cookie", cookie);
} else if (source.has("set-cookie")) {
target.append("Set-Cookie", source.get("set-cookie"));
}
}
const validRedirectStatuses = new Set([301, 302, 303, 307, 308]);
function createNoJSHandler({
base = ""
} = {}) {
return function handleNoJS(result, request, args, thrown) {
const url = new URL(request.url);
let back = new URL(base || "/", url.origin).toString();
try {
const referer = request.headers.get("referer");
if (referer) back = new URL(referer).toString();
} catch {}
let status = 303;
let headers;
if (result instanceof Response) {
headers = new Headers();
mergeResponseHeaders(headers, result.headers);
if (result.headers.has("Location")) {
headers.set("Location", new URL(result.headers.get("Location"), url.origin + base).toString());
if (validRedirectStatuses.has(result.status)) status = result.status;
} else {
headers.set("Location", back);
}
headers.delete("Content-Type");
headers.delete("Content-Length");
} else {
headers = new Headers({
Location: back
});
}
if (result && !(result instanceof Response)) {
headers.append("Set-Cookie", encodeFlashCookie(url.pathname + url.search, result, args, thrown));
}
return new Response(null, {
status,
headers
});
};
}
let defaultNoJSHandler;
function isFormPost(request) {
if (request.method !== "POST" || request.headers.has(BODY_FORMAT_HEADER)) return false;
const type = request.headers.get("content-type") || "";
return type.startsWith("application/x-www-form-urlencoded") || type.startsWith("multipart/form-data");
}
function serializedResponse(value, headers, codec) {
headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
headers.set("Content-Type", "text/plain");
return new Response(serializeStream(value, codec), {
headers
});
}
function encodeResult(value, headers, status, codec) {
const direct = getHeadersAndBody(value);
if (direct) {
for (const [key, val] of Object.entries(direct.headers || {})) {
headers.set(key, val);
}
return new Response(direct.body, {
status,
headers
});
}
const response = serializedResponse(value, headers, codec);
return status === 200 ? response : new Response(response.body, {
status,
headers
});
}
const GENERIC_SERVER_ERROR_MESSAGE = "Internal Server Error";
let DEV = true === true;
function setServerFunctionsDev(dev) {
DEV = !!dev;
}
function sanitizeServerError(value) {
if (DEV) return value;
if (isSafeError(value)) return value;
return new Error(GENERIC_SERVER_ERROR_MESSAGE);
}
async function handleServerFunctionRequest(request, options = {}) {
const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
const url = new URL(request.url);
const instance = request.headers.get(INSTANCE_HEADER);
const functionId = resolveFunctionId(request, url);
if (!functionId) {
return new Response(DEV ? "Server function not found" : null, {
status: 404
});
}
let serverFunction;
try {
serverFunction = getServerFunction(functionId);
} catch {
return new Response(DEV ? `Unknown server function: ${functionId}` : null, {
status: 404
});
}
if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
return new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
status: 405,
headers: {
Allow: "POST"
}
});
}
const event = options.createEvent ? options.createEvent(request) : {
request,
locals: {}
};
const provide = options.provideEvent || provideEvent;
const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
const wrapInvocation = options.wrapInvocation !== undefined ? options.wrapInvocation : config.wrapInvocation;
const transformFlightResult = options.transformFlightResult !== undefined ? options.transformFlightResult : config.transformFlightResult;
const handleNoJS = options.handleNoJS !== undefined ? options.handleNoJS : config.handleNoJS !== undefined ? config.handleNoJS : isFormPost(request) ? defaultNoJSHandler || (defaultNoJSHandler = createNoJSHandler()) : undefined;
const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
const parsed = await parseArguments(request, url, instance, codec);
const flightContext = {
id: functionId,
args: parsed,
instance,
request,
collectsFlight,
codec,
transformFlightResult
};
const headers = new Headers();
const dispatch = async () => {
try {
let result = await provide(event, async () => {
INVOCATIONS.set(event, {
id: functionId
});
const run = () => serverFunction(...parsed);
return wrapInvocation ? wrapInvocation(run, {
id: functionId,
args: parsed,
event,
request,
direct: false
}) : run();
});
if (transformResult) {
result = await transformResult(event, result, flightContext);
}
let status = 200;
let metadata;
if (isResponseEnvelope(result)) {
const {
response,
value
} = result;
if (!instance && !handleNoJS && response && response.body) {
return response;
}
if (response && response.headers) {
mergeResponseHeaders(headers, response.headers);
}
if (response && response.status && (response.status < 300 || response.status >= 400)) {
status = response.status;
}
metadata = response;
result = value;
} else if (result instanceof Response) {
if (result.headers && result.headers.has("X-Content-Raw")) return result;
if (instance) {
if (result.headers) {
mergeResponseHeaders(headers, result.headers);
}
if (result.status && (result.status < 300 || result.status >= 400)) {
status = result.status;
}
metadata = result;
if (result.body == null) {
result = null;
}
}
}
if (collectsFlight) {
result = await foldFlightData(flightHook, event, headers, {
id: functionId,
value: result,
response: metadata,
request,
thrown: false
}, flightContext);
if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
}
if (!instance) {
if (handleNoJS) return handleNoJS(result, request, parsed);
if (result instanceof Response) return result;
return encodeResult(result, headers, 200, codec);
}
return encodeResult(result, headers, status, codec);
} catch (x) {
if (x instanceof Response || isResponseEnvelope(x)) {
if (transformResult) {
x = await transformResult(event, x, {
...flightContext,
thrown: true
});
}
let status = 200;
let metadata;
if (isResponseEnvelope(x)) {
const {
response,
value
} = x;
if (response && response.headers) {
mergeResponseHeaders(headers, response.headers);
}
if (response && response.status && (!instance || response.status < 300 || response.status >= 400)) {
status = response.status;
}
metadata = response;
x = value;
} else if (x instanceof Response) {
if (x.headers) {
mergeResponseHeaders(headers, x.headers);
}
if (x.status && (!instance || x.status < 300 || x.status >= 400)) {
status = x.status;
}
metadata = x;
if (x.body == null) {
x = null;
}
}
if (collectsFlight) {
x = await foldFlightData(flightHook, event, headers, {
id: functionId,
value: x,
response: metadata,
request,
thrown: true
}, flightContext);
if (x instanceof Response && x.headers.has("X-Content-Raw")) {
x.headers.set(ERROR_HEADER, "true");
return x;
}
}
headers.set(ERROR_HEADER, "true");
if (!instance) {
if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
if (x instanceof Response) return x;
}
return encodeResult(x, headers, status, codec);
}
const safe = sanitizeServerError(x);
if (!instance) {
if (handleNoJS) return handleNoJS(safe, request, parsed, true);
const message = safe instanceof Error ? safe.message : String(safe);
return new Response(DEV ? message : null, {
status: 500
});
}
const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
return encodeResult(safe, headers, 200, codec);
}
};
return commitEventResponse(await dispatch(), event);
}
exports.ERROR_HEADER = ERROR_HEADER;
exports.FLASH_COOKIE = FLASH_COOKIE;
exports.FUNCTION_HEADER = FUNCTION_HEADER;
exports.GENERIC_SERVER_ERROR_MESSAGE = GENERIC_SERVER_ERROR_MESSAGE;
exports.GET = GET;
exports.INSTANCE_HEADER = INSTANCE_HEADER;
exports.SINGLE_FLIGHT_HEADER = SINGLE_FLIGHT_HEADER;
exports.clearFlashCookie = clearFlashCookie;
exports.configureServerFunctionsServer = configureServerFunctionsServer;
exports.createNoJSHandler = createNoJSHandler;
exports.createServerReference = createServerReference;
exports.decodeErrorHeaderValue = decodeErrorHeaderValue;
exports.decodeFlashCookie = decodeFlashCookie;
exports.decodeResponse = decodeResponse;
exports.decodeResponsePayload = decodeResponsePayload;
exports.encodeErrorHeaderValue = encodeErrorHeaderValue;
exports.encodeFlashCookie = encodeFlashCookie;
exports.foldSetCookies = foldSetCookies;
exports.getEventServerFunctionInvocation = getEventServerFunctionInvocation;
exports.getServerFunction = getServerFunction;
exports.getServerFunctionInvocation = getServerFunctionInvocation;
exports.getServerFunctionMetadata = getServerFunctionMetadata;
exports.handleServerFunctionRequest = handleServerFunctionRequest;
exports.hasFlashCookie = hasFlashCookie;
exports.isServerFunction = isServerFunction;
exports.registerServerFunction = registerServerFunction;
exports.registerServerReference = registerServerReference;
exports.sanitizeServerError = sanitizeServerError;
exports.setServerFunctionsDev = setServerFunctionsDev;
exports.subscribeFlightData = subscribeFlightData;
exports.withMeta = withMeta;
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';
const ENVELOPE = Symbol.for("solid.ResponseEnvelope");
function isResponseEnvelope(value) {
return !!(value && typeof value === "object" && value[ENVELOPE]);
}
const SAFE_ERROR = Symbol.for("solid.SafeError");
function isSafeError(value) {
return !!(value && (typeof value === "object" || typeof value === "function") && value[SAFE_ERROR]);
}
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 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, {
onParse,
onDone,
onError,
...codecOptions
}) {
const resolved = resolveCodecOptions(codecOptions);
return toCrossJSONStream(value, {
onParse,
onDone,
onError,
...resolved,
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
});
}
function createJSONDeserializer(options) {
const refs = new Map();
const resolved = resolveCodecOptions(options);
return function deserializeJSONChunk(node) {
return fromCrossJSON(node, {
refs,
...resolved
});
};
}
const codecConfig = {
codec: undefined
};
function configureServerFunctionsCodec(codec) {
codecConfig.codec = codec;
}
function getServerFunctionsCodec() {
return codecConfig.codec;
}
function subscribeFlightData(consumer) {
return () => {
};
}
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";
const ERROR_HEADER = "X-Server-Function-Error";
const ERROR_HEADER_MARKER = "=?1?";
const NEEDS_ENCODING = /[^\x20-\x7e\xa0-\xff]/;
function encodeErrorHeaderValue(value) {
let stripped = String(value).replace(/[\r\n]+/g, "");
if (!NEEDS_ENCODING.test(stripped) && !stripped.startsWith(ERROR_HEADER_MARKER) && stripped === stripped.trim()) {
return stripped;
}
if (typeof stripped.toWellFormed === "function") {
stripped = stripped.toWellFormed();
} else {
stripped = stripped.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g, "\uFFFD");
}
return ERROR_HEADER_MARKER + encodeURIComponent(stripped);
}
function decodeErrorHeaderValue(value) {
if (typeof value !== "string" || !value.startsWith(ERROR_HEADER_MARKER)) {
return value;
}
try {
return decodeURIComponent(value.slice(ERROR_HEADER_MARKER.length));
} catch {
return value;
}
}
const INSTANCE_HEADER = "X-Server-Function-Instance";
const BODY_FORMAT_HEADER = "X-Server-Function-Format";
const SINGLE_FLIGHT_HEADER = "X-Single-Flight";
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 = {
Serialized: "0",
String: "1",
FormData: "2",
URLSearchParams: "3",
Blob: "4",
File: "5",
ArrayBuffer: "6",
Uint8Array: "7",
Json: "8"
};
function getHeadersAndBody(body) {
switch (true) {
case typeof body === "string":
return {
headers: {
"Content-Type": "text/plain",
[BODY_FORMAT_HEADER]: BodyFormat.String
},
body
};
case body instanceof FormData:
return {
headers: {
[BODY_FORMAT_HEADER]: BodyFormat.FormData
},
body
};
case body instanceof URLSearchParams:
return {
headers: {
"Content-Type": "application/x-www-form-urlencoded",
[BODY_FORMAT_HEADER]: BodyFormat.URLSearchParams
},
body
};
case typeof File !== "undefined" && body instanceof File:
{
const formData = new FormData();
formData.append(FILE_FORM_KEY, body, body.name);
return {
headers: {
[BODY_FORMAT_HEADER]: BodyFormat.File
},
body: formData
};
}
case body instanceof Blob:
return {
headers: {
[BODY_FORMAT_HEADER]: BodyFormat.Blob
},
body
};
case body instanceof ArrayBuffer:
return {
headers: {
[BODY_FORMAT_HEADER]: BodyFormat.ArrayBuffer
},
body
};
case body instanceof Uint8Array:
return {
headers: {
[BODY_FORMAT_HEADER]: BodyFormat.Uint8Array
},
body: new Uint8Array(body)
};
default:
return undefined;
}
}
async function extractBody(source, codecOptions) {
const contentType = source.headers.get("content-type");
const format = source.headers.get(BODY_FORMAT_HEADER);
const clone = source.clone();
switch (true) {
case format === BodyFormat.Serialized:
return await deserializeStream(clone, codecOptions);
case format === BodyFormat.Json:
return JSON.parse(await clone.text());
case format === BodyFormat.String:
return await clone.text();
case format === BodyFormat.File:
{
const formData = await clone.formData();
return formData.get(FILE_FORM_KEY);
}
case format === BodyFormat.FormData:
case contentType && contentType.startsWith("multipart/form-data"):
return await clone.formData();
case format === BodyFormat.URLSearchParams:
case contentType && contentType.startsWith("application/x-www-form-urlencoded"):
return new URLSearchParams(await clone.text());
case format === BodyFormat.Blob:
return await clone.blob();
case format === BodyFormat.ArrayBuffer:
return await clone.arrayBuffer();
case format === BodyFormat.Uint8Array:
return new Uint8Array(await clone.arrayBuffer());
}
return undefined;
}
function createChunk(data) {
const encoder = new TextEncoder();
const encodeData = encoder.encode(data);
const bytes = encodeData.length;
const chunk = new Uint8Array(12 + bytes);
chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
chunk.set(encodeData, 12);
return chunk;
}
class ChunkReader {
constructor(stream) {
this.reader = stream.getReader();
this.buffer = new Uint8Array(0);
this.done = false;
}
async readChunk() {
const chunk = await this.reader.read();
if (!chunk.done) {
const newBuffer = new Uint8Array(this.buffer.length + chunk.value.length);
newBuffer.set(this.buffer);
newBuffer.set(chunk.value, this.buffer.length);
this.buffer = newBuffer;
} else {
this.done = true;
}
}
async next() {
while (this.buffer.length < 12) {
if (this.done) {
if (this.buffer.length === 0) return {
done: true,
value: undefined
};
throw new Error("Malformed server function stream.");
}
await this.readChunk();
}
const decoder = new TextDecoder();
const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
if (Number.isNaN(bytes)) {
throw new Error("Malformed server function stream.");
}
while (bytes > this.buffer.length - 12) {
if (this.done) {
throw new Error("Malformed server function stream.");
}
await this.readChunk();
}
const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
this.buffer = this.buffer.subarray(12 + bytes);
return {
done: false,
value: partial
};
}
async drain(interpret) {
while (true) {
const result = await this.next();
if (result.done) {
break;
}
interpret(result.value);
}
}
}
function serializeStream(value, codecOptions) {
return new ReadableStream({
start(controller) {
serializeJSON(value, {
...codecOptions,
onParse(node) {
controller.enqueue(createChunk(JSON.stringify(node)));
},
onDone() {
controller.close();
},
onError(error) {
controller.error(error);
}
});
}
});
}
async function deserializeStream(source, codecOptions) {
if (!source.body) {
throw new Error("missing body");
}
const reader = new ChunkReader(source.body);
const result = await reader.next();
if (!result.done) {
const deserializeChunk = createJSONDeserializer(codecOptions);
function interpretChunk(chunk) {
return deserializeChunk(JSON.parse(chunk));
}
void reader.drain(interpretChunk);
return interpretChunk(result.value);
}
return undefined;
}
async function deserializeString(text, codecOptions) {
return await deserializeStream(new Response(text), codecOptions);
}
async function decodeResponse(response, codecOptions) {
if (!response.body) return undefined;
return await extractBody(response, codecOptions === undefined ? codecConfig.codec : codecOptions);
}
async function decodeResponsePayload(response, codecOptions) {
const decoded = await decodeResponse(response, codecOptions);
if (decoded !== undefined && response.headers.has(SINGLE_FLIGHT_HEADER)) {
return {
value: decoded.value,
flightData: decoded.data
};
}
return {
value: decoded
};
}
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");
function getRequestEvent() {
return globalThis[RequestContext] ? globalThis[RequestContext].getStore() || sharedConfig.context && sharedConfig.context.event || console.warn("RequestEvent is missing. This is most likely due to accessing `getRequestEvent` non-managed async scope in a partially polyfilled environment. Try moving it above all `await` calls.") : undefined;
}
function reportLostHeaderWrite(method, name) {
const message = `Response header write dropped: headers.${method}(${JSON.stringify(String(name))}) ` + "ran after the response head was sent. Write headers before the shell flushes " + "(or before the handler returns).";
throw new Error(message);
}
function commitResponseStub(stub, {
allowLateLocation = false
} = {}) {
if (!stub || stub.committed) return stub;
stub.committed = true;
const headers = stub.headers;
if (!headers || typeof headers.set !== "function") return stub;
for (const method of ["set", "append", "delete"]) {
const original = headers[method].bind(headers);
headers[method] = function (name, ...rest) {
if (allowLateLocation && method === "set" && String(name).toLowerCase() === "location") {
return original(name, ...rest);
}
reportLostHeaderWrite(method, name);
};
}
return stub;
}
function copyInitHeaders(init) {
if (!init || !init.getSetCookie) return new Headers(init);
const headers = new Headers();
init.forEach((value, key) => {
if (key !== "set-cookie") headers.append(key, value);
});
for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
return headers;
}
const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, "Location"].map(header => header.toLowerCase()));
function fillsStubGap(key, headers, response) {
if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
if (response.body === null && (key === "content-type" || key === "content-length")) return false;
return !headers.has(key);
}
function commitEventResponse(response, event = getRequestEvent()) {
const stub = event && event.response;
if (!stub || !stub.headers || stub.committed) return response;
const cookies = stub.headers.getSetCookie ? stub.headers.getSetCookie() : [];
commitResponseStub(stub);
let hasGaps = false;
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, response.headers, response)) hasGaps = true;
});
if (!cookies.length && !hasGaps) return response;
try {
for (const cookie of cookies) response.headers.append("Set-Cookie", cookie);
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, response.headers, response)) response.headers.set(key, value);
});
return response;
} catch {
const headers = copyInitHeaders(response.headers);
for (const cookie of cookies) headers.append("Set-Cookie", cookie);
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, headers, response)) headers.set(key, value);
});
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers
});
}
}
function encodeInputValue(value) {
if (value instanceof FormData) return {
$f: [...value.entries()].filter(([, v]) => typeof v === "string")
};
if (value instanceof URLSearchParams) return {
$u: [...value.entries()]
};
return value;
}
function decodeInputValue(value) {
if (value && typeof value === "object") {
if (Array.isArray(value.$f)) {
const form = new FormData();
for (const [k, v] of value.$f) form.append(k, v);
return form;
}
if (Array.isArray(value.$u)) return new URLSearchParams(value.$u);
}
return value;
}
function encodeFlashCookie(url, result, input, thrown) {
const isError = result instanceof Error;
const payload = {
url,
result: isError ? result.message : result,
error: isError,
thrown: !!thrown,
input: input.map(encodeInputValue)
};
return serializeCookie(FLASH_COOKIE, JSON.stringify(payload), {
secure: true,
httpOnly: true
});
}
function decodeFlashCookie(cookieHeader) {
const match = parseCookieHeader(cookieHeader)[FLASH_COOKIE];
if (!match) return;
try {
const payload = JSON.parse(match);
if (!payload || !payload.result) return;
const result = payload.error ? new Error(payload.result) : payload.result;
return {
input: Array.isArray(payload.input) ? payload.input.map(decodeInputValue) : [],
url: payload.url,
result: payload.thrown ? undefined : result,
error: payload.thrown ? result : undefined
};
} catch (error) {
console.error(error);
}
}
const config = {
provideEvent: undefined,
wrapInvocation: undefined,
collectFlightData: undefined,
transformResult: undefined,
transformFlightResult: undefined,
transformDirectResult: undefined,
handleNoJS: undefined,
endpoint: "/_server"
};
function configureServerFunctionsServer({
provideEvent,
wrapInvocation,
collectFlightData,
transformResult,
transformFlightResult,
transformDirectResult,
handleNoJS,
endpoint,
codec
} = {}) {
if (provideEvent !== undefined) config.provideEvent = provideEvent;
if (wrapInvocation !== undefined) config.wrapInvocation = wrapInvocation;
if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;
if (transformResult !== undefined) config.transformResult = transformResult;
if (transformFlightResult !== undefined) config.transformFlightResult = transformFlightResult;
if (transformDirectResult !== undefined) config.transformDirectResult = transformDirectResult;
if (handleNoJS !== undefined) config.handleNoJS = handleNoJS;
if (endpoint !== undefined) config.endpoint = endpoint;
if (codec !== undefined) configureServerFunctionsCodec(codec);
}
function provideEvent(event, fn) {
if (config.provideEvent) return config.provideEvent(event, fn);
const ctx = globalThis[RequestContext];
if (ctx) return ctx.run(event, fn);
throw new Error("No request event provider. Configure one with configureServerFunctionsServer({ provideEvent }).");
}
const REGISTRATIONS = new Map();
const METHODS = new Map();
const INVOCATIONS = new WeakMap();
function registerServerFunction(id, callback) {
REGISTRATIONS.set(id, callback);
return callback;
}
function getServerFunction(id) {
const fn = REGISTRATIONS.get(id);
if (fn) {
return fn;
}
throw new Error("invalid server function: " + id);
}
function registerServerReference(id, fn, name) {
registerServerFunction(id, fn);
return {
id,
fn,
name
};
}
function createServerReference({
id,
fn,
name
}) {
if (typeof fn !== "function") throw new Error("Export from a 'use server' module must be a function");
const metadata = name === undefined ? {} : {
name
};
return new Proxy(fn, {
get(target, prop) {
if (prop === "id") return id;
if (prop === "url") {
return `${config.endpoint}?id=${encodeURIComponent(id)}`;
}
if (prop === SERVER_FUNCTION_METADATA) return metadata;
return target[prop];
},
apply(target, thisArg, args) {
const ogEvt = getRequestEvent();
if (!ogEvt) throw new Error("Cannot call server function outside of a request");
const evt = {
...ogEvt
};
INVOCATIONS.set(evt, {
id
});
evt.serverOnly = true;
const result = provideEvent(evt, () => {
const run = () => fn.apply(thisArg, args);
return config.wrapInvocation ? config.wrapInvocation(run, {
id,
args,
event: evt,
direct: true
}) : run();
});
const transform = config.transformDirectResult;
if (transform && result && typeof result.then === "function") {
return result.then(value => transform(value, {
id,
args,
event: evt
}));
}
return transform ? transform(result, {
id,
args,
event: evt
}) : result;
}
});
}
function GET(fn) {
if (!isServerFunction(fn) || typeof fn.id !== "string") {
throw new Error("GET expects a server function reference");
}
METHODS.set(fn.id, "GET");
return withMeta(fn, {
method: "GET"
});
}
function getServerFunctionInvocation() {
return getEventServerFunctionInvocation(getRequestEvent());
}
function getEventServerFunctionInvocation(event) {
return event && INVOCATIONS.get(event);
}
function resolveFunctionId(request, url) {
const reference = request.headers.get(FUNCTION_HEADER);
if (reference) {
return reference.split("#")[0];
}
return url.searchParams.get("id");
}
async function parseArguments(request, url, instance, codec) {
const parsed = [];
const bodyFormat = request.method === "POST" ? request.headers.get(BODY_FORMAT_HEADER) : null;
if (!instance || request.method === "GET" || bodyFormat !== BodyFormat.Serialized) {
const args = url.searchParams.get("args");
if (args) {
const result = args.startsWith(";0x") ? await deserializeString(args, codec) : JSON.parse(args);
for (const arg of result) {
parsed.push(arg);
}
}
}
if (request.method === "POST" && request.body !== null) {
const decoded = await extractBody(request.clone(), codec);
if (bodyFormat === BodyFormat.Serialized || bodyFormat === BodyFormat.Json) {
return decoded;
}
parsed.push(decoded);
}
return parsed;
}
async function foldFlightData(hook, event, headers, outcome, context = {}) {
if (outcome.value instanceof Response && outcome.value.body) return outcome.value;
digestOutcome(event, outcome);
const data = await hook(event, outcome);
if (data === undefined) return outcome.value;
headers.set(SINGLE_FLIGHT_HEADER, "true");
if (context.transformFlightResult) {
const transformed = await context.transformFlightResult(event, {
value: outcome.value,
data
}, context);
if (transformed !== undefined) {
for (const cookie of headers.getSetCookie()) transformed.headers.append("Set-Cookie", cookie);
headers.forEach((value, key) => {
if (key !== "set-cookie" && !transformed.headers.has(key)) {
transformed.headers.set(key, value);
}
});
return transformed;
}
}
return {
value: outcome.value,
data
};
}
function digestOutcome(event, outcome) {
const {
request,
response
} = outcome;
outcome.revalidateKeys = response?.headers.get(REVALIDATE_HEADER)?.split(",");
outcome.foldedHeaders = foldSetCookies(request.headers, [...(event.response?.headers?.getSetCookie() ?? []), ...(response?.headers?.getSetCookie() ?? [])]);
try {
const referrer = request.headers.get("referer");
if (referrer) {
const location = response?.headers.get("Location");
const target = location ? new URL(location, request.url) : new URL(referrer);
if (target.origin === new URL(request.url).origin) outcome.targetUrl = target.toString();
}
} catch {
}
}
function parseSetCookie(setCookie) {
const [pair, ...attributes] = setCookie.split(";");
const eq = pair.indexOf("=");
if (eq < 0) return undefined;
const parsed = {
name: pair.slice(0, eq).trim(),
value: pair.slice(eq + 1).trim()
};
for (const attribute of attributes) {
const attrEq = attribute.indexOf("=");
const key = (attrEq < 0 ? attribute : attribute.slice(0, attrEq)).trim().toLowerCase();
const value = attrEq < 0 ? "" : attribute.slice(attrEq + 1).trim();
if (key === "max-age") parsed.maxAge = Number(value);else if (key === "expires") parsed.expires = new Date(value);
}
return parsed;
}
function foldSetCookies(headers, setCookies) {
const folded = new Headers(headers);
if (!setCookies.length) return folded;
const cookies = {};
for (const pair of folded.get("cookie")?.split(";") ?? []) {
const eq = pair.indexOf("=");
if (eq > -1) cookies[pair.slice(0, eq).trim()] = pair.slice(eq + 1).trim();
}
for (const setCookie of setCookies) {
const parsed = parseSetCookie(setCookie);
if (!parsed) continue;
if (parsed.maxAge != null && parsed.maxAge <= 0 || parsed.expires != null && parsed.expires.getTime() <= Date.now()) {
delete cookies[parsed.name];
} else {
cookies[parsed.name] = parsed.value;
}
}
folded.delete("cookie");
const serialized = Object.entries(cookies).map(([name, value]) => `${name}=${value}`).join("; ");
if (serialized) folded.set("cookie", serialized);
return folded;
}
function mergeResponseHeaders(target, source) {
source.forEach((value, key) => {
if (key !== "set-cookie") target.append(key, value);
});
if (source.getSetCookie) {
for (const cookie of source.getSetCookie()) target.append("Set-Cookie", cookie);
} else if (source.has("set-cookie")) {
target.append("Set-Cookie", source.get("set-cookie"));
}
}
const validRedirectStatuses = new Set([301, 302, 303, 307, 308]);
function createNoJSHandler({
base = ""
} = {}) {
return function handleNoJS(result, request, args, thrown) {
const url = new URL(request.url);
let back = new URL(base || "/", url.origin).toString();
try {
const referer = request.headers.get("referer");
if (referer) back = new URL(referer).toString();
} catch {}
let status = 303;
let headers;
if (result instanceof Response) {
headers = new Headers();
mergeResponseHeaders(headers, result.headers);
if (result.headers.has("Location")) {
headers.set("Location", new URL(result.headers.get("Location"), url.origin + base).toString());
if (validRedirectStatuses.has(result.status)) status = result.status;
} else {
headers.set("Location", back);
}
headers.delete("Content-Type");
headers.delete("Content-Length");
} else {
headers = new Headers({
Location: back
});
}
if (result && !(result instanceof Response)) {
headers.append("Set-Cookie", encodeFlashCookie(url.pathname + url.search, result, args, thrown));
}
return new Response(null, {
status,
headers
});
};
}
let defaultNoJSHandler;
function isFormPost(request) {
if (request.method !== "POST" || request.headers.has(BODY_FORMAT_HEADER)) return false;
const type = request.headers.get("content-type") || "";
return type.startsWith("application/x-www-form-urlencoded") || type.startsWith("multipart/form-data");
}
function serializedResponse(value, headers, codec) {
headers.set(BODY_FORMAT_HEADER, BodyFormat.Serialized);
headers.set("Content-Type", "text/plain");
return new Response(serializeStream(value, codec), {
headers
});
}
function encodeResult(value, headers, status, codec) {
const direct = getHeadersAndBody(value);
if (direct) {
for (const [key, val] of Object.entries(direct.headers || {})) {
headers.set(key, val);
}
return new Response(direct.body, {
status,
headers
});
}
const response = serializedResponse(value, headers, codec);
return status === 200 ? response : new Response(response.body, {
status,
headers
});
}
const GENERIC_SERVER_ERROR_MESSAGE = "Internal Server Error";
let DEV = true === true;
function setServerFunctionsDev(dev) {
DEV = !!dev;
}
function sanitizeServerError(value) {
if (DEV) return value;
if (isSafeError(value)) return value;
return new Error(GENERIC_SERVER_ERROR_MESSAGE);
}
async function handleServerFunctionRequest(request, options = {}) {
const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();
const url = new URL(request.url);
const instance = request.headers.get(INSTANCE_HEADER);
const functionId = resolveFunctionId(request, url);
if (!functionId) {
return new Response(DEV ? "Server function not found" : null, {
status: 404
});
}
let serverFunction;
try {
serverFunction = getServerFunction(functionId);
} catch {
return new Response(DEV ? `Unknown server function: ${functionId}` : null, {
status: 404
});
}
if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
return new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
status: 405,
headers: {
Allow: "POST"
}
});
}
const event = options.createEvent ? options.createEvent(request) : {
request,
locals: {}
};
const provide = options.provideEvent || provideEvent;
const flightHook = options.collectFlightData !== undefined ? options.collectFlightData : config.collectFlightData;
const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
const wrapInvocation = options.wrapInvocation !== undefined ? options.wrapInvocation : config.wrapInvocation;
const transformFlightResult = options.transformFlightResult !== undefined ? options.transformFlightResult : config.transformFlightResult;
const handleNoJS = options.handleNoJS !== undefined ? options.handleNoJS : config.handleNoJS !== undefined ? config.handleNoJS : isFormPost(request) ? defaultNoJSHandler || (defaultNoJSHandler = createNoJSHandler()) : undefined;
const collectsFlight = !!(flightHook && instance && request.headers.has(SINGLE_FLIGHT_HEADER));
const parsed = await parseArguments(request, url, instance, codec);
const flightContext = {
id: functionId,
args: parsed,
instance,
request,
collectsFlight,
codec,
transformFlightResult
};
const headers = new Headers();
const dispatch = async () => {
try {
let result = await provide(event, async () => {
INVOCATIONS.set(event, {
id: functionId
});
const run = () => serverFunction(...parsed);
return wrapInvocation ? wrapInvocation(run, {
id: functionId,
args: parsed,
event,
request,
direct: false
}) : run();
});
if (transformResult) {
result = await transformResult(event, result, flightContext);
}
let status = 200;
let metadata;
if (isResponseEnvelope(result)) {
const {
response,
value
} = result;
if (!instance && !handleNoJS && response && response.body) {
return response;
}
if (response && response.headers) {
mergeResponseHeaders(headers, response.headers);
}
if (response && response.status && (response.status < 300 || response.status >= 400)) {
status = response.status;
}
metadata = response;
result = value;
} else if (result instanceof Response) {
if (result.headers && result.headers.has("X-Content-Raw")) return result;
if (instance) {
if (result.headers) {
mergeResponseHeaders(headers, result.headers);
}
if (result.status && (result.status < 300 || result.status >= 400)) {
status = result.status;
}
metadata = result;
if (result.body == null) {
result = null;
}
}
}
if (collectsFlight) {
result = await foldFlightData(flightHook, event, headers, {
id: functionId,
value: result,
response: metadata,
request,
thrown: false
}, flightContext);
if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
}
if (!instance) {
if (handleNoJS) return handleNoJS(result, request, parsed);
if (result instanceof Response) return result;
return encodeResult(result, headers, 200, codec);
}
return encodeResult(result, headers, status, codec);
} catch (x) {
if (x instanceof Response || isResponseEnvelope(x)) {
if (transformResult) {
x = await transformResult(event, x, {
...flightContext,
thrown: true
});
}
let status = 200;
let metadata;
if (isResponseEnvelope(x)) {
const {
response,
value
} = x;
if (response && response.headers) {
mergeResponseHeaders(headers, response.headers);
}
if (response && response.status && (!instance || response.status < 300 || response.status >= 400)) {
status = response.status;
}
metadata = response;
x = value;
} else if (x instanceof Response) {
if (x.headers) {
mergeResponseHeaders(headers, x.headers);
}
if (x.status && (!instance || x.status < 300 || x.status >= 400)) {
status = x.status;
}
metadata = x;
if (x.body == null) {
x = null;
}
}
if (collectsFlight) {
x = await foldFlightData(flightHook, event, headers, {
id: functionId,
value: x,
response: metadata,
request,
thrown: true
}, flightContext);
if (x instanceof Response && x.headers.has("X-Content-Raw")) {
x.headers.set(ERROR_HEADER, "true");
return x;
}
}
headers.set(ERROR_HEADER, "true");
if (!instance) {
if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
if (x instanceof Response) return x;
}
return encodeResult(x, headers, status, codec);
}
const safe = sanitizeServerError(x);
if (!instance) {
if (handleNoJS) return handleNoJS(safe, request, parsed, true);
const message = safe instanceof Error ? safe.message : String(safe);
return new Response(DEV ? message : null, {
status: 500
});
}
const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
return encodeResult(safe, headers, 200, codec);
}
};
return commitEventResponse(await dispatch(), event);
}
export { ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GENERIC_SERVER_ERROR_MESSAGE, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, decodeResponsePayload, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getEventServerFunctionInvocation, getServerFunction, getServerFunctionInvocation, getServerFunctionMetadata, handleServerFunctionRequest, hasFlashCookie, isServerFunction, registerServerFunction, registerServerReference, sanitizeServerError, setServerFunctionsDev, subscribeFlightData, withMeta };
{
"name": "@solidjs/web/server-functions/rich-args",
"main": "../dist/rich-args.cjs",
"module": "../dist/rich-args.js",
"types": "../../types/server-functions/rich-args.d.ts",
"type": "module",
"sideEffects": false,
"exports": {
".": {
"import": {
"types": "../../types/server-functions/rich-args.d.ts",
"default": "../dist/rich-args.js"
},
"require": {
"types": "../../types-cjs/server-functions/rich-args.d.cts",
"default": "../dist/rich-args.cjs"
}
}
}
}
/**
* Opt-in codec encoding for server-function ARGUMENTS. By default the client
* sends argument lists as plain JSON (no serializer in the bundle) and
* throws on values JSON can't carry faithfully. Call once at startup to
* send Dates, Maps, Sets, typed arrays, cyclic structures, etc. through the
* codec — at the cost of the serializer's write half (~5 KB gz on top of
* the decode half responses already need). The handler accepts both
* encodings unconditionally.
*/
export function enableRichArguments(): void;
/**
* Opt-in codec encoding for server-function ARGUMENTS. By default the client
* sends argument lists as plain JSON (no serializer in the bundle) and
* throws on values JSON can't carry faithfully. Call once at startup to
* send Dates, Maps, Sets, typed arrays, cyclic structures, etc. through the
* codec — at the cost of the serializer's write half (~5 KB gz on top of
* the decode half responses already need). The handler accepts both
* encodings unconditionally.
*/
export function enableRichArguments(): void;
+217
-112

@@ -99,5 +99,3 @@ 'use strict';

store.version = version;
for (const key of Object.keys(store.records)) {
if (key.startsWith("seg:") || key === ":error") delete store.records[key];
}
clearStreamRecords(store.records);
}

@@ -180,2 +178,3 @@ Object.assign(store.records, records);

#hasContent = false;
#errorNotified = false;
#revealed = new Set();

@@ -200,6 +199,8 @@ #fallbackShown = new Set();

if (!handlers) return;
const run = () => direct ? claimNode(handlers, node) : claimTree(handlers, node);
this.#scoped(() => direct ? claimNode(handlers, node) : claimTree(handlers, node));
};
#scoped(fn) {
const scope = this.#options.ownerScope;
scope ? scope(run) : run();
};
return scope ? scope(fn) : fn();
}
constructor(element, start, end, options = {}) {

@@ -265,11 +266,5 @@ this.#element = element;

this.#version = v;
this.#revealed.clear();
this.#fallbackShown.clear();
for (const key of Object.keys(this.#store)) {
if (key.startsWith("seg:") || key === ":error") {
delete this.#store[key];
}
}
this.#resetStreamState();
}
for (const key of Object.keys(write.r)) {
for (const key in write.r) {
const incoming = write.r[key];

@@ -286,2 +281,8 @@ if (incoming && incoming.kind === "slot" && key.charCodeAt(0) === 115 && key.startsWith("slot:")) {

}
#resetStreamState(root) {
this.#revealed.clear();
this.#fallbackShown.clear();
this.#errorNotified = false;
clearStreamRecords(this.#store, root);
}
#flush() {

@@ -297,6 +298,10 @@ if (this.#disposed) return;

}
if (this.#store[":error"] && !this.#errorNotified) {
this.#errorNotified = true;
this.#applied(version, "error");
}
let progressed = true;
while (progressed) {
progressed = false;
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
const name = segmentName(key);

@@ -310,3 +315,3 @@ if (name === null || this.#revealed.has(name)) continue;

}
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
const m = /^seg:([^:]+):fallback$/.exec(key);

@@ -323,3 +328,3 @@ if (!m) continue;

}
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;

@@ -356,2 +361,3 @@ this.#processedAssets.add(key);

const record = this.#resolveSlotRecord(occurrence);
if (record && record.kind === "slot" && this.#refsUnresolved(record.args)) continue;
const prev = this.#slotNodes.get(occurrence);

@@ -412,11 +418,3 @@ const prevFirst = Array.isArray(prev) ? prev[0] : prev;

let end = null;
if (start) {
const endData = slotEnd(occurrence);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
existing.push(n);
n = n.nextSibling;
}
end = n;
}
if (start) end = eachInRange(start, occurrence, n => existing.push(n));
const ctx = {

@@ -436,4 +434,3 @@ frame: this.#options.claimScope ?? this.#options.id,

const props = record && record.kind === "slot" ? this.#resolveArgs(occurrence, record.args) : {};
const scope = this.#options.ownerScope;
const content = scope ? scope(() => callback(props, ctx)) : callback(props, ctx);
const content = this.#scoped(() => callback(props, ctx));
this.#slotArgs.set(occurrence, record);

@@ -445,11 +442,5 @@ if (cleanups.length) this.#slotCleanups.set(occurrence, cleanups);

#replaceRange(key, start, nodes) {
const end = slotEnd(key);
const parent = start.parentNode;
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
for (const node of nodes) parent.insertBefore(node, n);
const end = eachInRange(start, key, n => parent.removeChild(n));
for (const node of nodes) parent.insertBefore(node, end);
}

@@ -466,5 +457,3 @@ #unmountSlot(key) {

if (regions) {
for (const {
frame
} of regions.values()) frame?.dispose();
disposeRegions(regions);
this.#slotRegions.delete(key);

@@ -479,11 +468,22 @@ }

}
#refsUnresolved(args) {
const {
host,
id
} = this.#options;
if (host) for (const key in args) {
if (isDataRef(args[key]) && host.resolve(args[key], id) === undefined) return true;
}
return false;
}
#regionsFor(slotKey) {
let regions = this.#slotRegions.get(slotKey);
if (!regions) this.#slotRegions.set(slotKey, regions = new Map());
return regions;
}
#resolveArgs(slotKey, args) {
const host = this.#options.host;
let regions = this.#slotRegions.get(slotKey);
if (!regions) {
regions = new Map();
this.#slotRegions.set(slotKey, regions);
}
const regions = this.#regionsFor(slotKey);
const props = {};
for (const key of Object.keys(args)) {
for (const key in args) {
const value = args[key];

@@ -518,13 +518,4 @@ if (isDataRef(value)) {

if (!start) return;
let regions = this.#slotRegions.get(slotKey);
if (!regions) {
regions = new Map();
this.#slotRegions.set(slotKey, regions);
}
const endData = slotEnd(slotKey);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
collectRegionElements(n, regions);
n = n.nextSibling;
}
const regions = this.#regionsFor(slotKey);
eachInRange(start, slotKey, n => collectRegionElements(n, regions));
}

@@ -545,7 +536,7 @@ #refArgsUnchanged(occurrence, record) {

if (va === vb) continue;
if (!va || !vb || typeof va !== "object" || typeof vb !== "object") return false;
if (typeof va.$frame === "string" && typeof vb.$frame === "string") continue;
if (typeof va.$ref === "string" && typeof vb.$ref === "string" && cache && key in cache) {
if (isFrameRef(va) && isFrameRef(vb)) continue;
if (isDataRef(va) && isDataRef(vb) && cache && key in cache) {
const host = this.#options.host;
const next = host ? host.resolve(vb, this.#options.id) : undefined;
if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;
try {

@@ -566,3 +557,3 @@ if (JSON.stringify(next) === JSON.stringify(cache[key])) continue;

if (!regions) return;
for (const key of Object.keys(args)) {
for (const key in args) {
const value = args[key];

@@ -617,7 +608,4 @@ if (!isFrameRef(value)) continue;

this.#version = undefined;
this.#revealed.clear();
this.#fallbackShown.clear();
for (const key of Object.keys(this.#store)) {
if (key.startsWith("seg:") || key === ":error") delete this.#store[key];
}
this.#appliedRootValue = undefined;
this.#resetStreamState(true);
if (host) host.register(id, this);

@@ -642,7 +630,3 @@ }

for (const key of this.#mountedSlots) this.#removeSlotRecord(key);
for (const regions of this.#slotRegions.values()) {
for (const {
frame
} of regions.values()) frame?.dispose();
}
for (const regions of this.#slotRegions.values()) disposeRegions(regions);
this.#slotRegions.clear();

@@ -669,9 +653,3 @@ this.#mountedSlots.clear();

#clearContent() {
const parent = this.#parent();
let n = this.#firstContent();
while (n && n !== this.#end) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
removeUntil(this.#parent(), this.#firstContent(), this.#end);
}

@@ -693,5 +671,3 @@ #claimContent() {

let ready = true;
for (const entry of assets.styles) {
if (!ensureStylesheet(entry, this.#styleFlush)) ready = false;
}
for (const entry of assets.styles) ready = ensureStylesheet(entry, this.#styleFlush) && ready;
if (!ready) return false;

@@ -710,8 +686,3 @@ }

const parent = tpl.parentNode;
let n = tpl.nextSibling;
while (n && n !== closing) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
removeUntil(parent, tpl.nextSibling, closing);
if (this.#options.reveal) {

@@ -765,5 +736,3 @@ const fallbackFrag = tpl.content.cloneNode(true);

element: el,
get frame() {
return frame;
},
frame,
dispose() {

@@ -788,2 +757,5 @@ frame.dispose();

}
function isAsyncLike(v) {
return !!v && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
}
function propOf(occurrence) {

@@ -794,7 +766,19 @@ const hash = occurrence.indexOf("#");

function isDataRef(value) {
return typeof value === "object" && value !== null && typeof value.$ref === "string";
return !!value && typeof value.$ref === "string";
}
function isFrameRef(value) {
return typeof value === "object" && value !== null && typeof value.$frame === "string";
return !!value && typeof value.$frame === "string";
}
function disposeRegions(regions) {
for (const {
frame
} of regions.values()) frame?.dispose();
}
function removeUntil(parent, n, stop) {
while (n && n !== stop) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
}
function parseFragment(html) {

@@ -806,5 +790,4 @@ const template = document.createElement("template");

function findHeadElement(selector, attr, value) {
const nodes = document.head.querySelectorAll(selector);
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].getAttribute(attr) === value) return nodes[i];
for (const node of document.head.querySelectorAll(selector)) {
if (node.getAttribute(attr) === value) return node;
}

@@ -858,3 +841,3 @@ return null;

function isPlaceholderStart(node, id) {
return node.nodeType === ELEMENT_NODE && node.tagName === "TEMPLATE" && node.getAttribute("id") === id;
return node.tagName === "TEMPLATE" && node.id === id;
}

@@ -915,2 +898,17 @@ function rangeClose(start, id) {

}
function eachInRange(start, key, cb) {
const end = slotEnd(key);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
const next = n.nextSibling;
cb(n);
n = next;
}
return n;
}
function clearStreamRecords(records, root) {
for (const key in records) {
if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
}
}
function argsEquivalent(a, b) {

@@ -926,5 +924,3 @@ if (a === b) return true;

if (va === vb) continue;
if (va && vb && typeof va === "object" && typeof vb === "object" && typeof va.$frame === "string" && va.$frame === vb.$frame) {
continue;
}
if (isFrameRef(va) && va.$frame === vb?.$frame) continue;
return false;

@@ -1292,3 +1288,3 @@ }

const comp = componentFor(functionId);
if ((typeof comp === "function" || typeof comp === "object" && comp !== null) && !comp[COMPONENT_BINDING]) {
if (comp && (typeof comp === "function" || typeof comp === "object") && !comp[COMPONENT_BINDING]) {
comp[COMPONENT_BINDING] = {

@@ -1385,2 +1381,5 @@ component: comp,

function asyncArg(value) {
return value;
}
let sharedHost;

@@ -1462,4 +1461,22 @@ const tables = new Map();

ctx.onUpdate(next => setArgs(() => next));
return slotArgsProxy(args);
}
function isAsyncValue(v) {
return v !== null && typeof v === "object" && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
}
function slotArgsProxy(args) {
const owner = solidJs.getOwner();
const asyncReads = new Map();
return new Proxy({}, {
get: (_, key) => args()[key],
get: (_, key) => {
const v = args()[key];
if (!isAsyncValue(v)) return v;
let read = asyncReads.get(key);
if (!read) {
const make = () => solidJs.createMemo(() => args()[key]);
read = owner ? solidJs.runWithOwner(owner, make) : make();
asyncReads.set(key, read);
}
return read();
},
has: (_, key) => key in args(),

@@ -1482,2 +1499,3 @@ ownKeys: () => Reflect.ownKeys(args()),

const bindings = new Map();
const fillScopes = new Map();
return new Proxy({}, {

@@ -1493,2 +1511,15 @@ get(_, prop) {

}
const prevFill = key !== undefined && fillScopes.get(key);
if (prevFill) {
fillScopes.delete(key);
prevFill.dispose();
}
const fillOwner = streamInvoke ? solidJs.createOwner() : null;
if (fillOwner && key !== undefined && ctx) {
fillScopes.set(key, fillOwner);
ctx.onCleanup(() => {
if (fillScopes.get(key) === fillOwner) fillScopes.delete(key);
fillOwner.dispose();
});
}
const settle = out => {

@@ -1506,3 +1537,3 @@ const existing = ctx && ctx.existing || [];

if (typeof v === "function" && ctx && ctx.invoked) {
return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotProps);
return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotArgsProxy(() => slotProps));
}

@@ -1513,3 +1544,3 @@ return v;

const prefix = ctx && ctx.frame ? `sc-${ctx.frame}-${ctx.key}-` : "";
const value = adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
const value = fillOwner ? solidJs.runWithOwner(fillOwner, () => adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate()) : adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
if (!isReactiveContent(value)) {

@@ -1539,11 +1570,23 @@ return settle(normalizeSlotContent(value));

}
let streamInvoke = false;
function boundaryScope(owner) {
return fn => solidJs.getOwner() ? fn() : solidJs.runWithOwner(owner, fn);
return fn => {
if (solidJs.getOwner()) return fn();
streamInvoke = true;
try {
return solidJs.runWithOwner(owner, fn);
} finally {
streamInvoke = false;
}
};
}
function followBinding(frame, binding) {
solidJs.createRenderEffect(binding, address => frame.rebind(address));
}
function boundaryComponent(host, fnId) {
return (props, binding) => {
const owner = solidJs.getOwner();
const id = binding ? binding() : fnId;
let applied = !tables.has(id);
let release;
const arm = () => new Promise(r => release = r);
const mountGate = applied ? undefined : arm();
let setGate;
const {

@@ -1555,10 +1598,30 @@ element,

host,
id: binding ? binding() : fnId,
id,
slots: slotsFor(props),
ownerScope: boundaryScope(owner),
reveal: revealSeam(owner)
reveal: revealSeam(owner),
onApply: () => {
applied = true;
if (release) {
release();
release = undefined;
}
setGate && setGate(undefined);
}
});
if (binding) followBinding(frame, binding);
const [gatePromise, setGatePromise] = solidJs.createSignal(applied ? undefined : mountGate);
setGate = setGatePromise;
if (binding) {
solidJs.createRenderEffect(binding, (address, prev) => {
if (prev !== undefined && address !== prev && tables.has(address)) {
applied = false;
setGatePromise(arm());
}
frame.rebind(address);
});
}
solidJs.onCleanup(dispose);
return element;
if (applied && !binding) return element;
const gate = solidJs.createMemo(() => gatePromise());
return solidJs.createMemo(() => (gate(), element));
};

@@ -1631,2 +1694,13 @@ }

const appliedRecords = new Set();
const claimedFragments = new Set();
const claimRegionFragments = root => {
const fr = globalThis._$HY?.fr;
if (!fr || !fr.claim) return;
root.querySelectorAll('template[id^="pl-"]').forEach(tpl => {
const fragId = tpl.id.slice(3);
if (claimedFragments.has(fragId)) return;
claimedFragments.add(fragId);
fr.claim(fragId);
});
};
const drainRecords = () => {

@@ -1663,4 +1737,16 @@ const hy = globalThis._$HY;

};
claimRegionFragments(el);
const fr = globalThis._$HY?.fr;
const unsubscribe = fr ? fr.subscribe((_fragId, parent) => {
if (fr.claim && parent && el.contains(parent)) claimRegionFragments(parent);
drainRecords();
}) : undefined;
solidJs.onCleanup(() => {
unsubscribe && unsubscribe();
if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);
});
drainRecords();
const owner = solidJs.getOwner();
let release;
let setGate;
const frame = createFrame(el, {

@@ -1673,2 +1759,9 @@ adopt: true,

reveal: revealSeam(owner),
onApply: () => {
if (release) {
release();
release = undefined;
}
setGate && setGate(undefined);
},
...{

@@ -1684,3 +1777,15 @@ recordsPending: () => {

});
if (binding) followBinding(frame, binding);
if (binding) {
const arm = () => new Promise(r => release = r);
const [gatePromise, setGatePromise] = solidJs.createSignal(undefined);
setGate = setGatePromise;
const gate = solidJs.createMemo(() => gatePromise());
solidJs.createRenderEffect(binding, (address, prev) => {
if (prev !== undefined && address !== prev && tables.has(address)) {
setGatePromise(arm());
}
frame.rebind(address);
});
solidJs.createRenderEffect(() => (gate(), undefined), () => {});
}
solidJs.onCleanup(() => frame.dispose());

@@ -1729,6 +1834,6 @@ return el;

exports.applyFrameResponse = applyFrameResponse;
exports.asyncArg = asyncArg;
exports.createFrame = createFrame;
exports.createFrameElement = createFrameElement;
exports.createFrameHost = createFrameHost;
exports.createJSONDataTable = createJSONDataTable;
exports.createServerComponentHandler = createServerComponentHandler;

@@ -1735,0 +1840,0 @@ exports.getFrameHost = getFrameHost;

@@ -99,5 +99,3 @@ 'use strict';

store.version = version;
for (const key of Object.keys(store.records)) {
if (key.startsWith("seg:") || key === ":error") delete store.records[key];
}
clearStreamRecords(store.records);
}

@@ -180,2 +178,3 @@ Object.assign(store.records, records);

#hasContent = false;
#errorNotified = false;
#revealed = new Set();

@@ -200,6 +199,8 @@ #fallbackShown = new Set();

if (!handlers) return;
const run = () => direct ? claimNode(handlers, node) : claimTree(handlers, node);
this.#scoped(() => direct ? claimNode(handlers, node) : claimTree(handlers, node));
};
#scoped(fn) {
const scope = this.#options.ownerScope;
scope ? scope(run) : run();
};
return scope ? scope(fn) : fn();
}
constructor(element, start, end, options = {}) {

@@ -265,11 +266,5 @@ this.#element = element;

this.#version = v;
this.#revealed.clear();
this.#fallbackShown.clear();
for (const key of Object.keys(this.#store)) {
if (key.startsWith("seg:") || key === ":error") {
delete this.#store[key];
}
}
this.#resetStreamState();
}
for (const key of Object.keys(write.r)) {
for (const key in write.r) {
const incoming = write.r[key];

@@ -286,2 +281,8 @@ if (incoming && incoming.kind === "slot" && key.charCodeAt(0) === 115 && key.startsWith("slot:")) {

}
#resetStreamState(root) {
this.#revealed.clear();
this.#fallbackShown.clear();
this.#errorNotified = false;
clearStreamRecords(this.#store, root);
}
#flush() {

@@ -297,6 +298,10 @@ if (this.#disposed) return;

}
if (this.#store[":error"] && !this.#errorNotified) {
this.#errorNotified = true;
this.#applied(version, "error");
}
let progressed = true;
while (progressed) {
progressed = false;
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
const name = segmentName(key);

@@ -310,3 +315,3 @@ if (name === null || this.#revealed.has(name)) continue;

}
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
const m = /^seg:([^:]+):fallback$/.exec(key);

@@ -323,3 +328,3 @@ if (!m) continue;

}
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;

@@ -356,2 +361,3 @@ this.#processedAssets.add(key);

const record = this.#resolveSlotRecord(occurrence);
if (record && record.kind === "slot" && this.#refsUnresolved(record.args)) continue;
const prev = this.#slotNodes.get(occurrence);

@@ -412,11 +418,3 @@ const prevFirst = Array.isArray(prev) ? prev[0] : prev;

let end = null;
if (start) {
const endData = slotEnd(occurrence);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
existing.push(n);
n = n.nextSibling;
}
end = n;
}
if (start) end = eachInRange(start, occurrence, n => existing.push(n));
const ctx = {

@@ -436,4 +434,3 @@ frame: this.#options.claimScope ?? this.#options.id,

const props = record && record.kind === "slot" ? this.#resolveArgs(occurrence, record.args) : {};
const scope = this.#options.ownerScope;
const content = scope ? scope(() => callback(props, ctx)) : callback(props, ctx);
const content = this.#scoped(() => callback(props, ctx));
this.#slotArgs.set(occurrence, record);

@@ -445,11 +442,5 @@ if (cleanups.length) this.#slotCleanups.set(occurrence, cleanups);

#replaceRange(key, start, nodes) {
const end = slotEnd(key);
const parent = start.parentNode;
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
for (const node of nodes) parent.insertBefore(node, n);
const end = eachInRange(start, key, n => parent.removeChild(n));
for (const node of nodes) parent.insertBefore(node, end);
}

@@ -466,5 +457,3 @@ #unmountSlot(key) {

if (regions) {
for (const {
frame
} of regions.values()) frame?.dispose();
disposeRegions(regions);
this.#slotRegions.delete(key);

@@ -479,11 +468,22 @@ }

}
#refsUnresolved(args) {
const {
host,
id
} = this.#options;
if (host) for (const key in args) {
if (isDataRef(args[key]) && host.resolve(args[key], id) === undefined) return true;
}
return false;
}
#regionsFor(slotKey) {
let regions = this.#slotRegions.get(slotKey);
if (!regions) this.#slotRegions.set(slotKey, regions = new Map());
return regions;
}
#resolveArgs(slotKey, args) {
const host = this.#options.host;
let regions = this.#slotRegions.get(slotKey);
if (!regions) {
regions = new Map();
this.#slotRegions.set(slotKey, regions);
}
const regions = this.#regionsFor(slotKey);
const props = {};
for (const key of Object.keys(args)) {
for (const key in args) {
const value = args[key];

@@ -518,13 +518,4 @@ if (isDataRef(value)) {

if (!start) return;
let regions = this.#slotRegions.get(slotKey);
if (!regions) {
regions = new Map();
this.#slotRegions.set(slotKey, regions);
}
const endData = slotEnd(slotKey);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
collectRegionElements(n, regions);
n = n.nextSibling;
}
const regions = this.#regionsFor(slotKey);
eachInRange(start, slotKey, n => collectRegionElements(n, regions));
}

@@ -545,7 +536,7 @@ #refArgsUnchanged(occurrence, record) {

if (va === vb) continue;
if (!va || !vb || typeof va !== "object" || typeof vb !== "object") return false;
if (typeof va.$frame === "string" && typeof vb.$frame === "string") continue;
if (typeof va.$ref === "string" && typeof vb.$ref === "string" && cache && key in cache) {
if (isFrameRef(va) && isFrameRef(vb)) continue;
if (isDataRef(va) && isDataRef(vb) && cache && key in cache) {
const host = this.#options.host;
const next = host ? host.resolve(vb, this.#options.id) : undefined;
if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;
try {

@@ -566,3 +557,3 @@ if (JSON.stringify(next) === JSON.stringify(cache[key])) continue;

if (!regions) return;
for (const key of Object.keys(args)) {
for (const key in args) {
const value = args[key];

@@ -617,7 +608,4 @@ if (!isFrameRef(value)) continue;

this.#version = undefined;
this.#revealed.clear();
this.#fallbackShown.clear();
for (const key of Object.keys(this.#store)) {
if (key.startsWith("seg:") || key === ":error") delete this.#store[key];
}
this.#appliedRootValue = undefined;
this.#resetStreamState(true);
if (host) host.register(id, this);

@@ -642,7 +630,3 @@ }

for (const key of this.#mountedSlots) this.#removeSlotRecord(key);
for (const regions of this.#slotRegions.values()) {
for (const {
frame
} of regions.values()) frame?.dispose();
}
for (const regions of this.#slotRegions.values()) disposeRegions(regions);
this.#slotRegions.clear();

@@ -669,9 +653,3 @@ this.#mountedSlots.clear();

#clearContent() {
const parent = this.#parent();
let n = this.#firstContent();
while (n && n !== this.#end) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
removeUntil(this.#parent(), this.#firstContent(), this.#end);
}

@@ -693,5 +671,3 @@ #claimContent() {

let ready = true;
for (const entry of assets.styles) {
if (!ensureStylesheet(entry, this.#styleFlush)) ready = false;
}
for (const entry of assets.styles) ready = ensureStylesheet(entry, this.#styleFlush) && ready;
if (!ready) return false;

@@ -713,8 +689,3 @@ }

const parent = tpl.parentNode;
let n = tpl.nextSibling;
while (n && n !== closing) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
removeUntil(parent, tpl.nextSibling, closing);
if (this.#options.reveal) {

@@ -768,5 +739,3 @@ const fallbackFrag = tpl.content.cloneNode(true);

element: el,
get frame() {
return frame;
},
frame,
dispose() {

@@ -791,2 +760,5 @@ frame.dispose();

}
function isAsyncLike(v) {
return !!v && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
}
function propOf(occurrence) {

@@ -797,7 +769,19 @@ const hash = occurrence.indexOf("#");

function isDataRef(value) {
return typeof value === "object" && value !== null && typeof value.$ref === "string";
return !!value && typeof value.$ref === "string";
}
function isFrameRef(value) {
return typeof value === "object" && value !== null && typeof value.$frame === "string";
return !!value && typeof value.$frame === "string";
}
function disposeRegions(regions) {
for (const {
frame
} of regions.values()) frame?.dispose();
}
function removeUntil(parent, n, stop) {
while (n && n !== stop) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
}
function parseFragment(html) {

@@ -809,5 +793,4 @@ const template = document.createElement("template");

function findHeadElement(selector, attr, value) {
const nodes = document.head.querySelectorAll(selector);
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].getAttribute(attr) === value) return nodes[i];
for (const node of document.head.querySelectorAll(selector)) {
if (node.getAttribute(attr) === value) return node;
}

@@ -861,3 +844,3 @@ return null;

function isPlaceholderStart(node, id) {
return node.nodeType === ELEMENT_NODE && node.tagName === "TEMPLATE" && node.getAttribute("id") === id;
return node.tagName === "TEMPLATE" && node.id === id;
}

@@ -919,2 +902,17 @@ function rangeClose(start, id) {

}
function eachInRange(start, key, cb) {
const end = slotEnd(key);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
const next = n.nextSibling;
cb(n);
n = next;
}
return n;
}
function clearStreamRecords(records, root) {
for (const key in records) {
if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
}
}
function argsEquivalent(a, b) {

@@ -930,5 +928,3 @@ if (a === b) return true;

if (va === vb) continue;
if (va && vb && typeof va === "object" && typeof vb === "object" && typeof va.$frame === "string" && va.$frame === vb.$frame) {
continue;
}
if (isFrameRef(va) && va.$frame === vb?.$frame) continue;
return false;

@@ -1305,3 +1301,3 @@ }

const comp = componentFor(functionId);
if ((typeof comp === "function" || typeof comp === "object" && comp !== null) && !comp[COMPONENT_BINDING]) {
if (comp && (typeof comp === "function" || typeof comp === "object") && !comp[COMPONENT_BINDING]) {
comp[COMPONENT_BINDING] = {

@@ -1398,2 +1394,5 @@ component: comp,

function asyncArg(value) {
return value;
}
let sharedHost;

@@ -1475,4 +1474,22 @@ const tables = new Map();

ctx.onUpdate(next => setArgs(() => next));
return slotArgsProxy(args);
}
function isAsyncValue(v) {
return v !== null && typeof v === "object" && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
}
function slotArgsProxy(args) {
const owner = solidJs.getOwner();
const asyncReads = new Map();
return new Proxy({}, {
get: (_, key) => args()[key],
get: (_, key) => {
const v = args()[key];
if (!isAsyncValue(v)) return v;
let read = asyncReads.get(key);
if (!read) {
const make = () => solidJs.createMemo(() => args()[key]);
read = owner ? solidJs.runWithOwner(owner, make) : make();
asyncReads.set(key, read);
}
return read();
},
has: (_, key) => key in args(),

@@ -1495,2 +1512,3 @@ ownKeys: () => Reflect.ownKeys(args()),

const bindings = new Map();
const fillScopes = new Map();
return new Proxy({}, {

@@ -1506,2 +1524,15 @@ get(_, prop) {

}
const prevFill = key !== undefined && fillScopes.get(key);
if (prevFill) {
fillScopes.delete(key);
prevFill.dispose();
}
const fillOwner = streamInvoke ? solidJs.createOwner() : null;
if (fillOwner && key !== undefined && ctx) {
fillScopes.set(key, fillOwner);
ctx.onCleanup(() => {
if (fillScopes.get(key) === fillOwner) fillScopes.delete(key);
fillOwner.dispose();
});
}
const settle = out => {

@@ -1519,3 +1550,3 @@ const existing = ctx && ctx.existing || [];

if (typeof v === "function" && ctx && ctx.invoked) {
return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotProps);
return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotArgsProxy(() => slotProps));
}

@@ -1526,3 +1557,3 @@ return v;

const prefix = ctx && ctx.frame ? `sc-${ctx.frame}-${ctx.key}-` : "";
const value = adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
const value = fillOwner ? solidJs.runWithOwner(fillOwner, () => adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate()) : adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
if (!isReactiveContent(value)) {

@@ -1552,11 +1583,23 @@ return settle(normalizeSlotContent(value));

}
let streamInvoke = false;
function boundaryScope(owner) {
return fn => solidJs.getOwner() ? fn() : solidJs.runWithOwner(owner, fn);
return fn => {
if (solidJs.getOwner()) return fn();
streamInvoke = true;
try {
return solidJs.runWithOwner(owner, fn);
} finally {
streamInvoke = false;
}
};
}
function followBinding(frame, binding) {
solidJs.createRenderEffect(binding, address => frame.rebind(address));
}
function boundaryComponent(host, fnId) {
return (props, binding) => {
const owner = solidJs.getOwner();
const id = binding ? binding() : fnId;
let applied = !tables.has(id);
let release;
const arm = () => new Promise(r => release = r);
const mountGate = applied ? undefined : arm();
let setGate;
const {

@@ -1568,10 +1611,30 @@ element,

host,
id: binding ? binding() : fnId,
id,
slots: slotsFor(props),
ownerScope: boundaryScope(owner),
reveal: revealSeam(owner)
reveal: revealSeam(owner),
onApply: () => {
applied = true;
if (release) {
release();
release = undefined;
}
setGate && setGate(undefined);
}
});
if (binding) followBinding(frame, binding);
const [gatePromise, setGatePromise] = solidJs.createSignal(applied ? undefined : mountGate);
setGate = setGatePromise;
if (binding) {
solidJs.createRenderEffect(binding, (address, prev) => {
if (prev !== undefined && address !== prev && tables.has(address)) {
applied = false;
setGatePromise(arm());
}
frame.rebind(address);
});
}
solidJs.onCleanup(dispose);
return element;
if (applied && !binding) return element;
const gate = solidJs.createMemo(() => gatePromise());
return solidJs.createMemo(() => (gate(), element));
};

@@ -1644,2 +1707,13 @@ }

const appliedRecords = new Set();
const claimedFragments = new Set();
const claimRegionFragments = root => {
const fr = globalThis._$HY?.fr;
if (!fr || !fr.claim) return;
root.querySelectorAll('template[id^="pl-"]').forEach(tpl => {
const fragId = tpl.id.slice(3);
if (claimedFragments.has(fragId)) return;
claimedFragments.add(fragId);
fr.claim(fragId);
});
};
const drainRecords = () => {

@@ -1676,4 +1750,16 @@ const hy = globalThis._$HY;

};
claimRegionFragments(el);
const fr = globalThis._$HY?.fr;
const unsubscribe = fr ? fr.subscribe((_fragId, parent) => {
if (fr.claim && parent && el.contains(parent)) claimRegionFragments(parent);
drainRecords();
}) : undefined;
solidJs.onCleanup(() => {
unsubscribe && unsubscribe();
if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);
});
drainRecords();
const owner = solidJs.getOwner();
let release;
let setGate;
const frame = createFrame(el, {

@@ -1686,2 +1772,9 @@ adopt: true,

reveal: revealSeam(owner),
onApply: () => {
if (release) {
release();
release = undefined;
}
setGate && setGate(undefined);
},
...{

@@ -1697,3 +1790,15 @@ recordsPending: () => {

});
if (binding) followBinding(frame, binding);
if (binding) {
const arm = () => new Promise(r => release = r);
const [gatePromise, setGatePromise] = solidJs.createSignal(undefined);
setGate = setGatePromise;
const gate = solidJs.createMemo(() => gatePromise());
solidJs.createRenderEffect(binding, (address, prev) => {
if (prev !== undefined && address !== prev && tables.has(address)) {
setGatePromise(arm());
}
frame.rebind(address);
});
solidJs.createRenderEffect(() => (gate(), undefined), () => {});
}
solidJs.onCleanup(() => frame.dispose());

@@ -1742,6 +1847,6 @@ return el;

exports.applyFrameResponse = applyFrameResponse;
exports.asyncArg = asyncArg;
exports.createFrame = createFrame;
exports.createFrameElement = createFrameElement;
exports.createFrameHost = createFrameHost;
exports.createJSONDataTable = createJSONDataTable;
exports.createServerComponentHandler = createServerComponentHandler;

@@ -1748,0 +1853,0 @@ exports.getFrameHost = getFrameHost;

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

import { getOwner, onCleanup, createMemo, runWithOwner, createLoadingBoundary, createOwner, createRenderEffect, sharedConfig, createSignal } from 'solid-js';
import { getOwner, onCleanup, createMemo, runWithOwner, createSignal, createRenderEffect, createLoadingBoundary, createOwner, sharedConfig } from 'solid-js';
import { insert } from '@solidjs/web';

@@ -97,5 +97,3 @@ import { createPlugin, fromCrossJSON, Feature } from 'seroval';

store.version = version;
for (const key of Object.keys(store.records)) {
if (key.startsWith("seg:") || key === ":error") delete store.records[key];
}
clearStreamRecords(store.records);
}

@@ -178,2 +176,3 @@ Object.assign(store.records, records);

#hasContent = false;
#errorNotified = false;
#revealed = new Set();

@@ -198,6 +197,8 @@ #fallbackShown = new Set();

if (!handlers) return;
const run = () => direct ? claimNode(handlers, node) : claimTree(handlers, node);
this.#scoped(() => direct ? claimNode(handlers, node) : claimTree(handlers, node));
};
#scoped(fn) {
const scope = this.#options.ownerScope;
scope ? scope(run) : run();
};
return scope ? scope(fn) : fn();
}
constructor(element, start, end, options = {}) {

@@ -263,11 +264,5 @@ this.#element = element;

this.#version = v;
this.#revealed.clear();
this.#fallbackShown.clear();
for (const key of Object.keys(this.#store)) {
if (key.startsWith("seg:") || key === ":error") {
delete this.#store[key];
}
}
this.#resetStreamState();
}
for (const key of Object.keys(write.r)) {
for (const key in write.r) {
const incoming = write.r[key];

@@ -284,2 +279,8 @@ if (incoming && incoming.kind === "slot" && key.charCodeAt(0) === 115 && key.startsWith("slot:")) {

}
#resetStreamState(root) {
this.#revealed.clear();
this.#fallbackShown.clear();
this.#errorNotified = false;
clearStreamRecords(this.#store, root);
}
#flush() {

@@ -295,6 +296,10 @@ if (this.#disposed) return;

}
if (this.#store[":error"] && !this.#errorNotified) {
this.#errorNotified = true;
this.#applied(version, "error");
}
let progressed = true;
while (progressed) {
progressed = false;
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
const name = segmentName(key);

@@ -308,3 +313,3 @@ if (name === null || this.#revealed.has(name)) continue;

}
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
const m = /^seg:([^:]+):fallback$/.exec(key);

@@ -321,3 +326,3 @@ if (!m) continue;

}
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;

@@ -354,2 +359,3 @@ this.#processedAssets.add(key);

const record = this.#resolveSlotRecord(occurrence);
if (record && record.kind === "slot" && this.#refsUnresolved(record.args)) continue;
const prev = this.#slotNodes.get(occurrence);

@@ -410,11 +416,3 @@ const prevFirst = Array.isArray(prev) ? prev[0] : prev;

let end = null;
if (start) {
const endData = slotEnd(occurrence);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
existing.push(n);
n = n.nextSibling;
}
end = n;
}
if (start) end = eachInRange(start, occurrence, n => existing.push(n));
const ctx = {

@@ -434,4 +432,3 @@ frame: this.#options.claimScope ?? this.#options.id,

const props = record && record.kind === "slot" ? this.#resolveArgs(occurrence, record.args) : {};
const scope = this.#options.ownerScope;
const content = scope ? scope(() => callback(props, ctx)) : callback(props, ctx);
const content = this.#scoped(() => callback(props, ctx));
this.#slotArgs.set(occurrence, record);

@@ -443,11 +440,5 @@ if (cleanups.length) this.#slotCleanups.set(occurrence, cleanups);

#replaceRange(key, start, nodes) {
const end = slotEnd(key);
const parent = start.parentNode;
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
for (const node of nodes) parent.insertBefore(node, n);
const end = eachInRange(start, key, n => parent.removeChild(n));
for (const node of nodes) parent.insertBefore(node, end);
}

@@ -464,5 +455,3 @@ #unmountSlot(key) {

if (regions) {
for (const {
frame
} of regions.values()) frame?.dispose();
disposeRegions(regions);
this.#slotRegions.delete(key);

@@ -477,11 +466,22 @@ }

}
#refsUnresolved(args) {
const {
host,
id
} = this.#options;
if (host) for (const key in args) {
if (isDataRef(args[key]) && host.resolve(args[key], id) === undefined) return true;
}
return false;
}
#regionsFor(slotKey) {
let regions = this.#slotRegions.get(slotKey);
if (!regions) this.#slotRegions.set(slotKey, regions = new Map());
return regions;
}
#resolveArgs(slotKey, args) {
const host = this.#options.host;
let regions = this.#slotRegions.get(slotKey);
if (!regions) {
regions = new Map();
this.#slotRegions.set(slotKey, regions);
}
const regions = this.#regionsFor(slotKey);
const props = {};
for (const key of Object.keys(args)) {
for (const key in args) {
const value = args[key];

@@ -516,13 +516,4 @@ if (isDataRef(value)) {

if (!start) return;
let regions = this.#slotRegions.get(slotKey);
if (!regions) {
regions = new Map();
this.#slotRegions.set(slotKey, regions);
}
const endData = slotEnd(slotKey);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
collectRegionElements(n, regions);
n = n.nextSibling;
}
const regions = this.#regionsFor(slotKey);
eachInRange(start, slotKey, n => collectRegionElements(n, regions));
}

@@ -543,7 +534,7 @@ #refArgsUnchanged(occurrence, record) {

if (va === vb) continue;
if (!va || !vb || typeof va !== "object" || typeof vb !== "object") return false;
if (typeof va.$frame === "string" && typeof vb.$frame === "string") continue;
if (typeof va.$ref === "string" && typeof vb.$ref === "string" && cache && key in cache) {
if (isFrameRef(va) && isFrameRef(vb)) continue;
if (isDataRef(va) && isDataRef(vb) && cache && key in cache) {
const host = this.#options.host;
const next = host ? host.resolve(vb, this.#options.id) : undefined;
if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;
try {

@@ -564,3 +555,3 @@ if (JSON.stringify(next) === JSON.stringify(cache[key])) continue;

if (!regions) return;
for (const key of Object.keys(args)) {
for (const key in args) {
const value = args[key];

@@ -615,7 +606,4 @@ if (!isFrameRef(value)) continue;

this.#version = undefined;
this.#revealed.clear();
this.#fallbackShown.clear();
for (const key of Object.keys(this.#store)) {
if (key.startsWith("seg:") || key === ":error") delete this.#store[key];
}
this.#appliedRootValue = undefined;
this.#resetStreamState(true);
if (host) host.register(id, this);

@@ -640,7 +628,3 @@ }

for (const key of this.#mountedSlots) this.#removeSlotRecord(key);
for (const regions of this.#slotRegions.values()) {
for (const {
frame
} of regions.values()) frame?.dispose();
}
for (const regions of this.#slotRegions.values()) disposeRegions(regions);
this.#slotRegions.clear();

@@ -667,9 +651,3 @@ this.#mountedSlots.clear();

#clearContent() {
const parent = this.#parent();
let n = this.#firstContent();
while (n && n !== this.#end) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
removeUntil(this.#parent(), this.#firstContent(), this.#end);
}

@@ -691,5 +669,3 @@ #claimContent() {

let ready = true;
for (const entry of assets.styles) {
if (!ensureStylesheet(entry, this.#styleFlush)) ready = false;
}
for (const entry of assets.styles) ready = ensureStylesheet(entry, this.#styleFlush) && ready;
if (!ready) return false;

@@ -711,8 +687,3 @@ }

const parent = tpl.parentNode;
let n = tpl.nextSibling;
while (n && n !== closing) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
removeUntil(parent, tpl.nextSibling, closing);
if (this.#options.reveal) {

@@ -766,5 +737,3 @@ const fallbackFrag = tpl.content.cloneNode(true);

element: el,
get frame() {
return frame;
},
frame,
dispose() {

@@ -789,2 +758,5 @@ frame.dispose();

}
function isAsyncLike(v) {
return !!v && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
}
function propOf(occurrence) {

@@ -795,7 +767,19 @@ const hash = occurrence.indexOf("#");

function isDataRef(value) {
return typeof value === "object" && value !== null && typeof value.$ref === "string";
return !!value && typeof value.$ref === "string";
}
function isFrameRef(value) {
return typeof value === "object" && value !== null && typeof value.$frame === "string";
return !!value && typeof value.$frame === "string";
}
function disposeRegions(regions) {
for (const {
frame
} of regions.values()) frame?.dispose();
}
function removeUntil(parent, n, stop) {
while (n && n !== stop) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
}
function parseFragment(html) {

@@ -807,5 +791,4 @@ const template = document.createElement("template");

function findHeadElement(selector, attr, value) {
const nodes = document.head.querySelectorAll(selector);
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].getAttribute(attr) === value) return nodes[i];
for (const node of document.head.querySelectorAll(selector)) {
if (node.getAttribute(attr) === value) return node;
}

@@ -859,3 +842,3 @@ return null;

function isPlaceholderStart(node, id) {
return node.nodeType === ELEMENT_NODE && node.tagName === "TEMPLATE" && node.getAttribute("id") === id;
return node.tagName === "TEMPLATE" && node.id === id;
}

@@ -917,2 +900,17 @@ function rangeClose(start, id) {

}
function eachInRange(start, key, cb) {
const end = slotEnd(key);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
const next = n.nextSibling;
cb(n);
n = next;
}
return n;
}
function clearStreamRecords(records, root) {
for (const key in records) {
if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
}
}
function argsEquivalent(a, b) {

@@ -928,5 +926,3 @@ if (a === b) return true;

if (va === vb) continue;
if (va && vb && typeof va === "object" && typeof vb === "object" && typeof va.$frame === "string" && va.$frame === vb.$frame) {
continue;
}
if (isFrameRef(va) && va.$frame === vb?.$frame) continue;
return false;

@@ -1303,3 +1299,3 @@ }

const comp = componentFor(functionId);
if ((typeof comp === "function" || typeof comp === "object" && comp !== null) && !comp[COMPONENT_BINDING]) {
if (comp && (typeof comp === "function" || typeof comp === "object") && !comp[COMPONENT_BINDING]) {
comp[COMPONENT_BINDING] = {

@@ -1396,2 +1392,5 @@ component: comp,

function asyncArg(value) {
return value;
}
let sharedHost;

@@ -1473,4 +1472,22 @@ const tables = new Map();

ctx.onUpdate(next => setArgs(() => next));
return slotArgsProxy(args);
}
function isAsyncValue(v) {
return v !== null && typeof v === "object" && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
}
function slotArgsProxy(args) {
const owner = getOwner();
const asyncReads = new Map();
return new Proxy({}, {
get: (_, key) => args()[key],
get: (_, key) => {
const v = args()[key];
if (!isAsyncValue(v)) return v;
let read = asyncReads.get(key);
if (!read) {
const make = () => createMemo(() => args()[key]);
read = owner ? runWithOwner(owner, make) : make();
asyncReads.set(key, read);
}
return read();
},
has: (_, key) => key in args(),

@@ -1493,2 +1510,3 @@ ownKeys: () => Reflect.ownKeys(args()),

const bindings = new Map();
const fillScopes = new Map();
return new Proxy({}, {

@@ -1504,2 +1522,15 @@ get(_, prop) {

}
const prevFill = key !== undefined && fillScopes.get(key);
if (prevFill) {
fillScopes.delete(key);
prevFill.dispose();
}
const fillOwner = streamInvoke ? createOwner() : null;
if (fillOwner && key !== undefined && ctx) {
fillScopes.set(key, fillOwner);
ctx.onCleanup(() => {
if (fillScopes.get(key) === fillOwner) fillScopes.delete(key);
fillOwner.dispose();
});
}
const settle = out => {

@@ -1517,3 +1548,3 @@ const existing = ctx && ctx.existing || [];

if (typeof v === "function" && ctx && ctx.invoked) {
return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotProps);
return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotArgsProxy(() => slotProps));
}

@@ -1524,3 +1555,3 @@ return v;

const prefix = ctx && ctx.frame ? `sc-${ctx.frame}-${ctx.key}-` : "";
const value = adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
const value = fillOwner ? runWithOwner(fillOwner, () => adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate()) : adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
if (!isReactiveContent(value)) {

@@ -1550,11 +1581,23 @@ return settle(normalizeSlotContent(value));

}
let streamInvoke = false;
function boundaryScope(owner) {
return fn => getOwner() ? fn() : runWithOwner(owner, fn);
return fn => {
if (getOwner()) return fn();
streamInvoke = true;
try {
return runWithOwner(owner, fn);
} finally {
streamInvoke = false;
}
};
}
function followBinding(frame, binding) {
createRenderEffect(binding, address => frame.rebind(address));
}
function boundaryComponent(host, fnId) {
return (props, binding) => {
const owner = getOwner();
const id = binding ? binding() : fnId;
let applied = !tables.has(id);
let release;
const arm = () => new Promise(r => release = r);
const mountGate = applied ? undefined : arm();
let setGate;
const {

@@ -1566,10 +1609,30 @@ element,

host,
id: binding ? binding() : fnId,
id,
slots: slotsFor(props),
ownerScope: boundaryScope(owner),
reveal: revealSeam(owner)
reveal: revealSeam(owner),
onApply: () => {
applied = true;
if (release) {
release();
release = undefined;
}
setGate && setGate(undefined);
}
});
if (binding) followBinding(frame, binding);
const [gatePromise, setGatePromise] = createSignal(applied ? undefined : mountGate);
setGate = setGatePromise;
if (binding) {
createRenderEffect(binding, (address, prev) => {
if (prev !== undefined && address !== prev && tables.has(address)) {
applied = false;
setGatePromise(arm());
}
frame.rebind(address);
});
}
onCleanup(dispose);
return element;
if (applied && !binding) return element;
const gate = createMemo(() => gatePromise());
return createMemo(() => (gate(), element));
};

@@ -1642,2 +1705,13 @@ }

const appliedRecords = new Set();
const claimedFragments = new Set();
const claimRegionFragments = root => {
const fr = globalThis._$HY?.fr;
if (!fr || !fr.claim) return;
root.querySelectorAll('template[id^="pl-"]').forEach(tpl => {
const fragId = tpl.id.slice(3);
if (claimedFragments.has(fragId)) return;
claimedFragments.add(fragId);
fr.claim(fragId);
});
};
const drainRecords = () => {

@@ -1674,4 +1748,16 @@ const hy = globalThis._$HY;

};
claimRegionFragments(el);
const fr = globalThis._$HY?.fr;
const unsubscribe = fr ? fr.subscribe((_fragId, parent) => {
if (fr.claim && parent && el.contains(parent)) claimRegionFragments(parent);
drainRecords();
}) : undefined;
onCleanup(() => {
unsubscribe && unsubscribe();
if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);
});
drainRecords();
const owner = getOwner();
let release;
let setGate;
const frame = createFrame(el, {

@@ -1684,2 +1770,9 @@ adopt: true,

reveal: revealSeam(owner),
onApply: () => {
if (release) {
release();
release = undefined;
}
setGate && setGate(undefined);
},
...{

@@ -1695,3 +1788,15 @@ recordsPending: () => {

});
if (binding) followBinding(frame, binding);
if (binding) {
const arm = () => new Promise(r => release = r);
const [gatePromise, setGatePromise] = createSignal(undefined);
setGate = setGatePromise;
const gate = createMemo(() => gatePromise());
createRenderEffect(binding, (address, prev) => {
if (prev !== undefined && address !== prev && tables.has(address)) {
setGatePromise(arm());
}
frame.rebind(address);
});
createRenderEffect(() => (gate(), undefined), () => {});
}
onCleanup(() => frame.dispose());

@@ -1737,2 +1842,2 @@ return el;

export { FRAME_APPLIED_EVENT, FRAME_STREAM_HEADER, applyFrameResponse, createFrame, createFrameElement, createFrameHost, createJSONDataTable, createServerComponentHandler, getFrameHost, installServerComponents, isFrameStreamResponse };
export { FRAME_APPLIED_EVENT, FRAME_STREAM_HEADER, applyFrameResponse, asyncArg, createFrame, createFrameElement, createFrameHost, createServerComponentHandler, getFrameHost, installServerComponents, isFrameStreamResponse };

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

import { getOwner, onCleanup, createMemo, runWithOwner, createLoadingBoundary, createOwner, createRenderEffect, sharedConfig, createSignal } from 'solid-js';
import { getOwner, onCleanup, createMemo, runWithOwner, createSignal, createRenderEffect, createLoadingBoundary, createOwner, sharedConfig } from 'solid-js';
import { insert } from '@solidjs/web';

@@ -97,5 +97,3 @@ import { createPlugin, fromCrossJSON, Feature } from 'seroval';

store.version = version;
for (const key of Object.keys(store.records)) {
if (key.startsWith("seg:") || key === ":error") delete store.records[key];
}
clearStreamRecords(store.records);
}

@@ -178,2 +176,3 @@ Object.assign(store.records, records);

#hasContent = false;
#errorNotified = false;
#revealed = new Set();

@@ -198,6 +197,8 @@ #fallbackShown = new Set();

if (!handlers) return;
const run = () => direct ? claimNode(handlers, node) : claimTree(handlers, node);
this.#scoped(() => direct ? claimNode(handlers, node) : claimTree(handlers, node));
};
#scoped(fn) {
const scope = this.#options.ownerScope;
scope ? scope(run) : run();
};
return scope ? scope(fn) : fn();
}
constructor(element, start, end, options = {}) {

@@ -263,11 +264,5 @@ this.#element = element;

this.#version = v;
this.#revealed.clear();
this.#fallbackShown.clear();
for (const key of Object.keys(this.#store)) {
if (key.startsWith("seg:") || key === ":error") {
delete this.#store[key];
}
}
this.#resetStreamState();
}
for (const key of Object.keys(write.r)) {
for (const key in write.r) {
const incoming = write.r[key];

@@ -284,2 +279,8 @@ if (incoming && incoming.kind === "slot" && key.charCodeAt(0) === 115 && key.startsWith("slot:")) {

}
#resetStreamState(root) {
this.#revealed.clear();
this.#fallbackShown.clear();
this.#errorNotified = false;
clearStreamRecords(this.#store, root);
}
#flush() {

@@ -295,6 +296,10 @@ if (this.#disposed) return;

}
if (this.#store[":error"] && !this.#errorNotified) {
this.#errorNotified = true;
this.#applied(version, "error");
}
let progressed = true;
while (progressed) {
progressed = false;
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
const name = segmentName(key);

@@ -308,3 +313,3 @@ if (name === null || this.#revealed.has(name)) continue;

}
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
const m = /^seg:([^:]+):fallback$/.exec(key);

@@ -321,3 +326,3 @@ if (!m) continue;

}
for (const key of Object.keys(this.#store)) {
for (const key in this.#store) {
if (this.#processedAssets.has(key) || !key.endsWith(":assets")) continue;

@@ -354,2 +359,3 @@ this.#processedAssets.add(key);

const record = this.#resolveSlotRecord(occurrence);
if (record && record.kind === "slot" && this.#refsUnresolved(record.args)) continue;
const prev = this.#slotNodes.get(occurrence);

@@ -410,11 +416,3 @@ const prevFirst = Array.isArray(prev) ? prev[0] : prev;

let end = null;
if (start) {
const endData = slotEnd(occurrence);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
existing.push(n);
n = n.nextSibling;
}
end = n;
}
if (start) end = eachInRange(start, occurrence, n => existing.push(n));
const ctx = {

@@ -434,4 +432,3 @@ frame: this.#options.claimScope ?? this.#options.id,

const props = record && record.kind === "slot" ? this.#resolveArgs(occurrence, record.args) : {};
const scope = this.#options.ownerScope;
const content = scope ? scope(() => callback(props, ctx)) : callback(props, ctx);
const content = this.#scoped(() => callback(props, ctx));
this.#slotArgs.set(occurrence, record);

@@ -443,11 +440,5 @@ if (cleanups.length) this.#slotCleanups.set(occurrence, cleanups);

#replaceRange(key, start, nodes) {
const end = slotEnd(key);
const parent = start.parentNode;
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
for (const node of nodes) parent.insertBefore(node, n);
const end = eachInRange(start, key, n => parent.removeChild(n));
for (const node of nodes) parent.insertBefore(node, end);
}

@@ -464,5 +455,3 @@ #unmountSlot(key) {

if (regions) {
for (const {
frame
} of regions.values()) frame?.dispose();
disposeRegions(regions);
this.#slotRegions.delete(key);

@@ -477,11 +466,22 @@ }

}
#refsUnresolved(args) {
const {
host,
id
} = this.#options;
if (host) for (const key in args) {
if (isDataRef(args[key]) && host.resolve(args[key], id) === undefined) return true;
}
return false;
}
#regionsFor(slotKey) {
let regions = this.#slotRegions.get(slotKey);
if (!regions) this.#slotRegions.set(slotKey, regions = new Map());
return regions;
}
#resolveArgs(slotKey, args) {
const host = this.#options.host;
let regions = this.#slotRegions.get(slotKey);
if (!regions) {
regions = new Map();
this.#slotRegions.set(slotKey, regions);
}
const regions = this.#regionsFor(slotKey);
const props = {};
for (const key of Object.keys(args)) {
for (const key in args) {
const value = args[key];

@@ -516,13 +516,4 @@ if (isDataRef(value)) {

if (!start) return;
let regions = this.#slotRegions.get(slotKey);
if (!regions) {
regions = new Map();
this.#slotRegions.set(slotKey, regions);
}
const endData = slotEnd(slotKey);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === endData)) {
collectRegionElements(n, regions);
n = n.nextSibling;
}
const regions = this.#regionsFor(slotKey);
eachInRange(start, slotKey, n => collectRegionElements(n, regions));
}

@@ -543,7 +534,7 @@ #refArgsUnchanged(occurrence, record) {

if (va === vb) continue;
if (!va || !vb || typeof va !== "object" || typeof vb !== "object") return false;
if (typeof va.$frame === "string" && typeof vb.$frame === "string") continue;
if (typeof va.$ref === "string" && typeof vb.$ref === "string" && cache && key in cache) {
if (isFrameRef(va) && isFrameRef(vb)) continue;
if (isDataRef(va) && isDataRef(vb) && cache && key in cache) {
const host = this.#options.host;
const next = host ? host.resolve(vb, this.#options.id) : undefined;
if (isAsyncLike(next) || isAsyncLike(cache[key])) return false;
try {

@@ -564,3 +555,3 @@ if (JSON.stringify(next) === JSON.stringify(cache[key])) continue;

if (!regions) return;
for (const key of Object.keys(args)) {
for (const key in args) {
const value = args[key];

@@ -615,7 +606,4 @@ if (!isFrameRef(value)) continue;

this.#version = undefined;
this.#revealed.clear();
this.#fallbackShown.clear();
for (const key of Object.keys(this.#store)) {
if (key.startsWith("seg:") || key === ":error") delete this.#store[key];
}
this.#appliedRootValue = undefined;
this.#resetStreamState(true);
if (host) host.register(id, this);

@@ -640,7 +628,3 @@ }

for (const key of this.#mountedSlots) this.#removeSlotRecord(key);
for (const regions of this.#slotRegions.values()) {
for (const {
frame
} of regions.values()) frame?.dispose();
}
for (const regions of this.#slotRegions.values()) disposeRegions(regions);
this.#slotRegions.clear();

@@ -667,9 +651,3 @@ this.#mountedSlots.clear();

#clearContent() {
const parent = this.#parent();
let n = this.#firstContent();
while (n && n !== this.#end) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
removeUntil(this.#parent(), this.#firstContent(), this.#end);
}

@@ -691,5 +669,3 @@ #claimContent() {

let ready = true;
for (const entry of assets.styles) {
if (!ensureStylesheet(entry, this.#styleFlush)) ready = false;
}
for (const entry of assets.styles) ready = ensureStylesheet(entry, this.#styleFlush) && ready;
if (!ready) return false;

@@ -708,8 +684,3 @@ }

const parent = tpl.parentNode;
let n = tpl.nextSibling;
while (n && n !== closing) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
removeUntil(parent, tpl.nextSibling, closing);
if (this.#options.reveal) {

@@ -763,5 +734,3 @@ const fallbackFrag = tpl.content.cloneNode(true);

element: el,
get frame() {
return frame;
},
frame,
dispose() {

@@ -786,2 +755,5 @@ frame.dispose();

}
function isAsyncLike(v) {
return !!v && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
}
function propOf(occurrence) {

@@ -792,7 +764,19 @@ const hash = occurrence.indexOf("#");

function isDataRef(value) {
return typeof value === "object" && value !== null && typeof value.$ref === "string";
return !!value && typeof value.$ref === "string";
}
function isFrameRef(value) {
return typeof value === "object" && value !== null && typeof value.$frame === "string";
return !!value && typeof value.$frame === "string";
}
function disposeRegions(regions) {
for (const {
frame
} of regions.values()) frame?.dispose();
}
function removeUntil(parent, n, stop) {
while (n && n !== stop) {
const next = n.nextSibling;
parent.removeChild(n);
n = next;
}
}
function parseFragment(html) {

@@ -804,5 +788,4 @@ const template = document.createElement("template");

function findHeadElement(selector, attr, value) {
const nodes = document.head.querySelectorAll(selector);
for (let i = 0; i < nodes.length; i++) {
if (nodes[i].getAttribute(attr) === value) return nodes[i];
for (const node of document.head.querySelectorAll(selector)) {
if (node.getAttribute(attr) === value) return node;
}

@@ -856,3 +839,3 @@ return null;

function isPlaceholderStart(node, id) {
return node.nodeType === ELEMENT_NODE && node.tagName === "TEMPLATE" && node.getAttribute("id") === id;
return node.tagName === "TEMPLATE" && node.id === id;
}

@@ -913,2 +896,17 @@ function rangeClose(start, id) {

}
function eachInRange(start, key, cb) {
const end = slotEnd(key);
let n = start.nextSibling;
while (n && !(n.nodeType === COMMENT_NODE && n.data === end)) {
const next = n.nextSibling;
cb(n);
n = next;
}
return n;
}
function clearStreamRecords(records, root) {
for (const key in records) {
if (key.startsWith("seg:") || key === ":error" || root && key === "") delete records[key];
}
}
function argsEquivalent(a, b) {

@@ -924,5 +922,3 @@ if (a === b) return true;

if (va === vb) continue;
if (va && vb && typeof va === "object" && typeof vb === "object" && typeof va.$frame === "string" && va.$frame === vb.$frame) {
continue;
}
if (isFrameRef(va) && va.$frame === vb?.$frame) continue;
return false;

@@ -1290,3 +1286,3 @@ }

const comp = componentFor(functionId);
if ((typeof comp === "function" || typeof comp === "object" && comp !== null) && !comp[COMPONENT_BINDING]) {
if (comp && (typeof comp === "function" || typeof comp === "object") && !comp[COMPONENT_BINDING]) {
comp[COMPONENT_BINDING] = {

@@ -1383,2 +1379,5 @@ component: comp,

function asyncArg(value) {
return value;
}
let sharedHost;

@@ -1460,4 +1459,22 @@ const tables = new Map();

ctx.onUpdate(next => setArgs(() => next));
return slotArgsProxy(args);
}
function isAsyncValue(v) {
return v !== null && typeof v === "object" && (typeof v.then === "function" || typeof v[Symbol.asyncIterator] === "function");
}
function slotArgsProxy(args) {
const owner = getOwner();
const asyncReads = new Map();
return new Proxy({}, {
get: (_, key) => args()[key],
get: (_, key) => {
const v = args()[key];
if (!isAsyncValue(v)) return v;
let read = asyncReads.get(key);
if (!read) {
const make = () => createMemo(() => args()[key]);
read = owner ? runWithOwner(owner, make) : make();
asyncReads.set(key, read);
}
return read();
},
has: (_, key) => key in args(),

@@ -1480,2 +1497,3 @@ ownKeys: () => Reflect.ownKeys(args()),

const bindings = new Map();
const fillScopes = new Map();
return new Proxy({}, {

@@ -1491,2 +1509,15 @@ get(_, prop) {

}
const prevFill = key !== undefined && fillScopes.get(key);
if (prevFill) {
fillScopes.delete(key);
prevFill.dispose();
}
const fillOwner = streamInvoke ? createOwner() : null;
if (fillOwner && key !== undefined && ctx) {
fillScopes.set(key, fillOwner);
ctx.onCleanup(() => {
if (fillScopes.get(key) === fillOwner) fillScopes.delete(key);
fillOwner.dispose();
});
}
const settle = out => {

@@ -1504,3 +1535,3 @@ const existing = ctx && ctx.existing || [];

if (typeof v === "function" && ctx && ctx.invoked) {
return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotProps);
return v(ctx.onUpdate ? liveSlotProps(slotProps, ctx) : slotArgsProxy(() => slotProps));
}

@@ -1511,3 +1542,3 @@ return v;

const prefix = ctx && ctx.frame ? `sc-${ctx.frame}-${ctx.key}-` : "";
const value = adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
const value = fillOwner ? runWithOwner(fillOwner, () => adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate()) : adopted ? claimRender(prefix, ctx.existing, evaluate) : evaluate();
if (!isReactiveContent(value)) {

@@ -1537,11 +1568,23 @@ return settle(normalizeSlotContent(value));

}
let streamInvoke = false;
function boundaryScope(owner) {
return fn => getOwner() ? fn() : runWithOwner(owner, fn);
return fn => {
if (getOwner()) return fn();
streamInvoke = true;
try {
return runWithOwner(owner, fn);
} finally {
streamInvoke = false;
}
};
}
function followBinding(frame, binding) {
createRenderEffect(binding, address => frame.rebind(address));
}
function boundaryComponent(host, fnId) {
return (props, binding) => {
const owner = getOwner();
const id = binding ? binding() : fnId;
let applied = !tables.has(id);
let release;
const arm = () => new Promise(r => release = r);
const mountGate = applied ? undefined : arm();
let setGate;
const {

@@ -1553,10 +1596,30 @@ element,

host,
id: binding ? binding() : fnId,
id,
slots: slotsFor(props),
ownerScope: boundaryScope(owner),
reveal: revealSeam(owner)
reveal: revealSeam(owner),
onApply: () => {
applied = true;
if (release) {
release();
release = undefined;
}
setGate && setGate(undefined);
}
});
if (binding) followBinding(frame, binding);
const [gatePromise, setGatePromise] = createSignal(applied ? undefined : mountGate);
setGate = setGatePromise;
if (binding) {
createRenderEffect(binding, (address, prev) => {
if (prev !== undefined && address !== prev && tables.has(address)) {
applied = false;
setGatePromise(arm());
}
frame.rebind(address);
});
}
onCleanup(dispose);
return element;
if (applied && !binding) return element;
const gate = createMemo(() => gatePromise());
return createMemo(() => (gate(), element));
};

@@ -1629,2 +1692,13 @@ }

const appliedRecords = new Set();
const claimedFragments = new Set();
const claimRegionFragments = root => {
const fr = globalThis._$HY?.fr;
if (!fr || !fr.claim) return;
root.querySelectorAll('template[id^="pl-"]').forEach(tpl => {
const fragId = tpl.id.slice(3);
if (claimedFragments.has(fragId)) return;
claimedFragments.add(fragId);
fr.claim(fragId);
});
};
const drainRecords = () => {

@@ -1661,4 +1735,16 @@ const hy = globalThis._$HY;

};
claimRegionFragments(el);
const fr = globalThis._$HY?.fr;
const unsubscribe = fr ? fr.subscribe((_fragId, parent) => {
if (fr.claim && parent && el.contains(parent)) claimRegionFragments(parent);
drainRecords();
}) : undefined;
onCleanup(() => {
unsubscribe && unsubscribe();
if (fr && fr.release) for (const fragId of claimedFragments) fr.release(fragId);
});
drainRecords();
const owner = getOwner();
let release;
let setGate;
const frame = createFrame(el, {

@@ -1671,2 +1757,9 @@ adopt: true,

reveal: revealSeam(owner),
onApply: () => {
if (release) {
release();
release = undefined;
}
setGate && setGate(undefined);
},
...{

@@ -1682,3 +1775,15 @@ recordsPending: () => {

});
if (binding) followBinding(frame, binding);
if (binding) {
const arm = () => new Promise(r => release = r);
const [gatePromise, setGatePromise] = createSignal(undefined);
setGate = setGatePromise;
const gate = createMemo(() => gatePromise());
createRenderEffect(binding, (address, prev) => {
if (prev !== undefined && address !== prev && tables.has(address)) {
setGatePromise(arm());
}
frame.rebind(address);
});
createRenderEffect(() => (gate(), undefined), () => {});
}
onCleanup(() => frame.dispose());

@@ -1724,2 +1829,2 @@ return el;

export { FRAME_APPLIED_EVENT, FRAME_STREAM_HEADER, applyFrameResponse, createFrame, createFrameElement, createFrameHost, createJSONDataTable, createServerComponentHandler, getFrameHost, installServerComponents, isFrameStreamResponse };
export { FRAME_APPLIED_EVENT, FRAME_STREAM_HEADER, applyFrameResponse, asyncArg, createFrame, createFrameElement, createFrameHost, createServerComponentHandler, getFrameHost, installServerComponents, isFrameStreamResponse };
{
"name": "@solidjs/web",
"description": "Solid's web runtime: client rendering, hydration, SSR, and DOM-specific control flow (Portal, Dynamic).",
"version": "2.0.0-beta.31",
"version": "2.0.0-beta.32",
"author": "Ryan Carniato",

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

"server-functions/package.json",
"server-functions/rich-args/package.json",
"frames/dist",

@@ -155,2 +156,12 @@ "frames/package.json"

"worker": {
"development": {
"import": {
"types": "./types/server-functions/server.d.ts",
"default": "./server-functions/dist/server.dev.js"
},
"require": {
"types": "./types-cjs/server-functions/server.d.cts",
"default": "./server-functions/dist/server.dev.cjs"
}
},
"import": {

@@ -176,2 +187,12 @@ "types": "./types/server-functions/server.d.ts",

"deno": {
"development": {
"import": {
"types": "./types/server-functions/server.d.ts",
"default": "./server-functions/dist/server.dev.js"
},
"require": {
"types": "./types-cjs/server-functions/server.d.cts",
"default": "./server-functions/dist/server.dev.cjs"
}
},
"import": {

@@ -187,2 +208,12 @@ "types": "./types/server-functions/server.d.ts",

"node": {
"development": {
"import": {
"types": "./types/server-functions/server.d.ts",
"default": "./server-functions/dist/server.dev.js"
},
"require": {
"types": "./types-cjs/server-functions/server.d.cts",
"default": "./server-functions/dist/server.dev.cjs"
}
},
"import": {

@@ -207,2 +238,12 @@ "types": "./types/server-functions/server.d.ts",

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

@@ -227,2 +268,12 @@ "types": "./types/server-functions/server.d.ts",

},
"./server-functions/rich-args": {
"import": {
"types": "./types/server-functions/rich-args.d.ts",
"default": "./server-functions/dist/rich-args.js"
},
"require": {
"types": "./types-cjs/server-functions/rich-args.d.cts",
"default": "./server-functions/dist/rich-args.cjs"
}
},
"./frames": {

@@ -325,6 +376,6 @@ "worker": {

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

@@ -344,3 +395,3 @@ "scripts": {

"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-server-functions": "node -e \"fs.mkdirSync('./types/server-functions', { recursive: true }); for (const f of ['shared', 'flash', 'client', 'server']) fs.copyFileSync('../../node_modules/@dom-expressions/runtime/src/server-functions/' + f + '.d.ts', './types/server-functions/' + f + '.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');\"",
"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/', './'));\"",

@@ -347,0 +398,0 @@ "types:cjs": "node ../../scripts/sync-dual-types.mjs ./types ./types-cjs ./storage/types ./storage/types-cjs ./serialization/types ./serialization/types-cjs",

@@ -16,8 +16,3 @@ # @solidjs/web

// Server (SSR)
import {
renderToString,
renderToStringAsync,
renderToStream,
isServer
} from "@solidjs/web";
import { renderToString, renderToStream, isServer } from "@solidjs/web";
```

@@ -24,0 +19,0 @@

@@ -161,2 +161,10 @@ 'use strict';

Object.defineProperty(exports, "OpaqueReference", {
enumerable: true,
get: function () { return seroval.OpaqueReference; }
});
Object.defineProperty(exports, "createPlugin", {
enumerable: true,
get: function () { return seroval.createPlugin; }
});
exports.DEFAULT_WEB_PLUGINS = DEFAULT_WEB_PLUGINS;

@@ -163,0 +171,0 @@ exports.createHydrationSerializer = createHydrationSerializer;

import { Feature, Serializer, getCrossReferenceHeader, toCrossJSONStream, fromCrossJSON } from 'seroval';
export { OpaqueReference, createPlugin } from 'seroval';
import { AbortSignalPlugin, CustomEventPlugin, DOMExceptionPlugin, EventPlugin, FormDataPlugin, HeadersPlugin, ReadableStreamPlugin, RequestPlugin, ResponsePlugin, URLSearchParamsPlugin, URLPlugin } from 'seroval-plugins/web';

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

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

import { Plugin, Serializer, SerovalNode } from "seroval";
// Serialization surface (published as `@solidjs/web/serialization`): the
// runtime's Seroval machinery, exposed for the runtime's own entries and
// for integrations building transports on the same codec. This is
// INTEGRATION-FACING plumbing, not application API — it is exempt from the
// 2.0 stability guarantee and may change between releases. Application and
// router code should configure `codec` on the server-function entries
// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";

@@ -6,13 +13,123 @@ /**

* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// ---- Plugin authoring ----
//
// Unlike the rest of this entry, plugin authoring is APPLICATION-FACING —
// it is the supported way to feed the serializers' `plugins` options and
// the server-function entries' `codec.plugins`. The values re-export
// 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.
/** 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. Supply matching plugins on both
* peers of a transport.
* 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 type SerializerPlugin = Plugin<any, any>;
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
* plugin authors stay on the exact seroval instance/version the runtime
* serializes with. Import it from HERE, not from your own `seroval`
* dependency: a plugin built against a different copy/version would not
* fail the build — it would emit nodes the other peer can't interpret.
*
* Application-facing (see the plugin-authoring banner above).
*/
export function createPlugin<Value, Info extends PluginInfo>(
plugin: SerializerPlugin<Value, Info>
): SerializerPlugin<Value, Info>;
/**
* Seroval's `OpaqueReference`, re-exported from the runtime's own instance
* (an `OpaqueReference` from another seroval copy fails the serializer's
* instanceof check and serializes as a plain value): wraps a value so it
* crosses the wire as its `replacement` (default `undefined`) while
* staying readable in-process through `.value`.
*
* Application-facing (see the plugin-authoring banner above).
*/
export class OpaqueReference<V, R = undefined> {
readonly value: V;
readonly replacement?: R;
constructor(value: V, replacement?: R);
}
/**
* Baseline plugin set for serializing web-platform values (AbortSignal,

@@ -22,2 +139,4 @@ * Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).

* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -31,6 +150,12 @@ export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];

* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/** Options for `createSerializer`. */
/**
* Options for `createSerializer`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface WebSerializerOptions {

@@ -64,2 +189,4 @@ /** Name of the global object the emitted scripts write resolved values into. */

* `serializeJSON` / `createJSONDeserializer` instead.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -103,2 +230,4 @@ export function createSerializer(options: WebSerializerOptions): Serializer;

* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -121,3 +250,7 @@ export interface JSONCodecOptions {

/** Options for `serializeJSON`. */
/**
* Options for `serializeJSON`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONSerializeOptions extends JSONCodecOptions {

@@ -141,2 +274,4 @@ /**

* function that aborts pending async serialization.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -151,6 +286,34 @@ export function serializeJSON(value: unknown, options: JSONSerializeOptions): () => void;

* 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`. */
export interface JSONSerializerOptions extends JSONCodecOptions {
/**
* Receives each keyed record — `initial` is true for a key's first node
* (the written value itself); async values patch through later records
* under the same key. The decoding peer is `createJSONDataTable`.
*/
onData: (record: { key: string; node: SerovalNode; initial: boolean }) => void;
onError?: (error: unknown) => void;
/** Fires once `flush()` has been called and every pending value settled. */
onDone?: () => void;
}
/**
* The keyed, streaming encoder of the eval-free JSON codec — the render
* stream's data serializer (frames default to it). Each `write(key, value)`
* shares one reference space, so cross-record identity holds; `flush()`
* marks the write set complete (writes after it are dropped, mirroring the
* hydration serializer); `close()` aborts pending async serialization.
*/
export function createJSONSerializer(options: JSONSerializerOptions): {
write(key: string, value: unknown): void;
flush(): void;
close(): void;
};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply

@@ -160,2 +323,6 @@ * each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with

* (`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.
*/

@@ -162,0 +329,0 @@ export interface JSONDataTable {

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

import { Plugin, Serializer, SerovalNode } from "seroval";
// Serialization surface (published as `@solidjs/web/serialization`): the
// runtime's Seroval machinery, exposed for the runtime's own entries and
// for integrations building transports on the same codec. This is
// INTEGRATION-FACING plumbing, not application API — it is exempt from the
// 2.0 stability guarantee and may change between releases. Application and
// router code should configure `codec` on the server-function entries
// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";

@@ -6,13 +13,123 @@ /**

* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// ---- Plugin authoring ----
//
// Unlike the rest of this entry, plugin authoring is APPLICATION-FACING —
// it is the supported way to feed the serializers' `plugins` options and
// the server-function entries' `codec.plugins`. The values re-export
// 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.
/** 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. Supply matching plugins on both
* peers of a transport.
* 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 type SerializerPlugin = Plugin<any, any>;
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
* plugin authors stay on the exact seroval instance/version the runtime
* serializes with. Import it from HERE, not from your own `seroval`
* dependency: a plugin built against a different copy/version would not
* fail the build — it would emit nodes the other peer can't interpret.
*
* Application-facing (see the plugin-authoring banner above).
*/
export function createPlugin<Value, Info extends PluginInfo>(
plugin: SerializerPlugin<Value, Info>
): SerializerPlugin<Value, Info>;
/**
* Seroval's `OpaqueReference`, re-exported from the runtime's own instance
* (an `OpaqueReference` from another seroval copy fails the serializer's
* instanceof check and serializes as a plain value): wraps a value so it
* crosses the wire as its `replacement` (default `undefined`) while
* staying readable in-process through `.value`.
*
* Application-facing (see the plugin-authoring banner above).
*/
export class OpaqueReference<V, R = undefined> {
readonly value: V;
readonly replacement?: R;
constructor(value: V, replacement?: R);
}
/**
* Baseline plugin set for serializing web-platform values (AbortSignal,

@@ -22,2 +139,4 @@ * Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).

* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -31,6 +150,12 @@ export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];

* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/** Options for `createSerializer`. */
/**
* Options for `createSerializer`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface WebSerializerOptions {

@@ -64,2 +189,4 @@ /** Name of the global object the emitted scripts write resolved values into. */

* `serializeJSON` / `createJSONDeserializer` instead.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -103,2 +230,4 @@ export function createSerializer(options: WebSerializerOptions): Serializer;

* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -121,3 +250,7 @@ export interface JSONCodecOptions {

/** Options for `serializeJSON`. */
/**
* Options for `serializeJSON`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONSerializeOptions extends JSONCodecOptions {

@@ -141,2 +274,4 @@ /**

* function that aborts pending async serialization.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -151,6 +286,34 @@ export function serializeJSON(value: unknown, options: JSONSerializeOptions): () => void;

* 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`. */
export interface JSONSerializerOptions extends JSONCodecOptions {
/**
* Receives each keyed record — `initial` is true for a key's first node
* (the written value itself); async values patch through later records
* under the same key. The decoding peer is `createJSONDataTable`.
*/
onData: (record: { key: string; node: SerovalNode; initial: boolean }) => void;
onError?: (error: unknown) => void;
/** Fires once `flush()` has been called and every pending value settled. */
onDone?: () => void;
}
/**
* The keyed, streaming encoder of the eval-free JSON codec — the render
* stream's data serializer (frames default to it). Each `write(key, value)`
* shares one reference space, so cross-record identity holds; `flush()`
* marks the write set complete (writes after it are dropped, mirroring the
* hydration serializer); `close()` aborts pending async serialization.
*/
export function createJSONSerializer(options: JSONSerializerOptions): {
write(key: string, value: unknown): void;
flush(): void;
close(): void;
};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply

@@ -160,2 +323,6 @@ * each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with

* (`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.
*/

@@ -162,0 +329,0 @@ export interface JSONDataTable {

@@ -9,2 +9,3 @@ 'use strict';

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,

@@ -29,2 +30,17 @@ web.CustomEventPlugin, web.DOMExceptionPlugin, web.EventPlugin,

}
function serializeJSON(value, {
onParse,
onDone,
onError,
...codecOptions
}) {
const resolved = resolveCodecOptions(codecOptions);
return seroval.toCrossJSONStream(value, {
onParse,
onDone,
onError,
...resolved,
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
});
}
function createJSONDeserializer(options) {

@@ -264,9 +280,7 @@ const refs = new Map();

function createChunk(data) {
const encodeData = new TextEncoder().encode(data);
const encoder = new TextEncoder();
const encodeData = encoder.encode(data);
const bytes = encodeData.length;
const baseHex = bytes.toString(16);
const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
const head = new TextEncoder().encode(`;0x${totalHex};`);
const chunk = new Uint8Array(12 + bytes);
chunk.set(head);
chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
chunk.set(encodeData, 12);

@@ -303,4 +317,4 @@ return chunk;

}
const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
const bytes = Number.parseInt(head, 16);
const decoder = new TextDecoder();
const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
if (Number.isNaN(bytes)) {

@@ -315,3 +329,3 @@ throw new Error("Malformed server function stream.");

}
const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
this.buffer = this.buffer.subarray(12 + bytes);

@@ -333,2 +347,24 @@ return {

}
function serializeStream(value, codecOptions) {
return new ReadableStream({
start(controller) {
serializeJSON(value, {
...codecOptions,
onParse(node) {
controller.enqueue(createChunk(JSON.stringify(node)));
},
onDone() {
controller.close();
},
onError(error) {
controller.error(error);
}
});
}
});
}
async function serializeString(value, codecOptions) {
const response = new Response(serializeStream(value, codecOptions));
return await response.text();
}
async function deserializeStream(source, codecOptions) {

@@ -390,3 +426,3 @@ if (!source.body) {

if (!config.serializeArgs) {
throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " + "(from the server-functions rich-args entry) once at startup to " + "send Dates, Maps, Sets, typed arrays, etc. through the codec — or " + "pass a single Blob/FormData/File argument, which has a native " + "HTTP encoding.");
throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " + '(from "@solidjs/web/server-functions/rich-args") once at startup ' + "to send Dates, Maps, Sets, typed arrays, etc. through the codec — " + "or pass a single Blob/FormData/File argument, which has a native " + "HTTP encoding.");
}

@@ -619,3 +655,4 @@ return config.serializeArgs(args);

exports.registerServerReference = registerServerReference;
exports.serializeString = serializeString;
exports.subscribeFlightData = subscribeFlightData;
exports.withMeta = withMeta;

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

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

@@ -7,2 +7,3 @@

Feature.AggregateError | Feature.BigIntTypedArray;
const serializeOnlyDisabledFeatures = () => process.env.NODE_ENV === "development" ? 0 : Feature.ErrorPrototypeStack;
const DEFAULT_WEB_PLUGINS = Object.freeze([AbortSignalPlugin,

@@ -27,2 +28,17 @@ CustomEventPlugin, DOMExceptionPlugin, EventPlugin,

}
function serializeJSON(value, {
onParse,
onDone,
onError,
...codecOptions
}) {
const resolved = resolveCodecOptions(codecOptions);
return toCrossJSONStream(value, {
onParse,
onDone,
onError,
...resolved,
disabledFeatures: resolved.disabledFeatures | serializeOnlyDisabledFeatures()
});
}
function createJSONDeserializer(options) {

@@ -262,9 +278,7 @@ const refs = new Map();

function createChunk(data) {
const encodeData = new TextEncoder().encode(data);
const encoder = new TextEncoder();
const encodeData = encoder.encode(data);
const bytes = encodeData.length;
const baseHex = bytes.toString(16);
const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
const head = new TextEncoder().encode(`;0x${totalHex};`);
const chunk = new Uint8Array(12 + bytes);
chunk.set(head);
chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
chunk.set(encodeData, 12);

@@ -301,4 +315,4 @@ return chunk;

}
const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
const bytes = Number.parseInt(head, 16);
const decoder = new TextDecoder();
const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
if (Number.isNaN(bytes)) {

@@ -313,3 +327,3 @@ throw new Error("Malformed server function stream.");

}
const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
this.buffer = this.buffer.subarray(12 + bytes);

@@ -331,2 +345,24 @@ return {

}
function serializeStream(value, codecOptions) {
return new ReadableStream({
start(controller) {
serializeJSON(value, {
...codecOptions,
onParse(node) {
controller.enqueue(createChunk(JSON.stringify(node)));
},
onDone() {
controller.close();
},
onError(error) {
controller.error(error);
}
});
}
});
}
async function serializeString(value, codecOptions) {
const response = new Response(serializeStream(value, codecOptions));
return await response.text();
}
async function deserializeStream(source, codecOptions) {

@@ -388,3 +424,3 @@ if (!source.body) {

if (!config.serializeArgs) {
throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " + "(from the server-functions rich-args entry) once at startup to " + "send Dates, Maps, Sets, typed arrays, etc. through the codec — or " + "pass a single Blob/FormData/File argument, which has a native " + "HTTP encoding.");
throw new Error("Server function arguments are sent as JSON by default and these " + "arguments are not JSON-serializable. Call enableRichArguments() " + '(from "@solidjs/web/server-functions/rich-args") once at startup ' + "to send Dates, Maps, Sets, typed arrays, etc. through the codec — " + "or pass a single Blob/FormData/File argument, which has a native " + "HTTP encoding.");
}

@@ -592,2 +628,2 @@ return config.serializeArgs(args);

export { ChunkReader, ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, REVALIDATE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsClient, createChunk, createServerReference, decodeErrorHeaderValue, decodeResponse, decodeResponsePayload, deserializeStream, encodeErrorHeaderValue, frameAddress, getFlightDataConsumer, getServerFunctionInvocation, getServerFunctionMetadata, getServerFunctionsCodec, hasFlashCookie, isServerFunction, registerServerReference, subscribeFlightData, withMeta };
export { ChunkReader, ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, REVALIDATE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsClient, createChunk, createServerReference, decodeErrorHeaderValue, decodeResponse, decodeResponsePayload, deserializeStream, encodeErrorHeaderValue, frameAddress, getFlightDataConsumer, getServerFunctionInvocation, getServerFunctionMetadata, getServerFunctionsCodec, hasFlashCookie, isServerFunction, registerServerReference, serializeString, subscribeFlightData, withMeta };
'use strict';
var solidJs = require('solid-js');
var seroval = require('seroval');
var web = require('seroval-plugins/web');
var solidJs = require('solid-js');

@@ -11,2 +11,6 @@ const ENVELOPE = Symbol.for("solid.ResponseEnvelope");

}
const SAFE_ERROR = Symbol.for("solid.SafeError");
function isSafeError(value) {
return !!(value && (typeof value === "object" || typeof value === "function") && value[SAFE_ERROR]);
}
const REVALIDATE_HEADER = "X-Revalidate";

@@ -61,7 +65,2 @@

const RequestContext = Symbol.for("solid.RequestContext");
function getRequestEvent() {
return globalThis[RequestContext] ? globalThis[RequestContext].getStore() || solidJs.sharedConfig.context && solidJs.sharedConfig.context.event || console.warn("RequestEvent is missing. This is most likely due to accessing `getRequestEvent` non-managed async scope in a partially polyfilled environment. Try moving it above all `await` calls.") : undefined;
}
const codecConfig = {

@@ -131,6 +130,2 @@ codec: undefined

}
function matchFlashCookie(cookieHeader) {
const match = cookieHeader && cookieHeader.match(FLASH_MATCHER);
return match ? match[1] : undefined;
}
function clearFlashCookie() {

@@ -243,9 +238,7 @@ return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;

function createChunk(data) {
const encodeData = new TextEncoder().encode(data);
const encoder = new TextEncoder();
const encodeData = encoder.encode(data);
const bytes = encodeData.length;
const baseHex = bytes.toString(16);
const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
const head = new TextEncoder().encode(`;0x${totalHex};`);
const chunk = new Uint8Array(12 + bytes);
chunk.set(head);
chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
chunk.set(encodeData, 12);

@@ -282,4 +275,4 @@ return chunk;

}
const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
const bytes = Number.parseInt(head, 16);
const decoder = new TextDecoder();
const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
if (Number.isNaN(bytes)) {

@@ -294,3 +287,3 @@ throw new Error("Malformed server function stream.");

}
const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
this.buffer = this.buffer.subarray(12 + bytes);

@@ -366,2 +359,110 @@ return {

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");
function getRequestEvent() {
return globalThis[RequestContext] ? globalThis[RequestContext].getStore() || solidJs.sharedConfig.context && solidJs.sharedConfig.context.event || console.warn("RequestEvent is missing. This is most likely due to accessing `getRequestEvent` non-managed async scope in a partially polyfilled environment. Try moving it above all `await` calls.") : undefined;
}
function reportLostHeaderWrite(method, name) {
const message = `Response header write dropped: headers.${method}(${JSON.stringify(String(name))}) ` + "ran after the response head was sent. Write headers before the shell flushes " + "(or before the handler returns).";
console.error(message);
}
function commitResponseStub(stub, {
allowLateLocation = false
} = {}) {
if (!stub || stub.committed) return stub;
stub.committed = true;
const headers = stub.headers;
if (!headers || typeof headers.set !== "function") return stub;
for (const method of ["set", "append", "delete"]) {
const original = headers[method].bind(headers);
headers[method] = function (name, ...rest) {
if (allowLateLocation && method === "set" && String(name).toLowerCase() === "location") {
return original(name, ...rest);
}
reportLostHeaderWrite(method, name);
};
}
return stub;
}
function copyInitHeaders(init) {
if (!init || !init.getSetCookie) return new Headers(init);
const headers = new Headers();
init.forEach((value, key) => {
if (key !== "set-cookie") headers.append(key, value);
});
for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
return headers;
}
const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, "Location"].map(header => header.toLowerCase()));
function fillsStubGap(key, headers, response) {
if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
if (response.body === null && (key === "content-type" || key === "content-length")) return false;
return !headers.has(key);
}
function commitEventResponse(response, event = getRequestEvent()) {
const stub = event && event.response;
if (!stub || !stub.headers || stub.committed) return response;
const cookies = stub.headers.getSetCookie ? stub.headers.getSetCookie() : [];
commitResponseStub(stub);
let hasGaps = false;
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, response.headers, response)) hasGaps = true;
});
if (!cookies.length && !hasGaps) return response;
try {
for (const cookie of cookies) response.headers.append("Set-Cookie", cookie);
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, response.headers, response)) response.headers.set(key, value);
});
return response;
} catch {
const headers = copyInitHeaders(response.headers);
for (const cookie of cookies) headers.append("Set-Cookie", cookie);
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, headers, response)) headers.set(key, value);
});
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers
});
}
}
function encodeInputValue(value) {

@@ -396,9 +497,12 @@ if (value instanceof FormData) return {

};
return `${FLASH_COOKIE}=${encodeURIComponent(JSON.stringify(payload))}; Secure; HttpOnly; Path=/`;
return serializeCookie(FLASH_COOKIE, JSON.stringify(payload), {
secure: true,
httpOnly: true
});
}
function decodeFlashCookie(cookieHeader) {
const match = matchFlashCookie(cookieHeader);
const match = parseCookieHeader(cookieHeader)[FLASH_COOKIE];
if (!match) return;
try {
const payload = JSON.parse(decodeURIComponent(match));
const payload = JSON.parse(match);
if (!payload || !payload.result) return;

@@ -419,2 +523,3 @@ const result = payload.error ? new Error(payload.result) : payload.result;

provideEvent: undefined,
wrapInvocation: undefined,
collectFlightData: undefined,

@@ -429,2 +534,3 @@ transformResult: undefined,

provideEvent,
wrapInvocation,
collectFlightData,

@@ -439,2 +545,3 @@ transformResult,

if (provideEvent !== undefined) config.provideEvent = provideEvent;
if (wrapInvocation !== undefined) config.wrapInvocation = wrapInvocation;
if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;

@@ -505,3 +612,9 @@ if (transformResult !== undefined) config.transformResult = transformResult;

const result = provideEvent(evt, () => {
return fn.apply(thisArg, args);
const run = () => fn.apply(thisArg, args);
return config.wrapInvocation ? config.wrapInvocation(run, {
id,
args,
event: evt,
direct: true
}) : run();
});

@@ -648,2 +761,12 @@ const transform = config.transformDirectResult;

}
function mergeResponseHeaders(target, source) {
source.forEach((value, key) => {
if (key !== "set-cookie") target.append(key, value);
});
if (source.getSetCookie) {
for (const cookie of source.getSetCookie()) target.append("Set-Cookie", cookie);
} else if (source.has("set-cookie")) {
target.append("Set-Cookie", source.get("set-cookie"));
}
}
const validRedirectStatuses = new Set([301, 302, 303, 307, 308]);

@@ -663,3 +786,4 @@ function createNoJSHandler({

if (result instanceof Response) {
headers = new Headers(result.headers);
headers = new Headers();
mergeResponseHeaders(headers, result.headers);
if (result.headers.has("Location")) {

@@ -717,2 +841,12 @@ headers.set("Location", new URL(result.headers.get("Location"), url.origin + base).toString());

}
const GENERIC_SERVER_ERROR_MESSAGE = "Internal Server Error";
let DEV = false === true;
function setServerFunctionsDev(dev) {
DEV = !!dev;
}
function sanitizeServerError(value) {
if (DEV) return value;
if (isSafeError(value)) return value;
return new Error(GENERIC_SERVER_ERROR_MESSAGE);
}
async function handleServerFunctionRequest(request, options = {}) {

@@ -724,3 +858,3 @@ const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();

if (!functionId) {
return new Response(process.env.NODE_ENV === "development" ? "Server function not found" : null, {
return new Response(DEV ? "Server function not found" : null, {
status: 404

@@ -733,3 +867,3 @@ });

} catch {
return new Response(process.env.NODE_ENV === "development" ? `Unknown server function: ${functionId}` : null, {
return new Response(DEV ? `Unknown server function: ${functionId}` : null, {
status: 404

@@ -739,3 +873,3 @@ });

if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
return new Response(process.env.NODE_ENV === "development" ? `Method not allowed for server function: ${functionId}` : null, {
return new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
status: 405,

@@ -754,2 +888,3 @@ headers: {

const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
const wrapInvocation = options.wrapInvocation !== undefined ? options.wrapInvocation : config.wrapInvocation;
const transformFlightResult = options.transformFlightResult !== undefined ? options.transformFlightResult : config.transformFlightResult;

@@ -769,127 +904,138 @@ const handleNoJS = options.handleNoJS !== undefined ? options.handleNoJS : config.handleNoJS !== undefined ? config.handleNoJS : isFormPost(request) ? defaultNoJSHandler || (defaultNoJSHandler = createNoJSHandler()) : undefined;

const headers = new Headers();
try {
let result = await provide(event, async () => {
INVOCATIONS.set(event, {
id: functionId
const dispatch = async () => {
try {
let result = await provide(event, async () => {
INVOCATIONS.set(event, {
id: functionId
});
const run = () => serverFunction(...parsed);
return wrapInvocation ? wrapInvocation(run, {
id: functionId,
args: parsed,
event,
request,
direct: false
}) : run();
});
return serverFunction(...parsed);
});
if (transformResult) {
result = await transformResult(event, result, flightContext);
}
let status = 200;
let metadata;
if (isResponseEnvelope(result)) {
const {
response,
value
} = result;
if (!instance && !handleNoJS && response && response.body) {
return response;
}
if (response && response.headers) {
response.headers.forEach((val, key) => headers.append(key, val));
}
if (response && response.status && (response.status < 300 || response.status >= 400)) {
status = response.status;
}
metadata = response;
result = value;
} else if (result instanceof Response) {
if (result.headers && result.headers.has("X-Content-Raw")) return result;
if (instance) {
if (result.headers) {
result.headers.forEach((value, key) => headers.append(key, value));
}
if (result.status && (result.status < 300 || result.status >= 400)) {
status = result.status;
}
metadata = result;
if (result.body == null) {
result = null;
}
}
}
if (collectsFlight) {
result = await foldFlightData(flightHook, event, headers, {
id: functionId,
value: result,
response: metadata,
request,
thrown: false
}, flightContext);
if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
}
if (!instance) {
if (handleNoJS) return handleNoJS(result, request, parsed);
if (result instanceof Response) return result;
return encodeResult(result, headers, 200, codec);
}
return encodeResult(result, headers, status, codec);
} catch (x) {
if (x instanceof Response || isResponseEnvelope(x)) {
if (transformResult) {
x = await transformResult(event, x, {
...flightContext,
thrown: true
});
result = await transformResult(event, result, flightContext);
}
let status = 200;
let metadata;
if (isResponseEnvelope(x)) {
if (isResponseEnvelope(result)) {
const {
response,
value
} = x;
} = result;
if (!instance && !handleNoJS && response && response.body) {
return response;
}
if (response && response.headers) {
response.headers.forEach((val, key) => headers.append(key, val));
mergeResponseHeaders(headers, response.headers);
}
if (response && response.status && (!instance || response.status < 300 || response.status >= 400)) {
if (response && response.status && (response.status < 300 || response.status >= 400)) {
status = response.status;
}
metadata = response;
x = value;
} else if (x instanceof Response) {
if (x.headers) {
x.headers.forEach((value, key) => headers.append(key, value));
result = value;
} else if (result instanceof Response) {
if (result.headers && result.headers.has("X-Content-Raw")) return result;
if (instance) {
if (result.headers) {
mergeResponseHeaders(headers, result.headers);
}
if (result.status && (result.status < 300 || result.status >= 400)) {
status = result.status;
}
metadata = result;
if (result.body == null) {
result = null;
}
}
if (x.status && (!instance || x.status < 300 || x.status >= 400)) {
status = x.status;
}
metadata = x;
if (x.body == null) {
x = null;
}
}
if (collectsFlight) {
x = await foldFlightData(flightHook, event, headers, {
result = await foldFlightData(flightHook, event, headers, {
id: functionId,
value: x,
value: result,
response: metadata,
request,
thrown: true
thrown: false
}, flightContext);
if (x instanceof Response && x.headers.has("X-Content-Raw")) {
x.headers.set(ERROR_HEADER, "true");
return x;
if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
}
if (!instance) {
if (handleNoJS) return handleNoJS(result, request, parsed);
if (result instanceof Response) return result;
return encodeResult(result, headers, 200, codec);
}
return encodeResult(result, headers, status, codec);
} catch (x) {
if (x instanceof Response || isResponseEnvelope(x)) {
if (transformResult) {
x = await transformResult(event, x, {
...flightContext,
thrown: true
});
}
let status = 200;
let metadata;
if (isResponseEnvelope(x)) {
const {
response,
value
} = x;
if (response && response.headers) {
mergeResponseHeaders(headers, response.headers);
}
if (response && response.status && (!instance || response.status < 300 || response.status >= 400)) {
status = response.status;
}
metadata = response;
x = value;
} else if (x instanceof Response) {
if (x.headers) {
mergeResponseHeaders(headers, x.headers);
}
if (x.status && (!instance || x.status < 300 || x.status >= 400)) {
status = x.status;
}
metadata = x;
if (x.body == null) {
x = null;
}
}
if (collectsFlight) {
x = await foldFlightData(flightHook, event, headers, {
id: functionId,
value: x,
response: metadata,
request,
thrown: true
}, flightContext);
if (x instanceof Response && x.headers.has("X-Content-Raw")) {
x.headers.set(ERROR_HEADER, "true");
return x;
}
}
headers.set(ERROR_HEADER, "true");
if (!instance) {
if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
if (x instanceof Response) return x;
}
return encodeResult(x, headers, status, codec);
}
headers.set(ERROR_HEADER, "true");
const safe = sanitizeServerError(x);
if (!instance) {
if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
if (x instanceof Response) return x;
if (handleNoJS) return handleNoJS(safe, request, parsed, true);
const message = safe instanceof Error ? safe.message : String(safe);
return new Response(DEV ? message : null, {
status: 500
});
}
return encodeResult(x, headers, status, codec);
const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
return encodeResult(safe, headers, 200, codec);
}
if (!instance) {
if (handleNoJS) return handleNoJS(x, request, parsed, true);
const message = x instanceof Error ? x.message : String(x);
return new Response(process.env.NODE_ENV === "development" ? message : null, {
status: 500
});
}
const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
return encodeResult(x, headers, 200, codec);
}
};
return commitEventResponse(await dispatch(), event);
}

@@ -900,2 +1046,3 @@

exports.FUNCTION_HEADER = FUNCTION_HEADER;
exports.GENERIC_SERVER_ERROR_MESSAGE = GENERIC_SERVER_ERROR_MESSAGE;
exports.GET = GET;

@@ -924,3 +1071,5 @@ exports.INSTANCE_HEADER = INSTANCE_HEADER;

exports.registerServerReference = registerServerReference;
exports.sanitizeServerError = sanitizeServerError;
exports.setServerFunctionsDev = setServerFunctionsDev;
exports.subscribeFlightData = subscribeFlightData;
exports.withMeta = withMeta;

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

import { sharedConfig } from 'solid-js';
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';

@@ -9,2 +9,6 @@ const ENVELOPE = Symbol.for("solid.ResponseEnvelope");

}
const SAFE_ERROR = Symbol.for("solid.SafeError");
function isSafeError(value) {
return !!(value && (typeof value === "object" || typeof value === "function") && value[SAFE_ERROR]);
}
const REVALIDATE_HEADER = "X-Revalidate";

@@ -59,7 +63,2 @@

const RequestContext = Symbol.for("solid.RequestContext");
function getRequestEvent() {
return globalThis[RequestContext] ? globalThis[RequestContext].getStore() || sharedConfig.context && sharedConfig.context.event || console.warn("RequestEvent is missing. This is most likely due to accessing `getRequestEvent` non-managed async scope in a partially polyfilled environment. Try moving it above all `await` calls.") : undefined;
}
const codecConfig = {

@@ -129,6 +128,2 @@ codec: undefined

}
function matchFlashCookie(cookieHeader) {
const match = cookieHeader && cookieHeader.match(FLASH_MATCHER);
return match ? match[1] : undefined;
}
function clearFlashCookie() {

@@ -241,9 +236,7 @@ return `${FLASH_COOKIE}=; Max-Age=0; Path=/`;

function createChunk(data) {
const encodeData = new TextEncoder().encode(data);
const encoder = new TextEncoder();
const encodeData = encoder.encode(data);
const bytes = encodeData.length;
const baseHex = bytes.toString(16);
const totalHex = "00000000".substring(0, 8 - baseHex.length) + baseHex;
const head = new TextEncoder().encode(`;0x${totalHex};`);
const chunk = new Uint8Array(12 + bytes);
chunk.set(head);
chunk.set(encoder.encode(`;0x${bytes.toString(16).padStart(8, "0")};`));
chunk.set(encodeData, 12);

@@ -280,4 +273,4 @@ return chunk;

}
const head = new TextDecoder().decode(this.buffer.subarray(1, 11));
const bytes = Number.parseInt(head, 16);
const decoder = new TextDecoder();
const bytes = Number.parseInt(decoder.decode(this.buffer.subarray(1, 11)), 16);
if (Number.isNaN(bytes)) {

@@ -292,3 +285,3 @@ throw new Error("Malformed server function stream.");

}
const partial = new TextDecoder().decode(this.buffer.subarray(12, 12 + bytes));
const partial = decoder.decode(this.buffer.subarray(12, 12 + bytes));
this.buffer = this.buffer.subarray(12 + bytes);

@@ -364,2 +357,110 @@ return {

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");
function getRequestEvent() {
return globalThis[RequestContext] ? globalThis[RequestContext].getStore() || sharedConfig.context && sharedConfig.context.event || console.warn("RequestEvent is missing. This is most likely due to accessing `getRequestEvent` non-managed async scope in a partially polyfilled environment. Try moving it above all `await` calls.") : undefined;
}
function reportLostHeaderWrite(method, name) {
const message = `Response header write dropped: headers.${method}(${JSON.stringify(String(name))}) ` + "ran after the response head was sent. Write headers before the shell flushes " + "(or before the handler returns).";
console.error(message);
}
function commitResponseStub(stub, {
allowLateLocation = false
} = {}) {
if (!stub || stub.committed) return stub;
stub.committed = true;
const headers = stub.headers;
if (!headers || typeof headers.set !== "function") return stub;
for (const method of ["set", "append", "delete"]) {
const original = headers[method].bind(headers);
headers[method] = function (name, ...rest) {
if (allowLateLocation && method === "set" && String(name).toLowerCase() === "location") {
return original(name, ...rest);
}
reportLostHeaderWrite(method, name);
};
}
return stub;
}
function copyInitHeaders(init) {
if (!init || !init.getSetCookie) return new Headers(init);
const headers = new Headers();
init.forEach((value, key) => {
if (key !== "set-cookie") headers.append(key, value);
});
for (const cookie of init.getSetCookie()) headers.append("Set-Cookie", cookie);
return headers;
}
const STUB_GAP_FILL_EXCLUDED = /*#__PURE__*/new Set([ERROR_HEADER, BODY_FORMAT_HEADER, SINGLE_FLIGHT_HEADER, REVALIDATE_HEADER, "Location"].map(header => header.toLowerCase()));
function fillsStubGap(key, headers, response) {
if (key === "set-cookie" || STUB_GAP_FILL_EXCLUDED.has(key)) return false;
if (response.body === null && (key === "content-type" || key === "content-length")) return false;
return !headers.has(key);
}
function commitEventResponse(response, event = getRequestEvent()) {
const stub = event && event.response;
if (!stub || !stub.headers || stub.committed) return response;
const cookies = stub.headers.getSetCookie ? stub.headers.getSetCookie() : [];
commitResponseStub(stub);
let hasGaps = false;
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, response.headers, response)) hasGaps = true;
});
if (!cookies.length && !hasGaps) return response;
try {
for (const cookie of cookies) response.headers.append("Set-Cookie", cookie);
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, response.headers, response)) response.headers.set(key, value);
});
return response;
} catch {
const headers = copyInitHeaders(response.headers);
for (const cookie of cookies) headers.append("Set-Cookie", cookie);
stub.headers.forEach((value, key) => {
if (fillsStubGap(key, headers, response)) headers.set(key, value);
});
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers
});
}
}
function encodeInputValue(value) {

@@ -394,9 +495,12 @@ if (value instanceof FormData) return {

};
return `${FLASH_COOKIE}=${encodeURIComponent(JSON.stringify(payload))}; Secure; HttpOnly; Path=/`;
return serializeCookie(FLASH_COOKIE, JSON.stringify(payload), {
secure: true,
httpOnly: true
});
}
function decodeFlashCookie(cookieHeader) {
const match = matchFlashCookie(cookieHeader);
const match = parseCookieHeader(cookieHeader)[FLASH_COOKIE];
if (!match) return;
try {
const payload = JSON.parse(decodeURIComponent(match));
const payload = JSON.parse(match);
if (!payload || !payload.result) return;

@@ -417,2 +521,3 @@ const result = payload.error ? new Error(payload.result) : payload.result;

provideEvent: undefined,
wrapInvocation: undefined,
collectFlightData: undefined,

@@ -427,2 +532,3 @@ transformResult: undefined,

provideEvent,
wrapInvocation,
collectFlightData,

@@ -437,2 +543,3 @@ transformResult,

if (provideEvent !== undefined) config.provideEvent = provideEvent;
if (wrapInvocation !== undefined) config.wrapInvocation = wrapInvocation;
if (collectFlightData !== undefined) config.collectFlightData = collectFlightData;

@@ -503,3 +610,9 @@ if (transformResult !== undefined) config.transformResult = transformResult;

const result = provideEvent(evt, () => {
return fn.apply(thisArg, args);
const run = () => fn.apply(thisArg, args);
return config.wrapInvocation ? config.wrapInvocation(run, {
id,
args,
event: evt,
direct: true
}) : run();
});

@@ -646,2 +759,12 @@ const transform = config.transformDirectResult;

}
function mergeResponseHeaders(target, source) {
source.forEach((value, key) => {
if (key !== "set-cookie") target.append(key, value);
});
if (source.getSetCookie) {
for (const cookie of source.getSetCookie()) target.append("Set-Cookie", cookie);
} else if (source.has("set-cookie")) {
target.append("Set-Cookie", source.get("set-cookie"));
}
}
const validRedirectStatuses = new Set([301, 302, 303, 307, 308]);

@@ -661,3 +784,4 @@ function createNoJSHandler({

if (result instanceof Response) {
headers = new Headers(result.headers);
headers = new Headers();
mergeResponseHeaders(headers, result.headers);
if (result.headers.has("Location")) {

@@ -715,2 +839,12 @@ headers.set("Location", new URL(result.headers.get("Location"), url.origin + base).toString());

}
const GENERIC_SERVER_ERROR_MESSAGE = "Internal Server Error";
let DEV = false === true;
function setServerFunctionsDev(dev) {
DEV = !!dev;
}
function sanitizeServerError(value) {
if (DEV) return value;
if (isSafeError(value)) return value;
return new Error(GENERIC_SERVER_ERROR_MESSAGE);
}
async function handleServerFunctionRequest(request, options = {}) {

@@ -722,3 +856,3 @@ const codec = options.codec !== undefined ? options.codec : getServerFunctionsCodec();

if (!functionId) {
return new Response(process.env.NODE_ENV === "development" ? "Server function not found" : null, {
return new Response(DEV ? "Server function not found" : null, {
status: 404

@@ -731,3 +865,3 @@ });

} catch {
return new Response(process.env.NODE_ENV === "development" ? `Unknown server function: ${functionId}` : null, {
return new Response(DEV ? `Unknown server function: ${functionId}` : null, {
status: 404

@@ -737,3 +871,3 @@ });

if (request.method === "GET" && METHODS.get(functionId) !== "GET") {
return new Response(process.env.NODE_ENV === "development" ? `Method not allowed for server function: ${functionId}` : null, {
return new Response(DEV ? `Method not allowed for server function: ${functionId}` : null, {
status: 405,

@@ -752,2 +886,3 @@ headers: {

const transformResult = options.transformResult !== undefined ? options.transformResult : config.transformResult;
const wrapInvocation = options.wrapInvocation !== undefined ? options.wrapInvocation : config.wrapInvocation;
const transformFlightResult = options.transformFlightResult !== undefined ? options.transformFlightResult : config.transformFlightResult;

@@ -767,129 +902,140 @@ const handleNoJS = options.handleNoJS !== undefined ? options.handleNoJS : config.handleNoJS !== undefined ? config.handleNoJS : isFormPost(request) ? defaultNoJSHandler || (defaultNoJSHandler = createNoJSHandler()) : undefined;

const headers = new Headers();
try {
let result = await provide(event, async () => {
INVOCATIONS.set(event, {
id: functionId
const dispatch = async () => {
try {
let result = await provide(event, async () => {
INVOCATIONS.set(event, {
id: functionId
});
const run = () => serverFunction(...parsed);
return wrapInvocation ? wrapInvocation(run, {
id: functionId,
args: parsed,
event,
request,
direct: false
}) : run();
});
return serverFunction(...parsed);
});
if (transformResult) {
result = await transformResult(event, result, flightContext);
}
let status = 200;
let metadata;
if (isResponseEnvelope(result)) {
const {
response,
value
} = result;
if (!instance && !handleNoJS && response && response.body) {
return response;
}
if (response && response.headers) {
response.headers.forEach((val, key) => headers.append(key, val));
}
if (response && response.status && (response.status < 300 || response.status >= 400)) {
status = response.status;
}
metadata = response;
result = value;
} else if (result instanceof Response) {
if (result.headers && result.headers.has("X-Content-Raw")) return result;
if (instance) {
if (result.headers) {
result.headers.forEach((value, key) => headers.append(key, value));
}
if (result.status && (result.status < 300 || result.status >= 400)) {
status = result.status;
}
metadata = result;
if (result.body == null) {
result = null;
}
}
}
if (collectsFlight) {
result = await foldFlightData(flightHook, event, headers, {
id: functionId,
value: result,
response: metadata,
request,
thrown: false
}, flightContext);
if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
}
if (!instance) {
if (handleNoJS) return handleNoJS(result, request, parsed);
if (result instanceof Response) return result;
return encodeResult(result, headers, 200, codec);
}
return encodeResult(result, headers, status, codec);
} catch (x) {
if (x instanceof Response || isResponseEnvelope(x)) {
if (transformResult) {
x = await transformResult(event, x, {
...flightContext,
thrown: true
});
result = await transformResult(event, result, flightContext);
}
let status = 200;
let metadata;
if (isResponseEnvelope(x)) {
if (isResponseEnvelope(result)) {
const {
response,
value
} = x;
} = result;
if (!instance && !handleNoJS && response && response.body) {
return response;
}
if (response && response.headers) {
response.headers.forEach((val, key) => headers.append(key, val));
mergeResponseHeaders(headers, response.headers);
}
if (response && response.status && (!instance || response.status < 300 || response.status >= 400)) {
if (response && response.status && (response.status < 300 || response.status >= 400)) {
status = response.status;
}
metadata = response;
x = value;
} else if (x instanceof Response) {
if (x.headers) {
x.headers.forEach((value, key) => headers.append(key, value));
result = value;
} else if (result instanceof Response) {
if (result.headers && result.headers.has("X-Content-Raw")) return result;
if (instance) {
if (result.headers) {
mergeResponseHeaders(headers, result.headers);
}
if (result.status && (result.status < 300 || result.status >= 400)) {
status = result.status;
}
metadata = result;
if (result.body == null) {
result = null;
}
}
if (x.status && (!instance || x.status < 300 || x.status >= 400)) {
status = x.status;
}
metadata = x;
if (x.body == null) {
x = null;
}
}
if (collectsFlight) {
x = await foldFlightData(flightHook, event, headers, {
result = await foldFlightData(flightHook, event, headers, {
id: functionId,
value: x,
value: result,
response: metadata,
request,
thrown: true
thrown: false
}, flightContext);
if (x instanceof Response && x.headers.has("X-Content-Raw")) {
x.headers.set(ERROR_HEADER, "true");
return x;
if (result instanceof Response && result.headers.has("X-Content-Raw")) return result;
}
if (!instance) {
if (handleNoJS) return handleNoJS(result, request, parsed);
if (result instanceof Response) return result;
return encodeResult(result, headers, 200, codec);
}
return encodeResult(result, headers, status, codec);
} catch (x) {
if (x instanceof Response || isResponseEnvelope(x)) {
if (transformResult) {
x = await transformResult(event, x, {
...flightContext,
thrown: true
});
}
let status = 200;
let metadata;
if (isResponseEnvelope(x)) {
const {
response,
value
} = x;
if (response && response.headers) {
mergeResponseHeaders(headers, response.headers);
}
if (response && response.status && (!instance || response.status < 300 || response.status >= 400)) {
status = response.status;
}
metadata = response;
x = value;
} else if (x instanceof Response) {
if (x.headers) {
mergeResponseHeaders(headers, x.headers);
}
if (x.status && (!instance || x.status < 300 || x.status >= 400)) {
status = x.status;
}
metadata = x;
if (x.body == null) {
x = null;
}
}
if (collectsFlight) {
x = await foldFlightData(flightHook, event, headers, {
id: functionId,
value: x,
response: metadata,
request,
thrown: true
}, flightContext);
if (x instanceof Response && x.headers.has("X-Content-Raw")) {
x.headers.set(ERROR_HEADER, "true");
return x;
}
}
headers.set(ERROR_HEADER, "true");
if (!instance) {
if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
if (x instanceof Response) return x;
}
return encodeResult(x, headers, status, codec);
}
headers.set(ERROR_HEADER, "true");
const safe = sanitizeServerError(x);
if (!instance) {
if (handleNoJS) return handleNoJS(x ?? metadata, request, parsed, true);
if (x instanceof Response) return x;
if (handleNoJS) return handleNoJS(safe, request, parsed, true);
const message = safe instanceof Error ? safe.message : String(safe);
return new Response(DEV ? message : null, {
status: 500
});
}
return encodeResult(x, headers, status, codec);
const error = safe instanceof Error ? safe.message : typeof safe === "string" ? safe : "true";
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
return encodeResult(safe, headers, 200, codec);
}
if (!instance) {
if (handleNoJS) return handleNoJS(x, request, parsed, true);
const message = x instanceof Error ? x.message : String(x);
return new Response(process.env.NODE_ENV === "development" ? message : null, {
status: 500
});
}
const error = x instanceof Error ? x.message : typeof x === "string" ? x : "true";
headers.set(ERROR_HEADER, encodeErrorHeaderValue(error));
return encodeResult(x, headers, 200, codec);
}
};
return commitEventResponse(await dispatch(), event);
}
export { ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, decodeResponsePayload, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getEventServerFunctionInvocation, getServerFunction, getServerFunctionInvocation, getServerFunctionMetadata, handleServerFunctionRequest, hasFlashCookie, isServerFunction, registerServerFunction, registerServerReference, subscribeFlightData, withMeta };
export { ERROR_HEADER, FLASH_COOKIE, FUNCTION_HEADER, GENERIC_SERVER_ERROR_MESSAGE, GET, INSTANCE_HEADER, SINGLE_FLIGHT_HEADER, clearFlashCookie, configureServerFunctionsServer, createNoJSHandler, createServerReference, decodeErrorHeaderValue, decodeFlashCookie, decodeResponse, decodeResponsePayload, encodeErrorHeaderValue, encodeFlashCookie, foldSetCookies, getEventServerFunctionInvocation, getServerFunction, getServerFunctionInvocation, getServerFunctionMetadata, handleServerFunctionRequest, hasFlashCookie, isServerFunction, registerServerFunction, registerServerReference, sanitizeServerError, setServerFunctionsDev, subscribeFlightData, withMeta };

@@ -20,2 +20,12 @@ {

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

@@ -22,0 +32,0 @@ "types": "../types/server-functions/server.d.ts",

@@ -18,3 +18,3 @@ import type { RequestEvent } from "@solidjs/web";

* return provideRequestEvent({ request, locals: {} }, () =>
* renderToStringAsync(() => <App />)
* renderToStream(() => <App />)
* );

@@ -21,0 +21,0 @@ * }

@@ -18,3 +18,3 @@ import type { RequestEvent } from "@solidjs/web";

* return provideRequestEvent({ request, locals: {} }, () =>
* renderToStringAsync(() => <App />)
* renderToStream(() => <App />)
* );

@@ -21,0 +21,0 @@ * }

import { JSX } from "./jsx.cjs";
import type { RequestEventLocals } from "./server.cjs";
// Element/property classification tables consumed by the JSX compiler and
// custom renderers. Compiler/tooling surface; not for hand-written code.
/** Compiler/tooling table; not for hand-written code. @internal */
export const DOMWithState: Record<string, Record<string, 1 | 2>>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const ChildProperties: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const DelegatedEvents: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const DOMElements: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const SVGElements: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const MathMLElements: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const VoidElements: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const RawTextElements: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const Namespaces: Record<string, string>;

@@ -20,2 +32,3 @@

/**
* Compiler-emitted primitive; not for hand-written code.
* @param flag

@@ -25,8 +38,21 @@ * - `undefined` — clone the template as-is (uses `cloneNode`).

* - `2` — the template html is wrapped; the outer tag is stripped at clone time.
* @internal
*/
export function template(html: string, flag?: 1 | 2): () => Element;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function scope<T extends () => any>(fn: T): T;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function effect<T>(fn: (prev?: T) => T, effect: (value: T, prev?: T) => void): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function memo<T>(fn: () => T, equal: boolean): () => T;
/**
* Compiler-emitted primitive; not for hand-written code — import `untrack`
* from `solid-js` instead.
* @internal
*/
export function untrack<T>(fn: () => T): T;
/**
* Compiler-emitted primitive; not for hand-written code.
* @internal
*/
export function insert<T>(

@@ -48,6 +74,11 @@ parent: MountableElement,

): JSX.Element;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function createComponent<T>(Comp: (props: T) => JSX.Element, props: T): JSX.Element;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function delegateEvents(eventNames: string[]): void;
/** Event-delegation plumbing (Portal/custom-root wiring). Integration plumbing. @internal */
export function registerDelegatedRoot(root: MountableElement): void;
/** Event-delegation plumbing (Portal/custom-root wiring). Integration plumbing. @internal */
export function unregisterDelegatedRoot(root: MountableElement): void;
/** Event-delegation plumbing (Portal/custom-root wiring). Integration plumbing. @internal */
export function registerDelegatedContainer(

@@ -57,2 +88,3 @@ container: MountableElement,

): void;
/** Event-delegation plumbing (Portal/custom-root wiring). Integration plumbing. @internal */
export function unregisterDelegatedContainer(

@@ -62,4 +94,7 @@ container: MountableElement,

): void;
/** Event-delegation plumbing (Portal/custom-root wiring). Integration plumbing. @internal */
export function getDelegatedRoot(node: MountableElement): MountableElement | undefined;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function spread<T>(node: Element, accessor: T, skipChildren?: Boolean): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function assign(

@@ -72,3 +107,5 @@ node: Element,

): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function setAttribute(node: Element, name: string, value: string): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function setAttributeNS(node: Element, namespace: string, name: string, value: string): void;

@@ -86,2 +123,6 @@ /**

* unregister function.
*
* Integration plumbing (routers register the consumer); not meant for
* application code.
* @internal
*/

@@ -92,2 +133,3 @@ export function registerElementClaim(handler: (element: Element) => void): () => void;

* Emitted by the compiler at element creation; idempotent by contract.
* @internal
*/

@@ -101,7 +143,14 @@ export function claimElement<T extends Element>(node: T): T;

* registered consumer.
*
* Integration plumbing; not meant for application code.
* @internal
*/
export function claimElementTree<T extends Node>(root: T): T;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function className(node: Element, value: JSX.ClassValue, prev?: JSX.ClassValue): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function setProperty(node: Element, name: string, value: any): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function setStyleProperty(node: Element, name: string, value: any): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function addEvent(

@@ -113,2 +162,3 @@ node: Element,

): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function style(

@@ -119,5 +169,17 @@ node: Element,

): void;
/**
* Compiler-emitted primitive; not for hand-written code — import `getOwner`
* from `solid-js` instead.
* @internal
*/
export function getOwner(): unknown;
/**
* Compiler-emitted prop-spread helper; not for hand-written code — import
* `merge` from `solid-js` instead.
* @internal
*/
export function mergeProps(...sources: unknown[]): unknown;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function dynamicProperty(props: unknown, key: string): unknown;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function applyRef<T extends Element = Element>(

@@ -127,2 +189,3 @@ r: ((element: NoInfer<T>) => void) | ((element: NoInfer<T>) => void)[],

): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function ref(

@@ -138,10 +201,10 @@ fn: () => ((element: Element) => void) | ((element: Element) => void)[],

): () => void;
/** Hydration-walk primitive; not for hand-written code. @internal */
export function getHydrationKey(): string | undefined;
/** Hydration-walk primitive; not for hand-written code. @internal */
export function getNextElement(template?: () => Element): Element;
/** Hydration-walk primitive; not for hand-written code. @internal */
export function getNextMatch(start: Node, elementName: string): Element;
/** Hydration-walk primitive; not for hand-written code. @internal */
export function getNextMarker(start: Node): [Node, Array<Node>];
/** @deprecated Use `useHead` — removed before `0.50.0` stable. */
export function useAssets(fn: () => JSX.Element): void;
/** @deprecated Use `useHead` — removed before `0.50.0` stable. */
export function getAssets(): string;
/**

@@ -151,2 +214,9 @@ * A head tag descriptor. Props values may be getters (reactive on the

* identity (`title` is a hard singleton that `key` cannot fork).
*
* Getters must be plain reads: they evaluate inside registry-owned
* computations here and at flush time on the server, so a getter that
* allocates a reactive owner (`createMemo`, a `children()` helper) consumes
* a hydration id slot on one side only and desyncs every id allocated after
* the `useHead` call. Create such helpers eagerly at component position and
* read them from the getter. See docs/head-management-rfc.md.
*/

@@ -180,3 +250,32 @@ export type HeadTag = {

}
/**
* @internal Ref-counted client asset ownership: acquire adopts or mounts the
* asset, the returned release follows the owner (with a grace period for
* back-and-forth navigation). Internal machinery, not a public CSS-lifecycle
* API — per the head-management RFC (docs/head-management-rfc.md), ambient
* bundler-injected CSS is never lifecycle-managed, and the head registry
* owns the lifecycle of directly-mounted stylesheets outright. This keeps
* its non-head roles (exclusive slots, owner-following DOM ownership).
*/
export function acquireAsset(descriptor: AssetDescriptor): () => void;
/**
* Registry entry returned by `warmAsset`. Stylesheet entries carry load
* tracking for the client reveal gate (docs/client-css-reveal-gating.md):
* `loadPromise` resolves on load OR error (never rejects) — an errored
* sheet releases the gate, parity with the server gate.
*/
export interface AssetEntry {
loadState?: "pending" | "loaded" | "errored";
loadPromise?: Promise<void>;
}
/**
* @internal Warm half of `acquireAsset`: idempotent and refcount-free,
* callable from a compute phase so the fetch starts at discovery and
* overlaps a transition's data wait. Stylesheets warm as
* `rel="preload" as="style"` and are flipped live by `acquireAsset` at
* commit — a branch superseded before it commits leaks only an inert
* preload, never an applied sheet. Only link-backed descriptors warm;
* inline styles and exclusive slots return `undefined`.
*/
export function warmAsset(descriptor: AssetDescriptor): AssetEntry | undefined;
export function HydrationScript(props?: { nonce?: string; eventNames?: string[] }): JSX.Element;

@@ -187,3 +286,2 @@ export function generateHydrationScript(options?: {

}): string;
export function Assets(props: { children?: JSX.Element }): JSX.Element;
/**

@@ -204,8 +302,31 @@ * See the server entry's `ResponseStub` — the shape of the mutable response

}
/**
* See the server entry's `RequestEventLocals` — the augmentable type of
* `RequestEvent.locals`. Re-exported (not re-declared) so both entries
* share ONE interface identity and a single augmentation reaches every
* `locals`, whichever entry typed the event.
*/
export type { RequestEventLocals } from "./server.cjs";
export interface RequestEvent {
request: Request;
locals: Record<string | number | symbol, any>;
locals: RequestEventLocals;
}
/**
* Registered symbol (`Symbol.for("solid.RequestContext")`) naming the global
* slot where `provideRequestEvent` parks the AsyncLocalStorage scoping
* request events. Integration plumbing — read the event through
* `getRequestEvent()` instead.
* @internal
*/
export declare const RequestContext: unique symbol;
export function getRequestEvent(): RequestEvent | undefined;
/**
* The cookie codec (the platform-gap primitives — see cookies.d.ts for the
* blessed patterns): the real implementation on both entries, never a
* stub — a pure value transformer has legitimate browser uses
* (`document.cookie`). Tree-shakes away when unused.
*/
export { parseCookieHeader, serializeCookie } from "./cookies.cjs";
export type { CookieOptions } from "./cookies.cjs";
/** Hydration-walk primitive; not for hand-written code. @internal */
export function runHydrationEvents(): void;
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration, runInServerComponentScope } from "solid-js";
export declare const effect: (fn: any, effectFn: any, options: any) => void;
export declare const effect: (fn: any, effectFn: any, options?: any) => void;
export declare const memo: (fn: any) => import("solid-js").SourceAccessor<any>;
export declare const runWithHydrationScope: (id: any, fn: any) => unknown;
export declare const ssrAsyncValue: (value: any) => import("solid-js").SourceAccessor<any>;
export declare const waitAsset: (promise: any) => void;
export { createFrame, createFrameHost, createFrameElement, FRAME_APPLIED_EVENT } from "./frame-client.cjs";
export { FRAME_STREAM_HEADER, applyFrameResponse, isFrameStreamResponse, createServerComponentHandler } from "./frame-transport.cjs";
export { createJSONDataTable } from "./serializer.cjs";
export type { Slot } from "./server.cjs";
/**
* Client-condition twin of the server face's `asyncArg` (DR-2 value tier):
* the identity that types an async value crossing the slot border as its
* settled value. Server component modules are authored in universal code and
* may resolve under the browser condition at typecheck/bundle time — the
* call never runs here (the `"use server"` body executes server-side), but
* the symbol must exist.
*/
export declare function asyncArg<T>(value: PromiseLike<T> | AsyncIterable<T>): T;
/**
* The app-wide shared frame host (created lazily): one chunk router with
* per-response codec data tables.
* @experimental
*/
export declare function getFrameHost(): any;

@@ -21,3 +34,4 @@ /**

* call again to rebind to a custom host.
* @experimental
*/
export declare function installServerComponents(host?: any): void;

@@ -9,5 +9,13 @@ /**

* place, and teardown is `dispose()`, never a version bump.
*
* EXPERIMENTAL — the frames/server-components surface ships as an
* experimental preview, excluded from the 2.0 stability guarantee: API
* shapes and the wire format may change between prereleases (RFC 11).
* Every export in this module is `@experimental`.
*/
/** One transport chunk of a frame stream, addressed by frame `id`. */
/**
* One transport chunk of a frame stream, addressed by frame `id`.
* @experimental
*/
export type FrameChunk =

@@ -52,2 +60,3 @@ | { type: "start"; id: string; version: number }

* its data hook.
* @experimental
*/

@@ -60,2 +69,3 @@ export function chunkToRecords(chunk: FrameChunk): Record<string, unknown>;

* stream stamp — an older version than the frame's current one is ignored.
* @experimental
*/

@@ -67,3 +77,6 @@ export interface FrameWrite {

/** Context passed to a slot callback. */
/**
* Context passed to a slot callback.
* @experimental
*/
export interface SlotContext {

@@ -124,5 +137,7 @@ /**

* the range, or `undefined` to claim `ctx.existing` untouched.
* @experimental
*/
export type Slot = (props: Record<string, unknown>, ctx: SlotContext) => Node | Node[] | undefined;
/** @experimental */
export interface Frame {

@@ -167,2 +182,3 @@ /** Merge a write into the store and flush (morph/reveal/slot sync). */

* delivery is seeded from a sibling's store.
* @experimental
*/

@@ -189,6 +205,10 @@ export interface FrameHost {

* reflection, e.g. `aria-current`) without a MutationObserver.
* @experimental
*/
export const FRAME_APPLIED_EVENT: "frame:applied";
/** Options for `createFrameHost`. */
/**
* Options for `createFrameHost`.
* @experimental
*/
export interface FrameHostOptions {

@@ -211,5 +231,9 @@ /**

/** @experimental */
export function createFrameHost(options?: FrameHostOptions): FrameHost;
/** Options for `createFrame` / `createFrameElement`. */
/**
* Options for `createFrame` / `createFrameElement`.
* @experimental
*/
export interface FrameOptions {

@@ -228,3 +252,3 @@ /** Register with this host under `id`, receiving routed/buffered chunks. */

/** Called after each apply flush (tests/telemetry). */
onApply?(info: { version: number; reason: "materialize" | "morph" | "reveal" }): void;
onApply?(info: { version: number; reason: "materialize" | "morph" | "reveal" | "error" }): void;
/**

@@ -271,8 +295,13 @@ * Wraps element-claim sweeps (`a[href]`/`form[action]` in materialized

* chunk.
* @experimental
*/
export function createFrame(boundary: Element, options?: FrameOptions): Frame;
/** The default boundary/region element tag and its id attribute — the DOM
* contract the producer emits at t=0 and the consumer creates/adopts. */
/**
* The default boundary/region element tag and its id attribute — the DOM
* contract the producer emits at t=0 and the consumer creates/adopts.
* @experimental
*/
export const FRAME_TAG: "dx-frame";
/** @experimental */
export const FRAME_ID_ATTR: "data-fid";

@@ -287,2 +316,3 @@

* `dispose()` (register it with your owner's cleanup).
* @experimental
*/

@@ -289,0 +319,0 @@ export function createFrameElement(options: FrameOptions): {

@@ -0,4 +1,11 @@

// EXPERIMENTAL — the frames/server-components surface ships as an
// experimental preview, excluded from the 2.0 stability guarantee: API
// shapes and the wire format may change between prereleases (RFC 11).
// Every export in this module is @experimental.
import { FrameChunk } from "./frame-client.cjs";
/** Addresses a frame stream: the boundary id and this response's version. */
/**
* Addresses a frame stream: the boundary id and this response's version.
* @experimental
*/
export interface FrameAddress {

@@ -15,2 +22,3 @@ id: string;

* `renderServerComponent` instead.
* @experimental
*/

@@ -22,3 +30,6 @@ export function createFrameSink(

/** Options shared by the frame producers. */
/**
* Options shared by the frame producers.
* @experimental
*/
export interface FrameStreamOptions {

@@ -31,3 +42,6 @@ /** Boundary address; defaults to `{ id: "", version: 1 }`. */

/** A produced frame stream: pipe chunks, or await the collected array. */
/**
* A produced frame stream: pipe chunks, or await the collected array.
* @experimental
*/
export interface FrameStream extends PromiseLike<FrameChunk[]> {

@@ -43,2 +57,3 @@ pipe(writable: { write(chunk: FrameChunk): void; end?(): void }): void;

* `createJSONDataTable`).
* @experimental
*/

@@ -64,2 +79,3 @@ export function renderToFrameStream(code: () => unknown, options?: FrameStreamOptions): FrameStream;

* function's arguments.
* @experimental
*/

@@ -78,2 +94,3 @@ export function renderServerComponent(

* @internal Exposed for framework bindings composing their own producers.
* @experimental
*/

@@ -91,2 +108,3 @@ export function createSlotProps(

* merges in; the frame tags win on conflict.
* @experimental
*/

@@ -113,2 +131,3 @@ export function serverComponentResponse(

* ```
* @experimental
*/

@@ -126,2 +145,3 @@ export function frameTransformResult(event: unknown, result: unknown): unknown;

* regions onto the server-rendered ranges.
* @experimental
*/

@@ -140,2 +160,3 @@ export function createDocumentSlotProps(

* Non-function results pass through.
* @experimental
*/

@@ -156,2 +177,3 @@ export function frameTransformDirectResult<T>(

* single-flight envelope).
* @experimental
*/

@@ -181,3 +203,4 @@ export function frameTransformFlightResult(

* `installServerComponents()`.
* @experimental
*/
export const SERVER_COMPONENT_BOOTSTRAP: string;

@@ -0,1 +1,5 @@

// EXPERIMENTAL — the frames/server-components surface ships as an
// experimental preview, excluded from the 2.0 stability guarantee: API
// shapes and the wire format may change between prereleases (RFC 11).
// Every export in this module is @experimental.
import { FrameChunk, FrameHost } from "./frame-client.cjs";

@@ -14,9 +18,16 @@ import { JSONCodecOptions } from "./serializer.cjs";

* `BodyFormat` entry, since the body is frame chunks, not a serialized value.
* @experimental
*/
export const FRAME_STREAM_HEADER: "X-Frame-Stream";
/** Whether a fetch Response carries a frame stream. */
/**
* Whether a fetch Response carries a frame stream.
* @experimental
*/
export function isFrameStreamResponse(response: Response): boolean;
/** Options for `applyFrameResponse`. */
/**
* Options for `applyFrameResponse`.
* @experimental
*/
export interface ApplyFrameResponseOptions {

@@ -61,2 +72,3 @@ /**

* ```
* @experimental
*/

@@ -69,9 +81,18 @@ export function applyFrameResponse(

/** Brands an inline-rendered server component with its function id. */
/**
* Brands an inline-rendered server component with its function id.
* @experimental
*/
export const SERVER_COMPONENT: unique symbol;
/** The unwrapped server component behind an inline-render wrap. */
/**
* The unwrapped server component behind an inline-render wrap.
* @experimental
*/
export const SERVER_COMPONENT_SOURCE: unique symbol;
/** The call's wire address (`frameAddress`), for regions to be emitted under. */
/**
* The call's wire address (`frameAddress`), for regions to be emitted under.
* @experimental
*/
export const SERVER_COMPONENT_ADDRESS: unique symbol;

@@ -87,6 +108,10 @@

* frameworks can honor it without importing this module.
* @experimental
*/
export const COMPONENT_BINDING: unique symbol;
/** The value under `COMPONENT_BINDING` on a transport-resolved binding. */
/**
* The value under `COMPONENT_BINDING` on a transport-resolved binding.
* @experimental
*/
export interface ComponentBinding<C = unknown> {

@@ -105,2 +130,3 @@ /** The per-function mount component (the equals-gate identity). */

* rides as data.
* @experimental
*/

@@ -116,2 +142,3 @@ export const ServerComponentPlugin: unknown;

* bootstrap text.
* @experimental
*/

@@ -124,6 +151,10 @@ export function setServerComponentBootstrap(resolve: (ctx: unknown) => string): void;

* legs; exported for integrations composing their own flight carriers.
* @experimental
*/
export function flightCodec(codec?: JSONCodecOptions): JSONCodecOptions;
/** Options for `createServerComponentHandler`. */
/**
* Options for `createServerComponentHandler`.
* @experimental
*/
export interface ServerComponentHandlerOptions<C = unknown> {

@@ -180,2 +211,3 @@ host: FrameHost;

* to warms its store (preload isolation is the default, not a rule).
* @experimental
*/

@@ -182,0 +214,0 @@ export function createServerComponentHandler<C>(options: ServerComponentHandlerOptions<C>): {

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

import { Plugin, Serializer, SerovalNode } from "seroval";
// Serialization surface (published as `@solidjs/web/serialization`): the
// runtime's Seroval machinery, exposed for the runtime's own entries and
// for integrations building transports on the same codec. This is
// INTEGRATION-FACING plumbing, not application API — it is exempt from the
// 2.0 stability guarantee and may change between releases. Application and
// router code should configure `codec` on the server-function entries
// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";

@@ -6,13 +13,123 @@ /**

* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// ---- Plugin authoring ----
//
// Unlike the rest of this entry, plugin authoring is APPLICATION-FACING —
// it is the supported way to feed the serializers' `plugins` options and
// the server-function entries' `codec.plugins`. The values re-export
// 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.
/** 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. Supply matching plugins on both
* peers of a transport.
* 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 type SerializerPlugin = Plugin<any, any>;
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
* plugin authors stay on the exact seroval instance/version the runtime
* serializes with. Import it from HERE, not from your own `seroval`
* dependency: a plugin built against a different copy/version would not
* fail the build — it would emit nodes the other peer can't interpret.
*
* Application-facing (see the plugin-authoring banner above).
*/
export function createPlugin<Value, Info extends PluginInfo>(
plugin: SerializerPlugin<Value, Info>
): SerializerPlugin<Value, Info>;
/**
* Seroval's `OpaqueReference`, re-exported from the runtime's own instance
* (an `OpaqueReference` from another seroval copy fails the serializer's
* instanceof check and serializes as a plain value): wraps a value so it
* crosses the wire as its `replacement` (default `undefined`) while
* staying readable in-process through `.value`.
*
* Application-facing (see the plugin-authoring banner above).
*/
export class OpaqueReference<V, R = undefined> {
readonly value: V;
readonly replacement?: R;
constructor(value: V, replacement?: R);
}
/**
* Baseline plugin set for serializing web-platform values (AbortSignal,

@@ -22,2 +139,4 @@ * Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).

* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -31,6 +150,12 @@ export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];

* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/** Options for `createSerializer`. */
/**
* Options for `createSerializer`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface WebSerializerOptions {

@@ -64,2 +189,4 @@ /** Name of the global object the emitted scripts write resolved values into. */

* `serializeJSON` / `createJSONDeserializer` instead.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -103,2 +230,4 @@ export function createSerializer(options: WebSerializerOptions): Serializer;

* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -121,3 +250,7 @@ export interface JSONCodecOptions {

/** Options for `serializeJSON`. */
/**
* Options for `serializeJSON`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONSerializeOptions extends JSONCodecOptions {

@@ -141,2 +274,4 @@ /**

* function that aborts pending async serialization.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -151,6 +286,34 @@ export function serializeJSON(value: unknown, options: JSONSerializeOptions): () => void;

* 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`. */
export interface JSONSerializerOptions extends JSONCodecOptions {
/**
* Receives each keyed record — `initial` is true for a key's first node
* (the written value itself); async values patch through later records
* under the same key. The decoding peer is `createJSONDataTable`.
*/
onData: (record: { key: string; node: SerovalNode; initial: boolean }) => void;
onError?: (error: unknown) => void;
/** Fires once `flush()` has been called and every pending value settled. */
onDone?: () => void;
}
/**
* The keyed, streaming encoder of the eval-free JSON codec — the render
* stream's data serializer (frames default to it). Each `write(key, value)`
* shares one reference space, so cross-record identity holds; `flush()`
* marks the write set complete (writes after it are dropped, mirroring the
* hydration serializer); `close()` aborts pending async serialization.
*/
export function createJSONSerializer(options: JSONSerializerOptions): {
write(key: string, value: unknown): void;
flush(): void;
close(): void;
};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply

@@ -160,2 +323,6 @@ * each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with

* (`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.
*/

@@ -162,0 +329,0 @@ export interface JSONDataTable {

@@ -25,2 +25,3 @@ import type { Element as SolidElement } from "solid-js";

* the right default; `$key` matters when a live list reorders.
* @experimental
*/

@@ -30,3 +31,24 @@ export type Slot<P = {}> = (props: P & {

}) => SolidElement;
/**
* Types an async value crossing the slot border (DR-2, value tier). What you
* pass is what ships — the promise / async iterable itself rides the data
* channel — but the client's prop READ settles: it suspends into the covering
* boundary until first arrival (a promise's resolution, an iterable's first
* yield), then reads as the settled value, updating per yield for iterables.
*
* `asyncArg` is the type-level statement of that contract: identity at
* runtime, settled type at the border, so `Slot<P>` keeps the fill's props
* truthful to what its reads actually return.
*
* Slots render as JSX — the compiler wraps each prop in a getter so the read
* defers to the slot border, where the runtime owns it. A call form
* (`props.status({ … })`) evaluates its args eagerly in the component body —
* a top-level read, an error in most cases.
*
* ```tsx
* <props.status progress={asyncArg(gen.progress)} stats={asyncArg(gen.stats)} />
* ```
*/
export declare function asyncArg<T>(value: PromiseLike<T> | AsyncIterable<T>): T;
export { renderToFrameStream, renderServerComponent, serverComponentResponse, frameTransformResult, frameTransformFlightResult, createFrameSink, frameTransformDirectResult, ServerComponentPlugin, SERVER_COMPONENT_BOOTSTRAP } from "./frame-sink.cjs";
export { FRAME_STREAM_HEADER, isFrameStreamResponse } from "./frame-transport.cjs";

@@ -99,5 +99,4 @@ import { hydrate as hydrateCore } from "./client.cjs";

*
* Use this when the page HTML was produced by `renderToString`,
* `renderToStringAsync`, or `renderToStream`. For client-only apps, use
* `render` instead.
* Use this when the page HTML was produced by `renderToString` or
* `renderToStream`. For client-only apps, use `render` instead.
*

@@ -104,0 +103,0 @@ * Pass `options.renderId` to hydrate one of multiple roots emitted by a

@@ -53,2 +53,47 @@ /**

/**
* Registered-symbol brand (`Symbol.for("solid.SafeError")`) marking a thrown
* value as safe to serialize to the client verbatim. Declared `unique
* symbol` type-side; the runtime value is the registered symbol, so
* separately bundled copies agree on identity.
*/
export declare const SAFE_ERROR: unique symbol;
/**
* Marks `error` as safe to serialize to the client verbatim, opting it out
* of the server-function handler's production error sanitization.
*
* By default a plain `Error` thrown from a server function is sanitized to a
* generic `Error` outside the dev build (the `development` export condition
* selects the full-fidelity copy; every other resolution sanitizes):
* its `message`, `stack`, and own-properties are dropped so a driver/ORM
* error can't leak a failing query or connection string over the wire.
* Dev builds keep full fidelity. This is the escape hatch for errors whose
* content is *intentional* client-facing information — brand them and their
* message/properties travel intact in every environment.
*
* The brand is a non-enumerable, symbol-keyed property, so it never itself
* serializes as an own-property. Sets the brand and returns the same value.
*
* @example
* ```ts
* import { markSafeError } from "@solidjs/web";
*
* async function transfer(amount: number) {
* "use server";
* if (amount > balance) {
* // The user must see this message; opt out of sanitization.
* throw markSafeError(new Error("Insufficient funds"));
* }
* }
* ```
*/
export function markSafeError<E>(error: E): E;
/**
* Whether `value` is branded safe to serialize (via `markSafeError`).
* Registered-symbol check, correct across duplicated module instances.
*/
export function isSafeError(value: unknown): value is Error;
/**
* Response header naming the cache keys a mutation invalidated

@@ -55,0 +100,0 @@ * (`"X-Revalidate"`), comma separated. The response helpers below set it

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

import { Plugin, Serializer, SerovalNode } from "seroval";
// Serialization surface (published as `@solidjs/web/serialization`): the
// runtime's Seroval machinery, exposed for the runtime's own entries and
// for integrations building transports on the same codec. This is
// INTEGRATION-FACING plumbing, not application API — it is exempt from the
// 2.0 stability guarantee and may change between releases. Application and
// router code should configure `codec` on the server-function entries
// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";

@@ -6,13 +13,123 @@ /**

* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// ---- Plugin authoring ----
//
// Unlike the rest of this entry, plugin authoring is APPLICATION-FACING —
// it is the supported way to feed the serializers' `plugins` options and
// the server-function entries' `codec.plugins`. The values re-export
// 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.
/** 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. Supply matching plugins on both
* peers of a transport.
* 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 type SerializerPlugin = Plugin<any, any>;
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
* plugin authors stay on the exact seroval instance/version the runtime
* serializes with. Import it from HERE, not from your own `seroval`
* dependency: a plugin built against a different copy/version would not
* fail the build — it would emit nodes the other peer can't interpret.
*
* Application-facing (see the plugin-authoring banner above).
*/
export function createPlugin<Value, Info extends PluginInfo>(
plugin: SerializerPlugin<Value, Info>
): SerializerPlugin<Value, Info>;
/**
* Seroval's `OpaqueReference`, re-exported from the runtime's own instance
* (an `OpaqueReference` from another seroval copy fails the serializer's
* instanceof check and serializes as a plain value): wraps a value so it
* crosses the wire as its `replacement` (default `undefined`) while
* staying readable in-process through `.value`.
*
* Application-facing (see the plugin-authoring banner above).
*/
export class OpaqueReference<V, R = undefined> {
readonly value: V;
readonly replacement?: R;
constructor(value: V, replacement?: R);
}
/**
* Baseline plugin set for serializing web-platform values (AbortSignal,

@@ -22,2 +139,4 @@ * Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).

* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -31,6 +150,12 @@ export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];

* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/** Options for `createSerializer`. */
/**
* Options for `createSerializer`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface WebSerializerOptions {

@@ -64,2 +189,4 @@ /** Name of the global object the emitted scripts write resolved values into. */

* `serializeJSON` / `createJSONDeserializer` instead.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -103,2 +230,4 @@ export function createSerializer(options: WebSerializerOptions): Serializer;

* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -121,3 +250,7 @@ export interface JSONCodecOptions {

/** Options for `serializeJSON`. */
/**
* Options for `serializeJSON`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONSerializeOptions extends JSONCodecOptions {

@@ -141,2 +274,4 @@ /**

* function that aborts pending async serialization.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -151,6 +286,34 @@ export function serializeJSON(value: unknown, options: JSONSerializeOptions): () => void;

* 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`. */
export interface JSONSerializerOptions extends JSONCodecOptions {
/**
* Receives each keyed record — `initial` is true for a key's first node
* (the written value itself); async values patch through later records
* under the same key. The decoding peer is `createJSONDataTable`.
*/
onData: (record: { key: string; node: SerovalNode; initial: boolean }) => void;
onError?: (error: unknown) => void;
/** Fires once `flush()` has been called and every pending value settled. */
onDone?: () => void;
}
/**
* The keyed, streaming encoder of the eval-free JSON codec — the render
* stream's data serializer (frames default to it). Each `write(key, value)`
* shares one reference space, so cross-record identity holds; `flush()`
* marks the write set complete (writes after it are dropped, mirroring the
* hydration serializer); `close()` aborts pending async serialization.
*/
export function createJSONSerializer(options: JSONSerializerOptions): {
write(key: string, value: unknown): void;
flush(): void;
close(): void;
};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply

@@ -160,2 +323,6 @@ * each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with

* (`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.
*/

@@ -162,0 +329,0 @@ export interface JSONDataTable {

@@ -24,2 +24,3 @@ import { JSONCodecOptions } from "../serializer.cjs";

isServerFunction,
serializeString,
subscribeFlightData,

@@ -26,0 +27,0 @@ withMeta

@@ -123,2 +123,29 @@ import { ResponseEnvelope } from "../response.cjs";

/**
* Wraps a server function execution — the per-invocation seam for
* framework policies (per-function middleware, auth, logging, error
* mapping). Called inside the call's event scope with the invocation
* identity already established: `getServerFunctionInvocation()` answers
* before, during and after `run()`. Must return (or resolve to) `run()`'s
* result — replacing it replaces the function's result; throwing routes
* through the handler's normal error encoding.
*
* The context carries the call's identity (`id`, parsed `args`), its
* `event`, and how it arrived: `direct` is `true` for in-process SSR calls
* (where `request` is absent) and `false` for HTTP dispatch. On the direct
* path the wrapper must stay transparent for synchronous functions —
* return `run()`'s value, not an unconditional promise, unless it needs to
* be async.
*/
export type WrapInvocationHook = (
run: () => unknown,
context: {
id: string;
args: unknown[];
event: ServerFunctionEvent;
request?: Request;
direct: boolean;
}
) => unknown;
/**
* Request headers with `setCookies` folded into the `Cookie` header, as the

@@ -185,2 +212,10 @@ * browser would have applied them before its next request. Later entries

/**
* Wraps every server function execution — HTTP dispatch and direct SSR
* calls alike — with the invocation identity already established (see
* `WrapInvocationHook`). The per-invocation seam for framework policies:
* per-function middleware, auth, logging, error mapping. A per-request
* option overrides it for HTTP dispatch.
*/
wrapInvocation?: WrapInvocationHook;
/**
* The single-flight hook: produces the data payload folded into

@@ -408,2 +443,9 @@ * responses of calls that opted in (see `CollectFlightDataHook`).

/**
* Overrides the configured per-invocation wrap for this handler — same
* contract as the `wrapInvocation` config option (see
* `WrapInvocationHook`), except it only applies to HTTP dispatch (a
* per-request option can't see direct SSR calls).
*/
wrapInvocation?: WrapInvocationHook;
/**
* Observes or replaces the function's result before encoding — the

@@ -479,2 +521,37 @@ * extension point for response metadata policies (headers, statuses,

*
* When the event carries a `response` head stub (`event.response`, see the
* server entry's `ResponseStub`), the handler folds it onto every outgoing
* response as the head freezes — its `Set-Cookie` values (cookies appended
* during the call) append cookie-by-cookie alongside the result's own,
* other stub headers fill gaps (the call's response metadata wins; the
* protocol-owned family — the error/format/single-flight tags, `Location`,
* `X-Revalidate` — never fills, and neither does `Content-Type`/`Content-
* Length` onto a bodiless response) — and marks the stub `committed`, so
* later cookie/header writes report instead of silently missing the wire.
*
* ## Thrown-error sanitization (security default)
*
* A thrown `Response`/envelope (`redirect`/`reload`/`respond`) is intentional
* control flow and is forwarded untouched. A *plain* thrown value (a bare
* `Error`, string, or object) is different: serialized verbatim it would ship
* its `message` and every own-property to the client — a driver/ORM error's
* failing query, connection string, or bound parameters included. So outside
* the dev build a plain thrown value is replaced with a generic `Error`
* before serialization; the client still receives *an* `Error` (the shape
* `submission.error` etc. expect), just with no leaked content. The dev
* build keeps full fidelity (message, stack, own-props) for DX and the dev
* toolbar inspector. Dev/prod is the BUILD VARIANT, not `NODE_ENV`:
* `@solidjs/web` publishes a dev copy of this entry behind the
* `development` export condition (what Vite dev resolves) and the default
* resolution sanitizes — as does importing the runtime source directly with
* no bundler signal (fail-safe).
*
* Escape hatch: brand the value with `markSafeError` (`Symbol.for(
* "solid.SafeError")`) to send its content intact in every environment.
* A `wrapInvocation`/`transformResult` override that maps errors expresses
* intent the same way — throw a `Response`/envelope, or brand the mapped
* error safe; an unbranded plain error it lets propagate is sanitized like
* any other, so a framework onError policy must brand its result to keep a
* custom client-facing message in production.
*
* @example

@@ -495,1 +572,22 @@ * ```ts

): Promise<Response>;
/** Message a sanitized (production) server error carries on the wire. */
export const GENERIC_SERVER_ERROR_MESSAGE: string;
/**
* The production error-sanitization policy `handleServerFunctionRequest`
* applies to a plain thrown value before serialization. Returns `value`
* unchanged in the dev build or when it is branded safe (`markSafeError`);
* otherwise returns a generic `Error` carrying `GENERIC_SERVER_ERROR_MESSAGE`.
* Exposed for frameworks composing their own dispatch around the same policy.
*/
export function sanitizeServerError(value: unknown): unknown;
/**
* Overrides the build-variant dev flag for this module instance — the seam
* for test harnesses and hand-rolled bundles whose packaging cannot replace
* `_DX_DEV_`. Applications never call this; select the dev build through
* the `development` export condition instead.
* @internal
*/
export function setServerFunctionsDev(dev: boolean): void;

@@ -21,2 +21,5 @@ import { JSONCodecOptions } from "../serializer.cjs";

* lower-level codec helpers so custom plugins configured by the app apply.
*
* Integration plumbing; not meant for hand-written application code.
* @internal
*/

@@ -61,2 +64,5 @@ export function getServerFunctionsCodec(): JSONCodecOptions | undefined;

* surrogates are replaced with U+FFFD — they cannot survive UTF-8 anyway).
*
* Transport wire detail; not meant for hand-written code.
* @internal
*/

@@ -69,2 +75,6 @@ export function encodeErrorHeaderValue(value: string): string;

* peers that never encode) passes through untouched.
*
* Integration plumbing for readers of `ERROR_HEADER`; not meant for
* hand-written application code.
* @internal
*/

@@ -418,2 +428,5 @@ export function decodeErrorHeaderValue(value: string): string;

* stays core's own.
*
* Integration plumbing; not meant for hand-written application code.
* @internal
*/

@@ -429,2 +442,5 @@ export function decodeResponsePayload<T = unknown, D = unknown>(

* responses and frame streams) share this framing.
*
* Transport wire detail; not meant for hand-written code.
* @internal
*/

@@ -437,2 +453,5 @@ export function createChunk(data: string): Uint8Array;

* buffering partial frames internally until their length prefix is satisfied.
*
* Transport wire detail; not meant for hand-written code.
* @internal
*/

@@ -450,3 +469,6 @@ export class ChunkReader {

* realms and releases.
*
* Transport wire detail; not meant for hand-written code.
* @internal
*/
export function frameAddress(id: string, args?: readonly unknown[]): string;

@@ -0,5 +1,46 @@

import type { RequestEvent, RequestEventLocals, ResponseStub } from "./client.cjs";
/** Static asset manifest produced by a build (e.g. parsed Vite manifest.json). */
export type AssetManifest = Record<string, {
file: string;
css?: string[];
isEntry?: boolean;
imports?: string[];
}> & {
_base?: string;
};
/** Inline style content, e.g. dev CSS collected from a bundler's module graph. */
export type InlineStyleAsset = {
id: string;
content: string;
attrs?: Record<string, string>;
};
export type ResolvedAssets = {
js: string[];
css: (string | InlineStyleAsset)[];
};
/**
* Resolver form of the manifest option — the primitive a dev server
* implements against its live module graph (a static manifest object is
* normalized into a sync resolver internally). `resolve` may return a
* promise (async resolvers require streaming rendering); CSS entries may be
* URL strings (emitted as load-gated `<link>` tags) or inline-style
* descriptors (emitted as `<style>` tags). A bare `resolve`-shaped function
* is accepted as shorthand for `{ resolve }`.
*/
export type AssetResolver = {
resolve(key: string): ResolvedAssets | null | undefined | Promise<ResolvedAssets | null | undefined>;
/**
* Synchronous fast path answering with whatever is knowable without async
* work (typically js URLs, omitting css). Sync consumers — e.g. a lazy
* component's `moduleUrl` getter used by islands — use this when `resolve`
* would return a promise, so adapters should provide it whenever possible.
*/
resolveSync?(key: string): ResolvedAssets | null | undefined;
};
/** Bare-function shorthand for `AssetResolver` (no sync fast path). */
export type AssetResolverFn = (key: string) => ResolvedAssets | null | undefined | Promise<ResolvedAssets | null | undefined>;
/**
* Renders a component tree synchronously to an HTML string. Async reads inside
* `<Loading>` boundaries emit their `fallback` content; for full-graph
* resolution use `renderToStringAsync` instead.
* resolution await `renderToStream` instead.
*

@@ -21,42 +62,17 @@ * Pair the returned HTML with `hydrate()` on the client.

plugins?: any[];
manifest?: Record<string, {
file: string;
css?: string[];
isEntry?: boolean;
isDynamicEntry?: boolean;
imports?: string[];
}>;
manifest?: AssetManifest | AssetResolver | AssetResolverFn;
onError?: (err: any) => void;
/**
* Embedded-render contract for hosts that own the document. When the
* render output contains no `</head>`, everything head-bound (resolved
* `useHead` winners, eager resources, tracked asset links, inline
* styles) is delivered here as one HTML string — prelude (charset/base)
* first — for the host to splice into its own `<head>` template, instead
* of being dropped. Called synchronously before `renderToString`
* returns; not called when the output has a `</head>` (splicing is
* automatic then).
*/
onHead?: (head: string) => void;
}): string;
/**
* Renders a component tree to an HTML string and awaits all async reads in the
* subtree before resolving. The returned HTML reflects the fully-settled state
* — no `<Loading>` fallbacks appear in the output.
*
* Use this when you want a complete page in one round-trip. For incremental
* streaming with progressive boundary resolution, use `renderToStream`.
*
* @example
* ```tsx
* import { renderToStringAsync } from "@solidjs/web";
*
* const html = await renderToStringAsync(() => <App />);
* ```
*/
export declare function renderToStringAsync<T>(fn: () => T, options?: {
timeoutMs?: number;
nonce?: string;
renderId?: string;
noScripts?: boolean;
plugins?: any[];
manifest?: Record<string, {
file: string;
css?: string[];
isEntry?: boolean;
isDynamicEntry?: boolean;
imports?: string[];
}>;
onError?: (err: any) => void;
}): Promise<string>;
/**
* Streams an HTML response, flushing the synchronous shell first and then

@@ -68,5 +84,6 @@ * progressively emitting async-resolved fragments as their `<Loading>`

* a Web `WritableStream`, a lazy `readable` byte-stream view for
* `new Response(stream.readable)`, plus a `then` for awaiting full
* completion. `pipe`, `pipeTo`, and `readable` each consume the render —
* use exactly one of the three.
* `new Response(stream.readable)`, plus a thenable for awaiting full
* completion — `await renderToStream(...)` resolves with the settled HTML
* (the fully-resolved-string form of the render). `pipe`, `pipeTo`, and
* `readable` each consume the render — use exactly one of the three.
*

@@ -82,2 +99,5 @@ * @example

* return new Response(renderToStream(() => <App />).readable);
*
* // Fully settled string:
* const html = await renderToStream(() => <App />);
* ```

@@ -90,9 +110,3 @@ */

plugins?: any[];
manifest?: Record<string, {
file: string;
css?: string[];
isEntry?: boolean;
isDynamicEntry?: boolean;
imports?: string[];
}>;
manifest?: AssetManifest | AssetResolver | AssetResolverFn;
onCompleteShell?: (info: {

@@ -105,4 +119,21 @@ write: (v: string) => void;

onError?: (err: any) => void;
/**
* Embedded-render contract for hosts that own the document. When the
* shell contains no `</head>`, everything head-bound at first flush
* (resolved `useHead` winners, eager resources, tracked asset links,
* inline styles) is delivered here as one HTML string — prelude first —
* before the shell chunk is emitted, so the host can write its own
* `<head>` ahead of piping the stream. Post-shell head updates flow
* through the stream itself and apply in the browser. Not called when
* the shell has a `</head>` (splicing is automatic then).
*/
onHead?: (head: string) => void;
}): {
then: (fn: (html: string) => void) => void;
/**
* Awaiting the stream resolves with the complete HTML once every boundary
* settles — the fully-settled-string form of the render. Render errors
* route through `onError` and the promise resolves with whatever HTML the
* render produced; it never rejects.
*/
then<TResult1 = string, TResult2 = never>(onfulfilled?: ((html: string) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
pipe: (writable: {

@@ -116,2 +147,71 @@ write: (v: string) => void;

/**
* Fetch-style middleware: receives the `Request` and a `next` continuation
* (pass a `Request` to substitute it downstream) and returns the `Response`.
* Composed with `composeMiddleware`; runs inside the request-event scope, so
* `getRequestEvent()` works exactly as in application code.
*/
export type FetchMiddleware = (request: Request, next: (request?: Request) => Promise<Response>) => Response | Promise<Response>;
/**
* Creates a fresh, uncommitted {@link ResponseStub}. Server-only.
*/
export declare function createResponseStub(): ResponseStub;
/**
* Builds the canonical request event — a web-standard `Request`, a `locals`
* bag, and a stub-backed `response` head — for `provideRequestEvent`.
* Server-only: on the client the request event belongs to the server that
* rendered the page.
*/
export declare function createRequestEvent<T extends object = {}>(request: Request, init?: T): {
request: Request;
locals: RequestEventLocals;
response: ResponseStub;
} & T;
/**
* The HTTP status a redirect should use: the stub's own status when it is a
* redirect status (301/302/303/307/308), 302 otherwise. Server-only.
*/
export declare function getExpectedRedirectStatus(response: ResponseStub): number;
/**
* Derives the outgoing `Response` for an SSR render result, running the
* response-head lifecycle against `event.response`: the stub commits at
* shell flush, a pre-flush `Location` becomes a real redirect
* (`getExpectedRedirectStatus`), and a post-flush one appends the
* nonce-aware `<script>window.location=...</script>` fallback. String
* results return a `Response` synchronously; stream results resolve at
* shell flush. Server-only.
*/
export declare function createSSRResponse(result: string, event: RequestEvent | undefined, options?: {
responseInit?: ResponseInit;
nonce?: string;
transformChunk?: (chunk: string) => string;
}): Response;
export declare function createSSRResponse(result: {
pipe(writable: {
write: (v: string) => void;
end: () => void;
}): void;
}, event: RequestEvent | undefined, options?: {
responseInit?: ResponseInit;
nonce?: string;
transformChunk?: (chunk: string) => string;
}): Promise<Response>;
/**
* Handler-lifecycle plumbing — the exit for a `Response` that did not go
* through `createSSRResponse` (a middleware early return, an API result):
* folds the request event's response stub onto it (cookies append
* entry-by-entry, other headers gap-fill, status never) and commits the
* stub. Already-committed stubs pass the response through untouched, so
* handlers apply it unconditionally after their middleware chain unwinds.
* `event` defaults to the ambient `getRequestEvent()`. Application
* middleware never calls this. Server-only.
*/
export declare function commitEventResponse(response: Response, event?: RequestEvent): Response;
/**
* Composes fetch-style middleware — `(request, next) => Response` — into a
* single function of the same shape. Nothing reaches the wire until the
* outermost middleware returns, so headers on the returned `Response` stay
* mutable through the whole unwind, streamed bodies included. Server-only.
*/
export declare function composeMiddleware(middlewares: FetchMiddleware[]): (request: Request, next: (request?: Request) => Response | Promise<Response>) => Promise<Response>;
/**
* Compiler primitive — emitted by JSX-DOM-Expressions for tagged-template

@@ -133,24 +233,36 @@ * SSR output. Not meant for hand-written code.

/**
* Compiler primitive — serializes a classList object for SSR output. Not
* meant for hand-written code.
* Compiler primitive — serializes a class value (string, object map, or
* array) for SSR output. Not meant for hand-written code.
* @internal
*/
export declare function ssrClassList(value: {
export declare function ssrClassName(value: string | {
[k: string]: boolean;
}): string;
} | Array<any>): string;
/**
* Compiler primitive — serializes a style object for SSR output. Not meant
* Compiler primitive — serializes a style value for SSR output. Not meant
* for hand-written code.
* @internal
*/
export declare function ssrStyle(value: {
export declare function ssrStyle(value: string | {
[k: string]: string;
}): string;
/**
* Compiler primitive — serializes a boolean attribute for SSR output. Not
* Compiler primitive — serializes one style property for SSR output. Not
* meant for hand-written code.
* @internal
*/
export declare function ssrAttribute(key: string, value: boolean): string;
export declare function ssrStyleProperty(name: string, value: any): string;
/**
* Compiler primitive — serializes an attribute for SSR output. Not meant
* for hand-written code.
* @internal
*/
export declare function ssrAttribute(key: string, value: any): string;
/**
* Compiler primitive — wraps a template-group closure for SSR output. Not
* meant for hand-written code.
* @internal
*/
export declare function ssrGroup<T extends () => any[]>(fn: T, n: number): T;
/**
* Compiler primitive — generates the hydration-key attribute for SSR

@@ -166,3 +278,3 @@ * output. Not meant for hand-written code.

*/
export declare function resolveSSRNode(node: any): string;
export declare function resolveSSRNode(node: any, result?: any, top?: boolean): any;
/**

@@ -173,2 +285,2 @@ * Escapes a string for safe inclusion in HTML output. Used by the SSR

*/
export declare function escape(html: string): string;
export declare function escape(s: any, attr?: boolean): any;

@@ -82,15 +82,2 @@ import { JSX } from "./jsx.cjs";

): string;
/** @deprecated use renderToStream which also returns a promise */
export function renderToStringAsync<T>(
fn: () => T,
options?: {
timeoutMs?: number;
nonce?: string;
renderId?: string;
noScripts?: boolean;
plugins?: SerializerPlugin[];
manifest?: AssetManifest | AssetResolver | AssetResolverFn;
onError?: (err: any) => void;
}
): Promise<string>;
export function renderToStream<T>(

@@ -120,3 +107,13 @@ fn: () => T,

): {
then: (fn: (html: string) => void) => void;
/**
* Awaiting the stream resolves with the complete HTML once every boundary
* settles — the fully-settled-string form of the render (`const html =
* await renderToStream(...)`). Render errors route through `onError` and
* the promise resolves with whatever HTML the render produced; it never
* rejects.
*/
then<TResult1 = string, TResult2 = never>(
onfulfilled?: ((html: string) => TResult1 | PromiseLike<TResult1>) | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
): Promise<TResult1 | TResult2>;
pipe: (writable: { write: (v: string) => void; end: () => void }) => void;

@@ -158,12 +155,3 @@ pipeTo: (writable: WritableStream) => Promise<void>;

): void;
/** @deprecated Use `useHead` — removed before `0.50.0` stable. */
export function useAssets(fn: () => JSX.Element): void;
/**
* @deprecated Use the `onHead` render option — removed before `0.50.0`
* stable. Reads ambient render state, so it is unsafe across concurrent
* renders; `onHead` is closure-bound to its render and also carries
* `useHead` output, which this does not.
*/
export function getAssets(): string;
/**
* A head tag descriptor. Props values may be getters (evaluated lazily on

@@ -174,2 +162,10 @@ * the server — at the owning flush boundary — and reactively on the client);

* that `key` cannot fork).
*
* Getters must be plain reads: they evaluate at flush time here (under no
* component owner) and inside registry-owned computations on the client, so
* a getter that allocates a reactive owner (`createMemo`, a `children()`
* helper) consumes a hydration id slot on one side only and desyncs every
* id allocated after the `useHead` call. Create such helpers eagerly at
* component position and read them from the getter. See
* docs/head-management-rfc.md.
*/

@@ -219,4 +215,6 @@ export type HeadTag = {

* and this type names the shape they agree on. Core's server-function
* handler reads its `Set-Cookie` headers when folding single-flight
* cookies but never requires it.
* handler folds it onto the outgoing response when present — its
* `Set-Cookie` values (cookies appended during the call via
* `serializeCookie`) append cookie-by-cookie, other headers fill gaps —
* and reads it when folding single-flight cookies, but never requires it.
*/

@@ -228,7 +226,10 @@ export interface ResponseStub {

/**
* Set by the integration once the response head has been derived/sent
* from this stub — status and headers can no longer change. Consumers
* that write response metadata during render (e.g. JSX response
* components) must treat later status/header writes and cleanup-time
* retractions as no-ops.
* Set once the response head has been derived/sent from this stub —
* status and headers can no longer change. Flip it through
* `commitResponseStub`, which also instruments the stub's `headers` so
* a post-commit write fails loudly (dev build throws, production
* reports + no-ops) instead of silently missing the wire. `status`/
* `statusText` stay plain fields: consumers that write response
* metadata during render (e.g. JSX response components) must still
* treat later status writes and cleanup-time retractions as no-ops.
*/

@@ -239,10 +240,35 @@ committed?: boolean;

/**
* The type of `RequestEvent.locals` — a module-augmentable interface so
* applications can type the state their middleware hangs on the event.
* Augment it through the package that re-exports the event (interface
* identity flows through the re-export chain):
*
* ```ts
* declare module "@solidjs/web" {
* interface RequestEventLocals {
* user: User;
* }
* }
* ```
*
* The index signature keeps un-augmented usage permissive — `locals` is a
* free-form bag by default — so augmentation adds precision for the keys
* it names without gating existing code. The flip side: unaugmented keys
* read as `any` rather than erroring, a deliberate trade (a strict-only
* `locals` would break every untyped write that works today).
*/
export interface RequestEventLocals {
[key: string | number | symbol]: any;
}
/**
* The per-request context available on the server: the incoming `Request`
* and a `locals` bag integrations and middleware can hang state on.
* Frameworks typically extend this shape with richer fields (e.g. a
* `response` head — see `ResponseStub`).
* and a `locals` bag integrations and middleware can hang state on (typed
* through the augmentable `RequestEventLocals`). Frameworks typically
* extend this shape with richer fields (e.g. a `response` head — see
* `ResponseStub`).
*/
export interface RequestEvent {
request: Request;
locals: Record<string | number | symbol, any>;
locals: RequestEventLocals;
}

@@ -257,3 +283,125 @@ /**

export function Assets(props: { children?: JSX.Element }): JSX.Element;
/** A fresh, uncommitted response head. */
export function createResponseStub(): ResponseStub;
/**
* The canonical request event for HTTP handlers: the incoming `Request`, a
* `locals` bag, and a `response` head stub the render writes to. `init`
* spreads over the defaults so frameworks can extend the shape.
*/
export function createRequestEvent<T extends object = {}>(
request: Request,
init?: T
): RequestEvent & { response: ResponseStub } & T;
/**
* The status an outgoing redirect should use for a response head carrying
* a `Location`: the stub's own status when it is a redirect status, 302
* otherwise.
*/
export function getExpectedRedirectStatus(response: ResponseStub): number;
/**
* Flips a response stub to `committed` — the moment its head freezes on
* the wire — and instruments the stub's `headers` mutating methods
* (`set`/`append`/`delete`, patched in place; the `Headers` identity and
* reads are untouched) so a post-commit write fails loudly instead of
* silently missing the wire: it throws in the dev build and reports +
* no-ops otherwise. Every head materialization path commits through here
* (`createSSRResponse`, the server-function handler's commit seam);
* integrations deriving their own heads should too.
*
* `allowLateLocation` is the stream path's documented exception: a
* `Location` set after the shell flushed is still honored client-side
* (stream completion appends a `window.location` script), so that one
* write stays permitted there.
*/
export function commitResponseStub(
stub: ResponseStub,
options?: { allowLateLocation?: boolean }
): ResponseStub;
/**
* Handler-lifecycle plumbing — a response's exit through the request
* event's response-stub lifecycle: page results leave through
* `createSSRResponse`, any other `Response` (a middleware early return, an
* API result) leaves through `commitEventResponse`; application middleware
* never calls this. Folds the event's stub onto the outgoing response —
* `Set-Cookie` appends entry-by-entry alongside the response's own, other
* stub headers fill gaps only (never the wire-protocol family the handlers
* own, never `Content-Type`/`Content-Length` on a bodiless response), the
* status is never taken from the stub — then commits the stub
* (`commitResponseStub`: post-commit writes fail loudly). Responses with
* immutable headers are rebuilt around merged copies.
*
* Idempotent at handler edges: an already-committed stub passes the
* response through untouched, so a handler may apply this unconditionally
* after its middleware chain unwinds — page responses from
* `createSSRResponse` come back committed and do not double-fold.
*
* `event` defaults to the ambient `getRequestEvent()`.
*/
export function commitEventResponse(response: Response, event?: RequestEvent): Response;
/**
* The cookie codec (the platform-gap primitives — see cookies.d.ts): ALL
* of core's cookie surface. The blessed patterns are
* `parseCookieHeader(event.request.headers.get("cookie"))` for reads and
* `event.response.headers.append("set-cookie", serializeCookie(name,
* value, options))` for writes.
*/
export { parseCookieHeader, serializeCookie } from "./cookies.cjs";
export type { CookieOptions } from "./cookies.cjs";
export interface SSRResponseOptions {
/** Base head; the stub's status/headers win over it. */
responseInit?: ResponseInit;
/** Nonce carried by the post-flush `<script>` redirect fallback. */
nonce?: string;
/** Rewrites each outgoing HTML chunk (entry script injection, ...). */
transformChunk?: (chunk: string) => string;
}
/**
* Derives the outgoing `Response` for an SSR render result, running the
* response-head lifecycle against `event.response`: commit at shell flush,
* pre-flush `Location` becomes a real redirect, post-flush `Location`
* appends a client-side script redirect before the stream closes.
* Synchronous for string results; resolves at shell flush for stream
* results.
*/
export function createSSRResponse(
result: string,
event: RequestEvent | undefined,
options?: SSRResponseOptions
): Response;
export function createSSRResponse(
result: { pipe(writable: { write: (v: string) => void; end: () => void }): void },
event: RequestEvent | undefined,
options?: SSRResponseOptions
): Promise<Response>;
/**
* Fetch-style middleware: return a `Response` to answer the request, or
* call `next()` (optionally with a substitute `Request`) to advance the
* chain and observe/replace the eventual response.
*/
export type FetchMiddleware = (
request: Request,
next: (request?: Request) => Promise<Response>
) => Response | Promise<Response>;
/**
* Composes fetch-style middleware into one function of the same shape;
* the terminal `next` dispatches to the actual handler. Runs in whatever
* scope the caller established (`provideRequestEvent`), so
* `getRequestEvent()` works exactly as in application code.
*/
export function composeMiddleware(
middlewares: FetchMiddleware[]
): (
request: Request,
next: (request?: Request) => Response | Promise<Response>
) => Promise<Response>;
export function untrack<T>(fn: () => T): T;

@@ -358,3 +506,7 @@

export function setStyleProperty(node: Element, name: string, value: any): void;
/** @deprecated not supported on the server side — register assets through the render context instead */
/**
* @internal See client.d.ts — head-management RFC policy: ambient CSS is
* unmanaged; the head registry owns directly-mounted stylesheet lifecycle.
* @deprecated not supported on the server side — register assets through the render context instead
*/
export function acquireAsset(descriptor: unknown): () => void;
import { JSX } from "./jsx.js";
import type { RequestEventLocals } from "./server.js";
// Element/property classification tables consumed by the JSX compiler and
// custom renderers. Compiler/tooling surface; not for hand-written code.
/** Compiler/tooling table; not for hand-written code. @internal */
export const DOMWithState: Record<string, Record<string, 1 | 2>>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const ChildProperties: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const DelegatedEvents: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const DOMElements: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const SVGElements: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const MathMLElements: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const VoidElements: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const RawTextElements: Set<string>;
/** Compiler/tooling table; not for hand-written code. @internal */
export const Namespaces: Record<string, string>;

@@ -20,2 +32,3 @@

/**
* Compiler-emitted primitive; not for hand-written code.
* @param flag

@@ -25,8 +38,21 @@ * - `undefined` — clone the template as-is (uses `cloneNode`).

* - `2` — the template html is wrapped; the outer tag is stripped at clone time.
* @internal
*/
export function template(html: string, flag?: 1 | 2): () => Element;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function scope<T extends () => any>(fn: T): T;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function effect<T>(fn: (prev?: T) => T, effect: (value: T, prev?: T) => void): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function memo<T>(fn: () => T, equal: boolean): () => T;
/**
* Compiler-emitted primitive; not for hand-written code — import `untrack`
* from `solid-js` instead.
* @internal
*/
export function untrack<T>(fn: () => T): T;
/**
* Compiler-emitted primitive; not for hand-written code.
* @internal
*/
export function insert<T>(

@@ -48,6 +74,11 @@ parent: MountableElement,

): JSX.Element;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function createComponent<T>(Comp: (props: T) => JSX.Element, props: T): JSX.Element;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function delegateEvents(eventNames: string[]): void;
/** Event-delegation plumbing (Portal/custom-root wiring). Integration plumbing. @internal */
export function registerDelegatedRoot(root: MountableElement): void;
/** Event-delegation plumbing (Portal/custom-root wiring). Integration plumbing. @internal */
export function unregisterDelegatedRoot(root: MountableElement): void;
/** Event-delegation plumbing (Portal/custom-root wiring). Integration plumbing. @internal */
export function registerDelegatedContainer(

@@ -57,2 +88,3 @@ container: MountableElement,

): void;
/** Event-delegation plumbing (Portal/custom-root wiring). Integration plumbing. @internal */
export function unregisterDelegatedContainer(

@@ -62,4 +94,7 @@ container: MountableElement,

): void;
/** Event-delegation plumbing (Portal/custom-root wiring). Integration plumbing. @internal */
export function getDelegatedRoot(node: MountableElement): MountableElement | undefined;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function spread<T>(node: Element, accessor: T, skipChildren?: Boolean): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function assign(

@@ -72,3 +107,5 @@ node: Element,

): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function setAttribute(node: Element, name: string, value: string): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function setAttributeNS(node: Element, namespace: string, name: string, value: string): void;

@@ -86,2 +123,6 @@ /**

* unregister function.
*
* Integration plumbing (routers register the consumer); not meant for
* application code.
* @internal
*/

@@ -92,2 +133,3 @@ export function registerElementClaim(handler: (element: Element) => void): () => void;

* Emitted by the compiler at element creation; idempotent by contract.
* @internal
*/

@@ -101,7 +143,14 @@ export function claimElement<T extends Element>(node: T): T;

* registered consumer.
*
* Integration plumbing; not meant for application code.
* @internal
*/
export function claimElementTree<T extends Node>(root: T): T;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function className(node: Element, value: JSX.ClassValue, prev?: JSX.ClassValue): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function setProperty(node: Element, name: string, value: any): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function setStyleProperty(node: Element, name: string, value: any): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function addEvent(

@@ -113,2 +162,3 @@ node: Element,

): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function style(

@@ -119,5 +169,17 @@ node: Element,

): void;
/**
* Compiler-emitted primitive; not for hand-written code — import `getOwner`
* from `solid-js` instead.
* @internal
*/
export function getOwner(): unknown;
/**
* Compiler-emitted prop-spread helper; not for hand-written code — import
* `merge` from `solid-js` instead.
* @internal
*/
export function mergeProps(...sources: unknown[]): unknown;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function dynamicProperty(props: unknown, key: string): unknown;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function applyRef<T extends Element = Element>(

@@ -127,2 +189,3 @@ r: ((element: NoInfer<T>) => void) | ((element: NoInfer<T>) => void)[],

): void;
/** Compiler-emitted primitive; not for hand-written code. @internal */
export function ref(

@@ -138,10 +201,10 @@ fn: () => ((element: Element) => void) | ((element: Element) => void)[],

): () => void;
/** Hydration-walk primitive; not for hand-written code. @internal */
export function getHydrationKey(): string | undefined;
/** Hydration-walk primitive; not for hand-written code. @internal */
export function getNextElement(template?: () => Element): Element;
/** Hydration-walk primitive; not for hand-written code. @internal */
export function getNextMatch(start: Node, elementName: string): Element;
/** Hydration-walk primitive; not for hand-written code. @internal */
export function getNextMarker(start: Node): [Node, Array<Node>];
/** @deprecated Use `useHead` — removed before `0.50.0` stable. */
export function useAssets(fn: () => JSX.Element): void;
/** @deprecated Use `useHead` — removed before `0.50.0` stable. */
export function getAssets(): string;
/**

@@ -151,2 +214,9 @@ * A head tag descriptor. Props values may be getters (reactive on the

* identity (`title` is a hard singleton that `key` cannot fork).
*
* Getters must be plain reads: they evaluate inside registry-owned
* computations here and at flush time on the server, so a getter that
* allocates a reactive owner (`createMemo`, a `children()` helper) consumes
* a hydration id slot on one side only and desyncs every id allocated after
* the `useHead` call. Create such helpers eagerly at component position and
* read them from the getter. See docs/head-management-rfc.md.
*/

@@ -180,3 +250,32 @@ export type HeadTag = {

}
/**
* @internal Ref-counted client asset ownership: acquire adopts or mounts the
* asset, the returned release follows the owner (with a grace period for
* back-and-forth navigation). Internal machinery, not a public CSS-lifecycle
* API — per the head-management RFC (docs/head-management-rfc.md), ambient
* bundler-injected CSS is never lifecycle-managed, and the head registry
* owns the lifecycle of directly-mounted stylesheets outright. This keeps
* its non-head roles (exclusive slots, owner-following DOM ownership).
*/
export function acquireAsset(descriptor: AssetDescriptor): () => void;
/**
* Registry entry returned by `warmAsset`. Stylesheet entries carry load
* tracking for the client reveal gate (docs/client-css-reveal-gating.md):
* `loadPromise` resolves on load OR error (never rejects) — an errored
* sheet releases the gate, parity with the server gate.
*/
export interface AssetEntry {
loadState?: "pending" | "loaded" | "errored";
loadPromise?: Promise<void>;
}
/**
* @internal Warm half of `acquireAsset`: idempotent and refcount-free,
* callable from a compute phase so the fetch starts at discovery and
* overlaps a transition's data wait. Stylesheets warm as
* `rel="preload" as="style"` and are flipped live by `acquireAsset` at
* commit — a branch superseded before it commits leaks only an inert
* preload, never an applied sheet. Only link-backed descriptors warm;
* inline styles and exclusive slots return `undefined`.
*/
export function warmAsset(descriptor: AssetDescriptor): AssetEntry | undefined;
export function HydrationScript(props?: { nonce?: string; eventNames?: string[] }): JSX.Element;

@@ -187,3 +286,2 @@ export function generateHydrationScript(options?: {

}): string;
export function Assets(props: { children?: JSX.Element }): JSX.Element;
/**

@@ -204,8 +302,31 @@ * See the server entry's `ResponseStub` — the shape of the mutable response

}
/**
* See the server entry's `RequestEventLocals` — the augmentable type of
* `RequestEvent.locals`. Re-exported (not re-declared) so both entries
* share ONE interface identity and a single augmentation reaches every
* `locals`, whichever entry typed the event.
*/
export type { RequestEventLocals } from "./server.js";
export interface RequestEvent {
request: Request;
locals: Record<string | number | symbol, any>;
locals: RequestEventLocals;
}
/**
* Registered symbol (`Symbol.for("solid.RequestContext")`) naming the global
* slot where `provideRequestEvent` parks the AsyncLocalStorage scoping
* request events. Integration plumbing — read the event through
* `getRequestEvent()` instead.
* @internal
*/
export declare const RequestContext: unique symbol;
export function getRequestEvent(): RequestEvent | undefined;
/**
* The cookie codec (the platform-gap primitives — see cookies.d.ts for the
* blessed patterns): the real implementation on both entries, never a
* stub — a pure value transformer has legitimate browser uses
* (`document.cookie`). Tree-shakes away when unused.
*/
export { parseCookieHeader, serializeCookie } from "./cookies.js";
export type { CookieOptions } from "./cookies.js";
/** Hydration-walk primitive; not for hand-written code. @internal */
export function runHydrationEvents(): void;
export { getOwner, runWithOwner, createComponent, createRoot as root, sharedConfig, untrack, merge as mergeProps, flatten, ssrHandleError, ssrScope, NoHydration, Hydration, runInServerComponentScope } from "solid-js";
export declare const effect: (fn: any, effectFn: any, options: any) => void;
export declare const effect: (fn: any, effectFn: any, options?: any) => void;
export declare const memo: (fn: any) => import("solid-js").SourceAccessor<any>;
export declare const runWithHydrationScope: (id: any, fn: any) => unknown;
export declare const ssrAsyncValue: (value: any) => import("solid-js").SourceAccessor<any>;
export declare const waitAsset: (promise: any) => void;
export { createFrame, createFrameHost, createFrameElement, FRAME_APPLIED_EVENT } from "./frame-client.js";
export { FRAME_STREAM_HEADER, applyFrameResponse, isFrameStreamResponse, createServerComponentHandler } from "./frame-transport.js";
export { createJSONDataTable } from "./serializer.js";
export type { Slot } from "./server.js";
/**
* Client-condition twin of the server face's `asyncArg` (DR-2 value tier):
* the identity that types an async value crossing the slot border as its
* settled value. Server component modules are authored in universal code and
* may resolve under the browser condition at typecheck/bundle time — the
* call never runs here (the `"use server"` body executes server-side), but
* the symbol must exist.
*/
export declare function asyncArg<T>(value: PromiseLike<T> | AsyncIterable<T>): T;
/**
* The app-wide shared frame host (created lazily): one chunk router with
* per-response codec data tables.
* @experimental
*/
export declare function getFrameHost(): any;

@@ -21,3 +34,4 @@ /**

* call again to rebind to a custom host.
* @experimental
*/
export declare function installServerComponents(host?: any): void;

@@ -9,5 +9,13 @@ /**

* place, and teardown is `dispose()`, never a version bump.
*
* EXPERIMENTAL — the frames/server-components surface ships as an
* experimental preview, excluded from the 2.0 stability guarantee: API
* shapes and the wire format may change between prereleases (RFC 11).
* Every export in this module is `@experimental`.
*/
/** One transport chunk of a frame stream, addressed by frame `id`. */
/**
* One transport chunk of a frame stream, addressed by frame `id`.
* @experimental
*/
export type FrameChunk =

@@ -52,2 +60,3 @@ | { type: "start"; id: string; version: number }

* its data hook.
* @experimental
*/

@@ -60,2 +69,3 @@ export function chunkToRecords(chunk: FrameChunk): Record<string, unknown>;

* stream stamp — an older version than the frame's current one is ignored.
* @experimental
*/

@@ -67,3 +77,6 @@ export interface FrameWrite {

/** Context passed to a slot callback. */
/**
* Context passed to a slot callback.
* @experimental
*/
export interface SlotContext {

@@ -124,5 +137,7 @@ /**

* the range, or `undefined` to claim `ctx.existing` untouched.
* @experimental
*/
export type Slot = (props: Record<string, unknown>, ctx: SlotContext) => Node | Node[] | undefined;
/** @experimental */
export interface Frame {

@@ -167,2 +182,3 @@ /** Merge a write into the store and flush (morph/reveal/slot sync). */

* delivery is seeded from a sibling's store.
* @experimental
*/

@@ -189,6 +205,10 @@ export interface FrameHost {

* reflection, e.g. `aria-current`) without a MutationObserver.
* @experimental
*/
export const FRAME_APPLIED_EVENT: "frame:applied";
/** Options for `createFrameHost`. */
/**
* Options for `createFrameHost`.
* @experimental
*/
export interface FrameHostOptions {

@@ -211,5 +231,9 @@ /**

/** @experimental */
export function createFrameHost(options?: FrameHostOptions): FrameHost;
/** Options for `createFrame` / `createFrameElement`. */
/**
* Options for `createFrame` / `createFrameElement`.
* @experimental
*/
export interface FrameOptions {

@@ -228,3 +252,3 @@ /** Register with this host under `id`, receiving routed/buffered chunks. */

/** Called after each apply flush (tests/telemetry). */
onApply?(info: { version: number; reason: "materialize" | "morph" | "reveal" }): void;
onApply?(info: { version: number; reason: "materialize" | "morph" | "reveal" | "error" }): void;
/**

@@ -271,8 +295,13 @@ * Wraps element-claim sweeps (`a[href]`/`form[action]` in materialized

* chunk.
* @experimental
*/
export function createFrame(boundary: Element, options?: FrameOptions): Frame;
/** The default boundary/region element tag and its id attribute — the DOM
* contract the producer emits at t=0 and the consumer creates/adopts. */
/**
* The default boundary/region element tag and its id attribute — the DOM
* contract the producer emits at t=0 and the consumer creates/adopts.
* @experimental
*/
export const FRAME_TAG: "dx-frame";
/** @experimental */
export const FRAME_ID_ATTR: "data-fid";

@@ -287,2 +316,3 @@

* `dispose()` (register it with your owner's cleanup).
* @experimental
*/

@@ -289,0 +319,0 @@ export function createFrameElement(options: FrameOptions): {

@@ -0,4 +1,11 @@

// EXPERIMENTAL — the frames/server-components surface ships as an
// experimental preview, excluded from the 2.0 stability guarantee: API
// shapes and the wire format may change between prereleases (RFC 11).
// Every export in this module is @experimental.
import { FrameChunk } from "./frame-client.js";
/** Addresses a frame stream: the boundary id and this response's version. */
/**
* Addresses a frame stream: the boundary id and this response's version.
* @experimental
*/
export interface FrameAddress {

@@ -15,2 +22,3 @@ id: string;

* `renderServerComponent` instead.
* @experimental
*/

@@ -22,3 +30,6 @@ export function createFrameSink(

/** Options shared by the frame producers. */
/**
* Options shared by the frame producers.
* @experimental
*/
export interface FrameStreamOptions {

@@ -31,3 +42,6 @@ /** Boundary address; defaults to `{ id: "", version: 1 }`. */

/** A produced frame stream: pipe chunks, or await the collected array. */
/**
* A produced frame stream: pipe chunks, or await the collected array.
* @experimental
*/
export interface FrameStream extends PromiseLike<FrameChunk[]> {

@@ -43,2 +57,3 @@ pipe(writable: { write(chunk: FrameChunk): void; end?(): void }): void;

* `createJSONDataTable`).
* @experimental
*/

@@ -64,2 +79,3 @@ export function renderToFrameStream(code: () => unknown, options?: FrameStreamOptions): FrameStream;

* function's arguments.
* @experimental
*/

@@ -78,2 +94,3 @@ export function renderServerComponent(

* @internal Exposed for framework bindings composing their own producers.
* @experimental
*/

@@ -91,2 +108,3 @@ export function createSlotProps(

* merges in; the frame tags win on conflict.
* @experimental
*/

@@ -113,2 +131,3 @@ export function serverComponentResponse(

* ```
* @experimental
*/

@@ -126,2 +145,3 @@ export function frameTransformResult(event: unknown, result: unknown): unknown;

* regions onto the server-rendered ranges.
* @experimental
*/

@@ -140,2 +160,3 @@ export function createDocumentSlotProps(

* Non-function results pass through.
* @experimental
*/

@@ -156,2 +177,3 @@ export function frameTransformDirectResult<T>(

* single-flight envelope).
* @experimental
*/

@@ -181,3 +203,4 @@ export function frameTransformFlightResult(

* `installServerComponents()`.
* @experimental
*/
export const SERVER_COMPONENT_BOOTSTRAP: string;

@@ -0,1 +1,5 @@

// EXPERIMENTAL — the frames/server-components surface ships as an
// experimental preview, excluded from the 2.0 stability guarantee: API
// shapes and the wire format may change between prereleases (RFC 11).
// Every export in this module is @experimental.
import { FrameChunk, FrameHost } from "./frame-client.js";

@@ -14,9 +18,16 @@ import { JSONCodecOptions } from "./serializer.js";

* `BodyFormat` entry, since the body is frame chunks, not a serialized value.
* @experimental
*/
export const FRAME_STREAM_HEADER: "X-Frame-Stream";
/** Whether a fetch Response carries a frame stream. */
/**
* Whether a fetch Response carries a frame stream.
* @experimental
*/
export function isFrameStreamResponse(response: Response): boolean;
/** Options for `applyFrameResponse`. */
/**
* Options for `applyFrameResponse`.
* @experimental
*/
export interface ApplyFrameResponseOptions {

@@ -61,2 +72,3 @@ /**

* ```
* @experimental
*/

@@ -69,9 +81,18 @@ export function applyFrameResponse(

/** Brands an inline-rendered server component with its function id. */
/**
* Brands an inline-rendered server component with its function id.
* @experimental
*/
export const SERVER_COMPONENT: unique symbol;
/** The unwrapped server component behind an inline-render wrap. */
/**
* The unwrapped server component behind an inline-render wrap.
* @experimental
*/
export const SERVER_COMPONENT_SOURCE: unique symbol;
/** The call's wire address (`frameAddress`), for regions to be emitted under. */
/**
* The call's wire address (`frameAddress`), for regions to be emitted under.
* @experimental
*/
export const SERVER_COMPONENT_ADDRESS: unique symbol;

@@ -87,6 +108,10 @@

* frameworks can honor it without importing this module.
* @experimental
*/
export const COMPONENT_BINDING: unique symbol;
/** The value under `COMPONENT_BINDING` on a transport-resolved binding. */
/**
* The value under `COMPONENT_BINDING` on a transport-resolved binding.
* @experimental
*/
export interface ComponentBinding<C = unknown> {

@@ -105,2 +130,3 @@ /** The per-function mount component (the equals-gate identity). */

* rides as data.
* @experimental
*/

@@ -116,2 +142,3 @@ export const ServerComponentPlugin: unknown;

* bootstrap text.
* @experimental
*/

@@ -124,6 +151,10 @@ export function setServerComponentBootstrap(resolve: (ctx: unknown) => string): void;

* legs; exported for integrations composing their own flight carriers.
* @experimental
*/
export function flightCodec(codec?: JSONCodecOptions): JSONCodecOptions;
/** Options for `createServerComponentHandler`. */
/**
* Options for `createServerComponentHandler`.
* @experimental
*/
export interface ServerComponentHandlerOptions<C = unknown> {

@@ -180,2 +211,3 @@ host: FrameHost;

* to warms its store (preload isolation is the default, not a rule).
* @experimental
*/

@@ -182,0 +214,0 @@ export function createServerComponentHandler<C>(options: ServerComponentHandlerOptions<C>): {

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

import { Plugin, Serializer, SerovalNode } from "seroval";
// Serialization surface (published as `@solidjs/web/serialization`): the
// runtime's Seroval machinery, exposed for the runtime's own entries and
// for integrations building transports on the same codec. This is
// INTEGRATION-FACING plumbing, not application API — it is exempt from the
// 2.0 stability guarantee and may change between releases. Application and
// router code should configure `codec` on the server-function entries
// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";

@@ -6,13 +13,123 @@ /**

* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// ---- Plugin authoring ----
//
// Unlike the rest of this entry, plugin authoring is APPLICATION-FACING —
// it is the supported way to feed the serializers' `plugins` options and
// the server-function entries' `codec.plugins`. The values re-export
// 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.
/** 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. Supply matching plugins on both
* peers of a transport.
* 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 type SerializerPlugin = Plugin<any, any>;
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
* plugin authors stay on the exact seroval instance/version the runtime
* serializes with. Import it from HERE, not from your own `seroval`
* dependency: a plugin built against a different copy/version would not
* fail the build — it would emit nodes the other peer can't interpret.
*
* Application-facing (see the plugin-authoring banner above).
*/
export function createPlugin<Value, Info extends PluginInfo>(
plugin: SerializerPlugin<Value, Info>
): SerializerPlugin<Value, Info>;
/**
* Seroval's `OpaqueReference`, re-exported from the runtime's own instance
* (an `OpaqueReference` from another seroval copy fails the serializer's
* instanceof check and serializes as a plain value): wraps a value so it
* crosses the wire as its `replacement` (default `undefined`) while
* staying readable in-process through `.value`.
*
* Application-facing (see the plugin-authoring banner above).
*/
export class OpaqueReference<V, R = undefined> {
readonly value: V;
readonly replacement?: R;
constructor(value: V, replacement?: R);
}
/**
* Baseline plugin set for serializing web-platform values (AbortSignal,

@@ -22,2 +139,4 @@ * Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).

* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -31,6 +150,12 @@ export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];

* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/** Options for `createSerializer`. */
/**
* Options for `createSerializer`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface WebSerializerOptions {

@@ -64,2 +189,4 @@ /** Name of the global object the emitted scripts write resolved values into. */

* `serializeJSON` / `createJSONDeserializer` instead.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -103,2 +230,4 @@ export function createSerializer(options: WebSerializerOptions): Serializer;

* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -121,3 +250,7 @@ export interface JSONCodecOptions {

/** Options for `serializeJSON`. */
/**
* Options for `serializeJSON`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONSerializeOptions extends JSONCodecOptions {

@@ -141,2 +274,4 @@ /**

* function that aborts pending async serialization.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -151,6 +286,34 @@ export function serializeJSON(value: unknown, options: JSONSerializeOptions): () => void;

* 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`. */
export interface JSONSerializerOptions extends JSONCodecOptions {
/**
* Receives each keyed record — `initial` is true for a key's first node
* (the written value itself); async values patch through later records
* under the same key. The decoding peer is `createJSONDataTable`.
*/
onData: (record: { key: string; node: SerovalNode; initial: boolean }) => void;
onError?: (error: unknown) => void;
/** Fires once `flush()` has been called and every pending value settled. */
onDone?: () => void;
}
/**
* The keyed, streaming encoder of the eval-free JSON codec — the render
* stream's data serializer (frames default to it). Each `write(key, value)`
* shares one reference space, so cross-record identity holds; `flush()`
* marks the write set complete (writes after it are dropped, mirroring the
* hydration serializer); `close()` aborts pending async serialization.
*/
export function createJSONSerializer(options: JSONSerializerOptions): {
write(key: string, value: unknown): void;
flush(): void;
close(): void;
};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply

@@ -160,2 +323,6 @@ * each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with

* (`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.
*/

@@ -162,0 +329,0 @@ export interface JSONDataTable {

@@ -25,2 +25,3 @@ import type { Element as SolidElement } from "solid-js";

* the right default; `$key` matters when a live list reorders.
* @experimental
*/

@@ -30,3 +31,24 @@ export type Slot<P = {}> = (props: P & {

}) => SolidElement;
/**
* Types an async value crossing the slot border (DR-2, value tier). What you
* pass is what ships — the promise / async iterable itself rides the data
* channel — but the client's prop READ settles: it suspends into the covering
* boundary until first arrival (a promise's resolution, an iterable's first
* yield), then reads as the settled value, updating per yield for iterables.
*
* `asyncArg` is the type-level statement of that contract: identity at
* runtime, settled type at the border, so `Slot<P>` keeps the fill's props
* truthful to what its reads actually return.
*
* Slots render as JSX — the compiler wraps each prop in a getter so the read
* defers to the slot border, where the runtime owns it. A call form
* (`props.status({ … })`) evaluates its args eagerly in the component body —
* a top-level read, an error in most cases.
*
* ```tsx
* <props.status progress={asyncArg(gen.progress)} stats={asyncArg(gen.stats)} />
* ```
*/
export declare function asyncArg<T>(value: PromiseLike<T> | AsyncIterable<T>): T;
export { renderToFrameStream, renderServerComponent, serverComponentResponse, frameTransformResult, frameTransformFlightResult, createFrameSink, frameTransformDirectResult, ServerComponentPlugin, SERVER_COMPONENT_BOOTSTRAP } from "./frame-sink.js";
export { FRAME_STREAM_HEADER, isFrameStreamResponse } from "./frame-transport.js";

@@ -99,5 +99,4 @@ import { hydrate as hydrateCore } from "./client.js";

*
* Use this when the page HTML was produced by `renderToString`,
* `renderToStringAsync`, or `renderToStream`. For client-only apps, use
* `render` instead.
* Use this when the page HTML was produced by `renderToString` or
* `renderToStream`. For client-only apps, use `render` instead.
*

@@ -104,0 +103,0 @@ * Pass `options.renderId` to hydrate one of multiple roots emitted by a

@@ -53,2 +53,47 @@ /**

/**
* Registered-symbol brand (`Symbol.for("solid.SafeError")`) marking a thrown
* value as safe to serialize to the client verbatim. Declared `unique
* symbol` type-side; the runtime value is the registered symbol, so
* separately bundled copies agree on identity.
*/
export declare const SAFE_ERROR: unique symbol;
/**
* Marks `error` as safe to serialize to the client verbatim, opting it out
* of the server-function handler's production error sanitization.
*
* By default a plain `Error` thrown from a server function is sanitized to a
* generic `Error` outside the dev build (the `development` export condition
* selects the full-fidelity copy; every other resolution sanitizes):
* its `message`, `stack`, and own-properties are dropped so a driver/ORM
* error can't leak a failing query or connection string over the wire.
* Dev builds keep full fidelity. This is the escape hatch for errors whose
* content is *intentional* client-facing information — brand them and their
* message/properties travel intact in every environment.
*
* The brand is a non-enumerable, symbol-keyed property, so it never itself
* serializes as an own-property. Sets the brand and returns the same value.
*
* @example
* ```ts
* import { markSafeError } from "@solidjs/web";
*
* async function transfer(amount: number) {
* "use server";
* if (amount > balance) {
* // The user must see this message; opt out of sanitization.
* throw markSafeError(new Error("Insufficient funds"));
* }
* }
* ```
*/
export function markSafeError<E>(error: E): E;
/**
* Whether `value` is branded safe to serialize (via `markSafeError`).
* Registered-symbol check, correct across duplicated module instances.
*/
export function isSafeError(value: unknown): value is Error;
/**
* Response header naming the cache keys a mutation invalidated

@@ -55,0 +100,0 @@ * (`"X-Revalidate"`), comma separated. The response helpers below set it

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

import { Plugin, Serializer, SerovalNode } from "seroval";
// Serialization surface (published as `@solidjs/web/serialization`): the
// runtime's Seroval machinery, exposed for the runtime's own entries and
// for integrations building transports on the same codec. This is
// INTEGRATION-FACING plumbing, not application API — it is exempt from the
// 2.0 stability guarantee and may change between releases. Application and
// router code should configure `codec` on the server-function entries
// instead of importing from here.
import { Serializer, SerovalNode } from "seroval";

@@ -6,13 +13,123 @@ /**

* emits and `createJSONDeserializer` consumes. Safe to `JSON.stringify`.
*
* Integration-facing; may change (see the entry banner).
*/
export type { SerovalNode };
// ---- Plugin authoring ----
//
// Unlike the rest of this entry, plugin authoring is APPLICATION-FACING —
// it is the supported way to feed the serializers' `plugins` options and
// the server-function entries' `codec.plugins`. The values re-export
// 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.
/** 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. Supply matching plugins on both
* peers of a transport.
* 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 type SerializerPlugin = Plugin<any, any>;
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
* plugin authors stay on the exact seroval instance/version the runtime
* serializes with. Import it from HERE, not from your own `seroval`
* dependency: a plugin built against a different copy/version would not
* fail the build — it would emit nodes the other peer can't interpret.
*
* Application-facing (see the plugin-authoring banner above).
*/
export function createPlugin<Value, Info extends PluginInfo>(
plugin: SerializerPlugin<Value, Info>
): SerializerPlugin<Value, Info>;
/**
* Seroval's `OpaqueReference`, re-exported from the runtime's own instance
* (an `OpaqueReference` from another seroval copy fails the serializer's
* instanceof check and serializes as a plain value): wraps a value so it
* crosses the wire as its `replacement` (default `undefined`) while
* staying readable in-process through `.value`.
*
* Application-facing (see the plugin-authoring banner above).
*/
export class OpaqueReference<V, R = undefined> {
readonly value: V;
readonly replacement?: R;
constructor(value: V, replacement?: R);
}
/**
* Baseline plugin set for serializing web-platform values (AbortSignal,

@@ -22,2 +139,4 @@ * Event, FormData, Headers, ReadableStream, Request, Response, URL, ...).

* of it via `resolveSerializerPlugins`.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -31,6 +150,12 @@ export const DEFAULT_WEB_PLUGINS: readonly SerializerPlugin[];

* plugin list to another serialization layer.
*
* Integration-facing; may change (see the entry banner).
*/
export function resolveSerializerPlugins(customPlugins?: SerializerPlugin[]): SerializerPlugin[];
/** Options for `createSerializer`. */
/**
* Options for `createSerializer`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface WebSerializerOptions {

@@ -64,2 +189,4 @@ /** Name of the global object the emitted scripts write resolved values into. */

* `serializeJSON` / `createJSONDeserializer` instead.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -103,2 +230,4 @@ export function createSerializer(options: WebSerializerOptions): Serializer;

* client/server `codec` config option.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -121,3 +250,7 @@ export interface JSONCodecOptions {

/** Options for `serializeJSON`. */
/**
* Options for `serializeJSON`.
*
* Integration-facing; may change (see the entry banner).
*/
export interface JSONSerializeOptions extends JSONCodecOptions {

@@ -141,2 +274,4 @@ /**

* function that aborts pending async serialization.
*
* Integration-facing; may change (see the entry banner).
*/

@@ -151,6 +286,34 @@ export function serializeJSON(value: unknown, options: JSONSerializeOptions): () => void;

* 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`. */
export interface JSONSerializerOptions extends JSONCodecOptions {
/**
* Receives each keyed record — `initial` is true for a key's first node
* (the written value itself); async values patch through later records
* under the same key. The decoding peer is `createJSONDataTable`.
*/
onData: (record: { key: string; node: SerovalNode; initial: boolean }) => void;
onError?: (error: unknown) => void;
/** Fires once `flush()` has been called and every pending value settled. */
onDone?: () => void;
}
/**
* The keyed, streaming encoder of the eval-free JSON codec — the render
* stream's data serializer (frames default to it). Each `write(key, value)`
* shares one reference space, so cross-record identity holds; `flush()`
* marks the write set complete (writes after it are dropped, mirroring the
* hydration serializer); `close()` aborts pending async serialization.
*/
export function createJSONSerializer(options: JSONSerializerOptions): {
write(key: string, value: unknown): void;
flush(): void;
close(): void;
};
/**
* A resident, response-scoped decode table over the keyed JSON codec: apply

@@ -160,2 +323,6 @@ * each frame `data` chunk with `apply`, resolve `{ $ref }` slot args with

* (`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.
*/

@@ -162,0 +329,0 @@ export interface JSONDataTable {

@@ -24,2 +24,3 @@ import { JSONCodecOptions } from "../serializer.js";

isServerFunction,
serializeString,
subscribeFlightData,

@@ -26,0 +27,0 @@ withMeta

@@ -123,2 +123,29 @@ import { ResponseEnvelope } from "../response.js";

/**
* Wraps a server function execution — the per-invocation seam for
* framework policies (per-function middleware, auth, logging, error
* mapping). Called inside the call's event scope with the invocation
* identity already established: `getServerFunctionInvocation()` answers
* before, during and after `run()`. Must return (or resolve to) `run()`'s
* result — replacing it replaces the function's result; throwing routes
* through the handler's normal error encoding.
*
* The context carries the call's identity (`id`, parsed `args`), its
* `event`, and how it arrived: `direct` is `true` for in-process SSR calls
* (where `request` is absent) and `false` for HTTP dispatch. On the direct
* path the wrapper must stay transparent for synchronous functions —
* return `run()`'s value, not an unconditional promise, unless it needs to
* be async.
*/
export type WrapInvocationHook = (
run: () => unknown,
context: {
id: string;
args: unknown[];
event: ServerFunctionEvent;
request?: Request;
direct: boolean;
}
) => unknown;
/**
* Request headers with `setCookies` folded into the `Cookie` header, as the

@@ -185,2 +212,10 @@ * browser would have applied them before its next request. Later entries

/**
* Wraps every server function execution — HTTP dispatch and direct SSR
* calls alike — with the invocation identity already established (see
* `WrapInvocationHook`). The per-invocation seam for framework policies:
* per-function middleware, auth, logging, error mapping. A per-request
* option overrides it for HTTP dispatch.
*/
wrapInvocation?: WrapInvocationHook;
/**
* The single-flight hook: produces the data payload folded into

@@ -408,2 +443,9 @@ * responses of calls that opted in (see `CollectFlightDataHook`).

/**
* Overrides the configured per-invocation wrap for this handler — same
* contract as the `wrapInvocation` config option (see
* `WrapInvocationHook`), except it only applies to HTTP dispatch (a
* per-request option can't see direct SSR calls).
*/
wrapInvocation?: WrapInvocationHook;
/**
* Observes or replaces the function's result before encoding — the

@@ -479,2 +521,37 @@ * extension point for response metadata policies (headers, statuses,

*
* When the event carries a `response` head stub (`event.response`, see the
* server entry's `ResponseStub`), the handler folds it onto every outgoing
* response as the head freezes — its `Set-Cookie` values (cookies appended
* during the call) append cookie-by-cookie alongside the result's own,
* other stub headers fill gaps (the call's response metadata wins; the
* protocol-owned family — the error/format/single-flight tags, `Location`,
* `X-Revalidate` — never fills, and neither does `Content-Type`/`Content-
* Length` onto a bodiless response) — and marks the stub `committed`, so
* later cookie/header writes report instead of silently missing the wire.
*
* ## Thrown-error sanitization (security default)
*
* A thrown `Response`/envelope (`redirect`/`reload`/`respond`) is intentional
* control flow and is forwarded untouched. A *plain* thrown value (a bare
* `Error`, string, or object) is different: serialized verbatim it would ship
* its `message` and every own-property to the client — a driver/ORM error's
* failing query, connection string, or bound parameters included. So outside
* the dev build a plain thrown value is replaced with a generic `Error`
* before serialization; the client still receives *an* `Error` (the shape
* `submission.error` etc. expect), just with no leaked content. The dev
* build keeps full fidelity (message, stack, own-props) for DX and the dev
* toolbar inspector. Dev/prod is the BUILD VARIANT, not `NODE_ENV`:
* `@solidjs/web` publishes a dev copy of this entry behind the
* `development` export condition (what Vite dev resolves) and the default
* resolution sanitizes — as does importing the runtime source directly with
* no bundler signal (fail-safe).
*
* Escape hatch: brand the value with `markSafeError` (`Symbol.for(
* "solid.SafeError")`) to send its content intact in every environment.
* A `wrapInvocation`/`transformResult` override that maps errors expresses
* intent the same way — throw a `Response`/envelope, or brand the mapped
* error safe; an unbranded plain error it lets propagate is sanitized like
* any other, so a framework onError policy must brand its result to keep a
* custom client-facing message in production.
*
* @example

@@ -495,1 +572,22 @@ * ```ts

): Promise<Response>;
/** Message a sanitized (production) server error carries on the wire. */
export const GENERIC_SERVER_ERROR_MESSAGE: string;
/**
* The production error-sanitization policy `handleServerFunctionRequest`
* applies to a plain thrown value before serialization. Returns `value`
* unchanged in the dev build or when it is branded safe (`markSafeError`);
* otherwise returns a generic `Error` carrying `GENERIC_SERVER_ERROR_MESSAGE`.
* Exposed for frameworks composing their own dispatch around the same policy.
*/
export function sanitizeServerError(value: unknown): unknown;
/**
* Overrides the build-variant dev flag for this module instance — the seam
* for test harnesses and hand-rolled bundles whose packaging cannot replace
* `_DX_DEV_`. Applications never call this; select the dev build through
* the `development` export condition instead.
* @internal
*/
export function setServerFunctionsDev(dev: boolean): void;

@@ -21,2 +21,5 @@ import { JSONCodecOptions } from "../serializer.js";

* lower-level codec helpers so custom plugins configured by the app apply.
*
* Integration plumbing; not meant for hand-written application code.
* @internal
*/

@@ -61,2 +64,5 @@ export function getServerFunctionsCodec(): JSONCodecOptions | undefined;

* surrogates are replaced with U+FFFD — they cannot survive UTF-8 anyway).
*
* Transport wire detail; not meant for hand-written code.
* @internal
*/

@@ -69,2 +75,6 @@ export function encodeErrorHeaderValue(value: string): string;

* peers that never encode) passes through untouched.
*
* Integration plumbing for readers of `ERROR_HEADER`; not meant for
* hand-written application code.
* @internal
*/

@@ -418,2 +428,5 @@ export function decodeErrorHeaderValue(value: string): string;

* stays core's own.
*
* Integration plumbing; not meant for hand-written application code.
* @internal
*/

@@ -429,2 +442,5 @@ export function decodeResponsePayload<T = unknown, D = unknown>(

* responses and frame streams) share this framing.
*
* Transport wire detail; not meant for hand-written code.
* @internal
*/

@@ -437,2 +453,5 @@ export function createChunk(data: string): Uint8Array;

* buffering partial frames internally until their length prefix is satisfied.
*
* Transport wire detail; not meant for hand-written code.
* @internal
*/

@@ -450,3 +469,6 @@ export class ChunkReader {

* realms and releases.
*
* Transport wire detail; not meant for hand-written code.
* @internal
*/
export function frameAddress(id: string, args?: readonly unknown[]): string;

@@ -0,5 +1,46 @@

import type { RequestEvent, RequestEventLocals, ResponseStub } from "./client.js";
/** Static asset manifest produced by a build (e.g. parsed Vite manifest.json). */
export type AssetManifest = Record<string, {
file: string;
css?: string[];
isEntry?: boolean;
imports?: string[];
}> & {
_base?: string;
};
/** Inline style content, e.g. dev CSS collected from a bundler's module graph. */
export type InlineStyleAsset = {
id: string;
content: string;
attrs?: Record<string, string>;
};
export type ResolvedAssets = {
js: string[];
css: (string | InlineStyleAsset)[];
};
/**
* Resolver form of the manifest option — the primitive a dev server
* implements against its live module graph (a static manifest object is
* normalized into a sync resolver internally). `resolve` may return a
* promise (async resolvers require streaming rendering); CSS entries may be
* URL strings (emitted as load-gated `<link>` tags) or inline-style
* descriptors (emitted as `<style>` tags). A bare `resolve`-shaped function
* is accepted as shorthand for `{ resolve }`.
*/
export type AssetResolver = {
resolve(key: string): ResolvedAssets | null | undefined | Promise<ResolvedAssets | null | undefined>;
/**
* Synchronous fast path answering with whatever is knowable without async
* work (typically js URLs, omitting css). Sync consumers — e.g. a lazy
* component's `moduleUrl` getter used by islands — use this when `resolve`
* would return a promise, so adapters should provide it whenever possible.
*/
resolveSync?(key: string): ResolvedAssets | null | undefined;
};
/** Bare-function shorthand for `AssetResolver` (no sync fast path). */
export type AssetResolverFn = (key: string) => ResolvedAssets | null | undefined | Promise<ResolvedAssets | null | undefined>;
/**
* Renders a component tree synchronously to an HTML string. Async reads inside
* `<Loading>` boundaries emit their `fallback` content; for full-graph
* resolution use `renderToStringAsync` instead.
* resolution await `renderToStream` instead.
*

@@ -21,42 +62,17 @@ * Pair the returned HTML with `hydrate()` on the client.

plugins?: any[];
manifest?: Record<string, {
file: string;
css?: string[];
isEntry?: boolean;
isDynamicEntry?: boolean;
imports?: string[];
}>;
manifest?: AssetManifest | AssetResolver | AssetResolverFn;
onError?: (err: any) => void;
/**
* Embedded-render contract for hosts that own the document. When the
* render output contains no `</head>`, everything head-bound (resolved
* `useHead` winners, eager resources, tracked asset links, inline
* styles) is delivered here as one HTML string — prelude (charset/base)
* first — for the host to splice into its own `<head>` template, instead
* of being dropped. Called synchronously before `renderToString`
* returns; not called when the output has a `</head>` (splicing is
* automatic then).
*/
onHead?: (head: string) => void;
}): string;
/**
* Renders a component tree to an HTML string and awaits all async reads in the
* subtree before resolving. The returned HTML reflects the fully-settled state
* — no `<Loading>` fallbacks appear in the output.
*
* Use this when you want a complete page in one round-trip. For incremental
* streaming with progressive boundary resolution, use `renderToStream`.
*
* @example
* ```tsx
* import { renderToStringAsync } from "@solidjs/web";
*
* const html = await renderToStringAsync(() => <App />);
* ```
*/
export declare function renderToStringAsync<T>(fn: () => T, options?: {
timeoutMs?: number;
nonce?: string;
renderId?: string;
noScripts?: boolean;
plugins?: any[];
manifest?: Record<string, {
file: string;
css?: string[];
isEntry?: boolean;
isDynamicEntry?: boolean;
imports?: string[];
}>;
onError?: (err: any) => void;
}): Promise<string>;
/**
* Streams an HTML response, flushing the synchronous shell first and then

@@ -68,5 +84,6 @@ * progressively emitting async-resolved fragments as their `<Loading>`

* a Web `WritableStream`, a lazy `readable` byte-stream view for
* `new Response(stream.readable)`, plus a `then` for awaiting full
* completion. `pipe`, `pipeTo`, and `readable` each consume the render —
* use exactly one of the three.
* `new Response(stream.readable)`, plus a thenable for awaiting full
* completion — `await renderToStream(...)` resolves with the settled HTML
* (the fully-resolved-string form of the render). `pipe`, `pipeTo`, and
* `readable` each consume the render — use exactly one of the three.
*

@@ -82,2 +99,5 @@ * @example

* return new Response(renderToStream(() => <App />).readable);
*
* // Fully settled string:
* const html = await renderToStream(() => <App />);
* ```

@@ -90,9 +110,3 @@ */

plugins?: any[];
manifest?: Record<string, {
file: string;
css?: string[];
isEntry?: boolean;
isDynamicEntry?: boolean;
imports?: string[];
}>;
manifest?: AssetManifest | AssetResolver | AssetResolverFn;
onCompleteShell?: (info: {

@@ -105,4 +119,21 @@ write: (v: string) => void;

onError?: (err: any) => void;
/**
* Embedded-render contract for hosts that own the document. When the
* shell contains no `</head>`, everything head-bound at first flush
* (resolved `useHead` winners, eager resources, tracked asset links,
* inline styles) is delivered here as one HTML string — prelude first —
* before the shell chunk is emitted, so the host can write its own
* `<head>` ahead of piping the stream. Post-shell head updates flow
* through the stream itself and apply in the browser. Not called when
* the shell has a `</head>` (splicing is automatic then).
*/
onHead?: (head: string) => void;
}): {
then: (fn: (html: string) => void) => void;
/**
* Awaiting the stream resolves with the complete HTML once every boundary
* settles — the fully-settled-string form of the render. Render errors
* route through `onError` and the promise resolves with whatever HTML the
* render produced; it never rejects.
*/
then<TResult1 = string, TResult2 = never>(onfulfilled?: ((html: string) => TResult1 | PromiseLike<TResult1>) | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null): Promise<TResult1 | TResult2>;
pipe: (writable: {

@@ -116,2 +147,71 @@ write: (v: string) => void;

/**
* Fetch-style middleware: receives the `Request` and a `next` continuation
* (pass a `Request` to substitute it downstream) and returns the `Response`.
* Composed with `composeMiddleware`; runs inside the request-event scope, so
* `getRequestEvent()` works exactly as in application code.
*/
export type FetchMiddleware = (request: Request, next: (request?: Request) => Promise<Response>) => Response | Promise<Response>;
/**
* Creates a fresh, uncommitted {@link ResponseStub}. Server-only.
*/
export declare function createResponseStub(): ResponseStub;
/**
* Builds the canonical request event — a web-standard `Request`, a `locals`
* bag, and a stub-backed `response` head — for `provideRequestEvent`.
* Server-only: on the client the request event belongs to the server that
* rendered the page.
*/
export declare function createRequestEvent<T extends object = {}>(request: Request, init?: T): {
request: Request;
locals: RequestEventLocals;
response: ResponseStub;
} & T;
/**
* The HTTP status a redirect should use: the stub's own status when it is a
* redirect status (301/302/303/307/308), 302 otherwise. Server-only.
*/
export declare function getExpectedRedirectStatus(response: ResponseStub): number;
/**
* Derives the outgoing `Response` for an SSR render result, running the
* response-head lifecycle against `event.response`: the stub commits at
* shell flush, a pre-flush `Location` becomes a real redirect
* (`getExpectedRedirectStatus`), and a post-flush one appends the
* nonce-aware `<script>window.location=...</script>` fallback. String
* results return a `Response` synchronously; stream results resolve at
* shell flush. Server-only.
*/
export declare function createSSRResponse(result: string, event: RequestEvent | undefined, options?: {
responseInit?: ResponseInit;
nonce?: string;
transformChunk?: (chunk: string) => string;
}): Response;
export declare function createSSRResponse(result: {
pipe(writable: {
write: (v: string) => void;
end: () => void;
}): void;
}, event: RequestEvent | undefined, options?: {
responseInit?: ResponseInit;
nonce?: string;
transformChunk?: (chunk: string) => string;
}): Promise<Response>;
/**
* Handler-lifecycle plumbing — the exit for a `Response` that did not go
* through `createSSRResponse` (a middleware early return, an API result):
* folds the request event's response stub onto it (cookies append
* entry-by-entry, other headers gap-fill, status never) and commits the
* stub. Already-committed stubs pass the response through untouched, so
* handlers apply it unconditionally after their middleware chain unwinds.
* `event` defaults to the ambient `getRequestEvent()`. Application
* middleware never calls this. Server-only.
*/
export declare function commitEventResponse(response: Response, event?: RequestEvent): Response;
/**
* Composes fetch-style middleware — `(request, next) => Response` — into a
* single function of the same shape. Nothing reaches the wire until the
* outermost middleware returns, so headers on the returned `Response` stay
* mutable through the whole unwind, streamed bodies included. Server-only.
*/
export declare function composeMiddleware(middlewares: FetchMiddleware[]): (request: Request, next: (request?: Request) => Response | Promise<Response>) => Promise<Response>;
/**
* Compiler primitive — emitted by JSX-DOM-Expressions for tagged-template

@@ -133,24 +233,36 @@ * SSR output. Not meant for hand-written code.

/**
* Compiler primitive — serializes a classList object for SSR output. Not
* meant for hand-written code.
* Compiler primitive — serializes a class value (string, object map, or
* array) for SSR output. Not meant for hand-written code.
* @internal
*/
export declare function ssrClassList(value: {
export declare function ssrClassName(value: string | {
[k: string]: boolean;
}): string;
} | Array<any>): string;
/**
* Compiler primitive — serializes a style object for SSR output. Not meant
* Compiler primitive — serializes a style value for SSR output. Not meant
* for hand-written code.
* @internal
*/
export declare function ssrStyle(value: {
export declare function ssrStyle(value: string | {
[k: string]: string;
}): string;
/**
* Compiler primitive — serializes a boolean attribute for SSR output. Not
* Compiler primitive — serializes one style property for SSR output. Not
* meant for hand-written code.
* @internal
*/
export declare function ssrAttribute(key: string, value: boolean): string;
export declare function ssrStyleProperty(name: string, value: any): string;
/**
* Compiler primitive — serializes an attribute for SSR output. Not meant
* for hand-written code.
* @internal
*/
export declare function ssrAttribute(key: string, value: any): string;
/**
* Compiler primitive — wraps a template-group closure for SSR output. Not
* meant for hand-written code.
* @internal
*/
export declare function ssrGroup<T extends () => any[]>(fn: T, n: number): T;
/**
* Compiler primitive — generates the hydration-key attribute for SSR

@@ -166,3 +278,3 @@ * output. Not meant for hand-written code.

*/
export declare function resolveSSRNode(node: any): string;
export declare function resolveSSRNode(node: any, result?: any, top?: boolean): any;
/**

@@ -173,2 +285,2 @@ * Escapes a string for safe inclusion in HTML output. Used by the SSR

*/
export declare function escape(html: string): string;
export declare function escape(s: any, attr?: boolean): any;

@@ -82,15 +82,2 @@ import { JSX } from "./jsx.js";

): string;
/** @deprecated use renderToStream which also returns a promise */
export function renderToStringAsync<T>(
fn: () => T,
options?: {
timeoutMs?: number;
nonce?: string;
renderId?: string;
noScripts?: boolean;
plugins?: SerializerPlugin[];
manifest?: AssetManifest | AssetResolver | AssetResolverFn;
onError?: (err: any) => void;
}
): Promise<string>;
export function renderToStream<T>(

@@ -120,3 +107,13 @@ fn: () => T,

): {
then: (fn: (html: string) => void) => void;
/**
* Awaiting the stream resolves with the complete HTML once every boundary
* settles — the fully-settled-string form of the render (`const html =
* await renderToStream(...)`). Render errors route through `onError` and
* the promise resolves with whatever HTML the render produced; it never
* rejects.
*/
then<TResult1 = string, TResult2 = never>(
onfulfilled?: ((html: string) => TResult1 | PromiseLike<TResult1>) | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null
): Promise<TResult1 | TResult2>;
pipe: (writable: { write: (v: string) => void; end: () => void }) => void;

@@ -158,12 +155,3 @@ pipeTo: (writable: WritableStream) => Promise<void>;

): void;
/** @deprecated Use `useHead` — removed before `0.50.0` stable. */
export function useAssets(fn: () => JSX.Element): void;
/**
* @deprecated Use the `onHead` render option — removed before `0.50.0`
* stable. Reads ambient render state, so it is unsafe across concurrent
* renders; `onHead` is closure-bound to its render and also carries
* `useHead` output, which this does not.
*/
export function getAssets(): string;
/**
* A head tag descriptor. Props values may be getters (evaluated lazily on

@@ -174,2 +162,10 @@ * the server — at the owning flush boundary — and reactively on the client);

* that `key` cannot fork).
*
* Getters must be plain reads: they evaluate at flush time here (under no
* component owner) and inside registry-owned computations on the client, so
* a getter that allocates a reactive owner (`createMemo`, a `children()`
* helper) consumes a hydration id slot on one side only and desyncs every
* id allocated after the `useHead` call. Create such helpers eagerly at
* component position and read them from the getter. See
* docs/head-management-rfc.md.
*/

@@ -219,4 +215,6 @@ export type HeadTag = {

* and this type names the shape they agree on. Core's server-function
* handler reads its `Set-Cookie` headers when folding single-flight
* cookies but never requires it.
* handler folds it onto the outgoing response when present — its
* `Set-Cookie` values (cookies appended during the call via
* `serializeCookie`) append cookie-by-cookie, other headers fill gaps —
* and reads it when folding single-flight cookies, but never requires it.
*/

@@ -228,7 +226,10 @@ export interface ResponseStub {

/**
* Set by the integration once the response head has been derived/sent
* from this stub — status and headers can no longer change. Consumers
* that write response metadata during render (e.g. JSX response
* components) must treat later status/header writes and cleanup-time
* retractions as no-ops.
* Set once the response head has been derived/sent from this stub —
* status and headers can no longer change. Flip it through
* `commitResponseStub`, which also instruments the stub's `headers` so
* a post-commit write fails loudly (dev build throws, production
* reports + no-ops) instead of silently missing the wire. `status`/
* `statusText` stay plain fields: consumers that write response
* metadata during render (e.g. JSX response components) must still
* treat later status writes and cleanup-time retractions as no-ops.
*/

@@ -239,10 +240,35 @@ committed?: boolean;

/**
* The type of `RequestEvent.locals` — a module-augmentable interface so
* applications can type the state their middleware hangs on the event.
* Augment it through the package that re-exports the event (interface
* identity flows through the re-export chain):
*
* ```ts
* declare module "@solidjs/web" {
* interface RequestEventLocals {
* user: User;
* }
* }
* ```
*
* The index signature keeps un-augmented usage permissive — `locals` is a
* free-form bag by default — so augmentation adds precision for the keys
* it names without gating existing code. The flip side: unaugmented keys
* read as `any` rather than erroring, a deliberate trade (a strict-only
* `locals` would break every untyped write that works today).
*/
export interface RequestEventLocals {
[key: string | number | symbol]: any;
}
/**
* The per-request context available on the server: the incoming `Request`
* and a `locals` bag integrations and middleware can hang state on.
* Frameworks typically extend this shape with richer fields (e.g. a
* `response` head — see `ResponseStub`).
* and a `locals` bag integrations and middleware can hang state on (typed
* through the augmentable `RequestEventLocals`). Frameworks typically
* extend this shape with richer fields (e.g. a `response` head — see
* `ResponseStub`).
*/
export interface RequestEvent {
request: Request;
locals: Record<string | number | symbol, any>;
locals: RequestEventLocals;
}

@@ -257,3 +283,125 @@ /**

export function Assets(props: { children?: JSX.Element }): JSX.Element;
/** A fresh, uncommitted response head. */
export function createResponseStub(): ResponseStub;
/**
* The canonical request event for HTTP handlers: the incoming `Request`, a
* `locals` bag, and a `response` head stub the render writes to. `init`
* spreads over the defaults so frameworks can extend the shape.
*/
export function createRequestEvent<T extends object = {}>(
request: Request,
init?: T
): RequestEvent & { response: ResponseStub } & T;
/**
* The status an outgoing redirect should use for a response head carrying
* a `Location`: the stub's own status when it is a redirect status, 302
* otherwise.
*/
export function getExpectedRedirectStatus(response: ResponseStub): number;
/**
* Flips a response stub to `committed` — the moment its head freezes on
* the wire — and instruments the stub's `headers` mutating methods
* (`set`/`append`/`delete`, patched in place; the `Headers` identity and
* reads are untouched) so a post-commit write fails loudly instead of
* silently missing the wire: it throws in the dev build and reports +
* no-ops otherwise. Every head materialization path commits through here
* (`createSSRResponse`, the server-function handler's commit seam);
* integrations deriving their own heads should too.
*
* `allowLateLocation` is the stream path's documented exception: a
* `Location` set after the shell flushed is still honored client-side
* (stream completion appends a `window.location` script), so that one
* write stays permitted there.
*/
export function commitResponseStub(
stub: ResponseStub,
options?: { allowLateLocation?: boolean }
): ResponseStub;
/**
* Handler-lifecycle plumbing — a response's exit through the request
* event's response-stub lifecycle: page results leave through
* `createSSRResponse`, any other `Response` (a middleware early return, an
* API result) leaves through `commitEventResponse`; application middleware
* never calls this. Folds the event's stub onto the outgoing response —
* `Set-Cookie` appends entry-by-entry alongside the response's own, other
* stub headers fill gaps only (never the wire-protocol family the handlers
* own, never `Content-Type`/`Content-Length` on a bodiless response), the
* status is never taken from the stub — then commits the stub
* (`commitResponseStub`: post-commit writes fail loudly). Responses with
* immutable headers are rebuilt around merged copies.
*
* Idempotent at handler edges: an already-committed stub passes the
* response through untouched, so a handler may apply this unconditionally
* after its middleware chain unwinds — page responses from
* `createSSRResponse` come back committed and do not double-fold.
*
* `event` defaults to the ambient `getRequestEvent()`.
*/
export function commitEventResponse(response: Response, event?: RequestEvent): Response;
/**
* The cookie codec (the platform-gap primitives — see cookies.d.ts): ALL
* of core's cookie surface. The blessed patterns are
* `parseCookieHeader(event.request.headers.get("cookie"))` for reads and
* `event.response.headers.append("set-cookie", serializeCookie(name,
* value, options))` for writes.
*/
export { parseCookieHeader, serializeCookie } from "./cookies.js";
export type { CookieOptions } from "./cookies.js";
export interface SSRResponseOptions {
/** Base head; the stub's status/headers win over it. */
responseInit?: ResponseInit;
/** Nonce carried by the post-flush `<script>` redirect fallback. */
nonce?: string;
/** Rewrites each outgoing HTML chunk (entry script injection, ...). */
transformChunk?: (chunk: string) => string;
}
/**
* Derives the outgoing `Response` for an SSR render result, running the
* response-head lifecycle against `event.response`: commit at shell flush,
* pre-flush `Location` becomes a real redirect, post-flush `Location`
* appends a client-side script redirect before the stream closes.
* Synchronous for string results; resolves at shell flush for stream
* results.
*/
export function createSSRResponse(
result: string,
event: RequestEvent | undefined,
options?: SSRResponseOptions
): Response;
export function createSSRResponse(
result: { pipe(writable: { write: (v: string) => void; end: () => void }): void },
event: RequestEvent | undefined,
options?: SSRResponseOptions
): Promise<Response>;
/**
* Fetch-style middleware: return a `Response` to answer the request, or
* call `next()` (optionally with a substitute `Request`) to advance the
* chain and observe/replace the eventual response.
*/
export type FetchMiddleware = (
request: Request,
next: (request?: Request) => Promise<Response>
) => Response | Promise<Response>;
/**
* Composes fetch-style middleware into one function of the same shape;
* the terminal `next` dispatches to the actual handler. Runs in whatever
* scope the caller established (`provideRequestEvent`), so
* `getRequestEvent()` works exactly as in application code.
*/
export function composeMiddleware(
middlewares: FetchMiddleware[]
): (
request: Request,
next: (request?: Request) => Response | Promise<Response>
) => Promise<Response>;
export function untrack<T>(fn: () => T): T;

@@ -358,3 +506,7 @@

export function setStyleProperty(node: Element, name: string, value: any): void;
/** @deprecated not supported on the server side — register assets through the render context instead */
/**
* @internal See client.d.ts — head-management RFC policy: ambient CSS is
* unmanaged; the head registry owns directly-mounted stylesheet lifecycle.
* @deprecated not supported on the server side — register assets through the render context instead
*/
export function acquireAsset(descriptor: unknown): () => void;

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