@browserstack/mcp-server 1.2.15-beta.2 → 1.2.16-beta.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/dist/server-factory.js +0 -4
- package/dist/tools/percy-sdk.js +20 -11
- package/dist/tools/testmanagement-utils/get-testplan.d.ts +16 -0
- package/dist/tools/testmanagement-utils/get-testplan.js +99 -0
- package/dist/tools/testmanagement-utils/list-folders.d.ts +16 -0
- package/dist/tools/testmanagement-utils/list-folders.js +77 -0
- package/dist/tools/testmanagement-utils/list-testcases.js +1 -1
- package/dist/tools/testmanagement-utils/list-testplans.d.ts +15 -0
- package/dist/tools/testmanagement-utils/list-testplans.js +75 -0
- package/dist/tools/testmanagement-utils/update-testcase.d.ts +16 -0
- package/dist/tools/testmanagement-utils/update-testcase.js +133 -10
- package/dist/tools/testmanagement.d.ts +15 -0
- package/dist/tools/testmanagement.js +73 -2
- package/package.json +2 -3
- package/dist/lib/percy-api/auth.d.ts +0 -41
- package/dist/lib/percy-api/auth.js +0 -96
- package/dist/lib/percy-api/cache.d.ts +0 -28
- package/dist/lib/percy-api/cache.js +0 -48
- package/dist/lib/percy-api/client.d.ts +0 -69
- package/dist/lib/percy-api/client.js +0 -275
- package/dist/lib/percy-api/errors.d.ts +0 -15
- package/dist/lib/percy-api/errors.js +0 -52
- package/dist/lib/percy-api/formatter.d.ts +0 -16
- package/dist/lib/percy-api/formatter.js +0 -344
- package/dist/lib/percy-api/percy-auth.d.ts +0 -43
- package/dist/lib/percy-api/percy-auth.js +0 -137
- package/dist/lib/percy-api/percy-error-handler.d.ts +0 -24
- package/dist/lib/percy-api/percy-error-handler.js +0 -302
- package/dist/lib/percy-api/percy-session.d.ts +0 -42
- package/dist/lib/percy-api/percy-session.js +0 -87
- package/dist/lib/percy-api/polling.d.ts +0 -26
- package/dist/lib/percy-api/polling.js +0 -42
- package/dist/lib/percy-api/types.d.ts +0 -56
- package/dist/lib/percy-api/types.js +0 -76
- package/dist/tools/percy-mcp/advanced/branchline-operations.d.ts +0 -16
- package/dist/tools/percy-mcp/advanced/branchline-operations.js +0 -81
- package/dist/tools/percy-mcp/advanced/manage-variants.d.ts +0 -16
- package/dist/tools/percy-mcp/advanced/manage-variants.js +0 -155
- package/dist/tools/percy-mcp/advanced/manage-visual-monitoring.d.ts +0 -16
- package/dist/tools/percy-mcp/advanced/manage-visual-monitoring.js +0 -171
- package/dist/tools/percy-mcp/auth/auth-status.d.ts +0 -3
- package/dist/tools/percy-mcp/auth/auth-status.js +0 -131
- package/dist/tools/percy-mcp/core/approve-build.d.ts +0 -14
- package/dist/tools/percy-mcp/core/approve-build.js +0 -97
- package/dist/tools/percy-mcp/core/get-build-items.d.ts +0 -13
- package/dist/tools/percy-mcp/core/get-build-items.js +0 -65
- package/dist/tools/percy-mcp/core/get-build.d.ts +0 -10
- package/dist/tools/percy-mcp/core/get-build.js +0 -16
- package/dist/tools/percy-mcp/core/get-comparison.d.ts +0 -11
- package/dist/tools/percy-mcp/core/get-comparison.js +0 -59
- package/dist/tools/percy-mcp/core/get-snapshot.d.ts +0 -10
- package/dist/tools/percy-mcp/core/get-snapshot.js +0 -40
- package/dist/tools/percy-mcp/core/list-builds.d.ts +0 -14
- package/dist/tools/percy-mcp/core/list-builds.js +0 -45
- package/dist/tools/percy-mcp/core/list-projects.d.ts +0 -12
- package/dist/tools/percy-mcp/core/list-projects.js +0 -51
- package/dist/tools/percy-mcp/creation/create-app-snapshot.d.ts +0 -12
- package/dist/tools/percy-mcp/creation/create-app-snapshot.js +0 -29
- package/dist/tools/percy-mcp/creation/create-build.d.ts +0 -19
- package/dist/tools/percy-mcp/creation/create-build.js +0 -68
- package/dist/tools/percy-mcp/creation/create-comparison.d.ts +0 -18
- package/dist/tools/percy-mcp/creation/create-comparison.js +0 -90
- package/dist/tools/percy-mcp/creation/create-snapshot.d.ts +0 -17
- package/dist/tools/percy-mcp/creation/create-snapshot.js +0 -99
- package/dist/tools/percy-mcp/creation/finalize-build.d.ts +0 -12
- package/dist/tools/percy-mcp/creation/finalize-build.js +0 -33
- package/dist/tools/percy-mcp/creation/finalize-comparison.d.ts +0 -10
- package/dist/tools/percy-mcp/creation/finalize-comparison.js +0 -16
- package/dist/tools/percy-mcp/creation/finalize-snapshot.d.ts +0 -12
- package/dist/tools/percy-mcp/creation/finalize-snapshot.js +0 -33
- package/dist/tools/percy-mcp/creation/upload-resource.d.ts +0 -15
- package/dist/tools/percy-mcp/creation/upload-resource.js +0 -43
- package/dist/tools/percy-mcp/creation/upload-tile.d.ts +0 -11
- package/dist/tools/percy-mcp/creation/upload-tile.js +0 -53
- package/dist/tools/percy-mcp/diagnostics/analyze-logs-realtime.d.ts +0 -13
- package/dist/tools/percy-mcp/diagnostics/analyze-logs-realtime.js +0 -65
- package/dist/tools/percy-mcp/diagnostics/get-build-logs.d.ts +0 -17
- package/dist/tools/percy-mcp/diagnostics/get-build-logs.js +0 -74
- package/dist/tools/percy-mcp/diagnostics/get-network-logs.d.ts +0 -5
- package/dist/tools/percy-mcp/diagnostics/get-network-logs.js +0 -21
- package/dist/tools/percy-mcp/diagnostics/get-suggestions.d.ts +0 -7
- package/dist/tools/percy-mcp/diagnostics/get-suggestions.js +0 -24
- package/dist/tools/percy-mcp/index.d.ts +0 -36
- package/dist/tools/percy-mcp/index.js +0 -1137
- package/dist/tools/percy-mcp/intelligence/get-ai-analysis.d.ts +0 -15
- package/dist/tools/percy-mcp/intelligence/get-ai-analysis.js +0 -166
- package/dist/tools/percy-mcp/intelligence/get-ai-quota.d.ts +0 -9
- package/dist/tools/percy-mcp/intelligence/get-ai-quota.js +0 -73
- package/dist/tools/percy-mcp/intelligence/get-build-summary.d.ts +0 -11
- package/dist/tools/percy-mcp/intelligence/get-build-summary.js +0 -78
- package/dist/tools/percy-mcp/intelligence/get-rca.d.ts +0 -6
- package/dist/tools/percy-mcp/intelligence/get-rca.js +0 -153
- package/dist/tools/percy-mcp/intelligence/suggest-prompt.d.ts +0 -15
- package/dist/tools/percy-mcp/intelligence/suggest-prompt.js +0 -86
- package/dist/tools/percy-mcp/intelligence/trigger-ai-recompute.d.ts +0 -16
- package/dist/tools/percy-mcp/intelligence/trigger-ai-recompute.js +0 -64
- package/dist/tools/percy-mcp/management/create-project.d.ts +0 -14
- package/dist/tools/percy-mcp/management/create-project.js +0 -52
- package/dist/tools/percy-mcp/management/get-usage-stats.d.ts +0 -12
- package/dist/tools/percy-mcp/management/get-usage-stats.js +0 -61
- package/dist/tools/percy-mcp/management/manage-browser-targets.d.ts +0 -12
- package/dist/tools/percy-mcp/management/manage-browser-targets.js +0 -136
- package/dist/tools/percy-mcp/management/manage-comments.d.ts +0 -14
- package/dist/tools/percy-mcp/management/manage-comments.js +0 -147
- package/dist/tools/percy-mcp/management/manage-ignored-regions.d.ts +0 -18
- package/dist/tools/percy-mcp/management/manage-ignored-regions.js +0 -182
- package/dist/tools/percy-mcp/management/manage-project-settings.d.ts +0 -16
- package/dist/tools/percy-mcp/management/manage-project-settings.js +0 -97
- package/dist/tools/percy-mcp/management/manage-tokens.d.ts +0 -14
- package/dist/tools/percy-mcp/management/manage-tokens.js +0 -90
- package/dist/tools/percy-mcp/management/manage-webhooks.d.ts +0 -15
- package/dist/tools/percy-mcp/management/manage-webhooks.js +0 -180
- package/dist/tools/percy-mcp/v2/auth-status.d.ts +0 -3
- package/dist/tools/percy-mcp/v2/auth-status.js +0 -80
- package/dist/tools/percy-mcp/v2/clone-build.d.ts +0 -24
- package/dist/tools/percy-mcp/v2/clone-build.js +0 -539
- package/dist/tools/percy-mcp/v2/create-app-build.d.ts +0 -28
- package/dist/tools/percy-mcp/v2/create-app-build.js +0 -442
- package/dist/tools/percy-mcp/v2/create-build.d.ts +0 -16
- package/dist/tools/percy-mcp/v2/create-build.js +0 -601
- package/dist/tools/percy-mcp/v2/create-project.d.ts +0 -8
- package/dist/tools/percy-mcp/v2/create-project.js +0 -33
- package/dist/tools/percy-mcp/v2/discover-urls.d.ts +0 -7
- package/dist/tools/percy-mcp/v2/discover-urls.js +0 -38
- package/dist/tools/percy-mcp/v2/figma-baseline.d.ts +0 -7
- package/dist/tools/percy-mcp/v2/figma-baseline.js +0 -18
- package/dist/tools/percy-mcp/v2/figma-build.d.ts +0 -7
- package/dist/tools/percy-mcp/v2/figma-build.js +0 -39
- package/dist/tools/percy-mcp/v2/figma-link.d.ts +0 -6
- package/dist/tools/percy-mcp/v2/figma-link.js +0 -27
- package/dist/tools/percy-mcp/v2/get-ai-summary.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/get-ai-summary.js +0 -109
- package/dist/tools/percy-mcp/v2/get-build-detail.d.ts +0 -22
- package/dist/tools/percy-mcp/v2/get-build-detail.js +0 -567
- package/dist/tools/percy-mcp/v2/get-builds.d.ts +0 -8
- package/dist/tools/percy-mcp/v2/get-builds.js +0 -63
- package/dist/tools/percy-mcp/v2/get-comparison.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/get-comparison.js +0 -94
- package/dist/tools/percy-mcp/v2/get-devices.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/get-devices.js +0 -33
- package/dist/tools/percy-mcp/v2/get-insights.d.ts +0 -7
- package/dist/tools/percy-mcp/v2/get-insights.js +0 -52
- package/dist/tools/percy-mcp/v2/get-projects.d.ts +0 -6
- package/dist/tools/percy-mcp/v2/get-projects.js +0 -41
- package/dist/tools/percy-mcp/v2/get-snapshot.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/get-snapshot.js +0 -96
- package/dist/tools/percy-mcp/v2/get-test-case-history.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/get-test-case-history.js +0 -20
- package/dist/tools/percy-mcp/v2/get-test-cases.d.ts +0 -6
- package/dist/tools/percy-mcp/v2/get-test-cases.js +0 -36
- package/dist/tools/percy-mcp/v2/index.d.ts +0 -35
- package/dist/tools/percy-mcp/v2/index.js +0 -544
- package/dist/tools/percy-mcp/v2/list-integrations.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/list-integrations.js +0 -41
- package/dist/tools/percy-mcp/v2/manage-domains.d.ts +0 -8
- package/dist/tools/percy-mcp/v2/manage-domains.js +0 -33
- package/dist/tools/percy-mcp/v2/manage-insights-email.d.ts +0 -8
- package/dist/tools/percy-mcp/v2/manage-insights-email.js +0 -49
- package/dist/tools/percy-mcp/v2/manage-usage-alerts.d.ts +0 -10
- package/dist/tools/percy-mcp/v2/manage-usage-alerts.js +0 -43
- package/dist/tools/percy-mcp/v2/migrate-integrations.d.ts +0 -6
- package/dist/tools/percy-mcp/v2/migrate-integrations.js +0 -20
- package/dist/tools/percy-mcp/v2/preview-comparison.d.ts +0 -5
- package/dist/tools/percy-mcp/v2/preview-comparison.js +0 -17
- package/dist/tools/percy-mcp/v2/search-build-items.d.ts +0 -12
- package/dist/tools/percy-mcp/v2/search-build-items.js +0 -45
- package/dist/tools/percy-mcp/workflows/auto-triage.d.ts +0 -7
- package/dist/tools/percy-mcp/workflows/auto-triage.js +0 -82
- package/dist/tools/percy-mcp/workflows/clone-build.d.ts +0 -22
- package/dist/tools/percy-mcp/workflows/clone-build.js +0 -414
- package/dist/tools/percy-mcp/workflows/create-percy-build.d.ts +0 -32
- package/dist/tools/percy-mcp/workflows/create-percy-build.js +0 -434
- package/dist/tools/percy-mcp/workflows/debug-failed-build.d.ts +0 -5
- package/dist/tools/percy-mcp/workflows/debug-failed-build.js +0 -122
- package/dist/tools/percy-mcp/workflows/diff-explain.d.ts +0 -6
- package/dist/tools/percy-mcp/workflows/diff-explain.js +0 -147
- package/dist/tools/percy-mcp/workflows/pr-visual-report.d.ts +0 -8
- package/dist/tools/percy-mcp/workflows/pr-visual-report.js +0 -184
- package/dist/tools/percy-mcp/workflows/run-tests.d.ts +0 -17
- package/dist/tools/percy-mcp/workflows/run-tests.js +0 -107
- package/dist/tools/percy-mcp/workflows/snapshot-urls.d.ts +0 -18
- package/dist/tools/percy-mcp/workflows/snapshot-urls.js +0 -197
|
@@ -1,275 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Percy API HTTP client.
|
|
3
|
-
*
|
|
4
|
-
* Uses native `fetch` (consistent with existing Percy tools in this repo).
|
|
5
|
-
* Handles JSON:API deserialization, rate limiting, and error enrichment.
|
|
6
|
-
*
|
|
7
|
-
* SECURITY: Token values are NEVER logged or exposed in error messages.
|
|
8
|
-
*/
|
|
9
|
-
import { getPercyHeaders, getPercyApiBaseUrl } from "./auth.js";
|
|
10
|
-
import { enrichPercyError } from "./errors.js";
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// Helpers – kebab-case to camelCase
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
function kebabToCamel(str) {
|
|
15
|
-
return str.replace(/-([a-z0-9])/g, (_, char) => char.toUpperCase());
|
|
16
|
-
}
|
|
17
|
-
function camelCaseKeys(obj) {
|
|
18
|
-
if (obj === null || obj === undefined) {
|
|
19
|
-
return obj;
|
|
20
|
-
}
|
|
21
|
-
if (Array.isArray(obj)) {
|
|
22
|
-
return obj.map(camelCaseKeys);
|
|
23
|
-
}
|
|
24
|
-
if (typeof obj === "object") {
|
|
25
|
-
const result = {};
|
|
26
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
27
|
-
const camelKey = kebabToCamel(key);
|
|
28
|
-
result[camelKey] =
|
|
29
|
-
value !== null && typeof value === "object"
|
|
30
|
-
? camelCaseKeys(value)
|
|
31
|
-
: value;
|
|
32
|
-
}
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
return obj;
|
|
36
|
-
}
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
// JSON:API Deserializer
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
/**
|
|
41
|
-
* Builds a lookup index of included resources keyed by `type:id`.
|
|
42
|
-
*/
|
|
43
|
-
function buildIncludedIndex(included) {
|
|
44
|
-
const index = new Map();
|
|
45
|
-
for (const resource of included) {
|
|
46
|
-
const flattened = flattenResource(resource);
|
|
47
|
-
index.set(`${resource.type}:${resource.id}`, flattened);
|
|
48
|
-
}
|
|
49
|
-
return index;
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Flattens a single JSON:API resource — merges `attributes` into the top
|
|
53
|
-
* level alongside `id` and `type`, converting keys to camelCase.
|
|
54
|
-
*/
|
|
55
|
-
function flattenResource(resource) {
|
|
56
|
-
const attrs = resource.attributes
|
|
57
|
-
? camelCaseKeys(resource.attributes)
|
|
58
|
-
: {};
|
|
59
|
-
return {
|
|
60
|
-
id: resource.id,
|
|
61
|
-
type: resource.type,
|
|
62
|
-
...attrs,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Resolves relationships for a resource against the included index.
|
|
67
|
-
* Returns the resolved object(s) or the raw { id, type } ref when not found.
|
|
68
|
-
*/
|
|
69
|
-
function resolveRelationships(resource, index) {
|
|
70
|
-
if (!resource.relationships) {
|
|
71
|
-
return {};
|
|
72
|
-
}
|
|
73
|
-
const resolved = {};
|
|
74
|
-
for (const [relName, relValue] of Object.entries(resource.relationships)) {
|
|
75
|
-
const camelName = kebabToCamel(relName);
|
|
76
|
-
const { data } = relValue;
|
|
77
|
-
if (data === null || data === undefined) {
|
|
78
|
-
resolved[camelName] = null;
|
|
79
|
-
}
|
|
80
|
-
else if (Array.isArray(data)) {
|
|
81
|
-
resolved[camelName] = data.map((ref) => index.get(`${ref.type}:${ref.id}`) ?? { id: ref.id, type: ref.type });
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
resolved[camelName] = index.get(`${data.type}:${data.id}`) ?? {
|
|
85
|
-
id: data.id,
|
|
86
|
-
type: data.type,
|
|
87
|
-
};
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return resolved;
|
|
91
|
-
}
|
|
92
|
-
/**
|
|
93
|
-
* Deserializes a JSON:API envelope into plain objects.
|
|
94
|
-
*
|
|
95
|
-
* - `data: null` → returns `null`
|
|
96
|
-
* - `data: []` → returns `[]`
|
|
97
|
-
* - `data: { ... }` → returns a single deserialized object
|
|
98
|
-
* - `data: [{ ... }, ...]` → returns an array of deserialized objects
|
|
99
|
-
*/
|
|
100
|
-
export function deserialize(envelope) {
|
|
101
|
-
const included = envelope.included ?? [];
|
|
102
|
-
const index = buildIncludedIndex(included);
|
|
103
|
-
if (envelope.data === null || envelope.data === undefined) {
|
|
104
|
-
return { data: null, meta: envelope.meta };
|
|
105
|
-
}
|
|
106
|
-
if (Array.isArray(envelope.data)) {
|
|
107
|
-
const records = envelope.data.map((resource) => ({
|
|
108
|
-
...flattenResource(resource),
|
|
109
|
-
...resolveRelationships(resource, index),
|
|
110
|
-
}));
|
|
111
|
-
return { data: records, meta: envelope.meta };
|
|
112
|
-
}
|
|
113
|
-
const record = {
|
|
114
|
-
...flattenResource(envelope.data),
|
|
115
|
-
...resolveRelationships(envelope.data, index),
|
|
116
|
-
};
|
|
117
|
-
return { data: record, meta: envelope.meta };
|
|
118
|
-
}
|
|
119
|
-
// ---------------------------------------------------------------------------
|
|
120
|
-
// Rate Limit / Retry
|
|
121
|
-
// ---------------------------------------------------------------------------
|
|
122
|
-
const MAX_RETRIES = 3;
|
|
123
|
-
const BASE_RETRY_DELAY_MS = 1_000;
|
|
124
|
-
async function sleep(ms) {
|
|
125
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
126
|
-
}
|
|
127
|
-
// ---------------------------------------------------------------------------
|
|
128
|
-
// PercyClient
|
|
129
|
-
// ---------------------------------------------------------------------------
|
|
130
|
-
export class PercyClient {
|
|
131
|
-
config;
|
|
132
|
-
options;
|
|
133
|
-
constructor(config, options) {
|
|
134
|
-
this.config = config;
|
|
135
|
-
this.options = options ?? {};
|
|
136
|
-
}
|
|
137
|
-
// -----------------------------------------------------------------------
|
|
138
|
-
// Public HTTP methods
|
|
139
|
-
// -----------------------------------------------------------------------
|
|
140
|
-
/**
|
|
141
|
-
* GET request with optional query params and JSON:API `include`.
|
|
142
|
-
*/
|
|
143
|
-
async get(path, params, includes) {
|
|
144
|
-
const url = this.buildUrl(path, params, includes);
|
|
145
|
-
return this.request("GET", url);
|
|
146
|
-
}
|
|
147
|
-
/**
|
|
148
|
-
* POST request with an optional JSON body.
|
|
149
|
-
*/
|
|
150
|
-
async post(path, body) {
|
|
151
|
-
const url = this.buildUrl(path);
|
|
152
|
-
return this.request("POST", url, body);
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* PATCH request with an optional JSON body.
|
|
156
|
-
*/
|
|
157
|
-
async patch(path, body) {
|
|
158
|
-
const url = this.buildUrl(path);
|
|
159
|
-
return this.request("PATCH", url, body);
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* DELETE request.
|
|
163
|
-
*/
|
|
164
|
-
async del(path) {
|
|
165
|
-
const url = this.buildUrl(path);
|
|
166
|
-
await this.request("DELETE", url);
|
|
167
|
-
}
|
|
168
|
-
// -----------------------------------------------------------------------
|
|
169
|
-
// Internal
|
|
170
|
-
// -----------------------------------------------------------------------
|
|
171
|
-
buildUrl(path, params, includes) {
|
|
172
|
-
const base = getPercyApiBaseUrl();
|
|
173
|
-
// Ensure no double slashes between base and path
|
|
174
|
-
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
175
|
-
const url = new URL(`${base}${normalizedPath}`);
|
|
176
|
-
if (params) {
|
|
177
|
-
for (const [key, value] of Object.entries(params)) {
|
|
178
|
-
url.searchParams.set(key, value);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
if (includes && includes.length > 0) {
|
|
182
|
-
url.searchParams.set("include", includes.join(","));
|
|
183
|
-
}
|
|
184
|
-
return url.toString();
|
|
185
|
-
}
|
|
186
|
-
async request(method, url, body) {
|
|
187
|
-
const headers = await getPercyHeaders(this.config, {
|
|
188
|
-
scope: this.options.scope,
|
|
189
|
-
projectName: this.options.projectName,
|
|
190
|
-
});
|
|
191
|
-
let lastError;
|
|
192
|
-
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
193
|
-
const fetchOptions = {
|
|
194
|
-
method,
|
|
195
|
-
headers,
|
|
196
|
-
};
|
|
197
|
-
if (body !== undefined) {
|
|
198
|
-
fetchOptions.body = JSON.stringify(body);
|
|
199
|
-
}
|
|
200
|
-
let response;
|
|
201
|
-
try {
|
|
202
|
-
response = await fetch(url, fetchOptions);
|
|
203
|
-
}
|
|
204
|
-
catch (networkError) {
|
|
205
|
-
lastError =
|
|
206
|
-
networkError instanceof Error
|
|
207
|
-
? networkError
|
|
208
|
-
: new Error(String(networkError));
|
|
209
|
-
// Network errors are not retryable via the rate-limit path,
|
|
210
|
-
// but we still respect the retry loop for consistency.
|
|
211
|
-
if (attempt < MAX_RETRIES) {
|
|
212
|
-
await sleep(BASE_RETRY_DELAY_MS * Math.pow(2, attempt));
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
throw lastError;
|
|
216
|
-
}
|
|
217
|
-
// 204 No Content
|
|
218
|
-
if (response.status === 204) {
|
|
219
|
-
return undefined;
|
|
220
|
-
}
|
|
221
|
-
// Rate limited — retry with backoff
|
|
222
|
-
if (response.status === 429) {
|
|
223
|
-
const retryAfter = response.headers.get("Retry-After");
|
|
224
|
-
const delayMs = retryAfter
|
|
225
|
-
? parseFloat(retryAfter) * 1_000
|
|
226
|
-
: BASE_RETRY_DELAY_MS * Math.pow(2, attempt);
|
|
227
|
-
if (attempt < MAX_RETRIES) {
|
|
228
|
-
await sleep(delayMs);
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
// Exhausted retries — throw enriched error
|
|
232
|
-
let errorBody;
|
|
233
|
-
try {
|
|
234
|
-
errorBody = await response.json();
|
|
235
|
-
}
|
|
236
|
-
catch {
|
|
237
|
-
errorBody = undefined;
|
|
238
|
-
}
|
|
239
|
-
throw enrichPercyError(429, errorBody, `${method} ${url}`);
|
|
240
|
-
}
|
|
241
|
-
// Non-2xx error
|
|
242
|
-
if (!response.ok) {
|
|
243
|
-
let errorBody;
|
|
244
|
-
try {
|
|
245
|
-
errorBody = await response.json();
|
|
246
|
-
}
|
|
247
|
-
catch {
|
|
248
|
-
errorBody = undefined;
|
|
249
|
-
}
|
|
250
|
-
throw enrichPercyError(response.status, errorBody, `${method} ${url}`);
|
|
251
|
-
}
|
|
252
|
-
// Successful JSON response — deserialize JSON:API
|
|
253
|
-
const json = await response.json();
|
|
254
|
-
// If the response has a JSON:API `data` key, deserialize it
|
|
255
|
-
if (json && typeof json === "object" && "data" in json) {
|
|
256
|
-
const deserialized = deserialize(json);
|
|
257
|
-
// Unwrap: return the data directly (single object or array)
|
|
258
|
-
// Attach meta as a non-enumerable property so it's accessible but doesn't clutter
|
|
259
|
-
const result = deserialized.data;
|
|
260
|
-
if (result && typeof result === "object" && deserialized.meta) {
|
|
261
|
-
Object.defineProperty(result, "__meta", {
|
|
262
|
-
value: deserialized.meta,
|
|
263
|
-
enumerable: false,
|
|
264
|
-
writable: false,
|
|
265
|
-
});
|
|
266
|
-
}
|
|
267
|
-
return result;
|
|
268
|
-
}
|
|
269
|
-
// Non-JSON:API response — return as-is
|
|
270
|
-
return json;
|
|
271
|
-
}
|
|
272
|
-
// Should not reach here, but satisfy TypeScript
|
|
273
|
-
throw lastError ?? new Error("Request failed after retries");
|
|
274
|
-
}
|
|
275
|
-
}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Percy API error enrichment module.
|
|
3
|
-
* Maps Percy API error responses to actionable, user-friendly messages.
|
|
4
|
-
*/
|
|
5
|
-
export declare class PercyApiError extends Error {
|
|
6
|
-
statusCode: number;
|
|
7
|
-
errorCode?: string;
|
|
8
|
-
body?: unknown;
|
|
9
|
-
constructor(message: string, statusCode: number, errorCode?: string, body?: unknown);
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Maps Percy API error responses to actionable messages.
|
|
13
|
-
* Handles known error codes from Percy's JSON:API responses.
|
|
14
|
-
*/
|
|
15
|
-
export declare function enrichPercyError(status: number, body: unknown, context?: string): PercyApiError;
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Percy API error enrichment module.
|
|
3
|
-
* Maps Percy API error responses to actionable, user-friendly messages.
|
|
4
|
-
*/
|
|
5
|
-
export class PercyApiError extends Error {
|
|
6
|
-
statusCode;
|
|
7
|
-
errorCode;
|
|
8
|
-
body;
|
|
9
|
-
constructor(message, statusCode, errorCode, body) {
|
|
10
|
-
super(message);
|
|
11
|
-
this.name = "PercyApiError";
|
|
12
|
-
this.statusCode = statusCode;
|
|
13
|
-
this.errorCode = errorCode;
|
|
14
|
-
this.body = body;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Maps Percy API error responses to actionable messages.
|
|
19
|
-
* Handles known error codes from Percy's JSON:API responses.
|
|
20
|
-
*/
|
|
21
|
-
export function enrichPercyError(status, body, context) {
|
|
22
|
-
const prefix = context ? `${context}: ` : "";
|
|
23
|
-
const errorBody = body;
|
|
24
|
-
const errors = (errorBody?.errors ?? []);
|
|
25
|
-
const firstError = errors[0];
|
|
26
|
-
const errorCode = (firstError?.code ?? firstError?.source);
|
|
27
|
-
const detail = (firstError?.detail ?? firstError?.title ?? "");
|
|
28
|
-
switch (status) {
|
|
29
|
-
case 401:
|
|
30
|
-
return new PercyApiError(`${prefix}Percy token is invalid or expired. Check PERCY_TOKEN environment variable.`, 401, errorCode, body);
|
|
31
|
-
case 403: {
|
|
32
|
-
if (errorCode === "project_rbac_access_denied") {
|
|
33
|
-
return new PercyApiError(`${prefix}Insufficient permissions. This operation requires write access to the project.`, 403, errorCode, body);
|
|
34
|
-
}
|
|
35
|
-
if (errorCode === "build_deleted") {
|
|
36
|
-
return new PercyApiError(`${prefix}This build has been deleted.`, 403, errorCode, body);
|
|
37
|
-
}
|
|
38
|
-
if (errorCode === "plan_history_exceeded") {
|
|
39
|
-
return new PercyApiError(`${prefix}This build is outside your plan's history limit.`, 403, errorCode, body);
|
|
40
|
-
}
|
|
41
|
-
return new PercyApiError(`${prefix}Forbidden: ${detail || "Access denied."}`, 403, errorCode, body);
|
|
42
|
-
}
|
|
43
|
-
case 404:
|
|
44
|
-
return new PercyApiError(`${prefix}Resource not found. Check the ID and try again.`, 404, errorCode, body);
|
|
45
|
-
case 422:
|
|
46
|
-
return new PercyApiError(`${prefix}Invalid request: ${detail || "Unprocessable entity."}`, 422, errorCode, body);
|
|
47
|
-
case 429:
|
|
48
|
-
return new PercyApiError(`${prefix}Rate limit exceeded. Try again shortly.`, 429, errorCode, body);
|
|
49
|
-
default:
|
|
50
|
-
return new PercyApiError(`${prefix}Percy API error (${status}): ${detail || "Unknown error"}`, status, errorCode, body);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Markdown formatting utilities for Percy API responses.
|
|
3
|
-
*
|
|
4
|
-
* Each function transforms typed Percy API data into concise,
|
|
5
|
-
* agent-readable markdown. All functions handle null/undefined
|
|
6
|
-
* fields gracefully — showing "N/A" or omitting the section.
|
|
7
|
-
*/
|
|
8
|
-
export declare function formatBuild(build: any): string;
|
|
9
|
-
export declare function formatSnapshot(snapshot: any, comparisons?: any[]): string;
|
|
10
|
-
export declare function formatComparison(comparison: any, options?: {
|
|
11
|
-
includeRegions?: boolean;
|
|
12
|
-
}): string;
|
|
13
|
-
export declare function formatSuggestions(suggestions: any[]): string;
|
|
14
|
-
export declare function formatNetworkLogs(logs: any[]): string;
|
|
15
|
-
export declare function formatBuildStatus(build: any): string;
|
|
16
|
-
export declare function formatAiWarning(comparisons: any[]): string;
|