mirror of
https://github.com/altcha-org/altcha-lib.git
synced 2026-01-25 04:18:21 +00:00
0.1.3
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
/node_modules
|
||||
/benchmark/node_modules
|
||||
.TODO
|
||||
.DS_Store
|
||||
6
.husky/pre-commit
Normal file
6
.husky/pre-commit
Normal file
@@ -0,0 +1,6 @@
|
||||
npm test
|
||||
npm run build
|
||||
npm run denoify
|
||||
git add deno_dist
|
||||
git add dist
|
||||
git add cjs
|
||||
65
README.md
65
README.md
@@ -42,6 +42,8 @@ Parameters:
|
||||
- `salt?: string`: Optional salt string. If not provided, a random salt will be generated.
|
||||
- `saltLength?: number` Optional maximum lenght of the random salt (in bytes, defaults to 12).
|
||||
|
||||
Returns: `Promise<Challenge>`
|
||||
|
||||
### `verifySolution(payload, hmacKey)`
|
||||
|
||||
Verifies an ALTCHA solution. The payload can be a Base64-encoded JSON payload (as submitted by the widget) or an object.
|
||||
@@ -51,6 +53,69 @@ Parameters:
|
||||
- `payload: string | Payload`
|
||||
- `hmacKey: string`
|
||||
|
||||
Returns: `Promise<boolean>`
|
||||
|
||||
### `solveChallenge(challenge, salt, algorithm?, max?, start?)`
|
||||
|
||||
Finds a solution to the given challenge.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `challenge: string` (required): The challenge hash.
|
||||
- `salt: string` (required): The challenge salt.
|
||||
- `algorithm?: string`: Optional algorithm (default: `SHA-256`).
|
||||
- `max?: string`: Optional `maxnumber` to iterate to (default: 1e6).
|
||||
- `start?: string`: Optional starting number (default: 0).
|
||||
|
||||
Returns: `{ controller: AbortController, promise: Promise<Solution | null> }`
|
||||
|
||||
### `solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm?, max?, start?)`
|
||||
|
||||
Finds a solution to the given challenge with [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker) running concurrently.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `workerScript: string` (required): The path or URL of the worker script.
|
||||
- `concurrency: number` (required): The concurrency (number of workers).
|
||||
- `challenge: string` (required): The challenge hash.
|
||||
- `salt: string` (required): The challenge salt.
|
||||
- `algorithm?: string`: Optional algorithm (default: `SHA-256`).
|
||||
- `max?: string`: Optional `maxnumber` to iterate to (default: 1e6).
|
||||
- `start?: string`: Optional starting number (default: 0).
|
||||
|
||||
Returns: `Promise<Solution | null>`
|
||||
|
||||
Usage with `altcha-lib/worker`:
|
||||
|
||||
```ts
|
||||
import { solveChallengeWorkers } from 'altcha-lib';
|
||||
|
||||
const solution = await solveChallengeWorkers(
|
||||
'altcha-lib/worker', // URL to
|
||||
8, // spawn 8 workers
|
||||
challenge,
|
||||
salt,
|
||||
);
|
||||
```
|
||||
|
||||
## Benchmarks
|
||||
|
||||
```
|
||||
> solveChallenge()
|
||||
- n = 1,000............................... 317 ops/s ±2.63%
|
||||
- n = 10,000.............................. 32 ops/s ±1.88%
|
||||
- n = 100,000............................. 3 ops/s ±0.34%
|
||||
- n = 500,000............................. 0 ops/s ±0.32%
|
||||
|
||||
> solveChallengeWorkers() (8 workers)
|
||||
- n = 1,000............................... 66 ops/s ±3.44%
|
||||
- n = 10,000.............................. 31 ops/s ±4.28%
|
||||
- n = 100,000............................. 7 ops/s ±4.40%
|
||||
- n = 500,000............................. 1 ops/s ±2.49%
|
||||
```
|
||||
|
||||
Run with Bun on MacBook Pro M3-Pro. See [/benchmark](/benchmark/) folder for more details.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
94
benchmark/bench.ts
Normal file
94
benchmark/bench.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import { benchmark } from './helpers.js';
|
||||
import {
|
||||
createChallenge,
|
||||
solveChallenge,
|
||||
solveChallengeWorkers,
|
||||
} from '../lib/index.js';
|
||||
|
||||
const hmacKey = 'test';
|
||||
const workers = 8;
|
||||
const workerScript = await import.meta.resolve!('../lib/worker.ts');
|
||||
|
||||
const challenge1 = await createChallenge({
|
||||
hmacKey,
|
||||
maxNumber: 1000,
|
||||
number: 1000,
|
||||
});
|
||||
|
||||
const challenge2 = await createChallenge({
|
||||
hmacKey,
|
||||
maxNumber: 10000,
|
||||
number: 10000,
|
||||
});
|
||||
|
||||
const challenge3 = await createChallenge({
|
||||
hmacKey,
|
||||
maxNumber: 100000,
|
||||
number: 100000,
|
||||
});
|
||||
|
||||
const challenge4 = await createChallenge({
|
||||
hmacKey,
|
||||
maxNumber: 500000,
|
||||
number: 500000,
|
||||
});
|
||||
|
||||
await benchmark('solveChallenge()', (bench) => {
|
||||
bench
|
||||
.add('n = 1,000', async () => {
|
||||
await solveChallenge(challenge1.challenge, challenge1.salt).promise;
|
||||
})
|
||||
.add('n = 10,000', async () => {
|
||||
await solveChallenge(challenge2.challenge, challenge2.salt).promise;
|
||||
})
|
||||
.add('n = 100,000', async () => {
|
||||
await solveChallenge(challenge3.challenge, challenge3.salt).promise;
|
||||
})
|
||||
.add('n = 500,000', async () => {
|
||||
await solveChallenge(challenge4.challenge, challenge4.salt).promise;
|
||||
})
|
||||
});
|
||||
|
||||
await benchmark(`solveChallengeWorkers() (${workers} workers)`, (bench) => {
|
||||
bench
|
||||
.add('n = 1,000', async () => {
|
||||
await solveChallengeWorkers(
|
||||
workerScript,
|
||||
workers,
|
||||
challenge1.challenge,
|
||||
challenge1.salt,
|
||||
challenge1.algorithm,
|
||||
challenge1.max,
|
||||
);
|
||||
})
|
||||
.add('n = 10,000', async () => {
|
||||
await solveChallengeWorkers(
|
||||
workerScript,
|
||||
workers,
|
||||
challenge2.challenge,
|
||||
challenge2.salt,
|
||||
challenge2.algorithm,
|
||||
challenge2.max,
|
||||
);
|
||||
})
|
||||
.add('n = 100,000', async () => {
|
||||
await solveChallengeWorkers(
|
||||
workerScript,
|
||||
workers,
|
||||
challenge3.challenge,
|
||||
challenge3.salt,
|
||||
challenge3.algorithm,
|
||||
challenge3.max,
|
||||
);
|
||||
})
|
||||
.add('n = 500,000', async () => {
|
||||
await solveChallengeWorkers(
|
||||
workerScript,
|
||||
workers,
|
||||
challenge4.challenge,
|
||||
challenge4.salt,
|
||||
challenge4.algorithm,
|
||||
challenge4.max,
|
||||
);
|
||||
});
|
||||
});
|
||||
27
benchmark/helpers.ts
Normal file
27
benchmark/helpers.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { Bench } from 'tinybench';
|
||||
|
||||
const NAME_MAX_LEN = 40;
|
||||
|
||||
export async function benchmark(name: string, initFn: (bench: Bench) => void, duration: number = 500) {
|
||||
const bench = new Bench({
|
||||
time: duration,
|
||||
throws: true,
|
||||
warmupTime: 2000,
|
||||
warmupIterations: 100,
|
||||
});
|
||||
initFn(bench);
|
||||
await bench.run();
|
||||
console.log('>', name);
|
||||
for (let row of bench.table()) {
|
||||
if (row) {
|
||||
console.log(
|
||||
'-',
|
||||
row['Task Name'].slice(0, NAME_MAX_LEN).padEnd(NAME_MAX_LEN, '.'),
|
||||
row['ops/sec'].padStart(10, ' '),
|
||||
'ops/s',
|
||||
row['Margin'],
|
||||
);
|
||||
}
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
18
benchmark/package-lock.json
generated
Normal file
18
benchmark/package-lock.json
generated
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "benchmark",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"devDependencies": {
|
||||
"tinybench": "^2.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/tinybench": {
|
||||
"version": "2.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.6.0.tgz",
|
||||
"integrity": "sha512-N8hW3PG/3aOoZAN5V/NSAEDz0ZixDSSt5b/a05iqtpgfLWMSVuCo7w0k2vVvEjdrIoeGqZzweX2WlyioNIHchA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
9
benchmark/package.json
Normal file
9
benchmark/package.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"bench": "npx tsx bench.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"tinybench": "^2.6.0"
|
||||
}
|
||||
}
|
||||
1
cjs/dist/crypto.d.ts
vendored
Normal file
1
cjs/dist/crypto.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
6
cjs/dist/crypto.js
vendored
Normal file
6
cjs/dist/crypto.js
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
if (!('crypto' in globalThis)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
globalThis.crypto = require('node:crypto').webcrypto;
|
||||
}
|
||||
2
cjs/dist/helpers.d.ts
vendored
2
cjs/dist/helpers.d.ts
vendored
@@ -1,4 +1,6 @@
|
||||
import './crypto.js';
|
||||
import type { Algorithm } from './types.js';
|
||||
export declare const encoder: TextEncoder;
|
||||
export declare function ab2hex(ab: ArrayBuffer | Uint8Array): string;
|
||||
export declare function hash(algorithm: Algorithm, str: string): Promise<string>;
|
||||
export declare function hmac(algorithm: Algorithm, str: string, secret: string): Promise<string>;
|
||||
|
||||
15
cjs/dist/helpers.js
vendored
15
cjs/dist/helpers.js
vendored
@@ -1,11 +1,8 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.randomInt = exports.randomBytes = exports.hmac = exports.hash = exports.ab2hex = void 0;
|
||||
const encoder = new TextEncoder();
|
||||
if (!('crypto' in globalThis)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
globalThis.crypto = require('node:crypto').webcrypto;
|
||||
}
|
||||
exports.randomInt = exports.randomBytes = exports.hmac = exports.hash = exports.ab2hex = exports.encoder = void 0;
|
||||
require("./crypto.js");
|
||||
exports.encoder = new TextEncoder();
|
||||
function ab2hex(ab) {
|
||||
return [...new Uint8Array(ab)]
|
||||
.map((x) => x.toString(16).padStart(2, '0'))
|
||||
@@ -13,15 +10,15 @@ function ab2hex(ab) {
|
||||
}
|
||||
exports.ab2hex = ab2hex;
|
||||
async function hash(algorithm, str) {
|
||||
return ab2hex(await crypto.subtle.digest(algorithm.toUpperCase(), encoder.encode(str)));
|
||||
return ab2hex(await crypto.subtle.digest(algorithm.toUpperCase(), exports.encoder.encode(str)));
|
||||
}
|
||||
exports.hash = hash;
|
||||
async function hmac(algorithm, str, secret) {
|
||||
const key = await crypto.subtle.importKey('raw', encoder.encode(secret), {
|
||||
const key = await crypto.subtle.importKey('raw', exports.encoder.encode(secret), {
|
||||
name: 'HMAC',
|
||||
hash: algorithm,
|
||||
}, false, ['sign', 'verify']);
|
||||
return ab2hex(await crypto.subtle.sign('HMAC', key, encoder.encode(str)));
|
||||
return ab2hex(await crypto.subtle.sign('HMAC', key, exports.encoder.encode(str)));
|
||||
}
|
||||
exports.hmac = hmac;
|
||||
function randomBytes(length) {
|
||||
|
||||
7
cjs/dist/index.d.ts
vendored
7
cjs/dist/index.d.ts
vendored
@@ -1,3 +1,8 @@
|
||||
import type { Challenge, ChallengeOptions, Payload } from './types.js';
|
||||
import type { Challenge, ChallengeOptions, Payload, Solution } from './types.js';
|
||||
export declare function createChallenge(options: ChallengeOptions): Promise<Challenge>;
|
||||
export declare function verifySolution(payload: string | Payload, hmacKey: string): Promise<boolean>;
|
||||
export declare function solveChallenge(challenge: string, salt: string, algorithm?: string, max?: number, start?: number): {
|
||||
promise: Promise<Solution | null>;
|
||||
controller: AbortController;
|
||||
};
|
||||
export declare function solveChallengeWorkers(workerScript: string | URL, concurrency: number, challenge: string, salt: string, algorithm?: string, max?: number, startNumber?: number): Promise<Solution | null>;
|
||||
|
||||
87
cjs/dist/index.js
vendored
87
cjs/dist/index.js
vendored
@@ -1,20 +1,21 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.verifySolution = exports.createChallenge = void 0;
|
||||
exports.solveChallengeWorkers = exports.solveChallenge = exports.verifySolution = exports.createChallenge = void 0;
|
||||
const helpers_js_1 = require("./helpers.js");
|
||||
const DEFAULT_MAX_NUMBER = 1e6;
|
||||
const DEFAULT_SALT_LEN = 12;
|
||||
const DEFAULT_ALG = 'SHA-256';
|
||||
async function createChallenge(options) {
|
||||
const algorithm = options.algorithm || DEFAULT_ALG;
|
||||
const maxNumber = options.maxNumber || DEFAULT_MAX_NUMBER;
|
||||
const max = options.maxNumber || DEFAULT_MAX_NUMBER;
|
||||
const saltLength = options.saltLength || DEFAULT_SALT_LEN;
|
||||
const salt = options.salt || (0, helpers_js_1.ab2hex)((0, helpers_js_1.randomBytes)(saltLength));
|
||||
const number = options.number === void 0 ? (0, helpers_js_1.randomInt)(maxNumber) : options.number;
|
||||
const number = options.number === void 0 ? (0, helpers_js_1.randomInt)(max) : options.number;
|
||||
const challenge = await (0, helpers_js_1.hash)(algorithm, salt + number);
|
||||
return {
|
||||
algorithm,
|
||||
challenge,
|
||||
max,
|
||||
salt,
|
||||
signature: await (0, helpers_js_1.hmac)(algorithm, challenge, options.hmacKey),
|
||||
};
|
||||
@@ -34,3 +35,83 @@ async function verifySolution(payload, hmacKey) {
|
||||
check.signature === payload.signature);
|
||||
}
|
||||
exports.verifySolution = verifySolution;
|
||||
function solveChallenge(challenge, salt, algorithm = 'SHA-256', max = 1e6, start = 0) {
|
||||
const controller = new AbortController();
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const startTime = Date.now();
|
||||
const next = (n) => {
|
||||
if (controller.signal.aborted || n > max) {
|
||||
resolve(null);
|
||||
}
|
||||
else {
|
||||
hashChallenge(salt, n, algorithm)
|
||||
.then((t) => {
|
||||
if (t === challenge) {
|
||||
resolve({
|
||||
number: n,
|
||||
took: Date.now() - startTime,
|
||||
});
|
||||
}
|
||||
else {
|
||||
next(n + 1);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
}
|
||||
};
|
||||
next(start);
|
||||
});
|
||||
return {
|
||||
promise,
|
||||
controller,
|
||||
};
|
||||
}
|
||||
exports.solveChallenge = solveChallenge;
|
||||
async function solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm = 'SHA-256', max = 1e6, startNumber = 0) {
|
||||
const workers = [];
|
||||
if (concurrency < 1) {
|
||||
throw new Error('Wrong number of workers configured.');
|
||||
}
|
||||
if (concurrency > 16) {
|
||||
throw new Error('Too many workers. Max. 16 allowed workers.');
|
||||
}
|
||||
for (let i = 0; i < concurrency; i++) {
|
||||
workers.push(new Worker(workerScript, {
|
||||
type: 'module',
|
||||
}));
|
||||
}
|
||||
const step = Math.ceil(max / concurrency);
|
||||
const solutions = await Promise.all(workers.map((worker, i) => {
|
||||
const start = startNumber + i * step;
|
||||
return new Promise((resolve) => {
|
||||
worker.addEventListener('message', (message) => {
|
||||
if (message.data) {
|
||||
for (const w of workers) {
|
||||
if (w !== worker) {
|
||||
w.postMessage({ type: 'abort' });
|
||||
}
|
||||
}
|
||||
}
|
||||
resolve(message.data);
|
||||
});
|
||||
worker.postMessage({
|
||||
payload: {
|
||||
algorithm,
|
||||
challenge,
|
||||
max: start + step,
|
||||
salt,
|
||||
start,
|
||||
},
|
||||
type: 'work',
|
||||
});
|
||||
});
|
||||
}));
|
||||
for (const worker of workers) {
|
||||
worker.terminate();
|
||||
}
|
||||
return solutions.find((solution) => !!solution) || null;
|
||||
}
|
||||
exports.solveChallengeWorkers = solveChallengeWorkers;
|
||||
async function hashChallenge(salt, num, algorithm) {
|
||||
return (0, helpers_js_1.ab2hex)(await crypto.subtle.digest(algorithm.toUpperCase(), helpers_js_1.encoder.encode(salt + num)));
|
||||
}
|
||||
|
||||
6
cjs/dist/types.d.ts
vendored
6
cjs/dist/types.d.ts
vendored
@@ -2,6 +2,7 @@ export type Algorithm = 'SHA-1' | 'SHA-256' | 'SHA-512';
|
||||
export interface Challenge {
|
||||
algorithm: Algorithm;
|
||||
challenge: string;
|
||||
max?: number;
|
||||
salt: string;
|
||||
signature: string;
|
||||
}
|
||||
@@ -20,3 +21,8 @@ export interface Payload {
|
||||
salt: string;
|
||||
signature: string;
|
||||
}
|
||||
export interface Solution {
|
||||
number: number;
|
||||
took: number;
|
||||
worker?: boolean;
|
||||
}
|
||||
|
||||
1
cjs/dist/worker.d.ts
vendored
Normal file
1
cjs/dist/worker.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
19
cjs/dist/worker.js
vendored
Normal file
19
cjs/dist/worker.js
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const index_js_1 = require("./index.js");
|
||||
let controller = undefined;
|
||||
onmessage = async (message) => {
|
||||
const { type, payload } = message.data;
|
||||
if (type === 'abort') {
|
||||
controller?.abort();
|
||||
controller = undefined;
|
||||
}
|
||||
else if (type === 'work') {
|
||||
const { alg, challenge, max, salt, start } = payload || {};
|
||||
const result = (0, index_js_1.solveChallenge)(challenge, salt, alg, max, start);
|
||||
controller = result.controller;
|
||||
result.promise.then((solution) => {
|
||||
self.postMessage(solution ? { ...solution, worker: true } : solution);
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -37,8 +37,12 @@ Parameters:
|
||||
- `options: ChallengeOptions`:
|
||||
- `algorithm?: string`: Algorithm to use (`SHA-1`, `SHA-256`, `SHA-512`, default: `SHA-256`).
|
||||
- `hmacKey: string` (required): Signature HMAC key.
|
||||
- `maxNumber?: number` Optional maximum number for the random number generator (defaults to 1,000,000).
|
||||
- `number?: number`: Optional number to use. If not provided, a random number will be generated.
|
||||
- `salt?: string`: Optional salt string. If not provided, a random salt will be generated.
|
||||
- `saltLength?: number` Optional maximum lenght of the random salt (in bytes, defaults to 12).
|
||||
|
||||
Returns: `Promise<Challenge>`
|
||||
|
||||
### `verifySolution(payload, hmacKey)`
|
||||
|
||||
@@ -49,6 +53,69 @@ Parameters:
|
||||
- `payload: string | Payload`
|
||||
- `hmacKey: string`
|
||||
|
||||
Returns: `Promise<boolean>`
|
||||
|
||||
### `solveChallenge(challenge, salt, algorithm?, max?, start?)`
|
||||
|
||||
Finds a solution to the given challenge.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `challenge: string` (required): The challenge hash.
|
||||
- `salt: string` (required): The challenge salt.
|
||||
- `algorithm?: string`: Optional algorithm (default: `SHA-256`).
|
||||
- `max?: string`: Optional `maxnumber` to iterate to (default: 1e6).
|
||||
- `start?: string`: Optional starting number (default: 0).
|
||||
|
||||
Returns: `{ controller: AbortController, promise: Promise<Solution | null> }`
|
||||
|
||||
### `solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm?, max?, start?)`
|
||||
|
||||
Finds a solution to the given challenge with [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker) running concurrently.
|
||||
|
||||
Parameters:
|
||||
|
||||
- `workerScript: string` (required): The path or URL of the worker script.
|
||||
- `concurrency: number` (required): The concurrency (number of workers).
|
||||
- `challenge: string` (required): The challenge hash.
|
||||
- `salt: string` (required): The challenge salt.
|
||||
- `algorithm?: string`: Optional algorithm (default: `SHA-256`).
|
||||
- `max?: string`: Optional `maxnumber` to iterate to (default: 1e6).
|
||||
- `start?: string`: Optional starting number (default: 0).
|
||||
|
||||
Returns: `Promise<Solution | null>`
|
||||
|
||||
Usage with `altcha-lib/worker`:
|
||||
|
||||
```ts
|
||||
import { solveChallengeWorkers } from 'altcha-lib';
|
||||
|
||||
const solution = await solveChallengeWorkers(
|
||||
'altcha-lib/worker', // URL to
|
||||
8, // spawn 8 workers
|
||||
challenge,
|
||||
salt,
|
||||
);
|
||||
```
|
||||
|
||||
## Benchmarks
|
||||
|
||||
```
|
||||
> solveChallenge()
|
||||
- n = 1,000............................... 317 ops/s ±2.63%
|
||||
- n = 10,000.............................. 32 ops/s ±1.88%
|
||||
- n = 100,000............................. 3 ops/s ±0.34%
|
||||
- n = 500,000............................. 0 ops/s ±0.32%
|
||||
|
||||
> solveChallengeWorkers() (8 workers)
|
||||
- n = 1,000............................... 66 ops/s ±3.44%
|
||||
- n = 10,000.............................. 31 ops/s ±4.28%
|
||||
- n = 100,000............................. 7 ops/s ±4.40%
|
||||
- n = 500,000............................. 1 ops/s ±2.49%
|
||||
```
|
||||
|
||||
Run with Bun on MacBook Pro M3-Pro. See [/benchmark](/benchmark/) folder for more details.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
4
deno_dist/crypto.ts
Normal file
4
deno_dist/crypto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
if (!('crypto' in globalThis)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
globalThis.crypto = require('node:crypto').webcrypto;
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
import './crypto.ts';
|
||||
import type { Algorithm } from './types.ts';
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
if (!('crypto' in globalThis)) {
|
||||
// @ts-ignore
|
||||
globalThis.crypto = (await import('node:crypto')).webcrypto;
|
||||
}
|
||||
export const encoder = new TextEncoder();
|
||||
|
||||
export function ab2hex(ab: ArrayBuffer | Uint8Array) {
|
||||
return [...new Uint8Array(ab)]
|
||||
|
||||
@@ -1,25 +1,36 @@
|
||||
import { ab2hex, hash, hmac, randomBytes, randomInt } from './helpers.ts';
|
||||
import {
|
||||
ab2hex,
|
||||
encoder,
|
||||
hash,
|
||||
hmac,
|
||||
randomBytes,
|
||||
randomInt,
|
||||
} from './helpers.ts';
|
||||
import type {
|
||||
Algorithm,
|
||||
Challenge,
|
||||
ChallengeOptions,
|
||||
Payload,
|
||||
Solution,
|
||||
} from './types.ts';
|
||||
|
||||
const DEFAULT_MAX_NUMBER = 1e7;
|
||||
const DEFAULT_MAX_NUMBER = 1e6;
|
||||
const DEFAULT_SALT_LEN = 12;
|
||||
const DEFAULT_ALG: Algorithm = 'SHA-256';
|
||||
|
||||
export async function createChallenge(
|
||||
options: ChallengeOptions
|
||||
): Promise<Challenge> {
|
||||
const algorithm = options.algorithm || DEFAULT_ALG;
|
||||
const salt = options.salt || ab2hex(randomBytes(12));
|
||||
const number =
|
||||
options.number === void 0 ? randomInt(DEFAULT_MAX_NUMBER) : options.number;
|
||||
const max = options.maxNumber || DEFAULT_MAX_NUMBER;
|
||||
const saltLength = options.saltLength || DEFAULT_SALT_LEN;
|
||||
const salt = options.salt || ab2hex(randomBytes(saltLength));
|
||||
const number = options.number === void 0 ? randomInt(max) : options.number;
|
||||
const challenge = await hash(algorithm, salt + number);
|
||||
return {
|
||||
algorithm,
|
||||
challenge,
|
||||
max,
|
||||
salt,
|
||||
signature: await hmac(algorithm, challenge, options.hmacKey),
|
||||
};
|
||||
@@ -43,3 +54,105 @@ export async function verifySolution(
|
||||
check.signature === payload.signature
|
||||
);
|
||||
}
|
||||
|
||||
export function solveChallenge(
|
||||
challenge: string,
|
||||
salt: string,
|
||||
algorithm: string = 'SHA-256',
|
||||
max: number = 1e6,
|
||||
start: number = 0
|
||||
): { promise: Promise<Solution | null>; controller: AbortController } {
|
||||
const controller = new AbortController();
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const startTime = Date.now();
|
||||
const next = (n: number) => {
|
||||
if (controller.signal.aborted || n > max) {
|
||||
resolve(null);
|
||||
} else {
|
||||
hashChallenge(salt, n, algorithm)
|
||||
.then((t) => {
|
||||
if (t === challenge) {
|
||||
resolve({
|
||||
number: n,
|
||||
took: Date.now() - startTime,
|
||||
});
|
||||
} else {
|
||||
next(n + 1);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
}
|
||||
};
|
||||
next(start);
|
||||
}) as Promise<Solution | null>;
|
||||
return {
|
||||
promise,
|
||||
controller,
|
||||
};
|
||||
}
|
||||
|
||||
export async function solveChallengeWorkers(
|
||||
workerScript: string | URL,
|
||||
concurrency: number,
|
||||
challenge: string,
|
||||
salt: string,
|
||||
algorithm: string = 'SHA-256',
|
||||
max: number = 1e6,
|
||||
startNumber: number = 0
|
||||
) {
|
||||
const workers: Worker[] = [];
|
||||
if (concurrency < 1) {
|
||||
throw new Error('Wrong number of workers configured.');
|
||||
}
|
||||
if (concurrency > 16) {
|
||||
throw new Error('Too many workers. Max. 16 allowed workers.');
|
||||
}
|
||||
for (let i = 0; i < concurrency; i++) {
|
||||
workers.push(
|
||||
new Worker(workerScript, {
|
||||
type: 'module',
|
||||
})
|
||||
);
|
||||
}
|
||||
const step = Math.ceil(max / concurrency);
|
||||
const solutions = await Promise.all(
|
||||
workers.map((worker, i) => {
|
||||
const start = startNumber + i * step;
|
||||
return new Promise((resolve) => {
|
||||
worker.addEventListener('message', (message: MessageEvent) => {
|
||||
if (message.data) {
|
||||
for (const w of workers) {
|
||||
if (w !== worker) {
|
||||
w.postMessage({ type: 'abort' });
|
||||
}
|
||||
}
|
||||
}
|
||||
resolve(message.data);
|
||||
});
|
||||
worker.postMessage({
|
||||
payload: {
|
||||
algorithm,
|
||||
challenge,
|
||||
max: start + step,
|
||||
salt,
|
||||
start,
|
||||
},
|
||||
type: 'work',
|
||||
});
|
||||
}) as Promise<Solution | null>;
|
||||
})
|
||||
);
|
||||
for (const worker of workers) {
|
||||
worker.terminate();
|
||||
}
|
||||
return solutions.find((solution) => !!solution) || null;
|
||||
}
|
||||
|
||||
async function hashChallenge(salt: string, num: number, algorithm: string) {
|
||||
return ab2hex(
|
||||
await crypto.subtle.digest(
|
||||
algorithm.toUpperCase(),
|
||||
encoder.encode(salt + num)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ export type Algorithm = 'SHA-1' | 'SHA-256' | 'SHA-512';
|
||||
export interface Challenge {
|
||||
algorithm: Algorithm;
|
||||
challenge: string;
|
||||
max?: number;
|
||||
salt: string;
|
||||
signature: string;
|
||||
}
|
||||
@@ -10,8 +11,10 @@ export interface Challenge {
|
||||
export interface ChallengeOptions {
|
||||
algorithm?: Algorithm;
|
||||
hmacKey: string;
|
||||
maxNumber?: number;
|
||||
number?: number;
|
||||
salt?: string;
|
||||
saltLength?: number;
|
||||
}
|
||||
|
||||
export interface Payload {
|
||||
@@ -21,3 +24,9 @@ export interface Payload {
|
||||
salt: string;
|
||||
signature: string;
|
||||
}
|
||||
|
||||
export interface Solution {
|
||||
number: number;
|
||||
took: number;
|
||||
worker?: boolean;
|
||||
}
|
||||
|
||||
20
deno_dist/worker.ts
Normal file
20
deno_dist/worker.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { solveChallenge } from './index.ts';
|
||||
|
||||
let controller: AbortController | undefined = undefined;
|
||||
|
||||
onmessage = async (message) => {
|
||||
const { type, payload } = message.data;
|
||||
if (type === 'abort') {
|
||||
controller?.abort();
|
||||
controller = undefined;
|
||||
} else if (type === 'work') {
|
||||
const { alg, challenge, max, salt, start } = payload || {};
|
||||
const result = solveChallenge(challenge, salt, alg, max, start);
|
||||
controller = result.controller;
|
||||
result.promise.then((solution) => {
|
||||
self.postMessage(solution ? { ...solution, worker: true } : solution);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export {};
|
||||
1
dist/crypto.d.ts
vendored
Normal file
1
dist/crypto.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
5
dist/crypto.js
vendored
Normal file
5
dist/crypto.js
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
if (!('crypto' in globalThis)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
globalThis.crypto = require('node:crypto').webcrypto;
|
||||
}
|
||||
export {};
|
||||
2
dist/helpers.d.ts
vendored
2
dist/helpers.d.ts
vendored
@@ -1,4 +1,6 @@
|
||||
import './crypto.js';
|
||||
import type { Algorithm } from './types.js';
|
||||
export declare const encoder: TextEncoder;
|
||||
export declare function ab2hex(ab: ArrayBuffer | Uint8Array): string;
|
||||
export declare function hash(algorithm: Algorithm, str: string): Promise<string>;
|
||||
export declare function hmac(algorithm: Algorithm, str: string, secret: string): Promise<string>;
|
||||
|
||||
7
dist/helpers.js
vendored
7
dist/helpers.js
vendored
@@ -1,8 +1,5 @@
|
||||
const encoder = new TextEncoder();
|
||||
if (!('crypto' in globalThis)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
globalThis.crypto = require('node:crypto').webcrypto;
|
||||
}
|
||||
import './crypto.js';
|
||||
export const encoder = new TextEncoder();
|
||||
export function ab2hex(ab) {
|
||||
return [...new Uint8Array(ab)]
|
||||
.map((x) => x.toString(16).padStart(2, '0'))
|
||||
|
||||
7
dist/index.d.ts
vendored
7
dist/index.d.ts
vendored
@@ -1,3 +1,8 @@
|
||||
import type { Challenge, ChallengeOptions, Payload } from './types.js';
|
||||
import type { Challenge, ChallengeOptions, Payload, Solution } from './types.js';
|
||||
export declare function createChallenge(options: ChallengeOptions): Promise<Challenge>;
|
||||
export declare function verifySolution(payload: string | Payload, hmacKey: string): Promise<boolean>;
|
||||
export declare function solveChallenge(challenge: string, salt: string, algorithm?: string, max?: number, start?: number): {
|
||||
promise: Promise<Solution | null>;
|
||||
controller: AbortController;
|
||||
};
|
||||
export declare function solveChallengeWorkers(workerScript: string | URL, concurrency: number, challenge: string, salt: string, algorithm?: string, max?: number, startNumber?: number): Promise<Solution | null>;
|
||||
|
||||
85
dist/index.js
vendored
85
dist/index.js
vendored
@@ -1,17 +1,18 @@
|
||||
import { ab2hex, hash, hmac, randomBytes, randomInt } from './helpers.js';
|
||||
import { ab2hex, encoder, hash, hmac, randomBytes, randomInt, } from './helpers.js';
|
||||
const DEFAULT_MAX_NUMBER = 1e6;
|
||||
const DEFAULT_SALT_LEN = 12;
|
||||
const DEFAULT_ALG = 'SHA-256';
|
||||
export async function createChallenge(options) {
|
||||
const algorithm = options.algorithm || DEFAULT_ALG;
|
||||
const maxNumber = options.maxNumber || DEFAULT_MAX_NUMBER;
|
||||
const max = options.maxNumber || DEFAULT_MAX_NUMBER;
|
||||
const saltLength = options.saltLength || DEFAULT_SALT_LEN;
|
||||
const salt = options.salt || ab2hex(randomBytes(saltLength));
|
||||
const number = options.number === void 0 ? randomInt(maxNumber) : options.number;
|
||||
const number = options.number === void 0 ? randomInt(max) : options.number;
|
||||
const challenge = await hash(algorithm, salt + number);
|
||||
return {
|
||||
algorithm,
|
||||
challenge,
|
||||
max,
|
||||
salt,
|
||||
signature: await hmac(algorithm, challenge, options.hmacKey),
|
||||
};
|
||||
@@ -29,3 +30,81 @@ export async function verifySolution(payload, hmacKey) {
|
||||
return (check.challenge === payload.challenge &&
|
||||
check.signature === payload.signature);
|
||||
}
|
||||
export function solveChallenge(challenge, salt, algorithm = 'SHA-256', max = 1e6, start = 0) {
|
||||
const controller = new AbortController();
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const startTime = Date.now();
|
||||
const next = (n) => {
|
||||
if (controller.signal.aborted || n > max) {
|
||||
resolve(null);
|
||||
}
|
||||
else {
|
||||
hashChallenge(salt, n, algorithm)
|
||||
.then((t) => {
|
||||
if (t === challenge) {
|
||||
resolve({
|
||||
number: n,
|
||||
took: Date.now() - startTime,
|
||||
});
|
||||
}
|
||||
else {
|
||||
next(n + 1);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
}
|
||||
};
|
||||
next(start);
|
||||
});
|
||||
return {
|
||||
promise,
|
||||
controller,
|
||||
};
|
||||
}
|
||||
export async function solveChallengeWorkers(workerScript, concurrency, challenge, salt, algorithm = 'SHA-256', max = 1e6, startNumber = 0) {
|
||||
const workers = [];
|
||||
if (concurrency < 1) {
|
||||
throw new Error('Wrong number of workers configured.');
|
||||
}
|
||||
if (concurrency > 16) {
|
||||
throw new Error('Too many workers. Max. 16 allowed workers.');
|
||||
}
|
||||
for (let i = 0; i < concurrency; i++) {
|
||||
workers.push(new Worker(workerScript, {
|
||||
type: 'module',
|
||||
}));
|
||||
}
|
||||
const step = Math.ceil(max / concurrency);
|
||||
const solutions = await Promise.all(workers.map((worker, i) => {
|
||||
const start = startNumber + i * step;
|
||||
return new Promise((resolve) => {
|
||||
worker.addEventListener('message', (message) => {
|
||||
if (message.data) {
|
||||
for (const w of workers) {
|
||||
if (w !== worker) {
|
||||
w.postMessage({ type: 'abort' });
|
||||
}
|
||||
}
|
||||
}
|
||||
resolve(message.data);
|
||||
});
|
||||
worker.postMessage({
|
||||
payload: {
|
||||
algorithm,
|
||||
challenge,
|
||||
max: start + step,
|
||||
salt,
|
||||
start,
|
||||
},
|
||||
type: 'work',
|
||||
});
|
||||
});
|
||||
}));
|
||||
for (const worker of workers) {
|
||||
worker.terminate();
|
||||
}
|
||||
return solutions.find((solution) => !!solution) || null;
|
||||
}
|
||||
async function hashChallenge(salt, num, algorithm) {
|
||||
return ab2hex(await crypto.subtle.digest(algorithm.toUpperCase(), encoder.encode(salt + num)));
|
||||
}
|
||||
|
||||
6
dist/types.d.ts
vendored
6
dist/types.d.ts
vendored
@@ -2,6 +2,7 @@ export type Algorithm = 'SHA-1' | 'SHA-256' | 'SHA-512';
|
||||
export interface Challenge {
|
||||
algorithm: Algorithm;
|
||||
challenge: string;
|
||||
max?: number;
|
||||
salt: string;
|
||||
signature: string;
|
||||
}
|
||||
@@ -20,3 +21,8 @@ export interface Payload {
|
||||
salt: string;
|
||||
signature: string;
|
||||
}
|
||||
export interface Solution {
|
||||
number: number;
|
||||
took: number;
|
||||
worker?: boolean;
|
||||
}
|
||||
|
||||
1
dist/worker.d.ts
vendored
Normal file
1
dist/worker.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export {};
|
||||
17
dist/worker.js
vendored
Normal file
17
dist/worker.js
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import { solveChallenge } from './index.js';
|
||||
let controller = undefined;
|
||||
onmessage = async (message) => {
|
||||
const { type, payload } = message.data;
|
||||
if (type === 'abort') {
|
||||
controller?.abort();
|
||||
controller = undefined;
|
||||
}
|
||||
else if (type === 'work') {
|
||||
const { alg, challenge, max, salt, start } = payload || {};
|
||||
const result = solveChallenge(challenge, salt, alg, max, start);
|
||||
controller = result.controller;
|
||||
result.promise.then((solution) => {
|
||||
self.postMessage(solution ? { ...solution, worker: true } : solution);
|
||||
});
|
||||
}
|
||||
};
|
||||
4
lib/crypto.ts
Normal file
4
lib/crypto.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
if (!('crypto' in globalThis)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
globalThis.crypto = require('node:crypto').webcrypto;
|
||||
}
|
||||
@@ -1,11 +1,7 @@
|
||||
import './crypto.js';
|
||||
import type { Algorithm } from './types.js';
|
||||
|
||||
const encoder = new TextEncoder();
|
||||
|
||||
if (!('crypto' in globalThis)) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
globalThis.crypto = require('node:crypto').webcrypto;
|
||||
}
|
||||
export const encoder = new TextEncoder();
|
||||
|
||||
export function ab2hex(ab: ArrayBuffer | Uint8Array) {
|
||||
return [...new Uint8Array(ab)]
|
||||
|
||||
118
lib/index.ts
118
lib/index.ts
@@ -1,9 +1,17 @@
|
||||
import { ab2hex, hash, hmac, randomBytes, randomInt } from './helpers.js';
|
||||
import {
|
||||
ab2hex,
|
||||
encoder,
|
||||
hash,
|
||||
hmac,
|
||||
randomBytes,
|
||||
randomInt,
|
||||
} from './helpers.js';
|
||||
import type {
|
||||
Algorithm,
|
||||
Challenge,
|
||||
ChallengeOptions,
|
||||
Payload,
|
||||
Solution,
|
||||
} from './types.js';
|
||||
|
||||
const DEFAULT_MAX_NUMBER = 1e6;
|
||||
@@ -14,15 +22,15 @@ export async function createChallenge(
|
||||
options: ChallengeOptions
|
||||
): Promise<Challenge> {
|
||||
const algorithm = options.algorithm || DEFAULT_ALG;
|
||||
const maxNumber = options.maxNumber || DEFAULT_MAX_NUMBER;
|
||||
const max = options.maxNumber || DEFAULT_MAX_NUMBER;
|
||||
const saltLength = options.saltLength || DEFAULT_SALT_LEN;
|
||||
const salt = options.salt || ab2hex(randomBytes(saltLength));
|
||||
const number =
|
||||
options.number === void 0 ? randomInt(maxNumber) : options.number;
|
||||
const number = options.number === void 0 ? randomInt(max) : options.number;
|
||||
const challenge = await hash(algorithm, salt + number);
|
||||
return {
|
||||
algorithm,
|
||||
challenge,
|
||||
max,
|
||||
salt,
|
||||
signature: await hmac(algorithm, challenge, options.hmacKey),
|
||||
};
|
||||
@@ -46,3 +54,105 @@ export async function verifySolution(
|
||||
check.signature === payload.signature
|
||||
);
|
||||
}
|
||||
|
||||
export function solveChallenge(
|
||||
challenge: string,
|
||||
salt: string,
|
||||
algorithm: string = 'SHA-256',
|
||||
max: number = 1e6,
|
||||
start: number = 0
|
||||
): { promise: Promise<Solution | null>; controller: AbortController } {
|
||||
const controller = new AbortController();
|
||||
const promise = new Promise((resolve, reject) => {
|
||||
const startTime = Date.now();
|
||||
const next = (n: number) => {
|
||||
if (controller.signal.aborted || n > max) {
|
||||
resolve(null);
|
||||
} else {
|
||||
hashChallenge(salt, n, algorithm)
|
||||
.then((t) => {
|
||||
if (t === challenge) {
|
||||
resolve({
|
||||
number: n,
|
||||
took: Date.now() - startTime,
|
||||
});
|
||||
} else {
|
||||
next(n + 1);
|
||||
}
|
||||
})
|
||||
.catch(reject);
|
||||
}
|
||||
};
|
||||
next(start);
|
||||
}) as Promise<Solution | null>;
|
||||
return {
|
||||
promise,
|
||||
controller,
|
||||
};
|
||||
}
|
||||
|
||||
export async function solveChallengeWorkers(
|
||||
workerScript: string | URL,
|
||||
concurrency: number,
|
||||
challenge: string,
|
||||
salt: string,
|
||||
algorithm: string = 'SHA-256',
|
||||
max: number = 1e6,
|
||||
startNumber: number = 0
|
||||
) {
|
||||
const workers: Worker[] = [];
|
||||
if (concurrency < 1) {
|
||||
throw new Error('Wrong number of workers configured.');
|
||||
}
|
||||
if (concurrency > 16) {
|
||||
throw new Error('Too many workers. Max. 16 allowed workers.');
|
||||
}
|
||||
for (let i = 0; i < concurrency; i++) {
|
||||
workers.push(
|
||||
new Worker(workerScript, {
|
||||
type: 'module',
|
||||
})
|
||||
);
|
||||
}
|
||||
const step = Math.ceil(max / concurrency);
|
||||
const solutions = await Promise.all(
|
||||
workers.map((worker, i) => {
|
||||
const start = startNumber + i * step;
|
||||
return new Promise((resolve) => {
|
||||
worker.addEventListener('message', (message: MessageEvent) => {
|
||||
if (message.data) {
|
||||
for (const w of workers) {
|
||||
if (w !== worker) {
|
||||
w.postMessage({ type: 'abort' });
|
||||
}
|
||||
}
|
||||
}
|
||||
resolve(message.data);
|
||||
});
|
||||
worker.postMessage({
|
||||
payload: {
|
||||
algorithm,
|
||||
challenge,
|
||||
max: start + step,
|
||||
salt,
|
||||
start,
|
||||
},
|
||||
type: 'work',
|
||||
});
|
||||
}) as Promise<Solution | null>;
|
||||
})
|
||||
);
|
||||
for (const worker of workers) {
|
||||
worker.terminate();
|
||||
}
|
||||
return solutions.find((solution) => !!solution) || null;
|
||||
}
|
||||
|
||||
async function hashChallenge(salt: string, num: number, algorithm: string) {
|
||||
return ab2hex(
|
||||
await crypto.subtle.digest(
|
||||
algorithm.toUpperCase(),
|
||||
encoder.encode(salt + num)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ export type Algorithm = 'SHA-1' | 'SHA-256' | 'SHA-512';
|
||||
export interface Challenge {
|
||||
algorithm: Algorithm;
|
||||
challenge: string;
|
||||
max?: number;
|
||||
salt: string;
|
||||
signature: string;
|
||||
}
|
||||
@@ -23,3 +24,9 @@ export interface Payload {
|
||||
salt: string;
|
||||
signature: string;
|
||||
}
|
||||
|
||||
export interface Solution {
|
||||
number: number;
|
||||
took: number;
|
||||
worker?: boolean;
|
||||
}
|
||||
|
||||
20
lib/worker.ts
Normal file
20
lib/worker.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { solveChallenge } from './index.js';
|
||||
|
||||
let controller: AbortController | undefined = undefined;
|
||||
|
||||
onmessage = async (message) => {
|
||||
const { type, payload } = message.data;
|
||||
if (type === 'abort') {
|
||||
controller?.abort();
|
||||
controller = undefined;
|
||||
} else if (type === 'work') {
|
||||
const { alg, challenge, max, salt, start } = payload || {};
|
||||
const result = solveChallenge(challenge, salt, alg, max, start);
|
||||
controller = result.controller;
|
||||
result.promise.then((solution) => {
|
||||
self.postMessage(solution ? { ...solution, worker: true } : solution);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export {};
|
||||
590
package-lock.json
generated
590
package-lock.json
generated
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"name": "altcha-lib",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "altcha-lib",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"denoify": "^1.6.9",
|
||||
"eslint": "^8.56.0",
|
||||
"husky": "^9.0.11",
|
||||
"prettier": "^3.2.5",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-node": "^10.9.1",
|
||||
@@ -953,9 +954,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.10.0.tgz",
|
||||
"integrity": "sha512-/MeDQmcD96nVoRumKUljsYOLqfv1YFJps+0pTrb2Z9Nl/w5qNUysMaWQsrd1mvAlNT4yza1iVyIu4Q4AgF6V3A==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.14.0.tgz",
|
||||
"integrity": "sha512-jwXtxYbRt1V+CdQSy6Z+uZti7JF5irRKF8hlKfEnF/xJpcNGuuiZMBvuoYM+x9sr9iWGnzrlM0+9hvQ1kgkf1w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -966,9 +967,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.10.0.tgz",
|
||||
"integrity": "sha512-lvu0jK97mZDJdpZKDnZI93I0Om8lSDaiPx3OiCk0RXn3E8CMPJNS/wxjAvSJJzhhZpfjXsjLWL8LnS6qET4VNQ==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.14.0.tgz",
|
||||
"integrity": "sha512-fI9nduZhCccjzlsA/OuAwtFGWocxA4gqXGTLvOyiF8d+8o0fZUeSztixkYjcGq1fGZY3Tkq4yRvHPFxU+jdZ9Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -979,9 +980,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.10.0.tgz",
|
||||
"integrity": "sha512-uFpayx8I8tyOvDkD7X6n0PriDRWxcqEjqgtlxnUA/G9oS93ur9aZ8c8BEpzFmsed1TH5WZNG5IONB8IiW90TQg==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.14.0.tgz",
|
||||
"integrity": "sha512-BcnSPRM76/cD2gQC+rQNGBN6GStBs2pl/FpweW8JYuz5J/IEa0Fr4AtrPv766DB/6b2MZ/AfSIOSGw3nEIP8SA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -992,9 +993,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.10.0.tgz",
|
||||
"integrity": "sha512-nIdCX03qFKoR/MwQegQBK+qZoSpO3LESurVAC6s6jazLA1Mpmgzo3Nj3H1vydXp/JM29bkCiuF7tDuToj4+U9Q==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.14.0.tgz",
|
||||
"integrity": "sha512-LDyFB9GRolGN7XI6955aFeI3wCdCUszFWumWU0deHA8VpR3nWRrjG6GtGjBrQxQKFevnUTHKCfPR4IvrW3kCgQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1005,9 +1006,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.10.0.tgz",
|
||||
"integrity": "sha512-Fz7a+y5sYhYZMQFRkOyCs4PLhICAnxRX/GnWYReaAoruUzuRtcf+Qnw+T0CoAWbHCuz2gBUwmWnUgQ67fb3FYw==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.14.0.tgz",
|
||||
"integrity": "sha512-ygrGVhQP47mRh0AAD0zl6QqCbNsf0eTo+vgwkY6LunBcg0f2Jv365GXlDUECIyoXp1kKwL5WW6rsO429DBY/bA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@@ -1018,9 +1019,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.10.0.tgz",
|
||||
"integrity": "sha512-yPtF9jIix88orwfTi0lJiqINnlWo6p93MtZEoaehZnmCzEmLL0eqjA3eGVeyQhMtxdV+Mlsgfwhh0+M/k1/V7Q==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.14.0.tgz",
|
||||
"integrity": "sha512-x+uJ6MAYRlHGe9wi4HQjxpaKHPM3d3JjqqCkeC5gpnnI6OWovLdXTpfa8trjxPLnWKyBsSi5kne+146GAxFt4A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1031,9 +1032,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.10.0.tgz",
|
||||
"integrity": "sha512-9GW9yA30ib+vfFiwjX+N7PnjTnCMiUffhWj4vkG4ukYv1kJ4T9gHNg8zw+ChsOccM27G9yXrEtMScf1LaCuoWQ==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.14.0.tgz",
|
||||
"integrity": "sha512-nrRw8ZTQKg6+Lttwqo6a2VxR9tOroa2m91XbdQ2sUUzHoedXlsyvY1fN4xWdqz8PKmf4orDwejxXHjh7YBGUCA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1043,10 +1044,23 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.14.0.tgz",
|
||||
"integrity": "sha512-xV0d5jDb4aFu84XKr+lcUJ9y3qpIWhttO3Qev97z8DKLXR62LC3cXT/bMZXrjLF9X+P5oSmJTzAhqwUbY96PnA==",
|
||||
"cpu": [
|
||||
"ppc64le"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.10.0.tgz",
|
||||
"integrity": "sha512-X1ES+V4bMq2ws5fF4zHornxebNxMXye0ZZjUrzOrf7UMx1d6wMQtfcchZ8SqUnQPPHdOyOLW6fTcUiFgHFadRA==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.14.0.tgz",
|
||||
"integrity": "sha512-SDDhBQwZX6LPRoPYjAZWyL27LbcBo7WdBFWJi5PI9RPCzU8ijzkQn7tt8NXiXRiFMJCVpkuMkBf4OxSxVMizAw==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@@ -1056,10 +1070,23 @@
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.14.0.tgz",
|
||||
"integrity": "sha512-RxB/qez8zIDshNJDufYlTT0ZTVut5eCpAZ3bdXDU9yTxBzui3KhbGjROK2OYTTor7alM7XBhssgoO3CZ0XD3qA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.10.0.tgz",
|
||||
"integrity": "sha512-w/5OpT2EnI/Xvypw4FIhV34jmNqU5PZjZue2l2Y3ty1Ootm3SqhI+AmfhlUYGBTd9JnpneZCDnt3uNOiOBkMyw==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.14.0.tgz",
|
||||
"integrity": "sha512-C6y6z2eCNCfhZxT9u+jAM2Fup89ZjiG5pIzZIDycs1IwESviLxwkQcFRGLjnDrP+PT+v5i4YFvlcfAs+LnreXg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1070,9 +1097,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.10.0.tgz",
|
||||
"integrity": "sha512-q/meftEe3QlwQiGYxD9rWwB21DoKQ9Q8wA40of/of6yGHhZuGfZO0c3WYkN9dNlopHlNT3mf5BPsUSxoPuVQaw==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.14.0.tgz",
|
||||
"integrity": "sha512-i0QwbHYfnOMYsBEyjxcwGu5SMIi9sImDVjDg087hpzXqhBSosxkE7gyIYFHgfFl4mr7RrXksIBZ4DoLoP4FhJg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -1083,9 +1110,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.10.0.tgz",
|
||||
"integrity": "sha512-NrR6667wlUfP0BHaEIKgYM/2va+Oj+RjZSASbBMnszM9k+1AmliRjHc3lJIiOehtSSjqYiO7R6KLNrWOX+YNSQ==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.14.0.tgz",
|
||||
"integrity": "sha512-Fq52EYb0riNHLBTAcL0cun+rRwyZ10S9vKzhGKKgeD+XbwunszSY0rVMco5KbOsTlwovP2rTOkiII/fQ4ih/zQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@@ -1096,9 +1123,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.10.0.tgz",
|
||||
"integrity": "sha512-FV0Tpt84LPYDduIDcXvEC7HKtyXxdvhdAOvOeWMWbQNulxViH2O07QXkT/FffX4FqEI02jEbCJbr+YcuKdyyMg==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.14.0.tgz",
|
||||
"integrity": "sha512-e/PBHxPdJ00O9p5Ui43+vixSgVf4NlLsmV6QneGERJ3lnjIua/kim6PRFe3iDueT1rQcgSkYP8ZBBXa/h4iPvw==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@@ -1109,9 +1136,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.10.0.tgz",
|
||||
"integrity": "sha512-OZoJd+o5TaTSQeFFQ6WjFCiltiYVjIdsXxwu/XZ8qRpsvMQr4UsVrE5UyT9RIvsnuF47DqkJKhhVZ2Q9YW9IpQ==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.14.0.tgz",
|
||||
"integrity": "sha512-aGg7iToJjdklmxlUlJh/PaPNa4PmqHfyRMLunbL3eaMO0gp656+q1zOKkpJ/CVe9CryJv6tAN1HDoR8cNGzkag==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@@ -2585,6 +2612,21 @@
|
||||
"node": ">=16.17.0"
|
||||
}
|
||||
},
|
||||
"node_modules/husky": {
|
||||
"version": "9.0.11",
|
||||
"resolved": "https://registry.npmjs.org/husky/-/husky-9.0.11.tgz",
|
||||
"integrity": "sha512-AB6lFlbwwyIqMdHYhwPe+kjOC3Oc5P3nThEoW/AaO2BX3vJDjWPFxYLxokUZOo6RNX20He3AaT8sESs9NJcmEw==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"husky": "bin.mjs"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/typicode"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
|
||||
@@ -3276,9 +3318,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/postcss": {
|
||||
"version": "8.4.35",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz",
|
||||
"integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==",
|
||||
"version": "8.4.38",
|
||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz",
|
||||
"integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -3297,7 +3339,7 @@
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.7",
|
||||
"picocolors": "^1.0.0",
|
||||
"source-map-js": "^1.0.2"
|
||||
"source-map-js": "^1.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
@@ -3432,9 +3474,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.10.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.10.0.tgz",
|
||||
"integrity": "sha512-t2v9G2AKxcQ8yrG+WGxctBes1AomT0M4ND7jTFBCVPXQ/WFTvNSefIrNSmLKhIKBrvN8SG+CZslimJcT3W2u2g==",
|
||||
"version": "4.14.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.14.0.tgz",
|
||||
"integrity": "sha512-Qe7w62TyawbDzB4yt32R0+AbIo6m1/sqO7UPzFS8Z/ksL5mrfhA0v4CavfdmFav3D+ub4QeAgsGEe84DoWe/nQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.5"
|
||||
@@ -3447,19 +3489,21 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.10.0",
|
||||
"@rollup/rollup-android-arm64": "4.10.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.10.0",
|
||||
"@rollup/rollup-darwin-x64": "4.10.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.10.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.10.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.10.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.10.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.10.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.10.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.10.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.10.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.10.0",
|
||||
"@rollup/rollup-android-arm-eabi": "4.14.0",
|
||||
"@rollup/rollup-android-arm64": "4.14.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.14.0",
|
||||
"@rollup/rollup-darwin-x64": "4.14.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.14.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.14.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.14.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.14.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.14.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.14.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.14.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.14.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.14.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.14.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.14.0",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@@ -3577,9 +3621,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
|
||||
"integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz",
|
||||
"integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -3955,14 +3999,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/vite": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.1.1.tgz",
|
||||
"integrity": "sha512-wclpAgY3F1tR7t9LL5CcHC41YPkQIpKUGeIuT8MdNwNZr6OqOTLs7JX5vIHAtzqLWXts0T+GDrh9pN2arneKqg==",
|
||||
"version": "5.2.8",
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-5.2.8.tgz",
|
||||
"integrity": "sha512-OyZR+c1CE8yeHw5V5t59aXsUPPVTHMDjEZz8MgguLL/Q7NblxhZUlTu9xSPqlsUO/y+X7dlU05jdhvyycD55DA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.19.3",
|
||||
"postcss": "^8.4.35",
|
||||
"rollup": "^4.2.0"
|
||||
"esbuild": "^0.20.1",
|
||||
"postcss": "^8.4.38",
|
||||
"rollup": "^4.13.0"
|
||||
},
|
||||
"bin": {
|
||||
"vite": "bin/vite.js"
|
||||
@@ -4031,6 +4075,412 @@
|
||||
"url": "https://opencollective.com/vitest"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.20.2.tgz",
|
||||
"integrity": "sha512-D+EBOJHXdNZcLJRBkhENNG8Wji2kgc9AZ9KiPr1JuZjsNtyHzrsfLRrY0tk2H2aoFu6RANO1y1iPPUCDYWkb5g==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/android-arm": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.20.2.tgz",
|
||||
"integrity": "sha512-t98Ra6pw2VaDhqNWO2Oph2LXbz/EJcnLmKLGBJwEwXX/JAN83Fym1rU8l0JUWK6HkIbWONCSSatf4sf2NBRx/w==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-mRzjLacRtl/tWU0SvD8lUEwb61yP9cqQo6noDZP/O8VkwafSYwZ4yWy24kan8jE/IMERpYncRt2dw438LP3Xmg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/android-x64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-btzExgV+/lMGDDa194CcUQm53ncxzeBrWJcncOBxuC6ndBkKxnHdFJn86mCIgTELsooUmwUm9FkhSp5HYu00Rg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-4J6IRT+10J3aJH3l1yzEg9y3wkTDgDk7TSDFX+wKFiWjqWp/iCfLIYzGyasx9l0SAFPT1HwSCR+0w/h1ES/MjA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-tBcXp9KNphnNH0dfhv8KYkZhjc+H3XBkF5DKtswJblV7KlT9EI2+jeA8DgBjp908WEuYll6pF+UStUCfEpdysA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-d3qI41G4SuLiCGCFGUrKsSeTXyWG6yem1KcGZVS+3FYlYhtNoNgYrWcvkOoaqMhwXSMrZRl69ArHsGJ9mYdbbw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-d+DipyvHRuqEeM5zDivKV1KuXn9WeRX6vqSqIDgwIfPQtwMP4jaDsQsDncjTDDsExT4lR/91OLjRo8bmC1e+Cw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.20.2.tgz",
|
||||
"integrity": "sha512-VhLPeR8HTMPccbuWWcEUD1Az68TqaTYyj6nfE4QByZIQEQVWBB8vup8PpR7y1QHL3CpcF6xd5WVBU/+SBEvGTg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-9pb6rBjGvTFNira2FLIWqDk/uaf42sSyLE8j1rnUpuzsODBq7FvpwHYZxQ/It/8b+QOS1RYfqgGFNLRI+qlq2A==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.20.2.tgz",
|
||||
"integrity": "sha512-o10utieEkNPFDZFQm9CoP7Tvb33UutoJqg3qKf1PWVeeJhJw0Q347PxMvBgVVFgouYLGIhFYG0UGdBumROyiig==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.20.2.tgz",
|
||||
"integrity": "sha512-PR7sp6R/UC4CFVomVINKJ80pMFlfDfMQMYynX7t1tNTeivQ6XdX5r2XovMmha/VjR1YN/HgHWsVcTRIMkymrgQ==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.20.2.tgz",
|
||||
"integrity": "sha512-4BlTqeutE/KnOiTG5Y6Sb/Hw6hsBOZapOVF6njAESHInhlQAghVVZL1ZpIctBOoTFbQyGW+LsVYZ8lSSB3wkjA==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.20.2.tgz",
|
||||
"integrity": "sha512-rD3KsaDprDcfajSKdn25ooz5J5/fWBylaaXkuotBDGnMnDP1Uv5DLAN/45qfnf3JDYyJv/ytGHQaziHUdyzaAg==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.20.2.tgz",
|
||||
"integrity": "sha512-snwmBKacKmwTMmhLlz/3aH1Q9T8v45bKYGE3j26TsaOVtjIag4wLfWSiZykXzXuE1kbCE+zJRmwp+ZbIHinnVg==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.20.2.tgz",
|
||||
"integrity": "sha512-wcWISOobRWNm3cezm5HOZcYz1sKoHLd8VL1dl309DiixxVFoFe/o8HnwuIwn6sXre88Nwj+VwZUvJf4AFxkyrQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-1MdwI6OOTsfQfek8sLwgyjOXAu+wKhLEoaOLTjbijk6E2WONYpH9ZU2mNtR+lZ2B4uwr+usqGuVfFT9tMtGvGw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-K8/DhBxcVQkzYc43yJXDSyjlFeHQJBiowJ0uVL6Tor3jGQfSGHNNJcWxNbOI8v5k82prYqzPuwkzHt3J1T1iZQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-eMpKlV0SThJmmJgiVyN9jTPJ2VBPquf6Kt/nAoo6DgHAoN57K15ZghiHaMvqjCye/uU4X5u3YSMgVBI1h3vKrQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-2UyFtRC6cXLyejf/YEld4Hajo7UHILetzE1vsRcGL3earZEW77JxrFjH4Ez2qaTiEfMgAXxfAZCm1fvM/G/o8w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.20.2.tgz",
|
||||
"integrity": "sha512-GRibxoawM9ZCnDxnP3usoUDO9vUkpAxIIZ6GQI+IlVmr5kP3zUq+l17xELTHMWTWzjxa2guPNyrpq1GWmPvcGQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.20.2.tgz",
|
||||
"integrity": "sha512-HfLOfn9YWmkSKRQqovpnITazdtquEW8/SoHW7pWpuEeguaZI4QnCRW6b+oZTztdBnZOS2hqJ6im/D5cPzBTTlQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.20.2.tgz",
|
||||
"integrity": "sha512-N49X4lJX27+l9jbLKSqZ6bKNjzQvHaT8IIFUy+YIqmXQdjYCToGWwOItDrfby14c78aDd5NHQl29xingXfCdLQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"dev": true,
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/vite/node_modules/esbuild": {
|
||||
"version": "0.20.2",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.20.2.tgz",
|
||||
"integrity": "sha512-WdOOppmUNU+IbZ0PaDiTst80zjnrOkyJNHoKupIcVyU8Lvla3Ugx94VzkQ32Ijqd7UhHJy75gNWDMUekcrSJ6g==",
|
||||
"dev": true,
|
||||
"hasInstallScript": true,
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.20.2",
|
||||
"@esbuild/android-arm": "0.20.2",
|
||||
"@esbuild/android-arm64": "0.20.2",
|
||||
"@esbuild/android-x64": "0.20.2",
|
||||
"@esbuild/darwin-arm64": "0.20.2",
|
||||
"@esbuild/darwin-x64": "0.20.2",
|
||||
"@esbuild/freebsd-arm64": "0.20.2",
|
||||
"@esbuild/freebsd-x64": "0.20.2",
|
||||
"@esbuild/linux-arm": "0.20.2",
|
||||
"@esbuild/linux-arm64": "0.20.2",
|
||||
"@esbuild/linux-ia32": "0.20.2",
|
||||
"@esbuild/linux-loong64": "0.20.2",
|
||||
"@esbuild/linux-mips64el": "0.20.2",
|
||||
"@esbuild/linux-ppc64": "0.20.2",
|
||||
"@esbuild/linux-riscv64": "0.20.2",
|
||||
"@esbuild/linux-s390x": "0.20.2",
|
||||
"@esbuild/linux-x64": "0.20.2",
|
||||
"@esbuild/netbsd-x64": "0.20.2",
|
||||
"@esbuild/openbsd-x64": "0.20.2",
|
||||
"@esbuild/sunos-x64": "0.20.2",
|
||||
"@esbuild/win32-arm64": "0.20.2",
|
||||
"@esbuild/win32-ia32": "0.20.2",
|
||||
"@esbuild/win32-x64": "0.20.2"
|
||||
}
|
||||
},
|
||||
"node_modules/vitest": {
|
||||
"version": "1.2.2",
|
||||
"resolved": "https://registry.npmjs.org/vitest/-/vitest-1.2.2.tgz",
|
||||
|
||||
14
package.json
14
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "altcha-lib",
|
||||
"version": "0.1.2",
|
||||
"version": "0.1.3",
|
||||
"description": "A library for creating and verifying ALTCHA challenges for Node.js, Bun and Deno.",
|
||||
"author": "Daniel Regeci",
|
||||
"license": "MIT",
|
||||
@@ -19,8 +19,9 @@
|
||||
"denoify": "rimraf deno_dist && denoify && find deno_dist/. -type f -exec sed -i '' -e 's/node:node:/node:/g' {} +",
|
||||
"eslint": "eslint ./lib/**/*",
|
||||
"format": "prettier --write './(lib|tests)/**/*'",
|
||||
"test": "vitest",
|
||||
"test:deno": "deno test tests/deno.ts"
|
||||
"test": "vitest --run",
|
||||
"test:deno": "deno test --allow-read tests/deno.ts",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"files": [
|
||||
"cjs",
|
||||
@@ -36,6 +37,12 @@
|
||||
"./types": {
|
||||
"types": "./dist/types.d.ts",
|
||||
"import": "./dist/types.js"
|
||||
},
|
||||
"./worker": {
|
||||
"types": "./dist/worker.d.ts",
|
||||
"import": "./dist/worker.js",
|
||||
"require": "./cjs/dist/worker.js",
|
||||
"default": "./dist/worker.js"
|
||||
}
|
||||
},
|
||||
"typesVersions": {
|
||||
@@ -50,6 +57,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "^6.21.0",
|
||||
"denoify": "^1.6.9",
|
||||
"eslint": "^8.56.0",
|
||||
"husky": "^9.0.11",
|
||||
"prettier": "^3.2.5",
|
||||
"rimraf": "^5.0.5",
|
||||
"ts-node": "^10.9.1",
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { createChallenge, verifySolution } from '../lib/index.js';
|
||||
import {
|
||||
createChallenge,
|
||||
solveChallenge,
|
||||
solveChallengeWorkers,
|
||||
verifySolution,
|
||||
} from '../lib/index.js';
|
||||
import { Challenge } from '../lib/types.js';
|
||||
|
||||
describe('challenge', () => {
|
||||
@@ -13,6 +18,7 @@ describe('challenge', () => {
|
||||
expect(challenge).toEqual({
|
||||
algorithm: 'SHA-256',
|
||||
challenge: expect.any(String),
|
||||
max: expect.any(Number),
|
||||
salt: expect.any(String),
|
||||
signature: expect.any(String),
|
||||
} satisfies Challenge);
|
||||
@@ -29,6 +35,7 @@ describe('challenge', () => {
|
||||
expect(challenge).toEqual({
|
||||
algorithm: 'SHA-1',
|
||||
challenge: expect.any(String),
|
||||
max: expect.any(Number),
|
||||
salt: expect.any(String),
|
||||
signature: expect.any(String),
|
||||
} satisfies Challenge);
|
||||
@@ -45,6 +52,7 @@ describe('challenge', () => {
|
||||
expect(challenge).toEqual({
|
||||
algorithm: 'SHA-512',
|
||||
challenge: expect.any(String),
|
||||
max: expect.any(Number),
|
||||
salt: expect.any(String),
|
||||
signature: expect.any(String),
|
||||
} satisfies Challenge);
|
||||
@@ -168,4 +176,39 @@ describe('challenge', () => {
|
||||
expect(ok).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('solveChallenge', () => {
|
||||
it('should solve challenge', async () => {
|
||||
const number = 100;
|
||||
const challenge = await createChallenge({
|
||||
number,
|
||||
hmacKey,
|
||||
});
|
||||
const { promise } = solveChallenge(
|
||||
challenge.challenge,
|
||||
challenge.salt,
|
||||
challenge.algorithm
|
||||
);
|
||||
const result = await promise;
|
||||
expect(result?.number).toEqual(number);
|
||||
});
|
||||
});
|
||||
|
||||
describe.skipIf(!('Worker' in globalThis))('solveChallengeWorkers', () => {
|
||||
it('should solve challenge using workers', async () => {
|
||||
const number = 100;
|
||||
const challenge = await createChallenge({
|
||||
number,
|
||||
hmacKey,
|
||||
});
|
||||
const result = await solveChallengeWorkers(
|
||||
'./lib/worker.ts',
|
||||
4,
|
||||
challenge.challenge,
|
||||
challenge.salt,
|
||||
challenge.algorithm
|
||||
);
|
||||
expect(result?.number).toEqual(number);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { assertEquals } from 'https://deno.land/std@0.213.0/assert/mod.ts';
|
||||
import { createChallenge, verifySolution } from '../deno_dist/index.ts';
|
||||
import {
|
||||
createChallenge,
|
||||
verifySolution,
|
||||
solveChallenge,
|
||||
solveChallengeWorkers,
|
||||
} from '../deno_dist/index.ts';
|
||||
|
||||
const hmacKey = 'test';
|
||||
|
||||
@@ -35,3 +40,33 @@ Deno.test('verifySolution()', async (t) => {
|
||||
assertEquals(ok, true);
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test('solveChallenge()', async (t) => {
|
||||
await t.step('should solve challenge', async () => {
|
||||
const number = 100;
|
||||
const challenge = await createChallenge({
|
||||
hmacKey,
|
||||
number,
|
||||
});
|
||||
const result = await solveChallenge(challenge.challenge, challenge.salt)
|
||||
.promise;
|
||||
assertEquals(result?.number, number);
|
||||
});
|
||||
});
|
||||
|
||||
Deno.test('solveChallengeWorkers()', async (t) => {
|
||||
await t.step('should solve challenge', async () => {
|
||||
const number = 100;
|
||||
const challenge = await createChallenge({
|
||||
hmacKey,
|
||||
number,
|
||||
});
|
||||
const result = await solveChallengeWorkers(
|
||||
new URL('../deno_dist/worker.ts', import.meta.url),
|
||||
8,
|
||||
challenge.challenge,
|
||||
challenge.salt
|
||||
);
|
||||
assertEquals(result?.number, number);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user