@neat.is/core 0.10.6-dev.20261007 → 0.10.6-dev.20261008
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/{chunk-KTJK2PAH.js → chunk-6QMWJAWR.js} +204 -609
- package/dist/chunk-6QMWJAWR.js.map +1 -0
- package/dist/{chunk-VL7ANDPI.js → chunk-EMFX3V77.js} +10 -8
- package/dist/{chunk-VL7ANDPI.js.map → chunk-EMFX3V77.js.map} +1 -1
- package/dist/chunk-MR64ICXX.js +19 -0
- package/dist/chunk-MR64ICXX.js.map +1 -0
- package/dist/{chunk-EFBWQIKG.js → chunk-QPG2QXCW.js} +11 -27
- package/dist/chunk-QPG2QXCW.js.map +1 -0
- package/dist/chunk-RGYC5U2Q.js +511 -0
- package/dist/chunk-RGYC5U2Q.js.map +1 -0
- package/dist/{chunk-LXQCL4MP.js → chunk-TZXSWSHX.js} +2 -2
- package/dist/{chunk-BC53SCT7.js → chunk-VFZ6CXLI.js} +20 -6
- package/dist/chunk-VFZ6CXLI.js.map +1 -0
- package/dist/cli.cjs +300 -74
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +2 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +249 -76
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +58 -15
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -99
- package/dist/index.d.ts +3 -99
- package/dist/index.js +18 -15
- package/dist/neatd.cjs +58 -15
- package/dist/neatd.cjs.map +1 -1
- package/dist/neatd.js +7 -4
- package/dist/neatd.js.map +1 -1
- package/dist/otel-grpc-CCCI772O.js +11 -0
- package/dist/registry.cjs +570 -0
- package/dist/registry.cjs.map +1 -0
- package/dist/registry.d.cts +220 -0
- package/dist/registry.d.ts +220 -0
- package/dist/registry.js +62 -0
- package/dist/registry.js.map +1 -0
- package/dist/server.cjs +78 -21
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +10 -6
- package/dist/server.js.map +1 -1
- package/package.json +8 -3
- package/dist/chunk-BC53SCT7.js.map +0 -1
- package/dist/chunk-EFBWQIKG.js.map +0 -1
- package/dist/chunk-KTJK2PAH.js.map +0 -1
- package/dist/otel-grpc-B7ABHLV7.js +0 -10
- /package/dist/{chunk-LXQCL4MP.js.map → chunk-TZXSWSHX.js.map} +0 -0
- /package/dist/{otel-grpc-B7ABHLV7.js.map → otel-grpc-CCCI772O.js.map} +0 -0
package/dist/index.d.cts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { MultiDirectedGraph } from 'graphology';
|
|
2
|
-
import { GraphNode, GraphEdge, EdgeEvidence, ErrorEvent, SpanAttributes, EdgeTypeValue, StaleEvent, BlastRadiusResult, RootCauseResult, RegistryEntry
|
|
2
|
+
import { GraphNode, GraphEdge, EdgeEvidence, ErrorEvent, SpanAttributes, EdgeTypeValue, StaleEvent, BlastRadiusResult, RootCauseResult, RegistryEntry } from '@neat.is/types';
|
|
3
3
|
export { StaleEvent } from '@neat.is/types';
|
|
4
4
|
import { FastifyInstance } from 'fastify';
|
|
5
|
+
export { ProjectNameCollisionError, addProject, getProject, listProjects, normalizeProjectPath, readRegistry, registryLockPath, registryPath, removeProject, setStatus, touchLastSeen, writeAtomically } from './registry.cjs';
|
|
5
6
|
|
|
6
7
|
type NeatGraph = MultiDirectedGraph<GraphNode, GraphEdge>;
|
|
7
8
|
declare function getGraph(project?: string): NeatGraph;
|
|
@@ -726,101 +727,4 @@ interface DaemonHandle {
|
|
|
726
727
|
declare function routeSpanToProject(serviceName: string | undefined, projects: ReadonlyArray<RegistryEntry>): string;
|
|
727
728
|
declare function startDaemon(opts?: DaemonOptions): Promise<DaemonHandle>;
|
|
728
729
|
|
|
729
|
-
|
|
730
|
-
* Machine-level project registry + machine-wide daemon discovery.
|
|
731
|
-
*
|
|
732
|
-
* This module owns two surfaces under `~/.neat/`:
|
|
733
|
-
*
|
|
734
|
-
* 1. The legacy project registry at `~/.neat/projects.json` (ADR-048). One
|
|
735
|
-
* file, per-user, machine-local, not synced. Under the project-daemon
|
|
736
|
-
* contract (ADR-096) it is no longer the coordination point — it is read
|
|
737
|
-
* once for migration and otherwise left to the additive writes the daemon
|
|
738
|
-
* and orchestrator still perform. The read-modify-write helpers below keep
|
|
739
|
-
* their atomic-write + exclusive-lock machinery for that legacy surface.
|
|
740
|
-
*
|
|
741
|
-
* 2. The machine-wide daemon discovery directory at `~/.neat/daemons/`
|
|
742
|
-
* (ADR-096 §6). One file per running daemon — `<project>.json` — each owned
|
|
743
|
-
* solely by the daemon that wrote it (on start) and removes it (on graceful
|
|
744
|
-
* stop). Discovery is **append-only and lock-free**: a reader scans the
|
|
745
|
-
* directory and reconciles liveness; it never acquires a shared lock, so it
|
|
746
|
-
* can never deadlock against a daemon (#506). Losing or rebuilding the
|
|
747
|
-
* directory costs discovery convenience, not correctness — each project's
|
|
748
|
-
* own `neat-out/daemon.json` stays authoritative.
|
|
749
|
-
*
|
|
750
|
-
* `neat ps` / `neat list` and the per-daemon `pause` / `resume` / `uninstall`
|
|
751
|
-
* verbs read discovery, falling back to the legacy registry where no daemon
|
|
752
|
-
* file is present yet (the migration window before every daemon self-describes).
|
|
753
|
-
*
|
|
754
|
-
* The legacy lock is a file we exclusively-create (`O_EXCL`), hold while we
|
|
755
|
-
* mutate, and unlink on the way out. Crude but cross-platform; matches what
|
|
756
|
-
* `proper-lockfile` does internally without pulling the dep in. It never sits
|
|
757
|
-
* on the discovery path.
|
|
758
|
-
*/
|
|
759
|
-
|
|
760
|
-
declare function registryPath(): string;
|
|
761
|
-
declare function registryLockPath(): string;
|
|
762
|
-
/**
|
|
763
|
-
* Path normalisation per ADR-048 #7. Two `init` calls from different relative
|
|
764
|
-
* paths to the same dir must collapse to one entry. `path.resolve` handles
|
|
765
|
-
* relative-to-cwd; we pass it through `fs.realpath` when the dir exists so
|
|
766
|
-
* symlinked paths land on the same canonical entry too.
|
|
767
|
-
*/
|
|
768
|
-
declare function normalizeProjectPath(input: string): Promise<string>;
|
|
769
|
-
/**
|
|
770
|
-
* tmp + fsync + rename. The fsync on the data fd guarantees the bytes are on
|
|
771
|
-
* disk before rename swaps the inode; rename itself is atomic on POSIX.
|
|
772
|
-
*
|
|
773
|
-
* Exported so the init flow and test harnesses can use the same helper.
|
|
774
|
-
*/
|
|
775
|
-
declare function writeAtomically(target: string, contents: string): Promise<void>;
|
|
776
|
-
/**
|
|
777
|
-
* Read the registry from disk. Returns an empty registry if the file does
|
|
778
|
-
* not exist yet — first run, never registered anything.
|
|
779
|
-
*
|
|
780
|
-
* Throws on parse / schema errors. The contract is single-source-of-truth;
|
|
781
|
-
* a corrupt file is louder than a silent reset.
|
|
782
|
-
*/
|
|
783
|
-
declare function readRegistry(): Promise<RegistryFile>;
|
|
784
|
-
interface AddProjectOptions {
|
|
785
|
-
name: string;
|
|
786
|
-
path: string;
|
|
787
|
-
languages?: string[];
|
|
788
|
-
status?: RegistryStatus;
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* Anything the registry refuses to do.
|
|
792
|
-
*
|
|
793
|
-
* The identity lives on the class, not in the message text. Every caller that
|
|
794
|
-
* prints one already prefixes its own name — the CLI writes `neat: ` — so a
|
|
795
|
-
* message carrying `neat registry:` came out as `neat: neat registry: …`
|
|
796
|
-
* (#1240). A non-CLI caller that wants to know where an error came from reads
|
|
797
|
-
* `err.name`, which is more reliable than a string prefix anyway.
|
|
798
|
-
*/
|
|
799
|
-
declare class RegistryError extends Error {
|
|
800
|
-
constructor(message: string);
|
|
801
|
-
}
|
|
802
|
-
declare class ProjectNameCollisionError extends RegistryError {
|
|
803
|
-
readonly projectName: string;
|
|
804
|
-
constructor(name: string);
|
|
805
|
-
}
|
|
806
|
-
/**
|
|
807
|
-
* Register a project, or update its `lastSeenAt` if the same path is already
|
|
808
|
-
* registered under the same name (idempotent re-init).
|
|
809
|
-
*
|
|
810
|
-
* Hard error on name collision against a different path — ADR-046 #7. The
|
|
811
|
-
* caller can recover by passing `--project <new-name>`.
|
|
812
|
-
*/
|
|
813
|
-
declare function addProject(opts: AddProjectOptions): Promise<RegistryEntry>;
|
|
814
|
-
declare function getProject(name: string): Promise<RegistryEntry | undefined>;
|
|
815
|
-
declare function listProjects(): Promise<RegistryEntry[]>;
|
|
816
|
-
declare function setStatus(name: string, status: RegistryStatus): Promise<RegistryEntry>;
|
|
817
|
-
declare function touchLastSeen(name: string, at?: string): Promise<void>;
|
|
818
|
-
/**
|
|
819
|
-
* Remove the registry entry for `name`. Per ADR-048 #6: this only removes the
|
|
820
|
-
* registry row. It does **not** touch `neat-out/`, `policy.json`, or any user
|
|
821
|
-
* file in the project directory. SDK-install rollback is a separate flow
|
|
822
|
-
* (`neat-rollback.patch`) that the caller opts in to.
|
|
823
|
-
*/
|
|
824
|
-
declare function removeProject(name: string): Promise<RegistryEntry | undefined>;
|
|
825
|
-
|
|
826
|
-
export { type BuildApiOptions, type BuildOtelGrpcReceiverOptions, type BuildOtelReceiverOptions, type CompatPair, type CompatibilityResult, type DaemonHandle, type DaemonOptions, type ExtractResult, type GraphDiff, type IngestContext, type MarkStaleOptions, type NeatGraph, type OtelGrpcReceiver, type OtlpTracesRequest, type ParsedSpan, type PersistedSnapshot, ProjectNameCollisionError, type ProjectSlot, type SpanHandler, type StalenessLoopOptions, addProject, buildApi, buildOtelReceiver, checkCompatibility, compatPairs, computeGraphDiff, confidenceForEdge, extractFromDirectory, getBlastRadius, getGraph, getProject, getRootCause, handleSpan, listProjects, loadGraphFromDisk, loadSnapshotForDiff, logSpanHandler, makeSpanHandler, markStaleEdges, normalizeProjectPath, parseOtlpRequest, readErrorEvents, readRegistry, readStaleEvents, registryLockPath, registryPath, removeProject, resetGraph, routeSpanToProject, saveGraphToDisk, setStatus, startDaemon, startOtelGrpcReceiver, startPersistLoop, startStalenessLoop, stitchTrace, thresholdForEdgeType, touchLastSeen, writeAtomically };
|
|
730
|
+
export { type BuildApiOptions, type BuildOtelGrpcReceiverOptions, type BuildOtelReceiverOptions, type CompatPair, type CompatibilityResult, type DaemonHandle, type DaemonOptions, type ExtractResult, type GraphDiff, type IngestContext, type MarkStaleOptions, type NeatGraph, type OtelGrpcReceiver, type OtlpTracesRequest, type ParsedSpan, type PersistedSnapshot, type ProjectSlot, type SpanHandler, type StalenessLoopOptions, buildApi, buildOtelReceiver, checkCompatibility, compatPairs, computeGraphDiff, confidenceForEdge, extractFromDirectory, getBlastRadius, getGraph, getRootCause, handleSpan, loadGraphFromDisk, loadSnapshotForDiff, logSpanHandler, makeSpanHandler, markStaleEdges, parseOtlpRequest, readErrorEvents, readStaleEvents, resetGraph, routeSpanToProject, saveGraphToDisk, startDaemon, startOtelGrpcReceiver, startPersistLoop, startStalenessLoop, stitchTrace, thresholdForEdgeType };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { MultiDirectedGraph } from 'graphology';
|
|
2
|
-
import { GraphNode, GraphEdge, EdgeEvidence, ErrorEvent, SpanAttributes, EdgeTypeValue, StaleEvent, BlastRadiusResult, RootCauseResult, RegistryEntry
|
|
2
|
+
import { GraphNode, GraphEdge, EdgeEvidence, ErrorEvent, SpanAttributes, EdgeTypeValue, StaleEvent, BlastRadiusResult, RootCauseResult, RegistryEntry } from '@neat.is/types';
|
|
3
3
|
export { StaleEvent } from '@neat.is/types';
|
|
4
4
|
import { FastifyInstance } from 'fastify';
|
|
5
|
+
export { ProjectNameCollisionError, addProject, getProject, listProjects, normalizeProjectPath, readRegistry, registryLockPath, registryPath, removeProject, setStatus, touchLastSeen, writeAtomically } from './registry.js';
|
|
5
6
|
|
|
6
7
|
type NeatGraph = MultiDirectedGraph<GraphNode, GraphEdge>;
|
|
7
8
|
declare function getGraph(project?: string): NeatGraph;
|
|
@@ -726,101 +727,4 @@ interface DaemonHandle {
|
|
|
726
727
|
declare function routeSpanToProject(serviceName: string | undefined, projects: ReadonlyArray<RegistryEntry>): string;
|
|
727
728
|
declare function startDaemon(opts?: DaemonOptions): Promise<DaemonHandle>;
|
|
728
729
|
|
|
729
|
-
|
|
730
|
-
* Machine-level project registry + machine-wide daemon discovery.
|
|
731
|
-
*
|
|
732
|
-
* This module owns two surfaces under `~/.neat/`:
|
|
733
|
-
*
|
|
734
|
-
* 1. The legacy project registry at `~/.neat/projects.json` (ADR-048). One
|
|
735
|
-
* file, per-user, machine-local, not synced. Under the project-daemon
|
|
736
|
-
* contract (ADR-096) it is no longer the coordination point — it is read
|
|
737
|
-
* once for migration and otherwise left to the additive writes the daemon
|
|
738
|
-
* and orchestrator still perform. The read-modify-write helpers below keep
|
|
739
|
-
* their atomic-write + exclusive-lock machinery for that legacy surface.
|
|
740
|
-
*
|
|
741
|
-
* 2. The machine-wide daemon discovery directory at `~/.neat/daemons/`
|
|
742
|
-
* (ADR-096 §6). One file per running daemon — `<project>.json` — each owned
|
|
743
|
-
* solely by the daemon that wrote it (on start) and removes it (on graceful
|
|
744
|
-
* stop). Discovery is **append-only and lock-free**: a reader scans the
|
|
745
|
-
* directory and reconciles liveness; it never acquires a shared lock, so it
|
|
746
|
-
* can never deadlock against a daemon (#506). Losing or rebuilding the
|
|
747
|
-
* directory costs discovery convenience, not correctness — each project's
|
|
748
|
-
* own `neat-out/daemon.json` stays authoritative.
|
|
749
|
-
*
|
|
750
|
-
* `neat ps` / `neat list` and the per-daemon `pause` / `resume` / `uninstall`
|
|
751
|
-
* verbs read discovery, falling back to the legacy registry where no daemon
|
|
752
|
-
* file is present yet (the migration window before every daemon self-describes).
|
|
753
|
-
*
|
|
754
|
-
* The legacy lock is a file we exclusively-create (`O_EXCL`), hold while we
|
|
755
|
-
* mutate, and unlink on the way out. Crude but cross-platform; matches what
|
|
756
|
-
* `proper-lockfile` does internally without pulling the dep in. It never sits
|
|
757
|
-
* on the discovery path.
|
|
758
|
-
*/
|
|
759
|
-
|
|
760
|
-
declare function registryPath(): string;
|
|
761
|
-
declare function registryLockPath(): string;
|
|
762
|
-
/**
|
|
763
|
-
* Path normalisation per ADR-048 #7. Two `init` calls from different relative
|
|
764
|
-
* paths to the same dir must collapse to one entry. `path.resolve` handles
|
|
765
|
-
* relative-to-cwd; we pass it through `fs.realpath` when the dir exists so
|
|
766
|
-
* symlinked paths land on the same canonical entry too.
|
|
767
|
-
*/
|
|
768
|
-
declare function normalizeProjectPath(input: string): Promise<string>;
|
|
769
|
-
/**
|
|
770
|
-
* tmp + fsync + rename. The fsync on the data fd guarantees the bytes are on
|
|
771
|
-
* disk before rename swaps the inode; rename itself is atomic on POSIX.
|
|
772
|
-
*
|
|
773
|
-
* Exported so the init flow and test harnesses can use the same helper.
|
|
774
|
-
*/
|
|
775
|
-
declare function writeAtomically(target: string, contents: string): Promise<void>;
|
|
776
|
-
/**
|
|
777
|
-
* Read the registry from disk. Returns an empty registry if the file does
|
|
778
|
-
* not exist yet — first run, never registered anything.
|
|
779
|
-
*
|
|
780
|
-
* Throws on parse / schema errors. The contract is single-source-of-truth;
|
|
781
|
-
* a corrupt file is louder than a silent reset.
|
|
782
|
-
*/
|
|
783
|
-
declare function readRegistry(): Promise<RegistryFile>;
|
|
784
|
-
interface AddProjectOptions {
|
|
785
|
-
name: string;
|
|
786
|
-
path: string;
|
|
787
|
-
languages?: string[];
|
|
788
|
-
status?: RegistryStatus;
|
|
789
|
-
}
|
|
790
|
-
/**
|
|
791
|
-
* Anything the registry refuses to do.
|
|
792
|
-
*
|
|
793
|
-
* The identity lives on the class, not in the message text. Every caller that
|
|
794
|
-
* prints one already prefixes its own name — the CLI writes `neat: ` — so a
|
|
795
|
-
* message carrying `neat registry:` came out as `neat: neat registry: …`
|
|
796
|
-
* (#1240). A non-CLI caller that wants to know where an error came from reads
|
|
797
|
-
* `err.name`, which is more reliable than a string prefix anyway.
|
|
798
|
-
*/
|
|
799
|
-
declare class RegistryError extends Error {
|
|
800
|
-
constructor(message: string);
|
|
801
|
-
}
|
|
802
|
-
declare class ProjectNameCollisionError extends RegistryError {
|
|
803
|
-
readonly projectName: string;
|
|
804
|
-
constructor(name: string);
|
|
805
|
-
}
|
|
806
|
-
/**
|
|
807
|
-
* Register a project, or update its `lastSeenAt` if the same path is already
|
|
808
|
-
* registered under the same name (idempotent re-init).
|
|
809
|
-
*
|
|
810
|
-
* Hard error on name collision against a different path — ADR-046 #7. The
|
|
811
|
-
* caller can recover by passing `--project <new-name>`.
|
|
812
|
-
*/
|
|
813
|
-
declare function addProject(opts: AddProjectOptions): Promise<RegistryEntry>;
|
|
814
|
-
declare function getProject(name: string): Promise<RegistryEntry | undefined>;
|
|
815
|
-
declare function listProjects(): Promise<RegistryEntry[]>;
|
|
816
|
-
declare function setStatus(name: string, status: RegistryStatus): Promise<RegistryEntry>;
|
|
817
|
-
declare function touchLastSeen(name: string, at?: string): Promise<void>;
|
|
818
|
-
/**
|
|
819
|
-
* Remove the registry entry for `name`. Per ADR-048 #6: this only removes the
|
|
820
|
-
* registry row. It does **not** touch `neat-out/`, `policy.json`, or any user
|
|
821
|
-
* file in the project directory. SDK-install rollback is a separate flow
|
|
822
|
-
* (`neat-rollback.patch`) that the caller opts in to.
|
|
823
|
-
*/
|
|
824
|
-
declare function removeProject(name: string): Promise<RegistryEntry | undefined>;
|
|
825
|
-
|
|
826
|
-
export { type BuildApiOptions, type BuildOtelGrpcReceiverOptions, type BuildOtelReceiverOptions, type CompatPair, type CompatibilityResult, type DaemonHandle, type DaemonOptions, type ExtractResult, type GraphDiff, type IngestContext, type MarkStaleOptions, type NeatGraph, type OtelGrpcReceiver, type OtlpTracesRequest, type ParsedSpan, type PersistedSnapshot, ProjectNameCollisionError, type ProjectSlot, type SpanHandler, type StalenessLoopOptions, addProject, buildApi, buildOtelReceiver, checkCompatibility, compatPairs, computeGraphDiff, confidenceForEdge, extractFromDirectory, getBlastRadius, getGraph, getProject, getRootCause, handleSpan, listProjects, loadGraphFromDisk, loadSnapshotForDiff, logSpanHandler, makeSpanHandler, markStaleEdges, normalizeProjectPath, parseOtlpRequest, readErrorEvents, readRegistry, readStaleEvents, registryLockPath, registryPath, removeProject, resetGraph, routeSpanToProject, saveGraphToDisk, setStatus, startDaemon, startOtelGrpcReceiver, startPersistLoop, startStalenessLoop, stitchTrace, thresholdForEdgeType, touchLastSeen, writeAtomically };
|
|
730
|
+
export { type BuildApiOptions, type BuildOtelGrpcReceiverOptions, type BuildOtelReceiverOptions, type CompatPair, type CompatibilityResult, type DaemonHandle, type DaemonOptions, type ExtractResult, type GraphDiff, type IngestContext, type MarkStaleOptions, type NeatGraph, type OtelGrpcReceiver, type OtlpTracesRequest, type ParsedSpan, type PersistedSnapshot, type ProjectSlot, type SpanHandler, type StalenessLoopOptions, buildApi, buildOtelReceiver, checkCompatibility, compatPairs, computeGraphDiff, confidenceForEdge, extractFromDirectory, getBlastRadius, getGraph, getRootCause, handleSpan, loadGraphFromDisk, loadSnapshotForDiff, logSpanHandler, makeSpanHandler, markStaleEdges, parseOtlpRequest, readErrorEvents, readStaleEvents, resetGraph, routeSpanToProject, saveGraphToDisk, startDaemon, startOtelGrpcReceiver, startPersistLoop, startStalenessLoop, stitchTrace, thresholdForEdgeType };
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
2
|
routeSpanToProject,
|
|
3
3
|
startDaemon
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-EMFX3V77.js";
|
|
5
5
|
import {
|
|
6
|
-
ProjectNameCollisionError,
|
|
7
|
-
addProject,
|
|
8
6
|
buildApi,
|
|
9
7
|
checkCompatibility,
|
|
10
8
|
compatPairs,
|
|
@@ -13,39 +11,44 @@ import {
|
|
|
13
11
|
extractFromDirectory,
|
|
14
12
|
getBlastRadius,
|
|
15
13
|
getGraph,
|
|
16
|
-
getProject,
|
|
17
14
|
getRootCause,
|
|
18
15
|
handleSpan,
|
|
19
|
-
listProjects,
|
|
20
16
|
loadGraphFromDisk,
|
|
21
17
|
loadSnapshotForDiff,
|
|
22
18
|
makeSpanHandler,
|
|
23
19
|
markStaleEdges,
|
|
24
|
-
normalizeProjectPath,
|
|
25
20
|
readErrorEvents,
|
|
26
|
-
readRegistry,
|
|
27
21
|
readStaleEvents,
|
|
28
|
-
registryLockPath,
|
|
29
|
-
registryPath,
|
|
30
|
-
removeProject,
|
|
31
22
|
resetGraph,
|
|
32
23
|
saveGraphToDisk,
|
|
33
|
-
setStatus,
|
|
34
24
|
startPersistLoop,
|
|
35
25
|
startStalenessLoop,
|
|
36
26
|
stitchTrace,
|
|
37
|
-
thresholdForEdgeType
|
|
27
|
+
thresholdForEdgeType
|
|
28
|
+
} from "./chunk-6QMWJAWR.js";
|
|
29
|
+
import {
|
|
30
|
+
ProjectNameCollisionError,
|
|
31
|
+
addProject,
|
|
32
|
+
getProject,
|
|
33
|
+
listProjects,
|
|
34
|
+
normalizeProjectPath,
|
|
35
|
+
readRegistry,
|
|
36
|
+
registryLockPath,
|
|
37
|
+
registryPath,
|
|
38
|
+
removeProject,
|
|
39
|
+
setStatus,
|
|
38
40
|
touchLastSeen,
|
|
39
41
|
writeAtomically
|
|
40
|
-
} from "./chunk-
|
|
42
|
+
} from "./chunk-RGYC5U2Q.js";
|
|
41
43
|
import {
|
|
42
44
|
startOtelGrpcReceiver
|
|
43
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-TZXSWSHX.js";
|
|
44
46
|
import {
|
|
45
47
|
buildOtelReceiver,
|
|
46
48
|
logSpanHandler,
|
|
47
49
|
parseOtlpRequest
|
|
48
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-QPG2QXCW.js";
|
|
51
|
+
import "./chunk-MR64ICXX.js";
|
|
49
52
|
export {
|
|
50
53
|
ProjectNameCollisionError,
|
|
51
54
|
addProject,
|
package/dist/neatd.cjs
CHANGED
|
@@ -6550,7 +6550,29 @@ function ensureFrontierNode(graph, host, ts) {
|
|
|
6550
6550
|
graph.addNode(id, node);
|
|
6551
6551
|
return id;
|
|
6552
6552
|
}
|
|
6553
|
-
|
|
6553
|
+
var EDGE_ERROR_MESSAGE_MAX = 512;
|
|
6554
|
+
function buildEdgeErrorExemplar(detail, ts) {
|
|
6555
|
+
return {
|
|
6556
|
+
...detail.exceptionType !== void 0 ? { exceptionType: detail.exceptionType } : {},
|
|
6557
|
+
...detail.message !== void 0 ? { message: detail.message.slice(0, EDGE_ERROR_MESSAGE_MAX) } : {},
|
|
6558
|
+
...detail.httpStatusCode !== void 0 ? { httpStatusCode: detail.httpStatusCode } : {},
|
|
6559
|
+
at: ts
|
|
6560
|
+
};
|
|
6561
|
+
}
|
|
6562
|
+
function edgeErrorDetailFromSpan(span) {
|
|
6563
|
+
const exceptionType = span.exception?.type;
|
|
6564
|
+
const message = span.exception?.message;
|
|
6565
|
+
const httpStatusCode = httpResponseStatus(span);
|
|
6566
|
+
if (exceptionType === void 0 && message === void 0 && httpStatusCode === void 0) {
|
|
6567
|
+
return void 0;
|
|
6568
|
+
}
|
|
6569
|
+
return {
|
|
6570
|
+
...exceptionType !== void 0 ? { exceptionType } : {},
|
|
6571
|
+
...message !== void 0 ? { message } : {},
|
|
6572
|
+
...httpStatusCode !== void 0 ? { httpStatusCode } : {}
|
|
6573
|
+
};
|
|
6574
|
+
}
|
|
6575
|
+
function upsertObservedEdge(graph, type, source, target, ts, isError = false, evidence, durationMs, errorDetail) {
|
|
6554
6576
|
if (!graph.hasNode(source) || !graph.hasNode(target)) return null;
|
|
6555
6577
|
const grain = source.startsWith("file:") || source.startsWith("symbol:") ? "file" : "service";
|
|
6556
6578
|
const id = makeObservedEdgeId(type, source, target);
|
|
@@ -6560,13 +6582,15 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
6560
6582
|
const newErrorCount = (existing.signal?.errorCount ?? 0) + (isError ? 1 : 0);
|
|
6561
6583
|
const latencyHist2 = durationMs !== void 0 ? recordLatency({ ...existing.signal?.latencyHist ?? {} }, durationMs) : existing.signal?.latencyHist;
|
|
6562
6584
|
const latencyMs2 = latencyPercentiles(latencyHist2) ?? existing.signal?.latencyMs;
|
|
6585
|
+
const lastError2 = isError && errorDetail ? buildEdgeErrorExemplar(errorDetail, ts) : existing.signal?.lastError;
|
|
6563
6586
|
const newSignal = {
|
|
6564
6587
|
spanCount: newSpanCount,
|
|
6565
6588
|
errorCount: newErrorCount,
|
|
6566
6589
|
lastObservedAgeMs: 0,
|
|
6567
6590
|
...latencyHist2 ? { latencyHist: latencyHist2 } : {},
|
|
6568
6591
|
...latencyMs2 ? { latencyMs: latencyMs2 } : {},
|
|
6569
|
-
...existing.signal?.anomalous !== void 0 ? { anomalous: existing.signal.anomalous } : {}
|
|
6592
|
+
...existing.signal?.anomalous !== void 0 ? { anomalous: existing.signal.anomalous } : {},
|
|
6593
|
+
...lastError2 ? { lastError: lastError2 } : {}
|
|
6570
6594
|
};
|
|
6571
6595
|
const updated = {
|
|
6572
6596
|
...existing,
|
|
@@ -6583,12 +6607,14 @@ function upsertObservedEdge(graph, type, source, target, ts, isError = false, ev
|
|
|
6583
6607
|
}
|
|
6584
6608
|
const latencyHist = durationMs !== void 0 ? recordLatency({}, durationMs) : void 0;
|
|
6585
6609
|
const latencyMs = latencyHist ? latencyPercentiles(latencyHist) : void 0;
|
|
6610
|
+
const lastError = isError && errorDetail ? buildEdgeErrorExemplar(errorDetail, ts) : void 0;
|
|
6586
6611
|
const signal = {
|
|
6587
6612
|
spanCount: 1,
|
|
6588
6613
|
errorCount: isError ? 1 : 0,
|
|
6589
6614
|
lastObservedAgeMs: 0,
|
|
6590
6615
|
...latencyHist ? { latencyHist } : {},
|
|
6591
|
-
...latencyMs ? { latencyMs } : {}
|
|
6616
|
+
...latencyMs ? { latencyMs } : {},
|
|
6617
|
+
...lastError ? { lastError } : {}
|
|
6592
6618
|
};
|
|
6593
6619
|
const edge = {
|
|
6594
6620
|
id,
|
|
@@ -6929,6 +6955,7 @@ async function handleSpan(ctx, span) {
|
|
|
6929
6955
|
const sourceId = ensureServiceNode(ctx.graph, span.service, env);
|
|
6930
6956
|
const isError = spanRecordsError(span);
|
|
6931
6957
|
const durationMs = span.durationNanos > 0n && !spanIsStreaming(span) ? Number(span.durationNanos) / 1e6 : void 0;
|
|
6958
|
+
const errorDetail = isError ? edgeErrorDetailFromSpan(span) : void 0;
|
|
6932
6959
|
const sourceServiceNode = ctx.graph.getNodeAttributes(sourceId);
|
|
6933
6960
|
const callSite = callSiteFromSpan(span, sourceServiceNode, ctx.scanPath);
|
|
6934
6961
|
cacheSpanService(span, nowMs, callSite);
|
|
@@ -6968,7 +6995,8 @@ async function handleSpan(ctx, span) {
|
|
|
6968
6995
|
ts,
|
|
6969
6996
|
isError,
|
|
6970
6997
|
callSiteEvidence,
|
|
6971
|
-
durationMs
|
|
6998
|
+
durationMs,
|
|
6999
|
+
errorDetail
|
|
6972
7000
|
);
|
|
6973
7001
|
if (result) affectedNode = targetId;
|
|
6974
7002
|
if (span.dbSystem === "mongodb" && span.dbCollection) {
|
|
@@ -6981,7 +7009,8 @@ async function handleSpan(ctx, span) {
|
|
|
6981
7009
|
ts,
|
|
6982
7010
|
isError,
|
|
6983
7011
|
callSiteEvidence,
|
|
6984
|
-
durationMs
|
|
7012
|
+
durationMs,
|
|
7013
|
+
errorDetail
|
|
6985
7014
|
);
|
|
6986
7015
|
}
|
|
6987
7016
|
if (span.dbTable) {
|
|
@@ -6994,7 +7023,8 @@ async function handleSpan(ctx, span) {
|
|
|
6994
7023
|
ts,
|
|
6995
7024
|
isError,
|
|
6996
7025
|
callSiteEvidence,
|
|
6997
|
-
durationMs
|
|
7026
|
+
durationMs,
|
|
7027
|
+
errorDetail
|
|
6998
7028
|
);
|
|
6999
7029
|
mergeObservedColumns(ctx.graph, tableId, span.dbColumns);
|
|
7000
7030
|
}
|
|
@@ -7014,7 +7044,8 @@ async function handleSpan(ctx, span) {
|
|
|
7014
7044
|
ts,
|
|
7015
7045
|
isError,
|
|
7016
7046
|
callSiteEvidence,
|
|
7017
|
-
durationMs
|
|
7047
|
+
durationMs,
|
|
7048
|
+
errorDetail
|
|
7018
7049
|
);
|
|
7019
7050
|
if (result) affectedNode = targetId;
|
|
7020
7051
|
} else if (span.graphqlOperationName && span.graphqlOperationType && spanServesGraphqlOperation(span.kind)) {
|
|
@@ -7032,7 +7063,8 @@ async function handleSpan(ctx, span) {
|
|
|
7032
7063
|
ts,
|
|
7033
7064
|
isError,
|
|
7034
7065
|
callSiteEvidence,
|
|
7035
|
-
durationMs
|
|
7066
|
+
durationMs,
|
|
7067
|
+
errorDetail
|
|
7036
7068
|
);
|
|
7037
7069
|
if (result) affectedNode = targetId;
|
|
7038
7070
|
} else if (span.rpcSystem === "grpc" && span.rpcService && span.rpcMethod && spanServesGrpcMethod(span.kind)) {
|
|
@@ -7045,7 +7077,8 @@ async function handleSpan(ctx, span) {
|
|
|
7045
7077
|
ts,
|
|
7046
7078
|
isError,
|
|
7047
7079
|
callSiteEvidence,
|
|
7048
|
-
durationMs
|
|
7080
|
+
durationMs,
|
|
7081
|
+
errorDetail
|
|
7049
7082
|
);
|
|
7050
7083
|
if (result) affectedNode = targetId;
|
|
7051
7084
|
} else if (span.websocketChannel && spanServesWebsocketChannel(span.kind)) {
|
|
@@ -7062,7 +7095,8 @@ async function handleSpan(ctx, span) {
|
|
|
7062
7095
|
ts,
|
|
7063
7096
|
isError,
|
|
7064
7097
|
callSiteEvidence,
|
|
7065
|
-
durationMs
|
|
7098
|
+
durationMs,
|
|
7099
|
+
errorDetail
|
|
7066
7100
|
);
|
|
7067
7101
|
if (result) affectedNode = targetId;
|
|
7068
7102
|
} else {
|
|
@@ -7079,7 +7113,8 @@ async function handleSpan(ctx, span) {
|
|
|
7079
7113
|
ts,
|
|
7080
7114
|
isError,
|
|
7081
7115
|
callSiteEvidence,
|
|
7082
|
-
durationMs
|
|
7116
|
+
durationMs,
|
|
7117
|
+
errorDetail
|
|
7083
7118
|
);
|
|
7084
7119
|
affectedNode = targetId;
|
|
7085
7120
|
resolvedViaAddress = true;
|
|
@@ -7093,7 +7128,8 @@ async function handleSpan(ctx, span) {
|
|
|
7093
7128
|
ts,
|
|
7094
7129
|
isError,
|
|
7095
7130
|
callSiteEvidence,
|
|
7096
|
-
durationMs
|
|
7131
|
+
durationMs,
|
|
7132
|
+
errorDetail
|
|
7097
7133
|
);
|
|
7098
7134
|
affectedNode = frontierNodeId;
|
|
7099
7135
|
resolvedViaAddress = true;
|
|
@@ -7120,7 +7156,8 @@ async function handleSpan(ctx, span) {
|
|
|
7120
7156
|
ts,
|
|
7121
7157
|
isError,
|
|
7122
7158
|
fallbackEvidence,
|
|
7123
|
-
durationMs
|
|
7159
|
+
durationMs,
|
|
7160
|
+
errorDetail
|
|
7124
7161
|
);
|
|
7125
7162
|
}
|
|
7126
7163
|
}
|
|
@@ -7135,7 +7172,7 @@ async function handleSpan(ctx, span) {
|
|
|
7135
7172
|
);
|
|
7136
7173
|
if (routeNodeId) {
|
|
7137
7174
|
const routeSvc = ctx.graph.getNodeAttributes(routeNodeId).service;
|
|
7138
|
-
upsertObservedEdge(ctx.graph, import_types8.EdgeType.CONTAINS, (0, import_types8.serviceId)(routeSvc), routeNodeId, ts, isError, void 0, durationMs);
|
|
7175
|
+
upsertObservedEdge(ctx.graph, import_types8.EdgeType.CONTAINS, (0, import_types8.serviceId)(routeSvc), routeNodeId, ts, isError, void 0, durationMs, errorDetail);
|
|
7139
7176
|
}
|
|
7140
7177
|
}
|
|
7141
7178
|
if (span.statusCode === 2) {
|
|
@@ -17637,6 +17674,11 @@ function detectObservedFailingEdge(graph, bucket) {
|
|
|
17637
17674
|
spanCount,
|
|
17638
17675
|
errorCount,
|
|
17639
17676
|
errorRate: clampConfidence(errorRate),
|
|
17677
|
+
// The bounded last-error exemplar rides the finding as evidence (ADR-236):
|
|
17678
|
+
// the error rate says HOW MUCH is failing, the exemplar says WHAT failed —
|
|
17679
|
+
// a timeout vs a deadlock vs a connection-refused, which `errorCount`
|
|
17680
|
+
// alone cannot name. Present only when the observed edge carries one.
|
|
17681
|
+
...signal.lastError ? { lastError: signal.lastError } : {},
|
|
17640
17682
|
confidence: OBSERVED_FAILING_CONFIDENCE,
|
|
17641
17683
|
reason: `Code declares ${bucket.source} \u2192 ${bucket.target} (${bucket.type}) and production observes it, but ${errorCount}/${spanCount} observed calls fail (${pct}% error rate) \u2014 the declared dependency is predominantly failing. The declared intent (EXTRACTED) and the observed behaviour (OBSERVED) diverge.`,
|
|
17642
17684
|
recommendation: "Treat this as a broken declared dependency, not a coverage gap: production runs the call the code declares and most calls error. Check the dependency\u2019s health, recent deploys, and the observed error responses on this edge."
|
|
@@ -19998,7 +20040,7 @@ async function computePrVerdict(req2, ctx, deps, deadline) {
|
|
|
19998
20040
|
const found = observedBreakFrom(node, getObservedDependencies(ctx.liveGraph, node.id));
|
|
19999
20041
|
if (found) observedBreaks.push(found);
|
|
20000
20042
|
}
|
|
20001
|
-
const { marker, body } = renderVerdict({
|
|
20043
|
+
const { marker, body: rendered } = renderVerdict({
|
|
20002
20044
|
graph: head,
|
|
20003
20045
|
delta,
|
|
20004
20046
|
changedFiles,
|
|
@@ -20006,6 +20048,7 @@ async function computePrVerdict(req2, ctx, deps, deadline) {
|
|
|
20006
20048
|
observedBreaks,
|
|
20007
20049
|
tone: req2.tone ?? "loud"
|
|
20008
20050
|
});
|
|
20051
|
+
const body = rendered.startsWith(marker) ? rendered.slice(marker.length).replace(/^(?:\r?\n)+/, "") : rendered;
|
|
20009
20052
|
return {
|
|
20010
20053
|
marker,
|
|
20011
20054
|
body,
|