@ic-reactor/mcp 0.0.0-stage → 0.0.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.
Files changed (1041) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +108 -0
  2. package/.github/ISSUE_TEMPLATE/config.yml +8 -0
  3. package/.github/ISSUE_TEMPLATE/feature_request.yml +77 -0
  4. package/.github/actions/setup-rust-wasm/action.yml +25 -0
  5. package/.github/dependabot.yml +107 -0
  6. package/.github/workflows/auth-latest.yml +67 -0
  7. package/.github/workflows/b3pay-site.yml +49 -0
  8. package/.github/workflows/ci.yml +430 -0
  9. package/.github/workflows/codeql.yml +41 -0
  10. package/.github/workflows/dependency-review.yml +68 -0
  11. package/.github/workflows/docs-check.yml +98 -0
  12. package/.github/workflows/docs.yml +265 -0
  13. package/.github/workflows/e2e.yml +90 -0
  14. package/.github/workflows/examples-published.yml +97 -0
  15. package/.github/workflows/release.yml +326 -0
  16. package/.husky/pre-commit +12 -0
  17. package/.prettierignore +109 -0
  18. package/.prettierrc +6 -0
  19. package/AGENTS.md +103 -0
  20. package/CHANGELOG.md +3268 -0
  21. package/CLAUDE.md +93 -0
  22. package/CNAME +1 -0
  23. package/CODE_OF_CONDUCT.md +7 -0
  24. package/CONTRIBUTING.md +433 -0
  25. package/LICENSE +21 -0
  26. package/README.md +2 -2
  27. package/SECURITY.md +74 -0
  28. package/b3pay-module.json +194 -0
  29. package/docs/.remarkrc.json +10 -0
  30. package/docs/README.md +64 -0
  31. package/docs/astro.config.mjs +205 -0
  32. package/docs/favicon.svg +15 -0
  33. package/docs/package.json +39 -0
  34. package/docs/public/favicon.svg +15 -0
  35. package/docs/public/ic-reactor-social-card.png +0 -0
  36. package/docs/scripts/b3pay-static.mjs +22 -0
  37. package/docs/scripts/check-example-docs.mjs +214 -0
  38. package/docs/scripts/check-links.mjs +212 -0
  39. package/docs/scripts/fix-dist-links.js +125 -0
  40. package/docs/scripts/fix-frontmatter.js +71 -0
  41. package/docs/scripts/remove-redundant-h1.cjs +48 -0
  42. package/docs/scripts/rewrite-typedoc-links.js +110 -0
  43. package/docs/scripts/split-index.js +69 -0
  44. package/docs/src/assets/ic-reactor-hero-dark.svg +23 -0
  45. package/docs/src/assets/ic-reactor-hero-light.svg +23 -0
  46. package/docs/src/assets/ic-reactor-lockup-dark.svg +15 -0
  47. package/docs/src/assets/ic-reactor-lockup-light.svg +15 -0
  48. package/docs/src/assets/ic-reactor-mark-dark.svg +14 -0
  49. package/docs/src/assets/ic-reactor-mark-light.svg +14 -0
  50. package/docs/src/assets/layers/candid-core-layer-icon-dark.svg +4 -0
  51. package/docs/src/assets/layers/candid-core-layer-icon-light.svg +4 -0
  52. package/docs/src/assets/layers/candid-profile-layer-icon-dark.svg +4 -0
  53. package/docs/src/assets/layers/candid-profile-layer-icon-light.svg +4 -0
  54. package/docs/src/assets/layers/ic-reactor-layer-icon-dark.svg +4 -0
  55. package/docs/src/assets/layers/ic-reactor-layer-icon-light.svg +4 -0
  56. package/docs/src/components/LibraryCards.astro +172 -0
  57. package/docs/src/components/LibraryIcon.astro +28 -0
  58. package/docs/src/components/SiteTitle.astro +241 -0
  59. package/docs/src/content/docs/404.mdx +19 -0
  60. package/docs/src/content/docs/build/agent-tools.mdx +593 -0
  61. package/docs/src/content/docs/build/icrc1-wallet.mdx +619 -0
  62. package/docs/src/content/docs/build/ssr-per-user.mdx +552 -0
  63. package/docs/src/content/docs/examples/canister-studio.mdx +95 -0
  64. package/docs/src/content/docs/examples/icrc-ledger.mdx +45 -0
  65. package/docs/src/content/docs/examples/index.mdx +38 -0
  66. package/docs/src/content/docs/examples/next-ssr.mdx +73 -0
  67. package/docs/src/content/docs/examples/node-agent-tool.mdx +80 -0
  68. package/docs/src/content/docs/examples/vite-wallet.mdx +71 -0
  69. package/docs/src/content/docs/guides/auth.mdx +1181 -0
  70. package/docs/src/content/docs/guides/client.mdx +551 -0
  71. package/docs/src/content/docs/guides/deploy.mdx +180 -0
  72. package/docs/src/content/docs/guides/errors.mdx +475 -0
  73. package/docs/src/content/docs/guides/getting-started.mdx +620 -0
  74. package/docs/src/content/docs/guides/reads.mdx +586 -0
  75. package/docs/src/content/docs/guides/ssr.mdx +529 -0
  76. package/docs/src/content/docs/guides/testing.mdx +401 -0
  77. package/docs/src/content/docs/guides/values.mdx +261 -0
  78. package/docs/src/content/docs/guides/vite-plugin.mdx +690 -0
  79. package/docs/src/content/docs/guides/writes.mdx +572 -0
  80. package/docs/src/content/docs/ic-reactor/index.mdx +167 -0
  81. package/docs/src/content/docs/index.mdx +65 -0
  82. package/docs/src/content/docs/migrating-from-3.mdx +535 -0
  83. package/docs/src/content/docs/packages/core.mdx +186 -0
  84. package/docs/src/content/docs/packages/mcp.mdx +163 -0
  85. package/docs/src/content/docs/packages/react.mdx +207 -0
  86. package/docs/src/content/docs/packages/vite-plugin.mdx +73 -0
  87. package/docs/src/content/docs/versioning.mdx +104 -0
  88. package/docs/src/content.config.ts +7 -0
  89. package/docs/src/styles/custom.css +189 -0
  90. package/docs/tsconfig.typedoc.json +26 -0
  91. package/docs/typedoc-entry.ts +17 -0
  92. package/docs/typedoc.json +37 -0
  93. package/e2e/Cargo.lock +646 -0
  94. package/e2e/Cargo.toml +3 -0
  95. package/e2e/README.md +101 -0
  96. package/e2e/frontends/build.mjs +94 -0
  97. package/e2e/frontends/index.html +10 -0
  98. package/e2e/global-setup.ts +65 -0
  99. package/e2e/icp.yaml +38 -0
  100. package/e2e/package.json +40 -0
  101. package/e2e/setup.ts +6 -0
  102. package/e2e/src/actor/Cargo.toml +14 -0
  103. package/e2e/src/actor/hello_actor.did +10 -0
  104. package/e2e/src/actor/src/lib.rs +73 -0
  105. package/e2e/src/calls.test.ts +132 -0
  106. package/e2e/src/declarations/hello_actor.envelope.json +175 -0
  107. package/e2e/src/declarations/hello_actor.ts +15 -0
  108. package/e2e/src/errors.test.ts +107 -0
  109. package/e2e/src/frontend-headers.test.ts +253 -0
  110. package/e2e/src/identity.test.ts +61 -0
  111. package/e2e/src/react.test.tsx +59 -0
  112. package/e2e/src/replica.ts +70 -0
  113. package/e2e/src/vite-env.test.ts +108 -0
  114. package/e2e/test.sh +47 -0
  115. package/e2e/tsconfig.json +20 -0
  116. package/e2e/tsconfig.typecheck.json +9 -0
  117. package/e2e/vitest.config.ts +22 -0
  118. package/eslint.config.mjs +100 -0
  119. package/evals/PREREGISTRATION.md +1346 -0
  120. package/evals/README.md +756 -0
  121. package/evals/conditions/thin/condition.json +8 -0
  122. package/evals/conditions/thin/docs/candid-core-cli.README.md +90 -0
  123. package/evals/conditions/thin/docs/candid-core-schema.README.md +339 -0
  124. package/evals/conditions/thin/package.json +25 -0
  125. package/evals/conditions/thin/starter/src/generated/icrc1.ts +57 -0
  126. package/evals/conditions/thin-guide/condition.json +10 -0
  127. package/evals/conditions/thin-guide/docs/llms.txt +292 -0
  128. package/evals/conditions/v3/condition.json +9 -0
  129. package/evals/conditions/v3/docs/ic-reactor-core.llms.txt +232 -0
  130. package/evals/conditions/v3/docs/ic-reactor-react.llms.txt +280 -0
  131. package/evals/conditions/v3/docs/llms-full.txt +1053 -0
  132. package/evals/conditions/v3/package.json +26 -0
  133. package/evals/conditions/v3/starter/src/declarations/icrc1.did.d.ts +95 -0
  134. package/evals/conditions/v3/starter/src/declarations/icrc1.did.js +140 -0
  135. package/evals/conditions/v4/PORTING.md +384 -0
  136. package/evals/conditions/v4/README.md +106 -0
  137. package/evals/conditions/v4/condition.json +5 -0
  138. package/evals/conditions/v4/docs/llms.txt +323 -0
  139. package/evals/conditions/v4/package.json +27 -0
  140. package/evals/conditions/v4/starter/src/generated/icrc1.ts +78 -0
  141. package/evals/conditions/v4-proto/condition.json +6 -0
  142. package/evals/conditions/v4-proto/docs/llms.txt +291 -0
  143. package/evals/conditions/v4-proto/lib/SHIM-NOTES.md +20 -0
  144. package/evals/conditions/v4-proto/lib/package.json +27 -0
  145. package/evals/conditions/v4-proto/lib/src/client.ts +471 -0
  146. package/evals/conditions/v4-proto/lib/src/errors.ts +214 -0
  147. package/evals/conditions/v4-proto/lib/src/index.ts +36 -0
  148. package/evals/conditions/v4-proto/lib/src/principal.ts +175 -0
  149. package/evals/conditions/v4-proto/lib/src/service.ts +69 -0
  150. package/evals/conditions/v4-proto/lib/src/types.ts +174 -0
  151. package/evals/conditions/v4-proto/lib/test/runtime.test.ts +170 -0
  152. package/evals/conditions/v4-proto/lib/test/tsconfig.json +10 -0
  153. package/evals/conditions/v4-proto/lib/test/types.test-d.ts +145 -0
  154. package/evals/conditions/v4-proto/lib/test/vitest.config.mjs +8 -0
  155. package/evals/conditions/v4-proto/lib/tsconfig.json +17 -0
  156. package/evals/conditions/v4-proto/package.json +26 -0
  157. package/evals/conditions/v4-proto/starter/src/generated/icrc1.service.ts +19 -0
  158. package/evals/conditions/v4-proto/starter/src/generated/icrc1.ts +57 -0
  159. package/evals/drive.mjs +1221 -0
  160. package/evals/gate.mjs +161 -0
  161. package/evals/harness/aggregate.mjs +534 -0
  162. package/evals/harness/aggregate.test.mjs +344 -0
  163. package/evals/harness/assemble.mjs +216 -0
  164. package/evals/harness/assemble.test.mjs +143 -0
  165. package/evals/harness/auth.mjs +91 -0
  166. package/evals/harness/check-docs.mjs +157 -0
  167. package/evals/harness/declarations/icrc1.did.d.ts +95 -0
  168. package/evals/harness/declarations/icrc1.did.js +140 -0
  169. package/evals/harness/drive-auth.test.mjs +584 -0
  170. package/evals/harness/drive.test.mjs +509 -0
  171. package/evals/harness/fake-auth.ts +67 -0
  172. package/evals/harness/fake-ledger.ts +178 -0
  173. package/evals/harness/gate-plan.mjs +104 -0
  174. package/evals/harness/gate-plan.test.mjs +243 -0
  175. package/evals/harness/icrc1.did +88 -0
  176. package/evals/harness/judge.mjs +63 -0
  177. package/evals/harness/judge.test.mjs +81 -0
  178. package/evals/harness/leak-scan.mjs +1373 -0
  179. package/evals/harness/leak-scan.test.mjs +1300 -0
  180. package/evals/harness/probes/amounts.ts +14 -0
  181. package/evals/harness/probes/node-tool.probe.ts +48 -0
  182. package/evals/harness/probes/probe.mjs +149 -0
  183. package/evals/harness/probes/react-wallet.probe.ts +96 -0
  184. package/evals/harness/public/support/auth.ts +38 -0
  185. package/evals/harness/public/support/fake-replica.d.ts +28 -0
  186. package/evals/harness/public/support/fake-replica.js +434 -0
  187. package/evals/harness/public/support/ledger.idl.d.ts +95 -0
  188. package/evals/harness/public/support/ledger.idl.js +140 -0
  189. package/evals/harness/public/support/world.ts +179 -0
  190. package/evals/harness/runs.mjs +139 -0
  191. package/evals/harness/sandbox.mjs +88 -0
  192. package/evals/harness/sandbox.test.mjs +134 -0
  193. package/evals/harness/ship.mjs +264 -0
  194. package/evals/harness/ship.test.mjs +552 -0
  195. package/evals/harness/world.ts +291 -0
  196. package/evals/package.json +26 -0
  197. package/evals/pnpm-lock.yaml +1537 -0
  198. package/evals/pnpm-workspace.yaml +12 -0
  199. package/evals/results/2026-09-30T14-22-05-924Z/pilot.json +384 -0
  200. package/evals/results/2026-09-30T14-22-05-924Z/pilot.rescanned.json +404 -0
  201. package/evals/results/2026-09-30T14-22-05-924Z/pilot.rescanned.rescored.json +404 -0
  202. package/evals/results/2026-09-30T14-22-05-924Z/plan.json +240 -0
  203. package/evals/results/2026-09-30T14-22-05-924Z/summary.json +1800 -0
  204. package/evals/results/2026-09-30T14-22-05-924Z/summary.rescanned.json +1843 -0
  205. package/evals/results/2026-09-30T14-22-05-924Z/summary.rescanned.rescored.json +2100 -0
  206. package/evals/results/2026-09-30T15-48-23-343Z/pilot.json +404 -0
  207. package/evals/results/2026-09-30T15-48-23-343Z/pilot.rescored.json +404 -0
  208. package/evals/results/2026-09-30T15-48-23-343Z/plan.json +242 -0
  209. package/evals/results/2026-09-30T15-48-23-343Z/summary.json +1843 -0
  210. package/evals/results/2026-09-30T15-48-23-343Z/summary.rescored.json +2195 -0
  211. package/evals/results/2026-10-03-beta1-gate/pilot.json +162 -0
  212. package/evals/results/2026-10-03-beta1-gate/plan.json +141 -0
  213. package/evals/results/2026-10-03-beta1-gate/summary.json +983 -0
  214. package/evals/results/2026-10-05-ga-gate/contaminated-run.json +55 -0
  215. package/evals/results/2026-10-05-ga-gate/pilot.json +162 -0
  216. package/evals/results/2026-10-05-ga-gate/plan.json +457 -0
  217. package/evals/results/2026-10-05-ga-gate/summary.json +1037 -0
  218. package/evals/results/2026-10-05-ga-gate/summary.rescanned.json +1037 -0
  219. package/evals/results/README.md +31 -0
  220. package/evals/runs-pilot.log +150 -0
  221. package/evals/runs-pilot2.log +150 -0
  222. package/evals/score.mjs +256 -0
  223. package/evals/setup.mjs +547 -0
  224. package/evals/tasks/node-tool/faulty/thin-accepts-exponent/meta.json +5 -0
  225. package/evals/tasks/node-tool/faulty/thin-accepts-exponent/src/index.ts +121 -0
  226. package/evals/tasks/node-tool/faulty/thin-anonymous-transfer/meta.json +5 -0
  227. package/evals/tasks/node-tool/faulty/thin-anonymous-transfer/src/index.ts +116 -0
  228. package/evals/tasks/node-tool/faulty/thin-every-reject-may-have-executed/meta.json +9 -0
  229. package/evals/tasks/node-tool/faulty/thin-every-reject-may-have-executed/src/index.ts +118 -0
  230. package/evals/tasks/node-tool/faulty/thin-fetches-root-key-off-local/meta.json +5 -0
  231. package/evals/tasks/node-tool/faulty/thin-fetches-root-key-off-local/src/index.ts +118 -0
  232. package/evals/tasks/node-tool/faulty/thin-ignores-root-key/meta.json +5 -0
  233. package/evals/tasks/node-tool/faulty/thin-ignores-root-key/src/index.ts +117 -0
  234. package/evals/tasks/node-tool/faulty/thin-misclassifies-reject-codes/meta.json +9 -0
  235. package/evals/tasks/node-tool/faulty/thin-misclassifies-reject-codes/src/index.ts +119 -0
  236. package/evals/tasks/node-tool/faulty/thin-no-nat64-cap/meta.json +9 -0
  237. package/evals/tasks/node-tool/faulty/thin-no-nat64-cap/src/index.ts +118 -0
  238. package/evals/tasks/node-tool/faulty/thin-number-amount/meta.json +10 -0
  239. package/evals/tasks/node-tool/faulty/thin-number-amount/src/index.ts +117 -0
  240. package/evals/tasks/node-tool/faulty/thin-truncates-fraction-digits/meta.json +5 -0
  241. package/evals/tasks/node-tool/faulty/thin-truncates-fraction-digits/src/index.ts +120 -0
  242. package/evals/tasks/node-tool/faulty/thin-unvalidated-recipient/meta.json +5 -0
  243. package/evals/tasks/node-tool/faulty/thin-unvalidated-recipient/src/index.ts +118 -0
  244. package/evals/tasks/node-tool/faulty/v3-retry-call-errors/meta.json +11 -0
  245. package/evals/tasks/node-tool/faulty/v3-retry-call-errors/src/index.ts +149 -0
  246. package/evals/tasks/node-tool/faulty/v4-anonymous-identity-sent/meta.json +5 -0
  247. package/evals/tasks/node-tool/faulty/v4-anonymous-identity-sent/src/index.ts +71 -0
  248. package/evals/tasks/node-tool/faulty/v4-never-may-have-executed/meta.json +9 -0
  249. package/evals/tasks/node-tool/faulty/v4-never-may-have-executed/src/index.ts +84 -0
  250. package/evals/tasks/node-tool/faulty/v4-proto-never-may-have-executed/meta.json +9 -0
  251. package/evals/tasks/node-tool/faulty/v4-proto-never-may-have-executed/src/index.ts +72 -0
  252. package/evals/tasks/node-tool/faulty/v4-proto-refuses-nat64-max/meta.json +5 -0
  253. package/evals/tasks/node-tool/faulty/v4-proto-refuses-nat64-max/src/index.ts +72 -0
  254. package/evals/tasks/node-tool/faulty/v4-refuses-nat64-max/meta.json +5 -0
  255. package/evals/tasks/node-tool/faulty/v4-refuses-nat64-max/src/index.ts +84 -0
  256. package/evals/tasks/node-tool/hidden/node-tool.test.ts +312 -0
  257. package/evals/tasks/node-tool/prompt.md +55 -0
  258. package/evals/tasks/node-tool/prompt.minimal.md +30 -0
  259. package/evals/tasks/node-tool/solutions/thin/reference/src/index.ts +118 -0
  260. package/evals/tasks/node-tool/solutions/thin/reference-module-scope/src/index.ts +145 -0
  261. package/evals/tasks/node-tool/solutions/thin-guide/reference/src/index.ts +118 -0
  262. package/evals/tasks/node-tool/solutions/thin-guide/reference-module-scope/src/index.ts +145 -0
  263. package/evals/tasks/node-tool/solutions/v3/reference/src/index.ts +140 -0
  264. package/evals/tasks/node-tool/solutions/v3/reference-module-scope/src/index.ts +153 -0
  265. package/evals/tasks/node-tool/solutions/v4/reference/src/index.ts +84 -0
  266. package/evals/tasks/node-tool/solutions/v4/reference-module-scope/src/index.ts +105 -0
  267. package/evals/tasks/node-tool/solutions/v4-proto/reference/src/index.ts +72 -0
  268. package/evals/tasks/node-tool/solutions/v4-proto/reference-module-scope/src/index.ts +87 -0
  269. package/evals/tasks/node-tool/starter/src/contract.ts +31 -0
  270. package/evals/tasks/node-tool/starter/src/index.ts +7 -0
  271. package/evals/tasks/node-tool/starter/test/smoke.test.ts +22 -0
  272. package/evals/tasks/node-tool/starter/tsconfig.json +17 -0
  273. package/evals/tasks/node-tool/task.json +63 -0
  274. package/evals/tasks/react-wallet/faulty/thin-accepts-exponent/meta.json +5 -0
  275. package/evals/tasks/react-wallet/faulty/thin-accepts-exponent/src/Wallet.tsx +216 -0
  276. package/evals/tasks/react-wallet/faulty/thin-anonymous-transfer/meta.json +5 -0
  277. package/evals/tasks/react-wallet/faulty/thin-anonymous-transfer/src/Wallet.tsx +212 -0
  278. package/evals/tasks/react-wallet/faulty/thin-both-auth-buttons/meta.json +5 -0
  279. package/evals/tasks/react-wallet/faulty/thin-both-auth-buttons/src/Wallet.tsx +210 -0
  280. package/evals/tasks/react-wallet/faulty/thin-constant-balance-key/meta.json +8 -0
  281. package/evals/tasks/react-wallet/faulty/thin-constant-balance-key/src/Wallet.tsx +213 -0
  282. package/evals/tasks/react-wallet/faulty/thin-no-nat64-cap/meta.json +9 -0
  283. package/evals/tasks/react-wallet/faulty/thin-no-nat64-cap/src/Wallet.tsx +213 -0
  284. package/evals/tasks/react-wallet/faulty/thin-number-balance/meta.json +5 -0
  285. package/evals/tasks/react-wallet/faulty/thin-number-balance/src/Wallet.tsx +213 -0
  286. package/evals/tasks/react-wallet/faulty/thin-transfer-as-query/meta.json +5 -0
  287. package/evals/tasks/react-wallet/faulty/thin-transfer-as-query/src/Wallet.tsx +254 -0
  288. package/evals/tasks/react-wallet/faulty/thin-truncates-fraction-digits/meta.json +5 -0
  289. package/evals/tasks/react-wallet/faulty/thin-truncates-fraction-digits/src/Wallet.tsx +215 -0
  290. package/evals/tasks/react-wallet/faulty/thin-unguarded-balance-effect/meta.json +5 -0
  291. package/evals/tasks/react-wallet/faulty/thin-unguarded-balance-effect/src/Wallet.tsx +214 -0
  292. package/evals/tasks/react-wallet/faulty/v3-http-4xx-unknown/meta.json +5 -0
  293. package/evals/tasks/react-wallet/faulty/v3-http-4xx-unknown/src/Wallet.tsx +224 -0
  294. package/evals/tasks/react-wallet/faulty/v3-mutation-retry-3/meta.json +12 -0
  295. package/evals/tasks/react-wallet/faulty/v3-mutation-retry-3/src/Wallet.tsx +225 -0
  296. package/evals/tasks/react-wallet/faulty/v3-unknown-as-error/meta.json +11 -0
  297. package/evals/tasks/react-wallet/faulty/v3-unknown-as-error/src/Wallet.tsx +182 -0
  298. package/evals/tasks/react-wallet/faulty/v4-every-reject-unknown/meta.json +5 -0
  299. package/evals/tasks/react-wallet/faulty/v4-every-reject-unknown/src/Wallet.tsx +160 -0
  300. package/evals/tasks/react-wallet/faulty/v4-keep-previous-data/meta.json +5 -0
  301. package/evals/tasks/react-wallet/faulty/v4-keep-previous-data/src/Wallet.tsx +166 -0
  302. package/evals/tasks/react-wallet/faulty/v4-proto-every-reject-unknown/meta.json +5 -0
  303. package/evals/tasks/react-wallet/faulty/v4-proto-every-reject-unknown/src/Wallet.tsx +161 -0
  304. package/evals/tasks/react-wallet/faulty/v4-proto-keep-previous-data/meta.json +5 -0
  305. package/evals/tasks/react-wallet/faulty/v4-proto-keep-previous-data/src/Wallet.tsx +163 -0
  306. package/evals/tasks/react-wallet/faulty/v4-proto-retry-spread/meta.json +12 -0
  307. package/evals/tasks/react-wallet/faulty/v4-proto-retry-spread/src/Wallet.tsx +162 -0
  308. package/evals/tasks/react-wallet/faulty/v4-proto-status-not-idle/meta.json +5 -0
  309. package/evals/tasks/react-wallet/faulty/v4-proto-status-not-idle/src/Wallet.tsx +163 -0
  310. package/evals/tasks/react-wallet/faulty/v4-retry-spread/meta.json +12 -0
  311. package/evals/tasks/react-wallet/faulty/v4-retry-spread/src/Wallet.tsx +163 -0
  312. package/evals/tasks/react-wallet/faulty/v4-status-not-idle/meta.json +5 -0
  313. package/evals/tasks/react-wallet/faulty/v4-status-not-idle/src/Wallet.tsx +162 -0
  314. package/evals/tasks/react-wallet/hidden/react-wallet.test.ts +536 -0
  315. package/evals/tasks/react-wallet/prompt.md +88 -0
  316. package/evals/tasks/react-wallet/prompt.minimal.md +51 -0
  317. package/evals/tasks/react-wallet/solutions/thin/reference/src/Wallet.tsx +213 -0
  318. package/evals/tasks/react-wallet/solutions/thin/reference-module-scope/src/Wallet.tsx +227 -0
  319. package/evals/tasks/react-wallet/solutions/thin-guide/reference/src/Wallet.tsx +213 -0
  320. package/evals/tasks/react-wallet/solutions/thin-guide/reference-module-scope/src/Wallet.tsx +227 -0
  321. package/evals/tasks/react-wallet/solutions/v3/reference/src/Wallet.tsx +224 -0
  322. package/evals/tasks/react-wallet/solutions/v3/reference-module-scope/src/Wallet.tsx +230 -0
  323. package/evals/tasks/react-wallet/solutions/v4/reference/src/Wallet.tsx +160 -0
  324. package/evals/tasks/react-wallet/solutions/v4/reference-module-scope/src/Wallet.tsx +178 -0
  325. package/evals/tasks/react-wallet/solutions/v4-proto/reference/src/Wallet.tsx +161 -0
  326. package/evals/tasks/react-wallet/solutions/v4-proto/reference-module-scope/src/Wallet.tsx +161 -0
  327. package/evals/tasks/react-wallet/starter/src/Wallet.tsx +12 -0
  328. package/evals/tasks/react-wallet/starter/src/auth.ts +16 -0
  329. package/evals/tasks/react-wallet/starter/src/config.ts +14 -0
  330. package/evals/tasks/react-wallet/starter/src/ii-auth.ts +43 -0
  331. package/evals/tasks/react-wallet/starter/src/main.tsx +15 -0
  332. package/evals/tasks/react-wallet/starter/test/smoke.test.ts +34 -0
  333. package/evals/tasks/react-wallet/starter/tsconfig.json +18 -0
  334. package/evals/tasks/react-wallet/task.json +84 -0
  335. package/examples/canister-studio/.stackblitzrc +4 -0
  336. package/examples/canister-studio/README.md +183 -0
  337. package/examples/canister-studio/did/ckbtc_minter.did +806 -0
  338. package/examples/canister-studio/did/icp_ledger.did +586 -0
  339. package/examples/canister-studio/did/icrc1.did +88 -0
  340. package/examples/canister-studio/package.json +45 -0
  341. package/examples/canister-studio/scripts/smoke.ts +188 -0
  342. package/examples/canister-studio/src/calls/CacheKeyPanel.tsx +104 -0
  343. package/examples/canister-studio/src/calls/ConfirmWrite.tsx +265 -0
  344. package/examples/canister-studio/src/calls/ErrorPanel.test.tsx +113 -0
  345. package/examples/canister-studio/src/calls/ErrorPanel.tsx +214 -0
  346. package/examples/canister-studio/src/calls/MethodRunner.test.tsx +354 -0
  347. package/examples/canister-studio/src/calls/MethodRunner.tsx +57 -0
  348. package/examples/canister-studio/src/calls/ReadRunner.tsx +244 -0
  349. package/examples/canister-studio/src/calls/ResendPanel.tsx +165 -0
  350. package/examples/canister-studio/src/calls/ShareLink.tsx +59 -0
  351. package/examples/canister-studio/src/calls/WriteRunner.test.tsx +678 -0
  352. package/examples/canister-studio/src/calls/WriteRunner.tsx +670 -0
  353. package/examples/canister-studio/src/calls/dedupe.ts +189 -0
  354. package/examples/canister-studio/src/calls/error-info.ts +168 -0
  355. package/examples/canister-studio/src/calls/history.test.ts +284 -0
  356. package/examples/canister-studio/src/calls/history.ts +287 -0
  357. package/examples/canister-studio/src/calls/kinds.ts +68 -0
  358. package/examples/canister-studio/src/calls/prepare.ts +32 -0
  359. package/examples/canister-studio/src/calls/query-options.ts +30 -0
  360. package/examples/canister-studio/src/calls/read.ts +47 -0
  361. package/examples/canister-studio/src/calls/readback.ts +100 -0
  362. package/examples/canister-studio/src/calls/record.ts +123 -0
  363. package/examples/canister-studio/src/calls/safe-calls.test.ts +299 -0
  364. package/examples/canister-studio/src/calls/share.ts +46 -0
  365. package/examples/canister-studio/src/calls/testing/doubles.tsx +108 -0
  366. package/examples/canister-studio/src/calls/testing/fixtures.ts +185 -0
  367. package/examples/canister-studio/src/calls/useCanisterHandle.ts +35 -0
  368. package/examples/canister-studio/src/canisters/ckbtc_minter.envelope.json +4228 -0
  369. package/examples/canister-studio/src/canisters/ckbtc_minter.ts +868 -0
  370. package/examples/canister-studio/src/canisters/icp_ledger.envelope.json +3454 -0
  371. package/examples/canister-studio/src/canisters/icp_ledger.ts +584 -0
  372. package/examples/canister-studio/src/canisters/icrc1.envelope.json +840 -0
  373. package/examples/canister-studio/src/canisters/icrc1.ts +78 -0
  374. package/examples/canister-studio/src/components/ActionButton.tsx +32 -0
  375. package/examples/canister-studio/src/components/AppHeader.tsx +83 -0
  376. package/examples/canister-studio/src/components/AppShell.tsx +29 -0
  377. package/examples/canister-studio/src/components/AuthButton.tsx +79 -0
  378. package/examples/canister-studio/src/components/Badge.tsx +79 -0
  379. package/examples/canister-studio/src/components/CanisterHeader.tsx +200 -0
  380. package/examples/canister-studio/src/components/CanisterJump.tsx +130 -0
  381. package/examples/canister-studio/src/components/Card.tsx +64 -0
  382. package/examples/canister-studio/src/components/Code.tsx +101 -0
  383. package/examples/canister-studio/src/components/CopyButton.tsx +126 -0
  384. package/examples/canister-studio/src/components/Disclosure.tsx +34 -0
  385. package/examples/canister-studio/src/components/EmptyState.tsx +34 -0
  386. package/examples/canister-studio/src/components/MethodList.test.tsx +137 -0
  387. package/examples/canister-studio/src/components/MethodList.tsx +244 -0
  388. package/examples/canister-studio/src/components/Notice.tsx +36 -0
  389. package/examples/canister-studio/src/components/ProvenanceBadge.tsx +44 -0
  390. package/examples/canister-studio/src/components/RouteError.tsx +42 -0
  391. package/examples/canister-studio/src/components/SectionBoundary.tsx +76 -0
  392. package/examples/canister-studio/src/components/Spinner.tsx +33 -0
  393. package/examples/canister-studio/src/components/Tabs.tsx +96 -0
  394. package/examples/canister-studio/src/components/canister/CanisterLoadFailure.test.tsx +56 -0
  395. package/examples/canister-studio/src/components/canister/CanisterLoadFailure.tsx +165 -0
  396. package/examples/canister-studio/src/components/canister/CanisterNotFound.tsx +59 -0
  397. package/examples/canister-studio/src/components/canister/CanisterPending.tsx +33 -0
  398. package/examples/canister-studio/src/components/canister/MethodOverview.tsx +93 -0
  399. package/examples/canister-studio/src/components/canister/MethodWorkspace.test.tsx +201 -0
  400. package/examples/canister-studio/src/components/canister/MethodWorkspace.tsx +187 -0
  401. package/examples/canister-studio/src/components/canister/shared-call.test.ts +186 -0
  402. package/examples/canister-studio/src/components/canister/shared-call.ts +133 -0
  403. package/examples/canister-studio/src/components/canister/shared-link-loader.test.ts +116 -0
  404. package/examples/canister-studio/src/components/canister/use-canister-page.ts +44 -0
  405. package/examples/canister-studio/src/components/canister/workspace-key.test.ts +27 -0
  406. package/examples/canister-studio/src/components/canister/workspace-key.ts +36 -0
  407. package/examples/canister-studio/src/components/canister-id-input.ts +69 -0
  408. package/examples/canister-studio/src/components/canister-names.ts +11 -0
  409. package/examples/canister-studio/src/components/download.ts +34 -0
  410. package/examples/canister-studio/src/components/fallbacks.tsx +33 -0
  411. package/examples/canister-studio/src/components/helpers.test.ts +107 -0
  412. package/examples/canister-studio/src/components/history/HistoryList.tsx +284 -0
  413. package/examples/canister-studio/src/components/home/FeaturedLedger.tsx +149 -0
  414. package/examples/canister-studio/src/components/home/OpenCanister.tsx +99 -0
  415. package/examples/canister-studio/src/components/home/featured.test.tsx +92 -0
  416. package/examples/canister-studio/src/components/home/featured.ts +64 -0
  417. package/examples/canister-studio/src/components/interface/CandidTab.tsx +38 -0
  418. package/examples/canister-studio/src/components/interface/CompatReport.tsx +92 -0
  419. package/examples/canister-studio/src/components/interface/CompatibilityTab.tsx +190 -0
  420. package/examples/canister-studio/src/components/interface/ProjectTab.tsx +196 -0
  421. package/examples/canister-studio/src/components/interface/TypeScriptTab.tsx +114 -0
  422. package/examples/canister-studio/src/components/interface/tools.test.tsx +169 -0
  423. package/examples/canister-studio/src/components/modes.ts +31 -0
  424. package/examples/canister-studio/src/components/playground/MethodPicker.tsx +67 -0
  425. package/examples/canister-studio/src/components/playground/PlaygroundForms.tsx +112 -0
  426. package/examples/canister-studio/src/components/playground/PlaygroundTools.tsx +219 -0
  427. package/examples/canister-studio/src/components/playground/sample.ts +43 -0
  428. package/examples/canister-studio/src/components/playground/target.test.tsx +86 -0
  429. package/examples/canister-studio/src/components/primitives.test.tsx +121 -0
  430. package/examples/canister-studio/src/components/provenance.ts +38 -0
  431. package/examples/canister-studio/src/components/route-render.test.tsx +71 -0
  432. package/examples/canister-studio/src/components/use-task.ts +56 -0
  433. package/examples/canister-studio/src/components/wallet/BalancePanel.tsx +110 -0
  434. package/examples/canister-studio/src/components/wallet/LedgerSummary.tsx +63 -0
  435. package/examples/canister-studio/src/components/wallet/MinterPanel.tsx +255 -0
  436. package/examples/canister-studio/src/components/wallet/SendPanel.test.tsx +297 -0
  437. package/examples/canister-studio/src/components/wallet/SendPanel.tsx +446 -0
  438. package/examples/canister-studio/src/components/wallet/ledgers.test.ts +36 -0
  439. package/examples/canister-studio/src/components/wallet/ledgers.ts +111 -0
  440. package/examples/canister-studio/src/forms/JsonPanel.tsx +157 -0
  441. package/examples/canister-studio/src/forms/MethodForm.test.tsx +369 -0
  442. package/examples/canister-studio/src/forms/MethodForm.tsx +368 -0
  443. package/examples/canister-studio/src/forms/TypedForm.tsx +55 -0
  444. package/examples/canister-studio/src/forms/context.ts +62 -0
  445. package/examples/canister-studio/src/forms/convert.ts +131 -0
  446. package/examples/canister-studio/src/forms/edge.test.ts +252 -0
  447. package/examples/canister-studio/src/forms/engine.test.ts +358 -0
  448. package/examples/canister-studio/src/forms/engine.ts +1492 -0
  449. package/examples/canister-studio/src/forms/fields/BytesField.tsx +210 -0
  450. package/examples/canister-studio/src/forms/fields/Field.tsx +106 -0
  451. package/examples/canister-studio/src/forms/fields/FieldShell.tsx +101 -0
  452. package/examples/canister-studio/src/forms/fields/Group.tsx +74 -0
  453. package/examples/canister-studio/src/forms/fields/ListField.tsx +256 -0
  454. package/examples/canister-studio/src/forms/fields/Segmented.tsx +78 -0
  455. package/examples/canister-studio/src/forms/fields/TimestampField.tsx +108 -0
  456. package/examples/canister-studio/src/forms/fields/UnitsField.tsx +119 -0
  457. package/examples/canister-studio/src/forms/fields/composites.tsx +389 -0
  458. package/examples/canister-studio/src/forms/fields/leaves.tsx +350 -0
  459. package/examples/canister-studio/src/forms/fields.test.tsx +396 -0
  460. package/examples/canister-studio/src/forms/helpers.test.ts +225 -0
  461. package/examples/canister-studio/src/forms/hints.ts +128 -0
  462. package/examples/canister-studio/src/forms/json.ts +119 -0
  463. package/examples/canister-studio/src/forms/parsing.test.ts +217 -0
  464. package/examples/canister-studio/src/forms/useMethodForm.ts +251 -0
  465. package/examples/canister-studio/src/interface/browser.test.ts +23 -0
  466. package/examples/canister-studio/src/interface/browser.ts +153 -0
  467. package/examples/canister-studio/src/interface/cache.ts +68 -0
  468. package/examples/canister-studio/src/interface/cli-cache.test.ts +155 -0
  469. package/examples/canister-studio/src/interface/cli-cache.ts +125 -0
  470. package/examples/canister-studio/src/interface/compile.server.test.ts +198 -0
  471. package/examples/canister-studio/src/interface/compile.server.ts +262 -0
  472. package/examples/canister-studio/src/interface/compile.test.ts +108 -0
  473. package/examples/canister-studio/src/interface/compile.ts +305 -0
  474. package/examples/canister-studio/src/interface/docs.test.ts +75 -0
  475. package/examples/canister-studio/src/interface/docs.ts +100 -0
  476. package/examples/canister-studio/src/interface/functions.test.ts +42 -0
  477. package/examples/canister-studio/src/interface/functions.ts +200 -0
  478. package/examples/canister-studio/src/interface/live-candid.server.ts +91 -0
  479. package/examples/canister-studio/src/interface/live-loader.test.ts +515 -0
  480. package/examples/canister-studio/src/interface/live-loader.ts +364 -0
  481. package/examples/canister-studio/src/interface/load.test.ts +328 -0
  482. package/examples/canister-studio/src/interface/load.ts +249 -0
  483. package/examples/canister-studio/src/interface/read-candid.test.ts +185 -0
  484. package/examples/canister-studio/src/interface/read-candid.ts +233 -0
  485. package/examples/canister-studio/src/interface/server-result.ts +42 -0
  486. package/examples/canister-studio/src/interface/typed.ts +68 -0
  487. package/examples/canister-studio/src/interface/types.ts +121 -0
  488. package/examples/canister-studio/src/lib/format.test.ts +107 -0
  489. package/examples/canister-studio/src/lib/format.ts +139 -0
  490. package/examples/canister-studio/src/lib/hex.test.ts +43 -0
  491. package/examples/canister-studio/src/lib/hex.ts +33 -0
  492. package/examples/canister-studio/src/lib/ids.test.ts +70 -0
  493. package/examples/canister-studio/src/lib/ids.ts +93 -0
  494. package/examples/canister-studio/src/lib/reactor.test.ts +55 -0
  495. package/examples/canister-studio/src/lib/reactor.ts +55 -0
  496. package/examples/canister-studio/src/lib/registry.test.ts +74 -0
  497. package/examples/canister-studio/src/lib/registry.ts +238 -0
  498. package/examples/canister-studio/src/lib/schema.ts +148 -0
  499. package/examples/canister-studio/src/routeTree.gen.ts +227 -0
  500. package/examples/canister-studio/src/router.test.ts +290 -0
  501. package/examples/canister-studio/src/router.tsx +127 -0
  502. package/examples/canister-studio/src/routes/__root.tsx +58 -0
  503. package/examples/canister-studio/src/routes/canister.$canisterId.index.tsx +172 -0
  504. package/examples/canister-studio/src/routes/canister.$canisterId.interface.tsx +98 -0
  505. package/examples/canister-studio/src/routes/canister.$canisterId.tsx +91 -0
  506. package/examples/canister-studio/src/routes/history.tsx +55 -0
  507. package/examples/canister-studio/src/routes/index.tsx +169 -0
  508. package/examples/canister-studio/src/routes/playground.tsx +286 -0
  509. package/examples/canister-studio/src/routes/safety.tsx +57 -0
  510. package/examples/canister-studio/src/routes/wallet.tsx +213 -0
  511. package/examples/canister-studio/src/safety/LabTransferForm.tsx +84 -0
  512. package/examples/canister-studio/src/safety/RequestLog.tsx +79 -0
  513. package/examples/canister-studio/src/safety/SafetyLab.test.tsx +89 -0
  514. package/examples/canister-studio/src/safety/SafetyLab.tsx +140 -0
  515. package/examples/canister-studio/src/safety/ScenarioCard.tsx +262 -0
  516. package/examples/canister-studio/src/safety/lab-run.ts +283 -0
  517. package/examples/canister-studio/src/safety/mock-ledger.test.ts +99 -0
  518. package/examples/canister-studio/src/safety/mock-ledger.ts +263 -0
  519. package/examples/canister-studio/src/safety/scenarios.test.ts +157 -0
  520. package/examples/canister-studio/src/safety/scenarios.ts +924 -0
  521. package/examples/canister-studio/src/styles.css +3453 -0
  522. package/examples/canister-studio/src/styles.test.ts +46 -0
  523. package/examples/canister-studio/src/values/CallPreview.test.tsx +86 -0
  524. package/examples/canister-studio/src/values/CallPreview.tsx +148 -0
  525. package/examples/canister-studio/src/values/ErrValueView.tsx +66 -0
  526. package/examples/canister-studio/src/values/Inspector.tsx +133 -0
  527. package/examples/canister-studio/src/values/ReplyView.test.tsx +341 -0
  528. package/examples/canister-studio/src/values/ReplyView.tsx +164 -0
  529. package/examples/canister-studio/src/values/candid-text.test.ts +516 -0
  530. package/examples/canister-studio/src/values/candid-text.ts +479 -0
  531. package/examples/canister-studio/src/values/hints.ts +58 -0
  532. package/examples/canister-studio/src/values/json.test.ts +66 -0
  533. package/examples/canister-studio/src/values/json.ts +31 -0
  534. package/examples/canister-studio/src/values/leaves.tsx +309 -0
  535. package/examples/canister-studio/src/values/schema.test.ts +123 -0
  536. package/examples/canister-studio/src/values/schema.ts +78 -0
  537. package/examples/canister-studio/src/values/test-router.tsx +21 -0
  538. package/examples/canister-studio/src/values/tree.tsx +670 -0
  539. package/examples/canister-studio/src/values/use-sha256.ts +29 -0
  540. package/examples/canister-studio/tsconfig.json +20 -0
  541. package/examples/canister-studio/vite.config.ts +50 -0
  542. package/examples/canister-studio/vitest.config.ts +20 -0
  543. package/examples/icrc-ledger/.stackblitzrc +4 -0
  544. package/examples/icrc-ledger/README.md +100 -0
  545. package/examples/icrc-ledger/ckbtc_minter.did +12 -0
  546. package/examples/icrc-ledger/icp_ledger.did +147 -0
  547. package/examples/icrc-ledger/icrc1.did +88 -0
  548. package/examples/icrc-ledger/index.html +13 -0
  549. package/examples/icrc-ledger/package.json +40 -0
  550. package/examples/icrc-ledger/scripts/smoke.ts +151 -0
  551. package/examples/icrc-ledger/src/App.tsx +71 -0
  552. package/examples/icrc-ledger/src/ErrorPanel.tsx +105 -0
  553. package/examples/icrc-ledger/src/MainnetTab.tsx +450 -0
  554. package/examples/icrc-ledger/src/SandboxTab.test.tsx +379 -0
  555. package/examples/icrc-ledger/src/SandboxTab.tsx +761 -0
  556. package/examples/icrc-ledger/src/blocks.test.ts +247 -0
  557. package/examples/icrc-ledger/src/blocks.ts +140 -0
  558. package/examples/icrc-ledger/src/canisters/ckbtc_minter.envelope.json +96 -0
  559. package/examples/icrc-ledger/src/canisters/ckbtc_minter.ts +16 -0
  560. package/examples/icrc-ledger/src/canisters/icp_ledger.envelope.json +690 -0
  561. package/examples/icrc-ledger/src/canisters/icp_ledger.ts +223 -0
  562. package/examples/icrc-ledger/src/canisters/icrc1.envelope.json +840 -0
  563. package/examples/icrc-ledger/src/canisters/icrc1.ts +78 -0
  564. package/examples/icrc-ledger/src/format.test.ts +41 -0
  565. package/examples/icrc-ledger/src/format.ts +68 -0
  566. package/examples/icrc-ledger/src/ledger.ts +15 -0
  567. package/examples/icrc-ledger/src/main.tsx +10 -0
  568. package/examples/icrc-ledger/src/minter.ts +16 -0
  569. package/examples/icrc-ledger/src/sandbox.test.ts +464 -0
  570. package/examples/icrc-ledger/src/sandbox.ts +281 -0
  571. package/examples/icrc-ledger/src/styles.css +391 -0
  572. package/examples/icrc-ledger/src/transfer-form.test.ts +62 -0
  573. package/examples/icrc-ledger/src/transfer-form.ts +97 -0
  574. package/examples/icrc-ledger/tsconfig.json +19 -0
  575. package/examples/icrc-ledger/vite.config.ts +44 -0
  576. package/examples/icrc-ledger/vitest.config.ts +12 -0
  577. package/examples/next-ssr/.stackblitzrc +4 -0
  578. package/examples/next-ssr/README.md +112 -0
  579. package/examples/next-ssr/icrc1.did +88 -0
  580. package/examples/next-ssr/next.config.ts +10 -0
  581. package/examples/next-ssr/package.json +41 -0
  582. package/examples/next-ssr/scripts/smoke.ts +169 -0
  583. package/examples/next-ssr/src/app/account/CertifiedBalances.tsx +58 -0
  584. package/examples/next-ssr/src/app/account/account.test.tsx +165 -0
  585. package/examples/next-ssr/src/app/account/page.tsx +82 -0
  586. package/examples/next-ssr/src/app/api/balance/[ledger]/[principal]/route.ts +10 -0
  587. package/examples/next-ssr/src/app/globals.css +278 -0
  588. package/examples/next-ssr/src/app/layout.tsx +34 -0
  589. package/examples/next-ssr/src/app/page.tsx +46 -0
  590. package/examples/next-ssr/src/app/providers.browser.test.tsx +127 -0
  591. package/examples/next-ssr/src/app/providers.test.tsx +73 -0
  592. package/examples/next-ssr/src/app/providers.tsx +60 -0
  593. package/examples/next-ssr/src/canisters/icrc1.envelope.json +840 -0
  594. package/examples/next-ssr/src/canisters/icrc1.ts +78 -0
  595. package/examples/next-ssr/src/components/BalanceTable.tsx +43 -0
  596. package/examples/next-ssr/src/components/LedgerCard.test.tsx +96 -0
  597. package/examples/next-ssr/src/components/LedgerCard.tsx +230 -0
  598. package/examples/next-ssr/src/components/LedgerError.tsx +32 -0
  599. package/examples/next-ssr/src/components/LedgerSections.tsx +22 -0
  600. package/examples/next-ssr/src/components/MyBalances.test.tsx +253 -0
  601. package/examples/next-ssr/src/components/MyBalances.tsx +129 -0
  602. package/examples/next-ssr/src/components/SessionBadge.tsx +18 -0
  603. package/examples/next-ssr/src/components/hydration.test.tsx +259 -0
  604. package/examples/next-ssr/src/components/server-data.ts +10 -0
  605. package/examples/next-ssr/src/format.ts +46 -0
  606. package/examples/next-ssr/src/ledgers.ts +38 -0
  607. package/examples/next-ssr/src/server/balance-route.test.ts +151 -0
  608. package/examples/next-ssr/src/server/balance-route.ts +157 -0
  609. package/examples/next-ssr/src/server/error-summary.test.ts +34 -0
  610. package/examples/next-ssr/src/server/error-summary.ts +37 -0
  611. package/examples/next-ssr/src/server/parse-owner.test.ts +39 -0
  612. package/examples/next-ssr/src/server/parse-owner.ts +34 -0
  613. package/examples/next-ssr/src/server/prefetch-ledgers.test.ts +137 -0
  614. package/examples/next-ssr/src/server/prefetch-ledgers.ts +70 -0
  615. package/examples/next-ssr/src/server/read-balances.test.ts +103 -0
  616. package/examples/next-ssr/src/server/read-balances.ts +78 -0
  617. package/examples/next-ssr/src/server/request-client.rsc.test.tsx +110 -0
  618. package/examples/next-ssr/src/server/request-client.ts +24 -0
  619. package/examples/next-ssr/src/testing/mock-ledgers.ts +115 -0
  620. package/examples/next-ssr/src/testing/react-server-dom-turbopack.d.ts +15 -0
  621. package/examples/next-ssr/src/testing/setup.ts +10 -0
  622. package/examples/next-ssr/tsconfig.json +31 -0
  623. package/examples/next-ssr/vitest.config.ts +59 -0
  624. package/examples/node-agent-tool/.stackblitzrc +4 -0
  625. package/examples/node-agent-tool/README.md +165 -0
  626. package/examples/node-agent-tool/icp.yaml +11 -0
  627. package/examples/node-agent-tool/icrc1.did +88 -0
  628. package/examples/node-agent-tool/package.json +32 -0
  629. package/examples/node-agent-tool/src/canisters/icrc1.envelope.json +840 -0
  630. package/examples/node-agent-tool/src/canisters/icrc1.ts +78 -0
  631. package/examples/node-agent-tool/src/cli.test.ts +64 -0
  632. package/examples/node-agent-tool/src/cli.ts +23 -0
  633. package/examples/node-agent-tool/src/command-line.ts +159 -0
  634. package/examples/node-agent-tool/src/commands/balance.ts +73 -0
  635. package/examples/node-agent-tool/src/commands/demo.test.ts +51 -0
  636. package/examples/node-agent-tool/src/commands/demo.ts +359 -0
  637. package/examples/node-agent-tool/src/commands/info.ts +69 -0
  638. package/examples/node-agent-tool/src/commands/reads.test.ts +133 -0
  639. package/examples/node-agent-tool/src/commands/resend.test.ts +363 -0
  640. package/examples/node-agent-tool/src/commands/transfer.test.ts +313 -0
  641. package/examples/node-agent-tool/src/commands/transfer.ts +415 -0
  642. package/examples/node-agent-tool/src/commands/watch.test.ts +159 -0
  643. package/examples/node-agent-tool/src/commands/watch.ts +135 -0
  644. package/examples/node-agent-tool/src/commands/whoami.ts +36 -0
  645. package/examples/node-agent-tool/src/context.ts +20 -0
  646. package/examples/node-agent-tool/src/dispatch.ts +70 -0
  647. package/examples/node-agent-tool/src/failure.test.ts +59 -0
  648. package/examples/node-agent-tool/src/failure.ts +176 -0
  649. package/examples/node-agent-tool/src/identity.test.ts +242 -0
  650. package/examples/node-agent-tool/src/identity.ts +228 -0
  651. package/examples/node-agent-tool/src/input.ts +110 -0
  652. package/examples/node-agent-tool/src/ledgers.test.ts +74 -0
  653. package/examples/node-agent-tool/src/ledgers.ts +62 -0
  654. package/examples/node-agent-tool/src/mock-ledger.ts +211 -0
  655. package/examples/node-agent-tool/src/network.test.ts +128 -0
  656. package/examples/node-agent-tool/src/network.ts +101 -0
  657. package/examples/node-agent-tool/src/output.test.ts +208 -0
  658. package/examples/node-agent-tool/src/output.ts +163 -0
  659. package/examples/node-agent-tool/src/run.ts +73 -0
  660. package/examples/node-agent-tool/src/test-kit.ts +167 -0
  661. package/examples/node-agent-tool/tsconfig.json +18 -0
  662. package/examples/node-agent-tool/vitest.config.ts +8 -0
  663. package/examples/vite-wallet/.stackblitzrc +4 -0
  664. package/examples/vite-wallet/Cargo.lock +646 -0
  665. package/examples/vite-wallet/Cargo.toml +5 -0
  666. package/examples/vite-wallet/README.md +107 -0
  667. package/examples/vite-wallet/backend/Cargo.toml +12 -0
  668. package/examples/vite-wallet/backend/backend.did +37 -0
  669. package/examples/vite-wallet/backend/src/lib.rs +133 -0
  670. package/examples/vite-wallet/icp.yaml +44 -0
  671. package/examples/vite-wallet/index.html +13 -0
  672. package/examples/vite-wallet/ledger.did +54 -0
  673. package/examples/vite-wallet/package.json +46 -0
  674. package/examples/vite-wallet/public/.ic-assets.json5 +26 -0
  675. package/examples/vite-wallet/scripts/faucet-args.test.ts +30 -0
  676. package/examples/vite-wallet/scripts/faucet-args.ts +41 -0
  677. package/examples/vite-wallet/scripts/faucet.ts +41 -0
  678. package/examples/vite-wallet/src/AddressBook.test.tsx +191 -0
  679. package/examples/vite-wallet/src/AddressBook.tsx +147 -0
  680. package/examples/vite-wallet/src/App.tsx +53 -0
  681. package/examples/vite-wallet/src/Balance.test.tsx +57 -0
  682. package/examples/vite-wallet/src/Balance.tsx +58 -0
  683. package/examples/vite-wallet/src/EnvironmentPanel.test.tsx +83 -0
  684. package/examples/vite-wallet/src/EnvironmentPanel.tsx +103 -0
  685. package/examples/vite-wallet/src/ErrorNote.tsx +70 -0
  686. package/examples/vite-wallet/src/Profile.test.tsx +145 -0
  687. package/examples/vite-wallet/src/Profile.tsx +92 -0
  688. package/examples/vite-wallet/src/SendIcp.test.tsx +231 -0
  689. package/examples/vite-wallet/src/SendIcp.tsx +244 -0
  690. package/examples/vite-wallet/src/SendToContact.test.tsx +113 -0
  691. package/examples/vite-wallet/src/SendToContact.tsx +102 -0
  692. package/examples/vite-wallet/src/SignIn.test.tsx +106 -0
  693. package/examples/vite-wallet/src/SignIn.tsx +120 -0
  694. package/examples/vite-wallet/src/Wallet.test.tsx +89 -0
  695. package/examples/vite-wallet/src/account-switch.test.tsx +108 -0
  696. package/examples/vite-wallet/src/auth/dev-accounts.test.ts +113 -0
  697. package/examples/vite-wallet/src/auth/dev-accounts.ts +216 -0
  698. package/examples/vite-wallet/src/auth/local-page.test.ts +28 -0
  699. package/examples/vite-wallet/src/auth/local-page.ts +20 -0
  700. package/examples/vite-wallet/src/auth/wallet-auth.test.ts +159 -0
  701. package/examples/vite-wallet/src/auth/wallet-auth.ts +131 -0
  702. package/examples/vite-wallet/src/canisters/backend.envelope.json +275 -0
  703. package/examples/vite-wallet/src/canisters/backend.ts +58 -0
  704. package/examples/vite-wallet/src/canisters/ledger.envelope.json +451 -0
  705. package/examples/vite-wallet/src/canisters/ledger.ts +56 -0
  706. package/examples/vite-wallet/src/client.ts +45 -0
  707. package/examples/vite-wallet/src/main.tsx +18 -0
  708. package/examples/vite-wallet/src/optimistic-contacts.ts +113 -0
  709. package/examples/vite-wallet/src/refusal.ts +17 -0
  710. package/examples/vite-wallet/src/styles.css +282 -0
  711. package/examples/vite-wallet/src/test/test-wallet.tsx +322 -0
  712. package/examples/vite-wallet/src/token.ts +50 -0
  713. package/examples/vite-wallet/src/transfer.test.ts +178 -0
  714. package/examples/vite-wallet/src/transfer.ts +110 -0
  715. package/examples/vite-wallet/src/use-canisters.ts +49 -0
  716. package/examples/vite-wallet/tsconfig.json +19 -0
  717. package/examples/vite-wallet/vite.config.ts +47 -0
  718. package/examples/vite-wallet/vitest.config.ts +17 -0
  719. package/package.json +9 -4
  720. package/packages/core/.size-limit.js +47 -0
  721. package/packages/core/LICENSE +21 -0
  722. package/packages/core/README.md +84 -0
  723. package/packages/core/llms.txt +610 -0
  724. package/packages/core/package.json +89 -0
  725. package/packages/core/src/call.ts +961 -0
  726. package/packages/core/src/canister.ts +259 -0
  727. package/packages/core/src/client.ts +2061 -0
  728. package/packages/core/src/errors.ts +1079 -0
  729. package/packages/core/src/hydration.ts +239 -0
  730. package/packages/core/src/index.ts +23 -0
  731. package/packages/core/src/keys.ts +108 -0
  732. package/packages/core/src/management.ts +138 -0
  733. package/packages/core/src/network.ts +1020 -0
  734. package/packages/core/src/options.ts +1000 -0
  735. package/packages/core/src/runtime.ts +50 -0
  736. package/packages/core/src/testing/fake-replica.ts +1166 -0
  737. package/packages/core/src/testing/index.ts +16 -0
  738. package/packages/core/src/testing/test-auth.ts +340 -0
  739. package/packages/core/src/testing/test-client.ts +559 -0
  740. package/packages/core/src/types.ts +541 -0
  741. package/packages/core/src/units.ts +299 -0
  742. package/packages/core/src/version.ts +4 -0
  743. package/packages/core/tests/canister-helpers.ts +187 -0
  744. package/packages/core/tests/canister-id.test.ts +158 -0
  745. package/packages/core/tests/canister-unwrap.test.ts +94 -0
  746. package/packages/core/tests/canister.test-d.ts +406 -0
  747. package/packages/core/tests/canister.test.ts +1921 -0
  748. package/packages/core/tests/client-account-change.test.ts +131 -0
  749. package/packages/core/tests/client-agents.test.ts +402 -0
  750. package/packages/core/tests/client-auth-network.test.ts +719 -0
  751. package/packages/core/tests/client-disposed.test.ts +403 -0
  752. package/packages/core/tests/client-helpers.ts +136 -0
  753. package/packages/core/tests/client-hydration.test.ts +166 -0
  754. package/packages/core/tests/client-pinned.test.ts +626 -0
  755. package/packages/core/tests/client-session.test.ts +348 -0
  756. package/packages/core/tests/client.test-d.ts +181 -0
  757. package/packages/core/tests/client.test.ts +1162 -0
  758. package/packages/core/tests/encode.test.ts +171 -0
  759. package/packages/core/tests/env-canister-id.test.ts +666 -0
  760. package/packages/core/tests/env-cookie-copies.test.ts +182 -0
  761. package/packages/core/tests/error-guard.test.ts +222 -0
  762. package/packages/core/tests/errors.test-d.ts +280 -0
  763. package/packages/core/tests/errors.test.ts +1359 -0
  764. package/packages/core/tests/fixtures/README.md +28 -0
  765. package/packages/core/tests/fixtures/accounts.did +21 -0
  766. package/packages/core/tests/fixtures/accounts.envelope.json +367 -0
  767. package/packages/core/tests/fixtures/accounts.ts +26 -0
  768. package/packages/core/tests/fixtures/archive.did +31 -0
  769. package/packages/core/tests/fixtures/archive.envelope.json +259 -0
  770. package/packages/core/tests/fixtures/archive.ts +55 -0
  771. package/packages/core/tests/fixtures/icrc1.did +88 -0
  772. package/packages/core/tests/fixtures/icrc1.envelope.json +840 -0
  773. package/packages/core/tests/fixtures/icrc1.ts +78 -0
  774. package/packages/core/tests/fixtures/management.did +724 -0
  775. package/packages/core/tests/fixtures/management.envelope.json +4455 -0
  776. package/packages/core/tests/fixtures/management.ts +486 -0
  777. package/packages/core/tests/fixtures/shapes.did +33 -0
  778. package/packages/core/tests/fixtures/shapes.envelope.json +440 -0
  779. package/packages/core/tests/fixtures/shapes.ts +56 -0
  780. package/packages/core/tests/fixtures/skippable.did +15 -0
  781. package/packages/core/tests/fixtures/skippable.envelope.json +182 -0
  782. package/packages/core/tests/fixtures/skippable.ts +33 -0
  783. package/packages/core/tests/func.test.ts +154 -0
  784. package/packages/core/tests/identity-attributes.test.ts +214 -0
  785. package/packages/core/tests/keys.test.ts +289 -0
  786. package/packages/core/tests/management.test.ts +206 -0
  787. package/packages/core/tests/network-helpers.ts +121 -0
  788. package/packages/core/tests/network.test-d.ts +78 -0
  789. package/packages/core/tests/network.test.ts +1051 -0
  790. package/packages/core/tests/options.test.ts +1594 -0
  791. package/packages/core/tests/redirect-sign-in.test.ts +393 -0
  792. package/packages/core/tests/resend.test-d.ts +81 -0
  793. package/packages/core/tests/resend.test.ts +723 -0
  794. package/packages/core/tests/skip.test-d.ts +125 -0
  795. package/packages/core/tests/testing-entry.test.ts +73 -0
  796. package/packages/core/tests/testing-fake-replica.test.ts +1242 -0
  797. package/packages/core/tests/testing-test-auth.test-d.ts +84 -0
  798. package/packages/core/tests/testing-test-auth.test.ts +483 -0
  799. package/packages/core/tests/testing-test-client.test-d.ts +184 -0
  800. package/packages/core/tests/testing-test-client.test.ts +1272 -0
  801. package/packages/core/tests/time-realm.test.ts +63 -0
  802. package/packages/core/tests/time.test.ts +238 -0
  803. package/packages/core/tests/traps/README.md +39 -0
  804. package/packages/core/tests/traps/anonymous-write.test.ts +145 -0
  805. package/packages/core/tests/traps/classification-table.test.ts +256 -0
  806. package/packages/core/tests/traps/err-arm.test.ts +144 -0
  807. package/packages/core/tests/traps/fetch-spy.ts +39 -0
  808. package/packages/core/tests/traps/invalidation-after-write.test.ts +178 -0
  809. package/packages/core/tests/traps/ledger.ts +248 -0
  810. package/packages/core/tests/traps/root-key-off-local.test.ts +130 -0
  811. package/packages/core/tests/traps/stale-data-across-users.test.ts +173 -0
  812. package/packages/core/tests/traps/unresolved-name.test.ts +155 -0
  813. package/packages/core/tests/traps/update-as-query.test.ts +244 -0
  814. package/packages/core/tests/traps/update-not-resent.test.ts +126 -0
  815. package/packages/core/tests/traps/update-resent-when-never-delivered.test.ts +159 -0
  816. package/packages/core/tests/traps.test-d.ts +476 -0
  817. package/packages/core/tests/units.test-d.ts +118 -0
  818. package/packages/core/tests/units.test.ts +634 -0
  819. package/packages/core/tsconfig.json +19 -0
  820. package/packages/core/tsconfig.typecheck.json +16 -0
  821. package/packages/core/vitest.config.ts +13 -0
  822. package/packages/mcp/LICENSE +21 -0
  823. package/packages/mcp/README.md +189 -0
  824. package/packages/mcp/package.json +62 -0
  825. package/packages/mcp/src/candid/candid-text.test.ts +455 -0
  826. package/packages/mcp/src/candid/candid-text.ts +501 -0
  827. package/packages/mcp/src/candid/contract.test.ts +398 -0
  828. package/packages/mcp/src/candid/formats.ts +155 -0
  829. package/packages/mcp/src/candid/hex.ts +29 -0
  830. package/packages/mcp/src/candid/json-schema.test.ts +758 -0
  831. package/packages/mcp/src/candid/json-schema.ts +454 -0
  832. package/packages/mcp/src/candid/json.test.ts +1207 -0
  833. package/packages/mcp/src/candid/json.ts +1053 -0
  834. package/packages/mcp/src/candid/schema-walk.ts +189 -0
  835. package/packages/mcp/src/candid/type-text.test.ts +179 -0
  836. package/packages/mcp/src/candid/type-text.ts +238 -0
  837. package/packages/mcp/src/cli.ts +134 -0
  838. package/packages/mcp/src/identity.ts +188 -0
  839. package/packages/mcp/src/interface/cache.test.ts +48 -0
  840. package/packages/mcp/src/interface/cache.ts +68 -0
  841. package/packages/mcp/src/interface/cli-cache.test.ts +150 -0
  842. package/packages/mcp/src/interface/cli-cache.ts +122 -0
  843. package/packages/mcp/src/interface/compile-worker.test.ts +246 -0
  844. package/packages/mcp/src/interface/compile-worker.ts +290 -0
  845. package/packages/mcp/src/interface/compile.test.ts +155 -0
  846. package/packages/mcp/src/interface/compile.ts +281 -0
  847. package/packages/mcp/src/interface/docs.test.ts +105 -0
  848. package/packages/mcp/src/interface/docs.ts +86 -0
  849. package/packages/mcp/src/interface/fixtures/icrc1.did +88 -0
  850. package/packages/mcp/src/interface/hex.ts +8 -0
  851. package/packages/mcp/src/interface/ids.test.ts +71 -0
  852. package/packages/mcp/src/interface/ids.ts +87 -0
  853. package/packages/mcp/src/interface/loader.test.ts +804 -0
  854. package/packages/mcp/src/interface/loader.ts +519 -0
  855. package/packages/mcp/src/interface/read-candid.test.ts +300 -0
  856. package/packages/mcp/src/interface/read-candid.ts +259 -0
  857. package/packages/mcp/src/interface/types.ts +113 -0
  858. package/packages/mcp/src/network.ts +122 -0
  859. package/packages/mcp/src/options.ts +149 -0
  860. package/packages/mcp/src/server/approval.ts +159 -0
  861. package/packages/mcp/src/server/naming.ts +43 -0
  862. package/packages/mcp/src/server/results.ts +102 -0
  863. package/packages/mcp/src/server/server.ts +797 -0
  864. package/packages/mcp/src/server/tools.ts +116 -0
  865. package/packages/mcp/src/usage.ts +40 -0
  866. package/packages/mcp/tests/approval.test.ts +60 -0
  867. package/packages/mcp/tests/identity.test.ts +234 -0
  868. package/packages/mcp/tests/naming.test.ts +62 -0
  869. package/packages/mcp/tests/options.test.ts +140 -0
  870. package/packages/mcp/tests/server.test.ts +997 -0
  871. package/packages/mcp/tsconfig.json +19 -0
  872. package/packages/mcp/tsconfig.typecheck.json +10 -0
  873. package/packages/mcp/vitest.config.ts +10 -0
  874. package/packages/react/LICENSE +21 -0
  875. package/packages/react/README.md +328 -0
  876. package/packages/react/llms.txt +95 -0
  877. package/packages/react/package.json +95 -0
  878. package/packages/react/src/index.tsx +678 -0
  879. package/packages/react/tests/abandoned-render.test.tsx +816 -0
  880. package/packages/react/tests/auth.test.tsx +315 -0
  881. package/packages/react/tests/caller-keys.test.tsx +2334 -0
  882. package/packages/react/tests/client-time.test.ts +40 -0
  883. package/packages/react/tests/entry.test.ts +52 -0
  884. package/packages/react/tests/helpers.tsx +291 -0
  885. package/packages/react/tests/hydration.test.tsx +262 -0
  886. package/packages/react/tests/ownership.test.tsx +260 -0
  887. package/packages/react/tests/provider.server.test.tsx +169 -0
  888. package/packages/react/tests/query-client.test.tsx +107 -0
  889. package/packages/react/tests/strict-mode.test.tsx +288 -0
  890. package/packages/react/tests/suspense.test-d.tsx +357 -0
  891. package/packages/react/tests/two-query-cores.test.tsx +135 -0
  892. package/packages/react/tests/types.test-d.tsx +63 -0
  893. package/packages/react/tsconfig.json +23 -0
  894. package/packages/react/tsconfig.test.json +8 -0
  895. package/packages/react/tsconfig.typecheck.json +16 -0
  896. package/packages/react/vitest.config.ts +39 -0
  897. package/packages/vite-plugin/LICENSE +21 -0
  898. package/packages/vite-plugin/README.md +532 -0
  899. package/packages/vite-plugin/package.json +76 -0
  900. package/packages/vite-plugin/src/describe-value.test.ts +43 -0
  901. package/packages/vite-plugin/src/describe-value.ts +167 -0
  902. package/packages/vite-plugin/src/dev-environment.test.ts +747 -0
  903. package/packages/vite-plugin/src/dev-environment.ts +510 -0
  904. package/packages/vite-plugin/src/env.ts +431 -0
  905. package/packages/vite-plugin/src/fetch.ts +348 -0
  906. package/packages/vite-plugin/src/file-identity.ts +106 -0
  907. package/packages/vite-plugin/src/generate.ts +691 -0
  908. package/packages/vite-plugin/src/index.test.ts +1243 -0
  909. package/packages/vite-plugin/src/index.ts +2565 -0
  910. package/packages/vite-plugin/src/interface.ts +339 -0
  911. package/packages/vite-plugin/src/local-host.ts +34 -0
  912. package/packages/vite-plugin/src/options.ts +641 -0
  913. package/packages/vite-plugin/src/principal-text.test.ts +87 -0
  914. package/packages/vite-plugin/src/principal-text.ts +98 -0
  915. package/packages/vite-plugin/tests/close.test.ts +141 -0
  916. package/packages/vite-plugin/tests/environment-mode.test.ts +136 -0
  917. package/packages/vite-plugin/tests/fake-ic.ts +214 -0
  918. package/packages/vite-plugin/tests/fetch-sdk.test.ts +27 -0
  919. package/packages/vite-plugin/tests/fetch.test.ts +1196 -0
  920. package/packages/vite-plugin/tests/fixtures/fake-cli/bin.js +90 -0
  921. package/packages/vite-plugin/tests/fixtures/fake-cli/package.json +8 -0
  922. package/packages/vite-plugin/tests/fixtures/icrc1.did +88 -0
  923. package/packages/vite-plugin/tests/generation.test.ts +1036 -0
  924. package/packages/vite-plugin/tests/icp-command.test.ts +194 -0
  925. package/packages/vite-plugin/tests/icp-timeout.test.ts +83 -0
  926. package/packages/vite-plugin/tests/no-process-exit.test.ts +83 -0
  927. package/packages/vite-plugin/tests/on-generated.test.ts +323 -0
  928. package/packages/vite-plugin/tests/output-collision.test.ts +329 -0
  929. package/packages/vite-plugin/tests/package.test.ts +62 -0
  930. package/packages/vite-plugin/tests/project.test.ts +2993 -0
  931. package/packages/vite-plugin/tests/support.ts +339 -0
  932. package/packages/vite-plugin/tests/timeout.test.ts +107 -0
  933. package/packages/vite-plugin/tsconfig.json +23 -0
  934. package/packages/vite-plugin/tsconfig.typecheck.json +16 -0
  935. package/packages/vite-plugin/vitest.config.ts +9 -0
  936. package/pnpm-workspace.yaml +23 -0
  937. package/scripts/ai-context-files.js +72 -0
  938. package/scripts/assemble-docs-site.mjs +485 -0
  939. package/scripts/assemble-docs-site.test.mjs +568 -0
  940. package/scripts/build-examples.js +58 -0
  941. package/scripts/check-ai-context.js +963 -0
  942. package/scripts/check-ai-context.test.mjs +811 -0
  943. package/scripts/check-exports.mjs +767 -0
  944. package/scripts/check-exports.test.mjs +1136 -0
  945. package/scripts/check-snippets/ambient.d.ts +428 -0
  946. package/scripts/check-snippets/app/generated/backend.did +7 -0
  947. package/scripts/check-snippets/app/generated/backend.ts +15 -0
  948. package/scripts/check-snippets/app/generated/icrc1.did +88 -0
  949. package/scripts/check-snippets/app/generated/icrc1.ts +78 -0
  950. package/scripts/check-snippets/app/globals.ts +26 -0
  951. package/scripts/check-snippets/docs/guides/App.tsx +21 -0
  952. package/scripts/check-snippets/docs/guides/app/providers.tsx +23 -0
  953. package/scripts/check-snippets/docs/guides/auth-client.ts +47 -0
  954. package/scripts/check-snippets/docs/guides/canisters/backend.ts +3 -0
  955. package/scripts/check-snippets/docs/guides/canisters/icrc1.ts +3 -0
  956. package/scripts/check-snippets/docs/guides/components/LedgerCard.tsx +26 -0
  957. package/scripts/check-snippets/docs/guides/components/shell.tsx +11 -0
  958. package/scripts/check-snippets/docs/guides/components/sign-in-page.tsx +29 -0
  959. package/scripts/check-snippets/docs/guides/components/slow-section.tsx +5 -0
  960. package/scripts/check-snippets/docs/guides/generated/accounts.ts +95 -0
  961. package/scripts/check-snippets/docs/guides/generated/ckbtc_minter.ts +40 -0
  962. package/scripts/check-snippets/docs/guides/generated/icp_ledger.ts +267 -0
  963. package/scripts/check-snippets/docs/guides/ledger.ts +10 -0
  964. package/scripts/check-snippets/docs/guides/server/request-client.ts +8 -0
  965. package/scripts/check-snippets.mjs +993 -0
  966. package/scripts/check-snippets.test.mjs +338 -0
  967. package/scripts/check-stack.mjs +501 -0
  968. package/scripts/check-stack.test.mjs +800 -0
  969. package/scripts/clean-node-modules.js +67 -0
  970. package/scripts/example-projects.js +58 -0
  971. package/scripts/export-budget.mjs +145 -0
  972. package/scripts/faults.json +6749 -0
  973. package/scripts/lib/fault-workspace.mjs +210 -0
  974. package/scripts/lib/io.mjs +29 -0
  975. package/scripts/lib/io.test.mjs +38 -0
  976. package/scripts/lib/markdown-fences.mjs +56 -0
  977. package/scripts/lib/markdown-fences.test.mjs +84 -0
  978. package/scripts/lib/package-exports.mjs +54 -0
  979. package/scripts/lib/package-exports.test.mjs +66 -0
  980. package/scripts/lib/release-pr-steps.mjs +47 -0
  981. package/scripts/release-tag.mjs +95 -0
  982. package/scripts/release-tag.test.mjs +278 -0
  983. package/scripts/release-workflow.test.mjs +98 -0
  984. package/scripts/release.js +167 -0
  985. package/scripts/release.test.mjs +347 -0
  986. package/scripts/removed-v3-names.js +125 -0
  987. package/scripts/size-app/.size-limit.js +90 -0
  988. package/scripts/size-app/core.js +3 -0
  989. package/scripts/size-app/package.json +24 -0
  990. package/scripts/size-app/react.js +4 -0
  991. package/scripts/size-app.test.mjs +73 -0
  992. package/scripts/sync-ai-context-versions.js +102 -0
  993. package/scripts/sync-example-versions.js +151 -0
  994. package/scripts/test-examples-published.mjs +1585 -0
  995. package/scripts/test-examples-published.test.mjs +1586 -0
  996. package/scripts/traps/any-method-vars-not-a-list.json +5 -0
  997. package/scripts/traps/canister-method-unknown.json +5 -0
  998. package/scripts/traps/client-says-who-calls-once.json +5 -0
  999. package/scripts/traps/client-says-who-calls.json +5 -0
  1000. package/scripts/traps/err-only-on-canister-err.json +14 -0
  1001. package/scripts/traps/format-units-refuses-missing-value.json +5 -0
  1002. package/scripts/traps/format-units-refuses-number.json +5 -0
  1003. package/scripts/traps/format-units-unknown-option.json +5 -0
  1004. package/scripts/traps/guard-err-is-the-methods.json +5 -0
  1005. package/scripts/traps/guard-false-keeps-every-kind.json +5 -0
  1006. package/scripts/traps/internals-not-exported.json +9 -0
  1007. package/scripts/traps/network-refuses-unknown-name.json +5 -0
  1008. package/scripts/traps/never-err-has-no-type.json +5 -0
  1009. package/scripts/traps/options-method-unknown.json +5 -0
  1010. package/scripts/traps/options-vars-wrong-type.json +5 -0
  1011. package/scripts/traps/parse-units-refuses-number.json +5 -0
  1012. package/scripts/traps/parse-units-unknown-option.json +5 -0
  1013. package/scripts/traps/principal-is-not-plain-text.json +14 -0
  1014. package/scripts/traps/query-options-refuses-oneway.json +5 -0
  1015. package/scripts/traps/query-options-refuses-update.json +5 -0
  1016. package/scripts/traps/refuse-next-unknown-option.json +5 -0
  1017. package/scripts/traps/resend-key-reads-the-methods-arg.json +5 -0
  1018. package/scripts/traps/resend-states-its-dedupe-key.json +5 -0
  1019. package/scripts/traps/suspense-refuses-argument-skip.json +5 -0
  1020. package/scripts/traps/suspense-refuses-skippable-read.json +5 -0
  1021. package/scripts/traps/suspense-refuses-vars-skip-fits.json +5 -0
  1022. package/scripts/traps/test-handler-method-unknown.json +5 -0
  1023. package/scripts/traps/test-handler-nat-is-bigint.json +5 -0
  1024. package/scripts/traps/test-handler-takes-the-domain-value.json +5 -0
  1025. package/scripts/traps/vars-of-two-arguments-are-the-tuple.json +5 -0
  1026. package/scripts/typecheck-examples.js +122 -0
  1027. package/scripts/verify-faults.mjs +375 -0
  1028. package/scripts/verify-faults.test.mjs +402 -0
  1029. package/scripts/verify-package-artifacts.mjs +590 -0
  1030. package/scripts/verify-peer-floors.js +399 -0
  1031. package/scripts/verify-peer-floors.test.mjs +422 -0
  1032. package/scripts/verify-test-fails-rules.mjs +104 -0
  1033. package/scripts/verify-test-fails-rules.test.mjs +215 -0
  1034. package/scripts/verify-test-fails.js +371 -0
  1035. package/scripts/verify-traps.mjs +505 -0
  1036. package/scripts/verify-traps.test.mjs +521 -0
  1037. package/scripts/verify-typescript-floor.js +239 -0
  1038. package/skill-packages/ic-reactor/SKILL.md +103 -0
  1039. package/stack.json +8 -0
  1040. package/tsconfig.base.json +23 -0
  1041. package/tsconfig.json +8 -0
