diff --git a/.agents/skills/add-column-type/SKILL.md b/.agents/skills/add-column-type/SKILL.md index 05f9816aa5f..e9a11707e43 100644 --- a/.agents/skills/add-column-type/SKILL.md +++ b/.agents/skills/add-column-type/SKILL.md @@ -15,7 +15,7 @@ This was not always true: adding `currency` originally took ~40 edits across 32 Do **not** hunt for places to edit. Add your type to the `ColumnType` union first and let `tsc` produce the list: ```bash -cd apps/sim && bunx tsc --noEmit -p tsconfig.json +cd apps/sim && bun run type-check ``` You will get two errors, naming `column-types/registry.ts` and `column-types/registry.server.ts`. Register in both. @@ -153,7 +153,7 @@ Registering the *type* is compiler-enforced. Registering its *metadata* is not, ## Final Validation (Required) -1. **`cd apps/sim && bunx tsc --noEmit -p tsconfig.json`** — must be clean. If any file *outside* `column-types/` errors, that file has a hardcoded type list; fix it to read the registry. +1. **`cd apps/sim && bun run type-check`** — must be clean. If any file *outside* `column-types/` errors, that file has a hardcoded type list; fix it to read the registry. 2. **Grep for leaks** — `grep -rnE "(===|!==) '{id}'|case '{id}':" apps/sim --include='*.ts' --include='*.tsx' | grep -v column-types/`. (All three forms: a plain `!==` and a `case` are how half of `currency`'s real branches are written.) Hits are expected; judge each. A hit is fine when it mounts a specific React component or encodes a genuinely one-off behavior (`json`'s mono textarea, `date`'s timezone-aware parsing). A hit is a **leak** when it restates something the registry could answer — an icon, a label, a colour, an operator set, a cast, a coercion. Leaks get a registry field, not a new branch. 3. **Run the suite** — `bunx vitest run lib/table 'app/workspace/[workspaceId]/tables' lib/api app/api/table app/api/v1 lib/copilot/tools/server/table`. Existing tests must pass **unchanged**; needing to edit one means you changed behavior for the other types. 4. **`bun run lint:check`, `bun run check:api-validation`, `bun run check:client-boundary`** from the repo root. diff --git a/.agents/skills/add-enrichment/SKILL.md b/.agents/skills/add-enrichment/SKILL.md index 44c3e9f95da..8ea6117db57 100644 --- a/.agents/skills/add-enrichment/SKILL.md +++ b/.agents/skills/add-enrichment/SKILL.md @@ -128,7 +128,7 @@ export const ENRICHMENT_REGISTRY: EnrichmentRegistry = { ## Step 5: Verify -1. `bunx tsc --noEmit` (from `apps/sim`, `NODE_OPTIONS=--max-old-space-size=8192`) and `bunx biome check` on the changed files. +1. `bun run type-check` (from `apps/sim`) and `bunx biome check` on the changed files. 2. In a table → **+ New column → Enrichments** → pick the new enrichment, map its inputs to columns, name the output column(s), Save. Confirm it appears in the catalog with its icon/description. 3. With hosted keys (or a workspace BYOK key) configured for each provider's service, run a row and confirm the cell fills; the dev-server log shows `Enrichment hit { provider }`. A row whose providers all miss completes blank; a row where every provider errored shows an error cell. diff --git a/.claude/commands/add-column-type.md b/.claude/commands/add-column-type.md index b390ccc0b98..7b362016218 100644 --- a/.claude/commands/add-column-type.md +++ b/.claude/commands/add-column-type.md @@ -14,7 +14,7 @@ This was not always true: adding `currency` originally took ~40 edits across 32 Do **not** hunt for places to edit. Add your type to the `ColumnType` union first and let `tsc` produce the list: ```bash -cd apps/sim && bunx tsc --noEmit -p tsconfig.json +cd apps/sim && bun run type-check ``` You will get two errors, naming `column-types/registry.ts` and `column-types/registry.server.ts`. Register in both. @@ -152,7 +152,7 @@ Registering the *type* is compiler-enforced. Registering its *metadata* is not, ## Final Validation (Required) -1. **`cd apps/sim && bunx tsc --noEmit -p tsconfig.json`** — must be clean. If any file *outside* `column-types/` errors, that file has a hardcoded type list; fix it to read the registry. +1. **`cd apps/sim && bun run type-check`** — must be clean. If any file *outside* `column-types/` errors, that file has a hardcoded type list; fix it to read the registry. 2. **Grep for leaks** — `grep -rnE "(===|!==) '{id}'|case '{id}':" apps/sim --include='*.ts' --include='*.tsx' | grep -v column-types/`. (All three forms: a plain `!==` and a `case` are how half of `currency`'s real branches are written.) Hits are expected; judge each. A hit is fine when it mounts a specific React component or encodes a genuinely one-off behavior (`json`'s mono textarea, `date`'s timezone-aware parsing). A hit is a **leak** when it restates something the registry could answer — an icon, a label, a colour, an operator set, a cast, a coercion. Leaks get a registry field, not a new branch. 3. **Run the suite** — `bunx vitest run lib/table 'app/workspace/[workspaceId]/tables' lib/api app/api/table app/api/v1 lib/copilot/tools/server/table`. Existing tests must pass **unchanged**; needing to edit one means you changed behavior for the other types. 4. **`bun run lint:check`, `bun run check:api-validation`, `bun run check:client-boundary`** from the repo root. diff --git a/.claude/commands/add-enrichment.md b/.claude/commands/add-enrichment.md index c23d001f70a..b8da9bb3bb4 100644 --- a/.claude/commands/add-enrichment.md +++ b/.claude/commands/add-enrichment.md @@ -127,7 +127,7 @@ export const ENRICHMENT_REGISTRY: EnrichmentRegistry = { ## Step 5: Verify -1. `bunx tsc --noEmit` (from `apps/sim`, `NODE_OPTIONS=--max-old-space-size=8192`) and `bunx biome check` on the changed files. +1. `bun run type-check` (from `apps/sim`) and `bunx biome check` on the changed files. 2. In a table → **+ New column → Enrichments** → pick the new enrichment, map its inputs to columns, name the output column(s), Save. Confirm it appears in the catalog with its icon/description. 3. With hosted keys (or a workspace BYOK key) configured for each provider's service, run a row and confirm the cell fills; the dev-server log shows `Enrichment hit { provider }`. A row whose providers all miss completes blank; a row where every provider errored shows an error cell. diff --git a/.claude/rules/global.md b/.claude/rules/global.md index 4d47c82f60a..86b2ee3be27 100644 --- a/.claude/rules/global.md +++ b/.claude/rules/global.md @@ -65,3 +65,10 @@ const filtered = filterUndefined(obj) ## Package Manager Use `bun` and `bunx`, not `npm` and `npx`. + +## Type-checking +`tsc` must resolve to the native (Go) TypeScript 7 compiler. Do not remove the `@typescript/native` alias from the root `devDependencies` — nothing imports it, and deleting it looks harmless. + +`apps/sim` needs `@typescript/typescript6` for its runtime TypeScript API, and that package depends on `@typescript/old` — an alias of `typescript@6` — which declares its own `tsc` bin. Package managers pick bin winners by lexical sort rather than dependency depth, so `@typescript/old` beats `typescript` and `node_modules/.bin/tsc` silently becomes the JavaScript TypeScript 6 compiler: identical diagnostics, ~10x slower (83s vs 8s on `apps/sim`). The `@typescript/native` alias exists only to sort ahead of `@typescript/old`. + +`bun run check:native-typecheck` fails the build if a bare `tsc` stops reporting 7.x — which is also what a newly added package that sorts ahead of `@typescript/native` and ships a `tsc` bin would look like. See [microsoft/typescript-go#4567](https://github.com/microsoft/typescript-go/issues/4567). diff --git a/.claude/skills/add-settings-page/SKILL.md b/.claude/skills/add-settings-page/SKILL.md index 5e84bbbc341..f3440cda8fd 100644 --- a/.claude/skills/add-settings-page/SKILL.md +++ b/.claude/skills/add-settings-page/SKILL.md @@ -37,7 +37,7 @@ Key paths: hand-roll a Save button, a `beforeunload`, or an "Unsaved changes" modal — they're centralized. See the "Save / Discard + unsaved-changes guard" section in `.claude/rules/sim-settings-pages.md`. -5. **Verify:** `cd apps/sim && bunx tsc --noEmit`; `bunx biome check --write `. +5. **Verify:** `cd apps/sim && bun run type-check`; `bunx biome check --write `. ## Mode B — Audit existing settings pages @@ -76,7 +76,7 @@ For each page component, confirm the checklist in `.claude/rules/sim-settings-pa unless they're also being changed for an unrelated, deliberate reason. 7. Remove now-unused imports (`ChipInput`/`Search`) ONLY after grepping that they are not still used elsewhere in the file (e.g. by a detail view). -8. **Verify the whole sweep:** `tsc --noEmit`, `biome check` on every touched +8. **Verify the whole sweep:** `bun run type-check`, `biome check` on every touched file, and run the affected pages' tests. Diff each file against the base and confirm the change is purely structural before shipping. @@ -105,5 +105,5 @@ contract. Then, per page: 7. Check what the old row rendered *beside* the title (a badge, a timestamp, a transport label). The row's title truncates as one unit, so anything folded into it can be ellipsised away — move it to `description` or `badge`. -8. Verify: `tsc --noEmit`, `biome check`, the page's tests, and a diff read of +8. Verify: `bun run type-check`, `biome check`, the page's tests, and a diff read of every converted block for lost props, conditions, and `key` placement. diff --git a/.cursor/commands/add-column-type.md b/.cursor/commands/add-column-type.md index f0be823ab6e..00f00215348 100644 --- a/.cursor/commands/add-column-type.md +++ b/.cursor/commands/add-column-type.md @@ -9,7 +9,7 @@ This was not always true: adding `currency` originally took ~40 edits across 32 Do **not** hunt for places to edit. Add your type to the `ColumnType` union first and let `tsc` produce the list: ```bash -cd apps/sim && bunx tsc --noEmit -p tsconfig.json +cd apps/sim && bun run type-check ``` You will get two errors, naming `column-types/registry.ts` and `column-types/registry.server.ts`. Register in both. @@ -147,7 +147,7 @@ Registering the *type* is compiler-enforced. Registering its *metadata* is not, ## Final Validation (Required) -1. **`cd apps/sim && bunx tsc --noEmit -p tsconfig.json`** — must be clean. If any file *outside* `column-types/` errors, that file has a hardcoded type list; fix it to read the registry. +1. **`cd apps/sim && bun run type-check`** — must be clean. If any file *outside* `column-types/` errors, that file has a hardcoded type list; fix it to read the registry. 2. **Grep for leaks** — `grep -rnE "(===|!==) '{id}'|case '{id}':" apps/sim --include='*.ts' --include='*.tsx' | grep -v column-types/`. (All three forms: a plain `!==` and a `case` are how half of `currency`'s real branches are written.) Hits are expected; judge each. A hit is fine when it mounts a specific React component or encodes a genuinely one-off behavior (`json`'s mono textarea, `date`'s timezone-aware parsing). A hit is a **leak** when it restates something the registry could answer — an icon, a label, a colour, an operator set, a cast, a coercion. Leaks get a registry field, not a new branch. 3. **Run the suite** — `bunx vitest run lib/table 'app/workspace/[workspaceId]/tables' lib/api app/api/table app/api/v1 lib/copilot/tools/server/table`. Existing tests must pass **unchanged**; needing to edit one means you changed behavior for the other types. 4. **`bun run lint:check`, `bun run check:api-validation`, `bun run check:client-boundary`** from the repo root. diff --git a/.cursor/commands/add-enrichment.md b/.cursor/commands/add-enrichment.md index 0644cce420f..93bfe6d9195 100644 --- a/.cursor/commands/add-enrichment.md +++ b/.cursor/commands/add-enrichment.md @@ -122,7 +122,7 @@ export const ENRICHMENT_REGISTRY: EnrichmentRegistry = { ## Step 5: Verify -1. `bunx tsc --noEmit` (from `apps/sim`, `NODE_OPTIONS=--max-old-space-size=8192`) and `bunx biome check` on the changed files. +1. `bun run type-check` (from `apps/sim`) and `bunx biome check` on the changed files. 2. In a table → **+ New column → Enrichments** → pick the new enrichment, map its inputs to columns, name the output column(s), Save. Confirm it appears in the catalog with its icon/description. 3. With hosted keys (or a workspace BYOK key) configured for each provider's service, run a row and confirm the cell fills; the dev-server log shows `Enrichment hit { provider }`. A row whose providers all miss completes blank; a row where every provider errored shows an error cell. diff --git a/.github/workflows/test-build.yml b/.github/workflows/test-build.yml index 57ef3fd77b1..04d8f566e54 100644 --- a/.github/workflows/test-build.yml +++ b/.github/workflows/test-build.yml @@ -192,6 +192,12 @@ jobs: fi bun run check:migrations "$BASE_REF" + # Runs before the type-check itself: if a bare `tsc` has fallen back to the + # JavaScript TypeScript 6 compiler that `@typescript/typescript6` drags in + # transitively, the type-check below still passes — it just takes ~10x longer. + - name: Native type-check audit + run: bun run check:native-typecheck + # Every workspace, not just realtime. packages/emcn, packages/utils, # apps/desktop and apps/docs had no type check in CI at all; apps/sim's # source was covered only as a side effect of `next build` in the separate diff --git a/CLAUDE.md b/CLAUDE.md index d2e376dbd9f..1a1e4671737 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -19,6 +19,7 @@ You are a professional software engineer. All code must follow best practices: a - `truncate(str, maxLength, suffix?)` from `@sim/utils/string` — never inline slice + ellipsis - `backoffWithJitter(attempt, retryAfterMs, options?)` / `parseRetryAfter(header)` from `@sim/utils/retry` — shared retry pacing; never reimplement exponential backoff inline - **Package Manager**: Use `bun` and `bunx`, not `npm` and `npx` +- **Type-checking**: Run `bun run type-check` (per workspace) or `bunx turbo run type-check` (all of them). Do not remove the `@typescript/native` alias from the root `devDependencies` — nothing imports it, but it is what makes a bare `tsc` resolve to the native TypeScript 7 compiler instead of the ~10x slower JavaScript TypeScript 6 one that `@typescript/typescript6` pulls in transitively. `bun run check:native-typecheck` enforces this ## Architecture diff --git a/apps/sim/package.json b/apps/sim/package.json index 83fb0170392..0ceeebf055d 100644 --- a/apps/sim/package.json +++ b/apps/sim/package.json @@ -25,7 +25,7 @@ "test:watch": "vitest", "test:coverage": "vitest run --coverage", "email:dev": "email dev --dir components/emails", - "type-check": "NODE_OPTIONS='--max-old-space-size=8192' tsc --noEmit", + "type-check": "tsc --noEmit", "lint": "biome check --write --unsafe .", "lint:check": "biome check .", "format": "biome format --write .", diff --git a/bun.lock b/bun.lock index 0132ebcfa12..bc3fda5a56c 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ "@clack/prompts": "1.7.0", "@octokit/rest": "^21.0.0", "@types/opentype.js": "1.3.10", + "@typescript/native": "npm:typescript@^7.0.2", "@vercel/og": "0.6.8", "chalk": "5.6.2", "glob": "13.0.0", @@ -2191,6 +2192,8 @@ "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], + "@typescript/native": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], + "@typescript/old": ["typescript@6.0.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw=="], "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], diff --git a/package.json b/package.json index 92cf947d955..bb7d1f6c553 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "check:bare-icons": "bun run scripts/check-bare-icons.ts", "check:icon-paths": "bun run scripts/check-icon-paths.ts", "check:migrations": "bun run scripts/check-migrations-safety.ts", + "check:native-typecheck": "bun run scripts/check-native-typecheck.ts", "check:desktop-bridge": "bun run scripts/check-desktop-bridge-contract.ts --check", "check:desktop-ipc": "bun run scripts/check-desktop-ipc-contract.ts", "desktop-bridge-contract:update": "bun run scripts/check-desktop-bridge-contract.ts --update", @@ -106,6 +107,7 @@ "@clack/prompts": "1.7.0", "@octokit/rest": "^21.0.0", "@types/opentype.js": "1.3.10", + "@typescript/native": "npm:typescript@^7.0.2", "@vercel/og": "0.6.8", "chalk": "5.6.2", "glob": "13.0.0", diff --git a/scripts/check-native-typecheck.ts b/scripts/check-native-typecheck.ts new file mode 100644 index 00000000000..e2f6edd6151 --- /dev/null +++ b/scripts/check-native-typecheck.ts @@ -0,0 +1,39 @@ +#!/usr/bin/env bun +/** + * Asserts that a bare `tsc` runs the native (Go) TypeScript 7 compiler. + * + * `apps/sim` needs `@typescript/typescript6` for its runtime TypeScript API, and that + * package depends on `@typescript/old` — an alias of `typescript@6` — which declares its + * own `tsc` bin. Package managers pick bin winners by lexical sort rather than dependency + * depth, so `@typescript/old` beats `typescript` and `node_modules/.bin/tsc` silently + * becomes the JavaScript TypeScript 6 compiler: identical diagnostics, ~10x slower. + * + * The root `@typescript/native` alias exists purely to win that sort. Nothing imports it — + * deleting it costs every `tsc` invocation in the repo an order of magnitude, with no + * visible failure. This audit is what makes that regression loud. + * + * @see https://github.com/microsoft/typescript-go/issues/4567 + */ +import { spawnSync } from 'node:child_process' +import path from 'node:path' + +const ROOT = path.resolve(import.meta.dir, '..') +const BIN_TSC = path.join(ROOT, 'node_modules', '.bin', 'tsc') + +const result = spawnSync(BIN_TSC, ['--version'], { encoding: 'utf8' }) +const reported = result.stdout?.trim() ?? '' + +if (!/^Version 7\./.test(reported)) { + const detail = reported || result.stderr?.trim() || `exit ${result.status}` + console.error('Native type-check audit failed:\n') + console.error(` node_modules/.bin/tsc reports "${detail}", expected TypeScript 7.x (native).`) + console.error( + '\n A bare `tsc` has fallen back to the JavaScript TypeScript 6 compiler that\n' + + ' `@typescript/typescript6` pulls in transitively — same diagnostics, ~10x slower.\n' + + ' Check that the `@typescript/native` alias is still in the root devDependencies,\n' + + ' and that no newly added package sorts ahead of it while declaring a `tsc` bin.' + ) + process.exit(1) +} + +console.log(`Native type-check audit passed (bare \`tsc\` is ${reported}).`)