@sdods/contracts 0.7.2 → 0.9.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.
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { EvidencePatchSchema, HealConfigSchema, PerfBudgetsSchema, ScreenshotConfigSchema, TimeoutsSchema, } from './project.js';
2
+ import { A11yPatchSchema, EvidencePatchSchema, HealConfigSchema, PerfBudgetsSchema, ScreenshotConfigSchema, TimeoutsSchema, } from './project.js';
3
+ import { EnvLoadSchema } from './load.js';
3
4
  export const ApiAuthSchema = z.discriminatedUnion('type', [
4
5
  z.object({ type: z.literal('none') }),
5
6
  z.object({ type: z.literal('bearer'), token: z.string() }),
@@ -24,6 +25,21 @@ export const EnvUseSchema = z.object({
24
25
  extraHTTPHeaders: z.record(z.string(), z.string()).optional(),
25
26
  httpCredentials: z.object({ username: z.string(), password: z.string() }).optional(),
26
27
  });
28
+ /**
29
+ * The mail catcher the email steps read. Only Mailpit today: MailHog's API hands back raw MIME
30
+ * (quoted-printable, nested multiparts) and is unmaintained, so an enum value for it would be a
31
+ * half-working promise. `auth.password` trips the secret-literal check, so it must be `${VAR}`.
32
+ */
33
+ export const MailConfigSchema = z.object({
34
+ provider: z.enum(['mailpit']).default('mailpit'),
35
+ url: z.string().url(),
36
+ auth: z
37
+ .object({ type: z.literal('basic'), username: z.string(), password: z.string() })
38
+ .optional(),
39
+ pollIntervalMs: z.number().int().positive().default(1000),
40
+ clockSkewMs: z.number().int().nonnegative().default(2000),
41
+ timeoutSeconds: z.number().int().positive().default(15),
42
+ });
27
43
  export const EnvConfigSchema = z.object({
28
44
  name: z.string().min(1),
29
45
  description: z.string().optional(),
@@ -36,6 +52,7 @@ export const EnvConfigSchema = z.object({
36
52
  }),
37
53
  aliases: z.array(z.string().url()).default([]),
38
54
  db: z.object({ driver: z.enum(['sqlite', 'postgres']), url: z.string() }).optional(),
55
+ mail: MailConfigSchema.optional(),
39
56
  users: z.object({ poolSize: z.number().int().positive().optional() }).default({}),
40
57
  vars: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).default({}),
41
58
  use: EnvUseSchema.default({}),
@@ -44,6 +61,9 @@ export const EnvConfigSchema = z.object({
44
61
  heal: HealConfigSchema.partial().optional(),
45
62
  timeouts: TimeoutsSchema.partial().optional(),
46
63
  perf: z.object({ budgets: PerfBudgetsSchema.partial() }).optional(),
64
+ a11y: A11yPatchSchema.optional(),
47
65
  ci: z.boolean().optional(),
66
+ /** Opt-in for `sdods load`; absent means load tests are refused. */
67
+ load: EnvLoadSchema.optional(),
48
68
  });
49
69
  //# sourceMappingURL=env.js.map
@@ -1,3 +1,5 @@
1
1
  export * from './project.js';
2
2
  export * from './env.js';
3
+ export * from './load.js';
4
+ export * from './matrix.js';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,5 @@
1
1
  export * from './project.js';
2
2
  export * from './env.js';
3
+ export * from './load.js';
4
+ export * from './matrix.js';
3
5
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,84 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Load testing is opt-in per environment. Load against a shared or production environment is
4
+ * destructive, and load against an environment that caps writes measures the cap rather than the
5
+ * system, so `sdods load` refuses to run unless the environment says `load.allowed: true`.
6
+ */
7
+ export declare const EnvLoadSchema: z.ZodObject<{
8
+ allowed: z.ZodDefault<z.ZodBoolean>;
9
+ maxVus: z.ZodOptional<z.ZodNumber>;
10
+ allowWrites: z.ZodDefault<z.ZodBoolean>;
11
+ allowBaseUrlOverride: z.ZodDefault<z.ZodBoolean>;
12
+ }, z.core.$strip>;
13
+ /** k6 duration: `30s`, `1m30s`, `500ms`, `2h`. */
14
+ export declare const K6DurationSchema: z.ZodString;
15
+ export declare const LOAD_READ_METHODS: readonly ["GET", "HEAD", "OPTIONS"];
16
+ export declare const LOAD_METHODS: readonly ["GET", "HEAD", "OPTIONS", "POST", "PUT", "PATCH", "DELETE"];
17
+ export declare const LoadRequestSchema: z.ZodObject<{
18
+ name: z.ZodOptional<z.ZodString>;
19
+ method: z.ZodDefault<z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodEnum<{
20
+ GET: "GET";
21
+ HEAD: "HEAD";
22
+ OPTIONS: "OPTIONS";
23
+ POST: "POST";
24
+ PUT: "PUT";
25
+ PATCH: "PATCH";
26
+ DELETE: "DELETE";
27
+ }>>>;
28
+ path: z.ZodString;
29
+ headers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
30
+ body: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodArray<z.ZodUnknown>]>>;
31
+ expect: z.ZodDefault<z.ZodObject<{
32
+ status: z.ZodPipe<z.ZodDefault<z.ZodUnion<readonly [z.ZodNumber, z.ZodArray<z.ZodNumber>]>>, z.ZodTransform<number[], number | number[]>>;
33
+ bodyContains: z.ZodOptional<z.ZodString>;
34
+ maxDurationMs: z.ZodOptional<z.ZodNumber>;
35
+ }, z.core.$strip>>;
36
+ }, z.core.$strip>;
37
+ export declare const LoadStageSchema: z.ZodObject<{
38
+ duration: z.ZodString;
39
+ target: z.ZodNumber;
40
+ }, z.core.$strip>;
41
+ /** `projects/<slug>/load/<name>.yaml` */
42
+ export declare const LoadProfileSchema: z.ZodObject<{
43
+ description: z.ZodOptional<z.ZodString>;
44
+ auth: z.ZodDefault<z.ZodEnum<{
45
+ env: "env";
46
+ none: "none";
47
+ }>>;
48
+ thinkTimeSeconds: z.ZodDefault<z.ZodNumber>;
49
+ vus: z.ZodOptional<z.ZodNumber>;
50
+ duration: z.ZodOptional<z.ZodString>;
51
+ iterations: z.ZodOptional<z.ZodNumber>;
52
+ stages: z.ZodOptional<z.ZodArray<z.ZodObject<{
53
+ duration: z.ZodString;
54
+ target: z.ZodNumber;
55
+ }, z.core.$strip>>>;
56
+ thresholds: z.ZodPipe<z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>>, z.ZodTransform<{
57
+ [k: string]: string[];
58
+ }, Record<string, string | string[]>>>;
59
+ requests: z.ZodArray<z.ZodObject<{
60
+ name: z.ZodOptional<z.ZodString>;
61
+ method: z.ZodDefault<z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodEnum<{
62
+ GET: "GET";
63
+ HEAD: "HEAD";
64
+ OPTIONS: "OPTIONS";
65
+ POST: "POST";
66
+ PUT: "PUT";
67
+ PATCH: "PATCH";
68
+ DELETE: "DELETE";
69
+ }>>>;
70
+ path: z.ZodString;
71
+ headers: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
72
+ body: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodArray<z.ZodUnknown>]>>;
73
+ expect: z.ZodDefault<z.ZodObject<{
74
+ status: z.ZodPipe<z.ZodDefault<z.ZodUnion<readonly [z.ZodNumber, z.ZodArray<z.ZodNumber>]>>, z.ZodTransform<number[], number | number[]>>;
75
+ bodyContains: z.ZodOptional<z.ZodString>;
76
+ maxDurationMs: z.ZodOptional<z.ZodNumber>;
77
+ }, z.core.$strip>>;
78
+ }, z.core.$strip>>;
79
+ }, z.core.$strip>;
80
+ export type EnvLoadConfig = z.infer<typeof EnvLoadSchema>;
81
+ export type LoadRequest = z.infer<typeof LoadRequestSchema>;
82
+ export type LoadProfile = z.infer<typeof LoadProfileSchema>;
83
+ export type LoadProfileInput = z.input<typeof LoadProfileSchema>;
84
+ //# sourceMappingURL=load.d.ts.map
@@ -0,0 +1,92 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * Load testing is opt-in per environment. Load against a shared or production environment is
4
+ * destructive, and load against an environment that caps writes measures the cap rather than the
5
+ * system, so `sdods load` refuses to run unless the environment says `load.allowed: true`.
6
+ */
7
+ export const EnvLoadSchema = z.object({
8
+ /** The environment is sized for load and owned by the team running it. */
9
+ allowed: z.boolean().default(false),
10
+ /** Refuse profiles whose peak virtual users exceed this. */
11
+ maxVus: z.number().int().positive().optional(),
12
+ /** Allow POST, PUT, PATCH and DELETE requests; read-only profiles need nothing. */
13
+ allowWrites: z.boolean().default(false),
14
+ /**
15
+ * Let `SDODS_API_BASE_URL` send the load somewhere other than this file's `api.baseUrl`. Off by
16
+ * default: the opt-in is for this environment, and a stray override would load another host.
17
+ */
18
+ allowBaseUrlOverride: z.boolean().default(false),
19
+ });
20
+ /** k6 duration: `30s`, `1m30s`, `500ms`, `2h`. */
21
+ export const K6DurationSchema = z
22
+ .string()
23
+ .regex(/^(\d+(\.\d+)?(ms|s|m|h))+$/, 'expected a k6 duration such as 30s, 1m30s or 500ms');
24
+ export const LOAD_READ_METHODS = ['GET', 'HEAD', 'OPTIONS'];
25
+ export const LOAD_METHODS = [...LOAD_READ_METHODS, 'POST', 'PUT', 'PATCH', 'DELETE'];
26
+ export const LoadRequestSchema = z.object({
27
+ /** Shown in k6 output and used as the request's `name` tag. Defaults to `METHOD path`. */
28
+ name: z.string().min(1).optional(),
29
+ method: z
30
+ .string()
31
+ .transform((m) => m.toUpperCase())
32
+ .pipe(z.enum(LOAD_METHODS))
33
+ .default('GET'),
34
+ /** Relative to the environment's `api.baseUrl`; absolute URLs are refused. */
35
+ path: z
36
+ .string()
37
+ .min(1)
38
+ .refine((p) => p.startsWith('/'), 'path must start with "/" (it is relative to api.baseUrl)'),
39
+ headers: z.record(z.string(), z.string()).default({}),
40
+ /** A string is sent as-is; an object or array is sent as JSON. `${VAR}` reads `__ENV`. */
41
+ body: z.union([z.string(), z.record(z.string(), z.unknown()), z.array(z.unknown())]).optional(),
42
+ expect: z
43
+ .object({
44
+ status: z
45
+ .union([z.number().int(), z.array(z.number().int()).min(1)])
46
+ .default(200)
47
+ .transform((s) => (Array.isArray(s) ? s : [s])),
48
+ bodyContains: z.string().optional(),
49
+ maxDurationMs: z.number().positive().optional(),
50
+ })
51
+ .default({ status: [200] }),
52
+ });
53
+ export const LoadStageSchema = z.object({
54
+ duration: K6DurationSchema,
55
+ target: z.number().int().nonnegative(),
56
+ });
57
+ /** `projects/<slug>/load/<name>.yaml` */
58
+ export const LoadProfileSchema = z
59
+ .object({
60
+ description: z.string().optional(),
61
+ /** `env` sends the environment's `api.auth` and `api.headers`; `none` sends neither. */
62
+ auth: z.enum(['env', 'none']).default('env'),
63
+ /** Pause between iterations of one virtual user, in seconds. */
64
+ thinkTimeSeconds: z.number().nonnegative().default(1),
65
+ vus: z.number().int().positive().optional(),
66
+ duration: K6DurationSchema.optional(),
67
+ iterations: z.number().int().positive().optional(),
68
+ stages: z.array(LoadStageSchema).min(1).optional(),
69
+ /** k6 thresholds, e.g. `http_req_duration: ['p(95)<500']`. A single string is accepted. */
70
+ thresholds: z
71
+ .record(z.string(), z.union([z.string(), z.array(z.string()).min(1)]))
72
+ .default({})
73
+ .transform((t) => Object.fromEntries(Object.entries(t).map(([k, v]) => [k, Array.isArray(v) ? v : [v]]))),
74
+ requests: z.array(LoadRequestSchema).min(1),
75
+ })
76
+ .superRefine((p, ctx) => {
77
+ if (p.stages && (p.duration || p.iterations || p.vus)) {
78
+ ctx.addIssue({
79
+ code: 'custom',
80
+ path: ['stages'],
81
+ message: 'use either stages, or vus with duration or iterations — not both',
82
+ });
83
+ }
84
+ if (!p.stages && !(p.vus && (p.duration || p.iterations))) {
85
+ ctx.addIssue({
86
+ code: 'custom',
87
+ path: ['vus'],
88
+ message: 'set stages, or vus with duration or iterations',
89
+ });
90
+ }
91
+ });
92
+ //# sourceMappingURL=load.js.map
@@ -0,0 +1,45 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * `projects/<slug>/roles.matrix.yaml` — the expected outcome of each role on each surface.
4
+ *
5
+ * `sdods matrix expand` turns every `@matrix:<name>` Scenario Outline into one `Examples:` block per
6
+ * role, tagged `@user:<role>`, so an actor × surface grid is data rather than hundreds of
7
+ * hand-written scenarios. Only the shape lives here; cross-field rules (every row has the same
8
+ * columns, every role resolves to an outcome, `expect` keys are declared roles) are checked by
9
+ * `loadRolesMatrices` in @sdods/core so they can be reported as lint findings.
10
+ */
11
+ export declare const ROLES_MATRIX_FILE = "roles.matrix.yaml";
12
+ /** A matrix name as it appears in `@matrix:<name>`. Checked by the loader for a readable message. */
13
+ export declare const MATRIX_NAME_RE: RegExp;
14
+ export declare const RolesMatrixRowSchema: z.ZodObject<{
15
+ expect: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
16
+ default: z.ZodOptional<z.ZodString>;
17
+ }, z.core.$catchall<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>;
18
+ export declare const RolesMatrixSchema: z.ZodObject<{
19
+ description: z.ZodOptional<z.ZodString>;
20
+ roles: z.ZodArray<z.ZodString>;
21
+ default: z.ZodOptional<z.ZodString>;
22
+ outcomes: z.ZodOptional<z.ZodArray<z.ZodString>>;
23
+ title: z.ZodOptional<z.ZodString>;
24
+ rows: z.ZodArray<z.ZodObject<{
25
+ expect: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
26
+ default: z.ZodOptional<z.ZodString>;
27
+ }, z.core.$catchall<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>>;
28
+ }, z.core.$strict>;
29
+ export declare const RolesMatrixFileSchema: z.ZodObject<{
30
+ matrices: z.ZodRecord<z.ZodString, z.ZodObject<{
31
+ description: z.ZodOptional<z.ZodString>;
32
+ roles: z.ZodArray<z.ZodString>;
33
+ default: z.ZodOptional<z.ZodString>;
34
+ outcomes: z.ZodOptional<z.ZodArray<z.ZodString>>;
35
+ title: z.ZodOptional<z.ZodString>;
36
+ rows: z.ZodArray<z.ZodObject<{
37
+ expect: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>]>>;
38
+ default: z.ZodOptional<z.ZodString>;
39
+ }, z.core.$catchall<z.ZodUnion<readonly [z.ZodString, z.ZodNumber, z.ZodBoolean]>>>>;
40
+ }, z.core.$strict>>;
41
+ }, z.core.$strict>;
42
+ export type RolesMatrixRowInput = z.infer<typeof RolesMatrixRowSchema>;
43
+ export type RolesMatrixInput = z.infer<typeof RolesMatrixSchema>;
44
+ export type RolesMatrixFile = z.infer<typeof RolesMatrixFileSchema>;
45
+ //# sourceMappingURL=matrix.d.ts.map
@@ -0,0 +1,44 @@
1
+ import { z } from 'zod';
2
+ /**
3
+ * `projects/<slug>/roles.matrix.yaml` — the expected outcome of each role on each surface.
4
+ *
5
+ * `sdods matrix expand` turns every `@matrix:<name>` Scenario Outline into one `Examples:` block per
6
+ * role, tagged `@user:<role>`, so an actor × surface grid is data rather than hundreds of
7
+ * hand-written scenarios. Only the shape lives here; cross-field rules (every row has the same
8
+ * columns, every role resolves to an outcome, `expect` keys are declared roles) are checked by
9
+ * `loadRolesMatrices` in @sdods/core so they can be reported as lint findings.
10
+ */
11
+ export const ROLES_MATRIX_FILE = 'roles.matrix.yaml';
12
+ /** A matrix name as it appears in `@matrix:<name>`. Checked by the loader for a readable message. */
13
+ export const MATRIX_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
14
+ const MatrixCellSchema = z.union([z.string(), z.number(), z.boolean()]);
15
+ export const RolesMatrixRowSchema = z
16
+ .object({
17
+ /** One outcome for every role, or a per-role map; roles left out fall back to `default`. */
18
+ expect: z.union([z.string().min(1), z.record(z.string(), z.string().min(1))]).optional(),
19
+ /** Outcome for the roles this row's `expect` map does not name. Overrides the matrix default. */
20
+ default: z.string().min(1).optional(),
21
+ })
22
+ // Every other key is an Examples column (`surface: /settings/billing` → `<surface>`).
23
+ .catchall(MatrixCellSchema);
24
+ export const RolesMatrixSchema = z
25
+ .object({
26
+ description: z.string().optional(),
27
+ /** The actors, in the order their Examples blocks are written. Each must be a declared role. */
28
+ roles: z.array(z.string().regex(/^[^\s@|]+$/, 'a role cannot contain spaces, @ or |')).min(1),
29
+ /** Outcome for any role a row does not name. */
30
+ default: z.string().min(1).optional(),
31
+ /** When set, every outcome must be one of these (catches `alowed`). */
32
+ outcomes: z.array(z.string().min(1)).min(1).optional(),
33
+ /** Example title, written as the `# title-format:` of each generated block. Must use `<role>` and every row column, so titles are unique. */
34
+ title: z.string().min(1).optional(),
35
+ rows: z.array(RolesMatrixRowSchema).min(1),
36
+ })
37
+ .strict();
38
+ export const RolesMatrixFileSchema = z
39
+ .object({
40
+ /** Keys are matrix names (`MATRIX_NAME_RE`), referenced as `@matrix:<name>`. */
41
+ matrices: z.record(z.string(), RolesMatrixSchema),
42
+ })
43
+ .strict();
44
+ //# sourceMappingURL=matrix.js.map
@@ -48,15 +48,19 @@ export declare const UserPoolSchema: z.ZodObject<{
48
48
  roleColumn: z.ZodDefault<z.ZodString>;
49
49
  leaseStore: z.ZodDefault<z.ZodEnum<{
50
50
  file: "file";
51
- db: "db";
52
51
  }>>;
53
52
  leaseTtlMs: z.ZodDefault<z.ZodNumber>;
54
53
  mode: z.ZodDefault<z.ZodEnum<{
55
54
  exclusive: "exclusive";
56
55
  shared: "shared";
57
56
  }>>;
58
- waitMs: z.ZodDefault<z.ZodNumber>;
57
+ waitMs: z.ZodOptional<z.ZodNumber>;
58
+ leaseScope: z.ZodDefault<z.ZodEnum<{
59
+ scenario: "scenario";
60
+ worker: "worker";
61
+ }>>;
59
62
  }, z.core.$strip>;
