@@ -132,71 +132,205 @@ export class DrizzleCommands {
132132 } ) ,
133133 ) ,
134134 handler : async ( { flags, args } ) => {
135- const rootDir = join ( process . cwd ( ) , flags . root ) ;
136- this . log . debug ( `Using project root: ${ rootDir } ` ) ;
135+ await this . runDrizzleKitCommand ( {
136+ flags,
137+ args,
138+ command : "generate" ,
139+ logMessage : ( providerName , dialect ) =>
140+ `Generate '${ providerName } ' migrations (${ dialect } ) ...` ,
141+ } ) ;
142+ } ,
143+ } ) ;
137144
138- const { alepha, entry } = await this . loadAlephaFromServerEntryFile (
139- rootDir ,
145+ /**
146+ * Push database schema changes directly to the database
147+ *
148+ * - Loads the Alepha instance from the specified entry file.
149+ * - Retrieves all repository descriptors to gather database models.
150+ * - Creates temporary entity definitions and Drizzle config.
151+ * - Invokes Drizzle Kit's push command to apply schema changes directly.
152+ */
153+ push = $command ( {
154+ name : "db:push" ,
155+ description : "Push database schema changes directly to the database" ,
156+ summary : false ,
157+ flags : this . flags ,
158+ args : t . optional (
159+ t . text ( {
160+ description : "Path to the Alepha server entry file" ,
161+ } ) ,
162+ ) ,
163+ handler : async ( { flags, args } ) => {
164+ await this . runDrizzleKitCommand ( {
165+ flags,
140166 args,
141- ) ;
167+ command : "push" ,
168+ logMessage : ( providerName , dialect ) =>
169+ `Push '${ providerName } ' schema (${ dialect } ) ...` ,
170+ } ) ;
171+ } ,
172+ } ) ;
142173
143- const inject = ( name : string ) =>
144- // biome-ignore lint/complexity/useLiteralKeys: private key
145- alepha [ "registry" ]
146- . values ( )
147- . find ( ( it : any ) => it . instance . constructor . name === name ) ?. instance ;
148-
149- // ---------------------------------------------------------------------------------------------------------------
150- const kit = inject ( "DrizzleKitProvider" ) ;
151- const accepted = new Set < string > ( [ ] ) ;
152- for ( const descriptor of alepha . descriptors ( "repository" ) as any [ ] ) {
153- const provider = descriptor . provider ;
154- const providerName = provider . name ;
155- const dialect = provider . dialect ;
156-
157- if ( ! accepted . has ( providerName ) ) {
158- accepted . add ( providerName ) ;
159- } else {
160- continue ;
161- }
174+ /**
175+ * Apply pending database migrations
176+ *
177+ * - Loads the Alepha instance from the specified entry file.
178+ * - Retrieves all repository descriptors to gather database models.
179+ * - Creates temporary entity definitions and Drizzle config.
180+ * - Invokes Drizzle Kit's migrate command to apply pending migrations.
181+ */
182+ migrate = $command ( {
183+ name : "db:migrate" ,
184+ description : "Apply pending database migrations" ,
185+ summary : false ,
186+ flags : this . flags ,
187+ args : t . optional (
188+ t . text ( {
189+ description : "Path to the Alepha server entry file" ,
190+ } ) ,
191+ ) ,
192+ handler : async ( { flags, args } ) => {
193+ await this . runDrizzleKitCommand ( {
194+ flags,
195+ args,
196+ command : "migrate" ,
197+ logMessage : ( providerName , dialect ) =>
198+ `Migrate '${ providerName } ' database (${ dialect } ) ...` ,
199+ } ) ;
200+ } ,
201+ } ) ;
162202
163- this . log . info ( "" ) ;
164- this . log . info ( `Generate '${ providerName } ' migrations (${ dialect } ) ...` ) ;
165-
166- const models = Object . keys ( kit . getModels ( provider ) ) ;
167- const entitiesJs = this . generateEntitiesJs ( entry , providerName , models ) ;
168-
169- const entitiesJsPath = await this . runner . writeConfigFile (
170- "entities.js" ,
171- entitiesJs ,
172- rootDir ,
173- ) ;
174-
175- const drizzleConfigJs =
176- "export default " +
177- JSON . stringify (
178- {
179- schema : entitiesJsPath ,
180- out : `./migrations/${ providerName } ` ,
181- dialect : dialect ,
182- } ,
183- null ,
184- 2 ,
185- ) ;
186-
187- const drizzleConfigJsPath = await this . runner . writeConfigFile (
188- "drizzle.config.js" ,
189- drizzleConfigJs ,
190- rootDir ,
191- ) ;
192-
193- await this . runner . exec (
194- `drizzle-kit generate --config=${ drizzleConfigJsPath } ` ,
195- ) ;
196- }
203+ /**
204+ * Launch Drizzle Studio database browser
205+ *
206+ * - Loads the Alepha instance from the specified entry file.
207+ * - Retrieves all repository descriptors to gather database models.
208+ * - Creates temporary entity definitions and Drizzle config.
209+ * - Invokes Drizzle Kit's studio command to launch the web-based database browser.
210+ */
211+ studio = $command ( {
212+ name : "db:studio" ,
213+ description : "Launch Drizzle Studio database browser" ,
214+ summary : false ,
215+ flags : this . flags ,
216+ args : t . optional (
217+ t . text ( {
218+ description : "Path to the Alepha server entry file" ,
219+ } ) ,
220+ ) ,
221+ handler : async ( { flags, args } ) => {
222+ await this . runDrizzleKitCommand ( {
223+ flags,
224+ args,
225+ command : "studio" ,
226+ logMessage : ( providerName , dialect ) =>
227+ `Launch Studio for '${ providerName } ' (${ dialect } ) ...` ,
228+ } ) ;
197229 } ,
198230 } ) ;
199231
232+ /**
233+ * Run a drizzle-kit command for all database providers
234+ */
235+ protected async runDrizzleKitCommand ( options : {
236+ flags : { root : string } ;
237+ args ?: string ;
238+ command : string ;
239+ logMessage : ( providerName : string , dialect : string ) => string ;
240+ } ) : Promise < void > {
241+ const rootDir = join ( process . cwd ( ) , options . flags . root ) ;
242+ this . log . debug ( `Using project root: ${ rootDir } ` ) ;
243+
244+ const { alepha, entry } = await this . loadAlephaFromServerEntryFile (
245+ rootDir ,
246+ options . args ,
247+ ) ;
248+
249+ const kit = this . getKitFromAlepha ( alepha ) ;
250+ const accepted = new Set < string > ( [ ] ) ;
251+
252+ for ( const descriptor of alepha . descriptors ( "repository" ) as any [ ] ) {
253+ const provider = descriptor . provider ;
254+ const providerName = provider . name ;
255+ const dialect = provider . dialect ;
256+
257+ if ( accepted . has ( providerName ) ) {
258+ continue ;
259+ }
260+ accepted . add ( providerName ) ;
261+
262+ this . log . info ( "" ) ;
263+ this . log . info ( options . logMessage ( providerName , dialect ) ) ;
264+
265+ const drizzleConfigJsPath = await this . prepareDrizzleConfig ( {
266+ kit,
267+ provider,
268+ providerName,
269+ dialect,
270+ entry,
271+ rootDir,
272+ } ) ;
273+
274+ await this . runner . exec (
275+ `drizzle-kit ${ options . command } --config=${ drizzleConfigJsPath } ` ,
276+ ) ;
277+ }
278+ }
279+
280+ /**
281+ * Prepare Drizzle configuration files for a provider
282+ */
283+ protected async prepareDrizzleConfig ( options : {
284+ kit : any ;
285+ provider : any ;
286+ providerName : string ;
287+ dialect : string ;
288+ entry : string ;
289+ rootDir : string ;
290+ } ) : Promise < string > {
291+ const models = Object . keys ( options . kit . getModels ( options . provider ) ) ;
292+ const entitiesJs = this . generateEntitiesJs (
293+ options . entry ,
294+ options . providerName ,
295+ models ,
296+ ) ;
297+
298+ const entitiesJsPath = await this . runner . writeConfigFile (
299+ "entities.js" ,
300+ entitiesJs ,
301+ options . rootDir ,
302+ ) ;
303+
304+ const drizzleConfigJs =
305+ "export default " +
306+ JSON . stringify (
307+ {
308+ schema : entitiesJsPath ,
309+ out : `./migrations/${ options . providerName } ` ,
310+ dialect : options . dialect ,
311+ } ,
312+ null ,
313+ 2 ,
314+ ) ;
315+
316+ return await this . runner . writeConfigFile (
317+ "drizzle.config.js" ,
318+ drizzleConfigJs ,
319+ options . rootDir ,
320+ ) ;
321+ }
322+
323+ /**
324+ * Get DrizzleKitProvider from Alepha instance
325+ */
326+ protected getKitFromAlepha ( alepha : Alepha ) : any {
327+ // biome-ignore lint/complexity/useLiteralKeys: private key
328+ return alepha [ "registry" ]
329+ . values ( )
330+ . find ( ( it : any ) => it . instance . constructor . name === "DrizzleKitProvider" )
331+ ?. instance ;
332+ }
333+
200334 public async loadAlephaFromServerEntryFile (
201335 rootDir ?: string ,
202336 explicitEntry ?: string ,
0 commit comments