@agirails/sdk 2.5.2 → 2.5.4

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 (172) hide show
  1. package/dist/ACTPClient.d.ts +18 -0
  2. package/dist/ACTPClient.d.ts.map +1 -1
  3. package/dist/ACTPClient.js +67 -22
  4. package/dist/ACTPClient.js.map +1 -1
  5. package/dist/adapters/BasicAdapter.d.ts +12 -0
  6. package/dist/adapters/BasicAdapter.d.ts.map +1 -1
  7. package/dist/adapters/BasicAdapter.js +30 -4
  8. package/dist/adapters/BasicAdapter.js.map +1 -1
  9. package/dist/adapters/StandardAdapter.d.ts +20 -3
  10. package/dist/adapters/StandardAdapter.d.ts.map +1 -1
  11. package/dist/adapters/StandardAdapter.js +45 -11
  12. package/dist/adapters/StandardAdapter.js.map +1 -1
  13. package/dist/cli/commands/publish.js +16 -4
  14. package/dist/cli/commands/publish.js.map +1 -1
  15. package/dist/cli/commands/register.js +16 -4
  16. package/dist/cli/commands/register.js.map +1 -1
  17. package/dist/cli/commands/tx.js +31 -3
  18. package/dist/cli/commands/tx.js.map +1 -1
  19. package/dist/cli/utils/client.d.ts.map +1 -1
  20. package/dist/cli/utils/client.js +1 -0
  21. package/dist/cli/utils/client.js.map +1 -1
  22. package/dist/config/networks.d.ts +2 -2
  23. package/dist/config/networks.d.ts.map +1 -1
  24. package/dist/config/networks.js +27 -22
  25. package/dist/config/networks.js.map +1 -1
  26. package/dist/level0/request.d.ts.map +1 -1
  27. package/dist/level0/request.js +2 -1
  28. package/dist/level0/request.js.map +1 -1
  29. package/dist/runtime/BlockchainRuntime.d.ts.map +1 -1
  30. package/dist/runtime/BlockchainRuntime.js +11 -5
  31. package/dist/runtime/BlockchainRuntime.js.map +1 -1
  32. package/dist/runtime/MockStateManager.d.ts.map +1 -1
  33. package/dist/runtime/MockStateManager.js +2 -1
  34. package/dist/runtime/MockStateManager.js.map +1 -1
  35. package/dist/utils/IPFSClient.d.ts +3 -1
  36. package/dist/utils/IPFSClient.d.ts.map +1 -1
  37. package/dist/utils/IPFSClient.js +27 -7
  38. package/dist/utils/IPFSClient.js.map +1 -1
  39. package/dist/wallet/AutoWalletProvider.d.ts.map +1 -1
  40. package/dist/wallet/AutoWalletProvider.js +52 -18
  41. package/dist/wallet/AutoWalletProvider.js.map +1 -1
  42. package/dist/wallet/SmartWalletRouter.d.ts +116 -0
  43. package/dist/wallet/SmartWalletRouter.d.ts.map +1 -0
  44. package/dist/wallet/SmartWalletRouter.js +212 -0
  45. package/dist/wallet/SmartWalletRouter.js.map +1 -0
  46. package/dist/wallet/aa/DualNonceManager.d.ts +19 -0
  47. package/dist/wallet/aa/DualNonceManager.d.ts.map +1 -1
  48. package/dist/wallet/aa/DualNonceManager.js +100 -5
  49. package/dist/wallet/aa/DualNonceManager.js.map +1 -1
  50. package/package.json +3 -6
  51. package/src/ACTPClient.ts +0 -1579
  52. package/src/abi/ACTPKernel.json +0 -1356
  53. package/src/abi/AgentRegistry.json +0 -915
  54. package/src/abi/ERC20.json +0 -40
  55. package/src/abi/EscrowVault.json +0 -134
  56. package/src/abi/IdentityRegistry.json +0 -316
  57. package/src/adapters/AdapterRegistry.ts +0 -173
  58. package/src/adapters/AdapterRouter.ts +0 -416
  59. package/src/adapters/BaseAdapter.ts +0 -498
  60. package/src/adapters/BasicAdapter.ts +0 -514
  61. package/src/adapters/IAdapter.ts +0 -292
  62. package/src/adapters/StandardAdapter.ts +0 -555
  63. package/src/adapters/X402Adapter.ts +0 -731
  64. package/src/adapters/index.ts +0 -60
  65. package/src/builders/DeliveryProofBuilder.ts +0 -327
  66. package/src/builders/QuoteBuilder.ts +0 -483
  67. package/src/builders/index.ts +0 -17
  68. package/src/cli/commands/balance.ts +0 -110
  69. package/src/cli/commands/batch.ts +0 -487
  70. package/src/cli/commands/config.ts +0 -231
  71. package/src/cli/commands/deploy-check.ts +0 -364
  72. package/src/cli/commands/deploy-env.ts +0 -120
  73. package/src/cli/commands/diff.ts +0 -141
  74. package/src/cli/commands/init.ts +0 -469
  75. package/src/cli/commands/mint.ts +0 -116
  76. package/src/cli/commands/pay.ts +0 -113
  77. package/src/cli/commands/publish.ts +0 -475
  78. package/src/cli/commands/pull.ts +0 -124
  79. package/src/cli/commands/register.ts +0 -247
  80. package/src/cli/commands/simulate.ts +0 -345
  81. package/src/cli/commands/time.ts +0 -302
  82. package/src/cli/commands/tx.ts +0 -448
  83. package/src/cli/commands/watch.ts +0 -211
  84. package/src/cli/index.ts +0 -134
  85. package/src/cli/utils/client.ts +0 -251
  86. package/src/cli/utils/config.ts +0 -389
  87. package/src/cli/utils/output.ts +0 -465
  88. package/src/cli/utils/wallet.ts +0 -109
  89. package/src/config/agirailsmd.ts +0 -262
  90. package/src/config/networks.ts +0 -275
  91. package/src/config/pendingPublish.ts +0 -237
  92. package/src/config/publishPipeline.ts +0 -359
  93. package/src/config/syncOperations.ts +0 -279
  94. package/src/erc8004/ERC8004Bridge.ts +0 -462
  95. package/src/erc8004/ReputationReporter.ts +0 -468
  96. package/src/erc8004/index.ts +0 -61
  97. package/src/errors/index.ts +0 -427
  98. package/src/index.ts +0 -364
  99. package/src/level0/Provider.ts +0 -117
  100. package/src/level0/ServiceDirectory.ts +0 -131
  101. package/src/level0/index.ts +0 -10
  102. package/src/level0/provide.ts +0 -132
  103. package/src/level0/request.ts +0 -432
  104. package/src/level1/Agent.ts +0 -1426
  105. package/src/level1/index.ts +0 -10
  106. package/src/level1/pricing/PriceCalculator.ts +0 -255
  107. package/src/level1/pricing/PricingStrategy.ts +0 -198
  108. package/src/level1/types/Job.ts +0 -179
  109. package/src/level1/types/Options.ts +0 -291
  110. package/src/level1/types/index.ts +0 -8
  111. package/src/protocol/ACTPKernel.ts +0 -808
  112. package/src/protocol/AgentRegistry.ts +0 -559
  113. package/src/protocol/DIDManager.ts +0 -629
  114. package/src/protocol/DIDResolver.ts +0 -554
  115. package/src/protocol/EASHelper.ts +0 -378
  116. package/src/protocol/EscrowVault.ts +0 -255
  117. package/src/protocol/EventMonitor.ts +0 -204
  118. package/src/protocol/MessageSigner.ts +0 -510
  119. package/src/protocol/ProofGenerator.ts +0 -339
  120. package/src/protocol/QuoteBuilder.ts +0 -15
  121. package/src/registry/AgentRegistryClient.ts +0 -202
  122. package/src/runtime/BlockchainRuntime.ts +0 -1015
  123. package/src/runtime/IACTPRuntime.ts +0 -306
  124. package/src/runtime/MockRuntime.ts +0 -1298
  125. package/src/runtime/MockStateManager.ts +0 -576
  126. package/src/runtime/index.ts +0 -25
  127. package/src/runtime/types/MockState.ts +0 -237
  128. package/src/storage/ArchiveBundleBuilder.ts +0 -561
  129. package/src/storage/ArweaveClient.ts +0 -946
  130. package/src/storage/FilebaseClient.ts +0 -790
  131. package/src/storage/index.ts +0 -96
  132. package/src/storage/types.ts +0 -348
  133. package/src/types/adapter.ts +0 -310
  134. package/src/types/agent.ts +0 -79
  135. package/src/types/did.ts +0 -223
  136. package/src/types/eip712.ts +0 -175
  137. package/src/types/erc8004.ts +0 -293
  138. package/src/types/escrow.ts +0 -27
  139. package/src/types/index.ts +0 -17
  140. package/src/types/message.ts +0 -145
  141. package/src/types/state.ts +0 -87
  142. package/src/types/transaction.ts +0 -69
  143. package/src/types/x402.ts +0 -251
  144. package/src/utils/ErrorRecoveryGuide.ts +0 -676
  145. package/src/utils/Helpers.ts +0 -688
  146. package/src/utils/IPFSClient.ts +0 -368
  147. package/src/utils/Logger.ts +0 -484
  148. package/src/utils/NonceManager.ts +0 -591
  149. package/src/utils/RateLimiter.ts +0 -534
  150. package/src/utils/ReceivedNonceTracker.ts +0 -567
  151. package/src/utils/SDKLifecycle.ts +0 -416
  152. package/src/utils/SecureNonce.ts +0 -78
  153. package/src/utils/Semaphore.ts +0 -276
  154. package/src/utils/UsedAttestationTracker.ts +0 -385
  155. package/src/utils/canonicalJson.ts +0 -38
  156. package/src/utils/circuitBreaker.ts +0 -324
  157. package/src/utils/computeTypeHash.ts +0 -48
  158. package/src/utils/fsSafe.ts +0 -80
  159. package/src/utils/index.ts +0 -80
  160. package/src/utils/retry.ts +0 -364
  161. package/src/utils/security.ts +0 -418
  162. package/src/utils/validation.ts +0 -540
  163. package/src/wallet/AutoWalletProvider.ts +0 -299
  164. package/src/wallet/EOAWalletProvider.ts +0 -69
  165. package/src/wallet/IWalletProvider.ts +0 -135
  166. package/src/wallet/aa/BundlerClient.ts +0 -274
  167. package/src/wallet/aa/DualNonceManager.ts +0 -173
  168. package/src/wallet/aa/PaymasterClient.ts +0 -174
  169. package/src/wallet/aa/TransactionBatcher.ts +0 -353
  170. package/src/wallet/aa/UserOpBuilder.ts +0 -246
  171. package/src/wallet/aa/constants.ts +0 -60
  172. package/src/wallet/keystore.ts +0 -240
