trellis 2.0.13 → 2.1.2

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.
Files changed (96) hide show
  1. package/dist/cli/index.js +1 -1
  2. package/dist/embeddings/index.js +1 -1
  3. package/dist/{index-7gvjxt27.js → index-2917tjd8.js} +1 -1
  4. package/package.json +2 -10
  5. package/dist/transformers.node-bx3q9d7k.js +0 -33130
  6. package/src/cli/index.ts +0 -3356
  7. package/src/core/agents/harness.ts +0 -380
  8. package/src/core/agents/index.ts +0 -18
  9. package/src/core/agents/types.ts +0 -90
  10. package/src/core/index.ts +0 -118
  11. package/src/core/kernel/middleware.ts +0 -44
  12. package/src/core/kernel/trellis-kernel.ts +0 -593
  13. package/src/core/ontology/builtins.ts +0 -248
  14. package/src/core/ontology/index.ts +0 -34
  15. package/src/core/ontology/registry.ts +0 -209
  16. package/src/core/ontology/types.ts +0 -124
  17. package/src/core/ontology/validator.ts +0 -382
  18. package/src/core/persist/backend.ts +0 -74
  19. package/src/core/persist/sqlite-backend.ts +0 -298
  20. package/src/core/plugins/index.ts +0 -17
  21. package/src/core/plugins/registry.ts +0 -322
  22. package/src/core/plugins/types.ts +0 -126
  23. package/src/core/query/datalog.ts +0 -188
  24. package/src/core/query/engine.ts +0 -370
  25. package/src/core/query/index.ts +0 -34
  26. package/src/core/query/parser.ts +0 -481
  27. package/src/core/query/types.ts +0 -200
  28. package/src/core/store/eav-store.ts +0 -467
  29. package/src/decisions/auto-capture.ts +0 -136
  30. package/src/decisions/hooks.ts +0 -163
  31. package/src/decisions/index.ts +0 -261
  32. package/src/decisions/types.ts +0 -103
  33. package/src/embeddings/auto-embed.ts +0 -248
  34. package/src/embeddings/chunker.ts +0 -327
  35. package/src/embeddings/index.ts +0 -48
  36. package/src/embeddings/model.ts +0 -112
  37. package/src/embeddings/search.ts +0 -305
  38. package/src/embeddings/store.ts +0 -313
  39. package/src/embeddings/types.ts +0 -92
  40. package/src/engine.ts +0 -1125
  41. package/src/garden/cluster.ts +0 -330
  42. package/src/garden/garden.ts +0 -306
  43. package/src/garden/index.ts +0 -29
  44. package/src/git/git-exporter.ts +0 -286
  45. package/src/git/git-importer.ts +0 -329
  46. package/src/git/git-reader.ts +0 -189
  47. package/src/git/index.ts +0 -22
  48. package/src/identity/governance.ts +0 -211
  49. package/src/identity/identity.ts +0 -224
  50. package/src/identity/index.ts +0 -30
  51. package/src/identity/signing-middleware.ts +0 -97
  52. package/src/index.ts +0 -29
  53. package/src/links/index.ts +0 -49
  54. package/src/links/lifecycle.ts +0 -400
  55. package/src/links/parser.ts +0 -484
  56. package/src/links/ref-index.ts +0 -186
  57. package/src/links/resolver.ts +0 -314
  58. package/src/links/types.ts +0 -108
  59. package/src/mcp/index.ts +0 -22
  60. package/src/mcp/server.ts +0 -1278
  61. package/src/semantic/csharp-parser.ts +0 -493
  62. package/src/semantic/go-parser.ts +0 -585
  63. package/src/semantic/index.ts +0 -34
  64. package/src/semantic/java-parser.ts +0 -456
  65. package/src/semantic/python-parser.ts +0 -659
  66. package/src/semantic/ruby-parser.ts +0 -446
  67. package/src/semantic/rust-parser.ts +0 -784
  68. package/src/semantic/semantic-merge.ts +0 -210
  69. package/src/semantic/ts-parser.ts +0 -681
  70. package/src/semantic/types.ts +0 -175
  71. package/src/sync/http-transport.ts +0 -144
  72. package/src/sync/index.ts +0 -43
  73. package/src/sync/memory-transport.ts +0 -66
  74. package/src/sync/multi-repo.ts +0 -200
  75. package/src/sync/reconciler.ts +0 -237
  76. package/src/sync/sync-engine.ts +0 -258
  77. package/src/sync/types.ts +0 -104
  78. package/src/sync/ws-transport.ts +0 -145
  79. package/src/ui/client.html +0 -695
  80. package/src/ui/server.ts +0 -419
  81. package/src/vcs/blob-store.ts +0 -124
  82. package/src/vcs/branch.ts +0 -150
  83. package/src/vcs/checkpoint.ts +0 -64
  84. package/src/vcs/decompose.ts +0 -469
  85. package/src/vcs/diff.ts +0 -409
  86. package/src/vcs/engine-context.ts +0 -26
  87. package/src/vcs/index.ts +0 -23
  88. package/src/vcs/issue.ts +0 -800
  89. package/src/vcs/merge.ts +0 -425
  90. package/src/vcs/milestone.ts +0 -124
  91. package/src/vcs/ops.ts +0 -59
  92. package/src/vcs/types.ts +0 -213
  93. package/src/vcs/vcs-middleware.ts +0 -81
  94. package/src/watcher/fs-watcher.ts +0 -255
  95. package/src/watcher/index.ts +0 -9
  96. package/src/watcher/ingestion.ts +0 -116
