@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,1053 @@
|
|
|
1
|
+
# IC Reactor: full guide for AI coding agents
|
|
2
|
+
|
|
3
|
+
> How to write correct IC Reactor code in an app: install, choose a setup, call
|
|
4
|
+
> canisters from React and from plain code, cache and invalidate, sign in,
|
|
5
|
+
> render on a server, handle errors, generate code, test, and the mistakes to
|
|
6
|
+
> avoid.
|
|
7
|
+
|
|
8
|
+
This guide is for code that uses the published packages. The short index of
|
|
9
|
+
the documentation is https://ic-reactor.b3pay.net/llms.txt. Both are published
|
|
10
|
+
from the main branch, so they can describe changes that no release carries
|
|
11
|
+
yet; the Unreleased section of
|
|
12
|
+
https://github.com/B3Pay/ic-reactor/blob/main/CHANGELOG.md lists those, with
|
|
13
|
+
migration hints for behaviour changes. Each installed package also carries a
|
|
14
|
+
guide for its own API at `node_modules/@ic-reactor/<package>/llms.txt`,
|
|
15
|
+
written for the version installed.
|
|
16
|
+
|
|
17
|
+
The rules below also ship as the `ic-reactor` Agent Skill. In Claude Code:
|
|
18
|
+
`/plugin marketplace add B3Pay/ic-reactor`, then
|
|
19
|
+
`/plugin install ic-reactor@ic-reactor`. For other agents:
|
|
20
|
+
`npx skills add B3Pay/ic-reactor --skill ic-reactor`.
|
|
21
|
+
|
|
22
|
+
Package versions this guide describes:
|
|
23
|
+
|
|
24
|
+
- `@ic-reactor/core`: `3.13.0`
|
|
25
|
+
- `@ic-reactor/react`: `3.13.0`
|
|
26
|
+
- `@ic-reactor/candid`: `3.13.0`
|
|
27
|
+
- `@ic-reactor/codegen`: `0.15.1`
|
|
28
|
+
- `@ic-reactor/cli`: `0.15.1`
|
|
29
|
+
- `@ic-reactor/vite-plugin`: `0.15.1`
|
|
30
|
+
- `@ic-reactor/parser`: `0.6.0`
|
|
31
|
+
|
|
32
|
+
## What IC Reactor Is
|
|
33
|
+
|
|
34
|
+
IC Reactor calls Internet Computer canisters with end-to-end TypeScript types
|
|
35
|
+
taken from the canister's Candid service type (`_SERVICE` in generated
|
|
36
|
+
declarations). It caches query results in TanStack Query, gives React hooks
|
|
37
|
+
and reusable query/mutation objects, unwraps Candid `Result` values into data
|
|
38
|
+
or a typed error, signs users in with Internet Identity, and can generate all
|
|
39
|
+
of this from `.did` files.
|
|
40
|
+
|
|
41
|
+
| Package | Use it for |
|
|
42
|
+
| ------------------------- | ------------------------------------------------------------------------------------------- |
|
|
43
|
+
| `@ic-reactor/core` | `ClientManager`, `Reactor`, `DisplayReactor`, errors, token helpers. No React. |
|
|
44
|
+
| `@ic-reactor/react` | Hooks, query/mutation factories, `defineReactor`, auth. Re-exports all of core. |
|
|
45
|
+
| `@ic-reactor/candid` | Reactors for canisters whose Candid is only known at run time; forms from Candid metadata. |
|
|
46
|
+
| `@ic-reactor/parser` | WASM Candid parser: `didToJs`, `didToTs`, `parseDid`, `validateIDL`. |
|
|
47
|
+
| `@ic-reactor/vite-plugin` | Generates declarations and hooks from `.did` files in a Vite app; local canister ids. |
|
|
48
|
+
| `@ic-reactor/cli` | The `ic-reactor` command: the same generation for non-Vite projects and CI. |
|
|
49
|
+
| `@ic-reactor/codegen` | The generation pipeline behind the CLI and the Vite plugin; apps rarely import it directly. |
|
|
50
|
+
|
|
51
|
+
## Install
|
|
52
|
+
|
|
53
|
+
React apps:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pnpm add @ic-reactor/react @icp-sdk/core @tanstack/react-query
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`@ic-reactor/react` needs `@tanstack/react-query` 5.90.2 or later and React 18
|
|
60
|
+
or later. IC Reactor needs TypeScript 5.7 or later: its declarations, and those
|
|
61
|
+
of `@icp-sdk/core`, use generic typed arrays such as `Uint8Array<ArrayBuffer>`,
|
|
62
|
+
which TypeScript 5.7 introduced. Import the runtime (`ClientManager`,
|
|
63
|
+
`Reactor`, ...) from `@ic-reactor/react` too: with the install above,
|
|
64
|
+
`@ic-reactor/core` is not a direct dependency, and pnpm does not let the app
|
|
65
|
+
import it.
|
|
66
|
+
|
|
67
|
+
Non-React apps:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
pnpm add @ic-reactor/core @icp-sdk/core @tanstack/query-core
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Internet Identity sign-in (optional; the auth classes ship in
|
|
74
|
+
`@ic-reactor/react`):
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pnpm add @icp-sdk/auth@^10
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
The `@icp-sdk/auth` peer is `^8.0.0 || ^10.0.0`. Install v10: it is the first
|
|
81
|
+
release to peer `@icp-sdk/core@^6`, so a strict `npm install` resolves the set
|
|
82
|
+
with no `overrides`. v7 and v9 are not supported; v9 still peers
|
|
83
|
+
`@icp-sdk/core@^5`. On v8, a strict `npm install` fails with `ERESOLVE`
|
|
84
|
+
(stale metadata, not an actual incompatibility). For npm on v8, add to
|
|
85
|
+
`package.json`:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{ "overrides": { "@icp-sdk/auth": { "@icp-sdk/core": "$@icp-sdk/core" } } }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
On v10, four constructor options are dropped with a warning because v10 has
|
|
92
|
+
no equivalent: `storage`, `keyType`, `idleOptions`, `identity`. `targets` on
|
|
93
|
+
`login()` is ignored by v10, so the delegation is NOT restricted to those
|
|
94
|
+
canisters; pin `^8` if you depend on canister-scoped delegations.
|
|
95
|
+
`maxTimeToIdle` on `login()` and `disableBrowserActivity` exist only on v10.
|
|
96
|
+
On v10 a custom `identityProvider` also needs `internetIdentityId`.
|
|
97
|
+
|
|
98
|
+
Dynamic Candid:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pnpm add @ic-reactor/candid @ic-reactor/core @icp-sdk/core @tanstack/query-core
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Code generation: `pnpm add -D @ic-reactor/vite-plugin` (Vite) or
|
|
105
|
+
`pnpm add -D @ic-reactor/cli` (anything else; Node.js 22.12 or later).
|
|
106
|
+
|
|
107
|
+
The snippets below import `canisterId`, `idlFactory` and the `_SERVICE` type
|
|
108
|
+
from `./declarations/backend`, which stands for your canister's declarations.
|
|
109
|
+
`idlFactory` comes from the generated `.js` file and `_SERVICE` from the
|
|
110
|
+
`.d.ts`. With the CLI or the Vite plugin those are
|
|
111
|
+
`src/declarations/<name>/declarations/<did>.js` and `.d.ts`, named after the
|
|
112
|
+
`.did` file; the folder's own `index.ts` exports the generated reactor and
|
|
113
|
+
hooks, not `idlFactory`.
|
|
114
|
+
`canisterId` is the canister's id as text: a constant, or a value from your
|
|
115
|
+
build's environment.
|
|
116
|
+
|
|
117
|
+
## Choose a Setup
|
|
118
|
+
|
|
119
|
+
| Your app | Use |
|
|
120
|
+
| ----------------------------------------------------------- | --------------------------------------------------------------------------------- |
|
|
121
|
+
| React SPA, a few canisters | `defineReactor(...)` or `defineDisplayReactor(...)` at module scope |
|
|
122
|
+
| React SPA, many canisters or a `.did` that changes often | `@ic-reactor/vite-plugin` (Vite) or `@ic-reactor/cli`, with `factories: true` |
|
|
123
|
+
| Server-rendered React (Next.js, any SSR) | `createReactorProvider(() => defineReactor(...))` in a `"use client"` module |
|
|
124
|
+
| React Server Component, server action, route handler | `ClientManager` + `Reactor` built inside the request; `fetchQuery` / `callMethod` |
|
|
125
|
+
| Construction order or dependency injection must be explicit | `ClientManager` + `Reactor` or `DisplayReactor` + `createActorHooks(reactor)` |
|
|
126
|
+
| No React (Node scripts, other frameworks, workers) | `@ic-reactor/core`: `ClientManager` + `Reactor`, imperative API |
|
|
127
|
+
| Several canisters with one interface (ICRC ledgers) | one reactor, plus `reactor.forCanister(canisterId)` for each other canister |
|
|
128
|
+
| Canister interface known only at run time | `@ic-reactor/candid` |
|
|
129
|
+
|
|
130
|
+
`Reactor` returns raw Candid values; `DisplayReactor` returns values a UI can
|
|
131
|
+
show and a form can send:
|
|
132
|
+
|
|
133
|
+
| Candid | `Reactor` | `DisplayReactor` |
|
|
134
|
+
| -------------------------------- | ----------------------------------- | ---------------------- |
|
|
135
|
+
| `nat`, `int`, `nat64`, `int64` | `bigint` | `string` |
|
|
136
|
+
| `nat8`..`nat32`, `int8`..`int32` | `number` | `number` |
|
|
137
|
+
| `principal` | `Principal` | `string` |
|
|
138
|
+
| `blob` | `Uint8Array` | hex `string`, no `0x` |
|
|
139
|
+
| `opt T` | `[] \| [T]` | `T \| undefined` |
|
|
140
|
+
| `variant { A: T }` | `{ A: T }` | `{ _type: "A", A: T }` |
|
|
141
|
+
| `variant { A }` (no value) | `{ A: null }` | `{ _type: "A" }` |
|
|
142
|
+
| `vec record { text; T }` | `[string, T][]` | `{ [key: string]: T }` |
|
|
143
|
+
| `Result` (`Ok`/`Err`) | Ok value, or throws `CanisterError` | same |
|
|
144
|
+
|
|
145
|
+
`DisplayReactor` adds zod (about 24 kB gzipped). Until the next major,
|
|
146
|
+
`defineReactor` includes it too because of its deprecated `display` flag; the
|
|
147
|
+
smallest setup is `createActorHooks(new Reactor(...))`.
|
|
148
|
+
|
|
149
|
+
## One-Call Setup
|
|
150
|
+
|
|
151
|
+
`defineReactor` creates the `QueryClient` (with `reactorRetry` as its query
|
|
152
|
+
retry), the `ClientManager`, the reactor and its hooks in one call.
|
|
153
|
+
`defineDisplayReactor` takes the same options and builds a `DisplayReactor`.
|
|
154
|
+
`defineReactor({ display: true })` is deprecated; never write it. Plain
|
|
155
|
+
`defineReactor` is not deprecated, although typescript-eslint's
|
|
156
|
+
`no-deprecated` also flags references such as `typeof defineReactor`: do not
|
|
157
|
+
switch a raw-value setup to `defineDisplayReactor` to silence it.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
// src/reactor.ts
|
|
161
|
+
import { defineDisplayReactor } from "@ic-reactor/react"
|
|
162
|
+
import { canisterId, idlFactory, type _SERVICE } from "./declarations/backend"
|
|
163
|
+
|
|
164
|
+
export const backendApp = defineDisplayReactor<_SERVICE>({
|
|
165
|
+
name: "backend",
|
|
166
|
+
idlFactory,
|
|
167
|
+
// Required unless the Vite plugin injects it on a local replica.
|
|
168
|
+
canisterId,
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
export const {
|
|
172
|
+
reactor: backend,
|
|
173
|
+
useActorQuery,
|
|
174
|
+
useActorMutation,
|
|
175
|
+
useAuth,
|
|
176
|
+
} = backendApp
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The result holds the six hooks of `createActorHooks` (`useActorQuery`,
|
|
180
|
+
`useActorSuspenseQuery`, `useActorInfiniteQuery`,
|
|
181
|
+
`useActorSuspenseInfiniteQuery`, `useActorMutation`, `useActorMethod`),
|
|
182
|
+
`useAuth`, `useAgentState`, `useUserPrincipal`, `useIdentityAttributes`, and
|
|
183
|
+
`reactor`, `clientManager`, `queryClient`, `authentication` and
|
|
184
|
+
`identityAttributes`. `authentication` and `identityAttributes` are lazy
|
|
185
|
+
getters, so the optional `@icp-sdk/auth` peer loads only once auth is used.
|
|
186
|
+
Without the peer the app still builds; webpack-family bundlers print one
|
|
187
|
+
`Module not found` warning for it, silenced with
|
|
188
|
+
`ignoreWarnings: [{ module: /@ic-reactor\/react/, message: /@icp-sdk\/auth/ }]`.
|
|
189
|
+
|
|
190
|
+
Without `canisterId`, the reactor reads `PUBLIC_CANISTER_ID:<name>` from the
|
|
191
|
+
`ic_env` cookie, which `ClientManager` trusts only on a local replica
|
|
192
|
+
(loopback and `localhost` hosts) unless `allowEnvConfig: true` is passed. On a
|
|
193
|
+
server or a deployed origin the constructor then throws
|
|
194
|
+
`canisterId is required for "backend"`.
|
|
195
|
+
|
|
196
|
+
A second canister shares the agent and the Internet Identity session when it
|
|
197
|
+
gets the first result's `authentication`:
|
|
198
|
+
|
|
199
|
+
```ts
|
|
200
|
+
// src/ledger.ts
|
|
201
|
+
import { defineReactor } from "@ic-reactor/react"
|
|
202
|
+
import {
|
|
203
|
+
canisterId as ledgerId,
|
|
204
|
+
idlFactory as ledgerIdl,
|
|
205
|
+
type _SERVICE as Ledger,
|
|
206
|
+
} from "./declarations/ledger"
|
|
207
|
+
import { backendApp } from "./reactor"
|
|
208
|
+
|
|
209
|
+
export const ledgerApp = defineReactor<Ledger>({
|
|
210
|
+
name: "ledger",
|
|
211
|
+
idlFactory: ledgerIdl,
|
|
212
|
+
canisterId: ledgerId,
|
|
213
|
+
authentication: backendApp.authentication,
|
|
214
|
+
})
|
|
215
|
+
export const ledger = ledgerApp.reactor
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
Passing `authentication` alone is enough; its `clientManager` is adopted.
|
|
219
|
+
Passing a different `clientManager` alongside it throws, because sign-in would
|
|
220
|
+
update one agent while the reactor calls through another. Do not build a
|
|
221
|
+
second `AuthenticationManager` for the same app.
|
|
222
|
+
|
|
223
|
+
## Manual Setup
|
|
224
|
+
|
|
225
|
+
Use this when construction order must be explicit:
|
|
226
|
+
|
|
227
|
+
```ts
|
|
228
|
+
// src/manual.ts
|
|
229
|
+
import {
|
|
230
|
+
ClientManager,
|
|
231
|
+
DisplayReactor,
|
|
232
|
+
createActorHooks,
|
|
233
|
+
reactorRetry,
|
|
234
|
+
} from "@ic-reactor/react"
|
|
235
|
+
import { QueryClient } from "@tanstack/react-query"
|
|
236
|
+
import { canisterId, idlFactory, type _SERVICE } from "./declarations/backend"
|
|
237
|
+
|
|
238
|
+
export const queryClient = new QueryClient({
|
|
239
|
+
defaultOptions: { queries: { retry: reactorRetry } },
|
|
240
|
+
})
|
|
241
|
+
export const clientManager = new ClientManager({ queryClient })
|
|
242
|
+
|
|
243
|
+
export const backend = new DisplayReactor<_SERVICE>({
|
|
244
|
+
clientManager,
|
|
245
|
+
idlFactory,
|
|
246
|
+
name: "backend",
|
|
247
|
+
canisterId,
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
export const { useActorQuery, useActorMutation } = createActorHooks(backend)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
Keep the `<_SERVICE>` type argument: without it every argument and result is
|
|
254
|
+
`unknown` (`any` on an untyped `Reactor`). A `QueryClient` you create keeps
|
|
255
|
+
TanStack's defaults unless you set `retry: reactorRetry` as above. The hooks
|
|
256
|
+
bind to the reactor's own `QueryClient`; a `QueryClientProvider` is optional.
|
|
257
|
+
|
|
258
|
+
## Queries and Mutations in Components
|
|
259
|
+
|
|
260
|
+
```tsx
|
|
261
|
+
import { skipToken } from "@ic-reactor/react"
|
|
262
|
+
import { useActorMutation, useActorQuery } from "./reactor"
|
|
263
|
+
|
|
264
|
+
export function ProfileCard({ userId }: { userId?: string }) {
|
|
265
|
+
const { data, isPending, error } = useActorQuery({
|
|
266
|
+
functionName: "get_profile",
|
|
267
|
+
// No call until the id exists; never `[userId!]`
|
|
268
|
+
args: userId ? [userId] : skipToken,
|
|
269
|
+
})
|
|
270
|
+
const rename = useActorMutation({
|
|
271
|
+
functionName: "update_profile",
|
|
272
|
+
// Refetch every get_profile query of this canister once it succeeds
|
|
273
|
+
invalidateQueries: [{ functionName: "get_profile" }],
|
|
274
|
+
// The canister returned Err: err.code is the variant name
|
|
275
|
+
onCanisterError: (err) => alert(`Not saved: ${err.code}`),
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
if (!userId) return <p>Sign in first</p>
|
|
279
|
+
if (isPending) return <p>Loading…</p>
|
|
280
|
+
if (error) return <p>{error.message}</p>
|
|
281
|
+
return (
|
|
282
|
+
<button
|
|
283
|
+
disabled={rename.isPending}
|
|
284
|
+
onClick={() => rename.mutate([{ name: "Ada" }])}
|
|
285
|
+
>
|
|
286
|
+
{data.name}: {data.likes} likes
|
|
287
|
+
</button>
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
- `args` is always the method's argument tuple (`[userId]`, `[]` or omitted
|
|
293
|
+
for no arguments), in the reactor's form (display strings above).
|
|
294
|
+
- `data` is the `Ok` payload of a `Result`: an `Err` becomes a `CanisterError`
|
|
295
|
+
in `error`, so never write `"Ok" in data`.
|
|
296
|
+
- A query hook takes TanStack's options too (`select`, `staleTime`,
|
|
297
|
+
`refetchInterval`, `enabled`, ...) and `callConfig` (`canisterId`, `agent`,
|
|
298
|
+
`effectiveCanisterId`) for one call.
|
|
299
|
+
- `skipToken` works in `useActorQuery`, `useActorInfiniteQuery` (as
|
|
300
|
+
`getArgs: owner ? (page) => [...] : skipToken`) and `createQueryFactory`.
|
|
301
|
+
The suspense hooks, `createQuery` and `createSuspenseQuery` do not take it.
|
|
302
|
+
Keep `enabled` for conditions that are not about missing args. Never call
|
|
303
|
+
`refetch()` on a skipped query.
|
|
304
|
+
- Call state-changing update methods only through `useActorMutation`,
|
|
305
|
+
`useActorMethod` or `createMutation`. A query hook or factory runs its method
|
|
306
|
+
again on every refetch (mount, window focus, reconnect, invalidation), and
|
|
307
|
+
each run of an update executes on the canister. A query of an update method
|
|
308
|
+
is fine only when repeating it is safe (a certified read).
|
|
309
|
+
- `useActorMethod` handles a query or an update method with one hook and an
|
|
310
|
+
imperative `call()`; use it only when that unified shape helps.
|
|
311
|
+
|
|
312
|
+
## Reusable Query and Mutation Objects
|
|
313
|
+
|
|
314
|
+
Define query and mutation objects once and use them in components, loaders,
|
|
315
|
+
actions and tests. In a client-only app they live at module scope; in a
|
|
316
|
+
server-rendered app build them inside the `createReactorProvider` factory.
|
|
317
|
+
|
|
318
|
+
```ts
|
|
319
|
+
// src/queries.ts
|
|
320
|
+
import {
|
|
321
|
+
createMutation,
|
|
322
|
+
createQuery,
|
|
323
|
+
createQueryFactory,
|
|
324
|
+
} from "@ic-reactor/react"
|
|
325
|
+
import { backend } from "./reactor"
|
|
326
|
+
|
|
327
|
+
export const postsQuery = createQuery(backend, { functionName: "get_posts" })
|
|
328
|
+
export const getPost = createQueryFactory(backend, { functionName: "get_post" })
|
|
329
|
+
|
|
330
|
+
export const createPost = createMutation(backend, {
|
|
331
|
+
functionName: "create_post",
|
|
332
|
+
invalidateQueries: [postsQuery, { functionName: "get_posts_count" }],
|
|
333
|
+
})
|
|
334
|
+
export const likePost = createMutation(backend, { functionName: "like_post" })
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
| Need | Use |
|
|
338
|
+
| ------------------------------ | ---------------------------------------------------------------------------- |
|
|
339
|
+
| A query method without args | `createQuery` (or `createSuspenseQuery`) |
|
|
340
|
+
| A query method whose args vary | `createQueryFactory` (or `createSuspenseQueryFactory`), called with the args |
|
|
341
|
+
| Paginated reads | `createInfiniteQuery`, `createSuspenseInfiniteQuery` (and their factories) |
|
|
342
|
+
| An update method | `createMutation` |
|
|
343
|
+
|
|
344
|
+
Inside React: `postsQuery.useQuery()`, `getPost([id]).useQuery()`,
|
|
345
|
+
`createPost.useMutation(options)`; `.useSuspenseQuery()`,
|
|
346
|
+
`.useInfiniteQuery()` and `.useSuspenseInfiniteQuery()` on the other kinds.
|
|
347
|
+
|
|
348
|
+
Outside React:
|
|
349
|
+
|
|
350
|
+
```ts
|
|
351
|
+
import { createPost, getPost, postsQuery } from "./queries"
|
|
352
|
+
|
|
353
|
+
export function postLoader(postId: string) {
|
|
354
|
+
// Cached and deduplicated; refetches as the new principal after a sign-in
|
|
355
|
+
return getPost([postId]).fetch()
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export function createPostAction(title: string) {
|
|
359
|
+
// Resolves with the Ok payload after the factory's invalidateQueries ran;
|
|
360
|
+
// an Err rejects with a CanisterError
|
|
361
|
+
return createPost.execute([title])
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
export function refreshPosts() {
|
|
365
|
+
return postsQuery.invalidate() // resolves once mounted queries refetched
|
|
366
|
+
}
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
A query object also has `.prefetch()`, `.getCacheData()`, `.setData()`,
|
|
370
|
+
`.getQueryKey()`, `.cancel()`, `.reset()` and `.optimisticUpdate()`
|
|
371
|
+
(`.prefetch()` and `.setData()` are not on infinite query objects). A query
|
|
372
|
+
factory function has `getQueryKey()` (the prefix of all its queries) and
|
|
373
|
+
`invalidate()`. `createQuery`, `createSuspenseQuery` and the query factories
|
|
374
|
+
take `callConfig` as the hooks do:
|
|
375
|
+
`createQuery(ledger, { functionName: "icrc1_symbol", callConfig: { canisterId: ckbtcId } })`.
|
|
376
|
+
|
|
377
|
+
Paginated reads take TanStack's `initialPageParam` and `getNextPageParam`,
|
|
378
|
+
plus `getArgs`, which turns a page param into the method's argument tuple:
|
|
379
|
+
|
|
380
|
+
```ts
|
|
381
|
+
import { createInfiniteQuery } from "@ic-reactor/react"
|
|
382
|
+
import { backend } from "./reactor"
|
|
383
|
+
|
|
384
|
+
// list_posts : (record { offset : nat; limit : nat })
|
|
385
|
+
// -> (record { posts : vec Post; next : opt nat }) query
|
|
386
|
+
export const postPages = createInfiniteQuery(backend, {
|
|
387
|
+
functionName: "list_posts",
|
|
388
|
+
initialPageParam: "0", // a DisplayReactor takes a nat as text
|
|
389
|
+
getArgs: (offset) => [{ offset, limit: "20" }] as const,
|
|
390
|
+
getNextPageParam: (lastPage) => lastPage.next, // undefined: no next page
|
|
391
|
+
})
|
|
392
|
+
|
|
393
|
+
// In a component:
|
|
394
|
+
// const { data, fetchNextPage, hasNextPage } = postPages.useInfiniteQuery()
|
|
395
|
+
// const posts = data?.pages.flatMap((page) => page.posts) ?? []
|
|
396
|
+
// In a loader: await postPages.fetch()
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
Keep `as const` on what `getArgs` returns: without it the args are inferred
|
|
400
|
+
as an array rather than a tuple, and `createInfiniteQuery` rejects them.
|
|
401
|
+
`useActorInfiniteQuery` takes the same fields.
|
|
402
|
+
|
|
403
|
+
## Cache Invalidation and Optimistic Updates
|
|
404
|
+
|
|
405
|
+
A mutation's `invalidateQueries` (on `createMutation`, `.useMutation()`,
|
|
406
|
+
`useActorMutation` and `useActorMethod`) takes, per entry:
|
|
407
|
+
|
|
408
|
+
- a query object: `[postsQuery]`;
|
|
409
|
+
- a query factory, covering every args instance: `[getPost]`;
|
|
410
|
+
- a method of the mutation's own reactor, type-checked:
|
|
411
|
+
`{ functionName: "get_post" }` for every query of the method, or
|
|
412
|
+
`{ functionName: "get_post", args: [id] }` for one. It is keyed at the
|
|
413
|
+
canister the mutation was sent to.
|
|
414
|
+
|
|
415
|
+
The invalidation is awaited before `onSuccess`, so `onSuccess` reads fresh
|
|
416
|
+
data. Never hand-write a key such as `["get_posts"]`: every key starts with
|
|
417
|
+
the canister id, so it matches nothing. A key is the canister id and the
|
|
418
|
+
method name, then segments for a non-default transform (a `DisplayReactor`),
|
|
419
|
+
another agent or effective target given in `callConfig`, and the args. Build
|
|
420
|
+
one with `reactor.generateQueryKey(...)` or `query.getQueryKey()` if you need
|
|
421
|
+
it, and never add the canister id to a `queryKey` yourself.
|
|
422
|
+
|
|
423
|
+
`query.invalidate()`, a factory's `invalidate()` and
|
|
424
|
+
`reactor.invalidateQueries(...)` return a `Promise` that resolves once the
|
|
425
|
+
active matches have refetched (a failed refetch does not reject it). Prefix a
|
|
426
|
+
call you do not wait for with `void`.
|
|
427
|
+
|
|
428
|
+
For optimistic UI, return the query's `optimisticUpdate(updater)` from
|
|
429
|
+
`onMutate`, roll back in `onError`, and invalidate in `onSettled`:
|
|
430
|
+
|
|
431
|
+
```tsx
|
|
432
|
+
import { getPost, likePost } from "./queries"
|
|
433
|
+
|
|
434
|
+
export function LikeButton({ postId }: { postId: string }) {
|
|
435
|
+
const { data: post } = getPost([postId]).useQuery()
|
|
436
|
+
const like = likePost.useMutation({
|
|
437
|
+
// Show the new count at once; undo it if the call fails
|
|
438
|
+
onMutate: ([id]) =>
|
|
439
|
+
getPost([id]).optimisticUpdate((cached) => ({
|
|
440
|
+
...cached,
|
|
441
|
+
likes: (BigInt(cached.likes) + 1n).toString(),
|
|
442
|
+
})),
|
|
443
|
+
onError: (_error, _args, update) => update?.rollback(),
|
|
444
|
+
onSettled: (_data, _error, [id]) => getPost([id]).invalidate(),
|
|
445
|
+
})
|
|
446
|
+
return (
|
|
447
|
+
<button onClick={() => like.mutate([postId])}>
|
|
448
|
+
{post?.title}: {post?.likes}
|
|
449
|
+
</button>
|
|
450
|
+
)
|
|
451
|
+
}
|
|
452
|
+
```
|
|
453
|
+
|
|
454
|
+
The updater gets the cached raw (pre-`select`) value and is skipped when
|
|
455
|
+
nothing is cached. `rollback()` does nothing after a sign-in or sign-out,
|
|
456
|
+
since the snapshot was the previous principal's. Do not hand-roll
|
|
457
|
+
`cancelQueries` / `setQueryData` snapshots. When you need the `QueryClient`
|
|
458
|
+
itself, use `reactor.queryClient`; `useQueryClient()` throws without a
|
|
459
|
+
`QueryClientProvider`.
|
|
460
|
+
|
|
461
|
+
## Inside React vs Outside React
|
|
462
|
+
|
|
463
|
+
Only call hooks inside React components or custom hooks: `useActorQuery`,
|
|
464
|
+
`useActorMutation`, `useActorMethod`, `useAuth`, `.useQuery()`,
|
|
465
|
+
`.useMutation()`, `.useSuspenseQuery()`, `.useInfiniteQuery()`. Loaders,
|
|
466
|
+
actions, services, scripts and non-hook tests use:
|
|
467
|
+
|
|
468
|
+
- query objects: `.fetch()`, `.prefetch()`, `.invalidate()`,
|
|
469
|
+
`.getCacheData()`, `.setData()`, `.cancel()`, `.reset()`,
|
|
470
|
+
`.optimisticUpdate()`
|
|
471
|
+
- mutation objects: `.execute(args)`
|
|
472
|
+
- reactors: `.fetchQuery()`, `.getQueryData()`, `.invalidateQueries()`,
|
|
473
|
+
`.callMethod()`
|
|
474
|
+
|
|
475
|
+
`query.fetch()` and `reactor.fetchQuery()` fetch again, at most three times,
|
|
476
|
+
when a sign-in or sign-out switches the principal mid-fetch (after that they
|
|
477
|
+
reject with a `CallError`). Wrap a fetch you write against the `QueryClient`
|
|
478
|
+
yourself (`queryClient.fetchQuery`, `fetchInfiniteQuery`, `ensureQueryData`) in
|
|
479
|
+
`clientManager.fetchAcrossIdentitySwitch(() => ...)`: unwrapped, a switch can
|
|
480
|
+
resolve it with the previous principal's cached data. A query object's
|
|
481
|
+
`.prefetch()` fetches again the same way and never rejects, and a query
|
|
482
|
+
method's `call()` and `refetch()` from `useActorMethod` resolve with the new
|
|
483
|
+
principal's answer (`undefined` if that fetch fails). Loaders and services
|
|
484
|
+
still use `query.fetch()` or `reactor.fetchQuery()`: hooks belong in
|
|
485
|
+
components.
|
|
486
|
+
|
|
487
|
+
## Several Canisters of One Interface
|
|
488
|
+
|
|
489
|
+
For a multi-token wallet, build one reactor and derive the others:
|
|
490
|
+
`reactor.forCanister(canisterId)` returns a memoized sibling of the same class
|
|
491
|
+
on the same `ClientManager`, with the same interface and validators, whose
|
|
492
|
+
calls and query keys use its own canister. The same id always gives the same
|
|
493
|
+
object, so it is safe in render and in `useMemo` dependencies. A single query
|
|
494
|
+
can instead take `callConfig: { canisterId }`.
|
|
495
|
+
|
|
496
|
+
```tsx
|
|
497
|
+
import { useMemo } from "react"
|
|
498
|
+
import {
|
|
499
|
+
createActorHooks,
|
|
500
|
+
formatTokenAmount,
|
|
501
|
+
type ReactorArgsOf,
|
|
502
|
+
} from "@ic-reactor/react"
|
|
503
|
+
import { ledger } from "./ledger"
|
|
504
|
+
|
|
505
|
+
type Account = ReactorArgsOf<typeof ledger, "icrc1_balance_of">[0]
|
|
506
|
+
|
|
507
|
+
export function Balance({
|
|
508
|
+
tokenId,
|
|
509
|
+
account,
|
|
510
|
+
}: {
|
|
511
|
+
tokenId: string
|
|
512
|
+
account: Account
|
|
513
|
+
}) {
|
|
514
|
+
const token = useMemo(
|
|
515
|
+
() => createActorHooks(ledger.forCanister(tokenId)),
|
|
516
|
+
[tokenId]
|
|
517
|
+
)
|
|
518
|
+
const { data: balance } = token.useActorQuery({
|
|
519
|
+
functionName: "icrc1_balance_of",
|
|
520
|
+
args: [account],
|
|
521
|
+
})
|
|
522
|
+
const { data: decimals } = token.useActorQuery({
|
|
523
|
+
functionName: "icrc1_decimals",
|
|
524
|
+
})
|
|
525
|
+
if (balance === undefined || decimals === undefined) return null
|
|
526
|
+
return (
|
|
527
|
+
<span>
|
|
528
|
+
{formatTokenAmount(balance, decimals, { maxFractionDigits: 4 })}
|
|
529
|
+
</span>
|
|
530
|
+
)
|
|
531
|
+
}
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
Do not retarget a shared reactor with `setCanisterId`, and do not build one
|
|
535
|
+
reactor per canister by hand.
|
|
536
|
+
|
|
537
|
+
## Types From a Reactor
|
|
538
|
+
|
|
539
|
+
Read types off the reactor instead of copying shapes:
|
|
540
|
+
|
|
541
|
+
- `ReactorArgsOf<typeof ledger, "icrc1_transfer">[0]`: the first argument in
|
|
542
|
+
the reactor's form (raw or display)
|
|
543
|
+
- `ReactorDataOf<typeof ledger, "icrc1_transfer">`: what `callMethod`, a
|
|
544
|
+
mutation and `execute()` resolve with
|
|
545
|
+
- `ReactorErrorOf<typeof ledger, "icrc1_transfer">`: a hook's `error` type
|
|
546
|
+
- `ServiceOf<typeof ledger>`, `TransformOf<typeof ledger>`
|
|
547
|
+
|
|
548
|
+
They take a reactor instance's type (any `Reactor`, `DisplayReactor` or
|
|
549
|
+
subclass), not a query or mutation object.
|
|
550
|
+
|
|
551
|
+
## Token Amounts and Principals
|
|
552
|
+
|
|
553
|
+
Ledger amounts are base units (e8s for ICP): a `bigint` from a `Reactor`,
|
|
554
|
+
integer text from a `DisplayReactor`. Never convert them with `Number`,
|
|
555
|
+
`parseFloat`, `Math.pow` or `toFixed`: `Math.floor(Number("0.29") * 10 ** 8)`
|
|
556
|
+
is 28999999.
|
|
557
|
+
|
|
558
|
+
```ts
|
|
559
|
+
import { isPrincipalText, parseTokenAmount } from "@ic-reactor/react"
|
|
560
|
+
import { Principal } from "@icp-sdk/core/principal"
|
|
561
|
+
import { ledger } from "./ledger"
|
|
562
|
+
|
|
563
|
+
export async function send(to: string, text: string, decimals: number) {
|
|
564
|
+
if (!isPrincipalText(to.trim())) throw new Error("Not a principal")
|
|
565
|
+
const amount = parseTokenAmount(text, decimals) // "0.29", 8 -> 29000000n
|
|
566
|
+
return ledger.callMethod({
|
|
567
|
+
functionName: "icrc1_transfer",
|
|
568
|
+
args: [
|
|
569
|
+
{
|
|
570
|
+
to: { owner: Principal.fromText(to.trim()), subaccount: [] },
|
|
571
|
+
amount,
|
|
572
|
+
fee: [],
|
|
573
|
+
memo: [],
|
|
574
|
+
from_subaccount: [],
|
|
575
|
+
created_at_time: [],
|
|
576
|
+
},
|
|
577
|
+
],
|
|
578
|
+
})
|
|
579
|
+
}
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
- `formatTokenAmount(value, decimals, options?)` takes the value and
|
|
583
|
+
`icrc1_decimals` as a query returns them. Options: `maxFractionDigits`,
|
|
584
|
+
`minFractionDigits`, `trimTrailingZeros`, `roundingMode`, `locale`,
|
|
585
|
+
`useGrouping`. It truncates past `maxFractionDigits` unless
|
|
586
|
+
`roundingMode: "halfExpand"`, and writes plain text (`"1234.5"`) unless given
|
|
587
|
+
a `locale`.
|
|
588
|
+
- `parseTokenAmount(text, decimals)` returns a `bigint`. It throws a
|
|
589
|
+
`TypeError` for text that is not digits with at most one `.` and a
|
|
590
|
+
`RangeError` for more fraction digits than the token has or a negative
|
|
591
|
+
amount (`{ allowNegative: true }` for an `int`). Send `amount.toString()` to
|
|
592
|
+
a `DisplayReactor`.
|
|
593
|
+
- `isPrincipalText(text.trim())` validates typed principal text; do not wrap
|
|
594
|
+
`Principal.fromText` in a `try`. A `DisplayReactor` takes the text, a
|
|
595
|
+
`Reactor` takes `Principal.fromText(text)`.
|
|
596
|
+
- `jsonToString(value)` prints a raw result, with bigints as digits,
|
|
597
|
+
principals as text and blobs as hex.
|
|
598
|
+
|
|
599
|
+
All four are plain functions in core, re-exported by `@ic-reactor/react` and by
|
|
600
|
+
its `react-server` entry.
|
|
601
|
+
|
|
602
|
+
## Server Rendering (SSR and RSC)
|
|
603
|
+
|
|
604
|
+
A reactor owns its `QueryClient`, and query keys carry no caller principal. A
|
|
605
|
+
reactor at module scope on a server is therefore one cache shared by every
|
|
606
|
+
request, and a caller-scoped result (`get_my_profile`, a balance of self) is
|
|
607
|
+
served to the next visitor. `AuthenticationManager` signs in on its
|
|
608
|
+
`ClientManager`'s agent, so it belongs in the same request as that manager.
|
|
609
|
+
Build the reactor, `ClientManager`, `AuthenticationManager` and any query or
|
|
610
|
+
mutation objects inside the request. A bare `defineReactor(...)` in a module
|
|
611
|
+
body is still module scope.
|
|
612
|
+
|
|
613
|
+
Client components in a server-rendered app use `createReactorProvider`:
|
|
614
|
+
|
|
615
|
+
```tsx
|
|
616
|
+
"use client"
|
|
617
|
+
import {
|
|
618
|
+
createReactorProvider,
|
|
619
|
+
defineReactor,
|
|
620
|
+
skipToken,
|
|
621
|
+
} from "@ic-reactor/react"
|
|
622
|
+
import { canisterId, idlFactory, type _SERVICE } from "../declarations/backend"
|
|
623
|
+
|
|
624
|
+
// The factory runs once per mounted provider: once per request on a server
|
|
625
|
+
export const { ReactorProvider, useReactor } = createReactorProvider(() =>
|
|
626
|
+
defineReactor<_SERVICE>({ name: "backend", idlFactory, canisterId })
|
|
627
|
+
)
|
|
628
|
+
|
|
629
|
+
// app/layout.tsx (a Server Component) wraps the app:
|
|
630
|
+
// <ReactorProvider>{children}</ReactorProvider>
|
|
631
|
+
|
|
632
|
+
export function MyProfile() {
|
|
633
|
+
const { useActorQuery, useAuth } = useReactor()
|
|
634
|
+
const { isAuthenticated, isAuthenticating, login } = useAuth()
|
|
635
|
+
const { data } = useActorQuery({
|
|
636
|
+
functionName: "get_my_profile",
|
|
637
|
+
args: isAuthenticated ? [] : skipToken,
|
|
638
|
+
})
|
|
639
|
+
// true until the stored session has been checked, and in every server render
|
|
640
|
+
if (isAuthenticating) return <p>Checking session…</p>
|
|
641
|
+
if (!isAuthenticated)
|
|
642
|
+
return <button onClick={() => void login()}>Sign in</button>
|
|
643
|
+
return <h1>{data?.name}</h1>
|
|
644
|
+
}
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
- `ReactorProvider` and `useReactor` are what the app destructures, not
|
|
648
|
+
package exports. Call `createReactorProvider` at module scope, never inside a
|
|
649
|
+
component, and take hooks from `useReactor()` rather than calling
|
|
650
|
+
`createActorHooks` or `createAuthHooks` in a component.
|
|
651
|
+
- The factory may return a record (`{ backend, ledger, posts }`, with query
|
|
652
|
+
objects built inside it), read with `useReactor("ledger")`. `useReactor()` is
|
|
653
|
+
typed as the factory's return: never write `as any` hook forwarders.
|
|
654
|
+
- The factory receives the provider's props other than `children`, read once
|
|
655
|
+
per mount; give the provider a new `key` to build again.
|
|
656
|
+
- On unmount the provider disposes the `AuthenticationManager`s built for its
|
|
657
|
+
value and leaves managers built elsewhere alone. When the value holds
|
|
658
|
+
exactly one `QueryClient` (every reactor in it built on one
|
|
659
|
+
`ClientManager`, as passing `authentication` on does), it renders a
|
|
660
|
+
`QueryClientProvider` for it, so `useQueryClient()` and `HydrationBoundary`
|
|
661
|
+
below it use the cache the hooks fill; pass `{ queryClientProvider: false }`
|
|
662
|
+
to keep your own.
|
|
663
|
+
- A provider mounted by a transition (router navigation, `startTransition`)
|
|
664
|
+
needs a `<Suspense>` boundary inside it around suspending components.
|
|
665
|
+
|
|
666
|
+
A React Server Component, server action or route handler imports the core
|
|
667
|
+
runtime from `@ic-reactor/react`: its `react-server` export condition resolves
|
|
668
|
+
to an entry that loads no React.
|
|
669
|
+
|
|
670
|
+
```tsx
|
|
671
|
+
// app/Supply.tsx: a React Server Component
|
|
672
|
+
import { ClientManager, Reactor, formatTokenAmount } from "@ic-reactor/react"
|
|
673
|
+
import { QueryClient } from "@tanstack/react-query"
|
|
674
|
+
import { canisterId, idlFactory, type _SERVICE } from "../declarations/ledger"
|
|
675
|
+
|
|
676
|
+
export default async function Supply() {
|
|
677
|
+
// Built inside the request: a module-scope reactor would share its cache
|
|
678
|
+
// with every visitor
|
|
679
|
+
const clientManager = new ClientManager({
|
|
680
|
+
queryClient: new QueryClient(),
|
|
681
|
+
agentOptions: { host: "https://ic0.app" },
|
|
682
|
+
})
|
|
683
|
+
const ledger = new Reactor<_SERVICE>({
|
|
684
|
+
name: "ledger",
|
|
685
|
+
clientManager,
|
|
686
|
+
idlFactory,
|
|
687
|
+
canisterId,
|
|
688
|
+
})
|
|
689
|
+
const [supply, decimals] = await Promise.all([
|
|
690
|
+
ledger.fetchQuery({ functionName: "icrc1_total_supply" }),
|
|
691
|
+
ledger.fetchQuery({ functionName: "icrc1_decimals" }),
|
|
692
|
+
])
|
|
693
|
+
return <p>{formatTokenAmount(supply, decimals, { maxFractionDigits: 0 })}</p>
|
|
694
|
+
}
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
Hooks, `defineReactor`, `defineDisplayReactor`, `createReactorProvider`,
|
|
698
|
+
`createActorHooks`, the query/mutation factories, `skipToken`, the auth classes
|
|
699
|
+
and the identity-attribute helpers are missing exports in the `react-server`
|
|
700
|
+
graph (`next build`: "Export X doesn't exist in target module"). When the RSC
|
|
701
|
+
bundler ignores the `react-server` condition, add `@ic-reactor/core` as a
|
|
702
|
+
dependency and import the runtime from it instead. Generated canister modules
|
|
703
|
+
(hooks and `factories: true` objects) import hooks, so a server component must
|
|
704
|
+
not import them.
|
|
705
|
+
|
|
706
|
+
Module scope stays correct in client-only apps.
|
|
707
|
+
|
|
708
|
+
## Authentication
|
|
709
|
+
|
|
710
|
+
`defineReactor` already returns `useAuth`, `useAgentState`,
|
|
711
|
+
`useUserPrincipal`, `useIdentityAttributes`, `authentication` and
|
|
712
|
+
`identityAttributes`. With a manual setup:
|
|
713
|
+
|
|
714
|
+
```ts
|
|
715
|
+
import {
|
|
716
|
+
AuthenticationManager,
|
|
717
|
+
IdentityAttributesManager,
|
|
718
|
+
createAuthHooks,
|
|
719
|
+
createIdentityAttributeHooks,
|
|
720
|
+
hexToUint8Array,
|
|
721
|
+
} from "@ic-reactor/react"
|
|
722
|
+
import { backend, clientManager } from "./manual"
|
|
723
|
+
|
|
724
|
+
export const authentication = new AuthenticationManager({ clientManager })
|
|
725
|
+
export const { useAuth, useAgentState, useUserPrincipal } =
|
|
726
|
+
createAuthHooks(authentication)
|
|
727
|
+
|
|
728
|
+
export const identityAttributes = new IdentityAttributesManager(authentication)
|
|
729
|
+
export const { useIdentityAttributes } =
|
|
730
|
+
createIdentityAttributeHooks(identityAttributes)
|
|
731
|
+
|
|
732
|
+
// In a click handler: pass the nonce as a callback so the popup opens within
|
|
733
|
+
// the user gesture. A DisplayReactor returns a blob as hex.
|
|
734
|
+
export const requestEmail = () =>
|
|
735
|
+
identityAttributes.request({
|
|
736
|
+
keys: ["email"],
|
|
737
|
+
nonce: async () =>
|
|
738
|
+
hexToUint8Array(
|
|
739
|
+
await backend.callMethod({ functionName: "register_begin" })
|
|
740
|
+
),
|
|
741
|
+
})
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
- `createAuthHooks` takes an `AuthenticationManager`, never a `ClientManager`,
|
|
745
|
+
and returns exactly `useAuth`, `useAgentState` and `useUserPrincipal`.
|
|
746
|
+
`useIdentityAttributes` comes only from `createIdentityAttributeHooks`.
|
|
747
|
+
- `useAuth()` returns `login`, `logout`, `authenticate`, `isAuthenticated`,
|
|
748
|
+
`isAuthenticating`, `principal`, `identity` and `error`. Read the principal
|
|
749
|
+
from it (or `useUserPrincipal()`), not from `identity.getPrincipal()` in
|
|
750
|
+
your own state.
|
|
751
|
+
- `useAuth()` reports `isAuthenticating: true` from the first render, and in
|
|
752
|
+
every server render, until the first session restore settles (a failed
|
|
753
|
+
restore counts as settled). Guard a protected route with
|
|
754
|
+
`if (isAuthenticating)` before `if (!isAuthenticated)`.
|
|
755
|
+
- Awaiting a nonce before `request()` ends the user gesture, and the browser
|
|
756
|
+
blocks the Internet Identity window; pass the nonce as a callback. With a raw
|
|
757
|
+
`Reactor`, the blob is already a `Uint8Array`:
|
|
758
|
+
`nonce: () => backend.callMethod({ ... })`.
|
|
759
|
+
- `authentication.dispose()` releases the auth client the manager built. It
|
|
760
|
+
does not sign out, and a later `login()` builds a new client, so it is safe
|
|
761
|
+
in a StrictMode effect cleanup. `createReactorProvider` calls it for you.
|
|
762
|
+
- On `@icp-sdk/auth` v10 a manager follows sign-outs and account switches made
|
|
763
|
+
in other tabs.
|
|
764
|
+
- A sign-in, sign-out or account switch cancels the fetches in flight of
|
|
765
|
+
every canister the `ClientManager` has a reactor for, removes their inactive
|
|
766
|
+
cache entries and refetches mounted queries as the new principal. A renewed
|
|
767
|
+
delegation for the same principal keeps the cache.
|
|
768
|
+
|
|
769
|
+
Local Internet Identity:
|
|
770
|
+
`localInternetIdentityProvider(port, canisterId?, authorizePath?)` builds
|
|
771
|
+
`http://<canisterId>.localhost:<port>/authorize`, and
|
|
772
|
+
`authentication.prepareClient()` probes the canister for `/authorize` or the
|
|
773
|
+
legacy `/#authorize`. Only Internet Identity builds up to `release-2026-03-16`
|
|
774
|
+
serve a local frontend; a login that hangs on the gateway's "Response
|
|
775
|
+
Verification Error" page is an unsupported build.
|
|
776
|
+
|
|
777
|
+
## Errors and Retries
|
|
778
|
+
|
|
779
|
+
| Error | Source | Handle with |
|
|
780
|
+
| ----------------- | ----------------------------------------------------------- | ---------------------------------------------------------------- |
|
|
781
|
+
| `CanisterError` | the canister returned `Err` | `onCanisterError` on mutations, `isCanisterError(e)` elsewhere |
|
|
782
|
+
| `CallError` | network, agent, certificate, a trap, a validator that threw | `onError`, `isCallError(e)` |
|
|
783
|
+
| `ValidationError` | a `DisplayReactor` validator refused the arguments | `isValidationError(e)`, `mapValidationErrors(e)` for form fields |
|
|
784
|
+
|
|
785
|
+
```ts
|
|
786
|
+
import {
|
|
787
|
+
isCallError,
|
|
788
|
+
isCanisterError,
|
|
789
|
+
isValidationError,
|
|
790
|
+
} from "@ic-reactor/react"
|
|
791
|
+
import { backend } from "./reactor"
|
|
792
|
+
|
|
793
|
+
export async function rename(name: string) {
|
|
794
|
+
try {
|
|
795
|
+
return await backend.callMethod({
|
|
796
|
+
functionName: "update_profile",
|
|
797
|
+
args: [{ name }],
|
|
798
|
+
})
|
|
799
|
+
} catch (error) {
|
|
800
|
+
if (isCanisterError(error)) {
|
|
801
|
+
// The canister's Err value: error.err, variant name: error.code
|
|
802
|
+
return `Refused: ${error.code}`
|
|
803
|
+
}
|
|
804
|
+
if (isValidationError(error)) return `Invalid: ${error.message}`
|
|
805
|
+
if (isCallError(error)) return `Network or agent failure: ${error.message}`
|
|
806
|
+
throw error
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
```
|
|
810
|
+
|
|
811
|
+
`CanisterError` carries the canister's error value on `.err`, not `.detail`,
|
|
812
|
+
`.data` or `.message`. `.code` is the variant key, or the `code` field when the
|
|
813
|
+
payload has the API shape (`code`, `message`, `details`). `.details` is that
|
|
814
|
+
payload's `details` field as decoded, never a `Map`. The guards also recognise
|
|
815
|
+
errors from another copy of the package; prefer them to `instanceof`.
|
|
816
|
+
|
|
817
|
+
Retries:
|
|
818
|
+
|
|
819
|
+
- `reactorRetry` (predicate `isRetryableReactorError`) is the query retry of
|
|
820
|
+
the `QueryClient` that `defineReactor` and `defineDisplayReactor` create. In
|
|
821
|
+
a browser it retries transport, certificate, HTTP 5xx/408/429 and
|
|
822
|
+
`SysTransient`/`SysUnknown` failures up to 3 times, and never a
|
|
823
|
+
`CanisterError`, a `ValidationError`, an encode/decode failure or another
|
|
824
|
+
HTTP 4xx (an expired delegation's 400, a 403).
|
|
825
|
+
- A query of an update method with no `retry` of its own retries only a
|
|
826
|
+
`SysTransient` rejection, within the `QueryClient`'s retry default.
|
|
827
|
+
- Mutations retry nothing unless `retry` is set on them or as the
|
|
828
|
+
`QueryClient`'s `mutations.retry` default, which also reaches
|
|
829
|
+
`createMutation().execute()` and `useActorMethod` update calls. Each retry
|
|
830
|
+
of an update is a new call the canister runs, so use
|
|
831
|
+
`retry: reactorUpdateRetry` (predicate `isRetryableUpdateError`), which
|
|
832
|
+
retries only `SysTransient`, never a number.
|
|
833
|
+
- `reactorRetry` and `reactorUpdateRetry` return `false` wherever there is no
|
|
834
|
+
`window` (Node scripts, server rendering, web workers), as TanStack Query
|
|
835
|
+
retries nothing on a server by default; the agent still resends a failed
|
|
836
|
+
HTTP request itself (3 times by default). For query retries in a Node
|
|
837
|
+
script, pass the predicate:
|
|
838
|
+
`retry: (count, error) => count < 3 && isRetryableReactorError(error)`.
|
|
839
|
+
|
|
840
|
+
## Code Generation
|
|
841
|
+
|
|
842
|
+
Use generated code when there are many canisters, `.did` files change often,
|
|
843
|
+
or you want consistent typed exports.
|
|
844
|
+
|
|
845
|
+
| Tool | Use when |
|
|
846
|
+
| ------------------------- | --------------------------------------------------------------------- |
|
|
847
|
+
| `@ic-reactor/vite-plugin` | Vite app: generates on start and on `.did` changes, injects local ids |
|
|
848
|
+
| `@ic-reactor/cli` | Any other project, CI, or explicit `ic-reactor generate` runs |
|
|
849
|
+
| `@ic-reactor/codegen` | Custom tooling only (`runCanisterPipeline`) |
|
|
850
|
+
|
|
851
|
+
```ts
|
|
852
|
+
// vite.config.ts
|
|
853
|
+
import { defineConfig, loadEnv } from "vite"
|
|
854
|
+
import { icReactor } from "@ic-reactor/vite-plugin"
|
|
855
|
+
|
|
856
|
+
export default defineConfig(({ mode }) => {
|
|
857
|
+
const env = loadEnv(mode, process.cwd(), "")
|
|
858
|
+
return {
|
|
859
|
+
plugins: [
|
|
860
|
+
icReactor({
|
|
861
|
+
canisters: [
|
|
862
|
+
{
|
|
863
|
+
name: "backend",
|
|
864
|
+
didFile: "./backend/backend.did",
|
|
865
|
+
factories: true,
|
|
866
|
+
// Written into the generated reactor, and used under `vite dev`
|
|
867
|
+
// too in place of the local id: set it per environment
|
|
868
|
+
canisterId: env.CANISTER_ID_BACKEND,
|
|
869
|
+
},
|
|
870
|
+
],
|
|
871
|
+
}),
|
|
872
|
+
],
|
|
873
|
+
}
|
|
874
|
+
})
|
|
875
|
+
```
|
|
876
|
+
|
|
877
|
+
The CLI reads the same per-canister fields from `ic-reactor.json`
|
|
878
|
+
(`pnpm exec ic-reactor init`, then `pnpm exec ic-reactor generate`). Each
|
|
879
|
+
canister's output directory holds:
|
|
880
|
+
|
|
881
|
+
- `declarations/`: the IDL factory and `_SERVICE` types;
|
|
882
|
+
- `index.generated.ts` (managed, rewritten on every run): the reactor
|
|
883
|
+
(`backendReactor`, a `DisplayReactor` unless `mode` names another class) and
|
|
884
|
+
six bound hooks named after the canister (`useBackendQuery`,
|
|
885
|
+
`useBackendMutation`, ...);
|
|
886
|
+
- `index.factories.generated.ts` (managed, only with `factories: true` and
|
|
887
|
+
`target: "react"`): a `<method>Query` per query method (`createQuery`, or
|
|
888
|
+
`createQueryFactory` when it takes args) and a `<method>Mutation`
|
|
889
|
+
(`createMutation`) per update or oneway method, each `/* @__PURE__ */` so
|
|
890
|
+
unused ones are tree-shaken;
|
|
891
|
+
- `index.ts` (created once, yours to edit): re-exports `index.generated.ts`,
|
|
892
|
+
and the factories file with `factories: true`. Put custom factories,
|
|
893
|
+
invalidation wiring and helpers here; an export with the same name replaces
|
|
894
|
+
the generated one.
|
|
895
|
+
|
|
896
|
+
Never edit the managed files. Prefer `factories: true` over hand-written
|
|
897
|
+
per-method factory modules. The generated modules are module scope, so they
|
|
898
|
+
are client-only; on a server build the reactor per request. Without
|
|
899
|
+
`canisterId`, the generated reactor reads its id from the `ic_env` cookie,
|
|
900
|
+
which works only on a local replica (or with `allowEnvConfig: true`), so
|
|
901
|
+
importing it throws `canisterId is required` in Node, during SSR and on a
|
|
902
|
+
deployed origin.
|
|
903
|
+
|
|
904
|
+
## Dynamic Candid and the Parser
|
|
905
|
+
|
|
906
|
+
Use `@ic-reactor/candid` for explorers, dev tools and forms over canisters
|
|
907
|
+
whose interface is known only at run time: `CandidAdapter` fetches and parses
|
|
908
|
+
Candid, `CandidReactor` / `CandidDisplayReactor` call it dynamically, and
|
|
909
|
+
`MetadataReactor` / `MetadataDisplayReactor` produce form and result metadata.
|
|
910
|
+
`forCanister` works on all four. `@ic-reactor/parser` is a dependency of
|
|
911
|
+
candid and loads on first parse. To parse Candid yourself, import it from its
|
|
912
|
+
single entrypoint:
|
|
913
|
+
|
|
914
|
+
```ts
|
|
915
|
+
import init, { didToJs, parseDid, validateIDL } from "@ic-reactor/parser"
|
|
916
|
+
|
|
917
|
+
await init() // required on the web build (browsers, bundlers); a no-op on Node
|
|
918
|
+
const schema = parseDid("service : { greet : (text) -> (text) query }")
|
|
919
|
+
console.log(schema.service?.methods) // [{ name: "greet", mode: "query", ... }]
|
|
920
|
+
```
|
|
921
|
+
|
|
922
|
+
There are no `/web`, `/nodejs` or `/bundler` subpaths; conditional exports pick
|
|
923
|
+
the browser or Node build. Do not deep-import `dist/`. `validateIDL` throws the
|
|
924
|
+
parser's message for invalid Candid instead of returning `false`, and
|
|
925
|
+
`didToJs` / `didToTs` return source strings.
|
|
926
|
+
|
|
927
|
+
## Testing
|
|
928
|
+
|
|
929
|
+
Run the real reactor and hooks against the fake replica from
|
|
930
|
+
`@ic-reactor/react/testing` (`@ic-reactor/core/testing` without React). Do not
|
|
931
|
+
stub a reactor with `as unknown as Reactor`, and do not hard-code query keys in
|
|
932
|
+
tests.
|
|
933
|
+
|
|
934
|
+
```ts
|
|
935
|
+
import { afterEach, beforeEach, expect, it } from "vitest"
|
|
936
|
+
import { QueryClient } from "@tanstack/react-query"
|
|
937
|
+
import { ClientManager, Reactor, isCanisterError } from "@ic-reactor/react"
|
|
938
|
+
import {
|
|
939
|
+
createTestCanister,
|
|
940
|
+
installFakeReplica,
|
|
941
|
+
type FakeReplica,
|
|
942
|
+
} from "@ic-reactor/react/testing"
|
|
943
|
+
import { canisterId, idlFactory, type _SERVICE } from "./declarations/backend"
|
|
944
|
+
|
|
945
|
+
let replica: FakeReplica
|
|
946
|
+
let backend: Reactor<_SERVICE>
|
|
947
|
+
|
|
948
|
+
beforeEach(() => {
|
|
949
|
+
// Install the fake before any ClientManager or agent is built
|
|
950
|
+
replica = installFakeReplica({
|
|
951
|
+
canisters: {
|
|
952
|
+
[canisterId]: createTestCanister<_SERVICE>(idlFactory, {
|
|
953
|
+
// Handlers get the decoded args and { caller } and return raw Candid
|
|
954
|
+
greet: ([name]) => `Hello, ${name}!`,
|
|
955
|
+
update_profile: () => ({ Err: { NotFound: null } }),
|
|
956
|
+
}),
|
|
957
|
+
},
|
|
958
|
+
})
|
|
959
|
+
backend = new Reactor<_SERVICE>({
|
|
960
|
+
clientManager: new ClientManager({
|
|
961
|
+
queryClient: new QueryClient(),
|
|
962
|
+
agentOptions: { host: replica.host },
|
|
963
|
+
}),
|
|
964
|
+
name: "backend",
|
|
965
|
+
canisterId,
|
|
966
|
+
idlFactory,
|
|
967
|
+
})
|
|
968
|
+
})
|
|
969
|
+
afterEach(() => replica.restore())
|
|
970
|
+
|
|
971
|
+
it("greets", async () => {
|
|
972
|
+
await expect(
|
|
973
|
+
backend.fetchQuery({ functionName: "greet", args: ["Ada"] })
|
|
974
|
+
).resolves.toBe("Hello, Ada!")
|
|
975
|
+
})
|
|
976
|
+
|
|
977
|
+
it("turns an Err into a CanisterError", async () => {
|
|
978
|
+
const error = await backend
|
|
979
|
+
.callMethod({ functionName: "update_profile", args: [{ name: "" }] })
|
|
980
|
+
.catch((e: unknown) => e)
|
|
981
|
+
expect(isCanisterError(error) && error.code).toBe("NotFound")
|
|
982
|
+
})
|
|
983
|
+
```
|
|
984
|
+
|
|
985
|
+
- A handler returns `{ Err: ... }` for a `CanisterError`, and throws for the
|
|
986
|
+
`CallError` of a trap.
|
|
987
|
+
- For a module-scope `defineReactor(...)`, install the fake at the top of the
|
|
988
|
+
test file (or in a setup file) and `await import(...)` the components after
|
|
989
|
+
it. With no `host` on either side, both use the page origin in jsdom or
|
|
990
|
+
happy-dom. Reset a shared reactor between tests with `queryClient.clear()`.
|
|
991
|
+
- Generated code is module scope too: its entry imports your `clientManager`.
|
|
992
|
+
Import `idlFactory` and `_SERVICE` from the canister's `declarations/`
|
|
993
|
+
folder (it builds nothing), key the fake by the `canisterId` the generator
|
|
994
|
+
wrote into `index.generated.ts`, and `await import(...)` the entry and the
|
|
995
|
+
components after the fake. Vitest runs `vite.config.ts` (unless a
|
|
996
|
+
`vitest.config.ts` replaces it), so the Vite plugin generates again in mode
|
|
997
|
+
`test`: set the variable a `loadEnv` `canisterId` comes from in
|
|
998
|
+
`.env.test`, or importing the regenerated entry throws
|
|
999
|
+
`canisterId is required`.
|
|
1000
|
+
- Test a signed-in user with `clientManager.updateAgent(identity)`, for
|
|
1001
|
+
example `Ed25519KeyIdentity.generate()`; the kit does not simulate Internet
|
|
1002
|
+
Identity.
|
|
1003
|
+
- The kit signs with `@noble/curves`, an optional peer that `@icp-sdk/core`
|
|
1004
|
+
already installs; add it as a devDependency under a strict install.
|
|
1005
|
+
|
|
1006
|
+
## Do Not
|
|
1007
|
+
|
|
1008
|
+
- Call hooks in loaders, actions, services or scripts.
|
|
1009
|
+
- Create hooks (`createActorHooks`, `createAuthHooks`) on every render: build
|
|
1010
|
+
them at module scope, in a `useMemo` keyed by what they depend on, or take
|
|
1011
|
+
them from `useReactor()`.
|
|
1012
|
+
- Put a state-changing update method in a query hook or query factory
|
|
1013
|
+
(`useActorQuery`, `createQuery`, `createQueryFactory`, the infinite and
|
|
1014
|
+
suspense variants); use a mutation.
|
|
1015
|
+
- Give an update mutation, `useActorMethod` or a `mutations.retry` default a
|
|
1016
|
+
numeric `retry`; use `retry: reactorUpdateRetry`.
|
|
1017
|
+
- Hand-write query keys (`["get_posts"]`) or add the canister id to a
|
|
1018
|
+
`queryKey`; pass query objects, query factories or
|
|
1019
|
+
`{ functionName, args? }` to `invalidateQueries`.
|
|
1020
|
+
- Write `args: [userId!]`, placeholder args, or `as any` plus `enabled` for
|
|
1021
|
+
args not known yet; pass `skipToken`.
|
|
1022
|
+
- Check `"Ok" in data`: a `Result` is already unwrapped, and an `Err` throws.
|
|
1023
|
+
- Do token math with `Number(x) / 10 ** decimals`, `parseFloat`, `Math.pow` or
|
|
1024
|
+
`toFixed`; use `formatTokenAmount` / `parseTokenAmount`.
|
|
1025
|
+
- Retarget a shared reactor with `setCanisterId`; use
|
|
1026
|
+
`reactor.forCanister(canisterId)`.
|
|
1027
|
+
- Write `defineReactor({ display: true })`; use `defineDisplayReactor`.
|
|
1028
|
+
- Build a reactor, `ClientManager` or `AuthenticationManager` at module scope
|
|
1029
|
+
in a server-rendered app, or import hooks into a server component.
|
|
1030
|
+
- Pass a `ClientManager` to `createAuthHooks`, or build a second
|
|
1031
|
+
`AuthenticationManager` instead of passing `authentication` on.
|
|
1032
|
+
- Use `useQueryClient()` for cache writes, or hand-roll `setQueryData`
|
|
1033
|
+
snapshots for optimistic UI; use `reactor.queryClient` and
|
|
1034
|
+
`optimisticUpdate`.
|
|
1035
|
+
- Stub a `Reactor` with `as unknown as Reactor` in tests.
|
|
1036
|
+
- Edit `index.generated.ts` or `index.factories.generated.ts`, or repeat
|
|
1037
|
+
per-method factories that `factories: true` generates.
|
|
1038
|
+
- Leave out the `<_SERVICE>` type argument, or cast hooks to `any`.
|
|
1039
|
+
|
|
1040
|
+
## Links
|
|
1041
|
+
|
|
1042
|
+
- Documentation: https://ic-reactor.b3pay.net/v3/
|
|
1043
|
+
- Index for agents: https://ic-reactor.b3pay.net/llms.txt
|
|
1044
|
+
- Changelog: https://github.com/B3Pay/ic-reactor/blob/main/CHANGELOG.md
|
|
1045
|
+
- Agent skill: https://github.com/B3Pay/ic-reactor/tree/main/skill-packages/ic-reactor
|
|
1046
|
+
- React setup: https://ic-reactor.b3pay.net/v3/framework/react-setup.md
|
|
1047
|
+
- Queries: https://ic-reactor.b3pay.net/v3/framework/queries.md
|
|
1048
|
+
- Mutations: https://ic-reactor.b3pay.net/v3/framework/mutations.md
|
|
1049
|
+
- Authentication: https://ic-reactor.b3pay.net/v3/guides/authentication.md
|
|
1050
|
+
- Error handling: https://ic-reactor.b3pay.net/v3/guides/error-handling.md
|
|
1051
|
+
- Testing: https://ic-reactor.b3pay.net/v3/guides/testing.md
|
|
1052
|
+
- createReactorProvider: https://ic-reactor.b3pay.net/v3/reference/createReactorProvider.md
|
|
1053
|
+
- Next.js App Router example: https://ic-reactor.b3pay.net/v3/examples/nextjs-app-router.md
|