@pantechdynamic/sdk 0.0.0-stage → 0.1.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/dist/index.cjs ADDED
@@ -0,0 +1,1515 @@
1
+ 'use strict';
2
+
3
+ // src/errors.ts
4
+ var PantechError = class extends Error {
5
+ name = "PantechError";
6
+ /** The HTTP status, when the API answered. */
7
+ status;
8
+ /** The problem's stable code, e.g. `INSUFFICIENT_CREDIT`. */
9
+ code;
10
+ /** A short summary of the problem class. */
11
+ title;
12
+ /** What went wrong and what to do next, in plain language. Safe to show to your users. */
13
+ detail;
14
+ /** The request's id. Quote it to support. */
15
+ requestId;
16
+ /** The invalid fields of a `422 VALIDATION_FAILED`; empty otherwise. */
17
+ errors;
18
+ /** A URI naming the problem class. */
19
+ type;
20
+ /** The response headers, when the API answered. */
21
+ headers;
22
+ constructor(message, init = {}) {
23
+ super(message, init.cause === void 0 ? void 0 : { cause: init.cause });
24
+ this.status = init.status;
25
+ this.code = init.code;
26
+ this.title = init.title;
27
+ this.detail = init.detail;
28
+ this.requestId = init.requestId;
29
+ this.errors = init.errors ?? [];
30
+ this.type = init.type;
31
+ this.headers = init.headers;
32
+ }
33
+ };
34
+ var BadRequestError = class extends PantechError {
35
+ name = "BadRequestError";
36
+ };
37
+ var AuthenticationError = class extends PantechError {
38
+ name = "AuthenticationError";
39
+ };
40
+ var PaymentRequiredError = class extends PantechError {
41
+ name = "PaymentRequiredError";
42
+ };
43
+ var PermissionDeniedError = class extends PantechError {
44
+ name = "PermissionDeniedError";
45
+ };
46
+ var NotFoundError = class extends PantechError {
47
+ name = "NotFoundError";
48
+ };
49
+ var ConflictError = class extends PantechError {
50
+ name = "ConflictError";
51
+ };
52
+ var ValidationError = class extends PantechError {
53
+ name = "ValidationError";
54
+ };
55
+ var RateLimitError = class extends PantechError {
56
+ name = "RateLimitError";
57
+ /** Seconds the API asked to wait (`Retry-After`), when it said. */
58
+ retryAfterSeconds;
59
+ constructor(message, init = {}) {
60
+ super(message, init);
61
+ this.retryAfterSeconds = init.retryAfterSeconds;
62
+ }
63
+ };
64
+ var InternalServerError = class extends PantechError {
65
+ name = "InternalServerError";
66
+ };
67
+ var APIConnectionError = class extends PantechError {
68
+ name = "APIConnectionError";
69
+ };
70
+ var APITimeoutError = class extends APIConnectionError {
71
+ name = "APITimeoutError";
72
+ };
73
+ var OperationFailedError = class extends PantechError {
74
+ name = "OperationFailedError";
75
+ /** The failed operation, as `GET /operations/{id}` returned it. */
76
+ operation;
77
+ constructor(message, operation, init = {}) {
78
+ super(message, init);
79
+ this.operation = operation;
80
+ }
81
+ };
82
+ var OrderFailedError = class extends PantechError {
83
+ name = "OrderFailedError";
84
+ /** The failed order. */
85
+ order;
86
+ constructor(message, order, init = {}) {
87
+ super(message, init);
88
+ this.order = order;
89
+ }
90
+ };
91
+ var WaitTimeoutError = class extends PantechError {
92
+ name = "WaitTimeoutError";
93
+ };
94
+ var WebhookVerificationError = class extends PantechError {
95
+ name = "WebhookVerificationError";
96
+ };
97
+ function isPantechError(err, code) {
98
+ return err instanceof PantechError && (code === void 0 || err.code === code);
99
+ }
100
+ var STATUS_TEXT = {
101
+ 400: "Bad request",
102
+ 401: "Unauthorized",
103
+ 402: "Payment required",
104
+ 403: "Forbidden",
105
+ 404: "Not found",
106
+ 405: "Method not allowed",
107
+ 409: "Conflict",
108
+ 413: "Request too large",
109
+ 415: "Unsupported media type",
110
+ 422: "Validation failed",
111
+ 429: "Too many requests",
112
+ 500: "Internal server error",
113
+ 502: "Bad gateway",
114
+ 503: "Service unavailable",
115
+ 504: "Gateway timeout"
116
+ };
117
+ var str = (v) => typeof v === "string" && v !== "" ? v : void 0;
118
+ function fieldErrors(v) {
119
+ if (!Array.isArray(v)) return [];
120
+ return v.filter((e) => typeof e === "object" && e !== null).map((e) => ({ field: String(e.field ?? ""), code: String(e.code ?? ""), message: String(e.message ?? "") }));
121
+ }
122
+ function errorFromResponse(status, headers, bodyText, retryAfterSeconds) {
123
+ let problem;
124
+ try {
125
+ const parsed = JSON.parse(bodyText);
126
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) problem = parsed;
127
+ } catch {
128
+ problem = void 0;
129
+ }
130
+ const code = str(problem?.code);
131
+ const title = str(problem?.title);
132
+ let detail = str(problem?.detail);
133
+ const errors = fieldErrors(problem?.errors);
134
+ const requestId = str(problem?.request_id) ?? headers.get("x-request-id") ?? void 0;
135
+ const isProblem = code !== void 0 || detail !== void 0 || title !== void 0 || errors.length > 0;
136
+ if (!isProblem) {
137
+ const text = STATUS_TEXT[status] ?? `HTTP ${status}`;
138
+ detail = status >= 500 ? `The Pantech API is temporarily unavailable (${status} ${text}). Try again in a moment.` : `The Pantech API refused the request (${status} ${text}).`;
139
+ }
140
+ let message = detail ?? title ?? STATUS_TEXT[status] ?? `HTTP ${status}`;
141
+ const tags = [code, requestId ? `request ${requestId}` : void 0].filter(Boolean);
142
+ if (tags.length > 0) message += ` (${tags.join(", ")})`;
143
+ const init = {
144
+ status,
145
+ code,
146
+ title,
147
+ detail,
148
+ requestId,
149
+ errors,
150
+ type: str(problem?.type),
151
+ headers
152
+ };
153
+ switch (true) {
154
+ case status === 400:
155
+ return new BadRequestError(message, init);
156
+ case status === 401:
157
+ return new AuthenticationError(message, init);
158
+ case status === 402:
159
+ return new PaymentRequiredError(message, init);
160
+ case status === 403:
161
+ return new PermissionDeniedError(message, init);
162
+ case status === 404:
163
+ return new NotFoundError(message, init);
164
+ case status === 409:
165
+ return new ConflictError(message, init);
166
+ case status === 422:
167
+ return new ValidationError(message, init);
168
+ case status === 429:
169
+ return new RateLimitError(message, { ...init, retryAfterSeconds });
170
+ case status >= 500:
171
+ return new InternalServerError(message, init);
172
+ default:
173
+ return new PantechError(message, init);
174
+ }
175
+ }
176
+
177
+ // src/pagination.ts
178
+ async function* paginate(fetchPage, startCursor) {
179
+ let cursor = startCursor ?? void 0;
180
+ const seen = /* @__PURE__ */ new Set();
181
+ for (; ; ) {
182
+ const page = await fetchPage(cursor);
183
+ for (const item of page.data ?? []) yield item;
184
+ const next = page.next_cursor;
185
+ if (!next || seen.has(next)) return;
186
+ seen.add(next);
187
+ cursor = next;
188
+ }
189
+ }
190
+
191
+ // src/resource.ts
192
+ var APIResource = class {
193
+ _client;
194
+ constructor(client) {
195
+ this._client = client;
196
+ }
197
+ _request(method, path2, init = {}, options) {
198
+ return this._client.request({ method, path: path2, ...init }, options);
199
+ }
200
+ _get(path2, query, options) {
201
+ return this._request("GET", path2, { query }, options);
202
+ }
203
+ _post(path2, body, options, extra) {
204
+ return this._request("POST", path2, { body, ...extra }, options);
205
+ }
206
+ _put(path2, body, options) {
207
+ return this._request("PUT", path2, { body }, options);
208
+ }
209
+ _patch(path2, body, options) {
210
+ return this._request("PATCH", path2, { body }, options);
211
+ }
212
+ _delete(path2, options, extra) {
213
+ return this._request("DELETE", path2, extra, options);
214
+ }
215
+ /** Every item of a list, following `next_cursor` page by page. */
216
+ _listAll(path2, params, options) {
217
+ return paginate((cursor) => this._get(path2, { ...params, cursor }, options));
218
+ }
219
+ };
220
+
221
+ // src/util.ts
222
+ function uuid() {
223
+ const c = globalThis.crypto;
224
+ if (typeof c?.randomUUID === "function") return c.randomUUID();
225
+ const b = new Uint8Array(16);
226
+ if (typeof c?.getRandomValues === "function") {
227
+ c.getRandomValues(b);
228
+ } else {
229
+ for (let i = 0; i < 16; i++) b[i] = Math.floor(Math.random() * 256);
230
+ }
231
+ b[6] = b[6] & 15 | 64;
232
+ b[8] = b[8] & 63 | 128;
233
+ const h = Array.from(b, (x) => x.toString(16).padStart(2, "0")).join("");
234
+ return `${h.slice(0, 8)}-${h.slice(8, 12)}-${h.slice(12, 16)}-${h.slice(16, 20)}-${h.slice(20)}`;
235
+ }
236
+ function readEnv(name) {
237
+ try {
238
+ const proc = globalThis.process;
239
+ const value = proc?.env?.[name]?.trim();
240
+ return value ? value : void 0;
241
+ } catch {
242
+ return void 0;
243
+ }
244
+ }
245
+ function sleep(ms, signal) {
246
+ return new Promise((resolve, reject) => {
247
+ if (signal?.aborted) {
248
+ reject(signal.reason);
249
+ return;
250
+ }
251
+ const onAbort = () => {
252
+ clearTimeout(timer);
253
+ reject(signal?.reason);
254
+ };
255
+ const timer = setTimeout(() => {
256
+ signal?.removeEventListener("abort", onAbort);
257
+ resolve();
258
+ }, ms);
259
+ signal?.addEventListener("abort", onAbort, { once: true });
260
+ });
261
+ }
262
+ function path(strings, ...ids) {
263
+ let out = strings[0] ?? "";
264
+ ids.forEach((id, i) => {
265
+ if (typeof id !== "string" || id === "") {
266
+ throw new PantechError("A resource id is required, and it must be a non-empty string.");
267
+ }
268
+ out += encodeURIComponent(id) + (strings[i + 1] ?? "");
269
+ });
270
+ return out;
271
+ }
272
+
273
+ // src/resources/billing.ts
274
+ var Billing = class extends APIResource {
275
+ credit = new Credit(this._client);
276
+ receipts = new Receipts(this._client);
277
+ };
278
+ var Credit = class extends APIResource {
279
+ purchases = new CreditPurchases(this._client);
280
+ /** Your credit balance and whether it covers the next invoice. */
281
+ get(options) {
282
+ return this._get("/billing/credit", void 0, options);
283
+ }
284
+ };
285
+ var CreditPurchases = class extends APIResource {
286
+ /** Creates a payment link that adds credit: send its `checkout_url` to whoever pays. */
287
+ create(body, options) {
288
+ return this._post("/billing/credit/purchases", body, options);
289
+ }
290
+ get(purchaseId, options) {
291
+ return this._get(path`/billing/credit/purchases/${purchaseId}`, void 0, options);
292
+ }
293
+ };
294
+ var Receipts = class extends APIResource {
295
+ list(params = {}, options) {
296
+ return this._get("/billing/receipts", params, options);
297
+ }
298
+ listAll(params = {}, options) {
299
+ return this._listAll("/billing/receipts", params, options);
300
+ }
301
+ get(receiptId, options) {
302
+ return this._get(path`/billing/receipts/${receiptId}`, void 0, options);
303
+ }
304
+ /** The receipt as a PDF document. */
305
+ getPdf(receiptId, options) {
306
+ return this._request("GET", path`/billing/receipts/${receiptId}/pdf`, { responseType: "arrayBuffer" }, options);
307
+ }
308
+ };
309
+
310
+ // src/resources/catalog.ts
311
+ var Regions = class extends APIResource {
312
+ list(params = {}, options) {
313
+ return this._get("/regions", params, options);
314
+ }
315
+ listAll(params = {}, options) {
316
+ return this._listAll("/regions", params, options);
317
+ }
318
+ };
319
+ var Plans = class extends APIResource {
320
+ list(params = {}, options) {
321
+ return this._get("/plans", params, options);
322
+ }
323
+ listAll(params = {}, options) {
324
+ return this._listAll("/plans", params, options);
325
+ }
326
+ };
327
+ var Images = class extends APIResource {
328
+ list(params = {}, options) {
329
+ return this._get("/images", params, options);
330
+ }
331
+ listAll(params = {}, options) {
332
+ return this._listAll("/images", params, options);
333
+ }
334
+ };
335
+ var DiskOfferings = class extends APIResource {
336
+ list(params = {}, options) {
337
+ return this._get("/disk-offerings", params, options);
338
+ }
339
+ listAll(params = {}, options) {
340
+ return this._listAll("/disk-offerings", params, options);
341
+ }
342
+ /** What a volume of this offering costs. */
343
+ quote(slug, params = {}, options) {
344
+ return this._get(path`/disk-offerings/${slug}/quote`, params, options);
345
+ }
346
+ };
347
+ var DatabaseEngines = class extends APIResource {
348
+ list(params = {}, options) {
349
+ return this._get("/database-engines", params, options);
350
+ }
351
+ listAll(params = {}, options) {
352
+ return this._listAll("/database-engines", params, options);
353
+ }
354
+ };
355
+ var KubernetesVersions = class extends APIResource {
356
+ /** The Kubernetes versions a new cluster can run, and the zones that offer high availability. */
357
+ list(params = {}, options) {
358
+ return this._get("/kubernetes-versions", params, options);
359
+ }
360
+ };
361
+ var Pricing = class extends APIResource {
362
+ /** Prices a set of instances, volumes and other resources. Changes nothing. */
363
+ estimate(body, options) {
364
+ return this._post("/pricing/estimate", body, options);
365
+ }
366
+ };
367
+
368
+ // src/wait.ts
369
+ var DEFAULT_WAIT_TIMEOUT_MS = 20 * 6e4;
370
+ var DEFAULT_POLL_INTERVAL_MS = 2e3;
371
+ async function poll(read, done, what, options = {}) {
372
+ const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
373
+ const interval = Math.max(0, options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS);
374
+ const deadline = Date.now() + timeoutMs;
375
+ for (; ; ) {
376
+ const value = await read();
377
+ options.onUpdate?.(value);
378
+ if (done(value)) return value;
379
+ if (Date.now() + interval > deadline) {
380
+ throw new WaitTimeoutError(
381
+ `${what} did not finish within ${Math.round(timeoutMs / 1e3)} seconds. It may still finish: check it again later.`
382
+ );
383
+ }
384
+ await sleep(interval, options.signal);
385
+ }
386
+ }
387
+
388
+ // src/orders.ts
389
+ var orderId = (ref) => typeof ref === "string" ? ref : ref.order_id;
390
+ async function waitForOrder(client, read, what, options = {}) {
391
+ const started = Date.now();
392
+ const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;
393
+ const order = await poll(
394
+ () => read(options.signal),
395
+ (o) => {
396
+ if (o.status === "failed" || o.status === "payment_failed") {
397
+ const code = o.failure_code ?? o.status;
398
+ throw new OrderFailedError(
399
+ `${what} ${o.status === "payment_failed" ? "could not be paid" : "failed"} (${code}). Any payment taken is returned to your credit.`,
400
+ o,
401
+ { code }
402
+ );
403
+ }
404
+ return o.status === "provisioned";
405
+ },
406
+ what,
407
+ options
408
+ );
409
+ if (options.waitForOperation !== false && order.operation_id) {
410
+ await client.operations.wait(order.operation_id, {
411
+ timeoutMs: Math.max(0, timeoutMs - (Date.now() - started)),
412
+ pollIntervalMs: options.pollIntervalMs,
413
+ signal: options.signal
414
+ });
415
+ }
416
+ return order;
417
+ }
418
+
419
+ // src/resources/databases.ts
420
+ var Databases = class extends APIResource {
421
+ /** Snapshots of a database. */
422
+ snapshots = new DatabaseSnapshots(this._client);
423
+ list(params = {}, options) {
424
+ return this._get("/databases", params, options);
425
+ }
426
+ listAll(params = {}, options) {
427
+ return this._listAll("/databases", params, options);
428
+ }
429
+ /**
430
+ * Orders a database. A generated admin password is in this response only;
431
+ * follow the order with `client.databaseOrders.wait(order)`.
432
+ */
433
+ create(body, options) {
434
+ return this._post("/databases", body, options);
435
+ }
436
+ get(databaseId, options) {
437
+ return this._get(path`/databases/${databaseId}`, void 0, options);
438
+ }
439
+ delete(databaseId, options) {
440
+ return this._delete(path`/databases/${databaseId}`, options);
441
+ }
442
+ /** Replaces the CIDRs allowed to connect. */
443
+ replaceAccessRules(databaseId, body, options) {
444
+ return this._put(path`/databases/${databaseId}/access-rules`, body, options);
445
+ }
446
+ /** Replaces the attached security groups. */
447
+ setSecurityGroups(databaseId, body, options) {
448
+ return this._put(path`/databases/${databaseId}/security-groups`, body, options);
449
+ }
450
+ changePassword(databaseId, body, options) {
451
+ return this._put(path`/databases/${databaseId}/password`, body, options);
452
+ }
453
+ /** Generates a new admin password, returned in this response only. */
454
+ resetPassword(databaseId, options) {
455
+ return this._post(path`/databases/${databaseId}/reset-password`, void 0, options);
456
+ }
457
+ start(databaseId, options) {
458
+ return this._post(path`/databases/${databaseId}/start`, void 0, options);
459
+ }
460
+ stop(databaseId, options) {
461
+ return this._post(path`/databases/${databaseId}/stop`, void 0, options);
462
+ }
463
+ resizeStorage(databaseId, body, options) {
464
+ return this._post(path`/databases/${databaseId}/resize-storage`, body, options);
465
+ }
466
+ };
467
+ var DatabaseSnapshots = class extends APIResource {
468
+ list(databaseId, params = {}, options) {
469
+ return this._get(path`/databases/${databaseId}/snapshots`, params, options);
470
+ }
471
+ listAll(databaseId, params = {}, options) {
472
+ return this._listAll(path`/databases/${databaseId}/snapshots`, params, options);
473
+ }
474
+ create(databaseId, body, options) {
475
+ return this._post(path`/databases/${databaseId}/snapshots`, body, options);
476
+ }
477
+ get(databaseId, snapshotId, options) {
478
+ return this._get(path`/databases/${databaseId}/snapshots/${snapshotId}`, void 0, options);
479
+ }
480
+ delete(databaseId, snapshotId, options) {
481
+ return this._delete(path`/databases/${databaseId}/snapshots/${snapshotId}`, options);
482
+ }
483
+ };
484
+ var DatabaseOrders = class extends APIResource {
485
+ list(params = {}, options) {
486
+ return this._get("/database-orders", params, options);
487
+ }
488
+ listAll(params = {}, options) {
489
+ return this._listAll("/database-orders", params, options);
490
+ }
491
+ get(orderId2, options) {
492
+ return this._get(path`/database-orders/${orderId2}`, void 0, options);
493
+ }
494
+ /**
495
+ * Polls the order until it is provisioned and (by default) the database's
496
+ * create operation has finished. Rejects with OrderFailedError or
497
+ * OperationFailedError. Accepts an id or the body `databases.create` returned.
498
+ */
499
+ wait(ref, options = {}) {
500
+ const id = orderId(ref);
501
+ return waitForOrder(this._client, (signal) => this.get(id, { signal }), `Database order ${id}`, options);
502
+ }
503
+ };
504
+
505
+ // src/resources/instances.ts
506
+ var Instances = class extends APIResource {
507
+ /** Snapshots of an instance's root disk. */
508
+ snapshots = new InstanceSnapshots(this._client);
509
+ /** Short-lived SSH access grants through the browser terminal relay. */
510
+ sshAccess = new InstanceSshAccess(this._client);
511
+ /** One page of your instances. */
512
+ list(params = {}, options) {
513
+ return this._get("/instances", params, options);
514
+ }
515
+ /** Every instance: `for await (const vm of client.instances.listAll())`. */
516
+ listAll(params = {}, options) {
517
+ return this._listAll("/instances", params, options);
518
+ }
519
+ /**
520
+ * Orders an instance. The initial payment is taken first, then it is
521
+ * provisioned: follow the order with `client.instanceOrders.wait(order)`.
522
+ */
523
+ create(body, options) {
524
+ return this._post("/instances", body, options);
525
+ }
526
+ get(instanceId, options) {
527
+ return this._get(path`/instances/${instanceId}`, void 0, options);
528
+ }
529
+ delete(instanceId, options) {
530
+ return this._delete(path`/instances/${instanceId}`, options);
531
+ }
532
+ start(instanceId, options) {
533
+ return this._post(path`/instances/${instanceId}/start`, void 0, options);
534
+ }
535
+ stop(instanceId, options) {
536
+ return this._post(path`/instances/${instanceId}/stop`, void 0, options);
537
+ }
538
+ reboot(instanceId, options) {
539
+ return this._post(path`/instances/${instanceId}/reboot`, void 0, options);
540
+ }
541
+ /** Reinstalls the root disk from an image. Everything on it is lost. */
542
+ rebuild(instanceId, body, options) {
543
+ return this._post(path`/instances/${instanceId}/rebuild`, body, options);
544
+ }
545
+ rename(instanceId, body, options) {
546
+ return this._post(path`/instances/${instanceId}/rename`, body, options);
547
+ }
548
+ /** Moves the instance to a bigger plan. */
549
+ resize(instanceId, body, options) {
550
+ return this._post(path`/instances/${instanceId}/resize`, body, options);
551
+ }
552
+ changeSshKey(instanceId, body, options) {
553
+ return this._post(path`/instances/${instanceId}/change-ssh-key`, body, options);
554
+ }
555
+ /** Restores the root disk from a snapshot. Changes since then are lost. */
556
+ restore(instanceId, body, options) {
557
+ return this._post(path`/instances/${instanceId}/restore`, body, options);
558
+ }
559
+ changeSecurityGroup(instanceId, body, options) {
560
+ return this._put(path`/instances/${instanceId}/security-group`, body, options);
561
+ }
562
+ attachPrivateNetwork(instanceId, options) {
563
+ return this._post(path`/instances/${instanceId}/private-network`, void 0, options);
564
+ }
565
+ detachPrivateNetwork(instanceId, options) {
566
+ return this._delete(path`/instances/${instanceId}/private-network`, options);
567
+ }
568
+ /** A short-lived URL to the instance's console. Sent once: the API does not replay it. */
569
+ openConsole(instanceId, options) {
570
+ return this._post(path`/instances/${instanceId}/console`, void 0, options, { noRetry: true });
571
+ }
572
+ /** Issues a credential that unlocks the instance's encrypted volumes. Sent once: the API does not replay it. */
573
+ issueVolumeKeyCredential(instanceId, options) {
574
+ return this._post(path`/instances/${instanceId}/volume-key-credential`, void 0, options, { noRetry: true });
575
+ }
576
+ metrics(instanceId, params = {}, options) {
577
+ return this._get(path`/instances/${instanceId}/metrics`, params, options);
578
+ }
579
+ usage(instanceId, options) {
580
+ return this._get(path`/instances/${instanceId}/usage`, void 0, options);
581
+ }
582
+ /** One page of the volumes attached to the instance. */
583
+ listVolumes(instanceId, params = {}, options) {
584
+ return this._get(path`/instances/${instanceId}/volumes`, params, options);
585
+ }
586
+ listAllVolumes(instanceId, params = {}, options) {
587
+ return this._listAll(path`/instances/${instanceId}/volumes`, params, options);
588
+ }
589
+ getSnapshotSchedule(instanceId, options) {
590
+ return this._get(path`/instances/${instanceId}/snapshot-schedule`, void 0, options);
591
+ }
592
+ setSnapshotSchedule(instanceId, body, options) {
593
+ return this._put(path`/instances/${instanceId}/snapshot-schedule`, body, options);
594
+ }
595
+ };
596
+ var InstanceSnapshots = class extends APIResource {
597
+ list(instanceId, params = {}, options) {
598
+ return this._get(path`/instances/${instanceId}/snapshots`, params, options);
599
+ }
600
+ listAll(instanceId, params = {}, options) {
601
+ return this._listAll(path`/instances/${instanceId}/snapshots`, params, options);
602
+ }
603
+ create(instanceId, body, options) {
604
+ return this._post(path`/instances/${instanceId}/snapshots`, body, options);
605
+ }
606
+ };
607
+ var InstanceSshAccess = class extends APIResource {
608
+ open(instanceId, options) {
609
+ return this._post(path`/instances/${instanceId}/ssh-access`, void 0, options);
610
+ }
611
+ get(instanceId, grantId, options) {
612
+ return this._get(path`/instances/${instanceId}/ssh-access/${grantId}`, void 0, options);
613
+ }
614
+ revoke(instanceId, grantId, options) {
615
+ return this._delete(path`/instances/${instanceId}/ssh-access/${grantId}`, options);
616
+ }
617
+ };
618
+ var InstanceOrders = class extends APIResource {
619
+ list(params = {}, options) {
620
+ return this._get("/instance-orders", params, options);
621
+ }
622
+ listAll(params = {}, options) {
623
+ return this._listAll("/instance-orders", params, options);
624
+ }
625
+ get(orderId2, options) {
626
+ return this._get(path`/instance-orders/${orderId2}`, void 0, options);
627
+ }
628
+ /**
629
+ * Polls the order until it is provisioned and (by default) the instance's
630
+ * create operation has finished. Rejects with OrderFailedError when the
631
+ * order fails or cannot be paid, and OperationFailedError when the create
632
+ * fails. Accepts an id or the body `instances.create` returned.
633
+ */
634
+ wait(ref, options = {}) {
635
+ const id = orderId(ref);
636
+ return waitForOrder(this._client, (signal) => this.get(id, { signal }), `Instance order ${id}`, options);
637
+ }
638
+ };
639
+
640
+ // src/resources/kubernetes.ts
641
+ var Kubernetes = class extends APIResource {
642
+ /** A cluster's node pools. */
643
+ nodePools = new KubernetesNodePools(this._client);
644
+ /** A cluster's add-ons. */
645
+ addons = new KubernetesAddons(this._client);
646
+ list(params = {}, options) {
647
+ return this._get("/kubernetes-clusters", params, options);
648
+ }
649
+ listAll(params = {}, options) {
650
+ return this._listAll("/kubernetes-clusters", params, options);
651
+ }
652
+ create(body, options) {
653
+ return this._post("/kubernetes-clusters", body, options);
654
+ }
655
+ get(clusterId, options) {
656
+ return this._get(path`/kubernetes-clusters/${clusterId}`, void 0, options);
657
+ }
658
+ /** Changes the cluster's size (`PATCH`). */
659
+ update(clusterId, body, options) {
660
+ return this._patch(path`/kubernetes-clusters/${clusterId}`, body, options);
661
+ }
662
+ delete(clusterId, options) {
663
+ return this._delete(path`/kubernetes-clusters/${clusterId}`, options);
664
+ }
665
+ /** Upgrades the cluster to a newer Kubernetes version. */
666
+ upgrade(clusterId, body, options) {
667
+ return this._post(path`/kubernetes-clusters/${clusterId}/upgrade`, body, options);
668
+ }
669
+ /** Moves every node to another plan. */
670
+ resizePlan(clusterId, body, options) {
671
+ return this._post(path`/kubernetes-clusters/${clusterId}/resize-plan`, body, options);
672
+ }
673
+ /** Changes the cluster's networking. */
674
+ upgradeNetwork(clusterId, body, options) {
675
+ return this._post(path`/kubernetes-clusters/${clusterId}/upgrade-network`, body, options);
676
+ }
677
+ start(clusterId, options) {
678
+ return this._post(path`/kubernetes-clusters/${clusterId}/start`, void 0, options);
679
+ }
680
+ stop(clusterId, options) {
681
+ return this._post(path`/kubernetes-clusters/${clusterId}/stop`, void 0, options);
682
+ }
683
+ /** The cluster's kubeconfig. It holds credentials: store it securely. */
684
+ getKubeconfig(clusterId, options) {
685
+ return this._post(path`/kubernetes-clusters/${clusterId}/kubeconfig`, void 0, options);
686
+ }
687
+ listNodes(clusterId, options) {
688
+ return this._get(path`/kubernetes-clusters/${clusterId}/nodes`, void 0, options);
689
+ }
690
+ getWorkloads(clusterId, options) {
691
+ return this._get(path`/kubernetes-clusters/${clusterId}/workloads`, void 0, options);
692
+ }
693
+ applyManifest(clusterId, body, options) {
694
+ return this._post(path`/kubernetes-clusters/${clusterId}/apply-manifest`, body, options);
695
+ }
696
+ getLiveManifests(clusterId, options) {
697
+ return this._get(path`/kubernetes-clusters/${clusterId}/live-manifests`, void 0, options);
698
+ }
699
+ exposeService(clusterId, body, options) {
700
+ return this._post(path`/kubernetes-clusters/${clusterId}/expose-service`, body, options);
701
+ }
702
+ syncRouting(clusterId, options) {
703
+ return this._post(path`/kubernetes-clusters/${clusterId}/sync-routing`, void 0, options);
704
+ }
705
+ /** Sets whether the cluster's API endpoint is private, public or both. */
706
+ configureEndpointAccess(clusterId, body, options) {
707
+ return this._post(path`/kubernetes-clusters/${clusterId}/endpoint-access`, body, options);
708
+ }
709
+ };
710
+ var KubernetesNodePools = class extends APIResource {
711
+ list(clusterId, params = {}, options) {
712
+ return this._get(path`/kubernetes-clusters/${clusterId}/node-pools`, params, options);
713
+ }
714
+ listAll(clusterId, params = {}, options) {
715
+ return this._listAll(path`/kubernetes-clusters/${clusterId}/node-pools`, params, options);
716
+ }
717
+ create(clusterId, body, options) {
718
+ return this._post(path`/kubernetes-clusters/${clusterId}/node-pools`, body, options);
719
+ }
720
+ /** Sets the pool's whole size: a missing `autoscaling_enabled` turns autoscaling off. */
721
+ scale(clusterId, poolId, body, options) {
722
+ return this._patch(path`/kubernetes-clusters/${clusterId}/node-pools/${poolId}`, body, options);
723
+ }
724
+ delete(clusterId, poolId, options) {
725
+ return this._delete(path`/kubernetes-clusters/${clusterId}/node-pools/${poolId}`, options);
726
+ }
727
+ };
728
+ var KubernetesAddons = class extends APIResource {
729
+ list(clusterId, params = {}, options) {
730
+ return this._get(path`/kubernetes-clusters/${clusterId}/addons`, params, options);
731
+ }
732
+ listAll(clusterId, params = {}, options) {
733
+ return this._listAll(path`/kubernetes-clusters/${clusterId}/addons`, params, options);
734
+ }
735
+ /** Turns an add-on on or off. */
736
+ toggle(clusterId, addonName, body, options) {
737
+ return this._post(path`/kubernetes-clusters/${clusterId}/addons/${addonName}/toggle`, body, options);
738
+ }
739
+ };
740
+
741
+ // src/resources/load-balancers.ts
742
+ var LoadBalancers = class extends APIResource {
743
+ list(params = {}, options) {
744
+ return this._get("/load-balancers", params, options);
745
+ }
746
+ listAll(params = {}, options) {
747
+ return this._listAll("/load-balancers", params, options);
748
+ }
749
+ create(body, options) {
750
+ return this._post("/load-balancers", body, options);
751
+ }
752
+ get(loadBalancerId, options) {
753
+ return this._get(path`/load-balancers/${loadBalancerId}`, void 0, options);
754
+ }
755
+ update(loadBalancerId, body, options) {
756
+ return this._patch(path`/load-balancers/${loadBalancerId}`, body, options);
757
+ }
758
+ delete(loadBalancerId, options) {
759
+ return this._delete(path`/load-balancers/${loadBalancerId}`, options);
760
+ }
761
+ };
762
+
763
+ // src/resources/me.ts
764
+ var MeResource = class extends APIResource {
765
+ /** The organization and project the key acts in, its scopes, kind and expiry. */
766
+ get(options) {
767
+ return this._get("/me", void 0, options);
768
+ }
769
+ /**
770
+ * Revokes the API key making the call. Every later call with it is a
771
+ * `401`. Sent once: a retry would find the key gone.
772
+ */
773
+ revoke(options) {
774
+ return this._delete("/me", options, { noRetry: true });
775
+ }
776
+ };
777
+
778
+ // src/resources/networks.ts
779
+ var Networks = class extends APIResource {
780
+ /** A network's subnets: `client.networks.subnets.list(networkId)`. */
781
+ subnets = new Subnets(this._client);
782
+ /** A subnet's firewall rules: `client.networks.firewallRules.list(subnetId)`. */
783
+ firewallRules = new FirewallRules(this._client);
784
+ list(params = {}, options) {
785
+ return this._get("/networks", params, options);
786
+ }
787
+ listAll(params = {}, options) {
788
+ return this._listAll("/networks", params, options);
789
+ }
790
+ create(body, options) {
791
+ return this._post("/networks", body, options);
792
+ }
793
+ /** What a network costs a month. */
794
+ quote(options) {
795
+ return this._get("/networks/quote", void 0, options);
796
+ }
797
+ get(networkId, options) {
798
+ return this._get(path`/networks/${networkId}`, void 0, options);
799
+ }
800
+ delete(networkId, options) {
801
+ return this._delete(path`/networks/${networkId}`, options);
802
+ }
803
+ };
804
+ var Subnets = class extends APIResource {
805
+ /** One page of a network's subnets. */
806
+ list(networkId, params = {}, options) {
807
+ return this._get(path`/networks/${networkId}/subnets`, params, options);
808
+ }
809
+ listAll(networkId, params = {}, options) {
810
+ return this._listAll(path`/networks/${networkId}/subnets`, params, options);
811
+ }
812
+ create(networkId, body, options) {
813
+ return this._post(path`/networks/${networkId}/subnets`, body, options);
814
+ }
815
+ get(subnetId, options) {
816
+ return this._get(path`/subnets/${subnetId}`, void 0, options);
817
+ }
818
+ delete(subnetId, options) {
819
+ return this._delete(path`/subnets/${subnetId}`, options);
820
+ }
821
+ };
822
+ var FirewallRules = class extends APIResource {
823
+ /** A subnet's rules, plus the `system_rules` every subnet has. */
824
+ list(subnetId, params = {}, options) {
825
+ return this._get(path`/subnets/${subnetId}/firewall-rules`, params, options);
826
+ }
827
+ listAll(subnetId, params = {}, options) {
828
+ return this._listAll(path`/subnets/${subnetId}/firewall-rules`, params, options);
829
+ }
830
+ create(subnetId, body, options) {
831
+ return this._post(path`/subnets/${subnetId}/firewall-rules`, body, options);
832
+ }
833
+ get(subnetId, ruleId, options) {
834
+ return this._get(path`/subnets/${subnetId}/firewall-rules/${ruleId}`, void 0, options);
835
+ }
836
+ delete(ruleId, options) {
837
+ return this._delete(path`/firewall-rules/${ruleId}`, options);
838
+ }
839
+ };
840
+
841
+ // src/resources/operations.ts
842
+ function operationId(ref) {
843
+ const id = typeof ref === "string" ? ref : ref.operation_id;
844
+ if (!id) throw new PantechError("There is no operation to wait for: the response carried no operation_id.");
845
+ return id;
846
+ }
847
+ function operationFailed(op) {
848
+ const reason = op.failure?.reason ?? "The operation failed.";
849
+ const code = op.failure?.code;
850
+ return new OperationFailedError(code ? `${reason} (${code})` : reason, op, { code, detail: reason });
851
+ }
852
+ var Operations = class extends APIResource {
853
+ /** An operation's current state. */
854
+ get(operationId2, options) {
855
+ return this._get(path`/operations/${operationId2}`, void 0, options);
856
+ }
857
+ /**
858
+ * Polls the operation until it succeeds (resolves with it) or fails
859
+ * (rejects with OperationFailedError). Accepts an id or the `202` body of
860
+ * any write: `await client.operations.wait(await client.instances.stop(id))`.
861
+ */
862
+ wait(ref, options = {}) {
863
+ const id = operationId(ref);
864
+ return poll(
865
+ () => this.get(id, { signal: options.signal }),
866
+ (op) => {
867
+ if (op.status === "failed") throw operationFailed(op);
868
+ return op.status === "succeeded";
869
+ },
870
+ `Operation ${id}`,
871
+ options
872
+ );
873
+ }
874
+ /**
875
+ * Streams the operation's changes (Server-Sent Events) until it succeeds
876
+ * or fails, yielding the Operation each time it changes. A stream the API
877
+ * ends early (after 15 minutes, say) rejects with its code; poll with
878
+ * `wait` instead.
879
+ */
880
+ async *stream(operationId2, options = {}) {
881
+ const res = await this._request(
882
+ "GET",
883
+ path`/operations/${operationId2}/events`,
884
+ { responseType: "response" },
885
+ { ...options, headers: { ...options.headers, Accept: "text/event-stream" } }
886
+ );
887
+ if (!res.body) return;
888
+ for await (const event of readEvents(res.body)) {
889
+ if (event.event === "error") {
890
+ let body = {};
891
+ try {
892
+ body = JSON.parse(event.data);
893
+ } catch {
894
+ body = {};
895
+ }
896
+ const detail = body.detail ?? "The operation stream ended before the operation finished.";
897
+ throw new PantechError(body.code ? `${detail} (${body.code})` : detail, {
898
+ code: body.code,
899
+ detail,
900
+ requestId: body.request_id
901
+ });
902
+ }
903
+ if (event.event !== "operation" || !event.data) continue;
904
+ const op = JSON.parse(event.data);
905
+ yield op;
906
+ if (op.status === "succeeded" || op.status === "failed") return;
907
+ }
908
+ }
909
+ };
910
+ async function* readEvents(body) {
911
+ const reader = body.getReader();
912
+ const decoder = new TextDecoder();
913
+ let buffer = "";
914
+ let event = "message";
915
+ let data = [];
916
+ try {
917
+ for (; ; ) {
918
+ const { value, done } = await reader.read();
919
+ buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });
920
+ const lines = buffer.split(/\r\n|\r|\n/);
921
+ buffer = done ? "" : lines.pop() ?? "";
922
+ for (const line of lines) {
923
+ if (line === "") {
924
+ if (data.length > 0) yield { event, data: data.join("\n") };
925
+ event = "message";
926
+ data = [];
927
+ } else if (!line.startsWith(":")) {
928
+ const colon = line.indexOf(":");
929
+ const field = colon === -1 ? line : line.slice(0, colon);
930
+ const value2 = colon === -1 ? "" : line.slice(colon + 1).replace(/^ /, "");
931
+ if (field === "event") event = value2;
932
+ else if (field === "data") data.push(value2);
933
+ }
934
+ }
935
+ if (done) {
936
+ if (data.length > 0) yield { event, data: data.join("\n") };
937
+ return;
938
+ }
939
+ }
940
+ } finally {
941
+ reader.releaseLock();
942
+ await body.cancel().catch(() => void 0);
943
+ }
944
+ }
945
+
946
+ // src/resources/public-ips.ts
947
+ var PublicIps = class extends APIResource {
948
+ /** An address's port forwarding rules. */
949
+ portForwardingRules = new PortForwardingRules(this._client);
950
+ list(params = {}, options) {
951
+ return this._get("/public-ips", params, options);
952
+ }
953
+ listAll(params = {}, options) {
954
+ return this._listAll("/public-ips", params, options);
955
+ }
956
+ create(body, options) {
957
+ return this._post("/public-ips", body, options);
958
+ }
959
+ /** What a public IP costs a month. */
960
+ quote(options) {
961
+ return this._get("/public-ips/quote", void 0, options);
962
+ }
963
+ get(publicIpId, options) {
964
+ return this._get(path`/public-ips/${publicIpId}`, void 0, options);
965
+ }
966
+ delete(publicIpId, options) {
967
+ return this._delete(path`/public-ips/${publicIpId}`, options);
968
+ }
969
+ attach(publicIpId, body, options) {
970
+ return this._post(path`/public-ips/${publicIpId}/attach`, body, options);
971
+ }
972
+ detach(publicIpId, options) {
973
+ return this._post(path`/public-ips/${publicIpId}/detach`, void 0, options);
974
+ }
975
+ };
976
+ var PortForwardingRules = class extends APIResource {
977
+ list(publicIpId, params = {}, options) {
978
+ return this._get(path`/public-ips/${publicIpId}/port-forwarding-rules`, params, options);
979
+ }
980
+ listAll(publicIpId, params = {}, options) {
981
+ return this._listAll(path`/public-ips/${publicIpId}/port-forwarding-rules`, params, options);
982
+ }
983
+ create(publicIpId, body, options) {
984
+ return this._post(path`/public-ips/${publicIpId}/port-forwarding-rules`, body, options);
985
+ }
986
+ get(publicIpId, ruleId, options) {
987
+ return this._get(path`/public-ips/${publicIpId}/port-forwarding-rules/${ruleId}`, void 0, options);
988
+ }
989
+ delete(ruleId, options) {
990
+ return this._delete(path`/port-forwarding-rules/${ruleId}`, options);
991
+ }
992
+ };
993
+
994
+ // src/resources/security-groups.ts
995
+ var SecurityGroups = class extends APIResource {
996
+ list(params = {}, options) {
997
+ return this._get("/security-groups", params, options);
998
+ }
999
+ listAll(params = {}, options) {
1000
+ return this._listAll("/security-groups", params, options);
1001
+ }
1002
+ create(body, options) {
1003
+ return this._post("/security-groups", body, options);
1004
+ }
1005
+ get(securityGroupId, options) {
1006
+ return this._get(path`/security-groups/${securityGroupId}`, void 0, options);
1007
+ }
1008
+ delete(securityGroupId, options) {
1009
+ return this._delete(path`/security-groups/${securityGroupId}`, options);
1010
+ }
1011
+ /** Replaces every rule of the group. */
1012
+ replaceRules(securityGroupId, body, options) {
1013
+ return this._put(path`/security-groups/${securityGroupId}/rules`, body, options);
1014
+ }
1015
+ };
1016
+
1017
+ // src/resources/snapshots.ts
1018
+ var Snapshots = class extends APIResource {
1019
+ list(params = {}, options) {
1020
+ return this._get("/snapshots", params, options);
1021
+ }
1022
+ listAll(params = {}, options) {
1023
+ return this._listAll("/snapshots", params, options);
1024
+ }
1025
+ /** What a snapshot of `size_gb` costs. */
1026
+ quote(params, options) {
1027
+ return this._get("/snapshots/quote", params, options);
1028
+ }
1029
+ get(snapshotId, options) {
1030
+ return this._get(path`/snapshots/${snapshotId}`, void 0, options);
1031
+ }
1032
+ delete(snapshotId, options) {
1033
+ return this._delete(path`/snapshots/${snapshotId}`, options);
1034
+ }
1035
+ /** Restores the snapshot into a new volume. */
1036
+ restore(snapshotId, body, options) {
1037
+ return this._post(path`/snapshots/${snapshotId}/restore`, body, options);
1038
+ }
1039
+ usage(snapshotId, options) {
1040
+ return this._get(path`/snapshots/${snapshotId}/usage`, void 0, options);
1041
+ }
1042
+ };
1043
+
1044
+ // src/resources/ssh-keys.ts
1045
+ var SshKeys = class extends APIResource {
1046
+ list(params = {}, options) {
1047
+ return this._get("/ssh-keys", params, options);
1048
+ }
1049
+ listAll(params = {}, options) {
1050
+ return this._listAll("/ssh-keys", params, options);
1051
+ }
1052
+ /**
1053
+ * Adds your public key, or generates a key pair and returns the private
1054
+ * key in this response only. If the call fails without an answer, list
1055
+ * your keys before trying again.
1056
+ */
1057
+ create(body, options) {
1058
+ return this._post("/ssh-keys", body, options, { noRetry: true });
1059
+ }
1060
+ get(sshKeyId, options) {
1061
+ return this._get(path`/ssh-keys/${sshKeyId}`, void 0, options);
1062
+ }
1063
+ /** Deletes the key. A repeat after it is gone is a `404`: treat that as done. */
1064
+ delete(sshKeyId, options) {
1065
+ return this._delete(path`/ssh-keys/${sshKeyId}`, options, { noRetry: true });
1066
+ }
1067
+ };
1068
+
1069
+ // src/resources/usage.ts
1070
+ var Usage = class extends APIResource {
1071
+ /** The organization's usage this period. */
1072
+ get(options) {
1073
+ return this._get("/usage", void 0, options);
1074
+ }
1075
+ /** Usage broken down per resource. */
1076
+ byResource(params = {}, options) {
1077
+ return this._get("/usage/by-resource", params, options);
1078
+ }
1079
+ };
1080
+
1081
+ // src/resources/volumes.ts
1082
+ var Volumes = class extends APIResource {
1083
+ /** Snapshots of a volume. */
1084
+ snapshots = new VolumeSnapshots(this._client);
1085
+ list(params = {}, options) {
1086
+ return this._get("/volumes", params, options);
1087
+ }
1088
+ listAll(params = {}, options) {
1089
+ return this._listAll("/volumes", params, options);
1090
+ }
1091
+ create(body, options) {
1092
+ return this._post("/volumes", body, options);
1093
+ }
1094
+ get(volumeId, options) {
1095
+ return this._get(path`/volumes/${volumeId}`, void 0, options);
1096
+ }
1097
+ delete(volumeId, options) {
1098
+ return this._delete(path`/volumes/${volumeId}`, options);
1099
+ }
1100
+ attach(volumeId, body, options) {
1101
+ return this._post(path`/volumes/${volumeId}/attach`, body, options);
1102
+ }
1103
+ detach(volumeId, options) {
1104
+ return this._post(path`/volumes/${volumeId}/detach`, void 0, options);
1105
+ }
1106
+ /** Grows a detached volume. */
1107
+ resize(volumeId, body, options) {
1108
+ return this._post(path`/volumes/${volumeId}/resize`, body, options);
1109
+ }
1110
+ usage(volumeId, options) {
1111
+ return this._get(path`/volumes/${volumeId}/usage`, void 0, options);
1112
+ }
1113
+ getSnapshotSchedule(volumeId, options) {
1114
+ return this._get(path`/volumes/${volumeId}/snapshot-schedule`, void 0, options);
1115
+ }
1116
+ setSnapshotSchedule(volumeId, body, options) {
1117
+ return this._put(path`/volumes/${volumeId}/snapshot-schedule`, body, options);
1118
+ }
1119
+ };
1120
+ var VolumeSnapshots = class extends APIResource {
1121
+ list(volumeId, params = {}, options) {
1122
+ return this._get(path`/volumes/${volumeId}/snapshots`, params, options);
1123
+ }
1124
+ listAll(volumeId, params = {}, options) {
1125
+ return this._listAll(path`/volumes/${volumeId}/snapshots`, params, options);
1126
+ }
1127
+ create(volumeId, body, options) {
1128
+ return this._post(path`/volumes/${volumeId}/snapshots`, body, options);
1129
+ }
1130
+ };
1131
+
1132
+ // src/resources/webhooks.ts
1133
+ var Webhooks = class extends APIResource {
1134
+ /** A webhook's delivery log. */
1135
+ deliveries = new WebhookDeliveries(this._client);
1136
+ list(params = {}, options) {
1137
+ return this._get("/webhooks", params, options);
1138
+ }
1139
+ listAll(params = {}, options) {
1140
+ return this._listAll("/webhooks", params, options);
1141
+ }
1142
+ /** Every event type a webhook can subscribe to. */
1143
+ listEventTypes(params = {}, options) {
1144
+ return this._get("/webhook-event-types", params, options);
1145
+ }
1146
+ /** Creates a webhook. Its signing `secret` is in this response only. */
1147
+ create(body, options) {
1148
+ return this._post("/webhooks", body, options);
1149
+ }
1150
+ get(webhookId, options) {
1151
+ return this._get(path`/webhooks/${webhookId}`, void 0, options);
1152
+ }
1153
+ update(webhookId, body, options) {
1154
+ return this._patch(path`/webhooks/${webhookId}`, body, options);
1155
+ }
1156
+ /** Deletes the webhook. A repeat after it is gone is a `404`: treat that as done. */
1157
+ delete(webhookId, options) {
1158
+ return this._delete(path`/webhooks/${webhookId}`, options, { noRetry: true });
1159
+ }
1160
+ /** Sends a test event. Each call sends another. */
1161
+ test(webhookId, options) {
1162
+ return this._post(path`/webhooks/${webhookId}/test`, void 0, options, { noRetry: true });
1163
+ }
1164
+ /** Issues a new signing secret, in this response only. The previous one keeps signing for 24 hours. */
1165
+ rotateSecret(webhookId, options) {
1166
+ return this._post(path`/webhooks/${webhookId}/rotate-secret`, void 0, options, { noRetry: true });
1167
+ }
1168
+ };
1169
+ var WebhookDeliveries = class extends APIResource {
1170
+ list(webhookId, params = {}, options) {
1171
+ return this._get(path`/webhooks/${webhookId}/deliveries`, params, options);
1172
+ }
1173
+ listAll(webhookId, params = {}, options) {
1174
+ return this._listAll(path`/webhooks/${webhookId}/deliveries`, params, options);
1175
+ }
1176
+ get(webhookId, deliveryId, options) {
1177
+ return this._get(path`/webhooks/${webhookId}/deliveries/${deliveryId}`, void 0, options);
1178
+ }
1179
+ /** Sends the delivery again. Each call sends another. */
1180
+ redeliver(webhookId, deliveryId, options) {
1181
+ return this._post(path`/webhooks/${webhookId}/deliveries/${deliveryId}/redeliver`, void 0, options, {
1182
+ noRetry: true
1183
+ });
1184
+ }
1185
+ };
1186
+
1187
+ // src/version.ts
1188
+ var VERSION = "0.1.0";
1189
+
1190
+ // src/client.ts
1191
+ var DEFAULT_BASE_URL = "https://api.pantechdynamics.com/public/v1";
1192
+ var DEFAULT_TIMEOUT_MS = 6e4;
1193
+ var DEFAULT_MAX_RETRIES = 3;
1194
+ var MAX_RETRY_AFTER_MS = 6e4;
1195
+ var RETRY_STATUSES = /* @__PURE__ */ new Set([429, 500, 502, 503, 504]);
1196
+ var Pantech = class {
1197
+ baseURL;
1198
+ maxRetries;
1199
+ timeout;
1200
+ // Private fields are invisible to console.log and JSON.stringify, so the
1201
+ // key cannot leak through a logged client.
1202
+ #apiKey;
1203
+ #fetch;
1204
+ #headers;
1205
+ /** `GET` and `DELETE /me`: the API key making the call. */
1206
+ me;
1207
+ /** Credit, credit purchases and receipts. */
1208
+ billing;
1209
+ regions;
1210
+ plans;
1211
+ images;
1212
+ diskOfferings;
1213
+ databaseEngines;
1214
+ kubernetesVersions;
1215
+ pricing;
1216
+ usage;
1217
+ instances;
1218
+ instanceOrders;
1219
+ /** VPC networks, with their subnets and firewall rules. */
1220
+ networks;
1221
+ /** Public IPs, with their port forwarding rules. */
1222
+ publicIps;
1223
+ loadBalancers;
1224
+ /** Managed Kubernetes clusters, with node pools and add-ons. */
1225
+ kubernetes;
1226
+ securityGroups;
1227
+ volumes;
1228
+ snapshots;
1229
+ databases;
1230
+ databaseOrders;
1231
+ sshKeys;
1232
+ webhooks;
1233
+ operations;
1234
+ constructor(options = {}) {
1235
+ const apiKey = options.apiKey ?? readEnv("PANTECH_API_KEY");
1236
+ if (!apiKey) {
1237
+ throw new PantechError(
1238
+ "No Pantech API key. Pass `apiKey` or set PANTECH_API_KEY. Create a key in the console under Organization \u203A API keys."
1239
+ );
1240
+ }
1241
+ this.#apiKey = apiKey;
1242
+ this.baseURL = (options.baseURL ?? readEnv("PANTECH_API_URL") ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
1243
+ this.maxRetries = options.maxRetries ?? DEFAULT_MAX_RETRIES;
1244
+ this.timeout = options.timeout ?? DEFAULT_TIMEOUT_MS;
1245
+ this.#fetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
1246
+ this.#headers = {
1247
+ ...options.defaultHeaders,
1248
+ "User-Agent": options.userAgent ?? `pantech-js/${VERSION}`
1249
+ };
1250
+ this.me = new MeResource(this);
1251
+ this.billing = new Billing(this);
1252
+ this.regions = new Regions(this);
1253
+ this.plans = new Plans(this);
1254
+ this.images = new Images(this);
1255
+ this.diskOfferings = new DiskOfferings(this);
1256
+ this.databaseEngines = new DatabaseEngines(this);
1257
+ this.kubernetesVersions = new KubernetesVersions(this);
1258
+ this.pricing = new Pricing(this);
1259
+ this.usage = new Usage(this);
1260
+ this.operations = new Operations(this);
1261
+ this.instances = new Instances(this);
1262
+ this.instanceOrders = new InstanceOrders(this);
1263
+ this.networks = new Networks(this);
1264
+ this.publicIps = new PublicIps(this);
1265
+ this.loadBalancers = new LoadBalancers(this);
1266
+ this.kubernetes = new Kubernetes(this);
1267
+ this.securityGroups = new SecurityGroups(this);
1268
+ this.volumes = new Volumes(this);
1269
+ this.snapshots = new Snapshots(this);
1270
+ this.databases = new Databases(this);
1271
+ this.databaseOrders = new DatabaseOrders(this);
1272
+ this.sshKeys = new SshKeys(this);
1273
+ this.webhooks = new Webhooks(this);
1274
+ }
1275
+ /**
1276
+ * Sends one request and returns its decoded body (undefined for an empty
1277
+ * one). A write carries an Idempotency-Key, reused by every retry, so a
1278
+ * change cannot happen twice.
1279
+ */
1280
+ async request(req, options = {}) {
1281
+ const url = this.baseURL + req.path + queryString(req.query);
1282
+ const isWrite = req.method !== "GET";
1283
+ const headers = {
1284
+ ...this.#headers,
1285
+ Accept: req.responseType === "arrayBuffer" ? "*/*" : "application/json",
1286
+ ...options.headers,
1287
+ Authorization: `Bearer ${this.#apiKey}`
1288
+ };
1289
+ let payload;
1290
+ if (req.body !== void 0) {
1291
+ payload = JSON.stringify(req.body);
1292
+ headers["Content-Type"] = "application/json";
1293
+ }
1294
+ if (isWrite) headers["Idempotency-Key"] = options.idempotencyKey ?? uuid();
1295
+ const maxRetries = options.maxRetries ?? this.maxRetries;
1296
+ const timeout = options.timeout ?? this.timeout;
1297
+ const signal = options.signal;
1298
+ for (let attempt = 0; ; attempt++) {
1299
+ const canRetry = attempt < maxRetries;
1300
+ const attemptSignal = linkSignal(signal, timeout);
1301
+ let res;
1302
+ try {
1303
+ res = await this.#fetch(url, { method: req.method, headers, body: payload, signal: attemptSignal.signal });
1304
+ } catch (err) {
1305
+ attemptSignal.done();
1306
+ if (signal?.aborted) throw signal.reason;
1307
+ const error = connectionError(err, attemptSignal.timedOut(), isWrite);
1308
+ if (canRetry && !req.noRetry) {
1309
+ await sleep(backoff(attempt), signal);
1310
+ continue;
1311
+ }
1312
+ throw error;
1313
+ }
1314
+ if (res.ok) {
1315
+ try {
1316
+ return await readBody(res, req.responseType);
1317
+ } catch (err) {
1318
+ if (signal?.aborted) throw signal.reason;
1319
+ if (err instanceof PantechError) throw err;
1320
+ throw connectionError(err, attemptSignal.timedOut(), isWrite);
1321
+ } finally {
1322
+ if (req.responseType !== "response") attemptSignal.done();
1323
+ else attemptSignal.stopTimer();
1324
+ }
1325
+ }
1326
+ let text = "";
1327
+ try {
1328
+ text = await res.text();
1329
+ } catch {
1330
+ text = "";
1331
+ } finally {
1332
+ attemptSignal.done();
1333
+ }
1334
+ const retryAfter = parseRetryAfter(res.headers.get("retry-after"));
1335
+ if (canRetry && RETRY_STATUSES.has(res.status) && (!req.noRetry || res.status === 429)) {
1336
+ await sleep(retryAfter !== void 0 ? Math.min(retryAfter, MAX_RETRY_AFTER_MS) : backoff(attempt), signal);
1337
+ continue;
1338
+ }
1339
+ throw errorFromResponse(
1340
+ res.status,
1341
+ res.headers,
1342
+ text,
1343
+ retryAfter === void 0 ? void 0 : Math.ceil(retryAfter / 1e3)
1344
+ );
1345
+ }
1346
+ }
1347
+ };
1348
+ function queryString(query) {
1349
+ if (!query) return "";
1350
+ const params = new URLSearchParams();
1351
+ for (const [key, value] of Object.entries(query)) {
1352
+ if (value === void 0 || value === null) continue;
1353
+ for (const v of Array.isArray(value) ? value : [value]) params.append(key, String(v));
1354
+ }
1355
+ const s = params.toString();
1356
+ return s ? `?${s}` : "";
1357
+ }
1358
+ async function readBody(res, type) {
1359
+ if (type === "response") return res;
1360
+ if (type === "arrayBuffer") return res.arrayBuffer();
1361
+ const text = await res.text();
1362
+ if (res.status === 204 || text === "") return void 0;
1363
+ try {
1364
+ return JSON.parse(text);
1365
+ } catch (cause) {
1366
+ throw new PantechError("The Pantech API sent a response that is not valid JSON.", {
1367
+ status: res.status,
1368
+ requestId: res.headers.get("x-request-id") ?? void 0,
1369
+ headers: res.headers,
1370
+ cause
1371
+ });
1372
+ }
1373
+ }
1374
+ function backoff(attempt) {
1375
+ const base = Math.min(500 * 2 ** attempt, 8e3);
1376
+ return base * (1 - Math.random() * 0.25);
1377
+ }
1378
+ function parseRetryAfter(value) {
1379
+ if (!value) return void 0;
1380
+ const seconds = Number(value);
1381
+ if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
1382
+ const date = Date.parse(value);
1383
+ if (!Number.isNaN(date)) return Math.max(0, date - Date.now());
1384
+ return void 0;
1385
+ }
1386
+ function connectionError(cause, timedOut, isWrite) {
1387
+ const maybeApplied = isWrite ? " The change may still have gone through: check before trying again, and retry with the same idempotencyKey." : " Try again in a moment.";
1388
+ if (timedOut) return new APITimeoutError(`The Pantech API did not answer in time.${maybeApplied}`, { cause });
1389
+ return new APIConnectionError(
1390
+ isWrite ? `The connection to the Pantech API failed.${maybeApplied}` : "Could not reach the Pantech API. Check the network connection and try again.",
1391
+ { cause }
1392
+ );
1393
+ }
1394
+ function linkSignal(parent, timeout) {
1395
+ const controller = new AbortController();
1396
+ let timedOut = false;
1397
+ const onAbort = () => controller.abort(parent?.reason);
1398
+ if (parent?.aborted) controller.abort(parent.reason);
1399
+ else parent?.addEventListener("abort", onAbort, { once: true });
1400
+ const timer = timeout > 0 && Number.isFinite(timeout) ? setTimeout(() => {
1401
+ timedOut = true;
1402
+ controller.abort(new APITimeoutError("The Pantech API did not answer in time."));
1403
+ }, timeout) : void 0;
1404
+ return {
1405
+ signal: controller.signal,
1406
+ timedOut: () => timedOut,
1407
+ /** For a streamed body: the timeout covers the headers only. */
1408
+ stopTimer: () => clearTimeout(timer),
1409
+ done: () => {
1410
+ clearTimeout(timer);
1411
+ parent?.removeEventListener("abort", onAbort);
1412
+ }
1413
+ };
1414
+ }
1415
+
1416
+ // src/webhooks.ts
1417
+ var WEBHOOK_SIGNATURE_HEADER = "Pantech-Signature";
1418
+ var DEFAULT_WEBHOOK_TOLERANCE_SECONDS = 300;
1419
+ var encoder = new TextEncoder();
1420
+ function bytes(body) {
1421
+ if (typeof body === "string") return encoder.encode(body);
1422
+ return body instanceof Uint8Array ? body : new Uint8Array(body);
1423
+ }
1424
+ function subtle() {
1425
+ const s = globalThis.crypto?.subtle;
1426
+ if (!s) {
1427
+ throw new PantechError(
1428
+ "Web Crypto is not available in this runtime. On Node.js 18 start node with --experimental-global-webcrypto, or use Node.js 20 or later."
1429
+ );
1430
+ }
1431
+ return s;
1432
+ }
1433
+ async function hmacHex(secret, timestamp, body) {
1434
+ const s = subtle();
1435
+ const key = await s.importKey("raw", encoder.encode(secret), { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
1436
+ const prefix = encoder.encode(`${timestamp}.`);
1437
+ const message = new Uint8Array(prefix.length + body.length);
1438
+ message.set(prefix, 0);
1439
+ message.set(body, prefix.length);
1440
+ const mac = new Uint8Array(await s.sign("HMAC", key, message));
1441
+ return Array.from(mac, (b) => b.toString(16).padStart(2, "0")).join("");
1442
+ }
1443
+ function timingSafeEqual(a, b) {
1444
+ if (a.length !== b.length) return false;
1445
+ let diff = 0;
1446
+ for (let i = 0; i < a.length; i++) diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
1447
+ return diff === 0;
1448
+ }
1449
+ async function verifyWebhook(options) {
1450
+ const { secret, header } = options;
1451
+ if (!secret) throw new WebhookVerificationError("No webhook secret was given to verify the signature with.");
1452
+ if (!header) throw new WebhookVerificationError(`The ${WEBHOOK_SIGNATURE_HEADER} header is missing.`);
1453
+ let timestamp;
1454
+ const signatures = [];
1455
+ for (const part of header.split(",")) {
1456
+ const eq = part.indexOf("=");
1457
+ if (eq === -1) continue;
1458
+ const key = part.slice(0, eq).trim();
1459
+ const value = part.slice(eq + 1).trim();
1460
+ if (key === "t") timestamp = value;
1461
+ else if (key === "v1" && value) signatures.push(value.toLowerCase());
1462
+ }
1463
+ if (!timestamp || !/^\d+$/.test(timestamp) || signatures.length === 0) {
1464
+ throw new WebhookVerificationError(`The ${WEBHOOK_SIGNATURE_HEADER} header is malformed.`);
1465
+ }
1466
+ const tolerance = options.toleranceSeconds ?? DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
1467
+ const now = options.now ?? Math.floor(Date.now() / 1e3);
1468
+ if (tolerance > 0 && Math.abs(now - Number(timestamp)) > tolerance) {
1469
+ throw new WebhookVerificationError(
1470
+ `The webhook's timestamp is more than ${tolerance} seconds from now. Check the server's clock, or this may be a replayed delivery.`
1471
+ );
1472
+ }
1473
+ const expected = await hmacHex(secret, timestamp, bytes(options.body));
1474
+ let matched = false;
1475
+ for (const signature of signatures) {
1476
+ if (timingSafeEqual(expected, signature)) matched = true;
1477
+ }
1478
+ if (!matched) {
1479
+ throw new WebhookVerificationError(
1480
+ "The webhook signature does not match. Check the secret, and pass the raw body."
1481
+ );
1482
+ }
1483
+ }
1484
+ async function signWebhook(options) {
1485
+ const t = String(options.timestamp ?? Math.floor(Date.now() / 1e3));
1486
+ return `t=${t},v1=${await hmacHex(options.secret, t, bytes(options.body))}`;
1487
+ }
1488
+
1489
+ exports.APIConnectionError = APIConnectionError;
1490
+ exports.APITimeoutError = APITimeoutError;
1491
+ exports.AuthenticationError = AuthenticationError;
1492
+ exports.BadRequestError = BadRequestError;
1493
+ exports.ConflictError = ConflictError;
1494
+ exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
1495
+ exports.DEFAULT_WEBHOOK_TOLERANCE_SECONDS = DEFAULT_WEBHOOK_TOLERANCE_SECONDS;
1496
+ exports.InternalServerError = InternalServerError;
1497
+ exports.NotFoundError = NotFoundError;
1498
+ exports.OperationFailedError = OperationFailedError;
1499
+ exports.OrderFailedError = OrderFailedError;
1500
+ exports.Pantech = Pantech;
1501
+ exports.PantechError = PantechError;
1502
+ exports.PaymentRequiredError = PaymentRequiredError;
1503
+ exports.PermissionDeniedError = PermissionDeniedError;
1504
+ exports.RateLimitError = RateLimitError;
1505
+ exports.VERSION = VERSION;
1506
+ exports.ValidationError = ValidationError;
1507
+ exports.WEBHOOK_SIGNATURE_HEADER = WEBHOOK_SIGNATURE_HEADER;
1508
+ exports.WaitTimeoutError = WaitTimeoutError;
1509
+ exports.WebhookVerificationError = WebhookVerificationError;
1510
+ exports.isPantechError = isPantechError;
1511
+ exports.paginate = paginate;
1512
+ exports.signWebhook = signWebhook;
1513
+ exports.verifyWebhook = verifyWebhook;
1514
+ //# sourceMappingURL=index.cjs.map
1515
+ //# sourceMappingURL=index.cjs.map