@syncular/cli 0.0.0-44
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/package.json +68 -0
- package/src/args.ts +112 -0
- package/src/auth-storage.ts +57 -0
- package/src/buildpacks/index.ts +2 -0
- package/src/buildpacks/registry.ts +47 -0
- package/src/buildpacks/types.ts +1 -0
- package/src/command-registry.ts +700 -0
- package/src/commands/auth.ts +514 -0
- package/src/commands/build.ts +251 -0
- package/src/commands/console.ts +288 -0
- package/src/commands/demo.ts +1154 -0
- package/src/commands/doctor.tsx +133 -0
- package/src/commands/migrate.ts +312 -0
- package/src/commands/project.ts +437 -0
- package/src/commands/target.ts +62 -0
- package/src/commands/typegen.ts +406 -0
- package/src/constants.ts +4 -0
- package/src/control-plane.ts +23 -0
- package/src/dev-logging.tsx +415 -0
- package/src/extensions/index.ts +1 -0
- package/src/extensions/manifest.ts +37 -0
- package/src/help.tsx +236 -0
- package/src/index.ts +4 -0
- package/src/interactive.tsx +306 -0
- package/src/main.tsx +257 -0
- package/src/output.tsx +47 -0
- package/src/paths.ts +11 -0
- package/src/spaces-config.ts +2 -0
- package/src/targets/index.ts +13 -0
- package/src/targets/state.ts +99 -0
- package/src/targets/types.ts +8 -0
- package/src/templates/index.ts +2 -0
- package/src/templates/registry.ts +42 -0
- package/src/templates/syncular-types.ts +10 -0
- package/src/types.ts +67 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
import { basename, resolve } from 'node:path';
|
|
2
|
+
import process from 'node:process';
|
|
3
|
+
import {
|
|
4
|
+
runSpaceDev,
|
|
5
|
+
type SpaceDatabaseProvider,
|
|
6
|
+
type SpaceDevLogger,
|
|
7
|
+
} from '@syncular/cli-runtime-dev';
|
|
8
|
+
import { CLI_NAME } from '../constants';
|
|
9
|
+
import {
|
|
10
|
+
configureSpacesDevTelemetry,
|
|
11
|
+
createSpacesDevLogger,
|
|
12
|
+
} from '../dev-logging';
|
|
13
|
+
import { printError, printInfo } from '../output';
|
|
14
|
+
import {
|
|
15
|
+
DEFAULT_SYNCULAR_LIBRARIES_TARGETS,
|
|
16
|
+
listTemplateIds,
|
|
17
|
+
SYNCULAR_LIBRARIES_TARGETS,
|
|
18
|
+
type SyncularClientDialect,
|
|
19
|
+
type SyncularElectronDialect,
|
|
20
|
+
type SyncularLibrariesTarget,
|
|
21
|
+
type SyncularServerDialect,
|
|
22
|
+
templateRegistry,
|
|
23
|
+
} from '../templates';
|
|
24
|
+
|
|
25
|
+
const DEFAULT_INIT_DIR = 'spaces-app';
|
|
26
|
+
const DEFAULT_DEMO_INIT_DIR = 'spaces-demo';
|
|
27
|
+
const DEFAULT_SYNCULAR_INIT_DIR = '.';
|
|
28
|
+
const DEFAULT_DEV_PORT = 4312;
|
|
29
|
+
const DEFAULT_DEV_HOST = '127.0.0.1';
|
|
30
|
+
const DEFAULT_DEV_CONSOLE_TOKEN = 'spaces-dev-console-token';
|
|
31
|
+
const SUPPORTED_CREATE_TEMPLATES = listTemplateIds();
|
|
32
|
+
|
|
33
|
+
type CreateTemplateId = string;
|
|
34
|
+
|
|
35
|
+
function optionalFlag(
|
|
36
|
+
flagValues: Map<string, string>,
|
|
37
|
+
flag: string,
|
|
38
|
+
fallback: string
|
|
39
|
+
): string {
|
|
40
|
+
const value = flagValues.get(flag)?.trim();
|
|
41
|
+
return value && value.length > 0 ? value : fallback;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function optionalBooleanFlag(
|
|
45
|
+
flagValues: Map<string, string>,
|
|
46
|
+
flag: string,
|
|
47
|
+
fallback: boolean
|
|
48
|
+
): boolean {
|
|
49
|
+
const value = flagValues.get(flag);
|
|
50
|
+
if (!value) {
|
|
51
|
+
return fallback;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const normalized = value.trim().toLowerCase();
|
|
55
|
+
if (
|
|
56
|
+
normalized === '1' ||
|
|
57
|
+
normalized === 'true' ||
|
|
58
|
+
normalized === 'yes' ||
|
|
59
|
+
normalized === 'on'
|
|
60
|
+
) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (
|
|
65
|
+
normalized === '0' ||
|
|
66
|
+
normalized === 'false' ||
|
|
67
|
+
normalized === 'no' ||
|
|
68
|
+
normalized === 'off'
|
|
69
|
+
) {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
throw new Error(
|
|
74
|
+
`Invalid ${flag} value "${value}". Use true/false, yes/no, on/off, or 1/0.`
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function optionalIntegerFlag(
|
|
79
|
+
flagValues: Map<string, string>,
|
|
80
|
+
flag: string,
|
|
81
|
+
fallback: number
|
|
82
|
+
): number {
|
|
83
|
+
const raw = flagValues.get(flag)?.trim();
|
|
84
|
+
if (!raw) {
|
|
85
|
+
return fallback;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const parsed = Number.parseInt(raw, 10);
|
|
89
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
90
|
+
throw new Error(`Invalid ${flag} value: ${raw}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return parsed;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function parseCreateTemplateId(
|
|
97
|
+
flagValues: Map<string, string>
|
|
98
|
+
): CreateTemplateId {
|
|
99
|
+
const rawValue = flagValues.get('--template')?.trim() || '';
|
|
100
|
+
if (rawValue.length === 0) {
|
|
101
|
+
return templateRegistry.syncularLibraries.id;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
for (const templateId of SUPPORTED_CREATE_TEMPLATES) {
|
|
105
|
+
if (rawValue === templateId) {
|
|
106
|
+
return templateId;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
throw new Error(
|
|
111
|
+
`Unsupported --template "${rawValue}". Supported templates: ${SUPPORTED_CREATE_TEMPLATES.join(', ')}.`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function parseSyncularTargets(
|
|
116
|
+
rawTargets: string | undefined
|
|
117
|
+
): SyncularLibrariesTarget[] | { error: string } {
|
|
118
|
+
if (!rawTargets) {
|
|
119
|
+
return [...DEFAULT_SYNCULAR_LIBRARIES_TARGETS];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const parsed = rawTargets
|
|
123
|
+
.split(',')
|
|
124
|
+
.map((target) => target.trim())
|
|
125
|
+
.filter((target) => target.length > 0);
|
|
126
|
+
|
|
127
|
+
if (parsed.length === 0) {
|
|
128
|
+
return { error: 'At least one target is required for syncular-libraries.' };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const allowed = new Set<string>(SYNCULAR_LIBRARIES_TARGETS);
|
|
132
|
+
const invalid = parsed.find((target) => !allowed.has(target));
|
|
133
|
+
if (invalid) {
|
|
134
|
+
return {
|
|
135
|
+
error:
|
|
136
|
+
`Invalid --targets value "${invalid}". ` +
|
|
137
|
+
`Use: ${SYNCULAR_LIBRARIES_TARGETS.join(', ')}`,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return Array.from(new Set(parsed)) as SyncularLibrariesTarget[];
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function parseSyncularServerDialect(
|
|
145
|
+
value: string | undefined
|
|
146
|
+
): SyncularServerDialect | { error: string } {
|
|
147
|
+
if (!value || value === 'sqlite') {
|
|
148
|
+
return 'sqlite';
|
|
149
|
+
}
|
|
150
|
+
if (value === 'postgres') {
|
|
151
|
+
return 'postgres';
|
|
152
|
+
}
|
|
153
|
+
return { error: 'Invalid --server-dialect. Use sqlite or postgres.' };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function parseSyncularClientDialect(
|
|
157
|
+
value: string | undefined,
|
|
158
|
+
flagName: '--react-dialect' | '--vanilla-dialect'
|
|
159
|
+
): SyncularClientDialect | { error: string } {
|
|
160
|
+
if (!value || value === 'wa-sqlite') {
|
|
161
|
+
return 'wa-sqlite';
|
|
162
|
+
}
|
|
163
|
+
if (
|
|
164
|
+
value === 'pglite' ||
|
|
165
|
+
value === 'bun-sqlite' ||
|
|
166
|
+
value === 'better-sqlite3' ||
|
|
167
|
+
value === 'sqlite3'
|
|
168
|
+
) {
|
|
169
|
+
return value;
|
|
170
|
+
}
|
|
171
|
+
return {
|
|
172
|
+
error:
|
|
173
|
+
`Invalid ${flagName}. Use wa-sqlite, pglite, bun-sqlite, ` +
|
|
174
|
+
'better-sqlite3, or sqlite3.',
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function parseSyncularElectronDialect(
|
|
179
|
+
value: string | undefined
|
|
180
|
+
): SyncularElectronDialect | { error: string } {
|
|
181
|
+
if (!value || value === 'electron-sqlite') {
|
|
182
|
+
return 'electron-sqlite';
|
|
183
|
+
}
|
|
184
|
+
if (value === 'better-sqlite3') {
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
error: 'Invalid --electron-dialect. Use electron-sqlite or better-sqlite3.',
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function runCreateSpacesApp(
|
|
193
|
+
flagValues: Map<string, string>,
|
|
194
|
+
overwrite: boolean
|
|
195
|
+
): Promise<void> {
|
|
196
|
+
const targetDir = resolve(
|
|
197
|
+
process.cwd(),
|
|
198
|
+
optionalFlag(flagValues, '--dir', DEFAULT_INIT_DIR)
|
|
199
|
+
);
|
|
200
|
+
const explicitName = flagValues.get('--name')?.trim();
|
|
201
|
+
const inferredName = basename(targetDir) || 'spaces';
|
|
202
|
+
const projectName =
|
|
203
|
+
explicitName && explicitName.length > 0 ? explicitName : inferredName;
|
|
204
|
+
|
|
205
|
+
await templateRegistry.spacesApp.scaffold({
|
|
206
|
+
targetDir,
|
|
207
|
+
overwrite,
|
|
208
|
+
projectName,
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
printInfo('Scaffold created.');
|
|
212
|
+
console.log(`Template: ${templateRegistry.spacesApp.id}`);
|
|
213
|
+
console.log(`Project: ${projectName}`);
|
|
214
|
+
console.log(`Path: ${targetDir}`);
|
|
215
|
+
console.log('Next:');
|
|
216
|
+
console.log(`1. cd ${targetDir}`);
|
|
217
|
+
console.log('2. bun install');
|
|
218
|
+
console.log(`3. ${CLI_NAME} dev --port 4312`);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
async function runCreateSpacesDemo(
|
|
222
|
+
flagValues: Map<string, string>,
|
|
223
|
+
overwrite: boolean
|
|
224
|
+
): Promise<void> {
|
|
225
|
+
const targetDir = resolve(
|
|
226
|
+
process.cwd(),
|
|
227
|
+
optionalFlag(flagValues, '--dir', DEFAULT_DEMO_INIT_DIR)
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
await templateRegistry.spacesDemo.scaffold({
|
|
231
|
+
targetDir,
|
|
232
|
+
overwrite,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
printInfo('Demo scaffold created.');
|
|
236
|
+
console.log(`Template: ${templateRegistry.spacesDemo.id}`);
|
|
237
|
+
console.log(`Path: ${targetDir}`);
|
|
238
|
+
console.log('Next:');
|
|
239
|
+
console.log(`1. cd ${targetDir}`);
|
|
240
|
+
console.log('2. bun install');
|
|
241
|
+
console.log(`3. bun ${CLI_NAME} dev --port 4330`);
|
|
242
|
+
console.log('4. bun dev');
|
|
243
|
+
console.log(`5. bun ${CLI_NAME} deploy --space <spaceId>`);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function runCreateSyncularDemo(
|
|
247
|
+
flagValues: Map<string, string>,
|
|
248
|
+
overwrite: boolean
|
|
249
|
+
): Promise<void> {
|
|
250
|
+
const targetDir = resolve(
|
|
251
|
+
process.cwd(),
|
|
252
|
+
optionalFlag(flagValues, '--dir', DEFAULT_SYNCULAR_INIT_DIR)
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const result = await templateRegistry.syncularDemo.scaffold({
|
|
256
|
+
targetDir,
|
|
257
|
+
overwrite,
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
printInfo('Syncular demo scaffold created.');
|
|
261
|
+
console.log(`Template: ${templateRegistry.syncularDemo.id}`);
|
|
262
|
+
console.log(`Path: ${targetDir}`);
|
|
263
|
+
console.log('Files:');
|
|
264
|
+
for (const line of result.lines) {
|
|
265
|
+
console.log(`- ${line}`);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async function runCreateSyncularLibraries(
|
|
270
|
+
flagValues: Map<string, string>,
|
|
271
|
+
overwrite: boolean
|
|
272
|
+
): Promise<void> {
|
|
273
|
+
const rawTargets = flagValues.get('--targets')?.trim();
|
|
274
|
+
const targets = parseSyncularTargets(rawTargets || undefined);
|
|
275
|
+
if (!Array.isArray(targets)) {
|
|
276
|
+
throw new Error(targets.error);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const serverDialect = parseSyncularServerDialect(
|
|
280
|
+
flagValues.get('--server-dialect')?.trim() || undefined
|
|
281
|
+
);
|
|
282
|
+
if (typeof serverDialect !== 'string') {
|
|
283
|
+
throw new Error(serverDialect.error);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const reactDialect = parseSyncularClientDialect(
|
|
287
|
+
flagValues.get('--react-dialect')?.trim() || undefined,
|
|
288
|
+
'--react-dialect'
|
|
289
|
+
);
|
|
290
|
+
if (typeof reactDialect !== 'string') {
|
|
291
|
+
throw new Error(reactDialect.error);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const vanillaDialect = parseSyncularClientDialect(
|
|
295
|
+
flagValues.get('--vanilla-dialect')?.trim() || undefined,
|
|
296
|
+
'--vanilla-dialect'
|
|
297
|
+
);
|
|
298
|
+
if (typeof vanillaDialect !== 'string') {
|
|
299
|
+
throw new Error(vanillaDialect.error);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const electronDialect = parseSyncularElectronDialect(
|
|
303
|
+
flagValues.get('--electron-dialect')?.trim() || undefined
|
|
304
|
+
);
|
|
305
|
+
if (typeof electronDialect !== 'string') {
|
|
306
|
+
throw new Error(electronDialect.error);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const targetDir = resolve(
|
|
310
|
+
process.cwd(),
|
|
311
|
+
optionalFlag(flagValues, '--dir', DEFAULT_SYNCULAR_INIT_DIR)
|
|
312
|
+
);
|
|
313
|
+
const result = await templateRegistry.syncularLibraries.scaffold({
|
|
314
|
+
targetDir,
|
|
315
|
+
overwrite,
|
|
316
|
+
targets,
|
|
317
|
+
serverDialect,
|
|
318
|
+
reactDialect,
|
|
319
|
+
vanillaDialect,
|
|
320
|
+
electronDialect,
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
printInfo('Syncular libraries scaffold created.');
|
|
324
|
+
console.log(`Template: ${templateRegistry.syncularLibraries.id}`);
|
|
325
|
+
console.log(`Path: ${targetDir}`);
|
|
326
|
+
console.log('Summary:');
|
|
327
|
+
for (const line of result.lines) {
|
|
328
|
+
console.log(`- ${line}`);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export async function runCreate(
|
|
333
|
+
flagValues: Map<string, string>
|
|
334
|
+
): Promise<number> {
|
|
335
|
+
try {
|
|
336
|
+
const templateId = parseCreateTemplateId(flagValues);
|
|
337
|
+
const overwrite = optionalBooleanFlag(flagValues, '--force', false);
|
|
338
|
+
|
|
339
|
+
if (templateId === templateRegistry.spacesApp.id) {
|
|
340
|
+
await runCreateSpacesApp(flagValues, overwrite);
|
|
341
|
+
return 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (templateId === templateRegistry.spacesDemo.id) {
|
|
345
|
+
await runCreateSpacesDemo(flagValues, overwrite);
|
|
346
|
+
return 0;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (templateId === templateRegistry.syncularDemo.id) {
|
|
350
|
+
await runCreateSyncularDemo(flagValues, overwrite);
|
|
351
|
+
return 0;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
await runCreateSyncularLibraries(flagValues, overwrite);
|
|
355
|
+
return 0;
|
|
356
|
+
} catch (error: unknown) {
|
|
357
|
+
printError(
|
|
358
|
+
error instanceof Error ? error.message : 'Failed to scaffold project.'
|
|
359
|
+
);
|
|
360
|
+
return 1;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function parseDbProviderFlag(
|
|
365
|
+
value: string | null | undefined
|
|
366
|
+
): SpaceDatabaseProvider | null {
|
|
367
|
+
if (!value) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const normalized = value.trim().toLowerCase();
|
|
372
|
+
if (
|
|
373
|
+
normalized === 'sqlite' ||
|
|
374
|
+
normalized === 'neon' ||
|
|
375
|
+
normalized === 'postgres'
|
|
376
|
+
) {
|
|
377
|
+
return normalized;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
throw new Error(
|
|
381
|
+
`Invalid --db-provider value "${value}". Use sqlite, neon, or postgres.`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
export async function runDev(flagValues: Map<string, string>): Promise<number> {
|
|
386
|
+
let restoreTelemetry: (() => void) | null = null;
|
|
387
|
+
try {
|
|
388
|
+
if (flagValues.has('--module')) {
|
|
389
|
+
throw new Error(
|
|
390
|
+
'--module has been removed. Run syncular dev from a project directory that contains syncular.config.ts.'
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const cwd = process.cwd();
|
|
395
|
+
const host = optionalFlag(flagValues, '--host', DEFAULT_DEV_HOST);
|
|
396
|
+
const port = optionalIntegerFlag(flagValues, '--port', DEFAULT_DEV_PORT);
|
|
397
|
+
const watchEnabled = optionalBooleanFlag(flagValues, '--watch', true);
|
|
398
|
+
const openConsole = optionalBooleanFlag(flagValues, '--open', false);
|
|
399
|
+
const requestedDbProvider = parseDbProviderFlag(
|
|
400
|
+
flagValues.get('--db-provider') || null
|
|
401
|
+
);
|
|
402
|
+
const dbPath = flagValues.get('--db')?.trim() || null;
|
|
403
|
+
const connectionString =
|
|
404
|
+
flagValues.get('--connection-string')?.trim() || null;
|
|
405
|
+
const logger: SpaceDevLogger = createSpacesDevLogger({
|
|
406
|
+
openConsoleOnStarted: openConsole,
|
|
407
|
+
});
|
|
408
|
+
restoreTelemetry = configureSpacesDevTelemetry();
|
|
409
|
+
|
|
410
|
+
const exitCode = await runSpaceDev({
|
|
411
|
+
cwd,
|
|
412
|
+
host,
|
|
413
|
+
port,
|
|
414
|
+
watch: watchEnabled,
|
|
415
|
+
dbProvider: requestedDbProvider,
|
|
416
|
+
dbPath,
|
|
417
|
+
connectionString,
|
|
418
|
+
consoleToken:
|
|
419
|
+
flagValues.get('--console-token')?.trim() || DEFAULT_DEV_CONSOLE_TOKEN,
|
|
420
|
+
logger,
|
|
421
|
+
});
|
|
422
|
+
restoreTelemetry();
|
|
423
|
+
restoreTelemetry = null;
|
|
424
|
+
return exitCode;
|
|
425
|
+
} catch (error: unknown) {
|
|
426
|
+
if (restoreTelemetry) {
|
|
427
|
+
restoreTelemetry();
|
|
428
|
+
restoreTelemetry = null;
|
|
429
|
+
}
|
|
430
|
+
printError(
|
|
431
|
+
error instanceof Error
|
|
432
|
+
? error.message
|
|
433
|
+
: 'Failed to start local dev runtime.'
|
|
434
|
+
);
|
|
435
|
+
return 1;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import process from 'node:process';
|
|
2
|
+
import { CLI_NAME } from '../constants';
|
|
3
|
+
import { printError, printInfo } from '../output';
|
|
4
|
+
import {
|
|
5
|
+
isTargetId,
|
|
6
|
+
listTargetDefinitions,
|
|
7
|
+
readStoredTargetId,
|
|
8
|
+
resolveEffectiveTargetId,
|
|
9
|
+
resolveTargetStatePath,
|
|
10
|
+
writeStoredTargetId,
|
|
11
|
+
} from '../targets';
|
|
12
|
+
|
|
13
|
+
export async function runTarget(
|
|
14
|
+
flagValues: Map<string, string>
|
|
15
|
+
): Promise<number> {
|
|
16
|
+
try {
|
|
17
|
+
const cwd = process.cwd();
|
|
18
|
+
const setValue = flagValues.get('--set')?.trim() || null;
|
|
19
|
+
if (setValue) {
|
|
20
|
+
if (!isTargetId(setValue)) {
|
|
21
|
+
throw new Error(
|
|
22
|
+
`Unsupported --set target "${setValue}". Supported targets: ${listTargetDefinitions()
|
|
23
|
+
.map((target) => target.id)
|
|
24
|
+
.join(', ')}.`
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const statePath = await writeStoredTargetId({
|
|
29
|
+
cwd,
|
|
30
|
+
targetId: setValue,
|
|
31
|
+
});
|
|
32
|
+
printInfo('Default target updated.');
|
|
33
|
+
console.log(`Target: ${setValue}`);
|
|
34
|
+
console.log(`State file: ${statePath}`);
|
|
35
|
+
return 0;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const effectiveTarget = await resolveEffectiveTargetId({
|
|
39
|
+
cwd,
|
|
40
|
+
explicitTargetId: null,
|
|
41
|
+
});
|
|
42
|
+
const storedTargetId = await readStoredTargetId(cwd);
|
|
43
|
+
const definitions = listTargetDefinitions();
|
|
44
|
+
|
|
45
|
+
console.log(`Effective target: ${effectiveTarget.targetId}`);
|
|
46
|
+
console.log(`Source: ${effectiveTarget.source}`);
|
|
47
|
+
console.log(
|
|
48
|
+
`State file: ${resolveTargetStatePath(cwd)}${storedTargetId ? '' : ' (not set)'}`
|
|
49
|
+
);
|
|
50
|
+
console.log('Available targets:');
|
|
51
|
+
for (const definition of definitions) {
|
|
52
|
+
console.log(`- ${definition.id}: ${definition.description}`);
|
|
53
|
+
}
|
|
54
|
+
console.log(`Set default with: ${CLI_NAME} target --set <target-id>`);
|
|
55
|
+
return 0;
|
|
56
|
+
} catch (error: unknown) {
|
|
57
|
+
printError(
|
|
58
|
+
error instanceof Error ? error.message : 'Failed to resolve target.'
|
|
59
|
+
);
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
}
|