@rvoh/dream 2.9.2 → 2.11.1

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 (219) hide show
  1. package/dist/cjs/src/cli/index.js +16 -3
  2. package/dist/cjs/src/dream/QueryDriver/Kysely.js +7 -8
  3. package/dist/cjs/src/dream/QueryDriver/helpers/kysely/checkForNeedToBeRunMigrations.js +1 -1
  4. package/dist/cjs/src/dream/QueryDriver/helpers/kysely/runMigration.js +1 -1
  5. package/dist/cjs/src/dream-app/index.js +14 -6
  6. package/dist/cjs/src/encrypt/index.js +5 -5
  7. package/dist/cjs/src/errors/dream-app/MissingDbSslDirective.js +29 -0
  8. package/dist/cjs/src/errors/encrypt/{DecryptionWithRotationError.js → DecryptionRotationError.js} +1 -1
  9. package/dist/cjs/src/helpers/cli/generateDreamContent.js +43 -17
  10. package/dist/cjs/src/helpers/cli/generateFactoryContent.js +34 -7
  11. package/dist/cjs/src/helpers/cli/generateMigrationContent.js +30 -4
  12. package/dist/cjs/src/helpers/cli/parseAttribute.js +61 -0
  13. package/dist/cjs/src/package-exports/errors.js +3 -0
  14. package/dist/esm/src/cli/index.js +16 -3
  15. package/dist/esm/src/dream/QueryDriver/Kysely.js +7 -8
  16. package/dist/esm/src/dream/QueryDriver/helpers/kysely/checkForNeedToBeRunMigrations.js +1 -1
  17. package/dist/esm/src/dream/QueryDriver/helpers/kysely/runMigration.js +1 -1
  18. package/dist/esm/src/dream-app/index.js +14 -6
  19. package/dist/esm/src/encrypt/index.js +5 -5
  20. package/dist/esm/src/errors/dream-app/MissingDbSslDirective.js +29 -0
  21. package/dist/esm/src/errors/encrypt/{DecryptionWithRotationError.js → DecryptionRotationError.js} +1 -1
  22. package/dist/esm/src/helpers/cli/generateDreamContent.js +43 -17
  23. package/dist/esm/src/helpers/cli/generateFactoryContent.js +34 -7
  24. package/dist/esm/src/helpers/cli/generateMigrationContent.js +30 -4
  25. package/dist/esm/src/helpers/cli/parseAttribute.js +61 -0
  26. package/dist/esm/src/package-exports/errors.js +3 -0
  27. package/dist/types/src/cli/index.d.ts +1 -1
  28. package/dist/types/src/dream/QueryDriver/Kysely.d.ts +8 -9
  29. package/dist/types/src/dream-app/index.d.ts +23 -15
  30. package/dist/types/src/encrypt/index.d.ts +3 -3
  31. package/dist/types/src/errors/dream-app/MissingDbSslDirective.d.ts +6 -0
  32. package/dist/types/src/errors/encrypt/{DecryptionWithRotationError.d.ts → DecryptionRotationError.d.ts} +1 -1
  33. package/dist/types/src/helpers/cli/generateDreamContent.d.ts +6 -2
  34. package/dist/types/src/helpers/cli/parseAttribute.d.ts +64 -0
  35. package/dist/types/src/package-exports/errors.d.ts +3 -0
  36. package/docs/assets/navigation.js +1 -1
  37. package/docs/assets/search.js +1 -1
  38. package/docs/classes/db.DreamMigrationHelpers.html +9 -9
  39. package/docs/classes/db.KyselyQueryDriver.html +32 -32
  40. package/docs/classes/db.PostgresQueryDriver.html +33 -33
  41. package/docs/classes/db.QueryDriverBase.html +31 -31
  42. package/docs/classes/errors.CheckConstraintViolation.html +3 -3
  43. package/docs/classes/errors.ColumnOverflow.html +3 -3
  44. package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
  45. package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
  46. package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
  47. package/docs/classes/errors.DecryptionError.html +33 -0
  48. package/docs/classes/errors.DecryptionParseError.html +33 -0
  49. package/docs/classes/errors.DecryptionRotationError.html +35 -0
  50. package/docs/classes/errors.GlobalNameNotSet.html +3 -3
  51. package/docs/classes/errors.InvalidCalendarDate.html +2 -2
  52. package/docs/classes/errors.InvalidClockTime.html +2 -2
  53. package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
  54. package/docs/classes/errors.InvalidDateTime.html +2 -2
  55. package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
  56. package/docs/classes/errors.NonLoadedAssociation.html +3 -3
  57. package/docs/classes/errors.NotNullViolation.html +3 -3
  58. package/docs/classes/errors.RecordNotFound.html +3 -3
  59. package/docs/classes/errors.ValidationError.html +3 -3
  60. package/docs/classes/index.CalendarDate.html +33 -33
  61. package/docs/classes/index.ClockTime.html +32 -32
  62. package/docs/classes/index.ClockTimeTz.html +35 -35
  63. package/docs/classes/index.DateTime.html +86 -86
  64. package/docs/classes/index.Decorators.html +19 -19
  65. package/docs/classes/index.Dream.html +118 -118
  66. package/docs/classes/index.DreamApp.html +5 -5
  67. package/docs/classes/index.DreamTransaction.html +2 -2
  68. package/docs/classes/index.Env.html +2 -2
  69. package/docs/classes/index.Query.html +56 -56
  70. package/docs/classes/system.CliFileWriter.html +4 -4
  71. package/docs/classes/system.DreamBin.html +2 -2
  72. package/docs/classes/system.DreamCLI.html +7 -7
  73. package/docs/classes/system.DreamImporter.html +2 -2
  74. package/docs/classes/system.DreamLogos.html +2 -2
  75. package/docs/classes/system.DreamSerializerBuilder.html +11 -11
  76. package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
  77. package/docs/classes/system.PathHelpers.html +3 -3
  78. package/docs/classes/utils.Encrypt.html +6 -6
  79. package/docs/classes/utils.Range.html +2 -2
  80. package/docs/functions/db.closeAllDbConnections.html +1 -1
  81. package/docs/functions/db.dreamDbConnections.html +1 -1
  82. package/docs/functions/db.untypedDb.html +1 -1
  83. package/docs/functions/db.validateColumn.html +1 -1
  84. package/docs/functions/db.validateTable.html +1 -1
  85. package/docs/functions/errors.pgErrorType.html +1 -1
  86. package/docs/functions/index.DreamSerializer.html +1 -1
  87. package/docs/functions/index.ObjectSerializer.html +1 -1
  88. package/docs/functions/index.ReplicaSafe.html +1 -1
  89. package/docs/functions/index.STI.html +1 -1
  90. package/docs/functions/index.SoftDelete.html +1 -1
  91. package/docs/functions/utils.camelize.html +1 -1
  92. package/docs/functions/utils.capitalize.html +1 -1
  93. package/docs/functions/utils.cloneDeepSafe.html +1 -1
  94. package/docs/functions/utils.compact.html +1 -1
  95. package/docs/functions/utils.groupBy.html +1 -1
  96. package/docs/functions/utils.hyphenize.html +1 -1
  97. package/docs/functions/utils.intersection.html +1 -1
  98. package/docs/functions/utils.isEmpty.html +1 -1
  99. package/docs/functions/utils.normalizeUnicode.html +1 -1
  100. package/docs/functions/utils.pascalize.html +1 -1
  101. package/docs/functions/utils.percent.html +1 -1
  102. package/docs/functions/utils.range.html +1 -1
  103. package/docs/functions/utils.round.html +1 -1
  104. package/docs/functions/utils.sanitizeString.html +1 -1
  105. package/docs/functions/utils.snakeify.html +1 -1
  106. package/docs/functions/utils.sort.html +1 -1
  107. package/docs/functions/utils.sortBy.html +1 -1
  108. package/docs/functions/utils.sortObjectByKey.html +1 -1
  109. package/docs/functions/utils.sortObjectByValue.html +1 -1
  110. package/docs/functions/utils.uncapitalize.html +1 -1
  111. package/docs/functions/utils.uniq.html +1 -1
  112. package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
  113. package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
  114. package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
  115. package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
  116. package/docs/interfaces/types.BelongsToStatement.html +2 -2
  117. package/docs/interfaces/types.DecoratorContext.html +2 -2
  118. package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
  119. package/docs/interfaces/types.DreamAppOpts.html +2 -2
  120. package/docs/interfaces/types.DurationObject.html +2 -2
  121. package/docs/interfaces/types.EncryptOptions.html +2 -2
  122. package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
  123. package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
  124. package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
  125. package/docs/modules/errors.html +1 -1
  126. package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
  127. package/docs/types/openapi.OpenapiAllTypes.html +1 -1
  128. package/docs/types/openapi.OpenapiFormats.html +1 -1
  129. package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
  130. package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
  131. package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
  132. package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
  133. package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
  134. package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
  135. package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
  136. package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
  137. package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
  138. package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
  139. package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
  140. package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
  141. package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
  142. package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
  143. package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
  144. package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
  145. package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
  146. package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
  147. package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
  148. package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
  149. package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
  150. package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  151. package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
  152. package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
  153. package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
  154. package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
  155. package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
  156. package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
  157. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
  158. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
  159. package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
  160. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
  161. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
  162. package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  163. package/docs/types/openapi.OpenapiSchemaString.html +1 -1
  164. package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
  165. package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  166. package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
  167. package/docs/types/openapi.OpenapiTypeField.html +1 -1
  168. package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
  169. package/docs/types/types.CalendarDateDurationUnit.html +1 -1
  170. package/docs/types/types.CalendarDateObject.html +1 -1
  171. package/docs/types/types.Camelized.html +1 -1
  172. package/docs/types/types.ClockTimeObject.html +1 -1
  173. package/docs/types/types.DbConnectionType.html +1 -1
  174. package/docs/types/types.DbTypes.html +1 -1
  175. package/docs/types/types.DreamAssociationMetadata.html +1 -1
  176. package/docs/types/types.DreamAttributes.html +1 -1
  177. package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
  178. package/docs/types/types.DreamClassColumn.html +1 -1
  179. package/docs/types/types.DreamColumn.html +1 -1
  180. package/docs/types/types.DreamColumnNames.html +1 -1
  181. package/docs/types/types.DreamLogLevel.html +1 -1
  182. package/docs/types/types.DreamLogger.html +2 -2
  183. package/docs/types/types.DreamModelSerializerType.html +1 -1
  184. package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
  185. package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
  186. package/docs/types/types.DreamParamSafeAttributes.html +1 -1
  187. package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
  188. package/docs/types/types.DreamSerializable.html +1 -1
  189. package/docs/types/types.DreamSerializableArray.html +1 -1
  190. package/docs/types/types.DreamSerializerKey.html +1 -1
  191. package/docs/types/types.DreamSerializers.html +1 -1
  192. package/docs/types/types.DreamVirtualColumns.html +1 -1
  193. package/docs/types/types.DurationUnit.html +1 -1
  194. package/docs/types/types.EncryptAlgorithm.html +1 -1
  195. package/docs/types/types.HasManyStatement.html +1 -1
  196. package/docs/types/types.HasOneStatement.html +1 -1
  197. package/docs/types/types.Hyphenized.html +1 -1
  198. package/docs/types/types.Pascalized.html +1 -1
  199. package/docs/types/types.PrimaryKeyType.html +1 -1
  200. package/docs/types/types.RoundingPrecision.html +1 -1
  201. package/docs/types/types.SerializerCasing.html +1 -1
  202. package/docs/types/types.SimpleObjectSerializerType.html +1 -1
  203. package/docs/types/types.Snakeified.html +1 -1
  204. package/docs/types/types.StrictInterface.html +1 -1
  205. package/docs/types/types.UpdateableAssociationProperties.html +1 -1
  206. package/docs/types/types.UpdateableProperties.html +1 -1
  207. package/docs/types/types.ValidationType.html +1 -1
  208. package/docs/types/types.ViewModel.html +2 -2
  209. package/docs/types/types.ViewModelClass.html +1 -1
  210. package/docs/types/types.WeekdayName.html +1 -1
  211. package/docs/types/types.WhereStatementForDream.html +1 -1
  212. package/docs/types/types.WhereStatementForDreamClass.html +1 -1
  213. package/docs/variables/index.DreamConst.html +1 -1
  214. package/docs/variables/index.ops.html +1 -1
  215. package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
  216. package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
  217. package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
  218. package/docs/variables/system.primaryKeyTypes.html +1 -1
  219. package/package.json +5 -5
