@dep-radius/core 0.1.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.
Files changed (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +48 -0
  3. package/dist/analyze/brief.d.ts +18 -0
  4. package/dist/analyze/brief.d.ts.map +1 -0
  5. package/dist/analyze/brief.js +89 -0
  6. package/dist/analyze/verdict.d.ts +16 -0
  7. package/dist/analyze/verdict.d.ts.map +1 -0
  8. package/dist/analyze/verdict.js +134 -0
  9. package/dist/context.d.ts +4 -0
  10. package/dist/context.d.ts.map +1 -0
  11. package/dist/context.js +36 -0
  12. package/dist/debug.d.ts +2 -0
  13. package/dist/debug.d.ts.map +1 -0
  14. package/dist/debug.js +130 -0
  15. package/dist/index.d.ts +9 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +8 -0
  18. package/dist/infra/cache.d.ts +18 -0
  19. package/dist/infra/cache.d.ts.map +1 -0
  20. package/dist/infra/cache.js +66 -0
  21. package/dist/infra/hash.d.ts +4 -0
  22. package/dist/infra/hash.d.ts.map +1 -0
  23. package/dist/infra/hash.js +18 -0
  24. package/dist/infra/http.d.ts +24 -0
  25. package/dist/infra/http.d.ts.map +1 -0
  26. package/dist/infra/http.js +44 -0
  27. package/dist/infra/limit.d.ts +4 -0
  28. package/dist/infra/limit.d.ts.map +1 -0
  29. package/dist/infra/limit.js +34 -0
  30. package/dist/infra/log.d.ts +8 -0
  31. package/dist/infra/log.d.ts.map +1 -0
  32. package/dist/infra/log.js +13 -0
  33. package/dist/inventory/installed.d.ts +15 -0
  34. package/dist/inventory/installed.d.ts.map +1 -0
  35. package/dist/inventory/installed.js +241 -0
  36. package/dist/inventory/lockfiles/bun.d.ts +3 -0
  37. package/dist/inventory/lockfiles/bun.d.ts.map +1 -0
  38. package/dist/inventory/lockfiles/bun.js +24 -0
  39. package/dist/inventory/lockfiles/npm.d.ts +3 -0
  40. package/dist/inventory/lockfiles/npm.d.ts.map +1 -0
  41. package/dist/inventory/lockfiles/npm.js +29 -0
  42. package/dist/inventory/lockfiles/pnpm.d.ts +3 -0
  43. package/dist/inventory/lockfiles/pnpm.d.ts.map +1 -0
  44. package/dist/inventory/lockfiles/pnpm.js +84 -0
  45. package/dist/inventory/lockfiles/types.d.ts +9 -0
  46. package/dist/inventory/lockfiles/types.d.ts.map +1 -0
  47. package/dist/inventory/lockfiles/types.js +1 -0
  48. package/dist/inventory/lockfiles/yarn.d.ts +3 -0
  49. package/dist/inventory/lockfiles/yarn.d.ts.map +1 -0
  50. package/dist/inventory/lockfiles/yarn.js +46 -0
  51. package/dist/inventory/manifests.d.ts +9 -0
  52. package/dist/inventory/manifests.d.ts.map +1 -0
  53. package/dist/inventory/manifests.js +135 -0
  54. package/dist/inventory/specifiers.d.ts +8 -0
  55. package/dist/inventory/specifiers.d.ts.map +1 -0
  56. package/dist/inventory/specifiers.js +53 -0
  57. package/dist/labels.d.ts +2 -0
  58. package/dist/labels.d.ts.map +1 -0
  59. package/dist/labels.js +18 -0
  60. package/dist/model.d.ts +276 -0
  61. package/dist/model.d.ts.map +1 -0
  62. package/dist/model.js +2 -0
  63. package/dist/notes/changelog.d.ts +11 -0
  64. package/dist/notes/changelog.d.ts.map +1 -0
  65. package/dist/notes/changelog.js +60 -0
  66. package/dist/notes/collect.d.ts +13 -0
  67. package/dist/notes/collect.d.ts.map +1 -0
  68. package/dist/notes/collect.js +225 -0
  69. package/dist/notes/entries.d.ts +3 -0
  70. package/dist/notes/entries.d.ts.map +1 -0
  71. package/dist/notes/entries.js +209 -0
  72. package/dist/notes/github.d.ts +25 -0
  73. package/dist/notes/github.d.ts.map +1 -0
  74. package/dist/notes/github.js +132 -0
  75. package/dist/notes/match.d.ts +13 -0
  76. package/dist/notes/match.d.ts.map +1 -0
  77. package/dist/notes/match.js +75 -0
  78. package/dist/notes/repo-url.d.ts +9 -0
  79. package/dist/notes/repo-url.d.ts.map +1 -0
  80. package/dist/notes/repo-url.js +46 -0
  81. package/dist/options.d.ts +53 -0
  82. package/dist/options.d.ts.map +1 -0
  83. package/dist/options.js +27 -0
  84. package/dist/registry/candidates.d.ts +24 -0
  85. package/dist/registry/candidates.d.ts.map +1 -0
  86. package/dist/registry/candidates.js +139 -0
  87. package/dist/registry/npmrc.d.ts +13 -0
  88. package/dist/registry/npmrc.d.ts.map +1 -0
  89. package/dist/registry/npmrc.js +99 -0
  90. package/dist/registry/packument.d.ts +34 -0
  91. package/dist/registry/packument.d.ts.map +1 -0
  92. package/dist/registry/packument.js +85 -0
  93. package/dist/registry/tar.d.ts +2 -0
  94. package/dist/registry/tar.d.ts.map +1 -0
  95. package/dist/registry/tar.js +75 -0
  96. package/dist/registry/tarball.d.ts +16 -0
  97. package/dist/registry/tarball.d.ts.map +1 -0
  98. package/dist/registry/tarball.js +59 -0
  99. package/dist/render/groups.d.ts +16 -0
  100. package/dist/render/groups.d.ts.map +1 -0
  101. package/dist/render/groups.js +105 -0
  102. package/dist/render/index.d.ts +2 -0
  103. package/dist/render/index.d.ts.map +1 -0
  104. package/dist/render/index.js +3 -0
  105. package/dist/render/json.d.ts +119 -0
  106. package/dist/render/json.d.ts.map +1 -0
  107. package/dist/render/json.js +130 -0
  108. package/dist/render/limits.d.ts +3 -0
  109. package/dist/render/limits.d.ts.map +1 -0
  110. package/dist/render/limits.js +25 -0
  111. package/dist/render/markdown.d.ts +3 -0
  112. package/dist/render/markdown.d.ts.map +1 -0
  113. package/dist/render/markdown.js +62 -0
  114. package/dist/run.d.ts +10 -0
  115. package/dist/run.d.ts.map +1 -0
  116. package/dist/run.js +218 -0
  117. package/dist/surface/analyze.d.ts +13 -0
  118. package/dist/surface/analyze.d.ts.map +1 -0
  119. package/dist/surface/analyze.js +166 -0
  120. package/dist/surface/delta.d.ts +5 -0
  121. package/dist/surface/delta.d.ts.map +1 -0
  122. package/dist/surface/delta.js +173 -0
  123. package/dist/surface/entries.d.ts +16 -0
  124. package/dist/surface/entries.d.ts.map +1 -0
  125. package/dist/surface/entries.js +187 -0
  126. package/dist/surface/extract.d.ts +15 -0
  127. package/dist/surface/extract.d.ts.map +1 -0
  128. package/dist/surface/extract.js +460 -0
  129. package/dist/surface/signature.d.ts +6 -0
  130. package/dist/surface/signature.d.ts.map +1 -0
  131. package/dist/surface/signature.js +229 -0
  132. package/dist/surface/vfs-host.d.ts +5 -0
  133. package/dist/surface/vfs-host.d.ts.map +1 -0
  134. package/dist/surface/vfs-host.js +54 -0
  135. package/dist/symbol-path.d.ts +28 -0
  136. package/dist/symbol-path.d.ts.map +1 -0
  137. package/dist/symbol-path.js +223 -0
  138. package/dist/usage/files.d.ts +18 -0
  139. package/dist/usage/files.d.ts.map +1 -0
  140. package/dist/usage/files.js +83 -0
  141. package/dist/usage/link.d.ts +23 -0
  142. package/dist/usage/link.d.ts.map +1 -0
  143. package/dist/usage/link.js +588 -0
  144. package/dist/usage/opaque.d.ts +7 -0
  145. package/dist/usage/opaque.d.ts.map +1 -0
  146. package/dist/usage/opaque.js +193 -0
  147. package/dist/usage/parse.d.ts +73 -0
  148. package/dist/usage/parse.d.ts.map +1 -0
  149. package/dist/usage/parse.js +608 -0
  150. package/dist/usage/resolve-local.d.ts +35 -0
  151. package/dist/usage/resolve-local.d.ts.map +1 -0
  152. package/dist/usage/resolve-local.js +340 -0
  153. package/dist/usage/scan.d.ts +4 -0
  154. package/dist/usage/scan.d.ts.map +1 -0
  155. package/dist/usage/scan.js +92 -0
  156. package/package.json +79 -0
  157. package/schema/brief-v1.schema.json +319 -0
@@ -0,0 +1,187 @@
1
+ import semver from "semver";
2
+ import ts from "typescript";
3
+ const WILDCARD_CAP = 50;
4
+ const RUNTIME_CONDITIONS = ["import", "require", "node", "default", "module"];
5
+ // A `types` condition anywhere in an export's condition tree wins; otherwise a runtime file with a
6
+ // declaration beside it. Custom conditions ("@acme/source") point at sources and are ignored.
7
+ export function typesForTarget(value, has, star) {
8
+ const sub = (s) => (star !== undefined ? s.replace(/\*/g, star) : s);
9
+ if (typeof value === "string") {
10
+ const p = norm(sub(value));
11
+ if (/\.d\.[cm]?ts$/.test(p))
12
+ return has(p) ? p : undefined;
13
+ for (const d of declarationFor(p))
14
+ if (has(d))
15
+ return d;
16
+ return undefined;
17
+ }
18
+ if (Array.isArray(value)) {
19
+ for (const v of value) {
20
+ const r = typesForTarget(v, has, star);
21
+ if (r)
22
+ return r;
23
+ }
24
+ return undefined;
25
+ }
26
+ if (!value || typeof value !== "object")
27
+ return undefined;
28
+ const o = value;
29
+ if ("types" in o) {
30
+ const r = typesForTarget(o.types, has, star);
31
+ if (r)
32
+ return r;
33
+ }
34
+ for (const c of RUNTIME_CONDITIONS) {
35
+ if (!(c in o))
36
+ continue;
37
+ const r = typesForTarget(o[c], has, star);
38
+ if (r)
39
+ return r;
40
+ }
41
+ return undefined;
42
+ }
43
+ function declarationFor(p) {
44
+ const m = /\.(m|c)?js$/.exec(p);
45
+ if (m) {
46
+ const base = p.slice(0, -m[0].length);
47
+ return [`${base}.d.${m[1] ?? ""}ts`, `${base}.d.ts`];
48
+ }
49
+ if (/\.[cm]?ts$/.test(p) && !/\.d\.[cm]?ts$/.test(p))
50
+ return [p.replace(/\.([cm]?)ts$/, ".d.$1ts")];
51
+ return [`${p}.d.ts`, `${p}/index.d.ts`];
52
+ }
53
+ function norm(p) {
54
+ return p.replace(/^\.\//, "").replace(/^\//, "");
55
+ }
56
+ export function resolveEntries(pj, files, wantedSubpaths = []) {
57
+ const set = new Set(files);
58
+ const has = (p) => set.has(p);
59
+ const entries = [];
60
+ let wildcardTruncated = false;
61
+ const exp = pj.exports;
62
+ const exportMap = exp === undefined
63
+ ? undefined
64
+ : typeof exp === "string" ||
65
+ Array.isArray(exp) ||
66
+ !Object.keys(exp).some((k) => k.startsWith("."))
67
+ ? { ".": exp }
68
+ : exp;
69
+ if (exportMap) {
70
+ for (const [key, value] of Object.entries(exportMap)) {
71
+ if (key === "./package.json" || value === null)
72
+ continue;
73
+ if (!key.includes("*")) {
74
+ const t = typesForTarget(value, has);
75
+ if (t)
76
+ entries.push({ subpath: key, typesFile: t });
77
+ continue;
78
+ }
79
+ const [pre, post] = key.split("*");
80
+ const pattern = firstString(value);
81
+ if (!pattern || !pattern.includes("*"))
82
+ continue;
83
+ const [fpre, fpost] = norm(pattern).split("*");
84
+ const stars = new Set();
85
+ for (const f of files) {
86
+ if (!f.startsWith(fpre))
87
+ continue;
88
+ const rest = f.slice(fpre.length);
89
+ const star = fpost
90
+ ? rest.endsWith(fpost)
91
+ ? rest.slice(0, -fpost.length)
92
+ : undefined
93
+ : rest.replace(/\.d\.[cm]?ts$|\.[cm]?js$/, "");
94
+ if (star && !star.includes("/"))
95
+ stars.add(star);
96
+ }
97
+ const sorted = [...stars].sort();
98
+ const wanted = sorted.filter((s) => wantedSubpaths.includes(`${pre}${s}${post}`));
99
+ const picked = [...new Set([...sorted.slice(0, WILDCARD_CAP), ...wanted])];
100
+ if (sorted.length > picked.length)
101
+ wildcardTruncated = true;
102
+ for (const star of picked) {
103
+ const t = typesForTarget(value, has, star);
104
+ if (t)
105
+ entries.push({ subpath: `${pre}${star}${post}`, typesFile: t });
106
+ }
107
+ }
108
+ }
109
+ else {
110
+ let main = typeof pj.types === "string"
111
+ ? pj.types
112
+ : typeof pj.typings === "string"
113
+ ? pj.typings
114
+ : undefined;
115
+ const tv = pj.typesVersions;
116
+ if (tv) {
117
+ const range = Object.keys(tv).find((r) => r === "*" || semver.satisfies(ts.version, r));
118
+ const mapping = range ? tv[range] : undefined;
119
+ const star = mapping?.["*"]?.[0];
120
+ if (star)
121
+ main = star.replace("*", (main ?? "index.d.ts").replace(/^\.\//, ""));
122
+ }
123
+ const candidates = [
124
+ main,
125
+ typeof pj.main === "string" ? pj.main : undefined,
126
+ "index.d.ts",
127
+ ].filter((x) => !!x);
128
+ for (const c of candidates) {
129
+ const t = /\.d\.[cm]?ts$/.test(c)
130
+ ? has(norm(c))
131
+ ? norm(c)
132
+ : undefined
133
+ : declarationFor(norm(c)).find(has);
134
+ if (t) {
135
+ entries.push({ subpath: ".", typesFile: t });
136
+ break;
137
+ }
138
+ }
139
+ for (const w of wantedSubpaths) {
140
+ if (w === ".")
141
+ continue;
142
+ const t = declarationFor(norm(w)).find(has);
143
+ if (t)
144
+ entries.push({ subpath: w, typesFile: t });
145
+ }
146
+ }
147
+ entries.sort((a, b) => a.subpath === "."
148
+ ? -1
149
+ : b.subpath === "."
150
+ ? 1
151
+ : a.subpath.localeCompare(b.subpath));
152
+ return { entries, wildcardTruncated };
153
+ }
154
+ function firstString(v) {
155
+ if (typeof v === "string")
156
+ return v;
157
+ if (Array.isArray(v)) {
158
+ for (const x of v) {
159
+ const r = firstString(x);
160
+ if (r)
161
+ return r;
162
+ }
163
+ return undefined;
164
+ }
165
+ if (v && typeof v === "object") {
166
+ const o = v;
167
+ for (const k of ["types", ...RUNTIME_CONDITIONS])
168
+ if (k in o) {
169
+ const r = firstString(o[k]);
170
+ if (r)
171
+ return r;
172
+ }
173
+ }
174
+ return undefined;
175
+ }
176
+ // Whether a version ships its own declarations, read from the packument without a download.
177
+ export function declaresTypes(pv) {
178
+ if (pv.types || pv.typings)
179
+ return true;
180
+ if (pv.exports !== undefined &&
181
+ JSON.stringify(pv.exports).includes('"types"'))
182
+ return true;
183
+ if (pv.exports !== undefined &&
184
+ /\.d\.[cm]?ts"/.test(JSON.stringify(pv.exports)))
185
+ return true;
186
+ return undefined;
187
+ }
@@ -0,0 +1,15 @@
1
+ import type { Ctx } from "../options.ts";
2
+ import type { Packument } from "../registry/packument.ts";
3
+ import type { RegistryConfig } from "../registry/npmrc.ts";
4
+ import type { Surface } from "../model.ts";
5
+ export type ExtractResult = {
6
+ ok: true;
7
+ surface: Surface;
8
+ } | {
9
+ ok: false;
10
+ reason: "no-types" | "offline-uncached" | "failed";
11
+ detail?: string;
12
+ };
13
+ export declare function extractSurface(ctx: Ctx, cfg: RegistryConfig, p: Packument, version: string, wantedSubpaths?: string[]): Promise<ExtractResult>;
14
+ export declare function build(pkg: string, version: string, integrity: string, files: Map<string, Buffer>, wantedSubpaths?: string[]): ExtractResult;
15
+ //# sourceMappingURL=extract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extract.d.ts","sourceRoot":"","sources":["../../src/surface/extract.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,eAAe,CAAA;AAKxC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAA;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAG1D,OAAO,KAAK,EAAa,OAAO,EAA6B,MAAM,aAAa,CAAA;AAKhF,MAAM,MAAM,aAAa,GACrB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,GAC9B;IACE,EAAE,EAAE,KAAK,CAAA;IACT,MAAM,EAAE,UAAU,GAAG,kBAAkB,GAAG,QAAQ,CAAA;IAClD,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAEL,wBAAsB,cAAc,CAClC,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,cAAc,EACnB,CAAC,EAAE,SAAS,EACZ,OAAO,EAAE,MAAM,EACf,cAAc,GAAE,MAAM,EAAO,GAC5B,OAAO,CAAC,aAAa,CAAC,CA6BxB;AAED,wBAAgB,KAAK,CACnB,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,cAAc,GAAE,MAAM,EAAO,GAC5B,aAAa,CAsbf"}
@@ -0,0 +1,460 @@
1
+ import ts from "typescript";
2
+ import { resolveEntries } from "./entries.js";
3
+ import { integrityHex } from "../infra/hash.js";
4
+ import { createVfsHost, PKG_ROOT } from "./vfs-host.js";
5
+ import { getTarballFiles } from "../registry/tarball.js";
6
+ import { ALGO, signatureText, typeText } from "./signature.js";
7
+ import { formatPath, parsePath } from "../symbol-path.js";
8
+ const SYMBOL_CAP = 25_000;
9
+ const NAMESPACE_DEPTH = 2;
10
+ export async function extractSurface(ctx, cfg, p, version, wantedSubpaths = []) {
11
+ const pv = p.versions[version];
12
+ if (!pv)
13
+ return {
14
+ ok: false,
15
+ reason: "failed",
16
+ detail: `version ${version} not in registry`,
17
+ };
18
+ const integrity = pv.dist.integrity ?? pv.dist.shasum ?? pv.dist.tarball;
19
+ const wantedKey = wantedSubpaths
20
+ .filter((w) => w.includes("/") && w !== ".")
21
+ .sort()
22
+ .join(",");
23
+ const key = `surfaces/${integrityHex(integrity) ?? Buffer.from(integrity).toString("hex").slice(0, 64)}.a${ALGO}.ts${ts.version}${wantedKey ? `.${Buffer.from(wantedKey).toString("base64url").slice(0, 40)}` : ""}.json`;
24
+ const cached = await ctx.cache.getJson(key);
25
+ if (cached)
26
+ return cached.value;
27
+ const tb = await getTarballFiles(ctx, cfg, pv);
28
+ if (!tb.ok)
29
+ return {
30
+ ok: false,
31
+ reason: tb.reason === "offline-uncached" ? "offline-uncached" : "failed",
32
+ detail: tb.reason,
33
+ };
34
+ const result = await ctx.heavy(async () => build(p.name, version, integrity, tb.files, wantedSubpaths));
35
+ await ctx.cache.setJson(key, result, ctx.now);
36
+ return result;
37
+ }
38
+ export function build(pkg, version, integrity, files, wantedSubpaths = []) {
39
+ const pjBuf = files.get("package.json");
40
+ let pj = {};
41
+ try {
42
+ pj = pjBuf
43
+ ? JSON.parse(pjBuf.toString("utf8"))
44
+ : {};
45
+ }
46
+ catch {
47
+ return { ok: false, reason: "failed", detail: "unreadable package.json" };
48
+ }
49
+ const fileList = [...files.keys()];
50
+ const { entries, wildcardTruncated } = resolveEntries(pj, fileList, wantedSubpaths);
51
+ if (entries.length === 0)
52
+ return { ok: false, reason: "no-types" };
53
+ const options = {
54
+ noEmit: true,
55
+ skipLibCheck: true,
56
+ types: [],
57
+ module: ts.ModuleKind.NodeNext,
58
+ moduleResolution: ts.ModuleResolutionKind.NodeNext,
59
+ target: ts.ScriptTarget.ES2022,
60
+ lib: ["lib.es2023.d.ts", "lib.dom.d.ts"],
61
+ strict: true,
62
+ allowJs: false,
63
+ noLib: false,
64
+ };
65
+ const host = createVfsHost(files);
66
+ const program = ts.createProgram({
67
+ rootNames: entries.map((e) => `${PKG_ROOT}/${e.typesFile}`),
68
+ options,
69
+ host,
70
+ });
71
+ const checker = program.getTypeChecker();
72
+ const symbols = {};
73
+ const surfaceEntries = {};
74
+ const firstPath = new Map();
75
+ const pendingReturns = [];
76
+ const flags = new Set();
77
+ if (wildcardTruncated)
78
+ flags.add("wildcard-truncated");
79
+ let count = 0;
80
+ const inPackage = (s) => !!s?.declarations?.some((d) => d.getSourceFile().fileName.startsWith(`${PKG_ROOT}/`));
81
+ const deprecatedOf = (s) => !!s.declarations?.some((d) => ts.getJSDocDeprecatedTag(d));
82
+ const emit = (sym) => {
83
+ if (count >= SYMBOL_CAP) {
84
+ flags.add("symbol-cap");
85
+ return false;
86
+ }
87
+ if (symbols[sym.path])
88
+ return false;
89
+ symbols[sym.path] = sym;
90
+ count++;
91
+ return true;
92
+ };
93
+ const resolveAlias = (s) => {
94
+ if (!(s.flags & ts.SymbolFlags.Alias))
95
+ return s;
96
+ try {
97
+ return checker.getAliasedSymbol(s);
98
+ }
99
+ catch {
100
+ return s;
101
+ }
102
+ };
103
+ const typeSymbolOf = (t) => {
104
+ const s = t.aliasSymbol ?? t.getSymbol();
105
+ if (!s || !inPackage(s))
106
+ return undefined;
107
+ if (s.flags &
108
+ (ts.SymbolFlags.Class |
109
+ ts.SymbolFlags.Interface |
110
+ ts.SymbolFlags.TypeAlias))
111
+ return s;
112
+ return undefined;
113
+ };
114
+ const isThisType = (t) => !!(t.flags & ts.TypeFlags.TypeParameter) &&
115
+ t.isThisType === true;
116
+ const callSigs = (t, path) => {
117
+ const sigs = t.getCallSignatures();
118
+ const first = sigs[0];
119
+ if (first) {
120
+ const rs = typeSymbolOf(checker.getReturnTypeOfSignature(first));
121
+ if (rs)
122
+ pendingReturns.push({ path, field: "returns", target: rs });
123
+ }
124
+ return sigs.map((s) => signatureText(checker, s)).sort();
125
+ };
126
+ const walkMembers = (ownerPath, type, sep, pkgName, entry, segs) => {
127
+ for (const prop of checker.getPropertiesOfType(type)) {
128
+ const name = prop.getName();
129
+ if (name === "prototype" ||
130
+ name.startsWith("__@") ||
131
+ name.startsWith("#"))
132
+ continue;
133
+ const decl = prop.valueDeclaration ?? prop.declarations?.[0];
134
+ if (decl &&
135
+ ts.getCombinedModifierFlags(decl) &
136
+ (ts.ModifierFlags.Private | ts.ModifierFlags.Protected))
137
+ continue;
138
+ const path = formatPath(pkgName, entry, [...segs, { name, sep }]);
139
+ let ptype;
140
+ try {
141
+ ptype = decl
142
+ ? checker.getTypeOfSymbolAtLocation(prop, decl)
143
+ : checker.getTypeOfSymbol(prop);
144
+ }
145
+ catch {
146
+ continue;
147
+ }
148
+ const record = {
149
+ path,
150
+ kind: sep === "#" ? "member" : "static-member",
151
+ sig: [],
152
+ deprecated: deprecatedOf(prop),
153
+ };
154
+ if (!emit(record))
155
+ continue;
156
+ const sigs = ptype.getCallSignatures();
157
+ if (sigs.length > 0) {
158
+ record.sig = sigs.map((s) => signatureText(checker, s)).sort();
159
+ const r = checker.getReturnTypeOfSignature(sigs[0]);
160
+ const rs = isThisType(r) ? undefined : typeSymbolOf(r);
161
+ if (rs)
162
+ pendingReturns.push({ path, field: "returns", target: rs });
163
+ // `refine(): Ch extends ... ? this & X : this` has no symbol, and is still the owner
164
+ else if (isThisType(r) ||
165
+ mentionsOwner(checker.typeToString(r), ownerPath))
166
+ record.returns = ownerPath;
167
+ }
168
+ else {
169
+ record.sig = [typeText(checker, ptype)];
170
+ const ts2 = typeSymbolOf(ptype);
171
+ if (ts2)
172
+ pendingReturns.push({ path, field: "instanceOf", target: ts2 });
173
+ }
174
+ }
175
+ };
176
+ const walk = (pkgName, entry, segs, raw, nsDepth) => {
177
+ if (count >= SYMBOL_CAP) {
178
+ flags.add("symbol-cap");
179
+ return;
180
+ }
181
+ const sym = resolveAlias(raw);
182
+ const path = formatPath(pkgName, entry, segs);
183
+ const seen = firstPath.get(sym);
184
+ const f = sym.flags;
185
+ const isModule = !!(f & ts.SymbolFlags.ValueModule) &&
186
+ !!sym.declarations?.some((d) => ts.isSourceFile(d) || ts.isModuleDeclaration(d));
187
+ const isNamespaceOnly = isModule ||
188
+ (!!(f & ts.SymbolFlags.NamespaceModule) &&
189
+ !(f &
190
+ (ts.SymbolFlags.Function |
191
+ ts.SymbolFlags.Class |
192
+ ts.SymbolFlags.Variable |
193
+ ts.SymbolFlags.Interface |
194
+ ts.SymbolFlags.TypeAlias |
195
+ ts.SymbolFlags.Enum)));
196
+ if (seen && seen !== path) {
197
+ emit({
198
+ path,
199
+ kind: isNamespaceOnly ? "namespace" : kindOf(f),
200
+ sig: [],
201
+ deprecated: false,
202
+ aliasOf: seen,
203
+ });
204
+ return;
205
+ }
206
+ firstPath.set(sym, path);
207
+ if (isNamespaceOnly) {
208
+ emit({ path, kind: "namespace", sig: [], deprecated: false });
209
+ if (nsDepth >= NAMESPACE_DEPTH)
210
+ return;
211
+ let exports = [];
212
+ try {
213
+ exports = checker.getExportsOfModule(sym);
214
+ }
215
+ catch {
216
+ return;
217
+ }
218
+ for (const e of [...exports].sort((a, b) => a.getName().localeCompare(b.getName()))) {
219
+ walk(pkgName, entry, [...segs, { name: e.getName(), sep: "." }], e, nsDepth + 1);
220
+ }
221
+ return;
222
+ }
223
+ const record = {
224
+ path,
225
+ kind: kindOf(f),
226
+ sig: [],
227
+ deprecated: deprecatedOf(sym),
228
+ };
229
+ if (!emit(record))
230
+ return;
231
+ try {
232
+ if (f & ts.SymbolFlags.Class) {
233
+ const staticType = checker.getTypeOfSymbol(sym);
234
+ record.sig = staticType
235
+ .getConstructSignatures()
236
+ .map((s) => signatureText(checker, s))
237
+ .sort();
238
+ record.instanceOf = path;
239
+ walkMembers(path, checker.getDeclaredTypeOfSymbol(sym), "#", pkgName, entry, segs);
240
+ walkMembers(path, staticType, ".", pkgName, entry, segs);
241
+ }
242
+ else if (f & ts.SymbolFlags.Interface) {
243
+ const t = checker.getDeclaredTypeOfSymbol(sym);
244
+ record.sig = t
245
+ .getCallSignatures()
246
+ .map((s) => signatureText(checker, s))
247
+ .sort();
248
+ walkMembers(path, t, "#", pkgName, entry, segs);
249
+ }
250
+ else if (f & ts.SymbolFlags.TypeAlias) {
251
+ const t = checker.getDeclaredTypeOfSymbol(sym);
252
+ const tps = sym.declarations?.[0]?.typeParameters?.map((tp) => tp.name.text) ?? [];
253
+ record.sig = [typeText(checker, t, tps)];
254
+ if (t.flags & ts.TypeFlags.Object && !(t.flags & ts.TypeFlags.Union))
255
+ walkMembers(path, t, "#", pkgName, entry, segs);
256
+ }
257
+ else if (f & ts.SymbolFlags.Enum) {
258
+ const t = checker.getTypeOfSymbol(sym);
259
+ for (const m of checker.getPropertiesOfType(t)) {
260
+ emit({
261
+ path: formatPath(pkgName, entry, [
262
+ ...segs,
263
+ { name: m.getName(), sep: "." },
264
+ ]),
265
+ kind: "enum-member",
266
+ sig: [typeText(checker, checker.getTypeOfSymbol(m))],
267
+ deprecated: deprecatedOf(m),
268
+ });
269
+ }
270
+ }
271
+ else if (f &
272
+ (ts.SymbolFlags.Function |
273
+ ts.SymbolFlags.Variable |
274
+ ts.SymbolFlags.Property)) {
275
+ const decl = sym.valueDeclaration ?? sym.declarations?.[0];
276
+ const t = decl
277
+ ? checker.getTypeOfSymbolAtLocation(sym, decl)
278
+ : checker.getTypeOfSymbol(sym);
279
+ const sigs = callSigs(t, path);
280
+ record.sig = sigs.length > 0 ? sigs : [typeText(checker, t)];
281
+ if (sigs.length === 0) {
282
+ const ts2 = typeSymbolOf(t);
283
+ if (ts2)
284
+ pendingReturns.push({ path, field: "instanceOf", target: ts2 });
285
+ else if (f & ts.SymbolFlags.Variable &&
286
+ t.flags & ts.TypeFlags.Object &&
287
+ nsDepth < NAMESPACE_DEPTH) {
288
+ walkMembers(path, t, ".", pkgName, entry, segs);
289
+ }
290
+ }
291
+ // function merged with a namespace: createApp(), createApp.json()
292
+ if (f & ts.SymbolFlags.NamespaceModule && nsDepth < NAMESPACE_DEPTH) {
293
+ for (const e of checker.getExportsOfModule(sym))
294
+ walk(pkgName, entry, [...segs, { name: e.getName(), sep: "." }], e, nsDepth + 1);
295
+ }
296
+ }
297
+ // a class, interface or type merged with a namespace: Dialog.Root and Dialog.Root.Props
298
+ if (f &
299
+ (ts.SymbolFlags.Class |
300
+ ts.SymbolFlags.Interface |
301
+ ts.SymbolFlags.TypeAlias |
302
+ ts.SymbolFlags.Enum) &&
303
+ f & ts.SymbolFlags.NamespaceModule &&
304
+ nsDepth < NAMESPACE_DEPTH) {
305
+ for (const e of checker.getExportsOfModule(sym))
306
+ walk(pkgName, entry, [...segs, { name: e.getName(), sep: "." }], e, nsDepth + 1);
307
+ }
308
+ }
309
+ catch (error) {
310
+ record.sig = [`#error:${String(error).slice(0, 40)}`];
311
+ }
312
+ };
313
+ for (const e of entries) {
314
+ const sf = program.getSourceFile(`${PKG_ROOT}/${e.typesFile}`);
315
+ if (!sf)
316
+ continue;
317
+ const moduleSym = checker.getSymbolAtLocation(sf);
318
+ if (!moduleSym)
319
+ continue;
320
+ const exportEquals = !!moduleSym.exports?.has(ts.InternalSymbolName.ExportEquals);
321
+ const externalReexports = unresolvedReexports(sf, pkg, options, host);
322
+ surfaceEntries[e.subpath] = {
323
+ typesFile: e.typesFile,
324
+ exportEquals,
325
+ ...(externalReexports.length > 0 ? { externalReexports } : {}),
326
+ };
327
+ if (exportEquals) {
328
+ const target = resolveAlias(moduleSym.exports.get(ts.InternalSymbolName.ExportEquals));
329
+ const decl = target.valueDeclaration ?? target.declarations?.[0];
330
+ const path = formatPath(pkg, e.subpath, []);
331
+ if (decl &&
332
+ target.flags &
333
+ (ts.SymbolFlags.Function |
334
+ ts.SymbolFlags.Variable |
335
+ ts.SymbolFlags.Class)) {
336
+ const t = checker.getTypeOfSymbolAtLocation(target, decl);
337
+ const construct = t.getConstructSignatures();
338
+ const callable = t.getCallSignatures().length > 0 || construct.length > 0;
339
+ const record = {
340
+ path,
341
+ kind: target.flags & ts.SymbolFlags.Class
342
+ ? "class"
343
+ : callable
344
+ ? "function"
345
+ : "variable",
346
+ sig: [],
347
+ deprecated: deprecatedOf(target),
348
+ };
349
+ if (emit(record)) {
350
+ record.sig = callable
351
+ ? [
352
+ ...callSigs(t, path),
353
+ ...construct.map((s) => `new ${signatureText(checker, s)}`),
354
+ ].sort()
355
+ : [typeText(checker, t)];
356
+ if (construct[0]) {
357
+ const rs = typeSymbolOf(checker.getReturnTypeOfSignature(construct[0]));
358
+ if (rs)
359
+ pendingReturns.push({ path, field: "instanceOf", target: rs });
360
+ }
361
+ // `export = globals` where globals is an object: its properties are the module's names
362
+ if (!callable && t.flags & ts.TypeFlags.Object)
363
+ walkMembers(path, t, ".", pkg, e.subpath, []);
364
+ }
365
+ }
366
+ }
367
+ let exports = [];
368
+ try {
369
+ exports = checker.getExportsOfModule(moduleSym);
370
+ }
371
+ catch {
372
+ continue;
373
+ }
374
+ for (const ex of [...exports].sort((a, b) => a.getName().localeCompare(b.getName()))) {
375
+ if (ex.escapedName === ts.InternalSymbolName.ExportEquals)
376
+ continue;
377
+ walk(pkg, e.subpath, [{ name: ex.getName(), sep: "." }], ex, 0);
378
+ }
379
+ }
380
+ for (const r of pendingReturns) {
381
+ const target = firstPath.get(r.target);
382
+ const rec = symbols[r.path];
383
+ if (target && rec)
384
+ rec[r.field] = target;
385
+ }
386
+ if (hasUnresolvedImports(program, options, host, pkg))
387
+ flags.add("external-types-unresolved");
388
+ return {
389
+ ok: true,
390
+ surface: {
391
+ pkg,
392
+ version,
393
+ integrity,
394
+ ts: ts.version,
395
+ algo: ALGO,
396
+ typesFrom: pkg.startsWith("@types/") ? "@types" : "package",
397
+ entries: surfaceEntries,
398
+ symbols,
399
+ flags: [...flags].sort(),
400
+ },
401
+ };
402
+ }
403
+ // On an instantiated owner `this` already reads as the owner's name: `C extends ... ? StringSchema & X : StringSchema`
404
+ function mentionsOwner(text, ownerPath) {
405
+ const owner = parsePath(ownerPath).segs.at(-1)?.name;
406
+ if (/(?<![\w$.])this(?![\w$])/.test(text))
407
+ return true;
408
+ return (!!owner &&
409
+ new RegExp(`(?<![\\w$.])${owner.replace(/[$]/g, "\\$")}(?![\\w$])`).test(text));
410
+ }
411
+ function kindOf(f) {
412
+ if (f & ts.SymbolFlags.Class)
413
+ return "class";
414
+ if (f & ts.SymbolFlags.Interface)
415
+ return "interface";
416
+ if (f & ts.SymbolFlags.TypeAlias)
417
+ return "type";
418
+ if (f & ts.SymbolFlags.Enum)
419
+ return "enum";
420
+ if (f & ts.SymbolFlags.Function)
421
+ return "function";
422
+ if (f & (ts.SymbolFlags.ValueModule | ts.SymbolFlags.NamespaceModule))
423
+ return "namespace";
424
+ return "variable";
425
+ }
426
+ function unresolvedReexports(sf, pkg, options, host) {
427
+ const out = [];
428
+ for (const stmt of sf.statements) {
429
+ if (!ts.isExportDeclaration(stmt) ||
430
+ !stmt.moduleSpecifier ||
431
+ !ts.isStringLiteral(stmt.moduleSpecifier))
432
+ continue;
433
+ const name = stmt.moduleSpecifier.text;
434
+ if (name.startsWith(".") || name === pkg || name.startsWith(`${pkg}/`))
435
+ continue;
436
+ if (!ts.resolveModuleName(name, sf.fileName, options, host).resolvedModule &&
437
+ !out.includes(name))
438
+ out.push(name);
439
+ }
440
+ return out;
441
+ }
442
+ // Imports of the package's own dependencies resolve to nothing in the virtual host, the same way in
443
+ // both versions. Worth a flag, not worth a full type check to find.
444
+ function hasUnresolvedImports(program, options, host, pkg) {
445
+ let checked = 0;
446
+ for (const sf of program.getSourceFiles()) {
447
+ if (!sf.fileName.startsWith(`${PKG_ROOT}/`))
448
+ continue;
449
+ for (const imp of ts.preProcessFile(sf.text, true, true).importedFiles) {
450
+ const name = imp.fileName;
451
+ if (name.startsWith(".") || name === pkg || name.startsWith(`${pkg}/`))
452
+ continue;
453
+ if (++checked > 500)
454
+ return false;
455
+ if (!ts.resolveModuleName(name, sf.fileName, options, host).resolvedModule)
456
+ return true;
457
+ }
458
+ }
459
+ return false;
460
+ }
@@ -0,0 +1,6 @@
1
+ import ts from "typescript";
2
+ export declare const ALGO = 8;
3
+ export declare function typeText(checker: ts.TypeChecker, type: ts.Type, typeParams?: string[]): string;
4
+ export declare function signatureText(checker: ts.TypeChecker, sig: ts.Signature): string;
5
+ export declare function normalizeTypeText(input: string): string;
6
+ //# sourceMappingURL=signature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"signature.d.ts","sourceRoot":"","sources":["../../src/surface/signature.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAM3B,eAAO,MAAM,IAAI,IAAI,CAAA;AAWrB,wBAAgB,QAAQ,CACtB,OAAO,EAAE,EAAE,CAAC,WAAW,EACvB,IAAI,EAAE,EAAE,CAAC,IAAI,EACb,UAAU,GAAE,MAAM,EAAO,GACxB,MAAM,CAIR;AAID,wBAAgB,aAAa,CAC3B,OAAO,EAAE,EAAE,CAAC,WAAW,EACvB,GAAG,EAAE,EAAE,CAAC,SAAS,GAChB,MAAM,CAmBR;AAcD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOvD"}