@rvoh/dream 1.0.3 → 1.0.5

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 (214) hide show
  1. package/CHANGELOG.md +16 -4
  2. package/dist/cjs/src/bin/helpers/sync.js +3 -1
  3. package/dist/cjs/src/cli/CliFileWriter.js +39 -0
  4. package/dist/cjs/src/cli/logger/DreamCliLogger.js +5 -0
  5. package/dist/cjs/src/decorators/field/association/shared.js +2 -2
  6. package/dist/cjs/src/dream/QueryDriver/Kysely.js +84 -34
  7. package/dist/cjs/src/errors/associations/ArrayTargetsIncompatibleWithThroughAssociation.js +19 -0
  8. package/dist/cjs/src/errors/associations/CannotAssociateThroughMultiplePolymorphics.js +27 -0
  9. package/dist/cjs/src/errors/associations/MissingThroughAssociationSource.js +1 -1
  10. package/dist/cjs/src/helpers/cli/SchemaBuilder.js +2 -1
  11. package/dist/cjs/src/index.js +4 -2
  12. package/dist/esm/src/bin/helpers/sync.js +3 -1
  13. package/dist/esm/src/cli/CliFileWriter.js +35 -0
  14. package/dist/esm/src/cli/logger/DreamCliLogger.js +5 -0
  15. package/dist/esm/src/decorators/field/association/shared.js +2 -2
  16. package/dist/esm/src/dream/QueryDriver/Kysely.js +84 -34
  17. package/dist/esm/src/errors/associations/ArrayTargetsIncompatibleWithThroughAssociation.js +16 -0
  18. package/dist/esm/src/errors/associations/CannotAssociateThroughMultiplePolymorphics.js +24 -0
  19. package/dist/esm/src/errors/associations/MissingThroughAssociationSource.js +1 -1
  20. package/dist/esm/src/helpers/cli/SchemaBuilder.js +2 -1
  21. package/dist/esm/src/index.js +1 -0
  22. package/dist/types/src/cli/CliFileWriter.d.ts +9 -0
  23. package/dist/types/src/cli/logger/DreamCliLogger.d.ts +1 -0
  24. package/dist/types/src/decorators/Decorators.d.ts +6 -6
  25. package/dist/types/src/decorators/field/association/HasMany.d.ts +3 -3
  26. package/dist/types/src/decorators/field/association/HasOne.d.ts +3 -3
  27. package/dist/types/src/errors/associations/ArrayTargetsIncompatibleWithThroughAssociation.d.ts +12 -0
  28. package/dist/types/src/errors/associations/CannotAssociateThroughMultiplePolymorphics.d.ts +17 -0
  29. package/dist/types/src/index.d.ts +1 -0
  30. package/dist/types/src/types/associations/belongsTo.d.ts +3 -1
  31. package/dist/types/src/types/associations/belongsTo.ts +4 -1
  32. package/dist/types/src/types/associations/hasMany.d.ts +3 -3
  33. package/dist/types/src/types/associations/hasMany.ts +7 -3
  34. package/dist/types/src/types/associations/hasOne.d.ts +3 -3
  35. package/dist/types/src/types/associations/hasOne.ts +6 -3
  36. package/dist/types/src/types/associations/shared.d.ts +6 -6
  37. package/dist/types/src/types/associations/shared.ts +24 -6
  38. package/dist/types/src/types/dream.d.ts +1 -1
  39. package/dist/types/src/types/dream.ts +1 -1
  40. package/docs/assets/navigation.js +1 -1
  41. package/docs/assets/search.js +1 -1
  42. package/docs/classes/Benchmark.html +2 -2
  43. package/docs/classes/CalendarDate.html +2 -2
  44. package/docs/classes/CliFileWriter.html +8 -0
  45. package/docs/classes/CreateOrFindByFailedToCreateAndFind.html +3 -3
  46. package/docs/classes/Decorators.html +19 -19
  47. package/docs/classes/Dream.html +137 -137
  48. package/docs/classes/DreamApp.html +4 -4
  49. package/docs/classes/DreamBin.html +2 -2
  50. package/docs/classes/DreamCLI.html +4 -4
  51. package/docs/classes/DreamImporter.html +2 -2
  52. package/docs/classes/DreamLogos.html +2 -2
  53. package/docs/classes/DreamMigrationHelpers.html +7 -7
  54. package/docs/classes/DreamSerializerBuilder.html +8 -8
  55. package/docs/classes/DreamTransaction.html +2 -2
  56. package/docs/classes/Encrypt.html +2 -2
  57. package/docs/classes/Env.html +2 -2
  58. package/docs/classes/GlobalNameNotSet.html +3 -3
  59. package/docs/classes/NonLoadedAssociation.html +3 -3
  60. package/docs/classes/ObjectSerializerBuilder.html +8 -8
  61. package/docs/classes/Query.html +60 -60
  62. package/docs/classes/Range.html +2 -2
  63. package/docs/classes/RecordNotFound.html +3 -3
  64. package/docs/classes/ValidationError.html +3 -3
  65. package/docs/functions/DreamSerializer.html +1 -1
  66. package/docs/functions/ObjectSerializer.html +1 -1
  67. package/docs/functions/ReplicaSafe.html +1 -1
  68. package/docs/functions/STI.html +1 -1
  69. package/docs/functions/SoftDelete.html +1 -1
  70. package/docs/functions/camelize.html +1 -1
  71. package/docs/functions/capitalize.html +1 -1
  72. package/docs/functions/cloneDeepSafe.html +1 -1
  73. package/docs/functions/closeAllDbConnections.html +1 -1
  74. package/docs/functions/compact.html +1 -1
  75. package/docs/functions/dreamDbConnections.html +1 -1
  76. package/docs/functions/dreamPath.html +1 -1
  77. package/docs/functions/expandStiClasses.html +1 -1
  78. package/docs/functions/generateDream.html +1 -1
  79. package/docs/functions/globalClassNameFromFullyQualifiedModelName.html +1 -1
  80. package/docs/functions/groupBy.html +1 -1
  81. package/docs/functions/hyphenize.html +1 -1
  82. package/docs/functions/inferSerializerFromDreamOrViewModel.html +1 -1
  83. package/docs/functions/inferSerializersFromDreamClassOrViewModelClass.html +1 -1
  84. package/docs/functions/intersection.html +1 -1
  85. package/docs/functions/isDreamSerializer.html +1 -1
  86. package/docs/functions/isEmpty.html +1 -1
  87. package/docs/functions/loadRepl.html +1 -1
  88. package/docs/functions/lookupClassByGlobalName.html +1 -1
  89. package/docs/functions/normalizeUnicode.html +1 -1
  90. package/docs/functions/pascalize.html +1 -1
  91. package/docs/functions/pgErrorType.html +1 -1
  92. package/docs/functions/range-1.html +1 -1
  93. package/docs/functions/relativeDreamPath.html +1 -1
  94. package/docs/functions/round.html +1 -1
  95. package/docs/functions/serializerNameFromFullyQualifiedModelName.html +1 -1
  96. package/docs/functions/sharedPathPrefix.html +1 -1
  97. package/docs/functions/snakeify.html +1 -1
  98. package/docs/functions/sort.html +1 -1
  99. package/docs/functions/sortBy.html +1 -1
  100. package/docs/functions/sortObjectByKey.html +1 -1
  101. package/docs/functions/sortObjectByValue.html +1 -1
  102. package/docs/functions/standardizeFullyQualifiedModelName.html +1 -1
  103. package/docs/functions/uncapitalize.html +1 -1
  104. package/docs/functions/uniq.html +1 -1
  105. package/docs/functions/untypedDb.html +1 -1
  106. package/docs/functions/validateColumn.html +1 -1
  107. package/docs/functions/validateTable.html +1 -1
  108. package/docs/interfaces/BelongsToStatement.html +2 -2
  109. package/docs/interfaces/DecoratorContext.html +2 -2
  110. package/docs/interfaces/DreamAppInitOptions.html +2 -2
  111. package/docs/interfaces/DreamAppOpts.html +2 -2
  112. package/docs/interfaces/EncryptOptions.html +2 -2
  113. package/docs/interfaces/InternalAnyTypedSerializerRendersMany.html +2 -2
  114. package/docs/interfaces/InternalAnyTypedSerializerRendersOne.html +2 -2
  115. package/docs/interfaces/OpenapiDescription.html +2 -2
  116. package/docs/interfaces/OpenapiSchemaProperties.html +1 -1
  117. package/docs/interfaces/OpenapiSchemaPropertiesShorthand.html +1 -1
  118. package/docs/interfaces/OpenapiTypeFieldObject.html +1 -1
  119. package/docs/interfaces/SerializerRendererOpts.html +2 -2
  120. package/docs/modules.html +1 -0
  121. package/docs/types/Camelized.html +1 -1
  122. package/docs/types/CommonOpenapiSchemaObjectFields.html +1 -1
  123. package/docs/types/DateTime.html +1 -1
  124. package/docs/types/DbConnectionType.html +1 -1
  125. package/docs/types/DbTypes.html +1 -1
  126. package/docs/types/DreamAppAllowedPackageManagersEnum.html +1 -1
  127. package/docs/types/DreamAssociationMetadata.html +1 -1
  128. package/docs/types/DreamAttributes.html +1 -1
  129. package/docs/types/DreamClassAssociationAndStatement.html +1 -1
  130. package/docs/types/DreamClassColumn.html +1 -1
  131. package/docs/types/DreamColumn.html +1 -1
  132. package/docs/types/DreamColumnNames.html +1 -1
  133. package/docs/types/DreamLogLevel.html +1 -1
  134. package/docs/types/DreamLogger.html +1 -1
  135. package/docs/types/DreamModelSerializerType.html +1 -1
  136. package/docs/types/DreamOrViewModelClassSerializerKey.html +1 -1
  137. package/docs/types/DreamOrViewModelSerializerKey.html +1 -1
  138. package/docs/types/DreamParamSafeAttributes.html +1 -1
  139. package/docs/types/DreamParamSafeColumnNames.html +1 -1
  140. package/docs/types/DreamSerializable.html +1 -1
  141. package/docs/types/DreamSerializableArray.html +1 -1
  142. package/docs/types/DreamSerializerKey.html +1 -1
  143. package/docs/types/DreamSerializers.html +1 -1
  144. package/docs/types/DreamTableSchema.html +1 -1
  145. package/docs/types/DreamVirtualColumns.html +1 -1
  146. package/docs/types/EncryptAlgorithm.html +1 -1
  147. package/docs/types/HasManyStatement.html +1 -1
  148. package/docs/types/HasOneStatement.html +1 -1
  149. package/docs/types/Hyphenized.html +1 -1
  150. package/docs/types/IdType.html +1 -1
  151. package/docs/types/OpenapiAllTypes.html +1 -1
  152. package/docs/types/OpenapiFormats.html +1 -1
  153. package/docs/types/OpenapiNumberFormats.html +1 -1
  154. package/docs/types/OpenapiPrimitiveBaseTypes.html +1 -1
  155. package/docs/types/OpenapiPrimitiveTypes.html +1 -1
  156. package/docs/types/OpenapiSchemaArray.html +1 -1
  157. package/docs/types/OpenapiSchemaArrayShorthand.html +1 -1
  158. package/docs/types/OpenapiSchemaBase.html +1 -1
  159. package/docs/types/OpenapiSchemaBody.html +1 -1
  160. package/docs/types/OpenapiSchemaBodyShorthand.html +1 -1
  161. package/docs/types/OpenapiSchemaCommonFields.html +1 -1
  162. package/docs/types/OpenapiSchemaExpressionAllOf.html +1 -1
  163. package/docs/types/OpenapiSchemaExpressionAnyOf.html +1 -1
  164. package/docs/types/OpenapiSchemaExpressionOneOf.html +1 -1
  165. package/docs/types/OpenapiSchemaExpressionRef.html +1 -1
  166. package/docs/types/OpenapiSchemaExpressionRefSchemaShorthand.html +1 -1
  167. package/docs/types/OpenapiSchemaInteger.html +1 -1
  168. package/docs/types/OpenapiSchemaNull.html +1 -1
  169. package/docs/types/OpenapiSchemaNumber.html +1 -1
  170. package/docs/types/OpenapiSchemaObject.html +1 -1
  171. package/docs/types/OpenapiSchemaObjectAllOf.html +1 -1
  172. package/docs/types/OpenapiSchemaObjectAllOfShorthand.html +1 -1
  173. package/docs/types/OpenapiSchemaObjectAnyOf.html +1 -1
  174. package/docs/types/OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  175. package/docs/types/OpenapiSchemaObjectBase.html +1 -1
  176. package/docs/types/OpenapiSchemaObjectBaseShorthand.html +1 -1
  177. package/docs/types/OpenapiSchemaObjectOneOf.html +1 -1
  178. package/docs/types/OpenapiSchemaObjectOneOfShorthand.html +1 -1
  179. package/docs/types/OpenapiSchemaObjectShorthand.html +1 -1
  180. package/docs/types/OpenapiSchemaPrimitiveGeneric.html +1 -1
  181. package/docs/types/OpenapiSchemaShorthandExpressionAllOf.html +1 -1
  182. package/docs/types/OpenapiSchemaShorthandExpressionAnyOf.html +1 -1
  183. package/docs/types/OpenapiSchemaShorthandExpressionOneOf.html +1 -1
  184. package/docs/types/OpenapiSchemaShorthandExpressionSerializableRef.html +1 -1
  185. package/docs/types/OpenapiSchemaShorthandExpressionSerializerRef.html +1 -1
  186. package/docs/types/OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  187. package/docs/types/OpenapiSchemaString.html +1 -1
  188. package/docs/types/OpenapiShorthandAllTypes.html +1 -1
  189. package/docs/types/OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  190. package/docs/types/OpenapiShorthandPrimitiveTypes.html +1 -1
  191. package/docs/types/OpenapiTypeField.html +1 -1
  192. package/docs/types/Pascalized.html +1 -1
  193. package/docs/types/PrimaryKeyType.html +1 -1
  194. package/docs/types/RoundingPrecision.html +1 -1
  195. package/docs/types/SerializerCasing.html +1 -1
  196. package/docs/types/SimpleObjectSerializerType.html +1 -1
  197. package/docs/types/Snakeified.html +1 -1
  198. package/docs/types/Timestamp.html +1 -1
  199. package/docs/types/UpdateableAssociationProperties.html +1 -1
  200. package/docs/types/UpdateableProperties.html +1 -1
  201. package/docs/types/ValidationType.html +1 -1
  202. package/docs/types/ViewModel.html +1 -1
  203. package/docs/types/ViewModelClass.html +1 -1
  204. package/docs/types/WhereStatementForDream.html +1 -1
  205. package/docs/types/WhereStatementForDreamClass.html +1 -1
  206. package/docs/variables/DateTime-1.html +1 -1
  207. package/docs/variables/DreamAppAllowedPackageManagersEnumValues.html +1 -1
  208. package/docs/variables/DreamConst.html +1 -1
  209. package/docs/variables/TRIGRAM_OPERATORS.html +1 -1
  210. package/docs/variables/openapiPrimitiveTypes-1.html +1 -1
  211. package/docs/variables/openapiShorthandPrimitiveTypes-1.html +1 -1
  212. package/docs/variables/ops.html +1 -1
  213. package/docs/variables/primaryKeyTypes.html +1 -1
  214. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,11 +1,23 @@
