ribaunt 0.1.6 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/README.md +10 -6
  2. package/dist/cjs/index.d.ts +43 -24
  3. package/dist/cjs/index.d.ts.map +1 -1
  4. package/dist/cjs/index.js +248 -237
  5. package/dist/cjs/index.js.map +1 -1
  6. package/dist/cjs/redis.d.ts +13 -0
  7. package/dist/cjs/redis.d.ts.map +1 -0
  8. package/dist/cjs/redis.js +53 -0
  9. package/dist/cjs/redis.js.map +1 -0
  10. package/dist/index.d.ts +43 -24
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +247 -237
  13. package/dist/index.js.map +1 -1
  14. package/dist/redis.d.ts +13 -0
  15. package/dist/redis.d.ts.map +1 -0
  16. package/dist/redis.js +49 -0
  17. package/dist/redis.js.map +1 -0
  18. package/dist/solver-worker.d.ts +2 -0
  19. package/dist/solver-worker.d.ts.map +1 -0
  20. package/dist/solver-worker.js +15 -0
  21. package/dist/solver-worker.js.map +1 -0
  22. package/dist/solver.d.ts +10 -0
  23. package/dist/solver.d.ts.map +1 -1
  24. package/dist/solver.js +14 -0
  25. package/dist/solver.js.map +1 -1
  26. package/dist/widget-react.d.ts +8 -11
  27. package/dist/widget-react.d.ts.map +1 -1
  28. package/dist/widget-react.js +30 -5
  29. package/dist/widget-react.js.map +1 -1
  30. package/dist/widget.d.ts +30 -1
  31. package/dist/widget.d.ts.map +1 -1
  32. package/dist/widget.js +132 -19
  33. package/dist/widget.js.map +1 -1
  34. package/dist/worker-client.d.ts +8 -0
  35. package/dist/worker-client.d.ts.map +1 -0
  36. package/dist/worker-client.js +78 -0
  37. package/dist/worker-client.js.map +1 -0
  38. package/package.json +23 -2
  39. package/dist/cjs/solver.d.ts +0 -16
  40. package/dist/cjs/solver.d.ts.map +0 -1
  41. package/dist/cjs/solver.js +0 -89
  42. package/dist/cjs/solver.js.map +0 -1
  43. package/dist/cjs/widget-browser.d.ts +0 -7
  44. package/dist/cjs/widget-browser.d.ts.map +0 -1
  45. package/dist/cjs/widget-browser.js +0 -12
  46. package/dist/cjs/widget-browser.js.map +0 -1
  47. package/dist/cjs/widget-react.d.ts +0 -40
  48. package/dist/cjs/widget-react.d.ts.map +0 -1
  49. package/dist/cjs/widget-react.js +0 -182
  50. package/dist/cjs/widget-react.js.map +0 -1
  51. package/dist/cjs/widget.d.ts +0 -97
  52. package/dist/cjs/widget.d.ts.map +0 -1
  53. package/dist/cjs/widget.js +0 -519
  54. package/dist/cjs/widget.js.map +0 -1
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNodeRedisReplayStore = createNodeRedisReplayStore;
4
+ exports.createIORedisReplayStore = createIORedisReplayStore;
5
+ const CONSUME_SCRIPT = `
6
+ for i, key in ipairs(KEYS) do
7
+ if redis.call('EXISTS', key) == 1 then
8
+ return 0
9
+ end
10
+ end
11
+ local ttl = tonumber(ARGV[1])
12
+ for i, key in ipairs(KEYS) do
13
+ redis.call('SET', key, '1', 'PX', ttl)
14
+ end
15
+ return 1
16
+ `;
17
+ function createKeys(jtis, prefix) {
18
+ return jtis.map((jti) => `${prefix}${jti}`);
19
+ }
20
+ function ttlMilliseconds(expiresAt) {
21
+ return Math.max(1, Math.floor((expiresAt * 1000) - Date.now()));
22
+ }
23
+ function resultIsConsumed(result) {
24
+ return result === 1 || result === '1' || result === true;
25
+ }
26
+ class RedisReplayStore {
27
+ constructor(evaluate, prefix) {
28
+ this.evaluate = evaluate;
29
+ this.prefix = prefix;
30
+ }
31
+ consume(jti, expiresAt) {
32
+ return this.consumeMany([jti], expiresAt);
33
+ }
34
+ async consumeMany(jtis, expiresAt) {
35
+ if (jtis.length === 0 || new Set(jtis).size !== jtis.length)
36
+ return false;
37
+ const result = await this.evaluate(createKeys(jtis, this.prefix), ttlMilliseconds(expiresAt));
38
+ return resultIsConsumed(result);
39
+ }
40
+ }
41
+ function createNodeRedisReplayStore(client, options = {}) {
42
+ return new RedisReplayStore((keys, ttlMs) => client.sendCommand([
43
+ 'EVAL',
44
+ CONSUME_SCRIPT,
45
+ String(keys.length),
46
+ ...keys,
47
+ String(ttlMs),
48
+ ]), options.prefix ?? '{ribaunt}:replay:');
49
+ }
50
+ function createIORedisReplayStore(client, options = {}) {
51
+ return new RedisReplayStore((keys, ttlMs) => client.eval(CONSUME_SCRIPT, keys.length, ...keys, ttlMs), options.prefix ?? '{ribaunt}:replay:');
52
+ }
53
+ //# sourceMappingURL=redis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/redis.ts"],"names":[],"mappings":";;AAwDA,gEAcC;AAED,4DAQC;AAlED,MAAM,cAAc,GAAG;;;;;;;;;;;CAWtB,CAAC;AAEF,SAAS,UAAU,CAAC,IAAc,EAAE,MAAc;IAChD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,eAAe,CAAC,SAAiB;IACxC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,gBAAgB,CAAC,MAAe;IACvC,OAAO,MAAM,KAAK,CAAC,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,IAAI,CAAC;AAC3D,CAAC;AAED,MAAM,gBAAgB;IACpB,YACmB,QAA6D,EAC7D,MAAc;QADd,aAAQ,GAAR,QAAQ,CAAqD;QAC7D,WAAM,GAAN,MAAM,CAAQ;IAC9B,CAAC;IAEJ,OAAO,CAAC,GAAW,EAAE,SAAiB;QACpC,OAAO,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,WAAW,CAAC,IAAc,EAAE,SAAiB;QACjD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC,SAAS,CAAC,CAAC,CAAC;QAC9F,OAAO,gBAAgB,CAAC,MAAM,CAAC,CAAC;IAClC,CAAC;CACF;AAED,SAAgB,0BAA0B,CACxC,MAA2B,EAC3B,UAAmC,EAAE;IAErC,OAAO,IAAI,gBAAgB,CACzB,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC;QAClC,MAAM;QACN,cAAc;QACd,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC;QACnB,GAAG,IAAI;QACP,MAAM,CAAC,KAAK,CAAC;KACd,CAAC,EACF,OAAO,CAAC,MAAM,IAAI,mBAAmB,CACtC,CAAC;AACJ,CAAC;AAED,SAAgB,wBAAwB,CACtC,MAAyB,EACzB,UAAmC,EAAE;IAErC,OAAO,IAAI,gBAAgB,CACzB,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,EAAE,KAAK,CAAC,EACzE,OAAO,CAAC,MAAM,IAAI,mBAAmB,CACtC,CAAC;AACJ,CAAC"}
package/dist/index.d.ts CHANGED
@@ -3,22 +3,59 @@ export interface ChallengeSolution {
3
3
  nonce: string;
4
4
  hash: string;
5
5
  }
