@ic-reactor/mcp 0.0.0-stage → 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.yml +108 -0
- package/.github/ISSUE_TEMPLATE/config.yml +8 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +77 -0
- package/.github/actions/setup-rust-wasm/action.yml +25 -0
- package/.github/dependabot.yml +107 -0
- package/.github/workflows/auth-latest.yml +67 -0
- package/.github/workflows/b3pay-site.yml +49 -0
- package/.github/workflows/ci.yml +430 -0
- package/.github/workflows/codeql.yml +41 -0
- package/.github/workflows/dependency-review.yml +68 -0
- package/.github/workflows/docs-check.yml +98 -0
- package/.github/workflows/docs.yml +265 -0
- package/.github/workflows/e2e.yml +90 -0
- package/.github/workflows/examples-published.yml +97 -0
- package/.github/workflows/release.yml +326 -0
- package/.husky/pre-commit +12 -0
- package/.prettierignore +109 -0
- package/.prettierrc +6 -0
- package/AGENTS.md +103 -0
- package/CHANGELOG.md +3268 -0
- package/CLAUDE.md +93 -0
- package/CNAME +1 -0
- package/CODE_OF_CONDUCT.md +7 -0
- package/CONTRIBUTING.md +433 -0
- package/LICENSE +21 -0
- package/README.md +2 -2
- package/SECURITY.md +74 -0
- package/b3pay-module.json +194 -0
- package/docs/.remarkrc.json +10 -0
- package/docs/README.md +64 -0
- package/docs/astro.config.mjs +205 -0
- package/docs/favicon.svg +15 -0
- package/docs/package.json +39 -0
- package/docs/public/favicon.svg +15 -0
- package/docs/public/ic-reactor-social-card.png +0 -0
- package/docs/scripts/b3pay-static.mjs +22 -0
- package/docs/scripts/check-example-docs.mjs +214 -0
- package/docs/scripts/check-links.mjs +212 -0
- package/docs/scripts/fix-dist-links.js +125 -0
- package/docs/scripts/fix-frontmatter.js +71 -0
- package/docs/scripts/remove-redundant-h1.cjs +48 -0
- package/docs/scripts/rewrite-typedoc-links.js +110 -0
- package/docs/scripts/split-index.js +69 -0
- package/docs/src/assets/ic-reactor-hero-dark.svg +23 -0
- package/docs/src/assets/ic-reactor-hero-light.svg +23 -0
- package/docs/src/assets/ic-reactor-lockup-dark.svg +15 -0
- package/docs/src/assets/ic-reactor-lockup-light.svg +15 -0
- package/docs/src/assets/ic-reactor-mark-dark.svg +14 -0
- package/docs/src/assets/ic-reactor-mark-light.svg +14 -0
- package/docs/src/assets/layers/candid-core-layer-icon-dark.svg +4 -0
- package/docs/src/assets/layers/candid-core-layer-icon-light.svg +4 -0
- package/docs/src/assets/layers/candid-profile-layer-icon-dark.svg +4 -0
- package/docs/src/assets/layers/candid-profile-layer-icon-light.svg +4 -0
- package/docs/src/assets/layers/ic-reactor-layer-icon-dark.svg +4 -0
- package/docs/src/assets/layers/ic-reactor-layer-icon-light.svg +4 -0
- package/docs/src/components/LibraryCards.astro +172 -0
- package/docs/src/components/LibraryIcon.astro +28 -0
- package/docs/src/components/SiteTitle.astro +241 -0
- package/docs/src/content/docs/404.mdx +19 -0
- package/docs/src/content/docs/build/agent-tools.mdx +593 -0
- package/docs/src/content/docs/build/icrc1-wallet.mdx +619 -0
- package/docs/src/content/docs/build/ssr-per-user.mdx +552 -0
- package/docs/src/content/docs/examples/canister-studio.mdx +95 -0
- package/docs/src/content/docs/examples/icrc-ledger.mdx +45 -0
- package/docs/src/content/docs/examples/index.mdx +38 -0
- package/docs/src/content/docs/examples/next-ssr.mdx +73 -0
- package/docs/src/content/docs/examples/node-agent-tool.mdx +80 -0
- package/docs/src/content/docs/examples/vite-wallet.mdx +71 -0
- package/docs/src/content/docs/guides/auth.mdx +1181 -0
- package/docs/src/content/docs/guides/client.mdx +551 -0
- package/docs/src/content/docs/guides/deploy.mdx +180 -0
- package/docs/src/content/docs/guides/errors.mdx +475 -0
- package/docs/src/content/docs/guides/getting-started.mdx +620 -0
- package/docs/src/content/docs/guides/reads.mdx +586 -0
- package/docs/src/content/docs/guides/ssr.mdx +529 -0
- package/docs/src/content/docs/guides/testing.mdx +401 -0
- package/docs/src/content/docs/guides/values.mdx +261 -0
- package/docs/src/content/docs/guides/vite-plugin.mdx +690 -0
- package/docs/src/content/docs/guides/writes.mdx +572 -0
- package/docs/src/content/docs/ic-reactor/index.mdx +167 -0
- package/docs/src/content/docs/index.mdx +65 -0
- package/docs/src/content/docs/migrating-from-3.mdx +535 -0
- package/docs/src/content/docs/packages/core.mdx +186 -0
- package/docs/src/content/docs/packages/mcp.mdx +163 -0
- package/docs/src/content/docs/packages/react.mdx +207 -0
- package/docs/src/content/docs/packages/vite-plugin.mdx +73 -0
- package/docs/src/content/docs/versioning.mdx +104 -0
- package/docs/src/content.config.ts +7 -0
- package/docs/src/styles/custom.css +189 -0
- package/docs/tsconfig.typedoc.json +26 -0
- package/docs/typedoc-entry.ts +17 -0
- package/docs/typedoc.json +37 -0
- package/e2e/Cargo.lock +646 -0
- package/e2e/Cargo.toml +3 -0
- package/e2e/README.md +101 -0
- package/e2e/frontends/build.mjs +94 -0
- package/e2e/frontends/index.html +10 -0
- package/e2e/global-setup.ts +65 -0
- package/e2e/icp.yaml +38 -0
- package/e2e/package.json +40 -0
- package/e2e/setup.ts +6 -0
- package/e2e/src/actor/Cargo.toml +14 -0
- package/e2e/src/actor/hello_actor.did +10 -0
- package/e2e/src/actor/src/lib.rs +73 -0
- package/e2e/src/calls.test.ts +132 -0
- package/e2e/src/declarations/hello_actor.envelope.json +175 -0
- package/e2e/src/declarations/hello_actor.ts +15 -0
- package/e2e/src/errors.test.ts +107 -0
- package/e2e/src/frontend-headers.test.ts +253 -0
- package/e2e/src/identity.test.ts +61 -0
- package/e2e/src/react.test.tsx +59 -0
- package/e2e/src/replica.ts +70 -0
- package/e2e/src/vite-env.test.ts +108 -0
- package/e2e/test.sh +47 -0
- package/e2e/tsconfig.json +20 -0
- package/e2e/tsconfig.typecheck.json +9 -0
- package/e2e/vitest.config.ts +22 -0
- package/eslint.config.mjs +100 -0
- package/evals/PREREGISTRATION.md +1346 -0
- package/evals/README.md +756 -0
- package/evals/conditions/thin/condition.json +8 -0
- package/evals/conditions/thin/docs/candid-core-cli.README.md +90 -0
- package/evals/conditions/thin/docs/candid-core-schema.README.md +339 -0
- package/evals/conditions/thin/package.json +25 -0
- package/evals/conditions/thin/starter/src/generated/icrc1.ts +57 -0
- package/evals/conditions/thin-guide/condition.json +10 -0
- package/evals/conditions/thin-guide/docs/llms.txt +292 -0
- package/evals/conditions/v3/condition.json +9 -0
- package/evals/conditions/v3/docs/ic-reactor-core.llms.txt +232 -0
- package/evals/conditions/v3/docs/ic-reactor-react.llms.txt +280 -0
- package/evals/conditions/v3/docs/llms-full.txt +1053 -0
- package/evals/conditions/v3/package.json +26 -0
- package/evals/conditions/v3/starter/src/declarations/icrc1.did.d.ts +95 -0
- package/evals/conditions/v3/starter/src/declarations/icrc1.did.js +140 -0
- package/evals/conditions/v4/PORTING.md +384 -0
- package/evals/conditions/v4/README.md +106 -0
- package/evals/conditions/v4/condition.json +5 -0
- package/evals/conditions/v4/docs/llms.txt +323 -0
- package/evals/conditions/v4/package.json +27 -0
- package/evals/conditions/v4/starter/src/generated/icrc1.ts +78 -0
- package/evals/conditions/v4-proto/condition.json +6 -0
- package/evals/conditions/v4-proto/docs/llms.txt +291 -0
- package/evals/conditions/v4-proto/lib/SHIM-NOTES.md +20 -0
- package/evals/conditions/v4-proto/lib/package.json +27 -0
- package/evals/conditions/v4-proto/lib/src/client.ts +471 -0
- package/evals/conditions/v4-proto/lib/src/errors.ts +214 -0
- package/evals/conditions/v4-proto/lib/src/index.ts +36 -0
- package/evals/conditions/v4-proto/lib/src/principal.ts +175 -0
- package/evals/conditions/v4-proto/lib/src/service.ts +69 -0
- package/evals/conditions/v4-proto/lib/src/types.ts +174 -0
- package/evals/conditions/v4-proto/lib/test/runtime.test.ts +170 -0
- package/evals/conditions/v4-proto/lib/test/tsconfig.json +10 -0
- package/evals/conditions/v4-proto/lib/test/types.test-d.ts +145 -0
- package/evals/conditions/v4-proto/lib/test/vitest.config.mjs +8 -0
- package/evals/conditions/v4-proto/lib/tsconfig.json +17 -0
- package/evals/conditions/v4-proto/package.json +26 -0
- package/evals/conditions/v4-proto/starter/src/generated/icrc1.service.ts +19 -0
- package/evals/conditions/v4-proto/starter/src/generated/icrc1.ts +57 -0
- package/evals/drive.mjs +1221 -0
- package/evals/gate.mjs +161 -0
- package/evals/harness/aggregate.mjs +534 -0
- package/evals/harness/aggregate.test.mjs +344 -0
- package/evals/harness/assemble.mjs +216 -0
- package/evals/harness/assemble.test.mjs +143 -0
- package/evals/harness/auth.mjs +91 -0
- package/evals/harness/check-docs.mjs +157 -0
- package/evals/harness/declarations/icrc1.did.d.ts +95 -0
- package/evals/harness/declarations/icrc1.did.js +140 -0
- package/evals/harness/drive-auth.test.mjs +584 -0
- package/evals/harness/drive.test.mjs +509 -0
- package/evals/harness/fake-auth.ts +67 -0
- package/evals/harness/fake-ledger.ts +178 -0
- package/evals/harness/gate-plan.mjs +104 -0
- package/evals/harness/gate-plan.test.mjs +243 -0
- package/evals/harness/icrc1.did +88 -0
- package/evals/harness/judge.mjs +63 -0
- package/evals/harness/judge.test.mjs +81 -0
- package/evals/harness/leak-scan.mjs +1373 -0
- package/evals/harness/leak-scan.test.mjs +1300 -0
- package/evals/harness/probes/amounts.ts +14 -0
- package/evals/harness/probes/node-tool.probe.ts +48 -0
- package/evals/harness/probes/probe.mjs +149 -0
- package/evals/harness/probes/react-wallet.probe.ts +96 -0
- package/evals/harness/public/support/auth.ts +38 -0
- package/evals/harness/public/support/fake-replica.d.ts +28 -0
- package/evals/harness/public/support/fake-replica.js +434 -0
- package/evals/harness/public/support/ledger.idl.d.ts +95 -0
- package/evals/harness/public/support/ledger.idl.js +140 -0
- package/evals/harness/public/support/world.ts +179 -0
- package/evals/harness/runs.mjs +139 -0
- package/evals/harness/sandbox.mjs +88 -0
- package/evals/harness/sandbox.test.mjs +134 -0
- package/evals/harness/ship.mjs +264 -0
- package/evals/harness/ship.test.mjs +552 -0
- package/evals/harness/world.ts +291 -0
- package/evals/package.json +26 -0
- package/evals/pnpm-lock.yaml +1537 -0
- package/evals/pnpm-workspace.yaml +12 -0
- package/evals/results/2026-09-30T14-22-05-924Z/pilot.json +384 -0
- package/evals/results/2026-09-30T14-22-05-924Z/pilot.rescanned.json +404 -0
- package/evals/results/2026-09-30T14-22-05-924Z/pilot.rescanned.rescored.json +404 -0
- package/evals/results/2026-09-30T14-22-05-924Z/plan.json +240 -0
- package/evals/results/2026-09-30T14-22-05-924Z/summary.json +1800 -0
- package/evals/results/2026-09-30T14-22-05-924Z/summary.rescanned.json +1843 -0
- package/evals/results/2026-09-30T14-22-05-924Z/summary.rescanned.rescored.json +2100 -0
- package/evals/results/2026-09-30T15-48-23-343Z/pilot.json +404 -0
- package/evals/results/2026-09-30T15-48-23-343Z/pilot.rescored.json +404 -0
- package/evals/results/2026-09-30T15-48-23-343Z/plan.json +242 -0
- package/evals/results/2026-09-30T15-48-23-343Z/summary.json +1843 -0
- package/evals/results/2026-09-30T15-48-23-343Z/summary.rescored.json +2195 -0
- package/evals/results/2026-10-03-beta1-gate/pilot.json +162 -0
- package/evals/results/2026-10-03-beta1-gate/plan.json +141 -0
- package/evals/results/2026-10-03-beta1-gate/summary.json +983 -0
- package/evals/results/2026-10-05-ga-gate/contaminated-run.json +55 -0
- package/evals/results/2026-10-05-ga-gate/pilot.json +162 -0
- package/evals/results/2026-10-05-ga-gate/plan.json +457 -0
- package/evals/results/2026-10-05-ga-gate/summary.json +1037 -0
- package/evals/results/2026-10-05-ga-gate/summary.rescanned.json +1037 -0
- package/evals/results/README.md +31 -0
- package/evals/runs-pilot.log +150 -0
- package/evals/runs-pilot2.log +150 -0
- package/evals/score.mjs +256 -0
- package/evals/setup.mjs +547 -0
- package/evals/tasks/node-tool/faulty/thin-accepts-exponent/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-accepts-exponent/src/index.ts +121 -0
- package/evals/tasks/node-tool/faulty/thin-anonymous-transfer/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-anonymous-transfer/src/index.ts +116 -0
- package/evals/tasks/node-tool/faulty/thin-every-reject-may-have-executed/meta.json +9 -0
- package/evals/tasks/node-tool/faulty/thin-every-reject-may-have-executed/src/index.ts +118 -0
- package/evals/tasks/node-tool/faulty/thin-fetches-root-key-off-local/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-fetches-root-key-off-local/src/index.ts +118 -0
- package/evals/tasks/node-tool/faulty/thin-ignores-root-key/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-ignores-root-key/src/index.ts +117 -0
- package/evals/tasks/node-tool/faulty/thin-misclassifies-reject-codes/meta.json +9 -0
- package/evals/tasks/node-tool/faulty/thin-misclassifies-reject-codes/src/index.ts +119 -0
- package/evals/tasks/node-tool/faulty/thin-no-nat64-cap/meta.json +9 -0
- package/evals/tasks/node-tool/faulty/thin-no-nat64-cap/src/index.ts +118 -0
- package/evals/tasks/node-tool/faulty/thin-number-amount/meta.json +10 -0
- package/evals/tasks/node-tool/faulty/thin-number-amount/src/index.ts +117 -0
- package/evals/tasks/node-tool/faulty/thin-truncates-fraction-digits/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-truncates-fraction-digits/src/index.ts +120 -0
- package/evals/tasks/node-tool/faulty/thin-unvalidated-recipient/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/thin-unvalidated-recipient/src/index.ts +118 -0
- package/evals/tasks/node-tool/faulty/v3-retry-call-errors/meta.json +11 -0
- package/evals/tasks/node-tool/faulty/v3-retry-call-errors/src/index.ts +149 -0
- package/evals/tasks/node-tool/faulty/v4-anonymous-identity-sent/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/v4-anonymous-identity-sent/src/index.ts +71 -0
- package/evals/tasks/node-tool/faulty/v4-never-may-have-executed/meta.json +9 -0
- package/evals/tasks/node-tool/faulty/v4-never-may-have-executed/src/index.ts +84 -0
- package/evals/tasks/node-tool/faulty/v4-proto-never-may-have-executed/meta.json +9 -0
- package/evals/tasks/node-tool/faulty/v4-proto-never-may-have-executed/src/index.ts +72 -0
- package/evals/tasks/node-tool/faulty/v4-proto-refuses-nat64-max/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/v4-proto-refuses-nat64-max/src/index.ts +72 -0
- package/evals/tasks/node-tool/faulty/v4-refuses-nat64-max/meta.json +5 -0
- package/evals/tasks/node-tool/faulty/v4-refuses-nat64-max/src/index.ts +84 -0
- package/evals/tasks/node-tool/hidden/node-tool.test.ts +312 -0
- package/evals/tasks/node-tool/prompt.md +55 -0
- package/evals/tasks/node-tool/prompt.minimal.md +30 -0
- package/evals/tasks/node-tool/solutions/thin/reference/src/index.ts +118 -0
- package/evals/tasks/node-tool/solutions/thin/reference-module-scope/src/index.ts +145 -0
- package/evals/tasks/node-tool/solutions/thin-guide/reference/src/index.ts +118 -0
- package/evals/tasks/node-tool/solutions/thin-guide/reference-module-scope/src/index.ts +145 -0
- package/evals/tasks/node-tool/solutions/v3/reference/src/index.ts +140 -0
- package/evals/tasks/node-tool/solutions/v3/reference-module-scope/src/index.ts +153 -0
- package/evals/tasks/node-tool/solutions/v4/reference/src/index.ts +84 -0
- package/evals/tasks/node-tool/solutions/v4/reference-module-scope/src/index.ts +105 -0
- package/evals/tasks/node-tool/solutions/v4-proto/reference/src/index.ts +72 -0
- package/evals/tasks/node-tool/solutions/v4-proto/reference-module-scope/src/index.ts +87 -0
- package/evals/tasks/node-tool/starter/src/contract.ts +31 -0
- package/evals/tasks/node-tool/starter/src/index.ts +7 -0
- package/evals/tasks/node-tool/starter/test/smoke.test.ts +22 -0
- package/evals/tasks/node-tool/starter/tsconfig.json +17 -0
- package/evals/tasks/node-tool/task.json +63 -0
- package/evals/tasks/react-wallet/faulty/thin-accepts-exponent/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-accepts-exponent/src/Wallet.tsx +216 -0
- package/evals/tasks/react-wallet/faulty/thin-anonymous-transfer/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-anonymous-transfer/src/Wallet.tsx +212 -0
- package/evals/tasks/react-wallet/faulty/thin-both-auth-buttons/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-both-auth-buttons/src/Wallet.tsx +210 -0
- package/evals/tasks/react-wallet/faulty/thin-constant-balance-key/meta.json +8 -0
- package/evals/tasks/react-wallet/faulty/thin-constant-balance-key/src/Wallet.tsx +213 -0
- package/evals/tasks/react-wallet/faulty/thin-no-nat64-cap/meta.json +9 -0
- package/evals/tasks/react-wallet/faulty/thin-no-nat64-cap/src/Wallet.tsx +213 -0
- package/evals/tasks/react-wallet/faulty/thin-number-balance/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-number-balance/src/Wallet.tsx +213 -0
- package/evals/tasks/react-wallet/faulty/thin-transfer-as-query/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-transfer-as-query/src/Wallet.tsx +254 -0
- package/evals/tasks/react-wallet/faulty/thin-truncates-fraction-digits/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-truncates-fraction-digits/src/Wallet.tsx +215 -0
- package/evals/tasks/react-wallet/faulty/thin-unguarded-balance-effect/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/thin-unguarded-balance-effect/src/Wallet.tsx +214 -0
- package/evals/tasks/react-wallet/faulty/v3-http-4xx-unknown/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v3-http-4xx-unknown/src/Wallet.tsx +224 -0
- package/evals/tasks/react-wallet/faulty/v3-mutation-retry-3/meta.json +12 -0
- package/evals/tasks/react-wallet/faulty/v3-mutation-retry-3/src/Wallet.tsx +225 -0
- package/evals/tasks/react-wallet/faulty/v3-unknown-as-error/meta.json +11 -0
- package/evals/tasks/react-wallet/faulty/v3-unknown-as-error/src/Wallet.tsx +182 -0
- package/evals/tasks/react-wallet/faulty/v4-every-reject-unknown/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-every-reject-unknown/src/Wallet.tsx +160 -0
- package/evals/tasks/react-wallet/faulty/v4-keep-previous-data/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-keep-previous-data/src/Wallet.tsx +166 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-every-reject-unknown/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-every-reject-unknown/src/Wallet.tsx +161 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-keep-previous-data/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-keep-previous-data/src/Wallet.tsx +163 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-retry-spread/meta.json +12 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-retry-spread/src/Wallet.tsx +162 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-status-not-idle/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-proto-status-not-idle/src/Wallet.tsx +163 -0
- package/evals/tasks/react-wallet/faulty/v4-retry-spread/meta.json +12 -0
- package/evals/tasks/react-wallet/faulty/v4-retry-spread/src/Wallet.tsx +163 -0
- package/evals/tasks/react-wallet/faulty/v4-status-not-idle/meta.json +5 -0
- package/evals/tasks/react-wallet/faulty/v4-status-not-idle/src/Wallet.tsx +162 -0
- package/evals/tasks/react-wallet/hidden/react-wallet.test.ts +536 -0
- package/evals/tasks/react-wallet/prompt.md +88 -0
- package/evals/tasks/react-wallet/prompt.minimal.md +51 -0
- package/evals/tasks/react-wallet/solutions/thin/reference/src/Wallet.tsx +213 -0
- package/evals/tasks/react-wallet/solutions/thin/reference-module-scope/src/Wallet.tsx +227 -0
- package/evals/tasks/react-wallet/solutions/thin-guide/reference/src/Wallet.tsx +213 -0
- package/evals/tasks/react-wallet/solutions/thin-guide/reference-module-scope/src/Wallet.tsx +227 -0
- package/evals/tasks/react-wallet/solutions/v3/reference/src/Wallet.tsx +224 -0
- package/evals/tasks/react-wallet/solutions/v3/reference-module-scope/src/Wallet.tsx +230 -0
- package/evals/tasks/react-wallet/solutions/v4/reference/src/Wallet.tsx +160 -0
- package/evals/tasks/react-wallet/solutions/v4/reference-module-scope/src/Wallet.tsx +178 -0
- package/evals/tasks/react-wallet/solutions/v4-proto/reference/src/Wallet.tsx +161 -0
- package/evals/tasks/react-wallet/solutions/v4-proto/reference-module-scope/src/Wallet.tsx +161 -0
- package/evals/tasks/react-wallet/starter/src/Wallet.tsx +12 -0
- package/evals/tasks/react-wallet/starter/src/auth.ts +16 -0
- package/evals/tasks/react-wallet/starter/src/config.ts +14 -0
- package/evals/tasks/react-wallet/starter/src/ii-auth.ts +43 -0
- package/evals/tasks/react-wallet/starter/src/main.tsx +15 -0
- package/evals/tasks/react-wallet/starter/test/smoke.test.ts +34 -0
- package/evals/tasks/react-wallet/starter/tsconfig.json +18 -0
- package/evals/tasks/react-wallet/task.json +84 -0
- package/examples/canister-studio/.stackblitzrc +4 -0
- package/examples/canister-studio/README.md +183 -0
- package/examples/canister-studio/did/ckbtc_minter.did +806 -0
- package/examples/canister-studio/did/icp_ledger.did +586 -0
- package/examples/canister-studio/did/icrc1.did +88 -0
- package/examples/canister-studio/package.json +45 -0
- package/examples/canister-studio/scripts/smoke.ts +188 -0
- package/examples/canister-studio/src/calls/CacheKeyPanel.tsx +104 -0
- package/examples/canister-studio/src/calls/ConfirmWrite.tsx +265 -0
- package/examples/canister-studio/src/calls/ErrorPanel.test.tsx +113 -0
- package/examples/canister-studio/src/calls/ErrorPanel.tsx +214 -0
- package/examples/canister-studio/src/calls/MethodRunner.test.tsx +354 -0
- package/examples/canister-studio/src/calls/MethodRunner.tsx +57 -0
- package/examples/canister-studio/src/calls/ReadRunner.tsx +244 -0
- package/examples/canister-studio/src/calls/ResendPanel.tsx +165 -0
- package/examples/canister-studio/src/calls/ShareLink.tsx +59 -0
- package/examples/canister-studio/src/calls/WriteRunner.test.tsx +678 -0
- package/examples/canister-studio/src/calls/WriteRunner.tsx +670 -0
- package/examples/canister-studio/src/calls/dedupe.ts +189 -0
- package/examples/canister-studio/src/calls/error-info.ts +168 -0
- package/examples/canister-studio/src/calls/history.test.ts +284 -0
- package/examples/canister-studio/src/calls/history.ts +287 -0
- package/examples/canister-studio/src/calls/kinds.ts +68 -0
- package/examples/canister-studio/src/calls/prepare.ts +32 -0
- package/examples/canister-studio/src/calls/query-options.ts +30 -0
- package/examples/canister-studio/src/calls/read.ts +47 -0
- package/examples/canister-studio/src/calls/readback.ts +100 -0
- package/examples/canister-studio/src/calls/record.ts +123 -0
- package/examples/canister-studio/src/calls/safe-calls.test.ts +299 -0
- package/examples/canister-studio/src/calls/share.ts +46 -0
- package/examples/canister-studio/src/calls/testing/doubles.tsx +108 -0
- package/examples/canister-studio/src/calls/testing/fixtures.ts +185 -0
- package/examples/canister-studio/src/calls/useCanisterHandle.ts +35 -0
- package/examples/canister-studio/src/canisters/ckbtc_minter.envelope.json +4228 -0
- package/examples/canister-studio/src/canisters/ckbtc_minter.ts +868 -0
- package/examples/canister-studio/src/canisters/icp_ledger.envelope.json +3454 -0
- package/examples/canister-studio/src/canisters/icp_ledger.ts +584 -0
- package/examples/canister-studio/src/canisters/icrc1.envelope.json +840 -0
- package/examples/canister-studio/src/canisters/icrc1.ts +78 -0
- package/examples/canister-studio/src/components/ActionButton.tsx +32 -0
- package/examples/canister-studio/src/components/AppHeader.tsx +83 -0
- package/examples/canister-studio/src/components/AppShell.tsx +29 -0
- package/examples/canister-studio/src/components/AuthButton.tsx +79 -0
- package/examples/canister-studio/src/components/Badge.tsx +79 -0
- package/examples/canister-studio/src/components/CanisterHeader.tsx +200 -0
- package/examples/canister-studio/src/components/CanisterJump.tsx +130 -0
- package/examples/canister-studio/src/components/Card.tsx +64 -0
- package/examples/canister-studio/src/components/Code.tsx +101 -0
- package/examples/canister-studio/src/components/CopyButton.tsx +126 -0
- package/examples/canister-studio/src/components/Disclosure.tsx +34 -0
- package/examples/canister-studio/src/components/EmptyState.tsx +34 -0
- package/examples/canister-studio/src/components/MethodList.test.tsx +137 -0
- package/examples/canister-studio/src/components/MethodList.tsx +244 -0
- package/examples/canister-studio/src/components/Notice.tsx +36 -0
- package/examples/canister-studio/src/components/ProvenanceBadge.tsx +44 -0
- package/examples/canister-studio/src/components/RouteError.tsx +42 -0
- package/examples/canister-studio/src/components/SectionBoundary.tsx +76 -0
- package/examples/canister-studio/src/components/Spinner.tsx +33 -0
- package/examples/canister-studio/src/components/Tabs.tsx +96 -0
- package/examples/canister-studio/src/components/canister/CanisterLoadFailure.test.tsx +56 -0
- package/examples/canister-studio/src/components/canister/CanisterLoadFailure.tsx +165 -0
- package/examples/canister-studio/src/components/canister/CanisterNotFound.tsx +59 -0
- package/examples/canister-studio/src/components/canister/CanisterPending.tsx +33 -0
- package/examples/canister-studio/src/components/canister/MethodOverview.tsx +93 -0
- package/examples/canister-studio/src/components/canister/MethodWorkspace.test.tsx +201 -0
- package/examples/canister-studio/src/components/canister/MethodWorkspace.tsx +187 -0
- package/examples/canister-studio/src/components/canister/shared-call.test.ts +186 -0
- package/examples/canister-studio/src/components/canister/shared-call.ts +133 -0
- package/examples/canister-studio/src/components/canister/shared-link-loader.test.ts +116 -0
- package/examples/canister-studio/src/components/canister/use-canister-page.ts +44 -0
- package/examples/canister-studio/src/components/canister/workspace-key.test.ts +27 -0
- package/examples/canister-studio/src/components/canister/workspace-key.ts +36 -0
- package/examples/canister-studio/src/components/canister-id-input.ts +69 -0
- package/examples/canister-studio/src/components/canister-names.ts +11 -0
- package/examples/canister-studio/src/components/download.ts +34 -0
- package/examples/canister-studio/src/components/fallbacks.tsx +33 -0
- package/examples/canister-studio/src/components/helpers.test.ts +107 -0
- package/examples/canister-studio/src/components/history/HistoryList.tsx +284 -0
- package/examples/canister-studio/src/components/home/FeaturedLedger.tsx +149 -0
- package/examples/canister-studio/src/components/home/OpenCanister.tsx +99 -0
- package/examples/canister-studio/src/components/home/featured.test.tsx +92 -0
- package/examples/canister-studio/src/components/home/featured.ts +64 -0
- package/examples/canister-studio/src/components/interface/CandidTab.tsx +38 -0
- package/examples/canister-studio/src/components/interface/CompatReport.tsx +92 -0
- package/examples/canister-studio/src/components/interface/CompatibilityTab.tsx +190 -0
- package/examples/canister-studio/src/components/interface/ProjectTab.tsx +196 -0
- package/examples/canister-studio/src/components/interface/TypeScriptTab.tsx +114 -0
- package/examples/canister-studio/src/components/interface/tools.test.tsx +169 -0
- package/examples/canister-studio/src/components/modes.ts +31 -0
- package/examples/canister-studio/src/components/playground/MethodPicker.tsx +67 -0
- package/examples/canister-studio/src/components/playground/PlaygroundForms.tsx +112 -0
- package/examples/canister-studio/src/components/playground/PlaygroundTools.tsx +219 -0
- package/examples/canister-studio/src/components/playground/sample.ts +43 -0
- package/examples/canister-studio/src/components/playground/target.test.tsx +86 -0
- package/examples/canister-studio/src/components/primitives.test.tsx +121 -0
- package/examples/canister-studio/src/components/provenance.ts +38 -0
- package/examples/canister-studio/src/components/route-render.test.tsx +71 -0
- package/examples/canister-studio/src/components/use-task.ts +56 -0
- package/examples/canister-studio/src/components/wallet/BalancePanel.tsx +110 -0
- package/examples/canister-studio/src/components/wallet/LedgerSummary.tsx +63 -0
- package/examples/canister-studio/src/components/wallet/MinterPanel.tsx +255 -0
- package/examples/canister-studio/src/components/wallet/SendPanel.test.tsx +297 -0
- package/examples/canister-studio/src/components/wallet/SendPanel.tsx +446 -0
- package/examples/canister-studio/src/components/wallet/ledgers.test.ts +36 -0
- package/examples/canister-studio/src/components/wallet/ledgers.ts +111 -0
- package/examples/canister-studio/src/forms/JsonPanel.tsx +157 -0
- package/examples/canister-studio/src/forms/MethodForm.test.tsx +369 -0
- package/examples/canister-studio/src/forms/MethodForm.tsx +368 -0
- package/examples/canister-studio/src/forms/TypedForm.tsx +55 -0
- package/examples/canister-studio/src/forms/context.ts +62 -0
- package/examples/canister-studio/src/forms/convert.ts +131 -0
- package/examples/canister-studio/src/forms/edge.test.ts +252 -0
- package/examples/canister-studio/src/forms/engine.test.ts +358 -0
- package/examples/canister-studio/src/forms/engine.ts +1492 -0
- package/examples/canister-studio/src/forms/fields/BytesField.tsx +210 -0
- package/examples/canister-studio/src/forms/fields/Field.tsx +106 -0
- package/examples/canister-studio/src/forms/fields/FieldShell.tsx +101 -0
- package/examples/canister-studio/src/forms/fields/Group.tsx +74 -0
- package/examples/canister-studio/src/forms/fields/ListField.tsx +256 -0
- package/examples/canister-studio/src/forms/fields/Segmented.tsx +78 -0
- package/examples/canister-studio/src/forms/fields/TimestampField.tsx +108 -0
- package/examples/canister-studio/src/forms/fields/UnitsField.tsx +119 -0
- package/examples/canister-studio/src/forms/fields/composites.tsx +389 -0
- package/examples/canister-studio/src/forms/fields/leaves.tsx +350 -0
- package/examples/canister-studio/src/forms/fields.test.tsx +396 -0
- package/examples/canister-studio/src/forms/helpers.test.ts +225 -0
- package/examples/canister-studio/src/forms/hints.ts +128 -0
- package/examples/canister-studio/src/forms/json.ts +119 -0
- package/examples/canister-studio/src/forms/parsing.test.ts +217 -0
- package/examples/canister-studio/src/forms/useMethodForm.ts +251 -0
- package/examples/canister-studio/src/interface/browser.test.ts +23 -0
- package/examples/canister-studio/src/interface/browser.ts +153 -0
- package/examples/canister-studio/src/interface/cache.ts +68 -0
- package/examples/canister-studio/src/interface/cli-cache.test.ts +155 -0
- package/examples/canister-studio/src/interface/cli-cache.ts +125 -0
- package/examples/canister-studio/src/interface/compile.server.test.ts +198 -0
- package/examples/canister-studio/src/interface/compile.server.ts +262 -0
- package/examples/canister-studio/src/interface/compile.test.ts +108 -0
- package/examples/canister-studio/src/interface/compile.ts +305 -0
- package/examples/canister-studio/src/interface/docs.test.ts +75 -0
- package/examples/canister-studio/src/interface/docs.ts +100 -0
- package/examples/canister-studio/src/interface/functions.test.ts +42 -0
- package/examples/canister-studio/src/interface/functions.ts +200 -0
- package/examples/canister-studio/src/interface/live-candid.server.ts +91 -0
- package/examples/canister-studio/src/interface/live-loader.test.ts +515 -0
- package/examples/canister-studio/src/interface/live-loader.ts +364 -0
- package/examples/canister-studio/src/interface/load.test.ts +328 -0
- package/examples/canister-studio/src/interface/load.ts +249 -0
- package/examples/canister-studio/src/interface/read-candid.test.ts +185 -0
- package/examples/canister-studio/src/interface/read-candid.ts +233 -0
- package/examples/canister-studio/src/interface/server-result.ts +42 -0
- package/examples/canister-studio/src/interface/typed.ts +68 -0
- package/examples/canister-studio/src/interface/types.ts +121 -0
- package/examples/canister-studio/src/lib/format.test.ts +107 -0
- package/examples/canister-studio/src/lib/format.ts +139 -0
- package/examples/canister-studio/src/lib/hex.test.ts +43 -0
- package/examples/canister-studio/src/lib/hex.ts +33 -0
- package/examples/canister-studio/src/lib/ids.test.ts +70 -0
- package/examples/canister-studio/src/lib/ids.ts +93 -0
- package/examples/canister-studio/src/lib/reactor.test.ts +55 -0
- package/examples/canister-studio/src/lib/reactor.ts +55 -0
- package/examples/canister-studio/src/lib/registry.test.ts +74 -0
- package/examples/canister-studio/src/lib/registry.ts +238 -0
- package/examples/canister-studio/src/lib/schema.ts +148 -0
- package/examples/canister-studio/src/routeTree.gen.ts +227 -0
- package/examples/canister-studio/src/router.test.ts +290 -0
- package/examples/canister-studio/src/router.tsx +127 -0
- package/examples/canister-studio/src/routes/__root.tsx +58 -0
- package/examples/canister-studio/src/routes/canister.$canisterId.index.tsx +172 -0
- package/examples/canister-studio/src/routes/canister.$canisterId.interface.tsx +98 -0
- package/examples/canister-studio/src/routes/canister.$canisterId.tsx +91 -0
- package/examples/canister-studio/src/routes/history.tsx +55 -0
- package/examples/canister-studio/src/routes/index.tsx +169 -0
- package/examples/canister-studio/src/routes/playground.tsx +286 -0
- package/examples/canister-studio/src/routes/safety.tsx +57 -0
- package/examples/canister-studio/src/routes/wallet.tsx +213 -0
- package/examples/canister-studio/src/safety/LabTransferForm.tsx +84 -0
- package/examples/canister-studio/src/safety/RequestLog.tsx +79 -0
- package/examples/canister-studio/src/safety/SafetyLab.test.tsx +89 -0
- package/examples/canister-studio/src/safety/SafetyLab.tsx +140 -0
- package/examples/canister-studio/src/safety/ScenarioCard.tsx +262 -0
- package/examples/canister-studio/src/safety/lab-run.ts +283 -0
- package/examples/canister-studio/src/safety/mock-ledger.test.ts +99 -0
- package/examples/canister-studio/src/safety/mock-ledger.ts +263 -0
- package/examples/canister-studio/src/safety/scenarios.test.ts +157 -0
- package/examples/canister-studio/src/safety/scenarios.ts +924 -0
- package/examples/canister-studio/src/styles.css +3453 -0
- package/examples/canister-studio/src/styles.test.ts +46 -0
- package/examples/canister-studio/src/values/CallPreview.test.tsx +86 -0
- package/examples/canister-studio/src/values/CallPreview.tsx +148 -0
- package/examples/canister-studio/src/values/ErrValueView.tsx +66 -0
- package/examples/canister-studio/src/values/Inspector.tsx +133 -0
- package/examples/canister-studio/src/values/ReplyView.test.tsx +341 -0
- package/examples/canister-studio/src/values/ReplyView.tsx +164 -0
- package/examples/canister-studio/src/values/candid-text.test.ts +516 -0
- package/examples/canister-studio/src/values/candid-text.ts +479 -0
- package/examples/canister-studio/src/values/hints.ts +58 -0
- package/examples/canister-studio/src/values/json.test.ts +66 -0
- package/examples/canister-studio/src/values/json.ts +31 -0
- package/examples/canister-studio/src/values/leaves.tsx +309 -0
- package/examples/canister-studio/src/values/schema.test.ts +123 -0
- package/examples/canister-studio/src/values/schema.ts +78 -0
- package/examples/canister-studio/src/values/test-router.tsx +21 -0
- package/examples/canister-studio/src/values/tree.tsx +670 -0
- package/examples/canister-studio/src/values/use-sha256.ts +29 -0
- package/examples/canister-studio/tsconfig.json +20 -0
- package/examples/canister-studio/vite.config.ts +50 -0
- package/examples/canister-studio/vitest.config.ts +20 -0
- package/examples/icrc-ledger/.stackblitzrc +4 -0
- package/examples/icrc-ledger/README.md +100 -0
- package/examples/icrc-ledger/ckbtc_minter.did +12 -0
- package/examples/icrc-ledger/icp_ledger.did +147 -0
- package/examples/icrc-ledger/icrc1.did +88 -0
- package/examples/icrc-ledger/index.html +13 -0
- package/examples/icrc-ledger/package.json +40 -0
- package/examples/icrc-ledger/scripts/smoke.ts +151 -0
- package/examples/icrc-ledger/src/App.tsx +71 -0
- package/examples/icrc-ledger/src/ErrorPanel.tsx +105 -0
- package/examples/icrc-ledger/src/MainnetTab.tsx +450 -0
- package/examples/icrc-ledger/src/SandboxTab.test.tsx +379 -0
- package/examples/icrc-ledger/src/SandboxTab.tsx +761 -0
- package/examples/icrc-ledger/src/blocks.test.ts +247 -0
- package/examples/icrc-ledger/src/blocks.ts +140 -0
- package/examples/icrc-ledger/src/canisters/ckbtc_minter.envelope.json +96 -0
- package/examples/icrc-ledger/src/canisters/ckbtc_minter.ts +16 -0
- package/examples/icrc-ledger/src/canisters/icp_ledger.envelope.json +690 -0
- package/examples/icrc-ledger/src/canisters/icp_ledger.ts +223 -0
- package/examples/icrc-ledger/src/canisters/icrc1.envelope.json +840 -0
- package/examples/icrc-ledger/src/canisters/icrc1.ts +78 -0
- package/examples/icrc-ledger/src/format.test.ts +41 -0
- package/examples/icrc-ledger/src/format.ts +68 -0
- package/examples/icrc-ledger/src/ledger.ts +15 -0
- package/examples/icrc-ledger/src/main.tsx +10 -0
- package/examples/icrc-ledger/src/minter.ts +16 -0
- package/examples/icrc-ledger/src/sandbox.test.ts +464 -0
- package/examples/icrc-ledger/src/sandbox.ts +281 -0
- package/examples/icrc-ledger/src/styles.css +391 -0
- package/examples/icrc-ledger/src/transfer-form.test.ts +62 -0
- package/examples/icrc-ledger/src/transfer-form.ts +97 -0
- package/examples/icrc-ledger/tsconfig.json +19 -0
- package/examples/icrc-ledger/vite.config.ts +44 -0
- package/examples/icrc-ledger/vitest.config.ts +12 -0
- package/examples/next-ssr/.stackblitzrc +4 -0
- package/examples/next-ssr/README.md +112 -0
- package/examples/next-ssr/icrc1.did +88 -0
- package/examples/next-ssr/next.config.ts +10 -0
- package/examples/next-ssr/package.json +41 -0
- package/examples/next-ssr/scripts/smoke.ts +169 -0
- package/examples/next-ssr/src/app/account/CertifiedBalances.tsx +58 -0
- package/examples/next-ssr/src/app/account/account.test.tsx +165 -0
- package/examples/next-ssr/src/app/account/page.tsx +82 -0
- package/examples/next-ssr/src/app/api/balance/[ledger]/[principal]/route.ts +10 -0
- package/examples/next-ssr/src/app/globals.css +278 -0
- package/examples/next-ssr/src/app/layout.tsx +34 -0
- package/examples/next-ssr/src/app/page.tsx +46 -0
- package/examples/next-ssr/src/app/providers.browser.test.tsx +127 -0
- package/examples/next-ssr/src/app/providers.test.tsx +73 -0
- package/examples/next-ssr/src/app/providers.tsx +60 -0
- package/examples/next-ssr/src/canisters/icrc1.envelope.json +840 -0
- package/examples/next-ssr/src/canisters/icrc1.ts +78 -0
- package/examples/next-ssr/src/components/BalanceTable.tsx +43 -0
- package/examples/next-ssr/src/components/LedgerCard.test.tsx +96 -0
- package/examples/next-ssr/src/components/LedgerCard.tsx +230 -0
- package/examples/next-ssr/src/components/LedgerError.tsx +32 -0
- package/examples/next-ssr/src/components/LedgerSections.tsx +22 -0
- package/examples/next-ssr/src/components/MyBalances.test.tsx +253 -0
- package/examples/next-ssr/src/components/MyBalances.tsx +129 -0
- package/examples/next-ssr/src/components/SessionBadge.tsx +18 -0
- package/examples/next-ssr/src/components/hydration.test.tsx +259 -0
- package/examples/next-ssr/src/components/server-data.ts +10 -0
- package/examples/next-ssr/src/format.ts +46 -0
- package/examples/next-ssr/src/ledgers.ts +38 -0
- package/examples/next-ssr/src/server/balance-route.test.ts +151 -0
- package/examples/next-ssr/src/server/balance-route.ts +157 -0
- package/examples/next-ssr/src/server/error-summary.test.ts +34 -0
- package/examples/next-ssr/src/server/error-summary.ts +37 -0
- package/examples/next-ssr/src/server/parse-owner.test.ts +39 -0
- package/examples/next-ssr/src/server/parse-owner.ts +34 -0
- package/examples/next-ssr/src/server/prefetch-ledgers.test.ts +137 -0
- package/examples/next-ssr/src/server/prefetch-ledgers.ts +70 -0
- package/examples/next-ssr/src/server/read-balances.test.ts +103 -0
- package/examples/next-ssr/src/server/read-balances.ts +78 -0
- package/examples/next-ssr/src/server/request-client.rsc.test.tsx +110 -0
- package/examples/next-ssr/src/server/request-client.ts +24 -0
- package/examples/next-ssr/src/testing/mock-ledgers.ts +115 -0
- package/examples/next-ssr/src/testing/react-server-dom-turbopack.d.ts +15 -0
- package/examples/next-ssr/src/testing/setup.ts +10 -0
- package/examples/next-ssr/tsconfig.json +31 -0
- package/examples/next-ssr/vitest.config.ts +59 -0
- package/examples/node-agent-tool/.stackblitzrc +4 -0
- package/examples/node-agent-tool/README.md +165 -0
- package/examples/node-agent-tool/icp.yaml +11 -0
- package/examples/node-agent-tool/icrc1.did +88 -0
- package/examples/node-agent-tool/package.json +32 -0
- package/examples/node-agent-tool/src/canisters/icrc1.envelope.json +840 -0
- package/examples/node-agent-tool/src/canisters/icrc1.ts +78 -0
- package/examples/node-agent-tool/src/cli.test.ts +64 -0
- package/examples/node-agent-tool/src/cli.ts +23 -0
- package/examples/node-agent-tool/src/command-line.ts +159 -0
- package/examples/node-agent-tool/src/commands/balance.ts +73 -0
- package/examples/node-agent-tool/src/commands/demo.test.ts +51 -0
- package/examples/node-agent-tool/src/commands/demo.ts +359 -0
- package/examples/node-agent-tool/src/commands/info.ts +69 -0
- package/examples/node-agent-tool/src/commands/reads.test.ts +133 -0
- package/examples/node-agent-tool/src/commands/resend.test.ts +363 -0
- package/examples/node-agent-tool/src/commands/transfer.test.ts +313 -0
- package/examples/node-agent-tool/src/commands/transfer.ts +415 -0
- package/examples/node-agent-tool/src/commands/watch.test.ts +159 -0
- package/examples/node-agent-tool/src/commands/watch.ts +135 -0
- package/examples/node-agent-tool/src/commands/whoami.ts +36 -0
- package/examples/node-agent-tool/src/context.ts +20 -0
- package/examples/node-agent-tool/src/dispatch.ts +70 -0
- package/examples/node-agent-tool/src/failure.test.ts +59 -0
- package/examples/node-agent-tool/src/failure.ts +176 -0
- package/examples/node-agent-tool/src/identity.test.ts +242 -0
- package/examples/node-agent-tool/src/identity.ts +228 -0
- package/examples/node-agent-tool/src/input.ts +110 -0
- package/examples/node-agent-tool/src/ledgers.test.ts +74 -0
- package/examples/node-agent-tool/src/ledgers.ts +62 -0
- package/examples/node-agent-tool/src/mock-ledger.ts +211 -0
- package/examples/node-agent-tool/src/network.test.ts +128 -0
- package/examples/node-agent-tool/src/network.ts +101 -0
- package/examples/node-agent-tool/src/output.test.ts +208 -0
- package/examples/node-agent-tool/src/output.ts +163 -0
- package/examples/node-agent-tool/src/run.ts +73 -0
- package/examples/node-agent-tool/src/test-kit.ts +167 -0
- package/examples/node-agent-tool/tsconfig.json +18 -0
- package/examples/node-agent-tool/vitest.config.ts +8 -0
- package/examples/vite-wallet/.stackblitzrc +4 -0
- package/examples/vite-wallet/Cargo.lock +646 -0
- package/examples/vite-wallet/Cargo.toml +5 -0
- package/examples/vite-wallet/README.md +107 -0
- package/examples/vite-wallet/backend/Cargo.toml +12 -0
- package/examples/vite-wallet/backend/backend.did +37 -0
- package/examples/vite-wallet/backend/src/lib.rs +133 -0
- package/examples/vite-wallet/icp.yaml +44 -0
- package/examples/vite-wallet/index.html +13 -0
- package/examples/vite-wallet/ledger.did +54 -0
- package/examples/vite-wallet/package.json +46 -0
- package/examples/vite-wallet/public/.ic-assets.json5 +26 -0
- package/examples/vite-wallet/scripts/faucet-args.test.ts +30 -0
- package/examples/vite-wallet/scripts/faucet-args.ts +41 -0
- package/examples/vite-wallet/scripts/faucet.ts +41 -0
- package/examples/vite-wallet/src/AddressBook.test.tsx +191 -0
- package/examples/vite-wallet/src/AddressBook.tsx +147 -0
- package/examples/vite-wallet/src/App.tsx +53 -0
- package/examples/vite-wallet/src/Balance.test.tsx +57 -0
- package/examples/vite-wallet/src/Balance.tsx +58 -0
- package/examples/vite-wallet/src/EnvironmentPanel.test.tsx +83 -0
- package/examples/vite-wallet/src/EnvironmentPanel.tsx +103 -0
- package/examples/vite-wallet/src/ErrorNote.tsx +70 -0
- package/examples/vite-wallet/src/Profile.test.tsx +145 -0
- package/examples/vite-wallet/src/Profile.tsx +92 -0
- package/examples/vite-wallet/src/SendIcp.test.tsx +231 -0
- package/examples/vite-wallet/src/SendIcp.tsx +244 -0
- package/examples/vite-wallet/src/SendToContact.test.tsx +113 -0
- package/examples/vite-wallet/src/SendToContact.tsx +102 -0
- package/examples/vite-wallet/src/SignIn.test.tsx +106 -0
- package/examples/vite-wallet/src/SignIn.tsx +120 -0
- package/examples/vite-wallet/src/Wallet.test.tsx +89 -0
- package/examples/vite-wallet/src/account-switch.test.tsx +108 -0
- package/examples/vite-wallet/src/auth/dev-accounts.test.ts +113 -0
- package/examples/vite-wallet/src/auth/dev-accounts.ts +216 -0
- package/examples/vite-wallet/src/auth/local-page.test.ts +28 -0
- package/examples/vite-wallet/src/auth/local-page.ts +20 -0
- package/examples/vite-wallet/src/auth/wallet-auth.test.ts +159 -0
- package/examples/vite-wallet/src/auth/wallet-auth.ts +131 -0
- package/examples/vite-wallet/src/canisters/backend.envelope.json +275 -0
- package/examples/vite-wallet/src/canisters/backend.ts +58 -0
- package/examples/vite-wallet/src/canisters/ledger.envelope.json +451 -0
- package/examples/vite-wallet/src/canisters/ledger.ts +56 -0
- package/examples/vite-wallet/src/client.ts +45 -0
- package/examples/vite-wallet/src/main.tsx +18 -0
- package/examples/vite-wallet/src/optimistic-contacts.ts +113 -0
- package/examples/vite-wallet/src/refusal.ts +17 -0
- package/examples/vite-wallet/src/styles.css +282 -0
- package/examples/vite-wallet/src/test/test-wallet.tsx +322 -0
- package/examples/vite-wallet/src/token.ts +50 -0
- package/examples/vite-wallet/src/transfer.test.ts +178 -0
- package/examples/vite-wallet/src/transfer.ts +110 -0
- package/examples/vite-wallet/src/use-canisters.ts +49 -0
- package/examples/vite-wallet/tsconfig.json +19 -0
- package/examples/vite-wallet/vite.config.ts +47 -0
- package/examples/vite-wallet/vitest.config.ts +17 -0
- package/package.json +9 -4
- package/packages/core/.size-limit.js +47 -0
- package/packages/core/LICENSE +21 -0
- package/packages/core/README.md +84 -0
- package/packages/core/llms.txt +610 -0
- package/packages/core/package.json +89 -0
- package/packages/core/src/call.ts +961 -0
- package/packages/core/src/canister.ts +259 -0
- package/packages/core/src/client.ts +2061 -0
- package/packages/core/src/errors.ts +1079 -0
- package/packages/core/src/hydration.ts +239 -0
- package/packages/core/src/index.ts +23 -0
- package/packages/core/src/keys.ts +108 -0
- package/packages/core/src/management.ts +138 -0
- package/packages/core/src/network.ts +1020 -0
- package/packages/core/src/options.ts +1000 -0
- package/packages/core/src/runtime.ts +50 -0
- package/packages/core/src/testing/fake-replica.ts +1166 -0
- package/packages/core/src/testing/index.ts +16 -0
- package/packages/core/src/testing/test-auth.ts +340 -0
- package/packages/core/src/testing/test-client.ts +559 -0
- package/packages/core/src/types.ts +541 -0
- package/packages/core/src/units.ts +299 -0
- package/packages/core/src/version.ts +4 -0
- package/packages/core/tests/canister-helpers.ts +187 -0
- package/packages/core/tests/canister-id.test.ts +158 -0
- package/packages/core/tests/canister-unwrap.test.ts +94 -0
- package/packages/core/tests/canister.test-d.ts +406 -0
- package/packages/core/tests/canister.test.ts +1921 -0
- package/packages/core/tests/client-account-change.test.ts +131 -0
- package/packages/core/tests/client-agents.test.ts +402 -0
- package/packages/core/tests/client-auth-network.test.ts +719 -0
- package/packages/core/tests/client-disposed.test.ts +403 -0
- package/packages/core/tests/client-helpers.ts +136 -0
- package/packages/core/tests/client-hydration.test.ts +166 -0
- package/packages/core/tests/client-pinned.test.ts +626 -0
- package/packages/core/tests/client-session.test.ts +348 -0
- package/packages/core/tests/client.test-d.ts +181 -0
- package/packages/core/tests/client.test.ts +1162 -0
- package/packages/core/tests/encode.test.ts +171 -0
- package/packages/core/tests/env-canister-id.test.ts +666 -0
- package/packages/core/tests/env-cookie-copies.test.ts +182 -0
- package/packages/core/tests/error-guard.test.ts +222 -0
- package/packages/core/tests/errors.test-d.ts +280 -0
- package/packages/core/tests/errors.test.ts +1359 -0
- package/packages/core/tests/fixtures/README.md +28 -0
- package/packages/core/tests/fixtures/accounts.did +21 -0
- package/packages/core/tests/fixtures/accounts.envelope.json +367 -0
- package/packages/core/tests/fixtures/accounts.ts +26 -0
- package/packages/core/tests/fixtures/archive.did +31 -0
- package/packages/core/tests/fixtures/archive.envelope.json +259 -0
- package/packages/core/tests/fixtures/archive.ts +55 -0
- package/packages/core/tests/fixtures/icrc1.did +88 -0
- package/packages/core/tests/fixtures/icrc1.envelope.json +840 -0
- package/packages/core/tests/fixtures/icrc1.ts +78 -0
- package/packages/core/tests/fixtures/management.did +724 -0
- package/packages/core/tests/fixtures/management.envelope.json +4455 -0
- package/packages/core/tests/fixtures/management.ts +486 -0
- package/packages/core/tests/fixtures/shapes.did +33 -0
- package/packages/core/tests/fixtures/shapes.envelope.json +440 -0
- package/packages/core/tests/fixtures/shapes.ts +56 -0
- package/packages/core/tests/fixtures/skippable.did +15 -0
- package/packages/core/tests/fixtures/skippable.envelope.json +182 -0
- package/packages/core/tests/fixtures/skippable.ts +33 -0
- package/packages/core/tests/func.test.ts +154 -0
- package/packages/core/tests/identity-attributes.test.ts +214 -0
- package/packages/core/tests/keys.test.ts +289 -0
- package/packages/core/tests/management.test.ts +206 -0
- package/packages/core/tests/network-helpers.ts +121 -0
- package/packages/core/tests/network.test-d.ts +78 -0
- package/packages/core/tests/network.test.ts +1051 -0
- package/packages/core/tests/options.test.ts +1594 -0
- package/packages/core/tests/redirect-sign-in.test.ts +393 -0
- package/packages/core/tests/resend.test-d.ts +81 -0
- package/packages/core/tests/resend.test.ts +723 -0
- package/packages/core/tests/skip.test-d.ts +125 -0
- package/packages/core/tests/testing-entry.test.ts +73 -0
- package/packages/core/tests/testing-fake-replica.test.ts +1242 -0
- package/packages/core/tests/testing-test-auth.test-d.ts +84 -0
- package/packages/core/tests/testing-test-auth.test.ts +483 -0
- package/packages/core/tests/testing-test-client.test-d.ts +184 -0
- package/packages/core/tests/testing-test-client.test.ts +1272 -0
- package/packages/core/tests/time-realm.test.ts +63 -0
- package/packages/core/tests/time.test.ts +238 -0
- package/packages/core/tests/traps/README.md +39 -0
- package/packages/core/tests/traps/anonymous-write.test.ts +145 -0
- package/packages/core/tests/traps/classification-table.test.ts +256 -0
- package/packages/core/tests/traps/err-arm.test.ts +144 -0
- package/packages/core/tests/traps/fetch-spy.ts +39 -0
- package/packages/core/tests/traps/invalidation-after-write.test.ts +178 -0
- package/packages/core/tests/traps/ledger.ts +248 -0
- package/packages/core/tests/traps/root-key-off-local.test.ts +130 -0
- package/packages/core/tests/traps/stale-data-across-users.test.ts +173 -0
- package/packages/core/tests/traps/unresolved-name.test.ts +155 -0
- package/packages/core/tests/traps/update-as-query.test.ts +244 -0
- package/packages/core/tests/traps/update-not-resent.test.ts +126 -0
- package/packages/core/tests/traps/update-resent-when-never-delivered.test.ts +159 -0
- package/packages/core/tests/traps.test-d.ts +476 -0
- package/packages/core/tests/units.test-d.ts +118 -0
- package/packages/core/tests/units.test.ts +634 -0
- package/packages/core/tsconfig.json +19 -0
- package/packages/core/tsconfig.typecheck.json +16 -0
- package/packages/core/vitest.config.ts +13 -0
- package/packages/mcp/LICENSE +21 -0
- package/packages/mcp/README.md +189 -0
- package/packages/mcp/package.json +62 -0
- package/packages/mcp/src/candid/candid-text.test.ts +455 -0
- package/packages/mcp/src/candid/candid-text.ts +501 -0
- package/packages/mcp/src/candid/contract.test.ts +398 -0
- package/packages/mcp/src/candid/formats.ts +155 -0
- package/packages/mcp/src/candid/hex.ts +29 -0
- package/packages/mcp/src/candid/json-schema.test.ts +758 -0
- package/packages/mcp/src/candid/json-schema.ts +454 -0
- package/packages/mcp/src/candid/json.test.ts +1207 -0
- package/packages/mcp/src/candid/json.ts +1053 -0
- package/packages/mcp/src/candid/schema-walk.ts +189 -0
- package/packages/mcp/src/candid/type-text.test.ts +179 -0
- package/packages/mcp/src/candid/type-text.ts +238 -0
- package/packages/mcp/src/cli.ts +134 -0
- package/packages/mcp/src/identity.ts +188 -0
- package/packages/mcp/src/interface/cache.test.ts +48 -0
- package/packages/mcp/src/interface/cache.ts +68 -0
- package/packages/mcp/src/interface/cli-cache.test.ts +150 -0
- package/packages/mcp/src/interface/cli-cache.ts +122 -0
- package/packages/mcp/src/interface/compile-worker.test.ts +246 -0
- package/packages/mcp/src/interface/compile-worker.ts +290 -0
- package/packages/mcp/src/interface/compile.test.ts +155 -0
- package/packages/mcp/src/interface/compile.ts +281 -0
- package/packages/mcp/src/interface/docs.test.ts +105 -0
- package/packages/mcp/src/interface/docs.ts +86 -0
- package/packages/mcp/src/interface/fixtures/icrc1.did +88 -0
- package/packages/mcp/src/interface/hex.ts +8 -0
- package/packages/mcp/src/interface/ids.test.ts +71 -0
- package/packages/mcp/src/interface/ids.ts +87 -0
- package/packages/mcp/src/interface/loader.test.ts +804 -0
- package/packages/mcp/src/interface/loader.ts +519 -0
- package/packages/mcp/src/interface/read-candid.test.ts +300 -0
- package/packages/mcp/src/interface/read-candid.ts +259 -0
- package/packages/mcp/src/interface/types.ts +113 -0
- package/packages/mcp/src/network.ts +122 -0
- package/packages/mcp/src/options.ts +149 -0
- package/packages/mcp/src/server/approval.ts +159 -0
- package/packages/mcp/src/server/naming.ts +43 -0
- package/packages/mcp/src/server/results.ts +102 -0
- package/packages/mcp/src/server/server.ts +797 -0
- package/packages/mcp/src/server/tools.ts +116 -0
- package/packages/mcp/src/usage.ts +40 -0
- package/packages/mcp/tests/approval.test.ts +60 -0
- package/packages/mcp/tests/identity.test.ts +234 -0
- package/packages/mcp/tests/naming.test.ts +62 -0
- package/packages/mcp/tests/options.test.ts +140 -0
- package/packages/mcp/tests/server.test.ts +997 -0
- package/packages/mcp/tsconfig.json +19 -0
- package/packages/mcp/tsconfig.typecheck.json +10 -0
- package/packages/mcp/vitest.config.ts +10 -0
- package/packages/react/LICENSE +21 -0
- package/packages/react/README.md +328 -0
- package/packages/react/llms.txt +95 -0
- package/packages/react/package.json +95 -0
- package/packages/react/src/index.tsx +678 -0
- package/packages/react/tests/abandoned-render.test.tsx +816 -0
- package/packages/react/tests/auth.test.tsx +315 -0
- package/packages/react/tests/caller-keys.test.tsx +2334 -0
- package/packages/react/tests/client-time.test.ts +40 -0
- package/packages/react/tests/entry.test.ts +52 -0
- package/packages/react/tests/helpers.tsx +291 -0
- package/packages/react/tests/hydration.test.tsx +262 -0
- package/packages/react/tests/ownership.test.tsx +260 -0
- package/packages/react/tests/provider.server.test.tsx +169 -0
- package/packages/react/tests/query-client.test.tsx +107 -0
- package/packages/react/tests/strict-mode.test.tsx +288 -0
- package/packages/react/tests/suspense.test-d.tsx +357 -0
- package/packages/react/tests/two-query-cores.test.tsx +135 -0
- package/packages/react/tests/types.test-d.tsx +63 -0
- package/packages/react/tsconfig.json +23 -0
- package/packages/react/tsconfig.test.json +8 -0
- package/packages/react/tsconfig.typecheck.json +16 -0
- package/packages/react/vitest.config.ts +39 -0
- package/packages/vite-plugin/LICENSE +21 -0
- package/packages/vite-plugin/README.md +532 -0
- package/packages/vite-plugin/package.json +76 -0
- package/packages/vite-plugin/src/describe-value.test.ts +43 -0
- package/packages/vite-plugin/src/describe-value.ts +167 -0
- package/packages/vite-plugin/src/dev-environment.test.ts +747 -0
- package/packages/vite-plugin/src/dev-environment.ts +510 -0
- package/packages/vite-plugin/src/env.ts +431 -0
- package/packages/vite-plugin/src/fetch.ts +348 -0
- package/packages/vite-plugin/src/file-identity.ts +106 -0
- package/packages/vite-plugin/src/generate.ts +691 -0
- package/packages/vite-plugin/src/index.test.ts +1243 -0
- package/packages/vite-plugin/src/index.ts +2565 -0
- package/packages/vite-plugin/src/interface.ts +339 -0
- package/packages/vite-plugin/src/local-host.ts +34 -0
- package/packages/vite-plugin/src/options.ts +641 -0
- package/packages/vite-plugin/src/principal-text.test.ts +87 -0
- package/packages/vite-plugin/src/principal-text.ts +98 -0
- package/packages/vite-plugin/tests/close.test.ts +141 -0
- package/packages/vite-plugin/tests/environment-mode.test.ts +136 -0
- package/packages/vite-plugin/tests/fake-ic.ts +214 -0
- package/packages/vite-plugin/tests/fetch-sdk.test.ts +27 -0
- package/packages/vite-plugin/tests/fetch.test.ts +1196 -0
- package/packages/vite-plugin/tests/fixtures/fake-cli/bin.js +90 -0
- package/packages/vite-plugin/tests/fixtures/fake-cli/package.json +8 -0
- package/packages/vite-plugin/tests/fixtures/icrc1.did +88 -0
- package/packages/vite-plugin/tests/generation.test.ts +1036 -0
- package/packages/vite-plugin/tests/icp-command.test.ts +194 -0
- package/packages/vite-plugin/tests/icp-timeout.test.ts +83 -0
- package/packages/vite-plugin/tests/no-process-exit.test.ts +83 -0
- package/packages/vite-plugin/tests/on-generated.test.ts +323 -0
- package/packages/vite-plugin/tests/output-collision.test.ts +329 -0
- package/packages/vite-plugin/tests/package.test.ts +62 -0
- package/packages/vite-plugin/tests/project.test.ts +2993 -0
- package/packages/vite-plugin/tests/support.ts +339 -0
- package/packages/vite-plugin/tests/timeout.test.ts +107 -0
- package/packages/vite-plugin/tsconfig.json +23 -0
- package/packages/vite-plugin/tsconfig.typecheck.json +16 -0
- package/packages/vite-plugin/vitest.config.ts +9 -0
- package/pnpm-workspace.yaml +23 -0
- package/scripts/ai-context-files.js +72 -0
- package/scripts/assemble-docs-site.mjs +485 -0
- package/scripts/assemble-docs-site.test.mjs +568 -0
- package/scripts/build-examples.js +58 -0
- package/scripts/check-ai-context.js +963 -0
- package/scripts/check-ai-context.test.mjs +811 -0
- package/scripts/check-exports.mjs +767 -0
- package/scripts/check-exports.test.mjs +1136 -0
- package/scripts/check-snippets/ambient.d.ts +428 -0
- package/scripts/check-snippets/app/generated/backend.did +7 -0
- package/scripts/check-snippets/app/generated/backend.ts +15 -0
- package/scripts/check-snippets/app/generated/icrc1.did +88 -0
- package/scripts/check-snippets/app/generated/icrc1.ts +78 -0
- package/scripts/check-snippets/app/globals.ts +26 -0
- package/scripts/check-snippets/docs/guides/App.tsx +21 -0
- package/scripts/check-snippets/docs/guides/app/providers.tsx +23 -0
- package/scripts/check-snippets/docs/guides/auth-client.ts +47 -0
- package/scripts/check-snippets/docs/guides/canisters/backend.ts +3 -0
- package/scripts/check-snippets/docs/guides/canisters/icrc1.ts +3 -0
- package/scripts/check-snippets/docs/guides/components/LedgerCard.tsx +26 -0
- package/scripts/check-snippets/docs/guides/components/shell.tsx +11 -0
- package/scripts/check-snippets/docs/guides/components/sign-in-page.tsx +29 -0
- package/scripts/check-snippets/docs/guides/components/slow-section.tsx +5 -0
- package/scripts/check-snippets/docs/guides/generated/accounts.ts +95 -0
- package/scripts/check-snippets/docs/guides/generated/ckbtc_minter.ts +40 -0
- package/scripts/check-snippets/docs/guides/generated/icp_ledger.ts +267 -0
- package/scripts/check-snippets/docs/guides/ledger.ts +10 -0
- package/scripts/check-snippets/docs/guides/server/request-client.ts +8 -0
- package/scripts/check-snippets.mjs +993 -0
- package/scripts/check-snippets.test.mjs +338 -0
- package/scripts/check-stack.mjs +501 -0
- package/scripts/check-stack.test.mjs +800 -0
- package/scripts/clean-node-modules.js +67 -0
- package/scripts/example-projects.js +58 -0
- package/scripts/export-budget.mjs +145 -0
- package/scripts/faults.json +6749 -0
- package/scripts/lib/fault-workspace.mjs +210 -0
- package/scripts/lib/io.mjs +29 -0
- package/scripts/lib/io.test.mjs +38 -0
- package/scripts/lib/markdown-fences.mjs +56 -0
- package/scripts/lib/markdown-fences.test.mjs +84 -0
- package/scripts/lib/package-exports.mjs +54 -0
- package/scripts/lib/package-exports.test.mjs +66 -0
- package/scripts/lib/release-pr-steps.mjs +47 -0
- package/scripts/release-tag.mjs +95 -0
- package/scripts/release-tag.test.mjs +278 -0
- package/scripts/release-workflow.test.mjs +98 -0
- package/scripts/release.js +167 -0
- package/scripts/release.test.mjs +347 -0
- package/scripts/removed-v3-names.js +125 -0
- package/scripts/size-app/.size-limit.js +90 -0
- package/scripts/size-app/core.js +3 -0
- package/scripts/size-app/package.json +24 -0
- package/scripts/size-app/react.js +4 -0
- package/scripts/size-app.test.mjs +73 -0
- package/scripts/sync-ai-context-versions.js +102 -0
- package/scripts/sync-example-versions.js +151 -0
- package/scripts/test-examples-published.mjs +1585 -0
- package/scripts/test-examples-published.test.mjs +1586 -0
- package/scripts/traps/any-method-vars-not-a-list.json +5 -0
- package/scripts/traps/canister-method-unknown.json +5 -0
- package/scripts/traps/client-says-who-calls-once.json +5 -0
- package/scripts/traps/client-says-who-calls.json +5 -0
- package/scripts/traps/err-only-on-canister-err.json +14 -0
- package/scripts/traps/format-units-refuses-missing-value.json +5 -0
- package/scripts/traps/format-units-refuses-number.json +5 -0
- package/scripts/traps/format-units-unknown-option.json +5 -0
- package/scripts/traps/guard-err-is-the-methods.json +5 -0
- package/scripts/traps/guard-false-keeps-every-kind.json +5 -0
- package/scripts/traps/internals-not-exported.json +9 -0
- package/scripts/traps/network-refuses-unknown-name.json +5 -0
- package/scripts/traps/never-err-has-no-type.json +5 -0
- package/scripts/traps/options-method-unknown.json +5 -0
- package/scripts/traps/options-vars-wrong-type.json +5 -0
- package/scripts/traps/parse-units-refuses-number.json +5 -0
- package/scripts/traps/parse-units-unknown-option.json +5 -0
- package/scripts/traps/principal-is-not-plain-text.json +14 -0
- package/scripts/traps/query-options-refuses-oneway.json +5 -0
- package/scripts/traps/query-options-refuses-update.json +5 -0
- package/scripts/traps/refuse-next-unknown-option.json +5 -0
- package/scripts/traps/resend-key-reads-the-methods-arg.json +5 -0
- package/scripts/traps/resend-states-its-dedupe-key.json +5 -0
- package/scripts/traps/suspense-refuses-argument-skip.json +5 -0
- package/scripts/traps/suspense-refuses-skippable-read.json +5 -0
- package/scripts/traps/suspense-refuses-vars-skip-fits.json +5 -0
- package/scripts/traps/test-handler-method-unknown.json +5 -0
- package/scripts/traps/test-handler-nat-is-bigint.json +5 -0
- package/scripts/traps/test-handler-takes-the-domain-value.json +5 -0
- package/scripts/traps/vars-of-two-arguments-are-the-tuple.json +5 -0
- package/scripts/typecheck-examples.js +122 -0
- package/scripts/verify-faults.mjs +375 -0
- package/scripts/verify-faults.test.mjs +402 -0
- package/scripts/verify-package-artifacts.mjs +590 -0
- package/scripts/verify-peer-floors.js +399 -0
- package/scripts/verify-peer-floors.test.mjs +422 -0
- package/scripts/verify-test-fails-rules.mjs +104 -0
- package/scripts/verify-test-fails-rules.test.mjs +215 -0
- package/scripts/verify-test-fails.js +371 -0
- package/scripts/verify-traps.mjs +505 -0
- package/scripts/verify-traps.test.mjs +521 -0
- package/scripts/verify-typescript-floor.js +239 -0
- package/skill-packages/ic-reactor/SKILL.md +103 -0
- package/stack.json +8 -0
- package/tsconfig.base.json +23 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,1243 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"
|
|
2
|
+
import * as vitePluginModule from "./index.js"
|
|
3
|
+
import type { IcReactorPluginOptions } from "./index.js"
|
|
4
|
+
import path from "node:path"
|
|
5
|
+
import { spawn } from "child_process"
|
|
6
|
+
import { installIcpAdvice } from "./env.js"
|
|
7
|
+
import { icpProcess } from "../tests/support.js"
|
|
8
|
+
import { getCanisterEnv } from "@icp-sdk/core/agent/canister-env"
|
|
9
|
+
import { resolveConfig as resolveViteConfig } from "vite"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The plugins a test created, each closed after it as a server's close does.
|
|
13
|
+
* The close ends the network watch a served plugin starts, which would
|
|
14
|
+
* otherwise keep connecting to the port the test's `icp` named (#881).
|
|
15
|
+
*/
|
|
16
|
+
const created: any[] = []
|
|
17
|
+
|
|
18
|
+
const createVitePlugin = (...args: unknown[]) => {
|
|
19
|
+
const plugin = (
|
|
20
|
+
(vitePluginModule as any).icReactor ??
|
|
21
|
+
(vitePluginModule as any).icReactorPlugin
|
|
22
|
+
)(...args)
|
|
23
|
+
created.push(plugin)
|
|
24
|
+
return plugin
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Mock child_process
|
|
28
|
+
vi.mock("child_process", () => ({
|
|
29
|
+
spawn: vi.fn(),
|
|
30
|
+
}))
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Answer each `icp` command with what `answer` returns for its arguments: its
|
|
34
|
+
* stdout, or an Error to fail with. An Error's `stderr`, or else its message,
|
|
35
|
+
* is the command's stderr (`icpProcess`).
|
|
36
|
+
*/
|
|
37
|
+
function mockIcp(answer: (args: string[]) => string | Error) {
|
|
38
|
+
;(spawn as any).mockImplementation((_file: string, args: string[]) =>
|
|
39
|
+
icpProcess(answer(args))
|
|
40
|
+
)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A Vite root that is deliberately not the process cwd, so every path
|
|
45
|
+
* assertion below fails if the plugin falls back to `process.cwd()`.
|
|
46
|
+
*/
|
|
47
|
+
const VITE_ROOT = path.resolve("/vite/root")
|
|
48
|
+
|
|
49
|
+
describe("icReactor", () => {
|
|
50
|
+
const mockOptions: IcReactorPluginOptions = {
|
|
51
|
+
canisters: { test_canister: {} },
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const mockServer: any = {
|
|
55
|
+
config: {
|
|
56
|
+
root: VITE_ROOT,
|
|
57
|
+
logger: {
|
|
58
|
+
info: vi.fn(),
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
middlewares: {
|
|
62
|
+
use: vi.fn(),
|
|
63
|
+
},
|
|
64
|
+
// A refused canister, with a didFile or not, is replayed to each browser
|
|
65
|
+
// that connects.
|
|
66
|
+
ws: {
|
|
67
|
+
on: vi.fn(),
|
|
68
|
+
send: vi.fn(),
|
|
69
|
+
},
|
|
70
|
+
restart: vi.fn(),
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
beforeEach(() => {
|
|
74
|
+
vi.resetAllMocks()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
for (const plugin of created.splice(0)) plugin.closeBundle.call({})
|
|
79
|
+
vi.restoreAllMocks()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it("should return correct plugin structure", () => {
|
|
83
|
+
const plugin = createVitePlugin(mockOptions)
|
|
84
|
+
expect(plugin.name).toBe("ic-reactor-plugin")
|
|
85
|
+
expect(plugin.configureServer).toBeDefined()
|
|
86
|
+
expect(plugin.configurePreviewServer).toBeDefined()
|
|
87
|
+
expect((plugin as any).config).toBeDefined()
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
// A JavaScript config is not type-checked: read by truthiness, `0`, `""`
|
|
91
|
+
// or `null` would turn the option off and `"false"` would turn it on.
|
|
92
|
+
it("should refuse a failOnError that is not true or false", () => {
|
|
93
|
+
for (const value of [null, 0, "", "false"]) {
|
|
94
|
+
expect(() => createVitePlugin({ failOnError: value })).toThrow(
|
|
95
|
+
`[ic-reactor] failOnError is ${JSON.stringify(value)}, not true or false: ` +
|
|
96
|
+
"set it to true or false, or remove it for the default"
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
for (const value of [undefined, true, false]) {
|
|
100
|
+
expect(() => createVitePlugin({ failOnError: value })).not.toThrow()
|
|
101
|
+
}
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
it("should refuse an onGenerated that is not a function", () => {
|
|
105
|
+
for (const value of [null, 0, "", "hook", {}]) {
|
|
106
|
+
expect(() => createVitePlugin({ onGenerated: value })).toThrow(
|
|
107
|
+
/^\[ic-reactor\] onGenerated is .*, not a function: give it a function to call after each generated module, or remove it$/
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
for (const value of [undefined, () => {}, async () => {}]) {
|
|
111
|
+
expect(() => createVitePlugin({ onGenerated: value })).not.toThrow()
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
it("should refuse an injectEnvironment that is not true or false", () => {
|
|
116
|
+
for (const value of [null, 0, "", "false"]) {
|
|
117
|
+
expect(() => createVitePlugin({ injectEnvironment: value })).toThrow(
|
|
118
|
+
`[ic-reactor] injectEnvironment is ${JSON.stringify(value)}, not true or false: ` +
|
|
119
|
+
"set it to true or false, or remove it for the default"
|
|
120
|
+
)
|
|
121
|
+
}
|
|
122
|
+
for (const value of [undefined, true, false]) {
|
|
123
|
+
expect(() => createVitePlugin({ injectEnvironment: value })).not.toThrow()
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
// Only an omitted option takes its default. `canisters: null` read as none
|
|
128
|
+
// started the plugin with no canister: nothing generated, none in the
|
|
129
|
+
// cookie, and the bindings of the last run left as they were.
|
|
130
|
+
it("should refuse canisters, or the options, given as null or any value that is not an object, and take only an omitted one for none", () => {
|
|
131
|
+
const given: Array<[unknown, string]> = [
|
|
132
|
+
[null, "null"],
|
|
133
|
+
[0, "0"],
|
|
134
|
+
["", '""'],
|
|
135
|
+
["ledger", '"ledger"'],
|
|
136
|
+
[false, "false"],
|
|
137
|
+
[[], "[]"],
|
|
138
|
+
[() => ({}), "a function"],
|
|
139
|
+
[1n, "1n"],
|
|
140
|
+
]
|
|
141
|
+
for (const [value, quoted] of given) {
|
|
142
|
+
expect(() => createVitePlugin({ canisters: value })).toThrow(
|
|
143
|
+
`[ic-reactor] canisters is ${quoted}, not a plain object of canisters by name: ` +
|
|
144
|
+
"give each canister's options under its name, or remove canisters"
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
expect(() => createVitePlugin(null)).toThrow(
|
|
148
|
+
"[ic-reactor] the options are null, not a plain object: give icReactor() an object of options, or none"
|
|
149
|
+
)
|
|
150
|
+
for (const options of [
|
|
151
|
+
undefined,
|
|
152
|
+
{},
|
|
153
|
+
{ canisters: undefined },
|
|
154
|
+
{ canisters: {} },
|
|
155
|
+
]) {
|
|
156
|
+
expect(() => createVitePlugin(options)).not.toThrow()
|
|
157
|
+
}
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
// An object that is not a plain one is no object of canisters: its keys
|
|
161
|
+
// are not the canisters it holds. Read for them, a Map of canisters, a
|
|
162
|
+
// Promise of them (an async loader not awaited) or the canisters a
|
|
163
|
+
// prototype holds were none, and the plugin started with no canister,
|
|
164
|
+
// without a word, as for canisters: null.
|
|
165
|
+
it("should refuse canisters, or the options, given as an object that is not a plain one, and take one with no prototype", () => {
|
|
166
|
+
class Canisters {
|
|
167
|
+
ledger = { didFile: "did/ledger.did" }
|
|
168
|
+
}
|
|
169
|
+
const canisters = { ledger: { didFile: "did/ledger.did" } }
|
|
170
|
+
const given: Array<[unknown, string]> = [
|
|
171
|
+
[new Map(Object.entries(canisters)), "Map {}"],
|
|
172
|
+
[Promise.resolve(canisters), "Promise {}"],
|
|
173
|
+
[new Canisters(), 'Canisters {"ledger":{"didFile":"did/ledger.did"}}'],
|
|
174
|
+
[Object.create(canisters), "{} (an object that inherits from another)"],
|
|
175
|
+
[new Date(0), "Date {}"],
|
|
176
|
+
[new Number(1), "Number {}"],
|
|
177
|
+
]
|
|
178
|
+
for (const [value, quoted] of given) {
|
|
179
|
+
expect(() => createVitePlugin({ canisters: value })).toThrow(
|
|
180
|
+
`[ic-reactor] canisters is ${quoted}, not a plain object of canisters by name: ` +
|
|
181
|
+
"give each canister's options under its name, or remove canisters"
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
const options = { canisters }
|
|
185
|
+
for (const [value, quoted] of [
|
|
186
|
+
[new Map(Object.entries(options)), "Map {}"],
|
|
187
|
+
[Promise.resolve(options), "Promise {}"],
|
|
188
|
+
] as const) {
|
|
189
|
+
expect(() => createVitePlugin(value)).toThrow(
|
|
190
|
+
`[ic-reactor] the options are ${quoted}, not a plain object: give icReactor() an object of options, or none`
|
|
191
|
+
)
|
|
192
|
+
}
|
|
193
|
+
// An object with no prototype, as Object.create(null) makes, is plain.
|
|
194
|
+
const bare = Object.assign(Object.create(null), canisters)
|
|
195
|
+
expect(() => createVitePlugin({ canisters: bare })).not.toThrow()
|
|
196
|
+
expect(() =>
|
|
197
|
+
createVitePlugin(Object.assign(Object.create(null), { canisters: bare }))
|
|
198
|
+
).not.toThrow()
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// A key that is no option was never read: a misspelled canisters started
|
|
202
|
+
// the plugin with no canister, without a word, as canisters: null did, and
|
|
203
|
+
// a misspelled failOnError left a build to ship stale bindings.
|
|
204
|
+
it("should refuse a key of the options that is not an option, whatever its value, and name the option it may misspell", () => {
|
|
205
|
+
const canisters = { ledger: { didFile: "did/ledger.did" } }
|
|
206
|
+
const given: Array<[unknown, string]> = [
|
|
207
|
+
[
|
|
208
|
+
{ canister: canisters },
|
|
209
|
+
'"canister" (did you mean canisters?) is not an option',
|
|
210
|
+
],
|
|
211
|
+
[
|
|
212
|
+
{ canisters, FailOnError: true },
|
|
213
|
+
'"FailOnError" (did you mean failOnError?) is not an option',
|
|
214
|
+
],
|
|
215
|
+
// Nine letters from canisters, case and all, and none with case aside.
|
|
216
|
+
[
|
|
217
|
+
{ CANISTERS: canisters },
|
|
218
|
+
'"CANISTERS" (did you mean canisters?) is not an option',
|
|
219
|
+
],
|
|
220
|
+
// No option is near enough to name.
|
|
221
|
+
[
|
|
222
|
+
{ canisters, outDir: "src/declarations", verbose: true },
|
|
223
|
+
'"outDir" and "verbose" are not options',
|
|
224
|
+
],
|
|
225
|
+
// The key is what is wrong, whatever its value.
|
|
226
|
+
[
|
|
227
|
+
{ canisters, failOnErorr: undefined },
|
|
228
|
+
'"failOnErorr" (did you mean failOnError?) is not an option',
|
|
229
|
+
],
|
|
230
|
+
// A JSON config's own key, as any other.
|
|
231
|
+
[
|
|
232
|
+
JSON.parse('{ "__proto__": {}, "canisters": {} }'),
|
|
233
|
+
'"__proto__" is not an option',
|
|
234
|
+
],
|
|
235
|
+
]
|
|
236
|
+
for (const [value, refused] of given) {
|
|
237
|
+
expect(() => createVitePlugin(value)).toThrow(
|
|
238
|
+
`[ic-reactor] ${refused} of icReactor(): ` +
|
|
239
|
+
"its options are canisters, injectEnvironment, failOnError and onGenerated"
|
|
240
|
+
)
|
|
241
|
+
}
|
|
242
|
+
const unlisted = new Proxy(
|
|
243
|
+
{ canisters },
|
|
244
|
+
{
|
|
245
|
+
ownKeys() {
|
|
246
|
+
throw new Error("no keys today")
|
|
247
|
+
},
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
expect(() => createVitePlugin(unlisted)).toThrow(
|
|
251
|
+
"[ic-reactor] the options could not be listed: no keys today"
|
|
252
|
+
)
|
|
253
|
+
for (const known of [
|
|
254
|
+
{},
|
|
255
|
+
{ canisters, injectEnvironment: true, failOnError: false },
|
|
256
|
+
{ canisters: undefined, injectEnvironment: undefined },
|
|
257
|
+
]) {
|
|
258
|
+
expect(() => createVitePlugin(known)).not.toThrow()
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
describe("config", () => {
|
|
263
|
+
/**
|
|
264
|
+
* Every canister id `icp` reports, by canister name: its own properties
|
|
265
|
+
* only, so that a canister named `toString` is one `icp` does not know.
|
|
266
|
+
*/
|
|
267
|
+
type Deployed = Record<string, string>
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* `icp canister status`'s answer to its arguments `args`, read as icp's
|
|
271
|
+
* parser reads them: `-e` takes a value, `-i` none, `--` ends the
|
|
272
|
+
* options, and the first other argument is the canister. Before `--`,
|
|
273
|
+
* an argument that begins with `-` is a flag: `--help` prints the help,
|
|
274
|
+
* with exit 0, and any other unknown one is refused.
|
|
275
|
+
*/
|
|
276
|
+
const statusOf = (args: string[], deployed: Deployed): string | Error => {
|
|
277
|
+
let name: string | undefined
|
|
278
|
+
for (let index = 0; index < args.length; index++) {
|
|
279
|
+
const arg = args[index]
|
|
280
|
+
if (arg === "--") {
|
|
281
|
+
name ??= args[index + 1]
|
|
282
|
+
break
|
|
283
|
+
}
|
|
284
|
+
if (arg === "-e") index += 1
|
|
285
|
+
else if (arg === "-h" || arg === "--help") {
|
|
286
|
+
return "Show the status of canister(s).\n\nUsage: icp canister status [OPTIONS] [CANISTER]\n"
|
|
287
|
+
} else if (arg.startsWith("-") && arg !== "-i") {
|
|
288
|
+
return new Error(`error: unexpected argument '${arg}' found`)
|
|
289
|
+
} else if (arg !== "-i") name ??= arg
|
|
290
|
+
}
|
|
291
|
+
return name !== undefined &&
|
|
292
|
+
Object.prototype.hasOwnProperty.call(deployed, name)
|
|
293
|
+
? deployed[name]
|
|
294
|
+
: new Error(`canister ${name} not found`)
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Answer each `icp` command the plugin runs: the network status, or
|
|
299
|
+
* `network` as the error it fails with, and each canister's id from
|
|
300
|
+
* `deployed`, failing for a canister that is not in it.
|
|
301
|
+
*/
|
|
302
|
+
const answerIcp = (
|
|
303
|
+
deployed: Deployed,
|
|
304
|
+
network: Record<string, unknown> | Error = {
|
|
305
|
+
root_key: "mock-root-key",
|
|
306
|
+
port: 4943,
|
|
307
|
+
}
|
|
308
|
+
) =>
|
|
309
|
+
mockIcp((args) => {
|
|
310
|
+
if (args[0] === "network") {
|
|
311
|
+
return network instanceof Error ? network : JSON.stringify(network)
|
|
312
|
+
}
|
|
313
|
+
return statusOf(args.slice(2), deployed)
|
|
314
|
+
})
|
|
315
|
+
|
|
316
|
+
/** How many `icp` commands the plugin has run. */
|
|
317
|
+
const icpCalls = () => (spawn as any).mock.calls.length
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Run the config hook as `vite dev` does, then configureServer, and return
|
|
321
|
+
* the config and the middleware that sets the cookie.
|
|
322
|
+
*/
|
|
323
|
+
const serveWithEnvironment = async (plugin: any, userConfig = {}) => {
|
|
324
|
+
const config = await plugin.config(userConfig, { command: "serve" })
|
|
325
|
+
mockServer.middlewares.use.mockClear()
|
|
326
|
+
plugin.configureServer(mockServer)
|
|
327
|
+
const middleware = mockServer.middlewares.use.mock.calls[0]?.[0]
|
|
328
|
+
return { config, middleware }
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** A page load. */
|
|
332
|
+
const PAGE = { method: "GET", accept: "text/html,application/xhtml+xml" }
|
|
333
|
+
/** A module or `fetch` request, which is not a page load. */
|
|
334
|
+
const MODULE = { method: "GET", accept: "*/*" }
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* The Set-Cookie of a page load with no cookie to send: it clears one the
|
|
338
|
+
* browser kept (#881).
|
|
339
|
+
*/
|
|
340
|
+
const CLEARED = "ic_env=; Path=/; SameSite=Lax; Max-Age=0"
|
|
341
|
+
|
|
342
|
+
/** Send `middleware` a request and return the Set-Cookie it added. */
|
|
343
|
+
const request = async (
|
|
344
|
+
middleware: any,
|
|
345
|
+
{ method, accept }: { method: string; accept: string } = PAGE
|
|
346
|
+
): Promise<string | undefined> => {
|
|
347
|
+
const headers = new Map<string, unknown>()
|
|
348
|
+
const res = {
|
|
349
|
+
getHeader: (name: string) => headers.get(name.toLowerCase()),
|
|
350
|
+
setHeader: (name: string, value: unknown) => {
|
|
351
|
+
headers.set(name.toLowerCase(), value)
|
|
352
|
+
},
|
|
353
|
+
}
|
|
354
|
+
await new Promise<void>((resolve, reject) =>
|
|
355
|
+
middleware({ method, headers: { accept } }, res, (error?: unknown) =>
|
|
356
|
+
error ? reject(error) : resolve()
|
|
357
|
+
)
|
|
358
|
+
)
|
|
359
|
+
const cookie = headers.get("set-cookie")
|
|
360
|
+
return cookie === undefined ? undefined : String(cookie)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* The `ic_env` cookie of a `Set-Cookie` header as the app reads it, with
|
|
365
|
+
* `@icp-sdk/core`'s `getCanisterEnv`, as a page does.
|
|
366
|
+
*/
|
|
367
|
+
const readBack = (setCookie: string | undefined) => {
|
|
368
|
+
vi.stubGlobal("document", { cookie: String(setCookie).split(";")[0] })
|
|
369
|
+
try {
|
|
370
|
+
return getCanisterEnv()
|
|
371
|
+
} finally {
|
|
372
|
+
vi.unstubAllGlobals()
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
it("should set up the API proxy and the cookie when icp-cli is available", async () => {
|
|
377
|
+
answerIcp({ test_canister: "mock-canister-id" })
|
|
378
|
+
|
|
379
|
+
const plugin = createVitePlugin(mockOptions)
|
|
380
|
+
const { config, middleware } = await serveWithEnvironment(plugin)
|
|
381
|
+
|
|
382
|
+
const cookie = await request(middleware)
|
|
383
|
+
expect(cookie).toContain("ic_env=")
|
|
384
|
+
expect(cookie).toContain(
|
|
385
|
+
"PUBLIC_CANISTER_ID%3Atest_canister%3Dmock-canister-id"
|
|
386
|
+
)
|
|
387
|
+
expect(cookie).toContain("ic_root_key%3Dmock-root-key")
|
|
388
|
+
expect(cookie).toContain("; Path=/; SameSite=Lax;")
|
|
389
|
+
expect(config.server.proxy["/api"]).toMatchObject({
|
|
390
|
+
target: "http://127.0.0.1:4943",
|
|
391
|
+
changeOrigin: true,
|
|
392
|
+
})
|
|
393
|
+
// Set per response by the middleware, not fixed at startup.
|
|
394
|
+
expect(config.server.headers).toBeUndefined()
|
|
395
|
+
})
|
|
396
|
+
|
|
397
|
+
// icp looks for icp.yaml in its working directory and the directories above
|
|
398
|
+
// it. `vite apps/web` from a monorepo root, or `root` in the config, leaves
|
|
399
|
+
// the process cwd outside the app, so icp has to start from Vite's root.
|
|
400
|
+
it("should run icp from the Vite root rather than the process cwd", async () => {
|
|
401
|
+
answerIcp({ test_canister: "mock-canister-id" })
|
|
402
|
+
|
|
403
|
+
const plugin = createVitePlugin(mockOptions)
|
|
404
|
+
await plugin.config({ root: "apps/web" }, { command: "serve" })
|
|
405
|
+
|
|
406
|
+
const workingDirs = (spawn as any).mock.calls.map(
|
|
407
|
+
([, , options]: any) => options?.cwd
|
|
408
|
+
)
|
|
409
|
+
expect(workingDirs.length).toBeGreaterThan(1)
|
|
410
|
+
expect(new Set(workingDirs)).toEqual(new Set([path.resolve("apps/web")]))
|
|
411
|
+
})
|
|
412
|
+
|
|
413
|
+
it("should prefer configured canisterId over CLI-discovered env values", async () => {
|
|
414
|
+
answerIcp({ test_canister: "local-cli-canister-id" })
|
|
415
|
+
|
|
416
|
+
const plugin = createVitePlugin({
|
|
417
|
+
...mockOptions,
|
|
418
|
+
canisters: {
|
|
419
|
+
test_canister: { canisterId: "yq4ns-hyaaa-aaaap-akbna-cai" },
|
|
420
|
+
},
|
|
421
|
+
})
|
|
422
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
423
|
+
|
|
424
|
+
const cookie = await request(middleware)
|
|
425
|
+
expect(cookie).toContain(
|
|
426
|
+
"PUBLIC_CANISTER_ID%3Atest_canister%3Dyq4ns-hyaaa-aaaap-akbna-cai"
|
|
427
|
+
)
|
|
428
|
+
expect(cookie).not.toContain("local-cli-canister-id")
|
|
429
|
+
})
|
|
430
|
+
|
|
431
|
+
// Its answer would be thrown away, and each question is an `icp` process
|
|
432
|
+
// (#980).
|
|
433
|
+
it("should not ask icp for a canister whose canisterId is configured, but for internet_identity", async () => {
|
|
434
|
+
answerIcp({
|
|
435
|
+
other: "bkyz2-fmaaa-aaaaa-qaaaq-cai",
|
|
436
|
+
internet_identity: "rdmx6-jaaaa-aaaaa-aaadq-cai",
|
|
437
|
+
})
|
|
438
|
+
const askedFor = () =>
|
|
439
|
+
(spawn as any).mock.calls
|
|
440
|
+
.map(([, args]: [string, string[]]) => args)
|
|
441
|
+
.filter((args: string[]) => args[0] === "canister")
|
|
442
|
+
.map((args: string[]) => args[args.length - 1])
|
|
443
|
+
|
|
444
|
+
const plugin = createVitePlugin({
|
|
445
|
+
canisters: {
|
|
446
|
+
test_canister: { canisterId: "yq4ns-hyaaa-aaaap-akbna-cai" },
|
|
447
|
+
other: {},
|
|
448
|
+
},
|
|
449
|
+
})
|
|
450
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
451
|
+
await request(middleware)
|
|
452
|
+
expect(askedFor()).toEqual(["other", "internet_identity"])
|
|
453
|
+
|
|
454
|
+
// A configured internet_identity is still looked up: icp reports the
|
|
455
|
+
// project's own, so the cookie names no built-in provider beside it.
|
|
456
|
+
;(spawn as any).mockClear()
|
|
457
|
+
const withIdentity = createVitePlugin({
|
|
458
|
+
canisters: {
|
|
459
|
+
other: {},
|
|
460
|
+
internet_identity: { canisterId: "rdmx6-jaaaa-aaaaa-aaadq-cai" },
|
|
461
|
+
},
|
|
462
|
+
})
|
|
463
|
+
const served = await serveWithEnvironment(withIdentity)
|
|
464
|
+
const cookie = await request(served.middleware)
|
|
465
|
+
expect(askedFor()).toEqual(["other", "internet_identity"])
|
|
466
|
+
expect(cookie).toContain(
|
|
467
|
+
"PUBLIC_CANISTER_ID%3Ainternet_identity%3Drdmx6-jaaaa-aaaaa-aaadq-cai"
|
|
468
|
+
)
|
|
469
|
+
expect(cookie).not.toContain("INTERNET_IDENTITY_PROVIDER")
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
// A canister's name is kept as given, and a JSON config can name one
|
|
473
|
+
// __proto__. Kept by name in a plain object, its configured ID went to
|
|
474
|
+
// the prototype's setter instead, and the cookie carried no ID for it.
|
|
475
|
+
it("should give a canister named __proto__ or constructor its configured canisterId over the one icp reports", async () => {
|
|
476
|
+
answerIcp(
|
|
477
|
+
Object.fromEntries([
|
|
478
|
+
["__proto__", "icp-proto-id"],
|
|
479
|
+
["constructor", "icp-constructor-id"],
|
|
480
|
+
])
|
|
481
|
+
)
|
|
482
|
+
|
|
483
|
+
const plugin = createVitePlugin({
|
|
484
|
+
canisters: JSON.parse(
|
|
485
|
+
'{"__proto__": {"canisterId": "ryjl3-tyaaa-aaaaa-aaaba-cai"},' +
|
|
486
|
+
' "constructor": {"canisterId": "mqygn-kiaaa-aaaar-qaadq-cai"}}'
|
|
487
|
+
),
|
|
488
|
+
})
|
|
489
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
490
|
+
|
|
491
|
+
const cookie = await request(middleware)
|
|
492
|
+
expect(cookie).toContain(
|
|
493
|
+
"PUBLIC_CANISTER_ID%3A__proto__%3Dryjl3-tyaaa-aaaaa-aaaba-cai"
|
|
494
|
+
)
|
|
495
|
+
expect(cookie).toContain(
|
|
496
|
+
"PUBLIC_CANISTER_ID%3Aconstructor%3Dmqygn-kiaaa-aaaar-qaadq-cai"
|
|
497
|
+
)
|
|
498
|
+
expect(cookie).not.toContain("icp-proto-id")
|
|
499
|
+
expect(cookie).not.toContain("icp-constructor-id")
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
// Looked up in a plain object, a canister named after a member of
|
|
503
|
+
// Object.prototype found that member, so it was never missing: no
|
|
504
|
+
// warning, no page load asked icp again, and the cookie never carried its
|
|
505
|
+
// ID. __proto__'s ID, which icp reported, went to the prototype's setter.
|
|
506
|
+
it("should ask icp for a canister named __proto__, constructor, toString or hasOwnProperty until it has an ID, and give the cookie the one icp reports", async () => {
|
|
507
|
+
const consoleWarnSpy = vi
|
|
508
|
+
.spyOn(console, "warn")
|
|
509
|
+
.mockImplementation(() => {})
|
|
510
|
+
vi.spyOn(console, "log").mockImplementation(() => {})
|
|
511
|
+
const names = ["__proto__", "constructor", "toString", "hasOwnProperty"]
|
|
512
|
+
answerIcp({})
|
|
513
|
+
const plugin = createVitePlugin({
|
|
514
|
+
canisters: JSON.parse(
|
|
515
|
+
'{"__proto__": {}, "constructor": {}, "toString": {}, "hasOwnProperty": {}}'
|
|
516
|
+
),
|
|
517
|
+
})
|
|
518
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
519
|
+
|
|
520
|
+
expect(consoleWarnSpy).toHaveBeenCalledOnce()
|
|
521
|
+
expect(consoleWarnSpy.mock.calls[0][0]).toContain(
|
|
522
|
+
'no canister ID could be resolved for "__proto__", "constructor", "toString", "hasOwnProperty"'
|
|
523
|
+
)
|
|
524
|
+
expect(await request(middleware)).not.toContain("PUBLIC_CANISTER_ID")
|
|
525
|
+
|
|
526
|
+
// Deployed: the next page load asks icp, and the cookie carries each ID.
|
|
527
|
+
answerIcp(
|
|
528
|
+
Object.fromEntries(
|
|
529
|
+
names.map((name, index) => [name, `icp-id-${index}`])
|
|
530
|
+
)
|
|
531
|
+
)
|
|
532
|
+
const cookie = await request(middleware)
|
|
533
|
+
names.forEach((name, index) =>
|
|
534
|
+
expect(cookie).toContain(
|
|
535
|
+
`PUBLIC_CANISTER_ID%3A${name}%3Dicp-id-${index}`
|
|
536
|
+
)
|
|
537
|
+
)
|
|
538
|
+
})
|
|
539
|
+
|
|
540
|
+
// icp gives a canister a name of ASCII letters, digits, _ and -, which
|
|
541
|
+
// may begin with -. Handed to icp before its options ended, such a name
|
|
542
|
+
// was read as a flag: icp refused it, or printed its help for --help,
|
|
543
|
+
// with exit 0, which the cookie then carried as the canister's ID.
|
|
544
|
+
it("should ask icp for a canister whose name begins with - by that name, and give the cookie the ID it reports", async () => {
|
|
545
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
546
|
+
answerIcp({ "-x": "icp-dash-id", "--help": "icp-help-id" })
|
|
547
|
+
|
|
548
|
+
const plugin = createVitePlugin({ canisters: { "-x": {}, "--help": {} } })
|
|
549
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
550
|
+
|
|
551
|
+
const cookie = await request(middleware)
|
|
552
|
+
expect(cookie).toContain("PUBLIC_CANISTER_ID%3A-x%3Dicp-dash-id")
|
|
553
|
+
expect(cookie).toContain("PUBLIC_CANISTER_ID%3A--help%3Dicp-help-id")
|
|
554
|
+
expect(cookie).not.toContain("Show%20the%20status")
|
|
555
|
+
})
|
|
556
|
+
|
|
557
|
+
// The cookie joins its entries with & and each name to its ID with =, as
|
|
558
|
+
// the asset canister writes it, and @icp-sdk/core reads it back so. A
|
|
559
|
+
// name that held either was read back as other keys, one of which could
|
|
560
|
+
// take the place of another canister's entry or of the root key: the app
|
|
561
|
+
// then read no environment at all.
|
|
562
|
+
it("should leave a canister whose name holds & or = out of the cookie, with a warning, and the app reads back every other name as given", async () => {
|
|
563
|
+
const consoleWarnSpy = vi
|
|
564
|
+
.spyOn(console, "warn")
|
|
565
|
+
.mockImplementation(() => {})
|
|
566
|
+
answerIcp(
|
|
567
|
+
{
|
|
568
|
+
"-x": "icp-dash-id",
|
|
569
|
+
"a=b": "icp-equals-id",
|
|
570
|
+
"x&ic_root_key=00": "icp-amp-id",
|
|
571
|
+
},
|
|
572
|
+
{ root_key: "ab".repeat(133), port: 4943 }
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
const plugin = createVitePlugin({
|
|
576
|
+
canisters: JSON.parse(`{
|
|
577
|
+
"__proto__": { "canisterId": "ryjl3-tyaaa-aaaaa-aaaba-cai" },
|
|
578
|
+
"-x": {},
|
|
579
|
+
"a b": { "canisterId": "bkyz2-fmaaa-aaaaa-qaaaq-cai" },
|
|
580
|
+
"a=b": {},
|
|
581
|
+
"x&ic_root_key=00": { "canisterId": "mqygn-kiaaa-aaaar-qaadq-cai" }
|
|
582
|
+
}`),
|
|
583
|
+
})
|
|
584
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
585
|
+
|
|
586
|
+
expect(readBack(await request(middleware))).toEqual({
|
|
587
|
+
IC_ROOT_KEY: new Uint8Array(133).fill(0xab),
|
|
588
|
+
"PUBLIC_CANISTER_ID:__proto__": "ryjl3-tyaaa-aaaaa-aaaba-cai",
|
|
589
|
+
"PUBLIC_CANISTER_ID:-x": "icp-dash-id",
|
|
590
|
+
"PUBLIC_CANISTER_ID:a b": "bkyz2-fmaaa-aaaaa-qaaaq-cai",
|
|
591
|
+
INTERNET_IDENTITY_PROVIDER: "http://id.ai.localhost:4943/authorize",
|
|
592
|
+
})
|
|
593
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
|
594
|
+
'[ic-reactor] The ic_env cookie cannot carry the canisters "a=b", "x&ic_root_key=00": ' +
|
|
595
|
+
'it joins its entries with "&" and each name to its ID with "=", so a name that holds either ' +
|
|
596
|
+
"would be read back as other keys, and it cannot encode text that is not well-formed. They are left " +
|
|
597
|
+
"out of the cookie, and icp is not asked for their IDs. Name the canister as icp does: ASCII letters, " +
|
|
598
|
+
'digits, "_" and "-".'
|
|
599
|
+
)
|
|
600
|
+
const asked = (spawn as any).mock.calls.map(
|
|
601
|
+
([, args]: [string, string[]]) => args[args.length - 1]
|
|
602
|
+
)
|
|
603
|
+
expect(asked).toContain("-x")
|
|
604
|
+
expect(asked).not.toContain("a=b")
|
|
605
|
+
expect(asked).not.toContain("x&ic_root_key=00")
|
|
606
|
+
})
|
|
607
|
+
|
|
608
|
+
// Text that is not well-formed, a lone surrogate as a JSON config can
|
|
609
|
+
// hold, cannot be URI-encoded: the cookie threw as it was built, and no
|
|
610
|
+
// cookie was set for any canister.
|
|
611
|
+
it("should leave a canister whose name is not well-formed text out of the cookie, with a warning, and set the cookie for the others", async () => {
|
|
612
|
+
const consoleWarnSpy = vi
|
|
613
|
+
.spyOn(console, "warn")
|
|
614
|
+
.mockImplementation(() => {})
|
|
615
|
+
answerIcp({ "-x": "icp-dash-id" })
|
|
616
|
+
|
|
617
|
+
const plugin = createVitePlugin({
|
|
618
|
+
canisters: JSON.parse(
|
|
619
|
+
'{ "-x": {}, "\\ud800": { "canisterId": "yq4ns-hyaaa-aaaap-akbna-cai" } }'
|
|
620
|
+
),
|
|
621
|
+
})
|
|
622
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
623
|
+
|
|
624
|
+
const cookie = await request(middleware)
|
|
625
|
+
expect(cookie).toBeDefined()
|
|
626
|
+
expect(cookie).toContain("PUBLIC_CANISTER_ID%3A-x%3Dicp-dash-id")
|
|
627
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
|
628
|
+
'[ic-reactor] The ic_env cookie cannot carry the canister "\\ud800": ' +
|
|
629
|
+
'it joins its entries with "&" and each name to its ID with "=", so a name that holds either ' +
|
|
630
|
+
"would be read back as other keys, and it cannot encode text that is not well-formed. It is left " +
|
|
631
|
+
"out of the cookie, and icp is not asked for its ID. Name the canister as icp does: ASCII letters, " +
|
|
632
|
+
'digits, "_" and "-".'
|
|
633
|
+
)
|
|
634
|
+
})
|
|
635
|
+
|
|
636
|
+
// A JavaScript config is not type-checked: a configured canisterId that
|
|
637
|
+
// is not text, or is empty, is neither put in the cookie nor dropped
|
|
638
|
+
// without a word. Neither is text that is no principal, which the cookie
|
|
639
|
+
// would hand the app as the canister's ID.
|
|
640
|
+
it("should leave a configured canisterId that is not one to icp, and warn", async () => {
|
|
641
|
+
const consoleWarnSpy = vi
|
|
642
|
+
.spyOn(console, "warn")
|
|
643
|
+
.mockImplementation(() => {})
|
|
644
|
+
answerIcp({
|
|
645
|
+
empty: "icp-empty",
|
|
646
|
+
nil: "icp-nil",
|
|
647
|
+
number: "icp-number",
|
|
648
|
+
text: "icp-text",
|
|
649
|
+
})
|
|
650
|
+
|
|
651
|
+
const plugin = createVitePlugin({
|
|
652
|
+
canisters: {
|
|
653
|
+
empty: { canisterId: "" },
|
|
654
|
+
nil: { canisterId: null },
|
|
655
|
+
number: { canisterId: 123 },
|
|
656
|
+
// yq4ns-hyaaa-aaaap-akbna-cai with its last letter changed.
|
|
657
|
+
text: { canisterId: "yq4ns-hyaaa-aaaap-akbna-caj" },
|
|
658
|
+
},
|
|
659
|
+
})
|
|
660
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
661
|
+
|
|
662
|
+
const cookie = await request(middleware)
|
|
663
|
+
expect(cookie).toContain("PUBLIC_CANISTER_ID%3Aempty%3Dicp-empty")
|
|
664
|
+
expect(cookie).toContain("PUBLIC_CANISTER_ID%3Anil%3Dicp-nil")
|
|
665
|
+
expect(cookie).toContain("PUBLIC_CANISTER_ID%3Anumber%3Dicp-number")
|
|
666
|
+
expect(cookie).toContain("PUBLIC_CANISTER_ID%3Atext%3Dicp-text")
|
|
667
|
+
expect(cookie).not.toContain("akbna-caj")
|
|
668
|
+
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
|
669
|
+
'[ic-reactor] canisterId is not a canister ID (empty: ""; nil: null; number: 123; text: "yq4ns-hyaaa-aaaap-akbna-caj"), ' +
|
|
670
|
+
"so the ic_env cookie takes the ID of those canisters from icp. " +
|
|
671
|
+
"Give the canister's principal as text, or remove canisterId."
|
|
672
|
+
)
|
|
673
|
+
})
|
|
674
|
+
|
|
675
|
+
it("should inject built-in local Internet Identity provider when no project II canister is found", async () => {
|
|
676
|
+
answerIcp(
|
|
677
|
+
{ test_canister: "mock-canister-id" },
|
|
678
|
+
{ root_key: "mock-root-key", api_url: "http://localhost:8000/" }
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
const plugin = createVitePlugin(mockOptions)
|
|
682
|
+
const { config, middleware } = await serveWithEnvironment(plugin)
|
|
683
|
+
|
|
684
|
+
expect(await request(middleware)).toContain(
|
|
685
|
+
"INTERNET_IDENTITY_PROVIDER%3Dhttp%3A%2F%2Fid.ai.localhost%3A8000%2Fauthorize"
|
|
686
|
+
)
|
|
687
|
+
expect(config.server.proxy["/api"].target).toBe("http://localhost:8000/")
|
|
688
|
+
})
|
|
689
|
+
|
|
690
|
+
it("should fallback to default proxy when icp-cli fails", async () => {
|
|
691
|
+
const consoleWarnSpy = vi
|
|
692
|
+
.spyOn(console, "warn")
|
|
693
|
+
.mockImplementation(() => {})
|
|
694
|
+
mockIcp(() => new Error("Command not found"))
|
|
695
|
+
|
|
696
|
+
const plugin = createVitePlugin(mockOptions)
|
|
697
|
+
const { config, middleware } = await serveWithEnvironment(plugin)
|
|
698
|
+
|
|
699
|
+
expect(await request(middleware)).toBe(CLEARED)
|
|
700
|
+
expect(config.server.proxy["/api"].target).toBe("http://127.0.0.1:8000")
|
|
701
|
+
// Silent detection failure is indistinguishable from a working setup
|
|
702
|
+
// until the app blows up on an undefined canister id at runtime. The
|
|
703
|
+
// warning says what the plugin does about it.
|
|
704
|
+
expect(consoleWarnSpy).toHaveBeenCalledOnce()
|
|
705
|
+
const [warning] = consoleWarnSpy.mock.calls[0]
|
|
706
|
+
expect(warning).toContain("Could not detect the local IC environment")
|
|
707
|
+
expect(warning).toContain("/api goes to http://127.0.0.1:8000 for now")
|
|
708
|
+
expect(warning).toContain(
|
|
709
|
+
"asks `icp` again on each page load until it answers"
|
|
710
|
+
)
|
|
711
|
+
expect(warning).toContain("reload the page")
|
|
712
|
+
})
|
|
713
|
+
|
|
714
|
+
it("says icp could not be run, not that the network may be down, when no icp starts", async () => {
|
|
715
|
+
const consoleWarnSpy = vi
|
|
716
|
+
.spyOn(console, "warn")
|
|
717
|
+
.mockImplementation(() => {})
|
|
718
|
+
// What spawn fails with when the file cannot be found: on Windows,
|
|
719
|
+
// also what an npm-installed icp.cmd gives.
|
|
720
|
+
mockIcp(() =>
|
|
721
|
+
Object.assign(new Error("spawn icp ENOENT"), { code: "ENOENT" })
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
const plugin = createVitePlugin(mockOptions)
|
|
725
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
726
|
+
|
|
727
|
+
expect(await request(middleware)).toBe(CLEARED)
|
|
728
|
+
expect(consoleWarnSpy).toHaveBeenCalledOnce()
|
|
729
|
+
const [warning] = consoleWarnSpy.mock.calls[0]
|
|
730
|
+
expect(warning).toContain("Could not run `icp`")
|
|
731
|
+
expect(warning).toContain(installIcpAdvice())
|
|
732
|
+
expect(warning).not.toContain("Is the local network running?")
|
|
733
|
+
})
|
|
734
|
+
|
|
735
|
+
// The replica being UP while a configured canister is simply not deployed
|
|
736
|
+
// is the common case: `vite dev` usually starts before `icp deploy`.
|
|
737
|
+
it("should warn when the replica is up but a configured canister has no id", async () => {
|
|
738
|
+
const consoleWarnSpy = vi
|
|
739
|
+
.spyOn(console, "warn")
|
|
740
|
+
.mockImplementation(() => {})
|
|
741
|
+
answerIcp({})
|
|
742
|
+
|
|
743
|
+
const plugin = createVitePlugin(mockOptions)
|
|
744
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
745
|
+
|
|
746
|
+
expect(consoleWarnSpy).toHaveBeenCalledOnce()
|
|
747
|
+
const [warning] = consoleWarnSpy.mock.calls[0]
|
|
748
|
+
expect(warning).toContain(
|
|
749
|
+
'no canister ID could be resolved for "test_canister"'
|
|
750
|
+
)
|
|
751
|
+
// It used to promise that deploying was enough, while the cookie stayed
|
|
752
|
+
// without the id until the dev server restarted.
|
|
753
|
+
expect(warning).toContain(
|
|
754
|
+
"Deploy it (`icp deploy`) and reload the page: the plugin asks `icp` " +
|
|
755
|
+
"again on each page load until every configured canister has an ID."
|
|
756
|
+
)
|
|
757
|
+
const cookie = await request(middleware)
|
|
758
|
+
expect(cookie).toContain("ic_root_key%3Dmock-root-key")
|
|
759
|
+
expect(cookie).not.toContain("PUBLIC_CANISTER_ID")
|
|
760
|
+
})
|
|
761
|
+
|
|
762
|
+
it("should stay quiet about failed detection when no canisters are configured", async () => {
|
|
763
|
+
const consoleWarnSpy = vi
|
|
764
|
+
.spyOn(console, "warn")
|
|
765
|
+
.mockImplementation(() => {})
|
|
766
|
+
mockIcp(() => new Error("project manifest not found"))
|
|
767
|
+
|
|
768
|
+
const plugin = createVitePlugin({ canisters: {} })
|
|
769
|
+
await plugin.config({}, { command: "serve" })
|
|
770
|
+
|
|
771
|
+
expect(consoleWarnSpy).not.toHaveBeenCalled()
|
|
772
|
+
})
|
|
773
|
+
|
|
774
|
+
it("should surface the icp stderr at debug level", async () => {
|
|
775
|
+
const consoleDebugSpy = vi
|
|
776
|
+
.spyOn(console, "debug")
|
|
777
|
+
.mockImplementation(() => {})
|
|
778
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
779
|
+
vi.stubEnv("DEBUG", "ic-reactor")
|
|
780
|
+
mockIcp(() =>
|
|
781
|
+
Object.assign(new Error("Command failed: icp network status"), {
|
|
782
|
+
stderr: "error: no local network is running\n",
|
|
783
|
+
})
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
const plugin = createVitePlugin(mockOptions)
|
|
787
|
+
await plugin.config({}, { command: "serve" })
|
|
788
|
+
|
|
789
|
+
expect(consoleDebugSpy).toHaveBeenCalledWith(
|
|
790
|
+
expect.stringContaining("no local network is running")
|
|
791
|
+
)
|
|
792
|
+
|
|
793
|
+
vi.unstubAllEnvs()
|
|
794
|
+
})
|
|
795
|
+
|
|
796
|
+
it("should inject default local II provider in env-only mode when icp-cli project detection fails", async () => {
|
|
797
|
+
mockIcp(() => new Error("project manifest not found"))
|
|
798
|
+
|
|
799
|
+
const plugin = createVitePlugin({ canisters: {} })
|
|
800
|
+
const { config, middleware } = await serveWithEnvironment(plugin)
|
|
801
|
+
|
|
802
|
+
expect(await request(middleware)).toContain(
|
|
803
|
+
"INTERNET_IDENTITY_PROVIDER%3Dhttp%3A%2F%2Fid.ai.localhost%3A8000%2Fauthorize"
|
|
804
|
+
)
|
|
805
|
+
expect(config.server.proxy["/api"].target).toBe("http://127.0.0.1:8000")
|
|
806
|
+
})
|
|
807
|
+
|
|
808
|
+
it("should return empty config for build command", async () => {
|
|
809
|
+
const plugin = createVitePlugin(mockOptions)
|
|
810
|
+
const config = await (plugin as any).config({}, { command: "build" })
|
|
811
|
+
|
|
812
|
+
expect(config).toEqual({})
|
|
813
|
+
expect(spawn).not.toHaveBeenCalled()
|
|
814
|
+
})
|
|
815
|
+
|
|
816
|
+
it("should add no middleware when injectEnvironment is off", async () => {
|
|
817
|
+
const plugin = createVitePlugin({
|
|
818
|
+
...mockOptions,
|
|
819
|
+
injectEnvironment: false,
|
|
820
|
+
})
|
|
821
|
+
const { config, middleware } = await serveWithEnvironment(plugin)
|
|
822
|
+
|
|
823
|
+
expect(config).toEqual({})
|
|
824
|
+
expect(middleware).toBeUndefined()
|
|
825
|
+
expect(spawn).not.toHaveBeenCalled()
|
|
826
|
+
})
|
|
827
|
+
|
|
828
|
+
// `vite preview` resolves the config with the `serve` command, and used to
|
|
829
|
+
// get the cookie through `preview.headers`, which defaults to
|
|
830
|
+
// `server.headers`.
|
|
831
|
+
it("should set the cookie on the preview server too", async () => {
|
|
832
|
+
answerIcp({ test_canister: "mock-canister-id" })
|
|
833
|
+
|
|
834
|
+
const plugin = createVitePlugin(mockOptions)
|
|
835
|
+
await plugin.config({}, { command: "serve" })
|
|
836
|
+
const previewServer = {
|
|
837
|
+
middlewares: { use: vi.fn() },
|
|
838
|
+
httpServer: { once: vi.fn() },
|
|
839
|
+
}
|
|
840
|
+
plugin.configurePreviewServer(previewServer)
|
|
841
|
+
|
|
842
|
+
expect(
|
|
843
|
+
await request(previewServer.middlewares.use.mock.calls[0][0])
|
|
844
|
+
).toContain("PUBLIC_CANISTER_ID%3Atest_canister%3Dmock-canister-id")
|
|
845
|
+
|
|
846
|
+
// The preview server's close ends its network watch.
|
|
847
|
+
expect(previewServer.httpServer.once).toHaveBeenCalledWith(
|
|
848
|
+
"close",
|
|
849
|
+
expect.any(Function)
|
|
850
|
+
)
|
|
851
|
+
previewServer.httpServer.once.mock.calls[0][1]()
|
|
852
|
+
})
|
|
853
|
+
|
|
854
|
+
// `vite dev` usually starts before the canister is deployed. The cookie
|
|
855
|
+
// was fixed at startup, so after `icp deploy` every reload still lacked the
|
|
856
|
+
// id until the dev server restarted (#664).
|
|
857
|
+
describe("while detection is incomplete", () => {
|
|
858
|
+
it("should give a page load after a deploy the new canister's id", async () => {
|
|
859
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
860
|
+
const consoleLogSpy = vi
|
|
861
|
+
.spyOn(console, "log")
|
|
862
|
+
.mockImplementation(() => {})
|
|
863
|
+
answerIcp({})
|
|
864
|
+
const plugin = createVitePlugin(mockOptions)
|
|
865
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
866
|
+
expect(await request(middleware)).not.toContain("PUBLIC_CANISTER_ID")
|
|
867
|
+
|
|
868
|
+
answerIcp({ test_canister: "bkyz2-fmaaa-aaaaa-qaaaq-cai" })
|
|
869
|
+
|
|
870
|
+
expect(await request(middleware)).toContain(
|
|
871
|
+
"PUBLIC_CANISTER_ID%3Atest_canister%3Dbkyz2-fmaaa-aaaaa-qaaaq-cai"
|
|
872
|
+
)
|
|
873
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
874
|
+
'[ic-reactor] The ic_env cookie now carries the canister ID for "test_canister".'
|
|
875
|
+
)
|
|
876
|
+
})
|
|
877
|
+
|
|
878
|
+
it("should pick up a network that was down at startup and send /api to it", async () => {
|
|
879
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
880
|
+
const consoleLogSpy = vi
|
|
881
|
+
.spyOn(console, "log")
|
|
882
|
+
.mockImplementation(() => {})
|
|
883
|
+
answerIcp({}, new Error("no local network is running"))
|
|
884
|
+
const plugin = createVitePlugin(mockOptions)
|
|
885
|
+
const { config, middleware } = await serveWithEnvironment(plugin)
|
|
886
|
+
|
|
887
|
+
// Vite builds the proxy from a copy of the entry and hands that copy to
|
|
888
|
+
// `configure`. It reads the target from it on every request.
|
|
889
|
+
const apiEntry = config.server.proxy["/api"]
|
|
890
|
+
const proxyOptions = { ...apiEntry }
|
|
891
|
+
apiEntry.configure({}, proxyOptions)
|
|
892
|
+
expect(proxyOptions.target).toBe("http://127.0.0.1:8000")
|
|
893
|
+
expect(await request(middleware)).toBe(CLEARED)
|
|
894
|
+
|
|
895
|
+
// A gateway on another port than the fallback's, so the switch shows.
|
|
896
|
+
answerIcp(
|
|
897
|
+
{ test_canister: "bkyz2-fmaaa-aaaaa-qaaaq-cai" },
|
|
898
|
+
{ root_key: "fresh-root-key", api_url: "http://127.0.0.1:8080" }
|
|
899
|
+
)
|
|
900
|
+
|
|
901
|
+
const cookie = await request(middleware)
|
|
902
|
+
expect(cookie).toContain("ic_root_key%3Dfresh-root-key")
|
|
903
|
+
expect(cookie).toContain(
|
|
904
|
+
"PUBLIC_CANISTER_ID%3Atest_canister%3Dbkyz2-fmaaa-aaaaa-qaaaq-cai"
|
|
905
|
+
)
|
|
906
|
+
expect(proxyOptions.target).toBe("http://127.0.0.1:8080")
|
|
907
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
908
|
+
"[ic-reactor] Detected the local IC network: the ic_env cookie now carries its root key and /api goes to http://127.0.0.1:8080."
|
|
909
|
+
)
|
|
910
|
+
})
|
|
911
|
+
|
|
912
|
+
// A restart of an inline config runs the config hook again with the
|
|
913
|
+
// same plugin object, for a new server with a proxy of its own.
|
|
914
|
+
it("should move only its own server's /api proxy when a server of the same plugin object detects a network", async () => {
|
|
915
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
916
|
+
vi.spyOn(console, "log").mockImplementation(() => {})
|
|
917
|
+
answerIcp({}, new Error("no local network is running"))
|
|
918
|
+
const plugin = createVitePlugin(mockOptions)
|
|
919
|
+
const proxyOf = (config: any) => {
|
|
920
|
+
const apiEntry = config.server.proxy["/api"]
|
|
921
|
+
const proxyOptions = { ...apiEntry }
|
|
922
|
+
apiEntry.configure({}, proxyOptions)
|
|
923
|
+
return proxyOptions
|
|
924
|
+
}
|
|
925
|
+
const first = await serveWithEnvironment(plugin)
|
|
926
|
+
const firstProxy = proxyOf(first.config)
|
|
927
|
+
const second = await serveWithEnvironment(plugin)
|
|
928
|
+
const secondProxy = proxyOf(second.config)
|
|
929
|
+
|
|
930
|
+
// A page load of the first server finds the network.
|
|
931
|
+
answerIcp(
|
|
932
|
+
{ test_canister: "bkyz2-fmaaa-aaaaa-qaaaq-cai" },
|
|
933
|
+
{ root_key: "fresh-root-key", api_url: "http://127.0.0.1:8080" }
|
|
934
|
+
)
|
|
935
|
+
await request(first.middleware)
|
|
936
|
+
expect(firstProxy.target).toBe("http://127.0.0.1:8080")
|
|
937
|
+
expect(secondProxy.target).toBe("http://127.0.0.1:8000")
|
|
938
|
+
})
|
|
939
|
+
|
|
940
|
+
it("should not run icp for a request that is not a page load", async () => {
|
|
941
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
942
|
+
answerIcp({})
|
|
943
|
+
const plugin = createVitePlugin(mockOptions)
|
|
944
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
945
|
+
const calls = icpCalls()
|
|
946
|
+
|
|
947
|
+
const cookie = await request(middleware, MODULE)
|
|
948
|
+
await request(middleware, { method: "POST", accept: "text/html" })
|
|
949
|
+
|
|
950
|
+
expect(icpCalls()).toBe(calls)
|
|
951
|
+
// It still carries what the last detection found.
|
|
952
|
+
expect(cookie).toContain("ic_root_key%3Dmock-root-key")
|
|
953
|
+
})
|
|
954
|
+
|
|
955
|
+
it("should share one detection between page loads that arrive together", async () => {
|
|
956
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
957
|
+
answerIcp({})
|
|
958
|
+
const plugin = createVitePlugin(mockOptions)
|
|
959
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
960
|
+
const calls = icpCalls()
|
|
961
|
+
|
|
962
|
+
await Promise.all([request(middleware), request(middleware)])
|
|
963
|
+
|
|
964
|
+
const networkStatusCalls = (spawn as any).mock.calls
|
|
965
|
+
.slice(calls)
|
|
966
|
+
.filter(([, args]: any) => args[0] === "network")
|
|
967
|
+
expect(networkStatusCalls).toHaveLength(1)
|
|
968
|
+
})
|
|
969
|
+
|
|
970
|
+
// A command can fail for a moment while `icp deploy` runs. The cookie
|
|
971
|
+
// that already worked must survive that page load.
|
|
972
|
+
it("should keep what it detected when icp fails for a moment", async () => {
|
|
973
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
974
|
+
const plugin = createVitePlugin({
|
|
975
|
+
canisters: { alpha: {}, beta: {} },
|
|
976
|
+
})
|
|
977
|
+
answerIcp({ alpha: "alpha-id" })
|
|
978
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
979
|
+
|
|
980
|
+
// The network status fails outright.
|
|
981
|
+
answerIcp({}, new Error("lock held by another icp process"))
|
|
982
|
+
let cookie = await request(middleware)
|
|
983
|
+
expect(cookie).toContain("ic_root_key%3Dmock-root-key")
|
|
984
|
+
expect(cookie).toContain("PUBLIC_CANISTER_ID%3Aalpha%3Dalpha-id")
|
|
985
|
+
|
|
986
|
+
// The network answers, but alpha's lookup fails.
|
|
987
|
+
answerIcp({ beta: "beta-id" })
|
|
988
|
+
cookie = await request(middleware)
|
|
989
|
+
expect(cookie).toContain("PUBLIC_CANISTER_ID%3Aalpha%3Dalpha-id")
|
|
990
|
+
expect(cookie).toContain("PUBLIC_CANISTER_ID%3Abeta%3Dbeta-id")
|
|
991
|
+
})
|
|
992
|
+
|
|
993
|
+
it("should not name the built-in Internet Identity next to a kept internet_identity id", async () => {
|
|
994
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
995
|
+
const localhost = {
|
|
996
|
+
root_key: "mock-root-key",
|
|
997
|
+
api_url: "http://localhost:8000",
|
|
998
|
+
}
|
|
999
|
+
answerIcp({ internet_identity: "ii-id" }, localhost)
|
|
1000
|
+
const plugin = createVitePlugin(mockOptions)
|
|
1001
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
1002
|
+
|
|
1003
|
+
// internet_identity's lookup fails this time.
|
|
1004
|
+
answerIcp({}, localhost)
|
|
1005
|
+
const cookie = await request(middleware)
|
|
1006
|
+
|
|
1007
|
+
expect(cookie).toContain(
|
|
1008
|
+
"PUBLIC_CANISTER_ID%3Ainternet_identity%3Dii-id"
|
|
1009
|
+
)
|
|
1010
|
+
expect(cookie).not.toContain("INTERNET_IDENTITY_PROVIDER")
|
|
1011
|
+
})
|
|
1012
|
+
|
|
1013
|
+
it("should drop the ids of a network whose root key changed", async () => {
|
|
1014
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
1015
|
+
const plugin = createVitePlugin({
|
|
1016
|
+
canisters: { alpha: {}, beta: {} },
|
|
1017
|
+
})
|
|
1018
|
+
answerIcp({ alpha: "alpha-id" })
|
|
1019
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
1020
|
+
|
|
1021
|
+
answerIcp(
|
|
1022
|
+
{ beta: "beta-id" },
|
|
1023
|
+
{ root_key: "fresh-root-key", port: 4943 }
|
|
1024
|
+
)
|
|
1025
|
+
const cookie = await request(middleware)
|
|
1026
|
+
|
|
1027
|
+
expect(cookie).toContain("ic_root_key%3Dfresh-root-key")
|
|
1028
|
+
expect(cookie).toContain("PUBLIC_CANISTER_ID%3Abeta%3Dbeta-id")
|
|
1029
|
+
expect(cookie).not.toContain("alpha-id")
|
|
1030
|
+
})
|
|
1031
|
+
})
|
|
1032
|
+
|
|
1033
|
+
describe("once every configured canister has an id", () => {
|
|
1034
|
+
it("should run no icp command for a page load", async () => {
|
|
1035
|
+
answerIcp({ test_canister: "mock-canister-id" })
|
|
1036
|
+
const plugin = createVitePlugin(mockOptions)
|
|
1037
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
1038
|
+
const calls = icpCalls()
|
|
1039
|
+
|
|
1040
|
+
for (let load = 0; load < 3; load++) {
|
|
1041
|
+
expect(await request(middleware)).toContain(
|
|
1042
|
+
"PUBLIC_CANISTER_ID%3Atest_canister%3Dmock-canister-id"
|
|
1043
|
+
)
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
expect(icpCalls()).toBe(calls)
|
|
1047
|
+
})
|
|
1048
|
+
|
|
1049
|
+
it("should stop running icp after a page load resolves the last id", async () => {
|
|
1050
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
1051
|
+
const consoleLogSpy = vi
|
|
1052
|
+
.spyOn(console, "log")
|
|
1053
|
+
.mockImplementation(() => {})
|
|
1054
|
+
answerIcp({})
|
|
1055
|
+
const plugin = createVitePlugin(mockOptions)
|
|
1056
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
1057
|
+
|
|
1058
|
+
answerIcp({ test_canister: "mock-canister-id" })
|
|
1059
|
+
await request(middleware)
|
|
1060
|
+
const calls = icpCalls()
|
|
1061
|
+
await request(middleware)
|
|
1062
|
+
await request(middleware)
|
|
1063
|
+
|
|
1064
|
+
expect(icpCalls()).toBe(calls)
|
|
1065
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
1066
|
+
expect.stringContaining(
|
|
1067
|
+
"Every configured canister has an ID, so page loads no longer run `icp`."
|
|
1068
|
+
)
|
|
1069
|
+
)
|
|
1070
|
+
})
|
|
1071
|
+
|
|
1072
|
+
// A canister id set in the plugin config counts as resolved.
|
|
1073
|
+
it("should count a configured canisterId as resolved", async () => {
|
|
1074
|
+
answerIcp({})
|
|
1075
|
+
const plugin = createVitePlugin({
|
|
1076
|
+
...mockOptions,
|
|
1077
|
+
canisters: {
|
|
1078
|
+
test_canister: { canisterId: "yq4ns-hyaaa-aaaap-akbna-cai" },
|
|
1079
|
+
},
|
|
1080
|
+
})
|
|
1081
|
+
const { middleware } = await serveWithEnvironment(plugin)
|
|
1082
|
+
const calls = icpCalls()
|
|
1083
|
+
|
|
1084
|
+
await request(middleware)
|
|
1085
|
+
|
|
1086
|
+
expect(icpCalls()).toBe(calls)
|
|
1087
|
+
})
|
|
1088
|
+
})
|
|
1089
|
+
|
|
1090
|
+
// Vite deep-merges what a plugin's config hook returns over the user's
|
|
1091
|
+
// config, so the /api entry the plugin returned replaced the user's own.
|
|
1092
|
+
// examples/codegen-in-action proxied /api to its own replica and got the
|
|
1093
|
+
// plugin's fallback. Here the user's replica is a dfx one on 4943, not the
|
|
1094
|
+
// fallback's 8000. These run Vite's real resolveConfig, which does that
|
|
1095
|
+
// merge.
|
|
1096
|
+
describe("with a Vite config that already proxies /api", () => {
|
|
1097
|
+
const userApiProxy = {
|
|
1098
|
+
target: "http://127.0.0.1:4943",
|
|
1099
|
+
changeOrigin: true,
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
const resolveWithUserProxy = (plugin: any) =>
|
|
1103
|
+
resolveViteConfig(
|
|
1104
|
+
{
|
|
1105
|
+
configFile: false,
|
|
1106
|
+
logLevel: "silent",
|
|
1107
|
+
plugins: [plugin],
|
|
1108
|
+
server: { proxy: { "/api": userApiProxy } },
|
|
1109
|
+
},
|
|
1110
|
+
"serve"
|
|
1111
|
+
)
|
|
1112
|
+
|
|
1113
|
+
afterEach(() => {
|
|
1114
|
+
vi.unstubAllEnvs()
|
|
1115
|
+
})
|
|
1116
|
+
|
|
1117
|
+
it("should keep the user's proxy when icp detection succeeds", async () => {
|
|
1118
|
+
const consoleDebugSpy = vi
|
|
1119
|
+
.spyOn(console, "debug")
|
|
1120
|
+
.mockImplementation(() => {})
|
|
1121
|
+
vi.stubEnv("DEBUG", "ic-reactor")
|
|
1122
|
+
answerIcp({ test_canister: "mock-canister-id" })
|
|
1123
|
+
|
|
1124
|
+
const plugin = createVitePlugin(mockOptions)
|
|
1125
|
+
const resolved = await resolveWithUserProxy(plugin)
|
|
1126
|
+
|
|
1127
|
+
expect(resolved.server.proxy?.["/api"]).toEqual(userApiProxy)
|
|
1128
|
+
expect(consoleDebugSpy).toHaveBeenCalledWith(
|
|
1129
|
+
expect.stringContaining("already proxies /api")
|
|
1130
|
+
)
|
|
1131
|
+
// Everything else the plugin injects still arrives.
|
|
1132
|
+
mockServer.middlewares.use.mockClear()
|
|
1133
|
+
plugin.configureServer(mockServer)
|
|
1134
|
+
expect(
|
|
1135
|
+
await request(mockServer.middlewares.use.mock.calls[0][0])
|
|
1136
|
+
).toContain("ic_root_key%3Dmock-root-key")
|
|
1137
|
+
})
|
|
1138
|
+
|
|
1139
|
+
it.each([
|
|
1140
|
+
["with canisters configured", mockOptions],
|
|
1141
|
+
["in env-only mode", { canisters: {} }],
|
|
1142
|
+
])(
|
|
1143
|
+
"should keep the user's proxy when detection fails %s",
|
|
1144
|
+
async (_label, options) => {
|
|
1145
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
1146
|
+
mockIcp(() => new Error("project manifest not found"))
|
|
1147
|
+
|
|
1148
|
+
const resolved = await resolveWithUserProxy(createVitePlugin(options))
|
|
1149
|
+
|
|
1150
|
+
expect(resolved.server.proxy?.["/api"]).toEqual(userApiProxy)
|
|
1151
|
+
}
|
|
1152
|
+
)
|
|
1153
|
+
|
|
1154
|
+
// The warning must not claim a target the plugin does not proxy to.
|
|
1155
|
+
it("should leave /api out of the warning", async () => {
|
|
1156
|
+
const consoleWarnSpy = vi
|
|
1157
|
+
.spyOn(console, "warn")
|
|
1158
|
+
.mockImplementation(() => {})
|
|
1159
|
+
mockIcp(() => new Error("project manifest not found"))
|
|
1160
|
+
|
|
1161
|
+
await resolveWithUserProxy(createVitePlugin(mockOptions))
|
|
1162
|
+
|
|
1163
|
+
expect(consoleWarnSpy).toHaveBeenCalledOnce()
|
|
1164
|
+
expect(consoleWarnSpy.mock.calls[0][0]).not.toContain("/api")
|
|
1165
|
+
})
|
|
1166
|
+
})
|
|
1167
|
+
|
|
1168
|
+
it("should still proxy /api when the Vite config proxies only other paths", async () => {
|
|
1169
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
1170
|
+
mockIcp(() => new Error("project manifest not found"))
|
|
1171
|
+
|
|
1172
|
+
const resolved = await resolveViteConfig(
|
|
1173
|
+
{
|
|
1174
|
+
configFile: false,
|
|
1175
|
+
logLevel: "silent",
|
|
1176
|
+
plugins: [createVitePlugin(mockOptions)],
|
|
1177
|
+
server: { proxy: { "/assets": "http://127.0.0.1:9000" } },
|
|
1178
|
+
},
|
|
1179
|
+
"serve"
|
|
1180
|
+
)
|
|
1181
|
+
|
|
1182
|
+
expect(resolved.server.proxy).toEqual({
|
|
1183
|
+
"/assets": "http://127.0.0.1:9000",
|
|
1184
|
+
"/api": {
|
|
1185
|
+
target: "http://127.0.0.1:8000",
|
|
1186
|
+
changeOrigin: true,
|
|
1187
|
+
configure: expect.any(Function),
|
|
1188
|
+
},
|
|
1189
|
+
})
|
|
1190
|
+
})
|
|
1191
|
+
|
|
1192
|
+
// A plugin whose config hook runs after this one can proxy /api as well.
|
|
1193
|
+
// Vite merges its entry over the plugin's own and keeps the plugin's
|
|
1194
|
+
// `configure`, which then took /api back to the detected network.
|
|
1195
|
+
it("should leave /api where a later plugin points it", async () => {
|
|
1196
|
+
vi.spyOn(console, "warn").mockImplementation(() => {})
|
|
1197
|
+
const consoleLogSpy = vi
|
|
1198
|
+
.spyOn(console, "log")
|
|
1199
|
+
.mockImplementation(() => {})
|
|
1200
|
+
answerIcp({}, new Error("no local network is running"))
|
|
1201
|
+
const plugin = createVitePlugin(mockOptions)
|
|
1202
|
+
const laterTarget = "http://127.0.0.1:3000"
|
|
1203
|
+
|
|
1204
|
+
const resolved = await resolveViteConfig(
|
|
1205
|
+
{
|
|
1206
|
+
configFile: false,
|
|
1207
|
+
logLevel: "silent",
|
|
1208
|
+
plugins: [
|
|
1209
|
+
plugin,
|
|
1210
|
+
{
|
|
1211
|
+
name: "later-api-proxy",
|
|
1212
|
+
config: () => ({
|
|
1213
|
+
server: { proxy: { "/api": { target: laterTarget } } },
|
|
1214
|
+
}),
|
|
1215
|
+
},
|
|
1216
|
+
],
|
|
1217
|
+
},
|
|
1218
|
+
"serve"
|
|
1219
|
+
)
|
|
1220
|
+
const apiEntry = resolved.server.proxy?.["/api"] as any
|
|
1221
|
+
const proxyOptions = { ...apiEntry }
|
|
1222
|
+
apiEntry.configure({}, proxyOptions)
|
|
1223
|
+
expect(proxyOptions.target).toBe(laterTarget)
|
|
1224
|
+
|
|
1225
|
+
// The network comes up on another port. The cookie follows it, /api
|
|
1226
|
+
// stays with the later plugin, and the terminal does not claim it moved.
|
|
1227
|
+
answerIcp(
|
|
1228
|
+
{ test_canister: "mock-canister-id" },
|
|
1229
|
+
{ root_key: "mock-root-key", api_url: "http://127.0.0.1:8000" }
|
|
1230
|
+
)
|
|
1231
|
+
mockServer.middlewares.use.mockClear()
|
|
1232
|
+
plugin.configureServer(mockServer)
|
|
1233
|
+
expect(
|
|
1234
|
+
await request(mockServer.middlewares.use.mock.calls[0][0])
|
|
1235
|
+
).toContain("ic_root_key%3Dmock-root-key")
|
|
1236
|
+
|
|
1237
|
+
expect(proxyOptions.target).toBe(laterTarget)
|
|
1238
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
1239
|
+
"[ic-reactor] Detected the local IC network: the ic_env cookie now carries its root key."
|
|
1240
|
+
)
|
|
1241
|
+
})
|
|
1242
|
+
})
|
|
1243
|
+
})
|