From 95e90e4e30fb2c7069794e7715db3f42e610d156 Mon Sep 17 00:00:00 2001 From: Dan Draper Date: Tue, 28 Jul 2026 16:30:04 +1000 Subject: [PATCH] docs: resolve protect-ffi's real types so the Stack reference stops saying `any` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The generated Stack API reference documents 13 types as `any` and gives AuthStrategy the wrong description entirely. Both come from one missing compiler option. @cipherstash/protect-ffi's `exports` map routes the `node` condition to lib/index.d.cts (the real type surface) and everything else to `default: ./dist/wasm/protect_ffi.js`, which ships no `types`. TypeScript therefore falls through to the sibling dist/wasm/protect_ffi.d.ts — raw wasm-bindgen output — where none of the hand-written types exist. `moduleResolution: "bundler"` does not imply the `node` condition; packages/stack/tsconfig.json adds `customConditions: ["node"]`, which is why the stack team never sees this. But typedoc.tsconfig.json extends stack's ROOT tsconfig, which does not set it. Set it on the generated config. Confirmed with a minimal repro against protect-ffi 0.30.0 (the version stack 1.0.0-rc.4 pins exactly): `moduleResolution: bundler` alone yields "has no exported member 'ProtectError'. Did you mean 'encryptQuery'?"; adding customConditions typechecks clean. Also turns error checking back on. It was disabled to tolerate this, on the theory that the references were "unresolved even though the source is correct" and TypeDoc would still emit accurate signatures. It does not — an unresolved import becomes `any` in the output, so the workaround converted a loud failure into a silently wrong reference. With the condition fixed the surface typechecks with 0 errors, so the next resolution break fails the build instead. Generating with and without the fix, back to back against the same stack tag, changes 23 pages. Representative: EncryptionError.code any -> ProtectErrorCode EncryptQueryOperation.execute() Promise> -> Promise> encryptQuery plaintext param any -> Plaintext | null | undefined AuthStrategy description the module's blurb -> the type's own docs Claude-Session: https://claude.ai/code/session_01NkuQNMvw9BpB4BWWeKtfV8 --- scripts/lib/docs-generator.ts | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/scripts/lib/docs-generator.ts b/scripts/lib/docs-generator.ts index b27c01a..e5c7579 100644 --- a/scripts/lib/docs-generator.ts +++ b/scripts/lib/docs-generator.ts @@ -213,6 +213,18 @@ export async function generateDocsForTag( include: config.tsconfigInclude, exclude: ["node_modules", "examples", "dist", "__tests__"], compilerOptions: { + // The stack repo's ROOT tsconfig (which this extends) sets + // `moduleResolution: "bundler"` but no `customConditions`; + // `packages/stack/tsconfig.json` — the config the stack team actually + // builds with — adds `customConditions: ["node"]`. That matters here: + // @cipherstash/protect-ffi's `exports` map routes the `node` condition to + // `lib/index.d.cts` (the real type surface) and everything else to + // `default: ./dist/wasm/protect_ffi.js`, which ships NO `types`. Without + // the condition, TypeScript falls through to the sibling + // `dist/wasm/protect_ffi.d.ts` — raw wasm-bindgen output — and every + // hand-written type resolves to nothing ("has no exported member + // 'ProtectError'. Did you mean 'encryptQuery'?"). + customConditions: ["node"], paths: { "@/*": ["./packages/stack/src/*"], "@cipherstash/schema": ["./packages/schema/src/index.ts"], @@ -323,13 +335,15 @@ export async function generateDocsForTag( sanitizeComments: true, fileExtension: ".mdx", entryFileName: "index", - // We document external source we don't control, installed here with a - // different package manager than the upstream monorepo uses. That can leave - // cross-package type references (e.g. @cipherstash/protect-ffi's ProtectError) - // unresolved for the isolated typecheck even though the source is correct, - // which would otherwise fail the whole build. TypeDoc still emits accurate - // signatures from the source AST, so tolerate type errors rather than block. - skipErrorChecking: true, + // Type errors fail the build, deliberately. This was `true` to tolerate + // "cross-package type references unresolved even though the source is + // correct" — but that diagnosis was wrong, and tolerating it was not free: + // an unresolved import does not just warn, it makes TypeDoc emit `any`. + // The cause was the missing `customConditions` above, and with that fixed + // the whole surface typechecks cleanly. Leaving this off means the next + // resolution break surfaces as a failed build instead of a reference page + // that quietly documents `any`. + skipErrorChecking: false, sort: ["source-order"], kindSortOrder: [ "Interface",