uql-orm 0.21.0 → 0.23.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 (214) hide show
  1. package/README.md +5 -4
  2. package/dist/browser/uql-browser.min.js.map +3 -3
  3. package/dist/bunSql/bunSqlCockroachDialect.d.ts +2 -2
  4. package/dist/bunSql/bunSqlCockroachDialect.js +4 -10
  5. package/dist/bunSql/bunSqlPostgresDialect.d.ts +2 -2
  6. package/dist/bunSql/bunSqlPostgresDialect.js +4 -10
  7. package/dist/cockroachdb/cockroachDialect.d.ts +20 -4
  8. package/dist/cockroachdb/cockroachDialect.js +33 -18
  9. package/dist/d1/d1Querier.d.ts +4 -3
  10. package/dist/d1/d1Querier.js +0 -3
  11. package/dist/d1/d1SqliteDialect.d.ts +7 -0
  12. package/dist/d1/d1SqliteDialect.js +9 -0
  13. package/dist/dialect/abstractDialect.d.ts +8 -3
  14. package/dist/dialect/abstractDialect.js +9 -4
  15. package/dist/dialect/abstractSqlDialect.d.ts +49 -8
  16. package/dist/dialect/abstractSqlDialect.js +95 -47
  17. package/dist/dialect/index.d.ts +1 -0
  18. package/dist/dialect/index.js +1 -0
  19. package/dist/dialect/indexSqlDialect.d.ts +57 -0
  20. package/dist/dialect/indexSqlDialect.js +101 -0
  21. package/dist/dialect/mysqlLikeSqlDialect.d.ts +26 -1
  22. package/dist/dialect/mysqlLikeSqlDialect.js +57 -1
  23. package/dist/dialect/pgLikeSqlDialect.d.ts +37 -22
  24. package/dist/dialect/pgLikeSqlDialect.js +97 -68
  25. package/dist/dialect/vectorCast.d.ts +21 -0
  26. package/dist/dialect/vectorCast.js +28 -0
  27. package/dist/dialect/vectorSqlDialect.d.ts +10 -3
  28. package/dist/dialect/vectorSqlDialect.js +19 -8
  29. package/dist/entity/decorator/bag.d.ts +35 -0
  30. package/dist/entity/decorator/bag.js +54 -0
  31. package/dist/entity/decorator/entity.d.ts +28 -2
  32. package/dist/entity/decorator/entity.js +40 -2
  33. package/dist/entity/decorator/members.d.ts +51 -0
  34. package/dist/entity/decorator/members.js +51 -0
  35. package/dist/entity/index.d.ts +3 -1
  36. package/dist/entity/index.js +3 -1
  37. package/dist/entity/metadata/definition.d.ts +23 -8
  38. package/dist/entity/metadata/definition.js +56 -100
  39. package/dist/http/contract.js +1 -2
  40. package/dist/http/handler.js +5 -26
  41. package/dist/http/query.js +1 -1
  42. package/dist/libsql/libsqlDialect.d.ts +12 -2
  43. package/dist/libsql/libsqlDialect.js +14 -2
  44. package/dist/libsql/libsqlQuerier.d.ts +9 -22
  45. package/dist/libsql/libsqlQuerier.js +9 -85
  46. package/dist/libsql/libsqlQuerierPool.d.ts +10 -8
  47. package/dist/libsql/libsqlQuerierPool.js +17 -15
  48. package/dist/maria/mariaDialect.d.ts +26 -4
  49. package/dist/maria/mariaDialect.js +54 -15
  50. package/dist/migrate/acquireQuerierForMigrations.d.ts +13 -1
  51. package/dist/migrate/acquireQuerierForMigrations.js +28 -0
  52. package/dist/migrate/builder/migrationBuilder.d.ts +9 -29
  53. package/dist/migrate/builder/migrationBuilder.js +58 -76
  54. package/dist/migrate/builder/splitSqlStatements.d.ts +0 -2
  55. package/dist/migrate/builder/splitSqlStatements.js +0 -2
  56. package/dist/migrate/builder/tableBuilder.d.ts +12 -2
  57. package/dist/migrate/builder/tableBuilder.js +71 -119
  58. package/dist/migrate/builder/types.d.ts +30 -41
  59. package/dist/migrate/cli-config.js +24 -8
  60. package/dist/migrate/cli.d.ts +3 -2
  61. package/dist/migrate/cli.js +26 -49
  62. package/dist/migrate/codegen/entityCodeGenerator.d.ts +0 -4
  63. package/dist/migrate/codegen/entityCodeGenerator.js +8 -67
  64. package/dist/migrate/codegen/fieldOptionsSource.d.ts +10 -0
  65. package/dist/migrate/codegen/fieldOptionsSource.js +55 -0
  66. package/dist/migrate/codegen/index.d.ts +0 -3
  67. package/dist/migrate/codegen/index.js +0 -6
  68. package/dist/migrate/drift/driftDetector.d.ts +10 -1
  69. package/dist/migrate/drift/driftDetector.js +22 -5
  70. package/dist/migrate/generator/indexNodeToSchema.d.ts +9 -0
  71. package/dist/migrate/generator/indexNodeToSchema.js +14 -0
  72. package/dist/migrate/generator/mongoCommand.d.ts +57 -0
  73. package/dist/migrate/generator/mongoCommand.js +26 -0
  74. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +9 -20
  75. package/dist/migrate/generator/mongoSchemaGenerator.js +40 -82
  76. package/dist/migrate/index.d.ts +0 -1
  77. package/dist/migrate/index.js +0 -1
  78. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.d.ts +30 -11
  79. package/dist/migrate/introspection/abstractSqlSchemaIntrospector.js +61 -57
  80. package/dist/migrate/introspection/baseSqlIntrospector.d.ts +0 -1
  81. package/dist/migrate/introspection/baseSqlIntrospector.js +3 -12
  82. package/dist/migrate/introspection/mongoIntrospector.js +9 -15
  83. package/dist/migrate/introspection/mysqlIntrospector.d.ts +5 -8
  84. package/dist/migrate/introspection/mysqlIntrospector.js +4 -10
  85. package/dist/migrate/introspection/postgresIntrospector.d.ts +5 -8
  86. package/dist/migrate/introspection/postgresIntrospector.js +4 -10
  87. package/dist/migrate/introspection/sqliteIntrospector.d.ts +6 -5
  88. package/dist/migrate/introspection/sqliteIntrospector.js +26 -18
  89. package/dist/migrate/migrator.d.ts +23 -3
  90. package/dist/migrate/migrator.js +101 -150
  91. package/dist/migrate/schemaGenerator.d.ts +26 -37
  92. package/dist/migrate/schemaGenerator.js +69 -174
  93. package/dist/migrate/schemaGeneratorAsync.d.ts +2 -2
  94. package/dist/migrate/schemaGeneratorAsync.js +3 -3
  95. package/dist/migrate/storage/databaseStorage.d.ts +2 -0
  96. package/dist/migrate/storage/databaseStorage.js +12 -27
  97. package/dist/mongo/mongoDialect.d.ts +2 -0
  98. package/dist/mongo/mongoDialect.js +14 -3
  99. package/dist/mongo/mongodbQuerier.js +171 -229
  100. package/dist/mysql/mysqlDialect.d.ts +8 -0
  101. package/dist/mysql/mysqlDialect.js +13 -0
  102. package/dist/nestjs/uqlContextInterceptor.js +58 -22
  103. package/dist/nestjs/uqlModule.d.ts +2 -11
  104. package/dist/nestjs/uqlModule.js +93 -44
  105. package/dist/postgres/postgresDialect.js +2 -2
  106. package/dist/querier/abstractQuerier.d.ts +28 -0
  107. package/dist/querier/abstractQuerier.js +47 -22
  108. package/dist/querier/abstractQuerierPool.d.ts +3 -0
  109. package/dist/querier/abstractQuerierPool.js +5 -3
  110. package/dist/querier/abstractSqlQuerier.d.ts +1 -3
  111. package/dist/querier/abstractSqlQuerier.js +48 -96
  112. package/dist/querier/index.d.ts +3 -1
  113. package/dist/querier/index.js +3 -1
  114. package/dist/querier/querierContext.browser.d.ts +12 -0
  115. package/dist/querier/querierContext.browser.js +18 -0
  116. package/dist/querier/querierContext.d.ts +22 -0
  117. package/dist/querier/querierContext.js +42 -0
  118. package/dist/querier/queryError.d.ts +22 -0
  119. package/dist/querier/queryError.js +20 -0
  120. package/dist/querier/transactional.d.ts +26 -0
  121. package/dist/querier/transactional.js +43 -0
  122. package/dist/schema/canonicalType.d.ts +14 -2
  123. package/dist/schema/canonicalType.js +41 -31
  124. package/dist/schema/index.d.ts +1 -1
  125. package/dist/schema/schemaASTBuilder.d.ts +9 -2
  126. package/dist/schema/schemaASTBuilder.js +58 -49
  127. package/dist/schema/types.d.ts +10 -58
  128. package/dist/sqlite/abstractSqliteQuerier.d.ts +46 -0
  129. package/dist/sqlite/abstractSqliteQuerier.js +44 -0
  130. package/dist/sqlite/bunSqliteAdapter.bun.d.ts +26 -0
  131. package/dist/sqlite/bunSqliteAdapter.bun.js +25 -0
  132. package/dist/sqlite/hranaQuerier.d.ts +54 -0
  133. package/dist/sqlite/hranaQuerier.js +68 -0
  134. package/dist/sqlite/hranaQuerierPool.d.ts +21 -0
  135. package/dist/sqlite/hranaQuerierPool.js +25 -0
  136. package/dist/sqlite/index.d.ts +3 -1
  137. package/dist/sqlite/index.js +3 -1
  138. package/dist/sqlite/localSqliteQuerierPool.d.ts +31 -0
  139. package/dist/sqlite/localSqliteQuerierPool.js +34 -0
  140. package/dist/sqlite/nodeSqliteAdapter.d.ts +35 -0
  141. package/dist/sqlite/nodeSqliteAdapter.js +28 -0
  142. package/dist/sqlite/nodeSqliteQuerierPool.d.ts +28 -0
  143. package/dist/sqlite/nodeSqliteQuerierPool.js +29 -0
  144. package/dist/sqlite/sqliteDialect.d.ts +18 -5
  145. package/dist/sqlite/sqliteDialect.js +23 -36
  146. package/dist/sqlite/sqliteQuerier.d.ts +16 -9
  147. package/dist/sqlite/sqliteQuerier.js +4 -27
  148. package/dist/sqlite/sqliteQuerierPool.d.ts +11 -14
  149. package/dist/sqlite/sqliteQuerierPool.js +16 -24
  150. package/dist/turso/index.d.ts +3 -0
  151. package/dist/turso/index.js +3 -0
  152. package/dist/turso/local.d.ts +3 -0
  153. package/dist/turso/local.js +3 -0
  154. package/dist/turso/tursoDialect.d.ts +15 -0
  155. package/dist/turso/tursoDialect.js +18 -0
  156. package/dist/turso/tursoLocalQuerier.d.ts +25 -0
  157. package/dist/turso/tursoLocalQuerier.js +20 -0
  158. package/dist/turso/tursoLocalQuerierPool.d.ts +32 -0
  159. package/dist/turso/tursoLocalQuerierPool.js +39 -0
  160. package/dist/turso/tursoQuerier.d.ts +10 -0
  161. package/dist/turso/tursoQuerier.js +10 -0
  162. package/dist/turso/tursoQuerierPool.d.ts +39 -0
  163. package/dist/turso/tursoQuerierPool.js +38 -0
  164. package/dist/type/config.d.ts +0 -6
  165. package/dist/type/dialect.d.ts +12 -4
  166. package/dist/type/dialect.js +7 -1
  167. package/dist/type/entity.d.ts +154 -26
  168. package/dist/type/migration.d.ts +32 -23
  169. package/dist/type/querier.d.ts +6 -0
  170. package/dist/type/vector.d.ts +5 -2
  171. package/dist/util/hook.util.js +1 -1
  172. package/dist/util/index.d.ts +1 -0
  173. package/dist/util/index.js +1 -0
  174. package/dist/util/indexColumn.util.d.ts +6 -0
  175. package/dist/util/indexColumn.util.js +26 -0
  176. package/package.json +21 -16
  177. package/dist/entity/decorator/field.d.ts +0 -2
  178. package/dist/entity/decorator/field.js +0 -7
  179. package/dist/entity/decorator/filter.d.ts +0 -7
  180. package/dist/entity/decorator/filter.js +0 -11
  181. package/dist/entity/decorator/hook.d.ts +0 -7
  182. package/dist/entity/decorator/hook.js +0 -14
  183. package/dist/entity/decorator/id.d.ts +0 -2
  184. package/dist/entity/decorator/id.js +0 -7
  185. package/dist/entity/decorator/index-decorator.d.ts +0 -29
  186. package/dist/entity/decorator/index-decorator.js +0 -26
  187. package/dist/entity/decorator/index.d.ts +0 -8
  188. package/dist/entity/decorator/index.js +0 -8
  189. package/dist/entity/decorator/relation.d.ts +0 -8
  190. package/dist/entity/decorator/relation.js +0 -19
  191. package/dist/migrate/codegen/entityMerger.d.ts +0 -114
  192. package/dist/migrate/codegen/entityMerger.js +0 -293
  193. package/dist/migrate/codegen/migrationCodeGenerator.d.ts +0 -61
  194. package/dist/migrate/codegen/migrationCodeGenerator.js +0 -355
  195. package/dist/migrate/codegen/smartRelationDetector.d.ts +0 -47
  196. package/dist/migrate/codegen/smartRelationDetector.js +0 -134
  197. package/dist/migrate/generator/index.d.ts +0 -2
  198. package/dist/migrate/generator/index.js +0 -2
  199. package/dist/migrate/sync/index.d.ts +0 -6
  200. package/dist/migrate/sync/index.js +0 -6
  201. package/dist/migrate/sync/schemaSync.d.ts +0 -131
  202. package/dist/migrate/sync/schemaSync.js +0 -259
  203. package/dist/querier/decorator/index.d.ts +0 -4
  204. package/dist/querier/decorator/index.js +0 -4
  205. package/dist/querier/decorator/injectQuerier.d.ts +0 -3
  206. package/dist/querier/decorator/injectQuerier.js +0 -33
  207. package/dist/querier/decorator/log.d.ts +0 -32
  208. package/dist/querier/decorator/log.js +0 -52
  209. package/dist/querier/decorator/serialized.d.ts +0 -6
  210. package/dist/querier/decorator/serialized.js +0 -13
  211. package/dist/querier/decorator/transactional.d.ts +0 -6
  212. package/dist/querier/decorator/transactional.js +0 -48
  213. package/dist/sqlite/betterSqlite3Dialect.d.ts +0 -9
  214. package/dist/sqlite/betterSqlite3Dialect.js +0 -9
