@nzila/sdk 0.1.7 → 0.1.10

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 CHANGED
@@ -25,6 +25,10 @@ Subpaths such as `@nzila/sdk/vitest` need **`moduleResolution`** set to **`bundl
25
25
 
26
26
  `@nzila/sdk@0.1.4+` also ships root `.d.ts` shims and `typesVersions` for broader editor support.
27
27
 
28
+ ### Vitest reporter API
29
+
30
+ `NzilaVitestReporter` implements Vitest’s `Reporter` interface and sends the webhook in **`onTestRunEnd`** (after all modules finish). Optional hooks like `onTestCaseResult` are not required.
31
+
28
32
  ### Vitest: tests hang or never start?
29
33
 
30
34
  Use the **tuple** from `nzilaVitestReporter()` — do **not** put `new NzilaVitestReporter()` in `vitest.config.ts`. Worker pools cannot serialize class instances.
@@ -73,23 +77,34 @@ After you sign in on [nzila-kappa.vercel.app](https://nzila-kappa.vercel.app), t
73
77
  **Tests (CI):**
74
78
 
75
79
  ```ts
80
+ import nzilaConfig from "./nzila.config";
76
81
  import { nzilaVitestReporter } from "@nzila/sdk/vitest";
82
+ import { reporterOptionsFromNzilaConfig } from "@nzila/sdk";
77
83
 
78
84
  export default defineConfig({
79
85
  test: {
80
86
  reporters: [
81
87
  "default",
82
- nzilaVitestReporter({
83
- webhookUrl: process.env.NZILA_WEBHOOK_URL!,
84
- apiKey: process.env.NZILA_API_KEY!,
85
- appName: "my-app",
86
- mapFeature: (path) => path[0] ?? "general",
87
- }),
88
+ nzilaVitestReporter(reporterOptionsFromNzilaConfig(nzilaConfig)),
88
89
  ],
89
90
  },
90
91
  });
91
92
  ```
92
93
 
94
+ `nzila.config.ts` (from `npx @nzila/sdk link --api-key … --project-id <uuid> --app-name <app>`):
95
+
96
+ ```ts
97
+ export default {
98
+ apiKey: "nzl_…",
99
+ projectId: "00000000-0000-0000-0000-000000000000",
100
+ appName: "my-app",
101
+ endpoint: "https://nzila-kappa.vercel.app",
102
+ framework: "vitest",
103
+ } as const;
104
+ ```
105
+
106
+ Or set env vars: `NZILA_API_KEY`, `NZILA_PROJECT_ID`, `NZILA_APP_NAME`, `NZILA_WEBHOOK_URL`.
107
+
93
108
  **React (runtime):**
94
109
 
95
110
  ```ts
package/dist/cli/main.js CHANGED
@@ -16,13 +16,15 @@ async function loadConfig(cwd) {
16
16
  const source = await readFile(file, "utf8");
17
17
  const match = source.match(/apiKey:\s*["']([^"']+)["']/);
18
18
  const projectMatch = source.match(/projectId:\s*["']([^"']+)["']/);
19
+ const appMatch = source.match(/appName:\s*["']([^"']+)["']/);
19
20
  const endpointMatch = source.match(/endpoint:\s*["']([^"']+)["']/);
20
21
  const frameworkMatch = source.match(/framework:\s*["']([^"']+)["']/);
21
- if (!match?.[1] || !projectMatch?.[1])
22
+ if (!match?.[1] || !projectMatch?.[1] || !appMatch?.[1])
22
23
  return null;
23
24
  return {
24
25
  apiKey: match[1],
25
26
  projectId: projectMatch[1],
27
+ appName: appMatch[1],
26
28
  endpoint: endpointMatch?.[1] ?? defaultEndpoint(),
27
29
  framework: frameworkMatch?.[1] ?? "vitest",
28
30
  };
@@ -32,6 +34,7 @@ async function writeConfig(cwd, config) {
32
34
  export default {
33
35
  apiKey: "${config.apiKey}",
34
36
  projectId: "${config.projectId}",
37
+ appName: "${config.appName.replace(/"/g, '\\"')}",
35
38
  endpoint: "${config.endpoint.replace(/"/g, '\\"')}",
36
39
  framework: "${config.framework}",
37
40
  } as const;
@@ -65,17 +68,45 @@ function detectFramework(cwd) {
65
68
  return "playwright";
66
69
  return "vitest";
67
70
  }
