@usethrottle/invoices 0.1.2 → 0.2.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/dist/index.cjs CHANGED
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
+ var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
5
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
6
8
  var __export = (target, all) => {
7
9
  for (var name in all)
@@ -15,17 +17,36 @@ var __copyProps = (to, from, except, desc) => {
15
17
  }
16
18
  return to;
17
19
  };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
18
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
29
 
20
30
  // src/index.tsx
21
31
  var index_exports = {};
22
32
  __export(index_exports, {
33
+ CURRENCIES: () => import_money.CURRENCIES,
23
34
  InvoiceProvider: () => InvoiceProvider,
24
35
  InvoicesClient: () => InvoicesClient,
25
36
  ThrottleError: () => import_errors2.ThrottleError,
26
37
  ThrottleInvoicesError: () => ThrottleInvoicesError,
38
+ UnknownCurrencyError: () => import_money.UnknownCurrencyError,
27
39
  createInvoicesClient: () => createInvoicesClient,
40
+ currencySymbol: () => import_money.currencySymbol,
41
+ formatMoney: () => import_money.formatMoney,
42
+ isKnownCurrency: () => import_money.isKnownCurrency,
28
43
  isThrottleError: () => import_errors2.isThrottleError,
44
+ minorUnitsOf: () => import_money.minorUnitsOf,
45
+ money: () => money,
46
+ moneyValue: () => import_money.moneyValue,
47
+ parseMoney: () => import_money.parseMoney,
48
+ toDecimalString: () => import_money.toDecimalString,
49
+ toMinor: () => import_money.toMinor,
29
50
  useDownloadInvoice: () => useDownloadInvoice,
30
51
  useInvoice: () => useInvoice,
31
52
  useInvoiceClient: () => useInvoiceClient,
@@ -223,14 +244,29 @@ function useStableValue(value) {
223
244
  const key = JSON.stringify(value);
224
245
  return (0, import_react2.useMemo)(() => value, [key]);
225
246
  }
247
+
248
+ // src/index.tsx
249
+ var money = __toESM(require("@usethrottle/money"), 1);
250
+ var import_money = require("@usethrottle/money");
226
251
  // Annotate the CommonJS export names for ESM import in node:
227
252
  0 && (module.exports = {
253
+ CURRENCIES,
228
254
  InvoiceProvider,
229
255
  InvoicesClient,
230
256
  ThrottleError,
231
257
  ThrottleInvoicesError,
258
+ UnknownCurrencyError,
232
259
  createInvoicesClient,
260
+ currencySymbol,
261
+ formatMoney,
262
+ isKnownCurrency,
233
263
  isThrottleError,
264
+ minorUnitsOf,
265
+ money,
266
+ moneyValue,
267
+ parseMoney,
268
+ toDecimalString,
269
+ toMinor,
234
270
  useDownloadInvoice,
235
271
  useInvoice,
236
272
  useInvoiceClient,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.tsx","../src/errors.ts","../src/client.ts","../src/provider.tsx","../src/hooks.ts"],"sourcesContent":["export { InvoicesClient, createInvoicesClient } from './client';\nexport { ThrottleInvoicesError, ThrottleError, isThrottleError } from './errors';\nexport { InvoiceProvider, useInvoiceClient, useInvoiceInvalidation } from './provider';\nexport { useInvoices, useInvoice, useDownloadInvoice } from './hooks';\nexport type * from './types';\n","import { ThrottleError, type ThrottleErrorArgs } from '@usethrottle/errors';\nexport class ThrottleInvoicesError extends ThrottleError {\n constructor(args: { code: string; message: string; statusCode: number; details?: unknown; responseHeaders?: ThrottleErrorArgs['responseHeaders'] }) {\n super(args); this.name = 'ThrottleInvoicesError';\n }\n}\nexport { ThrottleError, isThrottleError } from '@usethrottle/errors';\n","import { ThrottleInvoicesError } from './errors';\nimport type { Invoice, InvoicesClientContract, InvoicesClientOptions, ListInvoicesParams, PaginatedInvoices, ThrottleEnvelope } from './types';\n\nexport class InvoicesClient implements InvoicesClientContract {\n private readonly apiKey?: string; private readonly baseUrl: string;\n private readonly fetchImpl: typeof globalThis.fetch; private readonly fetcher?: InvoicesClientOptions['fetcher']; private readonly timeoutMs: number;\n constructor(opts: InvoicesClientOptions = {}) {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n if (!fetchImpl) throw new Error('InvoicesClient: fetch is required');\n if (!opts.apiKey && !opts.fetcher) throw new Error('InvoicesClient: apiKey or fetcher is required');\n this.apiKey = opts.apiKey; this.baseUrl = (opts.baseUrl ?? 'https://api.usethrottle.dev').replace(/\\/+$/, '');\n this.fetchImpl = fetchImpl; this.fetcher = opts.fetcher; this.timeoutMs = opts.timeoutMs ?? 30_000;\n }\n private async requestEnvelope<T>(method: string, path: string): Promise<ThrottleEnvelope<T>> {\n const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n try {\n const init: RequestInit = { method, headers: { 'content-type': 'application/json', ...(this.apiKey ? { 'x-api-key': this.apiKey } : {}) }, signal: ctrl.signal };\n const res = this.fetcher ? await this.fetcher(path, init) : await this.fetchImpl(`${this.baseUrl}${path}`, init);\n const json = (await res.json().catch(() => ({}))) as ThrottleEnvelope<T>;\n if (!res.ok) throw new ThrottleInvoicesError({ code: json.error?.code ?? 'unknown_error', message: json.error?.message ?? `HTTP ${res.status}`, statusCode: res.status, details: json.error?.details, responseHeaders: res.headers });\n return json;\n } finally { clearTimeout(timer); }\n }\n async list(params: ListInvoicesParams = {}): Promise<PaginatedInvoices> {\n const q = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) if (v !== undefined && v !== null && v !== '') q.set(k, String(v));\n const suffix = q.toString() ? `?${q}` : '';\n const env = await this.requestEnvelope<Invoice[]>('GET', `/api/v1/invoices${suffix}`);\n return { data: env.data ?? [], pagination: env.meta?.pagination ? { cursor: env.meta.pagination.cursor ?? null, hasMore: Boolean(env.meta.pagination.hasMore) } : undefined };\n }\n async get(id: string): Promise<Invoice> {\n const env = await this.requestEnvelope<Invoice>('GET', `/api/v1/invoices/${encodeURIComponent(id)}`);\n return env.data as Invoice;\n }\n async getDownloadUrl(id: string): Promise<string> {\n const env = await this.requestEnvelope<{ url: string }>('GET', `/api/v1/invoices/${encodeURIComponent(id)}/download?format=json`);\n return (env.data as { url: string }).url;\n }\n}\nexport function createInvoicesClient(opts: InvoicesClientOptions): InvoicesClient { return new InvoicesClient(opts); }\n","import { createContext, useCallback, useContext, useMemo, useReducer, type ReactNode } from 'react';\nimport { InvoicesClient } from './client';\nimport type {\n InvoiceFetcher,\n InvoicesProviderProps,\n InvoicesClientContract,\n} from './types';\n\ninterface InvoiceContextValue {\n client: InvoicesClientContract;\n cacheVersion: number;\n invalidate: () => void;\n}\n\nconst InvoiceContext = createContext<InvoiceContextValue | null>(null);\n\nexport function InvoiceProvider({\n children,\n client,\n fetcher,\n baseUrl,\n timeoutMs,\n}: InvoicesProviderProps) {\n const [cacheVersion, invalidate] = useReducer((value: number) => value + 1, 0);\n const resolvedClient = useMemo(() => {\n if (client) return client;\n return new InvoicesClient({\n fetcher: fetcher ?? createInvoiceFetcher('/api/throttle'),\n baseUrl,\n timeoutMs,\n });\n }, [baseUrl, client, fetcher, timeoutMs]);\n\n const value = useMemo(\n () => ({ client: resolvedClient, cacheVersion, invalidate }),\n [cacheVersion, resolvedClient],\n );\n\n return <InvoiceContext.Provider value={value}>{children}</InvoiceContext.Provider>;\n}\n\nexport function useInvoiceClient(): InvoicesClientContract {\n return useInvoiceContext().client;\n}\n\nexport function useInvoiceInvalidation(): () => void {\n return useInvoiceContext().invalidate;\n}\n\nexport function useInvoiceCacheVersion(): number {\n return useInvoiceContext().cacheVersion;\n}\n\nexport function createInvoiceFetcher(basePath: string): InvoiceFetcher {\n const cleanBase = basePath.replace(/\\/+$/, '');\n return (path, init) => {\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return fetch(`${cleanBase}${cleanPath}`, init);\n };\n}\n\nexport function useInvoiceContext(): InvoiceContextValue {\n const ctx = useContext(InvoiceContext);\n if (!ctx) {\n throw new Error(\n '@usethrottle/invoices hooks must be used inside <InvoiceProvider />',\n );\n }\n return ctx;\n}\n\nexport function InvoiceProviderWithClient({\n children,\n client,\n}: {\n children: ReactNode;\n client: InvoicesClientContract;\n}) {\n const [cacheVersion, invalidate] = useReducer((value: number) => value + 1, 0);\n const value = useMemo(() => ({ client, cacheVersion, invalidate }), [cacheVersion, client]);\n return <InvoiceContext.Provider value={value}>{children}</InvoiceContext.Provider>;\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n useInvoiceCacheVersion,\n useInvoiceClient,\n} from './provider';\nimport type {\n Invoice,\n ListInvoicesParams,\n PaginatedInvoices,\n QueryOptions,\n QueryState,\n} from './types';\n\nexport function useInvoices(\n params: ListInvoicesParams = {},\n options: QueryOptions<PaginatedInvoices> = {},\n): QueryState<PaginatedInvoices> {\n const client = useInvoiceClient();\n const v = useInvoiceCacheVersion();\n const p = useStableValue(params);\n return useAsyncQuery(() => client.list(p), [client, p, v], options);\n}\n\nexport function useInvoice(\n id: string | null | undefined,\n options: QueryOptions<Invoice> = {},\n): QueryState<Invoice> {\n const client = useInvoiceClient();\n const v = useInvoiceCacheVersion();\n return useAsyncQuery(\n () => (id ? client.get(id) : Promise.resolve(null)),\n [client, id, v],\n { ...options, enabled: options.enabled ?? Boolean(id) },\n );\n}\n\nexport function useDownloadInvoice() {\n const client = useInvoiceClient();\n return async (id: string) => {\n const url = await client.getDownloadUrl(id);\n if (typeof window !== 'undefined') window.open(url, '_blank');\n return url;\n };\n}\n\nfunction useAsyncQuery<T>(\n load: () => Promise<T | null>,\n deps: readonly unknown[],\n options: QueryOptions<T>,\n): QueryState<T> {\n const enabled = options.enabled ?? true;\n const [data, setData] = useState<T | null>(options.initialData ?? null);\n const [error, setError] = useState<unknown>(null);\n const [isLoading, setIsLoading] = useState(enabled && options.initialData === undefined);\n const [isFetching, setIsFetching] = useState(false);\n const loadRef = useRef(load);\n loadRef.current = load;\n\n const refetch = useCallback(async () => {\n if (!enabled) return data;\n setIsFetching(true);\n setError(null);\n try {\n const next = await loadRef.current();\n setData(next);\n return next;\n } catch (err) {\n setError(err);\n throw err;\n } finally {\n setIsFetching(false);\n setIsLoading(false);\n }\n }, [data, enabled]);\n\n useEffect(() => {\n if (!enabled) {\n setIsLoading(false);\n return;\n }\n let cancelled = false;\n setIsFetching(true);\n setError(null);\n load()\n .then((next) => {\n if (!cancelled) setData(next);\n })\n .catch((err) => {\n if (!cancelled) setError(err);\n })\n .finally(() => {\n if (!cancelled) {\n setIsLoading(false);\n setIsFetching(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, ...deps]);\n\n return { data, error, isLoading, isFetching, refetch };\n}\n\nfunction useStableValue<T>(value: T): T {\n const key = JSON.stringify(value);\n return useMemo(() => value, [key]);\n}\n"],"mappings":"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","names":["import_errors","value","import_react"]}
1
+ {"version":3,"sources":["../src/index.tsx","../src/errors.ts","../src/client.ts","../src/provider.tsx","../src/hooks.ts"],"sourcesContent":["export { InvoicesClient, createInvoicesClient } from './client';\nexport { ThrottleInvoicesError, ThrottleError, isThrottleError } from './errors';\nexport { InvoiceProvider, useInvoiceClient, useInvoiceInvalidation } from './provider';\nexport { useInvoices, useInvoice, useDownloadInvoice } from './hooks';\nexport type * from './types';\n// Money helpers (MC2-R34): amounts are integer minor units of their `currency`.\nexport * as money from '@usethrottle/money';\nexport {\n formatMoney,\n currencySymbol,\n minorUnitsOf,\n toDecimalString,\n toMinor,\n parseMoney,\n moneyValue,\n CURRENCIES,\n isKnownCurrency,\n UnknownCurrencyError,\n} from '@usethrottle/money';\nexport type { CurrencyCode, MoneyValue } from '@usethrottle/money';\n","import { ThrottleError, type ThrottleErrorArgs } from '@usethrottle/errors';\nexport class ThrottleInvoicesError extends ThrottleError {\n constructor(args: { code: string; message: string; statusCode: number; details?: unknown; responseHeaders?: ThrottleErrorArgs['responseHeaders'] }) {\n super(args); this.name = 'ThrottleInvoicesError';\n }\n}\nexport { ThrottleError, isThrottleError } from '@usethrottle/errors';\n","import { ThrottleInvoicesError } from './errors';\nimport type { Invoice, InvoicesClientContract, InvoicesClientOptions, ListInvoicesParams, PaginatedInvoices, ThrottleEnvelope } from './types';\n\nexport class InvoicesClient implements InvoicesClientContract {\n private readonly apiKey?: string; private readonly baseUrl: string;\n private readonly fetchImpl: typeof globalThis.fetch; private readonly fetcher?: InvoicesClientOptions['fetcher']; private readonly timeoutMs: number;\n constructor(opts: InvoicesClientOptions = {}) {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n if (!fetchImpl) throw new Error('InvoicesClient: fetch is required');\n if (!opts.apiKey && !opts.fetcher) throw new Error('InvoicesClient: apiKey or fetcher is required');\n this.apiKey = opts.apiKey; this.baseUrl = (opts.baseUrl ?? 'https://api.usethrottle.dev').replace(/\\/+$/, '');\n this.fetchImpl = fetchImpl; this.fetcher = opts.fetcher; this.timeoutMs = opts.timeoutMs ?? 30_000;\n }\n private async requestEnvelope<T>(method: string, path: string): Promise<ThrottleEnvelope<T>> {\n const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n try {\n const init: RequestInit = { method, headers: { 'content-type': 'application/json', ...(this.apiKey ? { 'x-api-key': this.apiKey } : {}) }, signal: ctrl.signal };\n const res = this.fetcher ? await this.fetcher(path, init) : await this.fetchImpl(`${this.baseUrl}${path}`, init);\n const json = (await res.json().catch(() => ({}))) as ThrottleEnvelope<T>;\n if (!res.ok) throw new ThrottleInvoicesError({ code: json.error?.code ?? 'unknown_error', message: json.error?.message ?? `HTTP ${res.status}`, statusCode: res.status, details: json.error?.details, responseHeaders: res.headers });\n return json;\n } finally { clearTimeout(timer); }\n }\n async list(params: ListInvoicesParams = {}): Promise<PaginatedInvoices> {\n const q = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) if (v !== undefined && v !== null && v !== '') q.set(k, String(v));\n const suffix = q.toString() ? `?${q}` : '';\n const env = await this.requestEnvelope<Invoice[]>('GET', `/api/v1/invoices${suffix}`);\n return { data: env.data ?? [], pagination: env.meta?.pagination ? { cursor: env.meta.pagination.cursor ?? null, hasMore: Boolean(env.meta.pagination.hasMore) } : undefined };\n }\n async get(id: string): Promise<Invoice> {\n const env = await this.requestEnvelope<Invoice>('GET', `/api/v1/invoices/${encodeURIComponent(id)}`);\n return env.data as Invoice;\n }\n async getDownloadUrl(id: string): Promise<string> {\n const env = await this.requestEnvelope<{ url: string }>('GET', `/api/v1/invoices/${encodeURIComponent(id)}/download?format=json`);\n return (env.data as { url: string }).url;\n }\n}\nexport function createInvoicesClient(opts: InvoicesClientOptions): InvoicesClient { return new InvoicesClient(opts); }\n","import { createContext, useCallback, useContext, useMemo, useReducer, type ReactNode } from 'react';\nimport { InvoicesClient } from './client';\nimport type {\n InvoiceFetcher,\n InvoicesProviderProps,\n InvoicesClientContract,\n} from './types';\n\ninterface InvoiceContextValue {\n client: InvoicesClientContract;\n cacheVersion: number;\n invalidate: () => void;\n}\n\nconst InvoiceContext = createContext<InvoiceContextValue | null>(null);\n\nexport function InvoiceProvider({\n children,\n client,\n fetcher,\n baseUrl,\n timeoutMs,\n}: InvoicesProviderProps) {\n const [cacheVersion, invalidate] = useReducer((value: number) => value + 1, 0);\n const resolvedClient = useMemo(() => {\n if (client) return client;\n return new InvoicesClient({\n fetcher: fetcher ?? createInvoiceFetcher('/api/throttle'),\n baseUrl,\n timeoutMs,\n });\n }, [baseUrl, client, fetcher, timeoutMs]);\n\n const value = useMemo(\n () => ({ client: resolvedClient, cacheVersion, invalidate }),\n [cacheVersion, resolvedClient],\n );\n\n return <InvoiceContext.Provider value={value}>{children}</InvoiceContext.Provider>;\n}\n\nexport function useInvoiceClient(): InvoicesClientContract {\n return useInvoiceContext().client;\n}\n\nexport function useInvoiceInvalidation(): () => void {\n return useInvoiceContext().invalidate;\n}\n\nexport function useInvoiceCacheVersion(): number {\n return useInvoiceContext().cacheVersion;\n}\n\nexport function createInvoiceFetcher(basePath: string): InvoiceFetcher {\n const cleanBase = basePath.replace(/\\/+$/, '');\n return (path, init) => {\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return fetch(`${cleanBase}${cleanPath}`, init);\n };\n}\n\nexport function useInvoiceContext(): InvoiceContextValue {\n const ctx = useContext(InvoiceContext);\n if (!ctx) {\n throw new Error(\n '@usethrottle/invoices hooks must be used inside <InvoiceProvider />',\n );\n }\n return ctx;\n}\n\nexport function InvoiceProviderWithClient({\n children,\n client,\n}: {\n children: ReactNode;\n client: InvoicesClientContract;\n}) {\n const [cacheVersion, invalidate] = useReducer((value: number) => value + 1, 0);\n const value = useMemo(() => ({ client, cacheVersion, invalidate }), [cacheVersion, client]);\n return <InvoiceContext.Provider value={value}>{children}</InvoiceContext.Provider>;\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n useInvoiceCacheVersion,\n useInvoiceClient,\n} from './provider';\nimport type {\n Invoice,\n ListInvoicesParams,\n PaginatedInvoices,\n QueryOptions,\n QueryState,\n} from './types';\n\nexport function useInvoices(\n params: ListInvoicesParams = {},\n options: QueryOptions<PaginatedInvoices> = {},\n): QueryState<PaginatedInvoices> {\n const client = useInvoiceClient();\n const v = useInvoiceCacheVersion();\n const p = useStableValue(params);\n return useAsyncQuery(() => client.list(p), [client, p, v], options);\n}\n\nexport function useInvoice(\n id: string | null | undefined,\n options: QueryOptions<Invoice> = {},\n): QueryState<Invoice> {\n const client = useInvoiceClient();\n const v = useInvoiceCacheVersion();\n return useAsyncQuery(\n () => (id ? client.get(id) : Promise.resolve(null)),\n [client, id, v],\n { ...options, enabled: options.enabled ?? Boolean(id) },\n );\n}\n\nexport function useDownloadInvoice() {\n const client = useInvoiceClient();\n return async (id: string) => {\n const url = await client.getDownloadUrl(id);\n if (typeof window !== 'undefined') window.open(url, '_blank');\n return url;\n };\n}\n\nfunction useAsyncQuery<T>(\n load: () => Promise<T | null>,\n deps: readonly unknown[],\n options: QueryOptions<T>,\n): QueryState<T> {\n const enabled = options.enabled ?? true;\n const [data, setData] = useState<T | null>(options.initialData ?? null);\n const [error, setError] = useState<unknown>(null);\n const [isLoading, setIsLoading] = useState(enabled && options.initialData === undefined);\n const [isFetching, setIsFetching] = useState(false);\n const loadRef = useRef(load);\n loadRef.current = load;\n\n const refetch = useCallback(async () => {\n if (!enabled) return data;\n setIsFetching(true);\n setError(null);\n try {\n const next = await loadRef.current();\n setData(next);\n return next;\n } catch (err) {\n setError(err);\n throw err;\n } finally {\n setIsFetching(false);\n setIsLoading(false);\n }\n }, [data, enabled]);\n\n useEffect(() => {\n if (!enabled) {\n setIsLoading(false);\n return;\n }\n let cancelled = false;\n setIsFetching(true);\n setError(null);\n load()\n .then((next) => {\n if (!cancelled) setData(next);\n })\n .catch((err) => {\n if (!cancelled) setError(err);\n })\n .finally(() => {\n if (!cancelled) {\n setIsLoading(false);\n setIsFetching(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, ...deps]);\n\n return { data, error, isLoading, isFetching, refetch };\n}\n\nfunction useStableValue<T>(value: T): T {\n const key = JSON.stringify(value);\n return useMemo(() => value, [key]);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAAsD;AAMtD,IAAAA,iBAA+C;AALxC,IAAM,wBAAN,cAAoC,4BAAc;AAAA,EACvD,YAAY,MAAwI;AAClJ,UAAM,IAAI;AAAG,SAAK,OAAO;AAAA,EAC3B;AACF;;;ACFO,IAAM,iBAAN,MAAuD;AAAA,EAC3C;AAAA,EAAkC;AAAA,EAClC;AAAA,EAAqD;AAAA,EAA6D;AAAA,EACnI,YAAY,OAA8B,CAAC,GAAG;AAC5C,UAAM,YAAY,KAAK,SAAS,WAAW;AAC3C,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,mCAAmC;AACnE,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,+CAA+C;AAClG,SAAK,SAAS,KAAK;AAAQ,SAAK,WAAW,KAAK,WAAW,+BAA+B,QAAQ,QAAQ,EAAE;AAC5G,SAAK,YAAY;AAAW,SAAK,UAAU,KAAK;AAAS,SAAK,YAAY,KAAK,aAAa;AAAA,EAC9F;AAAA,EACA,MAAc,gBAAmB,QAAgB,MAA4C;AAC3F,UAAM,OAAO,IAAI,gBAAgB;AAAG,UAAM,QAAQ,WAAW,MAAM,KAAK,MAAM,GAAG,KAAK,SAAS;AAC/F,QAAI;AACF,YAAM,OAAoB,EAAE,QAAQ,SAAS,EAAE,gBAAgB,oBAAoB,GAAI,KAAK,SAAS,EAAE,aAAa,KAAK,OAAO,IAAI,CAAC,EAAG,GAAG,QAAQ,KAAK,OAAO;AAC/J,YAAM,MAAM,KAAK,UAAU,MAAM,KAAK,QAAQ,MAAM,IAAI,IAAI,MAAM,KAAK,UAAU,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI,IAAI;AAC/G,YAAM,OAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC/C,UAAI,CAAC,IAAI,GAAI,OAAM,IAAI,sBAAsB,EAAE,MAAM,KAAK,OAAO,QAAQ,iBAAiB,SAAS,KAAK,OAAO,WAAW,QAAQ,IAAI,MAAM,IAAI,YAAY,IAAI,QAAQ,SAAS,KAAK,OAAO,SAAS,iBAAiB,IAAI,QAAQ,CAAC;AACpO,aAAO;AAAA,IACT,UAAE;AAAU,mBAAa,KAAK;AAAA,IAAG;AAAA,EACnC;AAAA,EACA,MAAM,KAAK,SAA6B,CAAC,GAA+B;AACtE,UAAM,IAAI,IAAI,gBAAgB;AAC9B,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,EAAG,KAAI,MAAM,UAAa,MAAM,QAAQ,MAAM,GAAI,GAAE,IAAI,GAAG,OAAO,CAAC,CAAC;AAC9G,UAAM,SAAS,EAAE,SAAS,IAAI,IAAI,CAAC,KAAK;AACxC,UAAM,MAAM,MAAM,KAAK,gBAA2B,OAAO,mBAAmB,MAAM,EAAE;AACpF,WAAO,EAAE,MAAM,IAAI,QAAQ,CAAC,GAAG,YAAY,IAAI,MAAM,aAAa,EAAE,QAAQ,IAAI,KAAK,WAAW,UAAU,MAAM,SAAS,QAAQ,IAAI,KAAK,WAAW,OAAO,EAAE,IAAI,OAAU;AAAA,EAC9K;AAAA,EACA,MAAM,IAAI,IAA8B;AACtC,UAAM,MAAM,MAAM,KAAK,gBAAyB,OAAO,oBAAoB,mBAAmB,EAAE,CAAC,EAAE;AACnG,WAAO,IAAI;AAAA,EACb;AAAA,EACA,MAAM,eAAe,IAA6B;AAChD,UAAM,MAAM,MAAM,KAAK,gBAAiC,OAAO,oBAAoB,mBAAmB,EAAE,CAAC,uBAAuB;AAChI,WAAQ,IAAI,KAAyB;AAAA,EACvC;AACF;AACO,SAAS,qBAAqB,MAA6C;AAAE,SAAO,IAAI,eAAe,IAAI;AAAG;;;ACvCrH,mBAA4F;AAsCnF;AAxBT,IAAM,qBAAiB,4BAA0C,IAAI;AAE9D,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,CAAC,cAAc,UAAU,QAAI,yBAAW,CAACC,WAAkBA,SAAQ,GAAG,CAAC;AAC7E,QAAM,qBAAiB,sBAAQ,MAAM;AACnC,QAAI,OAAQ,QAAO;AACnB,WAAO,IAAI,eAAe;AAAA,MACxB,SAAS,WAAW,qBAAqB,eAAe;AAAA,MACxD;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,SAAS,QAAQ,SAAS,SAAS,CAAC;AAExC,QAAM,YAAQ;AAAA,IACZ,OAAO,EAAE,QAAQ,gBAAgB,cAAc,WAAW;AAAA,IAC1D,CAAC,cAAc,cAAc;AAAA,EAC/B;AAEA,SAAO,4CAAC,eAAe,UAAf,EAAwB,OAAe,UAAS;AAC1D;AAEO,SAAS,mBAA2C;AACzD,SAAO,kBAAkB,EAAE;AAC7B;AAEO,SAAS,yBAAqC;AACnD,SAAO,kBAAkB,EAAE;AAC7B;AAEO,SAAS,yBAAiC;AAC/C,SAAO,kBAAkB,EAAE;AAC7B;AAEO,SAAS,qBAAqB,UAAkC;AACrE,QAAM,YAAY,SAAS,QAAQ,QAAQ,EAAE;AAC7C,SAAO,CAAC,MAAM,SAAS;AACrB,UAAM,YAAY,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI;AACxD,WAAO,MAAM,GAAG,SAAS,GAAG,SAAS,IAAI,IAAI;AAAA,EAC/C;AACF;AAEO,SAAS,oBAAyC;AACvD,QAAM,UAAM,yBAAW,cAAc;AACrC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;ACrEA,IAAAC,gBAAkE;AAa3D,SAAS,YACd,SAA6B,CAAC,GAC9B,UAA2C,CAAC,GACb;AAC/B,QAAM,SAAS,iBAAiB;AAChC,QAAM,IAAI,uBAAuB;AACjC,QAAM,IAAI,eAAe,MAAM;AAC/B,SAAO,cAAc,MAAM,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,GAAG,OAAO;AACpE;AAEO,SAAS,WACd,IACA,UAAiC,CAAC,GACb;AACrB,QAAM,SAAS,iBAAiB;AAChC,QAAM,IAAI,uBAAuB;AACjC,SAAO;AAAA,IACL,MAAO,KAAK,OAAO,IAAI,EAAE,IAAI,QAAQ,QAAQ,IAAI;AAAA,IACjD,CAAC,QAAQ,IAAI,CAAC;AAAA,IACd,EAAE,GAAG,SAAS,SAAS,QAAQ,WAAW,QAAQ,EAAE,EAAE;AAAA,EACxD;AACF;AAEO,SAAS,qBAAqB;AACnC,QAAM,SAAS,iBAAiB;AAChC,SAAO,OAAO,OAAe;AAC3B,UAAM,MAAM,MAAM,OAAO,eAAe,EAAE;AAC1C,QAAI,OAAO,WAAW,YAAa,QAAO,KAAK,KAAK,QAAQ;AAC5D,WAAO;AAAA,EACT;AACF;AAEA,SAAS,cACP,MACA,MACA,SACe;AACf,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,CAAC,MAAM,OAAO,QAAI,wBAAmB,QAAQ,eAAe,IAAI;AACtE,QAAM,CAAC,OAAO,QAAQ,QAAI,wBAAkB,IAAI;AAChD,QAAM,CAAC,WAAW,YAAY,QAAI,wBAAS,WAAW,QAAQ,gBAAgB,MAAS;AACvF,QAAM,CAAC,YAAY,aAAa,QAAI,wBAAS,KAAK;AAClD,QAAM,cAAU,sBAAO,IAAI;AAC3B,UAAQ,UAAU;AAElB,QAAM,cAAU,2BAAY,YAAY;AACtC,QAAI,CAAC,QAAS,QAAO;AACrB,kBAAc,IAAI;AAClB,aAAS,IAAI;AACb,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,QAAQ;AACnC,cAAQ,IAAI;AACZ,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,eAAS,GAAG;AACZ,YAAM;AAAA,IACR,UAAE;AACA,oBAAc,KAAK;AACnB,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,MAAM,OAAO,CAAC;AAElB,+BAAU,MAAM;AACd,QAAI,CAAC,SAAS;AACZ,mBAAa,KAAK;AAClB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,kBAAc,IAAI;AAClB,aAAS,IAAI;AACb,SAAK,EACF,KAAK,CAAC,SAAS;AACd,UAAI,CAAC,UAAW,SAAQ,IAAI;AAAA,IAC9B,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,UAAI,CAAC,UAAW,UAAS,GAAG;AAAA,IAC9B,CAAC,EACA,QAAQ,MAAM;AACb,UAAI,CAAC,WAAW;AACd,qBAAa,KAAK;AAClB,sBAAc,KAAK;AAAA,MACrB;AAAA,IACF,CAAC;AAEH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EAEF,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;AAErB,SAAO,EAAE,MAAM,OAAO,WAAW,YAAY,QAAQ;AACvD;AAEA,SAAS,eAAkB,OAAa;AACtC,QAAM,MAAM,KAAK,UAAU,KAAK;AAChC,aAAO,uBAAQ,MAAM,OAAO,CAAC,GAAG,CAAC;AACnC;;;AJvGA,YAAuB;AACvB,mBAWO;","names":["import_errors","value","import_react"]}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,8 @@
1
1
  import { ReactNode } from 'react';
2
+ import { CurrencyCode } from '@usethrottle/money';
3
+ import * as money from '@usethrottle/money';
4
+ export { money };
5
+ export { CURRENCIES, CurrencyCode, MoneyValue, UnknownCurrencyError, currencySymbol, formatMoney, isKnownCurrency, minorUnitsOf, moneyValue, parseMoney, toDecimalString, toMinor } from '@usethrottle/money';
2
6
  import { ThrottleError, ThrottleErrorArgs } from '@usethrottle/errors';
3
7
  export { ThrottleError, isThrottleError } from '@usethrottle/errors';
4
8
  import * as react_jsx_runtime from 'react/jsx-runtime';
@@ -10,7 +14,7 @@ interface Invoice {
10
14
  number: string;
11
15
  status: InvoiceStatus;
12
16
  sourceType?: InvoiceSourceType;
13
- currency: string;
17
+ currency: CurrencyCode;
14
18
  subtotal?: number;
15
19
  taxTotal?: number;
16
20
  shippingTotal?: number;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  import { ReactNode } from 'react';
2
+ import { CurrencyCode } from '@usethrottle/money';
3
+ import * as money from '@usethrottle/money';
4
+ export { money };
5
+ export { CURRENCIES, CurrencyCode, MoneyValue, UnknownCurrencyError, currencySymbol, formatMoney, isKnownCurrency, minorUnitsOf, moneyValue, parseMoney, toDecimalString, toMinor } from '@usethrottle/money';
2
6
  import { ThrottleError, ThrottleErrorArgs } from '@usethrottle/errors';
3
7
  export { ThrottleError, isThrottleError } from '@usethrottle/errors';
4
8
  import * as react_jsx_runtime from 'react/jsx-runtime';
@@ -10,7 +14,7 @@ interface Invoice {
10
14
  number: string;
11
15
  status: InvoiceStatus;
12
16
  sourceType?: InvoiceSourceType;
13
- currency: string;
17
+ currency: CurrencyCode;
14
18
  subtotal?: number;
15
19
  taxTotal?: number;
16
20
  shippingTotal?: number;
package/dist/index.js CHANGED
@@ -187,13 +187,39 @@ function useStableValue(value) {
187
187
  const key = JSON.stringify(value);
188
188
  return useMemo2(() => value, [key]);
189
189
  }
190
+
191
+ // src/index.tsx
192
+ import * as money from "@usethrottle/money";
193
+ import {
194
+ formatMoney,
195
+ currencySymbol,
196
+ minorUnitsOf,
197
+ toDecimalString,
198
+ toMinor,
199
+ parseMoney,
200
+ moneyValue,
201
+ CURRENCIES,
202
+ isKnownCurrency,
203
+ UnknownCurrencyError
204
+ } from "@usethrottle/money";
190
205
  export {
206
+ CURRENCIES,
191
207
  InvoiceProvider,
192
208
  InvoicesClient,
193
209
  ThrottleError2 as ThrottleError,
194
210
  ThrottleInvoicesError,
211
+ UnknownCurrencyError,
195
212
  createInvoicesClient,
213
+ currencySymbol,
214
+ formatMoney,
215
+ isKnownCurrency,
196
216
  isThrottleError,
217
+ minorUnitsOf,
218
+ money,
219
+ moneyValue,
220
+ parseMoney,
221
+ toDecimalString,
222
+ toMinor,
197
223
  useDownloadInvoice,
198
224
  useInvoice,
199
225
  useInvoiceClient,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/provider.tsx","../src/hooks.ts"],"sourcesContent":["import { ThrottleError, type ThrottleErrorArgs } from '@usethrottle/errors';\nexport class ThrottleInvoicesError extends ThrottleError {\n constructor(args: { code: string; message: string; statusCode: number; details?: unknown; responseHeaders?: ThrottleErrorArgs['responseHeaders'] }) {\n super(args); this.name = 'ThrottleInvoicesError';\n }\n}\nexport { ThrottleError, isThrottleError } from '@usethrottle/errors';\n","import { ThrottleInvoicesError } from './errors';\nimport type { Invoice, InvoicesClientContract, InvoicesClientOptions, ListInvoicesParams, PaginatedInvoices, ThrottleEnvelope } from './types';\n\nexport class InvoicesClient implements InvoicesClientContract {\n private readonly apiKey?: string; private readonly baseUrl: string;\n private readonly fetchImpl: typeof globalThis.fetch; private readonly fetcher?: InvoicesClientOptions['fetcher']; private readonly timeoutMs: number;\n constructor(opts: InvoicesClientOptions = {}) {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n if (!fetchImpl) throw new Error('InvoicesClient: fetch is required');\n if (!opts.apiKey && !opts.fetcher) throw new Error('InvoicesClient: apiKey or fetcher is required');\n this.apiKey = opts.apiKey; this.baseUrl = (opts.baseUrl ?? 'https://api.usethrottle.dev').replace(/\\/+$/, '');\n this.fetchImpl = fetchImpl; this.fetcher = opts.fetcher; this.timeoutMs = opts.timeoutMs ?? 30_000;\n }\n private async requestEnvelope<T>(method: string, path: string): Promise<ThrottleEnvelope<T>> {\n const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n try {\n const init: RequestInit = { method, headers: { 'content-type': 'application/json', ...(this.apiKey ? { 'x-api-key': this.apiKey } : {}) }, signal: ctrl.signal };\n const res = this.fetcher ? await this.fetcher(path, init) : await this.fetchImpl(`${this.baseUrl}${path}`, init);\n const json = (await res.json().catch(() => ({}))) as ThrottleEnvelope<T>;\n if (!res.ok) throw new ThrottleInvoicesError({ code: json.error?.code ?? 'unknown_error', message: json.error?.message ?? `HTTP ${res.status}`, statusCode: res.status, details: json.error?.details, responseHeaders: res.headers });\n return json;\n } finally { clearTimeout(timer); }\n }\n async list(params: ListInvoicesParams = {}): Promise<PaginatedInvoices> {\n const q = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) if (v !== undefined && v !== null && v !== '') q.set(k, String(v));\n const suffix = q.toString() ? `?${q}` : '';\n const env = await this.requestEnvelope<Invoice[]>('GET', `/api/v1/invoices${suffix}`);\n return { data: env.data ?? [], pagination: env.meta?.pagination ? { cursor: env.meta.pagination.cursor ?? null, hasMore: Boolean(env.meta.pagination.hasMore) } : undefined };\n }\n async get(id: string): Promise<Invoice> {\n const env = await this.requestEnvelope<Invoice>('GET', `/api/v1/invoices/${encodeURIComponent(id)}`);\n return env.data as Invoice;\n }\n async getDownloadUrl(id: string): Promise<string> {\n const env = await this.requestEnvelope<{ url: string }>('GET', `/api/v1/invoices/${encodeURIComponent(id)}/download?format=json`);\n return (env.data as { url: string }).url;\n }\n}\nexport function createInvoicesClient(opts: InvoicesClientOptions): InvoicesClient { return new InvoicesClient(opts); }\n","import { createContext, useCallback, useContext, useMemo, useReducer, type ReactNode } from 'react';\nimport { InvoicesClient } from './client';\nimport type {\n InvoiceFetcher,\n InvoicesProviderProps,\n InvoicesClientContract,\n} from './types';\n\ninterface InvoiceContextValue {\n client: InvoicesClientContract;\n cacheVersion: number;\n invalidate: () => void;\n}\n\nconst InvoiceContext = createContext<InvoiceContextValue | null>(null);\n\nexport function InvoiceProvider({\n children,\n client,\n fetcher,\n baseUrl,\n timeoutMs,\n}: InvoicesProviderProps) {\n const [cacheVersion, invalidate] = useReducer((value: number) => value + 1, 0);\n const resolvedClient = useMemo(() => {\n if (client) return client;\n return new InvoicesClient({\n fetcher: fetcher ?? createInvoiceFetcher('/api/throttle'),\n baseUrl,\n timeoutMs,\n });\n }, [baseUrl, client, fetcher, timeoutMs]);\n\n const value = useMemo(\n () => ({ client: resolvedClient, cacheVersion, invalidate }),\n [cacheVersion, resolvedClient],\n );\n\n return <InvoiceContext.Provider value={value}>{children}</InvoiceContext.Provider>;\n}\n\nexport function useInvoiceClient(): InvoicesClientContract {\n return useInvoiceContext().client;\n}\n\nexport function useInvoiceInvalidation(): () => void {\n return useInvoiceContext().invalidate;\n}\n\nexport function useInvoiceCacheVersion(): number {\n return useInvoiceContext().cacheVersion;\n}\n\nexport function createInvoiceFetcher(basePath: string): InvoiceFetcher {\n const cleanBase = basePath.replace(/\\/+$/, '');\n return (path, init) => {\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return fetch(`${cleanBase}${cleanPath}`, init);\n };\n}\n\nexport function useInvoiceContext(): InvoiceContextValue {\n const ctx = useContext(InvoiceContext);\n if (!ctx) {\n throw new Error(\n '@usethrottle/invoices hooks must be used inside <InvoiceProvider />',\n );\n }\n return ctx;\n}\n\nexport function InvoiceProviderWithClient({\n children,\n client,\n}: {\n children: ReactNode;\n client: InvoicesClientContract;\n}) {\n const [cacheVersion, invalidate] = useReducer((value: number) => value + 1, 0);\n const value = useMemo(() => ({ client, cacheVersion, invalidate }), [cacheVersion, client]);\n return <InvoiceContext.Provider value={value}>{children}</InvoiceContext.Provider>;\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n useInvoiceCacheVersion,\n useInvoiceClient,\n} from './provider';\nimport type {\n Invoice,\n ListInvoicesParams,\n PaginatedInvoices,\n QueryOptions,\n QueryState,\n} from './types';\n\nexport function useInvoices(\n params: ListInvoicesParams = {},\n options: QueryOptions<PaginatedInvoices> = {},\n): QueryState<PaginatedInvoices> {\n const client = useInvoiceClient();\n const v = useInvoiceCacheVersion();\n const p = useStableValue(params);\n return useAsyncQuery(() => client.list(p), [client, p, v], options);\n}\n\nexport function useInvoice(\n id: string | null | undefined,\n options: QueryOptions<Invoice> = {},\n): QueryState<Invoice> {\n const client = useInvoiceClient();\n const v = useInvoiceCacheVersion();\n return useAsyncQuery(\n () => (id ? client.get(id) : Promise.resolve(null)),\n [client, id, v],\n { ...options, enabled: options.enabled ?? Boolean(id) },\n );\n}\n\nexport function useDownloadInvoice() {\n const client = useInvoiceClient();\n return async (id: string) => {\n const url = await client.getDownloadUrl(id);\n if (typeof window !== 'undefined') window.open(url, '_blank');\n return url;\n };\n}\n\nfunction useAsyncQuery<T>(\n load: () => Promise<T | null>,\n deps: readonly unknown[],\n options: QueryOptions<T>,\n): QueryState<T> {\n const enabled = options.enabled ?? true;\n const [data, setData] = useState<T | null>(options.initialData ?? null);\n const [error, setError] = useState<unknown>(null);\n const [isLoading, setIsLoading] = useState(enabled && options.initialData === undefined);\n const [isFetching, setIsFetching] = useState(false);\n const loadRef = useRef(load);\n loadRef.current = load;\n\n const refetch = useCallback(async () => {\n if (!enabled) return data;\n setIsFetching(true);\n setError(null);\n try {\n const next = await loadRef.current();\n setData(next);\n return next;\n } catch (err) {\n setError(err);\n throw err;\n } finally {\n setIsFetching(false);\n setIsLoading(false);\n }\n }, [data, enabled]);\n\n useEffect(() => {\n if (!enabled) {\n setIsLoading(false);\n return;\n }\n let cancelled = false;\n setIsFetching(true);\n setError(null);\n load()\n .then((next) => {\n if (!cancelled) setData(next);\n })\n .catch((err) => {\n if (!cancelled) setError(err);\n })\n .finally(() => {\n if (!cancelled) {\n setIsLoading(false);\n setIsFetching(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, ...deps]);\n\n return { data, error, isLoading, isFetching, refetch };\n}\n\nfunction useStableValue<T>(value: T): T {\n const key = JSON.stringify(value);\n return useMemo(() => value, [key]);\n}\n"],"mappings":";AAAA,SAAS,qBAA6C;AAMtD,SAAS,iBAAAA,gBAAe,uBAAuB;AALxC,IAAM,wBAAN,cAAoC,cAAc;AAAA,EACvD,YAAY,MAAwI;AAClJ,UAAM,IAAI;AAAG,SAAK,OAAO;AAAA,EAC3B;AACF;;;ACFO,IAAM,iBAAN,MAAuD;AAAA,EAC3C;AAAA,EAAkC;AAAA,EAClC;AAAA,EAAqD;AAAA,EAA6D;AAAA,EACnI,YAAY,OAA8B,CAAC,GAAG;AAC5C,UAAM,YAAY,KAAK,SAAS,WAAW;AAC3C,QAAI,CAAC,UAAW,OAAM,IAAI,MAAM,mCAAmC;AACnE,QAAI,CAAC,KAAK,UAAU,CAAC,KAAK,QAAS,OAAM,IAAI,MAAM,+CAA+C;AAClG,SAAK,SAAS,KAAK;AAAQ,SAAK,WAAW,KAAK,WAAW,+BAA+B,QAAQ,QAAQ,EAAE;AAC5G,SAAK,YAAY;AAAW,SAAK,UAAU,KAAK;AAAS,SAAK,YAAY,KAAK,aAAa;AAAA,EAC9F;AAAA,EACA,MAAc,gBAAmB,QAAgB,MAA4C;AAC3F,UAAM,OAAO,IAAI,gBAAgB;AAAG,UAAM,QAAQ,WAAW,MAAM,KAAK,MAAM,GAAG,KAAK,SAAS;AAC/F,QAAI;AACF,YAAM,OAAoB,EAAE,QAAQ,SAAS,EAAE,gBAAgB,oBAAoB,GAAI,KAAK,SAAS,EAAE,aAAa,KAAK,OAAO,IAAI,CAAC,EAAG,GAAG,QAAQ,KAAK,OAAO;AAC/J,YAAM,MAAM,KAAK,UAAU,MAAM,KAAK,QAAQ,MAAM,IAAI,IAAI,MAAM,KAAK,UAAU,GAAG,KAAK,OAAO,GAAG,IAAI,IAAI,IAAI;AAC/G,YAAM,OAAQ,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC/C,UAAI,CAAC,IAAI,GAAI,OAAM,IAAI,sBAAsB,EAAE,MAAM,KAAK,OAAO,QAAQ,iBAAiB,SAAS,KAAK,OAAO,WAAW,QAAQ,IAAI,MAAM,IAAI,YAAY,IAAI,QAAQ,SAAS,KAAK,OAAO,SAAS,iBAAiB,IAAI,QAAQ,CAAC;AACpO,aAAO;AAAA,IACT,UAAE;AAAU,mBAAa,KAAK;AAAA,IAAG;AAAA,EACnC;AAAA,EACA,MAAM,KAAK,SAA6B,CAAC,GAA+B;AACtE,UAAM,IAAI,IAAI,gBAAgB;AAC9B,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,MAAM,EAAG,KAAI,MAAM,UAAa,MAAM,QAAQ,MAAM,GAAI,GAAE,IAAI,GAAG,OAAO,CAAC,CAAC;AAC9G,UAAM,SAAS,EAAE,SAAS,IAAI,IAAI,CAAC,KAAK;AACxC,UAAM,MAAM,MAAM,KAAK,gBAA2B,OAAO,mBAAmB,MAAM,EAAE;AACpF,WAAO,EAAE,MAAM,IAAI,QAAQ,CAAC,GAAG,YAAY,IAAI,MAAM,aAAa,EAAE,QAAQ,IAAI,KAAK,WAAW,UAAU,MAAM,SAAS,QAAQ,IAAI,KAAK,WAAW,OAAO,EAAE,IAAI,OAAU;AAAA,EAC9K;AAAA,EACA,MAAM,IAAI,IAA8B;AACtC,UAAM,MAAM,MAAM,KAAK,gBAAyB,OAAO,oBAAoB,mBAAmB,EAAE,CAAC,EAAE;AACnG,WAAO,IAAI;AAAA,EACb;AAAA,EACA,MAAM,eAAe,IAA6B;AAChD,UAAM,MAAM,MAAM,KAAK,gBAAiC,OAAO,oBAAoB,mBAAmB,EAAE,CAAC,uBAAuB;AAChI,WAAQ,IAAI,KAAyB;AAAA,EACvC;AACF;AACO,SAAS,qBAAqB,MAA6C;AAAE,SAAO,IAAI,eAAe,IAAI;AAAG;;;ACvCrH,SAAS,eAA4B,YAAY,SAAS,kBAAkC;AAsCnF;AAxBT,IAAM,iBAAiB,cAA0C,IAAI;AAE9D,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAA0B;AACxB,QAAM,CAAC,cAAc,UAAU,IAAI,WAAW,CAACC,WAAkBA,SAAQ,GAAG,CAAC;AAC7E,QAAM,iBAAiB,QAAQ,MAAM;AACnC,QAAI,OAAQ,QAAO;AACnB,WAAO,IAAI,eAAe;AAAA,MACxB,SAAS,WAAW,qBAAqB,eAAe;AAAA,MACxD;AAAA,MACA;AAAA,IACF,CAAC;AAAA,EACH,GAAG,CAAC,SAAS,QAAQ,SAAS,SAAS,CAAC;AAExC,QAAM,QAAQ;AAAA,IACZ,OAAO,EAAE,QAAQ,gBAAgB,cAAc,WAAW;AAAA,IAC1D,CAAC,cAAc,cAAc;AAAA,EAC/B;AAEA,SAAO,oBAAC,eAAe,UAAf,EAAwB,OAAe,UAAS;AAC1D;AAEO,SAAS,mBAA2C;AACzD,SAAO,kBAAkB,EAAE;AAC7B;AAEO,SAAS,yBAAqC;AACnD,SAAO,kBAAkB,EAAE;AAC7B;AAEO,SAAS,yBAAiC;AAC/C,SAAO,kBAAkB,EAAE;AAC7B;AAEO,SAAS,qBAAqB,UAAkC;AACrE,QAAM,YAAY,SAAS,QAAQ,QAAQ,EAAE;AAC7C,SAAO,CAAC,MAAM,SAAS;AACrB,UAAM,YAAY,KAAK,WAAW,GAAG,IAAI,OAAO,IAAI,IAAI;AACxD,WAAO,MAAM,GAAG,SAAS,GAAG,SAAS,IAAI,IAAI;AAAA,EAC/C;AACF;AAEO,SAAS,oBAAyC;AACvD,QAAM,MAAM,WAAW,cAAc;AACrC,MAAI,CAAC,KAAK;AACR,UAAM,IAAI;AAAA,MACR;AAAA,IACF;AAAA,EACF;AACA,SAAO;AACT;;;ACrEA,SAAS,eAAAC,cAAa,WAAW,WAAAC,UAAS,QAAQ,gBAAgB;AAa3D,SAAS,YACd,SAA6B,CAAC,GAC9B,UAA2C,CAAC,GACb;AAC/B,QAAM,SAAS,iBAAiB;AAChC,QAAM,IAAI,uBAAuB;AACjC,QAAM,IAAI,eAAe,MAAM;AAC/B,SAAO,cAAc,MAAM,OAAO,KAAK,CAAC,GAAG,CAAC,QAAQ,GAAG,CAAC,GAAG,OAAO;AACpE;AAEO,SAAS,WACd,IACA,UAAiC,CAAC,GACb;AACrB,QAAM,SAAS,iBAAiB;AAChC,QAAM,IAAI,uBAAuB;AACjC,SAAO;AAAA,IACL,MAAO,KAAK,OAAO,IAAI,EAAE,IAAI,QAAQ,QAAQ,IAAI;AAAA,IACjD,CAAC,QAAQ,IAAI,CAAC;AAAA,IACd,EAAE,GAAG,SAAS,SAAS,QAAQ,WAAW,QAAQ,EAAE,EAAE;AAAA,EACxD;AACF;AAEO,SAAS,qBAAqB;AACnC,QAAM,SAAS,iBAAiB;AAChC,SAAO,OAAO,OAAe;AAC3B,UAAM,MAAM,MAAM,OAAO,eAAe,EAAE;AAC1C,QAAI,OAAO,WAAW,YAAa,QAAO,KAAK,KAAK,QAAQ;AAC5D,WAAO;AAAA,EACT;AACF;AAEA,SAAS,cACP,MACA,MACA,SACe;AACf,QAAM,UAAU,QAAQ,WAAW;AACnC,QAAM,CAAC,MAAM,OAAO,IAAI,SAAmB,QAAQ,eAAe,IAAI;AACtE,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAkB,IAAI;AAChD,QAAM,CAAC,WAAW,YAAY,IAAI,SAAS,WAAW,QAAQ,gBAAgB,MAAS;AACvF,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,KAAK;AAClD,QAAM,UAAU,OAAO,IAAI;AAC3B,UAAQ,UAAU;AAElB,QAAM,UAAUC,aAAY,YAAY;AACtC,QAAI,CAAC,QAAS,QAAO;AACrB,kBAAc,IAAI;AAClB,aAAS,IAAI;AACb,QAAI;AACF,YAAM,OAAO,MAAM,QAAQ,QAAQ;AACnC,cAAQ,IAAI;AACZ,aAAO;AAAA,IACT,SAAS,KAAK;AACZ,eAAS,GAAG;AACZ,YAAM;AAAA,IACR,UAAE;AACA,oBAAc,KAAK;AACnB,mBAAa,KAAK;AAAA,IACpB;AAAA,EACF,GAAG,CAAC,MAAM,OAAO,CAAC;AAElB,YAAU,MAAM;AACd,QAAI,CAAC,SAAS;AACZ,mBAAa,KAAK;AAClB;AAAA,IACF;AACA,QAAI,YAAY;AAChB,kBAAc,IAAI;AAClB,aAAS,IAAI;AACb,SAAK,EACF,KAAK,CAAC,SAAS;AACd,UAAI,CAAC,UAAW,SAAQ,IAAI;AAAA,IAC9B,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,UAAI,CAAC,UAAW,UAAS,GAAG;AAAA,IAC9B,CAAC,EACA,QAAQ,MAAM;AACb,UAAI,CAAC,WAAW;AACd,qBAAa,KAAK;AAClB,sBAAc,KAAK;AAAA,MACrB;AAAA,IACF,CAAC;AAEH,WAAO,MAAM;AACX,kBAAY;AAAA,IACd;AAAA,EAEF,GAAG,CAAC,SAAS,GAAG,IAAI,CAAC;AAErB,SAAO,EAAE,MAAM,OAAO,WAAW,YAAY,QAAQ;AACvD;AAEA,SAAS,eAAkB,OAAa;AACtC,QAAM,MAAM,KAAK,UAAU,KAAK;AAChC,SAAOC,SAAQ,MAAM,OAAO,CAAC,GAAG,CAAC;AACnC;","names":["ThrottleError","value","useCallback","useMemo","useCallback","useMemo"]}
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/provider.tsx","../src/hooks.ts","../src/index.tsx"],"sourcesContent":["import { ThrottleError, type ThrottleErrorArgs } from '@usethrottle/errors';\nexport class ThrottleInvoicesError extends ThrottleError {\n constructor(args: { code: string; message: string; statusCode: number; details?: unknown; responseHeaders?: ThrottleErrorArgs['responseHeaders'] }) {\n super(args); this.name = 'ThrottleInvoicesError';\n }\n}\nexport { ThrottleError, isThrottleError } from '@usethrottle/errors';\n","import { ThrottleInvoicesError } from './errors';\nimport type { Invoice, InvoicesClientContract, InvoicesClientOptions, ListInvoicesParams, PaginatedInvoices, ThrottleEnvelope } from './types';\n\nexport class InvoicesClient implements InvoicesClientContract {\n private readonly apiKey?: string; private readonly baseUrl: string;\n private readonly fetchImpl: typeof globalThis.fetch; private readonly fetcher?: InvoicesClientOptions['fetcher']; private readonly timeoutMs: number;\n constructor(opts: InvoicesClientOptions = {}) {\n const fetchImpl = opts.fetch ?? globalThis.fetch;\n if (!fetchImpl) throw new Error('InvoicesClient: fetch is required');\n if (!opts.apiKey && !opts.fetcher) throw new Error('InvoicesClient: apiKey or fetcher is required');\n this.apiKey = opts.apiKey; this.baseUrl = (opts.baseUrl ?? 'https://api.usethrottle.dev').replace(/\\/+$/, '');\n this.fetchImpl = fetchImpl; this.fetcher = opts.fetcher; this.timeoutMs = opts.timeoutMs ?? 30_000;\n }\n private async requestEnvelope<T>(method: string, path: string): Promise<ThrottleEnvelope<T>> {\n const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);\n try {\n const init: RequestInit = { method, headers: { 'content-type': 'application/json', ...(this.apiKey ? { 'x-api-key': this.apiKey } : {}) }, signal: ctrl.signal };\n const res = this.fetcher ? await this.fetcher(path, init) : await this.fetchImpl(`${this.baseUrl}${path}`, init);\n const json = (await res.json().catch(() => ({}))) as ThrottleEnvelope<T>;\n if (!res.ok) throw new ThrottleInvoicesError({ code: json.error?.code ?? 'unknown_error', message: json.error?.message ?? `HTTP ${res.status}`, statusCode: res.status, details: json.error?.details, responseHeaders: res.headers });\n return json;\n } finally { clearTimeout(timer); }\n }\n async list(params: ListInvoicesParams = {}): Promise<PaginatedInvoices> {\n const q = new URLSearchParams();\n for (const [k, v] of Object.entries(params)) if (v !== undefined && v !== null && v !== '') q.set(k, String(v));\n const suffix = q.toString() ? `?${q}` : '';\n const env = await this.requestEnvelope<Invoice[]>('GET', `/api/v1/invoices${suffix}`);\n return { data: env.data ?? [], pagination: env.meta?.pagination ? { cursor: env.meta.pagination.cursor ?? null, hasMore: Boolean(env.meta.pagination.hasMore) } : undefined };\n }\n async get(id: string): Promise<Invoice> {\n const env = await this.requestEnvelope<Invoice>('GET', `/api/v1/invoices/${encodeURIComponent(id)}`);\n return env.data as Invoice;\n }\n async getDownloadUrl(id: string): Promise<string> {\n const env = await this.requestEnvelope<{ url: string }>('GET', `/api/v1/invoices/${encodeURIComponent(id)}/download?format=json`);\n return (env.data as { url: string }).url;\n }\n}\nexport function createInvoicesClient(opts: InvoicesClientOptions): InvoicesClient { return new InvoicesClient(opts); }\n","import { createContext, useCallback, useContext, useMemo, useReducer, type ReactNode } from 'react';\nimport { InvoicesClient } from './client';\nimport type {\n InvoiceFetcher,\n InvoicesProviderProps,\n InvoicesClientContract,\n} from './types';\n\ninterface InvoiceContextValue {\n client: InvoicesClientContract;\n cacheVersion: number;\n invalidate: () => void;\n}\n\nconst InvoiceContext = createContext<InvoiceContextValue | null>(null);\n\nexport function InvoiceProvider({\n children,\n client,\n fetcher,\n baseUrl,\n timeoutMs,\n}: InvoicesProviderProps) {\n const [cacheVersion, invalidate] = useReducer((value: number) => value + 1, 0);\n const resolvedClient = useMemo(() => {\n if (client) return client;\n return new InvoicesClient({\n fetcher: fetcher ?? createInvoiceFetcher('/api/throttle'),\n baseUrl,\n timeoutMs,\n });\n }, [baseUrl, client, fetcher, timeoutMs]);\n\n const value = useMemo(\n () => ({ client: resolvedClient, cacheVersion, invalidate }),\n [cacheVersion, resolvedClient],\n );\n\n return <InvoiceContext.Provider value={value}>{children}</InvoiceContext.Provider>;\n}\n\nexport function useInvoiceClient(): InvoicesClientContract {\n return useInvoiceContext().client;\n}\n\nexport function useInvoiceInvalidation(): () => void {\n return useInvoiceContext().invalidate;\n}\n\nexport function useInvoiceCacheVersion(): number {\n return useInvoiceContext().cacheVersion;\n}\n\nexport function createInvoiceFetcher(basePath: string): InvoiceFetcher {\n const cleanBase = basePath.replace(/\\/+$/, '');\n return (path, init) => {\n const cleanPath = path.startsWith('/') ? path : `/${path}`;\n return fetch(`${cleanBase}${cleanPath}`, init);\n };\n}\n\nexport function useInvoiceContext(): InvoiceContextValue {\n const ctx = useContext(InvoiceContext);\n if (!ctx) {\n throw new Error(\n '@usethrottle/invoices hooks must be used inside <InvoiceProvider />',\n );\n }\n return ctx;\n}\n\nexport function InvoiceProviderWithClient({\n children,\n client,\n}: {\n children: ReactNode;\n client: InvoicesClientContract;\n}) {\n const [cacheVersion, invalidate] = useReducer((value: number) => value + 1, 0);\n const value = useMemo(() => ({ client, cacheVersion, invalidate }), [cacheVersion, client]);\n return <InvoiceContext.Provider value={value}>{children}</InvoiceContext.Provider>;\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport {\n useInvoiceCacheVersion,\n useInvoiceClient,\n} from './provider';\nimport type {\n Invoice,\n ListInvoicesParams,\n PaginatedInvoices,\n QueryOptions,\n QueryState,\n} from './types';\n\nexport function useInvoices(\n params: ListInvoicesParams = {},\n options: QueryOptions<PaginatedInvoices> = {},\n): QueryState<PaginatedInvoices> {\n const client = useInvoiceClient();\n const v = useInvoiceCacheVersion();\n const p = useStableValue(params);\n return useAsyncQuery(() => client.list(p), [client, p, v], options);\n}\n\nexport function useInvoice(\n id: string | null | undefined,\n options: QueryOptions<Invoice> = {},\n): QueryState<Invoice> {\n const client = useInvoiceClient();\n const v = useInvoiceCacheVersion();\n return useAsyncQuery(\n () => (id ? client.get(id) : Promise.resolve(null)),\n [client, id, v],\n { ...options, enabled: options.enabled ?? Boolean(id) },\n );\n}\n\nexport function useDownloadInvoice() {\n const client = useInvoiceClient();\n return async (id: string) => {\n const url = await client.getDownloadUrl(id);\n if (typeof window !== 'undefined') window.open(url, '_blank');\n return url;\n };\n}\n\nfunction useAsyncQuery<T>(\n load: () => Promise<T | null>,\n deps: readonly unknown[],\n options: QueryOptions<T>,\n): QueryState<T> {\n const enabled = options.enabled ?? true;\n const [data, setData] = useState<T | null>(options.initialData ?? null);\n const [error, setError] = useState<unknown>(null);\n const [isLoading, setIsLoading] = useState(enabled && options.initialData === undefined);\n const [isFetching, setIsFetching] = useState(false);\n const loadRef = useRef(load);\n loadRef.current = load;\n\n const refetch = useCallback(async () => {\n if (!enabled) return data;\n setIsFetching(true);\n setError(null);\n try {\n const next = await loadRef.current();\n setData(next);\n return next;\n } catch (err) {\n setError(err);\n throw err;\n } finally {\n setIsFetching(false);\n setIsLoading(false);\n }\n }, [data, enabled]);\n\n useEffect(() => {\n if (!enabled) {\n setIsLoading(false);\n return;\n }\n let cancelled = false;\n setIsFetching(true);\n setError(null);\n load()\n .then((next) => {\n if (!cancelled) setData(next);\n })\n .catch((err) => {\n if (!cancelled) setError(err);\n })\n .finally(() => {\n if (!cancelled) {\n setIsLoading(false);\n setIsFetching(false);\n }\n });\n\n return () => {\n cancelled = true;\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [enabled, ...deps]);\n\n return { data, error, isLoading, isFetching, refetch };\n}\n\nfunction useStableValue<T>(value: T): T {\n const key = JSON.stringify(value);\n return useMemo(() => value, [key]);\n}\n","export { InvoicesClient, createInvoicesClient } from './client';\nexport { ThrottleInvoicesError, ThrottleError, isThrottleError } from './errors';\nexport { InvoiceProvider, useInvoiceClient, useInvoiceInvalidation } from './provider';\nexport { useInvoices, useInvoice, useDownloadInvoice } from './hooks';\nexport type * from './types';\n// Money helpers (MC2-R34): amounts are integer minor units of their `currency`.\nexport * as money from '@usethrottle/money';\nexport {\n formatMoney,\n currencySymbol,\n minorUnitsOf,\n toDecimalString,\n toMinor,\n parseMoney,\n moneyValue,\n CURRENCIES,\n isKnownCurrency,\n UnknownCurrencyError,\n} from '@usethrottle/money';\nexport type { CurrencyCode, MoneyValue } from '@usethrottle/money';\n"],"mappings":"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","names":["ThrottleError","value","useCallback","useMemo","useCallback","useMemo"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usethrottle/invoices",
3
- "version": "0.1.2",
3
+ "version": "0.2.0",
4
4
  "description": "React hooks + server proxy for Throttle customer invoices.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,7 +23,8 @@
23
23
  "README.md"
24
24
  ],
25
25
  "dependencies": {
26
- "@usethrottle/errors": "^1.1.0"
26
+ "@usethrottle/errors": "^1.1.0",
27
+ "@usethrottle/money": "^1.0.0"
27
28
  },
28
29
  "peerDependencies": {
29
30
  "react": ">=18"