uql-orm 0.69.0 → 0.70.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 (44) hide show
  1. package/dist/browser/querier/httpQuerier.d.ts +7 -7
  2. package/dist/browser/type/clientQuerier.d.ts +5 -5
  3. package/dist/browser/uql-browser.min.js +2 -2
  4. package/dist/browser/uql-browser.min.js.map +4 -4
  5. package/dist/cockroachdb/cockroachDialect.js +2 -2
  6. package/dist/dialect/abstractDialect.d.ts +8 -2
  7. package/dist/dialect/abstractDialect.js +17 -1
  8. package/dist/dialect/abstractSqlDialect.d.ts +16 -5
  9. package/dist/dialect/abstractSqlDialect.js +82 -44
  10. package/dist/dialect/aliases.d.ts +10 -7
  11. package/dist/dialect/aliases.js +10 -7
  12. package/dist/dialect/mysqlLikeSqlDialect.js +3 -2
  13. package/dist/dialect/pgLikeSqlDialect.js +1 -0
  14. package/dist/dialect/queryJoins.d.ts +8 -1
  15. package/dist/dialect/queryJoins.js +33 -10
  16. package/dist/entity/decorator/members.d.ts +25 -11
  17. package/dist/entity/metadata/definition.d.ts +8 -3
  18. package/dist/migrate/codegen/entityCodeGenerator.js +4 -2
  19. package/dist/migrate/migrator.d.ts +2 -1
  20. package/dist/migrate/migrator.js +5 -3
  21. package/dist/mongo/mongoDialect.d.ts +31 -27
  22. package/dist/mongo/mongoDialect.js +172 -112
  23. package/dist/mongo/mongodbQuerier.d.ts +0 -2
  24. package/dist/mongo/mongodbQuerier.js +12 -11
  25. package/dist/mssql/mssqlDialect.js +3 -2
  26. package/dist/postgres/postgresDialect.js +2 -2
  27. package/dist/querier/abstractQuerier.d.ts +16 -11
  28. package/dist/querier/abstractQuerier.js +28 -8
  29. package/dist/querier/abstractQuerierPool.d.ts +9 -9
  30. package/dist/querier/abstractSqlQuerier.d.ts +1 -1
  31. package/dist/querier/abstractSqlQuerier.js +3 -3
  32. package/dist/sqlite/sqliteDialect.js +1 -0
  33. package/dist/turso/tursoDialect.d.ts +1 -1
  34. package/dist/turso/tursoDialect.js +6 -2
  35. package/dist/type/dialect.d.ts +6 -0
  36. package/dist/type/entity.d.ts +31 -2
  37. package/dist/type/migration.d.ts +7 -0
  38. package/dist/type/query.d.ts +6 -0
  39. package/dist/type/queryAggregate.d.ts +77 -42
  40. package/dist/type/queryAggregate.js +4 -21
  41. package/dist/type/universalQuerier.d.ts +9 -9
  42. package/dist/util/dialect.util.d.ts +6 -2
  43. package/dist/util/dialect.util.js +19 -5
  44. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import type { EntityData, EntityId, FieldKey, RelationKey, UpdatePayload, WrittenId } from './entity.js';
1
+ import type { EntityId, EntityWrite, FieldKey, RelationKey, UpdateWrite, WrittenId } from './entity.js';
2
2
  import type { QueryConflictPaths, QueryFilter, QueryFindResult, QueryOneProjected, QueryOptions, QueryPage, QueryProjected, QuerySearch, QueryUpsertOneResult, QueryUpsertManyResult } from './query.js';
3
3
  import type { QueryAggMap, QueryAggregate, QueryAggregateResult, QueryGroupMap } from './queryAggregate.js';
4
4
  import type { Type } from './utility.js';
