@astrosheep/keiyaku 2.9.12 → 2.9.13
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 +22 -18
- package/build/.tsbuildinfo +1 -1
- package/build/agents/call-terms.js +1 -0
- package/build/agents/providers/claude-agent-sdk/adapter.js +2 -2
- package/build/cli/commands/akuma/akuma/handler.js +2 -1
- package/build/cli/commands/akuma/akuma/meta.js +4 -3
- package/build/cli/commands/akuma/catalog.js +2 -0
- package/build/cli/commands/akuma/list/handler.js +1 -1
- package/build/cli/commands/akuma/list/meta-list.js +12 -0
- package/build/cli/commands/akuma/list/meta-ls.js +2 -2
- package/build/cli/commands/akuma.js +5 -0
- package/build/cli/commands/contract/amend/meta.js +4 -3
- package/build/cli/commands/contract/arc/meta.js +2 -0
- package/build/cli/commands/contract/audit/handler.js +28 -2
- package/build/cli/commands/contract/audit/meta.js +4 -3
- package/build/cli/commands/contract/petition/handler.js +2 -1
- package/build/cli/commands/contract/petition/meta.js +2 -2
- package/build/cli/commands/contract/renew/handler.js +44 -3
- package/build/cli/commands/contract/renew/meta.js +5 -4
- package/build/cli/commands/metadata.js +12 -9
- package/build/cli/commands/projection/call/handler.js +1 -1
- package/build/cli/commands/projection/call/meta.js +1 -1
- package/build/cli/commands/projection/catalog.js +2 -2
- package/build/cli/commands/projection/{revive → fork}/handler.js +10 -5
- package/build/cli/commands/projection/fork/meta.js +12 -0
- package/build/cli/commands/projection/history/handler.js +46 -2
- package/build/cli/commands/projection/history/meta.js +3 -3
- package/build/cli/commands/projection/kill/meta.js +2 -2
- package/build/cli/commands/projection/status/handler.js +29 -13
- package/build/cli/commands/projection/status/meta.js +14 -3
- package/build/cli/commands/projection/tell/handler.js +8 -4
- package/build/cli/commands/projection/tell/meta.js +1 -1
- package/build/cli/commands/projection/wait/handler.js +45 -20
- package/build/cli/commands/projection/wait/meta.js +1 -1
- package/build/cli/commands/shared.js +1 -1
- package/build/cli/commands/system/catalog.js +2 -0
- package/build/cli/commands/task/add/handler.js +28 -12
- package/build/cli/commands/task/add/meta.js +18 -6
- package/build/cli/commands/task/catalog.js +2 -0
- package/build/cli/commands/task/compose/handler.js +2 -2
- package/build/cli/commands/task/compose/meta.js +2 -2
- package/build/cli/commands/task/doctor/handler.js +2 -3
- package/build/cli/commands/task/doctor/meta.js +2 -2
- package/build/cli/commands/task/done/meta.js +2 -2
- package/build/cli/commands/task/drop/handler.js +7 -2
- package/build/cli/commands/task/drop/meta.js +6 -3
- package/build/cli/commands/task/hold/meta.js +2 -2
- package/build/cli/commands/task/log/meta.js +2 -2
- package/build/cli/commands/task/ls/meta.js +2 -2
- package/build/cli/commands/task/note/handler.js +6 -0
- package/build/cli/commands/task/note/meta.js +11 -0
- package/build/cli/commands/task/resume/meta.js +2 -2
- package/build/cli/commands/task/shared.js +29 -15
- package/build/cli/commands/task/show/meta.js +2 -2
- package/build/cli/commands/task/start/meta.js +2 -2
- package/build/cli/commands/task/stop/meta.js +2 -2
- package/build/cli/commands/task/update/meta.js +3 -2
- package/build/cli/commands/verification/handler.js +43 -0
- package/build/cli/commands/verification/meta.js +13 -0
- package/build/cli/completion.js +16 -13
- package/build/cli/flags.js +43 -11
- package/build/cli/help.js +29 -30
- package/build/cli/index.js +6 -5
- package/build/cli/parse-flags.js +39 -0
- package/build/cli/parse-metadata.js +5 -2
- package/build/cli/parse-selectors.js +18 -0
- package/build/cli/parse.js +21 -8
- package/build/cli/render/arc.js +12 -3
- package/build/cli/render/audit.js +2 -2
- package/build/cli/render/call.js +4 -4
- package/build/cli/render/kanshi.js +122 -85
- package/build/cli/render/misc.js +2 -2
- package/build/cli/render/petition.js +16 -1
- package/build/cli/render/projection-history.js +17 -4
- package/build/cli/render/shared.js +5 -5
- package/build/cli/render/status-indicator.js +47 -0
- package/build/cli/render/status.js +106 -76
- package/build/cli/render/success-response.js +20 -5
- package/build/cli/render/task.js +6 -5
- package/build/cli/render/tell.js +10 -4
- package/build/cli/render/tool-command-normalization.js +137 -0
- package/build/cli/render/tool-presentation.js +2 -1
- package/build/cli/render/verification.js +23 -0
- package/build/cli/render/wait.js +101 -33
- package/build/cli/types.js +2 -1
- package/build/config/akuma-loader.js +21 -1
- package/build/config/settings/knobs.js +7 -1
- package/build/config/settings/schema.js +10 -5
- package/build/core/amend.js +102 -17
- package/build/core/arc.js +4 -4
- package/build/core/audit/candidate.js +10 -18
- package/build/core/audit/coordinates.js +12 -17
- package/build/core/audit/evidence.js +6 -4
- package/build/core/audit/facade.js +3 -3
- package/build/core/audit/report.js +1 -1
- package/build/core/bind-reconciliation.js +42 -23
- package/build/core/bind.js +182 -81
- package/build/core/call/call.js +6 -7
- package/build/core/call/context.js +43 -30
- package/build/core/call/execution.js +21 -6
- package/build/core/call/prompt.js +11 -42
- package/build/core/contract-carrier-runtime.js +209 -105
- package/build/core/contract-carrier.js +156 -73
- package/build/core/contract-view.js +42 -5
- package/build/core/contract.js +2 -1
- package/build/core/derived-replay.js +168 -35
- package/build/core/draft.js +9 -5
- package/build/core/forfeit.js +1 -1
- package/build/core/lifecycle-history.js +13 -0
- package/build/core/lifecycle-recovery.js +32 -27
- package/build/core/lifecycle-runner.js +14 -14
- package/build/core/log.js +1 -1
- package/build/core/outcome-base.js +2 -2
- package/build/core/projection/akuma-name.js +43 -0
- package/build/core/projection/generation/database.js +141 -79
- package/build/core/projection/generation/ledger.js +91 -0
- package/build/core/projection/generation/model.js +18 -13
- package/build/core/projection/generation/projection-generation-continuation.js +1 -1
- package/build/core/projection/generation/projection-generation-doorbell.js +88 -0
- package/build/core/projection/generation/projection-generation-execution.js +24 -0
- package/build/core/projection/generation/projection-generation-identity.js +4 -0
- package/build/core/projection/generation/projection-generation-launcher.js +72 -138
- package/build/core/projection/generation/projection-generation-process.js +16 -15
- package/build/core/projection/generation/projection-generation-runner.js +125 -79
- package/build/core/projection/generation/projection-generation-runtime.js +10 -61
- package/build/core/projection/generation/protocol.js +68 -0
- package/build/core/projection/generation/store.js +17 -7
- package/build/core/projection/generation/transitions.js +14 -14
- package/build/core/projection/index.js +5 -5
- package/build/core/projection/projection-core.js +6 -2
- package/build/core/projection/projection-execution-observer.js +1 -1
- package/build/core/projection/projection-history.js +7 -7
- package/build/core/projection/projection-kill.js +15 -6
- package/build/core/projection/projection-life-observer.js +8 -8
- package/build/core/projection/projection-mint.js +6 -8
- package/build/core/projection/projection-status-observation.js +77 -8
- package/build/core/projection/projection-status.js +151 -4
- package/build/core/projection/projection-wait.js +46 -27
- package/build/core/projection/projection-wake.js +27 -89
- package/build/core/projection/tell/launch-store.js +1 -1
- package/build/core/projection/tell/store.js +1 -1
- package/build/core/renew-build.js +204 -74
- package/build/core/renew-plan.js +34 -9
- package/build/core/renew-rewrite.js +56 -15
- package/build/core/renew.js +39 -35
- package/build/core/repository-ledger/accepted-fold-read.js +6 -3
- package/build/core/repository-ledger/codec.js +11 -5
- package/build/core/repository-ledger/current-state-store.js +13 -16
- package/build/core/repository-ledger/fold-repository.js +5 -6
- package/build/core/repository-ledger/identity.js +60 -0
- package/build/core/repository-ledger/inventory.js +3 -3
- package/build/core/repository-ledger/read-model.js +54 -6
- package/build/core/repository-ledger/write-transaction.js +9 -4
- package/build/core/run-control/connection-bound-close.js +41 -0
- package/build/core/run-control/detached-lease-bootstrap.js +208 -0
- package/build/core/run-control/detached-process.js +14 -0
- package/build/core/run-control/index.js +6 -0
- package/build/core/{process-group.js → run-control/process-tree.js} +1 -1
- package/build/core/run-control/runner-lease.js +172 -0
- package/build/core/{sqlite-process-lifetime-lock.js → run-control/sqlite-process-lifetime-lock.js} +4 -24
- package/build/core/seal.js +44 -32
- package/build/core/settlement/claim-delivery.js +22 -9
- package/build/core/settlement/claim.js +8 -6
- package/build/core/settlement/petition-claim-gates.js +9 -8
- package/build/core/settlement/petition-head-guard.js +5 -6
- package/build/core/settlement/petition-preview.js +21 -5
- package/build/core/settlement/petition.js +51 -17
- package/build/core/settlement/settlement.js +20 -15
- package/build/core/settlement/verdict.js +2 -2
- package/build/core/settlement/verification.js +492 -364
- package/build/core/status/board.js +184 -75
- package/build/core/status/drift.js +8 -10
- package/build/core/task/board.js +1 -0
- package/build/core/task/commands.js +25 -0
- package/build/core/task/compose.js +3 -2
- package/build/core/task/document.js +102 -39
- package/build/core/task/index.js +3 -3
- package/build/core/task/settlement-git.js +1 -4
- package/build/core/task/settlement-policy.js +27 -14
- package/build/core/task/source-board.js +1 -0
- package/build/core/task/task-bind-preparation.js +59 -9
- package/build/core/task/task-contract.js +2 -2
- package/build/core/task/task-store-repository.js +1 -0
- package/build/core/task/task.js +2 -2
- package/build/core/transcripts.js +4 -4
- package/build/core/verification-declaration.js +95 -0
- package/build/core/worktree-bootstrap.js +1 -1
- package/build/core/worktree-path.js +7 -11
- package/build/flow-error.js +9 -1
- package/build/generated/version.js +2 -2
- package/build/git/branches.js +41 -70
- package/build/git/commits.js +92 -21
- package/build/git/core.js +9 -161
- package/build/git/diff/preview.js +4 -4
- package/build/git/diff/read.js +4 -4
- package/build/git/diff/structured.js +5 -5
- package/build/git/process.js +229 -0
- package/build/git/refs.js +30 -5
- package/build/git/staging.js +39 -115
- package/build/git/streaming-batch.js +9 -16
- package/build/git/worktree.js +104 -91
- package/build/index.js +7 -0
- package/package.json +1 -1
- package/skills/keiyaku-akuma/SKILL.md +21 -20
- package/skills/keiyaku-task/SKILL.md +38 -4
- package/skills/keiyaku-workflow/SKILL.md +51 -11
- package/build/cli/commands/projection/revive/meta.js +0 -12
- package/build/core/projection/projection-life-protocol.js +0 -115
- package/build/core/projection/projection-runner-lock.js +0 -211
- package/build/core/projection/tell/database.js +0 -127
- package/build/core/settlement/verification-coordination.js +0 -305
- package/build/core/settlement/verification-supervisor.js +0 -275
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
import * as path from "node:path";
|
|
2
|
-
import { initializeSqliteProcessLifetimeLockDatabase, isSqliteProcessLifetimeLockContention, isSqliteProcessLifetimeLockPlatformSupported, openSqliteProcessLifetimeLockTransaction, SqliteProcessLifetimeLockError, sqliteProcessLifetimeLockErrorDetail, } from "../sqlite-process-lifetime-lock.js";
|
|
3
|
-
export const PROJECTION_RUNNER_LOCK_DATABASE = "leash";
|
|
4
|
-
export class ProjectionRunnerLockUnavailableError extends Error {
|
|
5
|
-
code = "PROJECTION_RUNNER_LOCK_UNAVAILABLE";
|
|
6
|
-
constructor(message, options) {
|
|
7
|
-
super(message, options);
|
|
8
|
-
this.name = "ProjectionRunnerLockUnavailableError";
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
export class ProjectionRunnerLockUnsupportedError extends Error {
|
|
12
|
-
code = "PROJECTION_RUNNER_LOCK_UNSUPPORTED";
|
|
13
|
-
constructor(platform) {
|
|
14
|
-
super(`projection runner locks are unsupported on platform '${platform}'`);
|
|
15
|
-
this.name = "ProjectionRunnerLockUnsupportedError";
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
export function projectionRunnerLockPath(projectionDirectory) {
|
|
19
|
-
return path.join(projectionDirectory, PROJECTION_RUNNER_LOCK_DATABASE);
|
|
20
|
-
}
|
|
21
|
-
export function isProjectionRunnerLockPlatformSupported(platform = process.platform) {
|
|
22
|
-
return isSqliteProcessLifetimeLockPlatformSupported(platform);
|
|
23
|
-
}
|
|
24
|
-
function errorDetail(error) {
|
|
25
|
-
return sqliteProcessLifetimeLockErrorDetail(error);
|
|
26
|
-
}
|
|
27
|
-
function isContention(error) {
|
|
28
|
-
return isSqliteProcessLifetimeLockContention(error)
|
|
29
|
-
|| (error instanceof SqliteProcessLifetimeLockError && error.reason === "busy");
|
|
30
|
-
}
|
|
31
|
-
function openGuard(lockPath, busyTimeoutMs) {
|
|
32
|
-
return openSqliteProcessLifetimeLockTransaction({ path: lockPath, mode: "deferred", busyTimeoutMs });
|
|
33
|
-
}
|
|
34
|
-
function openReservation(lockPath, busyTimeoutMs) {
|
|
35
|
-
return openSqliteProcessLifetimeLockTransaction({ path: lockPath, mode: "immediate", busyTimeoutMs });
|
|
36
|
-
}
|
|
37
|
-
function closeTransaction(transaction) {
|
|
38
|
-
try {
|
|
39
|
-
transaction.close();
|
|
40
|
-
}
|
|
41
|
-
catch (error) {
|
|
42
|
-
return error;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function createHeldLock(lockPath, busyTimeoutMs, guard, initialReservation) {
|
|
46
|
-
let closed = false;
|
|
47
|
-
let reservation = initialReservation;
|
|
48
|
-
return {
|
|
49
|
-
path: lockPath,
|
|
50
|
-
releaseReservationForTransfer() {
|
|
51
|
-
if (closed || reservation === undefined)
|
|
52
|
-
return;
|
|
53
|
-
const released = reservation;
|
|
54
|
-
reservation = undefined;
|
|
55
|
-
const failure = closeTransaction(released);
|
|
56
|
-
if (failure !== undefined)
|
|
57
|
-
throw new ProjectionRunnerLockUnavailableError(`projection runner reservation release failed: ${errorDetail(failure)}`, { cause: failure });
|
|
58
|
-
},
|
|
59
|
-
reclaimReservation() {
|
|
60
|
-
if (closed || reservation !== undefined)
|
|
61
|
-
return;
|
|
62
|
-
try {
|
|
63
|
-
reservation = openReservation(lockPath, busyTimeoutMs);
|
|
64
|
-
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
throw new ProjectionRunnerLockUnavailableError(`projection runner reservation reclaim failed: ${errorDetail(error)}`, { cause: error });
|
|
67
|
-
}
|
|
68
|
-
},
|
|
69
|
-
close() {
|
|
70
|
-
if (closed)
|
|
71
|
-
return;
|
|
72
|
-
closed = true;
|
|
73
|
-
let failure;
|
|
74
|
-
if (reservation !== undefined)
|
|
75
|
-
failure = closeTransaction(reservation);
|
|
76
|
-
failure ??= closeTransaction(guard);
|
|
77
|
-
if (failure !== undefined)
|
|
78
|
-
throw new ProjectionRunnerLockUnavailableError(`projection runner lock release failed: ${errorDetail(failure)}`, { cause: failure });
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
}
|
|
82
|
-
function unavailable(operation, error) {
|
|
83
|
-
return new ProjectionRunnerLockUnavailableError(`${operation}: ${errorDetail(error)}`, { cause: error });
|
|
84
|
-
}
|
|
85
|
-
/** Mint-time schema initialization retains no process-life authority. */
|
|
86
|
-
export function initializeProjectionRunnerLockDatabase(projectionDirectory, platform = process.platform) {
|
|
87
|
-
if (!isProjectionRunnerLockPlatformSupported(platform))
|
|
88
|
-
throw new ProjectionRunnerLockUnsupportedError(platform);
|
|
89
|
-
try {
|
|
90
|
-
return initializeSqliteProcessLifetimeLockDatabase(projectionRunnerLockPath(projectionDirectory), platform);
|
|
91
|
-
}
|
|
92
|
-
catch (error) {
|
|
93
|
-
throw unavailable("projection runner lock database could not be initialized", error);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
/**
|
|
97
|
-
* Projection policy: hold a SHARED guard plus a RESERVED claim. The transfer
|
|
98
|
-
* target overlaps its guard before the reservation moves, preserving exclusion.
|
|
99
|
-
*/
|
|
100
|
-
export function acquireProjectionRunnerLock(projectionDirectory, platform = process.platform, busyTimeoutMs = 0) {
|
|
101
|
-
if (!isProjectionRunnerLockPlatformSupported(platform))
|
|
102
|
-
throw new ProjectionRunnerLockUnsupportedError(platform);
|
|
103
|
-
const lockPath = projectionRunnerLockPath(projectionDirectory);
|
|
104
|
-
let guard;
|
|
105
|
-
try {
|
|
106
|
-
guard = openGuard(lockPath, busyTimeoutMs);
|
|
107
|
-
}
|
|
108
|
-
catch (error) {
|
|
109
|
-
throw unavailable("projection runner lock database could not be opened", error);
|
|
110
|
-
}
|
|
111
|
-
try {
|
|
112
|
-
return createHeldLock(lockPath, busyTimeoutMs, guard, openReservation(lockPath, busyTimeoutMs));
|
|
113
|
-
}
|
|
114
|
-
catch (error) {
|
|
115
|
-
closeTransaction(guard);
|
|
116
|
-
if (isContention(error))
|
|
117
|
-
throw new ProjectionRunnerLockUnavailableError("projection runner lock is already held", { cause: error });
|
|
118
|
-
throw unavailable("projection runner lock acquisition failed", error);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
export function acquireProjectionRunnerTransferTarget(projectionDirectory, platform = process.platform, busyTimeoutMs = 0) {
|
|
122
|
-
if (!isProjectionRunnerLockPlatformSupported(platform))
|
|
123
|
-
throw new ProjectionRunnerLockUnsupportedError(platform);
|
|
124
|
-
const lockPath = projectionRunnerLockPath(projectionDirectory);
|
|
125
|
-
let guard;
|
|
126
|
-
try {
|
|
127
|
-
guard = openGuard(lockPath, busyTimeoutMs);
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
throw unavailable("projection runner transfer acquisition failed", error);
|
|
131
|
-
}
|
|
132
|
-
let closed = false;
|
|
133
|
-
return {
|
|
134
|
-
path: lockPath,
|
|
135
|
-
promote() {
|
|
136
|
-
if (closed)
|
|
137
|
-
throw new ProjectionRunnerLockUnavailableError("projection runner transfer target is closed");
|
|
138
|
-
try {
|
|
139
|
-
const reservation = openReservation(lockPath, busyTimeoutMs);
|
|
140
|
-
closed = true;
|
|
141
|
-
return createHeldLock(lockPath, busyTimeoutMs, guard, reservation);
|
|
142
|
-
}
|
|
143
|
-
catch (error) {
|
|
144
|
-
throw unavailable("projection runner transfer promotion failed", error);
|
|
145
|
-
}
|
|
146
|
-
},
|
|
147
|
-
close() {
|
|
148
|
-
if (closed)
|
|
149
|
-
return;
|
|
150
|
-
closed = true;
|
|
151
|
-
const failure = closeTransaction(guard);
|
|
152
|
-
if (failure !== undefined)
|
|
153
|
-
throw unavailable("projection runner transfer release failed", failure);
|
|
154
|
-
},
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
/** Acquire positive death evidence. An active guard or transfer blocks this. */
|
|
158
|
-
export function acquireProjectionRunnerSettlementLock(projectionDirectory, platform = process.platform, busyTimeoutMs = 0) {
|
|
159
|
-
if (!isProjectionRunnerLockPlatformSupported(platform))
|
|
160
|
-
throw new ProjectionRunnerLockUnsupportedError(platform);
|
|
161
|
-
const lockPath = projectionRunnerLockPath(projectionDirectory);
|
|
162
|
-
let transaction;
|
|
163
|
-
try {
|
|
164
|
-
transaction = openSqliteProcessLifetimeLockTransaction({ path: lockPath, mode: "exclusive", busyTimeoutMs });
|
|
165
|
-
}
|
|
166
|
-
catch (error) {
|
|
167
|
-
if (isContention(error))
|
|
168
|
-
throw new ProjectionRunnerLockUnavailableError("projection runner lock is already held", { cause: error });
|
|
169
|
-
throw unavailable("projection runner settlement lock acquisition failed", error);
|
|
170
|
-
}
|
|
171
|
-
let closed = false;
|
|
172
|
-
return {
|
|
173
|
-
path: lockPath,
|
|
174
|
-
close() {
|
|
175
|
-
if (closed)
|
|
176
|
-
return;
|
|
177
|
-
closed = true;
|
|
178
|
-
const failure = closeTransaction(transaction);
|
|
179
|
-
if (failure !== undefined)
|
|
180
|
-
throw unavailable("projection runner settlement lock release failed", failure);
|
|
181
|
-
},
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
/** Positive death probe without creating a missing lock database. */
|
|
185
|
-
export function observeProjectionRunnerLock(projectionDirectory, platform = process.platform) {
|
|
186
|
-
if (!isProjectionRunnerLockPlatformSupported(platform)) {
|
|
187
|
-
return { state: "unknown", reason: "unsupported-platform", detail: `projection runner locks are unsupported on platform '${platform}'` };
|
|
188
|
-
}
|
|
189
|
-
const lockPath = projectionRunnerLockPath(projectionDirectory);
|
|
190
|
-
let probe;
|
|
191
|
-
try {
|
|
192
|
-
probe = openSqliteProcessLifetimeLockTransaction({ path: lockPath, mode: "exclusive", busyTimeoutMs: 0 });
|
|
193
|
-
return { state: "released" };
|
|
194
|
-
}
|
|
195
|
-
catch (error) {
|
|
196
|
-
if (isContention(error))
|
|
197
|
-
return { state: "held" };
|
|
198
|
-
const detail = errorDetail(error);
|
|
199
|
-
return {
|
|
200
|
-
state: "unknown",
|
|
201
|
-
reason: detail.includes("rollback-journal") ? "unsupported-journal-mode" : "probe-failed",
|
|
202
|
-
detail,
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
finally {
|
|
206
|
-
try {
|
|
207
|
-
probe?.close();
|
|
208
|
-
}
|
|
209
|
-
catch { /* no probe cleanup result is death evidence */ }
|
|
210
|
-
}
|
|
211
|
-
}
|
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
import * as fs from "node:fs";
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { DatabaseSync } from "node:sqlite";
|
|
4
|
-
import { ProjectionStateError } from "../../atomic-publish.js";
|
|
5
|
-
import { PROJECTION_GENERATION_STORE_FILE, } from "../generation/store.js";
|
|
6
|
-
import { configureHeartBusyTimeout } from "../heart.js";
|
|
7
|
-
function databasePath(projectionDirectory) {
|
|
8
|
-
return path.join(projectionDirectory, PROJECTION_GENERATION_STORE_FILE);
|
|
9
|
-
}
|
|
10
|
-
function openDatabase(projectionDirectory, readOnly) {
|
|
11
|
-
const target = databasePath(projectionDirectory);
|
|
12
|
-
if (!fs.lstatSync(target).isFile()) {
|
|
13
|
-
throw new ProjectionStateError("projection heart must be a regular file");
|
|
14
|
-
}
|
|
15
|
-
const database = new DatabaseSync(target, {
|
|
16
|
-
readOnly,
|
|
17
|
-
enableForeignKeyConstraints: true,
|
|
18
|
-
enableDoubleQuotedStringLiterals: false,
|
|
19
|
-
allowExtension: false,
|
|
20
|
-
});
|
|
21
|
-
configureHeartBusyTimeout(database);
|
|
22
|
-
if (!readOnly) {
|
|
23
|
-
database.exec("PRAGMA foreign_keys = ON");
|
|
24
|
-
database.exec("PRAGMA trusted_schema = OFF");
|
|
25
|
-
}
|
|
26
|
-
return database;
|
|
27
|
-
}
|
|
28
|
-
function rollbackQuietly(database) {
|
|
29
|
-
try {
|
|
30
|
-
database.exec("ROLLBACK");
|
|
31
|
-
}
|
|
32
|
-
catch {
|
|
33
|
-
// Preserve the transition failure.
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
export function withRead(projectionDirectory, read) {
|
|
37
|
-
const database = openDatabase(projectionDirectory, true);
|
|
38
|
-
try {
|
|
39
|
-
return read(database);
|
|
40
|
-
}
|
|
41
|
-
finally {
|
|
42
|
-
database.close();
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
export function withReadSnapshot(projectionDirectory, read) {
|
|
46
|
-
const database = openDatabase(projectionDirectory, true);
|
|
47
|
-
try {
|
|
48
|
-
database.exec("BEGIN");
|
|
49
|
-
try {
|
|
50
|
-
const result = read(database);
|
|
51
|
-
database.exec("COMMIT");
|
|
52
|
-
return result;
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
rollbackQuietly(database);
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
finally {
|
|
60
|
-
database.close();
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
export function withWrite(projectionDirectory, write) {
|
|
64
|
-
const database = openDatabase(projectionDirectory, false);
|
|
65
|
-
try {
|
|
66
|
-
database.exec("BEGIN IMMEDIATE");
|
|
67
|
-
try {
|
|
68
|
-
const result = write(database);
|
|
69
|
-
database.exec("COMMIT");
|
|
70
|
-
return result;
|
|
71
|
-
}
|
|
72
|
-
catch (error) {
|
|
73
|
-
rollbackQuietly(database);
|
|
74
|
-
throw error;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
finally {
|
|
78
|
-
database.close();
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
export function openProjectionTellObserver(projectionDirectory) {
|
|
82
|
-
const database = openDatabase(projectionDirectory, true);
|
|
83
|
-
let closed = false;
|
|
84
|
-
let observedDataVersion;
|
|
85
|
-
const assertOpen = () => {
|
|
86
|
-
if (closed)
|
|
87
|
-
throw new ProjectionStateError("projection tell observer is closed");
|
|
88
|
-
};
|
|
89
|
-
const readDataVersion = () => {
|
|
90
|
-
assertOpen();
|
|
91
|
-
const row = database.prepare("PRAGMA data_version").get();
|
|
92
|
-
const raw = row?.data_version;
|
|
93
|
-
const value = typeof raw === "bigint" ? Number(raw) : raw;
|
|
94
|
-
if (typeof value !== "number" || !Number.isSafeInteger(value) || value < 0) {
|
|
95
|
-
throw new ProjectionStateError("PRAGMA data_version returned an invalid value");
|
|
96
|
-
}
|
|
97
|
-
return value;
|
|
98
|
-
};
|
|
99
|
-
return {
|
|
100
|
-
hasDataVersionChanged() {
|
|
101
|
-
const current = readDataVersion();
|
|
102
|
-
if (observedDataVersion === undefined) {
|
|
103
|
-
observedDataVersion = current;
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
106
|
-
if (current === observedDataVersion)
|
|
107
|
-
return false;
|
|
108
|
-
observedDataVersion = current;
|
|
109
|
-
return true;
|
|
110
|
-
},
|
|
111
|
-
hasInbox() {
|
|
112
|
-
assertOpen();
|
|
113
|
-
return database.prepare(`
|
|
114
|
-
SELECT 1
|
|
115
|
-
FROM projection_tells
|
|
116
|
-
WHERE state = 'inbox'
|
|
117
|
-
LIMIT 1
|
|
118
|
-
`).get() !== undefined;
|
|
119
|
-
},
|
|
120
|
-
close() {
|
|
121
|
-
if (closed)
|
|
122
|
-
return;
|
|
123
|
-
closed = true;
|
|
124
|
-
database.close();
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
}
|
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
-
import * as fs from "node:fs";
|
|
3
|
-
import * as path from "node:path";
|
|
4
|
-
import { DatabaseSync } from "node:sqlite";
|
|
5
|
-
import { z } from "zod";
|
|
6
|
-
const COORDINATION_SCHEMA_VERSION = 1;
|
|
7
|
-
const MAX_RECEIPT_BYTES = 1_048_576;
|
|
8
|
-
const DATABASE_BUSY_TIMEOUT_MS = 5_000;
|
|
9
|
-
export const VERIFICATION_COMPLETED_RECEIPT_LIMIT = 8;
|
|
10
|
-
const outputTailSchema = z.object({
|
|
11
|
-
tail: z.string(),
|
|
12
|
-
truncated: z.boolean(),
|
|
13
|
-
omittedBytes: z.number().int().nonnegative(),
|
|
14
|
-
}).strict();
|
|
15
|
-
const executionSchema = z.discriminatedUnion("status", [
|
|
16
|
-
z.object({
|
|
17
|
-
status: z.literal("executed"),
|
|
18
|
-
code: z.number().int(),
|
|
19
|
-
durationMs: z.number().nonnegative(),
|
|
20
|
-
stdout: outputTailSchema,
|
|
21
|
-
stderr: outputTailSchema,
|
|
22
|
-
}).strict(),
|
|
23
|
-
z.object({
|
|
24
|
-
status: z.literal("runner_failed"),
|
|
25
|
-
durationMs: z.number().nonnegative(),
|
|
26
|
-
stdout: outputTailSchema,
|
|
27
|
-
stderr: outputTailSchema,
|
|
28
|
-
cause: z.object({
|
|
29
|
-
code: z.enum(["SPAWN_FAILED", "PROCESS_TERMINATED", "TIMED_OUT", "CANCELLED", "TREE_TERMINATION_FAILED"]),
|
|
30
|
-
message: z.string(),
|
|
31
|
-
}).strict(),
|
|
32
|
-
}).strict(),
|
|
33
|
-
]);
|
|
34
|
-
const resultSchema = z.object({
|
|
35
|
-
source: z.enum(["contract", "repository-gate"]),
|
|
36
|
-
label: z.string(),
|
|
37
|
-
declaration: z.string(),
|
|
38
|
-
command: z.string(),
|
|
39
|
-
args: z.array(z.string()),
|
|
40
|
-
shell: z.boolean(),
|
|
41
|
-
timeoutMs: z.number().int().positive(),
|
|
42
|
-
candidate: z.object({ head: z.string(), tree: z.string() }).strict(),
|
|
43
|
-
executor: z.string(),
|
|
44
|
-
execution: executionSchema,
|
|
45
|
-
}).strict();
|
|
46
|
-
const receiptSchema = z.object({
|
|
47
|
-
version: z.literal(COORDINATION_SCHEMA_VERSION),
|
|
48
|
-
results: z.array(resultSchema),
|
|
49
|
-
}).strict();
|
|
50
|
-
function processIsLive(pid) {
|
|
51
|
-
if (pid <= 0)
|
|
52
|
-
return false;
|
|
53
|
-
try {
|
|
54
|
-
process.kill(pid, 0);
|
|
55
|
-
return true;
|
|
56
|
-
}
|
|
57
|
-
catch (error) {
|
|
58
|
-
return !(error && typeof error === "object" && "code" in error && error.code === "ESRCH");
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
function rollbackQuietly(database) {
|
|
62
|
-
try {
|
|
63
|
-
database.exec("ROLLBACK");
|
|
64
|
-
}
|
|
65
|
-
catch { }
|
|
66
|
-
}
|
|
67
|
-
function pragmaNumber(database, name) {
|
|
68
|
-
const row = database.prepare(`PRAGMA ${name}`).get();
|
|
69
|
-
const value = row ? Object.values(row)[0] : undefined;
|
|
70
|
-
if (typeof value !== "number" && typeof value !== "bigint") {
|
|
71
|
-
throw new Error(`verification coordination PRAGMA ${name} is invalid`);
|
|
72
|
-
}
|
|
73
|
-
return Number(value);
|
|
74
|
-
}
|
|
75
|
-
function initializeOrValidate(database) {
|
|
76
|
-
database.exec("BEGIN EXCLUSIVE");
|
|
77
|
-
try {
|
|
78
|
-
const version = pragmaNumber(database, "user_version");
|
|
79
|
-
const objects = database.prepare(`
|
|
80
|
-
SELECT name, type FROM sqlite_schema
|
|
81
|
-
WHERE name NOT LIKE 'sqlite_%' ORDER BY name
|
|
82
|
-
`).all();
|
|
83
|
-
if (version === 0 && objects.length === 0) {
|
|
84
|
-
database.exec(`
|
|
85
|
-
CREATE TABLE verification_coordination_schema (
|
|
86
|
-
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
87
|
-
version INTEGER NOT NULL CHECK (version = ${COORDINATION_SCHEMA_VERSION})
|
|
88
|
-
) STRICT;
|
|
89
|
-
CREATE TABLE verification_executions (
|
|
90
|
-
fingerprint TEXT PRIMARY KEY CHECK (length(fingerprint) = 64),
|
|
91
|
-
owner_token TEXT NOT NULL CHECK (length(owner_token) > 0),
|
|
92
|
-
owner_pid INTEGER NOT NULL CHECK (owner_pid >= 0),
|
|
93
|
-
started_at_ms INTEGER NOT NULL CHECK (started_at_ms >= 0),
|
|
94
|
-
supervisor_pid INTEGER CHECK (supervisor_pid IS NULL OR supervisor_pid > 0),
|
|
95
|
-
supervisor_platform TEXT CHECK (supervisor_platform IS NULL OR supervisor_platform IN ('posix', 'win32')),
|
|
96
|
-
completed_at_ms INTEGER CHECK (completed_at_ms IS NULL OR completed_at_ms >= 0),
|
|
97
|
-
receipt_json TEXT CHECK (receipt_json IS NULL OR json_valid(receipt_json)),
|
|
98
|
-
CHECK ((supervisor_pid IS NULL) = (supervisor_platform IS NULL)),
|
|
99
|
-
CHECK ((receipt_json IS NULL) = (completed_at_ms IS NULL)),
|
|
100
|
-
CHECK (receipt_json IS NULL OR (owner_pid = 0 AND supervisor_pid IS NULL))
|
|
101
|
-
) STRICT;
|
|
102
|
-
`);
|
|
103
|
-
database.prepare("INSERT INTO verification_coordination_schema (singleton, version) VALUES (1, ?)")
|
|
104
|
-
.run(COORDINATION_SCHEMA_VERSION);
|
|
105
|
-
database.exec(`PRAGMA user_version = ${COORDINATION_SCHEMA_VERSION}`);
|
|
106
|
-
}
|
|
107
|
-
else if (version !== COORDINATION_SCHEMA_VERSION) {
|
|
108
|
-
throw new Error(`incompatible verification coordination schema version ${version}; expected ${COORDINATION_SCHEMA_VERSION}`);
|
|
109
|
-
}
|
|
110
|
-
const signature = database.prepare(`
|
|
111
|
-
SELECT name, type FROM sqlite_schema
|
|
112
|
-
WHERE name NOT LIKE 'sqlite_%' ORDER BY name
|
|
113
|
-
`).all().map((object) => {
|
|
114
|
-
const row = object;
|
|
115
|
-
return `${row.type}:${row.name}`;
|
|
116
|
-
}).join(",");
|
|
117
|
-
if (signature !== "table:verification_coordination_schema,table:verification_executions") {
|
|
118
|
-
throw new Error(`verification coordination schema contains unexpected objects: ${signature || "none"}`);
|
|
119
|
-
}
|
|
120
|
-
const columns = database.prepare("PRAGMA table_info(verification_executions)").all();
|
|
121
|
-
const columnSignature = columns
|
|
122
|
-
.map((column) => `${column.name}:${column.type}:${Number(column.notnull)}:${Number(column.pk)}`)
|
|
123
|
-
.join(",");
|
|
124
|
-
if (columnSignature !== "fingerprint:TEXT:1:1,owner_token:TEXT:1:0,owner_pid:INTEGER:1:0,started_at_ms:INTEGER:1:0,supervisor_pid:INTEGER:0:0,supervisor_platform:TEXT:0:0,completed_at_ms:INTEGER:0:0,receipt_json:TEXT:0:0") {
|
|
125
|
-
throw new Error("verification coordination execution schema is malformed");
|
|
126
|
-
}
|
|
127
|
-
const metadata = database.prepare("SELECT singleton, version FROM verification_coordination_schema").all();
|
|
128
|
-
if (metadata.length !== 1 || Number(metadata[0].singleton) !== 1 || Number(metadata[0].version) !== COORDINATION_SCHEMA_VERSION) {
|
|
129
|
-
throw new Error("verification coordination schema metadata is malformed");
|
|
130
|
-
}
|
|
131
|
-
const quickCheck = database.prepare("PRAGMA quick_check").get();
|
|
132
|
-
if (!quickCheck || Object.values(quickCheck)[0] !== "ok") {
|
|
133
|
-
throw new Error("verification coordination database failed quick_check");
|
|
134
|
-
}
|
|
135
|
-
database.exec("COMMIT");
|
|
136
|
-
}
|
|
137
|
-
catch (error) {
|
|
138
|
-
rollbackQuietly(database);
|
|
139
|
-
throw error;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
function parseReceipt(raw) {
|
|
143
|
-
if (Buffer.byteLength(raw) > MAX_RECEIPT_BYTES)
|
|
144
|
-
throw new Error("verification receipt exceeds its size bound");
|
|
145
|
-
return receiptSchema.parse(JSON.parse(raw)).results;
|
|
146
|
-
}
|
|
147
|
-
function serializeReceipt(results) {
|
|
148
|
-
const raw = JSON.stringify(receiptSchema.parse({ version: COORDINATION_SCHEMA_VERSION, results }));
|
|
149
|
-
if (Buffer.byteLength(raw) > MAX_RECEIPT_BYTES)
|
|
150
|
-
throw new Error("verification receipt exceeds its size bound");
|
|
151
|
-
return raw;
|
|
152
|
-
}
|
|
153
|
-
export class VerificationCoordination {
|
|
154
|
-
#databasePath;
|
|
155
|
-
constructor(repositoryRoot) {
|
|
156
|
-
const directory = path.join(repositoryRoot, ".keiyaku", "runtime");
|
|
157
|
-
fs.mkdirSync(directory, { recursive: true });
|
|
158
|
-
this.#databasePath = path.join(directory, "verification.sqlite");
|
|
159
|
-
this.#withDatabase(() => undefined);
|
|
160
|
-
}
|
|
161
|
-
fingerprint(key) {
|
|
162
|
-
return createHash("sha256").update(key).digest("hex");
|
|
163
|
-
}
|
|
164
|
-
claim(fingerprint, staleAfterMs, nowMs = Date.now()) {
|
|
165
|
-
return this.#withTransaction((database) => {
|
|
166
|
-
const row = database.prepare(`
|
|
167
|
-
SELECT owner_token, owner_pid, started_at_ms, supervisor_pid, supervisor_platform, completed_at_ms, receipt_json
|
|
168
|
-
FROM verification_executions WHERE fingerprint = ?
|
|
169
|
-
`).get(fingerprint);
|
|
170
|
-
if (row?.receipt_json !== null && row?.receipt_json !== undefined) {
|
|
171
|
-
return { status: "receipt", results: parseReceipt(row.receipt_json) };
|
|
172
|
-
}
|
|
173
|
-
const token = randomUUID();
|
|
174
|
-
if (!row) {
|
|
175
|
-
database.prepare(`
|
|
176
|
-
INSERT INTO verification_executions
|
|
177
|
-
(fingerprint, owner_token, owner_pid, started_at_ms, supervisor_pid, supervisor_platform, completed_at_ms, receipt_json)
|
|
178
|
-
VALUES (?, ?, ?, ?, NULL, NULL, NULL, NULL)
|
|
179
|
-
`).run(fingerprint, token, process.pid, nowMs);
|
|
180
|
-
return { status: "owner", token };
|
|
181
|
-
}
|
|
182
|
-
const ownerPid = Number(row.owner_pid);
|
|
183
|
-
const stale = nowMs - Number(row.started_at_ms) > staleAfterMs;
|
|
184
|
-
if (processIsLive(ownerPid) && !stale)
|
|
185
|
-
return { status: "wait" };
|
|
186
|
-
database.prepare(`
|
|
187
|
-
UPDATE verification_executions
|
|
188
|
-
SET owner_token = ?, owner_pid = ?, started_at_ms = ?
|
|
189
|
-
WHERE fingerprint = ?
|
|
190
|
-
`).run(token, process.pid, nowMs, fingerprint);
|
|
191
|
-
const platform = row.supervisor_platform;
|
|
192
|
-
const supervisorPid = row.supervisor_pid === null ? undefined : Number(row.supervisor_pid);
|
|
193
|
-
return {
|
|
194
|
-
status: "owner",
|
|
195
|
-
token,
|
|
196
|
-
...(supervisorPid && (platform === "posix" || platform === "win32")
|
|
197
|
-
? { priorSupervisor: { pid: supervisorPid, platform } }
|
|
198
|
-
: {}),
|
|
199
|
-
};
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
recordSupervisor(fingerprint, token, identity) {
|
|
203
|
-
this.#ownedUpdate(fingerprint, token, (database) => {
|
|
204
|
-
database.prepare(`
|
|
205
|
-
UPDATE verification_executions SET supervisor_pid = ?, supervisor_platform = ?
|
|
206
|
-
WHERE fingerprint = ? AND owner_token = ? AND receipt_json IS NULL
|
|
207
|
-
`).run(identity.pid, identity.platform, fingerprint, token);
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
clearSupervisor(fingerprint, token) {
|
|
211
|
-
this.#ownedUpdate(fingerprint, token, (database) => {
|
|
212
|
-
database.prepare(`
|
|
213
|
-
UPDATE verification_executions SET supervisor_pid = NULL, supervisor_platform = NULL
|
|
214
|
-
WHERE fingerprint = ? AND owner_token = ? AND receipt_json IS NULL
|
|
215
|
-
`).run(fingerprint, token);
|
|
216
|
-
});
|
|
217
|
-
}
|
|
218
|
-
complete(fingerprint, token, results, completedAtMs = Date.now()) {
|
|
219
|
-
const recoveryRequired = results.some((result) => result.execution.status === "runner_failed"
|
|
220
|
-
&& result.execution.cause.code === "TREE_TERMINATION_FAILED");
|
|
221
|
-
return this.#withTransaction((database) => {
|
|
222
|
-
if (recoveryRequired) {
|
|
223
|
-
const changed = database.prepare(`
|
|
224
|
-
UPDATE verification_executions
|
|
225
|
-
SET owner_pid = 0, started_at_ms = 0
|
|
226
|
-
WHERE fingerprint = ? AND owner_token = ? AND receipt_json IS NULL
|
|
227
|
-
`).run(fingerprint, token).changes;
|
|
228
|
-
return Number(changed) === 1
|
|
229
|
-
? { status: "recovery-required" }
|
|
230
|
-
: { status: "ownership-lost" };
|
|
231
|
-
}
|
|
232
|
-
const receipt = serializeReceipt(results);
|
|
233
|
-
const changed = database.prepare(`
|
|
234
|
-
UPDATE verification_executions
|
|
235
|
-
SET owner_pid = 0, supervisor_pid = NULL, supervisor_platform = NULL, completed_at_ms = ?, receipt_json = ?
|
|
236
|
-
WHERE fingerprint = ? AND owner_token = ? AND receipt_json IS NULL
|
|
237
|
-
`).run(completedAtMs, receipt, fingerprint, token).changes;
|
|
238
|
-
if (Number(changed) !== 1)
|
|
239
|
-
return { status: "ownership-lost" };
|
|
240
|
-
const countRow = database.prepare(`
|
|
241
|
-
SELECT count(*) AS count FROM verification_executions WHERE receipt_json IS NOT NULL
|
|
242
|
-
`).get();
|
|
243
|
-
const excess = Number(countRow.count) - VERIFICATION_COMPLETED_RECEIPT_LIMIT;
|
|
244
|
-
if (excess > 0) {
|
|
245
|
-
database.prepare(`
|
|
246
|
-
DELETE FROM verification_executions
|
|
247
|
-
WHERE fingerprint IN (
|
|
248
|
-
SELECT fingerprint FROM verification_executions
|
|
249
|
-
WHERE receipt_json IS NOT NULL
|
|
250
|
-
ORDER BY completed_at_ms ASC, fingerprint ASC
|
|
251
|
-
LIMIT ?
|
|
252
|
-
)
|
|
253
|
-
`).run(excess);
|
|
254
|
-
}
|
|
255
|
-
return { status: "published" };
|
|
256
|
-
});
|
|
257
|
-
}
|
|
258
|
-
releaseForTakeover(fingerprint, token) {
|
|
259
|
-
this.#withTransaction((database) => {
|
|
260
|
-
database.prepare(`
|
|
261
|
-
UPDATE verification_executions SET owner_pid = 0, started_at_ms = 0
|
|
262
|
-
WHERE fingerprint = ? AND owner_token = ? AND receipt_json IS NULL
|
|
263
|
-
`).run(fingerprint, token);
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
#ownedUpdate(fingerprint, token, update) {
|
|
267
|
-
this.#withTransaction((database) => {
|
|
268
|
-
update(database);
|
|
269
|
-
const row = database.prepare(`
|
|
270
|
-
SELECT owner_token FROM verification_executions
|
|
271
|
-
WHERE fingerprint = ? AND receipt_json IS NULL
|
|
272
|
-
`).get(fingerprint);
|
|
273
|
-
if (row?.owner_token !== token)
|
|
274
|
-
throw new Error("verification coordination ownership changed");
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
#withTransaction(operation) {
|
|
278
|
-
return this.#withDatabase((database) => {
|
|
279
|
-
database.exec("BEGIN IMMEDIATE");
|
|
280
|
-
try {
|
|
281
|
-
const result = operation(database);
|
|
282
|
-
database.exec("COMMIT");
|
|
283
|
-
return result;
|
|
284
|
-
}
|
|
285
|
-
catch (error) {
|
|
286
|
-
rollbackQuietly(database);
|
|
287
|
-
throw error;
|
|
288
|
-
}
|
|
289
|
-
});
|
|
290
|
-
}
|
|
291
|
-
#withDatabase(operation) {
|
|
292
|
-
const database = new DatabaseSync(this.#databasePath, {
|
|
293
|
-
enableDoubleQuotedStringLiterals: false,
|
|
294
|
-
allowExtension: false,
|
|
295
|
-
});
|
|
296
|
-
try {
|
|
297
|
-
database.exec(`PRAGMA busy_timeout = ${DATABASE_BUSY_TIMEOUT_MS}`);
|
|
298
|
-
initializeOrValidate(database);
|
|
299
|
-
return operation(database);
|
|
300
|
-
}
|
|
301
|
-
finally {
|
|
302
|
-
database.close();
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
}
|