@interfere/vite 0.1.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.
package/README.md ADDED
@@ -0,0 +1,222 @@
1
+ <p align="center">
2
+ <a href="https://interfere.com">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://qyzkf4cgb8ydxtq1.public.blob.vercel-storage.com/v2/header/logo-dark.png">
5
+ <img src="https://qyzkf4cgb8ydxtq1.public.blob.vercel-storage.com/v2/header/logo-light.png" height="64">
6
+ </picture>
7
+ </a>
8
+ <h1 align="center">@interfere/vite</h1>
9
+ </p>
10
+
11
+ <p align="center">
12
+ <a href="https://www.npmjs.com/package/@interfere/vite"><img src="https://img.shields.io/npm/v/@interfere/vite.svg" /></a>
13
+ <a href="https://github.com/interfere-inc/interfere/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/@interfere/vite.svg" /></a>
14
+ <a href="https://www.npmjs.com/package/@interfere/vite"><img src="https://img.shields.io/npm/dm/@interfere/vite.svg" /></a>
15
+ </p>
16
+
17
+ <p align="center">
18
+ Vite build plugin and client initialization for <a href="https://interfere.com">Interfere</a> — error tracking, session replay, and analytics for modern product teams.
19
+ </p>
20
+
21
+ <p align="center">
22
+ <a href="https://support.interfere.com/docs">Documentation</a>
23
+ ·
24
+ <a href="https://support.interfere.com/roadmap">Feature Requests</a>
25
+ ·
26
+ <a href="https://support.interfere.com/requests">Report a Bug</a>
27
+ ·
28
+ <a href="mailto:support@interfere.com">Get Help</a>
29
+ </p>
30
+
31
+ ---
32
+
33
+ ## Getting Started
34
+
35
+ ### Prerequisites
36
+
37
+ - Vite `>=5`
38
+ - React `>=19`
39
+ - [`@interfere/react`](https://www.npmjs.com/package/@interfere/react) for the React provider and hooks
40
+
41
+ ### Installation
42
+
43
+ ```bash
44
+ npm install @interfere/vite @interfere/react
45
+ ```
46
+
47
+ ### Quick Start
48
+
49
+ **1. Add the Vite plugin**
50
+
51
+ ```ts
52
+ // vite.config.ts
53
+ import { defineConfig } from "vite";
54
+ import react from "@vitejs/plugin-react";
55
+ import { interfere } from "@interfere/vite/plugin";
56
+
57
+ export default defineConfig({
58
+ plugins: [react(), interfere()],
59
+ });
60
+ ```
61
+
62
+ The plugin automatically injects your build ID and release ID into the page at build time. By default the build ID is resolved from `git rev-parse HEAD`; you can override it via plugin options or the `VITE_INTERFERE_BUILD_ID` environment variable.
63
+
64
+ **2. Initialize the client**
65
+
66
+ ```ts
67
+ // src/main.tsx
68
+ import { init } from "@interfere/vite/init";
69
+
70
+ init();
71
+ ```
72
+
73
+ Call `init()` before mounting your React tree so the SDK is ready to capture errors from first render.
74
+
75
+ **3. Add the React provider**
76
+
77
+ ```tsx
78
+ // src/App.tsx
79
+ import { InterfereProvider } from "@interfere/react/provider";
80
+
81
+ function App() {
82
+ return (
83
+ <InterfereProvider>
84
+ <YourApp />
85
+ </InterfereProvider>
86
+ );
87
+ }
88
+ ```
89
+
90
+ ### Environment Variables
91
+
92
+ | Variable | Required | Description |
93
+ | --- | --- | --- |
94
+ | `VITE_INTERFERE_PUBLIC_KEY` | Yes | Your project public key (injected into the page by the plugin) |
95
+ | `VITE_INTERFERE_BUILD_ID` | No | Override the auto-detected build ID |
96
+
97
+ ## Plugin Options
98
+
99
+ ```ts
100
+ interfere({
101
+ buildId: "custom-build-id",
102
+ releaseId: "v1.2.3",
103
+ });
104
+ ```
105
+
106
+ | Option | Default | Description |
107
+ | --- | --- | --- |
108
+ | `buildId` | `VITE_INTERFERE_BUILD_ID` → Git SHA → random UUID | Unique identifier for this build |
109
+ | `releaseId` | Same as `buildId` | Semantic release identifier for grouping errors by deploy |
110
+
111
+ ## Identity Management
112
+
113
+ Link sessions to your authenticated users with `identity.set()`:
114
+
115
+ ```tsx
116
+ import { useInterfere } from "@interfere/react/provider";
117
+
118
+ function useInterfereIdentity() {
119
+ const { identity } = useInterfere();
120
+
121
+ // Clerk, Auth0, etc.
122
+ const { user } = useAuthProvider();
123
+
124
+ useEffect(() => {
125
+ if (user) {
126
+ identity.set({
127
+ identifier: user.id,
128
+ name: user.name,
129
+ email: user.email,
130
+ source: { type: "clerk", name: "Clerk" },
131
+ });
132
+ } else {
133
+ identity.clear();
134
+ }
135
+ }, [user]);
136
+
137
+ return null;
138
+ }
139
+ ```
140
+
141
+ ### Parameters
142
+
143
+ | Field | Required | Description |
144
+ | --- | --- | --- |
145
+ | `identifier` | Yes | Unique user ID (your internal ID, not email) |
146
+ | `source` | Yes | Auth source: `{ type: "clerk", name: "Clerk" }`, `{ type: "auth0", name: "Auth0" }`, or `{ type: "custom", name: "Your Provider" }` |
147
+ | `name` | No | Display name |
148
+ | `email` | No | Email address |
149
+ | `avatar` | No | Avatar URL |
150
+ | `traits` | No | Arbitrary key-value metadata (`Record<string, unknown>`) |
151
+
152
+ ### API
153
+
154
+ | Method | Description |
155
+ | --- | --- |
156
+ | `identity.set(params)` | Link the current session to a user. Deduplicated per session — only the first call sends a request. |
157
+ | `identity.get()` | Returns the current `IdentifyParams`, or `null` if no identity has been set. |
158
+
159
+ Identity is automatically cleared when the SDK is closed or the session rotates.
160
+
161
+ ## Consent Management
162
+
163
+ By default, all SDK features are active. To gate features behind user consent, pass a `consent` prop to the provider:
164
+
165
+ ```tsx
166
+ import { InterfereProvider } from "@interfere/react/provider";
167
+
168
+ function App() {
169
+ return (
170
+ <InterfereProvider consent={{ analytics: true, replay: false }}>
171
+ <YourApp />
172
+ </InterfereProvider>
173
+ );
174
+ }
175
+ ```
176
+
177
+ ### Consent categories
178
+
179
+ | Category | Plugins | Gated? |
180
+ | --- | --- | --- |
181
+ | `necessary` | Error tracking | Always on |
182
+ | `analytics` | Page events, rage clicks, fingerprint | Yes |
183
+ | `replay` | Session replay | Yes |
184
+
185
+ Omitting the `consent` prop disables gating entirely (all features load). Passing it enables gating — only `necessary` plugins plus explicitly consented categories will activate.
186
+
187
+ ### Imperative API
188
+
189
+ Use `consent.set()` and `consent.get()` from the `useInterfere` hook:
190
+
191
+ ```tsx
192
+ const { consent } = useInterfere();
193
+
194
+ consent.set({ analytics: true, replay: true }); // selective
195
+ consent.set(); // grant all
196
+ consent.get(); // current state, or null if no gating
197
+ ```
198
+
199
+ ### Initial consent via init
200
+
201
+ To set consent before React renders (avoiding any window where non-consented plugins might load), pass it to `init()`:
202
+
203
+ ```ts
204
+ import { init } from "@interfere/vite/init";
205
+
206
+ init({ consent: { analytics: false, replay: false } });
207
+ ```
208
+
209
+ The provider's `consent` prop will then keep it in sync as the user updates their preferences.
210
+
211
+ ## What's Included
212
+
213
+ - **Build metadata injection** — automatically tags each build with a Git SHA-derived build and release ID
214
+ - **Error tracking** — automatic capture of uncaught errors and unhandled rejections
215
+ - **Session replay** — full visual playback of user sessions
216
+ - **Page analytics** — SPA-aware pageviews and UI interaction events
217
+ - **Rage click detection** — surface frustrated user behavior
218
+ - **Consent gating** — fine-grained control over which features activate
219
+
220
+ ## License
221
+
222
+ MIT
@@ -0,0 +1,7 @@
1
+ import { ClientOptions, ClientOptions as ClientOptions$1, consent } from "@interfere/react/internal/client";
2
+ import { ConsentCategory, ConsentState, GateableCategory } from "@interfere/types/sdk/plugins/manifest";
3
+
4
+ //#region src/init.d.ts
5
+ declare function init(opts?: ClientOptions$1): void;
6
+ //#endregion
7
+ export { type ClientOptions, type ConsentCategory, type ConsentState, type GateableCategory, consent, init };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.d.mts","names":[],"sources":["../src/init.ts"],"mappings":";;;;iBAcgB,IAAA,CAAK,IAAA,GAAO,eAAA"}
package/dist/init.mjs ADDED
@@ -0,0 +1,7 @@
1
+ import { consent, init as init$1 } from "@interfere/react/internal/client";
2
+ //#region src/init.ts
3
+ function init(opts) {
4
+ init$1(opts);
5
+ }
6
+ //#endregion
7
+ export { consent, init };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.mjs","names":[],"sources":["../src/init.ts"],"sourcesContent":["import {\n type ClientOptions,\n init as coreInit,\n} from \"@interfere/react/internal/client\";\n\nexport type { ClientOptions } from \"@interfere/react/internal/client\";\n// biome-ignore lint/performance/noBarrelFile: init entrypoint must re-export consent for SDK consumers.\nexport { consent } from \"@interfere/react/internal/client\";\nexport type {\n ConsentCategory,\n ConsentState,\n GateableCategory,\n} from \"@interfere/types/sdk/plugins/manifest\";\n\nexport function init(opts?: ClientOptions): void {\n coreInit(opts);\n}\n"],"mappings":";;AAcA,SAAgB,KAAK,MAA4B;AAC/C,QAAS,KAAK"}
@@ -0,0 +1,10 @@
1
+ import { Plugin } from "vite";
2
+
3
+ //#region src/plugin.d.ts
4
+ interface InterfereViteOptions {
5
+ buildId?: string;
6
+ releaseId?: string;
7
+ }
8
+ declare function interfere(options?: InterfereViteOptions): Plugin;
9
+ //#endregion
10
+ export { InterfereViteOptions, interfere as default, interfere };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.mts","names":[],"sources":["../src/plugin.ts"],"mappings":";;;UAGiB,oBAAA;EACf,OAAA;EACA,SAAA;AAAA;AAAA,iBAWc,SAAA,CAAU,OAAA,GAAU,oBAAA,GAAuB,MAAA"}
@@ -0,0 +1,34 @@
1
+ import { execSync } from "node:child_process";
2
+ //#region src/plugin.ts
3
+ function resolveGitSha() {
4
+ try {
5
+ return execSync("git rev-parse HEAD", { encoding: "utf8" }).trim();
6
+ } catch {
7
+ return null;
8
+ }
9
+ }
10
+ function interfere(options) {
11
+ let buildId;
12
+ let releaseId;
13
+ let publicKey;
14
+ return {
15
+ name: "interfere",
16
+ enforce: "pre",
17
+ configResolved(config) {
18
+ buildId = options?.buildId ?? config.env["VITE_INTERFERE_BUILD_ID"] ?? resolveGitSha() ?? crypto.randomUUID();
19
+ releaseId = options?.releaseId ?? buildId;
20
+ publicKey = config.env["VITE_INTERFERE_PUBLIC_KEY"];
21
+ },
22
+ transformIndexHtml() {
23
+ const assignments = [`globalThis["__INTERFERE_BUILD_ID__"]=${JSON.stringify(buildId)};`, `globalThis["__INTERFERE_RELEASE_ID__"]=${JSON.stringify(releaseId)};`];
24
+ if (publicKey) assignments.push(`globalThis["__INTERFERE_PUBLIC_KEY__"]=${JSON.stringify(publicKey)};`);
25
+ return [{
26
+ tag: "script",
27
+ children: assignments.join(""),
28
+ injectTo: "head-prepend"
29
+ }];
30
+ }
31
+ };
32
+ }
33
+ //#endregion
34
+ export { interfere as default, interfere };
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.mjs","names":[],"sources":["../src/plugin.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport type { HtmlTagDescriptor, Plugin } from \"vite\";\n\nexport interface InterfereViteOptions {\n buildId?: string;\n releaseId?: string;\n}\n\nfunction resolveGitSha(): string | null {\n try {\n return execSync(\"git rev-parse HEAD\", { encoding: \"utf8\" }).trim();\n } catch {\n return null;\n }\n}\n\nexport function interfere(options?: InterfereViteOptions): Plugin {\n let buildId: string;\n let releaseId: string;\n let publicKey: string | undefined;\n\n return {\n name: \"interfere\",\n enforce: \"pre\",\n\n configResolved(config) {\n buildId =\n options?.buildId ??\n config.env[\"VITE_INTERFERE_BUILD_ID\"] ??\n resolveGitSha() ??\n crypto.randomUUID();\n\n releaseId = options?.releaseId ?? buildId;\n\n publicKey = config.env[\"VITE_INTERFERE_PUBLIC_KEY\"];\n },\n\n transformIndexHtml() {\n const assignments = [\n `globalThis[\"__INTERFERE_BUILD_ID__\"]=${JSON.stringify(buildId)};`,\n `globalThis[\"__INTERFERE_RELEASE_ID__\"]=${JSON.stringify(releaseId)};`,\n ];\n\n if (publicKey) {\n assignments.push(\n `globalThis[\"__INTERFERE_PUBLIC_KEY__\"]=${JSON.stringify(publicKey)};`\n );\n }\n\n return [\n {\n tag: \"script\",\n children: assignments.join(\"\"),\n injectTo: \"head-prepend\",\n },\n ] satisfies HtmlTagDescriptor[];\n },\n };\n}\n\nexport default interfere;\n"],"mappings":";;AAQA,SAAS,gBAA+B;AACtC,KAAI;AACF,SAAO,SAAS,sBAAsB,EAAE,UAAU,QAAQ,CAAC,CAAC,MAAM;SAC5D;AACN,SAAO;;;AAIX,SAAgB,UAAU,SAAwC;CAChE,IAAI;CACJ,IAAI;CACJ,IAAI;AAEJ,QAAO;EACL,MAAM;EACN,SAAS;EAET,eAAe,QAAQ;AACrB,aACE,SAAS,WACT,OAAO,IAAI,8BACX,eAAe,IACf,OAAO,YAAY;AAErB,eAAY,SAAS,aAAa;AAElC,eAAY,OAAO,IAAI;;EAGzB,qBAAqB;GACnB,MAAM,cAAc,CAClB,wCAAwC,KAAK,UAAU,QAAQ,CAAC,IAChE,0CAA0C,KAAK,UAAU,UAAU,CAAC,GACrE;AAED,OAAI,UACF,aAAY,KACV,0CAA0C,KAAK,UAAU,UAAU,CAAC,GACrE;AAGH,UAAO,CACL;IACE,KAAK;IACL,UAAU,YAAY,KAAK,GAAG;IAC9B,UAAU;IACX,CACF;;EAEJ"}
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@interfere/vite",
3
+ "version": "0.1.0",
4
+ "license": "MIT",
5
+ "description": "Vite build plugin and client initialization for Interfere.",
6
+ "keywords": [
7
+ "observability",
8
+ "typescript",
9
+ "vite",
10
+ "error-tracking",
11
+ "session-replay"
12
+ ],
13
+ "homepage": "https://interfere.com",
14
+ "bugs": {
15
+ "url": "mailto:support@interfere.com"
16
+ },
17
+ "author": "Interfere <support@interfere.com> (https://interfere.com)",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/interfere-inc/interfere.git",
21
+ "directory": "src/packages/public/vite"
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "type": "module",
27
+ "exports": {
28
+ "./plugin": {
29
+ "@source": "./src/plugin.ts",
30
+ "types": "./dist/plugin.d.mts",
31
+ "default": "./dist/plugin.mjs"
32
+ },
33
+ "./init": {
34
+ "@source": "./src/init.ts",
35
+ "types": "./dist/init.d.mts",
36
+ "default": "./dist/init.mjs"
37
+ }
38
+ },
39
+ "sideEffects": false,
40
+ "publishConfig": {
41
+ "access": "public",
42
+ "tag": "alpha"
43
+ },
44
+ "scripts": {
45
+ "build": "tsdown",
46
+ "dev": "tsdown --watch",
47
+ "test": "vitest run --coverage",
48
+ "typecheck": "tsc --noEmit --incremental"
49
+ },
50
+ "dependencies": {
51
+ "@interfere/react": "^8.1.0",
52
+ "@interfere/types": "^8.1.0"
53
+ },
54
+ "peerDependencies": {
55
+ "vite": ">=5"
56
+ },
57
+ "devDependencies": {
58
+ "@interfere/typescript-config": "^8.1.0",
59
+ "@interfere/test-utils": "^1.0.0",
60
+ "@types/node": "^24.12.0",
61
+ "@vitest/coverage-v8": "^4.1.2",
62
+ "tsdown": "^0.21.6",
63
+ "typescript": "6.0.2",
64
+ "vite": "^8.0.0",
65
+ "vitest": "^4.1.2"
66
+ }
67
+ }