@gobing-ai/ts-db 0.2.2 → 0.2.3

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
@@ -2,7 +2,7 @@
2
2
 
3
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
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).
5
+ > **v0.2.3 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).
6
6
 
7
7
  ## Overview
8
8
 
@@ -15,7 +15,7 @@ Application code imports only `@gobing-ai/ts-db` — never `drizzle-orm`. drizzl
15
15
  | Component | Purpose |
16
16
  |-----------|---------|
17
17
  | `createDbAdapter` / `DbAdapter` | Construction + lifecycle + string-SQL escape; exposes an internal typed db to the DAO layer only |
18
- | `BunSqliteAdapter` | Bun SQLite implementation with statement caching and WAL pragmas |
18
+ | `BunSqliteAdapter` | Bun SQLite implementation with statement caching and WAL pragmas (`@gobing-ai/ts-db/bun-sqlite`) |
19
19
  | `D1Adapter` | Cloudflare D1 implementation (no `@cloudflare/workers-types` dependency) |
20
20
  | `BaseDao` | Raw tier — `query`/`one`/`tx`, drizzle-free signatures |
21
21
  | `EntityDao` | Structured CRUD — predicate filters, soft delete, RETURNING, batch, upsert, cursor pagination, composite PK |
@@ -236,7 +236,8 @@ const stats = await queue.getStats();
236
236
  ### Migrations
237
237
 
