@grafema/types 0.2.5-beta → 0.2.7

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/src/rfdb.ts CHANGED
@@ -46,6 +46,7 @@ export type RFDBCommand =
46
46
  | 'datalogClearRules'
47
47
  | 'datalogQuery'
48
48
  | 'checkGuarantee'
49
+ | 'executeDatalog'
49
50
  // Protocol v2 - Multi-Database Commands
50
51
  | 'hello'
51
52
  | 'createDatabase'
@@ -55,7 +56,14 @@ export type RFDBCommand =
55
56
  | 'listDatabases'
56
57
  | 'currentDatabase'
57
58
  // Schema declaration
58
- | 'declareFields';
59
+ | 'declareFields'
60
+ // Snapshot operations
61
+ | 'diffSnapshots'
62
+ | 'tagSnapshot'
63
+ | 'findSnapshot'
64
+ | 'listSnapshots'
65
+ // Batch operations
66
+ | 'commitBatch';
59
67
 
60
68
  // === WIRE FORMAT ===
61
69
  // Nodes as sent over the wire
@@ -66,6 +74,7 @@ export interface WireNode {
66
74
  file: string;
67
75
  exported: boolean;
68
76
  metadata: string; // JSON string
77
+ semanticId?: string; // Protocol v3: human-readable semantic ID
69
78
  }
70
79
 
71
80
  // Edges as sent over the wire
