@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 +222 -0
- package/dist/init.d.mts +7 -0
- package/dist/init.d.mts.map +1 -0
- package/dist/init.mjs +7 -0
- package/dist/init.mjs.map +1 -0
- package/dist/plugin.d.mts +10 -0
- package/dist/plugin.d.mts.map +1 -0
- package/dist/plugin.mjs +34 -0
- package/dist/plugin.mjs.map +1 -0
- package/package.json +67 -0
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
|
package/dist/init.d.mts
ADDED
|
@@ -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 @@
|
|
|
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"}
|
package/dist/plugin.mjs
ADDED
|
@@ -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
|
+
}
|