238
238
  ```ts
239
- import { BunSqliteAdapter, applyMigrations } from '@gobing-ai/ts-db';
239
+ import { applyMigrations } from '@gobing-ai/ts-db';
240
+ import { BunSqliteAdapter } from '@gobing-ai/ts-db/bun-sqlite';
240
241
 
241
242
  const adapter = new BunSqliteAdapter({ databaseUrl: './data/app.db' });
242
243
 
@@ -1,6 +1,6 @@
1
1
  import { type BunSQLiteDatabase } from 'drizzle-orm/bun-sqlite';
2
2
  import type { DbAdapter, InternalDb } from '../adapter';
3
- import * as schema from '../schema/index';
3
+ import * as schema from '../schema/runtime';
4
4
  /**
5
5
  * Configuration options for the bun:sqlite adapter (path, pragmas).
6
6
  */
@@ -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,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"}
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,mBAAmB,CAAC;AAS5C;;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"}
@@ -1,7 +1,7 @@
1
- import { Database } from 'bun:sqlite';
2
1
  import { isAbsolute, resolve } from 'node:path';
2
+ import { Database } from '@gobing-ai/ts-runtime/bun-sqlite';
3
3
  import { drizzle } from 'drizzle-orm/bun-sqlite';
4
- import * as schema from '../schema/index.js';
4
+ import * as schema from '../schema/runtime.js';
5
5
  const DEFAULT_PRAGMAS = {
6
6
  journalMode: 'PRAGMA journal_mode = WAL',
7
7
  synchronous: 'PRAGMA synchronous = NORMAL',
@@ -1,6 +1,6 @@
1
1
  import { type DrizzleD1Database } from 'drizzle-orm/d1';
2
2
  import type { DbAdapter, InternalDb } from '../adapter';
3
- import * as schema from '../schema/index';
3
+ import * as schema from '../schema/runtime';
4
4
  /**
5
5
  * Minimal D1 binding interface — avoids depending on @cloudflare/workers-types.
6
6
  */
@@ -1 +1 @@
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"}
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,mBAAmB,CAAC;AAE5C;;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"}
@@ -1,5 +1,5 @@
1
1
  import { drizzle } from 'drizzle-orm/d1';
2
- import * as schema from '../schema/index.js';
2
+ import * as schema from '../schema/runtime.js';
3
3
  /**
4
4
  * Cloudflare D1 database adapter.
5
5
  *
@@ -138,6 +138,7 @@ export declare class EntityDao<TTable extends EntityTable, TPK extends SQLiteCol
138
138
  rows: TTable['$inferSelect'][];
139
139
  nextCursor?: string | number;
140
140
  }>;
141
+ private resultKeyForColumn;
141
142
  /** Count records matching an optional predicate. */
142
143
  count(where?: Predicate, includeDeleted?: boolean): Promise<number>;
143
144
  /** Combine a caller predicate with the soft-delete active filter. */
@@ -1 +1 @@
1
- {"version":3,"file":"entity-dao.d.ts","sourceRoot":"","sources":["../src/entity-dao.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"entity-dao.d.ts","sourceRoot":"","sources":["../src/entity-dao.ts"],"names":[],"mappings":"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"}
@@ -166,11 +166,17 @@ export class EntityDao extends BaseDao {
166
166
  limit: spec.limit,
167
167
  });
168
168
  const last = rows[rows.length - 1];
169
- const nextCursor = rows.length === spec.limit && last
170
- ? last[spec.cursorColumn.name]
171
- : undefined;
169
+ const cursorResultKey = this.resultKeyForColumn(spec.cursorColumn);
170
+ const nextCursor = rows.length === spec.limit && last ? last[cursorResultKey] : undefined;
172
171
  return nextCursor !== undefined ? { rows, nextCursor } : { rows };
173
172
  }
173
+ resultKeyForColumn(column) {
174
+ for (const [key, value] of Object.entries(this.table)) {
175
+ if (value === column)
176
+ return key;
177
+ }
178
+ return column.name;
179
+ }
174
180
  /** Count records matching an optional predicate. */
175
181
  async count(where, includeDeleted = false) {
176
182
  const condition = this.withActive(where, includeDeleted);
package/dist/index.d.ts CHANGED
@@ -1,8 +1,6 @@
1
1
  export { createDbAdapter, type DbAdapter, type DbAdapterConfig, type InternalDb } from './adapter';
2
- export { BunSqliteAdapter, type BunSqliteOptions } from './adapters/bun-sqlite';
3
2
  export { D1Adapter } from './adapters/d1';
4
3
  export { BaseDao, type TxHandle } from './base-dao';
5
- export { type DefinedTable, defineTable } from './define-table';
6
4
  export { type EmbeddedMigration, embeddedMigrations } from './embedded-migrations';
7
5
  export { type CursorListSpec, type DaoValidator, EntityDao, type EntityDaoOptions, type EntityListSpec, type EntityTable, type PKColumn, type PKValue, type SoftDeletableTable, } from './entity-dao';
8
6
  export { applyMigrations, type MigrationOptions } from './migrate';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACnG,OAAO,EAAE,gBAAgB,EAAE,KAAK,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,KAAK,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,KAAK,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,KAAK,kBAAkB,GAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EACH,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,cAAc,EACd,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,SAAS,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,EACH,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,EACpB,kCAAkC,EAClC,YAAY,EACZ,eAAe,EACf,6BAA6B,GAChC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,WAAW,CAAC;AACnG,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEpD,OAAO,EAAE,KAAK,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AACnF,OAAO,EACH,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,SAAS,EACT,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,WAAW,EAChB,KAAK,QAAQ,EACb,KAAK,OAAO,EACZ,KAAK,kBAAkB,GAC1B,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,eAAe,EAAE,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AACnE,OAAO,EACH,KAAK,MAAM,EACX,KAAK,YAAY,EACjB,cAAc,EACd,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,SAAS,GACjB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,WAAW,EAAE,KAAK,cAAc,EAAE,KAAK,UAAU,EAAE,MAAM,iBAAiB,CAAC;AACpF,OAAO,EACH,iBAAiB,EACjB,sBAAsB,EACtB,oBAAoB,EACpB,kCAAkC,EAClC,YAAY,EACZ,eAAe,EACf,6BAA6B,GAChC,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,6 @@
1
1
  export { createDbAdapter } from './adapter.js';
2
- export { BunSqliteAdapter } from './adapters/bun-sqlite.js';
3
2
  export { D1Adapter } from './adapters/d1.js';
4
3
  export { BaseDao } from './base-dao.js';
5
- export { defineTable } from './define-table.js';
6
4
  export { embeddedMigrations } from './embedded-migrations.js';
7
5
  export { EntityDao, } from './entity-dao.js';
8
6
  export { applyMigrations } from './migrate.js';
@@ -0,0 +1,19 @@
1
+ import { type SQLiteTable } from 'drizzle-orm/sqlite-core';
2
+ /**
3
+ * Generate a `CREATE TABLE IF NOT EXISTS` DDL statement from a Drizzle SQLite table.
4
+ *
5
+ * Uses `getTableConfig` (drizzle-orm runtime introspection) to extract columns,
6
+ * types, constraints, and foreign keys — no drizzle-kit CLI required.
7
+ *
8
+ * The output is deterministic: columns are emitted in definition order, identifiers
9
+ * are double-quoted, and table constraints follow column definitions.
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * const users = sqliteTable('users', { id: text('id').primaryKey() });
14
+ * const ddl = generateCreateTableSql(users);
15
+ * // CREATE TABLE IF NOT EXISTS "users" ("id" text PRIMARY KEY NOT NULL)
16
+ * ```
17
+ */
18
+ export declare function generateCreateTableSql(table: SQLiteTable): string;
19
+ //# sourceMappingURL=ddl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ddl.d.ts","sourceRoot":"","sources":["../../src/schema/ddl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAkB,KAAK,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAuE3E;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAsFjE"}
@@ -0,0 +1,156 @@
1
+ import { getTableConfig } from 'drizzle-orm/sqlite-core';
2
+ /**
3
+ * Quote an identifier for use in SQL (double-quoted for SQLite compatibility).
4
+ */
5
+ function quoteIdent(name) {
6
+ return `"${name.replace(/"/g, '""')}"`;
7
+ }
8
+ /**
9
+ * Extract the SQL string from a drizzle-orm SQL expression by walking its
10
+ * internal `queryChunks` — StringChunk values plus Param placeholders.
11
+ */
12
+ function sqlToString(chunks) {
13
+ return chunks
14
+ .map((chunk) => {
15
+ // StringChunk — the literal SQL fragment
16
+ if ('value' in chunk && typeof chunk.value === 'string') {
17
+ return chunk.value;
18
+ }
19
+ // Param — use the input value if available
20
+ if ('input' in chunk && chunk.input !== undefined) {
21
+ return String(chunk.input);
22
+ }
23
+ return String(chunk.value ?? '?');
24
+ })
25
+ .join('');
26
+ }
27
+ /**
28
+ * Map a column default value to its SQL literal representation.
29
+ *
30
+ * drizzle-orm defaults can be:
31
+ * - primitives: number, string, boolean, null
32
+ * - SQL expressions (sql\`...\` template results)
33
+ * - undefined (no default, or runtime-only $defaultFn)
34
+ */
35
+ function defaultToSql(value) {
36
+ if (value == null) {
37
+ return value === null ? 'NULL' : undefined;
38
+ }
39
+ if (typeof value === 'number') {
40
+ return String(value);
41
+ }
42
+ if (typeof value === 'string') {
43
+ return `'${value.replace(/'/g, "''")}'`;
44
+ }
45
+ if (typeof value === 'boolean') {
46
+ return value ? '1' : '0';
47
+ }
48
+ // drizzle-orm SQL expression (has queryChunks)
49
+ if (typeof value === 'object' && value !== null && 'queryChunks' in value) {
50
+ const chunks = value.queryChunks;
51
+ if (chunks) {
52
+ return sqlToString(chunks);
53
+ }
54
+ }
55
+ return String(value);
56
+ }
57
+ /**
58
+ * Resolve a drizzle table object to its string name.
59
+ */
60
+ function getTableName(table) {
61
+ // Drizzle tables store name at Symbol.for('drizzle:Name')
62
+ const nameSym = Symbol.for('drizzle:Name');
63
+ return String(table[nameSym]);
64
+ }
65
+ /**
66
+ * Generate a `CREATE TABLE IF NOT EXISTS` DDL statement from a Drizzle SQLite table.
67
+ *
68
+ * Uses `getTableConfig` (drizzle-orm runtime introspection) to extract columns,
69
+ * types, constraints, and foreign keys — no drizzle-kit CLI required.
70
+ *
71
+ * The output is deterministic: columns are emitted in definition order, identifiers
72
+ * are double-quoted, and table constraints follow column definitions.
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * const users = sqliteTable('users', { id: text('id').primaryKey() });
77
+ * const ddl = generateCreateTableSql(users);
78
+ * // CREATE TABLE IF NOT EXISTS "users" ("id" text PRIMARY KEY NOT NULL)
79
+ * ```
80
+ */
81
+ export function generateCreateTableSql(table) {
82
+ const config = getTableConfig(table);
83
+ const columnDefs = [];
84
+ const tableConstraints = [];
85
+ // Track which columns participate in composite unique constraints
86
+ const compositeUniqueCols = new Set();
87
+ for (const uc of config.uniqueConstraints) {
88
+ if (uc.columns.length > 1) {
89
+ for (const col of uc.columns) {
90
+ compositeUniqueCols.add(col.name);
91
+ }
92
+ }
93
+ }
94
+ // Track which columns participate in composite primary keys
95
+ const compositePkCols = new Set();
96
+ for (const pk of config.primaryKeys) {
97
+ if (pk.columns.length > 1) {
98
+ for (const col of pk.columns) {
99
+ compositePkCols.add(col.name);
100
+ }
101
+ }
102
+ }
103
+ for (const col of config.columns) {
104
+ const parts = [quoteIdent(col.name), col.getSQLType()];
105
+ // Column-level PRIMARY KEY only for single-column PKs
106
+ if (col.primary && !compositePkCols.has(col.name)) {
107
+ parts.push('PRIMARY KEY');
108
+ }
109
+ if (col.notNull) {
110
+ parts.push('NOT NULL');
111
+ }
112
+ // DEFAULT — only for SQL-level defaults (not runtime $defaultFn)
113
+ if (col.hasDefault && col.default !== undefined) {
114
+ const sqlDefault = defaultToSql(col.default);
115
+ if (sqlDefault !== undefined) {
116
+ parts.push(`DEFAULT ${sqlDefault}`);
117
+ }
118
+ }
119
+ // UNIQUE at column level only when it's a single-column unique constraint
120
+ if (col.isUnique && !compositeUniqueCols.has(col.name)) {
121
+ parts.push('UNIQUE');
122
+ }
123
+ columnDefs.push(parts.join(' '));
124
+ }
125
+ // Composite PRIMARY KEY
126
+ for (const pk of config.primaryKeys) {
127
+ if (pk.columns.length > 1) {
128
+ const pkCols = pk.columns.map((c) => quoteIdent(c.name)).join(', ');
129
+ tableConstraints.push(`PRIMARY KEY (${pkCols})`);
130
+ }
131
+ }
132
+ // Composite UNIQUE constraints
133
+ for (const uc of config.uniqueConstraints) {
134
+ if (uc.columns.length > 1) {
135
+ const cols = uc.columns.map((c) => quoteIdent(c.name)).join(', ');
136
+ tableConstraints.push(`UNIQUE (${cols})`);
137
+ }
138
+ }
139
+ // Foreign keys
140
+ for (const fk of config.foreignKeys) {
141
+ const ref = fk.reference();
142
+ const localCols = ref.columns.map((c) => quoteIdent(c.name)).join(', ');
143
+ const foreignCols = ref.foreignColumns.map((c) => quoteIdent(c.name)).join(', ');
144
+ const foreignTableName = getTableName(ref.foreignTable);
145
+ let constraint = `FOREIGN KEY (${localCols}) REFERENCES ${quoteIdent(foreignTableName)} (${foreignCols})`;
146
+ if (fk.onDelete) {
147
+ constraint += ` ON DELETE ${fk.onDelete}`;
148
+ }
149
+ if (fk.onUpdate) {
150
+ constraint += ` ON UPDATE ${fk.onUpdate}`;
151
+ }
152
+ tableConstraints.push(constraint);
153
+ }
154
+ const allDefs = [...columnDefs, ...tableConstraints];
155
+ return `CREATE TABLE IF NOT EXISTS ${quoteIdent(config.name)} (\n ${allDefs.join(',\n ')}\n)`;
156
+ }
@@ -1,18 +1,22 @@
1
1
  import { type SQLiteColumnBuilderBase, sqliteTable } from 'drizzle-orm/sqlite-core';
