create-microact-app 1.0.1

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 (213) hide show
  1. package/index.js +95 -0
  2. package/package.json +21 -0
  3. package/templates/vanilla/.github/workflows/deploy.yml +38 -0
  4. package/templates/vanilla/index.html +13 -0
  5. package/templates/vanilla/node_modules/.package-lock.json +207 -0
  6. package/templates/vanilla/node_modules/@esbuild/darwin-x64/README.md +3 -0
  7. package/templates/vanilla/node_modules/@esbuild/darwin-x64/bin/esbuild +0 -0
  8. package/templates/vanilla/node_modules/@esbuild/darwin-x64/package.json +17 -0
  9. package/templates/vanilla/node_modules/@monygroupcorp/microact/README.md +154 -0
  10. package/templates/vanilla/node_modules/@monygroupcorp/microact/dist/microact.cjs.js +1749 -0
  11. package/templates/vanilla/node_modules/@monygroupcorp/microact/dist/microact.cjs.js.map +1 -0
  12. package/templates/vanilla/node_modules/@monygroupcorp/microact/dist/microact.esm.js +1743 -0
  13. package/templates/vanilla/node_modules/@monygroupcorp/microact/dist/microact.esm.js.map +1 -0
  14. package/templates/vanilla/node_modules/@monygroupcorp/microact/dist/microact.umd.js +2 -0
  15. package/templates/vanilla/node_modules/@monygroupcorp/microact/dist/microact.umd.js.map +1 -0
  16. package/templates/vanilla/node_modules/@monygroupcorp/microact/example/index.html +13 -0
  17. package/templates/vanilla/node_modules/@monygroupcorp/microact/example/index.js +63 -0
  18. package/templates/vanilla/node_modules/@monygroupcorp/microact/package.json +38 -0
  19. package/templates/vanilla/node_modules/@monygroupcorp/microact/rollup.config.cjs +30 -0
  20. package/templates/vanilla/node_modules/@monygroupcorp/microact/src/Component.js +831 -0
  21. package/templates/vanilla/node_modules/@monygroupcorp/microact/src/DOMUpdater.js +320 -0
  22. package/templates/vanilla/node_modules/@monygroupcorp/microact/src/EventBus.js +123 -0
  23. package/templates/vanilla/node_modules/@monygroupcorp/microact/src/Router.js +253 -0
  24. package/templates/vanilla/node_modules/@monygroupcorp/microact/src/UpdateScheduler.js +218 -0
  25. package/templates/vanilla/node_modules/@monygroupcorp/microact/src/index.js +6 -0
  26. package/templates/vanilla/node_modules/esbuild/LICENSE.md +21 -0
  27. package/templates/vanilla/node_modules/esbuild/README.md +3 -0
  28. package/templates/vanilla/node_modules/esbuild/bin/esbuild +0 -0
  29. package/templates/vanilla/node_modules/esbuild/install.js +287 -0
  30. package/templates/vanilla/node_modules/esbuild/lib/main.d.ts +660 -0
  31. package/templates/vanilla/node_modules/esbuild/lib/main.js +2393 -0
  32. package/templates/vanilla/node_modules/esbuild/package.json +42 -0
  33. package/templates/vanilla/node_modules/nanoid/LICENSE +20 -0
  34. package/templates/vanilla/node_modules/nanoid/README.md +39 -0
  35. package/templates/vanilla/node_modules/nanoid/async/index.browser.cjs +69 -0
  36. package/templates/vanilla/node_modules/nanoid/async/index.browser.js +34 -0
  37. package/templates/vanilla/node_modules/nanoid/async/index.cjs +71 -0
  38. package/templates/vanilla/node_modules/nanoid/async/index.d.ts +56 -0
  39. package/templates/vanilla/node_modules/nanoid/async/index.js +35 -0
  40. package/templates/vanilla/node_modules/nanoid/async/index.native.js +26 -0
  41. package/templates/vanilla/node_modules/nanoid/async/package.json +12 -0
  42. package/templates/vanilla/node_modules/nanoid/bin/nanoid.cjs +55 -0
  43. package/templates/vanilla/node_modules/nanoid/index.browser.cjs +72 -0
  44. package/templates/vanilla/node_modules/nanoid/index.browser.js +34 -0
  45. package/templates/vanilla/node_modules/nanoid/index.cjs +85 -0
  46. package/templates/vanilla/node_modules/nanoid/index.d.cts +91 -0
  47. package/templates/vanilla/node_modules/nanoid/index.d.ts +91 -0
  48. package/templates/vanilla/node_modules/nanoid/index.js +45 -0
  49. package/templates/vanilla/node_modules/nanoid/nanoid.js +1 -0
  50. package/templates/vanilla/node_modules/nanoid/non-secure/index.cjs +34 -0
  51. package/templates/vanilla/node_modules/nanoid/non-secure/index.d.ts +33 -0
  52. package/templates/vanilla/node_modules/nanoid/non-secure/index.js +21 -0
  53. package/templates/vanilla/node_modules/nanoid/non-secure/package.json +6 -0
  54. package/templates/vanilla/node_modules/nanoid/package.json +89 -0
  55. package/templates/vanilla/node_modules/nanoid/url-alphabet/index.cjs +7 -0
  56. package/templates/vanilla/node_modules/nanoid/url-alphabet/index.js +3 -0
  57. package/templates/vanilla/node_modules/nanoid/url-alphabet/package.json +6 -0
  58. package/templates/vanilla/node_modules/picocolors/LICENSE +15 -0
  59. package/templates/vanilla/node_modules/picocolors/README.md +21 -0
  60. package/templates/vanilla/node_modules/picocolors/package.json +25 -0
  61. package/templates/vanilla/node_modules/picocolors/picocolors.browser.js +4 -0
  62. package/templates/vanilla/node_modules/picocolors/picocolors.d.ts +5 -0
  63. package/templates/vanilla/node_modules/picocolors/picocolors.js +75 -0
  64. package/templates/vanilla/node_modules/picocolors/types.d.ts +51 -0
  65. package/templates/vanilla/node_modules/postcss/LICENSE +20 -0
  66. package/templates/vanilla/node_modules/postcss/README.md +29 -0
  67. package/templates/vanilla/node_modules/postcss/lib/at-rule.d.ts +140 -0
  68. package/templates/vanilla/node_modules/postcss/lib/at-rule.js +25 -0
  69. package/templates/vanilla/node_modules/postcss/lib/comment.d.ts +68 -0
  70. package/templates/vanilla/node_modules/postcss/lib/comment.js +13 -0
  71. package/templates/vanilla/node_modules/postcss/lib/container.d.ts +483 -0
  72. package/templates/vanilla/node_modules/postcss/lib/container.js +447 -0
  73. package/templates/vanilla/node_modules/postcss/lib/css-syntax-error.d.ts +248 -0
  74. package/templates/vanilla/node_modules/postcss/lib/css-syntax-error.js +133 -0
  75. package/templates/vanilla/node_modules/postcss/lib/declaration.d.ts +151 -0
  76. package/templates/vanilla/node_modules/postcss/lib/declaration.js +24 -0
  77. package/templates/vanilla/node_modules/postcss/lib/document.d.ts +69 -0
  78. package/templates/vanilla/node_modules/postcss/lib/document.js +33 -0
  79. package/templates/vanilla/node_modules/postcss/lib/fromJSON.d.ts +9 -0
  80. package/templates/vanilla/node_modules/postcss/lib/fromJSON.js +54 -0
  81. package/templates/vanilla/node_modules/postcss/lib/input.d.ts +227 -0
  82. package/templates/vanilla/node_modules/postcss/lib/input.js +265 -0
  83. package/templates/vanilla/node_modules/postcss/lib/lazy-result.d.ts +190 -0
  84. package/templates/vanilla/node_modules/postcss/lib/lazy-result.js +550 -0
  85. package/templates/vanilla/node_modules/postcss/lib/list.d.ts +60 -0
  86. package/templates/vanilla/node_modules/postcss/lib/list.js +58 -0
  87. package/templates/vanilla/node_modules/postcss/lib/map-generator.js +368 -0
  88. package/templates/vanilla/node_modules/postcss/lib/no-work-result.d.ts +46 -0
  89. package/templates/vanilla/node_modules/postcss/lib/no-work-result.js +138 -0
  90. package/templates/vanilla/node_modules/postcss/lib/node.d.ts +556 -0
  91. package/templates/vanilla/node_modules/postcss/lib/node.js +449 -0
  92. package/templates/vanilla/node_modules/postcss/lib/parse.d.ts +9 -0
  93. package/templates/vanilla/node_modules/postcss/lib/parse.js +42 -0
  94. package/templates/vanilla/node_modules/postcss/lib/parser.js +611 -0
  95. package/templates/vanilla/node_modules/postcss/lib/postcss.d.mts +69 -0
  96. package/templates/vanilla/node_modules/postcss/lib/postcss.d.ts +458 -0
  97. package/templates/vanilla/node_modules/postcss/lib/postcss.js +101 -0
  98. package/templates/vanilla/node_modules/postcss/lib/postcss.mjs +30 -0
  99. package/templates/vanilla/node_modules/postcss/lib/previous-map.d.ts +81 -0
  100. package/templates/vanilla/node_modules/postcss/lib/previous-map.js +144 -0
  101. package/templates/vanilla/node_modules/postcss/lib/processor.d.ts +115 -0
  102. package/templates/vanilla/node_modules/postcss/lib/processor.js +67 -0
  103. package/templates/vanilla/node_modules/postcss/lib/result.d.ts +205 -0
  104. package/templates/vanilla/node_modules/postcss/lib/result.js +42 -0
  105. package/templates/vanilla/node_modules/postcss/lib/root.d.ts +87 -0
  106. package/templates/vanilla/node_modules/postcss/lib/root.js +61 -0
  107. package/templates/vanilla/node_modules/postcss/lib/rule.d.ts +126 -0
  108. package/templates/vanilla/node_modules/postcss/lib/rule.js +27 -0
  109. package/templates/vanilla/node_modules/postcss/lib/stringifier.d.ts +46 -0
  110. package/templates/vanilla/node_modules/postcss/lib/stringifier.js +353 -0
  111. package/templates/vanilla/node_modules/postcss/lib/stringify.d.ts +9 -0
  112. package/templates/vanilla/node_modules/postcss/lib/stringify.js +11 -0
  113. package/templates/vanilla/node_modules/postcss/lib/symbols.js +5 -0
  114. package/templates/vanilla/node_modules/postcss/lib/terminal-highlight.js +70 -0
  115. package/templates/vanilla/node_modules/postcss/lib/tokenize.js +266 -0
  116. package/templates/vanilla/node_modules/postcss/lib/warn-once.js +13 -0
  117. package/templates/vanilla/node_modules/postcss/lib/warning.d.ts +147 -0
  118. package/templates/vanilla/node_modules/postcss/lib/warning.js +37 -0
  119. package/templates/vanilla/node_modules/postcss/package.json +88 -0
  120. package/templates/vanilla/node_modules/rollup/LICENSE.md +695 -0
  121. package/templates/vanilla/node_modules/rollup/README.md +125 -0
  122. package/templates/vanilla/node_modules/rollup/dist/bin/rollup +1715 -0
  123. package/templates/vanilla/node_modules/rollup/dist/es/getLogFilter.js +64 -0
  124. package/templates/vanilla/node_modules/rollup/dist/es/package.json +1 -0
  125. package/templates/vanilla/node_modules/rollup/dist/es/rollup.js +17 -0
  126. package/templates/vanilla/node_modules/rollup/dist/es/shared/node-entry.js +27273 -0
  127. package/templates/vanilla/node_modules/rollup/dist/es/shared/watch.js +4857 -0
  128. package/templates/vanilla/node_modules/rollup/dist/getLogFilter.d.ts +5 -0
  129. package/templates/vanilla/node_modules/rollup/dist/getLogFilter.js +69 -0
  130. package/templates/vanilla/node_modules/rollup/dist/loadConfigFile.d.ts +20 -0
  131. package/templates/vanilla/node_modules/rollup/dist/loadConfigFile.js +29 -0
  132. package/templates/vanilla/node_modules/rollup/dist/rollup.d.ts +1012 -0
  133. package/templates/vanilla/node_modules/rollup/dist/rollup.js +31 -0
  134. package/templates/vanilla/node_modules/rollup/dist/shared/fsevents-importer.js +37 -0
  135. package/templates/vanilla/node_modules/rollup/dist/shared/index.js +4571 -0
  136. package/templates/vanilla/node_modules/rollup/dist/shared/loadConfigFile.js +546 -0
  137. package/templates/vanilla/node_modules/rollup/dist/shared/rollup.js +27351 -0
  138. package/templates/vanilla/node_modules/rollup/dist/shared/watch-cli.js +561 -0
  139. package/templates/vanilla/node_modules/rollup/dist/shared/watch-proxy.js +87 -0
  140. package/templates/vanilla/node_modules/rollup/dist/shared/watch.js +316 -0
  141. package/templates/vanilla/node_modules/rollup/package.json +181 -0
  142. package/templates/vanilla/node_modules/source-map-js/LICENSE +28 -0
  143. package/templates/vanilla/node_modules/source-map-js/README.md +765 -0
  144. package/templates/vanilla/node_modules/source-map-js/lib/array-set.js +121 -0
  145. package/templates/vanilla/node_modules/source-map-js/lib/base64-vlq.js +140 -0
  146. package/templates/vanilla/node_modules/source-map-js/lib/base64.js +67 -0
  147. package/templates/vanilla/node_modules/source-map-js/lib/binary-search.js +111 -0
  148. package/templates/vanilla/node_modules/source-map-js/lib/mapping-list.js +79 -0
  149. package/templates/vanilla/node_modules/source-map-js/lib/quick-sort.js +132 -0
  150. package/templates/vanilla/node_modules/source-map-js/lib/source-map-consumer.d.ts +1 -0
  151. package/templates/vanilla/node_modules/source-map-js/lib/source-map-consumer.js +1188 -0
  152. package/templates/vanilla/node_modules/source-map-js/lib/source-map-generator.d.ts +1 -0
  153. package/templates/vanilla/node_modules/source-map-js/lib/source-map-generator.js +444 -0
  154. package/templates/vanilla/node_modules/source-map-js/lib/source-node.d.ts +1 -0
  155. package/templates/vanilla/node_modules/source-map-js/lib/source-node.js +413 -0
  156. package/templates/vanilla/node_modules/source-map-js/lib/util.js +594 -0
  157. package/templates/vanilla/node_modules/source-map-js/package.json +71 -0
  158. package/templates/vanilla/node_modules/source-map-js/source-map.d.ts +104 -0
  159. package/templates/vanilla/node_modules/source-map-js/source-map.js +8 -0
  160. package/templates/vanilla/node_modules/vite/LICENSE.md +3396 -0
  161. package/templates/vanilla/node_modules/vite/README.md +20 -0
  162. package/templates/vanilla/node_modules/vite/bin/openChrome.applescript +95 -0
  163. package/templates/vanilla/node_modules/vite/bin/vite.js +61 -0
  164. package/templates/vanilla/node_modules/vite/client.d.ts +281 -0
  165. package/templates/vanilla/node_modules/vite/dist/client/client.mjs +725 -0
  166. package/templates/vanilla/node_modules/vite/dist/client/client.mjs.map +1 -0
  167. package/templates/vanilla/node_modules/vite/dist/client/env.mjs +30 -0
  168. package/templates/vanilla/node_modules/vite/dist/client/env.mjs.map +1 -0
  169. package/templates/vanilla/node_modules/vite/dist/node/chunks/dep-7ec6f216.js +914 -0
  170. package/templates/vanilla/node_modules/vite/dist/node/chunks/dep-827b23df.js +66713 -0
  171. package/templates/vanilla/node_modules/vite/dist/node/chunks/dep-c423598f.js +561 -0
  172. package/templates/vanilla/node_modules/vite/dist/node/chunks/dep-f0c7dae0.js +7930 -0
  173. package/templates/vanilla/node_modules/vite/dist/node/chunks/dep-f1e8587f.js +7646 -0
  174. package/templates/vanilla/node_modules/vite/dist/node/cli.js +929 -0
  175. package/templates/vanilla/node_modules/vite/dist/node/constants.js +130 -0
  176. package/templates/vanilla/node_modules/vite/dist/node/index.d.ts +3548 -0
  177. package/templates/vanilla/node_modules/vite/dist/node/index.js +158 -0
  178. package/templates/vanilla/node_modules/vite/dist/node-cjs/publicUtils.cjs +4555 -0
  179. package/templates/vanilla/node_modules/vite/index.cjs +34 -0
  180. package/templates/vanilla/node_modules/vite/package.json +173 -0
  181. package/templates/vanilla/node_modules/vite/types/customEvent.d.ts +35 -0
  182. package/templates/vanilla/node_modules/vite/types/hmrPayload.d.ts +61 -0
  183. package/templates/vanilla/node_modules/vite/types/hot.d.ts +32 -0
  184. package/templates/vanilla/node_modules/vite/types/importGlob.d.ts +97 -0
  185. package/templates/vanilla/node_modules/vite/types/importMeta.d.ts +26 -0
  186. package/templates/vanilla/node_modules/vite/types/metadata.d.ts +10 -0
  187. package/templates/vanilla/node_modules/vite/types/package.json +4 -0
  188. package/templates/vanilla/package-lock.json +589 -0
  189. package/templates/vanilla/package.json +17 -0
  190. package/templates/vanilla/src/components/App.js +60 -0
  191. package/templates/vanilla/src/components/Card.js +21 -0
  192. package/templates/vanilla/src/components/Hero.js +15 -0
  193. package/templates/vanilla/src/components/InteractiveDemo.js +59 -0
  194. package/templates/vanilla/src/main.js +9 -0
  195. package/templates/vanilla/src/style/main.css +172 -0
  196. package/templates/vanilla/vite.config.js +8 -0
  197. package/templates/web3/.env.example +15 -0
  198. package/templates/web3/.github/workflows/deploy.yml +38 -0
  199. package/templates/web3/README.md +33 -0
  200. package/templates/web3/contracts/foundry.toml +11 -0
  201. package/templates/web3/contracts/script/Deploy.s.sol +13 -0
  202. package/templates/web3/contracts/src/Counter.sol +21 -0
  203. package/templates/web3/index.html +13 -0
  204. package/templates/web3/package.json +25 -0
  205. package/templates/web3/scripts/chain-start.mjs +305 -0
  206. package/templates/web3/scripts/chain-stop.mjs +34 -0
  207. package/templates/web3/scripts/deploy.mjs +155 -0
  208. package/templates/web3/scripts/setup.mjs +42 -0
  209. package/templates/web3/src/components/App.js +49 -0
  210. package/templates/web3/src/components/CounterCard.js +111 -0
  211. package/templates/web3/src/main.js +54 -0
  212. package/templates/web3/src/style/main.css +345 -0
  213. package/templates/web3/vite.config.js +29 -0
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process';
3
+ import { fileURLToPath } from 'node:url';
4
+ import path from 'node:path';
5
+ import fsSync from 'node:fs';
6
+ import { deployCounter } from './deploy.mjs';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const projectRoot = path.resolve(__filename, '..', '..');
10
+ const pidFile = path.join(projectRoot, '.anvil.pid');
11
+ const cacheDir = path.join(projectRoot, '.anvil-cache');
12
+
13
+ ensureDir(cacheDir);
14
+
15
+ loadEnvFiles();
16
+
17
+ const cliArgs = parseArgs();
18
+ let rpcUrl = cliArgs['rpc-url'] || process.env.RPC_URL || 'http://127.0.0.1:8545';
19
+ let port = cliArgs.port || (() => {
20
+ try {
21
+ return new URL(rpcUrl).port || '8545';
22
+ } catch {
23
+ return '8545';
24
+ }
25
+ })();
26
+ if (cliArgs.port && !cliArgs['rpc-url']) {
27
+ rpcUrl = `http://127.0.0.1:${cliArgs.port}`;
28
+ }
29
+ process.env.RPC_URL = rpcUrl;
30
+
31
+ let forkUrl = cliArgs['fork-url'] || process.env.FORK_RPC_URL || 'https://eth.llamarpc.com';
32
+ const noForkFlag = cliArgs['no-fork'] || forkUrl === 'none' || forkUrl === 'local';
33
+ if (noForkFlag) {
34
+ forkUrl = null;
35
+ }
36
+ const chainId = cliArgs['chain-id'] || process.env.CHAIN_ID || '31337';
37
+ const allowForkFallback = !cliArgs['no-fallback'] && process.env.ANVIL_DISABLE_FALLBACK !== '1';
38
+ const preferFork = Boolean(forkUrl);
39
+
40
+ const runInBackground = cliArgs.background || cliArgs.bg;
41
+
42
+ checkExistingInstance();
43
+
44
+ console.log(`[chain] RPC URL ${rpcUrl}`);
45
+ let chainProcess = null;
46
+ let runtimeLabel = 'anvil';
47
+ const startupErrors = [];
48
+
49
+ try {
50
+ const result = await startAnvilInstance({ fork: preferFork });
51
+ chainProcess = result.process;
52
+ runtimeLabel = result.runtime;
53
+ } catch (error) {
54
+ startupErrors.push(error);
55
+ if (preferFork && allowForkFallback) {
56
+ console.warn('[chain] Failed to start forked anvil:', error.message);
57
+ console.warn('[chain] Retrying without --fork-url (set ANVIL_DISABLE_FALLBACK=1 to disable this behavior).');
58
+ try {
59
+ const result = await startAnvilInstance({ fork: false });
60
+ chainProcess = result.process;
61
+ runtimeLabel = result.runtime;
62
+ } catch (fallbackError) {
63
+ startupErrors.push(fallbackError);
64
+ }
65
+ }
66
+ }
67
+
68
+ if (!chainProcess) {
69
+ const finalError = startupErrors.pop() || new Error('Unable to start local JSON-RPC chain');
70
+ console.error('[chain] Failed to boot a local chain:', finalError.message);
71
+ process.exitCode = 1;
72
+ throw finalError;
73
+ }
74
+
75
+ if (runInBackground) {
76
+ chainProcess.unref();
77
+ } else {
78
+ const shutdown = () => {
79
+ console.log(`\n[chain] Shutting down ${runtimeLabel}...`);
80
+ return killProcess(chainProcess).finally(() => process.exit());
81
+ };
82
+ process.on('SIGINT', shutdown);
83
+ process.on('SIGTERM', shutdown);
84
+ }
85
+
86
+ await deployCounter({ silent: cliArgs.silent, rpcUrl });
87
+ console.log(`[chain] ${runtimeLabel.charAt(0).toUpperCase() + runtimeLabel.slice(1)} is running.`);
88
+
89
+ if (runInBackground) {
90
+ console.log('[chain] Background mode enabled. Run "npm run chain:stop" to shut it down.');
91
+ process.exit(0);
92
+ }
93
+
94
+ console.log('Press Ctrl+C to stop.');
95
+ await new Promise((resolve) => chainProcess.on('exit', resolve));
96
+ removePidFile();
97
+
98
+ function sleep(ms) {
99
+ return new Promise((resolve) => setTimeout(resolve, ms));
100
+ }
101
+
102
+ async function waitForRpc(url, attempts = 20) {
103
+ const body = JSON.stringify({ jsonrpc: '2.0', method: 'eth_chainId', params: [], id: 1 });
104
+ for (let i = 0; i < attempts; i++) {
105
+ try {
106
+ const res = await fetch(url, {
107
+ method: 'POST',
108
+ headers: { 'content-type': 'application/json' },
109
+ body
110
+ });
111
+ if (res.ok) {
112
+ return true;
113
+ }
114
+ } catch (error) {
115
+ // ignore
116
+ }
117
+ await sleep(1500);
118
+ }
119
+ const error = new Error(`Timed out waiting for RPC at ${url}`);
120
+ error.code = 'RPC_TIMEOUT';
121
+ throw error;
122
+ }
123
+
124
+ function buildAnvilArgs({ fork }) {
125
+ const args = [
126
+ '--chain-id', chainId,
127
+ '--port', port,
128
+ '--block-time', process.env.ANVIL_BLOCK_TIME || '1',
129
+ '--cache-path', cacheDir
130
+ ];
131
+ if (fork) {
132
+ if (!forkUrl) {
133
+ throw new Error('Fork URL requested but not provided');
134
+ }
135
+ args.unshift(forkUrl);
136
+ args.unshift('--fork-url');
137
+ }
138
+ return args;
139
+ }
140
+
141
+ async function startAnvilInstance({ fork }) {
142
+ const args = buildAnvilArgs({ fork });
143
+ const spawnOptions = runInBackground
144
+ ? { stdio: 'ignore', detached: true }
145
+ : { stdio: 'inherit' };
146
+
147
+ console.log(`[chain] Spawning anvil${fork ? ' (fork)' : ''} with args:`, args.join(' '));
148
+ let child;
149
+ try {
150
+ child = spawn('anvil', args, spawnOptions);
151
+ } catch (error) {
152
+ throw new Error(`Unable to spawn anvil: ${error.message}`);
153
+ }
154
+ writePid(child.pid);
155
+
156
+ try {
157
+ await waitForRpcOrExit(child, rpcUrl, 'anvil');
158
+ return { process: child, runtime: 'anvil' };
159
+ } catch (error) {
160
+ await killProcess(child);
161
+ throw error;
162
+ }
163
+ }
164
+
165
+ function waitForRpcOrExit(childProcess, url, runtime = 'anvil') {
166
+ return new Promise((resolve, reject) => {
167
+ const onExit = (code, signal) => {
168
+ cleanup();
169
+ const reason = typeof code === 'number' ? `code ${code}` : `signal ${signal || 'unknown'}`;
170
+ reject(new Error(`${runtime} exited before RPC became ready (${reason})`));
171
+ };
172
+
173
+ const onReady = () => {
174
+ cleanup();
175
+ resolve();
176
+ };
177
+
178
+ const onError = (error) => {
179
+ cleanup();
180
+ reject(error);
181
+ };
182
+
183
+ const cleanup = () => {
184
+ childProcess.removeListener('exit', onExit);
185
+ };
186
+
187
+ childProcess.once('exit', onExit);
188
+ waitForRpc(url).then(onReady, onError);
189
+ });
190
+ }
191
+
192
+ function loadEnvFiles() {
193
+ const files = ['.env', '.env.local'];
194
+ for (const file of files) {
195
+ const full = path.join(projectRoot, file);
196
+ if (!fsSync.existsSync(full)) continue;
197
+ const content = fsSync.readFileSync(full, 'utf8');
198
+ for (const line of content.split(/\r?\n/)) {
199
+ if (!line || line.trim().startsWith('#') || !line.includes('=')) continue;
200
+ const [rawKey, ...rest] = line.split('=');
201
+ const key = rawKey.trim();
202
+ const value = rest.join('=').trim().replace(/^['"]|['"]$/g, '');
203
+ if (key && !process.env[key]) {
204
+ process.env[key] = value;
205
+ }
206
+ }
207
+ }
208
+ }
209
+
210
+ function parseArgs() {
211
+ const args = process.argv.slice(2);
212
+ const result = {};
213
+ for (let i = 0; i < args.length; i++) {
214
+ const arg = args[i];
215
+ if (!arg.startsWith('--')) continue;
216
+ const key = arg.replace(/^--/, '');
217
+ const next = args[i + 1];
218
+ if (!next || next.startsWith('--')) {
219
+ result[key] = true;
220
+ } else {
221
+ result[key] = next;
222
+ i++;
223
+ }
224
+ }
225
+ return result;
226
+ }
227
+
228
+ function checkExistingInstance() {
229
+ if (!fsSync.existsSync(pidFile)) return;
230
+ const pid = Number(fsSync.readFileSync(pidFile, 'utf8'));
231
+ if (pid && Number.isFinite(pid)) {
232
+ try {
233
+ process.kill(pid, 0);
234
+ console.log(`[chain] Found active anvil process (pid ${pid}). Sending SIGINT...`);
235
+ try {
236
+ process.kill(pid, 'SIGINT');
237
+ } catch {}
238
+ setTimeout(() => {
239
+ try {
240
+ process.kill(pid, 'SIGKILL');
241
+ } catch {}
242
+ removePidFile();
243
+ }, 1500);
244
+ } catch {
245
+ removePidFile();
246
+ }
247
+ return;
248
+ }
249
+ removePidFile();
250
+ }
251
+
252
+ function writePid(pid) {
253
+ try {
254
+ fsSync.writeFileSync(pidFile, String(pid));
255
+ } catch (error) {
256
+ console.warn('[chain] Unable to write PID file:', error.message);
257
+ }
258
+ }
259
+
260
+ function removePidFile() {
261
+ if (fsSync.existsSync(pidFile)) {
262
+ try {
263
+ fsSync.unlinkSync(pidFile);
264
+ } catch {}
265
+ }
266
+ }
267
+
268
+ function ensureDir(dir) {
269
+ if (!fsSync.existsSync(dir)) {
270
+ fsSync.mkdirSync(dir, { recursive: true });
271
+ }
272
+ }
273
+
274
+ function killProcess(childProcess) {
275
+ if (!childProcess) {
276
+ removePidFile();
277
+ return Promise.resolve();
278
+ }
279
+
280
+ if (childProcess.exitCode !== null || childProcess.killed) {
281
+ removePidFile();
282
+ return Promise.resolve();
283
+ }
284
+
285
+ return new Promise((resolve) => {
286
+ const pid = childProcess.pid;
287
+ const timer = setTimeout(() => {
288
+ try {
289
+ process.kill(pid, 'SIGKILL');
290
+ } catch {}
291
+ }, 2000);
292
+ childProcess.once('exit', () => {
293
+ clearTimeout(timer);
294
+ removePidFile();
295
+ resolve();
296
+ });
297
+ try {
298
+ process.kill(pid, 'SIGINT');
299
+ } catch {
300
+ clearTimeout(timer);
301
+ removePidFile();
302
+ resolve();
303
+ }
304
+ });
305
+ }
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const projectRoot = path.resolve(__filename, '..', '..');
8
+ const pidFile = path.join(projectRoot, '.anvil.pid');
9
+
10
+ if (!fs.existsSync(pidFile)) {
11
+ console.log('[chain] No PID file found. Is Anvil running?');
12
+ process.exit(0);
13
+ }
14
+
15
+ const pid = Number(fs.readFileSync(pidFile, 'utf8'));
16
+ if (!pid || !Number.isFinite(pid)) {
17
+ console.log('[chain] PID file is invalid. Removing it.');
18
+ fs.unlinkSync(pidFile);
19
+ process.exit(0);
20
+ }
21
+
22
+ try {
23
+ process.kill(pid, 'SIGINT');
24
+ console.log(`[chain] Sent SIGINT to Anvil (pid ${pid}).`);
25
+ } catch (error) {
26
+ if (error.code === 'ESRCH') {
27
+ console.log('[chain] Anvil process already stopped.');
28
+ } else {
29
+ console.error('[chain] Failed to send SIGINT:', error.message);
30
+ }
31
+ }
32
+
33
+ fs.unlinkSync(pidFile);
34
+ console.log('[chain] PID file removed.');
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process';
3
+ import { fileURLToPath } from 'node:url';
4
+ import path from 'node:path';
5
+ import fs from 'node:fs/promises';
6
+ import fsSync from 'node:fs';
7
+ import { ethers } from 'ethers';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const projectRoot = path.resolve(__filename, '..', '..');
11
+ const contractsDir = path.join(projectRoot, 'contracts');
12
+ const generatedDir = path.join(projectRoot, 'src', 'generated');
13
+ const artifactPath = path.join(contractsDir, 'out', 'Counter.sol', 'Counter.json');
14
+ loadEnvFiles();
15
+
16
+ defaultEnv('RPC_URL', 'http://127.0.0.1:8545');
17
+ defaultEnv('CHAIN_ID', '31337');
18
+
19
+ const DEFAULT_PRIVATE_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80';
20
+
21
+ function loadEnvFiles() {
22
+ const files = ['.env', '.env.local'];
23
+ for (const file of files) {
24
+ const full = path.join(projectRoot, file);
25
+ if (!fsSync.existsSync(full)) continue;
26
+ const content = fsSync.readFileSync(full, 'utf8');
27
+ for (const line of content.split(/\r?\n/)) {
28
+ if (!line || line.trim().startsWith('#') || !line.includes('=')) continue;
29
+ const [rawKey, ...rest] = line.split('=');
30
+ const key = rawKey.trim();
31
+ const value = rest.join('=').trim().replace(/^['"]|['"]$/g, '');
32
+ if (key && !process.env[key]) {
33
+ process.env[key] = value;
34
+ }
35
+ }
36
+ }
37
+ }
38
+
39
+ function defaultEnv(key, value) {
40
+ if (!process.env[key]) {
41
+ process.env[key] = value;
42
+ }
43
+ }
44
+
45
+ const cliArgs = parseArgs();
46
+ if (cliArgs['rpc-url']) {
47
+ process.env.RPC_URL = cliArgs['rpc-url'];
48
+ }
49
+
50
+ async function runForgeBuild() {
51
+ await new Promise((resolve, reject) => {
52
+ const forge = spawn('forge', ['build'], { cwd: contractsDir, stdio: 'inherit' });
53
+ forge.on('exit', (code) => {
54
+ if (code === 0) {
55
+ resolve();
56
+ } else {
57
+ reject(new Error('forge build failed. Is Foundry installed?'));
58
+ }
59
+ });
60
+ });
61
+ }
62
+
63
+ function loadArtifactBytes(artifact) {
64
+ if (artifact.bytecode?.object) {
65
+ return artifact.bytecode.object;
66
+ }
67
+ if (artifact.bytecode) {
68
+ return artifact.bytecode;
69
+ }
70
+ throw new Error('Unable to find bytecode inside Counter artifact.');
71
+ }
72
+
73
+ export async function deployCounter({ silent = false } = {}) {
74
+ if (!silent) {
75
+ console.log('\n[deploy] Building contracts with Foundry...');
76
+ }
77
+ await runForgeBuild();
78
+
79
+ const artifactRaw = await fs.readFile(artifactPath, 'utf8');
80
+ const artifact = JSON.parse(artifactRaw);
81
+
82
+ const rpcUrl = process.env.RPC_URL;
83
+ const provider = new ethers.providers.JsonRpcProvider(rpcUrl);
84
+ const network = await provider.getNetwork();
85
+
86
+ const privateKey = process.env.DEPLOYER_PRIVATE_KEY || DEFAULT_PRIVATE_KEY;
87
+ const wallet = new ethers.Wallet(privateKey, provider);
88
+
89
+ if (!silent) {
90
+ console.log(`[deploy] Deploying Counter from ${await wallet.getAddress()} to chain ${network.chainId}`);
91
+ }
92
+
93
+ const factory = new ethers.ContractFactory(artifact.abi, loadArtifactBytes(artifact), wallet);
94
+ const contract = await factory.deploy();
95
+ await contract.deployed();
96
+
97
+ await fs.mkdir(generatedDir, { recursive: true });
98
+ const outputPath = path.join(generatedDir, 'contract.json');
99
+ const payload = {
100
+ address: contract.address,
101
+ chainId: network.chainId,
102
+ rpcUrl,
103
+ abi: artifact.abi,
104
+ deployedAt: new Date().toISOString()
105
+ };
106
+ await fs.writeFile(outputPath, JSON.stringify(payload, null, 2));
107
+
108
+ if (!silent) {
109
+ console.log(`[deploy] Counter deployed at ${contract.address}`);
110
+ console.log(`[deploy] Wrote ${path.relative(projectRoot, outputPath)}`);
111
+ }
112
+
113
+ const userAddress = process.env.USER_ADDRESS;
114
+ if (userAddress) {
115
+ const amount = process.env.USER_FUNDING_ETH || '2.0';
116
+ if (!silent) {
117
+ console.log(`[deploy] Funding ${userAddress} with ${amount} ETH on the fork...`);
118
+ }
119
+ const tx = await wallet.sendTransaction({
120
+ to: userAddress,
121
+ value: ethers.utils.parseEther(amount)
122
+ });
123
+ await tx.wait();
124
+ if (!silent) {
125
+ console.log('[deploy] Funding complete.');
126
+ }
127
+ }
128
+
129
+ return payload;
130
+ }
131
+
132
+ if (import.meta.url === `file://${process.argv[1]}`) {
133
+ deployCounter().catch((error) => {
134
+ console.error('[deploy] Failed to deploy Counter:', error.message);
135
+ process.exit(1);
136
+ });
137
+ }
138
+
139
+ function parseArgs() {
140
+ const args = process.argv.slice(2);
141
+ const result = {};
142
+ for (let i = 0; i < args.length; i++) {
143
+ const arg = args[i];
144
+ if (!arg.startsWith('--')) continue;
145
+ const key = arg.replace(/^--/, '');
146
+ const next = args[i + 1];
147
+ if (!next || next.startsWith('--')) {
148
+ result[key] = true;
149
+ } else {
150
+ result[key] = next;
151
+ i++;
152
+ }
153
+ }
154
+ return result;
155
+ }
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process';
3
+ import { fileURLToPath } from 'node:url';
4
+ import path from 'node:path';
5
+ import fs from 'node:fs';
6
+
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const projectRoot = path.resolve(__filename, '..', '..');
9
+ const contractsDir = path.join(projectRoot, 'contracts');
10
+ const libDir = path.join(contractsDir, 'lib', 'forge-std');
11
+ const gitDir = path.join(contractsDir, '.git');
12
+
13
+ try {
14
+ if (!fs.existsSync(gitDir)) {
15
+ console.log('[setup] Initializing git repository for Foundry deps...');
16
+ await run('git', ['init']);
17
+ }
18
+ if (!fs.existsSync(libDir)) {
19
+ console.log('[setup] Installing forge-std...');
20
+ await run('forge', ['install', 'foundry-rs/forge-std']);
21
+ } else {
22
+ console.log('[setup] forge-std already installed, skipping.');
23
+ }
24
+ await run('forge', ['build']);
25
+ console.log('[setup] Contracts ready.');
26
+ } catch (error) {
27
+ console.error('[setup] Failed:', error.message);
28
+ process.exit(1);
29
+ }
30
+
31
+ function run(cmd, args) {
32
+ return new Promise((resolve, reject) => {
33
+ const child = spawn(cmd, args, { cwd: contractsDir, stdio: 'inherit' });
34
+ child.on('exit', (code) => {
35
+ if (code === 0) {
36
+ resolve();
37
+ } else {
38
+ reject(new Error(`[setup] Command failed: ${cmd} ${args.join(' ')}`));
39
+ }
40
+ });
41
+ });
42
+ }
@@ -0,0 +1,49 @@
1
+ import { Component } from '@monygroupcorp/microact';
2
+ import { WalletButton } from '@monygroupcorp/micro-web3';
3
+ import CounterCard from './CounterCard.js';
4
+ import contractInfo from '../generated/contract.json';
5
+
6
+ class App extends Component {
7
+ constructor(props) {
8
+ super();
9
+ this.walletService = props.walletService;
10
+ this.counterCard = this.createChild('counter-card', new CounterCard({ walletService: this.walletService }));
11
+ this.walletButton = this.createChild('fab-container', new WalletButton({ walletService: this.walletService }));
12
+ }
13
+
14
+ children() {
15
+ return {
16
+ '#counter-card': this.counterCard,
17
+ '#fab-container': this.walletButton,
18
+ };
19
+ }
20
+
21
+ render() {
22
+ const rpcUrl = contractInfo?.rpcUrl || 'http://127.0.0.1:8545';
23
+ const chainId = contractInfo?.chainId || 31337;
24
+
25
+ return `
26
+ <div class="app">
27
+ <header class="hero">
28
+ <p class="eyebrow">Foundry × Microact</p>
29
+ <h1>Anvil Counter Starter</h1>
30
+ <p>Spin up an Anvil fork, deploy Counter.sol, and interact with it through Microact + Micro Web3.</p>
31
+ <ol class="instructions">
32
+ <li>Run <code>npm run chain:start</code> in a terminal (starts Anvil, deploys, funds your wallet).</li>
33
+ <li>Connect your wallet to <strong>${rpcUrl}</strong> (Chain ID ${chainId}).</li>
34
+ <li>Click the floating wallet button, connect, then use the counter below.</li>
35
+ </ol>
36
+ </header>
37
+ <main class="main">
38
+ <section id="counter-card"></section>
39
+ </main>
40
+ <footer class="footer">
41
+ <p>2026 Mony Group Corporation - All code is released under the VPL (Viral Public License)</p>
42
+ </footer>
43
+ <div id="fab-container"></div>
44
+ </div>
45
+ `;
46
+ }
47
+ }
48
+
49
+ export default App;
@@ -0,0 +1,111 @@
1
+ import { Component, eventBus } from '@monygroupcorp/microact';
2
+ import { ethers } from 'ethers';
3
+ import contractInfo from '../generated/contract.json';
4
+
5
+ class CounterCard extends Component {
6
+ constructor(props) {
7
+ super();
8
+ this.walletService = props.walletService;
9
+ this.state = {
10
+ value: null,
11
+ pending: false,
12
+ error: null,
13
+ };
14
+ this.provider = new ethers.providers.JsonRpcProvider(contractInfo.rpcUrl);
15
+ this.contract = new ethers.Contract(contractInfo.address, contractInfo.abi, this.provider);
16
+ }
17
+
18
+ events() {
19
+ return {
20
+ 'click .counter__increment': 'handleIncrement',
21
+ 'click .counter__decrement': 'handleDecrement',
22
+ 'click .counter__refresh': 'loadValue',
23
+ };
24
+ }
25
+
26
+ onMount() {
27
+ this.loadValue();
28
+ const interval = setInterval(() => this.loadValue(), 4000);
29
+ this.registerCleanup(() => clearInterval(interval));
30
+
31
+ this.unsubscribeWallet = eventBus.on('wallet:connected', () => {
32
+ this.setState({ error: null });
33
+ });
34
+ this.registerCleanup(() => {
35
+ if (this.unsubscribeWallet) this.unsubscribeWallet();
36
+ });
37
+ }
38
+
39
+ async loadValue() {
40
+ try {
41
+ const value = await this.contract.value();
42
+ this.setState({ value: value.toString(), error: null });
43
+ } catch (error) {
44
+ console.error('Failed to read counter value', error);
45
+ this.setState({ error: 'Unable to read counter value. Is Anvil running?' });
46
+ }
47
+ }
48
+
49
+ async handleIncrement() {
50
+ await this.sendTx('increment');
51
+ }
52
+
53
+ async handleDecrement() {
54
+ await this.sendTx('decrement');
55
+ }
56
+
57
+ async sendTx(method) {
58
+ if (!this.walletService.isConnected()) {
59
+ this.setState({ error: 'Connect a wallet that points to the local Anvil network.' });
60
+ return;
61
+ }
62
+
63
+ try {
64
+ this.setState({ pending: true, error: null });
65
+ const signer = this.walletService.signer || (this.walletService.ethersProvider?.getSigner());
66
+ if (!signer) {
67
+ throw new Error('Wallet signer unavailable.');
68
+ }
69
+ const tx = await this.contract.connect(signer)[method]();
70
+ await tx.wait();
71
+ await this.loadValue();
72
+ } catch (error) {
73
+ console.error('Counter transaction failed', error);
74
+ this.setState({ error: error.message || 'Counter transaction failed.' });
75
+ } finally {
76
+ this.setState({ pending: false });
77
+ }
78
+ }
79
+
80
+ render() {
81
+ const { value, pending, error } = this.state;
82
+ const address = contractInfo.address;
83
+
84
+ return `
85
+ <section class="counter">
86
+ <header>
87
+ <h2>Local Counter</h2>
88
+ <p class="counter__meta">
89
+ Contract <code>${address}</code><br />
90
+ RPC <code>${contractInfo.rpcUrl}</code>
91
+ </p>
92
+ </header>
93
+ <div class="counter__value">
94
+ ${value === null ? '<span class="muted">loading...</span>' : `<span>${value}</span>`}
95
+ </div>
96
+ <div class="counter__actions">
97
+ <button class="counter__decrement" ${pending ? 'disabled' : ''}>-</button>
98
+ <button class="counter__increment" ${pending ? 'disabled' : ''}>+</button>
99
+ <button class="counter__refresh" ${pending ? 'disabled' : ''}>Refresh</button>
100
+ </div>
101
+ ${pending ? '<p class="counter__status">Waiting for confirmation...</p>' : ''}
102
+ ${error ? `<p class="counter__error">${error}</p>` : ''}
103
+ <p class="counter__helper">
104
+ This contract is deployed locally via Foundry + Anvil. Use the wallet button to connect, then interact here.
105
+ </p>
106
+ </section>
107
+ `;
108
+ }
109
+ }
110
+
111
+ export default CounterCard;