@tsdoctor/registry 0.5.14 → 0.7.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/PackageFetcher.js +1 -1
- package/README.md +1 -1
- package/TypeRegistry.js +1 -1
- package/TypeResolver.js +54 -69
- package/index.d.ts +24 -19
- package/index.js +2 -2
- package/internal/resolution.js +1 -163
- package/package.json +9 -8
- package/tsdoc-metadata.json +1 -1
package/PackageFetcher.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MAX_TYPE_BYTES_PER_PACKAGE, MAX_TYPE_FILES_PER_PACKAGE } from "./internal/limits.js";
|
|
2
1
|
import { FileTreeResponse, TYPE_FILE_PATTERN, VersionsResponse, fileTreeUrl, fileUrl, packageJsonUrl, versionsUrl } from "./internal/jsdelivr.js";
|
|
2
|
+
import { MAX_TYPE_BYTES_PER_PACKAGE, MAX_TYPE_FILES_PER_PACKAGE } from "./internal/limits.js";
|
|
3
3
|
import { emit } from "./RegistryEvent.js";
|
|
4
4
|
import { Cause, Context, Duration, Effect, Layer, Ref, Schedule, Schema } from "effect";
|
|
5
5
|
import * as HttpClient from "effect/http/HttpClient";
|
package/README.md
CHANGED
|
@@ -72,7 +72,7 @@ await Effect.runPromise(program.pipe(Effect.provide(RegistryLayer)));
|
|
|
72
72
|
- `TypeRegistry` — the facade over cache, fetcher and resolver: `getVfs`, `getPackageVfs`, `fetchAndCache`, `resolveVersion`, `resolveImport`, `getTypeEntries`, `hasCached`, `clearCache`, `pruneCache`.
|
|
73
73
|
- `TypeCache` — a two-plane cache: declaration files on disk, per-package metadata in an `@effected/store` `Cache` with native TTL expiry and pruning.
|
|
74
74
|
- `PackageFetcher` — the jsDelivr-backed CDN client, requiring only an `HttpClient`.
|
|
75
|
-
- `TypeResolver` — static resolution of import specifiers and type entry points against a package manifest, covering `exports`, `typesVersions` and legacy fields.
|
|
75
|
+
- `TypeResolver` — static resolution of import specifiers and type entry points against a package manifest, covering `exports`, `typesVersions` and legacy fields as TypeScript reads them (via `@effected/package-json`'s `PackageExports`).
|
|
76
76
|
- `RegistryEvent` and `RegistryObserver` — an opt-in, zero-cost progress channel; the library logs nothing on its own.
|
|
77
77
|
- Typed errors throughout: `FetchError`, `PackageNotFoundError`, `VersionNotFoundError`, `TypeCacheError`, `BatchLoadError`.
|
|
78
78
|
|
package/TypeRegistry.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { packageJsonUrl } from "./internal/jsdelivr.js";
|
|
2
|
-
import { emit } from "./RegistryEvent.js";
|
|
3
2
|
import { FetchError, PackageFetcher, PackageManifest, PackageNotFoundError, VersionNotFoundError } from "./PackageFetcher.js";
|
|
3
|
+
import { emit } from "./RegistryEvent.js";
|
|
4
4
|
import { TypeCache, TypeCacheMetadata } from "./TypeCache.js";
|
|
5
5
|
import { TypeResolver } from "./TypeResolver.js";
|
|
6
6
|
import { Context, DateTime, Effect, Layer, Option, Schema, Semaphore } from "effect";
|
package/TypeResolver.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isSafeRelativePath, isTypeDefinition, normalizePath } from "./internal/resolution.js";
|
|
2
2
|
import { PackageSpec } from "./PackageSpec.js";
|
|
3
|
-
import { Option, Schema } from "effect";
|
|
3
|
+
import { Option, Result, Schema } from "effect";
|
|
4
|
+
import { PackageExports } from "@effected/package-json";
|
|
4
5
|
|
|
5
6
|
// src/TypeResolver.ts
|
|
6
7
|
/**
|
|
@@ -17,11 +18,6 @@ var ResolvedModule = class extends Schema.Class("ResolvedModule")({
|
|
|
17
18
|
/** The package the path belongs to. */
|
|
18
19
|
package: PackageSpec
|
|
19
20
|
}) {};
|
|
20
|
-
const DUNDER_KEYS = /* @__PURE__ */ new Set([
|
|
21
|
-
"__proto__",
|
|
22
|
-
"constructor",
|
|
23
|
-
"prototype"
|
|
24
|
-
]);
|
|
25
21
|
/**
|
|
26
22
|
* Build a `ResolvedModule` only when the manifest-supplied path stays inside
|
|
27
23
|
* the package: resolved paths reach `PackageFetcher.downloadFile`'s URL and
|
|
@@ -38,6 +34,25 @@ const safeResolved = (filePath, pkg) => {
|
|
|
38
34
|
}));
|
|
39
35
|
};
|
|
40
36
|
/**
|
|
37
|
+
* The declaration path TypeScript reads for a subpath: the first declaration
|
|
38
|
+
* candidate of `@effected/package-json`'s `PackageExports.resolveTypesResult`,
|
|
39
|
+
* or the manifest's own target when no candidate is a declaration file.
|
|
40
|
+
*
|
|
41
|
+
* @remarks
|
|
42
|
+
* `Option.none()` when the manifest does not answer the subpath. Two kit
|
|
43
|
+
* answers are guesses rather than manifest evidence, and are refused the
|
|
44
|
+
* same way: a legacy subpath probing itself (`source: "file"`), and the
|
|
45
|
+
* root falling to the implicit `index.js` when the manifest has no `main`.
|
|
46
|
+
*/
|
|
47
|
+
const typesPath = (manifest, subpath) => {
|
|
48
|
+
const resolved = PackageExports.resolveTypesResult(manifest, subpath);
|
|
49
|
+
if (Result.isFailure(resolved)) return Option.none();
|
|
50
|
+
const target = resolved.success;
|
|
51
|
+
if (target.source === "file") return Option.none();
|
|
52
|
+
if (target.source === "main" && manifest.main === void 0) return Option.none();
|
|
53
|
+
return Option.some(Option.getOrElse(target.declaration, () => target.target));
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
41
56
|
* Pure `package.json` → declaration-file resolution.
|
|
42
57
|
*
|
|
43
58
|
* @remarks
|
|
@@ -48,10 +63,14 @@ const safeResolved = (filePath, pkg) => {
|
|
|
48
63
|
* too — it returns `Option.none()` where v3 fabricated a guessed fallback
|
|
49
64
|
* path, leaving fallback policy to the caller.
|
|
50
65
|
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* `
|
|
54
|
-
*
|
|
66
|
+
* Resolution is `@effected/package-json`'s `PackageExports`, TypeScript's
|
|
67
|
+
* reading of a manifest: conditions are walked in the manifest's own key
|
|
68
|
+
* order (`types`, `import` and `default` match), targets go through
|
|
69
|
+
* TypeScript's extension substitution (`./x.js` yields `./x.d.ts`), and
|
|
70
|
+
* `typesVersions` is read only when `exports` is absent. All map inputs are
|
|
71
|
+
* untrusted CDN data; the kit confines every target to the package, guards
|
|
72
|
+
* depth and wildcard counts and reads own keys only, and every resolved path
|
|
73
|
+
* is validated again here before a `ResolvedModule` is constructed.
|
|
55
74
|
*
|
|
56
75
|
* @public
|
|
57
76
|
*/
|
|
@@ -62,12 +81,12 @@ var TypeResolver = class TypeResolver {
|
|
|
62
81
|
* manifest.
|
|
63
82
|
*
|
|
64
83
|
* @remarks
|
|
65
|
-
*
|
|
66
|
-
* `
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
84
|
+
* The specifier's subpath resolves through `exports` when present, else
|
|
85
|
+
* `typesVersions["*"]`, then — for the root — `types`/`typings` and
|
|
86
|
+
* `main`, each target extension-substituted to the declaration file
|
|
87
|
+
* TypeScript would read. `Option.none()` when the manifest offers no
|
|
88
|
+
* evidence for the subpath, or when the evidence names a path outside the
|
|
89
|
+
* package (hostile manifest — fails closed).
|
|
71
90
|
*/
|
|
72
91
|
static resolveImport(specifier, manifest, pkg) {
|
|
73
92
|
let subpath;
|
|
@@ -77,50 +96,20 @@ var TypeResolver = class TypeResolver {
|
|
|
77
96
|
const bare = specifier.replace(/^\/+/, "");
|
|
78
97
|
subpath = bare === "" || bare === "." ? "." : bare.startsWith("./") ? bare : `./${bare}`;
|
|
79
98
|
}
|
|
80
|
-
|
|
81
|
-
const exportValue = getExportValue(manifest.exports, subpath);
|
|
82
|
-
const typesPath = extractTypesFromExport(exportValue);
|
|
83
|
-
if (typesPath !== null) return safeResolved(typesPath, pkg);
|
|
84
|
-
}
|
|
85
|
-
if (manifest.typesVersions !== void 0 && Object.hasOwn(manifest.typesVersions, "*")) {
|
|
86
|
-
const versionMap = manifest.typesVersions["*"];
|
|
87
|
-
if (versionMap !== void 0) {
|
|
88
|
-
const lookupPath = subpath === "." ? "." : subpath.replace(/^\.\//, "");
|
|
89
|
-
if (!DUNDER_KEYS.has(lookupPath) && Object.hasOwn(versionMap, lookupPath)) {
|
|
90
|
-
const mapped = versionMap[lookupPath];
|
|
91
|
-
const first = Array.isArray(mapped) ? mapped[0] : mapped;
|
|
92
|
-
if (typeof first === "string") return safeResolved(first, pkg);
|
|
93
|
-
}
|
|
94
|
-
for (const pattern of Object.keys(versionMap)) {
|
|
95
|
-
if (DUNDER_KEYS.has(pattern) || !pattern.includes("*")) continue;
|
|
96
|
-
if (!Object.hasOwn(versionMap, pattern)) continue;
|
|
97
|
-
const regex = compileWildcard(pattern);
|
|
98
|
-
if (regex === null) continue;
|
|
99
|
-
const match = regex.exec(lookupPath);
|
|
100
|
-
if (match === null) continue;
|
|
101
|
-
const mapped = versionMap[pattern];
|
|
102
|
-
const first = Array.isArray(mapped) ? mapped[0] : mapped;
|
|
103
|
-
if (typeof first === "string") return safeResolved(first.replace(/\*/g, match[1] ?? ""), pkg);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
if (subpath === ".") {
|
|
108
|
-
if (manifest.types !== void 0) return safeResolved(manifest.types, pkg);
|
|
109
|
-
if (manifest.typings !== void 0) return safeResolved(manifest.typings, pkg);
|
|
110
|
-
}
|
|
111
|
-
return Option.none();
|
|
99
|
+
return Option.flatMap(typesPath(manifest, subpath), (path) => safeResolved(path, pkg));
|
|
112
100
|
}
|
|
113
101
|
/**
|
|
114
102
|
* Resolve the manifest's main type entry.
|
|
115
103
|
*
|
|
116
104
|
* @remarks
|
|
117
|
-
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
105
|
+
* The root subpath as {@link TypeResolver.resolveImport} resolves it, made
|
|
106
|
+
* total by the documented `index.d.ts` convention floor. A main path that
|
|
107
|
+
* escapes the package (hostile manifest) also falls to the floor rather
|
|
108
|
+
* than surviving.
|
|
121
109
|
*/
|
|
122
110
|
static resolveMainEntry(manifest, pkg) {
|
|
123
|
-
|
|
111
|
+
const main = Option.flatMap(typesPath(manifest, "."), (path) => safeResolved(path, pkg));
|
|
112
|
+
return Option.getOrElse(main, () => ResolvedModule.make({
|
|
124
113
|
filePath: "index.d.ts",
|
|
125
114
|
isTypeDefinition: true,
|
|
126
115
|
package: pkg
|
|
@@ -132,24 +121,20 @@ var TypeResolver = class TypeResolver {
|
|
|
132
121
|
* deduplicated by file path.
|
|
133
122
|
*
|
|
134
123
|
* @remarks
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
* `dist/*.d.ts`.
|
|
138
|
-
* {@link TypeResolver.resolveImport},
|
|
139
|
-
*
|
|
124
|
+
* Subpaths come from `PackageExports.typesEntriesResult`. Pattern entries
|
|
125
|
+
* (`"./*"`) are skipped: enumeration has no captured segment to
|
|
126
|
+
* substitute, so a pattern entry would emit a literal `dist/*.d.ts`.
|
|
127
|
+
* Pattern subpaths resolve through {@link TypeResolver.resolveImport},
|
|
128
|
+
* which has the concrete specifier. An `exports` map holding an invalid
|
|
129
|
+
* target fails enumeration as a whole, leaving only the main entry.
|
|
140
130
|
*/
|
|
141
131
|
static resolveTypeEntries(manifest, pkg) {
|
|
142
132
|
const entries = [TypeResolver.resolveMainEntry(manifest, pkg)];
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
if (
|
|
148
|
-
const typesPath = extractTypesFromExport(manifest.exports[key]);
|
|
149
|
-
if (typesPath !== null) {
|
|
150
|
-
const entry = safeResolved(typesPath, pkg);
|
|
151
|
-
if (Option.isSome(entry)) entries.push(entry.value);
|
|
152
|
-
}
|
|
133
|
+
const enumerated = PackageExports.typesEntriesResult(manifest);
|
|
134
|
+
if (Result.isSuccess(enumerated)) for (const target of enumerated.success) {
|
|
135
|
+
if (target.pattern) continue;
|
|
136
|
+
const entry = safeResolved(Option.getOrElse(target.declaration, () => target.target), pkg);
|
|
137
|
+
if (Option.isSome(entry)) entries.push(entry.value);
|
|
153
138
|
}
|
|
154
139
|
const seen = /* @__PURE__ */ new Set();
|
|
155
140
|
return entries.filter((entry) => {
|
package/index.d.ts
CHANGED
|
@@ -596,10 +596,14 @@ export declare class ResolvedModule extends ResolvedModule_base {}
|
|
|
596
596
|
* too — it returns `Option.none()` where v3 fabricated a guessed fallback
|
|
597
597
|
* path, leaving fallback policy to the caller.
|
|
598
598
|
*
|
|
599
|
-
*
|
|
600
|
-
*
|
|
601
|
-
* `
|
|
602
|
-
*
|
|
599
|
+
* Resolution is `@effected/package-json`'s `PackageExports`, TypeScript's
|
|
600
|
+
* reading of a manifest: conditions are walked in the manifest's own key
|
|
601
|
+
* order (`types`, `import` and `default` match), targets go through
|
|
602
|
+
* TypeScript's extension substitution (`./x.js` yields `./x.d.ts`), and
|
|
603
|
+
* `typesVersions` is read only when `exports` is absent. All map inputs are
|
|
604
|
+
* untrusted CDN data; the kit confines every target to the package, guards
|
|
605
|
+
* depth and wildcard counts and reads own keys only, and every resolved path
|
|
606
|
+
* is validated again here before a `ResolvedModule` is constructed.
|
|
603
607
|
*
|
|
604
608
|
* @public
|
|
605
609
|
*/
|
|
@@ -610,22 +614,22 @@ export declare class TypeResolver {
|
|
|
610
614
|
* manifest.
|
|
611
615
|
*
|
|
612
616
|
* @remarks
|
|
613
|
-
*
|
|
614
|
-
* `
|
|
615
|
-
*
|
|
616
|
-
*
|
|
617
|
-
*
|
|
618
|
-
*
|
|
617
|
+
* The specifier's subpath resolves through `exports` when present, else
|
|
618
|
+
* `typesVersions["*"]`, then — for the root — `types`/`typings` and
|
|
619
|
+
* `main`, each target extension-substituted to the declaration file
|
|
620
|
+
* TypeScript would read. `Option.none()` when the manifest offers no
|
|
621
|
+
* evidence for the subpath, or when the evidence names a path outside the
|
|
622
|
+
* package (hostile manifest — fails closed).
|
|
619
623
|
*/
|
|
620
624
|
static resolveImport(specifier: string, manifest: PackageManifest, pkg: PackageSpec): Option.Option<ResolvedModule>;
|
|
621
625
|
/**
|
|
622
626
|
* Resolve the manifest's main type entry.
|
|
623
627
|
*
|
|
624
628
|
* @remarks
|
|
625
|
-
*
|
|
626
|
-
*
|
|
627
|
-
*
|
|
628
|
-
*
|
|
629
|
+
* The root subpath as {@link TypeResolver.resolveImport} resolves it, made
|
|
630
|
+
* total by the documented `index.d.ts` convention floor. A main path that
|
|
631
|
+
* escapes the package (hostile manifest) also falls to the floor rather
|
|
632
|
+
* than surviving.
|
|
629
633
|
*/
|
|
630
634
|
static resolveMainEntry(manifest: PackageManifest, pkg: PackageSpec): ResolvedModule;
|
|
631
635
|
/**
|
|
@@ -634,11 +638,12 @@ export declare class TypeResolver {
|
|
|
634
638
|
* deduplicated by file path.
|
|
635
639
|
*
|
|
636
640
|
* @remarks
|
|
637
|
-
*
|
|
638
|
-
*
|
|
639
|
-
* `dist/*.d.ts`.
|
|
640
|
-
* {@link TypeResolver.resolveImport},
|
|
641
|
-
*
|
|
641
|
+
* Subpaths come from `PackageExports.typesEntriesResult`. Pattern entries
|
|
642
|
+
* (`"./*"`) are skipped: enumeration has no captured segment to
|
|
643
|
+
* substitute, so a pattern entry would emit a literal `dist/*.d.ts`.
|
|
644
|
+
* Pattern subpaths resolve through {@link TypeResolver.resolveImport},
|
|
645
|
+
* which has the concrete specifier. An `exports` map holding an invalid
|
|
646
|
+
* target fails enumeration as a whole, leaving only the main entry.
|
|
642
647
|
*/
|
|
643
648
|
static resolveTypeEntries(manifest: PackageManifest, pkg: PackageSpec): ReadonlyArray<ResolvedModule>;
|
|
644
649
|
/**
|
package/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { RegistryEvent, RegistryObserver } from "./RegistryEvent.js";
|
|
2
1
|
import { FetchError, PackageFetcher, PackageManifest, PackageNotFoundError, VersionNotFoundError } from "./PackageFetcher.js";
|
|
3
2
|
import { PackageSpec } from "./PackageSpec.js";
|
|
3
|
+
import { RegistryEvent, RegistryObserver } from "./RegistryEvent.js";
|
|
4
4
|
import { TypeCache, TypeCacheError, TypeCacheMetadata } from "./TypeCache.js";
|
|
5
|
-
import { ResolvedModule, TypeResolver } from "./TypeResolver.js";
|
|
6
5
|
import { BatchLoadError, TypeRegistry } from "./TypeRegistry.js";
|
|
6
|
+
import { ResolvedModule, TypeResolver } from "./TypeResolver.js";
|
|
7
7
|
|
|
8
8
|
export { BatchLoadError, FetchError, PackageFetcher, PackageManifest, PackageNotFoundError, PackageSpec, RegistryEvent, RegistryObserver, ResolvedModule, TypeCache, TypeCacheError, TypeCacheMetadata, TypeRegistry, TypeResolver, VersionNotFoundError };
|
package/internal/resolution.js
CHANGED
|
@@ -1,20 +1,6 @@
|
|
|
1
1
|
import { isTypeDefinition } from "@tsdoctor/vfs";
|
|
2
2
|
|
|
3
3
|
// src/internal/resolution.ts
|
|
4
|
-
/**
|
|
5
|
-
* Exports-map resolution machinery. Every input here — `exports` values,
|
|
6
|
-
* `typesVersions` maps, wildcard patterns — is untrusted JSON fetched from a
|
|
7
|
-
* CDN, so every recursive surface carries a depth guard, wildcard patterns
|
|
8
|
-
* are bounded before regex compilation, and untrusted keys are only read
|
|
9
|
-
* through `Object.hasOwn` (a JSON-parsed `{"__proto__": …}` key would
|
|
10
|
-
* otherwise read or, worse, assign the prototype).
|
|
11
|
-
*/
|
|
12
|
-
/** Keys that are never data on a plain object. Skipped everywhere. */
|
|
13
|
-
const DUNDER_KEYS = /* @__PURE__ */ new Set([
|
|
14
|
-
"__proto__",
|
|
15
|
-
"constructor",
|
|
16
|
-
"prototype"
|
|
17
|
-
]);
|
|
18
4
|
/** Normalize backslashes to forward slashes. */
|
|
19
5
|
const normalizePath = (path) => path.replace(/\\/g, "/");
|
|
20
6
|
/**
|
|
@@ -28,153 +14,5 @@ const isSafeRelativePath = (filePath) => {
|
|
|
28
14
|
if (/^[A-Za-z]:/.test(filePath)) return false;
|
|
29
15
|
return !filePath.split(/[/\\]+/).some((segment) => segment === "..");
|
|
30
16
|
};
|
|
31
|
-
const escapeRegex = (value) => value.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
32
|
-
/**
|
|
33
|
-
* Compile an exports/typesVersions wildcard pattern to a regex, or `null`
|
|
34
|
-
* when the pattern exceeds the wildcard bound. npm semantics use exactly one
|
|
35
|
-
* `*`; a hostile pattern with many wildcards would compile to a
|
|
36
|
-
* catastrophic-backtracking regex, so past the bound it simply does not
|
|
37
|
-
* match.
|
|
38
|
-
*/
|
|
39
|
-
const compileWildcard = (pattern) => {
|
|
40
|
-
let stars = 0;
|
|
41
|
-
for (const char of pattern) if (char === "*") stars += 1;
|
|
42
|
-
if (stars === 0 || stars > 1) return null;
|
|
43
|
-
return new RegExp(`^${escapeRegex(pattern).replace(/\\?\*/g, "(.*)")}$`);
|
|
44
|
-
};
|
|
45
|
-
/**
|
|
46
|
-
* Substitute a captured wildcard segment into an exports value.
|
|
47
|
-
*
|
|
48
|
-
* @remarks
|
|
49
|
-
* This is where v3 had a live prototype-pollution defect: it copied untrusted
|
|
50
|
-
* keys into a plain object literal, so an `exports` map containing a
|
|
51
|
-
* `"__proto__"` key assigned the prototype of the result. Substituted maps
|
|
52
|
-
* are built with `Object.create(null)` and dunder keys are skipped. Past the
|
|
53
|
-
* depth guard nothing resolves (`null`).
|
|
54
|
-
*/
|
|
55
|
-
const substituteWildcard = (value, captured, depth = 0) => {
|
|
56
|
-
if (depth > 256) return null;
|
|
57
|
-
if (typeof value === "string") return value.split("*").join(captured);
|
|
58
|
-
if (Array.isArray(value)) return value.map((entry) => substituteWildcard(entry, captured, depth + 1));
|
|
59
|
-
if (typeof value === "object" && value !== null) {
|
|
60
|
-
const result = Object.create(null);
|
|
61
|
-
for (const key of Object.keys(value)) {
|
|
62
|
-
if (DUNDER_KEYS.has(key)) continue;
|
|
63
|
-
if (!Object.hasOwn(value, key)) continue;
|
|
64
|
-
const entry = value[key];
|
|
65
|
-
result[key] = typeof entry === "string" || typeof entry === "object" && entry !== null ? substituteWildcard(entry, captured, depth + 1) : entry;
|
|
66
|
-
}
|
|
67
|
-
return result;
|
|
68
|
-
}
|
|
69
|
-
return value;
|
|
70
|
-
};
|
|
71
|
-
/**
|
|
72
|
-
* Look up a subpath in an `exports` map: the root-conditions sugar form
|
|
73
|
-
* first, then exact keys (with and without the `./` prefix), then bounded
|
|
74
|
-
* wildcard patterns resolved by Node's specificity rules. Returns the export
|
|
75
|
-
* value (wildcards substituted) or `null`.
|
|
76
|
-
*/
|
|
77
|
-
const getExportValue = (exports, subpath) => {
|
|
78
|
-
if (exports === void 0 || exports === null) return null;
|
|
79
|
-
if (typeof exports === "string") return subpath === "." ? exports : null;
|
|
80
|
-
if (Array.isArray(exports)) return subpath === "." ? exports : null;
|
|
81
|
-
if (typeof exports !== "object") return null;
|
|
82
|
-
const exportsObj = exports;
|
|
83
|
-
const keys = Object.keys(exportsObj);
|
|
84
|
-
if (!keys.some((key) => key.startsWith("."))) return subpath === "." ? exportsObj : null;
|
|
85
|
-
const withoutDot = subpath.replace(/^\.\//, "");
|
|
86
|
-
for (const key of [subpath, withoutDot]) if (!DUNDER_KEYS.has(key) && Object.hasOwn(exportsObj, key)) return exportsObj[key];
|
|
87
|
-
let best = null;
|
|
88
|
-
for (const pattern of keys) {
|
|
89
|
-
if (DUNDER_KEYS.has(pattern) || !pattern.includes("*")) continue;
|
|
90
|
-
if (!Object.hasOwn(exportsObj, pattern)) continue;
|
|
91
|
-
const regex = compileWildcard(pattern);
|
|
92
|
-
if (regex === null) continue;
|
|
93
|
-
const match = regex.exec(subpath) ?? regex.exec(withoutDot);
|
|
94
|
-
if (match === null) continue;
|
|
95
|
-
const base = pattern.indexOf("*");
|
|
96
|
-
if (best === null || base > best.base || base === best.base && pattern.length > best.pattern.length) best = {
|
|
97
|
-
pattern,
|
|
98
|
-
captured: match[1] ?? "",
|
|
99
|
-
base
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
if (best !== null) return substituteWildcard(exportsObj[best.pattern], best.captured);
|
|
103
|
-
return null;
|
|
104
|
-
};
|
|
105
|
-
/**
|
|
106
|
-
* Extract a types-bearing path from an export value: `types` first, then
|
|
107
|
-
* `import` / `default`, recursing into nested condition objects under the
|
|
108
|
-
* depth guard. A fallback array (Node semantics) is walked in order and the
|
|
109
|
-
* first entry yielding a types path wins.
|
|
110
|
-
*/
|
|
111
|
-
const extractTypesFromExport = (exportValue, depth = 0) => {
|
|
112
|
-
if (depth > 256) return null;
|
|
113
|
-
if (exportValue === void 0 || exportValue === null) return null;
|
|
114
|
-
if (typeof exportValue === "string") return exportValue;
|
|
115
|
-
if (Array.isArray(exportValue)) {
|
|
116
|
-
for (const entry of exportValue) {
|
|
117
|
-
const found = extractTypesFromExport(entry, depth + 1);
|
|
118
|
-
if (found !== null) return found;
|
|
119
|
-
}
|
|
120
|
-
return null;
|
|
121
|
-
}
|
|
122
|
-
if (typeof exportValue !== "object") return null;
|
|
123
|
-
const conditions = exportValue;
|
|
124
|
-
for (const condition of [
|
|
125
|
-
"types",
|
|
126
|
-
"import",
|
|
127
|
-
"default"
|
|
128
|
-
]) {
|
|
129
|
-
if (!Object.hasOwn(conditions, condition)) continue;
|
|
130
|
-
const value = conditions[condition];
|
|
131
|
-
if (typeof value === "string") return value;
|
|
132
|
-
if (typeof value === "object" && value !== null) {
|
|
133
|
-
const nested = extractTypesFromExport(value, depth + 1);
|
|
134
|
-
if (nested !== null) return nested;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return null;
|
|
138
|
-
};
|
|
139
|
-
/** The conventional lookup candidates for a bare subpath, most specific first. */
|
|
140
|
-
const tryExtensions = (basePath) => [
|
|
141
|
-
basePath,
|
|
142
|
-
`${basePath}.d.ts`,
|
|
143
|
-
`${basePath}.d.mts`,
|
|
144
|
-
`${basePath}.d.cts`,
|
|
145
|
-
`${basePath}.ts`,
|
|
146
|
-
`${basePath}.mts`,
|
|
147
|
-
`${basePath}.cts`,
|
|
148
|
-
`${basePath}.js`,
|
|
149
|
-
`${basePath}.mjs`,
|
|
150
|
-
`${basePath}.cjs`,
|
|
151
|
-
`${basePath}/index.d.ts`,
|
|
152
|
-
`${basePath}/index.d.mts`,
|
|
153
|
-
`${basePath}/index.d.cts`,
|
|
154
|
-
`${basePath}/index.ts`,
|
|
155
|
-
`${basePath}/index.js`
|
|
156
|
-
].map(normalizePath);
|
|
157
|
-
/**
|
|
158
|
-
* The main type entry for a manifest: `types`/`typings`, then the root
|
|
159
|
-
* export's types condition, then a declaration-extension swap of `main`,
|
|
160
|
-
* with the documented `index.d.ts` convention floor — which is what makes
|
|
161
|
-
* `TypeResolver.resolveMainEntry` genuinely total.
|
|
162
|
-
*/
|
|
163
|
-
const findMainTypePath = (manifest) => {
|
|
164
|
-
if (manifest.types !== void 0) return manifest.types;
|
|
165
|
-
if (manifest.typings !== void 0) return manifest.typings;
|
|
166
|
-
if (manifest.exports !== void 0) {
|
|
167
|
-
const rootExport = getExportValue(manifest.exports, ".");
|
|
168
|
-
const typesPath = extractTypesFromExport(rootExport);
|
|
169
|
-
if (typesPath !== null) return typesPath;
|
|
170
|
-
}
|
|
171
|
-
if (manifest.main !== void 0) {
|
|
172
|
-
const mainWithoutExt = manifest.main.replace(/\.(m?[jt]s|cjs)$/, "");
|
|
173
|
-
const found = tryExtensions(mainWithoutExt).find(isTypeDefinition);
|
|
174
|
-
if (found !== void 0) return found;
|
|
175
|
-
return manifest.main;
|
|
176
|
-
}
|
|
177
|
-
return "index.d.ts";
|
|
178
|
-
};
|
|
179
17
|
|
|
180
|
-
export {
|
|
18
|
+
export { isSafeRelativePath, isTypeDefinition, normalizePath };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsdoctor/registry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"private": false,
|
|
5
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": [
|
|
@@ -38,15 +38,16 @@
|
|
|
38
38
|
"./package.json": "./package.json"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@effected/
|
|
42
|
-
"@
|
|
41
|
+
"@effected/package-json": "^0.29.0",
|
|
42
|
+
"@effected/semver": "^0.21.0",
|
|
43
|
+
"@tsdoctor/vfs": "0.7.0"
|
|
43
44
|
},
|
|
44
45
|
"peerDependencies": {
|
|
45
|
-
"@effect/platform-node": "
|
|
46
|
-
"@effected/store": "^0.
|
|
47
|
-
"@effected/tsconfig-json": "^0.
|
|
48
|
-
"@effected/xdg": "^0.
|
|
49
|
-
"effect": "
|
|
46
|
+
"@effect/platform-node": "~4.1.2",
|
|
47
|
+
"@effected/store": "^0.23.0",
|
|
48
|
+
"@effected/tsconfig-json": "^0.20.0",
|
|
49
|
+
"@effected/xdg": "^0.19.0",
|
|
50
|
+
"effect": "~4.1.2"
|
|
50
51
|
},
|
|
51
52
|
"peerDependenciesMeta": {
|
|
52
53
|
"@effected/xdg": {
|