uql-orm 0.34.1 → 0.36.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/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  </picture>
8
8
  </a>
9
9
 
10
- <h3>The JSON-native TypeScript ORM</h3>
10
+ <h3>The JSON-native ORM</h3>
11
11
 
12
12
  <p>Queries are plain JSON, typed to the leaf. Unified across SQL databases and MongoDB.</p>
13
13
 
@@ -1,86 +1,65 @@
1
+ import type { AbstractSqlDialect } from '../../dialect/index.js';
2
+ import type { SqlDialectName } from '../../type/index.js';
1
3
  /**
2
- * SQL Expressions
3
- *
4
- * Type-safe SQL expressions for default values and other uses.
5
- * Provides a `t` helper with common expressions.
4
+ * A {@link ColumnSchema.defaultValue} that is SQL rather than a literal. Kinds are symbolic: the
5
+ * dialect names the spelling and {@link formatDefaultValue} renders one at DDL time.
6
6
  */
7
+ export type SqlExpressionKind = 'now' | 'currentDate' | 'currentTime' | 'uuid' | 'uuidv7' | 'onUpdateNow' | 'raw';
8
+ /** Every kind's DDL spelling, `null` where an engine has none. `raw` carries its own text instead. */
9
+ export type SqlExpressionMap = Readonly<Record<Exclude<SqlExpressionKind, 'raw'>, string | null>>;
10
+ /** How one engine renders a DDL default. A new per-dialect rule is a field here, not a second table. */
11
+ export type DialectDefaults = {
12
+ /** Spelling of each kind, `null` where the engine has none. */
13
+ readonly expressions: SqlExpressionMap;
14
+ /** Column types whose `DEFAULT` this engine takes only as a parenthesized expression. */
15
+ readonly wrapTypes?: RegExp;
16
+ };
7
17
  /**
8
- * Represents a raw SQL expression (not a literal value).
18
+ * Looked up by name rather than carried on the dialect, which keeps DDL data out of the query
19
+ * bundle - the same split that keeps `CANONICAL_TO_SQL` in `schema/canonicalType.ts`. `uuidv7()` is
20
+ * Postgres 18+ and `UUID_v7()` MariaDB 11.7+; a server below those rejects it itself, the version
21
+ * not being knowable here.
22
+ */
23
+ export declare const DIALECT_DEFAULTS: Readonly<Record<SqlDialectName, DialectDefaults>>;
24
+ /**
25
+ * A DDL default that is SQL rather than a literal. A class, not a plain object, so a JSON default
26
+ * cannot masquerade as one: `defaultValue` accepts `unknown`.
9
27
  */
10
28
  export declare class SqlExpression {
11
- readonly sql: string;
12
- constructor(sql: string);
13
- toString(): string;
14
- /**
15
- * Check if a value is a SQL expression.
16
- */
29
+ readonly kind: SqlExpressionKind;
30
+ readonly sql?: string | undefined;
31
+ /** `sql` is set only for the `raw` kind, which carries its own text verbatim. */
32
+ constructor(kind: SqlExpressionKind, sql?: string | undefined);
17
33
  static isExpression(value: unknown): value is SqlExpression;
18
34
  }
19
35
  /**
20
- * Helper object for common SQL expressions.
21
- * Use in migrations: `table.timestamp('createdAt').defaultValue(t.now())`
36
+ * Symbolic DDL defaults. Each builds a token the dialect spells at DDL time, so `expr.uuid()` is
37
+ * `gen_random_uuid()` on Postgres and `UUID()` on MySQL from one migration.
38
+ *
39
+ * Use in migrations: `t.timestamp('createdAt', { defaultValue: expr.now() })`
22
40
  */