@@ -0,0 +1,1196 @@
1
+ /**
2
+ * Fetching: a canister with a `canisterId` whose `didFile` is not on disk has
3
+ * the file written from the live canister's certified `candid:service`, under
4
+ * a real `vite build` and a real `vite dev`, and then generated by the real
5
+ * `@meantjs/cli`.
6
+ *
7
+ * The canisters live on a fake replica on a local port (tests/fake-ic.ts),
8
+ * which signs its certificates with a root key of its own: a local host, so
9
+ * the plugin fetches that key from it as `createClient` would.
10
+ */
11
+ import fs from "node:fs"
12
+ import http from "node:http"
13
+ import type { AddressInfo } from "node:net"
14
+ import os from "node:os"
15
+ import path from "node:path"
16
+ import { spawn } from "node:child_process"
17
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"
18
+ import type { Plugin } from "vite"
19
+ import { fetchCandid, resolveNetwork } from "../src/fetch.js"
20
+ import { fileIdentity } from "../src/file-identity.js"
21
+ import { foldsCase } from "../src/generate.js"
22
+ import { icReactor, type IcReactorPluginOptions } from "../src/index.js"
23
+ import { startFakeIc } from "./fake-ic.js"
24
+ import {
25
+ createApp,
26
+ LEDGER_DID,
27
+ PING_DID,
28
+ recordingLogger,
29
+ runBuild,
30
+ spawnedExited,
31
+ startDev,
32
+ type App,
33
+ type Running,
34
+ } from "./support.js"
35
+
36
+ // The real `spawn`, watched: the tests count the generator processes.
37
+ vi.mock("node:child_process", async (importOriginal) => {
38
+ const actual = await importOriginal<typeof import("node:child_process")>()
39
+ return { ...actual, spawn: vi.fn(actual.spawn) }
40
+ })
41
+
42
+ const LEDGER = fs.readFileSync(LEDGER_DID, "utf-8")
43
+ const LEDGER_ID = "ryjl3-tyaaa-aaaaa-aaaba-cai"
44
+ const MINTER_ID = "mqygn-kiaaa-aaaar-qaadq-cai"
45
+ const PRIVATE_ID = "rrkah-fqaaa-aaaaa-aaaaq-cai"
46
+ const MISSING_ID = "qoctq-giaaa-aaaaa-aaaea-cai"
47
+
48
+ const generatorRuns = (): number =>
49
+ vi.mocked(spawn).mock.calls.filter(([, args]) => (args ?? [])[1] === "gen")
50
+ .length
51
+
52
+ const closers: Array<() => Promise<unknown>> = []
53
+ const apps: App[] = []
54
+
55
+ beforeEach(() => {
56
+ vi.mocked(spawn).mockClear()
57
+ })
58
+
59
+ afterEach(async () => {
60
+ vi.unstubAllGlobals()
61
+ vi.unstubAllEnvs()
62
+ for (const close of closers.splice(0).reverse()) await close()
63
+ await spawnedExited(vi.mocked(spawn))
64
+ for (const app of apps.splice(0)) app.cleanup()
65
+ })
66
+
67
+ function newApp(files: Record<string, string>) {
68
+ const app = createApp(files, "real")
69
+ apps.push(app)
70
+ return app
71
+ }
72
+
73
+ async function fakeIc(canisters: Parameters<typeof startFakeIc>[0]) {
74
+ const ic = await startFakeIc(canisters)
75
+ closers.push(ic.close)
76
+ return ic
77
+ }
78
+
79
+ async function serve(root: string, options: IcReactorPluginOptions) {
80
+ const running: Running = await startDev(root, options)
81
+ closers.push(running.close)
82
+ return running
83
+ }
84
+
85
+ /** A `fetch` that refuses every request and remembers what it was asked. */
86
+ function refuseNetwork(): string[] {
87
+ const asked: string[] = []
88
+ vi.stubGlobal(
89
+ "fetch",
90
+ vi.fn((input: unknown) => {
91
+ asked.push(String(input instanceof Request ? input.url : input))
92
+ return Promise.reject(
93
+ new TypeError("fetch failed: the test refuses the network")
94
+ )
95
+ })
96
+ )
97
+ return asked
98
+ }
99
+
100
+ /**
101
+ * A `fetch` that sends what is asked of `host` to `to` instead: a host that is
102
+ * not local, which the fake replica answers.
103
+ */
104
+ function forward(host: string, to: string): void {
105
+ const real = globalThis.fetch
106
+ vi.stubGlobal(
107
+ "fetch",
108
+ vi.fn((input: string | URL | Request, init?: RequestInit) => {
109
+ const url = input instanceof Request ? input.url : String(input)
110
+ if (!url.startsWith(host)) {
111
+ return Promise.reject(new TypeError(`the test refuses ${url}`))
112
+ }
113
+ return real(to + url.slice(host.length), init)
114
+ })
115
+ )
116
+ }
117
+
118
+ /** A local URL that accepts connections and never answers. */
119
+ async function silentHost(): Promise<string> {
120
+ const server = http.createServer(() => {})
121
+ await new Promise<void>((resolve) =>
122
+ server.listen(0, "127.0.0.1", () => resolve())
123
+ )
124
+ closers.push(
125
+ () =>
126
+ new Promise((resolve) => {
127
+ server.closeAllConnections()
128
+ server.close(resolve)
129
+ })
130
+ )
131
+ const { port } = server.address() as AddressInfo
132
+ return `http://127.0.0.1:${port}`
133
+ }
134
+
135
+ /** A local URL nothing listens on. */
136
+ async function closedHost(): Promise<string> {
137
+ const server = http.createServer()
138
+ await new Promise<void>((resolve) =>
139
+ server.listen(0, "127.0.0.1", () => resolve())
140
+ )
141
+ const { port } = server.address() as AddressInfo
142
+ await new Promise((resolve) => server.close(resolve))
143
+ return `http://127.0.0.1:${port}`
144
+ }
145
+
146
+ /**
147
+ * One `vite build` plugin for a ledger canister on `host`, whose buildStart
148
+ * the test calls directly: as `vite build --watch` does on every rebuild, and
149
+ * Vite 6 and later once for each environment.
150
+ */
151
+ function oneBuildPlugin(app: App, host: string) {
152
+ const plugin = icReactor({
153
+ canisters: {
154
+ ledger: {
155
+ didFile: "did/ledger.did",
156
+ canisterId: LEDGER_ID,
157
+ network: { host },
158
+ },
159
+ },
160
+ }) as Plugin & {
161
+ configResolved: (config: unknown) => void
162
+ buildStart: (this: unknown) => Promise<void>
163
+ }
164
+ const { logger, lines } = recordingLogger()
165
+ plugin.configResolved({ root: app.root, command: "build", logger })
166
+ const context = { addWatchFile: vi.fn(), error: vi.fn() }
167
+ return { buildStart: () => plugin.buildStart.call(context), context, lines }
168
+ }
169
+
170
+ const read = (app: App, file: string) =>
171
+ fs.readFileSync(path.join(app.root, file), "utf-8")
172
+ const exists = (app: App, file: string) =>
173
+ fs.existsSync(path.join(app.root, file))
174
+
175
+ /** What a build threw, as text. */
176
+ async function buildError(app: App, options: IcReactorPluginOptions) {
177
+ const { result } = runBuild(app.root, options)
178
+ return result.then(
179
+ () => "",
180
+ (error: unknown) => (error instanceof Error ? error.message : String(error))
181
+ )
182
+ }
183
+
184
+ describe("a didFile on disk", () => {
185
+ it("is generated with no network request under vite build and vite dev", async () => {
186
+ const asked = refuseNetwork()
187
+ const app = newApp({ "did/ledger.did": LEDGER })
188
+ const options: IcReactorPluginOptions = {
189
+ canisters: {
190
+ ledger: {
191
+ didFile: "did/ledger.did",
192
+ canisterId: LEDGER_ID,
193
+ network: "ic",
194
+ },
195
+ },
196
+ }
197
+
198
+ const { result, lines } = runBuild(app.root, options)
199
+ await result
200
+ expect(lines).toContainEqual(
201
+ expect.stringContaining("generated ledger into src/canisters/ledger.ts")
202
+ )
203
+
204
+ const running = await serve(app.root, options)
205
+ expect(running.lines.filter((line) => line.startsWith("error:"))).toEqual(
206
+ []
207
+ )
208
+
209
+ expect(asked).toEqual([])
210
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
211
+ })
212
+ })
213
+
214
+ describe("a missing didFile", () => {
215
+ it("is written from the certified candid:service and generated, and a second build makes no request", async () => {
216
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
217
+ const app = newApp({})
218
+ const options: IcReactorPluginOptions = {
219
+ canisters: {
220
+ ledger: {
221
+ didFile: "did/ledger.did",
222
+ canisterId: LEDGER_ID,
223
+ network: { host: ic.host },
224
+ },
225
+ },
226
+ }
227
+
228
+ const first = runBuild(app.root, options)
229
+ await first.result
230
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
231
+ expect(exists(app, "src/canisters/ledger.ts")).toBe(true)
232
+ expect(first.lines).toContainEqual(
233
+ expect.stringContaining(
234
+ `wrote did/ledger.did from the certified candid:service of ${LEDGER_ID} on ${ic.host}`
235
+ )
236
+ )
237
+ // The root key from the local host, then the certified read.
238
+ expect(ic.requests).toEqual([
239
+ "GET /api/v2/status",
240
+ `POST /api/v3/canister/${LEDGER_ID}/read_state`,
241
+ ])
242
+
243
+ const second = runBuild(app.root, options)
244
+ await second.result
245
+ expect(ic.requests).toHaveLength(2)
246
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
247
+ })
248
+
249
+ it("is fetched once for the entries that share it and name one canister", async () => {
250
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
251
+ const app = newApp({})
252
+ const network = { host: ic.host }
253
+ const { result } = runBuild(app.root, {
254
+ canisters: {
255
+ icp: { didFile: "icrc1.did", canisterId: LEDGER_ID, network },
256
+ icp_admin: {
257
+ didFile: "icrc1.did",
258
+ canisterId: LEDGER_ID,
259
+ network,
260
+ outDir: "src/admin",
261
+ },
262
+ },
263
+ })
264
+ await result
265
+ expect(ic.requests).toEqual([
266
+ "GET /api/v2/status",
267
+ `POST /api/v3/canister/${LEDGER_ID}/read_state`,
268
+ ])
269
+ expect(exists(app, "src/canisters/icrc1.ts")).toBe(true)
270
+ expect(exists(app, "src/admin/icrc1.ts")).toBe(true)
271
+ })
272
+
273
+ it("is written and generated once under vite dev, though the watcher reports the write", async () => {
274
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
275
+ const app = newApp({})
276
+ const { server, lines } = await serve(app.root, {
277
+ canisters: {
278
+ ledger: {
279
+ didFile: "did/ledger.did",
280
+ canisterId: LEDGER_ID,
281
+ network: { host: ic.host },
282
+ },
283
+ },
284
+ })
285
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
286
+ expect(generatorRuns()).toBe(1)
287
+
288
+ const file = path.join(app.root, "did/ledger.did")
289
+ server.watcher.emit("add", file)
290
+ await new Promise((resolve) => setTimeout(resolve, 200))
291
+ expect(generatorRuns()).toBe(1)
292
+
293
+ // A save after it is the user's, and regenerates.
294
+ server.watcher.emit("change", file)
295
+ await vi.waitFor(() => expect(generatorRuns()).toBe(2))
296
+ expect(lines.filter((line) => line.startsWith("error:"))).toEqual([])
297
+ })
298
+ })
299
+
300
+ /** `app` with `linked`, a symlink to its `did` directory, made first. */
301
+ function linkDid(app: App): App {
302
+ fs.mkdirSync(path.join(app.root, "did"), { recursive: true })
303
+ fs.symlinkSync(
304
+ path.join(app.root, "did"),
305
+ path.join(app.root, "linked"),
306
+ "junction"
307
+ )
308
+ return app
309
+ }
310
+
311
+ // Whether the temporary directory the apps are made in ignores case, as macOS
312
+ // and Windows do by default and Linux does not.
313
+ const IGNORES_CASE = foldsCase(fs.realpathSync(os.tmpdir())) === true
314
+
315
+ describe("one didFile under two paths", () => {
316
+ it("through a symlinked directory is fetched once, and refused when its canisters differ", async () => {
317
+ const ic = await fakeIc({
318
+ [LEDGER_ID]: { candid: LEDGER },
319
+ [MINTER_ID]: { candid: PING_DID },
320
+ })
321
+ const network = { host: ic.host }
322
+ const linked = (app: App) => {
323
+ fs.mkdirSync(path.join(app.root, "did"))
324
+ fs.symlinkSync(
325
+ path.join(app.root, "did"),
326
+ path.join(app.root, "linked"),
327
+ "junction"
328
+ )
329
+ return app
330
+ }
331
+
332
+ // One canister: one fetch, and both entries generate from what it wrote.
333
+ const app = linked(newApp({}))
334
+ await runBuild(app.root, {
335
+ canisters: {
336
+ a: { didFile: "did/ledger.did", canisterId: LEDGER_ID, network },
337
+ b: {
338
+ didFile: "linked/ledger.did",
339
+ canisterId: LEDGER_ID,
340
+ network,
341
+ outDir: "src/b",
342
+ },
343
+ },
344
+ }).result
345
+ expect(ic.requests).toEqual([
346
+ "GET /api/v2/status",
347
+ `POST /api/v3/canister/${LEDGER_ID}/read_state`,
348
+ ])
349
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
350
+ expect(read(app, "src/canisters/ledger.ts")).toContain("icrc1_transfer")
351
+ expect(read(app, "src/b/ledger.ts")).toContain("icrc1_transfer")
352
+
353
+ // Two canisters: whose interface the one file holds cannot be told.
354
+ const other = linked(newApp({}))
355
+ const message = await buildError(other, {
356
+ canisters: {
357
+ a: { didFile: "did/ledger.did", canisterId: LEDGER_ID, network },
358
+ b: {
359
+ didFile: "linked/ledger.did",
360
+ canisterId: MINTER_ID,
361
+ network,
362
+ outDir: "src/b",
363
+ },
364
+ },
365
+ })
366
+ expect(message).toContain(
367
+ `- a (did/ledger.did), b (linked/ledger.did): "a", "b" name did/ledger.did and linked/ledger.did, which are one file, ` +
368
+ `but not one canister to fetch it from (a: ${LEDGER_ID} on ${ic.host}; b: ${MINTER_ID} on ${ic.host})`
369
+ )
370
+ expect(ic.requests).toHaveLength(2)
371
+ expect(exists(other, "did/ledger.did")).toBe(false)
372
+ })
373
+
374
+ it("through a symlinked directory regenerates every entry when the watcher reports either name", async () => {
375
+ const app = newApp({ "did/ledger.did": LEDGER })
376
+ fs.symlinkSync(
377
+ path.join(app.root, "did"),
378
+ path.join(app.root, "linked"),
379
+ "junction"
380
+ )
381
+ const { server, lines } = await serve(app.root, {
382
+ canisters: {
383
+ a: { didFile: "did/ledger.did" },
384
+ b: { didFile: "linked/ledger.did", outDir: "src/b" },
385
+ },
386
+ })
387
+ // One generator process for each outDir.
388
+ expect(generatorRuns()).toBe(2)
389
+
390
+ server.watcher.emit("change", path.join(app.root, "linked/ledger.did"))
391
+ await vi.waitFor(() => expect(generatorRuns()).toBe(4))
392
+ expect(lines).toContainEqual(
393
+ expect.stringContaining("linked/ledger.did changed, regenerating a, b")
394
+ )
395
+ expect(lines.filter((line) => line.startsWith("error:"))).toEqual([])
396
+ })
397
+
398
+ // The watcher may report one file under any name of it: each of these
399
+ // reports it under the name the other entry gives it.
400
+ it("through a symlinked directory starts no run when the watcher reports the fetched write under the other name", async () => {
401
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
402
+ const app = linkDid(newApp({}))
403
+ const network = { host: ic.host }
404
+ const { server, lines } = await serve(app.root, {
405
+ canisters: {
406
+ a: { didFile: "did/ledger.did", canisterId: LEDGER_ID, network },
407
+ b: {
408
+ didFile: "linked/ledger.did",
409
+ canisterId: LEDGER_ID,
410
+ network,
411
+ outDir: "src/b",
412
+ },
413
+ },
414
+ })
415
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
416
+ expect(generatorRuns()).toBe(2)
417
+
418
+ server.watcher.emit("add", path.join(app.root, "linked/ledger.did"))
419
+ await new Promise((resolve) => setTimeout(resolve, 200))
420
+ expect(generatorRuns()).toBe(2)
421
+ expect(lines.filter((line) => line.startsWith("error:"))).toEqual([])
422
+ })
423
+
424
+ it("through a symlinked directory fails every entry when the watcher reports the file deleted under either name", async () => {
425
+ const app = linkDid(newApp({}))
426
+ fs.writeFileSync(path.join(app.root, "did/ledger.did"), LEDGER)
427
+ const { server, lines } = await serve(app.root, {
428
+ canisters: {
429
+ a: { didFile: "did/ledger.did" },
430
+ b: { didFile: "linked/ledger.did", outDir: "src/b" },
431
+ },
432
+ })
433
+ expect(generatorRuns()).toBe(2)
434
+
435
+ fs.rmSync(path.join(app.root, "did/ledger.did"))
436
+ server.watcher.emit("unlink", path.join(app.root, "linked/ledger.did"))
437
+ await vi.waitFor(() =>
438
+ expect(lines).toContainEqual(
439
+ expect.stringContaining(
440
+ "- a (did/ledger.did), b (linked/ledger.did): the .did file was deleted"
441
+ )
442
+ )
443
+ )
444
+ expect(generatorRuns()).toBe(2)
445
+ })
446
+
447
+ it("through a symlinked directory starts one run for two saves reported under each name", async () => {
448
+ const app = linkDid(newApp({}))
449
+ fs.writeFileSync(path.join(app.root, "did/ledger.did"), LEDGER)
450
+ fs.writeFileSync(path.join(app.root, "did/ping.did"), PING_DID)
451
+ const { server, lines } = await serve(app.root, {
452
+ canisters: {
453
+ a: { didFile: "did/ledger.did" },
454
+ b: { didFile: "linked/ledger.did", outDir: "src/b" },
455
+ c: { didFile: "did/ping.did", outDir: "src/c" },
456
+ },
457
+ })
458
+ expect(generatorRuns()).toBe(3)
459
+
460
+ // Both before the queued run starts: the second joins it.
461
+ server.watcher.emit("change", path.join(app.root, "did/ledger.did"))
462
+ server.watcher.emit("change", path.join(app.root, "linked/ledger.did"))
463
+ // Runs go one after another, so once the run of a save queued after
464
+ // these has started, any second run for them would already have been.
465
+ server.watcher.emit("change", path.join(app.root, "did/ping.did"))
466
+ const regenerating = (file: string) =>
467
+ lines.filter((line) => line.includes(`${file} changed, regenerating`))
468
+ await vi.waitFor(() => expect(regenerating("ping.did")).toHaveLength(1), {
469
+ timeout: 10_000,
470
+ })
471
+ await vi.waitFor(() => expect(generatorRuns()).toBe(6))
472
+ expect(regenerating("ledger.did")).toHaveLength(1)
473
+ expect(lines.filter((line) => line.startsWith("error:"))).toEqual([])
474
+ })
475
+
476
+ it("through a symlink to a file is written through it, so an entry that names the target generates from the fetch", async () => {
477
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
478
+ const app = newApp({ "shared/ledger.did": PING_DID })
479
+ fs.mkdirSync(path.join(app.root, "did"))
480
+ fs.symlinkSync(
481
+ "../shared/ledger.did",
482
+ path.join(app.root, "did/ledger.did"),
483
+ "file"
484
+ )
485
+ const network = { host: ic.host }
486
+ vi.stubEnv("IC_REACTOR_FETCH", "all")
487
+ // @meantjs/cli 0.5.1 reads no .did through a symlink to a file, so
488
+ // "a" fails to generate, loudly, with did_source_not_found, as it would
489
+ // with no fetch at all: failOnError is off to look past it.
490
+ const { result, lines } = runBuild(app.root, {
491
+ failOnError: false,
492
+ canisters: {
493
+ a: { didFile: "did/ledger.did", canisterId: LEDGER_ID, network },
494
+ b: {
495
+ didFile: "shared/ledger.did",
496
+ canisterId: LEDGER_ID,
497
+ network,
498
+ outDir: "src/b",
499
+ },
500
+ },
501
+ })
502
+ await result
503
+ expect(ic.requests).toHaveLength(2)
504
+ expect(
505
+ fs.lstatSync(path.join(app.root, "did/ledger.did")).isSymbolicLink()
506
+ ).toBe(true)
507
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
508
+ expect(read(app, "shared/ledger.did")).toBe(LEDGER)
509
+ expect(read(app, "src/b/ledger.ts")).toContain("icrc1_transfer")
510
+ expect(
511
+ lines.filter((line) => line.startsWith("error:")).join("\n")
512
+ ).toContain("- a (did/ledger.did): did_source_not_found")
513
+ expect(lines).not.toContainEqual(expect.stringContaining("- b ("))
514
+ })
515
+
516
+ it("through a directory symlink whose target is not made yet is one file, and refused when its canisters differ", async () => {
517
+ const ic = await fakeIc({
518
+ [LEDGER_ID]: { candid: LEDGER },
519
+ [MINTER_ID]: { candid: PING_DID },
520
+ })
521
+ const network = { host: ic.host }
522
+ // `linked` names `did`, which the fetch makes.
523
+ const dangling = (app: App) => {
524
+ fs.symlinkSync(
525
+ path.join(app.root, "did"),
526
+ path.join(app.root, "linked"),
527
+ "junction"
528
+ )
529
+ return app
530
+ }
531
+
532
+ const app = dangling(newApp({}))
533
+ await runBuild(app.root, {
534
+ canisters: {
535
+ a: { didFile: "did/ledger.did", canisterId: LEDGER_ID, network },
536
+ b: {
537
+ didFile: "linked/ledger.did",
538
+ canisterId: LEDGER_ID,
539
+ network,
540
+ outDir: "src/b",
541
+ },
542
+ },
543
+ }).result
544
+ expect(ic.requests).toHaveLength(2)
545
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
546
+ expect(read(app, "src/canisters/ledger.ts")).toContain("icrc1_transfer")
547
+ expect(read(app, "src/b/ledger.ts")).toContain("icrc1_transfer")
548
+
549
+ const other = dangling(newApp({}))
550
+ const message = await buildError(other, {
551
+ canisters: {
552
+ a: { didFile: "did/ledger.did", canisterId: LEDGER_ID, network },
553
+ b: {
554
+ didFile: "linked/ledger.did",
555
+ canisterId: MINTER_ID,
556
+ network,
557
+ outDir: "src/b",
558
+ },
559
+ },
560
+ })
561
+ expect(message).toContain(
562
+ `"a", "b" name did/ledger.did and linked/ledger.did, which are one file, but not one canister to fetch it from`
563
+ )
564
+ expect(ic.requests).toHaveLength(2)
565
+ expect(exists(other, "did")).toBe(false)
566
+ })
567
+
568
+ // Skipped where the filesystem tells the two names apart, as Linux's does:
569
+ // there they are two files, and fetched as two.
570
+ it.skipIf(!IGNORES_CASE)(
571
+ "in another case is one file where the filesystem ignores case",
572
+ async () => {
573
+ const ic = await fakeIc({
574
+ [LEDGER_ID]: { candid: LEDGER },
575
+ [MINTER_ID]: { candid: PING_DID },
576
+ })
577
+ const network = { host: ic.host }
578
+
579
+ // One canister: one fetch, written once. (The generator itself, at
580
+ // @meantjs/cli 0.5.1, reads a .did by its exact case, so the entry
581
+ // that spells it in another case fails to generate, loudly, with
582
+ // did_source_not_found: failOnError is off to look past it.)
583
+ const app = newApp({})
584
+ await runBuild(app.root, {
585
+ failOnError: false,
586
+ canisters: {
587
+ upper: { didFile: "did/Ledger.did", canisterId: LEDGER_ID, network },
588
+ lower: {
589
+ didFile: "did/ledger.did",
590
+ canisterId: LEDGER_ID,
591
+ network,
592
+ outDir: "src/lower",
593
+ },
594
+ },
595
+ }).result
596
+ expect(ic.requests).toEqual([
597
+ "GET /api/v2/status",
598
+ `POST /api/v3/canister/${LEDGER_ID}/read_state`,
599
+ ])
600
+ expect(fs.readdirSync(path.join(app.root, "did"))).toEqual(["Ledger.did"])
601
+ expect(read(app, "did/Ledger.did")).toBe(LEDGER)
602
+ expect(read(app, "src/canisters/Ledger.ts")).toContain("icrc1_transfer")
603
+
604
+ // Two canisters, each with an outDir of its own: one would otherwise
605
+ // generate from the other's interface.
606
+ const other = newApp({})
607
+ const message = await buildError(other, {
608
+ canisters: {
609
+ upper: { didFile: "did/Ledger.did", canisterId: LEDGER_ID, network },
610
+ lower: {
611
+ didFile: "did/ledger.did",
612
+ canisterId: MINTER_ID,
613
+ network,
614
+ outDir: "src/lower",
615
+ },
616
+ },
617
+ })
618
+ expect(message).toContain(
619
+ `"upper", "lower" name did/Ledger.did and did/ledger.did, which are one file, but not one canister to fetch it from`
620
+ )
621
+ expect(ic.requests).toHaveLength(2)
622
+ expect(exists(other, "did")).toBe(false)
623
+ }
624
+ )
625
+ })
626
+
627
+ describe("canisters that name one didFile and different canisters", () => {
628
+ it("are refused with no fetch, and share the file once it is on disk", async () => {
629
+ const ic = await fakeIc({
630
+ [LEDGER_ID]: { candid: LEDGER },
631
+ [MINTER_ID]: { candid: LEDGER },
632
+ })
633
+ const app = newApp({})
634
+ const network = { host: ic.host }
635
+ const options: IcReactorPluginOptions = {
636
+ canisters: {
637
+ icp: { didFile: "icrc1.did", canisterId: LEDGER_ID, network },
638
+ ckbtc: { didFile: "icrc1.did", canisterId: MINTER_ID, network },
639
+ },
640
+ }
641
+
642
+ const message = await buildError(app, options)
643
+ expect(message).toContain("could not generate 2 of 2 canisters")
644
+ expect(message).toContain(
645
+ `- icp (icrc1.did), ckbtc (icrc1.did): "icp", "ckbtc" name icrc1.did but not one canister to fetch it from ` +
646
+ `(icp: ${LEDGER_ID} on ${ic.host}; ckbtc: ${MINTER_ID} on ${ic.host}): ` +
647
+ `which one's interface the file holds cannot be told, so it is not fetched. ` +
648
+ `Give each canister a didFile of its own, or write icrc1.did yourself`
649
+ )
650
+ expect(ic.requests).toEqual([])
651
+ expect(exists(app, "icrc1.did")).toBe(false)
652
+
653
+ // One canister on two networks is two sources as well.
654
+ const networks = await buildError(app, {
655
+ canisters: {
656
+ icp: { didFile: "icrc1.did", canisterId: LEDGER_ID, network },
657
+ icp_main: {
658
+ didFile: "icrc1.did",
659
+ canisterId: LEDGER_ID,
660
+ network: "ic",
661
+ },
662
+ },
663
+ })
664
+ expect(networks).toContain(
665
+ `(icp: ${LEDGER_ID} on ${ic.host}; icp_main: ${LEDGER_ID} on ic)`
666
+ )
667
+ expect(ic.requests).toEqual([])
668
+
669
+ // A network that is not one refuses its canister, and the fetch of the
670
+ // file it shares, naming the network as given.
671
+ const invalid = await buildError(app, {
672
+ canisters: {
673
+ icp: { didFile: "icrc1.did", canisterId: LEDGER_ID, network },
674
+ icp_typo: {
675
+ didFile: "icrc1.did",
676
+ canisterId: LEDGER_ID,
677
+ network: "mainnet" as never,
678
+ },
679
+ },
680
+ })
681
+ expect(invalid).toContain(
682
+ `- icp (icrc1.did), icp_typo (icrc1.did): "icp", "icp_typo" name icrc1.did, which is not fetched, ` +
683
+ `since "icp_typo" cannot be generated (icp_typo: network "mainnet" is not "ic", "local" or { host: "<http or https URL>" })`
684
+ )
685
+ expect(ic.requests).toEqual([])
686
+
687
+ // On disk, the file is theirs to share, and nothing is fetched.
688
+ fs.writeFileSync(path.join(app.root, "icrc1.did"), LEDGER)
689
+ await runBuild(app.root, options).result
690
+ expect(read(app, "src/canisters/icrc1.ts")).toContain("icrc1_transfer")
691
+ expect(ic.requests).toEqual([])
692
+
693
+ // Fetching it again names one of them, and is refused the same way.
694
+ vi.stubEnv("IC_REACTOR_FETCH", "icp")
695
+ expect(await buildError(app, options)).toContain(
696
+ `"icp", "ckbtc" name icrc1.did but not one canister to fetch it from`
697
+ )
698
+ expect(ic.requests).toEqual([])
699
+ expect(read(app, "icrc1.did")).toBe(LEDGER)
700
+ })
701
+ })
702
+
703
+ describe("fileIdentity", () => {
704
+ it("is one key for every spelling of a file, before and after it exists", () => {
705
+ const app = newApp({})
706
+ fs.mkdirSync(path.join(app.root, "did"))
707
+ fs.symlinkSync(
708
+ path.join(app.root, "did"),
709
+ path.join(app.root, "linked"),
710
+ "junction"
711
+ )
712
+ const at = (file: string) => path.join(app.root, file)
713
+ const folding = (file: string) => fileIdentity(at(file), () => true)
714
+ const exact = (file: string) => fileIdentity(at(file), () => false)
715
+
716
+ // Missing: through the symlink, and in another case where case folds.
717
+ expect(exact("linked/ledger.did")).toBe(exact("did/ledger.did"))
718
+ expect(folding("did/Ledger.did")).toBe(folding("did/ledger.did"))
719
+ expect(folding("linked/new/Ledger.did")).toBe(folding("did/new/ledger.did"))
720
+ expect(exact("did/Ledger.did")).not.toBe(exact("did/ledger.did"))
721
+ const before = folding("did/ledger.did")
722
+
723
+ // On disk, under the spelling it was written with.
724
+ fs.writeFileSync(at("did/Ledger.did"), PING_DID)
725
+ expect(folding("did/Ledger.did")).toBe(before)
726
+ expect(folding("linked/Ledger.did")).toBe(before)
727
+ expect(exact("linked/Ledger.did")).toBe(exact("did/Ledger.did"))
728
+
729
+ // Through symlinks whose targets are not made yet: a directory, and a
730
+ // file, each named as the target it will be.
731
+ fs.symlinkSync(at("later"), at("ahead"), "junction")
732
+ expect(exact("ahead/ledger.did")).toBe(exact("later/ledger.did"))
733
+ fs.symlinkSync("../later/x.did", at("did/x.did"), "file")
734
+ expect(exact("did/x.did")).toBe(exact("later/x.did"))
735
+ expect(exact("linked/x.did")).toBe(exact("later/x.did"))
736
+ fs.mkdirSync(at("later"))
737
+ fs.writeFileSync(at("later/x.did"), PING_DID)
738
+ expect(exact("ahead/x.did")).toBe(exact("did/x.did"))
739
+ })
740
+ })
741
+
742
+ describe("a fetch that fails", () => {
743
+ it("says which of unreachable, refused, absent and not found it was, naming the canister", async () => {
744
+ const ic = await fakeIc({
745
+ [PRIVATE_ID]: { private: true },
746
+ [LEDGER_ID]: { absent: true },
747
+ })
748
+ const down = await closedHost()
749
+ const app = newApp({})
750
+ const message = await buildError(app, {
751
+ canisters: {
752
+ offline: {
753
+ didFile: "did/offline.did",
754
+ canisterId: LEDGER_ID,
755
+ network: { host: down },
756
+ },
757
+ secret: {
758
+ didFile: "did/secret.did",
759
+ canisterId: PRIVATE_ID,
760
+ network: { host: ic.host },
761
+ },
762
+ bare: {
763
+ didFile: "did/bare.did",
764
+ canisterId: LEDGER_ID,
765
+ network: { host: ic.host },
766
+ },
767
+ gone: {
768
+ didFile: "did/gone.did",
769
+ canisterId: MISSING_ID,
770
+ network: { host: ic.host },
771
+ },
772
+ },
773
+ })
774
+
775
+ expect(message).toContain("could not generate 4 of 4 canisters")
776
+ const lineOf = (name: string) =>
777
+ message.split("\n").find((line) => line.includes(`- ${name} (`)) ?? ""
778
+
779
+ expect(lineOf("offline")).toContain(
780
+ `offline (did/offline.did): could not reach ${down} to read the candid:service of ${LEDGER_ID} (`
781
+ )
782
+ expect(lineOf("offline")).toContain("Check the connection and build again")
783
+
784
+ expect(lineOf("secret")).toContain(
785
+ `secret (did/secret.did): ${ic.host} refused to read the candid:service of ${PRIVATE_ID} (HTTP 403`
786
+ )
787
+ expect(lineOf("secret")).toContain(
788
+ "get the .did from the canister's developers and save it as did/secret.did"
789
+ )
790
+
791
+ expect(lineOf("bare")).toContain(
792
+ `bare (did/bare.did): ${LEDGER_ID} on ${ic.host} publishes no candid:service metadata`
793
+ )
794
+ expect(lineOf("bare")).toContain("save it as did/bare.did")
795
+
796
+ expect(lineOf("gone")).toContain(
797
+ `gone (did/gone.did): ${MISSING_ID} does not exist on ${ic.host}`
798
+ )
799
+ expect(lineOf("gone")).toContain("Check canisterId")
800
+
801
+ for (const file of ["offline", "secret", "bare", "gone"]) {
802
+ expect(exists(app, `did/${file}.did`)).toBe(false)
803
+ }
804
+ })
805
+
806
+ it("follows failOnError: logged, and the other canisters generated", async () => {
807
+ const ic = await fakeIc({ [PRIVATE_ID]: { private: true } })
808
+ const app = newApp({ "did/ping.did": PING_DID })
809
+ const { result, lines } = runBuild(app.root, {
810
+ failOnError: false,
811
+ canisters: {
812
+ ping: { didFile: "did/ping.did" },
813
+ secret: {
814
+ didFile: "did/secret.did",
815
+ canisterId: PRIVATE_ID,
816
+ network: { host: ic.host },
817
+ },
818
+ },
819
+ })
820
+ await result
821
+ const errors = lines.filter((line) => line.startsWith("error:"))
822
+ expect(errors).toHaveLength(1)
823
+ expect(errors[0]).toContain("could not generate 1 of 2 canisters")
824
+ expect(errors[0]).toContain("secret (did/secret.did)")
825
+ expect(exists(app, "src/canisters/ping.ts")).toBe(true)
826
+ })
827
+ })
828
+
829
+ describe("IC_REACTOR_FETCH", () => {
830
+ const OLD = "service : { old : () -> () };\n"
831
+
832
+ it("is the only thing that rewrites a didFile on disk", async () => {
833
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
834
+ const app = newApp({ "did/ledger.did": OLD })
835
+ const options: IcReactorPluginOptions = {
836
+ canisters: {
837
+ ledger: {
838
+ didFile: "did/ledger.did",
839
+ canisterId: LEDGER_ID,
840
+ network: { host: ic.host },
841
+ },
842
+ },
843
+ }
844
+
845
+ await runBuild(app.root, options).result
846
+ expect(ic.requests).toEqual([])
847
+ expect(read(app, "did/ledger.did")).toBe(OLD)
848
+
849
+ vi.stubEnv("IC_REACTOR_FETCH", "ledger")
850
+ const { result, lines } = runBuild(app.root, options)
851
+ await result
852
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
853
+ expect(lines).toContainEqual(
854
+ expect.stringContaining(`rewrote did/ledger.did from the certified`)
855
+ )
856
+ expect(read(app, "src/canisters/ledger.ts")).toContain("icrc1_transfer")
857
+ })
858
+
859
+ it("takes all, and fails a named canister that has no canisterId", async () => {
860
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
861
+ const app = newApp({ "did/ledger.did": OLD, "did/ping.did": PING_DID })
862
+ const options: IcReactorPluginOptions = {
863
+ canisters: {
864
+ ledger: {
865
+ didFile: "did/ledger.did",
866
+ canisterId: LEDGER_ID,
867
+ network: { host: ic.host },
868
+ },
869
+ ping: { didFile: "did/ping.did" },
870
+ },
871
+ }
872
+
873
+ vi.stubEnv("IC_REACTOR_FETCH", "all")
874
+ await runBuild(app.root, options).result
875
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
876
+
877
+ vi.stubEnv("IC_REACTOR_FETCH", "ping")
878
+ const message = await buildError(app, options)
879
+ expect(message).toContain(
880
+ "ping (did/ping.did): IC_REACTOR_FETCH asks to fetch its didFile again, but it names no canisterId"
881
+ )
882
+ })
883
+
884
+ // `vite build --watch` calls buildStart again on every rebuild, and Vite 6
885
+ // and later call it once for each environment: one plugin, two buildStarts.
886
+ it("is tried again at the next buildStart when it failed, and served once it succeeds", async () => {
887
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
888
+ const app = newApp({ "did/ledger.did": OLD })
889
+ // The network is down for the first buildStart only.
890
+ let down = true
891
+ const real = globalThis.fetch
892
+ vi.stubGlobal(
893
+ "fetch",
894
+ vi.fn((input: string | URL | Request, init?: RequestInit) =>
895
+ down
896
+ ? Promise.reject(new TypeError("fetch failed: the network is down"))
897
+ : real(input, init)
898
+ )
899
+ )
900
+ vi.stubEnv("IC_REACTOR_FETCH", "ledger")
901
+ const { buildStart, context, lines } = oneBuildPlugin(app, ic.host)
902
+
903
+ await buildStart()
904
+ expect(context.error).toHaveBeenCalledTimes(1)
905
+ expect(String(context.error.mock.calls[0][0])).toContain(
906
+ `ledger (did/ledger.did): could not reach ${ic.host}`
907
+ )
908
+ expect(read(app, "did/ledger.did")).toBe(OLD)
909
+ expect(ic.requests).toEqual([])
910
+
911
+ down = false
912
+ await buildStart()
913
+ expect(context.error).toHaveBeenCalledTimes(1)
914
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
915
+ expect(lines).toContainEqual(
916
+ expect.stringContaining(`rewrote did/ledger.did from the certified`)
917
+ )
918
+ expect(read(app, "src/canisters/ledger.ts")).toContain("icrc1_transfer")
919
+ expect(ic.requests).toHaveLength(2)
920
+
921
+ // Served: the next buildStart reads the file it wrote.
922
+ await buildStart()
923
+ expect(ic.requests).toHaveLength(2)
924
+ })
925
+
926
+ it("is served once when the fetch matches the file on disk", async () => {
927
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
928
+ const app = newApp({ "did/ledger.did": LEDGER })
929
+ vi.stubEnv("IC_REACTOR_FETCH", "ledger")
930
+ const { buildStart, context, lines } = oneBuildPlugin(app, ic.host)
931
+
932
+ await buildStart()
933
+ expect(context.error).not.toHaveBeenCalled()
934
+ expect(lines).toContainEqual(
935
+ expect.stringContaining(`did/ledger.did matches the certified`)
936
+ )
937
+ expect(ic.requests).toHaveLength(2)
938
+
939
+ // Served: the next buildStart does not fetch it again.
940
+ await buildStart()
941
+ expect(context.error).not.toHaveBeenCalled()
942
+ expect(ic.requests).toHaveLength(2)
943
+ })
944
+
945
+ it("is served once for a didFile that is a symlink, which it writes through", async () => {
946
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
947
+ const app = newApp({ "shared/real.did": OLD })
948
+ fs.mkdirSync(path.join(app.root, "did"))
949
+ fs.symlinkSync(
950
+ "../shared/real.did",
951
+ path.join(app.root, "did/ledger.did"),
952
+ "file"
953
+ )
954
+ vi.stubEnv("IC_REACTOR_FETCH", "ledger")
955
+ const { buildStart, context } = oneBuildPlugin(app, ic.host)
956
+ // The fetch and the write succeed. @meantjs/cli 0.5.1 then reads no
957
+ // .did through a symlink to a file (did_source_not_found), as with no
958
+ // fetch at all: that is the only error allowed.
959
+ const onlyTheCliFailed = () => {
960
+ for (const [message] of context.error.mock.calls) {
961
+ expect(String(message)).toContain(
962
+ "- ledger (did/ledger.did): did_source_not_found"
963
+ )
964
+ }
965
+ }
966
+
967
+ await buildStart()
968
+ onlyTheCliFailed()
969
+ expect(ic.requests).toHaveLength(2)
970
+ expect(
971
+ fs.lstatSync(path.join(app.root, "did/ledger.did")).isSymbolicLink()
972
+ ).toBe(true)
973
+ expect(read(app, "shared/real.did")).toBe(LEDGER)
974
+
975
+ // Served: the file's key is the same after the write as before it.
976
+ await buildStart()
977
+ onlyTheCliFailed()
978
+ expect(ic.requests).toHaveLength(2)
979
+ })
980
+
981
+ // A read-only mode on a directory does not stop a write on Windows, so the
982
+ // write this test needs to fail succeeds there.
983
+ it.skipIf(process.platform === "win32")(
984
+ "is tried again at the next buildStart when the write failed",
985
+ async () => {
986
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
987
+ const app = newApp({ "did/ledger.did": OLD })
988
+ vi.stubEnv("IC_REACTOR_FETCH", "ledger")
989
+ const { buildStart, context, lines } = oneBuildPlugin(app, ic.host)
990
+ const dir = path.join(app.root, "did")
991
+
992
+ // did/ is read-only for the first buildStart only.
993
+ fs.chmodSync(dir, 0o555)
994
+ try {
995
+ await buildStart()
996
+ } finally {
997
+ fs.chmodSync(dir, 0o755)
998
+ }
999
+ expect(context.error).toHaveBeenCalledTimes(1)
1000
+ expect(String(context.error.mock.calls[0][0])).toContain(
1001
+ `ledger (did/ledger.did): fetched the certified candid:service of ${LEDGER_ID} on ${ic.host}, but could not write it to did/ledger.did: `
1002
+ )
1003
+ expect(read(app, "did/ledger.did")).toBe(OLD)
1004
+ expect(ic.requests).toHaveLength(2)
1005
+
1006
+ await buildStart()
1007
+ expect(context.error).toHaveBeenCalledTimes(1)
1008
+ expect(read(app, "did/ledger.did")).toBe(LEDGER)
1009
+ expect(lines).toContainEqual(
1010
+ expect.stringContaining(`rewrote did/ledger.did from the certified`)
1011
+ )
1012
+ expect(ic.requests).toHaveLength(4)
1013
+ }
1014
+ )
1015
+ })
1016
+
1017
+ describe("the root key", () => {
1018
+ it("is fetched only from a local host", () => {
1019
+ const fetches = (network: Parameters<typeof resolveNetwork>[0]) => {
1020
+ const resolved = resolveNetwork(network)
1021
+ return typeof resolved === "string" ? resolved : resolved.fetchRootKey
1022
+ }
1023
+ expect(fetches(undefined)).toBe(false)
1024
+ expect(fetches("ic")).toBe(false)
1025
+ expect(fetches({ host: "https://example.com" })).toBe(false)
1026
+ expect(fetches({ host: "http://localhost.example.com" })).toBe(false)
1027
+ expect(fetches({ host: "http://notlocalhost:8000" })).toBe(false)
1028
+ expect(fetches({ host: "http://0.0.0.0:8000" })).toBe(false)
1029
+ expect(fetches({ host: "http://127.0.0.1.example.com" })).toBe(false)
1030
+ expect(fetches("local")).toBe(true)
1031
+ expect(fetches({ host: "http://localhost:4943" })).toBe(true)
1032
+ expect(fetches({ host: "http://app.localhost:8000" })).toBe(true)
1033
+ expect(fetches({ host: "http://127.1.2.3:8000" })).toBe(true)
1034
+ expect(fetches({ host: "http://[::1]:8000" })).toBe(true)
1035
+ })
1036
+
1037
+ it("of a host that is not local is mainnet's, so its own certificate fails and nothing is written", async () => {
1038
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
1039
+ const remote = "https://replica.example"
1040
+ forward(remote, ic.host)
1041
+ const app = newApp({})
1042
+ const message = await buildError(app, {
1043
+ canisters: {
1044
+ ledger: {
1045
+ didFile: "did/ledger.did",
1046
+ canisterId: LEDGER_ID,
1047
+ network: { host: remote },
1048
+ },
1049
+ },
1050
+ })
1051
+
1052
+ // Its own key is never asked for: only the certified read.
1053
+ expect(ic.requests).toEqual([
1054
+ `POST /api/v3/canister/${LEDGER_ID}/read_state`,
1055
+ ])
1056
+ expect(message).toContain(
1057
+ `ledger (did/ledger.did): the certificate ${remote} sent for the candid:service of ${LEDGER_ID} did not verify against mainnet's root key, since the root key of a host that is not local is never fetched from it (`
1058
+ )
1059
+ expect(message).toContain("or write did/ledger.did yourself")
1060
+ expect(exists(app, "did/ledger.did")).toBe(false)
1061
+ })
1062
+ })
1063
+
1064
+ describe("a certificate", () => {
1065
+ it("signed with another key than the root key fails the canister, and nothing is written", async () => {
1066
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER, forged: true } })
1067
+ const app = newApp({ "did/ping.did": PING_DID })
1068
+ const { result, lines } = runBuild(app.root, {
1069
+ failOnError: false,
1070
+ canisters: {
1071
+ ping: { didFile: "did/ping.did" },
1072
+ ledger: {
1073
+ didFile: "did/ledger.did",
1074
+ canisterId: LEDGER_ID,
1075
+ network: { host: ic.host },
1076
+ },
1077
+ },
1078
+ })
1079
+ await result
1080
+ const errors = lines.filter((line) => line.startsWith("error:"))
1081
+ expect(errors).toHaveLength(1)
1082
+ expect(errors[0]).toContain("could not generate 1 of 2 canisters")
1083
+ expect(errors[0]).toContain(
1084
+ `ledger (did/ledger.did): the certificate ${ic.host} sent for the candid:service of ${LEDGER_ID} did not verify against the root key fetched from ${ic.host} (`
1085
+ )
1086
+ expect(exists(app, "did/ledger.did")).toBe(false)
1087
+ expect(exists(app, "src/canisters/ledger.ts")).toBe(false)
1088
+ expect(exists(app, "src/canisters/ping.ts")).toBe(true)
1089
+ })
1090
+ })
1091
+
1092
+ describe("a fetched didFile that cannot be written", () => {
1093
+ it("fails its own canisters only, naming the file, and the others generate", async () => {
1094
+ const ic = await fakeIc({ [LEDGER_ID]: { candid: LEDGER } })
1095
+ // `blocked` is a file, so no directory can be made there.
1096
+ const app = newApp({ "did/ping.did": PING_DID, blocked: "" })
1097
+ const { result, lines } = runBuild(app.root, {
1098
+ failOnError: false,
1099
+ canisters: {
1100
+ ping: { didFile: "did/ping.did" },
1101
+ ledger: {
1102
+ didFile: "blocked/ledger.did",
1103
+ canisterId: LEDGER_ID,
1104
+ network: { host: ic.host },
1105
+ },
1106
+ },
1107
+ })
1108
+ await result
1109
+ const errors = lines.filter((line) => line.startsWith("error:"))
1110
+ expect(errors).toHaveLength(1)
1111
+ expect(errors[0]).toContain("could not generate 1 of 2 canisters")
1112
+ expect(errors[0]).toContain(
1113
+ `ledger (blocked/ledger.did): fetched the certified candid:service of ${LEDGER_ID} on ${ic.host}, but could not write it to blocked/ledger.did: `
1114
+ )
1115
+ expect(exists(app, "src/canisters/ping.ts")).toBe(true)
1116
+ })
1117
+ })
1118
+
1119
+ describe("fetchCandid", () => {
1120
+ const request = { canisterId: LEDGER_ID, didFile: "did/ledger.did" }
1121
+
1122
+ it("refuses a network that is not ic, local or a host, before any request", async () => {
1123
+ const asked = refuseNetwork()
1124
+ const result = await fetchCandid({
1125
+ ...request,
1126
+ network: "mainnet" as never,
1127
+ })
1128
+ expect(result).toEqual({
1129
+ ok: false,
1130
+ message: `network "mainnet" is not "ic", "local" or { host: "<http or https URL>" }`,
1131
+ })
1132
+ expect(asked).toEqual([])
1133
+ })
1134
+
1135
+ // The plugin's options refuse such a value first; a fetch still says what
1136
+ // it is rather than hand it to Principal.fromText, and never throws.
1137
+ it("refuses a canisterId that is not text, or is empty, before any request", async () => {
1138
+ const asked = refuseNetwork()
1139
+ for (const [canisterId, shown] of [
1140
+ [null, "null"],
1141
+ ["", '""'],
1142
+ [1n, "1n"],
1143
+ ] as const) {
1144
+ const result = await fetchCandid({
1145
+ ...request,
1146
+ canisterId: canisterId as never,
1147
+ network: "ic",
1148
+ })
1149
+ expect(result).toEqual({
1150
+ ok: false,
1151
+ message: `canisterId is ${shown}, not a canister ID: give the canister's principal as text, or remove canisterId`,
1152
+ })
1153
+ }
1154
+ expect(asked).toEqual([])
1155
+ })
1156
+
1157
+ it("refuses a canisterId that is not a canister ID, before any request", async () => {
1158
+ const asked = refuseNetwork()
1159
+ const result = await fetchCandid({
1160
+ ...request,
1161
+ canisterId: "not-a-canister",
1162
+ network: "ic",
1163
+ })
1164
+ expect(result.ok).toBe(false)
1165
+ expect(!result.ok && result.message).toMatch(
1166
+ /^canisterId "not-a-canister" is not a canister ID: /
1167
+ )
1168
+ expect(asked).toEqual([])
1169
+ })
1170
+
1171
+ it("gives up when the network does not answer within the deadline", async () => {
1172
+ const host = await silentHost()
1173
+ const result = await fetchCandid({
1174
+ ...request,
1175
+ network: { host },
1176
+ timeoutMs: 300,
1177
+ })
1178
+ expect(result.ok).toBe(false)
1179
+ expect(!result.ok && result.message).toBe(
1180
+ `${host} did not answer within 0.3s, so the candid:service of ${LEDGER_ID} could not be read. ` +
1181
+ "Check the connection and build again, or write did/ledger.did yourself, and the plugin reads it instead of fetching."
1182
+ )
1183
+ })
1184
+
1185
+ it("says to start the local network when local cannot be reached", async () => {
1186
+ refuseNetwork()
1187
+ const result = await fetchCandid({ ...request, network: "local" })
1188
+ expect(result.ok).toBe(false)
1189
+ expect(!result.ok && result.message).toContain(
1190
+ `could not reach http://127.0.0.1:8000 to read the candid:service of ${LEDGER_ID} on local (`
1191
+ )
1192
+ expect(!result.ok && result.message).toContain(
1193
+ "Is the local network running? Start it with `icp network start`."
1194
+ )
1195
+ })
1196
+ })