@nzila/sdk 0.1.0 → 0.1.3

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 (38) hide show
  1. package/README.md +27 -1
  2. package/dist/cli/constants.d.ts +13 -0
  3. package/dist/cli/constants.d.ts.map +1 -0
  4. package/dist/cli/constants.js +20 -0
  5. package/dist/cli/main.d.ts +3 -0
  6. package/dist/cli/main.d.ts.map +1 -0
  7. package/dist/cli/main.js +223 -0
  8. package/dist/cli/open-browser.d.ts +2 -0
  9. package/dist/cli/open-browser.d.ts.map +1 -0
  10. package/dist/cli/open-browser.js +26 -0
  11. package/examples/COMPLEX-USAGE.md +113 -0
  12. package/examples/INDEX.md +27 -0
  13. package/examples/README.md +94 -0
  14. package/examples/RUNTIME-TRACE.md +222 -0
  15. package/examples/TRACING-FAILURES.md +186 -0
  16. package/examples/backend/error-tracing.test.ts +44 -0
  17. package/examples/backend/order-pricing.test.ts +54 -0
  18. package/examples/backend/order-pricing.ts +27 -0
  19. package/examples/backend/runtime-trace.example.ts +31 -0
  20. package/examples/backend/sample-payload.json +62 -0
  21. package/examples/backend/sample-tracing-payload.json +90 -0
  22. package/examples/complex/api-services.demo.ts +72 -0
  23. package/examples/complex/full-platform.demo.test.ts +46 -0
  24. package/examples/frontend/checkout-form.test.ts +54 -0
  25. package/examples/frontend/checkout-form.ts +29 -0
  26. package/examples/frontend/error-tracing.test.ts +59 -0
  27. package/examples/frontend/sample-payload.json +62 -0
  28. package/examples/frontend/sample-tracing-payload.json +73 -0
  29. package/examples/frontend-webhook-client.ts +32 -0
  30. package/examples/jest.config.example.cjs +18 -0
  31. package/examples/mocharc.nzila.example.cjs +21 -0
  32. package/examples/react/checkout-feature.example.tsx +41 -0
  33. package/examples/send-run-manual.mjs +16 -0
  34. package/examples/shared/feature-map.ts +24 -0
  35. package/examples/shared/load-env.mjs +24 -0
  36. package/examples/shared/send-webhook.mjs +43 -0
  37. package/examples/vitest.config.example.ts +20 -0
  38. package/package.json +9 -4
