@futdevpro/dynamo-e2e 1.18.16 → 1.18.21

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 (63) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/README.md +17 -0
  3. package/bin/dye2e.js +36 -1
  4. package/build/_modules/generators/_collections/cli-runner.util.d.ts.map +1 -1
  5. package/build/_modules/generators/_collections/cli-runner.util.js +9 -1
  6. package/build/_modules/generators/_collections/cli-runner.util.js.map +1 -1
  7. package/build/_modules/presets/_collections/ci-timeout.util.d.ts +21 -0
  8. package/build/_modules/presets/_collections/ci-timeout.util.d.ts.map +1 -0
  9. package/build/_modules/presets/_collections/ci-timeout.util.js +34 -0
  10. package/build/_modules/presets/_collections/ci-timeout.util.js.map +1 -0
  11. package/build/_modules/presets/_collections/global-setup.factory.d.ts +11 -0
  12. package/build/_modules/presets/_collections/global-setup.factory.d.ts.map +1 -1
  13. package/build/_modules/presets/_collections/global-setup.factory.js +54 -9
  14. package/build/_modules/presets/_collections/global-setup.factory.js.map +1 -1
  15. package/build/_modules/presets/_collections/playwright-config.factory.d.ts.map +1 -1
  16. package/build/_modules/presets/_collections/playwright-config.factory.js +9 -4
  17. package/build/_modules/presets/_collections/playwright-config.factory.js.map +1 -1
  18. package/build/_modules/presets/_models/interfaces/global-setup-options.interface.d.ts +10 -0
  19. package/build/_modules/presets/_models/interfaces/global-setup-options.interface.d.ts.map +1 -1
  20. package/build/_modules/presets/_models/interfaces/playwright-config-options.interface.d.ts +20 -1
  21. package/build/_modules/presets/_models/interfaces/playwright-config-options.interface.d.ts.map +1 -1
  22. package/build/_modules/presets/index.d.ts +2 -1
  23. package/build/_modules/presets/index.d.ts.map +1 -1
  24. package/build/_modules/presets/index.js +6 -1
  25. package/build/_modules/presets/index.js.map +1 -1
  26. package/build/_modules/runtime/_collections/journey-context.util.d.ts +72 -0
  27. package/build/_modules/runtime/_collections/journey-context.util.d.ts.map +1 -0
  28. package/build/_modules/runtime/_collections/journey-context.util.js +137 -0
  29. package/build/_modules/runtime/_collections/journey-context.util.js.map +1 -0
  30. package/build/_modules/runtime/_models/journey-context.interface.d.ts +31 -0
  31. package/build/_modules/runtime/_models/journey-context.interface.d.ts.map +1 -0
  32. package/build/_modules/runtime/_models/journey-context.interface.js +3 -0
  33. package/build/_modules/runtime/_models/journey-context.interface.js.map +1 -0
  34. package/build/_modules/runtime/index.d.ts +2 -0
  35. package/build/_modules/runtime/index.d.ts.map +1 -1
  36. package/build/_modules/runtime/index.js +2 -0
  37. package/build/_modules/runtime/index.js.map +1 -1
  38. package/build/_modules/vqa/_collections/constants/vqa-standard-viewports.const.d.ts +1 -1
  39. package/build/_modules/vqa/_collections/constants/vqa-standard-viewports.const.js +1 -1
  40. package/build/_modules/vqa/_collections/index.d.ts +1 -0
  41. package/build/_modules/vqa/_collections/index.d.ts.map +1 -1
  42. package/build/_modules/vqa/_collections/index.js +1 -0
  43. package/build/_modules/vqa/_collections/index.js.map +1 -1
  44. package/build/_modules/vqa/_collections/utils/review-gate.util.d.ts +36 -0
  45. package/build/_modules/vqa/_collections/utils/review-gate.util.d.ts.map +1 -0
  46. package/build/_modules/vqa/_collections/utils/review-gate.util.js +232 -0
  47. package/build/_modules/vqa/_collections/utils/review-gate.util.js.map +1 -0
  48. package/build/_modules/vqa/_models/interfaces/index.d.ts +1 -0
  49. package/build/_modules/vqa/_models/interfaces/index.d.ts.map +1 -1
  50. package/build/_modules/vqa/_models/interfaces/index.js +1 -0
  51. package/build/_modules/vqa/_models/interfaces/index.js.map +1 -1
  52. package/build/_modules/vqa/_models/interfaces/review-gate.interface.d.ts +55 -0
  53. package/build/_modules/vqa/_models/interfaces/review-gate.interface.d.ts.map +1 -0
  54. package/build/_modules/vqa/_models/interfaces/review-gate.interface.js +3 -0
  55. package/build/_modules/vqa/_models/interfaces/review-gate.interface.js.map +1 -0
  56. package/build/_modules/vqa/generators/ai-agent-readme.template.js +8 -8
  57. package/build/_modules/vqa/generators/capture-spec.generator.js +1 -1
  58. package/build/_modules/vqa/generators/review-bundle.generator.d.ts +16 -4
  59. package/build/_modules/vqa/generators/review-bundle.generator.d.ts.map +1 -1
  60. package/build/_modules/vqa/generators/review-bundle.generator.js +31 -5
  61. package/build/_modules/vqa/generators/review-bundle.generator.js.map +1 -1
  62. package/build/index.js +1 -1
  63. package/package.json +1 -1
