@c4a/extract 0.6.0-beta.4 → 0.6.0-beta.5

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.
@@ -14298,9 +14298,77 @@ var detectModuleAt = async (repoPath, modulePath, ref, pathFilter2) => {
14298
14298
  return buildModule(repoRoot, moduleDir, name, nestedSet, ref, pathFilter2, gitFiles);
14299
14299
  };
14300
14300
 
14301
+ // src/errors.ts
14302
+ var NO_ENTRY_DETECTED = "NO_ENTRY_DETECTED";
14303
+
14304
+ class ExtractionInputError extends Error {
14305
+ code;
14306
+ detail;
14307
+ constructor(code, message, detail) {
14308
+ super(message);
14309
+ this.name = "ExtractionInputError";
14310
+ this.code = code;
14311
+ this.detail = detail;
14312
+ }
14313
+ }
14314
+
14301
14315
  // src/repository.ts
14302
14316
  var PACKAGE_JSON = "package.json";
14303
14317
  var toPosixPath2 = (value) => value.split(path2.sep).join("/");
14318
+ function safeSourceRelativePath(value) {
14319
+ const slashPath = value.trim().replace(/\\/gu, "/");
14320
+ const normalized = path2.posix.normalize(slashPath).replace(/^\.\//u, "");
14321
+ if (normalized.length === 0 || normalized === "." || normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/") || /^[A-Za-z]:\//u.test(normalized)) {
14322
+ throw new Error(`Extraction entry must be a source-relative file path: ${value}`);
14323
+ }
14324
+ return normalized;
14325
+ }
14326
+ function moduleRelativeEntryPath(modulePath, sourcePath) {
14327
+ if (modulePath === ".")
14328
+ return sourcePath;
14329
+ const prefix = `${normalizeRelativePath(modulePath)}/`;
14330
+ if (!sourcePath.startsWith(prefix)) {
14331
+ throw new Error(`Extraction entry is outside the selected module ${modulePath}: ${sourcePath}`);
14332
+ }
14333
+ return sourcePath.slice(prefix.length);
14334
+ }
14335
+ function entrySubpath(filePath, index) {
14336
+ if (index === 0)
14337
+ return ".";
14338
+ const withoutExtension = filePath.replace(/\.(?:d\.)?(?:ts|tsx|mts|cts)$/u, "");
14339
+ return `./${withoutExtension}`;
14340
+ }
14341
+ function selectedEntryFiles(input) {
14342
+ const selection = input.selection ?? { mode: "auto" };
14343
+ const includedSourceFiles = new Set(input.module.files.map(safeSourceRelativePath));
14344
+ if (selection.mode === "auto") {
14345
+ if (input.detected.entries.length === 0) {
14346
+ throw new ExtractionInputError(NO_ENTRY_DETECTED, 'No TypeScript entry files were detected from package.json. Configure extractTs entries in the Context project, or use mode: "scan"; do not modify the source repository solely for Context.', { mode: "auto", module: input.module.path });
14347
+ }
14348
+ for (const entry of input.detected.entries) {
14349
+ const sourcePath = input.module.path === "." ? safeSourceRelativePath(entry.path) : safeSourceRelativePath(`${input.module.path}/${entry.path}`);
14350
+ if (!includedSourceFiles.has(sourcePath)) {
14351
+ throw new Error(`Auto-detected entry is outside extractTs include: ${sourcePath}. Update include or configure entries in the Context project.`);
14352
+ }
14353
+ }
14354
+ return input.detected.entries;
14355
+ }
14356
+ const sourceEntries = selection.mode === "scan" ? [...includedSourceFiles].sort() : [...new Set(selection.entries.map(safeSourceRelativePath))];
14357
+ if (sourceEntries.length === 0) {
14358
+ throw new ExtractionInputError(NO_ENTRY_DETECTED, selection.mode === "scan" ? "No TypeScript files match extractTs include for scan mode." : "extractTs entries must contain at least one source-relative file path.", { mode: selection.mode, module: input.module.path });
14359
+ }
14360
+ return sourceEntries.map((sourcePath, index) => {
14361
+ if (!includedSourceFiles.has(sourcePath)) {
14362
+ throw new Error(`Configured extraction entry is missing or outside extractTs include: ${sourcePath}. Update entries or include in the Context project.`);
14363
+ }
14364
+ const modulePath = moduleRelativeEntryPath(input.module.path, sourcePath);
14365
+ return {
14366
+ path: modulePath,
14367
+ subpath: entrySubpath(modulePath, index),
14368
+ type: "library"
14369
+ };
14370
+ });
14371
+ }
14304
14372
  var normalizeRelativePath = (value) => {
14305
14373
  const normalized = toPosixPath2(value).replace(/^\.\/+/, "").replace(/\/+/g, "/");
14306
14374
  return normalized || ".";
@@ -14468,7 +14536,15 @@ var runRepositoryExtraction = async (input) => {
14468
14536
  module_name: module.name,
14469
14537
  module_path: module.path
14470
14538
  });
14471
- const entryDetection = await plugin.detectEntries(manifest, fs);
14539
+ const detectedEntries = await plugin.detectEntries(manifest, fs);
14540
+ const entryDetection = {
14541
+ ...detectedEntries,
14542
+ entries: selectedEntryFiles({
14543
+ module,
14544
+ detected: detectedEntries,
14545
+ ...input.entrySelection !== undefined ? { selection: input.entrySelection } : {}
14546
+ })
14547
+ };
14472
14548
  input.onProgress?.({ phase: "parsing", progress: 20, module_name: module.name, module_path: module.path });
14473
14549
  const moduleDoc = await extractModuleDoc(entryDetection, fs);
14474
14550
  const rawExtraction = await plugin.extractSymbols(entryDetection.entries, fs);
@@ -14491,6 +14567,11 @@ var pluginSpecSchema = exports_external.object({
14491
14567
  package: exports_external.string().min(1),
14492
14568
  exportName: exports_external.string().min(1).optional()
14493
14569
  });
14570
+ var entrySelectionSchema = exports_external.discriminatedUnion("mode", [
14571
+ exports_external.object({ mode: exports_external.literal("auto") }),
14572
+ exports_external.object({ mode: exports_external.literal("configured"), entries: exports_external.array(exports_external.string().min(1)) }),
14573
+ exports_external.object({ mode: exports_external.literal("scan") })
14574
+ ]);
14494
14575
  var codeExtractRunnerInputSchema = exports_external.object({
14495
14576
  repoPath: exports_external.string().min(1),
14496
14577
  modules: exports_external.array(exports_external.string().min(1)).optional(),
@@ -14498,6 +14579,7 @@ var codeExtractRunnerInputSchema = exports_external.object({
14498
14579
  commitHash: exports_external.string().min(1).nullable().optional(),
14499
14580
  moduleCommits: exports_external.record(exports_external.string().nullable()).optional(),
14500
14581
  pathFilter: PathFilterConfigSchema.optional(),
14582
+ entrySelection: entrySelectionSchema.optional(),
14501
14583
  plugins: exports_external.array(pluginSpecSchema).min(1),
14502
14584
  snapshot: exports_external.object({
14503
14585
  sourceId: exports_external.string().min(1),
@@ -14552,6 +14634,9 @@ var loadRunnerPlugins = async (pluginSpecs, cwd = process.cwd()) => {
14552
14634
  };
14553
14635
  var runCodeExtractRunner = async (rawInput, cwd = process.cwd()) => {
14554
14636
  const input = codeExtractRunnerInputSchema.parse(rawInput);
14637
+ if (input.entrySelection?.mode === "configured" && input.entrySelection.entries.length === 0) {
14638
+ throw new ExtractionInputError(NO_ENTRY_DETECTED, "Configured extraction entries must contain at least one source-relative file path.", { mode: "configured" });
14639
+ }
14555
14640
  const events = [];
14556
14641
  const plugins = await loadRunnerPlugins(input.plugins, cwd);
14557
14642
  const extraction = await runRepositoryExtraction({
@@ -14561,6 +14646,7 @@ var runCodeExtractRunner = async (rawInput, cwd = process.cwd()) => {
14561
14646
  ...input.commitHash !== undefined ? { commitHash: input.commitHash } : {},
14562
14647
  ...input.moduleCommits ? { moduleCommits: input.moduleCommits } : {},
14563
14648
  ...input.pathFilter ? { pathFilter: input.pathFilter } : {},
14649
+ ...input.entrySelection ? { entrySelection: input.entrySelection } : {},
14564
14650
  plugins,
14565
14651
  onProgress: (event) => events.push({ type: "progress", ...event })
14566
14652
  });
package/index.js CHANGED
@@ -16405,9 +16405,77 @@ var detectModuleAt = async (repoPath, modulePath, ref, pathFilter2) => {
16405
16405
  return buildModule(repoRoot, moduleDir, name, nestedSet, ref, pathFilter2, gitFiles);
16406
16406
  };
16407
16407
 
16408
+ // src/errors.ts
16409
+ var NO_ENTRY_DETECTED = "NO_ENTRY_DETECTED";
16410
+
16411
+ class ExtractionInputError extends Error {
16412
+ code;
16413
+ detail;
16414
+ constructor(code, message, detail) {
16415
+ super(message);
16416
+ this.name = "ExtractionInputError";
16417
+ this.code = code;
16418
+ this.detail = detail;
16419
+ }
16420
+ }
16421
+
16408
16422
  // src/repository.ts
16409
16423
  var PACKAGE_JSON = "package.json";
16410
16424
  var toPosixPath2 = (value) => value.split(path2.sep).join("/");
16425
+ function safeSourceRelativePath(value) {
16426
+ const slashPath = value.trim().replace(/\\/gu, "/");
16427
+ const normalized = path2.posix.normalize(slashPath).replace(/^\.\//u, "");
16428
+ if (normalized.length === 0 || normalized === "." || normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/") || /^[A-Za-z]:\//u.test(normalized)) {
16429
+ throw new Error(`Extraction entry must be a source-relative file path: ${value}`);
16430
+ }
16431
+ return normalized;
16432
+ }
16433
+ function moduleRelativeEntryPath(modulePath, sourcePath) {
16434
+ if (modulePath === ".")
16435
+ return sourcePath;
16436
+ const prefix = `${normalizeRelativePath(modulePath)}/`;
16437
+ if (!sourcePath.startsWith(prefix)) {
16438
+ throw new Error(`Extraction entry is outside the selected module ${modulePath}: ${sourcePath}`);
16439
+ }
16440
+ return sourcePath.slice(prefix.length);
16441
+ }
16442
+ function entrySubpath(filePath, index) {
16443
+ if (index === 0)
16444
+ return ".";
16445
+ const withoutExtension = filePath.replace(/\.(?:d\.)?(?:ts|tsx|mts|cts)$/u, "");
16446
+ return `./${withoutExtension}`;
16447
+ }
16448
+ function selectedEntryFiles(input) {
16449
+ const selection = input.selection ?? { mode: "auto" };
16450
+ const includedSourceFiles = new Set(input.module.files.map(safeSourceRelativePath));
16451
+ if (selection.mode === "auto") {
16452
+ if (input.detected.entries.length === 0) {
16453
+ throw new ExtractionInputError(NO_ENTRY_DETECTED, 'No TypeScript entry files were detected from package.json. Configure extractTs entries in the Context project, or use mode: "scan"; do not modify the source repository solely for Context.', { mode: "auto", module: input.module.path });
16454
+ }
16455
+ for (const entry of input.detected.entries) {
16456
+ const sourcePath = input.module.path === "." ? safeSourceRelativePath(entry.path) : safeSourceRelativePath(`${input.module.path}/${entry.path}`);
16457
+ if (!includedSourceFiles.has(sourcePath)) {
16458
+ throw new Error(`Auto-detected entry is outside extractTs include: ${sourcePath}. Update include or configure entries in the Context project.`);
16459
+ }
16460
+ }
16461
+ return input.detected.entries;
16462
+ }
16463
+ const sourceEntries = selection.mode === "scan" ? [...includedSourceFiles].sort() : [...new Set(selection.entries.map(safeSourceRelativePath))];
16464
+ if (sourceEntries.length === 0) {
16465
+ throw new ExtractionInputError(NO_ENTRY_DETECTED, selection.mode === "scan" ? "No TypeScript files match extractTs include for scan mode." : "extractTs entries must contain at least one source-relative file path.", { mode: selection.mode, module: input.module.path });
16466
+ }
16467
+ return sourceEntries.map((sourcePath, index) => {
16468
+ if (!includedSourceFiles.has(sourcePath)) {
16469
+ throw new Error(`Configured extraction entry is missing or outside extractTs include: ${sourcePath}. Update entries or include in the Context project.`);
16470
+ }
16471
+ const modulePath = moduleRelativeEntryPath(input.module.path, sourcePath);
16472
+ return {
16473
+ path: modulePath,
16474
+ subpath: entrySubpath(modulePath, index),
16475
+ type: "library"
16476
+ };
16477
+ });
16478
+ }
16411
16479
  var normalizeRelativePath = (value) => {
16412
16480
  const normalized = toPosixPath2(value).replace(/^\.\/+/, "").replace(/\/+/g, "/");
16413
16481
  return normalized || ".";
@@ -16575,7 +16643,15 @@ var runRepositoryExtraction = async (input) => {
16575
16643
  module_name: module2.name,
16576
16644
  module_path: module2.path
16577
16645
  });
16578
- const entryDetection = await plugin.detectEntries(manifest, fs2);
16646
+ const detectedEntries = await plugin.detectEntries(manifest, fs2);
16647
+ const entryDetection = {
16648
+ ...detectedEntries,
16649
+ entries: selectedEntryFiles({
16650
+ module: module2,
16651
+ detected: detectedEntries,
16652
+ ...input.entrySelection !== undefined ? { selection: input.entrySelection } : {}
16653
+ })
16654
+ };
16579
16655
  input.onProgress?.({ phase: "parsing", progress: 20, module_name: module2.name, module_path: module2.path });
16580
16656
  const moduleDoc = await extractModuleDoc(entryDetection, fs2);
16581
16657
  const rawExtraction = await plugin.extractSymbols(entryDetection.entries, fs2);
@@ -16920,6 +16996,11 @@ var pluginSpecSchema = exports_external.object({
16920
16996
  package: exports_external.string().min(1),
16921
16997
  exportName: exports_external.string().min(1).optional()
16922
16998
  });
16999
+ var entrySelectionSchema = exports_external.discriminatedUnion("mode", [
17000
+ exports_external.object({ mode: exports_external.literal("auto") }),
17001
+ exports_external.object({ mode: exports_external.literal("configured"), entries: exports_external.array(exports_external.string().min(1)) }),
17002
+ exports_external.object({ mode: exports_external.literal("scan") })
17003
+ ]);
16923
17004
  var codeExtractRunnerInputSchema = exports_external.object({
16924
17005
  repoPath: exports_external.string().min(1),
16925
17006
  modules: exports_external.array(exports_external.string().min(1)).optional(),
@@ -16927,6 +17008,7 @@ var codeExtractRunnerInputSchema = exports_external.object({
16927
17008
  commitHash: exports_external.string().min(1).nullable().optional(),
16928
17009
  moduleCommits: exports_external.record(exports_external.string().nullable()).optional(),
16929
17010
  pathFilter: PathFilterConfigSchema.optional(),
17011
+ entrySelection: entrySelectionSchema.optional(),
16930
17012
  plugins: exports_external.array(pluginSpecSchema).min(1),
16931
17013
  snapshot: exports_external.object({
16932
17014
  sourceId: exports_external.string().min(1),
@@ -16981,6 +17063,9 @@ var loadRunnerPlugins = async (pluginSpecs, cwd = process.cwd()) => {
16981
17063
  };
16982
17064
  var runCodeExtractRunner = async (rawInput, cwd = process.cwd()) => {
16983
17065
  const input = codeExtractRunnerInputSchema.parse(rawInput);
17066
+ if (input.entrySelection?.mode === "configured" && input.entrySelection.entries.length === 0) {
17067
+ throw new ExtractionInputError(NO_ENTRY_DETECTED, "Configured extraction entries must contain at least one source-relative file path.", { mode: "configured" });
17068
+ }
16984
17069
  const events = [];
16985
17070
  const plugins = await loadRunnerPlugins(input.plugins, cwd);
16986
17071
  const extraction = await runRepositoryExtraction({
@@ -16990,6 +17075,7 @@ var runCodeExtractRunner = async (rawInput, cwd = process.cwd()) => {
16990
17075
  ...input.commitHash !== undefined ? { commitHash: input.commitHash } : {},
16991
17076
  ...input.moduleCommits ? { moduleCommits: input.moduleCommits } : {},
16992
17077
  ...input.pathFilter ? { pathFilter: input.pathFilter } : {},
17078
+ ...input.entrySelection ? { entrySelection: input.entrySelection } : {},
16993
17079
  plugins,
16994
17080
  onProgress: (event) => events.push({ type: "progress", ...event })
16995
17081
  });
@@ -17237,7 +17323,9 @@ export {
17237
17323
  buildCodeSnapshot,
17238
17324
  SCAN_EXCLUDED_DIRS,
17239
17325
  PACKAGE_JSON,
17326
+ NO_ENTRY_DETECTED,
17240
17327
  ExtractionPluginRegistry,
17328
+ ExtractionInputError,
17241
17329
  DOCUMENT_SNAPSHOT_MANIFEST_SCHEMA_VERSION,
17242
17330
  DOCUMENT_EVIDENCE_NORMALIZER_VERSION,
17243
17331
  DEFAULT_SOURCE_SPAN_HASH_LENGTH,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@c4a/extract",
3
- "version": "0.6.0-beta.4",
3
+ "version": "0.6.0-beta.5",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "c4a-extract-code": "./bin/c4a-extract-code.js"