@@ -1,576 +0,0 @@
1
- /**
2
- * MockStateManager - Persistent state management for Mock Mode
3
- *
4
- * Responsible for persisting mock blockchain state to disk, enabling
5
- * state sharing across CLI commands, SDK library usage, and Dashboard.
6
- *
7
- * Features:
8
- * - File-based JSON persistence in `.actp/mock-state.json`
9
- * - Atomic file operations (write to temp file, then rename)
10
- * - File locking to prevent concurrent access corruption
11
- * - Error recovery for corrupted state files
12
- *
13
- * @module runtime/MockStateManager
14
- * @see ADR-001 (Mock State Persistence Strategy)
15
- * @see MOCK_STATE_MANAGER_SPEC.md
16
- */
17
-
18
- import * as fs from 'fs';
19
- import * as path from 'path';
20
- import lockfile from 'proper-lockfile';
21
- import { MockState, MOCK_STATE_DEFAULTS } from './types/MockState';
22
- import { assertSafeFileForRead, ensureSafeDir } from '../utils/fsSafe';
23
-
24
- /**
25
- * Error thrown when mock state file is corrupted.
26
- */
27
- export class MockStateCorruptedError extends Error {
28
- public readonly statePath: string;
29
-
30
- constructor(statePath: string, cause?: Error) {
31
- super(
32
- `Mock state file corrupted: ${statePath}\n` +
33
- `Delete it manually or run: actp mock reset\n` +
34
- (cause ? `Cause: ${cause.message}` : '')
35
- );
36
- this.name = 'MockStateCorruptedError';
37
- this.statePath = statePath;
38
- }
39
- }
40
-
41
- /**
42
- * Error thrown when state version is unsupported.
43
- */
44
- export class MockStateVersionError extends Error {
45
- public readonly version: string;
46
- public readonly supportedVersion: string;
47
-
48
- constructor(version: string, supportedVersion: string = MOCK_STATE_DEFAULTS.VERSION) {
49
- super(
50
- `Unsupported state version: ${version}\n` +
51
- `Supported version: ${supportedVersion}\n` +
52
- `Run: actp mock reset to create new state file`
53
- );
54
- this.name = 'MockStateVersionError';
55
- this.version = version;
56
- this.supportedVersion = supportedVersion;
57
- }
58
- }
59
-
60
- /**
61
- * Error thrown when lock cannot be acquired.
62
- */
63
- export class MockStateLockError extends Error {
64
- public readonly statePath: string;
65
- public readonly cause?: Error;
66
-
67
- constructor(statePath: string, cause?: Error) {
68
- super(
69
- `Could not acquire lock on mock state: ${statePath}\n` +
70
- `Another ACTP process may be running.\n` +
71
- `Please wait a moment and try again.` +
72
- (cause ? `\nCause: ${cause.message}` : '')
73
- );
74
- this.name = 'MockStateLockError';
75
- this.statePath = statePath;
76
- this.cause = cause;
77
- }
78
- }
79
-
80
- /**
81
- * Lock options for proper-lockfile.
82
- * - 5 retries with exponential backoff (100ms -> 1000ms)
83
- * - 10 second stale lock detection (handles process crashes)
84
- */
85
- const LOCK_OPTIONS: lockfile.LockOptions = {
86
- retries: {
87
- retries: 5,
88
- minTimeout: 100,
89
- maxTimeout: 1000,
90
- },
91
- stale: 10000, // 10 seconds
92
- };
93
-
94
- /**
95
- * Maximum allowed state file size (10 MB).
96
- * Prevents disk exhaustion from runaway state growth.
97
- *
98
- * Note: For states approaching this limit, JSON.stringify/parse may
99
- * temporarily use ~3x the file size in memory (~30 MB for 10 MB state).
100
- * This is acceptable for a local development tool with <1000 transactions.
101
- */
102
- const MAX_STATE_FILE_SIZE = 10 * 1024 * 1024;
103
-
104
- /**
105
- * Maximum nesting depth for JSON parsing.
106
- * Prevents DoS attacks via deeply nested JSON structures.
107
- *
108
- * SECURITY: Deeply nested JSON can cause:
109
- * - Stack overflow during parsing
110
- * - Excessive CPU usage during traversal
111
- * - Memory exhaustion from recursion
112
- */
113
- const MAX_NESTING_DEPTH = 100;
114
-
115
- // ============================================================================
116
- // Security: Path Sanitization and JSON Depth Check
117
- // ============================================================================
118
-
119
- /**
120
- * Sanitize a file path for safe display in error messages.
121
- *
122
- * SECURITY: Replaces home directory with ~ to prevent information disclosure.
123
- *
124
- * @param fullPath - The full file path to sanitize
125
- * @returns Sanitized path safe for display
126
- */
127
- function sanitizePath(fullPath: string): string {
128
- // Import os dynamically to get home directory
129
- const home = process.env.HOME || process.env.USERPROFILE || '/';
130
- return fullPath.replace(home, '~');
131
- }
132
-
133
- /**
134
- * Check the nesting depth of a parsed JSON object.
135
- *
136
- * SECURITY: Prevents DoS via deeply nested JSON structures that can cause:
137
- * - Stack overflow
138
- * - Excessive CPU usage
139
- * - Memory exhaustion
140
- *
141
- * @param obj - The object to check
142
- * @param maxDepth - Maximum allowed nesting depth
143
- * @param currentDepth - Current depth (used for recursion)
144
- * @throws Error if object exceeds maximum nesting depth
145
- */
146
- function checkNestingDepth(
147
- obj: unknown,
148
- maxDepth: number = MAX_NESTING_DEPTH,
149
- currentDepth: number = 0
150
- ): void {
151
- if (currentDepth > maxDepth) {
152
- throw new Error(
153
- `State file exceeds maximum nesting depth of ${maxDepth}. ` +
154
- 'This may indicate a corrupted or malicious state file.'
155
- );
156
- }
157
-
158
- if (obj !== null && typeof obj === 'object') {
159
- // Check arrays and objects
160
- const values = Array.isArray(obj) ? obj : Object.values(obj);
161
- for (const value of values) {
162
- checkNestingDepth(value, maxDepth, currentDepth + 1);
163
- }
164
- }
165
- }
166
-
167
- /**
168
- * MockStateManager handles persistence of mock blockchain state.
169
- *
170
- * State is stored in `.actp/mock-state.json` within the project root.
171
- * Uses file locking to prevent corruption from concurrent access.
172
- *
173
- * @example
174
- * ```typescript
175
- * const manager = new MockStateManager();
176
- *
177
- * // Read-only access (no lock)
178
- * const state = manager.loadState();
179
- * console.log('Transactions:', Object.keys(state.transactions).length);
180
- *
181
- * // Read-modify-write with lock
182
- * const txId = await manager.withLock(async (state) => {
183
- * const txId = generateId();
184
- * state.transactions[txId] = { ... };
185
- * return txId;
186
- * });
187
- * ```
188
- */
189
- export class MockStateManager {
190
- /** Path to the state JSON file */
191
- private readonly statePath: string;
192
-
193
- /** Path to the .actp directory */
194
- private readonly actpDir: string;
195
-
196
- /**
197
- * Creates a new MockStateManager instance.
198
- *
199
- * @param projectRoot - Root directory for `.actp/` folder.
200
- * Defaults to current working directory.
201
- *
202
- * @example
203
- * ```typescript
204
- * // Use current directory
205
- * const manager = new MockStateManager();
206
- *
207
- * // Use specific project root
208
- * const manager = new MockStateManager('/path/to/project');
209
- * ```
210
- */
211
- constructor(projectRoot: string = process.cwd()) {
212
- this.actpDir = path.join(projectRoot, '.actp');
213
- this.statePath = path.join(this.actpDir, 'mock-state.json');
214
-
215
- // Ensure .actp directory exists
216
- this.ensureDirectory();
217
- }
218
-
219
- /**
220
- * Ensures the .actp directory exists.
221
- * Creates it with secure permissions if missing.
222
- */
223
- private ensureDirectory(): void {
224
- ensureSafeDir(this.actpDir, 0o755);
225
- }
226
-
227
- /**
228
- * Loads state from disk.
229
- *
230
- * Returns default state if file doesn't exist (first run).
231
- * Validates version compatibility and file size limits.
232
- *
233
- * SECURITY: Validates nesting depth to prevent DoS attacks.
234
- *
235
- * @returns The current mock state
236
- *
237
- * @throws {MockStateCorruptedError} If state file contains invalid JSON
238
- * @throws {MockStateVersionError} If state version is not supported
239
- * @throws {Error} If file exceeds size limit or other I/O errors
240
- *
241
- * @example
242
- * ```typescript
243
- * const state = manager.loadState();
244
- * console.log('Current time:', state.blockchain.currentTime);
245
- * ```
246
- */
247
- loadState(): MockState {
248
- // If file doesn't exist, return default state
249
- if (!fs.existsSync(this.statePath)) {
250
- return this.getDefaultState();
251
- }
252
-
253
- // SECURITY: Refuse to read from symlinked state files
254
- assertSafeFileForRead(this.statePath);
255
-
256
- // Check file size limit
257
- const stats = fs.statSync(this.statePath);
258
- if (stats.size > MAX_STATE_FILE_SIZE) {
259
- throw new Error(
260
- `Mock state file exceeds ${MAX_STATE_FILE_SIZE / 1024 / 1024} MB limit.\n` +
261
- `Run: actp mock compact or actp mock reset`
262
- );
263
- }
264
-
265
- // Read and parse file
266
- let raw: string;
267
- try {
268
- raw = fs.readFileSync(this.statePath, 'utf-8');
269
- } catch (error) {
270
- // SECURITY: Sanitize path in error message
271
- throw new Error(
272
- `Failed to read mock state file: ${sanitizePath(this.statePath)}\n` +
273
- `Error: ${(error as Error).message}`
274
- );
275
- }
276
-
277
- // Parse JSON
278
- let state: MockState;
279
- try {
280
- state = JSON.parse(raw);
281
- } catch (error) {
282
- // SECURITY: Sanitize path in error message
283
- throw new MockStateCorruptedError(sanitizePath(this.statePath), error as Error);
284
- }
285
-
286
- // SECURITY FIX: Check nesting depth to prevent DoS via deeply nested JSON
287
- try {
288
- checkNestingDepth(state);
289
- } catch (error) {
290
- throw new MockStateCorruptedError(
291
- sanitizePath(this.statePath),
292
- error as Error
293
- );
294
- }
295
-
296
- // Validate version
297
- if (state.version !== MOCK_STATE_DEFAULTS.VERSION) {
298
- throw new MockStateVersionError(state.version);
299
- }
300
-
301
- // Basic schema validation
302
- if (
303
- typeof state.mode !== 'string' ||
304
- typeof state.blockchain !== 'object' ||
305
- typeof state.transactions !== 'object' ||
306
- typeof state.escrows !== 'object' ||
307
- typeof state.accounts !== 'object'
308
- ) {
309
- throw new MockStateCorruptedError(sanitizePath(this.statePath));
310
- }
311
-
312
- return state;
313
- }
314
-
315
- /**
316
- * Saves state to disk atomically.
317
- *
318
- * Uses write-to-temp-then-rename pattern to ensure atomicity:
319
- * 1. Write state to `.tmp` file
320
- * 2. Rename `.tmp` to final path (atomic on POSIX)
321
- *
322
- * This prevents corruption if process crashes during write.
323
- *
324
- * @param state - The state to save
325
- *
326
- * @throws {Error} If write fails (disk full, permissions, etc.)
327
- *
328
- * @example
329
- * ```typescript
330
- * const state = manager.loadState();
331
- * state.blockchain.currentTime += 3600; // Advance 1 hour
332
- * manager.saveState(state);
333
- * ```
334
- */
335
- saveState(state: MockState): void {
336
- // Ensure directory exists (may have been deleted)
337
- this.ensureDirectory();
338
-
339
- const tempPath = `${this.statePath}.tmp`;
340
-
341
- try {
342
- // Prevent clobbering via pre-created temp symlink/file
343
- if (fs.existsSync(tempPath)) {
344
- fs.unlinkSync(tempPath);
345
- }
346
-
347
- // Write to temp file first (pretty-printed for human readability)
348
- const json = JSON.stringify(state, null, 2);
349
- fs.writeFileSync(tempPath, json, {
350
- encoding: 'utf-8',
351
- mode: 0o644,
352
- flag: 'wx', // exclusive create: do not follow existing symlink
353
- });
354
-
355
- // Rename atomically (POSIX rename is atomic)
356
- fs.renameSync(tempPath, this.statePath);
357
- } catch (error) {
358
- // Clean up temp file if it exists
359
- if (fs.existsSync(tempPath)) {
360
- try {
361
- fs.unlinkSync(tempPath);
362
- } catch {
363
- // Ignore cleanup errors
364
- }
365
- }
366
-
367
- throw new Error(
368
- `Failed to save mock state: ${(error as Error).message}\n` +
369
- `Path: ${sanitizePath(this.statePath)}`
370
- );
371
- }
372
- }
373
-
374
- /**
375
- * Executes an operation with exclusive file lock.
376
- *
377
- * Provides read-modify-write semantics with concurrency protection:
378
- * 1. Acquires exclusive lock on state file
379
- * 2. Loads current state
380
- * 3. Executes operation (may modify state)
381
- * 4. Saves updated state
382
- * 5. Releases lock
383
- *
384
- * Lock is always released, even if operation throws.
385
- *
386
- * @typeParam T - Return type of the operation
387
- * @param operation - Function that receives state and returns result.
388
- * Can be sync or async. May modify state object.
389
- *
390
- * @returns Promise resolving to operation's return value
391
- *
392
- * @throws {MockStateLockError} If lock cannot be acquired after retries
393
- * @throws {Error} If operation throws or save fails
394
- *
395
- * @example
396
- * ```typescript
397
- * // Create transaction with lock
398
- * const txId = await manager.withLock(async (state) => {
399
- * const txId = '0x' + crypto.randomBytes(32).toString('hex');
400
- * state.transactions[txId] = {
401
- * id: txId,
402
- * state: 'INITIATED',
403
- * // ...
404
- * };
405
- * return txId;
406
- * });
407
- * ```
408
- */
409
- async withLock<T>(operation: (state: MockState) => T | Promise<T>): Promise<T> {
410
- // Ensure state file exists before locking
411
- // (proper-lockfile requires the file to exist)
412
- if (!fs.existsSync(this.statePath)) {
413
- this.saveState(this.getDefaultState());
414
- }
415
-
416
- // SECURITY: Refuse to lock/use symlinked state files
417
- assertSafeFileForRead(this.statePath);
418
-
419
- let release: (() => Promise<void>) | null = null;
420
-
421
- try {
422
- // Acquire exclusive lock
423
- release = await lockfile.lock(this.statePath, LOCK_OPTIONS);
424
-
425
- // Load current state
426
- const state = this.loadState();
427
-
428
- // Execute operation (may be async)
429
- const result = await operation(state);
430
-
431
- // Save modified state
432
- this.saveState(state);
433
-
434
- return result;
435
- } catch (error) {
436
- // Transform lock errors into friendlier messages
437
- if (
438
- error instanceof Error &&
439
- (error.message.includes('ELOCKED') || error.message.includes('lock'))
440
- ) {
441
- throw new MockStateLockError(this.statePath, error);
442
- }
443
- throw error;
444
- } finally {
445
- // Always release lock
446
- if (release) {
447
- try {
448
- await release();
449
- } catch {
450
- // Ignore release errors (lock may have expired)
451
- }
452
- }
453
- }
454
- }
455
-
456
- /**
457
- * Resets state to default (fresh blockchain).
458
- *
459
- * Useful for starting fresh during testing or after corruption.
460
- * Does not require lock (overwrites entire file).
461
- *
462
- * @example
463
- * ```typescript
464
- * // Reset to clean state
465
- * manager.reset();
466
- *
467
- * // Verify reset
468
- * const state = manager.loadState();
469
- * console.log('Transactions:', Object.keys(state.transactions).length); // 0
470
- * ```
471
- */
472
- reset(): void {
473
- const defaultState = this.getDefaultState();
474
- this.saveState(defaultState);
475
- }
476
-
477
- /**
478
- * Checks if mock mode is initialized (state file exists).
479
- *
480
- * @returns true if state file exists, false otherwise
481
- *
482
- * @example
483
- * ```typescript
484
- * if (!manager.exists()) {
485
- * console.log('Run: actp init to initialize mock mode');
486
- * }
487
- * ```
488
- */
489
- exists(): boolean {
490
- return fs.existsSync(this.statePath);
491
- }
492
-
493
- /**
494
- * Gets the path to the state file.
495
- *
496
- * @returns Absolute path to mock-state.json
497
- */
498
- getStatePath(): string {
499
- return this.statePath;
500
- }
501
-
502
- /**
503
- * Gets the path to the .actp directory.
504
- *
505
- * @returns Absolute path to .actp directory
506
- */
507
- getActpDir(): string {
508
- return this.actpDir;
509
- }
510
-
511
- /**
512
- * Creates default/initial mock state.
513
- *
514
- * Used when state file doesn't exist or after reset.
515
- * Initializes blockchain with current timestamp.
516
- *
517
- * @returns Fresh mock state with empty transactions/escrows/accounts
518
- */
519
- getDefaultState(): MockState {
520
- return {
521
- version: MOCK_STATE_DEFAULTS.VERSION,
522
- mode: 'mock',
523
- blockchain: {
524
- currentTime: Math.floor(Date.now() / 1000),
525
- blockNumber: MOCK_STATE_DEFAULTS.INITIAL_BLOCK_NUMBER,
526
- chainId: MOCK_STATE_DEFAULTS.CHAIN_ID,
527
- blockTime: MOCK_STATE_DEFAULTS.BLOCK_TIME,
528
- },
529
- transactions: {},
530
- escrows: {},
531
- accounts: {},
532
- // SECURITY FIX (L-4): Initialize events array for persistence
533
- events: [],
534
- };
535
- }
536
-
537
- /**
538
- * Deletes the state file and .actp directory if empty.
539
- *
540
- * Used for cleanup during tests or uninitialization.
541
- *
542
- * @param force - If true, delete .actp directory even if not empty
543
- *
544
- * @example
545
- * ```typescript
546
- * // Clean up after tests
547
- * manager.destroy();
548
- * ```
549
- */
550
- destroy(force: boolean = false): void {
551
- // Remove state file
552
- if (fs.existsSync(this.statePath)) {
553
- fs.unlinkSync(this.statePath);
554
- }
555
-
556
- // Remove temp file if exists
557
- const tempPath = `${this.statePath}.tmp`;
558
- if (fs.existsSync(tempPath)) {
559
- fs.unlinkSync(tempPath);
560
- }
561
-
562
- // Remove lock file if exists (created by proper-lockfile)
563
- const lockPath = `${this.statePath}.lock`;
564
- if (fs.existsSync(lockPath)) {
565
- fs.unlinkSync(lockPath);
566
- }
567
-
568
- // Remove .actp directory if empty or forced
569
- if (fs.existsSync(this.actpDir)) {
570
- const files = fs.readdirSync(this.actpDir);
571
- if (files.length === 0 || force) {
572
- fs.rmSync(this.actpDir, { recursive: true, force: true });
573
- }
574
- }
575
- }
576
- }
@@ -1,25 +0,0 @@
1
- /**
2
- * Runtime module exports.
3
- *
4
- * Provides both the runtime interface and concrete implementations.
5
- *
6
- * @module runtime
7
- */
8
-
9
- export { IACTPRuntime, IMockRuntime, CreateTransactionParams } from './IACTPRuntime';
10
- export { MockRuntime } from './MockRuntime';
11
- export { MockStateManager } from './MockStateManager';
12
- export { BlockchainRuntime, BlockchainRuntimeConfig } from './BlockchainRuntime';
13
- export * from './types/MockState';
14
-
15
- // Re-export all custom errors from MockRuntime
16
- export {
17
- TransactionNotFoundError,
18
- InvalidStateTransitionError,
19
- InsufficientBalanceError,
20
- EscrowNotFoundError,
21
- DeadlinePassedError,
22
- ContractPausedError,
23
- InvalidAmountError,
24
- DisputeWindowActiveError,
25
- } from './MockRuntime';