Skip to content

Commit 9e8d4a6

Browse files
authored
sdks/bun-worker: use Temporal api (#31)
* chore: bump api in typescript-client * feat: replace datetime with temporal * chore: update sample to use Temporal api * sdks/bun-worker: 0.3.0-alpha.1
1 parent 2c8a080 commit 9e8d4a6

13 files changed

Lines changed: 106 additions & 53 deletions

File tree

samples/bun-worker/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import run from "@absurd-sqlite/bun-worker";
1+
import run, { Temporal } from "@absurd-sqlite/bun-worker";
22
import { Database } from "bun:sqlite";
33
import { existsSync, readdirSync } from "node:fs";
44
import { join } from "node:path";
@@ -19,7 +19,7 @@ await run(async (absurd) => {
1919
return {};
2020
});
2121

22-
await ctx.sleepFor("back off 15s", 15);
22+
await ctx.sleepFor("back off 15s", Temporal.Duration.from({ seconds: 15 }));
2323

2424
await ctx.step("process", async () => {
2525
console.log("process step");

samples/typescript-client/package-lock.json

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

samples/typescript-client/src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Absurd, SQLiteConnection, SQLiteDatabase } from "@absurd-sqlite/sdk";
1+
import { Absurd, SQLiteConnection, SQLiteDatabase, Temporal } from "@absurd-sqlite/sdk";
22
import sqlite from "better-sqlite3";
33

44
async function main() {
@@ -27,7 +27,7 @@ async function main() {
2727
return {};
2828
});
2929

30-
await ctx.sleepFor("back off 15s", 15);
30+
await ctx.sleepFor("back off 15s", Temporal.Duration.from({ seconds: 15 }));
3131

3232
await ctx.step("process", async () => {
3333
console.log("process step");

sdks/bun-worker/bun.lock

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

sdks/bun-worker/package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@absurd-sqlite/bun-worker",
3-
"version": "0.3.0-alpha.0",
3+
"version": "0.3.0-alpha.1",
44
"description": "Bun worker utilities for Absurd-SQLite",
55
"type": "module",
66
"main": "dist/index.js",
@@ -39,7 +39,8 @@
3939
"homepage": "https://github.com/b4fun/absurd-sqlite#readme",
4040
"dependencies": {
4141
"@absurd-sqlite/sdk": "next",
42-
"cac": "^6.7.14"
42+
"cac": "^6.7.14",
43+
"temporal-polyfill": "^0.3.0"
4344
},
4445
"devDependencies": {
4546
"bun-types": "^1.3.6",

sdks/bun-worker/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export type { WorkerOptions } from "@absurd-sqlite/sdk";
1414
export {
1515
downloadExtension,
1616
type DownloadExtensionOptions,
17+
Temporal,
1718
} from "@absurd-sqlite/sdk";
1819

1920
/**

sdks/bun-worker/src/sqlite.ts

Lines changed: 30 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type {
88
SQLiteStatement,
99
SQLiteValueCodec,
1010
} from "@absurd-sqlite/sdk";
11-
import { SQLiteConnection } from "@absurd-sqlite/sdk";
11+
import { SQLiteConnection, Temporal } from "@absurd-sqlite/sdk";
1212

1313
export class BunSqliteConnection extends SQLiteConnection {
1414
constructor(db: Database, options: SQLiteConnectionOptions = {}) {
@@ -115,6 +115,19 @@ function decodeRowValues<R extends object = any>(args: {
115115
}) => unknown;
116116
}): R {
117117
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+
118131
for (const [columnName, rawValue] of Object.entries(args.row)) {
119132
decodedRow[columnName] = decodeColumnValue({
120133
value: rawValue,
@@ -132,21 +145,22 @@ function decodeColumnValue<V = any>(args: {
132145
columnType: string | null;
133146
verbose?: (...args: any[]) => void;
134147
}): V | null {
135-
const { value, columnName } = args;
148+
const { value, columnName, columnType } = args;
136149
if (value === null || value === undefined) {
137150
return null;
138151
}
139152

140-
if (isTimestampColumn(columnName)) {
141-
if (typeof value === "number") {
142-
return new Date(value) as V;
143-
}
153+
const isDateTime = columnType === "datetime" || isTimestampColumn(columnName);
154+
if (isDateTime) {
144155
if (typeof value === "string") {
145-
const parsed = Date.parse(value);
146-
if (!Number.isNaN(parsed)) {
147-
return new Date(parsed) as V;
148-
}
156+
return Temporal.Instant.from(value) as V;
157+
}
158+
if (typeof value === "number") {
159+
return Temporal.Instant.fromEpochMilliseconds(value) as V;
149160
}
161+
throw new Error(
162+
`Expected datetime column ${columnName} to be a string or number, got ${typeof value}`
163+
);
150164
}
151165

152166
if (typeof value === "string") {
@@ -171,6 +185,12 @@ function tryDecodeJson<V = any>(value: string): V | null {
171185
}
172186

173187
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+
}
174194
if (value instanceof Date) {
175195
return value.toISOString();
176196
}

sdks/bun-worker/test/basic.test.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
jest,
88
} from "bun:test";
99
import assert from "node:assert/strict";
10-
import type { Absurd } from "@absurd-sqlite/sdk";
10+
import { Temporal, type Absurd } from "@absurd-sqlite/sdk";
1111
import { createTestAbsurd, randomName, type TestContext } from "./setup";
1212
import { EventEmitter, once } from "events";
1313
import { waitFor } from "./wait-for";
@@ -171,7 +171,7 @@ describe("Basic SDK Operations", () => {
171171
const scheduledRun = await ctx.getRun(runID);
172172
expect(scheduledRun).toMatchObject({
173173
state: "sleeping",
174-
available_at: wakeAt,
174+
available_at: Temporal.Instant.fromEpochMilliseconds(wakeAt.getTime()),
175175
wake_event: null,
176176
});
177177

@@ -189,7 +189,7 @@ describe("Basic SDK Operations", () => {
189189
const resumedRun = await ctx.getRun(runID);
190190
expect(resumedRun).toMatchObject({
191191
state: "running",
192-
started_at: wakeAt,
192+
started_at: Temporal.Instant.fromEpochMilliseconds(wakeAt.getTime()),
193193
});
194194
});
195195

@@ -216,7 +216,9 @@ describe("Basic SDK Operations", () => {
216216
expect(running).toMatchObject({
217217
state: "running",
218218
claimed_by: "worker-a",
219-
claim_expires_at: new Date(baseTime.getTime() + 30 * 1000),
219+
claim_expires_at: Temporal.Instant.fromEpochMilliseconds(
220+
baseTime.getTime() + 30 * 1000,
221+
),
220222
});
221223

222224
await ctx.setFakeNow(new Date(baseTime.getTime() + 5 * 60 * 1000));
@@ -275,7 +277,9 @@ describe("Basic SDK Operations", () => {
275277
const runRow = await ctx.getRun(runID);
276278
expect(runRow).toMatchObject({
277279
claimed_by: "worker-clean",
278-
claim_expires_at: new Date(base.getTime() + 60 * 1000),
280+
claim_expires_at: Temporal.Instant.fromEpochMilliseconds(
281+
base.getTime() + 60 * 1000,
282+
),
279283
});
280284

281285
const beforeTTL = new Date(finishTime.getTime() + 30 * 60 * 1000);
@@ -482,7 +486,9 @@ describe("Basic SDK Operations", () => {
482486

483487
const getExpiresAt = async (runID: string) => {
484488
const run = await ctx.getRun(runID);
485-
return run?.claim_expires_at ? run.claim_expires_at.getTime() : 0;
489+
return run?.claim_expires_at
490+
? run.claim_expires_at.epochMilliseconds
491+
: 0;
486492
};
487493

488494
absurd.workBatch("test-worker", claimTimeout);

sdks/bun-worker/test/events.test.ts

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, test, expect, beforeAll, afterEach } from "bun:test";
2-
import type { Absurd } from "@absurd-sqlite/sdk";
2+
import { Temporal, type Absurd } from "@absurd-sqlite/sdk";
33
import { createTestAbsurd, randomName, type TestContext } from "./setup";
44
import { TimeoutError } from "@absurd-sqlite/sdk";
55

@@ -21,7 +21,9 @@ describe("Event system", () => {
2121
const eventName = randomName("test_event");
2222

2323
absurd.registerTask({ name: "waiter" }, async (params, ctx) => {
24-
const payload = await ctx.awaitEvent(eventName, { timeout: 60 });
24+
const payload = await ctx.awaitEvent(eventName, {
25+
timeout: Temporal.Duration.from({ seconds: 60 }),
26+
});
2527
return { received: payload };
2628
});
2729

@@ -86,7 +88,7 @@ describe("Event system", () => {
8688
absurd.registerTask({ name: "timeout-waiter" }, async (_params, ctx) => {
8789
try {
8890
const payload = await ctx.awaitEvent(eventName, {
89-
timeout: timeoutSeconds,
91+
timeout: Temporal.Duration.from({ seconds: timeoutSeconds }),
9092
});
9193
return { timedOut: false, result: payload };
9294
} catch (err) {
@@ -109,7 +111,9 @@ describe("Event system", () => {
109111
wake_event: eventName,
110112
});
111113
const expectedWake = new Date(baseTime.getTime() + timeoutSeconds * 1000);
112-
expect(sleepingRun?.available_at?.getTime()).toBe(expectedWake.getTime());
114+
expect(sleepingRun?.available_at?.epochMilliseconds).toBe(
115+
expectedWake.getTime(),
116+
);
113117

114118
await ctx.setFakeNow(new Date(expectedWake.getTime() + 1000));
115119
await absurd.workBatch("worker1", 120, 1);
@@ -170,13 +174,16 @@ describe("Event system", () => {
170174

171175
absurd.registerTask({ name: "timeout-no-loop" }, async (_params, ctx) => {
172176
try {
173-
await ctx.awaitEvent(eventName, { stepName: "wait", timeout: 10 });
177+
await ctx.awaitEvent(eventName, {
178+
stepName: "wait",
179+
timeout: Temporal.Duration.from({ seconds: 10 }),
180+
});
174181
return { stage: "unexpected" };
175182
} catch (err) {
176183
if (err instanceof TimeoutError) {
177184
const payload = await ctx.awaitEvent(eventName, {
178185
stepName: "wait",
179-
timeout: 10,
186+
timeout: Temporal.Duration.from({ seconds: 10 }),
180187
});
181188
return { stage: "resumed", payload };
182189
}

sdks/bun-worker/test/retry.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, test, expect, beforeAll, afterEach } from "bun:test";
2-
import type { Absurd } from "@absurd-sqlite/sdk";
2+
import { Temporal, type Absurd } from "@absurd-sqlite/sdk";
33
import { createTestAbsurd, randomName, type TestContext } from "./setup";
44

55
describe("Retry and cancellation", () => {
@@ -159,7 +159,7 @@ describe("Retry and cancellation", () => {
159159
const { taskID } = await absurd.spawn("duration-cancel", undefined, {
160160
maxAttempts: 4,
161161
retryStrategy: { kind: "fixed", baseSeconds: 30 },
162-
cancellation: { maxDuration: 90 },
162+
cancellation: { maxDuration: Temporal.Duration.from({ seconds: 90 }) },
163163
});
164164

165165
await absurd.workBatch("worker1", 60, 1);
@@ -185,7 +185,7 @@ describe("Retry and cancellation", () => {
185185
});
186186

187187
const { taskID } = await absurd.spawn("delay-cancel", undefined, {
188-
cancellation: { maxDelay: 60 },
188+
cancellation: { maxDelay: Temporal.Duration.from({ seconds: 60 }) },
189189
});
190190

191191
await ctx.setFakeNow(new Date(baseTime.getTime() + 61 * 1000));
@@ -312,8 +312,8 @@ describe("Retry and cancellation", () => {
312312

313313
await absurd.cancelTask(taskID);
314314
const second = await ctx.getTask(taskID);
315-
expect(second?.cancelled_at?.getTime()).toBe(
316-
first?.cancelled_at?.getTime(),
315+
expect(second?.cancelled_at?.epochMilliseconds).toBe(
316+
first?.cancelled_at?.epochMilliseconds,
317317
);
318318
});
319319

0 commit comments

Comments
 (0)