@teneo-protocol/sdk 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +14 -0
- package/.env.test.example +14 -0
- package/.eslintrc.json +26 -0
- package/.github/workflows/claude-code-review.yml +78 -0
- package/.github/workflows/claude-reviewer.yml +64 -0
- package/.github/workflows/publish-npm.yml +38 -0
- package/.github/workflows/push-to-main.yml +23 -0
- package/.node-version +1 -0
- package/.prettierrc +11 -0
- package/Dockerfile +25 -0
- package/LICENCE +661 -0
- package/README.md +709 -0
- package/dist/constants.d.ts +42 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +45 -0
- package/dist/constants.js.map +1 -0
- package/dist/core/websocket-client.d.ts +261 -0
- package/dist/core/websocket-client.d.ts.map +1 -0
- package/dist/core/websocket-client.js +875 -0
- package/dist/core/websocket-client.js.map +1 -0
- package/dist/formatters/response-formatter.d.ts +354 -0
- package/dist/formatters/response-formatter.d.ts.map +1 -0
- package/dist/formatters/response-formatter.js +575 -0
- package/dist/formatters/response-formatter.js.map +1 -0
- package/dist/handlers/message-handler-registry.d.ts +155 -0
- package/dist/handlers/message-handler-registry.d.ts.map +1 -0
- package/dist/handlers/message-handler-registry.js +216 -0
- package/dist/handlers/message-handler-registry.js.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts +112 -0
- package/dist/handlers/message-handlers/agent-selected-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js +40 -0
- package/dist/handlers/message-handlers/agent-selected-handler.js.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/agents-list-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/agents-list-handler.js +25 -0
- package/dist/handlers/message-handlers/agents-list-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts +71 -0
- package/dist/handlers/message-handlers/auth-error-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-error-handler.js +30 -0
- package/dist/handlers/message-handlers/auth-error-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-message-handler.js +60 -0
- package/dist/handlers/message-handlers/auth-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/auth-required-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-required-handler.js +23 -0
- package/dist/handlers/message-handlers/auth-required-handler.js.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts +18 -0
- package/dist/handlers/message-handlers/auth-success-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/auth-success-handler.js +51 -0
- package/dist/handlers/message-handlers/auth-success-handler.js.map +1 -0
- package/dist/handlers/message-handlers/base-handler.d.ts +55 -0
- package/dist/handlers/message-handlers/base-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/base-handler.js +83 -0
- package/dist/handlers/message-handlers/base-handler.js.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts +73 -0
- package/dist/handlers/message-handlers/challenge-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/challenge-handler.js +47 -0
- package/dist/handlers/message-handlers/challenge-handler.js.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts +76 -0
- package/dist/handlers/message-handlers/error-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/error-message-handler.js +29 -0
- package/dist/handlers/message-handlers/error-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/index.d.ts +28 -0
- package/dist/handlers/message-handlers/index.d.ts.map +1 -0
- package/dist/handlers/message-handlers/index.js +100 -0
- package/dist/handlers/message-handlers/index.js.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts +122 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js +30 -0
- package/dist/handlers/message-handlers/list-rooms-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts +104 -0
- package/dist/handlers/message-handlers/ping-pong-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js +36 -0
- package/dist/handlers/message-handlers/ping-pong-handler.js.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts +56 -0
- package/dist/handlers/message-handlers/regular-message-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/regular-message-handler.js +59 -0
- package/dist/handlers/message-handlers/regular-message-handler.js.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/subscribe-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts +14 -0
- package/dist/handlers/message-handlers/task-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/task-response-handler.js +44 -0
- package/dist/handlers/message-handlers/task-response-handler.js.map +1 -0
- package/dist/handlers/message-handlers/types.d.ts +51 -0
- package/dist/handlers/message-handlers/types.d.ts.map +1 -0
- package/dist/handlers/message-handlers/types.js +7 -0
- package/dist/handlers/message-handlers/types.js.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts +81 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.d.ts.map +1 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js +48 -0
- package/dist/handlers/message-handlers/unsubscribe-response-handler.js.map +1 -0
- package/dist/handlers/webhook-handler.d.ts +202 -0
- package/dist/handlers/webhook-handler.d.ts.map +1 -0
- package/dist/handlers/webhook-handler.js +511 -0
- package/dist/handlers/webhook-handler.js.map +1 -0
- package/dist/index.d.ts +71 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +217 -0
- package/dist/index.js.map +1 -0
- package/dist/managers/agent-registry.d.ts +173 -0
- package/dist/managers/agent-registry.d.ts.map +1 -0
- package/dist/managers/agent-registry.js +310 -0
- package/dist/managers/agent-registry.js.map +1 -0
- package/dist/managers/connection-manager.d.ts +134 -0
- package/dist/managers/connection-manager.d.ts.map +1 -0
- package/dist/managers/connection-manager.js +176 -0
- package/dist/managers/connection-manager.js.map +1 -0
- package/dist/managers/index.d.ts +9 -0
- package/dist/managers/index.d.ts.map +1 -0
- package/dist/managers/index.js +16 -0
- package/dist/managers/index.js.map +1 -0
- package/dist/managers/message-router.d.ts +112 -0
- package/dist/managers/message-router.d.ts.map +1 -0
- package/dist/managers/message-router.js +260 -0
- package/dist/managers/message-router.js.map +1 -0
- package/dist/managers/room-manager.d.ts +165 -0
- package/dist/managers/room-manager.d.ts.map +1 -0
- package/dist/managers/room-manager.js +227 -0
- package/dist/managers/room-manager.js.map +1 -0
- package/dist/teneo-sdk.d.ts +703 -0
- package/dist/teneo-sdk.d.ts.map +1 -0
- package/dist/teneo-sdk.js +907 -0
- package/dist/teneo-sdk.js.map +1 -0
- package/dist/types/config.d.ts +1047 -0
- package/dist/types/config.d.ts.map +1 -0
- package/dist/types/config.js +720 -0
- package/dist/types/config.js.map +1 -0
- package/dist/types/error-codes.d.ts +29 -0
- package/dist/types/error-codes.d.ts.map +1 -0
- package/dist/types/error-codes.js +41 -0
- package/dist/types/error-codes.js.map +1 -0
- package/dist/types/events.d.ts +616 -0
- package/dist/types/events.d.ts.map +1 -0
- package/dist/types/events.js +261 -0
- package/dist/types/events.js.map +1 -0
- package/dist/types/health.d.ts +40 -0
- package/dist/types/health.d.ts.map +1 -0
- package/dist/types/health.js +6 -0
- package/dist/types/health.js.map +1 -0
- package/dist/types/index.d.ts +10 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +123 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/messages.d.ts +3734 -0
- package/dist/types/messages.d.ts.map +1 -0
- package/dist/types/messages.js +482 -0
- package/dist/types/messages.js.map +1 -0
- package/dist/types/validation.d.ts +81 -0
- package/dist/types/validation.d.ts.map +1 -0
- package/dist/types/validation.js +115 -0
- package/dist/types/validation.js.map +1 -0
- package/dist/utils/bounded-queue.d.ts +127 -0
- package/dist/utils/bounded-queue.d.ts.map +1 -0
- package/dist/utils/bounded-queue.js +181 -0
- package/dist/utils/bounded-queue.js.map +1 -0
- package/dist/utils/circuit-breaker.d.ts +141 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +215 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/deduplication-cache.d.ts +110 -0
- package/dist/utils/deduplication-cache.d.ts.map +1 -0
- package/dist/utils/deduplication-cache.js +177 -0
- package/dist/utils/deduplication-cache.js.map +1 -0
- package/dist/utils/event-waiter.d.ts +101 -0
- package/dist/utils/event-waiter.d.ts.map +1 -0
- package/dist/utils/event-waiter.js +118 -0
- package/dist/utils/event-waiter.js.map +1 -0
- package/dist/utils/index.d.ts +51 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +72 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +22 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +91 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/rate-limiter.d.ts +122 -0
- package/dist/utils/rate-limiter.d.ts.map +1 -0
- package/dist/utils/rate-limiter.js +190 -0
- package/dist/utils/rate-limiter.js.map +1 -0
- package/dist/utils/retry-policy.d.ts +191 -0
- package/dist/utils/retry-policy.d.ts.map +1 -0
- package/dist/utils/retry-policy.js +225 -0
- package/dist/utils/retry-policy.js.map +1 -0
- package/dist/utils/secure-private-key.d.ts +113 -0
- package/dist/utils/secure-private-key.d.ts.map +1 -0
- package/dist/utils/secure-private-key.js +188 -0
- package/dist/utils/secure-private-key.js.map +1 -0
- package/dist/utils/signature-verifier.d.ts +143 -0
- package/dist/utils/signature-verifier.d.ts.map +1 -0
- package/dist/utils/signature-verifier.js +238 -0
- package/dist/utils/signature-verifier.js.map +1 -0
- package/dist/utils/ssrf-validator.d.ts +36 -0
- package/dist/utils/ssrf-validator.d.ts.map +1 -0
- package/dist/utils/ssrf-validator.js +195 -0
- package/dist/utils/ssrf-validator.js.map +1 -0
- package/examples/.env.example +17 -0
- package/examples/basic-usage.ts +211 -0
- package/examples/production-dashboard/.env.example +153 -0
- package/examples/production-dashboard/package.json +39 -0
- package/examples/production-dashboard/public/dashboard.html +642 -0
- package/examples/production-dashboard/server.ts +753 -0
- package/examples/webhook-integration.ts +239 -0
- package/examples/x-influencer-battle-redesign.html +1065 -0
- package/examples/x-influencer-battle-server.ts +217 -0
- package/examples/x-influencer-battle.html +787 -0
- package/package.json +65 -0
- package/src/constants.ts +43 -0
- package/src/core/websocket-client.test.ts +512 -0
- package/src/core/websocket-client.ts +1056 -0
- package/src/formatters/response-formatter.test.ts +571 -0
- package/src/formatters/response-formatter.ts +677 -0
- package/src/handlers/message-handler-registry.ts +239 -0
- package/src/handlers/message-handlers/agent-selected-handler.ts +40 -0
- package/src/handlers/message-handlers/agents-list-handler.ts +26 -0
- package/src/handlers/message-handlers/auth-error-handler.ts +31 -0
- package/src/handlers/message-handlers/auth-message-handler.ts +66 -0
- package/src/handlers/message-handlers/auth-required-handler.ts +23 -0
- package/src/handlers/message-handlers/auth-success-handler.ts +57 -0
- package/src/handlers/message-handlers/base-handler.ts +101 -0
- package/src/handlers/message-handlers/challenge-handler.ts +57 -0
- package/src/handlers/message-handlers/error-message-handler.ts +27 -0
- package/src/handlers/message-handlers/index.ts +77 -0
- package/src/handlers/message-handlers/list-rooms-response-handler.ts +28 -0
- package/src/handlers/message-handlers/ping-pong-handler.ts +30 -0
- package/src/handlers/message-handlers/regular-message-handler.ts +65 -0
- package/src/handlers/message-handlers/subscribe-response-handler.ts +47 -0
- package/src/handlers/message-handlers/task-response-handler.ts +45 -0
- package/src/handlers/message-handlers/types.ts +77 -0
- package/src/handlers/message-handlers/unsubscribe-response-handler.ts +47 -0
- package/src/handlers/webhook-handler.test.ts +789 -0
- package/src/handlers/webhook-handler.ts +576 -0
- package/src/index.ts +269 -0
- package/src/managers/agent-registry.test.ts +466 -0
- package/src/managers/agent-registry.ts +347 -0
- package/src/managers/connection-manager.ts +195 -0
- package/src/managers/index.ts +9 -0
- package/src/managers/message-router.ts +349 -0
- package/src/managers/room-manager.ts +248 -0
- package/src/teneo-sdk.ts +1022 -0
- package/src/types/config.test.ts +325 -0
- package/src/types/config.ts +799 -0
- package/src/types/error-codes.ts +44 -0
- package/src/types/events.test.ts +302 -0
- package/src/types/events.ts +382 -0
- package/src/types/health.ts +46 -0
- package/src/types/index.ts +199 -0
- package/src/types/messages.test.ts +660 -0
- package/src/types/messages.ts +570 -0
- package/src/types/validation.ts +123 -0
- package/src/utils/bounded-queue.test.ts +356 -0
- package/src/utils/bounded-queue.ts +205 -0
- package/src/utils/circuit-breaker.test.ts +394 -0
- package/src/utils/circuit-breaker.ts +262 -0
- package/src/utils/deduplication-cache.test.ts +380 -0
- package/src/utils/deduplication-cache.ts +198 -0
- package/src/utils/event-waiter.test.ts +381 -0
- package/src/utils/event-waiter.ts +172 -0
- package/src/utils/index.ts +74 -0
- package/src/utils/logger.ts +87 -0
- package/src/utils/rate-limiter.test.ts +341 -0
- package/src/utils/rate-limiter.ts +211 -0
- package/src/utils/retry-policy.test.ts +558 -0
- package/src/utils/retry-policy.ts +272 -0
- package/src/utils/secure-private-key.test.ts +356 -0
- package/src/utils/secure-private-key.ts +205 -0
- package/src/utils/signature-verifier.test.ts +464 -0
- package/src/utils/signature-verifier.ts +298 -0
- package/src/utils/ssrf-validator.test.ts +372 -0
- package/src/utils/ssrf-validator.ts +224 -0
- package/tests/integration/real-server.test.ts +740 -0
- package/tests/integration/websocket.test.ts +381 -0
- package/tests/integration-setup.ts +16 -0
- package/tests/setup.ts +34 -0
- package/tsconfig.json +32 -0
- package/vitest.config.ts +42 -0
- package/vitest.integration.config.ts +23 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RetryPolicy - Configurable retry strategy for connection and webhook retries
|
|
3
|
+
* Supports exponential, linear, and constant backoff strategies
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Retry strategy types
|
|
10
|
+
*/
|
|
11
|
+
export type RetryStrategyType = "exponential" | "linear" | "constant";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Retry strategy configuration
|
|
15
|
+
*/
|
|
16
|
+
export interface RetryStrategy {
|
|
17
|
+
/** Strategy type: exponential, linear, or constant */
|
|
18
|
+
type: RetryStrategyType;
|
|
19
|
+
/** Base delay in milliseconds (first retry delay) */
|
|
20
|
+
baseDelay: number;
|
|
21
|
+
/** Maximum delay cap in milliseconds */
|
|
22
|
+
maxDelay: number;
|
|
23
|
+
/** Maximum number of retry attempts (0 = no retries) */
|
|
24
|
+
maxAttempts: number;
|
|
25
|
+
/** Add random jitter to prevent thundering herd */
|
|
26
|
+
jitter: boolean;
|
|
27
|
+
/** Backoff multiplier for exponential strategy (default: 2) */
|
|
28
|
+
backoffMultiplier?: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Zod schema for retry strategy validation
|
|
33
|
+
*/
|
|
34
|
+
export const RetryStrategySchema = z.object({
|
|
35
|
+
type: z.enum(["exponential", "linear", "constant"]),
|
|
36
|
+
baseDelay: z.number().min(0).max(300000), // 0-5 minutes
|
|
37
|
+
maxDelay: z.number().min(0).max(3600000), // 0-1 hour
|
|
38
|
+
maxAttempts: z.number().min(0).max(1000),
|
|
39
|
+
jitter: z.boolean(),
|
|
40
|
+
backoffMultiplier: z.number().min(1).max(10).optional()
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* RetryPolicy - Calculates retry delays and determines retry eligibility
|
|
45
|
+
*
|
|
46
|
+
* Supports three retry strategies:
|
|
47
|
+
* - **Exponential**: Delay increases exponentially (e.g., 1s, 2s, 4s, 8s, ...)
|
|
48
|
+
* - **Linear**: Delay increases linearly (e.g., 1s, 2s, 3s, 4s, ...)
|
|
49
|
+
* - **Constant**: Delay remains constant (e.g., 1s, 1s, 1s, 1s, ...)
|
|
50
|
+
*
|
|
51
|
+
* All strategies respect maxDelay cap and support optional jitter.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```typescript
|
|
55
|
+
* // Exponential backoff with default multiplier of 2
|
|
56
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 10, true);
|
|
57
|
+
* console.log(policy.calculateDelay(1)); // ~1000ms
|
|
58
|
+
* console.log(policy.calculateDelay(2)); // ~2000ms
|
|
59
|
+
* console.log(policy.calculateDelay(3)); // ~4000ms
|
|
60
|
+
*
|
|
61
|
+
* // Linear backoff
|
|
62
|
+
* const policy = RetryPolicy.linear(2000, 30000, 5, false);
|
|
63
|
+
* console.log(policy.calculateDelay(1)); // 2000ms
|
|
64
|
+
* console.log(policy.calculateDelay(2)); // 4000ms
|
|
65
|
+
* console.log(policy.calculateDelay(3)); // 6000ms
|
|
66
|
+
*
|
|
67
|
+
* // Constant delay
|
|
68
|
+
* const policy = RetryPolicy.constant(5000, 3);
|
|
69
|
+
* console.log(policy.calculateDelay(1)); // 5000ms
|
|
70
|
+
* console.log(policy.calculateDelay(2)); // 5000ms
|
|
71
|
+
* console.log(policy.calculateDelay(3)); // 5000ms
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export class RetryPolicy {
|
|
75
|
+
private readonly strategy: RetryStrategy;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Creates a new retry policy with the specified strategy
|
|
79
|
+
*
|
|
80
|
+
* @param strategy - Retry strategy configuration
|
|
81
|
+
* @throws {z.ZodError} If strategy is invalid
|
|
82
|
+
*/
|
|
83
|
+
constructor(strategy: RetryStrategy) {
|
|
84
|
+
// Validate strategy with Zod
|
|
85
|
+
this.strategy = RetryStrategySchema.parse(strategy);
|
|
86
|
+
|
|
87
|
+
// Additional validation: maxDelay must be >= baseDelay
|
|
88
|
+
if (this.strategy.maxDelay < this.strategy.baseDelay) {
|
|
89
|
+
throw new Error("maxDelay must be greater than or equal to baseDelay");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Calculates the retry delay for a given attempt number
|
|
95
|
+
*
|
|
96
|
+
* @param attempt - Attempt number (1-indexed: 1 = first retry)
|
|
97
|
+
* @returns Delay in milliseconds before next retry
|
|
98
|
+
*
|
|
99
|
+
* @example
|
|
100
|
+
* ```typescript
|
|
101
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 10, true);
|
|
102
|
+
*
|
|
103
|
+
* // First retry after 1st failure
|
|
104
|
+
* policy.calculateDelay(1); // ~1000ms (+ jitter if enabled)
|
|
105
|
+
*
|
|
106
|
+
* // Second retry after 2nd failure
|
|
107
|
+
* policy.calculateDelay(2); // ~2000ms (+ jitter if enabled)
|
|
108
|
+
* ```
|
|
109
|
+
*/
|
|
110
|
+
public calculateDelay(attempt: number): number {
|
|
111
|
+
// Validate attempt number
|
|
112
|
+
if (attempt < 1) {
|
|
113
|
+
throw new Error("Attempt number must be >= 1");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
let delay: number;
|
|
117
|
+
|
|
118
|
+
switch (this.strategy.type) {
|
|
119
|
+
case "exponential": {
|
|
120
|
+
const multiplier = this.strategy.backoffMultiplier || 2;
|
|
121
|
+
delay = this.strategy.baseDelay * Math.pow(multiplier, attempt - 1);
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
case "linear": {
|
|
125
|
+
delay = this.strategy.baseDelay * attempt;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case "constant": {
|
|
129
|
+
delay = this.strategy.baseDelay;
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// Cap at maxDelay
|
|
135
|
+
delay = Math.min(delay, this.strategy.maxDelay);
|
|
136
|
+
|
|
137
|
+
// Add jitter if enabled
|
|
138
|
+
if (this.strategy.jitter) {
|
|
139
|
+
// Add random jitter between 0 and 1000ms
|
|
140
|
+
delay += Math.random() * 1000;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return Math.floor(delay);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Determines if a retry should be attempted based on attempt count
|
|
148
|
+
*
|
|
149
|
+
* @param attempt - Current attempt number (1-indexed)
|
|
150
|
+
* @returns True if retry should be attempted, false otherwise
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* ```typescript
|
|
154
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 3, false);
|
|
155
|
+
*
|
|
156
|
+
* policy.shouldRetry(1); // true
|
|
157
|
+
* policy.shouldRetry(2); // true
|
|
158
|
+
* policy.shouldRetry(3); // true
|
|
159
|
+
* policy.shouldRetry(4); // false - exceeded maxAttempts
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
public shouldRetry(attempt: number): boolean {
|
|
163
|
+
return attempt <= this.strategy.maxAttempts;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Gets the retry strategy configuration
|
|
168
|
+
*
|
|
169
|
+
* @returns Copy of the retry strategy
|
|
170
|
+
*/
|
|
171
|
+
public getStrategy(): Readonly<RetryStrategy> {
|
|
172
|
+
return { ...this.strategy };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Static factory methods for common patterns
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Creates a retry policy with exponential backoff
|
|
179
|
+
*
|
|
180
|
+
* Delay formula: `min(baseDelay * multiplier^(attempt-1), maxDelay) + jitter`
|
|
181
|
+
*
|
|
182
|
+
* @param baseDelay - Initial delay in milliseconds
|
|
183
|
+
* @param maxDelay - Maximum delay cap in milliseconds
|
|
184
|
+
* @param maxAttempts - Maximum retry attempts
|
|
185
|
+
* @param jitter - Add random jitter (0-1000ms)
|
|
186
|
+
* @param backoffMultiplier - Exponential multiplier (default: 2)
|
|
187
|
+
* @returns RetryPolicy instance with exponential strategy
|
|
188
|
+
*
|
|
189
|
+
* @example
|
|
190
|
+
* ```typescript
|
|
191
|
+
* // Standard exponential backoff (2^n)
|
|
192
|
+
* const policy = RetryPolicy.exponential(1000, 60000, 10, true);
|
|
193
|
+
*
|
|
194
|
+
* // Faster exponential backoff (3^n)
|
|
195
|
+
* const aggressive = RetryPolicy.exponential(1000, 60000, 10, true, 3);
|
|
196
|
+
* ```
|
|
197
|
+
*/
|
|
198
|
+
public static exponential(
|
|
199
|
+
baseDelay: number,
|
|
200
|
+
maxDelay: number,
|
|
201
|
+
maxAttempts: number,
|
|
202
|
+
jitter: boolean,
|
|
203
|
+
backoffMultiplier: number = 2
|
|
204
|
+
): RetryPolicy {
|
|
205
|
+
return new RetryPolicy({
|
|
206
|
+
type: "exponential",
|
|
207
|
+
baseDelay,
|
|
208
|
+
maxDelay,
|
|
209
|
+
maxAttempts,
|
|
210
|
+
jitter,
|
|
211
|
+
backoffMultiplier
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Creates a retry policy with linear backoff
|
|
217
|
+
*
|
|
218
|
+
* Delay formula: `min(baseDelay * attempt, maxDelay) + jitter`
|
|
219
|
+
*
|
|
220
|
+
* @param baseDelay - Delay increment per attempt in milliseconds
|
|
221
|
+
* @param maxDelay - Maximum delay cap in milliseconds
|
|
222
|
+
* @param maxAttempts - Maximum retry attempts
|
|
223
|
+
* @param jitter - Add random jitter (0-1000ms)
|
|
224
|
+
* @returns RetryPolicy instance with linear strategy
|
|
225
|
+
*
|
|
226
|
+
* @example
|
|
227
|
+
* ```typescript
|
|
228
|
+
* // Linear backoff: 2s, 4s, 6s, 8s, ...
|
|
229
|
+
* const policy = RetryPolicy.linear(2000, 30000, 10, false);
|
|
230
|
+
* ```
|
|
231
|
+
*/
|
|
232
|
+
public static linear(
|
|
233
|
+
baseDelay: number,
|
|
234
|
+
maxDelay: number,
|
|
235
|
+
maxAttempts: number,
|
|
236
|
+
jitter: boolean
|
|
237
|
+
): RetryPolicy {
|
|
238
|
+
return new RetryPolicy({
|
|
239
|
+
type: "linear",
|
|
240
|
+
baseDelay,
|
|
241
|
+
maxDelay,
|
|
242
|
+
maxAttempts,
|
|
243
|
+
jitter
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Creates a retry policy with constant delay
|
|
249
|
+
*
|
|
250
|
+
* Delay formula: `baseDelay + jitter`
|
|
251
|
+
*
|
|
252
|
+
* @param delay - Constant delay in milliseconds
|
|
253
|
+
* @param maxAttempts - Maximum retry attempts
|
|
254
|
+
* @param jitter - Add random jitter (0-1000ms, default: false)
|
|
255
|
+
* @returns RetryPolicy instance with constant strategy
|
|
256
|
+
*
|
|
257
|
+
* @example
|
|
258
|
+
* ```typescript
|
|
259
|
+
* // Retry every 5 seconds, max 3 times
|
|
260
|
+
* const policy = RetryPolicy.constant(5000, 3);
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
263
|
+
public static constant(delay: number, maxAttempts: number, jitter: boolean = false): RetryPolicy {
|
|
264
|
+
return new RetryPolicy({
|
|
265
|
+
type: "constant",
|
|
266
|
+
baseDelay: delay,
|
|
267
|
+
maxDelay: delay,
|
|
268
|
+
maxAttempts,
|
|
269
|
+
jitter
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for SecurePrivateKey class
|
|
3
|
+
* Verifies secure encryption, decryption, memory cleanup, and integration with viem
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { SecurePrivateKey } from './secure-private-key';
|
|
7
|
+
import { privateKeyToAccount } from 'viem/accounts';
|
|
8
|
+
|
|
9
|
+
describe('SecurePrivateKey', () => {
|
|
10
|
+
// Test private key (do NOT use in production)
|
|
11
|
+
const testPrivateKey = '0x1234567890123456789012345678901234567890123456789012345678901234';
|
|
12
|
+
|
|
13
|
+
describe('constructor', () => {
|
|
14
|
+
it('should create instance with valid private key', () => {
|
|
15
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
16
|
+
expect(secureKey).toBeInstanceOf(SecurePrivateKey);
|
|
17
|
+
expect(secureKey.isDestroyed()).toBe(false);
|
|
18
|
+
secureKey.destroy();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('should throw error with empty private key', () => {
|
|
22
|
+
expect(() => new SecurePrivateKey('')).toThrow('Private key must be a non-empty string');
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('should throw error with null private key', () => {
|
|
26
|
+
expect(() => new SecurePrivateKey(null as any)).toThrow('Private key must be a non-empty string');
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('should throw error with undefined private key', () => {
|
|
30
|
+
expect(() => new SecurePrivateKey(undefined as any)).toThrow(
|
|
31
|
+
'Private key must be a non-empty string'
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should throw error with non-string private key', () => {
|
|
36
|
+
expect(() => new SecurePrivateKey(123 as any)).toThrow('Private key must be a non-empty string');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('use()', () => {
|
|
41
|
+
it('should decrypt and pass key to callback', () => {
|
|
42
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
43
|
+
|
|
44
|
+
const result = secureKey.use((key) => {
|
|
45
|
+
expect(key).toBe(testPrivateKey);
|
|
46
|
+
return 'success';
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
expect(result).toBe('success');
|
|
50
|
+
secureKey.destroy();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('should return callback result', () => {
|
|
54
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
55
|
+
|
|
56
|
+
const result = secureKey.use((key) => {
|
|
57
|
+
return { key: key.substring(0, 10), length: key.length };
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
expect(result).toEqual({ key: testPrivateKey.substring(0, 10), length: testPrivateKey.length });
|
|
61
|
+
secureKey.destroy();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should work with async callbacks', async () => {
|
|
65
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
66
|
+
|
|
67
|
+
const result = await secureKey.use(async (key) => {
|
|
68
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
69
|
+
return key.length;
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
expect(result).toBe(testPrivateKey.length);
|
|
73
|
+
secureKey.destroy();
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should allow multiple uses', () => {
|
|
77
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
78
|
+
|
|
79
|
+
const result1 = secureKey.use((key) => key.length);
|
|
80
|
+
const result2 = secureKey.use((key) => key.substring(0, 5));
|
|
81
|
+
const result3 = secureKey.use((key) => key);
|
|
82
|
+
|
|
83
|
+
expect(result1).toBe(testPrivateKey.length);
|
|
84
|
+
expect(result2).toBe(testPrivateKey.substring(0, 5));
|
|
85
|
+
expect(result3).toBe(testPrivateKey);
|
|
86
|
+
|
|
87
|
+
secureKey.destroy();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('should throw if key has been destroyed', () => {
|
|
91
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
92
|
+
secureKey.destroy();
|
|
93
|
+
|
|
94
|
+
expect(() => {
|
|
95
|
+
secureKey.use((key) => key);
|
|
96
|
+
}).toThrow('SecurePrivateKey has been destroyed and can no longer be used');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('should propagate errors from callback', () => {
|
|
100
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
101
|
+
|
|
102
|
+
expect(() => {
|
|
103
|
+
secureKey.use((key) => {
|
|
104
|
+
throw new Error('Test error');
|
|
105
|
+
});
|
|
106
|
+
}).toThrow('Test error');
|
|
107
|
+
|
|
108
|
+
secureKey.destroy();
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should clean up even if callback throws', () => {
|
|
112
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
secureKey.use((key) => {
|
|
116
|
+
throw new Error('Test error');
|
|
117
|
+
});
|
|
118
|
+
} catch (error) {
|
|
119
|
+
// Expected
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Key should still be usable after error
|
|
123
|
+
const result = secureKey.use((key) => key.length);
|
|
124
|
+
expect(result).toBe(testPrivateKey.length);
|
|
125
|
+
|
|
126
|
+
secureKey.destroy();
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
describe('destroy()', () => {
|
|
131
|
+
it('should mark instance as destroyed', () => {
|
|
132
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
133
|
+
expect(secureKey.isDestroyed()).toBe(false);
|
|
134
|
+
|
|
135
|
+
secureKey.destroy();
|
|
136
|
+
expect(secureKey.isDestroyed()).toBe(true);
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it('should be idempotent (safe to call multiple times)', () => {
|
|
140
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
141
|
+
|
|
142
|
+
secureKey.destroy();
|
|
143
|
+
secureKey.destroy();
|
|
144
|
+
secureKey.destroy();
|
|
145
|
+
|
|
146
|
+
expect(secureKey.isDestroyed()).toBe(true);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should prevent further use after destruction', () => {
|
|
150
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
151
|
+
secureKey.destroy();
|
|
152
|
+
|
|
153
|
+
expect(() => {
|
|
154
|
+
secureKey.use((key) => key);
|
|
155
|
+
}).toThrow('SecurePrivateKey has been destroyed');
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('isDestroyed()', () => {
|
|
160
|
+
it('should return false for new instance', () => {
|
|
161
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
162
|
+
expect(secureKey.isDestroyed()).toBe(false);
|
|
163
|
+
secureKey.destroy();
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('should return true after destruction', () => {
|
|
167
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
168
|
+
secureKey.destroy();
|
|
169
|
+
expect(secureKey.isDestroyed()).toBe(true);
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('encryption/decryption', () => {
|
|
174
|
+
it('should correctly encrypt and decrypt private key', () => {
|
|
175
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
176
|
+
|
|
177
|
+
const decrypted = secureKey.use((key) => key);
|
|
178
|
+
expect(decrypted).toBe(testPrivateKey);
|
|
179
|
+
|
|
180
|
+
secureKey.destroy();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('should handle keys with special characters', () => {
|
|
184
|
+
const specialKey = '0xABCDEF123456!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
185
|
+
const secureKey = new SecurePrivateKey(specialKey);
|
|
186
|
+
|
|
187
|
+
const decrypted = secureKey.use((key) => key);
|
|
188
|
+
expect(decrypted).toBe(specialKey);
|
|
189
|
+
|
|
190
|
+
secureKey.destroy();
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
it('should handle very long keys', () => {
|
|
194
|
+
const longKey = '0x' + 'a'.repeat(1000);
|
|
195
|
+
const secureKey = new SecurePrivateKey(longKey);
|
|
196
|
+
|
|
197
|
+
const decrypted = secureKey.use((key) => key);
|
|
198
|
+
expect(decrypted).toBe(longKey);
|
|
199
|
+
|
|
200
|
+
secureKey.destroy();
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('should produce different encrypted data for same key (random IV)', () => {
|
|
204
|
+
const secureKey1 = new SecurePrivateKey(testPrivateKey);
|
|
205
|
+
const secureKey2 = new SecurePrivateKey(testPrivateKey);
|
|
206
|
+
|
|
207
|
+
// Access private encrypted buffers via any to verify they're different
|
|
208
|
+
const encrypted1 = (secureKey1 as any).encrypted;
|
|
209
|
+
const encrypted2 = (secureKey2 as any).encrypted;
|
|
210
|
+
|
|
211
|
+
// Encrypted data should be different due to random IV
|
|
212
|
+
expect(Buffer.compare(encrypted1, encrypted2)).not.toBe(0);
|
|
213
|
+
|
|
214
|
+
// But decrypted should be the same
|
|
215
|
+
const decrypted1 = secureKey1.use((key) => key);
|
|
216
|
+
const decrypted2 = secureKey2.use((key) => key);
|
|
217
|
+
|
|
218
|
+
expect(decrypted1).toBe(testPrivateKey);
|
|
219
|
+
expect(decrypted2).toBe(testPrivateKey);
|
|
220
|
+
|
|
221
|
+
secureKey1.destroy();
|
|
222
|
+
secureKey2.destroy();
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
describe('integration with viem', () => {
|
|
227
|
+
it('should work with privateKeyToAccount', () => {
|
|
228
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
229
|
+
|
|
230
|
+
const account = secureKey.use((key) => {
|
|
231
|
+
return privateKeyToAccount(key as `0x${string}`);
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
expect(account).toBeDefined();
|
|
235
|
+
expect(account.address).toBeDefined();
|
|
236
|
+
expect(account.address).toMatch(/^0x[a-fA-F0-9]{40}$/);
|
|
237
|
+
|
|
238
|
+
secureKey.destroy();
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
it('should allow signing messages with account created from secure key', async () => {
|
|
242
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
243
|
+
|
|
244
|
+
const account = secureKey.use((key) => {
|
|
245
|
+
return privateKeyToAccount(key as `0x${string}`);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const message = 'Hello, Teneo!';
|
|
249
|
+
const signature = await account.signMessage({ message });
|
|
250
|
+
|
|
251
|
+
expect(signature).toBeDefined();
|
|
252
|
+
expect(signature).toMatch(/^0x[a-fA-F0-9]+$/);
|
|
253
|
+
expect(signature.length).toBeGreaterThan(100);
|
|
254
|
+
|
|
255
|
+
secureKey.destroy();
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
it('should create consistent account address across multiple uses', () => {
|
|
259
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
260
|
+
|
|
261
|
+
const address1 = secureKey.use((key) => {
|
|
262
|
+
return privateKeyToAccount(key as `0x${string}`).address;
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
const address2 = secureKey.use((key) => {
|
|
266
|
+
return privateKeyToAccount(key as `0x${string}`).address;
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
expect(address1).toBe(address2);
|
|
270
|
+
|
|
271
|
+
secureKey.destroy();
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
describe('security properties', () => {
|
|
276
|
+
it('should not expose private key in toString()', () => {
|
|
277
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
278
|
+
|
|
279
|
+
const stringified = String(secureKey);
|
|
280
|
+
expect(stringified).not.toContain(testPrivateKey);
|
|
281
|
+
expect(stringified).not.toContain(testPrivateKey.substring(5, 20));
|
|
282
|
+
|
|
283
|
+
secureKey.destroy();
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('should not expose private key in JSON.stringify()', () => {
|
|
287
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
288
|
+
|
|
289
|
+
const jsonString = JSON.stringify(secureKey);
|
|
290
|
+
expect(jsonString).not.toContain(testPrivateKey);
|
|
291
|
+
expect(jsonString).not.toContain(testPrivateKey.substring(5, 20));
|
|
292
|
+
|
|
293
|
+
secureKey.destroy();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
it('should not expose private key when inspecting object', () => {
|
|
297
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
298
|
+
|
|
299
|
+
// Try to access private properties (they're still accessible via 'any' but not exposed)
|
|
300
|
+
const keys = Object.keys(secureKey);
|
|
301
|
+
const values = Object.values(secureKey);
|
|
302
|
+
|
|
303
|
+
// Should not contain the plaintext key
|
|
304
|
+
const allValues = JSON.stringify(values);
|
|
305
|
+
expect(allValues).not.toContain(testPrivateKey);
|
|
306
|
+
|
|
307
|
+
secureKey.destroy();
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('should store key encrypted in memory', () => {
|
|
311
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
312
|
+
|
|
313
|
+
// Access encrypted buffer via any
|
|
314
|
+
const encrypted = (secureKey as any).encrypted as Buffer;
|
|
315
|
+
|
|
316
|
+
// Encrypted data should not contain the plaintext key
|
|
317
|
+
const encryptedString = encrypted.toString('utf8');
|
|
318
|
+
expect(encryptedString).not.toContain(testPrivateKey);
|
|
319
|
+
expect(encryptedString).not.toContain('1234567890');
|
|
320
|
+
|
|
321
|
+
secureKey.destroy();
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
describe('memory cleanup', () => {
|
|
326
|
+
it('should zero out encryption key on destroy', () => {
|
|
327
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
328
|
+
|
|
329
|
+
// Get reference to encryption key before destroy
|
|
330
|
+
const encryptionKey = (secureKey as any).encryptionKey as Buffer;
|
|
331
|
+
const originalKeyData = Buffer.from(encryptionKey);
|
|
332
|
+
|
|
333
|
+
expect(originalKeyData.some((byte) => byte !== 0)).toBe(true);
|
|
334
|
+
|
|
335
|
+
secureKey.destroy();
|
|
336
|
+
|
|
337
|
+
// After destroy, encryption key should be zeroed
|
|
338
|
+
expect(encryptionKey.every((byte) => byte === 0)).toBe(true);
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('should zero out encrypted buffer on destroy', () => {
|
|
342
|
+
const secureKey = new SecurePrivateKey(testPrivateKey);
|
|
343
|
+
|
|
344
|
+
// Get reference to encrypted buffer before destroy
|
|
345
|
+
const encrypted = (secureKey as any).encrypted as Buffer;
|
|
346
|
+
const originalEncryptedData = Buffer.from(encrypted);
|
|
347
|
+
|
|
348
|
+
expect(originalEncryptedData.some((byte) => byte !== 0)).toBe(true);
|
|
349
|
+
|
|
350
|
+
secureKey.destroy();
|
|
351
|
+
|
|
352
|
+
// After destroy, encrypted buffer should be zeroed
|
|
353
|
+
expect(encrypted.every((byte) => byte === 0)).toBe(true);
|
|
354
|
+
});
|
|
355
|
+
});
|
|
356
|
+
});
|