@gobing-ai/ts-db 0.1.8 → 0.2.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/README.md CHANGED
@@ -1,30 +1,52 @@
1
1
  # @gobing-ai/ts-db
2
2
 
3
- Database abstraction layer adapter pattern with Drizzle ORM, generic CRUD DAOs, job queue persistence, and migration tooling. Supports Bun SQLite (in-memory and file-based) and Cloudflare D1.
3
+ A **drizzle-free database facade**: typed DAOs over Bun SQLite / Cloudflare D1, a small predicate query spec, single-source-of-truth tables, and migration tooling. Drizzle ORM powers it internally but never appears in your application code so the storage layer is swappable without touching call sites.
4
+
5
+ > **v0.2.0 is a breaking redesign.** The public `DbClient` interface and `adapter.getDb()` are removed; DAOs now take a `DbAdapter`; `where`/`orderBy` use a ts-db predicate spec instead of drizzle expressions. See [Migrating from 0.1.x](#migrating-from-01x).
4
6
 
5
7
  ## Overview
6
8
 
7
- `ts-db` provides a typed database layer decoupled from any specific storage engine. The `DbAdapter` interface abstracts Bun SQLite and Cloudflare D1 behind a common API, while `BaseDao`, `EntityDao`, and `QueueJobDao` provide progressively richer data access patterns.
9
+ Application code imports only `@gobing-ai/ts-db` never `drizzle-orm`. drizzle is an internal implementation detail, which keeps the storage engine swappable and the query surface small and auditable. Two tiers, your choice:
10
+
11
+ - **Structured tier** (`EntityDao`) — typed `create`/`createMany`/`upsert`/`findById`/`findBy`/`update`/`delete`/`list`/`listByCursor`/`count`, filtered by a small predicate spec (`{ col, op, value }`).
12
+ - **Raw tier** (`BaseDao`) — `query`/`one`/`tx` for table-agnostic access; ETL/analytics/reporting DAOs extend this directly.
13
+ - **String-SQL escape** (`adapter.exec`/`run`/`queryFirst`/`queryAll`) — for DDL and dynamic identifiers only.
8
14
 
9
15
  | Component | Purpose |
10
16
  |-----------|---------|
11
- | `DbAdapter` | Unified interface across Bun SQLite and D1 |
17
+ | `createDbAdapter` / `DbAdapter` | Construction + lifecycle + string-SQL escape; exposes an internal typed db to the DAO layer only |
12
18
  | `BunSqliteAdapter` | Bun SQLite implementation with statement caching and WAL pragmas |
13
19
  | `D1Adapter` | Cloudflare D1 implementation (no `@cloudflare/workers-types` dependency) |
14
- | `BaseDao` | Transaction + timestamp utilities for all DAOs |
15
- | `EntityDao` | Generic CRUD with soft delete, pagination, and `count()` |
20
+ | `BaseDao` | Raw tier `query`/`one`/`tx`, drizzle-free signatures |
21
+ | `EntityDao` | Structured CRUD predicate filters, soft delete, RETURNING, batch, upsert, cursor pagination, composite PK |
22
+ | `defineTable` | Single source of truth — one table → drizzle table + derived zod insert/select schemas (optional peers) |
23
+ | `Predicate` / `ListSpec` / `OrderTerm` | The drizzle-free query vocabulary |
16
24
  | `QueueJobDao` | Job queue persistence — `enqueue`, `claimReady`, `markCompleted`, `failExpiredJobs` |
17
25
  | `applyMigrations` | Drizzle migration runner (file-based + embedded fallback) |
18
- | `schema` | Reusable Drizzle column helpers + `queue_jobs` table definition |
26
+ | `schema` helpers | `standardColumns`, `appendOnlyColumns`, soft-delete columns |
19
27
  | `SpanContext` | Re-exported from `@gobing-ai/ts-runtime` for telemetry |
20
28
 
29
+ ### Optional peers (validation)
30
+
31
+ `defineTable`'s `insertSchema`/`selectSchema` and DAO validation require the **optional** peers `zod` and `drizzle-zod`. Install them only if you use validation; the DAOs and queries work without them.
32
+
33
+ ### Migrating from 0.1.x
34
+
35
+ - `adapter.getDb()` → `adapter.db` (internal typed db; rarely needed directly).
36
+ - `new SomeDao(adapter.getDb())` → `new SomeDao(adapter)` — DAOs now take the adapter, not a db handle.
37
+ - `EntityDao` PK arg accepts an array: `super(adapter, table, [table.id], 'name')` (enables composite PKs).
38
+ - `BaseDao.withTransaction` → `tx`.
39
+ - `list({ where: eq(col, v) })` → `list({ where: { col, op: 'eq', value: v } })`.
40
+ - `count(eq(col, v))` → `count({ col, op: 'eq', value: v })`.
41
+ - `create`/`update` use `RETURNING`, so returned rows include DB-defaulted columns.
42
+
21
43
  ## Architecture
22
44
 
23
45
  ```mermaid
24
46
  classDiagram
25
47
  class DbAdapter {
26
48
  <<interface>>
27
- +getDb() DbClient
49
+ +db InternalDb (internal)
28
50
  +exec(sql) void
29
51
  +run(sql, ...params) void
30
52
  +queryFirst(sql, ...params) T?
@@ -49,21 +71,22 @@ classDiagram
49
71
  <<abstract>>
50
72
  #db
51
73
  +now() number
52
- +withTransaction(fn) T
74
+ +tx(fn) T
75
+ +query(table, spec) T[]
76
+ +one(table, where) T?
53
77
  }
54
78
 
55
79
  class EntityDao {
56
80
  +create(data) TSelect
81
+ +createMany(rows) TSelect[]
82
+ +upsert(data, conflict) TSelect
57
83
  +findById(id) TSelect?
58
- +findAll() TSelect[]
59
84
  +update(id, data) TSelect?
60
85
  +delete(id, soft?) TSelect?
61
86
  +findBy(column, value) TSelect?
62
- +findAllBy(column, value) TSelect[]
63
- +list(opts) TSelect[]
87
+ +list(spec) TSelect[]
88
+ +listByCursor(spec) Page
64
89
  +count(where?) number
65
- #hasSoftDelete boolean
66
- #activeCondition SQL?
67
90
  }
68
91
 
69
92
  class QueueJobDao {
@@ -154,8 +177,8 @@ const users = sqliteTable('users', {
154
177
  });
155
178
 
156
179
  class UsersDao extends EntityDao<typeof users, typeof users.id> {
157
- constructor(db: DbClient) {
158
- super(db, users, users.id, 'users');
180
+ constructor(adapter: DbAdapter) {
181
+ super(adapter, users, [users.id], 'users');
159
182
  }
160
183
 
161
184
  async findByEmail(email: string) {
@@ -164,7 +187,7 @@ class UsersDao extends EntityDao<typeof users, typeof users.id> {
164
187
  }
165
188
 
166
189
  // Usage
167
- const dao = new UsersDao(adapter.getDb());
190
+ const dao = new UsersDao(adapter);
168
191
  const user = await dao.create({ id: 'u1', name: 'Alice', email: 'a@test.com' });
169
192
  const found = await dao.findById('u1');
170
193
  const updated = await dao.update('u1', { name: 'Alice Updated' });
@@ -180,7 +203,7 @@ await dao.delete('u1'); // soft delete if table has `inUsed` column
180
203
  ```ts
181
204
  import { QueueJobDao } from '@gobing-ai/ts-db';
182
205
 
183
- const queue = new QueueJobDao(adapter.getDb());
206
+ const queue = new QueueJobDao(adapter);
184
207
 
185
208
  // Enqueue
186
209
  const jobId = await queue.enqueue('send-email', { to: 'user@test.com' }, { maxRetries: 5 });
@@ -275,13 +298,13 @@ export const todos = sqliteTable('todos', {
275
298
 
276
299
  ```ts
277
300
  // src/todos-dao.ts
278
- import type { DbClient } from '@gobing-ai/ts-db';
301
+ import type { DbAdapter } from '@gobing-ai/ts-db';
279
302
  import { EntityDao } from '@gobing-ai/ts-db';
280
303
  import { todos } from './schema';
281
304
 
282
305
  export class TodosDao extends EntityDao<typeof todos, typeof todos.id> {
283
- constructor(db: DbClient) {
284
- super(db, todos, todos.id, 'todos');
306
+ constructor(adapter: DbAdapter) {
307
+ super(adapter, todos, [todos.id], 'todos');
285
308
  }
286
309
 
287
310
  async findPending() {
@@ -304,7 +327,7 @@ import { TodosDao } from './todos-dao';
304
327
  const adapter = await createDbAdapter({ driver: 'bun-sqlite', url: ':memory:' });
305
328
  await applyMigrations(adapter);
306
329
 
307
- const todos = new TodosDao(adapter.getDb());
330
+ const todos = new TodosDao(adapter);
308
331
 
309
332
  await todos.create({ id: '1', title: 'Learn ts-db' });
310
333
  await todos.create({ id: '2', title: 'Build something' });
package/dist/adapter.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { BunSQLiteDatabase } from 'drizzle-orm/bun-sqlite';
2
+ import type { DrizzleD1Database } from 'drizzle-orm/d1';
1
3
  /**
2
4
  * Minimal D1 binding interface — avoids depending on @cloudflare/workers-types.
3
5
  */
@@ -6,61 +8,39 @@ interface D1Binding {
6
8
  exec(sql: string): Promise<void>;
7
9
  }
8
10
  /**
9
- * Generic database table descriptor carrying select and insert type info.
11
+ * Internal typed drizzle database handle.
12
+ *
13
+ * This is the REAL drizzle database (bun:sqlite or D1 flavour), fully typed.
14
+ * It is `@internal` — ts-db's DAO base classes use it to build queries, but it
15
+ * is never part of the public API. Consumers depend only on the ts-db facade
16
+ * (DAOs, the predicate spec), never on drizzle types directly. (G1)
17
+ *
18
+ * @internal
10
19
  */
11
- export interface DbTable<TSelect, TInsert = TSelect> {
12
- readonly $inferSelect: TSelect;
13
- readonly $inferInsert: TInsert;
14
- }
15
- type DbInsertBuilder<TTable extends DbTable<unknown, unknown>> = {
16
- values(values: TTable['$inferInsert'] | TTable['$inferInsert'][]): PromiseLike<unknown>;
17
- };
18
- interface DbSelectWhereResult<TTable extends DbTable<unknown, unknown>> extends PromiseLike<TTable['$inferSelect'][]> {
19
- limit(value: number): DbSelectWhereResult<TTable>;
20
- offset(value: number): DbSelectWhereResult<TTable>;
21
- orderBy(column: unknown): DbSelectWhereResult<TTable>;
22
- }
23
- type DbSelectFromResult<TTable extends DbTable<unknown, unknown>> = DbSelectWhereResult<TTable> & {
24
- where(condition: unknown): DbSelectWhereResult<TTable>;
25
- };
26
- type DbSelectBuilder = {
27
- from<TTable extends DbTable<unknown, unknown>>(table: TTable): DbSelectFromResult<TTable>;
28
- };
29
- type DbProjectionSelectBuilder<TProjection> = {
30
- from(table: DbTable<unknown, unknown>): PromiseLike<TProjection[]> & {
31
- where(condition: unknown): PromiseLike<TProjection[]>;
32
- };
33
- };
34
- interface DbUpdateResult {
35
- changes: number;
36
- }
37
- interface DbUpdateBuilder<TTable extends DbTable<unknown, unknown>> {
38
- set(values: Partial<TTable['$inferInsert']>): {
39
- where(condition: unknown): PromiseLike<DbUpdateResult>;
40
- };
41
- }
42
- /**
43
- * Abstract database client with insert/select/update/delete query builders.
44
- */
45
- export interface DbClient {
46
- insert<TTable extends DbTable<unknown, unknown>>(table: TTable): DbInsertBuilder<TTable>;
47
- select(): DbSelectBuilder;
48
- select<TProjection>(projection: Record<string, unknown>): DbProjectionSelectBuilder<TProjection>;
49
- update<TTable extends DbTable<unknown, unknown>>(table: TTable): DbUpdateBuilder<TTable>;
50
- delete<TTable extends DbTable<unknown, unknown>>(table: TTable): {
51
- where(condition: unknown): PromiseLike<DbUpdateResult>;
52
- };
53
- }
20
+ export type InternalDb = BunSQLiteDatabase<Record<string, unknown>> | DrizzleD1Database<Record<string, unknown>>;
54
21
  /**
55
- * Database adapter providing a unified client, raw SQL exec, and lifecycle management.
22
+ * Database adapter: construction, lifecycle, the internal typed drizzle db, and a
23
+ * raw string-SQL escape for DDL / dynamic identifiers.
24
+ *
25
+ * The internal drizzle db (`db`) is exposed only to ts-db's own DAO layer; the
26
+ * string-SQL methods are the sole raw escape, intended for DDL and dynamic
27
+ * identifiers and gated to DAO files by a consumer-side lint rule.
56
28
  */
57
29
  export interface DbAdapter {
58
- getDb(): DbClient;
30
+ /**
31
+ * The internal typed drizzle database. ts-db DAO layer only — not public API.
32
+ * @internal
33
+ */
34
+ readonly db: InternalDb;
35
+ /** Run a raw SQL statement with no parameters (DDL). */
59
36
  exec(sql: string): Promise<void>;
60
37
  /** Parameterized write (INSERT/UPDATE/DELETE) that returns no rows. */
61
38
  run(sql: string, ...params: unknown[]): Promise<void>;
39
+ /** Parameterized read returning the first row, or undefined. */
62
40
  queryFirst<T>(sql: string, ...params: unknown[]): Promise<T | undefined>;
41
+ /** Parameterized read returning all rows. */
63
42
  queryAll<T>(sql: string, ...params: unknown[]): Promise<T[]>;
43
+ /** Close the underlying connection. */
64
44
  close(): void;
65
45
  }
66
46
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAExD;;GAEG;AACH,UAAU,SAAS;IACf,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,UAAU,GAAG,iBAAiB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,iBAAiB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAEjH;;;;;;;GAOG;AACH,MAAM,WAAW,SAAS;IACtB;;;OAGG;IACH,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAC;IACxB,wDAAwD;IACxD,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,uEAAuE;IACvE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtD,gEAAgE;IAChE,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC;IACzE,6CAA6C;IAC7C,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAC7D,uCAAuC;IACvC,KAAK,IAAI,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,MAAM,eAAe,GACrB;IACI,MAAM,EAAE,YAAY,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE;QACN,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACL,GACD;IAAE,MAAM,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,SAAS,CAAA;CAAE,CAAC;AAE3C;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,CAcjF"}
@@ -1,5 +1,5 @@
1
1
  import { type BunSQLiteDatabase } from 'drizzle-orm/bun-sqlite';
2
- import type { DbAdapter, DbClient } from '../adapter';
2
+ import type { DbAdapter, InternalDb } from '../adapter';
3
3
  import * as schema from '../schema/index';
4
4
  /**
5
5
  * Configuration options for the bun:sqlite adapter (path, pragmas).
@@ -28,7 +28,8 @@ export declare class BunSqliteAdapter implements DbAdapter {
28
28
  private readonly stmtCache;
29
29
  private getStatement;
30
30
  constructor(options?: BunSqliteOptions);
31
- getDb(): DbClient;
31
+ /** The internal typed drizzle database (ts-db DAO layer + migrations only). */
32
+ get db(): InternalDb;
32
33
  /** Returns the underlying drizzle instance for migration operations. */
33
34
  getDrizzleDb(): BunSQLiteDatabase<typeof schema>;
34
35
  exec(sql: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"bun-sqlite.d.ts","sourceRoot":"","sources":["../../src/adapters/bun-sqlite.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAAW,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtD,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAS1C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE;QACN,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACL;AAUD;;GAEG;AACH,qBAAa,gBAAiB,YAAW,SAAS;IAC9C,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,SAAS,CAAmC;IACpD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;IAEpE,OAAO,CAAC,YAAY;gBAUR,OAAO,CAAC,EAAE,gBAAgB;IAkBtC,KAAK,IAAI,QAAQ;IAIjB,wEAAwE;IACxE,YAAY,IAAI,iBAAiB,CAAC,OAAO,MAAM,CAAC;IAI1C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrD,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAKxE,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAKlE,KAAK,IAAI,IAAI;CAGhB"}
1
+ {"version":3,"file":"bun-sqlite.d.ts","sourceRoot":"","sources":["../../src/adapters/bun-sqlite.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,iBAAiB,EAAW,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAS1C;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC7B,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kDAAkD;IAClD,OAAO,CAAC,EAAE;QACN,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,WAAW,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACL;AAUD;;GAEG;AACH,qBAAa,gBAAiB,YAAW,SAAS;IAC9C,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,SAAS,CAAmC;IACpD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;IAEpE,OAAO,CAAC,YAAY;gBAUR,OAAO,CAAC,EAAE,gBAAgB;IAkBtC,+EAA+E;IAC/E,IAAI,EAAE,IAAI,UAAU,CAEnB;IAED,wEAAwE;IACxE,YAAY,IAAI,iBAAiB,CAAC,OAAO,MAAM,CAAC;IAI1C,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrD,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAKxE,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAKlE,KAAK,IAAI,IAAI;CAGhB"}
@@ -42,7 +42,8 @@ export class BunSqliteAdapter {
42
42
  this.sqlite.run(pragmas.foreignKeys);
43
43
  this.drizzleDb = drizzle({ client: this.sqlite, schema });
44
44
  }
45
- getDb() {
45
+ /** The internal typed drizzle database (ts-db DAO layer + migrations only). */
46
+ get db() {
46
47
  return this.drizzleDb;
47
48
  }
48
49
  /** Returns the underlying drizzle instance for migration operations. */
@@ -1,4 +1,6 @@
1
- import type { DbAdapter, DbClient } from '../adapter';
1
+ import { type DrizzleD1Database } from 'drizzle-orm/d1';
2
+ import type { DbAdapter, InternalDb } from '../adapter';
3
+ import * as schema from '../schema/index';
2
4
  /**
3
5
  * Minimal D1 binding interface — avoids depending on @cloudflare/workers-types.
4
6
  */
@@ -35,7 +37,10 @@ export declare class D1Adapter implements DbAdapter {
35
37
  private binding;
36
38
  private drizzleDb;
37
39
  constructor(binding: D1Binding);
38
- getDb(): DbClient;
40
+ /** The internal typed drizzle database (ts-db DAO layer only). */
41
+ get db(): InternalDb;
42
+ /** Returns the underlying drizzle instance for migration operations. */
43
+ getDrizzleDb(): DrizzleD1Database<typeof schema>;
39
44
  /** Returns the non-mutating binding for advanced direct D1 calls. */
40
45
  getBinding(): D1Binding;
41
46
  exec(sql: string): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"d1.d.ts","sourceRoot":"","sources":["../../src/adapters/d1.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGtD;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QAClB,IAAI,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC;QAC7C,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC/B,GAAG,CAAC,IAAI,OAAO,CAAC;YAAE,OAAO,EAAE,OAAO,EAAE,CAAC;YAAC,OAAO,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;KAC7D,CAAC;IACF,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,UAAU,gBAAgB;IACtB,GAAG,CAAC,CAAC,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACtD,GAAG,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACzD,GAAG,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACvB,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CAClC;AAED;;;;;GAKG;AACH,qBAAa,SAAU,YAAW,SAAS;IACvC,OAAO,CAAC,OAAO,CAAY;IAC3B,OAAO,CAAC,SAAS,CAAmC;gBAExC,OAAO,EAAE,SAAS;IAK9B,KAAK,IAAI,QAAQ;IAIjB,qEAAqE;IACrE,UAAU,IAAI,SAAS;IAIjB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrD,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAQxE,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAOlE,KAAK,IAAI,IAAI;CAGhB"}
1
+ {"version":3,"file":"d1.d.ts","sourceRoot":"","sources":["../../src/adapters/d1.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,iBAAiB,EAAW,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACxD,OAAO,KAAK,MAAM,MAAM,iBAAiB,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,SAAS;IACtB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG;QAClB,IAAI,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,gBAAgB,CAAC;QAC7C,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;QAC/B,GAAG,CAAC,IAAI,OAAO,CAAC;YAAE,OAAO,EAAE,OAAO,EAAE,CAAC;YAAC,OAAO,EAAE,OAAO,CAAA;SAAE,CAAC,CAAC;KAC7D,CAAC;IACF,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED,UAAU,gBAAgB;IACtB,GAAG,CAAC,CAAC,KAAK,OAAO,CAAC;QAAE,OAAO,EAAE,CAAC,EAAE,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACtD,GAAG,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IACzD,GAAG,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IACvB,KAAK,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;CAClC;AAED;;;;;GAKG;AACH,qBAAa,SAAU,YAAW,SAAS;IACvC,OAAO,CAAC,OAAO,CAAY;IAC3B,OAAO,CAAC,SAAS,CAAmC;gBAExC,OAAO,EAAE,SAAS;IAK9B,kEAAkE;IAClE,IAAI,EAAE,IAAI,UAAU,CAEnB;IAED,wEAAwE;IACxE,YAAY,IAAI,iBAAiB,CAAC,OAAO,MAAM,CAAC;IAIhD,qEAAqE;IACrE,UAAU,IAAI,SAAS;IAIjB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMrD,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;IAQxE,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAOlE,KAAK,IAAI,IAAI;CAGhB"}
@@ -13,7 +13,12 @@ export class D1Adapter {
13
13
  this.binding = binding;
14
14
  this.drizzleDb = drizzle(this.binding, { schema });
15
15
  }
16
- getDb() {
16
+ /** The internal typed drizzle database (ts-db DAO layer only). */
17
+ get db() {
18
+ return this.drizzleDb;
19
+ }
20
+ /** Returns the underlying drizzle instance for migration operations. */
21
+ getDrizzleDb() {
17
22
  return this.drizzleDb;
18
23
  }
19
24
  /** Returns the non-mutating binding for advanced direct D1 calls. */
@@ -1,27 +1,47 @@
1
- import type { DbClient } from './adapter';
1
+ import type { SQLiteTable } from 'drizzle-orm/sqlite-core';
2
+ import type { DbAdapter, InternalDb } from './adapter';
3
+ import { type ListSpec, type Predicate } from './query-spec';
2
4
  /**
3
- * Abstract base DAO providing transaction and timestamp utilities to all entity DAOs.
5
+ * A transaction-scoped handle passed to {@link BaseDao.tx} callbacks.
6
+ *
7
+ * Drizzle-free by design — exposes the same internal db shape DAOs use, so a
8
+ * transactional block can run the same structured/raw operations. (G3)
9
+ *
10
+ * @internal
11
+ */
12
+ export type TxHandle = InternalDb;
13
+ /**
14
+ * Abstract base DAO — the RAW tier of the ts-db facade.
15
+ *
16
+ * Owns the adapter and provides generic, table-agnostic data access:
17
+ * transactions and parameterized queries expressed through the ts-db predicate
18
+ * spec (never drizzle's `sql` tag). ETL / analytics / reporting DAOs extend this
19
+ * directly; {@link EntityDao} extends it to add typed CRUD over a single table.
20
+ *
21
+ * All signatures are ts-db's own vocabulary — drizzle never leaks to consumers. (G1)
4
22
  */
5
23
  export declare abstract class BaseDao {
6
- protected readonly db: DbClient;
7
- /**
8
- * DB transaction utility for subclasses.
9
- *
10
- * Constructor is `protected` instantiate through concrete DAO subclasses,
11
- * not BaseDao directly. Tests must declare an explicit public constructor
12
- * that calls `super(db)` to expose the protected constructor publicly.
13
- */
14
- protected constructor(db: DbClient);
24
+ protected readonly adapter: DbAdapter;
25
+ protected constructor(adapter: DbAdapter);
26
+ /** The internal typed drizzle db. Subclasses use it to build queries. @internal */
27
+ protected get db(): InternalDb;
28
+ /** Current timestamp in milliseconds (audit columns, etc.). */
15
29
  protected now(): number;
16
30
  /**
17
31
  * Execute a function within a database transaction.
18
32
  *
19
- * Works uniformly on both D1 (async) and bun:sqlite (sync wrapped in promise).
20
- * The callback receives a transaction-scoped DbClient.
33
+ * Works uniformly on bun:sqlite (sync, wrapped in a promise) and D1 (async).
34
+ * The callback receives a transaction-scoped db handle.
35
+ */
36
+ protected tx<T>(fn: (tx: TxHandle) => Promise<T>): Promise<T>;
37
+ /**
38
+ * Run a SELECT against a table, filtered/ordered/paged by a {@link ListSpec}.
21
39
  *
22
- * @param fn - Function to execute within the transaction.
23
- * @returns The return value of `fn`.
40
+ * The raw-tier read primitive: drizzle-free input, typed rows out. Subclasses
41
+ * pass their table; `EntityDao` builds on this for `list`.
24
42
  */
25
- protected withTransaction<T>(fn: (tx: DbClient) => Promise<T>): Promise<T>;
43
+ protected query<T>(table: SQLiteTable, spec?: ListSpec): Promise<T[]>;
44
+ /** Run a SELECT and return the first matching row, or undefined. */
45
+ protected one<T>(table: SQLiteTable, where: Predicate): Promise<T | undefined>;
26
46
  }
27
47
  //# sourceMappingURL=base-dao.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-dao.d.ts","sourceRoot":"","sources":["../src/base-dao.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAE1C;;GAEG;AACH,8BAAsB,OAAO;IAQH,SAAS,CAAC,QAAQ,CAAC,EAAE,EAAE,QAAQ;IAPrD;;;;;;OAMG;IACH,SAAS,aAAgC,EAAE,EAAE,QAAQ;IAErD,SAAS,CAAC,GAAG,IAAI,MAAM;IAIvB;;;;;;;;OAQG;cACa,eAAe,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAQnF"}
1
+ {"version":3,"file":"base-dao.d.ts","sourceRoot":"","sources":["../src/base-dao.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvD,OAAO,EAAoC,KAAK,QAAQ,EAAE,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAE/F;;;;;;;GAOG;AACH,MAAM,MAAM,QAAQ,GAAG,UAAU,CAAC;AAElC;;;;;;;;;GASG;AACH,8BAAsB,OAAO;IACH,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,SAAS;IAA3D,SAAS,aAAgC,OAAO,EAAE,SAAS;IAE3D,mFAAmF;IACnF,SAAS,KAAK,EAAE,IAAI,UAAU,CAE7B;IAED,+DAA+D;IAC/D,SAAS,CAAC,GAAG,IAAI,MAAM;IAIvB;;;;;OAKG;cACa,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAMnE;;;;;OAKG;cACa,KAAK,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,GAAE,QAAa,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC;IAkB/E,oEAAoE;cACpD,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC;CAIvF"}
package/dist/base-dao.js CHANGED
@@ -1,34 +1,60 @@
1
+ import { compileOrderBy, compilePredicate } from './query-spec.js';
1
2
  /**
2
- * Abstract base DAO providing transaction and timestamp utilities to all entity DAOs.
3
+ * Abstract base DAO the RAW tier of the ts-db facade.
4
+ *
5
+ * Owns the adapter and provides generic, table-agnostic data access:
6
+ * transactions and parameterized queries expressed through the ts-db predicate
7
+ * spec (never drizzle's `sql` tag). ETL / analytics / reporting DAOs extend this
8
+ * directly; {@link EntityDao} extends it to add typed CRUD over a single table.
9
+ *
10
+ * All signatures are ts-db's own vocabulary — drizzle never leaks to consumers. (G1)
3
11
  */
4
12
  export class BaseDao {
5
- db;
6
- /**
7
- * DB transaction utility for subclasses.
8
- *
9
- * Constructor is `protected` instantiate through concrete DAO subclasses,
10
- * not BaseDao directly. Tests must declare an explicit public constructor
11
- * that calls `super(db)` to expose the protected constructor publicly.
12
- */
13
- constructor(db) {
14
- this.db = db;
13
+ adapter;
14
+ constructor(adapter) {
15
+ this.adapter = adapter;
16
+ }
17
+ /** The internal typed drizzle db. Subclasses use it to build queries. @internal */
18
+ get db() {
19
+ return this.adapter.db;
15
20
  }
21
+ /** Current timestamp in milliseconds (audit columns, etc.). */
16
22
  now() {
17
23
  return Date.now();
18
24
  }
19
25
  /**
20
26
  * Execute a function within a database transaction.
21
27
  *
22
- * Works uniformly on both D1 (async) and bun:sqlite (sync wrapped in promise).
23
- * The callback receives a transaction-scoped DbClient.
28
+ * Works uniformly on bun:sqlite (sync, wrapped in a promise) and D1 (async).
29
+ * The callback receives a transaction-scoped db handle.
30
+ */
31
+ async tx(fn) {
32
+ return this.db.transaction((tx) => fn(tx));
33
+ }
34
+ /**
35
+ * Run a SELECT against a table, filtered/ordered/paged by a {@link ListSpec}.
24
36
  *
25
- * @param fn - Function to execute within the transaction.
26
- * @returns The return value of `fn`.
37
+ * The raw-tier read primitive: drizzle-free input, typed rows out. Subclasses
38
+ * pass their table; `EntityDao` builds on this for `list`.
27
39
  */
28
- async withTransaction(fn) {
29
- // Drizzle's .transaction() works on both backends:
30
- // - bun:sqlite: sync wrapped in a promise
31
- // - D1: native async
32
- return this.db.transaction(async (tx) => fn(tx));
40
+ async query(table, spec = {}) {
41
+ const condition = spec.where ? compilePredicate(spec.where) : undefined;
42
+ const order = spec.orderBy ? compileOrderBy(spec.orderBy) : [];
43
+ // drizzle's fluent builder is internal here; the public input is the spec.
44
+ let q = this.db.select().from(table);
45
+ if (condition)
46
+ q = q.where(condition);
47
+ if (order.length > 0)
48
+ q = q.orderBy(...order);
49
+ if (spec.limit !== undefined)
50
+ q = q.limit(spec.limit);
51
+ if (spec.offset !== undefined)
52
+ q = q.offset(spec.offset);
53
+ return (await q) ?? [];
54
+ }
55
+ /** Run a SELECT and return the first matching row, or undefined. */
56
+ async one(table, where) {
57
+ const rows = await this.query(table, { where, limit: 1 });
58
+ return rows[0];
33
59
  }
34
60
  }
@@ -0,0 +1,40 @@
1
+ import { type SQLiteColumnBuilderBase, sqliteTable } from 'drizzle-orm/sqlite-core';
2
+ import type { ZodType } from 'zod';
3
+ /**
4
+ * A table definition bundled with its drizzle-zod validation schemas.
5
+ *
6
+ * The single source of truth (G2): one table authored once yields the drizzle
7
+ * table (for queries/migrations) plus insert/select zod schemas (for boundary
8
+ * validation), with no parallel re-authoring.
9
+ *
10
+ * The zod schemas are derived lazily — only materialised the first time they are
11
+ * read — so a consumer that only needs the table pays nothing extra.
12
+ *
13
+ * `defineTable`, `insertSchema`, and `selectSchema` require the optional peers
14
+ * `zod` and `drizzle-zod`. Consumers that never validate need not install them
15
+ * and simply use `createDbAdapter` + raw `sqliteTable` + the column helpers.
16
+ */
17
+ export interface DefinedTable<TTable> {
18
+ /** The underlying drizzle table — pass to DAOs, migrations, queries. */
19
+ readonly table: TTable;
20
+ /** Zod schema validating a row for insertion. */
21
+ readonly insertSchema: ZodType;
22
+ /** Zod schema validating a selected row. */
23
+ readonly selectSchema: ZodType;
24
+ }
25
+ /**
26
+ * Define a SQLite table and derive its validation schemas in one place (G2).
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * export const users = defineTable('users', {
31
+ * id: text('id').primaryKey(),
32
+ * email: text('email').notNull().unique(),
33
+ * ...standardColumns,
34
+ * });
35
+ * users.table // drizzle table for DAOs/migrations
36
+ * users.insertSchema // zod schema derived from the table
37
+ * ```
38
+ */
39
+ export declare function defineTable<TName extends string, TColumns extends Record<string, SQLiteColumnBuilderBase>>(name: TName, columns: TColumns): DefinedTable<ReturnType<typeof sqliteTable<TName, TColumns>>>;
40
+ //# sourceMappingURL=define-table.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-table.d.ts","sourceRoot":"","sources":["../src/define-table.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,uBAAuB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAEpF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,KAAK,CAAC;AAEnC;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY,CAAC,MAAM;IAChC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,iDAAiD;IACjD,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;IAC/B,4CAA4C;IAC5C,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC;CAClC;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,KAAK,SAAS,MAAM,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,uBAAuB,CAAC,EACtG,IAAI,EAAE,KAAK,EACX,OAAO,EAAE,QAAQ,GAClB,YAAY,CAAC,UAAU,CAAC,OAAO,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,CAqB/D"}
@@ -0,0 +1,36 @@
1
+ import { sqliteTable } from 'drizzle-orm/sqlite-core';
2
+ import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
3
+ /**
4
+ * Define a SQLite table and derive its validation schemas in one place (G2).
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * export const users = defineTable('users', {
9
+ * id: text('id').primaryKey(),
10
+ * email: text('email').notNull().unique(),
11
+ * ...standardColumns,
12
+ * });
13
+ * users.table // drizzle table for DAOs/migrations
14
+ * users.insertSchema // zod schema derived from the table
15
+ * ```
16
+ */
17
+ export function defineTable(name, columns) {
18
+ const table = sqliteTable(name, columns);
19
+ let insert;
20
+ let select;
21
+ return {
22
+ table,
23
+ get insertSchema() {
24
+ if (insert === undefined) {
25
+ insert = createInsertSchema(table);
26
+ }
27
+ return insert;
28
+ },
29
+ get selectSchema() {
30
+ if (select === undefined) {
31
+ select = createSelectSchema(table);
32
+ }
33
+ return select;
34
+ },
35
+ };
36
+ }