@usethrottle/invoices 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,240 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.tsx
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ InvoiceProvider: () => InvoiceProvider,
24
+ InvoicesClient: () => InvoicesClient,
25
+ ThrottleError: () => import_errors2.ThrottleError,
26
+ ThrottleInvoicesError: () => ThrottleInvoicesError,
27
+ createInvoicesClient: () => createInvoicesClient,
28
+ isThrottleError: () => import_errors2.isThrottleError,
29
+ useDownloadInvoice: () => useDownloadInvoice,
30
+ useInvoice: () => useInvoice,
31
+ useInvoiceClient: () => useInvoiceClient,
32
+ useInvoiceInvalidation: () => useInvoiceInvalidation,
33
+ useInvoices: () => useInvoices
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/errors.ts
38
+ var import_errors = require("@usethrottle/errors");
39
+ var import_errors2 = require("@usethrottle/errors");
40
+ var ThrottleInvoicesError = class extends import_errors.ThrottleError {
41
+ constructor(args) {
42
+ super(args);
43
+ this.name = "ThrottleInvoicesError";
44
+ }
45
+ };
46
+
47
+ // src/client.ts
48
+ var InvoicesClient = class {
49
+ apiKey;
50
+ baseUrl;
51
+ fetchImpl;
52
+ fetcher;
53
+ timeoutMs;
54
+ constructor(opts = {}) {
55
+ const fetchImpl = opts.fetch ?? globalThis.fetch;
56
+ if (!fetchImpl) throw new Error("InvoicesClient: fetch is required");
57
+ if (!opts.apiKey && !opts.fetcher) throw new Error("InvoicesClient: apiKey or fetcher is required");
58
+ this.apiKey = opts.apiKey;
59
+ this.baseUrl = (opts.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
60
+ this.fetchImpl = fetchImpl;
61
+ this.fetcher = opts.fetcher;
62
+ this.timeoutMs = opts.timeoutMs ?? 3e4;
63
+ }
64
+ async requestEnvelope(method, path) {
65
+ const ctrl = new AbortController();
66
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
67
+ try {
68
+ const init = { method, headers: { "content-type": "application/json", ...this.apiKey ? { "x-api-key": this.apiKey } : {} }, signal: ctrl.signal };
69
+ const res = this.fetcher ? await this.fetcher(path, init) : await this.fetchImpl(`${this.baseUrl}${path}`, init);
70
+ const json = await res.json().catch(() => ({}));
71
+ 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 });
72
+ return json;
73
+ } finally {
74
+ clearTimeout(timer);
75
+ }
76
+ }
77
+ async list(params = {}) {
78
+ const q = new URLSearchParams();
79
+ for (const [k, v] of Object.entries(params)) if (v !== void 0 && v !== null && v !== "") q.set(k, String(v));
80
+ const suffix = q.toString() ? `?${q}` : "";
81
+ const env = await this.requestEnvelope("GET", `/api/v1/invoices${suffix}`);
82
+ return { data: env.data ?? [], pagination: env.meta?.pagination ? { cursor: env.meta.pagination.cursor ?? null, hasMore: Boolean(env.meta.pagination.hasMore) } : void 0 };
83
+ }
84
+ async get(id) {
85
+ const env = await this.requestEnvelope("GET", `/api/v1/invoices/${encodeURIComponent(id)}`);
86
+ return env.data;
87
+ }
88
+ async getDownloadUrl(id) {
89
+ const env = await this.requestEnvelope("GET", `/api/v1/invoices/${encodeURIComponent(id)}/download?format=json`);
90
+ return env.data.url;
91
+ }
92
+ };
93
+ function createInvoicesClient(opts) {
94
+ return new InvoicesClient(opts);
95
+ }
96
+
97
+ // src/provider.tsx
98
+ var import_react = require("react");
99
+ var import_jsx_runtime = require("react/jsx-runtime");
100
+ var InvoiceContext = (0, import_react.createContext)(null);
101
+ function InvoiceProvider({
102
+ children,
103
+ client,
104
+ fetcher,
105
+ baseUrl,
106
+ timeoutMs
107
+ }) {
108
+ const [cacheVersion, invalidate] = (0, import_react.useReducer)((value2) => value2 + 1, 0);
109
+ const resolvedClient = (0, import_react.useMemo)(() => {
110
+ if (client) return client;
111
+ return new InvoicesClient({
112
+ fetcher: fetcher ?? createInvoiceFetcher("/api/throttle"),
113
+ baseUrl,
114
+ timeoutMs
115
+ });
116
+ }, [baseUrl, client, fetcher, timeoutMs]);
117
+ const value = (0, import_react.useMemo)(
118
+ () => ({ client: resolvedClient, cacheVersion, invalidate }),
119
+ [cacheVersion, resolvedClient]
120
+ );
121
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(InvoiceContext.Provider, { value, children });
122
+ }
123
+ function useInvoiceClient() {
124
+ return useInvoiceContext().client;
125
+ }
126
+ function useInvoiceInvalidation() {
127
+ return useInvoiceContext().invalidate;
128
+ }
129
+ function useInvoiceCacheVersion() {
130
+ return useInvoiceContext().cacheVersion;
131
+ }
132
+ function createInvoiceFetcher(basePath) {
133
+ const cleanBase = basePath.replace(/\/+$/, "");
134
+ return (path, init) => {
135
+ const cleanPath = path.startsWith("/") ? path : `/${path}`;
136
+ return fetch(`${cleanBase}${cleanPath}`, init);
137
+ };
138
+ }
139
+ function useInvoiceContext() {
140
+ const ctx = (0, import_react.useContext)(InvoiceContext);
141
+ if (!ctx) {
142
+ throw new Error(
143
+ "@usethrottle/invoices hooks must be used inside <InvoiceProvider />"
144
+ );
145
+ }
146
+ return ctx;
147
+ }
148
+
149
+ // src/hooks.ts
150
+ var import_react2 = require("react");
151
+ function useInvoices(params = {}, options = {}) {
152
+ const client = useInvoiceClient();
153
+ const v = useInvoiceCacheVersion();
154
+ const p = useStableValue(params);
155
+ return useAsyncQuery(() => client.list(p), [client, p, v], options);
156
+ }
157
+ function useInvoice(id, options = {}) {
158
+ const client = useInvoiceClient();
159
+ const v = useInvoiceCacheVersion();
160
+ return useAsyncQuery(
161
+ () => id ? client.get(id) : Promise.resolve(null),
162
+ [client, id, v],
163
+ { ...options, enabled: options.enabled ?? Boolean(id) }
164
+ );
165
+ }
166
+ function useDownloadInvoice() {
167
+ const client = useInvoiceClient();
168
+ return async (id) => {
169
+ const url = await client.getDownloadUrl(id);
170
+ if (typeof window !== "undefined") window.open(url, "_blank");
171
+ return url;
172
+ };
173
+ }
174
+ function useAsyncQuery(load, deps, options) {
175
+ const enabled = options.enabled ?? true;
176
+ const [data, setData] = (0, import_react2.useState)(options.initialData ?? null);
177
+ const [error, setError] = (0, import_react2.useState)(null);
178
+ const [isLoading, setIsLoading] = (0, import_react2.useState)(enabled && options.initialData === void 0);
179
+ const [isFetching, setIsFetching] = (0, import_react2.useState)(false);
180
+ const loadRef = (0, import_react2.useRef)(load);
181
+ loadRef.current = load;
182
+ const refetch = (0, import_react2.useCallback)(async () => {
183
+ if (!enabled) return data;
184
+ setIsFetching(true);
185
+ setError(null);
186
+ try {
187
+ const next = await loadRef.current();
188
+ setData(next);
189
+ return next;
190
+ } catch (err) {
191
+ setError(err);
192
+ throw err;
193
+ } finally {
194
+ setIsFetching(false);
195
+ setIsLoading(false);
196
+ }
197
+ }, [data, enabled]);
198
+ (0, import_react2.useEffect)(() => {
199
+ if (!enabled) {
200
+ setIsLoading(false);
201
+ return;
202
+ }
203
+ let cancelled = false;
204
+ setIsFetching(true);
205
+ setError(null);
206
+ load().then((next) => {
207
+ if (!cancelled) setData(next);
208
+ }).catch((err) => {
209
+ if (!cancelled) setError(err);
210
+ }).finally(() => {
211
+ if (!cancelled) {
212
+ setIsLoading(false);
213
+ setIsFetching(false);
214
+ }
215
+ });
216
+ return () => {
217
+ cancelled = true;
218
+ };
219
+ }, [enabled, ...deps]);
220
+ return { data, error, isLoading, isFetching, refetch };
221
+ }
222
+ function useStableValue(value) {
223
+ const key = JSON.stringify(value);
224
+ return (0, import_react2.useMemo)(() => value, [key]);
225
+ }
226
+ // Annotate the CommonJS export names for ESM import in node:
227
+ 0 && (module.exports = {
228
+ InvoiceProvider,
229
+ InvoicesClient,
230
+ ThrottleError,
231
+ ThrottleInvoicesError,
232
+ createInvoicesClient,
233
+ isThrottleError,
234
+ useDownloadInvoice,
235
+ useInvoice,
236
+ useInvoiceClient,
237
+ useInvoiceInvalidation,
238
+ useInvoices
239
+ });
240
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +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 } from '@usethrottle/errors';\nexport class ThrottleInvoicesError extends ThrottleError {\n constructor(args: { code: string; message: string; statusCode: number; details?: unknown }) {\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 });\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"]}
@@ -0,0 +1,118 @@
1
+ import { ReactNode } from 'react';
2
+ import { ThrottleError } from '@usethrottle/errors';
3
+ export { ThrottleError, isThrottleError } from '@usethrottle/errors';
4
+ import * as react_jsx_runtime from 'react/jsx-runtime';
5
+
6
+ type InvoiceStatus = 'paid' | 'open' | 'refunded' | 'partially_refunded' | 'void';
7
+ type InvoiceSourceType = 'order' | 'net30' | 'subscription';
8
+ interface Invoice {
9
+ id: string;
10
+ number: string;
11
+ status: InvoiceStatus;
12
+ sourceType?: InvoiceSourceType;
13
+ currency: string;
14
+ subtotal?: number;
15
+ taxTotal?: number;
16
+ shippingTotal?: number;
17
+ discountTotal?: number;
18
+ total: number;
19
+ amountRefunded?: number;
20
+ issuedAt?: string;
21
+ dueAt?: string | null;
22
+ periodStart?: string | null;
23
+ periodEnd?: string | null;
24
+ createdAt?: string;
25
+ }
26
+ interface Pagination {
27
+ cursor: string | null;
28
+ hasMore: boolean;
29
+ }
30
+ interface PaginatedInvoices {
31
+ data: Invoice[];
32
+ pagination?: Pagination;
33
+ }
34
+ interface ListInvoicesParams {
35
+ status?: InvoiceStatus;
36
+ type?: InvoiceSourceType;
37
+ cursor?: string;
38
+ limit?: number;
39
+ sort?: 'asc' | 'desc';
40
+ }
41
+ type InvoiceFetcher = (path: string, init?: RequestInit) => Promise<Response>;
42
+ interface InvoicesClientOptions {
43
+ apiKey?: string;
44
+ baseUrl?: string;
45
+ fetch?: typeof globalThis.fetch;
46
+ fetcher?: InvoiceFetcher;
47
+ timeoutMs?: number;
48
+ }
49
+ interface ThrottleEnvelope<T> {
50
+ data?: T;
51
+ meta?: {
52
+ pagination?: {
53
+ cursor?: string | null;
54
+ hasMore?: boolean;
55
+ };
56
+ };
57
+ error?: {
58
+ code?: string;
59
+ message?: string;
60
+ details?: unknown;
61
+ };
62
+ }
63
+ interface InvoicesClientContract {
64
+ list(params?: ListInvoicesParams): Promise<PaginatedInvoices>;
65
+ get(id: string): Promise<Invoice>;
66
+ getDownloadUrl(id: string): Promise<string>;
67
+ }
68
+ interface InvoicesProviderProps {
69
+ children: ReactNode;
70
+ client?: InvoicesClientContract;
71
+ fetcher?: InvoiceFetcher;
72
+ baseUrl?: string;
73
+ timeoutMs?: number;
74
+ }
75
+ interface QueryState<T> {
76
+ data: T | null;
77
+ isLoading: boolean;
78
+ isFetching: boolean;
79
+ error: unknown;
80
+ refetch: () => Promise<T | null>;
81
+ }
82
+ interface QueryOptions<T> {
83
+ enabled?: boolean;
84
+ initialData?: T | null;
85
+ }
86
+
87
+ declare class InvoicesClient implements InvoicesClientContract {
88
+ private readonly apiKey?;
89
+ private readonly baseUrl;
90
+ private readonly fetchImpl;
91
+ private readonly fetcher?;
92
+ private readonly timeoutMs;
93
+ constructor(opts?: InvoicesClientOptions);
94
+ private requestEnvelope;
95
+ list(params?: ListInvoicesParams): Promise<PaginatedInvoices>;
96
+ get(id: string): Promise<Invoice>;
97
+ getDownloadUrl(id: string): Promise<string>;
98
+ }
99
+ declare function createInvoicesClient(opts: InvoicesClientOptions): InvoicesClient;
100
+
101
+ declare class ThrottleInvoicesError extends ThrottleError {
102
+ constructor(args: {
103
+ code: string;
104
+ message: string;
105
+ statusCode: number;
106
+ details?: unknown;
107
+ });
108
+ }
109
+
110
+ declare function InvoiceProvider({ children, client, fetcher, baseUrl, timeoutMs, }: InvoicesProviderProps): react_jsx_runtime.JSX.Element;
111
+ declare function useInvoiceClient(): InvoicesClientContract;
112
+ declare function useInvoiceInvalidation(): () => void;
113
+
114
+ declare function useInvoices(params?: ListInvoicesParams, options?: QueryOptions<PaginatedInvoices>): QueryState<PaginatedInvoices>;
115
+ declare function useInvoice(id: string | null | undefined, options?: QueryOptions<Invoice>): QueryState<Invoice>;
116
+ declare function useDownloadInvoice(): (id: string) => Promise<string>;
117
+
118
+ export { type Invoice, type InvoiceFetcher, InvoiceProvider, type InvoiceSourceType, type InvoiceStatus, InvoicesClient, type InvoicesClientContract, type InvoicesClientOptions, type InvoicesProviderProps, type ListInvoicesParams, type PaginatedInvoices, type Pagination, type QueryOptions, type QueryState, type ThrottleEnvelope, ThrottleInvoicesError, createInvoicesClient, useDownloadInvoice, useInvoice, useInvoiceClient, useInvoiceInvalidation, useInvoices };
@@ -0,0 +1,118 @@
1
+ import { ReactNode } from 'react';
2
+ import { ThrottleError } from '@usethrottle/errors';
3
+ export { ThrottleError, isThrottleError } from '@usethrottle/errors';
4
+ import * as react_jsx_runtime from 'react/jsx-runtime';
5
+
6
+ type InvoiceStatus = 'paid' | 'open' | 'refunded' | 'partially_refunded' | 'void';
7
+ type InvoiceSourceType = 'order' | 'net30' | 'subscription';
8
+ interface Invoice {
9
+ id: string;
10
+ number: string;
11
+ status: InvoiceStatus;
12
+ sourceType?: InvoiceSourceType;
13
+ currency: string;
14
+ subtotal?: number;
15
+ taxTotal?: number;
16
+ shippingTotal?: number;
17
+ discountTotal?: number;
18
+ total: number;
19
+ amountRefunded?: number;
20
+ issuedAt?: string;
21
+ dueAt?: string | null;
22
+ periodStart?: string | null;
23
+ periodEnd?: string | null;
24
+ createdAt?: string;
25
+ }
26
+ interface Pagination {
27
+ cursor: string | null;
28
+ hasMore: boolean;
29
+ }
30
+ interface PaginatedInvoices {
31
+ data: Invoice[];
32
+ pagination?: Pagination;
33
+ }
34
+ interface ListInvoicesParams {
35
+ status?: InvoiceStatus;
36
+ type?: InvoiceSourceType;
37
+ cursor?: string;
38
+ limit?: number;
39
+ sort?: 'asc' | 'desc';
40
+ }
41
+ type InvoiceFetcher = (path: string, init?: RequestInit) => Promise<Response>;
42
+ interface InvoicesClientOptions {
43
+ apiKey?: string;
44
+ baseUrl?: string;
45
+ fetch?: typeof globalThis.fetch;
46
+ fetcher?: InvoiceFetcher;
47
+ timeoutMs?: number;
48
+ }
49
+ interface ThrottleEnvelope<T> {
50
+ data?: T;
51
+ meta?: {
52
+ pagination?: {
53
+ cursor?: string | null;
54
+ hasMore?: boolean;
55
+ };
56
+ };
57
+ error?: {
58
+ code?: string;
59
+ message?: string;
60
+ details?: unknown;
61
+ };
62
+ }
63
+ interface InvoicesClientContract {
64
+ list(params?: ListInvoicesParams): Promise<PaginatedInvoices>;
65
+ get(id: string): Promise<Invoice>;
66
+ getDownloadUrl(id: string): Promise<string>;
67
+ }
68
+ interface InvoicesProviderProps {
69
+ children: ReactNode;
70
+ client?: InvoicesClientContract;
71
+ fetcher?: InvoiceFetcher;
72
+ baseUrl?: string;
73
+ timeoutMs?: number;
74
+ }
75
+ interface QueryState<T> {
76
+ data: T | null;
77
+ isLoading: boolean;
78
+ isFetching: boolean;
79
+ error: unknown;
80
+ refetch: () => Promise<T | null>;
81
+ }
82
+ interface QueryOptions<T> {
83
+ enabled?: boolean;
84
+ initialData?: T | null;
85
+ }
86
+
87
+ declare class InvoicesClient implements InvoicesClientContract {
88
+ private readonly apiKey?;
89
+ private readonly baseUrl;
90
+ private readonly fetchImpl;
91
+ private readonly fetcher?;
92
+ private readonly timeoutMs;
93
+ constructor(opts?: InvoicesClientOptions);
94
+ private requestEnvelope;
95
+ list(params?: ListInvoicesParams): Promise<PaginatedInvoices>;
96
+ get(id: string): Promise<Invoice>;
97
+ getDownloadUrl(id: string): Promise<string>;
98
+ }
99
+ declare function createInvoicesClient(opts: InvoicesClientOptions): InvoicesClient;
100
+
101
+ declare class ThrottleInvoicesError extends ThrottleError {
102
+ constructor(args: {
103
+ code: string;
104
+ message: string;
105
+ statusCode: number;
106
+ details?: unknown;
107
+ });
108
+ }
109
+
110
+ declare function InvoiceProvider({ children, client, fetcher, baseUrl, timeoutMs, }: InvoicesProviderProps): react_jsx_runtime.JSX.Element;
111
+ declare function useInvoiceClient(): InvoicesClientContract;
112
+ declare function useInvoiceInvalidation(): () => void;
113
+
114
+ declare function useInvoices(params?: ListInvoicesParams, options?: QueryOptions<PaginatedInvoices>): QueryState<PaginatedInvoices>;
115
+ declare function useInvoice(id: string | null | undefined, options?: QueryOptions<Invoice>): QueryState<Invoice>;
116
+ declare function useDownloadInvoice(): (id: string) => Promise<string>;
117
+
118
+ export { type Invoice, type InvoiceFetcher, InvoiceProvider, type InvoiceSourceType, type InvoiceStatus, InvoicesClient, type InvoicesClientContract, type InvoicesClientOptions, type InvoicesProviderProps, type ListInvoicesParams, type PaginatedInvoices, type Pagination, type QueryOptions, type QueryState, type ThrottleEnvelope, ThrottleInvoicesError, createInvoicesClient, useDownloadInvoice, useInvoice, useInvoiceClient, useInvoiceInvalidation, useInvoices };
package/dist/index.js ADDED
@@ -0,0 +1,203 @@
1
+ // src/errors.ts
2
+ import { ThrottleError } from "@usethrottle/errors";
3
+ import { ThrottleError as ThrottleError2, isThrottleError } from "@usethrottle/errors";
4
+ var ThrottleInvoicesError = class extends ThrottleError {
5
+ constructor(args) {
6
+ super(args);
7
+ this.name = "ThrottleInvoicesError";
8
+ }
9
+ };
10
+
11
+ // src/client.ts
12
+ var InvoicesClient = class {
13
+ apiKey;
14
+ baseUrl;
15
+ fetchImpl;
16
+ fetcher;
17
+ timeoutMs;
18
+ constructor(opts = {}) {
19
+ const fetchImpl = opts.fetch ?? globalThis.fetch;
20
+ if (!fetchImpl) throw new Error("InvoicesClient: fetch is required");
21
+ if (!opts.apiKey && !opts.fetcher) throw new Error("InvoicesClient: apiKey or fetcher is required");
22
+ this.apiKey = opts.apiKey;
23
+ this.baseUrl = (opts.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
24
+ this.fetchImpl = fetchImpl;
25
+ this.fetcher = opts.fetcher;
26
+ this.timeoutMs = opts.timeoutMs ?? 3e4;
27
+ }
28
+ async requestEnvelope(method, path) {
29
+ const ctrl = new AbortController();
30
+ const timer = setTimeout(() => ctrl.abort(), this.timeoutMs);
31
+ try {
32
+ const init = { method, headers: { "content-type": "application/json", ...this.apiKey ? { "x-api-key": this.apiKey } : {} }, signal: ctrl.signal };
33
+ const res = this.fetcher ? await this.fetcher(path, init) : await this.fetchImpl(`${this.baseUrl}${path}`, init);
34
+ const json = await res.json().catch(() => ({}));
35
+ 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 });
36
+ return json;
37
+ } finally {
38
+ clearTimeout(timer);
39
+ }
40
+ }
41
+ async list(params = {}) {
42
+ const q = new URLSearchParams();
43
+ for (const [k, v] of Object.entries(params)) if (v !== void 0 && v !== null && v !== "") q.set(k, String(v));
44
+ const suffix = q.toString() ? `?${q}` : "";
45
+ const env = await this.requestEnvelope("GET", `/api/v1/invoices${suffix}`);
46
+ return { data: env.data ?? [], pagination: env.meta?.pagination ? { cursor: env.meta.pagination.cursor ?? null, hasMore: Boolean(env.meta.pagination.hasMore) } : void 0 };
47
+ }
48
+ async get(id) {
49
+ const env = await this.requestEnvelope("GET", `/api/v1/invoices/${encodeURIComponent(id)}`);
50
+ return env.data;
51
+ }
52
+ async getDownloadUrl(id) {
53
+ const env = await this.requestEnvelope("GET", `/api/v1/invoices/${encodeURIComponent(id)}/download?format=json`);
54
+ return env.data.url;
55
+ }
56
+ };
57
+ function createInvoicesClient(opts) {
58
+ return new InvoicesClient(opts);
59
+ }
60
+
61
+ // src/provider.tsx
62
+ import { createContext, useContext, useMemo, useReducer } from "react";
63
+ import { jsx } from "react/jsx-runtime";
64
+ var InvoiceContext = createContext(null);
65
+ function InvoiceProvider({
66
+ children,
67
+ client,
68
+ fetcher,
69
+ baseUrl,
70
+ timeoutMs
71
+ }) {
72
+ const [cacheVersion, invalidate] = useReducer((value2) => value2 + 1, 0);
73
+ const resolvedClient = useMemo(() => {
74
+ if (client) return client;
75
+ return new InvoicesClient({
76
+ fetcher: fetcher ?? createInvoiceFetcher("/api/throttle"),
77
+ baseUrl,
78
+ timeoutMs
79
+ });
80
+ }, [baseUrl, client, fetcher, timeoutMs]);
81
+ const value = useMemo(
82
+ () => ({ client: resolvedClient, cacheVersion, invalidate }),
83
+ [cacheVersion, resolvedClient]
84
+ );
85
+ return /* @__PURE__ */ jsx(InvoiceContext.Provider, { value, children });
86
+ }
87
+ function useInvoiceClient() {
88
+ return useInvoiceContext().client;
89
+ }
90
+ function useInvoiceInvalidation() {
91
+ return useInvoiceContext().invalidate;
92
+ }
93
+ function useInvoiceCacheVersion() {
94
+ return useInvoiceContext().cacheVersion;
95
+ }
96
+ function createInvoiceFetcher(basePath) {
97
+ const cleanBase = basePath.replace(/\/+$/, "");
98
+ return (path, init) => {
99
+ const cleanPath = path.startsWith("/") ? path : `/${path}`;
100
+ return fetch(`${cleanBase}${cleanPath}`, init);
101
+ };
102
+ }
103
+ function useInvoiceContext() {
104
+ const ctx = useContext(InvoiceContext);
105
+ if (!ctx) {
106
+ throw new Error(
107
+ "@usethrottle/invoices hooks must be used inside <InvoiceProvider />"
108
+ );
109
+ }
110
+ return ctx;
111
+ }
112
+
113
+ // src/hooks.ts
114
+ import { useCallback as useCallback2, useEffect, useMemo as useMemo2, useRef, useState } from "react";
115
+ function useInvoices(params = {}, options = {}) {
116
+ const client = useInvoiceClient();
117
+ const v = useInvoiceCacheVersion();
118
+ const p = useStableValue(params);
119
+ return useAsyncQuery(() => client.list(p), [client, p, v], options);
120
+ }
121
+ function useInvoice(id, options = {}) {
122
+ const client = useInvoiceClient();
123
+ const v = useInvoiceCacheVersion();
124
+ return useAsyncQuery(
125
+ () => id ? client.get(id) : Promise.resolve(null),
126
+ [client, id, v],
127
+ { ...options, enabled: options.enabled ?? Boolean(id) }
128
+ );
129
+ }
130
+ function useDownloadInvoice() {
131
+ const client = useInvoiceClient();
132
+ return async (id) => {
133
+ const url = await client.getDownloadUrl(id);
134
+ if (typeof window !== "undefined") window.open(url, "_blank");
135
+ return url;
136
+ };
137
+ }
138
+ function useAsyncQuery(load, deps, options) {
139
+ const enabled = options.enabled ?? true;
140
+ const [data, setData] = useState(options.initialData ?? null);
141
+ const [error, setError] = useState(null);
142
+ const [isLoading, setIsLoading] = useState(enabled && options.initialData === void 0);
143
+ const [isFetching, setIsFetching] = useState(false);
144
+ const loadRef = useRef(load);
145
+ loadRef.current = load;
146
+ const refetch = useCallback2(async () => {
147
+ if (!enabled) return data;
148
+ setIsFetching(true);
149
+ setError(null);
150
+ try {
151
+ const next = await loadRef.current();
152
+ setData(next);
153
+ return next;
154
+ } catch (err) {
155
+ setError(err);
156
+ throw err;
157
+ } finally {
158
+ setIsFetching(false);
159
+ setIsLoading(false);
160
+ }
161
+ }, [data, enabled]);
162
+ useEffect(() => {
163
+ if (!enabled) {
164
+ setIsLoading(false);
165
+ return;
166
+ }
167
+ let cancelled = false;
168
+ setIsFetching(true);
169
+ setError(null);
170
+ load().then((next) => {
171
+ if (!cancelled) setData(next);
172
+ }).catch((err) => {
173
+ if (!cancelled) setError(err);
174
+ }).finally(() => {
175
+ if (!cancelled) {
176
+ setIsLoading(false);
177
+ setIsFetching(false);
178
+ }
179
+ });
180
+ return () => {
181
+ cancelled = true;
182
+ };
183
+ }, [enabled, ...deps]);
184
+ return { data, error, isLoading, isFetching, refetch };
185
+ }
186
+ function useStableValue(value) {
187
+ const key = JSON.stringify(value);
188
+ return useMemo2(() => value, [key]);
189
+ }
190
+ export {
191
+ InvoiceProvider,
192
+ InvoicesClient,
193
+ ThrottleError2 as ThrottleError,
194
+ ThrottleInvoicesError,
195
+ createInvoicesClient,
196
+ isThrottleError,
197
+ useDownloadInvoice,
198
+ useInvoice,
199
+ useInvoiceClient,
200
+ useInvoiceInvalidation,
201
+ useInvoices
202
+ };
203
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/client.ts","../src/provider.tsx","../src/hooks.ts"],"sourcesContent":["import { ThrottleError } from '@usethrottle/errors';\nexport class ThrottleInvoicesError extends ThrottleError {\n constructor(args: { code: string; message: string; statusCode: number; details?: unknown }) {\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 });\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":["ThrottleError","value","useCallback","useMemo","useCallback","useMemo"]}
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/server.ts
21
+ var server_exports = {};
22
+ __export(server_exports, {
23
+ createInvoiceProxyHandler: () => createInvoiceProxyHandler
24
+ });
25
+ module.exports = __toCommonJS(server_exports);
26
+ var LIST_RE = /^\/api\/v1\/invoices$/;
27
+ var BYID_RE = /^\/api\/v1\/invoices\/[\w.-]+$/;
28
+ var DL_RE = /^\/api\/v1\/invoices\/[\w.-]+\/download$/;
29
+ function createInvoiceProxyHandler(options) {
30
+ return async function handler(request, context = {}) {
31
+ const externalId = await options.getCustomerId(request);
32
+ if (!externalId) return json(401, "unauthorized", "Unauthorized");
33
+ if (request.method.toUpperCase() !== "GET") return json(405, "method_not_allowed", "Method not allowed");
34
+ const path = normalize(await pathFrom(context));
35
+ const baseUrl = (options.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
36
+ const fetchImpl = options.fetch ?? globalThis.fetch;
37
+ if (!fetchImpl) throw new Error("fetch is required");
38
+ const cRes = await fetchImpl(
39
+ `${baseUrl}/api/v1/customers/by-external/${encodeURIComponent(externalId)}`,
40
+ { headers: { "x-api-key": options.apiKey } }
41
+ );
42
+ if (!cRes.ok) return json(401, "unauthorized", "Unauthorized");
43
+ const customerId = (await cRes.json()).data?.id;
44
+ if (!customerId) return json(401, "unauthorized", "Unauthorized");
45
+ const url = new URL(request.url);
46
+ if (LIST_RE.test(path)) {
47
+ const target = `/api/v1/customers/${encodeURIComponent(customerId)}/invoices${url.search}`;
48
+ const upstream = await fetchImpl(`${baseUrl}${target}`, {
49
+ method: "GET",
50
+ headers: { "x-api-key": options.apiKey, "content-type": "application/json" }
51
+ });
52
+ const headers = new Headers(upstream.headers);
53
+ headers.delete("content-encoding");
54
+ headers.delete("content-length");
55
+ headers.delete("transfer-encoding");
56
+ return new Response(upstream.body, { status: upstream.status, headers });
57
+ } else if (BYID_RE.test(path)) {
58
+ const id = path.split("/").pop();
59
+ const invRes = await fetchImpl(`${baseUrl}/api/v1/invoices/${encodeURIComponent(id)}`, {
60
+ headers: { "x-api-key": options.apiKey }
61
+ });
62
+ if (!invRes.ok) return json(404, "not_found", "Not found");
63
+ const invBody = await invRes.json();
64
+ if (invBody.data?.customerId !== customerId) return json(404, "not_found", "Not found");
65
+ return Response.json(invBody, { status: 200 });
66
+ } else if (DL_RE.test(path)) {
67
+ const id = path.split("/").slice(-2)[0];
68
+ const invRes = await fetchImpl(`${baseUrl}/api/v1/invoices/${encodeURIComponent(id)}`, {
69
+ headers: { "x-api-key": options.apiKey }
70
+ });
71
+ if (!invRes.ok) return json(404, "not_found", "Not found");
72
+ const invBody = await invRes.json();
73
+ if (invBody.data?.customerId !== customerId) return json(404, "not_found", "Not found");
74
+ const dlTarget = `/api/v1/invoices/${encodeURIComponent(id)}/download${url.search}`;
75
+ const upstream = await fetchImpl(`${baseUrl}${dlTarget}`, {
76
+ method: "GET",
77
+ headers: { "x-api-key": options.apiKey }
78
+ });
79
+ const headers = new Headers(upstream.headers);
80
+ headers.delete("content-encoding");
81
+ headers.delete("content-length");
82
+ headers.delete("transfer-encoding");
83
+ return new Response(upstream.body, { status: upstream.status, headers });
84
+ } else {
85
+ return json(403, "forbidden", "Path is not allowed by the invoice proxy");
86
+ }
87
+ };
88
+ }
89
+ async function pathFrom(ctx) {
90
+ if (ctx.path) return ctx.path;
91
+ const p = ctx.params?.path;
92
+ if (!p) return [];
93
+ return await p;
94
+ }
95
+ function normalize(parts) {
96
+ if (typeof parts === "string") return parts.startsWith("/") ? parts : `/${parts}`;
97
+ return `/${parts.map((s) => s.replace(/^\/+|\/+$/g, "")).join("/")}`;
98
+ }
99
+ function json(status, code, message) {
100
+ return Response.json({ error: { code, message } }, { status });
101
+ }
102
+ // Annotate the CommonJS export names for ESM import in node:
103
+ 0 && (module.exports = {
104
+ createInvoiceProxyHandler
105
+ });
106
+ //# sourceMappingURL=server.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["export interface InvoiceProxyOptions {\n apiKey: string;\n baseUrl?: string;\n fetch?: typeof globalThis.fetch;\n getCustomerId: (request: Request) => string | null | Promise<string | null>;\n}\n\nexport interface InvoiceProxyContext {\n params?: { path?: string[] | Promise<string[]> };\n path?: string[] | string;\n}\n\nconst LIST_RE = /^\\/api\\/v1\\/invoices$/;\nconst BYID_RE = /^\\/api\\/v1\\/invoices\\/[\\w.-]+$/;\nconst DL_RE = /^\\/api\\/v1\\/invoices\\/[\\w.-]+\\/download$/;\n\nexport function createInvoiceProxyHandler(options: InvoiceProxyOptions) {\n return async function handler(request: Request, context: InvoiceProxyContext = {}): Promise<Response> {\n const externalId = await options.getCustomerId(request);\n if (!externalId) return json(401, 'unauthorized', 'Unauthorized');\n if (request.method.toUpperCase() !== 'GET') return json(405, 'method_not_allowed', 'Method not allowed');\n\n const path = normalize(await pathFrom(context));\n const baseUrl = (options.baseUrl ?? 'https://api.usethrottle.dev').replace(/\\/+$/, '');\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (!fetchImpl) throw new Error('fetch is required');\n\n // Resolve the buyer's Throttle customerId via their external id.\n const cRes = await fetchImpl(\n `${baseUrl}/api/v1/customers/by-external/${encodeURIComponent(externalId)}`,\n { headers: { 'x-api-key': options.apiKey } },\n );\n if (!cRes.ok) return json(401, 'unauthorized', 'Unauthorized');\n const customerId = ((await cRes.json()) as { data?: { id?: string } }).data?.id;\n if (!customerId) return json(401, 'unauthorized', 'Unauthorized');\n\n const url = new URL(request.url);\n if (LIST_RE.test(path)) {\n const target = `/api/v1/customers/${encodeURIComponent(customerId)}/invoices${url.search}`;\n const upstream = await fetchImpl(`${baseUrl}${target}`, {\n method: 'GET',\n headers: { 'x-api-key': options.apiKey, 'content-type': 'application/json' },\n });\n const headers = new Headers(upstream.headers);\n headers.delete('content-encoding');\n headers.delete('content-length');\n headers.delete('transfer-encoding');\n return new Response(upstream.body, { status: upstream.status, headers });\n } else if (BYID_RE.test(path)) {\n // Ownership check: verify the invoice belongs to the resolved buyer before returning it.\n const id = path.split('/').pop()!;\n const invRes = await fetchImpl(`${baseUrl}/api/v1/invoices/${encodeURIComponent(id)}`, {\n headers: { 'x-api-key': options.apiKey },\n });\n if (!invRes.ok) return json(404, 'not_found', 'Not found');\n const invBody = (await invRes.json()) as { data?: { customerId?: string } };\n if (invBody.data?.customerId !== customerId) return json(404, 'not_found', 'Not found');\n // Re-use the already-fetched response rather than double-fetching.\n return Response.json(invBody, { status: 200 });\n } else if (DL_RE.test(path)) {\n // Ownership check before serving the download.\n const id = path.split('/').slice(-2)[0]!;\n const invRes = await fetchImpl(`${baseUrl}/api/v1/invoices/${encodeURIComponent(id)}`, {\n headers: { 'x-api-key': options.apiKey },\n });\n if (!invRes.ok) return json(404, 'not_found', 'Not found');\n const invBody = (await invRes.json()) as { data?: { customerId?: string } };\n if (invBody.data?.customerId !== customerId) return json(404, 'not_found', 'Not found');\n // Ownership confirmed — forward the download request.\n const dlTarget = `/api/v1/invoices/${encodeURIComponent(id)}/download${url.search}`;\n const upstream = await fetchImpl(`${baseUrl}${dlTarget}`, {\n method: 'GET',\n headers: { 'x-api-key': options.apiKey },\n });\n const headers = new Headers(upstream.headers);\n headers.delete('content-encoding');\n headers.delete('content-length');\n headers.delete('transfer-encoding');\n return new Response(upstream.body, { status: upstream.status, headers });\n } else {\n return json(403, 'forbidden', 'Path is not allowed by the invoice proxy');\n }\n };\n}\n\nasync function pathFrom(ctx: InvoiceProxyContext): Promise<string[] | string> {\n if (ctx.path) return ctx.path;\n const p = ctx.params?.path;\n if (!p) return [];\n return await p;\n}\n\nfunction normalize(parts: string[] | string): string {\n if (typeof parts === 'string') return parts.startsWith('/') ? parts : `/${parts}`;\n return `/${parts.map((s) => s.replace(/^\\/+|\\/+$/g, '')).join('/')}`;\n}\n\nfunction json(status: number, code: string, message: string): Response {\n return Response.json({ error: { code, message } }, { status });\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,15 @@
1
+ interface InvoiceProxyOptions {
2
+ apiKey: string;
3
+ baseUrl?: string;
4
+ fetch?: typeof globalThis.fetch;
5
+ getCustomerId: (request: Request) => string | null | Promise<string | null>;
6
+ }
7
+ interface InvoiceProxyContext {
8
+ params?: {
9
+ path?: string[] | Promise<string[]>;
10
+ };
11
+ path?: string[] | string;
12
+ }
13
+ declare function createInvoiceProxyHandler(options: InvoiceProxyOptions): (request: Request, context?: InvoiceProxyContext) => Promise<Response>;
14
+
15
+ export { type InvoiceProxyContext, type InvoiceProxyOptions, createInvoiceProxyHandler };
@@ -0,0 +1,15 @@
1
+ interface InvoiceProxyOptions {
2
+ apiKey: string;
3
+ baseUrl?: string;
4
+ fetch?: typeof globalThis.fetch;
5
+ getCustomerId: (request: Request) => string | null | Promise<string | null>;
6
+ }
7
+ interface InvoiceProxyContext {
8
+ params?: {
9
+ path?: string[] | Promise<string[]>;
10
+ };
11
+ path?: string[] | string;
12
+ }
13
+ declare function createInvoiceProxyHandler(options: InvoiceProxyOptions): (request: Request, context?: InvoiceProxyContext) => Promise<Response>;
14
+
15
+ export { type InvoiceProxyContext, type InvoiceProxyOptions, createInvoiceProxyHandler };
package/dist/server.js ADDED
@@ -0,0 +1,81 @@
1
+ // src/server.ts
2
+ var LIST_RE = /^\/api\/v1\/invoices$/;
3
+ var BYID_RE = /^\/api\/v1\/invoices\/[\w.-]+$/;
4
+ var DL_RE = /^\/api\/v1\/invoices\/[\w.-]+\/download$/;
5
+ function createInvoiceProxyHandler(options) {
6
+ return async function handler(request, context = {}) {
7
+ const externalId = await options.getCustomerId(request);
8
+ if (!externalId) return json(401, "unauthorized", "Unauthorized");
9
+ if (request.method.toUpperCase() !== "GET") return json(405, "method_not_allowed", "Method not allowed");
10
+ const path = normalize(await pathFrom(context));
11
+ const baseUrl = (options.baseUrl ?? "https://api.usethrottle.dev").replace(/\/+$/, "");
12
+ const fetchImpl = options.fetch ?? globalThis.fetch;
13
+ if (!fetchImpl) throw new Error("fetch is required");
14
+ const cRes = await fetchImpl(
15
+ `${baseUrl}/api/v1/customers/by-external/${encodeURIComponent(externalId)}`,
16
+ { headers: { "x-api-key": options.apiKey } }
17
+ );
18
+ if (!cRes.ok) return json(401, "unauthorized", "Unauthorized");
19
+ const customerId = (await cRes.json()).data?.id;
20
+ if (!customerId) return json(401, "unauthorized", "Unauthorized");
21
+ const url = new URL(request.url);
22
+ if (LIST_RE.test(path)) {
23
+ const target = `/api/v1/customers/${encodeURIComponent(customerId)}/invoices${url.search}`;
24
+ const upstream = await fetchImpl(`${baseUrl}${target}`, {
25
+ method: "GET",
26
+ headers: { "x-api-key": options.apiKey, "content-type": "application/json" }
27
+ });
28
+ const headers = new Headers(upstream.headers);
29
+ headers.delete("content-encoding");
30
+ headers.delete("content-length");
31
+ headers.delete("transfer-encoding");
32
+ return new Response(upstream.body, { status: upstream.status, headers });
33
+ } else if (BYID_RE.test(path)) {
34
+ const id = path.split("/").pop();
35
+ const invRes = await fetchImpl(`${baseUrl}/api/v1/invoices/${encodeURIComponent(id)}`, {
36
+ headers: { "x-api-key": options.apiKey }
37
+ });
38
+ if (!invRes.ok) return json(404, "not_found", "Not found");
39
+ const invBody = await invRes.json();
40
+ if (invBody.data?.customerId !== customerId) return json(404, "not_found", "Not found");
41
+ return Response.json(invBody, { status: 200 });
42
+ } else if (DL_RE.test(path)) {
43
+ const id = path.split("/").slice(-2)[0];
44
+ const invRes = await fetchImpl(`${baseUrl}/api/v1/invoices/${encodeURIComponent(id)}`, {
45
+ headers: { "x-api-key": options.apiKey }
46
+ });
47
+ if (!invRes.ok) return json(404, "not_found", "Not found");
48
+ const invBody = await invRes.json();
49
+ if (invBody.data?.customerId !== customerId) return json(404, "not_found", "Not found");
50
+ const dlTarget = `/api/v1/invoices/${encodeURIComponent(id)}/download${url.search}`;
51
+ const upstream = await fetchImpl(`${baseUrl}${dlTarget}`, {
52
+ method: "GET",
53
+ headers: { "x-api-key": options.apiKey }
54
+ });
55
+ const headers = new Headers(upstream.headers);
56
+ headers.delete("content-encoding");
57
+ headers.delete("content-length");
58
+ headers.delete("transfer-encoding");
59
+ return new Response(upstream.body, { status: upstream.status, headers });
60
+ } else {
61
+ return json(403, "forbidden", "Path is not allowed by the invoice proxy");
62
+ }
63
+ };
64
+ }
65
+ async function pathFrom(ctx) {
66
+ if (ctx.path) return ctx.path;
67
+ const p = ctx.params?.path;
68
+ if (!p) return [];
69
+ return await p;
70
+ }
71
+ function normalize(parts) {
72
+ if (typeof parts === "string") return parts.startsWith("/") ? parts : `/${parts}`;
73
+ return `/${parts.map((s) => s.replace(/^\/+|\/+$/g, "")).join("/")}`;
74
+ }
75
+ function json(status, code, message) {
76
+ return Response.json({ error: { code, message } }, { status });
77
+ }
78
+ export {
79
+ createInvoiceProxyHandler
80
+ };
81
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["export interface InvoiceProxyOptions {\n apiKey: string;\n baseUrl?: string;\n fetch?: typeof globalThis.fetch;\n getCustomerId: (request: Request) => string | null | Promise<string | null>;\n}\n\nexport interface InvoiceProxyContext {\n params?: { path?: string[] | Promise<string[]> };\n path?: string[] | string;\n}\n\nconst LIST_RE = /^\\/api\\/v1\\/invoices$/;\nconst BYID_RE = /^\\/api\\/v1\\/invoices\\/[\\w.-]+$/;\nconst DL_RE = /^\\/api\\/v1\\/invoices\\/[\\w.-]+\\/download$/;\n\nexport function createInvoiceProxyHandler(options: InvoiceProxyOptions) {\n return async function handler(request: Request, context: InvoiceProxyContext = {}): Promise<Response> {\n const externalId = await options.getCustomerId(request);\n if (!externalId) return json(401, 'unauthorized', 'Unauthorized');\n if (request.method.toUpperCase() !== 'GET') return json(405, 'method_not_allowed', 'Method not allowed');\n\n const path = normalize(await pathFrom(context));\n const baseUrl = (options.baseUrl ?? 'https://api.usethrottle.dev').replace(/\\/+$/, '');\n const fetchImpl = options.fetch ?? globalThis.fetch;\n if (!fetchImpl) throw new Error('fetch is required');\n\n // Resolve the buyer's Throttle customerId via their external id.\n const cRes = await fetchImpl(\n `${baseUrl}/api/v1/customers/by-external/${encodeURIComponent(externalId)}`,\n { headers: { 'x-api-key': options.apiKey } },\n );\n if (!cRes.ok) return json(401, 'unauthorized', 'Unauthorized');\n const customerId = ((await cRes.json()) as { data?: { id?: string } }).data?.id;\n if (!customerId) return json(401, 'unauthorized', 'Unauthorized');\n\n const url = new URL(request.url);\n if (LIST_RE.test(path)) {\n const target = `/api/v1/customers/${encodeURIComponent(customerId)}/invoices${url.search}`;\n const upstream = await fetchImpl(`${baseUrl}${target}`, {\n method: 'GET',\n headers: { 'x-api-key': options.apiKey, 'content-type': 'application/json' },\n });\n const headers = new Headers(upstream.headers);\n headers.delete('content-encoding');\n headers.delete('content-length');\n headers.delete('transfer-encoding');\n return new Response(upstream.body, { status: upstream.status, headers });\n } else if (BYID_RE.test(path)) {\n // Ownership check: verify the invoice belongs to the resolved buyer before returning it.\n const id = path.split('/').pop()!;\n const invRes = await fetchImpl(`${baseUrl}/api/v1/invoices/${encodeURIComponent(id)}`, {\n headers: { 'x-api-key': options.apiKey },\n });\n if (!invRes.ok) return json(404, 'not_found', 'Not found');\n const invBody = (await invRes.json()) as { data?: { customerId?: string } };\n if (invBody.data?.customerId !== customerId) return json(404, 'not_found', 'Not found');\n // Re-use the already-fetched response rather than double-fetching.\n return Response.json(invBody, { status: 200 });\n } else if (DL_RE.test(path)) {\n // Ownership check before serving the download.\n const id = path.split('/').slice(-2)[0]!;\n const invRes = await fetchImpl(`${baseUrl}/api/v1/invoices/${encodeURIComponent(id)}`, {\n headers: { 'x-api-key': options.apiKey },\n });\n if (!invRes.ok) return json(404, 'not_found', 'Not found');\n const invBody = (await invRes.json()) as { data?: { customerId?: string } };\n if (invBody.data?.customerId !== customerId) return json(404, 'not_found', 'Not found');\n // Ownership confirmed — forward the download request.\n const dlTarget = `/api/v1/invoices/${encodeURIComponent(id)}/download${url.search}`;\n const upstream = await fetchImpl(`${baseUrl}${dlTarget}`, {\n method: 'GET',\n headers: { 'x-api-key': options.apiKey },\n });\n const headers = new Headers(upstream.headers);\n headers.delete('content-encoding');\n headers.delete('content-length');\n headers.delete('transfer-encoding');\n return new Response(upstream.body, { status: upstream.status, headers });\n } else {\n return json(403, 'forbidden', 'Path is not allowed by the invoice proxy');\n }\n };\n}\n\nasync function pathFrom(ctx: InvoiceProxyContext): Promise<string[] | string> {\n if (ctx.path) return ctx.path;\n const p = ctx.params?.path;\n if (!p) return [];\n return await p;\n}\n\nfunction normalize(parts: string[] | string): string {\n if (typeof parts === 'string') return parts.startsWith('/') ? parts : `/${parts}`;\n return `/${parts.map((s) => s.replace(/^\\/+|\\/+$/g, '')).join('/')}`;\n}\n\nfunction json(status: number, code: string, message: string): Response {\n return Response.json({ error: { code, message } }, { status });\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usethrottle/invoices",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "React hooks + server proxy for Throttle customer invoices.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",