@absurd-sqlite/bun-worker 0.2.2-alpha.2 → 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 +8 -34
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4097 -4943
- package/dist/sqlite.d.ts +4 -12
- package/dist/sqlite.d.ts.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +9 -7
- package/src/sqlite.ts +150 -142
- package/test/basic.test.ts +12 -6
- package/test/events.test.ts +14 -7
- package/test/hooks.test.ts +1 -1
- package/test/idempotent.test.ts +1 -1
- package/test/index.test.ts +3 -3
- package/test/retry.test.ts +5 -5
- package/test/setup.ts +13 -16
- package/test/sqlite.test.ts +13 -10
- package/test/step.test.ts +13 -5
- package/test/worker.test.ts +1 -1
package/dist/sqlite.d.ts
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
|
-
import type {
|
|
3
|
-
import
|
|
4
|
-
export declare class BunSqliteConnection
|
|
5
|
-
|
|
6
|
-
private readonly maxRetries;
|
|
7
|
-
private readonly baseRetryDelayMs;
|
|
8
|
-
constructor(db: Database);
|
|
9
|
-
query<R extends object = Record<string, any>>(sql: string, params?: SQLiteRestBindParams): Promise<{
|
|
10
|
-
rows: R[];
|
|
11
|
-
}>;
|
|
12
|
-
exec(sql: string, params?: SQLiteRestBindParams): Promise<void>;
|
|
13
|
-
private runWithRetry;
|
|
2
|
+
import type { SQLiteConnectionOptions } from "@absurd-sqlite/sdk";
|
|
3
|
+
import { SQLiteConnection } from "@absurd-sqlite/sdk";
|
|
4
|
+
export declare class BunSqliteConnection extends SQLiteConnection {
|
|
5
|
+
constructor(db: Database, options?: SQLiteConnectionOptions);
|
|
14
6
|
}
|
|
15
7
|
//# sourceMappingURL=sqlite.d.ts.map
|
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,
|
|
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
|
+
"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,14 +39,14 @@
|
|
|
39
39
|
"homepage": "https://github.com/b4fun/absurd-sqlite#readme",
|
|
40
40
|
"dependencies": {
|
|
41
41
|
"@absurd-sqlite/sdk": "next",
|
|
42
|
-
"
|
|
43
|
-
"
|
|
42
|
+
"cac": "^6.7.14",
|
|
43
|
+
"temporal-polyfill": "^0.3.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"bun-types": "^1.3.
|
|
46
|
+
"bun-types": "^1.3.6",
|
|
47
47
|
"typescript": "^5.9.3"
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|
|
50
50
|
"bun": ">=1.1.0"
|
|
51
51
|
}
|
|
52
|
-
}
|
|
52
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import { Absurd, type WorkerOptions } from "absurd-sdk";
|
|
2
1
|
import { Database } from "bun:sqlite";
|
|
3
|
-
import
|
|
2
|
+
import {
|
|
3
|
+
Absurd,
|
|
4
|
+
type AbsurdClient,
|
|
5
|
+
type WorkerOptions,
|
|
6
|
+
} from "@absurd-sqlite/sdk";
|
|
4
7
|
import { cac } from "cac";
|
|
5
8
|
|
|
6
9
|
import { BunSqliteConnection } from "./sqlite";
|
|
7
10
|
|
|
8
11
|
export type { AbsurdClient } from "@absurd-sqlite/sdk";
|
|
9
|
-
export type { WorkerOptions } from "absurd-sdk";
|
|
12
|
+
export type { WorkerOptions } from "@absurd-sqlite/sdk";
|
|
10
13
|
|
|
11
14
|
export {
|
|
12
15
|
downloadExtension,
|
|
13
16
|
type DownloadExtensionOptions,
|
|
17
|
+
Temporal,
|
|
14
18
|
} from "@absurd-sqlite/sdk";
|
|
15
19
|
|
|
16
20
|
/**
|
|
@@ -101,12 +105,10 @@ export default async function run(setupFunction: SetupFunction): Promise<void> {
|
|
|
101
105
|
}
|
|
102
106
|
|
|
103
107
|
const db = new Database(dbPath);
|
|
104
|
-
|
|
105
|
-
extensionPath
|
|
106
|
-
);
|
|
108
|
+
db.loadExtension(extensionPath);
|
|
107
109
|
|
|
108
110
|
const conn = new BunSqliteConnection(db);
|
|
109
|
-
const absurd = new Absurd(
|
|
111
|
+
const absurd = new Absurd(conn);
|
|
110
112
|
|
|
111
113
|
await setupFunction(absurd);
|
|
112
114
|
|
package/src/sqlite.ts
CHANGED
|
@@ -1,126 +1,166 @@
|
|
|
1
1
|
import { Database } from "bun:sqlite";
|
|
2
2
|
|
|
3
|
-
import type { Queryable } from "@absurd-sqlite/sdk";
|
|
4
3
|
import type {
|
|
5
|
-
SQLiteBindParams,
|
|
6
4
|
SQLiteBindValue,
|
|
7
|
-
|
|
5
|
+
SQLiteColumnDefinition,
|
|
6
|
+
SQLiteConnectionOptions,
|
|
7
|
+
SQLiteDatabase,
|
|
8
|
+
SQLiteStatement,
|
|
9
|
+
SQLiteValueCodec,
|
|
8
10
|
} from "@absurd-sqlite/sdk";
|
|
11
|
+
import { SQLiteConnection, Temporal } from "@absurd-sqlite/sdk";
|
|
9
12
|
|
|
10
|
-
export class BunSqliteConnection
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
export class BunSqliteConnection extends SQLiteConnection {
|
|
14
|
+
constructor(db: Database, options: SQLiteConnectionOptions = {}) {
|
|
15
|
+
const valueCodec = buildValueCodec(options.valueCodec);
|
|
16
|
+
super(new BunSqliteDatabase(db), { ...options, valueCodec });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class BunSqliteDatabase implements SQLiteDatabase {
|
|
21
|
+
constructor(private readonly db: Database) {}
|
|
14
22
|
|
|
15
|
-
|
|
16
|
-
|
|
23
|
+
prepare<Result extends object = Record<string, any>>(
|
|
24
|
+
sql: string
|
|
25
|
+
): SQLiteStatement<Result> {
|
|
26
|
+
const statement = this.db.prepare(sql);
|
|
27
|
+
return new BunSqliteStatement(statement, isReadonlyQuery(sql));
|
|
17
28
|
}
|
|
18
29
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
): Promise<{ rows: R[] }> {
|
|
23
|
-
const { sql: sqliteQuery, paramOrder } = rewritePostgresQuery(sql);
|
|
24
|
-
const sqliteParams = rewritePostgresParams(
|
|
25
|
-
normalizeParams(params),
|
|
26
|
-
paramOrder
|
|
27
|
-
);
|
|
30
|
+
close(): void {
|
|
31
|
+
this.db.close();
|
|
32
|
+
}
|
|
28
33
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
decodeRowValues(row as Record<string, unknown>)
|
|
33
|
-
)
|
|
34
|
+
loadExtension(path: string): void {
|
|
35
|
+
(this.db as unknown as { loadExtension(path: string): void }).loadExtension(
|
|
36
|
+
path
|
|
34
37
|
);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class BunSqliteStatement<Result extends object = Record<string, any>>
|
|
42
|
+
implements SQLiteStatement<Result>
|
|
43
|
+
{
|
|
44
|
+
readonly readonly: boolean;
|
|
35
45
|
|
|
36
|
-
|
|
46
|
+
constructor(
|
|
47
|
+
private readonly stmt: ReturnType<Database["prepare"]>,
|
|
48
|
+
readonlyFlag: boolean
|
|
49
|
+
) {
|
|
50
|
+
this.readonly = readonlyFlag;
|
|
37
51
|
}
|
|
38
52
|
|
|
39
|
-
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
53
|
+
columns(): SQLiteColumnDefinition[] {
|
|
54
|
+
const columnNames = this.stmt.columnNames ?? [];
|
|
55
|
+
const declaredTypes = this.stmt.declaredTypes ?? [];
|
|
56
|
+
return columnNames.map((name, index) => ({
|
|
57
|
+
name,
|
|
58
|
+
column: null,
|
|
59
|
+
table: null,
|
|
60
|
+
database: null,
|
|
61
|
+
type: normalizeColumnType(declaredTypes[index] ?? null),
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
45
64
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return operation();
|
|
55
|
-
} catch (err) {
|
|
56
|
-
if (!isRetryableSQLiteError(err) || attempt >= this.maxRetries) {
|
|
57
|
-
throw err;
|
|
58
|
-
}
|
|
59
|
-
attempt++;
|
|
60
|
-
await delay(this.baseRetryDelayMs * attempt);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
65
|
+
all(...args: any[]): Result[] {
|
|
66
|
+
const normalizedArgs = normalizeStatementArgs(args);
|
|
67
|
+
return this.stmt.all(...normalizedArgs) as Result[];
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
run(...args: any[]): unknown {
|
|
71
|
+
const normalizedArgs = normalizeStatementArgs(args);
|
|
72
|
+
return this.stmt.run(...normalizedArgs);
|
|
63
73
|
}
|
|
64
74
|
}
|
|
65
75
|
|
|
66
|
-
function
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
return "?";
|
|
75
|
-
})
|
|
76
|
-
.replace(/absurd\.(\w+)/g, "absurd_$1");
|
|
77
|
-
|
|
78
|
-
return { sql, paramOrder };
|
|
76
|
+
function buildValueCodec(
|
|
77
|
+
overrides?: SQLiteValueCodec
|
|
78
|
+
): SQLiteValueCodec {
|
|
79
|
+
return {
|
|
80
|
+
encodeParam: overrides?.encodeParam ?? encodeColumnValue,
|
|
81
|
+
decodeColumn: overrides?.decodeColumn ?? decodeColumnValue,
|
|
82
|
+
decodeRow: overrides?.decodeRow ?? decodeRowValues,
|
|
83
|
+
};
|
|
79
84
|
}
|
|
80
85
|
|
|
81
|
-
function
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
function normalizeStatementArgs(args: any[]): any[] {
|
|
87
|
+
if (args.length !== 1) {
|
|
88
|
+
return args;
|
|
89
|
+
}
|
|
90
|
+
const params = args[0];
|
|
91
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
92
|
+
return args;
|
|
93
|
+
}
|
|
94
|
+
const normalized: Record<string, unknown> = {};
|
|
95
|
+
for (const [key, value] of Object.entries(params)) {
|
|
96
|
+
normalized[normalizeParamKey(key)] = value;
|
|
87
97
|
}
|
|
98
|
+
return [normalized];
|
|
99
|
+
}
|
|
88
100
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return
|
|
92
|
-
}
|
|
101
|
+
function normalizeParamKey(key: string): string {
|
|
102
|
+
if (key.startsWith("$") || key.startsWith(":") || key.startsWith("@")) {
|
|
103
|
+
return key;
|
|
104
|
+
}
|
|
105
|
+
return `:${key}`;
|
|
93
106
|
}
|
|
94
107
|
|
|
95
|
-
function decodeRowValues<R extends object = any>(
|
|
96
|
-
row: Record<string, unknown
|
|
97
|
-
|
|
108
|
+
function decodeRowValues<R extends object = any>(args: {
|
|
109
|
+
row: Record<string, unknown>;
|
|
110
|
+
columns?: SQLiteColumnDefinition[];
|
|
111
|
+
decodeColumn?: (args: {
|
|
112
|
+
value: unknown;
|
|
113
|
+
columnName: string;
|
|
114
|
+
columnType: string | null;
|
|
115
|
+
}) => unknown;
|
|
116
|
+
}): R {
|
|
98
117
|
const decodedRow: any = {};
|
|
99
|
-
|
|
100
|
-
|
|
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
|
+
|
|
131
|
+
for (const [columnName, rawValue] of Object.entries(args.row)) {
|
|
132
|
+
decodedRow[columnName] = decodeColumnValue({
|
|
133
|
+
value: rawValue,
|
|
134
|
+
columnName,
|
|
135
|
+
columnType: null,
|
|
136
|
+
});
|
|
101
137
|
}
|
|
102
138
|
|
|
103
139
|
return decodedRow as R;
|
|
104
140
|
}
|
|
105
141
|
|
|
106
|
-
function decodeColumnValue<V = any>(
|
|
107
|
-
value: unknown | V
|
|
108
|
-
columnName: string
|
|
109
|
-
|
|
142
|
+
function decodeColumnValue<V = any>(args: {
|
|
143
|
+
value: unknown | V;
|
|
144
|
+
columnName: string;
|
|
145
|
+
columnType: string | null;
|
|
146
|
+
verbose?: (...args: any[]) => void;
|
|
147
|
+
}): V | null {
|
|
148
|
+
const { value, columnName, columnType } = args;
|
|
110
149
|
if (value === null || value === undefined) {
|
|
111
150
|
return null;
|
|
112
151
|
}
|
|
113
152
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return new Date(value) as V;
|
|
117
|
-
}
|
|
153
|
+
const isDateTime = columnType === "datetime" || isTimestampColumn(columnName);
|
|
154
|
+
if (isDateTime) {
|
|
118
155
|
if (typeof value === "string") {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
156
|
+
return Temporal.Instant.from(value) as V;
|
|
157
|
+
}
|
|
158
|
+
if (typeof value === "number") {
|
|
159
|
+
return Temporal.Instant.fromEpochMilliseconds(value) as V;
|
|
123
160
|
}
|
|
161
|
+
throw new Error(
|
|
162
|
+
`Expected datetime column ${columnName} to be a string or number, got ${typeof value}`
|
|
163
|
+
);
|
|
124
164
|
}
|
|
125
165
|
|
|
126
166
|
if (typeof value === "string") {
|
|
@@ -128,8 +168,7 @@ function decodeColumnValue<V = any>(
|
|
|
128
168
|
}
|
|
129
169
|
|
|
130
170
|
if (value instanceof Uint8Array || value instanceof ArrayBuffer) {
|
|
131
|
-
const bytes =
|
|
132
|
-
value instanceof Uint8Array ? value : new Uint8Array(value);
|
|
171
|
+
const bytes = value instanceof Uint8Array ? value : new Uint8Array(value);
|
|
133
172
|
const decoded = new TextDecoder().decode(bytes);
|
|
134
173
|
return tryDecodeJson(decoded) ?? (value as V);
|
|
135
174
|
}
|
|
@@ -145,7 +184,13 @@ function tryDecodeJson<V = any>(value: string): V | null {
|
|
|
145
184
|
}
|
|
146
185
|
}
|
|
147
186
|
|
|
148
|
-
function encodeColumnValue(value:
|
|
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
|
+
}
|
|
149
194
|
if (value instanceof Date) {
|
|
150
195
|
return value.toISOString();
|
|
151
196
|
}
|
|
@@ -159,60 +204,23 @@ function isTimestampColumn(columnName: string): boolean {
|
|
|
159
204
|
return columnName.endsWith("_at");
|
|
160
205
|
}
|
|
161
206
|
|
|
162
|
-
function
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const inner = params[0];
|
|
171
|
-
if (Array.isArray(inner)) {
|
|
172
|
-
return inner;
|
|
173
|
-
}
|
|
174
|
-
return Object.values(inner);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return params as SQLiteBindValue[];
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function isBindParams(value: unknown): value is SQLiteBindParams {
|
|
181
|
-
if (Array.isArray(value)) {
|
|
182
|
-
return true;
|
|
183
|
-
}
|
|
184
|
-
if (!value || typeof value !== "object") {
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
const tag = Object.prototype.toString.call(value);
|
|
188
|
-
return tag === "[object Object]";
|
|
207
|
+
function isReadonlyQuery(sql: string): boolean {
|
|
208
|
+
const trimmed = sql.trim().toLowerCase();
|
|
209
|
+
return (
|
|
210
|
+
trimmed.startsWith("select") ||
|
|
211
|
+
trimmed.startsWith("with") ||
|
|
212
|
+
trimmed.startsWith("pragma") ||
|
|
213
|
+
trimmed.startsWith("explain")
|
|
214
|
+
);
|
|
189
215
|
}
|
|
190
216
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
function isRetryableSQLiteError(err: unknown): boolean {
|
|
195
|
-
if (!err || typeof err !== "object") {
|
|
196
|
-
return false;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
const code = (err as any).code;
|
|
200
|
-
if (typeof code === "string") {
|
|
201
|
-
for (const retryableCode of sqliteRetryableErrorCodes) {
|
|
202
|
-
if (code.startsWith(retryableCode)) {
|
|
203
|
-
return true;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
217
|
+
function normalizeColumnType(value: string | null): string | null {
|
|
218
|
+
if (!value) {
|
|
219
|
+
return null;
|
|
206
220
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
return true;
|
|
221
|
+
const lowered = value.toLowerCase();
|
|
222
|
+
if (lowered === "null") {
|
|
223
|
+
return null;
|
|
211
224
|
}
|
|
212
|
-
|
|
213
|
-
return false;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
function delay(ms: number): Promise<void> {
|
|
217
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
225
|
+
return lowered;
|
|
218
226
|
}
|
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,7 +1,7 @@
|
|
|
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
|
-
import { TimeoutError } from "absurd-sdk";
|
|
4
|
+
import { TimeoutError } from "@absurd-sqlite/sdk";
|
|
5
5
|
|
|
6
6
|
describe("Event system", () => {
|
|
7
7
|
let ctx: TestContext;
|
|
@@ -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/hooks.test.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, test, expect, beforeAll, afterEach } from "bun:test";
|
|
2
2
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
|
-
import type { Absurd, SpawnOptions } from "absurd-sdk";
|
|
3
|
+
import type { Absurd, SpawnOptions } from "@absurd-sqlite/sdk";
|
|
4
4
|
import { createTestAbsurd, randomName, type TestContext } from "./setup";
|
|
5
5
|
|
|
6
6
|
describe("Hooks", () => {
|
package/test/idempotent.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { describe, test, expect, beforeAll, afterEach } from "bun:test";
|
|
2
|
-
import type { Absurd } from "absurd-sdk";
|
|
2
|
+
import type { Absurd } from "@absurd-sqlite/sdk";
|
|
3
3
|
import { createTestAbsurd, randomName, type TestContext } from "./setup";
|
|
4
4
|
|
|
5
5
|
describe("Idempotent Task Spawning", () => {
|
package/test/index.test.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { mkdtempSync, rmSync } from "node:fs";
|
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { afterEach, describe, expect, it } from "bun:test";
|
|
6
|
-
import { Absurd } from "absurd-sdk";
|
|
6
|
+
import { Absurd } from "@absurd-sqlite/sdk";
|
|
7
7
|
|
|
8
8
|
import { BunSqliteConnection } from "../src/sqlite";
|
|
9
9
|
import { loadExtension } from "./setup";
|
|
@@ -29,7 +29,7 @@ describe("Absurd", () => {
|
|
|
29
29
|
it("creates and lists queues using the sqlite extension", async () => {
|
|
30
30
|
const db = createDatabaseWithMigrations();
|
|
31
31
|
const conn = new BunSqliteConnection(db);
|
|
32
|
-
const absurd = new Absurd(
|
|
32
|
+
const absurd = new Absurd(conn);
|
|
33
33
|
|
|
34
34
|
await absurd.createQueue("alpha");
|
|
35
35
|
await absurd.createQueue("beta");
|
|
@@ -52,7 +52,7 @@ describe("Absurd", () => {
|
|
|
52
52
|
it("closes workers without affecting the sqlite database", async () => {
|
|
53
53
|
const db = createDatabaseWithMigrations();
|
|
54
54
|
const conn = new BunSqliteConnection(db);
|
|
55
|
-
const absurd = new Absurd(
|
|
55
|
+
const absurd = new Absurd(conn);
|
|
56
56
|
|
|
57
57
|
await absurd.close();
|
|
58
58
|
|
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
|
|