cspr402 0.4.7

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.
Files changed (63) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +173 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +108 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/client.d.ts +207 -0
  8. package/dist/client.d.ts.map +1 -0
  9. package/dist/client.js +400 -0
  10. package/dist/client.js.map +1 -0
  11. package/dist/commands/onboard.d.ts +4 -0
  12. package/dist/commands/onboard.d.ts.map +1 -0
  13. package/dist/commands/onboard.js +192 -0
  14. package/dist/commands/onboard.js.map +1 -0
  15. package/dist/commands/onboard.test.d.ts +2 -0
  16. package/dist/commands/onboard.test.d.ts.map +1 -0
  17. package/dist/commands/onboard.test.js +48 -0
  18. package/dist/commands/onboard.test.js.map +1 -0
  19. package/dist/commands/purchase.d.ts +2 -0
  20. package/dist/commands/purchase.d.ts.map +1 -0
  21. package/dist/commands/purchase.js +206 -0
  22. package/dist/commands/purchase.js.map +1 -0
  23. package/dist/commands/wallet.d.ts +2 -0
  24. package/dist/commands/wallet.d.ts.map +1 -0
  25. package/dist/commands/wallet.js +161 -0
  26. package/dist/commands/wallet.js.map +1 -0
  27. package/dist/config.d.ts +77 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +329 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/errors.d.ts +101 -0
  32. package/dist/errors.d.ts.map +1 -0
  33. package/dist/errors.js +197 -0
  34. package/dist/errors.js.map +1 -0
  35. package/dist/index.d.ts +6 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +22 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/mcp.d.ts +2 -0
  40. package/dist/mcp.d.ts.map +1 -0
  41. package/dist/mcp.js +337 -0
  42. package/dist/mcp.js.map +1 -0
  43. package/dist/mpp.d.ts +57 -0
  44. package/dist/mpp.d.ts.map +1 -0
  45. package/dist/mpp.js +165 -0
  46. package/dist/mpp.js.map +1 -0
  47. package/dist/ows.d.ts +190 -0
  48. package/dist/ows.d.ts.map +1 -0
  49. package/dist/ows.js +565 -0
  50. package/dist/ows.js.map +1 -0
  51. package/dist/soroban.d.ts +92 -0
  52. package/dist/soroban.d.ts.map +1 -0
  53. package/dist/soroban.js +313 -0
  54. package/dist/soroban.js.map +1 -0
  55. package/dist/stellar.d.ts +53 -0
  56. package/dist/stellar.d.ts.map +1 -0
  57. package/dist/stellar.js +180 -0
  58. package/dist/stellar.js.map +1 -0
  59. package/dist/version-check.d.ts +5 -0
  60. package/dist/version-check.d.ts.map +1 -0
  61. package/dist/version-check.js +203 -0
  62. package/dist/version-check.js.map +1 -0
  63. package/package.json +80 -0
