@rvoh/dream 2.5.2 → 2.5.4
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.
- package/dist/cjs/src/cli/index.js +185 -48
- package/dist/cjs/src/cli/logger/DreamCliLogger.js +107 -6
- package/dist/cjs/src/db/helpers/syncDbTypesFiles.js +2 -2
- package/dist/cjs/src/dream/QueryDriver/Postgres.js +2 -2
- package/dist/cjs/src/dream/QueryDriver/helpers/kysely/runMigration.js +2 -2
- package/dist/cjs/src/dream/QueryDriver/helpers/pg/dropDb.js +2 -2
- package/dist/esm/src/cli/index.js +185 -48
- package/dist/esm/src/cli/logger/DreamCliLogger.js +107 -6
- package/dist/esm/src/db/helpers/syncDbTypesFiles.js +2 -2
- package/dist/esm/src/dream/QueryDriver/Postgres.js +2 -2
- package/dist/esm/src/dream/QueryDriver/helpers/kysely/runMigration.js +2 -2
- package/dist/esm/src/dream/QueryDriver/helpers/pg/dropDb.js +2 -2
- package/dist/types/src/cli/logger/DreamCliLogger.d.ts +6 -1
- package/docs/classes/db.DreamMigrationHelpers.html +9 -9
- package/docs/classes/db.KyselyQueryDriver.html +32 -32
- package/docs/classes/db.PostgresQueryDriver.html +33 -33
- package/docs/classes/db.QueryDriverBase.html +31 -31
- package/docs/classes/errors.CheckConstraintViolation.html +3 -3
- package/docs/classes/errors.ColumnOverflow.html +3 -3
- package/docs/classes/errors.CreateOrFindByFailedToCreateAndFind.html +3 -3
- package/docs/classes/errors.DataIncompatibleWithDatabaseField.html +3 -3
- package/docs/classes/errors.DataTypeColumnTypeMismatch.html +3 -3
- package/docs/classes/errors.GlobalNameNotSet.html +3 -3
- package/docs/classes/errors.InvalidCalendarDate.html +2 -2
- package/docs/classes/errors.InvalidClockTime.html +2 -2
- package/docs/classes/errors.InvalidClockTimeTz.html +2 -2
- package/docs/classes/errors.InvalidDateTime.html +2 -2
- package/docs/classes/errors.MissingSerializersDefinition.html +3 -3
- package/docs/classes/errors.NonLoadedAssociation.html +3 -3
- package/docs/classes/errors.NotNullViolation.html +3 -3
- package/docs/classes/errors.RecordNotFound.html +3 -3
- package/docs/classes/errors.ValidationError.html +3 -3
- package/docs/classes/index.CalendarDate.html +33 -33
- package/docs/classes/index.ClockTime.html +32 -32
- package/docs/classes/index.ClockTimeTz.html +35 -35
- package/docs/classes/index.DateTime.html +86 -86
- package/docs/classes/index.Decorators.html +19 -19
- package/docs/classes/index.Dream.html +118 -118
- package/docs/classes/index.DreamApp.html +5 -5
- package/docs/classes/index.DreamTransaction.html +2 -2
- package/docs/classes/index.Env.html +2 -2
- package/docs/classes/index.Query.html +56 -56
- package/docs/classes/system.CliFileWriter.html +4 -4
- package/docs/classes/system.DreamBin.html +2 -2
- package/docs/classes/system.DreamCLI.html +6 -6
- package/docs/classes/system.DreamImporter.html +2 -2
- package/docs/classes/system.DreamLogos.html +2 -2
- package/docs/classes/system.DreamSerializerBuilder.html +11 -11
- package/docs/classes/system.ObjectSerializerBuilder.html +8 -8
- package/docs/classes/system.PathHelpers.html +3 -3
- package/docs/classes/utils.Encrypt.html +2 -2
- package/docs/classes/utils.Range.html +2 -2
- package/docs/functions/db.closeAllDbConnections.html +1 -1
- package/docs/functions/db.dreamDbConnections.html +1 -1
- package/docs/functions/db.untypedDb.html +1 -1
- package/docs/functions/db.validateColumn.html +1 -1
- package/docs/functions/db.validateTable.html +1 -1
- package/docs/functions/errors.pgErrorType.html +1 -1
- package/docs/functions/index.DreamSerializer.html +1 -1
- package/docs/functions/index.ObjectSerializer.html +1 -1
- package/docs/functions/index.ReplicaSafe.html +1 -1
- package/docs/functions/index.STI.html +1 -1
- package/docs/functions/index.SoftDelete.html +1 -1
- package/docs/functions/utils.camelize.html +1 -1
- package/docs/functions/utils.capitalize.html +1 -1
- package/docs/functions/utils.cloneDeepSafe.html +1 -1
- package/docs/functions/utils.compact.html +1 -1
- package/docs/functions/utils.groupBy.html +1 -1
- package/docs/functions/utils.hyphenize.html +1 -1
- package/docs/functions/utils.intersection.html +1 -1
- package/docs/functions/utils.isEmpty.html +1 -1
- package/docs/functions/utils.normalizeUnicode.html +1 -1
- package/docs/functions/utils.pascalize.html +1 -1
- package/docs/functions/utils.percent.html +1 -1
- package/docs/functions/utils.range.html +1 -1
- package/docs/functions/utils.round.html +1 -1
- package/docs/functions/utils.sanitizeString.html +1 -1
- package/docs/functions/utils.snakeify.html +1 -1
- package/docs/functions/utils.sort.html +1 -1
- package/docs/functions/utils.sortBy.html +1 -1
- package/docs/functions/utils.sortObjectByKey.html +1 -1
- package/docs/functions/utils.sortObjectByValue.html +1 -1
- package/docs/functions/utils.uncapitalize.html +1 -1
- package/docs/functions/utils.uniq.html +1 -1
- package/docs/interfaces/openapi.OpenapiDescription.html +2 -2
- package/docs/interfaces/openapi.OpenapiSchemaProperties.html +1 -1
- package/docs/interfaces/openapi.OpenapiSchemaPropertiesShorthand.html +1 -1
- package/docs/interfaces/openapi.OpenapiTypeFieldObject.html +1 -1
- package/docs/interfaces/types.BelongsToStatement.html +2 -2
- package/docs/interfaces/types.DecoratorContext.html +2 -2
- package/docs/interfaces/types.DreamAppInitOptions.html +2 -2
- package/docs/interfaces/types.DreamAppOpts.html +2 -2
- package/docs/interfaces/types.DurationObject.html +2 -2
- package/docs/interfaces/types.EncryptOptions.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersMany.html +2 -2
- package/docs/interfaces/types.InternalAnyTypedSerializerRendersOne.html +2 -2
- package/docs/interfaces/types.SerializerRendererOpts.html +2 -2
- package/docs/types/openapi.CommonOpenapiSchemaObjectFields.html +1 -1
- package/docs/types/openapi.OpenapiAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiFormats.html +1 -1
- package/docs/types/openapi.OpenapiNumberFormats.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArray.html +1 -1
- package/docs/types/openapi.OpenapiSchemaArrayShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBody.html +1 -1
- package/docs/types/openapi.OpenapiSchemaBodyShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaCommonFields.html +1 -1
- package/docs/types/openapi.OpenapiSchemaExpressionAllOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionOneOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaExpressionRefSchemaShorthand.html +2 -2
- package/docs/types/openapi.OpenapiSchemaInteger.html +1 -1
- package/docs/types/openapi.OpenapiSchemaNull.html +2 -2
- package/docs/types/openapi.OpenapiSchemaNumber.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObject.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAllOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectAnyOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBase.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectBaseShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOf.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectOneOfShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaObjectShorthand.html +1 -1
- package/docs/types/openapi.OpenapiSchemaPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAllOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionAnyOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionOneOf.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializableRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandExpressionSerializerRef.html +2 -2
- package/docs/types/openapi.OpenapiSchemaShorthandPrimitiveGeneric.html +1 -1
- package/docs/types/openapi.OpenapiSchemaString.html +1 -1
- package/docs/types/openapi.OpenapiShorthandAllTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveBaseTypes.html +1 -1
- package/docs/types/openapi.OpenapiShorthandPrimitiveTypes.html +1 -1
- package/docs/types/openapi.OpenapiTypeField.html +1 -1
- package/docs/types/system.DreamAppAllowedPackageManagersEnum.html +1 -1
- package/docs/types/types.CalendarDateDurationUnit.html +1 -1
- package/docs/types/types.CalendarDateObject.html +1 -1
- package/docs/types/types.Camelized.html +1 -1
- package/docs/types/types.ClockTimeObject.html +1 -1
- package/docs/types/types.DbConnectionType.html +1 -1
- package/docs/types/types.DbTypes.html +1 -1
- package/docs/types/types.DreamAssociationMetadata.html +1 -1
- package/docs/types/types.DreamAttributes.html +1 -1
- package/docs/types/types.DreamClassAssociationAndStatement.html +1 -1
- package/docs/types/types.DreamClassColumn.html +1 -1
- package/docs/types/types.DreamColumn.html +1 -1
- package/docs/types/types.DreamColumnNames.html +1 -1
- package/docs/types/types.DreamLogLevel.html +1 -1
- package/docs/types/types.DreamLogger.html +2 -2
- package/docs/types/types.DreamModelSerializerType.html +1 -1
- package/docs/types/types.DreamOrViewModelClassSerializerKey.html +1 -1
- package/docs/types/types.DreamOrViewModelSerializerKey.html +1 -1
- package/docs/types/types.DreamParamSafeAttributes.html +1 -1
- package/docs/types/types.DreamParamSafeColumnNames.html +1 -1
- package/docs/types/types.DreamSerializable.html +1 -1
- package/docs/types/types.DreamSerializableArray.html +1 -1
- package/docs/types/types.DreamSerializerKey.html +1 -1
- package/docs/types/types.DreamSerializers.html +1 -1
- package/docs/types/types.DreamVirtualColumns.html +1 -1
- package/docs/types/types.DurationUnit.html +1 -1
- package/docs/types/types.EncryptAlgorithm.html +1 -1
- package/docs/types/types.HasManyStatement.html +1 -1
- package/docs/types/types.HasOneStatement.html +1 -1
- package/docs/types/types.Hyphenized.html +1 -1
- package/docs/types/types.Pascalized.html +1 -1
- package/docs/types/types.PrimaryKeyType.html +1 -1
- package/docs/types/types.RoundingPrecision.html +1 -1
- package/docs/types/types.SerializerCasing.html +1 -1
- package/docs/types/types.SimpleObjectSerializerType.html +1 -1
- package/docs/types/types.Snakeified.html +1 -1
- package/docs/types/types.StrictInterface.html +1 -1
- package/docs/types/types.UpdateableAssociationProperties.html +1 -1
- package/docs/types/types.UpdateableProperties.html +1 -1
- package/docs/types/types.ValidationType.html +1 -1
- package/docs/types/types.ViewModel.html +2 -2
- package/docs/types/types.ViewModelClass.html +1 -1
- package/docs/types/types.WeekdayName.html +1 -1
- package/docs/types/types.WhereStatementForDream.html +1 -1
- package/docs/types/types.WhereStatementForDreamClass.html +1 -1
- package/docs/variables/index.DreamConst.html +1 -1
- package/docs/variables/index.ops.html +1 -1
- package/docs/variables/openapi.openapiPrimitiveTypes.html +1 -1
- package/docs/variables/openapi.openapiShorthandPrimitiveTypes.html +1 -1
- package/docs/variables/system.DreamAppAllowedPackageManagersEnumValues.html +1 -1
- package/docs/variables/system.primaryKeyTypes.html +1 -1
- package/package.json +6 -8
|
@@ -7,6 +7,7 @@ import EnvInternal from '../helpers/EnvInternal.js';
|
|
|
7
7
|
import loadRepl from '../helpers/loadRepl.js';
|
|
8
8
|
import sspawn from '../helpers/sspawn.js';
|
|
9
9
|
import DreamCliLogger from './logger/DreamCliLogger.js';
|
|
10
|
+
import colorize from './logger/loggable/colorize.js';
|
|
10
11
|
export const CLI_INDENT = ' ';
|
|
11
12
|
const INDENT = CLI_INDENT;
|
|
12
13
|
export const baseColumnsWithTypesDescription = `space separated snake-case (except for belongs_to model name) properties like this:
|
|
@@ -63,18 +64,24 @@ const columnsWithTypesDescription = baseColumnsWithTypesDescription +
|
|
|
63
64
|
`
|
|
64
65
|
${INDENT}
|
|
65
66
|
${INDENT} - belongs_to:
|
|
66
|
-
${INDENT}
|
|
67
|
+
${INDENT} ALWAYS use this instead of adding a raw uuid column for foreign keys. It creates the FK column, adds a database index,
|
|
68
|
+
${INDENT} AND generates the @deco.BelongsTo association and typed property on the model. A raw uuid column does none of this.
|
|
67
69
|
${INDENT}
|
|
68
|
-
${INDENT}
|
|
69
|
-
${INDENT}
|
|
70
|
+
${INDENT} use the fully qualified model name (matching its path under src/app/models/):
|
|
71
|
+
${INDENT} User:belongs_to # creates user_id column + BelongsTo association
|
|
72
|
+
${INDENT} Health/Coach:belongs_to # creates health_coach_id column + BelongsTo association
|
|
73
|
+
${INDENT} User:belongs_to:optional # nullable foreign key (for optional associations)`;
|
|
70
74
|
const columnsWithTypesDescriptionForMigration = baseColumnsWithTypesDescription +
|
|
71
75
|
`
|
|
72
76
|
${INDENT}
|
|
73
77
|
${INDENT} - belongs_to:
|
|
74
|
-
${INDENT}
|
|
78
|
+
${INDENT} ALWAYS use this instead of adding a raw uuid column for foreign keys. It creates the FK column with an index.
|
|
79
|
+
${INDENT} Unlike in g:model/g:resource, this does NOT add a BelongsTo association (no model is generated).
|
|
75
80
|
${INDENT}
|
|
76
|
-
${INDENT}
|
|
77
|
-
${INDENT}
|
|
81
|
+
${INDENT} use the fully qualified model name (matching its path under src/app/models/):
|
|
82
|
+
${INDENT} User:belongs_to # creates user_id column with index
|
|
83
|
+
${INDENT} Health/Coach:belongs_to # creates health_coach_id column with index
|
|
84
|
+
${INDENT} User:belongs_to:optional # nullable foreign key`;
|
|
78
85
|
export default class DreamCLI {
|
|
79
86
|
/**
|
|
80
87
|
* Starts the Dream console
|
|
@@ -89,9 +96,9 @@ export default class DreamCLI {
|
|
|
89
96
|
static provide(program, { initializeDreamApp, seedDb, }) {
|
|
90
97
|
program
|
|
91
98
|
.command('sync')
|
|
92
|
-
.description('
|
|
93
|
-
.option('--schema-only')
|
|
94
|
-
.action(async (options
|
|
99
|
+
.description('Regenerates TypeScript types (types/db.ts, types/dream.ts) from the current database schema. Run this after changing associations, serializers, or enum types.')
|
|
100
|
+
.option('--schema-only', 'only regenerate database schema types, skipping any custom sync actions', false)
|
|
101
|
+
.action(async (options) => {
|
|
95
102
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true });
|
|
96
103
|
await DreamBin.sync(() => { }, options);
|
|
97
104
|
process.exit();
|
|
@@ -118,9 +125,25 @@ export default class DreamCLI {
|
|
|
118
125
|
program
|
|
119
126
|
.command('generate:migration')
|
|
120
127
|
.alias('g:migration')
|
|
121
|
-
.description(
|
|
122
|
-
|
|
123
|
-
|
|
128
|
+
.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.
|
|
129
|
+
${INDENT}
|
|
130
|
+
${INDENT}Examples:
|
|
131
|
+
${INDENT} # Add columns to an existing table (suffix with -to-<table_name> for auto alterTable scaffolding)
|
|
132
|
+
${INDENT} pnpm psy g:migration add-timezone-to-users timezone:string
|
|
133
|
+
${INDENT} pnpm psy g:migration add-bio-to-users bio:text:optional avatar_url:string:optional
|
|
134
|
+
${INDENT}
|
|
135
|
+
${INDENT} # Remove columns (suffix with -from-<table_name>)
|
|
136
|
+
${INDENT} pnpm psy g:migration remove-legacy-fields-from-posts
|
|
137
|
+
${INDENT}
|
|
138
|
+
${INDENT} # General schema change (no table suffix — generates empty up/down methods)
|
|
139
|
+
${INDENT} pnpm psy g:migration create-unique-index-on-invitations`)
|
|
140
|
+
.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.
|
|
141
|
+
${INDENT}
|
|
142
|
+
${INDENT}Examples:
|
|
143
|
+
${INDENT} add-phone-to-users # scaffolds alterTable('users', ...)
|
|
144
|
+
${INDENT} remove-status-from-posts # scaffolds alterTable('posts', ...)
|
|
145
|
+
${INDENT} create-join-table-host-places # empty migration (no table suffix match)`)
|
|
146
|
+
.option('--connection-name <connectionName>', 'the database connection to use for this migration. Only needed for multi-database setups; defaults to "default"')
|
|
124
147
|
.argument('[columnsWithTypes...]', columnsWithTypesDescriptionForMigration)
|
|
125
148
|
.action(async (migrationName, columnsWithTypes, options) => {
|
|
126
149
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true, bypassDbConnectionsDuringInit: true });
|
|
@@ -132,15 +155,47 @@ export default class DreamCLI {
|
|
|
132
155
|
.alias('g:model')
|
|
133
156
|
.alias('generate:dream')
|
|
134
157
|
.alias('g:dream')
|
|
135
|
-
.description(
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
158
|
+
.description(`Generates a Dream model with corresponding spec factory, serializer, and migration. Use this when the model will NOT be accessible via HTTP requests (e.g., internal join tables, data models with no API). For HTTP-accessible models, prefer g:resource which also generates a controller and specs.
|
|
159
|
+
${INDENT}
|
|
160
|
+
${INDENT}Examples:
|
|
161
|
+
${INDENT} # Simple model
|
|
162
|
+
${INDENT} pnpm psy g:model Tag value:citext
|
|
163
|
+
${INDENT}
|
|
164
|
+
${INDENT} # Join table model
|
|
165
|
+
${INDENT} pnpm psy g:model HostPlace Host:belongs_to Place:belongs_to
|
|
166
|
+
${INDENT}
|
|
167
|
+
${INDENT} # STI parent model (use with g:sti-child for children)
|
|
168
|
+
${INDENT} pnpm psy g:model --sti-base-serializer Room Place:belongs_to type:enum:room_types:Bathroom,Bedroom deleted_at:datetime:optional`)
|
|
169
|
+
.option('--no-serializer', 'skip serializer generation. Useful for internal models that will never be serialized in an API response (e.g., join tables, audit logs)')
|
|
170
|
+
.option('--connection-name <connectionName>', 'the name of the database connection to use for the model. Only needed for multi-database setups; defaults to "default"', 'default')
|
|
171
|
+
.option('--sti-base-serializer', `Creates generically typed base serializers (default and summary) that accept a \`StiChildClass\` parameter and include the \`type\` attribute with a per-child enum constraint. This allows consuming applications to determine the response shape based on the STI type discriminator.
|
|
172
|
+
${INDENT}
|
|
173
|
+
${INDENT}Use this when generating the parent model of an STI hierarchy. After generating the parent, use g:sti-child for each child type.
|
|
174
|
+
${INDENT}
|
|
175
|
+
${INDENT}Example:
|
|
176
|
+
${INDENT} # CRITICAL: the type enums must exactly match the class names of the STI children
|
|
177
|
+
${INDENT} pnpm psy g:model --sti-base-serializer Rental type:enum:place_types:Apartment,House,Condo
|
|
178
|
+
${INDENT} # STI children subsequently generated using the g:sti-child generator (note the use of \`--model-name\` to generate class names that match the \`type\` column, e.g., "Apartment" instead of the "RentalApartment" default):
|
|
179
|
+
${INDENT} pnpm psy g:sti-child --model-name=Apartment Rental/Apartment extends Rental
|
|
180
|
+
${INDENT} pnpm psy g:sti-child --model-name=House Rental/House extends Rental
|
|
181
|
+
${INDENT} pnpm psy g:sti-child --model-name=Condo Rental/Condo extends Rental`, false)
|
|
182
|
+
.option('--table-name <tableName>', `Explicit table name to use instead of the auto-generated one. Useful when model namespaces produce long or awkward table names.
|
|
183
|
+
${INDENT}
|
|
184
|
+
${INDENT}Example:
|
|
185
|
+
${INDENT} pnpm psy g:model --table-name=notif_prefs Settings/NotificationPreferences User:belongs_to`)
|
|
186
|
+
.option('--model-name <modelName>', `Explicit model class name to use instead of the one auto-derived from the model path. Useful when the path segments don't match the desired class name.
|
|
187
|
+
${INDENT}
|
|
188
|
+
${INDENT}Example:
|
|
189
|
+
${INDENT} pnpm psy g:model --model-name=GroupDanceLesson Lesson/Dance/Group
|
|
190
|
+
${INDENT} # model is named GroupDanceLesson instead of LessonDanceGroup`)
|
|
191
|
+
.option('--admin-serializers', 'also generate AdminSerializer and AdminSummarySerializer variants for admin-facing API endpoints that may expose additional fields', false)
|
|
192
|
+
.option('--internal-serializers', 'also generate InternalSerializer and InternalSummarySerializer variants for internal API endpoints that may expose additional fields', false)
|
|
193
|
+
.argument('<modelName>', `The fully qualified model name, using / for namespacing. This determines the model class name (may be overridden with \`--model-name\`), table name, and file path under src/app/models/.
|
|
194
|
+
${INDENT}
|
|
195
|
+
${INDENT}Examples:
|
|
196
|
+
${INDENT} Post # src/app/models/Post.ts, table: posts
|
|
197
|
+
${INDENT} HostPlace # src/app/models/HostPlace.ts, table: host_places
|
|
198
|
+
${INDENT} Settings/CommunicationPreferences # src/app/models/Settings/CommunicationPreferences.ts`)
|
|
144
199
|
.argument('[columnsWithTypes...]', columnsWithTypesDescription)
|
|
145
200
|
.action(async (modelName, columnsWithTypes, options) => {
|
|
146
201
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true, bypassDbConnectionsDuringInit: true });
|
|
@@ -150,17 +205,40 @@ export default class DreamCLI {
|
|
|
150
205
|
program
|
|
151
206
|
.command('generate:sti-child')
|
|
152
207
|
.alias('g:sti-child')
|
|
153
|
-
.description(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
${INDENT}
|
|
163
|
-
${INDENT}
|
|
208
|
+
.description(`Generates an STI (Single Table Inheritance) child model that extends an existing parent model. The child shares the parent's database table (discriminated by the \`type\` column) and can add child-specific columns. Generates a child model decorated with @STI(Parent), child serializers extending the parent's base serializers, a migration that ALTERs the parent table (not a new table), check constraints, a factory, and spec skeleton.
|
|
209
|
+
${INDENT}
|
|
210
|
+
${INDENT}The parent must already exist (typically generated with g:model --sti-base-serializer or g:resource --sti-base-serializer).
|
|
211
|
+
${INDENT}
|
|
212
|
+
${INDENT}Examples:
|
|
213
|
+
${INDENT} # Child with an enum column
|
|
214
|
+
${INDENT} pnpm psy g:sti-child --model-name=Bathroom Room/Bathroom extends Room bath_or_shower_style:enum:bath_or_shower_styles:bath,shower,none
|
|
215
|
+
${INDENT}
|
|
216
|
+
${INDENT} # Child with an enum array column
|
|
217
|
+
${INDENT} pnpm psy g:sti-child --model-name=Bedroom Room/Bedroom extends Room bed_types:enum[]:bed_types:twin,queen,king
|
|
218
|
+
${INDENT}
|
|
219
|
+
${INDENT} # Child with no additional columns
|
|
220
|
+
${INDENT} pnpm psy g:sti-child --model-name=Kitchen Room/Kitchen extends Room`)
|
|
221
|
+
.option('--no-serializer', 'skip serializer generation. Useful if the child uses the parent serializer directly or serialization is handled elsewhere')
|
|
222
|
+
.option('--connection-name', 'the name of the database connection to use for the model. Only needed for multi-database setups; defaults to "default"', 'default')
|
|
223
|
+
.option('--model-name <modelName>', `Explicit model class name to use instead of the one auto-derived from the model path. Useful when the path segments don't match the desired class name.
|
|
224
|
+
${INDENT}
|
|
225
|
+
${INDENT}Example:
|
|
226
|
+
${INDENT} pnpm psy g:sti-child --model-name=GroupDanceLesson Lesson/Dance/Group extends Lesson
|
|
227
|
+
${INDENT} # model is named GroupDanceLesson instead of LessonDanceGroup`)
|
|
228
|
+
.option('--admin-serializers', 'also generate AdminSerializer and AdminSummarySerializer variants for admin-facing API endpoints that may expose additional fields', false)
|
|
229
|
+
.option('--internal-serializers', 'also generate InternalSerializer and InternalSummarySerializer variants for internal API endpoints that may expose additional fields', false)
|
|
230
|
+
.argument('<childModelName>', `The namespaced child model path. By convention, children are nested under the parent name.
|
|
231
|
+
${INDENT}
|
|
232
|
+
${INDENT}Examples:
|
|
233
|
+
${INDENT} Room/Bathroom # src/app/models/Room/Bathroom.ts
|
|
234
|
+
${INDENT} Room/Bedroom # src/app/models/Room/Bedroom.ts
|
|
235
|
+
${INDENT} Vehicle/Truck # src/app/models/Vehicle/Truck.ts`)
|
|
236
|
+
.argument('<extends>', 'the literal word "extends" (required syntax)')
|
|
237
|
+
.argument('<parentModelName>', `Fully qualified name of the parent STI model to extend. Must match the parent's path under src/app/models/.
|
|
238
|
+
${INDENT}
|
|
239
|
+
${INDENT}Examples:
|
|
240
|
+
${INDENT} Room # extends src/app/models/Room.ts
|
|
241
|
+
${INDENT} Health/Coach # extends src/app/models/Health/Coach.ts`)
|
|
164
242
|
.argument('[columnsWithTypes...]', columnsWithTypesDescription)
|
|
165
243
|
.action(async (childModelName, extendsWord, parentModelName, columnsWithTypes, options) => {
|
|
166
244
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true, bypassDbConnectionsDuringInit: true });
|
|
@@ -172,8 +250,18 @@ ${INDENT} to extend the Coach model in src/app/models/Health/Coach: Health/Co
|
|
|
172
250
|
program
|
|
173
251
|
.command('generate:encryption-key')
|
|
174
252
|
.alias('g:encryption-key')
|
|
175
|
-
.description(
|
|
176
|
-
|
|
253
|
+
.description(`Generates a cryptographically secure encryption key and prints it to stdout. Use this to create keys for any Dream/Psychic encryption use case:
|
|
254
|
+
${INDENT}
|
|
255
|
+
${INDENT} - @deco.Encrypted() model columns (e.g., phone numbers, SSNs)
|
|
256
|
+
${INDENT} - Cookie encryption/decryption in Psychic sessions
|
|
257
|
+
${INDENT} - General-purpose use of the Dream Encrypt library
|
|
258
|
+
${INDENT}
|
|
259
|
+
${INDENT}Store the generated key in your environment variables (e.g., ENCRYPTION_KEY). Never commit keys to source control.
|
|
260
|
+
${INDENT}
|
|
261
|
+
${INDENT}Example:
|
|
262
|
+
${INDENT} pnpm psy g:encryption-key # generates aes-256-gcm key (default)
|
|
263
|
+
${INDENT} pnpm psy g:encryption-key --algorithm=aes-128-gcm`)
|
|
264
|
+
.addOption(new Option('--algorithm <algorithm>', 'the encryption algorithm to generate a key for. aes-256-gcm (default) is recommended for most use cases')
|
|
177
265
|
.choices(['aes-256-gcm', 'aes-192-gcm', 'aes-128-gcm'])
|
|
178
266
|
.default('aes-256-gcm'))
|
|
179
267
|
.action((options) => {
|
|
@@ -183,7 +271,7 @@ ${INDENT} to extend the Coach model in src/app/models/Health/Coach: Health/Co
|
|
|
183
271
|
});
|
|
184
272
|
program
|
|
185
273
|
.command('db:create')
|
|
186
|
-
.description('Creates a new database.')
|
|
274
|
+
.description('Creates the database defined in your Dream configuration. Run this once when setting up a new development environment, or after db:drop. Safe to run if the database already exists.')
|
|
187
275
|
.action(async () => {
|
|
188
276
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true, bypassDbConnectionsDuringInit: true });
|
|
189
277
|
await DreamBin.dbCreate();
|
|
@@ -191,7 +279,7 @@ ${INDENT} to extend the Coach model in src/app/models/Health/Coach: Health/Co
|
|
|
191
279
|
});
|
|
192
280
|
program
|
|
193
281
|
.command('db:integrity-check')
|
|
194
|
-
.description('
|
|
282
|
+
.description('Checks that all migrations have been run and exits with code 1 if any are pending. Useful as a CI check or deploy gate to ensure the database schema is up to date before starting the application.')
|
|
195
283
|
.action(async () => {
|
|
196
284
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true });
|
|
197
285
|
await DreamBin.dbEnsureAllMigrationsHaveBeenRun();
|
|
@@ -199,8 +287,13 @@ ${INDENT} to extend the Coach model in src/app/models/Health/Coach: Health/Co
|
|
|
199
287
|
});
|
|
200
288
|
program
|
|
201
289
|
.command('db:migrate')
|
|
202
|
-
.description(
|
|
203
|
-
|
|
290
|
+
.description(`Runs all pending database migrations in order, then automatically syncs types (in development/test). This is the primary command for applying schema changes after generating or editing a migration.
|
|
291
|
+
${INDENT}
|
|
292
|
+
${INDENT}Example workflow:
|
|
293
|
+
${INDENT} pnpm psy g:migration add-phone-to-users phone:string:optional
|
|
294
|
+
${INDENT} # edit the migration if needed (e.g., add unique constraints)
|
|
295
|
+
${INDENT} pnpm psy db:migrate`)
|
|
296
|
+
.option('--skip-sync', 'skip the automatic sync after migrating. Useful when running migrations in production or when you plan to sync manually afterward', false)
|
|
204
297
|
.action(async ({ skipSync }) => {
|
|
205
298
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true });
|
|
206
299
|
await DreamBin.dbMigrate();
|
|
@@ -211,9 +304,13 @@ ${INDENT} to extend the Coach model in src/app/models/Health/Coach: Health/Co
|
|
|
211
304
|
});
|
|
212
305
|
program
|
|
213
306
|
.command('db:rollback')
|
|
214
|
-
.description(
|
|
215
|
-
|
|
216
|
-
|
|
307
|
+
.description(`Rolls back the most recent migration(s), then automatically syncs types (in development/test). Use this to undo a migration so you can edit and re-run it.
|
|
308
|
+
${INDENT}
|
|
309
|
+
${INDENT}Examples:
|
|
310
|
+
${INDENT} pnpm psy db:rollback # rolls back the last migration
|
|
311
|
+
${INDENT} pnpm psy db:rollback --steps=3 # rolls back the last 3 migrations`)
|
|
312
|
+
.option('--steps <number>', 'number of migration steps to roll back (default: 1)', myParseInt, 1)
|
|
313
|
+
.option('--skip-sync', 'skip the automatic sync after rolling back. Useful when you plan to immediately re-migrate or sync manually', false)
|
|
217
314
|
.action(async ({ steps, skipSync }) => {
|
|
218
315
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true });
|
|
219
316
|
await DreamBin.dbRollback({ steps });
|
|
@@ -224,7 +321,7 @@ ${INDENT} to extend the Coach model in src/app/models/Health/Coach: Health/Co
|
|
|
224
321
|
});
|
|
225
322
|
program
|
|
226
323
|
.command('db:drop')
|
|
227
|
-
.description('Drops the database')
|
|
324
|
+
.description('Drops the database. This is a destructive operation — all data will be lost. Primarily used as part of db:reset or when you need a clean slate during development.')
|
|
228
325
|
.action(async () => {
|
|
229
326
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true, bypassDbConnectionsDuringInit: true });
|
|
230
327
|
await DreamBin.dbDrop();
|
|
@@ -232,20 +329,53 @@ ${INDENT} to extend the Coach model in src/app/models/Health/Coach: Health/Co
|
|
|
232
329
|
});
|
|
233
330
|
program
|
|
234
331
|
.command('db:reset')
|
|
235
|
-
.description(
|
|
332
|
+
.description(`Completely resets the database by running db:drop, db:create, db:migrate, sync, and db:seed in sequence. Use this when:
|
|
333
|
+
${INDENT}
|
|
334
|
+
${INDENT} - Switching between branches with incompatible migrations ("corrupted migrations" error)
|
|
335
|
+
${INDENT} - Starting fresh after a schema has diverged significantly
|
|
336
|
+
${INDENT} - Setting up a clean development environment
|
|
337
|
+
${INDENT}
|
|
338
|
+
${INDENT}Warning: all existing data will be lost. The seed file (db/seed.ts) will be run to repopulate initial data.`)
|
|
236
339
|
.action(async () => {
|
|
237
340
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true, bypassDbConnectionsDuringInit: true });
|
|
341
|
+
const arrows = colorize('⭣⭣⭣', { color: 'green' }) + '\n';
|
|
342
|
+
DreamCLI.logger.log(colorize('db:drop', { color: 'green' }), {
|
|
343
|
+
logPrefix: ' ',
|
|
344
|
+
logPrefixColor: 'green',
|
|
345
|
+
});
|
|
238
346
|
await DreamBin.dbDrop();
|
|
347
|
+
DreamCLI.logger.log(arrows, { logPrefix: ' ' });
|
|
348
|
+
DreamCLI.logger.log(colorize('db:create', { color: 'green' }), {
|
|
349
|
+
logPrefix: ' ',
|
|
350
|
+
logPrefixColor: 'green',
|
|
351
|
+
});
|
|
239
352
|
await DreamBin.dbCreate();
|
|
353
|
+
DreamCLI.logger.log(arrows, { logPrefix: ' ' });
|
|
240
354
|
await initializeDreamApp({ bypassDreamIntegrityChecks: true });
|
|
355
|
+
DreamCLI.logger.log(colorize('db:migrate', { color: 'green' }), {
|
|
356
|
+
logPrefix: ' ',
|
|
357
|
+
logPrefixColor: 'green',
|
|
358
|
+
});
|
|
241
359
|
await DreamBin.dbMigrate();
|
|
360
|
+
DreamCLI.logger.log(arrows, { logPrefix: ' ' });
|
|
361
|
+
DreamCLI.logger.log(colorize('sync', { color: 'green' }), {
|
|
362
|
+
logPrefix: ' ',
|
|
363
|
+
logPrefixColor: 'green',
|
|
364
|
+
});
|
|
242
365
|
await DreamBin.sync(onSync);
|
|
243
|
-
|
|
366
|
+
DreamCLI.logger.log(arrows, { logPrefix: ' ' });
|
|
367
|
+
DreamCLI.logger.log(colorize('db:seed', { color: 'green' }), {
|
|
368
|
+
logPrefix: ' ',
|
|
369
|
+
logPrefixColor: 'green',
|
|
370
|
+
});
|
|
371
|
+
await DreamCLI.logger.logProgress('seeding db...', async () => {
|
|
372
|
+
await seedDb();
|
|
373
|
+
});
|
|
244
374
|
process.exit();
|
|
245
375
|
});
|
|
246
376
|
program
|
|
247
377
|
.command('db:seed')
|
|
248
|
-
.description(
|
|
378
|
+
.description(`Seeds the database by running the seed function defined in db/seed.ts. Skipped automatically in test environments unless DREAM_SEED_DB_IN_TEST=1 is set. Runs automatically as the last step of db:reset.`)
|
|
249
379
|
.action(async () => {
|
|
250
380
|
if (process.env.NODE_ENV === 'test' && process.env.DREAM_SEED_DB_IN_TEST !== '1') {
|
|
251
381
|
DreamApp.log('skipping db seed for test env. To really seed for test, add DREAM_SEED_DB_IN_TEST=1');
|
|
@@ -258,9 +388,16 @@ ${INDENT} to extend the Coach model in src/app/models/Health/Coach: Health/Co
|
|
|
258
388
|
program
|
|
259
389
|
.command('inspect:serialization')
|
|
260
390
|
.alias('i:serialization')
|
|
261
|
-
.description(
|
|
262
|
-
|
|
263
|
-
|
|
391
|
+
.description(`Displays a detailed serialization map for a model, showing all attributes, associations, custom attributes, and their types. Useful for debugging serializer output, understanding what a model's API response will look like, and verifying that associations are preloaded correctly.
|
|
392
|
+
${INDENT}
|
|
393
|
+
${INDENT}Examples:
|
|
394
|
+
${INDENT} pnpm psy i:serialization Place # shows the default serializer for Place
|
|
395
|
+
${INDENT} pnpm psy i:serialization Place summary # shows the summary serializer for Place
|
|
396
|
+
${INDENT} pnpm psy i:serialization Room/Bedroom # shows serializer for an STI child`)
|
|
397
|
+
.argument('<globalName>', `The global name of the model as registered in Dream (typically matches the model class name or its fully qualified path).
|
|
398
|
+
${INDENT}
|
|
399
|
+
${INDENT}Examples: User, Place, Room/Bedroom, Settings/CommunicationPreferences`)
|
|
400
|
+
.argument('[serializerKey]', 'the serializer variant to display (e.g., "summary", "admin", "internal"). Defaults to "default" if omitted')
|
|
264
401
|
.action(async (globalName, serializerKey) => {
|
|
265
402
|
await initializeDreamApp();
|
|
266
403
|
const dreamApp = DreamApp.getOrFail();
|
|
@@ -1,25 +1,126 @@
|
|
|
1
|
+
import c from 'yoctocolors';
|
|
1
2
|
import DreamCliLoggableText from './loggable/DreamCliLoggableText.js';
|
|
3
|
+
const SPINNER_FRAMES = ['✺', '✹', '✸', '✷', '✶', '✵', '✴', '✵', '✶', '✷', '✸', '✹'];
|
|
4
|
+
const EYE_FRAMES = ['─', '◡', '◯', '◉', '●', '◉', '◯', '◡'];
|
|
5
|
+
const SPINNER_INTERVAL_MS = 200;
|
|
6
|
+
function formatElapsed(ms) {
|
|
7
|
+
if (ms < 1000)
|
|
8
|
+
return `${ms}ms`;
|
|
9
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
10
|
+
}
|
|
2
11
|
export default class DreamCliLogger {
|
|
12
|
+
spinner = null;
|
|
3
13
|
log(text, { logPrefix, logPrefixColor, logPrefixBgColor } = {}) {
|
|
4
14
|
const loggable = new DreamCliLoggableText(text, {
|
|
5
15
|
logPrefix,
|
|
6
|
-
logPrefixColor: logPrefixColor || '
|
|
16
|
+
logPrefixColor: logPrefixColor || 'greenBright',
|
|
7
17
|
logPrefixBgColor,
|
|
8
18
|
});
|
|
9
19
|
loggable.render();
|
|
10
20
|
}
|
|
11
21
|
async logProgress(text, cb, { logPrefix = '✺ ┌', logPrefixColor, logPrefixBgColor } = {}) {
|
|
12
|
-
this.
|
|
22
|
+
this.logStartProgress(text, { logPrefix, logPrefixColor, logPrefixBgColor });
|
|
13
23
|
await cb();
|
|
14
24
|
this.logEndProgress();
|
|
15
25
|
}
|
|
16
|
-
logStartProgress(text, {
|
|
17
|
-
|
|
26
|
+
logStartProgress(text, { logPrefixColor, logPrefixBgColor } = {}) {
|
|
27
|
+
if (!process.stdout.isTTY) {
|
|
28
|
+
this.log(text, { logPrefix: '✺ ┌', logPrefixColor: logPrefixColor || 'greenBright', logPrefixBgColor });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.startTopSpinner(text);
|
|
32
|
+
}
|
|
33
|
+
startTopSpinner(text) {
|
|
34
|
+
const renderFrame = (frameIndex) => {
|
|
35
|
+
const star = SPINNER_FRAMES[frameIndex % SPINNER_FRAMES.length];
|
|
36
|
+
process.stdout.write(`${this.escapeSequence('clearLine')}${c.yellow(`${star} ┌`)} ${c.yellow(text)}`);
|
|
37
|
+
};
|
|
38
|
+
renderFrame(0);
|
|
39
|
+
const interval = setInterval(() => {
|
|
40
|
+
if (this.spinner && !this.spinner.committed) {
|
|
41
|
+
this.spinner.frameIndex++;
|
|
42
|
+
renderFrame(this.spinner.frameIndex);
|
|
43
|
+
}
|
|
44
|
+
}, SPINNER_INTERVAL_MS);
|
|
45
|
+
this.spinner = {
|
|
46
|
+
actionText: text,
|
|
47
|
+
startTime: Date.now(),
|
|
48
|
+
frameIndex: 0,
|
|
49
|
+
interval,
|
|
50
|
+
committed: false,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
startBottomSpinner() {
|
|
54
|
+
if (!process.stdout.isTTY || !this.spinner)
|
|
55
|
+
return;
|
|
56
|
+
const renderFrame = (frameIndex) => {
|
|
57
|
+
const eye = EYE_FRAMES[frameIndex % EYE_FRAMES.length];
|
|
58
|
+
process.stdout.write(`${this.escapeSequence('clearLine')}${c.yellow(eye)} ${c.yellow(`└ ${this.spinner.actionText}`)}`);
|
|
59
|
+
};
|
|
60
|
+
this.spinner.frameIndex = 0;
|
|
61
|
+
renderFrame(0);
|
|
62
|
+
this.spinner.interval = setInterval(() => {
|
|
63
|
+
if (this.spinner) {
|
|
64
|
+
this.spinner.frameIndex++;
|
|
65
|
+
renderFrame(this.spinner.frameIndex);
|
|
66
|
+
}
|
|
67
|
+
}, SPINNER_INTERVAL_MS);
|
|
18
68
|
}
|
|
19
69
|
logContinueProgress(text, { logPrefix = ' ├', logPrefixColor, logPrefixBgColor } = {}) {
|
|
20
|
-
this.
|
|
70
|
+
if (this.spinner && !this.spinner.committed) {
|
|
71
|
+
clearInterval(this.spinner.interval);
|
|
72
|
+
// Overwrite the yellow animated header with green past-tense text before marking
|
|
73
|
+
// the spinner as committed
|
|
74
|
+
process.stdout.write(`${this.escapeSequence('clearLine')}${c.greenBright('✺ ┌')} ${c.greenBright(this.spinner.actionText)}\n`);
|
|
75
|
+
this.spinner.committed = true;
|
|
76
|
+
this.log(text, { logPrefix, logPrefixColor, logPrefixBgColor });
|
|
77
|
+
this.startBottomSpinner();
|
|
78
|
+
}
|
|
79
|
+
else if (this.spinner?.committed) {
|
|
80
|
+
// Clear the bottom running... spinner, log sub-line, then re-show it
|
|
81
|
+
clearInterval(this.spinner.interval);
|
|
82
|
+
this.clearLine();
|
|
83
|
+
this.log(text, { logPrefix, logPrefixColor, logPrefixBgColor });
|
|
84
|
+
this.startBottomSpinner();
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
this.log(text, { logPrefix, logPrefixColor, logPrefixBgColor });
|
|
88
|
+
}
|
|
21
89
|
}
|
|
22
90
|
logEndProgress(text = 'complete', { logPrefix = ' └', logPrefixColor, logPrefixBgColor } = {}) {
|
|
23
|
-
this.
|
|
91
|
+
if (!this.spinner) {
|
|
92
|
+
this.log(text, { logPrefix, logPrefixColor, logPrefixBgColor });
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const spinner = this.spinner;
|
|
96
|
+
this.spinner = null;
|
|
97
|
+
clearInterval(spinner.interval);
|
|
98
|
+
const elapsed = Date.now() - spinner.startTime;
|
|
99
|
+
const elapsedStr = formatElapsed(elapsed);
|
|
100
|
+
if (!spinner.committed) {
|
|
101
|
+
// No sub-lines: overwrite the spinner in-place with the green completed header
|
|
102
|
+
process.stdout.write(`${this.escapeSequence('clearLine')}${c.greenBright('✺ ┌')} ${c.greenBright(spinner.actionText)}\n`);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Has sub-lines: clear the bottom running... spinner line
|
|
106
|
+
if (process.stdout.isTTY)
|
|
107
|
+
this.clearLine();
|
|
108
|
+
}
|
|
109
|
+
this.log(c.greenBright(`done in ${elapsedStr}\n`), {
|
|
110
|
+
logPrefix,
|
|
111
|
+
logPrefixColor: logPrefixColor || 'greenBright',
|
|
112
|
+
logPrefixBgColor,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
clearLine() {
|
|
116
|
+
process.stdout.write(this.escapeSequence('clearLine'));
|
|
117
|
+
}
|
|
118
|
+
escapeSequence(sequence) {
|
|
119
|
+
switch (sequence) {
|
|
120
|
+
case 'clearLine':
|
|
121
|
+
return `\r\x1b[2K`;
|
|
122
|
+
default:
|
|
123
|
+
throw new Error(`unexpected sequence: ${sequence}`);
|
|
124
|
+
}
|
|
24
125
|
}
|
|
25
126
|
}
|
|
@@ -28,8 +28,8 @@ export default async function syncDbTypesFiles(connectionName) {
|
|
|
28
28
|
const kyselyCodegenCmd = `kysely-codegen ${dialect} ${url} ${includePattern} ${excludePattern} ${outfile}`;
|
|
29
29
|
await sspawn(kyselyCodegenCmd, {
|
|
30
30
|
onStdout: message => {
|
|
31
|
-
DreamCLI.logger.logContinueProgress(colorize(`[db]`, { color: '
|
|
32
|
-
logPrefixColor: '
|
|
31
|
+
DreamCLI.logger.logContinueProgress(colorize(`[db]`, { color: 'greenBright' }) + ' ' + message, {
|
|
32
|
+
logPrefixColor: 'greenBright',
|
|
33
33
|
});
|
|
34
34
|
},
|
|
35
35
|
});
|
|
@@ -157,13 +157,13 @@ export default class PostgresQueryDriver extends KyselyQueryDriver {
|
|
|
157
157
|
const client = await loadPgClient({ useSystemDb: true, connectionName });
|
|
158
158
|
if (EnvInternal.boolean('DREAM_CORE_DEVELOPMENT')) {
|
|
159
159
|
const replicaTestWorkerDatabaseName = `replica_test_${dbConf.name}`;
|
|
160
|
-
DreamCLI.logger.logContinueProgress(`creating fake replica test database ${replicaTestWorkerDatabaseName}...`, { logPrefix: ' ├ [db]', logPrefixColor: '
|
|
160
|
+
DreamCLI.logger.logContinueProgress(`creating fake replica test database ${replicaTestWorkerDatabaseName}...`, { logPrefix: ' ├ [db]', logPrefixColor: 'greenBright' });
|
|
161
161
|
await client.query(`DROP DATABASE IF EXISTS ${replicaTestWorkerDatabaseName};`);
|
|
162
162
|
await client.query(`CREATE DATABASE ${replicaTestWorkerDatabaseName} TEMPLATE ${dbConf.name};`);
|
|
163
163
|
}
|
|
164
164
|
for (let i = 2; i <= parallelTests; i++) {
|
|
165
165
|
const workerDatabaseName = `${dbConf.name}_${i}`;
|
|
166
|
-
DreamCLI.logger.logContinueProgress(`creating duplicate test database ${workerDatabaseName} for concurrent tests...`, { logPrefix: ' ├ [db]', logPrefixColor: '
|
|
166
|
+
DreamCLI.logger.logContinueProgress(`creating duplicate test database ${workerDatabaseName} for concurrent tests...`, { logPrefix: ' ├ [db]', logPrefixColor: 'greenBright' });
|
|
167
167
|
await client.query(`DROP DATABASE IF EXISTS ${workerDatabaseName};`);
|
|
168
168
|
await client.query(`CREATE DATABASE ${workerDatabaseName} TEMPLATE ${dbConf.name};`);
|
|
169
169
|
}
|
|
@@ -88,8 +88,8 @@ function migratedActionPastTense(mode) {
|
|
|
88
88
|
function logResults(results, mode) {
|
|
89
89
|
results?.forEach(it => {
|
|
90
90
|
if (it.status === 'Success') {
|
|
91
|
-
DreamCLI.logger.logContinueProgress(colorize(`[db]`, { color: '
|
|
92
|
-
` migration "${it.migrationName}" was ${migratedActionPastTense(mode)} successfully`, { logPrefixColor: '
|
|
91
|
+
DreamCLI.logger.logContinueProgress(colorize(`[db]`, { color: 'greenBright' }) +
|
|
92
|
+
` migration "${it.migrationName}" was ${migratedActionPastTense(mode)} successfully`, { logPrefixColor: 'greenBright' });
|
|
93
93
|
}
|
|
94
94
|
else if (it.status === 'Error') {
|
|
95
95
|
DreamCLI.logger.logContinueProgress(JSON.stringify(it, null, 2));
|
|
@@ -22,14 +22,14 @@ async function maybeDropDuplicateDatabases(client, dbName) {
|
|
|
22
22
|
return;
|
|
23
23
|
if (EnvInternal.boolean('DREAM_CORE_DEVELOPMENT')) {
|
|
24
24
|
const replicaTestWorkerDatabaseName = `replica_test_${dbName}`;
|
|
25
|
-
DreamCLI.logger.logContinueProgress(`dropping fake replica test database ${replicaTestWorkerDatabaseName}`, { logPrefix: ' ├ [db]', logPrefixColor: '
|
|
25
|
+
DreamCLI.logger.logContinueProgress(`dropping fake replica test database ${replicaTestWorkerDatabaseName}`, { logPrefix: ' ├ [db]', logPrefixColor: 'greenBright' });
|
|
26
26
|
await client.query(`DROP DATABASE IF EXISTS ${replicaTestWorkerDatabaseName};`);
|
|
27
27
|
}
|
|
28
28
|
for (let i = 2; i <= parallelTests; i++) {
|
|
29
29
|
const workerDatabaseName = `${dbName}_${i}`;
|
|
30
30
|
DreamCLI.logger.logContinueProgress(`dropping duplicate test database ${workerDatabaseName}`, {
|
|
31
31
|
logPrefix: ' ├ [db]',
|
|
32
|
-
logPrefixColor: '
|
|
32
|
+
logPrefixColor: 'greenBright',
|
|
33
33
|
});
|
|
34
34
|
await client.query(`DROP DATABASE IF EXISTS ${workerDatabaseName};`);
|
|
35
35
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { DreamCliLoggerLogOpts } from '../../types/logger.js';
|
|
2
2
|
export default class DreamCliLogger {
|
|
3
|
+
private spinner;
|
|
3
4
|
log(text: string, { logPrefix, logPrefixColor, logPrefixBgColor }?: DreamCliLoggerLogOpts): void;
|
|
4
5
|
logProgress(text: string, cb: () => void | Promise<void>, { logPrefix, logPrefixColor, logPrefixBgColor }?: DreamCliLoggerLogOpts): Promise<void>;
|
|
5
|
-
logStartProgress(text: string, {
|
|
6
|
+
logStartProgress(text: string, { logPrefixColor, logPrefixBgColor }?: DreamCliLoggerLogOpts): void;
|
|
7
|
+
private startTopSpinner;
|
|
8
|
+
private startBottomSpinner;
|
|
6
9
|
logContinueProgress(text: string, { logPrefix, logPrefixColor, logPrefixBgColor }?: DreamCliLoggerLogOpts): void;
|
|
7
10
|
logEndProgress(text?: string, { logPrefix, logPrefixColor, logPrefixBgColor }?: DreamCliLoggerLogOpts): void;
|
|
11
|
+
private clearLine;
|
|
12
|
+
private escapeSequence;
|
|
8
13
|
}
|