nonecap 0.1.0 → 0.3.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/README.md +51 -0
- package/dist/index.cjs +132 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +96 -7
- package/dist/index.d.ts +96 -7
- package/dist/index.js +132 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,6 +68,55 @@ try {
|
|
|
68
68
|
|
|
69
69
|
The error subclasses are `AuthenticationError` (401), `PermissionError` (403), `InsufficientCreditsError` (402), `ValidationError` (422/400, with a `param` naming the bad field), `NotFoundError` (404), `ConflictError` (409), `RateLimitError` (429), `APIError` (5xx), and `ConnectionError` (the request never landed). `SolveFailedError` carries the full `solve` so you can read `solve.error` and the timing fields.
|
|
70
70
|
|
|
71
|
+
## Cancelling a solve
|
|
72
|
+
|
|
73
|
+
`solve()` is the simple path when you just want a token. When you need to cancel while the solve is in flight — clean shutdown, freeing a worker slot, or stopping early — start it instead. `solves.start()` returns as soon as the submission is accepted, handing you a `SolveHandle` whose `id` is available right away.
|
|
74
|
+
|
|
75
|
+
Wait for the result when you want the token:
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const handle = await nc.solves.start({ type: "hcaptcha", sitekey, url });
|
|
79
|
+
console.log(handle.id); // available immediately
|
|
80
|
+
|
|
81
|
+
const solve = await handle.result({ timeout: 120_000 });
|
|
82
|
+
console.log(solve.token);
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Or hold onto the handle and cancel it from somewhere else — a shutdown hook, a request that was abandoned, a "stop" button:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
const handle = await nc.solves.start({ type: "hcaptcha", sitekey, url });
|
|
89
|
+
|
|
90
|
+
process.on("SIGTERM", () => {
|
|
91
|
+
handle.cancel().catch(() => {}); // free the worker slot on shutdown
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// elsewhere, whoever needs the token still awaits it:
|
|
95
|
+
const solve = await handle.result();
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`handle.result()` long-polls until the solve finishes, throwing `SolveFailedError` or `TimeoutError` exactly like `solve()`. The **terminal** outcome is memoized: once the solve actually finishes, every later `result()` call returns that same result. A `TimeoutError` is not memoized — it just means that wait gave up, so you can call `result()` again (for example with a larger `timeout`) to resume polling. Concurrent `result()` calls share a single in-flight poll.
|
|
99
|
+
|
|
100
|
+
`handle.cancel()` cancels a pending or in-flight solve. If the solve already finished, the API would answer 409; the handle swallows that and returns the solve's current state instead, since cancelling a finished solve is not something you need to handle. Cancelling settles the handle, so a concurrent or later `result()` stops polling and reflects the cancelled (terminal) state.
|
|
101
|
+
|
|
102
|
+
Cancelled and abandoned solves are **never charged** — nothing is billed unless a solve actually succeeds, and unfinished solves simply expire uncharged at the server deadline. So cancel is for cleanup and early-stop, not cost protection.
|
|
103
|
+
|
|
104
|
+
If you used the bare `solve()` path and it throws `TimeoutError`, the error usually carries the in-flight `solveId` (and last-known `solve`) so you can still cancel it. Guard on `solveId` being present: if the very first submission times out at the transport level, no id has been assigned yet, so `solveId` is `undefined`.
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { TimeoutError } from "nonecap";
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
await nc.solve({ type: "hcaptcha", sitekey, url });
|
|
111
|
+
} catch (err) {
|
|
112
|
+
if (err instanceof TimeoutError && err.solveId) {
|
|
113
|
+
await nc.solves.cancel(err.solveId);
|
|
114
|
+
} else {
|
|
115
|
+
throw err;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
71
120
|
## Enterprise captchas
|
|
72
121
|
|
|
73
122
|
For `hcaptcha_enterprise`, `rqdata` is required. The types enforce it, so leaving it out is a compile error, not a runtime surprise.
|
|
@@ -92,6 +141,8 @@ await nc.solve({
|
|
|
92
141
|
url,
|
|
93
142
|
proxy: { scheme: "http", host: "1.2.3.4", port: 8080, username: "u", password: "p" },
|
|
94
143
|
// or: proxy: "http://u:p@1.2.3.4:8080"
|
|
144
|
+
// scheme can be http, https, socks5, socks5h, or socks4 (default http)
|
|
145
|
+
// e.g. proxy: "socks5://u:p@1.2.3.4:1080"
|
|
95
146
|
});
|
|
96
147
|
```
|
|
97
148
|
|
package/dist/index.cjs
CHANGED
|
@@ -47,6 +47,15 @@ var SolveFailedError = class extends NoneCapError {
|
|
|
47
47
|
}
|
|
48
48
|
};
|
|
49
49
|
var TimeoutError = class extends NoneCapError {
|
|
50
|
+
/** The id of the in-flight solve, when the timeout came from a solve wait. */
|
|
51
|
+
solveId;
|
|
52
|
+
/** The last-known solve state, so you can cancel it after a wait timed out. */
|
|
53
|
+
solve;
|
|
54
|
+
constructor(message, opts = {}) {
|
|
55
|
+
super(message, opts);
|
|
56
|
+
this.solveId = opts.solveId;
|
|
57
|
+
this.solve = opts.solve;
|
|
58
|
+
}
|
|
50
59
|
};
|
|
51
60
|
function errorFromResponse(status, code, message, param) {
|
|
52
61
|
const opts = { code, status, param };
|
|
@@ -129,6 +138,25 @@ var NoneCap = class {
|
|
|
129
138
|
cancel: (id, options = {}) => this.#request("DELETE", `/v1/solves/${encodeURIComponent(id)}`, {
|
|
130
139
|
signal: options.signal
|
|
131
140
|
}),
|
|
141
|
+
/**
|
|
142
|
+
* Submit a solve and return a {@link SolveHandle} for it.
|
|
143
|
+
*
|
|
144
|
+
* Unlike {@link NoneCap.solve}, this returns as soon as the submission is
|
|
145
|
+
* accepted, so `handle.id` is available immediately. Use the handle to
|
|
146
|
+
* `cancel()` the solve or `await handle.result()` for the finished solve.
|
|
147
|
+
*
|
|
148
|
+
* ```ts
|
|
149
|
+
* const handle = await nc.solves.start({ type: "hcaptcha", sitekey, url });
|
|
150
|
+
* // ...later, e.g. on shutdown:
|
|
151
|
+
* await handle.cancel();
|
|
152
|
+
* // or wait for it:
|
|
153
|
+
* const { token } = await handle.result();
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
156
|
+
start: async (params, options = {}) => {
|
|
157
|
+
const solve = await this.solves.create(params, { signal: options.signal });
|
|
158
|
+
return new SolveHandle(this, solve);
|
|
159
|
+
},
|
|
132
160
|
/** Fetch one page of solves. */
|
|
133
161
|
list: (params = {}, options = {}) => this.#request("GET", "/v1/solves", {
|
|
134
162
|
query: {
|
|
@@ -158,28 +186,14 @@ var NoneCap = class {
|
|
|
158
186
|
* Uses the server's long-poll under the hood and keeps polling until the
|
|
159
187
|
* solve is terminal or `timeout` elapses. Throws {@link SolveFailedError} if
|
|
160
188
|
* the solve fails/expires/is cancelled, or {@link TimeoutError} on timeout.
|
|
189
|
+
*
|
|
190
|
+
* This is sugar for `(await solves.start(params)).result(options)`. When you
|
|
191
|
+
* need to cancel the solve while it runs, use {@link NoneCap.solves.start}
|
|
192
|
+
* and keep the {@link SolveHandle}.
|
|
161
193
|
*/
|
|
162
194
|
async solve(params, options = {}) {
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
let solve = await this.solves.create(params, {
|
|
166
|
-
wait: waitSeconds(deadline),
|
|
167
|
-
signal: options.signal
|
|
168
|
-
});
|
|
169
|
-
while (!isTerminal(solve.status)) {
|
|
170
|
-
if (Date.now() >= deadline) {
|
|
171
|
-
throw new TimeoutError(
|
|
172
|
-
`Solve ${solve.id} did not finish within ${timeout}ms (last status: ${solve.status}).`,
|
|
173
|
-
{ code: void 0 }
|
|
174
|
-
);
|
|
175
|
-
}
|
|
176
|
-
solve = await this.solves.retrieve(solve.id, {
|
|
177
|
-
wait: waitSeconds(deadline),
|
|
178
|
-
signal: options.signal
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
if (solve.status !== "solved") throw new SolveFailedError(solve);
|
|
182
|
-
return solve;
|
|
195
|
+
const handle = await this.solves.start(params, { signal: options.signal });
|
|
196
|
+
return handle.result(options);
|
|
183
197
|
}
|
|
184
198
|
// -- internals ------------------------------------------------------------
|
|
185
199
|
#timeoutForWait(wait) {
|
|
@@ -268,6 +282,103 @@ var NoneCap = class {
|
|
|
268
282
|
);
|
|
269
283
|
}
|
|
270
284
|
};
|
|
285
|
+
var SolveHandle = class {
|
|
286
|
+
/** The id of the started solve, available as soon as `start()` resolved. */
|
|
287
|
+
id;
|
|
288
|
+
#client;
|
|
289
|
+
/** The latest solve state we have seen, from the create, polls, or cancel. */
|
|
290
|
+
#solve;
|
|
291
|
+
/**
|
|
292
|
+
* The cached TERMINAL outcome: resolves to the solved solve, or rejects with
|
|
293
|
+
* {@link SolveFailedError} if terminal-but-not-solved. Only ever set once the
|
|
294
|
+
* solve actually reaches a terminal state, so it is safe to replay forever. A
|
|
295
|
+
* timed-out wait does NOT settle this — it is retryable.
|
|
296
|
+
*/
|
|
297
|
+
#terminal;
|
|
298
|
+
/** The poll currently in flight, shared by concurrent result() callers. */
|
|
299
|
+
#inflight;
|
|
300
|
+
constructor(client, solve) {
|
|
301
|
+
this.#client = client;
|
|
302
|
+
this.#solve = solve;
|
|
303
|
+
this.id = solve.id;
|
|
304
|
+
}
|
|
305
|
+
/** The last-known solve state for this handle. */
|
|
306
|
+
get solve() {
|
|
307
|
+
return this.#solve;
|
|
308
|
+
}
|
|
309
|
+
/**
|
|
310
|
+
* Long-poll until the solve is terminal, returning the solved solve. Throws
|
|
311
|
+
* {@link SolveFailedError} if it fails/expires/is cancelled, or
|
|
312
|
+
* {@link TimeoutError} on timeout (with `solveId`/`solve` attached so you can
|
|
313
|
+
* still {@link SolveHandle.cancel} it).
|
|
314
|
+
*
|
|
315
|
+
* Memoizes the *terminal* outcome: once the solve actually finishes, every
|
|
316
|
+
* later call returns that same settled result. A {@link TimeoutError} is not
|
|
317
|
+
* memoized — it just means this wait gave up, so a later call (e.g. with a
|
|
318
|
+
* larger `timeout`) resumes polling. Concurrent calls share one in-flight
|
|
319
|
+
* poll, so they never issue duplicate requests. The first caller's
|
|
320
|
+
* `timeout`/`signal` drive the shared poll; a later call starts a fresh one.
|
|
321
|
+
*/
|
|
322
|
+
result(options = {}) {
|
|
323
|
+
if (this.#terminal) return this.#terminal;
|
|
324
|
+
if (this.#inflight) return this.#inflight;
|
|
325
|
+
const run = this.#poll(options).finally(() => {
|
|
326
|
+
if (this.#inflight === run) this.#inflight = void 0;
|
|
327
|
+
});
|
|
328
|
+
this.#inflight = run;
|
|
329
|
+
return run;
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Cancel the solve. Cancelled solves are never charged, so this is for clean
|
|
333
|
+
* shutdown / freeing slots / stopping early, not cost protection.
|
|
334
|
+
*
|
|
335
|
+
* If the solve already reached a terminal state the API answers 409; that is
|
|
336
|
+
* not an error a caller cares about, so this swallows {@link ConflictError}
|
|
337
|
+
* and returns the current solve state instead. A cancel that lands on a
|
|
338
|
+
* terminal state (cancelled, or whatever the 409-swallow read back) settles
|
|
339
|
+
* this handle's {@link SolveHandle.result}, so any in-flight or later wait
|
|
340
|
+
* stops polling and reflects that terminal state.
|
|
341
|
+
*/
|
|
342
|
+
async cancel(options = {}) {
|
|
343
|
+
try {
|
|
344
|
+
this.#solve = await this.#client.solves.cancel(this.id, options);
|
|
345
|
+
} catch (err) {
|
|
346
|
+
if (!(err instanceof ConflictError)) throw err;
|
|
347
|
+
this.#solve = await this.#client.solves.retrieve(this.id, options);
|
|
348
|
+
}
|
|
349
|
+
if (!this.#terminal && isTerminal(this.#solve.status)) this.#cacheTerminal();
|
|
350
|
+
return this.#solve;
|
|
351
|
+
}
|
|
352
|
+
/**
|
|
353
|
+
* Build and memoize the terminal outcome from the current `#solve`. The
|
|
354
|
+
* precondition is that `#solve` is terminal and `#terminal` is unset.
|
|
355
|
+
*/
|
|
356
|
+
#cacheTerminal() {
|
|
357
|
+
const settled = this.#solve.status === "solved" ? Promise.resolve(this.#solve) : Promise.reject(new SolveFailedError(this.#solve));
|
|
358
|
+
settled.catch(() => {
|
|
359
|
+
});
|
|
360
|
+
this.#terminal = settled;
|
|
361
|
+
return settled;
|
|
362
|
+
}
|
|
363
|
+
async #poll(options) {
|
|
364
|
+
const timeout = options.timeout ?? 18e4;
|
|
365
|
+
const deadline = Date.now() + timeout;
|
|
366
|
+
for (; ; ) {
|
|
367
|
+
if (this.#terminal) return this.#terminal;
|
|
368
|
+
if (isTerminal(this.#solve.status)) return this.#cacheTerminal();
|
|
369
|
+
if (Date.now() >= deadline) {
|
|
370
|
+
throw new TimeoutError(
|
|
371
|
+
`Solve ${this.id} did not finish within ${timeout}ms (last status: ${this.#solve.status}).`,
|
|
372
|
+
{ solveId: this.id, solve: this.#solve }
|
|
373
|
+
);
|
|
374
|
+
}
|
|
375
|
+
this.#solve = await this.#client.solves.retrieve(this.id, {
|
|
376
|
+
wait: waitSeconds(deadline),
|
|
377
|
+
signal: options.signal
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
};
|
|
271
382
|
function waitSeconds(deadline) {
|
|
272
383
|
const remaining = Math.ceil((deadline - Date.now()) / 1e3);
|
|
273
384
|
return Math.max(1, Math.min(MAX_WAIT_SECONDS, remaining));
|
|
@@ -287,6 +398,7 @@ exports.NotFoundError = NotFoundError;
|
|
|
287
398
|
exports.PermissionError = PermissionError;
|
|
288
399
|
exports.RateLimitError = RateLimitError;
|
|
289
400
|
exports.SolveFailedError = SolveFailedError;
|
|
401
|
+
exports.SolveHandle = SolveHandle;
|
|
290
402
|
exports.TimeoutError = TimeoutError;
|
|
291
403
|
exports.ValidationError = ValidationError;
|
|
292
404
|
exports.isTerminal = isTerminal;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ErrorCode, Solve } from \"./types.js\";\n\n/**\n * Base class for every error this library throws. Catch `NoneCapError` to\n * handle all of them, or catch a subclass to handle one kind.\n */\nexport class NoneCapError extends Error {\n /** Machine-readable error code from the API envelope, when there is one. */\n readonly code: ErrorCode | undefined;\n /** HTTP status, when the error came from a response. */\n readonly status: number | undefined;\n /** The request field that was rejected, for validation errors. */\n readonly param: string | null;\n\n constructor(\n message: string,\n opts: { code?: ErrorCode; status?: number; param?: string | null } = {},\n ) {\n super(message);\n this.name = new.target.name;\n this.code = opts.code;\n this.status = opts.status;\n this.param = opts.param ?? null;\n // Restore the prototype chain when compiled down to ES5-era targets.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** 401 — the API key is missing, malformed, or revoked. */\nexport class AuthenticationError extends NoneCapError {}\n\n/** 403 — the key is valid but not allowed to do this (scope or locked account). */\nexport class PermissionError extends NoneCapError {}\n\n/** 402 — the account is out of credits. */\nexport class InsufficientCreditsError extends NoneCapError {}\n\n/** 422 / 400 — the request was rejected. `param` names the offending field. */\nexport class ValidationError extends NoneCapError {}\n\n/** 404 — no such resource. */\nexport class NotFoundError extends NoneCapError {}\n\n/** 409 — the solve is already in a terminal state (e.g. cancelling a finished solve). */\nexport class ConflictError extends NoneCapError {}\n\n/** 429 — too many concurrent solves, or rate limited. Back off and retry. */\nexport class RateLimitError extends NoneCapError {}\n\n/** 5xx, or a response that wasn't the expected shape. */\nexport class APIError extends NoneCapError {}\n\n/** The request never reached the API (DNS, TCP, TLS, timeout, offline). */\nexport class ConnectionError extends NoneCapError {}\n\n/**\n * Thrown by {@link NoneCap.solve} when a solve reaches a terminal state without\n * a token: `failed`, `expired`, or `cancelled`. The full solve is attached so\n * you can inspect `solve.error` and timings.\n */\nexport class SolveFailedError extends NoneCapError {\n readonly solve: Solve;\n\n constructor(solve: Solve) {\n const detail = solve.error ? `${solve.error.code}: ${solve.error.message}` : solve.status;\n super(`Solve ${solve.id} ${solve.status} (${detail})`, {\n code: undefined,\n status: undefined,\n });\n this.solve = solve;\n }\n}\n\n/** Thrown by {@link NoneCap.solve} when the client-side timeout elapses first. */\nexport class TimeoutError extends NoneCapError {}\n\n/** Map an API error envelope (plus HTTP status) to the right error subclass. */\nexport function errorFromResponse(\n status: number,\n code: ErrorCode | undefined,\n message: string,\n param: string | null,\n): NoneCapError {\n const opts = { code, status, param };\n switch (code) {\n case \"unauthorized\":\n return new AuthenticationError(message, opts);\n case \"forbidden\":\n case \"account_locked\":\n return new PermissionError(message, opts);\n case \"insufficient_credits\":\n return new InsufficientCreditsError(message, opts);\n case \"invalid_request\":\n case \"validation_error\":\n return new ValidationError(message, opts);\n case \"not_found\":\n return new NotFoundError(message, opts);\n case \"conflict\":\n return new ConflictError(message, opts);\n case \"rate_limited\":\n case \"concurrency_limit_exceeded\":\n case \"ext_daily_limit\":\n return new RateLimitError(message, opts);\n default:\n return new APIError(message, opts);\n }\n}\n","import {\n APIError,\n ConnectionError,\n NoneCapError,\n SolveFailedError,\n TimeoutError,\n errorFromResponse,\n} from \"./errors.js\";\nimport type {\n Account,\n ErrorCode,\n Solve,\n SolveCreateParams,\n SolveList,\n SolveListParams,\n SolveStatus,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.nonecap.com\";\n/** The API caps server-side long-poll at 90 seconds. */\nconst MAX_WAIT_SECONDS = 90;\nconst TERMINAL: ReadonlySet<SolveStatus> = new Set([\n \"solved\",\n \"failed\",\n \"cancelled\",\n \"expired\",\n]);\n\n/** Is this a terminal solve status? */\nexport function isTerminal(status: SolveStatus): boolean {\n return TERMINAL.has(status);\n}\n\n/** A `fetch` implementation. Defaults to the global `fetch`. */\nexport type FetchLike = (\n input: string,\n init?: RequestInit,\n) => Promise<Response>;\n\n/** Options for constructing a {@link NoneCap} client. */\nexport interface NoneCapOptions {\n /** Your NoneCap API key. Find it at https://dashboard.nonecap.com. */\n apiKey: string;\n /** Override the API base URL. Defaults to https://api.nonecap.com. */\n baseURL?: string;\n /**\n * Per-request timeout in milliseconds for a single HTTP call. Defaults to\n * 100000 (just above the 90s server long-poll). Note this bounds one HTTP\n * round trip, not the whole {@link NoneCap.solve} wait — use the `timeout`\n * option on `solve()` for that.\n */\n timeout?: number;\n /** Inject a custom `fetch` (for testing, proxies, or non-standard runtimes). */\n fetch?: FetchLike;\n}\n\n/** Per-call options for the long-polling endpoints. */\nexport interface WaitOptions {\n /**\n * Hold the connection open up to this many seconds (1–90) waiting for the\n * solve to finish before responding. Omit to return immediately.\n */\n wait?: number;\n /** Abort the request. */\n signal?: AbortSignal;\n}\n\n/** Options for {@link NoneCap.solve}. */\nexport interface SolveHelperOptions {\n /**\n * Give up after this many milliseconds and throw {@link TimeoutError}.\n * Defaults to 180000 (3 minutes).\n */\n timeout?: number;\n /** Abort waiting. */\n signal?: AbortSignal;\n}\n\ninterface RequestOptions {\n query?: Record<string, string | number | undefined>;\n body?: unknown;\n signal?: AbortSignal;\n /** Override the per-request timeout for this call (used by long polls). */\n timeout?: number;\n /** HTTP statuses to treat as success in addition to 2xx (e.g. 202). */\n expect?: number[];\n}\n\n/**\n * The NoneCap API client.\n *\n * ```ts\n * const nc = new NoneCap({ apiKey: process.env.NONECAP_KEY! });\n * const { token } = await nc.solve({ type: \"hcaptcha\", sitekey, url });\n * ```\n */\nexport class NoneCap {\n readonly #apiKey: string;\n readonly #baseURL: string;\n readonly #timeout: number;\n readonly #fetch: FetchLike;\n\n constructor(options: NoneCapOptions) {\n if (!options || !options.apiKey) {\n throw new NoneCapError(\n \"A NoneCap API key is required. Pass it as `new NoneCap({ apiKey })`.\",\n );\n }\n this.#apiKey = options.apiKey;\n this.#baseURL = (options.baseURL ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.#timeout = options.timeout ?? 100_000;\n const f = options.fetch ?? (globalThis.fetch as FetchLike | undefined);\n if (!f) {\n throw new NoneCapError(\n \"No `fetch` available. Use Node 18+, or pass a `fetch` in the client options.\",\n );\n }\n this.#fetch = f;\n }\n\n /** Operations on solves. */\n readonly solves = {\n /** Submit a solve. Pass `{ wait }` to hold the connection until it finishes. */\n create: (params: SolveCreateParams, options: WaitOptions = {}): Promise<Solve> =>\n this.#request<Solve>(\"POST\", \"/v1/solves\", {\n body: params,\n query: { wait: options.wait },\n signal: options.signal,\n timeout: this.#timeoutForWait(options.wait),\n expect: [202],\n }),\n\n /** Fetch a solve by id. Pass `{ wait }` to long-poll until it finishes. */\n retrieve: (id: string, options: WaitOptions = {}): Promise<Solve> =>\n this.#request<Solve>(\"GET\", `/v1/solves/${encodeURIComponent(id)}`, {\n query: { wait: options.wait },\n signal: options.signal,\n timeout: this.#timeoutForWait(options.wait),\n expect: [202],\n }),\n\n /** Cancel a pending or in-flight solve. */\n cancel: (id: string, options: { signal?: AbortSignal } = {}): Promise<Solve> =>\n this.#request<Solve>(\"DELETE\", `/v1/solves/${encodeURIComponent(id)}`, {\n signal: options.signal,\n }),\n\n /** Fetch one page of solves. */\n list: (params: SolveListParams = {}, options: { signal?: AbortSignal } = {}): Promise<SolveList> =>\n this.#request<SolveList>(\"GET\", \"/v1/solves\", {\n query: {\n limit: params.limit,\n starting_after: params.starting_after,\n status: params.status,\n type: params.type,\n },\n signal: options.signal,\n }),\n\n /**\n * Iterate every solve across pages, newest first.\n *\n * ```ts\n * for await (const solve of nc.solves.listAll()) { ... }\n * ```\n */\n listAll: (\n params: SolveListParams = {},\n options: { signal?: AbortSignal } = {},\n ): AsyncIterableIterator<Solve> => this.#listAll(params, options),\n };\n\n /** Fetch your account, including the current credit balance. */\n me(options: { signal?: AbortSignal } = {}): Promise<Account> {\n return this.#request<Account>(\"GET\", \"/v1/me\", { signal: options.signal });\n }\n\n /**\n * Submit a solve and wait for it to finish, returning the solved solve.\n *\n * Uses the server's long-poll under the hood and keeps polling until the\n * solve is terminal or `timeout` elapses. Throws {@link SolveFailedError} if\n * the solve fails/expires/is cancelled, or {@link TimeoutError} on timeout.\n */\n async solve(params: SolveCreateParams, options: SolveHelperOptions = {}): Promise<Solve> {\n const timeout = options.timeout ?? 180_000;\n const deadline = Date.now() + timeout;\n\n let solve = await this.solves.create(params, {\n wait: waitSeconds(deadline),\n signal: options.signal,\n });\n\n while (!isTerminal(solve.status)) {\n if (Date.now() >= deadline) {\n throw new TimeoutError(\n `Solve ${solve.id} did not finish within ${timeout}ms (last status: ${solve.status}).`,\n { code: undefined },\n );\n }\n solve = await this.solves.retrieve(solve.id, {\n wait: waitSeconds(deadline),\n signal: options.signal,\n });\n }\n\n if (solve.status !== \"solved\") throw new SolveFailedError(solve);\n return solve;\n }\n\n // -- internals ------------------------------------------------------------\n\n #timeoutForWait(wait?: number): number | undefined {\n if (wait === undefined) return undefined;\n // Give the socket a margin beyond the server's long-poll window.\n return wait * 1000 + 15_000;\n }\n\n async *#listAll(\n params: SolveListParams,\n options: { signal?: AbortSignal },\n ): AsyncIterableIterator<Solve> {\n let cursor = params.starting_after;\n for (;;) {\n const page = await this.solves.list({ ...params, starting_after: cursor }, options);\n for (const solve of page.data) yield solve;\n if (!page.has_more || page.data.length === 0) return;\n cursor = page.data[page.data.length - 1]!.id;\n }\n }\n\n async #request<T>(method: string, path: string, options: RequestOptions = {}): Promise<T> {\n const url = new URL(this.#baseURL + path);\n if (options.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value !== undefined && value !== \"\") url.searchParams.set(key, String(value));\n }\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.#apiKey}`,\n Accept: \"application/json\",\n };\n let bodyInit: string | undefined;\n if (options.body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n bodyInit = JSON.stringify(options.body);\n }\n\n const controller = new AbortController();\n const timeoutMs = options.timeout ?? this.#timeout;\n const timer = setTimeout(() => controller.abort(new TimeoutError(`Request timed out after ${timeoutMs}ms.`)), timeoutMs);\n const onAbort = () => controller.abort(options.signal?.reason);\n if (options.signal) {\n if (options.signal.aborted) onAbort();\n else options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n let res: Response;\n try {\n res = await this.#fetch(url.toString(), {\n method,\n headers,\n body: bodyInit,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof TimeoutError) throw err;\n if (isAbort(err)) {\n // A caller-supplied signal aborting surfaces as the caller's reason.\n if (options.signal?.aborted) throw options.signal.reason instanceof Error\n ? options.signal.reason\n : new NoneCapError(\"Request aborted.\");\n throw new TimeoutError(`Request timed out after ${timeoutMs}ms.`);\n }\n throw new ConnectionError(\n `Could not reach the NoneCap API at ${this.#baseURL}: ${(err as Error).message}`,\n );\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n }\n\n return this.#parse<T>(res, options.expect ?? []);\n }\n\n async #parse<T>(res: Response, expect: number[]): Promise<T> {\n const text = await res.text();\n let json: unknown;\n if (text) {\n try {\n json = JSON.parse(text);\n } catch {\n throw new APIError(\n `Unexpected non-JSON response (HTTP ${res.status}): ${text.slice(0, 200)}`,\n { status: res.status },\n );\n }\n }\n\n if (res.ok || expect.includes(res.status)) {\n return json as T;\n }\n\n const envelope = json as { error?: { code?: ErrorCode; message?: string; param?: string | null } } | undefined;\n const apiError = envelope?.error;\n throw errorFromResponse(\n res.status,\n apiError?.code,\n apiError?.message ?? `HTTP ${res.status}`,\n apiError?.param ?? null,\n );\n }\n}\n\n/**\n * The `wait` value for the next long-poll: whole seconds until `deadline`,\n * clamped to the server's 1–90 window. The floor of 1 keeps the param valid,\n * so callers must decide whether the deadline has passed with the clock, not\n * with this return value.\n */\nfunction waitSeconds(deadline: number): number {\n const remaining = Math.ceil((deadline - Date.now()) / 1000);\n return Math.max(1, Math.min(MAX_WAIT_SECONDS, remaining));\n}\n\nfunction isAbort(err: unknown): boolean {\n return err instanceof Error && err.name === \"AbortError\";\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["import type { ErrorCode, Solve } from \"./types.js\";\n\n/**\n * Base class for every error this library throws. Catch `NoneCapError` to\n * handle all of them, or catch a subclass to handle one kind.\n */\nexport class NoneCapError extends Error {\n /** Machine-readable error code from the API envelope, when there is one. */\n readonly code: ErrorCode | undefined;\n /** HTTP status, when the error came from a response. */\n readonly status: number | undefined;\n /** The request field that was rejected, for validation errors. */\n readonly param: string | null;\n\n constructor(\n message: string,\n opts: { code?: ErrorCode; status?: number; param?: string | null } = {},\n ) {\n super(message);\n this.name = new.target.name;\n this.code = opts.code;\n this.status = opts.status;\n this.param = opts.param ?? null;\n // Restore the prototype chain when compiled down to ES5-era targets.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** 401 — the API key is missing, malformed, or revoked. */\nexport class AuthenticationError extends NoneCapError {}\n\n/** 403 — the key is valid but not allowed to do this (scope or locked account). */\nexport class PermissionError extends NoneCapError {}\n\n/** 402 — the account is out of credits. */\nexport class InsufficientCreditsError extends NoneCapError {}\n\n/** 422 / 400 — the request was rejected. `param` names the offending field. */\nexport class ValidationError extends NoneCapError {}\n\n/** 404 — no such resource. */\nexport class NotFoundError extends NoneCapError {}\n\n/** 409 — the solve is already in a terminal state (e.g. cancelling a finished solve). */\nexport class ConflictError extends NoneCapError {}\n\n/** 429 — too many concurrent solves, or rate limited. Back off and retry. */\nexport class RateLimitError extends NoneCapError {}\n\n/** 5xx, or a response that wasn't the expected shape. */\nexport class APIError extends NoneCapError {}\n\n/** The request never reached the API (DNS, TCP, TLS, timeout, offline). */\nexport class ConnectionError extends NoneCapError {}\n\n/**\n * Thrown by {@link NoneCap.solve} when a solve reaches a terminal state without\n * a token: `failed`, `expired`, or `cancelled`. The full solve is attached so\n * you can inspect `solve.error` and timings.\n */\nexport class SolveFailedError extends NoneCapError {\n readonly solve: Solve;\n\n constructor(solve: Solve) {\n const detail = solve.error ? `${solve.error.code}: ${solve.error.message}` : solve.status;\n super(`Solve ${solve.id} ${solve.status} (${detail})`, {\n code: undefined,\n status: undefined,\n });\n this.solve = solve;\n }\n}\n\n/**\n * Thrown by {@link NoneCap.solve} (and {@link SolveHandle.result}) when the\n * client-side timeout elapses first. When the timeout came from waiting on a\n * solve, `solveId` and the last-known `solve` are attached so you can still\n * cancel the in-flight solve. They are `undefined` for transport-level timeouts.\n */\nexport class TimeoutError extends NoneCapError {\n /** The id of the in-flight solve, when the timeout came from a solve wait. */\n readonly solveId: string | undefined;\n /** The last-known solve state, so you can cancel it after a wait timed out. */\n readonly solve: Solve | undefined;\n\n constructor(\n message: string,\n opts: {\n code?: ErrorCode;\n status?: number;\n param?: string | null;\n solveId?: string;\n solve?: Solve;\n } = {},\n ) {\n super(message, opts);\n this.solveId = opts.solveId;\n this.solve = opts.solve;\n }\n}\n\n/** Map an API error envelope (plus HTTP status) to the right error subclass. */\nexport function errorFromResponse(\n status: number,\n code: ErrorCode | undefined,\n message: string,\n param: string | null,\n): NoneCapError {\n const opts = { code, status, param };\n switch (code) {\n case \"unauthorized\":\n return new AuthenticationError(message, opts);\n case \"forbidden\":\n case \"account_locked\":\n return new PermissionError(message, opts);\n case \"insufficient_credits\":\n return new InsufficientCreditsError(message, opts);\n case \"invalid_request\":\n case \"validation_error\":\n return new ValidationError(message, opts);\n case \"not_found\":\n return new NotFoundError(message, opts);\n case \"conflict\":\n return new ConflictError(message, opts);\n case \"rate_limited\":\n case \"concurrency_limit_exceeded\":\n case \"ext_daily_limit\":\n return new RateLimitError(message, opts);\n default:\n return new APIError(message, opts);\n }\n}\n","import {\n APIError,\n ConflictError,\n ConnectionError,\n NoneCapError,\n SolveFailedError,\n TimeoutError,\n errorFromResponse,\n} from \"./errors.js\";\nimport type {\n Account,\n ErrorCode,\n Solve,\n SolveCreateParams,\n SolveList,\n SolveListParams,\n SolveStatus,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.nonecap.com\";\n/** The API caps server-side long-poll at 90 seconds. */\nconst MAX_WAIT_SECONDS = 90;\nconst TERMINAL: ReadonlySet<SolveStatus> = new Set([\n \"solved\",\n \"failed\",\n \"cancelled\",\n \"expired\",\n]);\n\n/** Is this a terminal solve status? */\nexport function isTerminal(status: SolveStatus): boolean {\n return TERMINAL.has(status);\n}\n\n/** A `fetch` implementation. Defaults to the global `fetch`. */\nexport type FetchLike = (\n input: string,\n init?: RequestInit,\n) => Promise<Response>;\n\n/** Options for constructing a {@link NoneCap} client. */\nexport interface NoneCapOptions {\n /** Your NoneCap API key. Find it at https://dashboard.nonecap.com. */\n apiKey: string;\n /** Override the API base URL. Defaults to https://api.nonecap.com. */\n baseURL?: string;\n /**\n * Per-request timeout in milliseconds for a single HTTP call. Defaults to\n * 100000 (just above the 90s server long-poll). Note this bounds one HTTP\n * round trip, not the whole {@link NoneCap.solve} wait — use the `timeout`\n * option on `solve()` for that.\n */\n timeout?: number;\n /** Inject a custom `fetch` (for testing, proxies, or non-standard runtimes). */\n fetch?: FetchLike;\n}\n\n/** Per-call options for the long-polling endpoints. */\nexport interface WaitOptions {\n /**\n * Hold the connection open up to this many seconds (1–90) waiting for the\n * solve to finish before responding. Omit to return immediately.\n */\n wait?: number;\n /** Abort the request. */\n signal?: AbortSignal;\n}\n\n/** Options for {@link NoneCap.solve} and {@link SolveHandle.result}. */\nexport interface SolveHelperOptions {\n /**\n * Give up after this many milliseconds and throw {@link TimeoutError}.\n * Defaults to 180000 (3 minutes).\n */\n timeout?: number;\n /** Abort waiting. */\n signal?: AbortSignal;\n}\n\n/** Options for {@link NoneCap.solves.start}. */\nexport interface SolveStartOptions {\n /** Abort the submission. */\n signal?: AbortSignal;\n}\n\ninterface RequestOptions {\n query?: Record<string, string | number | undefined>;\n body?: unknown;\n signal?: AbortSignal;\n /** Override the per-request timeout for this call (used by long polls). */\n timeout?: number;\n /** HTTP statuses to treat as success in addition to 2xx (e.g. 202). */\n expect?: number[];\n}\n\n/**\n * The NoneCap API client.\n *\n * ```ts\n * const nc = new NoneCap({ apiKey: process.env.NONECAP_KEY! });\n * const { token } = await nc.solve({ type: \"hcaptcha\", sitekey, url });\n * ```\n */\nexport class NoneCap {\n readonly #apiKey: string;\n readonly #baseURL: string;\n readonly #timeout: number;\n readonly #fetch: FetchLike;\n\n constructor(options: NoneCapOptions) {\n if (!options || !options.apiKey) {\n throw new NoneCapError(\n \"A NoneCap API key is required. Pass it as `new NoneCap({ apiKey })`.\",\n );\n }\n this.#apiKey = options.apiKey;\n this.#baseURL = (options.baseURL ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.#timeout = options.timeout ?? 100_000;\n const f = options.fetch ?? (globalThis.fetch as FetchLike | undefined);\n if (!f) {\n throw new NoneCapError(\n \"No `fetch` available. Use Node 18+, or pass a `fetch` in the client options.\",\n );\n }\n this.#fetch = f;\n }\n\n /** Operations on solves. */\n readonly solves = {\n /** Submit a solve. Pass `{ wait }` to hold the connection until it finishes. */\n create: (params: SolveCreateParams, options: WaitOptions = {}): Promise<Solve> =>\n this.#request<Solve>(\"POST\", \"/v1/solves\", {\n body: params,\n query: { wait: options.wait },\n signal: options.signal,\n timeout: this.#timeoutForWait(options.wait),\n expect: [202],\n }),\n\n /** Fetch a solve by id. Pass `{ wait }` to long-poll until it finishes. */\n retrieve: (id: string, options: WaitOptions = {}): Promise<Solve> =>\n this.#request<Solve>(\"GET\", `/v1/solves/${encodeURIComponent(id)}`, {\n query: { wait: options.wait },\n signal: options.signal,\n timeout: this.#timeoutForWait(options.wait),\n expect: [202],\n }),\n\n /** Cancel a pending or in-flight solve. */\n cancel: (id: string, options: { signal?: AbortSignal } = {}): Promise<Solve> =>\n this.#request<Solve>(\"DELETE\", `/v1/solves/${encodeURIComponent(id)}`, {\n signal: options.signal,\n }),\n\n /**\n * Submit a solve and return a {@link SolveHandle} for it.\n *\n * Unlike {@link NoneCap.solve}, this returns as soon as the submission is\n * accepted, so `handle.id` is available immediately. Use the handle to\n * `cancel()` the solve or `await handle.result()` for the finished solve.\n *\n * ```ts\n * const handle = await nc.solves.start({ type: \"hcaptcha\", sitekey, url });\n * // ...later, e.g. on shutdown:\n * await handle.cancel();\n * // or wait for it:\n * const { token } = await handle.result();\n * ```\n */\n start: async (\n params: SolveCreateParams,\n options: SolveStartOptions = {},\n ): Promise<SolveHandle> => {\n const solve = await this.solves.create(params, { signal: options.signal });\n return new SolveHandle(this, solve);\n },\n\n /** Fetch one page of solves. */\n list: (params: SolveListParams = {}, options: { signal?: AbortSignal } = {}): Promise<SolveList> =>\n this.#request<SolveList>(\"GET\", \"/v1/solves\", {\n query: {\n limit: params.limit,\n starting_after: params.starting_after,\n status: params.status,\n type: params.type,\n },\n signal: options.signal,\n }),\n\n /**\n * Iterate every solve across pages, newest first.\n *\n * ```ts\n * for await (const solve of nc.solves.listAll()) { ... }\n * ```\n */\n listAll: (\n params: SolveListParams = {},\n options: { signal?: AbortSignal } = {},\n ): AsyncIterableIterator<Solve> => this.#listAll(params, options),\n };\n\n /** Fetch your account, including the current credit balance. */\n me(options: { signal?: AbortSignal } = {}): Promise<Account> {\n return this.#request<Account>(\"GET\", \"/v1/me\", { signal: options.signal });\n }\n\n /**\n * Submit a solve and wait for it to finish, returning the solved solve.\n *\n * Uses the server's long-poll under the hood and keeps polling until the\n * solve is terminal or `timeout` elapses. Throws {@link SolveFailedError} if\n * the solve fails/expires/is cancelled, or {@link TimeoutError} on timeout.\n *\n * This is sugar for `(await solves.start(params)).result(options)`. When you\n * need to cancel the solve while it runs, use {@link NoneCap.solves.start}\n * and keep the {@link SolveHandle}.\n */\n async solve(params: SolveCreateParams, options: SolveHelperOptions = {}): Promise<Solve> {\n const handle = await this.solves.start(params, { signal: options.signal });\n return handle.result(options);\n }\n\n // -- internals ------------------------------------------------------------\n\n #timeoutForWait(wait?: number): number | undefined {\n if (wait === undefined) return undefined;\n // Give the socket a margin beyond the server's long-poll window.\n return wait * 1000 + 15_000;\n }\n\n async *#listAll(\n params: SolveListParams,\n options: { signal?: AbortSignal },\n ): AsyncIterableIterator<Solve> {\n let cursor = params.starting_after;\n for (;;) {\n const page = await this.solves.list({ ...params, starting_after: cursor }, options);\n for (const solve of page.data) yield solve;\n if (!page.has_more || page.data.length === 0) return;\n cursor = page.data[page.data.length - 1]!.id;\n }\n }\n\n async #request<T>(method: string, path: string, options: RequestOptions = {}): Promise<T> {\n const url = new URL(this.#baseURL + path);\n if (options.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value !== undefined && value !== \"\") url.searchParams.set(key, String(value));\n }\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.#apiKey}`,\n Accept: \"application/json\",\n };\n let bodyInit: string | undefined;\n if (options.body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n bodyInit = JSON.stringify(options.body);\n }\n\n const controller = new AbortController();\n const timeoutMs = options.timeout ?? this.#timeout;\n const timer = setTimeout(() => controller.abort(new TimeoutError(`Request timed out after ${timeoutMs}ms.`)), timeoutMs);\n const onAbort = () => controller.abort(options.signal?.reason);\n if (options.signal) {\n if (options.signal.aborted) onAbort();\n else options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n let res: Response;\n try {\n res = await this.#fetch(url.toString(), {\n method,\n headers,\n body: bodyInit,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof TimeoutError) throw err;\n if (isAbort(err)) {\n // A caller-supplied signal aborting surfaces as the caller's reason.\n if (options.signal?.aborted) throw options.signal.reason instanceof Error\n ? options.signal.reason\n : new NoneCapError(\"Request aborted.\");\n throw new TimeoutError(`Request timed out after ${timeoutMs}ms.`);\n }\n throw new ConnectionError(\n `Could not reach the NoneCap API at ${this.#baseURL}: ${(err as Error).message}`,\n );\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n }\n\n return this.#parse<T>(res, options.expect ?? []);\n }\n\n async #parse<T>(res: Response, expect: number[]): Promise<T> {\n const text = await res.text();\n let json: unknown;\n if (text) {\n try {\n json = JSON.parse(text);\n } catch {\n throw new APIError(\n `Unexpected non-JSON response (HTTP ${res.status}): ${text.slice(0, 200)}`,\n { status: res.status },\n );\n }\n }\n\n if (res.ok || expect.includes(res.status)) {\n return json as T;\n }\n\n const envelope = json as { error?: { code?: ErrorCode; message?: string; param?: string | null } } | undefined;\n const apiError = envelope?.error;\n throw errorFromResponse(\n res.status,\n apiError?.code,\n apiError?.message ?? `HTTP ${res.status}`,\n apiError?.param ?? null,\n );\n }\n}\n\n/**\n * A handle on a started solve, returned by {@link NoneCap.solves.start}.\n *\n * The handle is the cancellable lifecycle object for one solve. It is a plain\n * object, not a promise: `await`ing it does nothing useful. Wait for the result\n * with {@link SolveHandle.result}, and cancel with {@link SolveHandle.cancel}.\n * You do not construct it yourself — `solves.start()` hands you one with `id`\n * already resolved.\n */\nexport class SolveHandle {\n /** The id of the started solve, available as soon as `start()` resolved. */\n readonly id: string;\n\n readonly #client: NoneCap;\n /** The latest solve state we have seen, from the create, polls, or cancel. */\n #solve: Solve;\n /**\n * The cached TERMINAL outcome: resolves to the solved solve, or rejects with\n * {@link SolveFailedError} if terminal-but-not-solved. Only ever set once the\n * solve actually reaches a terminal state, so it is safe to replay forever. A\n * timed-out wait does NOT settle this — it is retryable.\n */\n #terminal: Promise<Solve> | undefined;\n /** The poll currently in flight, shared by concurrent result() callers. */\n #inflight: Promise<Solve> | undefined;\n\n constructor(client: NoneCap, solve: Solve) {\n this.#client = client;\n this.#solve = solve;\n this.id = solve.id;\n }\n\n /** The last-known solve state for this handle. */\n get solve(): Solve {\n return this.#solve;\n }\n\n /**\n * Long-poll until the solve is terminal, returning the solved solve. Throws\n * {@link SolveFailedError} if it fails/expires/is cancelled, or\n * {@link TimeoutError} on timeout (with `solveId`/`solve` attached so you can\n * still {@link SolveHandle.cancel} it).\n *\n * Memoizes the *terminal* outcome: once the solve actually finishes, every\n * later call returns that same settled result. A {@link TimeoutError} is not\n * memoized — it just means this wait gave up, so a later call (e.g. with a\n * larger `timeout`) resumes polling. Concurrent calls share one in-flight\n * poll, so they never issue duplicate requests. The first caller's\n * `timeout`/`signal` drive the shared poll; a later call starts a fresh one.\n */\n result(options: SolveHelperOptions = {}): Promise<Solve> {\n if (this.#terminal) return this.#terminal;\n if (this.#inflight) return this.#inflight;\n const run = this.#poll(options).finally(() => {\n // Free the slot so a retry can re-poll after a timeout; a terminal\n // outcome is already memoized in #terminal and wins on the next call.\n if (this.#inflight === run) this.#inflight = undefined;\n });\n this.#inflight = run;\n return run;\n }\n\n /**\n * Cancel the solve. Cancelled solves are never charged, so this is for clean\n * shutdown / freeing slots / stopping early, not cost protection.\n *\n * If the solve already reached a terminal state the API answers 409; that is\n * not an error a caller cares about, so this swallows {@link ConflictError}\n * and returns the current solve state instead. A cancel that lands on a\n * terminal state (cancelled, or whatever the 409-swallow read back) settles\n * this handle's {@link SolveHandle.result}, so any in-flight or later wait\n * stops polling and reflects that terminal state.\n */\n async cancel(options: { signal?: AbortSignal } = {}): Promise<Solve> {\n try {\n this.#solve = await this.#client.solves.cancel(this.id, options);\n } catch (err) {\n if (!(err instanceof ConflictError)) throw err;\n // Already terminal — report the real state rather than the 409.\n this.#solve = await this.#client.solves.retrieve(this.id, options);\n }\n if (!this.#terminal && isTerminal(this.#solve.status)) this.#cacheTerminal();\n return this.#solve;\n }\n\n /**\n * Build and memoize the terminal outcome from the current `#solve`. The\n * precondition is that `#solve` is terminal and `#terminal` is unset.\n */\n #cacheTerminal(): Promise<Solve> {\n const settled =\n this.#solve.status === \"solved\"\n ? Promise.resolve(this.#solve)\n : Promise.reject(new SolveFailedError(this.#solve));\n // Never let the memoized rejection surface as an unhandled rejection; real\n // callers read it explicitly through result().\n settled.catch(() => {});\n this.#terminal = settled;\n return settled;\n }\n\n async #poll(options: SolveHelperOptions): Promise<Solve> {\n const timeout = options.timeout ?? 180_000;\n const deadline = Date.now() + timeout;\n\n for (;;) {\n // cancel() (or a previous poll) may have already settled this handle.\n if (this.#terminal) return this.#terminal;\n if (isTerminal(this.#solve.status)) return this.#cacheTerminal();\n if (Date.now() >= deadline) {\n throw new TimeoutError(\n `Solve ${this.id} did not finish within ${timeout}ms (last status: ${this.#solve.status}).`,\n { solveId: this.id, solve: this.#solve },\n );\n }\n this.#solve = await this.#client.solves.retrieve(this.id, {\n wait: waitSeconds(deadline),\n signal: options.signal,\n });\n }\n }\n}\n\n/**\n * The `wait` value for the next long-poll: whole seconds until `deadline`,\n * clamped to the server's 1–90 window. The floor of 1 keeps the param valid,\n * so callers must decide whether the deadline has passed with the clock, not\n * with this return value.\n */\nfunction waitSeconds(deadline: number): number {\n const remaining = Math.ceil((deadline - Date.now()) / 1000);\n return Math.max(1, Math.min(MAX_WAIT_SECONDS, remaining));\n}\n\nfunction isAbort(err: unknown): boolean {\n return err instanceof Error && err.name === \"AbortError\";\n}\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -6,11 +6,14 @@
|
|
|
6
6
|
type SolveType = "hcaptcha" | "hcaptcha_enterprise";
|
|
7
7
|
/** Lifecycle of a solve. `solved` and `failed`/`expired`/`cancelled` are terminal. */
|
|
8
8
|
type SolveStatus = "pending" | "solving" | "solved" | "failed" | "cancelled" | "expired";
|
|
9
|
-
/**
|
|
9
|
+
/** Proxy scheme. Defaults to `http` when omitted. */
|
|
10
|
+
type ProxyScheme = "http" | "https" | "socks5" | "socks5h" | "socks4";
|
|
11
|
+
/** A proxy the solve should egress through. `host` and `port` are required; the
|
|
12
|
+
* API rejects a partial proxy object with a 422. `port` must be 1–65535. */
|
|
10
13
|
interface Proxy {
|
|
11
|
-
scheme?:
|
|
12
|
-
host
|
|
13
|
-
port
|
|
14
|
+
scheme?: ProxyScheme;
|
|
15
|
+
host: string;
|
|
16
|
+
port: string | number;
|
|
14
17
|
username?: string;
|
|
15
18
|
password?: string;
|
|
16
19
|
}
|
|
@@ -135,7 +138,7 @@ interface WaitOptions {
|
|
|
135
138
|
/** Abort the request. */
|
|
136
139
|
signal?: AbortSignal;
|
|
137
140
|
}
|
|
138
|
-
/** Options for {@link NoneCap.solve}. */
|
|
141
|
+
/** Options for {@link NoneCap.solve} and {@link SolveHandle.result}. */
|
|
139
142
|
interface SolveHelperOptions {
|
|
140
143
|
/**
|
|
141
144
|
* Give up after this many milliseconds and throw {@link TimeoutError}.
|
|
@@ -145,6 +148,11 @@ interface SolveHelperOptions {
|
|
|
145
148
|
/** Abort waiting. */
|
|
146
149
|
signal?: AbortSignal;
|
|
147
150
|
}
|
|
151
|
+
/** Options for {@link NoneCap.solves.start}. */
|
|
152
|
+
interface SolveStartOptions {
|
|
153
|
+
/** Abort the submission. */
|
|
154
|
+
signal?: AbortSignal;
|
|
155
|
+
}
|
|
148
156
|
/**
|
|
149
157
|
* The NoneCap API client.
|
|
150
158
|
*
|
|
@@ -166,6 +174,22 @@ declare class NoneCap {
|
|
|
166
174
|
cancel: (id: string, options?: {
|
|
167
175
|
signal?: AbortSignal;
|
|
168
176
|
}) => Promise<Solve>;
|
|
177
|
+
/**
|
|
178
|
+
* Submit a solve and return a {@link SolveHandle} for it.
|
|
179
|
+
*
|
|
180
|
+
* Unlike {@link NoneCap.solve}, this returns as soon as the submission is
|
|
181
|
+
* accepted, so `handle.id` is available immediately. Use the handle to
|
|
182
|
+
* `cancel()` the solve or `await handle.result()` for the finished solve.
|
|
183
|
+
*
|
|
184
|
+
* ```ts
|
|
185
|
+
* const handle = await nc.solves.start({ type: "hcaptcha", sitekey, url });
|
|
186
|
+
* // ...later, e.g. on shutdown:
|
|
187
|
+
* await handle.cancel();
|
|
188
|
+
* // or wait for it:
|
|
189
|
+
* const { token } = await handle.result();
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
start: (params: SolveCreateParams, options?: SolveStartOptions) => Promise<SolveHandle>;
|
|
169
193
|
/** Fetch one page of solves. */
|
|
170
194
|
list: (params?: SolveListParams, options?: {
|
|
171
195
|
signal?: AbortSignal;
|
|
@@ -191,9 +215,58 @@ declare class NoneCap {
|
|
|
191
215
|
* Uses the server's long-poll under the hood and keeps polling until the
|
|
192
216
|
* solve is terminal or `timeout` elapses. Throws {@link SolveFailedError} if
|
|
193
217
|
* the solve fails/expires/is cancelled, or {@link TimeoutError} on timeout.
|
|
218
|
+
*
|
|
219
|
+
* This is sugar for `(await solves.start(params)).result(options)`. When you
|
|
220
|
+
* need to cancel the solve while it runs, use {@link NoneCap.solves.start}
|
|
221
|
+
* and keep the {@link SolveHandle}.
|
|
194
222
|
*/
|
|
195
223
|
solve(params: SolveCreateParams, options?: SolveHelperOptions): Promise<Solve>;
|
|
196
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* A handle on a started solve, returned by {@link NoneCap.solves.start}.
|
|
227
|
+
*
|
|
228
|
+
* The handle is the cancellable lifecycle object for one solve. It is a plain
|
|
229
|
+
* object, not a promise: `await`ing it does nothing useful. Wait for the result
|
|
230
|
+
* with {@link SolveHandle.result}, and cancel with {@link SolveHandle.cancel}.
|
|
231
|
+
* You do not construct it yourself — `solves.start()` hands you one with `id`
|
|
232
|
+
* already resolved.
|
|
233
|
+
*/
|
|
234
|
+
declare class SolveHandle {
|
|
235
|
+
#private;
|
|
236
|
+
/** The id of the started solve, available as soon as `start()` resolved. */
|
|
237
|
+
readonly id: string;
|
|
238
|
+
constructor(client: NoneCap, solve: Solve);
|
|
239
|
+
/** The last-known solve state for this handle. */
|
|
240
|
+
get solve(): Solve;
|
|
241
|
+
/**
|
|
242
|
+
* Long-poll until the solve is terminal, returning the solved solve. Throws
|
|
243
|
+
* {@link SolveFailedError} if it fails/expires/is cancelled, or
|
|
244
|
+
* {@link TimeoutError} on timeout (with `solveId`/`solve` attached so you can
|
|
245
|
+
* still {@link SolveHandle.cancel} it).
|
|
246
|
+
*
|
|
247
|
+
* Memoizes the *terminal* outcome: once the solve actually finishes, every
|
|
248
|
+
* later call returns that same settled result. A {@link TimeoutError} is not
|
|
249
|
+
* memoized — it just means this wait gave up, so a later call (e.g. with a
|
|
250
|
+
* larger `timeout`) resumes polling. Concurrent calls share one in-flight
|
|
251
|
+
* poll, so they never issue duplicate requests. The first caller's
|
|
252
|
+
* `timeout`/`signal` drive the shared poll; a later call starts a fresh one.
|
|
253
|
+
*/
|
|
254
|
+
result(options?: SolveHelperOptions): Promise<Solve>;
|
|
255
|
+
/**
|
|
256
|
+
* Cancel the solve. Cancelled solves are never charged, so this is for clean
|
|
257
|
+
* shutdown / freeing slots / stopping early, not cost protection.
|
|
258
|
+
*
|
|
259
|
+
* If the solve already reached a terminal state the API answers 409; that is
|
|
260
|
+
* not an error a caller cares about, so this swallows {@link ConflictError}
|
|
261
|
+
* and returns the current solve state instead. A cancel that lands on a
|
|
262
|
+
* terminal state (cancelled, or whatever the 409-swallow read back) settles
|
|
263
|
+
* this handle's {@link SolveHandle.result}, so any in-flight or later wait
|
|
264
|
+
* stops polling and reflects that terminal state.
|
|
265
|
+
*/
|
|
266
|
+
cancel(options?: {
|
|
267
|
+
signal?: AbortSignal;
|
|
268
|
+
}): Promise<Solve>;
|
|
269
|
+
}
|
|
197
270
|
|
|
198
271
|
/**
|
|
199
272
|
* Base class for every error this library throws. Catch `NoneCapError` to
|
|
@@ -248,8 +321,24 @@ declare class SolveFailedError extends NoneCapError {
|
|
|
248
321
|
readonly solve: Solve;
|
|
249
322
|
constructor(solve: Solve);
|
|
250
323
|
}
|
|
251
|
-
/**
|
|
324
|
+
/**
|
|
325
|
+
* Thrown by {@link NoneCap.solve} (and {@link SolveHandle.result}) when the
|
|
326
|
+
* client-side timeout elapses first. When the timeout came from waiting on a
|
|
327
|
+
* solve, `solveId` and the last-known `solve` are attached so you can still
|
|
328
|
+
* cancel the in-flight solve. They are `undefined` for transport-level timeouts.
|
|
329
|
+
*/
|
|
252
330
|
declare class TimeoutError extends NoneCapError {
|
|
331
|
+
/** The id of the in-flight solve, when the timeout came from a solve wait. */
|
|
332
|
+
readonly solveId: string | undefined;
|
|
333
|
+
/** The last-known solve state, so you can cancel it after a wait timed out. */
|
|
334
|
+
readonly solve: Solve | undefined;
|
|
335
|
+
constructor(message: string, opts?: {
|
|
336
|
+
code?: ErrorCode;
|
|
337
|
+
status?: number;
|
|
338
|
+
param?: string | null;
|
|
339
|
+
solveId?: string;
|
|
340
|
+
solve?: Solve;
|
|
341
|
+
});
|
|
253
342
|
}
|
|
254
343
|
|
|
255
|
-
export { APIError, type Account, AuthenticationError, ConflictError, ConnectionError, type ErrorCode, type ErrorEnvelope, type FetchLike, InsufficientCreditsError, NoneCap, NoneCapError, type NoneCapOptions, NotFoundError, PermissionError, type Proxy, RateLimitError, type Solve, type SolveCreateParams, type SolveError, SolveFailedError, type SolveHelperOptions, type SolveList, type SolveListParams, type SolveStatus, type SolveType, TimeoutError, ValidationError, type WaitOptions, isTerminal };
|
|
344
|
+
export { APIError, type Account, AuthenticationError, ConflictError, ConnectionError, type ErrorCode, type ErrorEnvelope, type FetchLike, InsufficientCreditsError, NoneCap, NoneCapError, type NoneCapOptions, NotFoundError, PermissionError, type Proxy, RateLimitError, type Solve, type SolveCreateParams, type SolveError, SolveFailedError, SolveHandle, type SolveHelperOptions, type SolveList, type SolveListParams, type SolveStartOptions, type SolveStatus, type SolveType, TimeoutError, ValidationError, type WaitOptions, isTerminal };
|
package/dist/index.d.ts
CHANGED
|
@@ -6,11 +6,14 @@
|
|
|
6
6
|
type SolveType = "hcaptcha" | "hcaptcha_enterprise";
|
|
7
7
|
/** Lifecycle of a solve. `solved` and `failed`/`expired`/`cancelled` are terminal. */
|
|
8
8
|
type SolveStatus = "pending" | "solving" | "solved" | "failed" | "cancelled" | "expired";
|
|
9
|
-
/**
|
|
9
|
+
/** Proxy scheme. Defaults to `http` when omitted. */
|
|
10
|
+
type ProxyScheme = "http" | "https" | "socks5" | "socks5h" | "socks4";
|
|
11
|
+
/** A proxy the solve should egress through. `host` and `port` are required; the
|
|
12
|
+
* API rejects a partial proxy object with a 422. `port` must be 1–65535. */
|
|
10
13
|
interface Proxy {
|
|
11
|
-
scheme?:
|
|
12
|
-
host
|
|
13
|
-
port
|
|
14
|
+
scheme?: ProxyScheme;
|
|
15
|
+
host: string;
|
|
16
|
+
port: string | number;
|
|
14
17
|
username?: string;
|
|
15
18
|
password?: string;
|
|
16
19
|
}
|
|
@@ -135,7 +138,7 @@ interface WaitOptions {
|
|
|
135
138
|
/** Abort the request. */
|
|
136
139
|
signal?: AbortSignal;
|
|
137
140
|
}
|
|
138
|
-
/** Options for {@link NoneCap.solve}. */
|
|
141
|
+
/** Options for {@link NoneCap.solve} and {@link SolveHandle.result}. */
|
|
139
142
|
interface SolveHelperOptions {
|
|
140
143
|
/**
|
|
141
144
|
* Give up after this many milliseconds and throw {@link TimeoutError}.
|
|
@@ -145,6 +148,11 @@ interface SolveHelperOptions {
|
|
|
145
148
|
/** Abort waiting. */
|
|
146
149
|
signal?: AbortSignal;
|
|
147
150
|
}
|
|
151
|
+
/** Options for {@link NoneCap.solves.start}. */
|
|
152
|
+
interface SolveStartOptions {
|
|
153
|
+
/** Abort the submission. */
|
|
154
|
+
signal?: AbortSignal;
|
|
155
|
+
}
|
|
148
156
|
/**
|
|
149
157
|
* The NoneCap API client.
|
|
150
158
|
*
|
|
@@ -166,6 +174,22 @@ declare class NoneCap {
|
|
|
166
174
|
cancel: (id: string, options?: {
|
|
167
175
|
signal?: AbortSignal;
|
|
168
176
|
}) => Promise<Solve>;
|
|
177
|
+
/**
|
|
178
|
+
* Submit a solve and return a {@link SolveHandle} for it.
|
|
179
|
+
*
|
|
180
|
+
* Unlike {@link NoneCap.solve}, this returns as soon as the submission is
|
|
181
|
+
* accepted, so `handle.id` is available immediately. Use the handle to
|
|
182
|
+
* `cancel()` the solve or `await handle.result()` for the finished solve.
|
|
183
|
+
*
|
|
184
|
+
* ```ts
|
|
185
|
+
* const handle = await nc.solves.start({ type: "hcaptcha", sitekey, url });
|
|
186
|
+
* // ...later, e.g. on shutdown:
|
|
187
|
+
* await handle.cancel();
|
|
188
|
+
* // or wait for it:
|
|
189
|
+
* const { token } = await handle.result();
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
start: (params: SolveCreateParams, options?: SolveStartOptions) => Promise<SolveHandle>;
|
|
169
193
|
/** Fetch one page of solves. */
|
|
170
194
|
list: (params?: SolveListParams, options?: {
|
|
171
195
|
signal?: AbortSignal;
|
|
@@ -191,9 +215,58 @@ declare class NoneCap {
|
|
|
191
215
|
* Uses the server's long-poll under the hood and keeps polling until the
|
|
192
216
|
* solve is terminal or `timeout` elapses. Throws {@link SolveFailedError} if
|
|
193
217
|
* the solve fails/expires/is cancelled, or {@link TimeoutError} on timeout.
|
|
218
|
+
*
|
|
219
|
+
* This is sugar for `(await solves.start(params)).result(options)`. When you
|
|
220
|
+
* need to cancel the solve while it runs, use {@link NoneCap.solves.start}
|
|
221
|
+
* and keep the {@link SolveHandle}.
|
|
194
222
|
*/
|
|
195
223
|
solve(params: SolveCreateParams, options?: SolveHelperOptions): Promise<Solve>;
|
|
196
224
|
}
|
|
225
|
+
/**
|
|
226
|
+
* A handle on a started solve, returned by {@link NoneCap.solves.start}.
|
|
227
|
+
*
|
|
228
|
+
* The handle is the cancellable lifecycle object for one solve. It is a plain
|
|
229
|
+
* object, not a promise: `await`ing it does nothing useful. Wait for the result
|
|
230
|
+
* with {@link SolveHandle.result}, and cancel with {@link SolveHandle.cancel}.
|
|
231
|
+
* You do not construct it yourself — `solves.start()` hands you one with `id`
|
|
232
|
+
* already resolved.
|
|
233
|
+
*/
|
|
234
|
+
declare class SolveHandle {
|
|
235
|
+
#private;
|
|
236
|
+
/** The id of the started solve, available as soon as `start()` resolved. */
|
|
237
|
+
readonly id: string;
|
|
238
|
+
constructor(client: NoneCap, solve: Solve);
|
|
239
|
+
/** The last-known solve state for this handle. */
|
|
240
|
+
get solve(): Solve;
|
|
241
|
+
/**
|
|
242
|
+
* Long-poll until the solve is terminal, returning the solved solve. Throws
|
|
243
|
+
* {@link SolveFailedError} if it fails/expires/is cancelled, or
|
|
244
|
+
* {@link TimeoutError} on timeout (with `solveId`/`solve` attached so you can
|
|
245
|
+
* still {@link SolveHandle.cancel} it).
|
|
246
|
+
*
|
|
247
|
+
* Memoizes the *terminal* outcome: once the solve actually finishes, every
|
|
248
|
+
* later call returns that same settled result. A {@link TimeoutError} is not
|
|
249
|
+
* memoized — it just means this wait gave up, so a later call (e.g. with a
|
|
250
|
+
* larger `timeout`) resumes polling. Concurrent calls share one in-flight
|
|
251
|
+
* poll, so they never issue duplicate requests. The first caller's
|
|
252
|
+
* `timeout`/`signal` drive the shared poll; a later call starts a fresh one.
|
|
253
|
+
*/
|
|
254
|
+
result(options?: SolveHelperOptions): Promise<Solve>;
|
|
255
|
+
/**
|
|
256
|
+
* Cancel the solve. Cancelled solves are never charged, so this is for clean
|
|
257
|
+
* shutdown / freeing slots / stopping early, not cost protection.
|
|
258
|
+
*
|
|
259
|
+
* If the solve already reached a terminal state the API answers 409; that is
|
|
260
|
+
* not an error a caller cares about, so this swallows {@link ConflictError}
|
|
261
|
+
* and returns the current solve state instead. A cancel that lands on a
|
|
262
|
+
* terminal state (cancelled, or whatever the 409-swallow read back) settles
|
|
263
|
+
* this handle's {@link SolveHandle.result}, so any in-flight or later wait
|
|
264
|
+
* stops polling and reflects that terminal state.
|
|
265
|
+
*/
|
|
266
|
+
cancel(options?: {
|
|
267
|
+
signal?: AbortSignal;
|
|
268
|
+
}): Promise<Solve>;
|
|
269
|
+
}
|
|
197
270
|
|
|
198
271
|
/**
|
|
199
272
|
* Base class for every error this library throws. Catch `NoneCapError` to
|
|
@@ -248,8 +321,24 @@ declare class SolveFailedError extends NoneCapError {
|
|
|
248
321
|
readonly solve: Solve;
|
|
249
322
|
constructor(solve: Solve);
|
|
250
323
|
}
|
|
251
|
-
/**
|
|
324
|
+
/**
|
|
325
|
+
* Thrown by {@link NoneCap.solve} (and {@link SolveHandle.result}) when the
|
|
326
|
+
* client-side timeout elapses first. When the timeout came from waiting on a
|
|
327
|
+
* solve, `solveId` and the last-known `solve` are attached so you can still
|
|
328
|
+
* cancel the in-flight solve. They are `undefined` for transport-level timeouts.
|
|
329
|
+
*/
|
|
252
330
|
declare class TimeoutError extends NoneCapError {
|
|
331
|
+
/** The id of the in-flight solve, when the timeout came from a solve wait. */
|
|
332
|
+
readonly solveId: string | undefined;
|
|
333
|
+
/** The last-known solve state, so you can cancel it after a wait timed out. */
|
|
334
|
+
readonly solve: Solve | undefined;
|
|
335
|
+
constructor(message: string, opts?: {
|
|
336
|
+
code?: ErrorCode;
|
|
337
|
+
status?: number;
|
|
338
|
+
param?: string | null;
|
|
339
|
+
solveId?: string;
|
|
340
|
+
solve?: Solve;
|
|
341
|
+
});
|
|
253
342
|
}
|
|
254
343
|
|
|
255
|
-
export { APIError, type Account, AuthenticationError, ConflictError, ConnectionError, type ErrorCode, type ErrorEnvelope, type FetchLike, InsufficientCreditsError, NoneCap, NoneCapError, type NoneCapOptions, NotFoundError, PermissionError, type Proxy, RateLimitError, type Solve, type SolveCreateParams, type SolveError, SolveFailedError, type SolveHelperOptions, type SolveList, type SolveListParams, type SolveStatus, type SolveType, TimeoutError, ValidationError, type WaitOptions, isTerminal };
|
|
344
|
+
export { APIError, type Account, AuthenticationError, ConflictError, ConnectionError, type ErrorCode, type ErrorEnvelope, type FetchLike, InsufficientCreditsError, NoneCap, NoneCapError, type NoneCapOptions, NotFoundError, PermissionError, type Proxy, RateLimitError, type Solve, type SolveCreateParams, type SolveError, SolveFailedError, SolveHandle, type SolveHelperOptions, type SolveList, type SolveListParams, type SolveStartOptions, type SolveStatus, type SolveType, TimeoutError, ValidationError, type WaitOptions, isTerminal };
|
package/dist/index.js
CHANGED
|
@@ -45,6 +45,15 @@ var SolveFailedError = class extends NoneCapError {
|
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
47
|
var TimeoutError = class extends NoneCapError {
|
|
48
|
+
/** The id of the in-flight solve, when the timeout came from a solve wait. */
|
|
49
|
+
solveId;
|
|
50
|
+
/** The last-known solve state, so you can cancel it after a wait timed out. */
|
|
51
|
+
solve;
|
|
52
|
+
constructor(message, opts = {}) {
|
|
53
|
+
super(message, opts);
|
|
54
|
+
this.solveId = opts.solveId;
|
|
55
|
+
this.solve = opts.solve;
|
|
56
|
+
}
|
|
48
57
|
};
|
|
49
58
|
function errorFromResponse(status, code, message, param) {
|
|
50
59
|
const opts = { code, status, param };
|
|
@@ -127,6 +136,25 @@ var NoneCap = class {
|
|
|
127
136
|
cancel: (id, options = {}) => this.#request("DELETE", `/v1/solves/${encodeURIComponent(id)}`, {
|
|
128
137
|
signal: options.signal
|
|
129
138
|
}),
|
|
139
|
+
/**
|
|
140
|
+
* Submit a solve and return a {@link SolveHandle} for it.
|
|
141
|
+
*
|
|
142
|
+
* Unlike {@link NoneCap.solve}, this returns as soon as the submission is
|
|
143
|
+
* accepted, so `handle.id` is available immediately. Use the handle to
|
|
144
|
+
* `cancel()` the solve or `await handle.result()` for the finished solve.
|
|
145
|
+
*
|
|
146
|
+
* ```ts
|
|
147
|
+
* const handle = await nc.solves.start({ type: "hcaptcha", sitekey, url });
|
|
148
|
+
* // ...later, e.g. on shutdown:
|
|
149
|
+
* await handle.cancel();
|
|
150
|
+
* // or wait for it:
|
|
151
|
+
* const { token } = await handle.result();
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
start: async (params, options = {}) => {
|
|
155
|
+
const solve = await this.solves.create(params, { signal: options.signal });
|
|
156
|
+
return new SolveHandle(this, solve);
|
|
157
|
+
},
|
|
130
158
|
/** Fetch one page of solves. */
|
|
131
159
|
list: (params = {}, options = {}) => this.#request("GET", "/v1/solves", {
|
|
132
160
|
query: {
|
|
@@ -156,28 +184,14 @@ var NoneCap = class {
|
|
|
156
184
|
* Uses the server's long-poll under the hood and keeps polling until the
|
|
157
185
|
* solve is terminal or `timeout` elapses. Throws {@link SolveFailedError} if
|
|
158
186
|
* the solve fails/expires/is cancelled, or {@link TimeoutError} on timeout.
|
|
187
|
+
*
|
|
188
|
+
* This is sugar for `(await solves.start(params)).result(options)`. When you
|
|
189
|
+
* need to cancel the solve while it runs, use {@link NoneCap.solves.start}
|
|
190
|
+
* and keep the {@link SolveHandle}.
|
|
159
191
|
*/
|
|
160
192
|
async solve(params, options = {}) {
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
let solve = await this.solves.create(params, {
|
|
164
|
-
wait: waitSeconds(deadline),
|
|
165
|
-
signal: options.signal
|
|
166
|
-
});
|
|
167
|
-
while (!isTerminal(solve.status)) {
|
|
168
|
-
if (Date.now() >= deadline) {
|
|
169
|
-
throw new TimeoutError(
|
|
170
|
-
`Solve ${solve.id} did not finish within ${timeout}ms (last status: ${solve.status}).`,
|
|
171
|
-
{ code: void 0 }
|
|
172
|
-
);
|
|
173
|
-
}
|
|
174
|
-
solve = await this.solves.retrieve(solve.id, {
|
|
175
|
-
wait: waitSeconds(deadline),
|
|
176
|
-
signal: options.signal
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
if (solve.status !== "solved") throw new SolveFailedError(solve);
|
|
180
|
-
return solve;
|
|
193
|
+
const handle = await this.solves.start(params, { signal: options.signal });
|
|
194
|
+
return handle.result(options);
|
|
181
195
|
}
|
|
182
196
|
// -- internals ------------------------------------------------------------
|
|
183
197
|
#timeoutForWait(wait) {
|
|
@@ -266,6 +280,103 @@ var NoneCap = class {
|
|
|
266
280
|
);
|
|
267
281
|
}
|
|
268
282
|
};
|
|
283
|
+
var SolveHandle = class {
|
|
284
|
+
/** The id of the started solve, available as soon as `start()` resolved. */
|
|
285
|
+
id;
|
|
286
|
+
#client;
|
|
287
|
+
/** The latest solve state we have seen, from the create, polls, or cancel. */
|
|
288
|
+
#solve;
|
|
289
|
+
/**
|
|
290
|
+
* The cached TERMINAL outcome: resolves to the solved solve, or rejects with
|
|
291
|
+
* {@link SolveFailedError} if terminal-but-not-solved. Only ever set once the
|
|
292
|
+
* solve actually reaches a terminal state, so it is safe to replay forever. A
|
|
293
|
+
* timed-out wait does NOT settle this — it is retryable.
|
|
294
|
+
*/
|
|
295
|
+
#terminal;
|
|
296
|
+
/** The poll currently in flight, shared by concurrent result() callers. */
|
|
297
|
+
#inflight;
|
|
298
|
+
constructor(client, solve) {
|
|
299
|
+
this.#client = client;
|
|
300
|
+
this.#solve = solve;
|
|
301
|
+
this.id = solve.id;
|
|
302
|
+
}
|
|
303
|
+
/** The last-known solve state for this handle. */
|
|
304
|
+
get solve() {
|
|
305
|
+
return this.#solve;
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Long-poll until the solve is terminal, returning the solved solve. Throws
|
|
309
|
+
* {@link SolveFailedError} if it fails/expires/is cancelled, or
|
|
310
|
+
* {@link TimeoutError} on timeout (with `solveId`/`solve` attached so you can
|
|
311
|
+
* still {@link SolveHandle.cancel} it).
|
|
312
|
+
*
|
|
313
|
+
* Memoizes the *terminal* outcome: once the solve actually finishes, every
|
|
314
|
+
* later call returns that same settled result. A {@link TimeoutError} is not
|
|
315
|
+
* memoized — it just means this wait gave up, so a later call (e.g. with a
|
|
316
|
+
* larger `timeout`) resumes polling. Concurrent calls share one in-flight
|
|
317
|
+
* poll, so they never issue duplicate requests. The first caller's
|
|
318
|
+
* `timeout`/`signal` drive the shared poll; a later call starts a fresh one.
|
|
319
|
+
*/
|
|
320
|
+
result(options = {}) {
|
|
321
|
+
if (this.#terminal) return this.#terminal;
|
|
322
|
+
if (this.#inflight) return this.#inflight;
|
|
323
|
+
const run = this.#poll(options).finally(() => {
|
|
324
|
+
if (this.#inflight === run) this.#inflight = void 0;
|
|
325
|
+
});
|
|
326
|
+
this.#inflight = run;
|
|
327
|
+
return run;
|
|
328
|
+
}
|
|
329
|
+
/**
|
|
330
|
+
* Cancel the solve. Cancelled solves are never charged, so this is for clean
|
|
331
|
+
* shutdown / freeing slots / stopping early, not cost protection.
|
|
332
|
+
*
|
|
333
|
+
* If the solve already reached a terminal state the API answers 409; that is
|
|
334
|
+
* not an error a caller cares about, so this swallows {@link ConflictError}
|
|
335
|
+
* and returns the current solve state instead. A cancel that lands on a
|
|
336
|
+
* terminal state (cancelled, or whatever the 409-swallow read back) settles
|
|
337
|
+
* this handle's {@link SolveHandle.result}, so any in-flight or later wait
|
|
338
|
+
* stops polling and reflects that terminal state.
|
|
339
|
+
*/
|
|
340
|
+
async cancel(options = {}) {
|
|
341
|
+
try {
|
|
342
|
+
this.#solve = await this.#client.solves.cancel(this.id, options);
|
|
343
|
+
} catch (err) {
|
|
344
|
+
if (!(err instanceof ConflictError)) throw err;
|
|
345
|
+
this.#solve = await this.#client.solves.retrieve(this.id, options);
|
|
346
|
+
}
|
|
347
|
+
if (!this.#terminal && isTerminal(this.#solve.status)) this.#cacheTerminal();
|
|
348
|
+
return this.#solve;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Build and memoize the terminal outcome from the current `#solve`. The
|
|
352
|
+
* precondition is that `#solve` is terminal and `#terminal` is unset.
|
|
353
|
+
*/
|
|
354
|
+
#cacheTerminal() {
|
|
355
|
+
const settled = this.#solve.status === "solved" ? Promise.resolve(this.#solve) : Promise.reject(new SolveFailedError(this.#solve));
|
|
356
|
+
settled.catch(() => {
|
|
357
|
+
});
|
|
358
|
+
this.#terminal = settled;
|
|
359
|
+
return settled;
|
|
360
|
+
}
|
|
361
|
+
async #poll(options) {
|
|
362
|
+
const timeout = options.timeout ?? 18e4;
|
|
363
|
+
const deadline = Date.now() + timeout;
|
|
364
|
+
for (; ; ) {
|
|
365
|
+
if (this.#terminal) return this.#terminal;
|
|
366
|
+
if (isTerminal(this.#solve.status)) return this.#cacheTerminal();
|
|
367
|
+
if (Date.now() >= deadline) {
|
|
368
|
+
throw new TimeoutError(
|
|
369
|
+
`Solve ${this.id} did not finish within ${timeout}ms (last status: ${this.#solve.status}).`,
|
|
370
|
+
{ solveId: this.id, solve: this.#solve }
|
|
371
|
+
);
|
|
372
|
+
}
|
|
373
|
+
this.#solve = await this.#client.solves.retrieve(this.id, {
|
|
374
|
+
wait: waitSeconds(deadline),
|
|
375
|
+
signal: options.signal
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
};
|
|
269
380
|
function waitSeconds(deadline) {
|
|
270
381
|
const remaining = Math.ceil((deadline - Date.now()) / 1e3);
|
|
271
382
|
return Math.max(1, Math.min(MAX_WAIT_SECONDS, remaining));
|
|
@@ -274,6 +385,6 @@ function isAbort(err) {
|
|
|
274
385
|
return err instanceof Error && err.name === "AbortError";
|
|
275
386
|
}
|
|
276
387
|
|
|
277
|
-
export { APIError, AuthenticationError, ConflictError, ConnectionError, InsufficientCreditsError, NoneCap, NoneCapError, NotFoundError, PermissionError, RateLimitError, SolveFailedError, TimeoutError, ValidationError, isTerminal };
|
|
388
|
+
export { APIError, AuthenticationError, ConflictError, ConnectionError, InsufficientCreditsError, NoneCap, NoneCapError, NotFoundError, PermissionError, RateLimitError, SolveFailedError, SolveHandle, TimeoutError, ValidationError, isTerminal };
|
|
278
389
|
//# sourceMappingURL=index.js.map
|
|
279
390
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ErrorCode, Solve } from \"./types.js\";\n\n/**\n * Base class for every error this library throws. Catch `NoneCapError` to\n * handle all of them, or catch a subclass to handle one kind.\n */\nexport class NoneCapError extends Error {\n /** Machine-readable error code from the API envelope, when there is one. */\n readonly code: ErrorCode | undefined;\n /** HTTP status, when the error came from a response. */\n readonly status: number | undefined;\n /** The request field that was rejected, for validation errors. */\n readonly param: string | null;\n\n constructor(\n message: string,\n opts: { code?: ErrorCode; status?: number; param?: string | null } = {},\n ) {\n super(message);\n this.name = new.target.name;\n this.code = opts.code;\n this.status = opts.status;\n this.param = opts.param ?? null;\n // Restore the prototype chain when compiled down to ES5-era targets.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** 401 — the API key is missing, malformed, or revoked. */\nexport class AuthenticationError extends NoneCapError {}\n\n/** 403 — the key is valid but not allowed to do this (scope or locked account). */\nexport class PermissionError extends NoneCapError {}\n\n/** 402 — the account is out of credits. */\nexport class InsufficientCreditsError extends NoneCapError {}\n\n/** 422 / 400 — the request was rejected. `param` names the offending field. */\nexport class ValidationError extends NoneCapError {}\n\n/** 404 — no such resource. */\nexport class NotFoundError extends NoneCapError {}\n\n/** 409 — the solve is already in a terminal state (e.g. cancelling a finished solve). */\nexport class ConflictError extends NoneCapError {}\n\n/** 429 — too many concurrent solves, or rate limited. Back off and retry. */\nexport class RateLimitError extends NoneCapError {}\n\n/** 5xx, or a response that wasn't the expected shape. */\nexport class APIError extends NoneCapError {}\n\n/** The request never reached the API (DNS, TCP, TLS, timeout, offline). */\nexport class ConnectionError extends NoneCapError {}\n\n/**\n * Thrown by {@link NoneCap.solve} when a solve reaches a terminal state without\n * a token: `failed`, `expired`, or `cancelled`. The full solve is attached so\n * you can inspect `solve.error` and timings.\n */\nexport class SolveFailedError extends NoneCapError {\n readonly solve: Solve;\n\n constructor(solve: Solve) {\n const detail = solve.error ? `${solve.error.code}: ${solve.error.message}` : solve.status;\n super(`Solve ${solve.id} ${solve.status} (${detail})`, {\n code: undefined,\n status: undefined,\n });\n this.solve = solve;\n }\n}\n\n/** Thrown by {@link NoneCap.solve} when the client-side timeout elapses first. */\nexport class TimeoutError extends NoneCapError {}\n\n/** Map an API error envelope (plus HTTP status) to the right error subclass. */\nexport function errorFromResponse(\n status: number,\n code: ErrorCode | undefined,\n message: string,\n param: string | null,\n): NoneCapError {\n const opts = { code, status, param };\n switch (code) {\n case \"unauthorized\":\n return new AuthenticationError(message, opts);\n case \"forbidden\":\n case \"account_locked\":\n return new PermissionError(message, opts);\n case \"insufficient_credits\":\n return new InsufficientCreditsError(message, opts);\n case \"invalid_request\":\n case \"validation_error\":\n return new ValidationError(message, opts);\n case \"not_found\":\n return new NotFoundError(message, opts);\n case \"conflict\":\n return new ConflictError(message, opts);\n case \"rate_limited\":\n case \"concurrency_limit_exceeded\":\n case \"ext_daily_limit\":\n return new RateLimitError(message, opts);\n default:\n return new APIError(message, opts);\n }\n}\n","import {\n APIError,\n ConnectionError,\n NoneCapError,\n SolveFailedError,\n TimeoutError,\n errorFromResponse,\n} from \"./errors.js\";\nimport type {\n Account,\n ErrorCode,\n Solve,\n SolveCreateParams,\n SolveList,\n SolveListParams,\n SolveStatus,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.nonecap.com\";\n/** The API caps server-side long-poll at 90 seconds. */\nconst MAX_WAIT_SECONDS = 90;\nconst TERMINAL: ReadonlySet<SolveStatus> = new Set([\n \"solved\",\n \"failed\",\n \"cancelled\",\n \"expired\",\n]);\n\n/** Is this a terminal solve status? */\nexport function isTerminal(status: SolveStatus): boolean {\n return TERMINAL.has(status);\n}\n\n/** A `fetch` implementation. Defaults to the global `fetch`. */\nexport type FetchLike = (\n input: string,\n init?: RequestInit,\n) => Promise<Response>;\n\n/** Options for constructing a {@link NoneCap} client. */\nexport interface NoneCapOptions {\n /** Your NoneCap API key. Find it at https://dashboard.nonecap.com. */\n apiKey: string;\n /** Override the API base URL. Defaults to https://api.nonecap.com. */\n baseURL?: string;\n /**\n * Per-request timeout in milliseconds for a single HTTP call. Defaults to\n * 100000 (just above the 90s server long-poll). Note this bounds one HTTP\n * round trip, not the whole {@link NoneCap.solve} wait — use the `timeout`\n * option on `solve()` for that.\n */\n timeout?: number;\n /** Inject a custom `fetch` (for testing, proxies, or non-standard runtimes). */\n fetch?: FetchLike;\n}\n\n/** Per-call options for the long-polling endpoints. */\nexport interface WaitOptions {\n /**\n * Hold the connection open up to this many seconds (1–90) waiting for the\n * solve to finish before responding. Omit to return immediately.\n */\n wait?: number;\n /** Abort the request. */\n signal?: AbortSignal;\n}\n\n/** Options for {@link NoneCap.solve}. */\nexport interface SolveHelperOptions {\n /**\n * Give up after this many milliseconds and throw {@link TimeoutError}.\n * Defaults to 180000 (3 minutes).\n */\n timeout?: number;\n /** Abort waiting. */\n signal?: AbortSignal;\n}\n\ninterface RequestOptions {\n query?: Record<string, string | number | undefined>;\n body?: unknown;\n signal?: AbortSignal;\n /** Override the per-request timeout for this call (used by long polls). */\n timeout?: number;\n /** HTTP statuses to treat as success in addition to 2xx (e.g. 202). */\n expect?: number[];\n}\n\n/**\n * The NoneCap API client.\n *\n * ```ts\n * const nc = new NoneCap({ apiKey: process.env.NONECAP_KEY! });\n * const { token } = await nc.solve({ type: \"hcaptcha\", sitekey, url });\n * ```\n */\nexport class NoneCap {\n readonly #apiKey: string;\n readonly #baseURL: string;\n readonly #timeout: number;\n readonly #fetch: FetchLike;\n\n constructor(options: NoneCapOptions) {\n if (!options || !options.apiKey) {\n throw new NoneCapError(\n \"A NoneCap API key is required. Pass it as `new NoneCap({ apiKey })`.\",\n );\n }\n this.#apiKey = options.apiKey;\n this.#baseURL = (options.baseURL ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.#timeout = options.timeout ?? 100_000;\n const f = options.fetch ?? (globalThis.fetch as FetchLike | undefined);\n if (!f) {\n throw new NoneCapError(\n \"No `fetch` available. Use Node 18+, or pass a `fetch` in the client options.\",\n );\n }\n this.#fetch = f;\n }\n\n /** Operations on solves. */\n readonly solves = {\n /** Submit a solve. Pass `{ wait }` to hold the connection until it finishes. */\n create: (params: SolveCreateParams, options: WaitOptions = {}): Promise<Solve> =>\n this.#request<Solve>(\"POST\", \"/v1/solves\", {\n body: params,\n query: { wait: options.wait },\n signal: options.signal,\n timeout: this.#timeoutForWait(options.wait),\n expect: [202],\n }),\n\n /** Fetch a solve by id. Pass `{ wait }` to long-poll until it finishes. */\n retrieve: (id: string, options: WaitOptions = {}): Promise<Solve> =>\n this.#request<Solve>(\"GET\", `/v1/solves/${encodeURIComponent(id)}`, {\n query: { wait: options.wait },\n signal: options.signal,\n timeout: this.#timeoutForWait(options.wait),\n expect: [202],\n }),\n\n /** Cancel a pending or in-flight solve. */\n cancel: (id: string, options: { signal?: AbortSignal } = {}): Promise<Solve> =>\n this.#request<Solve>(\"DELETE\", `/v1/solves/${encodeURIComponent(id)}`, {\n signal: options.signal,\n }),\n\n /** Fetch one page of solves. */\n list: (params: SolveListParams = {}, options: { signal?: AbortSignal } = {}): Promise<SolveList> =>\n this.#request<SolveList>(\"GET\", \"/v1/solves\", {\n query: {\n limit: params.limit,\n starting_after: params.starting_after,\n status: params.status,\n type: params.type,\n },\n signal: options.signal,\n }),\n\n /**\n * Iterate every solve across pages, newest first.\n *\n * ```ts\n * for await (const solve of nc.solves.listAll()) { ... }\n * ```\n */\n listAll: (\n params: SolveListParams = {},\n options: { signal?: AbortSignal } = {},\n ): AsyncIterableIterator<Solve> => this.#listAll(params, options),\n };\n\n /** Fetch your account, including the current credit balance. */\n me(options: { signal?: AbortSignal } = {}): Promise<Account> {\n return this.#request<Account>(\"GET\", \"/v1/me\", { signal: options.signal });\n }\n\n /**\n * Submit a solve and wait for it to finish, returning the solved solve.\n *\n * Uses the server's long-poll under the hood and keeps polling until the\n * solve is terminal or `timeout` elapses. Throws {@link SolveFailedError} if\n * the solve fails/expires/is cancelled, or {@link TimeoutError} on timeout.\n */\n async solve(params: SolveCreateParams, options: SolveHelperOptions = {}): Promise<Solve> {\n const timeout = options.timeout ?? 180_000;\n const deadline = Date.now() + timeout;\n\n let solve = await this.solves.create(params, {\n wait: waitSeconds(deadline),\n signal: options.signal,\n });\n\n while (!isTerminal(solve.status)) {\n if (Date.now() >= deadline) {\n throw new TimeoutError(\n `Solve ${solve.id} did not finish within ${timeout}ms (last status: ${solve.status}).`,\n { code: undefined },\n );\n }\n solve = await this.solves.retrieve(solve.id, {\n wait: waitSeconds(deadline),\n signal: options.signal,\n });\n }\n\n if (solve.status !== \"solved\") throw new SolveFailedError(solve);\n return solve;\n }\n\n // -- internals ------------------------------------------------------------\n\n #timeoutForWait(wait?: number): number | undefined {\n if (wait === undefined) return undefined;\n // Give the socket a margin beyond the server's long-poll window.\n return wait * 1000 + 15_000;\n }\n\n async *#listAll(\n params: SolveListParams,\n options: { signal?: AbortSignal },\n ): AsyncIterableIterator<Solve> {\n let cursor = params.starting_after;\n for (;;) {\n const page = await this.solves.list({ ...params, starting_after: cursor }, options);\n for (const solve of page.data) yield solve;\n if (!page.has_more || page.data.length === 0) return;\n cursor = page.data[page.data.length - 1]!.id;\n }\n }\n\n async #request<T>(method: string, path: string, options: RequestOptions = {}): Promise<T> {\n const url = new URL(this.#baseURL + path);\n if (options.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value !== undefined && value !== \"\") url.searchParams.set(key, String(value));\n }\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.#apiKey}`,\n Accept: \"application/json\",\n };\n let bodyInit: string | undefined;\n if (options.body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n bodyInit = JSON.stringify(options.body);\n }\n\n const controller = new AbortController();\n const timeoutMs = options.timeout ?? this.#timeout;\n const timer = setTimeout(() => controller.abort(new TimeoutError(`Request timed out after ${timeoutMs}ms.`)), timeoutMs);\n const onAbort = () => controller.abort(options.signal?.reason);\n if (options.signal) {\n if (options.signal.aborted) onAbort();\n else options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n let res: Response;\n try {\n res = await this.#fetch(url.toString(), {\n method,\n headers,\n body: bodyInit,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof TimeoutError) throw err;\n if (isAbort(err)) {\n // A caller-supplied signal aborting surfaces as the caller's reason.\n if (options.signal?.aborted) throw options.signal.reason instanceof Error\n ? options.signal.reason\n : new NoneCapError(\"Request aborted.\");\n throw new TimeoutError(`Request timed out after ${timeoutMs}ms.`);\n }\n throw new ConnectionError(\n `Could not reach the NoneCap API at ${this.#baseURL}: ${(err as Error).message}`,\n );\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n }\n\n return this.#parse<T>(res, options.expect ?? []);\n }\n\n async #parse<T>(res: Response, expect: number[]): Promise<T> {\n const text = await res.text();\n let json: unknown;\n if (text) {\n try {\n json = JSON.parse(text);\n } catch {\n throw new APIError(\n `Unexpected non-JSON response (HTTP ${res.status}): ${text.slice(0, 200)}`,\n { status: res.status },\n );\n }\n }\n\n if (res.ok || expect.includes(res.status)) {\n return json as T;\n }\n\n const envelope = json as { error?: { code?: ErrorCode; message?: string; param?: string | null } } | undefined;\n const apiError = envelope?.error;\n throw errorFromResponse(\n res.status,\n apiError?.code,\n apiError?.message ?? `HTTP ${res.status}`,\n apiError?.param ?? null,\n );\n }\n}\n\n/**\n * The `wait` value for the next long-poll: whole seconds until `deadline`,\n * clamped to the server's 1–90 window. The floor of 1 keeps the param valid,\n * so callers must decide whether the deadline has passed with the clock, not\n * with this return value.\n */\nfunction waitSeconds(deadline: number): number {\n const remaining = Math.ceil((deadline - Date.now()) / 1000);\n return Math.max(1, Math.min(MAX_WAIT_SECONDS, remaining));\n}\n\nfunction isAbort(err: unknown): boolean {\n return err instanceof Error && err.name === \"AbortError\";\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/errors.ts","../src/client.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["import type { ErrorCode, Solve } from \"./types.js\";\n\n/**\n * Base class for every error this library throws. Catch `NoneCapError` to\n * handle all of them, or catch a subclass to handle one kind.\n */\nexport class NoneCapError extends Error {\n /** Machine-readable error code from the API envelope, when there is one. */\n readonly code: ErrorCode | undefined;\n /** HTTP status, when the error came from a response. */\n readonly status: number | undefined;\n /** The request field that was rejected, for validation errors. */\n readonly param: string | null;\n\n constructor(\n message: string,\n opts: { code?: ErrorCode; status?: number; param?: string | null } = {},\n ) {\n super(message);\n this.name = new.target.name;\n this.code = opts.code;\n this.status = opts.status;\n this.param = opts.param ?? null;\n // Restore the prototype chain when compiled down to ES5-era targets.\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\n/** 401 — the API key is missing, malformed, or revoked. */\nexport class AuthenticationError extends NoneCapError {}\n\n/** 403 — the key is valid but not allowed to do this (scope or locked account). */\nexport class PermissionError extends NoneCapError {}\n\n/** 402 — the account is out of credits. */\nexport class InsufficientCreditsError extends NoneCapError {}\n\n/** 422 / 400 — the request was rejected. `param` names the offending field. */\nexport class ValidationError extends NoneCapError {}\n\n/** 404 — no such resource. */\nexport class NotFoundError extends NoneCapError {}\n\n/** 409 — the solve is already in a terminal state (e.g. cancelling a finished solve). */\nexport class ConflictError extends NoneCapError {}\n\n/** 429 — too many concurrent solves, or rate limited. Back off and retry. */\nexport class RateLimitError extends NoneCapError {}\n\n/** 5xx, or a response that wasn't the expected shape. */\nexport class APIError extends NoneCapError {}\n\n/** The request never reached the API (DNS, TCP, TLS, timeout, offline). */\nexport class ConnectionError extends NoneCapError {}\n\n/**\n * Thrown by {@link NoneCap.solve} when a solve reaches a terminal state without\n * a token: `failed`, `expired`, or `cancelled`. The full solve is attached so\n * you can inspect `solve.error` and timings.\n */\nexport class SolveFailedError extends NoneCapError {\n readonly solve: Solve;\n\n constructor(solve: Solve) {\n const detail = solve.error ? `${solve.error.code}: ${solve.error.message}` : solve.status;\n super(`Solve ${solve.id} ${solve.status} (${detail})`, {\n code: undefined,\n status: undefined,\n });\n this.solve = solve;\n }\n}\n\n/**\n * Thrown by {@link NoneCap.solve} (and {@link SolveHandle.result}) when the\n * client-side timeout elapses first. When the timeout came from waiting on a\n * solve, `solveId` and the last-known `solve` are attached so you can still\n * cancel the in-flight solve. They are `undefined` for transport-level timeouts.\n */\nexport class TimeoutError extends NoneCapError {\n /** The id of the in-flight solve, when the timeout came from a solve wait. */\n readonly solveId: string | undefined;\n /** The last-known solve state, so you can cancel it after a wait timed out. */\n readonly solve: Solve | undefined;\n\n constructor(\n message: string,\n opts: {\n code?: ErrorCode;\n status?: number;\n param?: string | null;\n solveId?: string;\n solve?: Solve;\n } = {},\n ) {\n super(message, opts);\n this.solveId = opts.solveId;\n this.solve = opts.solve;\n }\n}\n\n/** Map an API error envelope (plus HTTP status) to the right error subclass. */\nexport function errorFromResponse(\n status: number,\n code: ErrorCode | undefined,\n message: string,\n param: string | null,\n): NoneCapError {\n const opts = { code, status, param };\n switch (code) {\n case \"unauthorized\":\n return new AuthenticationError(message, opts);\n case \"forbidden\":\n case \"account_locked\":\n return new PermissionError(message, opts);\n case \"insufficient_credits\":\n return new InsufficientCreditsError(message, opts);\n case \"invalid_request\":\n case \"validation_error\":\n return new ValidationError(message, opts);\n case \"not_found\":\n return new NotFoundError(message, opts);\n case \"conflict\":\n return new ConflictError(message, opts);\n case \"rate_limited\":\n case \"concurrency_limit_exceeded\":\n case \"ext_daily_limit\":\n return new RateLimitError(message, opts);\n default:\n return new APIError(message, opts);\n }\n}\n","import {\n APIError,\n ConflictError,\n ConnectionError,\n NoneCapError,\n SolveFailedError,\n TimeoutError,\n errorFromResponse,\n} from \"./errors.js\";\nimport type {\n Account,\n ErrorCode,\n Solve,\n SolveCreateParams,\n SolveList,\n SolveListParams,\n SolveStatus,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.nonecap.com\";\n/** The API caps server-side long-poll at 90 seconds. */\nconst MAX_WAIT_SECONDS = 90;\nconst TERMINAL: ReadonlySet<SolveStatus> = new Set([\n \"solved\",\n \"failed\",\n \"cancelled\",\n \"expired\",\n]);\n\n/** Is this a terminal solve status? */\nexport function isTerminal(status: SolveStatus): boolean {\n return TERMINAL.has(status);\n}\n\n/** A `fetch` implementation. Defaults to the global `fetch`. */\nexport type FetchLike = (\n input: string,\n init?: RequestInit,\n) => Promise<Response>;\n\n/** Options for constructing a {@link NoneCap} client. */\nexport interface NoneCapOptions {\n /** Your NoneCap API key. Find it at https://dashboard.nonecap.com. */\n apiKey: string;\n /** Override the API base URL. Defaults to https://api.nonecap.com. */\n baseURL?: string;\n /**\n * Per-request timeout in milliseconds for a single HTTP call. Defaults to\n * 100000 (just above the 90s server long-poll). Note this bounds one HTTP\n * round trip, not the whole {@link NoneCap.solve} wait — use the `timeout`\n * option on `solve()` for that.\n */\n timeout?: number;\n /** Inject a custom `fetch` (for testing, proxies, or non-standard runtimes). */\n fetch?: FetchLike;\n}\n\n/** Per-call options for the long-polling endpoints. */\nexport interface WaitOptions {\n /**\n * Hold the connection open up to this many seconds (1–90) waiting for the\n * solve to finish before responding. Omit to return immediately.\n */\n wait?: number;\n /** Abort the request. */\n signal?: AbortSignal;\n}\n\n/** Options for {@link NoneCap.solve} and {@link SolveHandle.result}. */\nexport interface SolveHelperOptions {\n /**\n * Give up after this many milliseconds and throw {@link TimeoutError}.\n * Defaults to 180000 (3 minutes).\n */\n timeout?: number;\n /** Abort waiting. */\n signal?: AbortSignal;\n}\n\n/** Options for {@link NoneCap.solves.start}. */\nexport interface SolveStartOptions {\n /** Abort the submission. */\n signal?: AbortSignal;\n}\n\ninterface RequestOptions {\n query?: Record<string, string | number | undefined>;\n body?: unknown;\n signal?: AbortSignal;\n /** Override the per-request timeout for this call (used by long polls). */\n timeout?: number;\n /** HTTP statuses to treat as success in addition to 2xx (e.g. 202). */\n expect?: number[];\n}\n\n/**\n * The NoneCap API client.\n *\n * ```ts\n * const nc = new NoneCap({ apiKey: process.env.NONECAP_KEY! });\n * const { token } = await nc.solve({ type: \"hcaptcha\", sitekey, url });\n * ```\n */\nexport class NoneCap {\n readonly #apiKey: string;\n readonly #baseURL: string;\n readonly #timeout: number;\n readonly #fetch: FetchLike;\n\n constructor(options: NoneCapOptions) {\n if (!options || !options.apiKey) {\n throw new NoneCapError(\n \"A NoneCap API key is required. Pass it as `new NoneCap({ apiKey })`.\",\n );\n }\n this.#apiKey = options.apiKey;\n this.#baseURL = (options.baseURL ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.#timeout = options.timeout ?? 100_000;\n const f = options.fetch ?? (globalThis.fetch as FetchLike | undefined);\n if (!f) {\n throw new NoneCapError(\n \"No `fetch` available. Use Node 18+, or pass a `fetch` in the client options.\",\n );\n }\n this.#fetch = f;\n }\n\n /** Operations on solves. */\n readonly solves = {\n /** Submit a solve. Pass `{ wait }` to hold the connection until it finishes. */\n create: (params: SolveCreateParams, options: WaitOptions = {}): Promise<Solve> =>\n this.#request<Solve>(\"POST\", \"/v1/solves\", {\n body: params,\n query: { wait: options.wait },\n signal: options.signal,\n timeout: this.#timeoutForWait(options.wait),\n expect: [202],\n }),\n\n /** Fetch a solve by id. Pass `{ wait }` to long-poll until it finishes. */\n retrieve: (id: string, options: WaitOptions = {}): Promise<Solve> =>\n this.#request<Solve>(\"GET\", `/v1/solves/${encodeURIComponent(id)}`, {\n query: { wait: options.wait },\n signal: options.signal,\n timeout: this.#timeoutForWait(options.wait),\n expect: [202],\n }),\n\n /** Cancel a pending or in-flight solve. */\n cancel: (id: string, options: { signal?: AbortSignal } = {}): Promise<Solve> =>\n this.#request<Solve>(\"DELETE\", `/v1/solves/${encodeURIComponent(id)}`, {\n signal: options.signal,\n }),\n\n /**\n * Submit a solve and return a {@link SolveHandle} for it.\n *\n * Unlike {@link NoneCap.solve}, this returns as soon as the submission is\n * accepted, so `handle.id` is available immediately. Use the handle to\n * `cancel()` the solve or `await handle.result()` for the finished solve.\n *\n * ```ts\n * const handle = await nc.solves.start({ type: \"hcaptcha\", sitekey, url });\n * // ...later, e.g. on shutdown:\n * await handle.cancel();\n * // or wait for it:\n * const { token } = await handle.result();\n * ```\n */\n start: async (\n params: SolveCreateParams,\n options: SolveStartOptions = {},\n ): Promise<SolveHandle> => {\n const solve = await this.solves.create(params, { signal: options.signal });\n return new SolveHandle(this, solve);\n },\n\n /** Fetch one page of solves. */\n list: (params: SolveListParams = {}, options: { signal?: AbortSignal } = {}): Promise<SolveList> =>\n this.#request<SolveList>(\"GET\", \"/v1/solves\", {\n query: {\n limit: params.limit,\n starting_after: params.starting_after,\n status: params.status,\n type: params.type,\n },\n signal: options.signal,\n }),\n\n /**\n * Iterate every solve across pages, newest first.\n *\n * ```ts\n * for await (const solve of nc.solves.listAll()) { ... }\n * ```\n */\n listAll: (\n params: SolveListParams = {},\n options: { signal?: AbortSignal } = {},\n ): AsyncIterableIterator<Solve> => this.#listAll(params, options),\n };\n\n /** Fetch your account, including the current credit balance. */\n me(options: { signal?: AbortSignal } = {}): Promise<Account> {\n return this.#request<Account>(\"GET\", \"/v1/me\", { signal: options.signal });\n }\n\n /**\n * Submit a solve and wait for it to finish, returning the solved solve.\n *\n * Uses the server's long-poll under the hood and keeps polling until the\n * solve is terminal or `timeout` elapses. Throws {@link SolveFailedError} if\n * the solve fails/expires/is cancelled, or {@link TimeoutError} on timeout.\n *\n * This is sugar for `(await solves.start(params)).result(options)`. When you\n * need to cancel the solve while it runs, use {@link NoneCap.solves.start}\n * and keep the {@link SolveHandle}.\n */\n async solve(params: SolveCreateParams, options: SolveHelperOptions = {}): Promise<Solve> {\n const handle = await this.solves.start(params, { signal: options.signal });\n return handle.result(options);\n }\n\n // -- internals ------------------------------------------------------------\n\n #timeoutForWait(wait?: number): number | undefined {\n if (wait === undefined) return undefined;\n // Give the socket a margin beyond the server's long-poll window.\n return wait * 1000 + 15_000;\n }\n\n async *#listAll(\n params: SolveListParams,\n options: { signal?: AbortSignal },\n ): AsyncIterableIterator<Solve> {\n let cursor = params.starting_after;\n for (;;) {\n const page = await this.solves.list({ ...params, starting_after: cursor }, options);\n for (const solve of page.data) yield solve;\n if (!page.has_more || page.data.length === 0) return;\n cursor = page.data[page.data.length - 1]!.id;\n }\n }\n\n async #request<T>(method: string, path: string, options: RequestOptions = {}): Promise<T> {\n const url = new URL(this.#baseURL + path);\n if (options.query) {\n for (const [key, value] of Object.entries(options.query)) {\n if (value !== undefined && value !== \"\") url.searchParams.set(key, String(value));\n }\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${this.#apiKey}`,\n Accept: \"application/json\",\n };\n let bodyInit: string | undefined;\n if (options.body !== undefined) {\n headers[\"Content-Type\"] = \"application/json\";\n bodyInit = JSON.stringify(options.body);\n }\n\n const controller = new AbortController();\n const timeoutMs = options.timeout ?? this.#timeout;\n const timer = setTimeout(() => controller.abort(new TimeoutError(`Request timed out after ${timeoutMs}ms.`)), timeoutMs);\n const onAbort = () => controller.abort(options.signal?.reason);\n if (options.signal) {\n if (options.signal.aborted) onAbort();\n else options.signal.addEventListener(\"abort\", onAbort, { once: true });\n }\n\n let res: Response;\n try {\n res = await this.#fetch(url.toString(), {\n method,\n headers,\n body: bodyInit,\n signal: controller.signal,\n });\n } catch (err) {\n if (err instanceof TimeoutError) throw err;\n if (isAbort(err)) {\n // A caller-supplied signal aborting surfaces as the caller's reason.\n if (options.signal?.aborted) throw options.signal.reason instanceof Error\n ? options.signal.reason\n : new NoneCapError(\"Request aborted.\");\n throw new TimeoutError(`Request timed out after ${timeoutMs}ms.`);\n }\n throw new ConnectionError(\n `Could not reach the NoneCap API at ${this.#baseURL}: ${(err as Error).message}`,\n );\n } finally {\n clearTimeout(timer);\n options.signal?.removeEventListener(\"abort\", onAbort);\n }\n\n return this.#parse<T>(res, options.expect ?? []);\n }\n\n async #parse<T>(res: Response, expect: number[]): Promise<T> {\n const text = await res.text();\n let json: unknown;\n if (text) {\n try {\n json = JSON.parse(text);\n } catch {\n throw new APIError(\n `Unexpected non-JSON response (HTTP ${res.status}): ${text.slice(0, 200)}`,\n { status: res.status },\n );\n }\n }\n\n if (res.ok || expect.includes(res.status)) {\n return json as T;\n }\n\n const envelope = json as { error?: { code?: ErrorCode; message?: string; param?: string | null } } | undefined;\n const apiError = envelope?.error;\n throw errorFromResponse(\n res.status,\n apiError?.code,\n apiError?.message ?? `HTTP ${res.status}`,\n apiError?.param ?? null,\n );\n }\n}\n\n/**\n * A handle on a started solve, returned by {@link NoneCap.solves.start}.\n *\n * The handle is the cancellable lifecycle object for one solve. It is a plain\n * object, not a promise: `await`ing it does nothing useful. Wait for the result\n * with {@link SolveHandle.result}, and cancel with {@link SolveHandle.cancel}.\n * You do not construct it yourself — `solves.start()` hands you one with `id`\n * already resolved.\n */\nexport class SolveHandle {\n /** The id of the started solve, available as soon as `start()` resolved. */\n readonly id: string;\n\n readonly #client: NoneCap;\n /** The latest solve state we have seen, from the create, polls, or cancel. */\n #solve: Solve;\n /**\n * The cached TERMINAL outcome: resolves to the solved solve, or rejects with\n * {@link SolveFailedError} if terminal-but-not-solved. Only ever set once the\n * solve actually reaches a terminal state, so it is safe to replay forever. A\n * timed-out wait does NOT settle this — it is retryable.\n */\n #terminal: Promise<Solve> | undefined;\n /** The poll currently in flight, shared by concurrent result() callers. */\n #inflight: Promise<Solve> | undefined;\n\n constructor(client: NoneCap, solve: Solve) {\n this.#client = client;\n this.#solve = solve;\n this.id = solve.id;\n }\n\n /** The last-known solve state for this handle. */\n get solve(): Solve {\n return this.#solve;\n }\n\n /**\n * Long-poll until the solve is terminal, returning the solved solve. Throws\n * {@link SolveFailedError} if it fails/expires/is cancelled, or\n * {@link TimeoutError} on timeout (with `solveId`/`solve` attached so you can\n * still {@link SolveHandle.cancel} it).\n *\n * Memoizes the *terminal* outcome: once the solve actually finishes, every\n * later call returns that same settled result. A {@link TimeoutError} is not\n * memoized — it just means this wait gave up, so a later call (e.g. with a\n * larger `timeout`) resumes polling. Concurrent calls share one in-flight\n * poll, so they never issue duplicate requests. The first caller's\n * `timeout`/`signal` drive the shared poll; a later call starts a fresh one.\n */\n result(options: SolveHelperOptions = {}): Promise<Solve> {\n if (this.#terminal) return this.#terminal;\n if (this.#inflight) return this.#inflight;\n const run = this.#poll(options).finally(() => {\n // Free the slot so a retry can re-poll after a timeout; a terminal\n // outcome is already memoized in #terminal and wins on the next call.\n if (this.#inflight === run) this.#inflight = undefined;\n });\n this.#inflight = run;\n return run;\n }\n\n /**\n * Cancel the solve. Cancelled solves are never charged, so this is for clean\n * shutdown / freeing slots / stopping early, not cost protection.\n *\n * If the solve already reached a terminal state the API answers 409; that is\n * not an error a caller cares about, so this swallows {@link ConflictError}\n * and returns the current solve state instead. A cancel that lands on a\n * terminal state (cancelled, or whatever the 409-swallow read back) settles\n * this handle's {@link SolveHandle.result}, so any in-flight or later wait\n * stops polling and reflects that terminal state.\n */\n async cancel(options: { signal?: AbortSignal } = {}): Promise<Solve> {\n try {\n this.#solve = await this.#client.solves.cancel(this.id, options);\n } catch (err) {\n if (!(err instanceof ConflictError)) throw err;\n // Already terminal — report the real state rather than the 409.\n this.#solve = await this.#client.solves.retrieve(this.id, options);\n }\n if (!this.#terminal && isTerminal(this.#solve.status)) this.#cacheTerminal();\n return this.#solve;\n }\n\n /**\n * Build and memoize the terminal outcome from the current `#solve`. The\n * precondition is that `#solve` is terminal and `#terminal` is unset.\n */\n #cacheTerminal(): Promise<Solve> {\n const settled =\n this.#solve.status === \"solved\"\n ? Promise.resolve(this.#solve)\n : Promise.reject(new SolveFailedError(this.#solve));\n // Never let the memoized rejection surface as an unhandled rejection; real\n // callers read it explicitly through result().\n settled.catch(() => {});\n this.#terminal = settled;\n return settled;\n }\n\n async #poll(options: SolveHelperOptions): Promise<Solve> {\n const timeout = options.timeout ?? 180_000;\n const deadline = Date.now() + timeout;\n\n for (;;) {\n // cancel() (or a previous poll) may have already settled this handle.\n if (this.#terminal) return this.#terminal;\n if (isTerminal(this.#solve.status)) return this.#cacheTerminal();\n if (Date.now() >= deadline) {\n throw new TimeoutError(\n `Solve ${this.id} did not finish within ${timeout}ms (last status: ${this.#solve.status}).`,\n { solveId: this.id, solve: this.#solve },\n );\n }\n this.#solve = await this.#client.solves.retrieve(this.id, {\n wait: waitSeconds(deadline),\n signal: options.signal,\n });\n }\n }\n}\n\n/**\n * The `wait` value for the next long-poll: whole seconds until `deadline`,\n * clamped to the server's 1–90 window. The floor of 1 keeps the param valid,\n * so callers must decide whether the deadline has passed with the clock, not\n * with this return value.\n */\nfunction waitSeconds(deadline: number): number {\n const remaining = Math.ceil((deadline - Date.now()) / 1000);\n return Math.max(1, Math.min(MAX_WAIT_SECONDS, remaining));\n}\n\nfunction isAbort(err: unknown): boolean {\n return err instanceof Error && err.name === \"AbortError\";\n}\n"]}
|