@rvoh/dream 1.0.5 → 1.1.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 (212) hide show
  1. package/dist/cjs/src/Dream.js +2 -3
  2. package/dist/cjs/src/decorators/field/association/BelongsTo.js +3 -0
  3. package/dist/cjs/src/decorators/field/association/HasMany.js +1 -3
  4. package/dist/cjs/src/decorators/field/association/HasOne.js +1 -3
  5. package/dist/cjs/src/dream/QueryDriver/Kysely.js +349 -346
  6. package/dist/cjs/src/dream/internal/{extractAssociationMetadataFromAssociationName.js → associationStringToNameAndAlias.js} +3 -3
  7. package/dist/cjs/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.js +14 -0
  8. package/dist/cjs/src/dream/internal/unaliasTableName.js +2 -2
  9. package/dist/cjs/src/errors/UnexpectedUndefined.js +1 -3
  10. package/dist/cjs/src/errors/associations/{ArrayTargetsIncompatibleWithThroughAssociation.js → ArrayTargetIncompatibleWithThroughAssociation.js} +2 -2
  11. package/dist/cjs/src/errors/associations/ArrayTargetOnlyOnPolymorphicBelongsTo.js +19 -0
  12. package/dist/cjs/src/errors/associations/JoinAttemptedOnMissingAssociation.js +1 -1
  13. package/dist/cjs/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.js +20 -0
  14. package/dist/esm/src/Dream.js +2 -3
  15. package/dist/esm/src/decorators/field/association/BelongsTo.js +3 -0
  16. package/dist/esm/src/decorators/field/association/HasMany.js +1 -3
  17. package/dist/esm/src/decorators/field/association/HasOne.js +1 -3
  18. package/dist/esm/src/dream/QueryDriver/Kysely.js +349 -346
  19. package/dist/esm/src/dream/internal/{extractAssociationMetadataFromAssociationName.js → associationStringToNameAndAlias.js} +2 -2
  20. package/dist/esm/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.js +11 -0
  21. package/dist/esm/src/dream/internal/unaliasTableName.js +2 -2
  22. package/dist/esm/src/errors/UnexpectedUndefined.js +1 -3
  23. package/dist/esm/src/errors/associations/{ArrayTargetsIncompatibleWithThroughAssociation.js → ArrayTargetIncompatibleWithThroughAssociation.js} +1 -1
  24. package/dist/esm/src/errors/associations/ArrayTargetOnlyOnPolymorphicBelongsTo.js +16 -0
  25. package/dist/esm/src/errors/associations/JoinAttemptedOnMissingAssociation.js +1 -1
  26. package/dist/esm/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.js +17 -0
  27. package/dist/types/src/Dream.d.ts +0 -1
  28. package/dist/types/src/dream/QueryDriver/Kysely.d.ts +55 -27
  29. package/dist/types/src/dream/internal/associationStringToNameAndAlias.d.ts +4 -0
  30. package/dist/types/src/dream/internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.d.ts +11 -0
  31. package/dist/types/src/errors/associations/{ArrayTargetsIncompatibleWithThroughAssociation.d.ts → ArrayTargetIncompatibleWithThroughAssociation.d.ts} +1 -1
  32. package/dist/types/src/errors/associations/ArrayTargetOnlyOnPolymorphicBelongsTo.d.ts +10 -0
  33. package/dist/types/src/errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.d.ts +12 -0
  34. package/dist/types/src/types/associations/shared.d.ts +2 -4
  35. package/dist/types/src/types/associations/shared.ts +2 -12
  36. package/docs/classes/Benchmark.html +2 -2
  37. package/docs/classes/CalendarDate.html +2 -2
  38. package/docs/classes/CliFileWriter.html +2 -2
  39. package/docs/classes/CreateOrFindByFailedToCreateAndFind.html +3 -3
  40. package/docs/classes/Decorators.html +19 -19
  41. package/docs/classes/Dream.html +136 -136
  42. package/docs/classes/DreamApp.html +4 -4
  43. package/docs/classes/DreamBin.html +2 -2
  44. package/docs/classes/DreamCLI.html +4 -4
  45. package/docs/classes/DreamImporter.html +2 -2
  46. package/docs/classes/DreamLogos.html +2 -2
  47. package/docs/classes/DreamMigrationHelpers.html +7 -7
  48. package/docs/classes/DreamSerializerBuilder.html +8 -8
  49. package/docs/classes/DreamTransaction.html +2 -2
  50. package/docs/classes/Encrypt.html +2 -2
  51. package/docs/classes/Env.html +2 -2
  52. package/docs/classes/GlobalNameNotSet.html +3 -3
  53. package/docs/classes/NonLoadedAssociation.html +3 -3
  54. package/docs/classes/ObjectSerializerBuilder.html +8 -8
  55. package/docs/classes/Query.html +60 -60
  56. package/docs/classes/Range.html +2 -2
  57. package/docs/classes/RecordNotFound.html +3 -3
  58. package/docs/classes/ValidationError.html +3 -3
  59. package/docs/functions/DreamSerializer.html +1 -1
  60. package/docs/functions/ObjectSerializer.html +1 -1
  61. package/docs/functions/ReplicaSafe.html +1 -1
  62. package/docs/functions/STI.html +1 -1
  63. package/docs/functions/SoftDelete.html +1 -1
  64. package/docs/functions/camelize.html +1 -1
  65. package/docs/functions/capitalize.html +1 -1
  66. package/docs/functions/cloneDeepSafe.html +1 -1
  67. package/docs/functions/closeAllDbConnections.html +1 -1
  68. package/docs/functions/compact.html +1 -1
  69. package/docs/functions/dreamDbConnections.html +1 -1
  70. package/docs/functions/dreamPath.html +1 -1
  71. package/docs/functions/expandStiClasses.html +1 -1
  72. package/docs/functions/generateDream.html +1 -1
  73. package/docs/functions/globalClassNameFromFullyQualifiedModelName.html +1 -1
  74. package/docs/functions/groupBy.html +1 -1
  75. package/docs/functions/hyphenize.html +1 -1
  76. package/docs/functions/inferSerializerFromDreamOrViewModel.html +1 -1
  77. package/docs/functions/inferSerializersFromDreamClassOrViewModelClass.html +1 -1
  78. package/docs/functions/intersection.html +1 -1
  79. package/docs/functions/isDreamSerializer.html +1 -1
  80. package/docs/functions/isEmpty.html +1 -1
  81. package/docs/functions/loadRepl.html +1 -1
  82. package/docs/functions/lookupClassByGlobalName.html +1 -1
  83. package/docs/functions/normalizeUnicode.html +1 -1
  84. package/docs/functions/pascalize.html +1 -1
  85. package/docs/functions/pgErrorType.html +1 -1
  86. package/docs/functions/range-1.html +1 -1
  87. package/docs/functions/relativeDreamPath.html +1 -1
  88. package/docs/functions/round.html +1 -1
  89. package/docs/functions/serializerNameFromFullyQualifiedModelName.html +1 -1
  90. package/docs/functions/sharedPathPrefix.html +1 -1
  91. package/docs/functions/snakeify.html +1 -1
  92. package/docs/functions/sort.html +1 -1
  93. package/docs/functions/sortBy.html +1 -1
  94. package/docs/functions/sortObjectByKey.html +1 -1
  95. package/docs/functions/sortObjectByValue.html +1 -1
  96. package/docs/functions/standardizeFullyQualifiedModelName.html +1 -1
  97. package/docs/functions/uncapitalize.html +1 -1
  98. package/docs/functions/uniq.html +1 -1
  99. package/docs/functions/untypedDb.html +1 -1
  100. package/docs/functions/validateColumn.html +1 -1
  101. package/docs/functions/validateTable.html +1 -1
  102. package/docs/interfaces/BelongsToStatement.html +2 -2
  103. package/docs/interfaces/DecoratorContext.html +2 -2
  104. package/docs/interfaces/DreamAppInitOptions.html +2 -2
  105. package/docs/interfaces/DreamAppOpts.html +2 -2
  106. package/docs/interfaces/EncryptOptions.html +2 -2
  107. package/docs/interfaces/InternalAnyTypedSerializerRendersMany.html +2 -2
  108. package/docs/interfaces/InternalAnyTypedSerializerRendersOne.html +2 -2
  109. package/docs/interfaces/OpenapiDescription.html +2 -2
  110. package/docs/interfaces/OpenapiSchemaProperties.html +1 -1
  111. package/docs/interfaces/OpenapiSchemaPropertiesShorthand.html +1 -1
  112. package/docs/interfaces/OpenapiTypeFieldObject.html +1 -1
  113. package/docs/interfaces/SerializerRendererOpts.html +2 -2
  114. package/docs/types/Camelized.html +1 -1
  115. package/docs/types/CommonOpenapiSchemaObjectFields.html +1 -1
  116. package/docs/types/DateTime.html +1 -1
  117. package/docs/types/DbConnectionType.html +1 -1
  118. package/docs/types/DbTypes.html +1 -1
  119. package/docs/types/DreamAppAllowedPackageManagersEnum.html +1 -1
  120. package/docs/types/DreamAssociationMetadata.html +1 -1
  121. package/docs/types/DreamAttributes.html +1 -1
  122. package/docs/types/DreamClassAssociationAndStatement.html +1 -1
  123. package/docs/types/DreamClassColumn.html +1 -1
  124. package/docs/types/DreamColumn.html +1 -1
  125. package/docs/types/DreamColumnNames.html +1 -1
  126. package/docs/types/DreamLogLevel.html +1 -1
  127. package/docs/types/DreamLogger.html +1 -1
  128. package/docs/types/DreamModelSerializerType.html +1 -1
  129. package/docs/types/DreamOrViewModelClassSerializerKey.html +1 -1
  130. package/docs/types/DreamOrViewModelSerializerKey.html +1 -1
  131. package/docs/types/DreamParamSafeAttributes.html +1 -1
  132. package/docs/types/DreamParamSafeColumnNames.html +1 -1
  133. package/docs/types/DreamSerializable.html +1 -1
  134. package/docs/types/DreamSerializableArray.html +1 -1
  135. package/docs/types/DreamSerializerKey.html +1 -1
  136. package/docs/types/DreamSerializers.html +1 -1
  137. package/docs/types/DreamTableSchema.html +1 -1
  138. package/docs/types/DreamVirtualColumns.html +1 -1
  139. package/docs/types/EncryptAlgorithm.html +1 -1
  140. package/docs/types/HasManyStatement.html +1 -1
  141. package/docs/types/HasOneStatement.html +1 -1
  142. package/docs/types/Hyphenized.html +1 -1
  143. package/docs/types/IdType.html +1 -1
  144. package/docs/types/OpenapiAllTypes.html +1 -1
  145. package/docs/types/OpenapiFormats.html +1 -1
  146. package/docs/types/OpenapiNumberFormats.html +1 -1
  147. package/docs/types/OpenapiPrimitiveBaseTypes.html +1 -1
  148. package/docs/types/OpenapiPrimitiveTypes.html +1 -1
  149. package/docs/types/OpenapiSchemaArray.html +1 -1
  150. package/docs/types/OpenapiSchemaArrayShorthand.html +1 -1
  151. package/docs/types/OpenapiSchemaBase.html +1 -1
  152. package/docs/types/OpenapiSchemaBody.html +1 -1
  153. package/docs/types/OpenapiSchemaBodyShorthand.html +1 -1
  154. package/docs/types/OpenapiSchemaCommonFields.html +1 -1
  155. package/docs/types/OpenapiSchemaExpressionAllOf.html +1 -1
  156. package/docs/types/OpenapiSchemaExpressionAnyOf.html +1 -1
  157. package/docs/types/OpenapiSchemaExpressionOneOf.html +1 -1
  158. package/docs/types/OpenapiSchemaExpressionRef.html +1 -1
  159. package/docs/types/OpenapiSchemaExpressionRefSchemaShorthand.html +1 -1
  160. package/docs/types/OpenapiSchemaInteger.html +1 -1
  161. package/docs/types/OpenapiSchemaNull.html +1 -1
  162. package/docs/types/OpenapiSchemaNumber.html +1 -1
  163. package/docs/types/OpenapiSchemaObject.html +1 -1
  164. package/docs/types/OpenapiSchemaObjectAllOf.html +1 -1
  165. package/docs/types/OpenapiSchemaObjectAllOfShorthand.html +1 -1
  166. package/docs/types/OpenapiSchemaObjectAnyOf.html +1 -1
  167. package/docs/types/OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  168. package/docs/types/OpenapiSchemaObjectBase.html +1 -1
  169. package/docs/types/OpenapiSchemaObjectBaseShorthand.html +1 -1
  170. package/docs/types/OpenapiSchemaObjectOneOf.html +1 -1
  171. package/docs/types/OpenapiSchemaObjectOneOfShorthand.html +1 -1
  172. package/docs/types/OpenapiSchemaObjectShorthand.html +1 -1
  173. package/docs/types/OpenapiSchemaPrimitiveGeneric.html +1 -1
  174. package/docs/types/OpenapiSchemaShorthandExpressionAllOf.html +1 -1
  175. package/docs/types/OpenapiSchemaShorthandExpressionAnyOf.html +1 -1
  176. package/docs/types/OpenapiSchemaShorthandExpressionOneOf.html +1 -1
  177. package/docs/types/OpenapiSchemaShorthandExpressionSerializableRef.html +1 -1
  178. package/docs/types/OpenapiSchemaShorthandExpressionSerializerRef.html +1 -1
  179. package/docs/types/OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  180. package/docs/types/OpenapiSchemaString.html +1 -1
  181. package/docs/types/OpenapiShorthandAllTypes.html +1 -1
  182. package/docs/types/OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  183. package/docs/types/OpenapiShorthandPrimitiveTypes.html +1 -1
  184. package/docs/types/OpenapiTypeField.html +1 -1
  185. package/docs/types/Pascalized.html +1 -1
  186. package/docs/types/PrimaryKeyType.html +1 -1
  187. package/docs/types/RoundingPrecision.html +1 -1
  188. package/docs/types/SerializerCasing.html +1 -1
  189. package/docs/types/SimpleObjectSerializerType.html +1 -1
  190. package/docs/types/Snakeified.html +1 -1
  191. package/docs/types/Timestamp.html +1 -1
  192. package/docs/types/UpdateableAssociationProperties.html +1 -1
  193. package/docs/types/UpdateableProperties.html +1 -1
  194. package/docs/types/ValidationType.html +1 -1
  195. package/docs/types/ViewModel.html +1 -1
  196. package/docs/types/ViewModelClass.html +1 -1
  197. package/docs/types/WhereStatementForDream.html +1 -1
  198. package/docs/types/WhereStatementForDreamClass.html +1 -1
  199. package/docs/variables/DateTime-1.html +1 -1
  200. package/docs/variables/DreamAppAllowedPackageManagersEnumValues.html +1 -1
  201. package/docs/variables/DreamConst.html +1 -1
  202. package/docs/variables/TRIGRAM_OPERATORS.html +1 -1
  203. package/docs/variables/openapiPrimitiveTypes-1.html +1 -1
  204. package/docs/variables/openapiShorthandPrimitiveTypes-1.html +1 -1
  205. package/docs/variables/ops.html +1 -1
  206. package/docs/variables/primaryKeyTypes.html +1 -1
  207. package/package.json +2 -2
  208. package/CHANGELOG.md +0 -23
  209. package/dist/cjs/src/errors/associations/CannotAssociateThroughMultiplePolymorphics.js +0 -27
  210. package/dist/esm/src/errors/associations/CannotAssociateThroughMultiplePolymorphics.js +0 -24
  211. package/dist/types/src/dream/internal/extractAssociationMetadataFromAssociationName.d.ts +0 -4
  212. package/dist/types/src/errors/associations/CannotAssociateThroughMultiplePolymorphics.d.ts +0 -17