@@ -1,7 +1,7 @@
1
1
  import type { IndexType } from '../schema/types.js';
2
2
  import type { FilterOptions } from './query.js';
3
3
  import type { QueryRaw } from './queryRaw.js';
4
- import type { Json, Scalar, Type, Unpacked } from './utility.js';
4
+ import type { DistributiveOmit, Json, Scalar, Type, Unpacked } from './utility.js';
5
5
  import type { VectorDistance, VectorIndexOptions, VectorIndexType } from './vector.js';
6
6
  /**
7
7
  * Allow to customize the name of the property that identifies an entity
@@ -175,14 +175,36 @@ export type JsonColumnType = 'json' | 'jsonb';
175
175
  * SQL binary/blob column types
176
176
  */
177
177
  export type BlobColumnType = 'blob' | 'bytea';
178
+ /**
179
+ * SQL boolean column types
180
+ */
181
+ export type BooleanColumnType = 'bool' | 'boolean';
182
+ /**
183
+ * SQL vector column types
184
+ */
185
+ export type VectorColumnType = 'vector' | 'halfvec' | 'sparsevec';
178
186
  /**
179
187
  * SQL column types supported by uql migrations
180
188
  */
181
- export type ColumnType = NumericColumnType | StringColumnType | DateColumnType | JsonColumnType | BlobColumnType | 'bool' | 'boolean' | 'vector' | 'halfvec' | 'sparsevec';
189
+ export type ColumnType = NumericColumnType | StringColumnType | DateColumnType | JsonColumnType | BlobColumnType | BooleanColumnType | VectorColumnType;
182
190
  /**
183
191
  * Logical types for a field
184
192
  */
