@reopt-ai/data-sdk-client 0.1.6 → 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.
Files changed (57) hide show
  1. package/README.md +141 -7
  2. package/dist/client-B1GlWwVq.d.ts +744 -0
  3. package/dist/client-B1GlWwVq.d.ts.map +1 -0
  4. package/dist/client-BCrui21p.d.cts +744 -0
  5. package/dist/client-BCrui21p.d.cts.map +1 -0
  6. package/dist/exceptions-C9Fh0BIR.js +285 -0
  7. package/dist/exceptions-C9Fh0BIR.js.map +1 -0
  8. package/dist/exceptions-CuhGo9A5.cjs +289 -0
  9. package/dist/exceptions-CuhGo9A5.cjs.map +1 -0
  10. package/dist/index.cjs +75 -1874
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +83 -0
  13. package/dist/index.d.cts.map +1 -0
  14. package/dist/index.d.ts +35 -33
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +49 -70
  17. package/dist/index.js.map +1 -1
  18. package/dist/next.cjs +51 -1919
  19. package/dist/next.cjs.map +1 -1
  20. package/dist/next.d.cts +31 -0
  21. package/dist/next.d.cts.map +1 -0
  22. package/dist/next.d.ts +8 -10
  23. package/dist/next.d.ts.map +1 -0
  24. package/dist/next.js +36 -49
  25. package/dist/next.js.map +1 -1
  26. package/dist/observe-D-BHMwrO.cjs +54 -0
  27. package/dist/observe-D-BHMwrO.cjs.map +1 -0
  28. package/dist/observe-D1pNg1qL.js +54 -0
  29. package/dist/observe-D1pNg1qL.js.map +1 -0
  30. package/dist/react.cjs +105 -1863
  31. package/dist/react.cjs.map +1 -1
  32. package/dist/react.d.cts +80 -0
  33. package/dist/react.d.cts.map +1 -0
  34. package/dist/react.d.ts +52 -46
  35. package/dist/react.d.ts.map +1 -0
  36. package/dist/react.js +118 -23
  37. package/dist/react.js.map +1 -1
  38. package/dist/registry-BPhiH_l9.js +1775 -0
  39. package/dist/registry-BPhiH_l9.js.map +1 -0
  40. package/dist/registry-CGqrOtf4.cjs +1810 -0
  41. package/dist/registry-CGqrOtf4.cjs.map +1 -0
  42. package/dist/tracing-DAhaKkl0.cjs +93 -0
  43. package/dist/tracing-DAhaKkl0.cjs.map +1 -0
  44. package/dist/tracing-Dt1RVuQ9.js +93 -0
  45. package/dist/tracing-Dt1RVuQ9.js.map +1 -0
  46. package/package.json +33 -18
  47. package/dist/chunk-4MTDZBRS.js +0 -57
  48. package/dist/chunk-4MTDZBRS.js.map +0 -1
  49. package/dist/chunk-SYCBGBTH.js +0 -1627
  50. package/dist/chunk-SYCBGBTH.js.map +0 -1
  51. package/dist/chunk-YRP3I3OD.js +0 -106
  52. package/dist/chunk-YRP3I3OD.js.map +0 -1
  53. package/dist/client-BCd2fgmx.d.ts +0 -581
  54. package/dist/exceptions-NQHZUDYO.js +0 -9
  55. package/dist/exceptions-NQHZUDYO.js.map +0 -1
  56. package/dist/tracing-LC3NZND7.js +0 -91
  57. package/dist/tracing-LC3NZND7.js.map +0 -1
