@flareapp/svelte 2.4.0 → 2.5.0

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.
@@ -8,6 +8,7 @@
8
8
  children: Snippet;
9
9
  failed?: Snippet<[error: Error, reset: () => void]>;
10
10
  resetKeys?: unknown[];
11
+ flare?: FlareErrorHandlerOptions['flare'];
11
12
  beforeEvaluate?: FlareErrorHandlerOptions['beforeEvaluate'];
12
13
  beforeSubmit?: FlareErrorHandlerOptions['beforeSubmit'];
13
14
  afterSubmit?: FlareErrorHandlerOptions['afterSubmit'];
@@ -18,6 +19,7 @@
18
19
  children,
19
20
  failed: fallbackSnippet,
20
21
  resetKeys,
22
+ flare,
21
23
  beforeEvaluate,
22
24
  beforeSubmit,
23
25
  afterSubmit,
@@ -55,7 +57,7 @@
55
57
  const ancestor = __flareRegisterComponent('FlareErrorBoundary', '@flareapp/svelte/FlareErrorBoundary.svelte');
56
58
 
57
59
  const handler = $derived(
58
- createFlareErrorHandler({ ancestor, beforeEvaluate, beforeSubmit, afterSubmit }),
60
+ createFlareErrorHandler({ ancestor, flare, beforeEvaluate, beforeSubmit, afterSubmit }),
59
61
  );
60
62
 
