uql-orm 0.68.0 → 0.69.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/dist/browser/http/http.js +5 -8
- package/dist/browser/querier/httpQuerier.d.ts +9 -9
- package/dist/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +9 -8
- package/dist/d1/d1Querier.d.ts +5 -5
- package/dist/d1/d1QuerierPool.d.ts +3 -3
- package/dist/dialect/abstractSqlDialect.d.ts +31 -4
- package/dist/dialect/abstractSqlDialect.js +99 -21
- package/dist/dialect/aliases.d.ts +8 -0
- package/dist/dialect/aliases.js +8 -0
- package/dist/entity/decorator/members.d.ts +29 -1
- package/dist/entity/decorator/members.js +0 -5
- package/dist/entity/metadata/definition.js +10 -3
- package/dist/http/query.d.ts +10 -2
- package/dist/http/query.js +26 -1
- package/dist/migrate/introspection/postgresIntrospector.d.ts +6 -0
- package/dist/migrate/introspection/postgresIntrospector.js +7 -1
- package/dist/mongo/mongoDialect.d.ts +29 -3
- package/dist/mongo/mongoDialect.js +111 -14
- package/dist/mongo/mongodbQuerier.d.ts +2 -2
- package/dist/mongo/mongodbQuerier.js +4 -3
- package/dist/type/dialect.d.ts +29 -2
- package/dist/type/entity.d.ts +94 -7
- package/dist/type/query.d.ts +30 -34
- package/dist/type/queryRaw.d.ts +19 -1
- package/dist/type/queryRaw.js +18 -0
- package/dist/type/queryWhere.d.ts +20 -20
- package/dist/type/universalQuerier.d.ts +10 -10
- package/dist/type/wire.d.ts +9 -0
- package/dist/util/dialect.util.js +2 -2
- package/dist/util/field.util.d.ts +15 -1
- package/dist/util/field.util.js +18 -1
- package/dist/util/object.util.d.ts +1 -4
- package/dist/util/object.util.js +0 -3
- package/dist/util/raw.d.ts +2 -2
- package/dist/util/raw.js +29 -2
- package/package.json +2 -2
package/dist/type/queryRaw.js
CHANGED
|
@@ -30,3 +30,21 @@ export class ColumnRef extends QueryRaw {
|
|
|
30
30
|
this.key = key;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* A relation aggregate as SQL, read off a `computed` field's refs: `(user) => user.resources.count()`.
|
|
35
|
+
* It renders as the correlated subquery a `$count` reads, so a field holding one is read, filtered and
|
|
36
|
+
* sorted like any other.
|
|
37
|
+
*
|
|
38
|
+
* `V` is the value it reads and `Storable` whether a trigger could keep it, both carried in phantom
|
|
39
|
+
* fields so the aggregate a field declares decides the property's type and refuses `stored: true` on
|
|
40
|
+
* one no delta can maintain.
|
|
41
|
+
*/
|
|
42
|
+
export class RelationAggregate extends QueryRaw {
|
|
43
|
+
spec;
|
|
44
|
+
constructor(
|
|
45
|
+
/** What it reads, kept beside the SQL so a read decodes the value the way the target's field does. */
|
|
46
|
+
spec, value) {
|
|
47
|
+
super(value);
|
|
48
|
+
this.spec = spec;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -26,10 +26,10 @@ export type QueryTextSearchOptions<E> = {
|
|
|
26
26
|
* entity's keys so each stays linked for rename. An object and nothing else, so a wrong value is
|
|
27
27
|
* reported on its key: ids go through `{ id: 1 }` or the by-id methods.
|
|
28
28
|
*/
|
|
29
|
-
export type QueryWhere<E, K extends keyof E = FieldKey<E> | RelationKey<E>> = QueryWhereRootOperator<E> & {
|
|
30
|
-
[P in K]?: P extends FieldKey<E> ? QueryWhereFieldValue<E[P]> : QueryWhere<RelationTarget<E[P]
|
|
29
|
+
export type QueryWhere<E, Raw = QueryRaw, K extends keyof E = FieldKey<E> | RelationKey<E>> = QueryWhereRootOperator<E, Raw> & {
|
|
30
|
+
[P in K]?: P extends FieldKey<E> ? QueryWhereFieldValue<E[P], Raw> : QueryWhere<RelationTarget<E[P]>, Raw> | QueryRelationSizeFilter;
|
|
31
31
|
} & ([JsonFieldPaths<E>] extends [never] ? unknown : {
|
|
32
|
-
[P in JsonFieldPaths<E>]?: QueryWhereFieldValue<JsonFieldPathValue<E, P
|
|
32
|
+
[P in JsonFieldPaths<E>]?: QueryWhereFieldValue<JsonFieldPathValue<E, P>, Raw>;
|
|
33
33
|
});
|
|
34
34
|
/**
|
|
35
35
|
* Filter a to-many relation by its row count.
|
|
@@ -39,24 +39,24 @@ export type QueryWhere<E, K extends keyof E = FieldKey<E> | RelationKey<E>> = Qu
|
|
|
39
39
|
export type QueryRelationSizeFilter = {
|
|
40
40
|
readonly $size: number | QuerySizeComparisonOps;
|
|
41
41
|
};
|
|
42
|
-
export type QueryWhereRootOperator<E> = {
|
|
42
|
+
export type QueryWhereRootOperator<E, Raw = QueryRaw> = {
|
|
43
43
|
/**
|
|
44
44
|
* joins query clauses with a logical `AND`, returns records that match all the clauses.
|
|
45
45
|
*/
|
|
46
|
-
$and?: QueryWhereArray<E>;
|
|
46
|
+
$and?: QueryWhereArray<E, Raw>;
|
|
47
47
|
/**
|
|
48
48
|
* joins query clauses with a logical `OR`, returns records that match any of the clauses.
|
|
49
49
|
*/
|
|
50
|
-
$or?: QueryWhereArray<E>;
|
|
50
|
+
$or?: QueryWhereArray<E, Raw>;
|
|
51
51
|
/**
|
|
52
52
|
* joins query clauses with a logical `AND`, returns records that do not match all the clauses.
|
|
53
53
|
* @see {@link QueryWhereFieldOperatorMap.$not} for per-field negation.
|
|
54
54
|
*/
|
|
55
|
-
$not?: QueryWhereArray<E>;
|
|
55
|
+
$not?: QueryWhereArray<E, Raw>;
|
|
56
56
|
/**
|
|
57
57
|
* joins query clauses with a logical `OR`, returns records that do not match any of the clauses.
|
|
58
58
|
*/
|
|
59
|
-
$nor?: QueryWhereArray<E>;
|
|
59
|
+
$nor?: QueryWhereArray<E, Raw>;
|
|
60
60
|
/**
|
|
61
61
|
* whether the specified fields match against a full-text search of the given string.
|
|
62
62
|
*/
|
|
@@ -64,11 +64,11 @@ export type QueryWhereRootOperator<E> = {
|
|
|
64
64
|
/**
|
|
65
65
|
* whether the record exists in the given sub-query.
|
|
66
66
|
*/
|
|
67
|
-
$exists?:
|
|
67
|
+
$exists?: Raw;
|
|
68
68
|
/**
|
|
69
69
|
* whether the record does not exists in the given sub-query.
|
|
70
70
|
*/
|
|
71
|
-
$nexists?:
|
|
71
|
+
$nexists?: Raw;
|
|
72
72
|
};
|
|
73
73
|
/**
|
|
74
74
|
* Per-field negation operators. `Pick`'s constraint ties this back to
|
|
@@ -100,7 +100,7 @@ export type QuerySizeComparisonOps = {
|
|
|
100
100
|
export type QueryVectorNear = QueryVectorQuery & {
|
|
101
101
|
[K in QueryOrderedOp]?: NonNullable<QueryWhereFieldOperatorMap<number>[K]>;
|
|
102
102
|
};
|
|
103
|
-
export type QueryWhereFieldOperatorMap<T> = {
|
|
103
|
+
export type QueryWhereFieldOperatorMap<T, Raw = QueryRaw> = {
|
|
104
104
|
/**
|
|
105
105
|
* whether a value is equal to the given value.
|
|
106
106
|
*/
|
|
@@ -113,7 +113,7 @@ export type QueryWhereFieldOperatorMap<T> = {
|
|
|
113
113
|
* negates the given comparison for a single field.
|
|
114
114
|
* @see {@link QueryWhereRootOperator.$not} for root-level clause negation.
|
|
115
115
|
*/
|
|
116
|
-
$not?: QueryWhereFieldValue<T>;
|
|
116
|
+
$not?: QueryWhereFieldValue<T, Raw>;
|
|
117
117
|
/**
|
|
118
118
|
* whether a value is less than the given value.
|
|
119
119
|
*/
|
|
@@ -202,7 +202,7 @@ export type QueryWhereFieldOperatorMap<T> = {
|
|
|
202
202
|
* @example { addresses: { $elemMatch: { city: 'NYC', zip: '10001' } } }
|
|
203
203
|
* @example { addresses: { $elemMatch: { city: { $like: 'New%' } } } }
|
|
204
204
|
*/
|
|
205
|
-
$elemMatch?: unknown extends T ? QueryWhereElemMatch<unknown> : NonNullable<T> extends readonly (infer U)[] ? QueryWhereElemMatch<U> : never;
|
|
205
|
+
$elemMatch?: unknown extends T ? QueryWhereElemMatch<unknown, Raw> : NonNullable<T> extends readonly (infer U)[] ? QueryWhereElemMatch<U, Raw> : never;
|
|
206
206
|
/**
|
|
207
207
|
* whether a vector is within a given distance of the query vector. `$sort` ranks by distance;
|
|
208
208
|
* this filters by it, so "the closest ten" and "everything closer than 0.35" are separate asks.
|
|
@@ -216,10 +216,10 @@ export type QueryWhereFieldOperatorMap<T> = {
|
|
|
216
216
|
* field comparison. An untyped element (`unknown`) accepts any keys but still requires the
|
|
217
217
|
* object-of-conditions shape (a bare scalar is rejected).
|
|
218
218
|
*/
|
|
219
|
-
export type QueryWhereElemMatch<U> = unknown extends U ? {
|
|
220
|
-
[key: string]: QueryWhereFieldValue<unknown> | undefined;
|
|
221
|
-
} : NonNullable<U> extends Scalar ? QueryWhereFieldOperators<NonNullable<U
|
|
222
|
-
[K in keyof NonNullable<U>]?: QueryWhereFieldValue<NonNullable<U>[K]>;
|
|
219
|
+
export type QueryWhereElemMatch<U, Raw = QueryRaw> = unknown extends U ? {
|
|
220
|
+
[key: string]: QueryWhereFieldValue<unknown, Raw> | undefined;
|
|
221
|
+
} : NonNullable<U> extends Scalar ? QueryWhereFieldOperators<NonNullable<U>, Raw> : {
|
|
222
|
+
[K in keyof NonNullable<U>]?: QueryWhereFieldValue<NonNullable<U>[K], Raw>;
|
|
223
223
|
};
|
|
224
224
|
/**
|
|
225
225
|
* Simple relational comparison operators. `Pick`'s constraint ties this back to
|
|
@@ -269,7 +269,7 @@ type QueryAllowedOp<T> = QueryCommonOp | ([NonNullable<T>] extends [QueryCompara
|
|
|
269
269
|
* The operators a field of type `T` takes. `unknown`, and a column typed as every scalar at once (a
|
|
270
270
|
* runtime-defined entity), take all of them, since nothing narrows what they hold.
|
|
271
271
|
*/
|
|
272
|
-
export type QueryWhereFieldOperators<T> = unknown extends T ? QueryWhereFieldOperatorMap<T> : IsUntypedColumn<T> extends true ? QueryWhereFieldOperatorMap<T> : Pick<QueryWhereFieldOperatorMap<T>, QueryAllowedOp<T>>;
|
|
272
|
+
export type QueryWhereFieldOperators<T, Raw = QueryRaw> = unknown extends T ? QueryWhereFieldOperatorMap<T, Raw> : IsUntypedColumn<T> extends true ? QueryWhereFieldOperatorMap<T, Raw> : Pick<QueryWhereFieldOperatorMap<T, Raw>, QueryAllowedOp<T>>;
|
|
273
273
|
/**
|
|
274
274
|
* Whether a column admits every scalar at once, which is what an entity keyed by an index signature
|
|
275
275
|
* says about all of its columns. `Scalar` is the yardstick rather than a parameter: the question is
|
|
@@ -280,9 +280,9 @@ type IsUntypedColumn<T> = [Scalar] extends [NonNullable<T>] ? true : false;
|
|
|
280
280
|
* A field's filter value: the value, `null` where it is optional, a list as an implicit `$in` (not on
|
|
281
281
|
* an array field, where it would be ambiguous), or an operator map.
|
|
282
282
|
*/
|
|
283
|
-
export type QueryWhereFieldValue<T> = T | (undefined extends T ? null : never) | (IsMany<T> extends true ? never : T[]) | QueryWhereFieldOperators<T> |
|
|
283
|
+
export type QueryWhereFieldValue<T, Raw = QueryRaw> = T | (undefined extends T ? null : never) | (IsMany<T> extends true ? never : T[]) | QueryWhereFieldOperators<T, Raw> | Raw;
|
|
284
284
|
/**
|
|
285
285
|
* query filter array - the value every {@link QueryGroupOp} takes.
|
|
286
286
|
*/
|
|
287
|
-
export type QueryWhereArray<E> = (QueryWhere<E> |
|
|
287
|
+
export type QueryWhereArray<E, Raw = QueryRaw> = (QueryWhere<E, Raw> | Raw)[];
|
|
288
288
|
export {};
|
|
@@ -2,38 +2,38 @@ import type { EntityData, EntityId, FieldKey, RelationKey, UpdatePayload, Writte
|
|
|
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';
|
|
5
|
-
import type { QuerierCountedResult, QuerierResult, QuerierTransport } from './wire.js';
|
|
5
|
+
import type { QuerierCountedResult, QuerierRaw, QuerierResult, QuerierTransport } from './wire.js';
|
|
6
6
|
/**
|
|
7
7
|
* The operations the server and the browser client declare alike, per transport `W`, options `O`,
|
|
8
8
|
* and delete options `DO`, which on the client also carry the {@link QueryOptions} it cannot pass otherwise.
|
|
9
9
|
*/
|
|
10
10
|
export interface SharedQuerier<W extends QuerierTransport, O, DO = O> {
|
|
11
11
|
/** Find the record with the given primary key. */
|
|
12
|
-
findOneById<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>, id: EntityId<E>, q?: QueryOneProjected<E, S, V, X, P, C
|
|
12
|
+
findOneById<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>, id: EntityId<E>, q?: QueryOneProjected<E, S, V, X, P, C, QuerierRaw<W>>, opts?: O): QuerierResult<W, QueryFindResult<E, S, V, X, P, C> | undefined>;
|
|
13
13
|
/**
|
|
14
14
|
* obtains the first record matching the given search parameters.
|
|
15
15
|
* @param entity the target entity
|
|
16
16
|
* @param q the criteria options
|
|
17
17
|
* @return the record
|
|
18
18
|
*/
|
|
19
|
-
findOne<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: QueryOneProjected<E, S, V, X, P, C
|
|
19
|
+
findOne<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: QueryOneProjected<E, S, V, X, P, C, QuerierRaw<W>>, opts?: O): QuerierResult<W, QueryFindResult<E, S, V, X, P, C> | undefined>;
|
|
20
20
|
/**
|
|
21
21
|
* obtains the records matching the given search parameters.
|
|
22
22
|
* @param entity the target entity
|
|
23
23
|
* @param q the criteria options
|
|
24
24
|
* @return the records
|
|
25
25
|
*/
|
|
26
|
-
findMany<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
|
|
26
|
+
findMany<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, QuerierRaw<W>>, opts?: O): QuerierResult<W, QueryFindResult<E, S, V, X, P, C>[]>;
|
|
27
27
|
/** Find the records matching the query, and count every match past its page. */
|
|
28
|
-
findManyAndCount<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
|
|
28
|
+
findManyAndCount<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, QuerierRaw<W>>, opts?: O): QuerierCountedResult<W, QueryFindResult<E, S, V, X, P, C>>;
|
|
29
29
|
/** Count the records matching the filter, or those a page of them takes. */
|
|
30
|
-
count<E extends object>(entity: Type<E>, q?: QueryPage<E
|
|
30
|
+
count<E extends object>(entity: Type<E>, q?: QueryPage<E, QuerierRaw<W>>, opts?: O): QuerierResult<W, number>;
|
|
31
31
|
/** Whether any record matches: a count capped at one row, so the engine stops at the first match. */
|
|
32
|
-
exists<E extends object>(entity: Type<E>, q?: QueryFilter<E
|
|
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
|
|
34
|
+
updateOneById<E extends object>(entity: Type<E>, id: EntityId<E>, payload: UpdatePayload<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
|
|
36
|
+
updateMany<E extends object>(entity: Type<E>, q: QuerySearch<E, QuerierRaw<W>>, payload: UpdatePayload<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
|
|
@@ -47,7 +47,7 @@ export interface SharedQuerier<W extends QuerierTransport, O, DO = O> {
|
|
|
47
47
|
* @param q the criteria to look for the records
|
|
48
48
|
* @return the number of affected records
|
|
49
49
|
*/
|
|
50
|
-
deleteMany<E extends object>(entity: Type<E>, q: QuerySearch<E
|
|
50
|
+
deleteMany<E extends object>(entity: Type<E>, q: QuerySearch<E, QuerierRaw<W>>, opts?: DO): QuerierResult<W, number>;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
53
|
* A `querier` allows to interact with the datasource to perform persistence operations on any entity.
|
package/dist/type/wire.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { QueryRaw } from './queryRaw.js';
|
|
1
2
|
/**
|
|
2
3
|
* The envelope a wire response wraps its result in.
|
|
3
4
|
*/
|
|
@@ -16,6 +17,14 @@ export type RequestCountedSuccessResponse<E> = RequestSuccessResponse<E> & {
|
|
|
16
17
|
* client one hands back the envelope its transport wrapped it in.
|
|
17
18
|
*/
|
|
18
19
|
export type QuerierTransport = 'server' | 'client';
|
|
20
|
+
/**
|
|
21
|
+
* The `raw` SQL a transport carries. A client's query and payload travel as JSON, which a `raw` fragment
|
|
22
|
+
* is not: it would arrive as `{}`, so the client's types refuse one rather than let it leave.
|
|
23
|
+
*/
|
|
24
|
+
export type QuerierRaw<W extends QuerierTransport> = {
|
|
25
|
+
server: QueryRaw;
|
|
26
|
+
client: never;
|
|
27
|
+
}[W];
|
|
19
28
|
/**
|
|
20
29
|
* A querier method's result on a transport: `Promise<User[]>` on the server, the response envelope on
|
|
21
30
|
* the client. A map indexed by the transport, which resolves away in hovers.
|
|
@@ -2,8 +2,8 @@ import { getContext, UqlSecurityError } from '../context/context.js';
|
|
|
2
2
|
import { soleIdOf } from '../entity/metadata/definition.js';
|
|
3
3
|
import { QueryRaw, resolveAggregateOp, SOFT_DELETE_FILTER, } from '../type/index.js';
|
|
4
4
|
import { VECTOR_INDEX_TYPES } from '../type/vector.js';
|
|
5
|
-
import { isDatabaseWritten } from './field.util.js';
|
|
6
|
-
import { entityName,
|
|
5
|
+
import { getFieldKeys, isDatabaseWritten } from './field.util.js';
|
|
6
|
+
import { entityName, getKeys, hasKeys, isOperatorObject, isScalarId, isRecord, isWhereMap, someKey, } from './object.util.js';
|
|
7
7
|
/** The keys of `payload` a write persists as columns. */
|
|
8
8
|
export function filterFieldKeys(meta, payload, callbackKey) {
|
|
9
9
|
return getKeys(payload).filter((key) => {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ColumnFamily, type EntityMeta, type FieldOptions } from '../type/index.js';
|
|
1
|
+
import { type ColumnFamily, type EntityMeta, type FieldKey, type FieldOptions, type RelationAggregateSpec } from '../type/index.js';
|
|
2
2
|
/** The family of a logical field type, or `undefined` where it names none. */
|
|
3
3
|
export declare function columnFamily(type: unknown): ColumnFamily | undefined;
|
|
4
4
|
/**
|
|
@@ -12,6 +12,12 @@ export declare function isIntegerColumn(field: Pick<FieldOptions, 'type' | 'colu
|
|
|
12
12
|
* because an inlined field has no column to name, while a stored one is read like any other.
|
|
13
13
|
*/
|
|
14
14
|
export declare function isInlinedExpression<F extends FieldOptions>(field: F): field is F & Required<Pick<F, 'computed'>>;
|
|
15
|
+
/**
|
|
16
|
+
* The relation aggregate a field computes, where it computes one rather than writing SQL: what it
|
|
17
|
+
* reads, off which relation, narrowed and capped how. Every engine renders it from this - a correlated
|
|
18
|
+
* subquery on SQL, a lookup on MongoDB - so both read the same declaration rather than parsing SQL.
|
|
19
|
+
*/
|
|
20
|
+
export declare function aggregateOf(field: FieldOptions | undefined): RelationAggregateSpec | undefined;
|
|
15
21
|
/**
|
|
16
22
|
* Whether the database supplies this field's value, so no insert or update may write it: a stored
|
|
17
23
|
* computed column *is* a real column, read like one, but writing to it is an error on every engine.
|
|
@@ -25,3 +31,11 @@ export declare function isSoleIdField<E>(meta: EntityMeta<E>, field: FieldOption
|
|
|
25
31
|
* states its width. The one answer the create statement and the diff both read.
|
|
26
32
|
*/
|
|
27
33
|
export declare function isAutoIncrement(field: FieldOptions, isPrimaryKey: boolean): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* The fields a read answers with where it names none. A relation aggregate is left out unless it asks
|
|
36
|
+
* for `eager: true`: it reads the related rows, which is what a relation does, and a relation is loaded
|
|
37
|
+
* only when a query asks for it. Naming one in `$select` reads it, whatever the default.
|
|
38
|
+
*/
|
|
39
|
+
export declare function getFieldKeys<E>(fields: {
|
|
40
|
+
[K in FieldKey<E>]?: FieldOptions;
|
|
41
|
+
}): FieldKey<E>[];
|
package/dist/util/field.util.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { COLUMN_TYPES, } from '../type/index.js';
|
|
1
|
+
import { COLUMN_TYPES, RelationAggregate, } from '../type/index.js';
|
|
2
2
|
import { getKeys } from './object.util.js';
|
|
3
3
|
// Constructors and type strings in one map: a logical type is either, and every caller asks the same
|
|
4
4
|
// question of both.
|
|
@@ -41,6 +41,15 @@ export function isIntegerColumn(field) {
|
|
|
41
41
|
export function isInlinedExpression(field) {
|
|
42
42
|
return field.computed !== undefined && field.stored !== true;
|
|
43
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* The relation aggregate a field computes, where it computes one rather than writing SQL: what it
|
|
46
|
+
* reads, off which relation, narrowed and capped how. Every engine renders it from this - a correlated
|
|
47
|
+
* subquery on SQL, a lookup on MongoDB - so both read the same declaration rather than parsing SQL.
|
|
48
|
+
*/
|
|
49
|
+
export function aggregateOf(field) {
|
|
50
|
+
const computed = field?.computed;
|
|
51
|
+
return computed instanceof RelationAggregate ? computed.spec : undefined;
|
|
52
|
+
}
|
|
44
53
|
/**
|
|
45
54
|
* Whether the database supplies this field's value, so no insert or update may write it: a stored
|
|
46
55
|
* computed column *is* a real column, read like one, but writing to it is an error on every engine.
|
|
@@ -62,3 +71,11 @@ export function isAutoIncrement(field, isPrimaryKey) {
|
|
|
62
71
|
return field.autoIncrement;
|
|
63
72
|
return isPrimaryKey && columnFamily(field.type) === 'numeric' && !field.onInsert && !field.references;
|
|
64
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* The fields a read answers with where it names none. A relation aggregate is left out unless it asks
|
|
76
|
+
* for `eager: true`: it reads the related rows, which is what a relation does, and a relation is loaded
|
|
77
|
+
* only when a query asks for it. Naming one in `$select` reads it, whatever the default.
|
|
78
|
+
*/
|
|
79
|
+
export function getFieldKeys(fields) {
|
|
80
|
+
return getKeys(fields).filter((field) => fields[field].eager ?? !aggregateOf(fields[field]));
|
|
81
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EntityMeta
|
|
1
|
+
import type { EntityMeta } from '../type/index.js';
|
|
2
2
|
export declare function throwPendingTransaction(): never;
|
|
3
3
|
export declare function throwNoPendingTransaction(): never;
|
|
4
4
|
export declare function clone<T>(value: T): T;
|
|
@@ -24,9 +24,6 @@ export declare function definedEntries<K extends string, V>(record: Partial<Reco
|
|
|
24
24
|
* out reached for three different fallbacks, `?? ''` among them, and named nothing at all.
|
|
25
25
|
*/
|
|
26
26
|
export declare function entityName<E>(meta: EntityMeta<E>): string;
|
|
27
|
-
export declare function getFieldKeys<E>(fields: {
|
|
28
|
-
[K in FieldKey<E>]?: FieldOptions;
|
|
29
|
-
}): FieldKey<E>[];
|
|
30
27
|
/**
|
|
31
28
|
* Whether `value` addresses a row by itself rather than naming columns: every primitive, and the
|
|
32
29
|
* object ids a driver deals in (`ObjectId`, `Date`, bytes). Only a plain object names columns, which
|
package/dist/util/object.util.js
CHANGED
|
@@ -59,9 +59,6 @@ export function definedEntries(record) {
|
|
|
59
59
|
export function entityName(meta) {
|
|
60
60
|
return meta.name ?? meta.entity.name;
|
|
61
61
|
}
|
|
62
|
-
export function getFieldKeys(fields) {
|
|
63
|
-
return getKeys(fields).filter((field) => fields[field].eager ?? true);
|
|
64
|
-
}
|
|
65
62
|
/**
|
|
66
63
|
* Whether `value` addresses a row by itself rather than naming columns: every primitive, and the
|
|
67
64
|
* object ids a driver deals in (`ObjectId`, `Date`, bytes). Only a plain object names columns, which
|
package/dist/util/raw.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type EntitySql, type EntityWhere, type EntityWhereMeta, QueryRaw, type QueryRawFn, type RefMap, type Type } from '../type/index.js';
|
|
1
|
+
import { type EntitySql, type EntityWhere, type EntityWhereMeta, QueryRaw, type QueryRawFn, type ComputedRefs, type RefMap, type Type } from '../type/index.js';
|
|
2
2
|
/**
|
|
3
3
|
* Raw SQL, where an interpolated value binds, a `refs` field renders its column, and a `raw` renders
|
|
4
4
|
* in place: `raw`GREATEST(0, ${user.credits} - ${amount})``. A callback writes whatever it writes, so
|
|
@@ -16,7 +16,7 @@ export declare function refs<E>(entity: Type<E>): RefMap<E>;
|
|
|
16
16
|
* The refs a definition's callbacks read: an index's, a check's, a computed field's. A member decorator
|
|
17
17
|
* sees no class, so these name no entity and resolve against the one rendering them.
|
|
18
18
|
*/
|
|
19
|
-
export declare function memberRefs<E>():
|
|
19
|
+
export declare function memberRefs<E>(): ComputedRefs<E>;
|
|
20
20
|
/** SQL a definition writes, a callback's refs read off {@link memberRefs}. */
|
|
21
21
|
export declare function entitySql<E>(sql: EntitySql<E>): QueryRaw;
|
|
22
22
|
/** A definition's predicate, its callback resolved the way {@link entitySql} resolves one. */
|
package/dist/util/raw.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getMeta } from '../entity/metadata/definition.js';
|
|
2
|
-
import { ColumnRef, QueryRaw, } from '../type/index.js';
|
|
2
|
+
import { ColumnRef, QueryRaw, RelationAggregate, } from '../type/index.js';
|
|
3
3
|
import { isInlinedExpression } from './field.util.js';
|
|
4
4
|
export function raw(value, ...rest) {
|
|
5
5
|
if (!isTemplateStrings(value)) {
|
|
@@ -27,7 +27,7 @@ export function raw(value, ...rest) {
|
|
|
27
27
|
export function refs(entity) {
|
|
28
28
|
return new Proxy({}, { get: (_, key) => columnRef(entity, String(key)) });
|
|
29
29
|
}
|
|
30
|
-
const MEMBER_REFS = new Proxy({}, { get: (_, key) =>
|
|
30
|
+
const MEMBER_REFS = new Proxy({}, { get: (_, key) => memberRef(String(key)) });
|
|
31
31
|
/**
|
|
32
32
|
* The refs a definition's callbacks read: an index's, a check's, a computed field's. A member decorator
|
|
33
33
|
* sees no class, so these name no entity and resolve against the one rendering them.
|
|
@@ -43,6 +43,33 @@ export function entitySql(sql) {
|
|
|
43
43
|
export function entityWhere(where) {
|
|
44
44
|
return typeof where === 'function' ? where(memberRefs()) : where;
|
|
45
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* One member as a definition reads it: a {@link ColumnRef} where it names a field, and the same object
|
|
48
|
+
* answering `count`, `sum`, `min`, `max` and `avg` where it names a to-many. One runtime object, since
|
|
49
|
+
* a member decorator sees no class and so cannot know which the key is; the types keep them apart.
|
|
50
|
+
*/
|
|
51
|
+
function memberRef(relation) {
|
|
52
|
+
const over = (op) => (pick, q) => relationAggregate({ relation, op, field: pick(memberRefs()).key, ...(q && { query: q }) });
|
|
53
|
+
return Object.assign(columnRef(undefined, relation), {
|
|
54
|
+
count: (q) => relationAggregate({ relation, op: '$count', ...(q && { query: q }) }),
|
|
55
|
+
sum: over('$sum'),
|
|
56
|
+
min: over('$min'),
|
|
57
|
+
max: over('$max'),
|
|
58
|
+
avg: over('$avg'),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* A relation aggregate as SQL: the dialect writes the same correlated subquery a `$count` reads,
|
|
63
|
+
* correlated to whichever alias the clause naming the field is rendering under.
|
|
64
|
+
*/
|
|
65
|
+
function relationAggregate(spec) {
|
|
66
|
+
return new RelationAggregate(spec, (opts) => {
|
|
67
|
+
if (!opts.entity) {
|
|
68
|
+
throw new TypeError(`'${spec.relation}' was read off a definition's refs, so it renders only inside its entity's SQL`);
|
|
69
|
+
}
|
|
70
|
+
opts.dialect.appendRelationAggregate(opts.ctx, opts.entity, spec, opts.prefix);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
46
73
|
/** One field as SQL, against its own entity or, read off a definition, the entity rendering it. */
|
|
47
74
|
function columnRef(entity, key) {
|
|
48
75
|
return new ColumnRef(key, (opts) => {
|
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.
|
|
6
|
+
"version": "0.69.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"engines": {
|
|
9
9
|
"node": ">=24"
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
"repository": {
|
|
162
162
|
"type": "git",
|
|
163
163
|
"url": "git+https://github.com/rogerpadilla/uql.git",
|
|
164
|
-
"directory": "packages/
|
|
164
|
+
"directory": "packages/orm"
|
|
165
165
|
},
|
|
166
166
|
"bugs": {
|
|
167
167
|
"url": "https://github.com/rogerpadilla/uql/issues"
|