nonecap 0.2.0 → 0.4.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 +88 -1
- package/dist/index.cjs +219 -20
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +232 -4
- package/dist/index.d.ts +232 -4
- package/dist/index.js +218 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -89,6 +89,94 @@ interface SolveList {
|
|
|
89
89
|
data: Solve[];
|
|
90
90
|
has_more: boolean;
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* What the downstream target did with a solve's token.
|
|
94
|
+
*
|
|
95
|
+
* Only `accepted` and `rejected` count toward the acceptance rate — the other
|
|
96
|
+
* three are recorded but kept out of the denominator, so a client-side outage
|
|
97
|
+
* can't look like a token-quality regression.
|
|
98
|
+
*/
|
|
99
|
+
type FeedbackOutcome =
|
|
100
|
+
/** Downstream accepted the token. */
|
|
101
|
+
"accepted"
|
|
102
|
+
/** Downstream rejected the token. */
|
|
103
|
+
| "rejected"
|
|
104
|
+
/** Token was submitted but the verdict couldn't be determined. */
|
|
105
|
+
| "unknown"
|
|
106
|
+
/** Token was never submitted downstream (expired, aborted, deduped). */
|
|
107
|
+
| "unused"
|
|
108
|
+
/** Downstream failed for a non-token reason (network, 5xx, maintenance). */
|
|
109
|
+
| "error";
|
|
110
|
+
/**
|
|
111
|
+
* One verdict to report, for {@link NoneCap.feedback.report} and
|
|
112
|
+
* {@link NoneCap.feedback.reportMany}.
|
|
113
|
+
*/
|
|
114
|
+
interface FeedbackReport {
|
|
115
|
+
/** The solve id returned by `solves.create` / `solve()`. Must be your own, `solved` solve. */
|
|
116
|
+
solve_id: string;
|
|
117
|
+
outcome: FeedbackOutcome;
|
|
118
|
+
/**
|
|
119
|
+
* The raw downstream boolean, when your target has one (OSIPTEL's `estado`:
|
|
120
|
+
* `false` = accepted, `true` = rejected). Stored verbatim. The API rejects an
|
|
121
|
+
* item whose `estado` disagrees with its `outcome`.
|
|
122
|
+
*/
|
|
123
|
+
estado?: boolean | null;
|
|
124
|
+
/** Freeform downstream reason or code. Truncated to 512 chars server-side. */
|
|
125
|
+
reason?: string | null;
|
|
126
|
+
/** When the verdict happened. Advisory only — the server stamps its own timestamps. */
|
|
127
|
+
reported_at?: string | Date | null;
|
|
128
|
+
}
|
|
129
|
+
/** A recorded feedback resource. */
|
|
130
|
+
interface Feedback {
|
|
131
|
+
object: "feedback";
|
|
132
|
+
solve_id: string;
|
|
133
|
+
outcome: FeedbackOutcome;
|
|
134
|
+
estado: boolean | null;
|
|
135
|
+
reason: string | null;
|
|
136
|
+
reported_at: string | null;
|
|
137
|
+
/** How many times this solve's verdict has been written. 1 on first report. */
|
|
138
|
+
report_count: number;
|
|
139
|
+
created_at: string;
|
|
140
|
+
updated_at: string;
|
|
141
|
+
}
|
|
142
|
+
/** What happened to one item of a batch. */
|
|
143
|
+
type FeedbackStatus =
|
|
144
|
+
/** First report for this solve. */
|
|
145
|
+
"recorded"
|
|
146
|
+
/** Overwrote an earlier report. */
|
|
147
|
+
| "updated"
|
|
148
|
+
/** Identical to what was already stored — no write. */
|
|
149
|
+
| "unchanged"
|
|
150
|
+
/** Rejected; see `error`. */
|
|
151
|
+
| "error";
|
|
152
|
+
/** One item's result, in the same position as the report you sent. */
|
|
153
|
+
interface FeedbackResult {
|
|
154
|
+
solve_id: string;
|
|
155
|
+
status: FeedbackStatus;
|
|
156
|
+
/** Set only when `status === "error"`. Note the bare shape: no `error` wrapper. */
|
|
157
|
+
error: {
|
|
158
|
+
code: ErrorCode;
|
|
159
|
+
message: string;
|
|
160
|
+
param: string | null;
|
|
161
|
+
} | null;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The result of {@link NoneCap.feedback.reportMany}.
|
|
165
|
+
*
|
|
166
|
+
* Items are resolved independently, so one bad solve id never discards the
|
|
167
|
+
* rest — which also means a broken integration reports zero failures at the
|
|
168
|
+
* HTTP level. Check `failed` (or scan `results`), not just the absence of a
|
|
169
|
+
* thrown error.
|
|
170
|
+
*/
|
|
171
|
+
interface FeedbackBatch {
|
|
172
|
+
object: "feedback_batch";
|
|
173
|
+
recorded: number;
|
|
174
|
+
updated: number;
|
|
175
|
+
unchanged: number;
|
|
176
|
+
failed: number;
|
|
177
|
+
/** One entry per report you sent, in request order. */
|
|
178
|
+
results: FeedbackResult[];
|
|
179
|
+
}
|
|
92
180
|
/** Your account, including the current credit balance. */
|
|
93
181
|
interface Account {
|
|
94
182
|
object: "account";
|
|
@@ -98,7 +186,15 @@ interface Account {
|
|
|
98
186
|
created_at: string;
|
|
99
187
|
}
|
|
100
188
|
/** Error codes returned in the API's error envelope. */
|
|
101
|
-
type ErrorCode = "unauthorized" | "forbidden" | "account_locked" | "insufficient_credits" | "invalid_request" | "validation_error" | "not_found" | "conflict" | "rate_limited" | "ext_daily_limit" | "concurrency_limit_exceeded"
|
|
189
|
+
type ErrorCode = "unauthorized" | "forbidden" | "account_locked" | "insufficient_credits" | "invalid_request" | "validation_error" | "not_found" | "conflict" | "rate_limited" | "ext_daily_limit" | "concurrency_limit_exceeded"
|
|
190
|
+
/** hCaptcha is rate-limiting this sitekey; the submit was shed. Honour `Retry-After`. */
|
|
191
|
+
| "sitekey_rate_limited"
|
|
192
|
+
/** The submitting API key hit its own spend cap. */
|
|
193
|
+
| "key_credit_limit_exceeded"
|
|
194
|
+
/** Feedback: the solve is missing, not yours, or never produced a token. */
|
|
195
|
+
| "not_eligible"
|
|
196
|
+
/** Feedback: a first report arrived after the reporting window closed. */
|
|
197
|
+
| "expired_window" | "internal_error" | "pool_exhausted";
|
|
102
198
|
/** The Stripe-style error envelope every non-2xx response carries. */
|
|
103
199
|
interface ErrorEnvelope {
|
|
104
200
|
error: {
|
|
@@ -108,6 +204,8 @@ interface ErrorEnvelope {
|
|
|
108
204
|
};
|
|
109
205
|
}
|
|
110
206
|
|
|
207
|
+
/** The API caps one `POST /v1/feedback` call at 500 items. */
|
|
208
|
+
declare const FEEDBACK_BATCH_MAX = 500;
|
|
111
209
|
/** Is this a terminal solve status? */
|
|
112
210
|
declare function isTerminal(status: SolveStatus): boolean;
|
|
113
211
|
/** A `fetch` implementation. Defaults to the global `fetch`. */
|
|
@@ -138,7 +236,7 @@ interface WaitOptions {
|
|
|
138
236
|
/** Abort the request. */
|
|
139
237
|
signal?: AbortSignal;
|
|
140
238
|
}
|
|
141
|
-
/** Options for {@link NoneCap.solve}. */
|
|
239
|
+
/** Options for {@link NoneCap.solve} and {@link SolveHandle.result}. */
|
|
142
240
|
interface SolveHelperOptions {
|
|
143
241
|
/**
|
|
144
242
|
* Give up after this many milliseconds and throw {@link TimeoutError}.
|
|
@@ -148,6 +246,11 @@ interface SolveHelperOptions {
|
|
|
148
246
|
/** Abort waiting. */
|
|
149
247
|
signal?: AbortSignal;
|
|
150
248
|
}
|
|
249
|
+
/** Options for {@link NoneCap.solves.start}. */
|
|
250
|
+
interface SolveStartOptions {
|
|
251
|
+
/** Abort the submission. */
|
|
252
|
+
signal?: AbortSignal;
|
|
253
|
+
}
|
|
151
254
|
/**
|
|
152
255
|
* The NoneCap API client.
|
|
153
256
|
*
|
|
@@ -169,6 +272,22 @@ declare class NoneCap {
|
|
|
169
272
|
cancel: (id: string, options?: {
|
|
170
273
|
signal?: AbortSignal;
|
|
171
274
|
}) => Promise<Solve>;
|
|
275
|
+
/**
|
|
276
|
+
* Submit a solve and return a {@link SolveHandle} for it.
|
|
277
|
+
*
|
|
278
|
+
* Unlike {@link NoneCap.solve}, this returns as soon as the submission is
|
|
279
|
+
* accepted, so `handle.id` is available immediately. Use the handle to
|
|
280
|
+
* `cancel()` the solve or `await handle.result()` for the finished solve.
|
|
281
|
+
*
|
|
282
|
+
* ```ts
|
|
283
|
+
* const handle = await nc.solves.start({ type: "hcaptcha", sitekey, url });
|
|
284
|
+
* // ...later, e.g. on shutdown:
|
|
285
|
+
* await handle.cancel();
|
|
286
|
+
* // or wait for it:
|
|
287
|
+
* const { token } = await handle.result();
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
start: (params: SolveCreateParams, options?: SolveStartOptions) => Promise<SolveHandle>;
|
|
172
291
|
/** Fetch one page of solves. */
|
|
173
292
|
list: (params?: SolveListParams, options?: {
|
|
174
293
|
signal?: AbortSignal;
|
|
@@ -184,6 +303,50 @@ declare class NoneCap {
|
|
|
184
303
|
signal?: AbortSignal;
|
|
185
304
|
}) => AsyncIterableIterator<Solve>;
|
|
186
305
|
};
|
|
306
|
+
/**
|
|
307
|
+
* Report what your downstream target did with the tokens we minted.
|
|
308
|
+
*
|
|
309
|
+
* Feedback is free, own-solves-only, and idempotent per solve id: reporting
|
|
310
|
+
* the same solve again corrects the earlier verdict. Only `solved` solves can
|
|
311
|
+
* be reported, and only within the API's reporting window (~30 days from the
|
|
312
|
+
* solve; corrections to an already-reported solve are never re-gated by it).
|
|
313
|
+
*/
|
|
314
|
+
readonly feedback: {
|
|
315
|
+
/**
|
|
316
|
+
* Report one solve's downstream outcome, returning the recorded feedback.
|
|
317
|
+
*
|
|
318
|
+
* Unlike {@link NoneCap.feedback.reportMany}, a rejected report throws:
|
|
319
|
+
* {@link NotFoundError} if the solve isn't yours or never minted a token,
|
|
320
|
+
* {@link ValidationError} if a field is wrong or the reporting window closed.
|
|
321
|
+
*
|
|
322
|
+
* ```ts
|
|
323
|
+
* await nc.feedback.report({ solve_id: solve.id, outcome: "rejected", reason: "..." });
|
|
324
|
+
* ```
|
|
325
|
+
*/
|
|
326
|
+
report: (report: FeedbackReport, options?: {
|
|
327
|
+
signal?: AbortSignal;
|
|
328
|
+
}) => Promise<Feedback>;
|
|
329
|
+
/**
|
|
330
|
+
* Report many verdicts at once — the path to use at volume. Buffer verdicts
|
|
331
|
+
* as you learn them and flush them here.
|
|
332
|
+
*
|
|
333
|
+
* The call succeeds even when individual items are rejected: each is
|
|
334
|
+
* resolved on its own so one stale id never discards the rest. Check
|
|
335
|
+
* `failed` rather than relying on a thrown error.
|
|
336
|
+
*
|
|
337
|
+
* More than {@link FEEDBACK_BATCH_MAX} reports are split across sequential
|
|
338
|
+
* requests and merged into one result, in request order. An empty array is
|
|
339
|
+
* a no-op that returns an empty batch without a request.
|
|
340
|
+
*
|
|
341
|
+
* ```ts
|
|
342
|
+
* const batch = await nc.feedback.reportMany(buffer);
|
|
343
|
+
* if (batch.failed) console.warn(batch.results.filter((r) => r.status === "error"));
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
reportMany: (reports: readonly FeedbackReport[], options?: {
|
|
347
|
+
signal?: AbortSignal;
|
|
348
|
+
}) => Promise<FeedbackBatch>;
|
|
349
|
+
};
|
|
187
350
|
/** Fetch your account, including the current credit balance. */
|
|
188
351
|
me(options?: {
|
|
189
352
|
signal?: AbortSignal;
|
|
@@ -194,9 +357,58 @@ declare class NoneCap {
|
|
|
194
357
|
* Uses the server's long-poll under the hood and keeps polling until the
|
|
195
358
|
* solve is terminal or `timeout` elapses. Throws {@link SolveFailedError} if
|
|
196
359
|
* the solve fails/expires/is cancelled, or {@link TimeoutError} on timeout.
|
|
360
|
+
*
|
|
361
|
+
* This is sugar for `(await solves.start(params)).result(options)`. When you
|
|
362
|
+
* need to cancel the solve while it runs, use {@link NoneCap.solves.start}
|
|
363
|
+
* and keep the {@link SolveHandle}.
|
|
197
364
|
*/
|
|
198
365
|
solve(params: SolveCreateParams, options?: SolveHelperOptions): Promise<Solve>;
|
|
199
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* A handle on a started solve, returned by {@link NoneCap.solves.start}.
|
|
369
|
+
*
|
|
370
|
+
* The handle is the cancellable lifecycle object for one solve. It is a plain
|
|
371
|
+
* object, not a promise: `await`ing it does nothing useful. Wait for the result
|
|
372
|
+
* with {@link SolveHandle.result}, and cancel with {@link SolveHandle.cancel}.
|
|
373
|
+
* You do not construct it yourself — `solves.start()` hands you one with `id`
|
|
374
|
+
* already resolved.
|
|
375
|
+
*/
|
|
376
|
+
declare class SolveHandle {
|
|
377
|
+
#private;
|
|
378
|
+
/** The id of the started solve, available as soon as `start()` resolved. */
|
|
379
|
+
readonly id: string;
|
|
380
|
+
constructor(client: NoneCap, solve: Solve);
|
|
381
|
+
/** The last-known solve state for this handle. */
|
|
382
|
+
get solve(): Solve;
|
|
383
|
+
/**
|
|
384
|
+
* Long-poll until the solve is terminal, returning the solved solve. Throws
|
|
385
|
+
* {@link SolveFailedError} if it fails/expires/is cancelled, or
|
|
386
|
+
* {@link TimeoutError} on timeout (with `solveId`/`solve` attached so you can
|
|
387
|
+
* still {@link SolveHandle.cancel} it).
|
|
388
|
+
*
|
|
389
|
+
* Memoizes the *terminal* outcome: once the solve actually finishes, every
|
|
390
|
+
* later call returns that same settled result. A {@link TimeoutError} is not
|
|
391
|
+
* memoized — it just means this wait gave up, so a later call (e.g. with a
|
|
392
|
+
* larger `timeout`) resumes polling. Concurrent calls share one in-flight
|
|
393
|
+
* poll, so they never issue duplicate requests. The first caller's
|
|
394
|
+
* `timeout`/`signal` drive the shared poll; a later call starts a fresh one.
|
|
395
|
+
*/
|
|
396
|
+
result(options?: SolveHelperOptions): Promise<Solve>;
|
|
397
|
+
/**
|
|
398
|
+
* Cancel the solve. Cancelled solves are never charged, so this is for clean
|
|
399
|
+
* shutdown / freeing slots / stopping early, not cost protection.
|
|
400
|
+
*
|
|
401
|
+
* If the solve already reached a terminal state the API answers 409; that is
|
|
402
|
+
* not an error a caller cares about, so this swallows {@link ConflictError}
|
|
403
|
+
* and returns the current solve state instead. A cancel that lands on a
|
|
404
|
+
* terminal state (cancelled, or whatever the 409-swallow read back) settles
|
|
405
|
+
* this handle's {@link SolveHandle.result}, so any in-flight or later wait
|
|
406
|
+
* stops polling and reflects that terminal state.
|
|
407
|
+
*/
|
|
408
|
+
cancel(options?: {
|
|
409
|
+
signal?: AbortSignal;
|
|
410
|
+
}): Promise<Solve>;
|
|
411
|
+
}
|
|
200
412
|
|
|
201
413
|
/**
|
|
202
414
|
* Base class for every error this library throws. Catch `NoneCapError` to
|
|
@@ -251,8 +463,24 @@ declare class SolveFailedError extends NoneCapError {
|
|
|
251
463
|
readonly solve: Solve;
|
|
252
464
|
constructor(solve: Solve);
|
|
253
465
|
}
|
|
254
|
-
/**
|
|
466
|
+
/**
|
|
467
|
+
* Thrown by {@link NoneCap.solve} (and {@link SolveHandle.result}) when the
|
|
468
|
+
* client-side timeout elapses first. When the timeout came from waiting on a
|
|
469
|
+
* solve, `solveId` and the last-known `solve` are attached so you can still
|
|
470
|
+
* cancel the in-flight solve. They are `undefined` for transport-level timeouts.
|
|
471
|
+
*/
|
|
255
472
|
declare class TimeoutError extends NoneCapError {
|
|
473
|
+
/** The id of the in-flight solve, when the timeout came from a solve wait. */
|
|
474
|
+
readonly solveId: string | undefined;
|
|
475
|
+
/** The last-known solve state, so you can cancel it after a wait timed out. */
|
|
476
|
+
readonly solve: Solve | undefined;
|
|
477
|
+
constructor(message: string, opts?: {
|
|
478
|
+
code?: ErrorCode;
|
|
479
|
+
status?: number;
|
|
480
|
+
param?: string | null;
|
|
481
|
+
solveId?: string;
|
|
482
|
+
solve?: Solve;
|
|
483
|
+
});
|
|
256
484
|
}
|
|
257
485
|
|
|
258
|
-
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 };
|
|
486
|
+
export { APIError, type Account, AuthenticationError, ConflictError, ConnectionError, type ErrorCode, type ErrorEnvelope, FEEDBACK_BATCH_MAX, type Feedback, type FeedbackBatch, type FeedbackOutcome, type FeedbackReport, type FeedbackResult, type FeedbackStatus, 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
|
@@ -89,6 +89,94 @@ interface SolveList {
|
|
|
89
89
|
data: Solve[];
|
|
90
90
|
has_more: boolean;
|
|
91
91
|
}
|
|
92
|
+
/**
|
|
93
|
+
* What the downstream target did with a solve's token.
|
|
94
|
+
*
|
|
95
|
+
* Only `accepted` and `rejected` count toward the acceptance rate — the other
|
|
96
|
+
* three are recorded but kept out of the denominator, so a client-side outage
|
|
97
|
+
* can't look like a token-quality regression.
|
|
98
|
+
*/
|
|
99
|
+
type FeedbackOutcome =
|
|
100
|
+
/** Downstream accepted the token. */
|
|
101
|
+
"accepted"
|
|
102
|
+
/** Downstream rejected the token. */
|
|
103
|
+
| "rejected"
|
|
104
|
+
/** Token was submitted but the verdict couldn't be determined. */
|
|
105
|
+
| "unknown"
|
|
106
|
+
/** Token was never submitted downstream (expired, aborted, deduped). */
|
|
107
|
+
| "unused"
|
|
108
|
+
/** Downstream failed for a non-token reason (network, 5xx, maintenance). */
|
|
109
|
+
| "error";
|
|
110
|
+
/**
|
|
111
|
+
* One verdict to report, for {@link NoneCap.feedback.report} and
|
|
112
|
+
* {@link NoneCap.feedback.reportMany}.
|
|
113
|
+
*/
|
|
114
|
+
interface FeedbackReport {
|
|
115
|
+
/** The solve id returned by `solves.create` / `solve()`. Must be your own, `solved` solve. */
|
|
116
|
+
solve_id: string;
|
|
117
|
+
outcome: FeedbackOutcome;
|
|
118
|
+
/**
|
|
119
|
+
* The raw downstream boolean, when your target has one (OSIPTEL's `estado`:
|
|
120
|
+
* `false` = accepted, `true` = rejected). Stored verbatim. The API rejects an
|
|
121
|
+
* item whose `estado` disagrees with its `outcome`.
|
|
122
|
+
*/
|
|
123
|
+
estado?: boolean | null;
|
|
124
|
+
/** Freeform downstream reason or code. Truncated to 512 chars server-side. */
|
|
125
|
+
reason?: string | null;
|
|
126
|
+
/** When the verdict happened. Advisory only — the server stamps its own timestamps. */
|
|
127
|
+
reported_at?: string | Date | null;
|
|
128
|
+
}
|
|
129
|
+
/** A recorded feedback resource. */
|
|
130
|
+
interface Feedback {
|
|
131
|
+
object: "feedback";
|
|
132
|
+
solve_id: string;
|
|
133
|
+
outcome: FeedbackOutcome;
|
|
134
|
+
estado: boolean | null;
|
|
135
|
+
reason: string | null;
|
|
136
|
+
reported_at: string | null;
|
|
137
|
+
/** How many times this solve's verdict has been written. 1 on first report. */
|
|
138
|
+
report_count: number;
|
|
139
|
+
created_at: string;
|
|
140
|
+
updated_at: string;
|
|
141
|
+
}
|
|
142
|
+
/** What happened to one item of a batch. */
|
|
143
|
+
type FeedbackStatus =
|
|
144
|
+
/** First report for this solve. */
|
|
145
|
+
"recorded"
|
|
146
|
+
/** Overwrote an earlier report. */
|
|
147
|
+
| "updated"
|
|
148
|
+
/** Identical to what was already stored — no write. */
|
|
149
|
+
| "unchanged"
|
|
150
|
+
/** Rejected; see `error`. */
|
|
151
|
+
| "error";
|
|
152
|
+
/** One item's result, in the same position as the report you sent. */
|
|
153
|
+
interface FeedbackResult {
|
|
154
|
+
solve_id: string;
|
|
155
|
+
status: FeedbackStatus;
|
|
156
|
+
/** Set only when `status === "error"`. Note the bare shape: no `error` wrapper. */
|
|
157
|
+
error: {
|
|
158
|
+
code: ErrorCode;
|
|
159
|
+
message: string;
|
|
160
|
+
param: string | null;
|
|
161
|
+
} | null;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The result of {@link NoneCap.feedback.reportMany}.
|
|
165
|
+
*
|
|
166
|
+
* Items are resolved independently, so one bad solve id never discards the
|
|
167
|
+
* rest — which also means a broken integration reports zero failures at the
|
|
168
|
+
* HTTP level. Check `failed` (or scan `results`), not just the absence of a
|
|
169
|
+
* thrown error.
|
|
170
|
+
*/
|
|
171
|
+
interface FeedbackBatch {
|
|
172
|
+
object: "feedback_batch";
|
|
173
|
+
recorded: number;
|
|
174
|
+
updated: number;
|
|
175
|
+
unchanged: number;
|
|
176
|
+
failed: number;
|
|
177
|
+
/** One entry per report you sent, in request order. */
|
|
178
|
+
results: FeedbackResult[];
|
|
179
|
+
}
|
|
92
180
|
/** Your account, including the current credit balance. */
|
|
93
181
|
interface Account {
|
|
94
182
|
object: "account";
|
|
@@ -98,7 +186,15 @@ interface Account {
|
|
|
98
186
|
created_at: string;
|
|
99
187
|
}
|
|
100
188
|
/** Error codes returned in the API's error envelope. */
|
|
101
|
-
type ErrorCode = "unauthorized" | "forbidden" | "account_locked" | "insufficient_credits" | "invalid_request" | "validation_error" | "not_found" | "conflict" | "rate_limited" | "ext_daily_limit" | "concurrency_limit_exceeded"
|
|
189
|
+
type ErrorCode = "unauthorized" | "forbidden" | "account_locked" | "insufficient_credits" | "invalid_request" | "validation_error" | "not_found" | "conflict" | "rate_limited" | "ext_daily_limit" | "concurrency_limit_exceeded"
|
|
190
|
+
/** hCaptcha is rate-limiting this sitekey; the submit was shed. Honour `Retry-After`. */
|
|
191
|
+
| "sitekey_rate_limited"
|
|
192
|
+
/** The submitting API key hit its own spend cap. */
|
|
193
|
+
| "key_credit_limit_exceeded"
|
|
194
|
+
/** Feedback: the solve is missing, not yours, or never produced a token. */
|
|
195
|
+
| "not_eligible"
|
|
196
|
+
/** Feedback: a first report arrived after the reporting window closed. */
|
|
197
|
+
| "expired_window" | "internal_error" | "pool_exhausted";
|
|
102
198
|
/** The Stripe-style error envelope every non-2xx response carries. */
|
|
103
199
|
interface ErrorEnvelope {
|
|
104
200
|
error: {
|
|
@@ -108,6 +204,8 @@ interface ErrorEnvelope {
|
|
|
108
204
|
};
|
|
109
205
|
}
|
|
110
206
|
|
|
207
|
+
/** The API caps one `POST /v1/feedback` call at 500 items. */
|
|
208
|
+
declare const FEEDBACK_BATCH_MAX = 500;
|
|
111
209
|
/** Is this a terminal solve status? */
|
|
112
210
|
declare function isTerminal(status: SolveStatus): boolean;
|
|
113
211
|
/** A `fetch` implementation. Defaults to the global `fetch`. */
|
|
@@ -138,7 +236,7 @@ interface WaitOptions {
|
|
|
138
236
|
/** Abort the request. */
|
|
139
237
|
signal?: AbortSignal;
|
|
140
238
|
}
|
|
141
|
-
/** Options for {@link NoneCap.solve}. */
|
|
239
|
+
/** Options for {@link NoneCap.solve} and {@link SolveHandle.result}. */
|
|
142
240
|
interface SolveHelperOptions {
|
|
143
241
|
/**
|
|
144
242
|
* Give up after this many milliseconds and throw {@link TimeoutError}.
|
|
@@ -148,6 +246,11 @@ interface SolveHelperOptions {
|
|
|
148
246
|
/** Abort waiting. */
|
|
149
247
|
signal?: AbortSignal;
|
|
150
248
|
}
|
|
249
|
+
/** Options for {@link NoneCap.solves.start}. */
|
|
250
|
+
interface SolveStartOptions {
|
|
251
|
+
/** Abort the submission. */
|
|
252
|
+
signal?: AbortSignal;
|
|
253
|
+
}
|
|
151
254
|
/**
|
|
152
255
|
* The NoneCap API client.
|
|
153
256
|
*
|
|
@@ -169,6 +272,22 @@ declare class NoneCap {
|
|
|
169
272
|
cancel: (id: string, options?: {
|
|
170
273
|
signal?: AbortSignal;
|
|
171
274
|
}) => Promise<Solve>;
|
|
275
|
+
/**
|
|
276
|
+
* Submit a solve and return a {@link SolveHandle} for it.
|
|
277
|
+
*
|
|
278
|
+
* Unlike {@link NoneCap.solve}, this returns as soon as the submission is
|
|
279
|
+
* accepted, so `handle.id` is available immediately. Use the handle to
|
|
280
|
+
* `cancel()` the solve or `await handle.result()` for the finished solve.
|
|
281
|
+
*
|
|
282
|
+
* ```ts
|
|
283
|
+
* const handle = await nc.solves.start({ type: "hcaptcha", sitekey, url });
|
|
284
|
+
* // ...later, e.g. on shutdown:
|
|
285
|
+
* await handle.cancel();
|
|
286
|
+
* // or wait for it:
|
|
287
|
+
* const { token } = await handle.result();
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
start: (params: SolveCreateParams, options?: SolveStartOptions) => Promise<SolveHandle>;
|
|
172
291
|
/** Fetch one page of solves. */
|
|
173
292
|
list: (params?: SolveListParams, options?: {
|
|
174
293
|
signal?: AbortSignal;
|
|
@@ -184,6 +303,50 @@ declare class NoneCap {
|
|
|
184
303
|
signal?: AbortSignal;
|
|
185
304
|
}) => AsyncIterableIterator<Solve>;
|
|
186
305
|
};
|
|
306
|
+
/**
|
|
307
|
+
* Report what your downstream target did with the tokens we minted.
|
|
308
|
+
*
|
|
309
|
+
* Feedback is free, own-solves-only, and idempotent per solve id: reporting
|
|
310
|
+
* the same solve again corrects the earlier verdict. Only `solved` solves can
|
|
311
|
+
* be reported, and only within the API's reporting window (~30 days from the
|
|
312
|
+
* solve; corrections to an already-reported solve are never re-gated by it).
|
|
313
|
+
*/
|
|
314
|
+
readonly feedback: {
|
|
315
|
+
/**
|
|
316
|
+
* Report one solve's downstream outcome, returning the recorded feedback.
|
|
317
|
+
*
|
|
318
|
+
* Unlike {@link NoneCap.feedback.reportMany}, a rejected report throws:
|
|
319
|
+
* {@link NotFoundError} if the solve isn't yours or never minted a token,
|
|
320
|
+
* {@link ValidationError} if a field is wrong or the reporting window closed.
|
|
321
|
+
*
|
|
322
|
+
* ```ts
|
|
323
|
+
* await nc.feedback.report({ solve_id: solve.id, outcome: "rejected", reason: "..." });
|
|
324
|
+
* ```
|
|
325
|
+
*/
|
|
326
|
+
report: (report: FeedbackReport, options?: {
|
|
327
|
+
signal?: AbortSignal;
|
|
328
|
+
}) => Promise<Feedback>;
|
|
329
|
+
/**
|
|
330
|
+
* Report many verdicts at once — the path to use at volume. Buffer verdicts
|
|
331
|
+
* as you learn them and flush them here.
|
|
332
|
+
*
|
|
333
|
+
* The call succeeds even when individual items are rejected: each is
|
|
334
|
+
* resolved on its own so one stale id never discards the rest. Check
|
|
335
|
+
* `failed` rather than relying on a thrown error.
|
|
336
|
+
*
|
|
337
|
+
* More than {@link FEEDBACK_BATCH_MAX} reports are split across sequential
|
|
338
|
+
* requests and merged into one result, in request order. An empty array is
|
|
339
|
+
* a no-op that returns an empty batch without a request.
|
|
340
|
+
*
|
|
341
|
+
* ```ts
|
|
342
|
+
* const batch = await nc.feedback.reportMany(buffer);
|
|
343
|
+
* if (batch.failed) console.warn(batch.results.filter((r) => r.status === "error"));
|
|
344
|
+
* ```
|
|
345
|
+
*/
|
|
346
|
+
reportMany: (reports: readonly FeedbackReport[], options?: {
|
|
347
|
+
signal?: AbortSignal;
|
|
348
|
+
}) => Promise<FeedbackBatch>;
|
|
349
|
+
};
|
|
187
350
|
/** Fetch your account, including the current credit balance. */
|
|
188
351
|
me(options?: {
|
|
189
352
|
signal?: AbortSignal;
|
|
@@ -194,9 +357,58 @@ declare class NoneCap {
|
|
|
194
357
|
* Uses the server's long-poll under the hood and keeps polling until the
|
|
195
358
|
* solve is terminal or `timeout` elapses. Throws {@link SolveFailedError} if
|
|
196
359
|
* the solve fails/expires/is cancelled, or {@link TimeoutError} on timeout.
|
|
360
|
+
*
|
|
361
|
+
* This is sugar for `(await solves.start(params)).result(options)`. When you
|
|
362
|
+
* need to cancel the solve while it runs, use {@link NoneCap.solves.start}
|
|
363
|
+
* and keep the {@link SolveHandle}.
|
|
197
364
|
*/
|
|
198
365
|
solve(params: SolveCreateParams, options?: SolveHelperOptions): Promise<Solve>;
|
|
199
366
|
}
|
|
367
|
+
/**
|
|
368
|
+
* A handle on a started solve, returned by {@link NoneCap.solves.start}.
|
|
369
|
+
*
|
|
370
|
+
* The handle is the cancellable lifecycle object for one solve. It is a plain
|
|
371
|
+
* object, not a promise: `await`ing it does nothing useful. Wait for the result
|
|
372
|
+
* with {@link SolveHandle.result}, and cancel with {@link SolveHandle.cancel}.
|
|
373
|
+
* You do not construct it yourself — `solves.start()` hands you one with `id`
|
|
374
|
+
* already resolved.
|
|
375
|
+
*/
|
|
376
|
+
declare class SolveHandle {
|
|
377
|
+
#private;
|
|
378
|
+
/** The id of the started solve, available as soon as `start()` resolved. */
|
|
379
|
+
readonly id: string;
|
|
380
|
+
constructor(client: NoneCap, solve: Solve);
|
|
381
|
+
/** The last-known solve state for this handle. */
|
|
382
|
+
get solve(): Solve;
|
|
383
|
+
/**
|
|
384
|
+
* Long-poll until the solve is terminal, returning the solved solve. Throws
|
|
385
|
+
* {@link SolveFailedError} if it fails/expires/is cancelled, or
|
|
386
|
+
* {@link TimeoutError} on timeout (with `solveId`/`solve` attached so you can
|
|
387
|
+
* still {@link SolveHandle.cancel} it).
|
|
388
|
+
*
|
|
389
|
+
* Memoizes the *terminal* outcome: once the solve actually finishes, every
|
|
390
|
+
* later call returns that same settled result. A {@link TimeoutError} is not
|
|
391
|
+
* memoized — it just means this wait gave up, so a later call (e.g. with a
|
|
392
|
+
* larger `timeout`) resumes polling. Concurrent calls share one in-flight
|
|
393
|
+
* poll, so they never issue duplicate requests. The first caller's
|
|
394
|
+
* `timeout`/`signal` drive the shared poll; a later call starts a fresh one.
|
|
395
|
+
*/
|
|
396
|
+
result(options?: SolveHelperOptions): Promise<Solve>;
|
|
397
|
+
/**
|
|
398
|
+
* Cancel the solve. Cancelled solves are never charged, so this is for clean
|
|
399
|
+
* shutdown / freeing slots / stopping early, not cost protection.
|
|
400
|
+
*
|
|
401
|
+
* If the solve already reached a terminal state the API answers 409; that is
|
|
402
|
+
* not an error a caller cares about, so this swallows {@link ConflictError}
|
|
403
|
+
* and returns the current solve state instead. A cancel that lands on a
|
|
404
|
+
* terminal state (cancelled, or whatever the 409-swallow read back) settles
|
|
405
|
+
* this handle's {@link SolveHandle.result}, so any in-flight or later wait
|
|
406
|
+
* stops polling and reflects that terminal state.
|
|
407
|
+
*/
|
|
408
|
+
cancel(options?: {
|
|
409
|
+
signal?: AbortSignal;
|
|
410
|
+
}): Promise<Solve>;
|
|
411
|
+
}
|
|
200
412
|
|
|
201
413
|
/**
|
|
202
414
|
* Base class for every error this library throws. Catch `NoneCapError` to
|
|
@@ -251,8 +463,24 @@ declare class SolveFailedError extends NoneCapError {
|
|
|
251
463
|
readonly solve: Solve;
|
|
252
464
|
constructor(solve: Solve);
|
|
253
465
|
}
|
|
254
|
-
/**
|
|
466
|
+
/**
|
|
467
|
+
* Thrown by {@link NoneCap.solve} (and {@link SolveHandle.result}) when the
|
|
468
|
+
* client-side timeout elapses first. When the timeout came from waiting on a
|
|
469
|
+
* solve, `solveId` and the last-known `solve` are attached so you can still
|
|
470
|
+
* cancel the in-flight solve. They are `undefined` for transport-level timeouts.
|
|
471
|
+
*/
|
|
255
472
|
declare class TimeoutError extends NoneCapError {
|
|
473
|
+
/** The id of the in-flight solve, when the timeout came from a solve wait. */
|
|
474
|
+
readonly solveId: string | undefined;
|
|
475
|
+
/** The last-known solve state, so you can cancel it after a wait timed out. */
|
|
476
|
+
readonly solve: Solve | undefined;
|
|
477
|
+
constructor(message: string, opts?: {
|
|
478
|
+
code?: ErrorCode;
|
|
479
|
+
status?: number;
|
|
480
|
+
param?: string | null;
|
|
481
|
+
solveId?: string;
|
|
482
|
+
solve?: Solve;
|
|
483
|
+
});
|
|
256
484
|
}
|
|
257
485
|
|
|
258
|
-
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 };
|
|
486
|
+
export { APIError, type Account, AuthenticationError, ConflictError, ConnectionError, type ErrorCode, type ErrorEnvelope, FEEDBACK_BATCH_MAX, type Feedback, type FeedbackBatch, type FeedbackOutcome, type FeedbackReport, type FeedbackResult, type FeedbackStatus, 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 };
|