@@ -0,0 +1,72 @@
1
+ import { DyE2E_JourneyCleanupReport_Interface, DyE2E_JourneyTestApi_Interface } from '../_models/journey-context.interface';
2
+ /**
3
+ * BFR-FSMED-007 — typed user-journey context: carries the REAL ids / state a step created to the next step
4
+ * (state handoff), and owns a cleanup registry that removes everything the journey created, even when a step failed.
5
+ *
6
+ * - `step(name, fn)`: runs a step with the current state; the returned patch is merged into the state (handoff).
7
+ * A failed step is recorded (`failedStep`) and rethrown — the journey stops, the cleanups still run.
8
+ * - `require(key)`: a handoff guard — a step that needs an earlier step's id fails with the step + key named,
9
+ * instead of calling the API with `undefined`.
10
+ * - `registerCleanup(label, fn)`: register right AFTER the thing exists (create → register), so a later failure
11
+ * cannot leak it. Cleanups run LIFO (dependants before their parents).
12
+ * - `runCleanup()`: runs EVERY cleanup, each isolated (one failure does not skip the rest), once (idempotent), then
13
+ * throws if any failed — a leaked entity is a red journey, never a silent one.
14
+ *
15
+ * With Playwright: `DyE2E_useJourney(test, { name, initialState })` (serial mode + `afterAll` cleanup).
16
+ */
17
+ export declare class DyE2E_JourneyContext<TState extends object> {
18
+ readonly name: string;
19
+ private currentState;
20
+ private readonly cleanups;
21
+ private readonly steps;
22
+ private failed;
23
+ private cleanupReport;
24
+ /** Creates the context with the journey name and its initial state. */
25
+ constructor(set: {
26
+ name: string;
27
+ initialState: TState;
28
+ });
29
+ /** The current state (a copy — change it through a step's returned patch or `handoff`). */
30
+ get state(): Readonly<TState>;
31
+ /** The names of the steps that completed, in order. */
32
+ get completedSteps(): string[];
33
+ /** The name of the step that threw, if any. */
34
+ get failedStep(): string | undefined;
35
+ /** Runs one journey step; its returned patch is merged into the state (the handoff to the next step). */
36
+ step(name: string, fn: (state: Readonly<TState>) => Promise<Partial<TState> | void> | Partial<TState> | void): Promise<Readonly<TState>>;
37
+ /** Merges a patch into the state outside a step (e.g. from a Playwright `test` body). */
38
+ handoff(patch: Partial<TState>): void;
39
+ /** Returns a state value an earlier step must have handed off; throws with the key named when it is missing. */
40
+ require<TKey extends keyof TState>(key: TKey): NonNullable<TState[TKey]>;
41
+ /** Registers a cleanup for something the journey created — call it right after the creation. */
42
+ registerCleanup(label: string, fn: () => Promise<unknown> | unknown): void;
43
+ /**
44
+ * Runs every registered cleanup LIFO, each isolated; idempotent (a second call returns the first report).
45
+ * Throws after ALL of them ran when any failed, naming the failed labels.
46
+ */
47
+ runCleanup(): Promise<DyE2E_JourneyCleanupReport_Interface>;
48
+ }
49
+ /**
50
+ * BFR-FSMED-007 — Playwright binding: puts the calling `describe` into serial mode (the steps share state) and
51
+ * registers `afterAll → runCleanup()`, so the cleanups run even when a step failed. Call it inside `test.describe`.
52
+ *
53
+ * ```ts
54
+ * test.describe('J-002 post lifecycle', () => {
55
+ * const journey = DyE2E_useJourney(test, { name: 'J-002', initialState: { postId: undefined as string | undefined } });
56
+ * test('create', async ({ request }) => {
57
+ * const id = await createPost(request);
58
+ * journey.registerCleanup(`delete post ${id}`, () => deletePostWithOwnClient(id));
59
+ * journey.handoff({ postId: id });
60
+ * });
61
+ * test('read', async ({ request }) => { await readPost(request, journey.require('postId')); });
62
+ * });
63
+ * ```
64
+ *
65
+ * ⚠️ A cleanup runs in `afterAll`, AFTER the test-scoped fixtures (`page`, `request`) were disposed — it must not
66
+ * capture them: use its own client (`fetch`, or `playwright.request.newContext()` created inside the cleanup).
67
+ */
68
+ export declare function DyE2E_useJourney<TState extends object>(test: DyE2E_JourneyTestApi_Interface, set: {
69
+ name: string;
70
+ initialState: TState;
71
+ }): DyE2E_JourneyContext<TState>;
72
+ //# sourceMappingURL=journey-context.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey-context.util.d.ts","sourceRoot":"","sources":["../../../../src/_modules/runtime/_collections/journey-context.util.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,oCAAoC,EAEpC,8BAA8B,EAC/B,MAAM,sCAAsC,CAAC;AAQ9C;;;;;;;;;;;;;;GAcG;AACH,qBAAa,oBAAoB,CAAC,MAAM,SAAS,MAAM;IACrD,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6C;IACtE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgB;IACtC,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,aAAa,CAAmD;IAExE,uEAAuE;gBAC3D,GAAG,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE;IAKvD,2FAA2F;IAC3F,IAAW,KAAK,IAAI,QAAQ,CAAC,MAAM,CAAC,CAEnC;IAED,uDAAuD;IACvD,IAAW,cAAc,IAAI,MAAM,EAAE,CAEpC;IAED,+CAA+C;IAC/C,IAAW,UAAU,IAAI,MAAM,GAAG,SAAS,CAE1C;IAED,yGAAyG;IAC5F,IAAI,CACf,IAAI,EAAE,MAAM,EACZ,EAAE,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,GACxF,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAsB5B,yFAAyF;IAClF,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI;IAI5C,gHAAgH;IACzG,OAAO,CAAC,IAAI,SAAS,MAAM,MAAM,EAAE,GAAG,EAAE,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAa/E,gGAAgG;IACzF,eAAe,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,GAAG,IAAI;IAOjF;;;OAGG;IACU,UAAU,IAAI,OAAO,CAAC,oCAAoC,CAAC;CA8BzE;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,SAAS,MAAM,EACpD,IAAI,EAAE,8BAA8B,EACpC,GAAG,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC1C,oBAAoB,CAAC,MAAM,CAAC,CAS9B"}
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DyE2E_JourneyContext = void 0;
4
+ exports.DyE2E_useJourney = DyE2E_useJourney;
5
+ /**
6
+ * BFR-FSMED-007 — typed user-journey context: carries the REAL ids / state a step created to the next step
7
+ * (state handoff), and owns a cleanup registry that removes everything the journey created, even when a step failed.
8
+ *
9
+ * - `step(name, fn)`: runs a step with the current state; the returned patch is merged into the state (handoff).
10
+ * A failed step is recorded (`failedStep`) and rethrown — the journey stops, the cleanups still run.
11
+ * - `require(key)`: a handoff guard — a step that needs an earlier step's id fails with the step + key named,
12
+ * instead of calling the API with `undefined`.
13
+ * - `registerCleanup(label, fn)`: register right AFTER the thing exists (create → register), so a later failure
14
+ * cannot leak it. Cleanups run LIFO (dependants before their parents).
15
+ * - `runCleanup()`: runs EVERY cleanup, each isolated (one failure does not skip the rest), once (idempotent), then
16
+ * throws if any failed — a leaked entity is a red journey, never a silent one.
17
+ *
18
+ * With Playwright: `DyE2E_useJourney(test, { name, initialState })` (serial mode + `afterAll` cleanup).
19
+ */
20
+ class DyE2E_JourneyContext {
21
+ name;
22
+ currentState;
23
+ cleanups = [];
24
+ steps = [];
25
+ failed;
26
+ cleanupReport;
27
+ /** Creates the context with the journey name and its initial state. */
28
+ constructor(set) {
29
+ this.name = set.name;
30
+ this.currentState = { ...set.initialState };
31
+ }
32
+ /** The current state (a copy — change it through a step's returned patch or `handoff`). */
33
+ get state() {
34
+ return { ...this.currentState };
35
+ }
36
+ /** The names of the steps that completed, in order. */
37
+ get completedSteps() {
38
+ return [...this.steps];
39
+ }
40
+ /** The name of the step that threw, if any. */
41
+ get failedStep() {
42
+ return this.failed;
43
+ }
44
+ /** Runs one journey step; its returned patch is merged into the state (the handoff to the next step). */
45
+ async step(name, fn) {
46
+ if (this.failed) {
47
+ throw new Error(`[DyE2E journey "${this.name}"] step "${name}" not run: step "${this.failed}" already failed.`);
48
+ }
49
+ try {
50
+ const patch = await fn(this.state);
51
+ if (patch) {
52
+ this.currentState = { ...this.currentState, ...patch };
53
+ }
54
+ this.steps.push(name);
55
+ }
56
+ catch (err) {
57
+ this.failed = name;
58
+ throw err;
59
+ }
60
+ return this.state;
61
+ }
62
+ /** Merges a patch into the state outside a step (e.g. from a Playwright `test` body). */
63
+ handoff(patch) {
64
+ this.currentState = { ...this.currentState, ...patch };
65
+ }
66
+ /** Returns a state value an earlier step must have handed off; throws with the key named when it is missing. */
67
+ require(key) {
68
+ const value = this.currentState[key];
69
+ if (value === undefined || value === null) {
70
+ throw new Error(`[DyE2E journey "${this.name}"] state "${String(key)}" is missing — an earlier step did not hand it off ` +
71
+ `(completed: ${this.steps.join(' → ') || 'none'}).`);
72
+ }
73
+ return value;
74
+ }
75
+ /** Registers a cleanup for something the journey created — call it right after the creation. */
76
+ registerCleanup(label, fn) {
77
+ if (this.cleanupReport) {
78
+ throw new Error(`[DyE2E journey "${this.name}"] cleanup "${label}" registered after runCleanup().`);
79
+ }
80
+ this.cleanups.push({ label: label, fn: fn });
81
+ }
82
+ /**
83
+ * Runs every registered cleanup LIFO, each isolated; idempotent (a second call returns the first report).
84
+ * Throws after ALL of them ran when any failed, naming the failed labels.
85
+ */
86
+ async runCleanup() {
87
+ if (this.cleanupReport) {
88
+ return this.cleanupReport;
89
+ }
90
+ const results = [];
91
+ for (const entry of [...this.cleanups].reverse()) {
92
+ try {
93
+ await entry.fn();
94
+ results.push({ label: entry.label, ok: true });
95
+ }
96
+ catch (err) {
97
+ results.push({ label: entry.label, ok: false, error: err instanceof Error ? err.message : String(err) });
98
+ }
99
+ }
100
+ const failures = results.filter((result) => !result.ok);
101
+ this.cleanupReport = { journey: this.name, results: results, failed: failures.length };
102
+ if (failures.length) {
103
+ throw new Error(`[DyE2E journey "${this.name}"] ${failures.length}/${results.length} cleanup(s) failed — leaked: ` +
104
+ failures.map((f) => `${f.label} (${f.error})`).join('; '));
105
+ }
106
+ return this.cleanupReport;
107
+ }
108
+ }
109
+ exports.DyE2E_JourneyContext = DyE2E_JourneyContext;
110
+ /**
111
+ * BFR-FSMED-007 — Playwright binding: puts the calling `describe` into serial mode (the steps share state) and
112
+ * registers `afterAll → runCleanup()`, so the cleanups run even when a step failed. Call it inside `test.describe`.
113
+ *
114
+ * ```ts
115
+ * test.describe('J-002 post lifecycle', () => {
116
+ * const journey = DyE2E_useJourney(test, { name: 'J-002', initialState: { postId: undefined as string | undefined } });
117
+ * test('create', async ({ request }) => {
118
+ * const id = await createPost(request);
119
+ * journey.registerCleanup(`delete post ${id}`, () => deletePostWithOwnClient(id));
120
+ * journey.handoff({ postId: id });
121
+ * });
122
+ * test('read', async ({ request }) => { await readPost(request, journey.require('postId')); });
123
+ * });
124
+ * ```
125
+ *
126
+ * ⚠️ A cleanup runs in `afterAll`, AFTER the test-scoped fixtures (`page`, `request`) were disposed — it must not
127
+ * capture them: use its own client (`fetch`, or `playwright.request.newContext()` created inside the cleanup).
128
+ */
129
+ function DyE2E_useJourney(test, set) {
130
+ const journey = new DyE2E_JourneyContext(set);
131
+ test.describe.configure({ mode: 'serial' });
132
+ test.afterAll(async () => {
133
+ await journey.runCleanup();
134
+ });
135
+ return journey;
136
+ }
137
+ //# sourceMappingURL=journey-context.util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey-context.util.js","sourceRoot":"","sources":["../../../../src/_modules/runtime/_collections/journey-context.util.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /** BFR-FSMED-007 — the outcome of one registered cleanup in `DyE2E_JourneyContext.runCleanup()`. */
2
+ export interface DyE2E_JourneyCleanupResult_Interface {
3
+ /** The label given at registration (what was cleaned up — e.g. `hard-delete account 64f…`). */
4
+ label: string;
5
+ /** `true` when the cleanup resolved. */
6
+ ok: boolean;
7
+ /** The failure message when `ok` is `false`. */
8
+ error?: string;
9
+ }
10
+ /** BFR-FSMED-007 — what `DyE2E_JourneyContext.runCleanup()` did: every cleanup, in the order it ran (LIFO). */
11
+ export interface DyE2E_JourneyCleanupReport_Interface {
12
+ /** The journey name. */
13
+ journey: string;
14
+ /** One row per registered cleanup, in run order (last registered first). */
15
+ results: DyE2E_JourneyCleanupResult_Interface[];
16
+ /** The number of failed cleanups (`0` = everything the journey created is gone). */
17
+ failed: number;
18
+ }
19
+ /**
20
+ * BFR-FSMED-007 — the minimal Playwright `test` surface `DyE2E_useJourney` needs (structural, so a unit test can pass a
21
+ * double and the runtime needs no specific Playwright version).
22
+ */
23
+ export interface DyE2E_JourneyTestApi_Interface {
24
+ describe: {
25
+ configure: (options: {
26
+ mode: 'serial';
27
+ }) => void;
28
+ };
29
+ afterAll: (fn: () => Promise<void>) => void;
30
+ }
31
+ //# sourceMappingURL=journey-context.interface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey-context.interface.d.ts","sourceRoot":"","sources":["../../../../src/_modules/runtime/_models/journey-context.interface.ts"],"names":[],"mappings":"AAAA,oGAAoG;AACpG,MAAM,WAAW,oCAAoC;IACnD,+FAA+F;IAC/F,KAAK,EAAE,MAAM,CAAC;IACd,wCAAwC;IACxC,EAAE,EAAE,OAAO,CAAC;IACZ,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,+GAA+G;AAC/G,MAAM,WAAW,oCAAoC;IACnD,wBAAwB;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,OAAO,EAAE,oCAAoC,EAAE,CAAC;IAChD,oFAAoF;IACpF,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,MAAM,WAAW,8BAA8B;IAC7C,QAAQ,EAAE;QAAE,SAAS,EAAE,CAAC,OAAO,EAAE;YAAE,IAAI,EAAE,QAAQ,CAAA;SAAE,KAAK,IAAI,CAAA;KAAE,CAAC;IAC/D,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;CAC7C"}
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=journey-context.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"journey-context.interface.js","sourceRoot":"","sources":["../../../../src/_modules/runtime/_models/journey-context.interface.ts"],"names":[],"mappings":""}
@@ -13,12 +13,14 @@
13
13
  * ```
14
14
  */
15
15
  export * from './_collections/assertion-helpers.util';
16
+ export * from './_collections/journey-context.util';
16
17
  export * from './_collections/notification-detector-page-script.const';
17
18
  export * from './_collections/notification-detector.util';
18
19
  export * from './_collections/notification-sweep.util';
19
20
  export * from './_collections/unique-generator.util';
20
21
  export * from './_collections/wait-helpers.util';
21
22
  export * from './_models/index';
23
+ export * from './_models/journey-context.interface';
22
24
  export * from './_models/notification-report.interface';
23
25
  export * from './_models/notification-sweep.interface';
24
26
  export * from './_models/test-user.interface';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/_modules/runtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,cAAc,uCAAuC,CAAC;AACtD,cAAc,wDAAwD,CAAC;AACvE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iBAAiB,CAAC;AAChC,cAAc,yCAAyC,CAAC;AACxD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oCAAoC,CAAC;AACnD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,YAAY,CAAC;AAE3B,eAAO,MAAM,qBAAqB,EAAE,MAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/_modules/runtime/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,cAAc,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,wDAAwD,CAAC;AACvE,cAAc,2CAA2C,CAAC;AAC1D,cAAc,wCAAwC,CAAC;AACvD,cAAc,sCAAsC,CAAC;AACrD,cAAc,kCAAkC,CAAC;AACjD,cAAc,iBAAiB,CAAC;AAChC,cAAc,qCAAqC,CAAC;AACpD,cAAc,yCAAyC,CAAC;AACxD,cAAc,wCAAwC,CAAC;AACvD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,oCAAoC,CAAC;AACnD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,kCAAkC,CAAC;AACjD,cAAc,YAAY,CAAC;AAE3B,eAAO,MAAM,qBAAqB,EAAE,MAAwB,CAAC"}
@@ -30,12 +30,14 @@ exports.DYE2E_RUNTIME_VERSION = void 0;
30
30
  * ```
