@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,961 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one call path every canister call takes: a direct call on a canister
|
|
3
|
+
* object, a read's query function, a mutation's function and a func
|
|
4
|
+
* reference's function all end in {@link invoke}.
|
|
5
|
+
*
|
|
6
|
+
* In order, nothing sent until the last step:
|
|
7
|
+
*
|
|
8
|
+
* 1. the client: every call on a disposed client rejects `cancelled`
|
|
9
|
+
* (`client_disposed`), whatever else is wrong with it. This comes first
|
|
10
|
+
* because a disposed client calls as the anonymous principal, so the
|
|
11
|
+
* caller check below would refuse a write as `unauthenticated`;
|
|
12
|
+
* 2. the target: an unresolved `{ name }` rejects `invalid_args`
|
|
13
|
+
* (`canister_id_unresolved`); a composite query on a certified canister
|
|
14
|
+
* rejects `invalid_args` (`no_certified_path`);
|
|
15
|
+
* 3. the caller: an update or a oneway by a caller who is not signed in
|
|
16
|
+
* rejects `unauthenticated` (`anonymous_write`);
|
|
17
|
+
* 4. the arguments: Candid-encoded with the method's schemas, or
|
|
18
|
+
* `invalid_args` with the codec's `$`-rooted issues; a call to `aaaaa-aa`
|
|
19
|
+
* also needs a row of the effective canister id table (`management.ts`).
|
|
20
|
+
* A mutation that re-sends a write `client.resendOf()` offered must be
|
|
21
|
+
* that write: the same method, canister id and bytes, or it is refused
|
|
22
|
+
* (`target_changed`, `arg_changed`). A re-send refused at any step, or
|
|
23
|
+
* turned away when sent, rejects `outcome_unknown` with the refusal's
|
|
24
|
+
* code instead, since the write it re-sends may have run (see
|
|
25
|
+
* {@link invoke});
|
|
26
|
+
* 5. the send, as the caller's own agent: `agent.query` for a read,
|
|
27
|
+
* `agent.call` for an update, a oneway and a certified read. Every failure
|
|
28
|
+
* is classified (`classifyError`), and a call re-sends itself only when
|
|
29
|
+
* the classified failure allows it (see {@link CallRequest.resend}). The
|
|
30
|
+
* agent is asked for before every attempt, and a client disposed since
|
|
31
|
+
* the first step cancels the attempt (`ClientInternals.agentFor`);
|
|
32
|
+
* 6. the reply: decoded with the method's result schemas (`invalid_reply` if
|
|
33
|
+
* it does not decode), collapsed as the generator types it (0 results:
|
|
34
|
+
* `undefined`, 1: the value, n: the tuple), and unwrapped when the one
|
|
35
|
+
* result is an `Ok`/`Err` variant (`canister_err` with the `Err` payload).
|
|
36
|
+
*
|
|
37
|
+
* ## Why updates use `agent.call`, not `agent.update` (a recorded design call)
|
|
38
|
+
*
|
|
39
|
+
* DECISIONS Q8 chose `agent.update()`. The client builds its agents with
|
|
40
|
+
* `retryTimes: 0` (see `ClientInternals.agentFor`), so one failed `read_state`
|
|
41
|
+
* while `agent.update()` polls for a slow update ends the call as
|
|
42
|
+
* `outcome_unknown`, and `agent.update()` keeps the request id to itself, so
|
|
43
|
+
* nothing can poll again. This path sends with `agent.call()`, reads the
|
|
44
|
+
* response the way `agent.update()` does in `@icp-sdk/core` 6.1 (a certified
|
|
45
|
+
* v4 reply, an uncertified v2 reject, or 202 and then polling), and polls the
|
|
46
|
+
* same request id with `pollForResponse`. A transient polling failure (a lost
|
|
47
|
+
* connection, HTTP 408, 429 or 5xx) is followed by another poll of the same
|
|
48
|
+
* request id, at most {@link REPOLL_DELAYS_MS} times: `read_state` executes
|
|
49
|
+
* nothing, so asking again can never run the update twice. It also tells the
|
|
50
|
+
* classifier exactly when the request may have got in (`accepted`): only after
|
|
51
|
+
* `agent.call()` returned.
|
|
52
|
+
*
|
|
53
|
+
* Internal: not exported from the package entry.
|
|
54
|
+
*
|
|
55
|
+
* @module
|
|
56
|
+
*/
|
|
57
|
+
import {
|
|
58
|
+
resolveSchema,
|
|
59
|
+
type AnyFieldSchema,
|
|
60
|
+
type MethodMode,
|
|
61
|
+
type ServiceMethod,
|
|
62
|
+
} from "@meantjs/schema"
|
|
63
|
+
import {
|
|
64
|
+
decodeArgs,
|
|
65
|
+
encodeArgs,
|
|
66
|
+
type EncodeResult,
|
|
67
|
+
} from "@meantjs/schema/codec"
|
|
68
|
+
import { isResultSchema } from "@meantjs/schema/validate"
|
|
69
|
+
import {
|
|
70
|
+
Certificate,
|
|
71
|
+
CertifiedRejectErrorCode,
|
|
72
|
+
Expiry,
|
|
73
|
+
QueryResponseStatus,
|
|
74
|
+
RejectError,
|
|
75
|
+
UncertifiedRejectErrorCode,
|
|
76
|
+
UncertifiedRejectUpdateErrorCode,
|
|
77
|
+
UnexpectedErrorCode,
|
|
78
|
+
UnknownError,
|
|
79
|
+
defaultStrategy,
|
|
80
|
+
isV2ResponseBody,
|
|
81
|
+
isV4ResponseBody,
|
|
82
|
+
lookupResultToBuffer,
|
|
83
|
+
pollForResponse,
|
|
84
|
+
type HttpAgent,
|
|
85
|
+
type PollStrategy,
|
|
86
|
+
type ReplicaRejectCode,
|
|
87
|
+
type RequestId,
|
|
88
|
+
type SubmitResponse,
|
|
89
|
+
} from "@icp-sdk/core/agent"
|
|
90
|
+
import { Principal } from "@icp-sdk/core/principal"
|
|
91
|
+
import type { Caller, ClientInternals } from "./client.js"
|
|
92
|
+
import {
|
|
93
|
+
UPDATE_RESEND_DELAYS_MS,
|
|
94
|
+
asRecord,
|
|
95
|
+
classifyError,
|
|
96
|
+
createReactorError,
|
|
97
|
+
fromCanister,
|
|
98
|
+
withExpiry,
|
|
99
|
+
invalidReplyError,
|
|
100
|
+
isReactorError,
|
|
101
|
+
isRetryable,
|
|
102
|
+
readHttpStatus,
|
|
103
|
+
resendDidNotRun,
|
|
104
|
+
retryUpdate,
|
|
105
|
+
timedOutError,
|
|
106
|
+
type CallMode,
|
|
107
|
+
type ReactorError,
|
|
108
|
+
} from "./errors.js"
|
|
109
|
+
import { toHex } from "./keys.js"
|
|
110
|
+
import {
|
|
111
|
+
MANAGEMENT_CANISTER,
|
|
112
|
+
effectiveCanisterId,
|
|
113
|
+
isRoutable,
|
|
114
|
+
} from "./management.js"
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* How long to wait before each extra poll of an update whose `read_state`
|
|
118
|
+
* failed for a transient reason. Its length is the most extra polls one call
|
|
119
|
+
* gets; the agent's own polling strategy (five minutes in all) still bounds
|
|
120
|
+
* the whole wait.
|
|
121
|
+
*/
|
|
122
|
+
export const REPOLL_DELAYS_MS: readonly number[] = [500, 1_000, 2_000]
|
|
123
|
+
|
|
124
|
+
/** A method of a service or a func reference, read once. */
|
|
125
|
+
export interface PreparedMethod {
|
|
126
|
+
readonly name: string
|
|
127
|
+
readonly mode: MethodMode
|
|
128
|
+
readonly args: readonly AnyFieldSchema[]
|
|
129
|
+
readonly results: readonly AnyFieldSchema[]
|
|
130
|
+
/** The tags of the one result when it is an `Ok`/`Err` variant, else absent. */
|
|
131
|
+
readonly result?: { readonly ok: string; readonly err: string }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Reads what a call needs of a method once: whether its one result is a
|
|
136
|
+
* result variant (the rule of `isResultSchema`) and how its arms are spelled.
|
|
137
|
+
*/
|
|
138
|
+
export function prepareMethod(method: ServiceMethod): PreparedMethod {
|
|
139
|
+
const { name, mode, args, results } = method
|
|
140
|
+
if (results.length !== 1 || !isResultSchema(results[0])) {
|
|
141
|
+
return { name, mode, args, results }
|
|
142
|
+
}
|
|
143
|
+
const node = resolveSchema(results[0])
|
|
144
|
+
const arms = node.kind === "variant" ? node.arms : {}
|
|
145
|
+
return {
|
|
146
|
+
name,
|
|
147
|
+
mode,
|
|
148
|
+
args,
|
|
149
|
+
results,
|
|
150
|
+
result: "Ok" in arms ? { ok: "Ok", err: "Err" } : { ok: "ok", err: "err" },
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Whether a method changes state: an update or a oneway. */
|
|
155
|
+
export const isWrite = (mode: MethodMode): boolean =>
|
|
156
|
+
mode === "update" || mode === "oneway"
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* The argument list for `vars`, the builders' argument convention (DECISIONS
|
|
160
|
+
* Q3): nothing, the one value, or the tuple. Anything else is passed on as it
|
|
161
|
+
* is, for the codec to refuse with an issue that says what it got.
|
|
162
|
+
*/
|
|
163
|
+
export function valuesOf(
|
|
164
|
+
method: PreparedMethod,
|
|
165
|
+
vars: unknown
|
|
166
|
+
): readonly unknown[] {
|
|
167
|
+
const arity = method.args.length
|
|
168
|
+
if (arity === 1) return [vars]
|
|
169
|
+
if (arity === 0 && vars === undefined) return []
|
|
170
|
+
return Array.isArray(vars) ? vars : [vars]
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/** The `invalid_args` of arguments that do not encode, with the codec's issues. */
|
|
174
|
+
export function notEncoded(
|
|
175
|
+
where: { readonly method: string; readonly canisterId: string },
|
|
176
|
+
encoded: Extract<EncodeResult, { ok: false }>
|
|
177
|
+
): ReactorError {
|
|
178
|
+
const [first] = encoded.issues
|
|
179
|
+
return createReactorError("invalid_args", {
|
|
180
|
+
...where,
|
|
181
|
+
reason: first
|
|
182
|
+
? `the arguments could not be encoded: ${first.path}: ${first.message}`
|
|
183
|
+
: undefined,
|
|
184
|
+
issues: encoded.issues,
|
|
185
|
+
})
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Where a call goes: a canister id, or the key segment of an unresolved name and why. */
|
|
189
|
+
export type ResolvedTarget =
|
|
190
|
+
| { readonly ok: true; readonly id: string }
|
|
191
|
+
| { readonly ok: false; readonly slot: string; readonly message: string }
|
|
192
|
+
|
|
193
|
+
/** One call. */
|
|
194
|
+
export interface CallRequest {
|
|
195
|
+
readonly method: PreparedMethod
|
|
196
|
+
readonly target: ResolvedTarget
|
|
197
|
+
/** Whether the canister is certified: its `query` methods go out as replicated calls. */
|
|
198
|
+
readonly certified: boolean
|
|
199
|
+
/**
|
|
200
|
+
* Who calls: the caller current when a direct call or a mutation runs, or
|
|
201
|
+
* the one captured with a read's key.
|
|
202
|
+
*/
|
|
203
|
+
readonly caller: Caller
|
|
204
|
+
/** The arguments, as a list. */
|
|
205
|
+
readonly values: readonly unknown[]
|
|
206
|
+
/**
|
|
207
|
+
* Their encoding: made by the builder for the key, or by `run` when it
|
|
208
|
+
* encodes them, which sets it here. A write kept in {@link UNKNOWN_WRITES}
|
|
209
|
+
* therefore holds the bytes it sent, and `client.resendOf()` sends those
|
|
210
|
+
* again, whatever became of the argument objects since.
|
|
211
|
+
*/
|
|
212
|
+
encoded?: EncodeResult
|
|
213
|
+
/**
|
|
214
|
+
* The effective canister id a call to `aaaaa-aa` is routed by: derived from
|
|
215
|
+
* the arguments by `run` the first time, which sets it here, so a kept
|
|
216
|
+
* write is routed where its bytes went, as {@link encoded} is.
|
|
217
|
+
*/
|
|
218
|
+
effective?: string
|
|
219
|
+
/**
|
|
220
|
+
* The write this call sends again: set by a mutation given an argument
|
|
221
|
+
* `client.resendOf()` offered. The call is refused, and nothing sent,
|
|
222
|
+
* unless it is that write ({@link differs}).
|
|
223
|
+
*/
|
|
224
|
+
readonly again?: CallRequest
|
|
225
|
+
/**
|
|
226
|
+
* On a write no other re-sends ({@link firstOf}), set by `invoke`: the
|
|
227
|
+
* latest `expiresAt` of the errors it and its re-sends were kept in
|
|
228
|
+
* {@link UNKNOWN_WRITES} with. Until then a send of it may still start, so
|
|
229
|
+
* a re-send of it that did not run carries it on ({@link keptUnknown}).
|
|
230
|
+
*/
|
|
231
|
+
expiresAt?: bigint
|
|
232
|
+
/**
|
|
233
|
+
* The signal of a query function. Once it aborts, the call is not sent, or
|
|
234
|
+
* stops waiting for the HTTP answer to its request or polling for its
|
|
235
|
+
* answer, and rejects `cancelled`.
|
|
236
|
+
*/
|
|
237
|
+
readonly signal?: AbortSignal
|
|
238
|
+
/**
|
|
239
|
+
* How long the caller waits, from the start of the call to its answer: a
|
|
240
|
+
* direct call's `timeoutMs` (on its canister target), or a mutation's.
|
|
241
|
+
* When it passes, the call stops waiting as if {@link signal} had aborted,
|
|
242
|
+
* and rejects with code `"timeout"`: `cancelled` with nothing executed,
|
|
243
|
+
* except while an attempt of an update (or a oneway) is out, which may
|
|
244
|
+
* have been delivered, and for a re-send of an offered write ({@link again})
|
|
245
|
+
* at any point, whose earlier attempt may have run: `outcome_unknown` with
|
|
246
|
+
* `mayHaveExecuted: true`, the same as the polling bound. Nothing is ever sent again for it: the
|
|
247
|
+
* re-sends of {@link resend} only follow a failure that proves the attempt
|
|
248
|
+
* was not delivered, and the wait before one is a wait of this call.
|
|
249
|
+
*/
|
|
250
|
+
readonly timeoutMs?: number
|
|
251
|
+
/**
|
|
252
|
+
* Whether this call re-sends itself after a failure that allows it: `true`
|
|
253
|
+
* for a direct call and a mutation, `false` for a query function, whose
|
|
254
|
+
* retries are TanStack's (`retry`), which knows about focus, network and
|
|
255
|
+
* unmounting.
|
|
256
|
+
*
|
|
257
|
+
* An update (or oneway) is re-sent only when `retryUpdate` says so: reject
|
|
258
|
+
* code 2 or HTTP 429 before the request got in, at most twice. A call to
|
|
259
|
+
* `aaaaa-aa` is never re-sent: its reject codes prove nothing about what ran
|
|
260
|
+
* (see `classifyError`), so its every outcome is left to the app. A read is
|
|
261
|
+
* re-sent after any failure the classifier marks retryable, at most twice.
|
|
262
|
+
*/
|
|
263
|
+
readonly resend: boolean
|
|
264
|
+
/**
|
|
265
|
+
* The canister object the call is made on, absent for a func reference.
|
|
266
|
+
* Every error the call rejects with is marked with it (`fromCanister`), so
|
|
267
|
+
* that `isReactorError(error, canister, method)` can tell its own call's
|
|
268
|
+
* errors from any other's.
|
|
269
|
+
*/
|
|
270
|
+
readonly canister?: object
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* The request of each write that failed with `mayHaveExecuted: true`, by the
|
|
275
|
+
* error it rejected with: what `client.resendOf()` offers to send again, as
|
|
276
|
+
* the same caller, to the same canister, with the same arguments.
|
|
277
|
+
*/
|
|
278
|
+
export const UNKNOWN_WRITES = new WeakMap<object, CallRequest>()
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* What makes a call of `method` to `target` with `encoded` arguments another
|
|
282
|
+
* write than `sent`, a write that went out: `"target"` for another method or
|
|
283
|
+
* canister id (a `{ name }` the `ic_env` cookie now maps elsewhere),
|
|
284
|
+
* `"arg"` for other bytes (an argument changed after it was sent), and
|
|
285
|
+
* `undefined` when it is the same write. A re-send of another write is a new
|
|
286
|
+
* write that no dedupe key has been seen for.
|
|
287
|
+
*/
|
|
288
|
+
export const differs = (
|
|
289
|
+
sent: CallRequest,
|
|
290
|
+
method: PreparedMethod,
|
|
291
|
+
target: ResolvedTarget,
|
|
292
|
+
encoded: EncodeResult
|
|
293
|
+
): "target" | "arg" | undefined =>
|
|
294
|
+
method.name !== sent.method.name ||
|
|
295
|
+
!target.ok ||
|
|
296
|
+
target.id !== (sent.target as { id: string }).id
|
|
297
|
+
? "target"
|
|
298
|
+
: !encoded.ok ||
|
|
299
|
+
toHex(encoded.bytes) !==
|
|
300
|
+
toHex((sent.encoded as { bytes: Uint8Array }).bytes)
|
|
301
|
+
? "arg"
|
|
302
|
+
: undefined
|
|
303
|
+
|
|
304
|
+
/** The write `request` re-sends, at the end of a chain of re-sends, or `request` itself. */
|
|
305
|
+
const firstOf = (request: CallRequest): CallRequest =>
|
|
306
|
+
request.again === undefined ? request : firstOf(request.again)
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* The error of a re-send of `again` that did not run, `failure` proving only
|
|
310
|
+
* that: `outcome_unknown` ({@link resendDidNotRun}), since an earlier send of
|
|
311
|
+
* the write may have run, kept as `again` in {@link UNKNOWN_WRITES}, so that
|
|
312
|
+
* `client.resendOf()` offers that write again.
|
|
313
|
+
*/
|
|
314
|
+
export const keptUnknown = (
|
|
315
|
+
again: CallRequest,
|
|
316
|
+
failure: ReactorError | ReactorError<unknown>
|
|
317
|
+
): ReactorError => {
|
|
318
|
+
const kept = resendDidNotRun(failure, firstOf(again).expiresAt)
|
|
319
|
+
UNKNOWN_WRITES.set(kept, again)
|
|
320
|
+
return kept
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Runs one call through the steps of this module. Resolves with the reply as
|
|
325
|
+
* the generated `Actor` types it (unwrapped), and rejects with a
|
|
326
|
+
* `ReactorError`, marked with the request's canister when it has one. A write
|
|
327
|
+
* whose outcome is unknown is kept in {@link UNKNOWN_WRITES}.
|
|
328
|
+
*
|
|
329
|
+
* A re-send of an offered write ({@link CallRequest.again}) that did not run,
|
|
330
|
+
* and that the canister did not answer, rejects {@link keptUnknown}.
|
|
331
|
+
*/
|
|
332
|
+
export const invoke = (
|
|
333
|
+
internals: ClientInternals,
|
|
334
|
+
request: CallRequest
|
|
335
|
+
): Promise<unknown> =>
|
|
336
|
+
run(internals, request).catch((error: unknown) => {
|
|
337
|
+
const again = request.again
|
|
338
|
+
if (
|
|
339
|
+
again !== undefined &&
|
|
340
|
+
isReactorError(error) &&
|
|
341
|
+
!error.mayHaveExecuted &&
|
|
342
|
+
error.kind !== "canister_err"
|
|
343
|
+
) {
|
|
344
|
+
throw fromCanister(keptUnknown(again, error), request.canister)
|
|
345
|
+
}
|
|
346
|
+
if (
|
|
347
|
+
isReactorError(error) &&
|
|
348
|
+
error.mayHaveExecuted &&
|
|
349
|
+
isWrite(request.method.mode)
|
|
350
|
+
) {
|
|
351
|
+
UNKNOWN_WRITES.set(error, request)
|
|
352
|
+
// Each send of the write may start until its own expiry, so the write
|
|
353
|
+
// it re-sends keeps the latest.
|
|
354
|
+
const first = firstOf(request)
|
|
355
|
+
if (
|
|
356
|
+
first.expiresAt === undefined ||
|
|
357
|
+
(error.expiresAt ?? 0n) > first.expiresAt
|
|
358
|
+
) {
|
|
359
|
+
first.expiresAt = error.expiresAt
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
throw fromCanister(error, request.canister)
|
|
363
|
+
})
|
|
364
|
+
|
|
365
|
+
async function run(
|
|
366
|
+
internals: ClientInternals,
|
|
367
|
+
request: CallRequest
|
|
368
|
+
): Promise<unknown> {
|
|
369
|
+
const { method, target, caller } = request
|
|
370
|
+
const name = method.name
|
|
371
|
+
if (internals.disposed()) {
|
|
372
|
+
throw createReactorError("cancelled", {
|
|
373
|
+
method: name,
|
|
374
|
+
canisterId: target.ok ? target.id : target.slot,
|
|
375
|
+
code: "client_disposed",
|
|
376
|
+
reason: "the call was made on a disposed client; nothing was sent",
|
|
377
|
+
})
|
|
378
|
+
}
|
|
379
|
+
if (!target.ok) {
|
|
380
|
+
throw createReactorError("invalid_args", {
|
|
381
|
+
method: name,
|
|
382
|
+
canisterId: target.slot,
|
|
383
|
+
code: "canister_id_unresolved",
|
|
384
|
+
reason: target.message,
|
|
385
|
+
})
|
|
386
|
+
}
|
|
387
|
+
const where = { method: name, canisterId: target.id }
|
|
388
|
+
if (request.certified && method.mode === "composite_query") {
|
|
389
|
+
throw createReactorError("invalid_args", {
|
|
390
|
+
...where,
|
|
391
|
+
code: "no_certified_path",
|
|
392
|
+
reason:
|
|
393
|
+
"a composite query has no certified path; call it on the canister made without certified: true",
|
|
394
|
+
})
|
|
395
|
+
}
|
|
396
|
+
const write = isWrite(method.mode)
|
|
397
|
+
if (write && !caller.authenticated) {
|
|
398
|
+
throw createReactorError("unauthenticated", {
|
|
399
|
+
...where,
|
|
400
|
+
code: "anonymous_write",
|
|
401
|
+
reason: `${method.mode === "oneway" ? "a oneway" : "an update"} needs a signed-in caller, and the caller is ${caller.principal}; nothing was sent`,
|
|
402
|
+
})
|
|
403
|
+
}
|
|
404
|
+
const management = target.id === MANAGEMENT_CANISTER
|
|
405
|
+
if (management && !isRoutable(name)) {
|
|
406
|
+
throw unroutable(where, effectiveCanisterId(name, request.values))
|
|
407
|
+
}
|
|
408
|
+
const encoded = (request.encoded ??= encodeArgs(method.args, request.values))
|
|
409
|
+
if (!encoded.ok) throw notEncoded(where, encoded)
|
|
410
|
+
// A re-send of an offered write goes out only as that write: anything else
|
|
411
|
+
// would be a new write, which the canister has seen no dedupe key for.
|
|
412
|
+
const sent = request.again
|
|
413
|
+
if (sent) {
|
|
414
|
+
const change = differs(sent, method, target, encoded)
|
|
415
|
+
if (change) {
|
|
416
|
+
throw createReactorError(
|
|
417
|
+
change === "arg" ? "invalid_args" : "cancelled",
|
|
418
|
+
{
|
|
419
|
+
...where,
|
|
420
|
+
code: `${change}_changed`,
|
|
421
|
+
reason:
|
|
422
|
+
change === "arg"
|
|
423
|
+
? "the argument was changed after the write it re-sends went out; it was not sent"
|
|
424
|
+
: `the write it re-sends went to ${sent.method.name} on ${(sent.target as { id: string }).id}; it was not sent`,
|
|
425
|
+
}
|
|
426
|
+
)
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (management && !request.effective) {
|
|
430
|
+
const routed = effectiveCanisterId(name, request.values)
|
|
431
|
+
if (!routed.ok) throw unroutable(where, routed)
|
|
432
|
+
request.effective = routed.id
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const mode: CallMode = write ? "update" : "query"
|
|
436
|
+
const send: Send =
|
|
437
|
+
method.mode === "oneway"
|
|
438
|
+
? "oneway"
|
|
439
|
+
: write || (request.certified && method.mode === "query")
|
|
440
|
+
? "replicated"
|
|
441
|
+
: "query"
|
|
442
|
+
const call: Outgoing = {
|
|
443
|
+
canisterId: target.id,
|
|
444
|
+
effective: request.effective ?? target.id,
|
|
445
|
+
methodName: name,
|
|
446
|
+
// A copy per invocation: the bytes are what `watchSends` knows this
|
|
447
|
+
// invocation's sends by, and a `resendOf` offer or a cached encoding
|
|
448
|
+
// hands the same bytes to every invocation.
|
|
449
|
+
arg: encoded.bytes.slice(),
|
|
450
|
+
mode,
|
|
451
|
+
signal: request.signal,
|
|
452
|
+
again: sent !== undefined,
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// A timeout aborts a signal of its own, which the query function's signal
|
|
456
|
+
// (when there is one) aborts too, so every wait below ends at either.
|
|
457
|
+
const ms = request.timeoutMs
|
|
458
|
+
const outer = request.signal
|
|
459
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
460
|
+
let timedOut = false
|
|
461
|
+
// Whether an attempt is out, and may be in the IC: from the moment it is
|
|
462
|
+
// handed to the agent until it fails. A failure that lets the call re-send
|
|
463
|
+
// proves the attempt was not delivered, so a timeout during the wait
|
|
464
|
+
// before a re-send finds nothing out.
|
|
465
|
+
let out = false
|
|
466
|
+
let end: (() => void) | undefined
|
|
467
|
+
if (ms !== undefined) {
|
|
468
|
+
const stop = new AbortController()
|
|
469
|
+
end = () => stop.abort(outer?.reason)
|
|
470
|
+
call.signal = stop.signal
|
|
471
|
+
outer?.addEventListener("abort", end, { once: true })
|
|
472
|
+
if (outer?.aborted) end()
|
|
473
|
+
timer = setTimeout(() => {
|
|
474
|
+
timedOut = !stop.signal.aborted
|
|
475
|
+
stop.abort()
|
|
476
|
+
}, ms)
|
|
477
|
+
}
|
|
478
|
+
const signal = call.signal
|
|
479
|
+
|
|
480
|
+
let reply: Uint8Array | undefined
|
|
481
|
+
// The latest ingress expiry among this call's sends, for a write that ends
|
|
482
|
+
// with no answer (`expiresAt`).
|
|
483
|
+
const sends: Sent = {}
|
|
484
|
+
try {
|
|
485
|
+
for (let resends = 0; ; resends += 1) {
|
|
486
|
+
try {
|
|
487
|
+
if (resends > 0) {
|
|
488
|
+
await untilAborted(
|
|
489
|
+
sleep(UPDATE_RESEND_DELAYS_MS[resends - 1]),
|
|
490
|
+
signal
|
|
491
|
+
)
|
|
492
|
+
}
|
|
493
|
+
// Asked again before every attempt: a re-send for a caller who is no
|
|
494
|
+
// longer current is cancelled, not sent as them.
|
|
495
|
+
const agent = await untilAborted(
|
|
496
|
+
internals.agentFor(caller.principal, where),
|
|
497
|
+
signal
|
|
498
|
+
)
|
|
499
|
+
if (signal?.aborted) throw notSent(where, "the call was cancelled")
|
|
500
|
+
// agentFor checked for disposal before it returned the agent, but a
|
|
501
|
+
// dispose() can still run in the microtask between that check and
|
|
502
|
+
// this line (a reaction on the identity's promise, say): nothing is
|
|
503
|
+
// sent then.
|
|
504
|
+
if (internals.disposed()) {
|
|
505
|
+
throw notSent(where, "the client was disposed", "client_disposed")
|
|
506
|
+
}
|
|
507
|
+
out = true
|
|
508
|
+
reply = await dispatch(agent, send, call, sends)
|
|
509
|
+
break
|
|
510
|
+
} catch (error) {
|
|
511
|
+
// Never a re-send: what the attempt out did is unknown, and the
|
|
512
|
+
// caller has stopped waiting. A re-send of an offered write that
|
|
513
|
+
// times out before it goes out keeps the earlier attempt's unknown
|
|
514
|
+
// outcome all the same (`invoke`).
|
|
515
|
+
if (timedOut) {
|
|
516
|
+
throw withExpiry(timedOutError(where, write && out), sends.expiresAt)
|
|
517
|
+
}
|
|
518
|
+
out = false
|
|
519
|
+
if (signal?.aborted && !isReactorError(error)) {
|
|
520
|
+
throw notSent(where, "the call was cancelled")
|
|
521
|
+
}
|
|
522
|
+
const resend =
|
|
523
|
+
request.resend &&
|
|
524
|
+
(write
|
|
525
|
+
? !management && retryUpdate(error, resends)
|
|
526
|
+
: resends < UPDATE_RESEND_DELAYS_MS.length && isRetryable(error))
|
|
527
|
+
if (!resend) throw write ? withExpiry(error, sends.expiresAt) : error
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
} finally {
|
|
531
|
+
clearTimeout(timer)
|
|
532
|
+
if (end) outer?.removeEventListener("abort", end)
|
|
533
|
+
}
|
|
534
|
+
if (send === "oneway") return undefined
|
|
535
|
+
return decodeReply(method, reply as Uint8Array, internals, {
|
|
536
|
+
...where,
|
|
537
|
+
mode,
|
|
538
|
+
})
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const unroutable = (
|
|
542
|
+
where: { method: string; canisterId: string },
|
|
543
|
+
routed:
|
|
544
|
+
{ readonly ok: false; readonly reason: string } | { readonly ok: true }
|
|
545
|
+
) =>
|
|
546
|
+
createReactorError("invalid_args", {
|
|
547
|
+
...where,
|
|
548
|
+
code: "effective_canister_id_unknown",
|
|
549
|
+
reason: routed.ok ? "the call cannot be routed" : routed.reason,
|
|
550
|
+
})
|
|
551
|
+
|
|
552
|
+
/** A refusal before the call's next attempt went out. */
|
|
553
|
+
const notSent = (
|
|
554
|
+
where: { method: string; canisterId: string },
|
|
555
|
+
lead: string,
|
|
556
|
+
code?: string
|
|
557
|
+
) =>
|
|
558
|
+
createReactorError("cancelled", {
|
|
559
|
+
...where,
|
|
560
|
+
code,
|
|
561
|
+
reason: `${lead} before it was sent`,
|
|
562
|
+
})
|
|
563
|
+
|
|
564
|
+
const sleep = (ms: number) =>
|
|
565
|
+
new Promise<void>((resolve) => setTimeout(resolve, ms))
|
|
566
|
+
|
|
567
|
+
/** How a call goes out. */
|
|
568
|
+
type Send = "query" | "replicated" | "oneway"
|
|
569
|
+
|
|
570
|
+
interface Outgoing {
|
|
571
|
+
readonly canisterId: string
|
|
572
|
+
/** The canister the request is routed by: `canisterId`, or for `aaaaa-aa` the one its table names. */
|
|
573
|
+
readonly effective: string
|
|
574
|
+
readonly methodName: string
|
|
575
|
+
readonly arg: Uint8Array
|
|
576
|
+
/** How a failure is classified: `update` for a write, `query` for a read (certified or not). */
|
|
577
|
+
readonly mode: CallMode
|
|
578
|
+
/** The call's signal: the query function's, or the timeout's when there is one. */
|
|
579
|
+
signal?: AbortSignal
|
|
580
|
+
/** Whether the call re-sends a write `resendOf()` offered, which may have run. */
|
|
581
|
+
readonly again: boolean
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/** What the sends of one call record: the latest ingress expiry they were signed with. */
|
|
585
|
+
interface Sent {
|
|
586
|
+
expiresAt?: bigint
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/** Records the ingress expiry a send was signed with, keeping the latest. */
|
|
590
|
+
function recordExpiry(sent: Sent, expiry: unknown): void {
|
|
591
|
+
if (!Expiry.isExpiry(expiry)) return
|
|
592
|
+
const value = expiry.toBigInt()
|
|
593
|
+
if (sent.expiresAt === undefined || value > sent.expiresAt) {
|
|
594
|
+
sent.expiresAt = value
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* The writes being sent, by the encoded argument the agent puts into their
|
|
600
|
+
* call requests, for {@link watchSends}.
|
|
601
|
+
*/
|
|
602
|
+
const signing = new WeakMap<
|
|
603
|
+
Uint8Array,
|
|
604
|
+
{ readonly sent: Sent; readonly signal?: AbortSignal }
|
|
605
|
+
>()
|
|
606
|
+
const watched = new WeakSet<HttpAgent>()
|
|
607
|
+
|
|
608
|
+
/**
|
|
609
|
+
* Has `agent` record the ingress expiry of every call request of a write in
|
|
610
|
+
* its `Sent` while it builds the request, before it is signed and sent: a
|
|
611
|
+
* signal that aborts while the answer is awaited cannot lose it. A request
|
|
612
|
+
* whose signal aborted before then is refused, so it is never sent: every
|
|
613
|
+
* send of a write that may have gone out is recorded.
|
|
614
|
+
*/
|
|
615
|
+
function watchSends(agent: HttpAgent): void {
|
|
616
|
+
if (watched.has(agent)) return
|
|
617
|
+
watched.add(agent)
|
|
618
|
+
agent.addTransform("update", async (request) => {
|
|
619
|
+
const body = request.body as { arg?: Uint8Array; ingress_expiry?: unknown }
|
|
620
|
+
const write = body.arg && signing.get(body.arg)
|
|
621
|
+
if (!write) return
|
|
622
|
+
if (write.signal?.aborted) throw abortReason(write.signal)
|
|
623
|
+
recordExpiry(write.sent, body.ingress_expiry)
|
|
624
|
+
})
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Sends one attempt and returns the reply bytes (`undefined` for a oneway).
|
|
629
|
+
* Rejects with the classified failure. The ingress expiry of a call request
|
|
630
|
+
* is recorded in `sent` as the agent builds it ({@link watchSends}).
|
|
631
|
+
*/
|
|
632
|
+
async function dispatch(
|
|
633
|
+
agent: HttpAgent,
|
|
634
|
+
send: Send,
|
|
635
|
+
call: Outgoing,
|
|
636
|
+
sent: Sent
|
|
637
|
+
): Promise<Uint8Array | undefined> {
|
|
638
|
+
const effectiveTarget = { canisterId: call.effective }
|
|
639
|
+
// Whether the request may be in the IC: only once `agent.call` returned,
|
|
640
|
+
// which it does for a 200 or a 202. Until then every failure is the request
|
|
641
|
+
// being refused or lost on its way in, which the classifier reads from the
|
|
642
|
+
// failure itself.
|
|
643
|
+
let accepted = false
|
|
644
|
+
try {
|
|
645
|
+
// Neither the agent nor the `fetch` it was given bounds the wait for an
|
|
646
|
+
// HTTP answer, and the agent passes `fetch` no signal: the wait is ended
|
|
647
|
+
// here when the signal aborts. The request itself is not torn down, but
|
|
648
|
+
// nobody waits for its answer.
|
|
649
|
+
if (send === "query") {
|
|
650
|
+
const response = await untilAborted(
|
|
651
|
+
agent.query(call.canisterId, {
|
|
652
|
+
methodName: call.methodName,
|
|
653
|
+
arg: call.arg,
|
|
654
|
+
effectiveTarget,
|
|
655
|
+
}),
|
|
656
|
+
call.signal
|
|
657
|
+
)
|
|
658
|
+
if (response.status === QueryResponseStatus.Rejected) {
|
|
659
|
+
throw RejectError.fromCode(
|
|
660
|
+
new UncertifiedRejectErrorCode(
|
|
661
|
+
response.requestId,
|
|
662
|
+
response.reject_code,
|
|
663
|
+
response.reject_message,
|
|
664
|
+
response.error_code,
|
|
665
|
+
response.signatures
|
|
666
|
+
)
|
|
667
|
+
)
|
|
668
|
+
}
|
|
669
|
+
return response.reply.arg
|
|
670
|
+
}
|
|
671
|
+
watchSends(agent)
|
|
672
|
+
signing.set(call.arg, { sent, signal: call.signal })
|
|
673
|
+
const submitted = await untilAborted(
|
|
674
|
+
agent.call(call.canisterId, {
|
|
675
|
+
methodName: call.methodName,
|
|
676
|
+
arg: call.arg,
|
|
677
|
+
effectiveTarget,
|
|
678
|
+
// A oneway expects no reply, so it is sent asynchronously and settles
|
|
679
|
+
// once the IC has accepted it.
|
|
680
|
+
...(send === "oneway" ? { callSync: false } : {}),
|
|
681
|
+
}),
|
|
682
|
+
call.signal
|
|
683
|
+
)
|
|
684
|
+
accepted = true
|
|
685
|
+
const { body } = submitted.response
|
|
686
|
+
if (isV2ResponseBody(body)) {
|
|
687
|
+
throw RejectError.fromCode(
|
|
688
|
+
new UncertifiedRejectUpdateErrorCode(
|
|
689
|
+
submitted.requestId,
|
|
690
|
+
body.reject_code,
|
|
691
|
+
body.reject_message,
|
|
692
|
+
body.error_code
|
|
693
|
+
)
|
|
694
|
+
)
|
|
695
|
+
}
|
|
696
|
+
if (send === "oneway") return undefined
|
|
697
|
+
return await settle(agent, submitted, call)
|
|
698
|
+
} catch (error) {
|
|
699
|
+
throw classifyError(
|
|
700
|
+
error,
|
|
701
|
+
call.mode === "update"
|
|
702
|
+
? {
|
|
703
|
+
method: call.methodName,
|
|
704
|
+
canisterId: call.canisterId,
|
|
705
|
+
mode: "update",
|
|
706
|
+
accepted,
|
|
707
|
+
again: call.again,
|
|
708
|
+
signal: call.signal,
|
|
709
|
+
}
|
|
710
|
+
: {
|
|
711
|
+
method: call.methodName,
|
|
712
|
+
canisterId: call.canisterId,
|
|
713
|
+
mode: "query",
|
|
714
|
+
signal: call.signal,
|
|
715
|
+
}
|
|
716
|
+
)
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
const utf8 = new TextEncoder()
|
|
721
|
+
const text = new TextDecoder()
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* The reply of a submitted call, as `agent.update()` reads it in
|
|
725
|
+
* `@icp-sdk/core` 6.1: from the certificate of a synchronous (v4) answer, and
|
|
726
|
+
* by polling `read_state` after a 202 or a certificate that has no status for
|
|
727
|
+
* the request yet.
|
|
728
|
+
*/
|
|
729
|
+
async function settle(
|
|
730
|
+
agent: HttpAgent,
|
|
731
|
+
{ requestId, response }: SubmitResponse,
|
|
732
|
+
call: Outgoing
|
|
733
|
+
): Promise<Uint8Array> {
|
|
734
|
+
const { body } = response
|
|
735
|
+
if (isV4ResponseBody(body)) {
|
|
736
|
+
const reply = await untilAborted(
|
|
737
|
+
readCertificate(agent, body.certificate, call.effective, requestId),
|
|
738
|
+
call.signal
|
|
739
|
+
)
|
|
740
|
+
if (reply !== undefined) return reply
|
|
741
|
+
} else if (response.status !== 202) {
|
|
742
|
+
throw UnknownError.fromCode(
|
|
743
|
+
new UnexpectedErrorCode(
|
|
744
|
+
`the replica answered a call with HTTP ${response.status} and no body to read`
|
|
745
|
+
)
|
|
746
|
+
)
|
|
747
|
+
}
|
|
748
|
+
return poll(agent, call.effective, requestId, call.signal)
|
|
749
|
+
}
|
|
750
|
+
|
|
751
|
+
/**
|
|
752
|
+
* Verifies a v4 call answer's certificate and reads the request's status in
|
|
753
|
+
* it: the reply, a certified reject (thrown), or `undefined` when the
|
|
754
|
+
* certificate has no status for the request yet and it must be polled.
|
|
755
|
+
*/
|
|
756
|
+
async function readCertificate(
|
|
757
|
+
agent: HttpAgent,
|
|
758
|
+
raw: Uint8Array,
|
|
759
|
+
effective: string,
|
|
760
|
+
requestId: RequestId
|
|
761
|
+
): Promise<Uint8Array | undefined> {
|
|
762
|
+
const rootKey = agent.rootKey
|
|
763
|
+
if (rootKey === null) {
|
|
764
|
+
throw new Error("the agent has no root key to verify the reply with")
|
|
765
|
+
}
|
|
766
|
+
const certificate = await Certificate.create({
|
|
767
|
+
certificate: raw,
|
|
768
|
+
rootKey,
|
|
769
|
+
principal: { canisterId: Principal.fromText(effective) },
|
|
770
|
+
agent,
|
|
771
|
+
})
|
|
772
|
+
const path = [utf8.encode("request_status"), requestId]
|
|
773
|
+
const read = (leaf: string) =>
|
|
774
|
+
lookupResultToBuffer(certificate.lookup_path([...path, utf8.encode(leaf)]))
|
|
775
|
+
const status = read("status")
|
|
776
|
+
if (status === undefined) return undefined
|
|
777
|
+
switch (text.decode(status)) {
|
|
778
|
+
case "replied": {
|
|
779
|
+
const reply = read("reply")
|
|
780
|
+
if (reply === undefined) {
|
|
781
|
+
throw new Error("the certificate says replied but holds no reply")
|
|
782
|
+
}
|
|
783
|
+
return reply
|
|
784
|
+
}
|
|
785
|
+
case "rejected": {
|
|
786
|
+
const errorCode = read("error_code")
|
|
787
|
+
throw RejectError.fromCode(
|
|
788
|
+
new CertifiedRejectErrorCode(
|
|
789
|
+
requestId,
|
|
790
|
+
(read("reject_code")?.[0] ?? 0) as ReplicaRejectCode,
|
|
791
|
+
text.decode(read("reject_message") ?? new Uint8Array()),
|
|
792
|
+
errorCode === undefined ? undefined : text.decode(errorCode)
|
|
793
|
+
)
|
|
794
|
+
)
|
|
795
|
+
}
|
|
796
|
+
default:
|
|
797
|
+
throw UnknownError.fromCode(
|
|
798
|
+
new UnexpectedErrorCode(
|
|
799
|
+
`unexpected request status "${text.decode(status)}" in a call's certificate`
|
|
800
|
+
)
|
|
801
|
+
)
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
/** Whether a failed `read_state` is worth asking again: the request may simply not have arrived. */
|
|
806
|
+
function isTransientPollFailure(error: unknown): boolean {
|
|
807
|
+
if (asRecord(error).kind === "Transport") return true
|
|
808
|
+
const status = readHttpStatus(error)
|
|
809
|
+
return (
|
|
810
|
+
status !== undefined && (status === 408 || status === 429 || status >= 500)
|
|
811
|
+
)
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
/**
|
|
815
|
+
* `promise`, or a rejection with the signal's reason as soon as `signal`
|
|
816
|
+
* aborts, whichever comes first. What `promise` was waiting for goes on, but
|
|
817
|
+
* nobody waits for it.
|
|
818
|
+
*/
|
|
819
|
+
function untilAborted<T>(
|
|
820
|
+
promise: Promise<T>,
|
|
821
|
+
signal: AbortSignal | undefined
|
|
822
|
+
): Promise<T> {
|
|
823
|
+
if (signal === undefined) return promise
|
|
824
|
+
return new Promise<T>((resolve, reject) => {
|
|
825
|
+
const abort = () => reject(abortReason(signal))
|
|
826
|
+
// The promise is handled even when the signal has already aborted, so a
|
|
827
|
+
// request that fails after nobody waits for it is not an unhandled
|
|
828
|
+
// rejection.
|
|
829
|
+
signal.addEventListener("abort", abort, { once: true })
|
|
830
|
+
if (signal.aborted) abort()
|
|
831
|
+
promise.then(
|
|
832
|
+
(value) => {
|
|
833
|
+
signal.removeEventListener("abort", abort)
|
|
834
|
+
resolve(value)
|
|
835
|
+
},
|
|
836
|
+
(error: unknown) => {
|
|
837
|
+
signal.removeEventListener("abort", abort)
|
|
838
|
+
reject(error)
|
|
839
|
+
}
|
|
840
|
+
)
|
|
841
|
+
})
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/** What an aborted signal is thrown as. The classifier reads the signal, so any value does. */
|
|
845
|
+
const abortReason = (signal: AbortSignal): unknown =>
|
|
846
|
+
signal.reason ?? new Error("the call was cancelled")
|
|
847
|
+
|
|
848
|
+
/**
|
|
849
|
+
* Polls `read_state` for the request's reply, asking again after a transient
|
|
850
|
+
* failure, at most {@link REPOLL_DELAYS_MS} times.
|
|
851
|
+
*
|
|
852
|
+
* It stops, and rejects, as soon as `signal` aborts: before each poll,
|
|
853
|
+
* while a poll waits for its answer, and during every wait between two. A read whose observer went away (TanStack
|
|
854
|
+
* aborts the signal it gave the query function) would otherwise keep asking
|
|
855
|
+
* for up to the strategy's five minutes, for an answer nobody reads.
|
|
856
|
+
*/
|
|
857
|
+
async function poll(
|
|
858
|
+
agent: HttpAgent,
|
|
859
|
+
effective: string,
|
|
860
|
+
requestId: RequestId,
|
|
861
|
+
signal: AbortSignal | undefined
|
|
862
|
+
): Promise<Uint8Array> {
|
|
863
|
+
// One strategy for every poll, so its five-minute bound covers them all.
|
|
864
|
+
const waits = defaultStrategy()
|
|
865
|
+
const strategy: PollStrategy = (...args) =>
|
|
866
|
+
untilAborted(waits(...args), signal)
|
|
867
|
+
for (let failures = 0; ; failures += 1) {
|
|
868
|
+
if (signal?.aborted === true) throw abortReason(signal)
|
|
869
|
+
try {
|
|
870
|
+
// Each poll's own read_state request has no bound either: an abort
|
|
871
|
+
// ends the wait for its answer too.
|
|
872
|
+
const { reply } = await untilAborted(
|
|
873
|
+
pollForResponse(agent, { canisterId: effective }, requestId, {
|
|
874
|
+
strategy,
|
|
875
|
+
}),
|
|
876
|
+
signal
|
|
877
|
+
)
|
|
878
|
+
return reply
|
|
879
|
+
} catch (error) {
|
|
880
|
+
if (
|
|
881
|
+
failures >= REPOLL_DELAYS_MS.length ||
|
|
882
|
+
!isTransientPollFailure(error)
|
|
883
|
+
) {
|
|
884
|
+
throw error
|
|
885
|
+
}
|
|
886
|
+
// An abort ends the wait at once, and the check above ends the loop.
|
|
887
|
+
await untilAborted(sleep(REPOLL_DELAYS_MS[failures]), signal)
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
/** A short description of a decoded `Err` payload, for the error message. */
|
|
893
|
+
function describeErr(errTag: string, payload: unknown): string {
|
|
894
|
+
if (
|
|
895
|
+
typeof payload === "object" &&
|
|
896
|
+
payload !== null &&
|
|
897
|
+
typeof (payload as { tag?: unknown }).tag === "string"
|
|
898
|
+
) {
|
|
899
|
+
return `${errTag} ${(payload as { tag: string }).tag}`
|
|
900
|
+
}
|
|
901
|
+
if (typeof payload === "string") {
|
|
902
|
+
const line = payload.replace(/\s+/g, " ").trim()
|
|
903
|
+
return `${errTag} ${JSON.stringify(line.length > 120 ? `${line.slice(0, 119)}…` : line)}`
|
|
904
|
+
}
|
|
905
|
+
return errTag
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
/**
|
|
909
|
+
* Decodes, collapses and unwraps a reply. The codec returns `unknown[]`; what
|
|
910
|
+
* it returns is checked against the method's schemas, and the generated
|
|
911
|
+
* `Actor` type is the static description of the same thing, so the builders
|
|
912
|
+
* cast the result to the `Actor`'s reply type once, where they hand it out.
|
|
913
|
+
*/
|
|
914
|
+
function decodeReply(
|
|
915
|
+
method: PreparedMethod,
|
|
916
|
+
reply: Uint8Array,
|
|
917
|
+
limits: { readonly maxDepth: number; readonly maxElements: number },
|
|
918
|
+
context: { method: string; canisterId: string; mode: CallMode }
|
|
919
|
+
): unknown {
|
|
920
|
+
// `@meantjs/schema` charges a blob one element per byte, so a reply over its
|
|
921
|
+
// default budget of 1,000,000 (a wasm module, an asset chunk) would never
|
|
922
|
+
// decode. A reply may hold one value per byte plus the budget: that work is
|
|
923
|
+
// bounded by the IC's reply size, and the budget still bounds values that
|
|
924
|
+
// take no bytes on the wire, however large a blob beside them.
|
|
925
|
+
const decoded = decodeArgs(method.results, reply, {
|
|
926
|
+
maxDepth: limits.maxDepth,
|
|
927
|
+
maxElements: Math.min(
|
|
928
|
+
limits.maxElements + reply.length,
|
|
929
|
+
Number.MAX_SAFE_INTEGER
|
|
930
|
+
),
|
|
931
|
+
})
|
|
932
|
+
if (!decoded.ok) {
|
|
933
|
+
const [first] = decoded.issues
|
|
934
|
+
throw invalidReplyError(context, {
|
|
935
|
+
reason: first
|
|
936
|
+
? `the reply did not decode as the method's result: ${first.path}: ${first.message}`
|
|
937
|
+
: undefined,
|
|
938
|
+
issues: decoded.issues,
|
|
939
|
+
})
|
|
940
|
+
}
|
|
941
|
+
const { values } = decoded
|
|
942
|
+
const value =
|
|
943
|
+
values.length === 0
|
|
944
|
+
? undefined
|
|
945
|
+
: values.length === 1
|
|
946
|
+
? values[0]
|
|
947
|
+
: [...values]
|
|
948
|
+
if (method.result === undefined) return value
|
|
949
|
+
// The decoder checked the value against the result variant, so it is one of
|
|
950
|
+
// its two arms. An arm that carries nothing decodes without `value`: its
|
|
951
|
+
// payload is Candid `null`, as `unwrapResult` reads it.
|
|
952
|
+
const arm = value as { readonly tag: string; readonly value?: unknown }
|
|
953
|
+
const payload = "value" in arm ? arm.value : null
|
|
954
|
+
if (arm.tag === method.result.ok) return payload
|
|
955
|
+
throw createReactorError("canister_err", {
|
|
956
|
+
method: context.method,
|
|
957
|
+
canisterId: context.canisterId,
|
|
958
|
+
reason: `the canister returned ${describeErr(method.result.err, payload)}`,
|
|
959
|
+
err: payload,
|
|
960
|
+
})
|
|
961
|
+
}
|