88 type GenericQueryCtx ,
99 getFunctionAddress ,
1010 getFunctionName ,
11+ internalActionGeneric ,
1112 internalMutationGeneric ,
1213 makeFunctionReference ,
1314 type MutationBuilder ,
@@ -25,7 +26,7 @@ import {
2526} from "../shared.js" ;
2627export type { MigrationArgs , MigrationResult , MigrationStatus } ;
2728
28- import { ConvexError , type GenericId } from "convex/values" ;
29+ import { ConvexError , v , type GenericId } from "convex/values" ;
2930import type { ComponentApi } from "../component/_generated/component.js" ;
3031import { logStatusAndInstructions } from "./log.js" ;
3132import type { MigrationFunctionHandle } from "../component/lib.js" ;
@@ -124,8 +125,11 @@ export class Migrations<DataModel extends GenericDataModel> {
124125 | MigrationFunctionReference
125126 | MigrationFunctionReference [ ] ,
126127 ) {
127- return internalMutationGeneric ( {
128- args : migrationArgs ,
128+ return internalActionGeneric ( {
129+ args : {
130+ ...migrationArgs ,
131+ inline : v . optional ( v . boolean ( ) ) ,
132+ } ,
129133 handler : async ( ctx , args ) => {
130134 const [ specificMigration , next ] = Array . isArray (
131135 specificMigrationOrSeries ,
@@ -157,9 +161,9 @@ export class Migrations<DataModel extends GenericDataModel> {
157161
158162 private async _runInteractive (
159163 ctx : MutationCtx | ActionCtx ,
160- args : MigrationArgs ,
164+ args : MigrationArgs & { inline ?: boolean } ,
161165 fnRef ?: MigrationFunctionReference ,
162- next ?: { name : string ; fnHandle : string } [ ] ,
166+ next ?: { name : string ; fnHandle : MigrationFunctionHandle } [ ] ,
163167 ) {
164168 const name = args . fn ? this . prefixedName ( args . fn ) : getFunctionName ( fnRef ! ) ;
165169 async function makeFn ( fn : string ) {
@@ -190,14 +194,34 @@ export class Migrations<DataModel extends GenericDataModel> {
190194 }
191195 let status : MigrationStatus ;
192196 try {
193- status = await ctx . runMutation ( this . component . lib . migrate , {
194- name,
195- fnHandle,
196- cursor : args . cursor ,
197- batchSize : args . batchSize ,
198- next,
199- dryRun : args . dryRun ?? false ,
200- } ) ;
197+ if ( args . inline ) {
198+ if ( ! ( "runAction" in ctx ) ) {
199+ throw new Error ( "Cannot run inline migration from a mutation" ) ;
200+ }
201+ return await _runToCompletionInline ( ctx , this . component , [
202+ {
203+ fnHandle,
204+ name,
205+ batchSize : args . batchSize ,
206+ cursor : args . cursor ,
207+ dryRun : args . dryRun ,
208+ } ,
209+ ...( next ?? [ ] ) . map ( ( { name, fnHandle } ) => ( {
210+ fnHandle,
211+ name,
212+ dryRun : args . dryRun ,
213+ } ) ) ,
214+ ] ) ;
215+ } else {
216+ status = await ctx . runMutation ( this . component . lib . migrate , {
217+ name,
218+ fnHandle,
219+ cursor : args . cursor ,
220+ batchSize : args . batchSize ,
221+ next,
222+ dryRun : args . dryRun ?? false ,
223+ } ) ;
224+ }
201225 } catch ( e ) {
202226 if (
203227 args . dryRun &&
@@ -640,28 +664,7 @@ export async function runToCompletion(
640664 ctx : ActionCtx ,
641665 component : ComponentApi ,
642666 fnRef : MigrationFunctionReference | MigrationFunctionHandle ,
643- opts ?: {
644- /**
645- * The name of the migration function, generated with getFunctionName.
646- */
647- name ?: string ;
648- /**
649- * The cursor to start from.
650- * null: start from the beginning.
651- * undefined: start, or resume from where it failed. No-ops if already done.
652- */
653- cursor ?: string | null ;
654- /**
655- * The number of documents to process in a batch.
656- * Overrides the migrations's configured batch size.
657- */
658- batchSize ?: number ;
659- /**
660- * If true, it will run a batch and then throw an error.
661- * It's helpful to see what it would do without committing the transaction.
662- */
663- dryRun ?: boolean ;
664- } ,
667+ opts ?: RunToCompletionOptions ,
665668) : Promise < MigrationStatus > {
666669 let cursor = opts ?. cursor ;
667670 const {
@@ -697,6 +700,71 @@ export async function runToCompletion(
697700 }
698701}
699702
703+ type RunToCompletionOptions = {
704+ /**
705+ * The name of the migration function, generated with getFunctionName.
706+ */
707+ name ?: string ;
708+ /**
709+ * The cursor to start from.
710+ * null: start from the beginning.
711+ * undefined: start, or resume from where it failed. No-ops if already done.
712+ */
713+ cursor ?: string | null ;
714+ /**
715+ * The number of documents to process in a batch.
716+ * Overrides the migrations's configured batch size.
717+ */
718+ batchSize ?: number ;
719+ /**
720+ * If true, it will run a batch and then throw an error.
721+ * It's helpful to see what it would do without committing the transaction.
722+ */
723+ dryRun ?: boolean ;
724+ } ;
725+
726+ async function _runToCompletionInline (
727+ ctx : ActionCtx ,
728+ component : ComponentApi ,
729+ migrations : ( RunToCompletionOptions & {
730+ name : string ;
731+ fnHandle : MigrationFunctionHandle ;
732+ } ) [ ] ,
733+ ) {
734+ console . warn (
735+ `Running migration${
736+ migrations . length > 1
737+ ? "s " + migrations . map ( ( m ) => m . name ) . join ( ", " )
738+ : " " + migrations [ 0 ] . name
739+ } inline. ` +
740+ "Note: If this action times out, transiently fails, or is canceled, the migration will not continue." ,
741+ ) ;
742+ const totalStatus : Record < string , Record < string , unknown > > = { } ;
743+ for ( const { fnHandle, ...args } of migrations ) {
744+ const { name } = args ;
745+ if ( migrations . length > 1 ) {
746+ console . log ( "Starting " , name ) ;
747+ }
748+ const status = await runToCompletion ( ctx , component , fnHandle , args ) ;
749+ const {
750+ toCancel : _1 ,
751+ toMonitorStatus : _2 ,
752+ toStartOver : _3 ,
753+ ...log
754+ } = logStatusAndInstructions ( name , status , args ) ;
755+ totalStatus [ name ] = log ;
756+ if ( migrations . length > 1 ) {
757+ console . log (
758+ totalStatus [ name ] +
759+ ": " +
760+ totalStatus [ name ] . Status +
761+ `(${ status . processed } documents processed)` ,
762+ ) ;
763+ }
764+ }
765+ return totalStatus ;
766+ }
767+
700768/* Type utils follow */
701769
702770type QueryCtx = Pick < GenericQueryCtx < GenericDataModel > , "runQuery" > ;
0 commit comments