1
- # 1.0.1
1
+ ## 1.0.5
2
2
 
3
- - [bug] fix preloading STI model via polymporhic association (polymorphic type was being altered to the STI child rather than left as the STI base)
3
+ - Support HasOne/Many through polymorphic BelongsTo (trick is that it uses the associated model from the HasOne/Many to limit the BelongsTo to a single associated class / table)
4
4
 
5
- ## 1.0.2
5
+ - Fix HasOne/Many through source type
6
6
 
7
- - stop computing foreign keys in schema builder when building schema for through associations
7
+ - sync process is now fail-safe, leveraging a utility which caches old copies of files before writing to them. If an exception is thrown at any point during the process, dream will revert all files written using the new `CliFileWriter` class
8
+
9
+ ## 1.0.4
10
+
11
+ - properly exclude type from `DreamParamSafeColumnNames`
8
12
 
9
13
  ## 1.0.3
10
14
 
11
15
  - exclude type from `DreamParamSafeColumnNames`
16
+
17
+ ## 1.0.2
18
+
19
+ - stop computing foreign keys in schema builder when building schema for through associations
20
+
21
+ ## 1.0.1
22
+
23
+ - [bug] fix preloading STI model via polymporhic association (polymorphic type was being altered to the STI child rather than left as the STI base)
@@ -12,11 +12,13 @@ const EnvInternal_js_1 = require("../../helpers/EnvInternal.js");
12
12
  const dreamPath_js_1 = require("../../helpers/path/dreamPath.js");
