@krovacloud/sdk 0.1.3 → 0.1.4

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 CHANGED
@@ -139,12 +139,14 @@ var KrovaClient = class {
139
139
  // Cubes
140
140
  // ---------------------------------------------------------------------------
141
141
  cubes = {
142
- /** List Cubes in a Space. Returns the raw (paginated) response body. */
142
+ /** List Cubes in a Space, with pagination metadata. */
143
143
  list: async (spaceId) => {
144
144
  const { data, error, response } = await this.raw.GET("/spaces/{spaceId}/cubes", {
145
145
  params: { path: { spaceId } }
146
146
  });
147
147
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
148
+ if (data === void 0)
149
+ throw krovaErrorFrom(response, { error: "List Cubes response was empty." });
148
150
  return data;
149
151
  },
150
152
  /**
@@ -169,14 +171,18 @@ var KrovaClient = class {
169
171
  }
170
172
  return cube;
171
173
  },
172
- /** Get a single Cube. Returns the raw response body. */
174
+ /** Get a single Cube. Returns the {@link Cube}. */
173
175
  get: async (spaceId, cubeId) => {
174
176
  const { data, error, response } = await this.raw.GET(
175
177
  "/spaces/{spaceId}/cubes/{cubeId}",
176
178
  { params: { path: { spaceId, cubeId } } }
177
179
  );
178
180
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
179
- return data;
181
+ const cube = data?.cube;
182
+ if (!cube) {
183
+ throw krovaErrorFrom(response, { error: "Get Cube response had no `cube`." });
184
+ }
185
+ return cube;
180
186
  },
181
187
  /**
182
188
  * Update a Cube's SSH port.
@@ -201,6 +207,8 @@ var KrovaClient = class {
201
207
  { params: { path: { spaceId, cubeId } } }
202
208
  );
203
209
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
210
+ if (data === void 0)
211
+ throw krovaErrorFrom(response, { error: "Delete Cube response was empty." });
204
212
  return data;
205
213
  },
206
214
  /** Sleep a running Cube (asynchronous — sleep is enqueued). */
@@ -230,18 +238,24 @@ var KrovaClient = class {
230
238
  regions: async () => {
231
239
  const { data, error, response } = await this.raw.GET("/regions");
232
240
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
241
+ if (data === void 0)
242
+ throw krovaErrorFrom(response, { error: "Regions response was empty." });
233
243
  return data;
234
244
  },
235
245
  /** List available OS images. */
236
246
  images: async () => {
237
247
  const { data, error, response } = await this.raw.GET("/images");
238
248
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
249
+ if (data === void 0)
250
+ throw krovaErrorFrom(response, { error: "Images response was empty." });
239
251
  return data;
240
252
  },
241
253
  /** Per-resource hourly rates and volume pricing tiers. */
242
254
  pricing: async () => {
243
255
  const { data, error, response } = await this.raw.GET("/pricing");
244
256
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
257
+ if (data === void 0)
258
+ throw krovaErrorFrom(response, { error: "Pricing response was empty." });
245
259
  return data;
246
260
  }
247
261
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/error.ts","../src/client.ts"],"names":["createClient"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * The error body shape returned by the Krova Cloud API.\n *\n * Per the OpenAPI spec (`components.schemas.Error`), every non-2xx response\n * body is `{ \"error\": string }`. Additional fields may appear over time, so\n * we keep the type open.\n */\nexport interface KrovaErrorBody {\n error?: string;\n [key: string]: unknown;\n}\n\n/**\n * Error thrown by the ergonomic {@link KrovaClient} helpers when the API\n * responds with a non-2xx status.\n *\n * The raw openapi-fetch client (`client.raw`) never throws — it returns\n * `{ data, error, response }`. The helpers wrap that and throw `KrovaError`\n * so callers can `try/catch`.\n */\nexport class KrovaError extends Error {\n /** HTTP status code of the failing response. */\n readonly status: number;\n\n /**\n * A machine-readable error code, when the API surfaces one via the\n * `X-Error-Code` response header. The documented error body only carries a\n * human-readable `error` string, so this is best-effort.\n */\n readonly code?: string;\n\n /**\n * The request id from the `X-Request-Id` response header, when present.\n * Useful when contacting Krova Cloud support about a specific failure.\n */\n readonly requestId?: string;\n\n /** The parsed JSON error body, when the response had one. */\n readonly body?: KrovaErrorBody;\n\n /** The raw `Response` object, for callers that need headers/url/etc. */\n readonly response?: Response;\n\n constructor(\n message: string,\n init: {\n status: number;\n code?: string;\n requestId?: string;\n body?: KrovaErrorBody;\n response?: Response;\n },\n ) {\n super(message);\n this.name = \"KrovaError\";\n this.status = init.status;\n this.code = init.code;\n this.requestId = init.requestId;\n this.body = init.body;\n this.response = init.response;\n // Restore prototype chain for instanceof across compilation targets.\n Object.setPrototypeOf(this, KrovaError.prototype);\n }\n}\n\n/**\n * Build a {@link KrovaError} from a failing response + parsed error body.\n */\nexport function krovaErrorFrom(\n response: Response,\n body: KrovaErrorBody | undefined,\n): KrovaError {\n const message =\n (typeof body?.error === \"string\" && body.error) ||\n response.statusText ||\n `Request failed with status ${response.status}`;\n return new KrovaError(message, {\n status: response.status,\n code: response.headers.get(\"x-error-code\") ?? undefined,\n requestId: response.headers.get(\"x-request-id\") ?? undefined,\n body,\n response,\n });\n}\n","import createClient, { type Client, type Middleware } from \"openapi-fetch\";\nimport { krovaErrorFrom } from \"./error.js\";\nimport type { components, paths } from \"./generated/types.js\";\n\n/** The Cube resource, as defined in the Krova Cloud OpenAPI spec. */\nexport type Cube = components[\"schemas\"][\"Cube\"];\n\n/** Default API base URL — the single `servers[0].url` from the OpenAPI spec. */\nexport const DEFAULT_BASE_URL = \"https://krova.cloud/api/v1\";\n\n/**\n * How the API key is presented to the server.\n *\n * - `\"x-api-key\"` (default) — `X-API-KEY: <key>`, matching the spec's\n * `components.securitySchemes.ApiKeyAuth` (an `apiKey` header named\n * `X-API-KEY`).\n * - `\"bearer\"` — `Authorization: Bearer <key>`, for gateways that expect it.\n */\nexport type AuthScheme = \"x-api-key\" | \"bearer\";\n\nexport interface KrovaClientOptions {\n /**\n * Your Krova Cloud API key (a `kro_...` token). Keys are scoped per Space\n * and inherit the permissions of the membership that created them.\n */\n apiKey: string;\n /** Override the API base URL. Defaults to {@link DEFAULT_BASE_URL}. */\n baseUrl?: string;\n /**\n * Auth header scheme. Defaults to `\"x-api-key\"` (the spec's scheme).\n */\n authScheme?: AuthScheme;\n /**\n * Max automatic retries on retryable statuses (429, 503).\n * Defaults to 2. Set to 0 to disable retries.\n */\n maxRetries?: number;\n /**\n * A custom `fetch` implementation (e.g. for tests or a proxy). Defaults to\n * the global `fetch`.\n */\n fetch?: typeof fetch;\n}\n\n/** Statuses the retry middleware treats as transient. */\nconst RETRYABLE_STATUSES = new Set([429, 503]);\n/** Fallback backoff (ms) when the server sends no `Retry-After` header. */\nconst BASE_BACKOFF_MS = 500;\n/** Cap on any single backoff wait (ms), to keep retries \"small but real\". */\nconst MAX_BACKOFF_MS = 10_000;\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Parse a `Retry-After` header (RFC 7231): either delta-seconds or an\n * HTTP-date. Returns milliseconds to wait, or `null` if absent/unparseable.\n */\nfunction parseRetryAfterMs(headerValue: string | null): number | null {\n if (!headerValue) return null;\n const seconds = Number(headerValue);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n const dateMs = Date.parse(headerValue);\n if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());\n return null;\n}\n\nfunction authMiddleware(apiKey: string, scheme: AuthScheme): Middleware {\n return {\n onRequest({ request }) {\n if (scheme === \"bearer\") {\n request.headers.set(\"Authorization\", `Bearer ${apiKey}`);\n } else {\n request.headers.set(\"X-API-KEY\", apiKey);\n }\n return request;\n },\n };\n}\n\n/**\n * Retry middleware: on a retryable status, wait (honoring `Retry-After` when\n * present, else exponential backoff) and re-issue the request. openapi-fetch\n * clones the request per attempt, so re-fetching here is safe.\n */\nfunction retryMiddleware(maxRetries: number, doFetch: typeof fetch): Middleware {\n return {\n async onResponse({ request, response }) {\n if (maxRetries <= 0 || !RETRYABLE_STATUSES.has(response.status)) {\n return response;\n }\n let current = response;\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n if (!RETRYABLE_STATUSES.has(current.status)) break;\n const retryAfterMs = parseRetryAfterMs(current.headers.get(\"retry-after\"));\n const backoff = Math.min(BASE_BACKOFF_MS * 2 ** (attempt - 1), MAX_BACKOFF_MS);\n await sleep(retryAfterMs ?? backoff);\n current = await doFetch(request.clone());\n }\n return current;\n },\n };\n}\n\n/**\n * A typed client for the Krova Cloud API.\n *\n * @example\n * ```ts\n * const krova = new KrovaClient({ apiKey: \"kro_...\" });\n * const cubes = await krova.cubes.list(\"space_123\");\n * ```\n */\nexport class KrovaClient {\n /**\n * The underlying openapi-fetch client — a fully typed escape hatch to every\n * path in the spec. Returns `{ data, error, response }` and never throws.\n *\n * @example\n * ```ts\n * const { data, error } = await krova.raw.GET(\n * \"/spaces/{spaceId}/cubes/{cubeId}\",\n * { params: { path: { spaceId, cubeId } } },\n * );\n * ```\n */\n readonly raw: Client<paths>;\n\n /** The resolved base URL in use. */\n readonly baseUrl: string;\n\n constructor(options: KrovaClientOptions) {\n if (!options?.apiKey) {\n throw new Error(\"KrovaClient: `apiKey` is required.\");\n }\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n const doFetch = options.fetch ?? globalThis.fetch;\n const maxRetries = options.maxRetries ?? 2;\n\n this.raw = createClient<paths>({\n baseUrl: this.baseUrl,\n // SECURITY: never auto-follow redirects. The Krova Cloud API is a plain\n // JSON API and never legitimately 3xx's a data call. Following a redirect\n // would resend the `X-API-KEY` header to the redirect target — and unlike\n // `Authorization`, `Cookie`, and `Proxy-Authorization`, the Fetch spec does\n // NOT strip a custom header like `X-API-KEY` on a cross-origin redirect\n // (verified against undici/Node fetch). A compromised/misconfigured proxy,\n // an open-redirect on the API, or a MITM could otherwise exfiltrate the key\n // to an attacker's host. With `\"manual\"`, a redirect comes back as a\n // non-ok response and the helpers throw `KrovaError` instead of leaking.\n redirect: \"manual\",\n ...(options.fetch ? { fetch: options.fetch } : {}),\n });\n this.raw.use(authMiddleware(options.apiKey, options.authScheme ?? \"x-api-key\"));\n if (maxRetries > 0) {\n this.raw.use(retryMiddleware(maxRetries, doFetch));\n }\n }\n\n // ---------------------------------------------------------------------------\n // Cubes\n // ---------------------------------------------------------------------------\n\n readonly cubes = {\n /** List Cubes in a Space. Returns the raw (paginated) response body. */\n list: async (spaceId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.GET(\"/spaces/{spaceId}/cubes\", {\n params: { path: { spaceId } },\n });\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /**\n * Create a Cube. Returns the created {@link Cube}.\n *\n * @param spaceId Target Space id.\n * @param body Cube spec — `{ name, image, resources, sshPublicKey, ... }`.\n * @param opts Optional `idempotencyKey` (max 255 chars, scoped per space).\n */\n create: async (\n spaceId: string,\n body: NonNullable<\n paths[\"/spaces/{spaceId}/cubes\"][\"post\"][\"requestBody\"]\n >[\"content\"][\"application/json\"],\n opts?: { idempotencyKey?: string },\n ): Promise<Cube> => {\n const { data, error, response } = await this.raw.POST(\"/spaces/{spaceId}/cubes\", {\n params: {\n path: { spaceId },\n ...(opts?.idempotencyKey\n ? { header: { \"Idempotency-Key\": opts.idempotencyKey } }\n : {}),\n },\n body,\n });\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n const cube = data?.cube;\n if (!cube) {\n throw krovaErrorFrom(response, { error: \"Create Cube response had no `cube`.\" });\n }\n return cube;\n },\n\n /** Get a single Cube. Returns the raw response body. */\n get: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.GET(\n \"/spaces/{spaceId}/cubes/{cubeId}\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /**\n * Update a Cube's SSH port.\n *\n * The Krova Cloud API exposes no general Cube-mutation endpoint; the only\n * mutable Cube field over the API is its SSH port, via\n * `PUT /spaces/{spaceId}/cubes/{cubeId}/ssh-port`. This helper maps to that\n * endpoint. (Compute resize / rename are not part of the public API.)\n */\n update: async (\n spaceId: string,\n cubeId: string,\n body: NonNullable<\n paths[\"/spaces/{spaceId}/cubes/{cubeId}/ssh-port\"][\"put\"][\"requestBody\"]\n >[\"content\"][\"application/json\"],\n ): Promise<unknown> => {\n const { data, error, response } = await this.raw.PUT(\n \"/spaces/{spaceId}/cubes/{cubeId}/ssh-port\",\n { params: { path: { spaceId, cubeId } }, body },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Delete a Cube (asynchronous — deletion is enqueued). */\n delete: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.DELETE(\n \"/spaces/{spaceId}/cubes/{cubeId}\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Sleep a running Cube (asynchronous — sleep is enqueued). */\n sleep: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.POST(\n \"/spaces/{spaceId}/cubes/{cubeId}/sleep\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Wake a sleeping Cube (asynchronous — wake is enqueued). */\n wake: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.POST(\n \"/spaces/{spaceId}/cubes/{cubeId}/wake\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n };\n\n // ---------------------------------------------------------------------------\n // Public catalog (no auth required by the API, but the key is harmless)\n // ---------------------------------------------------------------------------\n\n readonly catalog = {\n /** List regions with available capacity. */\n regions: async (): Promise<unknown> => {\n const { data, error, response } = await this.raw.GET(\"/regions\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** List available OS images. */\n images: async (): Promise<unknown> => {\n const { data, error, response } = await this.raw.GET(\"/images\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Per-resource hourly rates and volume pricing tiers. */\n pricing: async (): Promise<unknown> => {\n const { data, error, response } = await this.raw.GET(\"/pricing\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/error.ts","../src/client.ts"],"names":["createClient"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * The error body shape returned by the Krova Cloud API.\n *\n * Per the OpenAPI spec (`components.schemas.Error`), every non-2xx response\n * body is `{ \"error\": string }`. Additional fields may appear over time, so\n * we keep the type open.\n */\nexport interface KrovaErrorBody {\n error?: string;\n [key: string]: unknown;\n}\n\n/**\n * Error thrown by the ergonomic {@link KrovaClient} helpers when the API\n * responds with a non-2xx status.\n *\n * The raw openapi-fetch client (`client.raw`) never throws — it returns\n * `{ data, error, response }`. The helpers wrap that and throw `KrovaError`\n * so callers can `try/catch`.\n */\nexport class KrovaError extends Error {\n /** HTTP status code of the failing response. */\n readonly status: number;\n\n /**\n * A machine-readable error code, when the API surfaces one via the\n * `X-Error-Code` response header. The documented error body only carries a\n * human-readable `error` string, so this is best-effort.\n */\n readonly code?: string;\n\n /**\n * The request id from the `X-Request-Id` response header, when present.\n * Useful when contacting Krova Cloud support about a specific failure.\n */\n readonly requestId?: string;\n\n /** The parsed JSON error body, when the response had one. */\n readonly body?: KrovaErrorBody;\n\n /** The raw `Response` object, for callers that need headers/url/etc. */\n readonly response?: Response;\n\n constructor(\n message: string,\n init: {\n status: number;\n code?: string;\n requestId?: string;\n body?: KrovaErrorBody;\n response?: Response;\n },\n ) {\n super(message);\n this.name = \"KrovaError\";\n this.status = init.status;\n this.code = init.code;\n this.requestId = init.requestId;\n this.body = init.body;\n this.response = init.response;\n // Restore prototype chain for instanceof across compilation targets.\n Object.setPrototypeOf(this, KrovaError.prototype);\n }\n}\n\n/**\n * Build a {@link KrovaError} from a failing response + parsed error body.\n */\nexport function krovaErrorFrom(\n response: Response,\n body: KrovaErrorBody | undefined,\n): KrovaError {\n const message =\n (typeof body?.error === \"string\" && body.error) ||\n response.statusText ||\n `Request failed with status ${response.status}`;\n return new KrovaError(message, {\n status: response.status,\n code: response.headers.get(\"x-error-code\") ?? undefined,\n requestId: response.headers.get(\"x-request-id\") ?? undefined,\n body,\n response,\n });\n}\n","import createClient, { type Client, type Middleware } from \"openapi-fetch\";\nimport { krovaErrorFrom } from \"./error.js\";\nimport type { components, paths } from \"./generated/types.js\";\n\n/** The Cube resource, as defined in the Krova Cloud OpenAPI spec. */\nexport type Cube = components[\"schemas\"][\"Cube\"];\n\n/** A region with available capacity (from the catalog). */\nexport type Region = components[\"schemas\"][\"Region\"];\n\n/** A selectable OS image (from the catalog). */\nexport type Image = components[\"schemas\"][\"Image\"];\n\n/** A volume-pricing tier (from the catalog). */\nexport type PricingTier = components[\"schemas\"][\"PricingTier\"];\n\n/** Pagination envelope returned alongside a Cube list. */\nexport type Pagination = components[\"schemas\"][\"Pagination\"];\n\n/** Default API base URL — the single `servers[0].url` from the OpenAPI spec. */\nexport const DEFAULT_BASE_URL = \"https://krova.cloud/api/v1\";\n\n/**\n * How the API key is presented to the server.\n *\n * - `\"x-api-key\"` (default) — `X-API-KEY: <key>`, matching the spec's\n * `components.securitySchemes.ApiKeyAuth` (an `apiKey` header named\n * `X-API-KEY`).\n * - `\"bearer\"` — `Authorization: Bearer <key>`, for gateways that expect it.\n */\nexport type AuthScheme = \"x-api-key\" | \"bearer\";\n\nexport interface KrovaClientOptions {\n /**\n * Your Krova Cloud API key (a `kro_...` token). Keys are scoped per Space\n * and inherit the permissions of the membership that created them.\n */\n apiKey: string;\n /** Override the API base URL. Defaults to {@link DEFAULT_BASE_URL}. */\n baseUrl?: string;\n /**\n * Auth header scheme. Defaults to `\"x-api-key\"` (the spec's scheme).\n */\n authScheme?: AuthScheme;\n /**\n * Max automatic retries on retryable statuses (429, 503).\n * Defaults to 2. Set to 0 to disable retries.\n */\n maxRetries?: number;\n /**\n * A custom `fetch` implementation (e.g. for tests or a proxy). Defaults to\n * the global `fetch`.\n */\n fetch?: typeof fetch;\n}\n\n/** Statuses the retry middleware treats as transient. */\nconst RETRYABLE_STATUSES = new Set([429, 503]);\n/** Fallback backoff (ms) when the server sends no `Retry-After` header. */\nconst BASE_BACKOFF_MS = 500;\n/** Cap on any single backoff wait (ms), to keep retries \"small but real\". */\nconst MAX_BACKOFF_MS = 10_000;\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Parse a `Retry-After` header (RFC 7231): either delta-seconds or an\n * HTTP-date. Returns milliseconds to wait, or `null` if absent/unparseable.\n */\nfunction parseRetryAfterMs(headerValue: string | null): number | null {\n if (!headerValue) return null;\n const seconds = Number(headerValue);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n const dateMs = Date.parse(headerValue);\n if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());\n return null;\n}\n\nfunction authMiddleware(apiKey: string, scheme: AuthScheme): Middleware {\n return {\n onRequest({ request }) {\n if (scheme === \"bearer\") {\n request.headers.set(\"Authorization\", `Bearer ${apiKey}`);\n } else {\n request.headers.set(\"X-API-KEY\", apiKey);\n }\n return request;\n },\n };\n}\n\n/**\n * Retry middleware: on a retryable status, wait (honoring `Retry-After` when\n * present, else exponential backoff) and re-issue the request. openapi-fetch\n * clones the request per attempt, so re-fetching here is safe.\n */\nfunction retryMiddleware(maxRetries: number, doFetch: typeof fetch): Middleware {\n return {\n async onResponse({ request, response }) {\n if (maxRetries <= 0 || !RETRYABLE_STATUSES.has(response.status)) {\n return response;\n }\n let current = response;\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n if (!RETRYABLE_STATUSES.has(current.status)) break;\n const retryAfterMs = parseRetryAfterMs(current.headers.get(\"retry-after\"));\n const backoff = Math.min(BASE_BACKOFF_MS * 2 ** (attempt - 1), MAX_BACKOFF_MS);\n await sleep(retryAfterMs ?? backoff);\n current = await doFetch(request.clone());\n }\n return current;\n },\n };\n}\n\n/**\n * A typed client for the Krova Cloud API.\n *\n * @example\n * ```ts\n * const krova = new KrovaClient({ apiKey: \"kro_...\" });\n * const cubes = await krova.cubes.list(\"space_123\");\n * ```\n */\nexport class KrovaClient {\n /**\n * The underlying openapi-fetch client — a fully typed escape hatch to every\n * path in the spec. Returns `{ data, error, response }` and never throws.\n *\n * @example\n * ```ts\n * const { data, error } = await krova.raw.GET(\n * \"/spaces/{spaceId}/cubes/{cubeId}\",\n * { params: { path: { spaceId, cubeId } } },\n * );\n * ```\n */\n readonly raw: Client<paths>;\n\n /** The resolved base URL in use. */\n readonly baseUrl: string;\n\n constructor(options: KrovaClientOptions) {\n if (!options?.apiKey) {\n throw new Error(\"KrovaClient: `apiKey` is required.\");\n }\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n const doFetch = options.fetch ?? globalThis.fetch;\n const maxRetries = options.maxRetries ?? 2;\n\n this.raw = createClient<paths>({\n baseUrl: this.baseUrl,\n // SECURITY: never auto-follow redirects. The Krova Cloud API is a plain\n // JSON API and never legitimately 3xx's a data call. Following a redirect\n // would resend the `X-API-KEY` header to the redirect target — and unlike\n // `Authorization`, `Cookie`, and `Proxy-Authorization`, the Fetch spec does\n // NOT strip a custom header like `X-API-KEY` on a cross-origin redirect\n // (verified against undici/Node fetch). A compromised/misconfigured proxy,\n // an open-redirect on the API, or a MITM could otherwise exfiltrate the key\n // to an attacker's host. With `\"manual\"`, a redirect comes back as a\n // non-ok response and the helpers throw `KrovaError` instead of leaking.\n redirect: \"manual\",\n ...(options.fetch ? { fetch: options.fetch } : {}),\n });\n this.raw.use(authMiddleware(options.apiKey, options.authScheme ?? \"x-api-key\"));\n if (maxRetries > 0) {\n this.raw.use(retryMiddleware(maxRetries, doFetch));\n }\n }\n\n // ---------------------------------------------------------------------------\n // Cubes\n // ---------------------------------------------------------------------------\n\n readonly cubes = {\n /** List Cubes in a Space, with pagination metadata. */\n list: async (spaceId: string) => {\n const { data, error, response } = await this.raw.GET(\"/spaces/{spaceId}/cubes\", {\n params: { path: { spaceId } },\n });\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n if (data === undefined)\n throw krovaErrorFrom(response, { error: \"List Cubes response was empty.\" });\n return data;\n },\n\n /**\n * Create a Cube. Returns the created {@link Cube}.\n *\n * @param spaceId Target Space id.\n * @param body Cube spec — `{ name, image, resources, sshPublicKey, ... }`.\n * @param opts Optional `idempotencyKey` (max 255 chars, scoped per space).\n */\n create: async (\n spaceId: string,\n body: NonNullable<\n paths[\"/spaces/{spaceId}/cubes\"][\"post\"][\"requestBody\"]\n >[\"content\"][\"application/json\"],\n opts?: { idempotencyKey?: string },\n ): Promise<Cube> => {\n const { data, error, response } = await this.raw.POST(\"/spaces/{spaceId}/cubes\", {\n params: {\n path: { spaceId },\n ...(opts?.idempotencyKey\n ? { header: { \"Idempotency-Key\": opts.idempotencyKey } }\n : {}),\n },\n body,\n });\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n const cube = data?.cube;\n if (!cube) {\n throw krovaErrorFrom(response, { error: \"Create Cube response had no `cube`.\" });\n }\n return cube;\n },\n\n /** Get a single Cube. Returns the {@link Cube}. */\n get: async (spaceId: string, cubeId: string): Promise<Cube> => {\n const { data, error, response } = await this.raw.GET(\n \"/spaces/{spaceId}/cubes/{cubeId}\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n const cube = data?.cube;\n if (!cube) {\n throw krovaErrorFrom(response, { error: \"Get Cube response had no `cube`.\" });\n }\n return cube;\n },\n\n /**\n * Update a Cube's SSH port.\n *\n * The Krova Cloud API exposes no general Cube-mutation endpoint; the only\n * mutable Cube field over the API is its SSH port, via\n * `PUT /spaces/{spaceId}/cubes/{cubeId}/ssh-port`. This helper maps to that\n * endpoint. (Compute resize / rename are not part of the public API.)\n */\n update: async (\n spaceId: string,\n cubeId: string,\n body: NonNullable<\n paths[\"/spaces/{spaceId}/cubes/{cubeId}/ssh-port\"][\"put\"][\"requestBody\"]\n >[\"content\"][\"application/json\"],\n ): Promise<unknown> => {\n const { data, error, response } = await this.raw.PUT(\n \"/spaces/{spaceId}/cubes/{cubeId}/ssh-port\",\n { params: { path: { spaceId, cubeId } }, body },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Delete a Cube (asynchronous — deletion is enqueued). */\n delete: async (spaceId: string, cubeId: string) => {\n const { data, error, response } = await this.raw.DELETE(\n \"/spaces/{spaceId}/cubes/{cubeId}\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n if (data === undefined)\n throw krovaErrorFrom(response, { error: \"Delete Cube response was empty.\" });\n return data;\n },\n\n /** Sleep a running Cube (asynchronous — sleep is enqueued). */\n sleep: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.POST(\n \"/spaces/{spaceId}/cubes/{cubeId}/sleep\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Wake a sleeping Cube (asynchronous — wake is enqueued). */\n wake: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.POST(\n \"/spaces/{spaceId}/cubes/{cubeId}/wake\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n };\n\n // ---------------------------------------------------------------------------\n // Public catalog (no auth required by the API, but the key is harmless)\n // ---------------------------------------------------------------------------\n\n readonly catalog = {\n /** List regions with available capacity. */\n regions: async () => {\n const { data, error, response } = await this.raw.GET(\"/regions\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n if (data === undefined)\n throw krovaErrorFrom(response, { error: \"Regions response was empty.\" });\n return data;\n },\n\n /** List available OS images. */\n images: async () => {\n const { data, error, response } = await this.raw.GET(\"/images\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n if (data === undefined)\n throw krovaErrorFrom(response, { error: \"Images response was empty.\" });\n return data;\n },\n\n /** Per-resource hourly rates and volume pricing tiers. */\n pricing: async () => {\n const { data, error, response } = await this.raw.GET(\"/pricing\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n if (data === undefined)\n throw krovaErrorFrom(response, { error: \"Pricing response was empty.\" });\n return data;\n },\n };\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -28,7 +28,9 @@ interface paths {
28
28
  [name: string]: unknown;
29
29
  };
30
30
  content: {
31
- "application/json": Record<string, never>;
31
+ "application/json": {
32
+ regions: components["schemas"]["Region"][];
33
+ };
32
34
  };
33
35
  };
34
36
  };
@@ -64,7 +66,9 @@ interface paths {
64
66
  [name: string]: unknown;
65
67
  };
66
68
  content: {
67
- "application/json": Record<string, never>;
69
+ "application/json": {
70
+ images: components["schemas"]["Image"][];
71
+ };
68
72
  };
69
73
  };
70
74
  };
