movehat 0.2.0 → 0.2.2

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 (184) hide show
  1. package/README.md +132 -279
  2. package/dist/__tests__/deployContract.test.js +56 -47
  3. package/dist/__tests__/deployContract.test.js.map +1 -1
  4. package/dist/__tests__/exports.test.d.ts +2 -0
  5. package/dist/__tests__/exports.test.d.ts.map +1 -0
  6. package/dist/__tests__/exports.test.js +30 -0
  7. package/dist/__tests__/exports.test.js.map +1 -0
  8. package/dist/__tests__/fixtures/sigint-deploy-harness.d.ts +4 -3
  9. package/dist/__tests__/fixtures/sigint-deploy-harness.d.ts.map +1 -1
  10. package/dist/__tests__/fixtures/sigint-deploy-harness.js +8 -7
  11. package/dist/__tests__/fixtures/sigint-deploy-harness.js.map +1 -1
  12. package/dist/__tests__/fork/api.test.js +7 -2
  13. package/dist/__tests__/fork/api.test.js.map +1 -1
  14. package/dist/__tests__/fork/api.timeout.test.d.ts +2 -0
  15. package/dist/__tests__/fork/api.timeout.test.d.ts.map +1 -0
  16. package/dist/__tests__/fork/api.timeout.test.js +98 -0
  17. package/dist/__tests__/fork/api.timeout.test.js.map +1 -0
  18. package/dist/__tests__/harness/Harness.proxy.test.js +7 -11
  19. package/dist/__tests__/harness/Harness.proxy.test.js.map +1 -1
  20. package/dist/__tests__/harness/codeObject.deploy.test.js +1 -1
  21. package/dist/__tests__/harness/codeObject.deploy.test.js.map +1 -1
  22. package/dist/__tests__/harness/view.test.js +3 -3
  23. package/dist/commands/__tests__/compile.toml-mutation.test.d.ts +2 -0
  24. package/dist/commands/__tests__/compile.toml-mutation.test.d.ts.map +1 -0
  25. package/dist/commands/__tests__/compile.toml-mutation.test.js +69 -0
  26. package/dist/commands/__tests__/compile.toml-mutation.test.js.map +1 -0
  27. package/dist/commands/__tests__/init.test.js +73 -11
  28. package/dist/commands/__tests__/init.test.js.map +1 -1
  29. package/dist/commands/__tests__/run.test.js +3 -3
  30. package/dist/commands/__tests__/run.test.js.map +1 -1
  31. package/dist/commands/init.d.ts +22 -0
  32. package/dist/commands/init.d.ts.map +1 -1
  33. package/dist/commands/init.js +55 -6
  34. package/dist/commands/init.js.map +1 -1
  35. package/dist/core/AccountManager.d.ts +0 -3
  36. package/dist/core/AccountManager.d.ts.map +1 -1
  37. package/dist/core/AccountManager.js +14 -7
  38. package/dist/core/AccountManager.js.map +1 -1
  39. package/dist/core/Publisher.d.ts +0 -5
  40. package/dist/core/Publisher.d.ts.map +1 -1
  41. package/dist/core/Publisher.js +52 -76
  42. package/dist/core/Publisher.js.map +1 -1
  43. package/dist/core/__tests__/AccountManager.global-state.test.d.ts +2 -0
  44. package/dist/core/__tests__/AccountManager.global-state.test.d.ts.map +1 -0
  45. package/dist/core/__tests__/AccountManager.global-state.test.js +69 -0
  46. package/dist/core/__tests__/AccountManager.global-state.test.js.map +1 -0
  47. package/dist/core/__tests__/movementProfile.test.d.ts +2 -0
  48. package/dist/core/__tests__/movementProfile.test.d.ts.map +1 -0
  49. package/dist/core/__tests__/movementProfile.test.js +112 -0
  50. package/dist/core/__tests__/movementProfile.test.js.map +1 -0
  51. package/dist/core/config.js +6 -5
  52. package/dist/core/config.js.map +1 -1
  53. package/dist/core/contract.d.ts +0 -3
  54. package/dist/core/contract.d.ts.map +1 -1
  55. package/dist/core/contract.js +0 -3
  56. package/dist/core/contract.js.map +1 -1
  57. package/dist/core/deployments.d.ts +0 -6
  58. package/dist/core/deployments.d.ts.map +1 -1
  59. package/dist/core/deployments.js +0 -12
  60. package/dist/core/deployments.js.map +1 -1
  61. package/dist/core/movementProfile.d.ts +55 -22
  62. package/dist/core/movementProfile.d.ts.map +1 -1
  63. package/dist/core/movementProfile.js +77 -99
  64. package/dist/core/movementProfile.js.map +1 -1
  65. package/dist/fork/__tests__/manager.test.js +1 -1
  66. package/dist/fork/__tests__/server.cors.test.d.ts +2 -0
  67. package/dist/fork/__tests__/server.cors.test.d.ts.map +1 -0
  68. package/dist/fork/__tests__/server.cors.test.js +79 -0
  69. package/dist/fork/__tests__/server.cors.test.js.map +1 -0
  70. package/dist/fork/api.d.ts +9 -1
  71. package/dist/fork/api.d.ts.map +1 -1
  72. package/dist/fork/api.js +37 -7
  73. package/dist/fork/api.js.map +1 -1
  74. package/dist/fork/manager.d.ts +1 -21
  75. package/dist/fork/manager.d.ts.map +1 -1
  76. package/dist/fork/manager.js +1 -41
  77. package/dist/fork/manager.js.map +1 -1
  78. package/dist/fork/server.d.ts +20 -1
  79. package/dist/fork/server.d.ts.map +1 -1
  80. package/dist/fork/server.js +19 -9
  81. package/dist/fork/server.js.map +1 -1
  82. package/dist/fork/test.d.ts +0 -1
  83. package/dist/fork/test.d.ts.map +1 -1
  84. package/dist/fork/test.js.map +1 -1
  85. package/dist/harness/Harness.d.ts +11 -13
  86. package/dist/harness/Harness.d.ts.map +1 -1
  87. package/dist/harness/Harness.js +13 -13
  88. package/dist/harness/Harness.js.map +1 -1
  89. package/dist/harness/codeObject.d.ts.map +1 -1
  90. package/dist/harness/codeObject.js +31 -38
  91. package/dist/harness/codeObject.js.map +1 -1
  92. package/dist/harness/script.d.ts +3 -3
  93. package/dist/harness/script.d.ts.map +1 -1
  94. package/dist/harness/script.js +33 -29
  95. package/dist/harness/script.js.map +1 -1
  96. package/dist/helpers/__tests__/setupLocalTesting.fork-network.test.d.ts +2 -0
  97. package/dist/helpers/__tests__/setupLocalTesting.fork-network.test.d.ts.map +1 -0
  98. package/dist/helpers/__tests__/setupLocalTesting.fork-network.test.js +172 -0
  99. package/dist/helpers/__tests__/setupLocalTesting.fork-network.test.js.map +1 -0
  100. package/dist/helpers/setupLocalTesting.d.ts +1 -2
  101. package/dist/helpers/setupLocalTesting.d.ts.map +1 -1
  102. package/dist/helpers/setupLocalTesting.js +28 -2
  103. package/dist/helpers/setupLocalTesting.js.map +1 -1
  104. package/dist/index.d.ts +1 -0
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +0 -1
  107. package/dist/index.js.map +1 -1
  108. package/dist/node/LocalNodeManager.d.ts +8 -0
  109. package/dist/node/LocalNodeManager.d.ts.map +1 -1
  110. package/dist/node/LocalNodeManager.js +10 -1
  111. package/dist/node/LocalNodeManager.js.map +1 -1
  112. package/dist/node/__tests__/LocalNodeManager.api-port.test.d.ts +2 -0
  113. package/dist/node/__tests__/LocalNodeManager.api-port.test.d.ts.map +1 -0
  114. package/dist/node/__tests__/LocalNodeManager.api-port.test.js +55 -0
  115. package/dist/node/__tests__/LocalNodeManager.api-port.test.js.map +1 -0
  116. package/dist/node/__tests__/LocalNodeManager.test.js +4 -3
  117. package/dist/node/__tests__/LocalNodeManager.test.js.map +1 -1
  118. package/dist/runtime.d.ts.map +1 -1
  119. package/dist/runtime.js +1 -3
  120. package/dist/runtime.js.map +1 -1
  121. package/dist/templates/move/Move.toml +1 -1
  122. package/dist/templates/move/sources/Counter.move +31 -4
  123. package/dist/templates/scripts/deploy-counter.ts +11 -1
  124. package/dist/templates/tests/Counter.test.ts +2 -2
  125. package/dist/types/config.d.ts +8 -1
  126. package/dist/types/config.d.ts.map +1 -1
  127. package/dist/utils/__tests__/childProcessAdapter.maxBuffer.test.d.ts +2 -0
  128. package/dist/utils/__tests__/childProcessAdapter.maxBuffer.test.d.ts.map +1 -0
  129. package/dist/utils/__tests__/childProcessAdapter.maxBuffer.test.js +43 -0
  130. package/dist/utils/__tests__/childProcessAdapter.maxBuffer.test.js.map +1 -0
  131. package/dist/utils/address.d.ts +0 -4
  132. package/dist/utils/address.d.ts.map +1 -1
  133. package/dist/utils/address.js +0 -4
  134. package/dist/utils/address.js.map +1 -1
  135. package/dist/utils/childProcessAdapter.d.ts +7 -0
  136. package/dist/utils/childProcessAdapter.d.ts.map +1 -1
  137. package/dist/utils/childProcessAdapter.js +23 -6
  138. package/dist/utils/childProcessAdapter.js.map +1 -1
  139. package/package.json +2 -1
  140. package/src/__tests__/deployContract.test.ts +59 -50
  141. package/src/__tests__/exports.test.ts +32 -0
  142. package/src/__tests__/fixtures/sigint-deploy-harness.ts +8 -7
  143. package/src/__tests__/fork/api.test.ts +7 -2
  144. package/src/__tests__/fork/api.timeout.test.ts +150 -0
  145. package/src/__tests__/harness/Harness.proxy.test.ts +7 -11
  146. package/src/__tests__/harness/codeObject.deploy.test.ts +1 -1
  147. package/src/__tests__/harness/view.test.ts +3 -3
  148. package/src/commands/__tests__/compile.toml-mutation.test.ts +77 -0
  149. package/src/commands/__tests__/init.test.ts +96 -11
  150. package/src/commands/__tests__/run.test.ts +3 -3
  151. package/src/commands/init.ts +77 -6
  152. package/src/core/AccountManager.ts +18 -13
  153. package/src/core/Publisher.ts +58 -85
  154. package/src/core/__tests__/AccountManager.global-state.test.ts +83 -0
  155. package/src/core/__tests__/movementProfile.test.ts +131 -0
  156. package/src/core/config.ts +9 -5
  157. package/src/core/contract.ts +0 -3
  158. package/src/core/deployments.ts +0 -12
  159. package/src/core/movementProfile.ts +75 -127
  160. package/src/fork/__tests__/manager.test.ts +1 -1
  161. package/src/fork/__tests__/server.cors.test.ts +101 -0
  162. package/src/fork/api.ts +69 -10
  163. package/src/fork/manager.ts +1 -41
  164. package/src/fork/server.ts +38 -9
  165. package/src/fork/test.ts +0 -1
  166. package/src/harness/Harness.ts +16 -13
  167. package/src/harness/codeObject.ts +38 -48
  168. package/src/harness/script.ts +40 -39
  169. package/src/helpers/__tests__/setupLocalTesting.fork-network.test.ts +212 -0
  170. package/src/helpers/setupLocalTesting.ts +37 -4
  171. package/src/index.ts +9 -2
  172. package/src/node/LocalNodeManager.ts +24 -2
  173. package/src/node/__tests__/LocalNodeManager.api-port.test.ts +62 -0
  174. package/src/node/__tests__/LocalNodeManager.test.ts +5 -4
  175. package/src/runtime.ts +1 -3
  176. package/src/templates/move/Move.toml +1 -1
  177. package/src/templates/move/sources/Counter.move +31 -4
  178. package/src/templates/scripts/deploy-counter.ts +11 -1
  179. package/src/templates/tests/Counter.test.ts +2 -2
  180. package/src/types/config.ts +8 -1
  181. package/src/types/runtime.ts +2 -2
  182. package/src/utils/__tests__/childProcessAdapter.maxBuffer.test.ts +51 -0
  183. package/src/utils/address.ts +0 -4
  184. package/src/utils/childProcessAdapter.ts +35 -6
