@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.
- package/.github/ISSUE_TEMPLATE/bug_report.yml +108 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +77 -0
- package/.github/actions/setup-rust-wasm/action.yml +25 -0
- package/.github/dependabot.yml +107 -0
- package/.github/workflows/auth-latest.yml +67 -0
- package/.github/workflows/b3pay-site.yml +49 -0
- package/.github/workflows/ci.yml +430 -0
- package/.github/workflows/codeql.yml +41 -0
- package/.github/workflows/dependency-review.yml +68 -0
- package/.github/workflows/docs-check.yml +98 -0
- package/.github/workflows/docs.yml +265 -0
- package/.github/workflows/e2e.yml +90 -0
- package/.github/workflows/examples-published.yml +97 -0
- package/.github/workflows/release.yml +326 -0
- package/.husky/pre-commit +12 -0
- package/.prettierignore +109 -0
- package/.prettierrc +6 -0
- package/AGENTS.md +103 -0
- package/CHANGELOG.md +3268 -0
- package/CLAUDE.md +93 -0
- package/CNAME +1 -0
- package/CODE_OF_CONDUCT.md +7 -0
- package/CONTRIBUTING.md +433 -0
- package/LICENSE +21 -0
- package/README.md +2 -2
- package/SECURITY.md +74 -0
- package/b3pay-module.json +194 -0
- package/docs/.remarkrc.json +10 -0
- package/docs/README.md +64 -0
- package/docs/astro.config.mjs +205 -0
- package/docs/favicon.svg +15 -0
- package/docs/package.json +39 -0
- package/docs/public/favicon.svg +15 -0
- package/docs/public/ic-reactor-social-card.png +0 -0
- package/docs/scripts/b3pay-static.mjs +22 -0
- package/docs/scripts/check-example-docs.mjs +214 -0
- package/docs/scripts/check-links.mjs +212 -0
- package/docs/scripts/fix-dist-links.js +125 -0
- package/docs/scripts/fix-frontmatter.js +71 -0
- package/docs/scripts/remove-redundant-h1.cjs +48 -0
- package/docs/scripts/rewrite-typedoc-links.js +110 -0
- package/docs/scripts/split-index.js +69 -0
- package/docs/src/assets/ic-reactor-hero-dark.svg +23 -0
- package/docs/src/assets/ic-reactor-hero-light.svg +23 -0
- package/docs/src/assets/ic-reactor-lockup-dark.svg +15 -0
- package/docs/src/assets/ic-reactor-lockup-light.svg +15 -0
- package/docs/src/assets/ic-reactor-mark-dark.svg +14 -0
- package/docs/src/assets/ic-reactor-mark-light.svg +14 -0
- package/docs/src/assets/layers/candid-core-layer-icon-dark.svg +4 -0
- package/docs/src/assets/layers/candid-core-layer-icon-light.svg +4 -0
- package/docs/src/assets/layers/candid-profile-layer-icon-dark.svg +4 -0
- package/docs/src/assets/layers/candid-profile-layer-icon-light.svg +4 -0
- package/docs/src/assets/layers/ic-reactor-layer-icon-dark.svg +4 -0
- package/docs/src/assets/layers/ic-reactor-layer-icon-light.svg +4 -0
- package/docs/src/components/LibraryCards.astro +172 -0
- package/docs/src/components/LibraryIcon.astro +28 -0
- package/docs/src/components/SiteTitle.astro +241 -0
- package/docs/src/content/docs/404.mdx +19 -0
- package/docs/src/content/docs/build/agent-tools.mdx +593 -0
- package/docs/src/content/docs/build/icrc1-wallet.mdx +619 -0
- package/docs/src/content/docs/build/ssr-per-user.mdx +552 -0
- package/docs/src/content/docs/examples/canister-studio.mdx +95 -0
- package/docs/src/content/docs/examples/icrc-ledger.mdx +45 -0
- package/docs/src/content/docs/examples/index.mdx +38 -0
- package/docs/src/content/docs/examples/next-ssr.mdx +73 -0
- package/docs/src/content/docs/examples/node-agent-tool.mdx +80 -0
- package/docs/src/content/docs/examples/vite-wallet.mdx +71 -0
- package/docs/src/content/docs/guides/auth.mdx +1181 -0
- package/docs/src/content/docs/guides/client.mdx +551 -0
- package/docs/src/content/docs/guides/deploy.mdx +180 -0
- package/docs/src/content/docs/guides/errors.mdx +475 -0
- package/docs/src/content/docs/guides/getting-started.mdx +620 -0
- package/docs/src/content/docs/guides/reads.mdx +586 -0
- package/docs/src/content/docs/guides/ssr.mdx +529 -0
- package/docs/src/content/docs/guides/testing.mdx +401 -0
- package/docs/src/content/docs/guides/values.mdx +261 -0
- package/docs/src/content/docs/guides/vite-plugin.mdx +690 -0
- package/docs/src/content/docs/guides/writes.mdx +572 -0
- package/docs/src/content/docs/ic-reactor/index.mdx +167 -0
- package/docs/src/content/docs/index.mdx +65 -0
- package/docs/src/content/docs/migrating-from-3.mdx +535 -0
- package/docs/src/content/docs/packages/core.mdx +186 -0
- package/docs/src/content/docs/packages/mcp.mdx +163 -0
- package/docs/src/content/docs/packages/react.mdx +207 -0
- package/docs/src/content/docs/packages/vite-plugin.mdx +73 -0
- package/docs/src/content/docs/versioning.mdx +104 -0
- package/docs/src/content.config.ts +7 -0
- package/docs/src/styles/custom.css +189 -0
- package/docs/tsconfig.typedoc.json +26 -0
- package/docs/typedoc-entry.ts +17 -0
- package/docs/typedoc.json +37 -0
- package/e2e/Cargo.lock +646 -0
- package/e2e/Cargo.toml +3 -0
- package/e2e/README.md +101 -0
- package/e2e/frontends/build.mjs +94 -0
- package/e2e/frontends/index.html +10 -0
- package/e2e/global-setup.ts +65 -0
- package/e2e/icp.yaml +38 -0
- package/e2e/package.json +40 -0
- package/e2e/setup.ts +6 -0
- package/e2e/src/actor/Cargo.toml +14 -0
- package/e2e/src/actor/hello_actor.did +10 -0
- package/e2e/src/actor/src/lib.rs +73 -0
- package/e2e/src/calls.test.ts +132 -0
- package/e2e/src/declarations/hello_actor.envelope.json +175 -0
- package/e2e/src/declarations/hello_actor.ts +15 -0
- package/e2e/src/errors.test.ts +107 -0
- package/e2e/src/frontend-headers.test.ts +253 -0
- package/e2e/src/identity.test.ts +61 -0
- package/e2e/src/react.test.tsx +59 -0
- package/e2e/src/replica.ts +70 -0
- package/e2e/src/vite-env.test.ts +108 -0
- package/e2e/test.sh +47 -0
- package/e2e/tsconfig.json +20 -0
- package/e2e/tsconfig.typecheck.json +9 -0
- package/e2e/vitest.config.ts +22 -0
- package/eslint.config.mjs +100 -0
- package/evals/PREREGISTRATION.md +1346 -0
- package/evals/README.md +756 -0
- package/evals/conditions/thin/condition.json +8 -0
- package/evals/conditions/thin/docs/candid-core-cli.README.md +90 -0
- package/evals/conditions/thin/docs/candid-core-schema.README.md +339 -0
- package/evals/conditions/thin/package.json +25 -0
- package/evals/conditions/thin/starter/src/generated/icrc1.ts +57 -0
- package/evals/conditions/thin-guide/condition.json +10 -0
- package/evals/conditions/thin-guide/docs/llms.txt +292 -0
- package/evals/conditions/v3/condition.json +9 -0
- package/evals/conditions/v3/docs/ic-reactor-core.llms.txt +232 -0
- package/evals/conditions/v3/docs/ic-reactor-react.llms.txt +280 -0
- package/evals/conditions/v3/docs/llms-full.txt +1053 -0
- package/evals/conditions/v3/package.json +26 -0
- package/evals/conditions/v3/starter/src/declarations/icrc1.did.d.ts +95 -0
- package/evals/conditions/v3/starter/src/declarations/icrc1.did.js +140 -0
- package/evals/conditions/v4/PORTING.md +384 -0
- package/evals/conditions/v4/README.md +106 -0
- package/evals/conditions/v4/condition.json +5 -0
- package/evals/conditions/v4/docs/llms.txt +323 -0
- package/evals/conditions/v4/package.json +27 -0
- package/evals/conditions/v4/starter/src/generated/icrc1.ts +78 -0
- package/evals/conditions/v4-proto/condition.json +6 -0
- package/evals/conditions/v4-proto/docs/llms.txt +291 -0
- package/evals/conditions/v4-proto/lib/SHIM-NOTES.md +20 -0
- package/evals/conditions/v4-proto/lib/package.json +27 -0
- package/evals/conditions/v4-proto/lib/src/client.ts +471 -0
- package/evals/conditions/v4-proto/lib/src/errors.ts +214 -0
- package/evals/conditions/v4-proto/lib/src/index.ts +36 -0
- package/evals/conditions/v4-proto/lib/src/principal.ts +175 -0
- package/evals/conditions/v4-proto/lib/src/service.ts +69 -0
- package/evals/conditions/v4-proto/lib/src/types.ts +174 -0
- package/evals/conditions/v4-proto/lib/test/runtime.test.ts +170 -0
- package/evals/conditions/v4-proto/lib/test/tsconfig.json +10 -0
- package/evals/conditions/v4-proto/lib/test/types.test-d.ts +145 -0
- package/evals/conditions/v4-proto/lib/test/vitest.config.mjs +8 -0
- package/evals/conditions/v4-proto/lib/tsconfig.json +17 -0
- package/evals/conditions/v4-proto/package.json +26 -0
- package/evals/conditions/v4-proto/starter/src/generated/icrc1.service.ts +19 -0
- package/evals/conditions/v4-proto/starter/src/generated/icrc1.ts +57 -0
- package/evals/drive.mjs +1221 -0
- package/evals/gate.mjs +161 -0
- package/evals/harness/aggregate.mjs +534 -0
- package/evals/harness/aggregate.test.mjs +344 -0
- package/evals/harness/assemble.mjs +216 -0
- package/evals/harness/assemble.test.mjs +143 -0
- package/evals/harness/auth.mjs +91 -0
- package/evals/harness/check-docs.mjs +157 -0
- package/evals/harness/declarations/icrc1.did.d.ts +95 -0
- package/evals/harness/declarations/icrc1.did.js +140 -0
- package/evals/harness/drive-auth.test.mjs +584 -0
- package/evals/harness/drive.test.mjs +509 -0
- package/evals/harness/fake-auth.ts +67 -0
- package/evals/harness/fake-ledger.ts +178 -0
- package/evals/harness/gate-plan.mjs +104 -0
- package/evals/harness/gate-plan.test.mjs +243 -0
- package/evals/harness/icrc1.did +88 -0
- package/evals/harness/judge.mjs +63 -0
- package/evals/harness/judge.test.mjs +81 -0
- package/evals/harness/leak-scan.mjs +1373 -0
- package/evals/harness/leak-scan.test.mjs +1300 -0
- package/evals/harness/probes/amounts.ts +14 -0
- package/evals/harness/probes/node-tool.probe.ts +48 -0
- package/evals/harness/probes/probe.mjs +149 -0
- package/evals/harness/probes/react-wallet.probe.ts +96 -0
- package/evals/harness/public/support/auth.ts +38 -0
- package/evals/harness/public/support/fake-replica.d.ts +28 -0
- package/evals/harness/public/support/fake-replica.js +434 -0
- package/evals/harness/public/support/ledger.idl.d.ts +95 -0
- package/evals/harness/public/support/ledger.idl.js +140 -0
- package/evals/harness/public/support/world.ts +179 -0
- package/evals/harness/runs.mjs +139 -0
- package/evals/harness/sandbox.mjs +88 -0
- package/evals/harness/sandbox.test.mjs +134 -0
- package/evals/harness/ship.mjs +264 -0
- package/evals/harness/ship.test.mjs +552 -0
- package/evals/harness/world.ts +291 -0
- package/evals/package.json +26 -0
- package/evals/pnpm-lock.yaml +1537 -0
- package/evals/pnpm-workspace.yaml +12 -0
- package/evals/results/2026-09-30T14-22-05-924Z/pilot.json +384 -0
- package/evals/results/2026-09-30T14-22-05-924Z/pilot.rescanned.json +404 -0
- package/evals/results/2026-09-30T14-22-05-924Z/pilot.rescanned.rescored.json +404 -0
- package/evals/results/2026-09-30T14-22-05-924Z/plan.json +240 -0
- package/evals/results/2026-09-30T14-22-05-924Z/summary.json +1800 -0
- package/evals/results/2026-09-30T14-22-05-924Z/summary.rescanned.json +1843 -0
- package/evals/results/2026-09-30T14-22-05-924Z/summary.rescanned.rescored.json +2100 -0
- package/evals/results/2026-09-30T15-48-23-343Z/pilot.json +404 -0
- package/evals/results/2026-09-30T15-48-23-343Z/pilot.rescored.json +404 -0
- package/evals/results/2026-09-30T15-48-23-343Z/plan.json +242 -0
- package/evals/results/2026-09-30T15-48-23-343Z/summary.json +1843 -0
- package/evals/results/2026-09-30T15-48-23-343Z/summary.rescored.json +2195 -0
- package/evals/results/2026-10-03-beta1-gate/pilot.json +162 -0
- package/evals/results/2026-10-03-beta1-gate/plan.json +141 -0
- package/evals/results/2026-10-03-beta1-gate/summary.json +983 -0
- package/evals/results/2026-10-05-ga-gate/contaminated-run.json +55 -0
- package/evals/results/2026-10-05-ga-gate/pilot.json +162 -0
- package/evals/results/2026-10-05-ga-gate/plan.json +457 -0
- package/evals/results/2026-10-05-ga-gate/summary.json +1037 -0
- package/evals/results/2026-10-05-ga-gate/summary.rescanned.json +1037 -0
- package/evals/results/README.md +31 -0
- package/evals/runs-pilot.log +150 -0
- package/evals/runs-pilot2.log +150 -0
- package/evals/score.mjs +256 -0
- package/evals/setup.mjs +547 -0
- package/evals/tasks/node-tool/faulty/thin-accepts-exponent/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-accepts-exponent/src/index.ts +121 -0
- package/evals/tasks/node-tool/faulty/thin-anonymous-transfer/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-anonymous-transfer/src/index.ts +116 -0
- package/evals/tasks/node-tool/faulty/thin-every-reject-may-have-executed/meta.json +9 -0
- package/evals/tasks/node-tool/faulty/thin-every-reject-may-have-executed/src/index.ts +118 -0
- package/evals/tasks/node-tool/faulty/thin-fetches-root-key-off-local/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-fetches-root-key-off-local/src/index.ts +118 -0
- package/evals/tasks/node-tool/faulty/thin-ignores-root-key/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-ignores-root-key/src/index.ts +117 -0
- package/evals/tasks/node-tool/faulty/thin-misclassifies-reject-codes/meta.json +9 -0
- package/evals/tasks/node-tool/faulty/thin-misclassifies-reject-codes/src/index.ts +119 -0
- package/evals/tasks/node-tool/faulty/thin-no-nat64-cap/meta.json +9 -0
- package/evals/tasks/node-tool/faulty/thin-no-nat64-cap/src/index.ts +118 -0
- package/evals/tasks/node-tool/faulty/thin-number-amount/meta.json +10 -0
- package/evals/tasks/node-tool/faulty/thin-number-amount/src/index.ts +117 -0
- package/evals/tasks/node-tool/faulty/thin-truncates-fraction-digits/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-truncates-fraction-digits/src/index.ts +120 -0
- package/evals/tasks/node-tool/faulty/thin-unvalidated-recipient/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-unvalidated-recipient/src/index.ts +118 -0
- package/evals/tasks/node-tool/faulty/v3-retry-call-errors/meta.json +11 -0
- package/evals/tasks/node-tool/faulty/v3-retry-call-errors/src/index.ts +149 -0
- package/evals/tasks/node-tool/faulty/v4-anonymous-identity-sent/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/v4-anonymous-identity-sent/src/index.ts +71 -0
- package/evals/tasks/node-tool/faulty/v4-never-may-have-executed/meta.json +9 -0
- package/evals/tasks/node-tool/faulty/v4-never-may-have-executed/src/index.ts +84 -0
- package/evals/tasks/node-tool/faulty/v4-proto-never-may-have-executed/meta.json +9 -0
- package/evals/tasks/node-tool/faulty/v4-proto-never-may-have-executed/src/index.ts +72 -0
- package/evals/tasks/node-tool/faulty/v4-proto-refuses-nat64-max/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/v4-proto-refuses-nat64-max/src/index.ts +72 -0
- package/evals/tasks/node-tool/faulty/v4-refuses-nat64-max/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/v4-refuses-nat64-max/src/index.ts +84 -0
- package/evals/tasks/node-tool/hidden/node-tool.test.ts +312 -0
- package/evals/tasks/node-tool/prompt.md +55 -0
- package/evals/tasks/node-tool/prompt.minimal.md +30 -0
- package/evals/tasks/node-tool/solutions/thin/reference/src/index.ts +118 -0
- package/evals/tasks/node-tool/solutions/thin/reference-module-scope/src/index.ts +145 -0
- package/evals/tasks/node-tool/solutions/thin-guide/reference/src/index.ts +118 -0
- package/evals/tasks/node-tool/solutions/thin-guide/reference-module-scope/src/index.ts +145 -0
- package/evals/tasks/node-tool/solutions/v3/reference/src/index.ts +140 -0
- package/evals/tasks/node-tool/solutions/v3/reference-module-scope/src/index.ts +153 -0
- package/evals/tasks/node-tool/solutions/v4/reference/src/index.ts +84 -0
- package/evals/tasks/node-tool/solutions/v4/reference-module-scope/src/index.ts +105 -0
- package/evals/tasks/node-tool/solutions/v4-proto/reference/src/index.ts +72 -0
- package/evals/tasks/node-tool/solutions/v4-proto/reference-module-scope/src/index.ts +87 -0
- package/evals/tasks/node-tool/starter/src/contract.ts +31 -0
- package/evals/tasks/node-tool/starter/src/index.ts +7 -0
- package/evals/tasks/node-tool/starter/test/smoke.test.ts +22 -0
- package/evals/tasks/node-tool/starter/tsconfig.json +17 -0
- package/evals/tasks/node-tool/task.json +63 -0
- package/evals/tasks/react-wallet/faulty/thin-accepts-exponent/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-accepts-exponent/src/Wallet.tsx +216 -0
- package/evals/tasks/react-wallet/faulty/thin-anonymous-transfer/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-anonymous-transfer/src/Wallet.tsx +212 -0
- package/evals/tasks/react-wallet/faulty/thin-both-auth-buttons/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-both-auth-buttons/src/Wallet.tsx +210 -0
- package/evals/tasks/react-wallet/faulty/thin-constant-balance-key/meta.json +8 -0
- package/evals/tasks/react-wallet/faulty/thin-constant-balance-key/src/Wallet.tsx +213 -0
- package/evals/tasks/react-wallet/faulty/thin-no-nat64-cap/meta.json +9 -0
- package/evals/tasks/react-wallet/faulty/thin-no-nat64-cap/src/Wallet.tsx +213 -0
- package/evals/tasks/react-wallet/faulty/thin-number-balance/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-number-balance/src/Wallet.tsx +213 -0
- package/evals/tasks/react-wallet/faulty/thin-transfer-as-query/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-transfer-as-query/src/Wallet.tsx +254 -0
- package/evals/tasks/react-wallet/faulty/thin-truncates-fraction-digits/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-truncates-fraction-digits/src/Wallet.tsx +215 -0
- package/evals/tasks/react-wallet/faulty/thin-unguarded-balance-effect/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-unguarded-balance-effect/src/Wallet.tsx +214 -0
- package/evals/tasks/react-wallet/faulty/v3-http-4xx-unknown/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v3-http-4xx-unknown/src/Wallet.tsx +224 -0
- package/evals/tasks/react-wallet/faulty/v3-mutation-retry-3/meta.json +12 -0
- package/evals/tasks/react-wallet/faulty/v3-mutation-retry-3/src/Wallet.tsx +225 -0
- package/evals/tasks/react-wallet/faulty/v3-unknown-as-error/meta.json +11 -0
- package/evals/tasks/react-wallet/faulty/v3-unknown-as-error/src/Wallet.tsx +182 -0
- package/evals/tasks/react-wallet/faulty/v4-every-reject-unknown/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-every-reject-unknown/src/Wallet.tsx +160 -0
- package/evals/tasks/react-wallet/faulty/v4-keep-previous-data/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-keep-previous-data/src/Wallet.tsx +166 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-every-reject-unknown/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-every-reject-unknown/src/Wallet.tsx +161 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-keep-previous-data/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-keep-previous-data/src/Wallet.tsx +163 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-retry-spread/meta.json +12 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-retry-spread/src/Wallet.tsx +162 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-status-not-idle/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-status-not-idle/src/Wallet.tsx +163 -0
- package/evals/tasks/react-wallet/faulty/v4-retry-spread/meta.json +12 -0
- package/evals/tasks/react-wallet/faulty/v4-retry-spread/src/Wallet.tsx +163 -0
- package/evals/tasks/react-wallet/faulty/v4-status-not-idle/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-status-not-idle/src/Wallet.tsx +162 -0
- package/evals/tasks/react-wallet/hidden/react-wallet.test.ts +536 -0
- package/evals/tasks/react-wallet/prompt.md +88 -0
- package/evals/tasks/react-wallet/prompt.minimal.md +51 -0
- package/evals/tasks/react-wallet/solutions/thin/reference/src/Wallet.tsx +213 -0
- package/evals/tasks/react-wallet/solutions/thin/reference-module-scope/src/Wallet.tsx +227 -0
- package/evals/tasks/react-wallet/solutions/thin-guide/reference/src/Wallet.tsx +213 -0
- package/evals/tasks/react-wallet/solutions/thin-guide/reference-module-scope/src/Wallet.tsx +227 -0
- package/evals/tasks/react-wallet/solutions/v3/reference/src/Wallet.tsx +224 -0
- package/evals/tasks/react-wallet/solutions/v3/reference-module-scope/src/Wallet.tsx +230 -0
- package/evals/tasks/react-wallet/solutions/v4/reference/src/Wallet.tsx +160 -0
- package/evals/tasks/react-wallet/solutions/v4/reference-module-scope/src/Wallet.tsx +178 -0
- package/evals/tasks/react-wallet/solutions/v4-proto/reference/src/Wallet.tsx +161 -0
- package/evals/tasks/react-wallet/solutions/v4-proto/reference-module-scope/src/Wallet.tsx +161 -0
- package/evals/tasks/react-wallet/starter/src/Wallet.tsx +12 -0
- package/evals/tasks/react-wallet/starter/src/auth.ts +16 -0
- package/evals/tasks/react-wallet/starter/src/config.ts +14 -0
- package/evals/tasks/react-wallet/starter/src/ii-auth.ts +43 -0
- package/evals/tasks/react-wallet/starter/src/main.tsx +15 -0
- package/evals/tasks/react-wallet/starter/test/smoke.test.ts +34 -0
- package/evals/tasks/react-wallet/starter/tsconfig.json +18 -0
- package/evals/tasks/react-wallet/task.json +84 -0
- package/examples/canister-studio/.stackblitzrc +4 -0
- package/examples/canister-studio/README.md +183 -0
- package/examples/canister-studio/did/ckbtc_minter.did +806 -0
- package/examples/canister-studio/did/icp_ledger.did +586 -0
- package/examples/canister-studio/did/icrc1.did +88 -0
- package/examples/canister-studio/package.json +45 -0
- package/examples/canister-studio/scripts/smoke.ts +188 -0
- package/examples/canister-studio/src/calls/CacheKeyPanel.tsx +104 -0
- package/examples/canister-studio/src/calls/ConfirmWrite.tsx +265 -0
- package/examples/canister-studio/src/calls/ErrorPanel.test.tsx +113 -0
- package/examples/canister-studio/src/calls/ErrorPanel.tsx +214 -0
- package/examples/canister-studio/src/calls/MethodRunner.test.tsx +354 -0
- package/examples/canister-studio/src/calls/MethodRunner.tsx +57 -0
- package/examples/canister-studio/src/calls/ReadRunner.tsx +244 -0
- package/examples/canister-studio/src/calls/ResendPanel.tsx +165 -0
- package/examples/canister-studio/src/calls/ShareLink.tsx +59 -0
- package/examples/canister-studio/src/calls/WriteRunner.test.tsx +678 -0
- package/examples/canister-studio/src/calls/WriteRunner.tsx +670 -0
- package/examples/canister-studio/src/calls/dedupe.ts +189 -0
- package/examples/canister-studio/src/calls/error-info.ts +168 -0
- package/examples/canister-studio/src/calls/history.test.ts +284 -0
- package/examples/canister-studio/src/calls/history.ts +287 -0
- package/examples/canister-studio/src/calls/kinds.ts +68 -0
- package/examples/canister-studio/src/calls/prepare.ts +32 -0
- package/examples/canister-studio/src/calls/query-options.ts +30 -0
- package/examples/canister-studio/src/calls/read.ts +47 -0
- package/examples/canister-studio/src/calls/readback.ts +100 -0
- package/examples/canister-studio/src/calls/record.ts +123 -0
- package/examples/canister-studio/src/calls/safe-calls.test.ts +299 -0
- package/examples/canister-studio/src/calls/share.ts +46 -0
- package/examples/canister-studio/src/calls/testing/doubles.tsx +108 -0
- package/examples/canister-studio/src/calls/testing/fixtures.ts +185 -0
- package/examples/canister-studio/src/calls/useCanisterHandle.ts +35 -0
- package/examples/canister-studio/src/canisters/ckbtc_minter.envelope.json +4228 -0
- package/examples/canister-studio/src/canisters/ckbtc_minter.ts +868 -0
- package/examples/canister-studio/src/canisters/icp_ledger.envelope.json +3454 -0
- package/examples/canister-studio/src/canisters/icp_ledger.ts +584 -0
- package/examples/canister-studio/src/canisters/icrc1.envelope.json +840 -0
- package/examples/canister-studio/src/canisters/icrc1.ts +78 -0
- package/examples/canister-studio/src/components/ActionButton.tsx +32 -0
- package/examples/canister-studio/src/components/AppHeader.tsx +83 -0
- package/examples/canister-studio/src/components/AppShell.tsx +29 -0
- package/examples/canister-studio/src/components/AuthButton.tsx +79 -0
- package/examples/canister-studio/src/components/Badge.tsx +79 -0
- package/examples/canister-studio/src/components/CanisterHeader.tsx +200 -0
- package/examples/canister-studio/src/components/CanisterJump.tsx +130 -0
- package/examples/canister-studio/src/components/Card.tsx +64 -0
- package/examples/canister-studio/src/components/Code.tsx +101 -0
- package/examples/canister-studio/src/components/CopyButton.tsx +126 -0
- package/examples/canister-studio/src/components/Disclosure.tsx +34 -0
- package/examples/canister-studio/src/components/EmptyState.tsx +34 -0
- package/examples/canister-studio/src/components/MethodList.test.tsx +137 -0
- package/examples/canister-studio/src/components/MethodList.tsx +244 -0
- package/examples/canister-studio/src/components/Notice.tsx +36 -0
- package/examples/canister-studio/src/components/ProvenanceBadge.tsx +44 -0
- package/examples/canister-studio/src/components/RouteError.tsx +42 -0
- package/examples/canister-studio/src/components/SectionBoundary.tsx +76 -0
- package/examples/canister-studio/src/components/Spinner.tsx +33 -0
- package/examples/canister-studio/src/components/Tabs.tsx +96 -0
- package/examples/canister-studio/src/components/canister/CanisterLoadFailure.test.tsx +56 -0
- package/examples/canister-studio/src/components/canister/CanisterLoadFailure.tsx +165 -0
- package/examples/canister-studio/src/components/canister/CanisterNotFound.tsx +59 -0
- package/examples/canister-studio/src/components/canister/CanisterPending.tsx +33 -0
- package/examples/canister-studio/src/components/canister/MethodOverview.tsx +93 -0
- package/examples/canister-studio/src/components/canister/MethodWorkspace.test.tsx +201 -0
- package/examples/canister-studio/src/components/canister/MethodWorkspace.tsx +187 -0
- package/examples/canister-studio/src/components/canister/shared-call.test.ts +186 -0
- package/examples/canister-studio/src/components/canister/shared-call.ts +133 -0
- package/examples/canister-studio/src/components/canister/shared-link-loader.test.ts +116 -0
- package/examples/canister-studio/src/components/canister/use-canister-page.ts +44 -0
- package/examples/canister-studio/src/components/canister/workspace-key.test.ts +27 -0
- package/examples/canister-studio/src/components/canister/workspace-key.ts +36 -0
- package/examples/canister-studio/src/components/canister-id-input.ts +69 -0
- package/examples/canister-studio/src/components/canister-names.ts +11 -0
- package/examples/canister-studio/src/components/download.ts +34 -0
- package/examples/canister-studio/src/components/fallbacks.tsx +33 -0
- package/examples/canister-studio/src/components/helpers.test.ts +107 -0
- package/examples/canister-studio/src/components/history/HistoryList.tsx +284 -0
- package/examples/canister-studio/src/components/home/FeaturedLedger.tsx +149 -0
- package/examples/canister-studio/src/components/home/OpenCanister.tsx +99 -0
- package/examples/canister-studio/src/components/home/featured.test.tsx +92 -0
- package/examples/canister-studio/src/components/home/featured.ts +64 -0
- package/examples/canister-studio/src/components/interface/CandidTab.tsx +38 -0
- package/examples/canister-studio/src/components/interface/CompatReport.tsx +92 -0
- package/examples/canister-studio/src/components/interface/CompatibilityTab.tsx +190 -0
- package/examples/canister-studio/src/components/interface/ProjectTab.tsx +196 -0
- package/examples/canister-studio/src/components/interface/TypeScriptTab.tsx +114 -0
- package/examples/canister-studio/src/components/interface/tools.test.tsx +169 -0
- package/examples/canister-studio/src/components/modes.ts +31 -0
- package/examples/canister-studio/src/components/playground/MethodPicker.tsx +67 -0
- package/examples/canister-studio/src/components/playground/PlaygroundForms.tsx +112 -0
- package/examples/canister-studio/src/components/playground/PlaygroundTools.tsx +219 -0
- package/examples/canister-studio/src/components/playground/sample.ts +43 -0
- package/examples/canister-studio/src/components/playground/target.test.tsx +86 -0
- package/examples/canister-studio/src/components/primitives.test.tsx +121 -0
- package/examples/canister-studio/src/components/provenance.ts +38 -0
- package/examples/canister-studio/src/components/route-render.test.tsx +71 -0
- package/examples/canister-studio/src/components/use-task.ts +56 -0
- package/examples/canister-studio/src/components/wallet/BalancePanel.tsx +110 -0
- package/examples/canister-studio/src/components/wallet/LedgerSummary.tsx +63 -0
- package/examples/canister-studio/src/components/wallet/MinterPanel.tsx +255 -0
- package/examples/canister-studio/src/components/wallet/SendPanel.test.tsx +297 -0
- package/examples/canister-studio/src/components/wallet/SendPanel.tsx +446 -0
- package/examples/canister-studio/src/components/wallet/ledgers.test.ts +36 -0
- package/examples/canister-studio/src/components/wallet/ledgers.ts +111 -0
- package/examples/canister-studio/src/forms/JsonPanel.tsx +157 -0
- package/examples/canister-studio/src/forms/MethodForm.test.tsx +369 -0
- package/examples/canister-studio/src/forms/MethodForm.tsx +368 -0
- package/examples/canister-studio/src/forms/TypedForm.tsx +55 -0
- package/examples/canister-studio/src/forms/context.ts +62 -0
- package/examples/canister-studio/src/forms/convert.ts +131 -0
- package/examples/canister-studio/src/forms/edge.test.ts +252 -0
- package/examples/canister-studio/src/forms/engine.test.ts +358 -0
- package/examples/canister-studio/src/forms/engine.ts +1492 -0
- package/examples/canister-studio/src/forms/fields/BytesField.tsx +210 -0
- package/examples/canister-studio/src/forms/fields/Field.tsx +106 -0
- package/examples/canister-studio/src/forms/fields/FieldShell.tsx +101 -0
- package/examples/canister-studio/src/forms/fields/Group.tsx +74 -0
- package/examples/canister-studio/src/forms/fields/ListField.tsx +256 -0
- package/examples/canister-studio/src/forms/fields/Segmented.tsx +78 -0
- package/examples/canister-studio/src/forms/fields/TimestampField.tsx +108 -0
- package/examples/canister-studio/src/forms/fields/UnitsField.tsx +119 -0
- package/examples/canister-studio/src/forms/fields/composites.tsx +389 -0
- package/examples/canister-studio/src/forms/fields/leaves.tsx +350 -0
- package/examples/canister-studio/src/forms/fields.test.tsx +396 -0
- package/examples/canister-studio/src/forms/helpers.test.ts +225 -0
- package/examples/canister-studio/src/forms/hints.ts +128 -0
- package/examples/canister-studio/src/forms/json.ts +119 -0
- package/examples/canister-studio/src/forms/parsing.test.ts +217 -0
- package/examples/canister-studio/src/forms/useMethodForm.ts +251 -0
- package/examples/canister-studio/src/interface/browser.test.ts +23 -0
- package/examples/canister-studio/src/interface/browser.ts +153 -0
- package/examples/canister-studio/src/interface/cache.ts +68 -0
- package/examples/canister-studio/src/interface/cli-cache.test.ts +155 -0
- package/examples/canister-studio/src/interface/cli-cache.ts +125 -0
- package/examples/canister-studio/src/interface/compile.server.test.ts +198 -0
- package/examples/canister-studio/src/interface/compile.server.ts +262 -0
- package/examples/canister-studio/src/interface/compile.test.ts +108 -0
- package/examples/canister-studio/src/interface/compile.ts +305 -0
- package/examples/canister-studio/src/interface/docs.test.ts +75 -0
- package/examples/canister-studio/src/interface/docs.ts +100 -0
- package/examples/canister-studio/src/interface/functions.test.ts +42 -0
- package/examples/canister-studio/src/interface/functions.ts +200 -0
- package/examples/canister-studio/src/interface/live-candid.server.ts +91 -0
- package/examples/canister-studio/src/interface/live-loader.test.ts +515 -0
- package/examples/canister-studio/src/interface/live-loader.ts +364 -0
- package/examples/canister-studio/src/interface/load.test.ts +328 -0
- package/examples/canister-studio/src/interface/load.ts +249 -0
- package/examples/canister-studio/src/interface/read-candid.test.ts +185 -0
- package/examples/canister-studio/src/interface/read-candid.ts +233 -0
- package/examples/canister-studio/src/interface/server-result.ts +42 -0
- package/examples/canister-studio/src/interface/typed.ts +68 -0
- package/examples/canister-studio/src/interface/types.ts +121 -0
- package/examples/canister-studio/src/lib/format.test.ts +107 -0
- package/examples/canister-studio/src/lib/format.ts +139 -0
- package/examples/canister-studio/src/lib/hex.test.ts +43 -0
- package/examples/canister-studio/src/lib/hex.ts +33 -0
- package/examples/canister-studio/src/lib/ids.test.ts +70 -0
- package/examples/canister-studio/src/lib/ids.ts +93 -0
- package/examples/canister-studio/src/lib/reactor.test.ts +55 -0
- package/examples/canister-studio/src/lib/reactor.ts +55 -0
- package/examples/canister-studio/src/lib/registry.test.ts +74 -0
- package/examples/canister-studio/src/lib/registry.ts +238 -0
- package/examples/canister-studio/src/lib/schema.ts +148 -0
- package/examples/canister-studio/src/routeTree.gen.ts +227 -0
- package/examples/canister-studio/src/router.test.ts +290 -0
- package/examples/canister-studio/src/router.tsx +127 -0
- package/examples/canister-studio/src/routes/__root.tsx +58 -0
- package/examples/canister-studio/src/routes/canister.$canisterId.index.tsx +172 -0
- package/examples/canister-studio/src/routes/canister.$canisterId.interface.tsx +98 -0
- package/examples/canister-studio/src/routes/canister.$canisterId.tsx +91 -0
- package/examples/canister-studio/src/routes/history.tsx +55 -0
- package/examples/canister-studio/src/routes/index.tsx +169 -0
- package/examples/canister-studio/src/routes/playground.tsx +286 -0
- package/examples/canister-studio/src/routes/safety.tsx +57 -0
- package/examples/canister-studio/src/routes/wallet.tsx +213 -0
- package/examples/canister-studio/src/safety/LabTransferForm.tsx +84 -0
- package/examples/canister-studio/src/safety/RequestLog.tsx +79 -0
- package/examples/canister-studio/src/safety/SafetyLab.test.tsx +89 -0
- package/examples/canister-studio/src/safety/SafetyLab.tsx +140 -0
- package/examples/canister-studio/src/safety/ScenarioCard.tsx +262 -0
- package/examples/canister-studio/src/safety/lab-run.ts +283 -0
- package/examples/canister-studio/src/safety/mock-ledger.test.ts +99 -0
- package/examples/canister-studio/src/safety/mock-ledger.ts +263 -0
- package/examples/canister-studio/src/safety/scenarios.test.ts +157 -0
- package/examples/canister-studio/src/safety/scenarios.ts +924 -0
- package/examples/canister-studio/src/styles.css +3453 -0
- package/examples/canister-studio/src/styles.test.ts +46 -0
- package/examples/canister-studio/src/values/CallPreview.test.tsx +86 -0
- package/examples/canister-studio/src/values/CallPreview.tsx +148 -0
- package/examples/canister-studio/src/values/ErrValueView.tsx +66 -0
- package/examples/canister-studio/src/values/Inspector.tsx +133 -0
- package/examples/canister-studio/src/values/ReplyView.test.tsx +341 -0
- package/examples/canister-studio/src/values/ReplyView.tsx +164 -0
- package/examples/canister-studio/src/values/candid-text.test.ts +516 -0
- package/examples/canister-studio/src/values/candid-text.ts +479 -0
- package/examples/canister-studio/src/values/hints.ts +58 -0
- package/examples/canister-studio/src/values/json.test.ts +66 -0
- package/examples/canister-studio/src/values/json.ts +31 -0
- package/examples/canister-studio/src/values/leaves.tsx +309 -0
- package/examples/canister-studio/src/values/schema.test.ts +123 -0
- package/examples/canister-studio/src/values/schema.ts +78 -0
- package/examples/canister-studio/src/values/test-router.tsx +21 -0
- package/examples/canister-studio/src/values/tree.tsx +670 -0
- package/examples/canister-studio/src/values/use-sha256.ts +29 -0
- package/examples/canister-studio/tsconfig.json +20 -0
- package/examples/canister-studio/vite.config.ts +50 -0
- package/examples/canister-studio/vitest.config.ts +20 -0
- package/examples/icrc-ledger/.stackblitzrc +4 -0
- package/examples/icrc-ledger/README.md +100 -0
- package/examples/icrc-ledger/ckbtc_minter.did +12 -0
- package/examples/icrc-ledger/icp_ledger.did +147 -0
- package/examples/icrc-ledger/icrc1.did +88 -0
- package/examples/icrc-ledger/index.html +13 -0
- package/examples/icrc-ledger/package.json +40 -0
- package/examples/icrc-ledger/scripts/smoke.ts +151 -0
- package/examples/icrc-ledger/src/App.tsx +71 -0
- package/examples/icrc-ledger/src/ErrorPanel.tsx +105 -0
- package/examples/icrc-ledger/src/MainnetTab.tsx +450 -0
- package/examples/icrc-ledger/src/SandboxTab.test.tsx +379 -0
- package/examples/icrc-ledger/src/SandboxTab.tsx +761 -0
- package/examples/icrc-ledger/src/blocks.test.ts +247 -0
- package/examples/icrc-ledger/src/blocks.ts +140 -0
- package/examples/icrc-ledger/src/canisters/ckbtc_minter.envelope.json +96 -0
- package/examples/icrc-ledger/src/canisters/ckbtc_minter.ts +16 -0
- package/examples/icrc-ledger/src/canisters/icp_ledger.envelope.json +690 -0
- package/examples/icrc-ledger/src/canisters/icp_ledger.ts +223 -0
- package/examples/icrc-ledger/src/canisters/icrc1.envelope.json +840 -0
- package/examples/icrc-ledger/src/canisters/icrc1.ts +78 -0
- package/examples/icrc-ledger/src/format.test.ts +41 -0
- package/examples/icrc-ledger/src/format.ts +68 -0
- package/examples/icrc-ledger/src/ledger.ts +15 -0
- package/examples/icrc-ledger/src/main.tsx +10 -0
- package/examples/icrc-ledger/src/minter.ts +16 -0
- package/examples/icrc-ledger/src/sandbox.test.ts +464 -0
- package/examples/icrc-ledger/src/sandbox.ts +281 -0
- package/examples/icrc-ledger/src/styles.css +391 -0
- package/examples/icrc-ledger/src/transfer-form.test.ts +62 -0
- package/examples/icrc-ledger/src/transfer-form.ts +97 -0
- package/examples/icrc-ledger/tsconfig.json +19 -0
- package/examples/icrc-ledger/vite.config.ts +44 -0
- package/examples/icrc-ledger/vitest.config.ts +12 -0
- package/examples/next-ssr/.stackblitzrc +4 -0
- package/examples/next-ssr/README.md +112 -0
- package/examples/next-ssr/icrc1.did +88 -0
- package/examples/next-ssr/next.config.ts +10 -0
- package/examples/next-ssr/package.json +41 -0
- package/examples/next-ssr/scripts/smoke.ts +169 -0
- package/examples/next-ssr/src/app/account/CertifiedBalances.tsx +58 -0
- package/examples/next-ssr/src/app/account/account.test.tsx +165 -0
- package/examples/next-ssr/src/app/account/page.tsx +82 -0
- package/examples/next-ssr/src/app/api/balance/[ledger]/[principal]/route.ts +10 -0
- package/examples/next-ssr/src/app/globals.css +278 -0
- package/examples/next-ssr/src/app/layout.tsx +34 -0
- package/examples/next-ssr/src/app/page.tsx +46 -0
- package/examples/next-ssr/src/app/providers.browser.test.tsx +127 -0
- package/examples/next-ssr/src/app/providers.test.tsx +73 -0
- package/examples/next-ssr/src/app/providers.tsx +60 -0
- package/examples/next-ssr/src/canisters/icrc1.envelope.json +840 -0
- package/examples/next-ssr/src/canisters/icrc1.ts +78 -0
- package/examples/next-ssr/src/components/BalanceTable.tsx +43 -0
- package/examples/next-ssr/src/components/LedgerCard.test.tsx +96 -0
- package/examples/next-ssr/src/components/LedgerCard.tsx +230 -0
- package/examples/next-ssr/src/components/LedgerError.tsx +32 -0
- package/examples/next-ssr/src/components/LedgerSections.tsx +22 -0
- package/examples/next-ssr/src/components/MyBalances.test.tsx +253 -0
- package/examples/next-ssr/src/components/MyBalances.tsx +129 -0
- package/examples/next-ssr/src/components/SessionBadge.tsx +18 -0
- package/examples/next-ssr/src/components/hydration.test.tsx +259 -0
- package/examples/next-ssr/src/components/server-data.ts +10 -0
- package/examples/next-ssr/src/format.ts +46 -0
- package/examples/next-ssr/src/ledgers.ts +38 -0
- package/examples/next-ssr/src/server/balance-route.test.ts +151 -0
- package/examples/next-ssr/src/server/balance-route.ts +157 -0
- package/examples/next-ssr/src/server/error-summary.test.ts +34 -0
- package/examples/next-ssr/src/server/error-summary.ts +37 -0
- package/examples/next-ssr/src/server/parse-owner.test.ts +39 -0
- package/examples/next-ssr/src/server/parse-owner.ts +34 -0
- package/examples/next-ssr/src/server/prefetch-ledgers.test.ts +137 -0
- package/examples/next-ssr/src/server/prefetch-ledgers.ts +70 -0
- package/examples/next-ssr/src/server/read-balances.test.ts +103 -0
- package/examples/next-ssr/src/server/read-balances.ts +78 -0
- package/examples/next-ssr/src/server/request-client.rsc.test.tsx +110 -0
- package/examples/next-ssr/src/server/request-client.ts +24 -0
- package/examples/next-ssr/src/testing/mock-ledgers.ts +115 -0
- package/examples/next-ssr/src/testing/react-server-dom-turbopack.d.ts +15 -0
- package/examples/next-ssr/src/testing/setup.ts +10 -0
- package/examples/next-ssr/tsconfig.json +31 -0
- package/examples/next-ssr/vitest.config.ts +59 -0
- package/examples/node-agent-tool/.stackblitzrc +4 -0
- package/examples/node-agent-tool/README.md +165 -0
- package/examples/node-agent-tool/icp.yaml +11 -0
- package/examples/node-agent-tool/icrc1.did +88 -0
- package/examples/node-agent-tool/package.json +32 -0
- package/examples/node-agent-tool/src/canisters/icrc1.envelope.json +840 -0
- package/examples/node-agent-tool/src/canisters/icrc1.ts +78 -0
- package/examples/node-agent-tool/src/cli.test.ts +64 -0
- package/examples/node-agent-tool/src/cli.ts +23 -0
- package/examples/node-agent-tool/src/command-line.ts +159 -0
- package/examples/node-agent-tool/src/commands/balance.ts +73 -0
- package/examples/node-agent-tool/src/commands/demo.test.ts +51 -0
- package/examples/node-agent-tool/src/commands/demo.ts +359 -0
- package/examples/node-agent-tool/src/commands/info.ts +69 -0
- package/examples/node-agent-tool/src/commands/reads.test.ts +133 -0
- package/examples/node-agent-tool/src/commands/resend.test.ts +363 -0
- package/examples/node-agent-tool/src/commands/transfer.test.ts +313 -0
- package/examples/node-agent-tool/src/commands/transfer.ts +415 -0
- package/examples/node-agent-tool/src/commands/watch.test.ts +159 -0
- package/examples/node-agent-tool/src/commands/watch.ts +135 -0
- package/examples/node-agent-tool/src/commands/whoami.ts +36 -0
- package/examples/node-agent-tool/src/context.ts +20 -0
- package/examples/node-agent-tool/src/dispatch.ts +70 -0
- package/examples/node-agent-tool/src/failure.test.ts +59 -0
- package/examples/node-agent-tool/src/failure.ts +176 -0
- package/examples/node-agent-tool/src/identity.test.ts +242 -0
- package/examples/node-agent-tool/src/identity.ts +228 -0
- package/examples/node-agent-tool/src/input.ts +110 -0
- package/examples/node-agent-tool/src/ledgers.test.ts +74 -0
- package/examples/node-agent-tool/src/ledgers.ts +62 -0
- package/examples/node-agent-tool/src/mock-ledger.ts +211 -0
- package/examples/node-agent-tool/src/network.test.ts +128 -0
- package/examples/node-agent-tool/src/network.ts +101 -0
- package/examples/node-agent-tool/src/output.test.ts +208 -0
- package/examples/node-agent-tool/src/output.ts +163 -0
- package/examples/node-agent-tool/src/run.ts +73 -0
- package/examples/node-agent-tool/src/test-kit.ts +167 -0
- package/examples/node-agent-tool/tsconfig.json +18 -0
- package/examples/node-agent-tool/vitest.config.ts +8 -0
- package/examples/vite-wallet/.stackblitzrc +4 -0
- package/examples/vite-wallet/Cargo.lock +646 -0
- package/examples/vite-wallet/Cargo.toml +5 -0
- package/examples/vite-wallet/README.md +107 -0
- package/examples/vite-wallet/backend/Cargo.toml +12 -0
- package/examples/vite-wallet/backend/backend.did +37 -0
- package/examples/vite-wallet/backend/src/lib.rs +133 -0
- package/examples/vite-wallet/icp.yaml +44 -0
- package/examples/vite-wallet/index.html +13 -0
- package/examples/vite-wallet/ledger.did +54 -0
- package/examples/vite-wallet/package.json +46 -0
- package/examples/vite-wallet/public/.ic-assets.json5 +26 -0
- package/examples/vite-wallet/scripts/faucet-args.test.ts +30 -0
- package/examples/vite-wallet/scripts/faucet-args.ts +41 -0
- package/examples/vite-wallet/scripts/faucet.ts +41 -0
- package/examples/vite-wallet/src/AddressBook.test.tsx +191 -0
- package/examples/vite-wallet/src/AddressBook.tsx +147 -0
- package/examples/vite-wallet/src/App.tsx +53 -0
- package/examples/vite-wallet/src/Balance.test.tsx +57 -0
- package/examples/vite-wallet/src/Balance.tsx +58 -0
- package/examples/vite-wallet/src/EnvironmentPanel.test.tsx +83 -0
- package/examples/vite-wallet/src/EnvironmentPanel.tsx +103 -0
- package/examples/vite-wallet/src/ErrorNote.tsx +70 -0
- package/examples/vite-wallet/src/Profile.test.tsx +145 -0
- package/examples/vite-wallet/src/Profile.tsx +92 -0
- package/examples/vite-wallet/src/SendIcp.test.tsx +231 -0
- package/examples/vite-wallet/src/SendIcp.tsx +244 -0
- package/examples/vite-wallet/src/SendToContact.test.tsx +113 -0
- package/examples/vite-wallet/src/SendToContact.tsx +102 -0
- package/examples/vite-wallet/src/SignIn.test.tsx +106 -0
- package/examples/vite-wallet/src/SignIn.tsx +120 -0
- package/examples/vite-wallet/src/Wallet.test.tsx +89 -0
- package/examples/vite-wallet/src/account-switch.test.tsx +108 -0
- package/examples/vite-wallet/src/auth/dev-accounts.test.ts +113 -0
- package/examples/vite-wallet/src/auth/dev-accounts.ts +216 -0
- package/examples/vite-wallet/src/auth/local-page.test.ts +28 -0
- package/examples/vite-wallet/src/auth/local-page.ts +20 -0
- package/examples/vite-wallet/src/auth/wallet-auth.test.ts +159 -0
- package/examples/vite-wallet/src/auth/wallet-auth.ts +131 -0
- package/examples/vite-wallet/src/canisters/backend.envelope.json +275 -0
- package/examples/vite-wallet/src/canisters/backend.ts +58 -0
- package/examples/vite-wallet/src/canisters/ledger.envelope.json +451 -0
- package/examples/vite-wallet/src/canisters/ledger.ts +56 -0
- package/examples/vite-wallet/src/client.ts +45 -0
- package/examples/vite-wallet/src/main.tsx +18 -0
- package/examples/vite-wallet/src/optimistic-contacts.ts +113 -0
- package/examples/vite-wallet/src/refusal.ts +17 -0
- package/examples/vite-wallet/src/styles.css +282 -0
- package/examples/vite-wallet/src/test/test-wallet.tsx +322 -0
- package/examples/vite-wallet/src/token.ts +50 -0
- package/examples/vite-wallet/src/transfer.test.ts +178 -0
- package/examples/vite-wallet/src/transfer.ts +110 -0
- package/examples/vite-wallet/src/use-canisters.ts +49 -0
- package/examples/vite-wallet/tsconfig.json +19 -0
- package/examples/vite-wallet/vite.config.ts +47 -0
- package/examples/vite-wallet/vitest.config.ts +17 -0
- package/package.json +9 -4
- package/packages/core/.size-limit.js +47 -0
- package/packages/core/LICENSE +21 -0
- package/packages/core/README.md +84 -0
- package/packages/core/llms.txt +610 -0
- package/packages/core/package.json +89 -0
- package/packages/core/src/call.ts +961 -0
- package/packages/core/src/canister.ts +259 -0
- package/packages/core/src/client.ts +2061 -0
- package/packages/core/src/errors.ts +1079 -0
- package/packages/core/src/hydration.ts +239 -0
- package/packages/core/src/index.ts +23 -0
- package/packages/core/src/keys.ts +108 -0
- package/packages/core/src/management.ts +138 -0
- package/packages/core/src/network.ts +1020 -0
- package/packages/core/src/options.ts +1000 -0
- package/packages/core/src/runtime.ts +50 -0
- package/packages/core/src/testing/fake-replica.ts +1166 -0
- package/packages/core/src/testing/index.ts +16 -0
- package/packages/core/src/testing/test-auth.ts +340 -0
- package/packages/core/src/testing/test-client.ts +559 -0
- package/packages/core/src/types.ts +541 -0
- package/packages/core/src/units.ts +299 -0
- package/packages/core/src/version.ts +4 -0
- package/packages/core/tests/canister-helpers.ts +187 -0
- package/packages/core/tests/canister-id.test.ts +158 -0
- package/packages/core/tests/canister-unwrap.test.ts +94 -0
- package/packages/core/tests/canister.test-d.ts +406 -0
- package/packages/core/tests/canister.test.ts +1921 -0
- package/packages/core/tests/client-account-change.test.ts +131 -0
- package/packages/core/tests/client-agents.test.ts +402 -0
- package/packages/core/tests/client-auth-network.test.ts +719 -0
- package/packages/core/tests/client-disposed.test.ts +403 -0
- package/packages/core/tests/client-helpers.ts +136 -0
- package/packages/core/tests/client-hydration.test.ts +166 -0
- package/packages/core/tests/client-pinned.test.ts +626 -0
- package/packages/core/tests/client-session.test.ts +348 -0
- package/packages/core/tests/client.test-d.ts +181 -0
- package/packages/core/tests/client.test.ts +1162 -0
- package/packages/core/tests/encode.test.ts +171 -0
- package/packages/core/tests/env-canister-id.test.ts +666 -0
- package/packages/core/tests/env-cookie-copies.test.ts +182 -0
- package/packages/core/tests/error-guard.test.ts +222 -0
- package/packages/core/tests/errors.test-d.ts +280 -0
- package/packages/core/tests/errors.test.ts +1359 -0
- package/packages/core/tests/fixtures/README.md +28 -0
- package/packages/core/tests/fixtures/accounts.did +21 -0
- package/packages/core/tests/fixtures/accounts.envelope.json +367 -0
- package/packages/core/tests/fixtures/accounts.ts +26 -0
- package/packages/core/tests/fixtures/archive.did +31 -0
- package/packages/core/tests/fixtures/archive.envelope.json +259 -0
- package/packages/core/tests/fixtures/archive.ts +55 -0
- package/packages/core/tests/fixtures/icrc1.did +88 -0
- package/packages/core/tests/fixtures/icrc1.envelope.json +840 -0
- package/packages/core/tests/fixtures/icrc1.ts +78 -0
- package/packages/core/tests/fixtures/management.did +724 -0
- package/packages/core/tests/fixtures/management.envelope.json +4455 -0
- package/packages/core/tests/fixtures/management.ts +486 -0
- package/packages/core/tests/fixtures/shapes.did +33 -0
- package/packages/core/tests/fixtures/shapes.envelope.json +440 -0
- package/packages/core/tests/fixtures/shapes.ts +56 -0
- package/packages/core/tests/fixtures/skippable.did +15 -0
- package/packages/core/tests/fixtures/skippable.envelope.json +182 -0
- package/packages/core/tests/fixtures/skippable.ts +33 -0
- package/packages/core/tests/func.test.ts +154 -0
- package/packages/core/tests/identity-attributes.test.ts +214 -0
- package/packages/core/tests/keys.test.ts +289 -0
- package/packages/core/tests/management.test.ts +206 -0
- package/packages/core/tests/network-helpers.ts +121 -0
- package/packages/core/tests/network.test-d.ts +78 -0
- package/packages/core/tests/network.test.ts +1051 -0
- package/packages/core/tests/options.test.ts +1594 -0
- package/packages/core/tests/redirect-sign-in.test.ts +393 -0
- package/packages/core/tests/resend.test-d.ts +81 -0
- package/packages/core/tests/resend.test.ts +723 -0
- package/packages/core/tests/skip.test-d.ts +125 -0
- package/packages/core/tests/testing-entry.test.ts +73 -0
- package/packages/core/tests/testing-fake-replica.test.ts +1242 -0
- package/packages/core/tests/testing-test-auth.test-d.ts +84 -0
- package/packages/core/tests/testing-test-auth.test.ts +483 -0
- package/packages/core/tests/testing-test-client.test-d.ts +184 -0
- package/packages/core/tests/testing-test-client.test.ts +1272 -0
- package/packages/core/tests/time-realm.test.ts +63 -0
- package/packages/core/tests/time.test.ts +238 -0
- package/packages/core/tests/traps/README.md +39 -0
- package/packages/core/tests/traps/anonymous-write.test.ts +145 -0
- package/packages/core/tests/traps/classification-table.test.ts +256 -0
- package/packages/core/tests/traps/err-arm.test.ts +144 -0
- package/packages/core/tests/traps/fetch-spy.ts +39 -0
- package/packages/core/tests/traps/invalidation-after-write.test.ts +178 -0
- package/packages/core/tests/traps/ledger.ts +248 -0
- package/packages/core/tests/traps/root-key-off-local.test.ts +130 -0
- package/packages/core/tests/traps/stale-data-across-users.test.ts +173 -0
- package/packages/core/tests/traps/unresolved-name.test.ts +155 -0
- package/packages/core/tests/traps/update-as-query.test.ts +244 -0
- package/packages/core/tests/traps/update-not-resent.test.ts +126 -0
- package/packages/core/tests/traps/update-resent-when-never-delivered.test.ts +159 -0
- package/packages/core/tests/traps.test-d.ts +476 -0
- package/packages/core/tests/units.test-d.ts +118 -0
- package/packages/core/tests/units.test.ts +634 -0
- package/packages/core/tsconfig.json +19 -0
- package/packages/core/tsconfig.typecheck.json +16 -0
- package/packages/core/vitest.config.ts +13 -0
- package/packages/mcp/LICENSE +21 -0
- package/packages/mcp/README.md +189 -0
- package/packages/mcp/package.json +62 -0
- package/packages/mcp/src/candid/candid-text.test.ts +455 -0
- package/packages/mcp/src/candid/candid-text.ts +501 -0
- package/packages/mcp/src/candid/contract.test.ts +398 -0
- package/packages/mcp/src/candid/formats.ts +155 -0
- package/packages/mcp/src/candid/hex.ts +29 -0
- package/packages/mcp/src/candid/json-schema.test.ts +758 -0
- package/packages/mcp/src/candid/json-schema.ts +454 -0
- package/packages/mcp/src/candid/json.test.ts +1207 -0
- package/packages/mcp/src/candid/json.ts +1053 -0
- package/packages/mcp/src/candid/schema-walk.ts +189 -0
- package/packages/mcp/src/candid/type-text.test.ts +179 -0
- package/packages/mcp/src/candid/type-text.ts +238 -0
- package/packages/mcp/src/cli.ts +134 -0
- package/packages/mcp/src/identity.ts +188 -0
- package/packages/mcp/src/interface/cache.test.ts +48 -0
- package/packages/mcp/src/interface/cache.ts +68 -0
- package/packages/mcp/src/interface/cli-cache.test.ts +150 -0
- package/packages/mcp/src/interface/cli-cache.ts +122 -0
- package/packages/mcp/src/interface/compile-worker.test.ts +246 -0
- package/packages/mcp/src/interface/compile-worker.ts +290 -0
- package/packages/mcp/src/interface/compile.test.ts +155 -0
- package/packages/mcp/src/interface/compile.ts +281 -0
- package/packages/mcp/src/interface/docs.test.ts +105 -0
- package/packages/mcp/src/interface/docs.ts +86 -0
- package/packages/mcp/src/interface/fixtures/icrc1.did +88 -0
- package/packages/mcp/src/interface/hex.ts +8 -0
- package/packages/mcp/src/interface/ids.test.ts +71 -0
- package/packages/mcp/src/interface/ids.ts +87 -0
- package/packages/mcp/src/interface/loader.test.ts +804 -0
- package/packages/mcp/src/interface/loader.ts +519 -0
- package/packages/mcp/src/interface/read-candid.test.ts +300 -0
- package/packages/mcp/src/interface/read-candid.ts +259 -0
- package/packages/mcp/src/interface/types.ts +113 -0
- package/packages/mcp/src/network.ts +122 -0
- package/packages/mcp/src/options.ts +149 -0
- package/packages/mcp/src/server/approval.ts +159 -0
- package/packages/mcp/src/server/naming.ts +43 -0
- package/packages/mcp/src/server/results.ts +102 -0
- package/packages/mcp/src/server/server.ts +797 -0
- package/packages/mcp/src/server/tools.ts +116 -0
- package/packages/mcp/src/usage.ts +40 -0
- package/packages/mcp/tests/approval.test.ts +60 -0
- package/packages/mcp/tests/identity.test.ts +234 -0
- package/packages/mcp/tests/naming.test.ts +62 -0
- package/packages/mcp/tests/options.test.ts +140 -0
- package/packages/mcp/tests/server.test.ts +997 -0
- package/packages/mcp/tsconfig.json +19 -0
- package/packages/mcp/tsconfig.typecheck.json +10 -0
- package/packages/mcp/vitest.config.ts +10 -0
- package/packages/react/LICENSE +21 -0
- package/packages/react/README.md +328 -0
- package/packages/react/llms.txt +95 -0
- package/packages/react/package.json +95 -0
- package/packages/react/src/index.tsx +678 -0
- package/packages/react/tests/abandoned-render.test.tsx +816 -0
- package/packages/react/tests/auth.test.tsx +315 -0
- package/packages/react/tests/caller-keys.test.tsx +2334 -0
- package/packages/react/tests/client-time.test.ts +40 -0
- package/packages/react/tests/entry.test.ts +52 -0
- package/packages/react/tests/helpers.tsx +291 -0
- package/packages/react/tests/hydration.test.tsx +262 -0
- package/packages/react/tests/ownership.test.tsx +260 -0
- package/packages/react/tests/provider.server.test.tsx +169 -0
- package/packages/react/tests/query-client.test.tsx +107 -0
- package/packages/react/tests/strict-mode.test.tsx +288 -0
- package/packages/react/tests/suspense.test-d.tsx +357 -0
- package/packages/react/tests/two-query-cores.test.tsx +135 -0
- package/packages/react/tests/types.test-d.tsx +63 -0
- package/packages/react/tsconfig.json +23 -0
- package/packages/react/tsconfig.test.json +8 -0
- package/packages/react/tsconfig.typecheck.json +16 -0
- package/packages/react/vitest.config.ts +39 -0
- package/packages/vite-plugin/LICENSE +21 -0
- package/packages/vite-plugin/README.md +532 -0
- package/packages/vite-plugin/package.json +76 -0
- package/packages/vite-plugin/src/describe-value.test.ts +43 -0
- package/packages/vite-plugin/src/describe-value.ts +167 -0
- package/packages/vite-plugin/src/dev-environment.test.ts +747 -0
- package/packages/vite-plugin/src/dev-environment.ts +510 -0
- package/packages/vite-plugin/src/env.ts +431 -0
- package/packages/vite-plugin/src/fetch.ts +348 -0
- package/packages/vite-plugin/src/file-identity.ts +106 -0
- package/packages/vite-plugin/src/generate.ts +691 -0
- package/packages/vite-plugin/src/index.test.ts +1243 -0
- package/packages/vite-plugin/src/index.ts +2565 -0
- package/packages/vite-plugin/src/interface.ts +339 -0
- package/packages/vite-plugin/src/local-host.ts +34 -0
- package/packages/vite-plugin/src/options.ts +641 -0
- package/packages/vite-plugin/src/principal-text.test.ts +87 -0
- package/packages/vite-plugin/src/principal-text.ts +98 -0
- package/packages/vite-plugin/tests/close.test.ts +141 -0
- package/packages/vite-plugin/tests/environment-mode.test.ts +136 -0
- package/packages/vite-plugin/tests/fake-ic.ts +214 -0
- package/packages/vite-plugin/tests/fetch-sdk.test.ts +27 -0
- package/packages/vite-plugin/tests/fetch.test.ts +1196 -0
- package/packages/vite-plugin/tests/fixtures/fake-cli/bin.js +90 -0
- package/packages/vite-plugin/tests/fixtures/fake-cli/package.json +8 -0
- package/packages/vite-plugin/tests/fixtures/icrc1.did +88 -0
- package/packages/vite-plugin/tests/generation.test.ts +1036 -0
- package/packages/vite-plugin/tests/icp-command.test.ts +194 -0
- package/packages/vite-plugin/tests/icp-timeout.test.ts +83 -0
- package/packages/vite-plugin/tests/no-process-exit.test.ts +83 -0
- package/packages/vite-plugin/tests/on-generated.test.ts +323 -0
- package/packages/vite-plugin/tests/output-collision.test.ts +329 -0
- package/packages/vite-plugin/tests/package.test.ts +62 -0
- package/packages/vite-plugin/tests/project.test.ts +2993 -0
- package/packages/vite-plugin/tests/support.ts +339 -0
- package/packages/vite-plugin/tests/timeout.test.ts +107 -0
- package/packages/vite-plugin/tsconfig.json +23 -0
- package/packages/vite-plugin/tsconfig.typecheck.json +16 -0
- package/packages/vite-plugin/vitest.config.ts +9 -0
- package/pnpm-workspace.yaml +23 -0
- package/scripts/ai-context-files.js +72 -0
- package/scripts/assemble-docs-site.mjs +485 -0
- package/scripts/assemble-docs-site.test.mjs +568 -0
- package/scripts/build-examples.js +58 -0
- package/scripts/check-ai-context.js +963 -0
- package/scripts/check-ai-context.test.mjs +811 -0
- package/scripts/check-exports.mjs +767 -0
- package/scripts/check-exports.test.mjs +1136 -0
- package/scripts/check-snippets/ambient.d.ts +428 -0
- package/scripts/check-snippets/app/generated/backend.did +7 -0
- package/scripts/check-snippets/app/generated/backend.ts +15 -0
- package/scripts/check-snippets/app/generated/icrc1.did +88 -0
- package/scripts/check-snippets/app/generated/icrc1.ts +78 -0
- package/scripts/check-snippets/app/globals.ts +26 -0
- package/scripts/check-snippets/docs/guides/App.tsx +21 -0
- package/scripts/check-snippets/docs/guides/app/providers.tsx +23 -0
- package/scripts/check-snippets/docs/guides/auth-client.ts +47 -0
- package/scripts/check-snippets/docs/guides/canisters/backend.ts +3 -0
- package/scripts/check-snippets/docs/guides/canisters/icrc1.ts +3 -0
- package/scripts/check-snippets/docs/guides/components/LedgerCard.tsx +26 -0
- package/scripts/check-snippets/docs/guides/components/shell.tsx +11 -0
- package/scripts/check-snippets/docs/guides/components/sign-in-page.tsx +29 -0
- package/scripts/check-snippets/docs/guides/components/slow-section.tsx +5 -0
- package/scripts/check-snippets/docs/guides/generated/accounts.ts +95 -0
- package/scripts/check-snippets/docs/guides/generated/ckbtc_minter.ts +40 -0
- package/scripts/check-snippets/docs/guides/generated/icp_ledger.ts +267 -0
- package/scripts/check-snippets/docs/guides/ledger.ts +10 -0
- package/scripts/check-snippets/docs/guides/server/request-client.ts +8 -0
- package/scripts/check-snippets.mjs +993 -0
- package/scripts/check-snippets.test.mjs +338 -0
- package/scripts/check-stack.mjs +501 -0
- package/scripts/check-stack.test.mjs +800 -0
- package/scripts/clean-node-modules.js +67 -0
- package/scripts/example-projects.js +58 -0
- package/scripts/export-budget.mjs +145 -0
- package/scripts/faults.json +6749 -0
- package/scripts/lib/fault-workspace.mjs +210 -0
- package/scripts/lib/io.mjs +29 -0
- package/scripts/lib/io.test.mjs +38 -0
- package/scripts/lib/markdown-fences.mjs +56 -0
- package/scripts/lib/markdown-fences.test.mjs +84 -0
- package/scripts/lib/package-exports.mjs +54 -0
- package/scripts/lib/package-exports.test.mjs +66 -0
- package/scripts/lib/release-pr-steps.mjs +47 -0
- package/scripts/release-tag.mjs +95 -0
- package/scripts/release-tag.test.mjs +278 -0
- package/scripts/release-workflow.test.mjs +98 -0
- package/scripts/release.js +167 -0
- package/scripts/release.test.mjs +347 -0
- package/scripts/removed-v3-names.js +125 -0
- package/scripts/size-app/.size-limit.js +90 -0
- package/scripts/size-app/core.js +3 -0
- package/scripts/size-app/package.json +24 -0
- package/scripts/size-app/react.js +4 -0
- package/scripts/size-app.test.mjs +73 -0
- package/scripts/sync-ai-context-versions.js +102 -0
- package/scripts/sync-example-versions.js +151 -0
- package/scripts/test-examples-published.mjs +1585 -0
- package/scripts/test-examples-published.test.mjs +1586 -0
- package/scripts/traps/any-method-vars-not-a-list.json +5 -0
- package/scripts/traps/canister-method-unknown.json +5 -0
- package/scripts/traps/client-says-who-calls-once.json +5 -0
- package/scripts/traps/client-says-who-calls.json +5 -0
- package/scripts/traps/err-only-on-canister-err.json +14 -0
- package/scripts/traps/format-units-refuses-missing-value.json +5 -0
- package/scripts/traps/format-units-refuses-number.json +5 -0
- package/scripts/traps/format-units-unknown-option.json +5 -0
- package/scripts/traps/guard-err-is-the-methods.json +5 -0
- package/scripts/traps/guard-false-keeps-every-kind.json +5 -0
- package/scripts/traps/internals-not-exported.json +9 -0
- package/scripts/traps/network-refuses-unknown-name.json +5 -0
- package/scripts/traps/never-err-has-no-type.json +5 -0
- package/scripts/traps/options-method-unknown.json +5 -0
- package/scripts/traps/options-vars-wrong-type.json +5 -0
- package/scripts/traps/parse-units-refuses-number.json +5 -0
- package/scripts/traps/parse-units-unknown-option.json +5 -0
- package/scripts/traps/principal-is-not-plain-text.json +14 -0
- package/scripts/traps/query-options-refuses-oneway.json +5 -0
- package/scripts/traps/query-options-refuses-update.json +5 -0
- package/scripts/traps/refuse-next-unknown-option.json +5 -0
- package/scripts/traps/resend-key-reads-the-methods-arg.json +5 -0
- package/scripts/traps/resend-states-its-dedupe-key.json +5 -0
- package/scripts/traps/suspense-refuses-argument-skip.json +5 -0
- package/scripts/traps/suspense-refuses-skippable-read.json +5 -0
- package/scripts/traps/suspense-refuses-vars-skip-fits.json +5 -0
- package/scripts/traps/test-handler-method-unknown.json +5 -0
- package/scripts/traps/test-handler-nat-is-bigint.json +5 -0
- package/scripts/traps/test-handler-takes-the-domain-value.json +5 -0
- package/scripts/traps/vars-of-two-arguments-are-the-tuple.json +5 -0
- package/scripts/typecheck-examples.js +122 -0
- package/scripts/verify-faults.mjs +375 -0
- package/scripts/verify-faults.test.mjs +402 -0
- package/scripts/verify-package-artifacts.mjs +590 -0
- package/scripts/verify-peer-floors.js +399 -0
- package/scripts/verify-peer-floors.test.mjs +422 -0
- package/scripts/verify-test-fails-rules.mjs +104 -0
- package/scripts/verify-test-fails-rules.test.mjs +215 -0
- package/scripts/verify-test-fails.js +371 -0
- package/scripts/verify-traps.mjs +505 -0
- package/scripts/verify-traps.test.mjs +521 -0
- package/scripts/verify-typescript-floor.js +239 -0
- package/skill-packages/ic-reactor/SKILL.md +103 -0
- package/stack.json +8 -0
- package/tsconfig.base.json +23 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trap: a canister id taken from a cookie the page cannot trust.
|
|
3
|
+
*
|
|
4
|
+
* The mistake a hand-written integration makes: it reads `ic_env`, the cookie
|
|
5
|
+
* that tells a front end where its canisters are, wherever its code runs, and
|
|
6
|
+
* calls the id it finds under the canister's name. A cookie is not isolated
|
|
7
|
+
* to an origin: any sibling subdomain of the page's domain can write one. The
|
|
8
|
+
* id such a cookie names is a real canister, whose replies verify against the
|
|
9
|
+
* real root key, so no certificate check ever notices the substitution, and
|
|
10
|
+
* the user's transfer goes to the ledger the attacker chose. On a server there
|
|
11
|
+
* is no cookie at all, and a hand-written reader finds none and sends the call
|
|
12
|
+
* to an `undefined` id or to a default.
|
|
13
|
+
*
|
|
14
|
+
* The guarantee: a canister named `{ name }` resolves only on a page where the
|
|
15
|
+
* cookie is as trustworthy as the replica: the page and the replica are both
|
|
16
|
+
* local, or the app wrote `allowEnvConfig: true`. Anywhere else (a server, an
|
|
17
|
+
* ordinary web host) every call to it rejects `invalid_args` with the code
|
|
18
|
+
* `canister_id_unresolved` and `mayHaveExecuted: false`, sends nothing, and
|
|
19
|
+
* the read's key carries `$unresolved:<name>`, so no data is cached for a
|
|
20
|
+
* guess. The control cases show the same calls resolve, and are sent, where
|
|
21
|
+
* the cookie is trusted.
|
|
22
|
+
*/
|
|
23
|
+
import { afterAll, afterEach, describe, expect, it, vi } from "vitest"
|
|
24
|
+
import * as icrc1 from "../fixtures/icrc1.js"
|
|
25
|
+
import {
|
|
26
|
+
ALICE,
|
|
27
|
+
LEDGER,
|
|
28
|
+
SIGNED_CALLS_TIMEOUT_MS,
|
|
29
|
+
disposeAll,
|
|
30
|
+
rejection,
|
|
31
|
+
setupLedger,
|
|
32
|
+
transferArg,
|
|
33
|
+
type TestClientOptions,
|
|
34
|
+
} from "./ledger.js"
|
|
35
|
+
|
|
36
|
+
vi.setConfig({ testTimeout: SIGNED_CALLS_TIMEOUT_MS })
|
|
37
|
+
afterAll(disposeAll)
|
|
38
|
+
afterEach(() => vi.unstubAllGlobals())
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The `ic_env` cookie a sibling subdomain, or an asset canister, writes: the id
|
|
42
|
+
* `LEDGER` under the name `ledger`, and a root key (which the client never
|
|
43
|
+
* takes from a cookie, so any key of the right length stands for one).
|
|
44
|
+
*/
|
|
45
|
+
const COOKIE = `ic_env=${encodeURIComponent(
|
|
46
|
+
`ic_root_key=${"07".repeat(133)}&PUBLIC_CANISTER_ID:ledger=${LEDGER}`
|
|
47
|
+
)}`
|
|
48
|
+
|
|
49
|
+
/** Runs the code on a browser page at `origin` whose cookie jar holds {@link COOKIE}. */
|
|
50
|
+
function onPage(origin: string): void {
|
|
51
|
+
vi.stubGlobal("window", {
|
|
52
|
+
location: { origin, protocol: new URL(origin).protocol },
|
|
53
|
+
})
|
|
54
|
+
vi.stubGlobal("document", { cookie: COOKIE })
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* A client whose ledger lives at `LEDGER` but whose app only knows it as
|
|
59
|
+
* `{ name: "ledger" }`, and what it calls: a read and an update.
|
|
60
|
+
*/
|
|
61
|
+
function namedLedger(options?: TestClientOptions) {
|
|
62
|
+
const l = setupLedger(options)
|
|
63
|
+
const named = l.client.canister<icrc1.Actor>(icrc1.actor, { name: "ledger" })
|
|
64
|
+
return { ...l, named }
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const unresolved = {
|
|
68
|
+
kind: "invalid_args",
|
|
69
|
+
code: "canister_id_unresolved",
|
|
70
|
+
mayHaveExecuted: false,
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
describe("a canister named { name } where the cookie is not trusted", () => {
|
|
74
|
+
it("is unresolved on a server, which has no cookie: every call is refused and nothing is sent", async () => {
|
|
75
|
+
// Node: no window, and so no cookie jar.
|
|
76
|
+
const { named, sent } = namedLedger()
|
|
77
|
+
|
|
78
|
+
await expect(
|
|
79
|
+
rejection(named.icrc1_balance_of({ owner: ALICE, subaccount: null }))
|
|
80
|
+
).resolves.toMatchObject(unresolved)
|
|
81
|
+
await expect(
|
|
82
|
+
rejection(named.icrc1_transfer(transferArg(500n)))
|
|
83
|
+
).resolves.toMatchObject(unresolved)
|
|
84
|
+
expect(sent()).toEqual([])
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
it("is unresolved on an ordinary web page whose cookie names a real ledger, though the replica is local", async () => {
|
|
88
|
+
// The page is `app.example.com`; a sibling subdomain wrote the cookie. The
|
|
89
|
+
// replica the client talks to is a local one, which would be trusted on a
|
|
90
|
+
// local page, so the page alone decides.
|
|
91
|
+
onPage("https://app.example.com")
|
|
92
|
+
const { named, sent, executed } = namedLedger()
|
|
93
|
+
|
|
94
|
+
await expect(
|
|
95
|
+
rejection(named.icrc1_balance_of({ owner: ALICE, subaccount: null }))
|
|
96
|
+
).resolves.toMatchObject(unresolved)
|
|
97
|
+
await expect(
|
|
98
|
+
rejection(named.icrc1_transfer(transferArg(500n)))
|
|
99
|
+
).resolves.toMatchObject(unresolved)
|
|
100
|
+
expect(sent()).toEqual([])
|
|
101
|
+
expect(executed()).toBe(0)
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it("is unresolved for a read through queryOptions, under a key that names the guess it did not make", async () => {
|
|
105
|
+
onPage("https://app.example.com")
|
|
106
|
+
const { client, named, sent } = namedLedger()
|
|
107
|
+
|
|
108
|
+
const options = client.queryOptions(named, "icrc1_balance_of", {
|
|
109
|
+
owner: ALICE,
|
|
110
|
+
subaccount: null,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
expect(options.queryKey).toContain("$unresolved:ledger")
|
|
114
|
+
await expect(
|
|
115
|
+
client.queryClient.fetchQuery({ ...options, retry: false })
|
|
116
|
+
).rejects.toMatchObject(unresolved)
|
|
117
|
+
expect(sent()).toEqual([])
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
describe("a canister named { name } where the cookie is trusted", () => {
|
|
122
|
+
it("is resolved on a local page with a local replica, and sent to the id the cookie names", async () => {
|
|
123
|
+
onPage("http://localhost:5173")
|
|
124
|
+
const { named, sent } = namedLedger()
|
|
125
|
+
|
|
126
|
+
await expect(
|
|
127
|
+
named.icrc1_balance_of({ owner: ALICE, subaccount: null })
|
|
128
|
+
).resolves.toBe(1_000_000n)
|
|
129
|
+
|
|
130
|
+
expect(sent()).toMatchObject([{ endpoint: "query", canisterId: LEDGER }])
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
it("is resolved on an ordinary page when the app writes allowEnvConfig: true", async () => {
|
|
134
|
+
onPage("https://app.example.com")
|
|
135
|
+
const { named, sent } = namedLedger({ allowEnvConfig: true })
|
|
136
|
+
|
|
137
|
+
await expect(
|
|
138
|
+
named.icrc1_balance_of({ owner: ALICE, subaccount: null })
|
|
139
|
+
).resolves.toBe(1_000_000n)
|
|
140
|
+
|
|
141
|
+
expect(sent()).toMatchObject([{ endpoint: "query", canisterId: LEDGER }])
|
|
142
|
+
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
it("never lets the cookie override a canister the app named by its id", async () => {
|
|
146
|
+
// The id is configuration, and it wins over whatever the page says.
|
|
147
|
+
onPage("http://localhost:5173")
|
|
148
|
+
const l = setupLedger()
|
|
149
|
+
|
|
150
|
+
await expect(
|
|
151
|
+
l.ledger.icrc1_balance_of({ owner: ALICE, subaccount: null })
|
|
152
|
+
).resolves.toBe(1_000_000n)
|
|
153
|
+
|
|
154
|
+
expect(l.sent()).toMatchObject([{ canisterId: LEDGER }])
|
|
155
|
+
})
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trap: an update method read through a query.
|
|
3
|
+
*
|
|
4
|
+
* The mistake a hand-written integration makes: it wraps a canister call in
|
|
5
|
+
* `useQuery`, because that is how data is fetched, and the method it wraps is
|
|
6
|
+
* `icrc1_transfer`. TanStack Query runs a query function again whenever it
|
|
7
|
+
* refetches: when the window regains focus, when the network comes back, when
|
|
8
|
+
* a component mounts again, after an invalidation, when it retries. Each run
|
|
9
|
+
* is another transfer.
|
|
10
|
+
*
|
|
11
|
+
* The guarantee: for a generated `Actor`, which says each method's mode,
|
|
12
|
+
* `client.queryOptions` with an update (or oneway) method does not compile
|
|
13
|
+
* (`tests/traps.test-d.ts`). For an `Actor` written by hand, which does not,
|
|
14
|
+
* it throws a `TypeError` at the line that builds the read, which says to use
|
|
15
|
+
* `mutationOptions`, and sends nothing: that run-time check is what this file
|
|
16
|
+
* proves, through the ledger typed by hand. The one case where an update is a
|
|
17
|
+
* read, a method that answers the same however often it runs (ckBTC's
|
|
18
|
+
* `get_btc_address`), is a written-out opt-in, `{ update: "idempotent" }`,
|
|
19
|
+
* which is bounded: the read runs once per caller, and focus, reconnect and
|
|
20
|
+
* remount do not run it again.
|
|
21
|
+
*/
|
|
22
|
+
import {
|
|
23
|
+
QueryObserver,
|
|
24
|
+
focusManager,
|
|
25
|
+
onlineManager,
|
|
26
|
+
} from "@tanstack/query-core"
|
|
27
|
+
import { afterAll, afterEach, describe, expect, it, vi } from "vitest"
|
|
28
|
+
import type { HandWritten } from "../canister-helpers.js"
|
|
29
|
+
import * as icrc1 from "../fixtures/icrc1.js"
|
|
30
|
+
import * as shapes from "../fixtures/shapes.js"
|
|
31
|
+
import {
|
|
32
|
+
ALICE,
|
|
33
|
+
BOB,
|
|
34
|
+
LEDGER,
|
|
35
|
+
SIGNED_CALLS_TIMEOUT_MS,
|
|
36
|
+
disposeAll,
|
|
37
|
+
eventually,
|
|
38
|
+
setupLedger,
|
|
39
|
+
sleep,
|
|
40
|
+
transferArg,
|
|
41
|
+
type Ledger,
|
|
42
|
+
} from "./ledger.js"
|
|
43
|
+
|
|
44
|
+
vi.setConfig({ testTimeout: SIGNED_CALLS_TIMEOUT_MS })
|
|
45
|
+
afterAll(disposeAll)
|
|
46
|
+
afterEach(() => {
|
|
47
|
+
focusManager.setFocused(undefined)
|
|
48
|
+
onlineManager.setOnline(true)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
/** Where the traps run a canister with an update method that is safe to read. */
|
|
52
|
+
const BOOK = "qoctq-giaaa-aaaaa-aaaea-cai"
|
|
53
|
+
|
|
54
|
+
/** A ledger, and next to it a canister whose `address` update answers the same every time. */
|
|
55
|
+
function withAddressBook(options?: Parameters<typeof setupLedger>[0]) {
|
|
56
|
+
const l = setupLedger(options)
|
|
57
|
+
l.mock<shapes.Actor>(shapes.actor, BOOK, {
|
|
58
|
+
address: (seed, { caller }) => `${seed}:${caller}`,
|
|
59
|
+
})
|
|
60
|
+
return {
|
|
61
|
+
...l,
|
|
62
|
+
book: l.client.canister<shapes.Actor>(shapes.actor, { id: BOOK }),
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Waits until the client has nothing in flight: a refetch that a trigger made
|
|
68
|
+
* has been signed, sent, answered and certified, or has failed, however long
|
|
69
|
+
* that took. A fixed pause cannot say that: on a loaded machine a refetch that
|
|
70
|
+
* is sent can arrive after it, and the test would pass without the policy it
|
|
71
|
+
* checks. A refetch that focus or reconnect makes begins on a later turn of
|
|
72
|
+
* the event loop than the trigger, so the short pause lets it begin before
|
|
73
|
+
* the look at what is in flight.
|
|
74
|
+
*/
|
|
75
|
+
async function untilIdle(l: Ledger): Promise<void> {
|
|
76
|
+
await sleep(20)
|
|
77
|
+
await eventually(() => expect(l.client.queryClient.isFetching()).toBe(0))
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Plays out what makes TanStack Query refetch: the window losing and regaining
|
|
82
|
+
* focus, the network dropping and coming back, and the component mounting
|
|
83
|
+
* again. After each, `settle` waits for whatever that is going to send.
|
|
84
|
+
*/
|
|
85
|
+
async function triggerRefetches(
|
|
86
|
+
l: Ledger,
|
|
87
|
+
mountAgain: () => () => void,
|
|
88
|
+
settle: () => Promise<void>
|
|
89
|
+
) {
|
|
90
|
+
focusManager.setFocused(false)
|
|
91
|
+
focusManager.setFocused(true)
|
|
92
|
+
await settle()
|
|
93
|
+
onlineManager.setOnline(false)
|
|
94
|
+
onlineManager.setOnline(true)
|
|
95
|
+
await settle()
|
|
96
|
+
const unmount = mountAgain()
|
|
97
|
+
await settle()
|
|
98
|
+
unmount()
|
|
99
|
+
l.client.queryClient.unmount()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
describe("a read built for an update method", () => {
|
|
103
|
+
it("is refused when it is built, and nothing is sent", () => {
|
|
104
|
+
const l = setupLedger()
|
|
105
|
+
// The same ledger, typed by an Actor written by hand: no mode says that
|
|
106
|
+
// icrc1_transfer is an update, so the read compiles and only the check at
|
|
107
|
+
// run time stands between it and a transfer on every refetch.
|
|
108
|
+
const ledger = l.client.canister<HandWritten<icrc1.Actor>>(icrc1.actor, {
|
|
109
|
+
id: LEDGER,
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
expect(() =>
|
|
113
|
+
l.client.queryOptions(ledger, "icrc1_transfer", transferArg(500n))
|
|
114
|
+
).toThrow(TypeError)
|
|
115
|
+
expect(() =>
|
|
116
|
+
l.client.queryOptions(ledger, "icrc1_transfer", transferArg(500n))
|
|
117
|
+
).toThrow(/mutationOptions/)
|
|
118
|
+
expect(l.sent()).toEqual([])
|
|
119
|
+
expect(l.executed()).toBe(0)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it("is sent again by a refetch when the method is a plain query, which is what the opt-in below must not be", async () => {
|
|
123
|
+
const l = setupLedger()
|
|
124
|
+
l.client.queryClient.mount()
|
|
125
|
+
const options = () => l.client.queryOptions(l.ledger, "icrc1_fee")
|
|
126
|
+
const observer = new QueryObserver(l.client.queryClient, options())
|
|
127
|
+
const stop = observer.subscribe(() => undefined)
|
|
128
|
+
await eventually(() =>
|
|
129
|
+
expect(observer.getCurrentResult().data).toBe(10_000n)
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
// Each trigger sends the read again: the log grows after every one.
|
|
133
|
+
let seen = l.sent().length
|
|
134
|
+
await triggerRefetches(
|
|
135
|
+
l,
|
|
136
|
+
() =>
|
|
137
|
+
new QueryObserver(l.client.queryClient, options()).subscribe(
|
|
138
|
+
() => undefined
|
|
139
|
+
),
|
|
140
|
+
async () => {
|
|
141
|
+
await eventually(() => expect(l.sent().length).toBeGreaterThan(seen))
|
|
142
|
+
seen = l.sent().length
|
|
143
|
+
}
|
|
144
|
+
)
|
|
145
|
+
stop()
|
|
146
|
+
|
|
147
|
+
expect(l.sent().length).toBeGreaterThanOrEqual(4)
|
|
148
|
+
})
|
|
149
|
+
})
|
|
150
|
+
|
|
151
|
+
describe("an update read with { update: 'idempotent' }", () => {
|
|
152
|
+
it("runs once, and is not run again on focus, reconnect or remount", async () => {
|
|
153
|
+
const l = withAddressBook()
|
|
154
|
+
l.client.queryClient.mount()
|
|
155
|
+
const options = () =>
|
|
156
|
+
l.client.queryOptions(l.book, "address", "home", {
|
|
157
|
+
update: "idempotent",
|
|
158
|
+
})
|
|
159
|
+
const observer = new QueryObserver(l.client.queryClient, options())
|
|
160
|
+
const stop = observer.subscribe(() => undefined)
|
|
161
|
+
await eventually(() =>
|
|
162
|
+
expect(observer.getCurrentResult().data).toBe(`home:${ALICE}`)
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
// Each trigger is followed by the wait for whatever it sent to be over.
|
|
166
|
+
await triggerRefetches(
|
|
167
|
+
l,
|
|
168
|
+
() =>
|
|
169
|
+
new QueryObserver(l.client.queryClient, options()).subscribe(
|
|
170
|
+
() => undefined
|
|
171
|
+
),
|
|
172
|
+
() => untilIdle(l)
|
|
173
|
+
)
|
|
174
|
+
stop()
|
|
175
|
+
|
|
176
|
+
// One call, as Alice; not one per trigger.
|
|
177
|
+
expect(l.calls()).toMatchObject([{ methodName: "address", caller: ALICE }])
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it("is read once for each caller, and never as the new caller under the old key", async () => {
|
|
181
|
+
const l = withAddressBook()
|
|
182
|
+
const { queryClient } = l.client
|
|
183
|
+
queryClient.mount()
|
|
184
|
+
const options = () =>
|
|
185
|
+
l.client.queryOptions(l.book, "address", "home", {
|
|
186
|
+
update: "idempotent",
|
|
187
|
+
})
|
|
188
|
+
const aliceOptions = options()
|
|
189
|
+
const aliceObserver = new QueryObserver(queryClient, aliceOptions)
|
|
190
|
+
const stopAlice = aliceObserver.subscribe(() => undefined)
|
|
191
|
+
await eventually(() =>
|
|
192
|
+
expect(aliceObserver.getCurrentResult().data).toBe(`home:${ALICE}`)
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
// Bob signs in. The read an app builds from now on is Bob's: it has his
|
|
196
|
+
// own key, and runs once, as him, however often it is triggered.
|
|
197
|
+
l.auth.switchTo(2)
|
|
198
|
+
const bobOptions = options()
|
|
199
|
+
expect(bobOptions.queryKey).not.toEqual(aliceOptions.queryKey)
|
|
200
|
+
const bobObserver = new QueryObserver(queryClient, bobOptions)
|
|
201
|
+
const stopBob = bobObserver.subscribe(() => undefined)
|
|
202
|
+
await eventually(() =>
|
|
203
|
+
expect(bobObserver.getCurrentResult().data).toBe(`home:${BOB}`)
|
|
204
|
+
)
|
|
205
|
+
await triggerRefetches(
|
|
206
|
+
l,
|
|
207
|
+
() =>
|
|
208
|
+
new QueryObserver(queryClient, bobOptions).subscribe(() => undefined),
|
|
209
|
+
() => untilIdle(l)
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
// Alice's read, built before the switch, is fetched again now. It is made
|
|
213
|
+
// for Alice and is never sent as Bob, whose answer would sit under her key.
|
|
214
|
+
await queryClient
|
|
215
|
+
.fetchQuery({ ...aliceOptions, staleTime: 0, retry: false })
|
|
216
|
+
.catch((error: unknown) => error)
|
|
217
|
+
await untilIdle(l)
|
|
218
|
+
stopAlice()
|
|
219
|
+
stopBob()
|
|
220
|
+
|
|
221
|
+
expect(l.calls()).toMatchObject([
|
|
222
|
+
{ methodName: "address", caller: ALICE },
|
|
223
|
+
{ methodName: "address", caller: BOB },
|
|
224
|
+
])
|
|
225
|
+
// Alice's reads left the cache when Bob became current, and the refused
|
|
226
|
+
// read put nothing under her key.
|
|
227
|
+
expect(queryClient.getQueryData(aliceOptions.queryKey)).toBeUndefined()
|
|
228
|
+
expect(queryClient.getQueryData(bobOptions.queryKey)).toBe(`home:${BOB}`)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it("is refused for a caller who is not signed in, like any update", async () => {
|
|
232
|
+
const l = withAddressBook({ signedIn: false })
|
|
233
|
+
|
|
234
|
+
await expect(
|
|
235
|
+
l.client.queryClient.fetchQuery({
|
|
236
|
+
...l.client.queryOptions(l.book, "address", "home", {
|
|
237
|
+
update: "idempotent",
|
|
238
|
+
}),
|
|
239
|
+
retry: false,
|
|
240
|
+
})
|
|
241
|
+
).rejects.toMatchObject({ kind: "unauthenticated", mayHaveExecuted: false })
|
|
242
|
+
expect(l.calls()).toEqual([])
|
|
243
|
+
})
|
|
244
|
+
})
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trap: an update sent again after a failure that does not say it never ran.
|
|
3
|
+
*
|
|
4
|
+
* The mistake a hand-written integration makes: it wraps the call in a retry
|
|
5
|
+
* ("three attempts, back off"), or leaves the agent's own retries on. Every
|
|
6
|
+
* attempt of an update is a new request with a new id, so the Internet
|
|
7
|
+
* Computer cannot tell a retry from a second payment and runs both. A
|
|
8
|
+
* canister that rejected with code 4 keeps what it changed, one that trapped
|
|
9
|
+
* (code 5) may have committed some of it before the trap, a reply that was
|
|
10
|
+
* lost on its way back came from a canister that ran, and an HTTP 408 or 5xx
|
|
11
|
+
* may follow a delivery: re-sending after any of them can pay twice.
|
|
12
|
+
*
|
|
13
|
+
* The guarantee: after reject code 4, reject code 5, a lost reply, HTTP 408
|
|
14
|
+
* and HTTP 5xx, exactly one request was sent, however long the client waits
|
|
15
|
+
* (it waits past both of its re-send delays, 300 ms and 600 ms, before it
|
|
16
|
+
* looks), and the failure says `mayHaveExecuted: true` so the app reads the
|
|
17
|
+
* balance back before it offers to try again. The cases where a re-send is
|
|
18
|
+
* safe, and made, are in `update-resent-when-never-delivered.test.ts`.
|
|
19
|
+
*/
|
|
20
|
+
import { MutationObserver } from "@tanstack/query-core"
|
|
21
|
+
import { afterAll, describe, expect, it, vi } from "vitest"
|
|
22
|
+
import {
|
|
23
|
+
ALICE,
|
|
24
|
+
FEE,
|
|
25
|
+
SIGNED_CALLS_TIMEOUT_MS,
|
|
26
|
+
disposeAll,
|
|
27
|
+
pastResendDelays,
|
|
28
|
+
rejection,
|
|
29
|
+
setupLedger,
|
|
30
|
+
transferArg,
|
|
31
|
+
} from "./ledger.js"
|
|
32
|
+
|
|
33
|
+
vi.setConfig({ testTimeout: SIGNED_CALLS_TIMEOUT_MS })
|
|
34
|
+
afterAll(disposeAll)
|
|
35
|
+
|
|
36
|
+
// One flat suite, so that the waits of its tests overlap: each sits out the
|
|
37
|
+
// client's re-send delays, and one after another they would take seconds.
|
|
38
|
+
describe.concurrent("an update that may have run", () => {
|
|
39
|
+
it.each([
|
|
40
|
+
[4, "refused after the debit", false],
|
|
41
|
+
[5, "trapped after the debit", true],
|
|
42
|
+
] as const)(
|
|
43
|
+
"is never re-sent after reject code %i, so the ledger debits once",
|
|
44
|
+
async (code, message, trap) => {
|
|
45
|
+
const l = setupLedger()
|
|
46
|
+
l.onTransfer((apply) => {
|
|
47
|
+
apply()
|
|
48
|
+
if (trap) throw new Error(message)
|
|
49
|
+
return l.reject(code, message)
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
const failure = await rejection(
|
|
53
|
+
l.ledger.icrc1_transfer(transferArg(500n))
|
|
54
|
+
)
|
|
55
|
+
await pastResendDelays()
|
|
56
|
+
|
|
57
|
+
expect(failure).toMatchObject({
|
|
58
|
+
kind: "rejected",
|
|
59
|
+
rejectCode: code,
|
|
60
|
+
mayHaveExecuted: true,
|
|
61
|
+
})
|
|
62
|
+
expect(l.calls()).toHaveLength(1)
|
|
63
|
+
expect(l.executed()).toBe(1)
|
|
64
|
+
expect(l.balances.get(ALICE)).toBe(1_000_000n - 500n - FEE)
|
|
65
|
+
}
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
it("is never re-sent after its reply was lost, so the ledger debits once", async () => {
|
|
69
|
+
const l = setupLedger()
|
|
70
|
+
l.dropNextReply()
|
|
71
|
+
|
|
72
|
+
const failure = await rejection(l.ledger.icrc1_transfer(transferArg(500n)))
|
|
73
|
+
await pastResendDelays()
|
|
74
|
+
|
|
75
|
+
// The ledger ran it: the reply was lost, not the call.
|
|
76
|
+
expect(failure).toMatchObject({
|
|
77
|
+
kind: "outcome_unknown",
|
|
78
|
+
mayHaveExecuted: true,
|
|
79
|
+
})
|
|
80
|
+
expect(l.calls()).toMatchObject([{ dropped: true }])
|
|
81
|
+
expect(l.executed()).toBe(1)
|
|
82
|
+
expect(l.balances.get(ALICE)).toBe(1_000_000n - 500n - FEE)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
it("is never re-sent after a mutation lost its reply: retry is off", async () => {
|
|
86
|
+
const l = setupLedger()
|
|
87
|
+
l.dropNextReply()
|
|
88
|
+
const mutation = new MutationObserver(
|
|
89
|
+
l.client.queryClient,
|
|
90
|
+
l.client.mutationOptions(l.ledger, "icrc1_transfer")
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
await mutation.mutate(transferArg(500n)).catch(() => undefined)
|
|
94
|
+
await pastResendDelays()
|
|
95
|
+
|
|
96
|
+
expect(mutation.getCurrentResult()).toMatchObject({
|
|
97
|
+
status: "error",
|
|
98
|
+
failureCount: 1,
|
|
99
|
+
error: { kind: "outcome_unknown", mayHaveExecuted: true },
|
|
100
|
+
})
|
|
101
|
+
expect(l.calls()).toHaveLength(1)
|
|
102
|
+
expect(l.executed()).toBe(1)
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
it.each([408, 500, 502, 503])(
|
|
106
|
+
"is never re-sent after HTTP %i",
|
|
107
|
+
async (status) => {
|
|
108
|
+
const l = setupLedger()
|
|
109
|
+
l.refuseNext(status)
|
|
110
|
+
|
|
111
|
+
const failure = await rejection(
|
|
112
|
+
l.ledger.icrc1_transfer(transferArg(500n))
|
|
113
|
+
)
|
|
114
|
+
await pastResendDelays()
|
|
115
|
+
|
|
116
|
+
// The client cannot tell whether a timeout or a gateway error came
|
|
117
|
+
// before or after the call reached a canister, so it says "may have".
|
|
118
|
+
expect(failure).toMatchObject({
|
|
119
|
+
kind: "outcome_unknown",
|
|
120
|
+
httpStatus: status,
|
|
121
|
+
mayHaveExecuted: true,
|
|
122
|
+
})
|
|
123
|
+
expect(l.calls()).toHaveLength(1)
|
|
124
|
+
}
|
|
125
|
+
)
|
|
126
|
+
})
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trap: an update that was never delivered, given up on, or sent again as
|
|
3
|
+
* someone else.
|
|
4
|
+
*
|
|
5
|
+
* The mistake a hand-written integration makes, in either direction. A cautious
|
|
6
|
+
* one never sends an update twice, so a transfer the replica turned away (it
|
|
7
|
+
* was rate limited, HTTP 429, or the subnet was busy, reject code 2) fails for
|
|
8
|
+
* the user although a moment later it would have gone through. A careless one
|
|
9
|
+
* sends again after anything, and its re-send goes out as whoever is signed in
|
|
10
|
+
* by then.
|
|
11
|
+
*
|
|
12
|
+
* The guarantee: after the two failures that prove the update never got in,
|
|
13
|
+
* reject code 2 (SYS_TRANSIENT) and HTTP 429, the client sends it again, at
|
|
14
|
+
* most twice (after 300 ms, then 600 ms), and resolves with the reply of the
|
|
15
|
+
* attempt that went through, with the ledger booking it once. Each re-send is
|
|
16
|
+
* made for the caller of the first attempt: a user who switched account in
|
|
17
|
+
* the meantime does not pay for it, the call is cancelled instead. To the
|
|
18
|
+
* management canister `aaaaa-aa`, whose rejects prove nothing about what ran,
|
|
19
|
+
* an update is never sent again. Failures that do not prove non-delivery are
|
|
20
|
+
* in `update-not-resent.test.ts`.
|
|
21
|
+
*/
|
|
22
|
+
import { principal } from "@meantjs/schema"
|
|
23
|
+
import { MutationObserver } from "@tanstack/query-core"
|
|
24
|
+
import { afterAll, describe, expect, it, vi } from "vitest"
|
|
25
|
+
import * as management from "../fixtures/management.js"
|
|
26
|
+
import {
|
|
27
|
+
ALICE,
|
|
28
|
+
BOB,
|
|
29
|
+
FEE,
|
|
30
|
+
SHOP,
|
|
31
|
+
SIGNED_CALLS_TIMEOUT_MS,
|
|
32
|
+
disposeAll,
|
|
33
|
+
eventually,
|
|
34
|
+
pastResendDelays,
|
|
35
|
+
rejection,
|
|
36
|
+
setupLedger,
|
|
37
|
+
transferArg,
|
|
38
|
+
} from "./ledger.js"
|
|
39
|
+
|
|
40
|
+
vi.setConfig({ testTimeout: SIGNED_CALLS_TIMEOUT_MS })
|
|
41
|
+
afterAll(disposeAll)
|
|
42
|
+
|
|
43
|
+
// One flat suite, so that the waits of its tests overlap.
|
|
44
|
+
describe.concurrent("an update that was never delivered", () => {
|
|
45
|
+
it("is sent again once after reject code 2, and the ledger books it once", async () => {
|
|
46
|
+
const l = setupLedger()
|
|
47
|
+
let attempts = 0
|
|
48
|
+
l.onTransfer((apply) => {
|
|
49
|
+
attempts += 1
|
|
50
|
+
// The first attempt is turned away before any canister code ran.
|
|
51
|
+
return attempts === 1 ? l.reject(2, "the subnet is busy") : apply()
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
await expect(l.ledger.icrc1_transfer(transferArg(500n))).resolves.toBe(1n)
|
|
55
|
+
|
|
56
|
+
expect(attempts).toBe(2)
|
|
57
|
+
expect(l.calls()).toHaveLength(2)
|
|
58
|
+
expect(l.executed()).toBe(1)
|
|
59
|
+
expect(l.balances.get(ALICE)).toBe(1_000_000n - 500n - FEE)
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
it("is sent again once after HTTP 429, and the ledger books it once", async () => {
|
|
63
|
+
const l = setupLedger()
|
|
64
|
+
l.refuseNext(429)
|
|
65
|
+
|
|
66
|
+
await expect(l.ledger.icrc1_transfer(transferArg(500n))).resolves.toBe(1n)
|
|
67
|
+
|
|
68
|
+
expect(l.calls()).toMatchObject([
|
|
69
|
+
{ refused: expect.stringContaining("429") },
|
|
70
|
+
{ methodName: "icrc1_transfer", caller: ALICE },
|
|
71
|
+
])
|
|
72
|
+
expect(l.executed()).toBe(1)
|
|
73
|
+
expect(l.balances.get(ALICE)).toBe(1_000_000n - 500n - FEE)
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
it("is sent again at most twice, and then fails as not delivered", async () => {
|
|
77
|
+
const l = setupLedger()
|
|
78
|
+
l.refuseNext(429, 5)
|
|
79
|
+
|
|
80
|
+
const failure = await rejection(l.ledger.icrc1_transfer(transferArg(500n)))
|
|
81
|
+
await pastResendDelays()
|
|
82
|
+
|
|
83
|
+
expect(failure).toMatchObject({
|
|
84
|
+
kind: "not_delivered",
|
|
85
|
+
httpStatus: 429,
|
|
86
|
+
mayHaveExecuted: false,
|
|
87
|
+
})
|
|
88
|
+
// The first attempt and two re-sends; nothing booked.
|
|
89
|
+
expect(l.calls()).toHaveLength(3)
|
|
90
|
+
expect(l.executed()).toBe(0)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
it("is sent again by a mutation, which TanStack Query does not retry on top", async () => {
|
|
94
|
+
const l = setupLedger()
|
|
95
|
+
l.refuseNext(429)
|
|
96
|
+
const mutation = new MutationObserver(
|
|
97
|
+
l.client.queryClient,
|
|
98
|
+
l.client.mutationOptions(l.ledger, "icrc1_transfer")
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
await mutation.mutate(transferArg(500n))
|
|
102
|
+
|
|
103
|
+
// Two requests came from one attempt of the mutation: TanStack's retry is
|
|
104
|
+
// off, and the client's own re-send does not count as a failure.
|
|
105
|
+
expect(mutation.getCurrentResult()).toMatchObject({
|
|
106
|
+
status: "success",
|
|
107
|
+
data: 1n,
|
|
108
|
+
failureCount: 0,
|
|
109
|
+
})
|
|
110
|
+
expect(l.calls()).toHaveLength(2)
|
|
111
|
+
expect(l.executed()).toBe(1)
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
it("is not sent again as somebody else when the account is switched during the wait", async () => {
|
|
115
|
+
const l = setupLedger()
|
|
116
|
+
l.refuseNext(429)
|
|
117
|
+
|
|
118
|
+
const sending = rejection(l.ledger.icrc1_transfer(transferArg(500n)))
|
|
119
|
+
// The first attempt was refused; the client waits 300 ms to send again.
|
|
120
|
+
await eventually(() => expect(l.calls()).toHaveLength(1))
|
|
121
|
+
l.auth.switchTo(2)
|
|
122
|
+
const failure = await sending
|
|
123
|
+
await pastResendDelays()
|
|
124
|
+
|
|
125
|
+
expect(failure).toMatchObject({
|
|
126
|
+
kind: "cancelled",
|
|
127
|
+
mayHaveExecuted: false,
|
|
128
|
+
})
|
|
129
|
+
// Nothing was sent for Bob, who was signed in when the wait ended, and
|
|
130
|
+
// nothing again for Alice: the one request is the first, refused one.
|
|
131
|
+
expect(l.calls()).toMatchObject([
|
|
132
|
+
{ refused: expect.stringContaining("429") },
|
|
133
|
+
])
|
|
134
|
+
expect(l.calls().filter((call) => call.caller === BOB)).toEqual([])
|
|
135
|
+
expect(l.executed()).toBe(0)
|
|
136
|
+
expect(l.balances.get(BOB)).toBe(500_000n)
|
|
137
|
+
expect(l.balances.get(ALICE)).toBe(1_000_000n)
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it("is never sent again to the management canister, where reject code 2 proves nothing", async () => {
|
|
141
|
+
const l = setupLedger()
|
|
142
|
+
l.mock<management.Actor>(management.actor, "aaaaa-aa", {
|
|
143
|
+
start_canister: () => l.reject(2, "the subnet is busy"),
|
|
144
|
+
})
|
|
145
|
+
const ic = l.client.canister<management.Actor>(management.actor, {
|
|
146
|
+
id: "aaaaa-aa",
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const failure = await rejection(
|
|
150
|
+
ic.start_canister({ canister_id: principal(SHOP) })
|
|
151
|
+
)
|
|
152
|
+
await pastResendDelays()
|
|
153
|
+
|
|
154
|
+
// A stop_canister rejected with code 2 may have taken effect, so the call
|
|
155
|
+
// says "may have executed" and is left to the app.
|
|
156
|
+
expect(failure).toMatchObject({ kind: "rejected", mayHaveExecuted: true })
|
|
157
|
+
expect(l.calls()).toHaveLength(1)
|
|
158
|
+
})
|
|
159
|
+
})
|