@electr0zed/test-results-dashboard-api-ingest 0.1.0

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 (45) hide show
  1. package/dist/app.d.ts +3 -0
  2. package/dist/app.d.ts.map +1 -0
  3. package/dist/app.js +14 -0
  4. package/dist/app.js.map +1 -0
  5. package/dist/config.d.ts +3 -0
  6. package/dist/config.d.ts.map +1 -0
  7. package/dist/config.js +4 -0
  8. package/dist/config.js.map +1 -0
  9. package/dist/events/dispatcher.d.ts +4 -0
  10. package/dist/events/dispatcher.d.ts.map +1 -0
  11. package/dist/events/dispatcher.js +19 -0
  12. package/dist/events/dispatcher.js.map +1 -0
  13. package/dist/events/handlers/runFinish.handler.d.ts +4 -0
  14. package/dist/events/handlers/runFinish.handler.d.ts.map +1 -0
  15. package/dist/events/handlers/runFinish.handler.js +51 -0
  16. package/dist/events/handlers/runFinish.handler.js.map +1 -0
  17. package/dist/events/handlers/runStart.handler.d.ts +4 -0
  18. package/dist/events/handlers/runStart.handler.d.ts.map +1 -0
  19. package/dist/events/handlers/runStart.handler.js +38 -0
  20. package/dist/events/handlers/runStart.handler.js.map +1 -0
  21. package/dist/events/handlers/specFinish.handler.d.ts +4 -0
  22. package/dist/events/handlers/specFinish.handler.d.ts.map +1 -0
  23. package/dist/events/handlers/specFinish.handler.js +107 -0
  24. package/dist/events/handlers/specFinish.handler.js.map +1 -0
  25. package/dist/index.d.ts +3 -0
  26. package/dist/index.d.ts.map +1 -0
  27. package/dist/index.js +10 -0
  28. package/dist/index.js.map +1 -0
  29. package/dist/routes/ingest.d.ts +5 -0
  30. package/dist/routes/ingest.d.ts.map +1 -0
  31. package/dist/routes/ingest.js +29 -0
  32. package/dist/routes/ingest.js.map +1 -0
  33. package/dist/services/context.d.ts +3 -0
  34. package/dist/services/context.d.ts.map +1 -0
  35. package/dist/services/context.js +8 -0
  36. package/dist/services/context.js.map +1 -0
  37. package/dist/services/db.d.ts +3 -0
  38. package/dist/services/db.d.ts.map +1 -0
  39. package/dist/services/db.js +11 -0
  40. package/dist/services/db.js.map +1 -0
  41. package/dist/types.d.ts +16 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +2 -0
  44. package/dist/types.js.map +1 -0
  45. package/package.json +31 -0