@@ -9,14 +9,14 @@ const index_js_2 = require("../../db/index.js");
9
9
  const associationToGetterSetterProp_js_1 = require("../../decorators/field/association/associationToGetterSetterProp.js");
10
10
  const PackageManager_js_1 = require("../../dream-app/helpers/PackageManager.js");
11
11
  const index_js_3 = require("../../dream-app/index.js");
12
- const ArrayTargetsIncompatibleWithThroughAssociation_js_1 = require("../../errors/associations/ArrayTargetsIncompatibleWithThroughAssociation.js");
13
- const CannotAssociateThroughMultiplePolymorphics_js_1 = require("../../errors/associations/CannotAssociateThroughMultiplePolymorphics.js");
12
+ const ArrayTargetIncompatibleWithThroughAssociation_js_1 = require("../../errors/associations/ArrayTargetIncompatibleWithThroughAssociation.js");
14
13
  const CannotJoinPolymorphicBelongsToError_js_1 = require("../../errors/associations/CannotJoinPolymorphicBelongsToError.js");
15
14
  const JoinAttemptedOnMissingAssociation_js_1 = require("../../errors/associations/JoinAttemptedOnMissingAssociation.js");
16
15
  const MissingRequiredAssociationAndClause_js_1 = require("../../errors/associations/MissingRequiredAssociationAndClause.js");
17
16
  const MissingRequiredPassthroughForAssociationAndClause_js_1 = require("../../errors/associations/MissingRequiredPassthroughForAssociationAndClause.js");
18
17
  const MissingThroughAssociation_js_1 = require("../../errors/associations/MissingThroughAssociation.js");
19
18
  const MissingThroughAssociationSource_js_1 = require("../../errors/associations/MissingThroughAssociationSource.js");