@@ -0,0 +1,90 @@
1
+ {
2
+ "runId": "tracing-demo-manual-001",
3
+ "appName": "checkout-api",
4
+ "framework": "vitest",
5
+ "startedAt": "2026-05-16T16:00:00.000Z",
6
+ "finishedAt": "2026-05-16T16:00:05.000Z",
7
+ "locale": "en",
8
+ "tests": [
9
+ {
10
+ "appName": "checkout-api",
11
+ "framework": "vitest",
12
+ "startedAt": "2026-05-16T16:00:00.000Z",
13
+ "finishedAt": "2026-05-16T16:00:01.000Z",
14
+ "describePath": ["Checkout", "Payment"],
15
+ "feature": "Checkout",
16
+ "testName": "should accept valid card payloads",
17
+ "status": "passed",
18
+ "duration": 10,
19
+ "errors": []
20
+ },
21
+ {
22
+ "appName": "checkout-api",
23
+ "framework": "vitest",
24
+ "startedAt": "2026-05-16T16:00:01.000Z",
25
+ "finishedAt": "2026-05-16T16:00:02.200Z",
26
+ "describePath": ["Checkout", "Payment"],
27
+ "feature": "Checkout",
28
+ "testName": "should reject expired cards",
29
+ "status": "failed",
30
+ "duration": 45,
31
+ "errors": [
32
+ {
33
+ "message": "expected false to be true — card marked valid but expiry check failed",
34
+ "stack": "AssertionError: expected false to be true\n at examples/backend/error-tracing.test.ts:18:26"
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "appName": "checkout-api",
40
+ "framework": "vitest",
41
+ "startedAt": "2026-05-16T16:00:02.200Z",
42
+ "finishedAt": "2026-05-16T16:00:03.000Z",
43
+ "describePath": ["Checkout", "Coupons"],
44
+ "feature": "Checkout",
45
+ "testName": "should apply percentage discounts",
46
+ "status": "failed",
47
+ "duration": 30,
48
+ "errors": [
49
+ {
50
+ "message": "expected 90 to be 80 — coupon math mismatch",
51
+ "stack": "AssertionError: expected 90 to be 80\n at examples/backend/error-tracing.test.ts:26:25"
52
+ }
53
+ ]
54
+ },
55
+ {
56
+ "appName": "checkout-api",
57
+ "framework": "vitest",
58
+ "startedAt": "2026-05-16T16:00:03.000Z",
59
+ "finishedAt": "2026-05-16T16:00:04.000Z",
60
+ "describePath": ["Inventory"],
61
+ "feature": "Inventory",
62
+ "testName": "should block checkout when stock is zero",
63
+ "status": "failed",
64
+ "duration": 22,
65
+ "errors": [
66
+ {
67
+ "message": "expected false to be true — checkout allowed with zero stock",
68
+ "stack": "AssertionError: expected false to be true\n at examples/backend/error-tracing.test.ts:35:24"
69
+ }
70
+ ]
71
+ },
72
+ {
73
+ "appName": "checkout-api",
74
+ "framework": "vitest",
75
+ "startedAt": "2026-05-16T16:00:04.000Z",
76
+ "finishedAt": "2026-05-16T16:00:05.000Z",
77
+ "describePath": ["Auth API"],
78
+ "feature": "Auth API",
79
+ "testName": "should rate-limit repeated login failures",
80
+ "status": "failed",
81
+ "duration": 18,
82
+ "errors": [
83
+ {
84
+ "message": "expected false to be true — account should lock after threshold",
85
+ "stack": "AssertionError: expected false to be true\n at examples/backend/error-tracing.test.ts:43:19"
86
+ }
87
+ ]
88
+ }
89
+ ]
90
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Complex backend example: multiple features, runApi, captureError, reportFeatureError.
3
+ *
4
+ * Run with: npx tsx examples/complex/api-services.demo.ts
5
+ * Env: NZILA_SIGNALS_URL, NZILA_API_KEY
6
+ */
7
+ import { config } from "dotenv";
8
+ import { resolve } from "node:path";
9
+ import {
10
+ initNzilaRuntime,
11
+ reportFeatureError,
12
+ traceNzilaFeature,
13
+ } from "@nzila/sdk/runtime";
14
+
15
+ config({ path: resolve(process.cwd(), ".env.local") });
16
+
17
+ initNzilaRuntime({
18
+ signalsUrl:
19
+ process.env.NZILA_SIGNALS_URL ?? "http://localhost:3000/api/signals/runtime",
20
+ apiKey: process.env.NZILA_API_KEY ?? "",
21
+ locale: "en",
22
+ });
23
+
24
+ async function fakePaymentGateway(amount: number): Promise<{ ok: boolean }> {
25
+ if (amount > 10_000) throw new Error("limit_exceeded");
26
+ return { ok: true };
27
+ }
28
+
29
+ export async function checkoutHandler(body: { amount: number; sku: string }) {
30
+ const nzila = traceNzilaFeature("Checkout");
31
+
32
+ try {
33
+ const inventory = await nzila.runApi("inventory.reserve", async () => {
34
+ if (body.sku === "OUT_OF_STOCK") throw new Error("no_stock");
35
+ return { reserved: true };
36
+ });
37
+
38
+ if (!inventory.reserved) {
39
+ const err = new Error("reservation_failed");
40
+ nzila.captureError(err, { sku: body.sku, handled: true });
41
+ return { status: 409 as const, body: { error: "out_of_stock" } };
42
+ }
43
+
44
+ await nzila.runApi("payment.charge", () => fakePaymentGateway(body.amount));
45
+ return { status: 200 as const, body: { ok: true } };
46
+ } catch (error) {
47
+ nzila.captureApiError(error, "checkout.pipeline", { sku: body.sku });
48
+ return { status: 502 as const, body: { error: "checkout_failed" } };
49
+ }
50
+ }
51
+
52
+ export async function inventorySyncJob() {
53
+ const nzila = traceNzilaFeature("Inventory");
54
+ try {
55
+ await nzila.runApi("warehouse.pull", async () => {
56
+ throw new Error("warehouse_timeout");
57
+ });
58
+ } catch (error) {
59
+ reportFeatureError("Inventory", error, { job: "nightly-sync", handled: true });
60
+ }
61
+ }
62
+
63
+ async function main() {
64
+ console.log("checkout 200:", await checkoutHandler({ amount: 50, sku: "SKU-1" }));
65
+ console.log("checkout 409:", await checkoutHandler({ amount: 50, sku: "OUT_OF_STOCK" }));
66
+ console.log("inventory job:");
67
+ await inventorySyncJob();
68
+ console.log("Signals queued — wait ~1s for flush.");
69
+ await new Promise((r) => setTimeout(r, 1500));
70
+ }
71
+
72
+ void main();
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Complex Vitest demo: multiple features, mapFeature, passes and failures.
3
+ * Sends one webhook when the file finishes (NzilaVitestReporter).
4
+ *
5
+ * npm run example:test:complex
6
+ */
7
+ import { describe, expect, it } from "vitest";
8
+
9
+ describe("Checkout UI", () => {
10
+ it("shows pay button when cart has items", () => {
11
+ expect([{ sku: "a" }].length).toBeGreaterThan(0);
12
+ });
13
+
14
+ it("validates card expiry format", () => {
15
+ const expiry = "13/99";
16
+ expect(/^\d{2}\/\d{2}$/.test(expiry)).toBe(true);
17
+ });
18
+
19
+ it("surfaces gateway error when charge fails", () => {
20
+ const gateway = { status: 502, message: "upstream unavailable" };
21
+ expect(gateway.status).toBe(200);
22
+ });
23
+ });
24
+
25
+ describe("Inventory", () => {
26
+ it("computes available quantity", () => {
27
+ expect(10 - 3).toBe(7);
28
+ });
29
+
30
+ it("flags oversell when reserved exceeds on-hand", () => {
31
+ const onHand = 2;
32
+ const reserved = 5;
33
+ expect(reserved).toBeLessThanOrEqual(onHand);
34
+ });
35
+ });
36
+
37
+ describe("Authentication", () => {
38
+ it("accepts valid session token shape", () => {
39
+ expect("sess_abc".startsWith("sess_")).toBe(true);
40
+ });
41
+
42
+ it("rejects expired session", () => {
43
+ const expired = true;
44
+ expect(expired).toBe(false);
45
+ });
46
+ });
@@ -0,0 +1,54 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ canSubmitCheckout,
4
+ formatCardDisplay,
5
+ isValidEmail,
6
+ shippingLabel,
7
+ } from "./checkout-form";
8
+
9
+ describe("Checkout UI", () => {
10
+ describe("email field", () => {
11
+ it("should accept a valid customer email", () => {
12
+ expect(isValidEmail("buyer@example.com")).toBe(true);
13
+ });
14
+
15
+ it("should reject malformed email addresses", () => {
16
+ expect(isValidEmail("not-an-email")).toBe(false);
17
+ });
18
+ });
19
+
20
+ describe("payment display", () => {
21
+ it("should mask card number showing last four digits", () => {
22
+ expect(formatCardDisplay("4242")).toBe("•••• •••• •••• 4242");
23
+ });
24
+
25
+ it("should block submit when terms are not accepted", () => {
26
+ expect(
27
+ canSubmitCheckout({
28
+ email: "a@b.co",
29
+ cardLast4: "1234",
30
+ country: "US",
31
+ acceptTerms: false,
32
+ }),
33
+ ).toBe(false);
34
+ });
35
+ });
36
+
37
+ describe("shipping banner", () => {
38
+ it("should show free shipping over threshold for US", () => {
39
+ expect(shippingLabel("US", 6000)).toBe("Free shipping");
40
+ });
41
+ });
42
+ });
43
+
44
+ describe("Product card component", () => {
45
+ it("should render sale badge when discount is active", () => {
46
+ const discountPct = 20;
47
+ expect(discountPct >= 15).toBe(true);
48
+ });
49
+
50
+ it("should hide add-to-cart when out of stock", () => {
51
+ const inStock = 0;
52
+ expect(inStock > 0).toBe(true);
53
+ });
54
+ });
@@ -0,0 +1,29 @@
1
+ export type CheckoutForm = {
2
+ email: string;
3
+ cardLast4: string;
4
+ country: string;
5
+ acceptTerms: boolean;
6
+ };
7
+
8
+ export function isValidEmail(email: string): boolean {
9
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.trim());
10
+ }
11
+
12
+ export function formatCardDisplay(last4: string): string {
13
+ const digits = last4.replace(/\D/g, "");
14
+ if (digits.length !== 4) return "•••• •••• •••• ••••";
15
+ return `•••• •••• •••• ${digits}`;
16
+ }
17
+
18
+ export function canSubmitCheckout(form: CheckoutForm): boolean {
19
+ if (!form.acceptTerms) return false;
20
+ if (!isValidEmail(form.email)) return false;
21
+ if (form.country.length !== 2) return false;
22
+ return form.cardLast4.replace(/\D/g, "").length === 4;
23
+ }
24
+
25
+ export function shippingLabel(country: string, subtotalCents: number): string {
26
+ if (country === "US" && subtotalCents >= 5000) return "Free shipping";
27
+ if (country === "US") return "Standard (3–5 days)";
28
+ return "International";
29
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Frontend tracing demo — failures attached to features via mapFeature in
3
+ * examples/vitest.tracing.frontend.config.ts (Checkout UI → feature "Checkout").
4
+ *
5
+ * Run: npm run example:test:tracing:frontend
6
+ */
7
+ import { describe, it, expect } from "vitest";
8
+ import {
9
+ canSubmitCheckout,
10
+ formatCardDisplay,
11
+ isValidEmail,
12
+ shippingLabel,
13
+ } from "./checkout-form";
14
+
15
+ describe("Checkout UI", () => {
16
+ describe("email field", () => {
17
+ it("should accept a valid customer email", () => {
18
+ expect(isValidEmail("buyer@example.com")).toBe(true);
19
+ });
20
+
21
+ it("should reject malformed email addresses", () => {
22
+ expect(isValidEmail("not-an-email")).toBe(true);
23
+ });
24
+ });
25
+
26
+ describe("payment display", () => {
27
+ it("should mask card number showing last four digits", () => {
28
+ expect(formatCardDisplay("4242")).toBe("•••• •••• •••• 9999");
29
+ });
30
+
31
+ it("should block submit when terms are not accepted", () => {
32
+ expect(
33
+ canSubmitCheckout({
34
+ email: "a@b.co",
35
+ cardLast4: "1234",
36
+ country: "US",
37
+ acceptTerms: false,
38
+ }),
39
+ ).toBe(true);
40
+ });
41
+ });
42
+
43
+ describe("shipping banner", () => {
44
+ it("should show free shipping over threshold for US", () => {
45
+ expect(shippingLabel("US", 6000)).toBe("Paid shipping");
46
+ });
47
+ });
48
+ });
49
+
50
+ describe("Product card component", () => {
51
+ it("should render sale badge when discount is active", () => {
52
+ expect(20 >= 15).toBe(false);
53
+ });
54
+
55
+ it("should hide add-to-cart when out of stock", () => {
56
+ const inStock = 0;
57
+ expect(inStock > 0).toBe(true);
58
+ });
59
+ });
@@ -0,0 +1,62 @@
1
+ {
2
+ "runId": "frontend-run-placeholder",
3
+ "appName": "checkout-web",
4
+ "framework": "vitest",
5
+ "startedAt": "2026-05-16T14:05:00.000Z",
6
+ "finishedAt": "2026-05-16T14:05:03.800Z",
7
+ "tests": [
8
+ {
9
+ "appName": "checkout-web",
10
+ "framework": "vitest",
11
+ "startedAt": "2026-05-16T14:05:00.000Z",
12
+ "finishedAt": "2026-05-16T14:05:00.600Z",
13
+ "describePath": ["Checkout UI", "email field"],
14
+ "feature": "Checkout UI",
15
+ "testName": "should accept a valid customer email",
16
+ "status": "passed",
17
+ "duration": 22,
18
+ "errors": []
19
+ },
20
+ {
21
+ "appName": "checkout-web",
22
+ "framework": "vitest",
23
+ "startedAt": "2026-05-16T14:05:00.600Z",
24
+ "finishedAt": "2026-05-16T14:05:01.200Z",
25
+ "describePath": ["Checkout UI", "payment display"],
26
+ "feature": "Checkout UI",
27
+ "testName": "should mask card number showing last four digits",
28
+ "status": "passed",
29
+ "duration": 18,
30
+ "errors": []
31
+ },
32
+ {
33
+ "appName": "checkout-web",
34
+ "framework": "vitest",
35
+ "startedAt": "2026-05-16T14:05:01.200Z",
36
+ "finishedAt": "2026-05-16T14:05:02.400Z",
37
+ "describePath": ["Checkout UI", "shipping banner"],
38
+ "feature": "Checkout UI",
39
+ "testName": "should show free shipping over threshold for US",
40
+ "status": "passed",
41
+ "duration": 11,
42
+ "errors": []
43
+ },
44
+ {
45
+ "appName": "checkout-web",
46
+ "framework": "vitest",
47
+ "startedAt": "2026-05-16T14:05:02.400Z",
48
+ "finishedAt": "2026-05-16T14:05:03.800Z",
49
+ "describePath": ["Product card component"],
50
+ "feature": "Product card",
51
+ "testName": "should hide add-to-cart when out of stock",
52
+ "status": "failed",
53
+ "duration": 40,
54
+ "errors": [
55
+ {
56
+ "message": "Expected inStock > 0 to be true, received false",
57
+ "stack": "AssertionError: expected false to be true\n at examples/frontend/checkout-form.test.ts"
58
+ }
59
+ ]
60
+ }
61
+ ]
62
+ }
@@ -0,0 +1,73 @@
1
+ {
2
+ "runId": "tracing-frontend-manual-001",
3
+ "appName": "checkout-web",
4
+ "framework": "vitest",
5
+ "startedAt": "2026-05-16T17:00:00.000Z",
6
+ "finishedAt": "2026-05-16T17:00:04.500Z",
7
+ "locale": "en",
8
+ "tests": [
9
+ {
10
+ "appName": "checkout-web",
11
+ "framework": "vitest",
12
+ "startedAt": "2026-05-16T17:00:00.000Z",
13
+ "finishedAt": "2026-05-16T17:00:01.000Z",
14
+ "describePath": ["Checkout UI", "email field"],
15
+ "feature": "Checkout",
16
+ "testName": "should accept a valid customer email",
17
+ "status": "passed",
18
+ "duration": 8,
19
+ "errors": []
20
+ },
21
+ {
22
+ "appName": "checkout-web",
23
+ "framework": "vitest",
24
+ "startedAt": "2026-05-16T17:00:01.000Z",
25
+ "finishedAt": "2026-05-16T17:00:01.800Z",
26
+ "describePath": ["Checkout UI", "email field"],
27
+ "feature": "Checkout",
28
+ "testName": "should reject malformed email addresses",
29
+ "status": "failed",
30
+ "duration": 35,
31
+ "errors": [
32
+ {
33
+ "message": "expected false to be true — invalid email was accepted in the form",
34
+ "stack": "AssertionError: expected false to be true\n at examples/frontend/error-tracing.test.ts:22:45"
35
+ }
36
+ ]
37
+ },
38
+ {
39
+ "appName": "checkout-web",
40
+ "framework": "vitest",
41
+ "startedAt": "2026-05-16T17:00:01.800Z",
42
+ "finishedAt": "2026-05-16T17:00:02.600Z",
43
+ "describePath": ["Checkout UI", "payment display"],
44
+ "feature": "Checkout",
45
+ "testName": "should block submit when terms are not accepted",
46
+ "status": "failed",
47
+ "duration": 28,
48
+ "errors": [
49
+ {
50
+ "message": "expected false to be true — checkout allowed without accepting terms",
51
+ "stack": "AssertionError: expected false to be true\n at examples/frontend/error-tracing.test.ts:38:9"
52
+ }
53
+ ]
54
+ },
55
+ {
56
+ "appName": "checkout-web",
57
+ "framework": "vitest",
58
+ "startedAt": "2026-05-16T17:00:02.600Z",
59
+ "finishedAt": "2026-05-16T17:00:03.400Z",
60
+ "describePath": ["Product card component"],
61
+ "feature": "Product catalog",
62
+ "testName": "should hide add-to-cart when out of stock",
63
+ "status": "failed",
64
+ "duration": 22,
65
+ "errors": [
66
+ {
67
+ "message": "expected false to be true — add-to-cart visible with zero stock",
68
+ "stack": "AssertionError: expected false to be true\n at examples/frontend/error-tracing.test.ts:56:24"
69
+ }
70
+ ]
71
+ }
72
+ ]
73
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Call the Nzila webhook from your **backend** (Next.js Route Handler, BFF, CI).
3
+ * Never import this from client components — API keys must stay server-side.
4
+ */
5
+ import axios from "axios";
6
+ import type { WebhookPayload } from "../src/lib/webhook-schema";
7
+
8
+ export type SendWebhookOptions = {
9
+ baseUrl?: string;
10
+ apiKey: string;
11
+ payload: WebhookPayload;
12
+ };
13
+
14
+ export async function sendNzilaWebhook({
15
+ baseUrl = process.env.NZILA_WEBHOOK_URL ?? "http://localhost:3000/api/webhooks/tests",
16
+ apiKey,
17
+ payload,
18
+ }: SendWebhookOptions) {
19
+ return axios.post(baseUrl, payload, {
20
+ headers: {
21
+ Authorization: `Bearer ${apiKey}`,
22
+ "Content-Type": "application/json",
23
+ },
24
+ validateStatus: () => true,
25
+ });
26
+ }
27
+
28
+ /** Backend (API) sample — use after `npm run example:webhook:backend` or build your own payload. */
29
+ export const backendPayloadHint = "examples/backend/sample-payload.json";
30
+
31
+ /** Frontend (web) sample — use after `npm run example:webhook:frontend`. */
32
+ export const frontendPayloadHint = "examples/frontend/sample-payload.json";
@@ -0,0 +1,18 @@
1
+ /** @type {import('jest').Config} */
2
+ const NzilaJestReporter = require("../@nzila/sdk/jest-reporter.ts").default;
3
+
4
+ module.exports = {
5
+ testEnvironment: "node",
6
+ reporters: [
7
+ "default",
8
+ [
9
+ NzilaJestReporter,
10
+ {
11
+ webhookUrl: process.env.NZILA_WEBHOOK_URL ?? "http://localhost:3000/api/webhooks/tests",
12
+ apiKey: process.env.NZILA_API_KEY,
13
+ appName: process.env.NZILA_APP_NAME ?? "my-app",
14
+ framework: "jest",
15
+ },
16
+ ],
17
+ ],
18
+ };
@@ -0,0 +1,21 @@
1
+ const { createMochaNzilaReporter } = require("../@nzila/sdk/mocha-reporter.ts");
2
+
3
+ module.exports = {
4
+ reporter: "spec",
5
+ reporterOptions: undefined,
6
+ require: [],
7
+ /** Register in .mocharc.cjs: */
8
+ // reporter: createMochaNzilaReporter({ ... }),
9
+ };
10
+
11
+ /** Usage in .mocharc.cjs:
12
+ * const { createMochaNzilaReporter } = require("./@nzila/sdk/mocha-reporter.ts");
13
+ * module.exports = {
14
+ * reporter: createMochaNzilaReporter({
15
+ * webhookUrl: process.env.NZILA_WEBHOOK_URL,
16
+ * apiKey: process.env.NZILA_API_KEY,
17
+ * appName: process.env.NZILA_APP_NAME,
18
+ * framework: "mocha",
19
+ * }),
20
+ * };
21
+ */
@@ -0,0 +1,41 @@
1
+ "use client";
2
+
3
+ /**
4
+ * React runtime tracing — same feature name as your Vitest `mapFeature` / describe.
5
+ * Env: NEXT_PUBLIC_NZILA_SIGNALS_URL or NZILA_SIGNALS_URL, NZILA_API_KEY (server init recommended).
6
+ */
7
+ import { initNzilaRuntime } from "@nzila/sdk";
8
+ import { useNzilaFeature, NzilaFeatureBoundary } from "@nzila/sdk/react";
9
+
10
+ initNzilaRuntime({
11
+ signalsUrl:
12
+ process.env.NEXT_PUBLIC_NZILA_SIGNALS_URL ??
13
+ process.env.NZILA_SIGNALS_URL ??
14
+ "https://your-app.com/api/signals/runtime",
15
+ apiKey: process.env.NZILA_API_KEY ?? "",
16
+ locale: "en",
17
+ });
18
+
19
+ export function CheckoutPage() {
20
+ const nzila = useNzilaFeature("Checkout");
21
+
22
+ async function submit() {
23
+ try {
24
+ await nzila.runApi("checkout.submit", async () => {
25
+ const res = await fetch("/api/checkout", { method: "POST" });
26
+ if (!res.ok) throw new Error(`HTTP ${res.status}`);
27
+ return res.json();
28
+ });
29
+ } catch (error) {
30
+ nzila.captureError(error, { step: "submit" });
31
+ }
32
+ }
33
+
34
+ return (
35
+ <NzilaFeatureBoundary feature="Checkout" nzila={nzila}>
36
+ <button type="button" onClick={() => void submit()}>
37
+ Pay
38
+ </button>
39
+ </NzilaFeatureBoundary>
40
+ );
41
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * POST a test run manually with {@link sendNzilaRun} (CI, custom runners).
3
+ */
4
+ import { sendNzilaRun, type NzilaRunPayload } from "@nzila/sdk";
5
+ import { readFileSync } from "node:fs";
6
+
7
+ const payload = JSON.parse(
8
+ readFileSync(new URL("./backend/sample-payload.json", import.meta.url), "utf8"),
9
+ ) as NzilaRunPayload;
10
+
11
+ await sendNzilaRun(payload, {
12
+ webhookUrl: process.env.NZILA_WEBHOOK_URL ?? "http://localhost:3000/api/webhooks/tests",
13
+ apiKey: process.env.NZILA_API_KEY ?? "",
14
+ });
15
+
16
+ console.log("Webhook sent (silent on failure).");
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Map Vitest describe() titles to Nzila **feature** names (dashboard grouping).
3
+ * Use the same strings in runtime signals: POST /api/signals/runtime { "feature": "Checkout" }.
4
+ */
5
+
6
+ export function resolveBackendFeature(describePath: string[], _testName: string): string {
7
+ const root = describePath[0] ?? "general";
8
+ const map: Record<string, string> = {
9
+ Checkout: "Checkout",
10
+ Inventory: "Inventory",
11
+ "Auth API": "Authentication",
12
+ "Payment API": "Payment",
13
+ };
14
+ return map[root] ?? root;
15
+ }
16
+
17
+ export function resolveFrontendFeature(describePath: string[], _testName: string): string {
18
+ const root = describePath[0] ?? "general";
19
+ const map: Record<string, string> = {
20
+ "Checkout UI": "Checkout",
21
+ "Product card component": "Product catalog",
22
+ };
23
+ return map[root] ?? root;
24
+ }
@@ -0,0 +1,24 @@
1
+ import { config } from "dotenv";
2
+ import { dirname, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ const examplesDir = dirname(fileURLToPath(import.meta.url));
6
+ const projectRoot = resolve(examplesDir, "../..");
7
+
8
+ config({ path: resolve(projectRoot, ".env.local") });
9
+ config({ path: resolve(projectRoot, ".env") });
10
+
11
+ export function getWebhookEnv() {
12
+ const apiKey = process.env.NZILA_API_KEY?.trim();
13
+ const webhookUrl =
14
+ process.env.NZILA_WEBHOOK_URL?.trim() ??
15
+ "http://localhost:3000/api/webhooks/tests";
16
+
17
+ if (!apiKey) {
18
+ console.error("Missing NZILA_API_KEY in .env.local");
19
+ console.error("Create a key at http://localhost:3000/en/keys");
20
+ process.exit(1);
21
+ }
22
+
23
+ return { apiKey, webhookUrl, projectRoot };
24
+ }