Просмотр исходного кода

Use explicit ts specifiers for declarations

Restore explicit .ts relative specifiers in source and enable rewriteRelativeImportExtensions so emitted JS uses .js while declarations keep explicit .ts specifiers. Add a NodeNext declaration-consumer gate to prevent extensionless declaration regressions.
Tianyi Cui 3 месяцев назад
Родитель
Сommit
07f4047ff0
56 измененных файлов с 323 добавлено и 147 удалено
  1. 4 3
      .github/workflows/ci.yml
  2. 4 2
      AGENTS.md
  3. 2 0
      docs/cookbook/adding-a-package.md
  4. 2 0
      docs/cookbook/adding-a-vendored-package.md
  5. 3 2
      docs/development.md
  6. 1 1
      docs/rfc/implemented/process/2026-06-11-quality-gates.md
  7. 7 2
      docs/rfc/implemented/process/2026-06-17-ts-build-config.md
  8. 2 1
      package.json
  9. 1 1
      packages/README.md
  10. 4 4
      packages/bash/bash-local/src/index.ts
  11. 3 3
      packages/bash/bash/src/index.ts
  12. 2 2
      packages/core/agent-loop/src/agent.ts
  13. 4 4
      packages/core/agent-loop/src/index.ts
  14. 1 1
      packages/core/agent-loop/src/loop.ts
  15. 2 2
      packages/core/agent/src/index.ts
  16. 6 6
      packages/core/session/src/index.ts
  17. 1 1
      packages/core/session/src/repair.ts
  18. 1 1
      packages/core/tools/src/index.ts
  19. 1 1
      packages/core/tools/src/schema.ts
  20. 5 5
      packages/llm/llm-deepseek/src/adapter.ts
  21. 8 8
      packages/llm/llm-deepseek/src/index.ts
  22. 1 1
      packages/llm/llm-deepseek/src/serialize.ts
  23. 2 2
      packages/llm/llm-deepseek/src/translate.ts
  24. 1 1
      packages/llm/llm-pi-ai/src/adapter.ts
  25. 5 5
      packages/llm/llm-pi-ai/src/index.ts
  26. 3 3
      packages/llm/llm/src/assembler.ts
  27. 7 7
      packages/llm/llm/src/index.ts
  28. 1 1
      packages/llm/llm/src/types.ts
  29. 1 1
      packages/session-persistence/session-persistence-jsonl/src/index.ts
  30. 2 2
      packages/session-persistence/session-persistence-sqlite/src/index.ts
  31. 1 1
      packages/session-persistence/session-persistence/src/coordinator.ts
  32. 2 2
      packages/session-persistence/session-persistence/src/index.ts
  33. 1 1
      packages/ui/acp/src/index.ts
  34. 160 0
      scripts/verify-node-next-types.ts
  35. 2 0
      tsconfig.base.json
  36. 1 1
      vendor/README.md
  37. 6 6
      vendor/cordis/src/context.ts
  38. 4 4
      vendor/cordis/src/events.ts
  39. 5 5
      vendor/cordis/src/fiber.ts
  40. 7 7
      vendor/cordis/src/index.ts
  41. 4 4
      vendor/cordis/src/logger.ts
  42. 4 4
      vendor/cordis/src/reflect.ts
  43. 4 4
      vendor/cordis/src/registry.ts
  44. 2 2
      vendor/cordis/src/service.ts
  45. 1 1
      vendor/cordis/src/utils.ts
  46. 1 1
      vendor/cosmokit/src/array.ts
  47. 5 5
      vendor/cosmokit/src/index.ts
  48. 1 1
      vendor/cosmokit/src/types.ts
  49. 1 1
      vendor/hmr/src/index.ts
  50. 4 4
      vendor/loader/src/config/entry.ts
  51. 2 2
      vendor/loader/src/config/group.ts
  52. 2 2
      vendor/loader/src/config/isolate.ts
  53. 2 2
      vendor/loader/src/config/tree.ts
  54. 10 10
      vendor/loader/src/index.ts
  55. 2 2
      vendor/logger-console/src/browser.ts
  56. 2 2
      vendor/logger-console/src/index.ts

+ 4 - 3
.github/workflows/ci.yml

@@ -69,12 +69,13 @@ jobs:
         run: pnpm run test:snapshot
 
       # Before hygiene: publint validates the packed artifacts (lib/index.js),
-      # which only the tsdown bundling step emits.
+      # which only the tsdown bundling step emits, and verify-node-next-types
+      # validates the built declarations.
       - name: Build (tsc -b + tsdown bundles)
         run: pnpm run build
 
-      - name: Hygiene (knip + publint)
-        run: pnpm run knip && pnpm run publint
+      - name: Hygiene (knip + publint + constraints + NodeNext types)
+        run: pnpm run hygiene
 
       - name: Demo smoke test
         run: |

+ 4 - 2
AGENTS.md

@@ -145,7 +145,7 @@ pnpm run lint:fix       # eslint . --fix
 pnpm run build          # tsc emits lib/types, then tsdown bundles runtime lib/index.*
 pnpm run knip           # dead-code / unused-dependency check
 pnpm run publint        # package.json publish-correctness check (every packages/*/* package)
-pnpm run hygiene        # knip + publint + workspace constraints
+pnpm run hygiene        # knip + publint + workspace constraints + NodeNext type-consumer check
 pnpm run doc-typecheck  # typecheck every ```ts block in README.md, docs/**/*.md,
                     # packages/*/*.md + packages/*/*/*.md (doc/code drift gate)
 pnpm run gen-cordis-catalog  # regenerate docs/cordis-catalog/events-and-services.md
@@ -160,6 +160,8 @@ pnpm run verify-package-paths  # assert every packages/<path> cited in Markdown
 pnpm run verify-rfc-classification  # assert every RFC lives in a valid
                     # {lifecycle}/{class}/ folder and docs/rfc/README.md lists it
                     # under the matching heading (closed class set + index completeness)
+pnpm run verify-node-next-types  # assert built declarations typecheck for a
+                    # standard external NodeNext ESM TypeScript consumer
 pnpm run doc-sync       # doc-typecheck + verify-cordis-catalog + verify-md-wrap + verify-md-links + verify-doc-refs + verify-package-paths + verify-rfc-classification + verify-type-equiv (CI runs this)
 pnpm run demo:echo      # run examples/echo-agent (no API key; type "echo hi" to
                     # see a tool call) — the mock skeleton
