@moda-ai/cli 1.55.67 → 1.55.69

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.
@@ -1,1183 +0,0 @@
1
- // src/harness-wrangler-bindings.ts
2
- import { lstatSync, readdirSync, readFileSync } from "node:fs";
3
- import { isAbsolute, join, posix, relative } from "node:path";
4
- var WRANGLER_FILE = /^wrangler(\.[\w-]+)?\.jsonc?$/;
5
- var ANY_WRANGLER_FILE = /^wrangler(?:\.[\w-]+)?\.(?:jsonc?|toml)$/;
6
- var SKIPPED_DIRS = new Set(["node_modules", ".git", ".wrangler", "dist", "build", ".next", ".turbo", "vendor"]);
7
- var MAX_DEPTH = 4;
8
- var AGENT_CLASS = /Agent(?:V\d+)?$/;
9
- var TEST_SEGMENT = /^(?:test|tests|__tests__|test-fixtures|fixtures?|__fixtures__|e2e|mocks?)$/i;
10
- var TEST_TOKEN = /(?:^|[._-])(?:test|tests|spec|e2e|fixtures?|mocks?)(?:$|[._-])/i;
11
- function isTestWorkerConfig(input) {
12
- const segments = input.path.split("/");
13
- const file = segments[segments.length - 1] ?? "";
14
- const variant = file.replace(/^wrangler\.?/, "").replace(/\.(?:jsonc?|toml)$/, "");
15
- if (segments.slice(0, -1).some((segment) => TEST_SEGMENT.test(segment)))
16
- return true;
17
- if (variant && TEST_TOKEN.test(variant))
18
- return true;
19
- if (typeof input.name === "string" && TEST_TOKEN.test(input.name))
20
- return true;
21
- return (input.mains ?? []).some((main) => typeof main === "string" && main.split("/").slice(0, -1).some((segment) => TEST_SEGMENT.test(segment)));
22
- }
23
- function stripJsonc(text) {
24
- let out = "";
25
- let inString = false;
26
- for (let i = 0;i < text.length; i += 1) {
27
- const ch = text[i];
28
- if (inString) {
29
- out += ch;
30
- if (ch === "\\") {
31
- out += text[i + 1] ?? "";
32
- i += 1;
33
- } else if (ch === '"') {
34
- inString = false;
35
- }
36
- continue;
37
- }
38
- if (ch === '"') {
39
- inString = true;
40
- out += ch;
41
- } else if (ch === "/" && text[i + 1] === "/") {
42
- while (i < text.length && text[i] !== `
43
- `)
44
- i += 1;
45
- out += `
46
- `;
47
- } else if (ch === "/" && text[i + 1] === "*") {
48
- i += 2;
49
- while (i < text.length && !(text[i] === "*" && text[i + 1] === "/")) {
50
- if (text[i] === `
51
- `)
52
- out += `
53
- `;
54
- i += 1;
55
- }
56
- i += 1;
57
- } else {
58
- out += ch;
59
- }
60
- }
61
- return out.replace(/,(\s*[}\]])/g, "$1");
62
- }
63
- function findWranglerFiles(rootDir, pattern = WRANGLER_FILE) {
64
- const found = [];
65
- const walk = (dir, depth) => {
66
- let entries;
67
- try {
68
- entries = readdirSync(dir);
69
- } catch {
70
- return;
71
- }
72
- for (const name of entries) {
73
- const abs = join(dir, name);
74
- let stat;
75
- try {
76
- stat = lstatSync(abs);
77
- } catch {
78
- continue;
79
- }
80
- if (stat.isDirectory()) {
81
- if (depth < MAX_DEPTH && !SKIPPED_DIRS.has(name) && !name.startsWith("."))
82
- walk(abs, depth + 1);
83
- } else if (stat.isFile() && pattern.test(name)) {
84
- found.push(abs);
85
- }
86
- }
87
- };
88
- walk(rootDir, 0);
89
- return found.sort();
90
- }
91
- function lineOf(text, className) {
92
- const index = text.search(new RegExp(`"class_name"\\s*:\\s*"${className.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}"`));
93
- return index < 0 ? undefined : text.slice(0, index).split(`
94
- `).length;
95
- }
96
- function workerDir(path) {
97
- const slash = path.lastIndexOf("/");
98
- return slash < 0 ? "" : path.slice(0, slash);
99
- }
100
- function detectBoundRuntimeClasses(rootDir) {
101
- const byClass = new Map;
102
- for (const abs of findWranglerFiles(rootDir)) {
103
- let text;
104
- let config;
105
- try {
106
- text = readFileSync(abs, "utf8");
107
- config = JSON.parse(stripJsonc(text));
108
- } catch {
109
- continue;
110
- }
111
- if (!config || typeof config !== "object")
112
- continue;
113
- const path = relative(rootDir, abs).split("\\").join("/");
114
- const root = config;
115
- if (isTestWorkerConfig({ path, name: root.name, mains: [root.main] }))
116
- continue;
117
- const sections = [root, ...Object.values(root.env ?? {})];
118
- for (const section of sections) {
119
- if (!section || typeof section !== "object")
120
- continue;
121
- const s = section;
122
- const durable = s.durable_objects?.bindings;
123
- const entries = [
124
- ...Array.isArray(durable) ? durable.map((b) => ["durable_object", b]) : [],
125
- ...Array.isArray(s.workflows) ? s.workflows.map((w) => ["workflow", w]) : []
126
- ];
127
- for (const [kind, raw] of entries) {
128
- if (!raw || typeof raw !== "object")
129
- continue;
130
- const entry = raw;
131
- const className = typeof entry.class_name === "string" ? entry.class_name : "";
132
- const scriptName = typeof entry.script_name === "string" && entry.script_name ? entry.script_name : undefined;
133
- const key = `${className}|${scriptName ? "external" : `config:${path}`}`;
134
- if (!className || byClass.has(key))
135
- continue;
136
- const binding = typeof (kind === "workflow" ? entry.binding : entry.name) === "string" ? String(kind === "workflow" ? entry.binding : entry.name) : className;
137
- byClass.set(key, {
138
- className,
139
- kind,
140
- binding,
141
- ...scriptName ? { scriptName } : {},
142
- path,
143
- line: lineOf(text, className)
144
- });
145
- }
146
- }
147
- }
148
- return [...byClass.values()];
149
- }
150
- var RUNTIME_BASES = {
151
- DurableObject: "durable_object",
152
- WorkflowEntrypoint: "workflow",
153
- AgentWorkflow: "workflow",
154
- Agent: "agent",
155
- AIChatAgent: "agent",
156
- Think: "agent",
157
- McpAgent: "agent"
158
- };
159
- var SOURCE_FILE = /\.tsx?$/;
160
- var SKIPPED_SOURCE_FILE = /\.(?:test|spec)\.tsx?$|\.d\.ts$/;
161
- var TEST_DIR = /^(?:tests?|__tests__|__mocks__|e2e|(?:.*[-_.])?fixtures?|__fixtures__)$/i;
162
- var MAX_SOURCE_DEPTH = 12;
163
- var MAX_SOURCE_FILES = 20000;
164
- var MAX_SOURCE_BYTES = 1e6;
165
- var CLASS_DECLARATION = /^[ \t]*(export[ \t]+(?:default[ \t]+)?)?(abstract[ \t]+)?class[ \t]+([A-Za-z_$][\w$]*)(?:[ \t]*<[^\n{]*?>)?[ \t]+extends[ \t]+([A-Za-z_$][\w$]*)/gm;
166
- function findSourceFiles(rootDir) {
167
- const found = [];
168
- const walk = (dir, depth) => {
169
- let entries;
170
- try {
171
- entries = readdirSync(dir).sort();
172
- } catch {
173
- return;
174
- }
175
- for (const name of entries) {
176
- if (found.length >= MAX_SOURCE_FILES)
177
- return;
178
- const abs = join(dir, name);
179
- let stat;
180
- try {
181
- stat = lstatSync(abs);
182
- } catch {
183
- continue;
184
- }
185
- if (stat.isDirectory()) {
186
- if (depth < MAX_SOURCE_DEPTH && !SKIPPED_DIRS.has(name) && !name.startsWith(".") && !TEST_DIR.test(name))
187
- walk(abs, depth + 1);
188
- } else if (stat.isFile() && SOURCE_FILE.test(name) && !SKIPPED_SOURCE_FILE.test(name) && stat.size <= MAX_SOURCE_BYTES) {
189
- found.push(abs);
190
- }
191
- }
192
- };
193
- walk(rootDir, 0);
194
- return found;
195
- }
196
- function bindingsOfSection(section) {
197
- const durable = section.durable_objects?.bindings;
198
- const raw = [...Array.isArray(durable) ? durable : [], ...Array.isArray(section.workflows) ? section.workflows : []];
199
- return raw.flatMap((entry) => {
200
- if (!entry || typeof entry !== "object")
201
- return [];
202
- const { class_name: className, script_name: scriptName } = entry;
203
- if (typeof className !== "string" || !className)
204
- return [];
205
- return [{ className, ...typeof scriptName === "string" && scriptName ? { scriptName } : {} }];
206
- });
207
- }
208
- function readWranglerScope(rootDir, abs) {
209
- let text;
210
- try {
211
- text = readFileSync(abs, "utf8");
212
- } catch {
213
- return;
214
- }
215
- const names = [];
216
- const mains = [];
217
- const bindings = [];
218
- let topName;
219
- let topMain;
220
- if (abs.endsWith(".toml")) {
221
- let header = "";
222
- let binding;
223
- for (const rawLine of text.split(`
224
- `)) {
225
- const line = rawLine.replace(/\s+#.*$/, "").trim();
226
- const table = /^\[\[?\s*([^\]]+?)\s*\]\]?$/.exec(line);
227
- if (table) {
228
- header = table[1];
229
- binding = undefined;
230
- if (/(?:^|\.)(?:durable_objects\.bindings|workflows)$/.test(header) && line.startsWith("[[")) {
231
- binding = { className: "" };
232
- bindings.push(binding);
233
- }
234
- continue;
235
- }
236
- const pair = /^([\w.-]+)\s*=\s*["']([^"']*)["']/.exec(line);
237
- if (!pair)
238
- continue;
239
- const [, key, value] = pair;
240
- const envSection = header === "" || /^env\.[\w-]+$/.test(header);
241
- if (binding && key === "class_name")
242
- binding.className = value;
243
- else if (binding && key === "script_name" && value)
244
- binding.scriptName = value;
245
- else if (envSection && key === "name" && value) {
246
- names.push(value);
247
- if (header === "")
248
- topName = value;
249
- } else if (envSection && key === "main" && value) {
250
- mains.push(value);
251
- if (header === "")
252
- topMain = value;
253
- }
254
- }
255
- } else {
256
- let config;
257
- try {
258
- config = JSON.parse(stripJsonc(text));
259
- } catch {
260
- return;
261
- }
262
- if (!config || typeof config !== "object" || Array.isArray(config))
263
- return;
264
- const root = config;
265
- if (typeof root.name === "string")
266
- topName = root.name;
267
- if (typeof root.main === "string")
268
- topMain = root.main;
269
- for (const section of [root, ...Object.values(root.env ?? {})]) {
270
- if (!section || typeof section !== "object")
271
- continue;
272
- const s = section;
273
- if (typeof s.name === "string" && s.name)
274
- names.push(s.name);
275
- if (typeof s.main === "string" && s.main)
276
- mains.push(s.main);
277
- bindings.push(...bindingsOfSection(s));
278
- }
279
- }
280
- const path = relative(rootDir, abs).split("\\").join("/");
281
- if (isTestWorkerConfig({ path, name: topName, mains: [topMain] }))
282
- return;
283
- const dir = workerDir(path);
284
- const dirs = [dir];
285
- for (const main of mains) {
286
- const mainDir = workerDir(posix.normalize(posix.join(dir || ".", main)).replace(/^\.\//, ""));
287
- if (!mainDir.startsWith("..") && !inside(mainDir, dir) && !dirs.includes(mainDir))
288
- dirs.push(mainDir);
289
- }
290
- return { dirs, names, bindings: bindings.filter((entry) => entry.className) };
291
- }
292
- function inside(path, dir) {
293
- return dir === "" || dir === "." || path === dir || path.startsWith(`${dir}/`);
294
- }
295
- function boundClassResolver(rootDir) {
296
- const scopes = findWranglerFiles(rootDir, ANY_WRANGLER_FILE).map((abs) => readWranglerScope(rootDir, abs)).filter((scope) => Boolean(scope));
297
- const anywhere = new Set;
298
- const external = new Map;
299
- for (const scope of scopes) {
300
- for (const binding of scope.bindings) {
301
- anywhere.add(binding.className);
302
- if (!binding.scriptName)
303
- continue;
304
- const servers = scopes.filter((other) => other.names.includes(binding.scriptName));
305
- if (servers.length === 0)
306
- external.set(binding.className, new Set(["*"]));
307
- for (const server of servers) {
308
- const set = external.get(binding.className) ?? new Set;
309
- server.names.forEach((name) => set.add(name));
310
- external.set(binding.className, set);
311
- }
312
- }
313
- }
314
- return (className, path) => {
315
- if (!anywhere.has(className))
316
- return false;
317
- if (external.get(className)?.has("*"))
318
- return true;
319
- let nearest = -1;
320
- let owners = [];
321
- for (const scope of scopes) {
322
- const depth = Math.max(-1, ...scope.dirs.filter((dir) => inside(path, dir)).map((dir) => dir ? dir.split("/").length : 0));
323
- if (depth < 0 || depth < nearest)
324
- continue;
325
- if (depth > nearest)
326
- owners = [];
327
- nearest = depth;
328
- owners.push(scope);
329
- }
330
- if (owners.length === 0)
331
- return true;
332
- const servedNames = external.get(className);
333
- return owners.some((owner) => owner.bindings.some((binding) => binding.className === className && !binding.scriptName) || servedNames !== undefined && owner.names.some((name) => servedNames.has(name)));
334
- };
335
- }
336
- function relativeImportStems(path, text) {
337
- const stems = new Set;
338
- for (const match of text.matchAll(/\bfrom\s*['"](\.{1,2}\/[^'"\n]*)['"]/g)) {
339
- const joined = posix.normalize(posix.join(workerDir(path) || ".", match[1])).replace(/^\.\//, "");
340
- const stem = joined.replace(/\.(?:[cm]?[jt]sx?)$/, "");
341
- stems.add(stem);
342
- stems.add(`${stem}/index`);
343
- }
344
- return stems;
345
- }
346
- function pickBaseDeclaration(candidates, from, importsOf) {
347
- if (candidates.length < 2)
348
- return candidates[0];
349
- const sameFile = candidates.find((candidate) => candidate.path === from.path);
350
- if (sameFile)
351
- return sameFile;
352
- const imports = importsOf(from.path);
353
- const imported = candidates.find((candidate) => imports.has(candidate.path.replace(/\.(?:[cm]?[jt]sx?)$/, "")));
354
- if (imported)
355
- return imported;
356
- const fromDirs = workerDir(from.path).split("/");
357
- const shared = (candidate) => {
358
- const dirs = workerDir(candidate.path).split("/");
359
- let n = 0;
360
- while (n < dirs.length && n < fromDirs.length && dirs[n] === fromDirs[n] && dirs[n] !== "")
361
- n += 1;
362
- return n;
363
- };
364
- let best = candidates[0];
365
- for (const candidate of candidates)
366
- if (shared(candidate) > shared(best))
367
- best = candidate;
368
- return best;
369
- }
370
- function detectDefinedUnboundRuntimeClasses(rootDir) {
371
- const declarations = [];
372
- const texts = new Map;
373
- for (const abs of findSourceFiles(rootDir)) {
374
- let text;
375
- try {
376
- text = readFileSync(abs, "utf8");
377
- } catch {
378
- continue;
379
- }
380
- if (!text.includes("extends"))
381
- continue;
382
- const path = relative(rootDir, abs).split("\\").join("/");
383
- texts.set(path, text);
384
- for (const match of text.matchAll(CLASS_DECLARATION)) {
385
- declarations.push({
386
- className: match[3],
387
- base: match[4],
388
- exported: Boolean(match[1]),
389
- abstract: Boolean(match[2]),
390
- path,
391
- line: text.slice(0, match.index).split(`
392
- `).length
393
- });
394
- }
395
- }
396
- const byName = new Map;
397
- for (const declaration of declarations) {
398
- const list = byName.get(declaration.className) ?? [];
399
- list.push(declaration);
400
- byName.set(declaration.className, list);
401
- }
402
- const importCache = new Map;
403
- const importsOf = (path) => {
404
- let stems = importCache.get(path);
405
- if (!stems) {
406
- stems = relativeImportStems(path, texts.get(path) ?? "");
407
- importCache.set(path, stems);
408
- }
409
- return stems;
410
- };
411
- const kindOf = (start) => {
412
- let current = start;
413
- for (let depth = 0;current && depth < 8; depth += 1) {
414
- const base = current.base;
415
- const kind = Object.hasOwn(RUNTIME_BASES, base) ? RUNTIME_BASES[base] : undefined;
416
- if (kind)
417
- return kind;
418
- current = pickBaseDeclaration(byName.get(base) ?? [], current, importsOf);
419
- }
420
- return;
421
- };
422
- const isBound = boundClassResolver(rootDir);
423
- const seen = new Set;
424
- const found = [];
425
- for (const declaration of declarations) {
426
- if (!declaration.exported || declaration.abstract || isBound(declaration.className, declaration.path))
427
- continue;
428
- const kind = kindOf(declaration);
429
- const key = `${declaration.className}|${declaration.path}`;
430
- if (!kind || seen.has(key))
431
- continue;
432
- seen.add(key);
433
- found.push({ className: declaration.className, kind, path: declaration.path, line: declaration.line });
434
- }
435
- return found;
436
- }
437
- function definedReason(defined) {
438
- const what = defined.kind === "workflow" ? "Workflow" : defined.kind === "agent" ? "Agent (Durable Object)" : "Durable Object";
439
- return `${what} class defined but not bound in any wrangler config`;
440
- }
441
- function norm(value) {
442
- return typeof value === "string" ? value.toLowerCase().replace(/[^a-z0-9]/g, "") : "";
443
- }
444
- function itemPaths(item) {
445
- const paths = [];
446
- for (const value of [item.root, item.sourcePath]) {
447
- if (typeof value === "string" && value && value !== ".")
448
- paths.push(value.replace(/^\.\//, ""));
449
- }
450
- for (const entry of Array.isArray(item.evidence) ? item.evidence : []) {
451
- const path = entry && typeof entry === "object" ? entry.path : undefined;
452
- if (typeof path === "string" && path)
453
- paths.push(path.replace(/^\.\//, ""));
454
- }
455
- return paths;
456
- }
457
- function withinWorker(path, dir, otherDirs) {
458
- const inside2 = (d) => d === "" || path === d || path.startsWith(`${d}/`);
459
- return inside2(dir) && !otherDirs.some((other) => other.length > dir.length && inside2(other));
460
- }
461
- function namesClass(name, className) {
462
- if (typeof name !== "string")
463
- return false;
464
- const target = norm(className);
465
- const bare = name.replace(/\s*\([^)]*\)\s*$/, "");
466
- return norm(bare) === target || bare.split(/[^A-Za-z0-9_]+/).some((word) => norm(word) === target);
467
- }
468
- function graphNamesClass(graph, bound, all) {
469
- const named = (name) => namesClass(name, bound.className);
470
- const sameName = bound.scriptName ? [bound] : all.filter((other) => other.className === bound.className && !other.scriptName);
471
- const dir = workerDir(bound.path);
472
- const otherDirs = sameName.map((other) => workerDir(other.path)).filter((other) => other !== dir);
473
- const covers = (item) => {
474
- if (!named(item.name))
475
- return false;
476
- if (sameName.length < 2)
477
- return true;
478
- return itemPaths(item).some((path) => withinWorker(path, dir, otherDirs));
479
- };
480
- return graph.runtimeAgents.some(covers) || graph.artifacts.some(covers);
481
- }
482
- function citedFile(rootDir, path) {
483
- let rel = path.trim().replace(/\\/g, "/").replace(/:\d+(?::\d+)?$/, "");
484
- if (isAbsolute(rel))
485
- rel = relative(rootDir, rel).split("\\").join("/");
486
- return posix.normalize(rel).replace(/^\.\//, "");
487
- }
488
- function graphNamesDeclaration(graph, defined, rootDir) {
489
- const named = [...graph.runtimeAgents, ...graph.artifacts].filter((item) => namesClass(item.name, defined.className));
490
- if (named.length === 0)
491
- return false;
492
- const target = citedFile(rootDir, defined.path);
493
- const cited = named.map((item) => filePaths(item).map((path) => citedFile(rootDir, path)));
494
- if (cited.every((paths) => paths.length === 0))
495
- return true;
496
- return cited.some((paths) => paths.includes(target));
497
- }
498
- function filePaths(item) {
499
- const paths = [];
500
- if (typeof item.sourcePath === "string" && item.sourcePath.trim())
501
- paths.push(item.sourcePath);
502
- for (const entry of Array.isArray(item.evidence) ? item.evidence : []) {
503
- const path = entry && typeof entry === "object" ? entry.path : undefined;
504
- if (typeof path === "string" && path.trim())
505
- paths.push(path);
506
- }
507
- return paths;
508
- }
509
- function bindingReason(bound) {
510
- const what = bound.kind === "workflow" ? "Workflow" : "Durable Object";
511
- const where = bound.scriptName ? ` served by the external Worker "${bound.scriptName}"` : "";
512
- return `${what} binding ${bound.binding} -> class ${bound.className}${where}`;
513
- }
514
- function uniqueId(base, taken) {
515
- let id = base;
516
- for (let n = 2;taken.has(id); n += 1)
517
- id = `${base}-${n}`;
518
- taken.add(id);
519
- return id;
520
- }
521
- function mergeBoundRuntimeClasses(graph, rootDir) {
522
- if (process.env.MODA_HARNESS_DETERMINISTIC_MERGE === "0")
523
- return { added: 0 };
524
- let bound;
525
- let defined;
526
- try {
527
- bound = detectBoundRuntimeClasses(rootDir);
528
- defined = detectDefinedUnboundRuntimeClasses(rootDir);
529
- } catch {
530
- return { added: 0 };
531
- }
532
- const taken = new Set([
533
- ...graph.runtimeAgents.map((agent) => agent.id),
534
- ...graph.artifacts.map((artifact) => artifact.id)
535
- ]);
536
- let added = 0;
537
- for (const entry of bound) {
538
- if (graphNamesClass(graph, entry, bound))
539
- continue;
540
- const evidence = [{ path: entry.path, ...entry.line ? { line: entry.line } : {}, reason: bindingReason(entry) }];
541
- const slug = entry.className.replace(/[^A-Za-z0-9]+/g, "-").toLowerCase();
542
- if (entry.kind === "durable_object" && AGENT_CLASS.test(entry.className) && !entry.scriptName) {
543
- const agent = {
544
- id: uniqueId(`agent.bound.${slug}`, taken),
545
- name: entry.className,
546
- kind: "runtime_agent",
547
- purpose: "Agent class bound as a Durable Object.",
548
- root: ".",
549
- entrypointIds: [],
550
- channelAdapterIds: [],
551
- artifactIds: [],
552
- identityNamespaceIds: [],
553
- deploymentIds: [],
554
- frameworkIds: [],
555
- modelProviderIds: [],
556
- confidence: 0.6,
557
- evidence
558
- };
559
- graph.runtimeAgents.push(agent);
560
- } else {
561
- const artifact = {
562
- id: uniqueId(`entrypoint.bound.${slug}`, taken),
563
- type: "entrypoint",
564
- name: entry.className,
565
- scope: "shared",
566
- sourcePath: entry.path,
567
- usedByAgentIds: [],
568
- confidence: 0.6,
569
- evidence
570
- };
571
- graph.artifacts.push(artifact);
572
- }
573
- added += 1;
574
- }
575
- for (const entry of defined) {
576
- if (graphNamesDeclaration(graph, entry, rootDir))
577
- continue;
578
- const slug = entry.className.replace(/[^A-Za-z0-9]+/g, "-").toLowerCase();
579
- graph.artifacts.push({
580
- id: uniqueId(`entrypoint.defined.${slug}`, taken),
581
- type: "entrypoint",
582
- name: entry.className,
583
- scope: "shared",
584
- sourcePath: entry.path,
585
- usedByAgentIds: [],
586
- confidence: 0.6,
587
- evidence: [{ path: entry.path, line: entry.line, reason: definedReason(entry) }]
588
- });
589
- added += 1;
590
- }
591
- return { added };
592
- }
593
-
594
- // src/harness-component-status.ts
595
- import { closeSync, openSync, readdirSync as readdirSync2, readFileSync as readFileSync2, readSync } from "node:fs";
596
- import { isAbsolute as isAbsolute2, join as join2, posix as posix2, relative as relative2 } from "node:path";
597
- var HARNESS_COMPONENT_STATUSES = new Set(["in_prod", "reachable", "defined_only"]);
598
- var STATUS_ALIASES = {
599
- in_prod: "in_prod",
600
- inprod: "in_prod",
601
- prod: "in_prod",
602
- production: "in_prod",
603
- in_production: "in_prod",
604
- deployed: "in_prod",
605
- live: "in_prod",
606
- reachable: "reachable",
607
- defined_only: "defined_only",
608
- dead: "defined_only",
609
- dead_code: "defined_only",
610
- unused: "defined_only",
611
- unreferenced: "defined_only",
612
- unreachable: "defined_only",
613
- orphan: "defined_only",
614
- orphaned: "defined_only"
615
- };
616
- function normalizeComponentStatus(value) {
617
- if (typeof value !== "string")
618
- return;
619
- return STATUS_ALIASES[value.trim().toLowerCase().replace(/[\s-]+/g, "_")];
620
- }
621
- var SKIPPED_DIRS2 = new Set(["node_modules", "dist", "build", "vendor", "coverage"]);
622
- var NON_DEPLOY_DIR = /^(?:test|tests|__tests__|fixtures?|__fixtures__|examples?|e2e)$/;
623
- var WRANGLER_FILE2 = /^wrangler(?:\.([\w.-]+))?\.(?:json|jsonc|toml)$/;
624
- var NON_DEPLOY_VARIANT = /(?:^|[.-])(?:preview|dev|local|test|example|sample)(?:$|[.-])/i;
625
- var CODE_FILE = /\.(?:[cm]?[jt]sx?)$/;
626
- var TEST_RUNNER_FILE = /(?:^|\/)__tests__\/|\.(?:test|spec)\.[cm]?[jt]sx?$/;
627
- var RESOLVE_EXTS = [".ts", ".tsx", ".mts", ".cts", ".js", ".jsx", ".mjs", ".cjs"];
628
- var TS_FOR_JS = { ".js": [".ts", ".tsx"], ".jsx": [".tsx"], ".mjs": [".mts"], ".cjs": [".cts"] };
629
- var MAX_FILES = 200000;
630
- var MAX_READ_BYTES = 1024 * 1024;
631
- var STATIC_IMPORT = /\b(?:import|export)\s+(type\s+)?([^'";]*?)\bfrom\s*['"]([^'"\n]+)['"]/g;
632
- var SIDE_EFFECT_IMPORT = /\bimport\s*['"]([^'"\n]+)['"]/g;
633
- var DYNAMIC_IMPORT = /\b(?:import|require)\s*\(\s*['"]([^'"\n]+)['"]\s*\)/g;
634
- var CLASS_DECL = /\bclass\s+([A-Za-z_$][\w$]*)/g;
635
- var SCRIPT_FILE = /(?:^|[\s;&|(])(?:bun|node|tsx)\s+((?:-{1,2}[\w-]+(?:=\S+)?\s+)*)(?:run\s+)?([^\s'"&|;()]+)/g;
636
- function listFiles(rootDir) {
637
- const files = new Set;
638
- const walk = (abs, rel) => {
639
- let entries;
640
- try {
641
- entries = readdirSync2(abs, { withFileTypes: true });
642
- } catch {
643
- return;
644
- }
645
- for (const entry of entries) {
646
- if (files.size >= MAX_FILES)
647
- return;
648
- const childRel = rel ? `${rel}/${entry.name}` : entry.name;
649
- if (entry.isDirectory()) {
650
- if (!entry.name.startsWith(".") && !SKIPPED_DIRS2.has(entry.name))
651
- walk(join2(abs, entry.name), childRel);
652
- } else if (entry.isFile()) {
653
- files.add(childRel);
654
- }
655
- }
656
- };
657
- walk(rootDir, "");
658
- return files;
659
- }
660
- function readCapped(abs) {
661
- const fd = openSync(abs, "r");
662
- try {
663
- const buffer = Buffer.alloc(MAX_READ_BYTES);
664
- const read = readSync(fd, buffer, 0, MAX_READ_BYTES, 0);
665
- return buffer.subarray(0, read).toString("utf8");
666
- } finally {
667
- closeSync(fd);
668
- }
669
- }
670
- function readJson(rootDir, rel) {
671
- try {
672
- const parsed = JSON.parse(stripJsonc(readFileSync2(join2(rootDir, rel), "utf8")));
673
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : undefined;
674
- } catch {
675
- return;
676
- }
677
- }
678
- function dirOf(rel) {
679
- const slash = rel.lastIndexOf("/");
680
- return slash < 0 ? "" : rel.slice(0, slash);
681
- }
682
- function joinRel(dir, path) {
683
- const joined = posix2.normalize(posix2.join(dir || ".", path));
684
- return joined === "." ? "" : joined.replace(/^\.\//, "").replace(/\/$/, "");
685
- }
686
- function isRecord(value) {
687
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
688
- }
689
- var PREFERRED_CONDITIONS = ["import", "default", "require", "node"];
690
- function exportTargets(value, out = []) {
691
- if (typeof value === "string")
692
- out.push(value);
693
- else if (Array.isArray(value))
694
- value.forEach((item) => exportTargets(item, out));
695
- else if (isRecord(value)) {
696
- const keys = Object.keys(value).filter((key) => key !== "types" && key !== "typings");
697
- const preferred = keys.filter((key) => PREFERRED_CONDITIONS.includes(key));
698
- for (const key of [...preferred, ...keys.filter((key2) => !preferred.includes(key2))])
699
- exportTargets(value[key], out);
700
- }
701
- return out;
702
- }
703
- function isTypeOnlyClause(clause) {
704
- const braces = /^\{([^{}]*)\}$/.exec(clause.trim());
705
- if (!braces)
706
- return false;
707
- const members = braces[1].split(",").map((member) => member.trim()).filter(Boolean);
708
- return members.length > 0 && members.every((member) => /^type\s+(?!as\s+[\w$]+$)\S/.test(member));
709
- }
710
- function stringLeaves(value, out = []) {
711
- if (typeof value === "string")
712
- out.push(value);
713
- else if (Array.isArray(value))
714
- value.forEach((item) => stringLeaves(item, out));
715
- else if (isRecord(value)) {
716
- for (const [key, item] of Object.entries(value)) {
717
- if (key !== "types" && key !== "typings")
718
- stringLeaves(item, out);
719
- }
720
- }
721
- return out;
722
- }
723
- function boundClassesOf(section) {
724
- const durable = isRecord(section.durable_objects) ? section.durable_objects.bindings : undefined;
725
- const raw = [...Array.isArray(durable) ? durable : [], ...Array.isArray(section.workflows) ? section.workflows : []];
726
- return raw.filter(isRecord).flatMap((entry) => typeof entry.class_name === "string" && entry.class_name ? [{ className: entry.class_name, ...typeof entry.script_name === "string" && entry.script_name ? { scriptName: entry.script_name } : {} }] : []);
727
- }
728
- var PRODUCTION_ENV = /^(?:production|prod)$/i;
729
- function parseWranglerToml(text) {
730
- const names = [];
731
- const mains = [];
732
- const otherMains = [];
733
- const boundClasses = [];
734
- let topName;
735
- let header = "";
736
- let binding;
737
- for (const rawLine of text.split(`
738
- `)) {
739
- const line = rawLine.replace(/\s+#.*$/, "").trim();
740
- const table = /^\[\[?\s*([^\]]+?)\s*\]\]?$/.exec(line);
741
- if (table) {
742
- header = table[1];
743
- binding = undefined;
744
- if (/(?:^|\.)(?:durable_objects\.bindings|workflows)$/.test(header) && line.startsWith("[[")) {
745
- binding = { className: "" };
746
- boundClasses.push(binding);
747
- }
748
- continue;
749
- }
750
- const pair = /^([\w.-]+)\s*=\s*["']([^"']*)["']/.exec(line);
751
- if (!pair)
752
- continue;
753
- const [, key, value] = pair;
754
- const envName = /^env\.([\w-]+)$/.exec(header)?.[1];
755
- const envSection = header === "" || envName !== undefined;
756
- if (binding && key === "class_name")
757
- binding.className = value;
758
- else if (binding && key === "script_name" && value)
759
- binding.scriptName = value;
760
- else if (envSection && key === "name") {
761
- if (header === "")
762
- topName = value;
763
- names.push(value);
764
- } else if (envSection && key === "main") {
765
- (envName === undefined || PRODUCTION_ENV.test(envName) ? mains : otherMains).push(value);
766
- }
767
- }
768
- return { name: topName, names, mains, otherMains, boundClasses: boundClasses.filter((entry) => entry.className) };
769
- }
770
- function readWranglerWorker(rootDir, rel) {
771
- const dir = dirOf(rel);
772
- const fileName = rel.slice(dir ? dir.length + 1 : 0);
773
- const variant = WRANGLER_FILE2.exec(fileName)?.[1] ?? "";
774
- let name;
775
- const names = [];
776
- const mains = [];
777
- const otherMains = [];
778
- const boundClasses = [];
779
- if (rel.endsWith(".toml")) {
780
- let text;
781
- try {
782
- text = readFileSync2(join2(rootDir, rel), "utf8");
783
- } catch {
784
- return;
785
- }
786
- const parsed = parseWranglerToml(text);
787
- name = parsed.name;
788
- names.push(...parsed.names);
789
- mains.push(...parsed.mains);
790
- otherMains.push(...parsed.otherMains);
791
- boundClasses.push(...parsed.boundClasses);
792
- } else {
793
- const config = readJson(rootDir, rel);
794
- if (!config)
795
- return;
796
- name = typeof config.name === "string" && config.name ? config.name : undefined;
797
- const sections = [[undefined, config], ...Object.entries(isRecord(config.env) ? config.env : {})];
798
- for (const [envName, section] of sections) {
799
- if (!isRecord(section))
800
- continue;
801
- if (typeof section.name === "string" && section.name)
802
- names.push(section.name);
803
- if (typeof section.main === "string" && section.main) {
804
- (envName === undefined || PRODUCTION_ENV.test(envName) ? mains : otherMains).push(section.main);
805
- }
806
- boundClasses.push(...boundClassesOf(section));
807
- }
808
- }
809
- const nonDeployDir = rel.split("/").slice(0, -1).some((segment) => NON_DEPLOY_DIR.test(segment));
810
- return {
811
- path: rel,
812
- deployed: Boolean(name) && !nonDeployDir && !NON_DEPLOY_VARIANT.test(variant) && !isTestWorkerConfig({ path: rel, name, mains }),
813
- names,
814
- mains: [...new Set(mains)].map((main) => joinRel(dir, main)),
815
- otherMains: [...new Set(otherMains)].filter((main) => !mains.includes(main)).map((main) => joinRel(dir, main)),
816
- boundClasses,
817
- reach: new Set
818
- };
819
- }
820
- function readWorkspacePackage(rootDir, rel) {
821
- const pkg = readJson(rootDir, rel);
822
- if (!pkg)
823
- return;
824
- const dir = dirOf(rel);
825
- const entries = [];
826
- const subpaths = new Map;
827
- const patterns = [];
828
- for (const field of ["main", "module"]) {
829
- if (typeof pkg[field] === "string")
830
- entries.push(pkg[field]);
831
- }
832
- if (typeof pkg.bin === "string")
833
- entries.push(pkg.bin);
834
- else if (isRecord(pkg.bin))
835
- entries.push(...stringLeaves(pkg.bin));
836
- const exportsField = pkg.exports;
837
- if (isRecord(exportsField) && Object.keys(exportsField).some((key) => key.startsWith("."))) {
838
- for (const [key, value] of Object.entries(exportsField)) {
839
- const targets = exportTargets(value);
840
- const star = key.indexOf("*");
841
- if (star >= 0) {
842
- if (key.indexOf("*", star + 1) < 0)
843
- patterns.push({ prefix: key.slice(0, star), suffix: key.slice(star + 1), targets });
844
- continue;
845
- }
846
- subpaths.set(key, targets);
847
- entries.push(...targets);
848
- }
849
- patterns.sort((a, b) => b.prefix.length - a.prefix.length || b.suffix.length - a.suffix.length);
850
- } else if (exportsField !== undefined) {
851
- const targets = exportTargets(exportsField);
852
- subpaths.set(".", targets);
853
- entries.push(...targets);
854
- }
855
- const scriptFiles = [];
856
- if (isRecord(pkg.scripts)) {
857
- for (const command of Object.values(pkg.scripts)) {
858
- if (typeof command !== "string")
859
- continue;
860
- for (const match of command.matchAll(SCRIPT_FILE))
861
- scriptFiles.push(match[2]);
862
- }
863
- }
864
- return {
865
- dir,
866
- ...typeof pkg.name === "string" && pkg.name ? { name: pkg.name } : {},
867
- entries: entries.filter((entry) => !entry.includes("*")),
868
- subpaths,
869
- patterns,
870
- scriptFiles
871
- };
872
- }
873
- function readPathAliases(rootDir, rel, depth = 0) {
874
- const config = readJson(rootDir, rel);
875
- if (!config)
876
- return [];
877
- const options = isRecord(config.compilerOptions) ? config.compilerOptions : {};
878
- if (isRecord(options.paths)) {
879
- const base = joinRel(dirOf(rel), typeof options.baseUrl === "string" ? options.baseUrl : ".");
880
- return Object.entries(options.paths).flatMap(([pattern, targets]) => {
881
- if (!Array.isArray(targets))
882
- return [];
883
- const star = pattern.indexOf("*");
884
- return [{
885
- prefix: star < 0 ? pattern : pattern.slice(0, star),
886
- suffix: star < 0 ? "" : pattern.slice(star + 1),
887
- wildcard: star >= 0,
888
- targets: targets.filter((target) => typeof target === "string").map((target) => joinRel(base, target))
889
- }];
890
- });
891
- }
892
- if (depth < 3 && typeof config.extends === "string" && config.extends.startsWith(".")) {
893
- const parent = joinRel(dirOf(rel), config.extends.endsWith(".json") ? config.extends : `${config.extends}.json`);
894
- return readPathAliases(rootDir, parent, depth + 1);
895
- }
896
- return [];
897
- }
898
- function analyzeRepository(rootDir) {
899
- const files = listFiles(rootDir);
900
- const workers = [];
901
- const packages = [];
902
- for (const rel of files) {
903
- const fileName = rel.slice(rel.lastIndexOf("/") + 1);
904
- if (WRANGLER_FILE2.test(fileName)) {
905
- const worker = readWranglerWorker(rootDir, rel);
906
- if (worker)
907
- workers.push(worker);
908
- } else if (fileName === "package.json") {
909
- const pkg = readWorkspacePackage(rootDir, rel);
910
- if (pkg)
911
- packages.push(pkg);
912
- }
913
- }
914
- workers.sort((a, b) => a.path.localeCompare(b.path));
915
- const packagesByName = new Map(packages.filter((pkg) => pkg.name).map((pkg) => [pkg.name, pkg]));
916
- const packageDirs = new Set(packages.map((pkg) => pkg.dir));
917
- const resolveFile = (base) => {
918
- if (!base || base.startsWith(".."))
919
- return;
920
- const ext = posix2.extname(base);
921
- for (const tsExt of TS_FOR_JS[ext] ?? []) {
922
- const candidate = base.slice(0, -ext.length) + tsExt;
923
- if (files.has(candidate))
924
- return candidate;
925
- }
926
- if (files.has(base))
927
- return base;
928
- for (const candidate of RESOLVE_EXTS)
929
- if (files.has(base + candidate))
930
- return base + candidate;
931
- for (const candidate of RESOLVE_EXTS)
932
- if (files.has(`${base}/index${candidate}`))
933
- return `${base}/index${candidate}`;
934
- return;
935
- };
936
- const resolveEntry = (dir, entry) => {
937
- const direct = resolveFile(joinRel(dir, entry));
938
- if (direct)
939
- return direct;
940
- const fromSource = entry.replace(/^\.?\/?(?:dist|build|lib|out)\//, "src/").replace(/\.d\.[cm]?ts$/, ".ts");
941
- return fromSource !== entry ? resolveFile(joinRel(dir, fromSource)) : undefined;
942
- };
943
- const aliasCache = new Map;
944
- const aliasesFor = (dir) => {
945
- const cached = aliasCache.get(dir);
946
- if (cached)
947
- return cached;
948
- const tsconfig = dir ? `${dir}/tsconfig.json` : "tsconfig.json";
949
- const aliases = files.has(tsconfig) ? readPathAliases(rootDir, tsconfig) : dir ? aliasesFor(dirOf(dir)) : [];
950
- aliasCache.set(dir, aliases);
951
- return aliases;
952
- };
953
- const resolvePackage = (specifier) => {
954
- const parts = specifier.split("/");
955
- const nameLength = specifier.startsWith("@") ? 2 : 1;
956
- const name = parts.slice(0, nameLength).join("/");
957
- const subpath = parts.slice(nameLength).join("/");
958
- const pkg = packagesByName.get(name);
959
- if (pkg) {
960
- const key = subpath ? `./${subpath}` : ".";
961
- let targets = pkg.subpaths.get(key);
962
- if (!targets && subpath) {
963
- const pattern = pkg.patterns.find((entry) => key.startsWith(entry.prefix) && key.endsWith(entry.suffix) && key.length >= entry.prefix.length + entry.suffix.length);
964
- const captured = pattern ? key.slice(pattern.prefix.length, key.length - pattern.suffix.length) : "";
965
- targets = pattern ? pattern.targets.map((target) => target.split("*").join(captured)) : [];
966
- }
967
- for (const target of targets ?? pkg.entries) {
968
- const resolved = resolveEntry(pkg.dir, target);
969
- if (resolved)
970
- return resolved;
971
- }
972
- if (subpath)
973
- return resolveFile(joinRel(pkg.dir, subpath)) ?? resolveFile(joinRel(pkg.dir, `src/${subpath}`));
974
- return resolveFile(joinRel(pkg.dir, "src/index")) ?? resolveFile(joinRel(pkg.dir, "index"));
975
- }
976
- const leaf = parts[nameLength - 1];
977
- for (const parent of ["packages", "apps", "libs"]) {
978
- const dir = `${parent}/${leaf}`;
979
- if (packageDirs.has(dir))
980
- continue;
981
- const resolved = subpath ? resolveFile(`${dir}/src/${subpath}`) ?? resolveFile(`${dir}/${subpath}`) : resolveFile(`${dir}/src/index`) ?? resolveFile(`${dir}/index`);
982
- if (resolved)
983
- return resolved;
984
- }
985
- return;
986
- };
987
- const resolveSpecifier = (raw, from) => {
988
- const specifier = raw.split("?")[0];
989
- if (!specifier || specifier.startsWith("node:") || specifier.startsWith("/"))
990
- return;
991
- if (specifier.startsWith("."))
992
- return resolveFile(joinRel(dirOf(from), specifier));
993
- for (const alias of aliasesFor(dirOf(from))) {
994
- let captured;
995
- if (!alias.wildcard && specifier === alias.prefix)
996
- captured = "";
997
- else if (alias.wildcard && specifier.startsWith(alias.prefix) && specifier.endsWith(alias.suffix) && specifier.length >= alias.prefix.length + alias.suffix.length) {
998
- captured = specifier.slice(alias.prefix.length, specifier.length - alias.suffix.length);
999
- }
1000
- if (captured === undefined)
1001
- continue;
1002
- for (const target of alias.targets) {
1003
- const resolved = resolveFile(alias.wildcard ? target.replace("*", captured) : target);
1004
- if (resolved)
1005
- return resolved;
1006
- }
1007
- }
1008
- return resolvePackage(specifier);
1009
- };
1010
- const classFiles = new Map;
1011
- const edgeCache = new Map;
1012
- const edgesOf = (file) => {
1013
- const cached = edgeCache.get(file);
1014
- if (cached)
1015
- return cached;
1016
- const edges = [];
1017
- edgeCache.set(file, edges);
1018
- if (!CODE_FILE.test(file))
1019
- return edges;
1020
- let text;
1021
- try {
1022
- text = readCapped(join2(rootDir, file));
1023
- } catch {
1024
- return edges;
1025
- }
1026
- text = text.replace(/^\s*\/\/.*$/gm, "");
1027
- const specifiers = [];
1028
- for (const match of text.matchAll(STATIC_IMPORT)) {
1029
- if (!match[1] && !isTypeOnlyClause(match[2]))
1030
- specifiers.push(match[3]);
1031
- }
1032
- for (const match of text.matchAll(SIDE_EFFECT_IMPORT))
1033
- specifiers.push(match[1]);
1034
- for (const match of text.matchAll(DYNAMIC_IMPORT))
1035
- specifiers.push(match[1]);
1036
- for (const specifier of new Set(specifiers)) {
1037
- const resolved = resolveSpecifier(specifier, file);
1038
- if (resolved && resolved !== file)
1039
- edges.push(resolved);
1040
- }
1041
- for (const match of text.matchAll(CLASS_DECL)) {
1042
- const list = classFiles.get(match[1]) ?? [];
1043
- if (!list.includes(file))
1044
- list.push(file);
1045
- classFiles.set(match[1], list);
1046
- }
1047
- return edges;
1048
- };
1049
- const walk = (starts, seen = new Set) => {
1050
- const queue = [...starts].filter((start) => Boolean(start) && !seen.has(start));
1051
- for (const start of queue)
1052
- seen.add(start);
1053
- while (queue.length > 0) {
1054
- const file = queue.pop();
1055
- for (const next of edgesOf(file)) {
1056
- if (!seen.has(next)) {
1057
- seen.add(next);
1058
- queue.push(next);
1059
- }
1060
- }
1061
- }
1062
- return seen;
1063
- };
1064
- const inProd = new Set;
1065
- for (const worker of workers) {
1066
- worker.reach = walk(worker.mains.map((main) => resolveEntry("", main)));
1067
- if (!worker.deployed)
1068
- continue;
1069
- inProd.add(worker.path);
1070
- for (const file of worker.reach)
1071
- inProd.add(file);
1072
- }
1073
- const otherStarts = workers.filter((worker) => !worker.deployed).map((worker) => worker.path);
1074
- for (const worker of workers) {
1075
- if (!worker.deployed)
1076
- otherStarts.push(...worker.reach);
1077
- otherStarts.push(...worker.otherMains.map((main) => resolveEntry("", main)));
1078
- }
1079
- for (const pkg of packages) {
1080
- otherStarts.push(...pkg.entries.map((entry) => resolveEntry(pkg.dir, entry)));
1081
- otherStarts.push(...pkg.scriptFiles.map((script) => resolveFile(joinRel(pkg.dir, script))));
1082
- }
1083
- const all = walk(otherStarts, new Set(inProd));
1084
- const reachable = new Set([...all].filter((file) => !inProd.has(file)));
1085
- return { inProd, reachable, files, workers, classFiles, resolveFile };
1086
- }
1087
- function buildImportGraphReachability(rootDir) {
1088
- const { inProd, reachable } = analyzeRepository(rootDir);
1089
- return { inProd, reachable };
1090
- }
1091
- function norm2(value) {
1092
- return typeof value === "string" ? value.toLowerCase().replace(/[^a-z0-9]/g, "") : "";
1093
- }
1094
- function normalizeItemPath(rootDir, path) {
1095
- let rel = path.trim().replace(/\\/g, "/").replace(/:\d+(?::\d+)?$/, "");
1096
- if (isAbsolute2(rel))
1097
- rel = relative2(rootDir, rel).split("\\").join("/");
1098
- return joinRel("", rel);
1099
- }
1100
- function definingPath(item) {
1101
- if (typeof item.sourcePath === "string" && item.sourcePath.trim())
1102
- return item.sourcePath;
1103
- for (const entry of Array.isArray(item.evidence) ? item.evidence : []) {
1104
- const path = isRecord(entry) ? entry.path : undefined;
1105
- if (typeof path === "string" && path.trim())
1106
- return path;
1107
- }
1108
- return;
1109
- }
1110
- function boundClassStatus(analysis, worker, item) {
1111
- const wanted = new Set([norm2(item.name), norm2(typeof item.id === "string" ? item.id.split(".").pop() : "")]);
1112
- const bound = worker.boundClasses.find((entry) => wanted.has(norm2(entry.className)));
1113
- if (!bound)
1114
- return null;
1115
- const servers = bound.scriptName ? analysis.workers.filter((other) => other.names.includes(bound.scriptName)) : [worker];
1116
- const declared = analysis.classFiles.get(bound.className) ?? [];
1117
- if (servers.some((server) => server.deployed && declared.some((file) => server.reach.has(file))))
1118
- return "in_prod";
1119
- if (declared.some((file) => analysis.inProd.has(file) || analysis.reachable.has(file)))
1120
- return "reachable";
1121
- return;
1122
- }
1123
- function itemStatus(analysis, rootDir, item) {
1124
- const raw = definingPath(item);
1125
- if (!raw)
1126
- return;
1127
- const path = normalizeItemPath(rootDir, raw);
1128
- const worker = analysis.workers.find((entry) => entry.path === path);
1129
- if (worker) {
1130
- const classStatus = boundClassStatus(analysis, worker, item);
1131
- if (classStatus !== null)
1132
- return classStatus;
1133
- return worker.deployed ? "in_prod" : "reachable";
1134
- }
1135
- const file = analysis.files.has(path) ? path : analysis.resolveFile(path);
1136
- if (!file)
1137
- return;
1138
- if (analysis.inProd.has(file))
1139
- return "in_prod";
1140
- if (analysis.reachable.has(file))
1141
- return "reachable";
1142
- return CODE_FILE.test(file) && !TEST_RUNNER_FILE.test(file) ? "defined_only" : undefined;
1143
- }
1144
- function staticStatus(status) {
1145
- return status === "in_prod" ? "reachable" : status;
1146
- }
1147
- function tagComponentStatus(graph, rootDir) {
1148
- if (process.env.MODA_HARNESS_DETERMINISTIC_MERGE === "0")
1149
- return { tagged: 0, counts: {} };
1150
- try {
1151
- const analysis = analyzeRepository(rootDir);
1152
- const counts = { in_prod: 0, reachable: 0, defined_only: 0, unknown: 0 };
1153
- let tagged = 0;
1154
- const items = [
1155
- ...Array.isArray(graph.runtimeAgents) ? graph.runtimeAgents : [],
1156
- ...Array.isArray(graph.artifacts) ? graph.artifacts : []
1157
- ];
1158
- for (const item of items) {
1159
- if (!isRecord(item))
1160
- continue;
1161
- if (item.status === undefined || !HARNESS_COMPONENT_STATUSES.has(item.status)) {
1162
- const status = itemStatus(analysis, rootDir, item);
1163
- if (status) {
1164
- item.status = staticStatus(status);
1165
- tagged += 1;
1166
- } else {
1167
- delete item.status;
1168
- }
1169
- } else {
1170
- item.status = staticStatus(item.status);
1171
- }
1172
- counts[item.status ?? "unknown"] += 1;
1173
- }
1174
- if (isRecord(graph.scan)) {
1175
- graph.scan.componentStatusCounts = counts;
1176
- }
1177
- return { tagged, counts };
1178
- } catch {
1179
- return { tagged: 0, counts: {} };
1180
- }
1181
- }
1182
-
1183
- export { mergeBoundRuntimeClasses, HARNESS_COMPONENT_STATUSES, normalizeComponentStatus, buildImportGraphReachability, tagComponentStatus };