MCPcopy Create free account
hub / github.com/TestSprite/testsprite-cli / runCreate

Function runCreate

src/commands/project.ts:131–214  ·  view source on GitHub ↗
(
  opts: CreateOptions,
  deps: ProjectDeps = {},
)

Source from the content-addressed store, hash-verified

129}
130
131export async function runCreate(
132 opts: CreateOptions,
133 deps: ProjectDeps = {},
134): Promise<CliCreateProjectResponse> {
135 const out = makeOutput(opts.output, deps);
136 const stderr = deps.stderr ?? ((line: string) => process.stderr.write(`${line}\n`));
137
138 // P1-2: validate idempotency key before sending as an HTTP header.
139 // Non-ASCII chars cause a ByteString TypeError at the transport layer
140 // (exit 10 UNAVAILABLE) — fail fast with a clear exit 5 instead.
141 assertIdempotencyKey(opts.idempotencyKey);
142
143 // P1-3: client-side length checks matching server limits.
144 if (opts.name !== undefined && opts.name.length > 200) {
145 throw localValidationError('--name must be at most 200 characters');
146 }
147 if (opts.description !== undefined && opts.description.length > 2000) {
148 throw localValidationError('--description must be at most 2000 characters');
149 }
150
151 // P2-7: guard --url against localhost/RFC1918/non-http(s) (same rules as
152 // `test create --target-url`). Applies to both FE (required) and BE (optional).
153 if (opts.targetUrl !== undefined) {
154 assertNotLocal(opts.targetUrl);
155 }
156
157 if (opts.type === 'frontend' && !opts.targetUrl) {
158 throw localValidationError('--url is required for --type frontend');
159 }
160
161 if (opts.dryRun) {
162 const idempotencyKey = opts.idempotencyKey ?? `cli-proj-create-${randomUUID()}`;
163 // P2-6: gate idempotency-key output behind --verbose/--debug/json (matches
164 // test create convention). Suppress in plain text interactive mode to reduce
165 // noise; still available for automation and retry flows.
166 if (
167 opts.idempotencyKey === undefined &&
168 (opts.output === 'json' || opts.verbose || opts.debug)
169 ) {
170 stderr(`idempotency-key: ${idempotencyKey}`);
171 }
172 const sample: CliCreateProjectResponse = {
173 id: 'p_dryrun_2026',
174 type: opts.type,
175 name: opts.name,
176 targetUrl: opts.targetUrl ?? '',
177 createdFrom: 'cli',
178 createdAt: '2026-05-16T00:00:00.000Z',
179 updatedAt: '2026-05-16T00:00:00.000Z',
180 } as unknown as CliCreateProjectResponse;
181 out.print(sample, data => renderProjectText(data as CliProject));
182 return sample;
183 }
184
185 // Resolve password: flag > file > none
186 let password = opts.password;
187 if (password === undefined && opts.passwordFile !== undefined) {
188 password = readFileSync(opts.passwordFile, 'utf8').trim();

Callers 1

createProjectCommandFunction · 0.70

Calls 9

assertIdempotencyKeyFunction · 0.85
assertNotLocalFunction · 0.85
stderrFunction · 0.85
renderProjectTextFunction · 0.85
readFileSyncFunction · 0.85
printMethod · 0.80
makeOutputFunction · 0.70
localValidationErrorFunction · 0.70
makeClientFunction · 0.70

Tested by

no test coverage detected