31
31
  */
32
32
  __exportStar(require("./_collections/assertion-helpers.util"), exports);
33
+ __exportStar(require("./_collections/journey-context.util"), exports);
33
34
  __exportStar(require("./_collections/notification-detector-page-script.const"), exports);
34
35
  __exportStar(require("./_collections/notification-detector.util"), exports);
35
36
  __exportStar(require("./_collections/notification-sweep.util"), exports);
36
37
  __exportStar(require("./_collections/unique-generator.util"), exports);
37
38
  __exportStar(require("./_collections/wait-helpers.util"), exports);
38
39
  __exportStar(require("./_models/index"), exports);
40
+ __exportStar(require("./_models/journey-context.interface"), exports);
39
41
  __exportStar(require("./_models/notification-report.interface"), exports);
40
42
  __exportStar(require("./_models/notification-sweep.interface"), exports);
41
43
  __exportStar(require("./_models/test-user.interface"), exports);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/_modules/runtime/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;GAaG;AACH,wEAAsD;AACtD,yFAAuE;AACvE,4EAA0D;AAC1D,yEAAuD;AACvD,uEAAqD;AACrD,mEAAiD;AACjD,kDAAgC;AAChC,0EAAwD;AACxD,yEAAuD;AACvD,gEAA8C;AAC9C,iEAA+C;AAC/C,gEAA8C;AAC9C,+DAA6C;AAC7C,iEAA+C;AAC/C,6DAA2C;AAC3C,qEAAmD;AACnD,8DAA4C;AAC5C,gEAA8C;AAC9C,mEAAiD;AACjD,6CAA2B;AAEd,QAAA,qBAAqB,GAAW,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/_modules/runtime/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA;;;;;;;;;;;;;GAaG;AACH,wEAAsD;AACtD,sEAAoD;AACpD,yFAAuE;AACvE,4EAA0D;AAC1D,yEAAuD;AACvD,uEAAqD;AACrD,mEAAiD;AACjD,kDAAgC;AAChC,sEAAoD;AACpD,0EAAwD;AACxD,yEAAuD;AACvD,gEAA8C;AAC9C,iEAA+C;AAC/C,gEAA8C;AAC9C,+DAA6C;AAC7C,iEAA+C;AAC/C,6DAA2C;AAC3C,qEAAmD;AACnD,8DAA4C;AAC5C,gEAA8C;AAC9C,mEAAiD;AACjD,6CAA2B;AAEd,QAAA,qBAAqB,GAAW,eAAe,CAAC"}
