@empiricalrun/playwright-utils 0.47.3 → 0.48.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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @empiricalrun/playwright-utils
2
2
 
3
+ ## 0.48.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 12b14e5: feat: webhook testing with inbox worker
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [12b14e5]
12
+ - @empiricalrun/dashboard-client@0.3.0
13
+
14
+ ## 0.47.4
15
+
16
+ ### Patch Changes
17
+
18
+ - 2d6f146: fix: incorrect types bundled
19
+ - Updated dependencies [2d6f146]
20
+ - @empiricalrun/dashboard-client@0.2.1
21
+
3
22
  ## 0.47.3
4
23
 
5
24
  ### Patch Changes
@@ -1,4 +1,4 @@
1
- import type { IDashboardAPIClient } from "@empiricalrun/shared-types/api/base";
1
+ import type { IEmailProxyClient } from "./types";
2
2
  type Link = {
3
3
  text?: string;
4
4
  href?: string;
@@ -11,7 +11,7 @@ export type InboxEmail = {
11
11
  codes: string[];
12
12
  };
13
13
  type InboxClientOptions = {
14
- apiClient: IDashboardAPIClient;
14
+ apiClient: IEmailProxyClient;
15
15
  timeout: number;
16
16
  };
17
17
  export declare class InboxClient {
@@ -1 +1 @@
1
- {"version":3,"file":"inbox-client.d.ts","sourceRoot":"","sources":["../src/inbox-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAE/E,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAmBF,KAAK,kBAAkB,GAAG;IACxB,SAAS,EAAE,mBAAmB,CAAC;IAC/B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qBAAa,WAAW;IACtB,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,kBAAkB;IAK9B,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1D,OAAO,CAAC,UAAU,CAAC;YASR,cAAc;IAgD5B,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,YAAY;IAwBpB,OAAO,CAAC,YAAY;CAYrB"}
1
+ {"version":3,"file":"inbox-client.d.ts","sourceRoot":"","sources":["../src/inbox-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAmBF,KAAK,kBAAkB,GAAG;IACxB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qBAAa,WAAW;IACtB,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,kBAAkB;IAK9B,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1D,OAAO,CAAC,UAAU,CAAC;YASR,cAAc;IAoD5B,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,YAAY;IAwBpB,OAAO,CAAC,YAAY;CAYrB"}
@@ -32,9 +32,9 @@ class InboxClient {
32
32
  const path = `/api/messages?${params.toString()}`;
33
33
  let result;
34
34
  try {
35
- result = await this.apiClient.callInboxProxy({
36
- method: "GET",
37
- path,
35
+ result = await this.apiClient.request("/api/inbox/proxy", {
36
+ method: "POST",
37
+ body: { method: "GET", path },
38
38
  });
39
39
  }
40
40
  catch (err) {
@@ -44,8 +44,9 @@ class InboxClient {
44
44
  }
45
45
  throw new Error("Email provider error: Network request failed");
46
46
  }
47
- if (result.data?.data) {
48
- return result.data.data;
47
+ const message = result.data?.data?.[0];
48
+ if (message) {
49
+ return message;
49
50
  }
50
51
  const elapsed = Date.now() - startTime;
51
52
  if (elapsed + DEFAULT_POLL_DELAY > this.timeout) {
@@ -1,5 +1,4 @@
1
- export type { IDashboardAPIClient } from "@empiricalrun/shared-types/api/base";
2
- import type { IDashboardAPIClient } from "@empiricalrun/shared-types/api/base";
1
+ import type { IEmailProxyClient } from "./types";
3
2
  type Link = {
4
3
  text?: string;
5
4
  href?: string;
@@ -12,7 +11,7 @@ export type MailosaurEmail = {
12
11
  codes: string[];
13
12
  };
14
13
  type MailosaurClientOptions = {
15
- apiClient: IDashboardAPIClient;
14
+ apiClient: IEmailProxyClient;
16
15
  serverId: string;
17
16
  timeout: number;
18
17
  };
@@ -32,4 +31,5 @@ export declare class MailosaurClient {
32
31
  private parseEmailResponse;
33
32
  private sleep;
34
33
  }
34
+ export {};
35
35
  //# sourceMappingURL=mailosaur-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"mailosaur-client.d.ts","sourceRoot":"","sources":["../src/mailosaur-client.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAE/E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,qCAAqC,CAAC;AAE/E,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAiCF,KAAK,sBAAsB,GAAG;IAC5B,SAAS,EAAE,mBAAmB,CAAC;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,sBAAsB;YAM1B,WAAW;IAsBnB,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1D,OAAO,CAAC,cAAc,CAAC;YAqBZ,iBAAiB;YA2EjB,cAAc;IA+B5B,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,KAAK;CAGd"}
1
+ {"version":3,"file":"mailosaur-client.d.ts","sourceRoot":"","sources":["../src/mailosaur-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,KAAK,IAAI,GAAG;IACV,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB,CAAC;AAiCF,KAAK,sBAAsB,GAAG;IAC5B,SAAS,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,qBAAa,eAAe;IAC1B,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,IAAI,EAAE,sBAAsB;YAM1B,WAAW;IAuBnB,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,MAAM,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,GAC1D,OAAO,CAAC,cAAc,CAAC;YAqBZ,iBAAiB;YA2EjB,cAAc;IA+B5B,OAAO,CAAC,kBAAkB;IAmB1B,OAAO,CAAC,KAAK;CAGd"}
@@ -12,10 +12,9 @@ class MailosaurClient {
12
12
  this.apiClient = opts.apiClient;
13
13
  }
14
14
  async makeRequest(method, path, body) {
15
- const result = await this.apiClient.callMailosaurProxy({
16
- method,
17
- path,
18
- body,
15
+ const result = await this.apiClient.request("/api/mailosaur/proxy", {
16
+ method: "POST",
17
+ body: { method, path, body },
19
18
  });
20
19
  return {
21
20
  data: result.data,
@@ -0,0 +1,7 @@
1
+ export interface IEmailProxyClient {
2
+ request<T>(endpoint: string, options: {
3
+ method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
4
+ body?: Record<string, unknown>;
5
+ }): Promise<T>;
6
+ }
7
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,CAAC,EACP,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE;QACP,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,OAAO,GAAG,QAAQ,CAAC;QACpD,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KAChC,GACA,OAAO,CAAC,CAAC,CAAC,CAAC;CACf"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
package/dist/webhook.d.ts CHANGED
@@ -13,8 +13,11 @@ export type WebhookRequest = {
13
13
  created_at: string;
14
14
  updated_at: string;
15
15
  };
16
+ type WebhookProvider = "inbox" | "webhook-site";
16
17
  export declare function getWebhookUrl(options?: {
17
18
  expiry?: number | null;
19
+ provider?: WebhookProvider;
18
20
  }): Promise<string>;
19
21
  export declare function queryWebhookRequests(webhookUrl: string, content: string | string[]): Promise<WebhookRequest[]>;
22
+ export {};
20
23
  //# sourceMappingURL=webhook.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAaF,wBAAsB,aAAa,CACjC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CAAO,GACvC,OAAO,CAAC,MAAM,CAAC,CAwCjB;AAgGD,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GACzB,OAAO,CAAC,cAAc,EAAE,CAAC,CAM3B"}
1
+ {"version":3,"file":"webhook.d.ts","sourceRoot":"","sources":["../src/webhook.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC;IACrC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,KAAK,eAAe,GAAG,OAAO,GAAG,cAAc,CAAC;AA0ShD,wBAAsB,aAAa,CACjC,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,eAAe,CAAA;CAAO,GACnE,OAAO,CAAC,MAAM,CAAC,CAMjB;AAED,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,GACzB,OAAO,CAAC,cAAc,EAAE,CAAC,CAO3B"}
package/dist/webhook.js CHANGED
@@ -3,18 +3,28 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getWebhookUrl = getWebhookUrl;
4
4
  exports.queryWebhookRequests = queryWebhookRequests;
5
5
  const dashboard_client_1 = require("@empiricalrun/dashboard-client");
6
+ // ── webhook.site provider ──
6
7
  const WEBHOOK_SITE_BASE_URL = "https://webhook.site";
7
8
  const DEFAULT_TOKEN_EXPIRY = 604800;
8
- async function getWebhookUrl(options = {}) {
9
+ function escapeQueryTerm(term) {
10
+ return term.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
11
+ }
12
+ function buildContentQuery(content) {
13
+ const terms = Array.isArray(content) ? content : [content];
14
+ if (terms.length === 0 || terms.some((t) => t.length === 0)) {
15
+ throw new Error("content must be a non-empty string or string[]");
16
+ }
17
+ return terms.map((t) => `content:"${escapeQueryTerm(t)}"`).join(" AND ");
18
+ }
19
+ async function webhookSiteGetUrl(options) {
9
20
  const expiry = options.expiry === undefined ? DEFAULT_TOKEN_EXPIRY : options.expiry;
10
21
  if (process.env.EMPIRICALRUN_API_KEY) {
11
22
  const apiClient = new dashboard_client_1.DashboardAPIClient({
12
23
  authType: "project-api-key",
13
24
  });
14
- const result = await apiClient.callWebhookSiteProxy({
25
+ const result = await apiClient.request("/api/webhook-site/proxy", {
15
26
  method: "POST",
16
- path: "/token",
17
- body: { expiry },
27
+ body: { method: "POST", path: "/token", body: { expiry } },
18
28
  });
19
29
  const tokenData = result.data;
20
30
  return `${WEBHOOK_SITE_BASE_URL}/${tokenData.uuid}`;
@@ -38,32 +48,7 @@ async function getWebhookUrl(options = {}) {
38
48
  const { uuid } = await response.json();
39
49
  return `${WEBHOOK_SITE_BASE_URL}/${uuid}`;
40
50
  }
41
- function extractToken(webhookUrl) {
42
- let url;
43
- try {
44
- url = new URL(webhookUrl);
45
- }
46
- catch {
47
- throw new Error(`Invalid webhook URL: ${webhookUrl}`);
48
- }
49
- const parts = url.pathname.split("/").filter(Boolean);
50
- const token = parts[parts.length - 1];
51
- if (!token) {
52
- throw new Error(`Invalid webhook URL (no token in path): ${webhookUrl}`);
53
- }
54
- return token;
55
- }
56
- function escapeQueryTerm(term) {
57
- return term.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
58
- }
59
- function buildContentQuery(content) {
60
- const terms = Array.isArray(content) ? content : [content];
61
- if (terms.length === 0 || terms.some((t) => t.length === 0)) {
62
- throw new Error("content must be a non-empty string or string[]");
63
- }
64
- return terms.map((t) => `content:"${escapeQueryTerm(t)}"`).join(" AND ");
65
- }
66
- async function queryViaProxy(token, content) {
51
+ async function webhookSiteQueryViaProxy(token, content) {
67
52
  const apiClient = new dashboard_client_1.DashboardAPIClient({
68
53
  authType: "project-api-key",
69
54
  });
@@ -72,9 +57,12 @@ async function queryViaProxy(token, content) {
72
57
  sorting: "newest",
73
58
  per_page: "20",
74
59
  });
75
- const result = await apiClient.callWebhookSiteProxy({
76
- method: "GET",
77
- path: `/token/${token}/requests?${params.toString()}`,
60
+ const result = await apiClient.request("/api/webhook-site/proxy", {
61
+ method: "POST",
62
+ body: {
63
+ method: "GET",
64
+ path: `/token/${token}/requests?${params.toString()}`,
65
+ },
78
66
  });
79
67
  const webhookResponse = result.data;
80
68
  if (!webhookResponse ||
@@ -84,7 +72,7 @@ async function queryViaProxy(token, content) {
84
72
  }
85
73
  return webhookResponse.data;
86
74
  }
87
- async function queryDirect(token, content) {
75
+ async function webhookSiteQueryDirect(token, content) {
88
76
  const apiKey = process.env.WEBHOOK_SITE_API_KEY;
89
77
  if (!apiKey) {
90
78
  throw new Error("Either EMPIRICALRUN_API_KEY or WEBHOOK_SITE_API_KEY must be set");
@@ -111,10 +99,142 @@ async function queryDirect(token, content) {
111
99
  }
112
100
  return json.data;
113
101
  }
102
+ async function webhookSiteQuery(token, content) {
103
+ if (process.env.EMPIRICALRUN_API_KEY) {
104
+ return webhookSiteQueryViaProxy(token, content);
105
+ }
106
+ return webhookSiteQueryDirect(token, content);
107
+ }
108
+ // ── inbox provider ──
109
+ const INBOX_WORKER_URL = "https://inbox.empirical-run.workers.dev";
110
+ function inboxRowToWebhookRequest(row) {
111
+ let headers = {};
112
+ try {
113
+ const parsed = JSON.parse(row.headers || "{}");
114
+ headers = Object.fromEntries(Object.entries(parsed).map(([k, v]) => [
115
+ k,
116
+ Array.isArray(v) ? v : [String(v)],
117
+ ]));
118
+ }
119
+ catch { }
120
+ let query = null;
121
+ try {
122
+ const parsed = JSON.parse(row.query_params || "{}");
123
+ if (Object.keys(parsed).length > 0)
124
+ query = parsed;
125
+ }
126
+ catch { }
127
+ return {
128
+ uuid: row.id,
129
+ token_id: row.path_id,
130
+ method: row.method,
131
+ content: row.body || "",
132
+ headers,
133
+ url: "",
134
+ query,
135
+ ip: "",
136
+ hostname: "",
137
+ user_agent: headers["user-agent"]?.[0] || "",
138
+ size: (row.body || "").length,
139
+ created_at: row.received_at,
140
+ updated_at: row.received_at,
141
+ };
142
+ }
143
+ async function inboxGetUrl() {
144
+ if (process.env.EMPIRICALRUN_API_KEY) {
145
+ const apiClient = new dashboard_client_1.DashboardAPIClient({
146
+ authType: "project-api-key",
147
+ });
148
+ const result = await apiClient.request("/api/inbox/proxy", {
149
+ method: "POST",
150
+ body: { method: "POST", path: "/api/webhooks/token" },
151
+ });
152
+ const tokenData = result.data;
153
+ return `${INBOX_WORKER_URL}/api/webhooks/${tokenData.uuid}`;
154
+ }
155
+ const response = await fetch(`${INBOX_WORKER_URL}/api/webhooks/token`, {
156
+ method: "POST",
157
+ });
158
+ if (!response.ok) {
159
+ const body = await response.text().catch(() => "");
160
+ throw new Error(`inbox webhook error [${response.status} ${response.statusText}]: ${body}`);
161
+ }
162
+ const { uuid } = await response.json();
163
+ return `${INBOX_WORKER_URL}/api/webhooks/${uuid}`;
164
+ }
165
+ async function inboxQuery(token, content) {
166
+ const terms = Array.isArray(content) ? content : [content];
167
+ const params = new URLSearchParams({
168
+ path_id: token,
169
+ sorting: "newest",
170
+ per_page: "20",
171
+ });
172
+ for (const t of terms) {
173
+ params.append("content", t);
174
+ }
175
+ if (process.env.EMPIRICALRUN_API_KEY) {
176
+ const apiClient = new dashboard_client_1.DashboardAPIClient({
177
+ authType: "project-api-key",
178
+ });
179
+ const result = await apiClient.request("/api/inbox/proxy", {
180
+ method: "POST",
181
+ body: {
182
+ method: "GET",
183
+ path: `/api/webhooks?${params.toString()}`,
184
+ },
185
+ });
186
+ const response = result.data;
187
+ if (!response || !Array.isArray(response.data)) {
188
+ return [];
189
+ }
190
+ return response.data.map(inboxRowToWebhookRequest);
191
+ }
192
+ const rawResponse = await fetch(`${INBOX_WORKER_URL}/api/webhooks?${params.toString()}`);
193
+ if (!rawResponse.ok) {
194
+ const body = await rawResponse.text().catch(() => "");
195
+ throw new Error(`inbox webhook error [${rawResponse.status} ${rawResponse.statusText}]: ${body}`);
196
+ }
197
+ const json = await rawResponse.json().catch((e) => {
198
+ throw new Error(`inbox webhook returned invalid JSON: ${String(e)}`);
199
+ });
200
+ if (!json || !Array.isArray(json.data)) {
201
+ return [];
202
+ }
203
+ return json.data.map(inboxRowToWebhookRequest);
204
+ }
205
+ // ── public API ──
206
+ function extractToken(webhookUrl) {
207
+ let url;
208
+ try {
209
+ url = new URL(webhookUrl);
210
+ }
211
+ catch {
212
+ throw new Error(`Invalid webhook URL: ${webhookUrl}`);
213
+ }
214
+ const parts = url.pathname.split("/").filter(Boolean);
215
+ const token = parts[parts.length - 1];
216
+ if (!token) {
217
+ throw new Error(`Invalid webhook URL (no token in path): ${webhookUrl}`);
218
+ }
219
+ return token;
220
+ }
221
+ function detectProvider(webhookUrl) {
222
+ if (webhookUrl.includes("webhook.site"))
223
+ return "webhook-site";
224
+ return "inbox";
225
+ }
226
+ async function getWebhookUrl(options = {}) {
227
+ const provider = options.provider || "webhook-site";
228
+ if (provider === "webhook-site") {
229
+ return webhookSiteGetUrl(options);
230
+ }
231
+ return inboxGetUrl();
232
+ }
114
233
  async function queryWebhookRequests(webhookUrl, content) {
115
234
  const token = extractToken(webhookUrl);
116
- if (process.env.EMPIRICALRUN_API_KEY) {
117
- return queryViaProxy(token, content);
235
+ const provider = detectProvider(webhookUrl);
236
+ if (provider === "webhook-site") {
237
+ return webhookSiteQuery(token, content);
118
238
  }
119
- return queryDirect(token, content);
239
+ return inboxQuery(token, content);
120
240
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@empiricalrun/playwright-utils",
3
- "version": "0.47.3",
3
+ "version": "0.48.0",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -44,7 +44,7 @@
44
44
  "rimraf": "^6.0.1",
45
45
  "ts-morph": "^23.0.0",
46
46
  "@empiricalrun/cua": "^0.3.0",
47
- "@empiricalrun/dashboard-client": "^0.2.0",
47
+ "@empiricalrun/dashboard-client": "^0.3.0",
48
48
  "@empiricalrun/llm": "^0.26.0",
49
49
  "@empiricalrun/r2-uploader": "^0.9.1",
50
50
  "@empiricalrun/reporter": "^0.28.1"
@@ -1 +1 @@
1
- {"root":["./src/email.ts","./src/inbox-client.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/postgres.ts","./src/telemetry.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/devices/types.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/reporter/attachment-cleanup-test-reporter.ts","./src/reporter/attachment-cleanup.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/failing-line.ts","./src/reporter/harness.ts","./src/reporter/ibr-utils.ts","./src/reporter/incremental-blob-reporter.ts","./src/reporter/lifecycle-events.ts","./src/reporter/local-test.ts","./src/reporter/reporter-state.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/coverage.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"],"version":"5.8.3"}
1
+ {"root":["./src/email.ts","./src/inbox-client.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/postgres.ts","./src/telemetry.ts","./src/types.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/devices/types.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/reporter/attachment-cleanup-test-reporter.ts","./src/reporter/attachment-cleanup.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/failing-line.ts","./src/reporter/harness.ts","./src/reporter/ibr-utils.ts","./src/reporter/incremental-blob-reporter.ts","./src/reporter/lifecycle-events.ts","./src/reporter/local-test.ts","./src/reporter/reporter-state.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/coverage.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"],"version":"5.8.3"}