@@ -100,7 +104,16 @@ interface paths {
100
104
  [name: string]: unknown;
101
105
  };
102
106
  content: {
103
- "application/json": Record<string, never>;
107
+ "application/json": {
108
+ currency: string;
109
+ rates: {
110
+ vcpuPerHour: number;
111
+ ramGbPerHour: number;
112
+ diskGbPerHour: number;
113
+ };
114
+ tiers: components["schemas"]["PricingTier"][];
115
+ note: string;
116
+ };
104
117
  };
105
118
  };
106
119
  };
@@ -138,7 +151,10 @@ interface paths {
138
151
  [name: string]: unknown;
139
152
  };
140
153
  content: {
141
- "application/json": Record<string, never>;
154
+ "application/json": {
155
+ cubes: components["schemas"]["Cube"][];
156
+ pagination: components["schemas"]["Pagination"];
157
+ };
142
158
  };
143
159
  };
144
160
  401: components["responses"]["Unauthorized"];
@@ -228,7 +244,9 @@ interface paths {
228
244
  [name: string]: unknown;
229
245
  };
230
246
  content: {
231
- "application/json": Record<string, never>;
247
+ "application/json": {
248
+ cube: components["schemas"]["Cube"];
249
+ };
232
250
  };
233
251
  };
234
252
  404: components["responses"]["NotFound"];