@@ -188,7 +190,7 @@ Dev/test/demo run **unbuilt** via tsx + the source `paths` map in the root `tsco
 ## Conventions
 
 - **Package naming**: every npm package in this repo is `@deepseek-ai/dsh-<name>` (vendored packages keep their upstream names and are `private: true`).
-- **ESM everywhere** (`"type": "module"`); imports between workspace packages use package names, never relative paths across package boundaries. In-package relative imports are extensionless so generated `.d.ts` files stay extensionless; `lib/types/**/*.js` is a bundler-only intermediate, not a Node ESM entrypoint.
+- **ESM everywhere** (`"type": "module"`); imports between workspace packages use package names, never relative paths across package boundaries. In-package relative imports use explicit `.ts` extensions; `rewriteRelativeImportExtensions` turns those into `.js` in emitted JS, while declarations keep explicit `.ts` specifiers that NodeNext/Node16 TypeScript consumers can resolve to sibling `.d.ts` files. `lib/types/**/*.js` is a bundler-only intermediate, not a Node ESM entrypoint.
 - **`cordis` is a peerDependency** (+ devDependency) of every harness package, mirroring upstream convention.
 - **Registrations are effects**: anything a plugin contributes (adapter, tool, section, agent, event listener) goes through `ctx.effect()` / `ctx.on()` so disposal and HMR work. If you write a registry, `register()` must return the disposer.
 - **Typed events via declaration merging**: services declare their events in `declare module 'cordis' { interface Events { … } }`, and their ctx key in `interface Context`. Extensible unions use the merge-extensible-map pattern (see `ContentBlockMap`, `MessageSourceMap`).

+ 2 - 0
docs/cookbook/adding-a-package.md

@@ -17,6 +17,8 @@ packages/<name>/
 
 package.json invariants (enforced by `pnpm run constraints` / `scripts/check-workspace-constraints.ts`): `private: true`, `version: 0.0.1`, `type: module`, `main: "lib/index.js"`, `types: "lib/types/index.d.ts"`, `exports["."].types: "./lib/types/index.d.ts"`, `exports["."].default: "./lib/index.js"`, `cordis` in BOTH peerDependencies and devDependencies (same range). Mirror every dsh peer dependency in devDependencies. `schemastery` goes in `dependencies` (it is a runtime validator), matching agent-loop. The `files` list is precise: `lib/index.js`, `lib/types/**/*.d.ts`, `lib/types/**/*.d.ts.map`, and `src`; do not publish `lib/types` JS or JS-map intermediates or stale root declaration files. CLI app packages with a package `bin` include `lib/bin.js` immediately after `lib/index.js` in `files`.
 
+In-package relative imports use explicit `.ts` specifiers in source (for example, `export * from './types.ts'`). The compiler rewrites those to `.js` in emitted JS and leaves explicit `.ts` specifiers in declarations, which standard NodeNext/Node16 TypeScript consumers resolve to the sibling `.d.ts` files.
+
 ## 2. Register it in the root configs
 
 | File | Change |

+ 2 - 0
docs/cookbook/adding-a-vendored-package.md

@@ -29,6 +29,8 @@ vendor/<dir>/
 
 `package.json` invariants: `"private": true` (vendored packages are never published), keep upstream's `name`/`version`/`exports`/`type`, point declaration metadata at `lib/types`, publish `.d.ts` and `.d.ts.map` declaration outputs, and list its cordis deps in `peerDependencies` (matching the upstream manifest). Transitive upstream deps must themselves be vendored or already present — vendoring one package often means vendoring its dependency tree (e.g. `@cordisjs/plugin-http` pulls `@cordisjs/fetch-file`).
 
+Local relative imports/exports in vendored TypeScript source use explicit `.ts` specifiers after copying. This is a repo-local build-shape divergence from upstream: `rewriteRelativeImportExtensions` emits `.js` runtime imports while declarations keep explicit `.ts` specifiers that NodeNext/Node16 TypeScript consumers can resolve.
+
 ## 2. Register it in the root configs
 
 | File | Change |

+ 3 - 2
docs/development.md

@@ -39,7 +39,7 @@ If you are preparing to push from a fresh clone or worktree, also build once:
 pnpm run build
 ```
 
-`pnpm run hygiene` includes `publint`, which validates package entrypoints against the built `lib/*.js` files. A fresh worktree has no bundled JS until `pnpm run build` runs.
+`pnpm run hygiene` includes `publint`, which validates package entrypoints against the built `lib/*.js` files, and `verify-node-next-types`, which validates built declarations against a temporary NodeNext consumer. A fresh worktree has no bundled JS or declarations until `pnpm run build` runs.
 
 ## Environment variables
 
@@ -101,7 +101,8 @@ pnpm run doc-sync       # doc-typecheck, cordis-catalog freshness, markdown wrap
 pnpm run gen-module-graph     # regenerate docs/module-graph.md from package peerDeps
 pnpm run verify-module-graph  # fail if docs/module-graph.md is stale
 pnpm run build          # emit lib/types intermediates, then bundle lib/index.* runtime files
-pnpm run hygiene        # knip, publint, and workspace constraints
+pnpm run verify-node-next-types  # fail if built declarations are not NodeNext-consumable
+pnpm run hygiene        # knip, publint, workspace constraints, and NodeNext declaration check
 ```
 
 When changing package public behavior, update the relevant README or JSDoc in the same change. `pnpm run doc-sync` catches checked TypeScript snippets, cordis events/services catalog drift, and hard-wrapped markdown prose, but broader prose/API sync still needs review.

+ 1 - 1
docs/rfc/implemented/process/2026-06-11-quality-gates.md

@@ -15,7 +15,7 @@ Every AGENTS.md promise gets a command that exits non-zero, wired into git hooks
 - Max-strict TypeScript (`noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, …); examples, tests, and scripts typecheck in CI via the root no-emit `tsconfig.json` while package/vendor code stays behind its own project-reference boundary.
 - ESLint strict-type-checked + @stylistic (the house style, enforced); vendored code excluded.
 - Per-file 100% coverage on `packages/*/src` (v8); unreachable defensive guards carry `/* v8 ignore */ ` with stated reasons instead of deletion.
-- knip (dead code/deps), publint (package correctness), workspace constraints (workspace rules: private, cordis peer+dev, uniform version, ESM).
+- knip (dead code/deps), publint (package correctness), workspace constraints (workspace rules: private, cordis peer+dev, uniform version, ESM), and a NodeNext consumer typecheck for built package declarations.
 - lefthook pre-commit (lint staged, typecheck, vendor-manifest guard) and pre-push (tests, hygiene); CI runs the full matrix on node 24/26 plus a demo smoke test driving the echo-agent end to end.
 
 ## Consequences

+ 7 - 2
docs/rfc/implemented/process/2026-06-17-ts-build-config.md

@@ -17,7 +17,7 @@ Validation found several concrete technical issues and possible routes:
 
 - `tsdown` uses `oxc` to transform TypeScript, which is not the same behavior as `tsc`.
     - Bundled `.d.ts` emitted by `tsdown` conflicts with Cordis' internal relative module augmentation shape.
-    - The tsc output is affected by `allowImportingTsExtensions`, so we need to ensure that generated `.js` files do not import `.ts` files and generated `.d.ts` files do not import `.js` files. Therefore, we need to adjust the import specifiers to extensionless in the TypeScript source.
+    - The tsc output is affected by `allowImportingTsExtensions`, so we need to ensure that generated `.js` files do not import `.ts` files and generated `.d.ts` files do not contain extensionless relative imports. Therefore, in-package relative imports use explicit `.ts` specifiers in TypeScript source and `rewriteRelativeImportExtensions` rewrites those specifiers to `.js` in emitted JS.
     - Bundled `.js` emitted by `tsdown` is not the same behavior as per-file `.js` emitted by `tsc -b`, such as decorator transform behavior.
 - `vendor/*/src`, examples, tests, and scripts cannot all be plain-included in one root strict program.
     - Directly typechecking `vendor/*/src` under the root strict config triggers many type errors outside this project's ownership.
