@uql/core 3.3.0 → 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.
- package/CHANGELOG.md +5 -2
- package/README.md +14 -12
- package/dist/browser/uql-browser.min.js +83 -94
- package/dist/browser/uql-browser.min.js.map +1 -1
- package/dist/d1/d1Querier.d.ts +49 -0
- package/dist/d1/d1Querier.d.ts.map +1 -0
- package/dist/d1/d1Querier.js +40 -0
- package/dist/d1/d1Querier.js.map +1 -0
- package/dist/d1/d1QuerierPool.d.ts +10 -0
- package/dist/d1/d1QuerierPool.d.ts.map +1 -0
- package/dist/d1/d1QuerierPool.js +16 -0
- package/dist/d1/d1QuerierPool.js.map +1 -0
- package/dist/d1/index.d.ts +3 -0
- package/dist/d1/index.d.ts.map +1 -0
- package/dist/d1/index.js +3 -0
- package/dist/d1/index.js.map +1 -0
- package/dist/libsql/index.d.ts +3 -0
- package/dist/libsql/index.d.ts.map +1 -0
- package/dist/libsql/index.js +3 -0
- package/dist/libsql/index.js.map +1 -0
- package/dist/libsql/libsqlQuerier.d.ts +21 -0
- package/dist/libsql/libsqlQuerier.d.ts.map +1 -0
- package/dist/libsql/libsqlQuerier.js +81 -0
- package/dist/libsql/libsqlQuerier.js.map +1 -0
- package/dist/libsql/libsqlQuerierPool.d.ts +12 -0
- package/dist/libsql/libsqlQuerierPool.d.ts.map +1 -0
- package/dist/libsql/libsqlQuerierPool.js +19 -0
- package/dist/libsql/libsqlQuerierPool.js.map +1 -0
- package/dist/neon/index.d.ts +3 -0
- package/dist/neon/index.d.ts.map +1 -0
- package/dist/neon/index.js +3 -0
- package/dist/neon/index.js.map +1 -0
- package/dist/neon/neonQuerier.d.ts +18 -0
- package/dist/neon/neonQuerier.d.ts.map +1 -0
- package/dist/neon/neonQuerier.js +40 -0
- package/dist/neon/neonQuerier.js.map +1 -0
- package/dist/neon/neonQuerierPool.d.ts +11 -0
- package/dist/neon/neonQuerierPool.d.ts.map +1 -0
- package/dist/neon/neonQuerierPool.js +17 -0
- package/dist/neon/neonQuerierPool.js.map +1 -0
- 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.
|
|
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
|
-
|
|
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
|
|
package/README.md
CHANGED
|
@@ -2,13 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://uql.app)
|
|
4
4
|
|
|
5
|
-
[](https://github.com/rogerpadilla/uql) [](https://github.com/rogerpadilla/uql) [](https://coveralls.io/github/rogerpadilla/uql?branch=main) [](https://github.com/rogerpadilla/uql/blob/main/LICENSE) [](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
|
|
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
|
-
|
|
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
|
|
60
|
-
|
|
|
61
|
-
| `PostgreSQL` | `pg`
|
|
62
|
-
| `
|
|
63
|
-
| `MariaDB`
|
|
64
|
-
| `
|
|
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
|
|
|
@@ -1,108 +1,97 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
35
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Escape an identifier (table name, column name, etc.)
|
|
18
|
+
*/ escapeId(identifier) {
|
|
19
|
+
return escapeSqlId(identifier, this.escapeIdChar);
|
|
36
20
|
}
|
|
37
|
-
|
|
21
|
+
generateCreateTable(entity, options = {}) {
|
|
38
22
|
const meta = getMeta(entity);
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
51
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
if (
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
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) {
|