61
63
  function onerror(rawError: unknown, reset: () => void) {
@@ -4,6 +4,7 @@ interface Props {
4
4
  children: Snippet;
5
5
  failed?: Snippet<[error: Error, reset: () => void]>;
6
6
  resetKeys?: unknown[];
7
+ flare?: FlareErrorHandlerOptions['flare'];
7
8
  beforeEvaluate?: FlareErrorHandlerOptions['beforeEvaluate'];
8
9
  beforeSubmit?: FlareErrorHandlerOptions['beforeSubmit'];
9
10
  afterSubmit?: FlareErrorHandlerOptions['afterSubmit'];
package/dist/config.d.ts CHANGED
@@ -7,6 +7,7 @@ interface SvelteConfig {
7
7
  export interface WithFlareConfigOptions {
8
8
  componentTracking?: boolean;
9
9
  exclude?: FlarePreprocessorOptions['exclude'];
10
+ importSource?: string;
10
11
  }
11
12
  export declare function withFlareConfig(config: SvelteConfig, options?: WithFlareConfigOptions): SvelteConfig;
12
13
  export { flarePreprocessor, type FlarePreprocessorOptions } from './preprocessor.js';
package/dist/config.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { flarePreprocessor } from './preprocessor.js';
2
2
  export function withFlareConfig(config, options) {
3
- const { componentTracking = true, exclude } = options ?? {};
3
+ const { componentTracking = true, exclude, importSource } = options ?? {};
4
4
  if (!componentTracking) {
5
5
  return config;
6
6
  }
@@ -8,7 +8,7 @@ export function withFlareConfig(config, options) {
8
8
  if (existing.some((p) => !!p.__flareId)) {
9
9
  return config;
10
10
  }
11
- const preprocessor = flarePreprocessor({ exclude });
11
+ const preprocessor = flarePreprocessor({ exclude, importSource });
12
12
  preprocessor.__flareId = true;
13
13
  return {
14
14
  ...config,
@@ -1,3 +1,3 @@
1
- import type { Attributes } from '@flareapp/js';
1
+ import type { Attributes } from '@flareapp/core';
2
2
  import type { FlareSvelteContext } from './types.js';
3
3
  export declare function contextToAttributes(context: FlareSvelteContext): Attributes;
@@ -1,6 +1,8 @@
1
+ import type { Flare } from '@flareapp/js/browser';
1
2
  import type { ComponentTreeNode } from './componentTree.js';
2
3
  import type { FlareSvelteContext } from './types.js';
3
4
  export interface FlareErrorHandlerOptions {
5
+ flare?: Flare;
4
6
  ancestor?: ComponentTreeNode | null;
5
7
  beforeEvaluate?: (params: {
6
8
  error: Error;
@@ -1,11 +1,13 @@
1
- import { convertToError, flare } from '@flareapp/js';
1
+ import { convertToError } from '@flareapp/core';
2
2
  import ErrorStackParser from 'error-stack-parser';
3
3
  import { contextToAttributes } from './contextToAttributes.js';
4
4
  import { extractComponentInfo } from './extractComponentInfo.js';
5
5
  import { getErrorOrigin } from './getErrorOrigin.js';
6
- import { registerSvelteSdkIdentity } from './identify.js';
7
- registerSvelteSdkIdentity();
6
+ import { tagSvelteFramework } from './identify.js';
7
+ import { resolveFlare } from './resolveFlare.js';
8
8
  export function createFlareErrorHandler(options) {
9
+ const flare = resolveFlare(options?.flare);
10
+ tagSvelteFramework(flare);
9
11
  return async (rawError, _reset) => {
10
12
  const error = convertToError(rawError);
11
13
  options?.beforeEvaluate?.({ error });
@@ -1 +1,3 @@
1
- export declare function registerSvelteSdkIdentity(): void;
1
+ import type { Flare } from '@flareapp/js/browser';
2
+ export declare function registerSvelteSdkIdentity(flare: Flare): void;
3
+ export declare function tagSvelteFramework(flare: Flare): void;
package/dist/identify.js CHANGED
@@ -1,10 +1,22 @@
1
- import { flare } from '@flareapp/js';
2
1
  import { PACKAGE_VERSION } from './version.js';
3
- let registered = false;
4
- export function registerSvelteSdkIdentity() {
5
- if (registered)
2
+ // Per-instance guards. A boolean cannot serve injection: with a singleton AND an
3
+ // injected RendererFlare, each instance must be tagged independently.
4
+ const sdkTagged = new WeakSet();
5
+ const frameworkTagged = new WeakSet();
6
+ // Web path: full identity on the default singleton (sdk + framework). Svelte's framework has no version.
7
+ export function registerSvelteSdkIdentity(flare) {
8
+ if (!sdkTagged.has(flare)) {
9
+ sdkTagged.add(flare);
10
+ flare.setSdkInfo({ name: '@flareapp/svelte', version: PACKAGE_VERSION });
11
+ }
12
+ tagSvelteFramework(flare);
13
+ }
14
+ // Injected path: framework tag ONLY. Never touch sdkInfo — that would clobber the
15
+ // injected instance's own SDK name (e.g. @flareapp/electron).
16
+ export function tagSvelteFramework(flare) {
17
+ if (frameworkTagged.has(flare)) {
6
18
  return;
7
- registered = true;
8
- flare.setSdkInfo({ name: '@flareapp/svelte', version: PACKAGE_VERSION });
19
+ }
20
+ frameworkTagged.add(flare);
9
21
  flare.setFramework({ name: 'Svelte' });
10
22
  }
package/dist/index.js CHANGED
@@ -1,5 +1,13 @@
1
+ import { flare } from '@flareapp/js';
1
2
  import { registerSvelteSdkIdentity } from './identify.js';
2
- registerSvelteSdkIdentity();
3
+ import { registerDefaultFlare } from './resolveFlare.js';
4
+ // Web entry. Importing @flareapp/js runs the root's own side effects (window.flare + global
5
+ // catch) — correct for the web. Register the singleton as the default Flare AND set its SDK
6
+ // identity AT IMPORT. The import-time identity registration is a hard contract: @flareapp/sveltekit
7
+ // does `export * from '@flareapp/svelte'` and overrides the SDK name per-report, relying on this
8
+ // running first (spec Decision 6). Do not defer it.
9
+ registerDefaultFlare(() => flare);
10
+ registerSvelteSdkIdentity(flare);
3
11
  export { default as FlareErrorBoundary } from './FlareErrorBoundary.svelte';
4
12
  export { createFlareErrorHandler } from './createFlareErrorHandler.js';
5
13
  export { __flareRegisterComponent, getComponentTreeContext } from './componentTree.js';
@@ -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';
package/dist/inject.js ADDED
@@ -0,0 +1,8 @@
1
+ // Electron-safe entry. NO @flareapp/js root import, NO default registration, NO import-time
2
+ // identity. The caller MUST pass `flare` (handler option / boundary prop); resolveFlare throws
3
+ // at wiring time if absent.
4
+ export { default as FlareErrorBoundary } from './FlareErrorBoundary.svelte';
5
+ export { createFlareErrorHandler } from './createFlareErrorHandler.js';
6
+ export { __flareRegisterComponent, getComponentTreeContext } from './componentTree.js';
7
+ export { withFlareConfig } from './config.js';
8
+ export { flarePreprocessor } from './preprocessor.js';
@@ -1,5 +1,6 @@
1
1
  import type { PreprocessorGroup } from 'svelte/compiler';
2
2
  export interface FlarePreprocessorOptions {
3
3
  exclude?: RegExp;
4
+ importSource?: string;
4
5
  }
5
6
  export declare function flarePreprocessor(options?: FlarePreprocessorOptions): PreprocessorGroup;
@@ -1,5 +1,6 @@
1
1
  export function flarePreprocessor(options) {
2
2
  const exclude = options?.exclude;
3
+ const importSource = options?.importSource ?? '@flareapp/svelte';
3
4
  return {
4
5
  name: 'flare-component-tree',
5
6
  markup({ content, filename }) {
@@ -16,7 +17,7 @@ export function flarePreprocessor(options) {
16
17
  const componentName = extractComponentName(filename);
17
18
  const escapedFile = escapeString(filename);
18
19
  const injection = `<script>\n` +
19
- `import { __flareRegisterComponent as __flare_reg__ } from '@flareapp/svelte';\n` +
20
+ `import { __flareRegisterComponent as __flare_reg__ } from '${importSource}';\n` +
20
21
  `const __flare_node__ = __flare_reg__('${componentName}', '${escapedFile}');\n` +
21
22
  `</script>\n`;
22
23
  return {
@@ -33,9 +34,17 @@ export function flarePreprocessor(options) {
33
34
  if (attributes.context === 'module' || attributes.module != null) {
34
35
  return;
35
36
  }
37
+ // Skip a script the markup hook already injected. For a scriptless component the markup
38
+ // hook adds a `<script>` with our registration; Svelte then runs THIS script hook over
39
+ // that injected block within the same preprocessor pass. Without this guard we inject a
40
+ // second time, producing a duplicate `const __flare_node__` -> "already been declared"
41
+ // compile error.
42
+ if (content.includes('__flare_node__')) {
43
+ return;
44
+ }
36
45
  const componentName = extractComponentName(filename);
37
46
  const escapedFile = escapeString(filename);
38
- const injection = `import { __flareRegisterComponent as __flare_reg__ } from '@flareapp/svelte';\n` +
47
+ const injection = `import { __flareRegisterComponent as __flare_reg__ } from '${importSource}';\n` +
39
48
  `const __flare_node__ = __flare_reg__('${componentName}', '${escapedFile}');\n`;
40
49
  return {
41
50
  code: injection + content,
@@ -0,0 +1,3 @@
1
+ import type { Flare } from '@flareapp/js/browser';
2
+ export declare function registerDefaultFlare(provider: () => Flare): void;
3
+ export declare function resolveFlare(explicit?: Flare): Flare;
@@ -0,0 +1,42 @@
1
+ let defaultProvider = null;
2
+ // `process.env.NODE_ENV` is replaced inline by bundlers (vite/webpack). The try/catch keeps a
3
+ // process-less environment safe: treat "undetermined" as production (warn, never crash).
4
+ function isDevMode() {
5
+ try {
6
+ return process.env.NODE_ENV !== 'production';
7
+ }
8
+ catch {
9
+ return false;
10
+ }
11
+ }
12
+ // Called once by the web entry (index.ts) as an import side effect.
13
+ export function registerDefaultFlare(provider) {
14
+ // Tripwire: a web default registering while the Electron bridge exists means a renderer pulled
15
+ // the package root — directly, or via component-tracking codegen emitting the root specifier
16
+ // (set the preprocessor's importSource to '@flareapp/svelte/inject' to avoid that). It drags
17
+ // the keyed @flareapp/js singleton and its global side effects into the renderer.
18
+ if (typeof window !== 'undefined' && window.__flare) {
19
+ const message = '[flare] @flareapp/svelte (web root) was imported in a renderer where the Electron ' +
20
+ 'bridge is present, pulling the keyed @flareapp/js singleton into the renderer. ' +
21
+ "Import @flareapp/svelte/inject (and set the preprocessor importSource to '@flareapp/svelte/inject') instead.";
22
+ // Dev: throw so the misconfiguration is impossible to miss. Production: warn instead, so a
23
+ // shipped app isn't crashed by a (recoverable) reporting-setup mistake.
24
+ if (isDevMode()) {
25
+ throw new Error(message);
26
+ }
27
+ console.warn(message);
28
+ }
29
+ defaultProvider = provider;
30
+ }
31
+ // Resolve at WIRING time (handler creation / component setup), never inside a report path.
32
+ export function resolveFlare(explicit) {
33
+ if (explicit) {
34
+ return explicit;
35
+ }
36
+ if (defaultProvider) {
37
+ return defaultProvider();
38
+ }
39
+ throw new Error('[flare] No Flare instance available. Pass `flare` (e.g. from ' +
40
+ '@flareapp/electron/renderer), or import @flareapp/svelte (the package root) ' +
41
+ 'to use the @flareapp/js default singleton.');
42
+ }
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "2.4.0";
1
+ export declare const PACKAGE_VERSION = "2.5.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // generated during release, do not modify
2
- export const PACKAGE_VERSION = '2.4.0';
2
+ export const PACKAGE_VERSION = '2.5.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flareapp/svelte",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
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",
@@ -42,17 +42,30 @@
42
42
  "types": "./dist/config.d.ts",
43
43
  "default": "./dist/config.js"
44
44
  }
45
+ },
46
+ "./inject": {
47
+ "svelte": "./dist/inject.js",
48
+ "import": {
49
+ "types": "./dist/inject.d.ts",
50
+ "default": "./dist/inject.js"
51
+ }
45
52
  }
46
53
  },
54
+ "sideEffects": [
55
+ "./dist/index.js"
56
+ ],
47
57
  "scripts": {
48
58
  "prepublishOnly": "npm run build",
49
59
  "generate:version": "node ../../scripts/generate-version.mjs .",
50
60
  "build": "npm run generate:version && svelte-package -i src -o dist",
51
61
  "test": "vitest run",
62
+ "verify:exports": "node scripts/verify-exports.mjs",
63
+ "verify:inject": "node scripts/verify-inject-no-root.mjs",
52
64
  "typescript": "tsc --noEmit",
53
65
  "release": "release-it"
54
66
  },
55
67
  "devDependencies": {
68
+ "@flareapp/electron": "file:../electron",
56
69
  "@flareapp/js": "file:../js",
57
70
  "@sveltejs/package": "^2.5.7",
58
71
  "@sveltejs/vite-plugin-svelte": "^5.0.0",
@@ -63,13 +76,14 @@
63
76
  "vitest": "^4.0.0"
64
77
  },
65
78
  "peerDependencies": {
66
- "@flareapp/js": "^2.4.0",
79
+ "@flareapp/js": "^2.5.0",
67
80
  "svelte": "^5.3.0"
68
81
  },
69
82
  "publishConfig": {
70
83
  "access": "public"
71
84
  },
72
85
  "dependencies": {
86
+ "@flareapp/core": "2.5.0",
73
87
  "error-stack-parser": "^2.1.4"
74
88
  }
75
89
  }