@@ -79,6 +88,7 @@ export interface WireEdge {
79
88
  // === REQUEST TYPES ===
80
89
  export interface RFDBRequest {
81
90
  cmd: RFDBCommand;
91
+ requestId?: string;
82
92
  [key: string]: unknown;
83
93
  }
84
94
 
@@ -170,6 +180,7 @@ export interface CountEdgesByTypeRequest extends RFDBRequest {
170
180
 
171
181
  // === RESPONSE TYPES ===
172
182
  export interface RFDBResponse {
183
+ requestId?: string;
173
184
  error?: string;
174
185
  [key: string]: unknown;
175
186
  }
@@ -227,6 +238,52 @@ export interface PingResponse extends RFDBResponse {
227
238
  version: string;
228
239
  }
229
240
 
241
+ // === STREAMING RESPONSE TYPES ===
242
+
243
+ /**
244
+ * A chunk of nodes in a streaming QueryNodes response.
245
+ *
246
+ * Sent by the server when the result set exceeds the streaming threshold
247
+ * and the client negotiated protocol version >= 3.
248
+ * Multiple NodesChunk messages share the same requestId. The client
249
+ * accumulates chunks until `done === true`.
250
+ *
251
+ * Discrimination: if a response has a `done` field, it is a streaming chunk.
252
+ * If it does not, it is a legacy single-shot `Nodes { nodes }` response.
253
+ */
254
+ export interface NodesChunkResponse extends RFDBResponse {
255
+ nodes: WireNode[];
256
+ /** true = last chunk for this requestId; false = more chunks coming */
257
+ done: boolean;
258
+ /** 0-based chunk index for ordering verification */
259
+ chunkIndex: number;
260
+ }
261
+
262
+ // === BATCH OPERATIONS ===
263
+
264
+ export interface CommitDelta {
265
+ changedFiles: string[];
266
+ nodesAdded: number;
267
+ nodesRemoved: number;
268
+ edgesAdded: number;
269
+ edgesRemoved: number;
270
+ changedNodeTypes: string[];
271
+ changedEdgeTypes: string[];
272
+ }
273
+
274
+ export interface CommitBatchRequest extends RFDBRequest {
275
+ cmd: 'commitBatch';
276
+ changedFiles: string[];
277
+ nodes: WireNode[];
278
+ edges: WireEdge[];
279
+ tags?: string[];
280
+ }
281
+
282
+ export interface CommitBatchResponse extends RFDBResponse {
283
+ ok: boolean;
284
+ delta: CommitDelta;
285
+ }
286
+
230
287
  // === PROTOCOL V2 - MULTI-DATABASE RESPONSES ===
231
288
 
232
289
  export interface HelloResponse extends RFDBResponse {
@@ -274,6 +331,7 @@ export interface AttrQuery {
274
331
  name?: string;
275
332
  file?: string;
276
333
  exported?: boolean;
334
+ /** @deprecated Node-level version filter is legacy. In v2, use snapshot/tag APIs for history. */
277
335
  version?: string;
278
336
  /** Extra fields are matched against node metadata JSON (e.g. object, method, async) */
279
337
  [key: string]: string | boolean | number | undefined;
@@ -299,10 +357,94 @@ export interface DatalogResult {
299
357
  bindings: DatalogBinding;
300
358
  }
301
359
 
360
+ // === SNAPSHOT TYPES ===
361
+
362
+ /**
363
+ * Reference to a snapshot — either by version number or by tag key/value pair.
364
+ *
365
+ * When used as a number, refers to the snapshot at that version.
366
+ * When used as an object, resolves the snapshot tagged with the given key/value.
367
+ */
368
+ export type SnapshotRef = number | { tag: string; value: string };
369
+
370
+ /**
371
+ * Aggregate statistics for a snapshot (mirrors Rust ManifestStats).
372
+ *
373
+ * Wire format: camelCase (Rust snake_case fields mapped via serde rename).
374
+ */
375
+ export interface SnapshotStats {
376
+ totalNodes: number;
377
+ totalEdges: number;
378
+ nodeSegmentCount: number;
379
+ edgeSegmentCount: number;
380
+ }
381
+
382
+ /**
383
+ * Segment descriptor — describes a single data segment in a snapshot.
384
+ *
385
+ * Simplified view of Rust SegmentDescriptor. Exposes fields relevant to
386
+ * client-side diff analysis. Internal fields (segmentType, shardId) omitted.
387
+ *
388
+ * Wire format: camelCase. HashSet<String> serializes as string[].
389
+ */
390
+ export interface SegmentInfo {
391
+ segmentId: number;
392
+ recordCount: number;
393
+ byteSize: number;
394
+ nodeTypes: string[];
395
+ filePaths: string[];
396
+ edgeTypes: string[];
397
+ }
398
+
399
+ /**
400
+ * Diff between two snapshots (from -> to).
401
+ *
402
+ * Shows which segments were added/removed and stats for both versions.
403
+ * Mirrors Rust SnapshotDiff (storage_v2/manifest.rs).
404
+ */
405
+ export interface SnapshotDiff {
406
+ fromVersion: number;
407
+ toVersion: number;
408
+ addedNodeSegments: SegmentInfo[];
409
+ removedNodeSegments: SegmentInfo[];
410
+ addedEdgeSegments: SegmentInfo[];
411
+ removedEdgeSegments: SegmentInfo[];
412
+ statsFrom: SnapshotStats;
413
+ statsTo: SnapshotStats;
414
+ }
415
+
416
+ /**
417
+ * Lightweight snapshot information for list operations.
418
+ *
419
+ * Mirrors Rust SnapshotInfo (storage_v2/manifest.rs).
420
+ * createdAt is Unix epoch seconds (u64 in Rust).
421
+ */
422
+ export interface SnapshotInfo {
423
+ version: number;
424
+ createdAt: number;
425
+ tags: Record<string, string>;
426
+ stats: SnapshotStats;
427
+ }
428
+
429
+ // Snapshot response types
430
+
431
+ export interface DiffSnapshotsResponse extends RFDBResponse {
432
+ diff: SnapshotDiff;
433
+ }
434
+
435
+ export interface FindSnapshotResponse extends RFDBResponse {
436
+ version: number | null;
437
+ }
438
+
439
+ export interface ListSnapshotsResponse extends RFDBResponse {
440
+ snapshots: SnapshotInfo[];
441
+ }
442
+
302
443
  // === CLIENT INTERFACE ===
303
444
  export interface IRFDBClient {
304
445
  readonly socketPath: string;
305
446
  readonly connected: boolean;
447
+ readonly supportsStreaming: boolean;
306
448
 
307
449
  // Connection
308
450
  connect(): Promise<void>;
@@ -325,6 +467,7 @@ export interface IRFDBClient {
325
467
  findByType(nodeType: NodeType): Promise<string[]>;
326
468
  findByAttr(query: Record<string, unknown>): Promise<string[]>;
327
469
  queryNodes(query: AttrQuery): AsyncGenerator<WireNode, void, unknown>;
470
+ queryNodesStream(query: AttrQuery): AsyncGenerator<WireNode, void, unknown>;
328
471
  getAllNodes(query?: AttrQuery): Promise<WireNode[]>;
329
472
  getAllEdges(): Promise<WireEdge[]>;
330
473
  isEndpoint(id: string): Promise<boolean>;
@@ -353,6 +496,14 @@ export interface IRFDBClient {
353
496
  datalogClearRules(): Promise<RFDBResponse>;
354
497
  datalogQuery(query: string): Promise<DatalogResult[]>;
355
498
  checkGuarantee(ruleSource: string): Promise<DatalogResult[]>;
499
+ executeDatalog(source: string): Promise<DatalogResult[]>;
500
+
501
+ // Batch operations
502
+ beginBatch(): void;
503
+ commitBatch(tags?: string[]): Promise<CommitDelta>;
504
+ abortBatch(): void;
505
+ isBatching(): boolean;
506
+ findDependentFiles(changedFiles: string[]): Promise<string[]>;
356
507
 
357
508
  // Protocol v2 - Multi-Database
358
509
  hello(protocolVersion?: number): Promise<HelloResponse>;
@@ -362,4 +513,10 @@ export interface IRFDBClient {
362
513
  dropDatabase(name: string): Promise<RFDBResponse>;
363
514
  listDatabases(): Promise<ListDatabasesResponse>;
364
515
  currentDatabase(): Promise<CurrentDatabaseResponse>;
516
+
517
+ // Snapshot operations
518
+ diffSnapshots(from: SnapshotRef, to: SnapshotRef): Promise<SnapshotDiff>;
519
+ tagSnapshot(version: number, tags: Record<string, string>): Promise<void>;
520
+ findSnapshot(tagKey: string, tagValue: string): Promise<number | null>;
521
+ listSnapshots(filterTag?: string): Promise<SnapshotInfo[]>;
365
522
  }
package/src/routing.ts ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Routing Types — cross-service URL mapping for infrastructure-level routing.
3
+ *
4
+ * The RoutingMap is source-agnostic. It doesn't know where rules came from
5
+ * (config.yaml, nginx.conf, k8s manifests). It only knows:
6
+ * "request from service A with path P routes to service B with path P'"
7
+ */
8
+
9
+ import type { Resource } from './resources.js';
10
+
11
+ /**
12
+ * A routing rule describes how requests are transformed between services.
13
+ * Source-agnostic — can come from config.yaml, nginx.conf, k8s, etc.
14
+ */
15
+ export interface RoutingRule {
16
+ /** Service where requests originate (matches ServiceDefinition.name) */
17
+ from: string;
18
+ /** Service where routes are defined (matches ServiceDefinition.name) */
19
+ to: string;
20
+ /** Path prefix to strip from request URL before matching.
21
+ * e.g., stripPrefix: '/api' transforms '/api/users' -> '/users' */
22
+ stripPrefix?: string;
23
+ /** Path prefix to add to request URL before matching.
24
+ * e.g., addPrefix: '/v2' transforms '/users' -> '/v2/users' */
25
+ addPrefix?: string;
26
+ /** Source of this rule (for debugging/traceability) */
27
+ source?: string;
28
+ /** Priority — lower numbers match first. Default: 0 */
29
+ priority?: number;
30
+ }
31
+
32
+ /**
33
+ * Context for matching a request against the routing map.
34
+ */
35
+ export interface MatchContext {
36
+ /** Service name where the request originates */
37
+ fromService: string;
38
+ /** Original request URL (before any transformation) */
39
+ requestUrl: string;
40
+ /** HTTP method (optional, for future method-based routing) */
41
+ method?: string;
42
+ }
43
+
44
+ /**
45
+ * Result of a routing match — the transformed URL to use for matching.
46
+ */
47
+ export interface MatchResult {
48
+ /** Transformed URL to match against route paths */
49
+ transformedUrl: string;
50
+ /** Service name where the route should be found */
51
+ targetService: string;
52
+ /** The rule that matched */
53
+ rule: RoutingRule;
54
+ }
55
+
56
+ /**
57
+ * RoutingMap Resource — abstract routing table built by multiple builder plugins.
58
+ *
59
+ * Resource ID: 'routing:map'
60
+ */
61
+ export interface RoutingMap extends Resource {
62
+ readonly id: 'routing:map';
63
+
64
+ /**
65
+ * Add a routing rule. Called by builder plugins during ENRICHMENT phase.
66
+ * Duplicate rules (same from/to/strip/add) are silently deduplicated.
67
+ */
68
+ addRule(rule: RoutingRule): void;
69
+
70
+ /** Add multiple rules at once. */
71
+ addRules(rules: RoutingRule[]): void;
72
+
73
+ /**
74
+ * Find matching route transformation for a request context.
75
+ *
76
+ * If multiple rules match, returns the most specific one:
77
+ * 1. Rules with longer stripPrefix match first (more specific)
78
+ * 2. Among equal-length prefixes, lower priority number wins
79
+ * 3. If still tied, first-added wins
80
+ *
81
+ * @returns MatchResult if a rule matches, null if no rule applies
82
+ */
83
+ findMatch(context: MatchContext): MatchResult | null;
84
+
85
+ /** Find ALL matching rules for a from/to service pair. */
86
+ findRulesForPair(fromService: string, toService: string): RoutingRule[];
87
+
88
+ /** Get all rules (for debugging/logging). */
89
+ getAllRules(): RoutingRule[];
90
+
91
+ /** Get count of rules (for metrics). */
92
+ get ruleCount(): number;
93
+ }
94
+
95
+ /** Well-known Resource ID for the RoutingMap */
96
+ export const ROUTING_MAP_RESOURCE_ID = 'routing:map' as const;