@tokenflight/fiat-client 0.0.1-rc.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/LICENSE.md ADDED
@@ -0,0 +1,52 @@
1
+ # TokenFlight SDK License
2
+
3
+ Copyright © 2026 Khalani Labs, Ltd. All rights reserved.
4
+
5
+ This software and its accompanying documentation (the "Software") are the
6
+ proprietary property of Khalani Labs, Ltd. ("Khalani").
7
+
8
+ ## Grant
9
+
10
+ Subject to the terms below, Khalani grants you a limited, non-exclusive,
11
+ non-transferable, non-sublicensable, revocable license to install and use the
12
+ Software, in unmodified form only, solely to integrate your applications with
13
+ TokenFlight and Hyperstream services operated by or with the authorization of
14
+ Khalani.
15
+
16
+ ## Restrictions
17
+
18
+ Except as expressly permitted above or required by applicable law, you may not:
19
+
20
+ - modify, adapt, translate, or create derivative works of the Software;
21
+ - distribute, sublicense, rent, lease, sell, or otherwise transfer the
22
+ Software or any copy of it, other than as an unmodified dependency bundled
23
+ into your application;
24
+ - reverse engineer, decompile, or disassemble the Software, except to the
25
+ extent such restriction is prohibited by applicable law;
26
+ - remove or alter any proprietary notices in the Software;
27
+ - use the Software to build a product or service that competes with
28
+ TokenFlight or Hyperstream services.
29
+
30
+ ## Ownership
31
+
32
+ The Software is licensed, not sold. Khalani retains all right, title, and
33
+ interest in and to the Software, including all intellectual property rights.
34
+
35
+ ## Termination
36
+
37
+ This license terminates automatically if you breach any of its terms. Upon
38
+ termination you must cease use of the Software and destroy all copies in your
39
+ possession. Khalani may discontinue the Software or this license at any time.
40
+
41
+ ## Disclaimer of Warranty
42
+
43
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45
+ FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT.
46
+
47
+ ## Limitation of Liability
48
+
49
+ TO THE MAXIMUM EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT SHALL KHALANI
50
+ BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF
51
+ CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE
52
+ SOFTWARE OR THE USE OF OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # @tokenflight/fiat-client
2
+
3
+ Zero-dependency typed client for the standard [`@tokenflight/fiat`](https://www.npmjs.com/package/@tokenflight/fiat) HTTP contract. Works in browsers, workers, and Node ≥ 18; every import from `@tokenflight/fiat` is type-only, so the runtime bundle is a few KB with no dependencies.
4
+
5
+ ```ts
6
+ import { createFiatClient } from "@tokenflight/fiat-client";
7
+
8
+ const fiat = createFiatClient(); // defaults to https://fiat.hyperstream.dev/v1/fiat
9
+
10
+ const { quotes, errors } = await fiat.quotes({
11
+ fiatCurrency: "USD",
12
+ fiatAmount: "100.00",
13
+ outputAsset: { chainId: 8453, address: "0x833589fcd6edb6e08f4c7c32d4f71b54bda02913" },
14
+ recipient: "0xYourWallet…",
15
+ });
16
+
17
+ // Pre-open the popup in the click handler's synchronous stack (popup blockers
18
+ // reject window.open after an await), then navigate it once the order lands.
19
+ const popup = window.open("about:blank", "_blank", "popup,width=480,height=720");
20
+ const order = await fiat.createOrder({ quoteId: quotes[0].quoteId });
21
+ if (order.action.type === "wrapper" && popup) popup.location = order.action.url;
22
+ else popup?.close();
23
+
24
+ const finished = await fiat.waitForOrder(order.orderId, { onOrder: (o) => render(o.progress) });
25
+ console.log(finished.status, finished.transferTxHash);
26
+ ```
27
+
28
+ - `quotesStream()` — async-iterate provider quotes as they settle (NDJSON), so one slow provider never delays a comparison UI.
29
+ - Errors are always a typed `FiatClientError` (`code`, `retryable`, `status`); transport failures map to `NETWORK_ERROR` / `TIMEOUT` / `INVALID_RESPONSE`.
30
+ - Point it at a self-hosted deployment with `createFiatClient({ baseUrl, basePath })`.
31
+
32
+ © Khalani Labs, Ltd. All rights reserved.
@@ -0,0 +1,52 @@
1
+ import { FiatConfigResponse, FiatCreateOrderRequest, FiatCreateOrderResponse, FiatOrderStatus, FiatQuoteRequest, FiatQuotesResponse, OrderView, QuoteProviderError, QuoteStreamEvent } from '@tokenflight/fiat';
2
+ export declare const DEFAULT_FIAT_BASE_URL = "https://fiat.hyperstream.dev";
3
+ export declare const DEFAULT_FIAT_BASE_PATH = "/v1/fiat";
4
+ /** Statuses after which an order can never change again — stop polling on these. */
5
+ export declare const TERMINAL_FIAT_ORDER_STATUSES: ReadonlySet<FiatOrderStatus>;
6
+ export interface FiatClientOptions {
7
+ /** Service origin, e.g. `https://fiat.hyperstream.dev` (the default). */
8
+ baseUrl?: string;
9
+ /** API prefix under the origin. Must match the server's `basePath`. Default `/v1/fiat`. */
10
+ basePath?: string;
11
+ /** Extra headers merged into every request (e.g. `{ authorization: "Bearer …" }`). */
12
+ headers?: Record<string, string>;
13
+ /** Custom fetch (tests, SSR, service bindings). Defaults to `globalThis.fetch`. */
14
+ fetch?: typeof globalThis.fetch;
15
+ /** Per-request time budget in ms; elapses → `TIMEOUT`. Default 30 000, `0` disables. */
16
+ timeoutMs?: number;
17
+ }
18
+ export interface RequestOptions {
19
+ signal?: AbortSignal;
20
+ }
21
+ export interface WaitForOrderOptions extends RequestOptions {
22
+ /** Delay between polls. Default 2 500 ms. */
23
+ pollIntervalMs?: number;
24
+ /** Overall budget before giving up with `TIMEOUT`. Default 15 min, `0` disables. */
25
+ timeoutMs?: number;
26
+ /** Called after every poll — drive UI progress from this. */
27
+ onOrder?: (order: OrderView) => void;
28
+ }
29
+ /** `quotes()` normalized result: `errors` is always an array (the wire omits it when empty). */
30
+ export interface FiatQuotesResult {
31
+ quotes: FiatQuotesResponse["quotes"];
32
+ errors: QuoteProviderError[];
33
+ }
34
+ export interface FiatClient {
35
+ /** GET /config — providers (display + capabilities), jump support, enabled features. */
36
+ config(opts?: RequestOptions): Promise<FiatConfigResponse>;
37
+ /** POST /quote — one quote per configured provider (pin `provider` for exactly one).
38
+ * Providers that could not price the request land in `errors`, never as a throw. */
39
+ quotes(request: FiatQuoteRequest, opts?: RequestOptions): Promise<FiatQuotesResult>;
40
+ /** POST /quote?mode=stream — yields each provider's `{quote}` / `{error}` as it settles,
41
+ * so one slow provider never delays the rest of a comparison UI. */
42
+ quotesStream(request: FiatQuoteRequest, opts?: RequestOptions): AsyncGenerator<QuoteStreamEvent>;
43
+ /** POST /order — commit to a quote. `action` says what the frontend does next
44
+ * (`wrapper` → open the URL as a popup). */
45
+ createOrder(request: FiatCreateOrderRequest, opts?: RequestOptions): Promise<FiatCreateOrderResponse>;
46
+ /** GET /order/:id — canonical order projection (includes `transferTxHash` once settled). */
47
+ getOrder(orderId: string, opts?: RequestOptions): Promise<OrderView>;
48
+ /** Polls `getOrder` until a terminal status and returns the final view. */
49
+ waitForOrder(orderId: string, opts?: WaitForOrderOptions): Promise<OrderView>;
50
+ }
51
+ export declare function createFiatClient(options?: FiatClientOptions): FiatClient;
52
+ //# sourceMappingURL=client.d.ts.map
package/dist/client.js ADDED
@@ -0,0 +1,197 @@
1
+ import { FiatClientError as e } from "./errors.js";
2
+ //#region src/client.ts
3
+ var t = "https://fiat.hyperstream.dev", n = "/v1/fiat", r = /* @__PURE__ */ new Set([
4
+ "completed",
5
+ "failed",
6
+ "canceled",
7
+ "expired"
8
+ ]);
9
+ function i(t = {}) {
10
+ let n = a(t.baseUrl ?? "https://fiat.hyperstream.dev", t.basePath ?? "/v1/fiat"), i = t.fetch ?? globalThis.fetch.bind(globalThis), l = t.timeoutMs ?? 3e4, h = { ...t.headers };
11
+ function g(e, t, r, a) {
12
+ return i(`${n}${t}`, {
13
+ method: e,
14
+ headers: {
15
+ accept: "application/json",
16
+ ...r === void 0 ? {} : { "content-type": "application/json" },
17
+ ...h
18
+ },
19
+ ...r === void 0 ? {} : { body: JSON.stringify(r) },
20
+ ...a ? { signal: a } : {}
21
+ });
22
+ }
23
+ async function _(t, n, r, i) {
24
+ let { signal: a, dispose: f } = o(i?.signal, l);
25
+ try {
26
+ let i = await g(t, n, r, a);
27
+ if (!i.ok) throw await s(i, a);
28
+ try {
29
+ return await c(i.json(), a);
30
+ } catch (t) {
31
+ throw u(t) ? t : new e("INVALID_RESPONSE", "response body is not valid JSON", {
32
+ status: i.status,
33
+ cause: t
34
+ });
35
+ }
36
+ } catch (e) {
37
+ throw d(e, i?.signal);
38
+ } finally {
39
+ f();
40
+ }
41
+ }
42
+ let v = {
43
+ config(e) {
44
+ return _("GET", "/config", void 0, e);
45
+ },
46
+ async quotes(e, t) {
47
+ let n = await _("POST", "/quote", e, t);
48
+ return {
49
+ quotes: n.quotes,
50
+ errors: n.errors ?? []
51
+ };
52
+ },
53
+ async *quotesStream(t, n) {
54
+ let { signal: r, dispose: i } = o(n?.signal, l);
55
+ try {
56
+ let n = await g("POST", "/quote?mode=stream", t, r);
57
+ if (!n.ok) throw await s(n, r);
58
+ if (!n.body) throw new e("INVALID_RESPONSE", "streaming response has no body", { status: n.status });
59
+ for await (let e of f(n.body, r)) {
60
+ let t = p(e);
61
+ t && (yield t);
62
+ }
63
+ } catch (e) {
64
+ throw d(e, n?.signal);
65
+ } finally {
66
+ i();
67
+ }
68
+ },
69
+ createOrder(e, t) {
70
+ return _("POST", "/order", e, t);
71
+ },
72
+ getOrder(e, t) {
73
+ return _("GET", `/order/${encodeURIComponent(e)}`, void 0, t);
74
+ },
75
+ async waitForOrder(t, n = {}) {
76
+ let i = Number.isFinite(n.pollIntervalMs) && n.pollIntervalMs > 0 ? n.pollIntervalMs : 2500, a = Number.isFinite(n.timeoutMs) && n.timeoutMs >= 0 ? n.timeoutMs : 15 * 6e4, o = a > 0 ? Date.now() + a : Infinity;
77
+ for (;;) {
78
+ let s = await v.getOrder(t, n.signal ? { signal: n.signal } : {});
79
+ if (n.onOrder?.(s), s.terminal ?? r.has(s.status)) return s;
80
+ if (Date.now() + i > o) throw new e("TIMEOUT", `order ${t} did not reach a terminal status within ${a}ms`, { retryable: !0 });
81
+ await m(i, n.signal);
82
+ }
83
+ }
84
+ };
85
+ return v;
86
+ }
87
+ function a(e, t) {
88
+ let n = e;
89
+ for (; n.endsWith("/");) n = n.slice(0, -1);
90
+ let r = t;
91
+ for (; r.endsWith("/");) r = r.slice(0, -1);
92
+ return r && !r.startsWith("/") && (r = `/${r}`), `${n}${r}`;
93
+ }
94
+ function o(t, n) {
95
+ if (!Number.isFinite(n) || n <= 0) return t ? {
96
+ signal: t,
97
+ dispose: () => {}
98
+ } : { dispose: () => {} };
99
+ let r = new AbortController(), i = setTimeout(() => r.abort(new e("TIMEOUT", `request timed out after ${n}ms`, { retryable: !0 })), n), a = () => r.abort(t?.reason);
100
+ return t && (t.aborted ? a() : t.addEventListener("abort", a, { once: !0 })), {
101
+ signal: r.signal,
102
+ dispose: () => {
103
+ clearTimeout(i), t?.removeEventListener("abort", a);
104
+ }
105
+ };
106
+ }
107
+ async function s(t, n) {
108
+ let r;
109
+ try {
110
+ r = await c(t.json(), n);
111
+ } catch (n) {
112
+ if (u(n)) throw n;
113
+ return new e("INVALID_RESPONSE", `HTTP ${t.status} without a JSON error envelope`, { status: t.status });
114
+ }
115
+ let i = r?.error;
116
+ return !i || typeof i.code != "string" || typeof i.message != "string" ? new e("INVALID_RESPONSE", `HTTP ${t.status} without a wire error envelope`, {
117
+ status: t.status,
118
+ details: r
119
+ }) : new e(i.code, i.message, {
120
+ retryable: i.retryable === !0,
121
+ status: t.status,
122
+ ...i.details === void 0 ? {} : { details: i.details }
123
+ });
124
+ }
125
+ function c(e, t) {
126
+ return t ? t.aborted ? Promise.reject(l(t)) : new Promise((n, r) => {
127
+ let i = () => r(l(t));
128
+ t.addEventListener("abort", i, { once: !0 }), e.then((e) => {
129
+ t.removeEventListener("abort", i), n(e);
130
+ }, (e) => {
131
+ t.removeEventListener("abort", i), r(e instanceof Error ? e : Error(String(e)));
132
+ });
133
+ }) : e;
134
+ }
135
+ function l(e) {
136
+ return e.reason instanceof Error ? e.reason : new DOMException("aborted", "AbortError");
137
+ }
138
+ function u(t) {
139
+ return t instanceof e || t instanceof DOMException && t.name === "AbortError";
140
+ }
141
+ function d(t, n) {
142
+ return t instanceof e ? t : t instanceof DOMException && t.name === "AbortError" ? n?.aborted ? t : new e("TIMEOUT", "request timed out", {
143
+ retryable: !0,
144
+ cause: t
145
+ }) : t instanceof Error && t.name === "TypeError" ? new e("NETWORK_ERROR", `request failed: ${t.message}`, {
146
+ retryable: !0,
147
+ cause: t
148
+ }) : t;
149
+ }
150
+ async function* f(e, t) {
151
+ let n = e.getReader(), r = new TextDecoder(), i = "";
152
+ try {
153
+ for (;;) {
154
+ let { done: e, value: a } = await c(n.read(), t);
155
+ if (e) break;
156
+ i += r.decode(a, { stream: !0 });
157
+ let o;
158
+ for (; (o = i.indexOf("\n")) !== -1;) {
159
+ let e = i.slice(0, o).trim();
160
+ i = i.slice(o + 1), e && (yield e);
161
+ }
162
+ }
163
+ let e = (i + r.decode()).trim();
164
+ e && (yield e);
165
+ } finally {
166
+ try {
167
+ await n.cancel();
168
+ } catch {}
169
+ n.releaseLock();
170
+ }
171
+ }
172
+ function p(t) {
173
+ let n;
174
+ try {
175
+ n = JSON.parse(t);
176
+ } catch (n) {
177
+ throw new e("INVALID_RESPONSE", "stream emitted a non-JSON line", {
178
+ cause: n,
179
+ details: { line: t }
180
+ });
181
+ }
182
+ return typeof n != "object" || !n || !("quote" in n) && !("error" in n) ? null : n;
183
+ }
184
+ function m(e, t) {
185
+ return new Promise((n, r) => {
186
+ if (t?.aborted) return r(t.reason instanceof Error ? t.reason : new DOMException("aborted", "AbortError"));
187
+ let i = setTimeout(() => {
188
+ t?.removeEventListener("abort", a), n();
189
+ }, e);
190
+ function a() {
191
+ clearTimeout(i), r(t?.reason instanceof Error ? t.reason : new DOMException("aborted", "AbortError"));
192
+ }
193
+ t?.addEventListener("abort", a, { once: !0 });
194
+ });
195
+ }
196
+ //#endregion
197
+ export { n as DEFAULT_FIAT_BASE_PATH, t as DEFAULT_FIAT_BASE_URL, r as TERMINAL_FIAT_ORDER_STATUSES, i as createFiatClient };
@@ -0,0 +1,20 @@
1
+ import { FiatErrorCode } from '@tokenflight/fiat';
2
+ /** Client-side failure codes, for errors the server never produced. */
3
+ export type FiatClientOnlyErrorCode = "NETWORK_ERROR" | "TIMEOUT" | "INVALID_RESPONSE";
4
+ export type FiatClientErrorCode = FiatErrorCode | FiatClientOnlyErrorCode;
5
+ /** One error type for everything the client throws. `code` is the wire error code when
6
+ * the server produced the failure; `status` is present iff an HTTP response arrived. */
7
+ export declare class FiatClientError extends Error {
8
+ readonly code: FiatClientErrorCode | (string & {});
9
+ readonly retryable: boolean;
10
+ readonly status?: number;
11
+ readonly details?: unknown;
12
+ constructor(code: FiatClientErrorCode | (string & {}), message: string, options?: {
13
+ retryable?: boolean;
14
+ status?: number;
15
+ details?: unknown;
16
+ cause?: unknown;
17
+ });
18
+ }
19
+ export declare function isFiatClientError(value: unknown): value is FiatClientError;
20
+ //# sourceMappingURL=errors.d.ts.map
package/dist/errors.js ADDED
@@ -0,0 +1,15 @@
1
+ //#region src/errors.ts
2
+ var e = class extends Error {
3
+ code;
4
+ retryable;
5
+ status;
6
+ details;
7
+ constructor(e, t, n = {}) {
8
+ super(t, n.cause === void 0 ? void 0 : { cause: n.cause }), this.name = "FiatClientError", this.code = e, this.retryable = n.retryable ?? !1, n.status !== void 0 && (this.status = n.status), n.details !== void 0 && (this.details = n.details);
9
+ }
10
+ };
11
+ function t(t) {
12
+ return t instanceof e;
13
+ }
14
+ //#endregion
15
+ export { e as FiatClientError, t as isFiatClientError };
@@ -0,0 +1,4 @@
1
+ export { createFiatClient, DEFAULT_FIAT_BASE_PATH, DEFAULT_FIAT_BASE_URL, TERMINAL_FIAT_ORDER_STATUSES, type FiatClient, type FiatClientOptions, type FiatQuotesResult, type RequestOptions, type WaitForOrderOptions, } from './client.js';
2
+ export { FiatClientError, isFiatClientError, type FiatClientErrorCode, type FiatClientOnlyErrorCode } from './errors.js';
3
+ export type { FiatAssetRef, FiatConfigResponse, FiatCreateOrderRequest, FiatCreateOrderResponse, FiatErrorCode, FiatOrderAction, FiatOrderFailure, FiatOrderStatus, FiatFailureSource, FiatProgress, FiatQuoteRequest, FiatQuoteRequestBase, FiatQuoteResult, FiatQuotesResponse, FiatTokenMetadata, InputMode, OrderView, ProviderCapabilities, ProviderDisplay, QuoteKind, QuoteProviderError, QuoteStreamEvent, SwapStrategy, WireError, } from '@tokenflight/fiat';
4
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ import { FiatClientError as e, isFiatClientError as t } from "./errors.js";
2
+ import { DEFAULT_FIAT_BASE_PATH as n, DEFAULT_FIAT_BASE_URL as r, TERMINAL_FIAT_ORDER_STATUSES as i, createFiatClient as a } from "./client.js";
3
+ export { n as DEFAULT_FIAT_BASE_PATH, r as DEFAULT_FIAT_BASE_URL, e as FiatClientError, i as TERMINAL_FIAT_ORDER_STATUSES, a as createFiatClient, t as isFiatClientError };
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@tokenflight/fiat-client",
3
+ "version": "0.0.1-rc.0",
4
+ "description": "Typed client for the @tokenflight/fiat HTTP contract — zero runtime dependencies",
5
+ "homepage": "https://fiat.hyperstream.dev/docs",
6
+ "license": "SEE LICENSE IN LICENSE.md",
7
+ "author": "Khalani Labs, Ltd. (https://khalani.network)",
8
+ "files": [
9
+ "dist",
10
+ "!dist/**/*.map"
11
+ ],
12
+ "type": "module",
13
+ "sideEffects": false,
14
+ "main": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "default": "./dist/index.js"
21
+ },
22
+ "./package.json": "./package.json"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "dependencies": {
28
+ "@tokenflight/fiat": "^0.0.1-rc.0"
29
+ },
30
+ "devDependencies": {
31
+ "typescript": "^6.0.3",
32
+ "vite": "^8.1.0",
33
+ "vite-plugin-dts": "^5.0.3"
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "scripts": {
39
+ "build": "vite build"
40
+ }
41
+ }