package/README.md CHANGED
@@ -104,9 +104,11 @@ init({
104
104
  scrollDepth: true,
105
105
  exceptions: false, // $exception for uncaught errors and unhandled rejections; loads as its own chunk when on
106
106
  },
107
+ release: undefined, // build id stamped on every event as $release_id; see below
107
108
  tracingHeaders: false, // opt-in: add reopt-device-id to same-origin fetch/XHR (true) or to listed hosts; loads as its own chunk
108
109
  normalizePath: (pathname) => pathname, // see below; applied to every path the SDK stamps itself
109
110
  fetch: undefined, // transport override for tests
111
+ observe: undefined, // local lifecycle observer for devtools; never sent over the network
110
112
  consent: { categories: ["analytics", "marketing"], defaultConsent: true },
111
113
  batch: { size: 100, intervalMs: 1000, maxBytes: 400_000 },
112
114
  retry: { maxRetries: 3, baseDelay: 1000, maxDelay: 30000, jitter: 0.1 },
@@ -115,6 +117,42 @@ init({
115
117
  });
116
118
  ```
117
119
 
120
+ ## Release tracking
121
+
122
+ `release` stamps every event with `$release_id`. Error tracking reads it to say
123
+ which build an issue first and last appeared in, and — once you resolve an issue
124
+ "in" a release — to tell a plain reopen from a **regression**, where the fix you
125
+ declared did not hold.
126
+
127
+ ```ts
128
+ init({ writeKey: "wk_…", release: "1.4.0" });
129
+ ```
130
+
131
+ Most apps do not know their version at the call site. The SDK falls back to
132
+ `globalThis.__REOPT_RELEASE__`, which is where a build step writes it:
133
+
134
+ ```js
135
+ // vite.config.js
136
+ export default { define: { __REOPT_RELEASE__: JSON.stringify(process.env.GIT_SHA) } };
137
+
138
+ // next.config.js
139
+ module.exports = {
140
+ webpack: (config, { webpack }) => {
141
+ config.plugins.push(
142
+ new webpack.DefinePlugin({ __REOPT_RELEASE__: JSON.stringify(process.env.VERCEL_GIT_COMMIT_SHA) })
143
+ );
144
+ return config;
145
+ },
146
+ };
147
+ ```
148
+
149
+ An explicit `release` wins over the global. With neither, the property is
150
+ omitted entirely — not sent as an empty string, which would create a release
151
+ named `""` and label every issue with it.
152
+
153
+ The label is yours: a version, a commit sha, a build id. The server never issues
154
+ one and never rejects an event for carrying an unfamiliar release.
155
+
118
156
  ## Runtime-resolved keys, manual page views, external consent
119
157
 
120
158
  The three things a multi-tenant host needs, in the shape it needs them:
@@ -134,6 +172,7 @@ const writeKey = await resolveBrandWriteKey(brandId);
134
172
  - **`register(properties)`** attaches properties to every event from then on — including the automatic ones. This is how a host puts its own breakdown axis (`page_id`, a tenant) on `$web_vitals` and `$pageleave`, which the SDK otherwise stamps with only a path. An event's own properties win; `reset()` clears them.
135
173
  - **Fail-open.** A missing `writeKey` or `baseUrl` logs a warning and yields a disabled client whose every call is a no-op; nothing throws. Analytics must never be why a page fails.
136
174
  - **`fetch`** injects the transport. A test can hand in a recording function and assert on the exact payload the SDK built, instead of intercepting the network.
175
+ - **`observe`** receives local configuration, enqueue/drop, identity, consent, and pause/resume facts in lifecycle order. Its implementation loads as an opt-in chunk, buffers facts while that chunk arrives, ignores callback errors, and never receives the write key or event properties. Use it for diagnostics such as `@reopt-ai/data-sdk-devtool`, not application behavior.
137
176
 
138
177
  ## Bootstrap and caching (Next.js)
139
178
 
@@ -145,7 +184,102 @@ const writeKey = await resolveBrandWriteKey(brandId);
145
184
 
146
185
  ## Size
147
186
 
148
- The production bundle is checked on every build (`scripts/check-size.mjs`): each entry is bundled as a consumer would (minified, `NODE_ENV=production`, framework externals) and must stay under **10.4 KB gzip** for the vanilla entry (10.65 / 10.9 KB for `./react` / `./next`) and carry no zod. Opt-in features (tracing headers, exception capture) are separate chunks. Event validation in production is a hand-written mirror of the contract; the zod schema runs in development only and is dropped by the consumer's bundler.
187
+ ### `$exception_list` the structured form
188
+
189
+ Alongside the flat keys, an exception carries the parsed cause chain. The flat
190
+ keys still describe the outermost error and are unchanged, so anything written
191
+ against them keeps working; `$exception_list` is what the issue page renders and
192
+ what fingerprinting groups on.
193
+
194
+ | Field | Meaning |
195
+ | --------------------- | ------------------------------------------------------------------------------------- |
196
+ | `type` / `value` | `TypeError` / the message. For a non-`Error` throw the SDK derives both. |
197
+ | `mechanism.handled` | `false` when nothing caught it. Causes are `true` — they were caught and re-thrown. |
198
+ | `mechanism.type` | `onerror` \| `unhandledrejection` \| `manual` \| `console` \| `nextjs_onRequestError` |
199
+ | `mechanism.synthetic` | The SDK invented the type because what was thrown was not an `Error`. |
200
+ | `stacktrace.frames[]` | `filename`, `function`, `lineno`, `colno`, `in_app` — innermost first. |
201
+
202
+ ```json
203
+ {
204
+ "$exception_type": "TypeError",
205
+ "$exception_message": "cart.items is not a function",
206
+ "$exception_list": [
207
+ {
208
+ "type": "TypeError",
209
+ "value": "cart.items is not a function",
210
+ "mechanism": { "handled": false, "type": "onerror" },
211
+ "stacktrace": {
212
+ "type": "raw",
213
+ "frames": [
214
+ {
215
+ "filename": "https://shop.example.com/main.a1b2c3d4.js",
216
+ "function": "handleCheckout",
217
+ "lineno": 412,
218
+ "colno": 19,
219
+ "in_app": true
220
+ }
221
+ ]
222
+ }
223
+ }
224
+ ]
225
+ }
226
+ ```
227
+
228
+ #### Throttling
229
+
230
+ Automatic capture is throttled per exception `type` — a token bucket of 10 with
231
+ one token back every 10s. One component stuck in a rejection loop can fire
232
+ thousands of identical events a second; unthrottled that fills the visitor's
233
+ network and the customer's quota with a single bug, and buries every other error
234
+ in the issue list.
235
+
236
+ ```ts
237
+ capture: {
238
+ exceptions: true,
239
+ exceptionRateLimit: { bucketSize: 20, refillSeconds: 5 }, // or `false` for no limit
240
+ }
241
+ ```
242
+
243
+ Per type, so a runaway `TypeError` cannot silence a `RangeError` that starts
244
+ later. Never applied to `captureException()` — a report you asked for explicitly
245
+ is not noise the SDK gets to drop. When throttling kicks in, `debug: true` logs
246
+ it once per type.
247
+
248
+ #### Breadcrumbs
249
+
250
+ `$exception_steps` carries what happened just before the error — up to 20 steps,
251
+ each with a 200-character message and at most 1 KB of serialized `data`.
252
+
253
+ ```ts
254
+ client.addExceptionStep({ category: "fetch", message: "POST /orders", data: { status: 500 } });
255
+ // …later, any exception carries the trail
256
+ ```
257
+
258
+ `capture.exceptionSteps: true` also records a `navigation` step on every page
259
+ view. Off by default: navigation history on every error is a privacy decision
260
+ the host makes, not the SDK. `addExceptionStep()` works either way.
261
+
262
+ The trail is copied onto an exception, not moved — a second error in the same
263
+ session sees the same history rather than an empty one. One buffer per page, not
264
+ per client.
265
+
266
+ A cause chain is carried outermost first, up to four links. A stack made
267
+ entirely of `chrome-extension://` frames is **not reported** — it is a bug in a
268
+ visitor's extension, which the site cannot reproduce or fix.
269
+
270
+ `captureException(error, options?)` takes `{ fingerprint?, level?, properties? }`.
271
+ `fingerprint` is a _suggestion_ — the server bounds it and decides — and is how
272
+ you say "group these two apart" or "group these together". The pre-options shape
273
+ `captureException(error, { myProperty: 1 })` still works: an object carrying none
274
+ of the three option keys is read as properties.
275
+
276
+ `captureException()` is the one place the list can be absent. The parsers live
277
+ in the exception chunk, and a manual capture made before that chunk has loaded
278
+ reports the flat keys only; the server falls back to fingerprinting those, so
279
+ the event still groups. Turning on `capture.exceptions` loads the chunk at init,
280
+ which is the usual case.
281
+
282
+ The production bundle is checked on every build (`scripts/check-size.mjs`): each entry is bundled as a consumer would (minified, `NODE_ENV=production`, framework externals) and must stay under **10.65 KB gzip** for the vanilla entry (10.9 / 11.15 KB for `./react` / `./next`) and carry no zod. Opt-in features (observer formatting, tracing headers, exception capture) are separate chunks. Event validation in production is a hand-written mirror of the contract; the zod schema runs in development only and is dropped by the consumer's bundler.
149
283
 
150
284
  ## What it does that you would otherwise have to remember
151
285
 
@@ -161,9 +295,9 @@ The production bundle is checked on every build (`scripts/check-size.mjs`): each
161
295
 
162
296
  Names and property keys are exported from `@reopt-ai/data-contract/events` (`AUTO_EVENT_NAMES`, `AUTO_EVENT_PROPERTIES`); the low-cardinality subset suitable as rollup dimensions is `AUTO_EVENT_ROLLUP_KEYS`. Derive catalogue entries from those rather than retyping them.
163
297
 
164
- | Event | Properties |
165
- | ------------- | ------------------------------------------------------------------------------------------------------------------ |
166
- | `$pageview` | `path`, `origin`, `title`, `referrer`, `utm_*`, `search` (Next, when present), plus anything `normalizePath` lifts |
167
- | `$pageleave` | `path`, `origin`, `duration` (s), `scroll_depth` (0–100) |
168
- | `$web_vitals` | `metric_name`, `metric_id`, `value`, `delta`, `rating`, `navigation_type`, `path` |
169
- | `$exception` | `$exception_type`, `$exception_message`, `$exception_stack`, `$exception_source`, `$exception_handled`, `path` |
298
+ | Event | Properties |
299
+ | ------------- | --------------------------------------------------------------------------------------------------------------------------------- |
300
+ | `$pageview` | `path`, `origin`, `title`, `referrer`, `utm_*`, `search` (Next, when present), plus anything `normalizePath` lifts |
301
+ | `$pageleave` | `path`, `origin`, `duration` (s), `scroll_depth` (0–100) |
302
+ | `$web_vitals` | `metric_name`, `metric_id`, `value`, `delta`, `rating`, `navigation_type`, `path` |
303
+ | `$exception` | `$exception_type`, `$exception_message`, `$exception_stack`, `$exception_source`, `$exception_handled`, `$exception_list`, `path` |