@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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,3268 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Notable changes to the published `@ic-reactor/*` packages. ic-reactor 4
|
|
4
|
+
releases from `main` in one lane, `@ic-reactor/core`, `@ic-reactor/react`,
|
|
5
|
+
`@ic-reactor/vite-plugin` and `@ic-reactor/mcp` at one version: stable
|
|
6
|
+
versions under npm's `latest` dist-tag, prereleases under `beta`. The 3.x
|
|
7
|
+
line, on the `v3` branch since the 4.0 GA flip, releases in three lanes, each
|
|
8
|
+
with its own version:
|
|
9
|
+
|
|
10
|
+
- runtime: `@ic-reactor/core`, `@ic-reactor/react`, `@ic-reactor/candid`
|
|
11
|
+
- codegen: `@ic-reactor/codegen`, `@ic-reactor/cli`, `@ic-reactor/vite-plugin`
|
|
12
|
+
- parser: `@ic-reactor/parser`
|
|
13
|
+
|
|
14
|
+
The GitHub release of each tag also lists every pull request it contains.
|
|
15
|
+
What a 4.x release may change, from 4.0.0 on, is in
|
|
16
|
+
[Versioning and support](docs/src/content/docs/versioning.mdx).
|
|
17
|
+
Issue numbers below refer to https://github.com/B3Pay/ic-reactor/issues.
|
|
18
|
+
|
|
19
|
+
## Unreleased
|
|
20
|
+
|
|
21
|
+
### New: @ic-reactor/mcp
|
|
22
|
+
|
|
23
|
+
`@ic-reactor/mcp` is an MCP server that gives an AI agent any Internet
|
|
24
|
+
Computer canister's methods as tools. Each canister named with
|
|
25
|
+
`--canister <name>=<id>` gets one tool per method, with an input schema built
|
|
26
|
+
from the canister's certified `candid:service` metadata, and `list_methods`,
|
|
27
|
+
`describe_method` and `query` read any canister. An update is sent only to a
|
|
28
|
+
listed canister, signed with the key given as `--identity`, after the person
|
|
29
|
+
approves the exact call in the MCP client's form (MCP form elicitation): the
|
|
30
|
+
form shows the canister, method, caller, network, the arguments as Candid text
|
|
31
|
+
and the sha256 of the encoded bytes, and the canister's module hash is
|
|
32
|
+
confirmed before the form and again before the call is sent. A certified read
|
|
33
|
+
goes out as the anonymous principal, never with the key, and is read with the
|
|
34
|
+
interface of the module installed now. Input schemas, `tools/list`, method
|
|
35
|
+
names in the form and each call's wait are bounded, so a hostile interface
|
|
36
|
+
cannot exhaust the server's memory or crowd the form.
|
|
37
|
+
|
|
38
|
+
It releases with core, react and vite-plugin, at their version, and installs
|
|
39
|
+
one command: `npm install --global @ic-reactor/mcp`, then
|
|
40
|
+
`ic-reactor-mcp --help`. Its page,
|
|
41
|
+
[@ic-reactor/mcp](https://ic-reactor.b3pay.net/v4/packages/mcp/), has the
|
|
42
|
+
tools, the approval rules and the limits, and `packages/mcp/README.md` the
|
|
43
|
+
JSON forms of Candid values too.
|
|
44
|
+
|
|
45
|
+
## core, react, vite-plugin 4.1.1
|
|
46
|
+
|
|
47
|
+
Prepared on 2026-11-06. 4.1.1 moves `@ic-reactor/vite-plugin` to
|
|
48
|
+
`@meantjs/cli` 0.5.1, Meant's patch with the corrected source nesting count.
|
|
49
|
+
`@ic-reactor/core` and `@ic-reactor/react` change only their version: the
|
|
50
|
+
schema peer stays `@meantjs/schema` 0.6.0. The release adds or removes no
|
|
51
|
+
`@ic-reactor/*` export (core 13, `@ic-reactor/core/testing` 2, react 4,
|
|
52
|
+
vite-plugin 2), and committed modules do not change.
|
|
53
|
+
|
|
54
|
+
The plugin pins the CLI exactly, so an app on 4.1.0 moves the CLI with it:
|
|
55
|
+
|
|
56
|
+
```sh
|
|
57
|
+
npm install --save-exact @ic-reactor/core@4.1.1 @ic-reactor/react@4.1.1
|
|
58
|
+
npm install --save-dev --save-exact @ic-reactor/vite-plugin@4.1.1 @meantjs/cli@0.5.1
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Name only the packages the app has.
|
|
62
|
+
|
|
63
|
+
### @ic-reactor/vite-plugin
|
|
64
|
+
|
|
65
|
+
#### Changed
|
|
66
|
+
|
|
67
|
+
- Peer `@meantjs/cli` at exactly `0.5.1`, where it was exactly `0.5.0`, and
|
|
68
|
+
the install hints name `@meantjs/cli@0.5.1`. Meant's 0.5.1 is 0.5.0 with
|
|
69
|
+
one compiler fix, a hardening: the source nesting limit counts every `opt`
|
|
70
|
+
and `vec` until its operand ends, so a `.did` nested past the limit is
|
|
71
|
+
refused before parsing with `resource_limit_exceeded`, where 0.5.0 could
|
|
72
|
+
run out of stack on it and fail that canister with `internal_error`. A type
|
|
73
|
+
nested exactly to the type depth limit whose innermost `record {}`,
|
|
74
|
+
`variant {}` or `func () -> ()` is empty now compiles, where 0.5.0 refused
|
|
75
|
+
it. For a `.did` both accept, the module and the envelope are the same, so
|
|
76
|
+
committed modules do not change. Move the CLI with the plugin, both exact.
|
|
77
|
+
|
|
78
|
+
### Examples (not published)
|
|
79
|
+
|
|
80
|
+
- `node-agent-tool` no longer prints text from a ledger raw. Without
|
|
81
|
+
`--json`, a token's name or symbol and an error message are printed with
|
|
82
|
+
each control character and bidirectional formatting character spelled out
|
|
83
|
+
(`\u{1b}`), so a ledger can't move the cursor, rewrite earlier lines, add a
|
|
84
|
+
line that looks like the CLI's own or reverse an amount. With `--json`,
|
|
85
|
+
DEL, the C1 controls and those formatting characters are `\u` escapes, as
|
|
86
|
+
the C0 controls already were.
|
|
87
|
+
|
|
88
|
+
### CI (not published)
|
|
89
|
+
|
|
90
|
+
- A release's run of the published-examples check waits until npm serves
|
|
91
|
+
each package's tarball, not only until the dist-tag and the integrity name
|
|
92
|
+
the release (`waitForPublished` in `scripts/test-examples-published.mjs`).
|
|
93
|
+
On 4.1.0's release run, npm answered 404 for core's tarball for about five
|
|
94
|
+
minutes after its dist-tag named 4.1.0, and every example's install failed
|
|
95
|
+
on it.
|
|
96
|
+
|
|
97
|
+
## core, react, vite-plugin 4.1.0
|
|
98
|
+
|
|
99
|
+
Prepared on 2026-11-02. 4.1.0 moves ic-reactor 4 onto Meant, the new name of
|
|
100
|
+
the candid-core pair, and changes two things `@ic-reactor/core` does at run
|
|
101
|
+
time: what the client keeps after the caller changes, and what it believes an
|
|
102
|
+
`ic_env` cookie for. Each of those entries, under `@ic-reactor/core` below,
|
|
103
|
+
says what an app does about it. Both packages of the pair are pinned exactly,
|
|
104
|
+
where 4.0.0 required `@candid-core/schema` `0.4.0` and `@candid-core/cli`
|
|
105
|
+
`0.3.0`:
|
|
106
|
+
|
|
107
|
+
- `@ic-reactor/core`: peer `@meantjs/schema` at exactly `0.6.0`;
|
|
108
|
+
- `@ic-reactor/vite-plugin`: peer `@meantjs/cli` at exactly `0.5.0`, the
|
|
109
|
+
version its install hints name.
|
|
110
|
+
|
|
111
|
+
`@meantjs/schema` 0.6.0 is `@candid-core/schema` 0.5.0 under the new name,
|
|
112
|
+
and `@meantjs/cli` 0.5.0 is `@candid-core/cli` 0.4.0 under the new name, with
|
|
113
|
+
one command, `meant`, where it had `candid-core-cli`. The `@candid-core/*`
|
|
114
|
+
packages get no new version. Meant's release notes say what each of the four
|
|
115
|
+
versions changed; what reaches an app through ic-reactor is below.
|
|
116
|
+
|
|
117
|
+
The repin adds or removes no `@ic-reactor/*` export (core 13,
|
|
118
|
+
`@ic-reactor/core/testing` 2, react 4, vite-plugin 2), and changes no call,
|
|
119
|
+
key or error at run time.
|
|
120
|
+
|
|
121
|
+
What an app on 4.0.0 does for the repin:
|
|
122
|
+
|
|
123
|
+
1. Bump the three `@ic-reactor/*` packages and move to the new pair, then
|
|
124
|
+
remove the old one. The new names are new packages to npm, so each goes in
|
|
125
|
+
the section of `package.json` its install command names:
|
|
126
|
+
|
|
127
|
+
```sh
|
|
128
|
+
npm install --save-exact @ic-reactor/core@4.1.0 @ic-reactor/react@4.1.0 @meantjs/schema@0.6.0
|
|
129
|
+
npm install --save-dev --save-exact @ic-reactor/vite-plugin@4.1.0 @meantjs/cli@0.5.0
|
|
130
|
+
npm uninstall @candid-core/schema @candid-core/cli
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Name only the packages the app has.
|
|
134
|
+
|
|
135
|
+
2. Replace `candid-core-cli` with `meant` in the scripts that call the
|
|
136
|
+
generator: `"gen": "meant gen icrc1.did -o src/canisters"`. A one-off
|
|
137
|
+
command is `npx @meantjs/cli@0.5.0 gen …`: where `@meantjs/cli` is not
|
|
138
|
+
installed, `npx meant` fetches and runs another package, the one named
|
|
139
|
+
`meant` on npm.
|
|
140
|
+
|
|
141
|
+
3. Regenerate every committed module with `meant gen` (the Vite plugin does
|
|
142
|
+
it when it starts). Each module's first two lines change, to
|
|
143
|
+
`// Generated by Meant from a Meant Contract. Do not edit.` and
|
|
144
|
+
`import * as $ from "@meantjs/schema";`, and every envelope beside one
|
|
145
|
+
names `meant` 0.1.0 as its producer. Until then `meant gen --check`
|
|
146
|
+
reports them `drifted` and exits 1. A module also changes where a declared
|
|
147
|
+
`blob`, `record {}` or `variant {}` is used: each use is written as the
|
|
148
|
+
shape itself (`Uint8Array`, `Record<string, never>`, `never`), where it
|
|
149
|
+
was written as the first alias of that shape. `memo: Subaccount | null`
|
|
150
|
+
becomes `memo: Uint8Array | null`, the same type, so code that imports
|
|
151
|
+
the generated names compiles unchanged.
|
|
152
|
+
|
|
153
|
+
4. Replace `@candid-core/schema` with `@meantjs/schema` in every import of
|
|
154
|
+
your own code; the subpaths keep their names (`@meantjs/schema/codec`,
|
|
155
|
+
`/validate`, `/contract`). Keep one copy of the schema. Each package
|
|
156
|
+
declares its own brands, so a `Principal` typed by `@candid-core/schema`
|
|
157
|
+
passed where core expects one of `@meantjs/schema` fails with `TS2322` or
|
|
158
|
+
`TS2345`.
|
|
159
|
+
|
|
160
|
+
5. Where your own code calls `decode` or `decodeArgs` from
|
|
161
|
+
`@meantjs/schema/codec`, the value is now the schema's own type, not
|
|
162
|
+
`unknown`, so a cast to a type the schema does not describe fails to
|
|
163
|
+
compile: drop the cast, or decode with the schema that describes the
|
|
164
|
+
type.
|
|
165
|
+
|
|
166
|
+
### @ic-reactor/core
|
|
167
|
+
|
|
168
|
+
#### Changed
|
|
169
|
+
|
|
170
|
+
- Peer `@meantjs/schema` at exactly `0.6.0`, where it was
|
|
171
|
+
`@candid-core/schema` at exactly `0.4.0`. The package imports the schema
|
|
172
|
+
runtime under that name, in its JavaScript and its types.
|
|
173
|
+
- The README and `llms.txt` name the new pair and `meant gen`.
|
|
174
|
+
- A sign-out, an expired session or a switch of account now removes the
|
|
175
|
+
previous principal's reads from the client's `QueryClient`, before any
|
|
176
|
+
listener, or any component, hears of the change. Their keys already kept
|
|
177
|
+
them from the new caller's reads. Now they also leave memory, where
|
|
178
|
+
devtools, a read by key prefix (`getQueriesData({ queryKey: ["ic-reactor"] })`)
|
|
179
|
+
or a persister that saves the cache could still reach them in a shared
|
|
180
|
+
browser. The anonymous reads and the app's own queries stay. Migration:
|
|
181
|
+
none for an app that builds its options from `useClient()`. Switching back
|
|
182
|
+
to an account reads its data again instead of showing a cached copy.
|
|
183
|
+
- On a canister's own `<id>.icp0.io` page, where the client believes the
|
|
184
|
+
`ic_env` cookie for canister ids without `allowEnvConfig`, it now takes
|
|
185
|
+
canister ids from it and nothing else. The `auth` factory is no longer
|
|
186
|
+
handed the cookie's `INTERNET_IDENTITY_PROVIDER` and
|
|
187
|
+
`PUBLIC_CANISTER_ID:internet_identity` there, so
|
|
188
|
+
`new AuthClient(network)` signs in with mainnet's Internet Identity.
|
|
189
|
+
Migration: an app on `icp0.io` that signs in with its own Internet Identity
|
|
190
|
+
names it in the factory,
|
|
191
|
+
`(network) => new AuthClient({ ...network, identityProvider: { authorizeUrl, canisterId } })`,
|
|
192
|
+
or writes `allowEnvConfig: true`.
|
|
193
|
+
- Off a local page, when a page receives more than one `ic_env` cookie and
|
|
194
|
+
they differ, the client believes none of them: a `{ name }` target is
|
|
195
|
+
refused with the reason `untrusted_host` (a call on it rejects
|
|
196
|
+
`canister_id_unresolved`), and neither their root key nor their Internet
|
|
197
|
+
Identity is taken. Copies that are the same are believed, and a local page
|
|
198
|
+
reads the first, as before. Migration: none for an app that bakes its
|
|
199
|
+
canister ids into the build, as a mainnet app should. An app that names
|
|
200
|
+
canisters on `icp0.io` can be refused after a deploy that changes the
|
|
201
|
+
cookie, on a page whose browser still holds the previous copy for another
|
|
202
|
+
directory, until a page in that directory is loaded again.
|
|
203
|
+
|
|
204
|
+
### @ic-reactor/vite-plugin
|
|
205
|
+
|
|
206
|
+
#### Changed
|
|
207
|
+
|
|
208
|
+
- Peer `@meantjs/cli` at exactly `0.5.0`, where it was `@candid-core/cli` at
|
|
209
|
+
exactly `0.3.0`. The plugin resolves `@meantjs/cli` from the app and runs
|
|
210
|
+
its `meant` bin for `gen`, `project` and `check`. In an app that has only
|
|
211
|
+
the old CLI, each canister fails to generate with
|
|
212
|
+
`cannot find @meantjs/cli from <root>` and the install line, a generation
|
|
213
|
+
failure that follows `failOnError`. The plugin's messages name the command
|
|
214
|
+
`meant`, and its install hints `@meantjs/cli@0.5.0`.
|
|
215
|
+
|
|
216
|
+
### Docs
|
|
217
|
+
|
|
218
|
+
- Every page, the README of each package and the skill name `@meantjs/schema`,
|
|
219
|
+
`@meantjs/cli` and `meant gen` where they named the candid-core pair and
|
|
220
|
+
`candid-core-cli gen`. The home page, its library card and the library
|
|
221
|
+
switcher call the library Meant.
|
|
222
|
+
- The auth guide's "Data" note says what a sign-out removes from the cache,
|
|
223
|
+
and how to keep a persister from restoring a signed-in principal's reads on
|
|
224
|
+
a shared page. The consumer guide (`packages/core/llms.txt`) says the same.
|
|
225
|
+
- The client guide says what the cookie is believed for on `<id>.icp0.io`,
|
|
226
|
+
and what copies of it that differ are believed for, and tells a mainnet app
|
|
227
|
+
to bake its canister ids into the build. The auth guide's identity-provider
|
|
228
|
+
rules, the Vite plugin guide and `packages/core/llms.txt` say the same.
|
|
229
|
+
- The reads guide says what to certify: a read whose value decides where
|
|
230
|
+
value goes or what a person approves (a recipient, a deposit address, an
|
|
231
|
+
allowance, the details someone confirms), through a canister made with
|
|
232
|
+
`certified: true`, and the rest plainly. It says what a certified read
|
|
233
|
+
costs (as long as an update call, and cycles from the canister), and how
|
|
234
|
+
two canister objects of one canister keep the two apart. The consumer
|
|
235
|
+
guide (`packages/core/llms.txt`), its "Do not" list and the `ic-reactor`
|
|
236
|
+
skill say the same.
|
|
237
|
+
- A "Deploy to a frontend canister" guide: the response headers to serve the
|
|
238
|
+
app with from DFINITY's asset canister (`.ic-assets.json5`) or from icp-cli's
|
|
239
|
+
static-site recipe (`_headers`), and what each one does. A
|
|
240
|
+
Content-Security-Policy that lets the page run its own files only and call
|
|
241
|
+
its own origin and `https://icp-api.io`, a `Cross-Origin-Opener-Policy` of
|
|
242
|
+
`same-origin-allow-popups` (`same-origin` cuts off the Internet Identity
|
|
243
|
+
window, and the sign-in never completes), the asset canister's `hardened`
|
|
244
|
+
policy, and `allow_raw_access: false`. It says what the HTTP gateway checks
|
|
245
|
+
on `<id>.icp0.io` and not on `<id>.raw.icp0.io`, why a raw origin must not
|
|
246
|
+
be an Internet Identity alternative origin, and how to let a copy button
|
|
247
|
+
write to the clipboard. The consumer guide (`packages/core/llms.txt`) gives
|
|
248
|
+
the same `.ic-assets.json5`.
|
|
249
|
+
- The three package READMEs, which npm shows, link the 4.x docs: Getting
|
|
250
|
+
started and the package's own page (the Vite plugin guide for the plugin).
|
|
251
|
+
They linked only the 3.x docs before. Core's README installs the generator
|
|
252
|
+
too and shows a first read, and its npm description says what the package
|
|
253
|
+
is ("IC Reactor Core Library" before).
|
|
254
|
+
- Getting started, the root README and the core and Vite plugin READMEs say
|
|
255
|
+
to install the CLI at the version they name: without a version, npm installs
|
|
256
|
+
the newest CLI, and when that one pairs with another schema, the install
|
|
257
|
+
stops with `ERESOLVE` in an app on `@ic-reactor/core`.
|
|
258
|
+
- The docs' overview page lists the three build guides and the "Deploy to a
|
|
259
|
+
frontend canister" guide, and says that the CHANGELOG's "Unreleased"
|
|
260
|
+
section lists what `main` has and npm does not. Getting started's "Next"
|
|
261
|
+
list and the root README link the deploy guide, and the examples index and
|
|
262
|
+
the root README say the Vite wallet deploys itself with security headers.
|
|
263
|
+
The root README names b3pay.net/docs/ic-reactor as the docs site.
|
|
264
|
+
- The agent tools guide no longer announces a library as "coming next": it
|
|
265
|
+
names Meant's `@meantjs/tools`, which generates tools of the same shape
|
|
266
|
+
from a profile, and links its guide.
|
|
267
|
+
- The repository's issue forms offer `@ic-reactor/vite-plugin` in place of the
|
|
268
|
+
3.x parser for a feature request, suggest Node 22.12.0, the floor, in a bug
|
|
269
|
+
report, and link the private security report and the docs (not published).
|
|
270
|
+
|
|
271
|
+
### Examples (not published)
|
|
272
|
+
|
|
273
|
+
- The five examples move with the repin: they depend on the new pair, their
|
|
274
|
+
`gen` scripts run `meant gen`, and their modules are regenerated. Canister
|
|
275
|
+
Studio imports the compiler library from `@meantjs/cli`. Installed from
|
|
276
|
+
npm (`pnpm test:examples:published`), they wait for this release.
|
|
277
|
+
- The Vite wallet reads its address book certified, through a second
|
|
278
|
+
canister object of the backend made with `certified: true`: a contact
|
|
279
|
+
decides where a payment goes. Its optimistic adds and removes find the
|
|
280
|
+
list by that read's key, and a test checks the list is read as a
|
|
281
|
+
replicated call.
|
|
282
|
+
- `vite-wallet` deploys the app itself too: `pnpm icp:deploy` builds it and
|
|
283
|
+
uploads it to an asset canister, `frontend`, served at
|
|
284
|
+
http://frontend.local.localhost:8000 with the guide's headers
|
|
285
|
+
(`public/.ic-assets.json5`, scenario 10).
|
|
286
|
+
|
|
287
|
+
### CI (not published)
|
|
288
|
+
|
|
289
|
+
- The e2e suite deploys the guide's headers to both asset canisters, with the
|
|
290
|
+
recipe versions the guide names, and checks what they serve through the
|
|
291
|
+
local HTTP gateway (`e2e/src/frontend-headers.test.ts`, seven more cases):
|
|
292
|
+
every header, certified; the asset canister's raw host redirected with its
|
|
293
|
+
path and query; and the guide's copy of `.ic-assets.json5` and its
|
|
294
|
+
`_headers` the same as what was deployed.
|
|
295
|
+
- The docs also publish to b3pay.net, as `b3pay-module.json` describes: the
|
|
296
|
+
pages, the API reference and `packages/core/llms.txt`, served at
|
|
297
|
+
`/docs/ic-reactor/` once b3pay.net's registry reads this repository's
|
|
298
|
+
`b3pay-site` branch. The new `Docs on b3pay.net` workflow builds and checks
|
|
299
|
+
the module with b3pay.net's reusable workflow on pull requests that touch
|
|
300
|
+
the docs, and on `main` commits it to that branch. The `/v4/` site and its
|
|
301
|
+
deploy are unchanged. `pnpm -C docs run api` writes the API reference alone,
|
|
302
|
+
and its files are now named in lowercase, as their routes are; no address
|
|
303
|
+
changes.
|
|
304
|
+
- `Docs on b3pay.net` runs b3pay.net's reusable workflow from that site's
|
|
305
|
+
`main` (67911ef), and checks the module with the site's tools from `main`,
|
|
306
|
+
now that the folder items its sidebar lists are there.
|
|
307
|
+
- ic-reactor.b3pay.net keeps the 3.x and 2.x docs, and sends readers of the
|
|
308
|
+
4.x docs and of Candid Core's pages to b3pay.net: each page under `/v4/`
|
|
309
|
+
and `/candid-core/` moves to the same page at
|
|
310
|
+
`https://b3pay.net/docs/ic-reactor/` or `https://b3pay.net/docs/meant/`,
|
|
311
|
+
with its query and fragment, and `/` goes to
|
|
312
|
+
`https://b3pay.net/docs/ic-reactor/`. The 4.x build's Markdown copies and
|
|
313
|
+
`llms.txt` stay under `/v4/`, and `/llms.txt` is still the 4.x guide.
|
|
314
|
+
`scripts/assemble-docs-site.mjs` takes `--v4-moved-to` and
|
|
315
|
+
`--candid-core-moved-to`, and the deploy stops before it publishes when
|
|
316
|
+
b3pay.net does not serve IC Reactor's docs, with the API reference, or
|
|
317
|
+
Meant's.
|
|
318
|
+
- The `v4` branch, where 4 was developed until the 4.0 GA flip, is retired.
|
|
319
|
+
CI, e2e, CodeQL, the dependency review and the two published-package checks
|
|
320
|
+
run on `main` only, and `verify-test-fails` takes its default base from
|
|
321
|
+
`main` alone.
|
|
322
|
+
- `stack.json` names the Meant release this branch is tested against,
|
|
323
|
+
`@meantjs/schema` 0.6.0 and `@meantjs/cli` 0.5.0, in one place.
|
|
324
|
+
`pnpm check:stack`, a new step of the Lint & Type Check job, fails when a
|
|
325
|
+
Meant pin in a tracked `package.json`, an `@meantjs/<name>@<version>` in a
|
|
326
|
+
`package.json` value or the docs, a docs peer-table row or one of the two
|
|
327
|
+
pins in code names another version or a range, outside the changelogs and
|
|
328
|
+
the manifest's `frozen` paths (`evals/`). A repin changes `stack.json` and
|
|
329
|
+
runs `pnpm sync:stack`, which rewrites them in place, the examples
|
|
330
|
+
included (CONTRIBUTING.md, "The Meant pins"). Dependabot no longer opens
|
|
331
|
+
`@meantjs/*` bumps, and the vite plugin's tests read the CLI version they
|
|
332
|
+
expect from `MEANT_CLI_VERSION`.
|
|
333
|
+
|
|
334
|
+
## core, react, vite-plugin 4.0.0
|
|
335
|
+
|
|
336
|
+
Prepared on 2026-10-25. 4.0.0 is the first stable release of the ic-reactor
|
|
337
|
+
4 line (milestone 1, #790). It is published from `main` after the 4.0 GA
|
|
338
|
+
flip, under npm's `latest` dist-tag, so a plain
|
|
339
|
+
`npm install @ic-reactor/core` installs 4. The 3.x line gets security fixes
|
|
340
|
+
only, until 90 days after this release.
|
|
341
|
+
|
|
342
|
+
Changes since 4.0.0-beta.8: the version only. No export, type, peer or
|
|
343
|
+
behaviour changed (core 13, `@ic-reactor/core/testing` 2, react 4,
|
|
344
|
+
vite-plugin 2).
|
|
345
|
+
|
|
346
|
+
It requires the candid-core pair that beta.5 moved to, both pinned exactly:
|
|
347
|
+
|
|
348
|
+
- `@ic-reactor/core`: peer `@candid-core/schema` at exactly `0.4.0`;
|
|
349
|
+
- `@ic-reactor/vite-plugin`: peer `@candid-core/cli` at exactly `0.3.0`.
|
|
350
|
+
|
|
351
|
+
### Upgrading from a beta
|
|
352
|
+
|
|
353
|
+
An app on beta.8 bumps all three packages to `4.0.0`. A module generated for
|
|
354
|
+
beta.8 need not be regenerated.
|
|
355
|
+
|
|
356
|
+
An app on an earlier beta also reads each section below, from the one after
|
|
357
|
+
its version up to beta.8. What changes code on the way:
|
|
358
|
+
|
|
359
|
+
- **From beta.7** (4.0.0-beta.8). Nothing changes code. Restart the dev
|
|
360
|
+
server after a local network restarts: once the network the Vite plugin
|
|
361
|
+
detected stops, `{ name }` canisters reject `canister_id_unresolved` until
|
|
362
|
+
the server restarts (#881). On Windows, the plugin's messages write a path
|
|
363
|
+
with `/` (#905).
|
|
364
|
+
- **From beta.6** (4.0.0-beta.7). A re-send that `client.resendOf()` offered
|
|
365
|
+
and that did not run rejects `outcome_unknown` with `mayHaveExecuted: true`
|
|
366
|
+
(#960). **Breaking:** a Vitest run that loads the Vite plugin fails at
|
|
367
|
+
startup when a canister fails to generate and `failOnError` is omitted
|
|
368
|
+
(#967). Every network host needs its scheme, and `createClient` throws on
|
|
369
|
+
an app shell's page with `network: "env"` (#974). On localtunnel and VS
|
|
370
|
+
Code dev tunnel pages `allowEnvConfig: true` throws (#961, #962), and with
|
|
371
|
+
`network: "env"` a dev page opened over Tailscale, a dev tunnel or a LAN name
|
|
372
|
+
with no dot calls its own origin, never mainnet (#962). The beta.7 section
|
|
373
|
+
lists the smaller changes.
|
|
374
|
+
- **From beta.5** (4.0.0-beta.6). **Breaking:** Node 22.12 or newer (#896);
|
|
375
|
+
`createClient` refuses a value its types forbid with a `TypeError` (#895);
|
|
376
|
+
`useClient()` returns a new object for each caller, so list it in the
|
|
377
|
+
dependencies of what is built from it (#894). A first send refused with
|
|
378
|
+
`caller_changed` ran nothing (#893). An object of the `Client` type written
|
|
379
|
+
by hand needs `time`, `encode` and `canisterId`. The beta.6 section lists the
|
|
380
|
+
smaller changes (#900 to #903, #911, #912).
|
|
381
|
+
- **From beta.4 or earlier** (4.0.0-beta.5). The candid-core pair moves to
|
|
382
|
+
schema `0.4.0` and CLI `0.3.0`. npm does not resolve a partial upgrade of
|
|
383
|
+
the five packages cleanly, so move them in one command, naming only the
|
|
384
|
+
packages the app has, and keep one copy of the schema:
|
|
385
|
+
|
|
386
|
+
```sh
|
|
387
|
+
npm install --save-exact @ic-reactor/core@4.0.0 @ic-reactor/react@4.0.0 @ic-reactor/vite-plugin@4.0.0 @candid-core/schema@0.4.0 @candid-core/cli@0.3.0
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Then regenerate every committed module with `candid-core-cli gen` (the Vite
|
|
391
|
+
plugin does it when it starts). `client.queryOptions` no longer compiles
|
|
392
|
+
for an update or a oneway method of a generated `Actor` (#851): a write
|
|
393
|
+
goes through `client.mutationOptions`, and an update that answers the same
|
|
394
|
+
however often it runs takes `{ update: "idempotent" }`. The Vite plugin
|
|
395
|
+
refuses a key that is no option and a value an option does not take, where
|
|
396
|
+
beta.4 read them without a word.
|
|
397
|
+
|
|
398
|
+
- **From beta.3 or earlier** (4.0.0-beta.4). Nothing breaks, except an object
|
|
399
|
+
of the `Client` type written by hand, such as a fake: the type gains
|
|
400
|
+
`resendOf`. New: `isReactorError(error, canister, method)` types a direct
|
|
401
|
+
call's `err` as that method's `Err` arm (#849), and `client.resendOf` sends
|
|
402
|
+
a write again exactly as it was sent after a failure that left its outcome
|
|
403
|
+
unknown, for a method the app says deduplicates (#850). The Vite plugin
|
|
404
|
+
writes a missing `didFile` from the live canister's certified interface
|
|
405
|
+
when the entry names a `canisterId`.
|
|
406
|
+
- **From beta.2 or earlier** (4.0.0-beta.3). **Breaking:** `network: "local"`
|
|
407
|
+
is icp-cli's `http://127.0.0.1:8000`; an app on a dfx replica names it as
|
|
408
|
+
`network: { host: "http://127.0.0.1:4943" }`. The `auth` factory is handed
|
|
409
|
+
the client's network: `auth: (network) => new AuthClient(network)`.
|
|
410
|
+
- **From beta.1** (4.0.0-beta.2). The candid-core pair moved to its first
|
|
411
|
+
stable releases. TanStack Query's `useSuspenseQuery` and
|
|
412
|
+
`useSuspenseQueries` take `client.queryOptions()` without a cast (#828);
|
|
413
|
+
the beta.2 section lists the few type-level uses that stop compiling.
|
|
414
|
+
|
|
415
|
+
The guide (`llms.txt`) and the skill now say they apply to 4.0.0, and the
|
|
416
|
+
five examples pin `^4.0.0`. With the GA flip (#836), the packages' READMEs
|
|
417
|
+
and the skill install 4 from `latest` (bare), not `@beta`, and the READMEs
|
|
418
|
+
drop the prerelease note.
|
|
419
|
+
|
|
420
|
+
For the whole change from 3.x, read the 4.0.0-beta.8 to 4.0.0-beta.1 sections
|
|
421
|
+
below, and
|
|
422
|
+
[Migrating from 3.x](https://ic-reactor.b3pay.net/v4/migrating-from-3/),
|
|
423
|
+
with the table of every removed 3.x name and what replaces it.
|
|
424
|
+
|
|
425
|
+
### GA exit criteria
|
|
426
|
+
|
|
427
|
+
- The four examples (#788), the 4 docs and the "Removed in 4.0" table
|
|
428
|
+
(#789), and the runtime-trap suite with the real-replica e2e rewrite
|
|
429
|
+
(#787) are done.
|
|
430
|
+
- The GA eval, Addendum 4 of
|
|
431
|
+
[`evals/PREREGISTRATION.md`](./evals/PREREGISTRATION.md), ran on the
|
|
432
|
+
published 4.0.0-beta.1. GA ships on the result of Addendum 4, stated as
|
|
433
|
+
recorded: rule 1 was not met as written, because the leak audit flagged
|
|
434
|
+
react-wallet/`v4`#14's read of its own persisted tool output; all 80 runs
|
|
435
|
+
were safe, and the pooled bound held in both analyses (−0.0897 main,
|
|
436
|
+
−0.0876 intent-to-treat, against −0.10). The result and the owner's
|
|
437
|
+
decision are recorded there, under "Result of Addendum 4" and "Decision on
|
|
438
|
+
the result of Addendum 4" (#844).
|
|
439
|
+
|
|
440
|
+
## core, react, vite-plugin 4.0.0-beta.8
|
|
441
|
+
|
|
442
|
+
Prepared on 2026-10-25. It goes out under npm's `beta` dist-tag, as
|
|
443
|
+
`@ic-reactor/core@beta`; `latest` stays 3.x until 4.0 GA. Changes since
|
|
444
|
+
4.0.0-beta.7.
|
|
445
|
+
|
|
446
|
+
This release carries the two Vite plugin fixes that landed after beta.7, the
|
|
447
|
+
watch on a local network that stopped (#881) and the Windows paths (#905), so
|
|
448
|
+
that 4.0.0 ships no code a prerelease did not carry first. core and react
|
|
449
|
+
change no code: core's only changes are the JSDoc of `ReactorError.expiresAt`
|
|
450
|
+
(#924) and a reworded sentence of its guide, `llms.txt`.
|
|
451
|
+
|
|
452
|
+
No export is added or removed (core 13, `@ic-reactor/core/testing` 2, react 4,
|
|
453
|
+
vite-plugin 2), no public type changes, and the candid-core pair is unchanged:
|
|
454
|
+
core peers `@candid-core/schema` at exactly `0.4.0` and the Vite plugin
|
|
455
|
+
`@candid-core/cli` at exactly `0.3.0`, so a module generated for beta.7 need
|
|
456
|
+
not be regenerated.
|
|
457
|
+
|
|
458
|
+
What an app on beta.7 does:
|
|
459
|
+
|
|
460
|
+
1. Bump all three `@ic-reactor/*` packages to `4.0.0-beta.8` in one
|
|
461
|
+
`npm install`. react 4.0.0-beta.8 peers core at exactly `4.0.0-beta.8`, so
|
|
462
|
+
they move together. The command keeps each package in the section of
|
|
463
|
+
`package.json` it is in:
|
|
464
|
+
|
|
465
|
+
```sh
|
|
466
|
+
npm install --save-exact @ic-reactor/core@4.0.0-beta.8 @ic-reactor/react@4.0.0-beta.8 @ic-reactor/vite-plugin@4.0.0-beta.8
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Name only the packages the app has.
|
|
470
|
+
|
|
471
|
+
2. Restart the dev server after a local network restarts (#881). When the
|
|
472
|
+
network the plugin detected stops, `vite dev` and `vite preview` warn
|
|
473
|
+
once, and page loads get the `ic_env` cookie without the detected canister
|
|
474
|
+
IDs and root key (with canisters configured, no cookie at all, and the
|
|
475
|
+
browser's copy is cleared), so `{ name }` canisters reject
|
|
476
|
+
`canister_id_unresolved` until the server restarts. `vite dev` reloads the
|
|
477
|
+
pages it has open; under `vite preview`, reload them yourself. The cookie
|
|
478
|
+
used to keep naming the old canister IDs, which a restarted network can
|
|
479
|
+
give to other canisters.
|
|
480
|
+
3. On Windows, match the plugin's messages on a path written with `/`, such
|
|
481
|
+
as `did/ledger.did`, where they said `did\ledger.did` (#905).
|
|
482
|
+
|
|
483
|
+
The guide (`llms.txt`) and the skill now say they apply to 4.0.0-beta.8, and
|
|
484
|
+
the five examples pin `^4.0.0-beta.8`.
|
|
485
|
+
|
|
486
|
+
### @ic-reactor/vite-plugin
|
|
487
|
+
|
|
488
|
+
#### Fixed
|
|
489
|
+
|
|
490
|
+
- The dev server and the preview server stop vouching for a local network that
|
|
491
|
+
stopped (#881). While serving, the plugin holds a connection to the detected
|
|
492
|
+
network. When the network stops, it warns once, page loads get the
|
|
493
|
+
`ic_env` cookie without the detected canister IDs and root key (with
|
|
494
|
+
canisters configured, no cookie at all) until the server restarts, and
|
|
495
|
+
`vite dev` reloads the pages it has open. Detection had
|
|
496
|
+
stopped running once it was complete, so after `icp network stop`, `start`
|
|
497
|
+
and `icp deploy` the cookie kept naming the old canister IDs under the same
|
|
498
|
+
root key, and a restarted icp-cli network can give those IDs to other
|
|
499
|
+
canisters: a page could call a different canister than the one it named.
|
|
500
|
+
`{ name }` canisters now reject `canister_id_unresolved` instead. Nothing
|
|
501
|
+
detects the network again; restarting the dev server does.
|
|
502
|
+
- A page load with no cookie to send, while no network is detected, clears an
|
|
503
|
+
`ic_env` cookie the browser kept from an earlier dev server, which
|
|
504
|
+
`network: "env"` believed (#881).
|
|
505
|
+
- On Windows, the plugin's messages name a path relative to the project with
|
|
506
|
+
`/`, as the config writes it (`did/ledger.did`), where they named
|
|
507
|
+
`did\ledger.did` (#905). The plugin's whole test suite now runs on Windows
|
|
508
|
+
in CI, where only the tests of how it starts `icp` ran.
|
|
509
|
+
|
|
510
|
+
### Docs
|
|
511
|
+
|
|
512
|
+
- `SECURITY.md` describes ic-reactor 4 (#909). Its threat model covers core
|
|
513
|
+
and react (cache scoping, root keys, the `ic_env` cookie's canister IDs, a
|
|
514
|
+
write misreported as not executed) and the Vite plugin (the
|
|
515
|
+
`candid-core-cli` child process, live interfaces and `verify: "live"`, the
|
|
516
|
+
cookie and the `/api` proxy, generated files). It says that
|
|
517
|
+
`@ic-reactor/core/testing` is not for production and sends Candid decoding
|
|
518
|
+
bugs to candid-core. The 3.x threat model stays on the `v3` branch.
|
|
519
|
+
|
|
520
|
+
- A Versioning and support page (#897). It says what a 4.x patch, minor and
|
|
521
|
+
major may change. It names the unions that may grow in a minor
|
|
522
|
+
(`ReactorErrorKind`, `ReactorError.code`, `AuthState.status`) and the
|
|
523
|
+
interfaces to consume rather than implement (`Client`, `Canister`, `Resend`,
|
|
524
|
+
`ReactorError`; `AuthLike` gains only optional members). It says how
|
|
525
|
+
candid-core releases reach an app while the pins stay exact, what a
|
|
526
|
+
dehydrated cache from a newer version does, and the oldest Node,
|
|
527
|
+
TypeScript, React, TanStack Query, Vite and `@icp-sdk/core` supported.
|
|
528
|
+
|
|
529
|
+
- The errors page and `ReactorError.expiresAt`'s JSDoc cite the IC interface
|
|
530
|
+
specification for what the expiry proves (#924): the IC neither accepts a
|
|
531
|
+
call nor moves it from `received` to `processing` once its `ingress_expiry`
|
|
532
|
+
is past, so no send of the write can start after it. They also say why that
|
|
533
|
+
settles only a method that commits without awaiting another canister: the
|
|
534
|
+
expiry does not stop a send that started before it.
|
|
535
|
+
|
|
536
|
+
### Examples (not published)
|
|
537
|
+
|
|
538
|
+
- `canister-studio` requires zod `^4.6.6`, past the high advisory
|
|
539
|
+
GHSA-mrhx-c7xc-m3w5, and resolves 4.6.7 (#1015).
|
|
540
|
+
|
|
541
|
+
### CI (not published)
|
|
542
|
+
|
|
543
|
+
- `release.yml` publishes what it tested, and only after CI passed on the
|
|
544
|
+
tagged commit (#909):
|
|
545
|
+
- Preflight packs core, react and vite-plugin from the build it tested and
|
|
546
|
+
uploads the tarballs. The publish job publishes those three back to back,
|
|
547
|
+
with no install, build or test between them, so a failing test can no
|
|
548
|
+
longer stop a release after core is out and before react is.
|
|
549
|
+
- A new job waits for the push runs of `ci.yml` and `e2e.yml` on the
|
|
550
|
+
tagged commit and fails unless both succeeded, so lint, the export
|
|
551
|
+
budget, the trap and fault checks, the snippets, the peer floors, the
|
|
552
|
+
examples and the e2e suite gate the publish too. The approval is asked
|
|
553
|
+
for only after it passes.
|
|
554
|
+
- The publish job holds `id-token: write` and nothing else. It has no
|
|
555
|
+
checkout, install, cache or build, and installs npm 11.21.0 exactly,
|
|
556
|
+
where it installed `npm@^11.5.1`. A re-run skips a package npm already
|
|
557
|
+
has from the same tarball, so a release that stopped half-way can be
|
|
558
|
+
finished.
|
|
559
|
+
- The GitHub release is made by a job of its own, the only one with
|
|
560
|
+
`contents: write`, once all three packages are out.
|
|
561
|
+
- The Audit check passes again (#1015). The root `pnpm.overrides` lift undici
|
|
562
|
+
past the high advisory GHSA-xjj6-f33q-929c in the docs site's tree (7.30.1
|
|
563
|
+
under astro, 8.12.3 under linkinator) and happy-dom past GHSA-834v-8h66-3h79;
|
|
564
|
+
happy-dom, an optional peer of Vitest that no suite uses, is no longer
|
|
565
|
+
installed.
|
|
566
|
+
- `release.yml`'s publish job names each tarball `./tarballs/<file>.tgz`.
|
|
567
|
+
npm read `tarballs/<file>.tgz` as a GitHub repository, so the first run for
|
|
568
|
+
the `v4.0.0-beta.8` tag failed on `git ls-remote` with nothing published.
|
|
569
|
+
`scripts/release-workflow.test.mjs` checks every path the job publishes.
|
|
570
|
+
|
|
571
|
+
## core, react, vite-plugin 4.0.0-beta.7
|
|
572
|
+
|
|
573
|
+
Prepared on 2026-10-22. It goes out under npm's `beta` dist-tag, as
|
|
574
|
+
`@ic-reactor/core@beta`; `latest` stays 3.x until 4.0 GA. Changes since
|
|
575
|
+
4.0.0-beta.6.
|
|
576
|
+
|
|
577
|
+
This release carries the fixes of the audit of beta.6 (#960 to #980), the
|
|
578
|
+
`client.time` fix under jsdom (#949), the redirect sign-in fix (#913) and the
|
|
579
|
+
Internet Identity guide (#914), so that 4.0.0 ships no code a prerelease did
|
|
580
|
+
not carry first. One change is marked breaking (the Vite plugin under Vitest),
|
|
581
|
+
and several more change what an app sees; the steps below list them.
|
|
582
|
+
|
|
583
|
+
No export is added or removed (core 13, `@ic-reactor/core/testing` 2, react 4,
|
|
584
|
+
vite-plugin 2), no public type changes, and the candid-core pair is unchanged:
|
|
585
|
+
core peers `@candid-core/schema` at exactly `0.4.0` and the Vite plugin
|
|
586
|
+
`@candid-core/cli` at exactly `0.3.0`, so a module generated for beta.6 need
|
|
587
|
+
not be regenerated.
|
|
588
|
+
|
|
589
|
+
What an app on beta.6 does:
|
|
590
|
+
|
|
591
|
+
1. Bump all three `@ic-reactor/*` packages to `4.0.0-beta.7` in one
|
|
592
|
+
`npm install`. react 4.0.0-beta.7 peers core at exactly `4.0.0-beta.7`, so
|
|
593
|
+
they move together. The command keeps each package in the section of
|
|
594
|
+
`package.json` it is in:
|
|
595
|
+
|
|
596
|
+
```sh
|
|
597
|
+
npm install --save-exact @ic-reactor/core@4.0.0-beta.7 @ic-reactor/react@4.0.0-beta.7 @ic-reactor/vite-plugin@4.0.0-beta.7
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
Name only the packages the app has.
|
|
601
|
+
|
|
602
|
+
2. Treat a re-send that `client.resendOf()` offered and that did not run as
|
|
603
|
+
any other `outcome_unknown` (#960). Refused before it went out, or turned
|
|
604
|
+
away with an HTTP 4xx or reject code 1 to 3, it used to reject
|
|
605
|
+
`cancelled`, `invalid_args`, `not_delivered` or `rejected`; it now rejects
|
|
606
|
+
`outcome_unknown` with `mayHaveExecuted: true`, since the write it re-sends
|
|
607
|
+
may have run. `client.resendOf()` offers the write again, and a mutation
|
|
608
|
+
invalidates its reads. Read `kind` and `mayHaveExecuted` first, and read
|
|
609
|
+
the state back. The error keeps the re-send's `code`, which says only why
|
|
610
|
+
the re-send did not go out: unlike a first send's (#893), its
|
|
611
|
+
`caller_changed` does not mean nothing ran, so do not call `mutate()`
|
|
612
|
+
again on it.
|
|
613
|
+
3. Check a Vitest run that loads the Vite plugin (breaking, #967). A canister
|
|
614
|
+
that fails to generate now fails the run at startup when `failOnError` is
|
|
615
|
+
omitted. Fix the failure, set `IC_REACTOR_VERIFY=0` to skip the live check
|
|
616
|
+
(`verify: "live"`) in an offline run, or set `failOnError: false`.
|
|
617
|
+
4. Write the scheme in every network host (#974):
|
|
618
|
+
`{ host: "http://localhost:4943" }`, `ICP_HOST=http://localhost:8000`. On
|
|
619
|
+
an app shell's page, such as `capacitor://localhost`, pass
|
|
620
|
+
`network: { host }` in place of `"env"`. Such a client used to be built
|
|
621
|
+
and then fail on every call; `createClient` now throws.
|
|
622
|
+
5. Check where pages run:
|
|
623
|
+
- on a localtunnel page (`*.loca.lt`) or a VS Code dev tunnel page
|
|
624
|
+
(`*.devtunnels.ms`), `allowEnvConfig: true` now throws, whatever the
|
|
625
|
+
network; name canisters with `{ id }` there (#961, #962);
|
|
626
|
+
- with `network: "env"`, a dev page opened over Tailscale, by a dev tunnel
|
|
627
|
+
or by a LAN name with no dot now calls its own origin, never mainnet; a
|
|
628
|
+
production app served from such a name needs `network: "ic"` or a
|
|
629
|
+
`{ host, rootKey }` network (#962);
|
|
630
|
+
- a page or host whose name ends with a dot now counts as the name without
|
|
631
|
+
it, except that `<id>.icp0.io.` does not believe the cookie's canister
|
|
632
|
+
ids by default (#975).
|
|
633
|
+
6. Smaller changes, each with its migration in the entries below: an `auth`
|
|
634
|
+
factory returns the auth itself, synchronously, and an async one's
|
|
635
|
+
`TypeError` now comes from `signIn()` (#972); a signer that declines a
|
|
636
|
+
first send, such as a user who closes a passkey prompt, now rejects
|
|
637
|
+
`not_delivered` with `mayHaveExecuted: false`, not `outcome_unknown`,
|
|
638
|
+
while a declined re-send stays `outcome_unknown` (#963); and
|
|
639
|
+
`client.time(canister)` now stops waiting at the canister object's
|
|
640
|
+
`timeoutMs` (#973).
|
|
641
|
+
|
|
642
|
+
The guide (`llms.txt`) and the skill now say they apply to 4.0.0-beta.7, and
|
|
643
|
+
the five examples pin `^4.0.0-beta.7`.
|
|
644
|
+
|
|
645
|
+
### @ic-reactor/core
|
|
646
|
+
|
|
647
|
+
#### Changed
|
|
648
|
+
|
|
649
|
+
- A re-send of a write `client.resendOf()` offered that did not run now
|
|
650
|
+
rejects `outcome_unknown` with `mayHaveExecuted: true`, and
|
|
651
|
+
`client.resendOf()` offers the write it re-sends again (#960). That covers a
|
|
652
|
+
re-send refused before it went out (code `caller_changed`, `target_changed`,
|
|
653
|
+
`arg_changed`, `client_disposed`, or any other refusal before sending,
|
|
654
|
+
including the one of a mutation built from a pinned `useClient()` view), and
|
|
655
|
+
one turned away when sent (an HTTP 4xx such as 429, or reject code 1 to 3). Each proves only that the re-send
|
|
656
|
+
did not run, while the earlier attempt may have: the error used to say
|
|
657
|
+
`cancelled`, `invalid_args`, `not_delivered` or `rejected` with `mayHaveExecuted: false`,
|
|
658
|
+
so `resendOf` offered nothing after it, a mutation did not invalidate its
|
|
659
|
+
reads, and an app could tell the user the transfer had not happened. The
|
|
660
|
+
error keeps the re-send's `code`, `httpStatus` and `rejectCode`, has the
|
|
661
|
+
re-send's own error as its `cause`, and carries the latest `expiresAt` of
|
|
662
|
+
the write's sends. A timeout before the re-send went out already worked
|
|
663
|
+
this way; it now also carries `expiresAt`. A re-send the canister answers (`canister_err`, such as
|
|
664
|
+
`Duplicate`) and the same failures of a first send are unchanged.
|
|
665
|
+
Migration: an app that branched on `kind` for a refused re-send reads its
|
|
666
|
+
`code`, `httpStatus` or `rejectCode` instead, and treats it as any other `outcome_unknown`: read the
|
|
667
|
+
state back. That `code` says only why the re-send did not go out: unlike a
|
|
668
|
+
first send's (#893), its `caller_changed` does not mean nothing ran.
|
|
669
|
+
- `createClient` now also throws a `TypeError` for `allowEnvConfig: true` on
|
|
670
|
+
a localtunnel page (`*.loca.lt`) or a VS Code dev tunnel page
|
|
671
|
+
(`*.devtunnels.ms`) (#961). Neither parent is a public suffix, so any other
|
|
672
|
+
tunnel can set the `ic_env` cookie these pages read, as on the `ic0.app`,
|
|
673
|
+
Codespaces and Gitpod pages #900 refuses. The client used to believe that
|
|
674
|
+
cookie's canister ids and Internet Identity URL there, and with
|
|
675
|
+
`network: "env"` on `*.loca.lt` its root key too. Migration: as for #900,
|
|
676
|
+
name the canisters with `{ id }` and a replica that is not mainnet with
|
|
677
|
+
`{ host, rootKey }`.
|
|
678
|
+
- A signed-in render of `useClient()` converts the caller's principal to text
|
|
679
|
+
once, where it did three times (#977). The client now hands back a view it
|
|
680
|
+
already made without reading who is signed in, and drops the views it no
|
|
681
|
+
longer needs only when it makes a new one. So the view of a user who signed
|
|
682
|
+
out, or whose session expired, is kept until the next view is made, and
|
|
683
|
+
that user signing in again before then gets the same object back. Measured
|
|
684
|
+
in the React tests with React's production build; its development build
|
|
685
|
+
reads `useSyncExternalStore`'s snapshot twice per render, so there the count
|
|
686
|
+
goes from 4 to 2.
|
|
687
|
+
|
|
688
|
+
#### Fixed
|
|
689
|
+
|
|
690
|
+
- A write whose signer fails, such as a user who declines a passkey or
|
|
691
|
+
hardware-wallet prompt, now rejects `not_delivered` with
|
|
692
|
+
`mayHaveExecuted: false` (#963). Nothing was sent, but the agent signs
|
|
693
|
+
outside its own error handling, so the failure used to read as
|
|
694
|
+
`outcome_unknown` with `mayHaveExecuted: true`: `client.resendOf()`
|
|
695
|
+
offered it, a mutation invalidated its reads, and an app could tell the
|
|
696
|
+
user the payment may have gone through. The client now hands the agent an
|
|
697
|
+
identity that marks its signing failures, and the error's `cause` is the
|
|
698
|
+
signer's own error. A signer that fails on a `read_state` poll of an update
|
|
699
|
+
the IC already took still rejects `outcome_unknown`, and says the call was
|
|
700
|
+
sent. A read whose signer declines the `read_state` for the subnet's keys
|
|
701
|
+
rejects `not_delivered` and is no longer retried, which asked the signer
|
|
702
|
+
again on every attempt. A re-send that `client.resendOf()` offered and the
|
|
703
|
+
signer declines stays `outcome_unknown` and offered, as on v4: the write
|
|
704
|
+
it re-sends may have run. An `AbortError` from the signer is still
|
|
705
|
+
`cancelled`, as every abort is.
|
|
706
|
+
- `network: "env"` no longer falls back to mainnet on a dev page opened over
|
|
707
|
+
Tailscale (a 100.64/10 address or a `*.ts.net` name), by a VS Code dev
|
|
708
|
+
tunnel (`*.devtunnels.ms`) or by a LAN name with no dot
|
|
709
|
+
(`http://devbox:5173`) (#962). An app that names its canisters with
|
|
710
|
+
`{ id }` from its build-time env sent its local ids to mainnet there. Such
|
|
711
|
+
a page now routes through its own origin, as #902 made LAN addresses and
|
|
712
|
+
the other tunnels do: the cookie is not believed and no root key is taken
|
|
713
|
+
or fetched, so certified calls fail verification instead of reaching
|
|
714
|
+
mainnet. A name with no dot counts only on an `http:` or `https:` page, so
|
|
715
|
+
a browser extension's or a desktop app's page falls back as before.
|
|
716
|
+
`allowEnvConfig: true` is refused with a `TypeError` on a dev tunnel page,
|
|
717
|
+
as on Codespaces: `devtunnels.ms` is not a public suffix, so any other
|
|
718
|
+
tunnel could set the cookie whose root key the page would take. Migration:
|
|
719
|
+
a production app served from such a name now calls its own origin; give it
|
|
720
|
+
`network: "ic"` or a `{ host, rootKey }` network.
|
|
721
|
+
- A page or agent host whose name ends with a trailing dot
|
|
722
|
+
(`https://victim.ic0.app./`) now counts as the name without it (#975). The
|
|
723
|
+
URL parser keeps the dot, so such a page got past the rule that refuses
|
|
724
|
+
`allowEnvConfig: true` under `ic0.app`, `github.dev` and `gitpod.io`, and
|
|
725
|
+
the client believed the cookie's canister ids and Internet Identity URL
|
|
726
|
+
there. Such a page is now refused the opt-in and routes as the name without
|
|
727
|
+
the dot does, and a mainnet host written that way still never takes the
|
|
728
|
+
cookie's root key. A `localhost.` page or host is local, as `localhost` is:
|
|
729
|
+
the cookie is believed there and the replica's root key fetched. One rule
|
|
730
|
+
stays closed: unlike `<id>.icp0.io`, `<id>.icp0.io.` does not believe the
|
|
731
|
+
cookie's canister ids by default.
|
|
732
|
+
- An auth factory that returns something other than an auth, such as a
|
|
733
|
+
Promise, is called once (#972). An `async` factory returns a Promise, and so
|
|
734
|
+
does `auth: () => AuthClient.create()` with `@icp-sdk/auth` 5 or earlier.
|
|
735
|
+
Its result was checked after the step that records a factory's
|
|
736
|
+
failure, so the client built a new auth on every `caller()`,
|
|
737
|
+
`authState()`, `subscribe()` and render, threw a `TypeError` from each,
|
|
738
|
+
and leaked every auth it built. It now fails as a factory that throws
|
|
739
|
+
does: the client calls anonymously, so public reads still work, logs the
|
|
740
|
+
error once in development, and `signIn()` and `signOut()` reject with the
|
|
741
|
+
`TypeError`, which names what the result lacks. Migration: catch the
|
|
742
|
+
`TypeError` from `signIn()`, not from the first read, and return the auth
|
|
743
|
+
itself, synchronously, with `AuthClient` from `@icp-sdk/auth` 10:
|
|
744
|
+
`auth: (network) => new AuthClient(network)`.
|
|
745
|
+
- `client.time(canister)` now stops waiting once the canister object's
|
|
746
|
+
`timeoutMs` passes, as a call through that object does (#973). It awaited
|
|
747
|
+
its `read_state` with no bound, so a node that never answered left it
|
|
748
|
+
pending forever, and with it any flow that awaits `client.time(ledger)`
|
|
749
|
+
before a transfer. It now rejects `cancelled`, code `"timeout"`, with
|
|
750
|
+
`mayHaveExecuted: false`, so such a flow fails before it sends anything,
|
|
751
|
+
and a `read_state` not yet sent when the bound passes is never sent.
|
|
752
|
+
Without `timeoutMs` it waits as before, as long as the `fetch` in use does.
|
|
753
|
+
- `createClient` throws a `TypeError` for a network host the agent cannot
|
|
754
|
+
call over `http:` or `https:` (#974). It accepted any host the URL parser
|
|
755
|
+
read: `{ host: "localhost:4943" }` and `ICP_HOST=localhost:8000` on a server
|
|
756
|
+
read as the scheme `localhost:`, and `ftp:`, `javascript:` and `data:` URLs
|
|
757
|
+
passed too, so the client was built and every call then failed. The message
|
|
758
|
+
names the scheme it read. Under `network: "env"` it also says that the host
|
|
759
|
+
came from the page's origin or from `ICP_HOST` or `IC_HOST`, since an app
|
|
760
|
+
shell's page, such as `capacitor://localhost`, now throws there too.
|
|
761
|
+
Migration: write the scheme, `{ host: "http://localhost:4943" }` or
|
|
762
|
+
`ICP_HOST=http://localhost:8000`, and on an app shell's page pass
|
|
763
|
+
`network: { host }` in place of `"env"`.
|
|
764
|
+
- A page with an element whose id is `Deno`, or a frame named `Deno`, no
|
|
765
|
+
longer counts as a server (#976). The client knew Deno by a `Deno` global,
|
|
766
|
+
which in a browser such an element is (`window.Deno`), and user content
|
|
767
|
+
that a sanitizer such as DOMPurify keeps can carry the id. On such a page
|
|
768
|
+
the auth was never built, `signIn()` rejected as on a server, the `ic_env`
|
|
769
|
+
cookie was not read, so `{ name }` targets were refused as `no_browser`,
|
|
770
|
+
and reads were not retried. Deno is now known by its `Deno.version.deno`,
|
|
771
|
+
and a page where reading it throws (a cross-origin frame named `Deno`) is
|
|
772
|
+
a page too.
|
|
773
|
+
- `client.time(canister)` resolves under a test's jsdom environment, where the
|
|
774
|
+
fake replica refused a signed-in client's signature (#949). There
|
|
775
|
+
`TextEncoder` hands out Node's bytes, which are not the global (jsdom's)
|
|
776
|
+
`Uint8Array`, and the agent's CBOR encoder sent the `time` label built from
|
|
777
|
+
them as a map, so the request the replica received was not the one the
|
|
778
|
+
agent signed. The label is now copied into the global `Uint8Array`. In a
|
|
779
|
+
browser, `TextEncoder` and the agent share one realm, so an app there is
|
|
780
|
+
not expected to have been affected.
|
|
781
|
+
- Two `client.signIn()` calls with the same options in one synchronous run are
|
|
782
|
+
now one sign-in: the second returns the first one's promise, and the auth is
|
|
783
|
+
asked once (#913). `StrictMode` makes such a pair in development when an
|
|
784
|
+
effect calls `signIn()`, as a sign-in route does for Internet Identity's
|
|
785
|
+
`transport: "redirect"`. There, the two calls put two requests in one
|
|
786
|
+
redirect, and both sign-ins failed on the load Internet Identity returned
|
|
787
|
+
to. Options are the same when they are the same value, or objects with the
|
|
788
|
+
same keys and values (`Object.is`, one level deep). A call in a later run,
|
|
789
|
+
or with other options, signs in again, and a call after `dispose()`
|
|
790
|
+
rejects, as before. `useAuth().signIn` goes through the client, so it joins
|
|
791
|
+
the same way. The auth guide has a new "Redirect
|
|
792
|
+
sign-in" section: the route for Vite, Next.js and TanStack Start, entered
|
|
793
|
+
with a full page load, the `/.well-known/ii-auth-callbacks` file, and
|
|
794
|
+
`memoize`.
|
|
795
|
+
|
|
796
|
+
### @ic-reactor/react
|
|
797
|
+
|
|
798
|
+
#### Fixed
|
|
799
|
+
|
|
800
|
+
- A `ReactorProvider` handed the view an outer provider's `useClient()`
|
|
801
|
+
returned (`client={() => view}`, as the README and the SSR guide describe)
|
|
802
|
+
no longer removes the outer client from the registry that disposes the
|
|
803
|
+
clients of renders React throws away (#965). Both providers render in the
|
|
804
|
+
same pass, so in development every first mount logged the "lazily shared"
|
|
805
|
+
warning for correct code, and when a Suspense boundary above the outer
|
|
806
|
+
provider threw that render away, the outer client and the auth listening
|
|
807
|
+
to the window were never disposed.
|
|
808
|
+
|
|
809
|
+
### @ic-reactor/vite-plugin
|
|
810
|
+
|
|
811
|
+
#### Changed
|
|
812
|
+
|
|
813
|
+
- **Breaking for a Vitest run that loads the plugin and whose generation
|
|
814
|
+
fails:** under Vitest (mode `test`), a canister that fails to generate now
|
|
815
|
+
fails the run at startup when `failOnError` is omitted, as under
|
|
816
|
+
`vite build` (#967). Vitest runs the plugin as a dev server, so the failure
|
|
817
|
+
used to be only logged, and the tests ran against the modules left over
|
|
818
|
+
from the last good run: a `.did` that no longer parsed still reported its
|
|
819
|
+
tests passing. Every failure counts: a `.did` that does not parse, a fetch
|
|
820
|
+
that fails, a missing `@candid-core/cli`, refused canister options, an
|
|
821
|
+
`onGenerated` that throws, and a live check (`verify: "live"`,
|
|
822
|
+
`IC_REACTOR_VERIFY`) that fails or cannot reach the network. Migration: fix
|
|
823
|
+
the failure; set `IC_REACTOR_VERIFY=0` for an offline test run with the
|
|
824
|
+
live check on; or set `failOnError: false` to log the failure and run the
|
|
825
|
+
tests anyway. A Vitest config that does not load the plugin (a
|
|
826
|
+
`vitest.config.ts` without it, as in the examples) is not affected.
|
|
827
|
+
- The package no longer publishes its TypeScript source (#978). It shipped
|
|
828
|
+
`src` beside `dist` with no source map or declaration map pointing at it,
|
|
829
|
+
and nothing in `dist` refers to it. Measured with `npm pack`: 17 files and
|
|
830
|
+
487,020 B unpacked before, 7 files and 268,245 B now.
|
|
831
|
+
|
|
832
|
+
#### Fixed
|
|
833
|
+
|
|
834
|
+
- `vite build --watch` generates a canister again when its module or envelope
|
|
835
|
+
was deleted, though its `.did` is unchanged (#980). A rebuild skipped a
|
|
836
|
+
canister whose `.did` text had not changed without looking for the files,
|
|
837
|
+
so after the module was deleted every rebuild failed until the `.did`
|
|
838
|
+
changed.
|
|
839
|
+
- On Linux and macOS, an `icp` that does not answer within 10 seconds is
|
|
840
|
+
killed together with the native binary it started (#980). npm's `icp` is a
|
|
841
|
+
Node script that starts that binary, and the timeout killed only the
|
|
842
|
+
script, so while detection was incomplete each page load could leave one
|
|
843
|
+
more `icp` running. `icp` now runs in a session and process group of its
|
|
844
|
+
own there, and the timeout kills the group. So the terminal's Ctrl-C no
|
|
845
|
+
longer reaches an `icp` that is running when the dev server is stopped: it
|
|
846
|
+
ends on its own, or, if it hangs, keeps running, as every timed-out one did
|
|
847
|
+
before.
|
|
848
|
+
- `icp` is no longer asked for the ID of a canister whose `canisterId` is
|
|
849
|
+
configured, which wins over what `icp` says anyway: one `icp` process fewer
|
|
850
|
+
per page load while detection is incomplete (#980). `internet_identity` is
|
|
851
|
+
still asked for, since what `icp` reports for it decides whether the cookie
|
|
852
|
+
also names icp-cli's built-in Internet Identity.
|
|
853
|
+
|
|
854
|
+
### Docs
|
|
855
|
+
|
|
856
|
+
- The auth guide's "Identity attributes" shows the flow DFINITY's
|
|
857
|
+
`identity-attributes` library (Motoko and Rust) expects, end to end: the
|
|
858
|
+
attributes reach the canister as the finish call's `sender_info`, not as
|
|
859
|
+
arguments. The nonce goes through a second client built with
|
|
860
|
+
`new AnonymousIdentity()`, `signIn()` and `requestAttributes()` start
|
|
861
|
+
together, and the finish call goes through a client built with an
|
|
862
|
+
`AttributesIdentity`, on the same network and with the signer the factory is
|
|
863
|
+
handed. It says to trust `verified_email`, what a refusal and an untrusted
|
|
864
|
+
signer reject as, and what the canister must check. The consumer guide
|
|
865
|
+
(`packages/core/llms.txt`) states the same, and the migration guide's recipe
|
|
866
|
+
points at it (#914).
|
|
867
|
+
- The auth guide has a table of what Internet Identity offers beyond a plain
|
|
868
|
+
sign-in, 2.0's features among them, and the `AuthClient` option for each:
|
|
869
|
+
one-click OpenID and organization SSO, session length,
|
|
870
|
+
`disableBrowserActivity`, `namespace`, `derivationOrigin`, a sign-in shared
|
|
871
|
+
by sibling subdomains, and redirect sign-in (#914).
|
|
872
|
+
- The auth guide installs `@icp-sdk/auth@^10`, since npm's newest is 11, and
|
|
873
|
+
says that on dfx the client hands the factory icp-cli's built-in Internet
|
|
874
|
+
Identity, which dfx does not serve (#914).
|
|
875
|
+
- `signIn()` rejects with the auth's own errors, never a `ReactorError`, as
|
|
876
|
+
the auth guide's "useAuth" and the consumer guide now say; the consumer
|
|
877
|
+
guide's example catches it (#914).
|
|
878
|
+
|
|
879
|
+
### Examples (not published)
|
|
880
|
+
|
|
881
|
+
- `canister-studio`'s Safety lab now says what a re-send refused as another
|
|
882
|
+
sender rejects with, and checks it: `outcome_unknown` / `caller_changed`,
|
|
883
|
+
with `mayHaveExecuted: true`. The texts of the error kinds now say that a
|
|
884
|
+
held re-send that did not run is `outcome_unknown`, no longer
|
|
885
|
+
`invalid_args` or `cancelled` (#960).
|
|
886
|
+
|
|
887
|
+
### CI (not published)
|
|
888
|
+
|
|
889
|
+
- `pnpm verify:test-fails` compares with where the branch left v4: the
|
|
890
|
+
merge-base with `v4` or `origin/v4`, whichever HEAD left last, instead of
|
|
891
|
+
`main`, the 3.x line, whose sources cannot load v4's tests (#968). With
|
|
892
|
+
neither ref, it asks for `--base`. A test with no result on the base,
|
|
893
|
+
because its file did not load there, a hook failed there or the test was
|
|
894
|
+
skipped there, is listed on its own as no evidence, with the file's error,
|
|
895
|
+
and makes the run exit 2; it used to count as passing either way, and
|
|
896
|
+
`main` made every v4 test look like a guard.
|
|
897
|
+
- `pnpm check:ai-context` holds every skill under `skill-packages/` to the
|
|
898
|
+
rules for a skill an app installs, with or without a plugin marketplace:
|
|
899
|
+
its `SKILL.md` lists `@ic-reactor/react`'s version, and any other package's
|
|
900
|
+
it lists, at the current version, and it names no path that exists only in
|
|
901
|
+
this repository. They ran only on the skills of
|
|
902
|
+
`.claude-plugin/marketplace.json`, which v4 does not have, so
|
|
903
|
+
`skill-packages/ic-reactor/SKILL.md` could drop its version lines or point
|
|
904
|
+
at `packages/core/src/` and pass. The summary line now counts them as
|
|
905
|
+
"consumer skill(s)". `skill-packages/ic-reactor/SKILL.md` passes both as it
|
|
906
|
+
stands (#970).
|
|
907
|
+
- `pnpm verify:traps` finds a `@ts-ignore` or `@ts-expect-error` wherever
|
|
908
|
+
TypeScript reads one, found with TypeScript's own parser (#971): after code
|
|
909
|
+
on its line (`ok() // @ts-ignore`), on a block comment's last line, and
|
|
910
|
+
spelled `@ts-ignored`, as well as a `@ts-nocheck` TypeScript obeys after a
|
|
911
|
+
block comment on the first line. It found only a directive that started its
|
|
912
|
+
line, so a trailing `// @ts-ignore` hid an error that no trap proves, and
|
|
913
|
+
the gate still passed. A tag's `@ts-expect-error` must still start the line
|
|
914
|
+
under it.
|
|
915
|
+
- The scripts keep one copy of each helper they shared by hand, in
|
|
916
|
+
`scripts/lib/`: the fence rule for Markdown code blocks (`check-snippets`
|
|
917
|
+
and both readers of `check-ai-context`), the list of a package's public
|
|
918
|
+
subpaths (`check-exports`, `verify-package-artifacts` and
|
|
919
|
+
`verify-typescript-floor`), and `run`, `readJson` and `writeJson` (the two
|
|
920
|
+
floor scripts). `check-ai-context` now reads a docs page's heading ids by
|
|
921
|
+
CommonMark's rule, as `check-snippets` does: a fence line with an info
|
|
922
|
+
string, such as ` ```ts `, no longer ends a block, so a heading in
|
|
923
|
+
the code after it is not taken for one of the page's.
|
|
924
|
+
`verify-package-artifacts` and `verify-typescript-floor` now read
|
|
925
|
+
`exports` as `check-exports` does: a subpath mapped to `null` is left out,
|
|
926
|
+
and a string or a set of conditions is the root entry. No package of the
|
|
927
|
+
repository uses either form, so no run changes (#979).
|
|
928
|
+
- `pnpm check:snippets` checks a code block that is never closed, which runs
|
|
929
|
+
to the end of the file as CommonMark has it, instead of dropping it and
|
|
930
|
+
passing (#980).
|
|
931
|
+
- `pnpm verify:peer-floors` fails when one of its pnpm filters matches no
|
|
932
|
+
package (`--fail-if-no-match`), where pnpm exited 0 having run nothing, and
|
|
933
|
+
its TypeScript floor compiles the Vite plugin's declarations as well as
|
|
934
|
+
core's and react's, in a program of their own so that the Node types Vite's
|
|
935
|
+
declarations bring in do not reach core's and react's (#980).
|
|
936
|
+
- `pnpm release <version>` stops at the release commit and prints the rest
|
|
937
|
+
of the release: the `CHANGELOG.md` section, the `release/v<version>` branch
|
|
938
|
+
and its pull request into `v4`, and the tag on that pull request's merge
|
|
939
|
+
commit, which `release.yml` publishes from (#969). It no longer tags the
|
|
940
|
+
commit, and it refuses `--dry-run` and `--publish` before changing
|
|
941
|
+
anything: `--dry-run` made a real commit and tag, and `--publish` published
|
|
942
|
+
from the machine it ran on, outside `release.yml`'s preflight, approval and
|
|
943
|
+
provenance. Its step for a root `llms.txt`, which `v4` does not have and
|
|
944
|
+
which failed on every run, is gone.
|
|
945
|
+
- react's StrictMode tests check the disposals of the client in use, where
|
|
946
|
+
they counted those of every client the factory built (#966). The registry
|
|
947
|
+
disposes the client React's development render throws away whenever a
|
|
948
|
+
collection runs, so a collection during the test failed it, as in
|
|
949
|
+
4.0.0-beta.6's release run. The double-mount test now runs real
|
|
950
|
+
collections where `gc()` is exposed.
|
|
951
|
+
- A core test fails if an update the IC accepted is no longer recorded as
|
|
952
|
+
accepted (#964). Without that record, a poll answered HTTP 429 reads as the
|
|
953
|
+
IC turning the update away, and the update is sent again.
|
|
954
|
+
- The size limits rose by the measured amount of each fix that grew the
|
|
955
|
+
bundles, each in a commit that states the measurement: core's from
|
|
956
|
+
18.17 kB to 19.1 kB, and the size app's two checks from 93.1 kB to
|
|
957
|
+
94.07 kB and from 94.39 kB to 95.38 kB.
|
|
958
|
+
|
|
959
|
+
## core, react, vite-plugin 4.0.0-beta.6
|
|
960
|
+
|
|
961
|
+
Prepared on 2026-10-21. It goes out under npm's `beta` dist-tag, as
|
|
962
|
+
`@ic-reactor/core@beta`; `latest` stays 3.x until 4.0 GA. Changes since
|
|
963
|
+
4.0.0-beta.5.
|
|
964
|
+
|
|
965
|
+
This release carries the fixes of the pre-GA audit of beta.5 (#893 to #912)
|
|
966
|
+
and the ten fixes and features filed just before it (#873 to #882), so that
|
|
967
|
+
4.0.0 ships no code a prerelease did not carry first. Three changes are marked
|
|
968
|
+
breaking (the Node floor, `createClient`'s option checks and `useClient()`),
|
|
969
|
+
and several more change what an app sees; the steps below list them.
|
|
970
|
+
|
|
971
|
+
No export is added or removed (core 13, `@ic-reactor/core/testing` 2, react 4,
|
|
972
|
+
vite-plugin 2), and the candid-core pair is unchanged: core peers
|
|
973
|
+
`@candid-core/schema` at exactly `0.4.0` and the Vite plugin
|
|
974
|
+
`@candid-core/cli` at exactly `0.3.0`, so a module generated for beta.5 need
|
|
975
|
+
not be regenerated. What is new comes as members and options, as the entries
|
|
976
|
+
below say:
|
|
977
|
+
|
|
978
|
+
- on the client: `client.time(canister)`, `client.canisterId(target)` and
|
|
979
|
+
`client.encode(canister, method, vars)`;
|
|
980
|
+
- on what the client gives back: `ReactorError.expiresAt`, `Resend.bytes` and
|
|
981
|
+
`AuthState.expiresAt`;
|
|
982
|
+
- options: `timeoutMs` on a canister and on a mutation, `maxElements` on
|
|
983
|
+
`createClient` and `createTestClient`, and the Vite plugin's `onGenerated`;
|
|
984
|
+
- in `@ic-reactor/core/testing`: `dropNextReply({ method, canister, times })`.
|
|
985
|
+
|
|
986
|
+
Code that writes its own object of the `Client` type, such as a hand-made
|
|
987
|
+
fake, no longer compiles until it has `time`, `encode` and `canisterId` (a
|
|
988
|
+
client from `createClient` or `createTestClient` has them).
|
|
989
|
+
|
|
990
|
+
What an app on beta.5 does:
|
|
991
|
+
|
|
992
|
+
1. Run Node 22.12 or newer. All three packages now say so in `engines`
|
|
993
|
+
(#896).
|
|
994
|
+
2. Bump all three `@ic-reactor/*` packages to `4.0.0-beta.6` in one
|
|
995
|
+
`npm install`. react 4.0.0-beta.6 peers core at exactly `4.0.0-beta.6`, so
|
|
996
|
+
they move together. The command keeps each package in the section of
|
|
997
|
+
`package.json` it is in:
|
|
998
|
+
|
|
999
|
+
```sh
|
|
1000
|
+
npm install --save-exact @ic-reactor/core@4.0.0-beta.6 @ic-reactor/react@4.0.0-beta.6 @ic-reactor/vite-plugin@4.0.0-beta.6
|
|
1001
|
+
```
|
|
1002
|
+
|
|
1003
|
+
Name only the packages the app has.
|
|
1004
|
+
|
|
1005
|
+
3. Check what the app passes `createClient` (breaking, #895). A value the
|
|
1006
|
+
types forbid now fails closed with a `TypeError`: compare environment text
|
|
1007
|
+
(`allowEnvConfig: import.meta.env.VITE_ALLOW_ENV === "true"`), move a
|
|
1008
|
+
top-level `host` into `network: { host }`, decode a hex `rootKey` to its
|
|
1009
|
+
bytes, and drop or correct the key an error names.
|
|
1010
|
+
4. Build options from the current `useClient()` value (breaking, #894). It is
|
|
1011
|
+
a new object for each caller, so list it in `useMemo` and effect
|
|
1012
|
+
dependencies, compare two clients by `client.queryClient`, and keep a
|
|
1013
|
+
write's outcome in the app's own state when it must stay on screen across
|
|
1014
|
+
an account switch.
|
|
1015
|
+
5. Treat `caller_changed` from a write as the same error from a read: nothing
|
|
1016
|
+
ran (#893). A write TanStack Query held back (offline, behind a mutation
|
|
1017
|
+
`scope`, or retried) is refused, with nothing sent, when someone else is
|
|
1018
|
+
signed in by the time it would go out, and a write restored from a
|
|
1019
|
+
persister is always refused. Call `mutate()` again as the new caller.
|
|
1020
|
+
6. Check where pages run with `network: "env"`:
|
|
1021
|
+
- on `ic0.app`, Codespaces or Gitpod, `allowEnvConfig: true` now throws;
|
|
1022
|
+
name canisters with `{ id }` there (#900);
|
|
1023
|
+
- a dev page opened by a LAN address, a `*.local` name or a tunnel now
|
|
1024
|
+
calls its own origin, never mainnet (#902);
|
|
1025
|
+
- on a canister's own `icp0.io` page, `{ name }` targets now resolve from
|
|
1026
|
+
the cookie's canister ids, with mainnet's root key (#901).
|
|
1027
|
+
7. Smaller changes, each with its migration in the entries below: a `switch`
|
|
1028
|
+
over `AuthLike`'s `getStatus().state` needs a `default` (#911); an app's
|
|
1029
|
+
own queries on `client.queryClient` now retry as a plain `QueryClient`'s do
|
|
1030
|
+
(#903); two copies of `@tanstack/query-core` now throw instead of
|
|
1031
|
+
misbehaving (#873); with the method typed `any`, pass `arg`, not `[arg]`
|
|
1032
|
+
(#874); and an auth factory's error now comes from `signIn()`, not from the
|
|
1033
|
+
first read (#912).
|
|
1034
|
+
|
|
1035
|
+
The guide (`llms.txt`) and the skill now say they apply to 4.0.0-beta.6, and
|
|
1036
|
+
the five examples pin `^4.0.0-beta.6`.
|
|
1037
|
+
|
|
1038
|
+
### @ic-reactor/core, @ic-reactor/react, @ic-reactor/vite-plugin
|
|
1039
|
+
|
|
1040
|
+
#### Changed
|
|
1041
|
+
|
|
1042
|
+
- **Breaking for Node below 22.12:** all three packages now declare
|
|
1043
|
+
`"engines": { "node": ">=22.12.0" }`; core and react said `>=18` and the
|
|
1044
|
+
Vite plugin said nothing. None of them worked on Node 18 (no global
|
|
1045
|
+
`crypto` in an ES module for `createTestClient`, and a `candid-core-cli gen`
|
|
1046
|
+
the plugin runs that needs a newer `readdir`), and `require()` of core and
|
|
1047
|
+
react, which are ES-only, needs Node 22.12 (or 20.19, which reached end of life on
|
|
1048
|
+
2026-04-30). A floor can fall in a 4.x minor but not rise, so it starts at
|
|
1049
|
+
the first Node 22 that works. CI now runs the package tests and a
|
|
1050
|
+
`candid-core-cli gen` on Node 22.12.0 itself. Migration: run Node 22.12 or
|
|
1051
|
+
newer (#896).
|
|
1052
|
+
|
|
1053
|
+
#### Fixed
|
|
1054
|
+
|
|
1055
|
+
- The tarball now ships the MIT `LICENSE` the manifest names; the repository
|
|
1056
|
+
has a root `LICENSE` again, and `pnpm verify:packages` fails a tarball that
|
|
1057
|
+
has none or one that is not a copy of the root file (#898).
|
|
1058
|
+
|
|
1059
|
+
### @ic-reactor/core
|
|
1060
|
+
|
|
1061
|
+
#### Added
|
|
1062
|
+
|
|
1063
|
+
- `timeoutMs` bounds how long a call waits for its answer (#875). On the
|
|
1064
|
+
canister target, `client.canister(actor, { id, timeoutMs })`, it bounds every
|
|
1065
|
+
direct call, `queryOptions` read and mutation made through that canister
|
|
1066
|
+
object; in `client.mutationOptions()`' third argument, `{ timeoutMs }`, it
|
|
1067
|
+
bounds that mutation's runs instead. When it passes, the call rejects with
|
|
1068
|
+
`code: "timeout"`: `cancelled` with `mayHaveExecuted: false` for a read and
|
|
1069
|
+
for a write that had not gone out, and `outcome_unknown` with
|
|
1070
|
+
`mayHaveExecuted: true` for a write whose request went out (an
|
|
1071
|
+
`{ update: "idempotent" }` read's included) and for a re-send of a write
|
|
1072
|
+
`client.resendOf()` offered, at any point, which `client.resendOf()` offers
|
|
1073
|
+
like any other unknown outcome when given the canister object the write went
|
|
1074
|
+
through (another `timeoutMs` is another object). Such a write is to be
|
|
1075
|
+
reconciled, never sent again blindly. A timed-out write that went out
|
|
1076
|
+
carries `expiresAt`; a re-send that timed out before it went out has none,
|
|
1077
|
+
and the earlier error's still holds. Nothing is re-sent for a timeout, and
|
|
1078
|
+
the request is not torn down. Canister objects that differ only in
|
|
1079
|
+
`timeoutMs` share query keys. Without `timeoutMs`, nothing changes, except
|
|
1080
|
+
for the next entry.
|
|
1081
|
+
- A read aborted while it waits for its caller's identity now rejects
|
|
1082
|
+
`cancelled` at once, with no code, and sends nothing. It used to wait for the
|
|
1083
|
+
identity, then reject `caller_changed`, `client_disposed` or
|
|
1084
|
+
`unauthenticated` (`identity_unavailable`) when one of those applied, and
|
|
1085
|
+
otherwise send the read nobody waited for. The abort now comes first.
|
|
1086
|
+
- Core's size limit is raised to 16.32 kB for `timeoutMs` (16,269 B, +381 B),
|
|
1087
|
+
and the app checks with the peers included to 91.34 kB (91,291 B, +394 B)
|
|
1088
|
+
and 92.61 kB (92,561 B, +399 B).
|
|
1089
|
+
- `client.time(canister)`: the IC's time in nanoseconds since the epoch, as a
|
|
1090
|
+
`bigint`, the unit of an ICRC-1 `created_at_time` (#876). It reads the
|
|
1091
|
+
`time` path with a `read_state` routed by the canister's subnet and verifies
|
|
1092
|
+
the certificate against the network's root key, as every reply is; a
|
|
1093
|
+
certificate that does not verify rejects `not_delivered`. The certificate's
|
|
1094
|
+
age is not compared with the host's clock, the clock this member replaces.
|
|
1095
|
+
It needs no signed-in caller. The management canister `aaaaa-aa` routes no
|
|
1096
|
+
`read_state`, so it is refused `invalid_args` (`management_canister`) with
|
|
1097
|
+
nothing sent. It is a `Client` member: the export budget is unchanged.
|
|
1098
|
+
- `client.encode(canister, method, vars)`: the Candid bytes a call of
|
|
1099
|
+
`method` with `vars` sends, as a `Uint8Array`, for an ICRC-21 consent
|
|
1100
|
+
request's `arg` or an audit log (#877). It uses the call path's own encoder
|
|
1101
|
+
and variables rule, and variables that do not encode throw `invalid_args`
|
|
1102
|
+
with the `issues` a call of them rejects with. It sends
|
|
1103
|
+
nothing and needs no canister id.
|
|
1104
|
+
- `Resend.bytes`, on what `client.resendOf()` offers: the bytes the attempt
|
|
1105
|
+
sent, which `send()` sends again; a copy on every read (#877).
|
|
1106
|
+
- `ReactorError.expiresAt`: on a write that got no answer (`outcome_unknown`,
|
|
1107
|
+
or `cancelled` with `mayHaveExecuted: true`), the latest ingress expiry its
|
|
1108
|
+
sends were signed with, in nanoseconds since the epoch on the IC's clock
|
|
1109
|
+
(#878). It is read from each call request as the agent builds it, before
|
|
1110
|
+
it is signed, so a signal that aborts while the answer is awaited keeps it.
|
|
1111
|
+
A call request whose signal aborted before it was signed is not sent. A
|
|
1112
|
+
write the client sent again carries the latest of its sends' expiries, and
|
|
1113
|
+
a `resendOf` re-send its own. Answered or refused writes and reads carry
|
|
1114
|
+
none. It is not enumerable, so `JSON.stringify(error)` keeps working. A
|
|
1115
|
+
field of an existing type: the export budget is unchanged.
|
|
1116
|
+
- `client.canisterId(target)` gives what a target resolves to now, from the
|
|
1117
|
+
resolver the calls use, without a call: `{ ok: true, id }`, or
|
|
1118
|
+
`{ ok: false, reason, message }` with `reason` `"no_browser"`,
|
|
1119
|
+
`"untrusted_host"` or `"absent"`, and the resolver's reason text, which a
|
|
1120
|
+
call on that target includes in its error. It reads the `ic_env` cookie on each call. A `Client`
|
|
1121
|
+
member, so the export budget is unchanged (#881).
|
|
1122
|
+
- `AuthState.expiresAt`: when the session ends, in milliseconds since the
|
|
1123
|
+
epoch, while signed in through an auth whose `getStatus()` carries
|
|
1124
|
+
`expiresAtMs` (`AuthClient` 10 does), so an app can show "signed in until".
|
|
1125
|
+
`AuthLike`'s `getStatus()` may carry that optional `expiresAtMs` (#912).
|
|
1126
|
+
- `createTestClient().dropNextReply({ method, canister, times })`
|
|
1127
|
+
(`@ic-reactor/core/testing`): loses the replies of the next `times` call
|
|
1128
|
+
requests that name `method` and are addressed to `canister`, with
|
|
1129
|
+
`refuseNext`'s matching rules and checks, and lets every other request
|
|
1130
|
+
through (#882). A test can aim a lost reply at its write while a certified
|
|
1131
|
+
read or an ICRC-21 consent read before it is answered. `dropNextReply()`
|
|
1132
|
+
with no argument is unchanged: it loses the reply of whatever call request
|
|
1133
|
+
comes next, certified reads included, as its JSDoc and the testing page now
|
|
1134
|
+
say. No new export.
|
|
1135
|
+
|
|
1136
|
+
#### Changed
|
|
1137
|
+
|
|
1138
|
+
- `network: "env"` on a canister's own `<id>.icp0.io` or `<id>.raw.icp0.io`
|
|
1139
|
+
page, with a mainnet agent host, believes the `ic_env` cookie's canister
|
|
1140
|
+
ids by default (#901). Both parents are on the Public Suffix List, so the
|
|
1141
|
+
cookie is the asset canister's own, and `{ name }` targets now resolve
|
|
1142
|
+
after `icp deploy -e ic` with no `allowEnvConfig`. Its root key is never
|
|
1143
|
+
taken there (the agent keeps mainnet's), and its Internet Identity URL
|
|
1144
|
+
only over `https:`. `ic0.app` and custom domains are unchanged. Migration:
|
|
1145
|
+
an app that relied on `{ name }` failing on icp0.io needs nothing; to
|
|
1146
|
+
refuse the cookie there, pass `allowEnvConfig: false`.
|
|
1147
|
+
- `AuthLike`'s `getStatus().state` takes any string beside the four states
|
|
1148
|
+
of `@icp-sdk/auth` 10, so an `@icp-sdk/auth` release that adds a session
|
|
1149
|
+
state does not stop `auth: (network) => new AuthClient(network)` compiling.
|
|
1150
|
+
The client already called anonymously in such a state; `authState()` reads
|
|
1151
|
+
it as `"anonymous"`. A weekly CI job type-checks core and runs the core and
|
|
1152
|
+
react tests against `@icp-sdk/auth@latest`. Code that switches over
|
|
1153
|
+
`getStatus().state` exhaustively needs a `default` (#911).
|
|
1154
|
+
- `allowEnvConfig: true` trusts less of the `ic_env` cookie (#900). The
|
|
1155
|
+
cookie's root key is never taken for a mainnet host (a mainnet boundary
|
|
1156
|
+
domain, or the `https://icp-api.io` fallback a custom-domain page uses):
|
|
1157
|
+
the agent keeps mainnet's own key. Its `INTERNET_IDENTITY_PROVIDER` is
|
|
1158
|
+
taken over `http:` only on a local page. And on a page under `ic0.app`,
|
|
1159
|
+
`*.app.github.dev` (Codespaces) or `*.gitpod.io`, none of which is a public
|
|
1160
|
+
suffix, so any sibling site can set the cookie, `createClient` throws a
|
|
1161
|
+
`TypeError` for `allowEnvConfig: true`. Migration: on those pages, name the
|
|
1162
|
+
canisters with `{ id }` (build-time env) and a replica that is not mainnet
|
|
1163
|
+
with `{ host, rootKey }`; on mainnet, drop any reliance on a cookie root
|
|
1164
|
+
key; serve a deployed Internet Identity URL over `https:`.
|
|
1165
|
+
- A view of a client pinned to a principal (what `useClient()` returns)
|
|
1166
|
+
writes only as that principal: its `mutationOptions` reject `cancelled`
|
|
1167
|
+
(`caller_changed`, `mayHaveExecuted: false`) and send nothing while someone
|
|
1168
|
+
else is the caller. Their `mutationKey` ends with the principal
|
|
1169
|
+
(`[..., method, caller]`, still found by a filter on the shorter key), so
|
|
1170
|
+
TanStack never hands a run pressed before a switch the later render's
|
|
1171
|
+
options, on any version and with any `QueryClient`; after a switch the
|
|
1172
|
+
mutation a component shows starts over, idle, and the stale run reports its
|
|
1173
|
+
refusal to the options it was pressed with. An app that shows an outcome
|
|
1174
|
+
across a switch (an unknown one, to read back as its sender) keeps it in
|
|
1175
|
+
its own state, from the promise `mutateAsync` returns. Its `authState()` is the client's own
|
|
1176
|
+
while its principal is the live caller, so an expired session reads as
|
|
1177
|
+
expired (#894). The client's own `mutationOptions`, a canister's direct
|
|
1178
|
+
calls and `func` still sign as the caller current when they run.
|
|
1179
|
+
|
|
1180
|
+
#### Fixed
|
|
1181
|
+
|
|
1182
|
+
- **BREAKING:** `createClient` checks every option, so a value the types
|
|
1183
|
+
forbid but untyped code can pass (an `import.meta.env` value, a spread,
|
|
1184
|
+
plain JavaScript) fails closed with a `TypeError` naming the option and
|
|
1185
|
+
what it got (#895). Before, `allowEnvConfig: "false"` was truthy and
|
|
1186
|
+
believed the `ic_env` cookie on any page, `fetchRootKey: "false"` fetched
|
|
1187
|
+
the root key from a remote host, a hex `rootKey` made every call fail, and
|
|
1188
|
+
an unknown key was ignored (`host` beside `network`, the 3.x habit, talked
|
|
1189
|
+
to port 8000). It now refuses an option it does not have, in the options
|
|
1190
|
+
and in a network object, listing those it has; an `allowEnvConfig` or
|
|
1191
|
+
`fetchRootKey` that is not `true` or `false`; a `rootKey` that is not 133
|
|
1192
|
+
bytes in a `Uint8Array` (`null` included); and a `fetch` that is not a
|
|
1193
|
+
function. `createTestClient` checks the network and `allowEnvConfig` it
|
|
1194
|
+
forwards the same way, and refuses a `signedIn` that is not a boolean. `parseUnits` and `formatUnits` refuse
|
|
1195
|
+
options that are not an object or have a key they do not take (Intl's
|
|
1196
|
+
`maximumFractionDigits`, a misspelt `signd`), and `parseUnits` a `signed`
|
|
1197
|
+
that is not a boolean. Migration: compare environment text,
|
|
1198
|
+
`allowEnvConfig: import.meta.env.VITE_ALLOW_ENV === "true"`; move `host`
|
|
1199
|
+
into `network: { host }`; decode a hex root key to its bytes; drop or
|
|
1200
|
+
correct the key the error names.
|
|
1201
|
+
- A write from `client.mutationOptions()` goes out as the caller current when
|
|
1202
|
+
`mutate()` was called, or not at all (#893). The client's `queryClient`
|
|
1203
|
+
reads the caller of each mutation run in its `MutationCache` when the run
|
|
1204
|
+
starts, so an `onMutate` of the app's that replaces the client's keeps the
|
|
1205
|
+
rule; on another `QueryClient`, the client's `onMutate` reads it (before
|
|
1206
|
+
TanStack Query 5.89, which passes no run context, by the options object
|
|
1207
|
+
TanStack calls it on; the runs of one observer share that object, so
|
|
1208
|
+
before 5.89 a write whose observer has another run pending that started
|
|
1209
|
+
as someone else is refused). It reads no caller for a mutation of the app's. When
|
|
1210
|
+
TanStack Query held the write back (paused offline, queued behind another
|
|
1211
|
+
mutation of its `scope`, or retried by an app's `retry`) and someone else is
|
|
1212
|
+
current by the time it is sent, it rejects with `kind: "cancelled"`,
|
|
1213
|
+
`code: "caller_changed"`, `mayHaveExecuted: false`, and nothing is sent; it
|
|
1214
|
+
was sent as whoever was signed in then. A mutation restored from a
|
|
1215
|
+
persister is now always refused that way, since who started it is not
|
|
1216
|
+
known. Migration: treat `caller_changed` from a write like the same error
|
|
1217
|
+
from a read (nothing ran), and call `mutate()` again as the new caller if
|
|
1218
|
+
that is what the user wants; resume a persisted write by calling `mutate()`
|
|
1219
|
+
again, not with `resumePausedMutations()`.
|
|
1220
|
+
- `network: "env"` no longer falls back to mainnet on a dev page opened by a
|
|
1221
|
+
LAN address (RFC 1918, link-local, IPv6 ULA or link-local), a `*.local`,
|
|
1222
|
+
`*.test` or `*.internal` name (`host.docker.internal`), or a tunnel
|
|
1223
|
+
(`*.ngrok-free.app`, `*.ngrok-free.dev`, `*.ngrok.app`, `*.ngrok.io`,
|
|
1224
|
+
`*.trycloudflare.com`, `*.loca.lt`) (#902). Such a page routes through its
|
|
1225
|
+
own origin, the dev server's proxy, as a Codespaces page does: the cookie
|
|
1226
|
+
is not believed and no root key is taken or fetched, so certified calls
|
|
1227
|
+
fail verification instead of reaching mainnet. A dev wallet opened from a
|
|
1228
|
+
phone showed, and after a mainnet sign-in sent, real ICP. Migration: a
|
|
1229
|
+
production app served from such a name now calls its own origin; give it
|
|
1230
|
+
`network: "ic"` or a `{ host, rootKey }` network.
|
|
1231
|
+
- An app's own queries on `client.queryClient`, such as REST calls under
|
|
1232
|
+
`ReactorProvider`, keep TanStack Query's retries: the default `retry`
|
|
1233
|
+
applies the client's rule only to a `ReactorError`, and retries any other
|
|
1234
|
+
error up to 3 times in a browser, as a plain `QueryClient` does (never on a
|
|
1235
|
+
server). The client's own reads set `retry` themselves and are unchanged.
|
|
1236
|
+
An app that turned retries off through this default sets `retry: false` on
|
|
1237
|
+
its queries instead (#903).
|
|
1238
|
+
- A write from `client.mutationOptions` invalidates the reads on the
|
|
1239
|
+
`QueryClient` it ran on, which TanStack Query 5.89 and later hands it, as
|
|
1240
|
+
well as on `client.queryClient`: an app that nests its own
|
|
1241
|
+
`QueryClientProvider` inside `ReactorProvider` now sees its reads refresh
|
|
1242
|
+
after a write (#903).
|
|
1243
|
+
- A reply over about a million bytes decodes. candid-core charges a blob one
|
|
1244
|
+
element per byte against its 1,000,000-element budget, so an SNS wasm from
|
|
1245
|
+
SNS-W's `get_wasm`, the ICPSwap factory's `getActiveWasm` or a large asset
|
|
1246
|
+
chunk rejected `invalid_reply` (`value_elements limit 1000000 exceeded`), and
|
|
1247
|
+
an update with such a reply rejected after it ran. A reply may now hold one
|
|
1248
|
+
value per byte plus the budget, which bounds only values that take no
|
|
1249
|
+
bytes, such as a long `vec null`, however large a blob beside them. A new `maxElements` option of
|
|
1250
|
+
`createClient` and `createTestClient` sets that budget, and a mock decodes
|
|
1251
|
+
its arguments the same way, so a test can upload a large chunk (#899).
|
|
1252
|
+
- `client.queryOptions()` and `client.queryKey()` refuse a symbol given as
|
|
1253
|
+
the variables that is not core's `skipToken`, with a `TypeError` that says
|
|
1254
|
+
two copies of `@tanstack/query-core` are installed and how to get one
|
|
1255
|
+
(#873). Such a symbol is the other copy's `skipToken`; it used to be
|
|
1256
|
+
encoded as the argument, so a read meant to wait ran and rejected
|
|
1257
|
+
`invalid_args`. A method whose one argument is Candid `reserved` still
|
|
1258
|
+
takes any value, a symbol included. This check runs in production too: an
|
|
1259
|
+
app with two copies gets a thrown `TypeError` where it got a rejected read.
|
|
1260
|
+
Migration: install `@tanstack/query-core` at the exact version your
|
|
1261
|
+
`@tanstack/react-query` depends on, so that `npm ls @tanstack/query-core`
|
|
1262
|
+
shows one copy.
|
|
1263
|
+
- A read made with `queryOptions` stops waiting for the HTTP answer to its
|
|
1264
|
+
request, and to each `read_state` that polls for it, once its query
|
|
1265
|
+
function's signal aborts, and rejects `cancelled` (part of #875). Its signal
|
|
1266
|
+
used to reach only the check before sending and the waits between polls:
|
|
1267
|
+
neither the client nor the agent bounds an HTTP request, so a request that
|
|
1268
|
+
got no answer kept the read waiting after TanStack cancelled it. The request
|
|
1269
|
+
itself is not torn down. `mayHaveExecuted` is `false`, except for an update
|
|
1270
|
+
read with `{ update: "idempotent" }` whose call request went out.
|
|
1271
|
+
- With the method given as `any` (`client.queryOptions<A, any>(canister,
|
|
1272
|
+
method, vars)` for a method named by data), the variables of
|
|
1273
|
+
`queryOptions`, `queryKey`, `mutationOptions` (its `mutationFn`) and
|
|
1274
|
+
`resendOf` (its `dedupedBy` and `arg`) follow the run-time rule: the value
|
|
1275
|
+
for a one-argument method, a tuple for several, `undefined` or `[]` for
|
|
1276
|
+
none (#874). They were typed `unknown[]`, so a one-argument call compiled
|
|
1277
|
+
only as `[arg]`, the shape the client refuses with `invalid_args`. They are
|
|
1278
|
+
now any value but a symbol, so a read with a real value keeps
|
|
1279
|
+
`CanisterQueryOptions<unknown, never, never>`. `[arg]` still compiles (an
|
|
1280
|
+
array is a value too) and the client still refuses it with `invalid_args`.
|
|
1281
|
+
Migration: code that wrote `[arg]` (or cast `arg as never`) on this path
|
|
1282
|
+
writes `arg`; a `dedupedBy` that read `arg[0]` reads `arg`. Inside a
|
|
1283
|
+
function generic over the `Actor`, only `queryOptions` gets the wider type;
|
|
1284
|
+
`mutationOptions`, `queryKey` and `resendOf` still need a cast there.
|
|
1285
|
+
- A session that ends by the clock is announced: the client sets a timer for
|
|
1286
|
+
the auth's `expiresAtMs` and tells its listeners, so `useAuth()` renders
|
|
1287
|
+
`"expired"` at the session's end with no user activity, where it kept
|
|
1288
|
+
saying `"signed-in"` while calls went out anonymous. A read of `caller()`
|
|
1289
|
+
or `authState()` that sees a change no notification announced also tells
|
|
1290
|
+
the listeners, in a microtask. A sign-in that ends at another time than the
|
|
1291
|
+
last one now notifies too, since `expiresAt` changed (#912).
|
|
1292
|
+
- An auth factory that throws (`new AuthClient()` reads `localStorage`,
|
|
1293
|
+
which throws where site data is blocked) no longer breaks every read: it is
|
|
1294
|
+
called once, the client calls anonymously so public reads still work, the
|
|
1295
|
+
error is logged once in development, and `signIn()` and `signOut()` reject
|
|
1296
|
+
with what the factory threw. It was called again on every read and render,
|
|
1297
|
+
and its error escaped from `caller()`, `authState()`, `subscribe()`,
|
|
1298
|
+
`queryOptions()` and direct calls. Migration: catch the factory's error
|
|
1299
|
+
from `signIn()`, not from the first read; build a new client to try the
|
|
1300
|
+
factory again (#912).
|
|
1301
|
+
- The test auth of `createTestClient()` expires by the clock, as
|
|
1302
|
+
`AuthClient` does: past its `expiresAtMs` (eight hours after a sign-in) its
|
|
1303
|
+
status reads `"expired"` without telling anyone. `auth.expire()` is still
|
|
1304
|
+
the explicit form, which does tell them (#912).
|
|
1305
|
+
|
|
1306
|
+
### @ic-reactor/react
|
|
1307
|
+
|
|
1308
|
+
#### Changed
|
|
1309
|
+
|
|
1310
|
+
- **BREAKING:** `useClient()` returns a view of the client for the caller
|
|
1311
|
+
the render shows every time, not the provider's client object once the
|
|
1312
|
+
live caller renders (#894). The view is a new object after a sign-in, a
|
|
1313
|
+
switch or a sign-out and the same one across renders for one caller, so a
|
|
1314
|
+
`useMemo`, an effect or React Compiler's cache keyed on it builds the new
|
|
1315
|
+
caller's options, where they kept the previous caller's key (and showed
|
|
1316
|
+
that caller's data) before. A write built from it is refused rather than
|
|
1317
|
+
signed by a caller the render did not show. Migration: build options from
|
|
1318
|
+
the current `useClient()` value and list it in the dependencies; compare
|
|
1319
|
+
two clients by `client.queryClient`, not by identity; read who signs now
|
|
1320
|
+
from the client you created, since the view's `caller()` is its render's.
|
|
1321
|
+
A handler that does `await client.signIn()` and then `mutation.mutate(arg)`
|
|
1322
|
+
presses with the last committed render's options: it is refused
|
|
1323
|
+
(`caller_changed`) when the signed-in render has not committed yet (one
|
|
1324
|
+
that suspends on the user's data); let the next render press, or use a
|
|
1325
|
+
direct canister call, which signs as the caller current.
|
|
1326
|
+
- `useAuth()` returns `expiresAt` while signed in, and renders again when it
|
|
1327
|
+
changes and when a session ends by the clock (#912).
|
|
1328
|
+
|
|
1329
|
+
#### Fixed
|
|
1330
|
+
|
|
1331
|
+
- In development, `ReactorProvider` throws when the client's `QueryClient`
|
|
1332
|
+
is not an instance of `@tanstack/react-query`'s `QueryClient`: two copies
|
|
1333
|
+
of `@tanstack/query-core` are loaded (#873). Production builds do not
|
|
1334
|
+
check. Migration: install `@tanstack/query-core` at the exact version your
|
|
1335
|
+
`@tanstack/react-query` depends on; `npm ls @tanstack/query-core` must show
|
|
1336
|
+
one copy.
|
|
1337
|
+
|
|
1338
|
+
### @ic-reactor/vite-plugin
|
|
1339
|
+
|
|
1340
|
+
#### Added
|
|
1341
|
+
|
|
1342
|
+
- `onGenerated`, an option called once for each module a run generated, in
|
|
1343
|
+
`vite build` and `vite dev`, with the module's canister `names`, the
|
|
1344
|
+
absolute `didFile`, `module` and `envelope` paths, its `status`
|
|
1345
|
+
(`"written"` or `"unchanged"`) and a `signal` that the run's close aborts.
|
|
1346
|
+
It runs inside the plugin's queue of runs, so `buildStart` waits for it,
|
|
1347
|
+
and a close stops waiting. A throw fails the module's canisters as a
|
|
1348
|
+
failed generation does, and the next run calls it again. Not called for a
|
|
1349
|
+
canister that failed, nor for a run that does not generate the module
|
|
1350
|
+
again. An option, not an export (#880).
|
|
1351
|
+
- Under `vite dev` and `vite preview`, a warning, once per server, when a
|
|
1352
|
+
page is loaded by a `Host` that is not loopback, saying that `"env"` is for
|
|
1353
|
+
`localhost` pages (#902).
|
|
1354
|
+
|
|
1355
|
+
#### Fixed
|
|
1356
|
+
|
|
1357
|
+
- On Windows, local network detection works with icp-cli installed from npm
|
|
1358
|
+
(`@icp-sdk/icp-cli`). npm links it as `icp.cmd`, which the plugin could not
|
|
1359
|
+
start, so detection always failed while the network was running. On
|
|
1360
|
+
Windows the plugin now runs the `icp.exe` that package installs, from the
|
|
1361
|
+
app's own install first, then a global npm install (`%APPDATA%\npm`), so a
|
|
1362
|
+
command that hangs is killed after its timeout; it runs the package's bin
|
|
1363
|
+
script with Node when that `icp.exe` is missing, and falls back to an
|
|
1364
|
+
`icp.exe` on `PATH`. When no `icp` can be started at all, the
|
|
1365
|
+
warning says so and how to make one runnable on that platform, instead of
|
|
1366
|
+
asking whether the network is running. CI runs the tests of how
|
|
1367
|
+
the plugin starts `icp` on Windows (#905).
|
|
1368
|
+
|
|
1369
|
+
### Docs
|
|
1370
|
+
|
|
1371
|
+
- The README and the Vite plugin guide say that any local network restart
|
|
1372
|
+
needs a dev server restart, even when the root key stays the same: the
|
|
1373
|
+
plugin keeps sending the ids it detected first (#881).
|
|
1374
|
+
- The client guide has a "Deploy to mainnet" section: `icp deploy -e ic`,
|
|
1375
|
+
what `"env"` resolves to on `icp0.io`, `ic0.app` and a custom domain,
|
|
1376
|
+
`{ id }` from build-time env with the local and the mainnet id, and when
|
|
1377
|
+
`allowEnvConfig: true` is safe; `llms.txt` states the rule (#901).
|
|
1378
|
+
- The consumer guide (`packages/core/llms.txt`) and the Reads, Writes, Values
|
|
1379
|
+
and Client pages say five things that already worked (#879): a read with
|
|
1380
|
+
`{ update: "idempotent" }` is still an update, refused for a signed-out or
|
|
1381
|
+
`"anonymous"` client, and an ICRC-21 consent message is read as the caller
|
|
1382
|
+
who will send the write; after a `canister_err` the canister calls
|
|
1383
|
+
temporary, send the same argument object again; `parseUnits` accepts
|
|
1384
|
+
trailing zeros, `"5."` and `".5"`, and refuses only significant excess
|
|
1385
|
+
fraction digits; a kept `resendOf` offer is typed
|
|
1386
|
+
`NonNullable<ReturnType<typeof client.resendOf<A, "method">>>`; and a client
|
|
1387
|
+
made outside a provider, or kept across its unmount, is the app's to release
|
|
1388
|
+
with `client.dispose()`.
|
|
1389
|
+
- The guide and the Writes page say how to get the bytes a write sends
|
|
1390
|
+
(#877).
|
|
1391
|
+
- The guide and the Errors page say when a read-back after an unknown write
|
|
1392
|
+
outcome is final: once the IC's time is past `error.expiresAt` (#878).
|
|
1393
|
+
- The guide, Writes and Errors stamp `created_at_time` from
|
|
1394
|
+
`client.time(ledger)` instead of `Date.now()` (#876).
|
|
1395
|
+
- The guide, the auth and client pages and `AuthLike`'s JSDoc no longer say a
|
|
1396
|
+
wallet fits with a small adapter. What fits is a sign-in that hands over an
|
|
1397
|
+
`Identity` (a delegation or a key); a wallet that signs each call itself
|
|
1398
|
+
(ICRC-25 and ICRC-49: OISY, Plug, NFID accounts) does not. The auth page
|
|
1399
|
+
shows such a wallet's writes through `SignerAgent` beside an anonymous
|
|
1400
|
+
client, with the reads invalidated by hand (#904).
|
|
1401
|
+
- The client guide has a section for a local network without the Vite plugin
|
|
1402
|
+
(Next.js, Rsbuild, webpack, esbuild): `network: "local"` with `{ id }` from
|
|
1403
|
+
a build-time variable written by `icp canister status -e local -i`, or a
|
|
1404
|
+
Next.js rewrite of `/api/v2` and `/api/v3` to keep `"env"`. Core's guide
|
|
1405
|
+
states the rule in a few lines (#906).
|
|
1406
|
+
- Getting started has a step 0 that creates the project (Vite's `react-ts`
|
|
1407
|
+
template, or `npm init -y` and `npm pkg set type=module` for Node), so its
|
|
1408
|
+
top-level `await` scripts run under `npx tsx` on the first try. It, core's
|
|
1409
|
+
package page and core's guide now say that core and react are ES modules
|
|
1410
|
+
only and what CommonJS code can do, and show the `.js` import form
|
|
1411
|
+
`moduleResolution: "nodenext"` needs (#907).
|
|
1412
|
+
- The guide's Errors section says how long a call waits: unless the call has
|
|
1413
|
+
a `timeoutMs`, an HTTP request has no timeout in the client or the agent, an accepted update polls for about
|
|
1414
|
+
five minutes (a deadline checked when a poll is answered) before `outcome_unknown`, and
|
|
1415
|
+
a cancelled read stops waiting (part of #875).
|
|
1416
|
+
- The guide's Setup, both READMEs, the core and react package pages and
|
|
1417
|
+
Getting started say to install `@tanstack/query-core` at the version
|
|
1418
|
+
`@tanstack/react-query` depends on, and to check that
|
|
1419
|
+
`npm ls @tanstack/query-core` shows one copy (#873).
|
|
1420
|
+
- The auth guide says that a `"signed-in"` session may be read-only:
|
|
1421
|
+
Internet Identity can scope one to queries, and `@icp-sdk/auth` does not
|
|
1422
|
+
expose that scope yet (#912).
|
|
1423
|
+
- The docs site has three guides that build a whole app step by step: an
|
|
1424
|
+
ICRC-1 wallet with Internet Identity sign-in, a Next.js server page for
|
|
1425
|
+
one user, and an MCP server that gives an AI agent a ledger's tools
|
|
1426
|
+
(#948, #953, #954). It now has B3Pay's look and the core mark, and one
|
|
1427
|
+
home for the three libraries with a library switcher (#943, #944, #945,
|
|
1428
|
+
#947, #955).
|
|
1429
|
+
|
|
1430
|
+
### Examples (not published)
|
|
1431
|
+
|
|
1432
|
+
- `icrc-ledger`'s Sandbox tab finds its sandbox by the client's
|
|
1433
|
+
`QueryClient`, and reads who signs now from the sandbox's client, since
|
|
1434
|
+
`useClient()` returns a view per caller (#894).
|
|
1435
|
+
- `canister-studio`'s write runner shows the confirm step again, with the
|
|
1436
|
+
new sender, when a write is refused because the caller changed before the
|
|
1437
|
+
page caught up (#894).
|
|
1438
|
+
- An outcome stays on screen across an account switch: `canister-studio`'s
|
|
1439
|
+
write runner and wallet Send panel, `icrc-ledger`'s Sandbox transfer, and
|
|
1440
|
+
`vite-wallet`'s Send and Pay forms (through one `useTransfer` hook), keep
|
|
1441
|
+
the outcome of the write they sent in their own state, since the mutation
|
|
1442
|
+
the new caller's render shows starts over idle; `vite-wallet` reads
|
|
1443
|
+
`client.resendOf`'s offer from that kept outcome (#894).
|
|
1444
|
+
- `node-agent-tool` no longer tells you to export an identity's key into the
|
|
1445
|
+
project. Its README and docs page say to give the tool, and an agent running
|
|
1446
|
+
it, an identity of its own that controls nothing, with its key outside the
|
|
1447
|
+
project and readable by you alone, and that the agent can read the seed
|
|
1448
|
+
variable.
|
|
1449
|
+
The example's `.gitignore` and the root one now ignore `*.pem` (#910).
|
|
1450
|
+
- A fifth example, `canister-studio`: a TanStack Start app, rendered on the
|
|
1451
|
+
server, that gives every method of any mainnet canister an automatic form
|
|
1452
|
+
and calls it with ic-reactor's rules in view. The ICP, ckBTC and ckETH
|
|
1453
|
+
ledgers and the ckBTC minter are typed by three generated modules; any
|
|
1454
|
+
other canister's interface is read from its certified `candid:service`
|
|
1455
|
+
metadata, compiled on the server by `@candid-core/cli` and rebuilt in the
|
|
1456
|
+
browser with `schemaFromContract`, and an interface from the uncertified
|
|
1457
|
+
fallback query is labelled so. It shows one client per request and per
|
|
1458
|
+
tab, reads hydrated without a refetch, shared query links rendered on the
|
|
1459
|
+
server, writes confirmed with their sender and exact bytes, `resendOf`
|
|
1460
|
+
after an unknown outcome, certified reads, an idempotent update read as a
|
|
1461
|
+
query (ckBTC's `get_btc_address`), a Safety lab of ten scenarios over
|
|
1462
|
+
`createTestClient()` in the browser, and a playground that compiles a
|
|
1463
|
+
pasted `.did` in the browser. Its tests run offline; its `smoke` script
|
|
1464
|
+
checks its `.did` files and reads against mainnet. It pins
|
|
1465
|
+
`^4.0.0-beta.6`, `@candid-core/schema` `0.4.0` and `@candid-core/cli`
|
|
1466
|
+
`0.3.0`, a runtime dependency here, and has a page in the examples docs.
|
|
1467
|
+
The root README, the docs' home page and Getting started now say five
|
|
1468
|
+
examples.
|
|
1469
|
+
|
|
1470
|
+
## core, react, vite-plugin 4.0.0-beta.5
|
|
1471
|
+
|
|
1472
|
+
Prepared on 2026-10-11. It goes out under npm's `beta` dist-tag, as
|
|
1473
|
+
`@ic-reactor/core@beta`; `latest` stays 3.x until 4.0 GA. Changes since
|
|
1474
|
+
4.0.0-beta.4.
|
|
1475
|
+
|
|
1476
|
+
This release moves ic-reactor 4 onto the candid-core pair published on
|
|
1477
|
+
2026-10-10. Both are pinned exactly, where beta.4 required `0.3.0` and
|
|
1478
|
+
`0.2.0`:
|
|
1479
|
+
|
|
1480
|
+
- `@ic-reactor/core`: peer `@candid-core/schema` at exactly `0.4.0`;
|
|
1481
|
+
- `@ic-reactor/vite-plugin`: peer `@candid-core/cli` at exactly `0.3.0`, the
|
|
1482
|
+
version its install hints now name.
|
|
1483
|
+
|
|
1484
|
+
Three things are new, as core's and the Vite plugin's entries below say:
|
|
1485
|
+
|
|
1486
|
+
- `client.queryOptions` reads the mode each method of a generated `Actor`
|
|
1487
|
+
now carries, and an update or a oneway method does not compile there
|
|
1488
|
+
(#851).
|
|
1489
|
+
- The Vite plugin's `methods` on a canister entry: a fetched `didFile` is
|
|
1490
|
+
written as the projection of the live interface to the methods the app
|
|
1491
|
+
calls, so the generated module has only them (#852, phase 2).
|
|
1492
|
+
- The Vite plugin's live check: `verify: "live"` on an entry, or
|
|
1493
|
+
`IC_REACTOR_VERIFY`, checks a `didFile` on disk against the live canister's
|
|
1494
|
+
certified interface when a build or dev server starts.
|
|
1495
|
+
|
|
1496
|
+
No `@ic-reactor/*` export is added or removed (core 13,
|
|
1497
|
+
`@ic-reactor/core/testing` 2, react 4, vite-plugin 2). The pair itself changes
|
|
1498
|
+
no call, key or error at run time: what it adds, each method's mode in the
|
|
1499
|
+
generated `Actor`, is read by `client.queryOptions` at compile time only.
|
|
1500
|
+
Schema 0.4.0's JavaScript is 0.3.0's but for one header comment, so an app's
|
|
1501
|
+
bundle does not change.
|
|
1502
|
+
|
|
1503
|
+
What an app on beta.4 does:
|
|
1504
|
+
|
|
1505
|
+
1. Bump all three `@ic-reactor/*` packages to `4.0.0-beta.5` and the pair to
|
|
1506
|
+
`0.4.0` and `0.3.0`, in one `npm install`. npm does not resolve a partial
|
|
1507
|
+
upgrade cleanly: core 4.0.0-beta.5 peers schema exactly
|
|
1508
|
+
`0.4.0` and core 4.0.0-beta.4 exactly `0.3.0`; the Vite plugin
|
|
1509
|
+
4.0.0-beta.5 peers CLI exactly `0.3.0` and 4.0.0-beta.4 exactly `0.2.0`;
|
|
1510
|
+
and CLI 0.3.0 peers schema exactly `0.4.0`, CLI 0.2.0 exactly `0.3.0`. So
|
|
1511
|
+
a command that moves only some of them meets the others' peers: with npm
|
|
1512
|
+
11, the CLI alone fails with `ERESOLVE`, and the schema alone installs
|
|
1513
|
+
with an `ERESOLVE overriding peer dependency` warning, leaving core's peer
|
|
1514
|
+
unmet. One command moves them all, and keeps each package in the section
|
|
1515
|
+
of `package.json` it is in (`dependencies` or `devDependencies`):
|
|
1516
|
+
|
|
1517
|
+
```sh
|
|
1518
|
+
npm install --save-exact @ic-reactor/core@4.0.0-beta.5 @ic-reactor/react@4.0.0-beta.5 @ic-reactor/vite-plugin@4.0.0-beta.5 @candid-core/schema@0.4.0 @candid-core/cli@0.3.0
|
|
1519
|
+
```
|
|
1520
|
+
|
|
1521
|
+
Name only the packages the app has. Keep one copy of the schema. A second
|
|
1522
|
+
one left at `0.3.0`, beside core's `0.4.0`, makes its `Principal` another
|
|
1523
|
+
type than core's, and passing one for the other fails with `TS2322` or
|
|
1524
|
+
`TS2345` ("Property '[principalBrand]' is missing").
|
|
1525
|
+
|
|
1526
|
+
2. Regenerate every committed module with `candid-core-cli gen` (the Vite
|
|
1527
|
+
plugin does it when it starts). Until then `gen --check` reports each
|
|
1528
|
+
module CLI 0.2.0 wrote as `drifted` and exits 1; the `.envelope.json`
|
|
1529
|
+
beside it does not change. A module CLI 0.2.0 wrote still compiles against
|
|
1530
|
+
schema 0.4.0, but a module CLI 0.3.0 writes does not compile against
|
|
1531
|
+
`0.3.0` (`TS2694`, "has no exported member 'WithMode'", once per method).
|
|
1532
|
+
|
|
1533
|
+
3. Code that imports the generated names compiles unchanged, but for the
|
|
1534
|
+
`queryOptions` calls of step 4. Two things in the module change what the
|
|
1535
|
+
compiler shows:
|
|
1536
|
+
- Each type is declared under its Candid name, `export type Account = …`,
|
|
1537
|
+
so a type error names `Account` where it named `$Account`. A name that
|
|
1538
|
+
cannot be a type keeps the `$`: the globals `Array`, `Promise`, `Record`
|
|
1539
|
+
and `Uint8Array`, and words TypeScript refuses as a type's name, such as
|
|
1540
|
+
`delete` or `string`.
|
|
1541
|
+
- Each method of `type Actor` is its call signature intersected with
|
|
1542
|
+
`$.WithMode<"query" | "composite_query" | "update" | "oneway">`.
|
|
1543
|
+
`Parameters<>`, `ReturnType<>`, `keyof Actor` and a plain async function
|
|
1544
|
+
that implements a method are unchanged, and so is what
|
|
1545
|
+
`client.canister<Actor>()` returns. A type test that compares a method
|
|
1546
|
+
type of `Actor` for exact equality with its bare signature now fails.
|
|
1547
|
+
|
|
1548
|
+
4. Expect the compile errors of `client.queryOptions` listed under
|
|
1549
|
+
`@ic-reactor/core` below: `TS2769` ("No overload matches this call") for
|
|
1550
|
+
an update or a oneway method of a generated `Actor`, and for some calls
|
|
1551
|
+
that ran without a `TypeError` on beta.4: an update's fourth argument whose
|
|
1552
|
+
type may be `undefined`, and a method TypeScript cannot tell is a read (a
|
|
1553
|
+
type parameter, a union that may name an update, any method of a generic
|
|
1554
|
+
`Actor`). A write goes through `client.mutationOptions`; an update that
|
|
1555
|
+
answers the same however often it runs takes `{ update: "idempotent" }`;
|
|
1556
|
+
and a generic wrapper gives the type arguments,
|
|
1557
|
+
`client.queryOptions<A, any>(…)`. Nothing changes at run time.
|
|
1558
|
+
|
|
1559
|
+
5. Check the Vite plugin's options. It now refuses what beta.4 read without a
|
|
1560
|
+
word, as its entries below say: a key that is no option (a misspelled
|
|
1561
|
+
`didfile` or `canisterID`), a value given that is not one (`null`
|
|
1562
|
+
included), a `failOnError` or `injectEnvironment` other than `true` or
|
|
1563
|
+
`false`, and options, `canisters` or an entry that is not a plain object.
|
|
1564
|
+
A key or value of a canister entry fails that canister, under
|
|
1565
|
+
`failOnError`; one of the options themselves throws when the config loads.
|
|
1566
|
+
Each message names the option or quotes the value.
|
|
1567
|
+
|
|
1568
|
+
### @ic-reactor/core
|
|
1569
|
+
|
|
1570
|
+
#### Changed
|
|
1571
|
+
|
|
1572
|
+
- Its peer `@candid-core/schema` is exactly `0.4.0`, where it was `0.3.0`
|
|
1573
|
+
(#866). Core's types read its `ModeOf` for the check below; core's
|
|
1574
|
+
JavaScript does not change but for its version string.
|
|
1575
|
+
|
|
1576
|
+
#### `queryOptions` refuses an update or a oneway method at compile time (#851)
|
|
1577
|
+
|
|
1578
|
+
`client.queryOptions(canister, method, …)` reads the mode each method of a
|
|
1579
|
+
generated `Actor` carries (`$.WithMode<mode>`, from `candid-core-cli gen`
|
|
1580
|
+
0.3.0), and its `method` takes only a method a read may be built from:
|
|
1581
|
+
|
|
1582
|
+
| The method's mode in the `Actor` | Without the fourth argument | With `{ update: "idempotent" }` |
|
|
1583
|
+
| --------------------------------- | --------------------------- | ------------------------------- |
|
|
1584
|
+
| `query`, `composite_query` | compiles | compiles |
|
|
1585
|
+
| `update` | **does not compile** | compiles |
|
|
1586
|
+
| `oneway` | **does not compile** | **does not compile** |
|
|
1587
|
+
| none (an `Actor` written by hand) | compiles | compiles |
|
|
1588
|
+
|
|
1589
|
+
This is a compile-time break for code that names an update method of a
|
|
1590
|
+
generated `Actor` without a fourth argument whose type is
|
|
1591
|
+
`{ update: "idempotent" }`, or that names a oneway method with or without it.
|
|
1592
|
+
The error is `TS2769` ("No overload matches this call"), saying that the
|
|
1593
|
+
method `is not assignable to parameter of type 'ReadMethod<Actor>'`, or
|
|
1594
|
+
`'OptInMethod<Actor>'` for a oneway with the opt-in. Use
|
|
1595
|
+
`client.mutationOptions` for a write, or call it directly; pass
|
|
1596
|
+
`{ update: "idempotent" }` only for an update that answers the same however
|
|
1597
|
+
often it runs.
|
|
1598
|
+
|
|
1599
|
+
Such a call with the method's name written out compiled on beta.4 and threw a
|
|
1600
|
+
`TypeError` when it ran. Two kinds of call compiled on beta.4, could run
|
|
1601
|
+
without a `TypeError`, and no longer compile:
|
|
1602
|
+
|
|
1603
|
+
- **A fourth argument that may be `undefined`.** Its type, such as that of an
|
|
1604
|
+
optional options parameter a wrapper passes on, does not say it is the
|
|
1605
|
+
opt-in, so for an update method the call does not compile, though it
|
|
1606
|
+
ran whenever the value was defined. Pass `{ update: "idempotent" }`, or a
|
|
1607
|
+
value typed `{ readonly update: "idempotent" }` (the type is not
|
|
1608
|
+
exported). For a query it compiles as before.
|
|
1609
|
+
- **A method whose type is not one name the `Actor` marks as a read.** The
|
|
1610
|
+
call compiles only when TypeScript can tell that the method is a
|
|
1611
|
+
`ReadMethod<A>`. It cannot for three kinds of method, each of which compiled
|
|
1612
|
+
on beta.4 and is valid at run time whenever the value names a query:
|
|
1613
|
+
- a type parameter: in
|
|
1614
|
+
`function read<A, M extends keyof A & string>(c: Canister<A>, method: M, vars: …)`,
|
|
1615
|
+
`client.queryOptions(c, method, vars)`;
|
|
1616
|
+
- a union that may name an update, such as a method picker typed
|
|
1617
|
+
`keyof Actor & string` or `"icrc1_name" | "icrc1_transfer"` (a union of
|
|
1618
|
+
queries compiles);
|
|
1619
|
+
- any method of a canister whose `Actor` type is generic: in
|
|
1620
|
+
`function fee<A extends { icrc1_fee: () => Promise<bigint> }>(c: Canister<A>)`,
|
|
1621
|
+
`client.queryOptions(c, "icrc1_fee", …)`, with or without the opt-in.
|
|
1622
|
+
|
|
1623
|
+
`ReadMethod` is not exported for a function to constrain its types with.
|
|
1624
|
+
The escape is explicit type arguments,
|
|
1625
|
+
`client.queryOptions<A, any>(c, method, vars)`: it compiles, the run-time
|
|
1626
|
+
check still refuses an update or a oneway, and the options' data is
|
|
1627
|
+
`unknown`, for the caller to state. (A wrapper that passed `...args: any[]`,
|
|
1628
|
+
or no variables, did not compile on beta.4 either.)
|
|
1629
|
+
|
|
1630
|
+
An `Actor` written by hand is unchanged: its methods say no mode, every one of
|
|
1631
|
+
them compiles as before, and the run-time check refuses an update or a oneway
|
|
1632
|
+
as before.
|
|
1633
|
+
|
|
1634
|
+
Nothing changes at run time: every check `queryOptions` made, it still
|
|
1635
|
+
makes, and it is the only check for an `Actor` written by hand and for a
|
|
1636
|
+
composite query of a certified canister (the type of a canister object does
|
|
1637
|
+
not say it is certified). A call that compiled and did not throw builds the
|
|
1638
|
+
same options, with the same types. No export is added (`ReadMethod` and
|
|
1639
|
+
`OptInMethod` are named in compiler messages, not exported), and the bundle
|
|
1640
|
+
does not change. The reads guide, the writes guide and the guide
|
|
1641
|
+
(`llms.txt`) say so.
|
|
1642
|
+
|
|
1643
|
+
### @ic-reactor/react
|
|
1644
|
+
|
|
1645
|
+
- No change but the version. Its peer `@ic-reactor/core` is packed as
|
|
1646
|
+
exactly `4.0.0-beta.5`, so the two move together.
|
|
1647
|
+
|
|
1648
|
+
### @ic-reactor/vite-plugin
|
|
1649
|
+
|
|
1650
|
+
#### Changed
|
|
1651
|
+
|
|
1652
|
+
- Its peer `@candid-core/cli` is exactly `0.3.0`, where it was `0.2.0`
|
|
1653
|
+
(#866), and the install hints the plugin prints name it. The plugin
|
|
1654
|
+
generates with it as before; `methods` and the live check also run its
|
|
1655
|
+
`project` and `check` commands, which 0.3.0 adds.
|
|
1656
|
+
- Its optional peer `@icp-sdk/core` is loaded for the live check too, as for
|
|
1657
|
+
a fetch.
|
|
1658
|
+
|
|
1659
|
+
#### Added
|
|
1660
|
+
|
|
1661
|
+
- `methods` on a canister entry (#852, phase 2): a `didFile` fetched from its
|
|
1662
|
+
`canisterId` is written as the projection of the live interface to those
|
|
1663
|
+
methods and every type they reach (`candid-core-cli project`), so the
|
|
1664
|
+
generated module has only them. A name the canister does not have fails the
|
|
1665
|
+
canister, naming it and listing the canister's methods, and nothing is
|
|
1666
|
+
written. A `didFile` on disk is never fetched for a change of `methods`: it
|
|
1667
|
+
is compared with the list, with no network access, and a name the file
|
|
1668
|
+
does not hold fails the canister, while a method the file holds that the
|
|
1669
|
+
list does not name is a warning. Both say to fetch it again with
|
|
1670
|
+
`IC_REACTOR_FETCH`. A file whose methods cannot be listed fails the
|
|
1671
|
+
canister. Canisters that name one `didFile` share its fetch only
|
|
1672
|
+
when they name one list, entries with no `canisterId` included (no
|
|
1673
|
+
`methods` is the whole interface): with different lists, or a list that
|
|
1674
|
+
cannot be projected to, nothing is fetched, before any request. A shared
|
|
1675
|
+
`didFile` on disk is read by them all and compared with each list.
|
|
1676
|
+
- The live check: `verify: "live"` on an entry, or `IC_REACTOR_VERIFY=1` (or
|
|
1677
|
+
`true`, `all`, or canister names separated by commas) for every entry with a
|
|
1678
|
+
`canisterId`; `IC_REACTOR_VERIFY=0` (or `false`) turns it off for a run,
|
|
1679
|
+
`verify: "live"` included. When a build or dev server starts, the plugin fetches the
|
|
1680
|
+
certified interface and checks the `didFile` on disk against it
|
|
1681
|
+
(`candid-core-cli check`). A method that is missing (`method_missing`),
|
|
1682
|
+
changed mode (`mode_changed`) or is no longer compatible
|
|
1683
|
+
(`method_incompatible`, with the path into its type) fails the canister,
|
|
1684
|
+
naming the canister and each method, under `failOnError`. A compatible
|
|
1685
|
+
change is a warning: `special_opt_rule` findings, and a change to the types
|
|
1686
|
+
of the file's methods, told by the identities of the two projections to
|
|
1687
|
+
them. A check that cannot be made fails the canister, and says to build
|
|
1688
|
+
with `IC_REACTOR_VERIFY=0`. Under `vite dev` a failed check stands after a
|
|
1689
|
+
save, whatever it changed, until a restart checks it again and it passes,
|
|
1690
|
+
fetches the file again, or runs with the check off. A pass is reused only
|
|
1691
|
+
within one build or dev server (a rebuild, another Vite environment), and
|
|
1692
|
+
only until a check of that canister fails or cannot be made, or a fetch of
|
|
1693
|
+
its file fails or reads another interface: a restart, or the next build of
|
|
1694
|
+
the same plugin object, checks every canister again. `verify` takes only `"live"`, and any other
|
|
1695
|
+
value fails the canister.
|
|
1696
|
+
- A run (the start of a build or dev server, or a save's regeneration) reads
|
|
1697
|
+
each `didFile` once, and again after each step that runs `candid-core-cli`
|
|
1698
|
+
on it (listing its methods, the live check, generation), since each reads
|
|
1699
|
+
the file itself. A file written before the run is done with it, even saved
|
|
1700
|
+
back, is not taken, and the run reports nothing its steps found of it.
|
|
1701
|
+
`vite build` fails the canister for that reason alone
|
|
1702
|
+
(`changed during the build`) and puts back a module the generator wrote
|
|
1703
|
+
meanwhile; `vite dev` queues the file again, as a save, and says nothing of
|
|
1704
|
+
the canister until that run does. A check of a file written before the
|
|
1705
|
+
check was done, passed or failed, records no pass and neither sets nor
|
|
1706
|
+
clears a failed check.
|
|
1707
|
+
- Every option value given is checked when the plugin is made, on every
|
|
1708
|
+
canister, one without a `didFile` included, and whether or not a build
|
|
1709
|
+
would use it: only an omitted one counts as not given. A canister whose
|
|
1710
|
+
entry is not a plain object, or whose value is not one (`null`, `false`,
|
|
1711
|
+
`0` or `""` from a JavaScript config, a `didFile` or `outDir` that is not a
|
|
1712
|
+
path, a `canisterId` that is not the text of a principal, a `network`,
|
|
1713
|
+
`methods` or `verify` that is none of their values, `verify: "live"` with
|
|
1714
|
+
no `canisterId`, a getter that throws) fails, quoted as given whatever its
|
|
1715
|
+
kind (a bigint, a symbol, an object that holds itself), before the plugin
|
|
1716
|
+
fetches, writes, runs or requests anything to generate it or a `didFile` it
|
|
1717
|
+
shares; the other canisters build. So does a canister whose module a
|
|
1718
|
+
canister before it writes (two `.did` files of one name in one `outDir`), at
|
|
1719
|
+
the start of each run, and a canister `IC_REACTOR_FETCH` or
|
|
1720
|
+
`IC_REACTOR_VERIFY` names with no `canisterId`. Under `vite dev` a save of
|
|
1721
|
+
the file of any of these, or its deletion, leaves the canister failed for
|
|
1722
|
+
the same reason and runs nothing for it: a save neither generates it, over
|
|
1723
|
+
another's module or not, nor takes it for fixed. Each is still named in the
|
|
1724
|
+
`ic_env` cookie, but one whose name the cookie cannot carry (below); a
|
|
1725
|
+
`canisterId` that is not one is left out of it, with a warning.
|
|
1726
|
+
`failOnError` and `injectEnvironment` take `true` or `false`, and
|
|
1727
|
+
`canisters` a plain object: any other value, `null` included, throws when
|
|
1728
|
+
the config loads, instead of being read by truthiness or taken for none.
|
|
1729
|
+
Every default is taken for an omitted option alone. Every key is checked
|
|
1730
|
+
too, whatever its value: a key of an entry that is none of its fields (a
|
|
1731
|
+
misspelled `didfile` or `canisterID`), or of a `network` of `{ host }`
|
|
1732
|
+
other than `host`, fails the canister, and a key of the options that is
|
|
1733
|
+
none of `canisters`, `injectEnvironment` and `failOnError` throws when the
|
|
1734
|
+
config loads; the message names the option the key may misspell.
|
|
1735
|
+
- A dev server restarted with the same plugin object (an inline config, as
|
|
1736
|
+
`server.restart()` keeps it) has runs of its own: the old server's close,
|
|
1737
|
+
which Vite runs after the new server started, stops only the old server's.
|
|
1738
|
+
The new server's overlay starts as the old one's, and a failed live check
|
|
1739
|
+
still stands through the restart until a check of it passes.
|
|
1740
|
+
- Both run the app's `candid-core-cli` in a child process, as `gen` does, and
|
|
1741
|
+
never the library in the Vite process. No export is added; the two fields
|
|
1742
|
+
are new keys of `IcReactorPluginOptions["canisters"]`.
|
|
1743
|
+
|
|
1744
|
+
#### Fixed
|
|
1745
|
+
|
|
1746
|
+
- A canister's name is kept as given. One named `__proto__` (from a JSON
|
|
1747
|
+
config, or a computed key) with no `canisterId` of its own lost the ID `icp`
|
|
1748
|
+
reported for it to `Object.prototype`'s setter, so the `ic_env` cookie
|
|
1749
|
+
carried none; and one named after a member of `Object.prototype`
|
|
1750
|
+
(`__proto__`, `constructor`, `toString`, `hasOwnProperty`) with no ID found
|
|
1751
|
+
that member and was never missing: no warning, no page load asked `icp`
|
|
1752
|
+
again, and the cookie never carried its ID. A name that begins with `-`,
|
|
1753
|
+
which `icp` allows, was handed to `icp canister status` where `icp` reads a
|
|
1754
|
+
flag: it refused `-x`, and for `--help` printed its help, which the cookie
|
|
1755
|
+
carried as the canister's ID. A name that holds `&` or `=`, which no name
|
|
1756
|
+
`icp` gives a canister does, is left out of the cookie, with a warning: the
|
|
1757
|
+
cookie joins its entries with them, so it was read back as other keys, one
|
|
1758
|
+
of which could take the root key's place, and the app read no environment.
|
|
1759
|
+
- The options, `canisters`, each entry and a `network` of `{ host }` are read
|
|
1760
|
+
only as plain objects. A `Map` or a `Promise` of canisters (a loader that
|
|
1761
|
+
was not awaited) was read for its own keys, none, so the plugin started
|
|
1762
|
+
with no canister without a word, and an entry given so was taken for a
|
|
1763
|
+
canister only named in the cookie. Each is now refused, as any value that
|
|
1764
|
+
is not one, and so is an instance of a class, which was read through its
|
|
1765
|
+
class, or an object that inherits from another.
|
|
1766
|
+
- A key that is no option was never read, without a word. A misspelled
|
|
1767
|
+
`canisters` started the plugin with no canister; a misspelled `didFile`
|
|
1768
|
+
took its canister for one only named in the cookie, so nothing was
|
|
1769
|
+
generated and the module of the last run stayed; a misspelled `canisterId`
|
|
1770
|
+
generated it without its ID; and a `rootKey` beside a network's `host` was
|
|
1771
|
+
read as nothing. Each is now refused, naming the option the key may
|
|
1772
|
+
misspell: a key of the options throws when the config loads, and a key of
|
|
1773
|
+
an entry or of its network fails the canister.
|
|
1774
|
+
- The generator names its module and envelope after the `.did`'s file name
|
|
1775
|
+
with a final `.did` cut off, and nothing else, and reads only a file whose
|
|
1776
|
+
name ends in `.did`. The plugin's check of two canisters writing one module
|
|
1777
|
+
cut any extension, so a `didFile` such as `a/api.candid`, which the
|
|
1778
|
+
generator cannot read, claimed `api.ts`, and a canister after it on
|
|
1779
|
+
`c/api.did` was refused. The plugin now names these files by the
|
|
1780
|
+
generator's rule, which a test compares with what the CLI reports.
|
|
1781
|
+
|
|
1782
|
+
### Docs
|
|
1783
|
+
|
|
1784
|
+
- The guide (`llms.txt`), react's guide and the skill say they apply to
|
|
1785
|
+
4.0.0-beta.5 (#866).
|
|
1786
|
+
- Every install line and peer table names `@candid-core/schema` `0.4.0` and
|
|
1787
|
+
`@candid-core/cli` `0.3.0`: getting started, the Vite plugin guide, the
|
|
1788
|
+
core, react and Vite plugin pages, and the root and package READMEs.
|
|
1789
|
+
Getting started says that each method of `type Actor` is its call
|
|
1790
|
+
signature marked with its mode (`& $.WithMode<"query">`) (#866).
|
|
1791
|
+
- The reads guide's "Update methods" shows that `queryOptions` with an update
|
|
1792
|
+
method of a generated `Actor` does not compile, says what `ReadMethod<Actor>`
|
|
1793
|
+
is, that an `Actor` written by hand is checked at run time, which methods
|
|
1794
|
+
TypeScript cannot tell are reads and the escape `queryOptions<A, any>`, and
|
|
1795
|
+
that `{ update: "idempotent" }` opts in an update only, after the variables.
|
|
1796
|
+
It also says that a composite query of a certified canister is refused at
|
|
1797
|
+
run time only. The writes guide says `queryOptions` does not compile for an
|
|
1798
|
+
update of a generated `Actor`, and the guide (`llms.txt`) says the same in
|
|
1799
|
+
its reads section (#851).
|
|
1800
|
+
- The Vite plugin guide has four new sections, "Only the methods the app
|
|
1801
|
+
calls", "The live check", "How the plugin runs candid-core" and "A .did
|
|
1802
|
+
saved while the plugin reads it", and its options section says which values
|
|
1803
|
+
and keys are refused, that the options are plain objects, and that a
|
|
1804
|
+
canister's name is kept as given. The plugin's README has the first two
|
|
1805
|
+
sections and the same options text.
|
|
1806
|
+
Getting started points at `methods` (#852, phase 2).
|
|
1807
|
+
|
|
1808
|
+
### Examples (not published)
|
|
1809
|
+
|
|
1810
|
+
- The four examples pin `^4.0.0-beta.5`, `@candid-core/schema` `0.4.0` and
|
|
1811
|
+
`@candid-core/cli` `0.3.0`, and their seven generated modules are
|
|
1812
|
+
regenerated with CLI 0.3.0. They moved in #866, ahead of this release: an
|
|
1813
|
+
example on schema `0.4.0` cannot install beta.4, whose core peers `0.3.0`.
|
|
1814
|
+
Until this release is on npm, their check against the published packages
|
|
1815
|
+
waits for it.
|
|
1816
|
+
- `icrc-ledger`'s Sandbox test marks
|
|
1817
|
+
`client.queryOptions(minter, "get_btc_address", ownAddress)` with
|
|
1818
|
+
`@ts-expect-error`, since that call no longer compiles, and still expects
|
|
1819
|
+
its `TypeError` at run time. The README says the call does not compile
|
|
1820
|
+
(#851).
|
|
1821
|
+
- `icrc-ledger`'s `icp_ledger.did` and `ckbtc_minter.did`, and `vite-wallet`'s
|
|
1822
|
+
`ledger.did`, were trimmed by hand. Each is now the projection the plugin
|
|
1823
|
+
wrote from the mainnet canister, and its entry names the `canisterId` and
|
|
1824
|
+
`methods` it came from. The files stay committed, so the examples build
|
|
1825
|
+
offline, and `IC_REACTOR_VERIFY=1` checks them against mainnet. The ICP
|
|
1826
|
+
ledger's projection names its transfer error `Icrc1TransferError`, so
|
|
1827
|
+
`vite-wallet` imports it by that name. `icp_ledger.envelope.json` does not
|
|
1828
|
+
change; the minter's argument record is inline, as the canister declares it.
|
|
1829
|
+
|
|
1830
|
+
### CI (not published)
|
|
1831
|
+
|
|
1832
|
+
- Every module this repository generates (core's test fixtures, the e2e
|
|
1833
|
+
`hello_actor`, the snippet checker's modules) is regenerated with CLI 0.3.0,
|
|
1834
|
+
and core's type tests compile against them unchanged. React's type tests
|
|
1835
|
+
write their own stand-in for a generated `Actor`; it now marks each method
|
|
1836
|
+
with `WithMode`, as CLI 0.3.0 does, and compiles. The root, `e2e/`,
|
|
1837
|
+
`scripts/size-app/` and the export budget's schema pin move to the new pair
|
|
1838
|
+
(#866).
|
|
1839
|
+
- `pnpm verify:traps` has two new traps, `query-options-refuses-update` and
|
|
1840
|
+
`query-options-refuses-oneway`, which weaken `ReadMethod` and `OptInMethod`
|
|
1841
|
+
and show that the `@ts-expect-error` lines of `traps.test-d.ts` that refuse
|
|
1842
|
+
an update and a oneway then stop biting; `options-method-unknown` follows
|
|
1843
|
+
the new signatures. Core's type tests add the calls that compiled on beta.4
|
|
1844
|
+
and no longer do (#851).
|
|
1845
|
+
- `scripts/faults.json` grows from 298 to 417 entries, for the Vite plugin's
|
|
1846
|
+
new tests (#852, phase 2).
|
|
1847
|
+
- No size limit moves: core 14.96 kB of 15 kB, react 1.28 kB of 1.28 kB, and
|
|
1848
|
+
the app checks 89.95 kB of 90 kB and 91.16 kB of 91.2 kB, as at beta.4.
|
|
1849
|
+
|
|
1850
|
+
## core, react, vite-plugin 4.0.0-beta.4
|
|
1851
|
+
|
|
1852
|
+
Prepared on 2026-10-10. It goes out under npm's `beta` dist-tag, as
|
|
1853
|
+
`@ic-reactor/core@beta`; `latest` stays 3.x until 4.0 GA. Changes since
|
|
1854
|
+
4.0.0-beta.3.
|
|
1855
|
+
|
|
1856
|
+
An app on beta.3 upgrades by bumping all three packages to `4.0.0-beta.4`.
|
|
1857
|
+
Nothing in this release breaks code written for beta.3: no export is added or
|
|
1858
|
+
removed (core 13, `@ic-reactor/core/testing` 2, react 4, vite-plugin 2), no
|
|
1859
|
+
error kind or code is removed, no required peer changed, and a module
|
|
1860
|
+
generated for beta.3 need not be regenerated. The one exception is code that
|
|
1861
|
+
writes its own object of the `Client` type, such as a hand-made fake: the type
|
|
1862
|
+
gains `resendOf`, so that object no longer compiles until it has one (a client
|
|
1863
|
+
from `createClient` or `createTestClient` has it). Three things are new, as
|
|
1864
|
+
core's and the Vite plugin's entries below say:
|
|
1865
|
+
|
|
1866
|
+
- `isReactorError(error, canister, method)` narrows a direct call's error to
|
|
1867
|
+
that method's `Err` arm, so `err` is typed after `kind === "canister_err"`
|
|
1868
|
+
without a cast. The one-argument form is unchanged.
|
|
1869
|
+
- `client.resendOf(error, canister, method, { dedupedBy })` offers to send a
|
|
1870
|
+
write again exactly as it was sent, after a failure that left its outcome
|
|
1871
|
+
unknown, and only for a method the app says deduplicates (an ICRC-1
|
|
1872
|
+
transfer with `created_at_time`).
|
|
1873
|
+
- The Vite plugin writes a canister's missing `didFile` from the live
|
|
1874
|
+
canister's certified `candid:service`, when the entry names a `canisterId`.
|
|
1875
|
+
The entry's new `network` field says where the canister is, and
|
|
1876
|
+
`IC_REACTOR_FETCH` fetches a file on disk again.
|
|
1877
|
+
|
|
1878
|
+
Two of these change what some setups see:
|
|
1879
|
+
|
|
1880
|
+
- The Vite plugin lists `@icp-sdk/core` `^6.1.0` as an optional peer. An app
|
|
1881
|
+
on `@ic-reactor/core` has it already, at the range core asks for, and npm
|
|
1882
|
+
installs nothing for an optional peer. A project that installs the plugin
|
|
1883
|
+
next to an `@icp-sdk/core` outside `^6.1.0` gets a peer conflict from its
|
|
1884
|
+
package manager.
|
|
1885
|
+
- A canister entry with a `canisterId` whose `didFile` is not on disk is
|
|
1886
|
+
fetched from `network` (mainnet by default) and the file is written, where
|
|
1887
|
+
beta.3 failed that canister's generation for want of the file. A project
|
|
1888
|
+
that writes such a `.did` in another step (a local canister's build, say)
|
|
1889
|
+
writes it before Vite starts, or gives that entry `network: "local"` so
|
|
1890
|
+
the fetch reads the deployed local canister.
|
|
1891
|
+
|
|
1892
|
+
The guide (`llms.txt`) and the skill now say they apply to 4.0.0-beta.4, use
|
|
1893
|
+
`isReactorError(error, canister, method)` after a direct call, and teach
|
|
1894
|
+
`client.resendOf` as the one safe re-send. The four examples pin
|
|
1895
|
+
`^4.0.0-beta.4`, and `vite-wallet` offers its re-send through
|
|
1896
|
+
`client.resendOf`. The size limits are tighter in CI only, which affects
|
|
1897
|
+
pull requests to this repository, not apps.
|
|
1898
|
+
|
|
1899
|
+
It requires the stable candid-core pair, both pinned exactly, as in beta.3:
|
|
1900
|
+
|
|
1901
|
+
- `@ic-reactor/core`: peer `@candid-core/schema` at exactly `0.3.0`;
|
|
1902
|
+
- `@ic-reactor/vite-plugin`: peer `@candid-core/cli` at exactly `0.2.0`.
|
|
1903
|
+
|
|
1904
|
+
```sh
|
|
1905
|
+
npm install --save-exact @candid-core/schema@0.3.0
|
|
1906
|
+
npm install --save-dev --save-exact @candid-core/cli@0.2.0
|
|
1907
|
+
```
|
|
1908
|
+
|
|
1909
|
+
### @ic-reactor/core
|
|
1910
|
+
|
|
1911
|
+
#### Added
|
|
1912
|
+
|
|
1913
|
+
- `isReactorError(error, canister, method)` narrows a direct call's error to
|
|
1914
|
+
`ReactorError<E>`, where `E` is that method's `Err` arm, so `err` is typed
|
|
1915
|
+
after `kind === "canister_err"` without a cast; for a method without an `Err`
|
|
1916
|
+
arm, `err` stays `undefined` (#849). The guard checks at run time: every
|
|
1917
|
+
error a call rejects with (a direct call, a query function or a mutation
|
|
1918
|
+
function built from the canister) carries the canister object it was made
|
|
1919
|
+
on, under an internal symbol, and the guard is `true` only for an error of
|
|
1920
|
+
that method on that canister object. An error of another method, of another
|
|
1921
|
+
canister object (the `certified: true` one, or the same canister of another
|
|
1922
|
+
client) or of a `client.func` call is `false`, and a `false` result leaves
|
|
1923
|
+
every `kind` possible. The one-argument form is unchanged, including
|
|
1924
|
+
point-free use: `errors.filter(isReactorError)` is still typed
|
|
1925
|
+
`ReactorError<unknown>[]`, because the one-argument signature is listed last.
|
|
1926
|
+
It is an overload of an existing export: core still has 13 names.
|
|
1927
|
+
- `client.resendOf(error, canister, method, { dedupedBy })` offers to send a
|
|
1928
|
+
write again exactly as it was sent, after a failure that left its outcome
|
|
1929
|
+
unknown, and only for a method the app says deduplicates (#850). It returns
|
|
1930
|
+
`undefined` unless `error` is a write of that method on that canister with
|
|
1931
|
+
`mayHaveExecuted: true`, its argument is an object in which `dedupedBy`
|
|
1932
|
+
finds a key (`(arg) => arg.created_at_time` for an ICRC-1 transfer; `null`
|
|
1933
|
+
or `undefined` offers nothing), and the principal that sent it is the
|
|
1934
|
+
caller now: nothing after a sign-out or a sign-in as another account, the
|
|
1935
|
+
offer again once the sender signs back in, and on a view pinned to a
|
|
1936
|
+
principal (hydration) the live caller decides. It also offers nothing once
|
|
1937
|
+
the argument object was changed since it was sent, or once the canister
|
|
1938
|
+
object resolves to another canister id than the write went to (a `{ name }`
|
|
1939
|
+
the `ic_env` cookie maps elsewhere). The offer's `arg` is the attempt's own
|
|
1940
|
+
argument, and goes out only as the same write. `send()` sends the bytes
|
|
1941
|
+
the attempt sent, to the canister id it went to (a management call routed
|
|
1942
|
+
by the effective canister id it went out with). `mutate(arg)`, on any
|
|
1943
|
+
mutation of this client, sends it only to that method and canister id, and
|
|
1944
|
+
only while it encodes to those bytes. Each refusal sends nothing: `cancelled`
|
|
1945
|
+
(`caller_changed`) once someone else is the caller, `cancelled`
|
|
1946
|
+
(`target_changed`) for another method or canister id, and `invalid_args`
|
|
1947
|
+
(`arg_changed`) for an argument changed since. Once offered, that object
|
|
1948
|
+
stays held to that write, so a new write needs a new argument object. The
|
|
1949
|
+
client cannot check that a canister deduplicates: a method without such a
|
|
1950
|
+
key has no safe re-send. It is a method of `Client`, not a new export: core
|
|
1951
|
+
still has 13 names. Core's size limit is raised to 15 kB (14,957 B
|
|
1952
|
+
measured, +508 B for `resendOf`), and the app checks to 90 kB (89,949 B,
|
|
1953
|
+
+465 B) and 91.2 kB (91,155 B, +463 B). React's own code is unchanged.
|
|
1954
|
+
|
|
1955
|
+
### @ic-reactor/react
|
|
1956
|
+
|
|
1957
|
+
- No change but the version. Its peer `@ic-reactor/core` is packed as
|
|
1958
|
+
exactly `4.0.0-beta.4`, so the two move together.
|
|
1959
|
+
|
|
1960
|
+
### @ic-reactor/vite-plugin
|
|
1961
|
+
|
|
1962
|
+
#### Added
|
|
1963
|
+
|
|
1964
|
+
- A canister entry with a `canisterId` has its `didFile` fetched from the live
|
|
1965
|
+
canister when the file is not on disk (#852, phase 1). The plugin reads
|
|
1966
|
+
`candid:service` from certified state with `HttpAgent.readState`, checked
|
|
1967
|
+
against the network's root key, writes it to `didFile`, and generates the
|
|
1968
|
+
module from it; the file is committed with the app. A `didFile` on disk
|
|
1969
|
+
costs no network request, under `vite build` and `vite dev`, and is fetched
|
|
1970
|
+
again only on request: `IC_REACTOR_FETCH=<name>` (names separated by
|
|
1971
|
+
commas, or `all`). The new `network` field says where the canister is:
|
|
1972
|
+
`"ic"` (mainnet's root key, the default), `"local"` (icp-cli's
|
|
1973
|
+
`http://127.0.0.1:8000`, its root key fetched) or `{ host }` (its root key
|
|
1974
|
+
fetched only when the host is local, as `createClient` decides). An
|
|
1975
|
+
unreachable network, a refused read (private metadata), absent metadata, a
|
|
1976
|
+
canister that does not exist, a certificate that does not verify (naming
|
|
1977
|
+
the root key it was checked against) and no answer within 30 seconds each
|
|
1978
|
+
fail with their own message, naming the canister and what to do next, under
|
|
1979
|
+
`failOnError` as generation does. A `didFile` that cannot be written fails
|
|
1980
|
+
only the canisters that name it.
|
|
1981
|
+
No export is added.
|
|
1982
|
+
- `@icp-sdk/core` `^6.1.0` is an optional peer, loaded only for a fetch.
|
|
1983
|
+
Every app on `@ic-reactor/core` has it already.
|
|
1984
|
+
|
|
1985
|
+
### Docs
|
|
1986
|
+
|
|
1987
|
+
- Getting started's "Get a canister's .did" leads with the plugin's
|
|
1988
|
+
`canisterId`, and keeps the script for an app without Vite. The Vite plugin
|
|
1989
|
+
guide has a section on the fetch: when it happens, `network` and its root
|
|
1990
|
+
keys, `IC_REACTOR_FETCH`, and its four failures.
|
|
1991
|
+
- The errors guide shows `isReactorError(error, canister, method)` and what it
|
|
1992
|
+
checks; its duplicate-transfer recipe and the writes guide's direct
|
|
1993
|
+
transfer read the typed `err` instead of casting it. The consumer guide
|
|
1994
|
+
(`llms.txt`) and the skill say to use it after a direct call.
|
|
1995
|
+
- The writes guide has a section, "Sending the same write again", on
|
|
1996
|
+
`client.resendOf` (#850): when a re-send is safe, what `dedupedBy` states,
|
|
1997
|
+
that the offer goes out only as its sender, and the refusals
|
|
1998
|
+
`caller_changed`, `target_changed` and `arg_changed`, each of which leaves
|
|
1999
|
+
the write's outcome unknown. The errors guide lists the two new codes,
|
|
2000
|
+
`target_changed` under `cancelled` and `arg_changed` under `invalid_args`,
|
|
2001
|
+
says not to ignore a refused re-send as another `cancelled`, and its
|
|
2002
|
+
duplicate-transfer recipe offers the re-send with `client.resendOf`. The
|
|
2003
|
+
core reference page shows the three-argument `isReactorError` signature and
|
|
2004
|
+
`resendOf` on `Client`, and the Vite plugin reference page lists the
|
|
2005
|
+
optional `@icp-sdk/core` peer and when it is loaded.
|
|
2006
|
+
- Getting started has a section on keeping the IC stack (the client,
|
|
2007
|
+
`AuthClient` and the generated module) out of an app's first load with
|
|
2008
|
+
`React.lazy`, with no change to the library (#855). Measured on the Vite
|
|
2009
|
+
wallet with Vite 8.3.0, the first load drops from 622.46 kB (193.56 kB
|
|
2010
|
+
gzipped) to 222.30 kB (69.90 kB gzipped), and 401.79 kB (126.21 kB gzipped)
|
|
2011
|
+
moves to chunks loaded asynchronously. It says what a route guard sees meanwhile:
|
|
2012
|
+
`AuthState.status` keeps its four members, with none for "not loaded yet",
|
|
2013
|
+
so a guard lives inside the lazy tree and the `Suspense` fallback stands for
|
|
2014
|
+
"not known yet". It also says that the boundaries for `useSuspenseQuery`
|
|
2015
|
+
reads and lazy routes go below the `ReactorProvider`: with only the entry's
|
|
2016
|
+
boundary above it, a suspending read rebuilds the client on every attempt
|
|
2017
|
+
and never renders. The Auth guide links to it.
|
|
2018
|
+
|
|
2019
|
+
### CI (not published)
|
|
2020
|
+
|
|
2021
|
+
- Core's size limit was set to 14.4 kB gzipped, just above its measured 14,347 B,
|
|
2022
|
+
where it was 50 kB, as react's 1.28 kB already sits at its size. Two new
|
|
2023
|
+
checks measure what an app pays with the peers included, which the
|
|
2024
|
+
packages' own checks never see: `{ createClient }` (89,363 B against
|
|
2025
|
+
89.5 kB) and `createClient` with `ReactorProvider` and `useClient`, React
|
|
2026
|
+
itself left out (90,566 B against 90.7 kB). They run in `pnpm size`, CI's
|
|
2027
|
+
"Check package sizes" step, from `scripts/size-app/`. A pull request that
|
|
2028
|
+
moves a size states the delta and raises its limit deliberately
|
|
2029
|
+
(CONTRIBUTING.md, "Size budget"). Core's config moved from its
|
|
2030
|
+
`package.json` to `packages/core/.size-limit.js`, which is not published.
|
|
2031
|
+
- Core's size limit is raised to 14.5 kB: the guard's canister check adds
|
|
2032
|
+
102 B (14,449 B measured). The app checks measure 89,484 B (+121 B) and
|
|
2033
|
+
90,692 B (+126 B), still under their limits.
|
|
2034
|
+
|
|
2035
|
+
### Examples (not published)
|
|
2036
|
+
|
|
2037
|
+
- The four examples pin `^4.0.0-beta.4`.
|
|
2038
|
+
- `vite-wallet`'s "Send the same transfer again" is `client.resendOf`'s offer,
|
|
2039
|
+
in `SendIcp.tsx` and `SendToContact.tsx`, in place of the attempt and its
|
|
2040
|
+
sender the page tracked by hand. The offer is made only while the account
|
|
2041
|
+
that sent the transfer is the caller, and a new test checks it is withdrawn
|
|
2042
|
+
on a sign-out and made again once the same account signs back in. Its
|
|
2043
|
+
`vite.config.ts` says when the plugin would fetch `ledger.did`: never while
|
|
2044
|
+
the trimmed, committed file is on disk, unless `IC_REACTOR_FETCH=ledger`
|
|
2045
|
+
asks. These edits waited for this release because each example installs
|
|
2046
|
+
the published packages, which carry `resendOf` and the fetch from
|
|
2047
|
+
4.0.0-beta.4 on.
|
|
2048
|
+
|
|
2049
|
+
## core, react, vite-plugin 4.0.0-beta.3
|
|
2050
|
+
|
|
2051
|
+
Prepared on 2026-10-07. It goes out under npm's `beta` dist-tag, as
|
|
2052
|
+
`@ic-reactor/core@beta`; `latest` stays 3.x until 4.0 GA. Changes since
|
|
2053
|
+
4.0.0-beta.2.
|
|
2054
|
+
|
|
2055
|
+
An app on beta.2 upgrades by bumping all three packages to `4.0.0-beta.3`. An
|
|
2056
|
+
app on `network: "local"` against a dfx replica names it instead:
|
|
2057
|
+
`network: { host: "http://127.0.0.1:4943" }`.
|
|
2058
|
+
|
|
2059
|
+
No export is added or removed (core 13, `@ic-reactor/core/testing` 2, react 4,
|
|
2060
|
+
vite-plugin 2), no peer changed, and a module generated for beta.2 need not be
|
|
2061
|
+
regenerated. Two things changed, as core's and the Vite plugin's entries
|
|
2062
|
+
below say:
|
|
2063
|
+
|
|
2064
|
+
- The `auth` factory is handed the client's network, so
|
|
2065
|
+
`auth: (network) => new AuthClient(network)` signs users in on the network
|
|
2066
|
+
the client calls. Code that compiled against beta.2 compiles unchanged,
|
|
2067
|
+
unless it calls a function typed `ClientOptions["auth"]` with no argument,
|
|
2068
|
+
uses one where a function of no argument is expected, or passes as `auth` a
|
|
2069
|
+
factory whose optional first parameter is not the network.
|
|
2070
|
+
- **BREAKING:** `network: "local"` is icp-cli's local network,
|
|
2071
|
+
`http://127.0.0.1:8000`, not dfx's `http://127.0.0.1:4943`. The test
|
|
2072
|
+
client's default host (where its fake replica answers) and the Vite
|
|
2073
|
+
plugin's fallback `/api` proxy moved with it. `"env"` on a server with
|
|
2074
|
+
dfx's `DFX_NETWORK=local` and no `ICP_NETWORK` keeps 4943.
|
|
2075
|
+
|
|
2076
|
+
The guide (`llms.txt`) and the skill now say they apply to 4.0.0-beta.3, and
|
|
2077
|
+
the guide teaches the factory's argument. The four examples pin
|
|
2078
|
+
`^4.0.0-beta.3`, and the two that sign in (`next-ssr`, `vite-wallet`) pass
|
|
2079
|
+
the network to `AuthClient`.
|
|
2080
|
+
|
|
2081
|
+
It requires the stable candid-core pair, both pinned exactly, as in beta.2:
|
|
2082
|
+
|
|
2083
|
+
- `@ic-reactor/core`: peer `@candid-core/schema` at exactly `0.3.0`;
|
|
2084
|
+
- `@ic-reactor/vite-plugin`: peer `@candid-core/cli` at exactly `0.2.0`.
|
|
2085
|
+
|
|
2086
|
+
```sh
|
|
2087
|
+
npm install --save-exact @candid-core/schema@0.3.0
|
|
2088
|
+
npm install --save-dev --save-exact @candid-core/cli@0.2.0
|
|
2089
|
+
```
|
|
2090
|
+
|
|
2091
|
+
### @ic-reactor/core
|
|
2092
|
+
|
|
2093
|
+
#### Added
|
|
2094
|
+
|
|
2095
|
+
- The `auth` factory of `createClient` is called with one argument, the
|
|
2096
|
+
client's network, under `@icp-sdk/auth` 10's own option names, so
|
|
2097
|
+
`auth: (network) => new AuthClient(network)` signs users in on the network
|
|
2098
|
+
the client calls (#790). `() => new AuthClient()` and every other factory
|
|
2099
|
+
that takes no argument work as before, which for `AuthClient` means on
|
|
2100
|
+
mainnet only, since such a factory ignores the network. No export is added:
|
|
2101
|
+
the argument's type is written inline in `ClientOptions`.
|
|
2102
|
+
- `network.agentOptions` is the host, root key and `shouldFetchRootKey` of
|
|
2103
|
+
the client's own agents, and the client's `fetch` when one was given,
|
|
2104
|
+
which `AuthClient` makes its mint and revoke calls with.
|
|
2105
|
+
- `network.identityProvider` is, in order: the `ic_env` cookie's
|
|
2106
|
+
`INTERNET_IDENTITY_PROVIDER` where the client trusts the cookie, with the
|
|
2107
|
+
cookie's `PUBLIC_CANISTER_ID:internet_identity` or
|
|
2108
|
+
`rdmx6-jaaaa-aaaaa-aaadq-cai`; for `"local"` or a network object whose
|
|
2109
|
+
host is local, icp-cli's built-in Internet Identity,
|
|
2110
|
+
`http://id.ai.localhost:<port of the host>/authorize` with
|
|
2111
|
+
`rdmx6-jaaaa-aaaaa-aaadq-cai`; and otherwise absent, which `AuthClient`
|
|
2112
|
+
reads as mainnet's Internet Identity.
|
|
2113
|
+
- A replica that is not mainnet rejects every delegation mainnet's Internet
|
|
2114
|
+
Identity mints. Where no provider can be named on a network that does not
|
|
2115
|
+
verify against mainnet's root key (a trusted cookie that names an
|
|
2116
|
+
`internet_identity` canister but no URL, `"env"` on a local page whose
|
|
2117
|
+
cookie names no provider, or a replica with a root key of its own, given
|
|
2118
|
+
or fetched), the client warns once, in development, when the factory
|
|
2119
|
+
reads `network.identityProvider`, as `new AuthClient(network)` does, that
|
|
2120
|
+
sign-in cannot work there, with the line that names a provider:
|
|
2121
|
+
`(network) => new AuthClient({ ...network, identityProvider: { authorizeUrl, canisterId } })`.
|
|
2122
|
+
A factory that names its own provider, as that line does, or builds an
|
|
2123
|
+
auth that is not Internet Identity never sees it, and neither does a
|
|
2124
|
+
spread that adds other options and names no provider, such as
|
|
2125
|
+
`{ ...network, derivationOrigin }`, which must name one there. `"ic"` and
|
|
2126
|
+
`"env"` on a mainnet page never warn, and neither does the test client of
|
|
2127
|
+
`@ic-reactor/core/testing`, whose factory ignores the network.
|
|
2128
|
+
|
|
2129
|
+
Code that compiled against 4.0.0-beta.2 and stops compiling: a call of a
|
|
2130
|
+
function typed `ClientOptions["auth"]` with no argument, or its use as a
|
|
2131
|
+
`() => AuthLike`. Pass it the network, or type the function
|
|
2132
|
+
`() => AuthLike`. A factory whose optional first parameter is not the
|
|
2133
|
+
network, such as `(x?: string) => ...`, stops compiling too.
|
|
2134
|
+
|
|
2135
|
+
#### Changed
|
|
2136
|
+
|
|
2137
|
+
- **BREAKING:** `network: "local"` is icp-cli's local network,
|
|
2138
|
+
`http://127.0.0.1:8000`, rather than dfx's replica on
|
|
2139
|
+
`http://127.0.0.1:4943` (#790). 8000 is icp-cli's default gateway port
|
|
2140
|
+
([`gateway.port`](https://github.com/dfinity/icp-cli/blob/ffcb2235515bc5623e6c98ba0b9d24a303aaac36/docs/reference/configuration.md#L294),
|
|
2141
|
+
[the `local` network](https://github.com/dfinity/icp-cli/blob/ffcb2235515bc5623e6c98ba0b9d24a303aaac36/docs/reference/configuration.md#L513),
|
|
2142
|
+
[environments](https://github.com/dfinity/icp-cli/blob/ffcb2235515bc5623e6c98ba0b9d24a303aaac36/docs/concepts/environments.md#L73)).
|
|
2143
|
+
For a dfx replica, name it: `network: { host: "http://127.0.0.1:4943" }`.
|
|
2144
|
+
The same holds for:
|
|
2145
|
+
- `"env"` on a server with `ICP_NETWORK=local` and no `ICP_HOST` or
|
|
2146
|
+
`IC_HOST`, which now falls back to `http://127.0.0.1:8000`. With dfx's
|
|
2147
|
+
`DFX_NETWORK=local` and no `ICP_NETWORK` (which overrides it), the
|
|
2148
|
+
fallback stays dfx's `http://127.0.0.1:4943`, since that variable says
|
|
2149
|
+
the project runs dfx.
|
|
2150
|
+
- The Internet Identity the `auth` factory is handed for `"local"`, which
|
|
2151
|
+
is `http://id.ai.localhost:8000/authorize`, the Vite plugin's default.
|
|
2152
|
+
- `createTestClient` of `@ic-reactor/core/testing`, whose default host is
|
|
2153
|
+
`"local"`'s. With no `network`, outside a page, it now answers at
|
|
2154
|
+
`http://127.0.0.1:8000`, and its query keys carry that host as their
|
|
2155
|
+
network segment. A test that asserted `http://127.0.0.1:4943` there
|
|
2156
|
+
passes `network: { host: "http://127.0.0.1:4943" }` to keep it.
|
|
2157
|
+
|
|
2158
|
+
### @ic-reactor/react
|
|
2159
|
+
|
|
2160
|
+
#### Documentation
|
|
2161
|
+
|
|
2162
|
+
- The README and `ReactorProviderProps`' doc comments build the client with
|
|
2163
|
+
`auth: (network) => new AuthClient(network)`. The code is unchanged.
|
|
2164
|
+
|
|
2165
|
+
### @ic-reactor/vite-plugin
|
|
2166
|
+
|
|
2167
|
+
#### Changed
|
|
2168
|
+
|
|
2169
|
+
- **BREAKING:** When detection fails, `/api` goes to icp-cli's local network,
|
|
2170
|
+
`http://127.0.0.1:8000`, rather than `http://127.0.0.1:4943` (#790), as
|
|
2171
|
+
`"local"` does in core and as the cookie's fallback Internet Identity,
|
|
2172
|
+
`http://id.ai.localhost:8000/authorize`, already did. A project on a dfx
|
|
2173
|
+
replica proxies `/api` itself (`server.proxy["/api"]`), which the plugin
|
|
2174
|
+
leaves in place.
|
|
2175
|
+
|
|
2176
|
+
### Docs
|
|
2177
|
+
|
|
2178
|
+
- Every `auth` factory in the docs, the guide (`llms.txt`) and the READMEs
|
|
2179
|
+
is `(network) => new AuthClient(network)`. The Auth guide's 25-line local
|
|
2180
|
+
Internet Identity setup is that one line, with the rules above and the
|
|
2181
|
+
override for a project that deploys its own `internet_identity`.
|
|
2182
|
+
- Getting started says where `icrc1.did` comes from, runs the first read
|
|
2183
|
+
with `npx tsx`, says to change the default `App` import of Vite's
|
|
2184
|
+
template, builds the transfer's argument in place, says to commit the
|
|
2185
|
+
`.did` and the generated module (Vite's `react-ts` template type-checks
|
|
2186
|
+
before the plugin generates), and names `injectEnvironment: false` for an
|
|
2187
|
+
app that only talks to mainnet. A new section, "Get a canister's .did",
|
|
2188
|
+
adds a script that saves a live canister's `.did` from certified state,
|
|
2189
|
+
with the `icp canister metadata` command that does the same.
|
|
2190
|
+
|
|
2191
|
+
### Examples (not published)
|
|
2192
|
+
|
|
2193
|
+
- The four examples pin `^4.0.0-beta.3`. The two that sign in pass the
|
|
2194
|
+
network to `AuthClient`: `next-ssr`'s provider and `vite-wallet`'s client
|
|
2195
|
+
build it with
|
|
2196
|
+
`(network) => new AuthClient(network)`. `vite-wallet`'s hand-built local
|
|
2197
|
+
Internet Identity (`src/auth/internet-identity.ts` and its test) is gone: on
|
|
2198
|
+
a local page the client hands its factory the cookie's provider and the
|
|
2199
|
+
local network, and on a deployed page no provider, which `AuthClient` reads
|
|
2200
|
+
as mainnet's. Its `isLocalPage` tests, which decide where the dev account is
|
|
2201
|
+
offered, moved to `src/auth/local-page.test.ts`. These edits waited for this
|
|
2202
|
+
release because each example installs the published packages, which carry
|
|
2203
|
+
the factory's argument from 4.0.0-beta.3 on.
|
|
2204
|
+
- `node-agent-tool`'s network comment says `local` is icp-cli's
|
|
2205
|
+
`http://127.0.0.1:8000`.
|
|
2206
|
+
|
|
2207
|
+
## core, react, vite-plugin 4.0.0-beta.2
|
|
2208
|
+
|
|
2209
|
+
Prepared on 2026-10-06. It goes out under npm's `beta` dist-tag, as
|
|
2210
|
+
`@ic-reactor/core@beta`; `latest` stays 3.x until 4.0 GA. Changes since
|
|
2211
|
+
4.0.0-beta.1.
|
|
2212
|
+
|
|
2213
|
+
An app on beta.1 upgrades by bumping all three packages to `4.0.0-beta.2`, and
|
|
2214
|
+
the candid-core pair to its stable releases, both exact:
|
|
2215
|
+
|
|
2216
|
+
```sh
|
|
2217
|
+
npm install --save-exact @candid-core/schema@0.3.0
|
|
2218
|
+
npm install --save-dev --save-exact @candid-core/cli@0.2.0
|
|
2219
|
+
```
|
|
2220
|
+
|
|
2221
|
+
No export is added or removed (core 13, `@ic-reactor/core/testing` 2, react 4,
|
|
2222
|
+
vite-plugin 2), and a module generated for beta.1 need not be regenerated.
|
|
2223
|
+
Code that compiled against beta.1 compiles unchanged, unless it does one of
|
|
2224
|
+
the four things core's Changed entry on `queryOptions` lists, or switches
|
|
2225
|
+
exhaustively over candid-core's `ContractIssueCode`. What is new is
|
|
2226
|
+
one error code a missing canister now carries, an aimed form of the test
|
|
2227
|
+
client's `refuseNext`, and read options that TanStack Query's suspense hooks
|
|
2228
|
+
take without a cast.
|
|
2229
|
+
|
|
2230
|
+
The 4 docs are at https://ic-reactor.b3pay.net/v4/: getting started, guides to
|
|
2231
|
+
the client, reads, writes, errors, auth, SSR, testing, values and the Vite
|
|
2232
|
+
plugin, a page per package, and "Migrating from 3.x" with the table of every
|
|
2233
|
+
removed 3.x name and what replaces it (#789). Each package's `homepage` now
|
|
2234
|
+
points at its page there.
|
|
2235
|
+
|
|
2236
|
+
### @ic-reactor/core
|
|
2237
|
+
|
|
2238
|
+
#### Added
|
|
2239
|
+
|
|
2240
|
+
- `ReactorError.code` is `"canister_not_found"` when the IC answers that the
|
|
2241
|
+
canister a call was routed to does not exist (#821), on a read or a write.
|
|
2242
|
+
The IC says it in two ways, and both get the code: a boundary node's HTTP
|
|
2243
|
+
400 whose body's error line is `canister_not_found`, for a canister id
|
|
2244
|
+
outside every subnet's range, and a replica's reject code 3 with the IC
|
|
2245
|
+
error code `IC0301`, for an id inside a subnet's range that holds no
|
|
2246
|
+
canister. Mainnet answers both ways, and the fake replica of
|
|
2247
|
+
`@ic-reactor/core/testing` answers the second. `kind` and `mayHaveExecuted`
|
|
2248
|
+
are unchanged from the classification table. Any other HTTP 400 and any
|
|
2249
|
+
other reject still has no `code`.
|
|
2250
|
+
- `refuseNext(status, { method?, canister?, times? })` on the object
|
|
2251
|
+
`createTestClient()` returns (`@ic-reactor/core/testing`): a refusal aimed
|
|
2252
|
+
at the queries and calls that name `method` and are addressed to
|
|
2253
|
+
`canister`, each when it is given, so a test can throttle `icrc1_transfer`
|
|
2254
|
+
while the reads around it are answered (#822). A query and a replicated
|
|
2255
|
+
call of the same method both match; `canister` is the canister a request
|
|
2256
|
+
names, and need not be mocked; `times` (default 1) counts matching
|
|
2257
|
+
requests, each send of a call the client re-sends included. The status and
|
|
2258
|
+
`read_state` requests an agent makes on its own never match. A request that
|
|
2259
|
+
several refusals match is refused by the one armed first. An option it does
|
|
2260
|
+
not have, an empty `method` or a `canister` that is not a canister id
|
|
2261
|
+
throws a `TypeError`. `refuseNext(status, times?)` is unchanged: it refuses
|
|
2262
|
+
the next `times` queries or calls, whatever they are for. No export is
|
|
2263
|
+
added.
|
|
2264
|
+
|
|
2265
|
+
#### Changed
|
|
2266
|
+
|
|
2267
|
+
- `client.queryOptions(...)` built from variables that cannot be `skipToken`
|
|
2268
|
+
(a method without arguments called without them or with `undefined`, or
|
|
2269
|
+
variables of a type `skipToken` is not assignable to) returns options whose
|
|
2270
|
+
`queryFn` type excludes `SkipToken`, so TanStack Query's `useSuspenseQuery`
|
|
2271
|
+
and `useSuspenseQueries` take them without a cast (#828). Variables typed
|
|
2272
|
+
`V | SkipToken`, or `skipToken` itself, keep `SkipToken` in `queryFn`'s
|
|
2273
|
+
type, and the suspense hooks still refuse them. `queryOptions` has a second
|
|
2274
|
+
signature for that case, and `CanisterQueryOptions` a third type parameter
|
|
2275
|
+
that defaults to today's type. The options' `retry` also takes an error
|
|
2276
|
+
typed `unknown`, as `useQueries` and `useSuspenseQueries` type it, so those
|
|
2277
|
+
hooks take the options too; `useQuery` and a `QueryObserver` still type the
|
|
2278
|
+
query's error `ReactorError<E>`. Variables of a type `skipToken` is
|
|
2279
|
+
assignable to cannot be told from it: the options of such a method keep
|
|
2280
|
+
`SkipToken` whatever the variables: read it with `useQuery`, or cast its
|
|
2281
|
+
`queryFn` for a suspense read. Of what `candid-core-cli gen` writes, that is only a method whose one
|
|
2282
|
+
argument is Candid `reserved` (typed `unknown`): it writes `record {}` as
|
|
2283
|
+
`Record<string, never>`, which a suspense read takes. A type written by
|
|
2284
|
+
hand that `skipToken` fits, such as `{}`, is another. Types only: nothing
|
|
2285
|
+
changes at run time, and no export is added. Code that compiled against
|
|
2286
|
+
4.0.0-beta.1 and stops compiling:
|
|
2287
|
+
- `queryFn === skipToken` on options built from variables that cannot be
|
|
2288
|
+
`skipToken` is now TS2367 ("no overlap"). Such a check was always false,
|
|
2289
|
+
so remove it.
|
|
2290
|
+
- A function typed `Client["queryOptions"]` (one function cannot satisfy
|
|
2291
|
+
both signatures), and a call that spreads
|
|
2292
|
+
`Parameters<Client["queryOptions"]>` back into `client.queryOptions`
|
|
2293
|
+
(`Parameters` takes the last signature only). Call `client.queryOptions`
|
|
2294
|
+
with the canister and method, and spread its result to add options:
|
|
2295
|
+
`{ ...options, staleTime: 1 }`. `ReturnType<Client["queryOptions"]>`
|
|
2296
|
+
still compiles.
|
|
2297
|
+
- A `retry` written into the options' type with its error parameter
|
|
2298
|
+
annotated, such as `retry: (n: number, e: ReactorError<E>) => ...` in a
|
|
2299
|
+
value typed by the options: the options' `retry` must now also take an
|
|
2300
|
+
error typed `unknown`. Annotate it `unknown`, or leave the parameter to
|
|
2301
|
+
the context. Passing such a `retry` to `useQuery({ ...options, retry })`
|
|
2302
|
+
still compiles.
|
|
2303
|
+
- A variable inferred from the options of a read that cannot be skipped,
|
|
2304
|
+
given the options of one that can: `let o = client.queryOptions(c, m, v)`
|
|
2305
|
+
then `o = client.queryOptions(c, m, skipToken)` is now TS2322, and so is
|
|
2306
|
+
the same through a `useState` setter, a `push` onto an inferred array or
|
|
2307
|
+
a parameter typed `typeof o`. The two options are different types now.
|
|
2308
|
+
Build both in one expression,
|
|
2309
|
+
`ready ? client.queryOptions(c, m, v) : client.queryOptions(c, m, skipToken)`,
|
|
2310
|
+
whose union `useQuery` takes, or type the variables `V | SkipToken` so
|
|
2311
|
+
that both reads give the options that keep `SkipToken`.
|
|
2312
|
+
|
|
2313
|
+
Wrong variables or an unknown method name are now reported as TS2769 ("No
|
|
2314
|
+
overload matches this call", listing both signatures), where they were
|
|
2315
|
+
TS2345.
|
|
2316
|
+
|
|
2317
|
+
- Peer dependency: `@candid-core/schema` at exactly `0.3.0`, the stable
|
|
2318
|
+
release of the 0.3 line, where beta.1 pinned `0.3.0-beta.1`. The other peers
|
|
2319
|
+
are unchanged. Migration: `npm install --save-exact @candid-core/schema@0.3.0`,
|
|
2320
|
+
with the plugin's `@candid-core/cli@0.2.0` below. What an app meets, from
|
|
2321
|
+
candid-core's [0.3.0 release notes](https://github.com/b3hr4d/candid-core/blob/acabe0fcfbd8019d14e2c2550c5e6cd872a3529a/.github/release-notes/npm/schema/0.3.0.md):
|
|
2322
|
+
- A `rec` hop is no longer charged against the decode limits, so the
|
|
2323
|
+
client's `maxDepth` (default 256) counts a reply's Candid nesting alone. A
|
|
2324
|
+
recursive reply, such as an ICRC-3 block's `Value`, may nest deeper at the
|
|
2325
|
+
same `maxDepth` than before; through a `schemaFromContract()` actor, about
|
|
2326
|
+
128 levels used to reach the default, and now 256 do.
|
|
2327
|
+
- Replies refused that were accepted, only at a limit: the `null` a reply
|
|
2328
|
+
gets for a field its message omits (`opt`, `null` or `reserved`) is now
|
|
2329
|
+
charged one depth level, at the field's own level, and one element. So
|
|
2330
|
+
such a field at exactly the client's `maxDepth` rejects `invalid_reply`
|
|
2331
|
+
with a `value_depth` issue, and a large reply whose records omit such
|
|
2332
|
+
fields can pass the decoder's 1,000,000-element bound, which the client
|
|
2333
|
+
does not change: 500,000 records that each omit one field now cost
|
|
2334
|
+
1,000,001 elements and reject `invalid_reply` with a `value_elements`
|
|
2335
|
+
issue. A test client checks its mocks' replies at its `maxDepth` too,
|
|
2336
|
+
where a tag-only variant (`{ tag }`) at exactly `maxDepth` is now
|
|
2337
|
+
refused. At the default `maxDepth`, only a reply 256 levels deep reaches
|
|
2338
|
+
the depth refusals.
|
|
2339
|
+
- `schemaFromContract()` refuses a Contract document with a key the format
|
|
2340
|
+
does not define, a type node nothing reaches, or type nodes and no root,
|
|
2341
|
+
as candid-core's own loader does. Every document `candid-core compile`
|
|
2342
|
+
writes still loads. Its `ContractIssueCode` gains `unknown_key`,
|
|
2343
|
+
`orphan_type_node` and `rootless_type_arena`.
|
|
2344
|
+
|
|
2345
|
+
#### Documentation
|
|
2346
|
+
|
|
2347
|
+
- The guide (`llms.txt`) said the `invalidates` option of
|
|
2348
|
+
`client.mutationOptions()` names other reads. The list replaces the
|
|
2349
|
+
default (every read of the canister written to): it now says so, and to
|
|
2350
|
+
name the written canister too if its reads change (#829). The code is
|
|
2351
|
+
unchanged.
|
|
2352
|
+
|
|
2353
|
+
### @ic-reactor/react
|
|
2354
|
+
|
|
2355
|
+
#### Documentation
|
|
2356
|
+
|
|
2357
|
+
- The README, `llms.txt` and `useClient()`'s doc comment told an app to
|
|
2358
|
+
ignore kind `"cancelled"` (code `"caller_changed"`) in a `QueryCache`
|
|
2359
|
+
`onError`, which no app can set: the client owns its `QueryClient` and
|
|
2360
|
+
takes no `QueryCache`. They now name the route an app has, a listener of
|
|
2361
|
+
`client.queryClient.getQueryCache().subscribe()`, whose `"updated"` event
|
|
2362
|
+
with `action.type` `"error"` carries the cancellation (#829). The code is
|
|
2363
|
+
unchanged.
|
|
2364
|
+
|
|
2365
|
+
### @ic-reactor/vite-plugin
|
|
2366
|
+
|
|
2367
|
+
CI now runs its typecheck and tests at the floor of each Vite major its `vite`
|
|
2368
|
+
peer range accepts, 4.2.0, 5.0.0, 6.0.0, 7.0.0 and 8.0.0, where before it ran
|
|
2369
|
+
on the newest Vite only (#823). The range is unchanged.
|
|
2370
|
+
|
|
2371
|
+
#### Changed
|
|
2372
|
+
|
|
2373
|
+
- Peer dependency: `@candid-core/cli` at exactly `0.2.0`, the stable generator
|
|
2374
|
+
that pairs with `@candid-core/schema` `0.3.0`, where beta.1 pinned
|
|
2375
|
+
`0.2.0-beta.1`. The plugin's install hints name it; nothing else in the
|
|
2376
|
+
plugin changed. Migration:
|
|
2377
|
+
`npm install --save-dev --save-exact @candid-core/cli@0.2.0`. It writes the
|
|
2378
|
+
same module as 0.2.0-beta.1 for every `.did` both accept, and refuses two
|
|
2379
|
+
that 0.2.0-beta.1 did not: a type on a cycle through `opt` alone
|
|
2380
|
+
(`type T = opt T;`, `did_type_check_error`) and a run of more than 256
|
|
2381
|
+
consecutive comments (`resource_limit_exceeded`). The plugin reports either
|
|
2382
|
+
as that canister's generation failure. See candid-core's
|
|
2383
|
+
[0.2.0 release notes](https://github.com/b3hr4d/candid-core/blob/acabe0fcfbd8019d14e2c2550c5e6cd872a3529a/.github/release-notes/npm/cli/0.2.0.md).
|
|
2384
|
+
|
|
2385
|
+
### Examples, e2e and evals (not published)
|
|
2386
|
+
|
|
2387
|
+
- The four examples pin `^4.0.0-beta.2`, and three use what it adds:
|
|
2388
|
+
`node-agent-tool`'s mock ledger throttles exactly `icrc1_transfer` with the
|
|
2389
|
+
aimed `refuseNext` instead of counting the reads before it, the
|
|
2390
|
+
`icrc-ledger` sandbox's HTTP 429 faults hit only the transfer, and
|
|
2391
|
+
`next-ssr`'s balance route answers a missing canister with a 502 that
|
|
2392
|
+
carries `code: "canister_not_found"` (#821, #822).
|
|
2393
|
+
- A new script and workflow run each example the way someone who copies it
|
|
2394
|
+
out of the repository does, and as StackBlitz does: a plain `npm install`
|
|
2395
|
+
of the `@ic-reactor/*` beta published on npm, then its typecheck, tests,
|
|
2396
|
+
build and StackBlitz start command. The release workflow runs it once the
|
|
2397
|
+
packages are published (#788). Each example has a `.stackblitzrc`, and
|
|
2398
|
+
`jsdom` is pinned to `30.0.1`.
|
|
2399
|
+
- `vite-wallet`'s and `icrc-ledger`'s transfers carry a memo of each
|
|
2400
|
+
transfer's own, kept for its re-send, so two transfers of the same amount
|
|
2401
|
+
in the same millisecond are never taken for each other's `Duplicate`; their
|
|
2402
|
+
test ledgers deduplicate on the whole argument, memo included (#833, #838).
|
|
2403
|
+
- The e2e suite is rewritten for 4: twelve cases on an icp-cli 1.2.0 local
|
|
2404
|
+
network, over a `hello_actor` module generated by `candid-core-cli gen`
|
|
2405
|
+
(#787).
|
|
2406
|
+
- The examples and the e2e suite pin `@candid-core/schema` `0.3.0` and
|
|
2407
|
+
`@candid-core/cli` `0.2.0`. Every module the examples, core's tests and the e2e
|
|
2408
|
+
suite generate is byte-identical under 0.2.0.
|
|
2409
|
+
- The eval harness can build the `v4` condition from the published
|
|
2410
|
+
4.0.0-beta.1 tarballs, and the GA eval (Addendum 4 of
|
|
2411
|
+
`evals/PREREGISTRATION.md`) ran on the published 4.0.0-beta.1 (#790). GA
|
|
2412
|
+
ships on the result of Addendum 4, stated as recorded: rule 1 was not met as
|
|
2413
|
+
written, because the leak audit flagged react-wallet/`v4`#14's read of its
|
|
2414
|
+
own persisted tool output; all 80 runs were safe, and the pooled bound held
|
|
2415
|
+
in both analyses (−0.0897 main, −0.0876 intent-to-treat, against −0.10).
|
|
2416
|
+
|
|
2417
|
+
## core, react, vite-plugin 4.0.0-beta.1
|
|
2418
|
+
|
|
2419
|
+
The first prerelease of ic-reactor 4 (milestone 1, #790), published under
|
|
2420
|
+
npm's `beta` dist-tag: install it as `@ic-reactor/core@beta`. `latest` stays
|
|
2421
|
+
3.x until 4.0 GA. Changes since core and react 3.13.0 and vite-plugin 0.15.1.
|
|
2422
|
+
|
|
2423
|
+
ic-reactor 4 is a breaking rewrite, not an upgrade of 3.x. It is a thin layer
|
|
2424
|
+
over the module `candid-core-cli gen` writes from a `.did` file, plus one
|
|
2425
|
+
guide, `node_modules/@ic-reactor/core/llms.txt`, shipped in core's tarball.
|
|
2426
|
+
An app calls canisters through a client from `createClient` and reads and
|
|
2427
|
+
writes them with TanStack Query's own hooks and the options the client
|
|
2428
|
+
builds. There are no reactors, hook factories or generated hooks. The three
|
|
2429
|
+
packages release together, at one version.
|
|
2430
|
+
|
|
2431
|
+
3.x stays on `main` (security fixes only until 4.0 GA) and is documented at
|
|
2432
|
+
https://ic-reactor.b3pay.net/v3/. Migration: there is no in-place upgrade;
|
|
2433
|
+
generate each canister's module with `candid-core-cli gen` and port the calls
|
|
2434
|
+
to the client by the guide. The table of every removed name and what replaces
|
|
2435
|
+
it comes with the 4 docs (#789).
|
|
2436
|
+
|
|
2437
|
+
### @ic-reactor/core
|
|
2438
|
+
|
|
2439
|
+
#### Added
|
|
2440
|
+
|
|
2441
|
+
- `createClient(options)`: one client per browser tab, or per request on a
|
|
2442
|
+
server. It takes a `network` and exactly one caller: `identity` (an
|
|
2443
|
+
`Identity`, or `"anonymous"` for a read-only client) or `auth` (a factory
|
|
2444
|
+
of an `AuthLike`, such as `() => new AuthClient()` from `@icp-sdk/auth` 10,
|
|
2445
|
+
called once and only in a browser). `allowEnvConfig`, `fetch` and
|
|
2446
|
+
`maxDepth` (default 256) are optional. The client owns a TanStack
|
|
2447
|
+
`QueryClient` (`client.queryClient`) that dehydrates and hydrates `bigint`,
|
|
2448
|
+
`Uint8Array` and non-finite floats without loss.
|
|
2449
|
+
- `network`: `"ic"` (mainnet and its built-in root key), `"local"`
|
|
2450
|
+
(`http://127.0.0.1:4943`, root key fetched), `"env"` (the page's network,
|
|
2451
|
+
with canisters named `{ name }` through the `ic_env` cookie), or
|
|
2452
|
+
`{ host, rootKey?, name?, fetchRootKey? }`. A given `rootKey` is used and
|
|
2453
|
+
never fetched; without one, a root key is fetched only from a loopback or
|
|
2454
|
+
`localhost` host, unless `fetchRootKey: true` is written out.
|
|
2455
|
+
- `client.canister<Actor>(actor, { id } | { name })`: a frozen object of plain
|
|
2456
|
+
async methods, one per Candid method, typed from the generated `Actor` and
|
|
2457
|
+
memoized, so it can be called in render. A method whose result is
|
|
2458
|
+
`variant { Ok; Err }` resolves with the `Ok` value and rejects the `Err` as
|
|
2459
|
+
kind `canister_err`. `{ id, certified: true }` sends `query` methods through
|
|
2460
|
+
the certified path. A call to the management canister (`aaaaa-aa`) takes
|
|
2461
|
+
its effective canister id from its arguments.
|
|
2462
|
+
- `client.queryOptions(canister, method, args | skipToken)`,
|
|
2463
|
+
`client.mutationOptions(canister, method, { invalidates? })` and
|
|
2464
|
+
`client.queryKey(canister, method?, args?)`, the options and keys for
|
|
2465
|
+
TanStack Query. `queryOptions` throws for an update method unless its
|
|
2466
|
+
fourth argument is `{ update: "idempotent" }`.
|
|
2467
|
+
- `client.func<F>(funcSchema, ref)` calls a func reference a reply carried,
|
|
2468
|
+
such as an ICRC ledger's archive callback.
|
|
2469
|
+
- `client.caller()`, `client.authState()`, `client.subscribe(fn)`,
|
|
2470
|
+
`client.signIn()`, `client.signOut()` and `client.dispose()`. Every call
|
|
2471
|
+
made after `dispose()`, writes included, rejects `cancelled` with code
|
|
2472
|
+
`client_disposed` and sends nothing (#818).
|
|
2473
|
+
- `isReactorError(error)` and the types `ReactorError` and
|
|
2474
|
+
`ReactorErrorKind`. Every rejection of a client call is a `ReactorError`
|
|
2475
|
+
with one of eight kinds (`invalid_args`, `unauthenticated`,
|
|
2476
|
+
`not_delivered`, `outcome_unknown`, `rejected`, `invalid_reply`,
|
|
2477
|
+
`canister_err`, `cancelled`) and a `mayHaveExecuted` boolean, which a
|
|
2478
|
+
classifier sets from the reject code, the HTTP status and whether the call
|
|
2479
|
+
was an update. On the management canister, reject codes 1 to 3 do not prove
|
|
2480
|
+
that a write had no effect.
|
|
2481
|
+
- `parseUnits(text, decimals, { signed? })` and
|
|
2482
|
+
`formatUnits(value, decimals, { maxFractionDigits?, minFractionDigits? })`
|
|
2483
|
+
convert token amounts exactly, in `bigint`. `parseUnits` throws for text
|
|
2484
|
+
that is not a plain decimal, for a negative unless `signed`, and for excess
|
|
2485
|
+
fraction digits.
|
|
2486
|
+
- The types `Client`, `ClientOptions`, `Network`, `AuthLike`, `AuthState`,
|
|
2487
|
+
`Canister` and `CanisterTarget`. With the names above, the entry exports 13.
|
|
2488
|
+
- `@ic-reactor/core/testing` exports `createTestClient` and `TestHandlers`: a
|
|
2489
|
+
real client over an in-memory replica that signs and verifies as a replica
|
|
2490
|
+
does, passed in through the client's `fetch` (nothing global is stubbed),
|
|
2491
|
+
with canisters written as functions of Candid values and a sign-in the test
|
|
2492
|
+
controls.
|
|
2493
|
+
|
|
2494
|
+
#### Changed
|
|
2495
|
+
|
|
2496
|
+
- The client keeps one immutable agent per principal and never replaces an
|
|
2497
|
+
agent's identity. A call goes out as the principal current when it was
|
|
2498
|
+
made. A read made for one caller that settles after a sign-in, sign-out or
|
|
2499
|
+
switch rejects `cancelled` instead of landing in the new caller's cache.
|
|
2500
|
+
- Query keys name the caller:
|
|
2501
|
+
`["ic-reactor", network, caller, canisterId, method, args]`, so one
|
|
2502
|
+
principal's cached answer is never served to another. Build keys with
|
|
2503
|
+
`client.queryKey`, never by hand.
|
|
2504
|
+
- An update is sent again only on proof that the canister never received it
|
|
2505
|
+
(reject code 2 or HTTP 429), at most twice, inside the call; any other
|
|
2506
|
+
failure is final. A read retries at most 3 times, only a `not_delivered`
|
|
2507
|
+
failure, and never on a server.
|
|
2508
|
+
- A mutation is never retried: `mutationOptions` returns `retry: false`. By
|
|
2509
|
+
default it invalidates its canister's reads, for every caller, after a
|
|
2510
|
+
success, a `canister_err`, or a failure that may have executed.
|
|
2511
|
+
- A write is refused before it is sent (`unauthenticated`) while nobody is
|
|
2512
|
+
signed in, and always on a client built with `identity: "anonymous"`.
|
|
2513
|
+
- Values are candid-core's: a principal is branded text (`principal()` and
|
|
2514
|
+
`isPrincipal()` from `@candid-core/schema`), `opt T` is `T | null`, a
|
|
2515
|
+
variant is `{ tag, value }` and every `blob` is a `Uint8Array`.
|
|
2516
|
+
- Peer dependencies: `@candid-core/schema` at exactly `0.3.0-beta.1` (new:
|
|
2517
|
+
install it with `--save-exact`), `@tanstack/query-core` `^5.62.0` (no longer
|
|
2518
|
+
optional), `@icp-sdk/core` `^6.1.0`, and `@noble/curves` `^2.2.0`, still
|
|
2519
|
+
optional and used only by `./testing`. `zod` is no longer a dependency.
|
|
2520
|
+
Migration: `npm install --save-exact @candid-core/schema@0.3.0-beta.1`.
|
|
2521
|
+
|
|
2522
|
+
#### Removed
|
|
2523
|
+
|
|
2524
|
+
- The 3.x runtime: `ClientManager`, `Reactor`, `DisplayReactor`, the display
|
|
2525
|
+
codecs and zod validation, the 3.x errors (`CanisterError`, `CallError`), the key and
|
|
2526
|
+
retry helpers (`generateKey`, `reactorRetry`), `formatTokenAmount`,
|
|
2527
|
+
`parseTokenAmount` and `isPrincipalText`, and the 3.x test helpers
|
|
2528
|
+
(`installFakeReplica`, `createTestCanister`). They stay on the 3.x line.
|
|
2529
|
+
|
|
2530
|
+
### @ic-reactor/react
|
|
2531
|
+
|
|
2532
|
+
#### Added
|
|
2533
|
+
|
|
2534
|
+
- `ReactorProvider({ client: () => Client, children })` calls its factory
|
|
2535
|
+
once per mounted tree, renders TanStack Query's `QueryClientProvider` with
|
|
2536
|
+
the client's `QueryClient`, and disposes a client its factory created when
|
|
2537
|
+
it unmounts (`StrictMode`-safe). A client the app owns, passed as
|
|
2538
|
+
`client={() => client}`, is borrowed and never disposed.
|
|
2539
|
+
- `useClient()` returns the nearest provider's client, and throws outside one.
|
|
2540
|
+
Its component renders again whenever the caller's principal changes, and
|
|
2541
|
+
the keys it builds are the caller's that the render shows: on a server and
|
|
2542
|
+
while a page hydrates, the anonymous caller's. So a tab that holds a
|
|
2543
|
+
session hydrates from the keys the server prefetched, with no mismatch and
|
|
2544
|
+
nothing read again, then reads the session's keys (#813).
|
|
2545
|
+
- `useAuth()` returns `{ status, principal, signIn, signOut }`. `status` is
|
|
2546
|
+
`"anonymous"`, `"signed-in"`, `"expired"` or `"signed-in-elsewhere"`; it is
|
|
2547
|
+
`"anonymous"` on a server and in a hydrating page's first render.
|
|
2548
|
+
- Known limits of a signed-in reload, described in the README's "Keys follow
|
|
2549
|
+
the caller a render shows": a Suspense boundary still hydrating below a
|
|
2550
|
+
component that renders with the caller is rendered on the client when that
|
|
2551
|
+
component moves to the session (pass such a boundary as `children`), and a
|
|
2552
|
+
component that reads with `useSuspenseQuery` needs a Suspense boundary
|
|
2553
|
+
above it.
|
|
2554
|
+
- The type `ReactorProviderProps`. The entry exports these 4 names and never
|
|
2555
|
+
re-exports `@ic-reactor/core`.
|
|
2556
|
+
|
|
2557
|
+
#### Changed
|
|
2558
|
+
|
|
2559
|
+
- `@ic-reactor/core` is a peer dependency at exactly this package's version
|
|
2560
|
+
(it was a dependency). The other peers are `@tanstack/react-query`
|
|
2561
|
+
`^5.90.2` and `react` `>=18.0.0`; `@icp-sdk/auth`, `@icp-sdk/core`,
|
|
2562
|
+
`@noble/curves` and `react-dom` are no longer peers.
|
|
2563
|
+
|
|
2564
|
+
#### Removed
|
|
2565
|
+
|
|
2566
|
+
- Every 3.x hook and factory: `defineReactor`, `createActorHooks`,
|
|
2567
|
+
`createReactorProvider`, `createQuery`, `createMutation`, the suspense and
|
|
2568
|
+
infinite variants, `createAuthHooks`, the identity-attribute hooks and the
|
|
2569
|
+
re-exported `skipToken`; and the auth classes behind them,
|
|
2570
|
+
`AuthenticationManager` and `IdentityAttributesManager`. Sign in with an
|
|
2571
|
+
`AuthLike` passed to `createClient` and `useAuth()`. Read and write with TanStack Query's `useQuery`
|
|
2572
|
+
and `useMutation` over `client.queryOptions` and `client.mutationOptions`.
|
|
2573
|
+
- The `react-server` entry and `@ic-reactor/react/testing`.
|
|
2574
|
+
|
|
2575
|
+
### @ic-reactor/vite-plugin
|
|
2576
|
+
|
|
2577
|
+
The plugin joins the lane of core and react, at their version.
|
|
2578
|
+
|
|
2579
|
+
#### Changed
|
|
2580
|
+
|
|
2581
|
+
- Generation runs `candid-core-cli gen` from the app's own `@candid-core/cli`,
|
|
2582
|
+
in a child process per `.did` file, so a generator failure stops that
|
|
2583
|
+
process and not the dev server. It writes candid-core's module (named after
|
|
2584
|
+
the `.did` file, exporting `actor` and the type `Actor`) and its
|
|
2585
|
+
`.envelope.json` into `outDir`, by default `src/canisters`, and nothing
|
|
2586
|
+
else.
|
|
2587
|
+
- `canisters` is a record keyed by canister name,
|
|
2588
|
+
`{ didFile?, outDir?, canisterId? }`, instead of an array of entries.
|
|
2589
|
+
`injectEnvironment` and `failOnError` work as before. The first line the
|
|
2590
|
+
plugin logs names `node_modules/@ic-reactor/core/llms.txt`. Migration:
|
|
2591
|
+
`canisters: [{ name: "ledger", didFile }]` becomes
|
|
2592
|
+
`canisters: { ledger: { didFile } }`.
|
|
2593
|
+
- `@candid-core/cli` is a peer dependency at exactly `0.2.0-beta.1`, the
|
|
2594
|
+
generator that pairs with `@candid-core/schema` `0.3.0-beta.1`. The `vite`
|
|
2595
|
+
range is unchanged. Migration:
|
|
2596
|
+
`npm install --save-dev --save-exact @candid-core/cli@0.2.0-beta.1`.
|
|
2597
|
+
|
|
2598
|
+
#### Removed
|
|
2599
|
+
|
|
2600
|
+
- Generation through `@ic-reactor/codegen`, which is no longer a dependency:
|
|
2601
|
+
the generated reactor and hook files (`index.generated.ts`,
|
|
2602
|
+
`index.factories.generated.ts`, `index.ts`) and the options that shaped
|
|
2603
|
+
them (the top-level `outDir`, `clientManagerPath` and `target`, and each
|
|
2604
|
+
entry's `name`, `mode`, `target` and `factories`).
|
|
2605
|
+
|
|
2606
|
+
## core, react, candid 3.13.0
|
|
2607
|
+
|
|
2608
|
+
Changes since core, react and candid 3.12.5.
|
|
2609
|
+
|
|
2610
|
+
### @ic-reactor/core
|
|
2611
|
+
|
|
2612
|
+
#### Added
|
|
2613
|
+
|
|
2614
|
+
- `reactor.forCanister(canisterId)` returns a memoized sibling reactor for
|
|
2615
|
+
another canister of the same interface. It has the same class, `ClientManager`
|
|
2616
|
+
and validators, and its calls and query keys use its own canister. Use it
|
|
2617
|
+
instead of `setCanisterId` or one hand-built reactor per canister. A
|
|
2618
|
+
subclass's constructor must build on the `canisterId` it is given;
|
|
2619
|
+
`forCanister` throws when the new reactor lands on another canister.
|
|
2620
|
+
- `ServiceOf`, `TransformOf`, `ReactorArgsOf`, `ReactorDataOf` and
|
|
2621
|
+
`ReactorErrorOf` read a method's types off `typeof reactor`.
|
|
2622
|
+
- `formatTokenAmount(value, decimals, options?)` and
|
|
2623
|
+
`parseTokenAmount(text, decimals)` convert ledger base units exactly.
|
|
2624
|
+
`isPrincipalText(text)` validates typed principal text.
|
|
2625
|
+
- `clientManager.fetchAcrossIdentitySwitch(fetch)` runs a hand-written
|
|
2626
|
+
`queryClient.fetchQuery` / `fetchInfiniteQuery` again when a sign-in or
|
|
2627
|
+
sign-out switches the principal while it runs (#647).
|
|
2628
|
+
- `clientManager.explicitRootKey` reports a root key the app passed as
|
|
2629
|
+
`agentOptions.rootKey` (#713).
|
|
2630
|
+
- `isRetryableUpdateError` and `reactorUpdateRetry` retry an update call only
|
|
2631
|
+
after a `SysTransient` rejection, and `Reactor.getQueryRetry()` gives the
|
|
2632
|
+
retry a query of a method falls back to (#622).
|
|
2633
|
+
- `Reactor.fetchQuery(params, options?)` takes a second argument of TanStack
|
|
2634
|
+
Query fetch options (`retry`, `retryDelay`, `networkMode`, `meta`, ...); the
|
|
2635
|
+
key and query function still come from `params`. The query factories pass
|
|
2636
|
+
their config's options through it, so a subclass that overrides `fetchQuery`
|
|
2637
|
+
still sees their fetches (#502).
|
|
2638
|
+
- `fromZodSchema(schema, { async: true })` accepts zod schemas with async
|
|
2639
|
+
refinements (#593).
|
|
2640
|
+
- `createPollingStrategy({ timeoutMs })`, 5 minutes per request by default
|
|
2641
|
+
(#563).
|
|
2642
|
+
- `@ic-reactor/core/testing` exports `installFakeReplica` and
|
|
2643
|
+
`createTestCanister`. The fake replica signs certificates and checks request
|
|
2644
|
+
signatures, so tests run the real `Reactor` and hooks. `@noble/curves` is a
|
|
2645
|
+
new optional peer dependency, used only by this entry.
|
|
2646
|
+
|
|
2647
|
+
#### Changed
|
|
2648
|
+
|
|
2649
|
+
- `Reactor.invalidateQueries()` returns TanStack Query's `Promise<void>`, which
|
|
2650
|
+
resolves once the active matches have refetched. Migration: `await` it, or
|
|
2651
|
+
prefix a call you do not wait for with `void`.
|
|
2652
|
+
- `reactorRetry` no longer retries an HTTP 4xx other than 408 and 429, such as
|
|
2653
|
+
an expired delegation's 400 (#646). Migration: none; these errors now show
|
|
2654
|
+
after the agent's own attempts instead of about 20 seconds later.
|
|
2655
|
+
- A query of an update method that sets no `retry` retries only a
|
|
2656
|
+
`SysTransient` rejection, within the QueryClient's retry default (#622).
|
|
2657
|
+
Migration: call state-changing methods through a mutation.
|
|
2658
|
+
- `Reactor.fetchQuery()`, and so every query factory's `fetch()`, fetches again
|
|
2659
|
+
as the new principal when a sign-in or sign-out cancels it, instead of
|
|
2660
|
+
rejecting with TanStack's `CancelledError` (#647). When the first run and
|
|
2661
|
+
three runs again are each overtaken by a switch, it rejects with a
|
|
2662
|
+
`CallError`. Migration: remove `CancelledError` handling from loaders.
|
|
2663
|
+
- `ClientManager.updateAgent()` keeps the cache when the new identity has the
|
|
2664
|
+
principal already installed, as a renewed delegation or a repeated sign-in
|
|
2665
|
+
does. Only entries that failed, or whose fetch in flight then fails, refetch
|
|
2666
|
+
(#719). Migration: after installing an identity that changes what canisters
|
|
2667
|
+
see under the same principal, such as an `AttributesIdentity`, invalidate the
|
|
2668
|
+
affected queries yourself.
|
|
2669
|
+
- Codespaces (`*.github.dev`) and Gitpod (`*.gitpod.io`) pages without an
|
|
2670
|
+
`agentOptions.host` route through their own origin with network `"remote"`
|
|
2671
|
+
instead of `https://ic0.app`, and their `ic_env` cookie is no longer trusted
|
|
2672
|
+
(#643). Migration: pass `allowEnvConfig: true` to read the cookie on those
|
|
2673
|
+
hosts.
|
|
2674
|
+
- In a development build, `ClientManager` skips query signature verification by
|
|
2675
|
+
default only for a local replica or a Codespaces or Gitpod tunnel to one. A
|
|
2676
|
+
dev page whose agent points at any other host, such as mainnet's
|
|
2677
|
+
`https://icp-api.io`, now verifies signatures, as a production build does,
|
|
2678
|
+
and a web worker follows the same default as its page. Migration: none; an
|
|
2679
|
+
explicit `verifyQuerySignatures` still wins.
|
|
2680
|
+
- On a local host, `initialize()` keeps a root key passed as
|
|
2681
|
+
`agentOptions.rootKey` instead of replacing it with the replica's (#713).
|
|
2682
|
+
Migration: pass the replica's key, or no key.
|
|
2683
|
+
- The args segment of a query key is no longer `JSON.stringify(args)`.
|
|
2684
|
+
`generateKey` sorts plain-object keys and writes `NaN`, `±Infinity` and `-0`
|
|
2685
|
+
behind a U+0000 tag. `generateQueryKey` also writes a blob as tagged hex,
|
|
2686
|
+
gives a `DisplayReactor` value one form whichever form was passed, and tags an
|
|
2687
|
+
argument the reactor would refuse (#515, #761, #762, #765). A query sent
|
|
2688
|
+
through `callConfig.agent`, when that is not the manager's own agent, is keyed
|
|
2689
|
+
with an `{ agent: n }` segment (#642). Migration: build keys with
|
|
2690
|
+
`reactor.generateQueryKey(...)` (or `generateKey`), never by hand or with
|
|
2691
|
+
`JSON.stringify`.
|
|
2692
|
+
- A validator that throws or rejects is reported as a `CallError`
|
|
2693
|
+
("Failed to validate the arguments of <method>") with the thrown value as
|
|
2694
|
+
`cause`, from `callMethod`, `callMethodWithValidation` and `validate` alike.
|
|
2695
|
+
A thrown `ValidationError` passes through unchanged (#593). Migration: read
|
|
2696
|
+
`error.cause` where code caught the raw error.
|
|
2697
|
+
- `CanisterError.details` is typed as the error value's own `details` field
|
|
2698
|
+
(`CanisterErrorDetails<E>`), e.g. `[] | [Array<[string, string]>]` from a
|
|
2699
|
+
`Reactor`, instead of `Map<string, string>`, which Candid decoding never
|
|
2700
|
+
returns; runtime values are unchanged (#685). `CanisterError` reads an
|
|
2701
|
+
`opt text` message of an API-shaped error. `ApiError`'s `message` and
|
|
2702
|
+
`details` are type parameters that default to `unknown`, and
|
|
2703
|
+
`CanisterError.isApiError()` narrows them to `unknown` (#690). Migration: read
|
|
2704
|
+
`details` in the shape the canister returns it, not as a `Map`, and name the
|
|
2705
|
+
payload types, e.g. `ApiError<[] | [string], [] | [Array<[string, string]>]>`.
|
|
2706
|
+
- `createPollingStrategy` keeps its attempt count, clock and timeout per
|
|
2707
|
+
request, so one instance can serve a whole reactor, and gives up after
|
|
2708
|
+
`timeoutMs` (#563). Migration: `timeoutMs: Infinity` restores polling without
|
|
2709
|
+
a limit.
|
|
2710
|
+
- `DisplayReactor` sends `NaN`, `±Infinity` and `-0` floats back as it read
|
|
2711
|
+
them, instead of refusing a non-finite float (#632). Migration: none; text
|
|
2712
|
+
from a form must still spell a finite number.
|
|
2713
|
+
- `DisplayReactor` displays `opt opt T`'s `some(none)` as `null` and none as
|
|
2714
|
+
`undefined`, so a cleared value differs from one never set, as Internet
|
|
2715
|
+
Identity's and Orbit's config types need (#486). Encoding is unchanged: `null`
|
|
2716
|
+
and `undefined` both send none, and `[null]` sends `some(none)`. Migration: to
|
|
2717
|
+
tell none from `some(none)`, check `=== undefined` and `=== null`; `== null`
|
|
2718
|
+
still covers both.
|
|
2719
|
+
- `hexToUint8Array`, and so a `DisplayReactor` blob argument, refuses hex with
|
|
2720
|
+
an odd number of digits instead of padding it with a leading zero. Padding
|
|
2721
|
+
shifted every byte, so a subaccount or memo that lost its last digit was sent
|
|
2722
|
+
as different, valid-looking bytes. Migration: pass two hex digits per byte.
|
|
2723
|
+
- `jsonToString` writes a `Principal` as its text and a `Uint8Array` as hex,
|
|
2724
|
+
instead of `{"__principal__": ...}` and an object keyed by index. Migration:
|
|
2725
|
+
drop a custom replacer that did this, and update code that parsed the old
|
|
2726
|
+
output.
|
|
2727
|
+
|
|
2728
|
+
#### Fixed
|
|
2729
|
+
|
|
2730
|
+
- Update calls: a v4 response whose certificate has no status for the request
|
|
2731
|
+
now falls back to polling. It used to fail with a `CallError` for a call that
|
|
2732
|
+
could still commit, which invited a retry that ran a transfer twice (#508). An
|
|
2733
|
+
anonymous call submitted before the first sign-in keeps polling as anonymous,
|
|
2734
|
+
so a sign-in during its polling no longer reports a committed call as failed.
|
|
2735
|
+
`pollingOptions.blsVerify` now also checks synchronous (v4) responses, not
|
|
2736
|
+
only polled ones.
|
|
2737
|
+
- On a local replica with no root key supplied, the agent fetches the replica's
|
|
2738
|
+
key before its first request. A call made before `initialize()` finished no
|
|
2739
|
+
longer fails certificate verification, including an update call that had
|
|
2740
|
+
already run on the replica when it was reported as failed.
|
|
2741
|
+
- `ClientManager` host detection: every loopback address (`127.0.0.0/8`,
|
|
2742
|
+
`[::1]`) counts as local (#512). A host without a scheme, such as
|
|
2743
|
+
`127.0.0.1:4943`, is read the way `HttpAgent` reads it instead of as mainnet.
|
|
2744
|
+
A web worker routes like the page that started it instead of falling back to
|
|
2745
|
+
`https://ic0.app`. Construction no longer throws in React Native or on a page
|
|
2746
|
+
with an opaque origin such as `file://` or `about:blank` (#510).
|
|
2747
|
+
- `ClientManager` resolves `agentOptions` into a copy. A frozen options object
|
|
2748
|
+
no longer throws. One options object shared by two managers no longer carries
|
|
2749
|
+
the first manager's resolved settings, such as a root key from the `ic_env`
|
|
2750
|
+
cookie, into the second (#516).
|
|
2751
|
+
- `ClientManager` subscriptions: calling the unsubscribe of a callback
|
|
2752
|
+
registered twice removes only that registration (#513). A subscriber that
|
|
2753
|
+
throws no longer stops later subscribers from being notified. A change made
|
|
2754
|
+
from inside a callback is no longer followed by the older value.
|
|
2755
|
+
- `initializeAgent()` recovers when an agent-state subscriber throws. The failed
|
|
2756
|
+
attempt clears `isInitializing` and `isInitialized`, so the next call starts a
|
|
2757
|
+
fresh attempt instead of returning the old rejection or staying in error for
|
|
2758
|
+
the rest of the session. A subscriber that calls `initializeAgent()` on the
|
|
2759
|
+
`initializing` notification now waits for that attempt (#514).
|
|
2760
|
+
- A sign-in or sign-out sweeps the query cache once instead of once per
|
|
2761
|
+
registered canister. With 1,000 canisters the sweep takes 0.7 ms instead of
|
|
2762
|
+
166 ms.
|
|
2763
|
+
- Query keys follow the Candid value a call sends, so one call is no longer
|
|
2764
|
+
answered from another's cache entry:
|
|
2765
|
+
- records with the same fields in any order share a key, while `NaN`,
|
|
2766
|
+
`±Infinity` and `-0` no longer share one (#515);
|
|
2767
|
+
- a blob has one key whether it is passed as a `Uint8Array`, a `number[]` or
|
|
2768
|
+
hex text, and a 1 MB blob's key segment shrinks from 13.1 MB to 2.1 MB;
|
|
2769
|
+
- a `DisplayReactor` map object, bare or wrapped `opt`, or variant with or
|
|
2770
|
+
without `_type` is keyed by what it sends (#761, #762), and so is a record
|
|
2771
|
+
or variant given as a class instance or with a non-enumerable field (#768);
|
|
2772
|
+
- an argument the reactor refuses, such as `undefined` for `null` or a bigint
|
|
2773
|
+
for `text`, fails instead of returning a valid call's cached result (#765).
|
|
2774
|
+
- A query sent through `callConfig.agent` (another identity or network) gets a
|
|
2775
|
+
cache entry of its own. It used to share the entry of the same query through
|
|
2776
|
+
the manager's agent, so whichever ran first answered both, such as a
|
|
2777
|
+
`whoami` for the wrong principal (#642).
|
|
2778
|
+
- After `setCanisterId`, a retry or refetch of an existing query fetches the
|
|
2779
|
+
canister its key names. It used to cache the new canister's answer under the
|
|
2780
|
+
old canister's key (#509).
|
|
2781
|
+
- `invalidateQueries()` without a `functionName` targets `callConfig.canisterId`
|
|
2782
|
+
when one is given. With params but no method, it now matches the canister's
|
|
2783
|
+
queries instead of none (#511).
|
|
2784
|
+
- Only a Result variant is unwrapped. A record with an `ok` or `err` field, or
|
|
2785
|
+
with its own `_type` field beside one, is returned whole instead of resolving
|
|
2786
|
+
to that field or throwing a `CanisterError` on success. `OkResult` and
|
|
2787
|
+
`ErrResult` type it the same way (#517).
|
|
2788
|
+
- `JSON.stringify` of a `CanisterError`, `CallError` or `ValidationError` that
|
|
2789
|
+
holds BigInts, such as any ICRC-1 transfer error, no longer throws. Each class
|
|
2790
|
+
has a `toJSON` that writes BigInts as decimal strings and hands a replacer the
|
|
2791
|
+
error's own values (#518).
|
|
2792
|
+
- `isCanisterError`, `isCallError` and `isValidationError` recognise errors
|
|
2793
|
+
thrown by another copy of `@ic-reactor/core` loaded in the same app, so a
|
|
2794
|
+
mutation's `onCanisterError` runs and `reactorRetry` retries their transport
|
|
2795
|
+
failures. Such a `ValidationError` or `CanisterError` also keeps its type
|
|
2796
|
+
through `callMethod`, `callMethodWithValidation` and `validate` (#593).
|
|
2797
|
+
- Validation: a validator for a method that takes no arguments now runs when
|
|
2798
|
+
`args` is omitted. When `callMethod()` refuses an async validator, it no
|
|
2799
|
+
longer leaves an unhandled rejection. `ValidationError.getIssuesForPath` and
|
|
2800
|
+
`hasErrorForPath` find whole-argument issues under `""` and accept a numeric
|
|
2801
|
+
array index, matching `1` and `"1"` alike (#592).
|
|
2802
|
+
- Methods typed by a recursive func alias (`type f = func (f) -> (f)`) work.
|
|
2803
|
+
`callMethod`, `fetchQuery` and `isQueryMethod` no longer fail on them (#608).
|
|
2804
|
+
`DisplayReactor` builds their codecs instead of dropping the codecs of that
|
|
2805
|
+
method and every method after it (#557).
|
|
2806
|
+
- The display types of a type that contains itself through `opt`, `vec`, a tuple
|
|
2807
|
+
or a map, such as Motoko's `List`, now resolve. They used to fail with TS2589
|
|
2808
|
+
in every hook and call on that method (#566).
|
|
2809
|
+
- `DisplayReactor` arguments:
|
|
2810
|
+
- a one-element `opt` value is no longer mistaken for the `[value]` wrapper,
|
|
2811
|
+
so a value the codec decoded encodes back. This fixes the cycles ledger's
|
|
2812
|
+
`opt vec record { Account; nat }` with one balance, `opt vec vec T`,
|
|
2813
|
+
`opt vec blob` given `[bytes]`, `opt opt vec text`, and a text-keyed map
|
|
2814
|
+
passed in wrapper form (#484);
|
|
2815
|
+
- a record field or variant arm named like an `Object.prototype` member
|
|
2816
|
+
(`constructor`, `toString`, ...) is treated as absent when it is left out,
|
|
2817
|
+
even for a value from another realm, instead of sending the inherited
|
|
2818
|
+
function. `getVariantValue` and `getVariantKeyValue` no longer return the
|
|
2819
|
+
inherited function either (#483);
|
|
2820
|
+
- a `Map` given for a `vec record { text; T }` is sent with its entries
|
|
2821
|
+
instead of as an empty vector (#767).
|
|
2822
|
+
- `DisplayReactor` on large recursive values: a recursive type's codec is built
|
|
2823
|
+
once instead of for every node, so 1,000 ICRC-3 blocks display in about 16 ms
|
|
2824
|
+
instead of 470 ms (#485). Long lists such as a Motoko `List` display and
|
|
2825
|
+
encode up to 1,875 elements. They used to fail past about 586 elements and
|
|
2826
|
+
return the raw Candid value.
|
|
2827
|
+
- Under Deno, `reactorRetry` retries nothing, as on any other server and as
|
|
2828
|
+
TanStack Query's own default does. Deno can define `window`, so a failed
|
|
2829
|
+
call during Deno server rendering was retried three times with backoff.
|
|
2830
|
+
`reactorUpdateRetry` and an update method's query retry follow the same
|
|
2831
|
+
rule.
|
|
2832
|
+
- Docs corrected to match the package. `Reactor.fetchQuery()` is cache-first and
|
|
2833
|
+
returns a stale or invalidated entry as is (#595). An empty `opt` displays as
|
|
2834
|
+
`undefined` (#589). The packages need TypeScript 5.7 or later, which their
|
|
2835
|
+
declarations already required (#591).
|
|
2836
|
+
|
|
2837
|
+
### @ic-reactor/react
|
|
2838
|
+
|
|
2839
|
+
#### Added
|
|
2840
|
+
|
|
2841
|
+
- `defineDisplayReactor(options)` takes `defineReactor`'s options and builds a
|
|
2842
|
+
`DisplayReactor` (#746).
|
|
2843
|
+
- `createReactorProvider(factory, options?)` returns
|
|
2844
|
+
`{ ReactorProvider, useReactor }`. It builds the reactors once per mounted
|
|
2845
|
+
provider, so once per request on a server, and disposes the
|
|
2846
|
+
`AuthenticationManager`s built for its value on unmount (#745). When the
|
|
2847
|
+
value holds one `QueryClient`, it also renders a `QueryClientProvider` for
|
|
2848
|
+
it; `queryClientProvider: false` opts out.
|
|
2849
|
+
- A `react-server` export condition. React Server Components, server actions
|
|
2850
|
+
and route handlers import the core runtime (`Reactor`, `DisplayReactor`,
|
|
2851
|
+
`ClientManager`, the token helpers) from `@ic-reactor/react`; hooks and
|
|
2852
|
+
factories are missing exports there (#679).
|
|
2853
|
+
- `skipToken`, re-exported from TanStack Query, in place of args in
|
|
2854
|
+
`useActorQuery`, `useActorInfiniteQuery` (`getArgs`) and `createQueryFactory`.
|
|
2855
|
+
- `invalidateQueries` on `createMutation`, `.useMutation()`,
|
|
2856
|
+
`useActorMutation` and `useActorMethod` takes query objects, query factories
|
|
2857
|
+
and typed `{ functionName, args? }` descriptors. Query factory functions gain
|
|
2858
|
+
`getQueryKey()` and `invalidate()`. New types: `InvalidationTarget`,
|
|
2859
|
+
`QueryKeySource`, `QueryDescriptor`, `QueryFactoryFn`, `QueryFactoryMethods`.
|
|
2860
|
+
- Query objects, those of the infinite factories included, gain `cancel()`,
|
|
2861
|
+
`reset()` and `optimisticUpdate(updater)`, which resolves with
|
|
2862
|
+
`{ rollback() }`.
|
|
2863
|
+
- `callConfig` in `createQuery`, `createSuspenseQuery` and their factories.
|
|
2864
|
+
- `AuthenticationManager.dispose()` releases the auth client the manager built
|
|
2865
|
+
(#745).
|
|
2866
|
+
- Support for `@icp-sdk/auth` v10 alongside v8. The peer range is
|
|
2867
|
+
`^8.0.0 || ^10.0.0` (v9 is excluded), so npm installs the set without an
|
|
2868
|
+
`overrides` block. `identityProvider` is still a URL. On v10 a custom provider
|
|
2869
|
+
also needs `internetIdentityId`, the canister that mints its delegations, or
|
|
2870
|
+
`login()` throws an error that names the option. Off mainnet, v10's minting
|
|
2871
|
+
agent is given the app's replica host and root key, so local sign-in works.
|
|
2872
|
+
- Two options that only v10 uses: `maxTimeToIdle` for `login()` and
|
|
2873
|
+
`IdentityAttributesManager.request()`, and `disableBrowserActivity` for the
|
|
2874
|
+
auth client. v8 drops both with a one-time warning. On v10, `storage`,
|
|
2875
|
+
`keyType`, `idleOptions` and `identity` are dropped with a one-time warning,
|
|
2876
|
+
and `login({ targets })` warns on every sign-in because v10 ignores `targets`
|
|
2877
|
+
and returns a broader delegation.
|
|
2878
|
+
- `@ic-reactor/react/testing` re-exports `@ic-reactor/core/testing`.
|
|
2879
|
+
`@noble/curves` is a new optional peer dependency here too, used only by
|
|
2880
|
+
this entry.
|
|
2881
|
+
|
|
2882
|
+
#### Changed
|
|
2883
|
+
|
|
2884
|
+
- The `@tanstack/react-query` peer is now `^5.90.2` (was `^5.0.0`); older
|
|
2885
|
+
releases fail to compile against the package or fail its tests (#475).
|
|
2886
|
+
Migration: upgrade `@tanstack/react-query` to 5.90.2 or later.
|
|
2887
|
+
- Every query and mutation hook (raw, bound and factory-made) mounts its
|
|
2888
|
+
reactor's `QueryClient` while it is mounted, as `QueryClientProvider` does,
|
|
2889
|
+
and a suspense hook also while it is suspended. Without a provider,
|
|
2890
|
+
`refetchOnWindowFocus` and `refetchOnReconnect` now fire, and a query or
|
|
2891
|
+
mutation paused while offline resumes when the connection returns (#498).
|
|
2892
|
+
Migration: if the app relied on no refetch on focus or reconnect, set
|
|
2893
|
+
`refetchOnWindowFocus` / `refetchOnReconnect` to `false` on the query or in
|
|
2894
|
+
the QueryClient's defaults.
|
|
2895
|
+
- `useAuth()` and `useUserPrincipal()` report `isAuthenticating: true` until
|
|
2896
|
+
the first session restore settles, in server renders too (#621). Migration:
|
|
2897
|
+
check `isAuthenticating` before redirecting on `!isAuthenticated`.
|
|
2898
|
+
- `createMutation(...).execute()` runs in the QueryClient's MutationCache: the
|
|
2899
|
+
cache's global callbacks, `useIsMutating`, the factory's `retry`,
|
|
2900
|
+
`networkMode`, `onMutate` and `onSettled`, and the QueryClient's mutation
|
|
2901
|
+
defaults now apply to it (#564). Migration: a `mutations.retry` default now
|
|
2902
|
+
retries `execute()` too; use `reactorUpdateRetry` for update methods.
|
|
2903
|
+
- `useActorMethod` awaits `invalidateQueries` before `onSuccess`, and `call()`
|
|
2904
|
+
resolves after the invalidated queries have refetched (#564). Migration:
|
|
2905
|
+
none; `onSuccess` now reads fresh data.
|
|
2906
|
+
- `useActorMethod` applies its `retry`, `retryDelay`, `networkMode` and `meta`
|
|
2907
|
+
to `call()`, for an update method as for a query method's `call(args)` (#564).
|
|
2908
|
+
Migration: replace a numeric `retry` on a hook that calls an update method
|
|
2909
|
+
with `reactorUpdateRetry`.
|
|
2910
|
+
- A query method's `call()` and `refetch()` from `useActorMethod` fetch again
|
|
2911
|
+
for the new principal when a sign-in or sign-out lands mid-call, and resolve
|
|
2912
|
+
with that answer instead of the previous principal's cached data or
|
|
2913
|
+
TanStack's `CancelledError`. When their fetch fails they resolve `undefined`
|
|
2914
|
+
(and report the error to `onError`) instead of the entry's last answer.
|
|
2915
|
+
Migration: treat an `undefined` result as a failed call.
|
|
2916
|
+
- A query object's `prefetch()` fetches again for the new principal when a
|
|
2917
|
+
sign-in or sign-out overtakes it, so the cache never keeps the previous
|
|
2918
|
+
principal's answer. Migration: none.
|
|
2919
|
+
- On `@icp-sdk/auth` v10, `AuthenticationManager` follows sign-outs and account
|
|
2920
|
+
switches made in other tabs and updates the agent (#754). Migration: none;
|
|
2921
|
+
expect `useAuth()` to change when another tab signs out.
|
|
2922
|
+
- `authentication.logout()` on a manager with no client builds one instead of
|
|
2923
|
+
throwing "Authentication module is missing". Migration: remove a `catch`
|
|
2924
|
+
for that error.
|
|
2925
|
+
|
|
2926
|
+
#### Deprecated
|
|
2927
|
+
|
|
2928
|
+
- `defineReactor({ display: true })`, its overload and
|
|
2929
|
+
`DefineDisplayReactorParameters`. Use `defineDisplayReactor(...)`. They will
|
|
2930
|
+
be removed at the next major. Plain `defineReactor` is not deprecated.
|
|
2931
|
+
|
|
2932
|
+
#### Fixed
|
|
2933
|
+
|
|
2934
|
+
- `useActorMethod`:
|
|
2935
|
+
- `call`, `refetch` and `reset` keep one identity for the component's
|
|
2936
|
+
lifetime. An effect that lists `call` no longer runs again after every
|
|
2937
|
+
render, which could call an update method endlessly. They also run with the
|
|
2938
|
+
last committed render's method, args and callbacks (#497);
|
|
2939
|
+
- `call(args)` with the hook's own args runs `onSuccess` / `onError` once
|
|
2940
|
+
instead of twice. A call that settles in the same millisecond as the
|
|
2941
|
+
previous result is still reported (#497);
|
|
2942
|
+
- `onSuccess` no longer runs for `placeholderData`, such as `keepPreviousData`
|
|
2943
|
+
(#501), or for `initialData` that no call returned (#564);
|
|
2944
|
+
- an `undefined` entry of `invalidateQueries` is skipped, as the mutation
|
|
2945
|
+
hooks skip it, instead of invalidating every query in the client.
|
|
2946
|
+
- Hooks and factories:
|
|
2947
|
+
- `useActorInfiniteQuery` and `useActorSuspenseInfiniteQuery` apply
|
|
2948
|
+
`getKeyArgs` as the factories do. An `initialPageParam` that changes on
|
|
2949
|
+
every render, such as `Date.now()`, no longer refetches the first page
|
|
2950
|
+
endlessly (#499);
|
|
2951
|
+
- infinite queries, both hooks and factories, fetch their pages from the
|
|
2952
|
+
canister their key names. Before, a later `setCanisterId` redirected the
|
|
2953
|
+
fetch (#558);
|
|
2954
|
+
- a query object's `fetch()` and `prefetch()`, and the infinite factories'
|
|
2955
|
+
`fetch()`, use the config's `networkMode`, `retry`, `retryDelay` and `meta`,
|
|
2956
|
+
as `useQuery()` does (#502);
|
|
2957
|
+
- `useActorMutation` keys its mutations like `createMutation` and
|
|
2958
|
+
`useActorMethod`, so `useIsMutating({ mutationKey })`, `useMutationState`
|
|
2959
|
+
and `setMutationDefaults` now match them (#564).
|
|
2960
|
+
- Validation helpers: `mapValidationErrors`, `extractValidationErrors` and
|
|
2961
|
+
`handleValidationError` keep issues on fields named `constructor`, `toString`
|
|
2962
|
+
or `__proto__`. Before, those issues were dropped, and with
|
|
2963
|
+
`{ multiple: true }` the call threw. `mapValidationErrors` now also accepts
|
|
2964
|
+
any `{ multiple?: boolean }` value, `{}` or `undefined`, and `getFieldError` /
|
|
2965
|
+
`getFieldErrors` find an issue about the whole argument under `""` (#592).
|
|
2966
|
+
- `AuthenticationManager`:
|
|
2967
|
+
- several `useAuth()` consumers that mount together share one auth client
|
|
2968
|
+
instead of building one each (#479), and the v10 clients a call replaces
|
|
2969
|
+
when its options differ are disposed (#729). On v8 an idle period runs
|
|
2970
|
+
`idleOptions.onIdle` once instead of once per client built;
|
|
2971
|
+
- the session restore runs once per manager instead of once per `useAuth()`,
|
|
2972
|
+
so mounting a consumer no longer flips `isAuthenticating`, and a component
|
|
2973
|
+
that hides consumers while authenticating no longer loops. A restore reads
|
|
2974
|
+
the identity from the client the manager still uses, and a restore of a
|
|
2975
|
+
manager disposed before it finished builds no client (#745);
|
|
2976
|
+
- when `logout()` fails after the client has already dropped the session, as a
|
|
2977
|
+
v10 revoke does while offline, the manager signs out locally: the agent gets
|
|
2978
|
+
the anonymous identity and the signed-out state is published. `logout()`
|
|
2979
|
+
still rejects with the error (#478);
|
|
2980
|
+
- a failed `login()` records its error and clears `isAuthenticating` before
|
|
2981
|
+
`onError` runs, so an `onError` that throws no longer leaves
|
|
2982
|
+
`isAuthenticating: true` (#480);
|
|
2983
|
+
- an `ic_env` cookie that is not valid percent-encoding is ignored. Before, it
|
|
2984
|
+
made `new AuthenticationManager()`, and so `useAuth()`, throw `URIError`
|
|
2985
|
+
(#482);
|
|
2986
|
+
- the unsubscribe that `subscribeAuthState` returns removes only its own
|
|
2987
|
+
registration. A subscriber that throws no longer stops later subscribers,
|
|
2988
|
+
including `useAuth()`, from hearing the change; the first error is rethrown
|
|
2989
|
+
after all of them have run;
|
|
2990
|
+
- when the local Internet Identity canister serves no sign-in page, the error
|
|
2991
|
+
tells a v10 app to serve an II frontend and set `identityProvider` and
|
|
2992
|
+
`internetIdentityId`. Before, it pointed to an II build that only v8 can use
|
|
2993
|
+
(#561).
|
|
2994
|
+
- `useAuth`, `useUserPrincipal` and `useAgentState` render a fixed server
|
|
2995
|
+
snapshot, so a component that hydrates after the session restore no longer
|
|
2996
|
+
causes a hydration mismatch. Each consumer also subscribes once when it
|
|
2997
|
+
mounts, not on every render.
|
|
2998
|
+
- Lapsed and multi-tab sessions no longer leave an identity on the agent that
|
|
2999
|
+
the auth client does not vouch for:
|
|
3000
|
+
- a manager built over a caller's `authClient` whose session has lapsed puts
|
|
3001
|
+
the anonymous identity on the agent;
|
|
3002
|
+
- on v10, a lapsed session is ended in this tab only, without calling the
|
|
3003
|
+
client's `signOut()`, which acts on the whole origin and could end or
|
|
3004
|
+
revoke a sign-in made in another tab;
|
|
3005
|
+
- on v8, a tab whose own delegation has lapsed signs out even after another
|
|
3006
|
+
tab signs in again (#755). A signed-out tab no longer reports signed in as
|
|
3007
|
+
the anonymous principal once another tab signs in.
|
|
3008
|
+
- Identity attributes:
|
|
3009
|
+
- Internet Identity's attribute message is decoded as the ICRC-3 map it is.
|
|
3010
|
+
Emails and names no longer come back with stray length characters, cut short
|
|
3011
|
+
at the first non-ASCII character, or read from the wrong key (#477);
|
|
3012
|
+
- when an attribute request fails but its sign-in completes, the sign-in is
|
|
3013
|
+
kept, and `useIdentityAttributes` shows the error (#481);
|
|
3014
|
+
- a sign-out or account switch made while an attribute request is pending is
|
|
3015
|
+
no longer undone when the request resolves. If the client's session ended
|
|
3016
|
+
meanwhile, for example because another tab signed out, the request signs the
|
|
3017
|
+
manager out. `useIdentityAttributes` no longer shows the attributes after
|
|
3018
|
+
such a sign-out.
|
|
3019
|
+
|
|
3020
|
+
### @ic-reactor/candid
|
|
3021
|
+
|
|
3022
|
+
#### Added
|
|
3023
|
+
|
|
3024
|
+
- `forCanister(canisterId)` on `CandidReactor`, `CandidDisplayReactor`,
|
|
3025
|
+
`MetadataReactor` and `MetadataDisplayReactor`; the sibling shares the
|
|
3026
|
+
Candid source and adapter.
|
|
3027
|
+
- A `defaultArgs` option (`"display"` or `"candid"`) on `ResultFieldVisitor`.
|
|
3028
|
+
|
|
3029
|
+
#### Changed
|
|
3030
|
+
|
|
3031
|
+
- `MetadataReactor` gives func-record `defaultArgs` in Candid form, so
|
|
3032
|
+
`callMethod({ args: defaultArgs })` on a reactor built from the node's
|
|
3033
|
+
`funcClass` works (#611). `MetadataDisplayReactor` keeps display defaults.
|
|
3034
|
+
Migration: use `MetadataDisplayReactor` where the code expects display
|
|
3035
|
+
defaults.
|
|
3036
|
+
- `CandidFormVisitor` and `FieldVisitor` accept `""` for a `text` field. A text
|
|
3037
|
+
field with a format, a number and a func reference's method name are still
|
|
3038
|
+
required (#611). Migration: none.
|
|
3039
|
+
- `isPrincipalId` calls `isPrincipalText`, so it refuses a principal longer
|
|
3040
|
+
than 29 bytes and the `{"__principal__": ...}` JSON form. Migration: pass
|
|
3041
|
+
principal text.
|
|
3042
|
+
|
|
3043
|
+
#### Deprecated
|
|
3044
|
+
|
|
3045
|
+
- `CandidAdapter.unsubscribe` is a no-op; remove the call (#644).
|
|
3046
|
+
|
|
3047
|
+
#### Fixed
|
|
3048
|
+
|
|
3049
|
+
- `registerMethod`, and `callDynamic`, `queryDynamic` and `fetchQueryDynamic`,
|
|
3050
|
+
which call it, now accept Candid they used to reject: type declarations that
|
|
3051
|
+
do not each start a line, as in the documented indented example (#540); a
|
|
3052
|
+
whole service definition written `service: {…}`, with a line break before the
|
|
3053
|
+
colon, or named (`service greeter : {…}`) (#545); a quoted name that ends in
|
|
3054
|
+
an escaped backslash (#576); and a method name holding a quote, backslash or
|
|
3055
|
+
control character, which is now escaped instead of breaking the service or
|
|
3056
|
+
naming another method (#635). Rejecting an unclosed quoted name now takes
|
|
3057
|
+
milliseconds instead of seconds, and `MetadataReactor.buildForValueType` reads
|
|
3058
|
+
a type that ends in a `//` comment.
|
|
3059
|
+
- The argument forms (`FieldVisitor`, `CandidFormVisitor`) now accept only what
|
|
3060
|
+
the call can send. A func reference inside an argument is a
|
|
3061
|
+
`[principal, method name]` field (`candidType: "func"`) and a service
|
|
3062
|
+
reference is a principal field (`candidType: "service"`); before,
|
|
3063
|
+
`initialize()` threw or the schema refused every valid value (#539). A blob
|
|
3064
|
+
field takes only hex text with two digits per byte (optional `0x`), integers
|
|
3065
|
+
from 0 to 255 or a `Uint8Array`, so odd-length hex is refused instead of being
|
|
3066
|
+
sent shifted by one digit (#543). A float field refuses blank text, non-finite
|
|
3067
|
+
numbers and a float32 overflow (#542). An `empty` field refuses every value,
|
|
3068
|
+
and a variant no longer defaults to an option that holds `empty` (#544). A
|
|
3069
|
+
text field labelled as a URL also accepts a request path starting with `/`,
|
|
3070
|
+
such as `http_request`'s `url`.
|
|
3071
|
+
- A service with a method typed by a recursive func alias
|
|
3072
|
+
(`type f = func (f) -> (f)`) now works. `CandidDisplayReactor.initialize()` no
|
|
3073
|
+
longer fails for the whole service, and a method whose display codec cannot be
|
|
3074
|
+
built is skipped with an error naming it. `MetadataReactor` and
|
|
3075
|
+
`MetadataDisplayReactor` no longer throw "Cannot read properties of undefined
|
|
3076
|
+
(reading 'includes')", and they describe, hydrate and offer candidates for
|
|
3077
|
+
that method (#557).
|
|
3078
|
+
- Names from `Object.prototype`: `getInputMeta` and `getOutputMeta` return
|
|
3079
|
+
`undefined` for a name such as `"toString"`, `"constructor"` or `"__proto__"`
|
|
3080
|
+
that the service does not declare. A method, record field or variant tag named
|
|
3081
|
+
`__proto__` is now kept in the metadata, defaults, schema, result tree and
|
|
3082
|
+
hydration. A left-out field named like an inherited member resolves as
|
|
3083
|
+
missing, and resolving a variant tag that the type does not have throws
|
|
3084
|
+
`Option "<tag>" not found` (#610, #659).
|
|
3085
|
+
- A result node's `resolve()` accepts values already in display form: an `opt`
|
|
3086
|
+
whose display value is an array (`opt vec text` as `["a", "b"]`) and a
|
|
3087
|
+
`vec record { text; V }` shown as an object. A null variant arm now resolves
|
|
3088
|
+
to `null` instead of `undefined` (#546).
|
|
3089
|
+
- `buildMethodVariableCandidates` now opens a recursive result, so an ICRC-3
|
|
3090
|
+
ledger's `icrc3_get_blocks` offers `.log_length`, `.blocks` and
|
|
3091
|
+
`.archived_blocks`. It names a func reference's members by index
|
|
3092
|
+
(`$get_callback.0`, `$get_callback.1`), which the value resolves by, instead
|
|
3093
|
+
of `.canisterId` and `.methodName`, which resolved to `undefined`.
|
|
3094
|
+
- `MetadataReactor` and `MetadataDisplayReactor` now use far less memory and
|
|
3095
|
+
time: a dropped reactor's service types are freed (about 42 KB each were kept
|
|
3096
|
+
before); `registerMethod` describes only the method it adds, and a repeat
|
|
3097
|
+
registration, which every `callDynamic`, `queryDynamic`, `fetchQueryDynamic`
|
|
3098
|
+
and `callDynamicWithMeta` makes, changes nothing (600 registrations went from
|
|
3099
|
+
11.7 s to 148 ms); and resolving a recursive value builds one result node per
|
|
3100
|
+
type and label instead of one per value (a 2,000-element Motoko List kept 7.6
|
|
3101
|
+
MB).
|
|
3102
|
+
- Candid reactors no longer leave an identity subscription on their
|
|
3103
|
+
`ClientManager` for good, and an assigned `adapter.didjsCanisterId` survives
|
|
3104
|
+
identity changes (#644).
|
|
3105
|
+
- Loading Candid: "Failed to retrieve Candid source by any method" now says why
|
|
3106
|
+
each attempt failed (#541). A rejected query from the tmp-hack fetch or the
|
|
3107
|
+
didjs compile reports "Query failed (reject code N, error code ICnnnn):
|
|
3108
|
+
<message>" instead of "Do not know how to serialize a BigInt". A parse that
|
|
3109
|
+
starts while the parser is still loading now waits for it instead of sending
|
|
3110
|
+
the Candid to the didjs canister, which failed offline and on a local replica.
|
|
3111
|
+
`importCandidDefinition` (used by `CandidReactor` and `CandidDisplayReactor`)
|
|
3112
|
+
no longer rewrites a Candid name that contains `export const` or
|
|
3113
|
+
`export function`, which sent that field under another hash.
|
|
3114
|
+
|
|
3115
|
+
## codegen, cli, vite-plugin 0.15.1
|
|
3116
|
+
|
|
3117
|
+
Changes since codegen, cli and vite-plugin 0.14.0. Version 0.15.0 was tagged
|
|
3118
|
+
but never published: its release job stopped before publishing, because
|
|
3119
|
+
codegen's tests needed packages that job did not build. 0.15.1 is the first
|
|
3120
|
+
release with these changes.
|
|
3121
|
+
|
|
3122
|
+
### @ic-reactor/codegen
|
|
3123
|
+
|
|
3124
|
+
#### Added
|
|
3125
|
+
|
|
3126
|
+
- `factories: true` on a canister config (with `target: "react"`) writes a
|
|
3127
|
+
managed `index.factories.generated.ts`: `<method>Query` (`createQuery`, or
|
|
3128
|
+
`createQueryFactory` when the method takes args) per query method and
|
|
3129
|
+
`<method>Mutation` (`createMutation`) per update or oneway method, each call
|
|
3130
|
+
marked `/* @__PURE__ */`. Exports `getFactoryExportNames`,
|
|
3131
|
+
`generateFactoriesFile` and `FACTORIES_FILE_NAME`.
|
|
3132
|
+
- `PipelineResult.warnings`, for problems a successful run could not fix, such
|
|
3133
|
+
as an edited `index.ts` that does not re-export the factories.
|
|
3134
|
+
- `findSharedOutDirs` and `sharedOutDirMessage`, moved here from the CLI.
|
|
3135
|
+
|
|
3136
|
+
#### Changed
|
|
3137
|
+
|
|
3138
|
+
- `index.generated.ts`, and the `index.ts` wrapper that generation creates, are
|
|
3139
|
+
formatted with the project's Prettier and config, like the declarations.
|
|
3140
|
+
`prettier --check` passes right after generation, and a formatted file is no
|
|
3141
|
+
longer rewritten on the next run. Migration: none; commit the reformatted
|
|
3142
|
+
`index.generated.ts` that the next run writes.
|
|
3143
|
+
- The `index.ts` that generation creates once now points agents at the
|
|
3144
|
+
`ic-reactor` Agent Skill published from this repository
|
|
3145
|
+
(`/plugin install ic-reactor@ic-reactor` in Claude Code after
|
|
3146
|
+
`/plugin marketplace add B3Pay/ic-reactor`, or
|
|
3147
|
+
`npx skills add B3Pay/ic-reactor --skill ic-reactor`) instead of the
|
|
3148
|
+
contributor skill in `B3Pay/ic-reactor-skills`. Migration: none; an existing
|
|
3149
|
+
`index.ts` is never rewritten, so edit its comment by hand if you want the
|
|
3150
|
+
new pointer.
|
|
3151
|
+
- A `didFile` inside `<outDir>/declarations` (the generated declarations
|
|
3152
|
+
directory) is refused with an error before anything is written. Before, the
|
|
3153
|
+
first run deleted a `.did` kept in a folder there, and every later run failed
|
|
3154
|
+
with "DID file not found". Migration: move the `.did` out of
|
|
3155
|
+
`<outDir>/declarations` and point `didFile` at it;
|
|
3156
|
+
`src/declarations/<canister>/<canister>.did` still works.
|
|
3157
|
+
|
|
3158
|
+
#### Fixed
|
|
3159
|
+
|
|
3160
|
+
- With `mode` set to `CandidReactor`, `CandidDisplayReactor` or
|
|
3161
|
+
`MetadataDisplayReactor`, the generated `index.generated.ts` now passes
|
|
3162
|
+
`createActorHooks` its type arguments. A service with a recursive type
|
|
3163
|
+
(Motoko's `List`) or one as large as Internet Identity's no longer fails `tsc`
|
|
3164
|
+
with TS2589. The hooks' types are unchanged (#503).
|
|
3165
|
+
- A run now leaves alone every generated file whose bytes are unchanged,
|
|
3166
|
+
`declarations/` included. `tsc --watch`, bundlers and editors no longer wake
|
|
3167
|
+
on a regeneration that changed nothing (#666).
|
|
3168
|
+
- A `.did` saved with a UTF-8 byte order mark (PowerShell, or "UTF-8 with BOM"
|
|
3169
|
+
editors) now generates instead of failing with `Unknown token <U+FEFF>`. The
|
|
3170
|
+
`declarations/` copy keeps the file's bytes.
|
|
3171
|
+
|
|
3172
|
+
### @ic-reactor/cli
|
|
3173
|
+
|
|
3174
|
+
#### Added
|
|
3175
|
+
|
|
3176
|
+
- `"factories"` per canister in `ic-reactor.json` and `schema.json`. A value
|
|
3177
|
+
that is not a boolean is rejected. `generate` prints codegen's warnings.
|
|
3178
|
+
|
|
3179
|
+
#### Fixed
|
|
3180
|
+
|
|
3181
|
+
- An `ic-reactor.json` saved with a UTF-8 byte order mark (such as PowerShell's
|
|
3182
|
+
`Set-Content -Encoding UTF8` writes) now loads instead of failing every
|
|
3183
|
+
command with "not valid JSON" (#665).
|
|
3184
|
+
- `generate --clean` no longer deletes the output of a configured canister whose
|
|
3185
|
+
`outDir` reaches the global `outDir` through a symlink, or differs from it
|
|
3186
|
+
only in case on a case-insensitive volume. That output includes the
|
|
3187
|
+
hand-written `index.ts` (#505).
|
|
3188
|
+
- `init -y` with an `--out-dir` that is not directly inside `src/` now writes
|
|
3189
|
+
the `clientManagerPath` that reaches `src/clients.ts`, so the first `generate`
|
|
3190
|
+
output compiles instead of failing with TS2307 (#559).
|
|
3191
|
+
- `generate --canister toString` (or `constructor`, `hasOwnProperty`,
|
|
3192
|
+
`__proto__`) now reports "Canister <name> not found in config." instead of an
|
|
3193
|
+
unrelated error. The `--bindgen-only` help now names the files it writes
|
|
3194
|
+
instead of a `.did.d.ts` that is never written (#666).
|
|
3195
|
+
|
|
3196
|
+
### @ic-reactor/vite-plugin
|
|
3197
|
+
|
|
3198
|
+
#### Added
|
|
3199
|
+
|
|
3200
|
+
- `factories` per canister entry. The plugin prints codegen's warnings to the
|
|
3201
|
+
terminal without failing the build.
|
|
3202
|
+
|
|
3203
|
+
#### Changed
|
|
3204
|
+
|
|
3205
|
+
- An entry that shares its output directory with an earlier one (same `name`
|
|
3206
|
+
and `outDir`) fails, as it does in the CLI; under `vite build` that fails the
|
|
3207
|
+
build (#565). Migration: give each entry its own `outDir`.
|
|
3208
|
+
- `injectEnvironment` sets the `ic_env` cookie on each response. While `icp`
|
|
3209
|
+
reports no network or a configured canister has no id, each page load asks
|
|
3210
|
+
`icp` again, so deploying after `vite dev` started needs only a reload
|
|
3211
|
+
(#664). The `/api` proxy follows the detected network unless the Vite config
|
|
3212
|
+
or another plugin sets its own `/api` proxy. `vite preview` gets the same
|
|
3213
|
+
cookie. Migration: none.
|
|
3214
|
+
|
|
3215
|
+
#### Fixed
|
|
3216
|
+
|
|
3217
|
+
- Under `vite dev`, the plugin now regenerates from the dev server's file
|
|
3218
|
+
watcher. A `.did` created after startup now regenerates, and so does one
|
|
3219
|
+
deleted and written again by a build tool or `git checkout`. Saves with
|
|
3220
|
+
`server.hmr: false` also regenerate now.
|
|
3221
|
+
- Under `vite build --watch`, saving a `.did` now rebuilds with the new
|
|
3222
|
+
bindings, and editing another file no longer starts rebuilds that never end. A
|
|
3223
|
+
watch rebuild regenerates only the entries whose `.did` changed, and it
|
|
3224
|
+
retries an entry that failed.
|
|
3225
|
+
- Two entries with the same canister `name` (for example one `DisplayReactor`
|
|
3226
|
+
and one `Reactor` output in different `outDir`s) now both regenerate when
|
|
3227
|
+
their `.did` is saved. The second entry's failure is still shown to browsers
|
|
3228
|
+
that connect later (#504).
|
|
3229
|
+
|
|
3230
|
+
## parser 0.6.0
|
|
3231
|
+
|
|
3232
|
+
Changes since parser 0.5.0.
|
|
3233
|
+
|
|
3234
|
+
### @ic-reactor/parser
|
|
3235
|
+
|
|
3236
|
+
#### Added
|
|
3237
|
+
|
|
3238
|
+
- The package ships `llms.txt`, a usage guide for coding agents, as the other
|
|
3239
|
+
packages do (`node_modules/@ic-reactor/parser/llms.txt`).
|
|
3240
|
+
|
|
3241
|
+
#### Changed
|
|
3242
|
+
|
|
3243
|
+
- `didToJs` and `didToTs` key a record field or variant tag whose name looks
|
|
3244
|
+
like a numeric id (`_0_`) by the hash of its name (`_4735054_`), and
|
|
3245
|
+
`parseDid` names it the same way, matching what a canister expects (#631).
|
|
3246
|
+
Migration: a hand-written `.did` that spells a Motoko numeric field `_0_`
|
|
3247
|
+
must write `0`.
|
|
3248
|
+
- A Candid type named `_SERVICE`, like a TypeScript type keyword (`string`,
|
|
3249
|
+
`number`, `bigint`, ...) or like a type operator (`keyof`, `readonly`,
|
|
3250
|
+
`unique`, `infer`) is renamed with `_` appended, e.g. `string_` (#737).
|
|
3251
|
+
Migration: import the renamed type.
|
|
3252
|
+
|
|
3253
|
+
#### Fixed
|
|
3254
|
+
|
|
3255
|
+
- `didToJs` and `didToTs` output now loads and compiles in more cases. A field,
|
|
3256
|
+
variant tag or method named `__proto__` is kept, printed as `['__proto__']`
|
|
3257
|
+
(#560). U+0000 in a name prints as `\x00` instead of `\0`, which read as an
|
|
3258
|
+
octal escape: it failed in strict mode or changed the field's hash. A type
|
|
3259
|
+
named `IDL`, `Principal`, `ActorMethod`, `Array` or a typed array no longer
|
|
3260
|
+
clashes with the binding's own names. A service whose actor type is named like
|
|
3261
|
+
a JavaScript keyword (`type class = service {…}; service : class`) now refers
|
|
3262
|
+
to the printed `class_` (#738).
|
|
3263
|
+
- `parseDid` returns `service: null`, as its type says, instead of `undefined`
|
|
3264
|
+
for Candid that declares no service (#506). Code that checks `=== undefined`
|
|
3265
|
+
must check `null` instead; `== null` works either way.
|
|
3266
|
+
- Docs: the README now says `validateIDL` returns `true` or throws the parser's
|
|
3267
|
+
error as a string. It never returned `false` as the README had said; use
|
|
3268
|
+
`CandidAdapter.validateCandid` for a boolean.
|