gateproof 0.1.0 → 0.2.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.
- package/README.md +277 -7
- package/dist/act.d.ts +2 -0
- package/dist/act.d.ts.map +1 -1
- package/dist/act.js +1 -1
- package/dist/act.js.map +1 -1
- package/dist/action-executors.d.ts +10 -0
- package/dist/action-executors.d.ts.map +1 -1
- package/dist/action-executors.js +14 -3
- package/dist/action-executors.js.map +1 -1
- package/dist/assert.d.ts +2 -2
- package/dist/assert.d.ts.map +1 -1
- package/dist/assert.js +1 -1
- package/dist/assert.js.map +1 -1
- package/dist/cloudflare/cli-stream.d.ts.map +1 -1
- package/dist/cloudflare/cli-stream.js +7 -3
- package/dist/cloudflare/cli-stream.js.map +1 -1
- package/dist/constants.d.ts +11 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +11 -0
- package/dist/constants.js.map +1 -0
- package/dist/http-backend.d.ts +23 -0
- package/dist/http-backend.d.ts.map +1 -0
- package/dist/http-backend.js +124 -0
- package/dist/http-backend.js.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +97 -49
- package/dist/index.js.map +1 -1
- package/dist/observe.d.ts.map +1 -1
- package/dist/observe.js +4 -3
- package/dist/observe.js.map +1 -1
- package/dist/prd/define-prd.d.ts +7 -0
- package/dist/prd/define-prd.d.ts.map +1 -0
- package/dist/prd/define-prd.js +8 -0
- package/dist/prd/define-prd.js.map +1 -0
- package/dist/prd/index.d.ts +5 -0
- package/dist/prd/index.d.ts.map +1 -0
- package/dist/prd/index.js +4 -0
- package/dist/prd/index.js.map +1 -0
- package/dist/prd/runner.d.ts +22 -0
- package/dist/prd/runner.d.ts.map +1 -0
- package/dist/prd/runner.js +221 -0
- package/dist/prd/runner.js.map +1 -0
- package/dist/prd/scope-check.d.ts +28 -0
- package/dist/prd/scope-check.d.ts.map +1 -0
- package/dist/prd/scope-check.js +135 -0
- package/dist/prd/scope-check.js.map +1 -0
- package/dist/prd/types.d.ts +21 -0
- package/dist/prd/types.d.ts.map +1 -0
- package/dist/prd/types.js +2 -0
- package/dist/prd/types.js.map +1 -0
- package/dist/report.d.ts +67 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +51 -0
- package/dist/report.js.map +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +3 -2
- package/dist/utils.js.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +7 -3
- package/dist/validation.js.map +1 -1
- package/package.json +11 -3
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import { resolve } from "path";
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { serializeError } from "../report";
|
|
4
|
+
import { validateScope, getDiffStats } from "./scope-check";
|
|
5
|
+
/**
|
|
6
|
+
* Validates that all story dependencies exist and there are no cycles.
|
|
7
|
+
*/
|
|
8
|
+
function validateDependencies(stories) {
|
|
9
|
+
const byId = new Map(stories.map((s) => [s.id, s]));
|
|
10
|
+
for (const story of stories) {
|
|
11
|
+
const deps = story.dependsOn ?? [];
|
|
12
|
+
for (const depId of deps) {
|
|
13
|
+
if (!byId.has(depId)) {
|
|
14
|
+
throw new Error(`Story "${story.id}" depends on unknown story "${depId}"`);
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Topologically sorts stories by their dependencies.
|
|
21
|
+
* Throws if there's a cycle or missing dependency.
|
|
22
|
+
*/
|
|
23
|
+
function orderStories(stories) {
|
|
24
|
+
validateDependencies(stories);
|
|
25
|
+
const byId = new Map(stories.map((s) => [s.id, s]));
|
|
26
|
+
const out = [];
|
|
27
|
+
const remaining = new Set(stories.map((s) => s.id));
|
|
28
|
+
const visiting = new Set();
|
|
29
|
+
function visit(id) {
|
|
30
|
+
if (visiting.has(id)) {
|
|
31
|
+
throw new Error(`PRD dependency cycle detected involving "${id}"`);
|
|
32
|
+
}
|
|
33
|
+
if (!remaining.has(id)) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
visiting.add(id);
|
|
37
|
+
const story = byId.get(id);
|
|
38
|
+
if (!story) {
|
|
39
|
+
throw new Error(`Unknown story id: ${id}`);
|
|
40
|
+
}
|
|
41
|
+
const deps = story.dependsOn ?? [];
|
|
42
|
+
for (const depId of deps) {
|
|
43
|
+
visit(depId);
|
|
44
|
+
}
|
|
45
|
+
visiting.delete(id);
|
|
46
|
+
remaining.delete(id);
|
|
47
|
+
out.push(story);
|
|
48
|
+
}
|
|
49
|
+
while (remaining.size > 0) {
|
|
50
|
+
const nextId = Array.from(remaining)[0];
|
|
51
|
+
visit(nextId);
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Loads default forbidden paths from .gateproof/scope.defaults.json
|
|
57
|
+
*/
|
|
58
|
+
function loadDefaultForbidden(cwd) {
|
|
59
|
+
try {
|
|
60
|
+
const defaultsPath = resolve(cwd, ".gateproof/scope.defaults.json");
|
|
61
|
+
const content = readFileSync(defaultsPath, "utf-8");
|
|
62
|
+
const parsed = JSON.parse(content);
|
|
63
|
+
return parsed.forbiddenPaths ?? [];
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
return [];
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Runs a PRD by executing story gates in dependency order.
|
|
71
|
+
* Stops on first failure.
|
|
72
|
+
*
|
|
73
|
+
* @param options.reportPath - If provided, writes a structured JSON report to this path
|
|
74
|
+
* @param options.checkScope - If true, validates scope constraints against git diff
|
|
75
|
+
* @param options.baseRef - Git ref to compare against for scope checking (default: "HEAD" for local, "origin/main" for CI)
|
|
76
|
+
*/
|
|
77
|
+
export async function runPrd(prd, cwd = process.cwd(), options = {}) {
|
|
78
|
+
const ordered = orderStories(prd.stories);
|
|
79
|
+
const storyResults = [];
|
|
80
|
+
const startTime = Date.now();
|
|
81
|
+
const defaultForbidden = loadDefaultForbidden(cwd);
|
|
82
|
+
const baseRef = options.baseRef ?? (process.env.CI ? "origin/main" : "HEAD");
|
|
83
|
+
for (const story of ordered) {
|
|
84
|
+
const storyStartTime = Date.now();
|
|
85
|
+
let storyError;
|
|
86
|
+
try {
|
|
87
|
+
if (options.checkScope && story.scope) {
|
|
88
|
+
const diffStats = getDiffStats(baseRef);
|
|
89
|
+
const violations = validateScope(story, diffStats, defaultForbidden);
|
|
90
|
+
if (violations.length > 0) {
|
|
91
|
+
const violation = violations[0];
|
|
92
|
+
storyError = {
|
|
93
|
+
name: "ScopeViolation",
|
|
94
|
+
message: violation.message,
|
|
95
|
+
tag: violation.category,
|
|
96
|
+
};
|
|
97
|
+
storyResults.push({
|
|
98
|
+
id: story.id,
|
|
99
|
+
title: story.title,
|
|
100
|
+
gateFile: story.gateFile,
|
|
101
|
+
status: "failed",
|
|
102
|
+
durationMs: Date.now() - storyStartTime,
|
|
103
|
+
error: storyError,
|
|
104
|
+
});
|
|
105
|
+
return {
|
|
106
|
+
success: false,
|
|
107
|
+
failedStory: story,
|
|
108
|
+
error: new Error(violation.message),
|
|
109
|
+
report: {
|
|
110
|
+
version: "1",
|
|
111
|
+
success: false,
|
|
112
|
+
stories: storyResults,
|
|
113
|
+
failedStory: {
|
|
114
|
+
id: story.id,
|
|
115
|
+
title: story.title,
|
|
116
|
+
gateFile: story.gateFile,
|
|
117
|
+
},
|
|
118
|
+
totalDurationMs: Date.now() - startTime,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
const gatePath = resolve(cwd, story.gateFile);
|
|
124
|
+
const mod = await import(`file://${gatePath}`);
|
|
125
|
+
const run = mod.run;
|
|
126
|
+
if (typeof run !== "function") {
|
|
127
|
+
throw new Error(`Gate file must export "run" function: ${story.gateFile}`);
|
|
128
|
+
}
|
|
129
|
+
console.log(`\n--- ${story.id}: ${story.title}`);
|
|
130
|
+
const result = (await run());
|
|
131
|
+
const durationMs = Date.now() - storyStartTime;
|
|
132
|
+
if (result.status !== "success") {
|
|
133
|
+
storyError = result.error ? serializeError(result.error) : {
|
|
134
|
+
name: "GateFailed",
|
|
135
|
+
message: `Gate failed with status: ${result.status}`,
|
|
136
|
+
};
|
|
137
|
+
storyResults.push({
|
|
138
|
+
id: story.id,
|
|
139
|
+
title: story.title,
|
|
140
|
+
gateFile: story.gateFile,
|
|
141
|
+
status: result.status,
|
|
142
|
+
durationMs,
|
|
143
|
+
error: storyError,
|
|
144
|
+
});
|
|
145
|
+
const report = {
|
|
146
|
+
version: "1",
|
|
147
|
+
success: false,
|
|
148
|
+
stories: storyResults,
|
|
149
|
+
failedStory: {
|
|
150
|
+
id: story.id,
|
|
151
|
+
title: story.title,
|
|
152
|
+
gateFile: story.gateFile,
|
|
153
|
+
},
|
|
154
|
+
totalDurationMs: Date.now() - startTime,
|
|
155
|
+
};
|
|
156
|
+
if (options.reportPath) {
|
|
157
|
+
const { writeFileSync } = await import("node:fs");
|
|
158
|
+
writeFileSync(options.reportPath, JSON.stringify(report, null, 2));
|
|
159
|
+
}
|
|
160
|
+
return {
|
|
161
|
+
success: false,
|
|
162
|
+
failedStory: story,
|
|
163
|
+
error: new Error(`Gate failed with status: ${result.status}`),
|
|
164
|
+
report,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
storyResults.push({
|
|
168
|
+
id: story.id,
|
|
169
|
+
title: story.title,
|
|
170
|
+
gateFile: story.gateFile,
|
|
171
|
+
status: "success",
|
|
172
|
+
durationMs,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
const durationMs = Date.now() - storyStartTime;
|
|
177
|
+
storyError = serializeError(error);
|
|
178
|
+
storyResults.push({
|
|
179
|
+
id: story.id,
|
|
180
|
+
title: story.title,
|
|
181
|
+
gateFile: story.gateFile,
|
|
182
|
+
status: "failed",
|
|
183
|
+
durationMs,
|
|
184
|
+
error: storyError,
|
|
185
|
+
});
|
|
186
|
+
const report = {
|
|
187
|
+
version: "1",
|
|
188
|
+
success: false,
|
|
189
|
+
stories: storyResults,
|
|
190
|
+
failedStory: {
|
|
191
|
+
id: story.id,
|
|
192
|
+
title: story.title,
|
|
193
|
+
gateFile: story.gateFile,
|
|
194
|
+
},
|
|
195
|
+
totalDurationMs: Date.now() - startTime,
|
|
196
|
+
};
|
|
197
|
+
if (options.reportPath) {
|
|
198
|
+
const { writeFileSync } = await import("node:fs");
|
|
199
|
+
writeFileSync(options.reportPath, JSON.stringify(report, null, 2));
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
success: false,
|
|
203
|
+
failedStory: story,
|
|
204
|
+
error: error instanceof Error ? error : new Error(String(error)),
|
|
205
|
+
report,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
const report = {
|
|
210
|
+
version: "1",
|
|
211
|
+
success: true,
|
|
212
|
+
stories: storyResults,
|
|
213
|
+
totalDurationMs: Date.now() - startTime,
|
|
214
|
+
};
|
|
215
|
+
if (options.reportPath) {
|
|
216
|
+
const { writeFileSync } = await import("node:fs");
|
|
217
|
+
writeFileSync(options.reportPath, JSON.stringify(report, null, 2));
|
|
218
|
+
}
|
|
219
|
+
return { success: true, report };
|
|
220
|
+
}
|
|
221
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../src/prd/runner.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Story } from "./types";
|
|
2
|
+
export interface ScopeViolation {
|
|
3
|
+
category: "scope_violation";
|
|
4
|
+
message: string;
|
|
5
|
+
details: {
|
|
6
|
+
storyId: string;
|
|
7
|
+
violation: "forbidden_path" | "max_files_exceeded" | "max_lines_exceeded" | "default_forbidden";
|
|
8
|
+
path?: string;
|
|
9
|
+
actualFiles?: number;
|
|
10
|
+
maxFiles?: number;
|
|
11
|
+
actualLines?: number;
|
|
12
|
+
maxLines?: number;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface DiffStats {
|
|
16
|
+
changedFiles: string[];
|
|
17
|
+
totalLines: number;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Gets git diff stats against a base ref.
|
|
21
|
+
* For CI PRs, use origin/main. For local, use HEAD or a passed ref.
|
|
22
|
+
*/
|
|
23
|
+
export declare function getDiffStats(baseRef?: string): DiffStats;
|
|
24
|
+
/**
|
|
25
|
+
* Validates that changed files match the story's scope constraints.
|
|
26
|
+
*/
|
|
27
|
+
export declare function validateScope(story: Story, diffStats: DiffStats, defaultForbidden?: string[]): ScopeViolation[];
|
|
28
|
+
//# sourceMappingURL=scope-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-check.d.ts","sourceRoot":"","sources":["../../src/prd/scope-check.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAc,MAAM,SAAS,CAAC;AAEjD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,gBAAgB,GAAG,oBAAoB,GAAG,oBAAoB,GAAG,mBAAmB,CAAC;QAChG,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,MAAM,WAAW,SAAS;IACxB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,MAAe,GAAG,SAAS,CA8BhE;AAiBD;;GAEG;AACH,wBAAgB,aAAa,CAC3B,KAAK,EAAE,KAAK,EACZ,SAAS,EAAE,SAAS,EACpB,gBAAgB,GAAE,MAAM,EAAO,GAC9B,cAAc,EAAE,CAyFlB"}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { execSync } from "node:child_process";
|
|
2
|
+
import { resolve } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Gets git diff stats against a base ref.
|
|
5
|
+
* For CI PRs, use origin/main. For local, use HEAD or a passed ref.
|
|
6
|
+
*/
|
|
7
|
+
export function getDiffStats(baseRef = "HEAD") {
|
|
8
|
+
try {
|
|
9
|
+
const changedFiles = execSync(`git diff --name-only ${baseRef}`, {
|
|
10
|
+
encoding: "utf-8",
|
|
11
|
+
cwd: process.cwd(),
|
|
12
|
+
})
|
|
13
|
+
.trim()
|
|
14
|
+
.split("\n")
|
|
15
|
+
.filter((f) => f.length > 0);
|
|
16
|
+
let totalLines = 0;
|
|
17
|
+
if (changedFiles.length > 0) {
|
|
18
|
+
const diffStat = execSync(`git diff --shortstat ${baseRef}`, {
|
|
19
|
+
encoding: "utf-8",
|
|
20
|
+
cwd: process.cwd(),
|
|
21
|
+
}).trim();
|
|
22
|
+
const match = diffStat.match(/(\d+)\s+files? changed/);
|
|
23
|
+
if (match) {
|
|
24
|
+
const linesMatch = diffStat.match(/(\d+)\s+insertions?/);
|
|
25
|
+
const deletionsMatch = diffStat.match(/(\d+)\s+deletions?/);
|
|
26
|
+
const insertions = linesMatch ? parseInt(linesMatch[1], 10) : 0;
|
|
27
|
+
const deletions = deletionsMatch ? parseInt(deletionsMatch[1], 10) : 0;
|
|
28
|
+
totalLines = insertions + deletions;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return { changedFiles, totalLines };
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
throw new Error(`Failed to get git diff stats: ${error instanceof Error ? error.message : String(error)}`);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Checks if a path matches any of the glob patterns.
|
|
39
|
+
* Simple prefix/suffix matching for now (not full glob).
|
|
40
|
+
*/
|
|
41
|
+
function matchesPattern(path, pattern) {
|
|
42
|
+
if (pattern.includes("**")) {
|
|
43
|
+
const prefix = pattern.replace(/\*\*/g, "");
|
|
44
|
+
return path.startsWith(prefix);
|
|
45
|
+
}
|
|
46
|
+
if (pattern.endsWith("*")) {
|
|
47
|
+
return path.startsWith(pattern.slice(0, -1));
|
|
48
|
+
}
|
|
49
|
+
return path === pattern;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Validates that changed files match the story's scope constraints.
|
|
53
|
+
*/
|
|
54
|
+
export function validateScope(story, diffStats, defaultForbidden = []) {
|
|
55
|
+
const violations = [];
|
|
56
|
+
const scope = story.scope;
|
|
57
|
+
if (!scope) {
|
|
58
|
+
return violations;
|
|
59
|
+
}
|
|
60
|
+
const { changedFiles, totalLines } = diffStats;
|
|
61
|
+
for (const file of changedFiles) {
|
|
62
|
+
const filePath = resolve(process.cwd(), file);
|
|
63
|
+
if (scope.forbiddenPaths) {
|
|
64
|
+
for (const forbidden of scope.forbiddenPaths) {
|
|
65
|
+
if (matchesPattern(file, forbidden)) {
|
|
66
|
+
violations.push({
|
|
67
|
+
category: "scope_violation",
|
|
68
|
+
message: `Story "${story.id}" changed forbidden path: ${file}`,
|
|
69
|
+
details: {
|
|
70
|
+
storyId: story.id,
|
|
71
|
+
violation: "forbidden_path",
|
|
72
|
+
path: file,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
for (const forbidden of defaultForbidden) {
|
|
79
|
+
if (matchesPattern(file, forbidden)) {
|
|
80
|
+
const isAllowed = scope.allowedPaths?.some((allowed) => matchesPattern(file, allowed));
|
|
81
|
+
if (!isAllowed) {
|
|
82
|
+
violations.push({
|
|
83
|
+
category: "scope_violation",
|
|
84
|
+
message: `Story "${story.id}" changed default-forbidden path: ${file}`,
|
|
85
|
+
details: {
|
|
86
|
+
storyId: story.id,
|
|
87
|
+
violation: "default_forbidden",
|
|
88
|
+
path: file,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (scope.allowedPaths && scope.allowedPaths.length > 0) {
|
|
95
|
+
const isAllowed = scope.allowedPaths.some((allowed) => matchesPattern(file, allowed));
|
|
96
|
+
if (!isAllowed) {
|
|
97
|
+
violations.push({
|
|
98
|
+
category: "scope_violation",
|
|
99
|
+
message: `Story "${story.id}" changed path outside allowed scope: ${file}`,
|
|
100
|
+
details: {
|
|
101
|
+
storyId: story.id,
|
|
102
|
+
violation: "forbidden_path",
|
|
103
|
+
path: file,
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
if (scope.maxChangedFiles !== undefined && changedFiles.length > scope.maxChangedFiles) {
|
|
110
|
+
violations.push({
|
|
111
|
+
category: "scope_violation",
|
|
112
|
+
message: `Story "${story.id}" changed ${changedFiles.length} files, max allowed: ${scope.maxChangedFiles}`,
|
|
113
|
+
details: {
|
|
114
|
+
storyId: story.id,
|
|
115
|
+
violation: "max_files_exceeded",
|
|
116
|
+
actualFiles: changedFiles.length,
|
|
117
|
+
maxFiles: scope.maxChangedFiles,
|
|
118
|
+
},
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (scope.maxChangedLines !== undefined && totalLines > scope.maxChangedLines) {
|
|
122
|
+
violations.push({
|
|
123
|
+
category: "scope_violation",
|
|
124
|
+
message: `Story "${story.id}" changed ${totalLines} lines, max allowed: ${scope.maxChangedLines}`,
|
|
125
|
+
details: {
|
|
126
|
+
storyId: story.id,
|
|
127
|
+
violation: "max_lines_exceeded",
|
|
128
|
+
actualLines: totalLines,
|
|
129
|
+
maxLines: scope.maxChangedLines,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return violations;
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=scope-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scope-check.js","sourceRoot":"","sources":["../../src/prd/scope-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface StoryScope {
|
|
2
|
+
allowedPaths?: string[];
|
|
3
|
+
forbiddenPaths?: string[];
|
|
4
|
+
maxChangedFiles?: number;
|
|
5
|
+
maxChangedLines?: number;
|
|
6
|
+
}
|
|
7
|
+
export type Story<TId extends string = string> = {
|
|
8
|
+
id: TId;
|
|
9
|
+
title: string;
|
|
10
|
+
gateFile: string;
|
|
11
|
+
dependsOn?: TId[];
|
|
12
|
+
scope?: StoryScope;
|
|
13
|
+
};
|
|
14
|
+
export type Prd<TId extends string = string> = {
|
|
15
|
+
stories: readonly Story<TId>[];
|
|
16
|
+
};
|
|
17
|
+
export type GateResult = {
|
|
18
|
+
status: "success" | "failed" | "timeout";
|
|
19
|
+
[key: string]: unknown;
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/prd/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,KAAK,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,IAAI;IAC/C,EAAE,EAAE,GAAG,CAAC;IACR,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,GAAG,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,GAAG,CAAC,GAAG,SAAS,MAAM,GAAG,MAAM,IAAI;IAC7C,OAAO,EAAE,SAAS,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzC,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/prd/types.ts"],"names":[],"mappings":""}
|
package/dist/report.d.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable, versioned report schemas for machine-readable gate outputs.
|
|
3
|
+
* All types are fully JSON-serializable (no Error objects, no functions).
|
|
4
|
+
*/
|
|
5
|
+
export interface SerializableError {
|
|
6
|
+
tag?: string;
|
|
7
|
+
name: string;
|
|
8
|
+
message: string;
|
|
9
|
+
stack?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface GateResultV1 {
|
|
12
|
+
version: "1";
|
|
13
|
+
status: "success" | "failed" | "timeout";
|
|
14
|
+
durationMs: number;
|
|
15
|
+
logs: unknown[];
|
|
16
|
+
evidence: {
|
|
17
|
+
requestIds: string[];
|
|
18
|
+
stagesSeen: string[];
|
|
19
|
+
actionsSeen: string[];
|
|
20
|
+
errorTags: string[];
|
|
21
|
+
};
|
|
22
|
+
error?: SerializableError;
|
|
23
|
+
}
|
|
24
|
+
export interface StoryResultV1 {
|
|
25
|
+
id: string;
|
|
26
|
+
title: string;
|
|
27
|
+
gateFile: string;
|
|
28
|
+
status: "success" | "failed" | "timeout";
|
|
29
|
+
durationMs: number;
|
|
30
|
+
error?: SerializableError;
|
|
31
|
+
}
|
|
32
|
+
export interface PrdReportV1 {
|
|
33
|
+
version: "1";
|
|
34
|
+
success: boolean;
|
|
35
|
+
stories: StoryResultV1[];
|
|
36
|
+
failedStory?: {
|
|
37
|
+
id: string;
|
|
38
|
+
title: string;
|
|
39
|
+
gateFile: string;
|
|
40
|
+
};
|
|
41
|
+
totalDurationMs: number;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Converts an Error (or Error-like object) into a SerializableError.
|
|
45
|
+
* Preserves Effect-tagged errors (_tag) when present.
|
|
46
|
+
*/
|
|
47
|
+
export declare function serializeError(error: unknown): SerializableError;
|
|
48
|
+
/**
|
|
49
|
+
* Sorts arrays deterministically (alphabetically) for stable output.
|
|
50
|
+
*/
|
|
51
|
+
export declare function sortDeterministic<T>(arr: T[]): T[];
|
|
52
|
+
/**
|
|
53
|
+
* Converts a GateResult to a fully serializable GateResultV1.
|
|
54
|
+
*/
|
|
55
|
+
export declare function toGateResultV1(result: {
|
|
56
|
+
status: "success" | "failed" | "timeout";
|
|
57
|
+
durationMs: number;
|
|
58
|
+
logs: unknown[];
|
|
59
|
+
evidence: {
|
|
60
|
+
requestIds: string[];
|
|
61
|
+
stagesSeen: string[];
|
|
62
|
+
actionsSeen: string[];
|
|
63
|
+
errorTags: string[];
|
|
64
|
+
};
|
|
65
|
+
error?: Error | unknown;
|
|
66
|
+
}): GateResultV1;
|
|
67
|
+
//# sourceMappingURL=report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,iBAAiB;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,GAAG,CAAC;IACb,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,SAAS,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,iBAAiB,CAAC;CAC3B;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,GAAG,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,WAAW,CAAC,EAAE;QACZ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,iBAAiB,CAwBhE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,EAAE,CAElD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE;IACrC,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,CAAC;IACzC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,OAAO,EAAE,CAAC;IAChB,QAAQ,EAAE;QACR,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,SAAS,EAAE,MAAM,EAAE,CAAC;KACrB,CAAC;IACF,KAAK,CAAC,EAAE,KAAK,GAAG,OAAO,CAAC;CACzB,GAAG,YAAY,CASf"}
|
package/dist/report.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stable, versioned report schemas for machine-readable gate outputs.
|
|
3
|
+
* All types are fully JSON-serializable (no Error objects, no functions).
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Converts an Error (or Error-like object) into a SerializableError.
|
|
7
|
+
* Preserves Effect-tagged errors (_tag) when present.
|
|
8
|
+
*/
|
|
9
|
+
export function serializeError(error) {
|
|
10
|
+
if (error instanceof Error) {
|
|
11
|
+
const errorWithTag = error;
|
|
12
|
+
return {
|
|
13
|
+
tag: errorWithTag._tag,
|
|
14
|
+
name: error.name || "Error",
|
|
15
|
+
message: error.message || String(error),
|
|
16
|
+
stack: error.stack,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
if (error && typeof error === "object" && "_tag" in error) {
|
|
20
|
+
const tagged = error;
|
|
21
|
+
return {
|
|
22
|
+
tag: tagged._tag,
|
|
23
|
+
name: tagged._tag,
|
|
24
|
+
message: tagged.message || String(error),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return {
|
|
28
|
+
name: "Error",
|
|
29
|
+
message: String(error),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Sorts arrays deterministically (alphabetically) for stable output.
|
|
34
|
+
*/
|
|
35
|
+
export function sortDeterministic(arr) {
|
|
36
|
+
return [...arr].sort();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Converts a GateResult to a fully serializable GateResultV1.
|
|
40
|
+
*/
|
|
41
|
+
export function toGateResultV1(result) {
|
|
42
|
+
return {
|
|
43
|
+
version: "1",
|
|
44
|
+
status: result.status,
|
|
45
|
+
durationMs: result.durationMs,
|
|
46
|
+
logs: result.logs,
|
|
47
|
+
evidence: result.evidence,
|
|
48
|
+
error: result.error ? serializeError(result.error) : undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAAA;;;GAGG;AA4CH;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc;IAC3C,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,MAAM,YAAY,GAAG,KAAkC,CAAC;QACxD,OAAO;YACL,GAAG,EAAE,YAAY,CAAC,IAAI;YACtB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,OAAO;YAC3B,OAAO,EAAE,KAAK,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC;YACvC,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,EAAE,CAAC;QAC1D,MAAM,MAAM,GAAG,KAAiE,CAAC;QACjF,OAAO;YACL,GAAG,EAAE,MAAM,CAAC,IAAI;YAChB,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC;SACzC,CAAC;IACJ,CAAC;IAED,OAAO;QACL,IAAI,EAAE,OAAO;QACb,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC;KACvB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAI,GAAQ;IAC3C,OAAO,CAAC,GAAG,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACzB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,MAW9B;IACC,OAAO;QACL,OAAO,EAAE,GAAG;QACZ,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;KAC/D,CAAC;AACJ,CAAC"}
|
package/dist/utils.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAQ,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAyB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAQ,KAAK,QAAQ,EAAE,KAAK,UAAU,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAyB,KAAK,OAAO,EAAE,MAAM,WAAW,CAAC;AAGhE;;;GAGG;AACH,wBAAgB,kBAAkB,IAAI,OAAO,CAU5C;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,wCAEzC;AAED;;;GAGG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,QAAQ,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,UAAU,CAAC,CAcrB"}
|
package/dist/utils.js
CHANGED
|
@@ -34,8 +34,9 @@ export async function runGateWithErrorHandling(gate, name) {
|
|
|
34
34
|
try {
|
|
35
35
|
return await Gate.run(gate);
|
|
36
36
|
}
|
|
37
|
-
catch (
|
|
38
|
-
|
|
37
|
+
catch (unknownError) {
|
|
38
|
+
const error = unknownError instanceof Error ? unknownError : new Error(String(unknownError));
|
|
39
|
+
console.error(` ❌ Error: ${error.message}`);
|
|
39
40
|
return {
|
|
40
41
|
status: "failed",
|
|
41
42
|
durationMs: 0,
|
package/dist/utils.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,IAAI,EAAkC,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAgB,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,IAAI,EAAkC,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,qBAAqB,EAAgB,MAAM,WAAW,CAAC;AAGhE;;;GAGG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO;QACL,KAAK,EAAE,GAAG,EAAE,CACV,MAAM,CAAC,OAAO,CAAqB;YACnC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,aAAa,CAAC;gBAC3B,OAAO;YACT,CAAC;SACF,CAAC;QACF,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI;KACxB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,0BAA0B;IACxC,OAAO,qBAAqB,CAAC,kBAAkB,EAAE,CAAC,CAAC;AACrD,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,IAAc,EACd,IAAY;IAEZ,IAAI,CAAC;QACH,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAAC,OAAO,YAAY,EAAE,CAAC;QACtB,MAAM,KAAK,GAAG,YAAY,YAAY,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC;QAC7F,OAAO,CAAC,KAAK,CAAC,eAAe,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9C,OAAO;YACL,MAAM,EAAE,QAAiB;YACzB,UAAU,EAAE,CAAC;YACb,IAAI,EAAE,EAAE;YACR,QAAQ,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE;YAC5E,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;SACjE,CAAC;IACJ,CAAC;AACH,CAAC"}
|
package/dist/validation.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAQjF;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAUzE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAQjF;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAUzE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAgBjF"}
|
package/dist/validation.js
CHANGED
|
@@ -25,9 +25,13 @@ export function validateCommand(command) {
|
|
|
25
25
|
if (!command || typeof command !== "string") {
|
|
26
26
|
return Effect.fail(new GateError({ cause: new Error("Command must be a non-empty string") }));
|
|
27
27
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
// Extremely strict shell safety: block common shell metacharacters and expansion patterns
|
|
29
|
+
const dangerous = /[;&|`$(){}[\]<>\\'"\n\r\t]/;
|
|
30
|
+
const envExpansion = /\$\w+|\$\{[^}]+\}/;
|
|
31
|
+
if (dangerous.test(command) || envExpansion.test(command)) {
|
|
32
|
+
return Effect.fail(new GateError({
|
|
33
|
+
cause: new Error("Command contains potentially dangerous shell characters")
|
|
34
|
+
}));
|
|
31
35
|
}
|
|
32
36
|
return Effect.succeed(command);
|
|
33
37
|
}
|
package/dist/validation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,wCAAwC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpG,CAAC;IACD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,yCAAyC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrG,CAAC;IACD,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,qCAAqC,CAAC,EAAE,CAAC,CAAC,CAAC;QACjG,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,oCAAoC,CAAC,EAAE,CAAC,CAAC,CAAC;IAChG,CAAC;IACD,MAAM,SAAS,GAAG,
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../src/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,IAAI,CAAC,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,wCAAwC,CAAC,EAAE,CAAC,CAAC,CAAC;IACpG,CAAC;IACD,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;QACjC,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,yCAAyC,CAAC,EAAE,CAAC,CAAC,CAAC;IACrG,CAAC;IACD,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC9B,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,IAAI,CAAC,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnD,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,qCAAqC,CAAC,EAAE,CAAC,CAAC,CAAC;QACjG,CAAC;QACD,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC,CAAC;IAChF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,IAAI,CAAC,OAAO,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC5C,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,EAAE,KAAK,EAAE,IAAI,KAAK,CAAC,oCAAoC,CAAC,EAAE,CAAC,CAAC,CAAC;IAChG,CAAC;IACD,0FAA0F;IAC1F,MAAM,SAAS,GACb,4BAA4B,CAAC;IAC/B,MAAM,YAAY,GAAG,mBAAmB,CAAC;IACzC,IAAI,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1D,OAAO,MAAM,CAAC,IAAI,CAChB,IAAI,SAAS,CAAC;YACZ,KAAK,EAAE,IAAI,KAAK,CAAC,yDAAyD,CAAC;SAC5E,CAAC,CACH,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC"}
|