@@ -6,6 +6,7 @@ import Encrypt from '../encrypt/index.js';
6
6
  import DreamAppInitMissingCallToLoadModels from '../errors/dream-app/DreamAppInitMissingCallToLoadModels.js';
7
7
  import DreamAppInitMissingMissingProjectRoot from '../errors/dream-app/DreamAppInitMissingMissingProjectRoot.js';
8
8
  import DreamAppInitMissingPackageManager from '../errors/dream-app/DreamAppInitMissingPackageManager.js';
9
+ import MissingDbSslDirective from '../errors/dream-app/MissingDbSslDirective.js';
9
10
  import autogeneratedFileDisclaimer from '../helpers/cli/autoGeneratedFileDisclaimer.js';
10
11
  import modelClassNameFrom from '../helpers/cli/modelClassNameFrom.js';
11
12
  import EnvInternal from '../helpers/EnvInternal.js';
@@ -345,14 +346,16 @@ A new key can also be generated from the CLI:
345
346
  case 'bypassDeprecationChecks':
346
347
  this._bypassDeprecationChecks = options;
347
348
  break;
348
- case 'db':
349
- if (typeof options === 'string') {
350
- this._dbCredentials[options] = secondaryOptions;
351
- }
352
- else {
353
- this._dbCredentials['default'] = options;
349
+ case 'db': {
350
+ const connectionName = typeof options === 'string' ? options : 'default';
351
+ const credentialOptions = (typeof options === 'string' ? secondaryOptions : options);
352
+ assertDbCredentialTlsDirective(credentialOptions.primary, connectionName, 'primary');
353
+ if (credentialOptions.replica) {
354
+ assertDbCredentialTlsDirective(credentialOptions.replica, connectionName, 'replica');
354
355
  }
356
+ this._dbCredentials[connectionName] = credentialOptions;
355
357
  break;
358
+ }
356
359
  case 'encryption':
357
360
  this._encryption = options;
358
361
  break;
@@ -412,6 +415,11 @@ A new key can also be generated from the CLI:
412
415
  }
