@rvoh/dream 2.24.0 → 2.24.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 (196) hide show
  1. package/dist/cjs/src/cli/index.js +29 -9
  2. package/dist/cjs/src/errors/InvalidMigrationTableName.js +30 -0
  3. package/dist/cjs/src/errors/NoColumnsToAlterMigration.js +35 -3
  4. package/dist/cjs/src/helpers/cli/generateMigration.js +33 -3
  5. package/dist/cjs/src/helpers/cli/generateMigrationContent.js +3 -3
  6. package/dist/esm/src/cli/index.js +29 -9
  7. package/dist/esm/src/errors/InvalidMigrationTableName.js +30 -0
  8. package/dist/esm/src/errors/NoColumnsToAlterMigration.js +35 -3
  9. package/dist/esm/src/helpers/cli/generateMigration.js +33 -3
  10. package/dist/esm/src/helpers/cli/generateMigrationContent.js +3 -3
  11. package/dist/types/src/cli/index.d.ts +8 -0
  12. package/dist/types/src/errors/InvalidMigrationTableName.d.ts +10 -0
  13. package/dist/types/src/errors/NoColumnsToAlterMigration.d.ts +11 -1
  14. package/dist/types/src/helpers/cli/generateMigrationContent.d.ts +18 -4
  15. package/docs/classes/db.DreamMigrationHelpers.html +11 -11
  16. package/docs/classes/db.KyselyQueryDriver.html +40 -40
  17. package/docs/classes/db.PostgresQueryDriver.html +41 -41
  18. package/docs/classes/db.QueryDriverBase.html +39 -39
  19. package/docs/classes/errors.CheckConstraintViolation.html +3 -3
  20. package/docs/classes/errors.ColumnOverflow.html +3 -3
  21. package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
  22. package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
  23. package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
  24. package/docs/classes/errors.DecryptionError.html +2 -2
  25. package/docs/classes/errors.DecryptionParseError.html +2 -2
  26. package/docs/classes/errors.DecryptionRotationError.html +3 -3
  27. package/docs/classes/errors.GlobalNameNotSet.html +3 -3
  28. package/docs/classes/errors.InvalidCalendarDate.html +2 -2
  29. package/docs/classes/errors.InvalidClockTime.html +2 -2
  30. package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
  31. package/docs/classes/errors.InvalidDateTime.html +2 -2
  32. package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
  33. package/docs/classes/errors.NonLoadedAssociation.html +3 -3
  34. package/docs/classes/errors.NotNullViolation.html +3 -3
  35. package/docs/classes/errors.RecordNotFound.html +3 -3
  36. package/docs/classes/errors.ValidationError.html +3 -3
  37. package/docs/classes/index.CalendarDate.html +33 -33
  38. package/docs/classes/index.ClockTime.html +32 -32
  39. package/docs/classes/index.ClockTimeTz.html +35 -35
  40. package/docs/classes/index.DateTime.html +86 -86
  41. package/docs/classes/index.Decorators.html +19 -19
  42. package/docs/classes/index.Dream.html +124 -124
  43. package/docs/classes/index.DreamApp.html +10 -10
  44. package/docs/classes/index.DreamTransaction.html +2 -2
  45. package/docs/classes/index.Env.html +2 -2
  46. package/docs/classes/index.Query.html +62 -62
  47. package/docs/classes/system.CliFileWriter.html +4 -4
  48. package/docs/classes/system.DreamBin.html +2 -2
  49. package/docs/classes/system.DreamCLI.html +7 -7
  50. package/docs/classes/system.DreamImporter.html +2 -2
  51. package/docs/classes/system.DreamLogos.html +2 -2
  52. package/docs/classes/system.DreamSerializerBuilder.html +11 -11
  53. package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
  54. package/docs/classes/system.PathHelpers.html +3 -3
  55. package/docs/classes/utils.Encrypt.html +3 -3
  56. package/docs/classes/utils.Range.html +2 -2
  57. package/docs/functions/db.closeAllDbConnections.html +1 -1
  58. package/docs/functions/db.dreamDbConnections.html +1 -1
  59. package/docs/functions/db.untypedDb.html +1 -1
  60. package/docs/functions/db.validateColumn.html +1 -1
  61. package/docs/functions/db.validateTable.html +1 -1
  62. package/docs/functions/errors.pgErrorType.html +1 -1
  63. package/docs/functions/index.DreamSerializer.html +1 -1
  64. package/docs/functions/index.ObjectSerializer.html +1 -1
  65. package/docs/functions/index.ReplicaSafe.html +1 -1
  66. package/docs/functions/index.STI.html +1 -1
  67. package/docs/functions/index.SoftDelete.html +1 -1
  68. package/docs/functions/utils.camelize.html +1 -1
  69. package/docs/functions/utils.capitalize.html +1 -1
  70. package/docs/functions/utils.cloneDeepSafe.html +1 -1
  71. package/docs/functions/utils.compact.html +1 -1
  72. package/docs/functions/utils.groupBy.html +1 -1
  73. package/docs/functions/utils.hyphenize.html +1 -1
  74. package/docs/functions/utils.intersection.html +1 -1
  75. package/docs/functions/utils.isEmpty.html +1 -1
  76. package/docs/functions/utils.normalizeUnicode.html +1 -1
  77. package/docs/functions/utils.pascalize.html +1 -1
  78. package/docs/functions/utils.percent.html +1 -1
  79. package/docs/functions/utils.range.html +1 -1
  80. package/docs/functions/utils.round.html +1 -1
  81. package/docs/functions/utils.sanitizeString.html +1 -1
  82. package/docs/functions/utils.snakeify.html +1 -1
  83. package/docs/functions/utils.sort.html +1 -1
  84. package/docs/functions/utils.sortBy.html +1 -1
  85. package/docs/functions/utils.sortObjectByKey.html +1 -1
  86. package/docs/functions/utils.sortObjectByValue.html +1 -1
  87. package/docs/functions/utils.uncapitalize.html +1 -1
  88. package/docs/functions/utils.uniq.html +1 -1
  89. package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
  90. package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
  91. package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
  92. package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
  93. package/docs/interfaces/types.BelongsToStatement.html +2 -2
  94. package/docs/interfaces/types.DecoratorContext.html +2 -2
  95. package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
  96. package/docs/interfaces/types.DreamAppOpts.html +2 -2
  97. package/docs/interfaces/types.DreamDbConfig.html +5 -5
  98. package/docs/interfaces/types.DurationObject.html +2 -2
  99. package/docs/interfaces/types.EncryptOptions.html +2 -2
  100. package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
  101. package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
  102. package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
  103. package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
  104. package/docs/types/openapi.OpenapiAllTypes.html +1 -1
  105. package/docs/types/openapi.OpenapiFormats.html +1 -1
  106. package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
  107. package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
  108. package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
  109. package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
  110. package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
  111. package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
  112. package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
  113. package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
  114. package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
  115. package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
  116. package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
  117. package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
  118. package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
  119. package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
  120. package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
  121. package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
  122. package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
  123. package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
  124. package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
  125. package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
  126. package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
  127. package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
  128. package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
  129. package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
  130. package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
  131. package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
  132. package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
  133. package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
  134. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
  135. package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
  136. package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
  137. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
  138. package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
  139. package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
  140. package/docs/types/openapi.OpenapiSchemaString.html +1 -1
  141. package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
  142. package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
  143. package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
  144. package/docs/types/openapi.OpenapiTypeField.html +1 -1
  145. package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
  146. package/docs/types/types.CalendarDateDurationUnit.html +1 -1
  147. package/docs/types/types.CalendarDateObject.html +1 -1
  148. package/docs/types/types.Camelized.html +1 -1
  149. package/docs/types/types.ClockTimeObject.html +1 -1
  150. package/docs/types/types.DbConnectionType.html +1 -1
  151. package/docs/types/types.DbTypes.html +1 -1
  152. package/docs/types/types.DreamAssociationMetadata.html +1 -1
  153. package/docs/types/types.DreamAttributes.html +1 -1
  154. package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
  155. package/docs/types/types.DreamClassColumn.html +1 -1
  156. package/docs/types/types.DreamColumn.html +1 -1
  157. package/docs/types/types.DreamColumnNames.html +1 -1
  158. package/docs/types/types.DreamLogLevel.html +1 -1
  159. package/docs/types/types.DreamLogger.html +2 -2
  160. package/docs/types/types.DreamModelSerializerType.html +1 -1
  161. package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
  162. package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
  163. package/docs/types/types.DreamParamSafeAttributes.html +1 -1
  164. package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
  165. package/docs/types/types.DreamSerializable.html +1 -1
  166. package/docs/types/types.DreamSerializableArray.html +1 -1
  167. package/docs/types/types.DreamSerializerKey.html +1 -1
  168. package/docs/types/types.DreamSerializers.html +1 -1
  169. package/docs/types/types.DreamVirtualColumns.html +1 -1
  170. package/docs/types/types.DurationUnit.html +1 -1
  171. package/docs/types/types.EncryptAlgorithm.html +1 -1
  172. package/docs/types/types.HasManyStatement.html +1 -1
  173. package/docs/types/types.HasOneStatement.html +1 -1
  174. package/docs/types/types.Hyphenized.html +1 -1
  175. package/docs/types/types.Pascalized.html +1 -1
  176. package/docs/types/types.PrimaryKeyType.html +1 -1
  177. package/docs/types/types.RoundingPrecision.html +1 -1
  178. package/docs/types/types.SerializerCasing.html +1 -1
  179. package/docs/types/types.SimpleObjectSerializerType.html +1 -1
  180. package/docs/types/types.Snakeified.html +1 -1
  181. package/docs/types/types.StrictInterface.html +1 -1
  182. package/docs/types/types.UpdateableAssociationProperties.html +1 -1
  183. package/docs/types/types.UpdateableProperties.html +1 -1
  184. package/docs/types/types.ValidationType.html +1 -1
  185. package/docs/types/types.ViewModel.html +2 -2
  186. package/docs/types/types.ViewModelClass.html +1 -1
  187. package/docs/types/types.WeekdayName.html +1 -1
  188. package/docs/types/types.WhereStatementForDream.html +1 -1
  189. package/docs/types/types.WhereStatementForDreamClass.html +1 -1
  190. package/docs/variables/index.DreamConst.html +1 -1
  191. package/docs/variables/index.ops.html +1 -1
  192. package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
  193. package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
  194. package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
  195. package/docs/variables/system.primaryKeyTypes.html +1 -1
  196. package/package.json +1 -1