185
193
  export type FieldType = StringConstructor | NumberConstructor | BooleanConstructor | DateConstructor | BigIntConstructor | ColumnType;
194
+ /**
195
+ * The {@link FieldType} values legal for a field declared as `V`.
196
+ *
197
+ * This is what makes an explicit `type` an improvement over the reflected one it replaces: the
198
+ * annotation is checked against the property's real TypeScript type, so `@Field({ type: String })` on
199
+ * a `number` no longer compiles into a silent TEXT column. `unknown` shapes fall through to the full
200
+ * {@link FieldType}, keeping genuinely untyped fields usable.
201
+ *
202
+ * JSON is matched on the `__json` brand rather than structurally, because {@link Json} intersects its
203
+ * payload (`Json<string>` really does extend `string`) and would otherwise land on the string arm.
204
+ * Both `Json<T>` and `Json<T>[]` have to be recognised, and the array check has to precede the scalar
205
+ * arms so a `number[]` vector is not read as a `number`.
206
+ */
207
+ export type TypeFor<V, T = NonNullable<V>> = IsJson<T> extends true ? JsonColumnType : IsJson<NonNullable<Unpacked<T>>> extends true ? JsonColumnType : T extends readonly number[] ? VectorColumnType : T extends string ? StringConstructor | StringColumnType : T extends number ? NumberConstructor | NumericColumnType : T extends bigint ? BigIntConstructor | NumericColumnType : T extends boolean ? BooleanConstructor | BooleanColumnType : T extends Date ? DateConstructor | DateColumnType : T extends Uint8Array ? BlobColumnType : FieldType;
186
208
  /**
187
209
  * Configurable options for a field
188
210
  */
