@voltro/plan 0.85.0 → 0.86.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/CHANGELOG.md +666 -0
- package/THIRD-PARTY-NOTICES.md +1 -1
- package/dist/canonical.js +118 -0
- package/dist/closure.js +116 -0
- package/dist/diagnostics.js +8 -0
- package/dist/envelope.js +16 -0
- package/dist/evidence.js +76 -0
- package/dist/ids.js +25 -0
- package/dist/impact.js +65 -0
- package/dist/index.js +14 -1034
- package/dist/lint.js +273 -0
- package/dist/manifest.js +16 -0
- package/dist/next.js +64 -0
- package/dist/schema.js +216 -0
- package/dist/state.js +67 -0
- package/dist/status.js +35 -0
- package/package.json +1 -1
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Third-Party Notices — @voltro/plan
|
|
2
2
|
|
|
3
|
-
This package
|
|
3
|
+
This package uses the open-source software listed below. Each is the
|
|
4
4
|
property of its respective copyright holders and is used under the terms of
|
|
5
5
|
its license. This file is provided for attribution; it grants no rights in
|
|
6
6
|
@voltro/plan itself, which is proprietary (see LICENSE).
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
//#region src/canonical.ts
|
|
2
|
+
var e = (e) => JSON.stringify(t(e)), t = (e) => {
|
|
3
|
+
if (Array.isArray(e)) return e.map(t);
|
|
4
|
+
if (typeof e == "object" && e) {
|
|
5
|
+
let n = {};
|
|
6
|
+
for (let r of Object.keys(e).sort()) {
|
|
7
|
+
let i = e[r];
|
|
8
|
+
i !== void 0 && (n[r] = t(i));
|
|
9
|
+
}
|
|
10
|
+
return n;
|
|
11
|
+
}
|
|
12
|
+
return e;
|
|
13
|
+
}, n = new Uint32Array([
|
|
14
|
+
1116352408,
|
|
15
|
+
1899447441,
|
|
16
|
+
3049323471,
|
|
17
|
+
3921009573,
|
|
18
|
+
961987163,
|
|
19
|
+
1508970993,
|
|
20
|
+
2453635748,
|
|
21
|
+
2870763221,
|
|
22
|
+
3624381080,
|
|
23
|
+
310598401,
|
|
24
|
+
607225278,
|
|
25
|
+
1426881987,
|
|
26
|
+
1925078388,
|
|
27
|
+
2162078206,
|
|
28
|
+
2614888103,
|
|
29
|
+
3248222580,
|
|
30
|
+
3835390401,
|
|
31
|
+
4022224774,
|
|
32
|
+
264347078,
|
|
33
|
+
604807628,
|
|
34
|
+
770255983,
|
|
35
|
+
1249150122,
|
|
36
|
+
1555081692,
|
|
37
|
+
1996064986,
|
|
38
|
+
2554220882,
|
|
39
|
+
2821834349,
|
|
40
|
+
2952996808,
|
|
41
|
+
3210313671,
|
|
42
|
+
3336571891,
|
|
43
|
+
3584528711,
|
|
44
|
+
113926993,
|
|
45
|
+
338241895,
|
|
46
|
+
666307205,
|
|
47
|
+
773529912,
|
|
48
|
+
1294757372,
|
|
49
|
+
1396182291,
|
|
50
|
+
1695183700,
|
|
51
|
+
1986661051,
|
|
52
|
+
2177026350,
|
|
53
|
+
2456956037,
|
|
54
|
+
2730485921,
|
|
55
|
+
2820302411,
|
|
56
|
+
3259730800,
|
|
57
|
+
3345764771,
|
|
58
|
+
3516065817,
|
|
59
|
+
3600352804,
|
|
60
|
+
4094571909,
|
|
61
|
+
275423344,
|
|
62
|
+
430227734,
|
|
63
|
+
506948616,
|
|
64
|
+
659060556,
|
|
65
|
+
883997877,
|
|
66
|
+
958139571,
|
|
67
|
+
1322822218,
|
|
68
|
+
1537002063,
|
|
69
|
+
1747873779,
|
|
70
|
+
1955562222,
|
|
71
|
+
2024104815,
|
|
72
|
+
2227730452,
|
|
73
|
+
2361852424,
|
|
74
|
+
2428436474,
|
|
75
|
+
2756734187,
|
|
76
|
+
3204031479,
|
|
77
|
+
3329325298
|
|
78
|
+
]), r = (e) => {
|
|
79
|
+
let t = new TextEncoder().encode(e), r = t.length * 8, i = new Uint8Array(t.length + 9 + 63 >> 6 << 6);
|
|
80
|
+
i.set(t), i[t.length] = 128;
|
|
81
|
+
let a = new DataView(i.buffer);
|
|
82
|
+
a.setUint32(i.length - 8, Math.floor(r / 4294967296)), a.setUint32(i.length - 4, r >>> 0);
|
|
83
|
+
let o = new Uint32Array([
|
|
84
|
+
1779033703,
|
|
85
|
+
3144134277,
|
|
86
|
+
1013904242,
|
|
87
|
+
2773480762,
|
|
88
|
+
1359893119,
|
|
89
|
+
2600822924,
|
|
90
|
+
528734635,
|
|
91
|
+
1541459225
|
|
92
|
+
]), s = /* @__PURE__ */ new Uint32Array(64), c = (e, t) => e >>> t | e << 32 - t;
|
|
93
|
+
for (let e = 0; e < i.length; e += 64) {
|
|
94
|
+
for (let t = 0; t < 16; t++) s[t] = a.getUint32(e + t * 4);
|
|
95
|
+
for (let e = 16; e < 64; e++) {
|
|
96
|
+
let t = c(s[e - 15], 7) ^ c(s[e - 15], 18) ^ s[e - 15] >>> 3, n = c(s[e - 2], 17) ^ c(s[e - 2], 19) ^ s[e - 2] >>> 10;
|
|
97
|
+
s[e] = s[e - 16] + t + s[e - 7] + n >>> 0;
|
|
98
|
+
}
|
|
99
|
+
let [t, r, i, l, u, d, f, p] = [
|
|
100
|
+
o[0],
|
|
101
|
+
o[1],
|
|
102
|
+
o[2],
|
|
103
|
+
o[3],
|
|
104
|
+
o[4],
|
|
105
|
+
o[5],
|
|
106
|
+
o[6],
|
|
107
|
+
o[7]
|
|
108
|
+
];
|
|
109
|
+
for (let e = 0; e < 64; e++) {
|
|
110
|
+
let a = p + (c(u, 6) ^ c(u, 11) ^ c(u, 25)) + (u & d ^ ~u & f) + n[e] + s[e] >>> 0, o = (c(t, 2) ^ c(t, 13) ^ c(t, 22)) + (t & r ^ t & i ^ r & i) >>> 0;
|
|
111
|
+
p = f, f = d, d = u, u = l + a >>> 0, l = i, i = r, r = t, t = a + o >>> 0;
|
|
112
|
+
}
|
|
113
|
+
o[0] = o[0] + t >>> 0, o[1] = o[1] + r >>> 0, o[2] = o[2] + i >>> 0, o[3] = o[3] + l >>> 0, o[4] = o[4] + u >>> 0, o[5] = o[5] + d >>> 0, o[6] = o[6] + f >>> 0, o[7] = o[7] + p >>> 0;
|
|
114
|
+
}
|
|
115
|
+
return [...o].map((e) => e.toString(16).padStart(8, "0")).join("");
|
|
116
|
+
}, i = (t) => `sha256:${r(e(t))}`;
|
|
117
|
+
//#endregion
|
|
118
|
+
export { e as canonicalJson, i as fingerprint, r as sha256Hex };
|
package/dist/closure.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { fingerprint as e } from "./canonical.js";
|
|
2
|
+
import { Schema as t } from "effect";
|
|
3
|
+
//#region src/closure.ts
|
|
4
|
+
var n = "voltro-plan-closure/v1", r = t.String.pipe(t.check(t.isMinLength(1))), i = t.String.pipe(t.check(t.isPattern(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/))), a = t.String.pipe(t.check(t.isPattern(/^sha256:[a-f0-9]{64}$/))), o = (e) => {
|
|
5
|
+
let o = e.grammar, s = (e) => t.String.pipe(t.check(t.isPattern(e))), c = t.Struct({
|
|
6
|
+
id: s(o.closureRun),
|
|
7
|
+
pass: t.Literals(["contract", "adversarial"]),
|
|
8
|
+
reviewer: r,
|
|
9
|
+
reviewContext: r,
|
|
10
|
+
planRevision: a,
|
|
11
|
+
completedAt: i,
|
|
12
|
+
findingRefs: t.Array(s(o.finding)),
|
|
13
|
+
result: t.Literals(["passed", "findings"]),
|
|
14
|
+
toolVersion: r
|
|
15
|
+
}), l = t.Struct({
|
|
16
|
+
id: s(o.certificate),
|
|
17
|
+
plan: s(o.plan),
|
|
18
|
+
planRevision: a,
|
|
19
|
+
workItems: t.Array(s(o.workItem)),
|
|
20
|
+
evidenceRefs: t.Array(s(o.evidence)),
|
|
21
|
+
closureRuns: t.Array(s(o.closureRun)).pipe(t.check(t.isBetweenLength(2, 2))),
|
|
22
|
+
confirmedBy: r,
|
|
23
|
+
issuedAt: i,
|
|
24
|
+
toolVersion: r
|
|
25
|
+
});
|
|
26
|
+
return t.Struct({
|
|
27
|
+
schemaVersion: t.Literal(n),
|
|
28
|
+
plan: s(o.plan),
|
|
29
|
+
runs: t.Array(c),
|
|
30
|
+
certificates: t.Array(l)
|
|
31
|
+
});
|
|
32
|
+
}, s = (t, n, r, i = () => void 0, a = /* @__PURE__ */ new Map()) => {
|
|
33
|
+
let o = new Set(t.workItems.map((e) => e.id)), s = [...new Set(t.workItems.flatMap((e) => e.dependsOn).filter((e) => !o.has(e)))].sort();
|
|
34
|
+
return e({
|
|
35
|
+
instance: t,
|
|
36
|
+
dependencies: s.map((e) => {
|
|
37
|
+
let t = i(e);
|
|
38
|
+
return [e, t === "closed" ? "verified" : t ?? "missing"];
|
|
39
|
+
}),
|
|
40
|
+
decisions: [...new Set(t.workItems.flatMap((e) => e.links.adrs))].sort().map((e) => [e, a.get(e) ?? null]),
|
|
41
|
+
evidence: n.filter((e) => e.subjectRefs.some((e) => o.has(e))).map((e) => e.id).sort(),
|
|
42
|
+
findings: r.filter((e) => e.targetWorkItems.some((e) => o.has(e))).map((e) => [e.id, e.status]).sort()
|
|
43
|
+
});
|
|
44
|
+
}, c = (e, t) => {
|
|
45
|
+
if (e === void 0) return { state: "open" };
|
|
46
|
+
let n = new Map(e.runs.map((e) => [e.id, e])), r = [...e.certificates].reverse().find((e) => e.planRevision === t && e.closureRuns.every((e) => n.get(e)?.result === "passed" && n.get(e)?.planRevision === t));
|
|
47
|
+
if (r !== void 0) return {
|
|
48
|
+
state: "certified",
|
|
49
|
+
certificate: r
|
|
50
|
+
};
|
|
51
|
+
let i = [...e.runs].reverse().find((e) => e.pass === "contract" && e.result === "passed" && e.planRevision === t);
|
|
52
|
+
return i === void 0 ? { state: "open" } : {
|
|
53
|
+
state: "closure-1",
|
|
54
|
+
run: i
|
|
55
|
+
};
|
|
56
|
+
}, l = (e, t, n, r) => ({
|
|
57
|
+
outcome: "refused",
|
|
58
|
+
diagnostic: {
|
|
59
|
+
ruleId: e,
|
|
60
|
+
severity: "error",
|
|
61
|
+
message: t,
|
|
62
|
+
locator: { plan: r },
|
|
63
|
+
related: [],
|
|
64
|
+
nextAction: n
|
|
65
|
+
}
|
|
66
|
+
}), u = (e, t) => `${e}${String(Math.max(0, ...t.filter((t) => t.startsWith(e)).map((t) => Number(t.slice(e.length)))) + 1).padStart(3, "0")}`, d = (e, t, r, i, a, o, s, c) => {
|
|
67
|
+
let d = t.plan.id, f = t.workItems.filter((e) => e.mandatory && e.supersededBy === void 0), p = f.filter((e) => a(e.id) !== "verified" && a(e.id) !== "closed");
|
|
68
|
+
if (c.findings.length === 0 && p.length > 0) return l("closure/unverified-items", `${p.length} of ${f.length} mandatory items of ${d} are not verified (${p.slice(0, 3).map((e) => e.id).join(", ")}${p.length > 3 ? ", …" : ""})`, "Verify every mandatory item first: voltro plan status, voltro plan evidence add.", d);
|
|
69
|
+
let m = new Set(t.workItems.map((e) => e.id)), h = s.filter((e) => e.status === "open" && e.targetWorkItems.some((e) => m.has(e)));
|
|
70
|
+
for (let e of c.findings) if (!h.some((t) => t.id === e)) return l("closure/unknown-finding", `${e} is not an open finding against ${d}`, "Record the finding first, against the items it concerns.", d);
|
|
71
|
+
if (c.findings.length === 0 && h.length > 0) return l("closure/open-findings", `${h.length} finding(s) against ${d} are open (${h.map((e) => e.id).slice(0, 3).join(", ")})`, "Resolve them or accept the risk; then close again.", d);
|
|
72
|
+
let g = r?.runs ?? [];
|
|
73
|
+
if (c.pass === "adversarial" && c.findings.length === 0) {
|
|
74
|
+
let e = [...g].reverse().find((e) => e.pass === "contract" && e.result === "passed" && e.planRevision === i);
|
|
75
|
+
if (e === void 0) return l("closure/order", `${d} has no passed contract pass on its current revision`, "Run the contract pass first: voltro plan close <plan> --pass contract.", d);
|
|
76
|
+
if (e.reviewer === c.reviewer || e.reviewContext === c.reviewContext) return l("closure/same-context", `the adversarial pass needs another reviewer and another context than ${e.id} (${e.reviewer}, ${e.reviewContext})`, "Have someone else audit it, in their own context.", d);
|
|
77
|
+
}
|
|
78
|
+
let _ = t.plan.number, v = {
|
|
79
|
+
id: u(`CLR-${e.prefix}-${_}-`, g.map((e) => e.id)),
|
|
80
|
+
pass: c.pass,
|
|
81
|
+
reviewer: c.reviewer,
|
|
82
|
+
reviewContext: c.reviewContext,
|
|
83
|
+
planRevision: i,
|
|
84
|
+
completedAt: c.at,
|
|
85
|
+
findingRefs: [...c.findings].sort(),
|
|
86
|
+
result: c.findings.length === 0 ? "passed" : "findings",
|
|
87
|
+
toolVersion: c.toolVersion
|
|
88
|
+
}, y = null;
|
|
89
|
+
if (c.pass === "adversarial" && v.result === "passed") {
|
|
90
|
+
let t = [...g].reverse().find((e) => e.pass === "contract" && e.result === "passed" && e.planRevision === i);
|
|
91
|
+
y = {
|
|
92
|
+
id: u(`CRT-${e.prefix}-${_}-`, (r?.certificates ?? []).map((e) => e.id)),
|
|
93
|
+
plan: d,
|
|
94
|
+
planRevision: i,
|
|
95
|
+
workItems: f.map((e) => e.id).sort(),
|
|
96
|
+
evidenceRefs: o.filter((e) => e.subjectRefs.some((e) => m.has(e))).map((e) => e.id).sort(),
|
|
97
|
+
closureRuns: [t.id, v.id],
|
|
98
|
+
confirmedBy: c.reviewer,
|
|
99
|
+
issuedAt: c.at,
|
|
100
|
+
toolVersion: c.toolVersion
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
return {
|
|
104
|
+
outcome: "recorded",
|
|
105
|
+
file: {
|
|
106
|
+
schemaVersion: n,
|
|
107
|
+
plan: d,
|
|
108
|
+
runs: [...g, v],
|
|
109
|
+
certificates: [...r?.certificates ?? [], ...y === null ? [] : [y]]
|
|
110
|
+
},
|
|
111
|
+
run: v,
|
|
112
|
+
certificate: y
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
//#endregion
|
|
116
|
+
export { n as CLOSURE_SCHEMA_VERSION, o as closureFileSchema, c as closureState, d as decideClosure, s as planRevision };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/diagnostics.ts
|
|
2
|
+
var e = {
|
|
3
|
+
security: 0,
|
|
4
|
+
error: 1,
|
|
5
|
+
warning: 2
|
|
6
|
+
}, t = (e, t) => (e ?? "") < (t ?? "") ? -1 : +((e ?? "") > (t ?? "")), n = (n) => [...n].sort((n, r) => e[n.severity] - e[r.severity] || t(n.locator.plan, r.locator.plan) || t(n.locator.workItem, r.locator.workItem) || t(n.ruleId, r.ruleId) || t(n.locator.file, r.locator.file) || t(n.locator.path, r.locator.path) || t(n.message, r.message)), r = (e, t = !1) => e.some((e) => e.severity !== "warning" || t);
|
|
7
|
+
//#endregion
|
|
8
|
+
export { r as blocks, n as sortDiagnostics };
|
package/dist/envelope.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//#region src/envelope.ts
|
|
2
|
+
var e = "voltro-plan-command/v1", t = (t) => ({
|
|
3
|
+
schemaVersion: e,
|
|
4
|
+
...t
|
|
5
|
+
}), n = {
|
|
6
|
+
ok: 0,
|
|
7
|
+
failed: 1,
|
|
8
|
+
usage: 2,
|
|
9
|
+
input: 3,
|
|
10
|
+
conflict: 4,
|
|
11
|
+
provider: 5,
|
|
12
|
+
security: 6,
|
|
13
|
+
internal: 70
|
|
14
|
+
};
|
|
15
|
+
//#endregion
|
|
16
|
+
export { e as COMMAND_SCHEMA_VERSION, n as EXIT, t as envelope };
|
package/dist/evidence.js
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { canonicalJson as e } from "./canonical.js";
|
|
2
|
+
import { EVIDENCE_KINDS as t } from "./schema.js";
|
|
3
|
+
import { EXIT as n } from "./envelope.js";
|
|
4
|
+
//#region src/evidence.ts
|
|
5
|
+
var r = (e, t, n, r, i, a) => ({
|
|
6
|
+
outcome: "refused",
|
|
7
|
+
exit: a,
|
|
8
|
+
diagnostic: {
|
|
9
|
+
ruleId: e,
|
|
10
|
+
severity: t,
|
|
11
|
+
message: n,
|
|
12
|
+
locator: { workItem: i },
|
|
13
|
+
related: [],
|
|
14
|
+
nextAction: r
|
|
15
|
+
}
|
|
16
|
+
}), i = [
|
|
17
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----/,
|
|
18
|
+
/\b[sr]k_(live|test)_[A-Za-z0-9]{16,}/,
|
|
19
|
+
/\bgh[pousr]_[A-Za-z0-9]{30,}/,
|
|
20
|
+
/\bAKIA[0-9A-Z]{16}\b/,
|
|
21
|
+
/\bxox[abprs]-[A-Za-z0-9-]{10,}/,
|
|
22
|
+
/\bBearer\s+[A-Za-z0-9._~+/-]{20,}/
|
|
23
|
+
], a = (a, o, s) => {
|
|
24
|
+
let c = a.items.get(s.workItem)?.item;
|
|
25
|
+
if (c === void 0) return r("evidence/unknown-target", "error", `${s.workItem} is not a work item of this program`, "Name an existing work item.", s.workItem, n.failed);
|
|
26
|
+
if ([
|
|
27
|
+
s.title,
|
|
28
|
+
s.uri,
|
|
29
|
+
s.producedBy,
|
|
30
|
+
s.command ?? "",
|
|
31
|
+
s.environment ?? ""
|
|
32
|
+
].some((e) => i.some((t) => t.test(e))) || /^[a-z][a-z0-9+.-]*:\/\/[^/@\s]*:[^/@\s]*@/i.test(s.uri)) return r("security/secret", "security", "the evidence carries a secret or a credential", "Remove it; reference where the result is kept, never a key.", c.id, n.security);
|
|
33
|
+
if (!t.includes(s.kind)) return r("evidence/kind", "error", `"${s.kind}" is not an evidence kind`, `Use one of: ${t.join(", ")}.`, c.id, n.usage);
|
|
34
|
+
if (/^http:\/\//i.test(s.uri)) return r("evidence/uri", "error", `${s.uri} is not https`, "Reference evidence over https, git: or a project-relative path.", c.id, n.failed);
|
|
35
|
+
if (s.uri.startsWith("/") || /^[A-Za-z]:[\\/]/.test(s.uri) || s.uri.split("/").includes("..")) return r("evidence/uri", "error", `${s.uri} is not portable`, "Use a path relative to the project root, or a URL.", c.id, n.failed);
|
|
36
|
+
let l = [c.id];
|
|
37
|
+
for (let e of s.proves) {
|
|
38
|
+
let t = /^[AE][0-9]{2}$/.test(e) ? `${c.id}-${e}` : e, i = c.acceptance.find((e) => e.id === t), a = c.evidenceRequirements.find((e) => e.id === t);
|
|
39
|
+
if (i === void 0 && a === void 0) return r("evidence/unknown-target", "error", `${t} is not a check or requirement of ${c.id}`, `Name one of ${[...c.acceptance, ...c.evidenceRequirements].map((e) => e.id).join(", ")}.`, c.id, n.failed);
|
|
40
|
+
if (a !== void 0 && !a.kinds.includes(s.kind)) return r("evidence/wrong-kind", "error", `${a.id} asks for ${a.kinds.join("/")}, not ${s.kind}`, "Add evidence of a kind the requirement names.", c.id, n.failed);
|
|
41
|
+
l.push(t);
|
|
42
|
+
}
|
|
43
|
+
let u = {
|
|
44
|
+
kind: s.kind,
|
|
45
|
+
title: s.title,
|
|
46
|
+
uri: s.uri,
|
|
47
|
+
result: s.result,
|
|
48
|
+
subjectRefs: [...new Set(l)],
|
|
49
|
+
producedBy: s.producedBy,
|
|
50
|
+
...s.command === void 0 ? {} : { command: s.command },
|
|
51
|
+
...s.environment === void 0 ? {} : { environment: s.environment },
|
|
52
|
+
...s.fingerprint === void 0 ? {} : { fingerprint: s.fingerprint }
|
|
53
|
+
};
|
|
54
|
+
if (s.idempotencyKey !== void 0) {
|
|
55
|
+
let t = a.evidence.find((e) => e.idempotencyKey === s.idempotencyKey);
|
|
56
|
+
if (t !== void 0) {
|
|
57
|
+
let { id: i, observedAt: a, idempotencyKey: o, ...l } = t;
|
|
58
|
+
return e(l) === e(u) ? {
|
|
59
|
+
outcome: "same",
|
|
60
|
+
evidence: t
|
|
61
|
+
} : r("evidence/idempotency-conflict", "error", `the key "${s.idempotencyKey}" was used for other evidence (${t.id})`, "Use a new key for new evidence.", c.id, n.conflict);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
let d = `EVD-${c.id}-`, f = a.evidence.filter((e) => e.id.startsWith(d)).map((e) => Number(e.id.slice(d.length))), p = String(Math.max(0, ...f) + 1).padStart(3, "0");
|
|
65
|
+
return o.evidence.test(`${d}${p}`) ? {
|
|
66
|
+
outcome: "add",
|
|
67
|
+
evidence: {
|
|
68
|
+
id: `${d}${p}`,
|
|
69
|
+
...u,
|
|
70
|
+
observedAt: s.observedAt,
|
|
71
|
+
...s.idempotencyKey === void 0 ? {} : { idempotencyKey: s.idempotencyKey }
|
|
72
|
+
}
|
|
73
|
+
} : r("evidence/exhausted", "error", `${c.id} has no evidence number left`, "Supersede the item.", c.id, n.failed);
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
76
|
+
export { a as decideEvidence };
|
package/dist/ids.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
//#region src/ids.ts
|
|
2
|
+
var e = /^[A-Z][A-Z0-9]{1,9}$/, t = (t) => {
|
|
3
|
+
if (!e.test(t)) throw Error(`plan id prefix "${t}" is not 2 to 10 capital letters or digits, starting with a letter`);
|
|
4
|
+
return {
|
|
5
|
+
prefix: t,
|
|
6
|
+
plan: RegExp(`^${t}-PLAN-[0-9]{2}$`),
|
|
7
|
+
workItem: RegExp(`^${t}-[0-9]{2}-[0-9]{3}$`),
|
|
8
|
+
acceptance: RegExp(`^${t}-[0-9]{2}-[0-9]{3}-A[0-9]{2}$`),
|
|
9
|
+
requirement: RegExp(`^${t}-[0-9]{2}-[0-9]{3}-E[0-9]{2}$`),
|
|
10
|
+
source: RegExp(`^SRC-${t}-[0-9]{2}-[0-9]{3}-[0-9]{3}$`),
|
|
11
|
+
evidence: RegExp(`^EVD-${t}-[0-9]{2}-[0-9]{3}-[0-9]{3}$`),
|
|
12
|
+
finding: RegExp(`^FND-${t}-[0-9]{2}-[0-9]{3}-[0-9]{3}$`),
|
|
13
|
+
adr: RegExp(`^ADR-${t}-[0-9]{3}$`),
|
|
14
|
+
closureRun: RegExp(`^CLR-${t}-[0-9]{2}-[0-9]{3}$`),
|
|
15
|
+
certificate: RegExp(`^CRT-${t}-[0-9]{2}-[0-9]{3}$`)
|
|
16
|
+
};
|
|
17
|
+
}, n = (e) => {
|
|
18
|
+
let t = /-(?:PLAN-)?([0-9]{2})(?:-|$)/.exec(e);
|
|
19
|
+
return t === null ? null : t[1];
|
|
20
|
+
}, r = (e, t) => {
|
|
21
|
+
let n = RegExp(`(?:^|-)(${e.prefix}-[0-9]{2}-[0-9]{3})(?:-|$)`).exec(t);
|
|
22
|
+
return n === null ? null : n[1];
|
|
23
|
+
};
|
|
24
|
+
//#endregion
|
|
25
|
+
export { t as idGrammar, r as ownerItemOf, n as planNumberOf };
|
package/dist/impact.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
//#region src/impact.ts
|
|
2
|
+
var e = [
|
|
3
|
+
".",
|
|
4
|
+
":",
|
|
5
|
+
"/",
|
|
6
|
+
"-"
|
|
7
|
+
], t = (t, n) => t === n || e.some((e) => n.startsWith(t + e) || t.startsWith(n + e)), n = (e, n) => {
|
|
8
|
+
let r = /* @__PURE__ */ new Map(), i = (e, t) => {
|
|
9
|
+
let n = r.get(e) ?? /* @__PURE__ */ new Set();
|
|
10
|
+
n.add(t), r.set(e, n);
|
|
11
|
+
};
|
|
12
|
+
for (let { item: r } of e.items.values()) for (let e of n.tags) for (let n of r.impactTags) t(e, n) && i(r.id, `tag ${n}`);
|
|
13
|
+
let a = [], o = new Map(e.instances.map(({ file: e, instance: t }) => [e, t])), s = /* @__PURE__ */ new Map();
|
|
14
|
+
for (let { item: t } of e.items.values()) for (let e of t.sourceRefs) {
|
|
15
|
+
let n = s.get(e.uri);
|
|
16
|
+
n === void 0 && (n = /* @__PURE__ */ new Set(), s.set(e.uri, n)), n.add(t.id);
|
|
17
|
+
}
|
|
18
|
+
for (let e of [...new Set(n.files)].sort()) {
|
|
19
|
+
let t = !1, n = o.get(e);
|
|
20
|
+
if (n !== void 0) {
|
|
21
|
+
t = !0;
|
|
22
|
+
for (let t of n.workItems) i(t.id, `its plan file ${e} changed`);
|
|
23
|
+
}
|
|
24
|
+
for (let n of s.get(e) ?? []) t = !0, i(n, `its source ${e} changed`);
|
|
25
|
+
t || a.push(e);
|
|
26
|
+
}
|
|
27
|
+
let c = /* @__PURE__ */ new Map();
|
|
28
|
+
for (let { item: t } of e.items.values()) for (let e of t.dependsOn) {
|
|
29
|
+
let n = c.get(e);
|
|
30
|
+
n === void 0 && (n = [], c.set(e, n)), n.push(t.id);
|
|
31
|
+
}
|
|
32
|
+
for (let e of c.values()) e.sort();
|
|
33
|
+
let l = /* @__PURE__ */ new Map(), u = [...r.keys()].sort().map((e) => [e]), d = new Set(r.keys());
|
|
34
|
+
for (let e = 0; e < u.length; e++) {
|
|
35
|
+
let t = u[e];
|
|
36
|
+
for (let e of c.get(t.at(-1)) ?? []) {
|
|
37
|
+
if (d.has(e)) continue;
|
|
38
|
+
d.add(e);
|
|
39
|
+
let n = [...t, e];
|
|
40
|
+
l.set(e, n), u.push(n);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
let f = [...d], p = [...new Set(f.map((t) => e.items.get(t).item.ownerPlan))].sort(), m = p.flatMap((t) => {
|
|
44
|
+
let n = e.closure.get(t);
|
|
45
|
+
return n?.state === "certified" ? [{
|
|
46
|
+
plan: t,
|
|
47
|
+
certificate: n.certificate.id
|
|
48
|
+
}] : [];
|
|
49
|
+
});
|
|
50
|
+
return {
|
|
51
|
+
direct: [...r.entries()].map(([e, t]) => ({
|
|
52
|
+
workItem: e,
|
|
53
|
+
because: [...t].sort()
|
|
54
|
+
})).sort((e, t) => e.workItem < t.workItem ? -1 : 1),
|
|
55
|
+
transitive: [...l.entries()].map(([e, t]) => ({
|
|
56
|
+
workItem: e,
|
|
57
|
+
path: t
|
|
58
|
+
})).sort((e, t) => e.workItem < t.workItem ? -1 : 1),
|
|
59
|
+
plans: p,
|
|
60
|
+
certificates: m,
|
|
61
|
+
unknown: a
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
//#endregion
|
|
65
|
+
export { n as impactOf };
|