uql-orm 0.56.0 → 0.58.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 (110) hide show
  1. package/README.md +7 -9
  2. package/dist/browser/uql-browser.min.js +2 -2
  3. package/dist/browser/uql-browser.min.js.map +4 -4
  4. package/dist/cockroachdb/cockroachDialect.d.ts +5 -2
  5. package/dist/cockroachdb/cockroachDialect.js +2 -10
  6. package/dist/d1/d1SqliteDialect.d.ts +1 -0
  7. package/dist/d1/d1SqliteDialect.js +2 -0
  8. package/dist/dialect/abstractSqlDialect.d.ts +196 -33
  9. package/dist/dialect/abstractSqlDialect.js +410 -203
  10. package/dist/dialect/aliases.d.ts +10 -7
  11. package/dist/dialect/aliases.js +12 -7
  12. package/dist/dialect/hydrateColumn.d.ts +8 -2
  13. package/dist/dialect/hydrateColumn.js +33 -1
  14. package/dist/dialect/jsonSql.d.ts +13 -5
  15. package/dist/dialect/jsonSql.js +24 -7
  16. package/dist/dialect/mysqlLikeSqlDialect.d.ts +31 -3
  17. package/dist/dialect/mysqlLikeSqlDialect.js +57 -5
  18. package/dist/dialect/pgLikeSqlDialect.d.ts +20 -20
  19. package/dist/dialect/pgLikeSqlDialect.js +23 -48
  20. package/dist/dialect/pgVectorMetrics.d.ts +13 -0
  21. package/dist/dialect/pgVectorMetrics.js +17 -0
  22. package/dist/dialect/queryContext.d.ts +3 -7
  23. package/dist/dialect/queryContext.js +13 -8
  24. package/dist/dialect/queryJoins.d.ts +8 -4
  25. package/dist/dialect/queryJoins.js +26 -11
  26. package/dist/dialect/vectorSqlDialect.d.ts +2 -2
  27. package/dist/dialect/vectorSqlDialect.js +2 -3
  28. package/dist/entity/decorator/bag.d.ts +2 -2
  29. package/dist/entity/decorator/entity.d.ts +8 -9
  30. package/dist/entity/decorator/entity.js +6 -7
  31. package/dist/entity/decorator/members.d.ts +7 -6
  32. package/dist/entity/decorator/members.js +2 -1
  33. package/dist/entity/metadata/definition.d.ts +16 -11
  34. package/dist/entity/metadata/definition.js +54 -42
  35. package/dist/http/handler.d.ts +2 -2
  36. package/dist/http/handler.js +0 -1
  37. package/dist/maria/mariaDialect.d.ts +13 -6
  38. package/dist/maria/mariaDialect.js +29 -9
  39. package/dist/migrate/cli.d.ts +2 -3
  40. package/dist/migrate/cli.js +2 -2
  41. package/dist/migrate/codegen/entityCodeGenerator.js +6 -4
  42. package/dist/migrate/codegen/entityTypes.d.ts +1 -1
  43. package/dist/migrate/codegen/entityTypes.js +4 -3
  44. package/dist/migrate/codegen/indexDecoratorSource.d.ts +5 -4
  45. package/dist/migrate/codegen/indexDecoratorSource.js +17 -13
  46. package/dist/migrate/codegen/sourceLiteral.d.ts +2 -0
  47. package/dist/migrate/codegen/sourceLiteral.js +4 -0
  48. package/dist/migrate/ddl/index.d.ts +1 -5
  49. package/dist/migrate/ddl/index.js +14 -25
  50. package/dist/migrate/ddl/indexDdl.d.ts +11 -2
  51. package/dist/migrate/ddl/indexDdl.js +17 -1
  52. package/dist/migrate/ddl/mssqlIndexDdl.d.ts +10 -0
  53. package/dist/migrate/ddl/mssqlIndexDdl.js +10 -0
  54. package/dist/migrate/ddl/mysqlIndexDdl.d.ts +10 -17
  55. package/dist/migrate/ddl/mysqlIndexDdl.js +16 -27
  56. package/dist/migrate/ddl/pgIndexDdl.d.ts +18 -8
  57. package/dist/migrate/ddl/pgIndexDdl.js +29 -12
  58. package/dist/migrate/generator/mongoSchemaGenerator.d.ts +3 -3
  59. package/dist/migrate/migrator.d.ts +4 -4
  60. package/dist/migrate/schemaGenerator.d.ts +8 -8
  61. package/dist/migrate/schemaGenerator.js +5 -7
  62. package/dist/migrate/schemaGeneratorAsync.d.ts +2 -3
  63. package/dist/mongo/mongoDialect.d.ts +31 -18
  64. package/dist/mongo/mongoDialect.js +146 -104
  65. package/dist/mongo/mongodbQuerier.d.ts +10 -17
  66. package/dist/mongo/mongodbQuerier.js +31 -106
  67. package/dist/mssql/mssqlDialect.d.ts +16 -0
  68. package/dist/mssql/mssqlDialect.js +26 -4
  69. package/dist/mysql/mysqlDialect.d.ts +2 -0
  70. package/dist/mysql/mysqlDialect.js +4 -0
  71. package/dist/querier/abstractQuerier.d.ts +20 -36
  72. package/dist/querier/abstractQuerier.js +35 -129
  73. package/dist/querier/abstractQuerierPool.d.ts +2 -2
  74. package/dist/querier/abstractSqlQuerier.d.ts +4 -17
  75. package/dist/querier/abstractSqlQuerier.js +40 -50
  76. package/dist/schema/canonicalType.js +4 -4
  77. package/dist/schema/indexDifferences.js +4 -4
  78. package/dist/schema/schemaASTBuilder.d.ts +3 -3
  79. package/dist/schema/schemaASTBuilder.js +32 -3
  80. package/dist/schema/schemaASTDiffer.js +5 -5
  81. package/dist/sqlite/sqliteDialect.d.ts +20 -1
  82. package/dist/sqlite/sqliteDialect.js +38 -7
  83. package/dist/turso/tursoDialect.d.ts +2 -0
  84. package/dist/turso/tursoDialect.js +2 -0
  85. package/dist/type/config.d.ts +3 -3
  86. package/dist/type/dialect.d.ts +4 -5
  87. package/dist/type/entity.d.ts +110 -69
  88. package/dist/type/migration.d.ts +7 -7
  89. package/dist/type/migratorDialect.d.ts +4 -0
  90. package/dist/type/querier.d.ts +6 -6
  91. package/dist/type/querierPool.d.ts +2 -2
  92. package/dist/type/query.d.ts +41 -72
  93. package/dist/type/query.js +10 -5
  94. package/dist/type/queryAggregate.d.ts +43 -34
  95. package/dist/type/queryAggregate.js +1 -1
  96. package/dist/type/queryWhere.d.ts +12 -9
  97. package/dist/type/universalQuerier.d.ts +4 -4
  98. package/dist/util/dialect.util.d.ts +4 -4
  99. package/dist/util/dialect.util.js +24 -15
  100. package/dist/util/field.util.d.ts +5 -0
  101. package/dist/util/field.util.js +19 -0
  102. package/dist/util/object.util.d.ts +2 -0
  103. package/dist/util/object.util.js +4 -0
  104. package/dist/util/relationQuery.util.d.ts +12 -65
  105. package/dist/util/relationQuery.util.js +27 -81
  106. package/dist/util/rowKey.util.d.ts +1 -11
  107. package/dist/util/rowKey.util.js +1 -13
  108. package/package.json +1 -1
  109. package/dist/querier/relationCount.d.ts +0 -16
  110. package/dist/querier/relationCount.js +0 -121