@@ -255,7 +273,9 @@ interface paths {
255
273
  [name: string]: unknown;
256
274
  };
257
275
  content: {
258
- "application/json": Record<string, never>;
276
+ "application/json": {
277
+ success: boolean;
278
+ };
259
279
  };
260
280
  };
261
281
  404: components["responses"]["NotFound"];
@@ -1375,6 +1395,29 @@ interface components {
1375
1395
  /** Format: date-time */
1376
1396
  updatedAt: string;
1377
1397
  };
1398
+ Pagination: {
1399
+ page: number;
1400
+ limit: number;
1401
+ total: number;
1402
+ totalPages: number;
1403
+ };
1404
+ Region: {
1405
+ id: string;
1406
+ name: string;
1407
+ slug: string;
1408
+ };
1409
+ Image: {
1410
+ id: string;
1411
+ name: string;
1412
+ version: string;
1413
+ description: string;
1414
+ };
1415
+ PricingTier: {
1416
+ minVcpus: number;
1417
+ maxVcpus: number | null;
1418
+ multiplier: number;
1419
+ label: string;
1420
+ };
1378
1421
  Domain: {
1379
1422
  id: string;
1380
1423
  cubeId: string;
@@ -1520,6 +1563,14 @@ interface components {
1520
1563
 
1521
1564
  /** The Cube resource, as defined in the Krova Cloud OpenAPI spec. */
1522
1565
  type Cube = components["schemas"]["Cube"];
1566
+ /** A region with available capacity (from the catalog). */
1567
+ type Region = components["schemas"]["Region"];
1568
+ /** A selectable OS image (from the catalog). */
1569
+ type Image = components["schemas"]["Image"];
1570
+ /** A volume-pricing tier (from the catalog). */
1571
+ type PricingTier = components["schemas"]["PricingTier"];
1572
+ /** Pagination envelope returned alongside a Cube list. */
1573
+ type Pagination = components["schemas"]["Pagination"];
1523
1574
  /** Default API base URL — the single `servers[0].url` from the OpenAPI spec. */
1524
1575
  declare const DEFAULT_BASE_URL = "https://krova.cloud/api/v1";
1525
1576
  /**
@@ -1581,8 +1632,30 @@ declare class KrovaClient {
1581
1632
  readonly baseUrl: string;
1582
1633
  constructor(options: KrovaClientOptions);
1583
1634
  readonly cubes: {
1584
- /** List Cubes in a Space. Returns the raw (paginated) response body. */
1585
- list: (spaceId: string) => Promise<unknown>;
1635
+ /** List Cubes in a Space, with pagination metadata. */
1636
+ list: (spaceId: string) => Promise<{
1637
+ cubes: {
1638
+ id: string;
1639
+ name: string;
1640
+ state: "pending" | "booting" | "running" | "sleeping" | "stopping" | "error" | "deleted";
1641
+ publicIpv4: string | null;
1642
+ resources: {
1643
+ vcpu: number;
1644
+ ramGb: number;
1645
+ diskGb: number;
1646
+ };
1647
+ image: string;
1648
+ costPerHour: number;
1649
+ createdAt: string;
1650
+ updatedAt: string;
1651
+ }[];
1652
+ pagination: {
1653
+ page: number;
1654
+ limit: number;
1655
+ total: number;
1656
+ totalPages: number;
1657
+ };
1658
+ }>;
1586
1659
  /**
1587
1660
  * Create a Cube. Returns the created {@link Cube}.
1588
1661
  *
@@ -1593,8 +1666,8 @@ declare class KrovaClient {
1593
1666
  create: (spaceId: string, body: NonNullable<paths["/spaces/{spaceId}/cubes"]["post"]["requestBody"]>["content"]["application/json"], opts?: {
1594
1667
  idempotencyKey?: string;
1595
1668
  }) => Promise<Cube>;
1596
- /** Get a single Cube. Returns the raw response body. */
1597
- get: (spaceId: string, cubeId: string) => Promise<unknown>;
1669
+ /** Get a single Cube. Returns the {@link Cube}. */
1670
+ get: (spaceId: string, cubeId: string) => Promise<Cube>;
1598
1671
  /**
1599
1672
  * Update a Cube's SSH port.
1600
1673
  *
@@ -1605,7 +1678,9 @@ declare class KrovaClient {
1605
1678
  */
1606
1679
  update: (spaceId: string, cubeId: string, body: NonNullable<paths["/spaces/{spaceId}/cubes/{cubeId}/ssh-port"]["put"]["requestBody"]>["content"]["application/json"]) => Promise<unknown>;
1607
1680
  /** Delete a Cube (asynchronous — deletion is enqueued). */
1608
- delete: (spaceId: string, cubeId: string) => Promise<unknown>;
1681
+ delete: (spaceId: string, cubeId: string) => Promise<{
1682
+ success: boolean;
1683
+ }>;
1609
1684
  /** Sleep a running Cube (asynchronous — sleep is enqueued). */
1610
1685
  sleep: (spaceId: string, cubeId: string) => Promise<unknown>;
1611
1686
  /** Wake a sleeping Cube (asynchronous — wake is enqueued). */
@@ -1613,11 +1688,38 @@ declare class KrovaClient {
1613
1688
  };
1614
1689
  readonly catalog: {
1615
1690
  /** List regions with available capacity. */
1616
- regions: () => Promise<unknown>;
1691
+ regions: () => Promise<{
1692
+ regions: {
1693
+ id: string;
1694
+ name: string;
1695
+ slug: string;
1696
+ }[];
1697
+ }>;
1617
1698
  /** List available OS images. */
1618
- images: () => Promise<unknown>;
1699
+ images: () => Promise<{
1700
+ images: {
1701
+ id: string;
1702
+ name: string;
1703
+ version: string;
1704
+ description: string;
1705
+ }[];
1706
+ }>;
1619
1707
  /** Per-resource hourly rates and volume pricing tiers. */
1620
- pricing: () => Promise<unknown>;
1708
+ pricing: () => Promise<{
1709
+ currency: string;
1710
+ rates: {
1711
+ vcpuPerHour: number;
1712
+ ramGbPerHour: number;
1713
+ diskGbPerHour: number;
1714
+ };
1715
+ tiers: {
1716
+ minVcpus: number;
1717
+ maxVcpus: number | null;
1718
+ multiplier: number;
1719
+ label: string;
1720
+ }[];
1721
+ note: string;
1722
+ }>;
1621
1723
  };
1622
1724
  }
1623
1725
 
@@ -1671,4 +1773,4 @@ declare class KrovaError extends Error {
1671
1773
  */
1672
1774
  declare function krovaErrorFrom(response: Response, body: KrovaErrorBody | undefined): KrovaError;
1673
1775
 
1674
- export { type AuthScheme, type Cube, DEFAULT_BASE_URL, KrovaClient, type KrovaClientOptions, KrovaError, type KrovaErrorBody, type components, krovaErrorFrom, type paths };
1776
+ export { type AuthScheme, type Cube, DEFAULT_BASE_URL, type Image, KrovaClient, type KrovaClientOptions, KrovaError, type KrovaErrorBody, type Pagination, type PricingTier, type Region, type components, krovaErrorFrom, type paths };
package/dist/index.d.ts CHANGED
@@ -28,7 +28,9 @@ interface paths {
28
28
  [name: string]: unknown;
29
29
  };
30
30
  content: {
31
- "application/json": Record<string, never>;
31
+ "application/json": {
32
+ regions: components["schemas"]["Region"][];
33
+ };
32
34
  };
33
35
  };
34
36
  };