6
+ export interface ClientCalibration {
7
+ iterations: number;
8
+ durationMs: number;
9
+ }
10
+ export interface WorkloadBounds {
11
+ minDifficulty?: number;
12
+ maxDifficulty?: number;
13
+ minAmount?: number;
14
+ maxAmount?: number;
15
+ }
16
+ export interface AdaptiveWorkloadOptions extends WorkloadBounds {
17
+ riskScore?: number;
18
+ targetDurationMs?: number;
19
+ calibration?: ClientCalibration;
20
+ }
21
+ export interface Workload {
22
+ difficulty: number;
23
+ amount: number;
24
+ estimatedAttempts: number;
25
+ }
26
+ export interface ChallengeOptions {
27
+ difficulty?: number;
28
+ amount?: number;
29
+ ttlSeconds?: number;
30
+ context?: string;
31
+ workload?: Pick<Workload, 'difficulty' | 'amount'>;
32
+ }
6
33
  export interface ReplayStore {
7
34
  consume(jti: string, expiresAt: number): Promise<boolean>;
35
+ consumeMany?(jtis: string[], expiresAt: number): Promise<boolean>;
8
36
  }
9
37
  export type ReplayPreventionMode = 'disabled' | 'local' | 'remote';
10
38
  export interface VerifySolutionOptions {
11
39
  replayPrevention?: ReplayPreventionMode;
12
40
  replayStore?: ReplayStore;
41
+ context?: string;
13
42
  debug?: boolean;
14
43
  onWarning?: (warning: VerifyWarning) => void;
15
44
  }
16
- export type VerifyWarningReason = 'invalid-token' | 'expired-token' | 'invalid-solution' | 'replay-detected' | 'configuration-error';
45
+ export type VerifyFailureReason = 'invalid-token' | 'expired-token' | 'invalid-solution' | 'context-mismatch' | 'replay-detected' | 'configuration-error';
46
+ export type VerifyWarningReason = VerifyFailureReason;
17
47
  export interface VerifyWarning {
18
48
  reason: VerifyWarningReason;
19
49
  message: string;
20
50
  error?: unknown;
21
51
  }
