@yieldstar/sqlite-runtime 0.5.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.
Files changed (56) hide show
  1. package/dist/bun.d.ts +13 -0
  2. package/dist/bun.d.ts.map +1 -0
  3. package/dist/bun.js +43 -0
  4. package/dist/dao/step-response-dao.d.ts +19 -0
  5. package/dist/dao/step-response-dao.d.ts.map +1 -0
  6. package/dist/dao/task-queue-dao.d.ts +21 -0
  7. package/dist/dao/task-queue-dao.d.ts.map +1 -0
  8. package/dist/dao/timers-dao.d.ts +22 -0
  9. package/dist/dao/timers-dao.d.ts.map +1 -0
  10. package/dist/index-4vq70w0r.js +796 -0
  11. package/dist/index-h7bm9xg4.js +26 -0
  12. package/dist/index-s8wqgfwt.js +19 -0
  13. package/dist/index-x891hxwg.js +796 -0
  14. package/dist/index.d.ts +9 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +16 -0
  17. package/dist/node-driver.d.ts +10 -0
  18. package/dist/node-driver.d.ts.map +1 -0
  19. package/dist/node.d.ts +13 -0
  20. package/dist/node.d.ts.map +1 -0
  21. package/dist/node.js +49 -0
  22. package/dist/sqlite-db.d.ts +29 -0
  23. package/dist/sqlite-db.d.ts.map +1 -0
  24. package/dist/sqlite-driver.d.ts +21 -0
  25. package/dist/sqlite-driver.d.ts.map +1 -0
  26. package/dist/sqlite-event-loop.d.ts +18 -0
  27. package/dist/sqlite-event-loop.d.ts.map +1 -0
  28. package/dist/sqlite-heap.d.ts +26 -0
  29. package/dist/sqlite-heap.d.ts.map +1 -0
  30. package/dist/sqlite-scheduler.d.ts +13 -0
  31. package/dist/sqlite-scheduler.d.ts.map +1 -0
  32. package/dist/sqlite-store.d.ts +54 -0
  33. package/dist/sqlite-store.d.ts.map +1 -0
  34. package/dist/sqlite-store.test.d.ts +2 -0
  35. package/dist/sqlite-store.test.d.ts.map +1 -0
  36. package/dist/sqlite-task-queue.d.ts +26 -0
  37. package/dist/sqlite-task-queue.d.ts.map +1 -0
  38. package/dist/sqlite-timers.d.ts +18 -0
  39. package/dist/sqlite-timers.d.ts.map +1 -0
  40. package/package.json +43 -0
  41. package/src/bun.ts +32 -0
  42. package/src/dao/step-response-dao.ts +80 -0
  43. package/src/dao/task-queue-dao.ts +79 -0
  44. package/src/dao/timers-dao.ts +81 -0
  45. package/src/index.ts +14 -0
  46. package/src/node.ts +47 -0
  47. package/src/sqlite-db.ts +70 -0
  48. package/src/sqlite-driver.ts +30 -0
  49. package/src/sqlite-event-loop.ts +41 -0
  50. package/src/sqlite-heap.ts +54 -0
  51. package/src/sqlite-scheduler.ts +24 -0
  52. package/src/sqlite-store.ts +755 -0
  53. package/src/sqlite-task-queue.ts +64 -0
  54. package/src/sqlite-timers.ts +40 -0
  55. package/tsconfig.json +9 -0
  56. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,81 @@