@@ -64,7 +66,9 @@ interface paths {
64
66
  [name: string]: unknown;
65
67
  };
66
68
  content: {
67
- "application/json": Record<string, never>;
69
+ "application/json": {
70
+ images: components["schemas"]["Image"][];
71
+ };
68
72
  };
69
73
  };
70
74
  };
@@ -100,7 +104,16 @@ interface paths {
100
104
  [name: string]: unknown;
101
105
  };
102
106
  content: {
103
- "application/json": Record<string, never>;
107
+ "application/json": {
108
+ currency: string;
109
+ rates: {
110
+ vcpuPerHour: number;
111
+ ramGbPerHour: number;
112
+ diskGbPerHour: number;
113
+ };
114
+ tiers: components["schemas"]["PricingTier"][];
115
+ note: string;
116
+ };
104
117
  };
105
118
  };
106
119
  };
@@ -138,7 +151,10 @@ interface paths {
138
151
  [name: string]: unknown;
139
152
  };
140
153
  content: {
141
- "application/json": Record<string, never>;
154
+ "application/json": {
155
+ cubes: components["schemas"]["Cube"][];
156
+ pagination: components["schemas"]["Pagination"];
157
+ };
142
158
  };
143
159
  };
144
160
  401: components["responses"]["Unauthorized"];
@@ -228,7 +244,9 @@ interface paths {
228
244
  [name: string]: unknown;
229
245
  };
230
246
  content: {
231
- "application/json": Record<string, never>;
247
+ "application/json": {
248
+ cube: components["schemas"]["Cube"];
249
+ };
232
250
  };
233
251
  };
234
252
  404: components["responses"]["NotFound"];
@@ -255,7 +273,9 @@ interface paths {
255
273
  [name: string]: unknown;
256
274
  };
257
275
  content: {
258
- "application/json": Record<string, never>;
276
+ "application/json": {
277
+ success: boolean;
278
+ };
259
279
  };
260
280
  };
261
281
  404: components["responses"]["NotFound"];
@@ -1375,6 +1395,29 @@ interface components {
1375
1395
  /** Format: date-time */
1376
1396
  updatedAt: string;
1377
1397
  };
