@flareapp/svelte 2.0.0-alpha.0 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.svelte-kit/__package__/FlareErrorBoundary.svelte +78 -0
- package/.svelte-kit/__package__/FlareErrorBoundary.svelte.d.ts +14 -0
- package/.svelte-kit/__package__/componentTree.d.ts +9 -0
- package/.svelte-kit/__package__/componentTree.js +99 -0
- package/.svelte-kit/__package__/config.d.ts +12 -0
- package/.svelte-kit/__package__/config.js +25 -0
- package/.svelte-kit/__package__/contextToAttributes.d.ts +3 -0
- package/.svelte-kit/__package__/contextToAttributes.js +7 -0
- package/.svelte-kit/__package__/createFlareErrorHandler.d.ts +17 -0
- package/.svelte-kit/__package__/createFlareErrorHandler.js +34 -0
- package/.svelte-kit/__package__/extractComponentInfo.d.ts +8 -0
- package/.svelte-kit/__package__/extractComponentInfo.js +42 -0
- package/.svelte-kit/__package__/getErrorOrigin.d.ts +3 -0
- package/.svelte-kit/__package__/getErrorOrigin.js +64 -0
- package/.svelte-kit/__package__/identify.d.ts +1 -0
- package/.svelte-kit/__package__/identify.js +10 -0
- package/.svelte-kit/__package__/index.d.ts +6 -0
- package/.svelte-kit/__package__/index.js +7 -0
- package/.svelte-kit/__package__/preprocessor.d.ts +5 -0
- package/.svelte-kit/__package__/preprocessor.js +53 -0
- package/.svelte-kit/__package__/types.d.ts +8 -0
- package/.svelte-kit/__package__/types.js +1 -0
- package/.svelte-kit/__package__/version.d.ts +1 -0
- package/.svelte-kit/__package__/version.js +2 -0
- package/README.md +15 -331
- package/dist/config.d.ts +12 -0
- package/dist/config.js +25 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +5 -5
- package/test-results/.last-run.json +4 -0
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { Snippet } from 'svelte';
|
|
3
|
+
|
|
4
|
+
import { __flareRegisterComponent, getComponentTreeContext } from './componentTree.js';
|
|
5
|
+
import { createFlareErrorHandler, type FlareErrorHandlerOptions } from './createFlareErrorHandler.js';
|
|
6
|
+
|
|
7
|
+
interface Props {
|
|
8
|
+
children: Snippet;
|
|
9
|
+
failed?: Snippet<[error: Error, reset: () => void]>;
|
|
10
|
+
resetKeys?: unknown[];
|
|
11
|
+
beforeEvaluate?: FlareErrorHandlerOptions['beforeEvaluate'];
|
|
12
|
+
beforeSubmit?: FlareErrorHandlerOptions['beforeSubmit'];
|
|
13
|
+
afterSubmit?: FlareErrorHandlerOptions['afterSubmit'];
|
|
14
|
+
onReset?: (error: Error | null) => void;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
let {
|
|
18
|
+
children,
|
|
19
|
+
failed: fallbackSnippet,
|
|
20
|
+
resetKeys,
|
|
21
|
+
beforeEvaluate,
|
|
22
|
+
beforeSubmit,
|
|
23
|
+
afterSubmit,
|
|
24
|
+
onReset,
|
|
25
|
+
}: Props = $props();
|
|
26
|
+
|
|
27
|
+
let currentError: Error | null = $state(null);
|
|
28
|
+
let resetBoundary: (() => void) | null = $state(null);
|
|
29
|
+
|
|
30
|
+
let previousKeys: unknown[] | undefined;
|
|
31
|
+
$effect(() => {
|
|
32
|
+
if (!currentError || !resetKeys) {
|
|
33
|
+
previousKeys = resetKeys ? [...resetKeys] : undefined;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const lengthChanged = previousKeys?.length !== resetKeys.length;
|
|
38
|
+
const valuesChanged = resetKeys.some((key, i) => !Object.is(key, previousKeys?.[i]));
|
|
39
|
+
|
|
40
|
+
if (lengthChanged || valuesChanged) {
|
|
41
|
+
handleReset();
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
previousKeys = [...resetKeys];
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
function handleReset() {
|
|
48
|
+
const error = currentError;
|
|
49
|
+
currentError = null;
|
|
50
|
+
onReset?.(error);
|
|
51
|
+
resetBoundary?.();
|
|
52
|
+
resetBoundary = null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const ancestor = __flareRegisterComponent('FlareErrorBoundary', '@flareapp/svelte/FlareErrorBoundary.svelte');
|
|
56
|
+
|
|
57
|
+
const handler = $derived(
|
|
58
|
+
createFlareErrorHandler({ ancestor, beforeEvaluate, beforeSubmit, afterSubmit }),
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
function onerror(rawError: unknown, reset: () => void) {
|
|
62
|
+
resetBoundary = reset;
|
|
63
|
+
const error = rawError instanceof Error ? rawError : new Error(String(rawError));
|
|
64
|
+
currentError = error;
|
|
65
|
+
|
|
66
|
+
handler(rawError, reset);
|
|
67
|
+
}
|
|
68
|
+
</script>
|
|
69
|
+
|
|
70
|
+
<svelte:boundary {onerror}>
|
|
71
|
+
{@render children()}
|
|
72
|
+
|
|
73
|
+
{#snippet failed(error, reset)}
|
|
74
|
+
{#if fallbackSnippet}
|
|
75
|
+
{@render fallbackSnippet(error instanceof Error ? error : new Error(String(error)), handleReset)}
|
|
76
|
+
{/if}
|
|
77
|
+
{/snippet}
|
|
78
|
+
</svelte:boundary>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Snippet } from 'svelte';
|
|
2
|
+
import { type FlareErrorHandlerOptions } from './createFlareErrorHandler.js';
|
|
3
|
+
interface Props {
|
|
4
|
+
children: Snippet;
|
|
5
|
+
failed?: Snippet<[error: Error, reset: () => void]>;
|
|
6
|
+
resetKeys?: unknown[];
|
|
7
|
+
beforeEvaluate?: FlareErrorHandlerOptions['beforeEvaluate'];
|
|
8
|
+
beforeSubmit?: FlareErrorHandlerOptions['beforeSubmit'];
|
|
9
|
+
afterSubmit?: FlareErrorHandlerOptions['afterSubmit'];
|
|
10
|
+
onReset?: (error: Error | null) => void;
|
|
11
|
+
}
|
|
12
|
+
declare const FlareErrorBoundary: import("svelte").Component<Props, {}, "">;
|
|
13
|
+
type FlareErrorBoundary = ReturnType<typeof FlareErrorBoundary>;
|
|
14
|
+
export default FlareErrorBoundary;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export interface ComponentTreeNode {
|
|
2
|
+
name: string;
|
|
3
|
+
file: string;
|
|
4
|
+
parent: ComponentTreeNode | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function __flareRegisterComponent(name: string, file: string): ComponentTreeNode;
|
|
7
|
+
export declare function getComponentTreeContext(): ComponentTreeNode | null;
|
|
8
|
+
export declare function lookupComponentTree(fileName: string, ancestor?: ComponentTreeNode | null): string[];
|
|
9
|
+
export declare function findNode(fileName: string, ancestor?: ComponentTreeNode | null): ComponentTreeNode | undefined;
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { getContext, onDestroy, setContext } from 'svelte';
|
|
2
|
+
const CONTEXT_KEY = '__flare_component_tree';
|
|
3
|
+
const registry = new Map();
|
|
4
|
+
export function __flareRegisterComponent(name, file) {
|
|
5
|
+
let parent = null;
|
|
6
|
+
try {
|
|
7
|
+
parent = getContext(CONTEXT_KEY) ?? null;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
// getContext throws outside component init
|
|
11
|
+
}
|
|
12
|
+
const node = { name, file, parent };
|
|
13
|
+
try {
|
|
14
|
+
setContext(CONTEXT_KEY, node);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// setContext throws outside component init
|
|
18
|
+
}
|
|
19
|
+
let nodes = registry.get(file);
|
|
20
|
+
if (!nodes) {
|
|
21
|
+
nodes = new Set();
|
|
22
|
+
registry.set(file, nodes);
|
|
23
|
+
}
|
|
24
|
+
nodes.add(node);
|
|
25
|
+
try {
|
|
26
|
+
onDestroy(() => {
|
|
27
|
+
const set = registry.get(file);
|
|
28
|
+
if (set) {
|
|
29
|
+
set.delete(node);
|
|
30
|
+
if (set.size === 0) {
|
|
31
|
+
registry.delete(file);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// onDestroy throws outside component init
|
|
38
|
+
}
|
|
39
|
+
return node;
|
|
40
|
+
}
|
|
41
|
+
export function getComponentTreeContext() {
|
|
42
|
+
try {
|
|
43
|
+
return getContext(CONTEXT_KEY) ?? null;
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export function lookupComponentTree(fileName, ancestor) {
|
|
50
|
+
const node = findNode(fileName, ancestor);
|
|
51
|
+
if (!node) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
const hierarchy = [];
|
|
55
|
+
let current = node;
|
|
56
|
+
const seen = new Set();
|
|
57
|
+
while (current && !seen.has(current)) {
|
|
58
|
+
seen.add(current);
|
|
59
|
+
hierarchy.push(current.name);
|
|
60
|
+
current = current.parent;
|
|
61
|
+
}
|
|
62
|
+
return hierarchy;
|
|
63
|
+
}
|
|
64
|
+
export function findNode(fileName, ancestor) {
|
|
65
|
+
const normalizedLookup = normalizePath(fileName);
|
|
66
|
+
const candidates = [];
|
|
67
|
+
for (const [registeredFile, nodes] of registry) {
|
|
68
|
+
const normalizedFile = normalizePath(registeredFile);
|
|
69
|
+
const match = normalizedLookup === normalizedFile ||
|
|
70
|
+
normalizedLookup.endsWith(normalizedFile) ||
|
|
71
|
+
normalizedFile.endsWith(normalizedLookup);
|
|
72
|
+
if (match) {
|
|
73
|
+
for (const node of nodes) {
|
|
74
|
+
candidates.push(node);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
if (candidates.length === 0) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
if (ancestor && candidates.length > 1) {
|
|
82
|
+
return candidates.find((c) => hasAncestor(c, ancestor)) ?? candidates[0];
|
|
83
|
+
}
|
|
84
|
+
return candidates[0];
|
|
85
|
+
}
|
|
86
|
+
function hasAncestor(node, ancestor) {
|
|
87
|
+
let current = node.parent;
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
while (current && !seen.has(current)) {
|
|
90
|
+
if (current === ancestor)
|
|
91
|
+
return true;
|
|
92
|
+
seen.add(current);
|
|
93
|
+
current = current.parent;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
function normalizePath(filePath) {
|
|
98
|
+
return filePath.replace(/\\/g, '/').replace(/^.*?\/src\//, 'src/');
|
|
99
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PreprocessorGroup } from 'svelte/compiler';
|
|
2
|
+
import { type FlarePreprocessorOptions } from './preprocessor.js';
|
|
3
|
+
interface SvelteConfig {
|
|
4
|
+
preprocess?: PreprocessorGroup | PreprocessorGroup[];
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface WithFlareConfigOptions {
|
|
8
|
+
componentTracking?: boolean;
|
|
9
|
+
exclude?: FlarePreprocessorOptions['exclude'];
|
|
10
|
+
}
|
|
11
|
+
export declare function withFlareConfig(config: SvelteConfig, options?: WithFlareConfigOptions): SvelteConfig;
|
|
12
|
+
export { flarePreprocessor, type FlarePreprocessorOptions } from './preprocessor.js';
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { flarePreprocessor } from './preprocessor.js';
|
|
2
|
+
export function withFlareConfig(config, options) {
|
|
3
|
+
const { componentTracking = true, exclude } = options ?? {};
|
|
4
|
+
if (!componentTracking) {
|
|
5
|
+
return config;
|
|
6
|
+
}
|
|
7
|
+
const existing = normalizePreprocessors(config.preprocess);
|
|
8
|
+
if (existing.some((p) => !!p.__flareId)) {
|
|
9
|
+
return config;
|
|
10
|
+
}
|
|
11
|
+
const preprocessor = flarePreprocessor({ exclude });
|
|
12
|
+
preprocessor.__flareId = true;
|
|
13
|
+
return {
|
|
14
|
+
...config,
|
|
15
|
+
preprocess: [preprocessor, ...existing],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function normalizePreprocessors(preprocess) {
|
|
19
|
+
if (!preprocess)
|
|
20
|
+
return [];
|
|
21
|
+
if (Array.isArray(preprocess))
|
|
22
|
+
return preprocess;
|
|
23
|
+
return [preprocess];
|
|
24
|
+
}
|
|
25
|
+
export { flarePreprocessor } from './preprocessor.js';
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ComponentTreeNode } from './componentTree.js';
|
|
2
|
+
import type { FlareSvelteContext } from './types.js';
|
|
3
|
+
export interface FlareErrorHandlerOptions {
|
|
4
|
+
ancestor?: ComponentTreeNode | null;
|
|
5
|
+
beforeEvaluate?: (params: {
|
|
6
|
+
error: Error;
|
|
7
|
+
}) => void;
|
|
8
|
+
beforeSubmit?: (params: {
|
|
9
|
+
error: Error;
|
|
10
|
+
context: FlareSvelteContext;
|
|
11
|
+
}) => FlareSvelteContext;
|
|
12
|
+
afterSubmit?: (params: {
|
|
13
|
+
error: Error;
|
|
14
|
+
context: FlareSvelteContext;
|
|
15
|
+
}) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function createFlareErrorHandler(options?: FlareErrorHandlerOptions): (rawError: unknown, _reset: () => void) => Promise<void>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { convertToError, flare } from '@flareapp/js';
|
|
2
|
+
import ErrorStackParser from 'error-stack-parser';
|
|
3
|
+
import { contextToAttributes } from './contextToAttributes.js';
|
|
4
|
+
import { extractComponentInfo } from './extractComponentInfo.js';
|
|
5
|
+
import { getErrorOrigin } from './getErrorOrigin.js';
|
|
6
|
+
import { registerSvelteSdkIdentity } from './identify.js';
|
|
7
|
+
registerSvelteSdkIdentity();
|
|
8
|
+
export function createFlareErrorHandler(options) {
|
|
9
|
+
return async (rawError, _reset) => {
|
|
10
|
+
const error = convertToError(rawError);
|
|
11
|
+
options?.beforeEvaluate?.({ error });
|
|
12
|
+
let frames = [];
|
|
13
|
+
try {
|
|
14
|
+
frames = ErrorStackParser.parse(error);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
// unparseable stack
|
|
18
|
+
}
|
|
19
|
+
const { componentName, componentHierarchy } = extractComponentInfo(frames, options?.ancestor);
|
|
20
|
+
const errorOrigin = getErrorOrigin(frames);
|
|
21
|
+
let context = {
|
|
22
|
+
svelte: {
|
|
23
|
+
componentName,
|
|
24
|
+
componentHierarchy,
|
|
25
|
+
errorOrigin,
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
if (options?.beforeSubmit) {
|
|
29
|
+
context = options.beforeSubmit({ error, context });
|
|
30
|
+
}
|
|
31
|
+
flare.reportSilently(error, contextToAttributes(context));
|
|
32
|
+
options?.afterSubmit?.({ error, context });
|
|
33
|
+
};
|
|
34
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type ErrorStackParser from 'error-stack-parser';
|
|
2
|
+
import { type ComponentTreeNode } from './componentTree.js';
|
|
3
|
+
interface ComponentInfo {
|
|
4
|
+
componentName: string | null;
|
|
5
|
+
componentHierarchy: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function extractComponentInfo(frames: ErrorStackParser.StackFrame[], ancestor?: ComponentTreeNode | null): ComponentInfo;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { lookupComponentTree } from './componentTree.js';
|
|
2
|
+
export function extractComponentInfo(frames, ancestor) {
|
|
3
|
+
const svelteFrames = frames.filter((frame) => frame.fileName && frame.fileName.includes('.svelte'));
|
|
4
|
+
if (svelteFrames.length === 0) {
|
|
5
|
+
return { componentName: null, componentHierarchy: [] };
|
|
6
|
+
}
|
|
7
|
+
// Try the preprocessor-based component tree first.
|
|
8
|
+
// Walk each .svelte frame until we find one that was registered.
|
|
9
|
+
for (const frame of svelteFrames) {
|
|
10
|
+
if (!frame.fileName)
|
|
11
|
+
continue;
|
|
12
|
+
const treeHierarchy = lookupComponentTree(frame.fileName, ancestor);
|
|
13
|
+
if (treeHierarchy.length > 0) {
|
|
14
|
+
return {
|
|
15
|
+
componentName: treeHierarchy[0],
|
|
16
|
+
componentHierarchy: treeHierarchy,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
// Fallback: extract names from stack frames only (no preprocessor).
|
|
21
|
+
const names = [];
|
|
22
|
+
for (const frame of svelteFrames) {
|
|
23
|
+
const name = extractName(frame);
|
|
24
|
+
if (name && name !== names[names.length - 1]) {
|
|
25
|
+
names.push(name);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return {
|
|
29
|
+
componentName: names[0] ?? null,
|
|
30
|
+
componentHierarchy: names,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function extractName(frame) {
|
|
34
|
+
if (frame.functionName && frame.functionName !== '<anonymous>' && !frame.functionName.includes('.')) {
|
|
35
|
+
return frame.functionName;
|
|
36
|
+
}
|
|
37
|
+
if (frame.fileName) {
|
|
38
|
+
const match = frame.fileName.match(/([^/]+)\.svelte/);
|
|
39
|
+
return match?.[1] ?? null;
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
// Heuristic classification of where a Svelte error originated, based on stack frame inspection.
|
|
2
|
+
//
|
|
3
|
+
// Svelte's onMount/beforeUpdate/$effect callbacks, DOM event handlers, and render functions
|
|
4
|
+
// all produce errors that look identical at the catch site (the error boundary sees a plain Error
|
|
5
|
+
// either way). The only distinguishing signal is the stack trace: different origins leave
|
|
6
|
+
// recognizable function names, file names, or DOM API calls in the frames.
|
|
7
|
+
//
|
|
8
|
+
// We check patterns in priority order: event > effect > render > unknown.
|
|
9
|
+
// Priority matters because an event handler inside a Svelte component would match both
|
|
10
|
+
// EVENT_PATTERNS and the .svelte filename check; we want 'event' to win.
|
|
11
|
+
// Inline event handlers (onclick, onsubmit, ...) and DOM event API calls.
|
|
12
|
+
// Browsers compile `onclick={handler}` to `.onclick = ...` or `addEventListener(...)`,
|
|
13
|
+
// so these patterns catch both Svelte's compiled output and manual DOM calls.
|
|
14
|
+
const EVENT_PATTERNS = [
|
|
15
|
+
/\.onclick\b/i,
|
|
16
|
+
/\.onsubmit\b/i,
|
|
17
|
+
/\.onchange\b/i,
|
|
18
|
+
/\.oninput\b/i,
|
|
19
|
+
/\.onkeydown\b/i,
|
|
20
|
+
/\.onkeyup\b/i,
|
|
21
|
+
/\.onfocus\b/i,
|
|
22
|
+
/\.onblur\b/i,
|
|
23
|
+
/\.onmouse/i,
|
|
24
|
+
/\.onpointer/i,
|
|
25
|
+
/\.ontouch/i,
|
|
26
|
+
/addEventListener/,
|
|
27
|
+
/dispatchEvent/,
|
|
28
|
+
/EventTarget\./,
|
|
29
|
+
/HTMLElement\./,
|
|
30
|
+
/HTMLButtonElement\./,
|
|
31
|
+
/HTMLInputElement\./,
|
|
32
|
+
/HTMLFormElement\./,
|
|
33
|
+
];
|
|
34
|
+
// Async side-effects: $effect callbacks, onMount microtasks, promise continuations.
|
|
35
|
+
// Svelte 5 schedules effects via queueMicrotask; promise chains and MutationObserver
|
|
36
|
+
// callbacks are also async side-effects, not render-phase code.
|
|
37
|
+
const EFFECT_PATTERNS = [/queueMicrotask/, /Promise\.then/, /Promise\.catch/, /MutationObserver/];
|
|
38
|
+
// Inspects a parsed stack trace and classifies the error origin into one of:
|
|
39
|
+
// 'event' - error happened in a DOM event handler
|
|
40
|
+
// 'effect' - error happened in an async side-effect ($effect, onMount, promise)
|
|
41
|
+
// 'render' - error happened during Svelte component render (template/script top-level)
|
|
42
|
+
// 'unknown' - no frames or no recognizable pattern
|
|
43
|
+
export function getErrorOrigin(frames) {
|
|
44
|
+
if (frames.length === 0) {
|
|
45
|
+
return 'unknown';
|
|
46
|
+
}
|
|
47
|
+
// Flatten each frame into a single searchable string so regex matching is straightforward.
|
|
48
|
+
const frameStrings = frames.map((f) => `${f.functionName ?? ''} ${f.fileName ?? ''} ${f.source ?? ''}`);
|
|
49
|
+
// Check event patterns first (highest priority).
|
|
50
|
+
if (frameStrings.some((s) => EVENT_PATTERNS.some((p) => p.test(s)))) {
|
|
51
|
+
return 'event';
|
|
52
|
+
}
|
|
53
|
+
// Then async effect patterns.
|
|
54
|
+
if (frameStrings.some((s) => EFFECT_PATTERNS.some((p) => p.test(s)))) {
|
|
55
|
+
return 'effect';
|
|
56
|
+
}
|
|
57
|
+
// If any frame originates from a .svelte file but didn't match event/effect,
|
|
58
|
+
// the error most likely occurred during the synchronous render phase.
|
|
59
|
+
const hasSvelteFrame = frames.some((f) => f.fileName?.includes('.svelte'));
|
|
60
|
+
if (hasSvelteFrame) {
|
|
61
|
+
return 'render';
|
|
62
|
+
}
|
|
63
|
+
return 'unknown';
|
|
64
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerSvelteSdkIdentity(): void;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { flare } from '@flareapp/js';
|
|
2
|
+
import { PACKAGE_VERSION } from './version.js';
|
|
3
|
+
let registered = false;
|
|
4
|
+
export function registerSvelteSdkIdentity() {
|
|
5
|
+
if (registered)
|
|
6
|
+
return;
|
|
7
|
+
registered = true;
|
|
8
|
+
flare.setSdkInfo({ name: '@flareapp/svelte', version: PACKAGE_VERSION });
|
|
9
|
+
flare.setFramework({ name: 'Svelte' });
|
|
10
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as FlareErrorBoundary } from './FlareErrorBoundary.svelte';
|
|
2
|
+
export { createFlareErrorHandler, type FlareErrorHandlerOptions } from './createFlareErrorHandler.js';
|
|
3
|
+
export { __flareRegisterComponent, getComponentTreeContext } from './componentTree.js';
|
|
4
|
+
export { withFlareConfig, type WithFlareConfigOptions } from './config.js';
|
|
5
|
+
export { flarePreprocessor, type FlarePreprocessorOptions } from './preprocessor.js';
|
|
6
|
+
export type { FlareSvelteContext, SvelteErrorOrigin } from './types.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { registerSvelteSdkIdentity } from './identify.js';
|
|
2
|
+
registerSvelteSdkIdentity();
|
|
3
|
+
export { default as FlareErrorBoundary } from './FlareErrorBoundary.svelte';
|
|
4
|
+
export { createFlareErrorHandler } from './createFlareErrorHandler.js';
|
|
5
|
+
export { __flareRegisterComponent, getComponentTreeContext } from './componentTree.js';
|
|
6
|
+
export { withFlareConfig } from './config.js';
|
|
7
|
+
export { flarePreprocessor } from './preprocessor.js';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export function flarePreprocessor(options) {
|
|
2
|
+
const exclude = options?.exclude;
|
|
3
|
+
return {
|
|
4
|
+
name: 'flare-component-tree',
|
|
5
|
+
markup({ content, filename }) {
|
|
6
|
+
if (!filename?.includes('.svelte')) {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (exclude?.test(filename)) {
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
const hasScript = /<script[\s>]/i.test(content);
|
|
13
|
+
if (hasScript) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const componentName = extractComponentName(filename);
|
|
17
|
+
const escapedFile = escapeString(filename);
|
|
18
|
+
const injection = `<script>\n` +
|
|
19
|
+
`import { __flareRegisterComponent as __flare_reg__ } from '@flareapp/svelte';\n` +
|
|
20
|
+
`const __flare_node__ = __flare_reg__('${componentName}', '${escapedFile}');\n` +
|
|
21
|
+
`</script>\n`;
|
|
22
|
+
return {
|
|
23
|
+
code: injection + content,
|
|
24
|
+
};
|
|
25
|
+
},
|
|
26
|
+
script({ content, filename, attributes }) {
|
|
27
|
+
if (!filename?.includes('.svelte')) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
if (exclude?.test(filename)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (attributes.context === 'module' || attributes.module != null) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const componentName = extractComponentName(filename);
|
|
37
|
+
const escapedFile = escapeString(filename);
|
|
38
|
+
const injection = `import { __flareRegisterComponent as __flare_reg__ } from '@flareapp/svelte';\n` +
|
|
39
|
+
`const __flare_node__ = __flare_reg__('${componentName}', '${escapedFile}');\n`;
|
|
40
|
+
return {
|
|
41
|
+
code: injection + content,
|
|
42
|
+
};
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function extractComponentName(filename) {
|
|
47
|
+
const normalized = filename.replace(/\\/g, '/');
|
|
48
|
+
const base = normalized.split('/').pop() ?? filename;
|
|
49
|
+
return base.replace(/\.svelte$/, '');
|
|
50
|
+
}
|
|
51
|
+
function escapeString(str) {
|
|
52
|
+
return str.replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
|
53
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const PACKAGE_VERSION = "2.0.1";
|
package/README.md
CHANGED
|
@@ -1,105 +1,33 @@
|
|
|
1
1
|
# @flareapp/svelte
|
|
2
2
|
|
|
3
|
-
Svelte 5 integration for [Flare](https://flareapp.io) error tracking.
|
|
4
|
-
|
|
5
|
-
Flare client configuration, global browser errors, manual reports, custom context, and glows.
|
|
3
|
+
Svelte 5 integration for [Flare](https://flareapp.io) error tracking. Provides an error boundary component that catches
|
|
4
|
+
component errors and reports them to Flare with Svelte-specific context (component name, hierarchy, error origin).
|
|
6
5
|
|
|
7
6
|
## Installation
|
|
8
7
|
|
|
9
|
-
Install both the core Flare client and the Svelte integration:
|
|
10
|
-
|
|
11
8
|
```bash
|
|
12
|
-
npm install @flareapp/
|
|
13
|
-
# or
|
|
14
|
-
yarn add @flareapp/js @flareapp/svelte
|
|
15
|
-
# or
|
|
16
|
-
pnpm add @flareapp/js @flareapp/svelte
|
|
9
|
+
npm install @flareapp/svelte @flareapp/js
|
|
17
10
|
```
|
|
18
11
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
If your app is bundled for production, also configure sourcemap uploads with `@flareapp/vite` so Flare can show readable
|
|
22
|
-
stack traces and code snippets.
|
|
23
|
-
|
|
24
|
-
## Setting up the Flare client
|
|
25
|
-
|
|
26
|
-
Initialize the Flare client as early as possible in your application, typically in `main.ts`:
|
|
12
|
+
## Quick start
|
|
27
13
|
|
|
28
|
-
|
|
29
|
-
import { flare } from '@flareapp/js';
|
|
30
|
-
import { mount } from 'svelte';
|
|
31
|
-
|
|
32
|
-
import App from './App.svelte';
|
|
33
|
-
|
|
34
|
-
if (import.meta.env.PROD) {
|
|
35
|
-
flare.light('YOUR PROJECT PUBLIC KEY');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
mount(App, {
|
|
39
|
-
target: document.getElementById('app')!,
|
|
40
|
-
});
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Use your project's public key from the JavaScript installation section in your Flare project settings.
|
|
44
|
-
|
|
45
|
-
If you use the sourcemap plugin, you do not need to pass a public key to `flare.light()`. The plugin injects the project
|
|
46
|
-
key during your build.
|
|
47
|
-
|
|
48
|
-
## Error boundary
|
|
49
|
-
|
|
50
|
-
`FlareErrorBoundary` wraps Svelte's native `<svelte:boundary>`. It catches errors from the component tree below it,
|
|
51
|
-
reports them to Flare, and can render your fallback snippet.
|
|
14
|
+
Initialize the Flare client and wrap your component tree with the error boundary:
|
|
52
15
|
|
|
53
16
|
```svelte
|
|
54
17
|
<script lang="ts">
|
|
18
|
+
import { flare } from '@flareapp/js';
|
|
55
19
|
import { FlareErrorBoundary } from '@flareapp/svelte';
|
|
56
20
|
|
|
57
21
|
import Root from './Root.svelte';
|
|
22
|
+
|
|
23
|
+
if (import.meta.env.PROD) {
|
|
24
|
+
flare.light('YOUR_FLARE_API_KEY');
|
|
25
|
+
}
|
|
58
26
|
</script>
|
|
59
27
|
|
|
60
28
|
<FlareErrorBoundary>
|
|
61
29
|
<Root />
|
|
62
30
|
|
|
63
|
-
{#snippet failed(error, reset)}
|
|
64
|
-
<section>
|
|
65
|
-
<h2>Something went wrong</h2>
|
|
66
|
-
<p>{error.message}</p>
|
|
67
|
-
<button onclick={reset}>Try again</button>
|
|
68
|
-
</section>
|
|
69
|
-
{/snippet}
|
|
70
|
-
</FlareErrorBoundary>
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
Without a `failed` snippet, the boundary still catches and reports the error, but it renders nothing for the failed
|
|
74
|
-
subtree.
|
|
75
|
-
|
|
76
|
-
### Resetting the boundary
|
|
77
|
-
|
|
78
|
-
Call the `reset` function passed to the `failed` snippet to clear the boundary state and retry rendering the children:
|
|
79
|
-
|
|
80
|
-
```svelte
|
|
81
|
-
<FlareErrorBoundary>
|
|
82
|
-
<CheckoutForm />
|
|
83
|
-
|
|
84
|
-
{#snippet failed(error, reset)}
|
|
85
|
-
<p>{error.message}</p>
|
|
86
|
-
<button onclick={reset}>Retry checkout</button>
|
|
87
|
-
{/snippet}
|
|
88
|
-
</FlareErrorBoundary>
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
You can also reset automatically when values in `resetKeys` change. Values are compared by index with `Object.is`, and a
|
|
92
|
-
length change also triggers a reset.
|
|
93
|
-
|
|
94
|
-
```svelte
|
|
95
|
-
<FlareErrorBoundary
|
|
96
|
-
resetKeys={[currentRoute, selectedAccountId]}
|
|
97
|
-
onReset={(error) => {
|
|
98
|
-
console.log('Recovered from:', error?.message);
|
|
99
|
-
}}
|
|
100
|
-
>
|
|
101
|
-
<AccountPage />
|
|
102
|
-
|
|
103
31
|
{#snippet failed(error, reset)}
|
|
104
32
|
<p>{error.message}</p>
|
|
105
33
|
<button onclick={reset}>Try again</button>
|
|
@@ -107,258 +35,14 @@ length change also triggers a reset.
|
|
|
107
35
|
</FlareErrorBoundary>
|
|
108
36
|
```
|
|
109
37
|
|
|
110
|
-
|
|
111
|
-
an error state. It receives the previous error, or `null` if no error was stored.
|
|
112
|
-
|
|
113
|
-
## Lifecycle callbacks
|
|
114
|
-
|
|
115
|
-
The boundary exposes three callbacks around the Svelte-specific reporting flow.
|
|
116
|
-
|
|
117
|
-
```svelte
|
|
118
|
-
<script lang="ts">
|
|
119
|
-
import { flare } from '@flareapp/js';
|
|
120
|
-
import { FlareErrorBoundary, type FlareSvelteContext } from '@flareapp/svelte';
|
|
121
|
-
</script>
|
|
122
|
-
|
|
123
|
-
<FlareErrorBoundary
|
|
124
|
-
beforeEvaluate={({ error }) => {
|
|
125
|
-
flare.addContext('feature', 'checkout');
|
|
126
|
-
flare.addContext('errorMessage', error.message);
|
|
127
|
-
}}
|
|
128
|
-
beforeSubmit={({ context }: { context: FlareSvelteContext }) => {
|
|
129
|
-
return {
|
|
130
|
-
...context,
|
|
131
|
-
svelte: {
|
|
132
|
-
...context.svelte,
|
|
133
|
-
componentHierarchy: context.svelte.componentHierarchy.filter(
|
|
134
|
-
(component) => component !== 'ThirdPartyWrapper',
|
|
135
|
-
),
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
}}
|
|
139
|
-
afterSubmit={({ error, context }) => {
|
|
140
|
-
console.error('Reported Svelte error:', error);
|
|
141
|
-
console.debug('Svelte context:', context);
|
|
142
|
-
}}
|
|
143
|
-
>
|
|
144
|
-
<Root />
|
|
145
|
-
</FlareErrorBoundary>
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
Callback order:
|
|
149
|
-
|
|
150
|
-
1. `beforeEvaluate` runs after the thrown value is converted to an `Error`, before Svelte context is built.
|
|
151
|
-
2. `beforeSubmit` runs after Svelte context is built. Return the context object that should be attached to the report.
|
|
152
|
-
3. `afterSubmit` runs after Flare reporting is started. The network request is asynchronous.
|
|
153
|
-
|
|
154
|
-
These callbacks are not wrapped in `try`/`catch`. If one throws, the error can bubble out of the boundary handler.
|
|
155
|
-
|
|
156
|
-
### Filtering errors
|
|
157
|
-
|
|
158
|
-
The boundary callbacks are for adding context and running side effects. They do not suppress reports.
|
|
159
|
-
|
|
160
|
-
To filter, suppress, or modify the final Flare report, use the core JavaScript client hooks:
|
|
161
|
-
|
|
162
|
-
```ts
|
|
163
|
-
import { flare } from '@flareapp/js';
|
|
164
|
-
|
|
165
|
-
flare.configure({
|
|
166
|
-
beforeEvaluate: (error) => {
|
|
167
|
-
if (error.message.includes('Ignored validation error')) {
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return error;
|
|
172
|
-
},
|
|
173
|
-
});
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
The execution order when both boundary callbacks and core client hooks are configured is:
|
|
177
|
-
|
|
178
|
-
1. Boundary `beforeEvaluate`
|
|
179
|
-
2. Boundary `beforeSubmit`
|
|
180
|
-
3. Internal `flare.reportSilently()` call
|
|
181
|
-
4. Client `beforeEvaluate` from `flare.configure()`
|
|
182
|
-
5. Client `beforeSubmit` from `flare.configure()`
|
|
183
|
-
6. Report is sent to Flare
|
|
184
|
-
7. Boundary `afterSubmit`
|
|
185
|
-
|
|
186
|
-
## Svelte context
|
|
187
|
-
|
|
188
|
-
When the Svelte integration reports an error, it attaches Svelte-specific context under `context.custom.svelte`.
|
|
189
|
-
|
|
190
|
-
```ts
|
|
191
|
-
interface FlareSvelteContext {
|
|
192
|
-
svelte: {
|
|
193
|
-
componentName: string | null;
|
|
194
|
-
componentHierarchy: string[];
|
|
195
|
-
errorOrigin: 'render' | 'event' | 'effect' | 'unknown';
|
|
196
|
-
};
|
|
197
|
-
}
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
Field details:
|
|
201
|
-
|
|
202
|
-
| Field | Description |
|
|
203
|
-
| -------------------- | ---------------------------------------------------------------- |
|
|
204
|
-
| `componentName` | Best-effort name of the component closest to the thrown error. |
|
|
205
|
-
| `componentHierarchy` | Component names ordered from inner component to outer component. |
|
|
206
|
-
| `errorOrigin` | Best-effort classification of where the error came from. |
|
|
207
|
-
|
|
208
|
-
When the same component is mounted in multiple places (e.g. a `Button` inside both `Sidebar` and `Header`),
|
|
209
|
-
`FlareErrorBoundary` disambiguates by matching the error to the instance whose ancestor chain includes the catching
|
|
210
|
-
boundary. Without an ancestor hint (e.g. manual `lookupComponentTree` calls), the first registered instance is returned.
|
|
211
|
-
|
|
212
|
-
Component context is extracted from `.svelte` stack frames. In production bundles, function names and filenames may be
|
|
213
|
-
minified. Configure sourcemaps so Flare can resolve the original source code on the backend.
|
|
214
|
-
|
|
215
|
-
## Event handlers and async errors
|
|
216
|
-
|
|
217
|
-
Svelte boundaries do not catch every kind of browser error. Errors thrown in event handlers and unhandled promise
|
|
218
|
-
rejections are handled by the global listeners installed by `@flareapp/js`, not by `FlareErrorBoundary`.
|
|
219
|
-
|
|
220
|
-
```svelte
|
|
221
|
-
<button
|
|
222
|
-
onclick={() => {
|
|
223
|
-
throw new Error('Clicked button failed');
|
|
224
|
-
}}
|
|
225
|
-
>
|
|
226
|
-
Trigger event error
|
|
227
|
-
</button>
|
|
228
|
-
```
|
|
229
|
-
|
|
230
|
-
The error above is still reported if the core Flare client is initialized, but it will not render the boundary fallback
|
|
231
|
-
UI.
|
|
232
|
-
|
|
233
|
-
## Custom boundary usage
|
|
234
|
-
|
|
235
|
-
Use `createFlareErrorHandler()` when you want to wire Flare into your own `<svelte:boundary>` instead of using
|
|
236
|
-
`FlareErrorBoundary`.
|
|
237
|
-
|
|
238
|
-
```svelte
|
|
239
|
-
<script lang="ts">
|
|
240
|
-
import { createFlareErrorHandler } from '@flareapp/svelte';
|
|
241
|
-
|
|
242
|
-
const reportToFlare = createFlareErrorHandler({
|
|
243
|
-
afterSubmit: ({ error }) => {
|
|
244
|
-
console.error('Reported through custom boundary:', error);
|
|
245
|
-
},
|
|
246
|
-
});
|
|
247
|
-
</script>
|
|
248
|
-
|
|
249
|
-
<svelte:boundary onerror={reportToFlare}>
|
|
250
|
-
<Root />
|
|
251
|
-
|
|
252
|
-
{#snippet failed(error, reset)}
|
|
253
|
-
<p>{error.message}</p>
|
|
254
|
-
<button onclick={reset}>Retry</button>
|
|
255
|
-
{/snippet}
|
|
256
|
-
</svelte:boundary>
|
|
257
|
-
```
|
|
258
|
-
|
|
259
|
-
The returned function matches the Svelte boundary `onerror` signature:
|
|
260
|
-
|
|
261
|
-
```ts
|
|
262
|
-
(error: unknown, reset: () => void) => void | Promise<void>;
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
It converts non-`Error` values, builds Svelte context from the stack trace, reports through the core Flare client, and
|
|
266
|
-
runs the lifecycle callbacks described above.
|
|
267
|
-
|
|
268
|
-
## Manual reports, context, and glows
|
|
269
|
-
|
|
270
|
-
The Svelte integration builds on the core JavaScript client. Use `@flareapp/js` directly for manual reporting, client
|
|
271
|
-
hooks, custom context, and glows:
|
|
272
|
-
|
|
273
|
-
```ts
|
|
274
|
-
import { flare } from '@flareapp/js';
|
|
275
|
-
|
|
276
|
-
flare.addContext('user', { id: '123' });
|
|
277
|
-
flare.glow('checkout', 'Payment method selected');
|
|
278
|
-
|
|
279
|
-
try {
|
|
280
|
-
await submitOrder();
|
|
281
|
-
} catch (error) {
|
|
282
|
-
flare.report(error);
|
|
283
|
-
}
|
|
284
|
-
```
|
|
285
|
-
|
|
286
|
-
Useful shared documentation:
|
|
287
|
-
|
|
288
|
-
- [Reporting errors](https://flareapp.io/docs/javascript/errors/reporting-errors)
|
|
289
|
-
- [Client hooks](https://flareapp.io/docs/javascript/errors/client-hooks)
|
|
290
|
-
- [Adding custom context](https://flareapp.io/docs/javascript/data-collection/adding-custom-context)
|
|
291
|
-
- [Adding glows](https://flareapp.io/docs/javascript/data-collection/adding-glows)
|
|
292
|
-
|
|
293
|
-
## Resolving bundled code
|
|
294
|
-
|
|
295
|
-
Production Svelte apps are usually minified and bundled, which makes raw stack traces hard to read. Configure sourcemap
|
|
296
|
-
uploads with `@flareapp/vite` so Flare can map stack frames back to your original `.svelte` files.
|
|
297
|
-
|
|
298
|
-
The Svelte integration uses the same sourcemap plugin as the JavaScript and React clients. See the
|
|
299
|
-
[JavaScript resolving bundled code documentation](https://flareapp.io/docs/javascript/general/resolving-bundled-code)
|
|
300
|
-
for the Vite, Webpack, Laravel Mix, and manual upload setup.
|
|
301
|
-
|
|
302
|
-
## Verifying your setup
|
|
303
|
-
|
|
304
|
-
The core client is available as `window.flare` in the browser. Build your app for production and run this in the browser
|
|
305
|
-
console:
|
|
306
|
-
|
|
307
|
-
```js
|
|
308
|
-
flare.test();
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
This sends a test error to your Flare project.
|
|
312
|
-
|
|
313
|
-
If nothing appears in Flare, enable debug mode:
|
|
314
|
-
|
|
315
|
-
```ts
|
|
316
|
-
flare.light('YOUR PROJECT PUBLIC KEY', true);
|
|
317
|
-
// or
|
|
318
|
-
flare.configure({ debug: true });
|
|
319
|
-
```
|
|
320
|
-
|
|
321
|
-
If `flare.light()` has not been called, for example because your production guard is false, reports are silently ignored.
|
|
322
|
-
|
|
323
|
-
## API reference
|
|
324
|
-
|
|
325
|
-
```ts
|
|
326
|
-
import { FlareErrorBoundary, createFlareErrorHandler } from '@flareapp/svelte';
|
|
327
|
-
|
|
328
|
-
import type { FlareErrorHandlerOptions, FlareSvelteContext, SvelteErrorOrigin } from '@flareapp/svelte';
|
|
329
|
-
```
|
|
330
|
-
|
|
331
|
-
Exports:
|
|
332
|
-
|
|
333
|
-
| Export | Description |
|
|
334
|
-
| ------------------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
335
|
-
| `FlareErrorBoundary` | Svelte component that catches boundary errors, reports them to Flare, and renders an optional fallback snippet. |
|
|
336
|
-
| `createFlareErrorHandler` | Factory that returns a Svelte boundary `onerror` callback for custom boundary usage. |
|
|
337
|
-
|
|
338
|
-
Types:
|
|
339
|
-
|
|
340
|
-
| Type | Description |
|
|
341
|
-
| -------------------------- | ------------------------------------------------------------------------------------------ |
|
|
342
|
-
| `FlareErrorHandlerOptions` | Lifecycle callback options accepted by `createFlareErrorHandler` and `FlareErrorBoundary`. |
|
|
343
|
-
| `FlareSvelteContext` | Shape of the Svelte context passed to `beforeSubmit` and `afterSubmit`. |
|
|
344
|
-
| `SvelteErrorOrigin` | Union of possible origin values: `'render'`, `'event'`, `'effect'`, and `'unknown'`. |
|
|
345
|
-
|
|
346
|
-
### `FlareErrorBoundary` props
|
|
38
|
+
## Documentation
|
|
347
39
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
| `children` | `Snippet` | Child snippet rendered inside the boundary. |
|
|
351
|
-
| `failed` | `Snippet<[error: Error, reset: () => void]>` | Optional fallback snippet rendered after an error is caught. |
|
|
352
|
-
| `resetKeys` | `unknown[]` | Values that reset the boundary when changed while an error is stored. |
|
|
353
|
-
| `beforeEvaluate` | `({ error }) => void` | Runs before Svelte context is built. |
|
|
354
|
-
| `beforeSubmit` | `({ error, context }) => FlareSvelteContext` | Runs before reporting. Return the context to attach. |
|
|
355
|
-
| `afterSubmit` | `({ error, context }) => void` | Runs after reporting is started. |
|
|
356
|
-
| `onReset` | `(error: Error | null) => void` | Runs when the boundary is reset. |
|
|
40
|
+
Full documentation on the error boundary, lifecycle callbacks, reset keys, custom boundary usage, and more is available
|
|
41
|
+
at [flareapp.io/docs/svelte/general/installation](https://flareapp.io/docs/svelte/general/installation).
|
|
357
42
|
|
|
358
|
-
##
|
|
43
|
+
## Compatibility
|
|
359
44
|
|
|
360
|
-
|
|
361
|
-
SvelteKit capture helpers, route context, and re-exports the Svelte boundary component for convenience.
|
|
45
|
+
- Svelte 5.3+
|
|
362
46
|
|
|
363
47
|
## License
|
|
364
48
|
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { PreprocessorGroup } from 'svelte/compiler';
|
|
2
|
+
import { type FlarePreprocessorOptions } from './preprocessor.js';
|
|
3
|
+
interface SvelteConfig {
|
|
4
|
+
preprocess?: PreprocessorGroup | PreprocessorGroup[];
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface WithFlareConfigOptions {
|
|
8
|
+
componentTracking?: boolean;
|
|
9
|
+
exclude?: FlarePreprocessorOptions['exclude'];
|
|
10
|
+
}
|
|
11
|
+
export declare function withFlareConfig(config: SvelteConfig, options?: WithFlareConfigOptions): SvelteConfig;
|
|
12
|
+
export { flarePreprocessor, type FlarePreprocessorOptions } from './preprocessor.js';
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { flarePreprocessor } from './preprocessor.js';
|
|
2
|
+
export function withFlareConfig(config, options) {
|
|
3
|
+
const { componentTracking = true, exclude } = options ?? {};
|
|
4
|
+
if (!componentTracking) {
|
|
5
|
+
return config;
|
|
6
|
+
}
|
|
7
|
+
const existing = normalizePreprocessors(config.preprocess);
|
|
8
|
+
if (existing.some((p) => !!p.__flareId)) {
|
|
9
|
+
return config;
|
|
10
|
+
}
|
|
11
|
+
const preprocessor = flarePreprocessor({ exclude });
|
|
12
|
+
preprocessor.__flareId = true;
|
|
13
|
+
return {
|
|
14
|
+
...config,
|
|
15
|
+
preprocess: [preprocessor, ...existing],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
function normalizePreprocessors(preprocess) {
|
|
19
|
+
if (!preprocess)
|
|
20
|
+
return [];
|
|
21
|
+
if (Array.isArray(preprocess))
|
|
22
|
+
return preprocess;
|
|
23
|
+
return [preprocess];
|
|
24
|
+
}
|
|
25
|
+
export { flarePreprocessor } from './preprocessor.js';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { default as FlareErrorBoundary } from './FlareErrorBoundary.svelte';
|
|
2
2
|
export { createFlareErrorHandler, type FlareErrorHandlerOptions } from './createFlareErrorHandler.js';
|
|
3
3
|
export { __flareRegisterComponent, getComponentTreeContext } from './componentTree.js';
|
|
4
|
+
export { withFlareConfig, type WithFlareConfigOptions } from './config.js';
|
|
4
5
|
export { flarePreprocessor, type FlarePreprocessorOptions } from './preprocessor.js';
|
|
5
6
|
export type { FlareSvelteContext, SvelteErrorOrigin } from './types.js';
|
package/dist/index.js
CHANGED
|
@@ -3,4 +3,5 @@ registerSvelteSdkIdentity();
|
|
|
3
3
|
export { default as FlareErrorBoundary } from './FlareErrorBoundary.svelte';
|
|
4
4
|
export { createFlareErrorHandler } from './createFlareErrorHandler.js';
|
|
5
5
|
export { __flareRegisterComponent, getComponentTreeContext } from './componentTree.js';
|
|
6
|
+
export { withFlareConfig } from './config.js';
|
|
6
7
|
export { flarePreprocessor } from './preprocessor.js';
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const PACKAGE_VERSION = "2.0.
|
|
1
|
+
export declare const PACKAGE_VERSION = "2.0.1";
|
package/dist/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// generated during release, do not modify
|
|
2
|
-
export const PACKAGE_VERSION = '2.0.
|
|
2
|
+
export const PACKAGE_VERSION = '2.0.1';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flareapp/svelte",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.1",
|
|
4
4
|
"description": "Svelte client for flareapp.io",
|
|
5
5
|
"homepage": "https://flareapp.io",
|
|
6
6
|
"bugs": "https://github.com/spatie/flare-client-js/issues",
|
|
@@ -34,10 +34,10 @@
|
|
|
34
34
|
"default": "./dist/index.js"
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
|
-
"./
|
|
37
|
+
"./config": {
|
|
38
38
|
"import": {
|
|
39
|
-
"types": "./dist/
|
|
40
|
-
"default": "./dist/
|
|
39
|
+
"types": "./dist/config.d.ts",
|
|
40
|
+
"default": "./dist/config.js"
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
},
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"vitest": "^4.0.0"
|
|
61
61
|
},
|
|
62
62
|
"peerDependencies": {
|
|
63
|
-
"@flareapp/js": "^2.0.
|
|
63
|
+
"@flareapp/js": "^2.0.1",
|
|
64
64
|
"svelte": "^5.3.0"
|
|
65
65
|
},
|
|
66
66
|
"publishConfig": {
|