@@ -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 FieldKey<E>]?: UpdateFieldValue<E[K]>;
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 RelationKey<E>]?: E[K];
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.
@@ -425,7 +423,8 @@ export type FieldOptions<V = TsTypeOf<FieldType>> = {
425
423
  */
426
424
  readonly autoIncrement?: boolean;
427
425
  /**
428
- * Index configuration. true for simple index, string for named index.
426
+ * `true` for an index over the column, a string to name it. A foreign key column is indexed unless
427
+ * this is `false`.
429
428
  */
430
429
  readonly index?: boolean | string;
431
430
  /**
@@ -477,17 +476,25 @@ export type FieldOptionsFor<V> = (FieldOptions<NonNullable<V>> & {
477
476
  * The entity a relation field points at: `Company` for both `company?: Company` and
478
477
  * `companies?: Company[]`.
479
478
  */
480
- export type RelationTarget<V> = NonNullable<Unpacked<NonNullable<V>>>;
479
+ export type RelationTarget<V> = Extract<Unpacked<V>, object>;
481
480
  /**
482
481
  * {@link RelationOptions} for a relation field declared as `V`, with `entity` required and pinned to
483
482
  * `V`'s own type, and the cardinality restricted to the ones that field shape can hold. Together those
484
483
  * reject `@ManyToOne({ entity: () => Other })` on a `Company` field, and any to-many cardinality on a
485
- * field that is not an array. An array field additionally needs a {@link RelationJoin}.
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.
486
489
  */
487
- export type RelationOptionsFor<V> = Omit<RelationOptions<RelationTarget<V>>, 'entity' | 'cardinality'> & {
490
+ export type RelationOptionsFor<V, O = unknown> = Omit<RelationOptions<RelationTarget<V>, O>, 'entity' | 'cardinality'> & {
488
491
  readonly entity: EntityGetter<RelationTarget<V>>;
489
492
  readonly cardinality: IsMany<V> extends true ? '1m' | 'mm' : '11' | 'm1';
490
- } & (IsMany<V> extends true ? RelationJoin<RelationTarget<V>> : unknown);
493
+ } & (({
494
+ readonly cardinality: '1m' | 'mm';
495
+ } & RelationJoin<RelationTarget<V>, O>) | {
496
+ readonly cardinality: '11' | 'm1';
497
+ });
491
498
  /**
492
499
  * The method names of an entity, so hook registrations name a method that exists.
493
500
  */
@@ -503,9 +510,13 @@ export type MethodKey<E> = {
503
510
  * entity graph almost always has. Nothing about the standard decorator spec changes that; it only removed
504
511
  * the reflected `design:type` that used to make `entity` optional.
505
512
  */
506
- export type EntityGetter<E = any> = () => Type<E>;
513
+ export type EntityGetter<E = object> = () => Type<E>;
507
514
  export type CascadeType = 'persist' | 'delete';
508
- export type RelationOptions<E = any> = {
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> = {
509
520
  entity: EntityGetter<E>;
510
521
  cardinality: RelationCardinality;
511
522
  readonly cascade?: boolean | CascadeType;
@@ -518,13 +529,24 @@ export type RelationOptions<E = any> = {
518
529
  */
519
530
  readonly onDelete?: ForeignKeyAction;
520
531
  readonly onUpdate?: ForeignKeyAction;
521
- mappedBy?: RelationMappedBy<E>;
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>;
522
534
  /**
523
535
  * The pivot entity of a many-to-many. Unconstrained by `E`: a pivot holds foreign keys to both
524
536
  * sides and is not a relation value of the target, so nothing about it is derivable from `E`.
525
537
  */
526
538
  through?: EntityGetter;
527
- references?: RelationReferences;
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>;
528
550
  };
529
551
  /**
530
552
  * A relation once `getMeta` has resolved it: `references` is filled in and `mappedBy` is the key its
@@ -537,46 +559,48 @@ export type RelationOptions<E = any> = {
537
559
  * from "declared, but an inverse side too, so neither owns the foreign key" needs the unresolved shape
538
560
  * still there to find. A phase-split metadata map costs more than the call parentheses it saves.
539
561
  */
540
- export type RelationMeta<E = any> = Omit<RelationOptions<E>, 'mappedBy' | 'references'> & {
541
- mappedBy?: Key<E>;
562
+ export type RelationMeta = RelationRegistration & {
542
563
  references: RelationReferences;
543
564
  };
544
- /** How a to-many owner reaches its children: a junction entity, or the join columns by name. */
545
- type RelationOwnerJoin<E> = Required<Pick<RelationOptions<E>, 'through'>> | Required<Pick<RelationOptions<E>, 'references'>>;
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
+ });
546
579
  /**
547
580
  * Every way a to-many can say where its rows are. Required because nothing about the field implies it:
548
581
  * without one of the three, resolution has no columns to join on and throws.
549
582
  */
550
- type RelationJoin<E> = RelationOwnerJoin<E> | Required<Pick<RelationOptions<E>, 'mappedBy'>>;
551
- 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'>;
552
585
  type RelationOptionsInverseSide<E> = Pick<RelationOptions<E>, 'entity' | 'cascade'> & Required<Pick<RelationOptions<E>, 'mappedBy'>>;
553
- type RelationOptionsThroughOwner<E> = Pick<RelationOptions<E>, 'entity' | 'cascade'> & RelationOwnerJoin<E>;
586
+ type RelationOptionsThroughOwner<E, O> = Pick<RelationOptions<E, O>, 'entity' | 'cascade'> & RelationOwnerJoin<E, O>;
554
587
  /**
555
- * The key names of `E` as values, so `mappedBy` can be written as `(user) => user.company` instead of
556
- * a string literal and survive a rename.
557
- *
558
- * Mapping over `Key<E>` rather than `keyof E` is what makes the callback usable: a homomorphic
559
- * `[K in keyof E]` inherits the entity's optional modifiers, so `user.company` is
560
- * `'company' | undefined` and {@link RelationKeyMapper} rejects it - every callback needed a `!`.
561
- *
562
- * At runtime a callback only ever reads one property off the map, so a single `Proxy` returning its
563
- * own key stands in for every entity's: see `RELATION_KEY_MAP`. A key that names neither a field nor
564
- * 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.
565
591
  */
566
- export type RelationKeyMap<E> = {
567
- readonly [K in Key<E>]: K;
592
+ export type KeyMap<E> = {
593
+ readonly [K in keyof E]-?: K;
568
594
  };
569
- export type RelationKeyMapper<E> = (keyMap: RelationKeyMap<E>) => Key<E>;
570
595
  export type RelationReferences = {
571
596
  readonly local: string;
572
597
  readonly foreign: string;
573
598
  }[];
574
- export type RelationMappedBy<E> = Key<E> | RelationKeyMapper<E>;
575
599
  export type RelationCardinality = '11' | 'm1' | '1m' | 'mm';
576
- export type RelationOneToOneOptions<E> = RelationOptionsOwner<E> | RelationOptionsInverseSide<E>;
577
- export type RelationOneToManyOptions<E> = RelationOptionsInverseSide<E> | RelationOptionsThroughOwner<E>;
578
- export type RelationManyToOneOptions<E> = RelationOptionsOwner<E>;
579
- 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>;
580
604
  /**
581
605
  * Lifecycle hook event names.
582
606
  */
@@ -613,10 +637,10 @@ export type IndexTypeOptions = {
613
637
  *
614
638
  * @example
615
639
  * ```ts
616
- * @Index(['tenantId', { column: 'createdAt', order: 'desc' }]) // keyset pagination
617
- * @Index([raw`lower("email")`], { unique: true }) // case-insensitive uniqueness
618
- * @Index([{ column: 'body', length: 64 }]) // MySQL needs a prefix on TEXT
619
- * @Index(['data'], { type: 'gin' }) // JSONB containment
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
620
644
  * ```
621
645
  *
622
646
  * `C` is the entity's `FieldKey` on the `@Index`/`defineEntity` paths, where the decorated class says
@@ -704,8 +728,8 @@ export type IndexColumnModifiers = {
704
728
  *
705
729
  * @example
706
730
  * ```ts
707
- * @Index([{ column: 'kind', jsonPath: { path: 'theme.color', type: String } }]) // 'kind.theme.color': 'red'
708
- * @Index([{ column: 'kind', jsonPath: { path: 'rating', type: Number } }]) // 'kind.rating': { $gte: 4 }
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 }
709
733
  * ```
710
734
  */
711
735
  export type IndexJsonPath = {
@@ -724,8 +748,8 @@ export type IndexJsonPath = {
724
748
  *
725
749
  * @example
726
750
  * ```ts
727
- * @Index([{ column: 'tags', jsonArray: { type: String, length: 64 } }]) // tags: { $all: [...] }
728
- * @Index([{ column: 'kind', jsonArray: { path: 'ids', type: Number } }]) // 'kind.ids': { $all: [...] }
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: [...] }
729
753
  * ```
730
754
  */
731
755
  export type IndexJsonArray = {
@@ -741,6 +765,8 @@ type IndexColumnPlainModifiers = Except<IndexColumnModifiers, 'jsonPath' | 'json
741
765
  export type IndexColumnOptions<C extends string = string> = IndexColumnPlainModifiers & {
742
766
  /** The column to index, or `raw(...)` for an expression. */
743
767
  readonly column: C | QueryRaw;
768
+ readonly jsonPath?: never;
769
+ readonly jsonArray?: never;
744
770
  };
745
771
  /**
746
772
  * One index entry, normalized: {@link IndexColumnInput}'s three authored shapes all reduce to this
@@ -829,9 +855,21 @@ export type CheckOptions = {
829
855
  */
830
856
  export type EntityMembers = {
831
857
  readonly fields?: Readonly<Record<string, FieldOptions | undefined>>;
832
- readonly relations?: Readonly<Record<string, RelationOptions | undefined>>;
858
+ readonly relations?: Readonly<Record<string, RelationRegistration | undefined>>;
833
859
  readonly hooks?: Readonly<Partial<Record<HookEvent, readonly string[]>>>;
834
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
+ };
835
873
  /**
836
874
  * Configurable options for an entity (`@Entity()` / `defineEntity`).
837
875
  *
@@ -848,38 +886,41 @@ export type EntityOptions<E = unknown> = {
848
886
  /** Named, default-on `$where` filters (soft-delete is auto-registered from `@Field({ softDelete })`). */
849
887
  readonly filters?: Record<string, FilterOptions<E>>;
850
888
  /** Scalar fields; use `isId: true` on exactly one field for the primary key. */
851
- readonly fields?: {
852
- readonly [K in FieldKey<E>]?: FieldOptionsFor<E[K]>;
853
- };
854
- readonly relations?: {
855
- readonly [K in RelationKey<E>]?: RelationOptionsFor<E[K]>;
856
- };
857
- readonly indexes?: readonly EntityIndexInput<FieldKey<E>, E>[];
889
+ readonly fields?: EntityFieldOptions<E>;
890
+ readonly relations?: EntityRelationOptions<E>;
891
+ readonly indexes?: readonly EntityIndexInput<E>[];
858
892
  /** Table-level `CHECK` constraints. See {@link CheckOptions}. */
859
893
  readonly checks?: readonly CheckOptions[];
860
- /** Map hook events to method names on the entity class. */
861
- readonly hooks?: Partial<Record<HookEvent, readonly MethodKey<E>[]>>;
894
+ /** Each lifecycle event and the methods it runs, read off the key map: `{ beforeInsert: (post) => [post.stamp] }`. */
895
+ readonly hooks?: Partial<Record<HookEvent, (keys: KeyMap<E>) => readonly MethodKey<E>[]>>;
862
896
  };
863
897
  /**
864
- * Everything an index carries beyond its columns, shared by `@Index`, `defineEntity` and the
865
- * migration builder's `table.index(...)`. `Except` (not plain `Omit`) keeps `type`/`distance` a
866
- * discriminated pair: omitting `distance` on a vector index type is a compile error.
898
+ * Everything an index carries beyond its columns, as the migration builder's `table.index(...)` takes it,
899
+ * and through {@link EntityIndexOptions} `@Index` and `defineEntity`. `Except` (not plain `Omit`) keeps
900
+ * `type`/`distance` a discriminated pair: omitting `distance` on a vector index type is a compile error.
867
901
  */
868
- export type IndexOptions<E = unknown> = Except<EntityIndexMeta, 'columns' | 'include' | 'where'> & {
869
- /** Non-key columns stored in the index; a typo builds nothing, the server refusing the statement. */
870
- readonly include?: readonly IndexFieldKey<E>[];
902
+ export type IndexOptions = Except<EntityIndexMeta, 'columns' | 'include' | 'where'> & {
903
+ /** Non-key columns stored in the index, by column name; a typo builds nothing, the server refusing it. */
904
+ readonly include?: readonly string[];
871
905
  /**
872
906
  * Partial-index predicate. `raw` with no interpolation, like an index expression: this is DDL, so
873
907
  * there is no placeholder for a bound value. A bare string is the older spelling and still works.
874
908
  */
875
909
  readonly where?: string | QueryRaw;
876
910
  };
877
- /** A field of `E`, or any name where there is no entity to check it against - the migration builder. */
878
- type IndexFieldKey<E> = unknown extends E ? string : FieldKey<E>;
879
911
  /**
880
- * An index as authored, before `defineIndex` normalizes its columns.
912
+ * {@link IndexOptions} on an entity, whose stored columns are read off its key map, `(post) => [post.slug]`,
913
+ * so they are checked against it and follow a rename. The migration builder names raw columns instead.
914
+ */
915
+ export type EntityIndexOptions<E> = Except<IndexOptions, 'include'> & {
916
+ readonly include?: (keys: KeyMap<E>) => readonly FieldKey<E>[];
917
+ };
918
+ /**
919
+ * An index as authored on an entity, before `defineIndex` reads its columns off the key map. Only the
920
+ * member lists are callbacks: TypeScript never checks a callback's returned literal for excess properties,
921
+ * so the options stay a literal of their own, where `uniqe: true` is a compile error.
881
922
  */
882
- export type EntityIndexInput<C extends string = string, E = unknown> = IndexOptions<E> & {
883
- readonly columns: readonly IndexColumnInput<C, E>[];
923
+ export type EntityIndexInput<E> = EntityIndexOptions<E> & {
924
+ readonly columns: (keys: KeyMap<E>) => readonly IndexColumnInput<FieldKey<E>, E>[];
884
925
  };
885
926
  export {};
@@ -73,7 +73,7 @@ export interface MigratorOptions {
73
73
  /**
74
74
  * Entities to use for schema generation
75
75
  */
76
- readonly entities?: Type<unknown>[];
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([...])`, still spells this `columns`, since that is what
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<unknown>;
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<unknown>[], options?: CreateSchemaOptions): string[];
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<unknown>[], options?: DropSchemaOptions): string[];
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<E>(entity: Type<E>, currentTable: TableNode | undefined, desiredAst?: SchemaAST): SchemaDiff | undefined;
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<unknown>[]): SchemaAST;
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.
@@ -1,4 +1,8 @@
1
+ import type { AbstractSqlDialect } from '../dialect/abstractSqlDialect.js';
2
+ import type { MongoDialect } from '../mongo/mongoDialect.js';
1
3
  import type { DialectName } from './querier.js';
4
+ /** The dialects the migrator runs on, which `dialectName` tells apart: every SQL engine, and MongoDB. */
5
+ export type MigratorDialect = AbstractSqlDialect | MongoDialect;
2
6
  declare const KNOWN_MIGRATOR_DIALECTS: readonly ["postgres", "cockroachdb", "mysql", "mariadb", "sqlite", "mssql", "mongodb"];
3
7
  export type KnownMigratorDialect = (typeof KNOWN_MIGRATOR_DIALECTS)[number];
4
8
  /**
@@ -4,7 +4,7 @@ import type { SqlDialectName } from './dialect.js';
4
4
  import type { FieldKey, HookEvent, RelationKey } from './entity.js';
5
5
  import type { LoggingOptions } from './logger.js';
6
6
  import type { NamingStrategy } from './namingStrategy.js';
7
- import type { QueryFilter, QueryFindResult, QueryOneProjected, QueryOptions, QueryPage, QueryProjected, QuerySearch, QueryStreamProjected, QueryUpdateResult } from './query.js';
7
+ import type { QueryFilter, QueryFindResult, QueryOneProjected, QueryOptions, QueryPage, QueryProjected, QuerySearch, QueryUpdateResult } from './query.js';
8
8
  import type { UniversalQuerier } from './universalQuerier.js';
9
9
  import type { Type } from './utility.js';
10
10
  /**
@@ -47,13 +47,13 @@ export interface Querier extends UniversalQuerier {
47
47
  }, opts?: QueryOptions): Promise<QueryFindResult<E, S, V, X, P, C>[]>;
48
48
  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>, opts?: QueryOptions): Promise<QueryFindResult<E, S, V, X, P, C>[]>;
49
49
  /**
50
- * Stream records as an async iterable. Supports both patterns.
51
- * Does not fill relations or fire lifecycle hooks.
50
+ * Stream records as an async iterable, in both patterns, each with the relations and counts
51
+ * `findMany` reads. Fires no lifecycle hooks.
52
52
  */
53
- 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>(q: QueryStreamProjected<E, S, V, X, P> & {
53
+ 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>(q: QueryProjected<E, S, V, X, P, C> & {
54
54
  $entity: Type<E>;
55
- }, opts?: QueryOptions): AsyncIterable<QueryFindResult<E, S, V, X, P>>;
56
- 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>(entity: Type<E>, q: QueryStreamProjected<E, S, V, X, P>, opts?: QueryOptions): AsyncIterable<QueryFindResult<E, S, V, X, P>>;
55
+ }, opts?: QueryOptions): AsyncIterable<QueryFindResult<E, S, V, X, P, C>>;
56
+ 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>>;
57
57
  /**
58
58
  * Find many records and count. Supports both patterns.
59
59
  */
@@ -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<any, infer D> ? D : never;
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, any> ? Q : never;
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
  */
@@ -1,4 +1,4 @@
1
- import type { FieldKey, IdKey, JsonFieldPaths, RelationKey, RelationTarget, WrittenId } from './entity.js';
1
+ import type { FieldKey, JsonFieldPaths, RelationKey, RelationTarget, WrittenId } from './entity.js';
2
2
  import type { QueryLock } from './queryLock.js';
3
3
  import type { QueryRaw } from './queryRaw.js';
4
4
  import type { QueryWhere } from './queryWhere.js';
@@ -25,23 +25,13 @@ export type QueryOptions = {
25
25
  */
26
26
  autoPrefix?: boolean;
27
27
  };
28
- export type QuerySelectOptions = {
29
- /**
30
- * prefix the query with this.
31
- */
32
- prefix?: string;
33
- /**
34
- * automatically add the prefix for the alias.
35
- */
36
- autoPrefixAlias?: boolean;
37
- };
38
28
  /**
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`.
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.
42
32
  */
43
- export type QuerySelect<E> = {
44
- [K in FieldKey<E>]?: BooleanLike;
33
+ export type QuerySelect<E, F extends keyof E = FieldKey<E>, V = BooleanLike> = {
34
+ [K in F]?: V;
45
35
  };
46
36
  /**
47
37
  * Accepted `$select` value: a field map, or raw SQL projections built with `raw()`
@@ -56,8 +46,8 @@ export type QueryExclude<E> = QuerySelect<E>;
56
46
  /**
57
47
  * relation population map.
58
48
  */
59
- export type QueryPopulate<E> = {
60
- [K in RelationKey<E>]?: BooleanLike | QueryPopulateRelationOptions<E[K]>;
49
+ export type QueryPopulate<E, R extends keyof E = RelationKey<E>> = {
50
+ [K in R]?: BooleanLike | QueryPopulateRelationOptions<E[K]>;
61
51
  };
62
52
  /**
63
53
  * The key a read carries its relation tallies under. One spelling for the type and the runtime that
@@ -66,23 +56,20 @@ export type QueryPopulate<E> = {
66
56
  export declare const COUNT_RESULT_KEY = "_count";
67
57
  /**
68
58
  * How many rows each named relation holds per parent, `true` for all of them or a filter to narrow
69
- * which ones count. One statement per relation named here, batched over every parent at once, so it
70
- * stays flat however many rows the read returned. Comes back under `_count`, which keeps it clear of
71
- * a relation of the same name that `$populate` filled with rows.
59
+ * which ones count: a correlated count in the read's own statement, so no related row is loaded. Comes
60
+ * back under `_count`, which keeps it clear of a relation of the same name `$populate` filled.
72
61
  */
73
- export type QueryCount<E> = {
74
- [K in ToManyRelationKey<E>]?: BooleanLike | QueryFilter<RelationTarget<E[K]>>;
62
+ export type QueryCount<E, R extends keyof E = ToManyRelationKey<E>> = {
63
+ [K in R]?: BooleanLike | QueryFilter<RelationTarget<E[K]>>;
75
64
  };
76
65
  /**
77
66
  * query conflict paths - subset of field keys used to detect upsert conflicts.
78
67
  */
79
- export type QueryConflictPaths<E> = {
80
- [K in FieldKey<E>]?: true;
81
- };
68
+ export type QueryConflictPaths<E> = QuerySelect<E, FieldKey<E>, true>;
82
69
  /**
83
70
  * Options to populate a relation declared as `V`, by its cardinality.
84
71
  */
85
- export type QueryPopulateRelationOptions<V> = (IsMany<V> extends true ? Except<Query<RelationTarget<V>>, '$lock'> : QueryUnique<RelationTarget<V>>) & {
72
+ export type QueryPopulateRelationOptions<V> = IsMany<V> extends true ? RelationQuery<RelationTarget<V>> : QueryUnique<RelationTarget<V>> & {
86
73
  $required?: boolean;
87
74
  };
88
75
  /**
@@ -160,9 +147,11 @@ export type QuerySortByCount = {
160
147
  * against an intersection is repeated per constituent, which made this the single most expensive
161
148
  * type in the package to check.
162
149
  */
163
- export type QuerySortMap<E, Vector extends boolean = true> = {
164
- [K in FieldKey<E> | JsonFieldPaths<E> | RelationKey<E>]?: K extends RelationKey<E> ? IsMany<E[K]> extends true ? QuerySortByCount : QuerySortMap<RelationTarget<E[K]>, false> : K extends FieldKey<E> ? Vector extends true ? NonNullable<E[K]> extends readonly number[] ? QuerySortValue : QuerySortDirection : QuerySortDirection : QuerySortDirection;
165
- };
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
+ });
166
155
  /**
167
156
  * pager options.
168
157
  */
@@ -276,14 +265,13 @@ export type Query<E> = {
276
265
  * Declared beside the type they describe so the two cannot drift, and `satisfies` fails the build
277
266
  * rather than the runtime if a clause is ever renamed.
278
267
  *
279
- * `$lock` belongs to no group on purpose: it is the one clause neither a wire query nor a relation's
280
- * query accepts, so leaving it out is what excludes it from both.
268
+ * `$lock` is only in {@link QUERY_STATEMENT_CLAUSES}: neither a wire query nor a relation's query
269
+ * accepts it.
281
270
  */
282
271
  export declare const QUERY_OBJECT_CLAUSES: readonly ["$select", "$populate", "$exclude", "$where", "$sort"];
283
272
  /**
284
- * Object clauses only the statement itself takes, never a relation's own query - the mirror of
285
- * `$lock`, which neither takes. Counting a relation is batched over the rows a read returned, and a
286
- * populated relation's rows are assembled after that, so there is nothing for a nested one to count.
273
+ * Object clauses only the statement itself takes: a populated relation's rows keep their declared type,
274
+ * so a `$count` inside one would have no `_count` to land in.
287
275
  */
288
276
  export declare const QUERY_ROOT_OBJECT_CLAUSES: readonly ["$count"];
289
277
  export declare const QUERY_NUMBER_CLAUSES: readonly ["$skip", "$limit"];
@@ -294,6 +282,16 @@ export declare const QUERY_NUMBER_CLAUSES: readonly ["$skip", "$limit"];
294
282
  */
295
283
  export declare const QUERY_ROOT_NUMBER_CLAUSES: readonly ["$candidates"];
296
284
  export declare const QUERY_BOOLEAN_CLAUSES: readonly ["$distinct"];
285
+ /** The clauses that describe the statement, which a populated relation's own query refuses by name. */
286
+ export declare const QUERY_STATEMENT_CLAUSES: readonly ["$lock", "$count", "$candidates"];
287
+ type RelationClause = (typeof QUERY_OBJECT_CLAUSES | typeof QUERY_NUMBER_CLAUSES | typeof QUERY_BOOLEAN_CLAUSES)[number];
288
+ /**
289
+ * A populated relation's own query: the clause groups its runtime check accepts, so the two cannot
290
+ * drift, and a clause added to {@link Query} stays off it until it joins one of them.
291
+ */
292
+ export type RelationQuery<E = object> = Pick<Query<E>, RelationClause> & {
293
+ $required?: boolean;
294
+ };
297
295
  /**
298
296
  * options to get a single record.
299
297
  */
@@ -315,33 +313,12 @@ export type QueryUnique<E> = Pick<QueryOne<E>, '$select' | '$exclude' | '$popula
315
313
  * populated relation's own query - stays the concrete {@link Query} it is today.
316
314
  * @internal
317
315
  */
318
- type QueryProjection<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>, C extends RelationKey<E>> = QueryStreamProjection<E, S, V, X, P> & {
319
- $count?: {
320
- [K in C & keyof QueryCount<E>]?: QueryCount<E>[K];
321
- };
322
- };
323
- /**
324
- * {@link QueryProjection} without `$count`, which a stream cannot honor. Split out rather than
325
- * subtracted afterwards: an optional key is a *known* key even when its value maps over `never`, so
326
- * a statement that must not take the clause has to be built without it in the first place.
327
- * @internal
328
- */
329
- type QueryStreamProjection<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>> = {
330
- $select?: {
331
- [K in S]?: V;
332
- } | readonly QueryRaw[];
333
- $exclude?: {
334
- [K in X]?: V;
335
- };
336
- $populate?: {
337
- [K in P]?: QueryPopulate<E>[K];
338
- };
316
+ type QueryProjection<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>, C extends RelationKey<E>> = {
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>>;
339
321
  };
340
- /**
341
- * A {@link QueryProjected} a stream can honor: no `$count`, which is batched over a result set a
342
- * stream never holds all of.
343
- */
344
- export type QueryStreamProjected<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>> = Except<Query<E>, '$count'> & QueryStreamProjection<E, S, V, X, P>;
345
322
  /**
346
323
  * A {@link Query} whose projection is captured, so {@link QueryFindResult} can shape the row.
347
324
  */
@@ -357,15 +334,7 @@ export type QueryOneProjected<E, S extends FieldKey<E>, V, X extends FieldKey<E>
357
334
  * why `$exclude` is only read on the branch where there is none.
358
335
  * @internal
359
336
  */
360
- type ProjectedKeys<E, S, V, X, P, C> = ([V] extends [false | 0] ? Exclude<FieldKey<E>, S> : [S] extends [never] ? Exclude<FieldKey<E>, X> : S) | P | ([P | C] extends [never] ? never : NamedIdKey<E>);
361
- /**
362
- * The id key when it can be named, and nothing when it cannot: {@link IdKey} widens to *every* field
363
- * for an entity whose id is neither branded nor called `id`/`_id`/`uuid`, and adding that back would
364
- * hand the caller a row claiming fields the query never fetched. Missing an id costs a `$select`
365
- * entry; promising absent fields is the bug this type exists to prevent.
366
- * @internal
367
- */
368
- type NamedIdKey<E> = [FieldKey<E>] extends [IdKey<E>] ? never : IdKey<E>;
337
+ type ProjectedKeys<E, S, V, X, P> = ([V] extends [false | 0] ? Exclude<FieldKey<E>, S> : [S] extends [never] ? Exclude<FieldKey<E>, X> : S) | P;
369
338
  /**
370
339
  * Whether every entry of the captured map says the same thing: all selected, or all subtracted.
371
340
  * @internal
@@ -394,8 +363,8 @@ type CountedRelations<C extends PropertyKey> = [C] extends [never] ? unknown : {
394
363
  };
395
364
  };
396
365
  /** @internal */
397
- type QueryProjectedRow<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>, C extends RelationKey<E>> = [S | X] extends [never] ? E : IsUniform<V> extends true ? [PopulatedToMany<E, P>] extends [never] ? Pick<E, ProjectedKeys<E, S, V, X, P, C> & keyof E> : // A populated to-many is always a list, empty where the parent has no children, so it maps
398
- Pick<E, Exclude<ProjectedKeys<E, S, V, X, P, C>, PopulatedToMany<E, P>> & keyof E> & {
366
+ type QueryProjectedRow<E, S extends FieldKey<E>, V, X extends FieldKey<E>, P extends RelationKey<E>, C extends RelationKey<E>> = [S | X] extends [never] ? E : IsUniform<V> extends true ? [PopulatedToMany<E, P>] extends [never] ? Pick<E, ProjectedKeys<E, S, V, X, P> & keyof E> : // A populated to-many is always a list, empty where the parent has no children, so it maps
367
+ Pick<E, Exclude<ProjectedKeys<E, S, V, X, P>, PopulatedToMany<E, P>> & keyof E> & {
399
368
  [K in PopulatedToMany<E, P>]-?: NonNullable<E[K]>;
400
369
  } : E;
401
370
  /** The to-many relations a query populated, which come back as lists rather than as optional ones. */