@manifest-network/manifest-mcp-browser 0.1.6 → 0.1.8

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 (95) hide show
  1. package/README.md +5 -2
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +0 -10
  4. package/dist/config.js.map +1 -1
  5. package/dist/index.d.ts +0 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +3 -7
  8. package/dist/index.js.map +1 -1
  9. package/dist/modules.d.ts.map +1 -1
  10. package/dist/modules.js +42 -0
  11. package/dist/modules.js.map +1 -1
  12. package/dist/queries/group.d.ts +12 -0
  13. package/dist/queries/group.d.ts.map +1 -0
  14. package/dist/queries/group.js +107 -0
  15. package/dist/queries/group.js.map +1 -0
  16. package/dist/queries/index.d.ts +2 -0
  17. package/dist/queries/index.d.ts.map +1 -1
  18. package/dist/queries/index.js +2 -0
  19. package/dist/queries/index.js.map +1 -1
  20. package/dist/queries/utils.d.ts +3 -18
  21. package/dist/queries/utils.d.ts.map +1 -1
  22. package/dist/queries/utils.js +2 -17
  23. package/dist/queries/utils.js.map +1 -1
  24. package/dist/transactions/bank.d.ts.map +1 -1
  25. package/dist/transactions/bank.js +7 -5
  26. package/dist/transactions/bank.js.map +1 -1
  27. package/dist/transactions/gov.d.ts.map +1 -1
  28. package/dist/transactions/gov.js +9 -30
  29. package/dist/transactions/gov.js.map +1 -1
  30. package/dist/transactions/group.d.ts +7 -0
  31. package/dist/transactions/group.d.ts.map +1 -0
  32. package/dist/transactions/group.js +339 -0
  33. package/dist/transactions/group.js.map +1 -0
  34. package/dist/transactions/index.d.ts +2 -0
  35. package/dist/transactions/index.d.ts.map +1 -1
  36. package/dist/transactions/index.js +2 -0
  37. package/dist/transactions/index.js.map +1 -1
  38. package/dist/transactions/utils.d.ts +37 -0
  39. package/dist/transactions/utils.d.ts.map +1 -1
  40. package/dist/transactions/utils.js +43 -0
  41. package/dist/transactions/utils.js.map +1 -1
  42. package/dist/types.d.ts +31 -3
  43. package/dist/types.d.ts.map +1 -1
  44. package/dist/types.js.map +1 -1
  45. package/package.json +5 -2
  46. package/.github/workflows/ci.yml +0 -37
  47. package/.github/workflows/publish.yml +0 -53
  48. package/CLAUDE.md +0 -111
  49. package/dist/config.test.d.ts +0 -2
  50. package/dist/config.test.d.ts.map +0 -1
  51. package/dist/config.test.js +0 -251
  52. package/dist/config.test.js.map +0 -1
  53. package/dist/modules.test.d.ts +0 -2
  54. package/dist/modules.test.d.ts.map +0 -1
  55. package/dist/modules.test.js +0 -159
  56. package/dist/modules.test.js.map +0 -1
  57. package/dist/queries/utils.test.d.ts +0 -2
  58. package/dist/queries/utils.test.d.ts.map +0 -1
  59. package/dist/queries/utils.test.js +0 -117
  60. package/dist/queries/utils.test.js.map +0 -1
  61. package/dist/transactions/utils.test.d.ts +0 -2
  62. package/dist/transactions/utils.test.d.ts.map +0 -1
  63. package/dist/transactions/utils.test.js +0 -471
  64. package/dist/transactions/utils.test.js.map +0 -1
  65. package/src/client.ts +0 -288
  66. package/src/config.test.ts +0 -299
  67. package/src/config.ts +0 -174
  68. package/src/cosmos.ts +0 -106
  69. package/src/index.ts +0 -478
  70. package/src/modules.test.ts +0 -189
  71. package/src/modules.ts +0 -428
  72. package/src/queries/auth.ts +0 -97
  73. package/src/queries/bank.ts +0 -99
  74. package/src/queries/billing.ts +0 -124
  75. package/src/queries/distribution.ts +0 -114
  76. package/src/queries/gov.ts +0 -104
  77. package/src/queries/index.ts +0 -16
  78. package/src/queries/sku.ts +0 -85
  79. package/src/queries/staking.ts +0 -154
  80. package/src/queries/utils.test.ts +0 -156
  81. package/src/queries/utils.ts +0 -145
  82. package/src/transactions/bank.ts +0 -86
  83. package/src/transactions/billing.ts +0 -286
  84. package/src/transactions/distribution.ts +0 -76
  85. package/src/transactions/gov.ts +0 -191
  86. package/src/transactions/index.ts +0 -7
  87. package/src/transactions/manifest.ts +0 -67
  88. package/src/transactions/sku.ts +0 -232
  89. package/src/transactions/staking.ts +0 -85
  90. package/src/transactions/utils.test.ts +0 -518
  91. package/src/transactions/utils.ts +0 -348
  92. package/src/types.ts +0 -497
  93. package/src/wallet/index.ts +0 -2
  94. package/src/wallet/mnemonic.ts +0 -146
  95. package/tsconfig.json +0 -23
