@neat.is/core 0.4.26-dev.20260702 → 0.4.26

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/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { MultiDirectedGraph } from 'graphology';
2
- import { GraphNode, GraphEdge, StaleEvent, ErrorEvent, BlastRadiusResult, RootCauseResult, RegistryEntry, RegistryStatus, RegistryFile } from '@neat.is/types';
2
+ import { GraphNode, GraphEdge, StaleEvent, ErrorEvent, BlastRadiusResult, RootCauseResult, EdgeTypeValue, RegistryEntry, RegistryStatus, RegistryFile } from '@neat.is/types';
3
3
  export { StaleEvent } from '@neat.is/types';
4
4
  import { FastifyInstance } from 'fastify';
5
5
 
@@ -152,6 +152,14 @@ interface ParsedSpan {
152
152
  attributes: Record<string, AttributeValue>;
153
153
  dbSystem?: string;
154
154
  dbName?: string;
155
+ messagingSystem?: string;
156
+ messagingDestination?: string;
157
+ graphqlOperationName?: string;
158
+ graphqlOperationType?: string;
159
+ rpcSystem?: string;
160
+ rpcService?: string;
161
+ rpcMethod?: string;
162
+ websocketChannel?: string;
155
163
  statusCode?: number;
156
164
  errorMessage?: string;
157
165
  exception?: {
@@ -335,6 +343,96 @@ interface GraphDiff {
335
343
  declare function loadSnapshotForDiff(target: string): Promise<PersistedSnapshot>;
336
344
  declare function computeGraphDiff(liveGraph: NeatGraph, baseSnapshot: PersistedSnapshot, currentExportedAt?: string): GraphDiff;
337
345
 
346
+ /**
347
+ * A connector implements exactly one method. Everything downstream of
348
+ * `poll()` — resolving a static call site, minting the OBSERVED edge — is
349
+ * shared, generic code in connectors/index.ts.
350
+ */
351
+ interface ObservedConnector {
352
+ readonly provider: string;
353
+ poll(ctx: ConnectorContext): Promise<ObservedSignal[]>;
354
+ }
355
+ /**
356
+ * Everything a connector's `poll()` needs, resolved once at connector setup
357
+ * — never re-derived at poll time.
358
+ *
359
+ * `credentials` is opaque here on purpose: its shape is entirely provider-
360
+ * and profile-defined (local vs hosted — docs/contracts/connectors.md §3).
361
+ * It flows through to `poll()` only. Never log it, never write it into a
362
+ * node or edge, never let it reach the graph snapshot (contract §6, and the
363
+ * `.env`-contents rule docs/contracts.md Rule 4 already states for local
364
+ * config).
365
+ */
366
+ interface ConnectorContext {
367
+ projectDir: string;
368
+ credentials: Record<string, unknown>;
369
+ since?: string;
370
+ }
371
+ /**
372
+ * `file:line` the provider's own signal carries, when it does (rare — see
373
+ * docs/connectors/README.md §Provider interface, which notes this is
374
+ * "usually resolved by the mapping layer below, not here"). Reconciled onto
375
+ * the EXTRACTED service-relative path by the shared fuse step the same way
376
+ * an OTel span's call site is (file-awareness.md §4).
377
+ */
378
+ interface ConnectorCallSite {
379
+ file: string;
380
+ line: number;
381
+ }
382
+ /**
383
+ * One provider-agnostic observation. `targetKind`/`targetName` are the
384
+ * provider's own vocabulary (`'supabase-table'`/`'orders'`,
385
+ * `'route'`/`'GET /users/:id'`, ...) — resolving that pair to a NEAT node id
386
+ * is the one genuinely provider-specific step (README.md's pipeline
387
+ * diagram), supplied to `runConnectorPoll` (connectors/index.ts) as a
388
+ * `resolveTarget` callback. Everything downstream of that resolution — file
389
+ * grain fusion, OBSERVED mint — is shared.
390
+ */
391
+ interface ObservedSignal {
392
+ targetKind: string;
393
+ targetName: string;
394
+ callCount: number;
395
+ errorCount: number;
396
+ lastObservedIso: string;
397
+ callSite?: ConnectorCallSite;
398
+ }
399
+
400
+ /**
401
+ * What a provider's target-resolution step hands back for one signal. The
402
+ * generic pipeline needs both endpoints of the edge it's about to mint:
403
+ *
404
+ * - `serviceName` is the NEAT manifest service whose code produced the
405
+ * signal — the edge's source. The shared pipeline turns it into a plain
406
+ * ServiceNode id, or a FileNode id once the fuse step below resolves the
407
+ * signal's callSite against it.
408
+ * - `targetNodeId` is the id the provider's own mapping already resolved —
409
+ * an `infraId(...)` sub-resource, a RouteNode, a ServiceNode, whatever
410
+ * (see each provider's docs/connectors/<provider>.md §Fusion).
411
+ *
412
+ * Returning `null` skips the signal honestly: an unresolvable target never
413
+ * fabricates a node or edge (the same discipline file-awareness.md §6
414
+ * states for OTel ingest).
415
+ */
416
+ interface ResolvedConnectorTarget {
417
+ targetNodeId: string;
418
+ serviceName: string;
419
+ edgeType: EdgeTypeValue;
420
+ }
421
+ type ResolveConnectorTarget = (signal: ObservedSignal, ctx: ConnectorContext) => ResolvedConnectorTarget | null;
422
+ /**
423
+ * One project's registered connector, ready for `daemon.ts` to poll on an
424
+ * interval. Deliberately thin — no config-loading or credential-broker logic
425
+ * lives here (that's provider- and profile-specific, later work per
426
+ * docs/contracts/connectors.md §3); this is just the seam a daemon slot
427
+ * wires a connector through.
428
+ */
429
+ interface ConnectorRegistration {
430
+ connector: ObservedConnector;
431
+ credentials: Record<string, unknown>;
432
+ resolveTarget: ResolveConnectorTarget;
433
+ intervalMs?: number;
434
+ }
435
+
338
436
  /**
339
437
  * Multi-project daemon (ADR-049).
340
438
  *
@@ -386,6 +484,7 @@ interface DaemonOptions {
386
484
  otlpPort?: number;
387
485
  host?: string;
388
486
  bindListeners?: boolean;
487
+ connectors?: ConnectorRegistration[];
389
488
  }
390
489
  interface ProjectSlot {
391
490
  entry: RegistryEntry;
@@ -393,6 +492,8 @@ interface ProjectSlot {
393
492
  outPath: string;
394
493
  paths: ProjectPaths;
395
494
  stopPersist: () => void;
495
+ stopStaleness: () => void;
496
+ stopConnectors: () => void;
396
497
  detachEvents: () => void;
397
498
  status: 'active' | 'broken';
398
499
  errorReason?: string;
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { MultiDirectedGraph } from 'graphology';
2
- import { GraphNode, GraphEdge, StaleEvent, ErrorEvent, BlastRadiusResult, RootCauseResult, RegistryEntry, RegistryStatus, RegistryFile } from '@neat.is/types';
2
+ import { GraphNode, GraphEdge, StaleEvent, ErrorEvent, BlastRadiusResult, RootCauseResult, EdgeTypeValue, RegistryEntry, RegistryStatus, RegistryFile } from '@neat.is/types';
3
3
  export { StaleEvent } from '@neat.is/types';
4
4
  import { FastifyInstance } from 'fastify';
5
5
 
@@ -152,6 +152,14 @@ interface ParsedSpan {
152
152
  attributes: Record<string, AttributeValue>;
153
153
  dbSystem?: string;
154
154
  dbName?: string;
155
+ messagingSystem?: string;
156
+ messagingDestination?: string;
157
+ graphqlOperationName?: string;
158
+ graphqlOperationType?: string;
159
+ rpcSystem?: string;
160
+ rpcService?: string;
161
+ rpcMethod?: string;
162
+ websocketChannel?: string;
155
163
  statusCode?: number;
156
164
  errorMessage?: string;
157
165
  exception?: {
@@ -335,6 +343,96 @@ interface GraphDiff {
335
343
  declare function loadSnapshotForDiff(target: string): Promise<PersistedSnapshot>;
336
344
  declare function computeGraphDiff(liveGraph: NeatGraph, baseSnapshot: PersistedSnapshot, currentExportedAt?: string): GraphDiff;
337
345
 
346
+ /**
347
+ * A connector implements exactly one method. Everything downstream of
348
+ * `poll()` — resolving a static call site, minting the OBSERVED edge — is
349
+ * shared, generic code in connectors/index.ts.
350
+ */
351
+ interface ObservedConnector {
352
+ readonly provider: string;
353
+ poll(ctx: ConnectorContext): Promise<ObservedSignal[]>;
354
+ }
355
+ /**
356
+ * Everything a connector's `poll()` needs, resolved once at connector setup
357
+ * — never re-derived at poll time.
358
+ *
359
+ * `credentials` is opaque here on purpose: its shape is entirely provider-
360
+ * and profile-defined (local vs hosted — docs/contracts/connectors.md §3).
361
+ * It flows through to `poll()` only. Never log it, never write it into a
362
+ * node or edge, never let it reach the graph snapshot (contract §6, and the
363
+ * `.env`-contents rule docs/contracts.md Rule 4 already states for local
364
+ * config).
365
+ */
366
+ interface ConnectorContext {
367
+ projectDir: string;
368
+ credentials: Record<string, unknown>;
369
+ since?: string;
370
+ }
371
+ /**
372
+ * `file:line` the provider's own signal carries, when it does (rare — see
373
+ * docs/connectors/README.md §Provider interface, which notes this is
374
+ * "usually resolved by the mapping layer below, not here"). Reconciled onto
375
+ * the EXTRACTED service-relative path by the shared fuse step the same way
376
+ * an OTel span's call site is (file-awareness.md §4).
377
+ */
378
+ interface ConnectorCallSite {
379
+ file: string;
380
+ line: number;
381
+ }
382
+ /**
383
+ * One provider-agnostic observation. `targetKind`/`targetName` are the
384
+ * provider's own vocabulary (`'supabase-table'`/`'orders'`,
385
+ * `'route'`/`'GET /users/:id'`, ...) — resolving that pair to a NEAT node id
386
+ * is the one genuinely provider-specific step (README.md's pipeline
387
+ * diagram), supplied to `runConnectorPoll` (connectors/index.ts) as a
388
+ * `resolveTarget` callback. Everything downstream of that resolution — file
389
+ * grain fusion, OBSERVED mint — is shared.
390
+ */
391
+ interface ObservedSignal {
392
+ targetKind: string;
393
+ targetName: string;
394
+ callCount: number;
395
+ errorCount: number;
396
+ lastObservedIso: string;
397
+ callSite?: ConnectorCallSite;
398
+ }
399
+
400
+ /**
401
+ * What a provider's target-resolution step hands back for one signal. The
402
+ * generic pipeline needs both endpoints of the edge it's about to mint:
403
+ *
404
+ * - `serviceName` is the NEAT manifest service whose code produced the
405
+ * signal — the edge's source. The shared pipeline turns it into a plain
406
+ * ServiceNode id, or a FileNode id once the fuse step below resolves the
407
+ * signal's callSite against it.
408
+ * - `targetNodeId` is the id the provider's own mapping already resolved —
409
+ * an `infraId(...)` sub-resource, a RouteNode, a ServiceNode, whatever
410
+ * (see each provider's docs/connectors/<provider>.md §Fusion).
411
+ *
412
+ * Returning `null` skips the signal honestly: an unresolvable target never
413
+ * fabricates a node or edge (the same discipline file-awareness.md §6
414
+ * states for OTel ingest).
415
+ */
416
+ interface ResolvedConnectorTarget {
417
+ targetNodeId: string;
418
+ serviceName: string;
419
+ edgeType: EdgeTypeValue;
420
+ }
421
+ type ResolveConnectorTarget = (signal: ObservedSignal, ctx: ConnectorContext) => ResolvedConnectorTarget | null;
422
+ /**
423
+ * One project's registered connector, ready for `daemon.ts` to poll on an
424
+ * interval. Deliberately thin — no config-loading or credential-broker logic
425
+ * lives here (that's provider- and profile-specific, later work per
426
+ * docs/contracts/connectors.md §3); this is just the seam a daemon slot
427
+ * wires a connector through.
428
+ */
429
+ interface ConnectorRegistration {
430
+ connector: ObservedConnector;
431
+ credentials: Record<string, unknown>;
432
+ resolveTarget: ResolveConnectorTarget;
433
+ intervalMs?: number;
434
+ }
435
+
338
436
  /**
339
437
  * Multi-project daemon (ADR-049).
340
438
  *
@@ -386,6 +484,7 @@ interface DaemonOptions {
386
484
  otlpPort?: number;
387
485
  host?: string;
388
486
  bindListeners?: boolean;
487
+ connectors?: ConnectorRegistration[];
389
488
  }
390
489
  interface ProjectSlot {
391
490
  entry: RegistryEntry;
@@ -393,6 +492,8 @@ interface ProjectSlot {
393
492
  outPath: string;
394
493
  paths: ProjectPaths;
395
494
  stopPersist: () => void;
495
+ stopStaleness: () => void;
496
+ stopConnectors: () => void;
396
497
  detachEvents: () => void;
397
498
  status: 'active' | 'broken';
398
499
  errorReason?: string;
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  routeSpanToProject,
3
3
  startDaemon
4
- } from "./chunk-IABNGQT2.js";
4
+ } from "./chunk-XV4D7A3Z.js";
5
5
  import {
6
6
  ProjectNameCollisionError,
7
7
  addProject,
@@ -37,15 +37,15 @@ import {
37
37
  thresholdForEdgeType,
38
38
  touchLastSeen,
39
39
  writeAtomically
40
- } from "./chunk-O25KZNZK.js";
40
+ } from "./chunk-QM6BMPVJ.js";
41
41
  import {
42
42
  startOtelGrpcReceiver
43
- } from "./chunk-GAFTW2OX.js";
43
+ } from "./chunk-UV5WSM7M.js";
44
44
  import {
45
45
  buildOtelReceiver,
46
46
  logSpanHandler,
47
47
  parseOtlpRequest
48
- } from "./chunk-ZX7PCMGZ.js";
48
+ } from "./chunk-A3322JYS.js";
49
49
  export {
50
50
  ProjectNameCollisionError,
51
51
  addProject,