@tsdoctor/registry 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,11 +5,11 @@
5
5
  [![Node.js %3E%3D24.11.0](https://img.shields.io/badge/Node.js-%3E%3D24.11.0-5fa04e.svg)](https://nodejs.org/)
6
6
  [![TypeScript 6.0](https://img.shields.io/badge/TypeScript-6.0-3178c6.svg)](https://www.typescriptlang.org/)
7
7
 
8
- TypeScript virtual file systems for Effect: fetch, cache and resolve type definitions from npm via the jsDelivr CDN, and build `@typescript/vfs` environments for Twoslash-style documentation tooling.
8
+ External TypeScript type loading for Effect: fetch, cache and resolve type definitions from npm via the jsDelivr CDN into a `Vfs`, the virtual file system Twoslash-style documentation tooling type-checks against.
9
9
 
10
10
  ## Why @tsdoctor/registry
11
11
 
12
- Documentation tooling that typechecks code samples needs the declaration files for whatever packages those samples import, and needs them without a real `node_modules`. Fetching them by hand means writing a CDN client, a disk cache with expiry, and a module resolver that understands `exports`, `typesVersions` and the legacy `types` field. This package is those three things behind one service, with typed errors and no hidden IO — every filesystem, HTTP and database dependency is provided by you at the edge.
12
+ Documentation tooling that typechecks code samples needs the declaration files for whatever packages those samples import, and needs them without a real `node_modules`. Fetching them by hand means writing a CDN client, a disk cache with expiry, and a module resolver that understands `exports`, `typesVersions` and the legacy `types` field. This package is those three things behind one service, with typed errors and no hidden IO — every filesystem, HTTP and database dependency is provided by you at the edge. What it produces is a `Vfs` from [`@tsdoctor/vfs`](../vfs), which also owns the virtual-package and `@typescript/vfs` environment primitives — this package fills a VFS, it does not define one.
13
13
 
14
14
  ## Install
15
15
 
@@ -21,13 +21,11 @@ npm install @tsdoctor/registry effect @effect/platform-node @effected/store @eff
21
21
  pnpm add @tsdoctor/registry effect @effect/platform-node @effected/store @effected/semver
22
22
  ```
23
23
 
24
- Requires Node.js >=24.11.0. Those four peers are required. The rest are optional and pull in only with the feature that uses them:
24
+ Requires Node.js >=24.11.0. Those four peers are required. `@effected/xdg` is optional and pulls in only with the feature that uses it:
25
25
 
26
26
  ```bash
27
27
  # for TypeCache.layerXdg
28
28
  npm install @effected/xdg
29
- # for TsEnvironment.make
30
- npm install @effected/tsconfig-json typescript @typescript/vfs
31
29
  ```
32
30
 
33
31
  Every dependency here is a peer rather than a bundled dependency, including `@effected/semver`, whose types appear in no exported signature. That is deliberate: each `@effected/*` package pins an exact `effect` version as its own peer, so bundling one would create a second resolution site that can land on a different `effect` build than yours and fail at import. As peers they all resolve in your closure, against your `effect`.
@@ -75,10 +73,8 @@ await Effect.runPromise(program.pipe(Effect.provide(RegistryLayer)));
75
73
  - `TypeCache` — a two-plane cache: declaration files on disk, per-package metadata in an `@effected/store` `Cache` with native TTL expiry and pruning.
76
74
  - `PackageFetcher` — the jsDelivr-backed CDN client, requiring only an `HttpClient`.
77
75
  - `TypeResolver` — static resolution of import specifiers and type entry points against a package manifest, covering `exports`, `typesVersions` and legacy fields.
78
- - `TsEnvironment` — builds a `VirtualTypeScriptEnvironment` over a VFS from tsconfig-JSON compiler options, loading the optional `typescript` peers lazily so a consumer that never calls it never loads the compiler.
79
- - `VirtualPackage` — synthesizes a package from locally supplied declaration content, for API Extractor output and hand-written ambient types.
80
76
  - `RegistryEvent` and `RegistryObserver` — an opt-in, zero-cost progress channel; the library logs nothing on its own.
81
- - Typed errors throughout: `FetchError`, `PackageNotFoundError`, `VersionNotFoundError`, `TypeCacheError`, `BatchLoadError`, `TsEnvironmentError`.
77
+ - Typed errors throughout: `FetchError`, `PackageNotFoundError`, `VersionNotFoundError`, `TypeCacheError`, `BatchLoadError`.
82
78
 
83
79
  ## Documentation
84
80
 
package/TypeRegistry.js CHANGED
@@ -3,8 +3,8 @@ import { emit } from "./RegistryEvent.js";
3
3
  import { FetchError, PackageFetcher, PackageManifest, PackageNotFoundError, VersionNotFoundError } from "./PackageFetcher.js";
4
4
  import { TypeCache, TypeCacheMetadata } from "./TypeCache.js";
5
5
  import { TypeResolver } from "./TypeResolver.js";
6
- import { mergeVfs } from "./Vfs.js";
7
6
  import { Context, DateTime, Effect, Layer, Option, Schema, Semaphore } from "effect";
7
+ import { mergeVfs } from "@tsdoctor/vfs";
8
8
  import { Range, SemVer } from "@effected/semver";
9
9
 
10
10
  //#region src/TypeRegistry.ts
package/index.d.ts CHANGED
@@ -1,9 +1,8 @@
1
- import { Cause, Context, Duration, Effect, FileSystem, Layer, Option, Path, PlatformError, Schema } from "effect";
1
+ import { Cause, Context, Duration, Effect, FileSystem, Layer, Option, Path, Schema } from "effect";
2
2
  import * as HttpClient from "effect/unstable/http/HttpClient";
3
- import { CompilerOptions } from "@effected/tsconfig-json";
4
- import { VirtualTypeScriptEnvironment } from "@typescript/vfs";
5
3
  import { Cache } from "@effected/store";
6
4
  import { AppDirs, AppDirsError } from "@effected/xdg";
5
+ import { Vfs } from "@tsdoctor/vfs";
7
6
  //#region src/PackageSpec.d.ts
8
7
  declare const PackageSpec_base: Schema.Class<PackageSpec, Schema.Struct<{
9
8
  /** The npm package name (e.g. `"zod"`, `"@effect/schema"`). */
@@ -365,132 +364,6 @@ declare class RegistryObserver extends RegistryObserver_base {
365
364
  static readonly layerNoop: Layer.Layer<RegistryObserver>;
366
365
  }
367
366
  //#endregion
368
- //#region src/Vfs.d.ts
369
- /**
370
- * The package's currency type: a virtual file system mapping
371
- * `node_modules/`-prefixed paths to file contents.
372
- */
373
- /**
374
- * A virtual file system: file paths (prefixed `node_modules/<package>/`)
375
- * mapped to their string contents.
376
- *
377
- * @remarks
378
- * This is the value every loading operation produces and every TypeScript
379
- * integration consumes. Maps from multiple packages merge with {@link mergeVfs};
380
- * `@typescript/vfs` consumes the merged map directly (see `TsEnvironment`).
381
- *
382
- * @public
383
- */
384
- type Vfs = Map<string, string>;
385
- /**
386
- * The v3 name for {@link Vfs}, kept as an alias for the consumer migration.
387
- *
388
- * @public
389
- */
390
- type VirtualFileSystem = Vfs;
391
- /**
392
- * Merge VFS maps left to right into a new map; later entries win on path
393
- * collisions.
394
- *
395
- * @example
396
- * ```ts
397
- * import { mergeVfs } from "@tsdoctor/registry";
398
- *
399
- * const combined = mergeVfs(vfsA, vfsB);
400
- * ```
401
- *
402
- * @public
403
- */
404
- declare const mergeVfs: (...maps: ReadonlyArray<ReadonlyMap<string, string>>) => Vfs;
405
- /**
406
- * Prefix every path in `entries` with `node_modules/<name>/`, normalizing
407
- * away leading slashes.
408
- *
409
- * @public
410
- */
411
- declare const prefixVfs: (name: string, entries: ReadonlyMap<string, string>) => Vfs;
412
- //#endregion
413
- //#region src/TsEnvironment.d.ts
414
- declare const TsEnvironmentError_base: Schema.Class<TsEnvironmentError, Schema.TaggedStruct<"TsEnvironmentError", {
415
- /** The underlying failure, preserved structurally. */
416
- readonly cause: Schema.Defect;
417
- }>, import("effect/Cause").YieldableError>;
418
- /**
419
- * Raised when building a virtual TypeScript environment fails — including
420
- * when the optional `typescript` / `@typescript/vfs` /
421
- * `@effected/tsconfig-json` peers are not installed.
422
- *
423
- * @public
424
- */
425
- declare class TsEnvironmentError extends TsEnvironmentError_base {
426
- get message(): string;
427
- }
428
- /**
429
- * Options for {@link TsEnvironment.make}.
430
- *
431
- * @public
432
- */
433
- interface TsEnvironmentOptions {
434
- /** The virtual file system to typecheck against. */
435
- readonly vfs: Vfs;
436
- /**
437
- * Compiler options for the language service, in tsconfig JSON form
438
- * (`{ target: "es2022" }`, not `ts.ScriptTarget.ES2022`). Enum-valued
439
- * fields are converted to the compiler's numeric enums internally, so this
440
- * type has no dependency on the `typescript` package.
441
- */
442
- readonly compilerOptions: CompilerOptions.Type;
443
- /**
444
- * The directory VFS paths are rooted under and the filesystem fallback
445
- * root. Defaults to `process.cwd()` (which v3 hardcoded).
446
- */
447
- readonly projectRoot?: string;
448
- }
449
- /**
450
- * The `@typescript/vfs` seam: builds a `VirtualTypeScriptEnvironment` over a
451
- * {@link Vfs} plus the TypeScript default lib files.
452
- *
453
- * @remarks
454
- * The ONLY module touching the optional `typescript` / `@typescript/vfs` /
455
- * `@effected/tsconfig-json` peers, and it loads all three lazily inside
456
- * {@link TsEnvironment.make} — a consumer that never calls it never loads
457
- * the compiler, and a missing peer fails typed as
458
- * {@link TsEnvironmentError} instead of crashing at import time. Keep every
459
- * one of them behind that dynamic `import()`: a static value import here is
460
- * reachable from `index.ts`, so it would turn an omitted optional peer into
461
- * an `ERR_MODULE_NOT_FOUND` on the entry graph for consumers who never
462
- * touch this module. Only the type-only `CompilerOptions` import is safe
463
- * statically, because it erases. The underlying `createDefaultMapFromNodeModules` /
464
- * `createFSBackedSystem` read the real filesystem through TypeScript's own
465
- * `sys`, outside the Effect `FileSystem` service — accepted and documented;
466
- * this module is why the package is integrated tier on its own surface.
467
- *
468
- * No cache map (v3's `createTypeScriptCache` returned a one-entry `Map`
469
- * keyed by `JSON.stringify(compilerOptions)`): a consumer that wants keyed
470
- * reuse holds its own map.
471
- *
472
- * `VirtualTypeScriptEnvironment` is deliberately not re-exported — import
473
- * the type from `@typescript/vfs`, which consumers of this module already
474
- * declare.
475
- *
476
- * @example
477
- * ```ts
478
- * import { TsEnvironment } from "@tsdoctor/registry";
479
- *
480
- * const environment = TsEnvironment.make({
481
- * vfs,
482
- * compilerOptions: { strict: true, target: "es2022" },
483
- * });
484
- * ```
485
- *
486
- * @public
487
- */
488
- declare class TsEnvironment {
489
- private constructor();
490
- /** Build a `VirtualTypeScriptEnvironment` over a {@link Vfs}. */
491
- static make(options: TsEnvironmentOptions): Effect.Effect<VirtualTypeScriptEnvironment, TsEnvironmentError>;
492
- }
493
- //#endregion
494
367
  //#region src/TypeCache.d.ts
495
368
  declare const TypeCacheMetadata_base: Schema.Class<TypeCacheMetadata, Schema.Struct<{
496
369
  /** The pinned version the files on disk belong to. */
@@ -629,7 +502,7 @@ interface TypeCacheShape {
629
502
  readonly readMetadata: (pkg: PackageSpec) => Effect.Effect<Option.Option<TypeCacheMetadata>, TypeCacheError>;
630
503
  /** Write the package's metadata entry, forwarding its `ttl` to the store. */
631
504
  readonly writeMetadata: (pkg: PackageSpec, metadata: TypeCacheMetadata) => Effect.Effect<void, TypeCacheError>;
632
- /** Build the package's {@link Vfs}: every cached file keyed `node_modules/<name>/<path>`. */
505
+ /** Build the package's `Vfs`: every cached file keyed `node_modules/<name>/<path>`. */
633
506
  readonly getVfs: (pkg: PackageSpec) => Effect.Effect<Vfs, TypeCacheError>;
634
507
  /**
635
508
  * Remove the package: metadata first, then files.
@@ -842,7 +715,7 @@ interface TypeRegistryShape {
842
715
  readonly ttl?: Duration.Duration;
843
716
  }) => Effect.Effect<void, FetchError | PackageNotFoundError | TypeCacheError>;
844
717
  /**
845
- * Build the {@link Vfs} for one package, fetching it first when missing or
718
+ * Build the `Vfs` for one package, fetching it first when missing or
846
719
  * stale (the stale-vs-miss ladder: live metadata → hit; files on disk with
847
720
  * no live metadata → stale, refetched when `autoFetch`, served as-is
848
721
  * otherwise; nothing → miss, fetched or failed typed on
@@ -850,7 +723,7 @@ interface TypeRegistryShape {
850
723
  */
851
724
  readonly getPackageVfs: (pkg: PackageSpec, options?: PackageVfsOptions) => Effect.Effect<Vfs, FetchError | PackageNotFoundError | TypeCacheError>;
852
725
  /**
853
- * Build a merged {@link Vfs} for several packages, best-effort.
726
+ * Build a merged `Vfs` for several packages, best-effort.
854
727
  *
855
728
  * @remarks
856
729
  * Loads concurrently (limit 5), accumulates per-package failures, merges
@@ -912,79 +785,5 @@ declare class TypeRegistry extends TypeRegistry_base {
912
785
  static readonly layer: Layer.Layer<TypeRegistry, never, TypeCache | PackageFetcher>;
913
786
  }
914
787
  //#endregion
915
- //#region src/VirtualPackage.d.ts
916
- declare const VirtualPackage_base: Schema.Class<VirtualPackage, Schema.Struct<{
917
- /** The package name (e.g. `"@my-org/api-types"`). */
918
- readonly name: Schema.String;
919
- /** The package version. */
920
- readonly version: Schema.String;
921
- /** Entry file names (e.g. `"index.d.ts"`) mapped to declaration source. */
922
- readonly entries: Schema.$ReadonlyMap<Schema.String, Schema.String>;
923
- }>, {}>;
924
- /**
925
- * A synthetic npm package built from locally supplied TypeScript declaration
926
- * content, for inclusion in a {@link Vfs} without fetching from the CDN.
927
- *
928
- * @remarks
929
- * Useful when you have locally generated `.d.ts` files — API Extractor
930
- * output, hand-written ambient declarations — and want them in the same VFS
931
- * `TypeRegistry` builds from remote packages. Instances are transient: they
932
- * are never persisted to the disk cache.
933
- *
934
- * The class is deliberately subclass-friendly (the rspress consumer extends
935
- * it): construct via `VirtualPackage.make(...)` or the statics, and extend
936
- * with `class Mine extends VirtualPackage { ... }`.
937
- *
938
- * @example
939
- * ```ts
940
- * import { VirtualPackage } from "@tsdoctor/registry";
941
- *
942
- * const pkg = VirtualPackage.create("@my-org/api-types", "1.0.0", "export interface User { id: string }");
943
- * const vfs = pkg.toVfs();
944
- * // node_modules/@my-org/api-types/package.json, node_modules/@my-org/api-types/index.d.ts
945
- * ```
946
- *
947
- * @public
948
- */
949
- declare class VirtualPackage extends VirtualPackage_base {
950
- /**
951
- * Single-entry factory: a virtual package whose sole entry point is
952
- * `index.d.ts`.
953
- */
954
- static create(name: string, version: string, declarations: string): VirtualPackage;
955
- /**
956
- * Multi-entry factory: one `.d.ts` per entry point, exposed through a
957
- * synthetic `exports` map.
958
- *
959
- * @remarks
960
- * An empty entries map is developer wiring, not input — it would produce a
961
- * package whose `types` points at a file that does not exist — so it
962
- * throws at construction (defect posture), as does an entry set whose
963
- * names collide after extension normalization (see
964
- * {@link VirtualPackage.toVfs}).
965
- */
966
- static createMultiEntry(name: string, version: string, entries: ReadonlyMap<string, string>): VirtualPackage;
967
- /**
968
- * Load a single `.d.ts` file from disk as a virtual package with one
969
- * `index.d.ts` entry.
970
- *
971
- * @remarks
972
- * Reads through the platform-agnostic `FileSystem` service; the
973
- * `PlatformError` surfaces typed.
974
- */
975
- static fromFile(name: string, version: string, filePath: string): Effect.Effect<VirtualPackage, PlatformError.PlatformError, FileSystem.FileSystem>;
976
- /**
977
- * The package's {@link Vfs}: a synthetic `package.json` plus every entry
978
- * file, each path prefixed `node_modules/<name>/`.
979
- *
980
- * @remarks
981
- * The `package.json` uses `types` for a single entry and an `exports` map
982
- * for multiple entries, so TypeScript module resolution works against the
983
- * generated VFS.
984
- */
985
- toVfs(): Vfs;
986
- private toPackageJson;
987
- }
988
- //#endregion
989
- export { BatchLoadError, type CachePruneResult, FetchError, PackageFetcher, type PackageFetcherShape, PackageManifest, PackageNotFoundError, PackageSpec, type PackageVersions, type PackageVfsOptions, RegistryEvent, RegistryObserver, type RegistryObserverShape, ResolvedModule, TsEnvironment, TsEnvironmentError, type TsEnvironmentOptions, TypeCache, TypeCacheError, TypeCacheMetadata, type TypeCacheShape, TypeRegistry, type TypeRegistryShape, TypeResolver, VersionNotFoundError, type Vfs, type VirtualFileSystem, VirtualPackage, mergeVfs, prefixVfs };
788
+ export { BatchLoadError, type CachePruneResult, FetchError, PackageFetcher, type PackageFetcherShape, PackageManifest, PackageNotFoundError, PackageSpec, type PackageVersions, type PackageVfsOptions, RegistryEvent, RegistryObserver, type RegistryObserverShape, ResolvedModule, TypeCache, TypeCacheError, TypeCacheMetadata, type TypeCacheShape, TypeRegistry, type TypeRegistryShape, TypeResolver, VersionNotFoundError };
990
789
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -1,11 +1,8 @@
1
1
  import { RegistryEvent, RegistryObserver } from "./RegistryEvent.js";
2
2
  import { FetchError, PackageFetcher, PackageManifest, PackageNotFoundError, VersionNotFoundError } from "./PackageFetcher.js";
3
3
  import { PackageSpec } from "./PackageSpec.js";
4
- import { TsEnvironment, TsEnvironmentError } from "./TsEnvironment.js";
5
4
  import { TypeCache, TypeCacheError, TypeCacheMetadata } from "./TypeCache.js";
6
5
  import { ResolvedModule, TypeResolver } from "./TypeResolver.js";
7
- import { mergeVfs, prefixVfs } from "./Vfs.js";
8
6
  import { BatchLoadError, TypeRegistry } from "./TypeRegistry.js";
9
- import { VirtualPackage } from "./VirtualPackage.js";
10
7
 
11
- export { BatchLoadError, FetchError, PackageFetcher, PackageManifest, PackageNotFoundError, PackageSpec, RegistryEvent, RegistryObserver, ResolvedModule, TsEnvironment, TsEnvironmentError, TypeCache, TypeCacheError, TypeCacheMetadata, TypeRegistry, TypeResolver, VersionNotFoundError, VirtualPackage, mergeVfs, prefixVfs };
8
+ export { BatchLoadError, FetchError, PackageFetcher, PackageManifest, PackageNotFoundError, PackageSpec, RegistryEvent, RegistryObserver, ResolvedModule, TypeCache, TypeCacheError, TypeCacheMetadata, TypeRegistry, TypeResolver, VersionNotFoundError };
@@ -1,3 +1,5 @@
1
+ import { isTypeDefinition } from "@tsdoctor/vfs";
2
+
1
3
  //#region src/internal/resolution.ts
2
4
  /**
3
5
  * Exports-map resolution machinery. Every input here — `exports` values,
@@ -13,8 +15,6 @@ const DUNDER_KEYS = /* @__PURE__ */ new Set([
13
15
  "constructor",
14
16
  "prototype"
15
17
  ]);
16
- /** Whether a path names a TypeScript declaration file. */
17
- const isTypeDefinition = (filePath) => filePath.endsWith(".d.ts") || filePath.endsWith(".d.mts") || filePath.endsWith(".d.cts");
18
18
  /** Normalize backslashes to forward slashes. */
19
19
  const normalizePath = (path) => path.replace(/\\/g, "/");
20
20
  /**
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@tsdoctor/registry",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "private": false,
5
- "description": "TypeScript virtual file systems for Effect: fetch, cache and resolve type definitions from npm via the jsDelivr CDN, and build @typescript/vfs environments for Twoslash-style documentation tooling.",
5
+ "description": "External TypeScript type loading for Effect: fetch, cache and resolve type definitions from npm via the jsDelivr CDN into a virtual file system.",
6
6
  "keywords": [
7
7
  "typescript",
8
8
  "vfs",
@@ -37,28 +37,19 @@
37
37
  },
38
38
  "./package.json": "./package.json"
39
39
  },
40
+ "dependencies": {
41
+ "@tsdoctor/vfs": "0.1.0"
42
+ },
40
43
  "peerDependencies": {
41
44
  "@effect/platform-node": "4.0.0-rc.109",
42
45
  "@effected/semver": "^0.5.0",
43
- "@effected/store": "^0.5.0",
44
- "@effected/tsconfig-json": "^0.6.0",
46
+ "@effected/store": "^0.6.0",
45
47
  "@effected/xdg": "^0.3.0",
46
- "@typescript/vfs": "^1.6.4",
47
- "effect": "4.0.0-rc.109",
48
- "typescript": "^6.0.3"
48
+ "effect": "4.0.0-rc.109"
49
49
  },
50
50
  "peerDependenciesMeta": {
51
- "@effected/tsconfig-json": {
52
- "optional": true
53
- },
54
51
  "@effected/xdg": {
55
52
  "optional": true
56
- },
57
- "@typescript/vfs": {
58
- "optional": true
59
- },
60
- "typescript": {
61
- "optional": true
62
53
  }
63
54
  },
64
55
  "engines": {
package/TsEnvironment.js DELETED
@@ -1,95 +0,0 @@
1
- import { isTypeDefinition } from "./internal/resolution.js";
2
- import { Effect, Schema } from "effect";
3
-
4
- //#region src/TsEnvironment.ts
5
- /**
6
- * Raised when building a virtual TypeScript environment fails — including
7
- * when the optional `typescript` / `@typescript/vfs` /
8
- * `@effected/tsconfig-json` peers are not installed.
9
- *
10
- * @public
11
- */
12
- var TsEnvironmentError = class extends Schema.TaggedError()("TsEnvironmentError", {
13
- /** The underlying failure, preserved structurally. */
14
- cause: Schema.Defect() }) {
15
- get message() {
16
- return "Failed to create the virtual TypeScript environment";
17
- }
18
- };
19
- /**
20
- * The `@typescript/vfs` seam: builds a `VirtualTypeScriptEnvironment` over a
21
- * {@link Vfs} plus the TypeScript default lib files.
22
- *
23
- * @remarks
24
- * The ONLY module touching the optional `typescript` / `@typescript/vfs` /
25
- * `@effected/tsconfig-json` peers, and it loads all three lazily inside
26
- * {@link TsEnvironment.make} — a consumer that never calls it never loads
27
- * the compiler, and a missing peer fails typed as
28
- * {@link TsEnvironmentError} instead of crashing at import time. Keep every
29
- * one of them behind that dynamic `import()`: a static value import here is
30
- * reachable from `index.ts`, so it would turn an omitted optional peer into
31
- * an `ERR_MODULE_NOT_FOUND` on the entry graph for consumers who never
32
- * touch this module. Only the type-only `CompilerOptions` import is safe
33
- * statically, because it erases. The underlying `createDefaultMapFromNodeModules` /
34
- * `createFSBackedSystem` read the real filesystem through TypeScript's own
35
- * `sys`, outside the Effect `FileSystem` service — accepted and documented;
36
- * this module is why the package is integrated tier on its own surface.
37
- *
38
- * No cache map (v3's `createTypeScriptCache` returned a one-entry `Map`
39
- * keyed by `JSON.stringify(compilerOptions)`): a consumer that wants keyed
40
- * reuse holds its own map.
41
- *
42
- * `VirtualTypeScriptEnvironment` is deliberately not re-exported — import
43
- * the type from `@typescript/vfs`, which consumers of this module already
44
- * declare.
45
- *
46
- * @example
47
- * ```ts
48
- * import { TsEnvironment } from "@tsdoctor/registry";
49
- *
50
- * const environment = TsEnvironment.make({
51
- * vfs,
52
- * compilerOptions: { strict: true, target: "es2022" },
53
- * });
54
- * ```
55
- *
56
- * @public
57
- */
58
- var TsEnvironment = class {
59
- constructor() {}
60
- /** Build a `VirtualTypeScriptEnvironment` over a {@link Vfs}. */
61
- static make(options) {
62
- return Effect.gen(function* () {
63
- const [tsModule, tsVfs, { TsEnumCodec }] = yield* Effect.tryPromise({
64
- try: () => Promise.all([
65
- import("typescript"),
66
- import("@typescript/vfs"),
67
- import("@effected/tsconfig-json")
68
- ]),
69
- catch: (cause) => new TsEnvironmentError({ cause })
70
- });
71
- return yield* Effect.try({
72
- try: () => {
73
- const typescript = tsModule.default;
74
- const projectRoot = options.projectRoot ?? process.cwd();
75
- const compilerOptions = TsEnumCodec.encodeCompilerOptions(options.compilerOptions);
76
- const executing = typescript.sys?.getExecutingFilePath?.();
77
- const libDirectory = executing === void 0 ? void 0 : executing.slice(0, Math.max(executing.lastIndexOf("/"), executing.lastIndexOf("\\")));
78
- const system = new Map(tsVfs.createDefaultMapFromNodeModules(compilerOptions, typescript, libDirectory));
79
- const rootFiles = [];
80
- for (const [path, content] of options.vfs) {
81
- const rooted = path.startsWith("/") ? path : `${projectRoot}/${path}`;
82
- system.set(rooted, content);
83
- if (isTypeDefinition(rooted)) rootFiles.push(rooted);
84
- }
85
- const sys = tsVfs.createFSBackedSystem(system, projectRoot, typescript, libDirectory);
86
- return tsVfs.createVirtualTypeScriptEnvironment(sys, rootFiles, typescript, compilerOptions);
87
- },
88
- catch: (cause) => new TsEnvironmentError({ cause })
89
- });
90
- }).pipe(Effect.withSpan("TsEnvironment.make"));
91
- }
92
- };
93
-
94
- //#endregion
95
- export { TsEnvironment, TsEnvironmentError };
package/Vfs.js DELETED
@@ -1,33 +0,0 @@
1
- //#region src/Vfs.ts
2
- /**
3
- * Merge VFS maps left to right into a new map; later entries win on path
4
- * collisions.
5
- *
6
- * @example
7
- * ```ts
8
- * import { mergeVfs } from "@tsdoctor/registry";
9
- *
10
- * const combined = mergeVfs(vfsA, vfsB);
11
- * ```
12
- *
13
- * @public
14
- */
15
- const mergeVfs = (...maps) => {
16
- const out = /* @__PURE__ */ new Map();
17
- for (const map of maps) for (const [path, content] of map) out.set(path, content);
18
- return out;
19
- };
20
- /**
21
- * Prefix every path in `entries` with `node_modules/<name>/`, normalizing
22
- * away leading slashes.
23
- *
24
- * @public
25
- */
26
- const prefixVfs = (name, entries) => {
27
- const out = /* @__PURE__ */ new Map();
28
- for (const [path, content] of entries) out.set(`node_modules/${name}/${path.replace(/^\/+/, "")}`, content);
29
- return out;
30
- };
31
-
32
- //#endregion
33
- export { mergeVfs, prefixVfs };
package/VirtualPackage.js DELETED
@@ -1,126 +0,0 @@
1
- import { Effect, FileSystem, Schema } from "effect";
2
-
3
- //#region src/VirtualPackage.ts
4
- /**
5
- * A synthetic npm package built from locally supplied TypeScript declaration
6
- * content, for inclusion in a {@link Vfs} without fetching from the CDN.
7
- *
8
- * @remarks
9
- * Useful when you have locally generated `.d.ts` files — API Extractor
10
- * output, hand-written ambient declarations — and want them in the same VFS
11
- * `TypeRegistry` builds from remote packages. Instances are transient: they
12
- * are never persisted to the disk cache.
13
- *
14
- * The class is deliberately subclass-friendly (the rspress consumer extends
15
- * it): construct via `VirtualPackage.make(...)` or the statics, and extend
16
- * with `class Mine extends VirtualPackage { ... }`.
17
- *
18
- * @example
19
- * ```ts
20
- * import { VirtualPackage } from "@tsdoctor/registry";
21
- *
22
- * const pkg = VirtualPackage.create("@my-org/api-types", "1.0.0", "export interface User { id: string }");
23
- * const vfs = pkg.toVfs();
24
- * // node_modules/@my-org/api-types/package.json, node_modules/@my-org/api-types/index.d.ts
25
- * ```
26
- *
27
- * @public
28
- */
29
- var VirtualPackage = class VirtualPackage extends Schema.Class("VirtualPackage")({
30
- /** The package name (e.g. `"@my-org/api-types"`). */
31
- name: Schema.String,
32
- /** The package version. */
33
- version: Schema.String,
34
- /** Entry file names (e.g. `"index.d.ts"`) mapped to declaration source. */
35
- entries: Schema.ReadonlyMap(Schema.String, Schema.String)
36
- }) {
37
- /**
38
- * Single-entry factory: a virtual package whose sole entry point is
39
- * `index.d.ts`.
40
- */
41
- static create(name, version, declarations) {
42
- return VirtualPackage.make({
43
- name,
44
- version,
45
- entries: /* @__PURE__ */ new Map([["index.d.ts", declarations]])
46
- });
47
- }
48
- /**
49
- * Multi-entry factory: one `.d.ts` per entry point, exposed through a
50
- * synthetic `exports` map.
51
- *
52
- * @remarks
53
- * An empty entries map is developer wiring, not input — it would produce a
54
- * package whose `types` points at a file that does not exist — so it
55
- * throws at construction (defect posture), as does an entry set whose
56
- * names collide after extension normalization (see
57
- * {@link VirtualPackage.toVfs}).
58
- */
59
- static createMultiEntry(name, version, entries) {
60
- if (entries.size === 0) throw new Error(`VirtualPackage.createMultiEntry: "${name}" needs at least one entry file`);
61
- return VirtualPackage.make({
62
- name,
63
- version,
64
- entries
65
- });
66
- }
67
- /**
68
- * Load a single `.d.ts` file from disk as a virtual package with one
69
- * `index.d.ts` entry.
70
- *
71
- * @remarks
72
- * Reads through the platform-agnostic `FileSystem` service; the
73
- * `PlatformError` surfaces typed.
74
- */
75
- static fromFile(name, version, filePath) {
76
- return Effect.gen(function* () {
77
- const content = yield* (yield* FileSystem.FileSystem).readFileString(filePath);
78
- return VirtualPackage.create(name, version, content);
79
- }).pipe(Effect.withSpan("VirtualPackage.fromFile"));
80
- }
81
- /**
82
- * The package's {@link Vfs}: a synthetic `package.json` plus every entry
83
- * file, each path prefixed `node_modules/<name>/`.
84
- *
85
- * @remarks
86
- * The `package.json` uses `types` for a single entry and an `exports` map
87
- * for multiple entries, so TypeScript module resolution works against the
88
- * generated VFS.
89
- */
90
- toVfs() {
91
- const vfs = /* @__PURE__ */ new Map();
92
- const prefix = `node_modules/${this.name}`;
93
- vfs.set(`${prefix}/package.json`, this.toPackageJson());
94
- for (const [fileName, content] of this.entries) {
95
- if (fileName === "package.json") throw new Error(`VirtualPackage: "${this.name}" cannot define package.json as an entry`);
96
- vfs.set(`${prefix}/${fileName}`, content);
97
- }
98
- return vfs;
99
- }
100
- toPackageJson() {
101
- if (this.entries.size === 0) throw new Error(`VirtualPackage: "${this.name}" has no entry files — nothing to point types at`);
102
- const manifest = {
103
- name: this.name,
104
- version: this.version
105
- };
106
- if (this.entries.size === 1) {
107
- const [only] = this.entries.keys();
108
- manifest.types = only ?? "index.d.ts";
109
- } else {
110
- manifest.exports = {};
111
- const sources = /* @__PURE__ */ new Map();
112
- for (const fileName of this.entries.keys()) {
113
- const baseName = fileName.replace(/\.d\.(m|c)?ts$/, "");
114
- const key = baseName === "index" ? "." : `./${baseName}`;
115
- const previous = sources.get(key);
116
- if (previous !== void 0) throw new Error(`VirtualPackage: "${this.name}" entries "${previous}" and "${fileName}" both normalize to the export key "${key}"`);
117
- sources.set(key, fileName);
118
- manifest.exports[key] = { types: `./${fileName}` };
119
- }
120
- }
121
- return JSON.stringify(manifest, null, 2);
122
- }
123
- };
124
-
125
- //#endregion
126
- export { VirtualPackage };