@@ -31,9 +31,9 @@ export interface SharedQuerier<W extends QuerierTransport, O, DO = O> {
31
31
  /** Whether any record matches: a count capped at one row, so the engine stops at the first match. */
32
32
  exists<E extends object>(entity: Type<E>, q?: QueryFilter<E, QuerierRaw<W>>, opts?: O): QuerierResult<W, boolean>;
33
33
  /** Update the record with the given primary key; resolves to the number of affected rows. */
34
- updateOneById<E extends object>(entity: Type<E>, id: EntityId<E>, payload: UpdatePayload<E, QuerierRaw<W>>, opts?: O): QuerierResult<W, number>;
34
+ updateOneById<E extends object>(entity: Type<E>, id: EntityId<E>, payload: UpdateWrite<E, QuerierRaw<W>>, opts?: O): QuerierResult<W, number>;
35
35
  /** Update the records matching the query; resolves to the number of affected rows. */
36
- updateMany<E extends object>(entity: Type<E>, q: QuerySearch<E, QuerierRaw<W>>, payload: UpdatePayload<E, QuerierRaw<W>>, opts?: O): QuerierResult<W, number>;
36
+ updateMany<E extends object>(entity: Type<E>, q: QuerySearch<E, QuerierRaw<W>>, payload: UpdateWrite<E, QuerierRaw<W>>, opts?: O): QuerierResult<W, number>;
37
37
  /**
38
38
  * delete or SoftDelete a record.
39
39
  * @param entity the entity to persist on
@@ -59,31 +59,31 @@ export interface UniversalQuerier extends SharedQuerier<'server', QueryOptions>
59
59
  */
60
60
  findManyStream<E extends object, const S extends FieldKey<E> = never, const V = true, const X extends FieldKey<E> = never, const P extends RelationKey<E> = never, const C extends RelationKey<E> = never>(entity: Type<E>, q: QueryProjected<E, S, V, X, P, C>, opts?: QueryOptions): AsyncIterable<QueryFindResult<E, S, V, X, P, C>>;
61
61
  /** Insert a record and resolve to its id. See {@link UniversalQuerier.insertMany}. */
62
- insertOne<E extends object>(entity: Type<E>, payload: EntityData<E>): Promise<WrittenId<E> | undefined>;
62
+ insertOne<E extends object>(entity: Type<E>, payload: EntityWrite<E>): Promise<WrittenId<E> | undefined>;
63
63
  /**
64
64
  * Insert records in as few statements as the bind limit allows, resolving to their ids in payload order.
65
65
  * Ids are exact everywhere but MySQL, which infers them from its header and reports `undefined` rather
66
66
  * than a guess where it cannot: a batch naming some keys, or a key that is not `AUTO_INCREMENT`.
67
67
  */
68
- insertMany<E extends object>(entity: Type<E>, payload: EntityData<E>[]): Promise<(WrittenId<E> | undefined)[]>;
68
+ insertMany<E extends object>(entity: Type<E>, payload: EntityWrite<E>[]): Promise<(WrittenId<E> | undefined)[]>;
69
69
  /** Insert or update a record by its conflict paths; resolves to its id and whether it was created. */
70
- upsertOne<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityData<E>): Promise<QueryUpsertOneResult<E>>;
70
+ upsertOne<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityWrite<E>): Promise<QueryUpsertOneResult<E>>;
71
71
  /** Insert or update records by their conflict paths; resolves to their ids in payload order. */
72
- upsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityData<E>[]): Promise<QueryUpsertManyResult<E>>;
72
+ upsertMany<E extends object>(entity: Type<E>, conflictPaths: QueryConflictPaths<E>, payload: EntityWrite<E>[]): Promise<QueryUpsertManyResult<E>>;
73
73
  /**
74
74
  * insert or update a record.
75
75
  * @param entity the entity to persist on
76
76
  * @param payload the data to be persisted
77
77
  * @return the ID
78
78
  */
79
- saveOne<E extends object>(entity: Type<E>, payload: EntityData<E>): Promise<WrittenId<E> | undefined>;
79
+ saveOne<E extends object>(entity: Type<E>, payload: EntityWrite<E>): Promise<WrittenId<E> | undefined>;
80
80
  /**
81
81
  * Insert or update records.
82
82
  * @param entity the entity to persist on
83
83
  * @param payload the data to be persisted
84
84
  * @return the IDs
85
85
  */
86
- saveMany<E extends object>(entity: Type<E>, payload: EntityData<E>[]): Promise<(WrittenId<E> | undefined)[]>;
86
+ saveMany<E extends object>(entity: Type<E>, payload: EntityWrite<E>[]): Promise<(WrittenId<E> | undefined)[]>;
87
87
  /**
88
88
  * Restore soft-deleted records (sets the soft-delete field back to `null`). Throws if the
89
89
  * entity has no soft-delete field.
@@ -104,9 +104,11 @@ export declare function applyFilters<E>(meta: EntityMeta<E>, whereMap: QueryWher
104
104
  /**
105
105
  * Parsed entry from a `$group` map - either a raw group key or an aggregate function call.
106
106
  */
