@jamesaphoenix/tx-core 0.4.2 → 0.4.4
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 +108 -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/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/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/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/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/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/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/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/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.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/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 +546 -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/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/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 +3 -3
- 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 +66 -27
- package/dist/services/orchestrator-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/sync-service.d.ts +17 -4
- package/dist/services/sync-service.d.ts.map +1 -1
- package/dist/services/sync-service.js +381 -116
- 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 +165 -35
- 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 +168 -72
- 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
- package/dist/mappers/anchor.d.ts +0 -14
- package/dist/mappers/anchor.d.ts.map +0 -1
- package/dist/mappers/anchor.js +0 -38
- package/dist/mappers/anchor.js.map +0 -1
- package/dist/mappers/candidate.d.ts +0 -23
- package/dist/mappers/candidate.d.ts.map +0 -1
- package/dist/mappers/candidate.js +0 -53
- package/dist/mappers/candidate.js.map +0 -1
- package/dist/mappers/edge.d.ts +0 -10
- package/dist/mappers/edge.d.ts.map +0 -1
- package/dist/mappers/edge.js +0 -19
- package/dist/mappers/edge.js.map +0 -1
- package/dist/repo/anchor-repo.d.ts +0 -52
- package/dist/repo/anchor-repo.d.ts.map +0 -1
- package/dist/repo/anchor-repo.js +0 -204
- package/dist/repo/anchor-repo.js.map +0 -1
- package/dist/repo/candidate-repo.d.ts +0 -16
- package/dist/repo/candidate-repo.d.ts.map +0 -1
- package/dist/repo/candidate-repo.js +0 -143
- package/dist/repo/candidate-repo.js.map +0 -1
- package/dist/repo/edge-repo.d.ts +0 -26
- package/dist/repo/edge-repo.d.ts.map +0 -1
- package/dist/repo/edge-repo.js +0 -227
- package/dist/repo/edge-repo.js.map +0 -1
- package/dist/services/anchor-service.d.ts +0 -147
- package/dist/services/anchor-service.d.ts.map +0 -1
- package/dist/services/anchor-service.js +0 -540
- package/dist/services/anchor-service.js.map +0 -1
- package/dist/services/anchor-verification.d.ts +0 -94
- package/dist/services/anchor-verification.d.ts.map +0 -1
- package/dist/services/anchor-verification.js +0 -617
- package/dist/services/anchor-verification.js.map +0 -1
- package/dist/services/ast-grep-service.d.ts +0 -58
- package/dist/services/ast-grep-service.d.ts.map +0 -1
- package/dist/services/ast-grep-service.js +0 -356
- package/dist/services/ast-grep-service.js.map +0 -1
- package/dist/services/candidate-extractor-service.d.ts +0 -56
- package/dist/services/candidate-extractor-service.d.ts.map +0 -1
- package/dist/services/candidate-extractor-service.js +0 -365
- package/dist/services/candidate-extractor-service.js.map +0 -1
- package/dist/services/edge-service.d.ts +0 -78
- package/dist/services/edge-service.d.ts.map +0 -1
- package/dist/services/edge-service.js +0 -158
- package/dist/services/edge-service.js.map +0 -1
- package/dist/services/feedback-tracker.d.ts +0 -64
- package/dist/services/feedback-tracker.d.ts.map +0 -1
- package/dist/services/feedback-tracker.js +0 -110
- package/dist/services/feedback-tracker.js.map +0 -1
- package/dist/services/graph-expansion.d.ts +0 -155
- package/dist/services/graph-expansion.d.ts.map +0 -1
- package/dist/services/graph-expansion.js +0 -466
- package/dist/services/graph-expansion.js.map +0 -1
- package/dist/services/promotion-service.d.ts +0 -67
- package/dist/services/promotion-service.d.ts.map +0 -1
- package/dist/services/promotion-service.js +0 -151
- package/dist/services/promotion-service.js.map +0 -1
- package/dist/services/swarm-verification.d.ts +0 -104
- package/dist/services/swarm-verification.d.ts.map +0 -1
- package/dist/services/swarm-verification.js +0 -400
- package/dist/services/swarm-verification.js.map +0 -1
|
@@ -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 { AttemptNotFoundError, DatabaseError, EntityFetchError } from "../errors.js";
|
|
4
4
|
import { rowToAttempt } from "../mappers/attempt.js";
|
|
5
5
|
export class AttemptRepository extends Context.Tag("AttemptRepository")() {
|
|
6
6
|
}
|
|
@@ -14,6 +14,13 @@ export const AttemptRepositoryLive = Layer.effect(AttemptRepository, Effect.gen(
|
|
|
14
14
|
VALUES (?, ?, ?, ?, ?)`).run(input.taskId, input.approach, input.outcome, input.reason ?? null, now);
|
|
15
15
|
// Fetch the inserted row
|
|
16
16
|
const row = db.prepare("SELECT * FROM attempts WHERE id = ?").get(result.lastInsertRowid);
|
|
17
|
+
if (!row) {
|
|
18
|
+
throw new EntityFetchError({
|
|
19
|
+
entity: "attempt",
|
|
20
|
+
id: result.lastInsertRowid,
|
|
21
|
+
operation: "insert"
|
|
22
|
+
});
|
|
23
|
+
}
|
|
17
24
|
return rowToAttempt(row);
|
|
18
25
|
},
|
|
19
26
|
catch: (cause) => new DatabaseError({ cause })
|
|
@@ -50,11 +57,14 @@ export const AttemptRepositoryLive = Layer.effect(AttemptRepository, Effect.gen(
|
|
|
50
57
|
},
|
|
51
58
|
catch: (cause) => new DatabaseError({ cause })
|
|
52
59
|
}),
|
|
53
|
-
remove: (id) => Effect.
|
|
54
|
-
|
|
55
|
-
db.prepare("DELETE FROM attempts WHERE id = ?").run(id)
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
remove: (id) => Effect.gen(function* () {
|
|
61
|
+
const result = yield* Effect.try({
|
|
62
|
+
try: () => db.prepare("DELETE FROM attempts WHERE id = ?").run(id),
|
|
63
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
64
|
+
});
|
|
65
|
+
if (result.changes === 0) {
|
|
66
|
+
yield* Effect.fail(new AttemptNotFoundError({ id }));
|
|
67
|
+
}
|
|
58
68
|
}),
|
|
59
69
|
getFailedCountsForTasks: (taskIds) => Effect.try({
|
|
60
70
|
try: () => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attempt-repo.js","sourceRoot":"","sources":["../../src/repo/attempt-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":"attempt-repo.js","sourceRoot":"","sources":["../../src/repo/attempt-repo.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,14 +1,58 @@
|
|
|
1
1
|
import { Context, Effect, Layer } from "effect";
|
|
2
2
|
import { SqliteClient } from "../db.js";
|
|
3
|
-
import { DatabaseError } from "../errors.js";
|
|
3
|
+
import { ClaimIdNotFoundError, DatabaseError } from "../errors.js";
|
|
4
4
|
import type { TaskClaim } from "../schemas/worker.js";
|
|
5
|
+
/**
|
|
6
|
+
* Result of an atomic claim insert attempt.
|
|
7
|
+
*/
|
|
8
|
+
export interface AtomicInsertResult {
|
|
9
|
+
/** Whether the insert succeeded (no existing active claim) */
|
|
10
|
+
success: boolean;
|
|
11
|
+
/** The claim if insert succeeded, null otherwise */
|
|
12
|
+
claim: TaskClaim | null;
|
|
13
|
+
/** If insert failed, the existing claim that blocked it */
|
|
14
|
+
existingClaim: TaskClaim | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Result of an atomic lease renewal attempt.
|
|
18
|
+
*/
|
|
19
|
+
export interface AtomicRenewResult {
|
|
20
|
+
/** Whether the renewal succeeded */
|
|
21
|
+
success: boolean;
|
|
22
|
+
/** The renewed claim if successful, null otherwise */
|
|
23
|
+
claim: TaskClaim | null;
|
|
24
|
+
/** Reason for failure if not successful */
|
|
25
|
+
failureReason: "not_found" | "expired" | null;
|
|
26
|
+
}
|
|
5
27
|
declare const ClaimRepository_base: Context.TagClass<ClaimRepository, "ClaimRepository", {
|
|
6
28
|
readonly insert: (claim: Omit<TaskClaim, "id">) => Effect.Effect<TaskClaim, DatabaseError>;
|
|
7
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Atomically insert a claim only if no active claim exists for the task.
|
|
31
|
+
* Uses a single SQL operation to prevent race conditions.
|
|
32
|
+
*/
|
|
33
|
+
readonly tryInsertAtomic: (claim: Omit<TaskClaim, "id">) => Effect.Effect<AtomicInsertResult, DatabaseError>;
|
|
34
|
+
readonly update: (claim: TaskClaim) => Effect.Effect<void, DatabaseError | ClaimIdNotFoundError>;
|
|
35
|
+
/**
|
|
36
|
+
* Atomically renew a lease only if it hasn't expired yet.
|
|
37
|
+
* Uses a single SQL operation to prevent race conditions between
|
|
38
|
+
* checking expiration and performing the renewal.
|
|
39
|
+
*
|
|
40
|
+
* @param claimId - The ID of the claim to renew
|
|
41
|
+
* @param workerId - The worker ID that must own the claim
|
|
42
|
+
* @param newLeaseExpiresAt - The new expiration time
|
|
43
|
+
* @returns AtomicRenewResult indicating success/failure and reason
|
|
44
|
+
*/
|
|
45
|
+
readonly tryRenewAtomic: (claimId: number, workerId: string, newLeaseExpiresAt: Date) => Effect.Effect<AtomicRenewResult, DatabaseError>;
|
|
8
46
|
readonly findById: (id: number) => Effect.Effect<TaskClaim | null, DatabaseError>;
|
|
9
47
|
readonly findActiveByTaskId: (taskId: string) => Effect.Effect<TaskClaim | null, DatabaseError>;
|
|
10
48
|
readonly findExpired: (now: Date) => Effect.Effect<readonly TaskClaim[], DatabaseError>;
|
|
11
49
|
readonly releaseAllByWorkerId: (workerId: string) => Effect.Effect<number, DatabaseError>;
|
|
50
|
+
/**
|
|
51
|
+
* Find active claims where the associated task status is not 'active'.
|
|
52
|
+
* This detects orphaned claims left behind when claim release fails
|
|
53
|
+
* after task completion.
|
|
54
|
+
*/
|
|
55
|
+
readonly findOrphanedClaims: () => Effect.Effect<readonly TaskClaim[], DatabaseError>;
|
|
12
56
|
}>;
|
|
13
57
|
export declare class ClaimRepository extends ClaimRepository_base {
|
|
14
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claim-repo.d.ts","sourceRoot":"","sources":["../../src/repo/claim-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,
|
|
1
|
+
{"version":3,"file":"claim-repo.d.ts","sourceRoot":"","sources":["../../src/repo/claim-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,oBAAoB,EAAE,aAAa,EAAoB,MAAM,cAAc,CAAA;AAEpF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAA;AAErD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,8DAA8D;IAC9D,OAAO,EAAE,OAAO,CAAA;IAChB,oDAAoD;IACpD,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IACvB,2DAA2D;IAC3D,aAAa,EAAE,SAAS,GAAG,IAAI,CAAA;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,oCAAoC;IACpC,OAAO,EAAE,OAAO,CAAA;IAChB,sDAAsD;IACtD,KAAK,EAAE,SAAS,GAAG,IAAI,CAAA;IACvB,2CAA2C;IAC3C,aAAa,EAAE,WAAW,GAAG,SAAS,GAAG,IAAI,CAAA;CAC9C;;qBAKoB,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,EAAE,aAAa,CAAC;IAC1F;;;OAGG;8BACuB,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,kBAAkB,EAAE,aAAa,CAAC;qBAC3F,CAAC,KAAK,EAAE,SAAS,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,oBAAoB,CAAC;IAChG;;;;;;;;;OASG;6BACsB,CACvB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,iBAAiB,EAAE,IAAI,KACpB,MAAM,CAAC,MAAM,CAAC,iBAAiB,EAAE,aAAa,CAAC;uBACjC,CAAC,EAAE,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,EAAE,aAAa,CAAC;iCACpD,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,GAAG,IAAI,EAAE,aAAa,CAAC;0BACzE,CAAC,GAAG,EAAE,IAAI,KAAK,MAAM,CAAC,MAAM,CAAC,SAAS,SAAS,EAAE,EAAE,aAAa,CAAC;mCACxD,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC;IACzF;;;;OAIG;iCAC0B,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,SAAS,EAAE,EAAE,aAAa,CAAC;;AAlCzF,qBAAa,eAAgB,SAAQ,oBAoClC;CAAG;AAEN,eAAO,MAAM,mBAAmB,mDAgP/B,CAAA"}
|
package/dist/repo/claim-repo.js
CHANGED
|
@@ -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 { ClaimIdNotFoundError, DatabaseError, EntityFetchError } from "../errors.js";
|
|
4
4
|
import { rowToClaim } from "../mappers/claim.js";
|
|
5
5
|
export class ClaimRepository extends Context.Tag("ClaimRepository")() {
|
|
6
6
|
}
|
|
@@ -13,16 +13,110 @@ export const ClaimRepositoryLive = Layer.effect(ClaimRepository, Effect.gen(func
|
|
|
13
13
|
(task_id, worker_id, claimed_at, lease_expires_at, renewed_count, status)
|
|
14
14
|
VALUES (?, ?, ?, ?, ?, ?)`).run(claim.taskId, claim.workerId, claim.claimedAt.toISOString(), claim.leaseExpiresAt.toISOString(), claim.renewedCount, claim.status);
|
|
15
15
|
const row = db.prepare("SELECT * FROM task_claims WHERE id = ?").get(result.lastInsertRowid);
|
|
16
|
+
if (!row) {
|
|
17
|
+
throw new EntityFetchError({
|
|
18
|
+
entity: "claim",
|
|
19
|
+
id: result.lastInsertRowid,
|
|
20
|
+
operation: "insert"
|
|
21
|
+
});
|
|
22
|
+
}
|
|
16
23
|
return rowToClaim(row);
|
|
17
24
|
},
|
|
18
25
|
catch: (cause) => new DatabaseError({ cause })
|
|
19
26
|
}),
|
|
20
|
-
|
|
27
|
+
tryInsertAtomic: (claim) => Effect.try({
|
|
21
28
|
try: () => {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
29
|
+
// Use INSERT ... WHERE NOT EXISTS to atomically check and insert
|
|
30
|
+
// This prevents the check-then-act race condition
|
|
31
|
+
const result = db.prepare(`INSERT INTO task_claims
|
|
32
|
+
(task_id, worker_id, claimed_at, lease_expires_at, renewed_count, status)
|
|
33
|
+
SELECT ?, ?, ?, ?, ?, ?
|
|
34
|
+
WHERE NOT EXISTS (
|
|
35
|
+
SELECT 1 FROM task_claims
|
|
36
|
+
WHERE task_id = ? AND status = 'active'
|
|
37
|
+
)`).run(claim.taskId, claim.workerId, claim.claimedAt.toISOString(), claim.leaseExpiresAt.toISOString(), claim.renewedCount, claim.status, claim.taskId // For the WHERE NOT EXISTS subquery
|
|
38
|
+
);
|
|
39
|
+
if (result.changes > 0) {
|
|
40
|
+
// Insert succeeded - fetch the newly created claim
|
|
41
|
+
const row = db.prepare("SELECT * FROM task_claims WHERE id = ?").get(result.lastInsertRowid);
|
|
42
|
+
if (!row) {
|
|
43
|
+
throw new EntityFetchError({
|
|
44
|
+
entity: "claim",
|
|
45
|
+
id: result.lastInsertRowid,
|
|
46
|
+
operation: "insert"
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
success: true,
|
|
51
|
+
claim: rowToClaim(row),
|
|
52
|
+
existingClaim: null
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
// Insert was blocked by existing active claim - fetch it
|
|
57
|
+
const existingRow = db.prepare("SELECT * FROM task_claims WHERE task_id = ? AND status = 'active' LIMIT 1").get(claim.taskId);
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
claim: null,
|
|
61
|
+
existingClaim: existingRow ? rowToClaim(existingRow) : null
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
66
|
+
}),
|
|
67
|
+
update: (claim) => Effect.gen(function* () {
|
|
68
|
+
const result = yield* Effect.try({
|
|
69
|
+
try: () => db.prepare(`UPDATE task_claims SET
|
|
70
|
+
task_id = ?, worker_id = ?, claimed_at = ?,
|
|
71
|
+
lease_expires_at = ?, renewed_count = ?, status = ?
|
|
72
|
+
WHERE id = ?`).run(claim.taskId, claim.workerId, claim.claimedAt.toISOString(), claim.leaseExpiresAt.toISOString(), claim.renewedCount, claim.status, claim.id),
|
|
73
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
74
|
+
});
|
|
75
|
+
if (result.changes === 0) {
|
|
76
|
+
yield* Effect.fail(new ClaimIdNotFoundError({ claimId: claim.id }));
|
|
77
|
+
}
|
|
78
|
+
}),
|
|
79
|
+
tryRenewAtomic: (claimId, workerId, newLeaseExpiresAt) => Effect.try({
|
|
80
|
+
try: () => {
|
|
81
|
+
const now = new Date().toISOString();
|
|
82
|
+
// Atomic update: only succeeds if claim is active, owned by worker, and not expired
|
|
83
|
+
const result = db.prepare(`UPDATE task_claims
|
|
84
|
+
SET lease_expires_at = ?, renewed_count = renewed_count + 1
|
|
85
|
+
WHERE id = ? AND worker_id = ? AND status = 'active' AND lease_expires_at >= ?`).run(newLeaseExpiresAt.toISOString(), claimId, workerId, now);
|
|
86
|
+
if (result.changes > 0) {
|
|
87
|
+
// Renewal succeeded - fetch the updated claim
|
|
88
|
+
const row = db.prepare("SELECT * FROM task_claims WHERE id = ?").get(claimId);
|
|
89
|
+
if (!row) {
|
|
90
|
+
throw new EntityFetchError({
|
|
91
|
+
entity: "claim",
|
|
92
|
+
id: claimId,
|
|
93
|
+
operation: "update"
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
success: true,
|
|
98
|
+
claim: rowToClaim(row),
|
|
99
|
+
failureReason: null
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
else {
|
|
103
|
+
// Renewal failed - determine why
|
|
104
|
+
// Check if claim exists and is owned by this worker
|
|
105
|
+
const existingRow = db.prepare("SELECT * FROM task_claims WHERE id = ? AND worker_id = ? AND status = 'active'").get(claimId, workerId);
|
|
106
|
+
if (!existingRow) {
|
|
107
|
+
return {
|
|
108
|
+
success: false,
|
|
109
|
+
claim: null,
|
|
110
|
+
failureReason: "not_found"
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
// Claim exists but lease has expired
|
|
114
|
+
return {
|
|
115
|
+
success: false,
|
|
116
|
+
claim: null,
|
|
117
|
+
failureReason: "expired"
|
|
118
|
+
};
|
|
119
|
+
}
|
|
26
120
|
},
|
|
27
121
|
catch: (cause) => new DatabaseError({ cause })
|
|
28
122
|
}),
|
|
@@ -56,6 +150,19 @@ export const ClaimRepositoryLive = Layer.effect(ClaimRepository, Effect.gen(func
|
|
|
56
150
|
return result.changes;
|
|
57
151
|
},
|
|
58
152
|
catch: (cause) => new DatabaseError({ cause })
|
|
153
|
+
}),
|
|
154
|
+
findOrphanedClaims: () => Effect.try({
|
|
155
|
+
try: () => {
|
|
156
|
+
// Find active claims where the task status is NOT 'active'
|
|
157
|
+
// This catches claims that weren't released after task completion
|
|
158
|
+
const rows = db.prepare(`SELECT c.* FROM task_claims c
|
|
159
|
+
JOIN tasks t ON c.task_id = t.id
|
|
160
|
+
WHERE c.status = 'active'
|
|
161
|
+
AND t.status != 'active'
|
|
162
|
+
ORDER BY c.claimed_at ASC`).all();
|
|
163
|
+
return rows.map(rowToClaim);
|
|
164
|
+
},
|
|
165
|
+
catch: (cause) => new DatabaseError({ cause })
|
|
59
166
|
})
|
|
60
167
|
};
|
|
61
168
|
}));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"claim-repo.js","sourceRoot":"","sources":["../../src/repo/claim-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":"claim-repo.js","sourceRoot":"","sources":["../../src/repo/claim-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"}
|