2
2
  import type { ZodType } from 'zod';
3
3
  /**
4
- * A table definition bundled with its drizzle-zod validation schemas.
4
+ * A table definition bundled with its drizzle-zod validation schemas and DDL.
5
5
  *
6
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.
7
+ * table (for queries/migrations), insert/select zod schemas (for boundary
8
+ * validation), and CREATE TABLE DDL (for migrations) — with no parallel
9
+ * re-authoring.
9
10
  *
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.
11
+ * The zod schemas and DDL are derived lazily — only materialised the first
12
+ * time they are read — so a consumer that only needs the table pays nothing
13
+ * extra.
12
14
  *
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.
15
+ * `defineTable`, `insertSchema`, `selectSchema`, and `createTableSql` require
16
+ * the optional peers `zod` and `drizzle-zod`. Consumers that never validate and
17
+ * don't need generated DDL can use `createDbAdapter` + raw `sqliteTable` +
18
+ * column helpers without installing those peers. Import from
19
+ * `@gobing-ai/ts-db/schema` to opt in.
16
20
  */
17
21
  export interface DefinedTable<TTable> {
18
22
  /** The underlying drizzle table — pass to DAOs, migrations, queries. */
@@ -21,12 +25,16 @@ export interface DefinedTable<TTable> {
21
25
  readonly insertSchema: ZodType;
22
26
  /** Zod schema validating a selected row. */
23
27
  readonly selectSchema: ZodType;
28
+ /** `CREATE TABLE IF NOT EXISTS` DDL generated from the table definition (lazy). */
29
+ readonly createTableSql: string;
24
30
  }
25
31
  /**
26
- * Define a SQLite table and derive its validation schemas in one place (G2).
32
+ * Define a SQLite table and derive its validation schemas and DDL in one place (G2).
27
33
  *
28
34
  * @example
29
35
  * ```ts
36
+ * import { defineTable } from '@gobing-ai/ts-db/schema';
37
+ *
30
38
  * export const users = defineTable('users', {
31
39
  * id: text('id').primaryKey(),
32
40
  * email: text('email').notNull().unique(),
@@ -34,6 +42,7 @@ export interface DefinedTable<TTable> {
34
42
  * });
35
43
  * users.table // drizzle table for DAOs/migrations
36
44
  * users.insertSchema // zod schema derived from the table
45
+ * users.createTableSql // CREATE TABLE IF NOT EXISTS "users" (...)
37
46
  * ```
38
47
  */
39
48
  export declare function defineTable<TName extends string, TColumns extends Record<string, SQLiteColumnBuilderBase>>(name: TName, columns: TColumns): DefinedTable<ReturnType<typeof sqliteTable<TName, TColumns>>>;
@@ -0,0 +1 @@
1
+ {"version":3,"file":"define-table.d.ts","sourceRoot":"","sources":["../../src/schema/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;AAGnC;;;;;;;;;;;;;;;;;GAiBG;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;IAC/B,mFAAmF;IACnF,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;;;;;;;;;GAgBG;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,CA4B/D"}
@@ -1,10 +1,13 @@
1
1
  import { sqliteTable } from 'drizzle-orm/sqlite-core';
2
2
  import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
3
+ import { generateCreateTableSql } from './ddl.js';
3
4
  /**
4
- * Define a SQLite table and derive its validation schemas in one place (G2).
5
+ * Define a SQLite table and derive its validation schemas and DDL in one place (G2).
5
6
  *
6
7
  * @example
7
8
  * ```ts
9
+ * import { defineTable } from '@gobing-ai/ts-db/schema';
10
+ *
8
11
  * export const users = defineTable('users', {
9
12
  * id: text('id').primaryKey(),
10
13
  * email: text('email').notNull().unique(),
@@ -12,12 +15,14 @@ import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
12
15
  * });
13
16
  * users.table // drizzle table for DAOs/migrations
14
17
  * users.insertSchema // zod schema derived from the table
18
+ * users.createTableSql // CREATE TABLE IF NOT EXISTS "users" (...)
15
19
  * ```
16
20
  */
17
21
  export function defineTable(name, columns) {
18
22
  const table = sqliteTable(name, columns);
19
23
  let insert;
20
24
  let select;
25
+ let ddl;
21
26
  return {
22
27
  table,
23
28
  get insertSchema() {
@@ -32,5 +37,11 @@ export function defineTable(name, columns) {
32
37
  }
33
38
  return select;
34
39
  },
40
+ get createTableSql() {
41
+ if (ddl === undefined) {
42
+ ddl = generateCreateTableSql(table);
43
+ }
44
+ return ddl;
45
+ },
35
46
  };
36
47
  }
@@ -1,3 +1,5 @@
1
1
  export * from './common';
2
+ export { generateCreateTableSql } from './ddl';
3
+ export { type DefinedTable, defineTable } from './define-table';
2
4
  export { queueJobs } from './queue-jobs';
3
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,OAAO,EAAE,sBAAsB,EAAE,MAAM,OAAO,CAAC;AAC/C,OAAO,EAAE,KAAK,YAAY,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
@@ -1,2 +1,4 @@
1
1
  export * from './common.js';
2
+ export { generateCreateTableSql } from './ddl.js';
3
+ export { defineTable } from './define-table.js';
2
4
  export { queueJobs } from './queue-jobs.js';
@@ -0,0 +1,2 @@
1
+ export { queueJobs } from './queue-jobs';
2
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/schema/runtime.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1 @@
1
+ export { queueJobs } from './queue-jobs.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-db",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "@gobing-ai/ts-db — a drizzle-free database facade: typed DAOs over Bun SQLite / Cloudflare D1, a small predicate query spec, single-source-of-truth tables, and migrations. Drizzle stays an internal detail.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -31,6 +31,18 @@
31
31
  ".": {
32
32
  "types": "./dist/index.d.ts",
33
33
  "import": "./dist/index.js"
34
+ },
35
+ "./bun-sqlite": {
36
+ "types": "./dist/adapters/bun-sqlite.d.ts",
37
+ "import": "./dist/adapters/bun-sqlite.js"
38
+ },
39
+ "./d1": {
40
+ "types": "./dist/adapters/d1.d.ts",
41
+ "import": "./dist/adapters/d1.js"
42
+ },
43
+ "./schema": {
44
+ "types": "./dist/schema/index.d.ts",
45
+ "import": "./dist/schema/index.js"
34
46
  }
35
47
  },
36
48
  "files": [
@@ -50,7 +62,7 @@
50
62
  "release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-db-v<version> && git push --tags' && exit 1"
51
63
  },
52
64
  "dependencies": {
53
- "@gobing-ai/ts-runtime": "^0.2.2"
65
+ "@gobing-ai/ts-runtime": "^0.2.3"
54
66
  },
55
67
  "peerDependencies": {
56
68
  "drizzle-orm": ">=0.38.0",
@@ -1,8 +1,8 @@
1
- import { Database } from 'bun:sqlite';
2
1
  import { isAbsolute, resolve } from 'node:path';
2
+ import { Database } from '@gobing-ai/ts-runtime/bun-sqlite';
3
3
  import { type BunSQLiteDatabase, drizzle } from 'drizzle-orm/bun-sqlite';
4
4
  import type { DbAdapter, InternalDb } from '../adapter';
5
- import * as schema from '../schema/index';
5
+ import * as schema from '../schema/runtime';
6
6
 
7
7
  type SqliteStatementLike = {
8
8
  all: (...params: unknown[]) => unknown;
@@ -1,6 +1,6 @@
1
1
  import { type DrizzleD1Database, drizzle } from 'drizzle-orm/d1';
2
2
  import type { DbAdapter, InternalDb } from '../adapter';
3
- import * as schema from '../schema/index';
3
+ import * as schema from '../schema/runtime';
4
4
 
5
5
  /**
6
6
  * Minimal D1 binding interface — avoids depending on @cloudflare/workers-types.
package/src/entity-dao.ts CHANGED
@@ -315,13 +315,19 @@ export class EntityDao<TTable extends EntityTable, TPK extends SQLiteColumn> ext
315
315
  limit: spec.limit,
316
316
  });
317
317
  const last = rows[rows.length - 1] as Record<string, unknown> | undefined;
318
+ const cursorResultKey = this.resultKeyForColumn(spec.cursorColumn);
318
319
  const nextCursor =
319
- rows.length === spec.limit && last
320
- ? (last[(spec.cursorColumn as unknown as { name: string }).name] as string | number)
321
- : undefined;
320
+ rows.length === spec.limit && last ? (last[cursorResultKey] as string | number | undefined) : undefined;
322
321
  return nextCursor !== undefined ? { rows, nextCursor } : { rows };
323
322
  }
324
323
 
324
+ private resultKeyForColumn(column: SQLiteColumn): string {
325
+ for (const [key, value] of Object.entries(this.table)) {
326
+ if (value === column) return key;
327
+ }
328
+ return (column as unknown as { name: string }).name;
329
+ }
330
+
325
331
  /** Count records matching an optional predicate. */
326
332
  async count(where?: Predicate, includeDeleted = false): Promise<number> {
327
333
  const condition = this.withActive(where, includeDeleted);
package/src/index.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  export { createDbAdapter, type DbAdapter, type DbAdapterConfig, type InternalDb } from './adapter';
2
- export { BunSqliteAdapter, type BunSqliteOptions } from './adapters/bun-sqlite';
3
2
  export { D1Adapter } from './adapters/d1';
4
3
  export { BaseDao, type TxHandle } from './base-dao';
5
- export { type DefinedTable, defineTable } from './define-table';
4
+
6
5
  export { type EmbeddedMigration, embeddedMigrations } from './embedded-migrations';
7
6
  export {
8
7
  type CursorListSpec,
@@ -0,0 +1,174 @@
1
+ import { getTableConfig, type SQLiteTable } from 'drizzle-orm/sqlite-core';
2
+
3
+ /**
4
+ * Quote an identifier for use in SQL (double-quoted for SQLite compatibility).
5
+ */
6
+ function quoteIdent(name: string): string {
7
+ return `"${name.replace(/"/g, '""')}"`;
8
+ }
9
+
10
+ /**
11
+ * Extract the SQL string from a drizzle-orm SQL expression by walking its
12
+ * internal `queryChunks` — StringChunk values plus Param placeholders.
13
+ */
14
+ function sqlToString(chunks: Array<{ value?: unknown; input?: unknown }>): string {
15
+ return chunks
16
+ .map((chunk) => {
17
+ // StringChunk — the literal SQL fragment
18
+ if ('value' in chunk && typeof chunk.value === 'string') {
19
+ return chunk.value;
20
+ }
21
+ // Param — use the input value if available
22
+ if ('input' in chunk && chunk.input !== undefined) {
23
+ return String(chunk.input);
24
+ }
25
+ return String(chunk.value ?? '?');
26
+ })
27
+ .join('');
28
+ }
29
+
30
+ /**
31
+ * Map a column default value to its SQL literal representation.
32
+ *
33
+ * drizzle-orm defaults can be:
34
+ * - primitives: number, string, boolean, null
35
+ * - SQL expressions (sql\`...\` template results)
36
+ * - undefined (no default, or runtime-only $defaultFn)
37
+ */
38
+ function defaultToSql(value: unknown): string | undefined {
39
+ if (value == null) {
40
+ return value === null ? 'NULL' : undefined;
41
+ }
42
+ if (typeof value === 'number') {
43
+ return String(value);
44
+ }
45
+ if (typeof value === 'string') {
46
+ return `'${value.replace(/'/g, "''")}'`;
47
+ }
48
+ if (typeof value === 'boolean') {
49
+ return value ? '1' : '0';
50
+ }
51
+ // drizzle-orm SQL expression (has queryChunks)
52
+ if (typeof value === 'object' && value !== null && 'queryChunks' in value) {
53
+ const chunks = (value as Record<string, unknown>).queryChunks as
54
+ | Array<{ value?: unknown; input?: unknown }>
55
+ | undefined;
56
+ if (chunks) {
57
+ return sqlToString(chunks);
58
+ }
59
+ }
60
+ return String(value);
61
+ }
62
+
63
+ /**
64
+ * Resolve a drizzle table object to its string name.
65
+ */
66
+ function getTableName(table: Record<string, unknown>): string {
67
+ // Drizzle tables store name at Symbol.for('drizzle:Name')
68
+ const nameSym = Symbol.for('drizzle:Name');
69
+ return String((table as unknown as Record<symbol, unknown>)[nameSym]);
70
+ }
71
+
72
+ /**
73
+ * Generate a `CREATE TABLE IF NOT EXISTS` DDL statement from a Drizzle SQLite table.
74
+ *
75
+ * Uses `getTableConfig` (drizzle-orm runtime introspection) to extract columns,
76
+ * types, constraints, and foreign keys — no drizzle-kit CLI required.
77
+ *
78
+ * The output is deterministic: columns are emitted in definition order, identifiers
79
+ * are double-quoted, and table constraints follow column definitions.
80
+ *
81
+ * @example
82
+ * ```ts
83
+ * const users = sqliteTable('users', { id: text('id').primaryKey() });
84
+ * const ddl = generateCreateTableSql(users);
85
+ * // CREATE TABLE IF NOT EXISTS "users" ("id" text PRIMARY KEY NOT NULL)
86
+ * ```
87
+ */
88
+ export function generateCreateTableSql(table: SQLiteTable): string {
89
+ const config = getTableConfig(table);
90
+
91
+ const columnDefs: string[] = [];
92
+ const tableConstraints: string[] = [];
93
+
94
+ // Track which columns participate in composite unique constraints
95
+ const compositeUniqueCols = new Set<string>();
96
+ for (const uc of config.uniqueConstraints) {
97
+ if (uc.columns.length > 1) {
98
+ for (const col of uc.columns) {
99
+ compositeUniqueCols.add(col.name);
100
+ }
101
+ }
102
+ }
103
+
104
+ // Track which columns participate in composite primary keys
105
+ const compositePkCols = new Set<string>();
106
+ for (const pk of config.primaryKeys) {
107
+ if (pk.columns.length > 1) {
108
+ for (const col of pk.columns) {
109
+ compositePkCols.add(col.name);
110
+ }
111
+ }
112
+ }
113
+
114
+ for (const col of config.columns) {
115
+ const parts: string[] = [quoteIdent(col.name), col.getSQLType()];
116
+
117
+ // Column-level PRIMARY KEY only for single-column PKs
118
+ if (col.primary && !compositePkCols.has(col.name)) {
119
+ parts.push('PRIMARY KEY');
120
+ }
121
+ if (col.notNull) {
122
+ parts.push('NOT NULL');
123
+ }
124
+ // DEFAULT — only for SQL-level defaults (not runtime $defaultFn)
125
+ if (col.hasDefault && col.default !== undefined) {
126
+ const sqlDefault = defaultToSql(col.default);
127
+ if (sqlDefault !== undefined) {
128
+ parts.push(`DEFAULT ${sqlDefault}`);
129
+ }
130
+ }
131
+ // UNIQUE at column level only when it's a single-column unique constraint
132
+ if (col.isUnique && !compositeUniqueCols.has(col.name)) {
133
+ parts.push('UNIQUE');
134
+ }
135
+
136
+ columnDefs.push(parts.join(' '));
137
+ }
138
+
139
+ // Composite PRIMARY KEY
140
+ for (const pk of config.primaryKeys) {
141
+ if (pk.columns.length > 1) {
142
+ const pkCols = pk.columns.map((c) => quoteIdent(c.name)).join(', ');
143
+ tableConstraints.push(`PRIMARY KEY (${pkCols})`);
144
+ }
145
+ }
146
+
147
+ // Composite UNIQUE constraints
148
+ for (const uc of config.uniqueConstraints) {
149
+ if (uc.columns.length > 1) {
150
+ const cols = uc.columns.map((c) => quoteIdent(c.name)).join(', ');
151
+ tableConstraints.push(`UNIQUE (${cols})`);
152
+ }
153
+ }
154
+
155
+ // Foreign keys
156
+ for (const fk of config.foreignKeys) {
157
+ const ref = fk.reference();
158
+ const localCols = ref.columns.map((c) => quoteIdent(c.name)).join(', ');
159
+ const foreignCols = ref.foreignColumns.map((c) => quoteIdent(c.name)).join(', ');
160
+ const foreignTableName = getTableName(ref.foreignTable as unknown as Record<string, symbol | unknown>);
161
+
162
+ let constraint = `FOREIGN KEY (${localCols}) REFERENCES ${quoteIdent(foreignTableName)} (${foreignCols})`;
163
+ if (fk.onDelete) {
164
+ constraint += ` ON DELETE ${fk.onDelete}`;
165
+ }
166
+ if (fk.onUpdate) {
167
+ constraint += ` ON UPDATE ${fk.onUpdate}`;
168
+ }
169
+ tableConstraints.push(constraint);
170
+ }
171
+
172
+ const allDefs = [...columnDefs, ...tableConstraints];
173
+ return `CREATE TABLE IF NOT EXISTS ${quoteIdent(config.name)} (\n ${allDefs.join(',\n ')}\n)`;
174
+ }
@@ -1,20 +1,25 @@
1
1
  import { type SQLiteColumnBuilderBase, sqliteTable } from 'drizzle-orm/sqlite-core';
2
2
  import { createInsertSchema, createSelectSchema } from 'drizzle-zod';
3
3
  import type { ZodType } from 'zod';
4
+ import { generateCreateTableSql } from './ddl';
4
5
 
5
6
  /**
6
- * A table definition bundled with its drizzle-zod validation schemas.
7
+ * A table definition bundled with its drizzle-zod validation schemas and DDL.
7
8
  *
8
9
  * The single source of truth (G2): one table authored once yields the drizzle
9
- * table (for queries/migrations) plus insert/select zod schemas (for boundary
10
- * validation), with no parallel re-authoring.
10
+ * table (for queries/migrations), insert/select zod schemas (for boundary
11
+ * validation), and CREATE TABLE DDL (for migrations) — with no parallel
12
+ * re-authoring.
11
13
  *
12
- * The zod schemas are derived lazily — only materialised the first time they are
13
- * read — so a consumer that only needs the table pays nothing extra.
14
+ * The zod schemas and DDL are derived lazily — only materialised the first
15
+ * time they are read — so a consumer that only needs the table pays nothing
16
+ * extra.
14
17
  *
15
- * `defineTable`, `insertSchema`, and `selectSchema` require the optional peers
16
- * `zod` and `drizzle-zod`. Consumers that never validate need not install them
17
- * and simply use `createDbAdapter` + raw `sqliteTable` + the column helpers.
18
+ * `defineTable`, `insertSchema`, `selectSchema`, and `createTableSql` require
19
+ * the optional peers `zod` and `drizzle-zod`. Consumers that never validate and
20
+ * don't need generated DDL can use `createDbAdapter` + raw `sqliteTable` +
21
+ * column helpers without installing those peers. Import from
22
+ * `@gobing-ai/ts-db/schema` to opt in.
18
23
  */
19
24
  export interface DefinedTable<TTable> {
20
25
  /** The underlying drizzle table — pass to DAOs, migrations, queries. */
@@ -23,13 +28,17 @@ export interface DefinedTable<TTable> {
23
28
  readonly insertSchema: ZodType;
24
29
  /** Zod schema validating a selected row. */
25
30
  readonly selectSchema: ZodType;
31
+ /** `CREATE TABLE IF NOT EXISTS` DDL generated from the table definition (lazy). */
32
+ readonly createTableSql: string;
26
33
  }
27
34
 
28
35
  /**
29
- * Define a SQLite table and derive its validation schemas in one place (G2).
36
+ * Define a SQLite table and derive its validation schemas and DDL in one place (G2).
30
37
  *
31
38
  * @example
32
39
  * ```ts
40
+ * import { defineTable } from '@gobing-ai/ts-db/schema';
41
+ *
33
42
  * export const users = defineTable('users', {
34
43
  * id: text('id').primaryKey(),
35
44
  * email: text('email').notNull().unique(),
@@ -37,6 +46,7 @@ export interface DefinedTable<TTable> {
37
46
  * });
38
47
  * users.table // drizzle table for DAOs/migrations
39
48
  * users.insertSchema // zod schema derived from the table
49
+ * users.createTableSql // CREATE TABLE IF NOT EXISTS "users" (...)
40
50
  * ```
41
51
  */
42
52
  export function defineTable<TName extends string, TColumns extends Record<string, SQLiteColumnBuilderBase>>(
@@ -47,6 +57,7 @@ export function defineTable<TName extends string, TColumns extends Record<string
47
57
 
48
58
  let insert: ZodType | undefined;
49
59
  let select: ZodType | undefined;
60
+ let ddl: string | undefined;
50
61
 
51
62
  return {
52
63
  table,
@@ -62,5 +73,11 @@ export function defineTable<TName extends string, TColumns extends Record<string
62
73
  }
63
74
  return select;
64
75
  },
76
+ get createTableSql(): string {
77
+ if (ddl === undefined) {
78
+ ddl = generateCreateTableSql(table);
79
+ }
80
+ return ddl;
81
+ },
65
82
  };
66
83
  }
@@ -1,2 +1,4 @@
1
1
  export * from './common';
2
+ export { generateCreateTableSql } from './ddl';
3
+ export { type DefinedTable, defineTable } from './define-table';
2
4
  export { queueJobs } from './queue-jobs';
@@ -0,0 +1 @@
1
+ export { queueJobs } from './queue-jobs';
@@ -1 +0,0 @@
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"}