package/dist/app.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { Config, HonoEnv } from './types';
2
+ export declare function createApp(config: Config): import("hono/hono-base").HonoBase<HonoEnv, import("hono/types").BlankSchema, string, string>;
3
+ //# sourceMappingURL=app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.d.ts","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAI/C,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,gGAcvC"}
package/dist/app.js ADDED
@@ -0,0 +1,14 @@
1
+ import { Hono } from 'hono';
2
+ import ingestRoute from './routes/ingest';
3
+ import { createAppContext } from './services/context';
4
+ export function createApp(config) {
5
+ const app = new Hono().basePath(config.basePath ?? '/ingest');
6
+ const appCtx = createAppContext(config);
7
+ app.use('*', async (c, next) => {
8
+ c.set('ctx', appCtx);
9
+ await next();
10
+ });
11
+ app.route('/', ingestRoute);
12
+ return app;
13
+ }
14
+ //# sourceMappingURL=app.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app.js","sourceRoot":"","sources":["../src/app.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,WAAW,MAAM,iBAAiB,CAAC;AAC1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,MAAM,UAAU,SAAS,CAAC,MAAc;IACvC,MAAM,GAAG,GAAG,IAAI,IAAI,EAAW,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,IAAI,SAAS,CAAC,CAAC;IAEvE,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAExC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QAC9B,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAErB,MAAM,IAAI,EAAE,CAAC;IACd,CAAC,CAAC,CAAC;IAEH,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IAE5B,OAAO,GAAG,CAAC;AACZ,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Config } from './types';
2
+ export declare function defineConfig(config: Config): Config;
3
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEtC,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEnD"}
package/dist/config.js ADDED
@@ -0,0 +1,4 @@
1
+ export function defineConfig(config) {
2
+ return config;
3
+ }
4
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,YAAY,CAAC,MAAc;IAC1C,OAAO,MAAM,CAAC;AACf,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { DashboardEvent } from '@electr0zed/test-results-dashboard-core';
2
+ import { AppCtx } from '../types';
3
+ export declare function dispatchEvent(ctx: AppCtx, event: DashboardEvent): Promise<void>;
4
+ //# sourceMappingURL=dispatcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher.d.ts","sourceRoot":"","sources":["../../src/events/dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAI9E,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC,wBAAsB,aAAa,CAClC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,cAAc,GACnB,OAAO,CAAC,IAAI,CAAC,CAcf"}
@@ -0,0 +1,19 @@
1
+ import { handleRunStart } from './handlers/runStart.handler';
2
+ import { handleSpecFinish } from './handlers/specFinish.handler';
3
+ import { handleRunFinish } from './handlers/runFinish.handler';
4
+ export async function dispatchEvent(ctx, event) {
5
+ switch (event.type) {
6
+ case 'run:start':
7
+ return handleRunStart(ctx, event);
8
+ case 'spec:finish':
9
+ return handleSpecFinish(ctx, event);
10
+ case 'run:finish':
11
+ return handleRunFinish(ctx, event);
12
+ default:
13
+ return assertNever(event);
14
+ }
15
+ }
16
+ function assertNever(value) {
17
+ throw new Error(`Unhandled event: ${JSON.stringify(value)}`);
18
+ }
19
+ //# sourceMappingURL=dispatcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatcher.js","sourceRoot":"","sources":["../../src/events/dispatcher.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AACjE,OAAO,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AAG/D,MAAM,CAAC,KAAK,UAAU,aAAa,CAClC,GAAW,EACX,KAAqB;IAErB,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,WAAW;YACf,OAAO,cAAc,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAEnC,KAAK,aAAa;YACjB,OAAO,gBAAgB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAErC,KAAK,YAAY;YAChB,OAAO,eAAe,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAEpC;YACC,OAAO,WAAW,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;AACF,CAAC;AAED,SAAS,WAAW,CAAC,KAAY;IAChC,MAAM,IAAI,KAAK,CAAC,oBAAoB,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAC9D,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { RunFinishEvent } from '@electr0zed/test-results-dashboard-core';
2
+ import type { AppCtx } from '../../types';
3
+ export declare function handleRunFinish(ctx: AppCtx, event: RunFinishEvent): Promise<void>;
4
+ //# sourceMappingURL=runFinish.handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runFinish.handler.d.ts","sourceRoot":"","sources":["../../../src/events/handlers/runFinish.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,wBAAsB,eAAe,CACpC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,cAAc,GACnB,OAAO,CAAC,IAAI,CAAC,CA6Cf"}
@@ -0,0 +1,51 @@
1
+ export async function handleRunFinish(ctx, event) {
2
+ const { db } = ctx;
3
+ const run = await db.run.findUnique({
4
+ where: {
5
+ publicId: event.payload.runId,
6
+ },
7
+ select: {
8
+ id: true,
9
+ },
10
+ });
11
+ if (!run) {
12
+ throw new Error(`Run not found: ${event.payload.runId}`);
13
+ }
14
+ if ('message' in event.payload) {
15
+ await db.run.update({
16
+ where: {
17
+ id: run.id,
18
+ },
19
+ data: {
20
+ status: 'failed',
21
+ endedAt: new Date(),
22
+ },
23
+ });
24
+ return;
25
+ }
26
+ await db.run.update({
27
+ where: {
28
+ id: run.id,
29
+ },
30
+ data: {
31
+ status: 'finished',
32
+ framework: event.payload.run.runner,
33
+ frameworkVersion: event.payload.run.runnerVersion ?? 'unknown',
34
+ browser: event.payload.run.browserName ?? 'unknown',
35
+ browserVersion: event.payload.run.browserVersion ?? 'unknown',
36
+ os: formatOs(event.payload.run.osName, event.payload.run.osVersion),
37
+ startedAt: parseOptionalDate(event.payload.run.startedAt),
38
+ endedAt: parseOptionalDate(event.payload.run.endedAt) ?? new Date(),
39
+ },
40
+ });
41
+ }
42
+ function formatOs(osName, osVersion) {
43
+ return [osName, osVersion].filter(Boolean).join(' ') || 'unknown';
44
+ }
45
+ function parseOptionalDate(value) {
46
+ if (!value) {
47
+ return undefined;
48
+ }
49
+ return new Date(value);
50
+ }
51
+ //# sourceMappingURL=runFinish.handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runFinish.handler.js","sourceRoot":"","sources":["../../../src/events/handlers/runFinish.handler.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,eAAe,CACpC,GAAW,EACX,KAAqB;IAErB,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IAEnB,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,UAAU,CAAC;QACnC,KAAK,EAAE;YACN,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK;SAC7B;QACD,MAAM,EAAE;YACP,EAAE,EAAE,IAAI;SACR;KACD,CAAC,CAAC;IAEH,IAAI,CAAC,GAAG,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CAAC,kBAAkB,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,SAAS,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAChC,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;YACnB,KAAK,EAAE;gBACN,EAAE,EAAE,GAAG,CAAC,EAAE;aACV;YACD,IAAI,EAAE;gBACL,MAAM,EAAE,QAAQ;gBAChB,OAAO,EAAE,IAAI,IAAI,EAAE;aACnB;SACD,CAAC,CAAC;QAEH,OAAO;IACR,CAAC;IAED,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;QACnB,KAAK,EAAE;YACN,EAAE,EAAE,GAAG,CAAC,EAAE;SACV;QACD,IAAI,EAAE;YACL,MAAM,EAAE,UAAU;YAClB,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM;YACnC,gBAAgB,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,SAAS;YAC9D,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,SAAS;YACnD,cAAc,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,SAAS;YAC7D,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;YACnE,SAAS,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;YACzD,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,IAAI,EAAE;SACnE;KACD,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAChB,MAA0B,EAC1B,SAA6B;IAE7B,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC;AACnE,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IACnD,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { RunStartEvent } from '@electr0zed/test-results-dashboard-core';
2
+ import type { AppCtx } from '../../types';
3
+ export declare function handleRunStart(ctx: AppCtx, event: RunStartEvent): Promise<void>;
4
+ //# sourceMappingURL=runStart.handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runStart.handler.d.ts","sourceRoot":"","sources":["../../../src/events/handlers/runStart.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,wBAAsB,cAAc,CACnC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,aAAa,GAClB,OAAO,CAAC,IAAI,CAAC,CA4Bf"}
@@ -0,0 +1,38 @@
1
+ export async function handleRunStart(ctx, event) {
2
+ const { db } = ctx;
3
+ await db.run.upsert({
4
+ where: {
5
+ publicId: event.payload.id,
6
+ },
7
+ create: {
8
+ publicId: event.payload.id,
9
+ framework: event.payload.runner,
10
+ frameworkVersion: event.payload.runnerVersion ?? 'unknown',
11
+ browser: event.payload.browserName ?? 'unknown',
12
+ browserVersion: event.payload.browserVersion ?? 'unknown',
13
+ os: formatOs(event.payload.osName, event.payload.osVersion),
14
+ status: 'running',
15
+ startedAt: parseOptionalDate(event.payload.startedAt) ?? new Date(),
16
+ },
17
+ update: {
18
+ framework: event.payload.runner,
19
+ frameworkVersion: event.payload.runnerVersion ?? 'unknown',
20
+ browser: event.payload.browserName ?? 'unknown',
21
+ browserVersion: event.payload.browserVersion ?? 'unknown',
22
+ os: formatOs(event.payload.osName, event.payload.osVersion),
23
+ status: 'running',
24
+ startedAt: parseOptionalDate(event.payload.startedAt) ?? new Date(),
25
+ endedAt: null,
26
+ },
27
+ });
28
+ }
29
+ function formatOs(osName, osVersion) {
30
+ return [osName, osVersion].filter(Boolean).join(' ') || 'unknown';
31
+ }
32
+ function parseOptionalDate(value) {
33
+ if (!value) {
34
+ return undefined;
35
+ }
36
+ return new Date(value);
37
+ }
38
+ //# sourceMappingURL=runStart.handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runStart.handler.js","sourceRoot":"","sources":["../../../src/events/handlers/runStart.handler.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,KAAK,UAAU,cAAc,CACnC,GAAW,EACX,KAAoB;IAEpB,MAAM,EAAE,EAAE,EAAE,GAAG,GAAG,CAAC;IAEnB,MAAM,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC;QACnB,KAAK,EAAE;YACN,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE;SAC1B;QACD,MAAM,EAAE;YACP,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,EAAE;YAC1B,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM;YAC/B,gBAAgB,EAAE,KAAK,CAAC,OAAO,CAAC,aAAa,IAAI,SAAS;YAC1D,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,WAAW,IAAI,SAAS;YAC/C,cAAc,EAAE,KAAK,CAAC,OAAO,CAAC,cAAc,IAAI,SAAS;YACzD,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;YAC3D,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,IAAI,IAAI,EAAE;SACnE;QACD,MAAM,EAAE;YACP,SAAS,EAAE,KAAK,CAAC,OAAO,CAAC,MAAM;YAC/B,gBAAgB,EAAE,KAAK,CAAC,OAAO,CAAC,aAAa,IAAI,SAAS;YAC1D,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,WAAW,IAAI,SAAS;YAC/C,cAAc,EAAE,KAAK,CAAC,OAAO,CAAC,cAAc,IAAI,SAAS;YACzD,EAAE,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;YAC3D,MAAM,EAAE,SAAS;YACjB,SAAS,EAAE,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,IAAI,IAAI,EAAE;YACnE,OAAO,EAAE,IAAI;SACb;KACD,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAChB,MAA0B,EAC1B,SAA6B;IAE7B,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,SAAS,CAAC;AACnE,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAyB;IACnD,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,OAAO,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC;AACxB,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { SpecFinishEvent } from '@electr0zed/test-results-dashboard-core';
2
+ import type { AppCtx } from '../../types';
3
+ export declare function handleSpecFinish(ctx: AppCtx, event: SpecFinishEvent): Promise<void>;
4
+ //# sourceMappingURL=specFinish.handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specFinish.handler.d.ts","sourceRoot":"","sources":["../../../src/events/handlers/specFinish.handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,yCAAyC,CAAC;AAC/E,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,wBAAsB,gBAAgB,CACrC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,eAAe,GACpB,OAAO,CAAC,IAAI,CAAC,CAkGf"}
@@ -0,0 +1,107 @@
1
+ export async function handleSpecFinish(ctx, event) {
2
+ const { db } = ctx;
3
+ const run = await db.run.findUnique({
4
+ where: {
5
+ publicId: event.payload.runId,
6
+ },
7
+ select: {
8
+ id: true,
9
+ },
10
+ });
11
+ if (!run) {
12
+ throw new Error(`Run not found: ${event.payload.runId}`);
13
+ }
14
+ const filename = getSpecFilename(event.payload.spec);
15
+ const spec = await db.spec.upsert({
16
+ where: {
17
+ runId_filename: {
18
+ runId: run.id,
19
+ filename,
20
+ },
21
+ },
22
+ create: {
23
+ runId: run.id,
24
+ filename,
25
+ tests: event.payload.spec.tests,
26
+ passed: event.payload.spec.passes,
27
+ failed: event.payload.spec.failures,
28
+ pending: event.payload.spec.pending,
29
+ skipped: event.payload.spec.skipped,
30
+ duration: event.payload.spec.duration ?? 0,
31
+ startedAt: new Date(event.payload.spec.startedAt),
32
+ endedAt: new Date(event.payload.spec.endedAt),
33
+ status: getSpecStatus(event),
34
+ message: getSpecMessage(event),
35
+ },
36
+ update: {
37
+ tests: event.payload.spec.tests,
38
+ passed: event.payload.spec.passes,
39
+ failed: event.payload.spec.failures,
40
+ pending: event.payload.spec.pending,
41
+ skipped: event.payload.spec.skipped,
42
+ duration: event.payload.spec.duration ?? 0,
43
+ startedAt: new Date(event.payload.spec.startedAt),
44
+ endedAt: new Date(event.payload.spec.endedAt),
45
+ status: getSpecStatus(event),
46
+ message: getSpecMessage(event),
47
+ },
48
+ select: {
49
+ id: true,
50
+ },
51
+ });
52
+ await db.specTestAttempt.deleteMany({
53
+ where: {
54
+ specTest: {
55
+ specId: spec.id,
56
+ },
57
+ },
58
+ });
59
+ await db.specTest.deleteMany({
60
+ where: {
61
+ specId: spec.id,
62
+ },
63
+ });
64
+ for (const test of event.payload.tests) {
65
+ const specTest = await db.specTest.create({
66
+ data: {
67
+ specId: spec.id,
68
+ title: test.title.join(' > '),
69
+ status: test.status,
70
+ duration: test.duration ?? 0,
71
+ message: test.displayError,
72
+ },
73
+ select: {
74
+ id: true,
75
+ },
76
+ });
77
+ if (!test.attempts?.length) {
78
+ continue;
79
+ }
80
+ await db.specTestAttempt.createMany({
81
+ data: test.attempts.map((attempt) => ({
82
+ specTestId: specTest.id,
83
+ status: attempt.state ?? test.status,
84
+ message: attempt.state === 'failed'
85
+ ? test.displayError
86
+ : undefined,
87
+ })),
88
+ });
89
+ }
90
+ }
91
+ function getSpecFilename(spec) {
92
+ return spec.relative ?? spec.name;
93
+ }
94
+ function getSpecStatus(event) {
95
+ if (event.payload.spec.failures > 0) {
96
+ return 'failed';
97
+ }
98
+ if (event.payload.spec.pending > 0 || event.payload.spec.skipped > 0) {
99
+ return 'partial';
100
+ }
101
+ return 'passed';
102
+ }
103
+ function getSpecMessage(event) {
104
+ const firstFailedTest = event.payload.tests.find((test) => test.displayError);
105
+ return firstFailedTest?.displayError;
106
+ }
107
+ //# sourceMappingURL=specFinish.handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"specFinish.handler.js","sourceRoot":"","sources":["../../../src/events/handlers/specFinish.handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Config } from './types';
2
+ export declare function createIngestionWorker(cfg: Config): ExportedHandler;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAEtC,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAQlE"}
package/dist/index.js ADDED
@@ -0,0 +1,10 @@
1
+ import { createApp } from './app';
2
+ export function createIngestionWorker(cfg) {
3
+ const app = createApp(cfg);
4
+ return {
5
+ fetch(request, env, ctx) {
6
+ return app.fetch(request, env, ctx);
7
+ },
8
+ };
9
+ }
10
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGlC,MAAM,UAAU,qBAAqB,CAAC,GAAW;IAChD,MAAM,GAAG,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAE3B,OAAO;QACN,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG;YACtB,OAAO,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QACrC,CAAC;KACD,CAAC;AACH,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Hono } from 'hono';
2
+ import type { HonoEnv } from '../types';
3
+ declare const app: Hono<HonoEnv, import("hono/types").BlankSchema, "/">;
4
+ export default app;
5
+ //# sourceMappingURL=ingest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/routes/ingest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG5B,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAExC,QAAA,MAAM,GAAG,sDAAsB,CAAC;AAgChC,eAAe,GAAG,CAAC"}
@@ -0,0 +1,29 @@
1
+ import { Hono } from 'hono';
2
+ import { dispatchEvent } from '../events/dispatcher';
3
+ import { IngestEventRequestSchema } from '@electr0zed/test-results-dashboard-core';
4
+ const app = new Hono();
5
+ app.post('/events', async (c) => {
6
+ const ctx = c.get('ctx');
7
+ const authHeader = c.req.header('authorization');
8
+ if (!authHeader) {
9
+ return c.json({ error: 'Unauthorized' }, 401);
10
+ }
11
+ if (authHeader !== `Bearer ${ctx.cfg.ingestionSecret}`) {
12
+ return c.json({ error: 'Forbidden' }, 403);
13
+ }
14
+ let body;
15
+ try {
16
+ body = await c.req.json();
17
+ }
18
+ catch (error) {
19
+ return c.json({ error: 'Invalid JSON' }, 400);
20
+ }
21
+ const parseResult = IngestEventRequestSchema.safeParse(body);
22
+ if (!parseResult.success) {
23
+ return c.json({ error: 'Invalid request body' }, 400);
24
+ }
25
+ await dispatchEvent(ctx, parseResult.data.event);
26
+ return c.json({ ok: true });
27
+ });
28
+ export default app;
29
+ //# sourceMappingURL=ingest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/routes/ingest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,yCAAyC,CAAC;AAGnF,MAAM,GAAG,GAAG,IAAI,IAAI,EAAW,CAAC;AAEhC,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAC,CAAC,EAAE,EAAE;IAC9B,MAAM,GAAG,GAAG,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEtB,MAAM,UAAU,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACjD,IAAI,CAAC,UAAU,EAAE,CAAC;QACd,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,UAAU,KAAK,UAAU,GAAG,CAAC,GAAG,CAAC,eAAe,EAAE,EAAE,CAAC;QACrD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,GAAG,CAAC,CAAC;IAC/C,CAAC;IAED,IAAI,IAAwB,CAAC;IAE7B,IAAI,CAAC;QACD,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAsB,CAAC;IAClD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACb,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,GAAG,CAAC,CAAC;IAClD,CAAC;IAED,MAAM,WAAW,GAAG,wBAAwB,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAC7D,IAAI,CAAC,WAAW,CAAC,OAAO,EAAE,CAAC;QACvB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,sBAAsB,EAAE,EAAE,GAAG,CAAC,CAAC;IAC1D,CAAC;IAED,MAAM,aAAa,CAAC,GAAG,EAAE,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEjD,OAAO,CAAC,CAAC,IAAI,CAAsB,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC;AACrD,CAAC,CAAC,CAAC;AAEH,eAAe,GAAG,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { AppCtx, Config } from '../types';
2
+ export declare function createAppContext(cfg: Config): AppCtx;
3
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/services/context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAE/C,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAKpD"}
@@ -0,0 +1,8 @@
1
+ import { getPrismaClient } from './db';
2
+ export function createAppContext(cfg) {
3
+ return {
4
+ cfg: cfg,
5
+ db: getPrismaClient(cfg.d1),
6
+ };
7
+ }
8
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/services/context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AAGvC,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAC3C,OAAO;QACA,GAAG,EAAE,GAAG;QACd,EAAE,EAAE,eAAe,CAAC,GAAG,CAAC,EAAE,CAAC;KAC3B,CAAC;AACH,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { PrismaClient } from '@electr0zed/test-results-dashboard-db';
2
+ export declare function getPrismaClient(db: D1Database): PrismaClient;
3
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/services/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAKrE,wBAAgB,eAAe,CAAC,EAAE,EAAE,UAAU,GAAG,YAAY,CAO5D"}
@@ -0,0 +1,11 @@
1
+ import { PrismaClient } from '@electr0zed/test-results-dashboard-db';
2
+ import { PrismaD1 } from '@prisma/adapter-d1';
3
+ let prisma;
4
+ export function getPrismaClient(db) {
5
+ if (!prisma) {
6
+ const adapter = new PrismaD1(db);
7
+ prisma = new PrismaClient({ adapter });
8
+ }
9
+ return prisma;
10
+ }
11
+ //# sourceMappingURL=db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/services/db.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AACrE,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAE9C,IAAI,MAAgC,CAAC;AAErC,MAAM,UAAU,eAAe,CAAC,EAAc;IAC1C,IAAI,CAAC,MAAM,EAAE,CAAC;QACV,MAAM,OAAO,GAAG,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC;QACjC,MAAM,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { PrismaClient } from '@electr0zed/test-results-dashboard-db';
2
+ export type Config = {
3
+ basePath?: string;
4
+ ingestionSecret: string;
5
+ d1: D1Database;
6
+ };
7
+ export type AppCtx = {
8
+ cfg: Config;
9
+ db: PrismaClient;
10
+ };
11
+ export type HonoEnv = {
12
+ Variables: {
13
+ ctx: AppCtx;
14
+ };
15
+ };
16
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uCAAuC,CAAC;AAE1E,MAAM,MAAM,MAAM,GAAG;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,MAAM,CAAC;IACxB,EAAE,EAAE,UAAU,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,MAAM,GAAG;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,YAAY,CAAC;CACpB,CAAA;AAED,MAAM,MAAM,OAAO,GAAG;IAClB,SAAS,EAAE;QACP,GAAG,EAAE,MAAM,CAAC;KACf,CAAC;CACL,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@electr0zed/test-results-dashboard-api-ingest",
3
+ "version": "0.1.0",
4
+ "license": "ISC",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "clean": "rimraf dist"
14
+ },
15
+ "dependencies": {
16
+ "@electr0zed/test-results-dashboard-core": "^0.1.0",
17
+ "@electr0zed/test-results-dashboard-db": "^0.1.0",
18
+ "@prisma/adapter-d1": "^7.8.0",
19
+ "hono": "^4.11.9",
20
+ "zod": "^4.4.3"
21
+ },
22
+ "devDependencies": {
23
+ "@cloudflare/workers-types": "^4.20260611.1",
24
+ "rimraf": "^6.1.3",
25
+ "typescript": "^5.9.3"
26
+ },
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "git+https://github.com/electr0zed/test-results-dashboard.git"
30
+ }
31
+ }