@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.
@@ -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
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * POST a sample payload to Nzila (backend or frontend JSON under examples/).
3
+ *
4
+ * Usage:
5
+ * node examples/shared/send-webhook.mjs examples/backend/sample-payload.json
6
+ * node examples/shared/send-webhook.mjs examples/frontend/sample-payload.json
7
+ */
8
+ import { readFileSync } from "node:fs";
9
+ import { resolve } from "node:path";
10
+ import axios from "axios";
11
+ import { getWebhookEnv } from "./load-env.mjs";
12
+
13
+ const payloadArg = process.argv[2];
14
+ if (!payloadArg) {
15
+ console.error("Usage: node examples/shared/send-webhook.mjs <path-to-payload.json>");
16
+ process.exit(1);
17
+ }
18
+
19
+ const { apiKey, webhookUrl, projectRoot } = getWebhookEnv();
20
+ const payloadPath = resolve(projectRoot, payloadArg);
21
+ const payload = JSON.parse(readFileSync(payloadPath, "utf8"));
22
+ payload.runId = `${payload.appName ?? "example"}-${Date.now()}`;
23
+
24
+ const res = await axios.post(webhookUrl, payload, {
25
+ headers: {
26
+ Authorization: `Bearer ${apiKey}`,
27
+ "Content-Type": "application/json",
28
+ },
29
+ validateStatus: () => true,
30
+ });
31
+
32
+ console.log("Payload:", payloadPath);
33
+ console.log("Status:", res.status);
34
+ console.log("Body:", JSON.stringify(res.data, null, 2));
35
+
36
+ if (res.status === 403 && res.data?.code === "API_KEY_EXPIRED") {
37
+ console.error("\nAPI key expired — create a new one at /en/keys");
38
+ process.exit(1);
39
+ }
40
+
41
+ if (res.status === 202 || res.status === 200) {
42
+ console.log("\nView the run: http://localhost:3000/en");
43
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Copy into your app or merge the reporters block into your vitest.config.ts.
3
+ * Nzila repo uses examples/vitest.config.ts (and backend/frontend-specific configs).
4
+ */
5
+ import { defineConfig } from "vitest/config";
6
+ import NzilaVitestReporter from "@nzila/sdk/vitest";
7
+
8
+ export default defineConfig({
9
+ test: {
10
+ reporters: [
11
+ "default",
12
+ new NzilaVitestReporter({
13
+ webhookUrl: process.env.NZILA_WEBHOOK_URL ?? "http://localhost:3000/api/webhooks/tests",
14
+ apiKey: process.env.NZILA_API_KEY!,
15
+ appName: process.env.NZILA_APP_NAME ?? "my-app",
16
+ maxRetries: 3,
17
+ }),
18
+ ],
19
+ },
20
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nzila/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Send test runs to Nzila and trace live feature errors (Vitest, Jest, Mocha, React, Node).",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -14,7 +14,8 @@
14
14
  },
15
15
  "files": [
16
16
  "dist",
17
- "README.md"
17
+ "README.md",
18
+ "examples"
18
19
  ],
19
20
  "main": "./dist/index.js",
20
21
  "types": "./dist/index.d.ts",
@@ -46,7 +47,8 @@
46
47
  },
47
48
  "scripts": {
48
49
  "build": "tsc -p tsconfig.build.json",
49
- "prepublishOnly": "npm run build"
50
+ "prepare-examples": "node scripts/sync-examples.mjs",
51
+ "prepublishOnly": "npm run prepare-examples && npm run build"
50
52
  },
51
53
  "peerDependencies": {
52
54
  "react": ">=18.0.0",