@rvoh/dream 2.35.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 +258 -55
  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 +258 -55
  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 +206 -42
  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 +270 -142
  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
@@ -92,9 +92,10 @@ export default class ObjectSerializerBuilder {
92
92
  * (e.g., delegating `'profile', 'avatarUrl'` with `as: 'avatar'` outputs the value
93
93
  * under `avatar`)
94
94
  * - `default` - Value to use when the target object or its attribute is null/undefined
95
- * - `optional` - Set to `true` to mark the value as nullable in the OpenAPI schema
96
- * (wraps the type in `anyOf: [schema, { type: 'null' }]`). OpenAPI-only — the
97
- * key is still rendered (as `null`)
95
+ * - `optional` - Set to `true` to mark the value as nullable in the OpenAPI schema:
96
+ * Psychic adds `null` to the rendered schema (e.g., `type: 'string'` becomes
97
+ * `type: ['string', 'null']`, and a `$ref` becomes `anyOf: [{ $ref }, { type: 'null' }]`).
98
+ * OpenAPI-only — the key is still rendered (as `null`)
98
99
  * - `precision` - Round decimal values to the specified number of decimal places (0–9)
99
100
  * during rendering; does not affect the OpenAPI shape
100
101
  * - `required` - Set to `false` to omit the key from the rendered output when the
@@ -137,17 +138,23 @@ export default class ObjectSerializerBuilder {
137
138
  * Executes a callback function to generate a custom attribute value.
138
139
  * The `openapi` option is always required since the return type cannot be inferred.
139
140
  *
140
- * Unlike DreamSerializerBuilder's `customAttribute`, this version does not support `as`
141
- * or `precision`.
141
+ * The callback's return value renders as is, so any fallback belongs in the callback.
142
142
  *
143
143
  * @param name - The attribute name for the computed value
144
144
  * @param fn - Callback function that returns the computed value
145
145
  * @param options - Configuration options:
146
146
  * - `openapi` - (required) OpenAPI schema definition for the computed value
147
- * - `default` - Value to use when the callback returns undefined
148
147
  * - `flatten` - When `true`, spreads the returned object's properties directly into the
149
- * parent serialized output instead of nesting them under `name`; the `openapi` option
150
- * should then define each flattened property individually
148
+ * parent serialized output instead of nesting them under `name`. The `openapi` option
149
+ * then describes the whole returned object, not each property, because Psychic adds it
150
+ * to the parent's schema as an `allOf` branch: either an object schema with full
151
+ * property bodies (`type: 'object'`, `properties`, and `required` listing the keys that
152
+ * are always present), or `{ $serializer: SomeSerializer }` when the callback returns
153
+ * what that serializer renders. A serializer ref with `many: true` does not type-check
154
+ * here, since a list has no properties to spread. A `null` or `undefined` return adds no
155
+ * keys. Attributes render in declaration order, so a key the returned object shares with
156
+ * another attribute takes the value of whichever is declared later; under the `allOf`,
157
+ * that value must match both schemas
151
158
  * - `required` - Set to `false` to mark the attribute as optional in the OpenAPI schema;
152
159
  * when omitted, attributes are required by default
153
160
  * @returns The serializer builder for method chaining
@@ -160,12 +167,16 @@ export default class ObjectSerializerBuilder {
160
167
  * { openapi: { type: 'string' } }
161
168
  * )
162
169
  *
163
- * // Flattened object properties
170
+ * // Flattened object properties: `openapi` describes the whole returned object
164
171
  * .customAttribute('coordinates', () => ({ lat: 40.7, lng: -74.0 }), {
165
172
  * flatten: true,
166
173
  * openapi: {
167
- * lat: { type: 'number' },
168
- * lng: { type: 'number' }
174
+ * type: 'object',
175
+ * required: ['lat', 'lng'],
176
+ * properties: {
177
+ * lat: { type: 'number' },
178
+ * lng: { type: 'number' }
179
+ * }
169
180
  * }
170
181
  * })
171
182
  * ```
@@ -189,9 +200,18 @@ export default class ObjectSerializerBuilder {
189
200
  * @param options - Configuration options:
190
201
  * - `as` - Rename the association key in the serialized output
191
202
  * - `flatten` - When `true`, spreads the rendered association's attributes directly into
192
- * the parent serialized output instead of nesting them under `name`. Be aware of
193
- * attribute shadowing: if the parent and flattened association share attribute names
194
- * (e.g., `id`), the flattened association's values overwrite the parent's
203
+ * the parent serialized output instead of nesting them under `name`. Attributes render in
204
+ * declaration order, so when the parent and the association share a key (e.g., `id`),
205
+ * whichever is declared later wins: the association's value replaces an attribute declared
206
+ * before the `rendersOne`, and an attribute declared after it replaces the association's.
207
+ * When the association is `null`, every key its serializer declares renders as `null`
208
+ * (for a polymorphic `BelongsTo`, every key the serializer of any of its target classes
209
+ * declares, and for an association to an STI base, every key the serializer of any of its
210
+ * STI children declares), in the same order (so an `id` declared before the `rendersOne` becomes
211
+ * `null`); nothing in that serializer is rendered, so its custom attribute callbacks do
212
+ * not run, and a flattened `customAttribute` in it adds no keys. Psychic adds the association's OpenAPI
213
+ * schema to the parent's as an `allOf` branch, so a shared key's value must match both
214
+ * schemas, whichever declaration wins
195
215
  * - `optional` - When `true`, allows the association to be null/missing without causing
196
216
  * an `OpenapiResponseValidationFailure` during Psychic controller unit specs. By default,
197
217
  * `rendersOne` expects the association to be present (mirroring the `optional` option on
@@ -0,0 +1,43 @@
1
+ import uniq from '../../helpers/uniq.js';
2
+ import { inferSerializersFromDreamClassOrViewModelClass } from './inferSerializerFromDreamOrViewModel.js';
3
+ /**
4
+ * Only used when flatten: true, and the associated object is null, in which case there is no
5
+ * object to resolve a serializer from, but the renderer still needs the serializers whose keys it
6
+ * renders as null in the parent's payload.
7
+ *
8
+ * Resolves, in order, to the `serializer` option, the serializers of the `dreamClass` or
9
+ * `viewModelClass` option, or the serializers of each class `parentClass` declares the association
10
+ * to: the one class of most associations, or every target class of a polymorphic BelongsTo. A
11
+ * class that is an STI base contributes the serializer of every one of its children, since the
12
+ * association renders with whichever child it holds when present. The renderer renders the union
13
+ * of all their keys, each key once. Returns no serializers when none applies, e.g. a
14
+ * non-association property without a `serializer` option, so the flattened association adds no
15
+ * keys.
16
+ */
17
+ export function serializersForAssociatedClass(parentClass, associationName, options) {
18
+ if (options.serializer)
19
+ return [options.serializer];
20
+ const optionClass = options.dreamClass ?? options.viewModelClass;
21
+ let associatedClasses;
22
+ if (optionClass) {
23
+ associatedClasses = [optionClass];
24
+ }
25
+ else {
26
+ const association = parentClass?.['getAssociationMetadata'](associationName);
27
+ if (!association)
28
+ return [];
29
+ const modelClassOrClasses = association.modelCB();
30
+ associatedClasses = Array.isArray(modelClassOrClasses) ? modelClassOrClasses : [modelClassOrClasses];
31
+ }
32
+ // Every serializer of every class: one for a plain class, and one per child (sorted by
33
+ // sanitizedName) for an STI base, so the parent JSON carries the flattened keys of every child
34
+ // as nulls, as it carries those of every target class of a polymorphic BelongsTo. Several classes
35
+ // may resolve to the same serializer; the renderer walks each serializer once either way.
36
+ // (rendersMany never reaches here and does not accept `flatten`.)
37
+ return uniq(associatedClasses.flatMap(associatedClass => inferSerializersFromDreamClassOrViewModelClass(associatedClass, options.serializerKey, {
38
+ // Reached only from SerializerRenderer's rendersOne branch, so the edge type is fixed. No
39
+ // declaring serializer: the renderer holds the rendered attribute, not the serializer that
40
+ // declared it.
41
+ edge: { type: 'rendersOne', associationName },
42
+ })));
43
+ }
@@ -1063,6 +1063,47 @@ export default class Dream {
1063
1063
  * @returns void
1064
1064
  */
1065
1065
  static findEach<T extends typeof Dream>(this: T, cb: (instance: InstanceType<T>) => void | Promise<void>, opts?: FindEachOpts): Promise<void>;
1066
+ /**
1067
+ * Encrypts every value of the model's `@deco.Encrypted` properties again
1068
+ * with the current column encryption key, so that reading them no longer
1069
+ * needs the `legacy` key. It is a step of a key rotation (see
1070
+ * `Encrypt.generateKey`): run it in each environment, once that
1071
+ * environment has deployed its rotated keys, from a console or a script,
1072
+ * not from a migration, whose result would then depend on the keys a given
1073
+ * environment has configured when the migration runs.
1074
+ *
1075
+ * ```ts
1076
+ * await User.reencryptEncryptedColumns()
1077
+ * ```
1078
+ *
1079
+ * It walks every row of the model's table, including rows its default
1080
+ * scopes (such as soft delete) hide and an STI model's siblings, in batches in primary key order, and opens
1081
+ * each non-null value with the current key or, failing that, the legacy
1082
+ * one (see `Encrypt.reencrypt`). Each row is written on its own, with
1083
+ * no model hooks and without changing `updatedAt`, and only while the
1084
+ * column still holds the value read, so a row an app saves in between
1085
+ * keeps the newer value. Reading values here does not fire
1086
+ * `dreamApp.on('encryption:legacy-key-used', ...)`.
1087
+ *
1088
+ * **Run it only once every server encrypts with the new key.** It walks
1089
+ * the table once, so a row a server still configured with the old
1090
+ * `current` writes behind it stays encrypted with the old key, and so does
1091
+ * old-key ciphertext later written straight into a backing column
1092
+ * (`setAttribute`, `setAttributes` and `updateAttributes` accept, for a
1093
+ * backing column, ciphertext that either key opens). Once no writer uses
1094
+ * the old key and this has run for every model with encrypted properties,
1095
+ * `legacy` is safe to drop.
1096
+ *
1097
+ * **A value neither key opens stops it**, throwing as reading the property
1098
+ * would (`DecryptionRotationError` while a `legacy` key is configured,
1099
+ * `DecryptionError` without one). The rows rewritten before it stay
1100
+ * rewritten; running it again once the value is fixed rewrites them again
1101
+ * harmlessly.
1102
+ *
1103
+ * @param opts - Optional parameters
1104
+ * @param opts.batchSize - how many rows to read per batch. Defaults to 1000
1105
+ */
1106
+ static reencryptEncryptedColumns<T extends typeof Dream>(this: T, opts?: FindEachOpts): Promise<void>;
1066
1107
  /**
1067
1108
  * Returns a new instance of Query scoped to the given
1068
1109
  * model class. Especially useful with {@link Query#toKysely}.
@@ -1174,15 +1215,15 @@ export default class Dream {
1174
1215
  * planned drop safe.
1175
1216
  *
1176
1217
  * ```ts
1177
- * const user = await User.leftJoinPreload('posts', 'comments', { visibilty: 'public' }, 'replies').first()
1218
+ * const user = await User.leftJoinPreload('posts', 'comments', { and: { visibility: 'public' } }, 'replies').first()
1178
1219
  * console.log(user.posts[0].comments[0].replies)
1179
1220
  * // [Reply{id: 1}, Reply{id: 2}]
1180
1221
  * ```
1181
1222
  *
1182
- * @param args - A chain of association names and where clauses
1223
+ * @param args - A chain of association names and and/andNot/andAny clauses
1183
1224
  * @returns A query for this model with the include statement applied
1184
1225
  */
1185
- static leftJoinPreload<T extends typeof Dream, I extends InstanceType<T>, DB extends I['DB'], TableName extends InstanceType<T>['table'], Schema extends I['schema'], const Arr extends readonly unknown[], const LastArg extends VariadicLeftJoinLoadArgs<I, DB, Schema, TableName, Arr>, const JoinedAssociationsCandidate = JoinedAssociationsTypeFromAssociations<DB, Schema, TableName, [
1226
+ static leftJoinPreload<T extends typeof Dream, I extends InstanceType<T>, DB extends I['DB'], TableName extends InstanceType<T>['table'], Schema extends I['schema'], const Arr extends readonly unknown[], const LastArg extends VariadicLeftJoinLoadArgs<I, DB, Schema, TableName, Arr, LastArg>, const JoinedAssociationsCandidate = JoinedAssociationsTypeFromAssociations<DB, Schema, TableName, [
1186
1227
  ...Arr,
1187
1228
  LastArg
1188
1229
  ]>, const JoinedAssociations extends readonly JoinedAssociation[] = JoinedAssociationsCandidate extends readonly JoinedAssociation[] ? JoinedAssociationsCandidate : never, RetQuery = QueryWithJoinedAssociationsTypeAndNoPreload<Query<I>, JoinedAssociations>>(this: T, ...args: [...Arr, LastArg]): RetQuery;
@@ -1197,12 +1238,12 @@ export default class Dream {
1197
1238
  * Preload is useful for avoiding the N+1 query problem
1198
1239
  *
1199
1240
  * ```ts
1200
- * const user = await User.preload('posts', 'comments', { visibilty: 'public' }, 'replies').first()
1241
+ * const user = await User.preload('posts', 'comments', { and: { visibility: 'public' } }, 'replies').first()
1201
1242
  * console.log(user.posts[0].comments[0].replies)
1202
1243
  * // [Reply{id: 1}, Reply{id: 2}]
1203
1244
  * ```
1204
1245
  *
1205
- * @param args - A chain of association names and where clauses
1246
+ * @param args - A chain of association names and and/andNot/andAny clauses
1206
1247
  * @returns A query for this model with the preload statement applied
1207
1248
  */
1208
1249
  static preload<T extends typeof Dream, I extends InstanceType<T>, DB extends I['DB'], TableName extends InstanceType<T>['table'], Schema extends I['schema'], const Arr extends readonly unknown[]>(this: T, ...args: [...Arr, VariadicLoadArgs<I, DB, Schema, TableName, Arr>]): Query<InstanceType<T>, Readonly<{
@@ -1276,10 +1317,10 @@ export default class Dream {
1276
1317
  * await User.innerJoin('posts').first()
1277
1318
  * ```
1278
1319
  *
1279
- * @param args - A chain of association names and where clauses
1320
+ * @param args - A chain of association names and and/andNot/andAny clauses
1280
1321
  * @returns A Query for this model with the inner join clause applied
1281
1322
  */
1282
- static innerJoin<T extends typeof Dream, I extends InstanceType<T>, DB extends I['DB'], Schema extends I['schema'], TableName extends I['table'] & keyof Schema, const Arr extends readonly unknown[], const LastArg extends VariadicJoinsArgs<I, DB, Schema, TableName, Arr>, const JoinedAssociationsCandidate = JoinedAssociationsTypeFromAssociations<DB, Schema, TableName, [
1323
+ static innerJoin<T extends typeof Dream, I extends InstanceType<T>, DB extends I['DB'], Schema extends I['schema'], TableName extends I['table'] & keyof Schema, const Arr extends readonly unknown[], const LastArg extends VariadicJoinsArgs<I, DB, Schema, TableName, Arr, LastArg>, const JoinedAssociationsCandidate = JoinedAssociationsTypeFromAssociations<DB, Schema, TableName, [
1283
1324
  ...Arr,
1284
1325
  LastArg
1285
1326
  ]>, const JoinedAssociations extends readonly JoinedAssociation[] = JoinedAssociationsCandidate extends readonly JoinedAssociation[] ? JoinedAssociationsCandidate : never, RetQuery = QueryWithJoinedAssociationsType<Query<I>, JoinedAssociations>>(this: T, ...args: [...Arr, LastArg]): RetQuery;
@@ -1291,10 +1332,10 @@ export default class Dream {
1291
1332
  * await user.innerJoin('posts').first()
1292
1333
  * ```
1293
1334
  *
1294
- * @param args - A chain of association names and where clauses
1335
+ * @param args - A chain of association names and and/andNot/andAny clauses
1295
1336
  * @returns A Query for this model with the inner join clause applied
1296
1337
  */
1297
- innerJoin<I extends Dream, DB extends I['DB'], Schema extends I['schema'], TableName extends I['table'] & keyof Schema, const Arr extends readonly unknown[], const LastArg extends VariadicJoinsArgs<I, DB, Schema, TableName, Arr>, const JoinedAssociationsCandidate = JoinedAssociationsTypeFromAssociations<DB, Schema, TableName, [
1338
+ innerJoin<I extends Dream, DB extends I['DB'], Schema extends I['schema'], TableName extends I['table'] & keyof Schema, const Arr extends readonly unknown[], const LastArg extends VariadicJoinsArgs<I, DB, Schema, TableName, Arr, LastArg>, const JoinedAssociationsCandidate = JoinedAssociationsTypeFromAssociations<DB, Schema, TableName, [
1298
1339
  ...Arr,
1299
1340
  LastArg
1300
1341
  ]>, const JoinedAssociations extends readonly JoinedAssociation[] = JoinedAssociationsCandidate extends readonly JoinedAssociation[] ? JoinedAssociationsCandidate : never, RetQuery = QueryWithJoinedAssociationsType<Query<I>, JoinedAssociations>>(this: I, ...args: [...Arr, LastArg]): RetQuery;
@@ -1306,10 +1347,10 @@ export default class Dream {
1306
1347
  * await User.leftJoin('posts').first()
1307
1348
  * ```
1308
1349
  *
1309
- * @param args - A chain of association names and where clauses
1350
+ * @param args - A chain of association names and and/andNot/andAny clauses
1310
1351
  * @returns A Query for this model with the left join clause applied
1311
1352
  */
1312
- static leftJoin<T extends typeof Dream, I extends InstanceType<T>, DB extends I['DB'], Schema extends I['schema'], TableName extends I['table'] & keyof Schema, const Arr extends readonly unknown[], const LastArg extends VariadicJoinsArgs<I, DB, Schema, TableName, Arr>, const JoinedAssociationsCandidate = JoinedAssociationsTypeFromAssociations<DB, Schema, TableName, [
1353
+ static leftJoin<T extends typeof Dream, I extends InstanceType<T>, DB extends I['DB'], Schema extends I['schema'], TableName extends I['table'] & keyof Schema, const Arr extends readonly unknown[], const LastArg extends VariadicJoinsArgs<I, DB, Schema, TableName, Arr, LastArg>, const JoinedAssociationsCandidate = JoinedAssociationsTypeFromAssociations<DB, Schema, TableName, [
1313
1354
  ...Arr,
1314
1355
  LastArg
1315
1356
  ]>, const JoinedAssociations extends readonly JoinedAssociation[] = JoinedAssociationsCandidate extends readonly JoinedAssociation[] ? JoinedAssociationsCandidate : never, RetQuery = QueryWithJoinedAssociationsType<Query<I>, JoinedAssociations>>(this: T, ...args: [...Arr, LastArg]): RetQuery;
@@ -1321,10 +1362,10 @@ export default class Dream {
1321
1362
  * await user.leftJoin('posts').first()
1322
1363
  * ```
1323
1364
  *
1324
- * @param args - A chain of association names and where clauses
1365
+ * @param args - A chain of association names and and/andNot/andAny clauses
1325
1366
  * @returns A Query for this model with the left join clause applied
1326
1367
  */
1327
- leftJoin<I extends Dream, DB extends I['DB'], Schema extends I['schema'], TableName extends I['table'] & keyof Schema, const Arr extends readonly unknown[], const LastArg extends VariadicJoinsArgs<I, DB, Schema, TableName, Arr>, const JoinedAssociationsCandidate = JoinedAssociationsTypeFromAssociations<DB, Schema, TableName, [
1368
+ leftJoin<I extends Dream, DB extends I['DB'], Schema extends I['schema'], TableName extends I['table'] & keyof Schema, const Arr extends readonly unknown[], const LastArg extends VariadicJoinsArgs<I, DB, Schema, TableName, Arr, LastArg>, const JoinedAssociationsCandidate = JoinedAssociationsTypeFromAssociations<DB, Schema, TableName, [
1328
1369
  ...Arr,
1329
1370
  LastArg
1330
1371
  ]>, const JoinedAssociations extends readonly JoinedAssociation[] = JoinedAssociationsCandidate extends readonly JoinedAssociation[] ? JoinedAssociationsCandidate : never, RetQuery = QueryWithJoinedAssociationsType<Query<I>, JoinedAssociations>>(this: I, ...args: [...Arr, LastArg]): RetQuery;
@@ -2165,6 +2206,15 @@ export default class Dream {
2165
2206
  * const user = User.new({ email: 'how@yadoin' })
2166
2207
  * ```
2167
2208
  *
2209
+ * @param opts - the attributes to set on the new instance
2210
+ * @param additionalOpts - optional parameters
2211
+ * @param additionalOpts.bypassUserDefinedSetters - if true, sets the attributes
2212
+ * as {@link Dream.setAttributes | setAttributes} does, bypassing any
2213
+ * custom-defined setters. An encrypted property (e.g. `secret`) is still
2214
+ * encrypted, and an `@Encrypted` backing column (e.g. `encryptedSecret`)
2215
+ * accepts only ciphertext that decrypts with this app's column encryption
2216
+ * keys, or `null`; anything else throws (see
2217
+ * {@link Dream.setAttribute | setAttribute}). Defaults to false
2168
2218
  * @returns A new (unpersisted) instance of the provided dream class
2169
2219
  */
2170
2220
  static new<T extends typeof Dream>(this: T, opts?: UpdateablePropertiesForClass<T>, additionalOpts?: {
@@ -2189,6 +2239,7 @@ export default class Dream {
2189
2239
  */
2190
2240
  constructor(opts: any, additionalOpts: {
2191
2241
  bypassUserDefinedSetters?: boolean;
2242
+ fromDatabase?: boolean;
2192
2243
  isPersisted?: boolean;
2193
2244
  _internalUseOnly: true;
2194
2245
  });
@@ -2276,12 +2327,39 @@ export default class Dream {
2276
2327
  * bypassing any custom-defined setters. If you would like to set attributes
2277
2328
  * without bypassing custom-defined setters, use #assignAttribute instead
2278
2329
  *
2330
+ * An `@Encrypted` backing column (e.g. `encryptedSecret`) accepts only
2331
+ * a string of ciphertext that decrypts with this app's column encryption
2332
+ * keys (current or legacy), or `null`, which clears it. Anything else,
2333
+ * including plaintext, `undefined`, a non-string value, and ciphertext
2334
+ * made with a key this app does not hold, throws and leaves the column
2335
+ * unchanged. The check covers the backing columns of this model's class,
2336
+ * including those it inherits from an STI parent; a backing column
2337
+ * declared only by another class in the same STI hierarchy is not
2338
+ * checked.
2339
+ *
2340
+ * Given the name of an encrypted property (e.g. `secret`), setAttribute
2341
+ * neither encrypts the value nor stores it: it keeps the value,
2342
+ * unencrypted, under that name in this instance's attributes, which a
2343
+ * save does not write, while the backing column and the property keep
2344
+ * the value they had. To store a new value, assign the property
2345
+ * (`user.secret = 'new secret'`) or pass it to
2346
+ * {@link Dream.setAttributes | setAttributes}, each of which encrypts it.
2347
+ *
2279
2348
  * ```ts
2280
2349
  * const user = new User()
2281
2350
  * user.setAttribute('email', 'sally@gmail.com')
2282
2351
  * ```
2283
2352
  */
2284
2353
  setAttribute<I extends Dream, Key extends AttributeKeys<I>>(this: I, column: Key & string, val: any): void;
2354
+ /**
2355
+ * @internal
2356
+ *
2357
+ * Writes the value into the current attributes as given, with none of
2358
+ * setAttribute's checks. Only for values Dream already trusts: a row read
2359
+ * from the database, and the ciphertext the @Encrypted property setter
2360
+ * has just produced. Every caller-supplied value goes through setAttribute.
2361
+ */
2362
+ private setAttributeUnchecked;
2285
2363
  /**
2286
2364
  * Returns the value for a columnName provided,
2287
2365
  * bypassing the getters.
@@ -2336,43 +2414,55 @@ export default class Dream {
2336
2414
  * changed since last persisting, along with their current
2337
2415
  * and previously persisted values.
2338
2416
  *
2417
+ * For a column with an unsaved change, `was` is its value
2418
+ * as of the most recent save. For every other column, `was`
2419
+ * is its value from before that save, so the changes the
2420
+ * save made keep appearing, beside any later unsaved change,
2421
+ * until the next save.
2422
+ *
2423
+ * On a create, that earlier value is `undefined` for every
2424
+ * column of an instance from `new` or `create`, assigned or
2425
+ * not, and the copied value for an instance from
2426
+ * {@link Dream.dup | dup}. So after a create from `new` or
2427
+ * `create`, every column the insert returned counts as
2428
+ * changed: the primary key, timestamps, database defaults,
2429
+ * and `null` for each nullable column nobody assigned. After
2430
+ * a create from `dup`, the columns `dup` cleared (the primary
2431
+ * key, timestamps and sortable positions) count, and a copied
2432
+ * column counts only if the insert returned a different value.
2433
+ *
2339
2434
  * ```ts
2340
2435
  * const pet = Pet.new({ species: 'dog' })
2341
2436
  * pet.changes()
2342
2437
  * // {
2343
- * // species: {
2344
- * // was: undefined,
2345
- * // now: 'dog',
2346
- * // }
2438
+ * // species: { was: undefined, now: 'dog' },
2347
2439
  * // }
2348
2440
  *
2349
2441
  * await pet.save()
2350
2442
  * pet.changes()
2351
2443
  * // {
2352
- * // species: {
2353
- * // was: undefined,
2354
- * // now: 'dog',
2355
- * // }
2444
+ * // id: { was: undefined, now: 1 },
2445
+ * // species: { was: undefined, now: 'dog' },
2446
+ * // name: { was: undefined, now: null },
2447
+ * // ...every other column the insert returned
2356
2448
  * // }
2357
2449
  *
2358
2450
  * pet.species = 'cat'
2359
2451
  * pet.species = 'frog'
2360
2452
  * pet.changes()
2361
2453
  * // {
2362
- * // species: {
2363
- * // was: 'dog',
2364
- * // now: 'frog',
2365
- * // }
2454
+ * // id: { was: undefined, now: 1 },
2455
+ * // species: { was: 'dog', now: 'frog' },
2456
+ * // name: { was: undefined, now: null },
2457
+ * // ...every other change the create made
2366
2458
  * // }
2367
2459
  *
2368
2460
  * await pet.save()
2369
2461
  * pet.changes()
2370
2462
  * // {
2371
- * // species: {
2372
- * // was: 'dog',
2373
- * // now: 'frog',
2374
- * // }
2463
+ * // species: { was: 'dog', now: 'frog' },
2375
2464
  * // }
2465
+ * // (and `updatedAt`, on a model with that column)
2376
2466
  * ```
2377
2467
  *
2378
2468
  * @returns An object containing changed attributes
@@ -2382,8 +2472,22 @@ export default class Dream {
2382
2472
  now: Selectable<Table>[DreamColumnNames<I>];
2383
2473
  }>>>(this: I): RetType;
2384
2474
  /**
2385
- * Returns the value most recently persisted
2386
- * to the database.
2475
+ * Returns the value the column held before the most
2476
+ * recent save. For a column with an unsaved change, it
2477
+ * returns the column's value as of that save instead.
2478
+ *
2479
+ * On a create, the value before the save is `undefined`
2480
+ * for every column of an instance from `new` or `create`,
2481
+ * assigned or not, and the copied value for an instance
2482
+ * from {@link Dream.dup | dup} (`undefined` for the
2483
+ * columns `dup` cleared). So after a create from `new` or
2484
+ * `create`, this returns `undefined` for every column
2485
+ * without an unsaved change, not the value the insert
2486
+ * wrote. After an update, it returns the column's value
2487
+ * from before that update, which for a column the update
2488
+ * did not change is its current value. On a record loaded
2489
+ * from the database and not saved since, it returns the
2490
+ * loaded value.
2387
2491
  *
2388
2492
  * ```ts
2389
2493
  * const pet = Pet.new({ species: 'cat' })
@@ -2414,7 +2518,38 @@ export default class Dream {
2414
2518
  /**
2415
2519
  * Returns true if the columnName provided has
2416
2520
  * changes that were persisted during the most
2417
- * recent save.
2521
+ * recent save. An unsaved change does not count,
2522
+ * and an unsaved edit made after that save does
2523
+ * not undo a change the save made. On a record
2524
+ * that has not been persisted, this returns false.
2525
+ *
2526
+ * A create compares the value the insert returned
2527
+ * with `undefined` for every column of an instance
2528
+ * from `new` or `create`, assigned or not, and with
2529
+ * the copied value for an instance from
2530
+ * {@link Dream.dup | dup}. So after a create from
2531
+ * `new` or `create`, this returns true for every
2532
+ * column the insert returned: the primary key,
2533
+ * timestamps, database defaults, and a nullable
2534
+ * column nobody assigned, which comes back `null`.
2535
+ * To react only to the columns a create assigned,
2536
+ * use the `ifChanged` option of an after hook.
2537
+ *
2538
+ * ```ts
2539
+ * const pet = await Pet.create({ species: 'cat' })
2540
+ * pet.savedChangeToAttribute('species')
2541
+ * // true
2542
+ * pet.savedChangeToAttribute('name')
2543
+ * // true (unassigned, so the insert returned null)
2544
+ *
2545
+ * await pet.update({ name: 'Snoopy' })
2546
+ * pet.savedChangeToAttribute('species')
2547
+ * // false
2548
+ *
2549
+ * pet.species = 'dog'
2550
+ * pet.savedChangeToAttribute('species')
2551
+ * // false (not saved yet)
2552
+ * ```
2418
2553
  *
2419
2554
  * @param columnName - the column name to check
2420
2555
  * @returns A boolean
@@ -2642,19 +2777,19 @@ export default class Dream {
2642
2777
  * if the association is not already loaded.
2643
2778
  *
2644
2779
  * ```ts
2645
- * const user = await user
2646
- * .load('posts', { body: ops.ilike('%hello world%') }, 'comments', 'replies')
2780
+ * const loadedUser = await user
2781
+ * .load('posts', { and: { body: ops.ilike('%hello world%') } }, 'comments', 'replies')
2647
2782
  * .load('images')
2648
2783
  * .execute()
2649
2784
  *
2650
- * user.posts[0].comments[0].replies[0]
2785
+ * loadedUser.posts[0].comments[0].replies[0]
2651
2786
  * // Reply{}
2652
2787
  *
2653
- * user.images[0]
2788
+ * loadedUser.images[0]
2654
2789
  * // Image{}
2655
2790
  * ```
2656
2791
  *
2657
- * @param args - A list of associations (and optional where clauses) to load
2792
+ * @param args - A chain of association names and and/andNot/andAny clauses
2658
2793
  * @returns A chainable LoadBuilder instance. Call `.execute()` to get the cloned model with associations loaded.
2659
2794
  */
2660
2795
  load<I extends Dream, DB extends I['DB'], TableName extends I['table'], Schema extends I['schema'], const Arr extends readonly unknown[]>(this: I, ...args: [...Arr, VariadicLoadArgs<I, DB, Schema, TableName, Arr>]): LoadBuilder<I>;
@@ -2855,19 +2990,19 @@ export default class Dream {
2855
2990
  * 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
2856
2991
  *
2857
2992
  * ```ts
2858
- * const user = await user
2859
- * .leftJoinLoad('posts', { body: ops.ilike('%hello world%') }, 'comments', 'replies')
2993
+ * const loadedUser = await user
2994
+ * .leftJoinLoad('posts', { and: { body: ops.ilike('%hello world%') } }, 'comments', 'replies')
2860
2995
  * .leftJoinLoad('images')
2861
2996
  * .execute()
2862
2997
  *
2863
- * user.posts[0].comments[0].replies[0]
2998
+ * loadedUser.posts[0].comments[0].replies[0]
2864
2999
  * // Reply{}
2865
3000
  *
2866
- * user.images[0]
3001
+ * loadedUser.images[0]
2867
3002
  * // Image{}
2868
3003
  * ```
2869
3004
  *
2870
- * @param args - A list of associations (and optional where clauses) to load
3005
+ * @param args - A chain of association names and and/andNot/andAny clauses
2871
3006
  * @returns A chainable LeftJoinLoadBuilder instance. Call `.execute()` to get the cloned model with associations loaded.
2872
3007
  */
2873
3008
  leftJoinLoad<I extends Dream, DB extends I['DB'], TableName extends I['table'], Schema extends I['schema'], const Arr extends readonly unknown[]>(this: I, ...args: [...Arr, VariadicLeftJoinLoadArgs<I, DB, Schema, TableName, Arr>]): LeftJoinLoadBuilder<I>;
@@ -2929,6 +3064,13 @@ export default class Dream {
2929
3064
  * Takes the attributes passed in and sets their values internally,
2930
3065
  * bypassing any custom setters defined for these attributes.
2931
3066
  *
3067
+ * An encrypted property (e.g. `secret`) is still encrypted. Its
3068
+ * `@Encrypted` backing column (e.g. `encryptedSecret`) accepts only
3069
+ * ciphertext that decrypts with this app's column encryption keys, or
3070
+ * `null`; anything else throws (see {@link Dream.setAttribute | setAttribute}).
3071
+ * Attributes are set in order, so those before the rejected one have
3072
+ * already been set when it throws.
3073
+ *
2932
3074
  * NOTE:
2933
3075
  * To leverage custom-defined setters, use `#assignAttributes` instead.
2934
3076
  *
@@ -2959,6 +3101,10 @@ export default class Dream {
2959
3101
  * Upon updating an instance, the update timestamp (if defined)
2960
3102
  * will be updated on the model.
2961
3103
  *
3104
+ * A column whose value is `undefined` is skipped, not written: a new
3105
+ * record gets the column's database default, and a persisted record
3106
+ * keeps the value stored for it. Set a column to `null` to clear it.
3107
+ *
2962
3108
  * ```ts
2963
3109
  * const user = User.new({ email: 'how@yadoin' })
2964
3110
  * user.name = 'fred'
@@ -3004,6 +3150,12 @@ export default class Dream {
3004
3150
  * See {@link Dream.save | save} for details on
3005
3151
  * the side effects of saving.
3006
3152
  *
3153
+ * A column whose value is still `undefined` after setters run is
3154
+ * skipped, not written (see {@link Dream.save | save}), with two
3155
+ * `@Encrypted` exceptions: `undefined` for an encrypted property (e.g.
3156
+ * `secret`) is encrypted to `null`, so it clears the column, and
3157
+ * `undefined` for its backing column (e.g. `encryptedSecret`) throws.
3158
+ *
3007
3159
  * NOTE:
3008
3160
  * To bypass custom-defined setters, use {@link Dream.updateAttributes | updateAttributes} instead.
3009
3161
  *
@@ -3027,6 +3179,18 @@ export default class Dream {
3027
3179
  * See {@link Dream.save | save} for details on
3028
3180
  * the side effects of saving.
3029
3181
  *
3182
+ * An encrypted property (e.g. `secret`) is still encrypted. Its
3183
+ * `@Encrypted` backing column (e.g. `encryptedSecret`) accepts only
3184
+ * ciphertext that decrypts with this app's column encryption keys, or
3185
+ * `null`; anything else throws before anything is saved (see
3186
+ * {@link Dream.setAttribute | setAttribute}).
3187
+ *
3188
+ * A column whose value is still `undefined` after setters run is
3189
+ * skipped, not written (see {@link Dream.save | save}), with two
3190
+ * `@Encrypted` exceptions: `undefined` for an encrypted property (e.g.
3191
+ * `secret`) is encrypted to `null`, so it clears the column, and
3192
+ * `undefined` for its backing column (e.g. `encryptedSecret`) throws.
3193
+ *
3030
3194
  * NOTE:
3031
3195
  * To update the values without bypassing any custom-defined
3032
3196
  * setters, use {@link Dream.update | update} instead.
@@ -21,7 +21,13 @@ export default class DreamBin {
21
21
  }): Promise<void>;
22
22
  static generateStiChild(fullyQualifiedModelName: string, fullyQualifiedParentName: string, columnsWithTypes: string[], options: {
23
23
  serializer: boolean;
24
- connectionName: string;
24
+ /**
25
+ * The connection whose migrations folder receives the child's
26
+ * migration. Defaults to the parent model's connection, which is the
27
+ * connection the child uses; the parent must then be one of the app's
28
+ * models, or this throws before any file is written.
29
+ */
30
+ connectionName?: string;
25
31
  adminSerializers?: boolean;
26
32
  internalSerializers?: boolean;
27
33
  modelName?: string;
@@ -16,7 +16,20 @@ export type SpawnOptions = Omit<NodeSpawnOptions, 'shell'> & {
16
16
  };
17
17
  export declare const CLI_INDENT = " ";
18
18
  export declare const baseColumnsWithTypesDescription = "space separated snake-case properties like this:\n title:citext subtitle:string body_markdown:text style:enum:post_styles:formal,informal\n \n all properties default to not nullable; null can be allowed by appending ':optional':\n subtitle:string:optional\n \n supported types:\n - uuid:\n - uuid[]:\n a column optimized for storing UUIDs\n \n - citext:\n - citext[]:\n case insensitive text (indexes and queries are automatically case insensitive)\n \n - encrypted:\n encrypted text (used in conjunction with the @deco.Encrypted decorator)\n \n - string:\n - string[]:\n varchar; allowed length defaults to 255, but may be customized, e.g.: subtitle:string:128 or subtitle:string:128:optional\n \n - text\n - text[]\n - date\n - date[]\n - datetime\n - datetime[]\n - time\n - time[]\n - timetz\n - timetz[]\n - integer\n - integer[]\n - boolean\n - boolean[]\n \n - decimal:\n - decimal[]:\n precision,scale is required, e.g.: volume:decimal:3,2 or volume:decimal:3,2:optional\n \n leveraging arrays, add the \"[]\" suffix, e.g.: volume:decimal[]:3,2\n \n - enum:\n - enum[]:\n include the enum name to automatically create the enum:\n type:enum:room_types:bathroom,kitchen,bedroom or type:enum:room_types:bathroom,kitchen,bedroom:optional\n \n omit the enum values to leverage an existing enum (omits the enum type creation):\n type:enum:room_types or type:enum:room_types:optional\n \n leveraging arrays, add the \"[]\" suffix, e.g.: type:enum[]:room_types:bathroom,kitchen,bedroom";
19
+ /**
20
+ * The `[columnsWithTypes...]` argument description for `g:model`. Exported so
21
+ * that spec/unit/cli/generateMigrationContent.spec.ts can run each
22
+ * `:belongs_to` example in this text through the migration generator and check
23
+ * the FK column the example's comment names: an example that drifts from what
24
+ * the generator actually produces then fails the suite instead of shipping.
25
+ */
26
+ export declare const columnsWithTypesDescription: string;
19
27
  export declare const columnsWithTypesDescriptionForStiChild: string;
28
+ /**
29
+ * The `[columnsWithTypes...]` argument description for `g:migration`. Exported
30
+ * for the same `:belongs_to` example check as `columnsWithTypesDescription`.
31
+ */
32
+ export declare const columnsWithTypesDescriptionForMigration: string;
20
33
  /**
21
34
  * The `<migrationName>` argument description for `g:migration`. Exported so
22
35
  * that spec/unit/helpers/cli/generateMigration.spec.ts can read the example
@@ -183,7 +183,7 @@ export default class DreamMigrationHelpers {
183
183
  * @param options - Configuration options
184
184
  * @param options.table - The name of the table
185
185
  * @param options.column - The current (plaintext) column name
186
- * @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.
186
+ * @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.
187
187
  * @param options.primaryKey - The primary key column used to keyset-paginate and target each row's update. Defaults to `id`.
188
188
  * @param options.batchSize - How many rows to read per batch. Defaults to `1000`.
189
189
  */
@@ -215,7 +215,7 @@ export default class DreamMigrationHelpers {
215
215
  * @param options - Configuration options
216
216
  * @param options.table - The name of the table
217
217
  * @param options.column - The target (plaintext) column name to rename back to
218
- * @param options.encryptedColumnName - The current encrypted column name. Defaults to `encrypted_<column>`.
218
+ * @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`).
219
219
  * @param options.primaryKey - The primary key column used to keyset-paginate and target each row's update. Defaults to `id`.
220
220
  * @param options.batchSize - How many rows to read per batch. Defaults to `1000`.
221
221
  * @param options.columnType - When provided, the restored column is converted to this type. When omitted, the column is left as `text`.