@living-architecture/riviere-cli 0.8.10 → 0.8.12

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 (3) hide show
  1. package/dist/bin.js +412 -266
  2. package/dist/index.js +411 -265
  3. package/package.json +6 -6
package/dist/bin.js CHANGED
@@ -2980,7 +2980,7 @@ var require_compile = __commonJS({
2980
2980
  const schOrFunc = root.refs[ref];
2981
2981
  if (schOrFunc)
2982
2982
  return schOrFunc;
2983
- let _sch = resolve6.call(this, root, ref);
2983
+ let _sch = resolve8.call(this, root, ref);
2984
2984
  if (_sch === void 0) {
2985
2985
  const schema = (_a2 = root.localRefs) === null || _a2 === void 0 ? void 0 : _a2[ref];
2986
2986
  const { schemaId } = this.opts;
@@ -3007,7 +3007,7 @@ var require_compile = __commonJS({
3007
3007
  function sameSchemaEnv(s1, s2) {
3008
3008
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3009
3009
  }
3010
- function resolve6(root, ref) {
3010
+ function resolve8(root, ref) {
3011
3011
  let sch;
3012
3012
  while (typeof (sch = this.refs[ref]) == "string")
3013
3013
  ref = sch;
@@ -3582,7 +3582,7 @@ var require_fast_uri = __commonJS({
3582
3582
  }
3583
3583
  return uri;
3584
3584
  }
3585
- function resolve6(baseURI, relativeURI, options) {
3585
+ function resolve8(baseURI, relativeURI, options) {
3586
3586
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3587
3587
  const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
3588
3588
  schemelessOptions.skipEscape = true;
@@ -3809,7 +3809,7 @@ var require_fast_uri = __commonJS({
3809
3809
  var fastUri = {
3810
3810
  SCHEMES,
3811
3811
  normalize,
3812
- resolve: resolve6,
3812
+ resolve: resolve8,
3813
3813
  resolveComponent,
3814
3814
  equal,
3815
3815
  serialize,
@@ -25180,7 +25180,8 @@ import { Command as Command21 } from "commander";
25180
25180
  // src/platform/infra/extraction-config/config-loader.ts
25181
25181
  import {
25182
25182
  dirname as dirname2,
25183
- resolve as resolve2
25183
+ resolve as resolve2,
25184
+ posix as posix3
25184
25185
  } from "node:path";
25185
25186
  import {
25186
25187
  existsSync as existsSync2,
@@ -25241,7 +25242,7 @@ var extraction_config_schema_default = {
25241
25242
  module: {
25242
25243
  type: "object",
25243
25244
  description: "A module defines extraction rules for a path pattern",
25244
- required: ["name", "path"],
25245
+ required: ["name", "path", "glob"],
25245
25246
  additionalProperties: false,
25246
25247
  properties: {
25247
25248
  name: {
@@ -25251,7 +25252,12 @@ var extraction_config_schema_default = {
25251
25252
  },
25252
25253
  path: {
25253
25254
  type: "string",
25254
- description: "Glob pattern for files in this module",
25255
+ description: "Module root directory relative to config file",
25256
+ minLength: 1
25257
+ },
25258
+ glob: {
25259
+ type: "string",
25260
+ description: "Glob pattern for source files within the module directory",
25255
25261
  minLength: 1
25256
25262
  },
25257
25263
  extends: {
@@ -26318,11 +26324,11 @@ function createFunctionComponent(func, filePath, domain2, componentType) {
26318
26324
  function findMatchingModule(filePath, modules, globMatcher, configDir) {
26319
26325
  const normalized = filePath.replaceAll(/\\+/g, "/");
26320
26326
  if (configDir === void 0) {
26321
- return modules.find((m) => globMatcher(normalized, m.path));
26327
+ return modules.find((m) => globMatcher(normalized, posix.join(m.path, m.glob)));
26322
26328
  }
26323
26329
  const normalizedConfigDir = configDir.replaceAll(/\\+/g, "/");
26324
26330
  const pathToMatch = posix.relative(normalizedConfigDir, normalized);
26325
- return modules.find((m) => globMatcher(pathToMatch, m.path));
26331
+ return modules.find((m) => globMatcher(pathToMatch, posix.join(m.path, m.glob)));
26326
26332
  }
26327
26333
 
26328
26334
  // ../riviere-extract-ts/dist/features/extraction/domain/config-resolution/config-resolution-errors.js
@@ -26360,6 +26366,7 @@ function resolveModule(moduleConfig, loader) {
26360
26366
  return {
26361
26367
  name: moduleConfig.name,
26362
26368
  path: moduleConfig.path,
26369
+ glob: moduleConfig.glob,
26363
26370
  api: requireRule(moduleConfig.api, "api", moduleConfig.name),
26364
26371
  useCase: requireRule(moduleConfig.useCase, "useCase", moduleConfig.name),
26365
26372
  domainOp: requireRule(moduleConfig.domainOp, "domainOp", moduleConfig.name),
@@ -26379,6 +26386,7 @@ function resolveModuleWithExtends(moduleConfig, extendsSource, loader) {
26379
26386
  return {
26380
26387
  name: moduleConfig.name,
26381
26388
  path: moduleConfig.path,
26389
+ glob: moduleConfig.glob,
26382
26390
  api: moduleConfig.api ?? baseModule.api,
26383
26391
  useCase: moduleConfig.useCase ?? baseModule.useCase,
26384
26392
  domainOp: moduleConfig.domainOp ?? baseModule.domainOp,
@@ -28845,50 +28853,48 @@ function traceNonComponent(project, componentIndex, source, typeName, calledMeth
28845
28853
  }
28846
28854
 
28847
28855
  // ../riviere-extract-ts/dist/features/extraction/domain/connection-detection/async-detection/detect-publish-connections.js
28848
- function detectPublishConnections(project, components, options) {
28856
+ function detectPublishConnections(components, options) {
28849
28857
  const publishers = components.filter((c) => c.type === "eventPublisher");
28850
28858
  const events = components.filter((c) => c.type === "event");
28851
28859
  const repository = options.repository;
28852
- return publishers.flatMap((publisher) => extractPublisherLinks(project, publisher, events, options, repository));
28853
- }
28854
- function extractPublisherLinks(project, publisher, events, options, repository) {
28855
- const publishedEventType = publisher.metadata["publishedEventType"];
28856
- if (typeof publishedEventType === "string") {
28860
+ return publishers.flatMap((publisher) => {
28861
+ const publishedEventType = publisher.metadata["publishedEventType"];
28857
28862
  const sourceLocation = {
28858
28863
  repository,
28859
28864
  filePath: publisher.location.file,
28860
28865
  lineNumber: publisher.location.line
28861
28866
  };
28862
- return resolvePublishTarget(publisher, publishedEventType, events, options, sourceLocation);
28863
- }
28864
- const classDecl = findClassInProject(project, publisher);
28865
- if (classDecl === void 0) {
28866
- return [];
28867
- }
28868
- const methods = classDecl.getMethods();
28869
- return methods.flatMap((method) => {
28870
- const firstParam = method.getParameters()[0];
28871
- if (firstParam === void 0) {
28872
- return [];
28867
+ if (typeof publishedEventType !== "string") {
28868
+ return [handleMissingMetadata(publisher, options, sourceLocation)];
28873
28869
  }
28874
- const paramType = firstParam.getType();
28875
- const paramTypeName = stripGenericArgs(paramType.getText(firstParam));
28876
- const sourceLocation = {
28877
- repository,
28878
- filePath: publisher.location.file,
28879
- lineNumber: method.getStartLineNumber()
28880
- };
28881
- return resolvePublishTarget(publisher, paramTypeName, events, options, sourceLocation);
28870
+ return resolvePublishTarget(publisher, publishedEventType, events, options, sourceLocation);
28882
28871
  });
28883
28872
  }
28884
- function resolvePublishTarget(publisher, paramTypeName, events, options, sourceLocation) {
28885
- const matchingEvents = events.filter((e) => e.metadata["eventName"] === paramTypeName);
28873
+ function handleMissingMetadata(publisher, options, sourceLocation) {
28874
+ if (options.strict) {
28875
+ throw new ConnectionDetectionError({
28876
+ file: sourceLocation.filePath,
28877
+ line: sourceLocation.lineNumber,
28878
+ typeName: publisher.name,
28879
+ reason: 'eventPublisher is missing required "publishedEventType" metadata'
28880
+ });
28881
+ }
28882
+ return {
28883
+ source: componentIdentity(publisher),
28884
+ target: "_unresolved",
28885
+ type: "async",
28886
+ sourceLocation,
28887
+ _uncertain: `eventPublisher "${publisher.name}" is missing required "publishedEventType" metadata`
28888
+ };
28889
+ }
28890
+ function resolvePublishTarget(publisher, publishedEventType, events, options, sourceLocation) {
28891
+ const matchingEvents = events.filter((e) => e.metadata["eventName"] === publishedEventType);
28886
28892
  if (matchingEvents.length === 0) {
28887
- return [handleNoMatch(publisher, paramTypeName, options, sourceLocation)];
28893
+ return [handleNoMatch(publisher, publishedEventType, options, sourceLocation)];
28888
28894
  }
28889
28895
  if (matchingEvents.length > 1) {
28890
28896
  return [
28891
- handleAmbiguousMatch(publisher, paramTypeName, matchingEvents.length, options, sourceLocation)
28897
+ handleAmbiguousMatch(publisher, publishedEventType, matchingEvents.length, options, sourceLocation)
28892
28898
  ];
28893
28899
  }
28894
28900
  return matchingEvents.map((event) => ({
@@ -28898,13 +28904,13 @@ function resolvePublishTarget(publisher, paramTypeName, events, options, sourceL
28898
28904
  sourceLocation
28899
28905
  }));
28900
28906
  }
28901
- function handleAmbiguousMatch(publisher, paramTypeName, matchCount, options, sourceLocation) {
28907
+ function handleAmbiguousMatch(publisher, publishedEventType, matchCount, options, sourceLocation) {
28902
28908
  if (options.strict) {
28903
28909
  throw new ConnectionDetectionError({
28904
28910
  file: sourceLocation.filePath,
28905
28911
  line: sourceLocation.lineNumber,
28906
28912
  typeName: publisher.name,
28907
- reason: `parameter type "${paramTypeName}" matches ${matchCount} Event components (ambiguous)`
28913
+ reason: `publishedEventType "${publishedEventType}" matches ${matchCount} Event components (ambiguous)`
28908
28914
  });
28909
28915
  }
28910
28916
  return {
@@ -28912,16 +28918,16 @@ function handleAmbiguousMatch(publisher, paramTypeName, matchCount, options, sou
28912
28918
  target: "_unresolved",
28913
28919
  type: "async",
28914
28920
  sourceLocation,
28915
- _uncertain: `ambiguous: ${matchCount} events match parameter type: ${paramTypeName}`
28921
+ _uncertain: `ambiguous: ${matchCount} events match publishedEventType: ${publishedEventType}`
28916
28922
  };
28917
28923
  }
28918
- function handleNoMatch(publisher, paramTypeName, options, sourceLocation) {
28924
+ function handleNoMatch(publisher, publishedEventType, options, sourceLocation) {
28919
28925
  if (options.strict) {
28920
28926
  throw new ConnectionDetectionError({
28921
28927
  file: sourceLocation.filePath,
28922
28928
  line: sourceLocation.lineNumber,
28923
28929
  typeName: publisher.name,
28924
- reason: `parameter type "${paramTypeName}" does not match any Event component`
28930
+ reason: `publishedEventType "${publishedEventType}" does not match any Event component`
28925
28931
  });
28926
28932
  }
28927
28933
  return {
@@ -28929,7 +28935,7 @@ function handleNoMatch(publisher, paramTypeName, options, sourceLocation) {
28929
28935
  target: "_unresolved",
28930
28936
  type: "async",
28931
28937
  sourceLocation,
28932
- _uncertain: `no event found for parameter type: ${paramTypeName}`
28938
+ _uncertain: `no event found for publishedEventType: ${publishedEventType}`
28933
28939
  };
28934
28940
  }
28935
28941
 
@@ -29238,8 +29244,7 @@ function deduplicateCrossStrategy(links) {
29238
29244
  }
29239
29245
  return [...seen.values()];
29240
29246
  }
29241
- function detectConnections(project, components, options, globMatcher) {
29242
- const totalStart = performance.now();
29247
+ function detectPerModuleConnections(project, components, options, globMatcher) {
29243
29248
  const setupStart = performance.now();
29244
29249
  const componentIndex = new ComponentIndex(components);
29245
29250
  const sourceFilePaths = computeFilteredFilePaths(project, options.moduleGlobs, globMatcher);
@@ -29253,43 +29258,50 @@ function detectConnections(project, components, options, globMatcher) {
29253
29258
  repository
29254
29259
  });
29255
29260
  const callGraphMs = performance.now() - callGraphStart;
29261
+ const patterns = options.patterns ?? [];
29262
+ const { configurableLinks, configurableMs } = runConfigurableDetection(project, patterns, components, componentIndex, strict, repository);
29263
+ return {
29264
+ links: [...syncLinks, ...configurableLinks],
29265
+ timings: {
29266
+ callGraphMs,
29267
+ configurableMs,
29268
+ setupMs
29269
+ }
29270
+ };
29271
+ }
29272
+ function detectCrossModuleConnections(allComponents, options) {
29273
+ const strict = options.allowIncomplete !== true;
29274
+ const repository = options.repository;
29256
29275
  const asyncStart = performance.now();
29257
- const publishLinks = detectPublishConnections(project, components, {
29276
+ const publishLinks = detectPublishConnections(allComponents, {
29258
29277
  strict,
29259
29278
  repository
29260
29279
  });
29261
- const subscribeLinks = detectSubscribeConnections(components, {
29280
+ const subscribeLinks = detectSubscribeConnections(allComponents, {
29262
29281
  strict,
29263
29282
  repository
29264
29283
  });
29265
29284
  const asyncDetectionMs = performance.now() - asyncStart;
29266
- const patterns = options.patterns ?? [];
29267
- const { configurableLinks, configurableMs } = patterns.length > 0 ? (() => {
29268
- const configurableStart = performance.now();
29269
- const links = detectConfigurableConnections(project, patterns, components, componentIndex, {
29270
- strict,
29271
- repository
29272
- });
29285
+ return {
29286
+ links: [...publishLinks, ...subscribeLinks],
29287
+ timings: { asyncDetectionMs }
29288
+ };
29289
+ }
29290
+ function runConfigurableDetection(project, patterns, components, componentIndex, strict, repository) {
29291
+ if (patterns.length === 0) {
29273
29292
  return {
29274
- configurableLinks: links,
29275
- configurableMs: performance.now() - configurableStart
29293
+ configurableLinks: [],
29294
+ configurableMs: 0
29276
29295
  };
29277
- })() : {
29278
- configurableLinks: [],
29279
- configurableMs: 0
29280
- };
29281
- const totalMs = performance.now() - totalStart;
29282
- const allLinks = [...syncLinks, ...publishLinks, ...subscribeLinks, ...configurableLinks];
29283
- const deduplicatedLinks = deduplicateCrossStrategy(allLinks);
29296
+ }
29297
+ const configurableStart = performance.now();
29298
+ const links = detectConfigurableConnections(project, patterns, components, componentIndex, {
29299
+ strict,
29300
+ repository
29301
+ });
29284
29302
  return {
29285
- links: deduplicatedLinks,
29286
- timings: {
29287
- callGraphMs,
29288
- asyncDetectionMs,
29289
- configurableMs,
29290
- setupMs,
29291
- totalMs
29292
- }
29303
+ configurableLinks: links,
29304
+ configurableMs: performance.now() - configurableStart
29293
29305
  };
29294
29306
  }
29295
29307
 
@@ -29298,7 +29310,7 @@ import { posix as posix2 } from "node:path";
29298
29310
  function findMatchingModule2(filePath, modules, globMatcher, configDir) {
29299
29311
  const normalized = filePath.replaceAll(/\\+/g, "/");
29300
29312
  const pathToMatch = posix2.relative(configDir.replaceAll(/\\+/g, "/"), normalized);
29301
- return modules.find((m) => globMatcher(pathToMatch, m.path));
29313
+ return modules.find((m) => globMatcher(pathToMatch, posix2.join(m.path, m.glob)));
29302
29314
  }
29303
29315
  function getBuiltInRule(module, componentType) {
29304
29316
  const ruleMap = {
@@ -29520,7 +29532,8 @@ var NOT_USED = { notUsed: true };
29520
29532
  function topLevelRulesToModule(parsed) {
29521
29533
  return {
29522
29534
  name: "extended",
29523
- path: "**",
29535
+ path: ".",
29536
+ glob: "**",
29524
29537
  api: parsed.api ?? NOT_USED,
29525
29538
  useCase: parsed.useCase ?? NOT_USED,
29526
29539
  domainOp: parsed.domainOp ?? NOT_USED,
@@ -29640,9 +29653,9 @@ function tryExpandModuleRefs(data, configDir) {
29640
29653
  }
29641
29654
  }
29642
29655
  function resolveSourceFiles(resolvedConfig, configDir) {
29643
- const sourceFilePaths = resolvedConfig.modules.flatMap((module) => globSync(module.path, { cwd: configDir })).map((filePath) => resolve2(configDir, filePath));
29656
+ const sourceFilePaths = resolvedConfig.modules.flatMap((module) => globSync(posix3.join(module.path, module.glob), { cwd: configDir })).map((filePath) => resolve2(configDir, filePath));
29644
29657
  if (sourceFilePaths.length === 0) {
29645
- const patterns = resolvedConfig.modules.map((m) => m.path).join(", ");
29658
+ const patterns = resolvedConfig.modules.map((m) => posix3.join(m.path, m.glob)).join(", ");
29646
29659
  throw new ConfigValidationError(
29647
29660
  "VALIDATION_ERROR" /* ValidationError */,
29648
29661
  `No files matched extraction patterns: ${patterns}
@@ -29897,111 +29910,6 @@ function validateFlagCombinations(options) {
29897
29910
  validateFormatOption(options);
29898
29911
  }
29899
29912
 
29900
- // src/platform/infra/cli-presentation/format-pr-markdown.ts
29901
- function formatComponentLine(component) {
29902
- return `- **${component.type}** \`${component.name}\` in \`${component.domain}\` domain`;
29903
- }
29904
- function formatSection(title, components) {
29905
- const header = `### ${title} (${components.length})`;
29906
- if (components.length === 0) {
29907
- return `${header}
29908
- None`;
29909
- }
29910
- return `${header}
29911
- ${components.map(formatComponentLine).join("\n")}`;
29912
- }
29913
- function formatPrMarkdown(categorized) {
29914
- const sections = [
29915
- formatSection("Added Components", categorized.added),
29916
- formatSection("Modified Components", categorized.modified),
29917
- formatSection("Removed Components", categorized.removed)
29918
- ];
29919
- return `## Architecture Changes
29920
-
29921
- ${sections.join("\n\n")}`;
29922
- }
29923
-
29924
- // src/platform/infra/cli-presentation/extract-output-formatter.ts
29925
- function compareByCodePoint2(a, b) {
29926
- if (a < b) return -1;
29927
- if (a > b) return 1;
29928
- return 0;
29929
- }
29930
- function formatDryRunOutput(components) {
29931
- const countsByDomain = /* @__PURE__ */ new Map();
29932
- for (const component of components) {
29933
- const existingTypeCounts = countsByDomain.get(component.domain);
29934
- const typeCounts = existingTypeCounts ?? /* @__PURE__ */ new Map();
29935
- if (existingTypeCounts === void 0) {
29936
- countsByDomain.set(component.domain, typeCounts);
29937
- }
29938
- const currentCount = typeCounts.get(component.type) ?? 0;
29939
- typeCounts.set(component.type, currentCount + 1);
29940
- }
29941
- const sortedDomains = [...countsByDomain.entries()].sort(([a], [b]) => compareByCodePoint2(a, b));
29942
- const lines = [];
29943
- for (const [domain2, typeCounts] of sortedDomains) {
29944
- const typeStrings = [...typeCounts.entries()].sort(([a], [b]) => compareByCodePoint2(a, b)).map(([type, count]) => `${type}(${count})`);
29945
- lines.push(`${domain2}: ${typeStrings.join(", ")}`);
29946
- }
29947
- return lines;
29948
- }
29949
-
29950
- // src/platform/infra/cli-presentation/output-writer.ts
29951
- import { writeFileSync } from "node:fs";
29952
- function outputResult(data, options) {
29953
- if (options.output !== void 0) {
29954
- try {
29955
- writeFileSync(options.output, JSON.stringify(data));
29956
- } catch {
29957
- console.log(
29958
- JSON.stringify(
29959
- formatError2(
29960
- "VALIDATION_ERROR" /* ValidationError */,
29961
- "Failed to write output file: " + options.output
29962
- )
29963
- )
29964
- );
29965
- process.exit(3 /* RuntimeError */);
29966
- }
29967
- return;
29968
- }
29969
- console.log(JSON.stringify(data));
29970
- }
29971
-
29972
- // src/platform/infra/cli-presentation/format-extraction-stats.ts
29973
- function countLinksByType(componentCount, links) {
29974
- const syncLinkCount = links.filter((l) => l.type === "sync").length;
29975
- const asyncLinkCount = links.filter((l) => l.type === "async").length;
29976
- const uncertainLinkCount = links.filter((l) => l._uncertain !== void 0).length;
29977
- return {
29978
- componentCount,
29979
- linkCount: links.length,
29980
- syncLinkCount,
29981
- asyncLinkCount,
29982
- uncertainLinkCount
29983
- };
29984
- }
29985
- function formatSeconds(ms) {
29986
- return (ms / 1e3).toFixed(2) + "s";
29987
- }
29988
- function formatExtractionStats(stats) {
29989
- const lines = [`Components: ${stats.componentCount}`];
29990
- if (stats.linkCount !== void 0) {
29991
- lines.push(
29992
- `Links: ${stats.linkCount} (sync: ${stats.syncLinkCount}, async: ${stats.asyncLinkCount})`
29993
- );
29994
- lines.push(`Uncertain: ${stats.uncertainLinkCount}`);
29995
- }
29996
- return lines;
29997
- }
29998
- function formatTimingLine(timings) {
29999
- return `Extraction completed in ${formatSeconds(timings.totalMs)} (call graph: ${formatSeconds(timings.callGraphMs)}, detection: ${formatSeconds(timings.asyncDetectionMs)}, setup: ${formatSeconds(timings.setupMs)})`;
30000
- }
30001
-
30002
- // src/features/extract/infra/safe-extraction-operations.ts
30003
- import "ts-morph";
30004
-
30005
29913
  // src/platform/infra/extraction-config/draft-component-loader.ts
30006
29914
  import {
30007
29915
  existsSync as existsSync4,
@@ -30040,43 +29948,6 @@ function loadDraftComponentsFromFile(filePath) {
30040
29948
  return parsed;
30041
29949
  }
30042
29950
 
30043
- // src/features/extract/infra/safe-extraction-operations.ts
30044
- function loadOrExtractComponents(project, sourceFilePaths, resolvedConfig, configDir, enrichPath) {
30045
- if (enrichPath === void 0) {
30046
- return extractComponents(project, sourceFilePaths, resolvedConfig, matchesGlob, configDir);
30047
- }
30048
- return loadDraftComponentsFromFile(enrichPath);
30049
- }
30050
- function enrichComponentsSafe(draftComponents, resolvedConfig, project, configDir, allowIncomplete) {
30051
- const result = enrichComponents(draftComponents, resolvedConfig, project, matchesGlob, configDir);
30052
- if (result.failures.length > 0) {
30053
- const failedFields = result.failures.map((f) => f.field);
30054
- if (!allowIncomplete) {
30055
- throw new ExtractionFieldFailureError(failedFields);
30056
- }
30057
- console.error(
30058
- `Warning: Enrichment failed for ${failedFields.length} field(s): ${failedFields.join(", ")}`
30059
- );
30060
- }
30061
- return result;
30062
- }
30063
- function detectConnectionsSafe(project, components, moduleGlobs, repository, allowIncomplete, showStats) {
30064
- const result = detectConnections(
30065
- project,
30066
- components,
30067
- {
30068
- allowIncomplete,
30069
- moduleGlobs,
30070
- repository
30071
- },
30072
- matchesGlob
30073
- );
30074
- if (showStats) {
30075
- console.error(formatTimingLine(result.timings));
30076
- }
30077
- return result;
30078
- }
30079
-
30080
29951
  // src/platform/infra/git/git-repository-info.ts
30081
29952
  import { execFileSync as execFileSync2 } from "node:child_process";
30082
29953
  var RepositoryUrlParseError = class extends Error {
@@ -30158,31 +30029,316 @@ function getRepositoryInfo(gitBinary = "git", cwd = process.cwd(), executor = de
30158
30029
  }
30159
30030
  }
30160
30031
 
30161
- // src/features/extract/infra/ts-morph/create-configured-project.ts
30032
+ // src/features/extract/infra/external-clients/create-module-contexts.ts
30033
+ import {
30034
+ posix as posix4,
30035
+ resolve as resolve7
30036
+ } from "node:path";
30037
+ import { globSync as globSync2 } from "glob";
30038
+
30039
+ // src/features/extract/infra/external-clients/find-module-tsconfig-dir.ts
30162
30040
  import { existsSync as existsSync5 } from "node:fs";
30163
30041
  import { resolve as resolve5 } from "node:path";
30164
- import { Project as Project3 } from "ts-morph";
30042
+ function findModuleTsConfigDir(configDir, modulePath) {
30043
+ const moduleTsConfigPath = resolve5(configDir, modulePath, "tsconfig.json");
30044
+ if (existsSync5(moduleTsConfigPath)) {
30045
+ return resolve5(configDir, modulePath);
30046
+ }
30047
+ return configDir;
30048
+ }
30049
+
30050
+ // src/features/extract/infra/external-clients/create-configured-project.ts
30051
+ import { existsSync as existsSync6 } from "node:fs";
30052
+ import { resolve as resolve6 } from "node:path";
30053
+ import { Project as Project2 } from "ts-morph";
30165
30054
  function createConfiguredProject(configDir, skipTsConfig) {
30166
30055
  if (skipTsConfig) {
30167
- return new Project3();
30056
+ return new Project2();
30168
30057
  }
30169
- const tsConfigPath = resolve5(configDir, "tsconfig.json");
30170
- if (!existsSync5(tsConfigPath)) {
30171
- return new Project3();
30058
+ const tsConfigPath = resolve6(configDir, "tsconfig.json");
30059
+ if (!existsSync6(tsConfigPath)) {
30060
+ return new Project2();
30172
30061
  }
30173
- return new Project3({
30062
+ return new Project2({
30174
30063
  tsConfigFilePath: tsConfigPath,
30175
30064
  skipAddingFilesFromTsConfig: true
30176
30065
  });
30177
30066
  }
30178
30067
 
30179
- // src/features/extract/infra/load-extraction-project.ts
30068
+ // src/features/extract/infra/external-clients/load-extraction-project.ts
30180
30069
  function loadExtractionProject(configDir, sourceFilePaths, skipTsConfig) {
30181
30070
  const project = createConfiguredProject(configDir, skipTsConfig);
30182
30071
  project.addSourceFilesAtPaths(sourceFilePaths);
30183
30072
  return project;
30184
30073
  }
30185
30074
 
30075
+ // src/features/extract/infra/external-clients/create-module-contexts.ts
30076
+ function createModuleContexts(resolvedConfig, configDir, sourceFilePaths, skipTsConfig) {
30077
+ const sourceFileSet = new Set(sourceFilePaths);
30078
+ return resolvedConfig.modules.map((module) => {
30079
+ const allModuleFiles = globSync2(posix4.join(module.path, module.glob), { cwd: configDir }).map(
30080
+ (f) => resolve7(configDir, f)
30081
+ );
30082
+ const moduleFiles = allModuleFiles.filter((f) => sourceFileSet.has(f));
30083
+ const tsConfigDir = findModuleTsConfigDir(configDir, module.path);
30084
+ const project = loadExtractionProject(tsConfigDir, moduleFiles, skipTsConfig);
30085
+ return {
30086
+ module,
30087
+ files: moduleFiles,
30088
+ project
30089
+ };
30090
+ });
30091
+ }
30092
+
30093
+ // src/features/extract/domain/extract-draft-components.ts
30094
+ function extractDraftComponents(moduleContexts, resolvedConfig, configDir) {
30095
+ return moduleContexts.flatMap(
30096
+ (ctx) => extractComponents(ctx.project, ctx.files, resolvedConfig, matchesGlob, configDir)
30097
+ );
30098
+ }
30099
+
30100
+ // src/features/extract/domain/enrich-per-module.ts
30101
+ var OrphanedDraftComponentError = class extends Error {
30102
+ constructor(orphanedModules, knownModules) {
30103
+ super(
30104
+ `Draft components reference unknown modules: [${orphanedModules.join(", ")}]. Known modules: [${knownModules.join(", ")}]`
30105
+ );
30106
+ this.name = "OrphanedDraftComponentError";
30107
+ }
30108
+ };
30109
+ function enrichPerModule(moduleContexts, draftComponents, resolvedConfig, configDir) {
30110
+ const moduleNames = new Set(moduleContexts.map((ctx) => ctx.module.name));
30111
+ const draftsByModule = groupDraftsByModule(draftComponents);
30112
+ assertAllDraftsMatchModules(draftsByModule, moduleNames);
30113
+ const components = [];
30114
+ const failedFieldSet = /* @__PURE__ */ new Set();
30115
+ for (const ctx of moduleContexts) {
30116
+ const moduleDrafts = draftsByModule.get(ctx.module.name) ?? [];
30117
+ if (moduleDrafts.length === 0) {
30118
+ continue;
30119
+ }
30120
+ const result = enrichComponents(
30121
+ moduleDrafts,
30122
+ resolvedConfig,
30123
+ ctx.project,
30124
+ matchesGlob,
30125
+ configDir
30126
+ );
30127
+ components.push(...result.components);
30128
+ for (const f of result.failures) {
30129
+ failedFieldSet.add(f.field);
30130
+ }
30131
+ }
30132
+ return {
30133
+ components,
30134
+ failedFields: [...failedFieldSet]
30135
+ };
30136
+ }
30137
+ function assertAllDraftsMatchModules(draftsByModule, moduleNames) {
30138
+ const orphanedModules = [...draftsByModule.keys()].filter((name) => !moduleNames.has(name));
30139
+ if (orphanedModules.length > 0) {
30140
+ throw new OrphanedDraftComponentError(orphanedModules, [...moduleNames]);
30141
+ }
30142
+ }
30143
+ function groupDraftsByModule(drafts) {
30144
+ const grouped = /* @__PURE__ */ new Map();
30145
+ for (const draft of drafts) {
30146
+ const existing = grouped.get(draft.domain);
30147
+ if (existing) {
30148
+ existing.push(draft);
30149
+ } else {
30150
+ grouped.set(draft.domain, [draft]);
30151
+ }
30152
+ }
30153
+ return grouped;
30154
+ }
30155
+
30156
+ // src/features/extract/domain/detect-connections-per-module.ts
30157
+ import { posix as posix5 } from "node:path";
30158
+ function detectConnectionsPerModule(moduleContexts, enrichedComponents, repositoryName, allowIncomplete) {
30159
+ const links = [];
30160
+ const timings = [];
30161
+ for (const ctx of moduleContexts) {
30162
+ const moduleComponents = enrichedComponents.filter((c) => c.domain === ctx.module.name);
30163
+ if (moduleComponents.length === 0) {
30164
+ continue;
30165
+ }
30166
+ const result = detectPerModuleConnections(
30167
+ ctx.project,
30168
+ moduleComponents,
30169
+ {
30170
+ allowIncomplete,
30171
+ moduleGlobs: [posix5.join(ctx.module.path, ctx.module.glob)],
30172
+ repository: repositoryName
30173
+ },
30174
+ matchesGlob
30175
+ );
30176
+ links.push(...result.links);
30177
+ timings.push({
30178
+ callGraphMs: result.timings.callGraphMs,
30179
+ asyncDetectionMs: 0,
30180
+ configurableMs: result.timings.configurableMs,
30181
+ setupMs: result.timings.setupMs,
30182
+ totalMs: result.timings.callGraphMs + result.timings.configurableMs + result.timings.setupMs
30183
+ });
30184
+ }
30185
+ const crossResult = detectCrossModuleConnections(enrichedComponents, {
30186
+ allowIncomplete,
30187
+ repository: repositoryName
30188
+ });
30189
+ links.push(...crossResult.links);
30190
+ timings.push({
30191
+ callGraphMs: 0,
30192
+ asyncDetectionMs: crossResult.timings.asyncDetectionMs,
30193
+ configurableMs: 0,
30194
+ setupMs: 0,
30195
+ totalMs: crossResult.timings.asyncDetectionMs
30196
+ });
30197
+ return {
30198
+ links: deduplicateCrossStrategy(links),
30199
+ timings
30200
+ };
30201
+ }
30202
+
30203
+ // src/features/extract/commands/run-extraction.ts
30204
+ function runExtraction(options, resolvedConfig, configDir, sourceFilePaths) {
30205
+ const skipTsConfig = options.tsConfig === false;
30206
+ const moduleContexts = createModuleContexts(
30207
+ resolvedConfig,
30208
+ configDir,
30209
+ sourceFilePaths,
30210
+ skipTsConfig
30211
+ );
30212
+ const draftComponents = options.enrich === void 0 ? extractDraftComponents(moduleContexts, resolvedConfig, configDir) : loadDraftComponentsFromFile(options.enrich);
30213
+ if (options.dryRun || options.format === "markdown" || options.componentsOnly) {
30214
+ return {
30215
+ kind: "draftOnly",
30216
+ components: draftComponents
30217
+ };
30218
+ }
30219
+ const allowIncomplete = options.allowIncomplete === true;
30220
+ const enrichment = enrichPerModule(moduleContexts, draftComponents, resolvedConfig, configDir);
30221
+ if (enrichment.failedFields.length > 0 && !allowIncomplete) {
30222
+ throw new ExtractionFieldFailureError(enrichment.failedFields);
30223
+ }
30224
+ const repositoryInfo = getRepositoryInfo();
30225
+ const connectionResult = detectConnectionsPerModule(
30226
+ moduleContexts,
30227
+ enrichment.components,
30228
+ repositoryInfo.name,
30229
+ allowIncomplete
30230
+ );
30231
+ return {
30232
+ kind: "full",
30233
+ components: enrichment.components,
30234
+ links: connectionResult.links,
30235
+ timings: connectionResult.timings,
30236
+ failedFields: enrichment.failedFields
30237
+ };
30238
+ }
30239
+
30240
+ // src/platform/infra/cli-presentation/format-pr-markdown.ts
30241
+ function formatComponentLine(component) {
30242
+ return `- **${component.type}** \`${component.name}\` in \`${component.domain}\` domain`;
30243
+ }
30244
+ function formatSection(title, components) {
30245
+ const header = `### ${title} (${components.length})`;
30246
+ if (components.length === 0) {
30247
+ return `${header}
30248
+ None`;
30249
+ }
30250
+ return `${header}
30251
+ ${components.map(formatComponentLine).join("\n")}`;
30252
+ }
30253
+ function formatPrMarkdown(categorized) {
30254
+ const sections = [
30255
+ formatSection("Added Components", categorized.added),
30256
+ formatSection("Modified Components", categorized.modified),
30257
+ formatSection("Removed Components", categorized.removed)
30258
+ ];
30259
+ return `## Architecture Changes
30260
+
30261
+ ${sections.join("\n\n")}`;
30262
+ }
30263
+
30264
+ // src/platform/infra/cli-presentation/extract-output-formatter.ts
30265
+ function compareByCodePoint2(a, b) {
30266
+ if (a < b) return -1;
30267
+ if (a > b) return 1;
30268
+ return 0;
30269
+ }
30270
+ function formatDryRunOutput(components) {
30271
+ const countsByDomain = /* @__PURE__ */ new Map();
30272
+ for (const component of components) {
30273
+ const existingTypeCounts = countsByDomain.get(component.domain);
30274
+ const typeCounts = existingTypeCounts ?? /* @__PURE__ */ new Map();
30275
+ if (existingTypeCounts === void 0) {
30276
+ countsByDomain.set(component.domain, typeCounts);
30277
+ }
30278
+ const currentCount = typeCounts.get(component.type) ?? 0;
30279
+ typeCounts.set(component.type, currentCount + 1);
30280
+ }
30281
+ const sortedDomains = [...countsByDomain.entries()].sort(([a], [b]) => compareByCodePoint2(a, b));
30282
+ const lines = [];
30283
+ for (const [domain2, typeCounts] of sortedDomains) {
30284
+ const typeStrings = [...typeCounts.entries()].sort(([a], [b]) => compareByCodePoint2(a, b)).map(([type, count]) => `${type}(${count})`);
30285
+ lines.push(`${domain2}: ${typeStrings.join(", ")}`);
30286
+ }
30287
+ return lines;
30288
+ }
30289
+
30290
+ // src/platform/infra/cli-presentation/output-writer.ts
30291
+ import { writeFileSync } from "node:fs";
30292
+ function outputResult(data, options) {
30293
+ if (options.output !== void 0) {
30294
+ try {
30295
+ writeFileSync(options.output, JSON.stringify(data));
30296
+ } catch {
30297
+ console.log(
30298
+ JSON.stringify(
30299
+ formatError2(
30300
+ "VALIDATION_ERROR" /* ValidationError */,
30301
+ "Failed to write output file: " + options.output
30302
+ )
30303
+ )
30304
+ );
30305
+ process.exit(3 /* RuntimeError */);
30306
+ }
30307
+ return;
30308
+ }
30309
+ console.log(JSON.stringify(data));
30310
+ }
30311
+
30312
+ // src/platform/infra/cli-presentation/format-extraction-stats.ts
30313
+ function countLinksByType(componentCount, links) {
30314
+ const syncLinkCount = links.filter((l) => l.type === "sync").length;
30315
+ const asyncLinkCount = links.filter((l) => l.type === "async").length;
30316
+ const uncertainLinkCount = links.filter((l) => l._uncertain !== void 0).length;
30317
+ return {
30318
+ componentCount,
30319
+ linkCount: links.length,
30320
+ syncLinkCount,
30321
+ asyncLinkCount,
30322
+ uncertainLinkCount
30323
+ };
30324
+ }
30325
+ function formatSeconds(ms) {
30326
+ return (ms / 1e3).toFixed(2) + "s";
30327
+ }
30328
+ function formatExtractionStats(stats) {
30329
+ const lines = [`Components: ${stats.componentCount}`];
30330
+ if (stats.linkCount !== void 0) {
30331
+ lines.push(
30332
+ `Links: ${stats.linkCount} (sync: ${stats.syncLinkCount}, async: ${stats.asyncLinkCount})`
30333
+ );
30334
+ lines.push(`Uncertain: ${stats.uncertainLinkCount}`);
30335
+ }
30336
+ return lines;
30337
+ }
30338
+ function formatTimingLine(timings) {
30339
+ return `Extraction completed in ${formatSeconds(timings.totalMs)} (call graph: ${formatSeconds(timings.callGraphMs)}, detection: ${formatSeconds(timings.asyncDetectionMs)}, setup: ${formatSeconds(timings.setupMs)})`;
30340
+ }
30341
+
30186
30342
  // src/platform/infra/cli-presentation/categorize-components.ts
30187
30343
  function componentKey(component) {
30188
30344
  return `${component.domain}:${component.type}:${component.name}`;
@@ -30213,61 +30369,50 @@ function categorizeComponents(current, baseline) {
30213
30369
  };
30214
30370
  }
30215
30371
 
30216
- // src/features/extract/commands/run-extraction.ts
30217
- function runExtraction(options, resolvedConfig, configDir, sourceFilePaths) {
30218
- const project = loadExtractionProject(configDir, sourceFilePaths, options.tsConfig === false);
30219
- const draftComponents = loadOrExtractComponents(
30220
- project,
30221
- sourceFilePaths,
30222
- resolvedConfig,
30223
- configDir,
30224
- options.enrich
30225
- );
30372
+ // src/features/extract/infra/mappers/present-extraction-result.ts
30373
+ function presentExtractionResult(result, options) {
30374
+ if (result.kind === "draftOnly") {
30375
+ presentDraftResult(result.components, options);
30376
+ return;
30377
+ }
30378
+ presentFullResult(result, options);
30379
+ }
30380
+ function presentDraftResult(components, options) {
30226
30381
  if (options.dryRun) {
30227
- for (const line of formatDryRunOutput(draftComponents)) {
30382
+ for (const line of formatDryRunOutput(components)) {
30228
30383
  console.log(line);
30229
30384
  }
30230
30385
  return;
30231
30386
  }
30232
30387
  if (options.format === "markdown") {
30233
- const categorized = categorizeComponents(draftComponents, void 0);
30388
+ const categorized = categorizeComponents(components, void 0);
30234
30389
  const markdown = formatPrMarkdown(categorized);
30235
30390
  console.log(markdown);
30236
30391
  return;
30237
30392
  }
30238
- if (options.componentsOnly) {
30239
- outputResult(formatSuccess(draftComponents), options);
30240
- return;
30393
+ outputResult(formatSuccess(components), { output: options.output });
30394
+ }
30395
+ function presentFullResult(result, options) {
30396
+ if (result.failedFields.length > 0) {
30397
+ console.error(
30398
+ `Warning: Enrichment failed for ${result.failedFields.length} field(s): ${result.failedFields.join(", ")}`
30399
+ );
30241
30400
  }
30242
- const enrichmentResult = enrichComponentsSafe(
30243
- draftComponents,
30244
- resolvedConfig,
30245
- project,
30246
- configDir,
30247
- options.allowIncomplete === true
30248
- );
30249
- const repositoryInfo = getRepositoryInfo();
30250
- const { links } = detectConnectionsSafe(
30251
- project,
30252
- enrichmentResult.components,
30253
- resolvedConfig.modules.map((m) => m.path),
30254
- repositoryInfo.name,
30255
- options.allowIncomplete === true,
30256
- options.stats === true
30257
- );
30258
30401
  if (options.stats === true) {
30259
- const stats = countLinksByType(enrichmentResult.components.length, links);
30402
+ for (const timing of result.timings) {
30403
+ console.error(formatTimingLine(timing));
30404
+ }
30405
+ const stats = countLinksByType(result.components.length, result.links);
30260
30406
  for (const line of formatExtractionStats(stats)) {
30261
30407
  console.error(line);
30262
30408
  }
30263
30409
  }
30264
- const outputOptions = options.output === void 0 ? {} : { output: options.output };
30265
30410
  outputResult(
30266
30411
  formatSuccess({
30267
- components: enrichmentResult.components,
30268
- links
30412
+ components: result.components,
30413
+ links: result.links
30269
30414
  }),
30270
- outputOptions
30415
+ { output: options.output }
30271
30416
  );
30272
30417
  }
30273
30418
 
@@ -30281,7 +30426,8 @@ function createExtractCommand() {
30281
30426
  } = loadAndValidateConfig(options.config);
30282
30427
  const allSourceFilePaths = resolveSourceFiles(resolvedConfig, configDir);
30283
30428
  const sourceFilePaths = resolveFilteredSourceFiles(allSourceFilePaths, options);
30284
- runExtraction(options, resolvedConfig, configDir, sourceFilePaths);
30429
+ const result = runExtraction(options, resolvedConfig, configDir, sourceFilePaths);
30430
+ presentExtractionResult(result, options);
30285
30431
  });
30286
30432
  }
30287
30433
 
@@ -30297,7 +30443,7 @@ function parsePackageJson(pkg) {
30297
30443
  }
30298
30444
  function loadPackageJson() {
30299
30445
  if (true) {
30300
- return { version: "0.8.9" };
30446
+ return { version: "0.8.11" };
30301
30447
  }
30302
30448
  const require2 = createRequire2(import.meta.url);
30303
30449
  return parsePackageJson(require2("../../package.json"));
@@ -30395,8 +30541,8 @@ program.parseAsync().catch(handleGlobalError);
30395
30541
  /* v8 ignore next -- @preserve: error is always Error from yaml parser; defensive guard */
30396
30542
  /* v8 ignore start -- @preserve: default executor delegates to execFileSync; tested via CLI integration */
30397
30543
  /* v8 ignore start -- @preserve: detectChangedTypeScriptFiles only throws GitError; non-GitError path is unreachable */
30398
- /* v8 ignore start -- @preserve: trivial comparator, Map keys guarantee a !== b */
30399
- /* v8 ignore start -- @preserve: dry-run output formatting; tested via CLI integration */
30400
30544
  /* v8 ignore start -- @preserve: git execution; mocked in all integration tests */
30401
30545
  /* v8 ignore start -- @preserve: defensive check; regex ([^/]+) requires non-empty groups */
30546
+ /* v8 ignore start -- @preserve: trivial comparator, Map keys guarantee a !== b */
30547
+ /* v8 ignore start -- @preserve: dry-run output formatting; tested via CLI integration */
30402
30548
  /* v8 ignore start -- @preserve: dry-run tested via CLI integration */