63
+ export type UserPoolConfig = z.infer<typeof UserPoolSchema>;
60
64
  export declare const AuthStrategySchema: z.ZodEnum<{
61
65
  custom: "custom";
62
66
  none: "none";
@@ -106,6 +110,14 @@ export declare const ProjectAuthSchema: z.ZodObject<{
106
110
  prefix: z.ZodDefault<z.ZodString>;
107
111
  }, z.core.$strip>>;
108
112
  }, z.core.$strip>;
113
+ /**
114
+ * Settings for one named visual baseline (`the page should match the visual baseline "<name>"`).
115
+ * `mask` is added to the project/env `screenshots.mask`, never a replacement for it.
116
+ */
117
+ export declare const BaselineConfigSchema: z.ZodObject<{
118
+ mask: z.ZodOptional<z.ZodArray<z.ZodString>>;
119
+ maxDiffPixelRatio: z.ZodOptional<z.ZodNumber>;
120
+ }, z.core.$strip>;
109
121
  export declare const ScreenshotConfigSchema: z.ZodObject<{
110
122
  policy: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodEnum<{
111
123
  visual: "visual";
@@ -116,6 +128,11 @@ export declare const ScreenshotConfigSchema: z.ZodObject<{
116
128
  }>>>;
117
129
  fullPage: z.ZodDefault<z.ZodBoolean>;
118
130
  mask: z.ZodDefault<z.ZodArray<z.ZodString>>;
131
+ maxDiffPixelRatio: z.ZodDefault<z.ZodNumber>;
132
+ baselines: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
133
+ mask: z.ZodOptional<z.ZodArray<z.ZodString>>;
134
+ maxDiffPixelRatio: z.ZodOptional<z.ZodNumber>;
135
+ }, z.core.$strip>>>;
119
136
  viewport: z.ZodDefault<z.ZodObject<{
120
137
  width: z.ZodNumber;
121
138
  height: z.ZodNumber;
@@ -229,6 +246,43 @@ export declare const EVIDENCE_DEFAULTS: {
229
246
  readonly trace: "on-first-retry";
230
247
  readonly redactTraces: true;
231
248
  };
249
+ /**
250
+ * Selectors the canvas steps (`canvas.steps.ts`) use to find nodes, handles, edges, node fields and
251
+ * badges on a graph editor. The defaults are the DOM React Flow (v11 and v12) renders; a project on
252
+ * another editor, or with its own markers, overrides only the keys that differ.
253
+ *
254
+ * Values are CSS selector templates. `{id}`, `{handle}`, `{source}`, `{target}`, `{field}` and
255
+ * `{badge}` are replaced by the step argument, escaped for a quoted CSS string, so write them inside
256
+ * quotes: `[data-id="{id}"]`. `{testIdAttribute}` is replaced by the project's attribute name.
257
+ */
258
+ export declare const CANVAS_DEFAULTS: {
259
+ /** The editor surface; every other lookup is scoped inside it. */
260
+ readonly root: ".react-flow";
261
+ /** Every node, for counting. */
262
+ readonly nodes: ".react-flow__node";
263
+ /** One node by id. */
264
+ readonly node: ".react-flow__node[data-id=\"{id}\"]";
265
+ /** A handle, searched inside its node. */
266
+ readonly handle: ".react-flow__handle[data-handleid=\"{handle}\"]";
267
+ /** An edge by source and target node id. React Flow's default edge aria-label. */
268
+ readonly edge: ".react-flow__edge[aria-label=\"Edge from {source} to {target}\"]";
269
+ /** A field, searched inside its node. A wrapper is fine: the editable control inside it is filled. */
270
+ readonly field: "[name=\"{field}\"], [aria-label=\"{field}\"], [{testIdAttribute}=\"{field}\"]";
271
+ /** A badge, searched inside its node. */
272
+ readonly badge: "[{testIdAttribute}=\"{badge}\"]";
273
+ /** What a node matches once selected; `I select the node` waits for it. Empty skips the check. */
274
+ readonly selected: ".selected";
275
+ };
276
+ export declare const CanvasConfigSchema: z.ZodDefault<z.ZodObject<{
277
+ root: z.ZodDefault<z.ZodString>;
278
+ nodes: z.ZodDefault<z.ZodString>;
279
+ node: z.ZodDefault<z.ZodString>;
280
+ handle: z.ZodDefault<z.ZodString>;
281
+ edge: z.ZodDefault<z.ZodString>;
282
+ field: z.ZodDefault<z.ZodString>;
283
+ badge: z.ZodDefault<z.ZodString>;
284
+ selected: z.ZodDefault<z.ZodString>;
285
+ }, z.core.$strip>>;
232
286
  /**
233
287
  * Scenarios that must pass before anything else in the run starts: probes, login, seeding.
234
288
  *
@@ -252,6 +306,45 @@ export declare const PerfBudgetsSchema: z.ZodObject<{
252
306
  ttfbMs: z.ZodOptional<z.ZodNumber>;
253
307
  apiP95Ms: z.ZodOptional<z.ZodNumber>;
254
308
  }, z.core.$strip>;
309
+ /** axe impact levels, weakest first. Mirrored by `IMPACT_ORDER` in core's a11y audit. */
310
+ export declare const A11yImpactSchema: z.ZodEnum<{
311
+ minor: "minor";
312
+ moderate: "moderate";
313
+ serious: "serious";
314
+ critical: "critical";
315
+ }>;
316
+ /**
317
+ * The audit every `@a11y` UI or hybrid scenario gets at its end.
318
+ *
319
+ * `failOn` is the weakest impact that fails the scenario: `serious` fails on serious and critical
320
+ * violations and reports the rest. `include` narrows the audit to these selectors, `exclude` leaves
321
+ * these selectors out (a third-party widget the team does not own, for instance). Both are passed
322
+ * to axe as they are, so a selector that matches nothing in `include` fails the audit rather than
323
+ * scanning an empty page.
324
+ */
325
+ export declare const A11yConfigSchema: z.ZodObject<{
326
+ failOn: z.ZodDefault<z.ZodEnum<{
327
+ minor: "minor";
328
+ moderate: "moderate";
329
+ serious: "serious";
330
+ critical: "critical";
331
+ }>>;
332
+ include: z.ZodDefault<z.ZodArray<z.ZodString>>;
333
+ exclude: z.ZodDefault<z.ZodArray<z.ZodString>>;
334
+ }, z.core.$strip>;
335
+ /** The env-level patch: every key optional and default-free, for the reason `EvidencePatchSchema` gives. */
336
+ export declare const A11yPatchSchema: z.ZodObject<{
337
+ failOn: z.ZodOptional<z.ZodEnum<{
338
+ minor: "minor";
339
+ moderate: "moderate";
340
+ serious: "serious";
341
+ critical: "critical";
342
+ }>>;
343
+ include: z.ZodOptional<z.ZodArray<z.ZodString>>;
344
+ exclude: z.ZodOptional<z.ZodArray<z.ZodString>>;
345
+ }, z.core.$strip>;
346
+ /** Branch names `integrations.github.evidence.branch` refuses: prune force-rewrites the branch. */
347
+ export declare const EVIDENCE_PROTECTED_BRANCHES: string[];
255
348
  export declare const GitHubIntegrationSchema: z.ZodObject<{
256
349
  enabled: z.ZodDefault<z.ZodBoolean>;
257
350
  owner: z.ZodOptional<z.ZodString>;
@@ -267,6 +360,19 @@ export declare const GitHubIntegrationSchema: z.ZodObject<{
267
360
  labels: z.ZodDefault<z.ZodArray<z.ZodString>>;
268
361
  tokenEnv: z.ZodDefault<z.ZodString>;
269
362
  uploadToRelease: z.ZodOptional<z.ZodString>;
363
+ evidence: z.ZodOptional<z.ZodObject<{
364
+ host: z.ZodDefault<z.ZodEnum<{
365
+ none: "none";
366
+ branch: "branch";
367
+ }>>;
368
+ repo: z.ZodOptional<z.ZodString>;
369
+ branch: z.ZodDefault<z.ZodString>;
370
+ tokenEnv: z.ZodOptional<z.ZodString>;
371
+ maxFileBytes: z.ZodDefault<z.ZodNumber>;
372
+ maxRunBytes: z.ZodDefault<z.ZodNumber>;
373
+ retainDays: z.ZodDefault<z.ZodNumber>;
374
+ gifPreview: z.ZodDefault<z.ZodBoolean>;
375
+ }, z.core.$strip>>;
270
376
  }, z.core.$strip>;
271
377
  export declare const JiraIntegrationSchema: z.ZodObject<{
272
378
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -305,6 +411,19 @@ export declare const IntegrationsSchema: z.ZodObject<{
305
411
  labels: z.ZodDefault<z.ZodArray<z.ZodString>>;
306
412
  tokenEnv: z.ZodDefault<z.ZodString>;
307
413
  uploadToRelease: z.ZodOptional<z.ZodString>;
414
+ evidence: z.ZodOptional<z.ZodObject<{
415
+ host: z.ZodDefault<z.ZodEnum<{
416
+ none: "none";
417
+ branch: "branch";
418
+ }>>;
419
+ repo: z.ZodOptional<z.ZodString>;
420
+ branch: z.ZodDefault<z.ZodString>;
421
+ tokenEnv: z.ZodOptional<z.ZodString>;
422
+ maxFileBytes: z.ZodDefault<z.ZodNumber>;
423
+ maxRunBytes: z.ZodDefault<z.ZodNumber>;
424
+ retainDays: z.ZodDefault<z.ZodNumber>;
425
+ gifPreview: z.ZodDefault<z.ZodBoolean>;
426
+ }, z.core.$strip>>;
308
427
  }, z.core.$strip>>;
309
428
  jira: z.ZodOptional<z.ZodObject<{
310
429
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -442,6 +561,17 @@ export declare const ScheduleSchema: z.ZodObject<{
442
561
  }>>>;
443
562
  retentionRuns: z.ZodOptional<z.ZodNumber>;
444
563
  }, z.core.$strip>;
564
+ /**
565
+ * Requirement traceability (`@req:<id>` tags → `sdods report traceability`).
566
+ *
567
+ * The id is opaque: SDODS does not decide whether it names a Jira story, a line in a requirements
568
+ * document or a clause of a regulation. That choice belongs to whoever signs the export.
569
+ */
570
+ export declare const TraceabilitySchema: z.ZodObject<{
571
+ requirements: z.ZodOptional<z.ZodString>;
572
+ link: z.ZodOptional<z.ZodString>;
573
+ require: z.ZodDefault<z.ZodBoolean>;
574
+ }, z.core.$strip>;
445
575
  /**
446
576
  * Hierarchy: organization → workspace → project → module.
447
577
  * A module is a feature area of an application (auth, inventory, checkout …) that owns a
@@ -688,6 +818,16 @@ export declare const ProjectConfigSchema: z.ZodObject<{
688
818
  }, z.core.$strip>>;
689
819
  }, z.core.$strip>>;
690
820
  routes: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodString>>;
821
+ canvas: z.ZodDefault<z.ZodObject<{
822
+ root: z.ZodDefault<z.ZodString>;
823
+ nodes: z.ZodDefault<z.ZodString>;
824
+ node: z.ZodDefault<z.ZodString>;
825
+ handle: z.ZodDefault<z.ZodString>;
826
+ edge: z.ZodDefault<z.ZodString>;
827
+ field: z.ZodDefault<z.ZodString>;
828
+ badge: z.ZodDefault<z.ZodString>;
829
+ selected: z.ZodDefault<z.ZodString>;
830
+ }, z.core.$strip>>;
691
831
  tags: z.ZodDefault<z.ZodObject<{
692
832
  suites: z.ZodDefault<z.ZodArray<z.ZodString>>;
693
833
  extra: z.ZodDefault<z.ZodArray<z.ZodString>>;
@@ -725,14 +865,17 @@ export declare const ProjectConfigSchema: z.ZodObject<{
725
865
  roleColumn: z.ZodDefault<z.ZodString>;
726
866
  leaseStore: z.ZodDefault<z.ZodEnum<{
727
867
  file: "file";
728
- db: "db";
729
868
  }>>;
730
869
  leaseTtlMs: z.ZodDefault<z.ZodNumber>;
731
870
  mode: z.ZodDefault<z.ZodEnum<{
732
871
  exclusive: "exclusive";
733
872
  shared: "shared";
734
873
  }>>;
735
- waitMs: z.ZodDefault<z.ZodNumber>;
874
+ waitMs: z.ZodOptional<z.ZodNumber>;
875
+ leaseScope: z.ZodDefault<z.ZodEnum<{
876
+ scenario: "scenario";
877
+ worker: "worker";
878
+ }>>;
736
879
  }, z.core.$strip>>;
737
880
  }, z.core.$strip>>;
738
881
  auth: z.ZodDefault<z.ZodObject<{
@@ -778,6 +921,11 @@ export declare const ProjectConfigSchema: z.ZodObject<{
778
921
  }>>>;
779
922
  fullPage: z.ZodDefault<z.ZodBoolean>;
780
923
  mask: z.ZodDefault<z.ZodArray<z.ZodString>>;
924
+ maxDiffPixelRatio: z.ZodDefault<z.ZodNumber>;
925
+ baselines: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
926
+ mask: z.ZodOptional<z.ZodArray<z.ZodString>>;
927
+ maxDiffPixelRatio: z.ZodOptional<z.ZodNumber>;
928
+ }, z.core.$strip>>>;
781
929
  viewport: z.ZodDefault<z.ZodObject<{
782
930
  width: z.ZodNumber;
783
931
  height: z.ZodNumber;
@@ -850,6 +998,16 @@ export declare const ProjectConfigSchema: z.ZodObject<{
850
998
  apiP95Ms: z.ZodOptional<z.ZodNumber>;
851
999
  }, z.core.$strip>>;
852
1000
  }, z.core.$strip>>;
1001
+ a11y: z.ZodDefault<z.ZodObject<{
1002
+ failOn: z.ZodDefault<z.ZodEnum<{
1003
+ minor: "minor";
1004
+ moderate: "moderate";
1005
+ serious: "serious";
1006
+ critical: "critical";
1007
+ }>>;
1008
+ include: z.ZodDefault<z.ZodArray<z.ZodString>>;
1009
+ exclude: z.ZodDefault<z.ZodArray<z.ZodString>>;
1010
+ }, z.core.$strip>>;
853
1011
  integrations: z.ZodDefault<z.ZodObject<{
854
1012
  github: z.ZodOptional<z.ZodObject<{
855
1013
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -866,6 +1024,19 @@ export declare const ProjectConfigSchema: z.ZodObject<{
866
1024
  labels: z.ZodDefault<z.ZodArray<z.ZodString>>;
867
1025
  tokenEnv: z.ZodDefault<z.ZodString>;
868
1026
  uploadToRelease: z.ZodOptional<z.ZodString>;
1027
+ evidence: z.ZodOptional<z.ZodObject<{
1028
+ host: z.ZodDefault<z.ZodEnum<{
1029
+ none: "none";
1030
+ branch: "branch";
1031
+ }>>;
1032
+ repo: z.ZodOptional<z.ZodString>;
1033
+ branch: z.ZodDefault<z.ZodString>;
1034
+ tokenEnv: z.ZodOptional<z.ZodString>;
1035
+ maxFileBytes: z.ZodDefault<z.ZodNumber>;
1036
+ maxRunBytes: z.ZodDefault<z.ZodNumber>;
1037
+ retainDays: z.ZodDefault<z.ZodNumber>;
1038
+ gifPreview: z.ZodDefault<z.ZodBoolean>;
1039
+ }, z.core.$strip>>;
869
1040
  }, z.core.$strip>>;
870
1041
  jira: z.ZodOptional<z.ZodObject<{
871
1042
  enabled: z.ZodDefault<z.ZodBoolean>;
@@ -984,6 +1155,11 @@ export declare const ProjectConfigSchema: z.ZodObject<{
984
1155
  allure: z.ZodDefault<z.ZodBoolean>;
985
1156
  junit: z.ZodDefault<z.ZodBoolean>;
986
1157
  }, z.core.$strip>>;
1158
+ traceability: z.ZodOptional<z.ZodObject<{
1159
+ requirements: z.ZodOptional<z.ZodString>;
1160
+ link: z.ZodOptional<z.ZodString>;
1161
+ require: z.ZodDefault<z.ZodBoolean>;
1162
+ }, z.core.$strip>>;
987
1163
  ci: z.ZodDefault<z.ZodObject<{
988
1164
  enabled: z.ZodDefault<z.ZodBoolean>;
989
1165
  env: z.ZodOptional<z.ZodString>;
@@ -1090,9 +1266,15 @@ export type TestingType = z.infer<typeof TestingTypeSchema>;
1090
1266
  export type ProjectConfig = z.infer<typeof ProjectConfigSchema>;
1091
1267
  export type ProjectConfigInput = z.input<typeof ProjectConfigSchema>;
1092
1268
  export type ScreenshotConfig = z.infer<typeof ScreenshotConfigSchema>;
1269
+ export type BaselineConfig = z.infer<typeof BaselineConfigSchema>;
1093
1270
  export type EvidenceConfig = z.infer<typeof EvidenceSchema>;
1271
+ export type CanvasConfig = z.infer<typeof CanvasConfigSchema>;
1094
1272
  export type SetupConfig = z.infer<typeof SetupSchema>;
1273
+ export type TraceabilityConfig = z.infer<typeof TraceabilitySchema>;
1095
1274
  export type HealConfig = z.infer<typeof HealConfigSchema>;
1275
+ export type A11yConfig = z.infer<typeof A11yConfigSchema>;
1276
+ export type A11yImpact = z.infer<typeof A11yImpactSchema>;
1277
+ export type ProcessGates = ProcessConfig['gates'];
1096
1278
  export type TimeoutsConfig = z.infer<typeof TimeoutsSchema>;
1097
1279
  export type ProjectAuthConfig = z.infer<typeof ProjectAuthSchema>;
1098
1280
  export type McpServerConfig = z.infer<typeof McpServerSchema>;