@gobing-ai/ts-db 0.2.1 → 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 +4 -3
- package/dist/adapters/bun-sqlite.d.ts +1 -1
- package/dist/adapters/bun-sqlite.d.ts.map +1 -1
- package/dist/adapters/bun-sqlite.js +2 -2
- package/dist/adapters/d1.d.ts +1 -1
- package/dist/adapters/d1.d.ts.map +1 -1
- package/dist/adapters/d1.js +1 -1
- package/dist/entity-dao.d.ts +1 -0
- package/dist/entity-dao.d.ts.map +1 -1
- package/dist/entity-dao.js +9 -3
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -2
- package/dist/schema/ddl.d.ts +19 -0
- package/dist/schema/ddl.d.ts.map +1 -0
- package/dist/schema/ddl.js +156 -0
- package/dist/{define-table.d.ts → schema/define-table.d.ts} +18 -9
- package/dist/schema/define-table.d.ts.map +1 -0
- package/dist/{define-table.js → schema/define-table.js} +12 -1
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +2 -0
- package/dist/schema/runtime.d.ts +2 -0
- package/dist/schema/runtime.d.ts.map +1 -0
- package/dist/schema/runtime.js +1 -0
- package/package.json +14 -2
- package/src/adapters/bun-sqlite.ts +2 -2
- package/src/adapters/d1.ts +1 -1
- package/src/entity-dao.ts +9 -3
- package/src/index.ts +1 -2
- package/src/schema/ddl.ts +174 -0
- package/src/{define-table.ts → schema/define-table.ts} +26 -9
- package/src/schema/index.ts +2 -0
- package/src/schema/runtime.ts +1 -0
- package/dist/define-table.d.ts.map +0 -1
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.
|
|
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 {
|
|
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/
|
|
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,
|
|
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/
|
|
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',
|
package/dist/adapters/d1.d.ts
CHANGED
|
@@ -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/
|
|
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,
|
|
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"}
|
package/dist/adapters/d1.js
CHANGED
package/dist/entity-dao.d.ts
CHANGED
|
@@ -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. */
|
package/dist/entity-dao.d.ts.map
CHANGED
|
@@ -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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"}
|
package/dist/entity-dao.js
CHANGED
|
@@ -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
|
|
170
|
-
|
|
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';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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)
|
|
8
|
-
* validation), with no parallel
|
|
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
|
|
11
|
-
* read — so a consumer that only needs the table pays nothing
|
|
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 `
|
|
14
|
-
* `zod` and `drizzle-zod`. Consumers that never validate
|
|
15
|
-
*
|
|
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
|
}
|
package/dist/schema/index.d.ts
CHANGED
|
@@ -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"}
|
package/dist/schema/index.js
CHANGED
|
@@ -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.
|
|
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.
|
|
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/
|
|
5
|
+
import * as schema from '../schema/runtime';
|
|
6
6
|
|
|
7
7
|
type SqliteStatementLike = {
|
|
8
8
|
all: (...params: unknown[]) => unknown;
|
package/src/adapters/d1.ts
CHANGED
|
@@ -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/
|
|
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
|
-
|
|
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)
|
|
10
|
-
* validation), with no parallel
|
|
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
|
|
13
|
-
* read — so a consumer that only needs the table pays nothing
|
|
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 `
|
|
16
|
-
* `zod` and `drizzle-zod`. Consumers that never validate
|
|
17
|
-
*
|
|
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
|
}
|
package/src/schema/index.ts
CHANGED
|
@@ -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"}
|