@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,993 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Type-checks the TypeScript code fences that readers and AI agents copy.
|
|
4
|
+
*
|
|
5
|
+
* The root and package `llms.txt` guides, the skills, the READMEs and the
|
|
6
|
+
* other AI-context files are where an agent in a consumer project learns the
|
|
7
|
+
* API, and it pastes their snippets as they stand. `check:ai-context` keeps
|
|
8
|
+
* their versions and links current but never compiles a line of code, so a
|
|
9
|
+
* renamed export or a changed signature left a snippet that no longer
|
|
10
|
+
* compiles with every gate green.
|
|
11
|
+
*
|
|
12
|
+
* This extracts every ```ts, ```tsx and ```typescript fence from those files,
|
|
13
|
+
* writes each one to its own module in a temporary directory, and compiles them
|
|
14
|
+
* all as one TypeScript program with the compiler options of Vite's React
|
|
15
|
+
* template (`strict`, `jsx: react-jsx`, `moduleResolution: bundler`,
|
|
16
|
+
* `verbatimModuleSyntax`). The directory reaches the `@ic-reactor/*` packages
|
|
17
|
+
* through symlinks, so a snippet compiles against the declarations
|
|
18
|
+
* `pnpm build` wrote to `packages/<name>/dist`, through each package's
|
|
19
|
+
* `exports` map, as it would in an app that installed them. Build first.
|
|
20
|
+
*
|
|
21
|
+
* ## The app a snippet runs in
|
|
22
|
+
*
|
|
23
|
+
* A snippet takes an app for granted: a canister's generated
|
|
24
|
+
* `./declarations/backend`, a `./reactor` module holding the setup, a
|
|
25
|
+
* name built earlier on the page. `scripts/check-snippets/` holds that app,
|
|
26
|
+
* and a document whose snippets assume another one gets its own, because
|
|
27
|
+
* documents can disagree. On the `v4` line the app holds only what no
|
|
28
|
+
* library export provides (`app/globals.ts`), the modules
|
|
29
|
+
* `meant gen` writes (`app/generated/`, for the guide's
|
|
30
|
+
* `./generated/icrc1` and `./generated/backend`) and the third-party
|
|
31
|
+
* declarations (`ambient.d.ts`). The 3.x fixtures went with the 3.x runtime.
|
|
32
|
+
*
|
|
33
|
+
* - `scripts/check-snippets/app/` is the app of every document not listed in
|
|
34
|
+
* CONTEXTS below. A context directory holds only what differs for its
|
|
35
|
+
* documents; everything else falls through to `app/`.
|
|
36
|
+
* - A relative import that resolves to nothing (`./reactor`,
|
|
37
|
+
* `../declarations/backend`) resolves to the module of the same path in the
|
|
38
|
+
* snippet's context directory, then in `app/`. The longest trailing part of
|
|
39
|
+
* the path that names one wins, so `./declarations/backend/declarations/backend`
|
|
40
|
+
* finds `declarations/backend.ts`.
|
|
41
|
+
* - An `@/` import, the alias Next.js and Vite apps give their `src/`
|
|
42
|
+
* (`@/canisters/ledger/reactor`), resolves the same way with the `@` taken
|
|
43
|
+
* off: to `canisters/ledger/reactor.ts` in the context directory, then in
|
|
44
|
+
* `app/`. An alias names a path from the root, so only the whole path is
|
|
45
|
+
* tried, never a trailing part of it: `reactor` alone would be some other
|
|
46
|
+
* module. The fixture is the app's module at that path, shaped as the real
|
|
47
|
+
* app's is.
|
|
48
|
+
* - The names `globals.ts` exports stand for names a snippet uses without
|
|
49
|
+
* importing or declaring them, such as a `clientManager` from an earlier
|
|
50
|
+
* snippet. Each snippet imports those it does not declare itself, on a line
|
|
51
|
+
* added above its first.
|
|
52
|
+
* - A third-party module a snippet imports is the real package when an example
|
|
53
|
+
* app installs it (DEPENDENCY_SOURCES below links their dependencies), such
|
|
54
|
+
* as `@tanstack/react-router`, `@tanstack/react-form`, `react-hook-form` and
|
|
55
|
+
* `next`. `ambient.d.ts` declares each of the rest once, after the package's
|
|
56
|
+
* own types, and only as far as the snippets reach. Two declarations of one
|
|
57
|
+
* module would merge into overloads and hide a snippet's mistake, and an
|
|
58
|
+
* ambient declaration wins over an installed package, so a module an app
|
|
59
|
+
* installs is never declared there.
|
|
60
|
+
*
|
|
61
|
+
* Declare a missing app name in a fixture. Do not rewrite a snippet to suit the
|
|
62
|
+
* checker. A `globals.ts` never exports a library export, or a hook that one
|
|
63
|
+
* returns: a snippet that uses one must import it, from the library or from the
|
|
64
|
+
* module that binds it, or an agent pasting it cannot run it. Nor is an ambient declaration bent
|
|
65
|
+
* to make a snippet pass: fix the snippet, as a reader would have to.
|
|
66
|
+
*
|
|
67
|
+
* ## The consumer guides
|
|
68
|
+
*
|
|
69
|
+
* `llms.txt`, `llms-full.txt`, each package's `llms.txt` and the consumer skill
|
|
70
|
+
* (`skill-packages/ic-reactor/`) are what an agent in another project reads,
|
|
71
|
+
* and it pastes their snippets into an app that has none of the names above.
|
|
72
|
+
* Two rules are stricter there:
|
|
73
|
+
*
|
|
74
|
+
* - A snippet gets no globals. Every name it uses is imported or declared in
|
|
75
|
+
* it.
|
|
76
|
+
* - A relative import resolves first to a module the guide shows: a snippet
|
|
77
|
+
* of the same document (or of the same skill) whose first line names its
|
|
78
|
+
* file, such as `// src/reactor.ts` for `./reactor`. Only a module the guide
|
|
79
|
+
* never shows, such as the canister's declarations, comes from a fixture.
|
|
80
|
+
*
|
|
81
|
+
* The docs site's build guides (`build/` under the docs content root) follow
|
|
82
|
+
* the same two rules: each one takes an app from nothing to working and shows
|
|
83
|
+
* every file of it, so a reader can paste each file as it stands.
|
|
84
|
+
*
|
|
85
|
+
* ## Hooks
|
|
86
|
+
*
|
|
87
|
+
* The compiler cannot see the rule the guides teach first: a React hook runs
|
|
88
|
+
* only inside a component or a custom hook. An agent pastes a snippet as it
|
|
89
|
+
* stands, so a snippet that calls `useActorQuery(...)` or `query.useQuery()` at
|
|
90
|
+
* the top of the module teaches code that throws. A call of a `useXxx`
|
|
91
|
+
* function or method fails the snippet when it is outside every function, in a
|
|
92
|
+
* class, or in a function that is neither a component (a capitalised name) nor
|
|
93
|
+
* a custom hook (`useXxx`); an anonymous callback such as `renderHook(() =>
|
|
94
|
+
* useX())` is let through, since it can be either. Wrap it in a component, as a
|
|
95
|
+
* reader's code would be.
|
|
96
|
+
*
|
|
97
|
+
* ## Opting out
|
|
98
|
+
*
|
|
99
|
+
* A fence that is not code to paste, such as a type signature or an interface
|
|
100
|
+
* restating a library type, opts out with `nocheck` after its language,
|
|
101
|
+
* ```ts nocheck, or with a first line of `// @snippet-skip`. Use it sparingly:
|
|
102
|
+
* a skipped snippet is one nothing verifies. Give a placeholder such as `...`
|
|
103
|
+
* a real value instead.
|
|
104
|
+
*
|
|
105
|
+
* Usage
|
|
106
|
+
* node scripts/check-snippets.mjs [--docs] [--verbose] [--keep] [path...]
|
|
107
|
+
*
|
|
108
|
+
* path... check only the files whose repo-relative path contains one of
|
|
109
|
+
* these strings
|
|
110
|
+
* --docs also check the docs site's MDX pages, gated like the rest
|
|
111
|
+
* (`pnpm check:snippets:docs`). A page compiles against its
|
|
112
|
+
* section's fixtures in `scripts/check-snippets/docs/<section>/`
|
|
113
|
+
* (the page's directory under the docs content root, `root` for
|
|
114
|
+
* a top-level page), then against the default app. Their
|
|
115
|
+
* examples import libraries the example apps install, so it
|
|
116
|
+
* needs `pnpm install` to have run for the examples too.
|
|
117
|
+
* --verbose list every snippet with its result
|
|
118
|
+
* --keep leave the temporary directory in place (debugging)
|
|
119
|
+
*/
|
|
120
|
+
import {
|
|
121
|
+
cpSync,
|
|
122
|
+
existsSync,
|
|
123
|
+
mkdirSync,
|
|
124
|
+
mkdtempSync,
|
|
125
|
+
readFileSync,
|
|
126
|
+
readdirSync,
|
|
127
|
+
realpathSync,
|
|
128
|
+
rmSync,
|
|
129
|
+
statSync,
|
|
130
|
+
symlinkSync,
|
|
131
|
+
unlinkSync,
|
|
132
|
+
writeFileSync,
|
|
133
|
+
} from "node:fs"
|
|
134
|
+
import { createRequire } from "node:module"
|
|
135
|
+
import { tmpdir } from "node:os"
|
|
136
|
+
import { dirname, join, relative, sep } from "node:path"
|
|
137
|
+
import { fileURLToPath } from "node:url"
|
|
138
|
+
import { AI_CONTEXT_FILES } from "./ai-context-files.js"
|
|
139
|
+
import { closesFence, fenceOpening } from "./lib/markdown-fences.mjs"
|
|
140
|
+
|
|
141
|
+
const rootDir = join(dirname(fileURLToPath(import.meta.url)), "..")
|
|
142
|
+
const fixturesDir = join(rootDir, "scripts", "check-snippets")
|
|
143
|
+
const ts = createRequire(import.meta.url)("typescript")
|
|
144
|
+
|
|
145
|
+
const args = process.argv.slice(2)
|
|
146
|
+
const includeDocs = args.includes("--docs")
|
|
147
|
+
const verbose = args.includes("--verbose")
|
|
148
|
+
const keep = args.includes("--keep")
|
|
149
|
+
const filters = args.filter((arg) => !arg.startsWith("--"))
|
|
150
|
+
|
|
151
|
+
/** Fence languages that are compiled, and the extension each module gets. */
|
|
152
|
+
const LANGUAGES = { ts: ".ts", typescript: ".ts", tsx: ".tsx" }
|
|
153
|
+
|
|
154
|
+
const SKIP_INFO = "nocheck"
|
|
155
|
+
const SKIP_COMMENT = /^\s*\/\/\s*@snippet-skip\b/
|
|
156
|
+
/** A first line that is a triple-slash directive, such as `/// <reference lib="..." />`. */
|
|
157
|
+
const TRIPLE_SLASH = /^\s*\/\/\/\s*</
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* A first line naming the file the snippet is, such as `// src/reactor.ts`
|
|
161
|
+
* or `// app/Supply.tsx: a React Server Component`. It captures the path
|
|
162
|
+
* without its extension.
|
|
163
|
+
*/
|
|
164
|
+
const MODULE_HEADER = /^\s*\/\/\s*((?:[\w.-]+\/)*[\w-]+)\.tsx?\b/
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The guides an agent in a consumer project reads: the docs site's
|
|
168
|
+
* `llms.txt` and `llms-full.txt`, each package's `llms.txt` (shipped in its
|
|
169
|
+
* tarball) and the consumer skill. The agent pastes their snippets into an app
|
|
170
|
+
* that has none of this checker's globals, so a snippet there gets no
|
|
171
|
+
* globals: every name it uses is imported, declared, or exported by a module
|
|
172
|
+
* the guide itself shows.
|
|
173
|
+
*/
|
|
174
|
+
function isConsumerGuide(file) {
|
|
175
|
+
return (
|
|
176
|
+
file === "llms.txt" ||
|
|
177
|
+
file === "llms-full.txt" ||
|
|
178
|
+
/^packages\/[^/]+\/llms\.txt$/.test(file) ||
|
|
179
|
+
file.startsWith("skill-packages/ic-reactor/")
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* The documents whose snippets share the modules they define: all the files
|
|
185
|
+
* of one skill (its SKILL.md and references), otherwise one document.
|
|
186
|
+
*/
|
|
187
|
+
function moduleGroup(file) {
|
|
188
|
+
const skill = /^skill-packages\/[^/]+\//.exec(file)
|
|
189
|
+
return skill ? skill[0] : file
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/** The default app, `scripts/check-snippets/app/`. */
|
|
193
|
+
const DEFAULT_CONTEXT = "app"
|
|
194
|
+
|
|
195
|
+
const DOCS_ROOT = "docs/src/content/docs/"
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The context of a docs page: `docs/<section>` when
|
|
199
|
+
* `scripts/check-snippets/docs/<section>/` exists, where the section is the
|
|
200
|
+
* page's directory under the docs content root (`reference/factories`,
|
|
201
|
+
* `framework`) or `root` for a page at the top. A section's directory holds
|
|
202
|
+
* only what its pages assume beyond the default app; without one, the page
|
|
203
|
+
* gets the default app.
|
|
204
|
+
*/
|
|
205
|
+
/**
|
|
206
|
+
* A build guide of the docs site: a page under `build/` that shows every file
|
|
207
|
+
* of the app it builds. Its snippets are held to the consumer guides' rules.
|
|
208
|
+
*/
|
|
209
|
+
function isBuildGuide(file) {
|
|
210
|
+
return file.startsWith(`${DOCS_ROOT}build/`)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function docsContext(file) {
|
|
214
|
+
if (!file.startsWith(DOCS_ROOT)) return undefined
|
|
215
|
+
const dir = dirname(file.slice(DOCS_ROOT.length))
|
|
216
|
+
const context = `docs/${dir === "." ? "root" : dir}`
|
|
217
|
+
return existsSync(join(fixturesDir, context)) ? context : undefined
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Documents whose snippets assume an app other than the default one, by
|
|
222
|
+
* repo-relative path, and the directory under `scripts/check-snippets/` that
|
|
223
|
+
* holds what differs.
|
|
224
|
+
*/
|
|
225
|
+
const CONTEXTS = {}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* The compiler options of Vite's React + TypeScript template, the setup most
|
|
229
|
+
* consumers paste into. `verbatimModuleSyntax` is on there, so a snippet that
|
|
230
|
+
* imports a type without `type` fails in that app and fails here. Unlike the
|
|
231
|
+
* template, `noUnusedLocals` and `noUnusedParameters` are off (a snippet
|
|
232
|
+
* declares names to show them, not to use them), and `@types/node` joins the
|
|
233
|
+
* template's `vite/client`, for the `vite.config.ts` and script snippets.
|
|
234
|
+
* `vite/client` types `import.meta.env`, where a Vite app reads a canister id.
|
|
235
|
+
* `allowImportingTsExtensions` is on, as in the template, so a script that
|
|
236
|
+
* Node runs as TypeScript can import `./tools.ts` as Node needs it to.
|
|
237
|
+
*/
|
|
238
|
+
const COMPILER_OPTIONS = {
|
|
239
|
+
target: "ES2022",
|
|
240
|
+
lib: ["ES2022", "DOM", "DOM.Iterable"],
|
|
241
|
+
module: "ESNext",
|
|
242
|
+
moduleResolution: "bundler",
|
|
243
|
+
moduleDetection: "force",
|
|
244
|
+
verbatimModuleSyntax: true,
|
|
245
|
+
erasableSyntaxOnly: true,
|
|
246
|
+
allowImportingTsExtensions: true,
|
|
247
|
+
jsx: "react-jsx",
|
|
248
|
+
strict: true,
|
|
249
|
+
noEmit: true,
|
|
250
|
+
skipLibCheck: true,
|
|
251
|
+
noFallthroughCasesInSwitch: true,
|
|
252
|
+
types: ["node", "vite/client"],
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Where third-party packages are linked from, in order of preference. A
|
|
257
|
+
* snippet can import anything a workspace package installs, and the first
|
|
258
|
+
* directory listed here that has it wins, so `react` and `@tanstack/*`
|
|
259
|
+
* resolve to the copies `@ic-reactor/react` itself compiles against.
|
|
260
|
+
*
|
|
261
|
+
* A docs page whose examples import a library no package installs (a router,
|
|
262
|
+
* a form library, `next`) needs an example app that installs it listed after
|
|
263
|
+
* the runtime packages, so that `react`, `@tanstack/react-query` and the rest
|
|
264
|
+
* keep the versions the packages themselves compile against.
|
|
265
|
+
* `examples/next-ssr` provides `next` for the SSR guide's App Router
|
|
266
|
+
* snippets (`next/server`, `next/headers`). A library only a build guide's app
|
|
267
|
+
* uses, and no example, is a devDependency of `docs`, listed last:
|
|
268
|
+
* `@modelcontextprotocol/server`, `@modelcontextprotocol/client` and `zod` for
|
|
269
|
+
* the agent tools guide.
|
|
270
|
+
*/
|
|
271
|
+
const DEPENDENCY_SOURCES = [
|
|
272
|
+
"packages/react",
|
|
273
|
+
"packages/core",
|
|
274
|
+
"packages/vite-plugin",
|
|
275
|
+
".",
|
|
276
|
+
"examples/next-ssr",
|
|
277
|
+
"docs",
|
|
278
|
+
]
|
|
279
|
+
|
|
280
|
+
// ── Source files ─────────────────────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
function walk(dir, predicate, out = []) {
|
|
283
|
+
// A tree that does not exist, such as skill-packages/ before a skill lands,
|
|
284
|
+
// holds no files.
|
|
285
|
+
if (!existsSync(dir)) return out
|
|
286
|
+
for (const entry of readdirSync(dir).sort()) {
|
|
287
|
+
const path = join(dir, entry)
|
|
288
|
+
if (statSync(path).isDirectory()) {
|
|
289
|
+
if (entry === "node_modules" || entry === "dist") continue
|
|
290
|
+
walk(path, predicate, out)
|
|
291
|
+
} else if (predicate(path)) {
|
|
292
|
+
out.push(path)
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
return out
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const toPosix = (path) => path.split(sep).join("/")
|
|
299
|
+
|
|
300
|
+
const packageDirs = readdirSync(join(rootDir, "packages"))
|
|
301
|
+
.filter((dir) => existsSync(join(rootDir, "packages", dir, "package.json")))
|
|
302
|
+
.sort()
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* The files agents and npm readers are pointed at: the READMEs and guides, the
|
|
306
|
+
* skills, and every file of `scripts/ai-context-files.js`. The last is what
|
|
307
|
+
* ties this gate to `check:ai-context`: a file that script checks for stale
|
|
308
|
+
* versions and removed names, this one compiles the snippets of, so a guide
|
|
309
|
+
* (the DX3 `packages/core/llms.txt`, a skill, a pointer) is gated from the
|
|
310
|
+
* commit that adds it, with no second list to forget.
|
|
311
|
+
*/
|
|
312
|
+
function gatedFiles() {
|
|
313
|
+
return [
|
|
314
|
+
...new Set([
|
|
315
|
+
"llms.txt",
|
|
316
|
+
"llms-full.txt",
|
|
317
|
+
"README.md",
|
|
318
|
+
...packageDirs.flatMap((dir) => [
|
|
319
|
+
`packages/${dir}/llms.txt`,
|
|
320
|
+
`packages/${dir}/README.md`,
|
|
321
|
+
]),
|
|
322
|
+
...walk(join(rootDir, "skill-packages"), (path) =>
|
|
323
|
+
path.endsWith(".md")
|
|
324
|
+
).map((path) => toPosix(relative(rootDir, path))),
|
|
325
|
+
...AI_CONTEXT_FILES,
|
|
326
|
+
]),
|
|
327
|
+
].filter((file) => existsSync(join(rootDir, file)))
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** The docs site's hand-written pages; `libs/` is TypeDoc output. */
|
|
331
|
+
function docsFiles() {
|
|
332
|
+
const docsDir = join(rootDir, "docs", "src", "content", "docs")
|
|
333
|
+
return walk(docsDir, (path) => path.endsWith(".mdx"))
|
|
334
|
+
.filter((path) => !path.startsWith(join(docsDir, "libs") + sep))
|
|
335
|
+
.map((path) => toPosix(relative(rootDir, path)))
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// ── Fence extraction ─────────────────────────────────────────────────────────
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* The fenced code blocks of a Markdown (or MDX) file, by the rule in
|
|
342
|
+
* `lib/markdown-fences.mjs`. A fence that is never closed runs to the end of
|
|
343
|
+
* the file, as CommonMark has it, so its code is checked rather than dropped
|
|
344
|
+
* (#980). The opening fence's indentation is removed from each line (fences
|
|
345
|
+
* nested in lists and MDX components are indented).
|
|
346
|
+
*
|
|
347
|
+
* @returns {{ lang: string, info: string[], line: number, code: string }[]}
|
|
348
|
+
* `line` is the 1-based line of the fence's first line of code.
|
|
349
|
+
*/
|
|
350
|
+
function extractFences(text) {
|
|
351
|
+
const lines = text.split("\n")
|
|
352
|
+
const fences = []
|
|
353
|
+
let open = null
|
|
354
|
+
for (let i = 0; i < lines.length; i++) {
|
|
355
|
+
const line = lines[i]
|
|
356
|
+
if (open === null) {
|
|
357
|
+
const opening = fenceOpening(line)
|
|
358
|
+
if (opening) open = { ...opening, line: i + 2, body: [] }
|
|
359
|
+
continue
|
|
360
|
+
}
|
|
361
|
+
if (closesFence(line, open.marker)) {
|
|
362
|
+
const { lang, info, line: start, body } = open
|
|
363
|
+
fences.push({ lang, info, line: start, code: body.join("\n") })
|
|
364
|
+
open = null
|
|
365
|
+
continue
|
|
366
|
+
}
|
|
367
|
+
const indent = /^\s*/.exec(line)[0].length
|
|
368
|
+
open.body.push(line.slice(Math.min(indent, open.indent)))
|
|
369
|
+
}
|
|
370
|
+
if (open !== null) {
|
|
371
|
+
const { lang, info, line: start, body } = open
|
|
372
|
+
fences.push({ lang, info, line: start, code: body.join("\n") })
|
|
373
|
+
}
|
|
374
|
+
return fences
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function collectSnippets(files, docs) {
|
|
378
|
+
const snippets = []
|
|
379
|
+
for (const file of files) {
|
|
380
|
+
const text = readFileSync(join(rootDir, file), "utf8")
|
|
381
|
+
for (const fence of extractFences(text)) {
|
|
382
|
+
if (!(fence.lang in LANGUAGES)) continue
|
|
383
|
+
const firstLine = fence.code.split("\n").find((l) => l.trim() !== "")
|
|
384
|
+
const skipped =
|
|
385
|
+
fence.info.includes(SKIP_INFO) ||
|
|
386
|
+
(firstLine !== undefined && SKIP_COMMENT.test(firstLine))
|
|
387
|
+
const defines =
|
|
388
|
+
firstLine === undefined ? undefined : MODULE_HEADER.exec(firstLine)
|
|
389
|
+
snippets.push({
|
|
390
|
+
file,
|
|
391
|
+
line: fence.line,
|
|
392
|
+
lang: fence.lang,
|
|
393
|
+
code: fence.code,
|
|
394
|
+
context: CONTEXTS[file] ?? docsContext(file) ?? DEFAULT_CONTEXT,
|
|
395
|
+
group: moduleGroup(file),
|
|
396
|
+
defines: defines ? defines[1].split("/") : undefined,
|
|
397
|
+
selfContained: isConsumerGuide(file) || isBuildGuide(file),
|
|
398
|
+
skipped,
|
|
399
|
+
docs,
|
|
400
|
+
})
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
return snippets
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
// ── The app a snippet assumes ────────────────────────────────────────────────
|
|
407
|
+
|
|
408
|
+
/** Whether a statement carries the `export` modifier. */
|
|
409
|
+
const isExported = (node) =>
|
|
410
|
+
ts.getModifiers?.(node)?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword)
|
|
411
|
+
|
|
412
|
+
/** The names a binding (`a`, `{ a, b: c }`, `[d, ...e]`) declares. */
|
|
413
|
+
function bindingNames(name, out = []) {
|
|
414
|
+
if (ts.isIdentifier(name)) out.push(name.text)
|
|
415
|
+
else
|
|
416
|
+
for (const element of name.elements) {
|
|
417
|
+
if (!ts.isOmittedExpression(element)) bindingNames(element.name, out)
|
|
418
|
+
}
|
|
419
|
+
return out
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function parse(fileName, text) {
|
|
423
|
+
return ts.createSourceFile(fileName, text, ts.ScriptTarget.Latest, true)
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* The names a context's `globals.ts` exports, each marked as a type or a
|
|
428
|
+
* value, following its `export * from "..."` statements.
|
|
429
|
+
*
|
|
430
|
+
* @returns {Map<string, "type" | "value">}
|
|
431
|
+
*/
|
|
432
|
+
function globalNames(file, seen = new Set()) {
|
|
433
|
+
const names = new Map()
|
|
434
|
+
if (seen.has(file)) return names
|
|
435
|
+
seen.add(file)
|
|
436
|
+
for (const statement of parse(file, readFileSync(file, "utf8")).statements) {
|
|
437
|
+
if (
|
|
438
|
+
ts.isExportDeclaration(statement) &&
|
|
439
|
+
!statement.exportClause &&
|
|
440
|
+
statement.moduleSpecifier
|
|
441
|
+
) {
|
|
442
|
+
const target = join(dirname(file), statement.moduleSpecifier.text)
|
|
443
|
+
for (const [name, kind] of globalNames(`${target}.ts`, seen)) {
|
|
444
|
+
if (!names.has(name)) names.set(name, kind)
|
|
445
|
+
}
|
|
446
|
+
} else if (ts.isExportDeclaration(statement) && statement.exportClause) {
|
|
447
|
+
for (const element of statement.exportClause.elements) {
|
|
448
|
+
const isType = statement.isTypeOnly || element.isTypeOnly
|
|
449
|
+
names.set(element.name.text, isType ? "type" : "value")
|
|
450
|
+
}
|
|
451
|
+
} else if (!isExported(statement)) {
|
|
452
|
+
continue
|
|
453
|
+
} else if (ts.isVariableStatement(statement)) {
|
|
454
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
455
|
+
for (const name of bindingNames(declaration.name)) {
|
|
456
|
+
names.set(name, "value")
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
} else if (
|
|
460
|
+
ts.isInterfaceDeclaration(statement) ||
|
|
461
|
+
ts.isTypeAliasDeclaration(statement)
|
|
462
|
+
) {
|
|
463
|
+
names.set(statement.name.text, "type")
|
|
464
|
+
} else if (statement.name && ts.isIdentifier(statement.name)) {
|
|
465
|
+
names.set(statement.name.text, "value")
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
return names
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/** The names a snippet declares or imports at its top level. */
|
|
472
|
+
function declaredNames(sourceFile) {
|
|
473
|
+
const names = new Set()
|
|
474
|
+
for (const statement of sourceFile.statements) {
|
|
475
|
+
if (ts.isImportDeclaration(statement)) {
|
|
476
|
+
const clause = statement.importClause
|
|
477
|
+
if (!clause) continue
|
|
478
|
+
if (clause.name) names.add(clause.name.text)
|
|
479
|
+
const bindings = clause.namedBindings
|
|
480
|
+
if (bindings && ts.isNamespaceImport(bindings)) {
|
|
481
|
+
names.add(bindings.name.text)
|
|
482
|
+
} else if (bindings) {
|
|
483
|
+
for (const element of bindings.elements) names.add(element.name.text)
|
|
484
|
+
}
|
|
485
|
+
} else if (ts.isVariableStatement(statement)) {
|
|
486
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
487
|
+
for (const name of bindingNames(declaration.name)) names.add(name)
|
|
488
|
+
}
|
|
489
|
+
} else if (statement.name && ts.isIdentifier(statement.name)) {
|
|
490
|
+
names.add(statement.name.text)
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
return names
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* The `@/` alias of Next.js and Vite apps: `@/canisters/ledger/reactor` is
|
|
498
|
+
* `src/canisters/ledger/reactor`, whichever module imports it.
|
|
499
|
+
*/
|
|
500
|
+
const isAppAlias = (specifier) => specifier.startsWith("@/")
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* The path segments of an import that name the module: a relative import's
|
|
504
|
+
* without its `.` and `..`, an `@/` import's without the alias, and either
|
|
505
|
+
* without a `.ts` or `.tsx` extension, which a script Node runs as
|
|
506
|
+
* TypeScript writes (`./generated/icrc1.ts`).
|
|
507
|
+
*/
|
|
508
|
+
function moduleSegments(specifier) {
|
|
509
|
+
return (isAppAlias(specifier) ? specifier.slice(1) : specifier)
|
|
510
|
+
.replace(/\.tsx?$/, "")
|
|
511
|
+
.split("/")
|
|
512
|
+
.filter((segment) => segment !== "" && segment !== "." && segment !== "..")
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* The module a relative or `@/` import names in `context`: the longest
|
|
517
|
+
* trailing part of its path that is a file in the context directory, then in
|
|
518
|
+
* `app/`. An `@/` import is anchored at the alias's root (`src/`), so only its
|
|
519
|
+
* whole path is tried: a trailing part of `@/canisters/ledger/reactor` such as
|
|
520
|
+
* `reactor` would name some other module.
|
|
521
|
+
*/
|
|
522
|
+
function findFixture(project, context, specifier) {
|
|
523
|
+
const segments = moduleSegments(specifier)
|
|
524
|
+
const contexts = [...new Set([context, DEFAULT_CONTEXT])]
|
|
525
|
+
const last = isAppAlias(specifier) ? 1 : segments.length
|
|
526
|
+
for (let start = 0; start < last; start++) {
|
|
527
|
+
const path = segments.slice(start).join("/")
|
|
528
|
+
for (const name of contexts) {
|
|
529
|
+
for (const candidate of [
|
|
530
|
+
`${path}.ts`,
|
|
531
|
+
`${path}.tsx`,
|
|
532
|
+
`${path}/index.ts`,
|
|
533
|
+
]) {
|
|
534
|
+
const file = join(project, "fixtures", name, candidate)
|
|
535
|
+
if (existsSync(file)) return file
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
return undefined
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* The snippet module a relative import of a consumer guide or a build guide
|
|
544
|
+
* names when the guide shows that module itself: a snippet of the same
|
|
545
|
+
* document, or of the same skill, whose first line names a path ending in the
|
|
546
|
+
* import's
|
|
547
|
+
* (`// src/reactor.ts` for `./reactor`). The nearest one above the importing
|
|
548
|
+
* snippet wins, then the first below it, then the first in another file of
|
|
549
|
+
* the skill. The import is then checked against the module the reader was
|
|
550
|
+
* shown, and a fixture cannot hide a name that module does not export.
|
|
551
|
+
*
|
|
552
|
+
* The READMEs keep their fixtures: they show a module such as `src/reactor.ts`
|
|
553
|
+
* several times, once per alternative setup, and later sections mean the
|
|
554
|
+
* first.
|
|
555
|
+
*/
|
|
556
|
+
function findPageModule(byModule, snippet, specifier) {
|
|
557
|
+
if (!snippet.selfContained) return undefined
|
|
558
|
+
const segments = moduleSegments(specifier)
|
|
559
|
+
const matches = (other) =>
|
|
560
|
+
other !== snippet &&
|
|
561
|
+
other.group === snippet.group &&
|
|
562
|
+
other.defines !== undefined &&
|
|
563
|
+
other.defines.length >= segments.length &&
|
|
564
|
+
other.defines.slice(-segments.length).join("/") === segments.join("/")
|
|
565
|
+
const candidates = [...byModule].filter(([, other]) => matches(other))
|
|
566
|
+
const rank = ([, other]) =>
|
|
567
|
+
other.file !== snippet.file
|
|
568
|
+
? Number.MAX_SAFE_INTEGER
|
|
569
|
+
: other.line < snippet.line
|
|
570
|
+
? snippet.line - other.line
|
|
571
|
+
: Number.MAX_SAFE_INTEGER / 2 + other.line
|
|
572
|
+
candidates.sort((a, b) => rank(a) - rank(b))
|
|
573
|
+
return candidates[0]?.[0]
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// ── The compilation ──────────────────────────────────────────────────────────
|
|
577
|
+
|
|
578
|
+
function readJson(path) {
|
|
579
|
+
return JSON.parse(readFileSync(path, "utf8"))
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
/** The installed packages under `nodeModules`, scoped ones as `@scope/name`. */
|
|
583
|
+
function installedPackages(nodeModules) {
|
|
584
|
+
if (!existsSync(nodeModules)) return []
|
|
585
|
+
return readdirSync(nodeModules)
|
|
586
|
+
.filter((entry) => !entry.startsWith("."))
|
|
587
|
+
.flatMap((entry) =>
|
|
588
|
+
entry.startsWith("@")
|
|
589
|
+
? readdirSync(join(nodeModules, entry)).map((n) => `${entry}/${n}`)
|
|
590
|
+
: [entry]
|
|
591
|
+
)
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/**
|
|
595
|
+
* Links every workspace package, and every dependency installed for one, into
|
|
596
|
+
* `project/node_modules`. Returns the links, so they can be removed before the
|
|
597
|
+
* directory is: they point into the workspace.
|
|
598
|
+
*/
|
|
599
|
+
function linkPackages(project) {
|
|
600
|
+
// The docs pages import libraries only an example app installs. Without
|
|
601
|
+
// them their imports fail one by one as missing modules, which hides what
|
|
602
|
+
// is wrong. CI installs the workspace without the examples, and checks only
|
|
603
|
+
// the guides, skills and READMEs, which need none of them.
|
|
604
|
+
for (const source of DEPENDENCY_SOURCES) {
|
|
605
|
+
if (
|
|
606
|
+
includeDocs &&
|
|
607
|
+
source.startsWith("examples/") &&
|
|
608
|
+
!existsSync(join(rootDir, source, "node_modules"))
|
|
609
|
+
) {
|
|
610
|
+
throw new Error(
|
|
611
|
+
`${source} is not installed, and the docs pages import libraries it provides. Run \`pnpm install\` without \`--filter '!./examples/**'\`.`
|
|
612
|
+
)
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
const links = []
|
|
616
|
+
const linked = new Set()
|
|
617
|
+
const link = (target, name) => {
|
|
618
|
+
if (linked.has(name)) return
|
|
619
|
+
linked.add(name)
|
|
620
|
+
const path = join(project, "node_modules", name)
|
|
621
|
+
mkdirSync(dirname(path), { recursive: true })
|
|
622
|
+
symlinkSync(target, path, "dir")
|
|
623
|
+
links.push(path)
|
|
624
|
+
}
|
|
625
|
+
for (const dir of packageDirs) {
|
|
626
|
+
const packageDir = join(rootDir, "packages", dir)
|
|
627
|
+
const manifest = readJson(join(packageDir, "package.json"))
|
|
628
|
+
if (manifest.private !== true && !existsSync(join(packageDir, "dist"))) {
|
|
629
|
+
throw new Error(
|
|
630
|
+
`${manifest.name} is not built (no dist/). Run \`pnpm build\` first.`
|
|
631
|
+
)
|
|
632
|
+
}
|
|
633
|
+
link(packageDir, manifest.name)
|
|
634
|
+
}
|
|
635
|
+
for (const source of DEPENDENCY_SOURCES) {
|
|
636
|
+
const sourceModules = join(rootDir, source, "node_modules")
|
|
637
|
+
for (const name of installedPackages(sourceModules)) {
|
|
638
|
+
link(join(sourceModules, name), name)
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
return links
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Writes each snippet to `project/snippets/`, preceded by an import of the
|
|
646
|
+
* globals it does not declare, and returns them by absolute module path.
|
|
647
|
+
*/
|
|
648
|
+
function writeSnippets(project, snippets) {
|
|
649
|
+
const globals = new Map()
|
|
650
|
+
const byModule = new Map()
|
|
651
|
+
mkdirSync(join(project, "snippets"), { recursive: true })
|
|
652
|
+
for (const snippet of snippets) {
|
|
653
|
+
const slug = snippet.file.replace(/[^A-Za-z0-9]+/g, "_")
|
|
654
|
+
const modulePath = toPosix(
|
|
655
|
+
join(
|
|
656
|
+
project,
|
|
657
|
+
"snippets",
|
|
658
|
+
`${slug}_L${snippet.line}${LANGUAGES[snippet.lang]}`
|
|
659
|
+
)
|
|
660
|
+
)
|
|
661
|
+
|
|
662
|
+
const contextDir = join(project, "fixtures", snippet.context)
|
|
663
|
+
const globalsFile = existsSync(join(contextDir, "globals.ts"))
|
|
664
|
+
? join(contextDir, "globals.ts")
|
|
665
|
+
: join(project, "fixtures", DEFAULT_CONTEXT, "globals.ts")
|
|
666
|
+
if (!globals.has(globalsFile)) {
|
|
667
|
+
globals.set(globalsFile, globalNames(globalsFile))
|
|
668
|
+
}
|
|
669
|
+
const declared = declaredNames(parse(modulePath, snippet.code))
|
|
670
|
+
const imported = snippet.selfContained
|
|
671
|
+
? []
|
|
672
|
+
: [...globals.get(globalsFile)]
|
|
673
|
+
.filter(([name]) => !declared.has(name))
|
|
674
|
+
.map(([name, kind]) => (kind === "type" ? `type ${name}` : name))
|
|
675
|
+
const specifier = toPosix(
|
|
676
|
+
relative(dirname(modulePath), globalsFile)
|
|
677
|
+
).replace(/\.ts$/, "")
|
|
678
|
+
const prelude =
|
|
679
|
+
imported.length > 0
|
|
680
|
+
? `import { ${imported.join(", ")} } from "${specifier}"\n`
|
|
681
|
+
: ""
|
|
682
|
+
|
|
683
|
+
// A triple-slash directive (`/// <reference lib="es2023.intl" />`) counts
|
|
684
|
+
// only above the first statement, so a snippet that opens with one gets
|
|
685
|
+
// the import after its code instead: an import is hoisted wherever it is.
|
|
686
|
+
const directive = TRIPLE_SLASH.test(snippet.code)
|
|
687
|
+
writeFileSync(
|
|
688
|
+
modulePath,
|
|
689
|
+
directive ? snippet.code + "\n" + prelude : prelude + snippet.code + "\n"
|
|
690
|
+
)
|
|
691
|
+
snippet.offset = prelude === "" || directive ? 0 : 1
|
|
692
|
+
byModule.set(modulePath, snippet)
|
|
693
|
+
}
|
|
694
|
+
return byModule
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
/**
|
|
698
|
+
* Compiles the snippets in one program and returns its diagnostics, each
|
|
699
|
+
* mapped back to the Markdown line it came from.
|
|
700
|
+
*
|
|
701
|
+
* This drives the compiler API rather than `tsc`: `tsc` reports no type errors
|
|
702
|
+
* at all while any file has a syntax error, so one malformed snippet would
|
|
703
|
+
* hide every other failure. Here a snippet with a syntax error reports only
|
|
704
|
+
* those, and every other snippet is still checked.
|
|
705
|
+
*/
|
|
706
|
+
function compile(project, byModule) {
|
|
707
|
+
const { options, errors } = ts.convertCompilerOptionsFromJson(
|
|
708
|
+
COMPILER_OPTIONS,
|
|
709
|
+
project
|
|
710
|
+
)
|
|
711
|
+
if (errors.length > 0) {
|
|
712
|
+
throw new Error(
|
|
713
|
+
ts.flattenDiagnosticMessageText(errors[0].messageText, "\n")
|
|
714
|
+
)
|
|
715
|
+
}
|
|
716
|
+
const host = ts.createCompilerHost(options)
|
|
717
|
+
// `types` and the other lookups a program without a tsconfig makes start
|
|
718
|
+
// from the current directory; make that the project, wherever this runs.
|
|
719
|
+
host.getCurrentDirectory = () => project
|
|
720
|
+
const cache = ts.createModuleResolutionCache(project, (name) => name, options)
|
|
721
|
+
const snippetModule = (fileName) => ({
|
|
722
|
+
resolvedModule: {
|
|
723
|
+
resolvedFileName: fileName,
|
|
724
|
+
extension: fileName.endsWith(".tsx") ? ts.Extension.Tsx : ts.Extension.Ts,
|
|
725
|
+
isExternalLibraryImport: false,
|
|
726
|
+
},
|
|
727
|
+
})
|
|
728
|
+
host.resolveModuleNameLiterals = (
|
|
729
|
+
literals,
|
|
730
|
+
containingFile,
|
|
731
|
+
redirectedReference,
|
|
732
|
+
compilerOptions,
|
|
733
|
+
containingSourceFile
|
|
734
|
+
) =>
|
|
735
|
+
literals.map((literal) => {
|
|
736
|
+
const snippet = byModule.get(containingFile)
|
|
737
|
+
const resolved = ts.resolveModuleName(
|
|
738
|
+
literal.text,
|
|
739
|
+
containingFile,
|
|
740
|
+
compilerOptions,
|
|
741
|
+
host,
|
|
742
|
+
cache,
|
|
743
|
+
redirectedReference,
|
|
744
|
+
ts.getModeForUsageLocation(
|
|
745
|
+
containingSourceFile,
|
|
746
|
+
literal,
|
|
747
|
+
compilerOptions
|
|
748
|
+
)
|
|
749
|
+
)
|
|
750
|
+
if (
|
|
751
|
+
resolved.resolvedModule ||
|
|
752
|
+
!snippet ||
|
|
753
|
+
!(literal.text.startsWith(".") || isAppAlias(literal.text))
|
|
754
|
+
) {
|
|
755
|
+
return resolved
|
|
756
|
+
}
|
|
757
|
+
const shown = findPageModule(byModule, snippet, literal.text)
|
|
758
|
+
if (shown) return snippetModule(shown)
|
|
759
|
+
const fixture = findFixture(project, snippet.context, literal.text)
|
|
760
|
+
return fixture ? snippetModule(fixture) : resolved
|
|
761
|
+
})
|
|
762
|
+
|
|
763
|
+
const program = ts.createProgram({
|
|
764
|
+
rootNames: [join(project, "fixtures", "ambient.d.ts"), ...byModule.keys()],
|
|
765
|
+
options,
|
|
766
|
+
host,
|
|
767
|
+
})
|
|
768
|
+
|
|
769
|
+
const found = [
|
|
770
|
+
...program.getOptionsDiagnostics(),
|
|
771
|
+
...program.getGlobalDiagnostics(),
|
|
772
|
+
]
|
|
773
|
+
for (const sourceFile of program.getSourceFiles()) {
|
|
774
|
+
if (!sourceFile.fileName.startsWith(toPosix(project) + "/")) continue
|
|
775
|
+
if (sourceFile.fileName.includes("/node_modules/")) continue
|
|
776
|
+
const syntactic = program.getSyntacticDiagnostics(sourceFile)
|
|
777
|
+
found.push(
|
|
778
|
+
...(syntactic.length > 0
|
|
779
|
+
? syntactic
|
|
780
|
+
: program.getSemanticDiagnostics(sourceFile))
|
|
781
|
+
)
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
return found.map((diagnostic) => {
|
|
785
|
+
const message = ts.flattenDiagnosticMessageText(
|
|
786
|
+
diagnostic.messageText,
|
|
787
|
+
"\n "
|
|
788
|
+
)
|
|
789
|
+
const code = `TS${diagnostic.code}`
|
|
790
|
+
if (!diagnostic.file || diagnostic.start === undefined) {
|
|
791
|
+
return { snippet: undefined, where: "", code, message }
|
|
792
|
+
}
|
|
793
|
+
const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(
|
|
794
|
+
diagnostic.start
|
|
795
|
+
)
|
|
796
|
+
const snippet = byModule.get(diagnostic.file.fileName)
|
|
797
|
+
if (!snippet) {
|
|
798
|
+
const file = toPosix(relative(project, diagnostic.file.fileName))
|
|
799
|
+
return {
|
|
800
|
+
snippet: undefined,
|
|
801
|
+
where: `${file.replace(/^fixtures\//, "scripts/check-snippets/")}:${line + 1}:${character + 1}`,
|
|
802
|
+
code,
|
|
803
|
+
message,
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
// An error on the added import line names a global the snippet shadows
|
|
807
|
+
// or misuses; report it on the snippet's first line.
|
|
808
|
+
const row = Math.max(line - snippet.offset, 0)
|
|
809
|
+
return {
|
|
810
|
+
snippet,
|
|
811
|
+
where: `${snippet.file}:${snippet.line + row}:${character + 1}`,
|
|
812
|
+
code,
|
|
813
|
+
message,
|
|
814
|
+
}
|
|
815
|
+
})
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// ── Hooks outside a component ────────────────────────────────────────────────
|
|
819
|
+
|
|
820
|
+
/** The name a function is declared or assigned under, if it has one. */
|
|
821
|
+
function functionName(node) {
|
|
822
|
+
if (node.name && ts.isIdentifier(node.name)) return node.name.text
|
|
823
|
+
const parent = node.parent
|
|
824
|
+
if (
|
|
825
|
+
parent &&
|
|
826
|
+
ts.isVariableDeclaration(parent) &&
|
|
827
|
+
ts.isIdentifier(parent.name)
|
|
828
|
+
)
|
|
829
|
+
return parent.name.text
|
|
830
|
+
if (parent && ts.isPropertyAssignment(parent) && ts.isIdentifier(parent.name))
|
|
831
|
+
return parent.name.text
|
|
832
|
+
return undefined
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
/**
|
|
836
|
+
* What surrounds a hook call: `"ok"` inside a component (a function named with
|
|
837
|
+
* a capital) or a custom hook (`useXxx`), or inside an anonymous callback of
|
|
838
|
+
* some call (`renderHook(() => useX())`, `memo(() => ...)`), which can be
|
|
839
|
+
* either; `"outside"` with no function at all; `"helper"` in a class or in a
|
|
840
|
+
* function named otherwise (`handleClick`, `load`), where React throws.
|
|
841
|
+
*/
|
|
842
|
+
function hookContext(call) {
|
|
843
|
+
let anonymousCallback = false
|
|
844
|
+
for (let node = call.parent; node; node = node.parent) {
|
|
845
|
+
if (ts.isClassLike(node)) return "helper"
|
|
846
|
+
if (!ts.isFunctionLike(node)) continue
|
|
847
|
+
const name = functionName(node)
|
|
848
|
+
if (name !== undefined) {
|
|
849
|
+
return /^[A-Z]/.test(name) || /^use[A-Z]/.test(name) ? "ok" : "helper"
|
|
850
|
+
}
|
|
851
|
+
if (node.parent && ts.isCallExpression(node.parent))
|
|
852
|
+
anonymousCallback = true
|
|
853
|
+
}
|
|
854
|
+
return anonymousCallback ? "ok" : "outside"
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* A call of a hook (`useXxx(...)` or `something.useXxx(...)`) outside a
|
|
859
|
+
* component or a custom hook, reported as a diagnostic of its snippet.
|
|
860
|
+
* TypeScript accepts one, and React throws when it runs.
|
|
861
|
+
*/
|
|
862
|
+
function hookDiagnostics(byModule) {
|
|
863
|
+
const found = []
|
|
864
|
+
for (const snippet of byModule.values()) {
|
|
865
|
+
const extension = LANGUAGES[snippet.lang]
|
|
866
|
+
const source = ts.createSourceFile(
|
|
867
|
+
`snippet${extension}`,
|
|
868
|
+
snippet.code,
|
|
869
|
+
ts.ScriptTarget.Latest,
|
|
870
|
+
true,
|
|
871
|
+
extension === ".tsx" ? ts.ScriptKind.TSX : ts.ScriptKind.TS
|
|
872
|
+
)
|
|
873
|
+
const visit = (node) => {
|
|
874
|
+
if (ts.isCallExpression(node)) {
|
|
875
|
+
const callee = node.expression
|
|
876
|
+
const name = ts.isIdentifier(callee)
|
|
877
|
+
? callee.text
|
|
878
|
+
: ts.isPropertyAccessExpression(callee)
|
|
879
|
+
? callee.name.text
|
|
880
|
+
: ""
|
|
881
|
+
if (/^use[A-Z]/.test(name)) {
|
|
882
|
+
const context = hookContext(node)
|
|
883
|
+
if (context !== "ok") {
|
|
884
|
+
const { line, character } = source.getLineAndCharacterOfPosition(
|
|
885
|
+
node.getStart(source)
|
|
886
|
+
)
|
|
887
|
+
found.push({
|
|
888
|
+
snippet,
|
|
889
|
+
where: `${snippet.file}:${snippet.line + line}:${character + 1}`,
|
|
890
|
+
code: "hooks",
|
|
891
|
+
message: `\`${name}\` is a React hook, called ${context === "outside" ? "outside any function" : "in a helper or a class, which is neither a component (capitalised) nor a custom hook (useXxx)"}. Call it from a component or a custom hook.`,
|
|
892
|
+
})
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
ts.forEachChild(node, visit)
|
|
897
|
+
}
|
|
898
|
+
visit(source)
|
|
899
|
+
}
|
|
900
|
+
return found
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// ── Main ─────────────────────────────────────────────────────────────────────
|
|
904
|
+
|
|
905
|
+
const matches = (file) =>
|
|
906
|
+
filters.length === 0 || filters.some((filter) => file.includes(filter))
|
|
907
|
+
const snippets = [
|
|
908
|
+
...collectSnippets(gatedFiles().filter(matches), false),
|
|
909
|
+
...(includeDocs ? collectSnippets(docsFiles().filter(matches), true) : []),
|
|
910
|
+
]
|
|
911
|
+
|
|
912
|
+
// Its real path: TypeScript names the files it reads by theirs, and a
|
|
913
|
+
// temporary directory can sit behind a symlink (/var -> /private/var on macOS).
|
|
914
|
+
const project = realpathSync(
|
|
915
|
+
mkdtempSync(join(tmpdir(), "ic-reactor-snippets-"))
|
|
916
|
+
)
|
|
917
|
+
let links = []
|
|
918
|
+
let exitCode = 1
|
|
919
|
+
try {
|
|
920
|
+
links = linkPackages(project)
|
|
921
|
+
cpSync(fixturesDir, join(project, "fixtures"), { recursive: true })
|
|
922
|
+
const byModule = writeSnippets(
|
|
923
|
+
project,
|
|
924
|
+
snippets.filter((snippet) => !snippet.skipped)
|
|
925
|
+
)
|
|
926
|
+
const diagnostics = [
|
|
927
|
+
...compile(project, byModule),
|
|
928
|
+
...hookDiagnostics(byModule),
|
|
929
|
+
]
|
|
930
|
+
|
|
931
|
+
const failed = new Set(diagnostics.map((d) => d.snippet).filter(Boolean))
|
|
932
|
+
const fixtureErrors = diagnostics.filter((d) => !d.snippet)
|
|
933
|
+
|
|
934
|
+
if (verbose) {
|
|
935
|
+
for (const snippet of snippets) {
|
|
936
|
+
const state = snippet.skipped
|
|
937
|
+
? "skip"
|
|
938
|
+
: failed.has(snippet)
|
|
939
|
+
? "FAIL"
|
|
940
|
+
: "ok "
|
|
941
|
+
// The line of the opening fence
|
|
942
|
+
console.log(`${state} ${snippet.file}:${snippet.line - 1}`)
|
|
943
|
+
}
|
|
944
|
+
console.log("")
|
|
945
|
+
}
|
|
946
|
+
for (const { snippet, where, code, message } of diagnostics) {
|
|
947
|
+
console.log(`${where ? `${where} ` : ""}${code}: ${message}`)
|
|
948
|
+
}
|
|
949
|
+
if (diagnostics.length > 0) console.log("")
|
|
950
|
+
|
|
951
|
+
const report = (label, list) => {
|
|
952
|
+
const failures = list.filter((s) => failed.has(s)).length
|
|
953
|
+
const skipped = list.filter((s) => s.skipped).length
|
|
954
|
+
console.log(
|
|
955
|
+
`${label}: ${list.length - skipped} checked (${list.length - skipped - failures} passed, ${failures} failed), ${skipped} skipped`
|
|
956
|
+
)
|
|
957
|
+
return failures
|
|
958
|
+
}
|
|
959
|
+
let gatedFailures = report(
|
|
960
|
+
"Guides, skills and READMEs",
|
|
961
|
+
snippets.filter((s) => !s.docs)
|
|
962
|
+
)
|
|
963
|
+
if (includeDocs) {
|
|
964
|
+
gatedFailures += report(
|
|
965
|
+
"Docs pages",
|
|
966
|
+
snippets.filter((s) => s.docs)
|
|
967
|
+
)
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
if (fixtureErrors.length > 0) {
|
|
971
|
+
console.error(
|
|
972
|
+
"✖ The snippets' app in scripts/check-snippets/ does not compile."
|
|
973
|
+
)
|
|
974
|
+
} else if (gatedFailures > 0) {
|
|
975
|
+
console.error(
|
|
976
|
+
"✖ Fix each failing snippet in its file. Only a name the snippet's app would define belongs in scripts/check-snippets/ (a docs page's in scripts/check-snippets/docs/<section>/); a deliberate fragment is marked ```ts nocheck."
|
|
977
|
+
)
|
|
978
|
+
} else {
|
|
979
|
+
exitCode = 0
|
|
980
|
+
console.log("✔ Every checked snippet compiles")
|
|
981
|
+
}
|
|
982
|
+
} catch (error) {
|
|
983
|
+
console.error(`✖ ${error.stack ?? error.message}`)
|
|
984
|
+
} finally {
|
|
985
|
+
if (keep) {
|
|
986
|
+
console.log(` project kept at ${project}`)
|
|
987
|
+
} else {
|
|
988
|
+
for (const link of links) unlinkSync(link)
|
|
989
|
+
rmSync(project, { recursive: true, force: true })
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
// Not process.exit(): it can cut off output still being written to a pipe.
|
|
993
|
+
process.exitCode = exitCode
|