@yieldstar/sqlite-runtime 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bun.js +15 -17
- package/dist/dao/effect-waiters-dao.d.ts +19 -0
- package/dist/dao/effect-waiters-dao.d.ts.map +1 -0
- package/dist/dao/effects-dao.d.ts +17 -0
- package/dist/dao/effects-dao.d.ts.map +1 -0
- package/dist/dao/executions-dao.d.ts +23 -0
- package/dist/dao/executions-dao.d.ts.map +1 -0
- package/dist/dao/start-requests-dao.d.ts +15 -0
- package/dist/dao/start-requests-dao.d.ts.map +1 -0
- package/dist/dao/step-response-dao.d.ts +1 -0
- package/dist/dao/step-response-dao.d.ts.map +1 -1
- package/dist/dao/store-waiters-dao.d.ts +26 -0
- package/dist/dao/store-waiters-dao.d.ts.map +1 -0
- package/dist/index-083bmj5k.js +800 -0
- package/dist/index-4kybaxga.js +798 -0
- package/dist/index-a8thbaee.js +718 -0
- package/dist/index-ag93sv33.js +664 -0
- package/dist/index-eabcgmaa.js +996 -0
- package/dist/index-gjfs8rn6.js +997 -0
- package/dist/index-h3h7r4b6.js +981 -0
- package/dist/index-ma984fze.js +718 -0
- package/dist/index-ne28djfn.js +718 -0
- package/dist/index-v6rn0pr6.js +664 -0
- package/dist/index-wx6vk994.js +669 -0
- package/dist/index.d.ts +3 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -13
- package/dist/node.js +15 -17
- package/dist/sqlite-effect.d.ts +30 -0
- package/dist/sqlite-effect.d.ts.map +1 -0
- package/dist/sqlite-execution.d.ts +54 -0
- package/dist/sqlite-execution.d.ts.map +1 -0
- package/dist/sqlite-heap.d.ts +12 -4
- package/dist/sqlite-heap.d.ts.map +1 -1
- package/dist/sqlite-runtime.d.ts +22 -0
- package/dist/sqlite-runtime.d.ts.map +1 -0
- package/dist/sqlite-store.d.ts +31 -25
- package/dist/sqlite-store.d.ts.map +1 -1
- package/dist/sqlite-transaction.d.ts +9 -0
- package/dist/sqlite-transaction.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/dao/effect-waiters-dao.ts +63 -0
- package/src/dao/effects-dao.ts +123 -0
- package/src/dao/executions-dao.ts +188 -0
- package/src/dao/start-requests-dao.ts +72 -0
- package/src/dao/step-response-dao.ts +16 -1
- package/src/dao/store-waiters-dao.ts +92 -0
- package/src/index.ts +6 -4
- package/src/sqlite-effect.ts +99 -0
- package/src/sqlite-execution.ts +238 -0
- package/src/sqlite-heap.ts +64 -12
- package/src/sqlite-runtime.ts +36 -0
- package/src/sqlite-store.ts +184 -520
- package/src/sqlite-transaction.ts +19 -0
- package/dist/dao/task-queue-dao.d.ts +0 -21
- package/dist/dao/task-queue-dao.d.ts.map +0 -1
- package/dist/dao/timers-dao.d.ts +0 -22
- package/dist/dao/timers-dao.d.ts.map +0 -1
- package/dist/index-4vq70w0r.js +0 -796
- package/dist/index-s8wqgfwt.js +0 -19
- package/dist/index-x891hxwg.js +0 -796
- package/dist/node-driver.d.ts +0 -10
- package/dist/node-driver.d.ts.map +0 -1
- package/dist/sqlite-event-loop.d.ts +0 -18
- package/dist/sqlite-event-loop.d.ts.map +0 -1
- package/dist/sqlite-scheduler.d.ts +0 -13
- package/dist/sqlite-scheduler.d.ts.map +0 -1
- package/dist/sqlite-store.test.d.ts +0 -2
- package/dist/sqlite-store.test.d.ts.map +0 -1
- package/dist/sqlite-task-queue.d.ts +0 -26
- package/dist/sqlite-task-queue.d.ts.map +0 -1
- package/dist/sqlite-timers.d.ts +0 -18
- package/dist/sqlite-timers.d.ts.map +0 -1
- package/src/dao/task-queue-dao.ts +0 -79
- package/src/dao/timers-dao.ts +0 -81
- package/src/sqlite-event-loop.ts +0 -41
- package/src/sqlite-scheduler.ts +0 -24
- package/src/sqlite-task-queue.ts +0 -64
- package/src/sqlite-timers.ts +0 -40
- package/tsconfig.tsbuildinfo +0 -1
- /package/dist/{index-h7bm9xg4.js → index-dapqh99v.js} +0 -0
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import {
|
|
2
|
+
acknowledgeExecution,
|
|
3
|
+
claimExecution,
|
|
4
|
+
createExecutionState,
|
|
5
|
+
createStartedExecution,
|
|
6
|
+
executionIdConflict,
|
|
7
|
+
executionNotFound,
|
|
8
|
+
isExecutionClaimable,
|
|
9
|
+
matchStartRequest,
|
|
10
|
+
prepareExecutionAdmission,
|
|
11
|
+
prepareExecutionStart,
|
|
12
|
+
releaseExecutionLease,
|
|
13
|
+
renewExecutionLease,
|
|
14
|
+
requestExecutionWake,
|
|
15
|
+
toExecutionRecord,
|
|
16
|
+
type ExecutionAcknowledgeResult,
|
|
17
|
+
type ExecutionAdmissionRequest,
|
|
18
|
+
type ExecutionAdmissionResult,
|
|
19
|
+
type ExecutionClient,
|
|
20
|
+
type ExecutionLease,
|
|
21
|
+
type ExecutionRecord,
|
|
22
|
+
type ExecutionStartRequest,
|
|
23
|
+
type ExecutionStartResult,
|
|
24
|
+
type ExecutionReleaseResult,
|
|
25
|
+
type ExecutionRenewResult,
|
|
26
|
+
type ExecutionState,
|
|
27
|
+
type ExecutionWakeResult,
|
|
28
|
+
type WorkflowRunOutcome,
|
|
29
|
+
} from "@yieldstar/core";
|
|
30
|
+
import { ExecutionsDao } from "./dao/executions-dao";
|
|
31
|
+
import { StartRequestsDao } from "./dao/start-requests-dao";
|
|
32
|
+
import type { SqliteDriver } from "./sqlite-driver";
|
|
33
|
+
import { runTransaction } from "./sqlite-transaction";
|
|
34
|
+
|
|
35
|
+
export type SqliteExecutionClientOptions = {
|
|
36
|
+
/** Reads the current time. Defaults to `Date.now`. */
|
|
37
|
+
clock?: () => number;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Execution records in the `executions` table. Every operation loads a row,
|
|
42
|
+
* applies a pure transition from @yieldstar/core and writes it back inside one
|
|
43
|
+
* IMMEDIATE transaction, so processes sharing the database file see the same
|
|
44
|
+
* semantics as the memory runtime.
|
|
45
|
+
*/
|
|
46
|
+
export class SqliteExecutionClient implements ExecutionClient {
|
|
47
|
+
private db: SqliteDriver;
|
|
48
|
+
private executionsDao: ExecutionsDao;
|
|
49
|
+
private startRequestsDao: StartRequestsDao;
|
|
50
|
+
private clock: () => number;
|
|
51
|
+
|
|
52
|
+
constructor(db: SqliteDriver, options: SqliteExecutionClientOptions = {}) {
|
|
53
|
+
this.db = db;
|
|
54
|
+
this.executionsDao = new ExecutionsDao(db);
|
|
55
|
+
this.startRequestsDao = new StartRequestsDao(db);
|
|
56
|
+
// Read Date.now lazily so fake timers installed later still apply.
|
|
57
|
+
this.clock = options.clock ?? (() => Date.now());
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async admit(
|
|
61
|
+
request: ExecutionAdmissionRequest
|
|
62
|
+
): Promise<ExecutionAdmissionResult> {
|
|
63
|
+
const prepared = prepareExecutionAdmission(request);
|
|
64
|
+
if (!prepared.ok) {
|
|
65
|
+
return {
|
|
66
|
+
status: "rejected",
|
|
67
|
+
code: prepared.code,
|
|
68
|
+
message: prepared.message,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return runTransaction(this.db, () => {
|
|
73
|
+
const state = createExecutionState({
|
|
74
|
+
executionId: prepared.executionId,
|
|
75
|
+
workflowId: request.workflowId,
|
|
76
|
+
encoded: prepared.encoded,
|
|
77
|
+
now: this.clock(),
|
|
78
|
+
});
|
|
79
|
+
// The record is inserted already due, so it and its first wake commit
|
|
80
|
+
// together.
|
|
81
|
+
if (!this.executionsDao.insertState(state)) {
|
|
82
|
+
return executionIdConflict(prepared.executionId);
|
|
83
|
+
}
|
|
84
|
+
return { status: "accepted", executionId: prepared.executionId };
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async start(request: ExecutionStartRequest): Promise<ExecutionStartResult> {
|
|
89
|
+
const prepared = prepareExecutionStart(request);
|
|
90
|
+
if (!prepared.ok) return prepared.result;
|
|
91
|
+
const { start } = prepared;
|
|
92
|
+
|
|
93
|
+
// The lookup, the target and the start request share one IMMEDIATE
|
|
94
|
+
// transaction, so concurrent starts with one request ID serialize and a
|
|
95
|
+
// new start commits all-or-nothing.
|
|
96
|
+
return runTransaction(this.db, () => {
|
|
97
|
+
const recorded = this.startRequestsDao.get(
|
|
98
|
+
start.callerExecutionId,
|
|
99
|
+
start.stepKey
|
|
100
|
+
);
|
|
101
|
+
if (recorded) return matchStartRequest(recorded, start);
|
|
102
|
+
|
|
103
|
+
const caller = this.executionsDao.getState(start.callerExecutionId);
|
|
104
|
+
if (!caller) {
|
|
105
|
+
throw new Error(
|
|
106
|
+
`Cannot start a workflow from execution "${start.callerExecutionId}": it does not exist`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
let created = createStartedExecution({ start, caller, now: this.clock() });
|
|
110
|
+
while (!this.executionsDao.insertState(created.target)) {
|
|
111
|
+
created = createStartedExecution({ start, caller, now: this.clock() });
|
|
112
|
+
}
|
|
113
|
+
this.startRequestsDao.insert(created.startRequest);
|
|
114
|
+
return {
|
|
115
|
+
status: "accepted" as const,
|
|
116
|
+
executionId: created.target.executionId,
|
|
117
|
+
};
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async get(executionId: string): Promise<ExecutionRecord | undefined> {
|
|
122
|
+
const state = this.executionsDao.getState(executionId);
|
|
123
|
+
return state ? toExecutionRecord(state, this.clock()) : undefined;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async requestWake(request: {
|
|
127
|
+
executionId: string;
|
|
128
|
+
wakeAt: number;
|
|
129
|
+
}): Promise<ExecutionWakeResult> {
|
|
130
|
+
return runTransaction(this.db, () => {
|
|
131
|
+
const state = this.executionsDao.getState(request.executionId);
|
|
132
|
+
if (!state) return executionNotFound(request.executionId);
|
|
133
|
+
|
|
134
|
+
const next = requestExecutionWake(state, request.wakeAt, this.clock());
|
|
135
|
+
if (!next) return { status: "ignored" };
|
|
136
|
+
|
|
137
|
+
this.executionsDao.updateState(next);
|
|
138
|
+
return { status: "requested" };
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async claimDue(request: {
|
|
143
|
+
limit: number;
|
|
144
|
+
duration: number;
|
|
145
|
+
}): Promise<ExecutionLease[]> {
|
|
146
|
+
if (request.limit <= 0) return [];
|
|
147
|
+
// The IMMEDIATE transaction holds the write lock, so concurrent claims from
|
|
148
|
+
// any process sharing the file see each other's leases.
|
|
149
|
+
return runTransaction(this.db, () => {
|
|
150
|
+
const now = this.clock();
|
|
151
|
+
const leases: ExecutionLease[] = [];
|
|
152
|
+
|
|
153
|
+
for (const executionId of this.executionsDao.getClaimableIds(
|
|
154
|
+
now,
|
|
155
|
+
request.limit
|
|
156
|
+
)) {
|
|
157
|
+
const state = this.executionsDao.getState(executionId);
|
|
158
|
+
if (!state || !isExecutionClaimable(state, now)) continue;
|
|
159
|
+
|
|
160
|
+
const claimed = claimExecution(state, {
|
|
161
|
+
now,
|
|
162
|
+
duration: request.duration,
|
|
163
|
+
});
|
|
164
|
+
this.executionsDao.updateState(claimed.state);
|
|
165
|
+
leases.push(claimed.lease);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return leases;
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
async renew(request: {
|
|
173
|
+
lease: ExecutionLease;
|
|
174
|
+
duration: number;
|
|
175
|
+
}): Promise<ExecutionRenewResult> {
|
|
176
|
+
return this.transition(request.lease, (state) =>
|
|
177
|
+
renewExecutionLease(state, {
|
|
178
|
+
lease: request.lease,
|
|
179
|
+
duration: request.duration,
|
|
180
|
+
now: this.clock(),
|
|
181
|
+
})
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
async release(request: {
|
|
186
|
+
lease: ExecutionLease;
|
|
187
|
+
}): Promise<ExecutionReleaseResult> {
|
|
188
|
+
return this.transition(request.lease, (state) =>
|
|
189
|
+
releaseExecutionLease(state, { lease: request.lease, now: this.clock() })
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async acknowledge(request: {
|
|
194
|
+
lease: ExecutionLease;
|
|
195
|
+
outcome: WorkflowRunOutcome;
|
|
196
|
+
}): Promise<ExecutionAcknowledgeResult> {
|
|
197
|
+
return this.transition(request.lease, (state) =>
|
|
198
|
+
acknowledgeExecution(state, {
|
|
199
|
+
lease: request.lease,
|
|
200
|
+
outcome: request.outcome,
|
|
201
|
+
now: this.clock(),
|
|
202
|
+
})
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Applies a fenced transition in one transaction. The transition checks the
|
|
208
|
+
* lease against the stored token, so the check and the write are atomic.
|
|
209
|
+
*/
|
|
210
|
+
private transition<R>(
|
|
211
|
+
lease: ExecutionLease,
|
|
212
|
+
fn: (state: ExecutionState) => { state: ExecutionState; result: R }
|
|
213
|
+
): Promise<R | { status: "lost" }> {
|
|
214
|
+
return Promise.resolve(
|
|
215
|
+
runTransaction(this.db, () => {
|
|
216
|
+
const state = this.executionsDao.getState(lease.executionId);
|
|
217
|
+
if (!state) return { status: "lost" as const };
|
|
218
|
+
const next = fn(state);
|
|
219
|
+
if (next.state !== state) this.executionsDao.updateState(next.state);
|
|
220
|
+
return next.result;
|
|
221
|
+
})
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* @internal Requests a wake from inside a transaction the caller already
|
|
227
|
+
* holds on the same database, so the wake commits with the caller's write
|
|
228
|
+
* (e.g. a store commit). `wakeAt` defaults to the runtime clock's now.
|
|
229
|
+
* Unknown and terminal executions are ignored.
|
|
230
|
+
*/
|
|
231
|
+
wakeInTransaction(executionId: string, wakeAt: number = this.clock()) {
|
|
232
|
+
const state = this.executionsDao.getState(executionId);
|
|
233
|
+
if (!state) return;
|
|
234
|
+
|
|
235
|
+
const next = requestExecutionWake(state, wakeAt, this.clock());
|
|
236
|
+
if (next) this.executionsDao.updateState(next);
|
|
237
|
+
}
|
|
238
|
+
}
|
package/src/sqlite-heap.ts
CHANGED
|
@@ -1,12 +1,30 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
isLeaseCurrent,
|
|
3
|
+
type ExecutionLease,
|
|
4
|
+
type HeapClient,
|
|
5
|
+
type HeapRecord,
|
|
6
|
+
type LeaseLost,
|
|
7
|
+
} from "@yieldstar/core";
|
|
8
|
+
import { EffectWaitersDao } from "./dao/effect-waiters-dao";
|
|
9
|
+
import { ExecutionsDao } from "./dao/executions-dao";
|
|
2
10
|
import { StepResponsesDao } from "./dao/step-response-dao";
|
|
11
|
+
import { StoreWaitersDao } from "./dao/store-waiters-dao";
|
|
3
12
|
import type { SqliteDriver } from "./sqlite-driver";
|
|
13
|
+
import { runTransaction } from "./sqlite-transaction";
|
|
4
14
|
|
|
5
15
|
export class SqliteHeapClient implements HeapClient {
|
|
16
|
+
private db: SqliteDriver;
|
|
17
|
+
private executionsDao: ExecutionsDao;
|
|
6
18
|
private stepResponsesDao: StepResponsesDao;
|
|
19
|
+
private storeWaitersDao: StoreWaitersDao;
|
|
20
|
+
private effectWaitersDao: EffectWaitersDao;
|
|
7
21
|
|
|
8
22
|
constructor(db: SqliteDriver) {
|
|
23
|
+
this.db = db;
|
|
24
|
+
this.executionsDao = new ExecutionsDao(db);
|
|
9
25
|
this.stepResponsesDao = new StepResponsesDao(db);
|
|
26
|
+
this.storeWaitersDao = new StoreWaitersDao(db);
|
|
27
|
+
this.effectWaitersDao = new EffectWaitersDao(db);
|
|
10
28
|
}
|
|
11
29
|
|
|
12
30
|
async getAllSteps() {
|
|
@@ -23,7 +41,7 @@ export class SqliteHeapClient implements HeapClient {
|
|
|
23
41
|
params.stepKey
|
|
24
42
|
);
|
|
25
43
|
|
|
26
|
-
if (!result
|
|
44
|
+
if (!result) {
|
|
27
45
|
return null;
|
|
28
46
|
}
|
|
29
47
|
|
|
@@ -36,19 +54,53 @@ export class SqliteHeapClient implements HeapClient {
|
|
|
36
54
|
};
|
|
37
55
|
}
|
|
38
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Checks the lease against the `executions` row and writes the step record
|
|
59
|
+
* in one IMMEDIATE transaction, so no claim can interleave between them.
|
|
60
|
+
*/
|
|
39
61
|
async writeStep(params: {
|
|
40
|
-
|
|
62
|
+
lease: ExecutionLease;
|
|
41
63
|
stepKey: string;
|
|
42
64
|
stepAttempt: number;
|
|
43
|
-
|
|
65
|
+
done: boolean;
|
|
44
66
|
stepResponseJson: string;
|
|
45
|
-
}) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
67
|
+
}): Promise<HeapRecord | LeaseLost> {
|
|
68
|
+
const { lease, stepKey, stepAttempt, done, stepResponseJson } = params;
|
|
69
|
+
const { executionId } = lease;
|
|
70
|
+
return runTransaction(this.db, () => {
|
|
71
|
+
const state = this.executionsDao.getState(executionId);
|
|
72
|
+
if (!state || !isLeaseCurrent(state, lease)) {
|
|
73
|
+
return { status: "lost" as const };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// A record never changes once written: return it as stored.
|
|
77
|
+
const existing = this.stepResponsesDao.getStepResponse(
|
|
78
|
+
executionId,
|
|
79
|
+
stepKey,
|
|
80
|
+
stepAttempt
|
|
81
|
+
);
|
|
82
|
+
if (existing) {
|
|
83
|
+
return {
|
|
84
|
+
stepResponseJson: existing.step_response,
|
|
85
|
+
meta: { attempt: existing.step_attempt, done: Boolean(existing.step_done) },
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
this.stepResponsesDao.insertStepResponse(
|
|
90
|
+
executionId,
|
|
91
|
+
stepKey,
|
|
92
|
+
stepAttempt,
|
|
93
|
+
done,
|
|
94
|
+
stepResponseJson
|
|
95
|
+
);
|
|
96
|
+
// A waiter is deleted with the write that records its step's result.
|
|
97
|
+
// Unfinished attempts (e.g. a store wait or a suspended effect) keep the
|
|
98
|
+
// waiter they registered.
|
|
99
|
+
if (done) {
|
|
100
|
+
this.storeWaitersDao.deleteStepWaiters(executionId, stepKey);
|
|
101
|
+
this.effectWaitersDao.deleteStepWaiters(executionId, stepKey);
|
|
102
|
+
}
|
|
103
|
+
return { stepResponseJson, meta: { attempt: stepAttempt, done } };
|
|
104
|
+
});
|
|
53
105
|
}
|
|
54
106
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Runtime, type RuntimeOptions } from "@yieldstar/core";
|
|
2
|
+
import { SqliteEffectClient } from "./sqlite-effect";
|
|
3
|
+
import { SqliteExecutionClient } from "./sqlite-execution";
|
|
4
|
+
import { SqliteHeapClient } from "./sqlite-heap";
|
|
5
|
+
import { SqliteStoreClient } from "./sqlite-store";
|
|
6
|
+
import type { SqliteDriver } from "./sqlite-driver";
|
|
7
|
+
|
|
8
|
+
export type SqliteRuntimeOptions = RuntimeOptions;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Execution records, step records, stores, effects and waiters in one SQLite
|
|
12
|
+
* database, so every guarantee that spans them (wakes for a store commit or an
|
|
13
|
+
* effect settlement, deleting a waiter with its step's result) holds in a
|
|
14
|
+
* single transaction.
|
|
15
|
+
*/
|
|
16
|
+
export class SqliteRuntime extends Runtime {
|
|
17
|
+
readonly clock: () => number;
|
|
18
|
+
readonly executions: SqliteExecutionClient;
|
|
19
|
+
readonly effects: SqliteEffectClient;
|
|
20
|
+
readonly heap: SqliteHeapClient;
|
|
21
|
+
readonly stores: SqliteStoreClient;
|
|
22
|
+
|
|
23
|
+
constructor(db: SqliteDriver, options: SqliteRuntimeOptions = {}) {
|
|
24
|
+
super();
|
|
25
|
+
// Read Date.now lazily so fake timers installed later still apply.
|
|
26
|
+
this.clock = options.clock ?? (() => Date.now());
|
|
27
|
+
this.executions = new SqliteExecutionClient(db, { clock: this.clock });
|
|
28
|
+
this.heap = new SqliteHeapClient(db);
|
|
29
|
+
this.stores = new SqliteStoreClient({ db, executions: this.executions });
|
|
30
|
+
this.effects = new SqliteEffectClient({
|
|
31
|
+
db,
|
|
32
|
+
executions: this.executions,
|
|
33
|
+
clock: this.clock,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
}
|