19
+ const ThroughAssociationConditionsIncompatibleWithThroughAssociationSource_js_1 = require("../../errors/associations/ThroughAssociationConditionsIncompatibleWithThroughAssociationSource.js");
20
20
  const CannotNegateSimilarityClause_js_1 = require("../../errors/CannotNegateSimilarityClause.js");
21
21
  const CannotPassUndefinedAsAValueToAWhereClause_js_1 = require("../../errors/CannotPassUndefinedAsAValueToAWhereClause.js");
22
22
  const UnexpectedUndefined_js_1 = require("../../errors/UnexpectedUndefined.js");
@@ -32,7 +32,6 @@ const loadPgClient_js_1 = require("../../helpers/db/loadPgClient.js");
32
32
  const runMigration_js_1 = require("../../helpers/db/runMigration.js");
33
33
  const EnvInternal_js_1 = require("../../helpers/EnvInternal.js");
34
34
  const isEmpty_js_1 = require("../../helpers/isEmpty.js");
35
- const isObject_js_1 = require("../../helpers/isObject.js");
36
35
  const namespaceColumn_js_1 = require("../../helpers/namespaceColumn.js");
37
36
  const normalizeUnicode_js_1 = require("../../helpers/normalizeUnicode.js");
38
37
  const objectPathsToArrays_js_1 = require("../../helpers/objectPathsToArrays.js");
@@ -44,8 +43,9 @@ const uniq_js_1 = require("../../helpers/uniq.js");
44
43
  const curried_ops_statement_js_1 = require("../../ops/curried-ops-statement.js");
45
44
  const ops_statement_js_1 = require("../../ops/ops-statement.js");
46
45
  const constants_js_1 = require("../constants.js");
46
+ const throughAssociationHasOptionsBesidesThroughAndSource_js_1 = require("../internal/associations/throughAssociationHasOptionsBesidesThroughAndSource.js");
47
+ const associationStringToNameAndAlias_js_1 = require("../internal/associationStringToNameAndAlias.js");
47
48
  const executeDatabaseQuery_js_1 = require("../internal/executeDatabaseQuery.js");
48
- const extractAssociationMetadataFromAssociationName_js_1 = require("../internal/extractAssociationMetadataFromAssociationName.js");
49
49
  const orderByDirection_js_1 = require("../internal/orderByDirection.js");
50
50
  const shouldBypassDefaultScope_js_1 = require("../internal/shouldBypassDefaultScope.js");
51
51
  const SimilarityBuilder_js_1 = require("../internal/similarity/SimilarityBuilder.js");
@@ -665,14 +665,12 @@ class KyselyQueryDriver extends Base_js_1.default {
665
665
  const aliases = Object.keys(aliasToDreamClassesMap);
666
666
  let nextColumnAliasCounter = 0;
667
667
  aliases.forEach((aliasOrExpression) => {
668
- const alias = (0, extractAssociationMetadataFromAssociationName_js_1.default)(aliasOrExpression).alias;
669
- if (alias === undefined)
670
- throw new UnexpectedUndefined_js_1.default();
668
+ const { name, alias: _alias } = (0, associationStringToNameAndAlias_js_1.default)(aliasOrExpression);
669
+ const alias = _alias || name;
671
670
  associationAliasToColumnAliasMap[alias] ||= {};
672
671
  const aliasedDreamClass = aliasToDreamClassesMap[alias];
673
672
  if (aliasedDreamClass === undefined)
674
673
  throw new UnexpectedUndefined_js_1.default();
675
- const association = aliasToAssociationsMap[alias];
676
674
  const columns = alias === this.query['baseSqlAlias']
677
675
  ? options.columns
678
676
  ? this.columnsWithRequiredLoadColumns(options.columns)
@@ -686,26 +684,6 @@ class KyselyQueryDriver extends Base_js_1.default {
686
684
  throw new UnexpectedUndefined_js_1.default();
687
685
  columnAliasMap[column] = columnAlias;
688
686
  });
689
- if (association?.type === 'HasOne' || association?.type === 'HasMany') {
690
- const setupPreloadData = (dbColumnName) => {
691
- const columnAlias = `dr${nextColumnAliasCounter++}`;
692
- const columnAliasMap = associationAliasToColumnAliasMap[association.through];
693
- if (columnAliasMap === undefined)
694
- throw new UnexpectedUndefined_js_1.default();
695
- columnAliasMap[dbColumnName] = columnAlias;
696
- kyselyQuery = kyselyQuery.select(`${this.namespaceColumn(dbColumnName, association.through)} as ${columnAlias}`);
697
- };
698
- if (association.through && association.preloadThroughColumns) {
699
- if ((0, isObject_js_1.default)(association.preloadThroughColumns)) {
700
- const preloadMap = association.preloadThroughColumns;
701
- Object.keys(preloadMap).forEach(columnName => setupPreloadData(columnName));
702
- }
703
- else {
704
- const preloadArray = association.preloadThroughColumns;
705
- preloadArray.forEach(columnName => setupPreloadData(columnName));
706
- }
707
- }
708
- }
709
687
  });
710
688
  const queryResults = await (0, executeDatabaseQuery_js_1.default)(kyselyQuery, 'execute');
711
689
  const aliasToDreamIdMap = queryResults.reduce((aliasToDreamIdMap, singleSqlResult) => {
@@ -749,23 +727,12 @@ class KyselyQueryDriver extends Base_js_1.default {
749
727
  return columnNameValueMap;
750
728
  }, {});
751
729
  const dream = this.dbResultToDreamInstance(columnValueMap, dreamClass);
752
- const association = aliasToAssociationsMap[currentAlias];
753
- if (association && association.through && association.preloadThroughColumns) {
754
- const throughAssociationColumnToColumnAliasMap = associationAliasToColumnAliasMap[association.through];
755
- if (throughAssociationColumnToColumnAliasMap === undefined)
756
- throw new UnexpectedUndefined_js_1.default();
757
- this.hydratePreloadedThroughColumns({
758
- association,
759
- columnToColumnAliasMap: throughAssociationColumnToColumnAliasMap,
760
- dream,
761
- singleSqlResult,
762
- });
763
- }
764
730
  aliasToDreamIdMap[(0, protectAgainstPollutingAssignment_js_1.default)(currentAlias)]?.set(primaryKeyValue, dream);
765
731
  }
766
732
  const dream = aliasToDreamIdMap[currentAlias].get(primaryKeyValue);
767
733
  Object.keys(leftJoinStatements).forEach(nextAlias => {
768
- const { name: associationName, alias } = (0, extractAssociationMetadataFromAssociationName_js_1.default)(nextAlias);
734
+ const { name: associationName, alias: _alias } = (0, associationStringToNameAndAlias_js_1.default)(nextAlias);
735
+ const alias = _alias || associationName;
769
736
  const association = dreamClass['getAssociationMetadata'](associationName);
770
737
  if (association === undefined)
771
738
  throw new UnexpectedUndefined_js_1.default();
@@ -801,38 +768,6 @@ class KyselyQueryDriver extends Base_js_1.default {
801
768
  });
802
769
  return dream;
803
770
  }
804
- hydratePreloadedThroughColumns({ association, columnToColumnAliasMap, dream, singleSqlResult, }) {
805
- if (!association.through)
806
- return;
807
- if (!dream.preloadedThroughColumns)
808
- return;
809
- let columnNames = [];
810
- const columnNameToPreloadedThroughColumnNameMap = {};
811
- if ((0, isObject_js_1.default)(association.preloadThroughColumns)) {
812
- const preloadMap = association.preloadThroughColumns;
813
- columnNames = Object.keys(preloadMap).map(columnName => {
814
- columnNameToPreloadedThroughColumnNameMap[columnName] = preloadMap[columnName];
815
- return columnName;
816
- });
817
- }
818
- else if (Array.isArray(association.preloadThroughColumns)) {
819
- columnNames = association.preloadThroughColumns.map(columnName => {
820
- columnNameToPreloadedThroughColumnNameMap[columnName] = columnName;
821
- return columnName;
822
- });
823
- }
824
- columnNames.forEach(columnName => {
825
- const preloadedThroughColumnName = columnNameToPreloadedThroughColumnNameMap[columnName];
826
- if (preloadedThroughColumnName === undefined)
827
- throw new UnexpectedUndefined_js_1.default();
828
- const columnAlias = columnToColumnAliasMap[columnName];
829
- if (columnAlias === undefined) {
830
- throw new UnexpectedUndefined_js_1.default();
831
- }
832
- ;
833
- dream.preloadedThroughColumns[preloadedThroughColumnName] = singleSqlResult[columnAlias];
834
- });
835
- }
836
771
  applyJoinAndStatement(join, joinAndStatement, rootTableOrAssociationAlias) {
837
772
  if (!joinAndStatement)
838
773
  return join;
@@ -878,7 +813,12 @@ class KyselyQueryDriver extends Base_js_1.default {
878
813
  joinStatement: query['innerJoinStatements'],
879
814
  joinAndStatements: query['innerJoinAndStatements'],
880
815
  dreamClass: query['dreamClass'],
881
- previousAssociationTableOrAlias: this.query['baseSqlAlias'],
816
+ // As recursivelyJoin progresses through the chain of associations that
817
+ // the developer has inclued in a join statement (e.g. `leftJoinPreload('hello as ho', 'world as wd')`),
818
+ // previousTableAlias will be modified so that the join statement can properly reference
819
+ // what we are joining on. When an `associationQuery` is the start of a join, then `baseSqlAlias` is set,
820
+ // this is passed into the very beginning of recursivelyJoin.
821
+ previousTableAlias: this.query['baseSqlAlias'],
882
822
  joinType: 'inner',
883
823
  });
