uql-orm 0.57.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -8
- package/dist/browser/uql-browser.min.js +2 -2
- package/dist/browser/uql-browser.min.js.map +3 -3
- package/dist/dialect/abstractSqlDialect.d.ts +2 -2
- package/dist/dialect/abstractSqlDialect.js +4 -4
- package/dist/dialect/mysqlLikeSqlDialect.d.ts +1 -1
- package/dist/dialect/mysqlLikeSqlDialect.js +1 -1
- package/dist/dialect/queryJoins.js +1 -0
- package/dist/entity/decorator/bag.d.ts +2 -2
- package/dist/entity/decorator/entity.d.ts +8 -9
- package/dist/entity/decorator/entity.js +6 -7
- package/dist/entity/decorator/members.d.ts +7 -6
- package/dist/entity/decorator/members.js +2 -1
- package/dist/entity/metadata/definition.d.ts +16 -11
- package/dist/entity/metadata/definition.js +73 -50
- package/dist/http/handler.d.ts +2 -2
- package/dist/http/handler.js +0 -1
- package/dist/migrate/codegen/entityCodeGenerator.js +6 -4
- package/dist/migrate/codegen/entityTypes.d.ts +1 -1
- package/dist/migrate/codegen/entityTypes.js +4 -3
- package/dist/migrate/codegen/indexDecoratorSource.d.ts +5 -4
- package/dist/migrate/codegen/indexDecoratorSource.js +17 -13
- package/dist/migrate/codegen/sourceLiteral.d.ts +2 -0
- package/dist/migrate/codegen/sourceLiteral.js +4 -0
- package/dist/migrate/generator/mongoSchemaGenerator.d.ts +3 -3
- package/dist/migrate/migrator.d.ts +2 -2
- package/dist/migrate/schemaGenerator.d.ts +5 -5
- package/dist/mongo/mongoDialect.d.ts +1 -1
- package/dist/mongo/mongoDialect.js +3 -3
- package/dist/mongo/mongodbQuerier.js +0 -1
- package/dist/querier/abstractSqlQuerier.js +6 -6
- package/dist/schema/schemaASTBuilder.d.ts +3 -3
- package/dist/schema/schemaASTBuilder.js +2 -2
- package/dist/type/config.d.ts +1 -1
- package/dist/type/entity.d.ts +115 -68
- package/dist/type/migration.d.ts +7 -7
- package/dist/type/querierPool.d.ts +2 -2
- package/dist/type/query.d.ts +19 -27
- package/dist/type/queryAggregate.d.ts +38 -29
- package/dist/type/queryWhere.d.ts +12 -9
- package/dist/util/dialect.util.d.ts +3 -3
- package/dist/util/dialect.util.js +24 -15
- package/dist/util/relationQuery.util.d.ts +1 -1
- package/package.json +1 -1
package/dist/type/entity.d.ts
CHANGED
|
@@ -172,10 +172,10 @@ export type EntityData<E> = Pick<E, FieldKey<E> | RelationKey<E>>;
|
|
|
172
172
|
* accept `QueryRaw` or `JsonUpdateOp` (for JSON fields), which gives IDE autocomplete for
|
|
173
173
|
* `$set`/`$push`/`$pull` keys via `Json<infer T>`.
|
|
174
174
|
*/
|
|
175
|
-
export type UpdatePayload<E
|
|
176
|
-
[K in
|
|
175
|
+
export type UpdatePayload<E, F extends keyof E = FieldKey<E>, R extends keyof E = RelationKey<E>> = {
|
|
176
|
+
[K in F]?: UpdateFieldValue<E[K]>;
|
|
177
177
|
} & {
|
|
178
|
-
[K in
|
|
178
|
+
[K in R]?: E[K];
|
|
179
179
|
};
|
|
180
180
|
/**
|
|
181
181
|
* Infers the field values of an entity
|
|
@@ -212,9 +212,7 @@ export type IdKey<E> = ([NamedIdKey<E>] extends [never] ? FieldKey<E> : NamedIdK
|
|
|
212
212
|
*/
|
|
213
213
|
export type IdValue<E> = E[IdKey<E>];
|
|
214
214
|
/** Every column of a key, which is how a composite row is named and what a `$where` reduces to. */
|
|
215
|
-
type IdMap<E> =
|
|
216
|
-
[K in IdKey<E>]?: E[K];
|
|
217
|
-
};
|
|
215
|
+
type IdMap<E> = Partial<Pick<E, IdKey<E>>>;
|
|
218
216
|
/**
|
|
219
217
|
* How a row is addressed by its primary key: the value for a single key, an object carrying every
|
|
220
218
|
* key for a composite - which is also the `$where` map it reduces to, so both spellings are one type.
|
|
@@ -478,17 +476,25 @@ export type FieldOptionsFor<V> = (FieldOptions<NonNullable<V>> & {
|
|
|
478
476
|
* The entity a relation field points at: `Company` for both `company?: Company` and
|
|
479
477
|
* `companies?: Company[]`.
|
|
480
478
|
*/
|
|
481
|
-
export type RelationTarget<V> =
|
|
479
|
+
export type RelationTarget<V> = Extract<Unpacked<V>, object>;
|
|
482
480
|
/**
|
|
483
481
|
* {@link RelationOptions} for a relation field declared as `V`, with `entity` required and pinned to
|
|
484
482
|
* `V`'s own type, and the cardinality restricted to the ones that field shape can hold. Together those
|
|
485
483
|
* reject `@ManyToOne({ entity: () => Other })` on a `Company` field, and any to-many cardinality on a
|
|
486
|
-
* field that is not an array.
|
|
484
|
+
* field that is not an array. A to-many additionally needs a {@link RelationJoin}.
|
|
485
|
+
*
|
|
486
|
+
* The join is required through the `cardinality` written rather than through `IsMany<V>`: a conditional
|
|
487
|
+
* member of the intersection leaves a `mappedBy` callback without a contextual type inside a generic
|
|
488
|
+
* call (`defineEntity`), where a union keyed on a property does not.
|
|
487
489
|
*/
|
|
488
|
-
export type RelationOptionsFor<V> = Omit<RelationOptions<RelationTarget<V
|
|
490
|
+
export type RelationOptionsFor<V, O = unknown> = Omit<RelationOptions<RelationTarget<V>, O>, 'entity' | 'cardinality'> & {
|
|
489
491
|
readonly entity: EntityGetter<RelationTarget<V>>;
|
|
490
492
|
readonly cardinality: IsMany<V> extends true ? '1m' | 'mm' : '11' | 'm1';
|
|
491
|
-
} & (
|
|
493
|
+
} & (({
|
|
494
|
+
readonly cardinality: '1m' | 'mm';
|
|
495
|
+
} & RelationJoin<RelationTarget<V>, O>) | {
|
|
496
|
+
readonly cardinality: '11' | 'm1';
|
|
497
|
+
});
|
|
492
498
|
/**
|
|
493
499
|
* The method names of an entity, so hook registrations name a method that exists.
|
|
494
500
|
*/
|
|
@@ -504,9 +510,13 @@ export type MethodKey<E> = {
|
|
|
504
510
|
* entity graph almost always has. Nothing about the standard decorator spec changes that; it only removed
|
|
505
511
|
* the reflected `design:type` that used to make `entity` optional.
|
|
506
512
|
*/
|
|
507
|
-
export type EntityGetter<E =
|
|
513
|
+
export type EntityGetter<E = object> = () => Type<E>;
|
|
508
514
|
export type CascadeType = 'persist' | 'delete';
|
|
509
|
-
|
|
515
|
+
/**
|
|
516
|
+
* `E` is the relation's target and `O` the entity declaring it, whose fields `references` names on its
|
|
517
|
+
* `local` side; the relation decorators infer `O` from the class they sit on.
|
|
518
|
+
*/
|
|
519
|
+
export type RelationOptions<E, O = unknown> = {
|
|
510
520
|
entity: EntityGetter<E>;
|
|
511
521
|
cardinality: RelationCardinality;
|
|
512
522
|
readonly cascade?: boolean | CascadeType;
|
|
@@ -519,13 +529,24 @@ export type RelationOptions<E = any> = {
|
|
|
519
529
|
*/
|
|
520
530
|
readonly onDelete?: ForeignKeyAction;
|
|
521
531
|
readonly onUpdate?: ForeignKeyAction;
|
|
522
|
-
|
|
532
|
+
/** The inverse side: the member of the target holding the foreign key or the owning relation, `(post) => post.author`. */
|
|
533
|
+
mappedBy?: (keys: KeyMap<E>) => Key<E>;
|
|
523
534
|
/**
|
|
524
535
|
* The pivot entity of a many-to-many. Unconstrained by `E`: a pivot holds foreign keys to both
|
|
525
536
|
* sides and is not a relation value of the target, so nothing about it is derivable from `E`.
|
|
526
537
|
*/
|
|
527
538
|
through?: EntityGetter;
|
|
528
|
-
|
|
539
|
+
/**
|
|
540
|
+
* The join columns where no convention fits: each pairs a field of the declaring entity with one of
|
|
541
|
+
* the target, `(order, customer) => [{ local: order.customerCode, foreign: customer.code }]`. A
|
|
542
|
+
* `through` relation takes none: its junction's columns follow the convention.
|
|
543
|
+
*/
|
|
544
|
+
references?: (local: KeyMap<O>, foreign: KeyMap<E>) => readonly RelationReference<O, E>[];
|
|
545
|
+
};
|
|
546
|
+
/** One pair of join columns, each a field read off its entity's key map. */
|
|
547
|
+
export type RelationReference<O, E> = {
|
|
548
|
+
readonly local: FieldKey<O>;
|
|
549
|
+
readonly foreign: FieldKey<E>;
|
|
529
550
|
};
|
|
530
551
|
/**
|
|
531
552
|
* A relation once `getMeta` has resolved it: `references` is filled in and `mappedBy` is the key its
|
|
@@ -538,46 +559,48 @@ export type RelationOptions<E = any> = {
|
|
|
538
559
|
* from "declared, but an inverse side too, so neither owns the foreign key" needs the unresolved shape
|
|
539
560
|
* still there to find. A phase-split metadata map costs more than the call parentheses it saves.
|
|
540
561
|
*/
|
|
541
|
-
export type RelationMeta
|
|
542
|
-
mappedBy?: Key<E>;
|
|
562
|
+
export type RelationMeta = RelationRegistration & {
|
|
543
563
|
references: RelationReferences;
|
|
544
564
|
};
|
|
545
|
-
/**
|
|
546
|
-
|
|
565
|
+
/**
|
|
566
|
+
* A relation as the registry takes it, whichever entity it targets: `mappedBy` and `references` read
|
|
567
|
+
* off their key maps down to the names they give, `references` unset until `getMeta` settles it.
|
|
568
|
+
*/
|
|
569
|
+
export type RelationRegistration = Omit<RelationOptions<object>, 'mappedBy' | 'references'> & {
|
|
570
|
+
mappedBy?: string;
|
|
571
|
+
references?: RelationReferences;
|
|
572
|
+
};
|
|
573
|
+
/** How a to-many owner reaches its children: a junction entity or the join columns, never both. */
|
|
574
|
+
type RelationOwnerJoin<E, O> = (Required<Pick<RelationOptions<E, O>, 'through'>> & {
|
|
575
|
+
readonly references?: never;
|
|
576
|
+
}) | (Required<Pick<RelationOptions<E, O>, 'references'>> & {
|
|
577
|
+
readonly through?: never;
|
|
578
|
+
});
|
|
547
579
|
/**
|
|
548
580
|
* Every way a to-many can say where its rows are. Required because nothing about the field implies it:
|
|
549
581
|
* without one of the three, resolution has no columns to join on and throws.
|
|
550
582
|
*/
|
|
551
|
-
type RelationJoin<E> = RelationOwnerJoin<E> | Required<Pick<RelationOptions<E>, 'mappedBy'>>;
|
|
552
|
-
type RelationOptionsOwner<E> = Pick<RelationOptions<E>, 'entity' | 'references' | 'cascade' | 'onDelete' | 'onUpdate'>;
|
|
583
|
+
type RelationJoin<E, O> = RelationOwnerJoin<E, O> | Required<Pick<RelationOptions<E>, 'mappedBy'>>;
|
|
584
|
+
type RelationOptionsOwner<E, O> = Pick<RelationOptions<E, O>, 'entity' | 'references' | 'cascade' | 'onDelete' | 'onUpdate'>;
|
|
553
585
|
type RelationOptionsInverseSide<E> = Pick<RelationOptions<E>, 'entity' | 'cascade'> & Required<Pick<RelationOptions<E>, 'mappedBy'>>;
|
|
554
|
-
type RelationOptionsThroughOwner<E> = Pick<RelationOptions<E>, 'entity' | 'cascade'> & RelationOwnerJoin<E>;
|
|
586
|
+
type RelationOptionsThroughOwner<E, O> = Pick<RelationOptions<E, O>, 'entity' | 'cascade'> & RelationOwnerJoin<E, O>;
|
|
555
587
|
/**
|
|
556
|
-
* The key names of `E` as values, so
|
|
557
|
-
* a
|
|
558
|
-
*
|
|
559
|
-
* Mapping over `Key<E>` rather than `keyof E` is what makes the callback usable: a homomorphic
|
|
560
|
-
* `[K in keyof E]` inherits the entity's optional modifiers, so `user.company` is
|
|
561
|
-
* `'company' | undefined` and {@link RelationKeyMapper} rejects it - every callback needed a `!`.
|
|
562
|
-
*
|
|
563
|
-
* At runtime a callback only ever reads one property off the map, so a single `Proxy` returning its
|
|
564
|
-
* own key stands in for every entity's: see `RELATION_KEY_MAP`. A key that names neither a field nor
|
|
565
|
-
* a relation of the target is rejected when the entity resolves.
|
|
588
|
+
* The key names of `E` as values, so a definition reads a member off it - `(post) => post.author` -
|
|
589
|
+
* and follows a rename. Homomorphic in `E`, which is what keeps that link, and `-?` so an optional
|
|
590
|
+
* member still names itself. At runtime one `Proxy` answering its own key serves every entity.
|
|
566
591
|
*/
|
|
567
|
-
export type
|
|
568
|
-
readonly [K in
|
|
592
|
+
export type KeyMap<E> = {
|
|
593
|
+
readonly [K in keyof E]-?: K;
|
|
569
594
|
};
|
|
570
|
-
export type RelationKeyMapper<E> = (keyMap: RelationKeyMap<E>) => Key<E>;
|
|
571
595
|
export type RelationReferences = {
|
|
572
596
|
readonly local: string;
|
|
573
597
|
readonly foreign: string;
|
|
574
598
|
}[];
|
|
575
|
-
export type RelationMappedBy<E> = Key<E> | RelationKeyMapper<E>;
|
|
576
599
|
export type RelationCardinality = '11' | 'm1' | '1m' | 'mm';
|
|
577
|
-
export type RelationOneToOneOptions<E> = RelationOptionsOwner<E> | RelationOptionsInverseSide<E>;
|
|
578
|
-
export type RelationOneToManyOptions<E> = RelationOptionsInverseSide<E> | RelationOptionsThroughOwner<E>;
|
|
579
|
-
export type RelationManyToOneOptions<E> = RelationOptionsOwner<E>;
|
|
580
|
-
export type RelationManyToManyOptions<E> = RelationOptionsThroughOwner<E> | RelationOptionsInverseSide<E>;
|
|
600
|
+
export type RelationOneToOneOptions<E, O = unknown> = RelationOptionsOwner<E, O> | RelationOptionsInverseSide<E>;
|
|
601
|
+
export type RelationOneToManyOptions<E, O = unknown> = RelationOptionsInverseSide<E> | RelationOptionsThroughOwner<E, O>;
|
|
602
|
+
export type RelationManyToOneOptions<E, O = unknown> = RelationOptionsOwner<E, O>;
|
|
603
|
+
export type RelationManyToManyOptions<E, O = unknown> = RelationOptionsThroughOwner<E, O> | RelationOptionsInverseSide<E>;
|
|
581
604
|
/**
|
|
582
605
|
* Lifecycle hook event names.
|
|
583
606
|
*/
|
|
@@ -614,10 +637,10 @@ export type IndexTypeOptions = {
|
|
|
614
637
|
*
|
|
615
638
|
* @example
|
|
616
639
|
* ```ts
|
|
617
|
-
* @Index([
|
|
618
|
-
* @Index([raw`lower("email")`], { unique: true })
|
|
619
|
-
* @Index([{ column:
|
|
620
|
-
* @Index([
|
|
640
|
+
* @Index((post) => [post.tenantId, { column: post.createdAt, order: 'desc' }]) // keyset pagination
|
|
641
|
+
* @Index(() => [raw`lower("email")`], { unique: true }) // case-insensitive uniqueness
|
|
642
|
+
* @Index((post) => [{ column: post.body, length: 64 }]) // MySQL needs a prefix on TEXT
|
|
643
|
+
* @Index((post) => [post.data], { type: 'gin' }) // JSONB containment
|
|
621
644
|
* ```
|
|
622
645
|
*
|
|
623
646
|
* `C` is the entity's `FieldKey` on the `@Index`/`defineEntity` paths, where the decorated class says
|
|
@@ -705,8 +728,8 @@ export type IndexColumnModifiers = {
|
|
|
705
728
|
*
|
|
706
729
|
* @example
|
|
707
730
|
* ```ts
|
|
708
|
-
* @Index([{ column:
|
|
709
|
-
* @Index([{ column:
|
|
731
|
+
* @Index((user) => [{ column: user.kind, jsonPath: { path: 'theme.color', type: String } }]) // 'kind.theme.color': 'red'
|
|
732
|
+
* @Index((user) => [{ column: user.kind, jsonPath: { path: 'rating', type: Number } }]) // 'kind.rating': { $gte: 4 }
|
|
710
733
|
* ```
|
|
711
734
|
*/
|
|
712
735
|
export type IndexJsonPath = {
|
|
@@ -725,8 +748,8 @@ export type IndexJsonPath = {
|
|
|
725
748
|
*
|
|
726
749
|
* @example
|
|
727
750
|
* ```ts
|
|
728
|
-
* @Index([{ column:
|
|
729
|
-
* @Index([{ column:
|
|
751
|
+
* @Index((user) => [{ column: user.tags, jsonArray: { type: String, length: 64 } }]) // tags: { $all: [...] }
|
|
752
|
+
* @Index((user) => [{ column: user.kind, jsonArray: { path: 'ids', type: Number } }]) // 'kind.ids': { $all: [...] }
|
|
730
753
|
* ```
|
|
731
754
|
*/
|
|
732
755
|
export type IndexJsonArray = {
|
|
@@ -742,6 +765,8 @@ type IndexColumnPlainModifiers = Except<IndexColumnModifiers, 'jsonPath' | 'json
|
|
|
742
765
|
export type IndexColumnOptions<C extends string = string> = IndexColumnPlainModifiers & {
|
|
743
766
|
/** The column to index, or `raw(...)` for an expression. */
|
|
744
767
|
readonly column: C | QueryRaw;
|
|
768
|
+
readonly jsonPath?: never;
|
|
769
|
+
readonly jsonArray?: never;
|
|
745
770
|
};
|
|
746
771
|
/**
|
|
747
772
|
* One index entry, normalized: {@link IndexColumnInput}'s three authored shapes all reduce to this
|
|
@@ -830,9 +855,21 @@ export type CheckOptions = {
|
|
|
830
855
|
*/
|
|
831
856
|
export type EntityMembers = {
|
|
832
857
|
readonly fields?: Readonly<Record<string, FieldOptions | undefined>>;
|
|
833
|
-
readonly relations?: Readonly<Record<string,
|
|
858
|
+
readonly relations?: Readonly<Record<string, RelationRegistration | undefined>>;
|
|
834
859
|
readonly hooks?: Readonly<Partial<Record<HookEvent, readonly string[]>>>;
|
|
835
860
|
};
|
|
861
|
+
/** An entity's fields as `defineEntity` takes them, keyed like every entity map (see `QuerySelect`). */
|
|
862
|
+
type EntityFieldOptions<E, F extends keyof E = FieldKey<E>> = {
|
|
863
|
+
readonly [K in F]?: FieldOptionsFor<E[K]>;
|
|
864
|
+
};
|
|
865
|
+
/**
|
|
866
|
+
* An entity's relations as `defineEntity` takes them. Keyed over every member rather than `RelationKey<E>`:
|
|
867
|
+
* inside the generic call, only a map over `keyof E` gives a `mappedBy` callback its contextual type. A
|
|
868
|
+
* field named here still fails, on its options, since its value is no entity.
|
|
869
|
+
*/
|
|
870
|
+
type EntityRelationOptions<E> = {
|
|
871
|
+
readonly [K in keyof E]?: RelationOptionsFor<E[K], E>;
|
|
872
|
+
};
|
|
836
873
|
/**
|
|
837
874
|
* Configurable options for an entity (`@Entity()` / `defineEntity`).
|
|
838
875
|
*
|
|
@@ -841,6 +878,13 @@ export type EntityMembers = {
|
|
|
841
878
|
*/
|
|
842
879
|
export type EntityOptions<E = unknown> = {
|
|
843
880
|
readonly name?: string;
|
|
881
|
+
/**
|
|
882
|
+
* The base to inherit fields, relations, hooks and filters from, for a class that cannot extend one
|
|
883
|
+
* (minted at runtime, or its base chosen from data): the merge `class Child extends Base` does, with
|
|
884
|
+
* the class's real base nearer, so it wins. Checked against whatever properties the entity declares,
|
|
885
|
+
* which a class behind an index signature has none of. See the Inheritance guide.
|
|
886
|
+
*/
|
|
887
|
+
readonly extends?: string extends keyof E ? Type<object> : Type<Partial<E>>;
|
|
844
888
|
/**
|
|
845
889
|
* The schema (in MySQL terms, database) this table lives in, pinning it whichever pool reads it;
|
|
846
890
|
* unset follows the pool's own. Not in `name`: a dotted `name` is rejected.
|
|
@@ -849,38 +893,41 @@ export type EntityOptions<E = unknown> = {
|
|
|
849
893
|
/** Named, default-on `$where` filters (soft-delete is auto-registered from `@Field({ softDelete })`). */
|
|
850
894
|
readonly filters?: Record<string, FilterOptions<E>>;
|
|
851
895
|
/** Scalar fields; use `isId: true` on exactly one field for the primary key. */
|
|
852
|
-
readonly fields?:
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
readonly relations?: {
|
|
856
|
-
readonly [K in RelationKey<E>]?: RelationOptionsFor<E[K]>;
|
|
857
|
-
};
|
|
858
|
-
readonly indexes?: readonly EntityIndexInput<FieldKey<E>, E>[];
|
|
896
|
+
readonly fields?: EntityFieldOptions<E>;
|
|
897
|
+
readonly relations?: EntityRelationOptions<E>;
|
|
898
|
+
readonly indexes?: readonly EntityIndexInput<E>[];
|
|
859
899
|
/** Table-level `CHECK` constraints. See {@link CheckOptions}. */
|
|
860
900
|
readonly checks?: readonly CheckOptions[];
|
|
861
|
-
/**
|
|
862
|
-
readonly hooks?: Partial<Record<HookEvent, readonly MethodKey<E>[]>>;
|
|
901
|
+
/** Each lifecycle event and the methods it runs, read off the key map: `{ beforeInsert: (post) => [post.stamp] }`. */
|
|
902
|
+
readonly hooks?: Partial<Record<HookEvent, (keys: KeyMap<E>) => readonly MethodKey<E>[]>>;
|
|
863
903
|
};
|
|
864
904
|
/**
|
|
865
|
-
* Everything an index carries beyond its columns,
|
|
866
|
-
*
|
|
867
|
-
* discriminated pair: omitting `distance` on a vector index type is a compile error.
|
|
905
|
+
* Everything an index carries beyond its columns, as the migration builder's `table.index(...)` takes it,
|
|
906
|
+
* and through {@link EntityIndexOptions} `@Index` and `defineEntity`. `Except` (not plain `Omit`) keeps
|
|
907
|
+
* `type`/`distance` a discriminated pair: omitting `distance` on a vector index type is a compile error.
|
|
868
908
|
*/
|
|
869
|
-
export type IndexOptions
|
|
870
|
-
/** Non-key columns stored in the index; a typo builds nothing, the server refusing
|
|
871
|
-
readonly include?: readonly
|
|
909
|
+
export type IndexOptions = Except<EntityIndexMeta, 'columns' | 'include' | 'where'> & {
|
|
910
|
+
/** Non-key columns stored in the index, by column name; a typo builds nothing, the server refusing it. */
|
|
911
|
+
readonly include?: readonly string[];
|
|
872
912
|
/**
|
|
873
913
|
* Partial-index predicate. `raw` with no interpolation, like an index expression: this is DDL, so
|
|
874
914
|
* there is no placeholder for a bound value. A bare string is the older spelling and still works.
|
|
875
915
|
*/
|
|
876
916
|
readonly where?: string | QueryRaw;
|
|
877
917
|
};
|
|
878
|
-
/** A field of `E`, or any name where there is no entity to check it against - the migration builder. */
|
|
879
|
-
type IndexFieldKey<E> = unknown extends E ? string : FieldKey<E>;
|
|
880
918
|
/**
|
|
881
|
-
*
|
|
919
|
+
* {@link IndexOptions} on an entity, whose stored columns are read off its key map, `(post) => [post.slug]`,
|
|
920
|
+
* so they are checked against it and follow a rename. The migration builder names raw columns instead.
|
|
921
|
+
*/
|
|
922
|
+
export type EntityIndexOptions<E> = Except<IndexOptions, 'include'> & {
|
|
923
|
+
readonly include?: (keys: KeyMap<E>) => readonly FieldKey<E>[];
|
|
924
|
+
};
|
|
925
|
+
/**
|
|
926
|
+
* An index as authored on an entity, before `defineIndex` reads its columns off the key map. Only the
|
|
927
|
+
* member lists are callbacks: TypeScript never checks a callback's returned literal for excess properties,
|
|
928
|
+
* so the options stay a literal of their own, where `uniqe: true` is a compile error.
|
|
882
929
|
*/
|
|
883
|
-
export type EntityIndexInput<
|
|
884
|
-
readonly columns: readonly IndexColumnInput<
|
|
930
|
+
export type EntityIndexInput<E> = EntityIndexOptions<E> & {
|
|
931
|
+
readonly columns: (keys: KeyMap<E>) => readonly IndexColumnInput<FieldKey<E>, E>[];
|
|
885
932
|
};
|
|
886
933
|
export {};
|
package/dist/type/migration.d.ts
CHANGED
|
@@ -73,7 +73,7 @@ export interface MigratorOptions {
|
|
|
73
73
|
/**
|
|
74
74
|
* Entities to use for schema generation
|
|
75
75
|
*/
|
|
76
|
-
readonly entities?: Type<
|
|
76
|
+
readonly entities?: Type<object>[];
|
|
77
77
|
/**
|
|
78
78
|
* Default action for foreign key ON DELETE and ON UPDATE clauses.
|
|
79
79
|
*/
|
|
@@ -141,7 +141,7 @@ export interface IndexSchema extends VectorIndexOptions {
|
|
|
141
141
|
/**
|
|
142
142
|
* What the index is over, in order. Named `entries` and not `columns` because an entry need not be
|
|
143
143
|
* a column at all: ``raw`lower(email)` `` is one, and so is a column carrying a prefix length or a
|
|
144
|
-
* stored order. The authored form, `@Index
|
|
144
|
+
* stored order. The authored form, `@Index`, still spells this `columns`, since that is what
|
|
145
145
|
* it reads like at the call site.
|
|
146
146
|
*/
|
|
147
147
|
readonly entries: readonly IndexColumnSchema[];
|
|
@@ -234,7 +234,7 @@ export interface SyncOptions {
|
|
|
234
234
|
readonly drop?: boolean;
|
|
235
235
|
readonly logging?: boolean;
|
|
236
236
|
/** One entity instead of every registered one, for a schema that grows while the process runs. */
|
|
237
|
-
readonly entity?: Type<
|
|
237
|
+
readonly entity?: Type<object>;
|
|
238
238
|
/** Drop every table and recreate it. Development only: it is the one option that loses data. */
|
|
239
239
|
readonly force?: boolean;
|
|
240
240
|
}
|
|
@@ -266,13 +266,13 @@ export interface SchemaGenerator {
|
|
|
266
266
|
* cross-entity foreign key has nothing to resolve against and is dropped: all three call sites that
|
|
267
267
|
* used to work that way emitted schemas with no referential integrity.
|
|
268
268
|
*/
|
|
269
|
-
generateCreateSchema(entities: readonly Type<
|
|
269
|
+
generateCreateSchema(entities: readonly Type<object>[], options?: CreateSchemaOptions): string[];
|
|
270
270
|
/**
|
|
271
271
|
* Every `DROP TABLE` for `entities`, dependents first. The inverse of {@link generateCreateSchema},
|
|
272
272
|
* and the reason it takes the whole set: dropping in any order that ignores the relation graph is
|
|
273
273
|
* rejected once the foreign keys are really there.
|
|
274
274
|
*/
|
|
275
|
-
generateDropSchema(entities: readonly Type<
|
|
275
|
+
generateDropSchema(entities: readonly Type<object>[], options?: DropSchemaOptions): string[];
|
|
276
276
|
/** Generate DROP TABLE statement. */
|
|
277
277
|
generateDropTable(tableName: string, options?: DropSchemaOptions): string;
|
|
278
278
|
/**
|
|
@@ -303,7 +303,7 @@ export interface SchemaGenerator {
|
|
|
303
303
|
* reads as missing from both sides, which is a match and no statement. Defaults to this entity
|
|
304
304
|
* alone, which is right only where it has no relations.
|
|
305
305
|
*/
|
|
306
|
-
diffSchema
|
|
306
|
+
diffSchema(entity: Type<object>, currentTable: TableNode | undefined, desiredAst?: SchemaAST): SchemaDiff | undefined;
|
|
307
307
|
/**
|
|
308
308
|
* The entity side as an AST, to hand to every {@link diffSchema} of one run - building it per
|
|
309
309
|
* entity instead is quadratic in the number of entities.
|
|
@@ -311,7 +311,7 @@ export interface SchemaGenerator {
|
|
|
311
311
|
* Optional because not every generator compares one: MongoDB has no foreign keys and diffs only
|
|
312
312
|
* indexes, so it neither implements this nor reads the argument.
|
|
313
313
|
*/
|
|
314
|
-
buildAST?(entities: readonly Type<
|
|
314
|
+
buildAST?(entities: readonly Type<object>[]): SchemaAST;
|
|
315
315
|
/**
|
|
316
316
|
* The table's key: {@link resolveTableAlias} behind {@link resolveSchema}, which is how a
|
|
317
317
|
* `SchemaAST` stores it and how a diff finds it again.
|
|
@@ -76,9 +76,9 @@ export interface QuerierPool<Q extends Querier = Querier, D extends AbstractDial
|
|
|
76
76
|
export interface SqlQuerierPool<Q extends SqlQuerier = SqlQuerier, D extends AbstractSqlDialect = AbstractSqlDialect> extends QuerierPool<Q, D>, Pick<SqlQuerier, 'all' | 'run'> {
|
|
77
77
|
}
|
|
78
78
|
/** Dialect class used by pool `P` (when `P` is a {@link QuerierPool}). */
|
|
79
|
-
export type QuerierPoolDialect<P> = P extends QuerierPool<
|
|
79
|
+
export type QuerierPoolDialect<P> = P extends QuerierPool<infer _Q, infer D> ? D : never;
|
|
80
80
|
/** Querier type produced by pool `P`. */
|
|
81
|
-
export type QuerierPoolQuerier<P> = P extends QuerierPool<infer Q,
|
|
81
|
+
export type QuerierPoolQuerier<P> = P extends QuerierPool<infer Q, infer _D> ? Q : never;
|
|
82
82
|
/**
|
|
83
83
|
* Represents a high-compatibility SQL pool shim for Node.js integrations (e.g., express-session).
|
|
84
84
|
*/
|
package/dist/type/query.d.ts
CHANGED
|
@@ -26,12 +26,12 @@ export type QueryOptions = {
|
|
|
26
26
|
autoPrefix?: boolean;
|
|
27
27
|
};
|
|
28
28
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
29
|
+
* Field selection - `{ name: true }` whitelists fields; relations go in `$populate`. Declared over
|
|
30
|
+
* `F extends keyof E`, like every map keyed by an entity's members, so each key stays linked to its
|
|
31
|
+
* property and an editor rename reaches it. `F` is also how a projection passes its captured key set.
|
|
32
32
|
*/
|
|
33
|
-
export type QuerySelect<E> = {
|
|
34
|
-
[K in
|
|
33
|
+
export type QuerySelect<E, F extends keyof E = FieldKey<E>, V = BooleanLike> = {
|
|
34
|
+
[K in F]?: V;
|
|
35
35
|
};
|
|
36
36
|
/**
|
|
37
37
|
* Accepted `$select` value: a field map, or raw SQL projections built with `raw()`
|
|
@@ -46,8 +46,8 @@ export type QueryExclude<E> = QuerySelect<E>;
|
|
|
46
46
|
/**
|
|
47
47
|
* relation population map.
|
|
48
48
|
*/
|
|
49
|
-
export type QueryPopulate<E
|
|
50
|
-
[K in
|
|
49
|
+
export type QueryPopulate<E, R extends keyof E = RelationKey<E>> = {
|
|
50
|
+
[K in R]?: BooleanLike | QueryPopulateRelationOptions<E[K]>;
|
|
51
51
|
};
|
|
52
52
|
/**
|
|
53
53
|
* The key a read carries its relation tallies under. One spelling for the type and the runtime that
|
|
@@ -59,15 +59,13 @@ export declare const COUNT_RESULT_KEY = "_count";
|
|
|
59
59
|
* which ones count: a correlated count in the read's own statement, so no related row is loaded. Comes
|
|
60
60
|
* back under `_count`, which keeps it clear of a relation of the same name `$populate` filled.
|
|
61
61
|
*/
|
|
62
|
-
export type QueryCount<E
|
|
63
|
-
[K in
|
|
62
|
+
export type QueryCount<E, R extends keyof E = ToManyRelationKey<E>> = {
|
|
63
|
+
[K in R]?: BooleanLike | QueryFilter<RelationTarget<E[K]>>;
|
|
64
64
|
};
|
|
65
65
|
/**
|
|
66
66
|
* query conflict paths - subset of field keys used to detect upsert conflicts.
|
|
67
67
|
*/
|
|
68
|
-
export type QueryConflictPaths<E> =
|
|
69
|
-
[K in FieldKey<E>]?: true;
|
|
70
|
-
};
|
|
68
|
+
export type QueryConflictPaths<E> = QuerySelect<E, FieldKey<E>, true>;
|
|
71
69
|
/**
|
|
72
70
|
* Options to populate a relation declared as `V`, by its cardinality.
|
|
73
71
|
*/
|
|
@@ -149,9 +147,11 @@ export type QuerySortByCount = {
|
|
|
149
147
|
* against an intersection is repeated per constituent, which made this the single most expensive
|
|
150
148
|
* type in the package to check.
|
|
151
149
|
*/
|
|
152
|
-
export type QuerySortMap<E, Vector extends boolean = true> = {
|
|
153
|
-
[
|
|
154
|
-
}
|
|
150
|
+
export type QuerySortMap<E, Vector extends boolean = true, K extends keyof E = FieldKey<E> | RelationKey<E>> = {
|
|
151
|
+
[P in K]?: P extends RelationKey<E> ? IsMany<E[P]> extends true ? QuerySortByCount : QuerySortMap<RelationTarget<E[P]>, false> : Vector extends true ? NonNullable<E[P]> extends readonly number[] ? QuerySortValue : QuerySortDirection : QuerySortDirection;
|
|
152
|
+
} & ([JsonFieldPaths<E>] extends [never] ? unknown : {
|
|
153
|
+
[P in JsonFieldPaths<E>]?: QuerySortDirection;
|
|
154
|
+
});
|
|
155
155
|
/**
|
|
156
156
|
* pager options.
|
|
157
157
|
*/
|
|
@@ -314,18 +314,10 @@ export type QueryUnique<E> = Pick<QueryOne<E>, '$select' | '$exclude' | '$popula
|
|
|
314
314
|
* @internal
|
|
315
315
|
*/
|
|
316
316
|
type QueryProjection<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>, C extends RelationKey<E>> = {
|
|
317
|
-
$select?:
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
$
|
|
321
|
-
[K in X]?: V;
|
|
322
|
-
};
|
|
323
|
-
$populate?: {
|
|
324
|
-
[K in P]?: QueryPopulate<E>[K];
|
|
325
|
-
};
|
|
326
|
-
$count?: {
|
|
327
|
-
[K in C & keyof QueryCount<E>]?: QueryCount<E>[K];
|
|
328
|
-
};
|
|
317
|
+
$select?: QuerySelect<E, S, V> | readonly QueryRaw[];
|
|
318
|
+
$exclude?: QuerySelect<E, X, V>;
|
|
319
|
+
$populate?: QueryPopulate<E, P>;
|
|
320
|
+
$count?: QueryCount<E, C & ToManyRelationKey<E>>;
|
|
329
321
|
};
|
|
330
322
|
/**
|
|
331
323
|
* A {@link Query} whose projection is captured, so {@link QueryFindResult} can shape the row.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { FieldKey } from './entity.js';
|
|
2
|
-
import type { QueryPager, QuerySortDirection } from './query.js';
|
|
2
|
+
import type { QueryPager, QuerySelect, QuerySortDirection } from './query.js';
|
|
3
3
|
import type { QueryWhere, QueryWhereFieldValue } from './queryWhere.js';
|
|
4
4
|
/**
|
|
5
5
|
* Maps the offending keys to `never`, turning an excess key into a compile error; resolves to
|
|
6
|
-
* `unknown` (an inert intersection member) when there are none. Needed because `$group`/`$
|
|
6
|
+
* `unknown` (an inert intersection member) when there are none. Needed because `$group`/`$select` are
|
|
7
7
|
* captured as whole maps, and TypeScript skips excess-property checking on a naked type parameter.
|
|
8
8
|
* A find captures key sets instead, where an unknown key fails the capture's own constraint.
|
|
9
9
|
* @internal
|
|
@@ -21,7 +21,7 @@ type GroupedKeys<G> = {
|
|
|
21
21
|
}[keyof G];
|
|
22
22
|
/**
|
|
23
23
|
* The keys `T` declares by name, or `never` when `T` is only an index signature - which is what an
|
|
24
|
-
* uninferred `$
|
|
24
|
+
* uninferred `$select` is, and what would otherwise make every key look like a declared alias.
|
|
25
25
|
* @internal
|
|
26
26
|
*/
|
|
27
27
|
type NamedKeys<T> = string extends keyof T ? never : keyof T;
|
|
@@ -57,8 +57,20 @@ export declare function resolveAggregateOp(key: string): {
|
|
|
57
57
|
op: QueryAggregateOp;
|
|
58
58
|
distinct: boolean;
|
|
59
59
|
};
|
|
60
|
+
/**
|
|
61
|
+
* Exactly one key of `T` with its value; every other key is forbidden (`never`). `Pick`, not `Record`,
|
|
62
|
+
* so the chosen key stays linked to `T`'s own property and renames follow it through.
|
|
63
|
+
*/
|
|
64
|
+
type ExactlyOne<T> = {
|
|
65
|
+
[K in keyof T]: Readonly<Pick<T, K>> & Partial<Readonly<Record<Exclude<keyof T, K>, never>>>;
|
|
66
|
+
}[keyof T];
|
|
67
|
+
/**
|
|
68
|
+
* One field named as a key - `{ amount: true }` - the way a statement names every field, so an editor
|
|
69
|
+
* rename reaches it where a string never would. `F` narrows which fields qualify.
|
|
70
|
+
*/
|
|
71
|
+
export type QueryFieldRef<E, F extends keyof E = FieldKey<E>> = ExactlyOne<Required<QuerySelect<E, F, true>>>;
|
|
60
72
|
/** The argument of an aggregate function: a field, or `'*'` (only meaningful for `COUNT(*)`). */
|
|
61
|
-
export type QueryAggregateArg<E> =
|
|
73
|
+
export type QueryAggregateArg<E> = QueryFieldRef<E> | '*';
|
|
62
74
|
/**
|
|
63
75
|
* Fields `SUM`/`AVG` can total. Restricted to numeric columns because the result is declared
|
|
64
76
|
* `number`: totalling a text or date column is either an engine error or a coercion, and neither
|
|
@@ -81,21 +93,17 @@ type TotallingOp = OpsOf<'$sum' | '$avg' | '$sumDistinct' | '$avgDistinct'>;
|
|
|
81
93
|
* Every aggregate op mapped to the argument it accepts: `$count` a field or `'*'` (`COUNT(*)`),
|
|
82
94
|
* the totalling ops a numeric field, `$min`/`$max`/`$countDistinct` any field.
|
|
83
95
|
*/
|
|
84
|
-
type QueryAggregateArgMap<E> = Record<'$count', QueryAggregateArg<E>> & Record<TotallingOp, NumericFieldKey<E
|
|
85
|
-
/** Exactly one key of `T`: the chosen op with its value; every other op key is forbidden (`never`). */
|
|
86
|
-
type ExactlyOne<T> = {
|
|
87
|
-
[K in keyof T]: Readonly<Record<K, T[K]>> & Partial<Readonly<Record<Exclude<keyof T, K>, never>>>;
|
|
88
|
-
}[keyof T];
|
|
96
|
+
type QueryAggregateArgMap<E> = Record<'$count', QueryAggregateArg<E>> & Record<TotallingOp, QueryFieldRef<E, NumericFieldKey<E>>> & Record<Exclude<AggregateOp, '$count' | TotallingOp>, QueryFieldRef<E>>;
|
|
89
97
|
/**
|
|
90
98
|
* An aggregate function applied to a field. Exactly one operation per entry (a second op is a
|
|
91
99
|
* compile error). Only `$count` accepts `'*'` (i.e. `COUNT(*)`); every other op requires a field.
|
|
92
100
|
* DISTINCT variants are flat ops (`$countDistinct`/`$sumDistinct`/`$avgDistinct`) taking a field.
|
|
93
101
|
*
|
|
94
|
-
* @example { $count: '*' }
|
|
95
|
-
* @example { $countDistinct:
|
|
96
|
-
* @example { $sum:
|
|
97
|
-
* @example { $sumDistinct:
|
|
98
|
-
* @example { $avg:
|
|
102
|
+
* @example { $count: '*' } -> COUNT(*)
|
|
103
|
+
* @example { $countDistinct: { id: true } } -> COUNT(DISTINCT "id")
|
|
104
|
+
* @example { $sum: { amount: true } } -> SUM("amount")
|
|
105
|
+
* @example { $sumDistinct: { amount: true } } -> SUM(DISTINCT "amount")
|
|
106
|
+
* @example { $avg: { age: true } } -> AVG("age")
|
|
99
107
|
*/
|
|
100
108
|
export type QueryAggregateFn<E> = ExactlyOne<QueryAggregateArgMap<E>>;
|
|
101
109
|
/** A single-key `{ [op]: unknown }` shape for each op in `Ops`, matched to infer that op's result. */
|
|
@@ -109,16 +117,14 @@ type CountingOp = OpsOf<'$count' | '$countDistinct'>;
|
|
|
109
117
|
/**
|
|
110
118
|
* Group-by columns: an object mapping entity field keys to `true`, exactly like {@link QuerySelect}.
|
|
111
119
|
* Typed against the entity, so a typo'd column is a compile error. Compute aggregate columns with
|
|
112
|
-
* {@link QueryAggMap} (the `$
|
|
120
|
+
* {@link QueryAggMap} (the `$select` key), not here.
|
|
113
121
|
*
|
|
114
122
|
* @example
|
|
115
123
|
* ```ts
|
|
116
124
|
* { status: true } // -> GROUP BY "status"
|
|
117
125
|
* ```
|
|
118
126
|
*/
|
|
119
|
-
export type QueryGroupMap<E> =
|
|
120
|
-
readonly [K in FieldKey<E>]?: true;
|
|
121
|
-
};
|
|
127
|
+
export type QueryGroupMap<E> = Readonly<QuerySelect<E, FieldKey<E>, true>>;
|
|
122
128
|
/**
|
|
123
129
|
* Computed aggregate columns: an object mapping your chosen output alias to an aggregate function.
|
|
124
130
|
* Alias names are free (you are naming new columns); the aggregated field reference inside each
|
|
@@ -126,7 +132,7 @@ export type QueryGroupMap<E> = {
|
|
|
126
132
|
*
|
|
127
133
|
* @example
|
|
128
134
|
* ```ts
|
|
129
|
-
* { count: { $count: '*' }, avgAge: { $avg:
|
|
135
|
+
* { count: { $count: '*' }, avgAge: { $avg: { age: true } } }
|
|
130
136
|
* // -> COUNT(*) AS "count", AVG("age") AS "avgAge"
|
|
131
137
|
* ```
|
|
132
138
|
*/
|
|
@@ -151,7 +157,7 @@ type QueryAggregateFnResult<E, Fn> = Fn extends FnWithOp<CountingOp> ? number :
|
|
|
151
157
|
readonly $min: infer F;
|
|
152
158
|
} | {
|
|
153
159
|
readonly $max: infer F;
|
|
154
|
-
} ? FieldValueType<E, F> | null : unknown;
|
|
160
|
+
} ? FieldValueType<E, keyof F> | null : unknown;
|
|
155
161
|
/**
|
|
156
162
|
* Flattens an intersection into a single object literal for readable editor hovers.
|
|
157
163
|
* @internal
|
|
@@ -166,9 +172,7 @@ type Simplify<T> = {
|
|
|
166
172
|
* Grouped columns come from {@link GroupedKeys}, not `keyof G`, so a `$group` the compiler could
|
|
167
173
|
* not read contributes none rather than all of them.
|
|
168
174
|
*/
|
|
169
|
-
export type QueryAggregateResult<E, G, A> = Simplify<{
|
|
170
|
-
-readonly [K in GroupedKeys<G> & FieldKey<E>]: E[K];
|
|
171
|
-
} & {
|
|
175
|
+
export type QueryAggregateResult<E, G, A> = Simplify<Pick<E, GroupedKeys<G> & FieldKey<E>> & {
|
|
172
176
|
-readonly [K in keyof A]: QueryAggregateFnResult<E, A[K]>;
|
|
173
177
|
}>;
|
|
174
178
|
/**
|
|
@@ -190,7 +194,7 @@ export type QueryHavingMap = {
|
|
|
190
194
|
* querier.aggregate(User, {
|
|
191
195
|
* $where: { deletedAt: { $isNull: true } },
|
|
192
196
|
* $group: { status: true },
|
|
193
|
-
* $
|
|
197
|
+
* $select: { count: { $count: '*' }, avgAge: { $avg: { age: true } } },
|
|
194
198
|
* $having: { count: { $gt: 5 } },
|
|
195
199
|
* $sort: { count: -1 },
|
|
196
200
|
* });
|
|
@@ -203,17 +207,22 @@ export type QueryAggregate<E, G extends QueryGroupMap<E> = QueryGroupMap<E>, A e
|
|
|
203
207
|
readonly $where?: QueryWhere<E>;
|
|
204
208
|
/**
|
|
205
209
|
* Columns to group by - `{ status: true }`, typed against the entity like `$select`. A computed
|
|
206
|
-
* aggregate wrongly placed here (it belongs in `$
|
|
207
|
-
* `$group` is captured as a generic and a bare generic skips excess-property checking.
|
|
210
|
+
* aggregate wrongly placed here (it belongs in `$select`) is rejected via {@link Reject}, since
|
|
211
|
+
* `$group` is captured as a generic and a bare generic skips excess-property checking. The captured
|
|
212
|
+
* map meets its schema, {@link QueryGroupMap}, so each key keeps its link to the entity property.
|
|
208
213
|
*/
|
|
209
|
-
readonly $group?: G & Reject<Exclude<keyof G, FieldKey<E>>>;
|
|
214
|
+
readonly $group?: G & QueryGroupMap<E> & Reject<Exclude<keyof G, FieldKey<E>>>;
|
|
210
215
|
/**
|
|
211
|
-
* Computed aggregate columns - `{ count: { $count: '*' }, avgAge: { $avg:
|
|
216
|
+
* Computed aggregate columns - `{ count: { $count: '*' }, avgAge: { $avg: { age: true } } }`. The
|
|
217
|
+
* captured map meets its schema over the same aliases, as `$group` does, so field keys stay linked
|
|
218
|
+
* (a `Record<keyof A, ...>` spelling of the same type breaks the inference of `A`).
|
|
212
219
|
*
|
|
213
220
|
* An alias repeating a `$group` column is rejected: both would be emitted under that one name,
|
|
214
221
|
* leaving the driver to keep whichever it read last.
|
|
215
222
|
*/
|
|
216
|
-
readonly $
|
|
223
|
+
readonly $select?: A & {
|
|
224
|
+
readonly [K in keyof A]: QueryAggregateFn<E>;
|
|
225
|
+
} & Reject<NamedKeys<A> & GroupedKeys<G>>;
|
|
217
226
|
/**
|
|
218
227
|
* Post-aggregation filtering, applied after grouping (SQL `HAVING`, MongoDB post-group `$match`).
|
|
219
228
|
* Keyed by the result columns (grouped columns + computed aliases), and each value is typed to that
|