1398
+ Pagination: {
1399
+ page: number;
1400
+ limit: number;
1401
+ total: number;
1402
+ totalPages: number;
1403
+ };
1404
+ Region: {
1405
+ id: string;
1406
+ name: string;
1407
+ slug: string;
1408
+ };
1409
+ Image: {
1410
+ id: string;
1411
+ name: string;
1412
+ version: string;
1413
+ description: string;
1414
+ };
1415
+ PricingTier: {
1416
+ minVcpus: number;
1417
+ maxVcpus: number | null;
1418
+ multiplier: number;
1419
+ label: string;
1420
+ };
1378
1421
  Domain: {
1379
1422
  id: string;
1380
1423
  cubeId: string;
@@ -1520,6 +1563,14 @@ interface components {
1520
1563
 
1521
1564
  /** The Cube resource, as defined in the Krova Cloud OpenAPI spec. */
1522
1565
  type Cube = components["schemas"]["Cube"];
1566
+ /** A region with available capacity (from the catalog). */
1567
+ type Region = components["schemas"]["Region"];
1568
+ /** A selectable OS image (from the catalog). */
1569
+ type Image = components["schemas"]["Image"];
1570
+ /** A volume-pricing tier (from the catalog). */
1571
+ type PricingTier = components["schemas"]["PricingTier"];
1572
+ /** Pagination envelope returned alongside a Cube list. */
1573
+ type Pagination = components["schemas"]["Pagination"];
1523
1574
  /** Default API base URL — the single `servers[0].url` from the OpenAPI spec. */
1524
1575
  declare const DEFAULT_BASE_URL = "https://krova.cloud/api/v1";
1525
1576
  /**
@@ -1581,8 +1632,30 @@ declare class KrovaClient {
1581
1632
  readonly baseUrl: string;
1582
1633
  constructor(options: KrovaClientOptions);
1583
1634
  readonly cubes: {
1584
- /** List Cubes in a Space. Returns the raw (paginated) response body. */
1585
- list: (spaceId: string) => Promise<unknown>;
1635
+ /** List Cubes in a Space, with pagination metadata. */
1636
+ list: (spaceId: string) => Promise<{
1637
+ cubes: {
1638
+ id: string;
1639
+ name: string;
1640
+ state: "pending" | "booting" | "running" | "sleeping" | "stopping" | "error" | "deleted";
1641
+ publicIpv4: string | null;
1642
+ resources: {
1643
+ vcpu: number;
1644
+ ramGb: number;
1645
+ diskGb: number;
1646
+ };
1647
+ image: string;
1648
+ costPerHour: number;
1649
+ createdAt: string;
1650
+ updatedAt: string;
1651
+ }[];
1652
+ pagination: {
1653
+ page: number;
1654
+ limit: number;
1655
+ total: number;
1656
+ totalPages: number;
1657
+ };
1658
+ }>;
1586
1659
  /**
1587
1660
  * Create a Cube. Returns the created {@link Cube}.
1588
1661
  *
@@ -1593,8 +1666,8 @@ declare class KrovaClient {
1593
1666
  create: (spaceId: string, body: NonNullable<paths["/spaces/{spaceId}/cubes"]["post"]["requestBody"]>["content"]["application/json"], opts?: {
1594
1667
  idempotencyKey?: string;
1595
1668
  }) => Promise<Cube>;
1596
- /** Get a single Cube. Returns the raw response body. */
1597
- get: (spaceId: string, cubeId: string) => Promise<unknown>;
1669
+ /** Get a single Cube. Returns the {@link Cube}. */
1670
+ get: (spaceId: string, cubeId: string) => Promise<Cube>;
1598
1671
  /**
1599
1672
  * Update a Cube's SSH port.
1600
1673
  *
@@ -1605,7 +1678,9 @@ declare class KrovaClient {
1605
1678
  */
1606
1679
  update: (spaceId: string, cubeId: string, body: NonNullable<paths["/spaces/{spaceId}/cubes/{cubeId}/ssh-port"]["put"]["requestBody"]>["content"]["application/json"]) => Promise<unknown>;
1607
1680
  /** Delete a Cube (asynchronous — deletion is enqueued). */
1608
- delete: (spaceId: string, cubeId: string) => Promise<unknown>;
1681
+ delete: (spaceId: string, cubeId: string) => Promise<{
1682
+ success: boolean;
1683
+ }>;
1609
1684
  /** Sleep a running Cube (asynchronous — sleep is enqueued). */
1610
1685
  sleep: (spaceId: string, cubeId: string) => Promise<unknown>;
1611
1686
  /** Wake a sleeping Cube (asynchronous — wake is enqueued). */
@@ -1613,11 +1688,38 @@ declare class KrovaClient {
1613
1688
  };
1614
1689
  readonly catalog: {
1615
1690
  /** List regions with available capacity. */
1616
- regions: () => Promise<unknown>;
1691
+ regions: () => Promise<{
1692
+ regions: {
1693
+ id: string;
1694
+ name: string;
1695
+ slug: string;
1696
+ }[];
1697
+ }>;
1617
1698
  /** List available OS images. */
1618
- images: () => Promise<unknown>;
1699
+ images: () => Promise<{
1700
+ images: {
1701
+ id: string;
1702
+ name: string;
1703
+ version: string;
1704
+ description: string;
1705
+ }[];
1706
+ }>;
1619
1707
  /** Per-resource hourly rates and volume pricing tiers. */
1620
- pricing: () => Promise<unknown>;
1708
+ pricing: () => Promise<{
1709
+ currency: string;
1710
+ rates: {
1711
+ vcpuPerHour: number;
1712
+ ramGbPerHour: number;
1713
+ diskGbPerHour: number;
1714
+ };
1715
+ tiers: {
1716
+ minVcpus: number;
1717
+ maxVcpus: number | null;
1718
+ multiplier: number;
1719
+ label: string;
1720
+ }[];
1721
+ note: string;
1722
+ }>;
1621
1723
  };
1622
1724
  }
1623
1725
 
@@ -1671,4 +1773,4 @@ declare class KrovaError extends Error {
1671
1773
  */
1672
1774
  declare function krovaErrorFrom(response: Response, body: KrovaErrorBody | undefined): KrovaError;
1673
1775
 
1674
- export { type AuthScheme, type Cube, DEFAULT_BASE_URL, KrovaClient, type KrovaClientOptions, KrovaError, type KrovaErrorBody, type components, krovaErrorFrom, type paths };
1776
+ export { type AuthScheme, type Cube, DEFAULT_BASE_URL, type Image, KrovaClient, type KrovaClientOptions, KrovaError, type KrovaErrorBody, type Pagination, type PricingTier, type Region, type components, krovaErrorFrom, type paths };
package/dist/index.js CHANGED
@@ -133,12 +133,14 @@ var KrovaClient = class {
133
133
  // Cubes
134
134
  // ---------------------------------------------------------------------------
135
135
  cubes = {
136
- /** List Cubes in a Space. Returns the raw (paginated) response body. */
136
+ /** List Cubes in a Space, with pagination metadata. */
137
137
  list: async (spaceId) => {
138
138
  const { data, error, response } = await this.raw.GET("/spaces/{spaceId}/cubes", {
139
139
  params: { path: { spaceId } }
140
140
  });
141
141
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
142
+ if (data === void 0)
143
+ throw krovaErrorFrom(response, { error: "List Cubes response was empty." });
142
144
  return data;
143
145
  },
144
146
  /**
@@ -163,14 +165,18 @@ var KrovaClient = class {
163
165
  }
164
166
  return cube;
165
167
  },
166
- /** Get a single Cube. Returns the raw response body. */
168
+ /** Get a single Cube. Returns the {@link Cube}. */
167
169
  get: async (spaceId, cubeId) => {
168
170
  const { data, error, response } = await this.raw.GET(
169
171
  "/spaces/{spaceId}/cubes/{cubeId}",
170
172
  { params: { path: { spaceId, cubeId } } }
171
173
  );
172
174
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
173
- return data;
175
+ const cube = data?.cube;
176
+ if (!cube) {
177
+ throw krovaErrorFrom(response, { error: "Get Cube response had no `cube`." });
178
+ }
179
+ return cube;
174
180
  },
175
181
  /**
176
182
  * Update a Cube's SSH port.
@@ -195,6 +201,8 @@ var KrovaClient = class {
195
201
  { params: { path: { spaceId, cubeId } } }
196
202
  );
197
203
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
204
+ if (data === void 0)
205
+ throw krovaErrorFrom(response, { error: "Delete Cube response was empty." });
198
206
  return data;
199
207
  },
200
208
  /** Sleep a running Cube (asynchronous — sleep is enqueued). */
@@ -224,18 +232,24 @@ var KrovaClient = class {
224
232
  regions: async () => {
225
233
  const { data, error, response } = await this.raw.GET("/regions");
226
234
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
235
+ if (data === void 0)
236
+ throw krovaErrorFrom(response, { error: "Regions response was empty." });
227
237
  return data;
228
238
  },
229
239
  /** List available OS images. */
230
240
  images: async () => {
231
241
  const { data, error, response } = await this.raw.GET("/images");
232
242
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
243
+ if (data === void 0)
244
+ throw krovaErrorFrom(response, { error: "Images response was empty." });
233
245
  return data;
234
246
  },
235
247
  /** Per-resource hourly rates and volume pricing tiers. */
236
248
  pricing: async () => {
237
249
  const { data, error, response } = await this.raw.GET("/pricing");
238
250
  if (error !== void 0 || !response.ok) throw krovaErrorFrom(response, error);
251
+ if (data === void 0)
252
+ throw krovaErrorFrom(response, { error: "Pricing response was empty." });
239
253
  return data;
240
254
  }
241
255
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/error.ts","../src/client.ts"],"names":[],"mappings":";;;;;AAoBO,IAAM,UAAA,GAAN,MAAM,WAAA,SAAmB,KAAA,CAAM;AAAA;AAAA,EAE3B,MAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,IAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,SAAA;AAAA;AAAA,EAGA,IAAA;AAAA;AAAA,EAGA,QAAA;AAAA,EAET,WAAA,CACE,SACA,IAAA,EAOA;AACA,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,YAAA;AACZ,IAAA,IAAA,CAAK,SAAS,IAAA,CAAK,MAAA;AACnB,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,YAAY,IAAA,CAAK,SAAA;AACtB,IAAA,IAAA,CAAK,OAAO,IAAA,CAAK,IAAA;AACjB,IAAA,IAAA,CAAK,WAAW,IAAA,CAAK,QAAA;AAErB,IAAA,MAAA,CAAO,cAAA,CAAe,IAAA,EAAM,WAAA,CAAW,SAAS,CAAA;AAAA,EAClD;AACF;AAKO,SAAS,cAAA,CACd,UACA,IAAA,EACY;AACZ,EAAA,MAAM,OAAA,GACH,OAAO,IAAA,EAAM,KAAA,KAAU,QAAA,IAAY,IAAA,CAAK,KAAA,IACzC,QAAA,CAAS,UAAA,IACT,CAAA,2BAAA,EAA8B,QAAA,CAAS,MAAM,CAAA,CAAA;AAC/C,EAAA,OAAO,IAAI,WAAW,OAAA,EAAS;AAAA,IAC7B,QAAQ,QAAA,CAAS,MAAA;AAAA,IACjB,IAAA,EAAM,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,MAAA;AAAA,IAC9C,SAAA,EAAW,QAAA,CAAS,OAAA,CAAQ,GAAA,CAAI,cAAc,CAAA,IAAK,MAAA;AAAA,IACnD,IAAA;AAAA,IACA;AAAA,GACD,CAAA;AACH;;;AC3EO,IAAM,gBAAA,GAAmB;AAqChC,IAAM,qCAAqB,IAAI,GAAA,CAAI,CAAC,GAAA,EAAK,GAAG,CAAC,CAAA;AAE7C,IAAM,eAAA,GAAkB,GAAA;AAExB,IAAM,cAAA,GAAiB,GAAA;AAEvB,IAAM,KAAA,GAAQ,CAAC,EAAA,KACb,IAAI,OAAA,CAAQ,CAAC,OAAA,KAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AAMlD,SAAS,kBAAkB,WAAA,EAA2C;AACpE,EAAA,IAAI,CAAC,aAAa,OAAO,IAAA;AACzB,EAAA,MAAM,OAAA,GAAU,OAAO,WAAW,CAAA;AAClC,EAAA,IAAI,MAAA,CAAO,SAAS,OAAO,CAAA,SAAU,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAA,GAAU,GAAI,CAAA;AAC/D,EAAA,MAAM,MAAA,GAAS,IAAA,CAAK,KAAA,CAAM,WAAW,CAAA;AACrC,EAAA,IAAI,MAAA,CAAO,QAAA,CAAS,MAAM,CAAA,EAAG,OAAO,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,MAAA,GAAS,IAAA,CAAK,GAAA,EAAK,CAAA;AACnE,EAAA,OAAO,IAAA;AACT;AAEA,SAAS,cAAA,CAAe,QAAgB,MAAA,EAAgC;AACtE,EAAA,OAAO;AAAA,IACL,SAAA,CAAU,EAAE,OAAA,EAAQ,EAAG;AACrB,MAAA,IAAI,WAAW,QAAA,EAAU;AACvB,QAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,eAAA,EAAiB,CAAA,OAAA,EAAU,MAAM,CAAA,CAAE,CAAA;AAAA,MACzD,CAAA,MAAO;AACL,QAAA,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,WAAA,EAAa,MAAM,CAAA;AAAA,MACzC;AACA,MAAA,OAAO,OAAA;AAAA,IACT;AAAA,GACF;AACF;AAOA,SAAS,eAAA,CAAgB,YAAoB,OAAA,EAAmC;AAC9E,EAAA,OAAO;AAAA,IACL,MAAM,UAAA,CAAW,EAAE,OAAA,EAAS,UAAS,EAAG;AACtC,MAAA,IAAI,cAAc,CAAA,IAAK,CAAC,mBAAmB,GAAA,CAAI,QAAA,CAAS,MAAM,CAAA,EAAG;AAC/D,QAAA,OAAO,QAAA;AAAA,MACT;AACA,MAAA,IAAI,OAAA,GAAU,QAAA;AACd,MAAA,KAAA,IAAS,OAAA,GAAU,CAAA,EAAG,OAAA,IAAW,UAAA,EAAY,OAAA,EAAA,EAAW;AACtD,QAAA,IAAI,CAAC,kBAAA,CAAmB,GAAA,CAAI,OAAA,CAAQ,MAAM,CAAA,EAAG;AAC7C,QAAA,MAAM,eAAe,iBAAA,CAAkB,OAAA,CAAQ,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAC,CAAA;AACzE,QAAA,MAAM,UAAU,IAAA,CAAK,GAAA,CAAI,kBAAkB,CAAA,KAAM,OAAA,GAAU,IAAI,cAAc,CAAA;AAC7E,QAAA,MAAM,KAAA,CAAM,gBAAgB,OAAO,CAAA;AACnC,QAAA,OAAA,GAAU,MAAM,OAAA,CAAQ,OAAA,CAAQ,KAAA,EAAO,CAAA;AAAA,MACzC;AACA,MAAA,OAAO,OAAA;AAAA,IACT;AAAA,GACF;AACF;AAWO,IAAM,cAAN,MAAkB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAad,GAAA;AAAA;AAAA,EAGA,OAAA;AAAA,EAET,YAAY,OAAA,EAA6B;AACvC,IAAA,IAAI,CAAC,SAAS,MAAA,EAAQ;AACpB,MAAA,MAAM,IAAI,MAAM,oCAAoC,CAAA;AAAA,IACtD;AACA,IAAA,IAAA,CAAK,OAAA,GAAU,QAAQ,OAAA,IAAW,gBAAA;AAClC,IAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,KAAA,IAAS,UAAA,CAAW,KAAA;AAC5C,IAAA,MAAM,UAAA,GAAa,QAAQ,UAAA,IAAc,CAAA;AAEzC,IAAA,IAAA,CAAK,MAAM,YAAA,CAAoB;AAAA,MAC7B,SAAS,IAAA,CAAK,OAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAUd,QAAA,EAAU,QAAA;AAAA,MACV,GAAI,QAAQ,KAAA,GAAQ,EAAE,OAAO,OAAA,CAAQ,KAAA,KAAU;AAAC,KACjD,CAAA;AACD,IAAA,IAAA,CAAK,GAAA,CAAI,IAAI,cAAA,CAAe,OAAA,CAAQ,QAAQ,OAAA,CAAQ,UAAA,IAAc,WAAW,CAAC,CAAA;AAC9E,IAAA,IAAI,aAAa,CAAA,EAAG;AAClB,MAAA,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,eAAA,CAAgB,UAAA,EAAY,OAAO,CAAC,CAAA;AAAA,IACnD;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMS,KAAA,GAAQ;AAAA;AAAA,IAEf,IAAA,EAAM,OAAO,OAAA,KAAsC;AACjD,MAAA,MAAM,EAAE,MAAM,KAAA,EAAO,QAAA,KAAa,MAAM,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,yBAAA,EAA2B;AAAA,QAC9E,MAAA,EAAQ,EAAE,IAAA,EAAM,EAAE,SAAQ;AAAE,OAC7B,CAAA;AACD,MAAA,IAAI,KAAA,KAAU,UAAa,CAAC,QAAA,CAAS,IAAI,MAAM,cAAA,CAAe,UAAU,KAAK,CAAA;AAC7E,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,MAAA,EAAQ,OACN,OAAA,EACA,IAAA,EAGA,IAAA,KACkB;AAClB,MAAA,MAAM,EAAE,MAAM,KAAA,EAAO,QAAA,KAAa,MAAM,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,yBAAA,EAA2B;AAAA,QAC/E,MAAA,EAAQ;AAAA,UACN,IAAA,EAAM,EAAE,OAAA,EAAQ;AAAA,UAChB,GAAI,IAAA,EAAM,cAAA,GACN,EAAE,MAAA,EAAQ,EAAE,iBAAA,EAAmB,IAAA,CAAK,cAAA,EAAe,EAAE,GACrD;AAAC,SACP;AAAA,QACA;AAAA,OACD,CAAA;AACD,MAAA,IAAI,KAAA,KAAU,UAAa,CAAC,QAAA,CAAS,IAAI,MAAM,cAAA,CAAe,UAAU,KAAK,CAAA;AAC7E,MAAA,MAAM,OAAO,IAAA,EAAM,IAAA;AACnB,MAAA,IAAI,CAAC,IAAA,EAAM;AACT,QAAA,MAAM,cAAA,CAAe,QAAA,EAAU,EAAE,KAAA,EAAO,uCAAuC,CAAA;AAAA,MACjF;AACA,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA;AAAA,IAGA,GAAA,EAAK,OAAO,OAAA,EAAiB,MAAA,KAAqC;AAChE,MAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,UAAS,GAAI,MAAM,KAAK,GAAA,CAAI,GAAA;AAAA,QAC/C,kCAAA;AAAA,QACA,EAAE,QAAQ,EAAE,IAAA,EAAM,EAAE,OAAA,EAAS,MAAA,IAAS;AAAE,OAC1C;AACA,MAAA,IAAI,KAAA,KAAU,UAAa,CAAC,QAAA,CAAS,IAAI,MAAM,cAAA,CAAe,UAAU,KAAK,CAAA;AAC7E,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,MAAA,EAAQ,OACN,OAAA,EACA,MAAA,EACA,IAAA,KAGqB;AACrB,MAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,UAAS,GAAI,MAAM,KAAK,GAAA,CAAI,GAAA;AAAA,QAC/C,2CAAA;AAAA,QACA,EAAE,QAAQ,EAAE,IAAA,EAAM,EAAE,OAAA,EAAS,MAAA,EAAO,EAAE,EAAG,IAAA;AAAK,OAChD;AACA,MAAA,IAAI,KAAA,KAAU,UAAa,CAAC,QAAA,CAAS,IAAI,MAAM,cAAA,CAAe,UAAU,KAAK,CAAA;AAC7E,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA;AAAA,IAGA,MAAA,EAAQ,OAAO,OAAA,EAAiB,MAAA,KAAqC;AACnE,MAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,UAAS,GAAI,MAAM,KAAK,GAAA,CAAI,MAAA;AAAA,QAC/C,kCAAA;AAAA,QACA,EAAE,QAAQ,EAAE,IAAA,EAAM,EAAE,OAAA,EAAS,MAAA,IAAS;AAAE,OAC1C;AACA,MAAA,IAAI,KAAA,KAAU,UAAa,CAAC,QAAA,CAAS,IAAI,MAAM,cAAA,CAAe,UAAU,KAAK,CAAA;AAC7E,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA;AAAA,IAGA,KAAA,EAAO,OAAO,OAAA,EAAiB,MAAA,KAAqC;AAClE,MAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,UAAS,GAAI,MAAM,KAAK,GAAA,CAAI,IAAA;AAAA,QAC/C,wCAAA;AAAA,QACA,EAAE,QAAQ,EAAE,IAAA,EAAM,EAAE,OAAA,EAAS,MAAA,IAAS;AAAE,OAC1C;AACA,MAAA,IAAI,KAAA,KAAU,UAAa,CAAC,QAAA,CAAS,IAAI,MAAM,cAAA,CAAe,UAAU,KAAK,CAAA;AAC7E,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA;AAAA,IAGA,IAAA,EAAM,OAAO,OAAA,EAAiB,MAAA,KAAqC;AACjE,MAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,UAAS,GAAI,MAAM,KAAK,GAAA,CAAI,IAAA;AAAA,QAC/C,uCAAA;AAAA,QACA,EAAE,QAAQ,EAAE,IAAA,EAAM,EAAE,OAAA,EAAS,MAAA,IAAS;AAAE,OAC1C;AACA,MAAA,IAAI,KAAA,KAAU,UAAa,CAAC,QAAA,CAAS,IAAI,MAAM,cAAA,CAAe,UAAU,KAAK,CAAA;AAC7E,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,GACF;AAAA;AAAA;AAAA;AAAA,EAMS,OAAA,GAAU;AAAA;AAAA,IAEjB,SAAS,YAA8B;AACrC,MAAA,MAAM,EAAE,MAAM,KAAA,EAAO,QAAA,KAAa,MAAM,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,UAAU,CAAA;AAC/D,MAAA,IAAI,KAAA,KAAU,UAAa,CAAC,QAAA,CAAS,IAAI,MAAM,cAAA,CAAe,UAAU,KAAK,CAAA;AAC7E,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA;AAAA,IAGA,QAAQ,YAA8B;AACpC,MAAA,MAAM,EAAE,MAAM,KAAA,EAAO,QAAA,KAAa,MAAM,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,SAAS,CAAA;AAC9D,MAAA,IAAI,KAAA,KAAU,UAAa,CAAC,QAAA,CAAS,IAAI,MAAM,cAAA,CAAe,UAAU,KAAK,CAAA;AAC7E,MAAA,OAAO,IAAA;AAAA,IACT,CAAA;AAAA;AAAA,IAGA,SAAS,YAA8B;AACrC,MAAA,MAAM,EAAE,MAAM,KAAA,EAAO,QAAA,KAAa,MAAM,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,UAAU,CAAA;AAC/D,MAAA,IAAI,KAAA,KAAU,UAAa,CAAC,QAAA,CAAS,IAAI,MAAM,cAAA,CAAe,UAAU,KAAK,CAAA;AAC7E,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,GACF;AACF","file":"index.js","sourcesContent":["/**\n * The error body shape returned by the Krova Cloud API.\n *\n * Per the OpenAPI spec (`components.schemas.Error`), every non-2xx response\n * body is `{ \"error\": string }`. Additional fields may appear over time, so\n * we keep the type open.\n */\nexport interface KrovaErrorBody {\n error?: string;\n [key: string]: unknown;\n}\n\n/**\n * Error thrown by the ergonomic {@link KrovaClient} helpers when the API\n * responds with a non-2xx status.\n *\n * The raw openapi-fetch client (`client.raw`) never throws — it returns\n * `{ data, error, response }`. The helpers wrap that and throw `KrovaError`\n * so callers can `try/catch`.\n */\nexport class KrovaError extends Error {\n /** HTTP status code of the failing response. */\n readonly status: number;\n\n /**\n * A machine-readable error code, when the API surfaces one via the\n * `X-Error-Code` response header. The documented error body only carries a\n * human-readable `error` string, so this is best-effort.\n */\n readonly code?: string;\n\n /**\n * The request id from the `X-Request-Id` response header, when present.\n * Useful when contacting Krova Cloud support about a specific failure.\n */\n readonly requestId?: string;\n\n /** The parsed JSON error body, when the response had one. */\n readonly body?: KrovaErrorBody;\n\n /** The raw `Response` object, for callers that need headers/url/etc. */\n readonly response?: Response;\n\n constructor(\n message: string,\n init: {\n status: number;\n code?: string;\n requestId?: string;\n body?: KrovaErrorBody;\n response?: Response;\n },\n ) {\n super(message);\n this.name = \"KrovaError\";\n this.status = init.status;\n this.code = init.code;\n this.requestId = init.requestId;\n this.body = init.body;\n this.response = init.response;\n // Restore prototype chain for instanceof across compilation targets.\n Object.setPrototypeOf(this, KrovaError.prototype);\n }\n}\n\n/**\n * Build a {@link KrovaError} from a failing response + parsed error body.\n */\nexport function krovaErrorFrom(\n response: Response,\n body: KrovaErrorBody | undefined,\n): KrovaError {\n const message =\n (typeof body?.error === \"string\" && body.error) ||\n response.statusText ||\n `Request failed with status ${response.status}`;\n return new KrovaError(message, {\n status: response.status,\n code: response.headers.get(\"x-error-code\") ?? undefined,\n requestId: response.headers.get(\"x-request-id\") ?? undefined,\n body,\n response,\n });\n}\n","import createClient, { type Client, type Middleware } from \"openapi-fetch\";\nimport { krovaErrorFrom } from \"./error.js\";\nimport type { components, paths } from \"./generated/types.js\";\n\n/** The Cube resource, as defined in the Krova Cloud OpenAPI spec. */\nexport type Cube = components[\"schemas\"][\"Cube\"];\n\n/** Default API base URL — the single `servers[0].url` from the OpenAPI spec. */\nexport const DEFAULT_BASE_URL = \"https://krova.cloud/api/v1\";\n\n/**\n * How the API key is presented to the server.\n *\n * - `\"x-api-key\"` (default) — `X-API-KEY: <key>`, matching the spec's\n * `components.securitySchemes.ApiKeyAuth` (an `apiKey` header named\n * `X-API-KEY`).\n * - `\"bearer\"` — `Authorization: Bearer <key>`, for gateways that expect it.\n */\nexport type AuthScheme = \"x-api-key\" | \"bearer\";\n\nexport interface KrovaClientOptions {\n /**\n * Your Krova Cloud API key (a `kro_...` token). Keys are scoped per Space\n * and inherit the permissions of the membership that created them.\n */\n apiKey: string;\n /** Override the API base URL. Defaults to {@link DEFAULT_BASE_URL}. */\n baseUrl?: string;\n /**\n * Auth header scheme. Defaults to `\"x-api-key\"` (the spec's scheme).\n */\n authScheme?: AuthScheme;\n /**\n * Max automatic retries on retryable statuses (429, 503).\n * Defaults to 2. Set to 0 to disable retries.\n */\n maxRetries?: number;\n /**\n * A custom `fetch` implementation (e.g. for tests or a proxy). Defaults to\n * the global `fetch`.\n */\n fetch?: typeof fetch;\n}\n\n/** Statuses the retry middleware treats as transient. */\nconst RETRYABLE_STATUSES = new Set([429, 503]);\n/** Fallback backoff (ms) when the server sends no `Retry-After` header. */\nconst BASE_BACKOFF_MS = 500;\n/** Cap on any single backoff wait (ms), to keep retries \"small but real\". */\nconst MAX_BACKOFF_MS = 10_000;\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Parse a `Retry-After` header (RFC 7231): either delta-seconds or an\n * HTTP-date. Returns milliseconds to wait, or `null` if absent/unparseable.\n */\nfunction parseRetryAfterMs(headerValue: string | null): number | null {\n if (!headerValue) return null;\n const seconds = Number(headerValue);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n const dateMs = Date.parse(headerValue);\n if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());\n return null;\n}\n\nfunction authMiddleware(apiKey: string, scheme: AuthScheme): Middleware {\n return {\n onRequest({ request }) {\n if (scheme === \"bearer\") {\n request.headers.set(\"Authorization\", `Bearer ${apiKey}`);\n } else {\n request.headers.set(\"X-API-KEY\", apiKey);\n }\n return request;\n },\n };\n}\n\n/**\n * Retry middleware: on a retryable status, wait (honoring `Retry-After` when\n * present, else exponential backoff) and re-issue the request. openapi-fetch\n * clones the request per attempt, so re-fetching here is safe.\n */\nfunction retryMiddleware(maxRetries: number, doFetch: typeof fetch): Middleware {\n return {\n async onResponse({ request, response }) {\n if (maxRetries <= 0 || !RETRYABLE_STATUSES.has(response.status)) {\n return response;\n }\n let current = response;\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n if (!RETRYABLE_STATUSES.has(current.status)) break;\n const retryAfterMs = parseRetryAfterMs(current.headers.get(\"retry-after\"));\n const backoff = Math.min(BASE_BACKOFF_MS * 2 ** (attempt - 1), MAX_BACKOFF_MS);\n await sleep(retryAfterMs ?? backoff);\n current = await doFetch(request.clone());\n }\n return current;\n },\n };\n}\n\n/**\n * A typed client for the Krova Cloud API.\n *\n * @example\n * ```ts\n * const krova = new KrovaClient({ apiKey: \"kro_...\" });\n * const cubes = await krova.cubes.list(\"space_123\");\n * ```\n */\nexport class KrovaClient {\n /**\n * The underlying openapi-fetch client — a fully typed escape hatch to every\n * path in the spec. Returns `{ data, error, response }` and never throws.\n *\n * @example\n * ```ts\n * const { data, error } = await krova.raw.GET(\n * \"/spaces/{spaceId}/cubes/{cubeId}\",\n * { params: { path: { spaceId, cubeId } } },\n * );\n * ```\n */\n readonly raw: Client<paths>;\n\n /** The resolved base URL in use. */\n readonly baseUrl: string;\n\n constructor(options: KrovaClientOptions) {\n if (!options?.apiKey) {\n throw new Error(\"KrovaClient: `apiKey` is required.\");\n }\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n const doFetch = options.fetch ?? globalThis.fetch;\n const maxRetries = options.maxRetries ?? 2;\n\n this.raw = createClient<paths>({\n baseUrl: this.baseUrl,\n // SECURITY: never auto-follow redirects. The Krova Cloud API is a plain\n // JSON API and never legitimately 3xx's a data call. Following a redirect\n // would resend the `X-API-KEY` header to the redirect target — and unlike\n // `Authorization`, `Cookie`, and `Proxy-Authorization`, the Fetch spec does\n // NOT strip a custom header like `X-API-KEY` on a cross-origin redirect\n // (verified against undici/Node fetch). A compromised/misconfigured proxy,\n // an open-redirect on the API, or a MITM could otherwise exfiltrate the key\n // to an attacker's host. With `\"manual\"`, a redirect comes back as a\n // non-ok response and the helpers throw `KrovaError` instead of leaking.\n redirect: \"manual\",\n ...(options.fetch ? { fetch: options.fetch } : {}),\n });\n this.raw.use(authMiddleware(options.apiKey, options.authScheme ?? \"x-api-key\"));\n if (maxRetries > 0) {\n this.raw.use(retryMiddleware(maxRetries, doFetch));\n }\n }\n\n // ---------------------------------------------------------------------------\n // Cubes\n // ---------------------------------------------------------------------------\n\n readonly cubes = {\n /** List Cubes in a Space. Returns the raw (paginated) response body. */\n list: async (spaceId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.GET(\"/spaces/{spaceId}/cubes\", {\n params: { path: { spaceId } },\n });\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /**\n * Create a Cube. Returns the created {@link Cube}.\n *\n * @param spaceId Target Space id.\n * @param body Cube spec — `{ name, image, resources, sshPublicKey, ... }`.\n * @param opts Optional `idempotencyKey` (max 255 chars, scoped per space).\n */\n create: async (\n spaceId: string,\n body: NonNullable<\n paths[\"/spaces/{spaceId}/cubes\"][\"post\"][\"requestBody\"]\n >[\"content\"][\"application/json\"],\n opts?: { idempotencyKey?: string },\n ): Promise<Cube> => {\n const { data, error, response } = await this.raw.POST(\"/spaces/{spaceId}/cubes\", {\n params: {\n path: { spaceId },\n ...(opts?.idempotencyKey\n ? { header: { \"Idempotency-Key\": opts.idempotencyKey } }\n : {}),\n },\n body,\n });\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n const cube = data?.cube;\n if (!cube) {\n throw krovaErrorFrom(response, { error: \"Create Cube response had no `cube`.\" });\n }\n return cube;\n },\n\n /** Get a single Cube. Returns the raw response body. */\n get: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.GET(\n \"/spaces/{spaceId}/cubes/{cubeId}\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /**\n * Update a Cube's SSH port.\n *\n * The Krova Cloud API exposes no general Cube-mutation endpoint; the only\n * mutable Cube field over the API is its SSH port, via\n * `PUT /spaces/{spaceId}/cubes/{cubeId}/ssh-port`. This helper maps to that\n * endpoint. (Compute resize / rename are not part of the public API.)\n */\n update: async (\n spaceId: string,\n cubeId: string,\n body: NonNullable<\n paths[\"/spaces/{spaceId}/cubes/{cubeId}/ssh-port\"][\"put\"][\"requestBody\"]\n >[\"content\"][\"application/json\"],\n ): Promise<unknown> => {\n const { data, error, response } = await this.raw.PUT(\n \"/spaces/{spaceId}/cubes/{cubeId}/ssh-port\",\n { params: { path: { spaceId, cubeId } }, body },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Delete a Cube (asynchronous — deletion is enqueued). */\n delete: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.DELETE(\n \"/spaces/{spaceId}/cubes/{cubeId}\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Sleep a running Cube (asynchronous — sleep is enqueued). */\n sleep: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.POST(\n \"/spaces/{spaceId}/cubes/{cubeId}/sleep\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Wake a sleeping Cube (asynchronous — wake is enqueued). */\n wake: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.POST(\n \"/spaces/{spaceId}/cubes/{cubeId}/wake\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n };\n\n // ---------------------------------------------------------------------------\n // Public catalog (no auth required by the API, but the key is harmless)\n // ---------------------------------------------------------------------------\n\n readonly catalog = {\n /** List regions with available capacity. */\n regions: async (): Promise<unknown> => {\n const { data, error, response } = await this.raw.GET(\"/regions\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** List available OS images. */\n images: async (): Promise<unknown> => {\n const { data, error, response } = await this.raw.GET(\"/images\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Per-resource hourly rates and volume pricing tiers. */\n pricing: async (): Promise<unknown> => {\n const { data, error, response } = await this.raw.GET(\"/pricing\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/error.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * The error body shape returned by the Krova Cloud API.\n *\n * Per the OpenAPI spec (`components.schemas.Error`), every non-2xx response\n * body is `{ \"error\": string }`. Additional fields may appear over time, so\n * we keep the type open.\n */\nexport interface KrovaErrorBody {\n error?: string;\n [key: string]: unknown;\n}\n\n/**\n * Error thrown by the ergonomic {@link KrovaClient} helpers when the API\n * responds with a non-2xx status.\n *\n * The raw openapi-fetch client (`client.raw`) never throws — it returns\n * `{ data, error, response }`. The helpers wrap that and throw `KrovaError`\n * so callers can `try/catch`.\n */\nexport class KrovaError extends Error {\n /** HTTP status code of the failing response. */\n readonly status: number;\n\n /**\n * A machine-readable error code, when the API surfaces one via the\n * `X-Error-Code` response header. The documented error body only carries a\n * human-readable `error` string, so this is best-effort.\n */\n readonly code?: string;\n\n /**\n * The request id from the `X-Request-Id` response header, when present.\n * Useful when contacting Krova Cloud support about a specific failure.\n */\n readonly requestId?: string;\n\n /** The parsed JSON error body, when the response had one. */\n readonly body?: KrovaErrorBody;\n\n /** The raw `Response` object, for callers that need headers/url/etc. */\n readonly response?: Response;\n\n constructor(\n message: string,\n init: {\n status: number;\n code?: string;\n requestId?: string;\n body?: KrovaErrorBody;\n response?: Response;\n },\n ) {\n super(message);\n this.name = \"KrovaError\";\n this.status = init.status;\n this.code = init.code;\n this.requestId = init.requestId;\n this.body = init.body;\n this.response = init.response;\n // Restore prototype chain for instanceof across compilation targets.\n Object.setPrototypeOf(this, KrovaError.prototype);\n }\n}\n\n/**\n * Build a {@link KrovaError} from a failing response + parsed error body.\n */\nexport function krovaErrorFrom(\n response: Response,\n body: KrovaErrorBody | undefined,\n): KrovaError {\n const message =\n (typeof body?.error === \"string\" && body.error) ||\n response.statusText ||\n `Request failed with status ${response.status}`;\n return new KrovaError(message, {\n status: response.status,\n code: response.headers.get(\"x-error-code\") ?? undefined,\n requestId: response.headers.get(\"x-request-id\") ?? undefined,\n body,\n response,\n });\n}\n","import createClient, { type Client, type Middleware } from \"openapi-fetch\";\nimport { krovaErrorFrom } from \"./error.js\";\nimport type { components, paths } from \"./generated/types.js\";\n\n/** The Cube resource, as defined in the Krova Cloud OpenAPI spec. */\nexport type Cube = components[\"schemas\"][\"Cube\"];\n\n/** A region with available capacity (from the catalog). */\nexport type Region = components[\"schemas\"][\"Region\"];\n\n/** A selectable OS image (from the catalog). */\nexport type Image = components[\"schemas\"][\"Image\"];\n\n/** A volume-pricing tier (from the catalog). */\nexport type PricingTier = components[\"schemas\"][\"PricingTier\"];\n\n/** Pagination envelope returned alongside a Cube list. */\nexport type Pagination = components[\"schemas\"][\"Pagination\"];\n\n/** Default API base URL — the single `servers[0].url` from the OpenAPI spec. */\nexport const DEFAULT_BASE_URL = \"https://krova.cloud/api/v1\";\n\n/**\n * How the API key is presented to the server.\n *\n * - `\"x-api-key\"` (default) — `X-API-KEY: <key>`, matching the spec's\n * `components.securitySchemes.ApiKeyAuth` (an `apiKey` header named\n * `X-API-KEY`).\n * - `\"bearer\"` — `Authorization: Bearer <key>`, for gateways that expect it.\n */\nexport type AuthScheme = \"x-api-key\" | \"bearer\";\n\nexport interface KrovaClientOptions {\n /**\n * Your Krova Cloud API key (a `kro_...` token). Keys are scoped per Space\n * and inherit the permissions of the membership that created them.\n */\n apiKey: string;\n /** Override the API base URL. Defaults to {@link DEFAULT_BASE_URL}. */\n baseUrl?: string;\n /**\n * Auth header scheme. Defaults to `\"x-api-key\"` (the spec's scheme).\n */\n authScheme?: AuthScheme;\n /**\n * Max automatic retries on retryable statuses (429, 503).\n * Defaults to 2. Set to 0 to disable retries.\n */\n maxRetries?: number;\n /**\n * A custom `fetch` implementation (e.g. for tests or a proxy). Defaults to\n * the global `fetch`.\n */\n fetch?: typeof fetch;\n}\n\n/** Statuses the retry middleware treats as transient. */\nconst RETRYABLE_STATUSES = new Set([429, 503]);\n/** Fallback backoff (ms) when the server sends no `Retry-After` header. */\nconst BASE_BACKOFF_MS = 500;\n/** Cap on any single backoff wait (ms), to keep retries \"small but real\". */\nconst MAX_BACKOFF_MS = 10_000;\n\nconst sleep = (ms: number): Promise<void> =>\n new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Parse a `Retry-After` header (RFC 7231): either delta-seconds or an\n * HTTP-date. Returns milliseconds to wait, or `null` if absent/unparseable.\n */\nfunction parseRetryAfterMs(headerValue: string | null): number | null {\n if (!headerValue) return null;\n const seconds = Number(headerValue);\n if (Number.isFinite(seconds)) return Math.max(0, seconds * 1000);\n const dateMs = Date.parse(headerValue);\n if (Number.isFinite(dateMs)) return Math.max(0, dateMs - Date.now());\n return null;\n}\n\nfunction authMiddleware(apiKey: string, scheme: AuthScheme): Middleware {\n return {\n onRequest({ request }) {\n if (scheme === \"bearer\") {\n request.headers.set(\"Authorization\", `Bearer ${apiKey}`);\n } else {\n request.headers.set(\"X-API-KEY\", apiKey);\n }\n return request;\n },\n };\n}\n\n/**\n * Retry middleware: on a retryable status, wait (honoring `Retry-After` when\n * present, else exponential backoff) and re-issue the request. openapi-fetch\n * clones the request per attempt, so re-fetching here is safe.\n */\nfunction retryMiddleware(maxRetries: number, doFetch: typeof fetch): Middleware {\n return {\n async onResponse({ request, response }) {\n if (maxRetries <= 0 || !RETRYABLE_STATUSES.has(response.status)) {\n return response;\n }\n let current = response;\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n if (!RETRYABLE_STATUSES.has(current.status)) break;\n const retryAfterMs = parseRetryAfterMs(current.headers.get(\"retry-after\"));\n const backoff = Math.min(BASE_BACKOFF_MS * 2 ** (attempt - 1), MAX_BACKOFF_MS);\n await sleep(retryAfterMs ?? backoff);\n current = await doFetch(request.clone());\n }\n return current;\n },\n };\n}\n\n/**\n * A typed client for the Krova Cloud API.\n *\n * @example\n * ```ts\n * const krova = new KrovaClient({ apiKey: \"kro_...\" });\n * const cubes = await krova.cubes.list(\"space_123\");\n * ```\n */\nexport class KrovaClient {\n /**\n * The underlying openapi-fetch client — a fully typed escape hatch to every\n * path in the spec. Returns `{ data, error, response }` and never throws.\n *\n * @example\n * ```ts\n * const { data, error } = await krova.raw.GET(\n * \"/spaces/{spaceId}/cubes/{cubeId}\",\n * { params: { path: { spaceId, cubeId } } },\n * );\n * ```\n */\n readonly raw: Client<paths>;\n\n /** The resolved base URL in use. */\n readonly baseUrl: string;\n\n constructor(options: KrovaClientOptions) {\n if (!options?.apiKey) {\n throw new Error(\"KrovaClient: `apiKey` is required.\");\n }\n this.baseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n const doFetch = options.fetch ?? globalThis.fetch;\n const maxRetries = options.maxRetries ?? 2;\n\n this.raw = createClient<paths>({\n baseUrl: this.baseUrl,\n // SECURITY: never auto-follow redirects. The Krova Cloud API is a plain\n // JSON API and never legitimately 3xx's a data call. Following a redirect\n // would resend the `X-API-KEY` header to the redirect target — and unlike\n // `Authorization`, `Cookie`, and `Proxy-Authorization`, the Fetch spec does\n // NOT strip a custom header like `X-API-KEY` on a cross-origin redirect\n // (verified against undici/Node fetch). A compromised/misconfigured proxy,\n // an open-redirect on the API, or a MITM could otherwise exfiltrate the key\n // to an attacker's host. With `\"manual\"`, a redirect comes back as a\n // non-ok response and the helpers throw `KrovaError` instead of leaking.\n redirect: \"manual\",\n ...(options.fetch ? { fetch: options.fetch } : {}),\n });\n this.raw.use(authMiddleware(options.apiKey, options.authScheme ?? \"x-api-key\"));\n if (maxRetries > 0) {\n this.raw.use(retryMiddleware(maxRetries, doFetch));\n }\n }\n\n // ---------------------------------------------------------------------------\n // Cubes\n // ---------------------------------------------------------------------------\n\n readonly cubes = {\n /** List Cubes in a Space, with pagination metadata. */\n list: async (spaceId: string) => {\n const { data, error, response } = await this.raw.GET(\"/spaces/{spaceId}/cubes\", {\n params: { path: { spaceId } },\n });\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n if (data === undefined)\n throw krovaErrorFrom(response, { error: \"List Cubes response was empty.\" });\n return data;\n },\n\n /**\n * Create a Cube. Returns the created {@link Cube}.\n *\n * @param spaceId Target Space id.\n * @param body Cube spec — `{ name, image, resources, sshPublicKey, ... }`.\n * @param opts Optional `idempotencyKey` (max 255 chars, scoped per space).\n */\n create: async (\n spaceId: string,\n body: NonNullable<\n paths[\"/spaces/{spaceId}/cubes\"][\"post\"][\"requestBody\"]\n >[\"content\"][\"application/json\"],\n opts?: { idempotencyKey?: string },\n ): Promise<Cube> => {\n const { data, error, response } = await this.raw.POST(\"/spaces/{spaceId}/cubes\", {\n params: {\n path: { spaceId },\n ...(opts?.idempotencyKey\n ? { header: { \"Idempotency-Key\": opts.idempotencyKey } }\n : {}),\n },\n body,\n });\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n const cube = data?.cube;\n if (!cube) {\n throw krovaErrorFrom(response, { error: \"Create Cube response had no `cube`.\" });\n }\n return cube;\n },\n\n /** Get a single Cube. Returns the {@link Cube}. */\n get: async (spaceId: string, cubeId: string): Promise<Cube> => {\n const { data, error, response } = await this.raw.GET(\n \"/spaces/{spaceId}/cubes/{cubeId}\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n const cube = data?.cube;\n if (!cube) {\n throw krovaErrorFrom(response, { error: \"Get Cube response had no `cube`.\" });\n }\n return cube;\n },\n\n /**\n * Update a Cube's SSH port.\n *\n * The Krova Cloud API exposes no general Cube-mutation endpoint; the only\n * mutable Cube field over the API is its SSH port, via\n * `PUT /spaces/{spaceId}/cubes/{cubeId}/ssh-port`. This helper maps to that\n * endpoint. (Compute resize / rename are not part of the public API.)\n */\n update: async (\n spaceId: string,\n cubeId: string,\n body: NonNullable<\n paths[\"/spaces/{spaceId}/cubes/{cubeId}/ssh-port\"][\"put\"][\"requestBody\"]\n >[\"content\"][\"application/json\"],\n ): Promise<unknown> => {\n const { data, error, response } = await this.raw.PUT(\n \"/spaces/{spaceId}/cubes/{cubeId}/ssh-port\",\n { params: { path: { spaceId, cubeId } }, body },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Delete a Cube (asynchronous — deletion is enqueued). */\n delete: async (spaceId: string, cubeId: string) => {\n const { data, error, response } = await this.raw.DELETE(\n \"/spaces/{spaceId}/cubes/{cubeId}\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n if (data === undefined)\n throw krovaErrorFrom(response, { error: \"Delete Cube response was empty.\" });\n return data;\n },\n\n /** Sleep a running Cube (asynchronous — sleep is enqueued). */\n sleep: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.POST(\n \"/spaces/{spaceId}/cubes/{cubeId}/sleep\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n\n /** Wake a sleeping Cube (asynchronous — wake is enqueued). */\n wake: async (spaceId: string, cubeId: string): Promise<unknown> => {\n const { data, error, response } = await this.raw.POST(\n \"/spaces/{spaceId}/cubes/{cubeId}/wake\",\n { params: { path: { spaceId, cubeId } } },\n );\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n return data;\n },\n };\n\n // ---------------------------------------------------------------------------\n // Public catalog (no auth required by the API, but the key is harmless)\n // ---------------------------------------------------------------------------\n\n readonly catalog = {\n /** List regions with available capacity. */\n regions: async () => {\n const { data, error, response } = await this.raw.GET(\"/regions\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n if (data === undefined)\n throw krovaErrorFrom(response, { error: \"Regions response was empty.\" });\n return data;\n },\n\n /** List available OS images. */\n images: async () => {\n const { data, error, response } = await this.raw.GET(\"/images\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n if (data === undefined)\n throw krovaErrorFrom(response, { error: \"Images response was empty.\" });\n return data;\n },\n\n /** Per-resource hourly rates and volume pricing tiers. */\n pricing: async () => {\n const { data, error, response } = await this.raw.GET(\"/pricing\");\n if (error !== undefined || !response.ok) throw krovaErrorFrom(response, error);\n if (data === undefined)\n throw krovaErrorFrom(response, { error: \"Pricing response was empty.\" });\n return data;\n },\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krovacloud/sdk",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "Official TypeScript SDK for Krova Cloud — a typed client for provisioning and managing Cubes (Firecracker microVMs) on bare-metal.",
5
5
  "license": "MIT",
6
6
  "type": "module",