uql-orm 0.31.2 → 0.31.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -30,6 +30,11 @@ export type UqlModuleAsyncOptions<Req = unknown> = UqlModuleCommon<Req> & {
30
30
  /** Providers to inject into `useFactory`. */
31
31
  readonly inject?: FactoryProvider['inject'];
32
32
  };
33
+ /**
34
+ * NestJS integration: provides the pool via DI, sets it as UQL's default pool (so `getQuerier()`,
35
+ * `querierMiddleware` (express platform) and `createFetchHandler` work unchanged), optionally scopes
36
+ * every request to a {@link UqlContext} (multi-tenancy), and ends the pool on application shutdown.
37
+ */
33
38
  export declare class UqlModule {
34
39
  /** Configure with an already-built pool. */
35
40
  static forRoot<Req = unknown>({ pool, global, getContext }: UqlModuleOptions<Req>): DynamicModule;
@@ -43,11 +43,6 @@ import { UqlContextInterceptor } from './uqlContextInterceptor.js';
43
43
  * does not redirect UQL internals.
44
44
  */
45
45
  export const UQL_QUERIER_POOL = Symbol('UQL_QUERIER_POOL');
46
- /**
47
- * NestJS integration: provides the pool via DI, sets it as UQL's default pool (so `getQuerier()`,
48
- * `querierMiddleware` (express platform) and `createFetchHandler` work unchanged), optionally scopes
49
- * every request to a {@link UqlContext} (multi-tenancy), and ends the pool on application shutdown.
50
- */
51
46
  /**
52
47
  * Ends the pool when Nest shuts down.
53
48
  *
@@ -66,6 +61,11 @@ class UqlPoolLifecycle {
66
61
  return this.pool.end();
67
62
  }
68
63
  }
64
+ /**
65
+ * NestJS integration: provides the pool via DI, sets it as UQL's default pool (so `getQuerier()`,
66
+ * `querierMiddleware` (express platform) and `createFetchHandler` work unchanged), optionally scopes
67
+ * every request to a {@link UqlContext} (multi-tenancy), and ends the pool on application shutdown.
68
+ */
69
69
  let UqlModule = (() => {
70
70
  let _classDecorators = [Module({})];
71
71
  let _classDescriptor;
@@ -23,11 +23,13 @@ export declare abstract class AbstractQuerier implements Querier {
23
23
  private validateProjectionQueryRecursive;
24
24
  /**
25
25
  * Resolves `[entity, query, opts]` for the dual call pattern: `(entity, q, opts)` (entity argument)
26
- * vs `(query, opts)` (entity via the query's `$entity` field).
26
+ * vs `(query, opts)` (entity via the query's `$entity` field). Generic in the query `Q` because it
27
+ * only ever reads `$entity`: pinning it to one statement's shape made every caller launder its own
28
+ * through a cast, which is how a read query's `$sort` used to reach a write's.
27
29
  */
28
- protected resolveEntityQuery<E extends object>(entityOrQuery: Type<E> | (QuerySearch<E> & {
30
+ protected resolveEntityQuery<E extends object, Q extends object>(entityOrQuery: Type<E> | (Q & {
29
31
  $entity: Type<E>;
30
- }), maybeQueryOrOpts?: QuerySearch<E> | QueryOptions, maybeOpts?: QueryOptions): [Type<E>, QuerySearch<E>, QueryOptions | undefined];
32
+ }), maybeQueryOrOpts?: Q | QueryOptions, maybeOpts?: QueryOptions): [Type<E>, Q, QueryOptions | undefined];
31
33
  findOneById<E extends object>(entity: Type<E>, id: IdValue<E>, q?: QueryOne<E>, opts?: QueryOptions): Promise<E | undefined>;
32
34
  /**
33
35
  * Find a single record matching the query.
@@ -65,7 +65,9 @@ export class AbstractQuerier {
65
65
  }
66
66
  /**
67
67
  * Resolves `[entity, query, opts]` for the dual call pattern: `(entity, q, opts)` (entity argument)
68
- * vs `(query, opts)` (entity via the query's `$entity` field).
68
+ * vs `(query, opts)` (entity via the query's `$entity` field). Generic in the query `Q` because it
69
+ * only ever reads `$entity`: pinning it to one statement's shape made every caller launder its own
70
+ * through a cast, which is how a read query's `$sort` used to reach a write's.
69
71
  */
70
72
  resolveEntityQuery(entityOrQuery, maybeQueryOrOpts, maybeOpts) {
71
73
  if (typeof entityOrQuery === 'function' && entityOrQuery.prototype) {
@@ -36,7 +36,9 @@ export type QuerySelectOptions = {
36
36
  autoPrefixAlias?: boolean;
37
37
  };
38
38
  /**
39
- * Query field selection - `{ name: true }` whitelists specific fields.
39
+ * Query field selection - `{ name: true }` whitelists specific fields. Fields only: a relation is a
40
+ * sub-query rather than a projection flag, and a whitelist naming one could not say whether the
41
+ * scalars come with it. Relations go in `$populate`.
40
42
  */
41
43
  export type QuerySelect<E> = {
42
44
  [K in FieldKey<E>]?: BooleanLike;
@@ -116,17 +118,27 @@ export type QuerySortDirection = -1 | 1 | 'asc' | 'desc';
116
118
  * Accepted value for a field in `$sort` - either a direction or a vector similarity search.
117
119
  */
118
120
  export type QuerySortValue = QuerySortDirection | QueryVectorSearch;
121
+ /**
122
+ * To-one relations only: a parent holds many rows of a to-many, so there is no single value to order
123
+ * it by, and joining one in would duplicate the parent instead. Order those inside `$populate`.
124
+ */
125
+ type ToOneRelationKey<E> = {
126
+ [K in RelationKey<E>]: IsMany<E[K]> extends true ? never : K;
127
+ }[RelationKey<E>];
119
128
  /**
120
129
  * sort by map - supports field keys, JSON dot-notation paths (restricted to real JSON fields,
121
130
  * like `QueryWhereMap`), relation sort via nested objects, and vector similarity search on
122
- * `number[]` fields.
131
+ * `number[]` fields. `Vector` is what confines a vector search to the level the statement ranks:
132
+ * the queried entity. A relation of it is joined in one row at a time, so there is nothing to rank
133
+ * there - the SQL dialects throw, and MongoDB would quietly drop it, so this is its only guard.
134
+ *
135
+ * One mapped type over the three key sets rather than three intersected. The sets are disjoint - a
136
+ * JSON path is dotted, and a field key cannot also be a relation key - and an assignability check
137
+ * against an intersection is repeated per constituent, which made this the single most expensive
138
+ * type in the package to check.
123
139
  */
124
140
  export type QuerySortMap<E, Vector extends boolean = true> = {
125
- [K in FieldKey<E>]?: Vector extends true ? NonNullable<E[K]> extends readonly number[] ? QuerySortValue : QuerySortDirection : QuerySortDirection;
126
- } & {
127
- [P in JsonFieldPaths<E>]?: QuerySortDirection;
128
- } & {
129
- [K in RelationKey<E> as IsMany<E[K]> extends true ? never : K]?: QuerySortMap<RelationTarget<E[K]>, false>;
141
+ [K in FieldKey<E> | JsonFieldPaths<E> | ToOneRelationKey<E>]?: K extends RelationKey<E> ? QuerySortMap<RelationTarget<E[K]>, false> : K extends FieldKey<E> ? Vector extends true ? NonNullable<E[K]> extends readonly number[] ? QuerySortValue : QuerySortDirection : QuerySortDirection : QuerySortDirection;
130
142
  };
131
143
  /**
132
144
  * pager options.
@@ -153,17 +165,16 @@ export type QueryFilter<E> = {
153
165
  };
154
166
  /**
155
167
  * A filter plus the ordering and page `updateMany`/`deleteMany` take. Both settle the rows they
156
- * picked before writing, so the page is portable rather than MySQL-only.
157
- *
158
- * `$sort` excludes vector search here for the reason `$lock` is declared on {@link Query} instead:
159
- * a vector search ranks rows into a projected distance column, and only a SELECT has a projection
160
- * list to hold one. Passing `QuerySortMap` its `Vector = false` is what keeps it off these.
168
+ * picked with a SELECT before writing, so the page is portable rather than MySQL-only - and so a
169
+ * vector `$sort` is as valid here as on a read: it ranks the settle query's rows, which has the
170
+ * projection list to hold the distance. `$lock` is the clause that stays off these, declared on
171
+ * {@link Query} instead.
161
172
  */
162
173
  export type QuerySearch<E> = QueryFilter<E> & {
163
174
  /**
164
175
  * sorting options.
165
176
  */
166
- $sort?: QuerySortMap<E, false>;
177
+ $sort?: QuerySortMap<E>;
167
178
  } & QueryPager;
168
179
  /**
169
180
  * query options.
@@ -243,3 +254,4 @@ export type QueryUpdateResult = {
243
254
  */
244
255
  created?: boolean;
245
256
  };
257
+ export {};
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "homepage": "https://uql-orm.dev",
4
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.",
5
5
  "license": "MIT",
6
- "version": "0.31.2",
6
+ "version": "0.31.3",
7
7
  "type": "module",
8
8
  "engines": {
9
9
  "node": ">=24"