@jamesaphoenix/tx-core 0.4.2 → 0.4.3
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/README.md +480 -0
- package/dist/db.d.ts +28 -14
- package/dist/db.d.ts.map +1 -1
- package/dist/db.js +102 -14
- package/dist/db.js.map +1 -1
- package/dist/errors.d.ts +178 -34
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +119 -26
- package/dist/errors.js.map +1 -1
- package/dist/id.d.ts +10 -0
- package/dist/id.d.ts.map +1 -1
- package/dist/id.js +17 -1
- package/dist/id.js.map +1 -1
- package/dist/index.d.ts +15 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +62 -8
- package/dist/index.js.map +1 -1
- package/dist/layer.d.ts +23 -14
- package/dist/layer.d.ts.map +1 -1
- package/dist/layer.js +75 -76
- package/dist/layer.js.map +1 -1
- package/dist/mappers/anchor.d.ts +15 -1
- package/dist/mappers/anchor.d.ts.map +1 -1
- package/dist/mappers/anchor.js +95 -28
- package/dist/mappers/anchor.js.map +1 -1
- package/dist/mappers/attempt.d.ts +3 -1
- package/dist/mappers/attempt.d.ts.map +1 -1
- package/dist/mappers/attempt.js +23 -9
- package/dist/mappers/attempt.js.map +1 -1
- package/dist/mappers/candidate.d.ts +3 -1
- package/dist/mappers/candidate.d.ts.map +1 -1
- package/dist/mappers/candidate.js +46 -16
- package/dist/mappers/candidate.js.map +1 -1
- package/dist/mappers/claim.d.ts +1 -1
- package/dist/mappers/claim.d.ts.map +1 -1
- package/dist/mappers/claim.js +11 -4
- package/dist/mappers/claim.js.map +1 -1
- package/dist/mappers/deduplication.d.ts +13 -1
- package/dist/mappers/deduplication.d.ts.map +1 -1
- package/dist/mappers/deduplication.js +22 -3
- package/dist/mappers/deduplication.js.map +1 -1
- package/dist/mappers/doc.d.ts +24 -0
- package/dist/mappers/doc.d.ts.map +1 -0
- package/dist/mappers/doc.js +161 -0
- package/dist/mappers/doc.js.map +1 -0
- package/dist/mappers/edge.d.ts +10 -1
- package/dist/mappers/edge.d.ts.map +1 -1
- package/dist/mappers/edge.js +74 -12
- package/dist/mappers/edge.js.map +1 -1
- package/dist/mappers/file-learning.d.ts.map +1 -1
- package/dist/mappers/file-learning.js +2 -1
- package/dist/mappers/file-learning.js.map +1 -1
- package/dist/mappers/index.d.ts +6 -7
- package/dist/mappers/index.d.ts.map +1 -1
- package/dist/mappers/index.js +10 -12
- package/dist/mappers/index.js.map +1 -1
- package/dist/mappers/learning.d.ts +9 -1
- package/dist/mappers/learning.d.ts.map +1 -1
- package/dist/mappers/learning.js +94 -14
- package/dist/mappers/learning.js.map +1 -1
- package/dist/mappers/orchestrator-state.d.ts +1 -1
- package/dist/mappers/orchestrator-state.d.ts.map +1 -1
- package/dist/mappers/orchestrator-state.js +31 -5
- package/dist/mappers/orchestrator-state.js.map +1 -1
- package/dist/mappers/parse-date.d.ts +11 -0
- package/dist/mappers/parse-date.d.ts.map +1 -0
- package/dist/mappers/parse-date.js +18 -0
- package/dist/mappers/parse-date.js.map +1 -0
- package/dist/mappers/run.d.ts +14 -4
- package/dist/mappers/run.d.ts.map +1 -1
- package/dist/mappers/run.js +49 -18
- package/dist/mappers/run.js.map +1 -1
- package/dist/mappers/task.d.ts +5 -1
- package/dist/mappers/task.d.ts.map +1 -1
- package/dist/mappers/task.js +66 -16
- package/dist/mappers/task.js.map +1 -1
- package/dist/mappers/tracked-project.d.ts +3 -1
- package/dist/mappers/tracked-project.d.ts.map +1 -1
- package/dist/mappers/tracked-project.js +23 -9
- package/dist/mappers/tracked-project.js.map +1 -1
- package/dist/mappers/worker.d.ts +1 -1
- package/dist/mappers/worker.d.ts.map +1 -1
- package/dist/mappers/worker.js +44 -6
- package/dist/mappers/worker.js.map +1 -1
- package/dist/repo/anchor-repo.d.ts +2 -2
- package/dist/repo/anchor-repo.d.ts.map +1 -1
- package/dist/repo/anchor-repo.js +46 -5
- package/dist/repo/anchor-repo.js.map +1 -1
- package/dist/repo/attempt-repo.d.ts +2 -2
- package/dist/repo/attempt-repo.d.ts.map +1 -1
- package/dist/repo/attempt-repo.js +16 -6
- package/dist/repo/attempt-repo.js.map +1 -1
- package/dist/repo/candidate-repo.d.ts.map +1 -1
- package/dist/repo/candidate-repo.js +22 -1
- package/dist/repo/candidate-repo.js.map +1 -1
- package/dist/repo/claim-repo.d.ts +46 -2
- package/dist/repo/claim-repo.d.ts.map +1 -1
- package/dist/repo/claim-repo.js +113 -6
- package/dist/repo/claim-repo.js.map +1 -1
- package/dist/repo/compaction-repo.d.ts +41 -0
- package/dist/repo/compaction-repo.d.ts.map +1 -0
- package/dist/repo/compaction-repo.js +84 -0
- package/dist/repo/compaction-repo.js.map +1 -0
- package/dist/repo/deduplication-repo.d.ts +9 -1
- package/dist/repo/deduplication-repo.d.ts.map +1 -1
- package/dist/repo/deduplication-repo.js +46 -9
- package/dist/repo/deduplication-repo.js.map +1 -1
- package/dist/repo/dep-repo.d.ts +27 -3
- package/dist/repo/dep-repo.d.ts.map +1 -1
- package/dist/repo/dep-repo.js +166 -39
- package/dist/repo/dep-repo.js.map +1 -1
- package/dist/repo/doc-repo.d.ts +59 -0
- package/dist/repo/doc-repo.d.ts.map +1 -0
- package/dist/repo/doc-repo.js +276 -0
- package/dist/repo/doc-repo.js.map +1 -0
- package/dist/repo/edge-repo.d.ts +1 -1
- package/dist/repo/edge-repo.d.ts.map +1 -1
- package/dist/repo/edge-repo.js +65 -34
- package/dist/repo/edge-repo.js.map +1 -1
- package/dist/repo/file-learning-repo.d.ts +3 -3
- package/dist/repo/file-learning-repo.d.ts.map +1 -1
- package/dist/repo/file-learning-repo.js +19 -8
- package/dist/repo/file-learning-repo.js.map +1 -1
- package/dist/repo/index.d.ts +4 -6
- package/dist/repo/index.d.ts.map +1 -1
- package/dist/repo/index.js +3 -5
- package/dist/repo/index.js.map +1 -1
- package/dist/repo/learning-repo.d.ts +10 -3
- package/dist/repo/learning-repo.d.ts.map +1 -1
- package/dist/repo/learning-repo.js +68 -11
- package/dist/repo/learning-repo.js.map +1 -1
- package/dist/repo/orchestrator-state-repo.d.ts.map +1 -1
- package/dist/repo/orchestrator-state-repo.js +8 -1
- package/dist/repo/orchestrator-state-repo.js.map +1 -1
- package/dist/repo/run-repo.d.ts +3 -3
- package/dist/repo/run-repo.d.ts.map +1 -1
- package/dist/repo/run-repo.js +40 -19
- package/dist/repo/run-repo.js.map +1 -1
- package/dist/repo/task-repo.d.ts +14 -3
- package/dist/repo/task-repo.d.ts.map +1 -1
- package/dist/repo/task-repo.js +194 -20
- package/dist/repo/task-repo.js.map +1 -1
- package/dist/repo/tracked-project-repo.d.ts.map +1 -1
- package/dist/repo/tracked-project-repo.js +15 -1
- package/dist/repo/tracked-project-repo.js.map +1 -1
- package/dist/repo/worker-repo.d.ts +3 -2
- package/dist/repo/worker-repo.d.ts.map +1 -1
- package/dist/repo/worker-repo.js +54 -8
- package/dist/repo/worker-repo.js.map +1 -1
- package/dist/schemas/sync.js +2 -2
- package/dist/schemas/sync.js.map +1 -1
- package/dist/schemas/worker.d.ts +1 -0
- package/dist/schemas/worker.d.ts.map +1 -1
- package/dist/schemas/worker.js +1 -0
- package/dist/schemas/worker.js.map +1 -1
- package/dist/services/agent-service.d.ts +57 -0
- package/dist/services/agent-service.d.ts.map +1 -0
- package/dist/services/agent-service.js +81 -0
- package/dist/services/agent-service.js.map +1 -0
- package/dist/services/anchor-service.js +1 -1
- package/dist/services/anchor-service.js.map +1 -1
- package/dist/services/anchor-verification.d.ts +8 -0
- package/dist/services/anchor-verification.d.ts.map +1 -1
- package/dist/services/anchor-verification.js +237 -37
- package/dist/services/anchor-verification.js.map +1 -1
- package/dist/services/ast-grep-service.d.ts.map +1 -1
- package/dist/services/ast-grep-service.js +93 -22
- package/dist/services/ast-grep-service.js.map +1 -1
- package/dist/services/attempt-service.d.ts.map +1 -1
- package/dist/services/attempt-service.js +1 -4
- package/dist/services/attempt-service.js.map +1 -1
- package/dist/services/auto-sync-service.d.ts +1 -1
- package/dist/services/auto-sync-service.d.ts.map +1 -1
- package/dist/services/auto-sync-service.js +18 -10
- package/dist/services/auto-sync-service.js.map +1 -1
- package/dist/services/claim-service.d.ts +8 -2
- package/dist/services/claim-service.d.ts.map +1 -1
- package/dist/services/claim-service.js +37 -23
- package/dist/services/claim-service.js.map +1 -1
- package/dist/services/compaction-service.d.ts +105 -0
- package/dist/services/compaction-service.d.ts.map +1 -0
- package/dist/services/compaction-service.js +369 -0
- package/dist/services/compaction-service.js.map +1 -0
- package/dist/services/cycle-scan-service.d.ts +32 -0
- package/dist/services/cycle-scan-service.d.ts.map +1 -0
- package/dist/services/cycle-scan-service.js +542 -0
- package/dist/services/cycle-scan-service.js.map +1 -0
- package/dist/services/daemon-service.d.ts +40 -2
- package/dist/services/daemon-service.d.ts.map +1 -1
- package/dist/services/daemon-service.js +199 -52
- package/dist/services/daemon-service.js.map +1 -1
- package/dist/services/deduplication-service.d.ts +8 -4
- package/dist/services/deduplication-service.d.ts.map +1 -1
- package/dist/services/deduplication-service.js +79 -25
- package/dist/services/deduplication-service.js.map +1 -1
- package/dist/services/dep-service.d.ts +2 -2
- package/dist/services/dep-service.d.ts.map +1 -1
- package/dist/services/dep-service.js +9 -5
- package/dist/services/dep-service.js.map +1 -1
- package/dist/services/diversifier-service.d.ts +2 -1
- package/dist/services/diversifier-service.d.ts.map +1 -1
- package/dist/services/diversifier-service.js +37 -43
- package/dist/services/diversifier-service.js.map +1 -1
- package/dist/services/doc-service.d.ts +49 -0
- package/dist/services/doc-service.d.ts.map +1 -0
- package/dist/services/doc-service.js +605 -0
- package/dist/services/doc-service.js.map +1 -0
- package/dist/services/edge-service.js +2 -2
- package/dist/services/edge-service.js.map +1 -1
- package/dist/services/embedding-service.d.ts +66 -2
- package/dist/services/embedding-service.d.ts.map +1 -1
- package/dist/services/embedding-service.js +138 -24
- package/dist/services/embedding-service.js.map +1 -1
- package/dist/services/file-learning-service.d.ts.map +1 -1
- package/dist/services/file-learning-service.js +8 -7
- package/dist/services/file-learning-service.js.map +1 -1
- package/dist/services/file-watcher-service.d.ts.map +1 -1
- package/dist/services/file-watcher-service.js +58 -11
- package/dist/services/file-watcher-service.js.map +1 -1
- package/dist/services/graph-expansion.d.ts +3 -0
- package/dist/services/graph-expansion.d.ts.map +1 -1
- package/dist/services/graph-expansion.js +28 -7
- package/dist/services/graph-expansion.js.map +1 -1
- package/dist/services/hierarchy-service.d.ts +1 -1
- package/dist/services/hierarchy-service.d.ts.map +1 -1
- package/dist/services/hierarchy-service.js +50 -32
- package/dist/services/hierarchy-service.js.map +1 -1
- package/dist/services/index.d.ts +13 -15
- package/dist/services/index.d.ts.map +1 -1
- package/dist/services/index.js +13 -15
- package/dist/services/index.js.map +1 -1
- package/dist/services/learning-service.d.ts +4 -4
- package/dist/services/learning-service.d.ts.map +1 -1
- package/dist/services/learning-service.js +75 -42
- package/dist/services/learning-service.js.map +1 -1
- package/dist/services/llm-service.d.ts +62 -0
- package/dist/services/llm-service.d.ts.map +1 -0
- package/dist/services/llm-service.js +172 -0
- package/dist/services/llm-service.js.map +1 -0
- package/dist/services/migration-service.d.ts +1 -1
- package/dist/services/migration-service.d.ts.map +1 -1
- package/dist/services/migration-service.js +18 -7
- package/dist/services/migration-service.js.map +1 -1
- package/dist/services/orchestrator-service.d.ts +4 -3
- package/dist/services/orchestrator-service.d.ts.map +1 -1
- package/dist/services/orchestrator-service.js +67 -29
- package/dist/services/orchestrator-service.js.map +1 -1
- package/dist/services/promotion-service.d.ts +1 -1
- package/dist/services/promotion-service.js +1 -1
- package/dist/services/promotion-service.js.map +1 -1
- package/dist/services/query-expansion-service.d.ts +30 -9
- package/dist/services/query-expansion-service.d.ts.map +1 -1
- package/dist/services/query-expansion-service.js +54 -63
- package/dist/services/query-expansion-service.js.map +1 -1
- package/dist/services/ready-service.d.ts +21 -1
- package/dist/services/ready-service.d.ts.map +1 -1
- package/dist/services/ready-service.js +44 -21
- package/dist/services/ready-service.js.map +1 -1
- package/dist/services/retriever-service.d.ts +10 -10
- package/dist/services/retriever-service.d.ts.map +1 -1
- package/dist/services/retriever-service.js +53 -161
- package/dist/services/retriever-service.js.map +1 -1
- package/dist/services/swarm-verification.d.ts +2 -2
- package/dist/services/swarm-verification.d.ts.map +1 -1
- package/dist/services/swarm-verification.js +12 -6
- package/dist/services/swarm-verification.js.map +1 -1
- package/dist/services/sync-service.d.ts +17 -4
- package/dist/services/sync-service.d.ts.map +1 -1
- package/dist/services/sync-service.js +378 -114
- package/dist/services/sync-service.js.map +1 -1
- package/dist/services/task-service.d.ts +6 -4
- package/dist/services/task-service.d.ts.map +1 -1
- package/dist/services/task-service.js +162 -33
- package/dist/services/task-service.js.map +1 -1
- package/dist/services/tracing-service.d.ts +55 -0
- package/dist/services/tracing-service.d.ts.map +1 -0
- package/dist/services/tracing-service.js +99 -0
- package/dist/services/tracing-service.js.map +1 -0
- package/dist/services/transcript-adapter.d.ts +99 -0
- package/dist/services/transcript-adapter.d.ts.map +1 -0
- package/dist/services/transcript-adapter.js +283 -0
- package/dist/services/transcript-adapter.js.map +1 -0
- package/dist/services/validation-service.d.ts +85 -0
- package/dist/services/validation-service.d.ts.map +1 -0
- package/dist/services/validation-service.js +289 -0
- package/dist/services/validation-service.js.map +1 -0
- package/dist/services/worker-process.d.ts +23 -4
- package/dist/services/worker-process.d.ts.map +1 -1
- package/dist/services/worker-process.js +159 -70
- package/dist/services/worker-process.js.map +1 -1
- package/dist/services/worker-service.d.ts.map +1 -1
- package/dist/services/worker-service.js +7 -12
- package/dist/services/worker-service.js.map +1 -1
- package/dist/sync/claude-task-writer.d.ts +49 -0
- package/dist/sync/claude-task-writer.d.ts.map +1 -0
- package/dist/sync/claude-task-writer.js +135 -0
- package/dist/sync/claude-task-writer.js.map +1 -0
- package/dist/utils/doc-hash.d.ts +10 -0
- package/dist/utils/doc-hash.d.ts.map +1 -0
- package/dist/utils/doc-hash.js +14 -0
- package/dist/utils/doc-hash.js.map +1 -0
- package/dist/utils/doc-renderer.d.ts +44 -0
- package/dist/utils/doc-renderer.d.ts.map +1 -0
- package/dist/utils/doc-renderer.js +202 -0
- package/dist/utils/doc-renderer.js.map +1 -0
- package/dist/utils/math.d.ts +5 -1
- package/dist/utils/math.d.ts.map +1 -1
- package/dist/utils/math.js +12 -4
- package/dist/utils/math.js.map +1 -1
- package/dist/utils/sql.d.ts +9 -0
- package/dist/utils/sql.d.ts.map +1 -0
- package/dist/utils/sql.js +9 -0
- package/dist/utils/sql.js.map +1 -0
- package/dist/utils/toml-config.d.ts +22 -0
- package/dist/utils/toml-config.d.ts.map +1 -0
- package/dist/utils/toml-config.js +75 -0
- package/dist/utils/toml-config.js.map +1 -0
- package/dist/worker/hooks.d.ts +102 -0
- package/dist/worker/hooks.d.ts.map +1 -0
- package/dist/worker/hooks.js +11 -0
- package/dist/worker/hooks.js.map +1 -0
- package/dist/worker/index.d.ts +9 -0
- package/dist/worker/index.d.ts.map +1 -0
- package/dist/worker/index.js +8 -0
- package/dist/worker/index.js.map +1 -0
- package/dist/worker/run-worker.d.ts +33 -0
- package/dist/worker/run-worker.d.ts.map +1 -0
- package/dist/worker/run-worker.js +265 -0
- package/dist/worker/run-worker.js.map +1 -0
- package/package.json +14 -12
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from "effect";
|
|
2
|
+
import { SqliteClient } from "../db.js";
|
|
3
|
+
import { DatabaseError, EntityFetchError } from "../errors.js";
|
|
4
|
+
/**
|
|
5
|
+
* Safely parse a JSON array of task ID strings, returning empty array on failure.
|
|
6
|
+
*/
|
|
7
|
+
const safeParseTaskIds = (json) => {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(json || "[]");
|
|
10
|
+
return Array.isArray(parsed) ? parsed.filter((v) => typeof v === "string") : [];
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Map database row to domain entity.
|
|
18
|
+
*/
|
|
19
|
+
const rowToCompactionLogEntry = (row) => ({
|
|
20
|
+
id: row.id,
|
|
21
|
+
compactedAt: new Date(row.compacted_at),
|
|
22
|
+
taskCount: row.task_count,
|
|
23
|
+
summary: row.summary,
|
|
24
|
+
taskIds: safeParseTaskIds(row.task_ids),
|
|
25
|
+
learningsExportedTo: row.learnings_exported_to,
|
|
26
|
+
learnings: row.learnings
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Repository for compaction_log table operations.
|
|
30
|
+
* Handles storage of compaction history and summaries.
|
|
31
|
+
*/
|
|
32
|
+
export class CompactionRepository extends Context.Tag("CompactionRepository")() {
|
|
33
|
+
}
|
|
34
|
+
export const CompactionRepositoryLive = Layer.effect(CompactionRepository, Effect.gen(function* () {
|
|
35
|
+
const db = yield* SqliteClient;
|
|
36
|
+
return {
|
|
37
|
+
insert: (input) => Effect.try({
|
|
38
|
+
try: () => {
|
|
39
|
+
const now = new Date().toISOString();
|
|
40
|
+
const result = db.prepare(`INSERT INTO compaction_log (compacted_at, task_count, summary, task_ids, learnings_exported_to, learnings)
|
|
41
|
+
VALUES (?, ?, ?, ?, ?, ?)`).run(now, input.taskCount, input.summary, JSON.stringify(input.taskIds), input.learningsExportedTo ?? null, input.learnings);
|
|
42
|
+
const row = db.prepare("SELECT * FROM compaction_log WHERE id = ?").get(result.lastInsertRowid);
|
|
43
|
+
if (!row) {
|
|
44
|
+
throw new EntityFetchError({
|
|
45
|
+
entity: "compaction_log",
|
|
46
|
+
id: result.lastInsertRowid,
|
|
47
|
+
operation: "insert"
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return rowToCompactionLogEntry(row);
|
|
51
|
+
},
|
|
52
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
53
|
+
}),
|
|
54
|
+
findById: (id) => Effect.try({
|
|
55
|
+
try: () => {
|
|
56
|
+
const row = db.prepare("SELECT * FROM compaction_log WHERE id = ?").get(id);
|
|
57
|
+
return row ? rowToCompactionLogEntry(row) : null;
|
|
58
|
+
},
|
|
59
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
60
|
+
}),
|
|
61
|
+
findAll: () => Effect.try({
|
|
62
|
+
try: () => {
|
|
63
|
+
const rows = db.prepare(`SELECT * FROM compaction_log ORDER BY compacted_at DESC`).all();
|
|
64
|
+
return rows.map(rowToCompactionLogEntry);
|
|
65
|
+
},
|
|
66
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
67
|
+
}),
|
|
68
|
+
findRecent: (limit) => Effect.try({
|
|
69
|
+
try: () => {
|
|
70
|
+
const rows = db.prepare(`SELECT * FROM compaction_log ORDER BY compacted_at DESC LIMIT ?`).all(limit);
|
|
71
|
+
return rows.map(rowToCompactionLogEntry);
|
|
72
|
+
},
|
|
73
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
74
|
+
}),
|
|
75
|
+
count: () => Effect.try({
|
|
76
|
+
try: () => {
|
|
77
|
+
const result = db.prepare("SELECT COUNT(*) as cnt FROM compaction_log").get();
|
|
78
|
+
return result.cnt;
|
|
79
|
+
},
|
|
80
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
81
|
+
})
|
|
82
|
+
};
|
|
83
|
+
}));
|
|
84
|
+
//# sourceMappingURL=compaction-repo.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compaction-repo.js","sourceRoot":"","sources":["../../src/repo/compaction-repo.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,6 +9,14 @@ declare const DeduplicationRepository_base: Context.TagClass<DeduplicationReposi
|
|
|
9
9
|
readonly hashesExist: (contentHashes: readonly string[]) => Effect.Effect<Set<string>, DatabaseError>;
|
|
10
10
|
/** Record a processed hash */
|
|
11
11
|
readonly insertHash: (input: CreateProcessedHashInput) => Effect.Effect<ProcessedHash, DatabaseError>;
|
|
12
|
+
/**
|
|
13
|
+
* Atomically try to insert a hash, returning whether it was actually inserted.
|
|
14
|
+
* Uses INSERT OR IGNORE to handle race conditions safely.
|
|
15
|
+
* Returns { inserted: true } if this was a new hash, { inserted: false } if it already existed.
|
|
16
|
+
*/
|
|
17
|
+
readonly tryInsertHash: (input: CreateProcessedHashInput) => Effect.Effect<{
|
|
18
|
+
inserted: boolean;
|
|
19
|
+
}, DatabaseError>;
|
|
12
20
|
/** Record multiple hashes at once (batch operation) */
|
|
13
21
|
readonly insertHashes: (inputs: readonly CreateProcessedHashInput[]) => Effect.Effect<number, DatabaseError>;
|
|
14
22
|
/** Find a hash record by content hash */
|
|
@@ -24,7 +32,7 @@ declare const DeduplicationRepository_base: Context.TagClass<DeduplicationReposi
|
|
|
24
32
|
/** Update or create progress for a file */
|
|
25
33
|
readonly upsertFileProgress: (input: UpsertFileProgressInput) => Effect.Effect<FileProgress, DatabaseError>;
|
|
26
34
|
/** Delete progress for a file */
|
|
27
|
-
readonly deleteFileProgress: (filePath: string) => Effect.Effect<
|
|
35
|
+
readonly deleteFileProgress: (filePath: string) => Effect.Effect<number, DatabaseError>;
|
|
28
36
|
/** Get all tracked files */
|
|
29
37
|
readonly getAllFileProgress: () => Effect.Effect<readonly FileProgress[], DatabaseError>;
|
|
30
38
|
/** Count tracked files */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deduplication-repo.d.ts","sourceRoot":"","sources":["../../src/repo/deduplication-repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"deduplication-repo.d.ts","sourceRoot":"","sources":["../../src/repo/deduplication-repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,aAAa,EAAoB,MAAM,cAAc,CAAA;AAE9D,OAAO,KAAK,EACV,aAAa,EAEb,wBAAwB,EACxB,YAAY,EAEZ,uBAAuB,EACxB,MAAM,yBAAyB,CAAA;;IAO5B,6CAA6C;yBACxB,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC;IAEnF,sDAAsD;0BAChC,CAAC,aAAa,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,aAAa,CAAC;IAErG,8BAA8B;yBACT,CAAC,KAAK,EAAE,wBAAwB,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,aAAa,CAAC;IAErG;;;;OAIG;4BACqB,CAAC,KAAK,EAAE,wBAAwB,KAAK,MAAM,CAAC,MAAM,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,EAAE,aAAa,CAAC;IAEhH,uDAAuD;2BAChC,CAAC,MAAM,EAAE,SAAS,wBAAwB,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;IAE5G,yCAAyC;yBACpB,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,GAAG,IAAI,EAAE,aAAa,CAAC;IAEhG,0CAA0C;0BACpB,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;IAEhE,qCAAqC;+BACV,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,aAAa,EAAE,EAAE,aAAa,CAAC;IAEvG,sDAAsD;kCACxB,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;IAIxF,8BAA8B;8BACJ,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,YAAY,GAAG,IAAI,EAAE,aAAa,CAAC;IAEjG,2CAA2C;iCACd,CAAC,KAAK,EAAE,uBAAuB,KAAK,MAAM,CAAC,MAAM,CAAC,YAAY,EAAE,aAAa,CAAC;IAE3G,iCAAiC;iCACJ,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;IAEvF,4BAA4B;iCACC,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,YAAY,EAAE,EAAE,aAAa,CAAC;IAExF,0BAA0B;yBACL,MAAM,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;;AAnDnE,qBAAa,uBAAwB,SAAQ,4BAqD1C;CAAG;AAEN,eAAO,MAAM,2BAA2B,2DAsOvC,CAAA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Context, Effect, Layer } from "effect";
|
|
2
2
|
import { SqliteClient } from "../db.js";
|
|
3
|
-
import { DatabaseError } from "../errors.js";
|
|
3
|
+
import { DatabaseError, EntityFetchError } from "../errors.js";
|
|
4
4
|
import { rowToProcessedHash, rowToFileProgress } from "../mappers/deduplication.js";
|
|
5
5
|
export class DeduplicationRepository extends Context.Tag("DeduplicationRepository")() {
|
|
6
6
|
}
|
|
@@ -11,7 +11,7 @@ export const DeduplicationRepositoryLive = Layer.effect(DeduplicationRepository,
|
|
|
11
11
|
hashExists: (contentHash) => Effect.try({
|
|
12
12
|
try: () => {
|
|
13
13
|
const row = db.prepare("SELECT 1 FROM processed_hashes WHERE content_hash = ? LIMIT 1").get(contentHash);
|
|
14
|
-
return row
|
|
14
|
+
return row != null;
|
|
15
15
|
},
|
|
16
16
|
catch: (cause) => new DatabaseError({ cause })
|
|
17
17
|
}),
|
|
@@ -33,10 +33,30 @@ export const DeduplicationRepositoryLive = Layer.effect(DeduplicationRepository,
|
|
|
33
33
|
VALUES (?, ?, ?)
|
|
34
34
|
`).run(input.contentHash, input.sourceFile, input.sourceLine);
|
|
35
35
|
const row = db.prepare("SELECT * FROM processed_hashes WHERE content_hash = ?").get(input.contentHash);
|
|
36
|
+
if (!row) {
|
|
37
|
+
throw new EntityFetchError({
|
|
38
|
+
entity: "processed_hash",
|
|
39
|
+
id: input.contentHash,
|
|
40
|
+
operation: "insert"
|
|
41
|
+
});
|
|
42
|
+
}
|
|
36
43
|
return rowToProcessedHash(row);
|
|
37
44
|
},
|
|
38
45
|
catch: (cause) => new DatabaseError({ cause })
|
|
39
46
|
}),
|
|
47
|
+
tryInsertHash: (input) => Effect.try({
|
|
48
|
+
try: () => {
|
|
49
|
+
// Use INSERT OR IGNORE to atomically handle duplicates
|
|
50
|
+
// This is race-condition safe: if two processes try to insert
|
|
51
|
+
// the same hash concurrently, one succeeds and the other gets changes=0
|
|
52
|
+
const result = db.prepare(`
|
|
53
|
+
INSERT OR IGNORE INTO processed_hashes (content_hash, source_file, source_line)
|
|
54
|
+
VALUES (?, ?, ?)
|
|
55
|
+
`).run(input.contentHash, input.sourceFile, input.sourceLine);
|
|
56
|
+
return { inserted: result.changes > 0 };
|
|
57
|
+
},
|
|
58
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
59
|
+
}),
|
|
40
60
|
insertHashes: (inputs) => Effect.try({
|
|
41
61
|
try: () => {
|
|
42
62
|
if (inputs.length === 0)
|
|
@@ -45,13 +65,22 @@ export const DeduplicationRepositoryLive = Layer.effect(DeduplicationRepository,
|
|
|
45
65
|
INSERT OR IGNORE INTO processed_hashes (content_hash, source_file, source_line)
|
|
46
66
|
VALUES (?, ?, ?)
|
|
47
67
|
`);
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
68
|
+
// Use a transaction for atomicity and performance
|
|
69
|
+
db.exec("BEGIN IMMEDIATE");
|
|
70
|
+
try {
|
|
71
|
+
let inserted = 0;
|
|
72
|
+
for (const input of inputs) {
|
|
73
|
+
const result = stmt.run(input.contentHash, input.sourceFile, input.sourceLine);
|
|
74
|
+
if (result.changes > 0)
|
|
75
|
+
inserted++;
|
|
76
|
+
}
|
|
77
|
+
db.exec("COMMIT");
|
|
78
|
+
return inserted;
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
db.exec("ROLLBACK");
|
|
82
|
+
throw e;
|
|
53
83
|
}
|
|
54
|
-
return inserted;
|
|
55
84
|
},
|
|
56
85
|
catch: (cause) => new DatabaseError({ cause })
|
|
57
86
|
}),
|
|
@@ -104,13 +133,21 @@ export const DeduplicationRepositoryLive = Layer.effect(DeduplicationRepository,
|
|
|
104
133
|
last_processed_at = datetime('now')
|
|
105
134
|
`).run(input.filePath, input.lastLineProcessed, input.lastByteOffset, input.fileSize ?? null, input.fileChecksum ?? null);
|
|
106
135
|
const row = db.prepare("SELECT * FROM file_progress WHERE file_path = ?").get(input.filePath);
|
|
136
|
+
if (!row) {
|
|
137
|
+
throw new EntityFetchError({
|
|
138
|
+
entity: "file_progress",
|
|
139
|
+
id: input.filePath,
|
|
140
|
+
operation: "insert"
|
|
141
|
+
});
|
|
142
|
+
}
|
|
107
143
|
return rowToFileProgress(row);
|
|
108
144
|
},
|
|
109
145
|
catch: (cause) => new DatabaseError({ cause })
|
|
110
146
|
}),
|
|
111
147
|
deleteFileProgress: (filePath) => Effect.try({
|
|
112
148
|
try: () => {
|
|
113
|
-
db.prepare("DELETE FROM file_progress WHERE file_path = ?").run(filePath);
|
|
149
|
+
const result = db.prepare("DELETE FROM file_progress WHERE file_path = ?").run(filePath);
|
|
150
|
+
return result.changes;
|
|
114
151
|
},
|
|
115
152
|
catch: (cause) => new DatabaseError({ cause })
|
|
116
153
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deduplication-repo.js","sourceRoot":"","sources":["../../src/repo/deduplication-repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"deduplication-repo.js","sourceRoot":"","sources":["../../src/repo/deduplication-repo.ts"],"names":[],"mappings":"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"}
|
package/dist/repo/dep-repo.d.ts
CHANGED
|
@@ -1,16 +1,40 @@
|
|
|
1
1
|
import { Context, Effect, Layer } from "effect";
|
|
2
2
|
import { SqliteClient } from "../db.js";
|
|
3
|
-
import { DatabaseError } from "../errors.js";
|
|
3
|
+
import { DatabaseError, DependencyNotFoundError } from "../errors.js";
|
|
4
4
|
import type { TaskId, TaskDependency } from "@jamesaphoenix/tx-types";
|
|
5
|
+
/**
|
|
6
|
+
* Result of atomic cycle-check-and-insert operation.
|
|
7
|
+
*/
|
|
8
|
+
export type InsertWithCycleCheckResult = {
|
|
9
|
+
readonly _tag: "inserted";
|
|
10
|
+
} | {
|
|
11
|
+
readonly _tag: "wouldCycle";
|
|
12
|
+
} | {
|
|
13
|
+
readonly _tag: "alreadyExists";
|
|
14
|
+
};
|
|
5
15
|
declare const DependencyRepository_base: Context.TagClass<DependencyRepository, "DependencyRepository", {
|
|
6
16
|
readonly insert: (blockerId: string, blockedId: string) => Effect.Effect<void, DatabaseError>;
|
|
7
|
-
readonly remove: (blockerId: string, blockedId: string) => Effect.Effect<void, DatabaseError>;
|
|
17
|
+
readonly remove: (blockerId: string, blockedId: string) => Effect.Effect<void, DatabaseError | DependencyNotFoundError>;
|
|
8
18
|
readonly getBlockerIds: (blockedId: string) => Effect.Effect<readonly TaskId[], DatabaseError>;
|
|
9
19
|
readonly getBlockingIds: (blockerId: string) => Effect.Effect<readonly TaskId[], DatabaseError>;
|
|
10
20
|
readonly getBlockerIdsForMany: (blockedIds: readonly string[]) => Effect.Effect<Map<string, readonly TaskId[]>, DatabaseError>;
|
|
11
21
|
readonly getBlockingIdsForMany: (blockerIds: readonly string[]) => Effect.Effect<Map<string, readonly TaskId[]>, DatabaseError>;
|
|
12
22
|
readonly hasPath: (fromId: string, toId: string) => Effect.Effect<boolean, DatabaseError>;
|
|
13
|
-
readonly getAll: () => Effect.Effect<readonly TaskDependency[], DatabaseError>;
|
|
23
|
+
readonly getAll: (limit?: number) => Effect.Effect<readonly TaskDependency[], DatabaseError>;
|
|
24
|
+
/**
|
|
25
|
+
* Remove all dependency edges where any of the given task IDs appear
|
|
26
|
+
* as either blocker_id or blocked_id. Used during cascade delete to
|
|
27
|
+
* explicitly clean up edges rather than relying solely on FK CASCADE.
|
|
28
|
+
*/
|
|
29
|
+
readonly removeByTaskIds: (taskIds: readonly string[]) => Effect.Effect<void, DatabaseError>;
|
|
30
|
+
/**
|
|
31
|
+
* Atomically check for cycles and insert dependency in a single transaction.
|
|
32
|
+
* This prevents race conditions where two concurrent addBlocker calls could
|
|
33
|
+
* both pass cycle detection before either inserts.
|
|
34
|
+
*
|
|
35
|
+
* Uses BEGIN IMMEDIATE to acquire write lock before cycle check.
|
|
36
|
+
*/
|
|
37
|
+
readonly insertWithCycleCheck: (blockerId: string, blockedId: string) => Effect.Effect<InsertWithCycleCheckResult, DatabaseError>;
|
|
14
38
|
}>;
|
|
15
39
|
export declare class DependencyRepository extends DependencyRepository_base {
|
|
16
40
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dep-repo.d.ts","sourceRoot":"","sources":["../../src/repo/dep-repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"dep-repo.d.ts","sourceRoot":"","sources":["../../src/repo/dep-repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,uBAAuB,EAA2B,MAAM,cAAc,CAAA;AAG9F,OAAO,KAAK,EAAE,MAAM,EAAE,cAAc,EAAiB,MAAM,yBAAyB,CAAA;AAYpF;;GAEG;AACH,MAAM,MAAM,0BAA0B,GAClC;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAC7B;IAAE,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAA;CAAE,GAC/B;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;CAAE,CAAA;;qBAKjB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC;qBAC5E,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,uBAAuB,CAAC;4BAC/F,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,MAAM,EAAE,EAAE,aAAa,CAAC;6BACrE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,MAAM,EAAE,EAAE,aAAa,CAAC;mCAChE,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,EAAE,aAAa,CAAC;oCAC9F,CAAC,UAAU,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,EAAE,aAAa,CAAC;sBAC7G,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE,aAAa,CAAC;qBACxE,CAAC,KAAK,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,cAAc,EAAE,EAAE,aAAa,CAAC;IAC5F;;;;OAIG;8BACuB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,CAAC;IAC5F;;;;;;OAMG;mCAC4B,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,0BAA0B,EAAE,aAAa,CAAC;;AAxBrI,qBAAa,oBAAqB,SAAQ,yBA0BvC;CAAG;AAEN,eAAO,MAAM,wBAAwB,wDAqRpC,CAAA"}
|
package/dist/repo/dep-repo.js
CHANGED
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { Context, Effect, Layer } from "effect";
|
|
2
2
|
import { SqliteClient } from "../db.js";
|
|
3
|
-
import { DatabaseError } from "../errors.js";
|
|
3
|
+
import { DatabaseError, DependencyNotFoundError, UnexpectedRowCountError } from "../errors.js";
|
|
4
4
|
import { rowToDependency } from "../mappers/task.js";
|
|
5
|
+
import { DEFAULT_QUERY_LIMIT } from "../utils/sql.js";
|
|
6
|
+
// Shared frozen empty array to avoid allocating new arrays for IDs with no dependencies
|
|
7
|
+
const EMPTY_TASK_IDS = Object.freeze([]);
|
|
8
|
+
/**
|
|
9
|
+
* Maximum recursion depth for dependency graph traversal.
|
|
10
|
+
* Prevents unbounded recursive CTEs from hitting SQLite's internal
|
|
11
|
+
* 1000-level recursion limit on deep dependency chains.
|
|
12
|
+
*/
|
|
13
|
+
const MAX_DEPENDENCY_DEPTH = 100;
|
|
5
14
|
export class DependencyRepository extends Context.Tag("DependencyRepository")() {
|
|
6
15
|
}
|
|
7
16
|
export const DependencyRepositoryLive = Layer.effect(DependencyRepository, Effect.gen(function* () {
|
|
@@ -9,15 +18,25 @@ export const DependencyRepositoryLive = Layer.effect(DependencyRepository, Effec
|
|
|
9
18
|
return {
|
|
10
19
|
insert: (blockerId, blockedId) => Effect.try({
|
|
11
20
|
try: () => {
|
|
12
|
-
db.prepare("INSERT INTO task_dependencies (blocker_id, blocked_id, created_at) VALUES (?, ?, ?)").run(blockerId, blockedId, new Date().toISOString());
|
|
21
|
+
const result = db.prepare("INSERT INTO task_dependencies (blocker_id, blocked_id, created_at) VALUES (?, ?, ?)").run(blockerId, blockedId, new Date().toISOString());
|
|
22
|
+
if (result.changes !== 1) {
|
|
23
|
+
throw new UnexpectedRowCountError({
|
|
24
|
+
operation: "dependency insert",
|
|
25
|
+
expected: 1,
|
|
26
|
+
actual: result.changes
|
|
27
|
+
});
|
|
28
|
+
}
|
|
13
29
|
},
|
|
14
30
|
catch: (cause) => new DatabaseError({ cause })
|
|
15
31
|
}),
|
|
16
|
-
remove: (blockerId, blockedId) => Effect.
|
|
17
|
-
|
|
18
|
-
db.prepare("DELETE FROM task_dependencies WHERE blocker_id = ? AND blocked_id = ?").run(blockerId, blockedId)
|
|
19
|
-
|
|
20
|
-
|
|
32
|
+
remove: (blockerId, blockedId) => Effect.gen(function* () {
|
|
33
|
+
const result = yield* Effect.try({
|
|
34
|
+
try: () => db.prepare("DELETE FROM task_dependencies WHERE blocker_id = ? AND blocked_id = ?").run(blockerId, blockedId),
|
|
35
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
36
|
+
});
|
|
37
|
+
if (result.changes === 0) {
|
|
38
|
+
yield* Effect.fail(new DependencyNotFoundError({ blockerId, blockedId }));
|
|
39
|
+
}
|
|
21
40
|
}),
|
|
22
41
|
getBlockerIds: (blockedId) => Effect.try({
|
|
23
42
|
try: () => {
|
|
@@ -40,14 +59,20 @@ export const DependencyRepositoryLive = Layer.effect(DependencyRepository, Effec
|
|
|
40
59
|
return result;
|
|
41
60
|
const placeholders = blockedIds.map(() => "?").join(",");
|
|
42
61
|
const rows = db.prepare(`SELECT blocked_id, blocker_id FROM task_dependencies WHERE blocked_id IN (${placeholders})`).all(...blockedIds);
|
|
43
|
-
//
|
|
44
|
-
|
|
45
|
-
result.set(id, []);
|
|
46
|
-
}
|
|
47
|
-
// Group by blocked_id
|
|
62
|
+
// Group rows by blocked_id in a temporary mutable map
|
|
63
|
+
const grouped = new Map();
|
|
48
64
|
for (const row of rows) {
|
|
49
|
-
const existing =
|
|
50
|
-
|
|
65
|
+
const existing = grouped.get(row.blocked_id);
|
|
66
|
+
if (existing) {
|
|
67
|
+
existing.push(row.blocker_id);
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
grouped.set(row.blocked_id, [row.blocker_id]);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Populate result: use grouped array or shared empty array
|
|
74
|
+
for (const id of blockedIds) {
|
|
75
|
+
result.set(id, grouped.get(id) ?? EMPTY_TASK_IDS);
|
|
51
76
|
}
|
|
52
77
|
return result;
|
|
53
78
|
},
|
|
@@ -60,44 +85,146 @@ export const DependencyRepositoryLive = Layer.effect(DependencyRepository, Effec
|
|
|
60
85
|
return result;
|
|
61
86
|
const placeholders = blockerIds.map(() => "?").join(",");
|
|
62
87
|
const rows = db.prepare(`SELECT blocker_id, blocked_id FROM task_dependencies WHERE blocker_id IN (${placeholders})`).all(...blockerIds);
|
|
63
|
-
//
|
|
64
|
-
|
|
65
|
-
result.set(id, []);
|
|
66
|
-
}
|
|
67
|
-
// Group by blocker_id
|
|
88
|
+
// Group rows by blocker_id in a temporary mutable map
|
|
89
|
+
const grouped = new Map();
|
|
68
90
|
for (const row of rows) {
|
|
69
|
-
const existing =
|
|
70
|
-
|
|
91
|
+
const existing = grouped.get(row.blocker_id);
|
|
92
|
+
if (existing) {
|
|
93
|
+
existing.push(row.blocked_id);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
grouped.set(row.blocker_id, [row.blocked_id]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Populate result: use grouped array or shared empty array
|
|
100
|
+
for (const id of blockerIds) {
|
|
101
|
+
result.set(id, grouped.get(id) ?? EMPTY_TASK_IDS);
|
|
71
102
|
}
|
|
72
103
|
return result;
|
|
73
104
|
},
|
|
74
105
|
catch: (cause) => new DatabaseError({ cause })
|
|
75
106
|
}),
|
|
76
|
-
//
|
|
77
|
-
|
|
107
|
+
// Cycle detection using recursive CTE: can we reach toId by following blocker chains from fromId?
|
|
108
|
+
// Uses UNION (not UNION ALL) to automatically prevent infinite recursion on existing cycles
|
|
109
|
+
// Depth-limited to MAX_DEPENDENCY_DEPTH to prevent hitting SQLite's internal 1000-level limit
|
|
110
|
+
hasPath: (fromId, toId) => Effect.gen(function* () {
|
|
111
|
+
// Special case: same ID
|
|
112
|
+
if (fromId === toId)
|
|
113
|
+
return true;
|
|
114
|
+
// Recursive CTE performs BFS in a single query
|
|
115
|
+
// UNION deduplicates visited nodes, preventing infinite loops
|
|
116
|
+
// depth tracking prevents unbounded recursion on deep chains
|
|
117
|
+
const result = yield* Effect.try({
|
|
118
|
+
try: () => db.prepare(`
|
|
119
|
+
WITH RECURSIVE reachable(id, depth) AS (
|
|
120
|
+
-- Base case: direct blockers of fromId (depth 1)
|
|
121
|
+
SELECT blocker_id, 1 FROM task_dependencies WHERE blocked_id = ?
|
|
122
|
+
UNION
|
|
123
|
+
-- Recursive case: follow blocker chain, bounded by depth limit
|
|
124
|
+
SELECT d.blocker_id, r.depth + 1
|
|
125
|
+
FROM task_dependencies d
|
|
126
|
+
JOIN reachable r ON d.blocked_id = r.id
|
|
127
|
+
WHERE r.depth < ?
|
|
128
|
+
)
|
|
129
|
+
SELECT
|
|
130
|
+
MAX(CASE WHEN id = ? THEN 1 ELSE 0 END) AS found,
|
|
131
|
+
MAX(depth) AS max_depth
|
|
132
|
+
FROM reachable
|
|
133
|
+
`).get(fromId, MAX_DEPENDENCY_DEPTH, toId),
|
|
134
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
135
|
+
});
|
|
136
|
+
const pathFound = result?.found === 1;
|
|
137
|
+
const depthLimitHit = result?.max_depth != null && result.max_depth >= MAX_DEPENDENCY_DEPTH;
|
|
138
|
+
if (depthLimitHit) {
|
|
139
|
+
yield* Effect.logWarning(`Dependency depth limit (${MAX_DEPENDENCY_DEPTH}) reached checking path from ${fromId} to ${toId}. Graph may have unexplored deeper chains.`);
|
|
140
|
+
}
|
|
141
|
+
return pathFound;
|
|
142
|
+
}),
|
|
143
|
+
getAll: (limit) => Effect.try({
|
|
78
144
|
try: () => {
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
while (queue.length > 0) {
|
|
82
|
-
const current = queue.shift();
|
|
83
|
-
if (current === toId)
|
|
84
|
-
return true;
|
|
85
|
-
if (visited.has(current))
|
|
86
|
-
continue;
|
|
87
|
-
visited.add(current);
|
|
88
|
-
const rows = db.prepare("SELECT blocker_id FROM task_dependencies WHERE blocked_id = ?").all(current);
|
|
89
|
-
queue.push(...rows.map(r => r.blocker_id));
|
|
90
|
-
}
|
|
91
|
-
return false;
|
|
145
|
+
const rows = db.prepare("SELECT blocker_id, blocked_id, created_at FROM task_dependencies LIMIT ?").all(limit ?? DEFAULT_QUERY_LIMIT);
|
|
146
|
+
return rows.map(rowToDependency);
|
|
92
147
|
},
|
|
93
148
|
catch: (cause) => new DatabaseError({ cause })
|
|
94
149
|
}),
|
|
95
|
-
|
|
150
|
+
removeByTaskIds: (taskIds) => Effect.try({
|
|
96
151
|
try: () => {
|
|
97
|
-
|
|
98
|
-
|
|
152
|
+
if (taskIds.length === 0)
|
|
153
|
+
return;
|
|
154
|
+
const placeholders = taskIds.map(() => "?").join(",");
|
|
155
|
+
db.prepare(`DELETE FROM task_dependencies WHERE blocker_id IN (${placeholders}) OR blocked_id IN (${placeholders})`).run(...taskIds, ...taskIds);
|
|
99
156
|
},
|
|
100
157
|
catch: (cause) => new DatabaseError({ cause })
|
|
158
|
+
}),
|
|
159
|
+
insertWithCycleCheck: (blockerId, blockedId) => Effect.gen(function* () {
|
|
160
|
+
const txResult = yield* Effect.try({
|
|
161
|
+
try: () => {
|
|
162
|
+
// BEGIN IMMEDIATE acquires write lock immediately, preventing other writers
|
|
163
|
+
// from modifying the dependency graph until we commit/rollback
|
|
164
|
+
db.exec("BEGIN IMMEDIATE");
|
|
165
|
+
try {
|
|
166
|
+
// Special case: same ID always indicates a cycle
|
|
167
|
+
if (blockerId === blockedId) {
|
|
168
|
+
db.exec("ROLLBACK");
|
|
169
|
+
return { _tag: "wouldCycle", depthLimitHit: false };
|
|
170
|
+
}
|
|
171
|
+
// Check if dependency already exists (idempotent)
|
|
172
|
+
const existing = db.prepare("SELECT 1 FROM task_dependencies WHERE blocker_id = ? AND blocked_id = ? LIMIT 1").get(blockerId, blockedId);
|
|
173
|
+
if (existing != null) {
|
|
174
|
+
db.exec("ROLLBACK");
|
|
175
|
+
return { _tag: "alreadyExists", depthLimitHit: false };
|
|
176
|
+
}
|
|
177
|
+
// Check if adding this dependency would create a cycle
|
|
178
|
+
// Uses depth-limited recursive CTE to detect if blockedId can already reach blockerId
|
|
179
|
+
const cycleCheck = db.prepare(`
|
|
180
|
+
WITH RECURSIVE reachable(id, depth) AS (
|
|
181
|
+
SELECT blocker_id, 1 FROM task_dependencies WHERE blocked_id = ?
|
|
182
|
+
UNION
|
|
183
|
+
SELECT d.blocker_id, r.depth + 1
|
|
184
|
+
FROM task_dependencies d
|
|
185
|
+
JOIN reachable r ON d.blocked_id = r.id
|
|
186
|
+
WHERE r.depth < ?
|
|
187
|
+
)
|
|
188
|
+
SELECT
|
|
189
|
+
MAX(CASE WHEN id = ? THEN 1 ELSE 0 END) AS found,
|
|
190
|
+
MAX(depth) AS max_depth
|
|
191
|
+
FROM reachable
|
|
192
|
+
`).get(blockerId, MAX_DEPENDENCY_DEPTH, blockedId);
|
|
193
|
+
const depthLimitHit = cycleCheck?.max_depth != null && cycleCheck.max_depth >= MAX_DEPENDENCY_DEPTH;
|
|
194
|
+
if (cycleCheck?.found === 1) {
|
|
195
|
+
db.exec("ROLLBACK");
|
|
196
|
+
return { _tag: "wouldCycle", depthLimitHit };
|
|
197
|
+
}
|
|
198
|
+
// No cycle detected - safe to insert
|
|
199
|
+
const result = db.prepare("INSERT INTO task_dependencies (blocker_id, blocked_id, created_at) VALUES (?, ?, ?)").run(blockerId, blockedId, new Date().toISOString());
|
|
200
|
+
if (result.changes !== 1) {
|
|
201
|
+
db.exec("ROLLBACK");
|
|
202
|
+
throw new UnexpectedRowCountError({
|
|
203
|
+
operation: "dependency insert (with cycle check)",
|
|
204
|
+
expected: 1,
|
|
205
|
+
actual: result.changes
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
db.exec("COMMIT");
|
|
209
|
+
return { _tag: "inserted", depthLimitHit };
|
|
210
|
+
}
|
|
211
|
+
catch (e) {
|
|
212
|
+
// Ensure rollback on any error
|
|
213
|
+
try {
|
|
214
|
+
db.exec("ROLLBACK");
|
|
215
|
+
}
|
|
216
|
+
catch {
|
|
217
|
+
// Ignore rollback errors (transaction may already be rolled back)
|
|
218
|
+
}
|
|
219
|
+
throw e;
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
223
|
+
});
|
|
224
|
+
if (txResult.depthLimitHit) {
|
|
225
|
+
yield* Effect.logWarning(`Dependency depth limit (${MAX_DEPENDENCY_DEPTH}) reached during cycle check for ${blockerId} → ${blockedId}. Insert proceeded but deeper cycles may exist.`);
|
|
226
|
+
}
|
|
227
|
+
return { _tag: txResult._tag };
|
|
101
228
|
})
|
|
102
229
|
};
|
|
103
230
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dep-repo.js","sourceRoot":"","sources":["../../src/repo/dep-repo.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"dep-repo.js","sourceRoot":"","sources":["../../src/repo/dep-repo.ts"],"names":[],"mappings":"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"}
|