@@ -26,7 +26,7 @@ Validation found several concrete technical issues and possible routes:
 
 ## Decision
 
-In-package relative imports are extensionless.
+In-package relative imports use explicit `.ts` specifiers.
 
 `pnpm run build` is a two-stage build:
 
@@ -47,6 +47,9 @@ pnpm run build:
 tsc -b tsconfig.build.json
 tsdown
 
+pnpm run verify-node-next-types:
+tsx scripts/verify-node-next-types.ts
+
 pnpm run typecheck:
 tsc -b tsconfig.json
 ```
@@ -60,8 +63,10 @@ Build responsibilities are clearer:
 - Each module under `packages/<group>/<pkg>` and `vendor/*` has one local tsconfig for build, typecheck, and tools that run source directly, such as `tsx` and `vitest`.
 - The `build` command uses `tsconfig.build.json`. `tsc -b` owns the publishable per-module `.js` and `.d.ts` output, and the bundler owns only `lib/index.*`.
     - `lib/types/*.d.ts` and `.d.ts.map` are the publish declaration output.
+    - `lib/types/*.d.ts` uses explicit `.ts` relative specifiers, which TypeScript's NodeNext/Node16 resolver maps to sibling `.d.ts` files.
     - `lib/types/*.js` is only a bundler input and must not be used as a runtime entry or public import target.
     - `lib/index.*` is the publish runtime output and is generated by the bundler, currently `tsdown`.
+- `pnpm run verify-node-next-types` scans built declarations for extensionless relative specifiers, then typechecks a temporary external ESM consumer with `moduleResolution: "NodeNext"` against the built `types`/`exports` surface, so declaration specifier regressions fail before publish.
 - The `typecheck` command uses `tsconfig.json`. Examples, tests, and scripts are checked by the root no-emit project, while packages and vendor modules keep the same emit behavior as `build`. Package and vendor source stays behind project-reference boundaries.
 
 The Cordis vendor copy now has one more type-structure divergence from upstream. During upstream sync, that divergence must be reapplied or explicitly retired.

+ 2 - 1
package.json

@@ -31,13 +31,14 @@
     "verify-package-paths": "tsx scripts/verify-package-paths.ts",
     "verify-rfc-classification": "tsx scripts/verify-rfc-classification.ts",
     "verify-type-equiv": "tsx scripts/verify-type-equiv.ts",
+    "verify-node-next-types": "tsx scripts/verify-node-next-types.ts",
     "gen-cordis-catalog": "tsx scripts/gen-cordis-catalog.ts",
     "verify-cordis-catalog": "tsx scripts/gen-cordis-catalog.ts --check",
     "gen-module-graph": "tsx scripts/gen-module-graph.ts",
     "verify-module-graph": "tsx scripts/gen-module-graph.ts --check",
     "constraints": "tsx scripts/check-workspace-constraints.ts",
     "doc-sync": "pnpm run doc-typecheck && pnpm run verify-cordis-catalog && pnpm run verify-md-wrap && pnpm run verify-md-links && pnpm run verify-doc-refs && pnpm run verify-package-paths && pnpm run verify-rfc-classification && pnpm run verify-type-equiv",
-    "hygiene": "pnpm run knip && pnpm run publint && pnpm run constraints",
+    "hygiene": "pnpm run knip && pnpm run publint && pnpm run constraints && pnpm run verify-node-next-types",
     "demo:echo": "node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/echo-agent/cordis.yml",
     "demo:coding": "node --expose-internals --import tsx packages/ui/stdio-agent/src/bin.ts examples/coding-agent/cordis.yml",
     "demo:acp": "node --import tsx packages/ui/acp-agent/src/bin.ts examples/acp-agent/cordis.yml",

+ 1 - 1
packages/README.md

@@ -79,5 +79,5 @@ Each package has its own `README.md` with purpose, service API, events, extensio
 - **Declaration merging for events and ctx**: services declare their events in `declare module 'cordis' { interface Events { ... } }` and their ctx key in `interface Context`.
 - **Waterfall semantics**: `ctx.waterfall` listeners receive `(...args, next)` and MUST call `next()` to delegate; returning without it short-circuits (the veto mechanism).
 - **Extensible unions**: `ContentBlockMap`, `MessageSourceMap`, `FinishReasonMap`, `TurnTriggerMap`, `TurnEndReasonMap`, and `SessionEventMap` use the merge-extensible-map pattern so plugins can add variants via declaration merging.
-- **ESM everywhere**; imports use package names across package boundaries and extensionless relative specifiers within a package.
+- **ESM everywhere**; imports use package names across package boundaries and explicit `.ts` relative specifiers within a package.
 - **Tests**: vitest, colocated under `packages/<group>/<pkg>/tests/*.spec.ts`. Every registry needs an HMR-safety test. Err on the side of more tests.

+ 4 - 4
packages/bash/bash-local/src/index.ts

@@ -17,11 +17,11 @@ import { Context } from 'cordis'
 import z from 'schemastery'
 import { BashExecutor, BashTaskId } from '@deepseek-ai/dsh-bash'
 import type { BashExecRequest, BashExecSpec, BashRunResult, BashTask, BashTaskRead, OwnerToken } from '@deepseek-ai/dsh-bash'
-import { runBash } from './run'
-import type { RunInternals, RunningBash } from './run'
+import { runBash } from './run.ts'
+import type { RunInternals, RunningBash } from './run.ts'
 
-export { DEFAULT_GRACE_MS, ENV_OVERRIDES, killGroup, OutputCollector, runBash } from './run'
-export type { RunInternals, RunningBash, SpawnOutcome, SpawnSpec } from './run'
+export { DEFAULT_GRACE_MS, ENV_OVERRIDES, killGroup, OutputCollector, runBash } from './run.ts'
+export type { RunInternals, RunningBash, SpawnOutcome, SpawnSpec } from './run.ts'
 
 /** Plugin config (all optional — `static Config` supplies the defaults). */
 export interface Config {

+ 3 - 3
packages/bash/bash/src/index.ts

@@ -15,9 +15,9 @@
  */
 
 import { Context, Service } from 'cordis'
-import type { BashExecRequest, BashExecSpec, BashRunResult, BashTask, BashTaskId, BashTaskListener, BashTaskRead, OwnerToken } from './types'
+import type { BashExecRequest, BashExecSpec, BashRunResult, BashTask, BashTaskId, BashTaskListener, BashTaskRead, OwnerToken } from './types.ts'
 
-export { BashTaskId, OwnerToken } from './types'
+export { BashTaskId, OwnerToken } from './types.ts'
 export type {
   BashExecRequest,
   BashExecSpec,
@@ -27,7 +27,7 @@ export type {
   BashTaskRead,
   BashTaskStatus,
   CollectedOutput,
-} from './types'
+} from './types.ts'
 
 declare module 'cordis' {
   interface Context {

+ 2 - 2
packages/core/agent-loop/src/agent.ts

@@ -11,8 +11,8 @@ import type { AgentId, AgentOptions, AgentStatus, SendOptions } from '@deepseek-
 import type { Agent } from '@deepseek-ai/dsh-agent'
 import type { ContentBlock, MessageSource } from '@deepseek-ai/dsh-llm'
 import type { Session } from '@deepseek-ai/dsh-session'
-import { Inbox } from './inbox'
-import { isTurnOpen, lastTurnNumber, runLoop } from './loop'
+import { Inbox } from './inbox.ts'
+import { isTurnOpen, lastTurnNumber, runLoop } from './loop.ts'
 
 /**
  * The concrete {@link Agent} implementation owned by the agent-loop plugin.

+ 4 - 4
packages/core/agent-loop/src/index.ts

@@ -17,11 +17,11 @@ import type { Session } from '@deepseek-ai/dsh-session'
 import type {} from '@deepseek-ai/dsh-system-prompt'
 import type {} from '@deepseek-ai/dsh-tools'
 import type { SessionPersistence } from '@deepseek-ai/dsh-session-persistence'
-import { ReactLoopAgent } from './agent'
+import { ReactLoopAgent } from './agent.ts'
 
-export { ReactLoopAgent } from './agent'
-export { Inbox, type InboxMessage } from './inbox'
-export { runLoop } from './loop'
+export { ReactLoopAgent } from './agent.ts'
+export { Inbox, type InboxMessage } from './inbox.ts'
+export { runLoop } from './loop.ts'
 
 declare module 'cordis' {
   interface Context {

+ 1 - 1
packages/core/agent-loop/src/loop.ts

@@ -13,7 +13,7 @@ import { BlockAssembler, HarnessError } from '@deepseek-ai/dsh-llm'
 import type { Session, TurnEndReason, TurnTrigger } from '@deepseek-ai/dsh-session'
 import { renderPrompt } from '@deepseek-ai/dsh-system-prompt'
 import type {} from '@deepseek-ai/dsh-tools'
-import type { ReactLoopAgent } from './agent'
+import type { ReactLoopAgent } from './agent.ts'
 
 /** An Error with an optional machine-readable code (e.g., from LlmError or a throwing plugin). */
 type CodedError = Error & { code?: string }

+ 2 - 2
packages/core/agent/src/index.ts

@@ -7,9 +7,9 @@
 
 import { Context, Service } from 'cordis'
 import type { SessionId } from '@deepseek-ai/dsh-session'
-import type { Agent, AgentId, AgentOptions } from './types'
+import type { Agent, AgentId, AgentOptions } from './types.ts'
 
-export * from './types'
+export * from './types.ts'
 
 declare module 'cordis' {
   interface Context {

+ 6 - 6
packages/core/session/src/index.ts

@@ -9,13 +9,13 @@
 import { Context, Service } from 'cordis'
 import { isAbsolute } from 'node:path'
 import type { ContentBlock, Message, MessageSource } from '@deepseek-ai/dsh-llm'
-import { SESSION_FORMAT_VERSION, SessionId } from './types'
-import type { CreateSessionOptions, SessionEvent, SessionEventMap, SessionEventType, SessionHeader } from './types'
-import { isJsonValue } from './json'
+import { SESSION_FORMAT_VERSION, SessionId } from './types.ts'
+import type { CreateSessionOptions, SessionEvent, SessionEventMap, SessionEventType, SessionHeader } from './types.ts'
+import { isJsonValue } from './json.ts'
 
-export * from './types'
-export { isJsonValue } from './json'
-export { interruptedTurnClosers } from './repair'
+export * from './types.ts'
+export { isJsonValue } from './json.ts'
+export { interruptedTurnClosers } from './repair.ts'
 
 declare module 'cordis' {
   interface Context {

+ 1 - 1
packages/core/session/src/repair.ts

@@ -36,7 +36,7 @@
  */
 
 import type { CallId } from '@deepseek-ai/dsh-llm'
-import type { SessionEvent } from './types'
+import type { SessionEvent } from './types.ts'
 
 /**
  * Scan `events` for an open turn/step at the tail and return the synthetic

+ 1 - 1
packages/core/tools/src/index.ts

@@ -24,7 +24,7 @@ export {
   type InferArgs,
   type DefineToolOptions,
   type JsonSchemaObject,
-} from './schema'
+} from './schema.ts'
 
 declare module 'cordis' {
   interface Context {

+ 1 - 1
packages/core/tools/src/schema.ts

@@ -21,7 +21,7 @@
 
 import type { ContentBlock } from '@deepseek-ai/dsh-llm'
 import { assertNever, HarnessError } from '@deepseek-ai/dsh-llm'
-import type { ToolCallPresentation, ToolDefinition, ToolExecution, ToolResult, ToolResultPresentation } from './index'
+import type { ToolCallPresentation, ToolDefinition, ToolExecution, ToolResult, ToolResultPresentation } from './index.ts'
 
 // ---------------------------------------------------------------------------
 // SchemaSpec — the author-facing per-property type

+ 5 - 5
packages/llm/llm-deepseek/src/adapter.ts

@@ -7,11 +7,11 @@
 
 import { LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm'
 import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
-import { serializeRequest } from './serialize'
-import type { RequestDefaults } from './serialize'
-import { parseSse } from './sse'
-import { translate } from './translate'
-import type { WireError } from './types'
+import { serializeRequest } from './serialize.ts'
+import type { RequestDefaults } from './serialize.ts'
+import { parseSse } from './sse.ts'
+import { translate } from './translate.ts'
+import type { WireError } from './types.ts'
 
 export interface DeepSeekAdapterOptions {
   apiKey: string

+ 8 - 8
packages/llm/llm-deepseek/src/index.ts

@@ -21,15 +21,15 @@
 import type { Context } from 'cordis'
 import z from 'schemastery'
 import type {} from '@deepseek-ai/dsh-llm'
-import { DeepSeekAdapter } from './adapter'
+import { DeepSeekAdapter } from './adapter.ts'
 
-export { DeepSeekAdapter, httpErrorCode } from './adapter'
-export type { DeepSeekAdapterOptions } from './adapter'
-export { serializeMessages, serializeRequest } from './serialize'
-export type { RequestDefaults } from './serialize'
-export { DONE, parseSse } from './sse'
-export { mapFinishReason, mapUsage, translate } from './translate'
-export type * from './types'
+export { DeepSeekAdapter, httpErrorCode } from './adapter.ts'
+export type { DeepSeekAdapterOptions } from './adapter.ts'
+export { serializeMessages, serializeRequest } from './serialize.ts'
+export type { RequestDefaults } from './serialize.ts'
+export { DONE, parseSse } from './sse.ts'
+export { mapFinishReason, mapUsage, translate } from './translate.ts'
+export type * from './types.ts'
 
 export const name = 'llm-deepseek'
 export const inject = ['llm']

+ 1 - 1
packages/llm/llm-deepseek/src/serialize.ts

@@ -18,7 +18,7 @@
 
 import { LlmError } from '@deepseek-ai/dsh-llm'
 import type { ContentBlock, GenerateOptions, Message } from '@deepseek-ai/dsh-llm'
-import type { WireMessage, WireRequest, WireTool } from './types'
+import type { WireMessage, WireRequest, WireTool } from './types.ts'
 
 /** Adapter-level request defaults (from plugin config). */
 export interface RequestDefaults {

+ 2 - 2
packages/llm/llm-deepseek/src/translate.ts

@@ -16,8 +16,8 @@
 
 import { CallId, LlmError } from '@deepseek-ai/dsh-llm'
 import type { ContentBlock, FinishReason, StreamChunk, TokenUsage } from '@deepseek-ai/dsh-llm'
-import { DONE } from './sse'
-import type { WireChunk, WireUsage } from './types'
+import { DONE } from './sse.ts'
+import type { WireChunk, WireUsage } from './types.ts'
 
 /** One open block under assembly. */
 interface OpenBlock {

+ 1 - 1
packages/llm/llm-pi-ai/src/adapter.ts

@@ -16,7 +16,7 @@ import type { Model } from '@earendil-works/pi-ai'
 import { LlmAdapter, LlmError } from '@deepseek-ai/dsh-llm'
 import { CallId } from '@deepseek-ai/dsh-llm'
 import type { GenerateOptions, StreamChunk, ToolSchema } from '@deepseek-ai/dsh-llm'
-import { toPiContext, toStreamChunks } from './convert'
+import { toPiContext, toStreamChunks } from './convert.ts'
 
 /** Reasoning levels surfaced by this adapter (DeepSeek wire: high|max). */
 export type PiAiReasoning = 'off' | 'high' | 'xhigh'

+ 5 - 5
packages/llm/llm-pi-ai/src/index.ts

@@ -19,12 +19,12 @@
 import type { Context } from 'cordis'
 import z from 'schemastery'
 import type {} from '@deepseek-ai/dsh-llm'
-import { PiAiAdapter } from './adapter'
-import type { PiAiReasoning } from './adapter'
+import { PiAiAdapter } from './adapter.ts'
+import type { PiAiReasoning } from './adapter.ts'
 
-export { buildModel, PiAiAdapter } from './adapter'
-export type { PiAiAdapterOptions, PiAiReasoning } from './adapter'
-export { mapStopReason, mapUsage, toPiContext, toStreamChunks } from './convert'
+export { buildModel, PiAiAdapter } from './adapter.ts'
+export type { PiAiAdapterOptions, PiAiReasoning } from './adapter.ts'
+export { mapStopReason, mapUsage, toPiContext, toStreamChunks } from './convert.ts'
 
 export const name = 'llm-pi-ai'
 export const inject = ['llm']

+ 3 - 3
packages/llm/llm/src/assembler.ts

@@ -6,9 +6,9 @@
  * @module @deepseek-ai/dsh-llm/assembler
  */
 
-import { CallId } from './brand'
-import { assertNever } from './never'
-import type { ContentBlock, FinishReason, Message, StreamChunk, TokenUsage } from './types'
+import { CallId } from './brand.ts'
+import { assertNever } from './never.ts'
+import type { ContentBlock, FinishReason, Message, StreamChunk, TokenUsage } from './types.ts'
 
 interface PartialBlock {
   blockType: string

+ 7 - 7
packages/llm/llm/src/index.ts

@@ -7,14 +7,14 @@
  */
 
 import { Context, Service } from 'cordis'
-import type { GenerateOptions, StreamChunk } from './types'
-import { HarnessError } from './error'
+import type { GenerateOptions, StreamChunk } from './types.ts'
+import { HarnessError } from './error.ts'
 
-export * from './brand'
-export * from './never'
-export * from './error'
-export * from './types'
-export { BlockAssembler } from './assembler'
+export * from './brand.ts'
+export * from './never.ts'
+export * from './error.ts'
+export * from './types.ts'
+export { BlockAssembler } from './assembler.ts'
 
 declare module 'cordis' {
   interface Context {

+ 1 - 1
packages/llm/llm/src/types.ts

@@ -19,7 +19,7 @@
  * ```
  */
 
-import type { CallId } from './brand'
+import type { CallId } from './brand.ts'
 
 /** Cache hint attached to a content block (provider-interpreted). */
 export type CacheHint = 'ephemeral'

+ 1 - 1
packages/session-persistence/session-persistence-jsonl/src/index.ts

@@ -29,7 +29,7 @@ import {
 import type { Session, SessionEvent, SessionId, SessionHeader } from '@deepseek-ai/dsh-session'
 import {
   encodeSegment, eventLine, logPath, parseHeaderMeta, scanLog, sessionDir, toHeaderLine,
-} from './format'
+} from './format.ts'
 
 export interface Config {
   /**

+ 2 - 2
packages/session-persistence/session-persistence-sqlite/src/index.ts

@@ -29,9 +29,9 @@ import {
 import type { Session, SessionEvent, SessionId, SessionHeader } from '@deepseek-ai/dsh-session'
 import {
   openDatabase, rowToMeta, scanRows, type EventRow, type SessionRow,
-} from './schema'
+} from './schema.ts'
 
-export { SCHEMA_VERSION } from './schema'
+export { SCHEMA_VERSION } from './schema.ts'
 
 /** Plugin configuration. */
 export interface Config {

+ 1 - 1
packages/session-persistence/session-persistence/src/coordinator.ts

@@ -27,7 +27,7 @@
 import { Context } from 'cordis'
 import { interruptedTurnClosers, SESSION_FORMAT_VERSION } from '@deepseek-ai/dsh-session'
 import type { Session, SessionEvent, SessionId, SessionHeader } from '@deepseek-ai/dsh-session'
-import { assertSerializable, seedCoversPrefix } from './index'
+import { assertSerializable, seedCoversPrefix } from './index.ts'
 
 /**
  * A stored session's durable prefix as read back from a backend: its

+ 2 - 2
packages/session-persistence/session-persistence/src/index.ts

@@ -29,8 +29,8 @@ import type { SessionEvent, SessionId, SessionHeader } from '@deepseek-ai/dsh-se
 export type { SessionHeader } from '@deepseek-ai/dsh-session'
 
 // The backend-agnostic write-path orchestration first-party backends compose.
-export { PersistenceCoordinator } from './coordinator'
-export type { PersistenceBackend, StoredPrefix } from './coordinator'
+export { PersistenceCoordinator } from './coordinator.ts'
+export type { PersistenceBackend, StoredPrefix } from './coordinator.ts'
 
 declare module 'cordis' {
   interface Context {

+ 1 - 1
packages/ui/acp/src/index.ts

@@ -74,7 +74,7 @@ import {
   harnessBlockToAcpContent,
   promptHasUnsupportedContent,
   turnEndToStopReason,
-} from './codec'
+} from './codec.ts'
 
 export const name = 'acp'
 // The bridge programs against the interface packages only (architecture rule:

+ 160 - 0
scripts/verify-node-next-types.ts

@@ -0,0 +1,160 @@
+/**
+ * Verify that built package declarations are consumable by a standard external
+ * TypeScript ESM project using NodeNext resolution.
+ *
+ * Run after `pnpm run build` has emitted declaration files under package
+ * `lib/types` directories.
+ */
+
+import { execFileSync } from 'node:child_process'
+import { existsSync, globSync, mkdirSync, mkdtempSync, readFileSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
+import { dirname, resolve } from 'node:path'
+
+const root = resolve(import.meta.dirname, '..')
+
+interface ExportTarget {
+  types?: string
+}
+
+interface PackageManifest {
+  name?: string
+  types?: string
+  exports?: Record<string, ExportTarget | string | null>
+}
+
+interface WorkspacePackage {
+  dir: string
+  name: string
+  manifest: PackageManifest
+}
+
+function readPackage(path: string): WorkspacePackage | null {
+  const manifest = JSON.parse(readFileSync(path, 'utf8')) as PackageManifest
+  if (!manifest.name) return null
+  return { dir: dirname(path), name: manifest.name, manifest }
+}
+
+function workspacePackages(): WorkspacePackage[] {
+  return [
+    ...globSync('vendor/*/package.json', { cwd: root }),
+    ...globSync('packages/*/*/package.json', { cwd: root }),
+  ]
+    .map(path => readPackage(resolve(root, path)))
+    .filter(pkg => pkg !== null)
+    .sort((a, b) => a.name.localeCompare(b.name))
+}
+
+const declarationSpecifierPattern = /(?:from\s*|import\s*\(\s*|import\s+|declare\s+module\s*)["'](\.{0,2}(?:\/[^"']*)?)["']/g
+const hasExtension = /\.[^/.]+$/
+
+function extensionlessRelativeSpecifiers(): string[] {
+  const errors: string[] = []
+  const files = [
+    ...globSync('vendor/*/lib/types/**/*.d.ts', { cwd: root }),
+    ...globSync('packages/*/*/lib/types/**/*.d.ts', { cwd: root }),
+  ].sort()
+
+  for (const file of files) {
+    const text = readFileSync(resolve(root, file), 'utf8')
+    for (const match of text.matchAll(declarationSpecifierPattern)) {
+      const specifier = match[1]
+      if (!specifier) continue
+      const isRelative = specifier === '.' || specifier.startsWith('./') || specifier.startsWith('../')
+      if (isRelative && !hasExtension.test(specifier)) errors.push(`${file}: ${specifier}`)
+    }
+  }
+
+  return errors
+}
+
+function publicSpecifiers(pkg: WorkspacePackage): string[] {
+  const specifiers = new Set<string>()
+  if (pkg.manifest.types) specifiers.add(pkg.name)
+
+  for (const [key, target] of Object.entries(pkg.manifest.exports ?? {})) {
+    if (key.includes('*') || key === './package.json') continue
+    if (typeof target !== 'object' || target === null || !target.types) continue
+    specifiers.add(key === '.' ? pkg.name : `${pkg.name}/${key.slice(2)}`)
+  }
+
+  return [...specifiers].sort()
+}
+
+function linkPackage(pkg: WorkspacePackage, nodeModules: string): void {
+  const parts = pkg.name.split('/')
+  const link = resolve(nodeModules, ...parts)
+  mkdirSync(dirname(link), { recursive: true })
+  symlinkSync(pkg.dir, link, 'dir')
+}
+
+const packages = workspacePackages()
+const badSpecifiers = extensionlessRelativeSpecifiers()
+if (badSpecifiers.length > 0) {
+  console.error('verify-node-next-types: declaration files still contain extensionless relative specifiers.')
+  console.error(badSpecifiers.join('\n'))
+  process.exit(1)
+}
+
+const missingOutputs = packages
+  .filter(pkg => pkg.manifest.types && !existsSync(resolve(pkg.dir, pkg.manifest.types)))
+  .map(pkg => `${pkg.name}: missing ${pkg.manifest.types}`)
+
+if (missingOutputs.length > 0) {
+  console.error('verify-node-next-types: build outputs are missing; run `pnpm run build` first.')
+  console.error(missingOutputs.join('\n'))
+  process.exit(1)
+}
+
+const tmp = mkdtempSync(resolve(root, '.node-next-types-'))
+let failed = false
+
+try {
+  const nodeModules = resolve(tmp, 'node_modules')
+  mkdirSync(nodeModules, { recursive: true })
+  for (const pkg of packages) linkPackage(pkg, nodeModules)
+
+  const rootTypes = resolve(root, 'node_modules/@types/node')
+  if (existsSync(rootTypes)) {
+    const typesDir = resolve(nodeModules, '@types')
+    mkdirSync(typesDir, { recursive: true })
+    symlinkSync(rootTypes, resolve(typesDir, 'node'), 'dir')
+  }
+
+  writeFileSync(resolve(tmp, 'package.json'), `${JSON.stringify({ type: 'module', private: true }, null, 2)}\n`)
+  writeFileSync(resolve(tmp, 'tsconfig.json'), `${JSON.stringify({
+    compilerOptions: {
+      target: 'es2024',
+      module: 'NodeNext',
+      moduleResolution: 'NodeNext',
+      strict: true,
+      // Third-party SDK declarations can have their own lib-check noise under a
+      // symlinked temp install. The explicit scan above owns our regression:
+      // extensionless relative specifiers in built declarations.
+      skipLibCheck: true,
+      preserveSymlinks: true,
+      noEmit: true,
+      types: ['node'],
+    },
+    include: ['index.ts'],
+  }, null, 2)}\n`)
+
+  const imports = packages.flatMap(publicSpecifiers)
+    .map((specifier, index) => `import * as mod${index} from ${JSON.stringify(specifier)};\nvoid mod${index};`)
+    .join('\n')
+  writeFileSync(resolve(tmp, 'index.ts'), `${imports}\n`)
+
+  execFileSync(resolve(root, 'node_modules/.bin/tsc'), ['-p', resolve(tmp, 'tsconfig.json'), '--pretty', 'false'], {
+    cwd: root,
+    stdio: 'pipe',
+  })
+  console.log(`verify-node-next-types: ${packages.length} workspace package declaration surface(s) compile under NodeNext.`)
+} catch (error: unknown) {
+  failed = true
+  const output = error as { stdout?: Buffer; stderr?: Buffer }
+  console.error('verify-node-next-types: NodeNext consumer typecheck failed.\n')
+  console.error(`${output.stdout?.toString() ?? ''}${output.stderr?.toString() ?? ''}`)
+} finally {
+  rmSync(tmp, { recursive: true, force: true })
+}
+
+if (failed) process.exit(1)

+ 2 - 0
tsconfig.base.json

@@ -10,6 +10,8 @@
     "incremental": true,
     "skipLibCheck": true,
     "esModuleInterop": true,
+    "allowImportingTsExtensions": true,
+    "rewriteRelativeImportExtensions": true,
     "verbatimModuleSyntax": false,
     "strict": true,
     "noUncheckedIndexedAccess": true,

+ 1 - 1
vendor/README.md

@@ -33,7 +33,7 @@ Keep this log exhaustive — every divergence from upstream must be listed.
 1. **`hmr/src/index.ts`**: removed the `./locales/en-US.yml` / `./locales/zh-CN.yml` imports, the `.i18n({...})` call on the `Config` schema, and the `src/locales/` directory. Rationale: those imports require a runtime YAML loader hook (`@cordisjs/unyaml`) that we do not vendor; the i18n texts only localize config descriptions.
 2. **All `package.json` files**: regenerated — added `private: true`, added precise `files` entries for bundled runtime files and `lib/types/**/*.d.ts` / `.d.ts.map`, preserved `src` in `files` only for packages whose previous file list already shipped it, added a `./src/*` export where missing, pointed declaration metadata at `lib/types`, and removed upstream `devDependencies`/`scripts`/`repository` fields. Dependency and peer-dependency ranges preserved, except `hmr` declares `esbuild` as a direct dev dependency because its source imports the `BuildFailure` type and pnpm's strict workspace resolution requires the owner package to name that dependency.
 3. **All `tsconfig.json` files**: regenerated to extend the repo-root `tsconfig.base.json`, emit TypeScript intermediates to `lib/types`, and declare project references.
-4. **Vendored TypeScript source internal specifiers**: changed local relative imports/exports from explicit `.ts` / `.js` specifiers to extensionless specifiers so generated `.js` and `.d.ts` intermediates are extensionless and no declaration postprocess is needed. This includes `loader/src/config/isolate.ts` changing `declare module './entry.ts'` to `declare module './entry'`.
+4. **Vendored TypeScript source internal specifiers**: changed local relative imports/exports from upstream's specifier shape to explicit `.ts` specifiers so TypeScript rewrites emitted JS to `.js` while declarations keep explicit, NodeNext-safe `.ts` specifiers. This includes `loader/src/config/isolate.ts` using `declare module './entry.ts'`.
 5. **`schemastery/tsdown.config.ts` and `logger-console/tsdown.config.ts`**: ours, not upstream files — per-package build-shape overrides (dual ESM+CJS output; separate node/browser entries) for the repo-root tsdown build. They read the JS emitted under `lib/types` and then write the publish runtime entries under `lib/`. Like the regenerated tsconfigs, they are not part of the upstream sync surface.
 
 ## Sync procedure

+ 6 - 6
vendor/cordis/src/context.ts

@@ -1,10 +1,10 @@
 import { Dict } from 'cosmokit'
-import { EventsService } from './events'
-import { LoggerService } from './logger'
-import { ReflectService } from './reflect'
-import { InjectKey, RegistryService } from './registry'
-import { getTraceable, symbols } from './utils'
-import { Fiber } from './fiber'
+import { EventsService } from './events.ts'
+import { LoggerService } from './logger.ts'
+import { ReflectService } from './reflect.ts'
+import { InjectKey, RegistryService } from './registry.ts'
+import { getTraceable, symbols } from './utils.ts'
+import { Fiber } from './fiber.ts'
 
 /**
  * Public shape of a Cordis context.

+ 4 - 4
vendor/cordis/src/events.ts

@@ -1,7 +1,7 @@
 import { defineProperty, Promisify } from 'cosmokit'
-import { Context } from './context'
-import { Fiber, FiberState } from './fiber'
-import { DisposableList, symbols } from './utils'
+import { Context } from './context.ts'
+import { Fiber, FiberState } from './fiber.ts'
+import { DisposableList, symbols } from './utils.ts'
 
 /** Return whether an event result should stop a bail-style dispatch. */
 export function isBailed(value: any) {
@@ -25,7 +25,7 @@ export type ThisType<F> = F extends (this: infer T, ...args: any) => any ? T : n
  */
 export type DispatchMode = 'emit' | 'parallel' | 'serial' | 'bail' | 'waterfall'
 
-declare module './context' {
+declare module './context.ts' {
   export interface Context {
     /* eslint-disable max-len */
     parallel<K extends keyof Events>(name: K, ...args: Parameters<Events[K]>): Promise<void>

+ 5 - 5
vendor/cordis/src/fiber.ts

@@ -1,11 +1,11 @@
 import { Awaitable, defineProperty, Dict, isNullable } from 'cosmokit'
-import { Context } from './context'
-import { Plugin } from './registry'
-import { buildOuterStack, composeError, DisposableList, getTraceable, isConstructor, isObject, symbols } from './utils'
-import { Impl } from './reflect'
+import { Context } from './context.ts'
+import { Plugin } from './registry.ts'
+import { buildOuterStack, composeError, DisposableList, getTraceable, isConstructor, isObject, symbols } from './utils.ts'
+import { Impl } from './reflect.ts'
 import { StandardSchemaV1 } from '@standard-schema/spec'
 
-declare module './context' {
+declare module './context.ts' {
   export interface Context extends Pick<Fiber, 'effect'> {
     fiber: Fiber
   }

+ 7 - 7
vendor/cordis/src/index.ts

@@ -1,14 +1,14 @@
 /** Core context type and root context implementation. */
-export * from './context'
+export * from './context.ts'
 /** Event bus, dispatch modes, and event augmentation types. */
-export * from './events'
+export * from './events.ts'
 /** Plugin fiber lifecycle, effects, and config validation helpers. */
-export * from './fiber'
+export * from './fiber.ts'
 /** Logger facade, logger service, message, exporter, and formatting types. */
-export * from './logger'
+export * from './logger.ts'
 /** Plugin registry, dependency injection, and plugin entrypoint types. */
-export * from './registry'
+export * from './registry.ts'
 /** Base service class and service lifecycle symbols. */
-export * from './service'
+export * from './service.ts'
 /** Shared internal helpers used by context, services, and plugin fibers. */
-export * from './utils'
+export * from './utils.ts'

+ 4 - 4
vendor/cordis/src/logger.ts

@@ -1,9 +1,9 @@
 import { defineProperty, hyphenate } from 'cosmokit'
-import { Context } from './context'
-import { Fiber } from './fiber'
-import { createCallable, joinPrototype, symbols, Tracker } from './utils'
+import { Context } from './context.ts'
+import { Fiber } from './fiber.ts'
+import { createCallable, joinPrototype, symbols, Tracker } from './utils.ts'
 
-declare module './context' {
+declare module './context.ts' {
   interface Intercept {
     logger: LoggerService.Intercept
   }

+ 4 - 4
vendor/cordis/src/reflect.ts

@@ -1,9 +1,9 @@
 import { defineProperty, Dict, isNullable } from 'cosmokit'
-import { Context } from './context'
-import { getTraceable, symbols, withProps } from './utils'
-import { Fiber, FiberState } from './fiber'
+import { Context } from './context.ts'
+import { getTraceable, symbols, withProps } from './utils.ts'
+import { Fiber, FiberState } from './fiber.ts'
 
-declare module './context' {
+declare module './context.ts' {
   interface Context {
     get<K extends string & keyof this>(name: K, strict?: boolean): undefined | this[K]
     get(name: string, strict?: boolean): any

+ 4 - 4
vendor/cordis/src/registry.ts

@@ -1,8 +1,8 @@
 import { defineProperty, Dict } from 'cosmokit'
 import { StandardSchemaV1 } from '@standard-schema/spec'
-import { Context } from './context'
-import { Fiber } from './fiber'
-import { buildOuterStack, DisposableList, symbols, withProps } from './utils'
+import { Context } from './context.ts'
+import { Fiber } from './fiber.ts'
+import { buildOuterStack, DisposableList, symbols, withProps } from './utils.ts'
 
 function isApplicable(object: Plugin) {
   return object && typeof object === 'object' && typeof object.apply === 'function'
@@ -140,7 +140,7 @@ type GetPluginConfig<P> =
   ? S
   : GetPluginParameters<P>[0]
 
-declare module './context' {
+declare module './context.ts' {
   export interface Context {
     inject(deps: Inject, callback: Plugin.Function<void>): Fiber & PromiseLike<Fiber>
     plugin<P extends Plugin>(plugin: P, ...args: Spread<GetPluginConfig<P>>): Fiber & PromiseLike<Fiber>

+ 2 - 2
vendor/cordis/src/service.ts

@@ -1,6 +1,6 @@
 import { defineProperty } from 'cosmokit'
-import { Context } from './context'
-import { createCallable, joinPrototype, symbols, Tracker } from './utils'
+import { Context } from './context.ts'
+import { createCallable, joinPrototype, symbols, Tracker } from './utils.ts'
 
 /**
  * Base class for services that expose a named API on `ctx`.

+ 1 - 1
vendor/cordis/src/utils.ts

@@ -1,5 +1,5 @@
 import { defineProperty } from 'cosmokit'
-import type { Context, Service } from '.'
+import type { Context, Service } from './index.ts'
 
 /** Ordered collection of disposable values with O(1) deletion by value. */
 export class DisposableList<T extends WeakKey> {

+ 1 - 1
vendor/cosmokit/src/array.ts

@@ -1,4 +1,4 @@
-import { isNullable } from './misc'
+import { isNullable } from './misc.ts'
 
 /** Return true when every item in `array2` is present in `array1`. */
 export function contain(array1: readonly any[], array2: readonly any[]) {

+ 5 - 5
vendor/cosmokit/src/index.ts

@@ -1,10 +1,10 @@
 /** Array set and normalization helpers. */
-export * from './array'
+export * from './array.ts'
 /** Runtime type, binary, clone, and equality helpers. */
-export * from './types'
+export * from './types.ts'
 /** Shared utility types and object helpers. */
-export * from './misc'
+export * from './misc.ts'
 /** String case, path, and property formatting helpers. */
-export * from './string'
+export * from './string.ts'
 /** Time constants, parsing, and formatting helpers. */
-export * from './time'
+export * from './time.ts'

+ 1 - 1
vendor/cosmokit/src/types.ts

@@ -1,4 +1,4 @@
-import { isNullable } from './misc'
+import { isNullable } from './misc.ts'
 
 type GlobalConstructorNames = keyof {
   [K in keyof typeof globalThis as typeof globalThis[K] extends abstract new (...args: any) => any ? K : never]: K

+ 1 - 1
vendor/hmr/src/index.ts

@@ -4,7 +4,7 @@ import { ModuleJob, ModuleLoader, ResolveResult } from '@cordisjs/plugin-loader'
 import type { Include } from '@cordisjs/plugin-include'
 import { ChokidarOptions, FSWatcher, watch } from 'chokidar'
 import { relative, resolve } from 'node:path'
-import { handleError } from './error'
+import { handleError } from './error.ts'
 import type {} from '@cordisjs/plugin-timer'
 import { fileURLToPath, pathToFileURL } from 'node:url'
 import { createRequire } from 'node:module'

+ 4 - 4
vendor/loader/src/config/entry.ts

@@ -1,9 +1,9 @@
 import { Context, Fiber, Inject } from 'cordis'
 import { deepEqual, isNullable } from 'cosmokit'
-import { Loader } from '../index'
-import { EntryGroup } from './group'
-import { EntryTree } from './tree'
-import { evaluate, interpolate } from './utils'
+import { Loader } from '../index.ts'
+import { EntryGroup } from './group.ts'
+import { EntryTree } from './tree.ts'
+import { evaluate, interpolate } from './utils.ts'
 
 /** Serialized plugin entry options stored in loader config files. */
 export interface EntryOptions {

+ 2 - 2
vendor/loader/src/config/group.ts

@@ -1,6 +1,6 @@
 import { Context, Service } from 'cordis'
-import { Entry, EntryOptions } from './entry'
-import { EntryTree } from './tree'
+import { Entry, EntryOptions } from './entry.ts'
+import { EntryTree } from './tree.ts'
 
 /** Runtime owner for a list of child loader entries. */
 export class EntryGroup {

+ 2 - 2
vendor/loader/src/config/isolate.ts

@@ -1,8 +1,8 @@
 import { Context } from 'cordis'
 import { Dict } from 'cosmokit'
-import { Entry } from './entry'
+import { Entry } from './entry.ts'
 
-declare module './entry' {
+declare module './entry.ts' {
   interface EntryOptions {
     intercept?: Dict | null
     isolate?: Dict<true | string> | null

+ 2 - 2
vendor/loader/src/config/tree.ts

@@ -1,7 +1,7 @@
 import { composeError, Context } from 'cordis'
 import { Dict, isNonNullable } from 'cosmokit'
-import { Entry, EntryOptions } from './entry'
-import { EntryGroup } from './group'
+import { Entry, EntryOptions } from './entry.ts'
+import { EntryGroup } from './group.ts'
 
 /** Mutable tree of loader entries. Persistence is supplied by subclasses. */
 export abstract class EntryTree {

+ 10 - 10
vendor/loader/src/index.ts

@@ -1,22 +1,22 @@
 import { Context, Inject, Service } from 'cordis'
 import { defineProperty, Dict, isNullable } from 'cosmokit'
-import { ModuleLoader } from './internal'
-import { Entry, EntryOptions } from './config/entry'
-import isolate from './config/isolate'
-import { EntryTree } from './config/tree'
+import { ModuleLoader } from './internal.ts'
+import { Entry, EntryOptions } from './config/entry.ts'
+import isolate from './config/isolate.ts'
+import { EntryTree } from './config/tree.ts'
 
 /** Re-export entry node APIs. */
-export * from './config/entry'
+export * from './config/entry.ts'
 /** Re-export nested entry group APIs. */
-export * from './config/group'
+export * from './config/group.ts'
 /** Re-export service isolation helpers. */
-export * from './config/isolate'
+export * from './config/isolate.ts'
 /** Re-export entry tree persistence APIs. */
-export * from './config/tree'
+export * from './config/tree.ts'
 /** Re-export loader config expression helpers. */
-export * from './config/utils'
+export * from './config/utils.ts'
 /** Re-export Node internal module loader compatibility types. */
-export * from './internal'
+export * from './internal.ts'
 
 declare module 'cordis' {
   interface Events {

+ 2 - 2
vendor/logger-console/src/browser.ts

@@ -1,8 +1,8 @@
 import { Message } from 'cordis'
-import { ConsoleExporter as Base } from './shared'
+import { ConsoleExporter as Base } from './shared.ts'
 
 /** Re-export shared console exporter config and base implementation. */
-export * from './shared'
+export * from './shared.ts'
 
 /** Browser console exporter that dispatches to native console methods. */
 export class ConsoleExporter extends Base {

+ 2 - 2
vendor/logger-console/src/index.ts

@@ -1,10 +1,10 @@
 import { Formatter } from 'cordis'
 import { inspect } from 'node:util'
 import supportsColor from 'supports-color'
-import { ConsoleExporter as Base } from './shared'
+import { ConsoleExporter as Base } from './shared.ts'
 
 /** Re-export shared console exporter config and base implementation. */
-export * from './shared'
+export * from './shared.ts'
 
 const inspectFormatter: Formatter = (value, target) => {
   return inspect(value, { colors: !!target.colors, depth: Infinity, compact: true, breakLength: Infinity })