52
+ export type VerifySolutionResult = {
53
+ valid: true;
54
+ } | {
55
+ valid: false;
56
+ reason: VerifyFailureReason;
57
+ message: string;
58
+ };
22
59
  export interface SolveChallengeOptions {
23
60
  maxIterations?: number;
24
61
  maxDurationMs?: number;
@@ -26,34 +63,16 @@ export interface SolveChallengeOptions {
26
63
  export declare class LocalReplayStore implements ReplayStore {
27
64
  private usedTokens;
28
65
  consume(jti: string, expiresAt: number): Promise<boolean>;
66
+ consumeMany(jtis: string[], expiresAt: number): Promise<boolean>;
29
67
  private cleanup;
30
68
  }
31
69
  /**
32
- * Creates one or more PoW challenges and returns them as signed JWT tokens.
33
- *
34
- * @param difficulty - Number of leading zeros required in the hash (default 5)
35
- * @param amount - Number of challenges to create (default 4)
36
- * @param ttlSeconds - Time to live for each challenge in seconds (default 30)
37
- * @returns An array of JWT challenge tokens
70
+ * Selects bounded proof-of-work using a server-owned risk floor and untrusted timing calibration.
38
71
  */
72
+ export declare function selectWorkload(options?: AdaptiveWorkloadOptions): Workload;
39
73
  export declare function createChallenge(difficulty?: number, amount?: number, ttlSeconds?: number): ChallengeToken[];
40
- /**
41
- * Solves one or more PoW challenges encoded in JWT tokens.
42
- *
43
- * @param token - The JWT challenge token or an array of tokens
44
- * @param options - Optional guardrails for test/debug usage (`maxIterations`, `maxDurationMs`)
45
- * @returns The nonce/hash pair for single input or an array of them for multiple tokens
46
- */
47
- export declare function solveChallenge(token: ChallengeToken): ChallengeSolution | undefined;
74
+ export declare function createChallenge(options: ChallengeOptions): ChallengeToken[];
48
75
  export declare function solveChallenge(token: ChallengeToken, options?: SolveChallengeOptions): ChallengeSolution | undefined;
49
- export declare function solveChallenge(token: ChallengeToken[]): ChallengeSolution[] | undefined;
50
76
  export declare function solveChallenge(token: ChallengeToken[], options?: SolveChallengeOptions): ChallengeSolution[] | undefined;
51
- /**
52
- * Verifies a PoW solution returned by the client.
53
- *
54
- * @param token - The original JWT issued as the challenge (single token or array of tokens)
55
- * @param nonce - The nonce/answer submitted by the client (single nonce, array of nonces, or array of solution objects)
56
- * @returns true only if every provided solution is valid; otherwise false
57
- */
58
- export declare function verifySolution(token: ChallengeToken | ChallengeToken[], nonce: number | string | Array<number | string> | ChallengeSolution | ChallengeSolution[], options?: VerifySolutionOptions): Promise<boolean>;
77
+ export declare function verifySolution(token: ChallengeToken | ChallengeToken[], nonce: number | string | Array<number | string> | ChallengeSolution | ChallengeSolution[], options?: VerifySolutionOptions): Promise<VerifySolutionResult>;
59
78
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC;AAEpC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,WAAW;IAC1B,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3D;AAED,MAAM,MAAM,oBAAoB,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEnE,MAAM,WAAW,qBAAqB;IACpC,gBAAgB,CAAC,EAAE,oBAAoB,CAAC;IACxC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAC;CAC9C;AAED,MAAM,MAAM,mBAAmB,GAC3B,eAAe,GACf,eAAe,GACf,kBAAkB,GAClB,iBAAiB,GACjB,qBAAqB,CAAC;AAE1B,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,gBAAiB,YAAW,WAAW;IAClD,OAAO,CAAC,UAAU,CAA6B;IAEzC,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAW/D,OAAO,CAAC,OAAO;CAShB;AA0LD;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,UAAU,GAAE,MAAU,EACtB,MAAM,GAAE,MAAU,EAClB,UAAU,GAAE,MAAW,GACtB,cAAc,EAAE,CAUlB;AA0FD;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,GAAG,iBAAiB,GAAG,SAAS,CAAC;AACrF,wBAAgB,cAAc,CAC5B,KAAK,EAAE,cAAc,EACrB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,iBAAiB,GAAG,SAAS,CAAC;AACjC,wBAAgB,cAAc,CAAC,KAAK,EAAE,cAAc,EAAE,GAAG,iBAAiB,EAAE,GAAG,SAAS,CAAC;AACzF,wBAAgB,cAAc,CAC5B,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,CAAC,EAAE,qBAAqB,GAC9B,iBAAiB,EAAE,GAAG,SAAS,CAAC;AAwBnC;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,cAAc,GAAG,cAAc,EAAE,EACxC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,iBAAiB,GAAG,iBAAiB,EAAE,EACzF,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,OAAO,CAAC,CAuDlB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}