@nzila/sdk 0.1.4 → 0.1.5

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,13 @@ 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
+ ### Reporters not sending?
29
+
30
+ 1. Set **`NZILA_WEBHOOK_URL`**, **`NZILA_API_KEY`**, and **`NZILA_APP_NAME`** (must match the project app name in the dashboard), or pass them in the reporter constructor.
31
+ 2. Run with **`NZILA_DEBUG=1`** to see skip/send messages on stderr (reporters are silent by default).
32
+ 3. **Vitest 3+** uses `onTestRunEnd` — use `@nzila/sdk@0.1.5+`.
33
+ 4. **Jest** with `jest.config.cjs`: use `reporters: [["@nzila/sdk/jest", { … }]]` (CJS entry is included).
34
+
28
35
  ## CLI (onboarding)
29
36
 
30
37
  `npx @nzila/sdk` opens the [Nzila dashboard](https://nzila-kappa.vercel.app) and prints setup commands. No separate CLI package.
@@ -0,0 +1,5 @@
1
+ import type { TestModule } from "vitest/node";
2
+ import type { NzilaClientOptions, NzilaTestCase } from "./types.js";
3
+ /** Collect finished tests from Vitest 3+ `TestModule` reporters API. */
4
+ export declare function collectTestsFromVitestModules(modules: ReadonlyArray<TestModule>, options: NzilaClientOptions, runStartedAt: Date): NzilaTestCase[];
5
+ //# sourceMappingURL=collect-vitest-tests.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collect-vitest-tests.d.ts","sourceRoot":"","sources":["../src/collect-vitest-tests.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAY,UAAU,EAAa,MAAM,aAAa,CAAC;AACnE,OAAO,KAAK,EAAE,kBAAkB,EAAE,aAAa,EAAmB,MAAM,YAAY,CAAC;AAmBrF,wEAAwE;AACxE,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,aAAa,CAAC,UAAU,CAAC,EAClC,OAAO,EAAE,kBAAkB,EAC3B,YAAY,EAAE,IAAI,GACjB,aAAa,EAAE,CAuCjB"}
@@ -0,0 +1,53 @@
1
+ function describePathFor(test) {
2
+ const path = [];
3
+ let parent = test.parent;
4
+ while (parent.type === "suite") {
5
+ path.unshift(parent.name);
6
+ parent = parent.parent;
7
+ }
8
+ return path;
9
+ }
10
+ function mapStatus(state) {
11
+ if (state === "passed")
12
+ return "passed";
13
+ if (state === "failed")
14
+ return "failed";
15
+ if (state === "skipped")
16
+ return "skipped";
17
+ return "pending";
18
+ }
19
+ /** Collect finished tests from Vitest 3+ `TestModule` reporters API. */
20
+ export function collectTestsFromVitestModules(modules, options, runStartedAt) {
21
+ const framework = options.framework ?? "vitest";
22
+ const tests = [];
23
+ const now = new Date().toISOString();
24
+ for (const mod of modules) {
25
+ for (const test of mod.children.allTests()) {
26
+ const result = test.result();
27
+ if (result.state === "pending")
28
+ continue;
29
+ const describePath = describePathFor(test);
30
+ const feature = options.mapFeature?.(describePath, test.name) ??
31
+ describePath[0] ??
32
+ "general";
33
+ const errors = result.errors?.map((e) => ({
34
+ message: e.message ?? String(e),
35
+ stack: e.stack,
36
+ })) ?? [];
37
+ tests.push({
38
+ appName: options.appName,
39
+ framework,
40
+ startedAt: runStartedAt.toISOString(),
41
+ finishedAt: now,
42
+ describePath,
43
+ feature,
44
+ context: { layer: options.appName },
45
+ testName: test.name,
46
+ status: mapStatus(result.state),
47
+ duration: test.diagnostic()?.duration ?? 0,
48
+ errors,
49
+ });
50
+ }
51
+ }
52
+ return tests;
53
+ }
package/dist/deliver.d.ts CHANGED
@@ -7,6 +7,8 @@ export type DeliverOptions = {
7
7
  apiKey: string;
8
8
  /** Retry count on non-2xx (default 3). */
9
9
  maxRetries?: number;
10
+ /** Log skips/failures to stderr (or `NZILA_DEBUG=1`). */
11
+ debug?: boolean;
10
12
  };
11
13
  /**
12
14
  * POST a completed test run payload to Nzila.
@@ -1 +1 @@
1
- {"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../src/deliver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD,wCAAwC;AACxC,MAAM,MAAM,cAAc,GAAG;IAC3B,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAOvC"}
1
+ {"version":3,"file":"deliver.d.ts","sourceRoot":"","sources":["../src/deliver.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAGlD,wCAAwC;AACxC,MAAM,MAAM,cAAc,GAAG;IAC3B,iEAAiE;IACjE,UAAU,EAAE,MAAM,CAAC;IACnB,mDAAmD;IACnD,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,CAAC;AAQF;;;;;;GAMG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,IAAI,CAAC,CAgDf;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,CAAC,EAAE,MAAM,GAChB,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,IAAI,GAAG,SAAS,CAOvC"}
package/dist/deliver.js CHANGED
@@ -1,4 +1,9 @@
1
1
  import { nzilaSafeAsync } from "./silent.js";
2
+ function nzilaLog(message, debug) {
3
+ if (debug || process.env.NZILA_DEBUG === "1") {
4
+ console.warn(`[nzila] ${message}`);
5
+ }
6
+ }
2
7
  /**
3
8
  * POST a completed test run payload to Nzila.
4
9
  * Never throws and never logs — failures are ignored so CI and apps stay unaffected.
@@ -10,10 +15,13 @@ export async function sendNzilaRun(payload, options) {
10
15
  await nzilaSafeAsync(async () => {
11
16
  const webhookUrl = options?.webhookUrl?.trim();
12
17
  const apiKey = options?.apiKey?.trim();
13
- if (!webhookUrl || !apiKey)
18
+ if (!webhookUrl || !apiKey) {
19
+ nzilaLog("Skipped webhook: set webhookUrl + apiKey (or NZILA_WEBHOOK_URL + NZILA_API_KEY).", options.debug);
14
20
  return;
21
+ }
15
22
  const maxRetries = options.maxRetries ?? 3;
16
23
  let attempt = 0;
24
+ let lastStatus;
17
25
  while (attempt < maxRetries) {
18
26
  attempt += 1;
19
27
  try {
@@ -25,7 +33,9 @@ export async function sendNzilaRun(payload, options) {
25
33
  },
26
34
  body: JSON.stringify(payload),
27
35
  });
36
+ lastStatus = res.status;
28
37
  if (res.ok || res.status === 200 || res.status === 202) {
38
+ nzilaLog(`Sent run ${payload.runId} (${payload.tests.length} tests) → ${res.status}`, options.debug);
29
39
  return;
30
40
  }
31
41
  }
@@ -34,6 +44,7 @@ export async function sendNzilaRun(payload, options) {
34
44
  }
35
45
  await new Promise((r) => setTimeout(r, attempt * 1000));
36
46
  }
47
+ nzilaLog(`Webhook failed after ${maxRetries} attempts${lastStatus != null ? ` (last HTTP ${lastStatus})` : ""}.`, options.debug);
37
48
  });
38
49
  }
39
50
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"jest-reporter.d.ts","sourceRoot":"","sources":["../src/jest-reporter.ts"],"names":[],"mappings":"AAEA,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,CAAqB;IAC7C,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;IA8CzD,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;IAmDzD,OAAO,CAAC,SAAS;CAQlB;AAED,4CAA4C;AAC5C,YAAY,EAAE,aAAa,EAAE,CAAC"}
@@ -1,5 +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
4
  /**
4
5
  * Jest reporter: aggregates results and POSTs one {@link sendNzilaRun} payload on `onRunComplete`.
5
6
  *
@@ -11,8 +12,8 @@ import { sendNzilaRun, resolveLocale } from "./deliver.js";
11
12
  export default class NzilaJestReporter {
12
13
  constructor(options, _globalConfig) {
13
14
  this.sent = false;
14
- this.options = options;
15
- this.runId = options.runId ?? randomUUID();
15
+ this.options = resolveClientOptions(options);
16
+ this.runId = this.options.runId ?? randomUUID();
16
17
  }
17
18
  onRunComplete(_contexts, results) {
18
19
  if (this.sent)
@@ -41,8 +42,12 @@ export default class NzilaJestReporter {
41
42
  });
42
43
  }
43
44
  }
44
- if (!tests.length)
45
+ if (!tests.length) {
46
+ if (this.options.debug) {
47
+ console.warn("[nzila] No Jest test results to send.");
48
+ }
45
49
  return;
50
+ }
46
51
  const locale = resolveLocale(this.options.locale);
47
52
  const payload = {
48
53
  runId: this.runId,
@@ -0,0 +1,6 @@
1
+ import type { NzilaClientOptions } from "./types.js";
2
+ /** Merge reporter options with `NZILA_*` environment variables. */
3
+ export declare function resolveClientOptions(options: NzilaClientOptions): NzilaClientOptions & {
4
+ debug: boolean;
5
+ };
6
+ //# sourceMappingURL=reporter-options.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,10 @@
1
+ /** Merge reporter options with `NZILA_*` environment variables. */
2
+ export function resolveClientOptions(options) {
3
+ return {
4
+ ...options,
5
+ webhookUrl: options.webhookUrl?.trim() || process.env.NZILA_WEBHOOK_URL?.trim() || "",
6
+ apiKey: options.apiKey?.trim() || process.env.NZILA_API_KEY?.trim() || "",
7
+ appName: options.appName?.trim() || process.env.NZILA_APP_NAME?.trim() || "",
8
+ debug: options.debug === true || process.env.NZILA_DEBUG === "1",
9
+ };
10
+ }
package/dist/types.d.ts CHANGED
@@ -49,5 +49,7 @@ export type NzilaClientOptions = {
49
49
  * Default: `describePath[0] ?? "general"`.
50
50
  */
51
51
  mapFeature?: (describePath: string[], testName: string) => string;
52
+ /** Log delivery skips/success to stderr (or set `NZILA_DEBUG=1`). */
53
+ debug?: boolean;
52
54
  };
53
55
  //# sourceMappingURL=types.d.ts.map
@@ -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;CACnE,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,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,37 +1,21 @@
1
1
  import type { File } from "@vitest/runner";
2
+ import type { Reporter, TestModule } from "vitest/node";
2
3
  import type { NzilaClientOptions } from "./types.js";
3
- interface VitestReporter {
4
- onFinished?: (files?: File[], errors?: unknown[]) => void;
5
- }
6
4
  /**
7
5
  * Vitest reporter that POSTs one webhook per finished run via {@link sendNzilaRun}.
8
- *
9
- * @example
10
- * ```ts
11
- * reporters: [
12
- * "default",
13
- * new NzilaVitestReporter({
14
- * webhookUrl: process.env.NZILA_WEBHOOK_URL!,
15
- * apiKey: process.env.NZILA_API_KEY!,
16
- * appName: "checkout-api",
17
- * mapFeature: (path) => path[0] ?? "general",
18
- * }),
19
- * ],
20
- * ```
6
+ * Supports Vitest 3+ (`onTestRunEnd`) and legacy `onFinished` file tasks.
21
7
  */
22
- export default class NzilaVitestReporter implements VitestReporter {
8
+ export default class NzilaVitestReporter implements Reporter {
23
9
  private readonly options;
24
10
  private startedAt;
25
11
  private sent;
26
12
  private readonly runId;
27
- private tests;
28
- /**
29
- * @param options - Webhook URL, API key, and project `appName` (required).
30
- */
31
13
  constructor(options: NzilaClientOptions);
14
+ onTestRunEnd(testModules: ReadonlyArray<TestModule>): void;
15
+ /** @deprecated Vitest 2 — still invoked in some setups */
32
16
  onFinished(files?: File[], _errors?: unknown[]): void;
17
+ private sendPayload;
33
18
  private walkTasks;
34
19
  private mapStatus;
35
20
  }
36
- export {};
37
21
  //# 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;AAGjD,OAAO,KAAK,EAAE,kBAAkB,EAAmD,MAAM,YAAY,CAAC;AAEtG,UAAU,cAAc;IACtB,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC;CAC3D;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,OAAO,OAAO,mBAAoB,YAAW,cAAc;IAChE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,KAAK,CAAuB;IAEpC;;OAEG;gBACS,OAAO,EAAE,kBAAkB;IAKvC,UAAU,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE;IA2B9C,OAAO,CAAC,SAAS;IAuCjB,OAAO,CAAC,SAAS;CAMlB"}
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,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKxD,OAAO,KAAK,EAAE,kBAAkB,EAAmD,MAAM,YAAY,CAAC;AAEtG;;;GAGG;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,YAAY,CAAC,WAAW,EAAE,aAAa,CAAC,UAAU,CAAC;IAKnD,0DAA0D;IAC1D,UAAU,CAAC,KAAK,CAAC,EAAE,IAAI,EAAE,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE;YAQhC,WAAW;IA2BzB,OAAO,CAAC,SAAS;IAyCjB,OAAO,CAAC,SAAS;CAMlB"}
@@ -1,42 +1,41 @@
1
1
  import { randomUUID } from "node:crypto";
2
+ import { collectTestsFromVitestModules } from "./collect-vitest-tests.js";
2
3
  import { sendNzilaRun, resolveLocale } from "./deliver.js";
4
+ import { resolveClientOptions } from "./reporter-options.js";
3
5
  /**
4
6
  * Vitest reporter that POSTs one webhook per finished run via {@link sendNzilaRun}.
5
- *
6
- * @example
7
- * ```ts
8
- * reporters: [
9
- * "default",
10
- * new NzilaVitestReporter({
11
- * webhookUrl: process.env.NZILA_WEBHOOK_URL!,
12
- * apiKey: process.env.NZILA_API_KEY!,
13
- * appName: "checkout-api",
14
- * mapFeature: (path) => path[0] ?? "general",
15
- * }),
16
- * ],
17
- * ```
7
+ * Supports Vitest 3+ (`onTestRunEnd`) and legacy `onFinished` file tasks.
18
8
  */
19
9
  export default class NzilaVitestReporter {
20
- /**
21
- * @param options - Webhook URL, API key, and project `appName` (required).
22
- */
23
10
  constructor(options) {
24
11
  this.startedAt = new Date();
25
12
  this.sent = false;
26
- this.tests = [];
27
- this.options = options;
28
- this.runId = options.runId ?? randomUUID();
13
+ this.options = resolveClientOptions(options);
14
+ this.runId = this.options.runId ?? randomUUID();
29
15
  }
16
+ onTestRunEnd(testModules) {
17
+ const tests = collectTestsFromVitestModules(testModules, this.options, this.startedAt);
18
+ void this.sendPayload(tests);
19
+ }
20
+ /** @deprecated Vitest 2 — still invoked in some setups */
30
21
  onFinished(files, _errors) {
22
+ const tests = [];
23
+ for (const file of files ?? []) {
24
+ this.walkTasks(file.tasks, file.name.split("/").slice(0, -1), tests);
25
+ }
26
+ void this.sendPayload(tests);
27
+ }
28
+ async sendPayload(tests) {
29
+ if (!tests.length) {
30
+ if (this.options.debug) {
31
+ console.warn("[nzila] No finished tests to send (check that tests ran).");
32
+ }
33
+ return;
34
+ }
31
35
  if (this.sent)
32
36
  return;
33
37
  this.sent = true;
34
38
  const finishedAt = new Date();
35
- for (const file of files ?? []) {
36
- this.walkTasks(file.tasks, file.name.split("/").slice(0, -1));
37
- }
38
- if (!this.tests.length)
39
- return;
40
39
  const framework = this.options.framework ?? "vitest";
41
40
  const locale = resolveLocale(this.options.locale);
42
41
  const payload = {
@@ -45,22 +44,24 @@ export default class NzilaVitestReporter {
45
44
  framework,
46
45
  startedAt: this.startedAt.toISOString(),
47
46
  finishedAt: finishedAt.toISOString(),
48
- tests: this.tests,
47
+ tests,
49
48
  ...(locale ? { locale } : {}),
50
49
  };
51
- void sendNzilaRun(payload, this.options);
50
+ await sendNzilaRun(payload, this.options);
52
51
  }
53
- walkTasks(tasks, describePath) {
52
+ walkTasks(tasks, describePath, out) {
54
53
  const framework = this.options.framework ?? "vitest";
55
54
  const now = new Date().toISOString();
56
55
  for (const task of tasks) {
57
56
  if (task.type === "suite") {
58
- this.walkTasks(task.tasks, [...describePath, task.name]);
57
+ this.walkTasks(task.tasks, [...describePath, task.name], out);
59
58
  continue;
60
59
  }
61
60
  if (task.type !== "test")
62
61
  continue;
63
62
  const result = task?.result;
63
+ if (!result?.state || result.state === "skip")
64
+ continue;
64
65
  const errors = result?.errors?.map((e) => ({
65
66
  message: e?.message ?? String(e),
66
67
  stack: e?.stack,
@@ -68,7 +69,7 @@ export default class NzilaVitestReporter {
68
69
  const feature = this.options.mapFeature?.(describePath, task.name) ??
69
70
  describePath[0] ??
70
71
  "general";
71
- this.tests.push({
72
+ out.push({
72
73
  appName: this.options.appName,
73
74
  framework,
74
75
  startedAt: this.startedAt.toISOString(),
package/jest.cjs ADDED
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Jest loads reporters via `require()` — this CJS entry delegates to the ESM implementation.
3
+ */
4
+ let reporterModulePromise;
5
+
6
+ function loadReporter() {
7
+ if (!reporterModulePromise) {
8
+ reporterModulePromise = import("./dist/jest-reporter.js");
9
+ }
10
+ return reporterModulePromise;
11
+ }
12
+
13
+ class NzilaJestReporter {
14
+ constructor(options, globalConfig) {
15
+ this._options = options;
16
+ this._globalConfig = globalConfig;
17
+ this._delegate = null;
18
+ }
19
+
20
+ async onRunComplete(contexts, results) {
21
+ if (!this._delegate) {
22
+ const mod = await loadReporter();
23
+ this._delegate = new mod.default(this._options, this._globalConfig);
24
+ }
25
+ return this._delegate.onRunComplete(contexts, results);
26
+ }
27
+ }
28
+
29
+ module.exports = NzilaJestReporter;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nzila/sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
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",
@@ -16,6 +16,7 @@
16
16
  "dist",
17
17
  "vitest.d.ts",
18
18
  "jest.d.ts",
19
+ "jest.cjs",
19
20
  "mocha.d.ts",
20
21
  "runtime.d.ts",
21
22
  "react.d.ts",
@@ -49,6 +50,7 @@
49
50
  "./jest": {
50
51
  "types": "./jest.d.ts",
51
52
  "import": "./dist/jest-reporter.js",
53
+ "require": "./jest.cjs",
52
54
  "default": "./dist/jest-reporter.js"
53
55
  },
54
56
  "./mocha": {