413
416
  }
414
417
  }
418
+ function assertDbCredentialTlsDirective(credential, connectionName, credentialKey) {
419
+ if (credential.ssl === undefined && !credential.useSsl) {
420
+ throw new MissingDbSslDirective(connectionName, credentialKey);
421
+ }
422
+ }
415
423
  function loggerArgToString(arg) {
416
424
  if (typeof arg === 'string')
417
425
  return arg;
@@ -1,5 +1,5 @@
1
1
  import DecryptionError from '../errors/encrypt/DecryptionError.js';
2
- import DecryptionWithRotationError from '../errors/encrypt/DecryptionWithRotationError.js';
2
+ import DecryptionRotationError from '../errors/encrypt/DecryptionRotationError.js';
3
3
  import MissingEncryptionKey from '../errors/encrypt/MissingEncryptionKey.js';
4
4
  import decryptAESGCM from './algorithms/aes-gcm/decryptAESGCM.js';
5
5
  import encryptAESGCM from './algorithms/aes-gcm/encryptAESGCM.js';
@@ -34,7 +34,7 @@ export default class Encrypt {
34
34
  *
35
35
  * **Three-arg form** (rotation): tries the current key first; on
36
36
  * `DecryptionError` falls back to the legacy key. If both fail, throws
37
- * `DecryptionWithRotationError` carrying both per-key errors. A
37
+ * `DecryptionRotationError` carrying both per-key errors. A
38
38
  * `DecryptionParseError` from the current key is **not** retried — the
39
39
  * cipher already matched, so a parse failure means the encrypted format
40
40
  * is wrong (an app bug), not a wrong key.
@@ -44,7 +44,7 @@ export default class Encrypt {
44
44
  * @throws MissingEncryptionKey
45
45
  * @throws DecryptionError
46
46
  * @throws DecryptionParseError
47
- * @throws DecryptionWithRotationError
47
+ * @throws DecryptionRotationError
48
48
  */
49
49
  static decrypt(encrypted, { algorithm, key }, legacyOpts) {
50
50
  if (legacyOpts)
@@ -81,7 +81,7 @@ export default class Encrypt {
81
81
  catch (err) {
82
82
  if (!(err instanceof DecryptionError))
83
83
  throw err;
84
- throw new DecryptionWithRotationError(currentKeyError, err);
84
+ throw new DecryptionRotationError(currentKeyError, err);
85
85
  }
86
86
  }
87
87
  /**
@@ -117,7 +117,7 @@ export default class Encrypt {
117
117
  * not forced to re-authenticate.
118
118
  * - For `@Encrypted` columns: until every existing row has been
119
119
  * re-encrypted under the new key. Dropping `legacy` early will cause
120
- * `DecryptionWithRotationError` on any not-yet-rewritten row.
120
+ * `DecryptionRotationError` on any not-yet-rewritten row.
121
121
  */
122
122
  static generateKey(algorithm) {
123
123
  switch (algorithm) {
@@ -0,0 +1,29 @@
1
+ export default class MissingDbSslDirective extends Error {
2
+ connectionName;
3
+ credentialKey;
4
+ constructor(connectionName, credentialKey) {
5
+ super();
6
+ this.connectionName = connectionName;
7
+ this.credentialKey = credentialKey;
8
+ }
9
+ get message() {
10
+ return `
11
+ DreamApp refused to register a db credential without an explicit TLS
12
+ directive. Every \`SingleDbCredential\` passed to \`app.set('db', ...)\`
13
+ must set one of:
14
+
15
+ ssl: { rejectUnauthorized: true } // verified TLS (system CA)
16
+ ssl: { rejectUnauthorized: true, ca: <pem> } // verified TLS (private CA)
17
+ ssl: { rejectUnauthorized: false } // unverified TLS
18
+ ssl: false // TLS disabled
19
+ useSsl: true // legacy, deprecated
20
+
21
+ Omitting the directive used to silently disable TLS. Throwing here
22
+ turns the safety question into a deliberate decision at the call
23
+ site, so a credential cannot reach production with TLS off by accident.
24
+
25
+ connection: ${this.connectionName}
26
+ credential: ${this.credentialKey}
27
+ `;
28
+ }
29
+ }
@@ -1,4 +1,4 @@
1
- export default class DecryptionWithRotationError extends Error {
1
+ export default class DecryptionRotationError extends Error {
2
2
  currentKeyError;
3
3
  legacyKeyError;
4
4
  constructor(currentKeyError, legacyKeyError) {
@@ -7,6 +7,7 @@ import absoluteDreamPath from '../path/absoluteDreamPath.js';
7
7
  import snakeify from '../snakeify.js';
8
8
  import standardizeFullyQualifiedModelName from '../standardizeFullyQualifiedModelName.js';
9
9
  import uniq from '../uniq.js';
10
+ import parseAttribute from './parseAttribute.js';
10
11
  /**
11
12
  * Column names that are automatically emitted by the model generator (and
12
13
  * the migration generator). When the user passes any of these explicitly,
@@ -35,8 +36,17 @@ export default function generateDreamContent(options) {
35
36
  const columnsForAttributes = config.isSTI
36
37
  ? options.columnsWithTypes
37
38
  : filterAutoGeneratedTimestampColumns(options.columnsWithTypes);
38
- const baseImports = createImportConfig(config, options, { includeSoftDelete });
39
39
  const attributesResult = processAttributes(columnsForAttributes, config.modelClassName);
40
+ // Decorators is only referenced via `@deco.*` annotations on fields (e.g.,
41
+ // BelongsTo, Encrypted). If the generated body emits none of those, the
42
+ // import + declaration would trigger lint errors for unused identifiers,
43
+ // so emit them commented out instead — keeps the boilerplate present for
44
+ // the developer to uncomment when they add their first decorator.
45
+ const decoratorsInUse = attributesResult.formattedDecorators.length > 0;
46
+ const baseImports = createImportConfig(config, options, {
47
+ includeSoftDelete,
48
+ includeDecorators: decoratorsInUse,
49
+ });
40
50
  const allImports = {
41
51
  ...baseImports,
42
52
  modelImportStatements: [...baseImports.modelImportStatements, ...attributesResult.imports],
@@ -48,9 +58,14 @@ export default function generateDreamContent(options) {
48
58
  const fieldsSection = buildFieldsSection(config, attributesResult, {
49
59
  includeDeletedAt: includeSoftDelete || hasExplicitDeletedAt,
50
60
  });
61
+ const decoBlock = decoratorsInUse
62
+ ? `const deco = new Decorators<typeof ${config.modelClassName}>()`
63
+ : `// Uncomment when adding decorators (@deco.BelongsTo, @deco.Validates, etc.):
64
+ // import { Decorators } from '@rvoh/dream'
65
+ // const deco = new Decorators<typeof ${config.modelClassName}>()`;
51
66
  return `${importSection}
52
67
 
53
- const deco = new Decorators<typeof ${config.modelClassName}>()
68
+ ${decoBlock}
54
69
 
55
70
  ${classDeclaration}
56
71
  ${tableMethod}${serializersMethod}${fieldsSection}
@@ -78,9 +93,11 @@ export function createModelConfig(options) {
78
93
  tableName,
79
94
  };
80
95
  }
81
- export function createImportConfig(config, options, { includeSoftDelete }) {
96
+ export function createImportConfig(config, options, { includeSoftDelete, includeDecorators = true }) {
82
97
  const dreamTypeImports = ['DreamColumn'];
83
- const dreamImports = ['Decorators'];
98
+ const dreamImports = [];
99
+ if (includeDecorators)
100
+ dreamImports.push('Decorators');
84
101
  if (options.serializer) {
85
102
  dreamTypeImports.push('DreamSerializers');
86
103
  }
@@ -119,32 +136,41 @@ export function processAttributes(columnsWithTypes, modelClassName) {
119
136
  };
120
137
  }
121
138
  export function processAttribute(attribute, modelClassName) {
122
- const [attributeName, attributeType, ...descriptors] = attribute.split(':');
123
- if (attributeName === undefined)
139
+ const [rawName, rawType] = attribute.split(':');
140
+ if (rawName === undefined)
124
141
  return { content: '', imports: [] };
125
- if (!attributeType) {
126
- throw new Error(`must pass a column type for ${attributeName} (i.e. ${attributeName}:string)`);
142
+ if (!rawType) {
143
+ throw new Error(`must pass a column type for ${rawName} (i.e. ${rawName}:string)`);
127
144
  }
128
- const processedAttrType = camelize(attributeType).toLowerCase();
129
- switch (processedAttrType) {
145
+ const parsed = parseAttribute(attribute);
146
+ // Malformed-but-parseable shapes (e.g., `Model@:belongs_to` with an empty
147
+ // alias) yield no content rather than throwing — the call site has already
148
+ // validated the basic name/type segments above.
149
+ if (!parsed)
150
+ return { content: '', imports: [] };
151
+ switch (parsed.normalizedAttributeType) {
130
152
  case 'belongsto':
131
- return createBelongsToAttribute(attributeName, descriptors, modelClassName);
153
+ return createBelongsToAttribute(parsed.rawAttributeName, modelClassName, {
154
+ aliasName: parsed.aliasName,
155
+ isOptional: parsed.isOptional,
156
+ });
132
157
  case 'hasone':
133
158
  case 'hasmany':
134
159
  return { content: '', imports: [] };
135
160
  case 'encrypted':
136
- return createEncryptedAttribute(attributeName, attribute, modelClassName);
161
+ return createEncryptedAttribute(parsed.rawAttributeName, attribute, modelClassName);
137
162
  default:
138
- return createRegularAttribute(attributeName, attribute, modelClassName);
163
+ return createRegularAttribute(parsed.rawAttributeName, attribute, modelClassName);
139
164
  }
140
165
  }
141
- export function createBelongsToAttribute(attributeName, descriptors, modelClassName) {
142
- const fullyQualifiedAssociatedModelName = standardizeFullyQualifiedModelName(attributeName);
166
+ export function createBelongsToAttribute(fullyQualifiedModelInput, modelClassName, { aliasName, isOptional = false, } = {}) {
167
+ const fullyQualifiedAssociatedModelName = standardizeFullyQualifiedModelName(fullyQualifiedModelInput);
143
168
  const associationModelName = globalClassNameFromFullyQualifiedModelName(fullyQualifiedAssociatedModelName);
144
169
  const associationImportStatement = importStatementForModel(fullyQualifiedAssociatedModelName);
145
- const associationName = camelize(fullyQualifiedAssociatedModelName.split('/').pop());
170
+ const associationName = aliasName
171
+ ? camelize(aliasName)
172
+ : camelize(fullyQualifiedAssociatedModelName.split('/').pop());
146
173
  const associationForeignKey = `${associationName}Id`;
147
- const isOptional = descriptors.includes('optional');
148
174
  const content = `
149
175
  @deco.BelongsTo('${fullyQualifiedAssociatedModelName}', { on: '${associationForeignKey}'${isOptional ? ', optional: true' : ''} })
150
176
  public ${associationName}: ${associationModelName}${isOptional ? ' | null' : ''}
@@ -17,14 +17,23 @@ export default function generateFactoryContent({ fullyQualifiedModelName, column
17
17
  const attributeDefaults = [];
18
18
  let counterVariableIncremented = false;
19
19
  for (const attribute of columnsWithTypes) {
20
- const [attributeName, _attributeType, ...descriptors] = attribute.split(':');
21
- if (attributeName === undefined)
20
+ const [rawSegmentOne, _attributeType, ...descriptors] = attribute.split(':');
21
+ if (rawSegmentOne === undefined)
22
22
  continue;
23
23
  if (_attributeType === undefined)
24
24
  continue;
25
25
  const optional = optionalFromDescriptors(descriptors);
26
26
  if (optional)
27
27
  continue;
28
+ // Extract optional `@alias` from segment-1 (Model@alias:belongs_to form).
29
+ // Non-association tokens never contain `@`, so this is a no-op for scalars.
30
+ const atIdx = rawSegmentOne.indexOf('@');
31
+ const aliasName = atIdx !== -1 ? rawSegmentOne.slice(atIdx + 1) : undefined;
32
+ const attributeName = atIdx !== -1 ? rawSegmentOne.slice(0, atIdx) : rawSegmentOne;
33
+ if (!attributeName)
34
+ continue;
35
+ if (atIdx !== -1 && !aliasName)
36
+ continue;
28
37
  const attributeVariable = camelize(attributeName.replace(/\//g, ''));
29
38
  if (/^type$/.test(attributeName))
30
39
  continue;
@@ -36,7 +45,9 @@ export default function generateFactoryContent({ fullyQualifiedModelName, column
36
45
  const safeAttributeType = camelize(attributeType)?.toLowerCase();
37
46
  switch (safeAttributeType) {
38
47
  case 'belongsto': {
39
- const attributeVariable = camelize(attributeName.split('/').pop());
48
+ // When `Model@alias:belongs_to`, the factory property uses the alias;
49
+ // otherwise it uses the model's last namespace segment (legacy form).
50
+ const attributeVariable = aliasName ? camelize(aliasName) : camelize(attributeName.split('/').pop());
40
51
  const fullyQualifiedAssociatedModelName = standardizeFullyQualifiedModelName(attributeName);
41
52
  const associationModelName = globalClassNameFromFullyQualifiedModelName(fullyQualifiedAssociatedModelName);
42
53
  const associationFactoryImportStatement = `import create${associationModelName} from '${absoluteDreamPath('factories', fullyQualifiedAssociatedModelName)}'`;
@@ -66,11 +77,27 @@ export default function generateFactoryContent({ fullyQualifiedModelName, column
66
77
  counterVariableIncremented = true;
67
78
  break;
68
79
  case 'enum':
69
- attributeDefaults.push(`${attributeVariable}: '${(descriptors.at(-1) || '<tbd>').split(',')[0]}',`);
70
- break;
71
- case 'enum[]':
72
- attributeDefaults.push(`${attributeVariable}: ['${(descriptors.at(-1) || '<tbd>').split(',')[0]}'],`);
80
+ case 'enum[]': {
81
+ // When the user passed `name:enum:enum_type_name` (reuse form, no
82
+ // inline values), descriptors has exactly one element — the enum
83
+ // type name. The factory cannot see the enum's values, so emit a
84
+ // TS-rejecting placeholder rather than the type name itself (the
85
+ // previous behavior emitted `'enum_type_name'` as the literal value,
86
+ // which compiles in the factory but fails at runtime).
87
+ const isReuseWithoutValues = descriptors.length === 1;
88
+ const isArrayEnum = safeAttributeType === 'enum[]';
89
+ if (isReuseWithoutValues) {
90
+ const enumTypeName = descriptors[0];
91
+ const placeholder = isArrayEnum ? `['TODO']` : `'TODO'`;
92
+ attributeDefaults.push(`// TODO: replace with a value from the \`${enumTypeName}\` enum\n ${attributeVariable}: ${placeholder},`);
93
+ }
94
+ else {
95
+ const firstValue = (descriptors.at(-1) || '<tbd>').split(',')[0];
96
+ const literal = isArrayEnum ? `['${firstValue}']` : `'${firstValue}'`;
97
+ attributeDefaults.push(`${attributeVariable}: ${literal},`);
98
+ }
73
99
  break;
100
+ }
74
101
  case 'integer':
75
102
  attributeDefaults.push(`${attributeVariable}: 1,`);
76
103
  break;
@@ -27,9 +27,19 @@ export default function generateMigrationContent({ connectionName = 'default', t
27
27
  const emitDeletedAtColumn = !altering && (softDelete || userExplicitlyPassedDeletedAt);
28
28
  const { columnDefs, columnDrops, indexDefs, indexDrops } = processedColumnsWithTypes.reduce((acc, attributeDeclaration) => {
29
29
  const { columnDefs, columnDrops, indexDefs, indexDrops } = acc;
30
- const [nonStandardAttributeName, _attributeType, ...descriptors] = attributeDeclaration.split(':');
30
+ const [rawSegmentOne, _attributeType, ...descriptors] = attributeDeclaration.split(':');
31
+ if (!rawSegmentOne)
32
+ return acc;
33
+ // Extract optional `@alias` from segment-1 (Model@alias:belongs_to form).
34
+ // The model name (without alias) is what gets standardized / referenced
35
+ // by table lookup; the alias drives the column / index naming when present.
36
+ const atIdx = rawSegmentOne.indexOf('@');
37
+ const aliasName = atIdx !== -1 ? rawSegmentOne.slice(atIdx + 1) : undefined;
38
+ const nonStandardAttributeName = atIdx !== -1 ? rawSegmentOne.slice(0, atIdx) : rawSegmentOne;
31
39
  if (!nonStandardAttributeName)
32
40
  return acc;
41
+ if (atIdx !== -1 && !aliasName)
42
+ return acc;
33
43
  /**
34
44
  * Automatically set email columns to citext since different casings of
35
45
  * email address are the same email address
@@ -64,8 +74,14 @@ export default function generateMigrationContent({ connectionName = 'default', t
64
74
  primaryKeyType,
65
75
  omitInlineNonNull,
66
76
  originalAssociationName: nonStandardAttributeName,
77
+ aliasName,
67
78
  }));
68
- attributeName = snakeify(nonStandardAttributeName.split('/').pop());
79
+ // Resolve the actual column name used for index + drop emission.
80
+ // When an alias is present (Model@alias:belongs_to), the column is
81
+ // `${alias}_id`; otherwise it's derived from the model's last segment.
82
+ attributeName = aliasName
83
+ ? snakeify(aliasName)
84
+ : snakeify(nonStandardAttributeName.split('/').pop());
69
85
  attributeName = associationNameToForeignKey(attributeName);
70
86
  break;
71
87
  case 'enum':
@@ -268,6 +284,7 @@ function generateColumnStr(attributeName, attributeType, descriptors, { omitInli
268
284
  const isUnique = /(email|token|uuid)$/.test(attributeName);
269
285
  const hasExtraValues = providedDefault || notNull || isUnique;
270
286
  const isArray = /\[\]$/.test(attributeType);
287
+ const needsJsonDefault = notNull && !isArray && (attributeType === 'jsonb' || attributeType === 'json');
271
288
  if (hasExtraValues)
272
289
  returnStr += ', col => col';
273
290
  if (notNull)
@@ -278,6 +295,12 @@ function generateColumnStr(attributeName, attributeType, descriptors, { omitInli
278
295
  returnStr += `.defaultTo('${providedDefault}')`;
279
296
  else if (isArray)
280
297
  returnStr += `.defaultTo('{}')`;
298
+ // jsonb / json columns get an empty-object default so calling create() on a
299
+ // model without explicitly setting the column doesn't trip NOT NULL. Mirrors
300
+ // the existing boolean → false and array → '{}' auto-defaults. Optional
301
+ // jsonb columns skip the default since null is the intended initial state.
302
+ else if (needsJsonDefault)
303
+ returnStr += `.defaultTo(sql\`'{}'::${attributeType}\`)`;
281
304
  returnStr = `${returnStr})`;
282
305
  if (attributeName === STI_TYPE_COLUMN_NAME)
283
306
  returnStr = `// CONSIDER: when using type for STI, always use an enum
@@ -306,11 +329,14 @@ function attributeTypeString(attributeType) {
306
329
  }
307
330
  }
308
331
  }
309
- function generateBelongsToStr(connectionName, associationName, { primaryKeyType, omitInlineNonNull: optional = false, originalAssociationName, }) {
332
+ function generateBelongsToStr(connectionName, associationName, { primaryKeyType, omitInlineNonNull: optional = false, originalAssociationName, aliasName, }) {
310
333
  const dbDriverClass = Query.dbDriverClass(connectionName);
311
334
  const dataType = dbDriverClass.foreignKeyTypeFromPrimaryKey(primaryKeyType);
312
335
  const references = lookupReferencesTable(associationName, originalAssociationName);
313
- return `.addColumn('${associationNameToForeignKey(associationName.split('/').pop())}', '${dataType}', col => col.references('${references}.id').onDelete('restrict')${optional ? '' : '.notNull()'})`;
336
+ // When the user passed `Model@alias:belongs_to`, the column name comes from
337
+ // the alias; otherwise it's the model's last segment (existing behavior).
338
+ const columnNameSource = aliasName ? snakeify(aliasName) : associationName.split('/').pop();
339
+ return `.addColumn('${associationNameToForeignKey(columnNameSource)}', '${dataType}', col => col.references('${references}.id').onDelete('restrict')${optional ? '' : '.notNull()'})`;
314
340
  }
315
341
  function generateIdStr({ primaryKeyType }) {
316
342
  switch (primaryKeyType) {
@@ -0,0 +1,61 @@
1
+ import camelize from '../camelize.js';
2
+ /**
3
+ * Parse a single `columnsWithTypes` CLI token into its structural pieces.
4
+ *
5
+ * Centralizes the splitting + normalization logic shared across the model
6
+ * generator (`generateDreamContent`), the migration generator
7
+ * (`generateMigrationContent`), the factory generator
8
+ * (`generateFactoryContent`), and Psychic's resource/controller generators.
9
+ * Keeps the shared layer thin: consumers handle their own coercions (e.g.,
10
+ * migration's `email$ → citext`) and filters (e.g., Psychic's `_type`/`_id`
11
+ * exclusion).
12
+ *
13
+ * Returns `null` for malformed tokens (missing name or type).
14
+ *
15
+ * Supported forms (segment-1 only — see `rawAttributeType` for the full
16
+ * vocabulary of types/associations recognized by individual generators):
17
+ *
18
+ * - `name:type` → standard column
19
+ * - `name:type:optional` → nullable column
20
+ * - `Model:belongs_to[:optional]` → association with model-derived alias
21
+ * - `Model@alias:belongs_to[:optional]` → association with explicit alias
22
+ */
23
+ export default function parseAttribute(attribute) {
24
+ const segments = attribute.split(':');
25
+ const rawSegmentOne = segments[0];
26
+ const rawAttributeType = segments[1];
27
+ const descriptors = segments.slice(2);
28
+ if (!rawSegmentOne || !rawAttributeType)
29
+ return null;
30
+ // Split segment-1 on `@` to extract an optional alias. Empty alias after `@`
31
+ // (e.g., `Model@:belongs_to`) is treated as malformed.
32
+ let rawAttributeName = rawSegmentOne;
33
+ let aliasName;
34
+ const atIndex = rawSegmentOne.indexOf('@');
35
+ if (atIndex !== -1) {
36
+ rawAttributeName = rawSegmentOne.slice(0, atIndex);
37
+ const rawAlias = rawSegmentOne.slice(atIndex + 1);
38
+ if (!rawAttributeName || !rawAlias)
39
+ return null;
40
+ aliasName = rawAlias;
41
+ }
42
+ // Pop trailing `optional` keyword off the descriptors list. Mirrors
43
+ // `optionalFromDescriptors` in generateMigrationContent.ts so the keyword
44
+ // behaves identically regardless of which consumer parses the token.
45
+ let isOptional = false;
46
+ if (descriptors[descriptors.length - 1] === 'optional') {
47
+ descriptors.pop();
48
+ isOptional = true;
49
+ }
50
+ const normalizedAttributeType = camelize(rawAttributeType).toLowerCase();
51
+ const isArray = /\[\]$/.test(rawAttributeType);
52
+ return {
53
+ rawAttributeName,
54
+ aliasName,
55
+ rawAttributeType,
56
+ normalizedAttributeType,
57
+ descriptors,
58
+ isOptional,
59
+ isArray,
60
+ };
61
+ }
@@ -7,6 +7,9 @@ export { default as DataIncompatibleWithDatabaseField } from '../errors/db/DataI
7
7
  export { default as DataTypeColumnTypeMismatch } from '../errors/db/DataTypeColumnTypeMismatch.js';
8
8
  export { default as NotNullViolation } from '../errors/db/NotNullViolation.js';
9
9
  export { default as GlobalNameNotSet } from '../errors/dream-app/GlobalNameNotSet.js';
10
+ export { default as DecryptionError } from '../errors/encrypt/DecryptionError.js';
11
+ export { default as DecryptionParseError } from '../errors/encrypt/DecryptionParseError.js';
12
+ export { default as DecryptionRotationError } from '../errors/encrypt/DecryptionRotationError.js';
10
13
  export { default as RecordNotFound } from '../errors/RecordNotFound.js';
11
14
  export { default as MissingSerializersDefinition } from '../errors/serializers/MissingSerializersDefinition.js';
12
15
  export { default as ValidationError } from '../errors/ValidationError.js';
@@ -14,7 +14,7 @@ export type SpawnOptions = Omit<NodeSpawnOptions, 'shell'> & {
14
14
  args?: string[];
15
15
  };
16
16
  export declare const CLI_INDENT = " ";
17
- export declare const baseColumnsWithTypesDescription = "space separated snake-case (except for belongs_to model name) properties like this:\n title:citext subtitle:string body_markdown:text style:enum:post_styles:formal,informal User:belongs_to\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 \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";
17
+ export declare const baseColumnsWithTypesDescription = "space separated snake-case (except for belongs_to model name, which may take an @alias suffix to rename the FK) properties like this:\n title:citext subtitle:string body_markdown:text style:enum:post_styles:formal,informal User:belongs_to\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 \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";
18
18
  export default class DreamCLI {
19
19
  /**
20
20
  * Starts the Dream console
@@ -473,14 +473,13 @@ export default class KyselyQueryDriver<DreamInstance extends Dream> extends Quer
473
473
  * Resolve the value passed to `pg.Pool`'s `ssl` field for a given credential.
474
474
  *
475
475
  * Precedence:
476
- * 1. If `connectionConf.ssl` is set (boolean or `tls.ConnectionOptions`),
477
- * pass it straight through to `pg`. This is the verified-TLS path —
478
- * apps configure `{ rejectUnauthorized: true, ca: <bundle> }` for
479
- * authenticated TLS against a private PKI, or `true` to use Node's
480
- * default verification against the system CA store.
481
- * 2. Else if `connectionConf.useSsl` is `true`, fall back to
476
+ * 1. If `connectionConf.ssl` is set (object or explicit `false`), pass it
477
+ * straight through to `pg`.
478
+ * 2. Else if `connectionConf.useSsl` is `true` (deprecated), fall back to
482
479
  * `{ rejectUnauthorized: false }` — encrypted but **not** authenticated.
483
- * Preserved for back-compat; new code should set `ssl` explicitly.
484
- * 3. Else disable TLS.
480
+ *
481
+ * `assertDbCredentialTlsDirective` (in `dream-app`) throws at
482
+ * `app.set('db', ...)` time when both `ssl` and `useSsl` are unset, so this
483
+ * resolver never sees the "neither directive" state.
485
484
  */
486
- export declare function resolvePostgresSsl(connectionConf: SingleDbCredential): boolean | TlsConnectionOptions;
485
+ export declare function resolvePostgresSsl(connectionConf: SingleDbCredential): TlsConnectionOptions | false;
@@ -213,27 +213,35 @@ export interface SingleDbCredential {
213
213
  /**
214
214
  * @deprecated Use `ssl` instead.
215
215
  *
216
- * The legacy boolean opt-in for Postgres TLS. When `true` (and `ssl` is not
216
+ * Legacy boolean opt-in for Postgres TLS. When `true` (and `ssl` is not
217
217
  * set), Dream connects with `{ rejectUnauthorized: false }` — TLS is on but
218
- * the server certificate is not verified. The new `ssl` field accepts a
219
- * full `tls.ConnectionOptions` object so callers can opt into verified TLS
220
- * (`ssl: { rejectUnauthorized: true, ca: readFileSync('ca.pem') }`) or use
221
- * Node's defaults (`ssl: true`). This field is preserved for back-compat
222
- * and will be removed in a future major version.
218
+ * the server certificate is not verified. Preserved for back-compat and
219
+ * will be removed in a future major version. New code should set `ssl`
220
+ * directly.
223
221
  */
224
222
  useSsl?: boolean;
225
223
  /**
226
- * Optional explicit TLS configuration passed straight through to `pg.Pool`'s
227
- * `ssl` field. Takes precedence over the deprecated `useSsl` when provided.
224
+ * TLS configuration passed straight through to `pg.Pool`'s `ssl` field.
225
+ * Takes precedence over the deprecated `useSsl` when provided.
228
226
  *
229
- * - `true` lets `pg` use Node's defaults (verified TLS against the system CA).
230
- * - `false` disables TLS.
231
- * - An object is `tls.ConnectionOptions` set `rejectUnauthorized: true` plus
232
- * a `ca` bundle for verified TLS against a private PKI; set
233
- * `rejectUnauthorized: false` only if you accept opportunistic-TLS-without-
234
- * authentication (the historical default produced by `useSsl: true` alone).
227
+ * Set `rejectUnauthorized: true` (Node's own default) for verified TLS
228
+ * against the system CA store — the right choice for managed providers that
229
+ * present a public-CA-signed certificate (Supabase, Neon, Render, Azure
230
+ * Database for PostgreSQL on Flexible Server, etc.).
231
+ *
232
+ * For providers that present a private-CA certificate (AWS RDS,
233
+ * GCP Cloud SQL), add a `ca` bundle:
234
+ * `ssl: { rejectUnauthorized: true, ca: readFileSync('rds-ca.pem') }`.
235
+ *
236
+ * For providers that present a self-signed certificate (Heroku Hobby,
237
+ * some local docker images), set `rejectUnauthorized: false` — encrypted
238
+ * but unauthenticated.
239
+ *
240
+ * Set `false` to disable TLS entirely. Omitting `ssl` (and `useSsl`) throws
241
+ * at `app.set('db', ...)` time so the safety question is a deliberate
242
+ * decision at the call site rather than a silent default.
235
243
  */
236
- ssl?: boolean | TlsConnectionOptions;
244
+ ssl?: TlsConnectionOptions | false;
237
245
  }
238
246
  export type DreamLogger = {
239
247
  info: (...args: any[]) => void;
@@ -12,7 +12,7 @@ export default class Encrypt {
12
12
  *
13
13
  * **Three-arg form** (rotation): tries the current key first; on
14
14
  * `DecryptionError` falls back to the legacy key. If both fail, throws
15
- * `DecryptionWithRotationError` carrying both per-key errors. A
15
+ * `DecryptionRotationError` carrying both per-key errors. A
16
16
  * `DecryptionParseError` from the current key is **not** retried — the
17
17
  * cipher already matched, so a parse failure means the encrypted format
18
18
  * is wrong (an app bug), not a wrong key.
@@ -22,7 +22,7 @@ export default class Encrypt {
22
22
  * @throws MissingEncryptionKey
23
23
  * @throws DecryptionError
24
24
  * @throws DecryptionParseError
25
- * @throws DecryptionWithRotationError
25
+ * @throws DecryptionRotationError
26
26
  */
27
27
  static decrypt<RetType>(encrypted: string, { algorithm, key }: DecryptOptions, legacyOpts?: DecryptOptions): RetType | null;
28
28
  private static attemptDecryptionWithLegacyKeys;
@@ -59,7 +59,7 @@ export default class Encrypt {
59
59
  * not forced to re-authenticate.
60
60
  * - For `@Encrypted` columns: until every existing row has been
61
61
  * re-encrypted under the new key. Dropping `legacy` early will cause
62
- * `DecryptionWithRotationError` on any not-yet-rewritten row.
62
+ * `DecryptionRotationError` on any not-yet-rewritten row.
63
63
  */
64
64
  static generateKey(algorithm: EncryptAlgorithm): string;
65
65
  static validateKey(base64EncodedKey: string, algorithm: EncryptAlgorithm): boolean;
@@ -0,0 +1,6 @@
1
+ export default class MissingDbSslDirective extends Error {
2
+ private connectionName;
3
+ private credentialKey;
4
+ constructor(connectionName: string, credentialKey: 'primary' | 'replica');
5
+ get message(): string;
6
+ }
@@ -1,5 +1,5 @@
1
1
  import DecryptionError from './DecryptionError.js';
2
- export default class DecryptionWithRotationError extends Error {
2
+ export default class DecryptionRotationError extends Error {
3
3
  readonly currentKeyError: DecryptionError;
4
4
  readonly legacyKeyError: DecryptionError;
5
5
  constructor(currentKeyError: DecryptionError, legacyKeyError: DecryptionError);
@@ -37,15 +37,19 @@ export interface AttributeProcessingResult {
37
37
  }
38
38
  export default function generateDreamContent(options: GenerateDreamContentOptions): string;
39
39
  export declare function createModelConfig(options: GenerateDreamContentOptions): ModelConfig;
40
- export declare function createImportConfig(config: ModelConfig, options: GenerateDreamContentOptions, { includeSoftDelete }: {
40
+ export declare function createImportConfig(config: ModelConfig, options: GenerateDreamContentOptions, { includeSoftDelete, includeDecorators }: {
41
41
  includeSoftDelete: boolean;
42
+ includeDecorators?: boolean;
42
43
  }): ImportConfig;
43
44
  export declare function processAttributes(columnsWithTypes: string[], modelClassName: string): AttributeProcessingResult & {
44
45
  formattedFields: string;
45
46
  formattedDecorators: string;
46
47
  };
47
48
  export declare function processAttribute(attribute: string, modelClassName: string): AttributeProcessingResult;
48
- export declare function createBelongsToAttribute(attributeName: string, descriptors: string[], modelClassName: string): AttributeProcessingResult;
49
+ export declare function createBelongsToAttribute(fullyQualifiedModelInput: string, modelClassName: string, { aliasName, isOptional, }?: {
50
+ aliasName?: string | undefined;
51
+ isOptional?: boolean;
52
+ }): AttributeProcessingResult;
49
53
  export declare function createEncryptedAttribute(attributeName: string, attribute: string, modelClassName: string): AttributeProcessingResult;
50
54
  export declare function createRegularAttribute(attributeName: string, attribute: string, modelClassName: string): AttributeProcessingResult;
51
55
  export {};