pgbeam 0.2.0 → 0.2.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/index.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { _ } from '@swc/helpers/_/_extends';
2
+
1
3
  /**
2
4
  * Generated by scripts/generate-maps.ts — do not edit manually.
3
5
  * Run: pnpm --filter pgbeam generate
@@ -344,7 +346,62 @@ class ApiError extends Error {
344
346
  if (typeof obj.message === "string") return obj.message;
345
347
  return undefined;
346
348
  }
349
+ const RETRYABLE_STATUS_CODES = new Set([
350
+ 408,
351
+ 429,
352
+ 502,
353
+ 503,
354
+ 504
355
+ ]);
356
+ const DEFAULT_RETRY = {
357
+ maxRetries: 5,
358
+ initialDelayMs: 500,
359
+ maxDelayMs: 30000,
360
+ idempotencyKeys: true
361
+ };
362
+ function resolveRetry(config) {
363
+ if (!config) return _({}, DEFAULT_RETRY, {
364
+ maxRetries: 0
365
+ });
366
+ return _({}, DEFAULT_RETRY, config);
367
+ }
368
+ /** Parse Retry-After header as milliseconds. Returns undefined if unparseable. */ function parseRetryAfter(response) {
369
+ const header = response.headers.get("Retry-After");
370
+ if (!header) return undefined;
371
+ // Integer seconds
372
+ const seconds = Number(header);
373
+ if (Number.isFinite(seconds) && seconds >= 0) {
374
+ return seconds * 1000;
375
+ }
376
+ // HTTP-date
377
+ const date = Date.parse(header);
378
+ if (Number.isFinite(date)) {
379
+ const ms = date - Date.now();
380
+ return ms > 0 ? ms : 0;
381
+ }
382
+ return undefined;
383
+ }
384
+ /** Compute exponential backoff with jitter. */ function backoff(attempt, initialDelayMs, maxDelayMs) {
385
+ const delay = Math.min(initialDelayMs * 2 ** attempt, maxDelayMs);
386
+ return delay * (0.5 + Math.random());
387
+ }
388
+ /** Generate a UUID v4 using crypto.randomUUID if available, otherwise fallback. */ function generateUUID() {
389
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
390
+ return crypto.randomUUID();
391
+ }
392
+ // Fallback for environments without crypto.randomUUID
393
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c)=>{
394
+ const r = Math.random() * 16 | 0;
395
+ const v = c === "x" ? r : r & 0x3 | 0x8;
396
+ return v.toString(16);
397
+ });
398
+ }
399
+ function isMutatingMethod(method) {
400
+ const upper = method.toUpperCase();
401
+ return upper === "POST" || upper === "PATCH";
402
+ }
347
403
  async function fetcher(options) {
404
+ var _options_fetchImpl;
348
405
  let url = options.path;
349
406
  if (options.pathParams) {
350
407
  for (const [key, value] of Object.entries(options.pathParams)){
@@ -366,20 +423,52 @@ async function fetcher(options) {
366
423
  if (options.body !== undefined) {
367
424
  headers["Content-Type"] = "application/json";
368
425
  }
369
- var _options_fetchImpl;
370
426
  const fetchFn = (_options_fetchImpl = options.fetchImpl) != null ? _options_fetchImpl : globalThis.fetch;
371
- const response = await fetchFn(fullUrl.toString(), {
372
- method: options.method,
373
- headers,
374
- body: options.body !== undefined ? JSON.stringify(options.body) : undefined
375
- });
376
- if (options.onResponse) {
377
- await options.onResponse(response);
378
- }
379
- if (!response.ok) {
380
- const errorBody = await response.json().catch(()=>null);
381
- throw new ApiError(response.status, response.statusText, errorBody);
427
+ const retry = resolveRetry(options.retry);
428
+ const urlStr = fullUrl.toString();
429
+ const fetchBody = options.body !== undefined ? JSON.stringify(options.body) : undefined;
430
+ // Generate idempotency key once, reused across all retry attempts.
431
+ const idempotencyKey = retry.maxRetries > 0 && retry.idempotencyKeys && isMutatingMethod(options.method) ? generateUUID() : undefined;
432
+ for(let attempt = 0; attempt <= retry.maxRetries; attempt++){
433
+ const reqHeaders = _({}, headers);
434
+ // Attach idempotency key on all attempts for mutating requests.
435
+ if (idempotencyKey) {
436
+ reqHeaders["Idempotency-Key"] = idempotencyKey;
437
+ }
438
+ try {
439
+ var _parseRetryAfter;
440
+ const response = await fetchFn(urlStr, {
441
+ method: options.method,
442
+ headers: reqHeaders,
443
+ body: fetchBody
444
+ });
445
+ if (options.onResponse) {
446
+ await options.onResponse(response);
447
+ }
448
+ if (response.ok) {
449
+ return parseResponseBody(response);
450
+ }
451
+ // Non-retryable error or last attempt — throw immediately.
452
+ if (!RETRYABLE_STATUS_CODES.has(response.status) || attempt === retry.maxRetries) {
453
+ const errorBody = await response.json().catch(()=>null);
454
+ throw new ApiError(response.status, response.statusText, errorBody);
455
+ }
456
+ // Retryable error — wait and retry.
457
+ const delay = (_parseRetryAfter = parseRetryAfter(response)) != null ? _parseRetryAfter : backoff(attempt, retry.initialDelayMs, retry.maxDelayMs);
458
+ await sleep(delay);
459
+ } catch (err) {
460
+ // If it's already an ApiError, rethrow (we threw it above).
461
+ if (err instanceof ApiError) throw err;
462
+ // Network error — retry if we have attempts left.
463
+ if (attempt === retry.maxRetries) throw err;
464
+ const delay = backoff(attempt, retry.initialDelayMs, retry.maxDelayMs);
465
+ await sleep(delay);
466
+ }
382
467
  }
468
+ // Unreachable — the loop either returns or throws.
469
+ throw new Error("fetcher: exhausted all retry attempts");
470
+ }
471
+ async function parseResponseBody(response) {
383
472
  if (response.status === 204) return undefined;
384
473
  const contentType = response.headers.get("content-type");
385
474
  if (contentType == null ? void 0 : contentType.includes("application/json")) {
@@ -388,7 +477,14 @@ async function fetcher(options) {
388
477
  // Fallback: try JSON, return undefined if body is empty/non-JSON
389
478
  const text = await response.text();
390
479
  if (!text) return undefined;
391
- return JSON.parse(text);
480
+ try {
481
+ return JSON.parse(text);
482
+ } catch (unused) {
483
+ return undefined;
484
+ }
485
+ }
486
+ function sleep(ms) {
487
+ return new Promise((resolve)=>setTimeout(resolve, ms));
392
488
  }
393
489
 
394
490
  class PgBeamClient {
@@ -410,7 +506,8 @@ class PgBeamClient {
410
506
  baseUrl: this._baseUrl,
411
507
  token,
412
508
  fetchImpl: this._fetchImpl,
413
- onResponse: this._onResponse
509
+ onResponse: this._onResponse,
510
+ retry: this._retry
414
511
  });
415
512
  };
416
513
  }
@@ -429,7 +526,8 @@ class PgBeamClient {
429
526
  baseUrl: this._baseUrl,
430
527
  token,
431
528
  fetchImpl: this._fetchImpl,
432
- onResponse: this._onResponse
529
+ onResponse: this._onResponse,
530
+ retry: this._retry
433
531
  });
434
532
  };
435
533
  this._api = new Proxy({}, {
@@ -450,10 +548,16 @@ class PgBeamClient {
450
548
  return this._api;
451
549
  }
452
550
  constructor(options){
551
+ var _options_retry;
453
552
  this._baseUrl = options.baseUrl;
454
553
  this._tokenOrFn = options.token;
455
554
  this._fetchImpl = options.fetch;
456
555
  this._onResponse = options.onResponse;
556
+ this._retry = options.retry === false ? {
557
+ maxRetries: 0
558
+ } : (_options_retry = options.retry) != null ? _options_retry : {
559
+ maxRetries: 5
560
+ };
457
561
  }
458
562
  }
459
563
 
@@ -0,0 +1,328 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ /**
4
+ * Generated by scripts/generate-maps.ts — do not edit manually.
5
+ * Run: pnpm --filter pgbeam generate
6
+ */ const operationsByTag = {
7
+ account: {
8
+ exportAccountData: {
9
+ method: "GET",
10
+ path: "/v1/account/export"
11
+ },
12
+ getOnboardingProgress: {
13
+ method: "GET",
14
+ path: "/v1/organizations/{org_id}/onboarding"
15
+ },
16
+ updateOnboardingProgress: {
17
+ method: "PATCH",
18
+ path: "/v1/organizations/{org_id}/onboarding"
19
+ }
20
+ },
21
+ analytics: {
22
+ getOrganizationActivity: {
23
+ method: "GET",
24
+ path: "/v1/organizations/{org_id}/activity"
25
+ },
26
+ listPlans: {
27
+ method: "GET",
28
+ path: "/v1/plans"
29
+ },
30
+ getOrganizationPlan: {
31
+ method: "GET",
32
+ path: "/v1/organizations/{org_id}/plan"
33
+ },
34
+ updateSpendLimit: {
35
+ method: "PUT",
36
+ path: "/v1/organizations/{org_id}/spend-limit"
37
+ },
38
+ submitCancellationFeedback: {
39
+ method: "POST",
40
+ path: "/v1/organizations/{org_id}/cancellation-feedback"
41
+ },
42
+ getProjectInsights: {
43
+ method: "GET",
44
+ path: "/v1/projects/{project_id}/insights"
45
+ },
46
+ getOrganizationUsage: {
47
+ method: "GET",
48
+ path: "/v1/organizations/{org_id}/usage"
49
+ },
50
+ getProjectUsage: {
51
+ method: "GET",
52
+ path: "/v1/projects/{project_id}/usage"
53
+ }
54
+ },
55
+ projects: {
56
+ listCacheRules: {
57
+ method: "GET",
58
+ path: "/v1/projects/{project_id}/databases/{database_id}/cache-rules"
59
+ },
60
+ updateCacheRule: {
61
+ method: "PUT",
62
+ path: "/v1/projects/{project_id}/databases/{database_id}/cache-rules/{query_hash}"
63
+ },
64
+ listCustomDomains: {
65
+ method: "GET",
66
+ path: "/v1/projects/{project_id}/domains"
67
+ },
68
+ createCustomDomain: {
69
+ method: "POST",
70
+ path: "/v1/projects/{project_id}/domains"
71
+ },
72
+ deleteCustomDomain: {
73
+ method: "DELETE",
74
+ path: "/v1/projects/{project_id}/domains/{domain_id}"
75
+ },
76
+ verifyCustomDomain: {
77
+ method: "POST",
78
+ path: "/v1/projects/{project_id}/domains/{domain_id}/verify"
79
+ },
80
+ listProjects: {
81
+ method: "GET",
82
+ path: "/v1/projects"
83
+ },
84
+ createProject: {
85
+ method: "POST",
86
+ path: "/v1/projects"
87
+ },
88
+ getProject: {
89
+ method: "GET",
90
+ path: "/v1/projects/{project_id}"
91
+ },
92
+ updateProject: {
93
+ method: "PATCH",
94
+ path: "/v1/projects/{project_id}"
95
+ },
96
+ deleteProject: {
97
+ method: "DELETE",
98
+ path: "/v1/projects/{project_id}"
99
+ },
100
+ getProjectMetrics: {
101
+ method: "GET",
102
+ path: "/v1/projects/{project_id}/metrics"
103
+ },
104
+ listReplicas: {
105
+ method: "GET",
106
+ path: "/v1/databases/{database_id}/replicas"
107
+ },
108
+ createReplica: {
109
+ method: "POST",
110
+ path: "/v1/databases/{database_id}/replicas"
111
+ },
112
+ deleteReplica: {
113
+ method: "DELETE",
114
+ path: "/v1/databases/{database_id}/replicas/{replica_id}"
115
+ }
116
+ },
117
+ databases: {
118
+ listDatabases: {
119
+ method: "GET",
120
+ path: "/v1/projects/{project_id}/databases"
121
+ },
122
+ createDatabase: {
123
+ method: "POST",
124
+ path: "/v1/projects/{project_id}/databases"
125
+ },
126
+ getDatabase: {
127
+ method: "GET",
128
+ path: "/v1/projects/{project_id}/databases/{database_id}"
129
+ },
130
+ updateDatabase: {
131
+ method: "PATCH",
132
+ path: "/v1/projects/{project_id}/databases/{database_id}"
133
+ },
134
+ deleteDatabase: {
135
+ method: "DELETE",
136
+ path: "/v1/projects/{project_id}/databases/{database_id}"
137
+ },
138
+ testDatabaseConnection: {
139
+ method: "POST",
140
+ path: "/v1/projects/{project_id}/databases/{database_id}/test-connection"
141
+ }
142
+ },
143
+ platform: {
144
+ getHealth: {
145
+ method: "GET",
146
+ path: "/v1/health"
147
+ },
148
+ listRegions: {
149
+ method: "GET",
150
+ path: "/v1/regions"
151
+ }
152
+ }
153
+ };
154
+ const operationsByPath = {
155
+ "GET /v1/account/export": {
156
+ method: "GET",
157
+ path: "/v1/account/export",
158
+ operationId: "exportAccountData"
159
+ },
160
+ "GET /v1/organizations/{org_id}/activity": {
161
+ method: "GET",
162
+ path: "/v1/organizations/{org_id}/activity",
163
+ operationId: "getOrganizationActivity"
164
+ },
165
+ "GET /v1/plans": {
166
+ method: "GET",
167
+ path: "/v1/plans",
168
+ operationId: "listPlans"
169
+ },
170
+ "GET /v1/organizations/{org_id}/plan": {
171
+ method: "GET",
172
+ path: "/v1/organizations/{org_id}/plan",
173
+ operationId: "getOrganizationPlan"
174
+ },
175
+ "PUT /v1/organizations/{org_id}/spend-limit": {
176
+ method: "PUT",
177
+ path: "/v1/organizations/{org_id}/spend-limit",
178
+ operationId: "updateSpendLimit"
179
+ },
180
+ "POST /v1/organizations/{org_id}/cancellation-feedback": {
181
+ method: "POST",
182
+ path: "/v1/organizations/{org_id}/cancellation-feedback",
183
+ operationId: "submitCancellationFeedback"
184
+ },
185
+ "GET /v1/projects/{project_id}/databases/{database_id}/cache-rules": {
186
+ method: "GET",
187
+ path: "/v1/projects/{project_id}/databases/{database_id}/cache-rules",
188
+ operationId: "listCacheRules"
189
+ },
190
+ "PUT /v1/projects/{project_id}/databases/{database_id}/cache-rules/{query_hash}": {
191
+ method: "PUT",
192
+ path: "/v1/projects/{project_id}/databases/{database_id}/cache-rules/{query_hash}",
193
+ operationId: "updateCacheRule"
194
+ },
195
+ "GET /v1/projects/{project_id}/databases": {
196
+ method: "GET",
197
+ path: "/v1/projects/{project_id}/databases",
198
+ operationId: "listDatabases"
199
+ },
200
+ "POST /v1/projects/{project_id}/databases": {
201
+ method: "POST",
202
+ path: "/v1/projects/{project_id}/databases",
203
+ operationId: "createDatabase"
204
+ },
205
+ "GET /v1/projects/{project_id}/databases/{database_id}": {
206
+ method: "GET",
207
+ path: "/v1/projects/{project_id}/databases/{database_id}",
208
+ operationId: "getDatabase"
209
+ },
210
+ "PATCH /v1/projects/{project_id}/databases/{database_id}": {
211
+ method: "PATCH",
212
+ path: "/v1/projects/{project_id}/databases/{database_id}",
213
+ operationId: "updateDatabase"
214
+ },
215
+ "DELETE /v1/projects/{project_id}/databases/{database_id}": {
216
+ method: "DELETE",
217
+ path: "/v1/projects/{project_id}/databases/{database_id}",
218
+ operationId: "deleteDatabase"
219
+ },
220
+ "POST /v1/projects/{project_id}/databases/{database_id}/test-connection": {
221
+ method: "POST",
222
+ path: "/v1/projects/{project_id}/databases/{database_id}/test-connection",
223
+ operationId: "testDatabaseConnection"
224
+ },
225
+ "GET /v1/projects/{project_id}/domains": {
226
+ method: "GET",
227
+ path: "/v1/projects/{project_id}/domains",
228
+ operationId: "listCustomDomains"
229
+ },
230
+ "POST /v1/projects/{project_id}/domains": {
231
+ method: "POST",
232
+ path: "/v1/projects/{project_id}/domains",
233
+ operationId: "createCustomDomain"
234
+ },
235
+ "DELETE /v1/projects/{project_id}/domains/{domain_id}": {
236
+ method: "DELETE",
237
+ path: "/v1/projects/{project_id}/domains/{domain_id}",
238
+ operationId: "deleteCustomDomain"
239
+ },
240
+ "POST /v1/projects/{project_id}/domains/{domain_id}/verify": {
241
+ method: "POST",
242
+ path: "/v1/projects/{project_id}/domains/{domain_id}/verify",
243
+ operationId: "verifyCustomDomain"
244
+ },
245
+ "GET /v1/health": {
246
+ method: "GET",
247
+ path: "/v1/health",
248
+ operationId: "getHealth"
249
+ },
250
+ "GET /v1/projects/{project_id}/insights": {
251
+ method: "GET",
252
+ path: "/v1/projects/{project_id}/insights",
253
+ operationId: "getProjectInsights"
254
+ },
255
+ "GET /v1/organizations/{org_id}/onboarding": {
256
+ method: "GET",
257
+ path: "/v1/organizations/{org_id}/onboarding",
258
+ operationId: "getOnboardingProgress"
259
+ },
260
+ "PATCH /v1/organizations/{org_id}/onboarding": {
261
+ method: "PATCH",
262
+ path: "/v1/organizations/{org_id}/onboarding",
263
+ operationId: "updateOnboardingProgress"
264
+ },
265
+ "GET /v1/projects": {
266
+ method: "GET",
267
+ path: "/v1/projects",
268
+ operationId: "listProjects"
269
+ },
270
+ "POST /v1/projects": {
271
+ method: "POST",
272
+ path: "/v1/projects",
273
+ operationId: "createProject"
274
+ },
275
+ "GET /v1/projects/{project_id}": {
276
+ method: "GET",
277
+ path: "/v1/projects/{project_id}",
278
+ operationId: "getProject"
279
+ },
280
+ "PATCH /v1/projects/{project_id}": {
281
+ method: "PATCH",
282
+ path: "/v1/projects/{project_id}",
283
+ operationId: "updateProject"
284
+ },
285
+ "DELETE /v1/projects/{project_id}": {
286
+ method: "DELETE",
287
+ path: "/v1/projects/{project_id}",
288
+ operationId: "deleteProject"
289
+ },
290
+ "GET /v1/projects/{project_id}/metrics": {
291
+ method: "GET",
292
+ path: "/v1/projects/{project_id}/metrics",
293
+ operationId: "getProjectMetrics"
294
+ },
295
+ "GET /v1/regions": {
296
+ method: "GET",
297
+ path: "/v1/regions",
298
+ operationId: "listRegions"
299
+ },
300
+ "GET /v1/databases/{database_id}/replicas": {
301
+ method: "GET",
302
+ path: "/v1/databases/{database_id}/replicas",
303
+ operationId: "listReplicas"
304
+ },
305
+ "POST /v1/databases/{database_id}/replicas": {
306
+ method: "POST",
307
+ path: "/v1/databases/{database_id}/replicas",
308
+ operationId: "createReplica"
309
+ },
310
+ "DELETE /v1/databases/{database_id}/replicas/{replica_id}": {
311
+ method: "DELETE",
312
+ path: "/v1/databases/{database_id}/replicas/{replica_id}",
313
+ operationId: "deleteReplica"
314
+ },
315
+ "GET /v1/organizations/{org_id}/usage": {
316
+ method: "GET",
317
+ path: "/v1/organizations/{org_id}/usage",
318
+ operationId: "getOrganizationUsage"
319
+ },
320
+ "GET /v1/projects/{project_id}/usage": {
321
+ method: "GET",
322
+ path: "/v1/projects/{project_id}/usage",
323
+ operationId: "getProjectUsage"
324
+ }
325
+ };
326
+
327
+ exports.operationsByPath = operationsByPath;
328
+ exports.operationsByTag = operationsByTag;