68
- function printEnvSnippet(endpoint) {
69
- const paths = platformPaths(endpoint);
71
+ function printEnvSnippet(config) {
72
+ const paths = platformPaths(config.endpoint);
70
73
  console.log("");
71
- console.log("Add to your project .env.local:");
74
+ console.log("Optional .env.local (or use nzila.config.ts only):");
72
75
  console.log(`NZILA_WEBHOOK_URL=${paths.webhook}`);
73
76
  console.log(`NZILA_SIGNALS_URL=${paths.signals}`);
74
- console.log("NZILA_API_KEY=<from dashboard → API Keys>");
77
+ console.log(`NZILA_API_KEY=${config.apiKey}`);
78
+ console.log(`NZILA_PROJECT_ID=${config.projectId}`);
79
+ console.log(`NZILA_APP_NAME=${config.appName}`);
75
80
  console.log("");
76
- console.log(`SDK already installed via ${PKG}; add Vitest reporter from docs.`);
81
+ printReporterSnippet(config);
77
82
  console.log(`Docs: ${paths.docs}`);
78
83
  }
84
+ function printReporterSnippet(config) {
85
+ const paths = platformPaths(config.endpoint);
86
+ if (config.framework === "vitest") {
87
+ console.log("vitest.config.ts:");
88
+ console.log(` import nzilaConfig from "./${CONFIG_FILE.replace(".ts", "")}";`);
89
+ console.log(` import { nzilaVitestReporter } from "${PKG}/vitest";`);
90
+ console.log(` import { reporterOptionsFromNzilaConfig } from "${PKG}";`);
91
+ console.log(" reporters: [");
92
+ console.log(' "default",');
93
+ console.log(" nzilaVitestReporter(reporterOptionsFromNzilaConfig(nzilaConfig)),");
94
+ console.log(" ],");
95
+ }
96
+ else if (config.framework === "jest") {
97
+ console.log("jest.config:");
98
+ console.log(` reporters: ["default", ["${PKG}/jest", {`);
99
+ console.log(` webhookUrl: "${paths.webhook}",`);
100
+ console.log(` apiKey: nzilaConfig.apiKey,`);
101
+ console.log(` projectId: "${config.projectId}",`);
102
+ console.log(` appName: "${config.appName}",`);
103
+ console.log(" }]],");
104
+ }
105
+ else {
106
+ console.log(`Wire ${PKG} reporter for ${config.framework} — see docs.`);
107
+ }
108
+ console.log("");
109
+ }
79
110
  async function tryOpen(url, quiet = false) {
80
111
  try {
81
112
  await openBrowser(url);
@@ -104,9 +135,15 @@ async function cmdInit(flags) {
104
135
  console.log("Quick start:");
105
136
  console.log(` 1. npx ${PKG} login → sign in (opens browser)`);
106
137
  console.log(" 2. Create a project + API key in the dashboard");
107
- console.log(` 3. npx ${PKG} link --api-key nzl_… --project-id <uuid>`);
138
+ console.log(` 3. npx ${PKG} link --api-key nzl_… --project-id <uuid> --app-name <app>`);
108
139
  console.log(" 4. Wire @nzila/sdk/vitest and run tests");
109
- printEnvSnippet(base);
140
+ printEnvSnippet({
141
+ apiKey: "<api-key>",
142
+ projectId: "<project-uuid>",
143
+ appName: "<app-name>",
144
+ endpoint: base,
145
+ framework,
146
+ });
110
147
  if (flags.open !== false) {
111
148
  await tryOpen(paths.register, true);
112
149
  }
@@ -119,22 +156,24 @@ async function cmdLogin(flags) {
119
156
  console.log(` ${platformPaths(base).keys}`);
120
157
  console.log("");
121
158
  console.log("Then link this repo:");
122
- console.log(` npx ${PKG} link --api-key <key> --project-id <uuid>`);
159
+ console.log(` npx ${PKG} link --api-key <key> --project-id <uuid> --app-name <app>`);
123
160
  await tryOpen(loginUrl);
124
161
  }
125
162
  async function cmdLink(flags) {
126
163
  const apiKey = String(flags["api-key"] ?? flags.apiKey ?? "");
127
164
  const projectId = String(flags["project-id"] ?? flags.projectId ?? "");
165
+ const appName = String(flags["app-name"] ?? flags.appName ?? "");
128
166
  const endpoint = String(flags.endpoint ?? defaultEndpoint()).replace(/\/$/, "");
129
- if (!apiKey || !projectId) {
130
- console.error(`Usage: npx ${PKG} link --api-key <token> --project-id <uuid> [--endpoint <url>]`);
167
+ if (!apiKey || !projectId || !appName) {
168
+ console.error(`Usage: npx ${PKG} link --api-key <token> --project-id <uuid> --app-name <name> [--endpoint <url>]`);
131
169
  process.exit(1);
132
170
  }
133
171
  const cwd = process.cwd();
134
172
  const framework = detectFramework(cwd);
135
- await writeConfig(cwd, { apiKey, projectId, endpoint, framework });
173
+ const config = { apiKey, projectId, appName, endpoint, framework };
174
+ await writeConfig(cwd, config);
136
175
  console.log(`Wrote ${CONFIG_FILE} (endpoint: ${endpoint})`);
137
- printEnvSnippet(endpoint);
176
+ printEnvSnippet(config);
138
177
  }
139
178
  async function cmdDoctor() {
140
179
  const cwd = process.cwd();
@@ -146,6 +185,7 @@ async function cmdDoctor() {
146
185
  const paths = platformPaths(config.endpoint);
147
186
  console.log("Config OK:", {
148
187
  projectId: config.projectId,
188
+ appName: config.appName,
149
189
  endpoint: config.endpoint,
150
190
  framework: config.framework,
151
191
  });
@@ -159,13 +199,13 @@ Commands:
159
199
  open Open the dashboard in your browser (default)
160
200
  init Detect framework + show setup steps
161
201
  login Open sign-in and linking instructions
162
- link Write nzila.config.ts (--api-key, --project-id)
202
+ link Write nzila.config.ts (--api-key, --project-id, --app-name)
163
203
  doctor Validate nzila.config.ts
164
204
 
165
205
  Examples:
166
206
  npx ${PKG}
167
207
  npx ${PKG} login
168
- npx ${PKG} link --api-key nzl_… --project-id <uuid>
208
+ npx ${PKG} link --api-key nzl_… --project-id <uuid> --app-name <app>
169
209
 
170
210
  Docs: ${paths.docs}
171
211
  `);
@@ -1,29 +1,5 @@
1
+ import type { TestModule } from "vitest/node";
1
2
  import type { NzilaClientOptions, NzilaTestCase } from "./types.js";
2
- /** Duck-typed Vitest 3 test tree (no `vitest/node` import — safe for worker load). */
3
- type VitestTestNode = {
4
- name: string;
5
- parent: {
6
- type: string;
7
- name: string;
8
- parent: VitestTestNode["parent"];
9
- };
10
- result(): {
11
- state: string;
12
- errors?: {
13
- message?: string;
14
- stack?: string;
15
- }[];
16
- };
17
- diagnostic(): {
18
- duration?: number;
19
- } | undefined;
20
- };
21
- type VitestTestModule = {
22
- children?: {
23
- allTests(): Iterable<VitestTestNode>;
24
- };
25
- };
26
- /** Collect finished tests from Vitest 3+ `TestModule` reporters API. */
27
- export declare function collectTestsFromVitestModules(modules: ReadonlyArray<VitestTestModule>, options: NzilaClientOptions, runStartedAt: Date): NzilaTestCase[];
28
- export {};
3
+ /** Collect finished tests from Vitest `onTestRunEnd` modules. */
4
+ export declare function collectTestsFromVitestModules(modules: ReadonlyArray<TestModule>, options: NzilaClientOptions, runStartedAt: Date): NzilaTestCase[];
29
5
  //# sourceMappingURL=collect-vitest-tests.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"collect-vitest-tests.d.ts","sourceRoot":"","sources":["../src/collect-vitest-tests.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAmB,MAAM,YAAY,CAAC;AAErF,sFAAsF;AACtF,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,cAAc,CAAC,QAAQ,CAAC,CAAA;KAAE,CAAC;IACzE,MAAM,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,EAAE,CAAA;KAAE,CAAC;IAC7E,UAAU,IAAI;QAAE,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CACjD,CAAC;AAEF,KAAK,gBAAgB,GAAG;IACtB,QAAQ,CAAC,EAAE;QACT,QAAQ,IAAI,QAAQ,CAAC,cAAc,CAAC,CAAC;KACtC,CAAC;CACH,CAAC;AAsBF,wEAAwE;AACxE,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,aAAa,CAAC,gBAAgB,CAAC,EACxC,OAAO,EAAE,kBAAkB,EAC3B,YAAY,EAAE,IAAI,GACjB,aAAa,EAAE,CAyCjB"}
1
+ {"version":3,"file":"collect-vitest-tests.d.ts","sourceRoot":"","sources":["../src/collect-vitest-tests.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAmB,MAAM,YAAY,CAAC;AA6BrF,iEAAiE;AACjE,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,EAClC,OAAO,EAAE,kBAAkB,EAC3B,YAAY,EAAE,IAAI,GACjB,aAAa,EAAE,CA0CjB"}
@@ -1,3 +1,4 @@
1
+ import { nzilaTestContext } from "./reporter-options.js";
1
2
  function describePathFor(test) {
2
3
  const path = [];
3
4
  let parent = test.parent;
@@ -20,7 +21,7 @@ function mapStatus(state) {
20
21
  return "skipped";
21
22
  return "pending";
22
23
  }
23
- /** Collect finished tests from Vitest 3+ `TestModule` reporters API. */
24
+ /** Collect finished tests from Vitest `onTestRunEnd` modules. */
24
25
  export function collectTestsFromVitestModules(modules, options, runStartedAt) {
25
26
  const framework = options.framework ?? "vitest";
26
27
  const tests = [];
@@ -30,11 +31,12 @@ export function collectTestsFromVitestModules(modules, options, runStartedAt) {
30
31
  if (!allTests)
31
32
  continue;
32
33
  for (const test of allTests) {
33
- const result = test.result();
34
+ const node = test;
35
+ const result = node.result();
34
36
  if (result.state === "pending")
35
37
  continue;
36
- const describePath = describePathFor(test);
37
- const feature = options.mapFeature?.(describePath, test.name) ??
38
+ const describePath = describePathFor(node);
39
+ const feature = options.mapFeature?.(describePath, node.name) ??
38
40
  describePath[0] ??
39
41
  "general";
40
42
  const errors = result.errors?.map((e) => ({
@@ -48,10 +50,10 @@ export function collectTestsFromVitestModules(modules, options, runStartedAt) {
48
50
  finishedAt: now,
49
51
  describePath,
50
52
  feature,
51
- context: { layer: options.appName },
52
- testName: test.name,
53
+ context: nzilaTestContext(options),
54
+ testName: node.name,
53
55
  status: mapStatus(result.state),
54
- duration: test.diagnostic()?.duration ?? 0,
56
+ duration: node.diagnostic()?.duration ?? 0,
55
57
  errors,
56
58
  });
57
59
  }
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export { NZILA_TEST_FRAMEWORKS, type NzilaTestFramework, type NzilaTestStatus, type NzilaLocale, type NzilaTestCase, type NzilaRunPayload, type NzilaClientOptions, } from "./types.js";
7
7
  export { sendNzilaRun, resolveLocale, type DeliverOptions } from "./deliver.js";
8
+ export { type NzilaConfig, webhookUrlFromEndpoint, reporterOptionsFromNzilaConfig, } from "./nzila-config.js";
8
9
  export { initNzilaRuntime, isNzilaRuntimeReady, traceNzilaFeature, createFeatureHandle, reportFeatureError, captureFeatureError, extractErrorFields, type NzilaFeatureHandle, type NzilaRuntimeConfig, type NzilaRuntimeSignal, } from "./runtime/index.js";
9
10
  export { default as NzilaVitestReporter } from "./vitest-reporter.js";
10
11
  export { default as NzilaJestReporter } from "./jest-reporter.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,wBAAwB,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,qBAAqB,EACrB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,kBAAkB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EACL,KAAK,WAAW,EAChB,sBAAsB,EACtB,8BAA8B,GAC/B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EACjB,mBAAmB,EACnB,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,OAAO,IAAI,mBAAmB,EAAE,MAAM,sBAAsB,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,wBAAwB,EAAE,OAAO,IAAI,yBAAyB,EAAE,MAAM,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export { NZILA_TEST_FRAMEWORKS, } from "./types.js";
7
7
  export { sendNzilaRun, resolveLocale } from "./deliver.js";
8
+ export { webhookUrlFromEndpoint, reporterOptionsFromNzilaConfig, } from "./nzila-config.js";
8
9
  export { initNzilaRuntime, isNzilaRuntimeReady, traceNzilaFeature, createFeatureHandle, reportFeatureError, captureFeatureError, extractErrorFields, } from "./runtime/index.js";
9
10
  export { default as NzilaVitestReporter } from "./vitest-reporter.js";
10
11
  export { default as NzilaJestReporter } from "./jest-reporter.js";
@@ -1 +1 @@
1
- {"version":3,"file":"jest-reporter.d.ts","sourceRoot":"","sources":["../src/jest-reporter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,kBAAkB,EAInB,MAAM,YAAY,CAAC;AAEpB,KAAK,aAAa,GAAG;IAAE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEpD,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;IAC1E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,WAAW,EAAE;QACX,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1C,WAAW,EAAE,cAAc,EAAE,CAAC;KAC/B,EAAE,CAAC;CACL,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0C;IAClE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,IAAI,CAAS;gBAET,OAAO,EAAE,kBAAkB,EAAE,aAAa,CAAC,EAAE,OAAO;IAKhE,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc;IAmDzD,OAAO,CAAC,SAAS;CAQlB;AAED,4CAA4C;AAC5C,YAAY,EAAE,aAAa,EAAE,CAAC"}
1
+ {"version":3,"file":"jest-reporter.d.ts","sourceRoot":"","sources":["../src/jest-reporter.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,kBAAkB,EAInB,MAAM,YAAY,CAAC;AAEpB,KAAK,aAAa,GAAG;IAAE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC;AAEpD,KAAK,cAAc,GAAG;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;IAC1E,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,WAAW,EAAE;QACX,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC;QAC1C,WAAW,EAAE,cAAc,EAAE,CAAC;KAC/B,EAAE,CAAC;CACL,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,OAAO,iBAAiB;IACpC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0C;IAClE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,IAAI,CAAS;gBAET,OAAO,EAAE,kBAAkB,EAAE,aAAa,CAAC,EAAE,OAAO;IAKhE,aAAa,CAAC,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc;IAoDzD,OAAO,CAAC,SAAS;CAQlB;AAED,4CAA4C;AAC5C,YAAY,EAAE,aAAa,EAAE,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { sendNzilaRun, resolveLocale } from "./deliver.js";
3
- import { resolveClientOptions } from "./reporter-options.js";
3
+ import { nzilaTestContext, resolveClientOptions } from "./reporter-options.js";
4
4
  /**
5
5
  * Jest reporter: aggregates results and POSTs one {@link sendNzilaRun} payload on `onRunComplete`.
6
6
  *
@@ -35,6 +35,7 @@ export default class NzilaJestReporter {
35
35
  finishedAt: new Date(file.perfStats.end).toISOString(),
36
36
  describePath,
37
37
  feature: describePath[0],
38
+ context: nzilaTestContext(this.options),
38
39
  testName: test.title,
39
40
  status: this.mapStatus(test.status),
40
41
  duration: test.duration ?? 0,
@@ -0,0 +1,28 @@
1
+ import type { NzilaClientOptions, NzilaTestFramework } from "./types.js";
2
+ /** Shape of `nzila.config.ts` written by `npx @nzila/sdk link`. */
3
+ export type NzilaConfig = {
4
+ apiKey: string;
5
+ /** Dashboard project UUID (API Keys → copy project ID). */
6
+ projectId: string;
7
+ /** Must match the project App name in Nzila (webhook payload). */
8
+ appName: string;
9
+ /** Platform base URL, e.g. https://nzila-kappa.vercel.app */
10
+ endpoint: string;
11
+ framework?: NzilaTestFramework;
12
+ locale?: NzilaClientOptions["locale"];
13
+ };
14
+ export declare function webhookUrlFromEndpoint(endpoint: string): string;
15
+ /**
16
+ * Build Vitest/Jest reporter options from `nzila.config.ts` (or the same fields inline).
17
+ *
18
+ * @example
19
+ * ```ts
20
+ * import nzilaConfig from "./nzila.config";
21
+ * import { nzilaVitestReporter } from "@nzila/sdk/vitest";
22
+ * import { reporterOptionsFromNzilaConfig } from "@nzila/sdk";
23
+ *
24
+ * reporters: ["default", nzilaVitestReporter(reporterOptionsFromNzilaConfig(nzilaConfig))],
25
+ * ```
26
+ */
27
+ export declare function reporterOptionsFromNzilaConfig(config: NzilaConfig): NzilaClientOptions;
28
+ //# sourceMappingURL=nzila-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nzila-config.d.ts","sourceRoot":"","sources":["../src/nzila-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEzE,mEAAmE;AACnE,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,2DAA2D;IAC3D,SAAS,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,OAAO,EAAE,MAAM,CAAC;IAChB,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,MAAM,CAAC,EAAE,kBAAkB,CAAC,QAAQ,CAAC,CAAC;CACvC,CAAC;AAEF,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,WAAW,GAClB,kBAAkB,CASpB"}
@@ -0,0 +1,26 @@
1
+ export function webhookUrlFromEndpoint(endpoint) {
2
+ const base = endpoint.trim().replace(/\/$/, "");
3
+ return `${base}/api/webhooks/tests`;
4
+ }
5
+ /**
6
+ * Build Vitest/Jest reporter options from `nzila.config.ts` (or the same fields inline).
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * import nzilaConfig from "./nzila.config";
11
+ * import { nzilaVitestReporter } from "@nzila/sdk/vitest";
12
+ * import { reporterOptionsFromNzilaConfig } from "@nzila/sdk";
13
+ *
14
+ * reporters: ["default", nzilaVitestReporter(reporterOptionsFromNzilaConfig(nzilaConfig))],
15
+ * ```
16
+ */
17
+ export function reporterOptionsFromNzilaConfig(config) {
18
+ return {
19
+ apiKey: config.apiKey,
20
+ projectId: config.projectId,
21
+ appName: config.appName,
22
+ webhookUrl: webhookUrlFromEndpoint(config.endpoint),
23
+ framework: config.framework,
24
+ locale: config.locale,
25
+ };
26
+ }
@@ -1,4 +1,5 @@
1
1
  import type { NzilaClientOptions } from "./types.js";
2
+ export declare function nzilaTestContext(options: Pick<NzilaClientOptions, "appName" | "projectId">): Record<string, unknown>;
2
3
  /** Merge reporter options with `NZILA_*` environment variables. */
3
4
  export declare function resolveClientOptions(options: NzilaClientOptions): NzilaClientOptions & {
4
5
  debug: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"reporter-options.d.ts","sourceRoot":"","sources":["../src/reporter-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,mEAAmE;AACnE,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,kBAAkB,GAC1B,kBAAkB,GAAG;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAQzC"}
1
+ {"version":3,"file":"reporter-options.d.ts","sourceRoot":"","sources":["../src/reporter-options.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,IAAI,CAAC,kBAAkB,EAAE,SAAS,GAAG,WAAW,CAAC,GACzD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAKzB;AAED,mEAAmE;AACnE,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,kBAAkB,GAC1B,kBAAkB,GAAG;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,CAUzC"}
@@ -1,3 +1,9 @@
1
+ export function nzilaTestContext(options) {
2
+ return {
3
+ layer: options.appName,
4
+ ...(options.projectId ? { projectId: options.projectId } : {}),
5
+ };
6
+ }
1
7
  /** Merge reporter options with `NZILA_*` environment variables. */
2
8
  export function resolveClientOptions(options) {
3
9
  return {
@@ -5,6 +11,7 @@ export function resolveClientOptions(options) {
5
11
  webhookUrl: options.webhookUrl?.trim() || process.env.NZILA_WEBHOOK_URL?.trim() || "",
6
12
  apiKey: options.apiKey?.trim() || process.env.NZILA_API_KEY?.trim() || "",
7
13
  appName: options.appName?.trim() || process.env.NZILA_APP_NAME?.trim() || "",
14
+ projectId: options.projectId?.trim() || process.env.NZILA_PROJECT_ID?.trim() || undefined,
8
15
  debug: options.debug === true || process.env.NZILA_DEBUG === "1",
9
16
  };
10
17
  }
package/dist/types.d.ts CHANGED
@@ -39,6 +39,8 @@ export type NzilaClientOptions = {
39
39
  webhookUrl: string;
40
40
  apiKey: string;
41
41
  appName: string;
42
+ /** Dashboard project UUID — use in `nzila.config.ts` and `NZILA_PROJECT_ID` (metadata on payloads). */
43
+ projectId?: string;
42
44
  framework?: NzilaTestFramework;
43
45
  /** Defaults to a random UUID per reporter instance. */
44
46
  runId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,eAAO,MAAM,qBAAqB,yGAUxB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAE1E,kEAAkE;AAClE,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEpD,oDAAoD;AACpD,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC/C,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,eAAe,GAAG;IAC5B,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,wEAAwE;AACxE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAC7D,eAAO,MAAM,qBAAqB,yGAUxB,CAAC;AAEX,MAAM,MAAM,kBAAkB,GAAG,CAAC,OAAO,qBAAqB,CAAC,CAAC,MAAM,CAAC,CAAC;AAExE,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,CAAC;AAE1E,kEAAkE;AAClE,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;AAEpD,oDAAoD;AACpD,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,kFAAkF;IAClF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,eAAe,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC/C,CAAC;AAEF,sEAAsE;AACtE,MAAM,MAAM,eAAe,GAAG;IAC5B,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,8DAA8D;IAC9D,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,kBAAkB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,wEAAwE;AACxE,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,uGAAuG;IACvG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;OAGG;IACH,UAAU,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,EAAE,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC;IAClE,qEAAqE;IACrE,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC"}
@@ -1,18 +1,24 @@
1
1
  import type { File } from "@vitest/runner";
2
+ import type { Reporter, SerializedError, TestModule, TestRunEndReason } from "vitest/node";
2
3
  import type { NzilaClientOptions } from "./types.js";
3
4
  /**
4
- * Vitest reporter that POSTs one webhook per finished run via {@link sendNzilaRun}.
5
- * Supports Vitest 3+ (`onTestRunEnd`) and legacy `onFinished` file tasks.
5
+ * Vitest {@link Reporter} that POSTs one webhook per run on {@link onTestRunEnd}.
6
+ *
7
+ * @see https://vitest.dev/guide/advanced/reporters
6
8
  */
7
- export default class NzilaVitestReporter {
9
+ export default class NzilaVitestReporter implements Reporter {
8
10
  private readonly options;
9
11
  private startedAt;
10
12
  private sent;
11
13
  private readonly runId;
12
14
  constructor(options: NzilaClientOptions);
13
- onTestRunEnd(testModules: ReadonlyArray<unknown>, _unhandledErrors?: ReadonlyArray<unknown>, _reason?: string): void;
14
- /** @deprecated Vitest 2 still invoked in some setups */
15
- onFinished(files: File[], _errors: unknown[], _coverage?: unknown): void;
15
+ onTestRunStart(): void;
16
+ onTestRunEnd(testModules: ReadonlyArray<TestModule>, _unhandledErrors: ReadonlyArray<SerializedError>, _reason: TestRunEndReason): Promise<void>;
17
+ /**
18
+ * Vitest 2.x only — not part of the current `Reporter` interface.
19
+ * @deprecated Use Vitest 3+ `onTestRunEnd` instead.
20
+ */
21
+ onFinished(files: File[], _errors: unknown[], _coverage?: unknown): Promise<void>;
16
22
  private sendPayload;
17
23
  private walkTasks;
18
24
  private mapStatus;
@@ -28,4 +34,5 @@ export type NzilaVitestReporterEntry = [
28
34
  * Do **not** use `new NzilaVitestReporter()` in config — worker pools cannot serialize instances.
29
35
  */
30
36
  export declare function nzilaVitestReporter(options: NzilaClientOptions): NzilaVitestReporterEntry;
37
+ export type { Reporter as VitestReporter } from "vitest/node";
31
38
  //# sourceMappingURL=vitest-reporter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"vitest-reporter.d.ts","sourceRoot":"","sources":["../src/vitest-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAQ,MAAM,gBAAgB,CAAC;AAKjD,OAAO,KAAK,EAAE,kBAAkB,EAAmD,MAAM,YAAY,CAAC;AAEtG;;;GAGG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAmB;IACtC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0C;IAClE,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,OAAO,EAAE,kBAAkB;IAKvC,YAAY,CACV,WAAW,EAAE,aAAa,CAAC,OAAO,CAAC,EACnC,gBAAgB,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,EACzC,OAAO,CAAC,EAAE,MAAM;IAUlB,0DAA0D;IAC1D,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,OAAO;YAQnD,WAAW;IA2BzB,OAAO,CAAC,SAAS;IAyCjB,OAAO,CAAC,SAAS;CAMlB;AAED,8FAA8F;AAC9F,eAAO,MAAM,qBAAqB,EAAG,mBAA4B,CAAC;AAElE,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,qBAAqB;IAC5B,kBAAkB;CACnB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,kBAAkB,GAC1B,wBAAwB,CAE1B"}
1
+ {"version":3,"file":"vitest-reporter.d.ts","sourceRoot":"","sources":["../src/vitest-reporter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAQ,MAAM,gBAAgB,CAAC;AACjD,OAAO,KAAK,EACV,QAAQ,EACR,eAAe,EACf,UAAU,EACV,gBAAgB,EACjB,MAAM,aAAa,CAAC;AAKrB,OAAO,KAAK,EAAE,kBAAkB,EAAmD,MAAM,YAAY,CAAC;AAEtG;;;;GAIG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAoB,YAAW,QAAQ;IAC1D,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA0C;IAClE,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;gBAEnB,OAAO,EAAE,kBAAkB;IAKvC,cAAc;IAKR,YAAY,CAChB,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC,EACtC,gBAAgB,EAAE,aAAa,CAAC,eAAe,CAAC,EAChD,OAAO,EAAE,gBAAgB;IAM3B;;;OAGG;IACG,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,SAAS,CAAC,EAAE,OAAO;YAQzD,WAAW;IA2BzB,OAAO,CAAC,SAAS;IAiEjB,OAAO,CAAC,SAAS;CAMlB;AAED,8FAA8F;AAC9F,eAAO,MAAM,qBAAqB,EAAG,mBAA4B,CAAC;AAElE,MAAM,MAAM,wBAAwB,GAAG;IACrC,OAAO,qBAAqB;IAC5B,kBAAkB;CACnB,CAAC;AAEF;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,kBAAkB,GAC1B,wBAAwB,CAE1B;AAED,YAAY,EAAE,QAAQ,IAAI,cAAc,EAAE,MAAM,aAAa,CAAC"}
@@ -1,10 +1,11 @@
1
1
  import { randomUUID } from "node:crypto";
2
2
  import { collectTestsFromVitestModules } from "./collect-vitest-tests.js";
3
3
  import { sendNzilaRun, resolveLocale } from "./deliver.js";
4
- import { resolveClientOptions } from "./reporter-options.js";
4
+ import { nzilaTestContext, resolveClientOptions } from "./reporter-options.js";
5
5
  /**
6
- * Vitest reporter that POSTs one webhook per finished run via {@link sendNzilaRun}.
7
- * Supports Vitest 3+ (`onTestRunEnd`) and legacy `onFinished` file tasks.
6
+ * Vitest {@link Reporter} that POSTs one webhook per run on {@link onTestRunEnd}.
7
+ *
8
+ * @see https://vitest.dev/guide/advanced/reporters
8
9
  */
9
10
  export default class NzilaVitestReporter {
10
11
  constructor(options) {
@@ -13,17 +14,24 @@ export default class NzilaVitestReporter {
13
14
  this.options = resolveClientOptions(options);
14
15
  this.runId = this.options.runId ?? randomUUID();
15
16
  }
16
- onTestRunEnd(testModules, _unhandledErrors, _reason) {
17
+ onTestRunStart() {
18
+ this.startedAt = new Date();
19
+ this.sent = false;
20
+ }
21
+ async onTestRunEnd(testModules, _unhandledErrors, _reason) {
17
22
  const tests = collectTestsFromVitestModules(testModules, this.options, this.startedAt);
18
- void this.sendPayload(tests);
23
+ await this.sendPayload(tests);
19
24
  }
20
- /** @deprecated Vitest 2 — still invoked in some setups */
21
- onFinished(files, _errors, _coverage) {
25
+ /**
26
+ * Vitest 2.x only — not part of the current `Reporter` interface.
27
+ * @deprecated Use Vitest 3+ `onTestRunEnd` instead.
28
+ */
29
+ async onFinished(files, _errors, _coverage) {
22
30
  const tests = [];
23
31
  for (const file of files ?? []) {
24
32
  this.walkTasks(file.tasks, file.name.split("/").slice(0, -1), tests);
25
33
  }
26
- void this.sendPayload(tests);
34
+ await this.sendPayload(tests);
27
35
  }
28
36
  async sendPayload(tests) {
29
37
  if (!tests.length) {
@@ -60,12 +68,34 @@ export default class NzilaVitestReporter {
60
68
  if (task.type !== "test")
61
69
  continue;
62
70
  const result = task?.result;
63
- if (!result?.state || result.state === "skip")
71
+ if (!result?.state)
64
72
  continue;
65
73
  const errors = result?.errors?.map((e) => ({
66
74
  message: e?.message ?? String(e),
67
75
  stack: e?.stack,
68
76
  })) ?? [];
77
+ if (result.state === "skip") {
78
+ const skipReason = errors[0]?.message;
79
+ out.push({
80
+ appName: this.options.appName,
81
+ framework,
82
+ startedAt: this.startedAt.toISOString(),
83
+ finishedAt: now,
84
+ describePath,
85
+ feature: this.options.mapFeature?.(describePath, task.name) ??
86
+ describePath[0] ??
87
+ "general",
88
+ context: {
89
+ ...nzilaTestContext(this.options),
90
+ ...(skipReason ? { skipReason } : {}),
91
+ },
92
+ testName: task.name,
93
+ status: "skipped",
94
+ duration: result?.duration ?? 0,
95
+ errors: [],
96
+ });
97
+ continue;
98
+ }
69
99
  const feature = this.options.mapFeature?.(describePath, task.name) ??
70
100
  describePath[0] ??
71
101
  "general";
@@ -76,7 +106,7 @@ export default class NzilaVitestReporter {
76
106
  finishedAt: now,
77
107
  describePath,
78
108
  feature,
79
- context: { layer: this.options.appName },
109
+ context: nzilaTestContext(this.options),
80
110
  testName: task.name,
81
111
  status: this.mapStatus(result?.state),
82
112
  duration: result?.duration ?? 0,
@@ -0,0 +1,8 @@
1
+ /** Example — copy to your app as `nzila.config.ts` */
2
+ export default {
3
+ apiKey: process.env.NZILA_API_KEY ?? "nzl_your_key_here",
4
+ projectId: process.env.NZILA_PROJECT_ID ?? "00000000-0000-0000-0000-000000000000",
5
+ appName: process.env.NZILA_APP_NAME ?? "your-app-name",
6
+ endpoint: process.env.NZILA_APP_URL ?? "https://nzila-kappa.vercel.app",
7
+ framework: "vitest" as const,
8
+ } as const;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copy to your repo root as `nzila.config.ts` (or run `npx @nzila/sdk link`).
3
+ * Values come from the dashboard: API Keys → generate key + copy project ID.
4
+ */
5
+ export default {
6
+ apiKey: process.env.NZILA_API_KEY ?? "nzl_your_key_here",
7
+ projectId: process.env.NZILA_PROJECT_ID ?? "00000000-0000-0000-0000-000000000000",
8
+ appName: process.env.NZILA_APP_NAME ?? "your-app-name",
9
+ endpoint: process.env.NZILA_APP_URL ?? "https://nzila-kappa.vercel.app",
10
+ framework: "vitest" as const,
11
+ } as const;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nzila/sdk",
3
- "version": "0.1.7",
3
+ "version": "0.1.10",
4
4
  "description": "Send test runs to Nzila, trace live feature errors, and onboard via npx @nzila/sdk.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -15,6 +15,7 @@
15
15
  "files": [
16
16
  "dist",
17
17
  "vitest.d.ts",
18
+ "nzila.config.example.ts",
18
19
  "jest.d.ts",
19
20
  "jest.cjs",
20
21
  "mocha.d.ts",