@gfdelarue/testkit 0.3.7 → 0.4.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/dist/cli/command-catalog.d.ts +0 -127
- package/dist/cli/command-catalog.js +1 -1
- package/dist/cli/command-registry.d.ts +1 -0
- package/dist/cli/command-registry.js +1 -1
- package/dist/cli/entrypoint.js +1 -1
- package/dist/cli/framework/execute.d.ts +1 -0
- package/dist/cli/framework/execute.js +6 -6
- package/dist/cli/index.js +1 -1
- package/dist/cli/version-drift.js +7 -7
- package/dist/config/authoring/index.d.ts +0 -5
- package/dist/config/loader/index.d.ts +0 -1
- package/dist/config/loader/index.js +1 -1
- package/dist/evidence/bundles.d.ts +2 -3
- package/dist/evidence/bundles.js +1 -1
- package/dist/evidence/contracts.d.ts +0 -8
- package/dist/evidence/index.d.ts +1 -1
- package/dist/evidence/index.js +1 -1
- package/dist/process/execa-bundle.json +1 -1
- package/package.json +3 -10
- package/testkit-config-schema.md +0 -23
- package/dist/app/cloud/campaigns.d.ts +0 -6
- package/dist/app/cloud/campaigns.js +0 -1
- package/dist/app/cloud/control-actions.d.ts +0 -12
- package/dist/app/cloud/control-actions.js +0 -2
- package/dist/app/cloud/control-links.d.ts +0 -5
- package/dist/app/cloud/control-links.js +0 -1
- package/dist/app/cloud/control-read.d.ts +0 -24
- package/dist/app/cloud/control-read.js +0 -1
- package/dist/app/cloud/control-resources.d.ts +0 -3
- package/dist/app/cloud/control-resources.js +0 -1
- package/dist/app/cloud/control.d.ts +0 -34
- package/dist/app/cloud/control.js +0 -5
- package/dist/app/cloud/credentials.d.ts +0 -11
- package/dist/app/cloud/credentials.js +0 -2
- package/dist/app/cloud/index.d.ts +0 -22
- package/dist/app/cloud/index.js +0 -2
- package/dist/app/cloud/login.d.ts +0 -63
- package/dist/app/cloud/login.js +0 -2
- package/dist/app/cloud/migration/archive.d.ts +0 -13
- package/dist/app/cloud/migration/archive.js +0 -1
- package/dist/app/cloud/migration/commands.d.ts +0 -8
- package/dist/app/cloud/migration/commands.js +0 -1
- package/dist/app/cloud/migration/evidence.d.ts +0 -13
- package/dist/app/cloud/migration/evidence.js +0 -2
- package/dist/app/cloud/migration/files.d.ts +0 -16
- package/dist/app/cloud/migration/files.js +0 -2
- package/dist/app/cloud/migration/index.d.ts +0 -6
- package/dist/app/cloud/migration/index.js +0 -1
- package/dist/app/cloud/migration/journal.d.ts +0 -64
- package/dist/app/cloud/migration/journal.js +0 -1
- package/dist/app/cloud/migration/mapping-dispositions.d.ts +0 -3
- package/dist/app/cloud/migration/mapping-dispositions.js +0 -1
- package/dist/app/cloud/migration/mapping.d.ts +0 -11
- package/dist/app/cloud/migration/mapping.js +0 -2
- package/dist/app/cloud/migration/model.d.ts +0 -175
- package/dist/app/cloud/migration/model.js +0 -1
- package/dist/app/cloud/migration/plan.d.ts +0 -7
- package/dist/app/cloud/migration/plan.js +0 -1
- package/dist/app/cloud/migration/progress.d.ts +0 -19
- package/dist/app/cloud/migration/progress.js +0 -1
- package/dist/app/cloud/migration/reconcile.d.ts +0 -22
- package/dist/app/cloud/migration/reconcile.js +0 -1
- package/dist/app/cloud/migration/reconciliation-spool.d.ts +0 -4
- package/dist/app/cloud/migration/reconciliation-spool.js +0 -1
- package/dist/app/cloud/migration/state.d.ts +0 -15
- package/dist/app/cloud/migration/state.js +0 -1
- package/dist/app/cloud/migration/transfer.d.ts +0 -6
- package/dist/app/cloud/migration/transfer.js +0 -1
- package/dist/cli/cloud-apply-help.d.ts +0 -1
- package/dist/cli/cloud-apply-help.js +0 -56
- package/dist/cli/cloud-selection.d.ts +0 -2
- package/dist/cli/cloud-selection.js +0 -3
- package/dist/cli/commands/cloud/apply.d.ts +0 -5
- package/dist/cli/commands/cloud/apply.js +0 -1
- package/dist/cli/commands/cloud/campaign/create.d.ts +0 -5
- package/dist/cli/commands/cloud/campaign/create.js +0 -1
- package/dist/cli/commands/cloud/download.d.ts +0 -5
- package/dist/cli/commands/cloud/download.js +0 -1
- package/dist/cli/commands/cloud/list.d.ts +0 -5
- package/dist/cli/commands/cloud/list.js +0 -1
- package/dist/cli/commands/cloud/login.d.ts +0 -13
- package/dist/cli/commands/cloud/login.js +0 -2
- package/dist/cli/commands/cloud/logout.d.ts +0 -7
- package/dist/cli/commands/cloud/logout.js +0 -1
- package/dist/cli/commands/cloud/migrate/apply.d.ts +0 -2
- package/dist/cli/commands/cloud/migrate/apply.js +0 -1
- package/dist/cli/commands/cloud/migrate/plan.d.ts +0 -7
- package/dist/cli/commands/cloud/migrate/plan.js +0 -1
- package/dist/cli/commands/cloud/migrate/progress.d.ts +0 -2
- package/dist/cli/commands/cloud/migrate/progress.js +0 -2
- package/dist/cli/commands/cloud/migrate/reconcile.d.ts +0 -2
- package/dist/cli/commands/cloud/migrate/reconcile.js +0 -1
- package/dist/cli/commands/cloud/policy/preview.d.ts +0 -5
- package/dist/cli/commands/cloud/policy/preview.js +0 -1
- package/dist/cli/commands/cloud/project/create.d.ts +0 -5
- package/dist/cli/commands/cloud/project/create.js +0 -1
- package/dist/cli/commands/cloud/publish.d.ts +0 -7
- package/dist/cli/commands/cloud/publish.js +0 -1
- package/dist/cli/commands/cloud/session/revoke.d.ts +0 -8
- package/dist/cli/commands/cloud/session/revoke.js +0 -1
- package/dist/cli/commands/cloud/show.d.ts +0 -5
- package/dist/cli/commands/cloud/show.js +0 -1
- package/dist/cli/commands/cloud/snapshot.d.ts +0 -5
- package/dist/cli/commands/cloud/snapshot.js +0 -1
- package/dist/cli/commands/cloud/status.d.ts +0 -33
- package/dist/cli/commands/cloud/status.js +0 -1
- package/dist/cloud/account.d.ts +0 -34
- package/dist/cloud/account.js +0 -1
- package/dist/cloud/archive-contract.d.ts +0 -1
- package/dist/cloud/archive-contract.js +0 -1
- package/dist/cloud/archive.d.ts +0 -12
- package/dist/cloud/archive.js +0 -1
- package/dist/cloud/campaigns.d.ts +0 -200
- package/dist/cloud/campaigns.js +0 -2
- package/dist/cloud/client.d.ts +0 -68
- package/dist/cloud/client.js +0 -4
- package/dist/cloud/download.d.ts +0 -5
- package/dist/cloud/download.js +0 -1
- package/dist/cloud/historical-contract.d.ts +0 -224
- package/dist/cloud/historical-contract.js +0 -1
- package/dist/cloud/historical-json.d.ts +0 -16
- package/dist/cloud/historical-json.js +0 -1
- package/dist/cloud/historical.d.ts +0 -69
- package/dist/cloud/historical.js +0 -1
- package/dist/cloud/pagination.d.ts +0 -5
- package/dist/cloud/pagination.js +0 -1
- package/dist/cloud/transport.d.ts +0 -18
- package/dist/cloud/transport.js +0 -1
- package/dist/config/loader/cloud.d.ts +0 -2
- package/dist/config/loader/cloud.js +0 -1
- package/docs/cloud-control.md +0 -108
- package/docs/testkit-cloud-boundary.md +0 -628
- package/node_modules/buffer-crc32/LICENSE +0 -19
- package/node_modules/buffer-crc32/README.md +0 -55
- package/node_modules/buffer-crc32/dist/index.cjs +0 -306
- package/node_modules/buffer-crc32/dist/index.d.cts +0 -35
- package/node_modules/buffer-crc32/dist/index.d.mts +0 -35
- package/node_modules/buffer-crc32/dist/index.mjs +0 -304
- package/node_modules/buffer-crc32/index.d.ts +0 -35
- package/node_modules/buffer-crc32/package.json +0 -54
- package/node_modules/yazl/LICENSE +0 -21
- package/node_modules/yazl/README.md +0 -457
- package/node_modules/yazl/index.js +0 -826
- package/node_modules/yazl/package.json +0 -34
|
@@ -1,224 +0,0 @@
|
|
|
1
|
-
import type { Actor, FindingData, Relationship } from "./campaigns.js";
|
|
2
|
-
export interface HistoricalSource {
|
|
3
|
-
kind: "run" | "case" | "finding";
|
|
4
|
-
id: string;
|
|
5
|
-
digest: string;
|
|
6
|
-
}
|
|
7
|
-
export interface HistoricalBegin {
|
|
8
|
-
schemaVersion: 1;
|
|
9
|
-
idempotencyKey: string;
|
|
10
|
-
namespace: string;
|
|
11
|
-
inventoryDigest: string;
|
|
12
|
-
inventory: HistoricalSource[];
|
|
13
|
-
}
|
|
14
|
-
export interface HistoricalImport {
|
|
15
|
-
id: string;
|
|
16
|
-
projectId: string;
|
|
17
|
-
campaignId: string;
|
|
18
|
-
namespace: string;
|
|
19
|
-
inventoryDigest: string;
|
|
20
|
-
actor: Actor;
|
|
21
|
-
createdAt: string;
|
|
22
|
-
completedAt?: string;
|
|
23
|
-
total: number;
|
|
24
|
-
}
|
|
25
|
-
export interface HistoricalArtifact {
|
|
26
|
-
path: string;
|
|
27
|
-
originalPath: string;
|
|
28
|
-
originalSha256: string;
|
|
29
|
-
originalBytes: number;
|
|
30
|
-
transformation?: string;
|
|
31
|
-
}
|
|
32
|
-
export interface HistoricalManifest {
|
|
33
|
-
algorithm: "sha256";
|
|
34
|
-
schemaVersion: 1 | 2;
|
|
35
|
-
createdAt: string;
|
|
36
|
-
runId: string;
|
|
37
|
-
digest: string;
|
|
38
|
-
files: Array<{
|
|
39
|
-
path: string;
|
|
40
|
-
bytes: number;
|
|
41
|
-
sha256: string;
|
|
42
|
-
}>;
|
|
43
|
-
redaction?: unknown;
|
|
44
|
-
storage?: unknown;
|
|
45
|
-
}
|
|
46
|
-
export interface HistoricalEvidence {
|
|
47
|
-
schemaVersion: 1;
|
|
48
|
-
source: HistoricalSource;
|
|
49
|
-
sourceRecord: unknown;
|
|
50
|
-
manifest: HistoricalManifest;
|
|
51
|
-
artifacts: HistoricalArtifact[];
|
|
52
|
-
evidenceFormat?: "zip-v1";
|
|
53
|
-
}
|
|
54
|
-
export interface HistoricalCase {
|
|
55
|
-
classification: "untriaged" | "regression" | "flaky" | "expected" | "quarantined";
|
|
56
|
-
engineId: string;
|
|
57
|
-
failureKey: string;
|
|
58
|
-
fingerprint: string;
|
|
59
|
-
firstSeenAt: string;
|
|
60
|
-
id: string;
|
|
61
|
-
lastSeenAt: string;
|
|
62
|
-
path: string;
|
|
63
|
-
profileId: string;
|
|
64
|
-
service: string;
|
|
65
|
-
state: "active" | "resolved" | "fixed_pending_verification";
|
|
66
|
-
testType: string;
|
|
67
|
-
title: string;
|
|
68
|
-
version?: number;
|
|
69
|
-
issueNumber?: number;
|
|
70
|
-
issueRepository?: string;
|
|
71
|
-
}
|
|
72
|
-
export interface HistoricalRecord {
|
|
73
|
-
source: HistoricalSource;
|
|
74
|
-
sourceRecord: unknown;
|
|
75
|
-
observedAt: string;
|
|
76
|
-
finding?: FindingData;
|
|
77
|
-
relationship?: Relationship;
|
|
78
|
-
verify?: boolean;
|
|
79
|
-
case?: HistoricalCase;
|
|
80
|
-
exception?: string;
|
|
81
|
-
}
|
|
82
|
-
export interface HistoricalBatch {
|
|
83
|
-
schemaVersion: 1;
|
|
84
|
-
idempotencyKey: string;
|
|
85
|
-
expectedVersion: number;
|
|
86
|
-
records: HistoricalRecord[];
|
|
87
|
-
}
|
|
88
|
-
export interface HistoricalOutcome extends HistoricalSource {
|
|
89
|
-
status: "pending" | "accepted" | "reused" | "rejected" | "unmapped";
|
|
90
|
-
cloudId?: string;
|
|
91
|
-
detail?: string;
|
|
92
|
-
required?: number;
|
|
93
|
-
eligible?: number;
|
|
94
|
-
evidenceAvailable?: boolean;
|
|
95
|
-
}
|
|
96
|
-
export interface HistoricalBatchResult {
|
|
97
|
-
importId: string;
|
|
98
|
-
version: number;
|
|
99
|
-
acceptedAt: string;
|
|
100
|
-
rejected: boolean;
|
|
101
|
-
items: HistoricalOutcome[];
|
|
102
|
-
}
|
|
103
|
-
export interface HistoricalPage {
|
|
104
|
-
import: HistoricalImport;
|
|
105
|
-
counts: Record<string, Record<string, number>>;
|
|
106
|
-
items: HistoricalOutcome[];
|
|
107
|
-
nextCursor?: string;
|
|
108
|
-
}
|
|
109
|
-
export interface HistoricalUploadPlan {
|
|
110
|
-
completeUrl: string;
|
|
111
|
-
publicationId: string;
|
|
112
|
-
reused: boolean;
|
|
113
|
-
status: "pending" | "accepted" | "rejected";
|
|
114
|
-
uploads: Array<{
|
|
115
|
-
bytes: number;
|
|
116
|
-
index: number;
|
|
117
|
-
path: string;
|
|
118
|
-
sha256: string;
|
|
119
|
-
url: string;
|
|
120
|
-
}>;
|
|
121
|
-
archive?: {
|
|
122
|
-
format: "zip-v1";
|
|
123
|
-
url: string;
|
|
124
|
-
maxBytes: number;
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
export interface HistoricalReceipt {
|
|
128
|
-
kind: "historical";
|
|
129
|
-
baseline: boolean;
|
|
130
|
-
baselineBranch: string;
|
|
131
|
-
branch: string;
|
|
132
|
-
commitSha: string;
|
|
133
|
-
decisionReasons: Array<{
|
|
134
|
-
ruleId: string;
|
|
135
|
-
message: string;
|
|
136
|
-
}> | null;
|
|
137
|
-
fullRun: boolean;
|
|
138
|
-
pullRequest: number | null;
|
|
139
|
-
regressions: {
|
|
140
|
-
fixed: number;
|
|
141
|
-
known: number;
|
|
142
|
-
new: number;
|
|
143
|
-
returning: number;
|
|
144
|
-
};
|
|
145
|
-
id: string;
|
|
146
|
-
projectId: string;
|
|
147
|
-
runId: string;
|
|
148
|
-
manifestDigest: string;
|
|
149
|
-
acceptedAt: string;
|
|
150
|
-
repository: string;
|
|
151
|
-
runStatus: string;
|
|
152
|
-
testkitVersion: string;
|
|
153
|
-
tokenId: string;
|
|
154
|
-
historical: {
|
|
155
|
-
importId: string;
|
|
156
|
-
sourceId: string;
|
|
157
|
-
sourceDigest: string;
|
|
158
|
-
manifestVersion: 1 | 2;
|
|
159
|
-
run: Record<string, unknown>;
|
|
160
|
-
sourceRecord: unknown;
|
|
161
|
-
artifacts: Array<HistoricalArtifact & {
|
|
162
|
-
originalVerified: boolean;
|
|
163
|
-
}>;
|
|
164
|
-
};
|
|
165
|
-
[key: string]: unknown;
|
|
166
|
-
}
|
|
167
|
-
export interface HistoricalCaseObservation {
|
|
168
|
-
importId: string;
|
|
169
|
-
sourceId: string;
|
|
170
|
-
observedAt: string;
|
|
171
|
-
importedAt: string;
|
|
172
|
-
actorTokenId: string;
|
|
173
|
-
sourceRecord: unknown;
|
|
174
|
-
case: HistoricalCase;
|
|
175
|
-
}
|
|
176
|
-
export interface HistoricalCaseView extends Omit<HistoricalCase, "state" | "version"> {
|
|
177
|
-
state: "active" | "resolved" | "historical";
|
|
178
|
-
version: number;
|
|
179
|
-
historical?: HistoricalCaseObservation;
|
|
180
|
-
}
|
|
181
|
-
export interface HistoricalCaseEvent {
|
|
182
|
-
id: string;
|
|
183
|
-
actor: string;
|
|
184
|
-
appliedAt: string;
|
|
185
|
-
commandType: string;
|
|
186
|
-
state: string;
|
|
187
|
-
classification: string;
|
|
188
|
-
issueRepository: string;
|
|
189
|
-
issueNumber: number;
|
|
190
|
-
historical?: HistoricalCaseObservation;
|
|
191
|
-
}
|
|
192
|
-
export interface HistoricalCasePage {
|
|
193
|
-
items: HistoricalCaseView[];
|
|
194
|
-
nextCursor?: string;
|
|
195
|
-
}
|
|
196
|
-
export interface HistoricalCaseHistoryPage {
|
|
197
|
-
items: HistoricalCaseEvent[];
|
|
198
|
-
nextCursor?: string;
|
|
199
|
-
}
|
|
200
|
-
export declare const HISTORICAL_BODY_LIMIT: number;
|
|
201
|
-
export declare const HISTORICAL_SOURCE_LIMIT: number;
|
|
202
|
-
export declare function validateHistoricalSourceRecord(value: unknown, expectedDigest?: string): void;
|
|
203
|
-
export declare function parseHistoricalResponse(text: string): unknown;
|
|
204
|
-
export declare function validateHistoricalBegin(input: unknown): asserts input is HistoricalBegin;
|
|
205
|
-
export declare function validateHistoricalBatch(input: unknown): asserts input is HistoricalBatch;
|
|
206
|
-
export declare function parseHistoricalImport(value: unknown, project: string, campaign: string, id?: string): HistoricalImport;
|
|
207
|
-
export declare function parseHistoricalBatchResult(value: unknown, importId: string, command: HistoricalBatch): HistoricalBatchResult;
|
|
208
|
-
export declare function parseHistoricalPage(value: unknown, project: string, campaign: string, id: string, limit?: number): HistoricalPage;
|
|
209
|
-
export declare function parseHistoricalUploadPlan(value: unknown, evidence: HistoricalEvidence): HistoricalUploadPlan;
|
|
210
|
-
export declare function validateHistoricalEvidence(input: unknown): asserts input is HistoricalEvidence;
|
|
211
|
-
export declare function parseHistoricalReceipt(value: unknown, project: string, expected?: {
|
|
212
|
-
id?: string;
|
|
213
|
-
importId?: string;
|
|
214
|
-
evidence?: HistoricalEvidence;
|
|
215
|
-
reused?: boolean;
|
|
216
|
-
}): HistoricalReceipt;
|
|
217
|
-
export declare function parseHistoricalCases(value: unknown, limit?: number): HistoricalCasePage;
|
|
218
|
-
export declare function parseHistoricalCaseHistory(value: unknown, limit?: number): HistoricalCaseHistoryPage;
|
|
219
|
-
export interface CloudProjectSnapshot {
|
|
220
|
-
projectId: string;
|
|
221
|
-
version: number;
|
|
222
|
-
cases: HistoricalCaseView[];
|
|
223
|
-
}
|
|
224
|
-
export declare function parseProjectSnapshot(value: unknown, projectId: string): CloudProjectSnapshot;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{isUnknownRecord as H}from"../kernel/unknown-values.js";import{invalidResponse as _}from"./transport.js";import{MAX_CLOUD_ARCHIVE_BYTES as q}from"./archive-contract.js";import{canonicalHistoricalJson as S,historicalDigest as V,HistoricalJsonNumber as w,parseHistoricalJson as O}from"./historical-json.js";const D=["run","case","finding"],B=["pending","accepted","reused","rejected","unmapped"];export const HISTORICAL_BODY_LIMIT=2*1024**2,HISTORICAL_SOURCE_LIMIT=128*1024;function s(i,e){if(!i)throw _(`historical ${e}`)}function c(i,e){return s(H(i),e),i}function a(i,e,t=4096,n=!1){return s(typeof i=="string"&&(n||i.trim().length>0)&&Buffer.byteLength(i)<=t&&!i.includes("\0"),e),i}function m(i,e,t=0,n=Number.MAX_SAFE_INTEGER){return s(typeof i=="number"&&Number.isSafeInteger(i)&&i>=t&&i<=n,e),i}function v(i,e,t){s(typeof i=="string"&&e.includes(i),t)}function A(i,e){s(typeof i=="boolean",e)}function I(i,e){s(typeof i=="string"&&/^[a-f0-9]{64}$/.test(i),e)}function g(i,e){const t=a(i,e,128);s(/^\d{4}-\d\d-\d\dT/.test(t)&&Number.isFinite(Date.parse(t)),e)}function y(i,e,t,n=0){return s(Array.isArray(i)&&i.length>=n&&i.length<=t,e),i}function L(i){const e=c(i,"actor");s(e.userId===void 0!=(e.tokenId===void 0),"actor identity"),a(e.userId??e.tokenId,"actor ID",128)}function k(i){const e=c(i,"source");return v(e.kind,D,"source.kind"),a(e.id,"source.id",128),I(e.digest,"source.digest"),e}function R(i){s(new Set(i.map(e=>`${e.kind}\0${e.id}`)).size===i.length,"duplicate source identity")}export function validateHistoricalSourceRecord(i,e){c(i,"source record"),s(!(i instanceof w),"source record must be an object"),s(Buffer.byteLength(S(i))<=HISTORICAL_SOURCE_LIMIT,"source record exceeds 128 KiB"),e!==void 0&&s(V(i)===e,"source record digest")}export function parseHistoricalResponse(i){function e(t,n=!1){if(n)return t;if(t instanceof w){const o=Number(t.lexeme);return s(Number.isFinite(o)&&(!Number.isInteger(o)||Number.isSafeInteger(o)),"unsafe response number"),o}return Array.isArray(t)?t.map(o=>e(o)):H(t)?Object.fromEntries(Object.entries(t).map(([o,r])=>[o,e(r,o==="sourceRecord"||o==="record")])):t}return e(O(i,{maxBytes:32*1024**2}))}export function validateHistoricalBegin(i){const e=c(i,"begin request");s(e.schemaVersion===1,"schemaVersion"),a(e.idempotencyKey,"idempotencyKey",128),a(e.namespace,"namespace",128);const t=y(e.inventory,"inventory",1e4,1).map(k);R(t),I(e.inventoryDigest,"inventoryDigest"),s(V(e.inventory)===e.inventoryDigest,"inventory digest")}export function validateHistoricalBatch(i){const e=c(i,"batch request");s(e.schemaVersion===1,"schemaVersion"),a(e.idempotencyKey,"idempotencyKey",128),m(e.expectedVersion,"expectedVersion",1);const t=y(e.records,"records",25,1).map(n=>{const o=c(n,"record"),r=k(o.source);return s(r.kind!=="run","batch source kind"),validateHistoricalSourceRecord(o.sourceRecord,r.digest),g(o.observedAt,"observedAt"),o.verify!==void 0&&A(o.verify,"verify"),o.exception!==void 0&&a(o.exception,"exception"),r});R(t)}export function parseHistoricalImport(i,e,t,n){const o=c(i,"import");return a(o.id,"import.id",128),s(n===void 0||o.id===n,"import identity"),s(o.projectId===e&&o.campaignId===t,"project/campaign identity"),a(o.namespace,"namespace",128),I(o.inventoryDigest,"inventoryDigest"),L(o.actor),g(o.createdAt,"createdAt"),o.completedAt!==void 0&&g(o.completedAt,"completedAt"),m(o.total,"total",1,1e4),o}function $(i){const e=c(i,"outcome");k(e),v(e.status,B,"outcome status"),e.cloudId!==void 0&&a(e.cloudId,"cloudId",1024),e.detail!==void 0&&a(e.detail,"detail",65536,!0);for(const t of["required","eligible"])e[t]!==void 0&&m(e[t],t);return e.evidenceAvailable!==void 0&&(s(e.kind==="run","evidence availability kind"),A(e.evidenceAvailable,"evidenceAvailable")),(e.status==="accepted"||e.status==="reused")&&a(e.cloudId,"accepted cloudId",1024),e}export function parseHistoricalBatchResult(i,e,t){const n=c(i,"batch result");s(n.importId===e,"batch import identity"),m(n.version,"version",1),g(n.acceptedAt,"acceptedAt"),A(n.rejected,"rejected");const o=y(n.items,"batch items",25,1).map($);R(o);const r=new Map(t.records.map(d=>[`${d.source.kind}\0${d.source.id}`,d.source.digest]));s(o.length===r.size,"batch outcome coverage");for(const d of o)s(r.get(`${d.kind}\0${d.id}`)===d.digest,"batch outcome identity");return s(n.rejected?n.version===t.expectedVersion:n.version===t.expectedVersion||n.version===t.expectedVersion+1,"batch version"),n.rejected||s(o.every(d=>["accepted","reused"].includes(d.status)),"accepted batch statuses"),n}function j(i,e){i.nextCursor!==void 0&&a(i.nextCursor,"nextCursor",1024);const t=y(i.items,"page items",e);return s(i.nextCursor===void 0||t.length>0,"empty page with cursor"),t}export function parseHistoricalPage(i,e,t,n,o=100){const r=c(i,"page"),d=parseHistoricalImport(r.import,e,t,n),u=j(r,o).map($);R(u);const h=c(r.counts,"counts");let p=0;for(const[f,l]of Object.entries(h)){v(f,D,"count kind");for(const[b,x]of Object.entries(c(l,"kind counts")))v(b,B,"count status"),p+=m(x,"count",0,1e4)}s(p===d.total,"count total");for(const f of u)s(m(c(h[f.kind],"item kind counts")[f.status],"item status count",1)>=u.filter(l=>l.kind===f.kind&&l.status===f.status).length,"page status counts");return r}export function parseHistoricalUploadPlan(i,e){const t=c(i,"upload plan");a(t.completeUrl,"completeUrl"),a(t.publicationId,"publicationId",128),A(t.reused,"reused"),v(t.status,["pending","accepted","rejected"],"upload status");const n=new Set;for(const o of y(t.uploads,"uploads",1e4)){const r=c(o,"upload"),d=m(r.index,"upload index",0,e.manifest.files.length-1),u=e.manifest.files[d];s(r.path===u.path&&r.bytes===u.bytes&&r.sha256===u.sha256,"upload manifest identity"),s(!n.has(u.path),"duplicate upload path"),n.add(u.path),a(r.url,"upload URL")}if(t.archive!==void 0){const o=c(t.archive,"archive");s(o.format==="zip-v1","archive format"),a(o.url,"archive URL"),m(o.maxBytes,"archive limit",1,q)}return t}function N(i,e=!1){const t=c(i,"artifact");a(t.path,"artifact path"),a(t.originalPath,"originalPath",1024),I(t.originalSha256,"originalSha256"),m(t.originalBytes,"originalBytes"),t.transformation!==void 0&&a(t.transformation,"transformation"),e&&(A(t.originalVerified,"originalVerified"),s(t.originalVerified===!t.transformation,"artifact original verification"))}export function validateHistoricalEvidence(i){const e=c(i,"evidence request");s(e.schemaVersion===1,"schemaVersion");const t=k(e.source);s(t.kind==="run","evidence source kind"),validateHistoricalSourceRecord(e.sourceRecord,t.digest);const n=c(e.manifest,"manifest");s(n.schemaVersion===1||n.schemaVersion===2,"manifest schemaVersion"),s(n.algorithm==="sha256","manifest algorithm"),I(n.digest,"manifest digest"),a(n.runId,"runId",1024),g(n.createdAt,"manifest createdAt");const o=y(n.files,"manifest files",1e4,1),r=new Set;for(const d of o){const u=c(d,"manifest file"),h=a(u.path,"manifest file path");s(!h.startsWith("/")&&!h.includes("\\")&&!h.split("/").some(p=>["",".",".."].includes(p)),"manifest relative path"),s(!r.has(h),"manifest duplicate path"),r.add(h),m(u.bytes,"manifest file bytes",0,2*1024**3),I(u.sha256,"manifest file sha256")}s(o.reduce((d,u)=>d+c(u,"manifest file").bytes,0)<=2*1024**3,"manifest total bytes"),s(V(o)===n.digest,"manifest digest identity");for(const d of y(e.artifacts,"artifacts",1e4,1)){N(d);const u=c(d,"artifact");s(r.delete(u.path),"artifact manifest identity");const h=o.map(p=>c(p,"manifest file")).find(p=>p.path===u.path);s(u.transformation||h&&h.bytes===u.originalBytes&&h.sha256===u.originalSha256,"changed artifact requires transformation")}s(r.size===0,"artifact coverage"),e.evidenceFormat!==void 0&&s(e.evidenceFormat==="zip-v1","evidence format")}export function parseHistoricalReceipt(i,e,t){const n=c(i,"receipt");s(n.kind==="historical"&&n.projectId===e,"receipt kind/project"),s(n.decision===void 0&&n.policyId===void 0&&n.policyRevision===void 0,"historical receipt release policy");for(const f of["id","runId","repository","runStatus","tokenId"])a(n[f],`receipt ${f}`,1024);a(n.testkitVersion,"receipt testkitVersion",1024,!0),g(n.acceptedAt,"receipt acceptedAt"),A(n.baseline,"receipt baseline"),A(n.fullRun,"receipt fullRun");for(const f of["baselineBranch","branch","commitSha"])a(n[f],`receipt ${f}`,4096,!0);if(n.pullRequest!==null&&m(n.pullRequest,"receipt pullRequest",1),n.decisionReasons!==null)for(const f of y(n.decisionReasons,"receipt decisionReasons",1e4)){const l=c(f,"receipt decision reason");a(l.ruleId,"receipt decision ruleId"),a(l.message,"receipt decision message")}const o=c(n.regressions,"receipt regressions");for(const f of["fixed","known","new","returning"])m(o[f],`receipt regressions ${f}`);I(n.manifestDigest,"receipt manifestDigest"),t?.id!==void 0&&s(n.id===t.id,"receipt identity");const r=c(n.historical,"receipt historical metadata");a(r.importId,"receipt importId",128),a(r.sourceId,"receipt sourceId",128),I(r.sourceDigest,"receipt sourceDigest"),s(r.manifestVersion===1||r.manifestVersion===2,"receipt manifestVersion");const d=c(r.run,"receipt run");s(d.schemaVersion===(r.manifestVersion===1?16:19),"receipt run schemaVersion");const u=c(d.run,"receipt original run");s(u.id===n.runId&&u.status===n.runStatus,"receipt original run identity"),g(u.startedAt,"receipt original run startedAt"),validateHistoricalSourceRecord(r.sourceRecord,r.sourceDigest);const h=new Set;for(const f of y(r.artifacts,"receipt artifacts",1e4,1)){N(f,!0);const l=c(f,"artifact").path;s(!h.has(l),"duplicate receipt artifact path"),h.add(l)}t?.importId!==void 0&&!t.reused&&s(r.importId===t.importId,"receipt import identity");const p=t?.evidence;if(p){s(n.runId===p.manifest.runId&&n.manifestDigest===p.manifest.digest&&(t.reused||r.sourceId===p.source.id)&&r.sourceDigest===p.source.digest&&r.manifestVersion===p.manifest.schemaVersion,"receipt evidence identity"),s(S(r.sourceRecord)===S(p.sourceRecord),"receipt source identity");const f=r.artifacts;s(f.length===p.artifacts.length,"receipt artifact coverage");for(const l of f){const b=p.artifacts.find(x=>x.path===l.path);s(b&&l.originalPath===b.originalPath&&l.originalBytes===b.originalBytes&&l.originalSha256===b.originalSha256&&l.transformation===b.transformation&&l.originalVerified===!b.transformation,"receipt artifact identity")}}return n}function T(i,e){const t=c(i,"case");for(const n of["id","engineId","failureKey","fingerprint","path","profileId","service","testType","title"])a(t[n],`case ${n}`,65536,!["id","fingerprint"].includes(n));v(t.classification,["untriaged","regression","flaky","expected","quarantined"],"case classification"),v(t.state,e?["active","resolved","fixed_pending_verification"]:["active","resolved","historical"],"case state"),g(t.firstSeenAt,"firstSeenAt"),g(t.lastSeenAt,"lastSeenAt"),(!e||t.version!==void 0)&&m(t.version,"case version"),t.issueNumber!==void 0&&m(t.issueNumber,"case issueNumber",1),t.issueRepository!==void 0&&a(t.issueRepository,"case issueRepository")}function C(i){const e=c(i,"case historical metadata");for(const t of["importId","sourceId","actorTokenId"])a(e[t],t,128);g(e.observedAt,"observedAt"),g(e.importedAt,"importedAt"),validateHistoricalSourceRecord(e.sourceRecord),T(e.case,!0),s(c(e.case,"historical case").id===e.sourceId,"historical case source identity")}export function parseHistoricalCases(i,e=100){const t=c(i,"case page"),n=new Set;for(const o of j(t,e)){T(o,!1);const r=c(o,"case"),d=r.id;s(!n.has(d),"duplicate case identity"),n.add(d),r.historical!==void 0&&C(r.historical),s(r.state!=="historical"||r.historical!==void 0,"historical case observation")}return t}export function parseHistoricalCaseHistory(i,e=100){const t=c(i,"case history page"),n=new Set;for(const o of j(t,e)){const r=c(o,"case event");for(const u of["id","actor","commandType","state","classification","issueRepository"])a(r[u],`case event ${u}`,4096,["state","classification","issueRepository"].includes(u));g(r.appliedAt,"appliedAt"),m(r.issueNumber,"issueNumber");const d=`${String(r.id)}\0${String(r.commandType)}`;s(!n.has(d),"duplicate case event"),n.add(d),r.historical!==void 0&&C(r.historical),s(r.commandType!=="case.import"||r.historical!==void 0,"case import observation")}return t}export function parseProjectSnapshot(i,e){const t=c(i,"project snapshot");return s(t.projectId===e,"project snapshot identity"),m(t.version,"project snapshot version"),parseHistoricalCases({items:t.cases},1e5),t}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/** A JSON number's spelling is part of its historical source identity. */
|
|
2
|
-
export declare class HistoricalJsonNumber {
|
|
3
|
-
readonly lexeme: string;
|
|
4
|
-
constructor(lexeme: string);
|
|
5
|
-
toJSON(): never;
|
|
6
|
-
}
|
|
7
|
-
export type HistoricalJsonValue = null | boolean | string | number | HistoricalJsonNumber | HistoricalJsonValue[] | {
|
|
8
|
-
[key: string]: HistoricalJsonValue;
|
|
9
|
-
};
|
|
10
|
-
/** Parse external JSON without rounding numbers or accepting duplicate keys. */
|
|
11
|
-
export declare function parseHistoricalJson(text: string, options?: {
|
|
12
|
-
maxBytes?: number;
|
|
13
|
-
}): HistoricalJsonValue;
|
|
14
|
-
/** Cloud's Go encoding/json canonical form, not RFC 8785. */
|
|
15
|
-
export declare function canonicalHistoricalJson(value: unknown): string;
|
|
16
|
-
export declare function historicalDigest(value: unknown): string;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{createHash as N}from"node:crypto";const p=/^-?(?:0|[1-9]\d*)(?:\.\d+)?(?:[eE][+-]?\d+)?$/,g=256,F=16*1024*1024;export class HistoricalJsonNumber{lexeme;constructor(n){if(this.lexeme=n,!p.test(n))throw new Error("Invalid historical JSON number");Object.freeze(this)}toJSON(){throw new Error("Use canonicalHistoricalJson to preserve historical numbers")}}function l(e){return e.replace(/[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?<![\uD800-\uDBFF])[\uDC00-\uDFFF]/g,"\uFFFD")}export function parseHistoricalJson(e,n={}){const t=n.maxBytes??F;if(!Number.isSafeInteger(t)||t<1||t>128*1024*1024)throw new Error("Historical JSON byte limit must be between 1 byte and 128 MiB");if(Buffer.byteLength(e)>t)throw new Error(`Historical JSON exceeds ${String(t/(1024*1024))} MiB`);let r=0;const i=()=>{throw new Error(`Invalid historical JSON at offset ${String(r)}`)},s=()=>{for(;/[\x20\t\r\n]/.test(e[r]??"");)r++},b=()=>{const u=r++;for(;r<e.length;){const c=e[r++];if(c==='"')try{return l(JSON.parse(e.slice(u,r)))}catch{return i()}c==="\\"&&r++}return i()},o=u=>{if(u>g)throw new Error("Historical JSON exceeds nesting limit");s();const c=e[r];if(c==='"')return b();if(c==="{"||c==="["){r++;const h=Object.create(null),m=[],w=c==="{"?"}":"]";if(s(),e[r]!==w)for(;;){if(c==="{"){if(s(),e[r]!=='"')return i();const O=b();if(Object.hasOwn(h,O))throw new Error("Duplicate historical JSON object key");if(s(),e[r++]!==":")return i();h[O]=o(u+1)}else m.push(o(u+1));if(s(),e[r]!==",")break;r++}return e[r++]!==w?i():c==="{"?h:m}const J=r;for(;r<e.length&&!/[\x20\t\r\n,\]}]/.test(e[r]);)r++;const f=e.slice(J,r);return f==="true"?!0:f==="false"?!1:f==="null"?null:p.test(f)?new HistoricalJsonNumber(f):i()},a=o(0);return s(),r!==e.length?i():a}function S(e){return JSON.stringify(l(e)).replace(/[<>&\u2028\u2029]/g,n=>`\\u${n.charCodeAt(0).toString(16).padStart(4,"0")}`)}export function canonicalHistoricalJson(e){const n=(t,r)=>{if(r>g)throw new Error("Historical JSON exceeds nesting limit");if(t instanceof HistoricalJsonNumber)return t.lexeme;if(t===null)return"null";if(typeof t=="string")return S(t);if(typeof t=="boolean")return String(t);if(typeof t=="number"&&Number.isFinite(t))return JSON.stringify(t);if(Array.isArray(t))return`[${Array.from(t,i=>n(i,r+1)).join(",")}]`;if(typeof t=="object"&&(Object.getPrototypeOf(t)===Object.prototype||Object.getPrototypeOf(t)===null)){const i=t,s=Object.keys(i).map(o=>({key:o,normalized:l(o),bytes:Buffer.from(l(o))}));return s.sort((o,a)=>Buffer.compare(o.bytes,a.bytes)),`{${s.map(({key:o,normalized:a},u)=>{if(u>0&&a===s[u-1].normalized)throw new Error("Duplicate historical JSON object key");return`${S(a)}:${n(i[o],r+1)}`}).join(",")}}`}throw new Error("Historical JSON requires finite numbers and JSON values")};return n(e,0)}export function historicalDigest(e){return N("sha256").update(canonicalHistoricalJson(e)).digest("hex")}
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import { CloudClient, type CloudClientOptions } from "./client.js";
|
|
2
|
-
import { type HistoricalBatch, type HistoricalBegin, type HistoricalEvidence } from "./historical-contract.js";
|
|
3
|
-
export interface HistoricalClientOptions extends CloudClientOptions {
|
|
4
|
-
campaignId: string;
|
|
5
|
-
}
|
|
6
|
-
export declare class HistoricalClient {
|
|
7
|
-
readonly origin: URL;
|
|
8
|
-
readonly projectId: string;
|
|
9
|
-
readonly campaignId: string;
|
|
10
|
-
readonly snapshot: CloudClient["snapshot"];
|
|
11
|
-
private readonly token;
|
|
12
|
-
private readonly ordinary;
|
|
13
|
-
private readonly staged;
|
|
14
|
-
constructor(options: Readonly<HistoricalClientOptions>);
|
|
15
|
-
private get base();
|
|
16
|
-
private importPath;
|
|
17
|
-
begin(command: HistoricalBegin): Promise<import("./historical-contract.js").HistoricalImport>;
|
|
18
|
-
stage(importId: string, evidence: HistoricalEvidence): Promise<import("./historical-contract.js").HistoricalUploadPlan>;
|
|
19
|
-
upload(target: string, filePath: string, bytes: number, contentType: "application/octet-stream" | "application/zip", onProgress?: (bytes: number) => void): Promise<void>;
|
|
20
|
-
completeEvidence(returnedURL: string): Promise<import("./historical-contract.js").HistoricalReceipt>;
|
|
21
|
-
batch(importId: string, command: HistoricalBatch): Promise<import("./historical-contract.js").HistoricalBatchResult>;
|
|
22
|
-
page(importId: string, cursor?: string, limit?: number): Promise<import("./historical-contract.js").HistoricalPage>;
|
|
23
|
-
complete(importId: string): Promise<import("./historical-contract.js").HistoricalPage>;
|
|
24
|
-
repository(): Promise<{
|
|
25
|
-
projectId: string;
|
|
26
|
-
host: string;
|
|
27
|
-
owner: string;
|
|
28
|
-
name: string;
|
|
29
|
-
}>;
|
|
30
|
-
cases(cursor?: string, limit?: number): Promise<import("./historical-contract.js").HistoricalCasePage>;
|
|
31
|
-
caseHistory(caseId: string, cursor?: string, limit?: number): Promise<import("./historical-contract.js").HistoricalCaseHistoryPage>;
|
|
32
|
-
receipt(id: string): Promise<import("./historical-contract.js").HistoricalReceipt>;
|
|
33
|
-
currentReceipt(id: string): Promise<{
|
|
34
|
-
acceptedAt: string;
|
|
35
|
-
commitSha: string;
|
|
36
|
-
decision: "hold" | "ineligible" | "ship";
|
|
37
|
-
decisionReasons: {
|
|
38
|
-
ruleId: string;
|
|
39
|
-
message: string;
|
|
40
|
-
}[];
|
|
41
|
-
fullRun: boolean;
|
|
42
|
-
id: string;
|
|
43
|
-
manifestDigest: string;
|
|
44
|
-
policyId: string;
|
|
45
|
-
policyRevision: number;
|
|
46
|
-
projectId: string;
|
|
47
|
-
regressions: {
|
|
48
|
-
fixed: number;
|
|
49
|
-
known: number;
|
|
50
|
-
new: number;
|
|
51
|
-
returning: number;
|
|
52
|
-
};
|
|
53
|
-
repository: string;
|
|
54
|
-
runId: string;
|
|
55
|
-
runStatus: string;
|
|
56
|
-
testkitVersion: string;
|
|
57
|
-
}>;
|
|
58
|
-
campaign(): Promise<import("./campaigns.js").Campaign>;
|
|
59
|
-
finding(id: string): Promise<import("./campaigns.js").FindingDetail>;
|
|
60
|
-
artifactURL(receiptId: string, path: string): string;
|
|
61
|
-
checkArtifactAvailable(receiptId: string, artifactPath: string): Promise<void>;
|
|
62
|
-
download(target: string, filePath: string, expected: {
|
|
63
|
-
bytes: number;
|
|
64
|
-
sha256: string;
|
|
65
|
-
}): Promise<void>;
|
|
66
|
-
private target;
|
|
67
|
-
private json;
|
|
68
|
-
private read;
|
|
69
|
-
}
|
package/dist/cloud/historical.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import j from"node:fs";import{stat as I}from"node:fs/promises";import{downloadVerifiedArtifact as C}from"./download.js";import{CloudClient as R,CloudResponseError as $}from"./client.js";import{request as p,invalidResponse as n}from"./transport.js";import{attachErrorDiagnostics as b}from"../kernel/error-serialization.js";import{isUnknownRecord as u}from"../kernel/unknown-values.js";import{canonicalHistoricalJson as v}from"./historical-json.js";import{HISTORICAL_BODY_LIMIT as H,parseHistoricalBatchResult as T,parseHistoricalCaseHistory as E,parseHistoricalCases as U,parseHistoricalImport as S,parseHistoricalPage as m,parseHistoricalReceipt as g,parseHistoricalResponse as w,parseHistoricalUploadPlan as P}from"./historical-contract.js";export class HistoricalClient{origin;projectId;campaignId;snapshot;token;ordinary;staged=new Map;constructor(t){const e=new R(t,{decodeJSON:w,maxResponseBytes:33554432});if(this.ordinary=e,this.snapshot=e.snapshot.bind(e),!/^[A-Za-z0-9_-]{1,128}$/.test(t.projectId)||!/^[A-Za-z0-9_-]{1,128}$/.test(t.campaignId))throw new Error("Historical Cloud project and campaign IDs must be valid Cloud identifiers");this.origin=e.origin,this.projectId=e.projectId,this.campaignId=t.campaignId,this.token=t.token}get base(){return`/api/v1/projects/${encodeURIComponent(this.projectId)}/campaigns/${encodeURIComponent(this.campaignId)}/imports`}importPath(t){if(!t.trim())throw new Error("Historical import ID is required");return`${this.base}/${encodeURIComponent(t)}`}async begin(t){const e=S(await this.json("POST",this.base,t),this.projectId,this.campaignId);if(e.namespace!==t.namespace||e.inventoryDigest!==t.inventoryDigest||e.total!==t.inventory.length)throw n("historical begin inventory identity");return e}async stage(t,e){const r=P(await this.json("POST",`${this.importPath(t)}/evidence`,e),e),i=this.target(r.completeUrl).href;for(const o of r.uploads)this.target(o.url);return r.archive&&this.target(r.archive.url),this.staged.set(i,{importId:t,evidence:e,reused:r.reused}),r}async upload(t,e,r,i,o){const c=this.target(t);if(!Number.isSafeInteger(r)||r<0)throw new Error("Historical upload byte length must be a nonnegative safe integer");const a=await I(e);if(!a.isFile()||a.size!==r)throw new Error("Historical upload file size does not match the manifest");const h=j.createReadStream(e),y=new Promise(d=>h.once("close",d));try{const d=await p({body:h,onUploadChunk:o,headers:{Authorization:`Bearer ${this.token}`,"Content-Length":String(r),"Content-Type":i},method:"PUT",timeoutMs:6e5,url:c});if(d.status<200||d.status>=300)throw f(d)}finally{h.destroy(),await y}}async completeEvidence(t){const e=this.target(t);return g(await this.json("POST",e.href),this.projectId,this.staged.get(e.href))}async batch(t,e){const{value:r,status:i}=await this.read("POST",`${this.importPath(t)}/batches`,e,!0),o=T(r,t,e);if(i===422!==o.rejected)throw n("historical batch HTTP status");return o}async page(t,e,r=100){const i=m(await this.json("GET",`${this.importPath(t)}${l(e,r)}`),this.projectId,this.campaignId,t,r);if(e!==void 0&&i.nextCursor===e)throw n("historical repeated cursor");return i}async complete(t){const e=m(await this.json("POST",`${this.importPath(t)}/complete`),this.projectId,this.campaignId,t);if(!e.import.completedAt||Object.values(e.counts).some(r=>Object.entries(r).some(([i,o])=>!["accepted","reused"].includes(i)&&o>0)))throw n("historical completion state");return e}async repository(){const t=await this.json("GET",`/api/v1/projects/${encodeURIComponent(this.projectId)}/repository`);if(!u(t)||t.projectId!==this.projectId||Object.keys(t).length!==4||Object.keys(t).some(r=>!["projectId","host","owner","name"].includes(r)))throw n("project repository identity");const e=r=>{const i=t[r];if(typeof i!="string"||!i.trim()||i.includes("\0")||Buffer.byteLength(i)>256)throw n(`project repository ${r}`);return i};return{projectId:this.projectId,host:e("host"),owner:e("owner"),name:e("name")}}async cases(t,e=100){const r=U(await this.json("GET",`/api/v1/projects/${encodeURIComponent(this.projectId)}/cases${l(t,e)}`),e);if(t!==void 0&&r.nextCursor===t)throw n("historical repeated cursor");return r}async caseHistory(t,e,r=100){const i=E(await this.json("GET",`/api/v1/projects/${encodeURIComponent(this.projectId)}/cases/${encodeURIComponent(t)}/history${l(e,r)}`),r);if(e!==void 0&&i.nextCursor===e)throw n("historical repeated cursor");return i}async receipt(t){const e=await this.json("GET",`/api/v1/receipts/${encodeURIComponent(t)}`);if(!u(e))throw n("historical receipt view");return g(e.receipt,this.projectId,{id:t})}async currentReceipt(t){return this.ordinary.receipt(t)}campaign(){return this.ordinary.campaign(this.campaignId)}finding(t){return this.ordinary.finding(this.campaignId,t)}artifactURL(t,e){if(e.split("/").some(r=>!r||r==="."||r==="..")||e.includes("\\"))throw new Error("Historical artifact path must be a normalized relative path");return`/api/v1/receipts/${encodeURIComponent(t)}/artifacts/${e.split("/").map(encodeURIComponent).join("/")}`}async checkArtifactAvailable(t,e){const r=await p({body:null,headers:{Authorization:`Bearer ${this.token}`,"Accept-Encoding":"identity"},method:"HEAD",timeoutMs:3e4,url:this.target(this.artifactURL(t,e))});if(r.status<200||r.status>=300)throw f(r)}async download(t,e,r){const i=this.target(t);await C(i,this.token,e,r)}target(t){if(!t)throw new Error("Testkit Cloud returned an empty historical URL");const e=new URL(t,this.origin);if(e.origin!==this.origin.origin||e.username||e.password||e.hash)throw new Error("Testkit Cloud returned a historical URL outside its configured origin or containing credentials/fragment");return e}async json(t,e,r){return(await this.read(t,e,r)).value}async read(t,e,r,i=!1){const o=this.target(e),c=r===void 0?null:Buffer.from(v(r));if(c&&c.length>H)throw new Error("Historical Cloud request exceeds 2 MiB; split its records or reduce the source inventory");const a=await p({body:c,headers:{Accept:"application/json",Authorization:`Bearer ${this.token}`,...c?{"Content-Length":String(c.length),"Content-Type":"application/json"}:{}},method:t,timeoutMs:3e4,url:o,maxResponseBytes:32*1024**2});if((a.status<200||a.status>=300)&&!(i&&a.status===422))throw f(a);try{return{value:w(new TextDecoder("utf-8",{fatal:!0}).decode(a.body)),status:a.status}}catch(h){throw n(`historical JSON response: ${h instanceof Error?h.message:String(h)}`)}}}function l(s,t){if(!Number.isSafeInteger(t)||t<1||t>100||s!==void 0&&(!s||Buffer.byteLength(s)>1024))throw new Error("Historical pagination requires limit 1\u2013100 and a cursor of at most 1024 bytes");const e=new URLSearchParams({limit:String(t)});return s!==void 0&&e.set("cursor",s),`?${e}`}function f(s){let t=s.body.toString("utf8").trim(),e=null,r={};try{const i=JSON.parse(t);u(i)&&(r=i,typeof i.detail=="string"&&(t=i.detail),typeof i.code=="string"&&(e=i.code))}catch{}return b(new $(s.status,t||`Testkit Cloud returned HTTP ${String(s.status)}`,e),{source:"cloud",...r})}
|
package/dist/cloud/pagination.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export async function walkCloudPages(t,s){const r=new Set;let n;for(let o=0;o<1e4;o++){const e=await t(n);if(e.nextCursor!==void 0&&(e.items.length===0||r.has(e.nextCursor)))throw new Error("Cloud pagination repeated a cursor or returned an empty continuing page");if(s(e.items),e.nextCursor===void 0)return;r.add(e.nextCursor),n=e.nextCursor}throw new Error("Cloud pagination exceeded 10,000 pages; the read is inconclusive")}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs";
|
|
2
|
-
export interface ResponseDocument {
|
|
3
|
-
body: Buffer;
|
|
4
|
-
status: number;
|
|
5
|
-
}
|
|
6
|
-
interface RequestInput {
|
|
7
|
-
body: Buffer | fs.ReadStream | null;
|
|
8
|
-
headers: Readonly<Record<string, string>>;
|
|
9
|
-
method: string;
|
|
10
|
-
timeoutMs: number;
|
|
11
|
-
url: URL;
|
|
12
|
-
onUploadChunk?: (bytes: number) => void;
|
|
13
|
-
onResponseChunk?: (chunk: Uint8Array) => Promise<void>;
|
|
14
|
-
maxResponseBytes?: number;
|
|
15
|
-
}
|
|
16
|
-
export declare function request(input: Readonly<RequestInput>): Promise<ResponseDocument>;
|
|
17
|
-
export declare function invalidResponse(field: string): Error;
|
|
18
|
-
export {};
|
package/dist/cloud/transport.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import i from"node:fs";import{Agent as f,fetch as h}from"undici";import{attachErrorDiagnostics as l}from"../kernel/error-serialization.js";import{isUnknownRecord as d}from"../kernel/unknown-values.js";const m=new f({connectTimeout:3e4,headersTimeout:10*6e4,bodyTimeout:10*6e4});export async function request(e){const s=new AbortController,a=setTimeout(()=>{s.abort(new Error("Testkit Cloud request timed out"))},e.timeoutMs);if(a.unref(),e.body instanceof i.ReadStream){const o=e.body,t=n=>{s.abort(n)};o.once("error",t),o.once("close",()=>o.off("error",t))}let r;try{const o=await h(e.url,{body:e.body instanceof i.ReadStream&&e.onUploadChunk?b(e.body,e.onUploadChunk):e.body,headers:e.headers,method:e.method,redirect:"manual",signal:s.signal,dispatcher:m,duplex:"half"});r=o.body?.getReader();const t=[];let n=0;for(;r;){const{done:u,value:c}=await r.read();if(u)break;if(e.onResponseChunk&&o.status>=200&&o.status<300){await e.onResponseChunk(c);continue}if(n+=c.byteLength,n>(e.maxResponseBytes??4*1024*1024))throw invalidResponse("response: exceeded byte limit");t.push(c)}return{body:Buffer.concat(t),status:o.status}}catch(o){const t=d(o)&&d(o.cause)?o.cause:o,n=d(t)&&typeof t.code=="string"?t.code:void 0;throw n==="invalid_cloud_response"?o:l(Object.assign(new Error(`Testkit Cloud transport failed: ${t instanceof Error?t.message:String(t)}`,{cause:o}),{code:"cloud_transport_error"}),{source:"transport",...n?{transportCode:n}:{}})}finally{clearTimeout(a),await r?.cancel().catch(()=>{}),r?.releaseLock()}}export function invalidResponse(e){return l(Object.assign(new Error(`Testkit Cloud returned an invalid ${e}`),{code:"invalid_cloud_response"}),{source:"cloud"})}async function*b(e,s){for await(const a of e){const r=a;s(r.byteLength),yield r}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import{isUnknownRecord as e}from"../../kernel/unknown-values.js";import{attachErrorDiagnostics as t}from"../../kernel/error-serialization.js";export function minimumConfirmingRuns(o){let i=o;for(const r of["cloud","verification","minimumConfirmingRuns"]){if(!e(i))throw n();if(i=i[r],i===void 0)return 2}if(typeof i!="number"||!Number.isSafeInteger(i)||i<1||i>64)throw n();return i}function n(){return t(Object.assign(new Error("cloud.verification.minimumConfirmingRuns must be an integer from 1 through 64"),{code:"invalid_cloud_verification_config"}),{source:"local",field:"cloud.verification.minimumConfirmingRuns"})}
|
package/docs/cloud-control.md
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# Cloud command reference
|
|
2
|
-
|
|
3
|
-
The `testkit cloud` commands are deprecated. See the
|
|
4
|
-
[Cloud boundary](testkit-cloud-boundary.md) for the direction.
|
|
5
|
-
|
|
6
|
-
`testkit cloud apply --help` prints the account-command envelope, every supported
|
|
7
|
-
operation, its scope IDs, and its input fields. This reference ships with Testkit.
|
|
8
|
-
|
|
9
|
-
## Account commands
|
|
10
|
-
|
|
11
|
-
`testkit cloud apply --file command.json --json` sends one account command.
|
|
12
|
-
Every command requires `schemaVersion: 1`, an `idempotencyKey`, an `operation`,
|
|
13
|
-
and an `input` object. Add the scope IDs shown below. Omit unrelated scope IDs.
|
|
14
|
-
The key has 1 to 128 characters without surrounding whitespace. The server
|
|
15
|
-
limits files to 256 KiB and rejects unknown input fields.
|
|
16
|
-
|
|
17
|
-
```json
|
|
18
|
-
{
|
|
19
|
-
"schemaVersion": 1,
|
|
20
|
-
"idempotencyKey": "retention-30",
|
|
21
|
-
"operation": "project.retention",
|
|
22
|
-
"projectId": "prj_example",
|
|
23
|
-
"input": { "days": 30 }
|
|
24
|
-
}
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
| Operation | Scope fields | Input fields |
|
|
28
|
-
| --------------------- | ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |
|
|
29
|
-
| `account.theme` | None | `theme`: `system`, `light`, or `dark` |
|
|
30
|
-
| `organisation.update` | `organisationId` | `name` |
|
|
31
|
-
| `invitation.create` | `organisationId` | `email`, `role`: `owner`, `admin`, `member`, or `viewer` |
|
|
32
|
-
| `invitation.revoke` | `organisationId`, `resourceId` | `{}` |
|
|
33
|
-
| `provider.create` | `organisationId` | `provider`: `gitea`, `host`: HTTPS origin, `displayName`, `oauthClientId`, `oauthClientSecret`; optional `botToken`, `caBundlePEM` |
|
|
34
|
-
| `provider.delete` | `organisationId`, `resourceId` | `{}` |
|
|
35
|
-
| `project.create` | `organisationId` | `name`, `provider`: `github` or `gitea`, `host`, `repository`: owner/repo; optional `publishDecisions`, default false |
|
|
36
|
-
| `project.publishing` | `projectId` | `enabled`: boolean |
|
|
37
|
-
| `project.baseline` | `projectId` | `branch`; an empty string resets the provider default |
|
|
38
|
-
| `project.retention` | `projectId` | `days`: 1 to 3650 |
|
|
39
|
-
| `project.policy` | `projectId` | `policyId`: string, or null to inherit the organisation default |
|
|
40
|
-
| `policy.create` | `organisationId` | `name`; optional `rules` or `sourcePolicyId`, mutually exclusive |
|
|
41
|
-
| `policy.revise` | `organisationId`, `resourceId` | Complete `rules` array |
|
|
42
|
-
| `policy.default` | `organisationId`, `resourceId` | `{}` |
|
|
43
|
-
| `notification.create` | `projectId` | `kind`: `slack` or `email`, `displayName`, `target`, `events` |
|
|
44
|
-
| `notification.update` | `projectId`, `resourceId` | `enabled`: boolean, `events` |
|
|
45
|
-
| `notification.delete` | `projectId`, `resourceId` | `{}` |
|
|
46
|
-
| `notification.test` | `projectId`, `resourceId` | `{}`; sends a real test notification |
|
|
47
|
-
| `token.create` | `projectId` | `name`; also requires `--secret-output NEW_FILE` |
|
|
48
|
-
| `token.revoke` | `projectId`, `resourceId` | `{}` |
|
|
49
|
-
|
|
50
|
-
Notification events are `baseline_hold`, `case_opened`, and `case_resolved`.
|
|
51
|
-
At least one event is required. An email target is an email address. A Slack
|
|
52
|
-
target is an HTTPS `hooks.slack.com` webhook URL.
|
|
53
|
-
|
|
54
|
-
Policy rules have `id`, `kind`, `mode`, `branches`, and typed `params`.
|
|
55
|
-
`cloud show policy ID --org ORG_ID` returns existing rules. Revisions replace the
|
|
56
|
-
whole rules array. `cloud policy preview ID --org ORG_ID --file preview.json`
|
|
57
|
-
accepts `{"receiptId":"rcpt_example","rules":[...]}` without saving a revision.
|
|
58
|
-
|
|
59
|
-
Secrets belong in private input files. `token.create` reserves a new private
|
|
60
|
-
output file before issuing the token and omits its value from terminal output.
|
|
61
|
-
If saving fails after issuance, the same command and key recover the original
|
|
62
|
-
token with a new `--secret-output` path.
|
|
63
|
-
|
|
64
|
-
Reusing a key with a changed command conflicts. For `outcome_uncertain`, inspect
|
|
65
|
-
the resource and audit history before choosing a new key. Provider publishing is
|
|
66
|
-
opt-in and can require browser consent. CI returns recovery details instead of
|
|
67
|
-
waiting for consent or a processing connection.
|
|
68
|
-
|
|
69
|
-
## Discovery and session management
|
|
70
|
-
|
|
71
|
-
`cloud list --help` and `cloud show --help` list resources and filters.
|
|
72
|
-
Project discovery matches Git origin to accessible Cloud projects when no
|
|
73
|
-
project is selected. A non-JSON TTY can prompt when several choices remain.
|
|
74
|
-
CI and JSON commands require explicit selectors for ambiguous choices.
|
|
75
|
-
|
|
76
|
-
Account project and campaign reads include `dashboardUrl`. Creation results also
|
|
77
|
-
include it when the optional directory lookup succeeds; a failed lookup does not
|
|
78
|
-
turn a completed creation into a failed command. Automation campaign commands
|
|
79
|
-
omit this field because that credential cannot read the account directory.
|
|
80
|
-
Receipt publication includes its stable dashboard link for both account and
|
|
81
|
-
automation credentials.
|
|
82
|
-
|
|
83
|
-
`cloud list sessions` lists your CLI sessions by ID, agent, creation time, and
|
|
84
|
-
expiry. `cloud session revoke SESSION_ID` revokes one session. These commands do
|
|
85
|
-
not refresh the current session's expiry. The agent label is client-supplied
|
|
86
|
-
context, not proof of identity. `cloud logout` revokes the saved session and
|
|
87
|
-
removes its local credential. Project tokens use `cloud list tokens` and
|
|
88
|
-
`token.revoke` instead.
|
|
89
|
-
|
|
90
|
-
## Case and campaign files
|
|
91
|
-
|
|
92
|
-
Case envelopes contain `schemaVersion: 1`, `projectId`, `idempotencyKey`,
|
|
93
|
-
`expectedVersion`, and `commands`. `cloud snapshot` supplies the project version.
|
|
94
|
-
Each command has `caseId` and one of these types:
|
|
95
|
-
|
|
96
|
-
| Type | Other required fields |
|
|
97
|
-
| ----------------- | ---------------------------------------------------------------------------------- |
|
|
98
|
-
| `case.set_state` | `state`: `active` or `resolved` |
|
|
99
|
-
| `case.classify` | `classification`: `untriaged`, `regression`, `flaky`, `expected`, or `quarantined` |
|
|
100
|
-
| `case.link_issue` | `issueRepository`, positive `issueNumber` |
|
|
101
|
-
|
|
102
|
-
The [Cloud boundary](testkit-cloud-boundary.md#report-an-externally-executed-campaign)
|
|
103
|
-
documents campaign report fields. `cloud snapshot --campaign ID` supplies the
|
|
104
|
-
campaign version.
|
|
105
|
-
`cloud apply --campaign ID --file report.json` can load product verification
|
|
106
|
-
configuration from `--dir`, so it retains the product CLI version check.
|
|
107
|
-
Remote-only reads, including `cloud snapshot`, do not require a matching local
|
|
108
|
-
Testkit installation.
|