@hoststack.dev/sdk 0.8.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 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/gethoststack/sdk](https://github.com/gethoststack/sdk)
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/403
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.status, err.body
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/gethoststack/sdk/issues](https://github.com/gethoststack/sdk/issues)
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
- constructor(statusCode, message) {
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. Check your API key.") {
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
- constructor(message = "Rate limit exceeded. Try again later.") {
26
- super(429, message);
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
- /** List all databases for a project. */
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
  }
@@ -374,22 +401,24 @@ var EnvVarsResource = class {
374
401
  async update(teamId, serviceId, envVarId, data) {
375
402
  const tid = await this.client.resolveId(teamId, { kind: "team" });
376
403
  const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
377
- const eid = await this.client.resolveId(envVarId, {
378
- kind: "envVar",
379
- teamId: tid,
380
- serviceId: sid
381
- });
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
+ }
382
410
  return this.client.request("PATCH", `/api/services/${tid}/${sid}/env/${eid}`, data);
383
411
  }
384
412
  /** Delete an environment variable. */
385
413
  async delete(teamId, serviceId, envVarId) {
386
414
  const tid = await this.client.resolveId(teamId, { kind: "team" });
387
415
  const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
388
- const eid = await this.client.resolveId(envVarId, {
389
- kind: "envVar",
390
- teamId: tid,
391
- serviceId: sid
392
- });
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
+ }
393
422
  return this.client.request("DELETE", `/api/services/${tid}/${sid}/env/${eid}`);
394
423
  }
395
424
  /** Bulk set environment variables (create or update). */
@@ -606,7 +635,13 @@ var ServicesResource = class {
606
635
  const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
607
636
  return this.client.request("POST", `/api/services/${tid}/${sid}/resume`);
608
637
  }
609
- /** Get service metrics. */
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
+ */
610
645
  async getMetrics(teamId, serviceId) {
611
646
  const tid = await this.client.resolveId(teamId, { kind: "team" });
612
647
  const sid = await this.client.resolveId(serviceId, { kind: "service", teamId: tid });
@@ -750,8 +785,7 @@ var PREFIX = {
750
785
  database: "db_",
751
786
  domain: "dom_",
752
787
  volume: "vol_",
753
- envVar: "env_",
754
- environment: "environment_",
788
+ environment: "env_",
755
789
  cronExecution: "cjob_"
756
790
  };
757
791
  var HostStack = class {
@@ -822,13 +856,20 @@ var HostStack = class {
822
856
  const message = data.error ?? `HTTP ${res.status}`;
823
857
  switch (res.status) {
824
858
  case 401:
825
- throw new AuthenticationError(message);
859
+ throw new AuthenticationError(message, data);
860
+ case 403:
861
+ throw new ForbiddenError(message, data);
826
862
  case 404:
827
- throw new NotFoundError(message);
828
- case 429:
829
- throw new RateLimitError(message);
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
+ }
830
871
  default:
831
- throw new HostStackError(res.status, message);
872
+ throw new HostStackError(res.status, message, data);
832
873
  }
833
874
  }
834
875
  if (res.status === 204) {
@@ -904,13 +945,6 @@ var HostStack = class {
904
945
  );
905
946
  return r.domains ?? [];
906
947
  }
907
- case "envVar": {
908
- const r = await this.request(
909
- "GET",
910
- `/api/services/${scope.teamId}/${scope.serviceId}/env`
911
- );
912
- return r.envVars ?? [];
913
- }
914
948
  case "volume": {
915
949
  const r = await this.request("GET", `/api/services/${scope.teamId}/${scope.serviceId}/volumes`);
916
950
  return r.volumes ?? [];
@@ -943,7 +977,6 @@ function cacheScope(scope) {
943
977
  return `${scope.kind}:${scope.teamId}`;
944
978
  case "deploy":
945
979
  case "volume":
946
- case "envVar":
947
980
  case "cronExecution":
948
981
  return `${scope.kind}:${scope.teamId}:${scope.serviceId}`;
949
982
  }
@@ -971,6 +1004,8 @@ function wrapArray(items, params) {
971
1004
  }
972
1005
 
973
1006
  exports.AuthenticationError = AuthenticationError;
1007
+ exports.ConflictError = ConflictError;
1008
+ exports.ForbiddenError = ForbiddenError;
974
1009
  exports.HostStack = HostStack;
975
1010
  exports.HostStackError = HostStackError;
976
1011
  exports.NotFoundError = NotFoundError;