@uql/core 3.1.4 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/CHANGELOG.md +6 -3
  2. package/README.md +40 -26
  3. package/dist/browser/uql-browser.min.js +112 -123
  4. package/dist/browser/uql-browser.min.js.map +1 -1
  5. package/dist/d1/d1Querier.d.ts +49 -0
  6. package/dist/d1/d1Querier.d.ts.map +1 -0
  7. package/dist/d1/d1Querier.js +40 -0
  8. package/dist/d1/d1Querier.js.map +1 -0
  9. package/dist/d1/d1QuerierPool.d.ts +10 -0
  10. package/dist/d1/d1QuerierPool.d.ts.map +1 -0
  11. package/dist/d1/d1QuerierPool.js +16 -0
  12. package/dist/d1/d1QuerierPool.js.map +1 -0
  13. package/dist/d1/index.d.ts +3 -0
  14. package/dist/d1/index.d.ts.map +1 -0
  15. package/dist/d1/index.js +3 -0
  16. package/dist/d1/index.js.map +1 -0
  17. package/dist/libsql/index.d.ts +3 -0
  18. package/dist/libsql/index.d.ts.map +1 -0
  19. package/dist/libsql/index.js +3 -0
  20. package/dist/libsql/index.js.map +1 -0
  21. package/dist/libsql/libsqlQuerier.d.ts +21 -0
  22. package/dist/libsql/libsqlQuerier.d.ts.map +1 -0
  23. package/dist/libsql/libsqlQuerier.js +81 -0
  24. package/dist/libsql/libsqlQuerier.js.map +1 -0
  25. package/dist/libsql/libsqlQuerierPool.d.ts +12 -0
  26. package/dist/libsql/libsqlQuerierPool.d.ts.map +1 -0
  27. package/dist/libsql/libsqlQuerierPool.js +19 -0
  28. package/dist/libsql/libsqlQuerierPool.js.map +1 -0
  29. package/dist/migrate/cli.js +7 -7
  30. package/dist/migrate/cli.js.map +1 -1
  31. package/dist/migrate/introspection/mongoIntrospector.d.ts +2 -2
  32. package/dist/migrate/introspection/mongoIntrospector.d.ts.map +1 -1
  33. package/dist/migrate/introspection/mongoIntrospector.js +5 -5
  34. package/dist/migrate/introspection/mongoIntrospector.js.map +1 -1
  35. package/dist/migrate/introspection/mysqlIntrospector.d.ts +2 -2
  36. package/dist/migrate/introspection/mysqlIntrospector.d.ts.map +1 -1
  37. package/dist/migrate/introspection/mysqlIntrospector.js +4 -4
  38. package/dist/migrate/introspection/mysqlIntrospector.js.map +1 -1
  39. package/dist/migrate/introspection/postgresIntrospector.d.ts +2 -2
  40. package/dist/migrate/introspection/postgresIntrospector.d.ts.map +1 -1
  41. package/dist/migrate/introspection/postgresIntrospector.js +4 -4
  42. package/dist/migrate/introspection/postgresIntrospector.js.map +1 -1
  43. package/dist/migrate/introspection/sqliteIntrospector.d.ts +2 -2
  44. package/dist/migrate/introspection/sqliteIntrospector.d.ts.map +1 -1
  45. package/dist/migrate/introspection/sqliteIntrospector.js +4 -4
  46. package/dist/migrate/introspection/sqliteIntrospector.js.map +1 -1
  47. package/dist/migrate/migrator.d.ts +2 -2
  48. package/dist/migrate/migrator.d.ts.map +1 -1
  49. package/dist/migrate/migrator.js +13 -13
  50. package/dist/migrate/migrator.js.map +1 -1
  51. package/dist/migrate/storage/databaseStorage.d.ts +2 -2
  52. package/dist/migrate/storage/databaseStorage.d.ts.map +1 -1
  53. package/dist/migrate/storage/databaseStorage.js +5 -5
  54. package/dist/migrate/storage/databaseStorage.js.map +1 -1
  55. package/dist/neon/index.d.ts +3 -0
  56. package/dist/neon/index.d.ts.map +1 -0
  57. package/dist/neon/index.js +3 -0
  58. package/dist/neon/index.js.map +1 -0
  59. package/dist/neon/neonQuerier.d.ts +18 -0
  60. package/dist/neon/neonQuerier.d.ts.map +1 -0
  61. package/dist/neon/neonQuerier.js +40 -0
  62. package/dist/neon/neonQuerier.js.map +1 -0
  63. package/dist/neon/neonQuerierPool.d.ts +11 -0
  64. package/dist/neon/neonQuerierPool.d.ts.map +1 -0
  65. package/dist/neon/neonQuerierPool.js +17 -0
  66. package/dist/neon/neonQuerierPool.js.map +1 -0
  67. package/dist/querier/decorator/transactional.d.ts +2 -2
  68. package/dist/querier/decorator/transactional.d.ts.map +1 -1
  69. package/dist/querier/decorator/transactional.js +2 -2
  70. package/dist/querier/decorator/transactional.js.map +1 -1
  71. package/package.json +16 -3
