@weave_protocol/domere 1.0.0

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 (104) hide show
  1. package/PLANNING.md +231 -0
  2. package/README.md +50 -0
  3. package/dist/anchoring/ethereum.d.ts +135 -0
  4. package/dist/anchoring/ethereum.d.ts.map +1 -0
  5. package/dist/anchoring/ethereum.js +474 -0
  6. package/dist/anchoring/ethereum.js.map +1 -0
  7. package/dist/anchoring/index.d.ts +93 -0
  8. package/dist/anchoring/index.d.ts.map +1 -0
  9. package/dist/anchoring/index.js +184 -0
  10. package/dist/anchoring/index.js.map +1 -0
  11. package/dist/anchoring/merkle.d.ts +91 -0
  12. package/dist/anchoring/merkle.d.ts.map +1 -0
  13. package/dist/anchoring/merkle.js +203 -0
  14. package/dist/anchoring/merkle.js.map +1 -0
  15. package/dist/anchoring/solana.d.ts +85 -0
  16. package/dist/anchoring/solana.d.ts.map +1 -0
  17. package/dist/anchoring/solana.js +301 -0
  18. package/dist/anchoring/solana.js.map +1 -0
  19. package/dist/constants.d.ts +130 -0
  20. package/dist/constants.d.ts.map +1 -0
  21. package/dist/constants.js +536 -0
  22. package/dist/constants.js.map +1 -0
  23. package/dist/index.d.ts +13 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +37 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/language/code-analyzer.d.ts +80 -0
  28. package/dist/language/code-analyzer.d.ts.map +1 -0
  29. package/dist/language/code-analyzer.js +489 -0
  30. package/dist/language/code-analyzer.js.map +1 -0
  31. package/dist/language/detector.d.ts +53 -0
  32. package/dist/language/detector.d.ts.map +1 -0
  33. package/dist/language/detector.js +248 -0
  34. package/dist/language/detector.js.map +1 -0
  35. package/dist/language/index.d.ts +61 -0
  36. package/dist/language/index.d.ts.map +1 -0
  37. package/dist/language/index.js +109 -0
  38. package/dist/language/index.js.map +1 -0
  39. package/dist/language/nl-analyzer.d.ts +59 -0
  40. package/dist/language/nl-analyzer.d.ts.map +1 -0
  41. package/dist/language/nl-analyzer.js +350 -0
  42. package/dist/language/nl-analyzer.js.map +1 -0
  43. package/dist/language/semantic.d.ts +48 -0
  44. package/dist/language/semantic.d.ts.map +1 -0
  45. package/dist/language/semantic.js +329 -0
  46. package/dist/language/semantic.js.map +1 -0
  47. package/dist/storage/index.d.ts +6 -0
  48. package/dist/storage/index.d.ts.map +1 -0
  49. package/dist/storage/index.js +6 -0
  50. package/dist/storage/index.js.map +1 -0
  51. package/dist/storage/memory.d.ts +48 -0
  52. package/dist/storage/memory.d.ts.map +1 -0
  53. package/dist/storage/memory.js +211 -0
  54. package/dist/storage/memory.js.map +1 -0
  55. package/dist/thread/drift.d.ts +43 -0
  56. package/dist/thread/drift.d.ts.map +1 -0
  57. package/dist/thread/drift.js +248 -0
  58. package/dist/thread/drift.js.map +1 -0
  59. package/dist/thread/index.d.ts +9 -0
  60. package/dist/thread/index.d.ts.map +1 -0
  61. package/dist/thread/index.js +9 -0
  62. package/dist/thread/index.js.map +1 -0
  63. package/dist/thread/intent.d.ts +68 -0
  64. package/dist/thread/intent.d.ts.map +1 -0
  65. package/dist/thread/intent.js +333 -0
  66. package/dist/thread/intent.js.map +1 -0
  67. package/dist/thread/manager.d.ts +85 -0
  68. package/dist/thread/manager.d.ts.map +1 -0
  69. package/dist/thread/manager.js +305 -0
  70. package/dist/thread/manager.js.map +1 -0
  71. package/dist/thread/weave.d.ts +61 -0
  72. package/dist/thread/weave.d.ts.map +1 -0
  73. package/dist/thread/weave.js +158 -0
  74. package/dist/thread/weave.js.map +1 -0
  75. package/dist/tools/index.d.ts +18 -0
  76. package/dist/tools/index.d.ts.map +1 -0
  77. package/dist/tools/index.js +102 -0
  78. package/dist/tools/index.js.map +1 -0
  79. package/dist/types.d.ts +466 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +48 -0
  82. package/dist/types.js.map +1 -0
  83. package/package.json +24 -0
  84. package/src/anchoring/ethereum.ts +568 -0
  85. package/src/anchoring/index.ts +236 -0
  86. package/src/anchoring/merkle.ts +256 -0
  87. package/src/anchoring/solana.ts +370 -0
  88. package/src/constants.ts +566 -0
  89. package/src/index.ts +43 -0
  90. package/src/language/code-analyzer.ts +564 -0
  91. package/src/language/detector.ts +297 -0
  92. package/src/language/index.ts +129 -0
  93. package/src/language/nl-analyzer.ts +411 -0
  94. package/src/language/semantic.ts +385 -0
  95. package/src/storage/index.ts +6 -0
  96. package/src/storage/memory.ts +271 -0
  97. package/src/thread/drift.ts +319 -0
  98. package/src/thread/index.ts +9 -0
  99. package/src/thread/intent.ts +409 -0
  100. package/src/thread/manager.ts +414 -0
  101. package/src/thread/weave.ts +205 -0
  102. package/src/tools/index.ts +107 -0
  103. package/src/types.ts +736 -0
  104. package/tsconfig.json +19 -0
