@jamesaphoenix/tx-core 0.4.1
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/db.d.ts +42 -0
- package/dist/db.d.ts.map +1 -0
- package/dist/db.js +46 -0
- package/dist/db.js.map +1 -0
- package/dist/errors.d.ts +231 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +139 -0
- package/dist/errors.js.map +1 -0
- package/dist/id.d.ts +6 -0
- package/dist/id.d.ts.map +1 -0
- package/dist/id.js +21 -0
- package/dist/id.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/layer.d.ts +50 -0
- package/dist/layer.d.ts.map +1 -0
- package/dist/layer.js +155 -0
- package/dist/layer.js.map +1 -0
- package/dist/mappers/anchor.d.ts +14 -0
- package/dist/mappers/anchor.d.ts.map +1 -0
- package/dist/mappers/anchor.js +38 -0
- package/dist/mappers/anchor.js.map +1 -0
- package/dist/mappers/attempt.d.ts +15 -0
- package/dist/mappers/attempt.d.ts.map +1 -0
- package/dist/mappers/attempt.js +23 -0
- package/dist/mappers/attempt.js.map +1 -0
- package/dist/mappers/candidate.d.ts +23 -0
- package/dist/mappers/candidate.d.ts.map +1 -0
- package/dist/mappers/candidate.js +53 -0
- package/dist/mappers/candidate.js.map +1 -0
- package/dist/mappers/claim.d.ts +30 -0
- package/dist/mappers/claim.d.ts.map +1 -0
- package/dist/mappers/claim.js +32 -0
- package/dist/mappers/claim.js.map +1 -0
- package/dist/mappers/deduplication.d.ts +39 -0
- package/dist/mappers/deduplication.d.ts.map +1 -0
- package/dist/mappers/deduplication.js +53 -0
- package/dist/mappers/deduplication.js.map +1 -0
- package/dist/mappers/edge.d.ts +10 -0
- package/dist/mappers/edge.d.ts.map +1 -0
- package/dist/mappers/edge.js +19 -0
- package/dist/mappers/edge.js.map +1 -0
- package/dist/mappers/file-learning.d.ts +14 -0
- package/dist/mappers/file-learning.d.ts.map +1 -0
- package/dist/mappers/file-learning.js +75 -0
- package/dist/mappers/file-learning.js.map +1 -0
- package/dist/mappers/index.d.ts +17 -0
- package/dist/mappers/index.d.ts.map +1 -0
- package/dist/mappers/index.js +30 -0
- package/dist/mappers/index.js.map +1 -0
- package/dist/mappers/learning.d.ts +19 -0
- package/dist/mappers/learning.d.ts.map +1 -0
- package/dist/mappers/learning.js +41 -0
- package/dist/mappers/learning.js.map +1 -0
- package/dist/mappers/orchestrator-state.d.ts +33 -0
- package/dist/mappers/orchestrator-state.d.ts.map +1 -0
- package/dist/mappers/orchestrator-state.js +34 -0
- package/dist/mappers/orchestrator-state.js.map +1 -0
- package/dist/mappers/run.d.ts +32 -0
- package/dist/mappers/run.d.ts.map +1 -0
- package/dist/mappers/run.js +64 -0
- package/dist/mappers/run.js.map +1 -0
- package/dist/mappers/task.d.ts +23 -0
- package/dist/mappers/task.d.ts.map +1 -0
- package/dist/mappers/task.js +54 -0
- package/dist/mappers/task.js.map +1 -0
- package/dist/mappers/tracked-project.d.ts +15 -0
- package/dist/mappers/tracked-project.d.ts.map +1 -0
- package/dist/mappers/tracked-project.js +23 -0
- package/dist/mappers/tracked-project.js.map +1 -0
- package/dist/mappers/worker.d.ts +33 -0
- package/dist/mappers/worker.d.ts.map +1 -0
- package/dist/mappers/worker.js +35 -0
- package/dist/mappers/worker.js.map +1 -0
- package/dist/repo/anchor-repo.d.ts +52 -0
- package/dist/repo/anchor-repo.d.ts.map +1 -0
- package/dist/repo/anchor-repo.js +204 -0
- package/dist/repo/anchor-repo.js.map +1 -0
- package/dist/repo/attempt-repo.d.ts +25 -0
- package/dist/repo/attempt-repo.d.ts.map +1 -0
- package/dist/repo/attempt-repo.js +78 -0
- package/dist/repo/attempt-repo.js.map +1 -0
- package/dist/repo/candidate-repo.d.ts +16 -0
- package/dist/repo/candidate-repo.d.ts.map +1 -0
- package/dist/repo/candidate-repo.js +143 -0
- package/dist/repo/candidate-repo.js.map +1 -0
- package/dist/repo/claim-repo.d.ts +17 -0
- package/dist/repo/claim-repo.d.ts.map +1 -0
- package/dist/repo/claim-repo.js +62 -0
- package/dist/repo/claim-repo.js.map +1 -0
- package/dist/repo/deduplication-repo.d.ts +37 -0
- package/dist/repo/deduplication-repo.d.ts.map +1 -0
- package/dist/repo/deduplication-repo.js +133 -0
- package/dist/repo/deduplication-repo.js.map +1 -0
- package/dist/repo/dep-repo.d.ts +19 -0
- package/dist/repo/dep-repo.d.ts.map +1 -0
- package/dist/repo/dep-repo.js +104 -0
- package/dist/repo/dep-repo.js.map +1 -0
- package/dist/repo/edge-repo.d.ts +26 -0
- package/dist/repo/edge-repo.d.ts.map +1 -0
- package/dist/repo/edge-repo.js +227 -0
- package/dist/repo/edge-repo.js.map +1 -0
- package/dist/repo/file-learning-repo.d.ts +17 -0
- package/dist/repo/file-learning-repo.d.ts.map +1 -0
- package/dist/repo/file-learning-repo.js +60 -0
- package/dist/repo/file-learning-repo.js.map +1 -0
- package/dist/repo/index.d.ts +18 -0
- package/dist/repo/index.d.ts.map +1 -0
- package/dist/repo/index.js +18 -0
- package/dist/repo/index.js.map +1 -0
- package/dist/repo/learning-repo.d.ts +31 -0
- package/dist/repo/learning-repo.d.ts.map +1 -0
- package/dist/repo/learning-repo.js +165 -0
- package/dist/repo/learning-repo.js.map +1 -0
- package/dist/repo/orchestrator-state-repo.d.ts +27 -0
- package/dist/repo/orchestrator-state-repo.d.ts.map +1 -0
- package/dist/repo/orchestrator-state-repo.js +96 -0
- package/dist/repo/orchestrator-state-repo.js.map +1 -0
- package/dist/repo/run-repo.d.ts +31 -0
- package/dist/repo/run-repo.d.ts.map +1 -0
- package/dist/repo/run-repo.js +132 -0
- package/dist/repo/run-repo.js.map +1 -0
- package/dist/repo/task-repo.d.ts +21 -0
- package/dist/repo/task-repo.d.ts.map +1 -0
- package/dist/repo/task-repo.js +169 -0
- package/dist/repo/task-repo.js.map +1 -0
- package/dist/repo/tracked-project-repo.d.ts +16 -0
- package/dist/repo/tracked-project-repo.d.ts.map +1 -0
- package/dist/repo/tracked-project-repo.js +54 -0
- package/dist/repo/tracked-project-repo.js.map +1 -0
- package/dist/repo/worker-repo.d.ts +19 -0
- package/dist/repo/worker-repo.d.ts.map +1 -0
- package/dist/repo/worker-repo.js +72 -0
- package/dist/repo/worker-repo.js.map +1 -0
- package/dist/schemas/index.d.ts +8 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +7 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/sync.d.ts +296 -0
- package/dist/schemas/sync.d.ts.map +1 -0
- package/dist/schemas/sync.js +146 -0
- package/dist/schemas/sync.js.map +1 -0
- package/dist/schemas/worker.d.ts +77 -0
- package/dist/schemas/worker.d.ts.map +1 -0
- package/dist/schemas/worker.js +80 -0
- package/dist/schemas/worker.js.map +1 -0
- package/dist/services/anchor-service.d.ts +147 -0
- package/dist/services/anchor-service.d.ts.map +1 -0
- package/dist/services/anchor-service.js +540 -0
- package/dist/services/anchor-service.js.map +1 -0
- package/dist/services/anchor-verification.d.ts +94 -0
- package/dist/services/anchor-verification.d.ts.map +1 -0
- package/dist/services/anchor-verification.js +617 -0
- package/dist/services/anchor-verification.js.map +1 -0
- package/dist/services/ast-grep-service.d.ts +58 -0
- package/dist/services/ast-grep-service.d.ts.map +1 -0
- package/dist/services/ast-grep-service.js +356 -0
- package/dist/services/ast-grep-service.js.map +1 -0
- package/dist/services/attempt-service.d.ts +24 -0
- package/dist/services/attempt-service.d.ts.map +1 -0
- package/dist/services/attempt-service.js +55 -0
- package/dist/services/attempt-service.js.map +1 -0
- package/dist/services/auto-sync-service.d.ts +56 -0
- package/dist/services/auto-sync-service.d.ts.map +1 -0
- package/dist/services/auto-sync-service.js +66 -0
- package/dist/services/auto-sync-service.js.map +1 -0
- package/dist/services/candidate-extractor-service.d.ts +56 -0
- package/dist/services/candidate-extractor-service.d.ts.map +1 -0
- package/dist/services/candidate-extractor-service.js +365 -0
- package/dist/services/candidate-extractor-service.js.map +1 -0
- package/dist/services/claim-service.d.ts +52 -0
- package/dist/services/claim-service.d.ts.map +1 -0
- package/dist/services/claim-service.js +134 -0
- package/dist/services/claim-service.js.map +1 -0
- package/dist/services/daemon-service.d.ts +214 -0
- package/dist/services/daemon-service.d.ts.map +1 -0
- package/dist/services/daemon-service.js +522 -0
- package/dist/services/daemon-service.js.map +1 -0
- package/dist/services/deduplication-service.d.ts +67 -0
- package/dist/services/deduplication-service.d.ts.map +1 -0
- package/dist/services/deduplication-service.js +145 -0
- package/dist/services/deduplication-service.js.map +1 -0
- package/dist/services/dep-service.d.ts +14 -0
- package/dist/services/dep-service.d.ts.map +1 -0
- package/dist/services/dep-service.js +34 -0
- package/dist/services/dep-service.js.map +1 -0
- package/dist/services/diversifier-service.d.ts +46 -0
- package/dist/services/diversifier-service.d.ts.map +1 -0
- package/dist/services/diversifier-service.js +197 -0
- package/dist/services/diversifier-service.js.map +1 -0
- package/dist/services/edge-service.d.ts +78 -0
- package/dist/services/edge-service.d.ts.map +1 -0
- package/dist/services/edge-service.js +158 -0
- package/dist/services/edge-service.js.map +1 -0
- package/dist/services/embedding-service.d.ts +138 -0
- package/dist/services/embedding-service.d.ts.map +1 -0
- package/dist/services/embedding-service.js +318 -0
- package/dist/services/embedding-service.js.map +1 -0
- package/dist/services/feedback-tracker.d.ts +64 -0
- package/dist/services/feedback-tracker.d.ts.map +1 -0
- package/dist/services/feedback-tracker.js +110 -0
- package/dist/services/feedback-tracker.js.map +1 -0
- package/dist/services/file-learning-service.d.ts +17 -0
- package/dist/services/file-learning-service.d.ts.map +1 -0
- package/dist/services/file-learning-service.js +41 -0
- package/dist/services/file-learning-service.js.map +1 -0
- package/dist/services/file-watcher-service.d.ts +141 -0
- package/dist/services/file-watcher-service.d.ts.map +1 -0
- package/dist/services/file-watcher-service.js +278 -0
- package/dist/services/file-watcher-service.js.map +1 -0
- package/dist/services/graph-expansion.d.ts +155 -0
- package/dist/services/graph-expansion.d.ts.map +1 -0
- package/dist/services/graph-expansion.js +466 -0
- package/dist/services/graph-expansion.js.map +1 -0
- package/dist/services/hierarchy-service.d.ts +16 -0
- package/dist/services/hierarchy-service.d.ts.map +1 -0
- package/dist/services/hierarchy-service.js +66 -0
- package/dist/services/hierarchy-service.js.map +1 -0
- package/dist/services/index.d.ts +36 -0
- package/dist/services/index.d.ts.map +1 -0
- package/dist/services/index.js +36 -0
- package/dist/services/index.js.map +1 -0
- package/dist/services/learning-service.d.ts +39 -0
- package/dist/services/learning-service.d.ts.map +1 -0
- package/dist/services/learning-service.js +151 -0
- package/dist/services/learning-service.js.map +1 -0
- package/dist/services/migration-service.d.ts +67 -0
- package/dist/services/migration-service.d.ts.map +1 -0
- package/dist/services/migration-service.js +144 -0
- package/dist/services/migration-service.js.map +1 -0
- package/dist/services/orchestrator-service.d.ts +52 -0
- package/dist/services/orchestrator-service.d.ts.map +1 -0
- package/dist/services/orchestrator-service.js +203 -0
- package/dist/services/orchestrator-service.js.map +1 -0
- package/dist/services/promotion-service.d.ts +67 -0
- package/dist/services/promotion-service.d.ts.map +1 -0
- package/dist/services/promotion-service.js +151 -0
- package/dist/services/promotion-service.js.map +1 -0
- package/dist/services/query-expansion-service.d.ts +55 -0
- package/dist/services/query-expansion-service.d.ts.map +1 -0
- package/dist/services/query-expansion-service.js +174 -0
- package/dist/services/query-expansion-service.js.map +1 -0
- package/dist/services/ready-service.d.ts +16 -0
- package/dist/services/ready-service.d.ts.map +1 -0
- package/dist/services/ready-service.js +70 -0
- package/dist/services/ready-service.js.map +1 -0
- package/dist/services/reranker-service.d.ts +51 -0
- package/dist/services/reranker-service.d.ts.map +1 -0
- package/dist/services/reranker-service.js +128 -0
- package/dist/services/reranker-service.js.map +1 -0
- package/dist/services/retriever-service.d.ts +49 -0
- package/dist/services/retriever-service.d.ts.map +1 -0
- package/dist/services/retriever-service.js +419 -0
- package/dist/services/retriever-service.js.map +1 -0
- package/dist/services/score-service.d.ts +43 -0
- package/dist/services/score-service.d.ts.map +1 -0
- package/dist/services/score-service.js +82 -0
- package/dist/services/score-service.js.map +1 -0
- package/dist/services/swarm-verification.d.ts +104 -0
- package/dist/services/swarm-verification.d.ts.map +1 -0
- package/dist/services/swarm-verification.js +400 -0
- package/dist/services/swarm-verification.js.map +1 -0
- package/dist/services/sync-service.d.ts +115 -0
- package/dist/services/sync-service.d.ts.map +1 -0
- package/dist/services/sync-service.js +350 -0
- package/dist/services/sync-service.js.map +1 -0
- package/dist/services/task-service.d.ts +22 -0
- package/dist/services/task-service.d.ts.map +1 -0
- package/dist/services/task-service.js +221 -0
- package/dist/services/task-service.js.map +1 -0
- package/dist/services/worker-process.d.ts +41 -0
- package/dist/services/worker-process.d.ts.map +1 -0
- package/dist/services/worker-process.js +280 -0
- package/dist/services/worker-process.js.map +1 -0
- package/dist/services/worker-service.d.ts +74 -0
- package/dist/services/worker-service.d.ts.map +1 -0
- package/dist/services/worker-service.js +148 -0
- package/dist/services/worker-service.js.map +1 -0
- package/dist/utils/glob.d.ts +15 -0
- package/dist/utils/glob.d.ts.map +1 -0
- package/dist/utils/glob.js +27 -0
- package/dist/utils/glob.js.map +1 -0
- package/dist/utils/math.d.ts +6 -0
- package/dist/utils/math.d.ts.map +1 -0
- package/dist/utils/math.js +21 -0
- package/dist/utils/math.js.map +1 -0
- package/package.json +72 -0
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { AnchorRepository } from "../repo/anchor-repo.js";
|
|
3
|
+
import { LearningRepository } from "../repo/learning-repo.js";
|
|
4
|
+
import { AnchorNotFoundError, ValidationError, LearningNotFoundError } from "../errors.js";
|
|
5
|
+
import { getAnchorTTL, isStale } from "./anchor-verification.js";
|
|
6
|
+
/** Validate anchor types at compile time */
|
|
7
|
+
const VALID_ANCHOR_TYPES = ["glob", "hash", "symbol", "line_range"];
|
|
8
|
+
export class AnchorService extends Context.Tag("AnchorService")() {
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Validate anchor input based on type.
|
|
12
|
+
* Each anchor type has specific requirements.
|
|
13
|
+
*/
|
|
14
|
+
const validateAnchorInput = (input) => Effect.gen(function* () {
|
|
15
|
+
// Validate anchor type
|
|
16
|
+
if (!VALID_ANCHOR_TYPES.includes(input.anchorType)) {
|
|
17
|
+
return yield* Effect.fail(new ValidationError({
|
|
18
|
+
reason: `Invalid anchor type: ${input.anchorType}. Valid types: ${VALID_ANCHOR_TYPES.join(", ")}`
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
// Validate file path
|
|
22
|
+
if (!input.filePath || input.filePath.trim().length === 0) {
|
|
23
|
+
return yield* Effect.fail(new ValidationError({
|
|
24
|
+
reason: "File path is required"
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
// Validate value
|
|
28
|
+
if (!input.value || input.value.trim().length === 0) {
|
|
29
|
+
return yield* Effect.fail(new ValidationError({
|
|
30
|
+
reason: "Anchor value is required"
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
// Type-specific validation
|
|
34
|
+
switch (input.anchorType) {
|
|
35
|
+
case "glob":
|
|
36
|
+
// Glob patterns should be valid patterns
|
|
37
|
+
// Basic validation: must contain at least a filename or pattern
|
|
38
|
+
if (input.value.length < 1) {
|
|
39
|
+
return yield* Effect.fail(new ValidationError({
|
|
40
|
+
reason: "Glob pattern cannot be empty"
|
|
41
|
+
}));
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
learningId: input.learningId,
|
|
45
|
+
anchorType: input.anchorType,
|
|
46
|
+
anchorValue: input.value,
|
|
47
|
+
filePath: input.filePath,
|
|
48
|
+
symbolFqname: null,
|
|
49
|
+
lineStart: null,
|
|
50
|
+
lineEnd: null,
|
|
51
|
+
contentHash: input.contentHash ?? null,
|
|
52
|
+
contentPreview: input.contentPreview ?? null
|
|
53
|
+
};
|
|
54
|
+
case "hash":
|
|
55
|
+
// Hash should be a valid SHA256 hash (64 hex characters)
|
|
56
|
+
if (!/^[a-f0-9]{64}$/i.test(input.value)) {
|
|
57
|
+
return yield* Effect.fail(new ValidationError({
|
|
58
|
+
reason: "Hash anchor value must be a valid SHA256 hash (64 hex characters)"
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
return {
|
|
62
|
+
learningId: input.learningId,
|
|
63
|
+
anchorType: input.anchorType,
|
|
64
|
+
anchorValue: input.value,
|
|
65
|
+
filePath: input.filePath,
|
|
66
|
+
symbolFqname: null,
|
|
67
|
+
lineStart: input.lineStart ?? null,
|
|
68
|
+
lineEnd: input.lineEnd ?? null,
|
|
69
|
+
contentHash: input.value,
|
|
70
|
+
contentPreview: input.contentPreview ?? null
|
|
71
|
+
};
|
|
72
|
+
case "symbol":
|
|
73
|
+
// Symbol anchors require a symbol FQName
|
|
74
|
+
if (!input.symbolFqname || input.symbolFqname.trim().length === 0) {
|
|
75
|
+
return yield* Effect.fail(new ValidationError({
|
|
76
|
+
reason: "Symbol anchor requires symbolFqname"
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
// Symbol FQName format: file::symbolName or file::class::method
|
|
80
|
+
if (!input.symbolFqname.includes("::")) {
|
|
81
|
+
return yield* Effect.fail(new ValidationError({
|
|
82
|
+
reason: "Symbol FQName must be in format: file::symbol or file::class::method"
|
|
83
|
+
}));
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
learningId: input.learningId,
|
|
87
|
+
anchorType: input.anchorType,
|
|
88
|
+
anchorValue: input.value,
|
|
89
|
+
filePath: input.filePath,
|
|
90
|
+
symbolFqname: input.symbolFqname,
|
|
91
|
+
lineStart: input.lineStart ?? null,
|
|
92
|
+
lineEnd: input.lineEnd ?? null,
|
|
93
|
+
contentHash: input.contentHash ?? null,
|
|
94
|
+
contentPreview: input.contentPreview ?? null
|
|
95
|
+
};
|
|
96
|
+
case "line_range":
|
|
97
|
+
// Line range anchors require valid line numbers
|
|
98
|
+
if (input.lineStart === undefined || input.lineStart < 1) {
|
|
99
|
+
return yield* Effect.fail(new ValidationError({
|
|
100
|
+
reason: "Line range anchor requires valid lineStart (>= 1)"
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
if (input.lineEnd !== undefined && input.lineEnd < input.lineStart) {
|
|
104
|
+
return yield* Effect.fail(new ValidationError({
|
|
105
|
+
reason: "lineEnd must be >= lineStart"
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
learningId: input.learningId,
|
|
110
|
+
anchorType: input.anchorType,
|
|
111
|
+
anchorValue: input.value,
|
|
112
|
+
filePath: input.filePath,
|
|
113
|
+
symbolFqname: null,
|
|
114
|
+
lineStart: input.lineStart,
|
|
115
|
+
lineEnd: input.lineEnd ?? input.lineStart,
|
|
116
|
+
contentHash: input.contentHash ?? null,
|
|
117
|
+
contentPreview: input.contentPreview ?? null
|
|
118
|
+
};
|
|
119
|
+
default:
|
|
120
|
+
return yield* Effect.fail(new ValidationError({
|
|
121
|
+
reason: `Unknown anchor type: ${input.anchorType}`
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
/**
|
|
126
|
+
* Validate anchor status.
|
|
127
|
+
*/
|
|
128
|
+
const VALID_STATUSES = ["valid", "drifted", "invalid"];
|
|
129
|
+
const validateStatus = (status) => Effect.gen(function* () {
|
|
130
|
+
if (!VALID_STATUSES.includes(status)) {
|
|
131
|
+
return yield* Effect.fail(new ValidationError({
|
|
132
|
+
reason: `Invalid anchor status: ${status}. Valid statuses: ${VALID_STATUSES.join(", ")}`
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
return status;
|
|
136
|
+
});
|
|
137
|
+
export const AnchorServiceLive = Layer.effect(AnchorService, Effect.gen(function* () {
|
|
138
|
+
const anchorRepo = yield* AnchorRepository;
|
|
139
|
+
const learningRepo = yield* LearningRepository;
|
|
140
|
+
return {
|
|
141
|
+
createAnchor: (input) => Effect.gen(function* () {
|
|
142
|
+
// Validate the input based on anchor type
|
|
143
|
+
const validatedInput = yield* validateAnchorInput(input);
|
|
144
|
+
// Verify the learning exists
|
|
145
|
+
const learning = yield* learningRepo.findById(input.learningId);
|
|
146
|
+
if (!learning) {
|
|
147
|
+
return yield* Effect.fail(new LearningNotFoundError({ id: input.learningId }));
|
|
148
|
+
}
|
|
149
|
+
// Create the anchor
|
|
150
|
+
return yield* anchorRepo.create(validatedInput);
|
|
151
|
+
}),
|
|
152
|
+
verifyAnchor: (anchorId) => Effect.gen(function* () {
|
|
153
|
+
const anchor = yield* anchorRepo.findById(anchorId);
|
|
154
|
+
if (!anchor) {
|
|
155
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
156
|
+
}
|
|
157
|
+
const previousStatus = anchor.status;
|
|
158
|
+
// Verification logic depends on anchor type
|
|
159
|
+
// For now, we simply update the verified_at timestamp
|
|
160
|
+
// Full verification would check file existence, hash matches, symbol presence, etc.
|
|
161
|
+
// This is a placeholder for future ast-grep integration
|
|
162
|
+
let newStatus = "valid";
|
|
163
|
+
let reason;
|
|
164
|
+
switch (anchor.anchorType) {
|
|
165
|
+
case "glob":
|
|
166
|
+
// Glob anchors are generally considered valid if file path matches pattern
|
|
167
|
+
// Full implementation would check if files matching the pattern exist
|
|
168
|
+
newStatus = "valid";
|
|
169
|
+
break;
|
|
170
|
+
case "hash":
|
|
171
|
+
// Hash anchors would compare stored hash with current file content hash
|
|
172
|
+
// Placeholder: assume valid for now (full implementation requires file access)
|
|
173
|
+
newStatus = "valid";
|
|
174
|
+
reason = "Hash verification requires file content access";
|
|
175
|
+
break;
|
|
176
|
+
case "symbol":
|
|
177
|
+
// Symbol anchors would use ast-grep to verify symbol exists
|
|
178
|
+
// Placeholder: assume valid for now (full implementation requires ast-grep)
|
|
179
|
+
newStatus = "valid";
|
|
180
|
+
reason = "Symbol verification requires ast-grep integration";
|
|
181
|
+
break;
|
|
182
|
+
case "line_range":
|
|
183
|
+
// Line range anchors would check if line range is still within file bounds
|
|
184
|
+
// Placeholder: assume valid for now
|
|
185
|
+
newStatus = "valid";
|
|
186
|
+
reason = "Line range verification requires file access";
|
|
187
|
+
break;
|
|
188
|
+
}
|
|
189
|
+
// Update status and verified_at timestamp
|
|
190
|
+
if (newStatus !== previousStatus) {
|
|
191
|
+
yield* anchorRepo.updateStatus(anchorId, newStatus);
|
|
192
|
+
}
|
|
193
|
+
yield* anchorRepo.updateVerifiedAt(anchorId);
|
|
194
|
+
return {
|
|
195
|
+
anchorId,
|
|
196
|
+
previousStatus,
|
|
197
|
+
newStatus,
|
|
198
|
+
verified: true,
|
|
199
|
+
reason
|
|
200
|
+
};
|
|
201
|
+
}),
|
|
202
|
+
updateAnchorStatus: (anchorId, status, reason, detectedBy = "manual") => Effect.gen(function* () {
|
|
203
|
+
// Validate status
|
|
204
|
+
yield* validateStatus(status);
|
|
205
|
+
// Get anchor to verify it exists
|
|
206
|
+
const anchor = yield* anchorRepo.findById(anchorId);
|
|
207
|
+
if (!anchor) {
|
|
208
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
209
|
+
}
|
|
210
|
+
const oldStatus = anchor.status;
|
|
211
|
+
// Update the status
|
|
212
|
+
yield* anchorRepo.updateStatus(anchorId, status);
|
|
213
|
+
// Log the status change if status actually changed
|
|
214
|
+
if (oldStatus !== status) {
|
|
215
|
+
yield* anchorRepo.logInvalidation({
|
|
216
|
+
anchorId,
|
|
217
|
+
oldStatus,
|
|
218
|
+
newStatus: status,
|
|
219
|
+
reason: reason ?? `Status changed from ${oldStatus} to ${status}`,
|
|
220
|
+
detectedBy,
|
|
221
|
+
oldContentHash: anchor.contentHash
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
// Return updated anchor
|
|
225
|
+
const updated = yield* anchorRepo.findById(anchorId);
|
|
226
|
+
if (!updated) {
|
|
227
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
228
|
+
}
|
|
229
|
+
return updated;
|
|
230
|
+
}),
|
|
231
|
+
findAnchorsForFile: (filePath) => anchorRepo.findByFilePath(filePath),
|
|
232
|
+
findAnchorsForLearning: (learningId) => anchorRepo.findByLearningId(learningId),
|
|
233
|
+
get: (id) => Effect.gen(function* () {
|
|
234
|
+
const anchor = yield* anchorRepo.findById(id);
|
|
235
|
+
if (!anchor) {
|
|
236
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id }));
|
|
237
|
+
}
|
|
238
|
+
return anchor;
|
|
239
|
+
}),
|
|
240
|
+
getWithVerification: (id, _options = {}) => Effect.gen(function* () {
|
|
241
|
+
// Get the anchor first
|
|
242
|
+
const anchor = yield* anchorRepo.findById(id);
|
|
243
|
+
if (!anchor) {
|
|
244
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id }));
|
|
245
|
+
}
|
|
246
|
+
// Get TTL and check staleness
|
|
247
|
+
const ttl = yield* getAnchorTTL();
|
|
248
|
+
const anchorIsStale = isStale(anchor, ttl);
|
|
249
|
+
// If fresh, return immediately without verification
|
|
250
|
+
if (!anchorIsStale) {
|
|
251
|
+
return {
|
|
252
|
+
anchor,
|
|
253
|
+
isFresh: true,
|
|
254
|
+
wasVerified: false
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
// Anchor is stale - run verification
|
|
258
|
+
const previousStatus = anchor.status;
|
|
259
|
+
// Verification logic (simplified - uses the same logic as verifyAnchor)
|
|
260
|
+
// For now, we simply update the verified_at timestamp
|
|
261
|
+
// Full verification would check file existence, hash matches, symbol presence, etc.
|
|
262
|
+
// Note: Future implementation will use AnchorVerificationService for full file-based verification
|
|
263
|
+
let newStatus = previousStatus;
|
|
264
|
+
let reason;
|
|
265
|
+
let action = "unchanged";
|
|
266
|
+
switch (anchor.anchorType) {
|
|
267
|
+
case "glob":
|
|
268
|
+
// Glob anchors are generally considered valid if file path matches pattern
|
|
269
|
+
// Full implementation would check if files matching the pattern exist
|
|
270
|
+
break;
|
|
271
|
+
case "hash":
|
|
272
|
+
// Hash anchors would compare stored hash with current file content hash
|
|
273
|
+
// Placeholder: assume unchanged for now (full implementation requires file access)
|
|
274
|
+
reason = "Hash verification requires file content access";
|
|
275
|
+
break;
|
|
276
|
+
case "symbol":
|
|
277
|
+
// Symbol anchors would use ast-grep to verify symbol exists
|
|
278
|
+
// Placeholder: assume unchanged for now (full implementation requires ast-grep)
|
|
279
|
+
reason = "Symbol verification requires ast-grep integration";
|
|
280
|
+
break;
|
|
281
|
+
case "line_range":
|
|
282
|
+
// Line range anchors would check if line range is still within file bounds
|
|
283
|
+
// Placeholder: assume unchanged for now
|
|
284
|
+
reason = "Line range verification requires file access";
|
|
285
|
+
break;
|
|
286
|
+
}
|
|
287
|
+
// Determine action based on status change
|
|
288
|
+
if (newStatus !== previousStatus) {
|
|
289
|
+
yield* anchorRepo.updateStatus(id, newStatus);
|
|
290
|
+
if (newStatus === "drifted") {
|
|
291
|
+
action = "drifted";
|
|
292
|
+
}
|
|
293
|
+
else if (newStatus === "invalid") {
|
|
294
|
+
action = "invalidated";
|
|
295
|
+
}
|
|
296
|
+
else if (previousStatus === "drifted" && newStatus === "valid") {
|
|
297
|
+
action = "self_healed";
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
// Update verified_at timestamp
|
|
301
|
+
yield* anchorRepo.updateVerifiedAt(id);
|
|
302
|
+
// Get the updated anchor
|
|
303
|
+
const updatedAnchor = yield* anchorRepo.findById(id);
|
|
304
|
+
if (!updatedAnchor) {
|
|
305
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id }));
|
|
306
|
+
}
|
|
307
|
+
return {
|
|
308
|
+
anchor: updatedAnchor,
|
|
309
|
+
isFresh: false,
|
|
310
|
+
wasVerified: true,
|
|
311
|
+
verificationResult: {
|
|
312
|
+
previousStatus,
|
|
313
|
+
newStatus,
|
|
314
|
+
action,
|
|
315
|
+
reason
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}),
|
|
319
|
+
remove: (id, reason = "Soft deleted") => Effect.gen(function* () {
|
|
320
|
+
const anchor = yield* anchorRepo.findById(id);
|
|
321
|
+
if (!anchor) {
|
|
322
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id }));
|
|
323
|
+
}
|
|
324
|
+
const oldStatus = anchor.status;
|
|
325
|
+
// Set status to invalid (soft delete)
|
|
326
|
+
yield* anchorRepo.updateStatus(id, "invalid");
|
|
327
|
+
// Log the status change
|
|
328
|
+
yield* anchorRepo.logInvalidation({
|
|
329
|
+
anchorId: id,
|
|
330
|
+
oldStatus,
|
|
331
|
+
newStatus: "invalid",
|
|
332
|
+
reason,
|
|
333
|
+
detectedBy: "manual",
|
|
334
|
+
oldContentHash: anchor.contentHash
|
|
335
|
+
});
|
|
336
|
+
// Return the updated anchor
|
|
337
|
+
const updated = yield* anchorRepo.findById(id);
|
|
338
|
+
if (!updated) {
|
|
339
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id }));
|
|
340
|
+
}
|
|
341
|
+
return updated;
|
|
342
|
+
}),
|
|
343
|
+
hardDelete: (id) => Effect.gen(function* () {
|
|
344
|
+
const anchor = yield* anchorRepo.findById(id);
|
|
345
|
+
if (!anchor) {
|
|
346
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id }));
|
|
347
|
+
}
|
|
348
|
+
yield* anchorRepo.delete(id);
|
|
349
|
+
}),
|
|
350
|
+
findDrifted: () => anchorRepo.findDrifted(),
|
|
351
|
+
findInvalid: () => anchorRepo.findInvalid(),
|
|
352
|
+
verifyAnchorsForFile: (filePath) => Effect.gen(function* () {
|
|
353
|
+
const anchors = yield* anchorRepo.findByFilePath(filePath);
|
|
354
|
+
let verified = 0;
|
|
355
|
+
let drifted = 0;
|
|
356
|
+
let invalid = 0;
|
|
357
|
+
for (const anchor of anchors) {
|
|
358
|
+
// Simple verification - full implementation would check actual file state
|
|
359
|
+
yield* anchorRepo.updateVerifiedAt(anchor.id);
|
|
360
|
+
switch (anchor.status) {
|
|
361
|
+
case "valid":
|
|
362
|
+
verified++;
|
|
363
|
+
break;
|
|
364
|
+
case "drifted":
|
|
365
|
+
drifted++;
|
|
366
|
+
break;
|
|
367
|
+
case "invalid":
|
|
368
|
+
invalid++;
|
|
369
|
+
break;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
total: anchors.length,
|
|
374
|
+
verified,
|
|
375
|
+
drifted,
|
|
376
|
+
invalid
|
|
377
|
+
};
|
|
378
|
+
}),
|
|
379
|
+
pin: (anchorId) => Effect.gen(function* () {
|
|
380
|
+
const anchor = yield* anchorRepo.findById(anchorId);
|
|
381
|
+
if (!anchor) {
|
|
382
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
383
|
+
}
|
|
384
|
+
yield* anchorRepo.setPinned(anchorId, true);
|
|
385
|
+
const updated = yield* anchorRepo.findById(anchorId);
|
|
386
|
+
if (!updated) {
|
|
387
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
388
|
+
}
|
|
389
|
+
return updated;
|
|
390
|
+
}),
|
|
391
|
+
unpin: (anchorId) => Effect.gen(function* () {
|
|
392
|
+
const anchor = yield* anchorRepo.findById(anchorId);
|
|
393
|
+
if (!anchor) {
|
|
394
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
395
|
+
}
|
|
396
|
+
yield* anchorRepo.setPinned(anchorId, false);
|
|
397
|
+
const updated = yield* anchorRepo.findById(anchorId);
|
|
398
|
+
if (!updated) {
|
|
399
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
400
|
+
}
|
|
401
|
+
return updated;
|
|
402
|
+
}),
|
|
403
|
+
invalidate: (anchorId, reason, detectedBy = "manual") => Effect.gen(function* () {
|
|
404
|
+
const anchor = yield* anchorRepo.findById(anchorId);
|
|
405
|
+
if (!anchor) {
|
|
406
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
407
|
+
}
|
|
408
|
+
const oldStatus = anchor.status;
|
|
409
|
+
// Update status to invalid
|
|
410
|
+
yield* anchorRepo.updateStatus(anchorId, "invalid");
|
|
411
|
+
// Log the invalidation
|
|
412
|
+
yield* anchorRepo.logInvalidation({
|
|
413
|
+
anchorId,
|
|
414
|
+
oldStatus,
|
|
415
|
+
newStatus: "invalid",
|
|
416
|
+
reason,
|
|
417
|
+
detectedBy,
|
|
418
|
+
oldContentHash: anchor.contentHash
|
|
419
|
+
});
|
|
420
|
+
const updated = yield* anchorRepo.findById(anchorId);
|
|
421
|
+
if (!updated) {
|
|
422
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
423
|
+
}
|
|
424
|
+
return updated;
|
|
425
|
+
}),
|
|
426
|
+
restore: (anchorId) => Effect.gen(function* () {
|
|
427
|
+
const anchor = yield* anchorRepo.findById(anchorId);
|
|
428
|
+
if (!anchor) {
|
|
429
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
430
|
+
}
|
|
431
|
+
const currentStatus = anchor.status;
|
|
432
|
+
// Find the most recent invalidation log entry with status change TO 'invalid'
|
|
433
|
+
const logs = yield* anchorRepo.getInvalidationLogs(anchorId);
|
|
434
|
+
const invalidationEntry = logs.find(log => log.newStatus === "invalid");
|
|
435
|
+
// Determine restore target status: use oldStatus from log or default to 'valid'
|
|
436
|
+
const restoreToStatus = invalidationEntry?.oldStatus ?? "valid";
|
|
437
|
+
// Update status to the restore target
|
|
438
|
+
yield* anchorRepo.updateStatus(anchorId, restoreToStatus);
|
|
439
|
+
yield* anchorRepo.updateVerifiedAt(anchorId);
|
|
440
|
+
// Optionally restore old_content_hash if it was stored in the log entry
|
|
441
|
+
if (invalidationEntry?.oldContentHash) {
|
|
442
|
+
yield* anchorRepo.update(anchorId, { contentHash: invalidationEntry.oldContentHash });
|
|
443
|
+
}
|
|
444
|
+
// Log the restoration
|
|
445
|
+
yield* anchorRepo.logInvalidation({
|
|
446
|
+
anchorId,
|
|
447
|
+
oldStatus: currentStatus,
|
|
448
|
+
newStatus: restoreToStatus,
|
|
449
|
+
reason: invalidationEntry
|
|
450
|
+
? `Restored to ${restoreToStatus} (from invalidation log)`
|
|
451
|
+
: "Manual restoration",
|
|
452
|
+
detectedBy: "manual",
|
|
453
|
+
oldContentHash: anchor.contentHash,
|
|
454
|
+
newContentHash: invalidationEntry?.oldContentHash ?? null
|
|
455
|
+
});
|
|
456
|
+
const updated = yield* anchorRepo.findById(anchorId);
|
|
457
|
+
if (!updated) {
|
|
458
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
459
|
+
}
|
|
460
|
+
return updated;
|
|
461
|
+
}),
|
|
462
|
+
prune: (olderThanDays) => Effect.gen(function* () {
|
|
463
|
+
const deleted = yield* anchorRepo.deleteOldInvalid(olderThanDays);
|
|
464
|
+
return { deleted };
|
|
465
|
+
}),
|
|
466
|
+
getStatus: () => Effect.gen(function* () {
|
|
467
|
+
const summary = yield* anchorRepo.getStatusSummary();
|
|
468
|
+
const recentInvalidations = yield* anchorRepo.getInvalidationLogs();
|
|
469
|
+
return {
|
|
470
|
+
total: summary.total,
|
|
471
|
+
valid: summary.valid,
|
|
472
|
+
drifted: summary.drifted,
|
|
473
|
+
invalid: summary.invalid,
|
|
474
|
+
pinned: summary.pinned,
|
|
475
|
+
recentInvalidations: recentInvalidations.slice(0, 10)
|
|
476
|
+
};
|
|
477
|
+
}),
|
|
478
|
+
verifyAll: () => Effect.gen(function* () {
|
|
479
|
+
const anchors = yield* anchorRepo.findAll();
|
|
480
|
+
let verified = 0;
|
|
481
|
+
let drifted = 0;
|
|
482
|
+
let invalid = 0;
|
|
483
|
+
for (const anchor of anchors) {
|
|
484
|
+
// Skip pinned anchors
|
|
485
|
+
if (anchor.pinned) {
|
|
486
|
+
switch (anchor.status) {
|
|
487
|
+
case "valid":
|
|
488
|
+
verified++;
|
|
489
|
+
break;
|
|
490
|
+
case "drifted":
|
|
491
|
+
drifted++;
|
|
492
|
+
break;
|
|
493
|
+
case "invalid":
|
|
494
|
+
invalid++;
|
|
495
|
+
break;
|
|
496
|
+
}
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
// Simple verification - full implementation would check actual file state
|
|
500
|
+
yield* anchorRepo.updateVerifiedAt(anchor.id);
|
|
501
|
+
switch (anchor.status) {
|
|
502
|
+
case "valid":
|
|
503
|
+
verified++;
|
|
504
|
+
break;
|
|
505
|
+
case "drifted":
|
|
506
|
+
drifted++;
|
|
507
|
+
break;
|
|
508
|
+
case "invalid":
|
|
509
|
+
invalid++;
|
|
510
|
+
break;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
return {
|
|
514
|
+
total: anchors.length,
|
|
515
|
+
verified,
|
|
516
|
+
drifted,
|
|
517
|
+
invalid
|
|
518
|
+
};
|
|
519
|
+
}),
|
|
520
|
+
logStatusChange: (anchorId, oldStatus, newStatus, reason, detectedBy = "manual", oldHash, newHash, similarity) => Effect.gen(function* () {
|
|
521
|
+
// Verify anchor exists
|
|
522
|
+
const anchor = yield* anchorRepo.findById(anchorId);
|
|
523
|
+
if (!anchor) {
|
|
524
|
+
return yield* Effect.fail(new AnchorNotFoundError({ id: anchorId }));
|
|
525
|
+
}
|
|
526
|
+
// Create entry in invalidation_log
|
|
527
|
+
return yield* anchorRepo.logInvalidation({
|
|
528
|
+
anchorId,
|
|
529
|
+
oldStatus,
|
|
530
|
+
newStatus,
|
|
531
|
+
reason,
|
|
532
|
+
detectedBy,
|
|
533
|
+
oldContentHash: oldHash ?? null,
|
|
534
|
+
newContentHash: newHash ?? null,
|
|
535
|
+
similarityScore: similarity ?? null
|
|
536
|
+
});
|
|
537
|
+
})
|
|
538
|
+
};
|
|
539
|
+
}));
|
|
540
|
+
//# sourceMappingURL=anchor-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor-service.js","sourceRoot":"","sources":["../../src/services/anchor-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AnchorVerificationService - Periodic anchor verification for PRD-017
|
|
3
|
+
*
|
|
4
|
+
* Verifies anchors by checking actual file system state:
|
|
5
|
+
* - glob: Check if glob pattern still matches files
|
|
6
|
+
* - hash: Compute content hash and compare
|
|
7
|
+
* - symbol: Grep-based check for symbol presence
|
|
8
|
+
* - line_range: Verify file exists and has enough lines
|
|
9
|
+
*
|
|
10
|
+
* Updates anchor status (valid/drifted/invalid) and logs all changes
|
|
11
|
+
* to invalidation_log via AnchorRepository.
|
|
12
|
+
*
|
|
13
|
+
* @see docs/prd/PRD-017-invalidation-maintenance.md
|
|
14
|
+
* @see docs/design/DD-017-invalidation-maintenance.md
|
|
15
|
+
*/
|
|
16
|
+
import { Context, Effect, Layer } from "effect";
|
|
17
|
+
import { AnchorRepository } from "../repo/anchor-repo.js";
|
|
18
|
+
import { DatabaseError } from "../errors.js";
|
|
19
|
+
import type { Anchor, AnchorStatus, InvalidationSource } from "@jamesaphoenix/tx-types";
|
|
20
|
+
/** Result of verifying a single anchor */
|
|
21
|
+
export interface VerificationResult {
|
|
22
|
+
readonly anchorId: number;
|
|
23
|
+
readonly previousStatus: AnchorStatus;
|
|
24
|
+
readonly newStatus: AnchorStatus;
|
|
25
|
+
readonly action: "unchanged" | "self_healed" | "drifted" | "invalidated";
|
|
26
|
+
readonly reason?: string;
|
|
27
|
+
readonly similarity?: number;
|
|
28
|
+
readonly oldContentHash?: string | null;
|
|
29
|
+
readonly newContentHash?: string | null;
|
|
30
|
+
}
|
|
31
|
+
/** Summary of batch verification */
|
|
32
|
+
export interface VerificationSummary {
|
|
33
|
+
readonly total: number;
|
|
34
|
+
readonly unchanged: number;
|
|
35
|
+
readonly selfHealed: number;
|
|
36
|
+
readonly drifted: number;
|
|
37
|
+
readonly invalid: number;
|
|
38
|
+
readonly errors: number;
|
|
39
|
+
readonly duration: number;
|
|
40
|
+
}
|
|
41
|
+
/** Options for verification */
|
|
42
|
+
export interface VerifyOptions {
|
|
43
|
+
/** Detection source for audit logging */
|
|
44
|
+
readonly detectedBy?: InvalidationSource;
|
|
45
|
+
/** Skip pinned anchors (default: true) */
|
|
46
|
+
readonly skipPinned?: boolean;
|
|
47
|
+
/** Base directory for relative file paths */
|
|
48
|
+
readonly baseDir?: string;
|
|
49
|
+
}
|
|
50
|
+
declare const AnchorVerificationService_base: Context.TagClass<AnchorVerificationService, "AnchorVerificationService", {
|
|
51
|
+
/**
|
|
52
|
+
* Verify a single anchor against the file system.
|
|
53
|
+
* Checks file existence, content hash, symbol presence, etc.
|
|
54
|
+
*/
|
|
55
|
+
readonly verify: (anchorId: number, options?: VerifyOptions) => Effect.Effect<VerificationResult, DatabaseError>;
|
|
56
|
+
/**
|
|
57
|
+
* Verify all anchors.
|
|
58
|
+
* Returns summary of verification results.
|
|
59
|
+
*/
|
|
60
|
+
readonly verifyAll: (options?: VerifyOptions) => Effect.Effect<VerificationSummary, DatabaseError>;
|
|
61
|
+
/**
|
|
62
|
+
* Verify all anchors for a specific file.
|
|
63
|
+
*/
|
|
64
|
+
readonly verifyFile: (filePath: string, options?: VerifyOptions) => Effect.Effect<VerificationSummary, DatabaseError>;
|
|
65
|
+
/**
|
|
66
|
+
* Verify anchors matching a glob pattern.
|
|
67
|
+
*/
|
|
68
|
+
readonly verifyGlob: (globPattern: string, options?: VerifyOptions) => Effect.Effect<VerificationSummary, DatabaseError>;
|
|
69
|
+
}>;
|
|
70
|
+
export declare class AnchorVerificationService extends AnchorVerificationService_base {
|
|
71
|
+
}
|
|
72
|
+
/** Default anchor cache TTL in seconds (1 hour) */
|
|
73
|
+
export declare const DEFAULT_ANCHOR_CACHE_TTL = 3600;
|
|
74
|
+
/**
|
|
75
|
+
* Get the anchor cache TTL from TX_ANCHOR_CACHE_TTL env var.
|
|
76
|
+
* Used by lazy verification to determine staleness.
|
|
77
|
+
*
|
|
78
|
+
* @returns Effect yielding TTL in seconds (default: 3600)
|
|
79
|
+
*/
|
|
80
|
+
export declare const getAnchorTTL: () => Effect.Effect<number, never>;
|
|
81
|
+
/**
|
|
82
|
+
* Check if an anchor is stale based on its verified_at timestamp and TTL.
|
|
83
|
+
* An anchor is stale if:
|
|
84
|
+
* - verified_at is null (never verified), or
|
|
85
|
+
* - verified_at is older than (now - TTL)
|
|
86
|
+
*
|
|
87
|
+
* @param anchor - The anchor to check
|
|
88
|
+
* @param ttlSeconds - TTL in seconds (from getAnchorTTL)
|
|
89
|
+
* @returns true if anchor is stale and needs verification
|
|
90
|
+
*/
|
|
91
|
+
export declare const isStale: (anchor: Anchor, ttlSeconds: number) => boolean;
|
|
92
|
+
export declare const AnchorVerificationServiceLive: Layer.Layer<AnchorVerificationService, never, AnchorRepository>;
|
|
93
|
+
export {};
|
|
94
|
+
//# sourceMappingURL=anchor-verification.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anchor-verification.d.ts","sourceRoot":"","sources":["../../src/services/anchor-verification.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAU,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAIvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AAE5C,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAA;AAMvF,0CAA0C;AAC1C,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,cAAc,EAAE,YAAY,CAAA;IACrC,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAA;IAChC,QAAQ,CAAC,MAAM,EAAE,WAAW,GAAG,aAAa,GAAG,SAAS,GAAG,aAAa,CAAA;IACxE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACvC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACxC;AAED,oCAAoC;AACpC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAC1B;AAED,+BAA+B;AAC/B,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,kBAAkB,CAAA;IACxC,0CAA0C;IAC1C,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAA;IAC7B,6CAA6C;IAC7C,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAC1B;;IASG;;;OAGG;qBACc,CACf,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,aAAa,KACpB,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,aAAa,CAAC;IAErD;;;OAGG;wBACiB,CAClB,OAAO,CAAC,EAAE,aAAa,KACpB,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,aAAa,CAAC;IAEtD;;OAEG;yBACkB,CACnB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,aAAa,KACpB,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,aAAa,CAAC;IAEtD;;OAEG;yBACkB,CACnB,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,aAAa,KACpB,MAAM,CAAC,MAAM,CAAC,mBAAmB,EAAE,aAAa,CAAC;;AAlC1D,qBAAa,yBAA0B,SAAQ,8BAoC5C;CAAG;AAMN,mDAAmD;AACnD,eAAO,MAAM,wBAAwB,OAAO,CAAA;AAE5C;;;;;GAKG;AACH,eAAO,MAAM,YAAY,QAAO,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,KAAK,CAIxD,CAAA;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO,GAAI,QAAQ,MAAM,EAAE,YAAY,MAAM,KAAG,OAU5D,CAAA;AAgMD,eAAO,MAAM,6BAA6B,iEAkfzC,CAAA"}
|