@@ -191,11 +213,13 @@ export type FieldOptions = {
191
213
  readonly isId?: true;
192
214
  readonly type?: FieldType;
193
215
  /**
194
- * Set by `defineField` when `type` was inferred via reflection rather than
195
- * given explicitly. Internal bookkeeping - do not set this from a decorator.
216
+ * Set by `defineField` when the field gave `references` but no `type`, so schema generation resolves
217
+ * the column from the referenced primary key rather than from whatever ended up in `type`. That is
218
+ * what keeps a `uuid` primary key from becoming TEXT on every foreign key pointing at it.
219
+ * Internal bookkeeping - do not set this from a decorator.
196
220
  * @internal
197
221
  */
198
- readonly typeInferred?: boolean;
222
+ readonly typeFromReference?: boolean;
199
223
  /**
200
224
  * Dimensions for vector fields. Used in schema generation.
201
225
  * @example `@Field({ type: 'vector', dimensions: 1536 })`
@@ -270,6 +294,55 @@ export type FieldOptions = {
270
294
  readonly comment?: string;
271
295
  };
272
296
  export type OnFieldCallback = Scalar | QueryRaw | (() => Scalar | QueryRaw);
297
+ /**
298
+ * The TypeScript types a field may be declared as, given the `type` it registers: the inverse of
299
+ * {@link TypeFor}.
300
+ *
301
+ * Both directions are needed because they are consumed at opposite ends. `defineEntity` keys its bulk
302
+ * `fields` by property name, so the property's type is already known and {@link TypeFor} narrows the
303
+ * `type` allowed. A decorator has it the other way round: `@Field({ type: String })` is checked before
304
+ * the class exists, so the only way to reach the property is to state what `type: String` implies and
305
+ * let the decorator's context position compare it against the real field. Neither can be derived from
306
+ * the other by inference, so `entityOptions.type-test.ts` asserts they agree instead.
307
+ */
308
+ export type TsTypeOf<T> = T extends StringConstructor ? string : T extends NumberConstructor ? number : T extends BigIntConstructor ? bigint : T extends BooleanConstructor ? boolean : T extends DateConstructor ? Date : T extends StringColumnType ? string : T extends NumericColumnType ? number | bigint : T extends BooleanColumnType ? boolean : T extends DateColumnType ? Date : T extends JsonColumnType ? Json<unknown> | readonly Json<unknown>[] : T extends BlobColumnType ? Uint8Array : T extends VectorColumnType ? readonly number[] : unknown;
309
+ /**
310
+ * {@link FieldOptions} for a field declared as `V`, with `type` required and checked by
311
+ * {@link TypeFor}.
312
+ *
313
+ * The second arm is load-bearing rather than a convenience: a foreign-key column may omit `type` so
314
+ * that schema generation resolves it from the referenced primary key instead, picking up that key's
315
+ * `columnType`, length and chained references. Forcing `type: Number` onto
316
+ * `@Field({ references: () => Company })` would silently downgrade a `uuid` key to TEXT on every
317
+ * column pointing at it.
318
+ */
319
+ export type FieldOptionsFor<V> = (FieldOptions & {
320
+ readonly type: TypeFor<V>;
321
+ }) | (FieldOptions & {
322
+ readonly references: EntityGetter;
323
+ readonly type?: TypeFor<V>;
324
+ });
325
+ /**
326
+ * The entity a relation field points at: `Company` for both `company?: Company` and
327
+ * `companies?: Company[]`.
328
+ */
329
+ export type RelationTarget<V> = NonNullable<Unpacked<NonNullable<V>>>;
330
+ /**
331
+ * {@link RelationOptions} for a relation field declared as `V`, with `entity` required and pinned to
332
+ * `V`'s own type, and the cardinality restricted to the ones that field shape can hold. Together those
333
+ * reject `@ManyToOne({ entity: () => Other })` on a `Company` field, and any to-many cardinality on a
334
+ * field that is not an array.
335
+ */
336
+ export type RelationOptionsFor<V> = Omit<RelationOptions<RelationTarget<V>>, 'entity' | 'cardinality'> & {
337
+ readonly entity: EntityGetter<RelationTarget<V>>;
338
+ readonly cardinality: NonNullable<V> extends readonly unknown[] ? '1m' | 'mm' : '11' | 'm1';
339
+ };
340
+ /**
341
+ * The method names of an entity, so hook registrations name a method that exists.
342
+ */
343
+ export type MethodKey<E> = {
344
+ readonly [K in keyof E]-?: NonNullable<E[K]> extends (...args: never[]) => unknown ? K : never;
345
+ }[Key<E>];
273
346
  export type EntityGetter<E = any> = () => Type<E>;
274
347
  export type CascadeType = 'persist' | 'delete';
275
348
  export type RelationOptions<E = any> = {
@@ -299,20 +372,6 @@ export type RelationOneToOneOptions<E> = RelationOptionsOwner<E> | RelationOptio
299
372
  export type RelationOneToManyOptions<E> = RelationOptionsInverseSide<E> | RelationOptionsThroughOwner<E>;
300
373
  export type RelationManyToOneOptions<E> = RelationOptionsOwner<E>;
301
374
  export type RelationManyToManyOptions<E> = RelationOptionsThroughOwner<E> | RelationOptionsInverseSide<E>;
302
- /**
303
- * Wrapper type for relation type definitions in entities.
304
- * Used to circumvent ESM modules circular dependency issue caused by reflection metadata saving the type of the property.
305
- *
306
- * Usage example:
307
- * @Entity()
308
- * export default class User {
309
- *
310
- * @OneToOne(() => Profile, profile => profile.user)
311
- * profile: Relation<Profile>;
312
- *
313
- * }
314
- */
315
- export type Relation<T> = T;
316
375
  /**
317
376
  * Lifecycle hook event names.
318
377
  */
@@ -344,17 +403,70 @@ export type IndexTypeOptions = {
344
403
  distance?: never;
345
404
  };
346
405
  /**
347
- * Index metadata from @Index decorator.
406
+ * One entry of an index: a column name by default, `raw(...)` to index an expression, or an object
407
+ * when the entry needs more than a name.
408
+ *
409
+ * @example
410
+ * ```ts
411
+ * @Index(['tenantId', { column: 'createdAt', order: 'desc' }]) // keyset pagination
412
+ * @Index([raw('lower("email")')], { unique: true }) // case-insensitive uniqueness
413
+ * @Index([{ column: 'body', length: 64 }]) // MySQL needs a prefix on TEXT
414
+ * @Index(['data'], { type: 'gin' }) // JSONB containment
415
+ * ```
416
+ *
417
+ * `C` is the entity's `FieldKey` on the `@Index`/`defineEntity` paths, where the decorated class says
418
+ * which columns exist. It defaults to `string` for the migration builder's `table.index(...)`, which
419
+ * names raw table columns with no entity in scope.
420
+ */
421
+ export type IndexColumnInput<C extends string = string> = C | QueryRaw | IndexColumnOptions<C>;
422
+ /**
423
+ * What an index entry can carry besides the thing being indexed. Shared with the normalized
424
+ * `IndexColumnSchema`, so the authored and internal shapes cannot drift apart.
425
+ */
426
+ export type IndexColumnModifiers = {
427
+ /**
428
+ * Index only the first `n` characters. MySQL and MariaDB *require* this to index a `TEXT`/`BLOB`
429
+ * column at all ("used in key specification without a key length"); no other engine accepts it.
430
+ */
431
+ readonly length?: number;
432
+ /** Stored sort order, which lets `ORDER BY ... DESC` pagination use the index. */
433
+ readonly order?: 'asc' | 'desc';
434
+ /** Where NULLs sort. Postgres only. */
435
+ readonly nulls?: 'first' | 'last';
436
+ /** Operator class, e.g. `jsonb_path_ops` for a smaller GIN index. Postgres only. */
437
+ readonly opsClass?: string;
438
+ };
439
+ export type IndexColumnOptions<C extends string = string> = IndexColumnModifiers & {
440
+ /** The column to index, or `raw(...)` for an expression. */
441
+ readonly column: C | QueryRaw;
442
+ };
443
+ /**
444
+ * One index entry, normalized: {@link IndexColumnInput}'s three authored shapes all reduce to this
445
+ * before any dialect or generator sees them, so rendering never re-parses the sugar.
446
+ */
447
+ export type IndexColumnSchema = IndexColumnModifiers & {
448
+ /** A column name, or raw SQL when {@link expression} is set. */
449
+ readonly column: string;
450
+ /** Whether {@link column} is an expression to emit as-is rather than an identifier to quote. */
451
+ readonly expression?: boolean;
452
+ };
453
+ /**
454
+ * An index as stored in entity metadata: authored options with the columns normalized.
348
455
  */
349
456
  export type EntityIndexMeta = {
350
- /** Column names in the index */
351
- columns: string[];
457
+ /** The indexed columns, in order. */
458
+ columns: readonly IndexColumnSchema[];
352
459
  /** Custom index name */
353
460
  name?: string;
354
461
  /** Whether index is unique; omit or `false` for a non-unique index (default). */
355
462
  unique?: boolean;
356
463
  /** Partial index condition (WHERE clause) */
357
464
  where?: string;
465
+ /**
466
+ * Extra columns stored in the index but not part of its key, so a query reading only these is
467
+ * answered from the index alone. Postgres-wire only (`INCLUDE`).
468
+ */
469
+ include?: readonly string[];
358
470
  } & VectorIndexOptions & IndexTypeOptions;
359
471
  export type EntityMeta<E> = {
360
472
  readonly entity: Type<E>;
@@ -390,10 +502,26 @@ export type EntityOptions<E = unknown> = {
390
502
  /** Named, default-on `$where` filters (soft-delete is auto-registered from `@Field({ softDelete })`). */
391
503
  readonly filters?: Record<string, FilterOptions<E>>;
392
504
  /** Scalar fields; use `isId: true` on exactly one field for the primary key. */
393
- readonly fields?: Record<string, FieldOptions>;
394
- readonly relations?: Record<string, RelationOptions>;
395
- readonly indexes?: readonly EntityIndexMeta[];
505
+ readonly fields?: {
506
+ readonly [K in FieldKey<E>]?: FieldOptionsFor<E[K]>;
507
+ };
508
+ readonly relations?: {
509
+ readonly [K in RelationKey<E>]?: RelationOptionsFor<E[K]>;
510
+ };
511
+ readonly indexes?: readonly EntityIndexInput<FieldKey<E>>[];
396
512
  /** Map hook events to method names on the entity class. */
397
- readonly hooks?: Partial<Record<HookEvent, readonly string[]>>;
513
+ readonly hooks?: Partial<Record<HookEvent, readonly MethodKey<E>[]>>;
514
+ };
515
+ /**
516
+ * Everything an index carries beyond its columns, shared by `@Index`, `defineEntity` and the
517
+ * migration builder's `table.index(...)`. `DistributiveOmit` (not plain `Omit`) keeps `type`/
518
+ * `distance` a discriminated pair: omitting `distance` on a vector index type is a compile error.
519
+ */
520
+ export type IndexOptions = DistributiveOmit<EntityIndexMeta, 'columns'>;
521
+ /**
522
+ * An index as authored, before `defineIndex` normalizes its columns.
523
+ */
524
+ export type EntityIndexInput<C extends string = string> = IndexOptions & {
525
+ readonly columns: readonly IndexColumnInput<C>[];
398
526
  };
399
527
  export {};
@@ -1,7 +1,8 @@
1
+ import type { VectorCast } from '../dialect/vectorCast.js';
1
2
  import type { FullColumnDefinition, TableDefinition, TableForeignKeyDefinition } from '../migrate/builder/types.js';
2
3
  import type { SchemaAST } from '../schema/schemaAST.js';
3
4
  import type { ForeignKeyAction, IndexNode, IndexType, TableNode } from '../schema/types.js';
4
- import type { EntityMeta, FieldOptions, LoggingOptions, NamingStrategy, SqlQuerier, Type, VectorIndexOptions } from './index.js';
5
+ import type { EntityMeta, FieldOptions, IndexColumnSchema, LoggingOptions, SqlQuerier, Type, VectorIndexOptions } from './index.js';
5
6
  /**
6
7
  * Defines a migration using a simple object literal
7
8
  */
@@ -72,10 +73,6 @@ export interface MigratorOptions {
72
73
  * Entities to use for schema generation
73
74
  */
74
75
  readonly entities?: Type<unknown>[];
75
- /**
76
- * Naming strategy for database tables and columns
77
- */
78
- readonly namingStrategy?: NamingStrategy;
79
76
  /**
80
77
  * Default action for foreign key ON DELETE and ON UPDATE clauses.
81
78
  */
@@ -101,6 +98,13 @@ export interface MigrationResult {
101
98
  */
102
99
  export interface ColumnSchema {
103
100
  readonly name: string;
101
+ /**
102
+ * The engine's own type spelling, as introspection read it (`tinyint(1)`, `DATETIME`, `VARCHAR`).
103
+ * Deliberately not a {@link CanonicalType}: the diff has to compare what the engine would *store*,
104
+ * and several canonical types share one storage type per engine - an entity `boolean` is `TINYINT(1)`
105
+ * on MySQL and `INTEGER` on SQLite. Comparing canonical categories instead reports an alteration on
106
+ * every sync for those columns. Use `sqlToCanonical` to interpret it.
107
+ */
104
108
  readonly type: string;
105
109
  readonly nullable: boolean;
106
110
  readonly defaultValue?: unknown;
@@ -127,10 +131,20 @@ export interface TableSchema {
127
131
  */
128
132
  export interface IndexSchema extends VectorIndexOptions {
129
133
  readonly name: string;
130
- readonly columns: string[];
134
+ readonly columns: readonly IndexColumnSchema[];
131
135
  readonly unique: boolean;
132
136
  /** Index type (btree, hnsw, ivfflat, etc.) */
133
137
  readonly type?: IndexType;
138
+ /** Partial index condition (WHERE clause) */
139
+ readonly where?: string;
140
+ /** Non-key columns stored in the index (Postgres-wire `INCLUDE`). */
141
+ readonly include?: readonly string[];
142
+ /**
143
+ * The indexed column's vector type, which pgvector's operator-class names are built from
144
+ * (`halfvec_cosine_ops`). Absent for a non-vector index, and for an index whose column types are
145
+ * unknown, where `vector` is assumed.
146
+ */
147
+ readonly vectorType?: VectorCast;
134
148
  }
135
149
  /**
136
150
  * Represents a foreign key constraint
@@ -171,10 +185,11 @@ export interface SchemaGenerator {
171
185
  generateCreateTable<E>(entity: Type<E>, options?: {
172
186
  ifNotExists?: boolean;
173
187
  }): string[];
174
- /**
175
- * Generate DROP TABLE statement for an entity
176
- */
177
- generateDropTable<E>(entity: Type<E>): string;
188
+ /** Generate DROP TABLE statement. */
189
+ generateDropTable(tableName: string, options?: {
190
+ ifExists?: boolean;
191
+ cascade?: boolean;
192
+ }): string;
178
193
  /**
179
194
  * Generate ALTER TABLE statements based on schema diff
180
195
  */
@@ -215,21 +230,19 @@ export interface SchemaGenerator {
215
230
  generateCreateIndexFromNode(index: IndexNode, options?: {
216
231
  ifNotExists?: boolean;
217
232
  }): string;
218
- /** Generate DROP TABLE statement from a TableNode */
219
- generateDropTableFromNode(table: TableNode, options?: {
220
- ifExists?: boolean;
221
- }): string;
222
233
  /** DDL from a `TableDefinition`, one string per `querier.run`. */
223
234
  generateCreateTableFromDefinition(table: TableDefinition, options?: {
224
235
  ifNotExists?: boolean;
225
236
  }): string[];
226
- /** Generate DROP TABLE statement */
227
- generateDropTableSql(tableName: string, options?: {
228
- ifExists?: boolean;
229
- cascade?: boolean;
230
- }): string;
231
237
  /** Generate RENAME TABLE statement */
232
238
  generateRenameTableSql(oldName: string, newName: string): string;
239
+ }
240
+ /**
241
+ * The column and constraint DDL a migration builder emits, which only a SQL engine has. Split from
242
+ * {@link SchemaGenerator} because MongoDB used to satisfy these six by returning `''`: a document store
243
+ * has no `ADD COLUMN`, and an empty statement silently did nothing rather than saying so.
244
+ */
245
+ export interface SqlDdlGenerator extends SchemaGenerator {
233
246
  /** Generate ADD COLUMN statement */
234
247
  generateAddColumnSql(tableName: string, column: FullColumnDefinition): string;
235
248
  /** Generate ALTER COLUMN statement */
@@ -238,10 +251,6 @@ export interface SchemaGenerator {
238
251
  generateDropColumnSql(tableName: string, columnName: string): string;
239
252
  /** Generate RENAME COLUMN statement */
240
253
  generateRenameColumnSql(tableName: string, oldName: string, newName: string): string;
241
- /** Generate CREATE INDEX statement from IndexSchema */
242
- generateCreateIndexSql(tableName: string, index: IndexSchema): string;
243
- /** Generate DROP INDEX statement */
244
- generateDropIndexSql(tableName: string, indexName: string): string;
245
254
  /** Generate ADD FOREIGN KEY statement */
246
255
  generateAddForeignKeySql(tableName: string, foreignKey: TableForeignKeyDefinition): string;
247
256
  /** Generate DROP FOREIGN KEY statement */
@@ -120,6 +120,12 @@ export interface Querier extends UniversalQuerier {
120
120
  * release the querier to the pool.
121
121
  */
122
122
  release(): Promise<void>;
123
+ /**
124
+ * Releases the querier when an `await using` binding goes out of scope, so a unit of work cannot
125
+ * leak a connection on an early return or a throw.
126
+ * @example `await using querier = await pool.getQuerier();`
127
+ */
128
+ [Symbol.asyncDispose](): Promise<void>;
123
129
  }
124
130
  export interface SqlQuerier extends Querier {
125
131
  /**
@@ -5,9 +5,12 @@ import type { IndexType } from '../schema/types.js';
5
5
  * - `l2` - Euclidean distance
6
6
  * - `inner` - inner (dot) product
7
7
  * - `l1` - Manhattan distance
8
- * - `hamming` - for binary vectors
8
+ *
9
+ * @remarks Hamming distance is absent because no engine can express it over a float vector column:
10
+ * pgvector's `<~>`/`bit_hamming_ops` and sqlite-vec's `vec_distance_hamming` both require a *bit*
11
+ * vector, which no field type maps to. It would be a value that compiles and always throws.
9
12
  */
10
- export type VectorDistance = 'cosine' | 'l2' | 'inner' | 'l1' | 'hamming';
13
+ export type VectorDistance = 'cosine' | 'l2' | 'inner' | 'l1';
11
14
  /**
12
15
  * Vector similarity search options - used inside `$sort` on vector fields.
13
16
  *
@@ -1,4 +1,4 @@
1
- import { getMeta } from '../entity/decorator/index.js';
1
+ import { getMeta } from '../entity/index.js';
2
2
  /**
3
3
  * Run all registered hooks for the given event on each payload.
4
4
  * Hooks are invoked with `this` bound to the payload via `call`,
@@ -2,6 +2,7 @@ export * from './dialect.util.js';
2
2
  export * from './field.util.js';
3
3
  export * from './filters.util.js';
4
4
  export * from './hook.util.js';
5
+ export * from './indexColumn.util.js';
5
6
  export * from './logger.js';
6
7
  export * from './object.util.js';
7
8
  export * from './raw.js';
@@ -2,6 +2,7 @@ export * from './dialect.util.js';
2
2
  export * from './field.util.js';
3
3
  export * from './filters.util.js';
4
4
  export * from './hook.util.js';
5
+ export * from './indexColumn.util.js';
5
6
  export * from './logger.js';
6
7
  export * from './object.util.js';
7
8
  export * from './raw.js';
@@ -0,0 +1,6 @@
1
+ import { type IndexColumnInput, type IndexColumnSchema } from '../type/index.js';
2
+ /**
3
+ * Reduces an authored index entry to its normalized form, so the three shapes users write - a column
4
+ * name, `raw(expression)`, or an options object - reach the dialects as one.
5
+ */
6
+ export declare function normalizeIndexColumn(entry: IndexColumnInput): IndexColumnSchema;
@@ -0,0 +1,26 @@
1
+ import { QueryRaw, RAW_VALUE } from '../type/index.js';
2
+ /**
3
+ * Reduces an authored index entry to its normalized form, so the three shapes users write - a column
4
+ * name, `raw(expression)`, or an options object - reach the dialects as one.
5
+ */
6
+ export function normalizeIndexColumn(entry) {
7
+ if (typeof entry === 'string') {
8
+ return { column: entry };
9
+ }
10
+ if (entry instanceof QueryRaw) {
11
+ return { column: rawSql(entry), expression: true };
12
+ }
13
+ const { column, ...rest } = entry;
14
+ return column instanceof QueryRaw ? { ...rest, column: rawSql(column), expression: true } : { ...rest, column };
15
+ }
16
+ /**
17
+ * An index expression is DDL, evaluated once at creation time, so it cannot take the dialect-aware
18
+ * callback form of `raw()` - there is no query context to hand it.
19
+ */
20
+ function rawSql(value) {
21
+ const sql = value[RAW_VALUE];
22
+ if (typeof sql !== 'string') {
23
+ throw new TypeError('an index expression needs raw() with a string, not a function');
24
+ }
25
+ return sql;
26
+ }
package/package.json CHANGED
@@ -3,16 +3,17 @@
3
3
  "homepage": "https://uql-orm.dev",
4
4
  "description": "Extremely fast, type-safe TypeScript ORM - one API for every database",
5
5
  "license": "MIT",
6
- "version": "0.21.0",
6
+ "version": "0.23.0",
7
7
  "type": "module",
8
8
  "engines": {
9
- "node": ">=20"
9
+ "node": ">=24"
10
10
  },
11
11
  "sideEffects": false,
12
12
  "main": "./dist/index.js",
13
13
  "types": "./dist/index.d.ts",
14
14
  "browser": {
15
- "./dist/context/context.js": "./dist/context/context.browser.js"
15
+ "./dist/context/context.js": "./dist/context/context.browser.js",
16
+ "./dist/querier/querierContext.js": "./dist/querier/querierContext.browser.js"
16
17
  },
17
18
  "bin": {
18
19
  "uql-migrate": "./dist/migrate/bin.js"
@@ -44,6 +45,8 @@
44
45
  "default": "./dist/browser/uql-browser.min.js"
45
46
  },
46
47
  "./libsql": "./dist/libsql/index.js",
48
+ "./turso": "./dist/turso/index.js",
49
+ "./turso/local": "./dist/turso/local.js",
47
50
  "./neon": "./dist/neon/index.js",
48
51
  "./d1": "./dist/d1/index.js",
49
52
  "./bunSql": "./dist/bunSql/index.js",
@@ -66,15 +69,15 @@
66
69
  "@neondatabase/serverless": ">=0.9.0",
67
70
  "@nestjs/common": ">=10.0.0",
68
71
  "@nestjs/core": ">=10.0.0",
72
+ "@tursodatabase/database": ">=0.7.0",
73
+ "@tursodatabase/serverless": ">=1.0.0",
69
74
  "better-sqlite3": ">=9.0.0",
70
75
  "express": ">=5.0.0",
71
- "jiti": ">=2.0.0",
72
76
  "mariadb": ">=3.0.0",
73
77
  "mongodb": ">=6.0.0",
74
78
  "mysql2": ">=3.0.0",
75
79
  "pg": ">=8.0.0",
76
80
  "pg-query-stream": ">=4.0.0",
77
- "reflect-metadata": ">=0.2.0",
78
81
  "rxjs": ">=7.0.0"
79
82
  },
80
83
  "peerDependenciesMeta": {
@@ -90,13 +93,16 @@
90
93
  "@nestjs/core": {
91
94
  "optional": true
92
95
  },
93
- "better-sqlite3": {
96
+ "@tursodatabase/database": {
94
97
  "optional": true
95
98
  },
96
- "express": {
99
+ "@tursodatabase/serverless": {
100
+ "optional": true
101
+ },
102
+ "better-sqlite3": {
97
103
  "optional": true
98
104
  },
99
- "jiti": {
105
+ "express": {
100
106
  "optional": true
101
107
  },
102
108
  "mariadb": {
@@ -114,9 +120,6 @@
114
120
  "pg-query-stream": {
115
121
  "optional": true
116
122
  },
117
- "reflect-metadata": {
118
- "optional": true
119
- },
120
123
  "rxjs": {
121
124
  "optional": true
122
125
  }
@@ -127,20 +130,22 @@
127
130
  "@nestjs/common": "^11.1.28",
128
131
  "@nestjs/core": "^11.1.28",
129
132
  "@nestjs/testing": "^11.1.28",
130
- "@types/better-sqlite3": "^7.6.13",
133
+ "@tursodatabase/database": "^0.7.2",
134
+ "@tursodatabase/serverless": "^1.4.0",
135
+ "@types/better-sqlite3": "^9.6.0",
131
136
  "@types/express": "^5.0.6",
132
- "@types/pg": "^8.20.0",
137
+ "@types/pg": "^8.20.3",
133
138
  "@types/ws": "^8.18.1",
134
139
  "better-sqlite3": "^13.0.2",
135
140
  "express": "^5.2.1",
136
- "jiti": "^2.7.0",
137
141
  "mariadb": "^3.5.3",
138
142
  "mongodb": "^7.5.0",
139
143
  "mysql2": "^3.23.2",
140
144
  "pg": "^8.22.0",
141
145
  "pg-query-stream": "^4.16.0",
142
- "reflect-metadata": "^0.2.2",
143
146
  "rxjs": "^7.8.2",
147
+ "sqlite-vec": "^0.1.9",
148
+ "tsx": "^4.23.1",
144
149
  "ws": "^8.21.1"
145
150
  },
146
151
  "author": "Roger Padilla",
@@ -194,5 +199,5 @@
194
199
  "publishConfig": {
195
200
  "access": "public"
196
201
  },
197
- "gitHead": "1d056e8d5030fe70e3dc21a151db282e9e553dd9"
202
+ "gitHead": "3bfca5950ac564b8503fb2140726501b01825b9d"
198
203
  }
@@ -1,2 +0,0 @@
1
- import type { FieldOptions } from '../../type/index.js';
2
- export declare function Field<E>(opts?: FieldOptions): (target: object, key: string) => void;
@@ -1,7 +0,0 @@
1
- import { defineField } from '../metadata/definition.js';
2
- export function Field(opts) {
3
- return (target, key) => {
4
- const entity = target.constructor;
5
- defineField(entity, key, opts);
6
- };
7
- }
@@ -1,7 +0,0 @@
1
- import type { FilterOptions, Type } from '../../type/index.js';
2
- /**
3
- * Registers a named, default-on `$where` filter on the entity, applied to every query
4
- * unless bypassed via `QueryOptions.filters`.
5
- * @example `@Filter('active', { condition: { status: 'active' }, default: false })`
6
- */
7
- export declare function Filter<E>(name: string, opts: FilterOptions<E>): (entity: Type<E>) => void;
@@ -1,11 +0,0 @@
1
- import { defineFilter } from '../metadata/definition.js';
2
- /**
3
- * Registers a named, default-on `$where` filter on the entity, applied to every query
4
- * unless bypassed via `QueryOptions.filters`.
5
- * @example `@Filter('active', { condition: { status: 'active' }, default: false })`
6
- */
7
- export function Filter(name, opts) {
8
- return (entity) => {
9
- defineFilter(entity, name, opts);
10
- };
11
- }
@@ -1,7 +0,0 @@
1
- export declare const BeforeInsert: () => MethodDecorator;
2
- export declare const AfterInsert: () => MethodDecorator;
3
- export declare const BeforeUpdate: () => MethodDecorator;
4
- export declare const AfterUpdate: () => MethodDecorator;
5
- export declare const BeforeDelete: () => MethodDecorator;
6
- export declare const AfterDelete: () => MethodDecorator;
7
- export declare const AfterLoad: () => MethodDecorator;
@@ -1,14 +0,0 @@
1
- import { defineHook } from '../metadata/definition.js';
2
- function createHookDecorator(event) {
3
- return () => (target, key) => {
4
- const entity = target.constructor;
5
- defineHook(entity, String(key), event);
6
- };
7
- }
8
- export const BeforeInsert = createHookDecorator('beforeInsert');
9
- export const AfterInsert = createHookDecorator('afterInsert');
10
- export const BeforeUpdate = createHookDecorator('beforeUpdate');
11
- export const AfterUpdate = createHookDecorator('afterUpdate');
12
- export const BeforeDelete = createHookDecorator('beforeDelete');
13
- export const AfterDelete = createHookDecorator('afterDelete');
14
- export const AfterLoad = createHookDecorator('afterLoad');
@@ -1,2 +0,0 @@
1
- import type { FieldOptions } from '../../type/index.js';
2
- export declare function Id<E>(opts?: FieldOptions): (target: object, key: string) => void;
@@ -1,7 +0,0 @@
1
- import { defineId } from '../metadata/definition.js';
2
- export function Id(opts = {}) {
3
- return (target, key) => {
4
- const entity = target.constructor;
5
- defineId(entity, key, opts);
6
- };
7
- }
@@ -1,29 +0,0 @@
1
- import type { DistributiveOmit, EntityIndexMeta, Type } from '../../type/index.js';
2
- /**
3
- * Options for the @Index decorator - {@link EntityIndexMeta} minus `columns`, which the decorator
4
- * takes as its own positional argument. `DistributiveOmit` (not plain `Omit`) keeps `type`/`distance`
5
- * a discriminated pair - `EntityIndexMeta`'s vector-index invariant, that omitting `distance` on a
6
- * vector index type is a compile error.
7
- */
8
- export type IndexDecoratorOptions = DistributiveOmit<EntityIndexMeta, 'columns'>;
9
- /**
10
- * Define a composite index on an entity class.
11
- *
12
- * @example
13
- * ```ts
14
- * @Index(['lastName', 'firstName'], { name: 'idx_users_fullname' })
15
- * @Entity()
16
- * export class User {
17
- * @Id() id?: number;
18
- * @Field() firstName?: string;
19
- * @Field() lastName?: string;
20
- * }
21
- *
22
- * // With unique and partial index
23
- * @Index(['email'], { unique: true })
24
- * @Index(['status'], { where: "status = 'active'" })
25
- * @Entity()
26
- * export class User { ... }
27
- * ```
28
- */
29
- export declare function Index<E>(columns: string[], options?: IndexDecoratorOptions): (target: Type<E>) => void;