@jevitate/cli 0.22.0 → 0.24.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/contracts/jevitate-catalog/v1/README.md +10 -0
- package/contracts/jevitate-catalog/v1/approval-hash-vectors.json +64 -0
- package/contracts/jevitate-catalog/v1/fixtures/invalid/jobs-unknown-field.json +12 -0
- package/contracts/jevitate-catalog/v1/fixtures/invalid/journey-unknown-field.json +15 -0
- package/contracts/jevitate-catalog/v1/fixtures/valid/jobs.json +11 -0
- package/contracts/jevitate-catalog/v1/fixtures/valid/journey.json +14 -0
- package/contracts/jevitate-catalog/v1/fixtures/valid/personas.json +7 -0
- package/contracts/jevitate-catalog/v1/manifest.json +69 -0
- package/contracts/jevitate-catalog/v1/schemas/job.schema.json +605 -0
- package/contracts/jevitate-catalog/v1/schemas/jobs.schema.json +28 -0
- package/contracts/jevitate-catalog/v1/schemas/journey.schema.json +10871 -0
- package/contracts/jevitate-catalog/v1/schemas/personas.schema.json +223 -0
- package/contracts/jevitate-catalog/v2/README.md +18 -0
- package/contracts/jevitate-catalog/v2/approval-hash-vectors.json +75 -0
- package/contracts/jevitate-catalog/v2/fixtures/invalid/jobs-unknown-field.json +12 -0
- package/contracts/jevitate-catalog/v2/fixtures/invalid/journey-unknown-field.json +15 -0
- package/contracts/jevitate-catalog/v2/fixtures/valid/jobs.json +11 -0
- package/contracts/jevitate-catalog/v2/fixtures/valid/journey.json +14 -0
- package/contracts/jevitate-catalog/v2/fixtures/valid/personas.json +7 -0
- package/contracts/jevitate-catalog/v2/manifest.json +78 -0
- package/contracts/jevitate-catalog/v2/schemas/job.schema.json +605 -0
- package/contracts/jevitate-catalog/v2/schemas/jobs.schema.json +28 -0
- package/contracts/jevitate-catalog/v2/schemas/journey.schema.json +10871 -0
- package/contracts/jevitate-catalog/v2/schemas/personas.schema.json +223 -0
- package/dist/bin.js +392 -147
- package/dist/build-info.generated.js +3 -3
- package/dist/catalog-bundle.d.ts +4 -2
- package/dist/catalog-bundle.js +8 -6
- package/dist/catalog-bundle.js.map +1 -1
- package/dist/catalog-contract-identity.d.ts +5 -0
- package/dist/catalog-contract-identity.js +7 -0
- package/dist/catalog-contract-identity.js.map +1 -0
- package/dist/catalog.js +18 -7
- package/dist/catalog.js.map +1 -1
- package/dist/journey-review.d.ts +3 -0
- package/dist/journey-review.js +13 -2
- package/dist/journey-review.js.map +1 -1
- package/dist/journey-stale-api.js +4 -4
- package/dist/journey-stale-api.js.map +1 -1
- package/dist/log-correlation.js +10 -2
- package/dist/log-correlation.js.map +1 -1
- package/package.json +9 -6
package/dist/bin.js
CHANGED
|
@@ -10,16 +10,36 @@ import { mkdir, stat } from "node:fs/promises";
|
|
|
10
10
|
|
|
11
11
|
// ../domain/dist/content-hash.js
|
|
12
12
|
import { createHash } from "node:crypto";
|
|
13
|
-
function
|
|
13
|
+
function compareUtf16CodeUnits(a, b2) {
|
|
14
|
+
const length = Math.min(a.length, b2.length);
|
|
15
|
+
for (let index = 0; index < length; index += 1) {
|
|
16
|
+
const difference = a.charCodeAt(index) - b2.charCodeAt(index);
|
|
17
|
+
if (difference !== 0)
|
|
18
|
+
return difference < 0 ? -1 : 1;
|
|
19
|
+
}
|
|
20
|
+
return a.length === b2.length ? 0 : a.length < b2.length ? -1 : 1;
|
|
21
|
+
}
|
|
22
|
+
function canonicalJson(value) {
|
|
14
23
|
if (value === null || typeof value !== "object")
|
|
15
24
|
return JSON.stringify(value);
|
|
16
25
|
if (Array.isArray(value))
|
|
17
|
-
return `[${value.map(
|
|
26
|
+
return `[${value.map(canonicalJson).join(",")}]`;
|
|
27
|
+
const entries = Object.entries(value).sort(([a], [b2]) => compareUtf16CodeUnits(a, b2));
|
|
28
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(",")}}`;
|
|
29
|
+
}
|
|
30
|
+
function legacyCanonicalJson(value) {
|
|
31
|
+
if (value === null || typeof value !== "object")
|
|
32
|
+
return JSON.stringify(value);
|
|
33
|
+
if (Array.isArray(value))
|
|
34
|
+
return `[${value.map(legacyCanonicalJson).join(",")}]`;
|
|
18
35
|
const entries = Object.entries(value).sort(([a], [b2]) => a.localeCompare(b2));
|
|
19
|
-
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${
|
|
36
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${legacyCanonicalJson(v)}`).join(",")}}`;
|
|
20
37
|
}
|
|
21
38
|
function contentHash(value) {
|
|
22
|
-
return createHash("sha256").update(
|
|
39
|
+
return createHash("sha256").update(canonicalJson(value)).digest("hex");
|
|
40
|
+
}
|
|
41
|
+
function legacyContentHash(value) {
|
|
42
|
+
return createHash("sha256").update(legacyCanonicalJson(value)).digest("hex");
|
|
23
43
|
}
|
|
24
44
|
function sha256(text2) {
|
|
25
45
|
return createHash("sha256").update(text2, "utf8").digest("hex");
|
|
@@ -33945,18 +33965,18 @@ function stepIdsOf(rec) {
|
|
|
33945
33965
|
ids.add(s3.stepId);
|
|
33946
33966
|
return ids;
|
|
33947
33967
|
}
|
|
33948
|
-
function
|
|
33968
|
+
function canonicalJson2(v) {
|
|
33949
33969
|
if (Array.isArray(v))
|
|
33950
|
-
return `[${v.map(
|
|
33970
|
+
return `[${v.map(canonicalJson2).join(",")}]`;
|
|
33951
33971
|
if (v !== null && typeof v === "object") {
|
|
33952
33972
|
const o = v;
|
|
33953
|
-
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${
|
|
33973
|
+
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${canonicalJson2(o[k])}`).join(",")}}`;
|
|
33954
33974
|
}
|
|
33955
33975
|
return JSON.stringify(v) ?? "null";
|
|
33956
33976
|
}
|
|
33957
33977
|
function seededRandom(rec) {
|
|
33958
33978
|
let h = 2166136261;
|
|
33959
|
-
const text2 =
|
|
33979
|
+
const text2 = canonicalJson2(rec);
|
|
33960
33980
|
for (let i = 0; i < text2.length; i++) {
|
|
33961
33981
|
h ^= text2.charCodeAt(i);
|
|
33962
33982
|
h = Math.imul(h, 16777619) >>> 0;
|
|
@@ -35260,9 +35280,9 @@ async function confineFixture(raw, root2) {
|
|
|
35260
35280
|
}
|
|
35261
35281
|
if (!isFile)
|
|
35262
35282
|
throw fixtureNotFound(candidate, root2);
|
|
35263
|
-
const
|
|
35264
|
-
if (
|
|
35265
|
-
throw fixtureSensitive(candidate, root2,
|
|
35283
|
+
const sensitive2 = fixtureSensitiveReason(real, realRoot) ?? (isInsideRoot(candidate, resolve3(root2)) ? fixtureSensitiveReason(candidate, resolve3(root2)) : null);
|
|
35284
|
+
if (sensitive2 !== null)
|
|
35285
|
+
throw fixtureSensitive(candidate, root2, sensitive2);
|
|
35266
35286
|
return real;
|
|
35267
35287
|
}
|
|
35268
35288
|
function fixtureEscapesRoot(raw, root2) {
|
|
@@ -39592,6 +39612,59 @@ async function clockBounded(work, ms, onTimeout) {
|
|
|
39592
39612
|
}
|
|
39593
39613
|
}
|
|
39594
39614
|
|
|
39615
|
+
// ../explore/dist/request-body-values.js
|
|
39616
|
+
var MIN_VALUE_CHARS = 3;
|
|
39617
|
+
var MAX_VALUES = 50;
|
|
39618
|
+
var MAX_BODY_CHARS = 64e3;
|
|
39619
|
+
var SENSITIVE_WORDS = /* @__PURE__ */ new Set(["pass", "password", "passwd", "pwd", "secret", "token", "auth", "authorization", "apikey", "key", "otp", "pin", "cvv", "cvc", "card", "ssn", "session", "cookie", "signature", "credential", "credentials"]);
|
|
39620
|
+
function sensitive(key2) {
|
|
39621
|
+
return key2.split(/[^A-Za-z0-9]+|(?<=[a-z0-9])(?=[A-Z])/).some((w) => SENSITIVE_WORDS.has(w.toLowerCase()));
|
|
39622
|
+
}
|
|
39623
|
+
function keep(out, key2, value) {
|
|
39624
|
+
if (out.length >= MAX_VALUES)
|
|
39625
|
+
return;
|
|
39626
|
+
if (key2 !== null && sensitive(key2))
|
|
39627
|
+
return;
|
|
39628
|
+
const v = value.replace(/\s+/g, " ").trim();
|
|
39629
|
+
if (v.length >= MIN_VALUE_CHARS)
|
|
39630
|
+
out.push(v);
|
|
39631
|
+
}
|
|
39632
|
+
function walk(out, key2, node2) {
|
|
39633
|
+
if (out.length >= MAX_VALUES)
|
|
39634
|
+
return;
|
|
39635
|
+
if (typeof node2 === "string")
|
|
39636
|
+
keep(out, key2, node2);
|
|
39637
|
+
else if (Array.isArray(node2))
|
|
39638
|
+
for (const item of node2)
|
|
39639
|
+
walk(out, key2, item);
|
|
39640
|
+
else if (node2 !== null && typeof node2 === "object") {
|
|
39641
|
+
for (const [k, v] of Object.entries(node2)) {
|
|
39642
|
+
if (sensitive(k))
|
|
39643
|
+
continue;
|
|
39644
|
+
walk(out, k, v);
|
|
39645
|
+
}
|
|
39646
|
+
}
|
|
39647
|
+
}
|
|
39648
|
+
function bodyValues(body, contentType) {
|
|
39649
|
+
if (body === null || body === "" || body.length > MAX_BODY_CHARS)
|
|
39650
|
+
return [];
|
|
39651
|
+
const type = (contentType ?? "").toLowerCase();
|
|
39652
|
+
const out = [];
|
|
39653
|
+
if (type.includes("application/x-www-form-urlencoded")) {
|
|
39654
|
+
for (const [k, v] of new URLSearchParams(body))
|
|
39655
|
+
keep(out, k, v);
|
|
39656
|
+
return out;
|
|
39657
|
+
}
|
|
39658
|
+
if (type === "" || type.includes("json")) {
|
|
39659
|
+
try {
|
|
39660
|
+
walk(out, null, JSON.parse(body));
|
|
39661
|
+
} catch {
|
|
39662
|
+
return [];
|
|
39663
|
+
}
|
|
39664
|
+
}
|
|
39665
|
+
return out;
|
|
39666
|
+
}
|
|
39667
|
+
|
|
39595
39668
|
// ../explore/dist/page-monitor.js
|
|
39596
39669
|
var RPC_TRAILER_READ_MS = 2e3;
|
|
39597
39670
|
var INTERACTIVE_SELECTOR2 = "a[href],button,input:not([type=hidden]),select,textarea,[role=button],[role=link],[role=textbox],[role=checkbox],[role=combobox],[contenteditable=true]";
|
|
@@ -39797,6 +39870,8 @@ function pathOf3(url2) {
|
|
|
39797
39870
|
return url2.split(/[?#]/)[0] ?? url2;
|
|
39798
39871
|
}
|
|
39799
39872
|
}
|
|
39873
|
+
var BODY_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH"]);
|
|
39874
|
+
var MAX_WRITE_BODIES = 200;
|
|
39800
39875
|
function requestContentTypeOf(r) {
|
|
39801
39876
|
try {
|
|
39802
39877
|
const v = r.headers()["content-type"];
|
|
@@ -39809,6 +39884,8 @@ var RECENT_REQUESTS_MS = 6e4;
|
|
|
39809
39884
|
var PageMonitor = class {
|
|
39810
39885
|
#page;
|
|
39811
39886
|
#inflight = /* @__PURE__ */ new Map();
|
|
39887
|
+
/** #649: the string values recent writes sent in their bodies (memory only; never persisted). */
|
|
39888
|
+
#writeBodies = [];
|
|
39812
39889
|
#completed = [];
|
|
39813
39890
|
#wakers = /* @__PURE__ */ new Set();
|
|
39814
39891
|
#statuses = /* @__PURE__ */ new WeakMap();
|
|
@@ -39846,6 +39923,18 @@ var PageMonitor = class {
|
|
|
39846
39923
|
startedAt,
|
|
39847
39924
|
...requestContentType2 === void 0 ? {} : { requestContentType: requestContentType2 }
|
|
39848
39925
|
});
|
|
39926
|
+
if (BODY_METHODS.has(r.method().toUpperCase())) {
|
|
39927
|
+
let values2 = [];
|
|
39928
|
+
try {
|
|
39929
|
+
values2 = bodyValues(r.postData(), requestContentType2);
|
|
39930
|
+
} catch {
|
|
39931
|
+
}
|
|
39932
|
+
if (values2.length > 0) {
|
|
39933
|
+
this.#writeBodies.push({ startedAt, url: r.url(), values: values2 });
|
|
39934
|
+
if (this.#writeBodies.length > MAX_WRITE_BODIES)
|
|
39935
|
+
this.#writeBodies.shift();
|
|
39936
|
+
}
|
|
39937
|
+
}
|
|
39849
39938
|
for (const c of this.#captures) {
|
|
39850
39939
|
c.began(r, {
|
|
39851
39940
|
method: r.method().toUpperCase(),
|
|
@@ -40097,6 +40186,10 @@ var PageMonitor = class {
|
|
|
40097
40186
|
clock.clearTimeout(timer);
|
|
40098
40187
|
}
|
|
40099
40188
|
}
|
|
40189
|
+
/** #649: the string values the writes started since `sinceMs` sent, from URLs `keep` accepts. */
|
|
40190
|
+
writeBodyValuesSince(sinceMs, keep2) {
|
|
40191
|
+
return this.#writeBodies.filter((w) => w.startedAt >= sinceMs && keep2(w.url)).flatMap((w) => w.values);
|
|
40192
|
+
}
|
|
40100
40193
|
/** Requests that ended since `sinceMs` (wall clock). */
|
|
40101
40194
|
completedSince(sinceMs) {
|
|
40102
40195
|
return this.#completed.filter((r) => r.endedAt >= sinceMs);
|
|
@@ -41129,8 +41222,8 @@ function boundCandidates(candidates, opts) {
|
|
|
41129
41222
|
return s3;
|
|
41130
41223
|
};
|
|
41131
41224
|
const ranked = candidates.map((c, i) => ({ c, i, s: score2(c, i) })).sort((a, b2) => b2.s - a.s || a.i - b2.i).slice(0, Math.max(0, opts.limit));
|
|
41132
|
-
const
|
|
41133
|
-
return { kept: candidates.filter((_, i) =>
|
|
41225
|
+
const keep2 = new Set(ranked.map((r) => r.i));
|
|
41226
|
+
return { kept: candidates.filter((_, i) => keep2.has(i)), omitted: candidates.length - keep2.size };
|
|
41134
41227
|
}
|
|
41135
41228
|
|
|
41136
41229
|
// ../explore/dist/stuck-actions.js
|
|
@@ -41415,7 +41508,8 @@ var SideEffectGuard = class {
|
|
|
41415
41508
|
const own2 = requests.filter((r) => this.#ours(r));
|
|
41416
41509
|
const settled = !inflightAny && own2.every((r) => r.status !== null && r.status >= 200 && r.status < 300);
|
|
41417
41510
|
const completedOk = own2.length > 0 && settled;
|
|
41418
|
-
|
|
41511
|
+
const sentValues = this.#monitor.writeBodyValuesSince(o.at, (url2) => this.#ours({ url: url2 }));
|
|
41512
|
+
this.#lastClick = { requestSent: requests.length > 0 || inflightAny, writes: [...done, ...pending], completedOk, settled, sentValues };
|
|
41419
41513
|
if (done.length + pending.length === 0)
|
|
41420
41514
|
return;
|
|
41421
41515
|
this.#fired.set(o.key, {
|
|
@@ -41921,6 +42015,7 @@ var UnsubmittedTypeTracker = class {
|
|
|
41921
42015
|
var GOAL_MET_THRESHOLD = 0.75;
|
|
41922
42016
|
var GOAL_CHECK_TRIGGER = 0.5;
|
|
41923
42017
|
var WRITES_DONE_NOTE = "the job's writes succeeded; the page shows no change (repeat data?)";
|
|
42018
|
+
var READ_BACK_DONE_NOTE = "the job's writes succeeded and a page loaded from the server shows what they sent";
|
|
41924
42019
|
function groundDone(e) {
|
|
41925
42020
|
if (e.unsubmitted.length > 0) {
|
|
41926
42021
|
return { accept: false, reason: `typed text in ${e.unsubmitted.map((l2) => `"${l2}"`).join(", ")} was never submitted` };
|
|
@@ -41946,6 +42041,9 @@ function groundDone(e) {
|
|
|
41946
42041
|
if (w !== void 0 && writesAllSucceeded(w) && w.goalIsWrites !== null && w.goalIsWrites >= GOAL_MET_THRESHOLD) {
|
|
41947
42042
|
return { accept: true, outcome: { status: "completed", verifiedBy: "succeeded-writes", note: WRITES_DONE_NOTE } };
|
|
41948
42043
|
}
|
|
42044
|
+
if (w !== void 0 && writesAllSucceeded(w) && e.readBack === true && p >= GOAL_CHECK_TRIGGER) {
|
|
42045
|
+
return { accept: true, outcome: { status: "completed", verifiedBy: "succeeded-writes", note: READ_BACK_DONE_NOTE } };
|
|
42046
|
+
}
|
|
41949
42047
|
const notMet = `the goal is not observably achieved on this page (p=${p.toFixed(2)})`;
|
|
41950
42048
|
if (w !== void 0 && writesAllSucceeded(w) && w.goalIsWrites !== null) {
|
|
41951
42049
|
return { accept: false, reason: `${notMet}; the job's writes succeeded, but they are not judged to be the whole goal (p=${w.goalIsWrites.toFixed(2)})` };
|
|
@@ -42094,6 +42192,7 @@ async function judgeGoalCompletion(judge, input2) {
|
|
|
42094
42192
|
...input2.signInFacts === void 0 ? [] : [`SIGN-IN (observed by code, trusted): ${input2.signInFacts}`],
|
|
42095
42193
|
...input2.saveFacts === void 0 ? [] : [`SAVE (observed by code, trusted): ${input2.saveFacts}`],
|
|
42096
42194
|
...input2.writeFacts === void 0 ? [] : [`WRITES (observed by code, trusted): ${input2.writeFacts}`],
|
|
42195
|
+
...input2.readBackFacts === void 0 ? [] : [`READ-BACK (observed by code, trusted): ${input2.readBackFacts}`],
|
|
42097
42196
|
...input2.pageStatus === void 0 || input2.pageStatus === "" ? [] : [`PAGE STATUS (untrusted): ${input2.pageStatus}`],
|
|
42098
42197
|
`VISIBLE PAGE TEXT (untrusted): ${input2.pageText.replace(/\s+/g, " ").slice(0, GOAL_TEXT_CHARS)}`,
|
|
42099
42198
|
...input2.fieldValues === void 0 || input2.fieldValues.length === 0 ? [] : [
|
|
@@ -45529,6 +45628,7 @@ var OBSERVED_PAGE_CHARS = 2e4;
|
|
|
45529
45628
|
var MIN_QUOTE_CHARS = 3;
|
|
45530
45629
|
var MAX_OBSERVED_FIELDS = 40;
|
|
45531
45630
|
var OBSERVED_FIELD_CHARS = 500;
|
|
45631
|
+
var MAX_READ_BACK_VALUES = 50;
|
|
45532
45632
|
var MAX_OBSERVED_CONTROLS = 120;
|
|
45533
45633
|
var HEADING_CHARS = 200;
|
|
45534
45634
|
function controlFields(controls) {
|
|
@@ -45583,6 +45683,56 @@ var ObservedPages = class {
|
|
|
45583
45683
|
if (document2 !== void 0)
|
|
45584
45684
|
this.#writeDocument = document2;
|
|
45585
45685
|
}
|
|
45686
|
+
/**
|
|
45687
|
+
* #649: a sent value the run did not type (a write's request body: a prefilled import's rows) that
|
|
45688
|
+
* a page on the write's own route already showed before the write — its source, not a copy the app
|
|
45689
|
+
* kept. Only a page on another route reads it back: reloading the source shows it either way.
|
|
45690
|
+
*/
|
|
45691
|
+
#sourceRoute = /* @__PURE__ */ new Map();
|
|
45692
|
+
/** #649: a write's body values the run did not type, decided on the next page seen (see `add`). */
|
|
45693
|
+
#pendingSent = null;
|
|
45694
|
+
/**
|
|
45695
|
+
* #649: a successful write the run fired sent `values` in its body, from a page on `url`. One the run
|
|
45696
|
+
* did not type awaits read-back like a typed one only when the next page shows it as NEW text — text
|
|
45697
|
+
* no earlier page on that route showed (an import's rows listed from its CSV; never a form's
|
|
45698
|
+
* untouched prefilled fields, still only in their fields). Decided when that page is added.
|
|
45699
|
+
*/
|
|
45700
|
+
noteSentValues(values2, url2) {
|
|
45701
|
+
const own2 = /* @__PURE__ */ new Set([...this.#ownInputs, ...this.#unread]);
|
|
45702
|
+
const fresh = values2.map((value) => fold(redactContext(value, this.secrets).slice(0, OBSERVED_FIELD_CHARS))).filter((v) => v !== "" && !own2.has(v));
|
|
45703
|
+
this.#pendingSent = fresh.length === 0 ? null : { route: routeOf(url2), values: fresh };
|
|
45704
|
+
}
|
|
45705
|
+
/** #649: the pending body values `text` (the page after the write) newly shows await read-back. */
|
|
45706
|
+
#settleSent(text2) {
|
|
45707
|
+
const pending = this.#pendingSent;
|
|
45708
|
+
if (pending === null)
|
|
45709
|
+
return;
|
|
45710
|
+
this.#pendingSent = null;
|
|
45711
|
+
const now2 = fold(text2);
|
|
45712
|
+
const before = this.#pages.filter((p) => routeOf(p.url) === pending.route);
|
|
45713
|
+
for (const v of pending.values) {
|
|
45714
|
+
if (!now2.includes(v) || before.some((p) => fold(p.text).includes(v)))
|
|
45715
|
+
continue;
|
|
45716
|
+
this.#unread.add(v);
|
|
45717
|
+
if (before.some((p) => (p.fields ?? []).some((f2) => fold(f2.value).includes(v))))
|
|
45718
|
+
this.#sourceRoute.set(v, pending.route);
|
|
45719
|
+
}
|
|
45720
|
+
}
|
|
45721
|
+
/**
|
|
45722
|
+
* #671: values the run typed into a field on one document load. A field holding one shows the run's
|
|
45723
|
+
* own entry, never what the app kept: retyping a sent message into its composer after a reload is
|
|
45724
|
+
* not a read-back.
|
|
45725
|
+
*/
|
|
45726
|
+
#typedOn = null;
|
|
45727
|
+
/** #671: the run typed `value` into a field (a message composer too) on document load `document`. */
|
|
45728
|
+
noteTypedOn(value, document2) {
|
|
45729
|
+
const v = fold(redactContext(value, this.secrets).slice(0, OBSERVED_FIELD_CHARS));
|
|
45730
|
+
if (document2 === void 0 || v === "")
|
|
45731
|
+
return;
|
|
45732
|
+
if (this.#typedOn?.document !== document2)
|
|
45733
|
+
this.#typedOn = { document: document2, values: /* @__PURE__ */ new Set() };
|
|
45734
|
+
this.#typedOn.values.add(v);
|
|
45735
|
+
}
|
|
45586
45736
|
/** #647: the run's sent values (folded) that no page loaded from the server since has shown. */
|
|
45587
45737
|
unreadWrites() {
|
|
45588
45738
|
return this.#unread;
|
|
@@ -45596,15 +45746,37 @@ var ObservedPages = class {
|
|
|
45596
45746
|
* #647: a page from a later document load than the write shows `shown` (its visible text, or field
|
|
45597
45747
|
* values): the sent values it contains are read back from the app. Returns those values.
|
|
45598
45748
|
*/
|
|
45599
|
-
readBack(document2, shown2) {
|
|
45749
|
+
readBack(document2, shown2, url2) {
|
|
45600
45750
|
if (this.#unread.size === 0 || document2 === void 0 || document2 === this.#writeDocument)
|
|
45601
45751
|
return [];
|
|
45602
45752
|
const text2 = fold(shown2);
|
|
45603
|
-
const
|
|
45604
|
-
|
|
45753
|
+
const route = url2 === void 0 ? void 0 : routeOf(url2);
|
|
45754
|
+
const back = [...this.#unread].filter((v) => text2.includes(v) && (route === void 0 || this.#sourceRoute.get(v) !== route));
|
|
45755
|
+
for (const v of back) {
|
|
45605
45756
|
this.#unread.delete(v);
|
|
45757
|
+
this.#sourceRoute.delete(v);
|
|
45758
|
+
}
|
|
45759
|
+
this.#readBackCount += back.length;
|
|
45760
|
+
for (const v of back)
|
|
45761
|
+
if (this.#readBackValues.length < MAX_READ_BACK_VALUES)
|
|
45762
|
+
this.#readBackValues.push(v);
|
|
45606
45763
|
return back;
|
|
45607
45764
|
}
|
|
45765
|
+
/** #645: how many sent values a page loaded from the server has shown again. */
|
|
45766
|
+
#readBackCount = 0;
|
|
45767
|
+
/** #667: the sent values (folded) read back so far, bounded. */
|
|
45768
|
+
#readBackValues = [];
|
|
45769
|
+
/** #667: the sent values (folded) a page loaded from the server has shown again. */
|
|
45770
|
+
readBackValues() {
|
|
45771
|
+
return this.#readBackValues;
|
|
45772
|
+
}
|
|
45773
|
+
/**
|
|
45774
|
+
* #645: the app was seen to keep what the run's writes sent — at least one value was read back and
|
|
45775
|
+
* none is still unread. Code evidence the page judgment lacks (see `groundDone`).
|
|
45776
|
+
*/
|
|
45777
|
+
readBackConfirmed() {
|
|
45778
|
+
return this.#readBackCount > 0 && this.#unread.size === 0;
|
|
45779
|
+
}
|
|
45608
45780
|
/** #239: the run's own typed, not-yet-saved values (folded). */
|
|
45609
45781
|
ownInputs() {
|
|
45610
45782
|
return this.#ownInputs;
|
|
@@ -45617,9 +45789,9 @@ var ObservedPages = class {
|
|
|
45617
45789
|
const clip5 = (h) => redactContext((h ?? "").replace(/\s+/g, " ").trim(), this.secrets).slice(0, HEADING_CHARS);
|
|
45618
45790
|
const heading = clip5(headings2.heading);
|
|
45619
45791
|
const title = clip5(headings2.title);
|
|
45620
|
-
const
|
|
45621
|
-
const names =
|
|
45622
|
-
const links =
|
|
45792
|
+
const keep2 = (xs) => (xs ?? []).slice(0, MAX_OBSERVED_CONTROLS).map((n3) => redactContext(n3.replace(/\s+/g, " ").trim(), this.secrets).slice(0, OBSERVED_FIELD_CHARS)).filter((n3) => n3 !== "");
|
|
45793
|
+
const names = keep2(headings2.controlNames);
|
|
45794
|
+
const links = keep2(headings2.contentLinks);
|
|
45623
45795
|
const capped = (headings2.controlNames ?? []).length > MAX_OBSERVED_CONTROLS || (headings2.contentLinks ?? []).length > MAX_OBSERVED_CONTROLS;
|
|
45624
45796
|
const incomplete = [headings2.inventoryIncomplete ?? "", capped ? `more than ${MAX_OBSERVED_CONTROLS} controls were kept` : ""].filter((x) => x !== "").join("; ");
|
|
45625
45797
|
const status = headings2.status;
|
|
@@ -45639,7 +45811,10 @@ var ObservedPages = class {
|
|
|
45639
45811
|
if (nav.length > 0)
|
|
45640
45812
|
this.#topNav = nav;
|
|
45641
45813
|
}
|
|
45642
|
-
this
|
|
45814
|
+
this.#settleSent(page.text);
|
|
45815
|
+
const typed = this.#typedOn !== null && this.#typedOn.document === headings2.document ? [...this.#typedOn.values] : [];
|
|
45816
|
+
const appFields = kept.map((f2) => f2.value).filter((value) => !typed.some((t) => fold(value).includes(t)));
|
|
45817
|
+
this.readBack(headings2.document, [page.text, ...appFields].join("\n"), url2);
|
|
45643
45818
|
if (page.text.trim() === "" && kept.length === 0)
|
|
45644
45819
|
return;
|
|
45645
45820
|
const same = (p) => JSON.stringify(p.fields ?? []) === JSON.stringify(page.fields ?? []) && p.heading === page.heading && p.title === page.title && p.status === page.status && JSON.stringify(p.controls ?? []) === JSON.stringify(page.controls ?? []) && JSON.stringify(p.contentLinks ?? []) === JSON.stringify(page.contentLinks ?? []) && p.inventoryIncomplete === page.inventoryIncomplete;
|
|
@@ -45655,6 +45830,13 @@ var ObservedPages = class {
|
|
|
45655
45830
|
return [...this.#pages].reverse();
|
|
45656
45831
|
}
|
|
45657
45832
|
};
|
|
45833
|
+
function routeOf(url2) {
|
|
45834
|
+
try {
|
|
45835
|
+
return new URL(url2).pathname;
|
|
45836
|
+
} catch {
|
|
45837
|
+
return url2.split(/[?#]/)[0] ?? url2;
|
|
45838
|
+
}
|
|
45839
|
+
}
|
|
45658
45840
|
function pathOf6(url2) {
|
|
45659
45841
|
try {
|
|
45660
45842
|
const u = new URL(url2);
|
|
@@ -46297,7 +46479,7 @@ var ABSENCE_GOAL = /\bwhether\b|\b(?:is|are) there\b|\bthere (?:is|are) (?:no|no
|
|
|
46297
46479
|
function goalAdmitsAbsence(goal) {
|
|
46298
46480
|
return ABSENCE_GOAL.test(goal);
|
|
46299
46481
|
}
|
|
46300
|
-
var WRITE_GOAL = /(?:^\s*|[.!?;:]\s+|\b(?:then|and|please)\s+)(?:record|save|create|add|invite|submit|send|post|publish|register|book|schedule)\b/i;
|
|
46482
|
+
var WRITE_GOAL = /(?:^\s*|[.!?;:]\s+|\b(?:then|and|please)\s+)(?:record|save|create|add|invite|submit|send|post|publish|register|book|schedule|import|upload|update|change|edit|rename|delete|remove)\b/i;
|
|
46301
46483
|
function goalAsksToWrite(goal) {
|
|
46302
46484
|
return WRITE_GOAL.test(goal);
|
|
46303
46485
|
}
|
|
@@ -48141,6 +48323,7 @@ function newStep(ctx, input2) {
|
|
|
48141
48323
|
if (proposedDone)
|
|
48142
48324
|
writes = jobWritesOf(ctx);
|
|
48143
48325
|
const writeFacts = writes !== void 0 && saved === null && writesAllSucceeded(writes) ? writes.facts : void 0;
|
|
48326
|
+
const readBackFacts = ctx.observed.readBackConfirmed() ? readBackFactsOf(ctx.observed.readBackValues(), pageText) : void 0;
|
|
48144
48327
|
const judged = await judgeGoalCompletion(cfg.judge, {
|
|
48145
48328
|
goal: cfg.goal,
|
|
48146
48329
|
url: snap.url,
|
|
@@ -48151,6 +48334,7 @@ function newStep(ctx, input2) {
|
|
|
48151
48334
|
...signIn === null ? {} : { signInFacts: signIn.facts },
|
|
48152
48335
|
...saved === null ? {} : { saveFacts: saved.facts },
|
|
48153
48336
|
...writeFacts === void 0 ? {} : { writeFacts },
|
|
48337
|
+
...readBackFacts === void 0 ? {} : { readBackFacts },
|
|
48154
48338
|
fieldValues: fieldValuesOf(snap.controls, ctx.isBound)
|
|
48155
48339
|
}).catch(() => ({ goalMet: null, goalIsSignIn: null, goalIsSave: null, goalIsWrites: null }));
|
|
48156
48340
|
goalMet = judged.goalMet;
|
|
@@ -48165,7 +48349,8 @@ function newStep(ctx, input2) {
|
|
|
48165
48349
|
...goalMet === void 0 ? {} : { goalMetProbability: goalMet },
|
|
48166
48350
|
...signIn === null || goalMet === void 0 ? {} : { signIn: { completed: signIn.completed, goalIsSignIn } },
|
|
48167
48351
|
...saved === null || goalMet === void 0 ? {} : { save: { completed: saved.completed, goalIsSave } },
|
|
48168
|
-
...writes === void 0 || goalMet === void 0 ? {} : { writes: { ...writes, goalIsWrites } }
|
|
48352
|
+
...writes === void 0 || goalMet === void 0 ? {} : { writes: { ...writes, goalIsWrites } },
|
|
48353
|
+
...writes === void 0 ? {} : { readBack: ctx.observed.readBackConfirmed() }
|
|
48169
48354
|
});
|
|
48170
48355
|
const judgments = {};
|
|
48171
48356
|
if (goalMet !== void 0 && goalMet !== null)
|
|
@@ -48180,6 +48365,11 @@ function newStep(ctx, input2) {
|
|
|
48180
48365
|
};
|
|
48181
48366
|
return { ...input2, record: record2, signIn, pendingNote, acceptedBy, groundGoal };
|
|
48182
48367
|
}
|
|
48368
|
+
function readBackFactsOf(values2, pageText) {
|
|
48369
|
+
const text2 = pageText.toLowerCase();
|
|
48370
|
+
const shown2 = values2.filter((v) => text2.includes(v)).length;
|
|
48371
|
+
return `every value this run's writes sent (${values2.length}) was shown again on a page loaded from the server after the write \u2014 the app kept them; this page shows ${shown2} of them`;
|
|
48372
|
+
}
|
|
48183
48373
|
|
|
48184
48374
|
// ../explore/dist/goal-loop/min-effort.js
|
|
48185
48375
|
function deferEnding(ctx, step, ending) {
|
|
@@ -48218,7 +48408,7 @@ async function readBackProbe(ctx, step) {
|
|
|
48218
48408
|
return [];
|
|
48219
48409
|
const text2 = await readPageText(ctx.page, ctx.secrets).catch(() => "");
|
|
48220
48410
|
const values2 = await ctx.page.evaluate(() => Array.from(document.querySelectorAll("input, textarea, select")).filter((e) => e.type !== "password").map((e) => e.value ?? "")).catch(() => []);
|
|
48221
|
-
const back = ctx.observed.readBack(await documentOf(ctx.page), redactPageText([text2, ...values2].join("\n"), ctx.secrets));
|
|
48411
|
+
const back = ctx.observed.readBack(await documentOf(ctx.page), redactPageText([text2, ...values2].join("\n"), ctx.secrets), ctx.page.url());
|
|
48222
48412
|
const listed2 = (vs) => vs.map((v) => `"${v}"`).join(", ");
|
|
48223
48413
|
const missing = unread.filter((v) => !back.includes(v));
|
|
48224
48414
|
ctx.history.push(missing.length === 0 ? `read back after a reload: the page loaded from the server shows what the run sent (${listed2(back)})` : `after a reload, the page loaded from the server no longer shows what the run sent (${listed2(missing)})`);
|
|
@@ -48309,6 +48499,7 @@ async function handleReport(ctx, step) {
|
|
|
48309
48499
|
}
|
|
48310
48500
|
|
|
48311
48501
|
// ../explore/dist/goal-loop/handle-done.js
|
|
48502
|
+
var NO_WRITE_YET = "no write of this run has succeeded yet \u2014 the goal asks for a change the app must receive: go back and press the control that submits it (Import, Save, Send\u2026)";
|
|
48312
48503
|
async function unreadWriteHold(ctx, step, outcome) {
|
|
48313
48504
|
if (outcome.status === "completed" && outcome.verifiedBy === "success-condition")
|
|
48314
48505
|
return null;
|
|
@@ -48400,12 +48591,13 @@ async function handleDone(ctx, step) {
|
|
|
48400
48591
|
}
|
|
48401
48592
|
}
|
|
48402
48593
|
ctx.doneRejections += 1;
|
|
48403
|
-
ctx.
|
|
48404
|
-
|
|
48594
|
+
const why = ctx.writeGoal && !ctx.wroteOk ? `${verdict.reason}; ${NO_WRITE_YET}` : verdict.reason;
|
|
48595
|
+
ctx.history.push(`done rejected: ${why} \u2014 keep working toward the goal`);
|
|
48596
|
+
record2(false, `done rejected (${ctx.doneRejections}/${MAX_DONE_REJECTIONS}): ${why}`, {
|
|
48405
48597
|
...judgments === void 0 ? {} : { judgments }
|
|
48406
48598
|
});
|
|
48407
48599
|
if (ctx.doneRejections >= MAX_DONE_REJECTIONS) {
|
|
48408
|
-
ctx.incomplete = `the model proposed done ${ctx.doneRejections} times, but ${
|
|
48600
|
+
ctx.incomplete = `the model proposed done ${ctx.doneRejections} times, but ${why}`;
|
|
48409
48601
|
ctx.endedOnRejectedDone = true;
|
|
48410
48602
|
ctx.stop = "done";
|
|
48411
48603
|
return "stop";
|
|
@@ -48787,6 +48979,7 @@ async function handleCodeTypedField(ctx, step) {
|
|
|
48787
48979
|
ctx.valueLog.typed(control.name || control.summary, value);
|
|
48788
48980
|
ctx.save.noteTyped(control.name || control.summary, value);
|
|
48789
48981
|
ctx.observed.noteOwnInput(value);
|
|
48982
|
+
ctx.observed.noteTypedOn(value, await documentOf(ctx.page));
|
|
48790
48983
|
ctx.noteMutation(`type ${control.name}`, control.descriptor, snap.signature, at3, { field: keyOf(control), value });
|
|
48791
48984
|
ctx.tracker.countAction();
|
|
48792
48985
|
ctx.cleared(control);
|
|
@@ -49349,6 +49542,7 @@ async function handleFill(ctx, step) {
|
|
|
49349
49542
|
ctx.typeProbe = { key: keyOf(control), label: quote(control.name || control.summary, 60), at: typedAt, background: typedBackground, state: typedState };
|
|
49350
49543
|
ctx.recorder.fill(control.descriptor, text2, at3);
|
|
49351
49544
|
ctx.valueLog.typed(control.name || control.summary, text2);
|
|
49545
|
+
ctx.observed.noteTypedOn(text2, await documentOf(ctx.page));
|
|
49352
49546
|
if (!ctx.isBound(control) && !isCredentialField(control) && !sendable(control)) {
|
|
49353
49547
|
ctx.save.noteTyped(control.name || control.summary, text2);
|
|
49354
49548
|
ctx.observed.noteOwnInput(text2);
|
|
@@ -49396,6 +49590,7 @@ async function perceiveStep(ctx) {
|
|
|
49396
49590
|
if (lc !== null && lc !== ctx.settledClick) {
|
|
49397
49591
|
ctx.settledClick = lc;
|
|
49398
49592
|
if (lc.writes.length > 0 && lc.writes.every((w) => w.status !== null && w.status >= 200 && w.status < 300)) {
|
|
49593
|
+
ctx.observed.noteSentValues(lc.sentValues, ctx.page.url());
|
|
49399
49594
|
ctx.observed.confirmOwnInputs(await documentOf(ctx.page));
|
|
49400
49595
|
ctx.wroteOk = true;
|
|
49401
49596
|
}
|
|
@@ -49947,6 +50142,7 @@ async function viewPage(ctx, step) {
|
|
|
49947
50142
|
} catch {
|
|
49948
50143
|
}
|
|
49949
50144
|
const document2 = await documentOf(ctx.page);
|
|
50145
|
+
const unreadBefore = [...ctx.observed.unreadWrites()];
|
|
49950
50146
|
ctx.observed.add(snap.url, visibleText, [...controlFields(snap.controls), ...richFields], {
|
|
49951
50147
|
...await readPageHeadings(ctx.page, ctx.secrets),
|
|
49952
50148
|
// #647: which document load this is — a later one that shows a sent value reads it back.
|
|
@@ -49963,6 +50159,13 @@ async function viewPage(ctx, step) {
|
|
|
49963
50159
|
navLinks: snap.controls.filter((c) => c.role === "link" && (c.landmark === "navigation" || c.landmark === "banner")).flatMap((c) => typeof c.href === "string" && c.href !== "" ? [c.href] : []),
|
|
49964
50160
|
...ctx.documentStatus.has(ctx.docKey(ctx.page.url())) ? { status: ctx.documentStatus.get(ctx.docKey(ctx.page.url())) } : {}
|
|
49965
50161
|
});
|
|
50162
|
+
{
|
|
50163
|
+
const now2 = ctx.observed.unreadWrites();
|
|
50164
|
+
const back = unreadBefore.filter((v) => !now2.has(v));
|
|
50165
|
+
if (back.length > 0) {
|
|
50166
|
+
ctx.history.push(`read back: this page, loaded from the server, shows what the run sent (${back.map((v) => `"${v}"`).join(", ")}) \u2014 the app kept it`);
|
|
50167
|
+
}
|
|
50168
|
+
}
|
|
49966
50169
|
ctx.noteReplyText(snap.url, visibleText);
|
|
49967
50170
|
ctx.depth.noteState(snap.signature, snap.url);
|
|
49968
50171
|
{
|
|
@@ -52889,14 +53092,14 @@ function ownAssertion(s3) {
|
|
|
52889
53092
|
return { field: "resume", assertion: s3.resume };
|
|
52890
53093
|
return "expect" in s3 ? { field: "expect", assertion: s3.expect } : null;
|
|
52891
53094
|
}
|
|
52892
|
-
function
|
|
53095
|
+
function canonical(d) {
|
|
52893
53096
|
const sort = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? Object.fromEntries(Object.entries(v).sort(([a], [b2]) => a.localeCompare(b2)).map(([k, x]) => [k, sort(x)])) : v;
|
|
52894
53097
|
return JSON.stringify(sort(d));
|
|
52895
53098
|
}
|
|
52896
53099
|
function isOwnTargetVisible(step) {
|
|
52897
53100
|
if (!("expect" in step) || !("target" in step))
|
|
52898
53101
|
return false;
|
|
52899
|
-
return step.expect.kind === "visible" &&
|
|
53102
|
+
return step.expect.kind === "visible" && canonical(step.expect.target) === canonical(step.target);
|
|
52900
53103
|
}
|
|
52901
53104
|
function isNoClaimExpect(step) {
|
|
52902
53105
|
if (!("expect" in step))
|
|
@@ -53257,13 +53460,13 @@ function lintUploadFixtures(journey, baseDir) {
|
|
|
53257
53460
|
});
|
|
53258
53461
|
continue;
|
|
53259
53462
|
}
|
|
53260
|
-
const
|
|
53261
|
-
if (
|
|
53463
|
+
const sensitive2 = fixtureSensitiveReason(real, realRootOf(baseDir)) ?? fixtureSensitiveReason(candidate, resolve5(baseDir));
|
|
53464
|
+
if (sensitive2 !== null) {
|
|
53262
53465
|
findings.push({
|
|
53263
53466
|
rule: "upload-sensitive-file",
|
|
53264
53467
|
level: "error",
|
|
53265
53468
|
step: i + 1,
|
|
53266
|
-
message: `step ${i + 1}: upload fixture ${candidate} looks sensitive (${
|
|
53469
|
+
message: `step ${i + 1}: upload fixture ${candidate} looks sensitive (${sensitive2})`,
|
|
53267
53470
|
fix: "use a dedicated test fixture file"
|
|
53268
53471
|
});
|
|
53269
53472
|
continue;
|
|
@@ -53388,7 +53591,7 @@ function lintJourney(journey, opts = {}) {
|
|
|
53388
53591
|
const refsBound = (a) => assertionRefsBound(journey, a);
|
|
53389
53592
|
const effects = sites.filter((site) => isEffectSite(site, site.where === "step" ? stepOf(site.step) : void 0, refsBound));
|
|
53390
53593
|
const endEffects = effects.filter((site) => site.where === "end-state");
|
|
53391
|
-
const effectAt = (n3) => effects.some((site) => (site.where === "step" || site.where === "step-request") && site.step === n3);
|
|
53594
|
+
const effectAt = (n3) => recorded[n3 - 1]?.step.kind === "waitFor" || effects.some((site) => (site.where === "step" || site.where === "step-request") && site.step === n3);
|
|
53392
53595
|
const writeRequests = recorded.map((r) => writeRequestsOf(r, classify2));
|
|
53393
53596
|
const isWrite2 = recorded.map((r, i) => isWriteStep(r, classify2, writeRequests[i]));
|
|
53394
53597
|
const lastWrite = isWrite2.lastIndexOf(true) + 1;
|
|
@@ -53591,9 +53794,9 @@ function splitBudget(total, stops) {
|
|
|
53591
53794
|
var CLAIM_KEYS = /* @__PURE__ */ new Set(["expect", "check", "resume", "waitFor"]);
|
|
53592
53795
|
function actionRefs(step) {
|
|
53593
53796
|
const out = /* @__PURE__ */ new Set();
|
|
53594
|
-
const
|
|
53797
|
+
const walk2 = (v) => {
|
|
53595
53798
|
if (Array.isArray(v)) {
|
|
53596
|
-
v.forEach(
|
|
53799
|
+
v.forEach(walk2);
|
|
53597
53800
|
return;
|
|
53598
53801
|
}
|
|
53599
53802
|
if (v === null || typeof v !== "object")
|
|
@@ -53608,9 +53811,9 @@ function actionRefs(step) {
|
|
|
53608
53811
|
out.add(n3);
|
|
53609
53812
|
for (const [k, x] of Object.entries(o))
|
|
53610
53813
|
if (!CLAIM_KEYS.has(k))
|
|
53611
|
-
|
|
53814
|
+
walk2(x);
|
|
53612
53815
|
};
|
|
53613
|
-
|
|
53816
|
+
walk2(step);
|
|
53614
53817
|
return [...out];
|
|
53615
53818
|
}
|
|
53616
53819
|
function sitePageAssertion(site) {
|
|
@@ -54786,10 +54989,10 @@ function bindParams(inputs, record2, request) {
|
|
|
54786
54989
|
return params;
|
|
54787
54990
|
}
|
|
54788
54991
|
function recordFingerprint(outputs, record2) {
|
|
54789
|
-
const
|
|
54992
|
+
const canonical6 = {};
|
|
54790
54993
|
for (const name of Object.keys(outputs.fields).sort())
|
|
54791
|
-
|
|
54792
|
-
return `sha256:${createHash5("sha256").update(JSON.stringify(
|
|
54994
|
+
canonical6[name] = record2[name] ?? null;
|
|
54995
|
+
return `sha256:${createHash5("sha256").update(JSON.stringify(canonical6)).digest("hex")}`;
|
|
54793
54996
|
}
|
|
54794
54997
|
function extractedNames(journey) {
|
|
54795
54998
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -57407,7 +57610,7 @@ function seedPath(seedUrl) {
|
|
|
57407
57610
|
}
|
|
57408
57611
|
|
|
57409
57612
|
// ../explore/dist/adversarial/scope.js
|
|
57410
|
-
function
|
|
57613
|
+
function routeOf2(url2) {
|
|
57411
57614
|
const path2 = new URL(url2).pathname;
|
|
57412
57615
|
return path2.length > 1 && path2.endsWith("/") ? path2.slice(0, -1) : path2;
|
|
57413
57616
|
}
|
|
@@ -57427,7 +57630,7 @@ function startRouteGlobs(startUrl) {
|
|
|
57427
57630
|
if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
|
|
57428
57631
|
throw new ScopeUnderivableError(`a ${parsed.protocol} URL has no route`);
|
|
57429
57632
|
}
|
|
57430
|
-
const route =
|
|
57633
|
+
const route = routeOf2(startUrl);
|
|
57431
57634
|
return route === "/" ? ["/", "/**"] : [route, `${route}/`, `${route}/**`];
|
|
57432
57635
|
}
|
|
57433
57636
|
function scopeGlobs(startUrl, extra = []) {
|
|
@@ -59559,7 +59762,7 @@ var CoverageTracker = class {
|
|
|
59559
59762
|
if (this.#counts(c))
|
|
59560
59763
|
this.#controls.add(key2);
|
|
59561
59764
|
}
|
|
59562
|
-
const route =
|
|
59765
|
+
const route = routeOf2(snapshot2.url);
|
|
59563
59766
|
const formControls = this.#region === void 0 ? snapshot2.controls : snapshot2.controls.filter((c) => c.inRegion === true);
|
|
59564
59767
|
for (const f2 of detectForms(formControls, this.#inScope))
|
|
59565
59768
|
this.#forms.add(`${route}|${f2.key}`);
|
|
@@ -59592,7 +59795,7 @@ var CoverageTracker = class {
|
|
|
59592
59795
|
submitted(url2, formKey) {
|
|
59593
59796
|
if (!this.#inScope(url2))
|
|
59594
59797
|
return;
|
|
59595
|
-
this.#submitted.add(`${
|
|
59798
|
+
this.#submitted.add(`${routeOf2(url2)}|${formKey}`);
|
|
59596
59799
|
}
|
|
59597
59800
|
/**
|
|
59598
59801
|
* A submit of `formKey` (on page `url`) never reached the server, so it never counts toward
|
|
@@ -59603,7 +59806,7 @@ var CoverageTracker = class {
|
|
|
59603
59806
|
blocked(url2, formKey, message2, why = "validation") {
|
|
59604
59807
|
if (!this.#inScope(url2))
|
|
59605
59808
|
return;
|
|
59606
|
-
const form = `${
|
|
59809
|
+
const form = `${routeOf2(url2)}|${formKey}`;
|
|
59607
59810
|
const key2 = `${form}|${why}`;
|
|
59608
59811
|
const cur = this.#blocked.get(key2);
|
|
59609
59812
|
this.#blocked.set(key2, { form, why, count: (cur?.count ?? 0) + 1, message: cur?.message ?? message2 });
|
|
@@ -60407,6 +60610,16 @@ function submitRequestSent(monitor, sinceMs) {
|
|
|
60407
60610
|
return true;
|
|
60408
60611
|
return false;
|
|
60409
60612
|
}
|
|
60613
|
+
var SUBMIT_REQUEST_POLL_MS = 50;
|
|
60614
|
+
async function submitRequestSentWithin(monitor, sinceMs, boundMs) {
|
|
60615
|
+
const deadline = clock.now() + boundMs;
|
|
60616
|
+
while (!submitRequestSent(monitor, sinceMs)) {
|
|
60617
|
+
if (clock.now() >= deadline)
|
|
60618
|
+
return false;
|
|
60619
|
+
await clock.sleep(SUBMIT_REQUEST_POLL_MS);
|
|
60620
|
+
}
|
|
60621
|
+
return true;
|
|
60622
|
+
}
|
|
60410
60623
|
async function nativeValidationMessage(page) {
|
|
60411
60624
|
return page.evaluate(() => {
|
|
60412
60625
|
const fields = Array.from(document.querySelectorAll("input,select,textarea"));
|
|
@@ -61626,6 +61839,7 @@ async function refuseMisuseStep(ctx, ep, turn, s3) {
|
|
|
61626
61839
|
}
|
|
61627
61840
|
|
|
61628
61841
|
// ../explore/dist/missions/adversarial-hunt/episode.js
|
|
61842
|
+
var SUBMIT_REQUEST_GRACE_MS = 1e3;
|
|
61629
61843
|
async function runEpisode(ctx, overlay, turn) {
|
|
61630
61844
|
const ep = {};
|
|
61631
61845
|
const { ran, planning, episode } = turn;
|
|
@@ -61708,10 +61922,12 @@ async function runEpisode(ctx, overlay, turn) {
|
|
|
61708
61922
|
if (result.ok)
|
|
61709
61923
|
ctx.cov.acted(ep.stepSnap.url, s3.control);
|
|
61710
61924
|
if (result.ok && s3.submitsForm !== void 0) {
|
|
61711
|
-
|
|
61925
|
+
const monitor = monitorFor(ctx.sessions.page);
|
|
61926
|
+
const invalid = submitRequestSent(monitor, at3) ? void 0 : await nativeValidationMessage(ctx.sessions.page);
|
|
61927
|
+
if (invalid === void 0 && await submitRequestSentWithin(monitor, at3, SUBMIT_REQUEST_GRACE_MS)) {
|
|
61712
61928
|
ctx.cov.submitted(ep.stepSnap.url, s3.submitsForm);
|
|
61713
61929
|
} else {
|
|
61714
|
-
ctx.cov.blocked(ep.stepSnap.url, s3.submitsForm,
|
|
61930
|
+
ctx.cov.blocked(ep.stepSnap.url, s3.submitsForm, invalid);
|
|
61715
61931
|
}
|
|
61716
61932
|
}
|
|
61717
61933
|
if (ran === "visit-route" && s3.control !== null)
|
|
@@ -62902,9 +63118,9 @@ function hostStarvedFailure(failure3, health) {
|
|
|
62902
63118
|
}
|
|
62903
63119
|
|
|
62904
63120
|
// src/build-info.generated.ts
|
|
62905
|
-
var GENERATED_COMMIT = "
|
|
62906
|
-
var GENERATED_TREE = "
|
|
62907
|
-
var GENERATED_BUILT_AT = "2026-10-
|
|
63121
|
+
var GENERATED_COMMIT = "e0f6f96";
|
|
63122
|
+
var GENERATED_TREE = "d0b472e";
|
|
63123
|
+
var GENERATED_BUILT_AT = "2026-10-24T16:37:57.792Z";
|
|
62908
63124
|
|
|
62909
63125
|
// src/version.ts
|
|
62910
63126
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
@@ -62926,13 +63142,13 @@ function readCliVersion() {
|
|
|
62926
63142
|
|
|
62927
63143
|
// src/engine.ts
|
|
62928
63144
|
function bundledCommit() {
|
|
62929
|
-
return true ? "
|
|
63145
|
+
return true ? "e0f6f96" : void 0;
|
|
62930
63146
|
}
|
|
62931
63147
|
function bundledBuiltAt() {
|
|
62932
|
-
return true ? "2026-10-
|
|
63148
|
+
return true ? "2026-10-24T16:37:57.792Z" : void 0;
|
|
62933
63149
|
}
|
|
62934
63150
|
function bundledTree() {
|
|
62935
|
-
return true ? "
|
|
63151
|
+
return true ? "d0b472e" : void 0;
|
|
62936
63152
|
}
|
|
62937
63153
|
function currentBuildTree() {
|
|
62938
63154
|
return bundledTree() ?? GENERATED_TREE;
|
|
@@ -63575,12 +63791,12 @@ function substituteDeep(v, sub) {
|
|
|
63575
63791
|
return v;
|
|
63576
63792
|
}
|
|
63577
63793
|
function fixtureSpecHash(spec, hooks) {
|
|
63578
|
-
return sha256(
|
|
63794
|
+
return sha256(canonical2({ spec: spec ?? null, hooks: hooks ?? null })).slice(0, 16);
|
|
63579
63795
|
}
|
|
63580
|
-
function
|
|
63581
|
-
if (Array.isArray(v)) return `[${v.map(
|
|
63796
|
+
function canonical2(v) {
|
|
63797
|
+
if (Array.isArray(v)) return `[${v.map(canonical2).join(",")}]`;
|
|
63582
63798
|
if (isRecord(v)) {
|
|
63583
|
-
return `{${Object.keys(v).sort().filter((k) => v[k] !== void 0).map((k) => `${JSON.stringify(k)}:${
|
|
63799
|
+
return `{${Object.keys(v).sort().filter((k) => v[k] !== void 0).map((k) => `${JSON.stringify(k)}:${canonical2(v[k])}`).join(",")}}`;
|
|
63584
63800
|
}
|
|
63585
63801
|
return JSON.stringify(v);
|
|
63586
63802
|
}
|
|
@@ -63702,7 +63918,7 @@ var MissionFixtures = class {
|
|
|
63702
63918
|
return redactUrl(redactText(text2, [...this.#opts.secrets ?? [], ...bound2, ...this.secrets()]));
|
|
63703
63919
|
}
|
|
63704
63920
|
identity() {
|
|
63705
|
-
return `fx-${sha256(
|
|
63921
|
+
return `fx-${sha256(canonical2({ spec: this.#specHash, outputs: this.publicOutputs(), secret: [...this.#secretNames].sort() })).slice(0, 16)}`;
|
|
63706
63922
|
}
|
|
63707
63923
|
record() {
|
|
63708
63924
|
return {
|
|
@@ -78800,14 +79016,14 @@ var GitSourceManager = class {
|
|
|
78800
79016
|
|
|
78801
79017
|
// ../sources/dist/hash.js
|
|
78802
79018
|
import { createHash as createHash9 } from "node:crypto";
|
|
78803
|
-
function
|
|
79019
|
+
function canonicalJson3(value) {
|
|
78804
79020
|
if (value === null)
|
|
78805
79021
|
return "null";
|
|
78806
79022
|
if (Array.isArray(value))
|
|
78807
|
-
return `[${value.map(
|
|
79023
|
+
return `[${value.map(canonicalJson3).join(",")}]`;
|
|
78808
79024
|
if (typeof value === "object") {
|
|
78809
79025
|
const entries = Object.entries(value).filter(([, v]) => v !== void 0).sort(([a], [b2]) => a < b2 ? -1 : a > b2 ? 1 : 0);
|
|
78810
|
-
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${
|
|
79026
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson3(v)}`).join(",")}}`;
|
|
78811
79027
|
}
|
|
78812
79028
|
if (typeof value === "number") {
|
|
78813
79029
|
if (!Number.isFinite(value))
|
|
@@ -78818,7 +79034,7 @@ function canonicalJson2(value) {
|
|
|
78818
79034
|
}
|
|
78819
79035
|
function canonicalJourneyHash(file2) {
|
|
78820
79036
|
const validated = SharedJourneyFileSchema.parse(file2);
|
|
78821
|
-
const hex3 = createHash9("sha256").update(
|
|
79037
|
+
const hex3 = createHash9("sha256").update(canonicalJson3(validated), "utf8").digest("hex");
|
|
78822
79038
|
return `sha256:${hex3}`;
|
|
78823
79039
|
}
|
|
78824
79040
|
|
|
@@ -80524,6 +80740,15 @@ function journeyReviewHash(journey) {
|
|
|
80524
80740
|
for (const k of BOOKKEEPING) delete metadata[k];
|
|
80525
80741
|
return contentHash({ metadata, recording: journey.recording });
|
|
80526
80742
|
}
|
|
80743
|
+
function journeyLegacyReviewHash(journey) {
|
|
80744
|
+
const metadata = { ...journey.metadata };
|
|
80745
|
+
for (const k of BOOKKEEPING) delete metadata[k];
|
|
80746
|
+
return legacyContentHash({ metadata, recording: journey.recording });
|
|
80747
|
+
}
|
|
80748
|
+
function journeyApprovalMatches(journey) {
|
|
80749
|
+
const approved = journey.metadata.approval?.contentHash;
|
|
80750
|
+
return approved !== void 0 && (approved === journeyReviewHash(journey) || approved === journeyLegacyReviewHash(journey));
|
|
80751
|
+
}
|
|
80527
80752
|
function targetOf3(step) {
|
|
80528
80753
|
if (step.kind === "forEach") return step.items;
|
|
80529
80754
|
return "target" in step ? step.target : void 0;
|
|
@@ -80730,7 +80955,7 @@ function buildChange(journey, hash3, snapshot2, stepIdOnly) {
|
|
|
80730
80955
|
const idOnly = stepIdOnly ? { stepIdOnly: true } : {};
|
|
80731
80956
|
if (snapshot2 === null || snapshot2 === void 0) {
|
|
80732
80957
|
if (approval !== void 0) {
|
|
80733
|
-
return { kind: "snapshot-missing", approvedHash: approval.contentHash, approvedAt: approval.at, changed:
|
|
80958
|
+
return { kind: "snapshot-missing", approvedHash: approval.contentHash, approvedAt: approval.at, changed: !journeyApprovalMatches(journey), ...idOnly };
|
|
80734
80959
|
}
|
|
80735
80960
|
return journey.metadata.promoted ? { kind: "no-record" } : { kind: "first-approval" };
|
|
80736
80961
|
}
|
|
@@ -81084,9 +81309,9 @@ var UnknownCatalogItemError = class extends Error {
|
|
|
81084
81309
|
return this.kind === "persona" ? "E_UNKNOWN_PERSONA" : "E_UNKNOWN_JOB";
|
|
81085
81310
|
}
|
|
81086
81311
|
};
|
|
81087
|
-
function statusOf3(approval, hash3) {
|
|
81312
|
+
function statusOf3(approval, hash3, legacyHash) {
|
|
81088
81313
|
if (approval === void 0) return "draft";
|
|
81089
|
-
return approval.contentHash === hash3 ? "approved" : "stale";
|
|
81314
|
+
return approval.contentHash === hash3 || approval.contentHash === legacyHash ? "approved" : "stale";
|
|
81090
81315
|
}
|
|
81091
81316
|
async function readJsonFile(path2) {
|
|
81092
81317
|
const raw = await readFile8(path2, "utf8");
|
|
@@ -81151,7 +81376,7 @@ function catalogPersona(p, path2) {
|
|
|
81151
81376
|
hasLogin: p.fields.login !== void 0,
|
|
81152
81377
|
...approval === void 0 ? {} : { approval },
|
|
81153
81378
|
contentHash: hash3,
|
|
81154
|
-
status: statusOf3(approval, hash3)
|
|
81379
|
+
status: statusOf3(approval, hash3, personaLegacyContentHash(p.id, p.fields))
|
|
81155
81380
|
};
|
|
81156
81381
|
}
|
|
81157
81382
|
function personaContentHash(id, fields) {
|
|
@@ -81159,6 +81384,11 @@ function personaContentHash(id, fields) {
|
|
|
81159
81384
|
for (const [k, v] of Object.entries(fields)) if (k !== "approval" && !SESSION_KEYS.has(k)) described[k] = v;
|
|
81160
81385
|
return contentHash(described);
|
|
81161
81386
|
}
|
|
81387
|
+
function personaLegacyContentHash(id, fields) {
|
|
81388
|
+
const described = { id };
|
|
81389
|
+
for (const [k, v] of Object.entries(fields)) if (k !== "approval" && !SESSION_KEYS.has(k)) described[k] = v;
|
|
81390
|
+
return legacyContentHash(described);
|
|
81391
|
+
}
|
|
81162
81392
|
function jobList(raw, path2) {
|
|
81163
81393
|
if (Array.isArray(raw)) return raw;
|
|
81164
81394
|
if (isRecord(raw) && Array.isArray(raw.jobs)) return raw.jobs;
|
|
@@ -81181,13 +81411,17 @@ function catalogJobOf(job) {
|
|
|
81181
81411
|
personas: [.../* @__PURE__ */ new Set([...job.personas ?? [], ...job.persona === void 0 ? [] : [job.persona]])],
|
|
81182
81412
|
...job.approval === void 0 ? {} : { approval: job.approval },
|
|
81183
81413
|
contentHash: hash3,
|
|
81184
|
-
status: statusOf3(job.approval, hash3)
|
|
81414
|
+
status: statusOf3(job.approval, hash3, jobLegacyContentHash(job))
|
|
81185
81415
|
};
|
|
81186
81416
|
}
|
|
81187
81417
|
function jobContentHash(job) {
|
|
81188
81418
|
const { approval: _a3, ...rest } = job;
|
|
81189
81419
|
return contentHash(rest);
|
|
81190
81420
|
}
|
|
81421
|
+
function jobLegacyContentHash(job) {
|
|
81422
|
+
const { approval: _a3, ...rest } = job;
|
|
81423
|
+
return legacyContentHash(rest);
|
|
81424
|
+
}
|
|
81191
81425
|
function catalogJourney(journey) {
|
|
81192
81426
|
const m = journey.metadata;
|
|
81193
81427
|
const hash3 = journeyReviewHash(journey);
|
|
@@ -81200,7 +81434,7 @@ function catalogJourney(journey) {
|
|
|
81200
81434
|
...m.persona === void 0 ? {} : { personaText: m.persona },
|
|
81201
81435
|
...m.approval === void 0 ? {} : { approval: m.approval },
|
|
81202
81436
|
contentHash: hash3,
|
|
81203
|
-
changedSinceApproval: m.promoted && m.approval !== void 0 &&
|
|
81437
|
+
changedSinceApproval: m.promoted && m.approval !== void 0 && !journeyApprovalMatches(journey)
|
|
81204
81438
|
};
|
|
81205
81439
|
}
|
|
81206
81440
|
var CatalogLoader = class {
|
|
@@ -82703,12 +82937,12 @@ function healFloor(recorded, riskOf) {
|
|
|
82703
82937
|
}
|
|
82704
82938
|
return { floor: "healable", reason: "" };
|
|
82705
82939
|
}
|
|
82706
|
-
function
|
|
82940
|
+
function canonical3(v) {
|
|
82707
82941
|
if (Array.isArray(v))
|
|
82708
|
-
return `[${v.map(
|
|
82942
|
+
return `[${v.map(canonical3).join(",")}]`;
|
|
82709
82943
|
if (v !== null && typeof v === "object") {
|
|
82710
82944
|
const o = v;
|
|
82711
|
-
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${
|
|
82945
|
+
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${canonical3(o[k])}`).join(",")}}`;
|
|
82712
82946
|
}
|
|
82713
82947
|
return JSON.stringify(v) ?? "undefined";
|
|
82714
82948
|
}
|
|
@@ -82730,7 +82964,7 @@ function assertProofUntouched(before, after) {
|
|
|
82730
82964
|
for (const k of /* @__PURE__ */ new Set([...Object.keys(b2), ...Object.keys(a)])) {
|
|
82731
82965
|
if (free.has(k))
|
|
82732
82966
|
continue;
|
|
82733
|
-
if (
|
|
82967
|
+
if (canonical3(b2[k]) !== canonical3(a[k]))
|
|
82734
82968
|
return { code: "proof-field-changed", detail: `the candidate changes the step's \`${k}\`` };
|
|
82735
82969
|
}
|
|
82736
82970
|
return null;
|
|
@@ -82741,15 +82975,15 @@ function assertRecordedProofUntouched(before, after) {
|
|
|
82741
82975
|
for (const k of /* @__PURE__ */ new Set([...Object.keys(b2), ...Object.keys(a)])) {
|
|
82742
82976
|
if (k === "step")
|
|
82743
82977
|
continue;
|
|
82744
|
-
if (
|
|
82978
|
+
if (canonical3(b2[k]) !== canonical3(a[k]))
|
|
82745
82979
|
return { code: "proof-field-changed", detail: `the candidate changes the recorded step's \`${k}\`` };
|
|
82746
82980
|
}
|
|
82747
|
-
return
|
|
82981
|
+
return canonical3(before.step) === canonical3(after.step) ? null : assertProofUntouched(before.step, after.step);
|
|
82748
82982
|
}
|
|
82749
82983
|
function assertRecordingProofUntouched(base, proposed) {
|
|
82750
82984
|
const { pages: bp, ...bRest } = base;
|
|
82751
82985
|
const { pages: pp, ...pRest } = proposed;
|
|
82752
|
-
if (
|
|
82986
|
+
if (canonical3(bRest) !== canonical3(pRest))
|
|
82753
82987
|
return { code: "proof-field-changed", detail: "the revision changes the recording's own fields" };
|
|
82754
82988
|
if (bp.length !== pp.length)
|
|
82755
82989
|
return { code: "shape-changed", detail: `the revision has ${pp.length} pages; the Journey has ${bp.length}` };
|
|
@@ -82757,7 +82991,7 @@ function assertRecordingProofUntouched(base, proposed) {
|
|
|
82757
82991
|
for (let i = 0; i < bp.length; i++) {
|
|
82758
82992
|
const { steps: bs, ...bPage } = bp[i];
|
|
82759
82993
|
const { steps: ps, ...pPage } = pp[i];
|
|
82760
|
-
if (
|
|
82994
|
+
if (canonical3(bPage) !== canonical3(pPage))
|
|
82761
82995
|
return { code: "proof-field-changed", detail: `the revision changes page ${i + 1}` };
|
|
82762
82996
|
if (bs.length !== ps.length)
|
|
82763
82997
|
return { code: "shape-changed", detail: `the revision has ${ps.length} steps on page ${i + 1}; the Journey has ${bs.length}` };
|
|
@@ -84343,11 +84577,11 @@ async function removeFile(path2) {
|
|
|
84343
84577
|
await assertNoSymlink(path2);
|
|
84344
84578
|
await rm4(path2, { force: true });
|
|
84345
84579
|
}
|
|
84346
|
-
function
|
|
84347
|
-
if (Array.isArray(v)) return `[${v.map(
|
|
84580
|
+
function canonical4(v) {
|
|
84581
|
+
if (Array.isArray(v)) return `[${v.map(canonical4).join(",")}]`;
|
|
84348
84582
|
if (v !== null && typeof v === "object") {
|
|
84349
84583
|
const o = v;
|
|
84350
|
-
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${
|
|
84584
|
+
return `{${Object.keys(o).filter((k) => o[k] !== void 0).sort().map((k) => `${JSON.stringify(k)}:${canonical4(o[k])}`).join(",")}}`;
|
|
84351
84585
|
}
|
|
84352
84586
|
return JSON.stringify(v) ?? "undefined";
|
|
84353
84587
|
}
|
|
@@ -84372,7 +84606,7 @@ function proposalFloorViolation(stored, proposed) {
|
|
|
84372
84606
|
for (let i = 0; i < after.length; i++) {
|
|
84373
84607
|
const b2 = before[i];
|
|
84374
84608
|
const a = after[i];
|
|
84375
|
-
if (b2 !== void 0 &&
|
|
84609
|
+
if (b2 !== void 0 && canonical4(b2.step) === canonical4(a.step)) continue;
|
|
84376
84610
|
for (const [which, rec] of [["before", b2?.recorded], ["after", a.recorded]]) {
|
|
84377
84611
|
if (rec === void 0) return `step ${i + 1} is not in the stored Journey`;
|
|
84378
84612
|
const floor = healFloor(rec, riskOf);
|
|
@@ -86065,7 +86299,7 @@ var FINGERPRINT_MS = 1e4;
|
|
|
86065
86299
|
function oneLine2(s3) {
|
|
86066
86300
|
return s3.replace(/\s+/g, " ").trim();
|
|
86067
86301
|
}
|
|
86068
|
-
function
|
|
86302
|
+
function routeOf3(url2) {
|
|
86069
86303
|
try {
|
|
86070
86304
|
const u = new URL(url2);
|
|
86071
86305
|
return u.pathname + (u.search === "" ? "" : u.search);
|
|
@@ -86142,7 +86376,7 @@ var RunScreenshots = class {
|
|
|
86142
86376
|
this.#entries.push({
|
|
86143
86377
|
n: n3,
|
|
86144
86378
|
step: at3.step,
|
|
86145
|
-
route: this.#redact(redactUrl(
|
|
86379
|
+
route: this.#redact(redactUrl(routeOf3(url2))),
|
|
86146
86380
|
what: this.#redact(oneLine2(at3.what)).slice(0, 300),
|
|
86147
86381
|
path: path2
|
|
86148
86382
|
});
|
|
@@ -86466,7 +86700,7 @@ function invariantSetupTexts(spec) {
|
|
|
86466
86700
|
return Object.fromEntries(specStrings(spec, "", []).filter(([, s3]) => s3.includes("${setup.")).map(([p, s3]) => [`--invariants ${p}`, s3]));
|
|
86467
86701
|
}
|
|
86468
86702
|
function substituteSpecSetupRefs(spec, b2, baseUrl) {
|
|
86469
|
-
const
|
|
86703
|
+
const walk2 = (v, path2) => {
|
|
86470
86704
|
if (typeof v === "string") {
|
|
86471
86705
|
if (!v.includes("${setup.")) return v;
|
|
86472
86706
|
const where = `--invariants ${path2}`;
|
|
@@ -86481,11 +86715,11 @@ function substituteSpecSetupRefs(spec, b2, baseUrl) {
|
|
|
86481
86715
|
if (before !== after) throw new UnboundSetupRefError(`${where}: a \${setup.*} value may fill the path or query, never the origin`);
|
|
86482
86716
|
return out;
|
|
86483
86717
|
}
|
|
86484
|
-
if (Array.isArray(v)) return v.map((x, i) =>
|
|
86485
|
-
if (v !== null && typeof v === "object") return Object.fromEntries(Object.entries(v).map(([k, x]) => [k,
|
|
86718
|
+
if (Array.isArray(v)) return v.map((x, i) => walk2(x, `${path2}[${i}]`));
|
|
86719
|
+
if (v !== null && typeof v === "object") return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, walk2(x, path2 === "" ? k : `${path2}.${k}`)]));
|
|
86486
86720
|
return v;
|
|
86487
86721
|
};
|
|
86488
|
-
return
|
|
86722
|
+
return walk2(spec, "");
|
|
86489
86723
|
}
|
|
86490
86724
|
function fixtureSetupFailedResult(err, fx) {
|
|
86491
86725
|
const reason = err instanceof FixtureSetupError ? err.message : `fixture setup failed: ${err.message}`;
|
|
@@ -87785,6 +88019,7 @@ var ServerLogRuntime = class {
|
|
|
87785
88019
|
};
|
|
87786
88020
|
}
|
|
87787
88021
|
};
|
|
88022
|
+
var NON_ACTING_OPS = /* @__PURE__ */ new Set(["done", "blocked", null]);
|
|
87788
88023
|
function stepWindows(transcript, stepEpoch, missionStartEpochMs, drainMs) {
|
|
87789
88024
|
const windows = [];
|
|
87790
88025
|
let prevEnd = missionStartEpochMs;
|
|
@@ -87793,7 +88028,9 @@ function stepWindows(transcript, stepEpoch, missionStartEpochMs, drainMs) {
|
|
|
87793
88028
|
const settle2 = stepEpoch.get(entry.step) ?? prevEnd;
|
|
87794
88029
|
const isLast = i === transcript.length - 1;
|
|
87795
88030
|
const endMs = isLast ? settle2 + drainMs : settle2;
|
|
87796
|
-
|
|
88031
|
+
const acting = windows[windows.length - 1];
|
|
88032
|
+
if (NON_ACTING_OPS.has(entry.op) && acting !== void 0) acting.endMs = endMs;
|
|
88033
|
+
else windows.push({ step: entry.step, startMs: prevEnd, endMs });
|
|
87797
88034
|
prevEnd = settle2;
|
|
87798
88035
|
}
|
|
87799
88036
|
return windows;
|
|
@@ -88690,7 +88927,7 @@ function findingIdentity(f2) {
|
|
|
88690
88927
|
function str2(v, fallback = "") {
|
|
88691
88928
|
return typeof v === "string" ? v : fallback;
|
|
88692
88929
|
}
|
|
88693
|
-
function
|
|
88930
|
+
function routeOf4(item) {
|
|
88694
88931
|
if (typeof item.route === "string") return item.route;
|
|
88695
88932
|
if (typeof item.url === "string") return normalizeRoute(item.url);
|
|
88696
88933
|
return "/";
|
|
@@ -88702,7 +88939,7 @@ function extractRunFindings(data) {
|
|
|
88702
88939
|
if (!Array.isArray(list2)) return;
|
|
88703
88940
|
for (const item of list2) {
|
|
88704
88941
|
if (!isRecord(item) || typeof item.fingerprint !== "string") continue;
|
|
88705
|
-
out.push({ kind: kindOf(item), fingerprint: item.fingerprint, route:
|
|
88942
|
+
out.push({ kind: kindOf(item), fingerprint: item.fingerprint, route: routeOf4(item), title: str2(item.title, item.fingerprint) });
|
|
88706
88943
|
}
|
|
88707
88944
|
};
|
|
88708
88945
|
fingerprinted(data.defects, (d) => d.kind === "invariant" ? "invariant" : `defect/${str2(d.kind, "unknown")}`);
|
|
@@ -88711,14 +88948,14 @@ function extractRunFindings(data) {
|
|
|
88711
88948
|
if (isRecord(data.coverage) && Array.isArray(data.coverage.defects)) {
|
|
88712
88949
|
for (const d of data.coverage.defects) {
|
|
88713
88950
|
if (!isRecord(d) || typeof d.stateFingerprint !== "string") continue;
|
|
88714
|
-
out.push({ kind: "coverage-defect", fingerprint: d.stateFingerprint, route:
|
|
88951
|
+
out.push({ kind: "coverage-defect", fingerprint: d.stateFingerprint, route: routeOf4(d), title: str2(d.reason, "looks broken") });
|
|
88715
88952
|
}
|
|
88716
88953
|
}
|
|
88717
88954
|
if (isRecord(data.report) && Array.isArray(data.report.findings)) {
|
|
88718
88955
|
for (const f2 of data.report.findings) {
|
|
88719
88956
|
if (!isRecord(f2) || typeof f2.rubricItemId !== "string") continue;
|
|
88720
88957
|
const controls = Array.isArray(f2.controls) ? f2.controls.filter((c) => typeof c === "string") : [];
|
|
88721
|
-
out.push({ kind: "ux", route:
|
|
88958
|
+
out.push({ kind: "ux", route: routeOf4(f2), title: str2(f2.observation, f2.rubricItemId), rubricItemId: f2.rubricItemId, controls });
|
|
88722
88959
|
}
|
|
88723
88960
|
}
|
|
88724
88961
|
return out;
|
|
@@ -91803,18 +92040,18 @@ function rebaseRecording(recording, env) {
|
|
|
91803
92040
|
if (mustBeAllowed && !env.allowedOrigins.includes(u.origin)) refusals.push(`${where} is on ${u.origin}`);
|
|
91804
92041
|
return raw;
|
|
91805
92042
|
};
|
|
91806
|
-
const
|
|
91807
|
-
if (Array.isArray(v)) return v.map((x, i) =>
|
|
92043
|
+
const walk2 = (v, where) => {
|
|
92044
|
+
if (Array.isArray(v)) return v.map((x, i) => walk2(x, `${where}[${i}]`));
|
|
91808
92045
|
if (v === null || typeof v !== "object") return v;
|
|
91809
92046
|
const o = v;
|
|
91810
92047
|
const out = {};
|
|
91811
|
-
for (const [k, x] of Object.entries(o)) out[k] =
|
|
92048
|
+
for (const [k, x] of Object.entries(o)) out[k] = walk2(x, `${where}.${k}`);
|
|
91812
92049
|
if (o.kind === "navigate" && typeof o.url === "string") out.url = move(o.url, `${where} (navigate ${o.url})`, true);
|
|
91813
92050
|
if (o.kind === "urlIncludes" && typeof o.text === "string") out.text = move(o.text, `${where}.text`, false);
|
|
91814
92051
|
return out;
|
|
91815
92052
|
};
|
|
91816
92053
|
const pages = recording.pages.map((p, i) => ({
|
|
91817
|
-
...
|
|
92054
|
+
...walk2(p, `pages[${i}]`),
|
|
91818
92055
|
url: move(p.url, `pages[${i}] (${p.url})`, true)
|
|
91819
92056
|
}));
|
|
91820
92057
|
if (refusals.length > 0) {
|
|
@@ -94586,7 +94823,7 @@ function classifyTarget(d, testIdAttributes, via) {
|
|
|
94586
94823
|
function kebabCase(s3) {
|
|
94587
94824
|
return s3.normalize("NFKD").replace(/[̀-ͯ]/g, "").replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40).replace(/-+$/g, "");
|
|
94588
94825
|
}
|
|
94589
|
-
function
|
|
94826
|
+
function routeOf5(url2) {
|
|
94590
94827
|
if (url2 === void 0 || url2 === "") return void 0;
|
|
94591
94828
|
try {
|
|
94592
94829
|
return new URL(url2).pathname;
|
|
@@ -94684,7 +94921,7 @@ function dedupeSuggestions(lists) {
|
|
|
94684
94921
|
return [...byKey.values()].sort((a, b2) => b2.occurrences.length - a.occurrences.length || a.key.localeCompare(b2.key));
|
|
94685
94922
|
}
|
|
94686
94923
|
function flatWithRoute(rec) {
|
|
94687
|
-
return rec.pages.flatMap((p) => p.steps.map((recorded) => ({ recorded, route:
|
|
94924
|
+
return rec.pages.flatMap((p) => p.steps.map((recorded) => ({ recorded, route: routeOf5(p.url) })));
|
|
94688
94925
|
}
|
|
94689
94926
|
function healthLine(h) {
|
|
94690
94927
|
const total = h.stable + h.brittle;
|
|
@@ -97454,7 +97691,7 @@ function controlKey2(c) {
|
|
|
97454
97691
|
}
|
|
97455
97692
|
|
|
97456
97693
|
// ../ux/dist/route.js
|
|
97457
|
-
function
|
|
97694
|
+
function routeOf6(url2) {
|
|
97458
97695
|
try {
|
|
97459
97696
|
return urlTemplate(new URL(url2).pathname || "/");
|
|
97460
97697
|
} catch {
|
|
@@ -97603,7 +97840,7 @@ function a11yChecks(evidence, rubric) {
|
|
|
97603
97840
|
const q = rubric.get(id)?.questions[0];
|
|
97604
97841
|
return q?.severity ?? "minor";
|
|
97605
97842
|
};
|
|
97606
|
-
const route =
|
|
97843
|
+
const route = routeOf6(evidence.url);
|
|
97607
97844
|
const controlByRef = new Map(evidence.controls.map((c) => [`control:${c.index}`, c]));
|
|
97608
97845
|
const labelOf2 = (refId) => {
|
|
97609
97846
|
const c = controlByRef.get(refId);
|
|
@@ -97946,7 +98183,7 @@ function makeSignalFinding(input2) {
|
|
|
97946
98183
|
recommendation: input2.recommendation.trim(),
|
|
97947
98184
|
tier: "signal",
|
|
97948
98185
|
screenId: input2.screenId,
|
|
97949
|
-
route:
|
|
98186
|
+
route: routeOf6(input2.url),
|
|
97950
98187
|
controls: [...input2.controls ?? []],
|
|
97951
98188
|
quotes: [...input2.quotes ?? []],
|
|
97952
98189
|
occurrences: input2.occurrences ?? 1,
|
|
@@ -98183,7 +98420,7 @@ function detectInternalIds(capture2) {
|
|
|
98183
98420
|
if (typed.includes(id))
|
|
98184
98421
|
continue;
|
|
98185
98422
|
const line2 = quoteLine(screen.visibleText, id);
|
|
98186
|
-
const key2 = `${
|
|
98423
|
+
const key2 = `${routeOf6(screen.url)}|${pattern.name}`;
|
|
98187
98424
|
const g = groups.get(key2) ?? { pattern, ids: /* @__PURE__ */ new Set(), lines: /* @__PURE__ */ new Set(), screens: [], intentional: true };
|
|
98188
98425
|
g.ids.add(id);
|
|
98189
98426
|
g.lines.add(line2);
|
|
@@ -98292,7 +98529,7 @@ var PASSIVE_OPS = /* @__PURE__ */ new Set(["wait", "scroll_down", "scroll_up"]);
|
|
|
98292
98529
|
function advanced(capture2, s3) {
|
|
98293
98530
|
const before = capture2.screens.filter((x) => x.step === s3.step).pop();
|
|
98294
98531
|
const after = capture2.screens.find((x) => x.step > s3.step);
|
|
98295
|
-
if (before === void 0 || after === void 0 ||
|
|
98532
|
+
if (before === void 0 || after === void 0 || routeOf6(after.url) !== routeOf6(before.url))
|
|
98296
98533
|
return void 0;
|
|
98297
98534
|
return after.visibleText !== before.visibleText ? after : void 0;
|
|
98298
98535
|
}
|
|
@@ -98315,7 +98552,7 @@ function detectRepeatedAdvances(capture2) {
|
|
|
98315
98552
|
close();
|
|
98316
98553
|
continue;
|
|
98317
98554
|
}
|
|
98318
|
-
const k = `${
|
|
98555
|
+
const k = `${routeOf6(s3.url)}|${label2.toLowerCase()}`;
|
|
98319
98556
|
if (key2 !== null && k !== key2)
|
|
98320
98557
|
close();
|
|
98321
98558
|
key2 = k;
|
|
@@ -98343,7 +98580,7 @@ function detectRepeatedAdvances(capture2) {
|
|
|
98343
98580
|
steps,
|
|
98344
98581
|
requests: [],
|
|
98345
98582
|
...last.after.screenshot === void 0 ? {} : { screenshot: last.after.screenshot },
|
|
98346
|
-
detail: `${n3} consecutive successful "${label2}" clicks on ${
|
|
98583
|
+
detail: `${n3} consecutive successful "${label2}" clicks on ${routeOf6(last.after.url)} with no input between them, each changing the page text`
|
|
98347
98584
|
}
|
|
98348
98585
|
});
|
|
98349
98586
|
});
|
|
@@ -98395,7 +98632,7 @@ function detectStuckJobs(capture2, opts = {}) {
|
|
|
98395
98632
|
const label2 = labelOf(start);
|
|
98396
98633
|
if (label2 === null)
|
|
98397
98634
|
continue;
|
|
98398
|
-
const route =
|
|
98635
|
+
const route = routeOf6(start.url);
|
|
98399
98636
|
const key2 = `${route}|${normLine(label2)}`;
|
|
98400
98637
|
if (reported.has(key2))
|
|
98401
98638
|
continue;
|
|
@@ -98404,7 +98641,7 @@ function detectStuckJobs(capture2, opts = {}) {
|
|
|
98404
98641
|
continue;
|
|
98405
98642
|
const window2 = [];
|
|
98406
98643
|
for (const s3 of capture2.steps.filter((x) => x.step > start.step)) {
|
|
98407
|
-
if (
|
|
98644
|
+
if (routeOf6(s3.url) !== route)
|
|
98408
98645
|
break;
|
|
98409
98646
|
const other = s3.op !== null && START_OPS.has(s3.op) && controlKey3(s3) !== controlKey3(start);
|
|
98410
98647
|
if (other && capture2.requests.some((r) => r.step === s3.step && isWrite(r) && okStatus(r)))
|
|
@@ -98412,7 +98649,7 @@ function detectStuckJobs(capture2, opts = {}) {
|
|
|
98412
98649
|
window2.push(s3);
|
|
98413
98650
|
}
|
|
98414
98651
|
const lastStep = window2.length > 0 ? window2[window2.length - 1].step : start.step;
|
|
98415
|
-
const screens = capture2.screens.filter((s3) => s3.step > start.step && s3.step <= lastStep + 1 &&
|
|
98652
|
+
const screens = capture2.screens.filter((s3) => s3.step > start.step && s3.step <= lastStep + 1 && routeOf6(s3.url) === route);
|
|
98416
98653
|
if (screens.length === 0)
|
|
98417
98654
|
continue;
|
|
98418
98655
|
const reclicks = window2.filter((s3) => s3.op === start.op && s3.actOk && controlKey3(s3) === controlKey3(start));
|
|
@@ -98613,7 +98850,7 @@ function detectFailedSubmits(capture2) {
|
|
|
98613
98850
|
const after = screenAfter(capture2, s3.step);
|
|
98614
98851
|
if (after === void 0)
|
|
98615
98852
|
continue;
|
|
98616
|
-
if (before !== void 0 &&
|
|
98853
|
+
if (before !== void 0 && routeOf6(after.url) !== routeOf6(before.url))
|
|
98617
98854
|
continue;
|
|
98618
98855
|
const lines = linesOf(`${after.visibleText}
|
|
98619
98856
|
${s3.reply ?? ""}`);
|
|
@@ -98682,15 +98919,15 @@ function headingOf(s3) {
|
|
|
98682
98919
|
function detectUrlMismatches(capture2) {
|
|
98683
98920
|
const hits = /* @__PURE__ */ new Map();
|
|
98684
98921
|
capture2.screens.forEach((b2, i) => {
|
|
98685
|
-
const route =
|
|
98922
|
+
const route = routeOf6(b2.url);
|
|
98686
98923
|
const heading = headingOf(b2);
|
|
98687
98924
|
if (heading.length < 3)
|
|
98688
98925
|
return;
|
|
98689
98926
|
const { words: words3, known } = routeWords2(route);
|
|
98690
98927
|
if (words3.length === 0 || relates(heading, words3))
|
|
98691
98928
|
return;
|
|
98692
|
-
const other = capture2.screens.find((s3) =>
|
|
98693
|
-
const prior = capture2.screens.slice(0, i).reverse().find((s3) =>
|
|
98929
|
+
const other = capture2.screens.find((s3) => routeOf6(s3.url) !== route && normLine(headingOf(s3)) === normLine(heading) && relates(heading, routeWords2(routeOf6(s3.url)).words));
|
|
98930
|
+
const prior = capture2.screens.slice(0, i).reverse().find((s3) => routeOf6(s3.url) === route && normLine(headingOf(s3)) !== normLine(heading));
|
|
98694
98931
|
const transitioned = known && prior !== void 0 && relates(headingOf(prior), words3);
|
|
98695
98932
|
if (other === void 0 && !transitioned)
|
|
98696
98933
|
return;
|
|
@@ -98701,7 +98938,7 @@ function detectUrlMismatches(capture2) {
|
|
|
98701
98938
|
route,
|
|
98702
98939
|
known,
|
|
98703
98940
|
...transitioned ? { was: headingOf(prior) } : {},
|
|
98704
|
-
...other === void 0 ? {} : { elsewhere:
|
|
98941
|
+
...other === void 0 ? {} : { elsewhere: routeOf6(other.url) }
|
|
98705
98942
|
};
|
|
98706
98943
|
g.screens.push(b2);
|
|
98707
98944
|
hits.set(k, g);
|
|
@@ -98806,7 +99043,7 @@ function detectFriction(capture2, outcome) {
|
|
|
98806
99043
|
};
|
|
98807
99044
|
const routesAt = (nums) => {
|
|
98808
99045
|
const set2 = new Set(nums);
|
|
98809
|
-
return [.../* @__PURE__ */ new Set([...steps.filter((s3) => set2.has(s3.step)).map((s3) =>
|
|
99046
|
+
return [.../* @__PURE__ */ new Set([...steps.filter((s3) => set2.has(s3.step)).map((s3) => routeOf6(s3.url)), ...capture2.screens.filter((s3) => set2.has(s3.step)).map((s3) => routeOf6(s3.url))])];
|
|
98810
99047
|
};
|
|
98811
99048
|
const add = (kind, impact, nums, detail) => {
|
|
98812
99049
|
const sorted2 = [...new Set(nums)].filter((n3) => n3 > 0).sort((a, b2) => a - b2);
|
|
@@ -98897,7 +99134,7 @@ function detectFriction(capture2, outcome) {
|
|
|
98897
99134
|
const blocking = reqs.some((r) => WRITE_METHODS3.has(r.method.toUpperCase()) && (r.failed === true || (r.status ?? 0) >= 500));
|
|
98898
99135
|
add("error", blocking ? "blocked" : "slowed", [step], `${reqs.map((r) => `${r.endpoint} \u2192 ${r.failed === true ? "failed" : r.status}`).join(", ")} after step ${step}`);
|
|
98899
99136
|
}
|
|
98900
|
-
const seq = capture2.screens.map((s3) => ({ route:
|
|
99137
|
+
const seq = capture2.screens.map((s3) => ({ route: routeOf6(s3.url), step: s3.step }));
|
|
98901
99138
|
for (let x = 2; x < seq.length; x++) {
|
|
98902
99139
|
const here = seq[x];
|
|
98903
99140
|
if (seq[x - 1].route === here.route)
|
|
@@ -98924,7 +99161,7 @@ function detectFriction(capture2, outcome) {
|
|
|
98924
99161
|
continue;
|
|
98925
99162
|
if (between.some((r) => r.actOk && (r.op === "click" || r.op === "send" || r.op === "select")))
|
|
98926
99163
|
continue;
|
|
98927
|
-
add("abandoned", "confused", [s3.step, rest[leftAt].step], `a value typed at step ${s3.step} was left unsubmitted when the run moved to ${
|
|
99164
|
+
add("abandoned", "confused", [s3.step, rest[leftAt].step], `a value typed at step ${s3.step} was left unsubmitted when the run moved to ${routeOf6(rest[leftAt].url)}`);
|
|
98928
99165
|
}
|
|
98929
99166
|
if (outcome?.status === "incomplete" && steps.length > 0) {
|
|
98930
99167
|
const tail = steps.slice(-3).map((s3) => s3.step);
|
|
@@ -99502,8 +99739,8 @@ function boundTargetOptions(controls, point, steps, limit = MAX_CHOICE_OPTIONS -
|
|
|
99502
99739
|
const acted = steps.filter((s3) => point.steps.includes(s3.step));
|
|
99503
99740
|
const words3 = new Set(normalizeText(point.detail).split(" ").filter((w) => w.length >= 3));
|
|
99504
99741
|
const score2 = (c) => (acted.some((s3) => stepNames(s3, c)) ? 400 : 0) + (normalizeText(c.name).split(" ").some((w) => words3.has(w)) ? 60 : 0) + (c.enabled ? 10 : 0);
|
|
99505
|
-
const
|
|
99506
|
-
return controls.filter((c) =>
|
|
99742
|
+
const keep2 = new Set(controls.map((c, i) => ({ c, i, s: score2(c) })).sort((a, b2) => b2.s - a.s || a.i - b2.i).slice(0, limit).map((x) => x.c.index));
|
|
99743
|
+
return controls.filter((c) => keep2.has(c.index));
|
|
99507
99744
|
}
|
|
99508
99745
|
function frictionText(p) {
|
|
99509
99746
|
return `${p.kind} (steps ${p.steps.join(", ")}): ${p.detail}`.slice(0, 600);
|
|
@@ -99532,7 +99769,7 @@ async function analyzeClaims(request, deps) {
|
|
|
99532
99769
|
for (const raw of request.screens) {
|
|
99533
99770
|
try {
|
|
99534
99771
|
const red = redactEvidence(raw, secrets, deps.redactor);
|
|
99535
|
-
screens.push({ raw, red, route:
|
|
99772
|
+
screens.push({ raw, red, route: routeOf6(red.url) });
|
|
99536
99773
|
} catch (cause) {
|
|
99537
99774
|
return { kind: "failed", reason: `redaction failed: ${message(cause)}`, screenId: raw.screenId };
|
|
99538
99775
|
}
|
|
@@ -100022,7 +100259,7 @@ ${f2.route}
|
|
|
100022
100259
|
${element}`, "utf8").digest("hex").slice(0, 16);
|
|
100023
100260
|
}
|
|
100024
100261
|
function contractRouteTemplate(urlOrRoute) {
|
|
100025
|
-
return
|
|
100262
|
+
return routeOf6(urlOrRoute).split("/").map((seg) => /^:[A-Za-z][A-Za-z0-9_]{0,63}$/.test(seg) ? `{${seg.slice(1)}}` : seg).join("/");
|
|
100026
100263
|
}
|
|
100027
100264
|
|
|
100028
100265
|
// ../ux/dist/rubric/schema.js
|
|
@@ -100651,14 +100888,14 @@ var CREDENTIAL_KEY2 = /pass|secret|token|otp|totp|pin|cvc|cvv|card|auth|session|
|
|
|
100651
100888
|
var MAX_DIGEST_BODY = 64e3;
|
|
100652
100889
|
var CredentialBody = class extends Error {
|
|
100653
100890
|
};
|
|
100654
|
-
function
|
|
100655
|
-
if (Array.isArray(v)) return v.map(
|
|
100891
|
+
function canonical5(v) {
|
|
100892
|
+
if (Array.isArray(v)) return v.map(canonical5);
|
|
100656
100893
|
if (v !== null && typeof v === "object") {
|
|
100657
100894
|
const out = {};
|
|
100658
100895
|
for (const k of Object.keys(v).sort()) {
|
|
100659
100896
|
if (CREDENTIAL_KEY2.test(k)) throw new CredentialBody();
|
|
100660
100897
|
if (VOLATILE_KEY.test(k)) continue;
|
|
100661
|
-
out[k] =
|
|
100898
|
+
out[k] = canonical5(v[k]);
|
|
100662
100899
|
}
|
|
100663
100900
|
return out;
|
|
100664
100901
|
}
|
|
@@ -100675,7 +100912,7 @@ function payloadDigest(method, endpoint2, body, secrets) {
|
|
|
100675
100912
|
value = Object.fromEntries(new URLSearchParams(body));
|
|
100676
100913
|
}
|
|
100677
100914
|
try {
|
|
100678
|
-
const canon = JSON.stringify(
|
|
100915
|
+
const canon = JSON.stringify(canonical5(value));
|
|
100679
100916
|
return createHash12("sha256").update(`${method.toUpperCase()} ${endpoint2}
|
|
100680
100917
|
${canon}`).digest("hex").slice(0, 32);
|
|
100681
100918
|
} catch {
|
|
@@ -101364,7 +101601,7 @@ function planGuardProbes(screens, secrets, safety) {
|
|
|
101364
101601
|
const refused = [];
|
|
101365
101602
|
for (const screen of screens) {
|
|
101366
101603
|
const red = redactEvidence(screen, secrets);
|
|
101367
|
-
const route =
|
|
101604
|
+
const route = routeOf6(red.url);
|
|
101368
101605
|
screen.controls.forEach((c, i) => {
|
|
101369
101606
|
if (controlRisk(c.name, c.role)?.risk !== "destructive") return;
|
|
101370
101607
|
const rc = red.controls[i];
|
|
@@ -102123,11 +102360,11 @@ var MAX_SCAN_DEPTH = 8;
|
|
|
102123
102360
|
function scanRuns(dirs) {
|
|
102124
102361
|
const seen = /* @__PURE__ */ new Set();
|
|
102125
102362
|
const runs = [];
|
|
102126
|
-
const
|
|
102363
|
+
const walk2 = (dir, depth) => {
|
|
102127
102364
|
for (const entry of readdirSync8(dir, { withFileTypes: true }).sort((a, b2) => a.name.localeCompare(b2.name))) {
|
|
102128
102365
|
const path2 = resolve13(dir, entry.name);
|
|
102129
102366
|
if (entry.isDirectory()) {
|
|
102130
|
-
if (depth < MAX_SCAN_DEPTH && !entry.name.startsWith(".") && entry.name !== "node_modules")
|
|
102367
|
+
if (depth < MAX_SCAN_DEPTH && !entry.name.startsWith(".") && entry.name !== "node_modules") walk2(path2, depth + 1);
|
|
102131
102368
|
continue;
|
|
102132
102369
|
}
|
|
102133
102370
|
if (!entry.isFile() || !isResultName(entry.name) || seen.has(path2)) continue;
|
|
@@ -102139,7 +102376,7 @@ function scanRuns(dirs) {
|
|
|
102139
102376
|
};
|
|
102140
102377
|
for (const dir of dirs) {
|
|
102141
102378
|
if (!existsSync23(dir) || !statSync9(dir).isDirectory()) continue;
|
|
102142
|
-
|
|
102379
|
+
walk2(dir, 0);
|
|
102143
102380
|
}
|
|
102144
102381
|
return runs.sort((a, b2) => (a.startedAt ?? "").localeCompare(b2.startedAt ?? "") || a.runId.localeCompare(b2.runId));
|
|
102145
102382
|
}
|
|
@@ -104104,10 +104341,17 @@ import { existsSync as existsSync25 } from "node:fs";
|
|
|
104104
104341
|
import { copyFile as copyFile4, lstat as lstat4, mkdir as mkdir25, open as open3, readdir as readdir9, readFile as readFile24, rename as rename4, rm as rm10, writeFile as writeFile29 } from "node:fs/promises";
|
|
104105
104342
|
import { basename as basename15, dirname as dirname24, join as join56, resolve as resolve18, sep as sep7 } from "node:path";
|
|
104106
104343
|
|
|
104344
|
+
// src/catalog-contract-identity.ts
|
|
104345
|
+
var CATALOG_CONTRACT_IDENTITY = {
|
|
104346
|
+
"kind": "jevitate.catalog-contract",
|
|
104347
|
+
"version": "2.0.0",
|
|
104348
|
+
"manifestSha256": "c1292a8727d7dc0dbe1d63dc768d34b6cc94ae60fab3a67aeb0a53837e1735ad"
|
|
104349
|
+
};
|
|
104350
|
+
|
|
104107
104351
|
// src/catalog-bundle.ts
|
|
104108
104352
|
var BUNDLE_KIND = "journeeze.catalog-bundle";
|
|
104109
104353
|
var BUNDLE_VERSION = 1;
|
|
104110
|
-
var BUNDLE_MINOR =
|
|
104354
|
+
var BUNDLE_MINOR = 2;
|
|
104111
104355
|
var CatalogBundleInputError = class extends CatalogInputError {
|
|
104112
104356
|
constructor(issues) {
|
|
104113
104357
|
super(`the catalog cannot be exported as a Journeeze bundle (${issues.length} problem(s)): ${issues.join("; ")}`);
|
|
@@ -104472,7 +104716,7 @@ function bundleChecks(input2, journeys, warn) {
|
|
|
104472
104716
|
if (outcome.outcome !== "clean") why = `the run was ${outcome.outcome}, and a baseline comes only from a clean run`;
|
|
104473
104717
|
else if (journeyId === void 0 || journeyHash === void 0) why = "the item names no Journey review hash";
|
|
104474
104718
|
else if (cj === void 0) why = `Journey ${journeyId} is not in the bundle`;
|
|
104475
|
-
else if (cj.approval?.contentHash !== journeyHash || cj.
|
|
104719
|
+
else if (cj.approval?.contentHash !== journeyHash || cj.changedSinceApproval) why = `it was measured on a Journey revision (${journeyHash.slice(0, 12)}\u2026) that is not ${journeyId}'s approved one`;
|
|
104476
104720
|
else {
|
|
104477
104721
|
const b2 = bundleBaseline(item.baseline, cj.journey);
|
|
104478
104722
|
if ("drop" in b2) why = b2.drop;
|
|
@@ -104526,13 +104770,13 @@ function bundleDemos(input2, journeys, warn) {
|
|
|
104526
104770
|
warn.push(`${where}: its Journey is not in the bundle (not promoted, or left out) \u2014 the demo is left out`);
|
|
104527
104771
|
continue;
|
|
104528
104772
|
}
|
|
104529
|
-
const approved = ex.cj.approval !== void 0 && ex.cj.
|
|
104773
|
+
const approved = ex.cj.approval !== void 0 && !ex.cj.changedSinceApproval;
|
|
104530
104774
|
if (!approved) {
|
|
104531
104775
|
warn.push(`${where}: its Journey changed since it was approved \u2014 the demo is left out (approve the Journey and its demo again)`);
|
|
104532
104776
|
continue;
|
|
104533
104777
|
}
|
|
104534
|
-
if (d.renderedFrom !== ex.cj.contentHash) {
|
|
104535
|
-
warn.push(`${where}: rendered from another version of its Journey (${d.renderedFrom.slice(0, 12)}, approved ${ex.cj.contentHash.slice(0, 12)}) \u2014 the demo is left out (run \`jevitate demo approve\` again)`);
|
|
104778
|
+
if (d.renderedFrom !== ex.cj.approval.contentHash) {
|
|
104779
|
+
warn.push(`${where}: rendered from another version of its Journey (${d.renderedFrom.slice(0, 12)}, approved ${ex.cj.approval.contentHash.slice(0, 12)}) \u2014 the demo is left out (run \`jevitate demo approve\` again)`);
|
|
104536
104780
|
continue;
|
|
104537
104781
|
}
|
|
104538
104782
|
if (d.steps.length === 0 || d.steps.length > MEDIA_LIMITS.demoSteps) {
|
|
@@ -104618,8 +104862,8 @@ function bundleDemos(input2, journeys, warn) {
|
|
|
104618
104862
|
if (demos.length > LIMITS.demos) {
|
|
104619
104863
|
warn.push(`${demos.length} demos: only the first ${LIMITS.demos} are exported`);
|
|
104620
104864
|
const kept = new Set(demos.slice(0, LIMITS.demos).map((d) => `media/${d.journey}/`));
|
|
104621
|
-
const
|
|
104622
|
-
return { demos: demos.slice(0, LIMITS.demos), files: files.filter((f2) =>
|
|
104865
|
+
const keep2 = (p) => [...kept].some((k) => p.startsWith(k));
|
|
104866
|
+
return { demos: demos.slice(0, LIMITS.demos), files: files.filter((f2) => keep2(f2.path)), media: media.filter((m) => keep2(m.path)) };
|
|
104623
104867
|
}
|
|
104624
104868
|
return { demos, files, media };
|
|
104625
104869
|
}
|
|
@@ -104684,6 +104928,7 @@ function buildCatalogBundle(input2) {
|
|
|
104684
104928
|
version: BUNDLE_VERSION,
|
|
104685
104929
|
minor: BUNDLE_MINOR,
|
|
104686
104930
|
producer: { tool: "jevitate", version: input2.producer.version, ...commit === void 0 ? {} : { commit } },
|
|
104931
|
+
catalogContract: CATALOG_CONTRACT_IDENTITY,
|
|
104687
104932
|
product: { name: input2.productName },
|
|
104688
104933
|
catalog: { personas, jobs, journeys: journeys.map((j) => j.entry) },
|
|
104689
104934
|
...shown2.demos.length === 0 ? {} : { demos: shown2.demos },
|
|
@@ -105315,7 +105560,7 @@ function findingDropWarning(fingerprint2, kind, issues) {
|
|
|
105315
105560
|
function bundleFindings(src) {
|
|
105316
105561
|
const warnings = [];
|
|
105317
105562
|
const latest = /* @__PURE__ */ new Map();
|
|
105318
|
-
const
|
|
105563
|
+
const keep2 = (m) => {
|
|
105319
105564
|
if (m === null) return;
|
|
105320
105565
|
if ("drop" in m) {
|
|
105321
105566
|
warnings.push(`${m.drop} \u2014 left out of the bundle`);
|
|
@@ -105330,10 +105575,10 @@ function bundleFindings(src) {
|
|
|
105330
105575
|
if (prev === void 0 || prev.at <= m.finding.at) latest.set(m.finding.fingerprint, m.finding);
|
|
105331
105576
|
};
|
|
105332
105577
|
const byPath = new Map(src.runs.map((r) => [r.path, r]));
|
|
105333
|
-
for (const d of consolidate(src.runs))
|
|
105578
|
+
for (const d of consolidate(src.runs)) keep2(defectFinding(d, byPath));
|
|
105334
105579
|
for (const { report: report2, run } of src.uxReports) {
|
|
105335
105580
|
if (!isRecord(report2) || !Array.isArray(report2.findings)) continue;
|
|
105336
|
-
for (const f2 of report2.findings) if (isRecord(f2))
|
|
105581
|
+
for (const f2 of report2.findings) if (isRecord(f2)) keep2(uxFinding(f2, run));
|
|
105337
105582
|
}
|
|
105338
105583
|
let findings = [...latest.values()].sort((a, b2) => a.at.localeCompare(b2.at) || a.fingerprint.localeCompare(b2.fingerprint));
|
|
105339
105584
|
if (findings.length > MAX_FINDINGS) {
|
|
@@ -109647,8 +109892,8 @@ var jevReadinessAnalyzer = {
|
|
|
109647
109892
|
// src/journey-stale-api.ts
|
|
109648
109893
|
function isStepIdOnlyStale(journey, snapshot2) {
|
|
109649
109894
|
const approval = journey.metadata.approval;
|
|
109650
|
-
if (approval === void 0 ||
|
|
109651
|
-
if (snapshot2 !== null && journeyReviewHash(snapshot2)
|
|
109895
|
+
if (approval === void 0 || journeyApprovalMatches(journey)) return false;
|
|
109896
|
+
if (snapshot2 !== null && [journeyReviewHash(snapshot2), journeyLegacyReviewHash(snapshot2)].includes(approval.contentHash)) return isStepIdOnlyChange(snapshot2, journey);
|
|
109652
109897
|
return isStepIdOnlyChange(approval.contentHash, journey);
|
|
109653
109898
|
}
|
|
109654
109899
|
async function listStaleJourneys(req) {
|
|
@@ -109662,7 +109907,7 @@ async function listStaleJourneys(req) {
|
|
|
109662
109907
|
const approval = journey.metadata.approval;
|
|
109663
109908
|
if (approval === void 0) continue;
|
|
109664
109909
|
const contentHash2 = journeyReviewHash(journey);
|
|
109665
|
-
const hashStale =
|
|
109910
|
+
const hashStale = !journeyApprovalMatches(journey);
|
|
109666
109911
|
const links = journeyLinks(catalog, catalogJourney(journey));
|
|
109667
109912
|
const catalogStale = !hashStale && links.linked && [links.job?.status, links.persona?.status].includes("stale");
|
|
109668
109913
|
if (!hashStale && !catalogStale) continue;
|
|
@@ -116748,8 +116993,8 @@ function confineSweepFixture(raw, root2) {
|
|
|
116748
116993
|
realRoot = realpathSync5(root2);
|
|
116749
116994
|
} catch {
|
|
116750
116995
|
}
|
|
116751
|
-
const
|
|
116752
|
-
if (
|
|
116996
|
+
const sensitive2 = fixtureSensitiveReason(real, realRoot) ?? fixtureSensitiveReason(absolute, resolve23(root2));
|
|
116997
|
+
if (sensitive2 !== null) throw fixtureSensitive(absolute, root2, sensitive2);
|
|
116753
116998
|
return absolute;
|
|
116754
116999
|
}
|
|
116755
117000
|
function tsvTags(cell2, where, problems) {
|
|
@@ -120101,17 +120346,17 @@ function isDefectStatus(v) {
|
|
|
120101
120346
|
return typeof v === "string" && DEFECT_OUTCOME_STATUSES.includes(v);
|
|
120102
120347
|
}
|
|
120103
120348
|
function parseResultOutcome(missionOutcome, goalOutcome, defectStatus) {
|
|
120104
|
-
const
|
|
120105
|
-
if (
|
|
120349
|
+
const canonical6 = parseMissionOutcome(missionOutcome);
|
|
120350
|
+
if (canonical6 !== null) {
|
|
120106
120351
|
if (goalOutcome === void 0)
|
|
120107
|
-
return { outcome:
|
|
120352
|
+
return { outcome: canonical6 };
|
|
120108
120353
|
if (!isGoalOutcome(goalOutcome))
|
|
120109
120354
|
return null;
|
|
120110
120355
|
if (defectStatus === void 0)
|
|
120111
|
-
return foldGoalOutcome(goalOutcome) ===
|
|
120356
|
+
return foldGoalOutcome(goalOutcome) === canonical6 ? { outcome: canonical6, goalOutcome } : null;
|
|
120112
120357
|
if (!isDefectStatus(defectStatus))
|
|
120113
120358
|
return null;
|
|
120114
|
-
return goalMissionOutcome(goalOutcome, defectStatus) ===
|
|
120359
|
+
return goalMissionOutcome(goalOutcome, defectStatus) === canonical6 ? { outcome: canonical6, goalOutcome } : null;
|
|
120115
120360
|
}
|
|
120116
120361
|
if (goalOutcome === void 0 && typeof missionOutcome === "string" && Object.hasOwn(LEGACY_GOAL_OUTCOMES, missionOutcome)) {
|
|
120117
120362
|
return { outcome: LEGACY_GOAL_OUTCOMES[missionOutcome], goalOutcome: missionOutcome };
|