@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
package/README.md CHANGED
@@ -261,11 +261,11 @@ const userIds = await User.where({ name: ops.similarity('steeven hawkins') }).pl
261
261
  const min = await User.where({ name: ops.similarity('steeven hawkins') }).min('id')
262
262
  const max = await User.where({ name: ops.similarity('steeven hawkins') }).max('id')
263
263
  const count = await User.where({ name: ops.similarity('steeven hawkins') }).count()
264
- const petIds = await User.innerJoin('pets', { name: ops.similarity('fido') }).pluck('pets.id')
264
+ const petIds = await User.innerJoin('pets', { and: { name: ops.similarity('fido') } }).pluck('pets.id')
265
265
  const numUpdatedRecords = await User.where({ name: ops.similarity('steeven hawkins') }).update({
266
266
  name: 'Stephen Hawking',
267
267
  })
268
- const users = await User.innerJoin('pets', { name: ops.similarity('fido') }).all()
268
+ const users = await User.innerJoin('pets', { and: { name: ops.similarity('fido') } }).all()
269
269
 
270
270
  const user = await User.first()
271
271
  const pets = await user
@@ -8,6 +8,7 @@ import DreamApp from './dream-app/index.js';
8
8
  import { RECURSIVE_SERIALIZATION_MAX_REPEATS } from './dream/constants.js';
9
9
  import DreamClassTransactionBuilder from './dream/DreamClassTransactionBuilder.js';
10
10
  import DreamInstanceTransactionBuilder from './dream/DreamInstanceTransactionBuilder.js';
11
+ import assertRecognizedAssociationConditionKeys, { DESTROY_ASSOCIATION_OPTION_KEYS, UPDATE_ASSOCIATION_OPTION_KEYS, } from './dream/internal/associations/assertRecognizedAssociationConditionKeys.js';
11
12
  import associationQuery from './dream/internal/associations/associationQuery.js';
12
13
  import associationUpdateQuery from './dream/internal/associations/associationUpdateQuery.js';
13
14
  import createAssociation from './dream/internal/associations/createAssociation.js';
@@ -30,6 +31,7 @@ import updateOrCreateBy from './dream/internal/updateOrCreateBy.js';
30
31
  import LeftJoinLoadBuilder from './dream/LeftJoinLoadBuilder.js';
31
32
  import LoadBuilder from './dream/LoadBuilder.js';
32
33
  import Query from './dream/Query.js';
34
+ import InternalEncrypt from './encrypt/InternalEncrypt.js';
33
35
  import CannotAssociationQueryOnUnpersistedDream from './errors/associations/CannotAssociationQueryOnUnpersistedDream.js';
34
36
  import CannotCreateAssociationOnUnpersistedDream from './errors/associations/CannotCreateAssociationOnUnpersistedDream.js';
35
37
  import CannotDestroyAssociationOnUnpersistedDream from './errors/associations/CannotDestroyAssociationOnUnpersistedDream.js';
@@ -40,6 +42,7 @@ import CannotCallUndestroyOnANonSoftDeleteModel from './errors/CannotCallUndestr
40
42
  import ConstructorOnlyForInternalUse from './errors/ConstructorOnlyForInternalUse.js';
41
43
  import CreateOrFindByFailedToCreateAndFind from './errors/CreateOrFindByFailedToCreateAndFind.js';
42
44
  import CreateOrUpdateByFailedToCreateAndUpdate from './errors/CreateOrUpdateByFailedToCreateAndUpdate.js';
45
+ import DoNotSetEncryptedFieldsDirectly from './errors/DoNotSetEncryptedFieldsDirectly.js';
43
46
  import GlobalNameNotSet from './errors/dream-app/GlobalNameNotSet.js';
44
47
  import DreamMissingRequiredOverride from './errors/DreamMissingRequiredOverride.js';
45
48
  import NonExistentScopeProvidedToResort from './errors/NonExistentScopeProvidedToResort.js';