package/dist/config.js ADDED
@@ -0,0 +1,329 @@
1
+ "use strict";
2
+ // Agent-local config file. Persisted at ~/.cards402/config.json after
3
+ // a successful `cards402 onboard --claim` so the SDK can load the api
4
+ // key on subsequent runs without the agent having to re-paste secrets.
5
+ //
6
+ // The file lives on the agent's machine and is readable only by the
7
+ // agent's user (chmod 0600). It holds the raw api key — same secret
8
+ // the older env-var workflow stored in process.env, just written to
9
+ // disk in a well-known place so the SDK can find it automatically.
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ exports.loadCards402Config = loadCards402Config;
45
+ exports.assertSafeBaseUrl = assertSafeBaseUrl;
46
+ exports.saveCards402Config = saveCards402Config;
47
+ exports.resolveCredentials = resolveCredentials;
48
+ const fs = __importStar(require("fs"));
49
+ const path = __importStar(require("path"));
50
+ const os = __importStar(require("os"));
51
+ const crypto = __importStar(require("crypto"));
52
+ // Adversarial audit F5-config: config files should be tiny. A 16 KB
53
+ // cap leaves plenty of room for a fat api_key + url + wallet_name
54
+ // while refusing a maliciously-enlarged file that would otherwise be
55
+ // parsed in full and flowed into request headers downstream.
56
+ const MAX_CONFIG_BYTES = 16 * 1024;
57
+ function defaultConfigDir() {
58
+ return (process.env.CSPR402_CONFIG_DIR ||
59
+ process.env.CARDS402_CONFIG_DIR ||
60
+ path.join(os.homedir(), '.cspr402'));
61
+ }
62
+ function defaultConfigPath() {
63
+ return path.join(defaultConfigDir(), 'config.json');
64
+ }
65
+ /**
66
+ * Load the agent's on-disk config, or return null if it doesn't exist.
67
+ * Never throws on missing file — only on corrupt JSON.
68
+ *
69
+ * On load we also tighten the file mode to 0600 if it's been loosened
70
+ * since the write (e.g. a bug in an older SDK version that wrote with
71
+ * default permissions, or an attacker pre-creating the file
72
+ * world-readable to farm credentials off the next onboarding). We
73
+ * warn rather than refuse the load, so operators with an existing
74
+ * loose config aren't hard-broken, but the mode is normalised
75
+ * immediately.
76
+ */
77
+ // F3-config (2026-04-16): validate the api_key shape before accepting
78
+ // it. A corrupt or tampered key containing CRLF, NUL, or other control
79
+ // chars would flow into the X-Api-Key HTTP header and trigger Node's
80
+ // ERR_INVALID_CHAR on every fetch call — same bug class as the backend's
81
+ // X-Request-ID audit (F1-app). Accept printable ASCII only (the backend
82
+ // mints keys as `cards402_<48 hex>`, which is pure ASCII alnum + underscore).
83
+ const API_KEY_SHAPE = /^[\x20-\x7e]+$/;
84
+ function loadCards402Config(configPath) {
85
+ const p = configPath || defaultConfigPath();
86
+ try {
87
+ // F1-config (2026-04-16): platform-independent checks (symlink,
88
+ // regular-file, size cap) are now run on ALL platforms including
89
+ // Windows. Pre-fix the entire block was gated on
90
+ // `process.platform !== 'win32'`, which meant Windows agents
91
+ // skipped the size cap — a planted 1 GB config.json (or an NTFS
92
+ // junction to a large file) would be fully loaded via readFileSync
93
+ // and OOM the agent. NTFS supports symlinks and junctions, and
94
+ // fs.lstatSync correctly reports them, so the symlink defense
95
+ // is also meaningful on Windows. Only the Unix permission-bit
96
+ // checks (chmod) are platform-gated now.
97
+ let stat;
98
+ try {
99
+ stat = fs.lstatSync(p);
100
+ }
101
+ catch (statErr) {
102
+ if (statErr.code === 'ENOENT')
103
+ return null;
104
+ throw statErr;
105
+ }
106
+ if (stat.isSymbolicLink()) {
107
+ throw new Error(`cards402 config at ${p} is a symbolic link. Refusing to load. ` +
108
+ `Remove the link and re-run 'cards402 onboard --claim <code>' to create a real file.`);
109
+ }
110
+ if (!stat.isFile()) {
111
+ throw new Error(`cards402 config at ${p} is not a regular file. ` +
112
+ `Remove it and re-run 'cards402 onboard --claim <code>'.`);
113
+ }
114
+ // F5-config: enforce a size cap BEFORE reading the file into
115
+ // memory or doing any further work on it. Config files are
116
+ // tiny; anything bigger than MAX_CONFIG_BYTES is either
117
+ // corruption or an attempt to flood request headers.
118
+ if (stat.size > MAX_CONFIG_BYTES) {
119
+ throw new Error(`cards402 config at ${p} is ${stat.size} bytes (max ${MAX_CONFIG_BYTES}). ` +
120
+ `Refusing to load — the file is either corrupted or has been tampered with. ` +
121
+ `Rotate your api key via the dashboard and re-run 'cards402 onboard'.`);
122
+ }
123
+ // Unix-only: tighten loose permission bits. chmod on a regular
124
+ // file (we verified above that it's not a symlink) is safe and
125
+ // only affects this file. Skipped on Windows where mode bits are
126
+ // simulated and chmod can fail or no-op unpredictably.
127
+ if (process.platform !== 'win32' && (stat.mode & 0o077) !== 0) {
128
+ try {
129
+ fs.chmodSync(p, 0o600);
130
+ process.stderr.write(`⚠ cards402 config at ${p} had loose permissions (${(stat.mode & 0o777).toString(8)}) — tightened to 600.\n` +
131
+ ' If this is unexpected, rotate your api key via the dashboard.\n');
132
+ }
133
+ catch {
134
+ /* non-fatal — we at least tried */
135
+ }
136
+ }
137
+ const raw = fs.readFileSync(p, 'utf8');
138
+ const config = JSON.parse(raw);
139
+ // F3-config (2026-04-16): validate api_key shape before accepting.
140
+ // A corrupt or tampered key with CRLF / NUL / non-printable bytes
141
+ // would crash every HTTP request downstream via Node's
142
+ // ERR_INVALID_CHAR header validation.
143
+ if (typeof config.api_key === 'string' && !API_KEY_SHAPE.test(config.api_key)) {
144
+ throw new Error(`cards402 config at ${p} contains an api_key with non-printable characters. ` +
145
+ `The file may be corrupted or tampered with. Rotate your key and re-run 'cards402 onboard'.`);
146
+ }
147
+ return config;
148
+ }
149
+ catch (err) {
150
+ if (err.code === 'ENOENT')
151
+ return null;
152
+ throw err;
153
+ }
154
+ }
155
+ /**
156
+ * Validate a base URL for safety before storing it in the config or
157
+ * using it for API calls. Rejects everything that isn't HTTPS unless
158
+ * the explicit CARDS402_ALLOW_INSECURE_BASE_URL escape hatch is set,
159
+ * which only exists so local dev against http://localhost:4000 still
160
+ * works. Returns the parsed URL.string() on success, throws on reject.
161
+ *
162
+ * Called from:
163
+ * - onboard, when persisting the api_url returned by the claim
164
+ * endpoint (defends against a MITM or compromised backend that
165
+ * injects http:// or a foreign origin into the response)
166
+ * - resolveCredentials, when an env-var override is used for
167
+ * baseUrl (defends against a user being tricked into setting
168
+ * CARDS402_BASE_URL to an attacker target)
169
+ */
170
+ function assertSafeBaseUrl(url, opts = {}) {
171
+ let parsed;
172
+ try {
173
+ parsed = new URL(url);
174
+ }
175
+ catch {
176
+ throw new Error(`Invalid base URL: ${url}`);
177
+ }
178
+ // F4-config: reject embedded userinfo. `https://api.cards402.com/v1@evil.com/`
179
+ // parses as username='api.cards402.com/v1', password='', hostname='evil.com'
180
+ // — the whole string looks plausibly cards402-ish in log output, but every
181
+ // request would go to evil.com carrying the user's api_key in the
182
+ // Authorization header. There's no legitimate reason for a cards402 base
183
+ // URL to include credentials, so refuse any URL with a non-empty username
184
+ // or password.
185
+ if (parsed.username !== '' || parsed.password !== '') {
186
+ throw new Error(`Refusing base URL ${JSON.stringify(url)} with embedded credentials. ` +
187
+ `Use a bare https://host/path form — the api key is sent via the ` +
188
+ `Authorization header, never in the URL.`);
189
+ }
190
+ if (parsed.protocol !== 'https:') {
191
+ if (process.env.CSPR402_ALLOW_INSECURE_BASE_URL === '1' ||
192
+ process.env.CARDS402_ALLOW_INSECURE_BASE_URL === '1') {
193
+ return parsed.toString();
194
+ }
195
+ throw new Error(`Refusing to use non-HTTPS base URL (${url})${opts.context ? ` for ${opts.context}` : ''}. ` +
196
+ `Set CARDS402_ALLOW_INSECURE_BASE_URL=1 to override for local development.`);
197
+ }
198
+ return parsed.toString();
199
+ }
200
+ /**
201
+ * Write the config file atomically with 0600 permissions so only the
202
+ * owner can read it. Creates the parent directory on demand.
203
+ *
204
+ * Atomicity: write to `<path>.tmp-<pid>-<rand>` first, fsync, then
205
+ * rename over the target. A mid-write crash (power loss, OOM, Ctrl-C
206
+ * between write and flush) leaves the old file intact instead of a
207
+ * truncated new one that loadCards402Config would explode on.
208
+ *
209
+ * Permission hardening: the `mode` option on writeFileSync only
210
+ * applies when the file is being CREATED, so a stale 0644 file from
211
+ * an earlier buggy version would retain its wide permissions forever.
212
+ * We fsync+rename so the temp path is always freshly created with
213
+ * 0600, then the rename replaces the target atomically.
214
+ */
215
+ function saveCards402Config(config, configPath) {
216
+ const p = configPath || defaultConfigPath();
217
+ const dir = path.dirname(p);
218
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
219
+ // F2-config: mkdirSync's mode option only applies to directories
220
+ // it actually CREATES. An existing ~/.cards402 directory at 0755
221
+ // (from an older buggy SDK version, a package install, or a
222
+ // manual mkdir) silently stays loose, and the config file sits
223
+ // inside a world-traversable parent. Explicit chmod after mkdir
224
+ // guarantees the directory ends up at 0700 regardless of its
225
+ // pre-existing state. Skip on Windows — mode bits are simulated
226
+ // and the chmod can fail or no-op unpredictably.
227
+ if (process.platform !== 'win32') {
228
+ try {
229
+ fs.chmodSync(dir, 0o700);
230
+ }
231
+ catch {
232
+ /* non-fatal — best effort */
233
+ }
234
+ }
235
+ // F3-config: crypto.randomBytes is strictly safer than Math.random
236
+ // for a temp-file suffix. Collision is already near-zero in
237
+ // practice but Math.random is seeded from the clock — two
238
+ // containers starting in the same millisecond could in principle
239
+ // produce the same sequence. Crypto random adds no meaningful
240
+ // cost and eliminates the class of concern.
241
+ const tmp = `${p}.tmp-${process.pid}-${crypto.randomBytes(4).toString('hex')}`;
242
+ const body = JSON.stringify(config, null, 2);
243
+ let committed = false;
244
+ try {
245
+ const fd = fs.openSync(tmp, 'w', 0o600);
246
+ try {
247
+ fs.writeFileSync(fd, body);
248
+ fs.fsyncSync(fd);
249
+ }
250
+ finally {
251
+ fs.closeSync(fd);
252
+ }
253
+ // Atomic rename. POSIX guarantees this replaces an existing file
254
+ // with the same semantics; on Windows rename-over-existing also
255
+ // works from Node 10+.
256
+ fs.renameSync(tmp, p);
257
+ committed = true;
258
+ }
259
+ finally {
260
+ // F3-config: clean up the temp file on any failure before the
261
+ // rename commits. A leaked ~/.cards402/config.json.tmp-* file
262
+ // holding a fresh api_key would otherwise linger on disk with
263
+ // the same 0600 permissions as the target but under a path no
264
+ // one checks — easy to miss during credential rotation.
265
+ if (!committed) {
266
+ try {
267
+ fs.unlinkSync(tmp);
268
+ }
269
+ catch {
270
+ /* temp already gone or never created — fine */
271
+ }
272
+ }
273
+ }
274
+ // Belt-and-braces: some filesystems (FAT on USB sticks) drop the
275
+ // mode on rename. Force-tighten after.
276
+ try {
277
+ fs.chmodSync(p, 0o600);
278
+ }
279
+ catch {
280
+ /* non-fatal — best effort */
281
+ }
282
+ return { path: p };
283
+ }
284
+ /**
285
+ * Resolve an api key + base URL at SDK call time, in priority order:
286
+ * 1. Explicit `apiKey` / `baseUrl` passed to the call
287
+ * 2. CSPR402_API_KEY / CSPR402_BASE_URL env vars
288
+ * 3. ~/.cspr402/config.json
289
+ *
290
+ * The two fields resolve independently — passing `apiKey` to a call
291
+ * that needs its `baseUrl` to come from config.json used to silently
292
+ * drop the config lookup because the early-return on `opts.apiKey`
293
+ * was only consulting env vars for baseUrl. Now both fields walk the
294
+ * full priority chain and only stop once each is filled.
295
+ */
296
+ function resolveCredentials(opts = {}) {
297
+ let apiKey = opts.apiKey;
298
+ let baseUrl = opts.baseUrl;
299
+ if (!apiKey && process.env.CSPR402_API_KEY)
300
+ apiKey = process.env.CSPR402_API_KEY;
301
+ if (!apiKey && process.env.CARDS402_API_KEY)
302
+ apiKey = process.env.CARDS402_API_KEY;
303
+ if (!baseUrl && process.env.CSPR402_BASE_URL)
304
+ baseUrl = process.env.CSPR402_BASE_URL;
305
+ if (!baseUrl && process.env.CARDS402_BASE_URL)
306
+ baseUrl = process.env.CARDS402_BASE_URL;
307
+ if (!apiKey || !baseUrl) {
308
+ // Only load config if at least one field is still missing — saves
309
+ // a filesystem read on the common case where env + opts fully cover it.
310
+ const cfg = loadCards402Config();
311
+ if (cfg) {
312
+ if (!apiKey)
313
+ apiKey = cfg.api_key;
314
+ if (!baseUrl)
315
+ baseUrl = cfg.api_url;
316
+ }
317
+ }
318
+ // Refuse any non-HTTPS baseUrl (env, opts, or config) unless the
319
+ // explicit local-dev escape hatch is set. Without this, an attacker
320
+ // who tricks the user into setting CARDS402_BASE_URL=http://evil/
321
+ // sees the api key in every request's Authorization header. The
322
+ // assertSafeBaseUrl helper throws on reject — we let it propagate
323
+ // rather than silently continue with an insecure URL.
324
+ if (baseUrl) {
325
+ baseUrl = assertSafeBaseUrl(baseUrl, { context: 'resolveCredentials' });
326
+ }
327
+ return { apiKey, baseUrl };
328
+ }
329
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":";AAAA,sEAAsE;AACtE,sEAAsE;AACtE,uEAAuE;AACvE,EAAE;AACF,oEAAoE;AACpE,oEAAoE;AACpE,oEAAoE;AACpE,mEAAmE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkEnE,gDA8EC;AAiBD,8CAkCC;AAiBD,gDAgEC;AAcD,gDAmCC;AAnUD,uCAAyB;AACzB,2CAA6B;AAC7B,uCAAyB;AACzB,+CAAiC;AAEjC,oEAAoE;AACpE,kEAAkE;AAClE,qEAAqE;AACrE,6DAA6D;AAC7D,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,CAAC;AAuBnC,SAAS,gBAAgB;IACvB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,kBAAkB;QAC9B,OAAO,CAAC,GAAG,CAAC,mBAAmB;QAC/B,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,UAAU,CAAC,CACpC,CAAC;AACJ,CAAC;AAED,SAAS,iBAAiB;IACxB,OAAO,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,EAAE,aAAa,CAAC,CAAC;AACtD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,sEAAsE;AACtE,uEAAuE;AACvE,qEAAqE;AACrE,yEAAyE;AACzE,wEAAwE;AACxE,8EAA8E;AAC9E,MAAM,aAAa,GAAG,gBAAgB,CAAC;AAEvC,SAAgB,kBAAkB,CAAC,UAAmB;IACpD,MAAM,CAAC,GAAG,UAAU,IAAI,iBAAiB,EAAE,CAAC;IAC5C,IAAI,CAAC;QACH,gEAAgE;QAChE,iEAAiE;QACjE,iDAAiD;QACjD,6DAA6D;QAC7D,gEAAgE;QAChE,mEAAmE;QACnE,+DAA+D;QAC/D,8DAA8D;QAC9D,8DAA8D;QAC9D,yCAAyC;QACzC,IAAI,IAAc,CAAC;QACnB,IAAI,CAAC;YACH,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;QAAC,OAAO,OAAgB,EAAE,CAAC;YAC1B,IAAK,OAAiC,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,IAAI,CAAC;YACtE,MAAM,OAAO,CAAC;QAChB,CAAC;QACD,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CACb,sBAAsB,CAAC,yCAAyC;gBAC9D,qFAAqF,CACxF,CAAC;QACJ,CAAC;QACD,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC;YACnB,MAAM,IAAI,KAAK,CACb,sBAAsB,CAAC,0BAA0B;gBAC/C,yDAAyD,CAC5D,CAAC;QACJ,CAAC;QACD,6DAA6D;QAC7D,2DAA2D;QAC3D,wDAAwD;QACxD,qDAAqD;QACrD,IAAI,IAAI,CAAC,IAAI,GAAG,gBAAgB,EAAE,CAAC;YACjC,MAAM,IAAI,KAAK,CACb,sBAAsB,CAAC,OAAO,IAAI,CAAC,IAAI,eAAe,gBAAgB,KAAK;gBACzE,6EAA6E;gBAC7E,sEAAsE,CACzE,CAAC;QACJ,CAAC;QACD,+DAA+D;QAC/D,+DAA+D;QAC/D,iEAAiE;QACjE,uDAAuD;QACvD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YAC9D,IAAI,CAAC;gBACH,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;gBACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,wBAAwB,CAAC,2BAA2B,CAAC,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,yBAAyB;oBAC1G,oEAAoE,CACvE,CAAC;YACJ,CAAC;YAAC,MAAM,CAAC;gBACP,mCAAmC;YACrC,CAAC;QACH,CAAC;QAED,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAmB,CAAC;QAEjD,mEAAmE;QACnE,kEAAkE;QAClE,uDAAuD;QACvD,sCAAsC;QACtC,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YAC9E,MAAM,IAAI,KAAK,CACb,sBAAsB,CAAC,sDAAsD;gBAC3E,4FAA4F,CAC/F,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAC;QAClE,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,iBAAiB,CAAC,GAAW,EAAE,OAA6B,EAAE;IAC5E,IAAI,MAAW,CAAC;IAChB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,qBAAqB,GAAG,EAAE,CAAC,CAAC;IAC9C,CAAC;IACD,+EAA+E;IAC/E,6EAA6E;IAC7E,2EAA2E;IAC3E,kEAAkE;IAClE,yEAAyE;IACzE,0EAA0E;IAC1E,eAAe;IACf,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE,IAAI,MAAM,CAAC,QAAQ,KAAK,EAAE,EAAE,CAAC;QACrD,MAAM,IAAI,KAAK,CACb,qBAAqB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,8BAA8B;YACpE,kEAAkE;YAClE,yCAAyC,CAC5C,CAAC;IACJ,CAAC;IACD,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACjC,IACE,OAAO,CAAC,GAAG,CAAC,+BAA+B,KAAK,GAAG;YACnD,OAAO,CAAC,GAAG,CAAC,gCAAgC,KAAK,GAAG,EACpD,CAAC;YACD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC3B,CAAC;QACD,MAAM,IAAI,KAAK,CACb,uCAAuC,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI;YAC1F,2EAA2E,CAC9E,CAAC;IACJ,CAAC;IACD,OAAO,MAAM,CAAC,QAAQ,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,SAAgB,kBAAkB,CAAC,MAAsB,EAAE,UAAmB;IAC5E,MAAM,CAAC,GAAG,UAAU,IAAI,iBAAiB,EAAE,CAAC;IAC5C,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;IAC5B,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,iEAAiE;IACjE,iEAAiE;IACjE,4DAA4D;IAC5D,+DAA+D;IAC/D,gEAAgE;IAChE,6DAA6D;IAC7D,gEAAgE;IAChE,iDAAiD;IACjD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,IAAI,CAAC;YACH,EAAE,CAAC,SAAS,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,6BAA6B;QAC/B,CAAC;IACH,CAAC;IAED,mEAAmE;IACnE,4DAA4D;IAC5D,0DAA0D;IAC1D,iEAAiE;IACjE,8DAA8D;IAC9D,4CAA4C;IAC5C,MAAM,GAAG,GAAG,GAAG,CAAC,QAAQ,OAAO,CAAC,GAAG,IAAI,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;IAC/E,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7C,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC;YACH,EAAE,CAAC,aAAa,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAC3B,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACnB,CAAC;gBAAS,CAAC;YACT,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;QACnB,CAAC;QACD,iEAAiE;QACjE,gEAAgE;QAChE,uBAAuB;QACvB,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACtB,SAAS,GAAG,IAAI,CAAC;IACnB,CAAC;YAAS,CAAC;QACT,8DAA8D;QAC9D,8DAA8D;QAC9D,8DAA8D;QAC9D,8DAA8D;QAC9D,wDAAwD;QACxD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC;gBACP,+CAA+C;YACjD,CAAC;QACH,CAAC;IACH,CAAC;IACD,iEAAiE;IACjE,uCAAuC;IACvC,IAAI,CAAC;QACH,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,6BAA6B;IAC/B,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;AACrB,CAAC;AAED;;;;;;;;;;;GAWG;AACH,SAAgB,kBAAkB,CAChC,OAGI,EAAE;IAEN,IAAI,MAAM,GAAuB,IAAI,CAAC,MAAM,CAAC;IAC7C,IAAI,OAAO,GAAuB,IAAI,CAAC,OAAO,CAAC;IAE/C,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACjF,IAAI,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACnF,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAAE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACrF,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB;QAAE,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;IAEvF,IAAI,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC;QACxB,kEAAkE;QAClE,wEAAwE;QACxE,MAAM,GAAG,GAAG,kBAAkB,EAAE,CAAC;QACjC,IAAI,GAAG,EAAE,CAAC;YACR,IAAI,CAAC,MAAM;gBAAE,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC;YAClC,IAAI,CAAC,OAAO;gBAAE,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;QACtC,CAAC;IACH,CAAC;IAED,iEAAiE;IACjE,oEAAoE;IACpE,kEAAkE;IAClE,gEAAgE;IAChE,kEAAkE;IAClE,sDAAsD;IACtD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,GAAG,iBAAiB,CAAC,OAAO,EAAE,EAAE,OAAO,EAAE,oBAAoB,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Structured error types for the cards402 SDK.
3
+ *
4
+ * Catch by type so your agent can handle each case without string-parsing:
5
+ *
6
+ * try {
7
+ * const card = await client.createOrder({ amount_usdc: '10.00' });
8
+ * } catch (err) {
9
+ * if (err instanceof SpendLimitError) { ... }
10
+ * if (err instanceof ServiceUnavailableError) { ... }
11
+ * }
12
+ */
13
+ /** Base class — all cards402 errors extend this. */
14
+ export declare class Cards402Error extends Error {
15
+ readonly code: string;
16
+ readonly status: number;
17
+ readonly raw?: unknown | undefined;
18
+ constructor(message: string, code: string, status: number, raw?: unknown | undefined);
19
+ }
20
+ /** The API key's spend limit has been reached. */
21
+ export declare class SpendLimitError extends Cards402Error {
22
+ readonly limit: string;
23
+ readonly spent: string;
24
+ constructor(limit: string, spent: string);
25
+ }
26
+ /**
27
+ * Rate limit hit. The backend returns `rate_limit_exceeded` for two
28
+ * different limits — 60 orders/hour on POST /v1/orders, and 10
29
+ * requests/second (600/minute) on GET /v1/orders/:id status polling.
30
+ * Which one fired is in the server's `message` field, not in the
31
+ * error code, so we forward the message verbatim instead of hardcoding
32
+ * the wrong explanation.
33
+ */
34
+ export declare class RateLimitError extends Cards402Error {
35
+ constructor(message?: string);
36
+ }
37
+ /** Service is temporarily suspended (fulfillment circuit breaker tripped). */
38
+ export declare class ServiceUnavailableError extends Cards402Error {
39
+ constructor(message?: string);
40
+ }
41
+ /** XLM price feed is unavailable — retry or use USDC. */
42
+ export declare class PriceUnavailableError extends Cards402Error {
43
+ constructor(message?: string);
44
+ }
45
+ /**
46
+ * amount_usdc was missing, zero, non-numeric, or outside the bounds
47
+ * [0.01, 10000]. The message defaults to the full-range explanation
48
+ * but is overridden by whatever the backend sent so a call with e.g.
49
+ * "9.99999999" (too many decimals) gets the specific reason instead
50
+ * of the generic bounds message.
51
+ */
52
+ export declare class InvalidAmountError extends Cards402Error {
53
+ constructor(message?: string);
54
+ }
55
+ /** The API key is missing or invalid. */
56
+ export declare class AuthError extends Cards402Error {
57
+ constructor();
58
+ }
59
+ /** Order failed during fulfillment. A refund may be in progress. */
60
+ export declare class OrderFailedError extends Cards402Error {
61
+ readonly orderId: string;
62
+ readonly refund?: {
63
+ stellar_txid: string;
64
+ } | undefined;
65
+ constructor(orderId: string, reason: string, refund?: {
66
+ stellar_txid: string;
67
+ } | undefined);
68
+ }
69
+ /**
70
+ * Thrown by purchaseCardOWS when the order was created (and possibly paid)
71
+ * but the flow couldn't finish — e.g. Soroban RPC finalization timed out,
72
+ * or waitForCard hit its deadline. Always carries the orderId so the caller
73
+ * can resume via `cards402 purchase --resume <order-id>` without minting a
74
+ * new order (which would strand the original one until it expires).
75
+ *
76
+ * `txHash` is present when the payment was submitted onto the ledger (even
77
+ * if finalization wasn't confirmed client-side) — the backend watcher can
78
+ * still credit the order after the fact.
79
+ *
80
+ * `phase: 'paid'` means payViaContractOWS returned successfully, so resume
81
+ * should skip straight to waitForCard. `phase: 'unpaid'` means the payment
82
+ * never went out and resume may need to retry the Soroban submit.
83
+ */
84
+ export declare class ResumableError extends Cards402Error {
85
+ readonly orderId: string;
86
+ readonly phase: 'unpaid' | 'paid';
87
+ readonly txHash?: string | undefined;
88
+ readonly cause?: unknown | undefined;
89
+ constructor(orderId: string, reason: string, phase: 'unpaid' | 'paid', txHash?: string | undefined, cause?: unknown | undefined);
90
+ }
91
+ /** Waiting for a card timed out — order may still be processing. */
92
+ export declare class WaitTimeoutError extends Cards402Error {
93
+ readonly orderId: string;
94
+ constructor(orderId: string, timeoutMs: number);
95
+ }
96
+ /**
97
+ * Parse a raw API error response into the appropriate typed error.
98
+ * Falls back to generic Cards402Error for unknown codes.
99
+ */
100
+ export declare function parseApiError(status: number, body: Record<string, unknown>): Cards402Error;
101
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,oDAAoD;AACpD,qBAAa,aAAc,SAAQ,KAAK;aAGpB,IAAI,EAAE,MAAM;aACZ,MAAM,EAAE,MAAM;aACd,GAAG,CAAC,EAAE,OAAO;gBAH7B,OAAO,EAAE,MAAM,EACC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,GAAG,CAAC,EAAE,OAAO,YAAA;CAMhC;AAED,kDAAkD;AAClD,qBAAa,eAAgB,SAAQ,aAAa;aAE9B,KAAK,EAAE,MAAM;aACb,KAAK,EAAE,MAAM;gBADb,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM;CAUhC;AAED;;;;;;;GAOG;AACH,qBAAa,cAAe,SAAQ,aAAa;gBACnC,OAAO,SAA6C;CAKjE;AAED,8EAA8E;AAC9E,qBAAa,uBAAwB,SAAQ,aAAa;gBAC5C,OAAO,SAAuE;CAK3F;AAED,yDAAyD;AACzD,qBAAa,qBAAsB,SAAQ,aAAa;gBAEpD,OAAO,SAAuF;CAMjG;AAED;;;;;;GAMG;AACH,qBAAa,kBAAmB,SAAQ,aAAa;gBAEjD,OAAO,SAAiG;CAM3G;AAED,yCAAyC;AACzC,qBAAa,SAAU,SAAQ,aAAa;;CAU3C;AAED,oEAAoE;AACpE,qBAAa,gBAAiB,SAAQ,aAAa;aAE/B,OAAO,EAAE,MAAM;aAEf,MAAM,CAAC,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE;gBAFjC,OAAO,EAAE,MAAM,EAC/B,MAAM,EAAE,MAAM,EACE,MAAM,CAAC,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,YAAA;CAapD;AAED;;;;;;;;;;;;;;GAcG;AACH,qBAAa,cAAe,SAAQ,aAAa;aAE7B,OAAO,EAAE,MAAM;aAEf,KAAK,EAAE,QAAQ,GAAG,MAAM;aACxB,MAAM,CAAC,EAAE,MAAM;aACf,KAAK,CAAC,EAAE,OAAO;gBAJf,OAAO,EAAE,MAAM,EAC/B,MAAM,EAAE,MAAM,EACE,KAAK,EAAE,QAAQ,GAAG,MAAM,EACxB,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,KAAK,CAAC,EAAE,OAAO,YAAA;CAalC;AAED,oEAAoE;AACpE,qBAAa,gBAAiB,SAAQ,aAAa;aAE/B,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM,EAC/B,SAAS,EAAE,MAAM;CAWpB;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,aAAa,CAyB1F"}
package/dist/errors.js ADDED
@@ -0,0 +1,197 @@
1
+ "use strict";
2
+ /**
3
+ * Structured error types for the cards402 SDK.
4
+ *
5
+ * Catch by type so your agent can handle each case without string-parsing:
6
+ *
7
+ * try {
8
+ * const card = await client.createOrder({ amount_usdc: '10.00' });
9
+ * } catch (err) {
10
+ * if (err instanceof SpendLimitError) { ... }
11
+ * if (err instanceof ServiceUnavailableError) { ... }
12
+ * }
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.WaitTimeoutError = exports.ResumableError = exports.OrderFailedError = exports.AuthError = exports.InvalidAmountError = exports.PriceUnavailableError = exports.ServiceUnavailableError = exports.RateLimitError = exports.SpendLimitError = exports.Cards402Error = void 0;
16
+ exports.parseApiError = parseApiError;
17
+ /** Base class — all cards402 errors extend this. */
18
+ class Cards402Error extends Error {
19
+ code;
20
+ status;
21
+ raw;
22
+ constructor(message, code, status, raw) {
23
+ super(message);
24
+ this.code = code;
25
+ this.status = status;
26
+ this.raw = raw;
27
+ this.name = 'Cards402Error';
28
+ Object.setPrototypeOf(this, new.target.prototype);
29
+ }
30
+ }
31
+ exports.Cards402Error = Cards402Error;
32
+ /** The API key's spend limit has been reached. */
33
+ class SpendLimitError extends Cards402Error {
34
+ limit;
35
+ spent;
36
+ constructor(limit, spent) {
37
+ super(`Spend limit exceeded: $${spent} spent of $${limit} limit. Ask your operator to raise the limit or wait for the next reset period.`, 'spend_limit_exceeded', 403);
38
+ this.limit = limit;
39
+ this.spent = spent;
40
+ this.name = 'SpendLimitError';
41
+ Object.setPrototypeOf(this, new.target.prototype);
42
+ }
43
+ }
44
+ exports.SpendLimitError = SpendLimitError;
45
+ /**
46
+ * Rate limit hit. The backend returns `rate_limit_exceeded` for two
47
+ * different limits — 60 orders/hour on POST /v1/orders, and 10
48
+ * requests/second (600/minute) on GET /v1/orders/:id status polling.
49
+ * Which one fired is in the server's `message` field, not in the
50
+ * error code, so we forward the message verbatim instead of hardcoding
51
+ * the wrong explanation.
52
+ */
53
+ class RateLimitError extends Cards402Error {
54
+ constructor(message = 'Rate limit exceeded. Back off and retry.') {
55
+ super(message, 'rate_limit_exceeded', 429);
56
+ this.name = 'RateLimitError';
57
+ Object.setPrototypeOf(this, new.target.prototype);
58
+ }
59
+ }
60
+ exports.RateLimitError = RateLimitError;
61
+ /** Service is temporarily suspended (fulfillment circuit breaker tripped). */
62
+ class ServiceUnavailableError extends Cards402Error {
63
+ constructor(message = 'Card fulfillment is temporarily suspended. Retry in a few minutes.') {
64
+ super(message, 'service_temporarily_unavailable', 503);
65
+ this.name = 'ServiceUnavailableError';
66
+ Object.setPrototypeOf(this, new.target.prototype);
67
+ }
68
+ }
69
+ exports.ServiceUnavailableError = ServiceUnavailableError;
70
+ /** XLM price feed is unavailable — retry or use USDC. */
71
+ class PriceUnavailableError extends Cards402Error {
72
+ constructor(message = 'XLM price is temporarily unavailable. Retry shortly, or use payment_asset: "usdc".') {
73
+ super(message, 'price_unavailable', 503);
74
+ this.name = 'PriceUnavailableError';
75
+ Object.setPrototypeOf(this, new.target.prototype);
76
+ }
77
+ }
78
+ exports.PriceUnavailableError = PriceUnavailableError;
79
+ /**
80
+ * amount_usdc was missing, zero, non-numeric, or outside the bounds
81
+ * [0.01, 10000]. The message defaults to the full-range explanation
82
+ * but is overridden by whatever the backend sent so a call with e.g.
83
+ * "9.99999999" (too many decimals) gets the specific reason instead
84
+ * of the generic bounds message.
85
+ */
86
+ class InvalidAmountError extends Cards402Error {
87
+ constructor(message = 'Invalid amount_usdc — must be a decimal string between "0.01" and "10000.00" (e.g. "10.00").') {
88
+ super(message, 'invalid_amount', 400);
89
+ this.name = 'InvalidAmountError';
90
+ Object.setPrototypeOf(this, new.target.prototype);
91
+ }
92
+ }
93
+ exports.InvalidAmountError = InvalidAmountError;
94
+ /** The API key is missing or invalid. */
95
+ class AuthError extends Cards402Error {
96
+ constructor() {
97
+ super('Invalid or missing API key. Pass it as the X-Api-Key header, or set CARDS402_API_KEY.', 'invalid_api_key', 401);
98
+ this.name = 'AuthError';
99
+ Object.setPrototypeOf(this, new.target.prototype);
100
+ }
101
+ }
102
+ exports.AuthError = AuthError;
103
+ /** Order failed during fulfillment. A refund may be in progress. */
104
+ class OrderFailedError extends Cards402Error {
105
+ orderId;
106
+ refund;
107
+ constructor(orderId, reason, refund) {
108
+ const refundNote = refund
109
+ ? ` Your payment is being refunded (txid: ${refund.stellar_txid}).`
110
+ : ' A refund will be processed if payment was received.';
111
+ super(`Order ${orderId} failed: ${reason}.${refundNote}`, 'order_failed', 200, {
112
+ orderId,
113
+ reason,
114
+ refund,
115
+ });
116
+ this.orderId = orderId;
117
+ this.refund = refund;
118
+ this.name = 'OrderFailedError';
119
+ Object.setPrototypeOf(this, new.target.prototype);
120
+ }
121
+ }
122
+ exports.OrderFailedError = OrderFailedError;
123
+ /**
124
+ * Thrown by purchaseCardOWS when the order was created (and possibly paid)
125
+ * but the flow couldn't finish — e.g. Soroban RPC finalization timed out,
126
+ * or waitForCard hit its deadline. Always carries the orderId so the caller
127
+ * can resume via `cards402 purchase --resume <order-id>` without minting a
128
+ * new order (which would strand the original one until it expires).
129
+ *
130
+ * `txHash` is present when the payment was submitted onto the ledger (even
131
+ * if finalization wasn't confirmed client-side) — the backend watcher can
132
+ * still credit the order after the fact.
133
+ *
134
+ * `phase: 'paid'` means payViaContractOWS returned successfully, so resume
135
+ * should skip straight to waitForCard. `phase: 'unpaid'` means the payment
136
+ * never went out and resume may need to retry the Soroban submit.
137
+ */
138
+ class ResumableError extends Cards402Error {
139
+ orderId;
140
+ phase;
141
+ txHash;
142
+ cause;
143
+ constructor(orderId, reason, phase, txHash, cause) {
144
+ const hashNote = txHash ? ` (tx: ${txHash})` : '';
145
+ super(`Purchase could not finish for order ${orderId}: ${reason}${hashNote}. ` +
146
+ `Resume with: cards402 purchase --resume ${orderId}`, 'resumable', 0, { orderId, reason, phase, txHash });
147
+ this.orderId = orderId;
148
+ this.phase = phase;
149
+ this.txHash = txHash;
150
+ this.cause = cause;
151
+ this.name = 'ResumableError';
152
+ Object.setPrototypeOf(this, new.target.prototype);
153
+ }
154
+ }
155
+ exports.ResumableError = ResumableError;
156
+ /** Waiting for a card timed out — order may still be processing. */
157
+ class WaitTimeoutError extends Cards402Error {
158
+ orderId;
159
+ constructor(orderId, timeoutMs) {
160
+ super(`Timed out waiting for card after ${timeoutMs / 1000}s (order: ${orderId}). ` +
161
+ 'Poll GET /v1/orders/:id to check status — it may still complete.', 'wait_timeout', 408);
162
+ this.orderId = orderId;
163
+ this.name = 'WaitTimeoutError';
164
+ Object.setPrototypeOf(this, new.target.prototype);
165
+ }
166
+ }
167
+ exports.WaitTimeoutError = WaitTimeoutError;
168
+ /**
169
+ * Parse a raw API error response into the appropriate typed error.
170
+ * Falls back to generic Cards402Error for unknown codes.
171
+ */
172
+ function parseApiError(status, body) {
173
+ const code = String(body.error ?? 'unknown');
174
+ const message = String(body.message ?? body.error ?? 'Unknown error');
175
+ switch (code) {
176
+ case 'spend_limit_exceeded':
177
+ return new SpendLimitError(String(body.limit ?? '?'), String(body.spent ?? '?'));
178
+ case 'rate_limit_exceeded':
179
+ // Forward the backend message verbatim — the code is shared
180
+ // between order-creation and polling rate limits, and the
181
+ // message is the only way to tell them apart.
182
+ return new RateLimitError(message);
183
+ case 'service_temporarily_unavailable':
184
+ return new ServiceUnavailableError(message);
185
+ case 'price_unavailable':
186
+ case 'xlm_price_unavailable':
187
+ return new PriceUnavailableError(message);
188
+ case 'invalid_amount':
189
+ return new InvalidAmountError(message);
190
+ case 'missing_api_key':
191
+ case 'invalid_api_key':
192
+ return new AuthError();
193
+ default:
194
+ return new Cards402Error(message, code, status, body);
195
+ }
196
+ }
197
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { Cards402Client, CSPR402Client } from './client';
2
+ export type { OrderOptions, OrderResponse, OrderStatus, OrderListItem, OrderPhase, CardDetails, PaymentInstructions, SorobanPaymentInstructions, CasperCSPRPaymentInstructions, MockUsdcCep18PaymentInstructions, CasperCSPRPaymentReceipt, CasperPaymentReceipt, MockUsdcReceipt, VerifyCasperPaymentResponse, Budget, UsageSummary, } from './client';
3
+ export { Cards402Error, SpendLimitError, RateLimitError, ServiceUnavailableError, PriceUnavailableError, InvalidAmountError, AuthError, OrderFailedError, WaitTimeoutError, ResumableError, } from './errors';
4
+ export { loadCards402Config, saveCards402Config, resolveCredentials } from './config';
5
+ export type { Cards402Config } from './config';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzD,YAAY,EACV,YAAY,EACZ,aAAa,EACb,WAAW,EACX,aAAa,EACb,UAAU,EACV,WAAW,EACX,mBAAmB,EACnB,0BAA0B,EAC1B,6BAA6B,EAC7B,gCAAgC,EAChC,wBAAwB,EACxB,oBAAoB,EACpB,eAAe,EACf,2BAA2B,EAC3B,MAAM,EACN,YAAY,GACb,MAAM,UAAU,CAAC;AAElB,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,uBAAuB,EACvB,qBAAqB,EACrB,kBAAkB,EAClB,SAAS,EACT,gBAAgB,EAChB,gBAAgB,EAChB,cAAc,GACf,MAAM,UAAU,CAAC;AAElB,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACtF,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC"}