@nzila/sdk 0.1.0 → 0.1.1
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 +12 -1
- package/examples/COMPLEX-USAGE.md +113 -0
- package/examples/INDEX.md +27 -0
- package/examples/README.md +94 -0
- package/examples/RUNTIME-TRACE.md +222 -0
- package/examples/TRACING-FAILURES.md +186 -0
- package/examples/backend/error-tracing.test.ts +44 -0
- package/examples/backend/order-pricing.test.ts +54 -0
- package/examples/backend/order-pricing.ts +27 -0
- package/examples/backend/runtime-trace.example.ts +31 -0
- package/examples/backend/sample-payload.json +62 -0
- package/examples/backend/sample-tracing-payload.json +90 -0
- package/examples/complex/api-services.demo.ts +72 -0
- package/examples/complex/full-platform.demo.test.ts +46 -0
- package/examples/frontend/checkout-form.test.ts +54 -0
- package/examples/frontend/checkout-form.ts +29 -0
- package/examples/frontend/error-tracing.test.ts +59 -0
- package/examples/frontend/sample-payload.json +62 -0
- package/examples/frontend/sample-tracing-payload.json +73 -0
- package/examples/frontend-webhook-client.ts +32 -0
- package/examples/jest.config.example.cjs +18 -0
- package/examples/mocharc.nzila.example.cjs +21 -0
- package/examples/react/checkout-feature.example.tsx +41 -0
- package/examples/send-run-manual.mjs +16 -0
- package/examples/shared/feature-map.ts +24 -0
- package/examples/shared/load-env.mjs +24 -0
- package/examples/shared/send-webhook.mjs +43 -0
- package/examples/vitest.config.example.ts +20 -0
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -67,4 +67,15 @@ const nzila = traceNzilaFeature("Checkout");
|
|
|
67
67
|
await nzila.runApi("charge", () => charge());
|
|
68
68
|
```
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
## Examples (included in this package)
|
|
71
|
+
|
|
72
|
+
After install, open **`node_modules/@nzila/sdk/examples/`**:
|
|
73
|
+
|
|
74
|
+
- **[INDEX.md](./examples/INDEX.md)** — map of all samples
|
|
75
|
+
- **[COMPLEX-USAGE.md](./examples/COMPLEX-USAGE.md)** — every feature (Vitest, runtime, `mapFeature`, webhooks)
|
|
76
|
+
- **[RUNTIME-TRACE.md](./examples/RUNTIME-TRACE.md)** — React + backend live tracing
|
|
77
|
+
- **[TRACING-FAILURES.md](./examples/TRACING-FAILURES.md)** — tests → dashboard
|
|
78
|
+
- Config: `vitest.config.example.ts`, `jest.config.example.cjs`, `mocharc.nzila.example.cjs`
|
|
79
|
+
- Code: `backend/`, `frontend/`, `complex/`, `react/checkout-feature.example.tsx`
|
|
80
|
+
|
|
81
|
+
All example imports use `@nzila/sdk` (same as your app).
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# Complex usage — all Nzila SDK features
|
|
2
|
+
|
|
3
|
+
This guide ties together **test webhooks**, **runtime signals**, **mapFeature**, and **manual error reporting** in one flow. Use it after the quick starts in [README.md](README.md), [TRACING-FAILURES.md](TRACING-FAILURES.md), and [RUNTIME-TRACE.md](RUNTIME-TRACE.md).
|
|
4
|
+
|
|
5
|
+
## Feature matrix
|
|
6
|
+
|
|
7
|
+
| Capability | API | When to use |
|
|
8
|
+
|------------|-----|-------------|
|
|
9
|
+
| CI test run | `NzilaVitestReporter` / `NzilaJestReporter` / Mocha | Every `vitest run` / Jest / Mocha finish |
|
|
10
|
+
| Manual run POST | `sendNzilaRun(payload, { webhookUrl, apiKey })` | Custom runners, replay payloads |
|
|
11
|
+
| Feature naming | `mapFeature(describePath, testName)` | Suite title ≠ dashboard feature |
|
|
12
|
+
| Backend handler | `traceNzilaFeature("Checkout")` | First line of route/service |
|
|
13
|
+
| React screen | `useNzilaFeature("Checkout")` | Top of component matching tests |
|
|
14
|
+
| HTTP wrapper | `nzila.runApi("label", () => fetch…)` | Auto `api` success/failure |
|
|
15
|
+
| Handled errors | `nzila.captureError(err, { … })` | `catch`, validation, business rules |
|
|
16
|
+
| No handle in scope | `reportFeatureError("Checkout", err)` | Deep helper, fire-and-forget |
|
|
17
|
+
| API failure label | `nzila.captureApiError(err, "payment.charge")` | After failed call outside `runApi` |
|
|
18
|
+
| UI issues | `nzila.reportUi("custom_banner", { … })` | Non-throwing UX problems |
|
|
19
|
+
| Render errors | `NzilaFeatureBoundary` + `useNzilaFeature` | Child tree throws |
|
|
20
|
+
| Static demo payload | `shared/send-webhook.mjs` | No Vitest installed |
|
|
21
|
+
|
|
22
|
+
Use the **same feature strings** everywhere (e.g. `"Checkout"`, `"Inventory"`) so test failures and runtime signals land on one dashboard row.
|
|
23
|
+
|
|
24
|
+
## 1. Complex Vitest run (multi-feature, pass + fail)
|
|
25
|
+
|
|
26
|
+
**Files:** `complex/full-platform.demo.test.ts`, `vitest.complex.config.ts`, `shared/feature-map.ts`
|
|
27
|
+
|
|
28
|
+
- Three describe roots: `Checkout UI`, `Inventory`, `Authentication`
|
|
29
|
+
- `mapFeature` maps UI vs API names via `resolveFrontendFeature` / `resolveBackendFeature`
|
|
30
|
+
- Several tests **fail on purpose** so Nzila shows failure analysis
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
# .env.local: NZILA_API_KEY, NZILA_WEBHOOK_URL
|
|
34
|
+
# Optional: NZILA_COMPLEX_APP_NAME=checkout-api (must exist in dashboard)
|
|
35
|
+
npm run example:test:complex
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Open **Runs** → latest run → failures grouped by feature.
|
|
39
|
+
|
|
40
|
+
## 2. Complex backend runtime (runApi + capture + reportFeatureError)
|
|
41
|
+
|
|
42
|
+
**File:** `complex/api-services.demo.ts`
|
|
43
|
+
|
|
44
|
+
Demonstrates:
|
|
45
|
+
|
|
46
|
+
1. `initNzilaRuntime({ signalsUrl, apiKey })`
|
|
47
|
+
2. `traceNzilaFeature("Checkout")` with `runApi` for inventory + payment
|
|
48
|
+
3. `captureError` for handled business failure (409)
|
|
49
|
+
4. `captureApiError` for pipeline failure
|
|
50
|
+
5. Second feature `Inventory` with `reportFeatureError` in a job
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm run example:runtime:complex
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Wait ~1s for batched flush, then check **Feature health** / runtime signals for `Checkout` and `Inventory`.
|
|
57
|
+
|
|
58
|
+
## 3. Tracing-only Vitest (focused failure fingerprints)
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm run example:test:tracing:all
|
|
62
|
+
npm run example:webhook:tracing
|
|
63
|
+
npm run example:webhook:tracing:frontend
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## 4. React runtime (monorepo)
|
|
67
|
+
|
|
68
|
+
See [RUNTIME-TRACE.md](RUNTIME-TRACE.md). Pattern:
|
|
69
|
+
|
|
70
|
+
```tsx
|
|
71
|
+
initNzilaRuntime({ signalsUrl, apiKey });
|
|
72
|
+
|
|
73
|
+
function CheckoutPage() {
|
|
74
|
+
const nzila = useNzilaFeature("Checkout");
|
|
75
|
+
async function pay() {
|
|
76
|
+
try {
|
|
77
|
+
await nzila.runApi("checkout.submit", () =>
|
|
78
|
+
fetch("/api/checkout", { method: "POST" }),
|
|
79
|
+
);
|
|
80
|
+
} catch (e) {
|
|
81
|
+
nzila.captureError(e, { step: "submit" });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return (
|
|
85
|
+
<NzilaFeatureBoundary feature="Checkout" nzila={nzila}>
|
|
86
|
+
{/* children */}
|
|
87
|
+
</NzilaFeatureBoundary>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## 5. Jest and Mocha
|
|
93
|
+
|
|
94
|
+
- Jest: copy `jest.config.example.cjs` → wire `NzilaJestReporter` with same env vars as Vitest.
|
|
95
|
+
- Mocha: `mocharc.nzila.example.cjs` — register reporter, call `sendNzilaRun` on `end` if you roll your own.
|
|
96
|
+
|
|
97
|
+
## 6. End-to-end local checklist
|
|
98
|
+
|
|
99
|
+
1. `npm run dev` + `npm run worker`
|
|
100
|
+
2. Create app(s): `checkout-api`, `checkout-web` (or your `NZILA_COMPLEX_APP_NAME`)
|
|
101
|
+
3. API key in `.env.local`
|
|
102
|
+
4. `npm run example:test:complex` → webhook run
|
|
103
|
+
5. `npm run example:runtime:complex` → runtime signals
|
|
104
|
+
6. Dashboard: **Runs**, **Failures**, **Feature health** — same feature names
|
|
105
|
+
|
|
106
|
+
## Environment reference
|
|
107
|
+
|
|
108
|
+
| Variable | Used by |
|
|
109
|
+
|----------|---------|
|
|
110
|
+
| `NZILA_API_KEY` | Webhook + runtime |
|
|
111
|
+
| `NZILA_WEBHOOK_URL` | Vitest/Jest reporters, `sendNzilaRun` |
|
|
112
|
+
| `NZILA_SIGNALS_URL` | `initNzilaRuntime` (default `http://localhost:3000/api/signals/runtime`) |
|
|
113
|
+
| `NZILA_COMPLEX_APP_NAME` | Complex Vitest config only |
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# @nzila/sdk examples
|
|
2
|
+
|
|
3
|
+
These files ship inside the npm package under `node_modules/@nzila/sdk/examples/`.
|
|
4
|
+
|
|
5
|
+
| Topic | Files |
|
|
6
|
+
|-------|--------|
|
|
7
|
+
| Overview | [README from monorepo](./README.md) |
|
|
8
|
+
| All features walkthrough | [COMPLEX-USAGE.md](./COMPLEX-USAGE.md) |
|
|
9
|
+
| Live app tracing | [RUNTIME-TRACE.md](./RUNTIME-TRACE.md) |
|
|
10
|
+
| Test → dashboard mapping | [TRACING-FAILURES.md](./TRACING-FAILURES.md) |
|
|
11
|
+
| Vitest | [vitest.config.example.ts](./vitest.config.example.ts), [complex/](./complex/) |
|
|
12
|
+
| Jest | [jest.config.example.cjs](./jest.config.example.cjs) |
|
|
13
|
+
| Mocha | [mocharc.nzila.example.cjs](./mocharc.nzila.example.cjs) |
|
|
14
|
+
| Backend runtime | [backend/runtime-trace.example.ts](./backend/runtime-trace.example.ts), [complex/api-services.demo.ts](./complex/api-services.demo.ts) |
|
|
15
|
+
| React | [react/checkout-feature.example.tsx](./react/checkout-feature.example.tsx) |
|
|
16
|
+
| Manual webhook | [send-run-manual.mjs](./send-run-manual.mjs), [shared/send-webhook.mjs](./shared/send-webhook.mjs) |
|
|
17
|
+
| Sample payloads | [backend/sample-payload.json](./backend/sample-payload.json), [frontend/sample-payload.json](./frontend/sample-payload.json) |
|
|
18
|
+
|
|
19
|
+
## Environment
|
|
20
|
+
|
|
21
|
+
```env
|
|
22
|
+
NZILA_API_KEY=nzl_...
|
|
23
|
+
NZILA_WEBHOOK_URL=https://your-host/api/webhooks/tests
|
|
24
|
+
NZILA_SIGNALS_URL=https://your-host/api/signals/runtime
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Imports in these files use `@nzila/sdk` — the same as in your application after `npm install @nzila/sdk`.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
> **npm:** Start with [INDEX.md](./INDEX.md). Imports use `@nzila/sdk`.
|
|
2
|
+
|
|
3
|
+
# Nzila integration examples
|
|
4
|
+
|
|
5
|
+
End-to-end samples for **backend (API)** and **frontend (UI)** test flows.
|
|
6
|
+
|
|
7
|
+
## Prerequisites
|
|
8
|
+
|
|
9
|
+
1. `npm run dev` and `npm run worker` (with `QUEUE_DRIVER` and DB migrated).
|
|
10
|
+
2. Register, create an API key at `/en/keys`.
|
|
11
|
+
3. In **`.env.local`**:
|
|
12
|
+
|
|
13
|
+
```env
|
|
14
|
+
NZILA_API_KEY=nzl_...
|
|
15
|
+
NZILA_WEBHOOK_URL=http://localhost:3000/api/webhooks/tests
|
|
16
|
+
HF_API_TOKEN=hf_... # optional, for AI summaries
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The same examples are published inside **`@nzila/sdk`** at `node_modules/@nzila/sdk/examples/` (see `INDEX.md` there).
|
|
20
|
+
|
|
21
|
+
## Layout
|
|
22
|
+
|
|
23
|
+
| Path | Purpose |
|
|
24
|
+
|------|---------|
|
|
25
|
+
| `backend/` | API-style unit tests (pricing, inventory, auth) |
|
|
26
|
+
| `frontend/` | UI logic tests (checkout form, product card) |
|
|
27
|
+
| `shared/send-webhook.mjs` | POST static JSON payloads (CI / manual) |
|
|
28
|
+
| `vitest.config.ts` | Vitest + Nzila reporter (fires webhook on finish) |
|
|
29
|
+
| `jest.config.example.cjs` | Jest + NzilaJestReporter |
|
|
30
|
+
| `mocharc.nzila.example.cjs` | Mocha reporter setup notes |
|
|
31
|
+
| `@nzila/sdk` | Installed npm package (you are reading its `examples/` folder) |
|
|
32
|
+
|
|
33
|
+
Each suite includes **passing and failing** tests so the dashboard shows real failure analysis.
|
|
34
|
+
|
|
35
|
+
## Trace failures (tests → dashboard)
|
|
36
|
+
|
|
37
|
+
See **[TRACING-FAILURES.md](TRACING-FAILURES.md)** for how `describe()` paths, fingerprints, and `errors[]` map to **Runs**, **Failures**, and **Feature health**.
|
|
38
|
+
|
|
39
|
+
## Complex usage (all features)
|
|
40
|
+
|
|
41
|
+
See **[COMPLEX-USAGE.md](COMPLEX-USAGE.md)** for a single walkthrough: multi-feature Vitest, `mapFeature`, runtime `runApi` / `captureError`, Jest/Mocha, and env vars.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
npm run example:test:complex # Vitest: Checkout UI + Inventory + Auth (pass/fail)
|
|
45
|
+
npm run example:runtime:complex # Backend: traceNzilaFeature + reportFeatureError
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Runtime trace (live app → Feature health)
|
|
49
|
+
|
|
50
|
+
See **[RUNTIME-TRACE.md](RUNTIME-TRACE.md)** for `useNzilaFeature` (React), `traceNzilaFeature` (backend), and **`captureError` / `reportFeatureError`** when you handle errors yourself.
|
|
51
|
+
|
|
52
|
+
Example backend handler: `backend/runtime-trace.example.ts`. Heavier demo: `complex/api-services.demo.ts`.
|
|
53
|
+
|
|
54
|
+
Quick demo (Vitest only):
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
npm run example:test:tracing # Vitest → webhook
|
|
58
|
+
npm run example:webhook:tracing # static JSON payload
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Run Vitest (sends one webhook per run)
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
# All examples (mixed app name: nzila-demo)
|
|
65
|
+
npm run example:test
|
|
66
|
+
|
|
67
|
+
# Backend only → app name checkout-api
|
|
68
|
+
npm run example:test:backend
|
|
69
|
+
|
|
70
|
+
# Frontend only → app name checkout-web
|
|
71
|
+
npm run example:test:frontend
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Send sample payloads (no Vitest)
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
npm run example:webhook:backend
|
|
78
|
+
npm run example:webhook:frontend
|
|
79
|
+
npm run example:webhook # both
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Full local demo
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm run example:all # vitest + both webhooks (needs dev + worker up)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Server-side client
|
|
89
|
+
|
|
90
|
+
Use `frontend-webhook-client.ts` from a Route Handler or CI job — not from browser bundles.
|
|
91
|
+
|
|
92
|
+
## Payload contract
|
|
93
|
+
|
|
94
|
+
See `backend/sample-payload.json`, `frontend/sample-payload.json`, and `src/lib/webhook-schema.ts`.
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# Runtime feature tracing (`traceNzilaFeature` / `useNzilaFeature`)
|
|
2
|
+
|
|
3
|
+
Pair **Vitest** failures (webhook) with **live** signals on the same **feature** name (e.g. `Checkout`). Call one function at the top of the UI component or backend handler that owns that feature; use **`captureError`** when you already handle errors.
|
|
4
|
+
|
|
5
|
+
## SDK package exports
|
|
6
|
+
|
|
7
|
+
| Import path | Use for |
|
|
8
|
+
|-------------|---------|
|
|
9
|
+
| `@nzila/sdk` | `initNzilaRuntime`, `traceNzilaFeature`, `reportFeatureError`, Vitest/Jest reporters |
|
|
10
|
+
| `@nzila/sdk/react` | `useNzilaFeature`, `NzilaFeatureBoundary`, `reportFeatureError` |
|
|
11
|
+
| `@nzila/sdk/runtime` | Low-level runtime client only |
|
|
12
|
+
| `@nzila/sdk/vitest` | `NzilaVitestReporter` |
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Setup (once per app)
|
|
17
|
+
|
|
18
|
+
```ts
|
|
19
|
+
import { initNzilaRuntime } from "@nzila/sdk/runtime";
|
|
20
|
+
// or: import { initNzilaRuntime } from "@nzila/sdk";
|
|
21
|
+
|
|
22
|
+
initNzilaRuntime({
|
|
23
|
+
signalsUrl: process.env.NZILA_SIGNALS_URL!, // https://your-app.vercel.app/api/signals/runtime
|
|
24
|
+
apiKey: process.env.NZILA_API_KEY!,
|
|
25
|
+
locale: "en",
|
|
26
|
+
renderLoopThreshold: 40, // optional — React render-loop detection
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
| Variable | Where |
|
|
31
|
+
|----------|--------|
|
|
32
|
+
| `NZILA_SIGNALS_URL` | Node / Route Handlers |
|
|
33
|
+
| `NEXT_PUBLIC_NZILA_SIGNALS_URL` | Browser (Next.js) |
|
|
34
|
+
| `NZILA_API_KEY` / `NEXT_PUBLIC_NZILA_API_KEY` | Same project key as test webhooks |
|
|
35
|
+
|
|
36
|
+
Requires DB migration `0003_feature_intelligence.sql` (`npm run db:migrate`).
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## API reference
|
|
41
|
+
|
|
42
|
+
| Function | Signature | When to use |
|
|
43
|
+
|----------|-----------|-------------|
|
|
44
|
+
| `initNzilaRuntime` | `(config)` once | Before any trace calls |
|
|
45
|
+
| `useNzilaFeature` | `(feature, options?)` | **Top of React component** |
|
|
46
|
+
| `traceNzilaFeature` | `(feature)` | **Top of backend handler** |
|
|
47
|
+
| `handle.captureError` | `(error, detail?)` | Your `try/catch`, toasts, form errors |
|
|
48
|
+
| `handle.captureApiError` | `(error, label, detail?)` | Caught fetch/HTTP without `runApi` |
|
|
49
|
+
| `handle.runApi` | `(label, fn)` | Wrap async API calls |
|
|
50
|
+
| `handle.reportError` | same as `captureError` | Alias |
|
|
51
|
+
| `reportFeatureError` | `(feature, error, detail?)` | Shared util; pass feature + error |
|
|
52
|
+
| `captureFeatureError` | alias of `reportFeatureError` | |
|
|
53
|
+
| `extractErrorFields` | `(error)` | `message`, `name`, `stack` normalization |
|
|
54
|
+
|
|
55
|
+
Returned **handle** (from hook or `traceNzilaFeature`): `{ feature, runApi, captureError, captureApiError, reportError, reportUi }`.
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## React — `useNzilaFeature` at the top of the component
|
|
60
|
+
|
|
61
|
+
```tsx
|
|
62
|
+
"use client";
|
|
63
|
+
|
|
64
|
+
import { useNzilaFeature } from "@nzila/sdk/react";
|
|
65
|
+
import { initNzilaRuntime } from "@nzila/sdk/react";
|
|
66
|
+
|
|
67
|
+
// e.g. app/layout.tsx or instrumentation.ts (once)
|
|
68
|
+
initNzilaRuntime({
|
|
69
|
+
signalsUrl: process.env.NEXT_PUBLIC_NZILA_SIGNALS_URL!,
|
|
70
|
+
apiKey: process.env.NEXT_PUBLIC_NZILA_API_KEY!,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export function CheckoutPage() {
|
|
74
|
+
const nzila = useNzilaFeature("Checkout"); // same feature as Vitest mapFeature
|
|
75
|
+
|
|
76
|
+
async function pay() {
|
|
77
|
+
await nzila.runApi("POST /api/checkout", () =>
|
|
78
|
+
fetch("/api/checkout", { method: "POST", body: "{}" }),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return <button onClick={() => void pay()}>Pay</button>;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Manual error handling (your `try/catch`)
|
|
87
|
+
|
|
88
|
+
Use the handle from the hook (feature is already bound):
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
export function CheckoutPage() {
|
|
92
|
+
const nzila = useNzilaFeature("Checkout");
|
|
93
|
+
|
|
94
|
+
async function pay() {
|
|
95
|
+
try {
|
|
96
|
+
await nzila.runApi("POST /api/checkout", () =>
|
|
97
|
+
fetch("/api/checkout", { method: "POST" }),
|
|
98
|
+
);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
nzila.captureError(err, { step: "pay", handled: true });
|
|
101
|
+
// your toast / form error state
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Or pass **feature + error** from anywhere (e.g. shared error util):
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { reportFeatureError } from "@nzila/sdk/react";
|
|
111
|
+
|
|
112
|
+
reportFeatureError("Checkout", error, { step: "validation" });
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`captureError` uses `extractErrorFields` — works with `Error`, strings, and unknown values.
|
|
116
|
+
|
|
117
|
+
### What is reported automatically
|
|
118
|
+
|
|
119
|
+
| Issue | Signal `type` | `payload` notes |
|
|
120
|
+
|-------|---------------|-----------------|
|
|
121
|
+
| Too many re-renders in ~1s | `ui` | `kind: "render_loop"`, `renderCount` |
|
|
122
|
+
| `window.onerror` | `error` | while component mounted |
|
|
123
|
+
| Unhandled promise rejection | `error` | while mounted |
|
|
124
|
+
| `runApi` failure | `api` | `success: false`, message |
|
|
125
|
+
| `runApi` success | `api` | `success: true`, `label` |
|
|
126
|
+
|
|
127
|
+
Optional error boundary:
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import { NzilaFeatureBoundary } from "@nzila/sdk/react";
|
|
131
|
+
|
|
132
|
+
<NzilaFeatureBoundary feature="Checkout" nzila={nzila}>
|
|
133
|
+
<CheckoutForm />
|
|
134
|
+
</NzilaFeatureBoundary>
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Backend — `traceNzilaFeature` at the top of the handler
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
import { initNzilaRuntime, traceNzilaFeature } from "@nzila/sdk";
|
|
143
|
+
|
|
144
|
+
initNzilaRuntime({
|
|
145
|
+
signalsUrl: process.env.NZILA_SIGNALS_URL!,
|
|
146
|
+
apiKey: process.env.NZILA_API_KEY!,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
export async function postCheckout(req: Request) {
|
|
150
|
+
const nzila = traceNzilaFeature("Checkout");
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const upstream = await nzila.runApi("payment-gateway", () =>
|
|
154
|
+
fetch("https://api.example.com/charge", { method: "POST" }),
|
|
155
|
+
);
|
|
156
|
+
if (!upstream.ok) throw new Error(`charge failed: ${upstream.status}`);
|
|
157
|
+
return Response.json({ ok: true });
|
|
158
|
+
} catch (err) {
|
|
159
|
+
nzila.captureError(err, { route: "POST /checkout", handled: true });
|
|
160
|
+
return Response.json({ error: "payment_failed" }, { status: 502 });
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
Caught an API error without `runApi`?
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
nzila.captureApiError(err, "payment-gateway", { status: 502 });
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
From a nested helper without the handle:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import { reportFeatureError } from "@nzila/sdk";
|
|
175
|
+
|
|
176
|
+
reportFeatureError("Checkout", err, { layer: "inventory-service" });
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Call **`traceNzilaFeature("Checkout")` as the first line** of the route/service that matches your tested feature.
|
|
180
|
+
|
|
181
|
+
Repo example: [backend/runtime-trace.example.ts](./backend/runtime-trace.example.ts).
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Dashboard
|
|
186
|
+
|
|
187
|
+
Open **Feature health** for the project — compare **tests** (from webhooks) with **runtime** (`api` / `ui` / `error` signals).
|
|
188
|
+
|
|
189
|
+
Use the **same feature string** everywhere:
|
|
190
|
+
|
|
191
|
+
| Source | Example |
|
|
192
|
+
|--------|---------|
|
|
193
|
+
| Vitest `describe` / `mapFeature` | `"Checkout"` |
|
|
194
|
+
| `useNzilaFeature("…")` | `"Checkout"` |
|
|
195
|
+
| `traceNzilaFeature("…")` | `"Checkout"` |
|
|
196
|
+
| `reportFeatureError("…", err)` | `"Checkout"` |
|
|
197
|
+
| Manual HTTP (advanced) | `POST /api/signals/runtime` |
|
|
198
|
+
|
|
199
|
+
Test-side tracing: [TRACING-FAILURES.md](./TRACING-FAILURES.md).
|
|
200
|
+
Deploy (Vercel + Render): [../docs/DEPLOY-VERCEL-RENDER.md](../docs/DEPLOY-VERCEL-RENDER.md).
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## Manual HTTP (optional)
|
|
205
|
+
|
|
206
|
+
Same payload the SDK sends:
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"locale": "en",
|
|
211
|
+
"signals": [
|
|
212
|
+
{
|
|
213
|
+
"feature": "Checkout",
|
|
214
|
+
"type": "error",
|
|
215
|
+
"success": false,
|
|
216
|
+
"payload": { "message": "card declined", "handled": true }
|
|
217
|
+
}
|
|
218
|
+
]
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Types: `navigation`, `api`, `ui`, `error`, `custom`.
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Tracing test failures in Nzila
|
|
2
|
+
|
|
3
|
+
How failures move from **backend** and **frontend** test runs into the dashboard, how to **attach tests to a feature**, and how to trace an error even when the test was already in your suite.
|
|
4
|
+
|
|
5
|
+
## Mental model
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
describe() tree → describePath + fingerprint
|
|
9
|
+
mapFeature / feature field → dashboard "Feature" column
|
|
10
|
+
test name → human label
|
|
11
|
+
errors[] → processed into Failure analysis (not raw stacks in UI)
|
|
12
|
+
worker → summaries + feature health
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
1. Vitest finishes one run → reporter sends **one** webhook (`runId` is idempotent).
|
|
16
|
+
2. Each test becomes a row with `describePath`, `feature`, `testName`, `status`, `errors`.
|
|
17
|
+
3. The worker enriches failures and updates **Stability**, **Failures**, and **Feature health**.
|
|
18
|
+
|
|
19
|
+
## Developer setup (one-time)
|
|
20
|
+
|
|
21
|
+
### 1. Run Nzila locally
|
|
22
|
+
|
|
23
|
+
From the repo root (see [README](../README.md)):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm install
|
|
27
|
+
cp .env.example .env.local # fill DATABASE_URL, AUTH_SECRET, etc.
|
|
28
|
+
npm run db:migrate
|
|
29
|
+
npm run dev # http://localhost:3000
|
|
30
|
+
npm run worker # second terminal — processes webhooks
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Create two projects (API + web)
|
|
34
|
+
|
|
35
|
+
In the dashboard, create **two** projects so backend and frontend keys do not mix:
|
|
36
|
+
|
|
37
|
+
| Project display name | **App name** (must match Vitest `appName`) | Example tests |
|
|
38
|
+
|----------------------|------------------------------------------|---------------|
|
|
39
|
+
| Checkout API | `checkout-api` | `examples/backend/` |
|
|
40
|
+
| Checkout Web | `checkout-web` | `examples/frontend/` |
|
|
41
|
+
|
|
42
|
+
For each project: **Settings → API keys** → create a key. You can use one key for both demos if both projects belong to your org and you switch `NZILA_API_KEY` when running each suite — separate keys are clearer.
|
|
43
|
+
|
|
44
|
+
### 3. Configure `.env.local`
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
NZILA_WEBHOOK_URL=http://localhost:3000/api/webhooks/tests
|
|
48
|
+
NZILA_API_KEY=nzl_your_key_for_checkout_api
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
For frontend tracing, use the **checkout-web** key (or change the key before `example:test:tracing:frontend`).
|
|
52
|
+
|
|
53
|
+
### 4. Run tracing demos
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
npm run example:test:tracing:backend # failures → checkout-api
|
|
57
|
+
npm run example:test:tracing:frontend # failures → checkout-web
|
|
58
|
+
npm run example:test:tracing:all # both
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Tracing-only Vitest configs (reporter + `mapFeature`):
|
|
62
|
+
|
|
63
|
+
- `examples/vitest.tracing.backend.config.ts`
|
|
64
|
+
- `examples/vitest.tracing.frontend.config.ts`
|
|
65
|
+
|
|
66
|
+
## Attach tests to a feature
|
|
67
|
+
|
|
68
|
+
Nzila groups failures and health by **feature** string. You have three ways to set it:
|
|
69
|
+
|
|
70
|
+
### A. Top-level `describe` name (default)
|
|
71
|
+
|
|
72
|
+
The Vitest reporter uses the **first** `describe()` segment as `feature` unless you override with `mapFeature`.
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
describe("Checkout", () => {
|
|
76
|
+
it("should reject expired cards", () => { /* ... */ });
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
→ `feature: "Checkout"`, fingerprint includes full `describePath`.
|
|
81
|
+
|
|
82
|
+
### B. `mapFeature` in the reporter (recommended for UI vs product names)
|
|
83
|
+
|
|
84
|
+
When suite titles are long (e.g. `"Checkout UI"`) but the product feature is `"Checkout"`, map them in config:
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
// examples/shared/feature-map.ts
|
|
88
|
+
export function resolveFrontendFeature(describePath: string[], _testName: string) {
|
|
89
|
+
if (describePath[0] === "Checkout UI") return "Checkout";
|
|
90
|
+
return describePath[0] ?? "general";
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Wire it in `NzilaVitestReporter({ mapFeature: resolveFrontendFeature, ... })`.
|
|
95
|
+
|
|
96
|
+
### C. Explicit `feature` in webhook JSON
|
|
97
|
+
|
|
98
|
+
For manual/CI payloads, set `feature` on each test (see `examples/backend/sample-tracing-payload.json` and `examples/frontend/sample-tracing-payload.json`):
|
|
99
|
+
|
|
100
|
+
```json
|
|
101
|
+
{
|
|
102
|
+
"describePath": ["Checkout UI", "email field"],
|
|
103
|
+
"feature": "Checkout",
|
|
104
|
+
"testName": "should reject malformed email addresses",
|
|
105
|
+
"status": "failed",
|
|
106
|
+
"errors": [{ "message": "...", "stack": "..." }]
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Server fallback: `feature ?? describePath[0] ?? "general"` (`ensureTestCase`).
|
|
111
|
+
|
|
112
|
+
**Runtime signals** should use the **same** feature string so **Feature health** lines up tests and production.
|
|
113
|
+
|
|
114
|
+
**Recommended (live app):** use the SDK so production matches tests:
|
|
115
|
+
|
|
116
|
+
| Layer | Call at top of unit | Manual `catch` |
|
|
117
|
+
|-------|---------------------|----------------|
|
|
118
|
+
| React | `const nzila = useNzilaFeature("Checkout")` | `nzila.captureError(err, { … })` |
|
|
119
|
+
| Backend | `const nzila = traceNzilaFeature("Checkout")` | `nzila.captureError(err, { … })` |
|
|
120
|
+
| Either | — | `reportFeatureError("Checkout", err, { … })` |
|
|
121
|
+
|
|
122
|
+
Auto-reported: render loops (`ui`), `runApi` failures (`api`), unhandled window errors. Full API: **[RUNTIME-TRACE.md](./RUNTIME-TRACE.md)**.
|
|
123
|
+
|
|
124
|
+
## Example suites
|
|
125
|
+
|
|
126
|
+
| Layer | File | App name | Script |
|
|
127
|
+
|-------|------|----------|--------|
|
|
128
|
+
| Backend | `examples/backend/error-tracing.test.ts` | `checkout-api` | `npm run example:test:tracing:backend` |
|
|
129
|
+
| Frontend | `examples/frontend/error-tracing.test.ts` | `checkout-web` | `npm run example:test:tracing:frontend` |
|
|
130
|
+
|
|
131
|
+
Failures are intentional so you can practice tracing without breaking production code paths.
|
|
132
|
+
|
|
133
|
+
## Trace a failure after tests ran (even with tests in CI)
|
|
134
|
+
|
|
135
|
+
Tests passing or failing does not block tracing: each **run** is stored with per-test `errors`. When a test **fails** (or starts failing in a new run):
|
|
136
|
+
|
|
137
|
+
1. **Test runs** (`/en/runs`) — open the latest run for `checkout-api` or `checkout-web`.
|
|
138
|
+
2. **Failure analysis** (`/en/failures`) — read processed summaries (from `errors[].message`, not raw stacks).
|
|
139
|
+
3. **Feature insights** / **Feature health** — filter by the same `feature` you attached in the suite or payload.
|
|
140
|
+
4. **Code** — match **fingerprint**: `describePath[0] › … › testName` (same `describe` + `it` names as in repo).
|
|
141
|
+
|
|
142
|
+
Optional: send runtime signals for the same feature and compare on **Feature health**.
|
|
143
|
+
|
|
144
|
+
## Manual webhook (no Vitest)
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npm run example:webhook:tracing # backend sample
|
|
148
|
+
npm run example:webhook:tracing:frontend # frontend sample
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Requires `NZILA_API_KEY` and `NZILA_WEBHOOK_URL` in `.env.local` (script loads env via `send-webhook.mjs`).
|
|
152
|
+
|
|
153
|
+
## Webhook error fields
|
|
154
|
+
|
|
155
|
+
Per test in `tests[]`:
|
|
156
|
+
|
|
157
|
+
```json
|
|
158
|
+
{
|
|
159
|
+
"describePath": ["Checkout", "Payment"],
|
|
160
|
+
"feature": "Checkout",
|
|
161
|
+
"testName": "should reject expired cards",
|
|
162
|
+
"status": "failed",
|
|
163
|
+
"duration": 42,
|
|
164
|
+
"errors": [
|
|
165
|
+
{ "message": "expected false to be true", "stack": "AssertionError: ..." }
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
- **message** — drives operator-facing copy after processing.
|
|
171
|
+
- **stack** — optional; used server-side, not shown as-is on **Failures**.
|
|
172
|
+
|
|
173
|
+
## Related files
|
|
174
|
+
|
|
175
|
+
| File | Role |
|
|
176
|
+
|------|------|
|
|
177
|
+
| `examples/shared/feature-map.ts` | Backend/frontend feature mapping |
|
|
178
|
+
| `@nzila/sdk/vitest-reporter.ts` | Vitest → webhook + `mapFeature` |
|
|
179
|
+
| `src/lib/webhook-schema.ts` | Payload validation |
|
|
180
|
+
| `src/server/test-cases.ts` | `feature` resolution |
|
|
181
|
+
| `src/server/process-run.ts` | Worker enrichment |
|
|
182
|
+
| `examples/TRACING-FAILURES.md` | This guide (tests) |
|
|
183
|
+
| `examples/RUNTIME-TRACE.md` | Live `useNzilaFeature`, `captureError` |
|
|
184
|
+
| `examples/backend/runtime-trace.example.ts` | Backend handler sample |
|
|
185
|
+
| `@nzila/sdk/runtime/` | Runtime client |
|
|
186
|
+
| `@nzila/sdk/react/` | React hook + boundary |
|