23
- export declare const t: {
24
- /**
25
- * Current timestamp (NOW() or CURRENT_TIMESTAMP depending on dialect).
26
- */
27
- now(): SqlExpression;
28
- /**
29
- * Current date (CURRENT_DATE).
30
- */
31
- currentDate(): SqlExpression;
32
- /**
33
- * Current time (CURRENT_TIME).
34
- */
35
- currentTime(): SqlExpression;
36
- /**
37
- * NULL value.
38
- */
39
- null(): SqlExpression;
40
- /**
41
- * Boolean true.
42
- */
43
- true(): SqlExpression;
44
- /**
45
- * Boolean false.
46
- */
47
- false(): SqlExpression;
48
- /**
49
- * Generate UUID (Postgres: gen_random_uuid(), MySQL: UUID()).
50
- */
51
- uuid(): SqlExpression;
52
- /**
53
- * Auto-generate UUID for MySQL.
54
- */
55
- mysqlUuid(): SqlExpression;
56
- /**
57
- * Raw SQL expression.
58
- * Use for custom expressions not covered by helpers.
59
- */
60
- raw(sql: string): SqlExpression;
61
- /**
62
- * String literal with proper escaping.
63
- */
64
- literal(value: string): SqlExpression;
65
- /**
66
- * Numeric literal.
67
- */
68
- number(value: number): SqlExpression;
69
- /**
70
- * Empty JSON object.
71
- */
72
- emptyObject(): SqlExpression;
73
- /**
74
- * Empty JSON array.
75
- */
76
- emptyArray(): SqlExpression;
41
+ export declare const expr: {
42
+ /** Current timestamp. */
43
+ now: () => SqlExpression;
44
+ /** Current date. */
45
+ currentDate: () => SqlExpression;
46
+ /** Current time. */
47
+ currentTime: () => SqlExpression;
48
+ /** Generated UUID. SQLite has no built-in one and throws; pass `expr.raw` there. */
49
+ uuid: () => SqlExpression;
77
50
  /**
78
- * MySQL: ON UPDATE CURRENT_TIMESTAMP.
51
+ * Time-ordered UUID, which indexes far better than a random one as a key. Postgres 18+ and
52
+ * MariaDB 11.7+ only; MySQL, SQLite and CockroachDB have no such function and throw.
79
53
  */
80
- onUpdateNow(): SqlExpression;
54
+ uuidv7: () => SqlExpression;
55
+ /** MySQL's `ON UPDATE CURRENT_TIMESTAMP`. Throws on dialects without it. */
56
+ onUpdateNow: () => SqlExpression;
57
+ /** SQL taken verbatim, for anything the kinds above do not cover. Not portable by definition. */
58
+ raw: (sql: string) => SqlExpression;
81
59
  };
82
60
  /**
83
- * Format a default value for SQL.
84
- * Handles SqlExpression vs literal values.
61
+ * The one place a DDL default becomes SQL: an expression through the dialect's own spelling, anything
62
+ * else as a literal, so `true` is `1` where booleans are integers. `columnType` decides only whether
63
+ * the result needs wrapping, which MySQL demands on its large types whatever the value.
85
64
  */
86
- export declare function formatDefaultValue(value: unknown): string;
65
+ export declare function formatDefaultValue(value: unknown, dialect: AbstractSqlDialect, columnType?: string): string;
@@ -1,149 +1,120 @@
1
+ const ANSI = {
2
+ now: 'CURRENT_TIMESTAMP',
3
+ currentDate: 'CURRENT_DATE',
4
+ currentTime: 'CURRENT_TIME',
5
+ uuid: null,
6
+ uuidv7: null,
7
+ onUpdateNow: null,
8
+ };
9
+ const PG = { ...ANSI, uuid: 'gen_random_uuid()' };
10
+ const MYSQL = {
11
+ ...ANSI,
12
+ uuid: 'UUID()',
13
+ onUpdateNow: 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
14
+ };
15
+ /** MySQL 8.0.13+ rejects `DEFAULT 'x'` on these but accepts `DEFAULT ('x')`, whatever the value. */
16
+ const MYSQL_LARGE_TYPES = /^\s*(TINY|MEDIUM|LONG)?(TEXT|BLOB)|^\s*(JSON|GEOMETRY)\b/i;
1
17
  /**
2
- * SQL Expressions
3
- *
4
- * Type-safe SQL expressions for default values and other uses.
5
- * Provides a `t` helper with common expressions.
18
+ * Looked up by name rather than carried on the dialect, which keeps DDL data out of the query
19
+ * bundle - the same split that keeps `CANONICAL_TO_SQL` in `schema/canonicalType.ts`. `uuidv7()` is
20
+ * Postgres 18+ and `UUID_v7()` MariaDB 11.7+; a server below those rejects it itself, the version
21
+ * not being knowable here.
6
22
  */
23
+ export const DIALECT_DEFAULTS = {
24
+ postgres: { expressions: { ...PG, uuidv7: 'uuidv7()' } },
25
+ cockroachdb: { expressions: PG },
26
+ mysql: { expressions: MYSQL, wrapTypes: MYSQL_LARGE_TYPES },
27
+ mariadb: { expressions: { ...MYSQL, uuidv7: 'UUID_v7()' }, wrapTypes: MYSQL_LARGE_TYPES },
28
+ sqlite: { expressions: ANSI },
29
+ };
7
30
  /**
8
- * Represents a raw SQL expression (not a literal value).
31
+ * A DDL default that is SQL rather than a literal. A class, not a plain object, so a JSON default
32
+ * cannot masquerade as one: `defaultValue` accepts `unknown`.
9
33
  */
10
34
  export class SqlExpression {
35
+ kind;
11
36
  sql;
12
- constructor(sql) {
37
+ /** `sql` is set only for the `raw` kind, which carries its own text verbatim. */
38
+ constructor(kind, sql) {
39
+ this.kind = kind;
13
40
  this.sql = sql;
14
41
  }
15
- toString() {
16
- return this.sql;
17
- }
18
- /**
19
- * Check if a value is a SQL expression.
20
- */
21
42
  static isExpression(value) {
22
43
  return value instanceof SqlExpression;
23
44
  }
24
45
  }
25
46
  /**
26
- * Helper object for common SQL expressions.
27
- * Use in migrations: `table.timestamp('createdAt').defaultValue(t.now())`
47
+ * Symbolic DDL defaults. Each builds a token the dialect spells at DDL time, so `expr.uuid()` is
48
+ * `gen_random_uuid()` on Postgres and `UUID()` on MySQL from one migration.
49
+ *
50
+ * Use in migrations: `t.timestamp('createdAt', { defaultValue: expr.now() })`
28
51
  */
29
- export const t = {
30
- /**
31
- * Current timestamp (NOW() or CURRENT_TIMESTAMP depending on dialect).
32
- */
33
- now() {
34
- return new SqlExpression('CURRENT_TIMESTAMP');
35
- },
36
- /**
37
- * Current date (CURRENT_DATE).
38
- */
39
- currentDate() {
40
- return new SqlExpression('CURRENT_DATE');
41
- },
42
- /**
43
- * Current time (CURRENT_TIME).
44
- */
45
- currentTime() {
46
- return new SqlExpression('CURRENT_TIME');
47
- },
48
- /**
49
- * NULL value.
50
- */
51
- null() {
52
- return new SqlExpression('NULL');
53
- },
54
- /**
55
- * Boolean true.
56
- */
57
- true() {
58
- return new SqlExpression('TRUE');
59
- },
60
- /**
61
- * Boolean false.
62
- */
63
- false() {
64
- return new SqlExpression('FALSE');
65
- },
66
- /**
67
- * Generate UUID (Postgres: gen_random_uuid(), MySQL: UUID()).
68
- */
69
- uuid() {
70
- return new SqlExpression('gen_random_uuid()');
71
- },
52
+ export const expr = {
53
+ /** Current timestamp. */
54
+ now: () => new SqlExpression('now'),
55
+ /** Current date. */
56
+ currentDate: () => new SqlExpression('currentDate'),
57
+ /** Current time. */
58
+ currentTime: () => new SqlExpression('currentTime'),
59
+ /** Generated UUID. SQLite has no built-in one and throws; pass `expr.raw` there. */
60
+ uuid: () => new SqlExpression('uuid'),
72
61
  /**
73
- * Auto-generate UUID for MySQL.
62
+ * Time-ordered UUID, which indexes far better than a random one as a key. Postgres 18+ and
63
+ * MariaDB 11.7+ only; MySQL, SQLite and CockroachDB have no such function and throw.
74
64
  */
75
- mysqlUuid() {
76
- return new SqlExpression('UUID()');
77
- },
78
- /**
79
- * Raw SQL expression.
80
- * Use for custom expressions not covered by helpers.
81
- */
82
- raw(sql) {
83
- return new SqlExpression(sql);
84
- },
85
- /**
86
- * String literal with proper escaping.
87
- */
88
- literal(value) {
89
- // Escape single quotes by doubling them
90
- const escaped = value.replace(/'/g, "''");
91
- return new SqlExpression(`'${escaped}'`);
92
- },
93
- /**
94
- * Numeric literal.
95
- */
96
- number(value) {
97
- return new SqlExpression(String(value));
98
- },
99
- /**
100
- * Empty JSON object.
101
- */
102
- emptyObject() {
103
- return new SqlExpression("'{}'::jsonb");
104
- },
105
- /**
106
- * Empty JSON array.
107
- */
108
- emptyArray() {
109
- return new SqlExpression("'[]'::jsonb");
110
- },
111
- /**
112
- * MySQL: ON UPDATE CURRENT_TIMESTAMP.
113
- */
114
- onUpdateNow() {
115
- return new SqlExpression('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP');
116
- },
65
+ uuidv7: () => new SqlExpression('uuidv7'),
66
+ /** MySQL's `ON UPDATE CURRENT_TIMESTAMP`. Throws on dialects without it. */
67
+ onUpdateNow: () => new SqlExpression('onUpdateNow'),
68
+ /** SQL taken verbatim, for anything the kinds above do not cover. Not portable by definition. */
69
+ raw: (sql) => new SqlExpression('raw', sql),
117
70
  };
118
71
  /**
119
- * Format a default value for SQL.
120
- * Handles SqlExpression vs literal values.
72
+ * The one place a DDL default becomes SQL: an expression through the dialect's own spelling, anything
73
+ * else as a literal, so `true` is `1` where booleans are integers. `columnType` decides only whether
74
+ * the result needs wrapping, which MySQL demands on its large types whatever the value.
75
+ */
76
+ export function formatDefaultValue(value, dialect, columnType) {
77
+ const sql = defaultLiteral(value, dialect);
78
+ const { wrapTypes } = DIALECT_DEFAULTS[dialect.dialectName];
79
+ return columnType !== undefined && wrapTypes?.test(columnType) ? `(${sql})` : sql;
80
+ }
81
+ /**
82
+ * Quoting is the dialect's `escape`, so a backslash in a default is escaped the way the engine reads
83
+ * it - MySQL takes `'a\b'` as a backspace where Postgres takes it literally. Only the cases `escape`
84
+ * cannot serve stay here: a boolean is `1` where booleans are integers, and a plain object or array
85
+ * is JSON rather than the throw and the IN-list `escape` gives them.
121
86
  */
122
- export function formatDefaultValue(value) {
87
+ function defaultLiteral(value, dialect) {
123
88
  if (value === undefined || value === null) {
124
89
  return 'NULL';
125
90
  }
126
91
  if (SqlExpression.isExpression(value)) {
127
- return value.sql;
128
- }
129
- if (typeof value === 'string') {
130
- // Escape single quotes for string literals
131
- const escaped = value.replace(/'/g, "''");
132
- return `'${escaped}'`;
133
- }
134
- if (typeof value === 'number') {
135
- return String(value);
92
+ return expressionSql(value, dialect);
136
93
  }
137
94
  if (typeof value === 'boolean') {
138
- return value ? 'TRUE' : 'FALSE';
95
+ return dialect.booleanLiteral === 'native' ? (value ? 'TRUE' : 'FALSE') : value ? '1' : '0';
139
96
  }
140
97
  if (value instanceof Date) {
141
- return `'${value.toISOString()}'`;
98
+ return dialect.escape(ddlTimestamp(value));
142
99
  }
143
100
  if (typeof value === 'object') {
144
- // JSON value
145
- const json = JSON.stringify(value).replace(/'/g, "''");
146
- return `'${json}'`;
101
+ return dialect.escape(JSON.stringify(value));
102
+ }
103
+ return dialect.escape(value);
104
+ }
105
+ /**
106
+ * `YYYY-MM-DD HH:mm:ss.SSS` in UTC. Not `toISOString`, whose `T` and `Z` MySQL rejects outright
107
+ * ("Invalid default value"), and not `escape`'s local-time form, which would make the DDL depend on
108
+ * the machine that generated it.
109
+ */
110
+ function ddlTimestamp(date) {
111
+ return date.toISOString().replace('T', ' ').replace('Z', '');
112
+ }
113
+ function expressionSql(expression, dialect) {
114
+ const { expressions } = DIALECT_DEFAULTS[dialect.dialectName];
115
+ const sql = expression.kind === 'raw' ? expression.sql : expressions[expression.kind];
116
+ if (sql == null) {
117
+ throw new TypeError(`${dialect.dialectName} has no '${expression.kind}' default; pass expr.raw(...) with SQL this engine accepts`);
147
118
  }
148
- return String(value);
119
+ return sql;
149
120
  }
@@ -40,6 +40,7 @@ export declare class TableBuilder implements ITableBuilder {
40
40
  private add;
41
41
  createdAt(): IColumnBuilder;
42
42
  updatedAt(): IColumnBuilder;
43
+ private timestampNow;
43
44
  timestamps(): void;
44
45
  primaryKey(columns: string[]): this;
45
46
  unique(columns: readonly IndexColumnInput[], options?: string | IndexOptions): this;
@@ -6,7 +6,7 @@
6
6
  import { normalizeIndexColumn } from '../../util/index.js';
7
7
  import { derivedIndexName } from '../../util/sql.util.js';
8
8
  import { ColumnBuilder } from './columnBuilder.js';
9
- import { t } from './expressions.js';
9
+ import { expr } from './expressions.js';
10
10
  /**
11
11
  * Builder for table-level foreign keys.
12
12
  */
@@ -137,10 +137,13 @@ export class TableBuilder {
137
137
  return column;
138
138
  }
139
139
  createdAt() {
140
- return this.add('createdAt', { category: 'timestamp' }, { defaultValue: t.now() });
140
+ return this.timestampNow('createdAt');
141
141
  }
142
142
  updatedAt() {
143
- return this.add('updatedAt', { category: 'timestamp' }, { defaultValue: t.now() });
143
+ return this.timestampNow('updatedAt');
144
+ }
145
+ timestampNow(name) {
146
+ return this.add(name, { category: 'timestamp' }, { defaultValue: expr.now() });
144
147
  }
145
148
  timestamps() {
146
149
  this.createdAt();
@@ -198,10 +198,10 @@ export interface BuilderMigrationDefinition {
198
198
  * ```ts
199
199
  * export default defineBuilderMigration({
200
200
  * async up(m) {
201
- * await m.createTable('users', (table) => {
202
- * table.id();
203
- * table.string('email', 255).unique();
204
- * table.timestamps();
201
+ * await m.createTable('users', (t) => {
202
+ * t.id();
203
+ * t.string('email', { length: 255 }).unique();
204
+ * t.timestamps();
205
205
  * });
206
206
  * },
207
207
  * async down(m) {
@@ -571,10 +571,10 @@ export function defineMigration(migration) {
571
571
  * ```ts
572
572
  * export default defineBuilderMigration({
573
573
  * async up(m) {
574
- * await m.createTable('users', (table) => {
575
- * table.id();
576
- * table.string('email', 255).unique();
577
- * table.timestamps();
574
+ * await m.createTable('users', (t) => {
575
+ * t.id();
576
+ * t.string('email', { length: 255 }).unique();
577
+ * t.timestamps();
578
578
  * });
579
579
  * },
580
580
  * async down(m) {
@@ -76,6 +76,9 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
76
76
  * rules are the same everywhere, and having them written twice is how the two paths drifted.
77
77
  */
78
78
  private renderColumn;
79
+ /** ` DEFAULT <sql>`, or nothing where the column declares none. Empty rather than `DEFAULT NULL`
80
+ * so an absent default stays absent - `defaultValue: null` is the way to ask for one. */
81
+ private defaultClause;
79
82
  getSqlType(field: FieldOptions, fieldType?: unknown): string;
80
83
  /**
81
84
  * Generate ALTER COLUMN statements (database-specific)
@@ -85,10 +88,6 @@ export declare class SqlSchemaGenerator implements SqlDdlGenerator {
85
88
  * Generate column comment clause (if supported)
86
89
  */
87
90
  generateColumnComment(columnName: string, comment: string): string;
88
- /**
89
- * Format a default value for SQL
90
- */
91
- formatDefaultValue(value: unknown): string;
92
91
  /**
93
92
  * Compare an entity with a database table node and return the differences.
94
93
  */
@@ -4,7 +4,7 @@ import { areTypesEqual, canonicalToSql, fieldOptionsToCanonical, isVectorCategor
4
4
  import { buildSchemaAST } from '../schema/schemaASTBuilder.js';
5
5
  import { getKeys, isAutoIncrement, qualifyName } from '../util/index.js';
6
6
  import { derivedForeignKeyName } from '../util/sql.util.js';
7
- import { formatDefaultValue } from './builder/expressions.js';
7
+ import { formatDefaultValue, SqlExpression } from './builder/expressions.js';
8
8
  import { fullColumnDefinitionToNode, tableDefinitionToNode } from './generator/definitionToNode.js';
9
9
  import { indexNodeToSchema } from './generator/indexNodeToSchema.js';
10
10
  /**
@@ -243,14 +243,19 @@ export class SqlSchemaGenerator {
243
243
  if (column.isUnique && !column.isPrimaryKey) {
244
244
  def += ' UNIQUE';
245
245
  }
246
- if (column.defaultValue !== undefined) {
247
- def += ` DEFAULT ${this.formatDefaultValue(column.defaultValue)}`;
248
- }
246
+ def += this.defaultClause(column);
249
247
  if (column.comment) {
250
248
  def += this.generateColumnComment(column.name, column.comment);
251
249
  }
252
250
  return def;
253
251
  }
252
+ /** ` DEFAULT <sql>`, or nothing where the column declares none. Empty rather than `DEFAULT NULL`
253
+ * so an absent default stays absent - `defaultValue: null` is the way to ask for one. */
254
+ defaultClause(column) {
255
+ return column.defaultValue === undefined
256
+ ? ''
257
+ : ` DEFAULT ${formatDefaultValue(column.defaultValue, this.dialect, column.type)}`;
258
+ }
254
259
  getSqlType(field, fieldType) {
255
260
  // If field has a reference, inherit type from the target primary key
256
261
  if (field.references) {
@@ -288,7 +293,7 @@ export class SqlSchemaGenerator {
288
293
  statements.push(`ALTER TABLE ${table} ALTER COLUMN ${colName} SET NOT NULL;`);
289
294
  }
290
295
  if (column.defaultValue !== undefined) {
291
- statements.push(`ALTER TABLE ${table} ALTER COLUMN ${colName} SET DEFAULT ${this.formatDefaultValue(column.defaultValue)};`);
296
+ statements.push(`ALTER TABLE ${table} ALTER COLUMN ${colName} SET${this.defaultClause(column)};`);
292
297
  }
293
298
  else {
294
299
  statements.push(`ALTER TABLE ${table} ALTER COLUMN ${colName} DROP DEFAULT;`);
@@ -307,15 +312,6 @@ export class SqlSchemaGenerator {
307
312
  }
308
313
  return '';
309
314
  }
310
- /**
311
- * Format a default value for SQL
312
- */
313
- formatDefaultValue(value) {
314
- if (this.dialect.booleanLiteral === 'integer' && typeof value === 'boolean') {
315
- return value ? '1' : '0';
316
- }
317
- return formatDefaultValue(value);
318
- }
319
315
  /**
320
316
  * Compare an entity with a database table node and return the differences.
321
317
  */
@@ -467,9 +463,12 @@ export class SqlSchemaGenerator {
467
463
  return true;
468
464
  if (current === undefined || desired === undefined)
469
465
  return current === desired;
470
- const normalize = (val) => {
471
- if (val === null)
466
+ const normalize = (value) => {
467
+ if (value === null)
472
468
  return 'null';
469
+ // Render first: the desired side may be a symbolic expression, the current side is always the
470
+ // engine's own text, and `{"kind":"now"}` matches no spelling of `CURRENT_TIMESTAMP`.
471
+ const val = SqlExpression.isExpression(value) ? formatDefaultValue(value, this.dialect) : value;
473
472
  if (typeof val === 'string') {
474
473
  let s = val.replace(/::[a-z_]+(\s+[a-z_]+)*(\[\])?$/i, '');
475
474
  s = s.replace(/^'(.*)'$/, '$1');
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "uql-orm",
3
3
  "homepage": "https://uql-orm.dev",
4
- "description": "JSON-native TypeScript ORM for Node.js, Bun and Deno. Supports PostgreSQL, PGlite, MySQL, MariaDB, SQLite, CockroachDB, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.",
4
+ "description": "JSON-native ORM for Node.js, Bun and Deno. Supports PostgreSQL, PGlite, MySQL, MariaDB, SQLite, CockroachDB, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.",
5
5
  "license": "MIT",
6
- "version": "0.34.1",
6
+ "version": "0.36.0",
7
7
  "type": "module",
8
8
  "engines": {
9
9
  "node": ">=24"