@@ -4,7 +4,7 @@ import { DyE2E_Viewport_Interface } from '../../../contracts/_models/interfaces/
4
4
  * ezekből választhat subset-et, vagy override-olhatja sajáttal.
5
5
  *
6
6
  * **Konvenció**: `key` filename-friendly + a `dye2e generate-review-bundle` ezzel
7
- * mappel artifact-ot (`screenshots/<state>/<key>-full.png`).
7
+ * mappel artifact-ot (`states/<state>/<key>-<orientation>/full.png`).
8
8
  */
9
9
  export declare const DyE2E_VQA_StandardViewports_Const: {
10
10
  [key: string]: DyE2E_Viewport_Interface;
@@ -6,7 +6,7 @@ exports.DyE2E_VQA_DefaultViewportSubset_Const = exports.DyE2E_VQA_StandardViewpo
6
6
  * ezekből választhat subset-et, vagy override-olhatja sajáttal.
7
7
  *
8
8
  * **Konvenció**: `key` filename-friendly + a `dye2e generate-review-bundle` ezzel
9
- * mappel artifact-ot (`screenshots/<state>/<key>-full.png`).
9
+ * mappel artifact-ot (`states/<state>/<key>-<orientation>/full.png`).
10
10
  */
11
11
  exports.DyE2E_VQA_StandardViewports_Const = {
12
12
  mobileSmall: {
@@ -7,4 +7,5 @@ export * from './utils/finding-validator.util';
7
7
  export * from './utils/finding-id.util';
8
8
  export * from './utils/risk-score.util';
9
9
  export * from './utils/deterministic-finding.util';
10
+ export * from './utils/review-gate.util';
10
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/_modules/vqa/_collections/index.ts"],"names":[],"mappings":"AAAA,cAAc,0CAA0C,CAAC;AACzD,cAAc,0CAA0C,CAAC;AACzD,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oCAAoC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/_modules/vqa/_collections/index.ts"],"names":[],"mappings":"AAAA,cAAc,0CAA0C,CAAC;AACzD,cAAc,0CAA0C,CAAC;AACzD,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,iCAAiC,CAAC;AAChD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oCAAoC,CAAC;AACnD,cAAc,0BAA0B,CAAC"}
@@ -23,4 +23,5 @@ __exportStar(require("./utils/finding-validator.util"), exports);
23
23
  __exportStar(require("./utils/finding-id.util"), exports);
24
24
  __exportStar(require("./utils/risk-score.util"), exports);
25
25
  __exportStar(require("./utils/deterministic-finding.util"), exports);
26
+ __exportStar(require("./utils/review-gate.util"), exports);
26
27
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/_modules/vqa/_collections/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2EAAyD;AACzD,2EAAyD;AACzD,mEAAiD;AACjD,oEAAkD;AAClD,kEAAgD;AAChD,iEAA+C;AAC/C,0DAAwC;AACxC,0DAAwC;AACxC,qEAAmD"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/_modules/vqa/_collections/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2EAAyD;AACzD,2EAAyD;AACzD,mEAAiD;AACjD,oEAAkD;AAClD,kEAAgD;AAChD,iEAA+C;AAC/C,0DAAwC;AACxC,0DAAwC;AACxC,qEAAmD;AACnD,2DAAyC"}
@@ -0,0 +1,36 @@
1
+ import { DyE2E_VQA_FindingStatus_Type } from '../../_enums/finding-status.enum';
2
+ import { DyE2E_VQA_ReviewGate_Options, DyE2E_VQA_ReviewGateCategory_Type, DyE2E_VQA_ReviewGateReport_Interface } from '../../_models/interfaces/review-gate.interface';
3
+ /** BFR-FSMED-015 — every gate category, in report order (the default `failOn`). */
4
+ export declare const DYE2E_VQA_REVIEW_GATE_CATEGORIES: DyE2E_VQA_ReviewGateCategory_Type[];
5
+ /**
6
+ * BFR-FSMED-015 — finding statuses that CLOSE a finding for the release verdict. Everything else (open, in-progress,
7
+ * fixed-but-not-re-audited, ready-for-re-audit, reopened) is still open.
8
+ */
9
+ export declare const DYE2E_VQA_CLOSED_FINDING_STATUSES: DyE2E_VQA_FindingStatus_Type[];
10
+ /**
11
+ * BFR-FSMED-015 (part 3) — the aggregate release gate for a VQA review bundle: manifest + captured evidence +
12
+ * finding-set + human-review marker ⇒ ONE verdict with stable codes and a machine-readable report.
13
+ *
14
+ * Checks: missing evidence (`VQA-GATE-EVIDENCE-MISSING`), missing findings file (`VQA-GATE-FINDINGS-MISSING`),
15
+ * orphan evidence (`VQA-GATE-EVIDENCE-ORPHAN`), invalid findings (`VQA-GATE-FINDING-INVALID`,
16
+ * `VQA-GATE-FINDING-FOREIGN`, `VQA-GATE-FINDING-REF-MISSING`), open blocker / high (`VQA-GATE-FINDING-OPEN-BLOCKER` /
17
+ * `-HIGH`), human review (`VQA-GATE-HUMAN-REVIEW-MISSING` / `-INVALID` / `-REJECTED`). A missing / unreadable bundle
18
+ * manifest (`VQA-GATE-MANIFEST-MISSING` / `-INVALID`) always fails — without it nothing can be checked.
19
+ */
20
+ export declare class DyE2E_VQA_ReviewGate_Util {
21
+ /** Runs every check and returns the report; `verdict` is `fail` when any blocking issue exists. */
22
+ static verify(options: DyE2E_VQA_ReviewGate_Options): DyE2E_VQA_ReviewGateReport_Interface;
23
+ /** Parses a `--fail-on a,b` list; an unknown category throws (a typo must not silently weaken the gate). */
24
+ static parseFailOn(raw: string | undefined): DyE2E_VQA_ReviewGateCategory_Type[];
25
+ /** Reads `<bundle>/manifest.json`; a missing / unreadable one is an always-blocking issue. */
26
+ private static readManifest;
27
+ /** Missing capture artifacts + evidence folders the manifest does not declare. */
28
+ private static checkEvidence;
29
+ /** Finding schema + consistency with the bundle + open blocker / high. Returns the number of findings read. */
30
+ private static checkFindings;
31
+ /** The human-review marker: present, well-formed, approving. */
32
+ private static checkHumanReview;
33
+ /** The subdirectory names of a directory. */
34
+ private static subdirs;
35
+ }
36
+ //# sourceMappingURL=review-gate.util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"review-gate.util.d.ts","sourceRoot":"","sources":["../../../../../src/_modules/vqa/_collections/utils/review-gate.util.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,4BAA4B,EAAE,MAAM,kCAAkC,CAAC;AAMhF,OAAO,EAEL,4BAA4B,EAC5B,iCAAiC,EAEjC,oCAAoC,EACrC,MAAM,gDAAgD,CAAC;AAIxD,mFAAmF;AACnF,eAAO,MAAM,gCAAgC,EAAE,iCAAiC,EACT,CAAC;AAExE;;;GAGG;AACH,eAAO,MAAM,iCAAiC,EAAE,4BAA4B,EAI3E,CAAC;AAcF;;;;;;;;;GASG;AACH,qBAAa,yBAAyB;IAEpC,mGAAmG;IACnG,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,4BAA4B,GAAG,oCAAoC;IAqC1F,4GAA4G;IAC5G,MAAM,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,iCAAiC,EAAE;IAmBhF,8FAA8F;IAC9F,OAAO,CAAC,MAAM,CAAC,YAAY;IAiC3B,kFAAkF;IAClF,OAAO,CAAC,MAAM,CAAC,aAAa;IAqC5B,+GAA+G;IAC/G,OAAO,CAAC,MAAM,CAAC,aAAa;IAyE5B,gEAAgE;IAChE,OAAO,CAAC,MAAM,CAAC,gBAAgB;IAuC/B,6CAA6C;IAC7C,OAAO,CAAC,MAAM,CAAC,OAAO;CAKvB"}
@@ -0,0 +1,232 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.DyE2E_VQA_ReviewGate_Util = exports.DYE2E_VQA_CLOSED_FINDING_STATUSES = exports.DYE2E_VQA_REVIEW_GATE_CATEGORIES = void 0;
27
+ const fs = __importStar(require("fs"));
28
+ const path = __importStar(require("path"));
29
+ const finding_status_enum_1 = require("../../_enums/finding-status.enum");
30
+ const finding_severity_enum_1 = require("../../_enums/finding-severity.enum");
31
+ const review_bundle_generator_1 = require("../../generators/review-bundle.generator");
32
+ const finding_validator_util_1 = require("./finding-validator.util");
33
+ /** BFR-FSMED-015 — every gate category, in report order (the default `failOn`). */
34
+ exports.DYE2E_VQA_REVIEW_GATE_CATEGORIES = ['missing', 'orphan', 'invalid', 'blocker', 'high', 'human-review'];
35
+ /**
36
+ * BFR-FSMED-015 — finding statuses that CLOSE a finding for the release verdict. Everything else (open, in-progress,
37
+ * fixed-but-not-re-audited, ready-for-re-audit, reopened) is still open.
38
+ */
39
+ exports.DYE2E_VQA_CLOSED_FINDING_STATUSES = [
40
+ finding_status_enum_1.DyE2E_VQA_FindingStatus_Type.verified,
41
+ finding_status_enum_1.DyE2E_VQA_FindingStatus_Type.ignored,
42
+ finding_status_enum_1.DyE2E_VQA_FindingStatus_Type.acceptedRisk,
43
+ ];
44
+ /**
45
+ * BFR-FSMED-015 (part 3) — the aggregate release gate for a VQA review bundle: manifest + captured evidence +
46
+ * finding-set + human-review marker ⇒ ONE verdict with stable codes and a machine-readable report.
47
+ *
48
+ * Checks: missing evidence (`VQA-GATE-EVIDENCE-MISSING`), missing findings file (`VQA-GATE-FINDINGS-MISSING`),
49
+ * orphan evidence (`VQA-GATE-EVIDENCE-ORPHAN`), invalid findings (`VQA-GATE-FINDING-INVALID`,
50
+ * `VQA-GATE-FINDING-FOREIGN`, `VQA-GATE-FINDING-REF-MISSING`), open blocker / high (`VQA-GATE-FINDING-OPEN-BLOCKER` /
51
+ * `-HIGH`), human review (`VQA-GATE-HUMAN-REVIEW-MISSING` / `-INVALID` / `-REJECTED`). A missing / unreadable bundle
52
+ * manifest (`VQA-GATE-MANIFEST-MISSING` / `-INVALID`) always fails — without it nothing can be checked.
53
+ */
54
+ class DyE2E_VQA_ReviewGate_Util {
55
+ /** Runs every check and returns the report; `verdict` is `fail` when any blocking issue exists. */
56
+ static verify(options) {
57
+ const failOn = options.failOn ?? exports.DYE2E_VQA_REVIEW_GATE_CATEGORIES;
58
+ const issues = [];
59
+ const add = (code, category, message, ref) => {
60
+ issues.push({ code: code, category: category, blocking: failOn.includes(category), message: message, ref: ref });
61
+ };
62
+ const manifest = DyE2E_VQA_ReviewGate_Util.readManifest(options.bundlePath, issues);
63
+ let findingsTotal = 0;
64
+ if (manifest) {
65
+ DyE2E_VQA_ReviewGate_Util.checkEvidence(options.bundlePath, manifest, add);
66
+ findingsTotal = DyE2E_VQA_ReviewGate_Util.checkFindings(options, manifest, add);
67
+ DyE2E_VQA_ReviewGate_Util.checkHumanReview(options, add);
68
+ }
69
+ const counts = {
70
+ 'missing': 0, 'orphan': 0, 'invalid': 0, 'blocker': 0, 'high': 0, 'human-review': 0,
71
+ };
72
+ issues.forEach((issue) => {
73
+ counts[issue.category]++;
74
+ });
75
+ return {
76
+ schema: 'dye2e-vqa-review-gate/1',
77
+ bundlePath: options.bundlePath,
78
+ featureId: manifest?.featureId ?? '',
79
+ verdict: issues.some((issue) => issue.blocking) ? 'fail' : 'pass',
80
+ failOn: failOn,
81
+ counts: counts,
82
+ findingsTotal: findingsTotal,
83
+ issues: issues,
84
+ };
85
+ }
86
+ /** Parses a `--fail-on a,b` list; an unknown category throws (a typo must not silently weaken the gate). */
87
+ static parseFailOn(raw) {
88
+ if (!raw) {
89
+ return exports.DYE2E_VQA_REVIEW_GATE_CATEGORIES;
90
+ }
91
+ return raw.split(',').map((part) => {
92
+ const category = exports.DYE2E_VQA_REVIEW_GATE_CATEGORIES
93
+ .find((known) => known === part.trim());
94
+ if (!category) {
95
+ throw new Error(`VQA-GATE-FAIL-ON-UNKNOWN: "${part.trim()}" — valid: ${exports.DYE2E_VQA_REVIEW_GATE_CATEGORIES.join(', ')}`);
96
+ }
97
+ return category;
98
+ });
99
+ }
100
+ /** Reads `<bundle>/manifest.json`; a missing / unreadable one is an always-blocking issue. */
101
+ static readManifest(bundlePath, issues) {
102
+ const manifestPath = path.join(bundlePath, 'manifest.json');
103
+ if (!fs.existsSync(manifestPath)) {
104
+ issues.push({
105
+ code: 'VQA-GATE-MANIFEST-MISSING', category: 'missing', blocking: true, ref: 'manifest.json',
106
+ message: `bundle manifest not found: ${manifestPath}`,
107
+ });
108
+ return undefined;
109
+ }
110
+ try {
111
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
112
+ if (!manifest?.featureId || !Array.isArray(manifest.states) || !Array.isArray(manifest.viewports)) {
113
+ throw new Error('featureId / states / viewports missing');
114
+ }
115
+ return manifest;
116
+ }
117
+ catch (err) {
118
+ issues.push({
119
+ code: 'VQA-GATE-MANIFEST-INVALID', category: 'invalid', blocking: true, ref: 'manifest.json',
120
+ message: `bundle manifest unreadable: ${err instanceof Error ? err.message : String(err)}`,
121
+ });
122
+ return undefined;
123
+ }
124
+ }
125
+ /** Missing capture artifacts + evidence folders the manifest does not declare. */
126
+ static checkEvidence(bundlePath, manifest, add) {
127
+ review_bundle_generator_1.DyE2E_VQA_ReviewBundle_Generator.verifyBundleArtifacts(bundlePath, manifest).missing
128
+ .forEach((ref) => add('VQA-GATE-EVIDENCE-MISSING', 'missing', `capture artifact missing: ${ref}`, ref));
129
+ const statesDir = path.join(bundlePath, 'states');
130
+ if (!fs.existsSync(statesDir)) {
131
+ return;
132
+ }
133
+ const orientations = manifest.orientations && manifest.orientations.length ? manifest.orientations : ['preserve'];
134
+ const stateIds = manifest.states.map((state) => state.stateId);
135
+ const viewportDirs = manifest.viewports.flatMap((viewport) => orientations.map((orientation) => `${viewport.key}-${orientation}`));
136
+ for (const stateDir of DyE2E_VQA_ReviewGate_Util.subdirs(statesDir)) {
137
+ if (!stateIds.includes(stateDir)) {
138
+ add('VQA-GATE-EVIDENCE-ORPHAN', 'orphan', `evidence for a state the manifest does not declare: ${stateDir}`, `states/${stateDir}`);
139
+ continue;
140
+ }
141
+ for (const viewportDir of DyE2E_VQA_ReviewGate_Util.subdirs(path.join(statesDir, stateDir))) {
142
+ if (!viewportDirs.includes(viewportDir)) {
143
+ add('VQA-GATE-EVIDENCE-ORPHAN', 'orphan', `evidence for a viewport / orientation the manifest does not declare: ${stateDir}/${viewportDir}`, `states/${stateDir}/${viewportDir}`);
144
+ }
145
+ }
146
+ }
147
+ }
148
+ /** Finding schema + consistency with the bundle + open blocker / high. Returns the number of findings read. */
149
+ static checkFindings(options, manifest, add) {
150
+ const findingsPath = options.findingsPath ?? path.join(options.bundlePath, 'findings', 'findings.json');
151
+ if (!fs.existsSync(findingsPath)) {
152
+ add('VQA-GATE-FINDINGS-MISSING', 'missing', `finding-set not found (the review did not run, or wrote elsewhere): ${findingsPath}`, findingsPath);
153
+ return 0;
154
+ }
155
+ let findings;
156
+ try {
157
+ const raw = JSON.parse(fs.readFileSync(findingsPath, 'utf-8'));
158
+ const list = Array.isArray(raw) ? raw : raw?.findings;
159
+ if (!Array.isArray(list)) {
160
+ throw new Error('expected an array or { findings: [] }');
161
+ }
162
+ findings = list;
163
+ }
164
+ catch (err) {
165
+ add('VQA-GATE-FINDING-INVALID', 'invalid', `finding-set unreadable: ${err instanceof Error ? err.message : String(err)}`, findingsPath);
166
+ return 0;
167
+ }
168
+ const stateIds = manifest.states.map((state) => state.stateId);
169
+ const viewportKeys = manifest.viewports.map((viewport) => viewport.key);
170
+ findings.forEach((finding, index) => {
171
+ const ref = finding?.findingId || `findings[${index}]`;
172
+ const validation = finding_validator_util_1.DyE2E_VQA_FindingValidator_Util.validate(finding);
173
+ if (!validation.ok) {
174
+ add('VQA-GATE-FINDING-INVALID', 'invalid', `schema: ${validation.errors
175
+ .map((e) => `${e.path} ${e.message}`).join('; ')}`, ref);
176
+ }
177
+ // Cross-check against the bundle: a finding about another feature / an undeclared state or viewport is foreign
178
+ if (finding?.featureId !== manifest.featureId || !stateIds.includes(finding?.stateId ?? '')
179
+ || !viewportKeys.includes(finding?.viewport ?? '')) {
180
+ add('VQA-GATE-FINDING-FOREIGN', 'invalid', `feature / state / viewport not in this bundle: ${finding?.featureId} / ${finding?.stateId} / ${finding?.viewport}`, ref);
181
+ }
182
+ // Every evidence reference must point at a file that exists in the bundle
183
+ [finding?.screenshotRef, finding?.domSnapshotRef]
184
+ .filter((evidenceRef) => !!evidenceRef)
185
+ .filter((evidenceRef) => !fs.existsSync(path.join(options.bundlePath, evidenceRef)))
186
+ .forEach((evidenceRef) => add('VQA-GATE-FINDING-REF-MISSING', 'invalid', `evidence reference not in the bundle: ${evidenceRef}`, ref));
187
+ const isOpen = !exports.DYE2E_VQA_CLOSED_FINDING_STATUSES
188
+ .some((status) => status === finding?.status);
189
+ if (isOpen && finding?.severity === finding_severity_enum_1.DyE2E_VQA_FindingSeverity_Type.blocker) {
190
+ add('VQA-GATE-FINDING-OPEN-BLOCKER', 'blocker', `open blocker (status: ${finding.status})`, ref);
191
+ }
192
+ if (isOpen && finding?.severity === finding_severity_enum_1.DyE2E_VQA_FindingSeverity_Type.high) {
193
+ add('VQA-GATE-FINDING-OPEN-HIGH', 'high', `open high (status: ${finding.status})`, ref);
194
+ }
195
+ });
196
+ return findings.length;
197
+ }
198
+ /** The human-review marker: present, well-formed, approving. */
199
+ static checkHumanReview(options, add) {
200
+ const reviewPath = options.humanReviewPath ?? path.join(options.bundlePath, 'human-review.json');
201
+ if (!fs.existsSync(reviewPath)) {
202
+ add('VQA-GATE-HUMAN-REVIEW-MISSING', 'human-review', `no human review marker: ${reviewPath}`, reviewPath);
203
+ return;
204
+ }
205
+ let review;
206
+ try {
207
+ review = JSON.parse(fs.readFileSync(reviewPath, 'utf-8'));
208
+ }
209
+ catch (err) {
210
+ add('VQA-GATE-HUMAN-REVIEW-INVALID', 'human-review', `human review marker unreadable: ${err instanceof Error ? err.message : String(err)}`, reviewPath);
211
+ return;
212
+ }
213
+ const wellFormed = typeof review?.reviewer === 'string' && !!review.reviewer.trim()
214
+ && typeof review.reviewedAt === 'string' && !Number.isNaN(Date.parse(review.reviewedAt))
215
+ && (review.verdict === 'approved' || review.verdict === 'rejected');
216
+ if (!wellFormed) {
217
+ add('VQA-GATE-HUMAN-REVIEW-INVALID', 'human-review', 'human review marker needs { reviewer, reviewedAt (ISO date), verdict: approved | rejected }', reviewPath);
218
+ return;
219
+ }
220
+ if (review.verdict === 'rejected') {
221
+ add('VQA-GATE-HUMAN-REVIEW-REJECTED', 'human-review', `the human reviewer (${review.reviewer}) rejected`, reviewPath);
222
+ }
223
+ }
224
+ /** The subdirectory names of a directory. */
225
+ static subdirs(dir) {
226
+ return fs.readdirSync(dir, { withFileTypes: true })
227
+ .filter((entry) => entry.isDirectory())
228
+ .map((entry) => entry.name);
229
+ }
230
+ }
231
+ exports.DyE2E_VQA_ReviewGate_Util = DyE2E_VQA_ReviewGate_Util;
232
+ //# sourceMappingURL=review-gate.util.js.map