@@ -35,6 +35,13 @@ export interface RunInput {
35
35
  * Default: `false`.
36
36
  */
37
37
  inheritStdio?: boolean;
38
+ /**
39
+ * Maximum combined bytes (stdout + stderr) the captured Buffers may
40
+ * grow to before the child is killed and the promise rejects. Defaults
41
+ * to 64 MiB. Set to `Infinity` to disable. Ignored when
42
+ * `inheritStdio` is `true` (no buffering happens).
43
+ */
44
+ maxBuffer?: number;
38
45
  }
39
46
  export interface RunResult {
40
47
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"childProcessAdapter.d.ts","sourceRoot":"","sources":["../../src/utils/childProcessAdapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACzC,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,CAAC;CAC1C;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IACvC;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CACzE;AAyHD,eAAO,MAAM,0BAA0B,EAAE,mBAAsD,CAAC"}
1
+ {"version":3,"file":"childProcessAdapter.d.ts","sourceRoot":"","sources":["../../src/utils/childProcessAdapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,MAAM,WAAW,mBAAmB;IAClC,GAAG,CAAC,KAAK,EAAE,QAAQ,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACzC,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,cAAc,CAAC;CAC1C;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAID,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CAC3B;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,GAAG,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,QAAQ,GAAG,IAAI,CAAC;IACxB,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IACvB,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IACvC;;;;OAIG;IACH,MAAM,EAAE,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC,CAAC;CACzE;AA6ID,eAAO,MAAM,0BAA0B,EAAE,mBAAsD,CAAC"}
@@ -1,13 +1,13 @@
1
1
  import { spawn } from 'node:child_process';
2
+ const DEFAULT_MAX_BUFFER = 64 * 1024 * 1024;
2
3
  const DEFAULT_TIMEOUT_MS = 5 * 60 * 1000;
3
4
  class DefaultChildProcessAdapter {
4
5
  run(input) {
5
6
  // Skip the default 5-minute timeout when the caller wires stdio
6
7
  // through to the terminal — interactive sessions (mocha, tsx scripts,
7
- // `movement move test`, `pnpm install`) routinely exceed 5 minutes and
8
- // SIGTERM'ing them silently is a regression vs the direct-spawn code
9
- // these callers used before M1.3a. An explicitly-passed `timeoutMs`
10
- // is always honored, regardless of `inheritStdio`.
8
+ // `movement move test`, `pnpm install`) routinely exceed 5 minutes
9
+ // and SIGTERM'ing them silently would be a regression. An explicit
10
+ // `timeoutMs` is always honored, regardless of `inheritStdio`.
11
11
  const timeoutMs = input.timeoutMs ?? (input.inheritStdio ? undefined : DEFAULT_TIMEOUT_MS);
12
12
  return new Promise((resolve, reject) => {
13
13
  const child = spawn(input.command, [...input.args], {
@@ -17,9 +17,26 @@ class DefaultChildProcessAdapter {
17
17
  });
18
18
  const stdoutChunks = [];
19
19
  const stderrChunks = [];
20
+ let totalBytes = 0;
21
+ let overflowed = false;
22
+ const maxBuffer = input.maxBuffer ?? DEFAULT_MAX_BUFFER;
23
+ const onChunk = (chunks) => (chunk) => {
24
+ if (overflowed)
25
+ return;
26
+ totalBytes += chunk.length;
27
+ if (totalBytes > maxBuffer) {
28
+ overflowed = true;
29
+ clearTimer();
30
+ input.signal?.removeEventListener('abort', onAbort);
31
+ child.kill('SIGTERM');
32
+ reject(new Error(`Command output exceeded maxBuffer (${maxBuffer} bytes): ${input.command}`));
33
+ return;
34
+ }
35
+ chunks.push(chunk);
36
+ };
20
37
  // Streams are null when stdio is 'inherit'; the `?.` covers that.
21
- child.stdout?.on('data', (chunk) => stdoutChunks.push(chunk));
22
- child.stderr?.on('data', (chunk) => stderrChunks.push(chunk));
38
+ child.stdout?.on('data', onChunk(stdoutChunks));
39
+ child.stderr?.on('data', onChunk(stderrChunks));
23
40
  let timeoutHandle;
24
41
  const clearTimer = () => {
25
42
  if (timeoutHandle)
@@ -1 +1 @@
1
- {"version":3,"file":"childProcessAdapter.js","sourceRoot":"","sources":["../../src/utils/childProcessAdapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"childProcessAdapter.js","sourceRoot":"","sources":["../../src/utils/childProcessAdapter.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "movehat",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "type": "module",
5
5
  "description": "Hardhat-like development framework for Movement L1 smart contracts",
6
6
  "bin": {
@@ -27,6 +27,7 @@
27
27
  "test:integration": "vitest run --config vitest.integration.config.ts",
28
28
  "docs:api": "typedoc && node scripts/postprocess-typedoc.mjs",
29
29
  "bench": "cd ../../examples/counter-example && tsx ../../packages/movehat/bench/fork.bench.ts",
30
+ "prepack": "cp ../../README.md README.md",
30
31
  "prepublishOnly": "npm run build"
31
32
  },
32
33
  "files": [
@@ -149,19 +149,19 @@ version = "0.0.1"
149
149
  expect(existsSync(join(tmpHome, ".aptos", "config.yaml"))).toBe(false);
150
150
  });
151
151
 
152
- it("two concurrent deploys do not corrupt ~/.aptos/config.yaml (#37)", async () => {
153
- // Pre-fix #37: both deploys overwrote ~/.aptos/config.yaml with the
154
- // SAME profile name ("default" by default), then both restored
155
- // independently. The second deploy's restore would overwrite the
156
- // first's profile mid-publish potential cross-contamination of
157
- // private keys / accounts. Post-fix: each deploy uses a unique
158
- // movehat-deploy-<uuid> profile name and only deletes its own key
159
- // on cleanup. The user's other profiles never get touched.
160
- //
161
- // The mutex is what makes this safe without it, the
162
- // read-modify-write cycles would race and silently drop a profile.
163
-
164
- // Seed the yaml with an unrelated user profile that MUST survive.
152
+ it("two concurrent deploys use distinct temp key files and never touch ~/.aptos/config.yaml", async () => {
153
+ // Each deploy writes its private key to a UUID-named temp file
154
+ // (see `core/movementProfile.ts:writeTempKeyFile`) and passes
155
+ // `--private-key-file <path>` to Movement CLI. Concurrent deploys
156
+ // have no shared state — no profile YAML, no mutex, no race.
157
+ // Asserts:
158
+ // 1. Each invocation records a DISTINCT --private-key-file path.
159
+ // 2. After both deploys finish, neither temp key file remains on
160
+ // disk (cleanup ran on both happy paths).
161
+ // 3. ~/.aptos/config.yaml is byte-identical to what was on disk
162
+ // before the new flow doesn't touch the user's CLI config.
163
+
164
+ // Seed an unrelated user profile that MUST survive untouched.
165
165
  const aptosDir = join(tmpHome, ".aptos");
166
166
  mkdirSync(aptosDir, { recursive: true });
167
167
  const preExisting = {
@@ -176,10 +176,11 @@ version = "0.0.1"
176
176
  };
177
177
  const configPath = join(aptosDir, "config.yaml");
178
178
  writeFileSync(configPath, yaml.dump(preExisting), { mode: 0o600 });
179
+ const initialConfigBytes = readFileSync(configPath, "utf8");
179
180
 
180
- // Set up two Publisher instances with fake adapters that record their
181
- // own --profile argument and inject a small delay on publish so the
182
- // critical sections overlap.
181
+ // Set up two Publisher instances with fake adapters that record
182
+ // their --private-key-file argument and inject a small delay on
183
+ // publish so the critical sections overlap.
183
184
  function makeDelayedAdapter(label: string): {
184
185
  adapter: ChildProcessAdapter;
185
186
  captured: { publishCall?: RunInput };
@@ -234,38 +235,41 @@ version = "0.0.1"
234
235
  }),
235
236
  ]);
236
237
 
237
- // Both publish calls captured distinct --profile args.
238
+ // Both publish calls captured distinct --private-key-file args.
238
239
  const argsA = a.captured.publishCall!.args;
239
240
  const argsB = b.captured.publishCall!.args;
240
- const profileArgA = argsA[argsA.indexOf("--profile") + 1];
241
- const profileArgB = argsB[argsB.indexOf("--profile") + 1];
242
- expect(profileArgA).toMatch(/^movehat-deploy-/);
243
- expect(profileArgB).toMatch(/^movehat-deploy-/);
244
- expect(profileArgA).not.toBe(profileArgB);
245
-
246
- // After both deploys finish, ~/.aptos/config.yaml contains the
247
- // user's original profile and zero movehat-deploy-* profiles.
248
- const finalYaml: any = yaml.load(readFileSync(configPath, "utf8"));
249
- expect(finalYaml.profiles).toBeDefined();
250
- expect(Object.keys(finalYaml.profiles)).toEqual(["user_main"]);
251
- expect(finalYaml.profiles.user_main.private_key).toBe(
252
- preExisting.profiles.user_main.private_key
253
- );
241
+ const keyFileArgA = argsA[argsA.indexOf("--private-key-file") + 1] as string;
242
+ const keyFileArgB = argsB[argsB.indexOf("--private-key-file") + 1] as string;
243
+ expect(keyFileArgA).toMatch(/movehat-key-/);
244
+ expect(keyFileArgB).toMatch(/movehat-key-/);
245
+ expect(keyFileArgA).not.toBe(keyFileArgB);
246
+
247
+ // Cleanup ran on both neither temp file persists after the
248
+ // deploys finished normally.
249
+ expect(existsSync(keyFileArgA)).toBe(false);
250
+ expect(existsSync(keyFileArgB)).toBe(false);
251
+
252
+ // ~/.aptos/config.yaml byte-identical to pre-deploy state — the new
253
+ // flow never touches the user's CLI config.
254
+ expect(readFileSync(configPath, "utf8")).toBe(initialConfigBytes);
254
255
  });
255
256
 
256
- it("SIGINT mid-deploy cleans the profile from ~/.aptos/config.yaml (#36)", async () => {
257
- // Pre-fix #36: between the yaml write (private key persisted) and the
258
- // finally block, a SIGINT would skip cleanup and leave the user's
259
- // private key sitting in ~/.aptos/config.yaml at mode 0o600.
260
- // Post-fix: a sync signal handler runs synchronously before exit,
261
- // removing the deploy's unique profile from the yaml.
257
+ it("SIGINT mid-deploy unlinks the temp key file", async () => {
258
+ // Without sync SIGINT cleanup, the temp private-key file written
259
+ // by `writeTempKeyFile` would persist on disk after an abnormal
260
+ // exit (chmod 0o600 prevents other users from reading it, but
261
+ // forensic recovery from /tmp is still possible). The sync signal
262
+ // handler runs synchronously before process.exit and unlinks
263
+ // every active deploy's key file.
262
264
  //
263
265
  // This test spawns a child process running a harness that drives
264
266
  // Publisher.deploy() with a 3-second-delayed publish, then sends
265
267
  // SIGINT mid-flight. Vitest's own process is unaffected because
266
268
  // the SIGINT goes to the child.
267
269
 
268
- // Seed an unrelated user profile that MUST survive.
270
+ // Seed an unrelated user profile that MUST be left untouched —
271
+ // the new flow doesn't read or write ~/.aptos/config.yaml at all,
272
+ // so this is an invariant check.
269
273
  const aptosDir = join(tmpHome, ".aptos");
270
274
  mkdirSync(aptosDir, { recursive: true });
271
275
  const configPath = join(aptosDir, "config.yaml");
@@ -283,6 +287,7 @@ version = "0.0.1"
283
287
  }),
284
288
  { mode: 0o600 }
285
289
  );
290
+ const initialConfigBytes = readFileSync(configPath, "utf8");
286
291
 
287
292
  const harnessPath = join(__dirname, "fixtures", "sigint-deploy-harness.ts");
288
293
  // Resolve tsx's CLI binary by absolute path — the test's tmp cwd has
@@ -302,8 +307,8 @@ version = "0.0.1"
302
307
  }
303
308
  );
304
309
 
305
- // Wait for the harness to announce its unique profile name via stdout
306
- // (it writes a JSON line `{"profile":"movehat-deploy-XXXX"}` just
310
+ // Wait for the harness to announce its temp key file path via stdout
311
+ // (it writes a JSON line `{"keyFile":"/tmp/movehat-key-XXXX"}` just
307
312
  // before entering the slow publish step).
308
313
  let announced: string | undefined;
309
314
  let stdoutBuf = "";
@@ -315,7 +320,7 @@ version = "0.0.1"
315
320
  if (!trimmed.startsWith("{")) continue;
316
321
  try {
317
322
  const parsed = JSON.parse(trimmed);
318
- if (typeof parsed.profile === "string") announced = parsed.profile;
323
+ if (typeof parsed.keyFile === "string") announced = parsed.keyFile;
319
324
  } catch {
320
325
  /* not a JSON line we care about */
321
326
  }
@@ -333,12 +338,17 @@ version = "0.0.1"
333
338
  if (!announced) {
334
339
  child.kill("SIGKILL");
335
340
  throw new Error(
336
- `harness never announced profile in 8s.\n` +
341
+ `harness never announced keyFile in 8s.\n` +
337
342
  `stdout so far:\n${stdoutBuf}\n---\nstderr so far:\n${stderrBuf}`
338
343
  );
339
344
  }
340
345
 
341
- expect(announced).toMatch(/^movehat-deploy-/);
346
+ expect(announced).toMatch(/movehat-key-/);
347
+ // The temp key file is present on disk while the harness is in
348
+ // the middle of the slow publish (the JSON announcement is emitted
349
+ // right before the simulated 3s wait, and the file is unlinked
350
+ // only on cleanup).
351
+ expect(existsSync(announced)).toBe(true);
342
352
 
343
353
  // Deliver SIGINT mid-publish and wait for the harness to exit.
344
354
  child.kill("SIGINT");
@@ -347,13 +357,12 @@ version = "0.0.1"
347
357
  });
348
358
  expect(exitCode).toBe(130);
349
359
 
350
- // The yaml on disk contains the user's original profile and NO
351
- // movehat-deploy-* leftovers.
352
- expect(existsSync(configPath)).toBe(true);
353
- const finalYaml: any = yaml.load(readFileSync(configPath, "utf8"));
354
- expect(finalYaml.profiles).toBeDefined();
355
- expect(Object.keys(finalYaml.profiles).sort()).toEqual(["user_main"]);
356
- expect(finalYaml.profiles.user_main.private_key).toBe("0x" + "a".repeat(64));
360
+ // The temp key file has been unlinked by the SIGINT handler.
361
+ expect(existsSync(announced)).toBe(false);
362
+
363
+ // The user's ~/.aptos/config.yaml is byte-identical to pre-deploy
364
+ // — the new flow never touches it.
365
+ expect(readFileSync(configPath, "utf8")).toBe(initialConfigBytes);
357
366
  }, 15000);
358
367
 
359
368
  it("does not mutate Move.toml during deploy (#38)", async () => {
@@ -0,0 +1,32 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import * as publicSurface from "../index.js";
3
+
4
+ /**
5
+ * Locks the public export surface of `movehat`. Adding a new symbol
6
+ * is a deliberate API change; removing one is a breaking change.
7
+ * Update this list (and the CHANGELOG) when the surface evolves.
8
+ */
9
+ const EXPECTED_RUNTIME_EXPORTS = [
10
+ "Harness",
11
+ "HarnessDisposedError",
12
+ "ForkManager",
13
+ "MovementApiClient",
14
+ "ForkStorage",
15
+ "ForkServer",
16
+ "ModuleAlreadyDeployedError",
17
+ "PostPublishError",
18
+ "initRuntime",
19
+ ] as const;
20
+
21
+ describe("public export surface (movehat root)", () => {
22
+ it.each(EXPECTED_RUNTIME_EXPORTS)("exports %s as a runtime value", (name) => {
23
+ expect(publicSurface[name as keyof typeof publicSurface]).toBeDefined();
24
+ });
25
+
26
+ // Type-only exports cannot be probed at runtime; the assertion is
27
+ // that the module imports successfully (failing types-only export
28
+ // would surface as a TS error in `pnpm check:example`).
29
+ it("imports without errors", () => {
30
+ expect(typeof publicSurface).toBe("object");
31
+ });
32
+ });
@@ -9,9 +9,10 @@
9
9
  * send SIGINT mid-flight.
10
10
  * 2. Drive `Publisher.deploy()` against the fake adapter using a
11
11
  * synthetic MovehatConfig + Account read from env vars.
12
- * 3. Write the chosen `movehat-deploy-<uuid>` profile name to stdout
13
- * as JSON (`{"profile": "..."}`) before the slow publish so the
14
- * parent test knows which key to look for after SIGINT.
12
+ * 3. Write the temp key file path to stdout as JSON
13
+ * (`{"keyFile": "/tmp/movehat-key-<uuid>"}`) before the slow
14
+ * publish so the parent test knows which file to look for after
15
+ * SIGINT.
15
16
  * 4. If the deploy completes naturally (test failure case), exit 0.
16
17
  * 5. When SIGINT arrives, Publisher's signal handler runs synchronous
17
18
  * cleanup and `setImmediate(() => process.exit(130))`.
@@ -61,10 +62,10 @@ async function main() {
61
62
  return { exitCode: 0, stdout: "built", stderr: "" };
62
63
  }
63
64
  if (input.args[1] === "publish") {
64
- // Surface the unique profile name to the parent BEFORE blocking.
65
- const profileIdx = input.args.indexOf("--profile");
66
- const profile = profileIdx >= 0 ? input.args[profileIdx + 1] : "";
67
- process.stdout.write(JSON.stringify({ profile }) + "\n");
65
+ // Surface the temp key file path to the parent BEFORE blocking.
66
+ const keyFileIdx = input.args.indexOf("--private-key-file");
67
+ const keyFile = keyFileIdx >= 0 ? input.args[keyFileIdx + 1] : "";
68
+ process.stdout.write(JSON.stringify({ keyFile }) + "\n");
68
69
  // Hold long enough for the parent to deliver SIGINT.
69
70
  await new Promise((r) => setTimeout(r, 3000));
70
71
  return {
@@ -3,8 +3,8 @@ import type { ClientRequest, IncomingMessage } from "node:http";
3
3
  import { EventEmitter } from "node:events";
4
4
 
5
5
  /**
6
- * Tests for `MovementApiClient` Authorization header injection (M2.4
7
- * follow-up — wires the apiKey threading from `Harness.createFork`).
6
+ * Tests for `MovementApiClient` Authorization header injection
7
+ * verifies the apiKey threading from `Harness.createFork`.
8
8
  *
9
9
  * Strategy: `vi.mock` `node:https` and `node:http` so the test
10
10
  * captures the request options passed to `client.get(url, options, cb)`
@@ -77,6 +77,11 @@ function setupGetCapture(): {
77
77
 
78
78
  const fakeReq = new EventEmitter() as unknown as ClientRequest;
79
79
  (fakeReq as unknown as { end: () => void }).end = () => {};
80
+ // F3: api.ts now installs a setTimeout on the request and may call
81
+ // destroy() on overflow / timeout. Stub both so this happy-path
82
+ // capture mock still satisfies the new contract.
83
+ (fakeReq as unknown as { setTimeout: (ms: number, cb?: () => void) => void }).setTimeout = () => {};
84
+ (fakeReq as unknown as { destroy: () => void }).destroy = () => {};
80
85
 
81
86
  if (callback) {
82
87
  const body = JSON.stringify({
@@ -0,0 +1,150 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import type { ClientRequest, IncomingMessage } from "node:http";
3
+ import { EventEmitter } from "node:events";
4
+
5
+ /**
6
+ * F3 — MovementApiClient must bound responses by time AND bytes.
7
+ *
8
+ * Without these guards a malicious / hung upstream can:
9
+ * - leak the request promise forever (never emits 'end'), or
10
+ * - exhaust heap by pushing unbounded `data` chunks.
11
+ *
12
+ * Strategy mirrors src/__tests__/fork/api.test.ts: vi.mock node:http
13
+ * and node:https, intercept `client.get(url, options, cb)`, and feed
14
+ * a controllable `IncomingMessage` to the callback. The fake
15
+ * `ClientRequest` exposes `setTimeout`, `destroy`, and emits 'error' /
16
+ * 'timeout' so we can drive the failure modes from the test.
17
+ */
18
+
19
+ interface FakeReq extends EventEmitter {
20
+ end(): void;
21
+ destroy(err?: Error): void;
22
+ setTimeout(ms: number, cb?: () => void): void;
23
+ destroyed: boolean;
24
+ }
25
+
26
+ const httpsGet = vi.fn();
27
+ const httpGet = vi.fn();
28
+
29
+ vi.mock("https", () => ({ default: { get: httpsGet }, get: httpsGet }));
30
+ vi.mock("http", () => ({ default: { get: httpGet }, get: httpGet }));
31
+
32
+ function makeFakeReq(): FakeReq {
33
+ const req = new EventEmitter() as FakeReq;
34
+ req.destroyed = false;
35
+ let timeoutHandle: NodeJS.Timeout | undefined;
36
+ req.end = () => {};
37
+ req.destroy = (err?: Error) => {
38
+ req.destroyed = true;
39
+ if (timeoutHandle) clearTimeout(timeoutHandle);
40
+ setImmediate(() => req.emit("error", err ?? new Error("destroyed")));
41
+ };
42
+ req.setTimeout = (ms: number, cb?: () => void) => {
43
+ timeoutHandle = setTimeout(() => {
44
+ req.emit("timeout");
45
+ if (cb) cb();
46
+ }, ms);
47
+ // Don't keep the event loop alive — Node sets this itself but the
48
+ // mock has no native socket to inherit from.
49
+ timeoutHandle.unref?.();
50
+ };
51
+ return req;
52
+ }
53
+
54
+ function makeUnresolvableResponse(): IncomingMessage {
55
+ const res = new EventEmitter() as unknown as IncomingMessage;
56
+ (res as unknown as { statusCode: number }).statusCode = 200;
57
+ return res;
58
+ }
59
+
60
+ function makeStreamingResponse(
61
+ bytesPerChunk: number,
62
+ chunks: number
63
+ ): IncomingMessage {
64
+ const res = new EventEmitter() as unknown as IncomingMessage;
65
+ (res as unknown as { statusCode: number }).statusCode = 200;
66
+ setImmediate(() => {
67
+ let i = 0;
68
+ const pump = () => {
69
+ if (i >= chunks) {
70
+ (res as unknown as EventEmitter).emit("end");
71
+ return;
72
+ }
73
+ (res as unknown as EventEmitter).emit(
74
+ "data",
75
+ Buffer.alloc(bytesPerChunk, 0x61)
76
+ );
77
+ i++;
78
+ setImmediate(pump);
79
+ };
80
+ pump();
81
+ });
82
+ return res;
83
+ }
84
+
85
+ describe("F3 — MovementApiClient timeouts and byte cap", () => {
86
+ beforeEach(() => {
87
+ httpsGet.mockReset();
88
+ httpGet.mockReset();
89
+ });
90
+
91
+ afterEach(() => {
92
+ vi.restoreAllMocks();
93
+ });
94
+
95
+ it("rejects with a timeout error when the upstream never responds", async () => {
96
+ const fakeReq = makeFakeReq();
97
+ httpGet.mockImplementation(
98
+ (
99
+ _url: string,
100
+ options: unknown,
101
+ cb?: (res: IncomingMessage) => void
102
+ ) => {
103
+ const callback =
104
+ typeof options === "function"
105
+ ? (options as (r: IncomingMessage) => void)
106
+ : cb;
107
+ if (callback) callback(makeUnresolvableResponse());
108
+ return fakeReq as unknown as ClientRequest;
109
+ }
110
+ );
111
+
112
+ const { MovementApiClient } = await import("../../fork/api.js");
113
+ const client = new MovementApiClient("http://hung.example/v1", undefined, {
114
+ timeoutMs: 25,
115
+ maxBytes: 1024 * 1024,
116
+ });
117
+
118
+ await expect(client.getLedgerInfo()).rejects.toThrow(/timed out|timeout/i);
119
+ expect(fakeReq.destroyed).toBe(true);
120
+ });
121
+
122
+ it("rejects and destroys the request when the response exceeds maxBytes", async () => {
123
+ const fakeReq = makeFakeReq();
124
+ httpGet.mockImplementation(
125
+ (
126
+ _url: string,
127
+ options: unknown,
128
+ cb?: (res: IncomingMessage) => void
129
+ ) => {
130
+ const callback =
131
+ typeof options === "function"
132
+ ? (options as (r: IncomingMessage) => void)
133
+ : cb;
134
+ if (callback) callback(makeStreamingResponse(2048, 100));
135
+ return fakeReq as unknown as ClientRequest;
136
+ }
137
+ );
138
+
139
+ const { MovementApiClient } = await import("../../fork/api.js");
140
+ const client = new MovementApiClient("http://big.example/v1", undefined, {
141
+ timeoutMs: 5000,
142
+ maxBytes: 4096,
143
+ });
144
+
145
+ await expect(client.getLedgerInfo()).rejects.toThrow(
146
+ /maxBytes|too large|exceeded/i
147
+ );
148
+ expect(fakeReq.destroyed).toBe(true);
149
+ });
150
+ });
@@ -4,17 +4,15 @@ import { Harness, HarnessDisposedError } from "../../harness/index.js";
4
4
  import { setupHarnessTestFixture, type HarnessTestFixture } from "./_fixture.js";
5
5
 
6
6
  /**
7
- * Proxy poisoning is the load-bearing safety guarantee of M2: once a
8
- * Harness has been cleaned up, any further deploy / view / script /
9
- * upgrade call must throw `HarnessDisposedError` synchronously on
10
- * property access — not after the awaited method body. These tests
11
- * lock that contract.
7
+ * Proxy poisoning is the load-bearing safety guarantee of the Harness:
8
+ * once cleaned up, any further deploy / view / script / upgrade call
9
+ * must throw `HarnessDisposedError` synchronously on property access —
10
+ * not after the awaited method body. These tests lock that contract.
12
11
  *
13
12
  * Uses `Harness.createLive(network)` because it does not spawn a real
14
13
  * Movement node — `initRuntime` only constructs the SDK client (no RPC
15
14
  * round-trip) from the fixture config. `createLocal` / `createFork`
16
- * runtime tests live in the M4 integration suite where spinning a real
17
- * node is acceptable.
15
+ * runtime tests live in the integration suite.
18
16
  */
19
17
  describe("Harness — proxy poisoning", () => {
20
18
  let fixture: HarnessTestFixture;
@@ -93,10 +91,8 @@ describe("Harness — proxy poisoning", () => {
93
91
  expect(harness.runtime).toBeDefined();
94
92
  });
95
93
 
96
- // The M2.3-era "stubs reject" assertion was retired when M2.3 shipped
97
- // real bodies for runViewFunction and runMoveScript. All four methods
98
- // now have dedicated test suites (codeObject.deploy/upgrade.test.ts,
99
- // view.test.ts, script.test.ts).
94
+ // Dedicated suites exist for each of the four methods
95
+ // (codeObject.deploy/upgrade.test.ts, view.test.ts, script.test.ts).
100
96
 
101
97
  it("await harness.someAsyncMethod() pattern: post-cleanup throw happens before await", async () => {
102
98
  const harness = await Harness.createLive("testnet");
@@ -304,7 +304,7 @@ describe("Harness.deployCodeObject", () => {
304
304
  }
305
305
  });
306
306
 
307
- it("fork-mode harness throws synchronously before any CLI call (covered separately for createLocal/createFork in M4 integration suite)", async () => {
307
+ it("fork-mode harness throws synchronously before any CLI call (createLocal/createFork covered by the integration suite)", async () => {
308
308
  // This case can't be tested with a real createFork (it spawns a fork
309
309
  // server). Instead, prove that the disposed-instance path also fires
310
310
  // synchronously: a poisoned harness throws HarnessDisposedError on
@@ -7,9 +7,9 @@ import { setupHarnessTestFixture, type HarnessTestFixture } from "./_fixture.js"
7
7
  * Tests for `Harness.runViewFunction` — the SDK delegation path.
8
8
  *
9
9
  * The Aptos SDK isn't behind an injectable adapter, so we monkey-patch
10
- * `harness.runtime.aptos.view` after `createLive`. One-off for M2.3.
11
- * Acceptable here because the function under test is a 6-line wrapper
12
- * that only forwards options.
10
+ * `harness.runtime.aptos.view` after `createLive`. Acceptable here
11
+ * because the function under test is a 6-line wrapper that only
12
+ * forwards options.
13
13
  */
14
14
  describe("Harness.runViewFunction", () => {
15
15
  let fixture: HarnessTestFixture;