@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.
Files changed (144) hide show
  1. package/dist/cli/command-catalog.d.ts +0 -127
  2. package/dist/cli/command-catalog.js +1 -1
  3. package/dist/cli/command-registry.d.ts +1 -0
  4. package/dist/cli/command-registry.js +1 -1
  5. package/dist/cli/entrypoint.js +1 -1
  6. package/dist/cli/framework/execute.d.ts +1 -0
  7. package/dist/cli/framework/execute.js +6 -6
  8. package/dist/cli/index.js +1 -1
  9. package/dist/cli/version-drift.js +7 -7
  10. package/dist/config/authoring/index.d.ts +0 -5
  11. package/dist/config/loader/index.d.ts +0 -1
  12. package/dist/config/loader/index.js +1 -1
  13. package/dist/evidence/bundles.d.ts +2 -3
  14. package/dist/evidence/bundles.js +1 -1
  15. package/dist/evidence/contracts.d.ts +0 -8
  16. package/dist/evidence/index.d.ts +1 -1
  17. package/dist/evidence/index.js +1 -1
  18. package/dist/process/execa-bundle.json +1 -1
  19. package/package.json +3 -10
  20. package/testkit-config-schema.md +0 -23
  21. package/dist/app/cloud/campaigns.d.ts +0 -6
  22. package/dist/app/cloud/campaigns.js +0 -1
  23. package/dist/app/cloud/control-actions.d.ts +0 -12
  24. package/dist/app/cloud/control-actions.js +0 -2
  25. package/dist/app/cloud/control-links.d.ts +0 -5
  26. package/dist/app/cloud/control-links.js +0 -1
  27. package/dist/app/cloud/control-read.d.ts +0 -24
  28. package/dist/app/cloud/control-read.js +0 -1
  29. package/dist/app/cloud/control-resources.d.ts +0 -3
  30. package/dist/app/cloud/control-resources.js +0 -1
  31. package/dist/app/cloud/control.d.ts +0 -34
  32. package/dist/app/cloud/control.js +0 -5
  33. package/dist/app/cloud/credentials.d.ts +0 -11
  34. package/dist/app/cloud/credentials.js +0 -2
  35. package/dist/app/cloud/index.d.ts +0 -22
  36. package/dist/app/cloud/index.js +0 -2
  37. package/dist/app/cloud/login.d.ts +0 -63
  38. package/dist/app/cloud/login.js +0 -2
  39. package/dist/app/cloud/migration/archive.d.ts +0 -13
  40. package/dist/app/cloud/migration/archive.js +0 -1
  41. package/dist/app/cloud/migration/commands.d.ts +0 -8
  42. package/dist/app/cloud/migration/commands.js +0 -1
  43. package/dist/app/cloud/migration/evidence.d.ts +0 -13
  44. package/dist/app/cloud/migration/evidence.js +0 -2
  45. package/dist/app/cloud/migration/files.d.ts +0 -16
  46. package/dist/app/cloud/migration/files.js +0 -2
  47. package/dist/app/cloud/migration/index.d.ts +0 -6
  48. package/dist/app/cloud/migration/index.js +0 -1
  49. package/dist/app/cloud/migration/journal.d.ts +0 -64
  50. package/dist/app/cloud/migration/journal.js +0 -1
  51. package/dist/app/cloud/migration/mapping-dispositions.d.ts +0 -3
  52. package/dist/app/cloud/migration/mapping-dispositions.js +0 -1
  53. package/dist/app/cloud/migration/mapping.d.ts +0 -11
  54. package/dist/app/cloud/migration/mapping.js +0 -2
  55. package/dist/app/cloud/migration/model.d.ts +0 -175
  56. package/dist/app/cloud/migration/model.js +0 -1
  57. package/dist/app/cloud/migration/plan.d.ts +0 -7
  58. package/dist/app/cloud/migration/plan.js +0 -1
  59. package/dist/app/cloud/migration/progress.d.ts +0 -19
  60. package/dist/app/cloud/migration/progress.js +0 -1
  61. package/dist/app/cloud/migration/reconcile.d.ts +0 -22
  62. package/dist/app/cloud/migration/reconcile.js +0 -1
  63. package/dist/app/cloud/migration/reconciliation-spool.d.ts +0 -4
  64. package/dist/app/cloud/migration/reconciliation-spool.js +0 -1
  65. package/dist/app/cloud/migration/state.d.ts +0 -15
  66. package/dist/app/cloud/migration/state.js +0 -1
  67. package/dist/app/cloud/migration/transfer.d.ts +0 -6
  68. package/dist/app/cloud/migration/transfer.js +0 -1
  69. package/dist/cli/cloud-apply-help.d.ts +0 -1
  70. package/dist/cli/cloud-apply-help.js +0 -56
  71. package/dist/cli/cloud-selection.d.ts +0 -2
  72. package/dist/cli/cloud-selection.js +0 -3
  73. package/dist/cli/commands/cloud/apply.d.ts +0 -5
  74. package/dist/cli/commands/cloud/apply.js +0 -1
  75. package/dist/cli/commands/cloud/campaign/create.d.ts +0 -5
  76. package/dist/cli/commands/cloud/campaign/create.js +0 -1
  77. package/dist/cli/commands/cloud/download.d.ts +0 -5
  78. package/dist/cli/commands/cloud/download.js +0 -1
  79. package/dist/cli/commands/cloud/list.d.ts +0 -5
  80. package/dist/cli/commands/cloud/list.js +0 -1
  81. package/dist/cli/commands/cloud/login.d.ts +0 -13
  82. package/dist/cli/commands/cloud/login.js +0 -2
  83. package/dist/cli/commands/cloud/logout.d.ts +0 -7
  84. package/dist/cli/commands/cloud/logout.js +0 -1
  85. package/dist/cli/commands/cloud/migrate/apply.d.ts +0 -2
  86. package/dist/cli/commands/cloud/migrate/apply.js +0 -1
  87. package/dist/cli/commands/cloud/migrate/plan.d.ts +0 -7
  88. package/dist/cli/commands/cloud/migrate/plan.js +0 -1
  89. package/dist/cli/commands/cloud/migrate/progress.d.ts +0 -2
  90. package/dist/cli/commands/cloud/migrate/progress.js +0 -2
  91. package/dist/cli/commands/cloud/migrate/reconcile.d.ts +0 -2
  92. package/dist/cli/commands/cloud/migrate/reconcile.js +0 -1
  93. package/dist/cli/commands/cloud/policy/preview.d.ts +0 -5
  94. package/dist/cli/commands/cloud/policy/preview.js +0 -1
  95. package/dist/cli/commands/cloud/project/create.d.ts +0 -5
  96. package/dist/cli/commands/cloud/project/create.js +0 -1
  97. package/dist/cli/commands/cloud/publish.d.ts +0 -7
  98. package/dist/cli/commands/cloud/publish.js +0 -1
  99. package/dist/cli/commands/cloud/session/revoke.d.ts +0 -8
  100. package/dist/cli/commands/cloud/session/revoke.js +0 -1
  101. package/dist/cli/commands/cloud/show.d.ts +0 -5
  102. package/dist/cli/commands/cloud/show.js +0 -1
  103. package/dist/cli/commands/cloud/snapshot.d.ts +0 -5
  104. package/dist/cli/commands/cloud/snapshot.js +0 -1
  105. package/dist/cli/commands/cloud/status.d.ts +0 -33
  106. package/dist/cli/commands/cloud/status.js +0 -1
  107. package/dist/cloud/account.d.ts +0 -34
  108. package/dist/cloud/account.js +0 -1
  109. package/dist/cloud/archive-contract.d.ts +0 -1
  110. package/dist/cloud/archive-contract.js +0 -1
  111. package/dist/cloud/archive.d.ts +0 -12
  112. package/dist/cloud/archive.js +0 -1
  113. package/dist/cloud/campaigns.d.ts +0 -200
  114. package/dist/cloud/campaigns.js +0 -2
  115. package/dist/cloud/client.d.ts +0 -68
  116. package/dist/cloud/client.js +0 -4
  117. package/dist/cloud/download.d.ts +0 -5
  118. package/dist/cloud/download.js +0 -1
  119. package/dist/cloud/historical-contract.d.ts +0 -224
  120. package/dist/cloud/historical-contract.js +0 -1
  121. package/dist/cloud/historical-json.d.ts +0 -16
  122. package/dist/cloud/historical-json.js +0 -1
  123. package/dist/cloud/historical.d.ts +0 -69
  124. package/dist/cloud/historical.js +0 -1
  125. package/dist/cloud/pagination.d.ts +0 -5
  126. package/dist/cloud/pagination.js +0 -1
  127. package/dist/cloud/transport.d.ts +0 -18
  128. package/dist/cloud/transport.js +0 -1
  129. package/dist/config/loader/cloud.d.ts +0 -2
  130. package/dist/config/loader/cloud.js +0 -1
  131. package/docs/cloud-control.md +0 -108
  132. package/docs/testkit-cloud-boundary.md +0 -628
  133. package/node_modules/buffer-crc32/LICENSE +0 -19
  134. package/node_modules/buffer-crc32/README.md +0 -55
  135. package/node_modules/buffer-crc32/dist/index.cjs +0 -306
  136. package/node_modules/buffer-crc32/dist/index.d.cts +0 -35
  137. package/node_modules/buffer-crc32/dist/index.d.mts +0 -35
  138. package/node_modules/buffer-crc32/dist/index.mjs +0 -304
  139. package/node_modules/buffer-crc32/index.d.ts +0 -35
  140. package/node_modules/buffer-crc32/package.json +0 -54
  141. package/node_modules/yazl/LICENSE +0 -21
  142. package/node_modules/yazl/README.md +0 -457
  143. package/node_modules/yazl/index.js +0 -826
  144. package/node_modules/yazl/package.json +0 -34
