@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,1000 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The builders on a client: `queryKey`, `queryOptions`, `mutationOptions`
|
|
3
|
+
* and `func`, with `canister` from `canister.ts`. Each returns a fresh plain
|
|
4
|
+
* object (DECISIONS Q3): TanStack compares keys by their hash, so nothing is
|
|
5
|
+
* gained by caching one, and a cached read would pin the caller it was built
|
|
6
|
+
* for.
|
|
7
|
+
*
|
|
8
|
+
* The types the `Client` interface gives these functions are generic over the
|
|
9
|
+
* generated `Actor`; the implementations below read plain values and are cast
|
|
10
|
+
* to those types once, in {@link createBuilders}. `decodeArgs` returns
|
|
11
|
+
* `unknown[]`, and the generated `Actor` type is the static description of
|
|
12
|
+
* what the schemas just checked, so that cast is where the one unavoidable
|
|
13
|
+
* `unknown` becomes the `Actor`'s reply type.
|
|
14
|
+
*
|
|
15
|
+
* Internal: not exported from the package entry.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
import { isPrincipal, resolveSchema } from "@meantjs/schema"
|
|
20
|
+
import { encodeArgs, type EncodeResult } from "@meantjs/schema/codec"
|
|
21
|
+
import {
|
|
22
|
+
skipToken,
|
|
23
|
+
type QueryClient,
|
|
24
|
+
type QueryFunctionContext,
|
|
25
|
+
type QueryKey,
|
|
26
|
+
} from "@tanstack/query-core"
|
|
27
|
+
import { lookupResultToBuffer } from "@icp-sdk/core/agent"
|
|
28
|
+
import { Principal } from "@icp-sdk/core/principal"
|
|
29
|
+
import {
|
|
30
|
+
UNKNOWN_WRITES,
|
|
31
|
+
differs,
|
|
32
|
+
invoke,
|
|
33
|
+
isWrite,
|
|
34
|
+
prepareMethod,
|
|
35
|
+
notEncoded,
|
|
36
|
+
valuesOf,
|
|
37
|
+
type ResolvedTarget,
|
|
38
|
+
} from "./call.js"
|
|
39
|
+
import {
|
|
40
|
+
canisterFactory,
|
|
41
|
+
describe,
|
|
42
|
+
badTimeout,
|
|
43
|
+
methodOf,
|
|
44
|
+
recordOf,
|
|
45
|
+
type CanisterRecord,
|
|
46
|
+
} from "./canister.js"
|
|
47
|
+
import type { Caller, Client, ClientInternals } from "./client.js"
|
|
48
|
+
import {
|
|
49
|
+
classifyError,
|
|
50
|
+
createReactorError,
|
|
51
|
+
fromCanister,
|
|
52
|
+
isReactorError,
|
|
53
|
+
retryQuery,
|
|
54
|
+
timedOutError,
|
|
55
|
+
} from "./errors.js"
|
|
56
|
+
import {
|
|
57
|
+
CERTIFIED,
|
|
58
|
+
INVALID,
|
|
59
|
+
KEY_ROOT,
|
|
60
|
+
SKIP,
|
|
61
|
+
stableText,
|
|
62
|
+
toHex,
|
|
63
|
+
} from "./keys.js"
|
|
64
|
+
import { MANAGEMENT_CANISTER } from "./management.js"
|
|
65
|
+
|
|
66
|
+
/** The builders `createClient` puts on a client. */
|
|
67
|
+
export type Builders = Pick<
|
|
68
|
+
Client,
|
|
69
|
+
| "canister"
|
|
70
|
+
| "queryKey"
|
|
71
|
+
| "queryOptions"
|
|
72
|
+
| "mutationOptions"
|
|
73
|
+
| "resendOf"
|
|
74
|
+
| "encode"
|
|
75
|
+
| "func"
|
|
76
|
+
| "time"
|
|
77
|
+
>
|
|
78
|
+
|
|
79
|
+
/** What an update read with `{ update: "idempotent" }` adds to its options (DECISIONS Q2). */
|
|
80
|
+
const IDEMPOTENT = Object.freeze({
|
|
81
|
+
staleTime: Infinity,
|
|
82
|
+
refetchOnMount: false,
|
|
83
|
+
refetchOnWindowFocus: false,
|
|
84
|
+
refetchOnReconnect: false,
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
const never = (): boolean => false
|
|
88
|
+
|
|
89
|
+
const slotOf = (target: ResolvedTarget): string =>
|
|
90
|
+
target.ok ? target.id : target.slot
|
|
91
|
+
|
|
92
|
+
/** The args segments of a key: the hex of the encoding, or the tag and text of arguments that do not encode. */
|
|
93
|
+
const argsSegments = (encoded: EncodeResult, vars: unknown): string[] =>
|
|
94
|
+
encoded.ok ? [toHex(encoded.bytes)] : [INVALID, stableText(vars)]
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Checks the fourth argument of `queryOptions`, and says whether it opts an
|
|
98
|
+
* update in as a read.
|
|
99
|
+
*/
|
|
100
|
+
function readQueryOptionsArg(options: unknown): boolean {
|
|
101
|
+
if (options === undefined) return false
|
|
102
|
+
if (
|
|
103
|
+
typeof options === "object" &&
|
|
104
|
+
options !== null &&
|
|
105
|
+
Object.keys(options).length === 1 &&
|
|
106
|
+
(options as { update?: unknown }).update === "idempotent"
|
|
107
|
+
) {
|
|
108
|
+
return true
|
|
109
|
+
}
|
|
110
|
+
throw new TypeError(
|
|
111
|
+
`[ic-reactor] client.queryOptions() takes { update: "idempotent" } as its fourth argument, or nothing; got ${describe(options)}.`
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* The `time` path of the state tree, copied into the global `Uint8Array`, the
|
|
117
|
+
* one the agent's CBOR encoder knows bytes by (`instanceof`). Under a test's
|
|
118
|
+
* jsdom that is jsdom's, while `TextEncoder` hands out Node's, and the encoder
|
|
119
|
+
* sent such a label as a map: the `read_state` the replica received was not
|
|
120
|
+
* the one the agent signed, so a signed-in caller's was refused (#949). The
|
|
121
|
+
* fake replica copies its labels for the same reason.
|
|
122
|
+
*/
|
|
123
|
+
const TIME_PATH = [Uint8Array.from(new TextEncoder().encode("time"))]
|
|
124
|
+
|
|
125
|
+
/** An unsigned LEB128 number, as the state tree writes `time`. */
|
|
126
|
+
function readLeb128(bytes: Uint8Array): bigint {
|
|
127
|
+
let value = 0n
|
|
128
|
+
let shift = 0n
|
|
129
|
+
for (const byte of bytes) {
|
|
130
|
+
value |= BigInt(byte & 0x7f) << shift
|
|
131
|
+
if ((byte & 0x80) === 0) return value
|
|
132
|
+
shift += 7n
|
|
133
|
+
}
|
|
134
|
+
throw new Error("the certificate's time is not a complete LEB128 number")
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* What a builder says when it is handed a symbol that is not the
|
|
139
|
+
* `skipToken` of the `@tanstack/query-core` this package imports. Correct
|
|
140
|
+
* variables are never a symbol (but see {@link takesAnything}), so such a
|
|
141
|
+
* symbol is almost always the `skipToken` of another copy of
|
|
142
|
+
* `@tanstack/query-core`: npm installs one beside the exact version
|
|
143
|
+
* `@tanstack/react-query` depends on when the two do not match, and each copy
|
|
144
|
+
* has its own `skipToken`. Encoded, it would make a read that is never
|
|
145
|
+
* skipped and rejects `invalid_args`.
|
|
146
|
+
*/
|
|
147
|
+
const twoQueryCores = (call: string): string =>
|
|
148
|
+
`[ic-reactor] ${call}: the variables are a symbol, not core's skipToken: two copies of @tanstack/query-core are installed (or a bundler loads it twice). ` +
|
|
149
|
+
"Install it at the exact version @tanstack/react-query depends on; `npm ls @tanstack/query-core` must show one copy."
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Whether a method's one argument is Candid `reserved`, which every value
|
|
153
|
+
* inhabits, a symbol included: the one method whose variables may be a
|
|
154
|
+
* symbol.
|
|
155
|
+
*/
|
|
156
|
+
const takesAnything = (method: {
|
|
157
|
+
readonly args: readonly unknown[]
|
|
158
|
+
}): boolean => {
|
|
159
|
+
if (method.args.length !== 1) return false
|
|
160
|
+
try {
|
|
161
|
+
const node = resolveSchema(
|
|
162
|
+
method.args[0] as Parameters<typeof resolveSchema>[0]
|
|
163
|
+
)
|
|
164
|
+
return (
|
|
165
|
+
node.kind === "primitive" &&
|
|
166
|
+
(node as { primitive?: unknown }).primitive === "reserved"
|
|
167
|
+
)
|
|
168
|
+
} catch {
|
|
169
|
+
// A schema resolveSchema refuses takes no symbol either.
|
|
170
|
+
return false
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/** Throws {@link twoQueryCores} for a symbol that is not our `skipToken`, unless the method takes anything. */
|
|
175
|
+
const refuseForeignSymbol = (
|
|
176
|
+
vars: unknown,
|
|
177
|
+
method: { readonly args: readonly unknown[] },
|
|
178
|
+
call: string
|
|
179
|
+
): void => {
|
|
180
|
+
if (
|
|
181
|
+
typeof vars === "symbol" &&
|
|
182
|
+
vars !== skipToken &&
|
|
183
|
+
!takesAnything(method)
|
|
184
|
+
) {
|
|
185
|
+
throw new TypeError(twoQueryCores(call))
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/** Whether a settled mutation may have changed what its canister's reads return. */
|
|
190
|
+
const mayHaveChanged = (error: unknown): boolean =>
|
|
191
|
+
error === null ||
|
|
192
|
+
error === undefined ||
|
|
193
|
+
// Not ours (a bug, or code the app wrapped around the call): it proves
|
|
194
|
+
// nothing, so the reads are refreshed.
|
|
195
|
+
!isReactorError(error) ||
|
|
196
|
+
error.kind === "canister_err" ||
|
|
197
|
+
error.mayHaveExecuted
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* What one run of a mutation resolved, in one synchronous step when it
|
|
201
|
+
* started: the caller it goes out as, the canister it writes to, and the
|
|
202
|
+
* canisterId segments (and method) of every read it invalidates. TanStack
|
|
203
|
+
* Query can run `mutationFn` long after `mutate()` (paused offline, or queued
|
|
204
|
+
* behind another mutation of its `scope`); the write goes out as the caller
|
|
205
|
+
* current when it started, or not at all. A `{ name }` is resolved from the
|
|
206
|
+
* `ic_env` cookie, which a local redeploy can rewrite while an update is in
|
|
207
|
+
* flight; resolving once keeps the write and its invalidation on the same
|
|
208
|
+
* canister.
|
|
209
|
+
*/
|
|
210
|
+
interface MutationRun {
|
|
211
|
+
/** Read by our `onMutate`; none for a run it did not start. */
|
|
212
|
+
readonly caller?: Caller
|
|
213
|
+
readonly target: ResolvedTarget
|
|
214
|
+
readonly invalidates: readonly {
|
|
215
|
+
readonly slots: readonly string[]
|
|
216
|
+
readonly method?: string
|
|
217
|
+
}[]
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Whether two callers read for runs are the same one, `undefined` (not read) never matching. */
|
|
221
|
+
const sameCaller = (a: Caller | undefined, b: Caller | undefined): boolean =>
|
|
222
|
+
a !== undefined &&
|
|
223
|
+
b !== undefined &&
|
|
224
|
+
a.principal === b.principal &&
|
|
225
|
+
a.authenticated === b.authenticated
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* The property a run is kept under in what `onMutate` returns. A symbol, so
|
|
229
|
+
* it cannot collide with a context of the app's, and `{ ...context }` copies
|
|
230
|
+
* it when an app's own `onMutate` spreads this one's result into its own.
|
|
231
|
+
*/
|
|
232
|
+
const RUN = Symbol("ic-reactor.mutationRun")
|
|
233
|
+
|
|
234
|
+
/** The run carried by what `onMutate` returned, if it carries one. */
|
|
235
|
+
const runIn = (onMutateResult: unknown): MutationRun | undefined =>
|
|
236
|
+
typeof onMutateResult === "object" && onMutateResult !== null
|
|
237
|
+
? (onMutateResult as { readonly [RUN]?: MutationRun })[RUN]
|
|
238
|
+
: undefined
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* The function context TanStack Query (5.89 and later) creates for one run of
|
|
242
|
+
* a mutation and passes to `onMutate`, `mutationFn` and `onSettled` alike, if
|
|
243
|
+
* one was passed: the key a run is kept under in `runsByContext`.
|
|
244
|
+
*/
|
|
245
|
+
const runKey = (context: unknown): object | undefined =>
|
|
246
|
+
typeof context === "object" && context !== null ? context : undefined
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* The builders of one client. `owner` returns the client, which is built
|
|
250
|
+
* after its builders.
|
|
251
|
+
*
|
|
252
|
+
* `current` says whom `queryKey` and `queryOptions` build for: the client's
|
|
253
|
+
* live caller, or the principal a view of the client is pinned to (see
|
|
254
|
+
* `CLIENT_AS` in `client.ts`). A read built for it still asks `agentFor` for
|
|
255
|
+
* that principal's agent, so it is cancelled rather than sent while someone
|
|
256
|
+
* else is current. A write reads the live caller when it starts (its
|
|
257
|
+
* `onMutate`, run by `mutate()`), and direct calls and `func` when they run.
|
|
258
|
+
*/
|
|
259
|
+
export function createBuilders(
|
|
260
|
+
internals: ClientInternals,
|
|
261
|
+
owner: () => Client,
|
|
262
|
+
current: () => Caller = internals.current,
|
|
263
|
+
refusals?: WeakSet<object>
|
|
264
|
+
): Builders {
|
|
265
|
+
const network = internals.network.keySegment
|
|
266
|
+
const pinned = current !== internals.current
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* A read built for a pinned principal, refused because someone else is
|
|
270
|
+
* current (`caller_changed`), must not leave the refusal as its query's
|
|
271
|
+
* error: a component that shows the key once that principal is current
|
|
272
|
+
* again (a sign-out after a signed-in reload) would show it. A query with
|
|
273
|
+
* data is left as it was found: TanStack reverts the fetch, as it reverts
|
|
274
|
+
* one its last observer left. A query with no data keeps the error for
|
|
275
|
+
* now, since a suspense read of a reverted one would fetch it again at once
|
|
276
|
+
* and be refused again (a hydrating boundary then never settles); the error
|
|
277
|
+
* goes into `refusals`, and the client clears it from the query once the
|
|
278
|
+
* pinned principal is current again.
|
|
279
|
+
*/
|
|
280
|
+
const keepOnRefusal = async (
|
|
281
|
+
context: QueryFunctionContext,
|
|
282
|
+
read: Promise<unknown>
|
|
283
|
+
): Promise<unknown> => {
|
|
284
|
+
try {
|
|
285
|
+
return await read
|
|
286
|
+
} catch (error) {
|
|
287
|
+
if (
|
|
288
|
+
!context.signal.aborted &&
|
|
289
|
+
isReactorError(error) &&
|
|
290
|
+
error.code === "caller_changed"
|
|
291
|
+
) {
|
|
292
|
+
// TanStack 5.62 does not hand the context its `QueryClient`.
|
|
293
|
+
const queryClient =
|
|
294
|
+
(context as { client?: QueryClient }).client ?? owner().queryClient
|
|
295
|
+
const query = queryClient
|
|
296
|
+
.getQueryCache()
|
|
297
|
+
.find({ queryKey: context.queryKey, exact: true })
|
|
298
|
+
if (query?.state.data !== undefined) void query.cancel({ revert: true })
|
|
299
|
+
else refusals?.add(error)
|
|
300
|
+
}
|
|
301
|
+
throw error
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* What was resolved for each run of a mutation, by the function context
|
|
307
|
+
* TanStack Query (5.89 and later) creates for the run and passes to
|
|
308
|
+
* `onMutate`, `mutationFn` and `onSettled` alike: how `mutationFn` finds
|
|
309
|
+
* what `onMutate` resolved, and `onSettled` what `mutationFn` wrote to,
|
|
310
|
+
* even when an `onMutate` of the app's replaced ours.
|
|
311
|
+
*/
|
|
312
|
+
const runsByContext = new WeakMap<object, MutationRun>()
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Before 5.89 TanStack passes `onMutate`, `mutationFn` and `onSettled` no
|
|
316
|
+
* context, but calls them as methods of the run's options object
|
|
317
|
+
* (`mutation.options.onMutate(variables)`). That object is not the run's
|
|
318
|
+
* own: every `mutate()` of one `MutationObserver` builds its run with the
|
|
319
|
+
* observer's options, so the runs of an observer share it. Kept for it, on
|
|
320
|
+
* a `QueryClient` other than the client's: the caller our `onMutate` read
|
|
321
|
+
* for the runs not yet settled, or `undefined` once two of them started as
|
|
322
|
+
* different callers, which refuses their writes, since `mutationFn`
|
|
323
|
+
* cannot tell which of them it runs for. Our `onSettled` counts them out.
|
|
324
|
+
* Weak, so an entry lives as long as those options. A later render that
|
|
325
|
+
* hands a pending run new options (`setOptions`) leaves its `mutationFn`
|
|
326
|
+
* nothing here, which refuses it.
|
|
327
|
+
*/
|
|
328
|
+
const callersByOptions = new WeakMap<
|
|
329
|
+
object,
|
|
330
|
+
{ readonly caller: Caller | undefined; readonly runs: number }
|
|
331
|
+
>()
|
|
332
|
+
|
|
333
|
+
/** The methods whose `onMutate` read a caller with no context. */
|
|
334
|
+
const startedMethods = new WeakSet<object>()
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* The runs of the client's own `queryClient` not yet settled whose options
|
|
338
|
+
* object is `options`: the runs a `mutationFn` called on it may run for.
|
|
339
|
+
* More than one when an observer's `mutate()` started another before the
|
|
340
|
+
* first settled, as for writes held back offline.
|
|
341
|
+
*/
|
|
342
|
+
const ownRuns = (options: unknown): object[] =>
|
|
343
|
+
typeof options === "object" && options !== null
|
|
344
|
+
? owner()
|
|
345
|
+
.queryClient.getMutationCache()
|
|
346
|
+
.getAll()
|
|
347
|
+
.filter(
|
|
348
|
+
(mutation) =>
|
|
349
|
+
mutation.options === options &&
|
|
350
|
+
mutation.state.status === "pending"
|
|
351
|
+
)
|
|
352
|
+
: []
|
|
353
|
+
|
|
354
|
+
const { offered } = internals
|
|
355
|
+
|
|
356
|
+
/** The run kept for a run's function context, if TanStack passed one and a run was kept for it. */
|
|
357
|
+
const keptRun = (context: unknown): MutationRun | undefined => {
|
|
358
|
+
const key = runKey(context)
|
|
359
|
+
return key === undefined ? undefined : runsByContext.get(key)
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/** A read's key, cut short where `method` or `args` are left out. */
|
|
363
|
+
const readKey = (
|
|
364
|
+
record: CanisterRecord,
|
|
365
|
+
caller: string,
|
|
366
|
+
target: ResolvedTarget,
|
|
367
|
+
method?: string,
|
|
368
|
+
args?: readonly string[]
|
|
369
|
+
): string[] => {
|
|
370
|
+
const key = [KEY_ROOT, network, caller, slotOf(target)]
|
|
371
|
+
if (method === undefined) return key
|
|
372
|
+
key.push(method)
|
|
373
|
+
if (args === undefined) return key
|
|
374
|
+
key.push(...args)
|
|
375
|
+
if (record.certified) key.push(CERTIFIED)
|
|
376
|
+
return key
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
const queryKey = (canister: unknown, ...rest: unknown[]): QueryKey => {
|
|
380
|
+
const call = "client.queryKey()"
|
|
381
|
+
const record = recordOf(canister, internals, call)
|
|
382
|
+
const caller = current().principal
|
|
383
|
+
const target = record.resolve()
|
|
384
|
+
const [method, vars] = rest
|
|
385
|
+
if (method === undefined) return readKey(record, caller, target)
|
|
386
|
+
const prepared = methodOf(record, method, call)
|
|
387
|
+
// Two arguments give the method's prefix, and three one read's key. A
|
|
388
|
+
// method without arguments has one read, so two arguments give its key:
|
|
389
|
+
// the one `queryOptions(c, method)` gives, which `getQueryData` and
|
|
390
|
+
// `setQueryData` must be handed whole.
|
|
391
|
+
if (rest.length < 2 && prepared.args.length > 0) {
|
|
392
|
+
return readKey(record, caller, target, prepared.name)
|
|
393
|
+
}
|
|
394
|
+
if (vars === skipToken) {
|
|
395
|
+
return readKey(record, caller, target, prepared.name, [SKIP])
|
|
396
|
+
}
|
|
397
|
+
refuseForeignSymbol(vars, prepared, call)
|
|
398
|
+
const encoded = encodeArgs(prepared.args, valuesOf(prepared, vars))
|
|
399
|
+
return readKey(
|
|
400
|
+
record,
|
|
401
|
+
caller,
|
|
402
|
+
target,
|
|
403
|
+
prepared.name,
|
|
404
|
+
argsSegments(encoded, vars)
|
|
405
|
+
)
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
const queryOptions = (
|
|
409
|
+
canister: unknown,
|
|
410
|
+
method: unknown,
|
|
411
|
+
vars: unknown,
|
|
412
|
+
options?: unknown
|
|
413
|
+
) => {
|
|
414
|
+
const call = "client.queryOptions()"
|
|
415
|
+
const record = recordOf(canister, internals, call)
|
|
416
|
+
const prepared = methodOf(record, method, call)
|
|
417
|
+
const idempotent = readQueryOptionsArg(options)
|
|
418
|
+
if (prepared.mode === "oneway") {
|
|
419
|
+
throw new TypeError(
|
|
420
|
+
`[ic-reactor] ${call}: ${prepared.name} is a oneway method; it has no reply to cache. Call it directly, or through mutationOptions().`
|
|
421
|
+
)
|
|
422
|
+
}
|
|
423
|
+
// A call of a method without results resolves `undefined`, and TanStack
|
|
424
|
+
// Query takes a query function that resolves `undefined` for a failed
|
|
425
|
+
// read: it reports an error and caches nothing, although the call
|
|
426
|
+
// succeeded. Refused here, at build time, like a oneway.
|
|
427
|
+
if (prepared.results.length === 0) {
|
|
428
|
+
throw new TypeError(
|
|
429
|
+
`[ic-reactor] ${call}: ${prepared.name} has no results, and a method without results has nothing to cache. ` +
|
|
430
|
+
`Call it directly${isWrite(prepared.mode) ? ", or through mutationOptions()" : ""}.`
|
|
431
|
+
)
|
|
432
|
+
}
|
|
433
|
+
if (isWrite(prepared.mode) && !idempotent) {
|
|
434
|
+
throw new TypeError(
|
|
435
|
+
`[ic-reactor] ${call}: ${prepared.name} is an update method, and a query refetches: every refetch would run the update again. ` +
|
|
436
|
+
`Use client.mutationOptions() for a write. Only for an update that returns the same answer however often it runs ` +
|
|
437
|
+
`(such as ckBTC's get_btc_address), pass { update: "idempotent" } as the fourth argument.`
|
|
438
|
+
)
|
|
439
|
+
}
|
|
440
|
+
if (record.certified && prepared.mode === "composite_query") {
|
|
441
|
+
throw new TypeError(
|
|
442
|
+
`[ic-reactor] ${call}: ${prepared.name} is a composite query, which has no certified path. Read it from the canister made without certified: true.`
|
|
443
|
+
)
|
|
444
|
+
}
|
|
445
|
+
// The read is made as the caller current now (or the one a view is
|
|
446
|
+
// pinned to), and only ever as them: the key holds their principal, and
|
|
447
|
+
// the query function asks for their agent, which is refused while someone
|
|
448
|
+
// else is signed in.
|
|
449
|
+
const caller = current()
|
|
450
|
+
const target = record.resolve()
|
|
451
|
+
const write = isWrite(prepared.mode)
|
|
452
|
+
const base = {
|
|
453
|
+
// A read of `aaaaa-aa` made from an update is never sent twice, like
|
|
454
|
+
// every other write to it.
|
|
455
|
+
retry:
|
|
456
|
+
write && target.ok && target.id === MANAGEMENT_CANISTER
|
|
457
|
+
? never
|
|
458
|
+
: retryQuery,
|
|
459
|
+
...(idempotent ? IDEMPOTENT : {}),
|
|
460
|
+
}
|
|
461
|
+
if (vars === skipToken) {
|
|
462
|
+
return {
|
|
463
|
+
queryKey: readKey(record, caller.principal, target, prepared.name, [
|
|
464
|
+
SKIP,
|
|
465
|
+
]),
|
|
466
|
+
queryFn: skipToken,
|
|
467
|
+
...base,
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
refuseForeignSymbol(vars, prepared, call)
|
|
471
|
+
const values = valuesOf(prepared, vars)
|
|
472
|
+
const encoded = encodeArgs(prepared.args, values)
|
|
473
|
+
return {
|
|
474
|
+
queryKey: readKey(
|
|
475
|
+
record,
|
|
476
|
+
caller.principal,
|
|
477
|
+
target,
|
|
478
|
+
prepared.name,
|
|
479
|
+
argsSegments(encoded, vars)
|
|
480
|
+
),
|
|
481
|
+
queryFn: (context: QueryFunctionContext) => {
|
|
482
|
+
const read = invoke(internals, {
|
|
483
|
+
method: prepared,
|
|
484
|
+
target,
|
|
485
|
+
certified: record.certified,
|
|
486
|
+
caller,
|
|
487
|
+
values,
|
|
488
|
+
encoded,
|
|
489
|
+
signal: context.signal,
|
|
490
|
+
timeoutMs: record.timeoutMs,
|
|
491
|
+
resend: false,
|
|
492
|
+
canister: canister as object,
|
|
493
|
+
})
|
|
494
|
+
return pinned ? keepOnRefusal(context, read) : read
|
|
495
|
+
},
|
|
496
|
+
...base,
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/** Checks the third argument of `mutationOptions`, and returns the reads it names. */
|
|
501
|
+
const readInvalidates = (
|
|
502
|
+
canister: unknown,
|
|
503
|
+
options: unknown
|
|
504
|
+
): { readonly record: CanisterRecord; readonly method?: string }[] => {
|
|
505
|
+
const call = "client.mutationOptions()"
|
|
506
|
+
if (options !== undefined) {
|
|
507
|
+
const keys =
|
|
508
|
+
typeof options === "object" && options !== null
|
|
509
|
+
? Object.keys(options)
|
|
510
|
+
: undefined
|
|
511
|
+
if (
|
|
512
|
+
keys === undefined ||
|
|
513
|
+
keys.some((key) => key !== "invalidates" && key !== "timeoutMs") ||
|
|
514
|
+
badTimeout((options as { timeoutMs?: unknown }).timeoutMs)
|
|
515
|
+
) {
|
|
516
|
+
throw new TypeError(
|
|
517
|
+
`[ic-reactor] ${call} takes { invalidates, timeoutMs } (timeoutMs: 1 to 2147483647 ms) as its third argument, or nothing; got ${describe(options)}. ` +
|
|
518
|
+
"A mutation is never retried: an update re-sent after an unknown outcome can run twice."
|
|
519
|
+
)
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
const listed = (options as { invalidates?: unknown } | undefined)
|
|
523
|
+
?.invalidates
|
|
524
|
+
if (listed === undefined) {
|
|
525
|
+
return [{ record: recordOf(canister, internals, call) }]
|
|
526
|
+
}
|
|
527
|
+
if (!Array.isArray(listed)) {
|
|
528
|
+
throw new TypeError(
|
|
529
|
+
`[ic-reactor] ${call}: invalidates is a list of canisters and [canister, method] pairs, got ${describe(listed)}.`
|
|
530
|
+
)
|
|
531
|
+
}
|
|
532
|
+
return listed.map((entry: unknown) => {
|
|
533
|
+
if (Array.isArray(entry)) {
|
|
534
|
+
const [target, method] = entry as unknown[]
|
|
535
|
+
const record = recordOf(target, internals, `${call} invalidates`)
|
|
536
|
+
return {
|
|
537
|
+
record,
|
|
538
|
+
method: methodOf(record, method, `${call} invalidates`).name,
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
return { record: recordOf(entry, internals, `${call} invalidates`) }
|
|
542
|
+
})
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const mutationOptions = (
|
|
546
|
+
canister: unknown,
|
|
547
|
+
method: unknown,
|
|
548
|
+
options?: unknown
|
|
549
|
+
) => {
|
|
550
|
+
const call = "client.mutationOptions()"
|
|
551
|
+
const record = recordOf(canister, internals, call)
|
|
552
|
+
const prepared = methodOf(record, method, call)
|
|
553
|
+
const invalidates = readInvalidates(canister, options)
|
|
554
|
+
const timeoutMs = (options as { timeoutMs?: number } | undefined)?.timeoutMs
|
|
555
|
+
// One synchronous step, so that every `{ name }` here (the canister
|
|
556
|
+
// written to, and any listed in `invalidates`) is read from the same
|
|
557
|
+
// cookie.
|
|
558
|
+
const resolveRun = (caller?: Caller): MutationRun => ({
|
|
559
|
+
caller,
|
|
560
|
+
target: record.resolve(),
|
|
561
|
+
invalidates: invalidates.map(({ record: listed, method: only }) => ({
|
|
562
|
+
slots: listed.slots(),
|
|
563
|
+
method: only,
|
|
564
|
+
})),
|
|
565
|
+
})
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* The caller a run of this write goes out as: the one current when
|
|
569
|
+
* `mutate()` started it, or `undefined` when that is not known, which
|
|
570
|
+
* refuses the write. Found, in order:
|
|
571
|
+
*
|
|
572
|
+
* - by the run's context (5.89 and later): what the client's
|
|
573
|
+
* `MutationCache` read for it, which no option replaces, else what our
|
|
574
|
+
* `onMutate` read (a run on another `QueryClient`). Neither, for a run
|
|
575
|
+
* TanStack restored from a persister (it runs no `onMutate` for one),
|
|
576
|
+
* or one on another `QueryClient` whose `onMutate` replaced ours.
|
|
577
|
+
* - without a context (before 5.89, or a call by hand), by `options`,
|
|
578
|
+
* the object TanStack called `mutationFn` on, which the pending runs
|
|
579
|
+
* of one observer share: the caller its `MutationCache` read for the
|
|
580
|
+
* pending runs of the client's own cache with those options, if they
|
|
581
|
+
* all started as one (none for a restored run), else what our
|
|
582
|
+
* `onMutate` read for them (runs on another `QueryClient`, the same
|
|
583
|
+
* rule). Only when no `onMutate` of ours ever ran without a context
|
|
584
|
+
* for this method is this a call by hand, made as the caller current
|
|
585
|
+
* now.
|
|
586
|
+
*/
|
|
587
|
+
const callerOf = (
|
|
588
|
+
options: unknown,
|
|
589
|
+
key: object | undefined,
|
|
590
|
+
run: MutationRun | undefined
|
|
591
|
+
): Caller | undefined => {
|
|
592
|
+
if (key !== undefined) {
|
|
593
|
+
return internals.mutationCallers.get(key) ?? run?.caller
|
|
594
|
+
}
|
|
595
|
+
const own = ownRuns(options)
|
|
596
|
+
if (own.length > 0) {
|
|
597
|
+
const callers = own.map((run) => internals.mutationCallers.get(run))
|
|
598
|
+
const [first] = callers
|
|
599
|
+
return callers.every((caller) => sameCaller(caller, first))
|
|
600
|
+
? first
|
|
601
|
+
: undefined
|
|
602
|
+
}
|
|
603
|
+
const read =
|
|
604
|
+
typeof options === "object" && options !== null
|
|
605
|
+
? callersByOptions.get(options)
|
|
606
|
+
: undefined
|
|
607
|
+
if (read !== undefined) return read.caller
|
|
608
|
+
return startedMethods.has(prepared) ? undefined : internals.current()
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
// The write goes out as the caller current when `mutate()` started it
|
|
612
|
+
// (`callerOf`), or not at all: `agentFor` refuses it (`caller_changed`,
|
|
613
|
+
// nothing sent) while someone else is current, as when TanStack held it
|
|
614
|
+
// back offline or behind its `scope` and the account was switched
|
|
615
|
+
// meanwhile, and a caller not known is refused here. A re-send
|
|
616
|
+
// `resendOf` offered goes out as its first sender, to the canister id it
|
|
617
|
+
// went to, with the bytes it had, or not at all (`run` checks the rest).
|
|
618
|
+
// The target is the one onMutate resolved for this run where TanStack
|
|
619
|
+
// passes the run's context (5.89 and later), and is resolved now
|
|
620
|
+
// otherwise.
|
|
621
|
+
const mutationFn = function (
|
|
622
|
+
this: unknown,
|
|
623
|
+
vars: unknown,
|
|
624
|
+
context?: unknown
|
|
625
|
+
) {
|
|
626
|
+
// TanStack hands a pending mutation the options of a later render
|
|
627
|
+
// only when their mutationKey is the same (another key detaches the
|
|
628
|
+
// observer instead), so a run found here was made by options for the
|
|
629
|
+
// same canister and method.
|
|
630
|
+
let run = keptRun(context)
|
|
631
|
+
const key = runKey(context)
|
|
632
|
+
if (run === undefined && key !== undefined) {
|
|
633
|
+
// An onMutate of the app's replaced ours, so nothing is resolved
|
|
634
|
+
// for this run yet: resolve it now, and keep it for onSettled,
|
|
635
|
+
// which TanStack hands the same context.
|
|
636
|
+
run = resolveRun()
|
|
637
|
+
runsByContext.set(key, run)
|
|
638
|
+
}
|
|
639
|
+
const target = run?.target ?? record.resolve()
|
|
640
|
+
const again = offered.get(vars as object)
|
|
641
|
+
const caller = again?.caller ?? callerOf(this, key, run)
|
|
642
|
+
// A disposed client cancels every call itself (`client_disposed`).
|
|
643
|
+
if (caller === undefined && !internals.disposed()) {
|
|
644
|
+
return Promise.reject(
|
|
645
|
+
fromCanister(
|
|
646
|
+
createReactorError("cancelled", {
|
|
647
|
+
method: prepared.name,
|
|
648
|
+
canisterId: target.ok ? target.id : target.slot,
|
|
649
|
+
code: "caller_changed",
|
|
650
|
+
reason: "who started this write is not known; it was not sent",
|
|
651
|
+
}),
|
|
652
|
+
canister as object
|
|
653
|
+
)
|
|
654
|
+
)
|
|
655
|
+
}
|
|
656
|
+
return invoke(internals, {
|
|
657
|
+
method: prepared,
|
|
658
|
+
target,
|
|
659
|
+
certified: record.certified,
|
|
660
|
+
caller: caller ?? internals.current(),
|
|
661
|
+
values: valuesOf(prepared, vars),
|
|
662
|
+
timeoutMs: timeoutMs ?? record.timeoutMs,
|
|
663
|
+
resend: true,
|
|
664
|
+
canister: canister as object,
|
|
665
|
+
again,
|
|
666
|
+
})
|
|
667
|
+
}
|
|
668
|
+
internals.ownMutationFns.add(mutationFn)
|
|
669
|
+
|
|
670
|
+
return {
|
|
671
|
+
mutationKey: [
|
|
672
|
+
KEY_ROOT,
|
|
673
|
+
network,
|
|
674
|
+
slotOf(record.resolve()),
|
|
675
|
+
prepared.name,
|
|
676
|
+
] as const,
|
|
677
|
+
// TanStack runs this first, before mutationFn, and hands what it
|
|
678
|
+
// returns to onSettled. The caller it reads matters only on a
|
|
679
|
+
// `QueryClient` other than the client's, whose own `MutationCache`
|
|
680
|
+
// reads it otherwise.
|
|
681
|
+
onMutate: function (
|
|
682
|
+
this: unknown,
|
|
683
|
+
_vars: unknown,
|
|
684
|
+
context?: unknown
|
|
685
|
+
): object {
|
|
686
|
+
const caller = internals.current()
|
|
687
|
+
const run = resolveRun(caller)
|
|
688
|
+
const key = runKey(context)
|
|
689
|
+
if (key !== undefined) runsByContext.set(key, run)
|
|
690
|
+
else {
|
|
691
|
+
startedMethods.add(prepared)
|
|
692
|
+
if (typeof this === "object" && this !== null) {
|
|
693
|
+
const kept = callersByOptions.get(this)
|
|
694
|
+
callersByOptions.set(
|
|
695
|
+
this,
|
|
696
|
+
kept === undefined || kept.runs === 0
|
|
697
|
+
? { caller, runs: 1 }
|
|
698
|
+
: {
|
|
699
|
+
caller: sameCaller(kept.caller, caller)
|
|
700
|
+
? caller
|
|
701
|
+
: undefined,
|
|
702
|
+
runs: kept.runs + 1,
|
|
703
|
+
}
|
|
704
|
+
)
|
|
705
|
+
}
|
|
706
|
+
}
|
|
707
|
+
return { [RUN]: run }
|
|
708
|
+
},
|
|
709
|
+
mutationFn,
|
|
710
|
+
retry: false as const,
|
|
711
|
+
onSettled: async function (
|
|
712
|
+
this: unknown,
|
|
713
|
+
_data: unknown,
|
|
714
|
+
error: unknown,
|
|
715
|
+
_vars: unknown,
|
|
716
|
+
onMutateResult?: unknown,
|
|
717
|
+
context?: unknown
|
|
718
|
+
): Promise<void> {
|
|
719
|
+
// A run of another QueryClient with no context settled: one fewer
|
|
720
|
+
// shares its options' caller.
|
|
721
|
+
const kept =
|
|
722
|
+
runKey(context) === undefined &&
|
|
723
|
+
typeof this === "object" &&
|
|
724
|
+
this !== null
|
|
725
|
+
? callersByOptions.get(this)
|
|
726
|
+
: undefined
|
|
727
|
+
if (kept !== undefined && kept.runs > 0) {
|
|
728
|
+
callersByOptions.set(this as object, {
|
|
729
|
+
caller: kept.runs > 1 ? kept.caller : undefined,
|
|
730
|
+
runs: kept.runs - 1,
|
|
731
|
+
})
|
|
732
|
+
}
|
|
733
|
+
if (!mayHaveChanged(error)) return
|
|
734
|
+
// The reads of the run mutationFn wrote with, found by the run's
|
|
735
|
+
// context (5.89 and later); else those onMutate returned (before
|
|
736
|
+
// 5.89); else, when an onMutate of the app's replaced ours before
|
|
737
|
+
// 5.89 or onSettled is called by hand, the reads resolved now.
|
|
738
|
+
const run = keptRun(context) ?? runIn(onMutateResult) ?? resolveRun()
|
|
739
|
+
const targets = run.invalidates
|
|
740
|
+
if (targets.length === 0) return
|
|
741
|
+
// Every caller's reads, certified or not: a write changes what the
|
|
742
|
+
// canister answers to everyone. On the QueryClient the mutation ran
|
|
743
|
+
// on, which TanStack (5.89 and later) hands onSettled, as well as the
|
|
744
|
+
// client's own: an app that nests its own QueryClientProvider keeps
|
|
745
|
+
// its reads there (#903).
|
|
746
|
+
const own = owner().queryClient
|
|
747
|
+
const ran = (runKey(context) as { client?: QueryClient } | undefined)
|
|
748
|
+
?.client
|
|
749
|
+
const queryClients =
|
|
750
|
+
ran !== undefined && ran !== own ? [own, ran] : [own]
|
|
751
|
+
const filters = {
|
|
752
|
+
predicate: ({ queryKey: key }: { queryKey: QueryKey }) =>
|
|
753
|
+
key[0] === KEY_ROOT &&
|
|
754
|
+
key[1] === network &&
|
|
755
|
+
targets.some(
|
|
756
|
+
({ slots, method: only }) =>
|
|
757
|
+
slots.includes(key[3] as string) &&
|
|
758
|
+
(only === undefined || key[4] === only)
|
|
759
|
+
),
|
|
760
|
+
}
|
|
761
|
+
await Promise.all(
|
|
762
|
+
queryClients.map((queryClient) =>
|
|
763
|
+
queryClient.invalidateQueries(filters)
|
|
764
|
+
)
|
|
765
|
+
)
|
|
766
|
+
},
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
const resendOf = (
|
|
771
|
+
error: unknown,
|
|
772
|
+
canister: unknown,
|
|
773
|
+
method: unknown,
|
|
774
|
+
options?: { readonly dedupedBy?: unknown }
|
|
775
|
+
) => {
|
|
776
|
+
const call = "client.resendOf()"
|
|
777
|
+
const record = recordOf(canister, internals, call)
|
|
778
|
+
const prepared = methodOf(record, method, call)
|
|
779
|
+
const { name, args } = prepared
|
|
780
|
+
const dedupedBy = options?.dedupedBy
|
|
781
|
+
if (typeof dedupedBy !== "function") {
|
|
782
|
+
throw new TypeError(
|
|
783
|
+
`[ic-reactor] ${call} needs { dedupedBy: (arg) => key }: what makes ${name} run the same argument at most once, such as an ICRC-1 transfer's created_at_time. ` +
|
|
784
|
+
"A write without one has no safe re-send."
|
|
785
|
+
)
|
|
786
|
+
}
|
|
787
|
+
// Kept only for a write whose outcome is unknown, made on `canister`
|
|
788
|
+
// (`differs` below compares the method).
|
|
789
|
+
const attempt = UNKNOWN_WRITES.get(error as object)
|
|
790
|
+
if (attempt === undefined || attempt.canister !== canister) {
|
|
791
|
+
return undefined
|
|
792
|
+
}
|
|
793
|
+
const { values, caller } = attempt
|
|
794
|
+
const arg = args.length > 1 ? values : values[0]
|
|
795
|
+
if (
|
|
796
|
+
typeof arg !== "object" ||
|
|
797
|
+
arg === null ||
|
|
798
|
+
// The sender, and no one else, may send it again.
|
|
799
|
+
internals.current().principal !== caller.principal ||
|
|
800
|
+
// Only for the method it called, while the argument is what was sent
|
|
801
|
+
// and the canister object still resolves to the canister it went to.
|
|
802
|
+
differs(attempt, prepared, record.resolve(), encodeArgs(args, values)) ||
|
|
803
|
+
(dedupedBy(arg) ?? null) === null
|
|
804
|
+
) {
|
|
805
|
+
return undefined
|
|
806
|
+
}
|
|
807
|
+
offered.set(arg, attempt)
|
|
808
|
+
const sent = (attempt.encoded as { bytes: Uint8Array }).bytes
|
|
809
|
+
return {
|
|
810
|
+
arg,
|
|
811
|
+
from: caller.principal,
|
|
812
|
+
// A copy on every read: `send()` sends the attempt's own bytes, which
|
|
813
|
+
// nothing outside the client can reach to change.
|
|
814
|
+
get bytes() {
|
|
815
|
+
return sent.slice()
|
|
816
|
+
},
|
|
817
|
+
// The attempt itself: its canister id and the bytes it sent.
|
|
818
|
+
// Marked as a re-send of itself, which it is: a timeout before it
|
|
819
|
+
// goes out keeps its unknown outcome (see `run`).
|
|
820
|
+
send: () => invoke(internals, { ...attempt, again: attempt }),
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* The bytes a call of `method` with `vars` sends: the same arity rule and
|
|
826
|
+
* the same encoder as the call path, so they are those bytes, not a second
|
|
827
|
+
* encoding of the value.
|
|
828
|
+
*/
|
|
829
|
+
const encode = (canister: unknown, method: unknown, vars: unknown) => {
|
|
830
|
+
const call = "client.encode()"
|
|
831
|
+
const record = recordOf(canister, internals, call)
|
|
832
|
+
const prepared = methodOf(record, method, call)
|
|
833
|
+
const encoded = encodeArgs(prepared.args, valuesOf(prepared, vars))
|
|
834
|
+
if (!encoded.ok) {
|
|
835
|
+
// Marked with the canister, as a call's error is, so that
|
|
836
|
+
// `isReactorError(error, canister, method)` reads its issues.
|
|
837
|
+
throw fromCanister(
|
|
838
|
+
notEncoded(
|
|
839
|
+
{ method: prepared.name, canisterId: slotOf(record.resolve()) },
|
|
840
|
+
encoded
|
|
841
|
+
),
|
|
842
|
+
canister as object
|
|
843
|
+
)
|
|
844
|
+
}
|
|
845
|
+
return encoded.bytes
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
const func = (funcSchema: unknown, ref: unknown) => {
|
|
849
|
+
const call = "client.func()"
|
|
850
|
+
let node: ReturnType<typeof resolveSchema>
|
|
851
|
+
try {
|
|
852
|
+
node = resolveSchema(funcSchema as Parameters<typeof resolveSchema>[0])
|
|
853
|
+
} catch (cause) {
|
|
854
|
+
const error = new TypeError(
|
|
855
|
+
`[ic-reactor] ${call} takes a func schema, such as the generated QueryArchiveFn: ${
|
|
856
|
+
cause instanceof Error ? cause.message : String(cause)
|
|
857
|
+
}.`
|
|
858
|
+
)
|
|
859
|
+
Object.defineProperty(error, "cause", { value: cause })
|
|
860
|
+
throw error
|
|
861
|
+
}
|
|
862
|
+
if (node.kind !== "func") {
|
|
863
|
+
throw new TypeError(
|
|
864
|
+
`[ic-reactor] ${call} takes a func schema, such as the generated QueryArchiveFn; got a ${node.kind} schema.`
|
|
865
|
+
)
|
|
866
|
+
}
|
|
867
|
+
const { principal, method } = (
|
|
868
|
+
typeof ref === "object" && ref !== null ? ref : {}
|
|
869
|
+
) as { principal?: unknown; method?: unknown }
|
|
870
|
+
if (!isPrincipal(principal)) {
|
|
871
|
+
throw new TypeError(
|
|
872
|
+
`[ic-reactor] ${call}: ${describe(principal)} is not a canister's principal text. Pass the func value the reply carried, { principal, method }.`
|
|
873
|
+
)
|
|
874
|
+
}
|
|
875
|
+
if (typeof method !== "string" || method === "") {
|
|
876
|
+
throw new TypeError(
|
|
877
|
+
`[ic-reactor] ${call}: ${describe(method)} is not a method name. Pass the func value the reply carried, { principal, method }.`
|
|
878
|
+
)
|
|
879
|
+
}
|
|
880
|
+
const prepared = prepareMethod({
|
|
881
|
+
name: method,
|
|
882
|
+
mode: node.mode,
|
|
883
|
+
args: node.args,
|
|
884
|
+
results: node.results,
|
|
885
|
+
})
|
|
886
|
+
const target: ResolvedTarget = { ok: true, id: principal }
|
|
887
|
+
return async (...args: unknown[]): Promise<unknown> =>
|
|
888
|
+
invoke(internals, {
|
|
889
|
+
method: prepared,
|
|
890
|
+
target,
|
|
891
|
+
certified: false,
|
|
892
|
+
caller: internals.current(),
|
|
893
|
+
values: args,
|
|
894
|
+
resend: true,
|
|
895
|
+
})
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* The IC's time, from a certified `read_state` of the `time` path routed by
|
|
900
|
+
* the canister's subnet, sent by the caller's agent. The certificate's
|
|
901
|
+
* signature is verified against the network's root key; its freshness is
|
|
902
|
+
* not compared with the host clock, which may be the clock that is wrong
|
|
903
|
+
* (as `HttpAgent.syncTime()` reads it).
|
|
904
|
+
*/
|
|
905
|
+
const time = async (canister: unknown): Promise<bigint> => {
|
|
906
|
+
const record = recordOf(canister, internals, "client.time()")
|
|
907
|
+
const target = record.resolve()
|
|
908
|
+
const method = "read_state"
|
|
909
|
+
if (!target.ok) {
|
|
910
|
+
throw createReactorError("invalid_args", {
|
|
911
|
+
method,
|
|
912
|
+
canisterId: target.slot,
|
|
913
|
+
code: "canister_id_unresolved",
|
|
914
|
+
reason: target.message,
|
|
915
|
+
})
|
|
916
|
+
}
|
|
917
|
+
// A read_state is routed by a canister in a subnet's range, and the
|
|
918
|
+
// management canister is in none: the IC would refuse it.
|
|
919
|
+
if (target.id === MANAGEMENT_CANISTER) {
|
|
920
|
+
throw createReactorError("invalid_args", {
|
|
921
|
+
method,
|
|
922
|
+
canisterId: target.id,
|
|
923
|
+
code: "management_canister",
|
|
924
|
+
reason: "the management canister routes no read_state",
|
|
925
|
+
})
|
|
926
|
+
}
|
|
927
|
+
const where = { method, canisterId: target.id }
|
|
928
|
+
const ms = record.timeoutMs
|
|
929
|
+
let timedOut = false
|
|
930
|
+
const read = readTime(where, () => timedOut)
|
|
931
|
+
if (ms === undefined) return read
|
|
932
|
+
// The canister object's timeoutMs bounds the wait from here, as it bounds
|
|
933
|
+
// a call through it: when it passes, the time rejects `cancelled` (code
|
|
934
|
+
// `timeout`), and a read_state not sent yet is never sent. One already
|
|
935
|
+
// out goes on, but nobody waits for it.
|
|
936
|
+
let timer: ReturnType<typeof setTimeout> | undefined
|
|
937
|
+
const timeout = new Promise<never>((_, reject) => {
|
|
938
|
+
timer = setTimeout(() => {
|
|
939
|
+
timedOut = true
|
|
940
|
+
reject(timedOutError(where, false))
|
|
941
|
+
}, ms)
|
|
942
|
+
})
|
|
943
|
+
try {
|
|
944
|
+
return await Promise.race([read, timeout])
|
|
945
|
+
} finally {
|
|
946
|
+
clearTimeout(timer)
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
/**
|
|
951
|
+
* The IC's time, read for `client.time()`. `timedOut` says whether its
|
|
952
|
+
* bound passed while the caller's agent was awaited: nothing is sent then.
|
|
953
|
+
*/
|
|
954
|
+
const readTime = async (
|
|
955
|
+
where: { readonly method: string; readonly canisterId: string },
|
|
956
|
+
timedOut: () => boolean
|
|
957
|
+
): Promise<bigint> => {
|
|
958
|
+
const agent = await internals.agentFor(internals.current().principal, where)
|
|
959
|
+
// As in the call path: a dispose() in the microtask after agentFor's own
|
|
960
|
+
// check still sends nothing.
|
|
961
|
+
if (internals.disposed()) {
|
|
962
|
+
throw createReactorError("cancelled", {
|
|
963
|
+
...where,
|
|
964
|
+
code: "client_disposed",
|
|
965
|
+
reason: "the client was disposed before the time was read",
|
|
966
|
+
})
|
|
967
|
+
}
|
|
968
|
+
if (timedOut()) throw timedOutError(where, false)
|
|
969
|
+
try {
|
|
970
|
+
// The agent verifies the certificate against the network's root key
|
|
971
|
+
// and the canister's subnet delegation. Its age is not checked: that
|
|
972
|
+
// would compare it with the host's clock, the clock this replaces, and
|
|
973
|
+
// a host clock out by minutes would first make the agent re-sync.
|
|
974
|
+
const { verifiedCertificate } = await agent.readState(
|
|
975
|
+
{ canisterId: Principal.fromText(where.canisterId) },
|
|
976
|
+
{ paths: [TIME_PATH], disableTimeVerification: true }
|
|
977
|
+
)
|
|
978
|
+
const raw = lookupResultToBuffer(
|
|
979
|
+
verifiedCertificate.lookup_path(TIME_PATH)
|
|
980
|
+
)
|
|
981
|
+
if (raw === undefined) {
|
|
982
|
+
throw new Error("the certificate holds no time")
|
|
983
|
+
}
|
|
984
|
+
return readLeb128(raw)
|
|
985
|
+
} catch (error) {
|
|
986
|
+
throw classifyError(error, { ...where, mode: "query" })
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
return {
|
|
991
|
+
canister: canisterFactory(internals),
|
|
992
|
+
time,
|
|
993
|
+
queryKey,
|
|
994
|
+
queryOptions,
|
|
995
|
+
mutationOptions,
|
|
996
|
+
resendOf,
|
|
997
|
+
encode,
|
|
998
|
+
func,
|
|
999
|
+
} as unknown as Builders
|
|
1000
|
+
}
|