@absurd-sqlite/bun-worker 0.3.0-alpha.0 → 0.3.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +6 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3827 -91
- package/dist/sqlite.d.ts.map +1 -1
- package/package.json +3 -2
- package/src/index.ts +1 -0
- package/src/sqlite.ts +30 -10
- package/test/basic.test.ts +12 -6
- package/test/events.test.ts +13 -6
- package/test/retry.test.ts +5 -5
- package/test/setup.ts +10 -9
- package/test/sqlite.test.ts +5 -4
- package/test/step.test.ts +13 -5
package/dist/sqlite.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,KAAK,EAGV,uBAAuB,EAIxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,
|
|
1
|
+
{"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,KAAK,EAGV,uBAAuB,EAIxB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAY,MAAM,oBAAoB,CAAC;AAEhE,qBAAa,mBAAoB,SAAQ,gBAAgB;gBAC3C,EAAE,EAAE,QAAQ,EAAE,OAAO,GAAE,uBAA4B;CAIhE"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@absurd-sqlite/bun-worker",
|
|
3
|
-
"version": "0.3.0-alpha.
|
|
3
|
+
"version": "0.3.0-alpha.1",
|
|
4
4
|
"description": "Bun worker utilities for Absurd-SQLite",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
"homepage": "https://github.com/b4fun/absurd-sqlite#readme",
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@absurd-sqlite/sdk": "next",
|
|
42
|
-
"cac": "^6.7.14"
|
|
42
|
+
"cac": "^6.7.14",
|
|
43
|
+
"temporal-polyfill": "^0.3.0"
|
|
43
44
|
},
|
|
44
45
|
"devDependencies": {
|
|
45
46
|
"bun-types": "^1.3.6",
|
package/src/index.ts
CHANGED
package/src/sqlite.ts
CHANGED
|
@@ -8,7 +8,7 @@ import type {
|
|
|
8
8
|
SQLiteStatement,
|
|
9
9
|
SQLiteValueCodec,
|
|
10
10
|
} from "@absurd-sqlite/sdk";
|
|
11
|
-
import { SQLiteConnection } from "@absurd-sqlite/sdk";
|
|
11
|
+
import { SQLiteConnection, Temporal } from "@absurd-sqlite/sdk";
|
|
12
12
|
|
|
13
13
|
export class BunSqliteConnection extends SQLiteConnection {
|
|
14
14
|
constructor(db: Database, options: SQLiteConnectionOptions = {}) {
|
|
@@ -115,6 +115,19 @@ function decodeRowValues<R extends object = any>(args: {
|
|
|
115
115
|
}) => unknown;
|
|
116
116
|
}): R {
|
|
117
117
|
const decodedRow: any = {};
|
|
118
|
+
if (args.columns && args.decodeColumn) {
|
|
119
|
+
for (const column of args.columns) {
|
|
120
|
+
const columnName = column.name;
|
|
121
|
+
const rawValue = args.row[columnName];
|
|
122
|
+
decodedRow[columnName] = args.decodeColumn({
|
|
123
|
+
value: rawValue,
|
|
124
|
+
columnName,
|
|
125
|
+
columnType: column.type,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
return decodedRow as R;
|
|
129
|
+
}
|
|
130
|
+
|
|
118
131
|
for (const [columnName, rawValue] of Object.entries(args.row)) {
|
|
119
132
|
decodedRow[columnName] = decodeColumnValue({
|
|
120
133
|
value: rawValue,
|
|
@@ -132,21 +145,22 @@ function decodeColumnValue<V = any>(args: {
|
|
|
132
145
|
columnType: string | null;
|
|
133
146
|
verbose?: (...args: any[]) => void;
|
|
134
147
|
}): V | null {
|
|
135
|
-
const { value, columnName } = args;
|
|
148
|
+
const { value, columnName, columnType } = args;
|
|
136
149
|
if (value === null || value === undefined) {
|
|
137
150
|
return null;
|
|
138
151
|
}
|
|
139
152
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return new Date(value) as V;
|
|
143
|
-
}
|
|
153
|
+
const isDateTime = columnType === "datetime" || isTimestampColumn(columnName);
|
|
154
|
+
if (isDateTime) {
|
|
144
155
|
if (typeof value === "string") {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
156
|
+
return Temporal.Instant.from(value) as V;
|
|
157
|
+
}
|
|
158
|
+
if (typeof value === "number") {
|
|
159
|
+
return Temporal.Instant.fromEpochMilliseconds(value) as V;
|
|
149
160
|
}
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Expected datetime column ${columnName} to be a string or number, got ${typeof value}`
|
|
163
|
+
);
|
|
150
164
|
}
|
|
151
165
|
|
|
152
166
|
if (typeof value === "string") {
|
|
@@ -171,6 +185,12 @@ function tryDecodeJson<V = any>(value: string): V | null {
|
|
|
171
185
|
}
|
|
172
186
|
|
|
173
187
|
function encodeColumnValue(value: SQLiteBindValue): SQLiteBindValue {
|
|
188
|
+
if (value instanceof Temporal.Instant) {
|
|
189
|
+
return value.toString();
|
|
190
|
+
}
|
|
191
|
+
if (value instanceof Temporal.Duration) {
|
|
192
|
+
return value.toString();
|
|
193
|
+
}
|
|
174
194
|
if (value instanceof Date) {
|
|
175
195
|
return value.toISOString();
|
|
176
196
|
}
|
package/test/basic.test.ts
CHANGED
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
jest,
|
|
8
8
|
} from "bun:test";
|
|
9
9
|
import assert from "node:assert/strict";
|
|
10
|
-
import type
|
|
10
|
+
import { Temporal, type Absurd } from "@absurd-sqlite/sdk";
|
|
11
11
|
import { createTestAbsurd, randomName, type TestContext } from "./setup";
|
|
12
12
|
import { EventEmitter, once } from "events";
|
|
13
13
|
import { waitFor } from "./wait-for";
|
|
@@ -171,7 +171,7 @@ describe("Basic SDK Operations", () => {
|
|
|
171
171
|
const scheduledRun = await ctx.getRun(runID);
|
|
172
172
|
expect(scheduledRun).toMatchObject({
|
|
173
173
|
state: "sleeping",
|
|
174
|
-
available_at: wakeAt,
|
|
174
|
+
available_at: Temporal.Instant.fromEpochMilliseconds(wakeAt.getTime()),
|
|
175
175
|
wake_event: null,
|
|
176
176
|
});
|
|
177
177
|
|
|
@@ -189,7 +189,7 @@ describe("Basic SDK Operations", () => {
|
|
|
189
189
|
const resumedRun = await ctx.getRun(runID);
|
|
190
190
|
expect(resumedRun).toMatchObject({
|
|
191
191
|
state: "running",
|
|
192
|
-
started_at: wakeAt,
|
|
192
|
+
started_at: Temporal.Instant.fromEpochMilliseconds(wakeAt.getTime()),
|
|
193
193
|
});
|
|
194
194
|
});
|
|
195
195
|
|
|
@@ -216,7 +216,9 @@ describe("Basic SDK Operations", () => {
|
|
|
216
216
|
expect(running).toMatchObject({
|
|
217
217
|
state: "running",
|
|
218
218
|
claimed_by: "worker-a",
|
|
219
|
-
claim_expires_at:
|
|
219
|
+
claim_expires_at: Temporal.Instant.fromEpochMilliseconds(
|
|
220
|
+
baseTime.getTime() + 30 * 1000,
|
|
221
|
+
),
|
|
220
222
|
});
|
|
221
223
|
|
|
222
224
|
await ctx.setFakeNow(new Date(baseTime.getTime() + 5 * 60 * 1000));
|
|
@@ -275,7 +277,9 @@ describe("Basic SDK Operations", () => {
|
|
|
275
277
|
const runRow = await ctx.getRun(runID);
|
|
276
278
|
expect(runRow).toMatchObject({
|
|
277
279
|
claimed_by: "worker-clean",
|
|
278
|
-
claim_expires_at:
|
|
280
|
+
claim_expires_at: Temporal.Instant.fromEpochMilliseconds(
|
|
281
|
+
base.getTime() + 60 * 1000,
|
|
282
|
+
),
|
|
279
283
|
});
|
|
280
284
|
|
|
281
285
|
const beforeTTL = new Date(finishTime.getTime() + 30 * 60 * 1000);
|
|
@@ -482,7 +486,9 @@ describe("Basic SDK Operations", () => {
|
|
|
482
486
|
|
|
483
487
|
const getExpiresAt = async (runID: string) => {
|
|
484
488
|
const run = await ctx.getRun(runID);
|
|
485
|
-
return run?.claim_expires_at
|
|
489
|
+
return run?.claim_expires_at
|
|
490
|
+
? run.claim_expires_at.epochMilliseconds
|
|
491
|
+
: 0;
|
|
486
492
|
};
|
|
487
493
|
|
|
488
494
|
absurd.workBatch("test-worker", claimTimeout);
|
package/test/events.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, test, expect, beforeAll, afterEach } from "bun:test";
|
|
2
|
-
import type
|
|
2
|
+
import { Temporal, type Absurd } from "@absurd-sqlite/sdk";
|
|
3
3
|
import { createTestAbsurd, randomName, type TestContext } from "./setup";
|
|
4
4
|
import { TimeoutError } from "@absurd-sqlite/sdk";
|
|
5
5
|
|
|
@@ -21,7 +21,9 @@ describe("Event system", () => {
|
|
|
21
21
|
const eventName = randomName("test_event");
|
|
22
22
|
|
|
23
23
|
absurd.registerTask({ name: "waiter" }, async (params, ctx) => {
|
|
24
|
-
const payload = await ctx.awaitEvent(eventName, {
|
|
24
|
+
const payload = await ctx.awaitEvent(eventName, {
|
|
25
|
+
timeout: Temporal.Duration.from({ seconds: 60 }),
|
|
26
|
+
});
|
|
25
27
|
return { received: payload };
|
|
26
28
|
});
|
|
27
29
|
|
|
@@ -86,7 +88,7 @@ describe("Event system", () => {
|
|
|
86
88
|
absurd.registerTask({ name: "timeout-waiter" }, async (_params, ctx) => {
|
|
87
89
|
try {
|
|
88
90
|
const payload = await ctx.awaitEvent(eventName, {
|
|
89
|
-
timeout: timeoutSeconds,
|
|
91
|
+
timeout: Temporal.Duration.from({ seconds: timeoutSeconds }),
|
|
90
92
|
});
|
|
91
93
|
return { timedOut: false, result: payload };
|
|
92
94
|
} catch (err) {
|
|
@@ -109,7 +111,9 @@ describe("Event system", () => {
|
|
|
109
111
|
wake_event: eventName,
|
|
110
112
|
});
|
|
111
113
|
const expectedWake = new Date(baseTime.getTime() + timeoutSeconds * 1000);
|
|
112
|
-
expect(sleepingRun?.available_at?.
|
|
114
|
+
expect(sleepingRun?.available_at?.epochMilliseconds).toBe(
|
|
115
|
+
expectedWake.getTime(),
|
|
116
|
+
);
|
|
113
117
|
|
|
114
118
|
await ctx.setFakeNow(new Date(expectedWake.getTime() + 1000));
|
|
115
119
|
await absurd.workBatch("worker1", 120, 1);
|
|
@@ -170,13 +174,16 @@ describe("Event system", () => {
|
|
|
170
174
|
|
|
171
175
|
absurd.registerTask({ name: "timeout-no-loop" }, async (_params, ctx) => {
|
|
172
176
|
try {
|
|
173
|
-
await ctx.awaitEvent(eventName, {
|
|
177
|
+
await ctx.awaitEvent(eventName, {
|
|
178
|
+
stepName: "wait",
|
|
179
|
+
timeout: Temporal.Duration.from({ seconds: 10 }),
|
|
180
|
+
});
|
|
174
181
|
return { stage: "unexpected" };
|
|
175
182
|
} catch (err) {
|
|
176
183
|
if (err instanceof TimeoutError) {
|
|
177
184
|
const payload = await ctx.awaitEvent(eventName, {
|
|
178
185
|
stepName: "wait",
|
|
179
|
-
timeout: 10,
|
|
186
|
+
timeout: Temporal.Duration.from({ seconds: 10 }),
|
|
180
187
|
});
|
|
181
188
|
return { stage: "resumed", payload };
|
|
182
189
|
}
|
package/test/retry.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, test, expect, beforeAll, afterEach } from "bun:test";
|
|
2
|
-
import type
|
|
2
|
+
import { Temporal, type Absurd } from "@absurd-sqlite/sdk";
|
|
3
3
|
import { createTestAbsurd, randomName, type TestContext } from "./setup";
|
|
4
4
|
|
|
5
5
|
describe("Retry and cancellation", () => {
|
|
@@ -159,7 +159,7 @@ describe("Retry and cancellation", () => {
|
|
|
159
159
|
const { taskID } = await absurd.spawn("duration-cancel", undefined, {
|
|
160
160
|
maxAttempts: 4,
|
|
161
161
|
retryStrategy: { kind: "fixed", baseSeconds: 30 },
|
|
162
|
-
cancellation: { maxDuration: 90 },
|
|
162
|
+
cancellation: { maxDuration: Temporal.Duration.from({ seconds: 90 }) },
|
|
163
163
|
});
|
|
164
164
|
|
|
165
165
|
await absurd.workBatch("worker1", 60, 1);
|
|
@@ -185,7 +185,7 @@ describe("Retry and cancellation", () => {
|
|
|
185
185
|
});
|
|
186
186
|
|
|
187
187
|
const { taskID } = await absurd.spawn("delay-cancel", undefined, {
|
|
188
|
-
cancellation: { maxDelay: 60 },
|
|
188
|
+
cancellation: { maxDelay: Temporal.Duration.from({ seconds: 60 }) },
|
|
189
189
|
});
|
|
190
190
|
|
|
191
191
|
await ctx.setFakeNow(new Date(baseTime.getTime() + 61 * 1000));
|
|
@@ -312,8 +312,8 @@ describe("Retry and cancellation", () => {
|
|
|
312
312
|
|
|
313
313
|
await absurd.cancelTask(taskID);
|
|
314
314
|
const second = await ctx.getTask(taskID);
|
|
315
|
-
expect(second?.cancelled_at?.
|
|
316
|
-
first?.cancelled_at?.
|
|
315
|
+
expect(second?.cancelled_at?.epochMilliseconds).toBe(
|
|
316
|
+
first?.cancelled_at?.epochMilliseconds,
|
|
317
317
|
);
|
|
318
318
|
});
|
|
319
319
|
|
package/test/setup.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { join } from "node:path";
|
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import {
|
|
8
8
|
Absurd,
|
|
9
|
+
Temporal,
|
|
9
10
|
type AbsurdHooks,
|
|
10
11
|
type JsonValue,
|
|
11
12
|
} from "@absurd-sqlite/sdk";
|
|
@@ -23,8 +24,8 @@ export interface TaskRow {
|
|
|
23
24
|
retry_strategy: JsonValue | null;
|
|
24
25
|
max_attempts: number | null;
|
|
25
26
|
cancellation: JsonValue | null;
|
|
26
|
-
enqueue_at:
|
|
27
|
-
first_started_at:
|
|
27
|
+
enqueue_at: Temporal.Instant;
|
|
28
|
+
first_started_at: Temporal.Instant | null;
|
|
28
29
|
state:
|
|
29
30
|
| "pending"
|
|
30
31
|
| "running"
|
|
@@ -35,7 +36,7 @@ export interface TaskRow {
|
|
|
35
36
|
attempts: number;
|
|
36
37
|
last_attempt_run: string | null;
|
|
37
38
|
completed_payload: JsonValue | null;
|
|
38
|
-
cancelled_at:
|
|
39
|
+
cancelled_at: Temporal.Instant | null;
|
|
39
40
|
}
|
|
40
41
|
|
|
41
42
|
export interface RunRow {
|
|
@@ -50,16 +51,16 @@ export interface RunRow {
|
|
|
50
51
|
| "failed"
|
|
51
52
|
| "cancelled";
|
|
52
53
|
claimed_by: string | null;
|
|
53
|
-
claim_expires_at:
|
|
54
|
-
available_at:
|
|
54
|
+
claim_expires_at: Temporal.Instant | null;
|
|
55
|
+
available_at: Temporal.Instant;
|
|
55
56
|
wake_event: string | null;
|
|
56
57
|
event_payload: JsonValue | null;
|
|
57
|
-
started_at:
|
|
58
|
-
completed_at:
|
|
59
|
-
failed_at:
|
|
58
|
+
started_at: Temporal.Instant | null;
|
|
59
|
+
completed_at: Temporal.Instant | null;
|
|
60
|
+
failed_at: Temporal.Instant | null;
|
|
60
61
|
result: JsonValue | null;
|
|
61
62
|
failure_reason: JsonValue | null;
|
|
62
|
-
created_at:
|
|
63
|
+
created_at: Temporal.Instant;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
interface SqliteFixture {
|
package/test/sqlite.test.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { join } from "node:path";
|
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
|
|
7
7
|
import { BunSqliteConnection } from "../src/sqlite";
|
|
8
|
+
import { Temporal } from "@absurd-sqlite/sdk";
|
|
8
9
|
|
|
9
10
|
describe("BunSqliteConnection", () => {
|
|
10
11
|
it("rewrites postgres-style params and absurd schema names", async () => {
|
|
@@ -75,7 +76,7 @@ describe("BunSqliteConnection", () => {
|
|
|
75
76
|
db.close();
|
|
76
77
|
});
|
|
77
78
|
|
|
78
|
-
it("decodes datetime columns into
|
|
79
|
+
it("decodes datetime columns into Temporal.Instant objects", async () => {
|
|
79
80
|
const db = new Database(":memory:");
|
|
80
81
|
const conn = new BunSqliteConnection(db);
|
|
81
82
|
const now = Date.now();
|
|
@@ -83,12 +84,12 @@ describe("BunSqliteConnection", () => {
|
|
|
83
84
|
await conn.exec("CREATE TABLE t_date (created_at DATETIME)");
|
|
84
85
|
await conn.exec("INSERT INTO t_date (created_at) VALUES ($1)", [now]);
|
|
85
86
|
|
|
86
|
-
const { rows } = await conn.query<{ created_at:
|
|
87
|
+
const { rows } = await conn.query<{ created_at: Temporal.Instant }>(
|
|
87
88
|
"SELECT created_at FROM t_date"
|
|
88
89
|
);
|
|
89
90
|
|
|
90
|
-
expect(rows[0]?.created_at).toBeInstanceOf(
|
|
91
|
-
expect(rows[0]?.created_at.
|
|
91
|
+
expect(rows[0]?.created_at).toBeInstanceOf(Temporal.Instant);
|
|
92
|
+
expect(rows[0]?.created_at.epochMilliseconds).toBe(now);
|
|
92
93
|
db.close();
|
|
93
94
|
});
|
|
94
95
|
|
package/test/step.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, test, expect, beforeAll, afterEach, jest } from "bun:test";
|
|
2
|
-
import type
|
|
2
|
+
import { Temporal, type Absurd } from "@absurd-sqlite/sdk";
|
|
3
3
|
import { createTestAbsurd, randomName, type TestContext } from "./setup";
|
|
4
4
|
|
|
5
5
|
describe("Step functionality", () => {
|
|
@@ -193,7 +193,10 @@ describe("Step functionality", () => {
|
|
|
193
193
|
|
|
194
194
|
const durationSeconds = 60;
|
|
195
195
|
absurd.registerTask({ name: "sleep-for" }, async (_params, ctx) => {
|
|
196
|
-
await ctx.sleepFor(
|
|
196
|
+
await ctx.sleepFor(
|
|
197
|
+
"wait-for",
|
|
198
|
+
Temporal.Duration.from({ seconds: durationSeconds }),
|
|
199
|
+
);
|
|
197
200
|
return { resumed: true };
|
|
198
201
|
});
|
|
199
202
|
|
|
@@ -205,7 +208,9 @@ describe("Step functionality", () => {
|
|
|
205
208
|
state: "sleeping",
|
|
206
209
|
});
|
|
207
210
|
const wakeTime = new Date(base.getTime() + durationSeconds * 1000);
|
|
208
|
-
expect(sleepingRun?.available_at?.
|
|
211
|
+
expect(sleepingRun?.available_at?.epochMilliseconds).toBe(
|
|
212
|
+
wakeTime.getTime(),
|
|
213
|
+
);
|
|
209
214
|
|
|
210
215
|
const resumeTime = new Date(wakeTime.getTime() + 5 * 1000);
|
|
211
216
|
jest.setSystemTime(resumeTime);
|
|
@@ -225,11 +230,14 @@ describe("Step functionality", () => {
|
|
|
225
230
|
await ctx.setFakeNow(base);
|
|
226
231
|
|
|
227
232
|
const wakeTime = new Date(base.getTime() + 5 * 60 * 1000);
|
|
233
|
+
const wakeInstant = Temporal.Instant.fromEpochMilliseconds(
|
|
234
|
+
wakeTime.getTime(),
|
|
235
|
+
);
|
|
228
236
|
let executions = 0;
|
|
229
237
|
|
|
230
238
|
absurd.registerTask({ name: "sleep-until" }, async (_params, ctx) => {
|
|
231
239
|
executions++;
|
|
232
|
-
await ctx.sleepUntil("sleep-step",
|
|
240
|
+
await ctx.sleepUntil("sleep-step", wakeInstant);
|
|
233
241
|
return { executions };
|
|
234
242
|
});
|
|
235
243
|
|
|
@@ -240,7 +248,7 @@ describe("Step functionality", () => {
|
|
|
240
248
|
expect(checkpointRow).toMatchObject({
|
|
241
249
|
checkpoint_name: "sleep-step",
|
|
242
250
|
owner_run_id: runID,
|
|
243
|
-
state:
|
|
251
|
+
state: wakeInstant.toString(),
|
|
244
252
|
});
|
|
245
253
|
|
|
246
254
|
const sleepingRun = await ctx.getRun(runID);
|