diff --git a/.agents/skills/write-gatekeeper/SKELETON.md b/.agents/skills/write-gatekeeper/SKELETON.md index 8ed5b88c4..b49332c86 100644 --- a/.agents/skills/write-gatekeeper/SKELETON.md +++ b/.agents/skills/write-gatekeeper/SKELETON.md @@ -605,7 +605,7 @@ script sharing a name. Build this one package with `pnpm exec vp run -F build` reports nothing to run; the workspace-wide `pnpm build` picks it up as usual. `deploy` goes through the task rather than calling -`build-gatekeeper-configurator.mjs` itself, so the codegen command lives in one place and cannot +`build-gatekeeper-configurator.ts` itself, so the codegen command lives in one place and cannot drift from the task that declares its env — `wrangler deploy` stays outside vp, since it has side effects and needs real credentials. diff --git a/.agents/skills/write-gatekeeper/SKILL.md b/.agents/skills/write-gatekeeper/SKILL.md index d12eb9a1a..7416bd12d 100644 --- a/.agents/skills/write-gatekeeper/SKILL.md +++ b/.agents/skills/write-gatekeeper/SKILL.md @@ -118,8 +118,8 @@ If you use this: - UI modules live in `src/configurator/*-ui.tsx`. - `resourceUrl()` returns the selected resource URL. - `src/configurator/*-types.d.ts` describes the iframe-facing `ui` API. -- `scripts/build-gatekeeper-configurator.mjs` generates `src/generated/*.txt`. -- Nothing invokes `build-gatekeeper-configurator.mjs` by hand. `vite.config.ts` re-exports the +- `scripts/build-gatekeeper-configurator.ts` generates `src/generated/*.txt`. +- Nothing invokes `build-gatekeeper-configurator.ts` by hand. `vite.config.ts` re-exports the shared `build` and `build:configurator` Vite+ tasks from `scripts/gatekeeper-configurator-vite-config.ts`; `build` is just `tsc` and depends on `build:configurator`, which carries `VITE_FRONTEND_ERROR_REPORTING` in its fingerprint, and diff --git a/.github/workflows/contribution-policy.yml b/.github/workflows/contribution-policy.yml index 6cc8374f2..f91adf55b 100644 --- a/.github/workflows/contribution-policy.yml +++ b/.github/workflows/contribution-policy.yml @@ -31,7 +31,7 @@ jobs: script: | const { pathToFileURL } = await import("node:url"); const policyUrl = pathToFileURL( - `${process.env.GITHUB_WORKSPACE}/scripts/contribution-policy.js`, + `${process.env.GITHUB_WORKSPACE}/scripts/contribution-policy.ts`, ); const { enforceContributionPolicy } = await import(policyUrl.href); await enforceContributionPolicy({ github, context, core }); diff --git a/.gitignore b/.gitignore index 2a81c9ec9..170008326 100644 --- a/.gitignore +++ b/.gitignore @@ -21,7 +21,7 @@ packages/workshop-backend/src/generated/browser-export-runtime.txt .wrangler/ .env -# Local dev secrets (loaded by run-dev-server.js) +# Local dev secrets (loaded by run-dev-server.ts) .dev.vars* # Site-specific deployment configs. diff --git a/AGENTS.md b/AGENTS.md index 8e71213e6..a222d9efb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,7 +19,7 @@ The project structure is: * This defines the application's RPC interface. * The RPC protocol is Cap'n Web, which has similar semantics to Cloudflare's Worker-to-Worker RPC system, while being able to run in a browser over WebSocket. Read the readme for details. * packages/configurator-ui: Type-only component helpers used by optional gatekeeper resource configurator UI modules. - * Gatekeeper configurator UI modules are compiled by `scripts/build-gatekeeper-configurator.mjs` as part of package builds. + * Gatekeeper configurator UI modules are compiled by `scripts/build-gatekeeper-configurator.ts` as part of package builds. * packages/gatekeeper-*: Gatekeeper workers for external service integrations. * Each gatekeeper runs as a separate Cloudflare Worker. * Gatekeepers handle OAuth flows and provide sandboxed access to external APIs. @@ -41,22 +41,22 @@ Deployment admin settings (the `/admin` panel) follow a few conventions worth kn Release pipeline (`scripts/release/`) — how customer instances get deployed: -* `build-release.mjs` bundles every deployable worker byte-identically (wrangler dry-run with the pinned wrangler), builds the Access-mode frontend asset build, and generates the release manifest — the contract between this repo's CI and the deploy service, produced by `manifest-lib.mjs` from each package's wrangler.jsonc with account-specific values replaced by placeholders (`$ACCOUNT_ID`, `$WORKER_NAME(...)`, `$SECRET(...)`, `$PUBLIC_BASE_URL`, ...). -* `upload-release.mjs` mirrors the release to R2 content-addressed, manifest last; with `--candidate` the manifest lands under `candidates//` (invisible to the deploy service) so e2e can verify it, and `promote-release.mjs` then copies it to `releases//` — publishing is that single all-or-nothing manifest copy. The copy is not isolated against concurrent promotions, so CI serializes promote runs (a GitLab resource group) and the script's newer-release guard skips candidates that a later release has already superseded. -* The manifest is covered by a golden-file test; after an intentional manifest change, regenerate with `UPDATE_GOLDEN=1 node --test scripts/release-manifest.test.js` and review the golden diff. +* `build-release.ts` bundles every deployable worker byte-identically (wrangler dry-run with the pinned wrangler), builds the Access-mode frontend asset build, and generates the release manifest — the contract between this repo's CI and the deploy service, produced by `manifest-lib.ts` from each package's wrangler.jsonc with account-specific values replaced by placeholders (`$ACCOUNT_ID`, `$WORKER_NAME(...)`, `$SECRET(...)`, `$PUBLIC_BASE_URL`, ...). +* `upload-release.ts` mirrors the release to R2 content-addressed, manifest last; with `--candidate` the manifest lands under `candidates//` (invisible to the deploy service) so e2e can verify it, and `promote-release.ts` then copies it to `releases//` — publishing is that single all-or-nothing manifest copy. The copy is not isolated against concurrent promotions, so CI serializes promote runs (a GitLab resource group) and the script's newer-release guard skips candidates that a later release has already superseded. +* The manifest is covered by a golden-file test; after an intentional manifest change, regenerate with `UPDATE_GOLDEN=1 node --test scripts/release/manifest-lib.test.ts` and review the golden diff. * Running the flow by hand (upload and promote need `R2_ENDPOINT`, `R2_BUCKET`, `R2_ACCESS_KEY_ID`, `R2_SECRET_ACCESS_KEY`): - * `node scripts/release/build-release.mjs --out release-out` — build everything into `release-out/` (id defaults to `r-` in CI, `dev-` locally; override with `--release-id `). - * `node scripts/release/upload-release.mjs --release release-out --candidate` — mirror to R2; omit `--candidate` to publish directly (bypasses the gate — CI never does this). - * `node scripts/release/promote-release.mjs --release-id ` — copy the verified candidate's manifest into `releases//`. -* Deploy-wizard configuration: an installable gatekeeper's user-supplied inputs default to OAuth `CLIENT_ID`/`CLIENT_SECRET` secrets; a per-package `deploy-inputs.json` overrides them, and `NO_DEFAULT_CRED_INPUTS` in `manifest-lib.mjs` opts out gatekeepers that take no third-party OAuth app credentials (the wizard blocks Install on unfilled secret inputs, so a spurious default makes a gatekeeper uninstallable). Backend instance-state vars (`ADMINS`, `DEPLOY_URL`, ...) are injected by the deploy service at PUT time, never manifest-templated. + * `node scripts/release/build-release.ts --out release-out` — build everything into `release-out/` (id defaults to `r-` in CI, `dev-` locally; override with `--release-id `). + * `node scripts/release/upload-release.ts --release release-out --candidate` — mirror to R2; omit `--candidate` to publish directly (bypasses the gate — CI never does this). + * `node scripts/release/promote-release.ts --release-id ` — copy the verified candidate's manifest into `releases//`. +* Deploy-wizard configuration: an installable gatekeeper's user-supplied inputs default to OAuth `CLIENT_ID`/`CLIENT_SECRET` secrets; a per-package `deploy-inputs.json` overrides them, and `NO_DEFAULT_CRED_INPUTS` in `manifest-lib.ts` opts out gatekeepers that take no third-party OAuth app credentials (the wizard blocks Install on unfilled secret inputs, so a spurious default makes a gatekeeper uninstallable). Backend instance-state vars (`ADMINS`, `DEPLOY_URL`, ...) are injected by the deploy service at PUT time, never manifest-templated. To test changes: - Run `pnpm build` to type-check, or `vp run -F build` for one package — most packages declare `build` as a task rather than a script, and `pnpm --filter` cannot see a task. It is a type check and codegen pass, not a compile: every package but `typed-storage` is `noEmit`, because nothing imports the others' `dist` — wrangler and vite bundle from source. `typed-storage` emits because its `exports` resolves to `dist/index.js`. A re-run with nothing changed replays from the task cache — see below. -- Run `pnpm test` to run unit tests, though as of this writing most packages don't have tests yet. It runs the root's own `node --test scripts/*.test.js` suite and then the per-package tests. +- Run `pnpm test` to run unit tests, though as of this writing most packages don't have tests yet. It runs the root's own `node --test 'scripts/**/*.test.ts'` suite and then the per-package tests. - The cached per-package test run is a Vite+ `test` task in each package's `vite.config.ts` rather than a `test` script, so its `input` can exclude the scratch paths vitest writes and reads back (`scripts/vitest-task-vite-config.ts`, shared by all of them). Gatekeepers with a configurator UI re-export `withTests` from `gatekeeper-configurator-vite-config.ts` to get both tasks at once; the ones with no test files re-export its default instead, because `vitest run` exits 1 when it finds none. - Two ways to run one package's tests: `pnpm --filter test:run` goes straight to vitest, `vp run -F test` goes through the cache. The cached path replays instantly when the package is untouched, but its fingerprinting and archiving lose to plain vitest on a package you just edited — by more than the whole suite costs on a small one. Use `test:run` while iterating and `pnpm test` to verify. The direct script is `test:run` rather than `test` because a task may not share a name with a script. - `pnpm build` and `pnpm clean` are `vp run -r `, not `pnpm run --recursive `. Vite+ runs the same per-package scripts and tasks, in dependency order, but caches each one against its inputs, so an unchanged package replays its previous output instead of re-running. Commands joined with `&&` — or given as an array in a task — are cached as separate entries, so a package whose codegen is fresh can still re-run its `tsc`. `vp run --last-details` explains every hit and miss, which is the thing to read when a build is slower than expected. Don't reintroduce a root script that calls `pnpm run --recursive`: `vp run -r` selects the root package too, and would run it as a task and rebuild the whole workspace a second time. -- **A cached `vp` run strips the environment.** Each task and script sees only a built-in set (`PATH`, `HOME`, `CI`, `NODE_OPTIONS`, …). Anything else is invisible to the command *and* absent from the fingerprint, so a build that depends on an env var silently ignores it and no warning says so. A var can only be declared on a task — `env`/`untrackedEnv` don't exist on a package.json script — so any build that reads one has to *be* a task. `workshop-frontend`'s `build` declares `env: ['VITE_*']`, which both forwards the flags and folds them into the fingerprint, so a changed value is a reported cache miss rather than a stale bundle replayed. Prefer `env` over `untrackedEnv` for anything that changes the output. `scripts/env-passthrough.test.js` fails on any build-time env read that isn't accounted for. +- **A cached `vp` run strips the environment.** Each task and script sees only a built-in set (`PATH`, `HOME`, `CI`, `NODE_OPTIONS`, …). Anything else is invisible to the command *and* absent from the fingerprint, so a build that depends on an env var silently ignores it and no warning says so. A var can only be declared on a task — `env`/`untrackedEnv` don't exist on a package.json script — so any build that reads one has to *be* a task. `workshop-frontend`'s `build` declares `env: ['VITE_*']`, which both forwards the flags and folds them into the fingerprint, so a changed value is a reported cache miss rather than a stale bundle replayed. Prefer `env` over `untrackedEnv` for anything that changes the output. `scripts/env-passthrough.test.ts` fails on any build-time env read that isn't accounted for. - **Declaring `env` on one task does not help a sibling script that does the same work.** A script duplicating a task's command takes the stripped path and the declaration buys nothing. So when you find a task with `env`, check what actually invokes that command. The configurator gatekeepers get it right by having no `build` script at all — `build` is a task that is only `tsc` and `dependsOn: ['build:configurator']`. `gatekeeper-context` and `gatekeeper-scheduler` take the other legitimate route, nesting `vp run --cache build:app` inside their `build` script, which vp inlines so the declaration still applies. - **`env` fingerprints the value, not what it points at.** `workshop-backend`'s `build` is therefore `cache: false`, not `env: ['FORMAT_BLUEPRINTS_DIR']`: the variable names a blueprint directory outside the workspace, so with the path held fixed, edits inside it are invisible and a stale `format-blueprints.ts` replays. An uncached task runs with the full ambient environment, so it needs no `env` declaration. Same caution for any var naming a path outside the workspace. - `pnpm test` uses `--filter '!cloudflare-os'` rather than `-r` for the mirror-image reason: the root has a `test` script of its own, and `-r` selects the root, so the scripts suite ran twice per `pnpm test`. The filter hardcodes the root package's name — rename the root and the duplicate run comes back silently. @@ -64,7 +64,7 @@ To test changes: - Caching is off for tasks that read a path they also write, which is why `workshop-frontend`'s `build` excludes its own `dist/` from `input` — without that it never cached (the gatekeeper SPA bundles are the same shape; see the `build:app` bullet below). The `test` tasks needed the same for the scratch paths vitest and wrangler write under `node_modules/.vite`, `node_modules/.vite-temp` and `.wrangler`; `scripts/vitest-task-vite-config.ts` covers which and why, and that list is unlikely to be closed — when a test task stops caching, `--last-details` names the path it read and wrote. This is also why no tsconfig sets `incremental`: `tsc` reads its own `.tsbuildinfo` and writes it back, taking the whole type check out of the task cache to save less than the cache does (measured: 2% hits and 21.1s on a clean tree, against 65% and 13.4s without it). Don't add it back without re-measuring `vp run --last-details`. - `pnpm dev-server` builds the gatekeeper UIs before starting `wrangler dev`, through two concurrent `vp run -r --cache` calls — `build:configurator` and `build:app:dev` (`vp run` takes one task each). `vp` selects packages by which ones declare the task or script, so a new gatekeeper needs one of those to be built here at all, the same requirement `pnpm build` has. Watchers spawn only after the pre-flight finishes: every watch mode builds before it watches, so an earlier spawn would put two processes on the same `src/generated` files. Two dev-only details keep startup fast: - The generated dev configs spawn each worker's `build.command` binary directly (`node `, resolved through the package's own `node_modules`) instead of through `pnpm exec`, which costs ~0.33s of process startup per call — paid for every worker, and again on each rebuild, all of it on the startup critical path. Commands that don't resolve are left as written. `wrangler dev` is reached the same way. - - The app watchers are deferred until Wrangler is listening (TCP poll, 60s backstop): `vite build --watch` can't skip its initial build and these are the largest builds in the repo. Hence `spawn` rather than `execFileSync` at the tail of `run-dev-server.js` — but shutdown is still driven by Wrangler's exit, because Ctrl-C reaches the whole process group and exiting out from under Wrangler would orphan its workerd children. + - The app watchers are deferred until Wrangler is listening (TCP poll, 60s backstop): `vite build --watch` can't skip its initial build and these are the largest builds in the repo. Hence `spawn` rather than `execFileSync` at the tail of `run-dev-server.ts` — but shutdown is still driven by Wrangler's exit, because Ctrl-C reaches the whole process group and exiting out from under Wrangler would orphan its workerd children. - `build:app` is a Vite+ task in each gatekeeper's `vite.config.ts` rather than a package.json script so its `input` can be stated explicitly: `{auto: true}` minus `**/dist-app/**`, `**/src/generated/**` and `**/.wrangler/**` at `base: "workspace"`, plus an explicit `output`. Automatic tracking alone never cached it, because the build writes into the same package tracking hashes as its input. Two traps: the exclusions have to be workspace-wide or the gatekeepers invalidate each other, and only directory *contents* can be excluded, not the directories themselves, so `pnpm clean` still costs one cold build. - `build:app:dev` is the same build with `minify: false`, run by the `pnpm dev-server` pre-flight so its `app.txt` matches what the watcher's un-skippable initial build will write — otherwise `emitAppText` rewrites the file and Wrangler restarts the worker mid-startup. It captures only `app.txt`, since `dist-app/` has no reader outside `vite.app.config.ts`. `build` and `deploy` still use `build:app`, so nothing unminified ships, and `build-app.mjs` always sets `GATEKEEPER_APP_UNMINIFIED` explicitly — an inherited value would otherwise make a production build unminified and get it cached that way. @@ -72,14 +72,14 @@ To test changes: - The five packages whose tests run in workerd (`router`, `typed-storage`, `backend-utils`, `workshop-backend`, `gatekeeper-scheduler`) load `test-setup/assert-workerd.ts` as a `setupFiles` entry. It throws unless `navigator.userAgent` is `Cloudflare-Workers`, so a `@cloudflare/vitest-pool-workers` pool that fails to start fails the suite instead of silently falling back to Node — which otherwise looks like a pass in the packages that import no `cloudflare:*` module. Don't remove it to make a suite green. Linting (oxlint, via Vite+): -- `pnpm lint` runs what CI enforces: `lint:check` (oxlint) and `types:check`. Run this before pushing. +- `pnpm lint` runs what CI enforces: `lint:check` (oxlint), `types:scripts` and `types:check`. Run this before pushing. - Individual scripts: * `pnpm lint:check` / `pnpm lint:fix` — `vp lint`, i.e. oxlint driven by Vite+ (rules in the `lint` block of the root `vite.config.ts`; `correctness` + `suspicious` as errors). Vite+ pins the oxlint it runs (1.76.0), so there is no separate `oxlint` dependency to drift from it and no `.oxlintrc.json` beside the config — one toolchain config, one version. Diagnostics are identical to what running that oxlint directly would emit. * `pnpm types:check` — an alias for `pnpm build`. They were separate scripts running the same recursive `tsc` twice; one name is kept for habit and the other because the codegen prerequisites hang off it. `vp lint` is not part of `vp run`, so it has no task cache; it takes about a second regardless. - Unused function parameters and caught errors are not lint-enforced; unused imports and local variables are still errors. - Some rules are kept as warnings (e.g. `no-shadow`) for incremental cleanup; warnings don't block CI. - Type-aware oxlint rules are intentionally not enabled yet. The type-aware engine is tsgo (TypeScript 7), which is now also the workspace `tsc`, and every package type-checks under it (capnweb 0.11's shared `RpcPromise` alias fixed the TS2321/TS2589 instantiation-depth errors that used to block three packages). But nobody has run the type-aware rules themselves: expect a triage pass, and note `no-floating-promises` conflicts with RPC promise pipelining (below), which intentionally leaves promises unawaited. Type safety is still enforced by `tsc` through `pnpm types:check` and `pnpm build`. -- The `typescript` catalog entry is 7.0.2 (tsgo), but TS 7's main export is `./lib/version.cjs` — the compiler API is gone from it — so everything that still needs that API gets its own JS-based compiler. `scripts/build-gatekeeper-configurator.mjs` (and the mcp-shared schema test) import the root `typescript6` alias (`npm:typescript@6.0.3`); `capnweb-validate` (0.2.4+) ships its own capped `typescript` dependency for the `@validateRpc` transform. `typed-storage`, the only package emitting declarations, sets the `"rootDir": "./src"` TS 7 requires (TS5011). +- The `typescript` catalog entry is 7.0.2 (tsgo), but TS 7's main export is `./lib/version.cjs` — the compiler API is gone from it — so everything that still needs that API gets its own JS-based compiler. `scripts/build-gatekeeper-configurator.ts` (and the mcp-shared schema test) import the root `typescript6` alias (`npm:typescript@6.0.3`); `capnweb-validate` (0.2.4+) ships its own capped `typescript` dependency for the `@validateRpc` transform. `typed-storage`, the only package emitting declarations, sets the `"rootDir": "./src"` TS 7 requires (TS5011). - No tsconfig sets `baseUrl`, and none should. Every `paths` entry here is an explicit relative path, which `tsc` resolves against the tsconfig's own directory, so `baseUrl` bought nothing — and TypeScript 7 removed the option outright (TS5102). IMPORTANT: This repository uses pnpm, not npm. Always use pnpm. diff --git a/docs/ai-gateway-billing.md b/docs/ai-gateway-billing.md index 3f0672631..9f2983e95 100644 --- a/docs/ai-gateway-billing.md +++ b/docs/ai-gateway-billing.md @@ -46,7 +46,7 @@ PUBLIC_BASE_URL=https://your-host AUTH_GATEKEEPERS=cloudflare # allow Cloudflare sign-in/connect (plus any others) # The Cloudflare gatekeeper's OAuth app (client id/secret live on the gatekeeper Worker; in dev -# they're seeded from these shell vars by run-dev-server.js): +# they're seeded from these shell vars by run-dev-server.ts): CLOUDFLARE_OAUTH_CLIENT_ID=... CLOUDFLARE_OAUTH_CLIENT_SECRET=... diff --git a/docs/oauth-signin.md b/docs/oauth-signin.md index 1df487817..1e9d602a9 100644 --- a/docs/oauth-signin.md +++ b/docs/oauth-signin.md @@ -68,7 +68,7 @@ OAuth app with its own redirect URI: - GitHub: `${PUBLIC_BASE_URL}/gatekeeper/github/oauth` - Cloudflare: `${PUBLIC_BASE_URL}/gatekeeper/cloudflare/oauth` -In local dev, `run-dev-server.js` seeds each gatekeeper's `CLIENT_ID`/`CLIENT_SECRET` from +In local dev, `run-dev-server.ts` seeds each gatekeeper's `CLIENT_ID`/`CLIENT_SECRET` from `GOOGLE_*` / `GITHUB_*` / `CLOUDFLARE_OAUTH_*` shell vars. ## Storage / bindings diff --git a/docs/public-server.md b/docs/public-server.md index db65d5736..aa2fd033a 100644 --- a/docs/public-server.md +++ b/docs/public-server.md @@ -13,7 +13,7 @@ connect the account's capabilities. There's no single switch — the pieces turn | Configure | Effect | | --- | --- | | `AUTH_GATEKEEPERS=cloudflare,google,github` | Allowlists which connected gatekeepers may be used to sign in. Each shows a "Continue with …" button alongside username/password. | -| Each gatekeeper's OAuth credentials (on the gatekeeper Worker) | Required for that gatekeeper to actually authenticate. In dev, seeded from `GOOGLE_*` / `GITHUB_*` / `CLOUDFLARE_OAUTH_*` shell vars (see `run-dev-server.js`). | +| Each gatekeeper's OAuth credentials (on the gatekeeper Worker) | Required for that gatekeeper to actually authenticate. In dev, seeded from `GOOGLE_*` / `GITHUB_*` / `CLOUDFLARE_OAUTH_*` shell vars (see `run-dev-server.ts`). | | `ENABLE_CLOUDFLARE_LIMITS=true` | Enables the free daily limit + Cloudflare-credits top-up flow. Billing reads a token from the connected Cloudflare gatekeeper. | | `DISABLE_PASSWORD_AUTH=true` | Hides username/password, leaving gatekeeper sign-in only (ignored unless `AUTH_GATEKEEPERS` is non-empty, to avoid lockout). | diff --git a/package.json b/package.json index 448908439..39bf22ca0 100644 --- a/package.json +++ b/package.json @@ -6,21 +6,21 @@ "packageManager": "pnpm@11.17.0+sha512.cca3cea332ad254bb84145f966d19f4879615210346fc92c79a047f23a0d7b3cca3c3792f0076ba1f1831d277efbcf0a9119b31a9a60eca7fb3d6231f331ef72", "scripts": { "build": "vp run -r --cache build", - "run-local": "node scripts/run-local.mjs", - "test": "node --test 'scripts/**/*.test.js' 'scripts/**/*.test.ts' && vp run --filter '!cloudflare-os' --cache test", + "run-local": "node scripts/run-local.ts", + "test": "node --test 'scripts/**/*.test.ts' && vp run --filter '!cloudflare-os' --cache test", "preview:config": "node scripts/preview/preview.ts config", "preview:deploy": "node scripts/preview/preview.ts deploy", "preview:delete": "node scripts/preview/preview.ts delete", "preview:sweep": "node scripts/preview/preview.ts sweep", "dev-client": "cd packages/workshop-frontend && pnpm run dev", - "dev-server": "node run-dev-server.js", + "dev-server": "node scripts/run-dev-server.ts", "clean": "vp run -r clean", "lint:check": "vp lint", "lint:fix": "vp lint --fix", "types:check": "pnpm run build", "types:scripts": "tsc -p scripts/tsconfig.json", "lint": "pnpm run lint:check && pnpm run types:scripts && pnpm run types:check", - "types:generate": "node scripts/generate-worker-types.mjs" + "types:generate": "node scripts/generate-worker-types.ts" }, "devDependencies": { "@types/node": "26.1.0", diff --git a/packages/configurator-ui/src/index.ts b/packages/configurator-ui/src/index.ts index 3baf86cfc..cc0ee6f67 100644 --- a/packages/configurator-ui/src/index.ts +++ b/packages/configurator-ui/src/index.ts @@ -167,7 +167,7 @@ export function Fragment(_props: { children?: unknown }): unknown { // JSX ambient types for configurator UI `.tsx` modules. These globals only apply when something // imports this package, which is intended only for sandboxed configurator UI modules compiled by -// `scripts/build-gatekeeper-configurator.mjs`. Workshop and gatekeeper-server code should NOT +// `scripts/build-gatekeeper-configurator.ts`. Workshop and gatekeeper-server code should NOT // import from this package to avoid clashing with React's `JSX` namespace. declare global { namespace JSX { diff --git a/packages/gatekeeper-cloudflare/README.md b/packages/gatekeeper-cloudflare/README.md index 86298f7d8..c6d7c7969 100644 --- a/packages/gatekeeper-cloudflare/README.md +++ b/packages/gatekeeper-cloudflare/README.md @@ -53,7 +53,7 @@ CLIENT_ID=your-client-id-here CLIENT_SECRET=your-client-secret-here ``` -In local dev, `run-dev-server.js` will also seed these from `CLOUDFLARE_OAUTH_CLIENT_ID` / +In local dev, `run-dev-server.ts` will also seed these from `CLOUDFLARE_OAUTH_CLIENT_ID` / `CLOUDFLARE_OAUTH_CLIENT_SECRET` if you'd rather set them in the root `.dev.vars`. A per-package `.env` takes precedence and keeps the credential with the gatekeeper that uses it. diff --git a/packages/gatekeeper-confluence/README.md b/packages/gatekeeper-confluence/README.md index b9c01fb95..b8f94dd30 100644 --- a/packages/gatekeeper-confluence/README.md +++ b/packages/gatekeeper-confluence/README.md @@ -80,7 +80,7 @@ CONFLUENCE_CLIENT_ID=your-client-id CONFLUENCE_CLIENT_SECRET=your-client-secret ``` -`run-dev-server.js` maps `CONFLUENCE_CLIENT_ID` / `CONFLUENCE_CLIENT_SECRET` into the Worker's +`run-dev-server.ts` maps `CONFLUENCE_CLIENT_ID` / `CONFLUENCE_CLIENT_SECRET` into the Worker's `CLIENT_ID` / `CLIENT_SECRET` vars. For production, set `CLIENT_ID` and `CLIENT_SECRET` as secrets on the deployed Worker and set `BASE_URL` to the public gatekeeper URL. diff --git a/packages/gatekeeper-context/vite.config.ts b/packages/gatekeeper-context/vite.config.ts index 945069752..5fd0cd010 100644 --- a/packages/gatekeeper-context/vite.config.ts +++ b/packages/gatekeeper-context/vite.config.ts @@ -16,7 +16,7 @@ export default defineConfig({ // survive a later disabled-reporting cache hit and could be collected as if they matched the // current bundle. This runs every time, before the cache lookup. 'clean:error-reporting-artifacts': { - command: 'node ../../scripts/clean-error-reporting-artifacts.mjs .', + command: 'node ../../scripts/clean-error-reporting-artifacts.ts .', cache: false, }, // A task rather than a package.json script so `input` can be stated explicitly: automatic diff --git a/packages/gatekeeper-notion/README.md b/packages/gatekeeper-notion/README.md index 6dd447d1d..731190111 100644 --- a/packages/gatekeeper-notion/README.md +++ b/packages/gatekeeper-notion/README.md @@ -7,7 +7,7 @@ databases. Runs as its own Cloudflare Worker and is auto-discovered by the backe ## Auth OAuth 2.0 public connection. Configure a Notion **public integration** and provide its client -credentials to the worker as `CLIENT_ID` / `CLIENT_SECRET`. For local dev, `run-dev-server.js` +credentials to the worker as `CLIENT_ID` / `CLIENT_SECRET`. For local dev, `run-dev-server.ts` maps `NOTION_CLIENT_ID` / `NOTION_CLIENT_SECRET` (e.g. from a root `.dev.vars`) into those vars. The integration's **redirect URI** must match `/oauth`, which in local dev defaults to diff --git a/packages/gatekeeper-scheduler/vite.config.ts b/packages/gatekeeper-scheduler/vite.config.ts index e08e727a1..c2fd2af69 100644 --- a/packages/gatekeeper-scheduler/vite.config.ts +++ b/packages/gatekeeper-scheduler/vite.config.ts @@ -17,7 +17,7 @@ export default defineConfig({ // survive a later disabled-reporting cache hit and could be collected as if they matched the // current bundle. This runs every time, before the cache lookup. "clean:error-reporting-artifacts": { - command: "node ../../scripts/clean-error-reporting-artifacts.mjs .", + command: "node ../../scripts/clean-error-reporting-artifacts.ts .", cache: false, }, // A task rather than a package.json script so `input` can be stated explicitly: automatic diff --git a/packages/gatekeeper-slack/README.md b/packages/gatekeeper-slack/README.md index fcc7ba550..06fe6360b 100644 --- a/packages/gatekeeper-slack/README.md +++ b/packages/gatekeeper-slack/README.md @@ -12,7 +12,7 @@ OAuth 2.0 using a **user token** (`xoxp-…`), requested via `user_scope` (not a agent sees exactly what the connecting user can see — including private channels, DMs, and search. Create a Slack app (https://api.slack.com/apps) and provide its client credentials to the worker -as `CLIENT_ID` / `CLIENT_SECRET`. For local dev, `run-dev-server.js` maps `SLACK_CLIENT_ID` / +as `CLIENT_ID` / `CLIENT_SECRET`. For local dev, `run-dev-server.ts` maps `SLACK_CLIENT_ID` / `SLACK_CLIENT_SECRET` (e.g. from a root `.dev.vars`) into those vars. App configuration: diff --git a/packages/gatekeeper-supabase/README.md b/packages/gatekeeper-supabase/README.md index 5c9e4ad3a..3c727bd91 100644 --- a/packages/gatekeeper-supabase/README.md +++ b/packages/gatekeeper-supabase/README.md @@ -33,7 +33,7 @@ flow. ## Configuration The gatekeeper Worker reads `CLIENT_ID` and `CLIENT_SECRET`. In local development these are seeded -from shell/`.dev.vars` variables by `run-dev-server.js`: +from shell/`.dev.vars` variables by `run-dev-server.ts`: ``` SUPABASE_CLIENT_ID= diff --git a/packages/gatekeeper-zoominfo/README.md b/packages/gatekeeper-zoominfo/README.md index 326a9fa50..143d48058 100644 --- a/packages/gatekeeper-zoominfo/README.md +++ b/packages/gatekeeper-zoominfo/README.md @@ -47,7 +47,7 @@ is **not** offered as a "Continue with…" sign-in method (`getAuthenticatedEmai ## Configuration The gatekeeper Worker reads `CLIENT_ID` and `CLIENT_SECRET`. In local development these are seeded -from the root `.dev.vars` by `run-dev-server.js`: +from the root `.dev.vars` by `run-dev-server.ts`: ``` ZOOMINFO_CLIENT_ID= diff --git a/packages/integration-tests/src/harness.ts b/packages/integration-tests/src/harness.ts index 53a8874ed..199facdeb 100644 --- a/packages/integration-tests/src/harness.ts +++ b/packages/integration-tests/src/harness.ts @@ -26,7 +26,7 @@ export const TEST_GATEKEEPER_WORKER = "gatekeeper-test"; export const TEST_GATEKEEPER_BINDING = "TEST"; export const TEST_VENDOR_ID = TEST_GATEKEEPER_BINDING.toLowerCase(); -/** Username that `vars.ADMINS` grants deployment-admin rights to, mirroring run-dev-server.js. */ +/** Username that `vars.ADMINS` grants deployment-admin rights to, mirroring run-dev-server.ts. */ export const ADMIN_USERNAME = "admin"; // The slice of wrangler.jsonc the harness reads or rewrites. Loose on purpose: everything else a @@ -64,7 +64,7 @@ export type GatekeeperSpec = { // Read a checked-in wrangler.jsonc and make it usable as an *inline* harness config. // // A worker whose `main` is generated (capnweb-validate) needs `build.cwd` pinned to its own directory -// or the output lands in the wrong place -- run-dev-server.js pins it for the same reason. `main` then +// or the output lands in the wrong place -- run-dev-server.ts pins it for the same reason. `main` then // has to be absolute too: an inline config has no file path of its own, so wrangler resolves a // relative `main` against the harness `root` rather than the worker directory. function readWorkerConfig(dir: string): WorkerConfig { @@ -84,7 +84,7 @@ function workshopConfig( patch?: (config: WorkerConfig) => void): WorkerConfig { const config = readWorkerConfig(WORKSHOP_DIR); - // The checked-in config declares no services; run-dev-server.js adds one per gatekeeper. We add + // The checked-in config declares no services; run-dev-server.ts adds one per gatekeeper. We add // only the ones the suite asked for, so buildGatekeeperVendorMap() discovers exactly those vendors // and the observer-config prompt has no surprise rows. config.services = gatekeepers.map(gk => ({ diff --git a/packages/workshop-backend/wrangler.jsonc b/packages/workshop-backend/wrangler.jsonc index 679689131..1a4cbd8a7 100644 --- a/packages/workshop-backend/wrangler.jsonc +++ b/packages/workshop-backend/wrangler.jsonc @@ -39,7 +39,7 @@ }, // Gatekeeper service bindings and the Workers AI binding are dynamically - // added by run-dev-server.js (for dev) and generate-wrangler-prod.js (for + // added by run-dev-server.ts (for dev) and generate-wrangler-prod.js (for // production). "migrations": [ diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 07620b8cf..aa1de4595 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -9,7 +9,7 @@ catalog: # Exact: pinned in lockstep with capnweb (declared as a >=0.7.0 peer) capnweb-validate: 0.2.4 # Exact: TypeScript 7 (tsgo). Type-checking runs on the native compiler. Build-time - # transpilers that need the JS compiler API (scripts/build-gatekeeper-configurator.mjs) + # transpilers that need the JS compiler API (scripts/build-gatekeeper-configurator.ts) # use the root "typescript6" npm alias instead; capnweb-validate ships its own # JS-based compiler dependency since 0.2.4. typescript: 7.0.2 diff --git a/scripts/build-gatekeeper-configurator.test.js b/scripts/build-gatekeeper-configurator.test.ts similarity index 88% rename from scripts/build-gatekeeper-configurator.test.js rename to scripts/build-gatekeeper-configurator.test.ts index 67b819484..b42ac6934 100644 --- a/scripts/build-gatekeeper-configurator.test.js +++ b/scripts/build-gatekeeper-configurator.test.ts @@ -5,21 +5,25 @@ import { tmpdir } from "node:os"; import { basename, join, resolve } from "node:path"; import { promisify } from "node:util"; import { after, before, describe, it } from "node:test"; -import ts from "typescript6"; // JS compiler API (decodeMappings); see build-gatekeeper-configurator.mjs +import ts from "typescript6"; // JS compiler API (decodeMappings); see build-gatekeeper-configurator.ts const execFileAsync = promisify(execFile); -const builder = resolve("scripts/build-gatekeeper-configurator.mjs"); +const builder = resolve("scripts/build-gatekeeper-configurator.ts"); const configuratorSource = 'import { h } from "@gadgets/configurator-ui";\n' + 'export default { render() { throw new Error("mapped configurator failure"); return
; } };\n'; -let fixtureDir; -let disabledFixtureDir; -let devModeFixtureDir; -let devEnvWithoutDevFlagFixtureDir; +let fixtureDir: string; +let disabledFixtureDir: string; +let devModeFixtureDir: string; +let devEnvWithoutDevFlagFixtureDir: string; // `envFile` is the `.env.*` file that enables reporting, so which one is written decides which build // mode picks it up. `staleArtifacts` pre-seeds the outputs a reporting-disabled build must remove. -async function createFixture(prefix, { envFile, builderArgs = [], staleArtifacts = false } = {}) { +async function createFixture(prefix: string, { envFile, builderArgs = [], staleArtifacts = false }: { + envFile?: string; + builderArgs?: string[]; + staleArtifacts?: boolean; +} = {}): Promise { const directory = await mkdtemp(join(tmpdir(), prefix)); await mkdir(join(directory, "src", "configurator"), { recursive: true }); await mkdir(join(directory, "node_modules", "capnweb", "dist"), { recursive: true }); @@ -38,7 +42,7 @@ async function createFixture(prefix, { envFile, builderArgs = [], staleArtifacts return directory; } -async function readRuntime(directory) { +async function readRuntime(directory: string): Promise { const html = await readFile(join(directory, "src", "generated", "test-ui.txt"), "utf8"); const match = html.match( /