@naughtbot/sdk 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # NaughtBot SDK
2
+
3
+ TypeScript SDK for the NaughtBot CAPTCHA widget. Provides BLE pairing, verification, and UI components.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm install @naughtbot/sdk
9
+ ```
10
+
11
+ ## Build & Test
12
+
13
+ ```sh
14
+ pnpm install
15
+ pnpm build
16
+ pnpm vitest run
17
+ ```
18
+
19
+ ## Dependencies
20
+
21
+ - [@ackagent/web-sdk](https://github.com/AckAgent/web-sdk) — peer dependency (npm)
22
+ - [@naughtbot/api](https://github.com/naughtbot/api) — TypeScript types (npm)
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Pure BLE fragmentation logic extracted for testability.
3
+ * Implements the AckAgent BLE fragmentation protocol:
4
+ * each fragment has a 2-byte header [flags, sequence] followed by payload.
5
+ */
6
+ export declare const FRAGMENT_HEADER_SIZE = 2;
7
+ export declare const MAX_FRAGMENT_SIZE = 500;
8
+ export declare const FRAGMENT_FLAG_FIRST = 1;
9
+ export declare const FRAGMENT_FLAG_LAST = 2;
10
+ export declare const MAX_REASSEMBLY_BYTES = 65536;
11
+ /**
12
+ * Split data into fragments with headers (flags + sequence byte).
13
+ * Each fragment is at most maxFragmentSize bytes including the 2-byte header.
14
+ */
15
+ export declare function fragmentMessage(data: Uint8Array, maxFragmentSize?: number): Uint8Array[];
16
+ /**
17
+ * Reassemble fragments into a complete message.
18
+ * Validates fragment ordering, flags, and size limits.
19
+ * Throws if the reassembled message exceeds MAX_REASSEMBLY_BYTES.
20
+ */
21
+ export declare function reassembleFragments(fragments: Uint8Array[]): Uint8Array;
22
+ //# sourceMappingURL=ble-fragmentation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ble-fragmentation.d.ts","sourceRoot":"","sources":["../src/ble-fragmentation.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,eAAO,MAAM,oBAAoB,IAAI,CAAC;AACtC,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,mBAAmB,IAAO,CAAC;AACxC,eAAO,MAAM,kBAAkB,IAAO,CAAC;AACvC,eAAO,MAAM,oBAAoB,QAAS,CAAC;AAE3C;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,UAAU,EAChB,eAAe,GAAE,MAA0B,GAC1C,UAAU,EAAE,CAkCd;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,UAAU,EAAE,GAAG,UAAU,CA+CvE"}
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Pure BLE fragmentation logic extracted for testability.
3
+ * Implements the AckAgent BLE fragmentation protocol:
4
+ * each fragment has a 2-byte header [flags, sequence] followed by payload.
5
+ */
6
+ export const FRAGMENT_HEADER_SIZE = 2;
7
+ export const MAX_FRAGMENT_SIZE = 500;
8
+ export const FRAGMENT_FLAG_FIRST = 0x01;
9
+ export const FRAGMENT_FLAG_LAST = 0x02;
10
+ export const MAX_REASSEMBLY_BYTES = 65_536; // 64 KB max reassembled message
11
+ /**
12
+ * Split data into fragments with headers (flags + sequence byte).
13
+ * Each fragment is at most maxFragmentSize bytes including the 2-byte header.
14
+ */
15
+ export function fragmentMessage(data, maxFragmentSize = MAX_FRAGMENT_SIZE) {
16
+ if (data.length === 0) {
17
+ // Even empty data produces one fragment with FIRST|LAST flags
18
+ const fragment = new Uint8Array(FRAGMENT_HEADER_SIZE);
19
+ fragment[0] = FRAGMENT_FLAG_FIRST | FRAGMENT_FLAG_LAST;
20
+ fragment[1] = 0;
21
+ return [fragment];
22
+ }
23
+ const maxPayload = maxFragmentSize - FRAGMENT_HEADER_SIZE;
24
+ const fragments = [];
25
+ let offset = 0;
26
+ let sequence = 0;
27
+ while (offset < data.length) {
28
+ const remaining = data.length - offset;
29
+ const chunkSize = Math.min(remaining, maxPayload);
30
+ const chunk = data.slice(offset, offset + chunkSize);
31
+ let flags = 0;
32
+ if (offset === 0)
33
+ flags |= FRAGMENT_FLAG_FIRST;
34
+ if (offset + chunkSize >= data.length)
35
+ flags |= FRAGMENT_FLAG_LAST;
36
+ const fragment = new Uint8Array(FRAGMENT_HEADER_SIZE + chunk.length);
37
+ fragment[0] = flags;
38
+ fragment[1] = sequence & 0xff;
39
+ fragment.set(chunk, FRAGMENT_HEADER_SIZE);
40
+ fragments.push(fragment);
41
+ offset += chunkSize;
42
+ sequence++;
43
+ }
44
+ return fragments;
45
+ }
46
+ /**
47
+ * Reassemble fragments into a complete message.
48
+ * Validates fragment ordering, flags, and size limits.
49
+ * Throws if the reassembled message exceeds MAX_REASSEMBLY_BYTES.
50
+ */
51
+ export function reassembleFragments(fragments) {
52
+ if (fragments.length === 0) {
53
+ throw new Error("No fragments to reassemble");
54
+ }
55
+ // Validate first fragment has FIRST flag
56
+ if (!(fragments[0][0] & FRAGMENT_FLAG_FIRST)) {
57
+ throw new Error("First fragment missing FIRST flag");
58
+ }
59
+ // Validate last fragment has LAST flag
60
+ if (!(fragments[fragments.length - 1][0] & FRAGMENT_FLAG_LAST)) {
61
+ throw new Error("Last fragment missing LAST flag");
62
+ }
63
+ let totalPayloadSize = 0;
64
+ const payloads = [];
65
+ for (let i = 0; i < fragments.length; i++) {
66
+ const frag = fragments[i];
67
+ if (frag.length < FRAGMENT_HEADER_SIZE) {
68
+ throw new Error(`Fragment ${i} too short`);
69
+ }
70
+ const seq = frag[1];
71
+ if (seq !== (i & 0xff)) {
72
+ throw new Error(`Fragment ${i} has wrong sequence number: expected ${i & 0xff}, got ${seq}`);
73
+ }
74
+ const payload = frag.slice(FRAGMENT_HEADER_SIZE);
75
+ totalPayloadSize += payload.length;
76
+ if (totalPayloadSize > MAX_REASSEMBLY_BYTES) {
77
+ throw new Error("BLE message exceeds maximum size");
78
+ }
79
+ payloads.push(payload);
80
+ }
81
+ const result = new Uint8Array(totalPayloadSize);
82
+ let offset = 0;
83
+ for (const p of payloads) {
84
+ result.set(p, offset);
85
+ offset += p.length;
86
+ }
87
+ return result;
88
+ }
89
+ //# sourceMappingURL=ble-fragmentation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ble-fragmentation.js","sourceRoot":"","sources":["../src/ble-fragmentation.ts"],"names":[],"mappings":"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"}
package/dist/ble.d.ts ADDED
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Web Bluetooth transport for NaughtBot captcha.
3
+ * Connects to NaughtBot phone as a BLE Central, discovers the captcha
4
+ * GATT service, and exchanges ephemeral keys + encrypted challenge/response
5
+ * without going through the relay.
6
+ *
7
+ * Requires a user gesture to trigger device selection.
8
+ * Only works in secure contexts (HTTPS).
9
+ */
10
+ import type { CaptchaOptions } from "./types.js";
11
+ import type { CaptchaSession } from "./captcha.js";
12
+ /** Check if Web Bluetooth is supported in this environment */
13
+ export declare function isBLESupported(): boolean;
14
+ /**
15
+ * Create a BLE-based captcha session.
16
+ * Must be called from a user gesture (click handler) because
17
+ * Web Bluetooth requires a user activation to show the device picker.
18
+ */
19
+ export declare function createBLESession(options: CaptchaOptions): Promise<CaptchaSession>;
20
+ //# sourceMappingURL=ble.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ble.d.ts","sourceRoot":"","sources":["../src/ble.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAYH,OAAO,KAAK,EACV,cAAc,EAMf,MAAM,YAAY,CAAC;AAGpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAcnD,8DAA8D;AAC9D,wBAAgB,cAAc,IAAI,OAAO,CAOxC;AAED;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAuNvF"}
package/dist/ble.js ADDED
@@ -0,0 +1,278 @@
1
+ /**
2
+ * Web Bluetooth transport for NaughtBot captcha.
3
+ * Connects to NaughtBot phone as a BLE Central, discovers the captcha
4
+ * GATT service, and exchanges ephemeral keys + encrypted challenge/response
5
+ * without going through the relay.
6
+ *
7
+ * Requires a user gesture to trigger device selection.
8
+ * Only works in secure contexts (HTTPS).
9
+ */
10
+ import { generateKeyPair, deriveRequestKey, deriveResponseKey, encrypt, decrypt, requestIdToBytes, } from "@ackagent/web-sdk";
11
+ import { base64urlEncode, base64urlDecode, generateUuid } from "@ackagent/web-sdk";
12
+ import { computeSAS } from "@ackagent/web-sdk";
13
+ import { DEFAULT_TIMEOUT_MS } from "./types.js";
14
+ import { hexEncode } from "./captcha.js";
15
+ import { fragmentMessage, FRAGMENT_HEADER_SIZE, FRAGMENT_FLAG_FIRST, FRAGMENT_FLAG_LAST, MAX_REASSEMBLY_BYTES, } from "./ble-fragmentation.js";
16
+ // NaughtBot-specific BLE UUIDs (must match iOS BLECaptchaPeripheralService)
17
+ const NAUGHTBOT_SERVICE_UUID = "0a097b07-ca9c-4a5b-8c7d-000000000001";
18
+ const REQUEST_CHARACTERISTIC_UUID = "0a097b07-ca9c-4a5b-8c7d-000000000002";
19
+ const RESPONSE_CHARACTERISTIC_UUID = "0a097b07-ca9c-4a5b-8c7d-000000000003";
20
+ /** Check if Web Bluetooth is supported in this environment */
21
+ export function isBLESupported() {
22
+ return (typeof navigator !== "undefined" &&
23
+ "bluetooth" in navigator &&
24
+ navigator.bluetooth !== undefined &&
25
+ typeof navigator.bluetooth.requestDevice === "function");
26
+ }
27
+ /**
28
+ * Create a BLE-based captcha session.
29
+ * Must be called from a user gesture (click handler) because
30
+ * Web Bluetooth requires a user activation to show the device picker.
31
+ */
32
+ export async function createBLESession(options) {
33
+ const { nonce, timeoutMs = DEFAULT_TIMEOUT_MS, debug = false } = options;
34
+ const log = debug ? (...args) => console.debug("[NaughtBot BLE]", ...args) : () => { };
35
+ if (!isBLESupported()) {
36
+ throw new Error("Web Bluetooth is not supported in this browser");
37
+ }
38
+ // Request device from user (shows system picker dialog)
39
+ log("Requesting NaughtBot device...");
40
+ const device = await navigator.bluetooth.requestDevice({
41
+ filters: [{ services: [NAUGHTBOT_SERVICE_UUID] }],
42
+ });
43
+ log("Device selected:", device.name);
44
+ const sessionId = generateUuid();
45
+ const requestIdBytes = requestIdToBytes(sessionId);
46
+ const ephemeralKeyPair = await generateKeyPair();
47
+ let currentState = "initializing";
48
+ let currentSas = null;
49
+ let cancelled = false;
50
+ let sasResolve = null;
51
+ let cancelNotification = null;
52
+ const sasConfirmed = new Promise((resolve) => {
53
+ sasResolve = resolve;
54
+ });
55
+ const stateCallbacks = [];
56
+ function setState(newState, sas) {
57
+ currentState = newState;
58
+ if (sas !== undefined)
59
+ currentSas = sas;
60
+ for (const cb of stateCallbacks)
61
+ cb(currentState, currentSas);
62
+ }
63
+ const session = {
64
+ get sessionId() {
65
+ return sessionId;
66
+ },
67
+ get qrCodeUrl() {
68
+ return ""; // No QR code for BLE sessions
69
+ },
70
+ get state() {
71
+ return currentState;
72
+ },
73
+ get sas() {
74
+ return currentSas;
75
+ },
76
+ async waitForResult() {
77
+ const domain = typeof window !== "undefined" ? window.location.hostname : "unknown";
78
+ const deadline = Date.now() + timeoutMs;
79
+ // Connect to GATT server
80
+ setState("waiting_for_scan"); // "waiting for connection"
81
+ log("Connecting to GATT server...");
82
+ const server = await device.gatt.connect();
83
+ const service = await server.getPrimaryService(NAUGHTBOT_SERVICE_UUID);
84
+ const requestChar = await service.getCharacteristic(REQUEST_CHARACTERISTIC_UUID);
85
+ const responseChar = await service.getCharacteristic(RESPONSE_CHARACTERISTIC_UUID);
86
+ await responseChar.startNotifications();
87
+ setState("phone_connected");
88
+ log("Connected and notifications started");
89
+ // Register notification listener BEFORE writing to avoid missing the response
90
+ const connectMsg = JSON.stringify({
91
+ type: "connect",
92
+ sessionId,
93
+ ephemeralPublicKey: base64urlEncode(ephemeralKeyPair.publicKey),
94
+ });
95
+ const phoneResponsePromise = waitForNotification(responseChar, deadline - Date.now(), () => cancelled, log, (fn) => {
96
+ cancelNotification = fn;
97
+ });
98
+ await writeFragmented(requestChar, new TextEncoder().encode(connectMsg));
99
+ // Wait for phone's ephemeral public key (via notification)
100
+ const phoneResponse = await phoneResponsePromise;
101
+ cancelNotification = null;
102
+ const phoneData = JSON.parse(new TextDecoder().decode(phoneResponse));
103
+ if (!phoneData.ephemeralPublicKey) {
104
+ throw new Error("Phone did not send ephemeral public key");
105
+ }
106
+ const phonePublicKey = base64urlDecode(phoneData.ephemeralPublicKey);
107
+ // Compute SAS
108
+ const approverKey = {
109
+ encryptionPublicKeyHex: hexEncode(phonePublicKey),
110
+ publicKey: phonePublicKey,
111
+ };
112
+ const sasResult = computeSAS(ephemeralKeyPair.publicKey, [approverKey]);
113
+ setState("sas_verification", sasResult);
114
+ log("SAS:", sasResult.wordString);
115
+ // Wait for user to confirm SAS match before sending challenge
116
+ const sasTimeRemaining = deadline - Date.now();
117
+ if (sasTimeRemaining <= 0)
118
+ throw new Error("BLE session timeout");
119
+ await Promise.race([
120
+ sasConfirmed,
121
+ new Promise((_, reject) => setTimeout(() => reject(new Error("SAS confirmation timeout")), sasTimeRemaining)),
122
+ ]);
123
+ if (cancelled)
124
+ throw new Error("Session cancelled");
125
+ // Build and encrypt challenge
126
+ const challenge = {
127
+ type: "captcha",
128
+ domain,
129
+ nonce,
130
+ timestamp: Date.now(),
131
+ };
132
+ const challengeBytes = new TextEncoder().encode(JSON.stringify(challenge));
133
+ const requestKey = await deriveRequestKey(ephemeralKeyPair.privateKey, phonePublicKey, requestIdBytes);
134
+ const { ciphertext, nonce: encNonce } = encrypt(requestKey, challengeBytes, requestIdBytes);
135
+ // Register notification listener BEFORE writing to avoid missing the response
136
+ const challengeMsg = JSON.stringify({
137
+ type: "challenge",
138
+ sessionId,
139
+ encryptedChallenge: base64urlEncode(ciphertext),
140
+ challengeNonce: base64urlEncode(encNonce),
141
+ });
142
+ const responsePromise = waitForNotification(responseChar, deadline - Date.now(), () => cancelled, log, (fn) => {
143
+ cancelNotification = fn;
144
+ });
145
+ await writeFragmented(requestChar, new TextEncoder().encode(challengeMsg));
146
+ setState("verifying");
147
+ log("Challenge sent, waiting for response...");
148
+ // Wait for encrypted response
149
+ const responseData = await responsePromise;
150
+ cancelNotification = null;
151
+ if (cancelled)
152
+ throw new Error("Session cancelled");
153
+ const encResponse = JSON.parse(new TextDecoder().decode(responseData));
154
+ // Decrypt response
155
+ const responseKey = await deriveResponseKey(ephemeralKeyPair.privateKey, phonePublicKey, requestIdBytes);
156
+ const decrypted = decrypt(responseKey, base64urlDecode(encResponse.responseNonce), base64urlDecode(encResponse.encryptedResponse), requestIdBytes);
157
+ const response = JSON.parse(new TextDecoder().decode(decrypted));
158
+ // Disconnect
159
+ server.disconnect();
160
+ if (!response.approved) {
161
+ setState("error");
162
+ throw new Error("Verification declined by user");
163
+ }
164
+ const result = {
165
+ proof: response.attestation
166
+ ? base64urlEncode(new TextEncoder().encode(JSON.stringify(response.attestation)))
167
+ : "",
168
+ nonce,
169
+ domain,
170
+ };
171
+ setState("verified");
172
+ log("BLE verification complete");
173
+ return result;
174
+ },
175
+ confirmSAS() {
176
+ sasResolve?.();
177
+ sasResolve = null;
178
+ },
179
+ cancel() {
180
+ cancelled = true;
181
+ sasResolve?.();
182
+ sasResolve = null;
183
+ cancelNotification?.();
184
+ cancelNotification = null;
185
+ setState("error");
186
+ if (device.gatt?.connected) {
187
+ device.gatt.disconnect();
188
+ }
189
+ },
190
+ onStateChange(callback) {
191
+ stateCallbacks.push(callback);
192
+ },
193
+ };
194
+ return session;
195
+ }
196
+ // --- BLE Fragmentation (matching AckAgent protocol) ---
197
+ /** Write data to a characteristic using BLE fragmentation */
198
+ async function writeFragmented(characteristic, data) {
199
+ const fragments = fragmentMessage(data);
200
+ for (const fragment of fragments) {
201
+ await characteristic.writeValue(fragment.buffer.slice(fragment.byteOffset, fragment.byteOffset + fragment.byteLength));
202
+ }
203
+ }
204
+ /** Wait for a complete fragmented notification */
205
+ function waitForNotification(characteristic, timeoutMs, isCancelled, log, onCleanup) {
206
+ return new Promise((resolve, reject) => {
207
+ const fragments = new Map();
208
+ let expectedNext = 0;
209
+ let started = false;
210
+ let timeoutId;
211
+ const handler = (event) => {
212
+ const target = event.target;
213
+ const value = target.value;
214
+ if (!value)
215
+ return;
216
+ const raw = new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
217
+ if (raw.length < FRAGMENT_HEADER_SIZE)
218
+ return;
219
+ const flags = raw[0];
220
+ const seq = raw[1];
221
+ const payload = raw.slice(FRAGMENT_HEADER_SIZE);
222
+ if (flags & FRAGMENT_FLAG_FIRST) {
223
+ fragments.clear();
224
+ started = true;
225
+ expectedNext = 0;
226
+ }
227
+ if (!started || seq !== expectedNext)
228
+ return;
229
+ fragments.set(seq, payload);
230
+ expectedNext = (expectedNext + 1) & 0xff;
231
+ // Check total reassembled size
232
+ let currentSize = 0;
233
+ for (const p of fragments.values())
234
+ currentSize += p.length;
235
+ if (currentSize > MAX_REASSEMBLY_BYTES) {
236
+ clearTimeout(timeoutId);
237
+ characteristic.removeEventListener("characteristicvaluechanged", handler);
238
+ reject(new Error("BLE message exceeds maximum size"));
239
+ return;
240
+ }
241
+ if (flags & FRAGMENT_FLAG_LAST) {
242
+ clearTimeout(timeoutId);
243
+ characteristic.removeEventListener("characteristicvaluechanged", handler);
244
+ let totalSize = 0;
245
+ for (const p of fragments.values())
246
+ totalSize += p.length;
247
+ const result = new Uint8Array(totalSize);
248
+ let off = 0;
249
+ for (let s = 0; s < expectedNext; s++) {
250
+ const p = fragments.get(s);
251
+ if (p) {
252
+ result.set(p, off);
253
+ off += p.length;
254
+ }
255
+ }
256
+ log(`Reassembled ${expectedNext} fragments (${totalSize} bytes)`);
257
+ resolve(result);
258
+ }
259
+ };
260
+ characteristic.addEventListener("characteristicvaluechanged", handler);
261
+ timeoutId = setTimeout(() => {
262
+ characteristic.removeEventListener("characteristicvaluechanged", handler);
263
+ if (isCancelled()) {
264
+ reject(new Error("Session cancelled"));
265
+ }
266
+ else {
267
+ reject(new Error("BLE response timeout"));
268
+ }
269
+ }, timeoutMs);
270
+ const cleanup = () => {
271
+ clearTimeout(timeoutId);
272
+ characteristic.removeEventListener("characteristicvaluechanged", handler);
273
+ reject(new Error("Session cancelled"));
274
+ };
275
+ onCleanup?.(cleanup);
276
+ });
277
+ }
278
+ //# sourceMappingURL=ble.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ble.js","sourceRoot":"","sources":["../src/ble.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Core captcha session logic.
3
+ *
4
+ * Handles ephemeral key generation, QR code URL creation, relay polling,
5
+ * E2E encrypted challenge/response exchange, and SAS computation.
6
+ */
7
+ import type { CaptchaOptions, CaptchaResult, CaptchaSessionState, SASDisplay } from "./types.js";
8
+ /** Captcha session — manages the ephemeral E2E encrypted session with the phone */
9
+ export interface CaptchaSession {
10
+ /** Unique session ID */
11
+ readonly sessionId: string;
12
+ /** URL to encode as QR code */
13
+ readonly qrCodeUrl: string;
14
+ /** Current session state */
15
+ readonly state: CaptchaSessionState;
16
+ /** SAS words/emojis (available after phone connects) */
17
+ readonly sas: SASDisplay | null;
18
+ /** Wait for the captcha result (resolves on approval, rejects on error/timeout) */
19
+ waitForResult(): Promise<CaptchaResult>;
20
+ /**
21
+ * Confirm SAS match — unblocks challenge sending.
22
+ * In BLE mode, the browser user must call this after visually confirming the SAS.
23
+ * In QR mode, SAS confirmation happens on the phone side, so this is a no-op.
24
+ */
25
+ confirmSAS(): void;
26
+ /** Cancel the session */
27
+ cancel(): void;
28
+ /** Subscribe to state changes */
29
+ onStateChange(callback: (state: CaptchaSessionState, sas: SASDisplay | null) => void): void;
30
+ }
31
+ /** Create a new captcha session */
32
+ export declare function createSession(options: CaptchaOptions): Promise<CaptchaSession>;
33
+ /** Hex-encode bytes (lowercase) */
34
+ export declare function hexEncode(data: Uint8Array): string;
35
+ //# sourceMappingURL=captcha.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"captcha.d.ts","sourceRoot":"","sources":["../src/captcha.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH,OAAO,KAAK,EACV,cAAc,EACd,aAAa,EACb,mBAAmB,EAGnB,UAAU,EACX,MAAM,YAAY,CAAC;AAMpB,mFAAmF;AACnF,MAAM,WAAW,cAAc;IAC7B,wBAAwB;IACxB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,+BAA+B;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAE3B,4BAA4B;IAC5B,QAAQ,CAAC,KAAK,EAAE,mBAAmB,CAAC;IAEpC,wDAAwD;IACxD,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IAEhC,mFAAmF;IACnF,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC,CAAC;IAExC;;;;OAIG;IACH,UAAU,IAAI,IAAI,CAAC;IAEnB,yBAAyB;IACzB,MAAM,IAAI,IAAI,CAAC;IAEf,iCAAiC;IACjC,aAAa,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,mBAAmB,EAAE,GAAG,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI,CAAC;CAC7F;AAED,mCAAmC;AACnC,wBAAsB,aAAa,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CAgLpF;AAuKD,mCAAmC;AACnC,wBAAgB,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAElD"}