@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/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Official TypeScript SDK for [HostStack](https://hoststack.dev) — the European
|
|
|
11
11
|
- **Website:** [hoststack.dev](https://hoststack.dev)
|
|
12
12
|
- **Documentation:** [hoststack.dev/docs](https://hoststack.dev/docs)
|
|
13
13
|
- **SDK reference:** [hoststack.dev/docs/sdk](https://hoststack.dev/docs/sdk)
|
|
14
|
-
- **Source:** [github.com/
|
|
14
|
+
- **Source:** [github.com/miccidk/hoststack](https://github.com/miccidk/hoststack/tree/master/packages/sdk)
|
|
15
15
|
|
|
16
16
|
## Installation
|
|
17
17
|
|
|
@@ -53,18 +53,18 @@ Generate an API key from your [HostStack dashboard → Settings → API Keys](ht
|
|
|
53
53
|
|
|
54
54
|
## Resources
|
|
55
55
|
|
|
56
|
-
| Resource | Methods
|
|
57
|
-
| ---------------------- |
|
|
58
|
-
| `client.projects` | `list`, `get`, `create`, `update`, `delete`
|
|
59
|
-
| `client.services` | `list`, `get`, `create`, `update`, `delete`, `suspend`, `resume`, `getMetrics`, `getConfig`, `updateConfig`, `getRuntimeLogs`, `streamLogs` |
|
|
60
|
-
| `client.deploys` | `list`, `get`, `trigger`, `cancel`, `rollback`, `promote`, `getLogs`
|
|
61
|
-
| `client.databases` | `list`, `get`, `create`, `update`, `delete`, `suspend`, `resume`, `getCredentials`, `resetPassword`
|
|
62
|
-
| `client.domains` | `list`, `add`, `update`, `remove`, `verify`
|
|
63
|
-
| `client.envVars` | `list`, `create`, `update`, `delete`, `bulkSet`
|
|
64
|
-
| `client.volumes` | `list`, `create`, `update`, `delete`
|
|
65
|
-
| `client.environments` | `list`, `get`, `create`, `update`, `delete`
|
|
66
|
-
| `client.cron` | `list`, `get`, `trigger`
|
|
67
|
-
| `client.notifications` | `listChannels`, `createChannel`, `updateChannel`, `deleteChannel`, `testChannel`
|
|
56
|
+
| Resource | Methods |
|
|
57
|
+
| ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
58
|
+
| `client.projects` | `list`, `get`, `create`, `update`, `delete` |
|
|
59
|
+
| `client.services` | `list`, `get`, `create`, `update`, `delete`, `suspend`, `resume`, `getMetrics`, `getMetricsHistory`, `getConfig`, `updateConfig`, `getRuntimeLogs`, `streamLogs` |
|
|
60
|
+
| `client.deploys` | `list`, `get`, `trigger`, `cancel`, `rollback`, `promote`, `getLogs` |
|
|
61
|
+
| `client.databases` | `list`, `get`, `create`, `update`, `delete`, `suspend`, `resume`, `getCredentials`, `resetPassword`, `query`, `upgradeToHa` |
|
|
62
|
+
| `client.domains` | `list`, `add`, `update`, `remove`, `verify` |
|
|
63
|
+
| `client.envVars` | `list`, `create`, `update`, `delete`, `bulkSet` |
|
|
64
|
+
| `client.volumes` | `list`, `create`, `update`, `delete` |
|
|
65
|
+
| `client.environments` | `list`, `get`, `create`, `update`, `delete` |
|
|
66
|
+
| `client.cron` | `list`, `get`, `trigger` |
|
|
67
|
+
| `client.notifications` | `listChannels`, `createChannel`, `updateChannel`, `deleteChannel`, `testChannel` |
|
|
68
68
|
|
|
69
69
|
Every method's first argument is the team id — accepts either the numeric id or the `team_…` publicId (the SDK resolves publicIds to numeric ids internally and caches the lookup). Full API reference: **[hoststack.dev/docs/sdk](https://hoststack.dev/docs/sdk)**.
|
|
70
70
|
|
|
@@ -76,7 +76,9 @@ Every method's first argument is the team id — accepts either the numeric id o
|
|
|
76
76
|
import {
|
|
77
77
|
HostStack,
|
|
78
78
|
AuthenticationError,
|
|
79
|
+
ForbiddenError,
|
|
79
80
|
NotFoundError,
|
|
81
|
+
ConflictError,
|
|
80
82
|
RateLimitError,
|
|
81
83
|
HostStackError,
|
|
82
84
|
} from '@hoststack.dev/sdk';
|
|
@@ -87,11 +89,15 @@ try {
|
|
|
87
89
|
if (err instanceof NotFoundError) {
|
|
88
90
|
// 404
|
|
89
91
|
} else if (err instanceof AuthenticationError) {
|
|
90
|
-
// 401
|
|
92
|
+
// 401 — API key missing or expired
|
|
93
|
+
} else if (err instanceof ForbiddenError) {
|
|
94
|
+
// 403 — key lacks the required scope (e.g. deploy_only trying to mutate)
|
|
95
|
+
} else if (err instanceof ConflictError) {
|
|
96
|
+
// 409 — resource state prevents the op (already exists, in-flight migration, …)
|
|
91
97
|
} else if (err instanceof RateLimitError) {
|
|
92
|
-
// 429 — err.retryAfter is seconds to wait
|
|
98
|
+
// 429 — err.retryAfter is seconds to wait (from Retry-After header), or undefined
|
|
93
99
|
} else if (err instanceof HostStackError) {
|
|
94
|
-
// any other API error — err.
|
|
100
|
+
// any other API error — err.statusCode and err.body are available
|
|
95
101
|
}
|
|
96
102
|
}
|
|
97
103
|
```
|
|
@@ -104,7 +110,7 @@ try {
|
|
|
104
110
|
|
|
105
111
|
## Support
|
|
106
112
|
|
|
107
|
-
- Issues: [github.com/
|
|
113
|
+
- Issues: [github.com/miccidk/hoststack/issues](https://github.com/miccidk/hoststack/issues)
|
|
108
114
|
- Docs: [hoststack.dev/docs](https://hoststack.dev/docs)
|
|
109
115
|
- Homepage: [hoststack.dev](https://hoststack.dev)
|
|
110
116
|
|
package/dist/index.cjs
CHANGED
|
@@ -3,28 +3,49 @@
|
|
|
3
3
|
// src/errors.ts
|
|
4
4
|
var HostStackError = class extends Error {
|
|
5
5
|
statusCode;
|
|
6
|
-
|
|
6
|
+
/** Raw response body, if it parsed as JSON. Useful for surfacing field-level validation errors. */
|
|
7
|
+
body;
|
|
8
|
+
constructor(statusCode, message, body) {
|
|
7
9
|
super(message);
|
|
8
10
|
this.name = "HostStackError";
|
|
9
11
|
this.statusCode = statusCode;
|
|
12
|
+
this.body = body;
|
|
10
13
|
}
|
|
11
14
|
};
|
|
12
15
|
var AuthenticationError = class extends HostStackError {
|
|
13
|
-
constructor(message = "Authentication failed
|
|
14
|
-
super(401, message);
|
|
16
|
+
constructor(message = "Authentication failed \u2014 check your API key.", body) {
|
|
17
|
+
super(401, message, body);
|
|
15
18
|
this.name = "AuthenticationError";
|
|
16
19
|
}
|
|
17
20
|
};
|
|
21
|
+
var ForbiddenError = class extends HostStackError {
|
|
22
|
+
constructor(message = "Permission denied \u2014 API key lacks the required scope.", body) {
|
|
23
|
+
super(403, message, body);
|
|
24
|
+
this.name = "ForbiddenError";
|
|
25
|
+
}
|
|
26
|
+
};
|
|
18
27
|
var NotFoundError = class extends HostStackError {
|
|
19
|
-
constructor(message = "Resource not found.") {
|
|
20
|
-
super(404, message);
|
|
28
|
+
constructor(message = "Resource not found.", body) {
|
|
29
|
+
super(404, message, body);
|
|
21
30
|
this.name = "NotFoundError";
|
|
22
31
|
}
|
|
23
32
|
};
|
|
33
|
+
var ConflictError = class extends HostStackError {
|
|
34
|
+
constructor(message = "Conflict \u2014 the resource state prevents this operation.", body) {
|
|
35
|
+
super(409, message, body);
|
|
36
|
+
this.name = "ConflictError";
|
|
37
|
+
}
|
|
38
|
+
};
|
|
24
39
|
var RateLimitError = class extends HostStackError {
|
|
25
|
-
|
|
26
|
-
|
|
40
|
+
/**
|
|
41
|
+
* Seconds to wait before retrying, parsed from the `Retry-After` response
|
|
42
|
+
* header. `undefined` if the server didn't send one.
|
|
43
|
+
*/
|
|
44
|
+
retryAfter;
|
|
45
|
+
constructor(message = "Rate limit exceeded \u2014 back off and retry.", retryAfter, body) {
|
|
46
|
+
super(429, message, body);
|
|
27
47
|
this.name = "RateLimitError";
|
|
48
|
+
this.retryAfter = retryAfter;
|
|
28
49
|
}
|
|
29
50
|
};
|
|
30
51
|
|
|
@@ -69,9 +90,15 @@ var DatabasesResource = class {
|
|
|
69
90
|
constructor(client) {
|
|
70
91
|
this.client = client;
|
|
71
92
|
}
|
|
72
|
-
/**
|
|
93
|
+
/**
|
|
94
|
+
* List databases for the team. Pass `projectId` to scope to one
|
|
95
|
+
* project; omit it to list every database the team owns.
|
|
96
|
+
*/
|
|
73
97
|
async list(teamId, projectId) {
|
|
74
98
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
99
|
+
if (projectId === void 0) {
|
|
100
|
+
return this.client.request("GET", `/api/databases/${tid}`);
|
|
101
|
+
}
|
|
75
102
|
const pid = await this.client.resolveId(projectId, { kind: "project", teamId: tid });
|
|
76
103
|
return this.client.request("GET", `/api/databases/${tid}?projectId=${pid}`);
|
|
77
104
|
}
|
|
@@ -140,6 +167,34 @@ var DatabasesResource = class {
|
|
|
140
167
|
const did = await this.client.resolveId(databaseId, { kind: "database", teamId: tid });
|
|
141
168
|
return this.client.request("POST", `/api/databases/${tid}/${did}/query`, { sql });
|
|
142
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* v89 Phase 4: trigger a single-node → HA migration (Patroni 3-node
|
|
172
|
+
* cluster). The customer-facing database briefly goes read-only during
|
|
173
|
+
* the pg_dump → bootstrap → pg_restore. The standalone container stays
|
|
174
|
+
* running (read-only) for 24 h as a rollback target before being
|
|
175
|
+
* decommissioned automatically.
|
|
176
|
+
*
|
|
177
|
+
* Requires (server-side): PATRONI_ENABLED env on the deployment +
|
|
178
|
+
* `teams.ha_beta=true` on this team. Throws 403 otherwise.
|
|
179
|
+
*
|
|
180
|
+
* Returns 202 — the upgrade is async; poll `get(...)` until
|
|
181
|
+
* `pgEngineType === 'patroni'` + `status === 'available'` to confirm.
|
|
182
|
+
*/
|
|
183
|
+
async upgradeToHa(teamId, databaseId) {
|
|
184
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
185
|
+
const did = await this.client.resolveId(databaseId, { kind: "database", teamId: tid });
|
|
186
|
+
return this.client.request("POST", `/api/databases/${tid}/${did}/upgrade-to-ha`);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* v89 Phase 5: cluster topology + failover history for a Patroni-managed
|
|
190
|
+
* Postgres database. Returns 400 for standalone databases — call
|
|
191
|
+
* `get(...)` first if you need to branch.
|
|
192
|
+
*/
|
|
193
|
+
async getCluster(teamId, databaseId) {
|
|
194
|
+
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
195
|
+
const did = await this.client.resolveId(databaseId, { kind: "database", teamId: tid });
|
|
196
|
+
return this.client.request("GET", `/api/databases/${tid}/${did}/cluster`);
|
|
197
|
+
}
|
|
143
198
|
};
|
|
144
199
|
|
|
145
200
|
// src/resources/deploys.ts
|
|
@@ -346,22 +401,24 @@ var EnvVarsResource = class {
|
|
|
346
401
|
async update(teamId, serviceId, envVarId, data) {
|
|
347
402
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
348
403
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
349
|
-
const eid =
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
404
|
+
const eid = typeof envVarId === "number" ? envVarId : Number.parseInt(envVarId, 10);
|
|
405
|
+
if (Number.isNaN(eid)) {
|
|
406
|
+
throw new Error(
|
|
407
|
+
`Invalid envVarId "${envVarId}": expected a numeric id (env vars have no publicId \u2014 read it from list()).`
|
|
408
|
+
);
|
|
409
|
+
}
|
|
354
410
|
return this.client.request("PATCH", `/api/services/${tid}/${sid}/env/${eid}`, data);
|
|
355
411
|
}
|
|
356
412
|
/** Delete an environment variable. */
|
|
357
413
|
async delete(teamId, serviceId, envVarId) {
|
|
358
414
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
359
415
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
360
|
-
const eid =
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
416
|
+
const eid = typeof envVarId === "number" ? envVarId : Number.parseInt(envVarId, 10);
|
|
417
|
+
if (Number.isNaN(eid)) {
|
|
418
|
+
throw new Error(
|
|
419
|
+
`Invalid envVarId "${envVarId}": expected a numeric id (env vars have no publicId \u2014 read it from list()).`
|
|
420
|
+
);
|
|
421
|
+
}
|
|
365
422
|
return this.client.request("DELETE", `/api/services/${tid}/${sid}/env/${eid}`);
|
|
366
423
|
}
|
|
367
424
|
/** Bulk set environment variables (create or update). */
|
|
@@ -578,7 +635,13 @@ var ServicesResource = class {
|
|
|
578
635
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
579
636
|
return this.client.request("POST", `/api/services/${tid}/${sid}/resume`);
|
|
580
637
|
}
|
|
581
|
-
/**
|
|
638
|
+
/**
|
|
639
|
+
* Get the latest metrics snapshot for a service.
|
|
640
|
+
*
|
|
641
|
+
* Returns `metrics: null` before the first agent sample lands;
|
|
642
|
+
* `serverOverview: null` when the service is not currently placed
|
|
643
|
+
* (suspended, between deploys, etc).
|
|
644
|
+
*/
|
|
582
645
|
async getMetrics(teamId, serviceId) {
|
|
583
646
|
const tid = await this.client.resolveId(teamId, { kind: "team" });
|
|
584
647
|
const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
|
|
@@ -722,8 +785,7 @@ var PREFIX = {
|
|
|
722
785
|
database: "db_",
|
|
723
786
|
domain: "dom_",
|
|
724
787
|
volume: "vol_",
|
|
725
|
-
|
|
726
|
-
environment: "environment_",
|
|
788
|
+
environment: "env_",
|
|
727
789
|
cronExecution: "cjob_"
|
|
728
790
|
};
|
|
729
791
|
var HostStack = class {
|
|
@@ -794,13 +856,20 @@ var HostStack = class {
|
|
|
794
856
|
const message = data.error ?? `HTTP ${res.status}`;
|
|
795
857
|
switch (res.status) {
|
|
796
858
|
case 401:
|
|
797
|
-
throw new AuthenticationError(message);
|
|
859
|
+
throw new AuthenticationError(message, data);
|
|
860
|
+
case 403:
|
|
861
|
+
throw new ForbiddenError(message, data);
|
|
798
862
|
case 404:
|
|
799
|
-
throw new NotFoundError(message);
|
|
800
|
-
case
|
|
801
|
-
throw new
|
|
863
|
+
throw new NotFoundError(message, data);
|
|
864
|
+
case 409:
|
|
865
|
+
throw new ConflictError(message, data);
|
|
866
|
+
case 429: {
|
|
867
|
+
const header = res.headers.get("Retry-After");
|
|
868
|
+
const retryAfter = header && /^\d+$/.test(header) ? Number(header) : void 0;
|
|
869
|
+
throw new RateLimitError(message, retryAfter, data);
|
|
870
|
+
}
|
|
802
871
|
default:
|
|
803
|
-
throw new HostStackError(res.status, message);
|
|
872
|
+
throw new HostStackError(res.status, message, data);
|
|
804
873
|
}
|
|
805
874
|
}
|
|
806
875
|
if (res.status === 204) {
|
|
@@ -876,13 +945,6 @@ var HostStack = class {
|
|
|
876
945
|
);
|
|
877
946
|
return r.domains ?? [];
|
|
878
947
|
}
|
|
879
|
-
case "envVar": {
|
|
880
|
-
const r = await this.request(
|
|
881
|
-
"GET",
|
|
882
|
-
`/api/services/${scope.teamId}/${scope.serviceId}/env`
|
|
883
|
-
);
|
|
884
|
-
return r.envVars ?? [];
|
|
885
|
-
}
|
|
886
948
|
case "volume": {
|
|
887
949
|
const r = await this.request("GET", `/api/services/${scope.teamId}/${scope.serviceId}/volumes`);
|
|
888
950
|
return r.volumes ?? [];
|
|
@@ -915,7 +977,6 @@ function cacheScope(scope) {
|
|
|
915
977
|
return `${scope.kind}:${scope.teamId}`;
|
|
916
978
|
case "deploy":
|
|
917
979
|
case "volume":
|
|
918
|
-
case "envVar":
|
|
919
980
|
case "cronExecution":
|
|
920
981
|
return `${scope.kind}:${scope.teamId}:${scope.serviceId}`;
|
|
921
982
|
}
|
|
@@ -943,6 +1004,8 @@ function wrapArray(items, params) {
|
|
|
943
1004
|
}
|
|
944
1005
|
|
|
945
1006
|
exports.AuthenticationError = AuthenticationError;
|
|
1007
|
+
exports.ConflictError = ConflictError;
|
|
1008
|
+
exports.ForbiddenError = ForbiddenError;
|
|
946
1009
|
exports.HostStack = HostStack;
|
|
947
1010
|
exports.HostStackError = HostStackError;
|
|
948
1011
|
exports.NotFoundError = NotFoundError;
|