@@ -1429,6 +1432,67 @@ export default class Dream {
1429
1432
  static async findEach(cb, opts) {
1430
1433
  await this.query().findEach(cb, opts);
1431
1434
  }
1435
+ /**
1436
+ * Encrypts every value of the model's `@deco.Encrypted` properties again
1437
+ * with the current column encryption key, so that reading them no longer
1438
+ * needs the `legacy` key. It is a step of a key rotation (see
1439
+ * `Encrypt.generateKey`): run it in each environment, once that
1440
+ * environment has deployed its rotated keys, from a console or a script,
1441
+ * not from a migration, whose result would then depend on the keys a given
1442
+ * environment has configured when the migration runs.
1443
+ *
1444
+ * ```ts
1445
+ * await User.reencryptEncryptedColumns()
1446
+ * ```
1447
+ *
1448
+ * It walks every row of the model's table, including rows its default
1449
+ * scopes (such as soft delete) hide and an STI model's siblings, in batches in primary key order, and opens
1450
+ * each non-null value with the current key or, failing that, the legacy
1451
+ * one (see `Encrypt.reencrypt`). Each row is written on its own, with
1452
+ * no model hooks and without changing `updatedAt`, and only while the
1453
+ * column still holds the value read, so a row an app saves in between
1454
+ * keeps the newer value. Reading values here does not fire
1455
+ * `dreamApp.on('encryption:legacy-key-used', ...)`.
1456
+ *
1457
+ * **Run it only once every server encrypts with the new key.** It walks
1458
+ * the table once, so a row a server still configured with the old
1459
+ * `current` writes behind it stays encrypted with the old key, and so does
1460
+ * old-key ciphertext later written straight into a backing column
1461
+ * (`setAttribute`, `setAttributes` and `updateAttributes` accept, for a
1462
+ * backing column, ciphertext that either key opens). Once no writer uses
1463
+ * the old key and this has run for every model with encrypted properties,
1464
+ * `legacy` is safe to drop.
1465
+ *
1466
+ * **A value neither key opens stops it**, throwing as reading the property
1467
+ * would (`DecryptionRotationError` while a `legacy` key is configured,
1468
+ * `DecryptionError` without one). The rows rewritten before it stay
1469
+ * rewritten; running it again once the value is fixed rewrites them again
1470
+ * harmlessly.
1471
+ *
1472
+ * @param opts - Optional parameters
1473
+ * @param opts.batchSize - how many rows to read per batch. Defaults to 1000
1474
+ */
1475
+ static async reencryptEncryptedColumns(opts) {
1476
+ const encryptedColumnNames = this.encryptedAttributes.map(attribute => attribute.encryptedColumnName);
1477
+ if (!encryptedColumnNames.length)
1478
+ return;
1479
+ const tableClass = this.stiBaseClassOrOwnClass;
1480
+ await tableClass.removeAllDefaultScopes().findEach(async (record) => {
1481
+ for (const columnName of encryptedColumnNames) {
1482
+ const ciphertext = record.getAttribute(columnName);
1483
+ if (ciphertext === null || ciphertext === undefined)
1484
+ continue;
1485
+ // written through Kysely, since Query#update refuses any value for a
1486
+ // backing column, ciphertext included
1487
+ await tableClass
1488
+ .removeAllDefaultScopes()
1489
+ .where({ [tableClass.primaryKey]: record.primaryKeyValue(), [columnName]: ciphertext })
1490
+ .toKysely('update')
1491
+ .set({ [columnName]: InternalEncrypt.reencryptColumn(ciphertext) })
1492
+ .execute();
1493
+ }
1494
+ }, opts);
1495
+ }
1432
1496
  /**
1433
1497
  * Returns a new instance of Query scoped to the given
1434
1498
  * model class. Especially useful with {@link Query#toKysely}.
@@ -1560,12 +1624,12 @@ export default class Dream {
1560
1624
  * planned drop safe.
1561
1625
  *
1562
1626
  * ```ts
1563
- * const user = await User.leftJoinPreload('posts', 'comments', { visibilty: 'public' }, 'replies').first()
1627
+ * const user = await User.leftJoinPreload('posts', 'comments', { and: { visibility: 'public' } }, 'replies').first()
1564
1628
  * console.log(user.posts[0].comments[0].replies)
1565
1629
  * // [Reply{id: 1}, Reply{id: 2}]
1566
1630
  * ```
1567
1631
  *
1568
- * @param args - A chain of association names and where clauses
1632
+ * @param args - A chain of association names and and/andNot/andAny clauses
1569
1633
  * @returns A query for this model with the include statement applied
1570
1634
  */
1571
1635
  static leftJoinPreload(...args) {
@@ -1582,12 +1646,12 @@ export default class Dream {
1582
1646
  * Preload is useful for avoiding the N+1 query problem
1583
1647
  *
1584
1648
  * ```ts
1585
- * const user = await User.preload('posts', 'comments', { visibilty: 'public' }, 'replies').first()
1649
+ * const user = await User.preload('posts', 'comments', { and: { visibility: 'public' } }, 'replies').first()
1586
1650
  * console.log(user.posts[0].comments[0].replies)
1587
1651
  * // [Reply{id: 1}, Reply{id: 2}]
1588
1652
  * ```
1589
1653
  *
1590
- * @param args - A chain of association names and where clauses
1654
+ * @param args - A chain of association names and and/andNot/andAny clauses
1591
1655
  * @returns A query for this model with the preload statement applied
1592
1656
  */
1593
1657
  static preload(...args) {
@@ -1647,7 +1711,7 @@ export default class Dream {
1647
1711
  * await User.innerJoin('posts').first()
1648
1712
  * ```
1649
1713
  *
1650
- * @param args - A chain of association names and where clauses
1714
+ * @param args - A chain of association names and and/andNot/andAny clauses
1651
1715
  * @returns A Query for this model with the inner join clause applied
1652
1716
  */
1653
1717
  static innerJoin(...args) {
@@ -1661,7 +1725,7 @@ export default class Dream {
1661
1725
  * await user.innerJoin('posts').first()
1662
1726
  * ```
1663
1727
  *
1664
- * @param args - A chain of association names and where clauses
1728
+ * @param args - A chain of association names and and/andNot/andAny clauses
1665
1729
  * @returns A Query for this model with the inner join clause applied
1666
1730
  */
1667
1731
  innerJoin(...args) {
@@ -1675,7 +1739,7 @@ export default class Dream {
1675
1739
  * await User.leftJoin('posts').first()
1676
1740
  * ```
1677
1741
  *
1678
- * @param args - A chain of association names and where clauses
1742
+ * @param args - A chain of association names and and/andNot/andAny clauses
1679
1743
  * @returns A Query for this model with the left join clause applied
1680
1744
  */
1681
1745
  static leftJoin(...args) {
@@ -1689,7 +1753,7 @@ export default class Dream {
1689
1753
  * await user.leftJoin('posts').first()
1690
1754
  * ```
1691
1755
  *
1692
- * @param args - A chain of association names and where clauses
1756
+ * @param args - A chain of association names and and/andNot/andAny clauses
1693
1757
  * @returns A Query for this model with the left join clause applied
1694
1758
  */
1695
1759
  leftJoin(...args) {
@@ -2302,8 +2366,8 @@ export default class Dream {
2302
2366
  /**
2303
2367
  * Returns true if any of the attributes on the instance
2304
2368
  * have changed since it was last pulled from the database.
2305
- * It will also return true if the instance is not yet
2306
- * persisted.
2369
+ * On an unpersisted instance, it returns true when at least
2370
+ * one column has a value other than `undefined`.
2307
2371
  *
2308
2372
  * ```ts
2309
2373
  * const post = Post.new({ body: 'howyadoin' })
@@ -2589,6 +2653,15 @@ export default class Dream {
2589
2653
  * const user = User.new({ email: 'how@yadoin' })
2590
2654
  * ```
2591
2655
  *
2656
+ * @param opts - the attributes to set on the new instance
2657
+ * @param additionalOpts - optional parameters
2658
+ * @param additionalOpts.bypassUserDefinedSetters - if true, sets the attributes
2659
+ * as {@link Dream.setAttributes | setAttributes} does, bypassing any
2660
+ * custom-defined setters. An encrypted property (e.g. `secret`) is still
2661
+ * encrypted, and an `@Encrypted` backing column (e.g. `encryptedSecret`)
2662
+ * accepts only ciphertext that decrypts with this app's column encryption
2663
+ * keys, or `null`; anything else throws (see
2664
+ * {@link Dream.setAttribute | setAttribute}). Defaults to false
2592
2665
  * @returns A new (unpersisted) instance of the provided dream class
2593
2666
  */
2594
2667
  static new(opts, additionalOpts = {}) {
@@ -2658,7 +2731,7 @@ export default class Dream {
2658
2731
  *
2659
2732
  * Used for determining which attributes to update
2660
2733
  */
2661
- static extractAttributesFromUpdateableProperties(attributes, dreamInstance, { bypassUserDefinedSetters = false } = {}) {
2734
+ static extractAttributesFromUpdateableProperties(attributes, dreamInstance, { bypassUserDefinedSetters = false, fromDatabase = false } = {}) {
2662
2735
  const returnValues = {};
2663
2736
  const setAttributeOnDreamInstance = (attr, value) => {
2664
2737
  if (!dreamInstance)
@@ -2672,8 +2745,13 @@ export default class Dream {
2672
2745
  }
2673
2746
  else if (bypassUserDefinedSetters && !isJsonColumn(this, attr)) {
2674
2747
  // bypass user defined setters, and this field is not json, so set
2675
- // the attribute directly and return the attribute
2676
- dreamInstance.setAttribute(attr, value);
2748
+ // the attribute directly and return the attribute. A row read from
2749
+ // the database already holds the ciphertext Dream stored, so it skips
2750
+ // setAttribute's check of @Encrypted backing columns
2751
+ if (fromDatabase)
2752
+ dreamInstance['setAttributeUnchecked'](attr, value);
2753
+ else
2754
+ dreamInstance.setAttribute(attr, value);
2677
2755
  return dreamInstance.getAttribute(attr);
2678
2756
  }
2679
2757
  else {
@@ -2903,13 +2981,47 @@ export default class Dream {
2903
2981
  * bypassing any custom-defined setters. If you would like to set attributes
2904
2982
  * without bypassing custom-defined setters, use #assignAttribute instead
2905
2983
  *
2984
+ * An `@Encrypted` backing column (e.g. `encryptedSecret`) accepts only
2985
+ * a string of ciphertext that decrypts with this app's column encryption
2986
+ * keys (current or legacy), or `null`, which clears it. Anything else,
2987
+ * including plaintext, `undefined`, a non-string value, and ciphertext
2988
+ * made with a key this app does not hold, throws and leaves the column
2989
+ * unchanged. The check covers the backing columns of this model's class,
2990
+ * including those it inherits from an STI parent; a backing column
2991
+ * declared only by another class in the same STI hierarchy is not
2992
+ * checked.
2993
+ *
2994
+ * Given the name of an encrypted property (e.g. `secret`), setAttribute
2995
+ * neither encrypts the value nor stores it: it keeps the value,
2996
+ * unencrypted, under that name in this instance's attributes, which a
2997
+ * save does not write, while the backing column and the property keep
2998
+ * the value they had. To store a new value, assign the property
2999
+ * (`user.secret = 'new secret'`) or pass it to
3000
+ * {@link Dream.setAttributes | setAttributes}, each of which encrypts it.
3001
+ *
2906
3002
  * ```ts
2907
3003
  * const user = new User()
2908
3004
  * user.setAttribute('email', 'sally@gmail.com')
2909
3005
  * ```
2910
3006
  */
2911
3007
  setAttribute(column, val) {
2912
- ;
3008
+ const dreamClass = this.constructor;
3009
+ const encryptedAttribute = dreamClass.encryptedAttributes.find(({ encryptedColumnName }) => encryptedColumnName === column);
3010
+ if (encryptedAttribute &&
3011
+ val !== null &&
3012
+ !(typeof val === 'string' && InternalEncrypt.isDecryptableColumnCiphertext(val)))
3013
+ throw new DoNotSetEncryptedFieldsDirectly(dreamClass, column, encryptedAttribute.property);
3014
+ this.setAttributeUnchecked(column, val);
3015
+ }
3016
+ /**
3017
+ * @internal
3018
+ *
3019
+ * Writes the value into the current attributes as given, with none of
3020
+ * setAttribute's checks. Only for values Dream already trusts: a row read
3021
+ * from the database, and the ciphertext the @Encrypted property setter
3022
+ * has just produced. Every caller-supplied value goes through setAttribute.
3023
+ */
3024
+ setAttributeUnchecked(column, val) {
2913
3025
  this.currentAttributes[column] = val;
2914
3026
  }
2915
3027
  /**
@@ -2946,9 +3058,8 @@ export default class Dream {
2946
3058
  * being persisted to the database, with the values
2947
3059
  * that were last persisted to the database.
2948
3060
  *
2949
- * On a record that has not been persisted, every
2950
- * column counts as changed: the object has a key
2951
- * for every column.
3061
+ * On a record that has not been persisted, only columns
3062
+ * with values other than `undefined` count as changed.
2952
3063
  *
2953
3064
  * ```ts
2954
3065
  * const user = User.new({ email: 'original@email', password: 'howyadoin' })
@@ -2978,43 +3089,55 @@ export default class Dream {
2978
3089
  * changed since last persisting, along with their current
2979
3090
  * and previously persisted values.
2980
3091
  *
3092
+ * For a column with an unsaved change, `was` is its value
3093
+ * as of the most recent save. For every other column, `was`
3094
+ * is its value from before that save, so the changes the
3095
+ * save made keep appearing, beside any later unsaved change,
3096
+ * until the next save.
3097
+ *
3098
+ * On a create, that earlier value is `undefined` for every
3099
+ * column of an instance from `new` or `create`, assigned or
3100
+ * not, and the copied value for an instance from
3101
+ * {@link Dream.dup | dup}. So after a create from `new` or
3102
+ * `create`, every column the insert returned counts as
3103
+ * changed: the primary key, timestamps, database defaults,
3104
+ * and `null` for each nullable column nobody assigned. After
3105
+ * a create from `dup`, the columns `dup` cleared (the primary
3106
+ * key, timestamps and sortable positions) count, and a copied
3107
+ * column counts only if the insert returned a different value.
3108
+ *
2981
3109
  * ```ts
2982
3110
  * const pet = Pet.new({ species: 'dog' })
2983
3111
  * pet.changes()
2984
3112
  * // {
2985
- * // species: {
2986
- * // was: undefined,
2987
- * // now: 'dog',
2988
- * // }
3113
+ * // species: { was: undefined, now: 'dog' },
2989
3114
  * // }
2990
3115
  *
2991
3116
  * await pet.save()
2992
3117
  * pet.changes()
2993
3118
  * // {
2994
- * // species: {
2995
- * // was: undefined,
2996
- * // now: 'dog',
2997
- * // }
3119
+ * // id: { was: undefined, now: 1 },
3120
+ * // species: { was: undefined, now: 'dog' },
3121
+ * // name: { was: undefined, now: null },
3122
+ * // ...every other column the insert returned
2998
3123
  * // }
2999
3124
  *
3000
3125
  * pet.species = 'cat'
3001
3126
  * pet.species = 'frog'
3002
3127
  * pet.changes()
3003
3128
  * // {
3004
- * // species: {
3005
- * // was: 'dog',
3006
- * // now: 'frog',
3007
- * // }
3129
+ * // id: { was: undefined, now: 1 },
3130
+ * // species: { was: 'dog', now: 'frog' },
3131
+ * // name: { was: undefined, now: null },
3132
+ * // ...every other change the create made
3008
3133
  * // }
3009
3134
  *
3010
3135
  * await pet.save()
3011
3136
  * pet.changes()
3012
3137
  * // {
3013
- * // species: {
3014
- * // was: 'dog',
3015
- * // now: 'frog',
3016
- * // }
3138
+ * // species: { was: 'dog', now: 'frog' },
3017
3139
  * // }
3140
+ * // (and `updatedAt`, on a model with that column)
3018
3141
  * ```
3019
3142
  *
3020
3143
  * @returns An object containing changed attributes
@@ -3030,8 +3153,22 @@ export default class Dream {
3030
3153
  return obj;
3031
3154
  }
3032
3155
  /**
3033
- * Returns the value most recently persisted
3034
- * to the database.
3156
+ * Returns the value the column held before the most
3157
+ * recent save. For a column with an unsaved change, it
3158
+ * returns the column's value as of that save instead.
3159
+ *
3160
+ * On a create, the value before the save is `undefined`
3161
+ * for every column of an instance from `new` or `create`,
3162
+ * assigned or not, and the copied value for an instance
3163
+ * from {@link Dream.dup | dup} (`undefined` for the
3164
+ * columns `dup` cleared). So after a create from `new` or
3165
+ * `create`, this returns `undefined` for every column
3166
+ * without an unsaved change, not the value the insert
3167
+ * wrote. After an update, it returns the column's value
3168
+ * from before that update, which for a column the update
3169
+ * did not change is its current value. On a record loaded
3170
+ * from the database and not saved since, it returns the
3171
+ * loaded value.
3035
3172
  *
3036
3173
  * ```ts
3037
3174
  * const pet = Pet.new({ species: 'cat' })
@@ -3066,24 +3203,54 @@ export default class Dream {
3066
3203
  /**
3067
3204
  * Returns true if the columnName provided has
3068
3205
  * changes that were persisted during the most
3069
- * recent save.
3206
+ * recent save. An unsaved change does not count,
3207
+ * and an unsaved edit made after that save does
3208
+ * not undo a change the save made. On a record
3209
+ * that has not been persisted, this returns false.
3210
+ *
3211
+ * A create compares the value the insert returned
3212
+ * with `undefined` for every column of an instance
3213
+ * from `new` or `create`, assigned or not, and with
3214
+ * the copied value for an instance from
3215
+ * {@link Dream.dup | dup}. So after a create from
3216
+ * `new` or `create`, this returns true for every
3217
+ * column the insert returned: the primary key,
3218
+ * timestamps, database defaults, and a nullable
3219
+ * column nobody assigned, which comes back `null`.
3220
+ * To react only to the columns a create assigned,
3221
+ * use the `ifChanged` option of an after hook.
3222
+ *
3223
+ * ```ts
3224
+ * const pet = await Pet.create({ species: 'cat' })
3225
+ * pet.savedChangeToAttribute('species')
3226
+ * // true
3227
+ * pet.savedChangeToAttribute('name')
3228
+ * // true (unassigned, so the insert returned null)
3229
+ *
3230
+ * await pet.update({ name: 'Snoopy' })
3231
+ * pet.savedChangeToAttribute('species')
3232
+ * // false
3233
+ *
3234
+ * pet.species = 'dog'
3235
+ * pet.savedChangeToAttribute('species')
3236
+ * // false (not saved yet)
3237
+ * ```
3070
3238
  *
3071
3239
  * @param columnName - the column name to check
3072
3240
  * @returns A boolean
3073
3241
  */
3074
3242
  savedChangeToAttribute(columnName) {
3075
- const changes = this.changes();
3076
- const was = changes?.[columnName]?.was;
3077
- const now = changes?.[columnName]?.now;
3078
- return this.isPersisted && notEqual(now, was);
3243
+ if (!this.isPersisted)
3244
+ return false;
3245
+ return notEqual(this.attributesFromBeforeLastSave[columnName], this.frozenAttributes[columnName]);
3079
3246
  }
3080
3247
  /**
3081
3248
  * Returns true if the columnName provided has
3082
3249
  * changes that have not yet been persisted.
3083
3250
  *
3084
- * On a record that has not been persisted, every
3085
- * column counts as changed, so this returns true
3086
- * for every column, whether or not it was assigned.
3251
+ * On a record that has not been persisted, this returns
3252
+ * true only for a column with a value other than `undefined`.
3253
+ * An explicit `null` counts as a change.
3087
3254
  *
3088
3255
  * @param attribute - the column name to check
3089
3256
  * @returns A boolean
@@ -3104,15 +3271,14 @@ export default class Dream {
3104
3271
  * of columns that have changed since last persist,
3105
3272
  * and their current values.
3106
3273
  *
3107
- * On a record that has not been persisted, every
3108
- * column counts as changed: the object has a key
3109
- * for every column, including those never assigned,
3110
- * whose value is `undefined`.
3274
+ * On a record that has not been persisted, the object
3275
+ * contains only columns with values other than `undefined`.
3276
+ * An explicit `null` is included.
3111
3277
  *
3112
3278
  * ```ts
3113
3279
  * const user = User.new({ email: 'hello@world' })
3114
3280
  * user.dirtyAttributes()
3115
- * // { id: undefined, email: 'hello@world', name: undefined, ...every other column: undefined }
3281
+ * // { email: 'hello@world' }
3116
3282
  *
3117
3283
  * await user.save()
3118
3284
  *
@@ -3146,7 +3312,7 @@ export default class Dream {
3146
3312
  */
3147
3313
  attributeIsDirty(attribute) {
3148
3314
  if (this.isNewRecord)
3149
- return true;
3315
+ return this.getAttribute(attribute) !== undefined;
3150
3316
  return !areEqual(this.frozenAttributes[attribute], this.getAttribute(attribute));
3151
3317
  }
3152
3318
  /**
@@ -3308,7 +3474,7 @@ export default class Dream {
3308
3474
  * deleting their corresponding records within the database.
3309
3475
  *
3310
3476
  * ```ts
3311
- * await user.destroyAssociation('posts', { body: 'hello world' })
3477
+ * await user.destroyAssociation('posts', { and: { body: 'hello world' } })
3312
3478
  * ```
3313
3479
  *
3314
3480
  * @param associationName - The name of the association to destroy
@@ -3323,6 +3489,7 @@ export default class Dream {
3323
3489
  * @returns The number of records deleted
3324
3490
  */
3325
3491
  async destroyAssociation(associationName, options) {
3492
+ assertRecognizedAssociationConditionKeys(options, DESTROY_ASSOCIATION_OPTION_KEYS, associationName, 'destroyAssociation');
3326
3493
  if (this.isNewRecord)
3327
3494
  throw new CannotDestroyAssociationOnUnpersistedDream(this, associationName);
3328
3495
  return await destroyAssociation(this, null, associationName, {
@@ -3344,7 +3511,7 @@ export default class Dream {
3344
3511
  * causing those records to be deleted from the database.
3345
3512
  *
3346
3513
  * ```ts
3347
- * await user.reallyDestroyAssociation('posts', { body: 'hello world' })
3514
+ * await user.reallyDestroyAssociation('posts', { and: { body: 'hello world' } })
3348
3515
  * ```
3349
3516
  *
3350
3517
  * @param associationName - The name of the association to destroy
@@ -3359,6 +3526,7 @@ export default class Dream {
3359
3526
  * @returns The number of records deleted
3360
3527
  */
3361
3528
  async reallyDestroyAssociation(associationName, options) {
3529
+ assertRecognizedAssociationConditionKeys(options, DESTROY_ASSOCIATION_OPTION_KEYS, associationName, 'reallyDestroyAssociation');
3362
3530
  if (this.isNewRecord)
3363
3531
  throw new CannotDestroyAssociationOnUnpersistedDream(this, associationName);
3364
3532
  return await destroyAssociation(this, null, associationName, {
@@ -3377,7 +3545,7 @@ export default class Dream {
3377
3545
  * will also be undeleted.
3378
3546
  *
3379
3547
  * ```ts
3380
- * await user.undestroyAssociation('posts', { body: 'hello world' })
3548
+ * await user.undestroyAssociation('posts', { and: { body: 'hello world' } })
3381
3549
  * ```
3382
3550
  *
3383
3551
  * @param associationName - The name of the association to undestroy
@@ -3392,6 +3560,7 @@ export default class Dream {
3392
3560
  * @returns The number of records undestroyed
3393
3561
  */
3394
3562
  async undestroyAssociation(associationName, options) {
3563
+ assertRecognizedAssociationConditionKeys(options, DESTROY_ASSOCIATION_OPTION_KEYS, associationName, 'undestroyAssociation');
3395
3564
  return await undestroyAssociation(this, null, associationName, {
3396
3565
  ...undestroyOptions(options),
3397
3566
  joinAndStatements: {
@@ -3424,8 +3593,8 @@ export default class Dream {
3424
3593
  }
3425
3594
  /**
3426
3595
  * Updates all records matching the association with
3427
- * the provided attributes. If a where statement is passed,
3428
- * The on statement will be applied to the query
3596
+ * the provided attributes. If an and, andNot or andAny
3597
+ * statement is passed, it will be applied to the query
3429
3598
  * before updating.
3430
3599
  *
3431
3600
  * ```ts
@@ -3437,14 +3606,17 @@ export default class Dream {
3437
3606
  *
3438
3607
  * @param associationName - The name of the association to update
3439
3608
  * @param attributes - The attributes to update on the association
3440
- * @param options - Options for updating the association
3441
- * @param options.and - Optional on statement to apply to query before updating
3442
- * @param options.skipHooks - If true, skips applying model hooks during the update operation. Defaults to false
3443
- * @param options.bypassAllDefaultScopes - If true, bypasses user-removable default scopes when updating the association; an STI child's reserved discriminator remains enforced, including when associations are loaded. Defaults to false
3444
- * @param options.defaultScopesToBypass - An array of default scope names to bypass when updating the association. Defaults to an empty array
3609
+ * @param updateAssociationOptions - Options for updating the association
3610
+ * @param updateAssociationOptions.and - Optional and statement to apply to query before updating
3611
+ * @param updateAssociationOptions.andNot - Optional andNot statement to apply to query before updating
3612
+ * @param updateAssociationOptions.andAny - Optional andAny statement to apply to query before updating
3613
+ * @param updateAssociationOptions.skipHooks - If true, skips applying model hooks during the update operation. Defaults to false
3614
+ * @param updateAssociationOptions.bypassAllDefaultScopes - If true, bypasses user-removable default scopes when updating the association; an STI child's reserved discriminator remains enforced, including when associations are loaded. Defaults to false
3615
+ * @param updateAssociationOptions.defaultScopesToBypass - An array of default scope names to bypass when updating the association. Defaults to an empty array
3445
3616
  * @returns The number of updated records
3446
3617
  */
3447
3618
  async updateAssociation(associationName, attributes, updateAssociationOptions) {
3619
+ assertRecognizedAssociationConditionKeys(updateAssociationOptions, UPDATE_ASSOCIATION_OPTION_KEYS, associationName, 'updateAssociation');
3448
3620
  if (this.isNewRecord)
3449
3621
  throw new CannotUpdateAssociationOnUnpersistedDream(this, associationName);
3450
3622
  return associationUpdateQuery(this, null, associationName, {
@@ -3510,19 +3682,19 @@ export default class Dream {
3510
3682
  * if the association is not already loaded.
3511
3683
  *
3512
3684
  * ```ts
3513
- * const user = await user
3514
- * .load('posts', { body: ops.ilike('%hello world%') }, 'comments', 'replies')
3685
+ * const loadedUser = await user
3686
+ * .load('posts', { and: { body: ops.ilike('%hello world%') } }, 'comments', 'replies')
3515
3687
  * .load('images')
3516
3688
  * .execute()
3517
3689
  *
3518
- * user.posts[0].comments[0].replies[0]
3690
+ * loadedUser.posts[0].comments[0].replies[0]
3519
3691
  * // Reply{}
3520
3692
  *
3521
- * user.images[0]
3693
+ * loadedUser.images[0]
3522
3694
  * // Image{}
3523
3695
  * ```
3524
3696
  *
3525
- * @param args - A list of associations (and optional where clauses) to load
3697
+ * @param args - A chain of association names and and/andNot/andAny clauses
3526
3698
  * @returns A chainable LoadBuilder instance. Call `.execute()` to get the cloned model with associations loaded.
3527
3699
  */
3528
3700
  load(...args) {
@@ -3604,19 +3776,19 @@ export default class Dream {
3604
3776
  * 5. associations loading associations loading associations could result in exponential amounts of data; in those cases, `.load(...).findEach(...)` avoids instantiating massive amounts of data at once
3605
3777
  *
3606
3778
  * ```ts
3607
- * const user = await user
3608
- * .leftJoinLoad('posts', { body: ops.ilike('%hello world%') }, 'comments', 'replies')
3779
+ * const loadedUser = await user
3780
+ * .leftJoinLoad('posts', { and: { body: ops.ilike('%hello world%') } }, 'comments', 'replies')
3609
3781
  * .leftJoinLoad('images')
3610
3782
  * .execute()
3611
3783
  *
3612
- * user.posts[0].comments[0].replies[0]
3784
+ * loadedUser.posts[0].comments[0].replies[0]
3613
3785
  * // Reply{}
3614
3786
  *
3615
- * user.images[0]
3787
+ * loadedUser.images[0]
3616
3788
  * // Image{}
3617
3789
  * ```
3618
3790
  *
3619
- * @param args - A list of associations (and optional where clauses) to load
3791
+ * @param args - A chain of association names and and/andNot/andAny clauses
3620
3792
  * @returns A chainable LeftJoinLoadBuilder instance. Call `.execute()` to get the cloned model with associations loaded.
3621
3793
  */
3622
3794
  leftJoinLoad(...args) {
@@ -3683,6 +3855,13 @@ export default class Dream {
3683
3855
  * Takes the attributes passed in and sets their values internally,
3684
3856
  * bypassing any custom setters defined for these attributes.
3685
3857
  *
3858
+ * An encrypted property (e.g. `secret`) is still encrypted. Its
3859
+ * `@Encrypted` backing column (e.g. `encryptedSecret`) accepts only
3860
+ * ciphertext that decrypts with this app's column encryption keys, or
3861
+ * `null`; anything else throws (see {@link Dream.setAttribute | setAttribute}).
3862
+ * Attributes are set in order, so those before the rejected one have
3863
+ * already been set when it throws.
3864
+ *
3686
3865
  * NOTE:
3687
3866
  * To leverage custom-defined setters, use `#assignAttributes` instead.
3688
3867
  *
@@ -3713,6 +3892,10 @@ export default class Dream {
3713
3892
  * Upon updating an instance, the update timestamp (if defined)
3714
3893
  * will be updated on the model.
3715
3894
  *
3895
+ * A column whose value is `undefined` is skipped, not written: a new
3896
+ * record gets the column's database default, and a persisted record
3897
+ * keeps the value stored for it. Set a column to `null` to clear it.
3898
+ *
3716
3899
  * ```ts
3717
3900
  * const user = User.new({ email: 'how@yadoin' })
3718
3901
  * user.name = 'fred'
@@ -3760,6 +3943,12 @@ export default class Dream {
3760
3943
  * See {@link Dream.save | save} for details on
3761
3944
  * the side effects of saving.
3762
3945
  *
3946
+ * A column whose value is still `undefined` after setters run is
3947
+ * skipped, not written (see {@link Dream.save | save}), with two
3948
+ * `@Encrypted` exceptions: `undefined` for an encrypted property (e.g.
3949
+ * `secret`) is encrypted to `null`, so it clears the column, and
3950
+ * `undefined` for its backing column (e.g. `encryptedSecret`) throws.
3951
+ *
3763
3952
  * NOTE:
3764
3953
  * To bypass custom-defined setters, use {@link Dream.updateAttributes | updateAttributes} instead.
3765
3954
  *
@@ -3787,6 +3976,18 @@ export default class Dream {
3787
3976
  * See {@link Dream.save | save} for details on
3788
3977
  * the side effects of saving.
3789
3978
  *
3979
+ * An encrypted property (e.g. `secret`) is still encrypted. Its
3980
+ * `@Encrypted` backing column (e.g. `encryptedSecret`) accepts only
3981
+ * ciphertext that decrypts with this app's column encryption keys, or
3982
+ * `null`; anything else throws before anything is saved (see
3983
+ * {@link Dream.setAttribute | setAttribute}).
3984
+ *
3985
+ * A column whose value is still `undefined` after setters run is
3986
+ * skipped, not written (see {@link Dream.save | save}), with two
3987
+ * `@Encrypted` exceptions: `undefined` for an encrypted property (e.g.
3988
+ * `secret`) is encrypted to `null`, so it clears the column, and
3989
+ * `undefined` for its backing column (e.g. `encryptedSecret`) throws.
3990
+ *
3790
3991
  * NOTE:
3791
3992
  * To update the values without bypassing any custom-defined
3792
3993
  * setters, use {@link Dream.update | update} instead.