@opentrace/components 0.1.1-rc.112 → 0.1.1-rc.120

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.
package/dist/pipeline.cjs CHANGED
@@ -3315,14 +3315,14 @@ class TemplateSummarizer {
3315
3315
  async dispose() {
3316
3316
  }
3317
3317
  }
3318
- const TYPE_TO_KIND = {
3318
+ const TYPE_TO_KIND$1 = {
3319
3319
  Function: "function",
3320
3320
  Class: "class",
3321
3321
  File: "file",
3322
3322
  Directory: "directory"
3323
3323
  };
3324
3324
  function summarizeNode(node) {
3325
- const kind = TYPE_TO_KIND[node.type];
3325
+ const kind = TYPE_TO_KIND$1[node.type];
3326
3326
  if (!kind) {
3327
3327
  return `${node.type} ${node.name}`;
3328
3328
  }
@@ -3431,8 +3431,537 @@ const DEFAULT_SUMMARIZER_CONFIG = {
3431
3431
  maxInputLength: 480,
3432
3432
  minLines: 5
3433
3433
  };
3434
+ function pushAll(dst, src) {
3435
+ for (let j = 0; j < src.length; j++) {
3436
+ dst.push(src[j]);
3437
+ }
3438
+ }
3439
+ function* runNodePipeline(opts) {
3440
+ const { ctx, stages, seeds } = opts;
3441
+ const stageCount = stages.length;
3442
+ const queues = Array.from({ length: stageCount }, () => []);
3443
+ pushAll(queues[0], seeds);
3444
+ let totalNodes = seeds.length;
3445
+ let totalRelationships = 0;
3446
+ const hasWork = () => queues.some((q) => q.length > 0);
3447
+ while (hasWork()) {
3448
+ if (ctx.cancelled) {
3449
+ yield { kind: "pipeline_error", error: "cancelled" };
3450
+ return;
3451
+ }
3452
+ let processed = false;
3453
+ for (let i = stageCount - 1; i >= 0; i--) {
3454
+ if (queues[i].length === 0) continue;
3455
+ const node = queues[i].shift();
3456
+ const stage = stages[i];
3457
+ yield { stage: stage.name(), node: node.id, action: "start" };
3458
+ let mutation;
3459
+ try {
3460
+ mutation = stage.process(node);
3461
+ } catch (err) {
3462
+ yield {
3463
+ kind: "item_error",
3464
+ stage: stage.name(),
3465
+ node: node.id,
3466
+ error: err instanceof Error ? err.message : String(err)
3467
+ };
3468
+ processed = true;
3469
+ break;
3470
+ }
3471
+ yield { stage: stage.name(), node: node.id, action: "end", mutation };
3472
+ totalRelationships += mutation.relationships.length;
3473
+ if (mutation.nodes.length > 0) {
3474
+ totalNodes += mutation.nodes.length;
3475
+ if (i < stageCount - 1) {
3476
+ pushAll(queues[i + 1], mutation.nodes);
3477
+ }
3478
+ }
3479
+ processed = true;
3480
+ break;
3481
+ }
3482
+ if (!processed) break;
3483
+ }
3484
+ for (const stage of stages) {
3485
+ if (ctx.cancelled) {
3486
+ yield { kind: "pipeline_error", error: "cancelled" };
3487
+ return;
3488
+ }
3489
+ yield { kind: "flush_start", stage: stage.name() };
3490
+ let mutation;
3491
+ try {
3492
+ mutation = stage.flush();
3493
+ } catch (err) {
3494
+ yield {
3495
+ kind: "pipeline_error",
3496
+ error: `flush error in ${stage.name()}: ${err instanceof Error ? err.message : String(err)}`
3497
+ };
3498
+ return;
3499
+ }
3500
+ totalNodes += mutation.nodes.length;
3501
+ totalRelationships += mutation.relationships.length;
3502
+ yield {
3503
+ kind: "flush_end",
3504
+ stage: stage.name(),
3505
+ mutation: mutation.nodes.length > 0 || mutation.relationships.length > 0 ? mutation : void 0
3506
+ };
3507
+ }
3508
+ yield {
3509
+ kind: "pipeline_done",
3510
+ totalNodes,
3511
+ totalRelationships
3512
+ };
3513
+ }
3514
+ const EMPTY_MUTATION = Object.freeze({
3515
+ nodes: [],
3516
+ relationships: []
3517
+ });
3518
+ const DEFAULT_CACHE_LIMIT = 500 * 1024 * 1024;
3519
+ class FileCacheStage {
3520
+ cache = /* @__PURE__ */ new Map();
3521
+ bytesUsed = 0;
3522
+ byteLimit;
3523
+ full = false;
3524
+ cachedCount = 0;
3525
+ skippedCount = 0;
3526
+ constructor(config) {
3527
+ this.byteLimit = config.byteLimit ?? DEFAULT_CACHE_LIMIT;
3528
+ for (const [fileId, content] of config.fileContentMap) {
3529
+ const byteSize = content.length * 2;
3530
+ if (this.bytesUsed + byteSize <= this.byteLimit) {
3531
+ this.cache.set(fileId, content);
3532
+ this.bytesUsed += byteSize;
3533
+ this.cachedCount++;
3534
+ } else {
3535
+ this.full = true;
3536
+ this.skippedCount++;
3537
+ }
3538
+ }
3539
+ }
3540
+ name() {
3541
+ return "cache";
3542
+ }
3543
+ process(node) {
3544
+ return { nodes: [node], relationships: [] };
3545
+ }
3546
+ flush() {
3547
+ return { nodes: [], relationships: [] };
3548
+ }
3549
+ /** Read cached content for a file. Returns undefined if not cached. */
3550
+ getContent(fileId) {
3551
+ return this.cache.get(fileId);
3552
+ }
3553
+ /**
3554
+ * Remove a file from the raw cache (e.g. after extraction is done).
3555
+ * Frees the JS string so GC can reclaim the memory.
3556
+ */
3557
+ evict(fileId) {
3558
+ const content = this.cache.get(fileId);
3559
+ if (content) {
3560
+ this.bytesUsed -= content.length * 2;
3561
+ this.cache.delete(fileId);
3562
+ }
3563
+ }
3564
+ /** Current bytes used by the cache. */
3565
+ getBytesUsed() {
3566
+ return this.bytesUsed;
3567
+ }
3568
+ /** Whether the cache limit has been reached. */
3569
+ isFull() {
3570
+ return this.full;
3571
+ }
3572
+ /** Number of files cached vs skipped. */
3573
+ stats() {
3574
+ return {
3575
+ cached: this.cachedCount,
3576
+ skipped: this.skippedCount,
3577
+ bytesUsed: this.bytesUsed,
3578
+ byteLimit: this.byteLimit
3579
+ };
3580
+ }
3581
+ }
3582
+ class ExtractStage {
3583
+ registries = {
3584
+ nameRegistry: /* @__PURE__ */ new Map(),
3585
+ fileRegistry: /* @__PURE__ */ new Map(),
3586
+ classRegistry: /* @__PURE__ */ new Map(),
3587
+ importRegistry: /* @__PURE__ */ new Map()
3588
+ };
3589
+ allCallInfo = [];
3590
+ knownPaths;
3591
+ pathToFileId;
3592
+ goModulePath;
3593
+ getContent;
3594
+ packageNodes;
3595
+ emittedNodeIds = /* @__PURE__ */ new Set();
3596
+ pendingPackageNodes = [];
3597
+ constructor(config) {
3598
+ const { scanResult, getContent } = config;
3599
+ this.knownPaths = scanResult.knownPaths;
3600
+ this.pathToFileId = scanResult.pathToFileId;
3601
+ this.goModulePath = scanResult.goModulePath;
3602
+ this.getContent = getContent;
3603
+ this.packageNodes = new Map(scanResult.packageNodes);
3604
+ }
3605
+ name() {
3606
+ return "extract";
3607
+ }
3608
+ process(node) {
3609
+ if (node.type !== "File") {
3610
+ return { nodes: [node], relationships: [] };
3611
+ }
3612
+ const filePath = node.properties?.path;
3613
+ if (!filePath) {
3614
+ return { nodes: [node], relationships: [] };
3615
+ }
3616
+ const ext = getExtension(filePath);
3617
+ const language = detectLanguage(ext);
3618
+ if (!language) {
3619
+ return { nodes: [node], relationships: [] };
3620
+ }
3621
+ const parser = getParserForLanguage(language, ext);
3622
+ const extractor = getExtractor(language);
3623
+ if (!parser || !extractor) {
3624
+ return { nodes: [node], relationships: [] };
3625
+ }
3626
+ const fileId = node.id;
3627
+ const content = this.getContent(fileId);
3628
+ if (content === void 0) {
3629
+ return { nodes: [node], relationships: [] };
3630
+ }
3631
+ const nodes = [node];
3632
+ const rels = [];
3633
+ try {
3634
+ const tree = parser.parse(content);
3635
+ if (!tree) {
3636
+ return { nodes: [node], relationships: [] };
3637
+ }
3638
+ const extraction = extractor(tree.rootNode);
3639
+ this.registries.fileRegistry.set(fileId, /* @__PURE__ */ new Map());
3640
+ for (const sym of extraction.symbols) {
3641
+ processSymbol(
3642
+ sym,
3643
+ fileId,
3644
+ language,
3645
+ this.registries,
3646
+ this.allCallInfo,
3647
+ nodes,
3648
+ rels,
3649
+ this.emittedNodeIds
3650
+ );
3651
+ }
3652
+ const rootNode = extraction.rootNode;
3653
+ if (rootNode) {
3654
+ const importResult = analyzeImports(
3655
+ rootNode,
3656
+ language,
3657
+ filePath,
3658
+ this.knownPaths,
3659
+ this.goModulePath
3660
+ );
3661
+ const fileImports = {};
3662
+ const seenTargetFiles = /* @__PURE__ */ new Set();
3663
+ for (const [alias, targetPath] of Object.entries(
3664
+ importResult.internal
3665
+ )) {
3666
+ const targetFileId = this.pathToFileId.get(targetPath);
3667
+ if (targetFileId) {
3668
+ fileImports[alias] = targetFileId;
3669
+ if (!seenTargetFiles.has(targetFileId)) {
3670
+ seenTargetFiles.add(targetFileId);
3671
+ rels.push({
3672
+ id: `${fileId}->IMPORTS->${targetFileId}`,
3673
+ type: "IMPORTS",
3674
+ source_id: fileId,
3675
+ target_id: targetFileId
3676
+ });
3677
+ }
3678
+ }
3679
+ }
3680
+ this.registries.importRegistry.set(fileId, fileImports);
3681
+ for (const [pkgName, pkgId] of Object.entries(
3682
+ importResult.external
3683
+ )) {
3684
+ if (!this.packageNodes.has(pkgId)) {
3685
+ const pkgNode = {
3686
+ id: pkgId,
3687
+ type: "Package",
3688
+ name: pkgName,
3689
+ properties: { registry: pkgId.split(":")[1] }
3690
+ };
3691
+ this.packageNodes.set(pkgId, pkgNode);
3692
+ this.pendingPackageNodes.push(pkgNode);
3693
+ }
3694
+ rels.push({
3695
+ id: `${fileId}->IMPORTS->${pkgId}`,
3696
+ type: "IMPORTS",
3697
+ source_id: fileId,
3698
+ target_id: pkgId
3699
+ });
3700
+ }
3701
+ }
3702
+ } catch {
3703
+ return { nodes: [node], relationships: [] };
3704
+ }
3705
+ return { nodes, relationships: rels };
3706
+ }
3707
+ flush() {
3708
+ const nodes = this.pendingPackageNodes.splice(0);
3709
+ return { nodes, relationships: [] };
3710
+ }
3711
+ }
3712
+ class ResolveStage {
3713
+ extractStage;
3714
+ constructor(extractStage) {
3715
+ this.extractStage = extractStage;
3716
+ }
3717
+ name() {
3718
+ return "resolve";
3719
+ }
3720
+ process(node) {
3721
+ return { nodes: [node], relationships: [] };
3722
+ }
3723
+ flush() {
3724
+ const { registries, allCallInfo } = this.extractStage;
3725
+ const resolvedCalls = resolveCalls(allCallInfo, registries);
3726
+ const callRels = resolvedCallsToRelationships(resolvedCalls);
3727
+ return { nodes: [], relationships: callRels };
3728
+ }
3729
+ }
3730
+ const TYPE_TO_KIND = {
3731
+ Function: "function",
3732
+ Class: "class",
3733
+ File: "file",
3734
+ Directory: "directory"
3735
+ };
3736
+ class SummarizeStage {
3737
+ name() {
3738
+ return "summarize";
3739
+ }
3740
+ process(node) {
3741
+ if (!node.properties?.summary) {
3742
+ const summary = this.summarizeNode(node);
3743
+ if (summary) {
3744
+ node.properties = { ...node.properties, summary };
3745
+ }
3746
+ }
3747
+ return { nodes: [node], relationships: [] };
3748
+ }
3749
+ flush() {
3750
+ return { nodes: [], relationships: [] };
3751
+ }
3752
+ summarizeNode(node) {
3753
+ const kind = TYPE_TO_KIND[node.type];
3754
+ if (!kind) {
3755
+ return `${node.type} ${node.name}`;
3756
+ }
3757
+ const props = node.properties ?? {};
3758
+ return summarizeFromMetadata({
3759
+ name: node.name,
3760
+ kind,
3761
+ signature: props.signature,
3762
+ language: props.language,
3763
+ lineCount: typeof props.start_line === "number" && typeof props.end_line === "number" ? props.end_line - props.start_line + 1 : void 0,
3764
+ receiverType: props.receiver_type,
3765
+ fileName: kind === "file" ? props.path ?? node.name : void 0,
3766
+ childNames: props.childNames,
3767
+ docs: props.docs
3768
+ });
3769
+ }
3770
+ }
3771
+ const DEFAULT_DRAIN_THRESHOLD = 500;
3772
+ class StoreStage {
3773
+ bufferedNodes = [];
3774
+ bufferedRelationships = [];
3775
+ totalNodes = 0;
3776
+ totalRelationships = 0;
3777
+ drainThreshold;
3778
+ constructor(drainThreshold = DEFAULT_DRAIN_THRESHOLD) {
3779
+ this.drainThreshold = drainThreshold;
3780
+ }
3781
+ name() {
3782
+ return "store";
3783
+ }
3784
+ process(node) {
3785
+ this.bufferedNodes.push(node);
3786
+ this.totalNodes++;
3787
+ return { nodes: [], relationships: [] };
3788
+ }
3789
+ /**
3790
+ * Feed relationships from upstream stage mutations.
3791
+ * Call this from the event loop when processing StageEvent 'end' mutations.
3792
+ */
3793
+ addRelationships(rels) {
3794
+ for (let i = 0; i < rels.length; i++) {
3795
+ this.bufferedRelationships.push(rels[i]);
3796
+ this.totalRelationships++;
3797
+ }
3798
+ }
3799
+ /** True when the node buffer has reached the drain threshold. */
3800
+ needsDrain() {
3801
+ return this.bufferedNodes.length >= this.drainThreshold;
3802
+ }
3803
+ /**
3804
+ * Return and clear buffered nodes. The caller should persist these
3805
+ * to the store (importBatch + flush). Called periodically from the
3806
+ * event loop, not just at the end.
3807
+ */
3808
+ drainNodes() {
3809
+ const nodes = this.bufferedNodes;
3810
+ this.bufferedNodes = [];
3811
+ return nodes;
3812
+ }
3813
+ /**
3814
+ * Return and clear buffered relationships. Called once at the end
3815
+ * after all nodes have been persisted.
3816
+ */
3817
+ drainRelationships() {
3818
+ const rels = this.bufferedRelationships;
3819
+ this.bufferedRelationships = [];
3820
+ return rels;
3821
+ }
3822
+ flush() {
3823
+ return {
3824
+ nodes: this.bufferedNodes,
3825
+ relationships: this.bufferedRelationships
3826
+ };
3827
+ }
3828
+ /** Cumulative counts (including already-drained items). */
3829
+ stats() {
3830
+ return {
3831
+ nodes: this.totalNodes,
3832
+ relationships: this.totalRelationships
3833
+ };
3834
+ }
3835
+ }
3836
+ class PipelineDebugLog {
3837
+ entries = [];
3838
+ startTime = 0;
3839
+ maxEntries;
3840
+ _enabled;
3841
+ constructor(opts = {}) {
3842
+ this.maxEntries = opts.maxEntries ?? 2e3;
3843
+ this._enabled = opts.enabled ?? true;
3844
+ }
3845
+ get enabled() {
3846
+ return this._enabled;
3847
+ }
3848
+ start() {
3849
+ this.entries = [];
3850
+ this.startTime = performance.now();
3851
+ this.log("pipeline", "started");
3852
+ }
3853
+ log(label, detail) {
3854
+ if (!this._enabled) return;
3855
+ const now = performance.now();
3856
+ const entry = {
3857
+ ts: now,
3858
+ elapsed: now - this.startTime,
3859
+ label,
3860
+ detail
3861
+ };
3862
+ this.entries.push(entry);
3863
+ if (this.entries.length > this.maxEntries) {
3864
+ this.entries.shift();
3865
+ }
3866
+ }
3867
+ logEvent(event) {
3868
+ if (!this._enabled) return;
3869
+ if ("action" in event) {
3870
+ const mutInfo = event.mutation ? ` nodes=${event.mutation.nodes.length} rels=${event.mutation.relationships.length}` : "";
3871
+ this.log(
3872
+ `stage:${event.stage}`,
3873
+ `${event.action} ${event.node}${mutInfo}`
3874
+ );
3875
+ } else if ("kind" in event) {
3876
+ switch (event.kind) {
3877
+ case "pipeline_done":
3878
+ this.log(
3879
+ "pipeline",
3880
+ `done nodes=${event.totalNodes} rels=${event.totalRelationships}`
3881
+ );
3882
+ break;
3883
+ case "pipeline_error":
3884
+ this.log("pipeline", `error: ${event.error}`);
3885
+ break;
3886
+ case "item_error":
3887
+ this.log(
3888
+ `stage:${event.stage}`,
3889
+ `item_error ${event.node}: ${event.error}`
3890
+ );
3891
+ break;
3892
+ case "flush_start":
3893
+ this.log(`stage:${event.stage}`, "flush_start");
3894
+ break;
3895
+ case "flush_end": {
3896
+ const mutInfo = event.mutation ? ` nodes=${event.mutation.nodes.length} rels=${event.mutation.relationships.length}` : "";
3897
+ this.log(`stage:${event.stage}`, `flush_end${mutInfo}`);
3898
+ break;
3899
+ }
3900
+ }
3901
+ }
3902
+ }
3903
+ /** Return all entries (most recent last). */
3904
+ getEntries() {
3905
+ return this.entries;
3906
+ }
3907
+ /** Summarize stage durations and counts. */
3908
+ summary() {
3909
+ const stages = {};
3910
+ for (const entry of this.entries) {
3911
+ if (!entry.label.startsWith("stage:")) continue;
3912
+ const stage = entry.label;
3913
+ if (!stages[stage]) {
3914
+ stages[stage] = { count: 0, totalMs: 0, lastStart: 0 };
3915
+ }
3916
+ if (entry.detail?.startsWith("start ")) {
3917
+ stages[stage].lastStart = entry.ts;
3918
+ } else if (entry.detail?.startsWith("end ")) {
3919
+ if (stages[stage].lastStart > 0) {
3920
+ stages[stage].totalMs += entry.ts - stages[stage].lastStart;
3921
+ stages[stage].count++;
3922
+ stages[stage].lastStart = 0;
3923
+ }
3924
+ }
3925
+ }
3926
+ const result = {};
3927
+ for (const [k, v] of Object.entries(stages)) {
3928
+ result[k] = { count: v.count, totalMs: Math.round(v.totalMs * 100) / 100 };
3929
+ }
3930
+ return result;
3931
+ }
3932
+ /** Dump to console in a readable format. */
3933
+ dump() {
3934
+ console.group("[PipelineDebug] Event log");
3935
+ for (const e of this.entries) {
3936
+ console.log(
3937
+ `%c+${e.elapsed.toFixed(1)}ms%c ${e.label} %c${e.detail ?? ""}`,
3938
+ "color: gray",
3939
+ "color: white; font-weight: bold",
3940
+ "color: cyan"
3941
+ );
3942
+ }
3943
+ console.groupEnd();
3944
+ const s = this.summary();
3945
+ if (Object.keys(s).length > 0) {
3946
+ console.group("[PipelineDebug] Stage summary");
3947
+ for (const [stage, info] of Object.entries(s)) {
3948
+ console.log(
3949
+ `${stage}: ${info.count} items in ${info.totalMs.toFixed(1)}ms (avg ${(info.totalMs / Math.max(info.count, 1)).toFixed(1)}ms)`
3950
+ );
3951
+ }
3952
+ console.groupEnd();
3953
+ }
3954
+ }
3955
+ }
3434
3956
  exports.DEFAULT_SUMMARIZER_CONFIG = DEFAULT_SUMMARIZER_CONFIG;
3957
+ exports.EMPTY_MUTATION = EMPTY_MUTATION;
3958
+ exports.ExtractStage = ExtractStage;
3959
+ exports.FileCacheStage = FileCacheStage;
3435
3960
  exports.MemoryStore = MemoryStore;
3961
+ exports.PipelineDebugLog = PipelineDebugLog;
3962
+ exports.ResolveStage = ResolveStage;
3963
+ exports.StoreStage = StoreStage;
3964
+ exports.SummarizeStage = SummarizeStage;
3436
3965
  exports.TemplateSummarizer = TemplateSummarizer;
3437
3966
  exports.addToRegistry = addToRegistry;
3438
3967
  exports.analyzeGoImports = analyzeGoImports;
@@ -3445,6 +3974,7 @@ exports.collectPipeline = collectPipeline;
3445
3974
  exports.countSymbols = countSymbols;
3446
3975
  exports.detectLanguage = detectLanguage;
3447
3976
  exports.ensureDirChain = ensureDirChain;
3977
+ exports.executeScanning = execute$3;
3448
3978
  exports.extractGeneric = extractGeneric;
3449
3979
  exports.extractGo = extractGo;
3450
3980
  exports.extractKeywords = extractKeywords;
@@ -3471,6 +4001,7 @@ exports.processSymbol = processSymbol;
3471
4001
  exports.resetDirIndexCache = resetDirIndexCache;
3472
4002
  exports.resolveCalls = resolveCalls;
3473
4003
  exports.resolvedCallsToRelationships = resolvedCallsToRelationships;
4004
+ exports.runNodePipeline = runNodePipeline;
3474
4005
  exports.runPipeline = runPipeline;
3475
4006
  exports.splitIdentifier = splitIdentifier;
3476
4007
  exports.summarizeClass = summarizeClass;