1
+ import type { WorkflowEvent } from "@yieldstar/core";
2
+ import type { SqliteDriver } from "../sqlite-driver";
3
+
4
+ class TimerRow {
5
+ id!: string;
6
+ delay!: number;
7
+ workflow_id!: string;
8
+ execution_id!: string;
9
+ created_at!: number;
10
+ params?: string;
11
+ context?: string;
12
+ }
13
+
14
+ class CountRow {
15
+ count!: number;
16
+ }
17
+
18
+ export class TimersDao {
19
+ private db: SqliteDriver;
20
+
21
+ constructor(db: SqliteDriver) {
22
+ this.db = db;
23
+ this.setupDb();
24
+ }
25
+
26
+ private setupDb() {
27
+ this.db.run(`
28
+ CREATE TABLE IF NOT EXISTS scheduled_tasks (
29
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
30
+ delay INTEGER NOT NULL,
31
+ workflow_id TEXT NOT NULL,
32
+ execution_id TEXT NOT NULL,
33
+ created_at INTEGER NOT NULL,
34
+ params TEXT,
35
+ context TEXT
36
+ );
37
+ `);
38
+ }
39
+
40
+ getExpiredTimers(): TimerRow[] {
41
+ const query = this.db.query<TimerRow>(
42
+ `SELECT id, delay, workflow_id, execution_id, created_at, params, context
43
+ FROM scheduled_tasks
44
+ WHERE (created_at + delay) < $currentTime`
45
+ );
46
+
47
+ return query.all({ $currentTime: Date.now() });
48
+ }
49
+
50
+ getTaskCount() {
51
+ const query = this.db.query<CountRow>(
52
+ `SELECT COUNT(*) as count
53
+ FROM scheduled_tasks
54
+ WHERE (created_at + delay) < $currentTime`
55
+ );
56
+
57
+ const count = query.get({ $currentTime: Date.now() })!;
58
+ return count.count;
59
+ }
60
+
61
+ insertTimer(event: WorkflowEvent, delay: number) {
62
+ const query = this.db.query(
63
+ `INSERT INTO scheduled_tasks (delay, workflow_id, execution_id, created_at, params, context)
64
+ VALUES ($delay, $workflowId, $executionId, $createdAt, $params, $context)`
65
+ );
66
+
67
+ query.run({
68
+ $delay: delay,
69
+ $workflowId: event.workflowId,
70
+ $executionId: event.executionId,
71
+ $createdAt: Date.now(),
72
+ $params: event.params ? JSON.stringify(event.params) : null,
73
+ $context: JSON.stringify(Array.from(event.context.entries())),
74
+ });
75
+ }
76
+
77
+ deleteTimerById(id: string) {
78
+ const query = this.db.query(`DELETE FROM scheduled_tasks WHERE id = $id`);
79
+ query.run({ $id: id });
80
+ }
81
+ }
package/src/index.ts ADDED
@@ -0,0 +1,14 @@
1
+ export { SqliteHeapClient } from "./sqlite-heap";
2
+ export { SqliteStoreClient } from "./sqlite-store";
3
+ export { SqliteSchedulerClient } from "./sqlite-scheduler";
4
+ export { SqliteEventLoop } from "./sqlite-event-loop";
5
+ export { SqliteTaskQueueClient } from "./sqlite-task-queue";
6
+ export { SqliteTimersClient } from "./sqlite-timers";
7
+ export type {
8
+ SqliteDriver,
9
+ SqliteParameters,
10
+ SqliteParameterValue,
11
+ SqliteRunResult,
12
+ SqliteStatement,
13
+ } from "./sqlite-driver";
14
+ export type { SqliteDbOpts } from "./sqlite-db";
package/src/node.ts ADDED
@@ -0,0 +1,47 @@
1
+ import { DatabaseSync, type SQLInputValue } from "node:sqlite";
2
+ import {
3
+ createSqliteDriver,
4
+ wrapSqliteStatement,
5
+ type SqliteDbOpts,
6
+ } from "./sqlite-db";
7
+ import type {
8
+ SqliteDriver,
9
+ SqliteParameters,
10
+ SqliteStatement,
11
+ } from "./sqlite-driver";
12
+
13
+ export * from "./index";
14
+
15
+ export class NodeSqliteDriver implements SqliteDriver {
16
+ constructor(private readonly database: DatabaseSync) {}
17
+
18
+ run(sql: string) {
19
+ this.database.exec(sql);
20
+ }
21
+
22
+ query<Row>(sql: string): SqliteStatement<Row> {
23
+ return wrapSqliteStatement(this.database.prepare(sql), normalizeParameters);
24
+ }
25
+
26
+ close() {
27
+ this.database.close();
28
+ }
29
+ }
30
+
31
+ function normalizeParameters(
32
+ parameters: SqliteParameters
33
+ ): Record<string, SQLInputValue> {
34
+ return Object.fromEntries(
35
+ Object.entries(parameters).map(([name, value]) => [
36
+ name,
37
+ typeof value === "boolean" ? Number(value) : value,
38
+ ])
39
+ );
40
+ }
41
+
42
+ export function createSqliteDb(opts?: SqliteDbOpts) {
43
+ return createSqliteDriver(
44
+ (path) => new NodeSqliteDriver(new DatabaseSync(path)),
45
+ opts
46
+ );
47
+ }
@@ -0,0 +1,70 @@
1
+ import { mkdirSync } from "node:fs";
2
+ import { dirname } from "node:path";
3
+ import type {
4
+ SqliteDriver,
5
+ SqliteParameters,
6
+ SqliteRunResult,
7
+ SqliteStatement,
8
+ } from "./sqlite-driver";
9
+
10
+ export type SqliteDbOpts = {
11
+ /**
12
+ * Path to the SQLite database file
13
+ * @default "./.db/yieldstar.sqlite"
14
+ */
15
+ path?: string;
16
+ /**
17
+ * Enable Write-Ahead Logging (WAL) mode.
18
+ * Recommended for better performance and concurrency.
19
+ * @default true
20
+ */
21
+ wal?: boolean;
22
+ };
23
+
24
+ const defaultOpts: Required<SqliteDbOpts> = {
25
+ path: "./.db/yieldstar.sqlite",
26
+ wal: true,
27
+ };
28
+
29
+ export function createSqliteDriver<Driver extends SqliteDriver>(
30
+ create: (path: string) => Driver,
31
+ opts?: SqliteDbOpts
32
+ ) {
33
+ const mergedOpts = { ...defaultOpts, ...opts };
34
+ mkdirSync(dirname(mergedOpts.path), { recursive: true });
35
+
36
+ const db = create(mergedOpts.path);
37
+ if (mergedOpts.wal) {
38
+ db.run("PRAGMA journal_mode = WAL");
39
+ }
40
+ db.run("PRAGMA busy_timeout = 5000");
41
+ return db;
42
+ }
43
+
44
+ /** The native prepared-statement shape connectors adapt to SqliteStatement. */
45
+ type NativeStatement = {
46
+ get(parameters?: any): unknown;
47
+ all(parameters?: any): unknown[];
48
+ run(parameters?: any): SqliteRunResult;
49
+ };
50
+
51
+ /**
52
+ * Adapt a native prepared statement to the SqliteStatement contract: bind
53
+ * parameters are only passed when provided, with `bind` mapping them to the
54
+ * native driver's binding shape.
55
+ */
56
+ export function wrapSqliteStatement<Row>(
57
+ statement: NativeStatement,
58
+ bind: (parameters: SqliteParameters) => any = (parameters) => parameters
59
+ ): SqliteStatement<Row> {
60
+ return {
61
+ get: (parameters) =>
62
+ (parameters ? statement.get(bind(parameters)) : statement.get()) as
63
+ | Row
64
+ | undefined,
65
+ all: (parameters) =>
66
+ (parameters ? statement.all(bind(parameters)) : statement.all()) as Row[],
67
+ run: (parameters) =>
68
+ parameters ? statement.run(bind(parameters)) : statement.run(),
69
+ };
70
+ }
@@ -0,0 +1,30 @@
1
+ export type SqliteParameterValue =
2
+ | string
3
+ | number
4
+ | bigint
5
+ | boolean
6
+ | Uint8Array
7
+ | null;
8
+
9
+ export type SqliteParameters = Record<`$${string}`, SqliteParameterValue>;
10
+
11
+ export type SqliteRunResult = {
12
+ changes: number | bigint;
13
+ };
14
+
15
+ /**
16
+ * Callers must supply exactly the named parameters used by the SQL, including
17
+ * each `$` prefix. Connectors do not normalize additional names because native
18
+ * drivers differ: some ignore them while others throw.
19
+ */
20
+ export interface SqliteStatement<Row> {
21
+ get(parameters?: SqliteParameters): Row | undefined;
22
+ all(parameters?: SqliteParameters): Row[];
23
+ run(parameters?: SqliteParameters): SqliteRunResult;
24
+ }
25
+
26
+ export interface SqliteDriver {
27
+ run(sql: string): void;
28
+ query<Row>(sql: string): SqliteStatement<Row>;
29
+ close(): void;
30
+ }
@@ -0,0 +1,41 @@
1
+ import { SqliteTaskQueue } from "./sqlite-task-queue";
2
+ import { SqliteTimers } from "./sqlite-timers";
3
+ import type { EventProcessor } from "@yieldstar/core";
4
+ import type { Logger } from "pino";
5
+ import type { SqliteDriver } from "./sqlite-driver";
6
+
7
+ export class SqliteEventLoop {
8
+ taskQueue: SqliteTaskQueue;
9
+ timers: SqliteTimers;
10
+ private isRunning: boolean = false;
11
+
12
+ constructor(db: SqliteDriver) {
13
+ this.taskQueue = new SqliteTaskQueue(db);
14
+ this.timers = new SqliteTimers({ db, taskQueue: this.taskQueue });
15
+ }
16
+
17
+ start(params: { onNewEvent: EventProcessor; logger: Logger }) {
18
+ this.isRunning = true;
19
+ this.loop(params.onNewEvent, params.logger);
20
+ }
21
+
22
+ stop() {
23
+ this.isRunning = false;
24
+ }
25
+
26
+ private async loop(processEvent: EventProcessor, logger: Logger) {
27
+ if (!this.isRunning) return;
28
+
29
+ while (!this.taskQueue.isEmpty) {
30
+ const task = this.taskQueue.process();
31
+ if (task) {
32
+ await processEvent(task.event, logger);
33
+ this.taskQueue.remove(task.taskId);
34
+ }
35
+ }
36
+
37
+ this.timers.processTimers();
38
+
39
+ setTimeout(() => this.loop(processEvent, logger), 10);
40
+ }
41
+ }
@@ -0,0 +1,54 @@
1
+ import type { HeapClient } from "@yieldstar/core";
2
+ import { StepResponsesDao } from "./dao/step-response-dao";
3
+ import type { SqliteDriver } from "./sqlite-driver";
4
+
5
+ export class SqliteHeapClient implements HeapClient {
6
+ private stepResponsesDao: StepResponsesDao;
7
+
8
+ constructor(db: SqliteDriver) {
9
+ this.stepResponsesDao = new StepResponsesDao(db);
10
+ }
11
+
12
+ async getAllSteps() {
13
+ return this.stepResponsesDao.getAllSteps();
14
+ }
15
+
16
+ async deleteAll() {
17
+ this.stepResponsesDao.deleteAll();
18
+ }
19
+
20
+ async readStep(params: { executionId: string; stepKey: string }) {
21
+ const result = this.stepResponsesDao.getLatestStepResponse(
22
+ params.executionId,
23
+ params.stepKey
24
+ );
25
+
26
+ if (!result?.step_response) {
27
+ return null;
28
+ }
29
+
30
+ return {
31
+ stepResponseJson: result.step_response,
32
+ meta: {
33
+ attempt: result.step_attempt,
34
+ done: Boolean(result.step_done),
35
+ },
36
+ };
37
+ }
38
+
39
+ async writeStep(params: {
40
+ executionId: string;
41
+ stepKey: string;
42
+ stepAttempt: number;
43
+ stepDone: boolean;
44
+ stepResponseJson: string;
45
+ }) {
46
+ this.stepResponsesDao.insertStepResponse(
47
+ params.executionId,
48
+ params.stepKey,
49
+ params.stepAttempt,
50
+ params.stepDone,
51
+ params.stepResponseJson
52
+ );
53
+ }
54
+ }
@@ -0,0 +1,24 @@
1
+ import type { SchedulerClient, WorkflowEvent } from "@yieldstar/core";
2
+ import { SqliteTaskQueueClient } from "./sqlite-task-queue";
3
+ import { SqliteTimersClient } from "./sqlite-timers";
4
+
5
+ export class SqliteSchedulerClient implements SchedulerClient {
6
+ private taskQueue: SqliteTaskQueueClient;
7
+ private timersClient: SqliteTimersClient;
8
+
9
+ constructor(params: {
10
+ taskQueueClient: SqliteTaskQueueClient;
11
+ timersClient: SqliteTimersClient;
12
+ }) {
13
+ this.taskQueue = params.taskQueueClient;
14
+ this.timersClient = params.timersClient;
15
+ }
16
+
17
+ async requestWakeUp(event: WorkflowEvent, resumeIn?: number) {
18
+ if (resumeIn) {
19
+ this.timersClient.createTimer(event, resumeIn);
20
+ } else {
21
+ this.taskQueue.add(event);
22
+ }
23
+ }
24
+ }