@@ -1,34 +0,0 @@
1
- /**
2
- * EQL-S Query Module — Public API Surface
3
- *
4
- * @module trellis/core/query
5
- */
6
-
7
- // Types
8
- export type {
9
- Variable, Literal, Term,
10
- FactPattern, LinkPattern, NotPattern, OrPattern, RuleApplication, Pattern,
11
- FilterOp, Filter,
12
- AggregateOp, Aggregate,
13
- OrderBy,
14
- Query,
15
- Bindings,
16
- DatalogRule,
17
- } from './types.js';
18
-
19
- export { isVariable, isLiteral, variable, literal } from './types.js';
20
-
21
- // Engine
22
- export { QueryEngine } from './engine.js';
23
- export type { QueryResult } from './engine.js';
24
-
25
- // Parser
26
- export { parseQuery, parseRule, parseSimple } from './parser.js';
27
-
28
- // Datalog
29
- export {
30
- DatalogRuntime,
31
- transitiveClosureRules,
32
- reverseReachabilityRules,
33
- siblingRules,
34
- } from './datalog.js';
@@ -1,481 +0,0 @@
1
- /**
2
- * EQL-S Query Parser — Parses a simple DSL into Query AST.
3
- *
4
- * Syntax overview:
5
- * SELECT ?e ?name
6
- * WHERE {
7
- * [?e "type" "Project"]
8
- * [?e "name" ?name]
9
- * (?e "memberOf" ?org)
10
- * }
11
- * FILTER ?name != "archived"
12
- * ORDER BY ?name ASC
13
- * LIMIT 10
14
- * OFFSET 5
15
- *
16
- * Fact patterns: [entity attr value]
17
- * Link patterns: (source attr target)
18
- * Not patterns: NOT [entity attr value]
19
- * Or patterns: OR { branch1 } { branch2 }
20
- * Rule calls: ruleName(?x, ?y)
21
- *
22
- * Variables start with `?`. Strings are double-quoted.
23
- * Numbers are bare. Booleans: true/false.
24
- *
25
- * @module trellis/core/query
26
- */
27
-
28
- import type {
29
- Query, Pattern, FactPattern, LinkPattern, NotPattern, OrPattern,
30
- RuleApplication, Filter, FilterOp, Aggregate, AggregateOp, OrderBy,
31
- Term, DatalogRule,
32
- } from './types.js';
33
- import { variable, literal } from './types.js';
34
-
35
- // ---------------------------------------------------------------------------
36
- // Tokenizer
37
- // ---------------------------------------------------------------------------
38
-
39
- type TokenKind = 'word' | 'string' | 'number' | 'symbol' | 'eof';
40
-
41
- interface Token {
42
- kind: TokenKind;
43
- value: string;
44
- pos: number;
45
- }
46
-
47
- function tokenize(input: string): Token[] {
48
- const tokens: Token[] = [];
49
- let i = 0;
50
- while (i < input.length) {
51
- // Skip whitespace
52
- if (/\s/.test(input[i])) { i++; continue; }
53
-
54
- // Skip comments (// to end of line)
55
- if (input[i] === '/' && input[i + 1] === '/') {
56
- while (i < input.length && input[i] !== '\n') i++;
57
- continue;
58
- }
59
-
60
- const pos = i;
61
-
62
- // Symbols
63
- if ('[](){},:'.includes(input[i])) {
64
- tokens.push({ kind: 'symbol', value: input[i], pos });
65
- i++;
66
- continue;
67
- }
68
-
69
- // Multi-char operators
70
- if (input[i] === '!' && input[i + 1] === '=') {
71
- tokens.push({ kind: 'symbol', value: '!=', pos });
72
- i += 2;
73
- continue;
74
- }
75
- if (input[i] === '<' && input[i + 1] === '=') {
76
- tokens.push({ kind: 'symbol', value: '<=', pos });
77
- i += 2;
78
- continue;
79
- }
80
- if (input[i] === '>' && input[i + 1] === '=') {
81
- tokens.push({ kind: 'symbol', value: '>=', pos });
82
- i += 2;
83
- continue;
84
- }
85
- if ('<>='.includes(input[i])) {
86
- tokens.push({ kind: 'symbol', value: input[i], pos });
87
- i++;
88
- continue;
89
- }
90
-
91
- // Strings
92
- if (input[i] === '"') {
93
- i++;
94
- let s = '';
95
- while (i < input.length && input[i] !== '"') {
96
- if (input[i] === '\\' && i + 1 < input.length) {
97
- s += input[i + 1]; i += 2;
98
- } else {
99
- s += input[i]; i++;
100
- }
101
- }
102
- if (i < input.length) i++; // skip closing "
103
- tokens.push({ kind: 'string', value: s, pos });
104
- continue;
105
- }
106
-
107
- // Numbers (including negative)
108
- if (/[0-9]/.test(input[i]) || (input[i] === '-' && i + 1 < input.length && /[0-9]/.test(input[i + 1]))) {
109
- let n = input[i]; i++;
110
- while (i < input.length && /[0-9.]/.test(input[i])) { n += input[i]; i++; }
111
- tokens.push({ kind: 'number', value: n, pos });
112
- continue;
113
- }
114
-
115
- // Words (identifiers, variables, keywords)
116
- if (/[?a-zA-Z_]/.test(input[i])) {
117
- let w = '';
118
- while (i < input.length && /[?a-zA-Z0-9_.:/-]/.test(input[i])) { w += input[i]; i++; }
119
- tokens.push({ kind: 'word', value: w, pos });
120
- continue;
121
- }
122
-
123
- // Unknown char — skip
124
- i++;
125
- }
126
-
127
- tokens.push({ kind: 'eof', value: '', pos: input.length });
128
- return tokens;
129
- }
130
-
131
- // ---------------------------------------------------------------------------
132
- // Parser
133
- // ---------------------------------------------------------------------------
134
-
135
- class Parser {
136
- private tokens: Token[];
137
- private pos = 0;
138
-
139
- constructor(tokens: Token[]) {
140
- this.tokens = tokens;
141
- }
142
-
143
- private peek(): Token { return this.tokens[this.pos]; }
144
- private advance(): Token { return this.tokens[this.pos++]; }
145
-
146
- private expect(kind: TokenKind, value?: string): Token {
147
- const t = this.advance();
148
- if (t.kind !== kind || (value !== undefined && t.value !== value)) {
149
- throw new Error(`Expected ${kind}${value ? ` "${value}"` : ''} at pos ${t.pos}, got ${t.kind} "${t.value}"`);
150
- }
151
- return t;
152
- }
153
-
154
- private match(kind: TokenKind, value?: string): boolean {
155
- const t = this.peek();
156
- if (t.kind === kind && (value === undefined || t.value === value)) {
157
- this.pos++;
158
- return true;
159
- }
160
- return false;
161
- }
162
-
163
- private isAt(kind: TokenKind, value?: string): boolean {
164
- const t = this.peek();
165
- return t.kind === kind && (value === undefined || t.value === value);
166
- }
167
-
168
- // -----------------------------------------------------------------------
169
- // Terms
170
- // -----------------------------------------------------------------------
171
-
172
- parseTerm(): Term {
173
- const t = this.peek();
174
- if (t.kind === 'word' && t.value.startsWith('?')) {
175
- this.advance();
176
- return variable(t.value.slice(1));
177
- }
178
- if (t.kind === 'string') {
179
- this.advance();
180
- return literal(t.value);
181
- }
182
- if (t.kind === 'number') {
183
- this.advance();
184
- const n = Number(t.value);
185
- return literal(n);
186
- }
187
- if (t.kind === 'word') {
188
- const v = t.value;
189
- this.advance();
190
- if (v === 'true') return literal(true);
191
- if (v === 'false') return literal(false);
192
- return literal(v);
193
- }
194
- throw new Error(`Unexpected token at pos ${t.pos}: ${t.kind} "${t.value}"`);
195
- }
196
-
197
- // -----------------------------------------------------------------------
198
- // Patterns
199
- // -----------------------------------------------------------------------
200
-
201
- parsePattern(): Pattern {
202
- const t = this.peek();
203
-
204
- // NOT pattern
205
- if (t.kind === 'word' && t.value.toUpperCase() === 'NOT') {
206
- this.advance();
207
- const inner = this.parsePattern();
208
- return { kind: 'not', pattern: inner } as NotPattern;
209
- }
210
-
211
- // OR pattern
212
- if (t.kind === 'word' && t.value.toUpperCase() === 'OR') {
213
- this.advance();
214
- const branches: Pattern[][] = [];
215
- while (this.isAt('symbol', '{')) {
216
- this.advance();
217
- const branch: Pattern[] = [];
218
- while (!this.isAt('symbol', '}') && !this.isAt('eof', undefined)) {
219
- branch.push(this.parsePattern());
220
- }
221
- this.expect('symbol', '}');
222
- branches.push(branch);
223
- }
224
- return { kind: 'or', branches } as OrPattern;
225
- }
226
-
227
- // Fact pattern: [e a v]
228
- if (t.kind === 'symbol' && t.value === '[') {
229
- this.advance();
230
- const entity = this.parseTerm();
231
- const attribute = this.parseTerm();
232
- const value = this.parseTerm();
233
- this.expect('symbol', ']');
234
- return { kind: 'fact', entity, attribute, value } as FactPattern;
235
- }
236
-
237
- // Link pattern: (src a tgt)
238
- if (t.kind === 'symbol' && t.value === '(') {
239
- this.advance();
240
- const source = this.parseTerm();
241
- const attribute = this.parseTerm();
242
- const target = this.parseTerm();
243
- this.expect('symbol', ')');
244
- return { kind: 'link', source, attribute, target } as LinkPattern;
245
- }
246
-
247
- // Rule application: ruleName(?x, ?y)
248
- if (t.kind === 'word' && !t.value.startsWith('?')) {
249
- const name = this.advance().value;
250
- if (this.isAt('symbol', '(')) {
251
- this.advance();
252
- const args: Term[] = [];
253
- while (!this.isAt('symbol', ')') && !this.isAt('eof', undefined)) {
254
- args.push(this.parseTerm());
255
- this.match('symbol', ',');
256
- }
257
- this.expect('symbol', ')');
258
- return { kind: 'rule', name, args } as RuleApplication;
259
- }
260
- throw new Error(`Expected '(' after rule name "${name}" at pos ${t.pos}`);
261
- }
262
-
263
- throw new Error(`Cannot parse pattern at pos ${t.pos}: ${t.kind} "${t.value}"`);
264
- }
265
-
266
- // -----------------------------------------------------------------------
267
- // Filter
268
- // -----------------------------------------------------------------------
269
-
270
- parseFilter(): Filter {
271
- const left = this.parseTerm();
272
- const op = this.advance().value as FilterOp;
273
- const right = this.parseTerm();
274
- return { kind: 'filter', left, op, right };
275
- }
276
-
277
- // -----------------------------------------------------------------------
278
- // Full Query
279
- // -----------------------------------------------------------------------
280
-
281
- parseQuery(): Query {
282
- const query: Query = {
283
- select: [],
284
- where: [],
285
- filters: [],
286
- aggregates: [],
287
- orderBy: [],
288
- limit: 0,
289
- offset: 0,
290
- };
291
-
292
- // Parse clauses in any order
293
- while (!this.isAt('eof', undefined)) {
294
- const kw = this.peek();
295
- if (kw.kind !== 'word') {
296
- throw new Error(`Expected keyword at pos ${kw.pos}, got ${kw.kind} "${kw.value}"`);
297
- }
298
-
299
- switch (kw.value.toUpperCase()) {
300
- case 'SELECT': {
301
- this.advance();
302
- while (this.peek().kind === 'word' && this.peek().value.startsWith('?')) {
303
- query.select.push(this.advance().value.slice(1));
304
- }
305
- break;
306
- }
307
-
308
- case 'WHERE': {
309
- this.advance();
310
- this.expect('symbol', '{');
311
- while (!this.isAt('symbol', '}') && !this.isAt('eof', undefined)) {
312
- query.where.push(this.parsePattern());
313
- }
314
- this.expect('symbol', '}');
315
- break;
316
- }
317
-
318
- case 'FILTER': {
319
- this.advance();
320
- query.filters.push(this.parseFilter());
321
- break;
322
- }
323
-
324
- case 'AGGREGATE': {
325
- this.advance();
326
- const op = this.advance().value as AggregateOp;
327
- this.expect('symbol', '(');
328
- const varName = this.advance().value;
329
- const varClean = varName.startsWith('?') ? varName.slice(1) : varName;
330
- this.expect('symbol', ')');
331
- this.expect('word', 'AS');
332
- const asName = this.advance().value;
333
- const asClean = asName.startsWith('?') ? asName.slice(1) : asName;
334
- query.aggregates.push({ op, variable: varClean, as: asClean });
335
- break;
336
- }
337
-
338
- case 'ORDER': {
339
- this.advance();
340
- this.expect('word', 'BY');
341
- while (this.peek().kind === 'word' && this.peek().value.startsWith('?')) {
342
- const v = this.advance().value.slice(1);
343
- let dir: 'asc' | 'desc' = 'asc';
344
- if (this.peek().kind === 'word' && ['ASC', 'DESC'].includes(this.peek().value.toUpperCase())) {
345
- dir = this.advance().value.toLowerCase() as 'asc' | 'desc';
346
- }
347
- query.orderBy.push({ variable: v, direction: dir });
348
- }
349
- break;
350
- }
351
-
352
- case 'LIMIT': {
353
- this.advance();
354
- query.limit = Number(this.expect('number').value);
355
- break;
356
- }
357
-
358
- case 'OFFSET': {
359
- this.advance();
360
- query.offset = Number(this.expect('number').value);
361
- break;
362
- }
363
-
364
- default:
365
- throw new Error(`Unknown keyword "${kw.value}" at pos ${kw.pos}`);
366
- }
367
- }
368
-
369
- return query;
370
- }
371
-
372
- // -----------------------------------------------------------------------
373
- // Datalog Rule
374
- // -----------------------------------------------------------------------
375
-
376
- parseRule(): DatalogRule {
377
- // name(?x, ?y) :- body
378
- const name = this.expect('word').value;
379
- this.expect('symbol', '(');
380
- const params: string[] = [];
381
- while (!this.isAt('symbol', ')') && !this.isAt('eof', undefined)) {
382
- const v = this.expect('word').value;
383
- params.push(v.startsWith('?') ? v.slice(1) : v);
384
- this.match('symbol', ',');
385
- }
386
- this.expect('symbol', ')');
387
-
388
- // :- separator (colon + minus as two tokens, or we accept ":-" as word)
389
- if (this.isAt('symbol', ':')) {
390
- this.advance();
391
- // Accept - as a word or skip
392
- if (this.peek().kind === 'number' && this.peek().value.startsWith('-')) {
393
- this.advance();
394
- }
395
- } else if (this.isAt('word', ':-')) {
396
- this.advance();
397
- }
398
-
399
- const body: Pattern[] = [];
400
- const filters: Filter[] = [];
401
-
402
- while (!this.isAt('eof', undefined)) {
403
- if (this.peek().kind === 'word' && this.peek().value.toUpperCase() === 'FILTER') {
404
- this.advance();
405
- filters.push(this.parseFilter());
406
- } else {
407
- body.push(this.parsePattern());
408
- }
409
- this.match('symbol', ',');
410
- }
411
-
412
- return { name, params, body, filters };
413
- }
414
- }
415
-
416
- // ---------------------------------------------------------------------------
417
- // Public API
418
- // ---------------------------------------------------------------------------
419
-
420
- export function parseQuery(input: string): Query {
421
- const tokens = tokenize(input);
422
- return new Parser(tokens).parseQuery();
423
- }
424
-
425
- export function parseRule(input: string): DatalogRule {
426
- const tokens = tokenize(input);
427
- return new Parser(tokens).parseRule();
428
- }
429
-
430
- /**
431
- * Shorthand: parse a simple "find entities where" query.
432
- *
433
- * Example: `find ?e where type = "Project"`
434
- * Becomes: SELECT ?e WHERE { [?e "type" "Project"] }
435
- */
436
- export function parseSimple(input: string): Query {
437
- const trimmed = input.trim();
438
-
439
- // Try to detect if it's already a full query (starts with SELECT/WHERE)
440
- const upper = trimmed.toUpperCase();
441
- if (upper.startsWith('SELECT') || upper.startsWith('WHERE')) {
442
- return parseQuery(trimmed);
443
- }
444
-
445
- // Simple format: find ?vars where attr op value [and attr op value]*
446
- const findMatch = trimmed.match(/^find\s+(.+?)\s+where\s+(.+)$/i);
447
- if (findMatch) {
448
- const vars = findMatch[1].trim().split(/\s+/);
449
- const conditions = findMatch[2].trim();
450
-
451
- const selectVars = vars.map((v) => v.startsWith('?') ? v : `?${v}`);
452
- const entity = selectVars[0];
453
-
454
- // Parse conditions: "attr op value [and attr op value]*"
455
- const parts = conditions.split(/\s+and\s+/i);
456
- const patterns: string[] = [];
457
- const filters: string[] = [];
458
-
459
- for (const part of parts) {
460
- const eqMatch = part.match(/^(\S+)\s*(=|!=|<|<=|>|>=|contains|startsWith|endsWith|matches)\s*(.+)$/);
461
- if (eqMatch) {
462
- const [, attr, op, val] = eqMatch;
463
- const valTrimmed = val.trim();
464
- if (op === '=') {
465
- // Direct fact pattern
466
- patterns.push(`[${entity} "${attr}" ${valTrimmed}]`);
467
- } else {
468
- // Need a variable + filter
469
- const tmpVar = `?_${attr.replace(/[^a-zA-Z0-9]/g, '_')}`;
470
- patterns.push(`[${entity} "${attr}" ${tmpVar}]`);
471
- filters.push(`FILTER ${tmpVar} ${op} ${valTrimmed}`);
472
- }
473
- }
474
- }
475
-
476
- const fullQuery = `SELECT ${selectVars.join(' ')}\nWHERE {\n ${patterns.join('\n ')}\n}\n${filters.join('\n')}`;
477
- return parseQuery(fullQuery);
478
- }
479
-
480
- throw new Error(`Cannot parse query: "${trimmed}". Use full EQL-S syntax or "find ?e where attr = value".`);
481
- }
@@ -1,200 +0,0 @@
1
- /**
2
- * EQL-S Query Types — Entity Query Language (Structured)
3
- *
4
- * Defines the AST for structured queries over the EAV store.
5
- * Queries are composed of patterns (fact/link clauses) that bind
6
- * variables, plus optional filters, aggregations, and projections.
7
- *
8
- * @module trellis/core/query
9
- */
10
-
11
- import type { Atom } from '../store/eav-store.js';
12
-
13
- // ---------------------------------------------------------------------------
14
- // Variables & Terms
15
- // ---------------------------------------------------------------------------
16
-
17
- /**
18
- * A query variable, prefixed with `?` in the DSL.
19
- * e.g. `?e`, `?name`, `?type`
20
- */
21
- export interface Variable {
22
- kind: 'variable';
23
- name: string;
24
- }
25
-
26
- /**
27
- * A literal constant value.
28
- */
29
- export interface Literal {
30
- kind: 'literal';
31
- value: Atom;
32
- }
33
-
34
- /**
35
- * A term is either a variable or a literal.
36
- */
37
- export type Term = Variable | Literal;
38
-
39
- // ---------------------------------------------------------------------------
40
- // Patterns (clauses)
41
- // ---------------------------------------------------------------------------
42
-
43
- /**
44
- * A fact pattern matches triples (e, a, v) in the EAV store.
45
- *
46
- * Example DSL: `[?e "type" "Project"]` or `[?e ?attr ?val]`
47
- */
48
- export interface FactPattern {
49
- kind: 'fact';
50
- entity: Term;
51
- attribute: Term;
52
- value: Term;
53
- }
54
-
55
- /**
56
- * A link pattern matches links (e1, a, e2) in the graph.
57
- *
58
- * Example DSL: `(?src "memberOf" ?tgt)`
59
- */
60
- export interface LinkPattern {
61
- kind: 'link';
62
- source: Term;
63
- attribute: Term;
64
- target: Term;
65
- }
66
-
67
- /**
68
- * A negation pattern — succeeds when the inner pattern has NO matches.
69
- */
70
- export interface NotPattern {
71
- kind: 'not';
72
- pattern: Pattern;
73
- }
74
-
75
- /**
76
- * An or-pattern — succeeds when ANY branch matches.
77
- */
78
- export interface OrPattern {
79
- kind: 'or';
80
- branches: Pattern[][];
81
- }
82
-
83
- /**
84
- * A rule application — invoke a named Datalog rule.
85
- */
86
- export interface RuleApplication {
87
- kind: 'rule';
88
- name: string;
89
- args: Term[];
90
- }
91
-
92
- export type Pattern = FactPattern | LinkPattern | NotPattern | OrPattern | RuleApplication;
93
-
94
- // ---------------------------------------------------------------------------
95
- // Filters
96
- // ---------------------------------------------------------------------------
97
-
98
- export type FilterOp = '=' | '!=' | '<' | '<=' | '>' | '>=' | 'contains' | 'startsWith' | 'endsWith' | 'matches';
99
-
100
- export interface Filter {
101
- kind: 'filter';
102
- left: Term;
103
- op: FilterOp;
104
- right: Term;
105
- }
106
-
107
- // ---------------------------------------------------------------------------
108
- // Aggregation
109
- // ---------------------------------------------------------------------------
110
-
111
- export type AggregateOp = 'count' | 'sum' | 'avg' | 'min' | 'max' | 'collect';
112
-
113
- export interface Aggregate {
114
- op: AggregateOp;
115
- /** Variable to aggregate over. */
116
- variable: string;
117
- /** Output variable name. */
118
- as: string;
119
- }
120
-
121
- // ---------------------------------------------------------------------------
122
- // Ordering
123
- // ---------------------------------------------------------------------------
124
-
125
- export interface OrderBy {
126
- variable: string;
127
- direction: 'asc' | 'desc';
128
- }
129
-
130
- // ---------------------------------------------------------------------------
131
- // Query
132
- // ---------------------------------------------------------------------------
133
-
134
- /**
135
- * A complete EQL-S query.
136
- */
137
- export interface Query {
138
- /** Variables to project in the result. Empty = return all bound variables. */
139
- select: string[];
140
- /** Pattern clauses that must all match (conjunction). */
141
- where: Pattern[];
142
- /** Post-match filters. */
143
- filters: Filter[];
144
- /** Aggregation functions. */
145
- aggregates: Aggregate[];
146
- /** Ordering. */
147
- orderBy: OrderBy[];
148
- /** Maximum number of results (0 = unlimited). */
149
- limit: number;
150
- /** Number of results to skip. */
151
- offset: number;
152
- }
153
-
154
- // ---------------------------------------------------------------------------
155
- // Bindings
156
- // ---------------------------------------------------------------------------
157
-
158
- /**
159
- * A single set of variable bindings.
160
- */
161
- export type Bindings = Map<string, Atom>;
162
-
163
- // ---------------------------------------------------------------------------
164
- // Datalog Rules
165
- // ---------------------------------------------------------------------------
166
-
167
- /**
168
- * A Datalog rule: `head :- body`.
169
- *
170
- * Example: `ancestor(?x, ?z) :- [?x "parent" ?y], ancestor(?y, ?z)`
171
- */
172
- export interface DatalogRule {
173
- name: string;
174
- /** Parameter variable names for the head. */
175
- params: string[];
176
- /** Body patterns (conjunction). */
177
- body: Pattern[];
178
- /** Body filters. */
179
- filters: Filter[];
180
- }
181
-
182
- // ---------------------------------------------------------------------------
183
- // Helpers
184
- // ---------------------------------------------------------------------------
185
-
186
- export function isVariable(t: Term): t is Variable {
187
- return t.kind === 'variable';
188
- }
189
-
190
- export function isLiteral(t: Term): t is Literal {
191
- return t.kind === 'literal';
192
- }
193
-
194
- export function variable(name: string): Variable {
195
- return { kind: 'variable', name };
196
- }
197
-
198
- export function literal(value: Atom): Literal {
199
- return { kind: 'literal', value };
200
- }