package/CHANGELOG.md CHANGED
@@ -3,9 +3,12 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [3.1.4](https://github.com/rogerpadilla/uql/compare/@uql/core@3.1.3...@uql/core@3.1.4) (2025-12-30)
6
+ # [3.4.0](https://github.com/rogerpadilla/uql/compare/@uql/core@3.3.0...@uql/core@3.4.0) (2025-12-30)
7
7
 
8
- **Note:** Version bump only for package @uql/core
8
+
9
+ ### Features
10
+
11
+ * add support for Neon, LibSQL, and D1 database queriers, including new querier pools, implementations, tests, and updated documentation. ([aad1df0](https://github.com/rogerpadilla/uql/commit/aad1df0f6bab112b2a0eb0fdffee15dcbc6b0824))
9
12
 
10
13
 
11
14
 
@@ -192,7 +195,7 @@ Reflect major changes in the package structure and dependencies.
192
195
  - Update dependencies.
193
196
 
194
197
  ```ts
195
- const ids = await querierPool.transaction(async (querier) => {
198
+ const ids = await pool.transaction(async (querier) => {
196
199
  const data = await querier.findMany(...);
197
200
  const ids = await querier.insertMany(...);
198
201
  return ids;
package/README.md CHANGED
@@ -2,13 +2,12 @@
2
2
 
3
3
  [![uql maku](assets/logo.svg)](https://uql.app)
4
4
 
5
- [![tests](https://github.com/rogerpadilla/uql/actions/workflows/tests.yml/badge.svg)](https://github.com/rogerpadilla/uql) [![coverage status](https://coveralls.io/repos/rogerpadilla/uql/badge.svg?branch=main)](https://coveralls.io/r/rogerpadilla/uql?branch=main) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/rogerpadilla/uql/blob/main/LICENSE) [![npm version](https://img.shields.io/npm/v/@uql/core.svg)](https://www.npmjs.com/package/@uql/core)
5
+ [![tests](https://github.com/rogerpadilla/uql/actions/workflows/tests.yml/badge.svg)](https://github.com/rogerpadilla/uql) [![Coverage Status](https://coveralls.io/repos/github/rogerpadilla/uql/badge.svg?branch=main)](https://coveralls.io/github/rogerpadilla/uql?branch=main) [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/rogerpadilla/uql/blob/main/LICENSE) [![npm version](https://img.shields.io/npm/v/@uql/core.svg)](https://www.npmjs.com/package/@uql/core)
6
6
 
7
- [UQL](https://uql.app) is the [smartest ORM](https://medium.com/@rogerpadillac/in-search-of-the-perfect-orm-e01fcc9bce3d) for TypeScript, it is designed to be fast, safe, and easy to integrate into any application.
7
+ **[UQL](https://uql.app)** is the [smartest ORM](https://medium.com/@rogerpadillac/in-search-of-the-perfect-orm-e01fcc9bce3d) for TypeScript. It is engineered to be **fast**, **safe**, and **universally compatible**.
8
8
 
9
- It can run in Node.js, Browser, React Native, Expo, Electron, Deno, Bun, and many more!
10
-
11
- Uses a consistent API for distinct databases, including PostgreSQL, MySQL, MariaDB, and SQLite (inspired by MongoDB glorious syntax).
9
+ - **Runs Everywhere**: Node.js, Bun, Deno, Cloudflare Workers, Electron, React Native, and even the Browser.
10
+ - **Unified API**: A consistent, expressive query interface for PostgreSQL, MySQL, MariaDB, SQLite, LibSQL, Neon, Cloudflare D1, and MongoDB (inspired by its glorious syntax).
12
11
 
13
12
   
14
13
 
@@ -33,7 +32,7 @@ See [this article](https://medium.com/@rogerpadillac/in-search-of-the-perfect-or
33
32
 
34
33
  - **Type-safe and Context-aware queries**: Squeeze the power of `TypeScript` for auto-completion and validation of operators at any depth, [including relations and their fields](https://www.uql.app/docs/querying-relations).
35
34
  - **Context-Object SQL Generation**: Uses a sophisticated `QueryContext` pattern to ensure perfectly indexed placeholders ($1, $2, etc.) and robust SQL fragment management, even in the most complex sub-queries.
36
- - **Unified API across Databases**: Write once, run anywhere. Seamlessly switch between `PostgreSQL`, `MySQL`, `MariaDB`, `SQLite`, and even `MongoDB`.
35
+ - **Unified API across Databases**: Write once, run anywhere. Seamlessly switch between `PostgreSQL`, `MySQL`, `MariaDB`, `SQLite`, `LibSQL`, `Neon`, `Cloudflare D1`, and even `MongoDB`.
37
36
  - **Serializable JSON Syntax**: Queries can be expressed as `100%` valid `JSON`, allowing them to be easily transported from frontend to backend.
38
37
  - **Naming Strategies**: Effortlessly translate between TypeScript `CamelCase` and database `snake_case` (or any custom format) with a pluggable system.
39
38
  - **Built-in Serialization**: A centralized task queue and `@Serialized()` decorator ensure database operations are thread-safe and race-condition free by default.
@@ -56,12 +55,15 @@ See [this article](https://medium.com/@rogerpadillac/in-search-of-the-perfect-or
56
55
 
57
56
  2. Install one of the specific adapters for your database:
58
57
 
59
- | Database | Driver
60
- | ------------ | ----------------
61
- | `PostgreSQL` | `pg`
62
- | `SQLite` | `better-sqlite3`
63
- | `MariaDB` | `mariadb`
64
- | `MySQL` | `mysql2`
58
+ | Database | Driver
59
+ | :--- | :---
60
+ | `PostgreSQL` (incl. CockroachDB, YugabyteDB) | `pg`
61
+ | `MySQL` (incl. TiDB, Aurora) | `mysql2`
62
+ | `MariaDB` | `mariadb`
63
+ | `SQLite` | `better-sqlite3`
64
+ | `Cloudflare D1` | `Native Binding`
65
+ | `LibSQL` (Turso) | `@libsql/client`
66
+ | `Neon` (Serverless Postgres) | `@neondatabase/serverless`
65
67
 
66
68
   
67
69
 
@@ -92,11 +94,12 @@ bun add pg
92
94
  Annotate your classes with decorators from `@uql/core`. UQL supports detailed schema metadata for precise DDL generation.
93
95
 
94
96
  ```ts
97
+ import { v7 as uuidv7 } from 'uuid';
95
98
  import { Entity, Id, Field, OneToOne, OneToMany, ManyToOne, ManyToMany, type Relation } from '@uql/core';
96
99
 
97
100
  @Entity()
98
101
  export class User {
99
- @Id()
102
+ @Id({ onInsert: () => uuidv7() })
100
103
  id?: string;
101
104
 
102
105
  @Field({ length: 100, index: true })
@@ -114,7 +117,7 @@ export class User {
114
117
 
115
118
  @Entity()
116
119
  export class Profile {
117
- @Id()
120
+ @Id({ onInsert: () => uuidv7() })
118
121
  id?: string;
119
122
 
120
123
  @Field()
@@ -147,7 +150,7 @@ export class Post {
147
150
 
148
151
  @Entity()
149
152
  export class Tag {
150
- @Id()
153
+ @Id({ onInsert: () => uuidv7() })
151
154
  id?: string;
152
155
 
153
156
  @Field()
@@ -156,7 +159,7 @@ export class Tag {
156
159
 
157
160
  @Entity()
158
161
  export class PostTag {
159
- @Id()
162
+ @Id({ onInsert: () => uuidv7() })
160
163
  id?: string;
161
164
 
162
165
  @Field({ reference: () => Post })
@@ -178,7 +181,7 @@ A querier-pool can be set in any of the bootstrap files of your app (e.g. in the
178
181
  import { SnakeCaseNamingStrategy } from '@uql/core';
179
182
  import { PgQuerierPool } from '@uql/core/postgres';
180
183
 
181
- export const querierPool = new PgQuerierPool(
184
+ export const pool = new PgQuerierPool(
182
185
  {
183
186
  host: 'localhost',
184
187
  user: 'theUser',
@@ -211,10 +214,10 @@ Repositories provide a clean, Data-Mapper style interface for your entities.
211
214
  ```ts
212
215
  import { GenericRepository } from '@uql/core';
213
216
  import { User } from './shared/models/index.js';
214
- import { querierPool } from './shared/orm.js';
217
+ import { pool } from './shared/orm.js';
215
218
 
216
219
  // Get a querier from the pool
217
- const querier = await querierPool.getQuerier();
220
+ const querier = await pool.getQuerier();
218
221
 
219
222
  try {
220
223
  const userRepository = new GenericRepository(User, querier);
@@ -246,10 +249,10 @@ UQL's query syntax is context-aware. When you query a relation, the available fi
246
249
 
247
250
  ```ts
248
251
  import { GenericRepository } from '@uql/core';
249
- import { querierPool } from './shared/orm.js';
252
+ import { pool } from './shared/orm.js';
250
253
  import { User } from './shared/models/index.js';
251
254
 
252
- const authorsWithPopularPosts = await querierPool.transaction(async (querier) => {
255
+ const authorsWithPopularPosts = await pool.transaction(async (querier) => {
253
256
  const userRepository = new GenericRepository(User, querier);
254
257
 
255
258
  return userRepository.findMany({
@@ -282,6 +285,7 @@ const authorsWithPopularPosts = await querierPool.transaction(async (querier) =>
282
285
  Define complex logic directly in your entities using `raw` functions from `uql/util`. These are highly efficient as they are resolved during SQL generation.
283
286
 
284
287
  ```ts
288
+ import { v7 as uuidv7 } from 'uuid';
285
289
  import { Entity, Id, Field, raw } from '@uql/core';
286
290
  import { ItemTag } from './shared/models/index.js';
287
291
 
@@ -313,10 +317,10 @@ export class Item {
313
317
  UQL ensures your operations are serialized and thread-safe.
314
318
 
315
319
  ```ts
316
- import { querierPool } from './shared/orm.js';
320
+ import { pool } from './shared/orm.js';
317
321
  import { User, Profile } from './shared/models/index.js';
318
322
 
319
- const result = await querierPool.transaction(async (querier) => {
323
+ const result = await pool.transaction(async (querier) => {
320
324
  const user = await querier.findOne(User, { $where: { email: '...' } });
321
325
  const profileId = await querier.insertOne(Profile, { userId: user.id, ... });
322
326
  return { userId: user.id, profileId };
@@ -339,7 +343,7 @@ import { PgQuerierPool } from '@uql/core/postgres';
339
343
  import { User, Post } from './shared/models/index.js';
340
344
 
341
345
  export default {
342
- querierPool: new PgQuerierPool({ /* config */ }),
346
+ pool: new PgQuerierPool({ /* config */ }),
343
347
  entities: [User, Post],
344
348
  migrationsPath: './migrations',
345
349
  };
@@ -377,9 +381,9 @@ In development, you can use `autoSync` to automatically keep your database in sy
377
381
 
378
382
  ```ts
379
383
  import { Migrator } from '@uql/core/migrate';
380
- import { querierPool } from './shared/orm.js';
384
+ import { pool } from './shared/orm.js';
381
385
 
382
- const migrator = new Migrator(querierPool);
386
+ const migrator = new Migrator(pool);
383
387
  await migrator.autoSync({ logging: true });
384
388
  ```
385
389
 
@@ -406,3 +410,13 @@ For those who want to see the "engine under the hood," check out these resources
406
410
  - [PostgreSQL Spec](https://github.com/rogerpadilla/uql/blob/main/packages/core/src/postgres/postgresDialect.spec.ts) | [MySQL Spec](https://github.com/rogerpadilla/uql/blob/main/packages/core/src/mysql/mysqlDialect.spec.ts) | [SQLite Spec](https://github.com/rogerpadilla/uql/blob/main/packages/core/src/sqlite/sqliteDialect.spec.ts).
407
411
  - [Querier Integration Tests](https://github.com/rogerpadilla/uql/blob/main/packages/core/src/querier/abstractSqlQuerier-spec.ts): Testing the interaction between SQL generation and connection management.
408
412
  - [MongoDB Migration Tests](https://github.com/rogerpadilla/uql/blob/main/packages/core/src/migrate/migrator-mongo.it.ts): Integration tests ensuring correct collection and index synchronization for MongoDB.
413
+
414
+ ---
415
+
416
+ ## Built with ❤️ and supported by
417
+
418
+ UQL is an open-source project driven by the community and proudly sponsored by **[Variability.ai](https://variability.ai)**.
419
+
420
+ > "Intelligence in Every Fluctuation"
421
+
422
+ Their support helps us maintain and evolve the "Smartest ORM" for developers everywhere. Thank you for being part of our journey!
@@ -1,108 +1,97 @@
1
- import sqlstring from 'sqlstring-sqlite';
2
- import { AbstractSqlDialect } from '../dialect/index.js';
3
- import { getMeta } from '../entity/index.js';
4
- import { AbstractSqlQuerier, AbstractQuerierPool } from '../querier/index.js';
1
+ import { isSqlQuerier } from '../type/index.js';
2
+ export { isSqlQuerier } from '../type/index.js';
3
+ import { AbstractDialect } from '../dialect/index.js';
4
+ import { getMeta, getEntities } from '../entity/index.js';
5
+ import { escapeSqlId, getKeys } from '../util/index.js';
6
+ import { readdir, readFile, mkdir, writeFile } from 'node:fs/promises';
7
+ import { join, basename, extname, dirname } from 'node:path';
8
+ import { pathToFileURL } from 'node:url';
5
9
 
6
- class SqliteDialect extends AbstractSqlDialect {
7
- constructor(namingStrategy){
8
- super(namingStrategy, '`', 'BEGIN TRANSACTION');
9
- }
10
- addValue(values, value) {
11
- if (value instanceof Date) {
12
- value = value.getTime();
13
- } else if (typeof value === 'boolean') {
14
- value = value ? 1 : 0;
15
- }
16
- return super.addValue(values, value);
17
- }
18
- compare(ctx, entity, key, val, opts) {
19
- if (key === '$text') {
20
- const meta = getMeta(entity);
21
- const search = val;
22
- const fields = search.$fields.map((fKey)=>{
23
- const field = meta.fields[fKey];
24
- const columnName = this.resolveColumnName(fKey, field);
25
- return this.escapeId(columnName);
26
- });
27
- const tableName = this.resolveTableName(entity, meta);
28
- ctx.append(`${this.escapeId(tableName)} MATCH {${fields.join(' ')}} : `);
29
- ctx.addValue(search.$value);
30
- return;
31
- }
32
- super.compare(ctx, entity, key, val, opts);
10
+ /**
11
+ * Abstract base class for SQL schema generation
12
+ */ class AbstractSchemaGenerator extends AbstractDialect {
13
+ constructor(namingStrategy, escapeIdChar = '`'){
14
+ super(namingStrategy), this.escapeIdChar = escapeIdChar;
33
15
  }
34
- compareFieldOperator(ctx, entity, key, op, val, opts = {}) {
35
- super.compareFieldOperator(ctx, entity, key, op, val, opts);
16
+ /**
17
+ * Escape an identifier (table name, column name, etc.)
18
+ */ escapeId(identifier) {
19
+ return escapeSqlId(identifier, this.escapeIdChar);
36
20
  }
37
- upsert(ctx, entity, conflictPaths, payload) {
21
+ generateCreateTable(entity, options = {}) {
38
22
  const meta = getMeta(entity);
39
- const update = this.getUpsertUpdateAssignments(ctx, meta, conflictPaths, payload, (name)=>`EXCLUDED.${name}`);
40
- const keysStr = this.getUpsertConflictPathsStr(meta, conflictPaths);
41
- const onConflict = update ? `DO UPDATE SET ${update}` : 'DO NOTHING';
42
- this.insert(ctx, entity, payload);
43
- ctx.append(` ON CONFLICT (${keysStr}) ${onConflict}`);
44
- }
45
- escape(value) {
46
- return sqlstring.escape(value);
23
+ const tableName = this.resolveTableName(entity, meta);
24
+ const columns = this.generateColumnDefinitions(meta);
25
+ const constraints = this.generateTableConstraints(meta);
26
+ const ifNotExists = options.ifNotExists ? 'IF NOT EXISTS ' : '';
27
+ let sql = `CREATE TABLE ${ifNotExists}${this.escapeId(tableName)} (\n`;
28
+ sql += columns.map((col)=>` ${col}`).join(',\n');
29
+ if (constraints.length > 0) {
30
+ sql += ',\n';
31
+ sql += constraints.map((c)=>` ${c}`).join(',\n');
32
+ }
33
+ sql += '\n)';
34
+ sql += this.getTableOptions(meta);
35
+ sql += ';';
36
+ return sql;
47
37
  }
48
- }
49
-
50
- class SqliteQuerier extends AbstractSqlQuerier {
51
- constructor(db, extra){
52
- super(new SqliteDialect(extra?.namingStrategy)), this.db = db, this.extra = extra;
53
- }
54
- async internalAll(query, values) {
55
- this.extra?.logger?.(query, values);
56
- return this.db.prepare(query).all(values || []);
57
- }
58
- async internalRun(query, values) {
59
- this.extra?.logger?.(query, values);
60
- const { changes, lastInsertRowid } = this.db.prepare(query).run(values || []);
61
- const firstId = lastInsertRowid ? Number(lastInsertRowid) - (changes - 1) : undefined;
62
- const ids = firstId ? Array(changes).fill(firstId).map((i, index)=>i + index) : [];
63
- return {
64
- changes,
65
- ids,
66
- firstId
67
- };
38
+ generateDropTable(entity) {
39
+ const meta = getMeta(entity);
40
+ const tableName = this.resolveTableName(entity, meta);
41
+ return `DROP TABLE IF EXISTS ${this.escapeId(tableName)};`;
68
42
  }
69
- async internalRelease() {
70
- if (this.hasOpenTransaction) {
71
- throw TypeError('pending transaction');
43
+ generateAlterTable(diff) {
44
+ const statements = [];
45
+ const tableName = this.escapeId(diff.tableName);
46
+ // Add new columns
47
+ if (diff.columnsToAdd?.length) {
48
+ for (const column of diff.columnsToAdd){
49
+ const colDef = this.generateColumnDefinitionFromSchema(column);
50
+ statements.push(`ALTER TABLE ${tableName} ADD COLUMN ${colDef};`);
51
+ }
72
52
  }
73
- // no-op
74
- }
75
- }
76
-
77
- class Sqlite3QuerierPool extends AbstractQuerierPool {
78
- constructor(filename, opts, extra){
79
- super('sqlite', extra), this.filename = filename, this.opts = opts;
80
- }
81
- async getQuerier() {
82
- if (!this.querier) {
83
- let db;
84
- if (typeof Bun !== 'undefined') {
85
- const { Database } = await import('bun:sqlite');
86
- db = new Database(this.filename, this.opts);
87
- db.run('PRAGMA journal_mode = WAL');
88
- } else {
89
- const { default: Database } = await import('better-sqlite3');
90
- db = new Database(this.filename, this.opts);
91
- db.pragma('journal_mode = WAL');
53
+ // Alter existing columns
54
+ if (diff.columnsToAlter?.length) {
55
+ for (const { to } of diff.columnsToAlter){
56
+ const colDef = this.generateColumnDefinitionFromSchema(to);
57
+ const colStatements = this.generateAlterColumnStatements(diff.tableName, to, colDef);
58
+ statements.push(...colStatements);
59
+ }
60
+ }
61
+ // Drop columns
62
+ if (diff.columnsToDrop?.length) {
63
+ for (const columnName of diff.columnsToDrop){
64
+ statements.push(`ALTER TABLE ${tableName} DROP COLUMN ${this.escapeId(columnName)};`);
92
65
  }
93
- this.querier = new SqliteQuerier(db, this.extra);
94
66
  }
95
- return this.querier;
67
+ // Add indexes
68
+ if (diff.indexesToAdd?.length) {
69
+ for (const index of diff.indexesToAdd){
70
+ statements.push(this.generateCreateIndex(diff.tableName, index));
71
+ }
72
+ }
73
+ // Drop indexes
74
+ if (diff.indexesToDrop?.length) {
75
+ for (const indexName of diff.indexesToDrop){
76
+ statements.push(this.generateDropIndex(diff.tableName, indexName));
77
+ }
78
+ }
79
+ return statements;
96
80
  }
97
- async end() {
98
- await this.querier.db.close();
99
- delete this.querier;
81
+ generateAlterTableDown(diff) {
82
+ const statements = [];
83
+ const tableName = this.escapeId(diff.tableName);
84
+ // Rollback additions by dropping columns
85
+ if (diff.columnsToAdd?.length) {
86
+ for (const column of diff.columnsToAdd){
87
+ statements.push(`ALTER TABLE ${tableName} DROP COLUMN ${this.escapeId(column.name)};`);
88
+ }
89
+ }
90
+ return statements;
100
91
  }
101
- }
102
-
103
- export { Sqlite3QuerierPool, SqliteDialect, SqliteQuerier };
104
- //# sourceMappingURL=uql-browser.min.js.map
105
- >this.escapeId(c)).join(', ');
92
+ generateCreateIndex(tableName, index) {
93
+ const unique = index.unique ? 'UNIQUE ' : '';
94
+ const columns = index.columns.map((c)=>this.escapeId(c)).join(', ');
106
95
  return `CREATE ${unique}INDEX ${this.escapeId(index.name)} ON ${this.escapeId(tableName)} (${columns});`;
107
96
  }
108
97
  generateDropIndex(tableName, indexName) {
@@ -620,8 +609,8 @@ export { Sqlite3QuerierPool, SqliteDialect, SqliteQuerier };
620
609
  * MySQL/MariaDB schema introspector.
621
610
  * Works with both MySQL and MariaDB as they share the same information_schema structure.
622
611
  */ class MysqlSchemaIntrospector {
623
- constructor(querierPool){
624
- this.querierPool = querierPool;
612
+ constructor(pool){
613
+ this.pool = pool;
625
614
  }
626
615
  async getTableSchema(tableName) {
627
616
  const querier = await this.getQuerier();
@@ -684,7 +673,7 @@ export { Sqlite3QuerierPool, SqliteDialect, SqliteQuerier };
684
673
  return (results[0]?.count ?? 0) > 0;
685
674
  }
686
675
  async getQuerier() {
687
- const querier = await this.querierPool.getQuerier();
676
+ const querier = await this.pool.getQuerier();
688
677
  if (!isSqlQuerier(querier)) {
689
678
  await querier.release();
690
679
  throw new Error('MysqlSchemaIntrospector requires a SQL-based querier');
@@ -842,8 +831,8 @@ export { Sqlite3QuerierPool, SqliteDialect, SqliteQuerier };
842
831
  /**
843
832
  * PostgreSQL schema introspector
844
833
  */ class PostgresSchemaIntrospector {
845
- constructor(querierPool){
846
- this.querierPool = querierPool;
834
+ constructor(pool){
835
+ this.pool = pool;
847
836
  }
848
837
  async getTableSchema(tableName) {
849
838
  const querier = await this.getQuerier();
@@ -907,7 +896,7 @@ export { Sqlite3QuerierPool, SqliteDialect, SqliteQuerier };
907
896
  return results[0]?.exists ?? false;
908
897
  }
909
898
  async getQuerier() {
910
- const querier = await this.querierPool.getQuerier();
899
+ const querier = await this.pool.getQuerier();
911
900
  if (!isSqlQuerier(querier)) {
912
901
  await querier.release();
913
902
  throw new Error('PostgresSchemaIntrospector requires a SQL-based querier');
@@ -1115,8 +1104,8 @@ export { Sqlite3QuerierPool, SqliteDialect, SqliteQuerier };
1115
1104
  /**
1116
1105
  * SQLite schema introspector
1117
1106
  */ class SqliteSchemaIntrospector {
1118
- constructor(querierPool){
1119
- this.querierPool = querierPool;
1107
+ constructor(pool){
1108
+ this.pool = pool;
1120
1109
  }
1121
1110
  async getTableSchema(tableName) {
1122
1111
  const querier = await this.getQuerier();
@@ -1179,7 +1168,7 @@ export { Sqlite3QuerierPool, SqliteDialect, SqliteQuerier };
1179
1168
  return (results[0]?.count ?? 0) > 0;
1180
1169
  }
1181
1170
  async getQuerier() {
1182
- const querier = await this.querierPool.getQuerier();
1171
+ const querier = await this.pool.getQuerier();
1183
1172
  if (!isSqlQuerier(querier)) {
1184
1173
  await querier.release();
1185
1174
  throw new Error('SqliteSchemaIntrospector requires a SQL-based querier');
@@ -1436,11 +1425,11 @@ class MongoSchemaGenerator extends AbstractDialect {
1436
1425
  }
1437
1426
 
1438
1427
  class MongoSchemaIntrospector {
1439
- constructor(querierPool){
1440
- this.querierPool = querierPool;
1428
+ constructor(pool){
1429
+ this.pool = pool;
1441
1430
  }
1442
1431
  async getTableSchema(tableName) {
1443
- const querier = await this.querierPool.getQuerier();
1432
+ const querier = await this.pool.getQuerier();
1444
1433
  try {
1445
1434
  const { db } = querier;
1446
1435
  const collections = await db.listCollections({
@@ -1465,7 +1454,7 @@ class MongoSchemaIntrospector {
1465
1454
  }
1466
1455
  }
1467
1456
  async getTableNames() {
1468
- const querier = await this.querierPool.getQuerier();
1457
+ const querier = await this.pool.getQuerier();
1469
1458
  try {
1470
1459
  const { db } = querier;
1471
1460
  const collections = await db.listCollections().toArray();
@@ -1484,8 +1473,8 @@ class MongoSchemaIntrospector {
1484
1473
  * Stores migration state in a database table.
1485
1474
  * Uses the querier's dialect for escaping and placeholders.
1486
1475
  */ class DatabaseMigrationStorage {
1487
- constructor(querierPool, options = {}){
1488
- this.querierPool = querierPool;
1476
+ constructor(pool, options = {}){
1477
+ this.pool = pool;
1489
1478
  this.storageInitialized = false;
1490
1479
  this.tableName = options.tableName ?? 'uql_migrations';
1491
1480
  }
@@ -1493,7 +1482,7 @@ class MongoSchemaIntrospector {
1493
1482
  if (this.storageInitialized) {
1494
1483
  return;
1495
1484
  }
1496
- const querier = await this.querierPool.getQuerier();
1485
+ const querier = await this.pool.getQuerier();
1497
1486
  if (!isSqlQuerier(querier)) {
1498
1487
  await querier.release();
1499
1488
  throw new Error('DatabaseMigrationStorage requires a SQL-based querier');
@@ -1517,7 +1506,7 @@ class MongoSchemaIntrospector {
1517
1506
  }
1518
1507
  async executed() {
1519
1508
  await this.ensureStorage();
1520
- const querier = await this.querierPool.getQuerier();
1509
+ const querier = await this.pool.getQuerier();
1521
1510
  if (!isSqlQuerier(querier)) {
1522
1511
  await querier.release();
1523
1512
  throw new Error('DatabaseMigrationStorage requires a SQL-based querier');
@@ -1556,10 +1545,10 @@ class MongoSchemaIntrospector {
1556
1545
  /**
1557
1546
  * Main class for managing database migrations
1558
1547
  */ class Migrator {
1559
- constructor(querierPool, options = {}){
1560
- this.querierPool = querierPool;
1561
- this.dialect = options.dialect ?? querierPool.dialect ?? 'postgres';
1562
- this.storage = options.storage ?? new DatabaseMigrationStorage(querierPool, {
1548
+ constructor(pool, options = {}){
1549
+ this.pool = pool;
1550
+ this.dialect = options.dialect ?? pool.dialect ?? 'postgres';
1551
+ this.storage = options.storage ?? new DatabaseMigrationStorage(pool, {
1563
1552
  tableName: options.tableName
1564
1553
  });
1565
1554
  this.migrationsPath = options.migrationsPath ?? './migrations';
@@ -1576,15 +1565,15 @@ class MongoSchemaIntrospector {
1576
1565
  createIntrospector() {
1577
1566
  switch(this.dialect){
1578
1567
  case 'postgres':
1579
- return new PostgresSchemaIntrospector(this.querierPool);
1568
+ return new PostgresSchemaIntrospector(this.pool);
1580
1569
  case 'mysql':
1581
- return new MysqlSchemaIntrospector(this.querierPool);
1570
+ return new MysqlSchemaIntrospector(this.pool);
1582
1571
  case 'mariadb':
1583
- return new MariadbSchemaIntrospector(this.querierPool);
1572
+ return new MariadbSchemaIntrospector(this.pool);
1584
1573
  case 'sqlite':
1585
- return new SqliteSchemaIntrospector(this.querierPool);
1574
+ return new SqliteSchemaIntrospector(this.pool);
1586
1575
  case 'mongodb':
1587
- return new MongoSchemaIntrospector(this.querierPool);
1576
+ return new MongoSchemaIntrospector(this.pool);
1588
1577
  default:
1589
1578
  return undefined;
1590
1579
  }
@@ -1698,7 +1687,7 @@ class MongoSchemaIntrospector {
1698
1687
  * Run a single migration within a transaction
1699
1688
  */ async runMigration(migration, direction) {
1700
1689
  const startTime = Date.now();
1701
- const querier = await this.querierPool.getQuerier();
1690
+ const querier = await this.pool.getQuerier();
1702
1691
  if (!isSqlQuerier(querier)) {
1703
1692
  await querier.release();
1704
1693
  throw new Error('Migrator requires a SQL-based querier');
@@ -1840,7 +1829,7 @@ class MongoSchemaIntrospector {
1840
1829
  throw new Error('Schema generator not set. Call setSchemaGenerator() first.');
1841
1830
  }
1842
1831
  const entities = this.entities.length > 0 ? this.entities : getEntities();
1843
- const querier = await this.querierPool.getQuerier();
1832
+ const querier = await this.pool.getQuerier();
1844
1833
  if (!isSqlQuerier(querier)) {
1845
1834
  await querier.release();
1846
1835
  throw new Error('Migrator requires a SQL-based querier');
@@ -1912,7 +1901,7 @@ class MongoSchemaIntrospector {
1912
1901
  return filteredDiff;
1913
1902
  }
1914
1903
  async executeSyncStatements(statements, options) {
1915
- const querier = await this.querierPool.getQuerier();
1904
+ const querier = await this.pool.getQuerier();
1916
1905
  try {
1917
1906
  if (this.dialect === 'mongodb') {
1918
1907
  await this.executeMongoSyncStatements(statements, options, querier);