@alwaysmeticulous/custom-checks 2.290.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.
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const get_snapshots_from_test_run_1 = require("../get-snapshots-from-test-run");
5
+ const report_custom_check_results_1 = require("../report-custom-check-results");
6
+ const wait_for_test_run_1 = require("../wait-for-test-run");
7
+ const testRun = (id, status) => ({ id, status });
8
+ (0, vitest_1.describe)("custom checks SDK helpers", () => {
9
+ let client;
10
+ const asClient = () => client;
11
+ (0, vitest_1.beforeEach)(() => {
12
+ client = { get: vitest_1.vi.fn(), post: vitest_1.vi.fn(), put: vitest_1.vi.fn() };
13
+ });
14
+ (0, vitest_1.describe)("reportCustomCheckResults", () => {
15
+ (0, vitest_1.it)("POSTs the results to the custom-check-results endpoint", async () => {
16
+ client.post.mockResolvedValue({ data: { reported: true } });
17
+ const results = {
18
+ status: "complete",
19
+ checks: [
20
+ {
21
+ checkId: "network-requests-diff",
22
+ verdict: "warn",
23
+ summary: "1 session changed",
24
+ report: { type: "markdown", markdown: "# Report" },
25
+ },
26
+ ],
27
+ };
28
+ await (0, report_custom_check_results_1.reportCustomCheckResults)({
29
+ client: asClient(),
30
+ testRunId: "tr-1",
31
+ results,
32
+ });
33
+ (0, vitest_1.expect)(client.post).toHaveBeenCalledWith("test-runs/tr-1/custom-check-results", results);
34
+ });
35
+ (0, vitest_1.it)("reports a run-level execution error", async () => {
36
+ client.post.mockResolvedValue({ data: { reported: true } });
37
+ const results = {
38
+ status: "execution-error",
39
+ error: "boom",
40
+ };
41
+ await (0, report_custom_check_results_1.reportCustomCheckResults)({
42
+ client: asClient(),
43
+ testRunId: "tr-1",
44
+ results,
45
+ });
46
+ (0, vitest_1.expect)(client.post).toHaveBeenCalledWith("test-runs/tr-1/custom-check-results", { status: "execution-error", error: "boom" });
47
+ });
48
+ });
49
+ (0, vitest_1.describe)("getSnapshotsFromTestRun", () => {
50
+ (0, vitest_1.it)("fetches snapshots for the requested types", async () => {
51
+ const response = {
52
+ testRunId: "tr-1",
53
+ baseTestRunId: "base-1",
54
+ baseSnapshots: [],
55
+ headSnapshots: [],
56
+ };
57
+ client.get.mockResolvedValue({ data: response });
58
+ const result = await (0, get_snapshots_from_test_run_1.getSnapshotsFromTestRun)({
59
+ client: asClient(),
60
+ testRunId: "tr-1",
61
+ snapshotTypes: ["network-requests"],
62
+ });
63
+ (0, vitest_1.expect)(result).toEqual(response);
64
+ (0, vitest_1.expect)(client.get).toHaveBeenCalledWith("test-runs/tr-1/custom-check-snapshots?snapshotTypes=network-requests");
65
+ });
66
+ });
67
+ (0, vitest_1.describe)("findTestRunByIdAndWaitForCompletion", () => {
68
+ (0, vitest_1.it)("polls until the test run reaches a terminal status", async () => {
69
+ client.get
70
+ .mockResolvedValueOnce({ data: testRun("tr-1", "Running") })
71
+ .mockResolvedValueOnce({ data: testRun("tr-1", "PostProcessing") })
72
+ .mockResolvedValueOnce({ data: testRun("tr-1", "Success") });
73
+ const result = await (0, wait_for_test_run_1.findTestRunByIdAndWaitForCompletion)({
74
+ client: asClient(),
75
+ testRunId: "tr-1",
76
+ pollIntervalMs: 1,
77
+ });
78
+ (0, vitest_1.expect)(result.testRun.status).toBe("Success");
79
+ (0, vitest_1.expect)(client.get).toHaveBeenCalledTimes(3);
80
+ });
81
+ (0, vitest_1.it)("returns when a run leaves the in-progress states (e.g. Partial), not only on Success/Failure", async () => {
82
+ client.get
83
+ .mockResolvedValueOnce({ data: testRun("tr-1", "Running") })
84
+ .mockResolvedValueOnce({ data: testRun("tr-1", "Partial") });
85
+ const result = await (0, wait_for_test_run_1.findTestRunByIdAndWaitForCompletion)({
86
+ client: asClient(),
87
+ testRunId: "tr-1",
88
+ pollIntervalMs: 1,
89
+ });
90
+ (0, vitest_1.expect)(result.testRun.status).toBe("Partial");
91
+ (0, vitest_1.expect)(client.get).toHaveBeenCalledTimes(2);
92
+ });
93
+ (0, vitest_1.it)("throws once the timeout elapses", async () => {
94
+ client.get.mockResolvedValue({ data: testRun("tr-1", "Running") });
95
+ await (0, vitest_1.expect)((0, wait_for_test_run_1.findTestRunByIdAndWaitForCompletion)({
96
+ client: asClient(),
97
+ testRunId: "tr-1",
98
+ pollIntervalMs: 1,
99
+ timeoutMs: 0,
100
+ })).rejects.toThrow(/Timed out/);
101
+ });
102
+ });
103
+ (0, vitest_1.describe)("findTestRunByCommitAndWaitForCompletion", () => {
104
+ (0, vitest_1.it)("resolves the latest run for the commit, then waits for it to complete", async () => {
105
+ client.get.mockImplementation(async (url) => url === "test-runs/cache"
106
+ ? { data: testRun("tr-9", "Running") }
107
+ : { data: testRun("tr-9", "Success") });
108
+ const result = await (0, wait_for_test_run_1.findTestRunByCommitAndWaitForCompletion)({
109
+ client: asClient(),
110
+ commitSha: "abc123",
111
+ pollIntervalMs: 1,
112
+ });
113
+ (0, vitest_1.expect)(result.testRunId).toBe("tr-9");
114
+ (0, vitest_1.expect)(result.testRun.status).toBe("Success");
115
+ });
116
+ (0, vitest_1.it)("throws when no test run exists for the commit", async () => {
117
+ // The cache endpoint yields no run (getLatestTestRunResults returns null).
118
+ client.get.mockResolvedValue({ data: null });
119
+ await (0, vitest_1.expect)((0, wait_for_test_run_1.findTestRunByCommitAndWaitForCompletion)({
120
+ client: asClient(),
121
+ commitSha: "missing",
122
+ })).rejects.toThrow(/No test run found for commit missing/);
123
+ });
124
+ });
125
+ });
126
+ //# sourceMappingURL=custom-checks.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-checks.spec.js","sourceRoot":"","sources":["../../src/__tests__/custom-checks.spec.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const promises_1 = require("fs/promises");
4
+ const path_1 = require("path");
5
+ const downloading_helpers_1 = require("@alwaysmeticulous/downloading-helpers");
6
+ const vitest_1 = require("vitest");
7
+ const download_snapshots_1 = require("../download-snapshots");
8
+ vitest_1.vi.mock("@alwaysmeticulous/downloading-helpers", () => ({
9
+ downloadAndExtractFile: vitest_1.vi.fn(),
10
+ }));
11
+ const SIGNED_BASE_URL = "https://cf.example/?Signature=sig&Key-Pair-Id=k";
12
+ const keyFromUrl = (url) => new URL(url).pathname.slice(1);
13
+ (0, vitest_1.describe)("downloadAndAssembleSnapshots", () => {
14
+ (0, vitest_1.beforeEach)(() => {
15
+ vitest_1.vi.clearAllMocks();
16
+ // Emulate the real helper: extract the (zip-wrapped) file into `extractDir`
17
+ // as a single `<type>.json` containing the stored snapshot array, with the
18
+ // file's key echoed into `data` so the test can assert per-file wiring.
19
+ downloading_helpers_1.downloadAndExtractFile.mockImplementation(async (url, _zipPath, extractDir) => {
20
+ const key = keyFromUrl(url);
21
+ const fileName = key.split("/").pop().replace(/\.gz$/, "");
22
+ await (0, promises_1.mkdir)(extractDir, { recursive: true });
23
+ await (0, promises_1.writeFile)((0, path_1.join)(extractDir, fileName), JSON.stringify([
24
+ { stageDuringSession: "final-state", data: { fromKey: key } },
25
+ ]));
26
+ return [fileName];
27
+ });
28
+ });
29
+ (0, vitest_1.it)("downloads each file from the signed base URL and tags entries with type + sessionId", async () => {
30
+ const files = [
31
+ {
32
+ type: "network-requests",
33
+ sessionId: "sess-a",
34
+ key: "proj/replay-a/custom-checks-snapshots/network-requests.json.gz",
35
+ },
36
+ {
37
+ type: "network-requests",
38
+ sessionId: "sess-b",
39
+ key: "proj/replay-b/custom-checks-snapshots/network-requests.json.gz",
40
+ },
41
+ ];
42
+ const snapshots = await (0, download_snapshots_1.downloadAndAssembleSnapshots)({
43
+ signedBaseUrl: SIGNED_BASE_URL,
44
+ files,
45
+ });
46
+ (0, vitest_1.expect)(snapshots).toEqual([
47
+ {
48
+ type: "network-requests",
49
+ sessionId: "sess-a",
50
+ stageDuringSession: "final-state",
51
+ data: { fromKey: files[0].key },
52
+ },
53
+ {
54
+ type: "network-requests",
55
+ sessionId: "sess-b",
56
+ stageDuringSession: "final-state",
57
+ data: { fromKey: files[1].key },
58
+ },
59
+ ]);
60
+ // Each file is fetched by setting the path on the signed base URL while
61
+ // preserving the signature query string.
62
+ const requestedUrls = downloading_helpers_1.downloadAndExtractFile.mock.calls.map((call) => call[0]);
63
+ (0, vitest_1.expect)(requestedUrls).toEqual(vitest_1.expect.arrayContaining([
64
+ `https://cf.example/${files[0].key}?Signature=sig&Key-Pair-Id=k`,
65
+ `https://cf.example/${files[1].key}?Signature=sig&Key-Pair-Id=k`,
66
+ ]));
67
+ });
68
+ (0, vitest_1.it)("returns no snapshots and does not download when there are no files", async () => {
69
+ const snapshots = await (0, download_snapshots_1.downloadAndAssembleSnapshots)({
70
+ signedBaseUrl: SIGNED_BASE_URL,
71
+ files: [],
72
+ });
73
+ (0, vitest_1.expect)(snapshots).toEqual([]);
74
+ (0, vitest_1.expect)(downloading_helpers_1.downloadAndExtractFile).not.toHaveBeenCalled();
75
+ });
76
+ (0, vitest_1.it)("throws if a downloaded file has no .json entry", async () => {
77
+ downloading_helpers_1.downloadAndExtractFile.mockResolvedValue([]);
78
+ await (0, vitest_1.expect)((0, download_snapshots_1.downloadAndAssembleSnapshots)({
79
+ signedBaseUrl: SIGNED_BASE_URL,
80
+ files: [
81
+ {
82
+ type: "network-requests",
83
+ sessionId: "sess-a",
84
+ key: "proj/replay-a/custom-checks-snapshots/network-requests.json.gz",
85
+ },
86
+ ],
87
+ })).rejects.toThrow(/did not contain a .json entry/);
88
+ });
89
+ });
90
+ //# sourceMappingURL=download-snapshots.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-snapshots.spec.js","sourceRoot":"","sources":["../../src/__tests__/download-snapshots.spec.ts"],"names":[],"mappings":";;AAAA,0CAA+C;AAC/C,+BAA4B;AAC5B,+EAA+E;AAC/E,mCAAyE;AACzE,8DAAqE;AAErE,WAAE,CAAC,IAAI,CAAC,uCAAuC,EAAE,GAAG,EAAE,CAAC,CAAC;IACtD,sBAAsB,EAAE,WAAE,CAAC,EAAE,EAAE;CAChC,CAAC,CAAC,CAAC;AAEJ,MAAM,eAAe,GAAG,iDAAiD,CAAC;AAE1E,MAAM,UAAU,GAAG,CAAC,GAAW,EAAU,EAAE,CAAC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAE3E,IAAA,iBAAQ,EAAC,8BAA8B,EAAE,GAAG,EAAE;IAC5C,IAAA,mBAAU,EAAC,GAAG,EAAE;QACd,WAAE,CAAC,aAAa,EAAE,CAAC;QACnB,4EAA4E;QAC5E,2EAA2E;QAC3E,wEAAwE;QACvE,4CAA+B,CAAC,kBAAkB,CACjD,KAAK,EAAE,GAAW,EAAE,QAAgB,EAAE,UAAkB,EAAE,EAAE;YAC1D,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;YAC5B,MAAM,QAAQ,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAG,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YAC5D,MAAM,IAAA,gBAAK,EAAC,UAAU,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7C,MAAM,IAAA,oBAAS,EACb,IAAA,WAAI,EAAC,UAAU,EAAE,QAAQ,CAAC,EAC1B,IAAI,CAAC,SAAS,CAAC;gBACb,EAAE,kBAAkB,EAAE,aAAa,EAAE,IAAI,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE;aAC9D,CAAC,CACH,CAAC;YACF,OAAO,CAAC,QAAQ,CAAC,CAAC;QACpB,CAAC,CACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,qFAAqF,EAAE,KAAK,IAAI,EAAE;QACnG,MAAM,KAAK,GAAG;YACZ;gBACE,IAAI,EAAE,kBAAkB;gBACxB,SAAS,EAAE,QAAQ;gBACnB,GAAG,EAAE,gEAAgE;aACtE;YACD;gBACE,IAAI,EAAE,kBAAkB;gBACxB,SAAS,EAAE,QAAQ;gBACnB,GAAG,EAAE,gEAAgE;aACtE;SACF,CAAC;QAEF,MAAM,SAAS,GAAG,MAAM,IAAA,iDAA4B,EAAC;YACnD,aAAa,EAAE,eAAe;YAC9B,KAAK;SACN,CAAC,CAAC;QAEH,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,OAAO,CAAC;YACxB;gBACE,IAAI,EAAE,kBAAkB;gBACxB,SAAS,EAAE,QAAQ;gBACnB,kBAAkB,EAAE,aAAa;gBACjC,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE;aAChC;YACD;gBACE,IAAI,EAAE,kBAAkB;gBACxB,SAAS,EAAE,QAAQ;gBACnB,kBAAkB,EAAE,aAAa;gBACjC,IAAI,EAAE,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,EAAE;aAChC;SACF,CAAC,CAAC;QAEH,wEAAwE;QACxE,yCAAyC;QACzC,MAAM,aAAa,GAAI,4CAA+B,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CACnE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,CAAW,CAC5B,CAAC;QACF,IAAA,eAAM,EAAC,aAAa,CAAC,CAAC,OAAO,CAC3B,eAAM,CAAC,eAAe,CAAC;YACrB,sBAAsB,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,8BAA8B;YAChE,sBAAsB,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,8BAA8B;SACjE,CAAC,CACH,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;QAClF,MAAM,SAAS,GAAG,MAAM,IAAA,iDAA4B,EAAC;YACnD,aAAa,EAAE,eAAe;YAC9B,KAAK,EAAE,EAAE;SACV,CAAC,CAAC;QAEH,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAA,eAAM,EAAC,4CAAsB,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC7D,4CAA+B,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;QAEvD,MAAM,IAAA,eAAM,EACV,IAAA,iDAA4B,EAAC;YAC3B,aAAa,EAAE,eAAe;YAC9B,KAAK,EAAE;gBACL;oBACE,IAAI,EAAE,kBAAkB;oBACxB,SAAS,EAAE,QAAQ;oBACnB,GAAG,EAAE,gEAAgE;iBACtE;aACF;SACF,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,+BAA+B,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,20 @@
1
+ import type { Snapshot } from "@alwaysmeticulous/api";
2
+ /**
3
+ * One custom check snapshot file to download. `key` is appended to the signed
4
+ * base URL to fetch it; `type` and `sessionId` aren't stored in the file and so
5
+ * are stamped onto each parsed snapshot.
6
+ */
7
+ export interface CustomCheckSnapshotFileToDownload {
8
+ type: string;
9
+ sessionId: string;
10
+ key: string;
11
+ }
12
+ /**
13
+ * Downloads every snapshot file in parallel from the single signed base URL and
14
+ * assembles them into a flat list of {@link Snapshot}s for a custom check.
15
+ */
16
+ export declare const downloadAndAssembleSnapshots: ({ signedBaseUrl, files, concurrency, }: {
17
+ signedBaseUrl: string;
18
+ files: CustomCheckSnapshotFileToDownload[];
19
+ concurrency?: number;
20
+ }) => Promise<Snapshot[]>;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.downloadAndAssembleSnapshots = void 0;
7
+ const promises_1 = require("fs/promises");
8
+ const os_1 = require("os");
9
+ const path_1 = require("path");
10
+ const downloading_helpers_1 = require("@alwaysmeticulous/downloading-helpers");
11
+ const p_limit_1 = __importDefault(require("p-limit"));
12
+ /** How many snapshot files to download and extract in parallel. */
13
+ const DEFAULT_DOWNLOAD_CONCURRENCY = 20;
14
+ /**
15
+ * Downloads every snapshot file in parallel from the single signed base URL and
16
+ * assembles them into a flat list of {@link Snapshot}s for a custom check.
17
+ */
18
+ const downloadAndAssembleSnapshots = async ({ signedBaseUrl, files, concurrency = DEFAULT_DOWNLOAD_CONCURRENCY, }) => {
19
+ if (files.length === 0) {
20
+ return [];
21
+ }
22
+ const limit = (0, p_limit_1.default)(concurrency);
23
+ const snapshotsPerFile = await Promise.all(files.map((file) => limit(() => downloadSnapshotFile(signedBaseUrl, file))));
24
+ return snapshotsPerFile.flat();
25
+ };
26
+ exports.downloadAndAssembleSnapshots = downloadAndAssembleSnapshots;
27
+ /**
28
+ * Downloads and parses a single snapshot file, tagging each entry with the
29
+ * file's `type` and `sessionId`.
30
+ *
31
+ * Despite the `.json.gz` key the stored file is a zip archive (containing a
32
+ * single `<type>.json` entry) like the other replay artifacts, so we reuse the
33
+ * same download-and-extract helper via a throwaway temp dir.
34
+ */
35
+ const downloadSnapshotFile = async (signedBaseUrl, file) => {
36
+ const url = buildSnapshotFileUrl(signedBaseUrl, file.key);
37
+ const workDir = await (0, promises_1.mkdtemp)((0, path_1.join)((0, os_1.tmpdir)(), "met-custom-check-snapshots-"));
38
+ try {
39
+ const zipPath = (0, path_1.join)(workDir, "snapshot.json.gz");
40
+ const extractDir = (0, path_1.join)(workDir, "extracted");
41
+ const entries = await (0, downloading_helpers_1.downloadAndExtractFile)(url, zipPath, extractDir);
42
+ const jsonEntry = entries.find((entry) => entry.endsWith(".json"));
43
+ if (jsonEntry == null) {
44
+ throw new Error(`Custom check snapshot file "${file.key}" did not contain a .json entry (got: ${entries.join(", ") || "<none>"}).`);
45
+ }
46
+ const parsed = JSON.parse(await (0, promises_1.readFile)((0, path_1.join)(extractDir, jsonEntry), "utf-8"));
47
+ if (!Array.isArray(parsed)) {
48
+ throw new Error(`Expected custom check snapshot file "${file.key}" to contain a JSON array, got ${typeof parsed}.`);
49
+ }
50
+ return parsed.map((snapshot) => ({
51
+ type: file.type,
52
+ sessionId: file.sessionId,
53
+ stageDuringSession: snapshot.stageDuringSession,
54
+ data: snapshot.data,
55
+ }));
56
+ }
57
+ finally {
58
+ await (0, promises_1.rm)(workDir, { recursive: true, force: true });
59
+ }
60
+ };
61
+ /**
62
+ * Builds a file's URL by setting the path on the signed base URL. The CloudFront
63
+ * signature is in the query string, so we keep it and only replace the path.
64
+ */
65
+ const buildSnapshotFileUrl = (signedBaseUrl, key) => {
66
+ const url = new URL(signedBaseUrl);
67
+ url.pathname = `/${key}`;
68
+ return url.toString();
69
+ };
70
+ //# sourceMappingURL=download-snapshots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download-snapshots.js","sourceRoot":"","sources":["../src/download-snapshots.ts"],"names":[],"mappings":";;;;;;AAAA,0CAAoD;AACpD,2BAA4B;AAC5B,+BAA4B;AAE5B,+EAA+E;AAC/E,sDAA6B;AAE7B,mEAAmE;AACnE,MAAM,4BAA4B,GAAG,EAAE,CAAC;AAaxC;;;GAGG;AACI,MAAM,4BAA4B,GAAG,KAAK,EAAE,EACjD,aAAa,EACb,KAAK,EACL,WAAW,GAAG,4BAA4B,GAK3C,EAAuB,EAAE;IACxB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,KAAK,GAAG,IAAA,iBAAM,EAAC,WAAW,CAAC,CAAC;IAClC,MAAM,gBAAgB,GAAG,MAAM,OAAO,CAAC,GAAG,CACxC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,oBAAoB,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC,CAAC,CAC5E,CAAC;IACF,OAAO,gBAAgB,CAAC,IAAI,EAAE,CAAC;AACjC,CAAC,CAAC;AAjBW,QAAA,4BAA4B,gCAiBvC;AAEF;;;;;;;GAOG;AACH,MAAM,oBAAoB,GAAG,KAAK,EAChC,aAAqB,EACrB,IAAuC,EAClB,EAAE;IACvB,MAAM,GAAG,GAAG,oBAAoB,CAAC,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,MAAM,IAAA,kBAAO,EAAC,IAAA,WAAI,EAAC,IAAA,WAAM,GAAE,EAAE,6BAA6B,CAAC,CAAC,CAAC;IAC7E,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,IAAA,WAAI,EAAC,OAAO,EAAE,kBAAkB,CAAC,CAAC;QAClD,MAAM,UAAU,GAAG,IAAA,WAAI,EAAC,OAAO,EAAE,WAAW,CAAC,CAAC;QAC9C,MAAM,OAAO,GAAG,MAAM,IAAA,4CAAsB,EAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QAEvE,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;QACnE,IAAI,SAAS,IAAI,IAAI,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CACb,+BAA+B,IAAI,CAAC,GAAG,yCACrC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,QACxB,IAAI,CACL,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAY,IAAI,CAAC,KAAK,CAChC,MAAM,IAAA,mBAAQ,EAAC,IAAA,WAAI,EAAC,UAAU,EAAE,SAAS,CAAC,EAAE,OAAO,CAAC,CACrD,CAAC;QACF,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC3B,MAAM,IAAI,KAAK,CACb,wCAAwC,IAAI,CAAC,GAAG,kCAAkC,OAAO,MAAM,GAAG,CACnG,CAAC;QACJ,CAAC;QAED,OAAQ,MAA+D,CAAC,GAAG,CACzE,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;YACb,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,kBAAkB,EAAE,QAAQ,CAAC,kBAAkB;YAC/C,IAAI,EAAE,QAAQ,CAAC,IAAI;SACpB,CAAC,CACH,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,IAAA,aAAE,EAAC,OAAO,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACtD,CAAC;AACH,CAAC,CAAC;AAEF;;;GAGG;AACH,MAAM,oBAAoB,GAAG,CAAC,aAAqB,EAAE,GAAW,EAAU,EAAE;IAC1E,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,aAAa,CAAC,CAAC;IACnC,GAAG,CAAC,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAC;IACzB,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;AACxB,CAAC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { Snapshot } from "@alwaysmeticulous/api";
2
+ import { type MeticulousClient } from "@alwaysmeticulous/client";
3
+ export interface GetSnapshotsFromTestRunOptions {
4
+ client: MeticulousClient;
5
+ testRunId: string;
6
+ /** The custom check snapshot types to fetch, e.g. ["network-requests"]. */
7
+ snapshotTypes: string[];
8
+ }
9
+ export interface SnapshotsFromTestRun {
10
+ testRunId: string;
11
+ /** The base test run the snapshots were compared against. */
12
+ baseTestRunId: string;
13
+ baseSnapshots: Snapshot[];
14
+ headSnapshots: Snapshot[];
15
+ }
16
+ /**
17
+ * Fetches the custom check snapshots gathered during the base and head replays
18
+ * of a test run, ready to be passed to a custom check's `execute`. Throws if the
19
+ * test run has no resolvable base test run.
20
+ */
21
+ export declare const getSnapshotsFromTestRun: ({ client, testRunId, snapshotTypes, }: GetSnapshotsFromTestRunOptions) => Promise<SnapshotsFromTestRun>;
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSnapshotsFromTestRun = void 0;
4
+ const client_1 = require("@alwaysmeticulous/client");
5
+ /**
6
+ * Fetches the custom check snapshots gathered during the base and head replays
7
+ * of a test run, ready to be passed to a custom check's `execute`. Throws if the
8
+ * test run has no resolvable base test run.
9
+ */
10
+ const getSnapshotsFromTestRun = async ({ client, testRunId, snapshotTypes, }) => {
11
+ const params = new URLSearchParams();
12
+ for (const snapshotType of snapshotTypes) {
13
+ params.append("snapshotTypes", snapshotType);
14
+ }
15
+ const { data } = await client
16
+ .get(`test-runs/${testRunId}/custom-check-snapshots?${params.toString()}`)
17
+ .catch((error) => {
18
+ throw (0, client_1.maybeEnrichFetchError)(error);
19
+ });
20
+ return data;
21
+ };
22
+ exports.getSnapshotsFromTestRun = getSnapshotsFromTestRun;
23
+ //# sourceMappingURL=get-snapshots-from-test-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-snapshots-from-test-run.js","sourceRoot":"","sources":["../src/get-snapshots-from-test-run.ts"],"names":[],"mappings":";;;AACA,qDAGkC;AAiBlC;;;;GAIG;AACI,MAAM,uBAAuB,GAAG,KAAK,EAAE,EAC5C,MAAM,EACN,SAAS,EACT,aAAa,GACkB,EAAiC,EAAE;IAClE,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,KAAK,MAAM,YAAY,IAAI,aAAa,EAAE,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,YAAY,CAAC,CAAC;IAC/C,CAAC;IACD,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM;SAC1B,GAAG,CAGF,aAAa,SAAS,2BAA2B,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;SACtE,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACf,MAAM,IAAA,8BAAqB,EAAC,KAAK,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;IACL,OAAO,IAAI,CAAC;AACd,CAAC,CAAC;AAlBW,QAAA,uBAAuB,2BAkBlC"}
@@ -0,0 +1,5 @@
1
+ export { ReportCustomCheckResultsOptions, reportCustomCheckResults, } from "./report-custom-check-results";
2
+ export { WaitForTestRunCompletionOptions, WaitForTestRunResult, FindTestRunByCommitAndWaitForCompletionOptions, findTestRunByCommitAndWaitForCompletion, FindTestRunByIdAndWaitForCompletionOptions, findTestRunByIdAndWaitForCompletion, } from "./wait-for-test-run";
3
+ export { GetSnapshotsFromTestRunOptions, SnapshotsFromTestRun, getSnapshotsFromTestRun, } from "./get-snapshots-from-test-run";
4
+ export { createClient, createClientWithOAuth, MeticulousClient, } from "@alwaysmeticulous/client";
5
+ export { CustomCheckVerdict, CustomCheckOutput, CustomCheckReport, MarkdownReport, Snapshot, ReportedCustomCheckResult, ReportCustomCheckResultsRequest, CustomCheckResultsStatus, CUSTOM_CHECK_RESULTS_STATUSES, CUSTOM_CHECK_SUMMARY_MAX_LENGTH, } from "@alwaysmeticulous/api";
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CUSTOM_CHECK_SUMMARY_MAX_LENGTH = exports.CUSTOM_CHECK_RESULTS_STATUSES = exports.createClientWithOAuth = exports.createClient = exports.getSnapshotsFromTestRun = exports.findTestRunByIdAndWaitForCompletion = exports.findTestRunByCommitAndWaitForCompletion = exports.reportCustomCheckResults = void 0;
4
+ var report_custom_check_results_1 = require("./report-custom-check-results");
5
+ Object.defineProperty(exports, "reportCustomCheckResults", { enumerable: true, get: function () { return report_custom_check_results_1.reportCustomCheckResults; } });
6
+ var wait_for_test_run_1 = require("./wait-for-test-run");
7
+ Object.defineProperty(exports, "findTestRunByCommitAndWaitForCompletion", { enumerable: true, get: function () { return wait_for_test_run_1.findTestRunByCommitAndWaitForCompletion; } });
8
+ Object.defineProperty(exports, "findTestRunByIdAndWaitForCompletion", { enumerable: true, get: function () { return wait_for_test_run_1.findTestRunByIdAndWaitForCompletion; } });
9
+ var get_snapshots_from_test_run_1 = require("./get-snapshots-from-test-run");
10
+ Object.defineProperty(exports, "getSnapshotsFromTestRun", { enumerable: true, get: function () { return get_snapshots_from_test_run_1.getSnapshotsFromTestRun; } });
11
+ // Re-export the client building blocks a custom-check script needs (creating a
12
+ // client), so everything can be imported from one package.
13
+ var client_1 = require("@alwaysmeticulous/client");
14
+ Object.defineProperty(exports, "createClient", { enumerable: true, get: function () { return client_1.createClient; } });
15
+ Object.defineProperty(exports, "createClientWithOAuth", { enumerable: true, get: function () { return client_1.createClientWithOAuth; } });
16
+ // Re-export the custom-check authoring and transport types.
17
+ var api_1 = require("@alwaysmeticulous/api");
18
+ Object.defineProperty(exports, "CUSTOM_CHECK_RESULTS_STATUSES", { enumerable: true, get: function () { return api_1.CUSTOM_CHECK_RESULTS_STATUSES; } });
19
+ Object.defineProperty(exports, "CUSTOM_CHECK_SUMMARY_MAX_LENGTH", { enumerable: true, get: function () { return api_1.CUSTOM_CHECK_SUMMARY_MAX_LENGTH; } });
20
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,6EAGuC;AADrC,uIAAA,wBAAwB,OAAA;AAE1B,yDAO6B;AAH3B,4IAAA,uCAAuC,OAAA;AAEvC,wIAAA,mCAAmC,OAAA;AAErC,6EAIuC;AADrC,sIAAA,uBAAuB,OAAA;AAGzB,+EAA+E;AAC/E,2DAA2D;AAC3D,mDAIkC;AAHhC,sGAAA,YAAY,OAAA;AACZ,+GAAA,qBAAqB,OAAA;AAIvB,4DAA4D;AAC5D,6CAW+B;AAF7B,oHAAA,6BAA6B,OAAA;AAC7B,sHAAA,+BAA+B,OAAA"}
@@ -0,0 +1,19 @@
1
+ import type { ReportCustomCheckResultsRequest } from "@alwaysmeticulous/api";
2
+ import { type MeticulousClient } from "@alwaysmeticulous/client";
3
+ export interface ReportCustomCheckResultsOptions {
4
+ client: MeticulousClient;
5
+ testRunId: string;
6
+ /**
7
+ * The results to report: either every check's result (`status: "complete"`)
8
+ * or a single execution error for the run as a whole (`status:
9
+ * "execution-error"`).
10
+ */
11
+ results: ReportCustomCheckResultsRequest;
12
+ }
13
+ /**
14
+ * Reports the custom check results computed for a test run (e.g. from a job in
15
+ * the customer's CI after the run completes). May be called only once per test
16
+ * run — a second call fails. Pair with {@link findTestRunByCommitAndWaitForCompletion}
17
+ * and `getSnapshotsFromTestRun` to write a custom check from any script.
18
+ */
19
+ export declare const reportCustomCheckResults: ({ client, testRunId, results, }: ReportCustomCheckResultsOptions) => Promise<void>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.reportCustomCheckResults = void 0;
4
+ const client_1 = require("@alwaysmeticulous/client");
5
+ /**
6
+ * Reports the custom check results computed for a test run (e.g. from a job in
7
+ * the customer's CI after the run completes). May be called only once per test
8
+ * run — a second call fails. Pair with {@link findTestRunByCommitAndWaitForCompletion}
9
+ * and `getSnapshotsFromTestRun` to write a custom check from any script.
10
+ */
11
+ const reportCustomCheckResults = async ({ client, testRunId, results, }) => {
12
+ await client
13
+ .post(`test-runs/${testRunId}/custom-check-results`, results)
14
+ .catch((error) => {
15
+ throw (0, client_1.maybeEnrichFetchError)(error);
16
+ });
17
+ };
18
+ exports.reportCustomCheckResults = reportCustomCheckResults;
19
+ //# sourceMappingURL=report-custom-check-results.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report-custom-check-results.js","sourceRoot":"","sources":["../src/report-custom-check-results.ts"],"names":[],"mappings":";;;AACA,qDAGkC;AAalC;;;;;GAKG;AACI,MAAM,wBAAwB,GAAG,KAAK,EAAE,EAC7C,MAAM,EACN,SAAS,EACT,OAAO,GACyB,EAAiB,EAAE;IACnD,MAAM,MAAM;SACT,IAAI,CAAC,aAAa,SAAS,uBAAuB,EAAE,OAAO,CAAC;SAC5D,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACf,MAAM,IAAA,8BAAqB,EAAC,KAAK,CAAC,CAAC;IACrC,CAAC,CAAC,CAAC;AACP,CAAC,CAAC;AAVW,QAAA,wBAAwB,4BAUnC"}
@@ -0,0 +1,34 @@
1
+ import type { TestRun } from "@alwaysmeticulous/api";
2
+ import { type MeticulousClient } from "@alwaysmeticulous/client";
3
+ export interface WaitForTestRunCompletionOptions {
4
+ /** How often to poll the test run's status, in ms. Defaults to 10000. */
5
+ pollIntervalMs?: number;
6
+ /**
7
+ * Maximum time to wait for the test run to complete before throwing, in ms.
8
+ * Defaults to 30 minutes.
9
+ */
10
+ timeoutMs?: number;
11
+ }
12
+ export interface WaitForTestRunResult {
13
+ testRunId: string;
14
+ testRun: TestRun;
15
+ }
16
+ export type FindTestRunByCommitAndWaitForCompletionOptions = WaitForTestRunCompletionOptions & {
17
+ client: MeticulousClient;
18
+ commitSha: string;
19
+ };
20
+ /**
21
+ * Resolves the latest test run for a commit and waits for it to reach a terminal
22
+ * status, returning it. Throws if no test run exists for the commit, or if it
23
+ * does not complete within the timeout.
24
+ */
25
+ export declare const findTestRunByCommitAndWaitForCompletion: ({ client, commitSha, ...waitOptions }: FindTestRunByCommitAndWaitForCompletionOptions) => Promise<WaitForTestRunResult>;
26
+ export type FindTestRunByIdAndWaitForCompletionOptions = WaitForTestRunCompletionOptions & {
27
+ client: MeticulousClient;
28
+ testRunId: string;
29
+ };
30
+ /**
31
+ * Waits for a known test run to reach a terminal status, returning it. Throws if
32
+ * it does not complete within the timeout.
33
+ */
34
+ export declare const findTestRunByIdAndWaitForCompletion: ({ client, testRunId, pollIntervalMs, timeoutMs, }: FindTestRunByIdAndWaitForCompletionOptions) => Promise<WaitForTestRunResult>;
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.findTestRunByIdAndWaitForCompletion = exports.findTestRunByCommitAndWaitForCompletion = void 0;
4
+ const client_1 = require("@alwaysmeticulous/client");
5
+ const common_1 = require("@alwaysmeticulous/common");
6
+ // Mirrors the base-test-run polling in the SDK's `pollWhileBaseNotFound`: poll on
7
+ // a fixed interval, cap the total wait, and log progress at most once per
8
+ // PROGRESS_LOG_INTERVAL_MS so a long-running script isn't silent.
9
+ const DEFAULT_POLL_INTERVAL_MS = 10_000;
10
+ const DEFAULT_TIMEOUT_MS = 30 * 60 * 1_000;
11
+ const PROGRESS_LOG_INTERVAL_MS = 30_000;
12
+ /**
13
+ * Resolves the latest test run for a commit and waits for it to reach a terminal
14
+ * status, returning it. Throws if no test run exists for the commit, or if it
15
+ * does not complete within the timeout.
16
+ */
17
+ const findTestRunByCommitAndWaitForCompletion = async ({ client, commitSha, ...waitOptions }) => {
18
+ const latest = await (0, client_1.getLatestTestRunResults)({ client, commitSha });
19
+ if (!latest) {
20
+ throw new Error(`No test run found for commit ${commitSha}. A test run must be triggered for the commit before its custom check results can be reported.`);
21
+ }
22
+ (0, common_1.initLogger)().info(`Found test run ${latest.id} for commit ${commitSha}; waiting for it to complete...`);
23
+ return (0, exports.findTestRunByIdAndWaitForCompletion)({
24
+ client,
25
+ testRunId: latest.id,
26
+ ...waitOptions,
27
+ });
28
+ };
29
+ exports.findTestRunByCommitAndWaitForCompletion = findTestRunByCommitAndWaitForCompletion;
30
+ /**
31
+ * Waits for a known test run to reach a terminal status, returning it. Throws if
32
+ * it does not complete within the timeout.
33
+ */
34
+ const findTestRunByIdAndWaitForCompletion = async ({ client, testRunId, pollIntervalMs = DEFAULT_POLL_INTERVAL_MS, timeoutMs = DEFAULT_TIMEOUT_MS, }) => {
35
+ const logger = (0, common_1.initLogger)();
36
+ const startTime = Date.now();
37
+ let lastLoggedElapsedMs = 0;
38
+ for (;;) {
39
+ const testRun = await (0, client_1.getTestRun)({ client, testRunId });
40
+ // Done once the run leaves the in-progress states — matching how the rest of
41
+ // the SDK determines completion. This also returns for `Partial` (lazy
42
+ // session pools), which is terminal enough and would otherwise hang here
43
+ // until the timeout.
44
+ if (!client_1.IN_PROGRESS_TEST_RUN_STATUS.includes(testRun.status)) {
45
+ return { testRunId: testRun.id, testRun };
46
+ }
47
+ const elapsedMs = Date.now() - startTime;
48
+ if (elapsedMs > timeoutMs) {
49
+ throw new Error(`Timed out after ${Math.round(timeoutMs / 1000)}s waiting for test run ${testRunId} to complete (current status: ${testRun.status}).`);
50
+ }
51
+ // Log progress at most once every PROGRESS_LOG_INTERVAL_MS (mirroring the
52
+ // base-test-run wait) so a script blocked here for minutes isn't silent.
53
+ if (lastLoggedElapsedMs === 0 ||
54
+ elapsedMs - lastLoggedElapsedMs >= PROGRESS_LOG_INTERVAL_MS) {
55
+ logger.info(`Waiting for test run ${testRunId} to complete (current status: ${testRun.status}). Time elapsed: ${Math.round(elapsedMs / 1000)}s`);
56
+ lastLoggedElapsedMs = elapsedMs;
57
+ }
58
+ await sleep(pollIntervalMs);
59
+ }
60
+ };
61
+ exports.findTestRunByIdAndWaitForCompletion = findTestRunByIdAndWaitForCompletion;
62
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
63
+ //# sourceMappingURL=wait-for-test-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"wait-for-test-run.js","sourceRoot":"","sources":["../src/wait-for-test-run.ts"],"names":[],"mappings":";;;AACA,qDAKkC;AAClC,qDAAsD;AAEtD,kFAAkF;AAClF,0EAA0E;AAC1E,kEAAkE;AAClE,MAAM,wBAAwB,GAAG,MAAM,CAAC;AACxC,MAAM,kBAAkB,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;AAC3C,MAAM,wBAAwB,GAAG,MAAM,CAAC;AAuBxC;;;;GAIG;AACI,MAAM,uCAAuC,GAAG,KAAK,EAAE,EAC5D,MAAM,EACN,SAAS,EACT,GAAG,WAAW,EACiC,EAAiC,EAAE;IAClF,MAAM,MAAM,GAAG,MAAM,IAAA,gCAAuB,EAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;IACpE,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,gCAAgC,SAAS,gGAAgG,CAC1I,CAAC;IACJ,CAAC;IACD,IAAA,mBAAU,GAAE,CAAC,IAAI,CACf,kBAAkB,MAAM,CAAC,EAAE,eAAe,SAAS,iCAAiC,CACrF,CAAC;IACF,OAAO,IAAA,2CAAmC,EAAC;QACzC,MAAM;QACN,SAAS,EAAE,MAAM,CAAC,EAAE;QACpB,GAAG,WAAW;KACf,CAAC,CAAC;AACL,CAAC,CAAC;AAnBW,QAAA,uCAAuC,2CAmBlD;AAQF;;;GAGG;AACI,MAAM,mCAAmC,GAAG,KAAK,EAAE,EACxD,MAAM,EACN,SAAS,EACT,cAAc,GAAG,wBAAwB,EACzC,SAAS,GAAG,kBAAkB,GACa,EAAiC,EAAE;IAC9E,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC5B,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,mBAAmB,GAAG,CAAC,CAAC;IAE5B,SAAS,CAAC;QACR,MAAM,OAAO,GAAG,MAAM,IAAA,mBAAU,EAAC,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAC;QACxD,6EAA6E;QAC7E,uEAAuE;QACvE,yEAAyE;QACzE,qBAAqB;QACrB,IAAI,CAAC,oCAA2B,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1D,OAAO,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC;QAC5C,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;QACzC,IAAI,SAAS,GAAG,SAAS,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,mBAAmB,IAAI,CAAC,KAAK,CAC3B,SAAS,GAAG,IAAI,CACjB,0BAA0B,SAAS,iCAAiC,OAAO,CAAC,MAAM,IAAI,CACxF,CAAC;QACJ,CAAC;QAED,0EAA0E;QAC1E,yEAAyE;QACzE,IACE,mBAAmB,KAAK,CAAC;YACzB,SAAS,GAAG,mBAAmB,IAAI,wBAAwB,EAC3D,CAAC;YACD,MAAM,CAAC,IAAI,CACT,wBAAwB,SAAS,iCAAiC,OAAO,CAAC,MAAM,oBAAoB,IAAI,CAAC,KAAK,CAC5G,SAAS,GAAG,IAAI,CACjB,GAAG,CACL,CAAC;YACF,mBAAmB,GAAG,SAAS,CAAC;QAClC,CAAC;QAED,MAAM,KAAK,CAAC,cAAc,CAAC,CAAC;IAC9B,CAAC;AACH,CAAC,CAAC;AA7CW,QAAA,mCAAmC,uCA6C9C;AAEF,MAAM,KAAK,GAAG,CAAC,EAAU,EAAiB,EAAE,CAC1C,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@alwaysmeticulous/custom-checks",
3
+ "version": "2.290.0",
4
+ "description": "Helpers for writing Meticulous custom checks: fetch snapshots, wait for a test run, and report results from any script",
5
+ "license": "ISC",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist"
10
+ ],
11
+ "scripts": {
12
+ "clean": "rimraf dist tsconfig.tsbuildinfo",
13
+ "build": "tsc --build tsconfig.json",
14
+ "dev": "tsc --build tsconfig.json --watch",
15
+ "format": "prettier --write src",
16
+ "lint": "eslint \"src/**/*.{js,ts,tsx}\" --cache",
17
+ "lint:commit": "eslint --cache $(git diff --relative --name-only --diff-filter=ACMRTUXB master | grep -E \"(.js$|.ts$|.tsx$)\")",
18
+ "lint:fix": "eslint \"src/**/*.{js,ts,tsx}\" --cache --fix",
19
+ "depcheck": "depcheck --ignore-patterns=dist",
20
+ "test": "vitest run"
21
+ },
22
+ "dependencies": {
23
+ "@alwaysmeticulous/api": "workspace:*",
24
+ "@alwaysmeticulous/client": "workspace:*",
25
+ "@alwaysmeticulous/common": "workspace:*"
26
+ },
27
+ "devDependencies": {
28
+ "vitest": "catalog:"
29
+ },
30
+ "author": {
31
+ "name": "The Meticulous Team",
32
+ "email": "eng@meticulous.ai",
33
+ "url": "https://meticulous.ai"
34
+ },
35
+ "engines": {
36
+ "node": ">= 18"
37
+ },
38
+ "homepage": "https://github.com/alwaysmeticulous/meticulous-sdk",
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/alwaysmeticulous/meticulous-sdk.git",
42
+ "directory": "packages/custom-checks"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/alwaysmeticulous/meticulous-sdk/issues"
46
+ }
47
+ }