Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

svelte

Package Overview
Dependencies
Maintainers
3
Versions
1058
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

svelte - npm Package Compare versions

Comparing version
5.52.0
to
5.53.0
+1
-1
package.json

@@ -5,3 +5,3 @@ {

"license": "MIT",
"version": "5.52.0",
"version": "5.53.0",
"type": "module",

@@ -8,0 +8,0 @@ "types": "./types/index.d.ts",

@@ -104,3 +104,8 @@ /** @import { Expression } from 'estree' */

module,
css: ast.css ? visit(ast.css) : undefined
css: ast.css ? visit(ast.css) : undefined,
// put it on _comments not comments because the latter is checked by prettier and then fails
// if we don't adjust stuff accordingly in our prettier plugin, and so it would be kind of an
// indirect breaking change for people updating their Svelte version but not their prettier plugin version.
// We can keep it as comments for the modern AST because the modern AST is not used in the plugin yet.
_comments: ast.comments?.length > 0 ? ast.comments : undefined
};

@@ -107,0 +112,0 @@ },

@@ -509,2 +509,11 @@ /** @import { Expression, Identifier, SourceLocation } from 'estree' */

function read_attribute(parser) {
/** @type {AST.JSComment | null} */
// eslint-disable-next-line no-useless-assignment -- it is, in fact, eslint that is useless
let comment = null;
while ((comment = read_comment(parser))) {
parser.root.comments.push(comment);
parser.allow_whitespace();
}
const start = parser.index;

@@ -707,2 +716,46 @@

/**
* @param {Parser} parser
* @returns {AST.JSComment | null}
*/
function read_comment(parser) {
const start = parser.index;
if (parser.eat('//')) {
const value = parser.read_until(/\n/);
const end = parser.index;
return {
type: 'Line',
start,
end,
value,
loc: {
start: locator(start),
end: locator(end)
}
};
}
if (parser.eat('/*')) {
const value = parser.read_until(/\*\//);
parser.eat('*/');
const end = parser.index;
return {
type: 'Block',
start,
end,
value,
loc: {
start: locator(start),
end: locator(end)
}
};
}
return null;
}
/**
* @param {string} name

@@ -709,0 +762,0 @@ * @returns {any}

@@ -18,3 +18,13 @@ /** @import { BlockStatement } from 'estree' */

export function SvelteBoundary(node, context) {
// if this has a `pending` snippet, render it
// Extract the `failed` snippet/attribute
const failed_snippet = /** @type {AST.SnippetBlock | undefined} */ (
node.fragment.nodes.find(
(node) => node.type === 'SnippetBlock' && node.expression.name === 'failed'
)
);
const failed_attribute = /** @type {AST.Attribute} */ (
node.attributes.find((node) => node.type === 'Attribute' && node.name === 'failed')
);
// Extract the `pending` snippet/attribute
const pending_attribute = /** @type {AST.Attribute} */ (

@@ -28,4 +38,3 @@ node.attributes.find((node) => node.type === 'Attribute' && node.name === 'pending')

!context.state.scope.evaluate(pending_attribute.value.expression).is_defined;
const pending_snippet = /** @type {AST.SnippetBlock} */ (
const pending_snippet = /** @type {AST.SnippetBlock | undefined} */ (
node.fragment.nodes.find(

@@ -36,39 +45,98 @@ (node) => node.type === 'SnippetBlock' && node.expression.name === 'pending'

const children_nodes = node.fragment.nodes.filter(
(child) =>
!(child.type === 'SnippetBlock' && ['failed', 'pending'].includes(child.expression.name))
);
const children_fragment = { ...node.fragment, nodes: children_nodes };
const children_block = /** @type {BlockStatement} */ (
context.visit(children_fragment, {
...context.state,
scope: context.state.scopes.get(node.fragment) ?? context.state.scope
})
);
/** @type {BlockStatement} */
let children_body;
if (pending_attribute || pending_snippet) {
if (pending_attribute && is_pending_attr_nullish && !pending_snippet) {
const callee = build_attribute_value(
pending_attribute.value,
context,
(expression) => expression,
false,
true
);
const pending = b.call(callee, b.id('$$renderer'));
const block = /** @type {BlockStatement} */ (context.visit(node.fragment));
context.state.template.push(
const { callee, pending_block } = build_pending_attribute_block(pending_attribute, context);
children_body = b.block([
b.if(
callee,
b.block(build_template([block_open_else, b.stmt(pending), block_close])),
b.block(build_template([block_open, block, block_close]))
pending_block,
b.block(build_template([block_open, children_block, block_close]))
)
);
]);
} else {
const pending = pending_attribute
? b.call(
build_attribute_value(
pending_attribute.value,
context,
(expression) => expression,
false,
true
),
b.id('$$renderer')
)
: /** @type {BlockStatement} */ (context.visit(pending_snippet.body));
context.state.template.push(block_open_else, pending, block_close);
children_body = pending_attribute
? build_pending_attribute_block(pending_attribute, context).pending_block
: build_pending_snippet_block(/** @type {AST.SnippetBlock} */ (pending_snippet), context);
}
} else {
const block = /** @type {BlockStatement} */ (context.visit(node.fragment));
context.state.template.push(block_open, block, block_close);
children_body = b.block(build_template([block_open, children_block, block_close]));
}
// When there's no `failed` snippet/attribute, skip the boundary wrapper entirely
// (saves bytes / more performant at runtime)
if (!failed_snippet && !failed_attribute) {
context.state.template.push(...children_body.body);
return;
}
const props = b.object([]);
if (failed_attribute && !failed_snippet) {
const failed_callee = build_attribute_value(
failed_attribute.value,
context,
(expression) => expression,
false,
true
);
props.properties.push(b.init('failed', failed_callee));
} else if (failed_snippet) {
context.visit(failed_snippet, context.state);
props.properties.push(b.init('failed', failed_snippet.expression));
}
context.state.template.push(
b.stmt(b.call('$$renderer.boundary', props, b.arrow([b.id('$$renderer')], children_body)))
);
}
/**
* @param {AST.Attribute} attribute
* @param {ComponentContext} context
*/
function build_pending_attribute_block(attribute, context) {
const callee = build_attribute_value(
attribute.value,
context,
(expression) => expression,
false,
true
);
const pending = b.call(callee, b.id('$$renderer'));
return {
callee,
pending_block: b.block(build_template([block_open_else, b.stmt(pending), block_close]))
};
}
/**
* @param {AST.SnippetBlock} snippet
* @param {ComponentContext} context
*/
function build_pending_snippet_block(snippet, context) {
return b.block(
build_template([
block_open_else,
/** @type {BlockStatement} */ (context.visit(snippet.body)),
block_close
])
);
}

@@ -21,2 +21,4 @@ /** @import { AST } from '#compiler'; */

export function print(ast, options = undefined) {
const comments = (ast.type === 'Root' && ast.comments) || [];
return esrap.print(

@@ -26,7 +28,7 @@ ast,

...ts({
comments: ast.type === 'Root' ? ast.comments : [],
comments,
getLeadingComments: options?.getLeadingComments,
getTrailingComments: options?.getTrailingComments
}),
...svelte_visitors,
...svelte_visitors(comments),
...css_visitors

@@ -62,7 +64,9 @@ })

/**
* @param {AST.BaseNode} node
* @param {AST.BaseElement['attributes']} attributes
* @param {Context} context
* @param {AST.JSComment[]} comments
* @returns {boolean} true if attributes were formatted on multiple lines
*/
function attributes(attributes, context) {
function attributes(node, attributes, context, comments) {
if (attributes.length === 0) {

@@ -72,17 +76,48 @@ return false;

// Measure total width of all attributes when rendered inline
const child_context = context.new();
let length = -1;
for (const attribute of attributes) {
child_context.write(' ');
child_context.visit(attribute);
let comment_index = comments.findIndex((comment) => comment.start > node.start);
if (comment_index === -1) {
comment_index = comments.length;
}
const multiline = child_context.measure() > LINE_BREAK_THRESHOLD;
const separator = context.new();
const children = attributes.map((attribute) => {
const child_context = context.new();
while (comment_index < comments.length) {
const comment = comments[comment_index];
if (comment.start < attribute.start) {
if (comment.type === 'Line') {
child_context.write('//' + comment.value);
child_context.newline();
} else {
child_context.write('/*' + comment.value + '*/'); // TODO match indentation?
child_context.append(separator);
}
comment_index += 1;
} else {
break;
}
}
child_context.visit(attribute);
length += child_context.measure() + 1;
return child_context;
});
let multiline = context.multiline || length > LINE_BREAK_THRESHOLD;
if (multiline) {
separator.newline();
context.indent();
for (const attribute of attributes) {
for (const child of children) {
context.newline();
context.visit(attribute);
context.append(child);
}

@@ -92,3 +127,7 @@ context.dedent();

} else {
context.append(child_context);
separator.write(' ');
for (const child of children) {
context.write(' ');
context.append(child);
}
}

@@ -102,4 +141,5 @@

* @param {Context} context
* @param {AST.JSComment[]} comments
*/
function base_element(node, context) {
function base_element(node, context, comments) {
const child_context = context.new();

@@ -120,3 +160,3 @@

const multiline_attributes = attributes(node.attributes, child_context);
const multiline_attributes = attributes(node, node.attributes, child_context, comments);
const is_doctype_node = node.name.toLowerCase() === '!doctype';

@@ -294,4 +334,7 @@ const is_self_closing =

/** @type {Visitors<AST.SvelteNode>} */
const svelte_visitors = {
/**
* @param {AST.JSComment[]} comments
* @returns {Visitors<AST.SvelteNode>}
*/
const svelte_visitors = (comments) => ({
Root(node, context) {

@@ -326,3 +369,3 @@ if (node.options) {

context.write('<script');
attributes(node.attributes, context);
attributes(node, node.attributes, context, comments);
context.write('>');

@@ -557,3 +600,3 @@ block(context, node.content);

Component(node, context) {
base_element(node, context);
base_element(node, context, comments);
},

@@ -694,3 +737,3 @@

RegularElement(node, context) {
base_element(node, context);
base_element(node, context, comments);
},

@@ -705,3 +748,3 @@

SlotElement(node, context) {
base_element(node, context);
base_element(node, context, comments);
},

@@ -762,3 +805,3 @@

context.write('<style');
attributes(node.attributes, context);
attributes(node, node.attributes, context, comments);
context.write('>');

@@ -790,3 +833,3 @@

SvelteBoundary(node, context) {
base_element(node, context);
base_element(node, context, comments);
},

@@ -800,3 +843,3 @@

context.write('}');
attributes(node.attributes, context);
attributes(node, node.attributes, context, comments);
if (node.fragment && node.fragment.nodes.length > 0) {

@@ -812,3 +855,3 @@ context.write('>');

SvelteDocument(node, context) {
base_element(node, context);
base_element(node, context, comments);
},

@@ -822,3 +865,3 @@

context.write('}');
attributes(node.attributes, context);
attributes(node, node.attributes, context, comments);

@@ -835,15 +878,15 @@ if (node.fragment && node.fragment.nodes.length > 0) {

SvelteFragment(node, context) {
base_element(node, context);
base_element(node, context, comments);
},
SvelteHead(node, context) {
base_element(node, context);
base_element(node, context, comments);
},
SvelteSelf(node, context) {
base_element(node, context);
base_element(node, context, comments);
},
SvelteWindow(node, context) {
base_element(node, context);
base_element(node, context, comments);
},

@@ -856,3 +899,3 @@

TitleElement(node, context) {
base_element(node, context);
base_element(node, context, comments);
},

@@ -887,2 +930,2 @@

}
};
});

@@ -26,2 +26,4 @@ export const EACH_ITEM_REACTIVE = 1;

export const HYDRATION_START_ELSE = '[!';
/** used to indicate that a boundary's `failed` snippet was rendered on the server */
export const HYDRATION_START_FAILED = '[?';
export const HYDRATION_END = ']';

@@ -28,0 +30,0 @@ export const HYDRATION_ERROR = {};

@@ -15,3 +15,3 @@ /** @import { Source, TemplateNode } from '#client' */

import { is_runes } from '../../context.js';
import { Batch, flushSync, is_flushing_sync } from '../../reactivity/batch.js';
import { Batch, current_batch, flushSync, is_flushing_sync } from '../../reactivity/batch.js';
import { BranchManager } from './branches.js';

@@ -88,3 +88,3 @@ import { capture, unset_context } from '../../reactivity/async.js';

} finally {
unset_context();
unset_context(false);

@@ -91,0 +91,0 @@ // without this, the DOM does not update until two ticks after the promise

@@ -9,3 +9,3 @@ /** @import { Effect, Source, TemplateNode, } from '#client' */

} from '#client/constants';
import { HYDRATION_START_ELSE } from '../../../../constants.js';
import { HYDRATION_START_ELSE, HYDRATION_START_FAILED } from '../../../../constants.js';
import { component_context, set_component_context } from '../../context.js';

@@ -61,6 +61,7 @@ import { handle_error, invoke_error_boundary } from '../../error-handling.js';

* @param {((anchor: Node) => void)} children
* @param {((error: unknown) => unknown) | undefined} [transform_error]
* @returns {void}
*/
export function boundary(node, props, children) {
new Boundary(node, props, children);
export function boundary(node, props, children, transform_error) {
new Boundary(node, props, children, transform_error);
}

@@ -74,2 +75,9 @@

/**
* API-level transformError transform function. Transforms errors before they reach the `failed` snippet.
* Inherited from parent boundary, or defaults to identity.
* @type {(error: unknown) => unknown}
*/
transform_error;
/** @type {TemplateNode} */

@@ -137,4 +145,5 @@ #anchor;

* @param {((anchor: Node) => void)} children
* @param {((error: unknown) => unknown) | undefined} [transform_error]
*/
constructor(node, props, children) {
constructor(node, props, children, transform_error) {
this.#anchor = node;

@@ -154,2 +163,5 @@ this.#props = props;

// Inherit transform_error from parent boundary, or use the provided one, or default to identity
this.transform_error = transform_error ?? this.parent?.transform_error ?? ((e) => e);
this.#effect = block(() => {

@@ -160,3 +172,11 @@ if (hydrating) {

if (comment.data === HYDRATION_START_ELSE) {
const server_rendered_pending = comment.data === HYDRATION_START_ELSE;
const server_rendered_failed = comment.data.startsWith(HYDRATION_START_FAILED);
if (server_rendered_failed) {
// Server rendered the failed snippet - hydrate it.
// The serialized error is embedded in the comment: <!--[?<json>-->
const serialized_error = JSON.parse(comment.data.slice(HYDRATION_START_FAILED.length));
this.#hydrate_failed_content(serialized_error);
} else if (server_rendered_pending) {
this.#hydrate_pending_content();

@@ -184,2 +204,18 @@ } else {

/**
* @param {unknown} error The deserialized error from the server's hydration comment
*/
#hydrate_failed_content(error) {
const failed = this.#props.failed;
if (!failed) return;
this.#failed_effect = branch(() => {
failed(
this.#anchor,
() => error,
() => () => {}
);
});
}
#hydrate_pending_content() {

@@ -426,6 +462,7 @@ const pending = this.#props.pending;

queue_micro_task(() => {
/** @param {unknown} transformed_error */
const handle_error_result = (transformed_error) => {
try {
calling_on_error = true;
onerror?.(error, reset);
onerror?.(transformed_error, reset);
calling_on_error = false;

@@ -451,3 +488,3 @@ } catch (error) {

this.#anchor,
() => error,
() => transformed_error,
() => reset

@@ -462,2 +499,30 @@ );

}
};
queue_micro_task(() => {
// Run the error through the API-level transformError transform (e.g. SvelteKit's handleError)
/** @type {unknown} */
var result;
try {
result = this.transform_error(error);
} catch (e) {
invoke_error_boundary(e, this.#effect && this.#effect.parent);
return;
}
if (
result !== null &&
typeof result === 'object' &&
typeof (/** @type {any} */ (result).then) === 'function'
) {
// transformError returned a Promise — wait for it
/** @type {any} */ (result).then(
handle_error_result,
/** @param {unknown} e */
(e) => invoke_error_boundary(e, this.#effect && this.#effect.parent)
);
} else {
// Synchronous result — handle immediately
handle_error_result(result);
}
});

@@ -464,0 +529,0 @@ }

@@ -5,3 +5,3 @@ /** @import { TemplateNode } from '#client' */

import { block } from '../../reactivity/effects.js';
import { COMMENT_NODE, HEAD_EFFECT } from '#client/constants';
import { COMMENT_NODE, EFFECT_PRESERVED, HEAD_EFFECT } from '#client/constants';

@@ -53,3 +53,5 @@ /**

try {
block(() => render_fn(anchor), HEAD_EFFECT);
// normally a branch is the child of a block and would have the EFFECT_PRESERVED flag,
// but since head blocks don't necessarily only have direct branch children we add it on the block itself
block(() => render_fn(anchor), HEAD_EFFECT | EFFECT_PRESERVED);
} finally {

@@ -56,0 +58,0 @@ if (was_hydrating) {

import { hydrating, reset, set_hydrate_node, set_hydrating } from '../hydration.js';
import { create_comment, create_element } from '../operations.js';
import { create_trusted_html } from '../reconciler.js';
import { attach } from './attachments.js';

@@ -17,3 +18,3 @@

var select = create_element('select');
select.innerHTML = '<option><span>t</span></option>';
select.innerHTML = create_trusted_html('<option><span>t</span></option>');
supported = /** @type {Element} */ (select.firstChild)?.firstChild?.nodeType === 1;

@@ -20,0 +21,0 @@ }

@@ -14,3 +14,3 @@ import { create_element } from './operations.js';

/** @param {string} html */
function create_trusted_html(html) {
export function create_trusted_html(html) {
return /** @type {string} */ (policy?.createHTML(html) ?? html);

@@ -17,0 +17,0 @@ }

@@ -69,3 +69,2 @@ /** @import { Blocker, Effect, Value } from '#client' */

batch?.deactivate();
unset_context();

@@ -211,6 +210,7 @@ }

export function unset_context() {
export function unset_context(deactivate_batch = true) {
set_active_effect(null);
set_active_reaction(null);
set_component_context(null);
if (deactivate_batch) current_batch?.deactivate();

@@ -276,5 +276,3 @@ if (DEV) {

blocker.settled = true;
unset_context();
current_batch?.deactivate();
});

@@ -281,0 +279,0 @@ }

@@ -75,4 +75,2 @@ /** @import { Fork } from 'svelte' */

export class Batch {
committed = false;
/**

@@ -429,3 +427,2 @@ * The current values of any sources that are updated in this batch

this.committed = true;
batches.delete(this);

@@ -432,0 +429,0 @@ }

@@ -136,13 +136,3 @@ /** @import { Derived, Effect, Source } from '#client' */

// We call `unset_context` to undo any `save` calls that happen inside `fn()`
Promise.resolve(fn())
.then(d.resolve, d.reject)
.then(() => {
if (batch === current_batch && batch.committed) {
// if the batch was rejected as stale, we need to cleanup
// after any `$.save(...)` calls inside `fn()`
batch.deactivate();
}
unset_context();
});
Promise.resolve(fn()).then(d.resolve, d.reject).finally(unset_context);
} catch (error) {

@@ -149,0 +139,0 @@ d.reject(error);

@@ -84,2 +84,3 @@ /** @import { ComponentContext, Effect, EffectNodes, TemplateNode } from '#client' */

* recover?: boolean;
* transformError?: (error: unknown) => unknown;
* } : {

@@ -92,2 +93,3 @@ * target: Document | Element | ShadowRoot;

* recover?: boolean;
* transformError?: (error: unknown) => unknown;
* }} options

@@ -163,3 +165,6 @@ * @returns {Exports}

*/
function _mount(Component, { target, anchor, props = {}, events, context, intro = true }) {
function _mount(
Component,
{ target, anchor, props = {}, events, context, intro = true, transformError }
) {
init_operations();

@@ -212,3 +217,4 @@

pop();
}
},
transformError
);

@@ -215,0 +221,0 @@

@@ -68,3 +68,3 @@ /** @import { ComponentType, SvelteComponent, Component } from 'svelte' */

* @param {Component<Props> | ComponentType<SvelteComponent<Props>>} component
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp }} [options]
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} [options]
* @returns {RenderOutput}

@@ -71,0 +71,0 @@ */

@@ -6,6 +6,7 @@ /** @import { Component } from 'svelte' */

import { abort } from './abort-signal.js';
import { pop, push, set_ssr_context, ssr_context, save } from './context.js';
import { pop, push, set_ssr_context, ssr_context } from './context.js';
import * as e from './errors.js';
import * as w from './warnings.js';
import { BLOCK_CLOSE, BLOCK_OPEN } from './hydration.js';
import { HYDRATION_START_FAILED } from '../../constants.js';
import { attributes } from './index.js';

@@ -54,2 +55,13 @@ import { get_render_context, with_render_context, init_render_context } from './render-context.js';

/**
* If set, this renderer is an error boundary. When async collection
* of the children fails, the failed snippet is rendered instead.
* @type {{
* failed: (renderer: Renderer, error: unknown, reset: () => void) => void;
* transformError: (error: unknown) => unknown;
* context: SSRContext | null;
* } | null}
*/
#boundary = null;
/**
* The type of string content that this renderer is accumulating.

@@ -209,5 +221,5 @@ * @type {RendererType}

if (result instanceof Promise) {
result.finally(() => {
set_ssr_context(null);
});
// catch to avoid unhandled promise rejections - we'll end up throwing in `collect_async` if something fails
result.catch(noop);
result.finally(() => set_ssr_context(null)).catch(noop);

@@ -218,4 +230,2 @@ if (child.global.mode === 'sync') {

// just to avoid unhandled promise rejections -- we'll end up throwing in `collect_async` if something fails
result.catch(() => {});
child.promise = result;

@@ -228,2 +238,78 @@ }

/**
* Render children inside an error boundary. If the children throw and the API-level
* `transformError` transform handles the error (doesn't re-throw), the `failed` snippet is
* rendered instead. Otherwise the error propagates.
*
* @param {{ failed?: (renderer: Renderer, error: unknown, reset: () => void) => void }} props
* @param {(renderer: Renderer) => MaybePromise<void>} children_fn
*/
boundary(props, children_fn) {
// Create a child renderer for the boundary content.
// Mark it as a boundary so that #collect_content_async can catch
// errors from nested async children and render the failed snippet.
const child = new Renderer(this.global, this);
this.#out.push(child);
const parent_context = ssr_context;
if (props.failed) {
child.#boundary = {
failed: props.failed,
transformError: this.global.transformError,
context: parent_context
};
}
set_ssr_context({
...ssr_context,
p: parent_context,
c: null,
r: child
});
try {
const result = children_fn(child);
set_ssr_context(parent_context);
if (result instanceof Promise) {
if (child.global.mode === 'sync') {
e.await_invalid();
}
result.catch(noop);
child.promise = result;
}
} catch (error) {
// synchronous errors are handled here, async errors will be handled in #collect_content_async
set_ssr_context(parent_context);
const failed_snippet = props.failed;
if (!failed_snippet) throw error;
const result = this.global.transformError(error);
child.#out.length = 0;
child.#boundary = null;
if (result instanceof Promise) {
if (this.global.mode === 'sync') {
e.await_invalid();
}
child.promise = /** @type {Promise<unknown>} */ (result).then((transformed) => {
child.#out.push(`<!--${HYDRATION_START_FAILED}${JSON.stringify(transformed)}-->`);
failed_snippet(child, transformed, noop);
child.#out.push(BLOCK_CLOSE);
});
child.promise.catch(noop);
} else {
child.#out.push(`<!--${HYDRATION_START_FAILED}${JSON.stringify(result)}-->`);
failed_snippet(child, result, noop);
child.#out.push(BLOCK_CLOSE);
}
}
}
/**
* Create a component renderer. The component renderer inherits the state from the parent,

@@ -602,3 +688,32 @@ * but has its own content. It is treated as an ordering boundary for ondestroy callbacks.

} else if (item instanceof Renderer) {
await item.#collect_content_async(content);
if (item.#boundary) {
// This renderer is an error boundary - collect into a separate
// accumulator so we can discard partial content on error
/** @type {AccumulatedContent} */
const boundary_content = { head: '', body: '' };
try {
await item.#collect_content_async(boundary_content);
// Success - merge into the main content
content.head += boundary_content.head;
content.body += boundary_content.body;
} catch (error) {
const { context, failed, transformError } = item.#boundary;
set_ssr_context(context);
let transformed = await transformError(error);
// Render the failed snippet instead of the partial children content
const failed_renderer = new Renderer(item.global, item);
failed_renderer.type = item.type;
failed_renderer.#out.push(
`<!--${HYDRATION_START_FAILED}${JSON.stringify(transformed)}-->`
);
failed(failed_renderer, transformed, noop);
failed_renderer.#out.push(BLOCK_CLOSE);
await failed_renderer.#collect_content_async(content);
}
} else {
await item.#collect_content_async(content);
}
}

@@ -631,3 +746,3 @@ }

* @param {import('svelte').Component<Props>} component
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp }} options
* @param {{ props?: Omit<Props, '$$slots' | '$$events'>; context?: Map<any, any>; idPrefix?: string; csp?: Csp; transformError?: (error: unknown) => unknown }} options
* @returns {Renderer}

@@ -640,3 +755,8 @@ */

const renderer = new Renderer(
new SSRState(mode, options.idPrefix ? options.idPrefix + '-' : '', options.csp)
new SSRState(
mode,
options.idPrefix ? options.idPrefix + '-' : '',
options.csp,
options.transformError
)
);

@@ -752,2 +872,9 @@

/**
* `transformError` passed to `render`. Called when an error boundary catches an error.
* Throws by default if unset in `render`.
* @type {(error: unknown) => unknown}
*/
transformError;
/** @type {{ path: number[], value: string }} */

@@ -760,7 +887,14 @@ #title = { path: [], value: '' };

* @param {Csp} csp
* @param {((error: unknown) => unknown) | undefined} [transformError]
*/
constructor(mode, id_prefix = '', csp = { hash: false }) {
constructor(mode, id_prefix = '', csp = { hash: false }, transformError) {
this.mode = mode;
this.csp = { ...csp, script_hashes: [] };
this.transformError =
transformError ??
((error) => {
throw error;
});
let uid = 1;

@@ -767,0 +901,0 @@ this.uid = () => `${id_prefix}s${uid++}`;

@@ -122,3 +122,4 @@ /** @import { ComponentConstructorOptions, ComponentType, SvelteComponent, Component } from 'svelte' */

intro: options.intro ?? false,
recover: options.recover
recover: options.recover,
transformError: options.transformError
});

@@ -125,0 +126,0 @@

@@ -28,6 +28,6 @@ /** @import { SvelteComponent } from '../index.js' */

const component_constructor = as_class_component(component);
/** @type {(props?: {}, opts?: { $$slots?: {}; context?: Map<any, any>; csp?: Csp }) => LegacyRenderResult & PromiseLike<LegacyRenderResult> } */
const _render = (props, { context, csp } = {}) => {
/** @type {(props?: {}, opts?: { $$slots?: {}; context?: Map<any, any>; csp?: Csp; transformError?: (error: unknown) => unknown }) => LegacyRenderResult & PromiseLike<LegacyRenderResult> } */
const _render = (props, { context, csp, transformError } = {}) => {
// @ts-expect-error the typings are off, but this will work if the component is compiled in SSR mode
const result = render(component, { props, context, csp });
const result = render(component, { props, context, csp, transformError });

@@ -34,0 +34,0 @@ const munged = Object.defineProperties(

@@ -7,3 +7,3 @@ // generated during release, do not modify

*/
export const VERSION = '5.52.0';
export const VERSION = '5.53.0';
export const PUBLIC_VERSION = '5';

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 not supported yet