11import { Either , Match } from "effect"
2- import { type CreateCommand , type ParseError , deriveRepoPathParts , resolveRepoInput } from "./frontend-lib/core/domain.js"
2+ import {
3+ type CreateCommand ,
4+ deriveRepoPathParts ,
5+ type ParseError ,
6+ resolveRepoInput
7+ } from "./frontend-lib/core/domain.js"
38import { defaultProjectsRoot } from "./frontend-lib/usecases/menu-helpers.js"
49
510import { buildCreateCommand } from "./cli/parser-create.js"
@@ -14,13 +19,22 @@ export type CreateFlowContext = {
1419 readonly projectsRoot ?: string | undefined
1520}
1621
22+ type TokenQuote = "'" | "\""
23+
24+ type TokenizerState = {
25+ current : string
26+ escaping : boolean
27+ quote : TokenQuote | null
28+ readonly tokens : Array < string >
29+ }
30+
1731export type CreateFlowView = {
1832 readonly step : number
1933 readonly buffer : string
2034 readonly values : Partial < CreateInputs >
2135}
2236
23- type AdvanceCreateFlowResult =
37+ export type AdvanceCreateFlowResult =
2438 | { readonly _tag : "Continue" ; readonly view : CreateFlowView }
2539 | { readonly _tag : "Error" ; readonly error : ParseError }
2640 | { readonly _tag : "Complete" ; readonly inputs : CreateInputs }
@@ -29,6 +43,12 @@ type AdvanceCreateFlowOptions = {
2943 readonly quickCreate ?: boolean
3044}
3145
46+ type AdvanceCreateFlowHandlers = {
47+ readonly onComplete : ( inputs : CreateInputs ) => void
48+ readonly onContinue : ( view : CreateFlowView ) => void
49+ readonly onError : ( error : ParseError ) => void
50+ }
51+
3252const trimLeftSlash = ( value : string ) : string => {
3353 let start = 0
3454 while ( start < value . length && value [ start ] === "/" ) {
@@ -134,59 +154,64 @@ const createParseError = (reason: string): ParseError => ({
134154 reason
135155} )
136156
157+ const pushCreateToken = ( state : TokenizerState ) : void => {
158+ if ( state . current . length > 0 ) {
159+ state . tokens . push ( state . current )
160+ state . current = ""
161+ }
162+ }
163+
164+ const consumeQuotedCreateTokenChar = ( state : TokenizerState , char : string ) : void => {
165+ if ( char === state . quote ) {
166+ state . quote = null
167+ return
168+ }
169+ state . current += char
170+ }
171+
172+ const consumeCreateTokenChar = ( state : TokenizerState , char : string ) : void => {
173+ if ( state . escaping ) {
174+ state . current += char
175+ state . escaping = false
176+ return
177+ }
178+ if ( char === "\\" ) {
179+ state . escaping = true
180+ return
181+ }
182+ if ( state . quote !== null ) {
183+ consumeQuotedCreateTokenChar ( state , char )
184+ return
185+ }
186+ if ( char === "'" || char === "\"" ) {
187+ state . quote = char
188+ return
189+ }
190+ if ( / \s / u. test ( char ) ) {
191+ pushCreateToken ( state )
192+ return
193+ }
194+ state . current += char
195+ }
196+
137197const tokenizeCreateCommandLine = (
138198 input : string
139199) : Either . Either < ReadonlyArray < string > , ParseError > => {
140- const tokens : Array < string > = [ ]
141- let current = ""
142- let quote : "'" | "\"" | null = null
143- let escaping = false
144-
145- const pushCurrent = ( ) => {
146- if ( current . length > 0 ) {
147- tokens . push ( current )
148- current = ""
149- }
150- }
200+ const state : TokenizerState = { current : "" , escaping : false , quote : null , tokens : [ ] }
151201
152202 for ( const char of input . trim ( ) ) {
153- if ( escaping ) {
154- current += char
155- escaping = false
156- continue
157- }
158- if ( char === "\\" ) {
159- escaping = true
160- continue
161- }
162- if ( quote !== null ) {
163- if ( char === quote ) {
164- quote = null
165- } else {
166- current += char
167- }
168- continue
169- }
170- if ( char === "'" || char === "\"" ) {
171- quote = char
172- continue
173- }
174- if ( / \s / u. test ( char ) ) {
175- pushCurrent ( )
176- continue
177- }
178- current += char
203+ consumeCreateTokenChar ( state , char )
179204 }
180205
181- if ( escaping ) {
206+ if ( state . escaping ) {
182207 return Either . left ( createParseError ( "unterminated escape sequence" ) )
183208 }
184- if ( quote !== null ) {
209+ if ( state . quote !== null ) {
185210 return Either . left ( createParseError ( "unterminated quoted value" ) )
186211 }
187212
188- pushCurrent ( )
189- return Either . right ( tokens )
213+ pushCreateToken ( state )
214+ return Either . right ( state . tokens )
190215}
191216
192217const unsupportedCreatePrefixes = new Set ( [
@@ -242,14 +267,14 @@ const createInputsFromCommand = (
242267 repoUrl,
243268 repoRef : command . config . repoRef ,
244269 outDir : command . outDir ,
245- ...( raw . cpuLimit !== undefined ? { cpuLimit : command . config . cpuLimit ?? "" } : { } ) ,
246- ...( raw . ramLimit !== undefined ? { ramLimit : command . config . ramLimit ?? "" } : { } ) ,
247- ...( raw . up !== undefined ? { runUp : command . runUp } : { } ) ,
248- ...( raw . enableMcpPlaywright ! == undefined
249- ? { enableMcpPlaywright : command . config . enableMcpPlaywright }
250- : { } ) ,
251- ...( raw . force !== undefined ? { force : command . force } : { } ) ,
252- ...( raw . forceEnv !== undefined ? { forceEnv : command . forceEnv } : { } )
270+ ...( raw . cpuLimit === undefined ? { } : { cpuLimit : command . config . cpuLimit ?? "" } ) ,
271+ ...( raw . ramLimit === undefined ? { } : { ramLimit : command . config . ramLimit ?? "" } ) ,
272+ ...( raw . up === undefined ? { } : { runUp : command . runUp } ) ,
273+ ...( raw . enableMcpPlaywright = == undefined
274+ ? { }
275+ : { enableMcpPlaywright : command . config . enableMcpPlaywright } ) ,
276+ ...( raw . force === undefined ? { } : { force : command . force } ) ,
277+ ...( raw . forceEnv === undefined ? { } : { forceEnv : command . forceEnv } )
253278} )
254279
255280const parseRepoStepInput = (
@@ -281,7 +306,7 @@ const parseRepoStepInput = (
281306
282307const createStepApplied = ( ) : Either . Either < true , ParseError > => Either . right ( true )
283308
284- const hasOwn = < K extends keyof CreateInputs > ( values : Partial < CreateInputs > , key : K ) : boolean =>
309+ const hasOwn = ( values : Partial < CreateInputs > , key : keyof CreateInputs ) : boolean =>
285310 Object . prototype . hasOwnProperty . call ( values , key )
286311
287312const isCreateStepSatisfied = (
@@ -432,6 +457,24 @@ export const advanceCreateFlow = (
432457 }
433458}
434459
460+ export const handleAdvanceCreateFlowResult = (
461+ next : AdvanceCreateFlowResult | null ,
462+ handlers : AdvanceCreateFlowHandlers
463+ ) : void => {
464+ if ( next === null ) {
465+ return
466+ }
467+ if ( next . _tag === "Error" ) {
468+ handlers . onError ( next . error )
469+ return
470+ }
471+ if ( next . _tag === "Continue" ) {
472+ handlers . onContinue ( next . view )
473+ return
474+ }
475+ handlers . onComplete ( next . inputs )
476+ }
477+
435478export const createProjectDraftFromInputs = (
436479 input : CreateInputs
437480) : {
0 commit comments