vite-plugin-unirate 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +128 -0
- package/dist/cjs/client.cjs +228 -0
- package/dist/cjs/client.cjs.map +1 -0
- package/dist/cjs/index.cjs +25 -0
- package/dist/cjs/index.cjs.map +1 -0
- package/dist/cjs/plugin.cjs +180 -0
- package/dist/cjs/plugin.cjs.map +1 -0
- package/dist/esm/client.d.ts +64 -0
- package/dist/esm/client.d.ts.map +1 -0
- package/dist/esm/client.js +217 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +3 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/plugin.d.ts +46 -0
- package/dist/esm/plugin.d.ts.map +1 -0
- package/dist/esm/plugin.js +173 -0
- package/dist/esm/plugin.js.map +1 -0
- package/package.json +75 -0
- package/virtual.d.ts +20 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Unirate Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# vite-plugin-unirate
|
|
2
|
+
|
|
3
|
+
Vite plugin for [UniRate](https://unirateapi.com) that gives you two ways to use live currency data without ever shipping your API key to the browser:
|
|
4
|
+
|
|
5
|
+
1. **`virtual:unirate`** — a build-time virtual module. The plugin fetches rates and the supported-currency list once during the build and bakes them into your bundle as frozen, tree-shakeable constants. No runtime fetch, no client-side key.
|
|
6
|
+
2. **Dev-server proxy** — `configureServer` mounts `/__unirate/rate|convert|currencies|vat`. Those routes call UniRate **server-side**, so the API key stays on your machine while you develop against live rates.
|
|
7
|
+
|
|
8
|
+
- Zero runtime dependencies (uses native `fetch`).
|
|
9
|
+
- `vite` is a peer dependency (`>=5.0.0`).
|
|
10
|
+
- Full typed error mapping (400/401/403/404/429/503).
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
npm install vite-plugin-unirate
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
You'll need a free UniRate API key — grab one at <https://unirateapi.com>.
|
|
19
|
+
|
|
20
|
+
## Configure
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
// vite.config.ts
|
|
24
|
+
import { defineConfig } from "vite";
|
|
25
|
+
import unirate from "vite-plugin-unirate";
|
|
26
|
+
|
|
27
|
+
export default defineConfig({
|
|
28
|
+
plugins: [
|
|
29
|
+
unirate({
|
|
30
|
+
apiKey: process.env.UNIRATE_API_KEY,
|
|
31
|
+
baseCurrency: "USD",
|
|
32
|
+
currencies: ["EUR", "GBP", "JPY", "CAD", "AUD"],
|
|
33
|
+
}),
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If `apiKey` is omitted the plugin reads `UNIRATE_API_KEY` from the environment.
|
|
39
|
+
|
|
40
|
+
### Options
|
|
41
|
+
|
|
42
|
+
| Option | Type | Default | Description |
|
|
43
|
+
|---|---|---|---|
|
|
44
|
+
| `apiKey` | `string` | `process.env.UNIRATE_API_KEY` | UniRate API key. Used server-side only. |
|
|
45
|
+
| `baseCurrency` | `string` | `"USD"` | Base currency the build-time snapshot is keyed against. |
|
|
46
|
+
| `currencies` | `string[]` | _all UniRate currencies_ | Optional allowlist to shrink the baked snapshot. |
|
|
47
|
+
| `baseUrl` | `string` | `"https://api.unirateapi.com"` | API host. Override for self-hosted or testing. |
|
|
48
|
+
|
|
49
|
+
## 1. Build-time virtual module
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
import { rates, currencies, base, fetchedAt } from "virtual:unirate";
|
|
53
|
+
|
|
54
|
+
console.log(`1 ${base} = ${rates.EUR} EUR`); // baked at build time
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Everything resolves to constants in the final bundle — there is no network call at runtime and the API key never leaves the build.
|
|
58
|
+
|
|
59
|
+
### Typing the virtual module
|
|
60
|
+
|
|
61
|
+
Add the ambient reference once (e.g. in your `src/env.d.ts` next to `/// <reference types="vite/client" />`):
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
/// <reference types="vite-plugin-unirate/virtual" />
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Now the import is fully typed:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import snapshot from "virtual:unirate";
|
|
71
|
+
// snapshot: { base: string; rates: Record<string, number>; currencies: string[]; fetchedAt: string }
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## 2. Dev-server proxy
|
|
75
|
+
|
|
76
|
+
While `vite dev` is running, four routes call UniRate on the server:
|
|
77
|
+
|
|
78
|
+
| Route | Upstream | Response |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `GET /__unirate/rate?from=USD&to=EUR` | `/api/rates` | `{ "rates": { "USD": 1, "EUR": 0.925 } }` |
|
|
81
|
+
| `GET /__unirate/convert?from=USD&to=EUR&amount=100` | `/api/convert` | `{ "result": 92.5 }` |
|
|
82
|
+
| `GET /__unirate/currencies` | `/api/currencies` | `{ "currencies": ["USD", "EUR", ...] }` |
|
|
83
|
+
| `GET /__unirate/vat?country=DE` | `/api/vat/rates` | `{ "vat_data": { ... } }` (omit `country` for all) |
|
|
84
|
+
|
|
85
|
+
```ts
|
|
86
|
+
const res = await fetch("/__unirate/convert?from=USD&to=EUR&amount=100");
|
|
87
|
+
const { result } = await res.json();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Because the key lives in the Vite process, nothing sensitive ships to the browser. For production, either rely on the build-time `virtual:unirate` snapshot or run the same proxy behind your own backend.
|
|
91
|
+
|
|
92
|
+
## Error handling
|
|
93
|
+
|
|
94
|
+
Upstream UniRate errors are mapped to typed errors and re-surfaced with the correct status. On the dev-server proxy the HTTP status is preserved and the body is `{ "error": "<message>" }`.
|
|
95
|
+
|
|
96
|
+
| Status | Error type | Meaning |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| 400 | `InvalidDateError` | Invalid request parameters |
|
|
99
|
+
| 401 | `AuthenticationError` | Missing or invalid API key |
|
|
100
|
+
| 403 | `APIError` | Endpoint requires a Pro subscription |
|
|
101
|
+
| 404 | `InvalidCurrencyError` | Currency not found or no data available |
|
|
102
|
+
| 429 | `RateLimitError` | Rate limit exceeded |
|
|
103
|
+
| 503 | `APIError` | Service unavailable |
|
|
104
|
+
|
|
105
|
+
All inherit from `UniRateError`. Network/transport failures surface as the base `UniRateError` (and become a `502` on the proxy).
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
import { UniRateClient, AuthenticationError } from "vite-plugin-unirate";
|
|
109
|
+
|
|
110
|
+
const client = new UniRateClient({ apiKey: process.env.UNIRATE_API_KEY! });
|
|
111
|
+
try {
|
|
112
|
+
await client.getRates("USD", ["EUR"]);
|
|
113
|
+
} catch (err) {
|
|
114
|
+
if (err instanceof AuthenticationError) { /* fix the key */ }
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
## Rate limits
|
|
119
|
+
|
|
120
|
+
The free tier is rate-limited; a `429` maps to `RateLimitError`. Historical and time-series endpoints are Pro-gated (`403`) and are intentionally not exposed by this plugin.
|
|
121
|
+
|
|
122
|
+
## Related clients
|
|
123
|
+
|
|
124
|
+
Part of the UniRate client family — see [github.com/UniRate-API](https://github.com/UniRate-API) for the Astro, Eleventy, Next, Nuxt, SvelteKit, Vue, and framework-agnostic Node/Python/Go/Rust/etc. clients.
|
|
125
|
+
|
|
126
|
+
## License
|
|
127
|
+
|
|
128
|
+
MIT © 2026 Unirate Team
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Tiny, dependency-free fetch wrapper around the UniRate REST API.
|
|
3
|
+
//
|
|
4
|
+
// The plugin runs this on the server side only — during the build (to bake the
|
|
5
|
+
// `virtual:unirate` snapshot) and inside the dev-server proxy — so the API key
|
|
6
|
+
// never reaches the client bundle. Native `fetch` is available in Node 18+
|
|
7
|
+
// (required by Vite 5).
|
|
8
|
+
//
|
|
9
|
+
// Free-tier endpoints used here:
|
|
10
|
+
// GET /api/rates — current rates for one base
|
|
11
|
+
// GET /api/convert — single amount conversion
|
|
12
|
+
// GET /api/currencies — supported ISO codes
|
|
13
|
+
// GET /api/vat/rates — VAT rates (all countries or one)
|
|
14
|
+
//
|
|
15
|
+
// `Accept: application/json` is set on every request because /api/currencies
|
|
16
|
+
// (and friends) return an HTML 404 without it (see workspace CLAUDE.md
|
|
17
|
+
// "Key gotchas" #2).
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.UniRateClient = exports.APIError = exports.RateLimitError = exports.InvalidCurrencyError = exports.AuthenticationError = exports.InvalidDateError = exports.UniRateError = void 0;
|
|
20
|
+
exports.mapError = mapError;
|
|
21
|
+
/** Base error every typed UniRate error inherits from. */
|
|
22
|
+
class UniRateError extends Error {
|
|
23
|
+
status;
|
|
24
|
+
body;
|
|
25
|
+
constructor(message, status, body) {
|
|
26
|
+
super(message);
|
|
27
|
+
this.name = "UniRateError";
|
|
28
|
+
this.status = status;
|
|
29
|
+
this.body = body;
|
|
30
|
+
// Restore prototype chain for instanceof across the down-leveled build.
|
|
31
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.UniRateError = UniRateError;
|
|
35
|
+
/** 400 — malformed request parameters (also the historical `date` case). */
|
|
36
|
+
class InvalidDateError extends UniRateError {
|
|
37
|
+
constructor(message = "Invalid request parameters", body) {
|
|
38
|
+
super(message, 400, body);
|
|
39
|
+
this.name = "InvalidDateError";
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
exports.InvalidDateError = InvalidDateError;
|
|
43
|
+
/** 401 — missing or invalid API key. */
|
|
44
|
+
class AuthenticationError extends UniRateError {
|
|
45
|
+
constructor(message = "Missing or invalid API key", body) {
|
|
46
|
+
super(message, 401, body);
|
|
47
|
+
this.name = "AuthenticationError";
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.AuthenticationError = AuthenticationError;
|
|
51
|
+
/** 404 — currency (or country) not found / no data available. */
|
|
52
|
+
class InvalidCurrencyError extends UniRateError {
|
|
53
|
+
constructor(message = "Currency not found or no data available", body) {
|
|
54
|
+
super(message, 404, body);
|
|
55
|
+
this.name = "InvalidCurrencyError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.InvalidCurrencyError = InvalidCurrencyError;
|
|
59
|
+
/** 429 — rate limit exceeded. */
|
|
60
|
+
class RateLimitError extends UniRateError {
|
|
61
|
+
constructor(message = "Rate limit exceeded", body) {
|
|
62
|
+
super(message, 429, body);
|
|
63
|
+
this.name = "RateLimitError";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
exports.RateLimitError = RateLimitError;
|
|
67
|
+
/**
|
|
68
|
+
* Generic API error carrying the HTTP status. Used for 403 (Pro-gated
|
|
69
|
+
* endpoint on the free tier), 503 (service unavailable), and any other
|
|
70
|
+
* unexpected non-2xx status.
|
|
71
|
+
*/
|
|
72
|
+
class APIError extends UniRateError {
|
|
73
|
+
constructor(message, status, body) {
|
|
74
|
+
super(message, status, body);
|
|
75
|
+
this.name = "APIError";
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
exports.APIError = APIError;
|
|
79
|
+
const DEFAULT_BASE_URL = "https://api.unirateapi.com";
|
|
80
|
+
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
81
|
+
const toNum = (v) => {
|
|
82
|
+
const n = typeof v === "string" ? Number.parseFloat(v) : v;
|
|
83
|
+
if (!Number.isFinite(n))
|
|
84
|
+
throw new UniRateError(`Non-numeric value: ${String(v)}`);
|
|
85
|
+
return n;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Map an HTTP status + parsed body to the correct typed error.
|
|
89
|
+
* Exported so the dev-server proxy can reuse the exact same mapping.
|
|
90
|
+
*/
|
|
91
|
+
function mapError(status, body, path) {
|
|
92
|
+
const apiMsg = body && typeof body === "object" && "error" in body && typeof body.error === "string"
|
|
93
|
+
? body.error
|
|
94
|
+
: undefined;
|
|
95
|
+
switch (status) {
|
|
96
|
+
case 400:
|
|
97
|
+
return new InvalidDateError(apiMsg ?? "Invalid request parameters", body);
|
|
98
|
+
case 401:
|
|
99
|
+
return new AuthenticationError(apiMsg ?? "Missing or invalid API key", body);
|
|
100
|
+
case 403:
|
|
101
|
+
return new APIError(apiMsg ?? "Endpoint requires a Pro subscription", 403, body);
|
|
102
|
+
case 404:
|
|
103
|
+
return new InvalidCurrencyError(apiMsg ?? "Currency not found or no data available", body);
|
|
104
|
+
case 429:
|
|
105
|
+
return new RateLimitError(apiMsg ?? "Rate limit exceeded", body);
|
|
106
|
+
case 503:
|
|
107
|
+
return new APIError(apiMsg ?? "Service unavailable", 503, body);
|
|
108
|
+
default:
|
|
109
|
+
return new APIError(apiMsg ?? `HTTP ${status} from ${path}`, status, body);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
class UniRateClient {
|
|
113
|
+
#apiKey;
|
|
114
|
+
#baseUrl;
|
|
115
|
+
#fetch;
|
|
116
|
+
#timeoutMs;
|
|
117
|
+
constructor(opts) {
|
|
118
|
+
if (!opts.apiKey)
|
|
119
|
+
throw new UniRateError("apiKey is required");
|
|
120
|
+
this.#apiKey = opts.apiKey;
|
|
121
|
+
this.#baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
122
|
+
this.#fetch = opts.fetch ?? globalThis.fetch;
|
|
123
|
+
this.#timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
124
|
+
if (typeof this.#fetch !== "function") {
|
|
125
|
+
throw new UniRateError("global fetch is unavailable; pass `fetch` explicitly");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async #get(path, params) {
|
|
129
|
+
const url = new URL(this.#baseUrl + path);
|
|
130
|
+
url.searchParams.set("api_key", this.#apiKey);
|
|
131
|
+
for (const [k, v] of Object.entries(params)) {
|
|
132
|
+
if (v !== undefined)
|
|
133
|
+
url.searchParams.set(k, String(v));
|
|
134
|
+
}
|
|
135
|
+
const ctrl = new AbortController();
|
|
136
|
+
const timer = setTimeout(() => ctrl.abort(), this.#timeoutMs);
|
|
137
|
+
let res;
|
|
138
|
+
try {
|
|
139
|
+
res = await this.#fetch(url.toString(), {
|
|
140
|
+
headers: { Accept: "application/json" },
|
|
141
|
+
signal: ctrl.signal,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
catch (err) {
|
|
145
|
+
// Network / transport / abort failure — wrap in the base error.
|
|
146
|
+
throw new UniRateError(`Network error contacting ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
clearTimeout(timer);
|
|
150
|
+
}
|
|
151
|
+
const text = await res.text();
|
|
152
|
+
let body;
|
|
153
|
+
try {
|
|
154
|
+
body = text ? JSON.parse(text) : null;
|
|
155
|
+
}
|
|
156
|
+
catch {
|
|
157
|
+
// The HTML-404 case (missing Accept header) also lands here defensively.
|
|
158
|
+
if (!res.ok)
|
|
159
|
+
throw mapError(res.status, text, path);
|
|
160
|
+
throw new UniRateError(`Non-JSON response from ${path}`, res.status, text);
|
|
161
|
+
}
|
|
162
|
+
if (!res.ok)
|
|
163
|
+
throw mapError(res.status, body, path);
|
|
164
|
+
return body;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Current rates for `from`. With no `to`, returns every rate for the base.
|
|
168
|
+
* With a single-element `to`, tolerates the `{ rate }` shorthand shape.
|
|
169
|
+
*/
|
|
170
|
+
async getRates(from, to) {
|
|
171
|
+
const params = { from: from.toUpperCase() };
|
|
172
|
+
if (to && to.length > 0)
|
|
173
|
+
params.to = to.map((c) => c.toUpperCase()).join(",");
|
|
174
|
+
const raw = await this.#get("/api/rates", params);
|
|
175
|
+
const rates = {};
|
|
176
|
+
if (raw.rates) {
|
|
177
|
+
for (const [code, val] of Object.entries(raw.rates))
|
|
178
|
+
rates[code] = toNum(val);
|
|
179
|
+
}
|
|
180
|
+
else if (raw.rate !== undefined && to && to.length === 1) {
|
|
181
|
+
rates[to[0].toUpperCase()] = toNum(raw.rate);
|
|
182
|
+
}
|
|
183
|
+
else {
|
|
184
|
+
throw new UniRateError("Malformed /api/rates response", undefined, raw);
|
|
185
|
+
}
|
|
186
|
+
rates[from.toUpperCase()] = 1;
|
|
187
|
+
return rates;
|
|
188
|
+
}
|
|
189
|
+
/** Convert `amount` from one currency to another. */
|
|
190
|
+
async convert(amount, from, to) {
|
|
191
|
+
const raw = await this.#get("/api/convert", {
|
|
192
|
+
from: from.toUpperCase(),
|
|
193
|
+
to: to.toUpperCase(),
|
|
194
|
+
amount,
|
|
195
|
+
});
|
|
196
|
+
if (raw.result === undefined)
|
|
197
|
+
throw new UniRateError("Malformed /api/convert response", undefined, raw);
|
|
198
|
+
return toNum(raw.result);
|
|
199
|
+
}
|
|
200
|
+
/** Supported ISO currency codes. */
|
|
201
|
+
async listCurrencies() {
|
|
202
|
+
const raw = await this.#get("/api/currencies", {});
|
|
203
|
+
if (!Array.isArray(raw.currencies)) {
|
|
204
|
+
throw new UniRateError("Malformed /api/currencies response", undefined, raw);
|
|
205
|
+
}
|
|
206
|
+
return raw.currencies;
|
|
207
|
+
}
|
|
208
|
+
/** VAT rates for all countries, keyed by ISO-3166 alpha-2 code. */
|
|
209
|
+
async getVatRates() {
|
|
210
|
+
const raw = await this.#get("/api/vat/rates", {});
|
|
211
|
+
if (!raw.vat_rates || typeof raw.vat_rates !== "object") {
|
|
212
|
+
throw new UniRateError("Malformed /api/vat/rates response", undefined, raw);
|
|
213
|
+
}
|
|
214
|
+
return raw.vat_rates;
|
|
215
|
+
}
|
|
216
|
+
/** VAT rate for a single country. */
|
|
217
|
+
async getVatRate(country) {
|
|
218
|
+
const raw = await this.#get("/api/vat/rates", {
|
|
219
|
+
country: country.toUpperCase(),
|
|
220
|
+
});
|
|
221
|
+
if (!raw.vat_data || typeof raw.vat_data !== "object") {
|
|
222
|
+
throw new UniRateError("Malformed /api/vat/rates response", undefined, raw);
|
|
223
|
+
}
|
|
224
|
+
return raw.vat_data;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
exports.UniRateClient = UniRateClient;
|
|
228
|
+
//# sourceMappingURL=client.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.mapError = exports.RateLimitError = exports.InvalidDateError = exports.InvalidCurrencyError = exports.AuthenticationError = exports.APIError = exports.UniRateError = exports.UniRateClient = exports.PROXY_BASE = exports.RESOLVED_ID = exports.VIRTUAL_ID = exports.renderVirtualModule = exports.createProxyHandler = exports.buildSnapshot = exports.uniratePlugin = exports.default = void 0;
|
|
7
|
+
var plugin_js_1 = require("./plugin.cjs");
|
|
8
|
+
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(plugin_js_1).default; } });
|
|
9
|
+
Object.defineProperty(exports, "uniratePlugin", { enumerable: true, get: function () { return __importDefault(plugin_js_1).default; } });
|
|
10
|
+
Object.defineProperty(exports, "buildSnapshot", { enumerable: true, get: function () { return plugin_js_1.buildSnapshot; } });
|
|
11
|
+
Object.defineProperty(exports, "createProxyHandler", { enumerable: true, get: function () { return plugin_js_1.createProxyHandler; } });
|
|
12
|
+
Object.defineProperty(exports, "renderVirtualModule", { enumerable: true, get: function () { return plugin_js_1.renderVirtualModule; } });
|
|
13
|
+
Object.defineProperty(exports, "VIRTUAL_ID", { enumerable: true, get: function () { return plugin_js_1.VIRTUAL_ID; } });
|
|
14
|
+
Object.defineProperty(exports, "RESOLVED_ID", { enumerable: true, get: function () { return plugin_js_1.RESOLVED_ID; } });
|
|
15
|
+
Object.defineProperty(exports, "PROXY_BASE", { enumerable: true, get: function () { return plugin_js_1.PROXY_BASE; } });
|
|
16
|
+
var client_js_1 = require("./client.cjs");
|
|
17
|
+
Object.defineProperty(exports, "UniRateClient", { enumerable: true, get: function () { return client_js_1.UniRateClient; } });
|
|
18
|
+
Object.defineProperty(exports, "UniRateError", { enumerable: true, get: function () { return client_js_1.UniRateError; } });
|
|
19
|
+
Object.defineProperty(exports, "APIError", { enumerable: true, get: function () { return client_js_1.APIError; } });
|
|
20
|
+
Object.defineProperty(exports, "AuthenticationError", { enumerable: true, get: function () { return client_js_1.AuthenticationError; } });
|
|
21
|
+
Object.defineProperty(exports, "InvalidCurrencyError", { enumerable: true, get: function () { return client_js_1.InvalidCurrencyError; } });
|
|
22
|
+
Object.defineProperty(exports, "InvalidDateError", { enumerable: true, get: function () { return client_js_1.InvalidDateError; } });
|
|
23
|
+
Object.defineProperty(exports, "RateLimitError", { enumerable: true, get: function () { return client_js_1.RateLimitError; } });
|
|
24
|
+
Object.defineProperty(exports, "mapError", { enumerable: true, get: function () { return client_js_1.mapError; } });
|
|
25
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";;;;;;AAAA,yCASqB;AARnB,qHAAA,OAAO,OAAA;AACP,2HAAA,OAAO,OAAiB;AACxB,0GAAA,aAAa,OAAA;AACb,+GAAA,kBAAkB,OAAA;AAClB,gHAAA,mBAAmB,OAAA;AACnB,uGAAA,UAAU,OAAA;AACV,wGAAA,WAAW,OAAA;AACX,uGAAA,UAAU,OAAA;AAIZ,yCASqB;AARnB,0GAAA,aAAa,OAAA;AACb,yGAAA,YAAY,OAAA;AACZ,qGAAA,QAAQ,OAAA;AACR,gHAAA,mBAAmB,OAAA;AACnB,iHAAA,oBAAoB,OAAA;AACpB,6GAAA,gBAAgB,OAAA;AAChB,2GAAA,cAAc,OAAA;AACd,qGAAA,QAAQ,OAAA"}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PROXY_BASE = exports.RESOLVED_ID = exports.VIRTUAL_ID = void 0;
|
|
4
|
+
exports.renderVirtualModule = renderVirtualModule;
|
|
5
|
+
exports.buildSnapshot = buildSnapshot;
|
|
6
|
+
exports.createProxyHandler = createProxyHandler;
|
|
7
|
+
exports.default = uniratePlugin;
|
|
8
|
+
const client_js_1 = require("./client.cjs");
|
|
9
|
+
exports.VIRTUAL_ID = "virtual:unirate";
|
|
10
|
+
exports.RESOLVED_ID = "\0" + exports.VIRTUAL_ID;
|
|
11
|
+
/** Dev-server proxy mount path. Routes: /rate /convert /currencies /vat. */
|
|
12
|
+
exports.PROXY_BASE = "/__unirate";
|
|
13
|
+
const DEFAULT_BASE_CURRENCY = "USD";
|
|
14
|
+
function resolveApiKey(options) {
|
|
15
|
+
const key = options.apiKey ?? process.env.UNIRATE_API_KEY;
|
|
16
|
+
if (!key) {
|
|
17
|
+
throw new client_js_1.UniRateError("vite-plugin-unirate: no API key. Pass `apiKey` or set UNIRATE_API_KEY.");
|
|
18
|
+
}
|
|
19
|
+
return key;
|
|
20
|
+
}
|
|
21
|
+
/** Serialize a snapshot into a typed ESM module source string. */
|
|
22
|
+
function renderVirtualModule(snapshot) {
|
|
23
|
+
const payload = JSON.stringify(snapshot);
|
|
24
|
+
return [
|
|
25
|
+
`const snapshot = Object.freeze(${payload});`,
|
|
26
|
+
`export default snapshot;`,
|
|
27
|
+
`export const base = snapshot.base;`,
|
|
28
|
+
`export const rates = snapshot.rates;`,
|
|
29
|
+
`export const currencies = snapshot.currencies;`,
|
|
30
|
+
`export const fetchedAt = snapshot.fetchedAt;`,
|
|
31
|
+
``,
|
|
32
|
+
].join("\n");
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Fetch the build-time snapshot: all rates for the base currency plus the
|
|
36
|
+
* supported-currency list. `currencies` narrows the baked rate map.
|
|
37
|
+
*/
|
|
38
|
+
async function buildSnapshot(options) {
|
|
39
|
+
const apiKey = resolveApiKey(options);
|
|
40
|
+
const base = (options.baseCurrency ?? DEFAULT_BASE_CURRENCY).toUpperCase();
|
|
41
|
+
const client = new client_js_1.UniRateClient({
|
|
42
|
+
apiKey,
|
|
43
|
+
baseUrl: options.baseUrl,
|
|
44
|
+
fetch: options.fetch,
|
|
45
|
+
});
|
|
46
|
+
const wanted = options.currencies?.map((c) => c.toUpperCase());
|
|
47
|
+
const rates = await client.getRates(base, wanted);
|
|
48
|
+
const currencies = await client.listCurrencies();
|
|
49
|
+
return {
|
|
50
|
+
base,
|
|
51
|
+
rates,
|
|
52
|
+
currencies,
|
|
53
|
+
fetchedAt: new Date().toISOString(),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Read the JSON body of the response and translate our errors to HTTP. */
|
|
57
|
+
function sendJson(res, status, payload) {
|
|
58
|
+
const text = JSON.stringify(payload);
|
|
59
|
+
res.statusCode = status;
|
|
60
|
+
res.setHeader("Content-Type", "application/json");
|
|
61
|
+
res.end(text);
|
|
62
|
+
}
|
|
63
|
+
function errorStatus(err) {
|
|
64
|
+
if (err instanceof client_js_1.UniRateError && typeof err.status === "number")
|
|
65
|
+
return err.status;
|
|
66
|
+
return 502;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Build the dev-server request handler for the `/__unirate/*` proxy routes.
|
|
70
|
+
* Exported so tests can drive it without a real Vite server.
|
|
71
|
+
*/
|
|
72
|
+
function createProxyHandler(options) {
|
|
73
|
+
const client = new client_js_1.UniRateClient({
|
|
74
|
+
apiKey: resolveApiKey(options),
|
|
75
|
+
baseUrl: options.baseUrl,
|
|
76
|
+
fetch: options.fetch,
|
|
77
|
+
});
|
|
78
|
+
return async function unirateProxy(req, res, next) {
|
|
79
|
+
const rawUrl = req.url ?? "";
|
|
80
|
+
if (!rawUrl.startsWith(exports.PROXY_BASE + "/")) {
|
|
81
|
+
next();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Parse against a dummy origin so query params are accessible.
|
|
85
|
+
let url;
|
|
86
|
+
try {
|
|
87
|
+
url = new URL(rawUrl, "http://localhost");
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
sendJson(res, 400, { error: "Bad request URL" });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
const route = url.pathname.slice(exports.PROXY_BASE.length + 1);
|
|
94
|
+
const q = url.searchParams;
|
|
95
|
+
try {
|
|
96
|
+
switch (route) {
|
|
97
|
+
case "rate": {
|
|
98
|
+
const from = q.get("from") ?? DEFAULT_BASE_CURRENCY;
|
|
99
|
+
const to = q.get("to");
|
|
100
|
+
const rates = await client.getRates(from, to ? [to] : undefined);
|
|
101
|
+
sendJson(res, 200, { rates });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
case "convert": {
|
|
105
|
+
const from = q.get("from") ?? DEFAULT_BASE_CURRENCY;
|
|
106
|
+
const to = q.get("to");
|
|
107
|
+
const amountRaw = q.get("amount") ?? "1";
|
|
108
|
+
if (!to) {
|
|
109
|
+
sendJson(res, 400, { error: "`to` is required" });
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const amount = Number.parseFloat(amountRaw);
|
|
113
|
+
if (!Number.isFinite(amount)) {
|
|
114
|
+
sendJson(res, 400, { error: "`amount` must be numeric" });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const result = await client.convert(amount, from, to);
|
|
118
|
+
sendJson(res, 200, { result });
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
case "currencies": {
|
|
122
|
+
const currencies = await client.listCurrencies();
|
|
123
|
+
sendJson(res, 200, { currencies });
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
case "vat": {
|
|
127
|
+
const country = q.get("country");
|
|
128
|
+
if (country) {
|
|
129
|
+
const vat = await client.getVatRate(country);
|
|
130
|
+
sendJson(res, 200, { vat_data: vat });
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
const vat = await client.getVatRates();
|
|
134
|
+
sendJson(res, 200, { vat_rates: vat });
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
default:
|
|
139
|
+
sendJson(res, 404, { error: `Unknown UniRate proxy route: ${route}` });
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
const status = errorStatus(err);
|
|
145
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
146
|
+
sendJson(res, status, { error: message });
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Vite plugin for UniRate.
|
|
153
|
+
*
|
|
154
|
+
* 1. `virtual:unirate` — a build-time virtual module that fetches rates +
|
|
155
|
+
* currencies once and exposes them as a typed, tree-shakeable ESM export.
|
|
156
|
+
* 2. `/__unirate/{rate,convert,currencies,vat}` — a dev-server proxy that
|
|
157
|
+
* calls UniRate server-side so the API key never enters the client bundle.
|
|
158
|
+
*/
|
|
159
|
+
function uniratePlugin(options = {}) {
|
|
160
|
+
let snapshotPromise;
|
|
161
|
+
return {
|
|
162
|
+
name: "vite-plugin-unirate",
|
|
163
|
+
resolveId(id) {
|
|
164
|
+
return id === exports.VIRTUAL_ID ? exports.RESOLVED_ID : null;
|
|
165
|
+
},
|
|
166
|
+
async load(id) {
|
|
167
|
+
if (id !== exports.RESOLVED_ID)
|
|
168
|
+
return null;
|
|
169
|
+
// Fetch once and memoize across the build.
|
|
170
|
+
snapshotPromise ??= buildSnapshot(options);
|
|
171
|
+
const snapshot = await snapshotPromise;
|
|
172
|
+
return renderVirtualModule(snapshot);
|
|
173
|
+
},
|
|
174
|
+
configureServer(server) {
|
|
175
|
+
const handler = createProxyHandler(options);
|
|
176
|
+
server.middlewares.use(handler);
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
//# sourceMappingURL=plugin.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** Base error every typed UniRate error inherits from. */
|
|
2
|
+
export declare class UniRateError extends Error {
|
|
3
|
+
readonly status?: number;
|
|
4
|
+
readonly body?: unknown;
|
|
5
|
+
constructor(message: string, status?: number, body?: unknown);
|
|
6
|
+
}
|
|
7
|
+
/** 400 — malformed request parameters (also the historical `date` case). */
|
|
8
|
+
export declare class InvalidDateError extends UniRateError {
|
|
9
|
+
constructor(message?: string, body?: unknown);
|
|
10
|
+
}
|
|
11
|
+
/** 401 — missing or invalid API key. */
|
|
12
|
+
export declare class AuthenticationError extends UniRateError {
|
|
13
|
+
constructor(message?: string, body?: unknown);
|
|
14
|
+
}
|
|
15
|
+
/** 404 — currency (or country) not found / no data available. */
|
|
16
|
+
export declare class InvalidCurrencyError extends UniRateError {
|
|
17
|
+
constructor(message?: string, body?: unknown);
|
|
18
|
+
}
|
|
19
|
+
/** 429 — rate limit exceeded. */
|
|
20
|
+
export declare class RateLimitError extends UniRateError {
|
|
21
|
+
constructor(message?: string, body?: unknown);
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Generic API error carrying the HTTP status. Used for 403 (Pro-gated
|
|
25
|
+
* endpoint on the free tier), 503 (service unavailable), and any other
|
|
26
|
+
* unexpected non-2xx status.
|
|
27
|
+
*/
|
|
28
|
+
export declare class APIError extends UniRateError {
|
|
29
|
+
constructor(message: string, status: number, body?: unknown);
|
|
30
|
+
}
|
|
31
|
+
export interface UniRateClientOptions {
|
|
32
|
+
apiKey: string;
|
|
33
|
+
baseUrl?: string;
|
|
34
|
+
fetch?: typeof fetch;
|
|
35
|
+
timeoutMs?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface VatRate {
|
|
38
|
+
country_code: string;
|
|
39
|
+
country_name: string;
|
|
40
|
+
vat_rate: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Map an HTTP status + parsed body to the correct typed error.
|
|
44
|
+
* Exported so the dev-server proxy can reuse the exact same mapping.
|
|
45
|
+
*/
|
|
46
|
+
export declare function mapError(status: number, body: unknown, path: string): UniRateError;
|
|
47
|
+
export declare class UniRateClient {
|
|
48
|
+
#private;
|
|
49
|
+
constructor(opts: UniRateClientOptions);
|
|
50
|
+
/**
|
|
51
|
+
* Current rates for `from`. With no `to`, returns every rate for the base.
|
|
52
|
+
* With a single-element `to`, tolerates the `{ rate }` shorthand shape.
|
|
53
|
+
*/
|
|
54
|
+
getRates(from: string, to?: string[]): Promise<Record<string, number>>;
|
|
55
|
+
/** Convert `amount` from one currency to another. */
|
|
56
|
+
convert(amount: number, from: string, to: string): Promise<number>;
|
|
57
|
+
/** Supported ISO currency codes. */
|
|
58
|
+
listCurrencies(): Promise<string[]>;
|
|
59
|
+
/** VAT rates for all countries, keyed by ISO-3166 alpha-2 code. */
|
|
60
|
+
getVatRates(): Promise<Record<string, VatRate>>;
|
|
61
|
+
/** VAT rate for a single country. */
|
|
62
|
+
getVatRate(country: string): Promise<VatRate>;
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"AAiBA,0DAA0D;AAC1D,qBAAa,YAAa,SAAQ,KAAK;IACrC,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC;gBACZ,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;CAQ7D;AAED,4EAA4E;AAC5E,qBAAa,gBAAiB,SAAQ,YAAY;gBACpC,OAAO,SAA+B,EAAE,IAAI,CAAC,EAAE,OAAO;CAInE;AAED,wCAAwC;AACxC,qBAAa,mBAAoB,SAAQ,YAAY;gBACvC,OAAO,SAA+B,EAAE,IAAI,CAAC,EAAE,OAAO;CAInE;AAED,iEAAiE;AACjE,qBAAa,oBAAqB,SAAQ,YAAY;gBACxC,OAAO,SAA4C,EAAE,IAAI,CAAC,EAAE,OAAO;CAIhF;AAED,iCAAiC;AACjC,qBAAa,cAAe,SAAQ,YAAY;gBAClC,OAAO,SAAwB,EAAE,IAAI,CAAC,EAAE,OAAO;CAI5D;AAED;;;;GAIG;AACH,qBAAa,QAAS,SAAQ,YAAY;gBAC5B,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO;CAI5D;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,OAAO;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAWD;;;GAGG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAqBlF;AAED,qBAAa,aAAa;;gBAMZ,IAAI,EAAE,oBAAoB;IA+CtC;;;OAGG;IACG,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAmB5E,qDAAqD;IAC/C,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAUxE,oCAAoC;IAC9B,cAAc,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAQzC,mEAAmE;IAC7D,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAQrD,qCAAqC;IAC/B,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CASpD"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
// Tiny, dependency-free fetch wrapper around the UniRate REST API.
|
|
2
|
+
//
|
|
3
|
+
// The plugin runs this on the server side only — during the build (to bake the
|
|
4
|
+
// `virtual:unirate` snapshot) and inside the dev-server proxy — so the API key
|
|
5
|
+
// never reaches the client bundle. Native `fetch` is available in Node 18+
|
|
6
|
+
// (required by Vite 5).
|
|
7
|
+
//
|
|
8
|
+
// Free-tier endpoints used here:
|
|
9
|
+
// GET /api/rates — current rates for one base
|
|
10
|
+
// GET /api/convert — single amount conversion
|
|
11
|
+
// GET /api/currencies — supported ISO codes
|
|
12
|
+
// GET /api/vat/rates — VAT rates (all countries or one)
|
|
13
|
+
//
|
|
14
|
+
// `Accept: application/json` is set on every request because /api/currencies
|
|
15
|
+
// (and friends) return an HTML 404 without it (see workspace CLAUDE.md
|
|
16
|
+
// "Key gotchas" #2).
|
|
17
|
+
/** Base error every typed UniRate error inherits from. */
|
|
18
|
+
export class UniRateError extends Error {
|
|
19
|
+
status;
|
|
20
|
+
body;
|
|
21
|
+
constructor(message, status, body) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.name = "UniRateError";
|
|
24
|
+
this.status = status;
|
|
25
|
+
this.body = body;
|
|
26
|
+
// Restore prototype chain for instanceof across the down-leveled build.
|
|
27
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
/** 400 — malformed request parameters (also the historical `date` case). */
|
|
31
|
+
export class InvalidDateError extends UniRateError {
|
|
32
|
+
constructor(message = "Invalid request parameters", body) {
|
|
33
|
+
super(message, 400, body);
|
|
34
|
+
this.name = "InvalidDateError";
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
/** 401 — missing or invalid API key. */
|
|
38
|
+
export class AuthenticationError extends UniRateError {
|
|
39
|
+
constructor(message = "Missing or invalid API key", body) {
|
|
40
|
+
super(message, 401, body);
|
|
41
|
+
this.name = "AuthenticationError";
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/** 404 — currency (or country) not found / no data available. */
|
|
45
|
+
export class InvalidCurrencyError extends UniRateError {
|
|
46
|
+
constructor(message = "Currency not found or no data available", body) {
|
|
47
|
+
super(message, 404, body);
|
|
48
|
+
this.name = "InvalidCurrencyError";
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/** 429 — rate limit exceeded. */
|
|
52
|
+
export class RateLimitError extends UniRateError {
|
|
53
|
+
constructor(message = "Rate limit exceeded", body) {
|
|
54
|
+
super(message, 429, body);
|
|
55
|
+
this.name = "RateLimitError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Generic API error carrying the HTTP status. Used for 403 (Pro-gated
|
|
60
|
+
* endpoint on the free tier), 503 (service unavailable), and any other
|
|
61
|
+
* unexpected non-2xx status.
|
|
62
|
+
*/
|
|
63
|
+
export class APIError extends UniRateError {
|
|
64
|
+
constructor(message, status, body) {
|
|
65
|
+
super(message, status, body);
|
|
66
|
+
this.name = "APIError";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
const DEFAULT_BASE_URL = "https://api.unirateapi.com";
|
|
70
|
+
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
71
|
+
const toNum = (v) => {
|
|
72
|
+
const n = typeof v === "string" ? Number.parseFloat(v) : v;
|
|
73
|
+
if (!Number.isFinite(n))
|
|
74
|
+
throw new UniRateError(`Non-numeric value: ${String(v)}`);
|
|
75
|
+
return n;
|
|
76
|
+
};
|
|
77
|
+
/**
|
|
78
|
+
* Map an HTTP status + parsed body to the correct typed error.
|
|
79
|
+
* Exported so the dev-server proxy can reuse the exact same mapping.
|
|
80
|
+
*/
|
|
81
|
+
export function mapError(status, body, path) {
|
|
82
|
+
const apiMsg = body && typeof body === "object" && "error" in body && typeof body.error === "string"
|
|
83
|
+
? body.error
|
|
84
|
+
: undefined;
|
|
85
|
+
switch (status) {
|
|
86
|
+
case 400:
|
|
87
|
+
return new InvalidDateError(apiMsg ?? "Invalid request parameters", body);
|
|
88
|
+
case 401:
|
|
89
|
+
return new AuthenticationError(apiMsg ?? "Missing or invalid API key", body);
|
|
90
|
+
case 403:
|
|
91
|
+
return new APIError(apiMsg ?? "Endpoint requires a Pro subscription", 403, body);
|
|
92
|
+
case 404:
|
|
93
|
+
return new InvalidCurrencyError(apiMsg ?? "Currency not found or no data available", body);
|
|
94
|
+
case 429:
|
|
95
|
+
return new RateLimitError(apiMsg ?? "Rate limit exceeded", body);
|
|
96
|
+
case 503:
|
|
97
|
+
return new APIError(apiMsg ?? "Service unavailable", 503, body);
|
|
98
|
+
default:
|
|
99
|
+
return new APIError(apiMsg ?? `HTTP ${status} from ${path}`, status, body);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export class UniRateClient {
|
|
103
|
+
#apiKey;
|
|
104
|
+
#baseUrl;
|
|
105
|
+
#fetch;
|
|
106
|
+
#timeoutMs;
|
|
107
|
+
constructor(opts) {
|
|
108
|
+
if (!opts.apiKey)
|
|
109
|
+
throw new UniRateError("apiKey is required");
|
|
110
|
+
this.#apiKey = opts.apiKey;
|
|
111
|
+
this.#baseUrl = (opts.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
112
|
+
this.#fetch = opts.fetch ?? globalThis.fetch;
|
|
113
|
+
this.#timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
114
|
+
if (typeof this.#fetch !== "function") {
|
|
115
|
+
throw new UniRateError("global fetch is unavailable; pass `fetch` explicitly");
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
async #get(path, params) {
|
|
119
|
+
const url = new URL(this.#baseUrl + path);
|
|
120
|
+
url.searchParams.set("api_key", this.#apiKey);
|
|
121
|
+
for (const [k, v] of Object.entries(params)) {
|
|
122
|
+
if (v !== undefined)
|
|
123
|
+
url.searchParams.set(k, String(v));
|
|
124
|
+
}
|
|
125
|
+
const ctrl = new AbortController();
|
|
126
|
+
const timer = setTimeout(() => ctrl.abort(), this.#timeoutMs);
|
|
127
|
+
let res;
|
|
128
|
+
try {
|
|
129
|
+
res = await this.#fetch(url.toString(), {
|
|
130
|
+
headers: { Accept: "application/json" },
|
|
131
|
+
signal: ctrl.signal,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
catch (err) {
|
|
135
|
+
// Network / transport / abort failure — wrap in the base error.
|
|
136
|
+
throw new UniRateError(`Network error contacting ${path}: ${err instanceof Error ? err.message : String(err)}`);
|
|
137
|
+
}
|
|
138
|
+
finally {
|
|
139
|
+
clearTimeout(timer);
|
|
140
|
+
}
|
|
141
|
+
const text = await res.text();
|
|
142
|
+
let body;
|
|
143
|
+
try {
|
|
144
|
+
body = text ? JSON.parse(text) : null;
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
// The HTML-404 case (missing Accept header) also lands here defensively.
|
|
148
|
+
if (!res.ok)
|
|
149
|
+
throw mapError(res.status, text, path);
|
|
150
|
+
throw new UniRateError(`Non-JSON response from ${path}`, res.status, text);
|
|
151
|
+
}
|
|
152
|
+
if (!res.ok)
|
|
153
|
+
throw mapError(res.status, body, path);
|
|
154
|
+
return body;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Current rates for `from`. With no `to`, returns every rate for the base.
|
|
158
|
+
* With a single-element `to`, tolerates the `{ rate }` shorthand shape.
|
|
159
|
+
*/
|
|
160
|
+
async getRates(from, to) {
|
|
161
|
+
const params = { from: from.toUpperCase() };
|
|
162
|
+
if (to && to.length > 0)
|
|
163
|
+
params.to = to.map((c) => c.toUpperCase()).join(",");
|
|
164
|
+
const raw = await this.#get("/api/rates", params);
|
|
165
|
+
const rates = {};
|
|
166
|
+
if (raw.rates) {
|
|
167
|
+
for (const [code, val] of Object.entries(raw.rates))
|
|
168
|
+
rates[code] = toNum(val);
|
|
169
|
+
}
|
|
170
|
+
else if (raw.rate !== undefined && to && to.length === 1) {
|
|
171
|
+
rates[to[0].toUpperCase()] = toNum(raw.rate);
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
throw new UniRateError("Malformed /api/rates response", undefined, raw);
|
|
175
|
+
}
|
|
176
|
+
rates[from.toUpperCase()] = 1;
|
|
177
|
+
return rates;
|
|
178
|
+
}
|
|
179
|
+
/** Convert `amount` from one currency to another. */
|
|
180
|
+
async convert(amount, from, to) {
|
|
181
|
+
const raw = await this.#get("/api/convert", {
|
|
182
|
+
from: from.toUpperCase(),
|
|
183
|
+
to: to.toUpperCase(),
|
|
184
|
+
amount,
|
|
185
|
+
});
|
|
186
|
+
if (raw.result === undefined)
|
|
187
|
+
throw new UniRateError("Malformed /api/convert response", undefined, raw);
|
|
188
|
+
return toNum(raw.result);
|
|
189
|
+
}
|
|
190
|
+
/** Supported ISO currency codes. */
|
|
191
|
+
async listCurrencies() {
|
|
192
|
+
const raw = await this.#get("/api/currencies", {});
|
|
193
|
+
if (!Array.isArray(raw.currencies)) {
|
|
194
|
+
throw new UniRateError("Malformed /api/currencies response", undefined, raw);
|
|
195
|
+
}
|
|
196
|
+
return raw.currencies;
|
|
197
|
+
}
|
|
198
|
+
/** VAT rates for all countries, keyed by ISO-3166 alpha-2 code. */
|
|
199
|
+
async getVatRates() {
|
|
200
|
+
const raw = await this.#get("/api/vat/rates", {});
|
|
201
|
+
if (!raw.vat_rates || typeof raw.vat_rates !== "object") {
|
|
202
|
+
throw new UniRateError("Malformed /api/vat/rates response", undefined, raw);
|
|
203
|
+
}
|
|
204
|
+
return raw.vat_rates;
|
|
205
|
+
}
|
|
206
|
+
/** VAT rate for a single country. */
|
|
207
|
+
async getVatRate(country) {
|
|
208
|
+
const raw = await this.#get("/api/vat/rates", {
|
|
209
|
+
country: country.toUpperCase(),
|
|
210
|
+
});
|
|
211
|
+
if (!raw.vat_data || typeof raw.vat_data !== "object") {
|
|
212
|
+
throw new UniRateError("Malformed /api/vat/rates response", undefined, raw);
|
|
213
|
+
}
|
|
214
|
+
return raw.vat_data;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default, default as uniratePlugin, buildSnapshot, createProxyHandler, renderVirtualModule, VIRTUAL_ID, RESOLVED_ID, PROXY_BASE, } from "./plugin.js";
|
|
2
|
+
export type { UniRatePluginOptions, UniRateSnapshot } from "./plugin.js";
|
|
3
|
+
export { UniRateClient, UniRateError, APIError, AuthenticationError, InvalidCurrencyError, InvalidDateError, RateLimitError, mapError, } from "./client.js";
|
|
4
|
+
export type { UniRateClientOptions, VatRate } from "./client.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,OAAO,IAAI,aAAa,EACxB,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,UAAU,GACX,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEzE,OAAO,EACL,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,QAAQ,GACT,MAAM,aAAa,CAAC;AACrB,YAAY,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { default, default as uniratePlugin, buildSnapshot, createProxyHandler, renderVirtualModule, VIRTUAL_ID, RESOLVED_ID, PROXY_BASE, } from "./plugin.js";
|
|
2
|
+
export { UniRateClient, UniRateError, APIError, AuthenticationError, InvalidCurrencyError, InvalidDateError, RateLimitError, mapError, } from "./client.js";
|
|
3
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,EACP,OAAO,IAAI,aAAa,EACxB,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,UAAU,EACV,WAAW,EACX,UAAU,GACX,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,aAAa,EACb,YAAY,EACZ,QAAQ,EACR,mBAAmB,EACnB,oBAAoB,EACpB,gBAAgB,EAChB,cAAc,EACd,QAAQ,GACT,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { Plugin, Connect } from "vite";
|
|
2
|
+
export declare const VIRTUAL_ID = "virtual:unirate";
|
|
3
|
+
export declare const RESOLVED_ID: string;
|
|
4
|
+
/** Dev-server proxy mount path. Routes: /rate /convert /currencies /vat. */
|
|
5
|
+
export declare const PROXY_BASE = "/__unirate";
|
|
6
|
+
export interface UniRatePluginOptions {
|
|
7
|
+
/** UniRate API key. Falls back to `process.env.UNIRATE_API_KEY`. */
|
|
8
|
+
apiKey?: string;
|
|
9
|
+
/** Base currency for the build-time snapshot. Default `"USD"`. */
|
|
10
|
+
baseCurrency?: string;
|
|
11
|
+
/** Optional allowlist to shrink the baked snapshot. Default: all currencies. */
|
|
12
|
+
currencies?: string[];
|
|
13
|
+
/** API host. Override for self-hosted / testing. */
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
/** Injectable fetch — used by tests, defaults to global fetch. */
|
|
16
|
+
fetch?: typeof fetch;
|
|
17
|
+
}
|
|
18
|
+
/** Shape emitted by the `virtual:unirate` module. */
|
|
19
|
+
export interface UniRateSnapshot {
|
|
20
|
+
base: string;
|
|
21
|
+
rates: Record<string, number>;
|
|
22
|
+
currencies: string[];
|
|
23
|
+
fetchedAt: string;
|
|
24
|
+
}
|
|
25
|
+
/** Serialize a snapshot into a typed ESM module source string. */
|
|
26
|
+
export declare function renderVirtualModule(snapshot: UniRateSnapshot): string;
|
|
27
|
+
/**
|
|
28
|
+
* Fetch the build-time snapshot: all rates for the base currency plus the
|
|
29
|
+
* supported-currency list. `currencies` narrows the baked rate map.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildSnapshot(options: UniRatePluginOptions): Promise<UniRateSnapshot>;
|
|
32
|
+
/**
|
|
33
|
+
* Build the dev-server request handler for the `/__unirate/*` proxy routes.
|
|
34
|
+
* Exported so tests can drive it without a real Vite server.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createProxyHandler(options: UniRatePluginOptions): Connect.NextHandleFunction;
|
|
37
|
+
/**
|
|
38
|
+
* Vite plugin for UniRate.
|
|
39
|
+
*
|
|
40
|
+
* 1. `virtual:unirate` — a build-time virtual module that fetches rates +
|
|
41
|
+
* currencies once and exposes them as a typed, tree-shakeable ESM export.
|
|
42
|
+
* 2. `/__unirate/{rate,convert,currencies,vat}` — a dev-server proxy that
|
|
43
|
+
* calls UniRate server-side so the API key never enters the client bundle.
|
|
44
|
+
*/
|
|
45
|
+
export default function uniratePlugin(options?: UniRatePluginOptions): Plugin;
|
|
46
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAiB,OAAO,EAAE,MAAM,MAAM,CAAC;AAG3D,eAAO,MAAM,UAAU,oBAAoB,CAAC;AAC5C,eAAO,MAAM,WAAW,QAAoB,CAAC;AAE7C,4EAA4E;AAC5E,eAAO,MAAM,UAAU,eAAe,CAAC;AAIvC,MAAM,WAAW,oBAAoB;IACnC,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,qDAAqD;AACrD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAcD,kEAAkE;AAClE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,eAAe,GAAG,MAAM,CAWrE;AAED;;;GAGG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC,CAmB3F;AAeD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,kBAAkB,CA6E5F;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAuBhF"}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { UniRateClient, UniRateError } from "./client.js";
|
|
2
|
+
export const VIRTUAL_ID = "virtual:unirate";
|
|
3
|
+
export const RESOLVED_ID = "\0" + VIRTUAL_ID;
|
|
4
|
+
/** Dev-server proxy mount path. Routes: /rate /convert /currencies /vat. */
|
|
5
|
+
export const PROXY_BASE = "/__unirate";
|
|
6
|
+
const DEFAULT_BASE_CURRENCY = "USD";
|
|
7
|
+
function resolveApiKey(options) {
|
|
8
|
+
const key = options.apiKey ?? process.env.UNIRATE_API_KEY;
|
|
9
|
+
if (!key) {
|
|
10
|
+
throw new UniRateError("vite-plugin-unirate: no API key. Pass `apiKey` or set UNIRATE_API_KEY.");
|
|
11
|
+
}
|
|
12
|
+
return key;
|
|
13
|
+
}
|
|
14
|
+
/** Serialize a snapshot into a typed ESM module source string. */
|
|
15
|
+
export function renderVirtualModule(snapshot) {
|
|
16
|
+
const payload = JSON.stringify(snapshot);
|
|
17
|
+
return [
|
|
18
|
+
`const snapshot = Object.freeze(${payload});`,
|
|
19
|
+
`export default snapshot;`,
|
|
20
|
+
`export const base = snapshot.base;`,
|
|
21
|
+
`export const rates = snapshot.rates;`,
|
|
22
|
+
`export const currencies = snapshot.currencies;`,
|
|
23
|
+
`export const fetchedAt = snapshot.fetchedAt;`,
|
|
24
|
+
``,
|
|
25
|
+
].join("\n");
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Fetch the build-time snapshot: all rates for the base currency plus the
|
|
29
|
+
* supported-currency list. `currencies` narrows the baked rate map.
|
|
30
|
+
*/
|
|
31
|
+
export async function buildSnapshot(options) {
|
|
32
|
+
const apiKey = resolveApiKey(options);
|
|
33
|
+
const base = (options.baseCurrency ?? DEFAULT_BASE_CURRENCY).toUpperCase();
|
|
34
|
+
const client = new UniRateClient({
|
|
35
|
+
apiKey,
|
|
36
|
+
baseUrl: options.baseUrl,
|
|
37
|
+
fetch: options.fetch,
|
|
38
|
+
});
|
|
39
|
+
const wanted = options.currencies?.map((c) => c.toUpperCase());
|
|
40
|
+
const rates = await client.getRates(base, wanted);
|
|
41
|
+
const currencies = await client.listCurrencies();
|
|
42
|
+
return {
|
|
43
|
+
base,
|
|
44
|
+
rates,
|
|
45
|
+
currencies,
|
|
46
|
+
fetchedAt: new Date().toISOString(),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/** Read the JSON body of the response and translate our errors to HTTP. */
|
|
50
|
+
function sendJson(res, status, payload) {
|
|
51
|
+
const text = JSON.stringify(payload);
|
|
52
|
+
res.statusCode = status;
|
|
53
|
+
res.setHeader("Content-Type", "application/json");
|
|
54
|
+
res.end(text);
|
|
55
|
+
}
|
|
56
|
+
function errorStatus(err) {
|
|
57
|
+
if (err instanceof UniRateError && typeof err.status === "number")
|
|
58
|
+
return err.status;
|
|
59
|
+
return 502;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Build the dev-server request handler for the `/__unirate/*` proxy routes.
|
|
63
|
+
* Exported so tests can drive it without a real Vite server.
|
|
64
|
+
*/
|
|
65
|
+
export function createProxyHandler(options) {
|
|
66
|
+
const client = new UniRateClient({
|
|
67
|
+
apiKey: resolveApiKey(options),
|
|
68
|
+
baseUrl: options.baseUrl,
|
|
69
|
+
fetch: options.fetch,
|
|
70
|
+
});
|
|
71
|
+
return async function unirateProxy(req, res, next) {
|
|
72
|
+
const rawUrl = req.url ?? "";
|
|
73
|
+
if (!rawUrl.startsWith(PROXY_BASE + "/")) {
|
|
74
|
+
next();
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
// Parse against a dummy origin so query params are accessible.
|
|
78
|
+
let url;
|
|
79
|
+
try {
|
|
80
|
+
url = new URL(rawUrl, "http://localhost");
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
sendJson(res, 400, { error: "Bad request URL" });
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
const route = url.pathname.slice(PROXY_BASE.length + 1);
|
|
87
|
+
const q = url.searchParams;
|
|
88
|
+
try {
|
|
89
|
+
switch (route) {
|
|
90
|
+
case "rate": {
|
|
91
|
+
const from = q.get("from") ?? DEFAULT_BASE_CURRENCY;
|
|
92
|
+
const to = q.get("to");
|
|
93
|
+
const rates = await client.getRates(from, to ? [to] : undefined);
|
|
94
|
+
sendJson(res, 200, { rates });
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
case "convert": {
|
|
98
|
+
const from = q.get("from") ?? DEFAULT_BASE_CURRENCY;
|
|
99
|
+
const to = q.get("to");
|
|
100
|
+
const amountRaw = q.get("amount") ?? "1";
|
|
101
|
+
if (!to) {
|
|
102
|
+
sendJson(res, 400, { error: "`to` is required" });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const amount = Number.parseFloat(amountRaw);
|
|
106
|
+
if (!Number.isFinite(amount)) {
|
|
107
|
+
sendJson(res, 400, { error: "`amount` must be numeric" });
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const result = await client.convert(amount, from, to);
|
|
111
|
+
sendJson(res, 200, { result });
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
case "currencies": {
|
|
115
|
+
const currencies = await client.listCurrencies();
|
|
116
|
+
sendJson(res, 200, { currencies });
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
case "vat": {
|
|
120
|
+
const country = q.get("country");
|
|
121
|
+
if (country) {
|
|
122
|
+
const vat = await client.getVatRate(country);
|
|
123
|
+
sendJson(res, 200, { vat_data: vat });
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
const vat = await client.getVatRates();
|
|
127
|
+
sendJson(res, 200, { vat_rates: vat });
|
|
128
|
+
}
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
default:
|
|
132
|
+
sendJson(res, 404, { error: `Unknown UniRate proxy route: ${route}` });
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (err) {
|
|
137
|
+
const status = errorStatus(err);
|
|
138
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
139
|
+
sendJson(res, status, { error: message });
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Vite plugin for UniRate.
|
|
146
|
+
*
|
|
147
|
+
* 1. `virtual:unirate` — a build-time virtual module that fetches rates +
|
|
148
|
+
* currencies once and exposes them as a typed, tree-shakeable ESM export.
|
|
149
|
+
* 2. `/__unirate/{rate,convert,currencies,vat}` — a dev-server proxy that
|
|
150
|
+
* calls UniRate server-side so the API key never enters the client bundle.
|
|
151
|
+
*/
|
|
152
|
+
export default function uniratePlugin(options = {}) {
|
|
153
|
+
let snapshotPromise;
|
|
154
|
+
return {
|
|
155
|
+
name: "vite-plugin-unirate",
|
|
156
|
+
resolveId(id) {
|
|
157
|
+
return id === VIRTUAL_ID ? RESOLVED_ID : null;
|
|
158
|
+
},
|
|
159
|
+
async load(id) {
|
|
160
|
+
if (id !== RESOLVED_ID)
|
|
161
|
+
return null;
|
|
162
|
+
// Fetch once and memoize across the build.
|
|
163
|
+
snapshotPromise ??= buildSnapshot(options);
|
|
164
|
+
const snapshot = await snapshotPromise;
|
|
165
|
+
return renderVirtualModule(snapshot);
|
|
166
|
+
},
|
|
167
|
+
configureServer(server) {
|
|
168
|
+
const handler = createProxyHandler(options);
|
|
169
|
+
server.middlewares.use(handler);
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vite-plugin-unirate",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Vite plugin for UniRate — a build-time `virtual:unirate` module with baked currency rates/currencies, plus a dev-server proxy that keeps your API key server-side.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Unirate Team",
|
|
8
|
+
"homepage": "https://github.com/UniRate-API/vite-plugin-unirate#readme",
|
|
9
|
+
"bugs": "https://github.com/UniRate-API/vite-plugin-unirate/issues",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/UniRate-API/vite-plugin-unirate.git"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"vite",
|
|
16
|
+
"vite-plugin",
|
|
17
|
+
"unirate",
|
|
18
|
+
"currency",
|
|
19
|
+
"exchange-rates",
|
|
20
|
+
"forex",
|
|
21
|
+
"money",
|
|
22
|
+
"fx",
|
|
23
|
+
"fintech"
|
|
24
|
+
],
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"virtual.d.ts",
|
|
28
|
+
"README.md",
|
|
29
|
+
"LICENSE"
|
|
30
|
+
],
|
|
31
|
+
"main": "./dist/cjs/index.cjs",
|
|
32
|
+
"module": "./dist/esm/index.js",
|
|
33
|
+
"types": "./dist/esm/index.d.ts",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/esm/index.d.ts",
|
|
37
|
+
"import": "./dist/esm/index.js",
|
|
38
|
+
"require": "./dist/cjs/index.cjs"
|
|
39
|
+
},
|
|
40
|
+
"./client": {
|
|
41
|
+
"types": "./dist/esm/client.d.ts",
|
|
42
|
+
"import": "./dist/esm/client.js",
|
|
43
|
+
"require": "./dist/cjs/client.cjs"
|
|
44
|
+
},
|
|
45
|
+
"./virtual": {
|
|
46
|
+
"types": "./virtual.d.ts"
|
|
47
|
+
},
|
|
48
|
+
"./package.json": "./package.json"
|
|
49
|
+
},
|
|
50
|
+
"scripts": {
|
|
51
|
+
"build": "npm run build:esm && npm run build:cjs",
|
|
52
|
+
"build:esm": "tsc -p tsconfig.build.json",
|
|
53
|
+
"build:cjs": "tsc -p tsconfig.cjs.json && node ./scripts/finalize-cjs.mjs",
|
|
54
|
+
"test": "vitest run",
|
|
55
|
+
"test:watch": "vitest",
|
|
56
|
+
"typecheck": "tsc --noEmit",
|
|
57
|
+
"prepublishOnly": "npm run build"
|
|
58
|
+
},
|
|
59
|
+
"peerDependencies": {
|
|
60
|
+
"vite": ">=5.0.0"
|
|
61
|
+
},
|
|
62
|
+
"devDependencies": {
|
|
63
|
+
"@types/node": "^20.11.0",
|
|
64
|
+
"typescript": "^5.4.0",
|
|
65
|
+
"vite": "^7.1.5",
|
|
66
|
+
"vitest": "^4.1.7"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=20.19.0"
|
|
70
|
+
},
|
|
71
|
+
"publishConfig": {
|
|
72
|
+
"access": "public",
|
|
73
|
+
"provenance": true
|
|
74
|
+
}
|
|
75
|
+
}
|
package/virtual.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Ambient declaration for the `virtual:unirate` module baked at build time by
|
|
2
|
+
// vite-plugin-unirate. Reference it from your app's env.d.ts / a *.d.ts file:
|
|
3
|
+
//
|
|
4
|
+
// /// <reference types="vite-plugin-unirate/virtual" />
|
|
5
|
+
//
|
|
6
|
+
// Then `import { rates, currencies } from "virtual:unirate"` is fully typed.
|
|
7
|
+
declare module "virtual:unirate" {
|
|
8
|
+
export interface UniRateSnapshot {
|
|
9
|
+
base: string;
|
|
10
|
+
rates: Record<string, number>;
|
|
11
|
+
currencies: string[];
|
|
12
|
+
fetchedAt: string;
|
|
13
|
+
}
|
|
14
|
+
const snapshot: UniRateSnapshot;
|
|
15
|
+
export default snapshot;
|
|
16
|
+
export const base: string;
|
|
17
|
+
export const rates: Record<string, number>;
|
|
18
|
+
export const currencies: string[];
|
|
19
|
+
export const fetchedAt: string;
|
|
20
|
+
}
|