107
- export type ParsedGroupEntry = {
107
+ export type ParsedGroupEntry<E = object> = {
108
108
  readonly kind: 'key';
109
109
  readonly alias: string;
110
+ /** The field it reads, behind the to-one relations leading to it: `['transaction', 'orderId']`. */
111
+ readonly path: readonly string[];
110
112
  } | {
111
113
  readonly kind: 'fn';
112
114
  readonly alias: string;
@@ -114,6 +116,8 @@ export type ParsedGroupEntry = {
114
116
  readonly fieldRef: string;
115
117
  /** `true` for a flat distinct op (`$countDistinct`, ...) -> `COUNT(DISTINCT field)`. */
116
118
  readonly distinct: boolean;
119
+ /** The rows it reads, where not all of the statement's. */
120
+ readonly where?: QueryWhere<E>;
117
121
  };
118
122
  /**
119
123
  * The `$size` of a relation condition, `{ comments: { $size: { $gte: 2 } } }`, or `undefined` where it
@@ -130,7 +134,7 @@ export declare function parseSortByCount(val: unknown): unknown;
130
134
  * Parse the `$group` (grouped columns) and `$select` (computed aggregates) maps into structured
131
135
  * entries consumable by any dialect. Grouped columns come first, then computed columns.
132
136
  */
133
- export declare function parseGroupMap<E>(group?: QueryGroupMap<E>, select?: QueryAggMap<E>): ParsedGroupEntry[];
137
+ export declare function parseGroupMap<E>(group?: QueryGroupMap<E>, select?: QueryAggMap<E>): ParsedGroupEntry<E>[];
134
138
  /**
135
139
  * Whether `value` is a map of comparison operators rather than a value to compare against. Only a
136
140
  * plain object qualifies: `Date`, `QueryRaw`, `Uint8Array` and arrays are all `typeof 'object'`, and
@@ -344,22 +344,36 @@ export function parseGroupMap(group, select) {
344
344
  const entries = [];
345
345
  const groupMap = group ?? {};
346
346
  for (const alias of getKeys(groupMap)) {
347
- if (groupMap[alias]) {
348
- entries.push({ kind: 'key', alias });
347
+ const ref = groupMap[alias];
348
+ if (ref) {
349
+ entries.push({ kind: 'key', alias, path: ref === true ? [alias] : groupRefPath(alias, ref) });
349
350
  }
350
351
  }
351
352
  if (!select) {
352
353
  return entries;
353
354
  }
354
355
  for (const alias of getKeys(select)) {
355
- const fnEntry = select[alias];
356
- const key = getKeys(fnEntry)[0];
356
+ const { $where: where } = select[alias];
357
+ const call = select[alias];
358
+ const key = getKeys(call).find((name) => name !== '$where');
359
+ if (key === undefined) {
360
+ throw new TypeError(`aggregate '${alias}' names no op, only a $where`);
361
+ }
357
362
  // Flat DISTINCT ops (`$countDistinct`, ...) normalize to their base op + a `distinct` flag.
358
363
  const { op, distinct } = resolveAggregateOp(key);
359
- entries.push({ kind: 'fn', alias, op, fieldRef: aggregateFieldRef(alias, fnEntry[key]), distinct });
364
+ const fieldRef = aggregateFieldRef(alias, call[key]);
365
+ entries.push({ kind: 'fn', alias, op, fieldRef, distinct, ...(where && hasKeys(where) ? { where } : {}) });
360
366
  }
361
367
  return entries;
362
368
  }
369
+ /** The path a group key's `{ transaction: { orderId: true } }` names, one key at each level. */
370
+ function groupRefPath(alias, ref) {
371
+ const [key, ...rest] = isRecord(ref) ? getKeys(ref) : [];
372
+ if (!isRecord(ref) || key === undefined || rest.length) {
373
+ throw new TypeError(`$group '${alias}' names one field by the path to it: got ${JSON.stringify(ref)}`);
374
+ }
375
+ return ref[key] === true ? [key] : [key, ...groupRefPath(alias, ref[key])];
376
+ }
363
377
  /** The column an aggregate reads: `'*'`, or the one field its `{ field: true }` names. */
364
378
  function aggregateFieldRef(alias, arg) {
365
379
  const [field, ...rest] = arg === '*' ? [arg] : namedKeys(arg);
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "homepage": "https://uql-orm.dev",
4
4
  "description": "The JSON-native TypeScript ORM for Bun, Browsers, Edge, Deno, Node, Workers. Supports PostgreSQL, PGlite, MySQL, MariaDB, SQLite, CockroachDB, SQL Server, Turso, Neon, Cloudflare D1 and MongoDB. Queries are plain JSON, typed to the leaf.",
5
5
  "license": "MIT",
6
- "version": "0.69.0",
6
+ "version": "0.70.0",
7
7
  "type": "module",
8
8
  "engines": {
9
9
  "node": ">=24"