@hoststack.dev/sdk 0.7.0 → 0.8.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/README.md +23 -17
- package/dist/index.cjs +97 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +139 -34
- package/dist/index.d.ts +139 -34
- package/dist/index.js +96 -35
- package/dist/index.js.map +1 -1
- package/package.json +1 -4
package/dist/index.js
CHANGED
|
@@ -1,28 +1,49 @@
|
|
|
1
1
|
// src/errors.ts
|
|
2
2
|
var HostStackError = class extends Error {
|
|
3
3
|
statusCode;
|
|
4
|
-
|
|
4
|
+
/** Raw response body, if it parsed as JSON. Useful for surfacing field-level validation errors. */
|
|
5
|
+
body;
|
|
6
|
+
constructor(statusCode, message, body) {
|
|
5
7
|
super(message);
|
|
6
8
|
this.name = "HostStackError";
|
|
7
9
|
this.statusCode = statusCode;
|
|
10
|
+
this.body = body;
|
|
8
11
|
}
|
|
9
12
|
};
|
|
10
13
|
var AuthenticationError = class extends HostStackError {
|
|
11
|
-
constructor(message = "Authentication failed
|
|
12
|
-
super(401, message);
|
|
14
|
+
constructor(message = "Authentication failed \u2014 check your API key.", body) {
|
|
15
|
+
super(401, message, body);
|
|
13
16
|
this.name = "AuthenticationError";
|
|
14
17
|
}
|
|
15
18
|
};
|
|
19
|
+
var ForbiddenError = class extends HostStackError {
|
|
20
|
+
constructor(message = "Permission denied \u2014 API key lacks the required scope.", body) {
|
|
21
|
+
super(403, message, body);
|
|
22
|
+
this.name = "ForbiddenError";
|
|
23
|
+
}
|
|
24
|
+
};
|
|
16
25
|
var NotFoundError = class extends HostStackError {
|
|
17
|
-
constructor(message = "Resource not found.") {
|
|
18
|
-
super(404, message);
|
|
26
|
+
constructor(message = "Resource not found.", body) {
|
|
27
|
+
super(404, message, body);
|
|
19
28
|
this.name = "NotFoundError";
|
|
20
29
|
}
|
|
21
30
|
};
|
|
31
|
+
var ConflictError = class extends HostStackError {
|
|
32
|
+
constructor(message = "Conflict \u2014 the resource state prevents this operation.", body) {
|
|
33
|
+
super(409, message, body);
|
|
34
|
+
this.name = "ConflictError";
|
|
35
|
+
}
|
|
36
|
+
};
|
|
22
37
|
var RateLimitError = class extends HostStackError {
|
|
23
|
-
|
|
24
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Seconds to wait before retrying, parsed from the `Retry-After` response
|
|
40
|
+
* header. `undefined` if the server didn't send one.
|
|
41
|
+
*/
|
|
42
|
+
retryAfter;
|
|
43
|
+
constructor(message = "Rate limit exceeded \u2014 back off and retry.", retryAfter, body) {
|
|
44
|
+
super(429, message, body);
|
|
25
45
|
this.name = "RateLimitError";
|
|
46
|
+
this.retryAfter = retryAfter;
|
|
26
47
|
}
|
|
27
48
|
};
|
|
28
49
|
|
|
@@ -67,9 +88,15 @@ var DatabasesResource = class {
|
|
|
67
88
|
constructor(client) {
|
|
68
89
|
this.client = client;
|
|
69
90
|
}
|
|
70
|
-
/**
|
|
91
|
+
/**
|
|
92
|
+
* List databases for the team. Pass `projectId` to scope to one
|
|
93
|
+
* project; omit it to list every database the team owns.
|
|
94
|
+
*/
|
|
71
95
|
async list(teamId, projectId) {
|
|
72
96
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
97
|
+
if (projectId === void 0) {
|
|
98
|
+
return this.client.request("GET", `/api/databases/${tid}`);
|
|
99
|
+
}
|
|
73
100
|
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
74
101
|
return this.client.request("GET", `/api/databases/${tid}?projectId=${pid}`);
|
|
75
102
|
}
|
|
@@ -138,6 +165,34 @@ var DatabasesResource = class {
|
|
|
138
165
|
const did = await this.client.resolveId(databaseId, { kind: "database", teamId: tid });
|
|
139
166
|
return this.client.request("POST", `/api/databases/${tid}/${did}/query`, { sql });
|
|
140
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* v89 Phase 4: trigger a single-node → HA migration (Patroni 3-node
|
|
170
|
+
* cluster). The customer-facing database briefly goes read-only during
|
|
171
|
+
* the pg_dump → bootstrap → pg_restore. The standalone container stays
|
|
172
|
+
* running (read-only) for 24 h as a rollback target before being
|
|
173
|
+
* decommissioned automatically.
|
|
174
|
+
*
|
|
175
|
+
* Requires (server-side): PATRONI_ENABLED env on the deployment +
|
|
176
|
+
* `teams.ha_beta=true` on this team. Throws 403 otherwise.
|
|
177
|
+
*
|
|
178
|
+
* Returns 202 — the upgrade is async; poll `get(...)` until
|
|
179
|
+
* `pgEngineType === 'patroni'` + `status === 'available'` to confirm.
|
|
180
|
+
*/
|
|
181
|
+
async upgradeToHa(teamId, databaseId) {
|
|
182
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
183
|
+
const did = await this.client.resolveId(databaseId, { kind: "database", teamId: tid });
|
|
184
|
+
return this.client.request("POST", `/api/databases/${tid}/${did}/upgrade-to-ha`);
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* v89 Phase 5: cluster topology + failover history for a Patroni-managed
|
|
188
|
+
* Postgres database. Returns 400 for standalone databases — call
|
|
189
|
+
* `get(...)` first if you need to branch.
|
|
190
|
+
*/
|
|
191
|
+
async getCluster(teamId, databaseId) {
|
|
192
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
193
|
+
const did = await this.client.resolveId(databaseId, { kind: "database", teamId: tid });
|
|
194
|
+
return this.client.request("GET", `/api/databases/${tid}/${did}/cluster`);
|
|
195
|
+
}
|
|
141
196
|
};
|
|
142
197
|
|
|
143
198
|
// src/resources/deploys.ts
|
|
@@ -344,22 +399,24 @@ var EnvVarsResource = class {
|
|
|
344
399
|
async update(teamId, serviceId, envVarId, data) {
|
|
345
400
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
346
401
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
347
|
-
const eid =
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
402
|
+
const eid = typeof envVarId === "number" ? envVarId : Number.parseInt(envVarId, 10);
|
|
403
|
+
if (Number.isNaN(eid)) {
|
|
404
|
+
throw new Error(
|
|
405
|
+
`Invalid envVarId "${envVarId}": expected a numeric id (env vars have no publicId \u2014 read it from list()).`
|
|
406
|
+
);
|
|
407
|
+
}
|
|
352
408
|
return this.client.request("PATCH", `/api/services/${tid}/${sid}/env/${eid}`, data);
|
|
353
409
|
}
|
|
354
410
|
/** Delete an environment variable. */
|
|
355
411
|
async delete(teamId, serviceId, envVarId) {
|
|
356
412
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
357
413
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
358
|
-
const eid =
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
414
|
+
const eid = typeof envVarId === "number" ? envVarId : Number.parseInt(envVarId, 10);
|
|
415
|
+
if (Number.isNaN(eid)) {
|
|
416
|
+
throw new Error(
|
|
417
|
+
`Invalid envVarId "${envVarId}": expected a numeric id (env vars have no publicId \u2014 read it from list()).`
|
|
418
|
+
);
|
|
419
|
+
}
|
|
363
420
|
return this.client.request("DELETE", `/api/services/${tid}/${sid}/env/${eid}`);
|
|
364
421
|
}
|
|
365
422
|
/** Bulk set environment variables (create or update). */
|
|
@@ -576,7 +633,13 @@ var ServicesResource = class {
|
|
|
576
633
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
577
634
|
return this.client.request("POST", `/api/services/${tid}/${sid}/resume`);
|
|
578
635
|
}
|
|
579
|
-
/**
|
|
636
|
+
/**
|
|
637
|
+
* Get the latest metrics snapshot for a service.
|
|
638
|
+
*
|
|
639
|
+
* Returns `metrics: null` before the first agent sample lands;
|
|
640
|
+
* `serverOverview: null` when the service is not currently placed
|
|
641
|
+
* (suspended, between deploys, etc).
|
|
642
|
+
*/
|
|
580
643
|
async getMetrics(teamId, serviceId) {
|
|
581
644
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
582
645
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
@@ -720,8 +783,7 @@ var PREFIX = {
|
|
|
720
783
|
database: "db_",
|
|
721
784
|
domain: "dom_",
|
|
722
785
|
volume: "vol_",
|
|
723
|
-
|
|
724
|
-
environment: "environment_",
|
|
786
|
+
environment: "env_",
|
|
725
787
|
cronExecution: "cjob_"
|
|
726
788
|
};
|
|
727
789
|
var HostStack = class {
|
|
@@ -792,13 +854,20 @@ var HostStack = class {
|
|
|
792
854
|
const message = data.error ?? `HTTP ${res.status}`;
|
|
793
855
|
switch (res.status) {
|
|
794
856
|
case 401:
|
|
795
|
-
throw new AuthenticationError(message);
|
|
857
|
+
throw new AuthenticationError(message, data);
|
|
858
|
+
case 403:
|
|
859
|
+
throw new ForbiddenError(message, data);
|
|
796
860
|
case 404:
|
|
797
|
-
throw new NotFoundError(message);
|
|
798
|
-
case
|
|
799
|
-
throw new
|
|
861
|
+
throw new NotFoundError(message, data);
|
|
862
|
+
case 409:
|
|
863
|
+
throw new ConflictError(message, data);
|
|
864
|
+
case 429: {
|
|
865
|
+
const header = res.headers.get("Retry-After");
|
|
866
|
+
const retryAfter = header && /^\d+$/.test(header) ? Number(header) : void 0;
|
|
867
|
+
throw new RateLimitError(message, retryAfter, data);
|
|
868
|
+
}
|
|
800
869
|
default:
|
|
801
|
-
throw new HostStackError(res.status, message);
|
|
870
|
+
throw new HostStackError(res.status, message, data);
|
|
802
871
|
}
|
|
803
872
|
}
|
|
804
873
|
if (res.status === 204) {
|
|
@@ -874,13 +943,6 @@ var HostStack = class {
|
|
|
874
943
|
);
|
|
875
944
|
return r.domains ?? [];
|
|
876
945
|
}
|
|
877
|
-
case "envVar": {
|
|
878
|
-
const r = await this.request(
|
|
879
|
-
"GET",
|
|
880
|
-
`/api/services/${scope.teamId}/${scope.serviceId}/env`
|
|
881
|
-
);
|
|
882
|
-
return r.envVars ?? [];
|
|
883
|
-
}
|
|
884
946
|
case "volume": {
|
|
885
947
|
const r = await this.request("GET", `/api/services/${scope.teamId}/${scope.serviceId}/volumes`);
|
|
886
948
|
return r.volumes ?? [];
|
|
@@ -913,7 +975,6 @@ function cacheScope(scope) {
|
|
|
913
975
|
return `${scope.kind}:${scope.teamId}`;
|
|
914
976
|
case "deploy":
|
|
915
977
|
case "volume":
|
|
916
|
-
case "envVar":
|
|
917
978
|
case "cronExecution":
|
|
918
979
|
return `${scope.kind}:${scope.teamId}:${scope.serviceId}`;
|
|
919
980
|
}
|
|
@@ -940,6 +1001,6 @@ function wrapArray(items, params) {
|
|
|
940
1001
|
};
|
|
941
1002
|
}
|
|
942
1003
|
|
|
943
|
-
export { AuthenticationError, HostStack, HostStackError, NotFoundError, RateLimitError, buildPaginationQuery, wrapArray };
|
|
1004
|
+
export { AuthenticationError, ConflictError, ForbiddenError, HostStack, HostStackError, NotFoundError, RateLimitError, buildPaginationQuery, wrapArray };
|
|
944
1005
|
//# sourceMappingURL=index.js.map
|
|
945
1006
|
//# sourceMappingURL=index.js.map
|