From 9d2d13aaf83d339700ecd7535b685cf9efe99c86 Mon Sep 17 00:00:00 2001 From: kkdev92 <112151103+kkdev92@users.noreply.github.com> Date: Sun, 30 Aug 2026 00:34:10 +0900 Subject: [PATCH] feat(docs): generate the API reference, and export the types public signatures name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `npm run docs:api` renders every public entry point with TypeDoc, and CI runs it with warnings as errors. Setting that up found what the warnings exist to find: types that public signatures name — the port a tree provider returns, the surface a fake hands back, the options `setting.boolean` takes, the reason on a cancelled operation — which the package never exported, so a consumer could receive one and not be able to write its type. They are exported now, from the entry whose signatures name them. `{@link}` tags that pointed at internal symbols became plain code. `ApplicationPlan` and `ModuleDefinition` keep their members internal: both are handles — `defineExtension` and `createTestHost` consume them — and `describePlan` is the readable form. The output lands in `docs/api/`, ignored by git; hosting it is a separate step. Co-Authored-By: Claude Fable 5 --- .github/workflows/ci.yml | 6 + .gitignore | 2 + CHANGELOG.md | 13 ++ CONTRIBUTING.md | 3 +- README.md | 8 +- package-lock.json | 207 ++++++++++++++++++ package.json | 4 +- src/capabilities/core/disposable.ts | 4 +- src/capabilities/core/schema.ts | 2 +- src/capabilities/secrets/secrets.ts | 4 +- src/capabilities/std/retry.ts | 2 +- src/capabilities/ui/quick-input.ts | 8 +- src/foundation/application/application.ts | 2 +- src/foundation/application/plan.ts | 22 ++ src/foundation/commands/contract.ts | 2 +- src/foundation/modules/definition.ts | 21 +- src/foundation/platform/ports.ts | 2 +- .../resources/registration-scope.ts | 2 +- src/foundation/resources/resource-scope.ts | 2 +- src/foundation/settings/definition.ts | 14 +- src/index.ts | 43 ++++ src/testing/index.ts | 23 ++ src/testing/mock/vitest-config.ts | 2 +- src/testing/mock/vitest.ts | 2 +- src/testing/mock/vscode-mock.ts | 2 +- src/testing/test-host.ts | 2 +- typedoc.json | 20 ++ 27 files changed, 392 insertions(+), 32 deletions(-) create mode 100644 typedoc.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7a5af19..baec17a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -56,6 +56,12 @@ jobs: - name: Check for unused code run: npm run knip + # The API reference, with warnings as errors: a broken `{@link}`, or a + # type a public signature names that the package does not export, fails + # here rather than shipping as a hole in the documentation. + - name: Generate the API reference + run: npm run docs:api + # Real Extension Host. The only lane that can verify the shutdown contract the # whole design rests on -- and the one that found VS Code disposes # context.subscriptions *during* deactivate(), not after it. diff --git a/.gitignore b/.gitignore index f48c349..0efd2e2 100644 --- a/.gitignore +++ b/.gitignore @@ -64,3 +64,5 @@ mkdocs/ # CLAUDE.md CLAUDE.md +# Generated API reference (`npm run docs:api`) +docs/api/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 70fd159..a7420c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -82,6 +82,19 @@ Pre-1.0 releases followed it in spirit; their breaking changes are marked **Brea it concerns, and the JSON that would settle it when the fix is mechanical. The assertion is unchanged and built on top of it. +- **An API reference, generated from the JSDoc.** `npm run docs:api` renders + every public entry point with TypeDoc, and CI runs it with warnings as + errors. Setting that up found what the warnings exist to find: types that + public signatures name — the port a tree provider returns, the surface a fake + hands back, the options `setting.boolean` takes, the reason on a cancelled + operation — which the package never exported, so a consumer could receive one + and not be able to write its type. They are exported now, from the entry + whose signatures name them. `{@link}` tags that pointed at internal symbols + became plain code. `ApplicationPlan`'s members are marked internal: the plan + is a handle for `createTestHost` and `describePlan`, and `describePlan` is + its readable form. The reference is not hosted yet; the output is under + `docs/api/`, ignored by git. + ### Changed - **`defineExtension` is single-use, like the extension host it serves.** A diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 14f4889..26b5438 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,8 +42,9 @@ npm run test:coverage # the same suite, with the coverage gate npm run lint # type-aware ESLint over src + tests, --max-warnings 0 npm run format # prettier npm run knip # dead code +npm run docs:api # TypeDoc into docs/api; warnings are errors npm run verify:package # pack, install into a throwaway consumer, import every subpath -npm run quality # typecheck + lint + test:coverage + knip — the gate CI runs +npm run quality # typecheck + lint + test:coverage + knip + docs:api — the gate CI runs ``` `tsc -b --noEmit` does not work (TS6310: project references need `composite`, and diff --git a/README.md b/README.md index 60aa57a..ad5c085 100644 --- a/README.md +++ b/README.md @@ -257,8 +257,10 @@ Values you pass around: `ok` `err` `unwrap` `mapResult` and the `s.*` schema builders; `FrameworkError` with `userError` / `validationError` / `classifyError` / `isCancellation`; `DisposableCollection` and `createScope`. -Full signatures live in the `.d.ts` files and the JSDoc on each export; a -generated API reference is not built yet. +Full signatures live in the `.d.ts` files and the JSDoc on each export. +`npm run docs:api` renders that JSDoc into an API reference under `docs/api/` +(TypeDoc); CI runs it with warnings as errors, so a broken link or a type a +public signature names but the package does not export cannot ship. --- @@ -308,7 +310,7 @@ trusted for things it cannot do. - **The Test Host does not reproduce VS Code**: it renders no UI, interprets no contribution point, and does not substitute a direct `import "vscode"` - **No editor events yet**: `Editors` hands you the active editor and cross-file edits, but there is no `onDidChangeActive` / `onDidChangeSelection` / `onDidChangeDocument`; subscribing means reaching for `vscode` directly and disposing by hand, which is the one place the single-cleanup-owner rule leaks - **No log-level filtering, deliberately**: the framework writes to a `LogOutputChannel` and VS Code owns the level — per channel, persisted, in the Output panel. An extension cannot raise its own channel's level, so a `logLevel` setting of your own can only ever make the log quieter -- **No generated API reference yet**, and no step-by-step migration guide from 2.x +- **No hosted API reference yet** — it is generated from the JSDoc (`npm run docs:api`) but not published — and no step-by-step migration guide from 2.x --- diff --git a/package-lock.json b/package-lock.json index 40cd2c6..942aad0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@kkdev92/vscode-ext-kit", "version": "4.0.1", "license": "MIT", + "bin": { + "vscode-ext-kit": "bin/vscode-ext-kit.mjs" + }, "devDependencies": { "@eslint/js": "^10.0.1", "@types/node": "^26.1.1", @@ -23,6 +26,7 @@ "lint-staged": "^17.3.0", "prettier": "^3.9.6", "rimraf": "^6.1.3", + "typedoc": "^0.28.20", "typescript": "~6.0.3", "typescript-eslint": "^8.66.0", "vitest": "^4.1.10" @@ -704,6 +708,20 @@ "node": "^20.19.0 || ^22.13.0 || >=24" } }, + "node_modules/@gerrit0/mini-shiki": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@gerrit0/mini-shiki/-/mini-shiki-3.23.0.tgz", + "integrity": "sha512-bEMORlG0cqdjVyCEuU0cDQbORWX+kYCeo0kV1lbxF5bt4r7SID2l9bqsxJEM0zndaxpOUT7riCyIVEuqq/Ynxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/engine-oniguruma": "^3.23.0", + "@shikijs/langs": "^3.23.0", + "@shikijs/themes": "^3.23.0", + "@shikijs/types": "^3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, "node_modules/@humanfs/core": { "version": "0.19.2", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", @@ -1803,6 +1821,55 @@ "dev": true, "license": "MIT" }, + "node_modules/@shikijs/engine-oniguruma": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/engine-oniguruma/-/engine-oniguruma-3.23.0.tgz", + "integrity": "sha512-1nWINwKXxKKLqPibT5f4pAFLej9oZzQTsby8942OTlsJzOBZ0MWKiwzMsd+jhzu8YPCHAswGnnN1YtQfirL35g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0", + "@shikijs/vscode-textmate": "^10.0.2" + } + }, + "node_modules/@shikijs/langs": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/langs/-/langs-3.23.0.tgz", + "integrity": "sha512-2Ep4W3Re5aB1/62RSYQInK9mM3HsLeB91cHqznAJMuylqjzNVAVCMnNWRHFtcNHXsoNRayP9z1qj4Sq3nMqYXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/themes": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/themes/-/themes-3.23.0.tgz", + "integrity": "sha512-5qySYa1ZgAT18HR/ypENL9cUSGOeI2x+4IvYJu4JgVJdizn6kG4ia5Q1jDEOi7gTbN4RbuYtmHh0W3eccOrjMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/types": "3.23.0" + } + }, + "node_modules/@shikijs/types": { + "version": "3.23.0", + "resolved": "https://registry.npmjs.org/@shikijs/types/-/types-3.23.0.tgz", + "integrity": "sha512-3JZ5HXOZfYjsYSk0yPwBrkupyYSLpAE26Qc0HLghhZNGTZg/SKxXIIgoxOpmmeQP0RRSDJTk1/vPfw9tbw+jSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shikijs/vscode-textmate": "^10.0.2", + "@types/hast": "^3.0.4" + } + }, + "node_modules/@shikijs/vscode-textmate": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/@shikijs/vscode-textmate/-/vscode-textmate-10.0.2.tgz", + "integrity": "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg==", + "dev": true, + "license": "MIT" + }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", @@ -1853,6 +1920,16 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -1870,6 +1947,13 @@ "undici-types": "~8.3.0" } }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/vscode": { "version": "1.134.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.134.0.tgz", @@ -2438,6 +2522,13 @@ "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -2849,6 +2940,19 @@ "once": "^1.4.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-module-lexer": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.3.1.tgz", @@ -4182,6 +4286,26 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/linkify-it": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, "node_modules/lint-staged": { "version": "17.3.0", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-17.3.0.tgz", @@ -4262,6 +4386,13 @@ "node": "20 || >=22" } }, + "node_modules/lunr": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz", + "integrity": "sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==", + "dev": true, + "license": "MIT" + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -4300,6 +4431,41 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/markdown-it": { + "version": "14.3.1", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.1.tgz", + "integrity": "sha512-4Ej49aYTDFIQ+uBkfX8GBvJGccoARxxPep+7aWTs55ozbjQJpW9M26Fe53vnGgvLeVzva/amzjQQaQu9w0vMhA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/mdurl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", + "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==", + "dev": true, + "license": "MIT" + }, "node_modules/media-typer": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.1.tgz", @@ -4942,6 +5108,16 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", @@ -5550,6 +5726,30 @@ "url": "https://opencollective.com/express" } }, + "node_modules/typedoc": { + "version": "0.28.20", + "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.28.20.tgz", + "integrity": "sha512-uSKqkh8Cr48vllnEy+jdaAgOeR6Y+QCBW7usgUsKj7gJEfR7stw9U/fE49LBnj2tPRKPY0c0EBJSWe9Appmplg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@gerrit0/mini-shiki": "^3.23.0", + "lunr": "^2.3.9", + "markdown-it": "^14.3.0", + "minimatch": "^10.2.5", + "yaml": "^2.9.0" + }, + "bin": { + "typedoc": "bin/typedoc" + }, + "engines": { + "node": ">= 18", + "pnpm": ">= 10" + }, + "peerDependencies": { + "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x || 5.9.x || 6.0.x" + } + }, "node_modules/typescript": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", @@ -5588,6 +5788,13 @@ "typescript": ">=4.8.4 <6.1.0" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, "node_modules/unbash": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/unbash/-/unbash-4.0.10.tgz", diff --git a/package.json b/package.json index 521464f..0b43953 100644 --- a/package.json +++ b/package.json @@ -96,6 +96,7 @@ "lint-staged": "^17.3.0", "prettier": "^3.9.6", "rimraf": "^6.1.3", + "typedoc": "^0.28.20", "typescript": "~6.0.3", "typescript-eslint": "^8.66.0", "vitest": "^4.1.10" @@ -114,12 +115,13 @@ "format": "prettier --write \"{src,tests,fixtures,docs}/**/*.ts\" \"{bin,scripts,fixtures,tests}/**/*.{mjs,cjs}\"", "format:check": "prettier --check \"{src,tests,fixtures,docs}/**/*.ts\" \"{bin,scripts,fixtures,tests}/**/*.{mjs,cjs}\"", "knip": "knip", + "docs:api": "typedoc", "verify:package": "node scripts/verify-package.mjs", "fixture:build": "node fixtures/extension-host/build.mjs", "fixture:build:web": "node fixtures/web-extension/build.mjs", "test:eh": "npm run build && npm run fixture:build && node fixtures/extension-host/run-test.mjs", "test:web": "npm run build && npm run fixture:build:web && node fixtures/web-extension/run-test.mjs", - "quality": "npm run format:check && npm run typecheck && npm run lint && npm run test:coverage && npm run knip" + "quality": "npm run format:check && npm run typecheck && npm run lint && npm run test:coverage && npm run knip && npm run docs:api" }, "keywords": [ "vscode", diff --git a/src/capabilities/core/disposable.ts b/src/capabilities/core/disposable.ts index 0788b9c..b4cc08c 100644 --- a/src/capabilities/core/disposable.ts +++ b/src/capabilities/core/disposable.ts @@ -13,7 +13,7 @@ * A returned promise is not awaited; asynchronous cleanup belongs in a hosted * service or `ResourceScope`. */ -interface DisposableLike { +export interface DisposableLike { dispose(): unknown; } @@ -109,7 +109,7 @@ export class DisposableCollection implements DisposableLike { } /** The part of `vscode.ExtensionContext` that {@link createScope} needs. */ -interface SubscriptionsHost { +export interface SubscriptionsHost { readonly subscriptions: { push(disposable: DisposableLike): void }; } diff --git a/src/capabilities/core/schema.ts b/src/capabilities/core/schema.ts index b496be4..c13b7fd 100644 --- a/src/capabilities/core/schema.ts +++ b/src/capabilities/core/schema.ts @@ -300,7 +300,7 @@ export const s = { * * Keys come from the input, which for a record is the whole point — and * `JSON.parse` produces `__proto__` as a genuine own property, so a record - * built from parsed JSON can carry one. See {@link define} for why that key + * built from parsed JSON can carry one. See `defineOwn` in `foundation/internal/record.ts` for why that key * is not written with `output[key] = …`. */ record( diff --git a/src/capabilities/secrets/secrets.ts b/src/capabilities/secrets/secrets.ts index d450f86..06664f3 100644 --- a/src/capabilities/secrets/secrets.ts +++ b/src/capabilities/secrets/secrets.ts @@ -2,7 +2,7 @@ * Secret-storage services above the platform's encrypted secrets port. * * Public surface: {@link SecretStore} addresses dynamic user-chosen keys, - * {@link SecretStorage} narrows that store to one string key, and + * `SecretStorage` narrows that store to one string key, and * {@link SecretAccessor} adds JSON serialization plus synchronous Standard * Schema validation for a declared structured secret. * @@ -177,7 +177,7 @@ export function createSecretStorage(capability: SecretsCapability, key: string): /** * A typed secret scoped to one key. * - * The structured flavour of {@link SecretStorage}: values are JSON-serialized, + * The structured flavour of `SecretStorage`: values are JSON-serialized, * and when a schema is declared they are validated against it in both * directions — before a write is serialized, and after a read is parsed. */ diff --git a/src/capabilities/std/retry.ts b/src/capabilities/std/retry.ts index b421d80..872bc1d 100644 --- a/src/capabilities/std/retry.ts +++ b/src/capabilities/std/retry.ts @@ -78,7 +78,7 @@ export interface RetryOptions { */ readonly signal?: AbortSignal; /** - * Per-attempt timeout. A timed-out attempt throws {@link TimeoutError}, which + * Per-attempt timeout. A timed-out attempt throws `TimeoutError` (from `./timing`), which * is then treated like any other failure. */ readonly timeoutMs?: number; diff --git a/src/capabilities/ui/quick-input.ts b/src/capabilities/ui/quick-input.ts index 2cb52a8..aa2a6dc 100644 --- a/src/capabilities/ui/quick-input.ts +++ b/src/capabilities/ui/quick-input.ts @@ -55,7 +55,7 @@ export function toPickSeparator(label = ''): QuickPickItemLike { return { label, kind: SEPARATOR_KIND }; } -/** Options accepted by {@link pickOne} and {@link pickMany}. */ +/** Options accepted by `pickOne` and `pickMany`. */ export interface PickOptions { /** Title shown at the top of the picker. */ readonly title?: string; @@ -310,7 +310,7 @@ export async function pickMany( return (await pickFromQuickPick(capability, items, opts, true)) as T[] | undefined; } -/** Options for {@link inputText}. */ +/** Options for `inputText`. */ export interface InputTextOptions { /** Prompt text to display. */ readonly prompt: string; @@ -322,7 +322,7 @@ export interface InputTextOptions { readonly password?: boolean; /** * Validation function. May be asynchronous. A non-empty message blocks - * acceptance; a thrown/rejected error rejects {@link inputText}. + * acceptance; a thrown/rejected error rejects `inputText`. */ readonly validate?: (value: string) => string | undefined | Promise; /** @@ -584,7 +584,7 @@ export interface PickButtonOptions { } /** - * Builds a {@link QuickInputButtonLike}, accepting a codicon name in place + * Builds a `QuickInputButtonLike`, accepting a codicon name in place * of a hand-built theme icon the same way {@link toPickItem} * does for item icons. * diff --git a/src/foundation/application/application.ts b/src/foundation/application/application.ts index 09fe0ff..09b986d 100644 --- a/src/foundation/application/application.ts +++ b/src/foundation/application/application.ts @@ -101,7 +101,7 @@ import type { ApplicationPlan } from './plan.js'; * Declared structurally so the runtime core never imports `vscode`; a real * `ExtensionContext` satisfies it. */ -interface ExtensionHostContext { +export interface ExtensionHostContext { readonly subscriptions: { push(disposable: { dispose(): unknown }): void }; } diff --git a/src/foundation/application/plan.ts b/src/foundation/application/plan.ts index f64d840..fcb05bb 100644 --- a/src/foundation/application/plan.ts +++ b/src/foundation/application/plan.ts @@ -64,24 +64,46 @@ interface ShutdownPolicy { * binding does not discover an internal id clash or broken dependency graph. * Platform conflicts and host-dependent requirements can still fail activation * and are rolled back by the Application Host. + * + * To a consumer this is a handle: `defineExtension` produces it, `createTestHost` + * and `describePlan` consume it. Its members carry the framework's own + * definition records and are not a public contract — `describePlan` is the + * readable form, and the shape a tool should depend on. */ export interface ApplicationPlan { + /** @internal */ readonly name: string; + /** @internal */ readonly modules: readonly ModuleDefinition[]; + /** @internal */ readonly services: readonly ServiceDescriptor[]; + /** @internal */ readonly commands: readonly CommandDefinition[]; + /** @internal */ readonly textEditorCommands: readonly TextEditorCommandDefinition[]; + /** @internal */ readonly hostedServices: readonly HostedServiceDefinition[]; + /** @internal */ readonly settings: readonly SettingsRegistration[]; + /** @internal */ readonly storage: readonly StorageRegistration[]; + /** @internal */ readonly secrets: readonly SecretRegistration[]; + /** @internal */ readonly fileWatchers: readonly FileWatcherDefinition[]; + /** @internal */ readonly statusBarItems: readonly StatusBarItemDefinition[]; + /** @internal */ readonly languageStatusItems: readonly LanguageStatusItemDefinition[]; + /** @internal */ readonly treeViews: readonly TreeViewDefinition[]; + /** @internal */ readonly webviewViews: readonly WebviewViewDefinition[]; + /** @internal */ readonly webviewSerializers: readonly WebviewPanelSerializerDefinition[]; + /** @internal */ readonly rawRegistrations: readonly RawRegistrationDefinition[]; + /** @internal */ readonly shutdown: ShutdownPolicy; } diff --git a/src/foundation/commands/contract.ts b/src/foundation/commands/contract.ts index 714543f..5961e20 100644 --- a/src/foundation/commands/contract.ts +++ b/src/foundation/commands/contract.ts @@ -29,7 +29,7 @@ export type ValidationResult = * Minimal validator contract. The core depends on no validator library. * * Any synchronous Standard Schema v1 validator is also accepted; see - * {@link toValidator}. + * `toValidator`. */ export interface Validator { validate(value: unknown): ValidationResult; diff --git a/src/foundation/modules/definition.ts b/src/foundation/modules/definition.ts index b259a8c..277ed86 100644 --- a/src/foundation/modules/definition.ts +++ b/src/foundation/modules/definition.ts @@ -387,7 +387,7 @@ export interface SecretCollection { /** * Records VS Code APIs the framework has no model for. The callback receives * the Module scopes needed to participate in ownership and activation rollback; - * see {@link RawRegistrationDefinition}. + * see `RawRegistrationDefinition`. */ export interface RawRegistrationCollection> { /** Registers with declared dependencies. */ @@ -485,22 +485,41 @@ export interface DefineModuleOptions; diff --git a/src/foundation/settings/definition.ts b/src/foundation/settings/definition.ts index 91e7662..0abf105 100644 --- a/src/foundation/settings/definition.ts +++ b/src/foundation/settings/definition.ts @@ -57,7 +57,7 @@ export interface SettingSpec { } /** Common options for every `setting.*` builder. */ -interface BaseOptions { +export interface SettingBaseOptions { readonly default: T; readonly scope?: SettingContributionScope | undefined; } @@ -96,7 +96,7 @@ function typeNames(type: SettingSpec['type']): readonly SettingValueTyp * ``` */ export const setting = { - boolean(options: BaseOptions): SettingSpec { + boolean(options: SettingBaseOptions): SettingSpec { return { type: 'boolean', default: options.default, @@ -107,7 +107,7 @@ export const setting = { }, number( - options: BaseOptions & { readonly minimum?: number; readonly maximum?: number } + options: SettingBaseOptions & { readonly minimum?: number; readonly maximum?: number } ): SettingSpec { return { type: 'number', @@ -136,7 +136,7 @@ export const setting = { * makes the same distinction. */ integer( - options: BaseOptions & { readonly minimum?: number; readonly maximum?: number } + options: SettingBaseOptions & { readonly minimum?: number; readonly maximum?: number } ): SettingSpec { return { type: 'integer', @@ -157,7 +157,7 @@ export const setting = { }; }, - string(options: BaseOptions): SettingSpec { + string(options: SettingBaseOptions): SettingSpec { return { type: 'string', default: options.default, @@ -168,7 +168,7 @@ export const setting = { }, enum( - options: BaseOptions & { readonly values: TValues } + options: SettingBaseOptions & { readonly values: TValues } ): SettingSpec { return { type: 'string', @@ -191,7 +191,7 @@ export const setting = { * Patterns are tested as supplied; avoid stateful `g` or `y` flags. */ stringArray( - options: BaseOptions & { + options: SettingBaseOptions & { readonly items?: { readonly minLength?: number; readonly pattern?: RegExp }; } ): SettingSpec { diff --git a/src/index.ts b/src/index.ts index 8f21323..695cf40 100644 --- a/src/index.ts +++ b/src/index.ts @@ -374,3 +374,46 @@ export { DisposableCollection, createScope } from './capabilities/core/disposabl // --- Where the host is running -------------------------------------------- export { UiKind } from './foundation/platform/ports.js'; export type { HostEnvironment, ResourceUri, WatchedUri } from './foundation/platform/ports.js'; + +// --- Types that public signatures name --------------------------------------- +// A consumer handed one of these should be able to write its type. They are +// the contracts behind existing exports, not another way to reach anything. +export type { + AccessibilityInformationLike, + CommandLike, + CommandLinkLike, + LanguageFilterLike, + LanguageSelectorLike, + MarkdownLike, + NotificationSeverity, + PlatformRegistration, + ProgressArea, + ProgressReporterLike, + QuickInputButtonLike, + QuickInputCapability, + QuickPickItemLike, + RelativePatternLike, + TooltipLike, + TreeDataSource, + TreeDragAndDrop, + TreeItemLike, +} from './foundation/platform/ports.js'; +export type { DisposableLike } from './capabilities/core/disposable.js'; +// What `defineStatusBarItem` and `defineLanguageStatusItem` return. +export type { StatusBarItemDefinition } from './capabilities/ui/definition.js'; +export type { LanguageStatusItemDefinition } from './capabilities/ui/definition.js'; +export type { CommandDescriptor } from './foundation/commands/contract.js'; +export type { SchemaPathSegment } from './capabilities/core/schema.js'; +export type { RememberedChoice } from './capabilities/ui/notifications.js'; +export type { Emitter } from './foundation/internal/emitter.js'; +// Why an operation was cancelled and what kind of ingress started it: both are +// read off values existing exports hand out (`OperationCancelledError.reason`, +// `OperationContext.kind`), so both the constants and the types are here. +export { CancellationReason } from './foundation/operations/cancellation.js'; +export { OperationKind } from './foundation/operations/context.js'; +export type { SubscriptionsHost } from './capabilities/core/disposable.js'; +export type { WebviewLike } from './capabilities/views/webview/rpc.js'; +export type { WebviewCspSourceLike } from './capabilities/views/webview/html.js'; +export type { DefineExtensionOptionsWithExports } from './vscode/foundation/extension.js'; +export type { StandardSchemaResult } from './foundation/commands/contract.js'; +export type { SettingBaseOptions } from './foundation/settings/definition.js'; diff --git a/src/testing/index.ts b/src/testing/index.ts index b5dfa81..f1a7728 100644 --- a/src/testing/index.ts +++ b/src/testing/index.ts @@ -84,6 +84,29 @@ export type { export { createRecordingLogSink } from './fakes/recording-log-sink.js'; export type { RecordingLogSink } from './fakes/recording-log-sink.js'; +// --- Port types the fakes implement or hand back ---------------------------- +// Named by the fakes' public signatures; exported so a test can type what it +// scripts and what it receives. +export type { + ActiveTextEditor, + CancellationTokenLike, + FileWatcherHandle, + InputBoxLike, + LanguageStatusItemFields, + LanguageStatusItemHandle, + LocalizedMessage, + NotificationActionLike, + QuickPickLike, + SettingsChangeSource, + StatusBarItemFields, + StatusBarItemHandle, + WebviewPanelSurface, + WebviewViewSurface, +} from '../foundation/platform/ports.js'; +export type { Application, ExtensionHostContext } from '../foundation/application/application.js'; +export type { ApplicationHost, HostInspection } from '../foundation/hosting/application-host.js'; +export type { HostState, StopReason } from '../foundation/hosting/host-state.js'; + // --- Partial stand-in for direct `vscode` imports --------------------------- // This is deliberately lower-level than TestHost. It implements the subset // documented by its builders, not the whole VS Code API. Factories accept the diff --git a/src/testing/mock/vitest-config.ts b/src/testing/mock/vitest-config.ts index e2f7091..42b4b3b 100644 --- a/src/testing/mock/vitest-config.ts +++ b/src/testing/mock/vitest-config.ts @@ -31,7 +31,7 @@ * * - **`resolve.alias`** points every Vite-resolved `import ... from 'vscode'` at * the mock in - * {@link ./vitest.js `@kkdev92/vscode-ext-kit/testing/vitest`}. This makes the + * `@kkdev92/vscode-ext-kit/testing/vitest`. This makes the * replacement a project configuration rule instead of per-test mock setup, * and also covers a built bundle when Vite keeps it in the module graph. * - **`server.deps.inline`** stops Vitest from externalizing this package. diff --git a/src/testing/mock/vitest.ts b/src/testing/mock/vitest.ts index 439ee81..7789239 100644 --- a/src/testing/mock/vitest.ts +++ b/src/testing/mock/vitest.ts @@ -24,7 +24,7 @@ * ); * ``` * - * Unlike {@link ../testing/index.js `@kkdev92/vscode-ext-kit/testing`} — which + * Unlike `@kkdev92/vscode-ext-kit/testing` — which * takes an injected `{ fn }` and works with any runner — this module imports * `vi` directly, because an aliased module is resolved before any test file * runs and so has no chance to be handed a framework. That's also why the mock diff --git a/src/testing/mock/vscode-mock.ts b/src/testing/mock/vscode-mock.ts index b2d7a5f..8d39894 100644 --- a/src/testing/mock/vscode-mock.ts +++ b/src/testing/mock/vscode-mock.ts @@ -1504,7 +1504,7 @@ function createMockWindowNamespace(framework: MockFrameworkLike) { }), /** * `Dark` by default. A plain mutable field, so a test can assign it - * directly — use {@link _setColorTheme} instead when listeners registered + * directly — use `_setColorTheme` instead when listeners registered * through `onDidChangeActiveColorTheme` also need to fire. */ activeColorTheme: { kind: ColorThemeKind.Dark } as vscode.ColorTheme, diff --git a/src/testing/test-host.ts b/src/testing/test-host.ts index fafe092..f6132e8 100644 --- a/src/testing/test-host.ts +++ b/src/testing/test-host.ts @@ -115,7 +115,7 @@ export interface TestHost { readonly statusBar: FakeStatusBar; /** The fake language status area the plan's items render on. */ readonly languageStatus: FakeLanguageStatus; - /** The fake quick input the {@link QuickInput} service resolves to. */ + /** The fake quick input the `QuickInput` service resolves to. */ readonly quickInput: FakeQuickInput; /** The fake tree view registry the plan's views register into. */ readonly treeViews: FakeTreeViews; diff --git a/typedoc.json b/typedoc.json new file mode 100644 index 0000000..8720b5b --- /dev/null +++ b/typedoc.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://typedoc.org/schema.json", + "entryPoints": [ + "src/index.ts", + "src/testing/index.ts", + "src/testing/mock/vitest.ts", + "src/testing/mock/vitest-config.ts", + "src/capabilities/webview-client/index.ts", + "src/capabilities/std/timing.ts", + "src/capabilities/std/retry.ts", + "src/capabilities/l10n/format.ts" + ], + "tsconfig": "tsconfig.json", + "out": "docs/api", + "readme": "none", + "excludeInternal": true, + "excludePrivate": true, + "validation": { "notExported": true, "invalidLink": true, "notDocumented": false }, + "treatWarningsAsErrors": true +}