@absurd-sqlite/sdk 0.2.0-alpha.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/README.md ADDED
@@ -0,0 +1 @@
1
+ # Absurd Sqlite SDK for TypeScript
@@ -0,0 +1,109 @@
1
+ import type { ClaimedTask, JsonValue, SpawnOptions, SpawnResult, TaskHandler, TaskRegistrationOptions, WorkerOptions } from "absurd-sdk";
2
+ import type { SQLiteRestBindParams } from "./sqlite-types";
3
+ /**
4
+ * Minimal query interface compatible with Absurd's database operations.
5
+ */
6
+ export interface Queryable {
7
+ /**
8
+ * Execute a parameterized SQL query and return rows.
9
+ * @param sql SQL text with parameter placeholders.
10
+ * @param params Optional positional parameters.
11
+ */
12
+ query<R extends object = Record<string, any>>(sql: string, params?: SQLiteRestBindParams): Promise<{
13
+ rows: R[];
14
+ }>;
15
+ }
16
+ /**
17
+ * Background worker handle returned by startWorker().
18
+ */
19
+ export interface Worker {
20
+ /**
21
+ * Stop the worker loop and wait for in-flight tasks to settle.
22
+ */
23
+ close(): Promise<void>;
24
+ }
25
+ /**
26
+ * Absurd client interface.
27
+ */
28
+ export interface AbsurdClient {
29
+ /**
30
+ * Create a new client bound to the provided connection.
31
+ * @param con Connection to use for queries.
32
+ * @param owned If true, close the connection when close() is called.
33
+ */
34
+ /**
35
+ * Register a task handler.
36
+ * @param options Task registration options.
37
+ * @param handler Async task handler.
38
+ */
39
+ registerTask<P = any, R = any>(options: TaskRegistrationOptions, handler: TaskHandler<P, R>): void;
40
+ /**
41
+ * Create a queue.
42
+ * @param queueName Optional queue name (defaults to client queue).
43
+ */
44
+ createQueue(queueName?: string): Promise<void>;
45
+ /**
46
+ * Drop a queue.
47
+ * @param queueName Optional queue name (defaults to client queue).
48
+ */
49
+ dropQueue(queueName?: string): Promise<void>;
50
+ /**
51
+ * List available queues.
52
+ */
53
+ listQueues(): Promise<Array<string>>;
54
+ /**
55
+ * Spawn a task execution.
56
+ * @param taskName Task name.
57
+ * @param params Task parameters.
58
+ * @param options Spawn options including queue and retry behavior.
59
+ */
60
+ spawn<P = any>(taskName: string, params: P, options?: SpawnOptions): Promise<SpawnResult>;
61
+ /**
62
+ * Emit an event on a queue.
63
+ * @param eventName Non-empty event name.
64
+ * @param payload Optional JSON payload.
65
+ * @param queueName Optional queue name (defaults to client queue).
66
+ */
67
+ emitEvent(eventName: string, payload?: JsonValue, queueName?: string): Promise<void>;
68
+ /**
69
+ * Cancel a task by ID.
70
+ * @param taskID Task identifier.
71
+ * @param queueName Optional queue name (defaults to client queue).
72
+ */
73
+ cancelTask(taskID: string, queueName?: string): Promise<void>;
74
+ /**
75
+ * Claim tasks for processing.
76
+ * @param options Claiming options.
77
+ */
78
+ claimTasks(options?: {
79
+ batchSize?: number;
80
+ claimTimeout?: number;
81
+ workerId?: string;
82
+ }): Promise<ClaimedTask[]>;
83
+ /**
84
+ * Claim and process a batch of tasks sequentially.
85
+ * @param workerId Worker identifier.
86
+ * @param claimTimeout Lease duration in seconds.
87
+ * @param batchSize Max tasks to process.
88
+ */
89
+ workBatch(workerId?: string, claimTimeout?: number, batchSize?: number): Promise<void>;
90
+ /**
91
+ * Start a background worker that polls and executes tasks.
92
+ * @param options Worker behavior options.
93
+ */
94
+ startWorker(options?: WorkerOptions): Promise<Worker>;
95
+ /**
96
+ * Close the client and any owned resources.
97
+ */
98
+ close(): Promise<void>;
99
+ /**
100
+ * Execute a claimed task (used by workers).
101
+ * @param task Claimed task record.
102
+ * @param claimTimeout Lease duration in seconds.
103
+ * @param options Execution options.
104
+ */
105
+ executeTask(task: ClaimedTask, claimTimeout: number, options?: {
106
+ fatalOnLeaseTimeout?: boolean;
107
+ }): Promise<void>;
108
+ }
109
+ //# sourceMappingURL=absurd-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"absurd-types.d.ts","sourceRoot":"","sources":["../src/absurd-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,SAAS,EACT,YAAY,EACZ,WAAW,EACX,WAAW,EACX,uBAAuB,EACvB,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,SAAS;IACxB;;;;OAIG;IACH,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC1C,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,EAAE,CAAA;KAAE,CAAC,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,MAAM;IACrB;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;OAIG;IAGH;;;;OAIG;IACH,YAAY,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EAC3B,OAAO,EAAE,uBAAuB,EAChC,OAAO,EAAE,WAAW,CAAC,CAAC,EAAE,CAAC,CAAC,GACzB,IAAI,CAAC;IAER;;;OAGG;IACH,WAAW,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C;;;OAGG;IACH,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7C;;OAEG;IACH,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;IAErC;;;;;OAKG;IACH,KAAK,CAAC,CAAC,GAAG,GAAG,EACX,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,CAAC,EACT,OAAO,CAAC,EAAE,YAAY,GACrB,OAAO,CAAC,WAAW,CAAC,CAAC;IAExB;;;;;OAKG;IACH,SAAS,CACP,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,SAAS,EACnB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;;OAIG;IACH,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9D;;;OAGG;IACH,UAAU,CAAC,OAAO,CAAC,EAAE;QACnB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IAE3B;;;;;OAKG;IACH,SAAS,CACP,QAAQ,CAAC,EAAE,MAAM,EACjB,YAAY,CAAC,EAAE,MAAM,EACrB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAAC;IAEjB;;;OAGG;IACH,WAAW,CAAC,OAAO,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAEtD;;OAEG;IACH,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB;;;;;OAKG;IACH,WAAW,CACT,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE;QAAE,mBAAmB,CAAC,EAAE,OAAO,CAAA;KAAE,GAC1C,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=absurd-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"absurd-types.js","sourceRoot":"","sources":["../src/absurd-types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Absurd = void 0;
4
+ const absurd_sdk_1 = require("absurd-sdk");
5
+ const sqlite_1 = require("./sqlite");
6
+ class Absurd extends absurd_sdk_1.Absurd {
7
+ db;
8
+ constructor(db, extensionPath) {
9
+ db.loadExtension(extensionPath);
10
+ const queryable = new sqlite_1.SqliteConnection(db);
11
+ super(queryable);
12
+ this.db = db;
13
+ }
14
+ async close() {
15
+ this.db.close();
16
+ }
17
+ }
18
+ exports.Absurd = Absurd;
@@ -0,0 +1 @@
1
+ {"type":"commonjs"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SqliteConnection = void 0;
4
+ class SqliteConnection {
5
+ db;
6
+ constructor(db) {
7
+ this.db = db;
8
+ // TODO: verbose logging
9
+ }
10
+ async query(sql, params) {
11
+ const sqliteQuery = rewritePostgresQuery(sql);
12
+ const sqliteParams = rewritePostgresParams(params);
13
+ const statement = this.db.prepare(sqliteQuery);
14
+ if (!statement.readonly) {
15
+ // this indicates `return_data` is false
16
+ // https://github.com/WiseLibs/better-sqlite3/blob/6209be238d6a1b181f516e4e636986604b0f62e1/src/objects/statement.cpp#L134C83-L134C95
17
+ throw new Error("The query() method is only statements that return data");
18
+ }
19
+ const rowsDecoded = statement
20
+ .all(sqliteParams)
21
+ .map((row) => decodeRowValues(statement, row));
22
+ return { rows: rowsDecoded };
23
+ }
24
+ async exec(sql, params) {
25
+ const sqliteQuery = rewritePostgresQuery(sql);
26
+ const sqliteParams = rewritePostgresParams(params);
27
+ this.db.prepare(sqliteQuery).run(sqliteParams);
28
+ }
29
+ }
30
+ exports.SqliteConnection = SqliteConnection;
31
+ const namedParamPrefix = "p";
32
+ function rewritePostgresQuery(text) {
33
+ return text
34
+ .replace(/\$(\d+)/g, `:${namedParamPrefix}$1`)
35
+ .replace(/absurd\.(\w+)/g, "absurd_$1");
36
+ }
37
+ function rewritePostgresParams(params) {
38
+ if (!params) {
39
+ return {};
40
+ }
41
+ const rewrittenParams = {};
42
+ params.forEach((value, index) => {
43
+ const paramKey = `${namedParamPrefix}${index + 1}`;
44
+ const encodedParamValue = encodeColumnValue(value);
45
+ rewrittenParams[paramKey] = encodedParamValue;
46
+ });
47
+ return rewrittenParams;
48
+ }
49
+ function decodeRowValues(statement, row, verbose) {
50
+ const columns = statement.columns();
51
+ const decodedRow = {};
52
+ for (const column of columns) {
53
+ const columnName = column.name;
54
+ const columnType = column.type;
55
+ const rawValue = row[columnName];
56
+ const decodedValue = decodeColumnValue(rawValue, columnName, columnType, verbose);
57
+ decodedRow[columnName] = decodedValue;
58
+ }
59
+ return decodedRow;
60
+ }
61
+ function decodeColumnValue(value, columnName, columnType, verbose) {
62
+ if (value === null || value === undefined) {
63
+ return null;
64
+ }
65
+ if (columnType === null) {
66
+ if (typeof value === "string") {
67
+ // When column type is not known but the value is string
68
+ // try parse it as JSON -- for cases where the column is computed
69
+ // e.g. `SELECT json(x) as y from ....`
70
+ // FIXME: better type detection
71
+ let rv;
72
+ let isValidJSON = false;
73
+ try {
74
+ rv = JSON.parse(value);
75
+ isValidJSON = true;
76
+ }
77
+ catch (e) {
78
+ verbose?.(`Failed to decode string column ${columnName} as JSON`, e);
79
+ rv = value;
80
+ }
81
+ if (isValidJSON) {
82
+ verbose?.(`Decoded column ${columnName} with null as JSON`);
83
+ }
84
+ return rv;
85
+ }
86
+ verbose?.(`Column ${columnName} has null type, returning raw value`);
87
+ return value;
88
+ }
89
+ const columnTypeName = columnType.toLowerCase();
90
+ if (columnTypeName === "blob") {
91
+ // BLOB values are JSON string decoded from JSONB
92
+ try {
93
+ return JSON.parse(value.toString());
94
+ }
95
+ catch (e) {
96
+ verbose?.(`Failed to decode BLOB column ${columnName} as JSON`, e);
97
+ throw e;
98
+ }
99
+ }
100
+ if (columnTypeName === "datetime") {
101
+ if (typeof value !== "number") {
102
+ throw new Error(`Expected datetime column ${columnName} to be a number, got ${typeof value}`);
103
+ }
104
+ return new Date(value);
105
+ }
106
+ // For other types, return as is
107
+ return value;
108
+ }
109
+ function encodeColumnValue(value) {
110
+ if (value instanceof Date) {
111
+ return value.toISOString();
112
+ }
113
+ if (typeof value === "number" && Number.isInteger(value)) {
114
+ return value.toString();
115
+ }
116
+ return value;
117
+ }
@@ -0,0 +1,12 @@
1
+ import { Absurd as AbsurdBase } from "absurd-sdk";
2
+ import type { AbsurdClient } from "./absurd-types";
3
+ import type { SQLiteDatabase } from "./sqlite-types";
4
+ export type { AbsurdClient, Queryable, Worker } from "./absurd-types";
5
+ export type { AbsurdHooks, AbsurdOptions, CancellationPolicy, ClaimedTask, JsonObject, JsonValue, RetryStrategy, SpawnOptions, SpawnResult, TaskContext, TaskHandler, TaskRegistrationOptions, WorkerOptions, } from "absurd-sdk";
6
+ export type { SQLiteBindParams, SQLiteBindValue, SQLiteColumnDefinition, SQLiteDatabase, SQLiteRestBindParams, SQLiteStatement, SQLiteVerboseLog, } from "./sqlite-types";
7
+ export declare class Absurd extends AbsurdBase implements AbsurdClient {
8
+ private db;
9
+ constructor(db: SQLiteDatabase, extensionPath: string);
10
+ close(): Promise<void>;
11
+ }
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,YAAY,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACtE,YAAY,EACV,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,WAAW,EACX,UAAU,EACV,SAAS,EACT,aAAa,EACb,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,EACX,uBAAuB,EACvB,aAAa,GACd,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,gBAAgB,EAChB,eAAe,EACf,sBAAsB,EACtB,cAAc,EACd,oBAAoB,EACpB,eAAe,EACf,gBAAgB,GACjB,MAAM,gBAAgB,CAAC;AAExB,qBAAa,MAAO,SAAQ,UAAW,YAAW,YAAY;IAC5D,OAAO,CAAC,EAAE,CAAiB;gBAEf,EAAE,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM;IAO/C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAG7B"}
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ import { Absurd as AbsurdBase } from "absurd-sdk";
2
+ import { SqliteConnection } from "./sqlite";
3
+ export class Absurd extends AbsurdBase {
4
+ db;
5
+ constructor(db, extensionPath) {
6
+ db.loadExtension(extensionPath);
7
+ const queryable = new SqliteConnection(db);
8
+ super(queryable);
9
+ this.db = db;
10
+ }
11
+ async close() {
12
+ this.db.close();
13
+ }
14
+ }
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,UAAU,EAAE,MAAM,YAAY,CAAC;AAIlD,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AA4B5C,MAAM,OAAO,MAAO,SAAQ,UAAU;IAC5B,EAAE,CAAiB;IAE3B,YAAY,EAAkB,EAAE,aAAqB;QACnD,EAAE,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;QAChC,MAAM,SAAS,GAAG,IAAI,gBAAgB,CAAC,EAAE,CAAC,CAAC;QAC3C,KAAK,CAAC,SAAS,CAAC,CAAC;QACjB,IAAI,CAAC,EAAE,GAAG,EAAE,CAAC;IACf,CAAC;IAED,KAAK,CAAC,KAAK;QACT,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;IAClB,CAAC;CACF"}
@@ -0,0 +1,23 @@
1
+ export type SQLiteBindValue = number | string | Buffer | bigint | Date | null;
2
+ export type SQLiteBindParams = SQLiteBindValue[] | Record<string, SQLiteBindValue>;
3
+ export type SQLiteRestBindParams = SQLiteBindValue[] | [SQLiteBindParams];
4
+ export interface SQLiteColumnDefinition {
5
+ name: string;
6
+ column: string | null;
7
+ table: string | null;
8
+ database: string | null;
9
+ type: string | null;
10
+ }
11
+ export interface SQLiteStatement<Result extends object = Record<string, any>> {
12
+ readonly: boolean;
13
+ columns(): SQLiteColumnDefinition[];
14
+ all(...args: SQLiteRestBindParams): Result[];
15
+ run(...args: SQLiteRestBindParams): number;
16
+ }
17
+ export interface SQLiteDatabase {
18
+ prepare<Result extends object = Record<string, any>>(sql: string): SQLiteStatement<Result>;
19
+ close(): void;
20
+ loadExtension(path: string): void;
21
+ }
22
+ export type SQLiteVerboseLog = (message?: any, ...optionalParams: any[]) => void;
23
+ //# sourceMappingURL=sqlite-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-types.d.ts","sourceRoot":"","sources":["../src/sqlite-types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;AAE9E,MAAM,MAAM,gBAAgB,GACxB,eAAe,EAAE,GACjB,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAEpC,MAAM,MAAM,oBAAoB,GAAG,eAAe,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC;AAE1E,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED,MAAM,WAAW,eAAe,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAC1E,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,IAAI,sBAAsB,EAAE,CAAC;IACpC,GAAG,CAAC,GAAG,IAAI,EAAE,oBAAoB,GAAG,MAAM,EAAE,CAAC;IAC7C,GAAG,CAAC,GAAG,IAAI,EAAE,oBAAoB,GAAG,MAAM,CAAC;CAC5C;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,MAAM,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EACjD,GAAG,EAAE,MAAM,GACV,eAAe,CAAC,MAAM,CAAC,CAAC;IAC3B,KAAK,IAAI,IAAI,CAAC;IACd,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACnC;AAED,MAAM,MAAM,gBAAgB,GAAG,CAC7B,OAAO,CAAC,EAAE,GAAG,EACb,GAAG,cAAc,EAAE,GAAG,EAAE,KACrB,IAAI,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=sqlite-types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite-types.js","sourceRoot":"","sources":["../src/sqlite-types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,11 @@
1
+ import type { Queryable } from "./absurd-types";
2
+ import type { SQLiteRestBindParams, SQLiteDatabase } from "./sqlite-types";
3
+ export declare class SqliteConnection implements Queryable {
4
+ private readonly db;
5
+ constructor(db: SQLiteDatabase);
6
+ query<R extends object = Record<string, any>>(sql: string, params?: SQLiteRestBindParams): Promise<{
7
+ rows: R[];
8
+ }>;
9
+ exec(sql: string, params?: SQLiteRestBindParams): Promise<void>;
10
+ }
11
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EACV,oBAAoB,EACpB,cAAc,EAGf,MAAM,gBAAgB,CAAC;AAExB,qBAAa,gBAAiB,YAAW,SAAS;IAChD,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAiB;gBAExB,EAAE,EAAE,cAAc;IAKxB,KAAK,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAChD,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,oBAAoB,GAC5B,OAAO,CAAC;QAAE,IAAI,EAAE,CAAC,EAAE,CAAA;KAAE,CAAC;IAkBnB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;CAMtE"}
package/dist/sqlite.js ADDED
@@ -0,0 +1,114 @@
1
+ export class SqliteConnection {
2
+ db;
3
+ constructor(db) {
4
+ this.db = db;
5
+ // TODO: verbose logging
6
+ }
7
+ async query(sql, params) {
8
+ const sqliteQuery = rewritePostgresQuery(sql);
9
+ const sqliteParams = rewritePostgresParams(params);
10
+ const statement = this.db.prepare(sqliteQuery);
11
+ if (!statement.readonly) {
12
+ // this indicates `return_data` is false
13
+ // https://github.com/WiseLibs/better-sqlite3/blob/6209be238d6a1b181f516e4e636986604b0f62e1/src/objects/statement.cpp#L134C83-L134C95
14
+ throw new Error("The query() method is only statements that return data");
15
+ }
16
+ const rowsDecoded = statement
17
+ .all(sqliteParams)
18
+ .map((row) => decodeRowValues(statement, row));
19
+ return { rows: rowsDecoded };
20
+ }
21
+ async exec(sql, params) {
22
+ const sqliteQuery = rewritePostgresQuery(sql);
23
+ const sqliteParams = rewritePostgresParams(params);
24
+ this.db.prepare(sqliteQuery).run(sqliteParams);
25
+ }
26
+ }
27
+ const namedParamPrefix = "p";
28
+ function rewritePostgresQuery(text) {
29
+ return text
30
+ .replace(/\$(\d+)/g, `:${namedParamPrefix}$1`)
31
+ .replace(/absurd\.(\w+)/g, "absurd_$1");
32
+ }
33
+ function rewritePostgresParams(params) {
34
+ if (!params) {
35
+ return {};
36
+ }
37
+ const rewrittenParams = {};
38
+ params.forEach((value, index) => {
39
+ const paramKey = `${namedParamPrefix}${index + 1}`;
40
+ const encodedParamValue = encodeColumnValue(value);
41
+ rewrittenParams[paramKey] = encodedParamValue;
42
+ });
43
+ return rewrittenParams;
44
+ }
45
+ function decodeRowValues(statement, row, verbose) {
46
+ const columns = statement.columns();
47
+ const decodedRow = {};
48
+ for (const column of columns) {
49
+ const columnName = column.name;
50
+ const columnType = column.type;
51
+ const rawValue = row[columnName];
52
+ const decodedValue = decodeColumnValue(rawValue, columnName, columnType, verbose);
53
+ decodedRow[columnName] = decodedValue;
54
+ }
55
+ return decodedRow;
56
+ }
57
+ function decodeColumnValue(value, columnName, columnType, verbose) {
58
+ if (value === null || value === undefined) {
59
+ return null;
60
+ }
61
+ if (columnType === null) {
62
+ if (typeof value === "string") {
63
+ // When column type is not known but the value is string
64
+ // try parse it as JSON -- for cases where the column is computed
65
+ // e.g. `SELECT json(x) as y from ....`
66
+ // FIXME: better type detection
67
+ let rv;
68
+ let isValidJSON = false;
69
+ try {
70
+ rv = JSON.parse(value);
71
+ isValidJSON = true;
72
+ }
73
+ catch (e) {
74
+ verbose?.(`Failed to decode string column ${columnName} as JSON`, e);
75
+ rv = value;
76
+ }
77
+ if (isValidJSON) {
78
+ verbose?.(`Decoded column ${columnName} with null as JSON`);
79
+ }
80
+ return rv;
81
+ }
82
+ verbose?.(`Column ${columnName} has null type, returning raw value`);
83
+ return value;
84
+ }
85
+ const columnTypeName = columnType.toLowerCase();
86
+ if (columnTypeName === "blob") {
87
+ // BLOB values are JSON string decoded from JSONB
88
+ try {
89
+ return JSON.parse(value.toString());
90
+ }
91
+ catch (e) {
92
+ verbose?.(`Failed to decode BLOB column ${columnName} as JSON`, e);
93
+ throw e;
94
+ }
95
+ }
96
+ if (columnTypeName === "datetime") {
97
+ if (typeof value !== "number") {
98
+ throw new Error(`Expected datetime column ${columnName} to be a number, got ${typeof value}`);
99
+ }
100
+ return new Date(value);
101
+ }
102
+ // For other types, return as is
103
+ return value;
104
+ }
105
+ function encodeColumnValue(value) {
106
+ if (value instanceof Date) {
107
+ return value.toISOString();
108
+ }
109
+ if (typeof value === "number" && Number.isInteger(value)) {
110
+ return value.toString();
111
+ }
112
+ return value;
113
+ }
114
+ //# sourceMappingURL=sqlite.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.js","sourceRoot":"","sources":["../src/sqlite.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@absurd-sqlite/sdk",
3
+ "version": "0.2.0-alpha.0",
4
+ "description": "TypeScript SDK for Absurd-SQLite - SQLite-based durable task execution",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc --project tsconfig.build.json && tsc --project tsconfig.cjs.json && echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json",
9
+ "type-check": "tsc --noEmit",
10
+ "prepare": "npm run build",
11
+ "test:prepare": "cd ../../ && cargo build -p absurd-sqlite-extension --release",
12
+ "test": "npm run test:prepare && vitest --silent --run",
13
+ "test:watch": "npm run test:prepare && vitest",
14
+ "test:ui": "npm run test:prepare && vitest --ui",
15
+ "docs": "typedoc"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/b4fun/absurd-sqlite.git"
20
+ },
21
+ "keywords": [
22
+ "absurd",
23
+ "sqlite",
24
+ "queue",
25
+ "task",
26
+ "durable",
27
+ "workflow"
28
+ ],
29
+ "author": "",
30
+ "license": "Apache-2.0",
31
+ "bugs": {
32
+ "url": "https://github.com/b4fun/absurd-sqlite/issues"
33
+ },
34
+ "homepage": "https://github.com/b4fun/absurd-sqlite#readme",
35
+ "peerDependencies": {
36
+ "better-sqlite3": "^12.5.0"
37
+ },
38
+ "dependencies": {
39
+ "absurd-sdk": "~0.0.7"
40
+ },
41
+ "devDependencies": {
42
+ "@types/better-sqlite3": "^7.6.13",
43
+ "@types/node": "^22.18.0",
44
+ "typedoc": "^0.28.14",
45
+ "typescript": "^5.9.3",
46
+ "vitest": "^4.0.7"
47
+ },
48
+ "engines": {
49
+ "node": ">=18.0.0"
50
+ }
51
+ }