@@ -0,0 +1,385 @@
1
+ /**
2
+ * Dōmere - The Judge Protocol
3
+ * Semantic Analysis
4
+ */
5
+
6
+ import type {
7
+ SemanticAnalysis,
8
+ ExtractedEntity,
9
+ EntityType,
10
+ IntentClassification,
11
+ } from '../types.js';
12
+ import { INTENT_KEYWORDS, ENTITY_PATTERNS } from '../constants.js';
13
+
14
+ // ============================================================================
15
+ // Semantic Analyzer
16
+ // ============================================================================
17
+
18
+ export class SemanticAnalyzer {
19
+ /**
20
+ * Perform semantic analysis on content
21
+ */
22
+ analyze(content: string): SemanticAnalysis {
23
+ return {
24
+ intent_classification: this.classifyIntent(content),
25
+ entities: this.extractEntities(content),
26
+ actions_implied: this.extractImpliedActions(content),
27
+ topics: this.extractTopics(content),
28
+ sentiment: this.analyzeSentiment(content),
29
+ formality: this.analyzeFormality(content),
30
+ urgency: this.analyzeUrgency(content),
31
+ };
32
+ }
33
+
34
+ /**
35
+ * Classify the intent of the content
36
+ */
37
+ classifyIntent(content: string): IntentClassification {
38
+ const contentLower = content.toLowerCase();
39
+ const scores: Record<IntentClassification, number> = {
40
+ query: 0,
41
+ mutation: 0,
42
+ deletion: 0,
43
+ execution: 0,
44
+ communication: 0,
45
+ analysis: 0,
46
+ generation: 0,
47
+ unknown: 0,
48
+ };
49
+
50
+ // Score based on keywords
51
+ for (const [intent, keywords] of Object.entries(INTENT_KEYWORDS)) {
52
+ for (const keyword of keywords) {
53
+ const regex = new RegExp(`\\b${keyword}\\b`, 'gi');
54
+ const matches = contentLower.match(regex);
55
+ if (matches) {
56
+ scores[intent as IntentClassification] += matches.length;
57
+ }
58
+ }
59
+ }
60
+
61
+ // Find highest scoring intent
62
+ let maxIntent: IntentClassification = 'unknown';
63
+ let maxScore = 0;
64
+
65
+ for (const [intent, score] of Object.entries(scores)) {
66
+ if (score > maxScore) {
67
+ maxScore = score;
68
+ maxIntent = intent as IntentClassification;
69
+ }
70
+ }
71
+
72
+ // If no strong signal, check question patterns
73
+ if (maxScore < 2) {
74
+ if (/^(what|where|who|when|how|why|which|is|are|do|does|can|could|would|should)\b/i.test(content.trim())) {
75
+ return 'query';
76
+ }
77
+ }
78
+
79
+ return maxIntent;
80
+ }
81
+
82
+ /**
83
+ * Extract entities from content
84
+ */
85
+ extractEntities(content: string): ExtractedEntity[] {
86
+ const entities: ExtractedEntity[] = [];
87
+
88
+ // Extract using patterns
89
+ for (const [type, pattern] of Object.entries(ENTITY_PATTERNS)) {
90
+ const regex = new RegExp(pattern.source, pattern.flags);
91
+ let match;
92
+
93
+ while ((match = regex.exec(content)) !== null) {
94
+ entities.push({
95
+ type: type as EntityType,
96
+ value: match[0],
97
+ confidence: 0.9,
98
+ position: { start: match.index, end: match.index + match[0].length },
99
+ });
100
+ }
101
+ }
102
+
103
+ // Extract potential names (capitalized words not at start of sentence)
104
+ const namePattern = /(?<![.!?]\s)(?<!\n)\b([A-Z][a-z]+(?:\s+[A-Z][a-z]+)+)\b/g;
105
+ let match;
106
+ while ((match = namePattern.exec(content)) !== null) {
107
+ const value = match[1];
108
+ if (!this.isCommonPhrase(value)) {
109
+ entities.push({
110
+ type: 'person',
111
+ value,
112
+ confidence: 0.6,
113
+ position: { start: match.index, end: match.index + match[0].length },
114
+ });
115
+ }
116
+ }
117
+
118
+ // Extract database/table references
119
+ const dbPattern = /\b(database|table|collection|schema|db)\s*[`'"]?(\w+)[`'"]?/gi;
120
+ while ((match = dbPattern.exec(content)) !== null) {
121
+ entities.push({
122
+ type: match[1].toLowerCase() === 'table' ? 'table' : 'database',
123
+ value: match[2],
124
+ confidence: 0.85,
125
+ position: { start: match.index, end: match.index + match[0].length },
126
+ });
127
+ }
128
+
129
+ // Extract API endpoints
130
+ const apiPattern = /(?:api|endpoint|route):\s*([^\s,]+)|\/api\/[\w/]+/gi;
131
+ while ((match = apiPattern.exec(content)) !== null) {
132
+ entities.push({
133
+ type: 'api_endpoint',
134
+ value: match[1] || match[0],
135
+ confidence: 0.8,
136
+ position: { start: match.index, end: match.index + match[0].length },
137
+ });
138
+ }
139
+
140
+ // Remove duplicates
141
+ return this.deduplicateEntities(entities);
142
+ }
143
+
144
+ /**
145
+ * Extract implied actions from content
146
+ */
147
+ extractImpliedActions(content: string): string[] {
148
+ const actions: string[] = [];
149
+ const contentLower = content.toLowerCase();
150
+
151
+ // Look for verb phrases
152
+ const verbPatterns = [
153
+ /\b(will|should|must|need to|want to|going to|have to)\s+(\w+(?:\s+\w+)?)/gi,
154
+ /\b(please|kindly)?\s*(get|fetch|create|update|delete|send|run|execute|analyze|generate)\s+/gi,
155
+ /\b(i want|i need|i'd like)\s+(?:to\s+)?(\w+)/gi,
156
+ ];
157
+
158
+ for (const pattern of verbPatterns) {
159
+ let match;
160
+ while ((match = pattern.exec(contentLower)) !== null) {
161
+ const action = match[0].trim().replace(/^(please|kindly)\s*/i, '');
162
+ if (action.length > 3 && !actions.includes(action)) {
163
+ actions.push(action);
164
+ }
165
+ }
166
+ }
167
+
168
+ // Infer from intent keywords
169
+ for (const [intent, keywords] of Object.entries(INTENT_KEYWORDS)) {
170
+ for (const keyword of keywords) {
171
+ if (contentLower.includes(keyword) && !actions.some(a => a.includes(keyword))) {
172
+ actions.push(`${keyword} data/content`);
173
+ break;
174
+ }
175
+ }
176
+ }
177
+
178
+ return actions.slice(0, 10);
179
+ }
180
+
181
+ /**
182
+ * Extract topics from content
183
+ */
184
+ extractTopics(content: string): string[] {
185
+ const topics: string[] = [];
186
+ const contentLower = content.toLowerCase();
187
+
188
+ // Domain-specific topic detection
189
+ const topicPatterns: Record<string, RegExp[]> = {
190
+ 'sales': [/\b(sales|revenue|quota|deal|pipeline|forecast)\b/gi],
191
+ 'customer': [/\b(customer|client|account|user|subscriber)\b/gi],
192
+ 'finance': [/\b(finance|budget|expense|cost|profit|invoice|payment)\b/gi],
193
+ 'hr': [/\b(employee|staff|hire|recruit|payroll|benefits|performance)\b/gi],
194
+ 'marketing': [/\b(marketing|campaign|lead|conversion|engagement|brand)\b/gi],
195
+ 'product': [/\b(product|feature|release|roadmap|backlog|sprint)\b/gi],
196
+ 'engineering': [/\b(code|deploy|bug|fix|release|infrastructure|api)\b/gi],
197
+ 'security': [/\b(security|password|auth|permission|access|credential)\b/gi],
198
+ 'data': [/\b(data|database|query|report|analytics|metrics)\b/gi],
199
+ 'support': [/\b(support|ticket|issue|help|resolve|escalat)\b/gi],
200
+ };
201
+
202
+ for (const [topic, patterns] of Object.entries(topicPatterns)) {
203
+ for (const pattern of patterns) {
204
+ if (pattern.test(contentLower)) {
205
+ if (!topics.includes(topic)) {
206
+ topics.push(topic);
207
+ }
208
+ break;
209
+ }
210
+ }
211
+ }
212
+
213
+ return topics;
214
+ }
215
+
216
+ /**
217
+ * Analyze sentiment (-1 to 1)
218
+ */
219
+ analyzeSentiment(content: string): number {
220
+ const contentLower = content.toLowerCase();
221
+
222
+ const positiveWords = [
223
+ 'good', 'great', 'excellent', 'amazing', 'wonderful', 'fantastic', 'love',
224
+ 'like', 'happy', 'pleased', 'thank', 'thanks', 'appreciate', 'helpful',
225
+ 'perfect', 'awesome', 'brilliant', 'success', 'successful', 'best',
226
+ ];
227
+
228
+ const negativeWords = [
229
+ 'bad', 'terrible', 'awful', 'horrible', 'hate', 'dislike', 'angry',
230
+ 'frustrated', 'annoyed', 'disappointed', 'fail', 'failed', 'failure',
231
+ 'wrong', 'error', 'problem', 'issue', 'bug', 'broken', 'worst',
232
+ ];
233
+
234
+ let positiveCount = 0;
235
+ let negativeCount = 0;
236
+
237
+ for (const word of positiveWords) {
238
+ const regex = new RegExp(`\\b${word}\\b`, 'gi');
239
+ const matches = contentLower.match(regex);
240
+ if (matches) positiveCount += matches.length;
241
+ }
242
+
243
+ for (const word of negativeWords) {
244
+ const regex = new RegExp(`\\b${word}\\b`, 'gi');
245
+ const matches = contentLower.match(regex);
246
+ if (matches) negativeCount += matches.length;
247
+ }
248
+
249
+ // Check for negation
250
+ const negationPattern = /\b(not|no|never|don't|doesn't|didn't|won't|wouldn't|can't|couldn't)\s+\w+/gi;
251
+ const negations = contentLower.match(negationPattern)?.length || 0;
252
+
253
+ const total = positiveCount + negativeCount;
254
+ if (total === 0) return 0;
255
+
256
+ let sentiment = (positiveCount - negativeCount) / total;
257
+
258
+ // Adjust for negations
259
+ if (negations > 0) {
260
+ sentiment *= 0.5;
261
+ }
262
+
263
+ return Math.max(-1, Math.min(1, sentiment));
264
+ }
265
+
266
+ /**
267
+ * Analyze formality (0 to 1)
268
+ */
269
+ analyzeFormality(content: string): number {
270
+ const contentLower = content.toLowerCase();
271
+
272
+ // Informal indicators
273
+ const informalPatterns = [
274
+ /\b(gonna|wanna|gotta|kinda|sorta|y'all|ain't)\b/gi,
275
+ /\b(lol|lmao|omg|wtf|btw|fyi|imo|imho)\b/gi,
276
+ /!{2,}/g,
277
+ /\?{2,}/g,
278
+ /\.{3,}/g,
279
+ /\b(hey|hi|yo|sup)\b/gi,
280
+ ];
281
+
282
+ // Formal indicators
283
+ const formalPatterns = [
284
+ /\b(hereby|therefore|whereas|pursuant|accordingly)\b/gi,
285
+ /\b(please|kindly|respectfully|sincerely)\b/gi,
286
+ /\b(regarding|concerning|pertaining|with respect to)\b/gi,
287
+ /\b(Dear|Sir|Madam|Mr\.|Mrs\.|Ms\.|Dr\.)\b/g,
288
+ ];
289
+
290
+ let informalCount = 0;
291
+ let formalCount = 0;
292
+
293
+ for (const pattern of informalPatterns) {
294
+ const matches = contentLower.match(pattern);
295
+ if (matches) informalCount += matches.length;
296
+ }
297
+
298
+ for (const pattern of formalPatterns) {
299
+ const matches = content.match(pattern);
300
+ if (matches) formalCount += matches.length;
301
+ }
302
+
303
+ // Calculate average sentence length (longer = more formal)
304
+ const sentences = content.split(/[.!?]+/).filter(s => s.trim().length > 0);
305
+ const avgSentenceLength = sentences.length > 0
306
+ ? sentences.reduce((sum, s) => sum + s.split(/\s+/).length, 0) / sentences.length
307
+ : 0;
308
+
309
+ // Normalize
310
+ let formality = 0.5;
311
+
312
+ if (formalCount > 0 || informalCount > 0) {
313
+ formality += (formalCount - informalCount) * 0.1;
314
+ }
315
+
316
+ if (avgSentenceLength > 15) {
317
+ formality += 0.1;
318
+ } else if (avgSentenceLength < 8) {
319
+ formality -= 0.1;
320
+ }
321
+
322
+ return Math.max(0, Math.min(1, formality));
323
+ }
324
+
325
+ /**
326
+ * Analyze urgency (0 to 1)
327
+ */
328
+ analyzeUrgency(content: string): number {
329
+ const contentLower = content.toLowerCase();
330
+
331
+ const urgentPatterns = [
332
+ { pattern: /\b(urgent|asap|immediately|right now|right away)\b/gi, weight: 0.3 },
333
+ { pattern: /\b(critical|emergency|priority|important)\b/gi, weight: 0.2 },
334
+ { pattern: /\b(deadline|due|by end of day|eod|cob)\b/gi, weight: 0.15 },
335
+ { pattern: /\b(need|must|have to|required)\b/gi, weight: 0.1 },
336
+ { pattern: /!+/g, weight: 0.05 },
337
+ { pattern: /\b(please|help)\b/gi, weight: 0.05 },
338
+ ];
339
+
340
+ let urgency = 0;
341
+
342
+ for (const { pattern, weight } of urgentPatterns) {
343
+ const matches = contentLower.match(pattern);
344
+ if (matches) {
345
+ urgency += matches.length * weight;
346
+ }
347
+ }
348
+
349
+ return Math.min(1, urgency);
350
+ }
351
+
352
+ /**
353
+ * Check if a phrase is a common non-name phrase
354
+ */
355
+ private isCommonPhrase(phrase: string): boolean {
356
+ const commonPhrases = [
357
+ 'The', 'This', 'That', 'These', 'Those', 'What', 'When', 'Where', 'How',
358
+ 'New York', 'Los Angeles', 'San Francisco', 'United States', 'United Kingdom',
359
+ 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday',
360
+ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August',
361
+ 'September', 'October', 'November', 'December',
362
+ 'First', 'Second', 'Third', 'Last', 'Next', 'Previous',
363
+ ];
364
+
365
+ return commonPhrases.some(p => phrase.toLowerCase() === p.toLowerCase());
366
+ }
367
+
368
+ /**
369
+ * Deduplicate entities
370
+ */
371
+ private deduplicateEntities(entities: ExtractedEntity[]): ExtractedEntity[] {
372
+ const seen = new Set<string>();
373
+ const unique: ExtractedEntity[] = [];
374
+
375
+ for (const entity of entities) {
376
+ const key = `${entity.type}:${entity.value.toLowerCase()}`;
377
+ if (!seen.has(key)) {
378
+ seen.add(key);
379
+ unique.push(entity);
380
+ }
381
+ }
382
+
383
+ return unique;
384
+ }
385
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Dōmere - The Judge Protocol
3
+ * Storage Module
4
+ */
5
+
6
+ export { MemoryStorage, createStorage } from './memory.js';
@@ -0,0 +1,271 @@
1
+ /**
2
+ * Dōmere - The Judge Protocol
3
+ * Memory Storage Implementation
4
+ */
5
+
6
+ import type {
7
+ IDomereStorage,
8
+ Thread,
9
+ ThreadHop,
10
+ ThreadFilters,
11
+ ComplianceResult,
12
+ CompliancePolicy,
13
+ AnchorReference,
14
+ ArbitrationCase,
15
+ ArbitrationEvidence,
16
+ CaseFilters,
17
+ } from '../types.js';
18
+
19
+ // ============================================================================
20
+ // Memory Storage
21
+ // ============================================================================
22
+
23
+ export class MemoryStorage implements IDomereStorage {
24
+ private threads: Map<string, Thread> = new Map();
25
+ private hops: Map<string, ThreadHop[]> = new Map();
26
+ private complianceResults: Map<string, ComplianceResult[]> = new Map();
27
+ private policies: Map<string, CompliancePolicy> = new Map();
28
+ private anchors: Map<string, AnchorReference[]> = new Map();
29
+ private cases: Map<string, ArbitrationCase> = new Map();
30
+ private evidence: Map<string, ArbitrationEvidence[]> = new Map();
31
+
32
+ // ============================================================================
33
+ // Thread Operations
34
+ // ============================================================================
35
+
36
+ async saveThread(thread: Thread): Promise<void> {
37
+ this.threads.set(thread.id, { ...thread });
38
+ this.hops.set(thread.id, [...thread.hops]);
39
+ }
40
+
41
+ async getThread(id: string): Promise<Thread | null> {
42
+ const thread = this.threads.get(id);
43
+ if (!thread) return null;
44
+
45
+ return {
46
+ ...thread,
47
+ hops: this.hops.get(id) || [],
48
+ };
49
+ }
50
+
51
+ async updateThread(thread: Thread): Promise<void> {
52
+ this.threads.set(thread.id, { ...thread });
53
+ this.hops.set(thread.id, [...thread.hops]);
54
+ }
55
+
56
+ async listThreads(filters?: ThreadFilters): Promise<Thread[]> {
57
+ let threads = Array.from(this.threads.values());
58
+
59
+ if (filters) {
60
+ if (filters.status) {
61
+ threads = threads.filter(t => t.status === filters.status);
62
+ }
63
+ if (filters.origin_type) {
64
+ threads = threads.filter(t => t.origin.type === filters.origin_type);
65
+ }
66
+ if (filters.origin_identity) {
67
+ threads = threads.filter(t => t.origin.identity === filters.origin_identity);
68
+ }
69
+ if (filters.since) {
70
+ threads = threads.filter(t => t.created_at >= filters.since!);
71
+ }
72
+ if (filters.until) {
73
+ threads = threads.filter(t => t.created_at <= filters.until!);
74
+ }
75
+ }
76
+
77
+ // Sort by creation date (newest first)
78
+ threads.sort((a, b) => b.created_at.getTime() - a.created_at.getTime());
79
+
80
+ // Apply pagination
81
+ const offset = filters?.offset ?? 0;
82
+ const limit = filters?.limit ?? 100;
83
+
84
+ return threads.slice(offset, offset + limit).map(t => ({
85
+ ...t,
86
+ hops: this.hops.get(t.id) || [],
87
+ }));
88
+ }
89
+
90
+ // ============================================================================
91
+ // Hop Operations
92
+ // ============================================================================
93
+
94
+ async addHop(threadId: string, hop: ThreadHop): Promise<void> {
95
+ const hops = this.hops.get(threadId) || [];
96
+ hops.push({ ...hop });
97
+ this.hops.set(threadId, hops);
98
+ }
99
+
100
+ async getHops(threadId: string): Promise<ThreadHop[]> {
101
+ return [...(this.hops.get(threadId) || [])];
102
+ }
103
+
104
+ // ============================================================================
105
+ // Compliance Operations
106
+ // ============================================================================
107
+
108
+ async saveComplianceResult(result: ComplianceResult): Promise<void> {
109
+ const results = this.complianceResults.get(result.thread_id) || [];
110
+ results.push({ ...result });
111
+ this.complianceResults.set(result.thread_id, results);
112
+ }
113
+
114
+ async getComplianceResults(threadId: string): Promise<ComplianceResult[]> {
115
+ return [...(this.complianceResults.get(threadId) || [])];
116
+ }
117
+
118
+ // ============================================================================
119
+ // Policy Operations
120
+ // ============================================================================
121
+
122
+ async savePolicy(policy: CompliancePolicy): Promise<void> {
123
+ this.policies.set(policy.id, { ...policy });
124
+ }
125
+
126
+ async getPolicy(id: string): Promise<CompliancePolicy | null> {
127
+ const policy = this.policies.get(id);
128
+ return policy ? { ...policy } : null;
129
+ }
130
+
131
+ async listPolicies(): Promise<CompliancePolicy[]> {
132
+ return Array.from(this.policies.values()).map(p => ({ ...p }));
133
+ }
134
+
135
+ // ============================================================================
136
+ // Anchor Operations
137
+ // ============================================================================
138
+
139
+ async saveAnchor(threadId: string, anchor: AnchorReference): Promise<void> {
140
+ const anchors = this.anchors.get(threadId) || [];
141
+ anchors.push({ ...anchor });
142
+ this.anchors.set(threadId, anchors);
143
+ }
144
+
145
+ async getAnchors(threadId: string): Promise<AnchorReference[]> {
146
+ return [...(this.anchors.get(threadId) || [])];
147
+ }
148
+
149
+ // ============================================================================
150
+ // Arbitration Operations
151
+ // ============================================================================
152
+
153
+ async saveCase(case_: ArbitrationCase): Promise<void> {
154
+ this.cases.set(case_.id, { ...case_ });
155
+ }
156
+
157
+ async getCase(id: string): Promise<ArbitrationCase | null> {
158
+ const case_ = this.cases.get(id);
159
+ if (!case_) return null;
160
+
161
+ return {
162
+ ...case_,
163
+ evidence: this.evidence.get(id) || [],
164
+ };
165
+ }
166
+
167
+ async listCases(filters?: CaseFilters): Promise<ArbitrationCase[]> {
168
+ let cases = Array.from(this.cases.values());
169
+
170
+ if (filters) {
171
+ if (filters.status) {
172
+ cases = cases.filter(c => c.status === filters.status);
173
+ }
174
+ if (filters.dispute_type) {
175
+ cases = cases.filter(c => c.dispute.type === filters.dispute_type);
176
+ }
177
+ if (filters.thread_id) {
178
+ cases = cases.filter(c => c.thread_id === filters.thread_id);
179
+ }
180
+ }
181
+
182
+ // Sort by creation date (newest first)
183
+ cases.sort((a, b) => b.created_at.getTime() - a.created_at.getTime());
184
+
185
+ // Apply pagination
186
+ const offset = filters?.offset ?? 0;
187
+ const limit = filters?.limit ?? 100;
188
+
189
+ return cases.slice(offset, offset + limit).map(c => ({
190
+ ...c,
191
+ evidence: this.evidence.get(c.id) || [],
192
+ }));
193
+ }
194
+
195
+ // ============================================================================
196
+ // Evidence Operations
197
+ // ============================================================================
198
+
199
+ async saveEvidence(evidence: ArbitrationEvidence): Promise<void> {
200
+ const evidenceList = this.evidence.get(evidence.case_id) || [];
201
+ evidenceList.push({ ...evidence });
202
+ this.evidence.set(evidence.case_id, evidenceList);
203
+ }
204
+
205
+ async getEvidence(caseId: string): Promise<ArbitrationEvidence[]> {
206
+ return [...(this.evidence.get(caseId) || [])];
207
+ }
208
+
209
+ // ============================================================================
210
+ // Utility Operations
211
+ // ============================================================================
212
+
213
+ /**
214
+ * Clear all data
215
+ */
216
+ clear(): void {
217
+ this.threads.clear();
218
+ this.hops.clear();
219
+ this.complianceResults.clear();
220
+ this.policies.clear();
221
+ this.anchors.clear();
222
+ this.cases.clear();
223
+ this.evidence.clear();
224
+ }
225
+
226
+ /**
227
+ * Get statistics
228
+ */
229
+ getStats(): {
230
+ threads: number;
231
+ hops: number;
232
+ policies: number;
233
+ anchors: number;
234
+ cases: number;
235
+ } {
236
+ let totalHops = 0;
237
+ for (const hops of this.hops.values()) {
238
+ totalHops += hops.length;
239
+ }
240
+
241
+ let totalAnchors = 0;
242
+ for (const anchors of this.anchors.values()) {
243
+ totalAnchors += anchors.length;
244
+ }
245
+
246
+ return {
247
+ threads: this.threads.size,
248
+ hops: totalHops,
249
+ policies: this.policies.size,
250
+ anchors: totalAnchors,
251
+ cases: this.cases.size,
252
+ };
253
+ }
254
+ }
255
+
256
+ // ============================================================================
257
+ // Storage Factory
258
+ // ============================================================================
259
+
260
+ export function createStorage(type: 'memory' | 'sqlite' = 'memory'): IDomereStorage {
261
+ switch (type) {
262
+ case 'memory':
263
+ return new MemoryStorage();
264
+ case 'sqlite':
265
+ // TODO: Implement SQLite storage
266
+ console.warn('SQLite storage not implemented, falling back to memory');
267
+ return new MemoryStorage();
268
+ default:
269
+ return new MemoryStorage();
270
+ }
271
+ }