@@ -1,5 +0,0 @@
1
- import type { CommandContext } from "../../command-catalog.js";
2
- export default function run({ flags, json, log }: CommandContext<"cloud snapshot">): Promise<{
3
- ok: boolean;
4
- snapshot: unknown;
5
- }>;
@@ -1 +0,0 @@
1
- import{readCloudSnapshot as t}from"../../../app/cloud/index.js";export default async function o({flags:i,json:r,log:n}){const e=await t({campaignId:i.campaign,findingId:i.finding,findings:i.findings,receipts:i.receipts,history:i.history,rootCauseKey:i["root-cause-key"],issueReference:i["issue-reference"],view:i.view,limit:i.limit,cursor:i.cursor,origin:i.url,projectId:i.project});return r||n(JSON.stringify(e,null,2)),{ok:!0,snapshot:e}}
@@ -1,33 +0,0 @@
1
- import type { CommandContext } from "../../command-catalog.js";
2
- export default function run({ flags, json, log }: CommandContext<"cloud status">): Promise<{
3
- origin: string;
4
- source: "environment";
5
- authenticated: null;
6
- message: string;
7
- ok: boolean;
8
- } | {
9
- message?: undefined;
10
- origin: string;
11
- source: "none";
12
- authenticated: boolean;
13
- ok: boolean;
14
- } | {
15
- user: {
16
- id: string;
17
- email: string;
18
- name: string;
19
- };
20
- expiresAt: string;
21
- maximumExpiresAt: string;
22
- message?: undefined;
23
- origin: string;
24
- source: "account";
25
- authenticated: boolean;
26
- ok: boolean;
27
- } | {
28
- message?: undefined;
29
- origin: string;
30
- source: "account";
31
- authenticated: boolean;
32
- ok: boolean;
33
- }>;
@@ -1 +0,0 @@
1
- import{cloudLoginStatus as u}from"../../../app/cloud/login.js";export default async function o({flags:t,json:e,log:i}){const n=await u({origin:t.url});return e||(i(`Cloud: ${n.origin}`),i(n.source==="environment"?n.message:n.authenticated&&"user"in n?`Signed in as ${n.user.email}`:"Not signed in. Run testkit cloud login.")),{ok:!0,...n}}
@@ -1,34 +0,0 @@
1
- export declare function cloudOrigin(value: string): string;
2
- export declare function isCloudAccountToken(value: unknown): value is string;
3
- export interface AccountSession {
4
- user: {
5
- id: string;
6
- email: string;
7
- name: string;
8
- };
9
- expiresAt: string;
10
- maximumExpiresAt: string;
11
- }
12
- export interface LoginRequest {
13
- deviceCode: string;
14
- userCode: string;
15
- verificationUrl: string;
16
- expiresIn: number;
17
- interval: number;
18
- }
19
- export declare class CloudAccountClient {
20
- private readonly token?;
21
- readonly origin: string;
22
- constructor(origin: string, token?: string | undefined);
23
- downloadArtifact(url: URL, target: string, expected: {
24
- bytes: number;
25
- sha256: string;
26
- }): Promise<void>;
27
- login(): Promise<LoginRequest>;
28
- poll(deviceCode: string): Promise<AccountSession>;
29
- session(): Promise<AccountSession>;
30
- refresh(): Promise<AccountSession>;
31
- revoke(deviceCode: string): Promise<void>;
32
- request(method: string, target: string, input?: unknown): Promise<unknown>;
33
- }
34
- export declare function parseSession(value: unknown): AccountSession;
@@ -1 +0,0 @@
1
- import{downloadVerifiedArtifact as p}from"./download.js";import{request as u,invalidResponse as i}from"./transport.js";import{parseResponse as c}from"./client.js";import{isUnknownRecord as a}from"../kernel/unknown-values.js";export function cloudOrigin(t){const e=new URL(t);if(!["https:","http:"].includes(e.protocol)||e.username||e.password||e.pathname!=="/"||e.search||e.hash)throw new Error("Testkit Cloud URL must be an HTTP or HTTPS origin without credentials, a path, query, or fragment");if(e.protocol==="http:"&&!["localhost","127.0.0.1","[::1]"].includes(e.hostname))throw new Error("Testkit Cloud account credentials require HTTPS except on localhost");return e.origin}export function isCloudAccountToken(t){return typeof t=="string"&&/^tkc_cli_[A-Za-z0-9_-]{43}$/.test(t)}export class CloudAccountClient{token;origin;constructor(e,r){this.token=r,this.origin=cloudOrigin(e)}async downloadArtifact(e,r,s){if(e.origin!==this.origin||e.username||e.password)throw i("Cloud request URL");await p(e,this.token??"",r,s)}async login(){const e=await this.request("POST","/api/v1/auth/cli",{});if(!a(e)||!isCloudAccountToken(e.deviceCode)||typeof e.userCode!="string"||!/^[A-Z2-7]{4}-[A-Z2-7]{4}-[A-Z2-7]{4}$/.test(e.userCode)||typeof e.verificationUrl!="string"||typeof e.expiresIn!="number"||!Number.isInteger(e.expiresIn)||e.expiresIn<1||e.expiresIn>1800||typeof e.interval!="number"||!Number.isInteger(e.interval)||e.interval<1||e.interval>60)throw i("CLI login request");let r;try{r=new URL(e.verificationUrl)}catch{throw i("CLI approval URL")}if(r.origin!==this.origin||r.username||r.password||r.pathname!=="/cli/authorize"||r.searchParams.get("code")!==e.userCode)throw i("CLI approval URL");return{deviceCode:e.deviceCode,userCode:e.userCode,verificationUrl:r.href,expiresIn:e.expiresIn,interval:e.interval}}async poll(e){return parseSession(await this.request("POST","/api/v1/auth/cli/poll",{deviceCode:e}))}async session(){return parseSession(await this.request("GET","/api/v1/auth/cli/session"))}async refresh(){return parseSession(await this.request("POST","/api/v1/auth/cli/refresh",{}))}async revoke(e){await this.request("POST","/api/v1/auth/cli/revoke",{deviceCode:e})}async request(e,r,s){const o=new URL(r,this.origin);if(o.origin!==this.origin||o.username||o.password)throw i("Cloud request URL");const n=s===void 0?null:Buffer.from(JSON.stringify(s));return c(await u({method:e,url:o,body:n,timeoutMs:3e4,maxResponseBytes:4*1024*1024,headers:{Accept:"application/json","User-Agent":"testkit",...this.token?{Authorization:`Bearer ${this.token}`}:{},...n?{"Content-Type":"application/json","Content-Length":String(n.length)}:{}}}))}}export function parseSession(t){if(!a(t)||!a(t.user)||typeof t.user.id!="string"||!t.user.id||typeof t.user.email!="string"||!t.user.email||typeof t.user.name!="string"||typeof t.expiresAt!="string"||!Number.isFinite(Date.parse(t.expiresAt))||typeof t.maximumExpiresAt!="string"||!Number.isFinite(Date.parse(t.maximumExpiresAt)))throw i("CLI session");return{user:{id:t.user.id,email:t.user.email,name:t.user.name},expiresAt:t.expiresAt,maximumExpiresAt:t.maximumExpiresAt}}
@@ -1 +0,0 @@
1
- export declare const MAX_CLOUD_ARCHIVE_BYTES: number;
@@ -1 +0,0 @@
1
- export const MAX_CLOUD_ARCHIVE_BYTES=2164260864;
@@ -1,12 +0,0 @@
1
- import type { RunBundleManifest, RunBundleUploadFile } from "../evidence/contracts.js";
2
- import type { HistoricalManifest } from "./historical-contract.js";
3
- export interface UploadArchiveSource {
4
- manifest: RunBundleManifest | HistoricalManifest;
5
- files: readonly Pick<RunBundleUploadFile, "path" | "absolutePath" | "bytes" | "sha256">[];
6
- }
7
- export declare function withUploadArchive<Result>(source: UploadArchiveSource, maxBytes: number, upload: (file: string, bytes: number) => Promise<Result>): Promise<Result>;
8
- /** Write one verified ZIP to a caller-owned, previously absent path. */
9
- export declare function writeUploadArchive(source: UploadArchiveSource, maxBytes: number, target: string, serializeManifest?: (manifest: UploadArchiveSource["manifest"]) => string): Promise<{
10
- bytes: number;
11
- sha256: string;
12
- }>;
@@ -1 +0,0 @@
1
- import O from"node:crypto";import t from"node:fs";import E from"node:path";import{Readable as L,Transform as x}from"node:stream";import{finished as T,pipeline as $}from"node:stream/promises";import{ZipFile as F}from"yazl";import{makeTempDir as R,removeTempDir as V}from"../process/files/temp-dir.js";export async function withUploadArchive(l,u,d){const c=R("testkit-cloud-"),p=E.join(c,"archive.zip");try{const o=await writeUploadArchive(l,u,p);return await d(p,o.bytes)}finally{V(c)}}export async function writeUploadArchive(l,u,d,c=JSON.stringify){const p=t.openSync(d,"wx",384),o=new F;let a,m,S=!1,y=0;const v=O.createHash("sha256"),f=new x({transform(i,e,h){y+=i.length,v.update(i),h(y>u?new Error("Cloud archive exceeds upload size limit"):null,i)}});o.on("error",i=>f.destroy(i));const w=$(o.outputStream,f,t.createWriteStream(d,{fd:p}));w.catch(()=>{});try{const i={mtime:new Date("1980-01-01T00:00:00Z"),forceDosTimestamp:!0,mode:33152};o.addBuffer(Buffer.from(c(l.manifest)),"manifest.json",i);for(const e of l.files)o.addReadStreamLazy(`files/${e.path}`,{...i,size:e.bytes},h=>{let s;try{const n=t.lstatSync(e.absolutePath);if(!n.isFile()||n.isSymbolicLink()||n.size!==e.bytes)throw new Error(`Verified bundle file changed before upload: ${e.path}`);s=t.openSync(e.absolutePath,t.constants.O_RDONLY|t.constants.O_NOFOLLOW);const b=t.fstatSync(s);if(b.ino!==n.ino||b.dev!==n.dev||b.size!==e.bytes)throw new Error(`Verified bundle file changed before upload: ${e.path}`);const z=O.createHash("sha256");let g=0;m=new x({transform(r,_,D){g+=r.length,z.update(r),D(g>e.bytes?new Error(`Verified bundle file changed during upload: ${e.path}`):null,r)},flush(r){r(g!==e.bytes||z.digest("hex")!==e.sha256?new Error(`Verified bundle file changed during upload: ${e.path}`):null)}}),m.on("error",r=>f.destroy(r)),a=t.createReadStream(e.absolutePath,{fd:s}),s=void 0,a.on("error",r=>f.destroy(r)),h(null,a.pipe(m))}catch(n){s!==void 0&&t.closeSync(s),h(n,L.from([]))}});return o.end(),await w,S=!0,{bytes:y,sha256:v.digest("hex")}}finally{a?.destroy(),a&&await T(a).catch(()=>{}),m?.destroy(),f.destroy(),await w.catch(()=>{}),S||t.rmSync(d,{force:!0})}}
@@ -1,200 +0,0 @@
1
- export interface CampaignReport {
2
- schemaVersion: 1;
3
- projectId: string;
4
- campaignId: string;
5
- idempotencyKey: string;
6
- expectedVersion: number;
7
- command: CampaignCommand;
8
- }
9
- export type Actor = {
10
- userId: string;
11
- tokenId?: never;
12
- } | {
13
- tokenId: string;
14
- userId?: never;
15
- };
16
- export type Relationship = {
17
- kind: "independent";
18
- targetId?: never;
19
- } | {
20
- kind: "known" | "duplicate";
21
- targetId: string;
22
- };
23
- export interface Verification {
24
- actor: Actor;
25
- acceptedAt: string;
26
- findingRevision: number;
27
- minimumConfirmingRuns: number;
28
- confirmingReceiptIds: string[];
29
- }
30
- export interface Campaign {
31
- id: string;
32
- projectId: string;
33
- version: number;
34
- targetBugCount: number;
35
- minimumConfirmingRuns: number;
36
- state: "awaiting_reports" | "in_progress" | "finished";
37
- focus: string;
38
- reason: string;
39
- createdAt: string;
40
- lastReportAt: string | null;
41
- creator: Actor;
42
- }
43
- export interface CampaignResult {
44
- schemaVersion: 1;
45
- projectId: string;
46
- campaignId: string;
47
- commandId: string;
48
- version: number;
49
- acceptedAt: string;
50
- changed: boolean;
51
- findingId?: string;
52
- findingRevision?: number;
53
- verification?: Verification;
54
- previousMinimumConfirmingRuns?: number;
55
- dashboardPath?: string;
56
- }
57
- export interface FindingData extends Partial<Record<(typeof dataText)[number], string>> {
58
- title: string;
59
- rootCauseKey: string;
60
- classification: (typeof classifications)[number];
61
- variants?: string[] | null;
62
- issueReferences?: string[] | null;
63
- regressionCaseIds?: string[] | null;
64
- evidence?: Array<{
65
- receiptId: string;
66
- path: string;
67
- }> | null;
68
- }
69
- export type CampaignCommand = {
70
- reason?: string;
71
- } & ({
72
- type: "state.report";
73
- state: "in_progress" | "finished";
74
- } | {
75
- type: "receipt.link";
76
- receiptId: string;
77
- } | {
78
- type: "finding.record" | "finding.update";
79
- findingId: string;
80
- finding: FindingData;
81
- invalidateVerification?: boolean;
82
- } | {
83
- type: "finding.verify";
84
- findingId: string;
85
- } | {
86
- type: "finding.relate";
87
- findingId: string;
88
- relationship: Relationship;
89
- } | {
90
- type: "verification.settings.update";
91
- minimumConfirmingRuns: number;
92
- reason: string;
93
- });
94
- type HistoryCommand = CampaignCommand | {
95
- type: "campaign.create";
96
- } | {
97
- type: "historical.import";
98
- } | {
99
- type: "verification.settings.update";
100
- minimumConfirmingRuns: number;
101
- reason?: string;
102
- };
103
- export interface HistoricalFindingSource {
104
- importId: string;
105
- namespace: string;
106
- sourceId: string;
107
- digest: string;
108
- observedAt: string;
109
- importedAt: string;
110
- actor: Actor;
111
- record: unknown;
112
- }
113
- export interface Finding {
114
- import?: HistoricalFindingSource;
115
- id: string;
116
- projectId: string;
117
- campaignId: string;
118
- revision: number;
119
- createdAt: string;
120
- updatedAt: string;
121
- data: FindingData;
122
- relationship: Relationship;
123
- verification: Verification | null;
124
- }
125
- export interface FindingDetail {
126
- campaignVersion: number;
127
- finding: Finding;
128
- relatedCampaignId?: string;
129
- evidence: Array<{
130
- receiptId: string;
131
- path: string;
132
- available: boolean;
133
- }>;
134
- }
135
- export interface FindingSummary extends Pick<Finding, "id" | "campaignId" | "createdAt" | "relationship">, Pick<FindingData, "title" | "rootCauseKey" | "classification"> {
136
- campaignVersion: number;
137
- verified: boolean;
138
- counted: boolean;
139
- }
140
- interface LinkedReceipt {
141
- receiptId: string;
142
- runIdPreview: string;
143
- linkedAt: string;
144
- actor: Actor;
145
- evidenceExpiredAt: string | null;
146
- }
147
- export interface FindingPage {
148
- items: FindingSummary[];
149
- bugCount: number;
150
- campaign?: Campaign;
151
- campaignVersion?: number;
152
- nextCursor?: string;
153
- }
154
- export type CampaignPage = FindingPage | {
155
- items: LinkedReceipt[];
156
- nextCursor?: string;
157
- } | {
158
- items: Array<CampaignResult & {
159
- actor: Actor;
160
- command: HistoryCommand;
161
- }>;
162
- nextCursor?: string;
163
- };
164
- export type CampaignRead = "project" | "campaign" | "findings" | "finding" | "receipts" | "history";
165
- export interface CampaignSelection {
166
- campaignId?: string;
167
- findingId?: string;
168
- findings?: boolean;
169
- receipts?: boolean;
170
- history?: boolean;
171
- rootCauseKey?: string;
172
- issueReference?: string;
173
- view?: string;
174
- limit?: number;
175
- cursor?: string;
176
- }
177
- export type FindingSelection = Omit<CampaignSelection, "findingId" | "receipts" | "history"> & {
178
- findingId?: never;
179
- receipts?: false;
180
- history?: false;
181
- } & ({
182
- campaignId: string;
183
- } | {
184
- findings: true;
185
- });
186
- declare const classifications: readonly ["product_bug", "testkit_failure", "environment_failure", "stale_assertion", "unproven", "intended_behavior", "observation"];
187
- declare const dataText: readonly ["surface", "severity", "expectedBehavior", "authority", "observedBehavior", "impact", "reproduction", "limitations"];
188
- export declare function campaignError(field: string, detail: string): Error;
189
- export declare function requireCloudId(value: unknown, field: string): string;
190
- export declare function parseCampaignReport(value: unknown, project: string, campaign: string): CampaignReport;
191
- export declare function parseCampaign(value: unknown, project: string, campaign: string | undefined): Campaign;
192
- export declare function parseCampaignResult(value: unknown, project: string, campaign: string | undefined, finding?: string): CampaignResult;
193
- export declare function parseCampaignReportResult(value: unknown, report: CampaignReport): CampaignResult;
194
- export declare function parseFindingDetail(value: unknown, project: string, campaign: string, id: string): FindingDetail;
195
- export declare function campaignReadPath(project: string, selection: CampaignSelection): {
196
- path: string;
197
- kind: CampaignRead;
198
- };
199
- export declare function parseCampaignPage(value: unknown, kind: CampaignRead, project: string, campaign?: string): CampaignPage;
200
- export {};
@@ -1,2 +0,0 @@
1
- import{isUnknownRecord as k}from"../kernel/unknown-values.js";import{validateHistoricalSourceRecord as K}from"./historical-contract.js";import{attachErrorDiagnostics as j}from"../kernel/error-serialization.js";const A=["product_bug","testkit_failure","environment_failure","stale_assertion","unproven","intended_behavior","observation"],b=["surface","severity","expectedBehavior","authority","observedBehavior","impact","reproduction","limitations"];export function campaignError(t,i){return j(Object.assign(new Error(`${t}: ${i}`),{code:"invalid_campaign_request"}),{source:"local",field:t})}function d(t,i){if(!t)throw campaignError(i,"invalid campaign value")}function c(t,i){return d(k(t),i),t}function I(t,i,e){d(Object.keys(t).every(n=>i.includes(n)),e)}function a(t,i,e=4096,n=!1){return d(typeof t=="string"&&Buffer.byteLength(t)<=e&&!/[\ud800-\udfff]/u.test(t)&&Array.from(t).every(r=>r.charCodeAt(0)>=32||`
2
- \r `.includes(r))&&(!n||t.trim().length>0),i),t}export function requireCloudId(t,i){return d(typeof t=="string"&&/^[A-Za-z0-9_-]{1,128}$/.test(t),i),t}function f(t,i,e=1,n=Number.MAX_SAFE_INTEGER){return d(typeof t=="number"&&Number.isSafeInteger(t)&&t>=e&&t<=n,i),t}function m(t,i,e){d(typeof t=="string"&&i.includes(t),e)}function v(t,i){d(typeof t=="boolean",i)}function l(t,i,e){return d(Array.isArray(t)&&t.length<=e,i),t}function p(t,i,e){requireCloudId(t,e),i!==void 0&&d(t===i,e)}function C(t){const i=c(t,"evidence");return{receiptId:requireCloudId(i.receiptId,"receiptId"),path:a(i.path,"path",4096,!0)}}function R(t){const i=c(t,"relationship");I(i,["kind","targetId"],"relationship"),m(i.kind,["independent","known","duplicate"],"relationship.kind"),i.kind==="independent"?d(i.targetId===void 0,"relationship.targetId"):requireCloudId(i.targetId,"relationship.targetId")}function V(t){const i=c(t,"finding data");I(i,["title","rootCauseKey","classification",...b,"variants","issueReferences","regressionCaseIds","evidence"],"finding data");for(const n of["title","rootCauseKey"])a(i[n],n,256,!0);m(i.classification,A,"classification");for(const n of b)i[n]!==void 0&&a(i[n],n);for(const n of["variants","issueReferences","regressionCaseIds"])for(const r of l(i[n]??[],n,32)){const s=a(r,n,4096,!0);if(n==="regressionCaseIds"&&requireCloudId(s,n),n==="issueReferences"){const o=URL.parse(s);d(o&&["http:","https:"].includes(o.protocol)&&!o.username&&!o.password,n)}}const e=l(i.evidence??[],"evidence",64).map(n=>(I(c(n,"evidence"),["receiptId","path"],"evidence"),JSON.stringify(C(n))));return d(new Set(e).size===e.length,"evidence"),i}function x(t,i=!0){const e=c(t,"command"),n={"state.report":["state"],"receipt.link":["receiptId"],"finding.record":["findingId","finding"],"finding.update":["findingId","finding","invalidateVerification"],"finding.verify":["findingId"],"finding.relate":["findingId","relationship"],"verification.settings.update":["minimumConfirmingRuns"],...i?{}:{"campaign.create":[],"historical.import":[]}},r=a(e.type,"command.type",128,!0);return d(Object.hasOwn(n,r),"command.type"),I(e,["type","reason",...n[r]],"command"),e.reason!==void 0&&a(e.reason,"reason"),r==="state.report"&&(m(e.state,["in_progress","finished"],"state"),e.state==="finished"&&a(e.reason,"reason",4096,!0)),r==="receipt.link"&&requireCloudId(e.receiptId,"receiptId"),r.startsWith("finding.")&&requireCloudId(e.findingId,"findingId"),(r==="finding.record"||r==="finding.update")&&V(e.finding),e.invalidateVerification!==void 0&&v(e.invalidateVerification,"invalidateVerification"),r==="finding.relate"&&(R(e.relationship),d(c(e.relationship,"relationship").targetId!==e.findingId,"relationship.targetId")),r==="verification.settings.update"&&(f(e.minimumConfirmingRuns,"minimumConfirmingRuns",1,64),i&&a(e.reason,"reason",4096,!0)),e}export function parseCampaignReport(t,i,e){const n=c(t,"report");return I(n,["schemaVersion","projectId","campaignId","idempotencyKey","expectedVersion","command"],"report"),d(n.schemaVersion===1,"schemaVersion"),p(n.projectId,i,"projectId"),p(n.campaignId,e,"campaignId"),d(typeof n.idempotencyKey=="string"&&Buffer.byteLength(n.idempotencyKey)<=128&&n.idempotencyKey.trim()&&!n.idempotencyKey.includes("\0")&&!/[\ud800-\udfff]/u.test(n.idempotencyKey),"idempotencyKey"),f(n.expectedVersion,"expectedVersion"),x(n.command),n}function y(t){const i=c(t,"actor");d(i.userId===void 0!=(i.tokenId===void 0),"actor"),requireCloudId(i.userId||i.tokenId,"actor ID")}function w(t){const i=c(t,"verification");y(i.actor),a(i.acceptedAt,"acceptedAt",128,!0),f(i.findingRevision,"findingRevision"),f(i.minimumConfirmingRuns,"minimumConfirmingRuns",1,64);const e=l(i.confirmingReceiptIds,"confirmingReceiptIds",64);for(const n of e)requireCloudId(n,"confirmingReceiptId");d(new Set(e).size===e.length&&e.length>=Number(i.minimumConfirmingRuns),"confirmingReceiptIds")}export function parseCampaign(t,i,e){const n=c(t,"campaign");return p(n.id,e,"campaignId"),p(n.projectId,i,"projectId"),f(n.version,"version"),f(n.targetBugCount,"targetBugCount",1,2147483647),f(n.minimumConfirmingRuns,"minimumConfirmingRuns",1,64),m(n.state,["awaiting_reports","in_progress","finished"],"state"),a(n.focus,"focus"),a(n.reason,"reason"),a(n.createdAt,"createdAt",128,!0),n.lastReportAt!==null&&a(n.lastReportAt,"lastReportAt",128,!0),y(n.creator),n}export function parseCampaignResult(t,i,e,n){const r=c(t,"campaign result");return d(r.schemaVersion===1,"schemaVersion"),p(r.projectId,i,"projectId"),p(r.campaignId,e,"campaignId"),requireCloudId(r.commandId,"commandId"),f(r.version,"version"),a(r.acceptedAt,"acceptedAt",128,!0),v(r.changed,"changed"),(n!==void 0||r.findingId!==void 0||r.findingRevision!==void 0||r.verification!==void 0)&&(p(r.findingId,n,"findingId"),f(r.findingRevision,"findingRevision")),r.verification!==void 0&&w(r.verification),r.previousMinimumConfirmingRuns!==void 0&&f(r.previousMinimumConfirmingRuns,"previousMinimumConfirmingRuns",1,64),r.dashboardPath!==void 0&&a(r.dashboardPath,"dashboardPath",4096,!0),r}export function parseCampaignReportResult(t,i){const e=parseCampaignResult(t,i.projectId,i.campaignId,"findingId"in i.command?i.command.findingId:void 0);return d(e.version===i.expectedVersion+Number(e.changed),"accepted report version"),d(i.command.type!=="finding.verify"||e.changed&&e.verification!==void 0&&e.verification.findingRevision===e.findingRevision,"accepted verification"),e}export function parseFindingDetail(t,i,e,n){const r=c(t,"finding detail");f(r.campaignVersion,"campaignVersion");const s=c(r.finding,"finding");if(p(s.id,n,"findingId"),p(s.projectId,i,"projectId"),p(s.campaignId,e,"campaignId"),f(s.revision,"revision"),a(s.createdAt,"createdAt",128,!0),a(s.updatedAt,"updatedAt",128,!0),s.import!==void 0){const u=c(s.import,"finding import metadata");for(const h of["importId","namespace","sourceId"])a(u[h],h,128,!0);d(typeof u.digest=="string"&&/^[a-f0-9]{64}$/u.test(u.digest),"source digest"),a(u.observedAt,"observedAt",128,!0),a(u.importedAt,"importedAt",128,!0),y(u.actor),K(u.record)}const o=V(s.data);R(s.relationship),s.verification!==null&&w(s.verification),r.relatedCampaignId!==void 0&&requireCloudId(r.relatedCampaignId,"relatedCampaignId");const g=new Set(l(o.evidence,"evidence",64).map(u=>JSON.stringify(C(u))));return l(r.evidence,"evidence availability",64).forEach(u=>{const h=JSON.stringify(C(u));d(g.delete(h),"evidence availability identity"),v(c(u,"evidence").available,"available")}),d(g.size===0,"evidence availability coverage"),r}export function campaignReadPath(t,i){const e=i;if([e.campaignId,e.findingId,e.findings,e.receipts,e.history,e.rootCauseKey,e.issueReference,e.view,e.limit,e.cursor].every(o=>o===void 0||o===!1))return{path:`/api/v1/projects/${encodeURIComponent(t)}/snapshot`,kind:"project"};requireCloudId(t,"projectId"),e.campaignId!==void 0&&requireCloudId(e.campaignId,"campaignId"),e.findingId!==void 0&&requireCloudId(e.findingId,"findingId"),d([e.findingId!==void 0,e.findings,e.receipts,e.history].filter(Boolean).length<=1,"snapshot selectors");const n=e.findingId!==void 0?"finding":e.receipts?"receipts":e.history?"history":"findings";d(e.campaignId||n==="findings"&&e.findings,"campaign selector");const r=new URLSearchParams;e.limit!==void 0&&r.set("limit",String(f(e.limit,"limit",1,n==="history"?20:100))),e.cursor!==void 0&&r.set("cursor",a(e.cursor,"cursor",1024,!0)),n==="finding"&&d(!r.size,"finding pagination");for(const[o,g]of[["rootCauseKey",e.rootCauseKey],["issueReference",e.issueReference],["view",e.view]])g!==void 0&&(d(n==="findings",o),o==="view"&&m(g,["all","counted","secondary"],"view"),r.set(o,a(g,o,o==="rootCauseKey"?256:4096)));return{path:`${`/api/v1/projects/${t}/campaigns${e.campaignId?`/${e.campaignId}`:""}`}/${n==="finding"?`findings/${e.findingId}`:n}${r.size?`?${r}`:""}`,kind:n}}export function parseCampaignPage(t,i,e,n){const r=c(t,"campaign page");if(r.nextCursor!==void 0&&a(r.nextCursor,"nextCursor",1024,!0),i==="findings"&&(f(r.bugCount,"bugCount",0),n!==void 0||r.campaign!==void 0||r.campaignVersion!==void 0)){const s=parseCampaign(r.campaign,e,n);d(f(r.campaignVersion,"campaignVersion")===s.version,"campaignVersion")}for(const s of l(r.items,"items",i==="history"?20:100)){const o=c(s,"page item");i==="history"?(parseCampaignResult(o,e,n),y(o.actor),x(o.command,!1)):i==="receipts"?(requireCloudId(o.receiptId,"receiptId"),a(o.runIdPreview,"runIdPreview",1024),a(o.linkedAt,"linkedAt",128,!0),y(o.actor),o.evidenceExpiredAt!==null&&a(o.evidenceExpiredAt,"evidenceExpiredAt",128,!0)):(requireCloudId(o.id,"findingId"),p(o.campaignId,n,"campaignId"),f(o.campaignVersion,"campaignVersion"),a(o.title,"title",256,!0),a(o.rootCauseKey,"rootCauseKey",256,!0),m(o.classification,A,"classification"),v(o.verified,"verified"),v(o.counted,"counted"),R(o.relationship),a(o.createdAt,"createdAt",128,!0))}return r}
@@ -1,68 +0,0 @@
1
- import { type ResponseDocument } from "./transport.js";
2
- import { type Campaign, type CampaignResult, type CampaignReport, type CampaignSelection, type FindingDetail, type FindingPage, type FindingSelection, type CampaignPage } from "./campaigns.js";
3
- import { type CloudProjectSnapshot } from "./historical-contract.js";
4
- import type { VerifiedRunBundleUpload } from "../evidence/index.js";
5
- export interface CloudClientOptions {
6
- origin: string;
7
- projectId: string;
8
- token: string;
9
- }
10
- export type CloudReceipt = ReturnType<typeof parseCloudReceipt>;
11
- export declare class CloudResponseError extends Error {
12
- readonly code: null | string;
13
- readonly status: number;
14
- constructor(status: number, message: string, code?: null | string);
15
- }
16
- export declare class CloudClient {
17
- private readonly responses;
18
- readonly origin: URL;
19
- readonly projectId: string;
20
- private readonly token;
21
- constructor(options: Readonly<CloudClientOptions>, responses?: {
22
- decodeJSON?: (text: string) => unknown;
23
- maxResponseBytes?: number;
24
- });
25
- publish(source: VerifiedRunBundleUpload, onProgress?: (message: string) => void): Promise<CloudReceipt>;
26
- apply(envelope: unknown): Promise<unknown>;
27
- campaign(id: string): Promise<Campaign>;
28
- report(report: CampaignReport): Promise<CampaignResult>;
29
- snapshot(): Promise<CloudProjectSnapshot>;
30
- snapshot(selection: CampaignSelection & {
31
- findingId: string;
32
- campaignId: string;
33
- }): Promise<FindingDetail>;
34
- snapshot(selection: FindingSelection): Promise<FindingPage>;
35
- snapshot(selection: CampaignSelection): Promise<CloudProjectSnapshot | FindingDetail | CampaignPage>;
36
- finding(campaignId: string, findingId: string): Promise<FindingDetail>;
37
- receipt(id: string): Promise<CloudReceipt>;
38
- private decodeCampaign;
39
- private requestJSON;
40
- private uploadFile;
41
- private target;
42
- }
43
- export declare function parseResponse(response: ResponseDocument, decodeJSON?: (text: string) => unknown): unknown;
44
- export declare function parseCloudReceipt(value: unknown): {
45
- acceptedAt: string;
46
- commitSha: string;
47
- decision: "hold" | "ineligible" | "ship";
48
- decisionReasons: {
49
- ruleId: string;
50
- message: string;
51
- }[];
52
- fullRun: boolean;
53
- id: string;
54
- manifestDigest: string;
55
- policyId: string;
56
- policyRevision: number;
57
- projectId: string;
58
- regressions: {
59
- fixed: number;
60
- known: number;
61
- new: number;
62
- returning: number;
63
- };
64
- repository: string;
65
- runId: string;
66
- runStatus: string;
67
- testkitVersion: string;
68
- };
@@ -1,4 +0,0 @@
1
- import{request as f,invalidResponse as o}from"./transport.js";import{isUnknownRecord as p}from"../kernel/unknown-values.js";import{withUploadArchive as w}from"./archive.js";import{MAX_CLOUD_ARCHIVE_BYTES as y}from"./archive-contract.js";import{attachErrorDiagnostics as I}from"../kernel/error-serialization.js";import{campaignReadPath as C,parseCampaign as R,parseCampaignPage as S,parseCampaignReportResult as j,parseFindingDetail as m}from"./campaigns.js";import T from"node:fs";import{parseProjectSnapshot as U}from"./historical-contract.js";export class CloudResponseError extends Error{code;status;constructor(e,r,i=null){super(r),this.name="CloudResponseError",this.code=i,this.status=e}}export class CloudClient{responses;origin;projectId;token;constructor(e,r={}){this.responses=r;const i=new URL(e.origin);if(!["http:","https:"].includes(i.protocol))throw new Error("Testkit Cloud URL must use HTTP or HTTPS");if(i.username||i.password)throw new Error("Testkit Cloud URL must not contain credentials");if(i.pathname!=="/"||i.search||i.hash)throw new Error("Testkit Cloud URL must be an origin without a path, query, or fragment");if(!e.projectId||!e.token)throw new Error("Testkit Cloud project ID and token are required");this.origin=i,this.projectId=e.projectId,this.token=e.token}async publish(e,r=()=>{}){const i=b(await this.requestJSON("POST",`/api/v1/projects/${encodeURIComponent(this.projectId)}/publications`,{evidenceFormat:"zip-v1",idempotencyKey:`bundle:${e.manifest.digest}`,manifest:e.manifest,projectId:this.projectId}));if(i.status==="rejected")throw new Error("Testkit Cloud publication was rejected");if(i.status==="pending"){if(!i.archive)throw new Error("This Cloud server or pending publication does not support zip-v1. Upgrade Cloud before publishing with this Testkit version.");const{url:s,maxBytes:u}=i.archive;this.target(s),r(`Compressing ${String(e.files.length)} evidence files for Cloud...
2
- `),await w(e,u,async(a,h)=>{r(`Uploading ${String(h)} compressed bytes to Cloud...
3
- `),await this.uploadFile(s,a,h)}),r(`Cloud is verifying and accepting the run...
4
- `)}const n=parseCloudReceipt(await this.requestJSON("POST",i.completeUrl));if(n.projectId!==this.projectId||n.runId!==e.runId||n.manifestDigest!==e.manifest.digest)throw new Error("Testkit Cloud receipt does not match the published bundle");return n}async apply(e){return this.requestJSON("POST",`/api/v1/projects/${encodeURIComponent(this.projectId)}/mutations`,e)}async campaign(e){const r=`/api/v1/projects/${encodeURIComponent(this.projectId)}/campaigns/${encodeURIComponent(e)}`;return this.decodeCampaign(()=>this.requestJSON("GET",r),i=>R(i,this.projectId,e))}async report(e){const r=`/api/v1/projects/${encodeURIComponent(e.projectId)}/campaigns/${encodeURIComponent(e.campaignId)}/reports`;return this.decodeCampaign(()=>this.requestJSON("POST",r,e),i=>j(i,e))}async snapshot(e={}){const{path:r,kind:i}=C(this.projectId,e);return i==="project"?U(await this.requestJSON("GET",r),this.projectId):this.decodeCampaign(()=>this.requestJSON("GET",r),n=>e.findingId!==void 0&&e.campaignId!==void 0?m(n,this.projectId,e.campaignId,e.findingId):S(n,i,this.projectId,e.campaignId))}async finding(e,r){return this.decodeCampaign(()=>this.requestJSON("GET",`/api/v1/projects/${encodeURIComponent(this.projectId)}/campaigns/${encodeURIComponent(e)}/findings/${encodeURIComponent(r)}`),i=>m(i,this.projectId,e,r))}async receipt(e){const r=await this.requestJSON("GET",`/api/v1/receipts/${encodeURIComponent(e)}`);if(!p(r))throw o("receipt view");const i=parseCloudReceipt(r.receipt);if(i.id!==e||i.projectId!==this.projectId)throw o("receipt identity");return i}async decodeCampaign(e,r){const i=await e();try{return r(i)}catch(n){throw o(`campaign response: ${n instanceof Error?n.message:String(n)}`)}}async requestJSON(e,r,i){const n=i===void 0?null:Buffer.from(JSON.stringify(i)),s=await f({body:n,headers:{Accept:"application/json",Authorization:`Bearer ${this.token}`,...n?{"Content-Length":String(n.length),"Content-Type":"application/json"}:{}},method:e,timeoutMs:3e4,maxResponseBytes:this.responses.maxResponseBytes,url:this.target(r)});return parseResponse(s,this.responses.decodeJSON)}async uploadFile(e,r,i){const n=this.target(e),s=T.createReadStream(r),u=new Promise(a=>s.once("close",a));try{const a=await f({body:s,headers:{Authorization:`Bearer ${this.token}`,"Content-Length":String(i),"Content-Type":"application/zip"},method:"PUT",timeoutMs:6e5,url:n});if(a.status<200||a.status>=300)throw g(a)}finally{s.destroy(),await u}}target(e){if(!e)throw new Error("Testkit Cloud returned an empty request URL");const r=new URL(e,this.origin);if(r.origin!==this.origin.origin||r.username||r.password)throw new Error("Testkit Cloud returned a URL outside its configured origin");return r}}export function parseResponse(t,e=JSON.parse){if(t.status<200||t.status>=300)throw g(t);if(t.body.length!==0)try{return e(new TextDecoder("utf-8",{fatal:!0}).decode(t.body))}catch{throw o("JSON response")}}function g(t){let e=null;const r={source:"cloud"};let i=t.body.toString("utf8").trim();try{const n=JSON.parse(i);if(p(n)){typeof n.code=="string"&&(e=n.code),typeof n.detail=="string"&&(i=n.detail);for(const s of["field","required","eligible","detail"])(typeof n[s]=="string"||typeof n[s]=="number"&&Number.isSafeInteger(n[s]))&&(r[s]=n[s])}}catch{}return I(new CloudResponseError(t.status,i||`Testkit Cloud returned HTTP ${String(t.status)}`,e),r)}function b(t){if(!p(t))throw o("publication plan");const e=t.status;if(!l(e,["accepted","pending","rejected"]))throw o("publication status");let r=null;if(t.archive!==void 0&&t.archive!==null){if(!p(t.archive)||t.archive.format!=="zip-v1")throw o("archive format");const i=d(t.archive.maxBytes,"archive byte limit");if(i===0)throw o("archive byte limit");r={url:c(t.archive.url,"archive URL"),maxBytes:Math.min(i,y)}}return{completeUrl:c(t.completeUrl,"completion URL"),status:e,archive:r}}export function parseCloudReceipt(t){if(!p(t))throw o("receipt");const e=t.decision;if(!l(e,["hold","ineligible","ship"]))throw o("receipt decision");if(!Array.isArray(t.decisionReasons))throw o("receipt decision reasons");const r=t.decisionReasons.map(n=>{if(!p(n))throw o("receipt decision reason");return{ruleId:c(n.ruleId,"receipt decision rule ID"),message:c(n.message,"receipt decision message")}}),i=d(t.policyRevision,"receipt policy revision");if(i===0)throw o("receipt policy revision");if(!p(t.regressions))throw o("receipt regressions");return{acceptedAt:c(t.acceptedAt,"receipt acceptance time"),commitSha:c(t.commitSha,"receipt commit SHA",!0),decision:e,decisionReasons:r,fullRun:k(t.fullRun,"receipt full-run flag"),id:c(t.id,"receipt ID"),manifestDigest:c(t.manifestDigest,"receipt manifest digest"),policyId:c(t.policyId,"receipt policy ID"),policyRevision:i,projectId:c(t.projectId,"receipt project ID"),regressions:{fixed:d(t.regressions.fixed,"fixed regression count"),known:d(t.regressions.known,"known regression count"),new:d(t.regressions.new,"new regression count"),returning:d(t.regressions.returning,"returning regression count")},repository:c(t.repository,"receipt repository"),runId:c(t.runId,"receipt run ID"),runStatus:c(t.runStatus,"receipt run status"),testkitVersion:c(t.testkitVersion,"receipt Testkit version")}}function l(t,e){return typeof t=="string"&&e.includes(t)}function k(t,e){if(typeof t!="boolean")throw o(e);return t}function d(t,e){if(!Number.isSafeInteger(t)||t<0)throw o(e);return t}function c(t,e,r=!1){if(typeof t!="string"||!r&&t.length===0)throw o(e);return t}
@@ -1,5 +0,0 @@
1
- export declare function downloadVerifiedArtifact(url: URL, token: string, filePath: string, expected: {
2
- bytes: number;
3
- sha256: string;
4
- }): Promise<void>;
5
- export declare function publishArtifactFile(temporary: string, filePath: string): Promise<void>;
@@ -1 +0,0 @@
1
- import{open as h,link as m,rm as p}from"node:fs/promises";import{createHash as u,randomUUID as b}from"node:crypto";import{request as d,invalidResponse as l}from"./transport.js";import{parseResponse as A}from"./client.js";export async function downloadVerifiedArtifact(a,o,w,t){if(!Number.isSafeInteger(t.bytes)||t.bytes<0||!/^[a-f0-9]{64}$/u.test(t.sha256))throw new Error("Artifact download requires an exact byte count and SHA-256");const i=`${w}.${b()}.partial`,s=await h(i,"wx",384),c=u("sha256");let n=0;try{const f=await d({body:null,headers:{Authorization:`Bearer ${o}`,"Accept-Encoding":"identity"},method:"GET",timeoutMs:6e5,url:a,onResponseChunk:async e=>{if(n+=e.byteLength,n>t.bytes)throw l("artifact exceeds expected bytes");c.update(e);let r=0;for(;r<e.length;){const y=await s.write(e,r,e.length-r);if(!y.bytesWritten)throw new Error("Artifact write made no progress");r+=y.bytesWritten}}});if((f.status<200||f.status>=300)&&A(f),n!==t.bytes||c.digest("hex")!==t.sha256)throw l("artifact byte count or SHA-256");await s.close(),await publishArtifactFile(i,w)}finally{await s.close(),await p(i,{force:!0})}}export async function publishArtifactFile(a,o){await m(a,o)}