@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,588 @@
1
+ import { level0Names } from "../symbol-path.js";
2
+ const MAX_DERIVED_HOPS = 3;
3
+ const MAX_DEPTH = 12;
4
+ export async function link(input) {
5
+ const { facts } = input;
6
+ const acc = new Map();
7
+ const accFor = (t) => {
8
+ let a = acc.get(t.installedId);
9
+ if (!a) {
10
+ a = { refs: [], blind: new Map(), opaque: new Map(), proxied: false };
11
+ acc.set(t.installedId, a);
12
+ }
13
+ names.set(t.installedId, t.pkg);
14
+ return a;
15
+ };
16
+ const names = new Map();
17
+ const note = (m, k, s) => {
18
+ const list = m.get(k) ?? [];
19
+ list.push(s);
20
+ m.set(k, list);
21
+ };
22
+ // ---------------------------------------------------------------- resolution, memoized per file
23
+ const resolved = new Map();
24
+ const resolveIn = async (rel, spec) => {
25
+ let m = resolved.get(rel);
26
+ if (!m) {
27
+ m = new Map();
28
+ resolved.set(rel, m);
29
+ }
30
+ let t = m.get(spec);
31
+ if (!t) {
32
+ t = await input.resolver.resolve(rel, spec);
33
+ m.set(spec, t);
34
+ }
35
+ return t;
36
+ };
37
+ // pre-resolve every specifier so the synchronous walks below never wait
38
+ for (const [rel, f] of facts) {
39
+ const specs = new Set([
40
+ ...f.imports.map((i) => i.specifier),
41
+ ...f.reExports.map((r) => r.specifier),
42
+ ...f.inline.map((i) => i.specifier),
43
+ ...f.sideEffects.map((s) => s.specifier),
44
+ ]);
45
+ for (const s of specs)
46
+ await resolveIn(rel, s);
47
+ }
48
+ const target = (rel, spec) => resolved.get(rel)?.get(spec) ?? { kind: "unknown" };
49
+ const site = (rel, p, typeOnly = false, via) => ({
50
+ file: rel,
51
+ line: p.line,
52
+ col: p.col,
53
+ typeOnly,
54
+ text: p.text,
55
+ ...(via && via.length > 0 ? { via } : {}),
56
+ });
57
+ // ---------------------------------------------------------------- export origins
58
+ const originMemo = new Map();
59
+ const exportOrigin = (rel, name, depth = 0, seen = new Set()) => {
60
+ const key = `${rel}\0${name}`;
61
+ const memo = originMemo.get(key);
62
+ if (memo)
63
+ return memo;
64
+ if (depth > MAX_DEPTH || seen.has(key))
65
+ return [];
66
+ seen.add(key);
67
+ const f = facts.get(rel);
68
+ if (!f)
69
+ return [];
70
+ const out = [];
71
+ const viaHere = (o) => o.kind === "local"
72
+ ? o
73
+ : { ...o, via: o.via.includes(rel) ? o.via : [rel, ...o.via] };
74
+ const fromBinding = (b) => {
75
+ const t = target(rel, b.specifier);
76
+ if (t.kind === "pkg")
77
+ return [{ kind: "pkg", target: t, binding: b.binding, via: [rel] }];
78
+ if (t.kind === "file") {
79
+ if (b.binding.kind === "named")
80
+ return exportOrigin(t.rel, b.binding.imported, depth + 1, seen).map(viaHere);
81
+ if (b.binding.kind === "default")
82
+ return exportOrigin(t.rel, "default", depth + 1, seen).map(viaHere);
83
+ return [{ kind: "file-namespace", rel: t.rel, via: [rel] }];
84
+ }
85
+ return [];
86
+ };
87
+ const derivedOrigin = (local) => {
88
+ const derived = derivedSources(rel).get(local);
89
+ if (!derived)
90
+ return [];
91
+ return derived.map((d) => ({
92
+ kind: "derived",
93
+ target: d.target,
94
+ hops: d.hops + 1,
95
+ via: [rel],
96
+ ...(d.origin ? { origin: d.origin } : {}),
97
+ }));
98
+ };
99
+ for (const el of f.exportedLocals) {
100
+ if (el.exported !== name)
101
+ continue;
102
+ const b = f.imports.find((i) => i.local === el.local);
103
+ if (b)
104
+ out.push(...fromBinding(b));
105
+ else {
106
+ const d = derivedOrigin(el.local);
107
+ out.push(...(d.length > 0 ? d : [{ kind: "local" }]));
108
+ }
109
+ }
110
+ if (out.length === 0 && f.declaredExports.includes(name)) {
111
+ const d = derivedOrigin(name);
112
+ out.push(...(d.length > 0 ? d : [{ kind: "local" }]));
113
+ }
114
+ if (out.length === 0) {
115
+ for (const r of f.reExports) {
116
+ const t = target(rel, r.specifier);
117
+ if (r.kind === "named" || r.kind === "star-as") {
118
+ const hit = r.names.find((n) => n.exported === name);
119
+ if (!hit)
120
+ continue;
121
+ if (t.kind === "pkg") {
122
+ const binding = hit.imported === "*"
123
+ ? { kind: "namespace" }
124
+ : hit.imported === "default"
125
+ ? { kind: "default" }
126
+ : { kind: "named", imported: hit.imported };
127
+ out.push({ kind: "pkg", target: t, binding, via: [rel] });
128
+ }
129
+ else if (t.kind === "file") {
130
+ if (hit.imported === "*")
131
+ out.push({ kind: "file-namespace", rel: t.rel, via: [rel] });
132
+ else
133
+ out.push(...exportOrigin(t.rel, hit.imported, depth + 1, seen).map(viaHere));
134
+ }
135
+ }
136
+ }
137
+ }
138
+ if (out.length === 0 && name !== "default") {
139
+ // export * never forwards default; local files first, a package only for what they lack
140
+ const stars = f.reExports.filter((r) => r.kind === "star");
141
+ for (const r of stars) {
142
+ const t = target(rel, r.specifier);
143
+ if (t.kind === "file")
144
+ out.push(...exportOrigin(t.rel, name, depth + 1, seen).map(viaHere));
145
+ }
146
+ if (out.length === 0) {
147
+ for (const r of stars) {
148
+ const t = target(rel, r.specifier);
149
+ if (t.kind === "pkg")
150
+ out.push({
151
+ kind: "pkg",
152
+ target: t,
153
+ binding: { kind: "named", imported: name },
154
+ via: [rel],
155
+ });
156
+ }
157
+ }
158
+ }
159
+ seen.delete(key);
160
+ if (depth === 0)
161
+ originMemo.set(key, out);
162
+ return out;
163
+ };
164
+ // Packages a local file forwards wholesale or by name: what makes it a proxy.
165
+ const forwardMemo = new Map();
166
+ const forwardedPkgs = (rel, seen = new Set()) => {
167
+ const memo = forwardMemo.get(rel);
168
+ if (memo)
169
+ return memo;
170
+ const out = new Map();
171
+ if (seen.has(rel))
172
+ return out;
173
+ seen.add(rel);
174
+ const f = facts.get(rel);
175
+ if (f) {
176
+ for (const r of f.reExports) {
177
+ const t = target(rel, r.specifier);
178
+ if (t.kind === "pkg")
179
+ out.set(t.installedId, t);
180
+ else if (t.kind === "file")
181
+ for (const [k, v] of forwardedPkgs(t.rel, seen))
182
+ out.set(k, v);
183
+ }
184
+ for (const el of f.exportedLocals) {
185
+ const b = f.imports.find((i) => i.local === el.local);
186
+ if (!b)
187
+ continue;
188
+ const t = target(rel, b.specifier);
189
+ if (t.kind === "pkg")
190
+ out.set(t.installedId, t);
191
+ else if (t.kind === "file")
192
+ for (const [k, v] of forwardedPkgs(t.rel, seen))
193
+ out.set(k, v);
194
+ }
195
+ }
196
+ forwardMemo.set(rel, out);
197
+ return out;
198
+ };
199
+ const derivedMemo = new Map();
200
+ const derivedInProgress = new Set();
201
+ const derivedSources = (rel) => {
202
+ const memo = derivedMemo.get(rel);
203
+ if (memo)
204
+ return memo;
205
+ const out = new Map();
206
+ if (derivedInProgress.has(rel))
207
+ return out;
208
+ derivedInProgress.add(rel);
209
+ const f = facts.get(rel);
210
+ if (f) {
211
+ const push = (name, src) => {
212
+ const list = out.get(name) ?? [];
213
+ if (!list.some((x) => x.target.installedId === src.target.installedId))
214
+ list.push(src);
215
+ out.set(name, list);
216
+ };
217
+ for (const r of f.references) {
218
+ if (!r.derivedInto)
219
+ continue;
220
+ const b = f.imports.find((i) => i.local === r.local);
221
+ if (b)
222
+ for (const t of pkgTargetsOfBinding(rel, b, r.chain, r.callSelf))
223
+ push(r.derivedInto, t);
224
+ }
225
+ // callback parameters: what a package hands to your function is a value of that package,
226
+ // of a type the scan cannot name, so only its member names are known
227
+ const callbackSources = () => {
228
+ for (const r of f.references) {
229
+ if (!r.callbackInto)
230
+ continue;
231
+ const b = f.imports.find((i) => i.local === r.local);
232
+ const sources = b
233
+ ? pkgTargetsOfBinding(rel, b, r.chain, r.callSelf)
234
+ : (out.get(r.local) ?? []);
235
+ for (const src of sources)
236
+ for (const cb of r.callbackInto)
237
+ push(cb, { target: src.target, hops: src.hops });
238
+ }
239
+ for (const u of f.inline) {
240
+ if (!u.callbackInto)
241
+ continue;
242
+ const t = target(rel, u.specifier);
243
+ if (t.kind === "pkg")
244
+ for (const cb of u.callbackInto)
245
+ push(cb, { target: t, hops: 0 });
246
+ }
247
+ };
248
+ callbackSources();
249
+ for (const u of f.inline) {
250
+ if (!u.derivedInto)
251
+ continue;
252
+ const t = target(rel, u.specifier);
253
+ if (t.kind === "pkg" && u.binding.kind !== "derived") {
254
+ push(u.derivedInto, {
255
+ target: t,
256
+ hops: 0,
257
+ origin: {
258
+ binding: u.binding,
259
+ entry: t.entry,
260
+ chain: u.chain,
261
+ callSelf: u.callSelf,
262
+ },
263
+ });
264
+ }
265
+ }
266
+ // chains of derived values inside the file: const app = createApp(); const r = app.route()
267
+ for (let round = 0; round < 3; round++) {
268
+ for (const r of f.references) {
269
+ if (!r.derivedInto || f.imports.some((i) => i.local === r.local))
270
+ continue;
271
+ for (const src of out.get(r.local) ?? []) {
272
+ push(r.derivedInto, {
273
+ ...src,
274
+ ...(src.origin
275
+ ? { origin: extendOrigin(src.origin, r.chain, r.callSelf) }
276
+ : {}),
277
+ });
278
+ }
279
+ }
280
+ callbackSources();
281
+ }
282
+ }
283
+ derivedInProgress.delete(rel);
284
+ derivedMemo.set(rel, out);
285
+ return out;
286
+ };
287
+ // The packages a binding's value comes from, following local files.
288
+ const pkgTargetsOfBinding = (rel, b, chain, callSelf) => {
289
+ const t = target(rel, b.specifier);
290
+ const direct = b.binding.kind === "derived" ? undefined : b.binding;
291
+ if (t.kind === "pkg")
292
+ return direct
293
+ ? [
294
+ {
295
+ target: t,
296
+ hops: 0,
297
+ origin: { binding: direct, entry: t.entry, chain, callSelf },
298
+ },
299
+ ]
300
+ : [];
301
+ if (t.kind !== "file")
302
+ return [];
303
+ let origins;
304
+ let rest = chain;
305
+ let restCall = callSelf;
306
+ if (b.binding.kind === "named")
307
+ origins = exportOrigin(t.rel, b.binding.imported);
308
+ else if (b.binding.kind === "default")
309
+ origins = exportOrigin(t.rel, "default");
310
+ else {
311
+ // `import * as v from "./schema"; const s = v.object()`: the head names the export
312
+ const head = chain[0];
313
+ origins = head ? exportOrigin(t.rel, head.name) : [];
314
+ rest = chain.slice(1);
315
+ restCall = !!head?.call;
316
+ }
317
+ const out = [];
318
+ for (const o of origins) {
319
+ if (o.kind === "pkg" && o.binding.kind !== "derived") {
320
+ out.push({
321
+ target: o.target,
322
+ hops: 0,
323
+ origin: {
324
+ binding: o.binding,
325
+ entry: o.target.entry,
326
+ chain: rest,
327
+ callSelf: restCall,
328
+ },
329
+ });
330
+ }
331
+ else if (o.kind === "derived") {
332
+ out.push({
333
+ target: o.target,
334
+ hops: o.hops,
335
+ ...(o.origin
336
+ ? { origin: extendOrigin(o.origin, rest, restCall) }
337
+ : {}),
338
+ });
339
+ }
340
+ }
341
+ return out;
342
+ };
343
+ // ---------------------------------------------------------------- attribution
344
+ const emitThrough = (rel, origins, chain, callSelf, s, specifier, escape, computed, depth = 0) => {
345
+ for (const o of origins) {
346
+ if (o.kind === "local")
347
+ continue;
348
+ if (o.kind === "pkg") {
349
+ const a = accFor(o.target);
350
+ a.proxied = true;
351
+ const viaSite = { ...s, via: o.via };
352
+ if (escape && o.binding.kind !== "named")
353
+ note(a.blind, "namespace-escape", viaSite);
354
+ else if (computed && chain.length === 0)
355
+ note(a.blind, "computed-member", viaSite);
356
+ else
357
+ a.refs.push(rawRef(o.target, specifier, o.binding, chain, callSelf, viaSite));
358
+ }
359
+ else if (o.kind === "derived") {
360
+ const a = accFor(o.target);
361
+ const viaSite = { ...s, via: o.via };
362
+ if (o.hops > MAX_DERIVED_HOPS)
363
+ note(a.blind, "derived-escape", viaSite);
364
+ else if (chain.length > 0)
365
+ a.refs.push(rawRef(o.target, specifier, { kind: "derived" }, chain, callSelf, viaSite, o.origin));
366
+ }
367
+ else if (o.kind === "file-namespace") {
368
+ if (depth > MAX_DEPTH)
369
+ continue;
370
+ const [head, ...rest] = chain;
371
+ if (!head) {
372
+ if (escape)
373
+ for (const t of forwardedPkgs(o.rel).values())
374
+ note(accFor(t).blind, "namespace-escape", { ...s, via: o.via });
375
+ continue;
376
+ }
377
+ const next = exportOrigin(o.rel, head.name).map((x) => x.kind === "local"
378
+ ? x
379
+ : {
380
+ ...x,
381
+ via: [...o.via, ...x.via.filter((v) => !o.via.includes(v))],
382
+ });
383
+ // `ns.email()` through a namespace of a proxy: the head becomes the named import
384
+ const adjusted = next.map((x) => x.kind === "pkg" && x.binding.kind === "named"
385
+ ? {
386
+ ...x,
387
+ binding: {
388
+ kind: "named",
389
+ imported: x.binding.imported,
390
+ },
391
+ }
392
+ : x);
393
+ emitThrough(rel, adjusted, rest, head.call, s, specifier, false, computed, depth + 1);
394
+ }
395
+ }
396
+ };
397
+ const globalCounts = new Map();
398
+ const unresolvedLocal = [];
399
+ for (const [rel, f] of facts) {
400
+ if (f.unparseable)
401
+ note(globalCounts, "unparseable-file", site(rel, { line: 1, col: 1, text: "" }));
402
+ for (const p of f.nonLiteralRequire)
403
+ note(globalCounts, "require-nonliteral", site(rel, p));
404
+ for (const p of f.nonLiteralImport)
405
+ note(globalCounts, "dynamic-import-nonliteral", site(rel, p));
406
+ const bindingByLocal = new Map(f.imports.map((i) => [i.local, i]));
407
+ for (const b of f.imports) {
408
+ const t = target(rel, b.specifier);
409
+ if (t.kind === "unresolved-local")
410
+ unresolvedLocal.push(site(rel, b.pos));
411
+ }
412
+ for (const r of f.references) {
413
+ const s = site(rel, r.pos, r.typeOnly);
414
+ const b = bindingByLocal.get(r.local);
415
+ if (b) {
416
+ const t = target(rel, b.specifier);
417
+ if (t.kind === "pkg") {
418
+ const a = accFor(t);
419
+ if (r.escape)
420
+ note(a.blind, "namespace-escape", s);
421
+ else if (r.computed && r.chain.length === 0)
422
+ note(a.blind, "computed-member", s);
423
+ else
424
+ a.refs.push(rawRef(t, b.specifier, b.binding, r.chain, r.callSelf, s));
425
+ if (r.computed && r.chain.length > 0)
426
+ note(a.blind, "computed-member", s);
427
+ }
428
+ else if (t.kind === "file") {
429
+ let origins;
430
+ if (b.binding.kind === "named")
431
+ origins = exportOrigin(t.rel, b.binding.imported);
432
+ else if (b.binding.kind === "default")
433
+ origins = exportOrigin(t.rel, "default");
434
+ else
435
+ origins = [{ kind: "file-namespace", rel: t.rel, via: [] }];
436
+ emitThrough(rel, origins, r.chain, r.callSelf, s, b.specifier, r.escape, r.computed);
437
+ }
438
+ continue;
439
+ }
440
+ // a reference on a derived value of this file
441
+ for (const src of derivedSources(rel).get(r.local) ?? []) {
442
+ const a = accFor(src.target);
443
+ if (src.hops > MAX_DERIVED_HOPS)
444
+ note(a.blind, "derived-escape", s);
445
+ else if (r.chain.length > 0)
446
+ a.refs.push(rawRef(src.target, src.target.pkg, { kind: "derived" }, r.chain, r.callSelf, s, src.origin));
447
+ }
448
+ }
449
+ for (const u of f.inline) {
450
+ const t = target(rel, u.specifier);
451
+ const s = site(rel, u.pos);
452
+ if (t.kind === "pkg") {
453
+ const a = accFor(t);
454
+ if (u.escape)
455
+ note(a.blind, u.binding.kind === "namespace"
456
+ ? "dynamic-import-escape"
457
+ : "namespace-escape", s);
458
+ else
459
+ a.refs.push(rawRef(t, u.specifier, u.binding, u.chain, u.callSelf, s));
460
+ }
461
+ else if (t.kind === "file" && !u.escape) {
462
+ emitThrough(rel, [{ kind: "file-namespace", rel: t.rel, via: [] }], u.chain, u.callSelf, s, u.specifier, false, false);
463
+ }
464
+ }
465
+ for (const se of f.sideEffects) {
466
+ const t = target(rel, se.specifier);
467
+ if (t.kind === "pkg")
468
+ note(accFor(t).opaque, "side-effect-import", site(rel, se.pos));
469
+ }
470
+ for (const p of f.prefixImports) {
471
+ const t = await resolveIn(rel, p.prefix.replace(/\/$/, ""));
472
+ if (t.kind === "pkg")
473
+ note(accFor(t).blind, "prefix-dynamic-import", site(rel, p.pos));
474
+ }
475
+ // a file that forwards a package: mark it so unresolved local imports count against that package
476
+ for (const t of forwardedPkgs(rel).values())
477
+ accFor(t).proxied = true;
478
+ }
479
+ // public entry points of publishable manifests that forward a package
480
+ for (const m of input.inventory.manifests) {
481
+ if (m.private)
482
+ continue;
483
+ const dirRel = relOf(input.root, m.dir);
484
+ const base = dirRel === "." ? "" : `${dirRel}/`;
485
+ const entries = new Set();
486
+ for (const e of [m.main, m.module, m.types])
487
+ if (e)
488
+ entries.add(e);
489
+ collectExportTargets(m.exports, entries);
490
+ for (const e of entries) {
491
+ const hit = input.resolver.tryFile(base + e.replace(/^\.\//, ""));
492
+ if (!hit || !facts.has(hit))
493
+ continue;
494
+ for (const t of forwardedPkgs(hit).values())
495
+ note(accFor(t).blind, "public-reexport", site(hit, { line: 1, col: 1, text: "" }));
496
+ }
497
+ }
498
+ // every file was scanned, so the proxies are all known; what stays unknown is who reached them
499
+ // through an import that did not resolve
500
+ for (const a of acc.values()) {
501
+ if (a.proxied && unresolvedLocal.length > 0)
502
+ a.blind.set("unresolved-local-import", unresolvedLocal);
503
+ }
504
+ for (const s of input.globalSkipped)
505
+ note(globalCounts, "skipped-generated", s);
506
+ const packages = {};
507
+ for (const [id, a] of acc) {
508
+ const strong = new Set();
509
+ const weak = new Set();
510
+ for (const r of a.refs) {
511
+ const n = level0Names(r);
512
+ for (const x of n.strong)
513
+ strong.add(x);
514
+ for (const x of n.weak)
515
+ weak.add(x);
516
+ }
517
+ for (const x of strong)
518
+ weak.delete(x);
519
+ packages[id] = {
520
+ installedId: id,
521
+ pkg: names.get(id) ?? id,
522
+ refs: a.refs,
523
+ strongNames: [...strong].sort(),
524
+ weakNames: [...weak].sort(),
525
+ files: new Set(a.refs.map((r) => r.site.file)).size,
526
+ opaque: counted(a.opaque),
527
+ blindSpots: counted(a.blind),
528
+ };
529
+ }
530
+ return {
531
+ scannedFiles: facts.size,
532
+ packages,
533
+ global: counted(globalCounts),
534
+ acc,
535
+ };
536
+ }
537
+ function rawRef(t, specifier, binding, chain, callSelf, site, origin) {
538
+ return {
539
+ installedId: t.installedId,
540
+ pkg: t.pkg,
541
+ specifier,
542
+ entry: origin?.entry ?? t.entry,
543
+ binding,
544
+ chain,
545
+ callSelf,
546
+ site,
547
+ ...(origin ? { origin } : {}),
548
+ };
549
+ }
550
+ // The value built by `origin`, then read further: `schema` then `schema.safeParse()`.
551
+ export function extendOrigin(origin, chain, callSelf) {
552
+ const base = origin.chain.map((s) => ({ ...s }));
553
+ let self = origin.callSelf;
554
+ if (callSelf) {
555
+ const last = base[base.length - 1];
556
+ if (last)
557
+ last.call = true;
558
+ else
559
+ self = true;
560
+ }
561
+ return { ...origin, chain: [...base, ...chain], callSelf: self };
562
+ }
563
+ export function counted(m) {
564
+ return [...m.entries()]
565
+ .filter(([, sites]) => sites.length > 0)
566
+ .map(([kind, sites]) => ({
567
+ kind,
568
+ count: sites.length,
569
+ examples: sites.slice(0, 3),
570
+ }))
571
+ .sort((a, b) => a.kind.localeCompare(b.kind));
572
+ }
573
+ function collectExportTargets(exp, out) {
574
+ if (typeof exp === "string")
575
+ out.add(exp);
576
+ else if (Array.isArray(exp))
577
+ for (const x of exp)
578
+ collectExportTargets(x, out);
579
+ else if (exp && typeof exp === "object")
580
+ for (const v of Object.values(exp))
581
+ collectExportTargets(v, out);
582
+ }
583
+ function relOf(root, abs) {
584
+ const r = abs.startsWith(root)
585
+ ? abs.slice(root.length).replace(/^\/+/, "")
586
+ : abs;
587
+ return r || ".";
588
+ }
@@ -0,0 +1,7 @@
1
+ import type { FileFacts } from "./parse.ts";
2
+ import type { LocalResolver } from "./resolve-local.ts";
3
+ import type { Inventory, OpaqueKind, Site } from "../model.ts";
4
+ export declare const CONVENTION_FRAMEWORKS: Set<string>;
5
+ export type OpaqueHits = Map<string, Map<OpaqueKind, Site[]>>;
6
+ export declare function findOpaque(root: string, files: string[], facts: Map<string, FileFacts>, inventory: Inventory, resolver: LocalResolver): Promise<OpaqueHits>;
7
+ //# sourceMappingURL=opaque.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"opaque.d.ts","sourceRoot":"","sources":["../../src/usage/opaque.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAC3C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAEvD,OAAO,KAAK,EAAgB,SAAS,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,aAAa,CAAA;AAI5E,eAAO,MAAM,qBAAqB,aAchC,CAAA;AAKF,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;AAE7D,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EAAE,EACf,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,SAAS,CAAC,EAC7B,SAAS,EAAE,SAAS,EACpB,QAAQ,EAAE,aAAa,GACtB,OAAO,CAAC,UAAU,CAAC,CA6HrB"}