@@ -105,6 +105,31 @@ ${INDENT}
105
105
  ${INDENT} rename the FK column with Model@alias (snake_case alias becomes the column name):
106
106
  ${INDENT} InternalUser@canceled_by:belongs_to:optional # canceled_by_id column with index
107
107
  ${INDENT} Messaging/Template@template:belongs_to # template_id column (strips the namespace from the column name)`);
108
+ /**
109
+ * The `<migrationName>` argument description for `g:migration`. Exported so
110
+ * that spec/unit/helpers/cli/generateMigration.spec.ts can read the example
111
+ * migration names out of this text and run them through the generator: a help
112
+ * example that drifts from what the generator actually produces then fails the
113
+ * suite instead of shipping.
114
+ */
115
+ export const migrationNameArgumentDescription = `Kebab-case name describing the change. A -to-<table_name>
116
+ ${INDENT}anywhere in the name — not just at the end — auto-generates
117
+ ${INDENT}an add-columns alterTable scaffold for that table;
118
+ ${INDENT}-from-<table_name> generates a remove-columns scaffold, but
119
+ ${INDENT}only when the name contains no -to- at all. The text after
120
+ ${INDENT}the marker's first occurrence is snake-cased and pluralized
121
+ ${INDENT}into the table name, so add-a-to-b-to-cs targets b_to_cs. A
122
+ ${INDENT}hand-written migration must avoid both strings anywhere in
123
+ ${INDENT}its name.
124
+ ${INDENT}
125
+ ${INDENT}Examples:
126
+ ${INDENT} add-phone-to-users phone:encrypted # scaffolds alterTable('users', ...)
127
+ ${INDENT} remove-status-from-posts status:string # scaffolds alterTable('posts', ...)
128
+ ${INDENT} create-many-to-many-posts-users # WRONG: selects 'many_posts_users'
129
+ ${INDENT} # -to- inside "many-to-many" matches: with no columns
130
+ ${INDENT} # this errors; with columns it silently scaffolds it
131
+ ${INDENT} create-posts-users-join-table # correct: no marker match
132
+ ${INDENT} # scaffolds alterTable('<table-name>') to hand-edit`;
108
133
  export default class DreamCLI {
109
134
  /**
110
135
  * Starts the Dream console
@@ -151,22 +176,17 @@ export default class DreamCLI {
151
176
  .description(`Generates a new Kysely migration file for schema changes. Use this for altering existing tables (adding/removing columns, indexes, constraints). Prefer g:resource or g:model when creating a new model, since they generate the migration along with the model, serializer, and spec files.
152
177
  ${INDENT}
153
178
  ${INDENT}Examples:
154
- ${INDENT} # Add columns to an existing table (suffix with -to-<table_name> for auto alterTable scaffolding)
179
+ ${INDENT} # Add columns to an existing table (include -to-<table_name> for auto alterTable scaffolding)
155
180
  ${INDENT} pnpm psy g:migration add-timezone-to-users timezone:string
156
181
  ${INDENT} pnpm psy g:migration add-bio-to-users bio:text:optional avatar_url:string:optional
157
182
  ${INDENT}
158
- ${INDENT} # Remove columns (suffix with -from-<table_name>; columns are described with
183
+ ${INDENT} # Remove columns (include -from-<table_name>; columns are described with
159
184
  ${INDENT} # their types, same shorthand as -to-, so the rollback (down) can recreate them)
160
185
  ${INDENT} pnpm psy g:migration remove-legacy-fields-from-posts legacy_status:string
161
186
  ${INDENT}
162
- ${INDENT} # General schema change (no table suffix — generates empty up/down methods)
187
+ ${INDENT} # General schema change (no -to-/-from- marker — scaffolds alterTable('<table-name>') to hand-edit)
163
188
  ${INDENT} pnpm psy g:migration create-unique-index-on-invitations`)
164
- .argument('<migrationName>', `Kebab-case name describing the change. End with -to-<table_name> or -from-<table_name> to auto-generate an alterTable scaffold for that table.
165
- ${INDENT}
166
- ${INDENT}Examples:
167
- ${INDENT} add-phone-to-users phone:encrypted # scaffolds alterTable('users', ...)
168
- ${INDENT} remove-status-from-posts status:string # scaffolds alterTable('posts', ...)
169
- ${INDENT} create-join-table-host-places # empty migration (no table suffix match)`)
189
+ .argument('<migrationName>', migrationNameArgumentDescription)
170
190
  .option('--connection-name <connectionName>', 'the database connection to use for this migration. Only needed for multi-database setups; defaults to "default"')
171
191
  .argument('[columnsWithTypes...]', columnsWithTypesDescriptionForMigration)
172
192
  .action(async (migrationName, columnsWithTypes, options) => {
@@ -0,0 +1,30 @@
1
+ export default class InvalidMigrationTableName extends Error {
2
+ migrationName;
3
+ table;
4
+ /**
5
+ * @param migrationName - the migration name the generator was invoked with
6
+ * @param table - the table name the generator resolved for it
7
+ */
8
+ constructor(migrationName, table) {
9
+ super();
10
+ this.migrationName = migrationName;
11
+ this.table = table;
12
+ }
13
+ get message() {
14
+ return `
15
+ generating the migration '${this.migrationName}' resolved the table name:
16
+ ${this.table}
17
+
18
+ which is not a valid table name. The table name is written into the generated
19
+ migration, which 'db:migrate' later executes, so it must be a plain
20
+ identifier: lowercase letters, digits and underscores only.
21
+
22
+ A standalone 'g:migration' takes the table from the text after the first
23
+ '-to-'/'-from-' in the name, snake-cased and pluralized, e.g.:
24
+ add-phone-to-users
25
+
26
+ A model generator takes it from the model name, or verbatim from
27
+ '--table-name'.
28
+ `;
29
+ }
30
+ }
@@ -1,17 +1,49 @@
1
1
  export default class NoColumnsToAlterMigration extends Error {
2
2
  table;
3
3
  alterDirection;
4
- constructor(table, alterDirection) {
4
+ migrationName;
5
+ marker;
6
+ /**
7
+ * @param table - the table the migration resolved to
8
+ * @param alterDirection - 'add' for a `-to-` migration, 'remove' for a `-from-` migration
9
+ * @param migrationName - the migration name the user typed, when available. Only
10
+ * the standalone `g:migration` flow knows it, so the message degrades to a
11
+ * table-only explanation when it is omitted.
12
+ * @param marker - the `-to-`/`-from-` marker that matched within `migrationName`
13
+ */
14
+ constructor(table, alterDirection, migrationName, marker) {
5
15
  super();
6
16
  this.table = table;
7
17
  this.alterDirection = alterDirection;
18
+ this.migrationName = migrationName;
19
+ this.marker = marker;
8
20
  }
9
21
  get message() {
10
- return `
11
- no valid columns to ${this.alterDirection === 'remove' ? 'drop from' : 'add to'} table '${this.table}' in this alter migration.
22
+ const verb = this.alterDirection === 'remove' ? 'drop from' : 'add to';
23
+ if (!this.migrationName || !this.marker) {
24
+ return `
25
+ no valid columns to ${verb} table '${this.table}' in this alter migration.
12
26
 
13
27
  Pass at least one column declaration with a resolvable type, e.g.:
14
28
  name:string
29
+ `;
30
+ }
31
+ return `
32
+ no valid columns to ${verb} table '${this.table}' in this alter migration.
33
+
34
+ The migration name '${this.migrationName}' contains '${this.marker}', which selected that
35
+ table and the '${this.alterDirection}' direction: everything after the marker's first
36
+ occurrence - wherever in the name that falls - is snake-cased and pluralized
37
+ into the table name. A '-to-' anywhere in the name wins, even when a '-from-'
38
+ appears earlier; '-from-' applies only when the name contains no '-to-' at all.
39
+
40
+ Either:
41
+
42
+ - pass at least one column declaration with a resolvable type, e.g.:
43
+ name:string
44
+
45
+ - or, if this was meant to be a hand-written migration, rename it so that
46
+ neither '-to-' nor '-from-' appears anywhere in the name.
15
47
  `;
16
48
  }
17
49
  }
@@ -1,5 +1,6 @@
1
1
  import * as path from 'node:path';
2
2
  import pluralize from 'pluralize-esm';
3
+ import InvalidMigrationTableName from '../../errors/InvalidMigrationTableName.js';
3
4
  import generateMigrationContent, { MIGRATION_TABLE_NAME_PLACEHOLDER, } from '../cli/generateMigrationContent.js';
4
5
  import primaryKeyType from '../db/primaryKeyType.js';
5
6
  import hyphenize from '../hyphenize.js';
@@ -21,6 +22,30 @@ function tableNameFromSuffix(migrationName, marker) {
21
22
  return undefined;
22
23
  return migrationName.slice(markerIndex + marker.length) || undefined;
23
24
  }
25
+ /**
26
+ * Whatever table name a generated migration ends up with is written verbatim
27
+ * into a single-quoted string literal in the migration file, which
28
+ * `db:migrate` later executes. Anything that is not a plain identifier could
29
+ * therefore close that literal and inject statements, so the generator fails
30
+ * closed instead of escaping. Every source of that table name is validated:
31
+ * the text after a `-to-`/`-from-` marker, the `--table-name` option, and the
32
+ * name derived from a model name.
33
+ *
34
+ * The pattern's job is blocking characters that can escape the generated
35
+ * string literal, not enforcing an SQL identifier convention: Kysely quotes
36
+ * identifiers, so a leading digit (`alterTable('2fa_tokens')`) is legal and is
37
+ * allowed here. Non-ASCII names stay rejected.
38
+ *
39
+ * This is a pure string check: it never consults the database, since the
40
+ * derived name legitimately need not be an existing table (e.g.
41
+ * `add-treehouse-to-place-styles` targets a Postgres enum).
42
+ */
43
+ const VALID_DERIVED_TABLE_NAME = /^[a-z0-9_]+$/;
44
+ function validTableNameOrThrow(migrationName, table) {
45
+ if (!VALID_DERIVED_TABLE_NAME.test(table))
46
+ throw new InvalidMigrationTableName(migrationName, table);
47
+ return table;
48
+ }
24
49
  export default async function generateMigration({ migrationName, columnsWithTypes, connectionName, fullyQualifiedModelName, fullyQualifiedParentName, tableName: explicitTableName, modelClassName, softDelete = false, }) {
25
50
  const migrationsBasePath = connectionName === 'default'
26
51
  ? path.join(dreamPath('db'), 'migrations')
@@ -30,7 +55,7 @@ export default async function generateMigration({ migrationName, columnsWithType
30
55
  let content = '';
31
56
  if (isSTI) {
32
57
  content = generateStiMigrationContent({
33
- table: snakeify(pluralize(pascalizePath(fullyQualifiedParentName))),
58
+ table: validTableNameOrThrow(migrationName, snakeify(pluralize(pascalizePath(fullyQualifiedParentName)))),
34
59
  columnsWithTypes,
35
60
  primaryKeyType: primaryKeyType(connectionName),
36
61
  stiChildClassName: modelClassName || pascalizePath(fullyQualifiedModelName),
@@ -38,7 +63,7 @@ export default async function generateMigration({ migrationName, columnsWithType
38
63
  }
39
64
  else if (fullyQualifiedModelName) {
40
65
  content = generateMigrationContent({
41
- table: explicitTableName || snakeify(pluralize(pascalizePath(fullyQualifiedModelName))),
66
+ table: validTableNameOrThrow(migrationName, explicitTableName || snakeify(pluralize(pascalizePath(fullyQualifiedModelName)))),
42
67
  columnsWithTypes,
43
68
  primaryKeyType: primaryKeyType(connectionName),
44
69
  softDelete,
@@ -50,12 +75,17 @@ export default async function generateMigration({ migrationName, columnsWithType
50
75
  ? undefined
51
76
  : tableNameFromSuffix(migrationName, '-from-');
52
77
  const tableName = toTableName || fromTableName;
78
+ const table = tableName
79
+ ? validTableNameOrThrow(migrationName, pluralize(snakeify(tableName)))
80
+ : MIGRATION_TABLE_NAME_PLACEHOLDER;
53
81
  content = generateMigrationContent({
54
- table: tableName ? pluralize(snakeify(tableName)) : MIGRATION_TABLE_NAME_PLACEHOLDER,
82
+ table,
55
83
  columnsWithTypes,
56
84
  primaryKeyType: primaryKeyType(connectionName),
57
85
  createOrAlter: 'alter',
58
86
  alterDirection: fromTableName ? 'remove' : 'add',
87
+ migrationName,
88
+ alterMarker: toTableName ? '-to-' : fromTableName ? '-from-' : undefined,
59
89
  });
60
90
  }
61
91
  await writeGeneratedFile({
@@ -10,7 +10,7 @@ import globalClassNameFromFullyQualifiedModelName from '../globalClassNameFromFu
10
10
  import snakeify from '../snakeify.js';
11
11
  import standardizeFullyQualifiedModelName from '../standardizeFullyQualifiedModelName.js';
12
12
  // Sentinel table name used by generateMigration.ts when a standalone
13
- // migration name matches neither a `-to-<table>` nor `-from-<table>` suffix.
13
+ // migration name contains neither a `-to-<table>` nor `-from-<table>` marker.
14
14
  // That path intentionally produces a stub the user is expected to hand-edit
15
15
  // (e.g. an index-only migration with no column operations at all), so it's
16
16
  // exempt from the "no valid columns" check below.
@@ -23,7 +23,7 @@ const STI_TYPE_COLUMN_NAME = 'type';
23
23
  // default. See spec/unit/cli/generateMigrationContent.spec.ts
24
24
  // ("deleted_at is deliberately NOT indexed") and the CHANGELOG.
25
25
  const COLUMNS_TO_INDEX = [STI_TYPE_COLUMN_NAME];
26
- export default function generateMigrationContent({ connectionName = 'default', table, columnsWithTypes = [], primaryKeyType = 'bigserial', createOrAlter = 'create', alterDirection = 'add', stiChildClassName, softDelete = false, } = {}) {
26
+ export default function generateMigrationContent({ connectionName = 'default', table, columnsWithTypes = [], primaryKeyType = 'bigserial', createOrAlter = 'create', alterDirection = 'add', migrationName, alterMarker, stiChildClassName, softDelete = false, } = {}) {
27
27
  const altering = createOrAlter === 'alter';
28
28
  const removingInUp = altering && alterDirection === 'remove';
29
29
  let requireCitextExtension = false;
@@ -205,7 +205,7 @@ export async function down(db: Kysely<any>): Promise<void> {
205
205
  columnDefs.length === 0 &&
206
206
  table !== MIGRATION_TABLE_NAME_PLACEHOLDER &&
207
207
  !stiChildClassName) {
208
- throw new NoColumnsToAlterMigration(table, alterDirection);
208
+ throw new NoColumnsToAlterMigration(table, alterDirection, migrationName, alterMarker);
209
209
  }
210
210
  const citextExtension = requireCitextExtension
211
211
  ? ` await DreamMigrationHelpers.createExtension(db, 'citext')\n\n`
@@ -105,6 +105,31 @@ ${INDENT}
105
105
  ${INDENT} rename the FK column with Model@alias (snake_case alias becomes the column name):
106
106
  ${INDENT} InternalUser@canceled_by:belongs_to:optional # canceled_by_id column with index
107
107
  ${INDENT} Messaging/Template@template:belongs_to # template_id column (strips the namespace from the column name)`);
108
+ /**
109
+ * The `<migrationName>` argument description for `g:migration`. Exported so
110
+ * that spec/unit/helpers/cli/generateMigration.spec.ts can read the example
111
+ * migration names out of this text and run them through the generator: a help
112
+ * example that drifts from what the generator actually produces then fails the
113
+ * suite instead of shipping.
114
+ */
115
+ export const migrationNameArgumentDescription = `Kebab-case name describing the change. A -to-<table_name>
116
+ ${INDENT}anywhere in the name — not just at the end — auto-generates
117
+ ${INDENT}an add-columns alterTable scaffold for that table;
118
+ ${INDENT}-from-<table_name> generates a remove-columns scaffold, but
119
+ ${INDENT}only when the name contains no -to- at all. The text after
120
+ ${INDENT}the marker's first occurrence is snake-cased and pluralized
121
+ ${INDENT}into the table name, so add-a-to-b-to-cs targets b_to_cs. A
122
+ ${INDENT}hand-written migration must avoid both strings anywhere in
123
+ ${INDENT}its name.
124
+ ${INDENT}
125
+ ${INDENT}Examples:
126
+ ${INDENT} add-phone-to-users phone:encrypted # scaffolds alterTable('users', ...)
127
+ ${INDENT} remove-status-from-posts status:string # scaffolds alterTable('posts', ...)
128
+ ${INDENT} create-many-to-many-posts-users # WRONG: selects 'many_posts_users'
129
+ ${INDENT} # -to- inside "many-to-many" matches: with no columns
130
+ ${INDENT} # this errors; with columns it silently scaffolds it
131
+ ${INDENT} create-posts-users-join-table # correct: no marker match
132
+ ${INDENT} # scaffolds alterTable('<table-name>') to hand-edit`;
108
133
  export default class DreamCLI {
109
134
  /**
110
135
  * Starts the Dream console
@@ -151,22 +176,17 @@ export default class DreamCLI {
151
176
  .description(`Generates a new Kysely migration file for schema changes. Use this for altering existing tables (adding/removing columns, indexes, constraints). Prefer g:resource or g:model when creating a new model, since they generate the migration along with the model, serializer, and spec files.
152
177
  ${INDENT}
153
178
  ${INDENT}Examples:
154
- ${INDENT} # Add columns to an existing table (suffix with -to-<table_name> for auto alterTable scaffolding)
179
+ ${INDENT} # Add columns to an existing table (include -to-<table_name> for auto alterTable scaffolding)
155
180
  ${INDENT} pnpm psy g:migration add-timezone-to-users timezone:string
156
181
  ${INDENT} pnpm psy g:migration add-bio-to-users bio:text:optional avatar_url:string:optional
157
182
  ${INDENT}
158
- ${INDENT} # Remove columns (suffix with -from-<table_name>; columns are described with
183
+ ${INDENT} # Remove columns (include -from-<table_name>; columns are described with
159
184
  ${INDENT} # their types, same shorthand as -to-, so the rollback (down) can recreate them)
160
185
  ${INDENT} pnpm psy g:migration remove-legacy-fields-from-posts legacy_status:string
161
186
  ${INDENT}
162
- ${INDENT} # General schema change (no table suffix — generates empty up/down methods)
187
+ ${INDENT} # General schema change (no -to-/-from- marker — scaffolds alterTable('<table-name>') to hand-edit)
163
188
  ${INDENT} pnpm psy g:migration create-unique-index-on-invitations`)
164
- .argument('<migrationName>', `Kebab-case name describing the change. End with -to-<table_name> or -from-<table_name> to auto-generate an alterTable scaffold for that table.
165
- ${INDENT}
166
- ${INDENT}Examples:
167
- ${INDENT} add-phone-to-users phone:encrypted # scaffolds alterTable('users', ...)
168
- ${INDENT} remove-status-from-posts status:string # scaffolds alterTable('posts', ...)
169
- ${INDENT} create-join-table-host-places # empty migration (no table suffix match)`)
189
+ .argument('<migrationName>', migrationNameArgumentDescription)
170
190
  .option('--connection-name <connectionName>', 'the database connection to use for this migration. Only needed for multi-database setups; defaults to "default"')
171
191
  .argument('[columnsWithTypes...]', columnsWithTypesDescriptionForMigration)
172
192
  .action(async (migrationName, columnsWithTypes, options) => {
@@ -0,0 +1,30 @@
1
+ export default class InvalidMigrationTableName extends Error {
2
+ migrationName;
3
+ table;
4
+ /**
5
+ * @param migrationName - the migration name the generator was invoked with
6
+ * @param table - the table name the generator resolved for it
7
+ */
8
+ constructor(migrationName, table) {
9
+ super();
10
+ this.migrationName = migrationName;
11
+ this.table = table;
12
+ }
13
+ get message() {
14
+ return `
15
+ generating the migration '${this.migrationName}' resolved the table name:
16
+ ${this.table}
17
+
18
+ which is not a valid table name. The table name is written into the generated
19
+ migration, which 'db:migrate' later executes, so it must be a plain
20
+ identifier: lowercase letters, digits and underscores only.
21
+
22
+ A standalone 'g:migration' takes the table from the text after the first
23
+ '-to-'/'-from-' in the name, snake-cased and pluralized, e.g.:
24
+ add-phone-to-users
25
+
26
+ A model generator takes it from the model name, or verbatim from
27
+ '--table-name'.
28
+ `;
29
+ }
30
+ }
@@ -1,17 +1,49 @@
1
1
  export default class NoColumnsToAlterMigration extends Error {
2
2
  table;
3
3
  alterDirection;
4
- constructor(table, alterDirection) {
4
+ migrationName;
5
+ marker;
6
+ /**
7
+ * @param table - the table the migration resolved to
8
+ * @param alterDirection - 'add' for a `-to-` migration, 'remove' for a `-from-` migration
9
+ * @param migrationName - the migration name the user typed, when available. Only
10
+ * the standalone `g:migration` flow knows it, so the message degrades to a
11
+ * table-only explanation when it is omitted.
12
+ * @param marker - the `-to-`/`-from-` marker that matched within `migrationName`
13
+ */
14
+ constructor(table, alterDirection, migrationName, marker) {
5
15
  super();
6
16
  this.table = table;
7
17
  this.alterDirection = alterDirection;
18
+ this.migrationName = migrationName;
19
+ this.marker = marker;
8
20
  }
9
21
  get message() {
10
- return `
11
- no valid columns to ${this.alterDirection === 'remove' ? 'drop from' : 'add to'} table '${this.table}' in this alter migration.
22
+ const verb = this.alterDirection === 'remove' ? 'drop from' : 'add to';
23
+ if (!this.migrationName || !this.marker) {
24
+ return `
25
+ no valid columns to ${verb} table '${this.table}' in this alter migration.
12
26
 
13
27
  Pass at least one column declaration with a resolvable type, e.g.:
14
28
  name:string
29
+ `;
30
+ }
31
+ return `
32
+ no valid columns to ${verb} table '${this.table}' in this alter migration.
33
+
34
+ The migration name '${this.migrationName}' contains '${this.marker}', which selected that
35
+ table and the '${this.alterDirection}' direction: everything after the marker's first
36
+ occurrence - wherever in the name that falls - is snake-cased and pluralized
37
+ into the table name. A '-to-' anywhere in the name wins, even when a '-from-'
38
+ appears earlier; '-from-' applies only when the name contains no '-to-' at all.
39
+
40
+ Either:
41
+
42
+ - pass at least one column declaration with a resolvable type, e.g.:
43
+ name:string
44
+
45
+ - or, if this was meant to be a hand-written migration, rename it so that
46
+ neither '-to-' nor '-from-' appears anywhere in the name.
15
47
  `;
16
48
  }
17
49
  }
@@ -1,5 +1,6 @@
1
1
  import * as path from 'node:path';
2
2
  import pluralize from 'pluralize-esm';
3
+ import InvalidMigrationTableName from '../../errors/InvalidMigrationTableName.js';
3
4
  import generateMigrationContent, { MIGRATION_TABLE_NAME_PLACEHOLDER, } from '../cli/generateMigrationContent.js';
4
5
  import primaryKeyType from '../db/primaryKeyType.js';
5
6
  import hyphenize from '../hyphenize.js';
@@ -21,6 +22,30 @@ function tableNameFromSuffix(migrationName, marker) {
21
22
  return undefined;
22
23
  return migrationName.slice(markerIndex + marker.length) || undefined;
23
24
  }
25
+ /**
26
+ * Whatever table name a generated migration ends up with is written verbatim
27
+ * into a single-quoted string literal in the migration file, which
28
+ * `db:migrate` later executes. Anything that is not a plain identifier could
29
+ * therefore close that literal and inject statements, so the generator fails
30
+ * closed instead of escaping. Every source of that table name is validated:
31
+ * the text after a `-to-`/`-from-` marker, the `--table-name` option, and the
32
+ * name derived from a model name.
33
+ *
34
+ * The pattern's job is blocking characters that can escape the generated
35
+ * string literal, not enforcing an SQL identifier convention: Kysely quotes
36
+ * identifiers, so a leading digit (`alterTable('2fa_tokens')`) is legal and is
37
+ * allowed here. Non-ASCII names stay rejected.
38
+ *
39
+ * This is a pure string check: it never consults the database, since the
40
+ * derived name legitimately need not be an existing table (e.g.
41
+ * `add-treehouse-to-place-styles` targets a Postgres enum).
42
+ */
43
+ const VALID_DERIVED_TABLE_NAME = /^[a-z0-9_]+$/;
44
+ function validTableNameOrThrow(migrationName, table) {
45
+ if (!VALID_DERIVED_TABLE_NAME.test(table))
46
+ throw new InvalidMigrationTableName(migrationName, table);
47
+ return table;
48
+ }
24
49
  export default async function generateMigration({ migrationName, columnsWithTypes, connectionName, fullyQualifiedModelName, fullyQualifiedParentName, tableName: explicitTableName, modelClassName, softDelete = false, }) {
25
50
  const migrationsBasePath = connectionName === 'default'
26
51
  ? path.join(dreamPath('db'), 'migrations')
@@ -30,7 +55,7 @@ export default async function generateMigration({ migrationName, columnsWithType
30
55
  let content = '';
31
56
  if (isSTI) {
32
57
  content = generateStiMigrationContent({
33
- table: snakeify(pluralize(pascalizePath(fullyQualifiedParentName))),
58
+ table: validTableNameOrThrow(migrationName, snakeify(pluralize(pascalizePath(fullyQualifiedParentName)))),
34
59
  columnsWithTypes,
35
60
  primaryKeyType: primaryKeyType(connectionName),
36
61
  stiChildClassName: modelClassName || pascalizePath(fullyQualifiedModelName),
@@ -38,7 +63,7 @@ export default async function generateMigration({ migrationName, columnsWithType
38
63
  }
39
64
  else if (fullyQualifiedModelName) {
40
65
  content = generateMigrationContent({
41
- table: explicitTableName || snakeify(pluralize(pascalizePath(fullyQualifiedModelName))),
66
+ table: validTableNameOrThrow(migrationName, explicitTableName || snakeify(pluralize(pascalizePath(fullyQualifiedModelName)))),
42
67
  columnsWithTypes,
43
68
  primaryKeyType: primaryKeyType(connectionName),
44
69
  softDelete,
@@ -50,12 +75,17 @@ export default async function generateMigration({ migrationName, columnsWithType
50
75
  ? undefined
51
76
  : tableNameFromSuffix(migrationName, '-from-');
52
77
  const tableName = toTableName || fromTableName;
78
+ const table = tableName
79
+ ? validTableNameOrThrow(migrationName, pluralize(snakeify(tableName)))
80
+ : MIGRATION_TABLE_NAME_PLACEHOLDER;
53
81
  content = generateMigrationContent({
54
- table: tableName ? pluralize(snakeify(tableName)) : MIGRATION_TABLE_NAME_PLACEHOLDER,
82
+ table,
55
83
  columnsWithTypes,
56
84
  primaryKeyType: primaryKeyType(connectionName),
57
85
  createOrAlter: 'alter',
58
86
  alterDirection: fromTableName ? 'remove' : 'add',
87
+ migrationName,
88
+ alterMarker: toTableName ? '-to-' : fromTableName ? '-from-' : undefined,
59
89
  });
60
90
  }
61
91
  await writeGeneratedFile({
@@ -10,7 +10,7 @@ import globalClassNameFromFullyQualifiedModelName from '../globalClassNameFromFu
10
10
  import snakeify from '../snakeify.js';
11
11
  import standardizeFullyQualifiedModelName from '../standardizeFullyQualifiedModelName.js';
12
12
  // Sentinel table name used by generateMigration.ts when a standalone
13
- // migration name matches neither a `-to-<table>` nor `-from-<table>` suffix.
13
+ // migration name contains neither a `-to-<table>` nor `-from-<table>` marker.
14
14
  // That path intentionally produces a stub the user is expected to hand-edit
15
15
  // (e.g. an index-only migration with no column operations at all), so it's
16
16
  // exempt from the "no valid columns" check below.
@@ -23,7 +23,7 @@ const STI_TYPE_COLUMN_NAME = 'type';
23
23
  // default. See spec/unit/cli/generateMigrationContent.spec.ts
24
24
  // ("deleted_at is deliberately NOT indexed") and the CHANGELOG.
25
25
  const COLUMNS_TO_INDEX = [STI_TYPE_COLUMN_NAME];
26
- export default function generateMigrationContent({ connectionName = 'default', table, columnsWithTypes = [], primaryKeyType = 'bigserial', createOrAlter = 'create', alterDirection = 'add', stiChildClassName, softDelete = false, } = {}) {
26
+ export default function generateMigrationContent({ connectionName = 'default', table, columnsWithTypes = [], primaryKeyType = 'bigserial', createOrAlter = 'create', alterDirection = 'add', migrationName, alterMarker, stiChildClassName, softDelete = false, } = {}) {
27
27
  const altering = createOrAlter === 'alter';
28
28
  const removingInUp = altering && alterDirection === 'remove';
29
29
  let requireCitextExtension = false;
@@ -205,7 +205,7 @@ export async function down(db: Kysely<any>): Promise<void> {
205
205
  columnDefs.length === 0 &&
206
206
  table !== MIGRATION_TABLE_NAME_PLACEHOLDER &&
207
207
  !stiChildClassName) {
208
- throw new NoColumnsToAlterMigration(table, alterDirection);
208
+ throw new NoColumnsToAlterMigration(table, alterDirection, migrationName, alterMarker);
209
209
  }
210
210
  const citextExtension = requireCitextExtension
211
211
  ? ` await DreamMigrationHelpers.createExtension(db, 'citext')\n\n`
@@ -16,6 +16,14 @@ export type SpawnOptions = Omit<NodeSpawnOptions, 'shell'> & {
16
16
  export declare const CLI_INDENT = " ";
17
17
  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";
18
18
  export declare const columnsWithTypesDescriptionForStiChild: string;
19
+ /**
20
+ * The `<migrationName>` argument description for `g:migration`. Exported so
21
+ * that spec/unit/helpers/cli/generateMigration.spec.ts can read the example
22
+ * migration names out of this text and run them through the generator: a help
23
+ * example that drifts from what the generator actually produces then fails the
24
+ * suite instead of shipping.
25
+ */
26
+ export declare const migrationNameArgumentDescription = "Kebab-case name describing the change. A -to-<table_name>\n anywhere in the name \u2014 not just at the end \u2014 auto-generates\n an add-columns alterTable scaffold for that table;\n -from-<table_name> generates a remove-columns scaffold, but\n only when the name contains no -to- at all. The text after\n the marker's first occurrence is snake-cased and pluralized\n into the table name, so add-a-to-b-to-cs targets b_to_cs. A\n hand-written migration must avoid both strings anywhere in\n its name.\n \n Examples:\n add-phone-to-users phone:encrypted # scaffolds alterTable('users', ...)\n remove-status-from-posts status:string # scaffolds alterTable('posts', ...)\n create-many-to-many-posts-users # WRONG: selects 'many_posts_users'\n # -to- inside \"many-to-many\" matches: with no columns\n # this errors; with columns it silently scaffolds it\n create-posts-users-join-table # correct: no marker match\n # scaffolds alterTable('<table-name>') to hand-edit";
19
27
  export default class DreamCLI {
20
28
  /**
21
29
  * Starts the Dream console
@@ -0,0 +1,10 @@
1
+ export default class InvalidMigrationTableName extends Error {
2
+ migrationName: string;
3
+ table: string;
4
+ /**
5
+ * @param migrationName - the migration name the generator was invoked with
6
+ * @param table - the table name the generator resolved for it
7
+ */
8
+ constructor(migrationName: string, table: string);
9
+ get message(): string;
10
+ }
@@ -1,6 +1,16 @@
1
1
  export default class NoColumnsToAlterMigration extends Error {
2
2
  table: string;
3
3
  alterDirection: 'add' | 'remove';
4
- constructor(table: string, alterDirection: 'add' | 'remove');
4
+ migrationName: string | undefined;
5
+ marker: '-to-' | '-from-' | undefined;
6
+ /**
7
+ * @param table - the table the migration resolved to
8
+ * @param alterDirection - 'add' for a `-to-` migration, 'remove' for a `-from-` migration
9
+ * @param migrationName - the migration name the user typed, when available. Only
10
+ * the standalone `g:migration` flow knows it, so the message degrades to a
11
+ * table-only explanation when it is omitted.
12
+ * @param marker - the `-to-`/`-from-` marker that matched within `migrationName`
13
+ */
14
+ constructor(table: string, alterDirection: 'add' | 'remove', migrationName?: string, marker?: '-to-' | '-from-');
5
15
  get message(): string;
6
16
  }
@@ -1,6 +1,6 @@
1
1
  import { LegacyCompatiblePrimaryKeyType } from '../../types/db.js';
2
2
  export declare const MIGRATION_TABLE_NAME_PLACEHOLDER = "<table-name>";
3
- export default function generateMigrationContent({ connectionName, table, columnsWithTypes, primaryKeyType, createOrAlter, alterDirection, stiChildClassName, softDelete, }?: {
3
+ export default function generateMigrationContent({ connectionName, table, columnsWithTypes, primaryKeyType, createOrAlter, alterDirection, migrationName, alterMarker, stiChildClassName, softDelete, }?: {
4
4
  connectionName?: string;
5
5
  table?: string | undefined;
6
6
  columnsWithTypes?: string[] | undefined;
@@ -8,13 +8,27 @@ export default function generateMigrationContent({ connectionName, table, column
8
8
  createOrAlter?: 'create' | 'alter' | undefined;
9
9
  /**
10
10
  * Only meaningful when `createOrAlter: 'alter'`. `'add'` (the default,
11
- * matching a `-to-<table>`-suffixed migration name) emits column additions
12
- * in `up` and the matching drops in `down`. `'remove'` (matching a
13
- * `-from-<table>`-suffixed migration name) inverts this: `up` drops the
11
+ * matching a migration name containing a `-to-<table>` marker) emits column
12
+ * additions in `up` and the matching drops in `down`. `'remove'` (matching a
13
+ * migration name containing a `-from-<table>` marker) inverts this: `up` drops the
14
14
  * named columns and `down` re-adds them with their declared types, so
15
15
  * `down` is the rollback that restores what `up` removed.
16
16
  */
17
17
  alterDirection?: 'add' | 'remove' | undefined;
18
+ /**
19
+ * The migration name the user typed. Only the standalone `g:migration` flow
20
+ * has one; it is threaded through solely so `NoColumnsToAlterMigration` can
21
+ * name it, and the error message degrades to a table-only explanation when it
22
+ * (or `alterMarker`) is absent.
23
+ */
24
+ migrationName?: string | undefined;
25
+ /**
26
+ * The `-to-`/`-from-` marker that matched within `migrationName` and resolved
27
+ * both the table and the add/remove direction (alter mode itself is
28
+ * unconditional on that path). Reported by `NoColumnsToAlterMigration`; see
29
+ * `migrationName`.
30
+ */
31
+ alterMarker?: '-to-' | '-from-' | undefined;
18
32
  stiChildClassName?: string | undefined;
19
33
  /**
20
34
  * When true (and creating a new table), auto-emits a nullable `deleted_at`