@usero/sdk 0.2.0 → 0.3.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 +86 -0
- package/dist/all-M6KEAHE5.cjs +9110 -0
- package/dist/all-M6KEAHE5.cjs.map +1 -0
- package/dist/all-T4CCPHSL.js +9095 -0
- package/dist/all-T4CCPHSL.js.map +1 -0
- package/dist/chunk-5BLDMQED.cjs +18 -0
- package/dist/chunk-5BLDMQED.cjs.map +1 -0
- package/dist/chunk-NSBPE2FW.js +15 -0
- package/dist/chunk-NSBPE2FW.js.map +1 -0
- package/dist/plugins/session-replay.cjs +180 -0
- package/dist/plugins/session-replay.cjs.map +1 -0
- package/dist/plugins/session-replay.d.cts +75 -0
- package/dist/plugins/session-replay.d.ts +75 -0
- package/dist/plugins/session-replay.js +177 -0
- package/dist/plugins/session-replay.js.map +1 -0
- package/dist/react.cjs +138 -35
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +23 -0
- package/dist/react.d.ts +23 -0
- package/dist/react.js +138 -35
- package/dist/react.js.map +1 -1
- package/dist/usero.iife.js +59 -52
- package/dist/usero.iife.js.map +1 -1
- package/dist/vanilla.cjs +139 -35
- package/dist/vanilla.cjs.map +1 -1
- package/dist/vanilla.d.cts +25 -1
- package/dist/vanilla.d.ts +25 -1
- package/dist/vanilla.js +139 -36
- package/dist/vanilla.js.map +1 -1
- package/package.json +9 -1
package/README.md
CHANGED
|
@@ -80,6 +80,91 @@ The widget auto-detects the OS color scheme via `prefers-color-scheme`. It picks
|
|
|
80
80
|
| `onError` | `(err: Error) => void` | undefined | Fires on init or submission error |
|
|
81
81
|
| `onOpen` / `onClose` | `() => void` | undefined | Fire when the panel opens/closes |
|
|
82
82
|
|
|
83
|
+
## Plugins
|
|
84
|
+
|
|
85
|
+
The widget has a tiny plugin API for opt-in features that would otherwise bloat the base bundle. Plugins live in subpath exports so the base widget stays small for everyone who doesn't use them.
|
|
86
|
+
|
|
87
|
+
### Session replay
|
|
88
|
+
|
|
89
|
+
Attaches a rolling rrweb buffer (last 30 seconds by default) to each feedback submission, gzipped via the native CompressionStream API.
|
|
90
|
+
|
|
91
|
+
`rrweb` ships inside the plugin chunk, so `npm install @usero/sdk` is the only install step. The plugin entry is a separate subpath export, so consumers who never `import` it pay zero rrweb bytes on the base bundle.
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { initUseroFeedbackWidget } from '@usero/sdk'
|
|
95
|
+
import { sessionReplay } from '@usero/sdk/plugins/session-replay'
|
|
96
|
+
|
|
97
|
+
initUseroFeedbackWidget({
|
|
98
|
+
clientId: 'YOUR_CLIENT_ID',
|
|
99
|
+
plugins: [
|
|
100
|
+
sessionReplay({
|
|
101
|
+
bufferSeconds: 30,
|
|
102
|
+
// Wait 3s of engagement before loading rrweb. If the user navigates
|
|
103
|
+
// away first, rrweb is never fetched.
|
|
104
|
+
startAfterMs: 3000,
|
|
105
|
+
// Sample 50% of sessions. Decided once at init via Math.random().
|
|
106
|
+
sampleRate: 0.5,
|
|
107
|
+
}),
|
|
108
|
+
],
|
|
109
|
+
})
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The base bundle (`@usero/sdk` and `@usero/sdk/react`) has zero rrweb references. Importing the plugin pulls in a separate chunk, and `rrweb` itself lazy-loads at runtime via dynamic import the first time the engagement gate elapses, so even consumers who DO opt into the plugin don't pay rrweb's bytes upfront.
|
|
113
|
+
|
|
114
|
+
#### Privacy defaults
|
|
115
|
+
|
|
116
|
+
| Option | Default | What it does |
|
|
117
|
+
| ------------------- | ---------------------- | ------------------------------------------------------------- |
|
|
118
|
+
| `maskAllInputs` | `true` | Mask `<input>` and `<textarea>` values in the recording |
|
|
119
|
+
| `maskTextSelector` | `'[data-usero-mask]'` | Mask text content of any node matching this selector |
|
|
120
|
+
| `blockSelector` | `'[data-usero-block]'` | Skip recording subtrees entirely |
|
|
121
|
+
| `inlineStylesheet` | `true` | Inline external stylesheets so replays render offline |
|
|
122
|
+
| `sampling` | `{ mousemove: 50, scroll: 100 }` | Throttle high-frequency events |
|
|
123
|
+
| `bufferSeconds` | `30` | Length of the rolling in-memory buffer in seconds |
|
|
124
|
+
| `startAfterMs` | `0` | Engagement gate before loading rrweb |
|
|
125
|
+
| `sampleRate` | `1` | Probability (0..1) that a given session records at all |
|
|
126
|
+
|
|
127
|
+
Tag any DOM node you want masked at the source: `<div data-usero-mask>...</div>`. Tag entire subtrees you want skipped with `data-usero-block`.
|
|
128
|
+
|
|
129
|
+
### Writing your own plugin
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import type { UseroPlugin } from '@usero/sdk'
|
|
133
|
+
|
|
134
|
+
export function consoleCapture(): UseroPlugin {
|
|
135
|
+
return {
|
|
136
|
+
name: 'console-capture',
|
|
137
|
+
onInit(ctx) {
|
|
138
|
+
const logs: string[] = []
|
|
139
|
+
ctx.setStore(logs)
|
|
140
|
+
const original = console.log
|
|
141
|
+
console.log = (...args) => {
|
|
142
|
+
logs.push(args.map(String).join(' '))
|
|
143
|
+
original(...args)
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
onFeedbackSubmit(ctx) {
|
|
147
|
+
const logs = ctx.getStore<string[]>() ?? []
|
|
148
|
+
return { metadata: { recentLogs: logs.slice(-50) } }
|
|
149
|
+
},
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Plugins return a `Partial<FeedbackSubmission>` from `onFeedbackSubmit`. Top-level keys are shallow-merged into the outgoing payload (later plugins win wholesale). `metadata` is deep-merged one level so multiple plugins can each contribute their own metadata keys without clobbering each other.
|
|
155
|
+
|
|
156
|
+
### `widget.whenReady()`
|
|
157
|
+
|
|
158
|
+
`initUseroFeedbackWidget` returns a handle with a `whenReady(): Promise<void>` method that resolves once every plugin's `onInit` has settled (fulfilled or rejected — a misbehaving plugin never blocks readiness). It's intended for end-to-end tests and dogfooding scripts that want to trigger a synthetic submit only after all plugins are live:
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
const widget = initUseroFeedbackWidget({ clientId, plugins: [sessionReplay()] })
|
|
162
|
+
await widget.whenReady()
|
|
163
|
+
widget.open()
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
If no plugins are registered, `whenReady()` resolves immediately.
|
|
167
|
+
|
|
83
168
|
## Why named exports only
|
|
84
169
|
|
|
85
170
|
Default exports break tree-shaking and rename inconsistently across consumer codebases. The package exports nothing as a default, anywhere, on purpose.
|
|
@@ -99,6 +184,7 @@ Outputs:
|
|
|
99
184
|
|
|
100
185
|
- `dist/vanilla.js` (ESM) + `dist/vanilla.cjs` + `dist/vanilla.d.ts`
|
|
101
186
|
- `dist/react.js` (ESM) + `dist/react.cjs` + `dist/react.d.ts`
|
|
187
|
+
- `dist/plugins/session-replay.js` (ESM) + `.cjs` + `.d.ts` plus a sibling `dist/all-*.js` chunk that holds the bundled rrweb runtime. The plugin loads this chunk via dynamic `import()` so it only downloads when the plugin actually needs it.
|
|
102
188
|
- `dist/usero.iife.js` (minified, exposes `window.Usero`)
|
|
103
189
|
|
|
104
190
|
## License
|