@empiricalrun/playwright-utils 0.65.1 → 0.66.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @empiricalrun/playwright-utils
2
2
 
3
+ ## 0.66.1
4
+
5
+ ### Patch Changes
6
+
7
+ - 1598d3d: Fix `@empiricalrun/playwright-utils/test` failing to load with "Cannot find module '@empiricalrun/shared-types/playwright-utils'"
8
+ - @empiricalrun/cua@0.6.0
9
+ - @empiricalrun/reporter@0.30.3
10
+
11
+ ## 0.66.0
12
+
13
+ ### Minor Changes
14
+
15
+ - 53ed84b: `baseTestFixture` holds Playwright `lock` names across every shard of an Empirical test run, using a KV lease per lock with a `lock` annotation for time spent waiting
16
+ - 3ab73d4: `KVClient` uses the `/api/kv` API: `getEntry`, conditional `set` (`ifAbsent`, `ifVersion`) with `KvPreconditionFailedError`, `refresh`, `delete`, `list` and `entries`. Keys are limited to `A-Z a-z 0-9 . _ : -`, values to 1 MB and TTLs to 30 days.
17
+
3
18
  ## 0.65.1
4
19
 
5
20
  ### Patch Changes
@@ -6,9 +6,22 @@ export type ProviderProxyRequest = components["schemas"]["PlaywrightUtilsProxyRe
6
6
  export type ProviderProxyResponse = components["schemas"]["PlaywrightUtilsProxyResponse"];
7
7
  export type TwoCaptchaProxyRequest = components["schemas"]["TwoCaptchaProxyRequest"];
8
8
  export type TwoCaptchaProxyResponse = components["schemas"]["TwoCaptchaProxyResponse"];
9
- export type ProjectKvGetResponse = components["schemas"]["ProjectKvGetResponse"];
10
- export type ProjectKvSetRequest = NonNullable<paths["/api/projects/kv"]["post"]["requestBody"]>["content"]["application/json"];
11
- export type ProjectKvSetResponse = components["schemas"]["ProjectKvSetResponse"];
9
+ export type KvEntryRecord = components["schemas"]["KvEntry"];
10
+ export type KvListQuery = NonNullable<paths["/api/kv"]["get"]["parameters"]["query"]>;
11
+ export type KvListPageRecord = components["schemas"]["KvListResponse"]["data"];
12
+ export type KvPutBody = components["schemas"]["PutKvEntryRequest"];
13
+ export type KvWriteCondition = {
14
+ ifAbsent: true;
15
+ } | {
16
+ ifVersion: number;
17
+ };
18
+ export type KvConditionalResult<T> = {
19
+ ok: true;
20
+ value: T;
21
+ } | {
22
+ ok: false;
23
+ current: KvEntryRecord | null;
24
+ };
12
25
  export type TelemetryTrackRequest = components["schemas"]["TelemetryTrackRequest"];
13
26
  export type TestCaseRunEndEventRequest = NonNullable<paths["/api/test-runs/events"]["put"]["requestBody"]>["content"]["application/json"];
14
27
  export type TestReportUploadUrlsRequest = components["schemas"]["CreateTestReportUploadUrlsRequest"];
@@ -26,7 +39,9 @@ export declare class NonRetryableHTTPError extends HTTPError {
26
39
  }
27
40
  export declare class RetryableHTTPError extends HTTPError {
28
41
  }
29
- export declare function fetchWithRetry(input: RequestInfo | URL, init?: RequestInit): Promise<Response>;
42
+ export declare function fetchWithRetry(input: RequestInfo | URL, init?: RequestInit, { acceptStatuses }?: {
43
+ acceptStatuses?: readonly number[];
44
+ }): Promise<Response>;
30
45
  export declare class ApiWorkerClient {
31
46
  private baseUrl;
32
47
  readonly client: OpenApiWorkerClient;
@@ -35,8 +50,12 @@ export declare class ApiWorkerClient {
35
50
  baseUrl?: string;
36
51
  });
37
52
  getBaseUrl(): string;
38
- getProjectKv(key: string): Promise<ProjectKvGetResponse>;
39
- setProjectKv(body: ProjectKvSetRequest): Promise<ProjectKvSetResponse>;
53
+ getKvEntry(key: string): Promise<KvEntryRecord | null>;
54
+ listKvEntries(query: KvListQuery): Promise<KvListPageRecord>;
55
+ putKvEntry(key: string, body: KvPutBody, condition?: KvWriteCondition): Promise<KvConditionalResult<KvEntryRecord>>;
56
+ refreshKvEntry(key: string, ttl: number, ifVersion?: number): Promise<KvConditionalResult<KvEntryRecord> | null>;
57
+ deleteKvEntry(key: string, ifVersion?: number): Promise<KvConditionalResult<boolean>>;
58
+ private conditional;
40
59
  postProviderProxy(path: ProviderProxyPath, body: ProviderProxyRequest): Promise<ProviderProxyResponse>;
41
60
  putTestCaseRunEndEvent(body: TestCaseRunEndEventRequest): Promise<void>;
42
61
  putTestCaseRunBeginEvent(body: TestCaseRunBeginEventRequest): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"api-worker-client.d.ts","sourceRoot":"","sources":["../src/api-worker-client.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"api-worker-client.d.ts","sourceRoot":"","sources":["../src/api-worker-client.ts"],"names":[],"mappings":"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"}
@@ -46,7 +46,7 @@ function describeRequest(input, init) {
46
46
  const url = input instanceof Request ? input.url : input.toString();
47
47
  return { method, url };
48
48
  }
49
- async function fetchWithRetry(input, init) {
49
+ async function fetchWithRetry(input, init, { acceptStatuses = [] } = {}) {
50
50
  // openapi-fetch passes a Request object, and the first fetch consumes its
51
51
  // body: re-sending it throws "Request object that has already been used",
52
52
  // so every POST/PUT retry failed. Keep an unread copy and clone per attempt.
@@ -54,7 +54,7 @@ async function fetchWithRetry(input, init) {
54
54
  return (0, async_retry_1.default)(async (bail) => {
55
55
  const attemptInput = pristine instanceof Request ? pristine.clone() : pristine;
56
56
  const response = await fetch(attemptInput, init);
57
- if (!response.ok) {
57
+ if (!response.ok && !acceptStatuses.includes(response.status)) {
58
58
  const { method, url } = describeRequest(input, init);
59
59
  const message = `HTTP error in ${method} request to ${url}! Status code: ${response.status}.`;
60
60
  if (response.status < 500) {
@@ -90,6 +90,24 @@ function formatError(error) {
90
90
  return String(error);
91
91
  }
92
92
  }
93
+ const KV_EXPECTED_STATUSES = [404, 412];
94
+ const kvFetch = (input) => fetchWithRetry(input, undefined, { acceptStatuses: KV_EXPECTED_STATUSES });
95
+ function versionOf(ifVersion) {
96
+ return ifVersion === undefined ? undefined : { ifVersion };
97
+ }
98
+ function conditionHeaders(condition) {
99
+ if (!condition) {
100
+ return undefined;
101
+ }
102
+ return "ifAbsent" in condition
103
+ ? { "if-none-match": "*" }
104
+ : { "if-match": `"${condition.ifVersion}"` };
105
+ }
106
+ function preconditionCurrent(error) {
107
+ const current = error
108
+ ?.error?.current;
109
+ return current ?? null;
110
+ }
93
111
  class ApiWorkerClient {
94
112
  baseUrl;
95
113
  client;
@@ -111,17 +129,63 @@ class ApiWorkerClient {
111
129
  getBaseUrl() {
112
130
  return this.baseUrl;
113
131
  }
114
- async getProjectKv(key) {
115
- const result = await this.client.GET("/api/projects/kv", {
116
- params: { query: { key } },
132
+ async getKvEntry(key) {
133
+ const result = await this.client.GET("/api/kv/{key}", {
134
+ params: { path: { key } },
135
+ fetch: kvFetch,
117
136
  });
118
- return this.unwrap(result, "GET /api/projects/kv");
137
+ if (result.response.status === 404) {
138
+ return null;
139
+ }
140
+ return this.unwrap(result, "GET /api/kv/{key}").data.entry;
141
+ }
142
+ async listKvEntries(query) {
143
+ const result = await this.client.GET("/api/kv", {
144
+ params: { query },
145
+ });
146
+ return this.unwrap(result, "GET /api/kv").data;
119
147
  }
120
- async setProjectKv(body) {
121
- const result = await this.client.POST("/api/projects/kv", {
148
+ async putKvEntry(key, body, condition) {
149
+ const result = await this.client.PUT("/api/kv/{key}", {
150
+ params: { path: { key }, header: conditionHeaders(condition) },
122
151
  body,
152
+ fetch: kvFetch,
153
+ });
154
+ return this.conditional(result, "PUT /api/kv/{key}");
155
+ }
156
+ async refreshKvEntry(key, ttl, ifVersion) {
157
+ const result = await this.client.PATCH("/api/kv/{key}", {
158
+ params: { path: { key }, header: conditionHeaders(versionOf(ifVersion)) },
159
+ body: { ttl },
160
+ fetch: kvFetch,
123
161
  });
124
- return this.unwrap(result, "POST /api/projects/kv");
162
+ if (result.response.status === 404) {
163
+ return null;
164
+ }
165
+ return this.conditional(result, "PATCH /api/kv/{key}");
166
+ }
167
+ async deleteKvEntry(key, ifVersion) {
168
+ const result = await this.client.DELETE("/api/kv/{key}", {
169
+ params: { path: { key }, header: conditionHeaders(versionOf(ifVersion)) },
170
+ fetch: kvFetch,
171
+ });
172
+ if (result.response.status === 404) {
173
+ return { ok: true, value: false };
174
+ }
175
+ if (result.response.status === 412) {
176
+ return { ok: false, current: preconditionCurrent(result.error) };
177
+ }
178
+ if (result.response.ok) {
179
+ return { ok: true, value: true };
180
+ }
181
+ return this.unwrap(result, "DELETE /api/kv/{key}");
182
+ }
183
+ conditional(result, label) {
184
+ if (result.response.status === 412) {
185
+ return { ok: false, current: preconditionCurrent(result.error) };
186
+ }
187
+ const { data } = this.unwrap(result, label);
188
+ return { ok: true, value: data.entry };
125
189
  }
126
190
  async postProviderProxy(path, body) {
127
191
  const result = await this.client.POST(path, {