package/src/client.ts DELETED
@@ -1,288 +0,0 @@
1
- import { SigningStargateClient, GasPrice, HttpEndpoint } from '@cosmjs/stargate';
2
- import {
3
- cosmosProtoRegistry,
4
- cosmosAminoConverters,
5
- liftedinitProtoRegistry,
6
- liftedinitAminoConverters,
7
- strangeloveVenturesProtoRegistry,
8
- strangeloveVenturesAminoConverters,
9
- osmosisProtoRegistry,
10
- osmosisAminoConverters,
11
- liftedinit,
12
- } from '@manifest-network/manifestjs';
13
- import { Registry } from '@cosmjs/proto-signing';
14
- import { AminoTypes } from '@cosmjs/stargate';
15
- import { RateLimiter } from 'limiter';
16
- import { ManifestMCPConfig, WalletProvider, ManifestMCPError, ManifestMCPErrorCode } from './types.js';
17
- import { DEFAULT_REQUESTS_PER_SECOND } from './config.js';
18
-
19
- // Type for the RPC query client from manifestjs liftedinit bundle
20
- // This includes cosmos modules + liftedinit-specific modules (billing, manifest, sku)
21
- export type ManifestQueryClient = Awaited<ReturnType<typeof liftedinit.ClientFactory.createRPCQueryClient>>;
22
-
23
- /** Default timeout for transaction broadcast (60 seconds) */
24
- const DEFAULT_BROADCAST_TIMEOUT_MS = 60_000;
25
-
26
- /** Default polling interval for transaction confirmation (3 seconds) */
27
- const DEFAULT_BROADCAST_POLL_INTERVAL_MS = 3_000;
28
-
29
- /**
30
- * Get combined signing client options with all Manifest registries
31
- */
32
- function getSigningManifestClientOptions() {
33
- const registry = new Registry([
34
- ...cosmosProtoRegistry,
35
- ...liftedinitProtoRegistry,
36
- ...strangeloveVenturesProtoRegistry,
37
- ...osmosisProtoRegistry,
38
- ]);
39
-
40
- const aminoTypes = new AminoTypes({
41
- ...cosmosAminoConverters,
42
- ...liftedinitAminoConverters,
43
- ...strangeloveVenturesAminoConverters,
44
- ...osmosisAminoConverters,
45
- });
46
-
47
- return { registry, aminoTypes };
48
- }
49
-
50
- /**
51
- * Manages CosmJS client instances with lazy initialization and singleton pattern
52
- */
53
- export class CosmosClientManager {
54
- private static instances: Map<string, CosmosClientManager> = new Map();
55
-
56
- private config: ManifestMCPConfig;
57
- private walletProvider: WalletProvider;
58
- private queryClient: ManifestQueryClient | null = null;
59
- private signingClient: SigningStargateClient | null = null;
60
- private rateLimiter: RateLimiter;
61
-
62
- // Promises to prevent concurrent client initialization (lazy init race condition)
63
- private queryClientPromise: Promise<ManifestQueryClient> | null = null;
64
- private signingClientPromise: Promise<SigningStargateClient> | null = null;
65
-
66
- private constructor(config: ManifestMCPConfig, walletProvider: WalletProvider) {
67
- this.config = config;
68
- this.walletProvider = walletProvider;
69
-
70
- // Initialize rate limiter with configured or default requests per second
71
- const requestsPerSecond = config.rateLimit?.requestsPerSecond ?? DEFAULT_REQUESTS_PER_SECOND;
72
- this.rateLimiter = new RateLimiter({
73
- tokensPerInterval: requestsPerSecond,
74
- interval: 'second',
75
- });
76
- }
77
-
78
- /**
79
- * Get or create a singleton instance for the given config.
80
- * Instances are keyed by chainId:rpcUrl. For existing instances:
81
- * - Config and walletProvider references are always updated
82
- * - Signing client is disconnected/recreated if gasPrice or walletProvider changed
83
- * - Rate limiter is updated if requestsPerSecond changed (without affecting signing client)
84
- */
85
- static getInstance(
86
- config: ManifestMCPConfig,
87
- walletProvider: WalletProvider
88
- ): CosmosClientManager {
89
- const key = `${config.chainId}:${config.rpcUrl}`;
90
- let instance = CosmosClientManager.instances.get(key);
91
-
92
- if (!instance) {
93
- instance = new CosmosClientManager(config, walletProvider);
94
- CosmosClientManager.instances.set(key, instance);
95
- } else {
96
- // Check what changed to determine what needs updating
97
- const signingClientAffected =
98
- instance.config.gasPrice !== config.gasPrice ||
99
- instance.walletProvider !== walletProvider;
100
-
101
- const rateLimitChanged =
102
- instance.config.rateLimit?.requestsPerSecond !== config.rateLimit?.requestsPerSecond;
103
-
104
- // Always update config reference
105
- instance.config = config;
106
- instance.walletProvider = walletProvider;
107
-
108
- // Only invalidate signing client if fields it depends on changed
109
- if (signingClientAffected) {
110
- if (instance.signingClient) {
111
- instance.signingClient.disconnect();
112
- instance.signingClient = null;
113
- }
114
- // Also clear the promise to allow re-initialization with new config
115
- instance.signingClientPromise = null;
116
- }
117
-
118
- // Update rate limiter independently (doesn't affect signing client)
119
- if (rateLimitChanged) {
120
- const newRps = config.rateLimit?.requestsPerSecond ?? DEFAULT_REQUESTS_PER_SECOND;
121
- instance.rateLimiter = new RateLimiter({
122
- tokensPerInterval: newRps,
123
- interval: 'second',
124
- });
125
- }
126
- }
127
-
128
- return instance;
129
- }
130
-
131
- /**
132
- * Clear all cached instances (useful for testing or reconnection)
133
- */
134
- static clearInstances(): void {
135
- CosmosClientManager.instances.clear();
136
- }
137
-
138
- /**
139
- * Get the manifestjs RPC query client with all module extensions
140
- */
141
- async getQueryClient(): Promise<ManifestQueryClient> {
142
- // Return cached client if available
143
- if (this.queryClient) {
144
- return this.queryClient;
145
- }
146
-
147
- // If initialization is already in progress, wait for it
148
- if (this.queryClientPromise) {
149
- return this.queryClientPromise;
150
- }
151
-
152
- // Start initialization and cache the promise to prevent concurrent init
153
- this.queryClientPromise = (async () => {
154
- // Capture reference to detect if superseded by disconnect/config change
155
- const thisInitPromise = this.queryClientPromise;
156
- try {
157
- // Use liftedinit ClientFactory which includes cosmos + liftedinit modules
158
- const client = await liftedinit.ClientFactory.createRPCQueryClient({
159
- rpcEndpoint: this.config.rpcUrl,
160
- });
161
- // Only store if this is still the active promise
162
- if (this.queryClientPromise === thisInitPromise) {
163
- this.queryClient = client;
164
- this.queryClientPromise = null;
165
- }
166
- return client;
167
- } catch (error) {
168
- // Clear promise on failure so retry is possible (only if still active)
169
- if (this.queryClientPromise === thisInitPromise) {
170
- this.queryClientPromise = null;
171
- }
172
- throw new ManifestMCPError(
173
- ManifestMCPErrorCode.RPC_CONNECTION_FAILED,
174
- `Failed to connect to RPC endpoint: ${error instanceof Error ? error.message : String(error)}`,
175
- { rpcUrl: this.config.rpcUrl }
176
- );
177
- }
178
- })();
179
-
180
- return this.queryClientPromise;
181
- }
182
-
183
- /**
184
- * Get a signing client with all Manifest registries (for transactions)
185
- */
186
- async getSigningClient(): Promise<SigningStargateClient> {
187
- // Return cached client if available
188
- if (this.signingClient) {
189
- return this.signingClient;
190
- }
191
-
192
- // If initialization is already in progress, wait for it
193
- if (this.signingClientPromise) {
194
- return this.signingClientPromise;
195
- }
196
-
197
- // Start initialization and cache the promise to prevent concurrent init
198
- this.signingClientPromise = (async () => {
199
- // Capture reference to detect if superseded by disconnect/config change
200
- const thisInitPromise = this.signingClientPromise;
201
- try {
202
- const signer = await this.walletProvider.getSigner();
203
- const gasPrice = GasPrice.fromString(this.config.gasPrice);
204
- const { registry, aminoTypes } = getSigningManifestClientOptions();
205
-
206
- // Configure endpoint with HTTP timeout
207
- const endpoint: HttpEndpoint = {
208
- url: this.config.rpcUrl,
209
- headers: {},
210
- };
211
-
212
- // Note: Registry type from @cosmjs/proto-signing doesn't perfectly match
213
- // SigningStargateClientOptions due to telescope-generated proto types.
214
- // This is a known limitation with custom cosmos-sdk module registries.
215
- const client = await SigningStargateClient.connectWithSigner(
216
- endpoint,
217
- signer,
218
- {
219
- registry: registry as Parameters<typeof SigningStargateClient.connectWithSigner>[2] extends { registry?: infer R } ? R : never,
220
- aminoTypes,
221
- gasPrice,
222
- broadcastTimeoutMs: DEFAULT_BROADCAST_TIMEOUT_MS,
223
- broadcastPollIntervalMs: DEFAULT_BROADCAST_POLL_INTERVAL_MS,
224
- }
225
- );
226
- // Only store if this is still the active promise
227
- if (this.signingClientPromise === thisInitPromise) {
228
- this.signingClient = client;
229
- this.signingClientPromise = null;
230
- } else {
231
- // Promise was superseded, clean up the client we just created
232
- client.disconnect();
233
- }
234
- return client;
235
- } catch (error) {
236
- // Clear promise on failure so retry is possible (only if still active)
237
- if (this.signingClientPromise === thisInitPromise) {
238
- this.signingClientPromise = null;
239
- }
240
- if (error instanceof ManifestMCPError) {
241
- throw error;
242
- }
243
- throw new ManifestMCPError(
244
- ManifestMCPErrorCode.RPC_CONNECTION_FAILED,
245
- `Failed to connect signing client: ${error instanceof Error ? error.message : String(error)}`,
246
- { rpcUrl: this.config.rpcUrl }
247
- );
248
- }
249
- })();
250
-
251
- return this.signingClientPromise;
252
- }
253
-
254
- /**
255
- * Get the wallet address
256
- */
257
- async getAddress(): Promise<string> {
258
- return this.walletProvider.getAddress();
259
- }
260
-
261
- /**
262
- * Get the configuration
263
- */
264
- getConfig(): ManifestMCPConfig {
265
- return this.config;
266
- }
267
-
268
- /**
269
- * Acquire a rate limit token before making an RPC request.
270
- * This will wait if the rate limit has been exceeded.
271
- */
272
- async acquireRateLimit(): Promise<void> {
273
- await this.rateLimiter.removeTokens(1);
274
- }
275
-
276
- /**
277
- * Disconnect all clients
278
- */
279
- disconnect(): void {
280
- if (this.signingClient) {
281
- this.signingClient.disconnect();
282
- this.signingClient = null;
283
- }
284
- this.signingClientPromise = null;
285
- this.queryClient = null;
286
- this.queryClientPromise = null;
287
- }
288
- }
@@ -1,299 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { createConfig, validateConfig, createValidatedConfig } from './config.js';
3
- import { ManifestMCPError, ManifestMCPErrorCode } from './types.js';
4
-
5
- describe('createConfig', () => {
6
- it('should apply default values', () => {
7
- const config = createConfig({
8
- chainId: 'test-chain',
9
- rpcUrl: 'https://example.com',
10
- gasPrice: '1.0umfx',
11
- });
12
-
13
- expect(config.chainId).toBe('test-chain');
14
- expect(config.rpcUrl).toBe('https://example.com');
15
- expect(config.gasPrice).toBe('1.0umfx');
16
- expect(config.gasAdjustment).toBe(1.3);
17
- expect(config.addressPrefix).toBe('manifest');
18
- });
19
-
20
- it('should preserve provided optional values', () => {
21
- const config = createConfig({
22
- chainId: 'test-chain',
23
- rpcUrl: 'https://example.com',
24
- gasPrice: '1.0umfx',
25
- gasAdjustment: 2.0,
26
- addressPrefix: 'custom',
27
- });
28
-
29
- expect(config.gasAdjustment).toBe(2.0);
30
- expect(config.addressPrefix).toBe('custom');
31
- });
32
-
33
- it('should apply default rateLimit', () => {
34
- const config = createConfig({
35
- chainId: 'test-chain',
36
- rpcUrl: 'https://example.com',
37
- gasPrice: '1.0umfx',
38
- });
39
-
40
- expect(config.rateLimit).toBeDefined();
41
- expect(config.rateLimit?.requestsPerSecond).toBe(10);
42
- });
43
-
44
- it('should preserve provided rateLimit', () => {
45
- const config = createConfig({
46
- chainId: 'test-chain',
47
- rpcUrl: 'https://example.com',
48
- gasPrice: '1.0umfx',
49
- rateLimit: { requestsPerSecond: 20 },
50
- });
51
-
52
- expect(config.rateLimit?.requestsPerSecond).toBe(20);
53
- });
54
- });
55
-
56
- describe('validateConfig', () => {
57
- it('should return valid for correct config', () => {
58
- const result = validateConfig({
59
- chainId: 'manifest-testnet',
60
- rpcUrl: 'https://rpc.example.com',
61
- gasPrice: '1.0umfx',
62
- });
63
-
64
- expect(result.valid).toBe(true);
65
- expect(result.errors).toHaveLength(0);
66
- });
67
-
68
- it('should detect missing required fields', () => {
69
- const result = validateConfig({});
70
-
71
- expect(result.valid).toBe(false);
72
- expect(result.errors).toContain('chainId is required');
73
- expect(result.errors).toContain('rpcUrl is required');
74
- expect(result.errors).toContain('gasPrice is required');
75
- });
76
-
77
- it('should validate chainId format', () => {
78
- const result = validateConfig({
79
- chainId: '-invalid',
80
- rpcUrl: 'https://example.com',
81
- gasPrice: '1.0umfx',
82
- });
83
-
84
- expect(result.valid).toBe(false);
85
- expect(result.errors.some(e => e.includes('chainId'))).toBe(true);
86
- });
87
-
88
- it('should validate rpcUrl format', () => {
89
- const result = validateConfig({
90
- chainId: 'test',
91
- rpcUrl: 'not-a-url',
92
- gasPrice: '1.0umfx',
93
- });
94
-
95
- expect(result.valid).toBe(false);
96
- expect(result.errors.some(e => e.includes('rpcUrl'))).toBe(true);
97
- });
98
-
99
- it('should validate gasPrice format', () => {
100
- const result = validateConfig({
101
- chainId: 'test',
102
- rpcUrl: 'https://example.com',
103
- gasPrice: 'invalid',
104
- });
105
-
106
- expect(result.valid).toBe(false);
107
- expect(result.errors.some(e => e.includes('gasPrice'))).toBe(true);
108
- });
109
-
110
- it('should validate optional gasAdjustment', () => {
111
- const result = validateConfig({
112
- chainId: 'test',
113
- rpcUrl: 'https://example.com',
114
- gasPrice: '1.0umfx',
115
- gasAdjustment: -1,
116
- });
117
-
118
- expect(result.valid).toBe(false);
119
- expect(result.errors.some(e => e.includes('gasAdjustment'))).toBe(true);
120
- });
121
-
122
- it('should validate optional addressPrefix', () => {
123
- const result = validateConfig({
124
- chainId: 'test',
125
- rpcUrl: 'https://example.com',
126
- gasPrice: '1.0umfx',
127
- addressPrefix: 'INVALID',
128
- });
129
-
130
- expect(result.valid).toBe(false);
131
- expect(result.errors.some(e => e.includes('addressPrefix'))).toBe(true);
132
- });
133
-
134
- it('should accept HTTPS URLs', () => {
135
- const result = validateConfig({
136
- chainId: 'test',
137
- rpcUrl: 'https://rpc.example.com',
138
- gasPrice: '1.0umfx',
139
- });
140
-
141
- expect(result.valid).toBe(true);
142
- });
143
-
144
- it('should accept HTTP URLs for localhost', () => {
145
- const result = validateConfig({
146
- chainId: 'test',
147
- rpcUrl: 'http://localhost:26657',
148
- gasPrice: '1.0umfx',
149
- });
150
-
151
- expect(result.valid).toBe(true);
152
- });
153
-
154
- it('should accept HTTP URLs for 127.0.0.1', () => {
155
- const result = validateConfig({
156
- chainId: 'test',
157
- rpcUrl: 'http://127.0.0.1:26657',
158
- gasPrice: '1.0umfx',
159
- });
160
-
161
- expect(result.valid).toBe(true);
162
- });
163
-
164
- it('should accept HTTP URLs for IPv6 localhost', () => {
165
- const result = validateConfig({
166
- chainId: 'test',
167
- rpcUrl: 'http://[::1]:26657',
168
- gasPrice: '1.0umfx',
169
- });
170
-
171
- expect(result.valid).toBe(true);
172
- });
173
-
174
- it('should reject HTTP URLs for non-localhost', () => {
175
- const result = validateConfig({
176
- chainId: 'test',
177
- rpcUrl: 'http://rpc.example.com',
178
- gasPrice: '1.0umfx',
179
- });
180
-
181
- expect(result.valid).toBe(false);
182
- expect(result.errors.some(e => e.includes('HTTPS'))).toBe(true);
183
- });
184
-
185
- it('should accept valid rateLimit config', () => {
186
- const result = validateConfig({
187
- chainId: 'test',
188
- rpcUrl: 'https://example.com',
189
- gasPrice: '1.0umfx',
190
- rateLimit: { requestsPerSecond: 5 },
191
- });
192
-
193
- expect(result.valid).toBe(true);
194
- });
195
-
196
- it('should reject non-integer rateLimit.requestsPerSecond', () => {
197
- const result = validateConfig({
198
- chainId: 'test',
199
- rpcUrl: 'https://example.com',
200
- gasPrice: '1.0umfx',
201
- rateLimit: { requestsPerSecond: 5.5 },
202
- });
203
-
204
- expect(result.valid).toBe(false);
205
- expect(result.errors.some(e => e.includes('requestsPerSecond'))).toBe(true);
206
- });
207
-
208
- it('should reject negative rateLimit.requestsPerSecond', () => {
209
- const result = validateConfig({
210
- chainId: 'test',
211
- rpcUrl: 'https://example.com',
212
- gasPrice: '1.0umfx',
213
- rateLimit: { requestsPerSecond: -1 },
214
- });
215
-
216
- expect(result.valid).toBe(false);
217
- expect(result.errors.some(e => e.includes('requestsPerSecond'))).toBe(true);
218
- });
219
-
220
- it('should reject zero rateLimit.requestsPerSecond', () => {
221
- const result = validateConfig({
222
- chainId: 'test',
223
- rpcUrl: 'https://example.com',
224
- gasPrice: '1.0umfx',
225
- rateLimit: { requestsPerSecond: 0 },
226
- });
227
-
228
- expect(result.valid).toBe(false);
229
- expect(result.errors.some(e => e.includes('requestsPerSecond'))).toBe(true);
230
- });
231
-
232
- it('should reject null rateLimit', () => {
233
- const result = validateConfig({
234
- chainId: 'test',
235
- rpcUrl: 'https://example.com',
236
- gasPrice: '1.0umfx',
237
- rateLimit: null as unknown as { requestsPerSecond?: number },
238
- });
239
-
240
- expect(result.valid).toBe(false);
241
- expect(result.errors.some(e => e.includes('rateLimit must be a plain object'))).toBe(true);
242
- });
243
-
244
- it('should reject non-object rateLimit', () => {
245
- const result = validateConfig({
246
- chainId: 'test',
247
- rpcUrl: 'https://example.com',
248
- gasPrice: '1.0umfx',
249
- rateLimit: 'invalid' as unknown as { requestsPerSecond?: number },
250
- });
251
-
252
- expect(result.valid).toBe(false);
253
- expect(result.errors.some(e => e.includes('rateLimit must be a plain object'))).toBe(true);
254
- });
255
-
256
- it('should reject array rateLimit', () => {
257
- const result = validateConfig({
258
- chainId: 'test',
259
- rpcUrl: 'https://example.com',
260
- gasPrice: '1.0umfx',
261
- rateLimit: [] as unknown as { requestsPerSecond?: number },
262
- });
263
-
264
- expect(result.valid).toBe(false);
265
- expect(result.errors.some(e => e.includes('rateLimit must be a plain object'))).toBe(true);
266
- });
267
- });
268
-
269
- describe('createValidatedConfig', () => {
270
- it('should return config for valid input', () => {
271
- const config = createValidatedConfig({
272
- chainId: 'test-chain',
273
- rpcUrl: 'https://example.com',
274
- gasPrice: '1.0umfx',
275
- });
276
-
277
- expect(config.chainId).toBe('test-chain');
278
- });
279
-
280
- it('should throw ManifestMCPError for invalid input', () => {
281
- expect(() => createValidatedConfig({
282
- chainId: '',
283
- rpcUrl: '',
284
- gasPrice: '',
285
- })).toThrow(ManifestMCPError);
286
- });
287
-
288
- it('should have INVALID_CONFIG error code', () => {
289
- try {
290
- createValidatedConfig({
291
- chainId: '',
292
- rpcUrl: '',
293
- gasPrice: '',
294
- });
295
- } catch (error) {
296
- expect((error as ManifestMCPError).code).toBe(ManifestMCPErrorCode.INVALID_CONFIG);
297
- }
298
- });
299
- });