13
13
  const snakeify_js_1 = require("../../helpers/snakeify.js");
14
14
  const sspawn_js_1 = require("../../helpers/sspawn.js");
15
+ const CliFileWriter_js_1 = require("../../cli/CliFileWriter.js");
15
16
  async function writeSyncFile() {
16
17
  const dreamApp = index_js_2.default.getOrFail();
17
18
  const dbConf = dreamApp.dbConnectionConfig('primary');
18
19
  const dbSyncFilePath = path.join((0, dreamPath_js_1.default)('types'), 'db.ts');
19
20
  const absoluteDbSyncPath = path.join(dreamApp.projectRoot, dbSyncFilePath);
21
+ await CliFileWriter_js_1.CliFileWriter.cache(absoluteDbSyncPath);
20
22
  await (0, sspawn_js_1.default)(`kysely-codegen --dialect=postgres --url=postgres://${dbConf.user}:${dbConf.password}@${dbConf.host}:${dbConf.port}/${dbConf.name} --out-file=${absoluteDbSyncPath}`, {
21
23
  onStdout: message => {
22
24
  index_js_1.default.logger.logContinueProgress((0, colorize_js_1.default)(`[db]`, { color: 'cyan' }) + ' ' + message, {
@@ -28,7 +30,7 @@ async function writeSyncFile() {
28
30
  // from the dist folder, whereas dirname here is pointing to true src folder.
29
31
  const file = (await fs.readFile(absoluteDbSyncPath)).toString();
30
32
  const enhancedSchema = enhanceSchema(file);
31
- await fs.writeFile(absoluteDbSyncPath, enhancedSchema);
33
+ await CliFileWriter_js_1.CliFileWriter.write(absoluteDbSyncPath, enhancedSchema);
32
34
  }
33
35
  // begin: schema helpers
34
36
  function enhanceSchema(file) {
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CliFileWriter = void 0;
4
+ const fs = require("node:fs/promises");
5
+ const index_js_1 = require("./index.js");
6
+ class CliFileWriter {
7
+ static async write(filepath, contents) {
8
+ await cliFileWriter.write(filepath, contents);
9
+ }
10
+ static async revert() {
11
+ await cliFileWriter.revert();
12
+ }
13
+ static async cache(filepath) {
14
+ await cliFileWriter.cache(filepath);
15
+ }
16
+ fileCache = {};
17
+ async write(filepath, contents) {
18
+ // if we have manually backed up this file, or else this file
19
+ // has been written to twice in one CLI session, we want
20
+ // to preserve the original backup, so we do not attempt
21
+ // to cache a second time
22
+ if (!this.fileCache[filepath])
23
+ await this.cache(filepath);
24
+ await fs.writeFile(filepath, contents);
25
+ }
26
+ async revert() {
27
+ const filepaths = Object.keys(this.fileCache);
28
+ for (const filepath of filepaths) {
29
+ index_js_1.default.logger.logContinueProgress(`reverting ${filepath}`);
30
+ await fs.writeFile(filepath, this.fileCache[filepath]);
31
+ }
32
+ }
33
+ async cache(filepath) {
34
+ const originalContents = (await fs.readFile(filepath)).toString();
35
+ this.fileCache[filepath] = originalContents;
36
+ }
37
+ }
38
+ exports.CliFileWriter = CliFileWriter;
39
+ const cliFileWriter = new CliFileWriter();
@@ -10,6 +10,11 @@ class DreamCliLogger {
10
10
  });
11
11
  loggable.render();
12
12
  }
13
+ async logProgress(text, cb, { logPrefix = '✺ ┌', logPrefixColor, logPrefixBgColor } = {}) {
14
+ this.log(text, { logPrefix, logPrefixColor, logPrefixBgColor });
15
+ await cb();
16
+ this.logEndProgress();
17
+ }
13
18
  logStartProgress(text, { logPrefix = '✺ ┌', logPrefixColor, logPrefixBgColor } = {}) {
14
19
  this.log(text, { logPrefix, logPrefixColor, logPrefixBgColor });
15
20
  }
@@ -98,12 +98,12 @@ function applyGetterAndSetter(target, partialAssociation, { foreignKeyBase, isBe
98
98
  function associationPrimaryKeyAccessors(partialAssociation, dreamClass) {
99
99
  return {
100
100
  ...partialAssociation,
101
- primaryKey(associationInstance) {
101
+ primaryKey(associationInstance, { associatedClassOverride } = {}) {
102
102
  if (this.primaryKeyOverride)
103
103
  return this.primaryKeyOverride;
104
104
  if (associationInstance)
105
105
  return associationInstance.primaryKey;
106
- const associationClass = this.modelCB();
106
+ const associationClass = associatedClassOverride ?? this.modelCB();
107
107
  if (Array.isArray(associationClass)) {
108
108
  throw new Error(`
109
109
  Cannot lookup primaryKey on polymorphic association:
@@ -3,12 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const kysely_1 = require("kysely");
4
4
  const pluralize_esm_1 = require("pluralize-esm");
5
5
  const sync_js_1 = require("../../bin/helpers/sync.js");
6
+ const CliFileWriter_js_1 = require("../../cli/CliFileWriter.js");
6
7
  const index_js_1 = require("../../cli/index.js");
7
8
  const index_js_2 = require("../../db/index.js");
8
9
  const associationToGetterSetterProp_js_1 = require("../../decorators/field/association/associationToGetterSetterProp.js");
9
10
  const PackageManager_js_1 = require("../../dream-app/helpers/PackageManager.js");
10
11
  const index_js_3 = require("../../dream-app/index.js");
11
- const CannotAssociateThroughPolymorphic_js_1 = require("../../errors/associations/CannotAssociateThroughPolymorphic.js");
12
+ const ArrayTargetsIncompatibleWithThroughAssociation_js_1 = require("../../errors/associations/ArrayTargetsIncompatibleWithThroughAssociation.js");
13
+ const CannotAssociateThroughMultiplePolymorphics_js_1 = require("../../errors/associations/CannotAssociateThroughMultiplePolymorphics.js");
12
14
  const CannotJoinPolymorphicBelongsToError_js_1 = require("../../errors/associations/CannotJoinPolymorphicBelongsToError.js");
13
15
  const JoinAttemptedOnMissingAssociation_js_1 = require("../../errors/associations/JoinAttemptedOnMissingAssociation.js");
14
16
  const MissingRequiredAssociationAndClause_js_1 = require("../../errors/associations/MissingRequiredAssociationAndClause.js");
@@ -94,33 +96,41 @@ class KyselyQueryDriver extends Base_js_1.default {
94
96
  await (0, generateMigration_js_1.default)({ migrationName, columnsWithTypes });
95
97
  }
96
98
  static async sync(onSync, options = {}) {
97
- if (!options?.schemaOnly) {
98
- index_js_1.default.logger.logStartProgress('writing db schema...');
99
- await (0, sync_js_1.default)();
100
- index_js_1.default.logger.logEndProgress();
101
- }
102
- index_js_1.default.logger.logStartProgress('building dream schema...');
103
- const schemaBuilder = new SchemaBuilder_js_1.default();
104
- await schemaBuilder.build();
105
- index_js_1.default.logger.logEndProgress();
106
- if (schemaBuilder.hasForeignKeyError && !options?.schemaOnly) {
107
- index_js_1.default.logger.logStartProgress('triggering resync to correct for foreign key errors...');
108
- // TODO: make this customizable to enable dream apps to separate
109
- const cliCmd = EnvInternal_js_1.default.boolean('DREAM_CORE_DEVELOPMENT') ? 'dream' : 'psy';
110
- await index_js_1.default.spawn(PackageManager_js_1.default.runCmd(`${cliCmd} sync --schema-only`), {
111
- onStdout: str => {
112
- index_js_1.default.logger.logContinueProgress(`${str}`, {
113
- logPrefix: ' ├ [resync]',
114
- logPrefixColor: 'blue',
115
- });
116
- },
99
+ try {
100
+ if (!options?.schemaOnly) {
101
+ await index_js_1.default.logger.logProgress('writing db schema...', async () => {
102
+ await (0, sync_js_1.default)();
103
+ });
104
+ }
105
+ const schemaBuilder = new SchemaBuilder_js_1.default();
106
+ await index_js_1.default.logger.logProgress('building dream schema...', async () => {
107
+ await schemaBuilder.build();
117
108
  });
118
- index_js_1.default.logger.logEndProgress();
109
+ if (schemaBuilder.hasForeignKeyError && !options?.schemaOnly) {
110
+ await index_js_1.default.logger.logProgress('triggering resync to correct for foreign key errors...', async () => {
111
+ // TODO: make this customizable to enable dream apps to separate
112
+ const cliCmd = EnvInternal_js_1.default.boolean('DREAM_CORE_DEVELOPMENT') ? 'dream' : 'psy';
113
+ await index_js_1.default.spawn(PackageManager_js_1.default.runCmd(`${cliCmd} sync --schema-only`), {
114
+ onStdout: str => {
115
+ index_js_1.default.logger.logContinueProgress(`${str}`, {
116
+ logPrefix: ' ├ [resync]',
117
+ logPrefixColor: 'blue',
118
+ });
119
+ },
120
+ });
121
+ });
122
+ }
123
+ if (!options?.schemaOnly) {
124
+ // intentionally leaving logs off here, since it allows other
125
+ // onSync handlers to determine their own independent logging approach
126
+ await onSync();
127
+ }
119
128
  }
120
- if (!options?.schemaOnly) {
121
- // intentionally leaving logs off here, since it allows other
122
- // onSync handlers to determine their own independent logging approach
123
- await onSync();
129
+ catch (error) {
130
+ console.error(error);
131
+ await index_js_1.default.logger.logProgress('sync failed, reverting file contents...', async () => {
132
+ await CliFileWriter_js_1.CliFileWriter.revert();
133
+ });
124
134
  }
125
135
  }
126
136
  /**
@@ -1089,6 +1099,8 @@ class KyselyQueryDriver extends Base_js_1.default {
1089
1099
  currentAssociationTableOrAlias,
1090
1100
  joinAndStatements,
1091
1101
  joinType,
1102
+ baseThroughAssociation: undefined,
1103
+ polymorphicCount: { count: 0 },
1092
1104
  });
1093
1105
  query = results.query;
1094
1106
  const association = results.association;
@@ -1284,7 +1296,7 @@ class KyselyQueryDriver extends Base_js_1.default {
1284
1296
  * the `through` association are only added when the recursion returns
1285
1297
  * back to the association that kicked off the through associations.
1286
1298
  */
1287
- joinsBridgeThroughAssociations({ query, dreamClass, association, previousAssociationTableOrAlias, throughAssociations, joinType, }) {
1299
+ joinsBridgeThroughAssociations({ query, dreamClass, association, previousAssociationTableOrAlias, throughAssociations, joinType, baseThroughAssociation, polymorphicCount, }) {
1288
1300
  if (association.type === 'BelongsTo' || !association.through) {
1289
1301
  return {
1290
1302
  query,
@@ -1307,6 +1319,8 @@ class KyselyQueryDriver extends Base_js_1.default {
1307
1319
  currentAssociationTableOrAlias: association.through,
1308
1320
  throughAssociations,
1309
1321
  joinType,
1322
+ baseThroughAssociation,
1323
+ polymorphicCount,
1310
1324
  });
1311
1325
  // The through association has both a `through` and a `source`. The `source`
1312
1326
  // is the association on the model that has now been joined. In our example,
@@ -1322,6 +1336,8 @@ class KyselyQueryDriver extends Base_js_1.default {
1322
1336
  previousAssociationTableOrAlias: throughAssociation.as,
1323
1337
  throughAssociations,
1324
1338
  joinType,
1339
+ baseThroughAssociation,
1340
+ polymorphicCount,
1325
1341
  });
1326
1342
  }
1327
1343
  else {
@@ -1351,7 +1367,7 @@ class KyselyQueryDriver extends Base_js_1.default {
1351
1367
  });
1352
1368
  const throughClass = throughAssociation.modelCB();
1353
1369
  if (Array.isArray(throughClass))
1354
- throw new CannotAssociateThroughPolymorphic_js_1.default({
1370
+ throw new ArrayTargetsIncompatibleWithThroughAssociation_js_1.default({
1355
1371
  dreamClass,
1356
1372
  association,
1357
1373
  });
@@ -1375,18 +1391,23 @@ class KyselyQueryDriver extends Base_js_1.default {
1375
1391
  * the `through` association are only added when the recursion returns
1376
1392
  * back to the association that kicked off the through associations.
1377
1393
  */
1378
- applyOneJoin({ query, dreamClass, previousAssociationTableOrAlias, currentAssociationTableOrAlias, joinAndStatements = {}, throughAssociations = [], joinType, }) {
1394
+ applyOneJoin({ query, dreamClass, previousAssociationTableOrAlias, currentAssociationTableOrAlias, joinAndStatements = {}, throughAssociations = [], joinType, baseThroughAssociation, polymorphicCount, }) {
1379
1395
  const { name, alias } = (0, extractAssociationMetadataFromAssociationName_js_1.default)(currentAssociationTableOrAlias);
1380
1396
  const joinAndStatement = joinAndStatements[currentAssociationTableOrAlias];
1381
1397
  previousAssociationTableOrAlias = (0, extractAssociationMetadataFromAssociationName_js_1.default)(previousAssociationTableOrAlias).alias;
1382
1398
  currentAssociationTableOrAlias = alias;
1383
1399
  let association = dreamClass['getAssociationMetadata'](name);
1400
+ if (association?.polymorphic)
1401
+ polymorphicCount.count++;
1384
1402
  if (!association) {
1385
1403
  throw new JoinAttemptedOnMissingAssociation_js_1.default({
1386
1404
  dreamClass,
1387
1405
  associationName: currentAssociationTableOrAlias,
1388
1406
  });
1389
1407
  }
1408
+ baseThroughAssociation ??= association.through
1409
+ ? association
1410
+ : undefined;
1390
1411
  const results = this.joinsBridgeThroughAssociations({
1391
1412
  query,
1392
1413
  dreamClass,
@@ -1394,11 +1415,23 @@ class KyselyQueryDriver extends Base_js_1.default {
1394
1415
  previousAssociationTableOrAlias,
1395
1416
  throughAssociations,
1396
1417
  joinType,
1418
+ baseThroughAssociation,
1419
+ polymorphicCount,
1397
1420
  });
1398
1421
  query = results.query;
1399
1422
  dreamClass = results.dreamClass;
1400
1423
  association = results.association;
1424
+ if (association?.polymorphic)
1425
+ polymorphicCount.count++;
1401
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
+ });
1402
1435
  const originalPreviousAssociationTableOrAlias = previousAssociationTableOrAlias;
1403
1436
  previousAssociationTableOrAlias = results.previousAssociationTableOrAlias;
1404
1437
  const throughClass = results.throughClass;
@@ -1436,19 +1469,34 @@ class KyselyQueryDriver extends Base_js_1.default {
1436
1469
  });
1437
1470
  }
1438
1471
  if (association.type === 'BelongsTo') {
1439
- if (Array.isArray(association.modelCB()))
1472
+ if (!baseThroughAssociationToApply && Array.isArray(association.modelCB()))
1440
1473
  throw new CannotJoinPolymorphicBelongsToError_js_1.default({
1441
1474
  dreamClass,
1442
1475
  association,
1443
1476
  innerJoinStatements: this.query['innerJoinStatements'],
1444
1477
  leftJoinStatements: this.query['leftJoinStatements'],
1445
1478
  });
1446
- const to = association.modelCB().table;
1479
+ const to = (baseThroughAssociationToApply ?? association).modelCB().table;
1447
1480
  const joinTableExpression = currentAssociationTableOrAlias === to
1448
1481
  ? currentAssociationTableOrAlias
1449
1482
  : `${to} as ${currentAssociationTableOrAlias}`;
1450
1483
  query = query[(joinType === 'inner' ? 'innerJoin' : 'leftJoin')](joinTableExpression, (join) => {
1451
- join = join.onRef(this.namespaceColumn(association.foreignKey(), previousAssociationTableOrAlias), '=', this.namespaceColumn(association.primaryKey(), currentAssociationTableOrAlias));
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
+ }
1452
1500
  if (timeToApplyThroughAssociations) {
1453
1501
  throughAssociations.forEach((throughAssociation) => {
1454
1502
  join = this.applyAssociationAndStatementsToJoinStatement({
@@ -1464,6 +1512,7 @@ class KyselyQueryDriver extends Base_js_1.default {
1464
1512
  join,
1465
1513
  tableNameOrAlias: currentAssociationTableOrAlias,
1466
1514
  association,
1515
+ throughAssociatedClassOverride: baseThroughAssociationToApply?.modelCB(),
1467
1516
  });
1468
1517
  join = this.applyJoinAndStatement(join, joinAndStatement, currentAssociationTableOrAlias);
1469
1518
  return join;
@@ -1505,6 +1554,7 @@ class KyselyQueryDriver extends Base_js_1.default {
1505
1554
  join,
1506
1555
  tableNameOrAlias: currentAssociationTableOrAlias,
1507
1556
  association,
1557
+ throughAssociatedClassOverride: baseThroughAssociationToApply?.modelCB(),
1508
1558
  });
1509
1559
  join = this.applyJoinAndStatement(join, joinAndStatement, currentAssociationTableOrAlias);
1510
1560
  return join;
@@ -1594,9 +1644,9 @@ class KyselyQueryDriver extends Base_js_1.default {
1594
1644
  if (missingRequiredWhereStatements.length)
1595
1645
  throw new MissingRequiredAssociationAndClause_js_1.default(association, missingRequiredWhereStatements[0]);
1596
1646
  }
1597
- conditionallyApplyDefaultScopesDependentOnAssociation({ join, tableNameOrAlias, association, }) {
1647
+ conditionallyApplyDefaultScopesDependentOnAssociation({ join, tableNameOrAlias, association, throughAssociatedClassOverride, }) {
1598
1648
  let scopesQuery = new Query_js_1.default(this.dreamInstance);
1599
- const associationClass = association.modelCB();
1649
+ const associationClass = throughAssociatedClassOverride ?? association.modelCB();
1600
1650
  const associationScopes = associationClass['scopes'].default;
1601
1651
  for (const scope of associationScopes) {
1602
1652
  if (!(0, shouldBypassDefaultScope_js_1.default)(scope.method, {
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class ArrayTargetsIncompatibleWithThroughAssociation extends Error {
4
+ dreamClass;
5
+ association;
6
+ constructor({ dreamClass, association, }) {
7
+ super();
8
+ this.dreamClass = dreamClass;
9
+ this.association = association;
10
+ }
11
+ get message() {
12
+ return `
13
+ Through associations may not define an array of targets:
14
+ Dream class: ${this.dreamClass.sanitizedName}
15
+ Association: ${this.association.as}
16
+ `;
17
+ }
18
+ }
19
+ exports.default = ArrayTargetsIncompatibleWithThroughAssociation;
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class CannotAssociateThroughMultiplePolymorphics extends Error {
4
+ dreamClass;
5
+ association;
6
+ innerJoinStatements;
7
+ leftJoinStatements;
8
+ constructor({ dreamClass, association, innerJoinStatements, leftJoinStatements, }) {
9
+ super();
10
+ this.dreamClass = dreamClass;
11
+ this.association = association;
12
+ this.innerJoinStatements = innerJoinStatements;
13
+ this.leftJoinStatements = leftJoinStatements;
14
+ }
15
+ get message() {
16
+ return `
17
+ Cannot associate through multiple polymorphic associations if one of them is a BelongsTo:
18
+ Dream class: ${this.dreamClass.sanitizedName}
19
+ Association: ${this.association.as}
20
+ Inner Join statements:
21
+ ${JSON.stringify(this.innerJoinStatements, null, 2)}
22
+ Left Join statements:
23
+ ${JSON.stringify(this.leftJoinStatements, null, 2)}
24
+ `;
25
+ }
26
+ }
27
+ exports.default = CannotAssociateThroughMultiplePolymorphics;
@@ -16,7 +16,7 @@ class MissingThroughAssociationSource extends Error {
16
16
 
17
17
  \`${this.dreamClass.sanitizedName}\` association \`${this.association.through}\` points to \`${this.throughClass.sanitizedName}\`.
18
18
 
19
- Dream expects association \`${this.association.source}\` to be defiend on \`${this.throughClass.sanitizedName}\`, but \`${this.throughClass.sanitizedName}\` does not define association \`${this.association.source}\`.
19
+ Dream expects association \`${this.association.source}\` to be defined on \`${this.throughClass.sanitizedName}\`, but \`${this.throughClass.sanitizedName}\` does not define association \`${this.association.source}\`.
20
20
 
21
21
  There are two possible fixes:
22
22
 
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const kysely_1 = require("kysely");
4
4
  const fs = require("node:fs/promises");
5
5
  const path = require("node:path");
6
+ const CliFileWriter_js_1 = require("../../cli/CliFileWriter.js");
6
7
  const dataTypes_js_1 = require("../../db/dataTypes.js");
7
8
  const index_js_1 = require("../../db/index.js");
8
9
  const index_js_2 = require("../../dream-app/index.js");
@@ -51,7 +52,7 @@ export const globalSchema = {
51
52
  // ${schemaConstContent}
52
53
  // `
53
54
  const schemaPath = path.join(dreamApp.projectRoot, dreamApp.paths.types, 'dream.ts');
54
- await fs.writeFile(schemaPath, newSchemaFileContents);
55
+ await CliFileWriter_js_1.CliFileWriter.write(schemaPath, newSchemaFileContents);
55
56
  }
56
57
  globalModelNames() {
57
58
  const dreamApp = index_js_2.default.getOrFail();
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.sharedPathPrefix = exports.relativeDreamPath = exports.dreamPath = exports.pascalize = exports.normalizeUnicode = exports.loadRepl = exports.isEmpty = exports.intersection = exports.hyphenize = exports.groupBy = exports.globalClassNameFromFullyQualifiedModelName = exports.Env = exports.DreamLogos = exports.DateTime = exports.compact = exports.cloneDeepSafe = exports.generateDream = exports.capitalize = exports.camelize = exports.CalendarDate = exports.Benchmark = exports.ValidationError = exports.RecordNotFound = exports.GlobalNameNotSet = exports.CreateOrFindByFailedToCreateAndFind = exports.NonLoadedAssociation = exports.Encrypt = exports.Query = exports.DreamTransaction = exports.openapiShorthandPrimitiveTypes = exports.openapiPrimitiveTypes = exports.DreamConst = exports.Dream = exports.DreamAppAllowedPackageManagersEnumValues = exports.DreamApp = exports.lookupClassByGlobalName = exports.DreamImporter = exports.Decorators = exports.STI = exports.SoftDelete = exports.ReplicaSafe = exports.validateTable = exports.validateColumn = exports.DreamMigrationHelpers = exports.untypedDb = exports.pgErrorType = exports.dreamDbConnections = exports.closeAllDbConnections = exports.DreamCLI = exports.DreamBin = void 0;
4
- exports.ObjectSerializerBuilder = exports.DreamSerializerBuilder = exports.ObjectSerializer = exports.DreamSerializer = exports.serializerNameFromFullyQualifiedModelName = exports.isDreamSerializer = exports.inferSerializersFromDreamClassOrViewModelClass = exports.inferSerializerFromDreamOrViewModel = exports.ops = exports.uniq = exports.uncapitalize = exports.expandStiClasses = exports.standardizeFullyQualifiedModelName = exports.sortObjectByValue = exports.sortObjectByKey = exports.sortBy = exports.sort = exports.snakeify = exports.round = exports.range = exports.Range = void 0;
3
+ exports.relativeDreamPath = exports.dreamPath = exports.pascalize = exports.normalizeUnicode = exports.loadRepl = exports.isEmpty = exports.intersection = exports.hyphenize = exports.groupBy = exports.globalClassNameFromFullyQualifiedModelName = exports.Env = exports.DreamLogos = exports.DateTime = exports.compact = exports.cloneDeepSafe = exports.generateDream = exports.capitalize = exports.camelize = exports.CalendarDate = exports.Benchmark = exports.ValidationError = exports.RecordNotFound = exports.GlobalNameNotSet = exports.CreateOrFindByFailedToCreateAndFind = exports.NonLoadedAssociation = exports.Encrypt = exports.Query = exports.DreamTransaction = exports.openapiShorthandPrimitiveTypes = exports.openapiPrimitiveTypes = exports.DreamConst = exports.Dream = exports.DreamAppAllowedPackageManagersEnumValues = exports.DreamApp = exports.lookupClassByGlobalName = exports.DreamImporter = exports.Decorators = exports.STI = exports.SoftDelete = exports.ReplicaSafe = exports.validateTable = exports.validateColumn = exports.DreamMigrationHelpers = exports.untypedDb = exports.pgErrorType = exports.dreamDbConnections = exports.closeAllDbConnections = exports.DreamCLI = exports.CliFileWriter = exports.DreamBin = void 0;
4
+ exports.ObjectSerializerBuilder = exports.DreamSerializerBuilder = exports.ObjectSerializer = exports.DreamSerializer = exports.serializerNameFromFullyQualifiedModelName = exports.isDreamSerializer = exports.inferSerializersFromDreamClassOrViewModelClass = exports.inferSerializerFromDreamOrViewModel = exports.ops = exports.uniq = exports.uncapitalize = exports.expandStiClasses = exports.standardizeFullyQualifiedModelName = exports.sortObjectByValue = exports.sortObjectByKey = exports.sortBy = exports.sort = exports.snakeify = exports.round = exports.range = exports.Range = exports.sharedPathPrefix = void 0;
5
5
  var index_js_1 = require("./bin/index.js");
6
6
  Object.defineProperty(exports, "DreamBin", { enumerable: true, get: function () { return index_js_1.default; } });
7
+ var CliFileWriter_js_1 = require("./cli/CliFileWriter.js");
8
+ Object.defineProperty(exports, "CliFileWriter", { enumerable: true, get: function () { return CliFileWriter_js_1.CliFileWriter; } });
7
9
  var index_js_2 = require("./cli/index.js");
8
10
  Object.defineProperty(exports, "DreamCLI", { enumerable: true, get: function () { return index_js_2.default; } });
9
11
  var DreamDbConnection_js_1 = require("./db/DreamDbConnection.js");
@@ -9,11 +9,13 @@ import EnvInternal from '../../helpers/EnvInternal.js';
9
9
  import dreamPath from '../../helpers/path/dreamPath.js';
10
10
  import snakeify from '../../helpers/snakeify.js';
11
11
  import sspawn from '../../helpers/sspawn.js';
12
+ import { CliFileWriter } from '../../cli/CliFileWriter.js';
12
13
  export default async function writeSyncFile() {
13
14
  const dreamApp = DreamApp.getOrFail();
14
15
  const dbConf = dreamApp.dbConnectionConfig('primary');
15
16
  const dbSyncFilePath = path.join(dreamPath('types'), 'db.ts');
16
17
  const absoluteDbSyncPath = path.join(dreamApp.projectRoot, dbSyncFilePath);
18
+ await CliFileWriter.cache(absoluteDbSyncPath);
17
19
  await sspawn(`kysely-codegen --dialect=postgres --url=postgres://${dbConf.user}:${dbConf.password}@${dbConf.host}:${dbConf.port}/${dbConf.name} --out-file=${absoluteDbSyncPath}`, {
18
20
  onStdout: message => {
19
21
  DreamCLI.logger.logContinueProgress(colorize(`[db]`, { color: 'cyan' }) + ' ' + message, {
@@ -25,7 +27,7 @@ export default async function writeSyncFile() {
25
27
  // from the dist folder, whereas dirname here is pointing to true src folder.
26
28
  const file = (await fs.readFile(absoluteDbSyncPath)).toString();
27
29
  const enhancedSchema = enhanceSchema(file);
28
- await fs.writeFile(absoluteDbSyncPath, enhancedSchema);
30
+ await CliFileWriter.write(absoluteDbSyncPath, enhancedSchema);
29
31
  }
30
32
  // begin: schema helpers
31
33
  function enhanceSchema(file) {
@@ -0,0 +1,35 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import DreamCLI from './index.js';
3
+ export class CliFileWriter {
4
+ static async write(filepath, contents) {
5
+ await cliFileWriter.write(filepath, contents);
6
+ }
7
+ static async revert() {
8
+ await cliFileWriter.revert();
9
+ }
10
+ static async cache(filepath) {
11
+ await cliFileWriter.cache(filepath);
12
+ }
13
+ fileCache = {};
14
+ async write(filepath, contents) {
15
+ // if we have manually backed up this file, or else this file
16
+ // has been written to twice in one CLI session, we want
17
+ // to preserve the original backup, so we do not attempt
18
+ // to cache a second time
19
+ if (!this.fileCache[filepath])
20
+ await this.cache(filepath);
21
+ await fs.writeFile(filepath, contents);
22
+ }
23
+ async revert() {
24
+ const filepaths = Object.keys(this.fileCache);
25
+ for (const filepath of filepaths) {
26
+ DreamCLI.logger.logContinueProgress(`reverting ${filepath}`);
27
+ await fs.writeFile(filepath, this.fileCache[filepath]);
28
+ }
29
+ }
30
+ async cache(filepath) {
31
+ const originalContents = (await fs.readFile(filepath)).toString();
32
+ this.fileCache[filepath] = originalContents;
33
+ }
34
+ }
35
+ const cliFileWriter = new CliFileWriter();
@@ -8,6 +8,11 @@ export default class DreamCliLogger {
8
8
  });
9
9
  loggable.render();
10
10
  }
11
+ async logProgress(text, cb, { logPrefix = '✺ ┌', logPrefixColor, logPrefixBgColor } = {}) {
12
+ this.log(text, { logPrefix, logPrefixColor, logPrefixBgColor });
13
+ await cb();
14
+ this.logEndProgress();
15
+ }
11
16
  logStartProgress(text, { logPrefix = '✺ ┌', logPrefixColor, logPrefixBgColor } = {}) {
12
17
  this.log(text, { logPrefix, logPrefixColor, logPrefixBgColor });
13
18
  }
@@ -89,12 +89,12 @@ export function applyGetterAndSetter(target, partialAssociation, { foreignKeyBas
89
89
  export function associationPrimaryKeyAccessors(partialAssociation, dreamClass) {
90
90
  return {
91
91
  ...partialAssociation,
92
- primaryKey(associationInstance) {
92
+ primaryKey(associationInstance, { associatedClassOverride } = {}) {
93
93
  if (this.primaryKeyOverride)
94
94
  return this.primaryKeyOverride;
95
95
  if (associationInstance)
96
96
  return associationInstance.primaryKey;
97
- const associationClass = this.modelCB();
97
+ const associationClass = associatedClassOverride ?? this.modelCB();
98
98
  if (Array.isArray(associationClass)) {
99
99
  throw new Error(`
100
100
  Cannot lookup primaryKey on polymorphic association: