@rvoh/dream 2.34.0 → 2.36.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 (302) hide show
  1. package/README.md +2 -2
  2. package/dist/cjs/src/Dream.js +270 -69
  3. package/dist/cjs/src/bin/index.js +13 -0
  4. package/dist/cjs/src/cli/index.js +28 -17
  5. package/dist/cjs/src/db/migration-helpers/DreamMigrationHelpers.js +23 -21
  6. package/dist/cjs/src/decorators/Decorators.js +26 -4
  7. package/dist/cjs/src/decorators/field/Encrypted.js +30 -6
  8. package/dist/cjs/src/dream/DreamClassTransactionBuilder.js +6 -6
  9. package/dist/cjs/src/dream/DreamInstanceTransactionBuilder.js +63 -27
  10. package/dist/cjs/src/dream/LeftJoinLoadBuilder.js +3 -3
  11. package/dist/cjs/src/dream/LoadBuilder.js +1 -1
  12. package/dist/cjs/src/dream/Query.js +542 -167
  13. package/dist/cjs/src/dream/QueryDriver/Base.js +72 -0
  14. package/dist/cjs/src/dream/QueryDriver/Kysely.js +216 -50
  15. package/dist/cjs/src/dream/internal/associations/assertRecognizedAssociationConditionKeys.js +48 -0
  16. package/dist/cjs/src/dream/internal/associations/associationQuery.js +4 -0
  17. package/dist/cjs/src/dream/internal/reload.js +6 -1
  18. package/dist/cjs/src/dream/internal/saveDream.js +8 -2
  19. package/dist/cjs/src/dream/internal/sqlResultToDreamInstance.js +2 -0
  20. package/dist/cjs/src/dream/internal/undestroyDream.js +1 -1
  21. package/dist/cjs/src/dream-app/index.js +38 -0
  22. package/dist/cjs/src/encrypt/InternalEncrypt.js +49 -4
  23. package/dist/cjs/src/encrypt/defaultEncryptedColumnName.js +10 -0
  24. package/dist/cjs/src/encrypt/index.js +76 -9
  25. package/dist/cjs/src/errors/DoNotSetEncryptedFieldsDirectly.js +4 -0
  26. package/dist/cjs/src/errors/associations/UnrecognizedAssociationConditionKeys.js +25 -0
  27. package/dist/cjs/src/helpers/Env.js +28 -0
  28. package/dist/cjs/src/helpers/cli/generateDreamContent.js +2 -1
  29. package/dist/cjs/src/helpers/cli/generateFactoryContent.js +1 -1
  30. package/dist/cjs/src/helpers/cli/generateMigrationContent.js +9 -9
  31. package/dist/cjs/src/helpers/cli/generateSerializerContent.js +21 -8
  32. package/dist/cjs/src/serializer/SerializerRenderer.js +65 -13
  33. package/dist/cjs/src/serializer/builders/DreamSerializerBuilder.js +54 -38
  34. package/dist/cjs/src/serializer/builders/ObjectSerializerBuilder.js +34 -14
  35. package/dist/cjs/src/serializer/helpers/serializersForAssociatedClass.js +43 -0
  36. package/dist/esm/src/Dream.js +270 -69
  37. package/dist/esm/src/bin/index.js +13 -0
  38. package/dist/esm/src/cli/index.js +28 -17
  39. package/dist/esm/src/db/migration-helpers/DreamMigrationHelpers.js +23 -21
  40. package/dist/esm/src/decorators/Decorators.js +26 -4
  41. package/dist/esm/src/decorators/field/Encrypted.js +30 -6
  42. package/dist/esm/src/dream/DreamClassTransactionBuilder.js +6 -6
  43. package/dist/esm/src/dream/DreamInstanceTransactionBuilder.js +63 -27
  44. package/dist/esm/src/dream/LeftJoinLoadBuilder.js +3 -3
  45. package/dist/esm/src/dream/LoadBuilder.js +1 -1
  46. package/dist/esm/src/dream/Query.js +542 -167
  47. package/dist/esm/src/dream/QueryDriver/Base.js +72 -0
  48. package/dist/esm/src/dream/QueryDriver/Kysely.js +216 -50
  49. package/dist/esm/src/dream/internal/associations/assertRecognizedAssociationConditionKeys.js +48 -0
  50. package/dist/esm/src/dream/internal/associations/associationQuery.js +4 -0
  51. package/dist/esm/src/dream/internal/reload.js +6 -1
  52. package/dist/esm/src/dream/internal/saveDream.js +8 -2
  53. package/dist/esm/src/dream/internal/sqlResultToDreamInstance.js +2 -0
  54. package/dist/esm/src/dream/internal/undestroyDream.js +1 -1
  55. package/dist/esm/src/dream-app/index.js +38 -0
  56. package/dist/esm/src/encrypt/InternalEncrypt.js +49 -4
  57. package/dist/esm/src/encrypt/defaultEncryptedColumnName.js +10 -0
  58. package/dist/esm/src/encrypt/index.js +76 -9
  59. package/dist/esm/src/errors/DoNotSetEncryptedFieldsDirectly.js +4 -0
  60. package/dist/esm/src/errors/associations/UnrecognizedAssociationConditionKeys.js +25 -0
  61. package/dist/esm/src/helpers/Env.js +28 -0
  62. package/dist/esm/src/helpers/cli/generateDreamContent.js +2 -1
  63. package/dist/esm/src/helpers/cli/generateFactoryContent.js +1 -1
  64. package/dist/esm/src/helpers/cli/generateMigrationContent.js +9 -9
  65. package/dist/esm/src/helpers/cli/generateSerializerContent.js +21 -8
  66. package/dist/esm/src/serializer/SerializerRenderer.js +65 -13
  67. package/dist/esm/src/serializer/builders/DreamSerializerBuilder.js +54 -38
  68. package/dist/esm/src/serializer/builders/ObjectSerializerBuilder.js +34 -14
  69. package/dist/esm/src/serializer/helpers/serializersForAssociatedClass.js +43 -0
  70. package/dist/types/src/Dream.d.ts +217 -55
  71. package/dist/types/src/bin/index.d.ts +7 -1
  72. package/dist/types/src/cli/index.d.ts +13 -0
  73. package/dist/types/src/db/migration-helpers/DreamMigrationHelpers.d.ts +2 -2
  74. package/dist/types/src/decorators/Decorators.d.ts +26 -3
  75. package/dist/types/src/decorators/field/Encrypted.d.ts +6 -1
  76. package/dist/types/src/dream/DreamClassTransactionBuilder.d.ts +9 -9
  77. package/dist/types/src/dream/DreamInstanceTransactionBuilder.d.ts +52 -25
  78. package/dist/types/src/dream/LeftJoinLoadBuilder.d.ts +4 -4
  79. package/dist/types/src/dream/LoadBuilder.d.ts +1 -1
  80. package/dist/types/src/dream/Query.d.ts +272 -33
  81. package/dist/types/src/dream/QueryDriver/Base.d.ts +63 -1
  82. package/dist/types/src/dream/QueryDriver/Kysely.d.ts +85 -1
  83. package/dist/types/src/dream/internal/associations/assertRecognizedAssociationConditionKeys.d.ts +29 -0
  84. package/dist/types/src/dream-app/index.d.ts +56 -2
  85. package/dist/types/src/encrypt/InternalEncrypt.d.ts +20 -1
  86. package/dist/types/src/encrypt/defaultEncryptedColumnName.d.ts +7 -0
  87. package/dist/types/src/encrypt/index.d.ts +75 -6
  88. package/dist/types/src/errors/associations/UnrecognizedAssociationConditionKeys.d.ts +8 -0
  89. package/dist/types/src/helpers/Env.d.ts +28 -0
  90. package/dist/types/src/package-exports/openapi.d.ts +1 -1
  91. package/dist/types/src/package-exports/types.d.ts +2 -2
  92. package/dist/types/src/serializer/SerializerRenderer.d.ts +13 -0
  93. package/dist/types/src/serializer/builders/DreamSerializerBuilder.d.ts +59 -55
  94. package/dist/types/src/serializer/builders/ObjectSerializerBuilder.d.ts +40 -19
  95. package/dist/types/src/serializer/helpers/serializersForAssociatedClass.d.ts +17 -0
  96. package/dist/types/src/types/lifecycle.d.ts +6 -0
  97. package/dist/types/src/types/openapi.d.ts +52 -29
  98. package/dist/types/src/types/serializer.d.ts +43 -2
  99. package/dist/types/src/types/types/lifecycle.ts +6 -0
  100. package/dist/types/src/types/types/openapi.ts +51 -37
  101. package/dist/types/src/types/types/serializer.ts +62 -2
  102. package/dist/types/src/types/types/variadic.ts +99 -43
  103. package/dist/types/src/types/variadic.d.ts +29 -3
  104. package/docs/assets/hierarchy.js +1 -1
  105. package/docs/assets/highlight.css +4 -11
  106. package/docs/assets/navigation.js +1 -1
  107. package/docs/assets/search.js +1 -1
  108. package/docs/classes/db.DreamMigrationHelpers.html +13 -13
  109. package/docs/classes/db.KyselyQueryDriver.html +85 -46
  110. package/docs/classes/db.PostgresQueryDriver.html +86 -47
  111. package/docs/classes/db.QueryDriverBase.html +84 -45
  112. package/docs/classes/errors.CannotSaveMissingDream.html +2 -2
  113. package/docs/classes/errors.CheckConstraintViolation.html +3 -3
  114. package/docs/classes/errors.ColumnOverflow.html +3 -3
  115. package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
  116. package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
  117. package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
  118. package/docs/classes/errors.DecryptionError.html +2 -2
  119. package/docs/classes/errors.DecryptionParseError.html +2 -2
  120. package/docs/classes/errors.DecryptionRotationError.html +3 -3
  121. package/docs/classes/errors.GlobalNameNotSet.html +3 -3
  122. package/docs/classes/errors.InvalidCalendarDate.html +2 -2
  123. package/docs/classes/errors.InvalidClockTime.html +2 -2
  124. package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
  125. package/docs/classes/errors.InvalidDateTime.html +2 -2
  126. package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
  127. package/docs/classes/errors.NonLoadedAssociation.html +3 -3
  128. package/docs/classes/errors.NotNullViolation.html +3 -3
  129. package/docs/classes/errors.RecordNotFound.html +3 -3
  130. package/docs/classes/errors.SortableScopeDidNotStabilize.html +3 -3
  131. package/docs/classes/errors.SortableScopeLockWaitTimedOut.html +3 -3
  132. package/docs/classes/errors.ValidationError.html +3 -3
  133. package/docs/classes/index.CalendarDate.html +33 -33
  134. package/docs/classes/index.ClockTime.html +32 -32
  135. package/docs/classes/index.ClockTimeTz.html +35 -35
  136. package/docs/classes/index.DateTime.html +86 -86
  137. package/docs/classes/index.Decorators.html +37 -23
  138. package/docs/classes/index.Dream.html +281 -155
  139. package/docs/classes/index.DreamApp.html +40 -10
  140. package/docs/classes/index.DreamTransaction.html +3 -3
  141. package/docs/classes/index.Env.html +16 -2
  142. package/docs/classes/index.Query.html +331 -129
  143. package/docs/classes/system.CliFileWriter.html +4 -4
  144. package/docs/classes/system.DreamBin.html +6 -2
  145. package/docs/classes/system.DreamCLI.html +7 -7
  146. package/docs/classes/system.DreamImporter.html +2 -2
  147. package/docs/classes/system.DreamLogos.html +2 -2
  148. package/docs/classes/system.DreamSerializerBuilder.html +52 -34
  149. package/docs/classes/system.ObjectSerializerBuilder.html +41 -22
  150. package/docs/classes/system.PathHelpers.html +4 -4
  151. package/docs/classes/utils.Encrypt.html +54 -10
  152. package/docs/classes/utils.Range.html +2 -2
  153. package/docs/functions/db.closeAllDbConnections.html +1 -1
  154. package/docs/functions/db.dreamDbConnections.html +1 -1
  155. package/docs/functions/db.untypedDb.html +1 -1
  156. package/docs/functions/db.validateColumn.html +1 -1
  157. package/docs/functions/db.validateTable.html +1 -1
  158. package/docs/functions/errors.pgErrorType.html +1 -1
  159. package/docs/functions/index.DreamSerializer.html +1 -1
  160. package/docs/functions/index.ObjectSerializer.html +1 -1
  161. package/docs/functions/index.ReplicaSafe.html +1 -1
  162. package/docs/functions/index.STI.html +1 -1
  163. package/docs/functions/index.SoftDelete.html +1 -1
  164. package/docs/functions/utils.camelize.html +1 -1
  165. package/docs/functions/utils.capitalize.html +1 -1
  166. package/docs/functions/utils.cloneDeepSafe.html +1 -1
  167. package/docs/functions/utils.compact.html +1 -1
  168. package/docs/functions/utils.groupBy.html +1 -1
  169. package/docs/functions/utils.hyphenize.html +1 -1
  170. package/docs/functions/utils.intersection.html +1 -1
  171. package/docs/functions/utils.isEmpty.html +1 -1
  172. package/docs/functions/utils.normalizeUnicode.html +1 -1
  173. package/docs/functions/utils.pascalize.html +1 -1
  174. package/docs/functions/utils.percent.html +1 -1
  175. package/docs/functions/utils.range.html +1 -1
  176. package/docs/functions/utils.round.html +1 -1
  177. package/docs/functions/utils.sanitizeString.html +1 -1
  178. package/docs/functions/utils.snakeify.html +1 -1
  179. package/docs/functions/utils.sort.html +1 -1
  180. package/docs/functions/utils.sortBy.html +1 -1
  181. package/docs/functions/utils.sortObjectByKey.html +1 -1
  182. package/docs/functions/utils.sortObjectByValue.html +1 -1
  183. package/docs/functions/utils.uncapitalize.html +1 -1
  184. package/docs/functions/utils.uniq.html +1 -1
  185. package/docs/hierarchy.html +1 -1
  186. package/docs/index.html +1 -1
  187. package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
  188. package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
  189. package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
  190. package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
  191. package/docs/interfaces/types.BelongsToStatement.html +2 -2
  192. package/docs/interfaces/types.DecoratorContext.html +2 -2
  193. package/docs/interfaces/types.DecryptCallbacks.html +6 -0
  194. package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
  195. package/docs/interfaces/types.DreamAppOpts.html +2 -2
  196. package/docs/interfaces/types.DreamDbConfig.html +6 -6
  197. package/docs/interfaces/types.DurationObject.html +2 -2
  198. package/docs/interfaces/types.EncryptOptions.html +2 -2
  199. package/docs/interfaces/types.EncryptionLegacyKeyUsedEvent.html +10 -0
  200. package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
  201. package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
  202. package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
  203. package/docs/modules/openapi.html +1 -1
  204. package/docs/modules/types.html +1 -1
  205. package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
  206. package/docs/types/openapi.OpenapiAllTypes.html +1 -1
  207. package/docs/types/openapi.OpenapiFormats.html +1 -1
  208. package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
  209. package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
  210. package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
  211. package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
  212. package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
  213. package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
  214. package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
  215. package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
  216. package/docs/types/openapi.OpenapiSchemaCombinatorFields.html +4 -0
  217. package/docs/types/openapi.OpenapiSchemaCommonFields.html +5 -1
  218. package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +1 -2
  219. package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +1 -2
  220. package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +1 -2
  221. package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
  222. package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
  223. package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
  224. package/docs/types/openapi.OpenapiSchemaNull.html +4 -2
  225. package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
  226. package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
  227. package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +5 -1
  228. package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +2 -1
  229. package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
  230. package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
  231. package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
  232. package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
  233. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +1 -2
  234. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +1 -2
  235. package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +1 -2
  236. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
  237. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
  238. package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  239. package/docs/types/openapi.OpenapiSchemaString.html +1 -1
  240. package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
  241. package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  242. package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
  243. package/docs/types/openapi.OpenapiTypeField.html +1 -1
  244. package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
  245. package/docs/types/types.CalendarDateDurationUnit.html +1 -1
  246. package/docs/types/types.CalendarDateObject.html +1 -1
  247. package/docs/types/types.Camelized.html +1 -1
  248. package/docs/types/types.ClockTimeObject.html +1 -1
  249. package/docs/types/types.DbConnectionType.html +1 -1
  250. package/docs/types/types.DbTypes.html +1 -1
  251. package/docs/types/types.DreamAssociationMetadata.html +1 -1
  252. package/docs/types/types.DreamAttributes.html +1 -1
  253. package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
  254. package/docs/types/types.DreamClassColumn.html +1 -1
  255. package/docs/types/types.DreamColumn.html +1 -1
  256. package/docs/types/types.DreamColumnNames.html +1 -1
  257. package/docs/types/types.DreamLogLevel.html +1 -1
  258. package/docs/types/types.DreamLogger.html +2 -2
  259. package/docs/types/types.DreamModelSerializerType.html +1 -1
  260. package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
  261. package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
  262. package/docs/types/types.DreamParamSafeAttributes.html +1 -1
  263. package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
  264. package/docs/types/types.DreamSerializable.html +1 -1
  265. package/docs/types/types.DreamSerializableArray.html +1 -1
  266. package/docs/types/types.DreamSerializerKey.html +1 -1
  267. package/docs/types/types.DreamSerializers.html +1 -1
  268. package/docs/types/types.DreamVirtualColumns.html +1 -1
  269. package/docs/types/types.DurationUnit.html +1 -1
  270. package/docs/types/types.EncryptAlgorithm.html +1 -1
  271. package/docs/types/types.HasManyStatement.html +1 -1
  272. package/docs/types/types.HasOneStatement.html +1 -1
  273. package/docs/types/types.Hyphenized.html +1 -1
  274. package/docs/types/types.Pascalized.html +1 -1
  275. package/docs/types/types.PrimaryKeyType.html +1 -1
  276. package/docs/types/types.RoundingPrecision.html +1 -1
  277. package/docs/types/types.SerializerCasing.html +1 -1
  278. package/docs/types/types.SimpleObjectSerializerType.html +1 -1
  279. package/docs/types/types.Snakeified.html +1 -1
  280. package/docs/types/types.StrictInterface.html +1 -1
  281. package/docs/types/types.UpdateableAssociationProperties.html +1 -1
  282. package/docs/types/types.UpdateableProperties.html +1 -1
  283. package/docs/types/types.ValidationType.html +1 -1
  284. package/docs/types/types.ViewModel.html +2 -2
  285. package/docs/types/types.ViewModelClass.html +1 -1
  286. package/docs/types/types.WeekdayName.html +1 -1
  287. package/docs/types/types.WhereStatementForDream.html +1 -1
  288. package/docs/types/types.WhereStatementForDreamClass.html +1 -1
  289. package/docs/variables/index.DreamConst.html +1 -1
  290. package/docs/variables/index.ops.html +1 -1
  291. package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
  292. package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
  293. package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
  294. package/docs/variables/system.primaryKeyTypes.html +1 -1
  295. package/package.json +1 -1
  296. package/dist/cjs/src/serializer/helpers/serializerForAssociatedClass.js +0 -37
  297. package/dist/esm/src/serializer/helpers/serializerForAssociatedClass.js +0 -37
  298. package/dist/types/src/serializer/helpers/serializerForAssociatedClass.d.ts +0 -9
  299. package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +0 -1
  300. package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +0 -1
  301. package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +0 -1
  302. package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +0 -1
@@ -4,6 +4,7 @@ import Query from '../dream/Query.js';
4
4
  import DBClassDeprecation from '../helpers/cli/DBClassDeprecation.js';
5
5
  import generateDream from '../helpers/cli/generateDream.js';
6
6
  import EnvInternal from '../helpers/EnvInternal.js';
7
+ import standardizeFullyQualifiedModelName from '../helpers/standardizeFullyQualifiedModelName.js';
7
8
  export default class DreamBin {
8
9
  static async sync(onSync, options) {
9
10
  if (!EnvInternal.isTest) {
@@ -58,6 +59,7 @@ export default class DreamBin {
58
59
  });
59
60
  }
60
61
  static async generateStiChild(fullyQualifiedModelName, fullyQualifiedParentName, columnsWithTypes, options) {
62
+ const connectionName = options.connectionName ?? stiParentConnectionName(fullyQualifiedModelName, fullyQualifiedParentName);
61
63
  await generateDream({
62
64
  fullyQualifiedModelName,
63
65
  columnsWithTypes,
@@ -65,6 +67,7 @@ export default class DreamBin {
65
67
  includeAdminSerializers: options.adminSerializers ?? false,
66
68
  includeInternalSerializers: options.internalSerializers ?? false,
67
69
  ...options,
70
+ connectionName,
68
71
  stiBaseSerializer: false,
69
72
  // `@SoftDelete()` is incompatible with STI children — never auto-apply.
70
73
  softDelete: false,
@@ -95,3 +98,13 @@ export default class DreamBin {
95
98
  DreamCLI.logger.logEndProgress();
96
99
  }
97
100
  }
101
+ /**
102
+ * An STI child shares its parent's table, so its migration belongs to the
103
+ * parent model's connection.
104
+ */
105
+ function stiParentConnectionName(fullyQualifiedModelName, fullyQualifiedParentName) {
106
+ const parentModel = DreamApp.getOrFail().models[standardizeFullyQualifiedModelName(fullyQualifiedParentName)];
107
+ if (!parentModel)
108
+ throw new Error(`Cannot generate the STI child ${fullyQualifiedModelName}: its parent, ${fullyQualifiedParentName}, is not one of the app models. The parent must already exist, and its name must match its path under the models directory (e.g. Health/Coach for Health/Coach.ts).`);
109
+ return parentModel.prototype.connectionName;
110
+ }
@@ -68,44 +68,56 @@ function buildColumnsWithTypesDescription(belongsToDescription) {
68
68
  return `${baseColumnsWithTypesDescription}
69
69
  ${belongsToDescription}`;
70
70
  }
71
- const columnsWithTypesDescription = buildColumnsWithTypesDescription(`
71
+ /**
72
+ * The `[columnsWithTypes...]` argument description for `g:model`. Exported so
73
+ * that spec/unit/cli/generateMigrationContent.spec.ts can run each
74
+ * `:belongs_to` example in this text through the migration generator and check
75
+ * the FK column the example's comment names: an example that drifts from what
76
+ * the generator actually produces then fails the suite instead of shipping.
77
+ */
78
+ export const columnsWithTypesDescription = buildColumnsWithTypesDescription(`
72
79
  ${INDENT}
73
80
  ${INDENT} - belongs_to:
74
81
  ${INDENT} ALWAYS use this instead of adding a raw uuid column for foreign keys. It creates the FK column, adds a database index,
75
82
  ${INDENT} AND generates the @deco.BelongsTo association and typed property on the model. A raw uuid column does none of this.
76
83
  ${INDENT}
77
- ${INDENT} use the fully qualified model name (matching its path under src/app/models/):
84
+ ${INDENT} use the fully qualified model name (matching its path under src/app/models/). The last segment of that
85
+ ${INDENT} path names the FK column, the typed FK property and the association:
78
86
  ${INDENT} User:belongs_to # creates user_id column + BelongsTo association
79
- ${INDENT} Health/Coach:belongs_to # creates health_coach_id column + BelongsTo association
87
+ ${INDENT} Health/Coach:belongs_to # creates coach_id column, coachId property, coach association
80
88
  ${INDENT} User:belongs_to:optional # nullable foreign key (for optional associations)
81
89
  ${INDENT}
82
- ${INDENT} rename the association with Model@alias — the snake_case alias drives the FK column name AND the
83
- ${INDENT} @deco.BelongsTo association + typed FK property on the generated model:
90
+ ${INDENT} rename the association with Model@alias — the snake_case alias takes the last segment's place, naming the
91
+ ${INDENT} FK column <alias>_id AND the @deco.BelongsTo association + typed FK property on the generated model:
84
92
  ${INDENT} InternalUser@canceled_by:belongs_to:optional # canceled_by_id column, canceledById property, canceledBy association,
85
93
  ${INDENT} # @deco.BelongsTo('InternalUser', { on: 'canceledById', optional: true })
86
- ${INDENT} Messaging/Template@template:belongs_to # template_id column, templateId property, template association
87
- ${INDENT} # (strips the namespace from the property/association names while keeping
88
- ${INDENT} # the namespaced model reference intact)
94
+ ${INDENT} Health/Coach@health_coach:belongs_to # health_coach_id column, healthCoachId property, healthCoach association,
95
+ ${INDENT} # @deco.BelongsTo('Health/Coach', { on: 'healthCoachId' })
89
96
  ${INDENT} Aliasing also lets you declare multiple FKs to the same model in one generator call without column collisions.`);
90
97
  export const columnsWithTypesDescriptionForStiChild = buildColumnsWithTypesDescription(`
91
98
  ${INDENT}
92
99
  ${INDENT} - belongs_to:
93
100
  ${INDENT} NOT supported for STI children. STI children cannot declare associations; declare all BelongsTo,
94
101
  ${INDENT} HasOne, and HasMany associations on the STI parent model instead.`);
95
- const columnsWithTypesDescriptionForMigration = buildColumnsWithTypesDescription(`
102
+ /**
103
+ * The `[columnsWithTypes...]` argument description for `g:migration`. Exported
104
+ * for the same `:belongs_to` example check as `columnsWithTypesDescription`.
105
+ */
106
+ export const columnsWithTypesDescriptionForMigration = buildColumnsWithTypesDescription(`
96
107
  ${INDENT}
97
108
  ${INDENT} - belongs_to:
98
109
  ${INDENT} ALWAYS use this instead of adding a raw uuid column for foreign keys. It creates the FK column with an index.
99
110
  ${INDENT} Unlike in g:model/g:resource, this does NOT add a BelongsTo association (no model is generated).
100
111
  ${INDENT}
101
- ${INDENT} use the fully qualified model name (matching its path under src/app/models/):
112
+ ${INDENT} use the fully qualified model name (matching its path under src/app/models/). The last segment of that
113
+ ${INDENT} path names the FK column:
102
114
  ${INDENT} User:belongs_to # creates user_id column with index
103
- ${INDENT} Health/Coach:belongs_to # creates health_coach_id column with index
115
+ ${INDENT} Health/Coach:belongs_to # creates coach_id column with index
104
116
  ${INDENT} User:belongs_to:optional # nullable foreign key
105
117
  ${INDENT}
106
- ${INDENT} rename the FK column with Model@alias (snake_case alias becomes the column name):
118
+ ${INDENT} rename the FK column with Model@alias (the column is <alias>_id, with the alias snake_cased):
107
119
  ${INDENT} InternalUser@canceled_by:belongs_to:optional # canceled_by_id column with index
108
- ${INDENT} Messaging/Template@template:belongs_to # template_id column (strips the namespace from the column name)`);
120
+ ${INDENT} Health/Coach@health_coach:belongs_to # health_coach_id column with index`);
109
121
  /**
110
122
  * The `<migrationName>` argument description for `g:migration`. Exported so
111
123
  * that spec/unit/helpers/cli/generateMigration.spec.ts can read the example
@@ -217,7 +229,7 @@ ${INDENT} # STI parent model (use with g:sti-child for children)
217
229
  ${INDENT} pnpm psy g:model --sti-base-serializer Room Place:belongs_to type:enum:room_types:Bathroom,Bedroom deleted_at:datetime:optional`)
218
230
  .option('--no-serializer', 'skip serializer generation. Useful for internal models that will never be serialized in an API response (e.g., join tables, audit logs)')
219
231
  .option('--connection-name <connectionName>', 'the name of the database connection to use for the model. Only needed for multi-database setups; defaults to "default"', 'default')
220
- .option('--sti-base-serializer', `Creates generically typed base serializers (default and summary) that accept a \`StiChildClass\` parameter and include the \`type\` attribute with a per-child enum constraint. This allows consuming applications to determine the response shape based on the STI type discriminator.
232
+ .option('--sti-base-serializer', `Creates generically typed base serializers (summary and default, plus each Admin and Internal pair requested with --admin-serializers or --internal-serializers) that accept a \`StiChildClass\` parameter. When a \`type\` column is given, each summary serializer renders \`type\` right after \`id\`, with an OpenAPI enum of just the STI child's class name, and the default serializer inherits it from the summary. This allows consuming applications to determine the response shape based on the STI type discriminator.
221
233
  ${INDENT}
222
234
  ${INDENT}Use this when generating the parent model of an STI hierarchy. After generating the parent, use g:sti-child for each child type.
223
235
  ${INDENT}
@@ -255,9 +267,9 @@ ${INDENT} Settings/CommunicationPreferences # src/app/models/Settings/Communi
255
267
  program
256
268
  .command('generate:sti-child')
257
269
  .alias('g:sti-child')
258
- .description(`Generates an STI (Single Table Inheritance) child model that extends an existing parent model. The child shares the parent's database table (discriminated by the \`type\` column) and can add child-specific columns. Generates a child model decorated with @STI(Parent), child serializers extending the parent's base serializers, a migration that ALTERs the parent table (not a new table), check constraints, a factory, and spec skeleton.
270
+ .description(`Generates an STI (Single Table Inheritance) child model that extends an existing parent model. The child shares the parent's database table (discriminated by the \`type\` column) and can add child-specific columns. Always generates the child model decorated with @STI(Parent), a factory, and a spec skeleton; unless --no-serializer is passed, also generates child serializers extending the parent's base serializers.
259
271
  ${INDENT}
260
- ${INDENT}If the child declares no additional columns, only the model file is generated — no migration is created. STI children share the parent's table, so a no-columns child requires no schema change. Add a migration only by passing positional field:type args, in which case the generator emits one with the appropriate check constraint. STI children cannot declare associations — declare them on the parent model instead. The generator rejects belongs_to columns. STI children never receive @SoftDelete() — soft delete is enforced at the parent level only — and the generator does not accept --no-soft-delete.
272
+ ${INDENT}A migration is generated only when positional field:type args are passed. It ALTERs the parent's table (it does not create a new one), adds a check constraint requiring a value on this child's rows for each column that is not :optional, an array or a boolean, and goes in the migrations folder of the parent model's database connection, which the child shares. A child with no additional columns needs no schema change, so no migration is generated for it. STI children cannot declare associations — declare them on the parent model instead. The generator rejects belongs_to columns. STI children never receive @SoftDelete() — soft delete is enforced at the parent level only — and the generator does not accept --no-soft-delete.
261
273
  ${INDENT}
262
274
  ${INDENT}The parent must already exist (typically generated with g:model --sti-base-serializer or g:resource --sti-base-serializer).
263
275
  ${INDENT}
@@ -271,7 +283,6 @@ ${INDENT}
271
283
  ${INDENT} # Child with no additional columns
272
284
  ${INDENT} pnpm psy g:sti-child --model-name=Kitchen Room/Kitchen extends Room`)
273
285
  .option('--no-serializer', 'skip serializer generation. Useful if the child uses the parent serializer directly or serialization is handled elsewhere')
274
- .option('--connection-name', 'the name of the database connection to use for the model. Only needed for multi-database setups; defaults to "default"', 'default')
275
286
  .option('--model-name <modelName>', `Explicit model class name to use instead of the one auto-derived from the model path. Useful when the path segments don't match the desired class name.
276
287
  ${INDENT}
277
288
  ${INDENT}Example:
@@ -1,6 +1,8 @@
1
1
  import { sql } from 'kysely';
2
+ import defaultEncryptedColumnName from '../../encrypt/defaultEncryptedColumnName.js';
2
3
  import InternalEncrypt from '../../encrypt/InternalEncrypt.js';
3
4
  import DropEnumValueRetypeFailed, { quotedRelationName, } from '../../errors/DropEnumValueRetypeFailed.js';
5
+ import snakeify from '../../helpers/snakeify.js';
4
6
  export default class DreamMigrationHelpers {
5
7
  /**
6
8
  * Rename a table and its associated primary key index and sequence.
@@ -49,8 +51,8 @@ export default class DreamMigrationHelpers {
49
51
  await this.dropConstraint(db, constraintName, { table });
50
52
  await sql `
51
53
  ALTER TABLE ${sql.table(table)}
52
- ADD CONSTRAINT ${sql.table(constraintName)}
53
- UNIQUE (${sql.raw(columns.join(', '))})
54
+ ADD CONSTRAINT ${sql.ref(constraintName)}
55
+ UNIQUE (${sql.join(columns.map(column => sql.ref(column)))})
54
56
  DEFERRABLE INITIALLY DEFERRED;
55
57
  `.execute(db);
56
58
  }
@@ -65,7 +67,7 @@ export default class DreamMigrationHelpers {
65
67
  * @param __namedParameters.value - The name of the value to add to the enum
66
68
  */
67
69
  static async addEnumValue(db, { enumName, value }) {
68
- await sql `ALTER TYPE ${sql.raw(enumName)} ADD VALUE IF NOT EXISTS '${sql.raw(value)}';`.execute(db);
70
+ await sql `ALTER TYPE ${sql.ref(enumName)} ADD VALUE IF NOT EXISTS ${sql.lit(value)};`.execute(db);
69
71
  }
70
72
  /**
71
73
  * Create a database extension.
@@ -104,7 +106,7 @@ export default class DreamMigrationHelpers {
104
106
  */
105
107
  static async createGinIndex(db, indexName, { table, column }) {
106
108
  await sql `
107
- CREATE INDEX IF NOT EXISTS ${sql.raw(indexName)} ON ${sql.raw(table)} USING GIN (${sql.raw(`${column} gin_trgm_ops`)});
109
+ CREATE INDEX IF NOT EXISTS ${sql.ref(indexName)} ON ${sql.table(table)} USING GIN (${sql.ref(column)} gin_trgm_ops);
108
110
  `.execute(db);
109
111
  }
110
112
  /**
@@ -120,7 +122,7 @@ export default class DreamMigrationHelpers {
120
122
  */
121
123
  static async dropConstraint(db, constraintName, { table }) {
122
124
  await sql `
123
- ALTER TABLE ${sql.table(table)} DROP CONSTRAINT IF EXISTS ${sql.table(constraintName)};
125
+ ALTER TABLE ${sql.table(table)} DROP CONSTRAINT IF EXISTS ${sql.ref(constraintName)};
124
126
  `.execute(db);
125
127
  }
126
128
  /**
@@ -262,11 +264,11 @@ export default class DreamMigrationHelpers {
262
264
  * @param options - Configuration options
263
265
  * @param options.table - The name of the table
264
266
  * @param options.column - The current (plaintext) column name
265
- * @param options.encryptedColumnName - The target encrypted column name. Defaults to `encrypted_<column>`, matching the `@Encrypted` decorator's default; pass this when the decorator was given a custom encrypted column name.
267
+ * @param options.encryptedColumnName - The target encrypted column name. Defaults to the column the `@Encrypted` decorator reads for a property named after `column` (`encrypted_phone` for `phone`); pass this when the decorator was given a custom encrypted column name.
266
268
  * @param options.primaryKey - The primary key column used to keyset-paginate and target each row's update. Defaults to `id`.
267
269
  * @param options.batchSize - How many rows to read per batch. Defaults to `1000`.
268
270
  */
269
- static async encryptColumn(db, { table, column, encryptedColumnName = `encrypted_${column}`, primaryKey = 'id', batchSize = 1000, }) {
271
+ static async encryptColumn(db, { table, column, encryptedColumnName = snakeify(defaultEncryptedColumnName(column)), primaryKey = 'id', batchSize = 1000, }) {
270
272
  await db.schema.alterTable(table).renameColumn(column, encryptedColumnName).execute();
271
273
  await db.schema
272
274
  .alterTable(table)
@@ -301,12 +303,12 @@ export default class DreamMigrationHelpers {
301
303
  * @param options - Configuration options
302
304
  * @param options.table - The name of the table
303
305
  * @param options.column - The target (plaintext) column name to rename back to
304
- * @param options.encryptedColumnName - The current encrypted column name. Defaults to `encrypted_<column>`.
306
+ * @param options.encryptedColumnName - The current encrypted column name. Defaults to the column the `@Encrypted` decorator reads for a property named after `column` (`encrypted_phone` for `phone`).
305
307
  * @param options.primaryKey - The primary key column used to keyset-paginate and target each row's update. Defaults to `id`.
306
308
  * @param options.batchSize - How many rows to read per batch. Defaults to `1000`.
307
309
  * @param options.columnType - When provided, the restored column is converted to this type. When omitted, the column is left as `text`.
308
310
  */
309
- static async decryptColumn(db, { table, column, encryptedColumnName = `encrypted_${column}`, primaryKey = 'id', batchSize = 1000, columnType, }) {
311
+ static async decryptColumn(db, { table, column, encryptedColumnName = snakeify(defaultEncryptedColumnName(column)), primaryKey = 'id', batchSize = 1000, columnType, }) {
310
312
  await this.transformColumnInBatches(db, { table, column: encryptedColumnName, primaryKey, batchSize }, value => InternalEncrypt.decryptColumn(value));
311
313
  await db.schema.alterTable(table).renameColumn(encryptedColumnName, column).execute();
312
314
  if (columnType !== undefined) {
@@ -452,7 +454,7 @@ function constraintContextFor(byPosition, position) {
452
454
  return byPosition[position] ?? { relationResolved: false, checkConstraints: [] };
453
455
  }
454
456
  async function getEnumValues(db, enumName) {
455
- const response = await sql `SELECT unnest(enum_range(NULL::${sql.raw(enumName)}))`.execute(db);
457
+ const response = await sql `SELECT unnest(enum_range(NULL::${sql.ref(enumName)}))`.execute(db);
456
458
  return response.rows.map(row => row.unnest);
457
459
  }
458
460
  // finds any records in the specified table
@@ -466,10 +468,10 @@ async function replaceArrayValues(db, value, tableAndColumnToChange) {
466
468
  .updateTable(table)
467
469
  .set({
468
470
  [column]: tableAndColumnToChange.behavior === 'remove'
469
- ? sql.raw(`array_remove(${column}, '${value}')`)
470
- : sql.raw(`array_replace(${column}, '${value}', '${tableAndColumnToChange.replaceWith}')`),
471
+ ? sql `array_remove(${sql.ref(column)}, ${value})`
472
+ : sql `array_replace(${sql.ref(column)}, ${value}, ${tableAndColumnToChange.replaceWith})`,
471
473
  })
472
- .where(sql.raw(`'${value}'`), '=', sql.raw(`ANY(${column})`))
474
+ .where(sql `${value} = ANY(${sql.ref(column)})`)
473
475
  .execute();
474
476
  }
475
477
  // finds any records in the specified table
@@ -489,20 +491,20 @@ async function replaceNonArrayValues(db, value, tableAndColumnToChange) {
489
491
  async function updateTableColumnToNewEnumArrayType(db, enumName, tableAndColumnToChange) {
490
492
  const { table, column } = tableAndColumnToChange;
491
493
  await sql `
492
- ALTER TABLE ${sql.raw(table)}
493
- ALTER ${sql.raw(column)}
494
- TYPE ${sql.raw(enumName)}[]
495
- USING ${sql.raw(column)}::${sql.raw(enumName)}[];
494
+ ALTER TABLE ${sql.table(table)}
495
+ ALTER COLUMN ${sql.ref(column)}
496
+ TYPE ${sql.ref(enumName)}[]
497
+ USING ${sql.ref(column)}::${sql.ref(enumName)}[];
496
498
  `.execute(db);
497
499
  }
498
500
  // updates the table's targeted column to the newly-created enum
499
501
  async function updateTableColumnToNewEnumType(db, enumName, tableAndColumnToChange) {
500
502
  const { table, column } = tableAndColumnToChange;
501
503
  await sql `
502
- ALTER TABLE ${sql.raw(table)}
503
- ALTER ${sql.raw(column)}
504
- TYPE ${sql.raw(enumName)}
505
- USING ${sql.raw(column)}::${sql.raw(enumName)};
504
+ ALTER TABLE ${sql.table(table)}
505
+ ALTER COLUMN ${sql.ref(column)}
506
+ TYPE ${sql.ref(enumName)}
507
+ USING ${sql.ref(column)}::${sql.ref(enumName)};
506
508
  `.execute(db);
507
509
  }
508
510
  function computedTemporaryType(array) {
@@ -115,16 +115,38 @@ export default class Decorators {
115
115
  *
116
116
  * // automatically sets `myEncryptedPhone` to the encrypted value that
117
117
  * // `phone` is set to new/create/update, e.g., `await user.update({ phone })`
118
- * @deco.Encrypted('myEncryptedPhone)
118
+ * @deco.Encrypted('myEncryptedPhone')
119
119
  * public phone: string
120
+ *
121
+ * // automatically sets `myEncryptedMfaSettings` to the encrypted value that
122
+ * // `mfaSettings` is set to in new/create/update, and declares the
123
+ * // value's OpenAPI shape, since it is not a string
124
+ * @deco.Encrypted({
125
+ * column: 'myEncryptedMfaSettings',
126
+ * openapi: { type: ['object', 'null'], properties: { token: 'string' } },
127
+ * })
128
+ * public mfaSettings: { token: string } | null
120
129
  * }
121
130
  * ```
122
131
  *
123
- * @param column — if omitted, then 'encrypted' is prepended to the Pascal cased version of the decorated field
132
+ * The decorated property may hold any JSON-serializable value. Its OpenAPI
133
+ * shape is what Psychic uses to cast the property in params and to render
134
+ * it in request body and serializer OpenAPI. Without an `openapi` option,
135
+ * that shape is `'string'`, or `['string', 'null']` when the backing column
136
+ * allows null. Pass `openapi` when the property holds a non-string value,
137
+ * such as an object, a number, or an array; it accepts any shape
138
+ * `@deco.Virtual` accepts. The declared shape is used exactly as given and
139
+ * replaces the nullability inferred from the backing column, so when the
140
+ * backing column allows null, include `'null'` in the shape, e.g.
141
+ * `type: ['object', 'null']`.
142
+ *
143
+ * @param columnOrOptions - Either the backing column's name, or an options object:
144
+ * - `column` — the backing column's name; if omitted, then 'encrypted' is prepended to the Pascal cased version of the decorated field
145
+ * - `openapi` — the OpenAPI shape of the decorated property's value; if omitted, the property's shape is a string (see above)
124
146
  * @returns An Encrypted decorator
125
147
  */
126
- Encrypted(column) {
127
- return Encrypted(column);
148
+ Encrypted(columnOrOptions) {
149
+ return Encrypted(columnOrOptions);
128
150
  }
129
151
  /**
130
152
  * The Scope decorator decorates a static method that accepts
@@ -1,11 +1,18 @@
1
+ import DreamApp from '../../dream-app/index.js';
2
+ import defaultEncryptedColumnName from '../../encrypt/defaultEncryptedColumnName.js';
1
3
  import InternalEncrypt from '../../encrypt/InternalEncrypt.js';
2
4
  import DoNotSetEncryptedFieldsDirectly from '../../errors/DoNotSetEncryptedFieldsDirectly.js';
3
5
  import columnAllowsNull from '../../helpers/db/columnAllowsNull.js';
4
- import pascalize from '../../helpers/pascalize.js';
5
- export default function Encrypted(encryptedColumnName) {
6
+ export default function Encrypted(columnOrOptions) {
7
+ const { column, openapi } = typeof columnOrOptions === 'string' ? { column: columnOrOptions } : (columnOrOptions ?? {});
6
8
  return function (_, context) {
7
9
  const key = context.name;
8
- const encryptedKey = encryptedColumnName || `encrypted${pascalize(key)}`;
10
+ const encryptedKey = column || defaultEncryptedColumnName(key);
11
+ // records whose legacy-key listeners for this property are running; a
12
+ // read of the property on one of them returns its value without
13
+ // dispatching the event again, so a listener that reads the property
14
+ // does not recurse
15
+ const recordsDispatchingLegacyKeyUsed = new WeakSet();
9
16
  context.addInitializer(function () {
10
17
  const dreamClass = this.constructor;
11
18
  if (!dreamClass['globallyInitializingDecorators']) {
@@ -30,7 +37,7 @@ export default function Encrypted(encryptedColumnName) {
30
37
  ;
31
38
  dreamClass['virtualAttributes'].push({
32
39
  property: key,
33
- type: columnAllowsNull(dreamClass, encryptedKey) ? ['string', 'null'] : 'string',
40
+ type: openapi ?? (columnAllowsNull(dreamClass, encryptedKey) ? ['string', 'null'] : 'string'),
34
41
  });
35
42
  if (!Object.getOwnPropertyDescriptor(dreamClass, 'explicitUnsafeParamColumns')) {
36
43
  // This pattern allows `explicitUnsafeParamColumns` on a base STI class and on
@@ -56,7 +63,22 @@ export default function Encrypted(encryptedColumnName) {
56
63
  const dreamPrototype = Object.getPrototypeOf(this);
57
64
  Object.defineProperty(dreamPrototype, key, {
58
65
  get() {
59
- return InternalEncrypt.decryptColumn(this.getAttribute(encryptedKey));
66
+ const dream = this;
67
+ return InternalEncrypt.decryptColumn(this.getAttribute(encryptedKey), {
68
+ onLegacyKeyUsed: () => {
69
+ if (recordsDispatchingLegacyKeyUsed.has(dream))
70
+ return;
71
+ recordsDispatchingLegacyKeyUsed.add(dream);
72
+ try {
73
+ DreamApp.getOrFail().specialHooks.encryptionLegacyKeyUsed.forEach(fn => {
74
+ fn({ dream, property: key, encryptedColumnName: encryptedKey });
75
+ });
76
+ }
77
+ finally {
78
+ recordsDispatchingLegacyKeyUsed.delete(dream);
79
+ }
80
+ },
81
+ });
60
82
  },
61
83
  set(val) {
62
84
  /**
@@ -74,7 +96,9 @@ export default function Encrypted(encryptedColumnName) {
74
96
  */
75
97
  if (this.columnSetterGuardActivated)
76
98
  return;
77
- this.setAttribute(encryptedKey, InternalEncrypt.encryptColumn(val));
99
+ // the value was just encrypted, so it bypasses setAttribute's check
100
+ // that a backing column receives only decryptable ciphertext
101
+ this.setAttributeUnchecked(encryptedKey, InternalEncrypt.encryptColumn(val));
78
102
  },
79
103
  configurable: false,
80
104
  });
@@ -498,12 +498,12 @@ export default class DreamClassTransactionBuilder {
498
498
  * 5. associations loading associations loading associations could result in exponential amounts of data; in those cases, `.preload(...).findEach(...)` avoids instantiating massive amounts of data at once
499
499
  *
500
500
  * ```ts
501
- * const user = await User.txn(txn).leftJoinPreload('posts', 'comments', { visibilty: 'public' }, 'replies').first()
501
+ * const user = await User.txn(txn).leftJoinPreload('posts', 'comments', { and: { visibility: 'public' } }, 'replies').first()
502
502
  * console.log(user.posts[0].comments[0].replies)
503
503
  * // [Reply{id: 1}, Reply{id: 2}]
504
504
  * ```
505
505
  *
506
- * @param args - A chain of association names and where clauses
506
+ * @param args - A chain of association names and and/andNot/andAny clauses
507
507
  * @returns A query for this model with the include statement applied
508
508
  */
509
509
  leftJoinPreload(...args) {
@@ -521,13 +521,13 @@ export default class DreamClassTransactionBuilder {
521
521
  *
522
522
  * ```ts
523
523
  * await ApplicationModel.transaction(async txn => {
524
- * const user = await User.txn(txn).preload('posts', 'comments', { visibilty: 'public' }, 'replies').first()
524
+ * const user = await User.txn(txn).preload('posts', 'comments', { and: { visibility: 'public' } }, 'replies').first()
525
525
  * console.log(user.posts[0].comments[0].replies)
526
526
  * // [Reply{id: 1}, Reply{id: 2}]
527
527
  * })
528
528
  * ```
529
529
  *
530
- * @param args - A chain of association names and where clauses
530
+ * @param args - A chain of association names and and/andNot/andAny clauses
531
531
  * @returns A query for this model with the preload statement applied
532
532
  */
533
533
  preload(...args) {
@@ -591,7 +591,7 @@ export default class DreamClassTransactionBuilder {
591
591
  * })
592
592
  * ```
593
593
  *
594
- * @param args - A chain of association names and where clauses
594
+ * @param args - A chain of association names and and/andNot/andAny clauses
595
595
  * @returns A Query for this model with the inner join clause applied
596
596
  */
597
597
  innerJoin(...args) {
@@ -607,7 +607,7 @@ export default class DreamClassTransactionBuilder {
607
607
  * })
608
608
  * ```
609
609
  *
610
- * @param args - A chain of association names and where clauses
610
+ * @param args - A chain of association names and and/andNot/andAny clauses
611
611
  * @returns A Query for this model with the left join clause applied
612
612
  */
613
613
  leftJoin(...args) {