884
824
  }
@@ -888,7 +828,12 @@ class KyselyQueryDriver extends Base_js_1.default {
888
828
  joinStatement: query['leftJoinStatements'],
889
829
  joinAndStatements: query['leftJoinAndStatements'],
890
830
  dreamClass: query['dreamClass'],
891
- previousAssociationTableOrAlias: this.query['baseSqlAlias'],
831
+ // As recursivelyJoin progresses through the chain of associations that
832
+ // the developer has inclued in a join statement (e.g. `leftJoinPreload('hello as ho', 'world as wd')`),
833
+ // previousTableAlias will be modified so that the join statement can properly reference
834
+ // what we are joining on. When an `associationQuery` is the start of a join, then `baseSqlAlias` is set,
835
+ // this is passed into the very beginning of recursivelyJoin.
836
+ previousTableAlias: this.query['baseSqlAlias'],
892
837
  joinType: 'left',
893
838
  });
894
839
  }
@@ -1090,26 +1035,41 @@ class KyselyQueryDriver extends Base_js_1.default {
1090
1035
  throw new CannotPassUndefinedAsAValueToAWhereClause_js_1.default(this.dreamClass, a2);
1091
1036
  return { a, b, c, a2, b2, c2 };
1092
1037
  }
1093
- recursivelyJoin({ query, joinStatement, joinAndStatements, dreamClass, previousAssociationTableOrAlias, joinType, }) {
1094
- for (const currentAssociationTableOrAlias of Object.keys(joinStatement)) {
1038
+ recursivelyJoin({ query, joinStatement, joinAndStatements, dreamClass, previousTableAlias, joinType, }) {
1039
+ for (const associationString of Object.keys(joinStatement)) {
1040
+ const joinAndStatement = joinAndStatements[associationString];
1041
+ const { association, alias } = associationStringToAssociationAndMaybeAlias({
1042
+ dreamClass,
1043
+ associationString,
1044
+ });
1095
1045
  const results = this.applyOneJoin({
1096
1046
  query,
1097
1047
  dreamClass,
1098
- previousAssociationTableOrAlias,
1099
- currentAssociationTableOrAlias,
1100
- joinAndStatements,
1048
+ association,
1049
+ previousTableAlias,
1050
+ explicitAlias: alias,
1051
+ joinAndStatement,
1101
1052
  joinType,
1102
- baseThroughAssociation: undefined,
1103
- polymorphicCount: { count: 0 },
1053
+ previousThroughAssociation: undefined,
1054
+ dreamClassThroughAssociationWantsToHydrate: undefined,
1104
1055
  });
1105
1056
  query = results.query;
1106
- const association = results.association;
1057
+ /**
1058
+ * If the returned association links to multiple models, then it is a polymorphic BelongsTo association,
1059
+ * and we can only join through a polymorphic BelongsTo association when the association included in
1060
+ * the join statement is a `through` association that clamps the types on the other side of the polymorphic
1061
+ * BelongsTo down to a single Dream model. So if an array, use the associated type of the association
1062
+ * included in the join statement.
1063
+ */
1064
+ const nextDreamClass = Array.isArray(results.association.modelCB())
1065
+ ? association.modelCB()
1066
+ : results.association.modelCB();
1107
1067
  query = this.recursivelyJoin({
1108
1068
  query,
1109
- joinStatement: joinStatement[currentAssociationTableOrAlias],
1110
- joinAndStatements: joinAndStatements[currentAssociationTableOrAlias],
1111
- dreamClass: association.modelCB(),
1112
- previousAssociationTableOrAlias: currentAssociationTableOrAlias,
1069
+ joinStatement: joinStatement[associationString],
1070
+ joinAndStatements: joinAndStatement,
1071
+ dreamClass: nextDreamClass,
1072
+ previousTableAlias: alias || association.as,
1113
1073
  joinType,
1114
1074
  });
1115
1075
  }
@@ -1171,21 +1131,12 @@ class KyselyQueryDriver extends Base_js_1.default {
1171
1131
  associationNamesToAssociationDataAndDreamClassesMap(associationNames) {
1172
1132
  const associationsToDreamClassesMap = {};
1173
1133
  associationNames.reduce((dreamClass, associationName) => {
1174
- const { name, alias } = (0, extractAssociationMetadataFromAssociationName_js_1.default)(associationName);
1134
+ const { name, alias: _alias } = (0, associationStringToNameAndAlias_js_1.default)(associationName);
1135
+ const alias = _alias || name;
1175
1136
  const association = dreamClass['getAssociationMetadata'](name);
1176
1137
  if (association === undefined)
1177
1138
  throw new UnexpectedUndefined_js_1.default();
1178
- const through = association.through;
1179
- if (through) {
1180
- const { throughAssociation, throughAssociationDreamClass } = this.throughAssociationDetails(dreamClass, through);
1181
- associationsToDreamClassesMap[through] = {
1182
- association: throughAssociation,
1183
- dreamClass: throughAssociationDreamClass,
1184
- };
1185
- }
1186
1139
  const nextDreamClass = association.modelCB();
1187
- if (alias === undefined)
1188
- throw new UnexpectedUndefined_js_1.default();
1189
1140
  associationsToDreamClassesMap[alias] = { association, dreamClass: nextDreamClass };
1190
1141
  return nextDreamClass;
1191
1142
  }, this.dreamClass);
@@ -1286,291 +1237,349 @@ class KyselyQueryDriver extends Base_js_1.default {
1286
1237
  return query;
1287
1238
  }
1288
1239
  /**
1289
- * Each association in the chain is pushed onto `throughAssociations`
1290
- * and `applyOneJoin` is recursively called. The trick is that the
1291
- * through associations don't get written into the SQL; they
1292
- * locate the next association we need to build into the SQL,
1293
- * which is only run by the association that started the `through`
1294
- * chain. The final association at the end of the `through` chain _is_
1295
- * written into the SQL as a full association, but the modifications from
1296
- * the `through` association are only added when the recursion returns
1297
- * back to the association that kicked off the through associations.
1240
+ * Recursively traverse the through association chain until we reach a non-through association.
1241
+ * The non-through association is the association that joins the model with the through
1242
+ * association's intended source (the model that it wants to pull into scope). The non-through
1243
+ * association is added to the query, and then the process continues with the source on the other
1244
+ * side of that association, which may itself be a through association, resulting in another
1245
+ * recursive call to joinsBridgeThroughAssociations.
1246
+ *
1247
+ * A chain of through associations on a single Dream model class may be used to join through
1248
+ * several other models. For example, given the following:
1249
+ *
1250
+ * ```
1251
+ * model MyModel
1252
+ * @deco.HasOne('OtherModel')
1253
+ * public otherModel
1254
+ *
1255
+ * @deco.HasOne('A', { through: 'otherModel', source: 'a' })
1256
+ * public myA
1257
+ *
1258
+ * @deco.HasOne('B', { through: 'myA', source: 'b' })
1259
+ * public myB
1260
+ *
1261
+ * model OtherModel
1262
+ * @deco.HasOne('AToOtherModelJoinModel')
1263
+ * public aToOtherModelJoinModel
1264
+ *
1265
+ * @deco.HasOne('A', { through: 'aToOtherModelJoinModel' })
1266
+ * public a
1267
+ *
1268
+ * model AToOtherModelJoinModel
1269
+ * @deco.BelongsTo('A')
1270
+ * public a
1271
+ *
1272
+ * @deco.BelongsTo('OtherModel
1273
+ * public otherModel
1274
+ *
1275
+ * model A
1276
+ * @deco.HasOne('B')
1277
+ * public b
1278
+ * ```
1279
+ *
1280
+ * Then `MyModel.leftJoinPreload('myB')` is processed as follows:
1281
+ * - `applyOneJoin` is called with the `myB` association
1282
+ * - `joinsBridgeThroughAssociations` is called with the `myB` association
1283
+ * - `joinsBridgeThroughAssociations` is called with the `myA` association
1284
+ * - `addAssociationJoinStatementToQuery` is called with the `otherModel` association
1285
+ * - `applyOneJoin` is called with the `a` association from OtherModel
1286
+ * - `joinsBridgeThroughAssociations` is called with the `a` association from OtherModel
1287
+ * // throw ThroughAssociationConditionsIncompatibleWithThroughAssociationSource if
1288
+ * // myA in MyModel defines conditions, distinct, or order
1289
+ * - `addAssociationJoinStatementToQuery` is called with the `aToOtherModelJoinModel` association
1290
+ * - `applyOneJoin` is called with the `a` association from AToOtherModelJoinModel with conditions (if present) on `a` defined on OtherModel
1291
+ * - `addAssociationJoinStatementToQuery` is called with the `myA` association
1292
+ * - `applyOneJoin` is called with the `b` association from A with conditions (if present) from `myB` defined on MyModel
1293
+ * - `addAssociationJoinStatementToQuery` is called with the `myB` association
1298
1294
  */
1299
- joinsBridgeThroughAssociations({ query, dreamClass, association, previousAssociationTableOrAlias, throughAssociations, joinType, baseThroughAssociation, polymorphicCount, }) {
1300
- if (association.type === 'BelongsTo' || !association.through) {
1301
- return {
1295
+ joinsBridgeThroughAssociations({ query, dreamClassTheAssociationIsDefinedOn, throughAssociation,
1296
+ /**
1297
+ * If the join statement includes an explicit alias, e.g.,
1298
+ * `leftJoinPreload('hello as ho', 'world as wd')`
1299
+ * then explicitAlias is set (e.g., to 'ho' the first time that `recursivelyJoin`
1300
+ * calls this method and to 'ho' the second time `recursivelyJoin` is called)
1301
+ * If only implicit aliasing happens (based on association name)
1302
+ * (e.g. `leftJoinPreload('hello', 'world')`), then explicitAlias is null
1303
+ */
1304
+ explicitAlias,
1305
+ /**
1306
+ * previousTableAlias is always set
1307
+ */
1308
+ previousTableAlias, joinAndStatement, joinType, }) {
1309
+ /**
1310
+ * `through` associations always point to other associations on the same model
1311
+ * they are defined on. So when we want to find an association referenced by a
1312
+ * through association, we look on the same model as the through association
1313
+ */
1314
+ const associationReferencedByThroughAssociation = dreamClassTheAssociationIsDefinedOn['getAssociationMetadata'](throughAssociation.through);
1315
+ if (!associationReferencedByThroughAssociation) {
1316
+ // Type protection on association declarations should protect against this ever actually being reached
1317
+ throw new MissingThroughAssociation_js_1.default({
1318
+ dreamClass: dreamClassTheAssociationIsDefinedOn,
1319
+ association: throughAssociation,
1320
+ });
1321
+ }
1322
+ let recursiveResult;
1323
+ const addingNestedThroughAssociation = associationReferencedByThroughAssociation.type !== 'BelongsTo' &&
1324
+ associationReferencedByThroughAssociation.through;
1325
+ if (addingNestedThroughAssociation) {
1326
+ /**
1327
+ * This new association is itself a through association, so we recursively
1328
+ * call joinsBridgeThroughAssociations.
1329
+ */
1330
+ recursiveResult = this.joinsBridgeThroughAssociations({
1302
1331
  query,
1303
- dreamClass,
1304
- association,
1305
- previousAssociationTableOrAlias,
1306
- };
1332
+ /**
1333
+ * `through` associations always point to other associations on the same model
1334
+ * they are defined on. So when this association is a through association,
1335
+ * we can simply pass the dreamClassAssociationDefinedIn we already have
1336
+ */
1337
+ dreamClassTheAssociationIsDefinedOn,
1338
+ throughAssociation: associationReferencedByThroughAssociation,
1339
+ explicitAlias: undefined,
1340
+ previousTableAlias,
1341
+ joinAndStatement: {},
1342
+ joinType,
1343
+ });
1344
+ //
1307
1345
  }
1308
1346
  else {
1309
- throughAssociations.push(association);
1310
- // We have entered joinsBridgeThroughAssociations with the
1311
- // CompositionAssetAudits HasOne User association, which
1312
- // is through compositionAsset
1313
- // We now apply the compositionAsset association (a BelongsTo)
1314
- // to the query
1315
- const { query: queryWithThroughAssociationApplied } = this.applyOneJoin({
1347
+ /**
1348
+ * We have reached the association at the end of the through chain, the final
1349
+ * association that we pass _through_ to reach the model class on which the
1350
+ * _source_ association of the through association is found. That source
1351
+ * association will be added to the query as the next step at the end of this
1352
+ * method.
1353
+ */
1354
+ recursiveResult = this.addAssociationJoinStatementToQuery({
1316
1355
  query,
1317
- dreamClass,
1318
- previousAssociationTableOrAlias,
1319
- currentAssociationTableOrAlias: association.through,
1320
- throughAssociations,
1356
+ dreamClass: dreamClassTheAssociationIsDefinedOn,
1357
+ association: associationReferencedByThroughAssociation,
1358
+ /**
1359
+ * The explicit alias is reserved for the final association, not intermediary join tables
1360
+ */
1361
+ explicitAlias: undefined,
1362
+ previousTableAlias,
1363
+ selfTableAlias: previousTableAlias,
1364
+ /**
1365
+ * The joinAndStatement is reserved for the final association, not intermediary join tables
1366
+ */
1367
+ joinAndStatement: {},
1368
+ previousThroughAssociation: undefined,
1321
1369
  joinType,
1322
- baseThroughAssociation,
1323
- polymorphicCount,
1370
+ dreamClassThroughAssociationWantsToHydrate: undefined,
1324
1371
  });
1325
- // The through association has both a `through` and a `source`. The `source`
1326
- // is the association on the model that has now been joined. In our example,
1327
- // the `source` is the `user` association on the CompositionAsset model
1328
- const { newAssociation, throughAssociation, throughClass } = this.followThroughAssociation(dreamClass, association);
1329
- if (newAssociation.through) {
1330
- // This new association is itself a through association, so we recursively
1331
- // call joinsBridgeThroughAssociations
1332
- return this.joinsBridgeThroughAssociations({
1333
- query: queryWithThroughAssociationApplied,
1334
- dreamClass: throughClass,
1335
- association: newAssociation,
1336
- previousAssociationTableOrAlias: throughAssociation.as,
1337
- throughAssociations,
1338
- joinType,
1339
- baseThroughAssociation,
1340
- polymorphicCount,
1341
- });
1342
- }
1343
- else {
1344
- // This new association is not a through association, so
1345
- // this is the target association we were looking for
1346
- return {
1347
- query: queryWithThroughAssociationApplied,
1348
- dreamClass: association.modelCB(),
1349
- association: newAssociation,
1350
- throughClass,
1351
- previousAssociationTableOrAlias: association.through,
1352
- };
1353
- }
1354
1372
  }
1355
- }
1356
- /**
1357
- * @internal
1358
- *
1359
- * Used to bridge through associations
1360
- */
1361
- followThroughAssociation(dreamClass, association) {
1362
- const throughAssociation = association.through && dreamClass['getAssociationMetadata'](association.through);
1363
- if (!throughAssociation)
1364
- throw new MissingThroughAssociation_js_1.default({
1365
- dreamClass,
1366
- association,
1373
+ const dreamClassWithSourceAssociation = associationReferencedByThroughAssociation.modelCB();
1374
+ if (Array.isArray(dreamClassWithSourceAssociation))
1375
+ throw new ArrayTargetIncompatibleWithThroughAssociation_js_1.default({
1376
+ dreamClass: dreamClassTheAssociationIsDefinedOn,
1377
+ association: throughAssociation,
1367
1378
  });
1368
- const throughClass = throughAssociation.modelCB();
1369
- if (Array.isArray(throughClass))
1370
- throw new ArrayTargetsIncompatibleWithThroughAssociation_js_1.default({
1371
- dreamClass,
1372
- association,
1373
- });
1374
- const newAssociation = getSourceAssociation(throughClass, association.source);
1375
- if (!newAssociation)
1379
+ const sourceAssociation = getSourceAssociation(dreamClassWithSourceAssociation, throughAssociation.source);
1380
+ if (!sourceAssociation)
1376
1381
  throw new MissingThroughAssociationSource_js_1.default({
1377
- dreamClass,
1378
- throughClass,
1379
- association,
1382
+ dreamClass: dreamClassTheAssociationIsDefinedOn,
1383
+ association: throughAssociation,
1384
+ throughClass: dreamClassWithSourceAssociation,
1380
1385
  });
1381
- return { throughAssociation, throughClass, newAssociation };
1386
+ /**
1387
+ * When the source is a polymorphic BelongsTo association, use the target of the through
1388
+ * association to determine which class to target.
1389
+ */
1390
+ const dreamClassThroughAssociationWantsToHydrate = Array.isArray(sourceAssociation.modelCB())
1391
+ ? throughAssociation.modelCB()
1392
+ : undefined;
1393
+ /**
1394
+ * Add the source that `sourceAssociation` points to. Note that since joinsBridgeThroughAssociations
1395
+ * is recursive, it may have added other through associations to the query in between when this
1396
+ * particular method call started and now.
1397
+ */
1398
+ return this.applyOneJoin({
1399
+ query: recursiveResult.query,
1400
+ dreamClass: dreamClassWithSourceAssociation,
1401
+ association: sourceAssociation,
1402
+ // since joinsBridgeThroughAssociations passes undefined to explicitAlias recursively, we know this is only set on the
1403
+ // first call to joinsBridgeThroughAssociations, which corresponds to the outermoset through association and therefore
1404
+ // the last source association to be added to the statement (because joinsBridgeThroughAssociations was called
1405
+ // recursively and therefore may have added other through associations and their sources prior to reaching this point)
1406
+ explicitAlias,
1407
+ previousTableAlias: recursiveResult.association.as,
1408
+ selfTableAlias: (throughAssociation.selfAnd ?? throughAssociation.selfAndNot)
1409
+ ? previousTableAlias
1410
+ : recursiveResult.association.as,
1411
+ // since joinsBridgeThroughAssociations passes {} to joinAndStatement recursively, we know this is only set on the
1412
+ // first call to joinsBridgeThroughAssociations, which corresponds to the outermoset through association and therefore
1413
+ // the last source association to be added to the statement (because joinsBridgeThroughAssociations was called
1414
+ // recursively and therefore may have added other through associations and their sources prior to reaching this point)
1415
+ joinAndStatement,
1416
+ previousThroughAssociation: throughAssociation,
1417
+ joinType,
1418
+ dreamClassThroughAssociationWantsToHydrate,
1419
+ });
1382
1420
  }
1421
+ applyOneJoin({ query, dreamClass, association,
1383
1422
  /**
1384
- * Each association in the chain is pushed onto `throughAssociations`
1385
- * and `applyOneJoin` is recursively called. The trick is that the
1386
- * through associations don't get written into the SQL; they
1387
- * locate the next association we need to build into the SQL,
1388
- * which is only run by the association that started the `through`
1389
- * chain. The final association at the end of the `through` chain _is_
1390
- * written into the SQL as a full association, but the modifications from
1391
- * the `through` association are only added when the recursion returns
1392
- * back to the association that kicked off the through associations.
1423
+ * If the join statement includes an explicit alias, e.g.,
1424
+ * `leftJoinPreload('hello as ho', 'world as wd')`
1425
+ * then explicitAlias is set (e.g., to 'ho' the first time that `recursivelyJoin`
1426
+ * calls this method and to 'ho' the second time `recursivelyJoin` is called)
1427
+ * If only implicit aliasing happens (based on association name)
1428
+ * (e.g. `leftJoinPreload('hello', 'world')`), then explicitAlias is null
1393
1429
  */
1394
- applyOneJoin({ query, dreamClass, previousAssociationTableOrAlias, currentAssociationTableOrAlias, joinAndStatements = {}, throughAssociations = [], joinType, baseThroughAssociation, polymorphicCount, }) {
1395
- const { name, alias } = (0, extractAssociationMetadataFromAssociationName_js_1.default)(currentAssociationTableOrAlias);
1396
- const joinAndStatement = joinAndStatements[currentAssociationTableOrAlias];
1397
- previousAssociationTableOrAlias = (0, extractAssociationMetadataFromAssociationName_js_1.default)(previousAssociationTableOrAlias).alias;
1398
- currentAssociationTableOrAlias = alias;
1399
- let association = dreamClass['getAssociationMetadata'](name);
1400
- if (association?.polymorphic)
1401
- polymorphicCount.count++;
1402
- if (!association) {
1403
- throw new JoinAttemptedOnMissingAssociation_js_1.default({
1404
- dreamClass,
1405
- associationName: currentAssociationTableOrAlias,
1430
+ explicitAlias,
1431
+ /**
1432
+ * previousTableAlias is always set
1433
+ */
1434
+ previousTableAlias, selfTableAlias = previousTableAlias, joinAndStatement = {}, previousThroughAssociation, joinType, dreamClassThroughAssociationWantsToHydrate, }) {
1435
+ if (association.type !== 'BelongsTo' && association.through) {
1436
+ if ((0, throughAssociationHasOptionsBesidesThroughAndSource_js_1.default)(previousThroughAssociation)) {
1437
+ throw new ThroughAssociationConditionsIncompatibleWithThroughAssociationSource_js_1.default({
1438
+ dreamClass,
1439
+ association,
1440
+ });
1441
+ }
1442
+ return this.joinsBridgeThroughAssociations({
1443
+ query,
1444
+ dreamClassTheAssociationIsDefinedOn: dreamClass,
1445
+ throughAssociation: association,
1446
+ explicitAlias,
1447
+ previousTableAlias,
1448
+ joinAndStatement,
1449
+ joinType,
1406
1450
  });
1407
1451
  }
1408
- baseThroughAssociation ??= association.through
1409
- ? association
1410
- : undefined;
1411
- const results = this.joinsBridgeThroughAssociations({
1452
+ return this.addAssociationJoinStatementToQuery({
1412
1453
  query,
1413
1454
  dreamClass,
1414
1455
  association,
1415
- previousAssociationTableOrAlias,
1416
- throughAssociations,
1456
+ explicitAlias,
1457
+ previousTableAlias,
1458
+ selfTableAlias,
1459
+ joinAndStatement,
1460
+ previousThroughAssociation,
1417
1461
  joinType,
1418
- baseThroughAssociation,
1419
- polymorphicCount,
1462
+ dreamClassThroughAssociationWantsToHydrate,
1420
1463
  });
1421
- query = results.query;
1422
- dreamClass = results.dreamClass;
1423
- association = results.association;
1424
- if (association?.polymorphic)
1425
- polymorphicCount.count++;
1426
- const timeToApplyThroughAssociations = throughAssociations.length && throughAssociations[0]?.source === association.as;
1427
- const baseThroughAssociationToApply = association.polymorphic ? baseThroughAssociation : undefined;
1428
- if (baseThroughAssociationToApply && polymorphicCount.count > 1)
1429
- throw new CannotAssociateThroughMultiplePolymorphics_js_1.default({
1430
- dreamClass,
1431
- association,
1432
- innerJoinStatements: this.query['innerJoinStatements'],
1433
- leftJoinStatements: this.query['leftJoinStatements'],
1434
- });
1435
- const originalPreviousAssociationTableOrAlias = previousAssociationTableOrAlias;
1436
- previousAssociationTableOrAlias = results.previousAssociationTableOrAlias;
1437
- const throughClass = results.throughClass;
1438
- if (timeToApplyThroughAssociations) {
1439
- /**
1440
- * Each association in the chain is pushed onto `throughAssociations`
1441
- * and `applyOneJoin` is recursively called. The trick is that the
1442
- * through associations don't get written into the SQL; they
1443
- * locate the next association we need to build into the SQL,
1444
- * which is only run by the association that started the `through`
1445
- * chain (thus the
1446
- * `throughAssociations.length && throughAssociations[0].source === association.as`
1447
- * above). The final association at the end of the `through` chain _is_
1448
- * written into the SQL as a full association, but the modifications from
1449
- * the `through` association are only added when the recursion returns
1450
- * back to the association that kicked off the through associations.
1451
- */
1452
- throughAssociations.forEach((throughAssociation) => {
1453
- if (throughAssociation.type === 'HasMany') {
1454
- if (query?.distinctOn && throughAssociation.distinct) {
1455
- query = query.distinctOn(this.distinctColumnNameForAssociation({
1456
- association: throughAssociation,
1457
- tableNameOrAlias: currentAssociationTableOrAlias,
1458
- foreignKey: throughAssociation.primaryKey(),
1459
- }));
1460
- }
1461
- if (throughAssociation.order) {
1462
- query = this.applyOrderStatementForAssociation({
1463
- query,
1464
- association: throughAssociation,
1465
- tableNameOrAlias: currentAssociationTableOrAlias,
1466
- });
1467
- }
1468
- }
1469
- });
1464
+ }
1465
+ addAssociationJoinStatementToQuery({ query, dreamClass, association,
1466
+ /**
1467
+ * If the join statement includes an explicit alias, e.g.,
1468
+ * `leftJoinPreload('hello as ho', 'world as wd')`
1469
+ * then explicitAlias is set (e.g., to 'ho' the first time that `recursivelyJoin`
1470
+ * calls this method and to 'ho' the second time `recursivelyJoin` is called)
1471
+ * If only implicit aliasing happens (based on association name)
1472
+ * (e.g. `leftJoinPreload('hello', 'world')`), then explicitAlias is null
1473
+ */
1474
+ explicitAlias,
1475
+ /**
1476
+ * previousTableAlias is always set
1477
+ */
1478
+ previousTableAlias, selfTableAlias, joinAndStatement, previousThroughAssociation, joinType, dreamClassThroughAssociationWantsToHydrate, }) {
1479
+ const currentTableAlias = explicitAlias ?? association.as;
1480
+ if (previousThroughAssociation?.type === 'HasMany') {
1481
+ if (query?.distinctOn && previousThroughAssociation.distinct) {
1482
+ query = query.distinctOn(this.distinctColumnNameForAssociation({
1483
+ association: previousThroughAssociation,
1484
+ tableNameOrAlias: currentTableAlias,
1485
+ foreignKey: previousThroughAssociation.primaryKey(),
1486
+ }));
1487
+ }
1488
+ if (previousThroughAssociation?.order) {
1489
+ query = this.applyOrderStatementForAssociation({
1490
+ query,
1491
+ association: previousThroughAssociation,
1492
+ tableNameOrAlias: currentTableAlias,
1493
+ });
1494
+ }
1470
1495
  }
1471
1496
  if (association.type === 'BelongsTo') {
1472
- if (!baseThroughAssociationToApply && Array.isArray(association.modelCB()))
1497
+ if (!dreamClassThroughAssociationWantsToHydrate && Array.isArray(association.modelCB()))
1473
1498
  throw new CannotJoinPolymorphicBelongsToError_js_1.default({
1474
1499
  dreamClass,
1475
1500
  association,
1476
1501
  innerJoinStatements: this.query['innerJoinStatements'],
1477
1502
  leftJoinStatements: this.query['leftJoinStatements'],
1478
1503
  });
1479
- const to = (baseThroughAssociationToApply ?? association).modelCB().table;
1480
- const joinTableExpression = currentAssociationTableOrAlias === to
1481
- ? currentAssociationTableOrAlias
1482
- : `${to} as ${currentAssociationTableOrAlias}`;
1504
+ const to = (dreamClassThroughAssociationWantsToHydrate ?? association.modelCB()).table;
1505
+ const joinTableExpression = (0, snakeify_js_1.default)(currentTableAlias) === to ? currentTableAlias : `${to} as ${currentTableAlias}`;
1483
1506
  query = query[(joinType === 'inner' ? 'innerJoin' : 'leftJoin')](joinTableExpression, (join) => {
1484
- join = join.onRef(this.namespaceColumn(association.foreignKey(), previousAssociationTableOrAlias), '=', this.namespaceColumn(association.primaryKey(undefined, {
1485
- associatedClassOverride: baseThroughAssociationToApply?.modelCB(),
1486
- }), currentAssociationTableOrAlias));
1487
- if (baseThroughAssociationToApply) {
1488
- /**
1489
- * At this point, all `through` associations have been bridged and a
1490
- * real association has been reached that is a polymorphic BelongsTo (it
1491
- * is polymorphic and has an array of target models). Join with that
1492
- * table conditionally based on the polymorphic foreign key type,
1493
- * namespaced to the name of that association (which we know from the
1494
- * target of the final `through` association)
1495
- */
1496
- join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement({
1497
- [association.foreignKeyTypeField()]: baseThroughAssociationToApply.modelCB().sanitizedName,
1498
- }, throughAssociations.at(-1).through)));
1499
- }
1500
- if (timeToApplyThroughAssociations) {
1501
- throughAssociations.forEach((throughAssociation) => {
1502
- join = this.applyAssociationAndStatementsToJoinStatement({
1503
- join,
1504
- association: throughAssociation,
1505
- currentAssociationTableOrAlias,
1506
- previousAssociationTableOrAlias: originalPreviousAssociationTableOrAlias,
1507
- joinAndStatements,
1508
- });
1507
+ join = join.onRef(this.namespaceColumn(association.foreignKey(), previousTableAlias), '=', this.namespaceColumn(association.primaryKey(undefined, {
1508
+ associatedClassOverride: dreamClassThroughAssociationWantsToHydrate,
1509
+ }), currentTableAlias));
1510
+ if (previousThroughAssociation) {
1511
+ if (dreamClassThroughAssociationWantsToHydrate) {
1512
+ join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement({
1513
+ [association.foreignKeyTypeField()]: dreamClassThroughAssociationWantsToHydrate.sanitizedName,
1514
+ }, previousThroughAssociation.through)));
1515
+ }
1516
+ join = this.applyAssociationAndStatementsToJoinStatement({
1517
+ join,
1518
+ association: previousThroughAssociation,
1519
+ currentTableAlias,
1520
+ selfTableAlias,
1521
+ joinAndStatement,
1509
1522
  });
1510
1523
  }
1511
1524
  join = this.conditionallyApplyDefaultScopesDependentOnAssociation({
1512
1525
  join,
1513
- tableNameOrAlias: currentAssociationTableOrAlias,
1526
+ tableNameOrAlias: currentTableAlias,
1514
1527
  association,
1515
- throughAssociatedClassOverride: baseThroughAssociationToApply?.modelCB(),
1528
+ throughAssociatedClassOverride: dreamClassThroughAssociationWantsToHydrate,
1516
1529
  });
1517
- join = this.applyJoinAndStatement(join, joinAndStatement, currentAssociationTableOrAlias);
1530
+ join = this.applyJoinAndStatement(join, joinAndStatement, currentTableAlias);
1518
1531
  return join;
1519
1532
  });
1520
1533
  }
1521
1534
  else {
1522
1535
  const to = association.modelCB().table;
1523
- const joinTableExpression = currentAssociationTableOrAlias === to
1524
- ? currentAssociationTableOrAlias
1525
- : `${to} as ${currentAssociationTableOrAlias}`;
1536
+ const joinTableExpression = (0, snakeify_js_1.default)(currentTableAlias) === to ? currentTableAlias : `${to} as ${currentTableAlias}`;
1526
1537
  query = query[(joinType === 'inner' ? 'innerJoin' : 'leftJoin')](joinTableExpression, (join) => {
1527
- join = join.onRef(this.namespaceColumn(association.primaryKey(), previousAssociationTableOrAlias), '=', this.namespaceColumn(association.foreignKey(), currentAssociationTableOrAlias));
1538
+ join = join.onRef(this.namespaceColumn(association.primaryKey(), previousTableAlias), '=', this.namespaceColumn(association.foreignKey(), currentTableAlias));
1528
1539
  if (association.polymorphic) {
1529
1540
  join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement({
1530
- [association.foreignKeyTypeField()]: throughClass
1531
- ? throughClass['stiBaseClassOrOwnClassName']
1541
+ [association.foreignKeyTypeField()]: dreamClassThroughAssociationWantsToHydrate
1542
+ ? dreamClassThroughAssociationWantsToHydrate['stiBaseClassOrOwnClassName']
1532
1543
  : dreamClass['stiBaseClassOrOwnClassName'],
1533
- }, currentAssociationTableOrAlias)));
1544
+ }, currentTableAlias)));
1534
1545
  }
1535
- if (timeToApplyThroughAssociations) {
1536
- throughAssociations.forEach((throughAssociation) => {
1537
- join = this.applyAssociationAndStatementsToJoinStatement({
1538
- join,
1539
- association: throughAssociation,
1540
- currentAssociationTableOrAlias,
1541
- previousAssociationTableOrAlias: originalPreviousAssociationTableOrAlias,
1542
- joinAndStatements,
1543
- });
1546
+ if (previousThroughAssociation) {
1547
+ join = this.applyAssociationAndStatementsToJoinStatement({
1548
+ join,
1549
+ association: previousThroughAssociation,
1550
+ currentTableAlias,
1551
+ selfTableAlias,
1552
+ joinAndStatement,
1544
1553
  });
1545
1554
  }
1546
1555
  join = this.applyAssociationAndStatementsToJoinStatement({
1547
1556
  join,
1548
1557
  association,
1549
- currentAssociationTableOrAlias,
1550
- previousAssociationTableOrAlias,
1551
- joinAndStatements,
1558
+ currentTableAlias,
1559
+ selfTableAlias,
1560
+ joinAndStatement,
1552
1561
  });
1553
1562
  join = this.conditionallyApplyDefaultScopesDependentOnAssociation({
1554
1563
  join,
1555
- tableNameOrAlias: currentAssociationTableOrAlias,
1564
+ tableNameOrAlias: currentTableAlias,
1556
1565
  association,
1557
- throughAssociatedClassOverride: baseThroughAssociationToApply?.modelCB(),
1566
+ throughAssociatedClassOverride: dreamClassThroughAssociationWantsToHydrate,
1558
1567
  });
1559
- join = this.applyJoinAndStatement(join, joinAndStatement, currentAssociationTableOrAlias);
1568
+ join = this.applyJoinAndStatement(join, joinAndStatement, currentTableAlias);
1560
1569
  return join;
1561
1570
  });
1562
1571
  if (association.type === 'HasMany') {
1563
1572
  if (association.order) {
1564
1573
  query = this.applyOrderStatementForAssociation({
1565
1574
  query,
1566
- tableNameOrAlias: currentAssociationTableOrAlias,
1575
+ tableNameOrAlias: currentTableAlias,
1567
1576
  association,
1568
1577
  });
1569
1578
  }
1570
1579
  if (query.distinctOn && association.distinct) {
1571
1580
  query = query.distinctOn(this.distinctColumnNameForAssociation({
1572
1581
  association,
1573
- tableNameOrAlias: currentAssociationTableOrAlias,
1582
+ tableNameOrAlias: currentTableAlias,
1574
1583
  foreignKey: association.foreignKey(),
1575
1584
  }));
1576
1585
  }
@@ -1579,8 +1588,6 @@ class KyselyQueryDriver extends Base_js_1.default {
1579
1588
  return {
1580
1589
  query,
1581
1590
  association,
1582
- previousAssociationTableOrAlias,
1583
- currentAssociationTableOrAlias,
1584
1591
  };
1585
1592
  }
1586
1593
  applyOrderStatementForAssociation({ query, tableNameOrAlias, association, }) {
@@ -1606,28 +1613,28 @@ class KyselyQueryDriver extends Base_js_1.default {
1606
1613
  return this.namespaceColumn(foreignKey, tableNameOrAlias);
1607
1614
  return this.namespaceColumn(association.distinct, tableNameOrAlias);
1608
1615
  }
1609
- applyAssociationAndStatementsToJoinStatement({ join, currentAssociationTableOrAlias, previousAssociationTableOrAlias, association, joinAndStatements, }) {
1616
+ applyAssociationAndStatementsToJoinStatement({ join, currentTableAlias, selfTableAlias, association, joinAndStatement, }) {
1610
1617
  if (association.and) {
1611
- this.throwUnlessAllRequiredWhereClausesProvided(association, currentAssociationTableOrAlias, joinAndStatements);
1612
- join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(association.and, currentAssociationTableOrAlias), { disallowSimilarityOperator: false }));
1618
+ this.throwUnlessAllRequiredWhereClausesProvided(association, currentTableAlias, joinAndStatement);
1619
+ join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(association.and, currentTableAlias), { disallowSimilarityOperator: false }));
1613
1620
  }
1614
1621
  if (association.andNot) {
1615
- join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(association.andNot, currentAssociationTableOrAlias), { negate: true }));
1622
+ join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(association.andNot, currentTableAlias), { negate: true }));
1616
1623
  }
1617
1624
  if (association.andAny) {
1618
- join = join.on((eb) => eb.or(association.andAny.map(whereAnyStatement => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(whereAnyStatement, currentAssociationTableOrAlias), { disallowSimilarityOperator: false }))));
1625
+ join = join.on((eb) => eb.or(association.andAny.map(whereAnyStatement => this.whereStatementToExpressionWrapper(eb, this.aliasWhereStatement(whereAnyStatement, currentTableAlias), { disallowSimilarityOperator: false }))));
1619
1626
  }
1620
1627
  if (association.selfAnd) {
1621
1628
  join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.rawifiedSelfOnClause({
1622
1629
  associationAlias: association.as,
1623
- selfAlias: previousAssociationTableOrAlias,
1630
+ selfAlias: selfTableAlias,
1624
1631
  selfAndClause: association.selfAnd,
1625
1632
  })));
1626
1633
  }
1627
1634
  if (association.selfAndNot) {
1628
1635
  join = join.on((eb) => this.whereStatementToExpressionWrapper(eb, this.rawifiedSelfOnClause({
1629
1636
  associationAlias: association.as,
1630
- selfAlias: previousAssociationTableOrAlias,
1637
+ selfAlias: selfTableAlias,
1631
1638
  selfAndClause: association.selfAndNot,
1632
1639
  }), { negate: true }));
1633
1640
  }
@@ -1640,7 +1647,7 @@ class KyselyQueryDriver extends Base_js_1.default {
1640
1647
  agg.push(column);
1641
1648
  return agg;
1642
1649
  }, []);
1643
- const missingRequiredWhereStatements = columnsRequiringAndStatements.filter(column => joinAndStatements[namespace]?.and?.[column] === undefined);
1650
+ const missingRequiredWhereStatements = columnsRequiringAndStatements.filter(column => joinAndStatements?.and?.[column] === undefined);
1644
1651
  if (missingRequiredWhereStatements.length)
1645
1652
  throw new MissingRequiredAssociationAndClause_js_1.default(association, missingRequiredWhereStatements[0]);
1646
1653
  }
@@ -1767,7 +1774,8 @@ class KyselyQueryDriver extends Base_js_1.default {
1767
1774
  const dream = dreams.find(dream => dream['getAssociationMetadata'](associationName));
1768
1775
  if (!dream)
1769
1776
  return;
1770
- const { name, alias } = (0, extractAssociationMetadataFromAssociationName_js_1.default)(associationName);
1777
+ const { name, alias: _alias } = (0, associationStringToNameAndAlias_js_1.default)(associationName);
1778
+ const alias = _alias || name;
1771
1779
  const association = dream['getAssociationMetadata'](name);
1772
1780
  if (association === undefined)
1773
1781
  throw new UnexpectedUndefined_js_1.default();
@@ -1776,25 +1784,7 @@ class KyselyQueryDriver extends Base_js_1.default {
1776
1784
  if ((association.polymorphic && association.type === 'BelongsTo') || Array.isArray(dreamClassToHydrate))
1777
1785
  return this.preloadPolymorphicBelongsTo(association, dreams);
1778
1786
  const dreamClassToHydrateColumns = [...dreamClassToHydrate.columns()];
1779
- const throughColumnsToHydrate = [];
1780
1787
  const columnsToPluck = dreamClassToHydrateColumns.map(column => this.namespaceColumn(column.toString(), alias));
1781
- const asHasAssociation = association;
1782
- if (asHasAssociation.through && asHasAssociation.preloadThroughColumns) {
1783
- if ((0, isObject_js_1.default)(asHasAssociation.preloadThroughColumns)) {
1784
- const preloadMap = asHasAssociation.preloadThroughColumns;
1785
- Object.keys(preloadMap).forEach(preloadThroughColumn => {
1786
- throughColumnsToHydrate.push(preloadMap[preloadThroughColumn]);
1787
- columnsToPluck.push(this.namespaceColumn(preloadThroughColumn, asHasAssociation.through));
1788
- });
1789
- }
1790
- else {
1791
- const preloadArray = asHasAssociation.preloadThroughColumns;
1792
- preloadArray.forEach(preloadThroughColumn => {
1793
- throughColumnsToHydrate.push(preloadThroughColumn);
1794
- columnsToPluck.push(this.namespaceColumn(preloadThroughColumn, asHasAssociation.through));
1795
- });
1796
- }
1797
- }
1798
1788
  columnsToPluck.push(this.namespaceColumn(dreamClass.primaryKey, dreamClass.table));
1799
1789
  const baseClass = dreamClass['stiBaseClassOrOwnClass']['getAssociationMetadata'](associationName)
1800
1790
  ? dreamClass['stiBaseClassOrOwnClass']
@@ -1817,8 +1807,6 @@ class KyselyQueryDriver extends Base_js_1.default {
1817
1807
  const attributes = {};
1818
1808
  dreamClassToHydrateColumns.forEach((columnName, index) => (attributes[(0, protectAgainstPollutingAssignment_js_1.default)(columnName)] = pluckedData[index]));
1819
1809
  const hydratedDream = this.dbResultToDreamInstance(attributes, dreamClassToHydrate);
1820
- throughColumnsToHydrate.forEach((throughAssociationColumn, index) => (hydratedDream.preloadedThroughColumns[throughAssociationColumn] =
1821
- pluckedData[dreamClassToHydrateColumns.length + index]));
1822
1810
  return {
1823
1811
  dream: hydratedDream,
1824
1812
  pointsToPrimaryKey: pluckedData.at(-1),
@@ -1837,3 +1825,18 @@ function getSourceAssociation(dream, sourceName) {
1837
1825
  return (dream['getAssociationMetadata'](sourceName) ||
1838
1826
  dream['getAssociationMetadata'](pluralize_esm_1.default.singular(sourceName)));
1839
1827
  }
1828
+ const associationStringToAssociationAndMaybeAlias = function ({ dreamClass, associationString, }) {
1829
+ const { name, alias: _alias } = (0, associationStringToNameAndAlias_js_1.default)(associationString);
1830
+ const alias = _alias || name;
1831
+ const association = dreamClass['getAssociationMetadata'](name);
1832
+ if (!association) {
1833
+ throw new JoinAttemptedOnMissingAssociation_js_1.default({
1834
+ dreamClass,
1835
+ associationName: associationString,
1836
+ });
1837
+ }
1838
+ return {
1839
+ association,
1840
+ alias,
1841
+ };
1842
+ };