@goable-io/sdk 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # @goable-io/sdk
2
+
3
+ TypeScript client for the [Goable](https://goable.io) API — 0-100 suitability
4
+ scoring for outdoor activities (water, snow, air, land) from real-time weather
5
+ and multi-domain physics.
6
+
7
+ > **Status: v0.1, publish-ready (not yet on npm).** Thin, typed transport over
8
+ > the public tenant-facing REST surface. Zero runtime dependencies. Works in
9
+ > Node ≥18 and modern browser/edge runtimes (uses the global `fetch`).
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm install @goable-io/sdk # once published
15
+ ```
16
+
17
+ ## Quickstart
18
+
19
+ ```ts
20
+ import { GoableClient } from "@goable-io/sdk"
21
+
22
+ const goable = new GoableClient({ apiKey: process.env.GOABLE_API_KEY! })
23
+
24
+ const result = await goable.score({
25
+ activity: "kitesurfing",
26
+ location: { lat: 43.7, lng: 7.27 },
27
+ window: { from: "2026-06-01T06:00:00Z", to: "2026-06-01T18:00:00Z" },
28
+ })
29
+
30
+ result.score // 0-100
31
+ result.verdict // "unsafe" | "poor" | "marginal" | "fair" | "favorable" | "excellent"
32
+ result.confidence
33
+ ```
34
+
35
+ ## Configuration
36
+
37
+ ```ts
38
+ new GoableClient({
39
+ apiKey: "…", // required — sent as Authorization: Bearer
40
+ baseUrl: "https://api.goable.io", // default
41
+ timeoutMs: 30_000, // default; 0 disables
42
+ fetch: customFetch, // default globalThis.fetch (inject for Node <18 / tests)
43
+ })
44
+ ```
45
+
46
+ ## Methods
47
+
48
+ | Method | Endpoint | Notes |
49
+ |---|---|---|
50
+ | `score(input)` | `POST /v1/score` | `ensemble: true` → probabilistic (Pro+) |
51
+ | `scoreSeries(input)` | `POST /v1/score/series` | per-step over a window |
52
+ | `scoreMulti(input)` | `POST /v1/score/multi` | many activities, one location |
53
+ | `scoreHistorical(input)` | `POST /v1/score/historical` | climatology percentiles (Pro+) |
54
+ | `scorePortfolio(input)` | `POST /v1/score/portfolio` | multi-spot joint variance |
55
+ | `explainCounterfactual(input)` | `POST /v1/score/explain-counterfactual` | binding constraint, sensitivities, best window/spot |
56
+ | `decision(input)` | `POST /v1/decision` | personalized go/no-go (Pro+) |
57
+ | `deleteUserData(pseudonym)` | `DELETE /v1/decision/user-data/:p` | GDPR erasure; returns receipt headers |
58
+ | `explain(input)` / `briefing(input)` | `POST /v1/intelligence/*` | LLM narratives (Pro+) |
59
+ | `projections(input)` | `POST /v1/projections` | climate-decadal (Scale) |
60
+ | `quote(input)` | `POST /v1/underwriting/quote` | parametric premium (Scale) |
61
+ | `health()` | `GET /v1/health` | liveness |
62
+
63
+ ## Errors
64
+
65
+ ```ts
66
+ import { GoableApiError, GoableNetworkError } from "@goable-io/sdk"
67
+
68
+ try {
69
+ await goable.score({ activity: "kitesurfing", location: { lat: 43.7, lng: 7.27 }, ensemble: true })
70
+ } catch (err) {
71
+ if (err instanceof GoableApiError) {
72
+ err.status // 402
73
+ err.code // "PAYMENT_REQUIRED"
74
+ err.issues // Zod issues on 422 VALIDATION_ERROR
75
+ err.detail // free-form context (e.g. plan info)
76
+ } else if (err instanceof GoableNetworkError) {
77
+ err.kind // "timeout" | "network" | "parse"
78
+ }
79
+ }
80
+ ```
81
+
82
+ ## Browser use
83
+
84
+ The client runs in the browser, but **API keys are secrets** — prefer
85
+ server-side use. For direct browser calls the API's CORS allowlist must
86
+ include your origin (contact Goable to allowlist it).
87
+
88
+ ## Types are generated from the API contract
89
+
90
+ The request/response types are **generated** from the API's OpenAPI
91
+ document (`apps/api/src/openapi/spec.ts`) via
92
+ [`openapi-typescript`](https://github.com/openapi-ts/openapi-typescript) —
93
+ they are never hand-authored, so they can't drift from the server. When the
94
+ API contract changes, a maintainer runs:
95
+
96
+ ```bash
97
+ pnpm --filter @goable-io/sdk gen
98
+ ```
99
+
100
+ which rewrites `src/generated/api.ts` + `openapi.json`. A test
101
+ (`generatedFresh.test.ts`) fails in CI if the committed output is stale, so
102
+ the types are always in lock-step with the contract. Adding a brand-new
103
+ endpoint is the only manual step (a new client method).
104
+
105
+ Request types are precise (the API rejects unknown fields). Some response
106
+ bodies are modelled as open objects where the endpoint returns a rich,
107
+ documented payload — the full wire payload is always returned, and these
108
+ tighten automatically as the OpenAPI response schemas are enriched.
109
+
110
+ ## Release (maintainers)
111
+
112
+ Publishing is automated by `.github/workflows/sdk-release.yml` (on changes
113
+ to `packages/sdk/**` on `main`): it builds, runs the freshness guard +
114
+ tests, resolves the next version, prints `npm pack --dry-run` as a leak
115
+ check, and `pnpm publish --access public` using the org `NPM_TOKEN`. The
116
+ private engine never leaves the repo — only this package's `files` are
117
+ published. `publishConfig` repoints `main`/`types`/`exports` at `dist/` at
118
+ publish time, so in-repo development keeps using the TS sources.
119
+
120
+ > No npm provenance: it requires a public source repo, and the SDK source
121
+ > stays in this private monorepo by design.
@@ -0,0 +1,52 @@
1
+ /**
2
+ * GoableClient — thin typed transport over the public Goable REST API.
3
+ * No caching, no business logic; one `request<T>` powers every method.
4
+ */
5
+ import type { BriefingRequest, BriefingResponse, CounterfactualRequest, CounterfactualResponse, DecisionRequest, DecisionResponse, DeleteUserDataResult, ExplainRequest, ExplainResponse, HealthResponse, ProjectionsRequest, ProjectionsResponse, QuoteRequest, QuoteResponse, ScoreHistoricalRequest, ScoreHistoricalResponse, ScoreMultiRequest, ScoreMultiResponse, ScorePortfolioRequest, ScorePortfolioResponse, ScoreRequest, ScoreResponse, ScoreSeriesRequest, ScoreSeriesResponse } from "./types.js";
6
+ export type FetchLike = (input: string, init?: {
7
+ method?: string;
8
+ headers?: Record<string, string>;
9
+ body?: string;
10
+ signal?: AbortSignal;
11
+ }) => Promise<{
12
+ ok: boolean;
13
+ status: number;
14
+ headers: {
15
+ get(name: string): string | null;
16
+ };
17
+ text(): Promise<string>;
18
+ }>;
19
+ export interface GoableClientOptions {
20
+ /** Tenant API key — sent as `Authorization: Bearer <apiKey>`. */
21
+ apiKey: string;
22
+ /** Base URL. Default https://api.goable.io */
23
+ baseUrl?: string;
24
+ /** Injected fetch (tests, non-global-fetch runtimes). Default globalThis.fetch. */
25
+ fetch?: FetchLike;
26
+ /** Per-request timeout in ms. Default 30000. 0 disables. */
27
+ timeoutMs?: number;
28
+ }
29
+ export declare class GoableClient {
30
+ private readonly apiKey;
31
+ private readonly baseUrl;
32
+ private readonly fetchImpl;
33
+ private readonly timeoutMs;
34
+ constructor(options: GoableClientOptions);
35
+ health(): Promise<HealthResponse>;
36
+ score(input: ScoreRequest): Promise<ScoreResponse>;
37
+ scoreSeries(input: ScoreSeriesRequest): Promise<ScoreSeriesResponse>;
38
+ scoreMulti(input: ScoreMultiRequest): Promise<ScoreMultiResponse>;
39
+ scoreHistorical(input: ScoreHistoricalRequest): Promise<ScoreHistoricalResponse>;
40
+ scorePortfolio(input: ScorePortfolioRequest): Promise<ScorePortfolioResponse>;
41
+ explainCounterfactual(input: CounterfactualRequest): Promise<CounterfactualResponse>;
42
+ decision(input: DecisionRequest): Promise<DecisionResponse>;
43
+ explain(input: ExplainRequest): Promise<ExplainResponse>;
44
+ briefing(input: BriefingRequest): Promise<BriefingResponse>;
45
+ projections(input: ProjectionsRequest): Promise<ProjectionsResponse>;
46
+ quote(input: QuoteRequest): Promise<QuoteResponse>;
47
+ /** GDPR Art. 17 erasure. Surfaces the receipt headers from a 204. */
48
+ deleteUserData(pseudonym: string): Promise<DeleteUserDataResult>;
49
+ private request;
50
+ private rawRequest;
51
+ }
52
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,eAAe,EACf,gBAAgB,EAChB,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,cAAc,EACd,kBAAkB,EAClB,mBAAmB,EACnB,YAAY,EACZ,aAAa,EACb,sBAAsB,EACtB,uBAAuB,EACvB,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,sBAAsB,EACtB,YAAY,EACZ,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,YAAY,CAAA;AAEnB,MAAM,MAAM,SAAS,GAAG,CACtB,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE;IACL,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,KACE,OAAO,CAAC;IACX,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE;QAAE,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;KAAE,CAAA;IAC7C,IAAI,IAAI,OAAO,CAAC,MAAM,CAAC,CAAA;CACxB,CAAC,CAAA;AAEF,MAAM,WAAW,mBAAmB;IAClC,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAA;IACd,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,mFAAmF;IACnF,KAAK,CAAC,EAAE,SAAS,CAAA;IACjB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAKD,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAQ;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAQ;gBAEtB,OAAO,EAAE,mBAAmB;IAaxC,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC;IAIjC,KAAK,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAIlD,WAAW,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAIpE,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAIjE,eAAe,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAIhF,cAAc,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAI7E,qBAAqB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,sBAAsB,CAAC;IAIpF,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI3D,OAAO,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,eAAe,CAAC;IAIxD,QAAQ,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAI3D,WAAW,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAIpE,KAAK,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,aAAa,CAAC;IAIlD,qEAAqE;IAC/D,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,CAAC;YAsBxD,OAAO;YAOP,UAAU;CA+BzB"}
package/dist/client.js ADDED
@@ -0,0 +1,134 @@
1
+ /**
2
+ * GoableClient — thin typed transport over the public Goable REST API.
3
+ * No caching, no business logic; one `request<T>` powers every method.
4
+ */
5
+ import { GoableNetworkError, toApiError } from "./errors.js";
6
+ const DEFAULT_BASE_URL = "https://api.goable.io";
7
+ const DEFAULT_TIMEOUT_MS = 30_000;
8
+ export class GoableClient {
9
+ apiKey;
10
+ baseUrl;
11
+ fetchImpl;
12
+ timeoutMs;
13
+ constructor(options) {
14
+ if (!options.apiKey)
15
+ throw new Error("GoableClient requires an apiKey");
16
+ this.apiKey = options.apiKey;
17
+ this.baseUrl = (options.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
18
+ const f = options.fetch ?? globalThis.fetch;
19
+ if (!f) {
20
+ throw new Error("No fetch available — pass options.fetch (Node < 18 or non-fetch runtime)");
21
+ }
22
+ this.fetchImpl = f;
23
+ this.timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
24
+ }
25
+ // ── public methods ──────────────────────────────────────────────────────
26
+ health() {
27
+ return this.request("GET", "/v1/health");
28
+ }
29
+ score(input) {
30
+ return this.request("POST", "/v1/score", input);
31
+ }
32
+ scoreSeries(input) {
33
+ return this.request("POST", "/v1/score/series", input);
34
+ }
35
+ scoreMulti(input) {
36
+ return this.request("POST", "/v1/score/multi", input);
37
+ }
38
+ scoreHistorical(input) {
39
+ return this.request("POST", "/v1/score/historical", input);
40
+ }
41
+ scorePortfolio(input) {
42
+ return this.request("POST", "/v1/score/portfolio", input);
43
+ }
44
+ explainCounterfactual(input) {
45
+ return this.request("POST", "/v1/score/explain-counterfactual", input);
46
+ }
47
+ decision(input) {
48
+ return this.request("POST", "/v1/decision", input);
49
+ }
50
+ explain(input) {
51
+ return this.request("POST", "/v1/intelligence/explain", input);
52
+ }
53
+ briefing(input) {
54
+ return this.request("POST", "/v1/intelligence/briefing", input);
55
+ }
56
+ projections(input) {
57
+ return this.request("POST", "/v1/projections", input);
58
+ }
59
+ quote(input) {
60
+ return this.request("POST", "/v1/underwriting/quote", input);
61
+ }
62
+ /** GDPR Art. 17 erasure. Surfaces the receipt headers from a 204. */
63
+ async deleteUserData(pseudonym) {
64
+ const res = await this.rawRequest("DELETE", `/v1/decision/user-data/${encodeURIComponent(pseudonym)}`);
65
+ if (!res.ok) {
66
+ throw toApiError(res.status, await safeJson(res));
67
+ }
68
+ const intHeader = (name) => {
69
+ const v = res.headers.get(name);
70
+ const n = v === null ? Number.NaN : Number(v);
71
+ return Number.isFinite(n) ? n : null;
72
+ };
73
+ return {
74
+ status: res.status,
75
+ anonymizedRows: intHeader("X-Anonymized-Rows"),
76
+ anonymizedDecisionRuns: intHeader("X-Anonymized-Decision-Runs"),
77
+ receipt: res.headers.get("X-Receipt"),
78
+ };
79
+ }
80
+ // ── transport ─────────────────────────────────────────────────────────────
81
+ async request(method, path, body) {
82
+ const res = await this.rawRequest(method, path, body);
83
+ const parsed = await safeJson(res);
84
+ if (!res.ok)
85
+ throw toApiError(res.status, parsed);
86
+ return parsed;
87
+ }
88
+ async rawRequest(method, path, body) {
89
+ const headers = {
90
+ Authorization: `Bearer ${this.apiKey}`,
91
+ Accept: "application/json",
92
+ };
93
+ if (body !== undefined)
94
+ headers["Content-Type"] = "application/json";
95
+ const controller = this.timeoutMs > 0 ? new AbortController() : undefined;
96
+ const timer = controller && this.timeoutMs > 0
97
+ ? setTimeout(() => controller.abort(), this.timeoutMs)
98
+ : undefined;
99
+ try {
100
+ return await this.fetchImpl(`${this.baseUrl}${path}`, {
101
+ method,
102
+ headers,
103
+ ...(body !== undefined && { body: JSON.stringify(body) }),
104
+ ...(controller && { signal: controller.signal }),
105
+ });
106
+ }
107
+ catch (err) {
108
+ const aborted = err?.name === "AbortError";
109
+ throw new GoableNetworkError(aborted ? `Request timed out after ${this.timeoutMs}ms` : `Network request failed: ${path}`, aborted ? "timeout" : "network", err);
110
+ }
111
+ finally {
112
+ if (timer)
113
+ clearTimeout(timer);
114
+ }
115
+ }
116
+ }
117
+ async function safeJson(res) {
118
+ let text;
119
+ try {
120
+ text = await res.text();
121
+ }
122
+ catch (err) {
123
+ throw new GoableNetworkError("Failed to read response body", "parse", err);
124
+ }
125
+ if (text === "")
126
+ return undefined;
127
+ try {
128
+ return JSON.parse(text);
129
+ }
130
+ catch {
131
+ return text;
132
+ }
133
+ }
134
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * SDK error model. Two distinct failure modes:
3
+ * - GoableApiError : the API returned a non-2xx response. Maps the
4
+ * canonical flat error envelope
5
+ * ({ error, message?, issues?, detail? }).
6
+ * - GoableNetworkError: the request never produced an HTTP response
7
+ * (DNS, connection, abort/timeout, JSON parse).
8
+ */
9
+ export interface ZodIssueLike {
10
+ code?: string;
11
+ path?: Array<string | number>;
12
+ message?: string;
13
+ [k: string]: unknown;
14
+ }
15
+ export declare class GoableApiError extends Error {
16
+ readonly name = "GoableApiError";
17
+ /** HTTP status code. */
18
+ readonly status: number;
19
+ /** Machine-readable code from the `error` field (e.g. "PAYMENT_REQUIRED"). */
20
+ readonly code: string;
21
+ /** Zod validation issues, present on 422 VALIDATION_ERROR responses. */
22
+ readonly issues?: ZodIssueLike[];
23
+ /** Free-form extra context (e.g. plan info, quote id). */
24
+ readonly detail?: Record<string, unknown>;
25
+ constructor(status: number, code: string, message?: string, extra?: {
26
+ issues?: ZodIssueLike[];
27
+ detail?: Record<string, unknown>;
28
+ });
29
+ }
30
+ export declare class GoableNetworkError extends Error {
31
+ readonly name = "GoableNetworkError";
32
+ /** "timeout" when the request was aborted by the configured timeout. */
33
+ readonly kind: "timeout" | "network" | "parse";
34
+ readonly cause?: unknown;
35
+ constructor(message: string, kind: "timeout" | "network" | "parse", cause?: unknown);
36
+ }
37
+ /** Map a parsed error body + status into a GoableApiError. Tolerant of a
38
+ * non-conforming body (falls back to the status code as the error code). */
39
+ export declare function toApiError(status: number, body: unknown): GoableApiError;
40
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,CAAA;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;CACrB;AAED,qBAAa,cAAe,SAAQ,KAAK;IACvC,SAAkB,IAAI,oBAAmB;IACzC,wBAAwB;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,8EAA8E;IAC9E,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,wEAAwE;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,CAAA;IAChC,0DAA0D;IAC1D,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;gBAGvC,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,MAAM,EAChB,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,YAAY,EAAE,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE;CAUxE;AAED,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,SAAkB,IAAI,wBAAuB;IAC7C,wEAAwE;IACxE,QAAQ,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;IAC9C,SAAkB,KAAK,CAAC,EAAE,OAAO,CAAA;gBAErB,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,EAAE,KAAK,CAAC,EAAE,OAAO;CAMpF;AASD;6EAC6E;AAC7E,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,cAAc,CAQxE"}
package/dist/errors.js ADDED
@@ -0,0 +1,57 @@
1
+ /**
2
+ * SDK error model. Two distinct failure modes:
3
+ * - GoableApiError : the API returned a non-2xx response. Maps the
4
+ * canonical flat error envelope
5
+ * ({ error, message?, issues?, detail? }).
6
+ * - GoableNetworkError: the request never produced an HTTP response
7
+ * (DNS, connection, abort/timeout, JSON parse).
8
+ */
9
+ export class GoableApiError extends Error {
10
+ name = "GoableApiError";
11
+ /** HTTP status code. */
12
+ status;
13
+ /** Machine-readable code from the `error` field (e.g. "PAYMENT_REQUIRED"). */
14
+ code;
15
+ /** Zod validation issues, present on 422 VALIDATION_ERROR responses. */
16
+ issues;
17
+ /** Free-form extra context (e.g. plan info, quote id). */
18
+ detail;
19
+ constructor(status, code, message, extra) {
20
+ super(message ?? code);
21
+ this.status = status;
22
+ this.code = code;
23
+ if (extra?.issues)
24
+ this.issues = extra.issues;
25
+ if (extra?.detail)
26
+ this.detail = extra.detail;
27
+ // Restore prototype chain for `instanceof` across transpilation targets.
28
+ Object.setPrototypeOf(this, GoableApiError.prototype);
29
+ }
30
+ }
31
+ export class GoableNetworkError extends Error {
32
+ name = "GoableNetworkError";
33
+ /** "timeout" when the request was aborted by the configured timeout. */
34
+ kind;
35
+ cause;
36
+ constructor(message, kind, cause) {
37
+ super(message);
38
+ this.kind = kind;
39
+ if (cause !== undefined)
40
+ this.cause = cause;
41
+ Object.setPrototypeOf(this, GoableNetworkError.prototype);
42
+ }
43
+ }
44
+ /** Map a parsed error body + status into a GoableApiError. Tolerant of a
45
+ * non-conforming body (falls back to the status code as the error code). */
46
+ export function toApiError(status, body) {
47
+ const b = (body ?? {});
48
+ const code = typeof b.error === "string" ? b.error : `HTTP_${status}`;
49
+ const message = typeof b.message === "string" ? b.message : undefined;
50
+ const extra = {};
51
+ if (Array.isArray(b.issues))
52
+ extra.issues = b.issues;
53
+ if (b.detail && typeof b.detail === "object")
54
+ extra.detail = b.detail;
55
+ return new GoableApiError(status, code, message, extra);
56
+ }
57
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AASH,MAAM,OAAO,cAAe,SAAQ,KAAK;IACrB,IAAI,GAAG,gBAAgB,CAAA;IACzC,wBAAwB;IACf,MAAM,CAAQ;IACvB,8EAA8E;IACrE,IAAI,CAAQ;IACrB,wEAAwE;IAC/D,MAAM,CAAiB;IAChC,0DAA0D;IACjD,MAAM,CAA0B;IAEzC,YACE,MAAc,EACd,IAAY,EACZ,OAAgB,EAChB,KAAqE;QAErE,KAAK,CAAC,OAAO,IAAI,IAAI,CAAC,CAAA;QACtB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAA;QACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,KAAK,EAAE,MAAM;YAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;QAC7C,IAAI,KAAK,EAAE,MAAM;YAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC,MAAM,CAAA;QAC7C,yEAAyE;QACzE,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,cAAc,CAAC,SAAS,CAAC,CAAA;IACvD,CAAC;CACF;AAED,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IACzB,IAAI,GAAG,oBAAoB,CAAA;IAC7C,wEAAwE;IAC/D,IAAI,CAAiC;IAC5B,KAAK,CAAU;IAEjC,YAAY,OAAe,EAAE,IAAqC,EAAE,KAAe;QACjF,KAAK,CAAC,OAAO,CAAC,CAAA;QACd,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,KAAK,KAAK,SAAS;YAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAC3C,MAAM,CAAC,cAAc,CAAC,IAAI,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAA;IAC3D,CAAC;CACF;AASD;6EAC6E;AAC7E,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,IAAa;IACtD,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAiB,CAAA;IACtC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,MAAM,EAAE,CAAA;IACrE,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;IACrE,MAAM,KAAK,GAAkE,EAAE,CAAA;IAC/E,IAAI,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;QAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,MAAwB,CAAA;IACtE,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ;QAAE,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,MAAiC,CAAA;IAChG,OAAO,IAAI,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,CAAC,CAAA;AACzD,CAAC"}