ethershell 0.1.1-alpha.15 → 0.1.2-beta.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.
@@ -15,12 +15,15 @@ import {
15
15
  extractLoadableVersion
16
16
  } from '../utils/builder.js';
17
17
  import fs from 'fs';
18
+ import {
19
+ generateAllTypes,
20
+ } from '../utils/typeGenerator.js';
18
21
 
19
22
  /**
20
- * Stored compiler config path
23
+ * Stored config path
21
24
  * @type {string}
22
25
  */
23
- const compConfigPath = './localStorage/compilerConfig.json';
26
+ export const configPath = './ethershell/config.json';
24
27
 
25
28
  /**
26
29
  * Global compiler configuration state
@@ -40,7 +43,11 @@ let compConfig = {};
40
43
  * @property {number} optimizerRuns - Number of optimizer runs
41
44
  * @property {boolean} viaIR - Whether to use IR-based code generation
42
45
  */
43
- export let compConfigFile = {};
46
+ export let configFile = {
47
+ providerEndpoint: '',
48
+ defaultWallet: {},
49
+ compiler: {}
50
+ };
44
51
 
45
52
  /**
46
53
  * Global compiler configuration state
@@ -52,33 +59,38 @@ export let compConfigFile = {};
52
59
  let storedCompConfig;
53
60
 
54
61
  // Load config file
55
- if(fs.existsSync(compConfigPath)){
56
- storedCompConfig = JSON.parse(fs.readFileSync(compConfigPath));
62
+ if(fs.existsSync(configPath)){
63
+ storedCompConfig = JSON.parse(fs.readFileSync(configPath));
57
64
  } else {
58
65
  storedCompConfig = null;
59
66
  }
60
67
 
61
68
  // Initialize global configuration of compiler
62
69
  if(storedCompConfig){
63
- compConfigFile = storedCompConfig;
64
- compConfig.currentSolcInstance = await loadSolcVersion(compConfigFile.version);
65
- compConfig.optimizer = compConfigFile.optimizer;
66
- compConfig.viaIR = compConfigFile.viaIR;
67
- compConfig.optimizerRuns = compConfigFile.optimizerRuns;
70
+ configFile.compiler = storedCompConfig.compiler;
71
+ console.info(`Compiler is loading ...`);
72
+ compConfig.currentSolcInstance = await loadSolcVersion(configFile.compiler.version);
73
+ console.info(`Loading done!`);
74
+ compConfig.optimizer = configFile.compiler.optimizer;
75
+ compConfig.viaIR = configFile.compiler.viaIR;
76
+ compConfig.optimizerRuns = configFile.compiler.optimizerRuns;
77
+ compConfig.compilePath = configFile.compiler.compilePath;
68
78
  } else {
69
79
  compConfig = {
70
80
  currentSolcInstance: solc, // default local compiler
71
81
  optimizer: false,
72
82
  viaIR: false,
73
- optimizerRuns: 200
83
+ optimizerRuns: 200,
84
+ compilePath: './build'
74
85
  }
75
- compConfigFile.version = extractLoadableVersion(compConfig.currentSolcInstance.version());
76
- compConfigFile.optimizer = compConfig.optimizer;
77
- compConfigFile.viaIR = compConfig.viaIR;
78
- compConfigFile.optimizerRuns = compConfig.optimizerRuns;
86
+ configFile.compiler.version = extractLoadableVersion(compConfig.currentSolcInstance.version());
87
+ configFile.compiler.optimizer = compConfig.optimizer;
88
+ configFile.compiler.viaIR = compConfig.viaIR;
89
+ configFile.compiler.optimizerRuns = compConfig.optimizerRuns;
90
+ configFile.compiler.compilePath = compConfig.compilePath;
79
91
 
80
92
  // Update config file
81
- fs.writeFileSync(compConfigPath, JSON.stringify(compConfigFile, null, 2));
93
+ fs.writeFileSync(configPath, JSON.stringify(configFile, null, 2));
82
94
  }
83
95
 
84
96
  /**
@@ -96,8 +108,8 @@ export async function updateCompiler(version){
96
108
  compConfig.currentSolcInstance = await setVersion(version, compConfig.currentSolcInstance);
97
109
 
98
110
  // Update config file
99
- compConfigFile.version = extractLoadableVersion(compConfig.currentSolcInstance.version());
100
- fs.writeFileSync(compConfigPath, JSON.stringify(compConfigFile, null, 2));
111
+ configFile.compiler.version = extractLoadableVersion(compConfig.currentSolcInstance.version());
112
+ fs.writeFileSync(configPath, JSON.stringify(configFile, null, 2));
101
113
  } catch(err) {
102
114
  console.error(err);
103
115
  }
@@ -141,20 +153,20 @@ export function compilerOptions(gasOptimizer, viaIR, optimizerRuns = 200) {
141
153
  compConfig.viaIR = viaIR;
142
154
  compConfig.optimizerRuns = optimizerRuns;
143
155
 
144
- compConfigFile.optimizer = compConfig.optimizer;
145
- compConfigFile.viaIR = compConfig.viaIR;
146
- compConfigFile.optimizerRuns = compConfig.optimizerRuns;
147
-
148
156
  // Update config file
149
- fs.writeFileSync(compConfigPath, JSON.stringify(compConfigFile, null, 2));
157
+ configFile.compiler.optimizer = compConfig.optimizer;
158
+ configFile.compiler.viaIR = compConfig.viaIR;
159
+ configFile.compiler.optimizerRuns = compConfig.optimizerRuns;
160
+ fs.writeFileSync(configPath, JSON.stringify(configFile, null, 2));
150
161
 
151
162
  // Provide user feedback
152
163
  console.log('✓ Compiler options updated:');
153
164
  console.log(` Gas Optimizer: ${compConfig.optimizer ? 'Enabled' : 'Disabled'}`);
165
+ console.log(` ViaIR: ${compConfig.viaIR ? 'Enabled' : 'Disabled'}`);
154
166
  if (compConfig.optimizer) {
155
167
  console.log(` Optimizer Runs: ${compConfig.optimizerRuns}`);
156
168
  }
157
- console.log(` ViaIR: ${compConfig.viaIR ? 'Enabled' : 'Disabled'}`);
169
+
158
170
  } catch (error) {
159
171
  console.error('Error setting compiler options:', error.message);
160
172
  return null;
@@ -168,7 +180,7 @@ export function compilerOptions(gasOptimizer, viaIR, optimizerRuns = 200) {
168
180
  * const opts = getCompilerOptions();
169
181
  */
170
182
  export function getCompilerOptions() {
171
- return { ...compConfig };
183
+ return { ...configFile.compiler };
172
184
  }
173
185
 
174
186
  /**
@@ -187,7 +199,7 @@ export function compile(fullPath, selectedContracts, buildPath){
187
199
  try{
188
200
  // Set default path if buildPath is undefined
189
201
  if(!buildPath){
190
- buildPath = path.resolve('.', 'build');
202
+ buildPath = compConfig.compilePath;
191
203
  [buildPath].forEach(check);
192
204
  }
193
205
 
@@ -213,7 +225,22 @@ export function compile(fullPath, selectedContracts, buildPath){
213
225
  build(fullPath, selectedContracts, buildPath);
214
226
  console.log(`Contract compiled into ${path.resolve(buildPath)}`);
215
227
  }
228
+
229
+ // Generate TypeScript types
230
+ const typesOutputPath = path.join(buildPath, 'types');
231
+ generateAllTypes(buildPath, typesOutputPath);
232
+ console.log(`TypeScript types generated in ${path.resolve(typesOutputPath)}`);
216
233
  } catch(err){
217
234
  console.error(err);
218
235
  }
236
+ }
237
+
238
+ /**
239
+ * Changes the default path to generate build files there
240
+ * @param {string} newPath - The new path to build
241
+ */
242
+ export function changeCompPath(newPath) {
243
+ compConfig.compilePath = newPath;
244
+ configFile.compiler.compilePath = compConfig.compilePath;
245
+ fs.writeFileSync(configPath, JSON.stringify(configFile, null, 2));
219
246
  }
@@ -1,5 +1,15 @@
1
- import { compConfigFile } from './build.js';
1
+ import { configFile } from './build.js';
2
2
 
3
+ /**
4
+ * Gets all fields of config file
5
+ */
3
6
  export function getConfigInfo() {
4
- console.log(compConfigFile);
7
+ console.log(configFile);
8
+ }
9
+
10
+ /**
11
+ * Gets just default account of config file
12
+ */
13
+ export function getDefaultAccount() {
14
+ console.log(configFile.defaultWallet);
5
15
  }
@@ -19,12 +19,13 @@ import path from 'path';
19
19
  export function deleteDirectory(dirPath){
20
20
  try {
21
21
  if(!dirPath){
22
- dirPath = path.resolve('..', 'build');
22
+ dirPath = path.resolve('.', 'build');
23
23
  }
24
24
  // Check if the directory exists
25
25
  if(!fs.existsSync(dirPath)){
26
26
  console.log('Path is not a directory');
27
- return; }
27
+ return;
28
+ }
28
29
 
29
30
  // For Node.js 14.14.0+ (recommended)
30
31
  fs.rmSync(dirPath, { recursive: true, force: true });
@@ -6,13 +6,9 @@
6
6
  */
7
7
 
8
8
  import { ethers } from 'ethers';
9
- import { LocalStorage } from 'node-localstorage';
10
-
11
- /**
12
- * Local storage instance for persisting compiler artifacts paths
13
- * @type {LocalStorage}
14
- */
15
- const localStorage = new LocalStorage('./localStorage');
9
+ import { configPath } from './build.js';
10
+ import fs from 'fs';
11
+ import { changeProvider } from '../utils/configFileUpdate.js';
16
12
 
17
13
  /**
18
14
  * Default JSON-RPC URL for local Ethereum node
@@ -37,13 +33,14 @@ export let provider
37
33
  * The specific RPC endpoint URL saved on storage before.
38
34
  * @type {string}
39
35
  */
40
- const storedUrl = localStorage.getItem('url');
36
+ const storedUrl = JSON.parse(fs.readFileSync(configPath)).providerEndpoint;
41
37
  if(storedUrl) {
42
- provider = new ethers.JsonRpcProvider(storedUrl);;
43
- currentUrl = storedUrl
38
+ provider = new ethers.JsonRpcProvider(storedUrl);
39
+ currentUrl = storedUrl;
44
40
  } else {
45
41
  provider = new ethers.JsonRpcProvider(defaultUrl);
46
42
  currentUrl = defaultUrl;
43
+ changeProvider(currentUrl);
47
44
  }
48
45
 
49
46
 
@@ -66,7 +63,7 @@ export async function set(url){
66
63
  name: result.name,
67
64
  chainId: result.chainId
68
65
  }
69
- localStorage.setItem('url', url);
66
+ changeProvider(currentUrl);
70
67
  console.log(network);
71
68
  }catch(err){
72
69
  console.error(err);
@@ -15,15 +15,28 @@ import {
15
15
  detectDupWallet,
16
16
  updateWalletJSON,
17
17
  getWalletJSON,
18
- updateAccountMemory
18
+ updateAccountMemory,
19
+ setDefaultAccount
19
20
  } from '../utils/accounter.js';
21
+ import { configPath } from './build.js';
22
+ import fs from 'fs';
20
23
 
21
24
  /**
22
25
  * Array containing all accounts (imported, generated, HD, and node-managed)
23
26
  * @type {Array<Object>}
24
27
  */
25
- export let allAccounts = [];
26
- allAccounts = getWalletJSON();
28
+ export let allAccounts = getWalletJSON();
29
+
30
+ // Set the default account from stored wallets
31
+ const defWallet = JSON.parse(fs.readFileSync(configPath)).defaultWallet;
32
+ if(defWallet.address) {
33
+ setDefaultAccount(defWallet);
34
+ } else {
35
+ if(allAccounts && allAccounts.length > 0) {
36
+ setDefaultAccount(allAccounts[0]);
37
+ }
38
+ }
39
+
27
40
 
28
41
  /**
29
42
  * Array containing only regular accounts (imported and generated)
@@ -101,6 +114,7 @@ export function addAccounts(privKeyArr) {
101
114
 
102
115
  console.log(allAccounts.slice(newFrom));
103
116
  }
117
+ setDefaultAccount(allAccounts[0]);
104
118
  }
105
119
 
106
120
  /**
@@ -142,6 +156,7 @@ export function addHD(phrase, count = 10) {
142
156
  }
143
157
  }
144
158
  updateWalletJSON(allAccounts);
159
+ setDefaultAccount(allAccounts[0]);
145
160
  console.log(`!WARNING!\n The generated accounts are NOT safe. Do NOT use them on main net!`);
146
161
  console.log(allAccounts.slice(newFrom));
147
162
  }
@@ -171,6 +186,7 @@ export function createAccounts(count = 1) {
171
186
 
172
187
  }
173
188
  updateWalletJSON(allAccounts);
189
+ setDefaultAccount(allAccounts[0]);
174
190
  console.log(`!WARNING!\n The generated accounts are NOT safe. Do NOT use them on main net!`);
175
191
  console.log(allAccounts.slice(newFrom));
176
192
  }
@@ -205,6 +221,7 @@ export function createHD(count = 10) {
205
221
  hdAccounts.push(newAccObj);
206
222
  }
207
223
  updateWalletJSON(allAccounts);
224
+ setDefaultAccount(allAccounts[0]);
208
225
  console.log(`!WARNING!\n The generated accounts are NOT safe. Do NOT use them on main net!`);
209
226
  console.log(allAccounts.slice(newFrom));
210
227
  }
@@ -362,3 +379,43 @@ export async function getWalletInfo(accPointer) {
362
379
  console.error(err);
363
380
  }
364
381
  }
382
+
383
+ /**
384
+ * Changes the default account in config file by user
385
+ * @param {number | string} accPointer
386
+ * @throws {Error} If input is empty or invalid
387
+ */
388
+ export function changeDefaultAccount(accPointer) {
389
+ try {
390
+ if(!accPointer && accPointer != 0) {
391
+ throw new Error('Error: Empty input is NOT valid!');
392
+ }
393
+
394
+ if(typeof accPointer === 'number') {
395
+ const index = allAccounts.findIndex(wallet => wallet.index == accPointer);
396
+ setDefaultAccount(allAccounts[index]);
397
+ }
398
+
399
+ if(ethers.isAddress(accPointer)) {
400
+ const index = allAccounts.findIndex(wallet => wallet.address == accPointer);
401
+ setDefaultAccount(allAccounts[index]);
402
+ }
403
+
404
+ if(ethers.isHexString(accPointer, 32)) {
405
+ const newAccount = new ethers.Wallet(accPointer, provider);
406
+ const newAccObj = {
407
+ index: allAccounts.length,
408
+ address: newAccount.address,
409
+ privateKey: accPointer,
410
+ type: 'user-imported',
411
+ contracts: []
412
+ }
413
+ allAccounts.push(newAccObj);
414
+ accounts.push(newAccObj);
415
+ setDefaultAccount(newAccObj);
416
+ }
417
+
418
+ } catch(err) {
419
+ console.error(err);
420
+ }
421
+ }
@@ -8,12 +8,14 @@
8
8
  import { allAccounts, accounts, hdAccounts } from '../services/wallet.js';
9
9
  import { provider } from '../services/network.js';
10
10
  import fs from 'fs';
11
+ import { configFile, configPath } from '../services/build.js';
12
+ import { serializeBigInts } from './serialize.js';
11
13
 
12
14
  /**
13
15
  * The path which in wallets json file will be saved.
14
16
  * @type {String}
15
17
  */
16
- const walletJSONPath = './localStorage/wallets.json';
18
+ const walletJSONPath = './ethershell/wallets.json';
17
19
 
18
20
  /**
19
21
  * Delete account(s) by index
@@ -104,7 +106,8 @@ export function detectDupWallet(privKeyArr) {
104
106
  }]);
105
107
  */
106
108
  export function updateWalletJSON(walletArr) {
107
- fs.writeFileSync(walletJSONPath, JSON.stringify(walletArr, null, 2));
109
+ const walletObj = serializeBigInts(walletArr);
110
+ fs.writeFileSync(walletJSONPath, JSON.stringify(walletObj, null, 2));
108
111
  }
109
112
 
110
113
  /**
@@ -112,7 +115,20 @@ export function updateWalletJSON(walletArr) {
112
115
  * @returns {Object}
113
116
  */
114
117
  export function getWalletJSON() {
115
- return JSON.parse(fs.readFileSync(walletJSONPath));
118
+ if(fs.existsSync(walletJSONPath)){
119
+ const walletJSON = fs.readFileSync(walletJSONPath, 'utf8');
120
+ // Return empry array if wallet is empty
121
+ if(walletJSON.length === 0) {
122
+ return [];
123
+ } else {
124
+ return JSON.parse(fs.readFileSync(walletJSONPath));
125
+ }
126
+ } else {
127
+ // Generate empty wallet.json if it doesn't exist
128
+ const fd = fs.openSync(walletJSONPath, 'w');
129
+ fs.closeSync(fd);
130
+ return [];
131
+ }
116
132
  }
117
133
 
118
134
  export function updateAccountMemory(allAccArr) {
@@ -268,4 +284,13 @@ function _deleteAll() {
268
284
  hdAccounts.splice(0);
269
285
  fs.writeFileSync(walletJSONPath, JSON.stringify([], null, 2));
270
286
  return;
271
- }
287
+ }
288
+
289
+ /**
290
+ * Updates default account in config file
291
+ * @param {Object} account - // The given account to set as default account
292
+ */
293
+ export function setDefaultAccount(account) {
294
+ configFile.defaultWallet = serializeBigInts(account);
295
+ fs.writeFileSync(configPath, JSON.stringify(configFile, null, 2));
296
+ }
@@ -16,7 +16,7 @@ import { LocalStorage } from 'node-localstorage';
16
16
  * Local storage instance for persisting compiler artifacts paths
17
17
  * @type {LocalStorage}
18
18
  */
19
- const localStorage = new LocalStorage('./localStorage');
19
+ const localStorage = new LocalStorage('./ethershell');
20
20
 
21
21
  /**
22
22
  * Load a specific version of the Solidity compiler
@@ -0,0 +1,7 @@
1
+ import { configFile, configPath } from '../services/build.js';
2
+ import fs from 'fs';
3
+
4
+ export function changeProvider(url) {
5
+ configFile.providerEndpoint = url;
6
+ fs.writeFileSync(configPath, JSON.stringify(configFile, null, 2));
7
+ }
@@ -0,0 +1,162 @@
1
+ // src/utils/contractProxy.js
2
+ import { ethers } from 'ethers';
3
+
4
+ /**
5
+ * Creates a proxy wrapper for ethers.js Contract objects
6
+ * Allows dynamic sender changes and comprehensive transaction options
7
+ *
8
+ * @param {ethers.Contract} contract - The ethers.js contract instance
9
+ * @param {ethers.JsonRpcProvider} provider - The blockchain provider
10
+ * @param {Array} allAccounts - Array of available accounts with privateKey property
11
+ * @returns {Proxy} Proxied contract object
12
+ *
13
+ * @example
14
+ * // Call with comprehensive options
15
+ * await Payment.spend(amount, {
16
+ * from: '0x...',
17
+ * value: ethers.parseEther('1'),
18
+ * gasLimit: 500000,
19
+ * maxFeePerGas: ethers.parseUnits('100', 'gwei'),
20
+ * maxPriorityFeePerGas: ethers.parseUnits('2', 'gwei'),
21
+ * nonce: 42,
22
+ * chainId: 1
23
+ * })
24
+ */
25
+ export function createContractProxy(contract, provider, allAccounts) {
26
+ return new Proxy(contract, {
27
+ get(target, prop) {
28
+ // Pass through non-function properties
29
+ if (typeof target[prop] !== 'function') {
30
+ return target[prop];
31
+ }
32
+
33
+ // Return a wrapper for contract methods
34
+ return async function(...args) {
35
+ // Extract options from last argument if it's an object with custom properties
36
+ const lastArg = args[args.length - 1];
37
+
38
+ // List of all valid transaction option keys (except 'data', 'to', 'from' which are handled internally)
39
+ const validTxOptions = [
40
+ 'value',
41
+ 'nonce',
42
+ 'gasLimit',
43
+ 'gas', // Maps to gasLimit
44
+ 'gasPrice',
45
+ 'maxFeePerGas',
46
+ 'maxPriorityFeePerGas',
47
+ 'chainId',
48
+ 'accessList',
49
+ 'type',
50
+ 'customData',
51
+ 'from' // Special handling for signer switching
52
+ ];
53
+
54
+ const hasOptions =
55
+ lastArg &&
56
+ typeof lastArg === 'object' &&
57
+ !Array.isArray(lastArg) &&
58
+ Object.keys(lastArg).some(key => validTxOptions.includes(key));
59
+
60
+ // Warn if user tries to pass 'data'
61
+ if (lastArg && typeof lastArg === 'object' && lastArg.data) {
62
+ console.warn(
63
+ "Warning: 'data' option is ignored. " +
64
+ "Function calldata is automatically encoded by ethers.js. " +
65
+ "Use .connect() to change the signer if needed."
66
+ );
67
+ }
68
+
69
+ const options = hasOptions ? args.pop() : null;
70
+
71
+ let method = target[prop];
72
+ let txOptions = {};
73
+
74
+ // Handle 'from' option - switch signer if specified
75
+ if (options && options.from) {
76
+ const account = allAccounts.find(
77
+ acc => acc.address?.toLowerCase() === options.from.toLowerCase()
78
+ );
79
+
80
+ if (!account) {
81
+ throw new Error(
82
+ `Account ${options.from} not found in registered accounts`
83
+ );
84
+ }
85
+
86
+ if (!account.privateKey) {
87
+ throw new Error(
88
+ `Account ${options.from} is a node-managed account and cannot be used with {from}`
89
+ );
90
+ }
91
+
92
+ // Create new signer with the specified account
93
+ const newSigner = new ethers.Wallet(account.privateKey, provider);
94
+ const connectedContract = target.connect(newSigner);
95
+ method = connectedContract[prop];
96
+ }
97
+
98
+ // Build transaction options object with all supported ethers.js v6 options
99
+ if (options) {
100
+ // Value (for payable functions)
101
+ if (options.value !== undefined) {
102
+ txOptions.value = options.value;
103
+ }
104
+
105
+ // Gas limit (handle both 'gas' and 'gasLimit')
106
+ if (options.gasLimit !== undefined) {
107
+ txOptions.gasLimit = options.gasLimit;
108
+ } else if (options.gas !== undefined) {
109
+ txOptions.gasLimit = options.gas; // Map 'gas' to 'gasLimit'
110
+ }
111
+
112
+ // Legacy gas price
113
+ if (options.gasPrice !== undefined) {
114
+ txOptions.gasPrice = options.gasPrice;
115
+ }
116
+
117
+ // EIP-1559 options
118
+ if (options.maxFeePerGas !== undefined) {
119
+ txOptions.maxFeePerGas = options.maxFeePerGas;
120
+ }
121
+
122
+ if (options.maxPriorityFeePerGas !== undefined) {
123
+ txOptions.maxPriorityFeePerGas = options.maxPriorityFeePerGas;
124
+ }
125
+
126
+ // Nonce for transaction ordering
127
+ if (options.nonce !== undefined) {
128
+ txOptions.nonce = options.nonce;
129
+ }
130
+
131
+ // Chain ID
132
+ if (options.chainId !== undefined) {
133
+ txOptions.chainId = options.chainId;
134
+ }
135
+
136
+ // EIP-2930 access list
137
+ if (options.accessList !== undefined) {
138
+ txOptions.accessList = options.accessList;
139
+ }
140
+
141
+ // Transaction type
142
+ if (options.type !== undefined) {
143
+ txOptions.type = options.type;
144
+ }
145
+
146
+ // Custom data (for special networks like zkSync)
147
+ if (options.customData !== undefined) {
148
+ txOptions.customData = options.customData;
149
+ }
150
+ }
151
+
152
+ // If there are transaction options, pass them as the last argument
153
+ if (Object.keys(txOptions).length > 0) {
154
+ args.push(txOptions);
155
+ }
156
+
157
+ // Call the method with remaining args and tx options
158
+ return method.apply(method, args);
159
+ };
160
+ }
161
+ });
162
+ }
package/src/utils/dir.js CHANGED
@@ -57,6 +57,16 @@ export function collectSolFiles(dirPath) {
57
57
  */
58
58
  export function findImports(importPath, basePath) {
59
59
  try {
60
+ // Handle node_modules imports (e.g., @openzeppelin/contracts/...)
61
+ if (importPath.startsWith('@')) {
62
+ const nodeModulesPath = path.resolve(process.cwd(), 'node_modules', importPath);
63
+
64
+ if (fs.existsSync(nodeModulesPath)) {
65
+ const content = fs.readFileSync(nodeModulesPath, 'utf8');
66
+ return { contents: content };
67
+ }
68
+ }
69
+
60
70
  // Resolve relative to the current file's directory
61
71
  const resolvedPath = path.resolve(basePath, importPath);
62
72
 
@@ -0,0 +1,17 @@
1
+ export function serializeBigInts(obj) {
2
+ try{
3
+ if (Array.isArray(obj)) {
4
+ return obj.map(serializeBigInts);
5
+ } else if (obj && typeof obj === 'object') {
6
+ return Object.fromEntries(
7
+ Object.entries(obj).map(([key, value]) => [
8
+ key,
9
+ typeof value === 'bigint' ? value.toString() : serializeBigInts(value)
10
+ ])
11
+ );
12
+ }
13
+ return obj;
14
+ }catch(err){
15
+ console.error(err);
16
+ }
17
+ }