tiendu 0.6.0 → 0.7.0
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/README.md +106 -42
- package/bin/tiendu.js +130 -55
- package/lib/build.mjs +240 -131
- package/lib/config.mjs +68 -12
- package/lib/dev.mjs +26 -30
- package/lib/init.mjs +116 -106
- package/lib/preview.mjs +63 -42
- package/lib/publish.mjs +14 -16
- package/lib/pull.mjs +9 -6
- package/lib/push.mjs +8 -10
- package/lib/stores.mjs +91 -0
- package/lib/ui.mjs +138 -0
- package/lib/update-check.mjs +8 -4
- package/package.json +1 -1
package/lib/preview.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as p from "@clack/prompts";
|
|
2
1
|
import { loadConfigOrFail, writeConfig } from "./config.mjs";
|
|
3
2
|
import { apiFetch, fetchPreview } from "./api.mjs";
|
|
3
|
+
import * as ui from "./ui.mjs";
|
|
4
4
|
|
|
5
5
|
export const buildPreviewUrl = (apiBaseUrl, previewHostname) => {
|
|
6
6
|
const base = new URL(apiBaseUrl);
|
|
@@ -215,24 +215,41 @@ export const resolvePreviewKeyInteractively = async ({ config, credentials }) =>
|
|
|
215
215
|
return config.previewKey;
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
|
|
218
|
+
ui.log.warn(`Stored preview ${config.previewKey} was not found. Please select a preview.`);
|
|
219
219
|
const { previewKey: _, ...rest } = config;
|
|
220
220
|
await writeConfig(rest);
|
|
221
221
|
} else {
|
|
222
|
-
|
|
222
|
+
ui.log.warn("No preview attached.");
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
// 2. List previews
|
|
226
226
|
const listResult = await listPreviews(apiBaseUrl, apiKey, storeId);
|
|
227
227
|
if (!listResult.ok) {
|
|
228
|
-
|
|
228
|
+
ui.log.error(listResult.error);
|
|
229
229
|
process.exit(1);
|
|
230
230
|
}
|
|
231
231
|
|
|
232
232
|
const previews = listResult.data;
|
|
233
|
+
const singlePreview = resolveActivePreview(previews);
|
|
234
|
+
|
|
235
|
+
if (singlePreview) {
|
|
236
|
+
await writeConfig({ ...config, previewKey: singlePreview.previewKey });
|
|
237
|
+
const displayName = getPreviewDisplayName(singlePreview);
|
|
238
|
+
ui.log.success(`Using preview "${displayName}" (${singlePreview.previewKey})`);
|
|
239
|
+
return singlePreview.previewKey;
|
|
240
|
+
}
|
|
233
241
|
|
|
234
242
|
if (previews.length === 0) {
|
|
235
|
-
|
|
243
|
+
ui.log.info("No previews found for this store.");
|
|
244
|
+
if (!ui.isInteractive()) {
|
|
245
|
+
ui.log.error("Create a preview first with tiendu preview create [name].");
|
|
246
|
+
process.exit(1);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (!ui.isInteractive()) {
|
|
251
|
+
ui.log.error("No preview selected. Provide a preview key or attach one with tiendu preview attach <key>.");
|
|
252
|
+
process.exit(1);
|
|
236
253
|
}
|
|
237
254
|
|
|
238
255
|
// 3. Show picker
|
|
@@ -248,37 +265,37 @@ export const resolvePreviewKeyInteractively = async ({ config, credentials }) =>
|
|
|
248
265
|
},
|
|
249
266
|
];
|
|
250
267
|
|
|
251
|
-
const selected = await
|
|
268
|
+
const selected = await ui.select({
|
|
252
269
|
message: "Select a preview",
|
|
253
270
|
options,
|
|
254
271
|
});
|
|
255
272
|
|
|
256
|
-
if (
|
|
257
|
-
|
|
273
|
+
if (ui.isCancel(selected)) {
|
|
274
|
+
ui.cancel("Cancelled.");
|
|
258
275
|
process.exit(0);
|
|
259
276
|
}
|
|
260
277
|
|
|
261
278
|
// 4. Handle create new
|
|
262
279
|
if (selected === CREATE_NEW_VALUE) {
|
|
263
|
-
const nameInput = await
|
|
280
|
+
const nameInput = await ui.text({
|
|
264
281
|
message: "Preview name (optional)",
|
|
265
282
|
placeholder: "Press Enter to skip",
|
|
266
283
|
defaultValue: "",
|
|
267
284
|
});
|
|
268
285
|
|
|
269
|
-
if (
|
|
270
|
-
|
|
286
|
+
if (ui.isCancel(nameInput)) {
|
|
287
|
+
ui.cancel("Cancelled.");
|
|
271
288
|
process.exit(0);
|
|
272
289
|
}
|
|
273
290
|
|
|
274
291
|
const name = (nameInput ?? "").trim();
|
|
275
|
-
const spinner =
|
|
292
|
+
const spinner = ui.spinner();
|
|
276
293
|
spinner.start("Creating preview...");
|
|
277
294
|
|
|
278
295
|
const createResult = await createPreview(apiBaseUrl, apiKey, storeId, name);
|
|
279
296
|
if (!createResult.ok) {
|
|
280
297
|
spinner.stop("Failed to create preview.", 1);
|
|
281
|
-
|
|
298
|
+
ui.log.error(createResult.error);
|
|
282
299
|
process.exit(1);
|
|
283
300
|
}
|
|
284
301
|
|
|
@@ -286,7 +303,7 @@ export const resolvePreviewKeyInteractively = async ({ config, credentials }) =>
|
|
|
286
303
|
const displayName = getPreviewDisplayName(preview);
|
|
287
304
|
const url = getPreviewUrl(apiBaseUrl, preview);
|
|
288
305
|
spinner.stop(`Preview "${displayName}" created (${preview.previewKey})`);
|
|
289
|
-
|
|
306
|
+
ui.log.message(` ${url}`);
|
|
290
307
|
|
|
291
308
|
await writeConfig({ ...config, previewKey: preview.previewKey });
|
|
292
309
|
return preview.previewKey;
|
|
@@ -296,7 +313,7 @@ export const resolvePreviewKeyInteractively = async ({ config, credentials }) =>
|
|
|
296
313
|
await writeConfig({ ...config, previewKey: selected });
|
|
297
314
|
const selectedPreview = previews.find((p) => p.previewKey === selected);
|
|
298
315
|
const displayName = selectedPreview ? getPreviewDisplayName(selectedPreview) : selected;
|
|
299
|
-
|
|
316
|
+
ui.log.success(`Attached to "${displayName}" (${selected})`);
|
|
300
317
|
|
|
301
318
|
return selected;
|
|
302
319
|
};
|
|
@@ -308,7 +325,7 @@ export const resolvePreviewKeyInteractively = async ({ config, credentials }) =>
|
|
|
308
325
|
export const previewCreate = async (name) => {
|
|
309
326
|
const { config, credentials } = await loadConfigOrFail();
|
|
310
327
|
|
|
311
|
-
const spinner =
|
|
328
|
+
const spinner = ui.spinner();
|
|
312
329
|
spinner.start("Creating preview...");
|
|
313
330
|
|
|
314
331
|
const result = await createPreview(
|
|
@@ -320,7 +337,7 @@ export const previewCreate = async (name) => {
|
|
|
320
337
|
|
|
321
338
|
if (!result.ok) {
|
|
322
339
|
spinner.stop("Failed to create preview.", 1);
|
|
323
|
-
|
|
340
|
+
ui.log.error(result.error);
|
|
324
341
|
process.exit(1);
|
|
325
342
|
}
|
|
326
343
|
|
|
@@ -328,7 +345,7 @@ export const previewCreate = async (name) => {
|
|
|
328
345
|
const url = getPreviewUrl(config.apiBaseUrl, preview);
|
|
329
346
|
const displayName = getPreviewDisplayName(preview);
|
|
330
347
|
spinner.stop(`Preview "${displayName}" created (${preview.previewKey})`);
|
|
331
|
-
|
|
348
|
+
ui.log.message(` ${url}`);
|
|
332
349
|
|
|
333
350
|
await writeConfig({ ...config, previewKey: preview.previewKey });
|
|
334
351
|
};
|
|
@@ -336,7 +353,7 @@ export const previewCreate = async (name) => {
|
|
|
336
353
|
export const previewList = async () => {
|
|
337
354
|
const { config, credentials } = await loadConfigOrFail();
|
|
338
355
|
|
|
339
|
-
const spinner =
|
|
356
|
+
const spinner = ui.spinner();
|
|
340
357
|
spinner.start("Fetching previews...");
|
|
341
358
|
|
|
342
359
|
const result = await listPreviews(
|
|
@@ -347,7 +364,7 @@ export const previewList = async () => {
|
|
|
347
364
|
|
|
348
365
|
if (!result.ok) {
|
|
349
366
|
spinner.stop("Failed to fetch previews.", 1);
|
|
350
|
-
|
|
367
|
+
ui.log.error(result.error);
|
|
351
368
|
process.exit(1);
|
|
352
369
|
}
|
|
353
370
|
|
|
@@ -365,10 +382,10 @@ export const previewList = async () => {
|
|
|
365
382
|
const indicator = isAttached ? " \u2190 attached" : "";
|
|
366
383
|
const url = buildPreviewUrl(config.apiBaseUrl, preview.previewHostname);
|
|
367
384
|
const displayName = getPreviewDisplayName(preview);
|
|
368
|
-
|
|
385
|
+
ui.log.message(` ${displayName} ${url}${indicator}`);
|
|
369
386
|
}
|
|
370
387
|
|
|
371
|
-
|
|
388
|
+
ui.log.info("Tip: run tiendu preview attach <key> to switch previews.");
|
|
372
389
|
};
|
|
373
390
|
|
|
374
391
|
const formatRelativeDate = (value) => {
|
|
@@ -392,7 +409,7 @@ export const previewShow = async () => {
|
|
|
392
409
|
const { config, credentials } = await loadConfigOrFail();
|
|
393
410
|
|
|
394
411
|
if (!config.previewKey) {
|
|
395
|
-
|
|
412
|
+
ui.log.warn("No preview attached. Run tiendu preview list or tiendu preview create.");
|
|
396
413
|
process.exit(0);
|
|
397
414
|
}
|
|
398
415
|
|
|
@@ -404,8 +421,8 @@ export const previewShow = async () => {
|
|
|
404
421
|
);
|
|
405
422
|
|
|
406
423
|
if (!result.ok) {
|
|
407
|
-
|
|
408
|
-
|
|
424
|
+
ui.log.warn(`Stored preview ${config.previewKey} was not found.`);
|
|
425
|
+
ui.log.info("Run tiendu preview list to see available previews.");
|
|
409
426
|
process.exit(1);
|
|
410
427
|
}
|
|
411
428
|
|
|
@@ -413,7 +430,7 @@ export const previewShow = async () => {
|
|
|
413
430
|
const url = getPreviewUrl(config.apiBaseUrl, preview);
|
|
414
431
|
const displayName = getPreviewDisplayName(preview);
|
|
415
432
|
|
|
416
|
-
|
|
433
|
+
ui.note(
|
|
417
434
|
[
|
|
418
435
|
`Name: ${displayName}`,
|
|
419
436
|
`Key: ${preview.previewKey}`,
|
|
@@ -428,11 +445,15 @@ export const previewAttach = async (keyArg) => {
|
|
|
428
445
|
const { config, credentials } = await loadConfigOrFail();
|
|
429
446
|
|
|
430
447
|
if (!keyArg) {
|
|
448
|
+
if (!ui.isInteractive()) {
|
|
449
|
+
ui.log.error("Preview key required in non-interactive mode. Use: tiendu preview attach <key>");
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
431
452
|
await resolvePreviewKeyInteractively({ config, credentials });
|
|
432
453
|
return;
|
|
433
454
|
}
|
|
434
455
|
|
|
435
|
-
const spinner =
|
|
456
|
+
const spinner = ui.spinner();
|
|
436
457
|
spinner.start("Validating preview...");
|
|
437
458
|
|
|
438
459
|
const result = await fetchPreview(
|
|
@@ -444,7 +465,7 @@ export const previewAttach = async (keyArg) => {
|
|
|
444
465
|
|
|
445
466
|
if (!result.ok) {
|
|
446
467
|
spinner.stop("Preview not found.", 1);
|
|
447
|
-
|
|
468
|
+
ui.log.error("Preview not found. Run tiendu preview list to see available previews.");
|
|
448
469
|
process.exit(1);
|
|
449
470
|
}
|
|
450
471
|
|
|
@@ -452,7 +473,7 @@ export const previewAttach = async (keyArg) => {
|
|
|
452
473
|
const url = getPreviewUrl(config.apiBaseUrl, preview);
|
|
453
474
|
const displayName = getPreviewDisplayName(preview);
|
|
454
475
|
spinner.stop(`Attached to preview "${displayName}" (${preview.previewKey})`);
|
|
455
|
-
|
|
476
|
+
ui.log.message(` ${url}`);
|
|
456
477
|
|
|
457
478
|
await writeConfig({ ...config, previewKey: preview.previewKey });
|
|
458
479
|
};
|
|
@@ -461,7 +482,7 @@ export const previewDetach = async () => {
|
|
|
461
482
|
const { config } = await loadConfigOrFail();
|
|
462
483
|
|
|
463
484
|
if (!config.previewKey) {
|
|
464
|
-
|
|
485
|
+
ui.log.warn("No preview is currently attached.");
|
|
465
486
|
process.exit(0);
|
|
466
487
|
}
|
|
467
488
|
|
|
@@ -469,7 +490,7 @@ export const previewDetach = async () => {
|
|
|
469
490
|
const { previewKey: _, ...rest } = config;
|
|
470
491
|
await writeConfig(rest);
|
|
471
492
|
|
|
472
|
-
|
|
493
|
+
ui.log.success(`Detached from preview ${detachedKey}. No active preview.`);
|
|
473
494
|
};
|
|
474
495
|
|
|
475
496
|
export const previewDelete = async (keyArg) => {
|
|
@@ -479,8 +500,8 @@ export const previewDelete = async (keyArg) => {
|
|
|
479
500
|
|
|
480
501
|
if (!previewKey) {
|
|
481
502
|
if (!config.previewKey) {
|
|
482
|
-
|
|
483
|
-
|
|
503
|
+
ui.log.warn("No preview attached and no key provided.");
|
|
504
|
+
ui.log.info("Run tiendu preview delete <key> or tiendu preview attach first.");
|
|
484
505
|
process.exit(1);
|
|
485
506
|
}
|
|
486
507
|
previewKey = config.previewKey;
|
|
@@ -495,23 +516,23 @@ export const previewDelete = async (keyArg) => {
|
|
|
495
516
|
);
|
|
496
517
|
|
|
497
518
|
if (!fetchResult.ok) {
|
|
498
|
-
|
|
519
|
+
ui.log.error(`Preview ${previewKey} not found.`);
|
|
499
520
|
process.exit(1);
|
|
500
521
|
}
|
|
501
522
|
|
|
502
523
|
const displayName = getPreviewDisplayName(fetchResult.data);
|
|
503
524
|
const url = getPreviewUrl(config.apiBaseUrl, fetchResult.data);
|
|
504
525
|
|
|
505
|
-
const confirmed = await
|
|
526
|
+
const confirmed = await ui.confirm({
|
|
506
527
|
message: `Delete preview ${previewKey} "${displayName}" (${url})?`,
|
|
507
528
|
});
|
|
508
529
|
|
|
509
|
-
if (
|
|
510
|
-
|
|
530
|
+
if (ui.isCancel(confirmed) || !confirmed) {
|
|
531
|
+
ui.cancel("Cancelled.");
|
|
511
532
|
process.exit(0);
|
|
512
533
|
}
|
|
513
534
|
|
|
514
|
-
const spinner =
|
|
535
|
+
const spinner = ui.spinner();
|
|
515
536
|
spinner.start("Deleting preview...");
|
|
516
537
|
|
|
517
538
|
const result = await deletePreview(
|
|
@@ -523,7 +544,7 @@ export const previewDelete = async (keyArg) => {
|
|
|
523
544
|
|
|
524
545
|
if (!result.ok) {
|
|
525
546
|
spinner.stop("Failed to delete preview.", 1);
|
|
526
|
-
|
|
547
|
+
ui.log.error(result.error);
|
|
527
548
|
process.exit(1);
|
|
528
549
|
}
|
|
529
550
|
|
|
@@ -539,11 +560,11 @@ export const previewOpen = async () => {
|
|
|
539
560
|
const { config, credentials } = await loadConfigOrFail();
|
|
540
561
|
|
|
541
562
|
if (!config.previewKey) {
|
|
542
|
-
|
|
563
|
+
ui.log.warn("No preview attached. Run tiendu preview attach or tiendu preview create.");
|
|
543
564
|
process.exit(1);
|
|
544
565
|
}
|
|
545
566
|
|
|
546
|
-
const spinner =
|
|
567
|
+
const spinner = ui.spinner();
|
|
547
568
|
spinner.start("Fetching preview URL...");
|
|
548
569
|
|
|
549
570
|
const result = await fetchPreview(
|
|
@@ -555,7 +576,7 @@ export const previewOpen = async () => {
|
|
|
555
576
|
|
|
556
577
|
if (!result.ok) {
|
|
557
578
|
spinner.stop("Preview not found.", 1);
|
|
558
|
-
|
|
579
|
+
ui.log.error("Stored preview was not found. Run tiendu preview list.");
|
|
559
580
|
process.exit(1);
|
|
560
581
|
}
|
|
561
582
|
|
package/lib/publish.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { loadConfigOrFail, isBuiltTheme } from "./config.mjs";
|
|
1
|
+
import { loadConfigOrFail } from "./config.mjs";
|
|
3
2
|
import {
|
|
4
3
|
fetchPreviewDetails,
|
|
5
4
|
publishPreview,
|
|
6
5
|
resolvePreviewKeyInteractively,
|
|
7
6
|
} from "./preview.mjs";
|
|
8
7
|
import { push } from "./push.mjs";
|
|
8
|
+
import * as ui from "./ui.mjs";
|
|
9
9
|
|
|
10
10
|
export const publish = async ({ skipBuild = false, previewKey: previewKeyArg } = {}) => {
|
|
11
11
|
const { config, credentials } = await loadConfigOrFail();
|
|
@@ -26,27 +26,25 @@ export const publish = async ({ skipBuild = false, previewKey: previewKeyArg } =
|
|
|
26
26
|
: previewKey;
|
|
27
27
|
const previewUrl = fetchResult.ok ? fetchResult.data.url : null;
|
|
28
28
|
|
|
29
|
-
const confirmed = await
|
|
29
|
+
const confirmed = await ui.confirm({
|
|
30
30
|
message: previewUrl
|
|
31
31
|
? `Publish preview "${displayName}" (${previewKey}) at ${previewUrl} to the live storefront?`
|
|
32
32
|
: `Publish preview "${displayName}" (${previewKey}) to the live storefront?`,
|
|
33
33
|
});
|
|
34
34
|
|
|
35
|
-
if (
|
|
36
|
-
|
|
35
|
+
if (ui.isCancel(confirmed) || !confirmed) {
|
|
36
|
+
ui.cancel("Publish cancelled.");
|
|
37
37
|
process.exit(0);
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
await push({ skipBuild, previewKey });
|
|
47
|
-
}
|
|
40
|
+
ui.log.info(
|
|
41
|
+
skipBuild
|
|
42
|
+
? "Syncing existing dist/ output to the preview before publishing..."
|
|
43
|
+
: "Building and syncing the latest dist/ output before publishing...",
|
|
44
|
+
);
|
|
45
|
+
await push({ skipBuild, previewKey });
|
|
48
46
|
|
|
49
|
-
const spinner =
|
|
47
|
+
const spinner = ui.spinner();
|
|
50
48
|
spinner.start("Publishing preview to live storefront...");
|
|
51
49
|
|
|
52
50
|
const result = await publishPreview(
|
|
@@ -58,12 +56,12 @@ export const publish = async ({ skipBuild = false, previewKey: previewKeyArg } =
|
|
|
58
56
|
|
|
59
57
|
if (!result.ok) {
|
|
60
58
|
spinner.stop("Publish failed.", 1);
|
|
61
|
-
|
|
59
|
+
ui.log.error(result.error);
|
|
62
60
|
process.exit(1);
|
|
63
61
|
}
|
|
64
62
|
|
|
65
63
|
spinner.stop(`Preview ${previewKey} published. Your live storefront has been updated.`);
|
|
66
64
|
if (previewUrl) {
|
|
67
|
-
|
|
65
|
+
ui.log.message(` ${previewUrl}`);
|
|
68
66
|
}
|
|
69
67
|
};
|
package/lib/pull.mjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { mkdir, rm } from "node:fs/promises";
|
|
2
|
+
import { getDistDir, loadConfigOrFail } from "./config.mjs";
|
|
3
3
|
import { downloadStorefrontArchive, downloadPreviewArchive } from "./api.mjs";
|
|
4
4
|
import { fetchPreviewDetails } from "./preview.mjs";
|
|
5
5
|
import { extractZip } from "./zip.mjs";
|
|
6
|
+
import * as ui from "./ui.mjs";
|
|
6
7
|
|
|
7
8
|
/** @param {number} bytes */
|
|
8
9
|
const formatBytes = (bytes) => {
|
|
@@ -22,7 +23,7 @@ export const pull = async ({ previewKey } = {}) => {
|
|
|
22
23
|
)
|
|
23
24
|
: null;
|
|
24
25
|
|
|
25
|
-
const spinner =
|
|
26
|
+
const spinner = ui.spinner();
|
|
26
27
|
const isPreviewPull = Boolean(previewKey);
|
|
27
28
|
|
|
28
29
|
spinner.start(
|
|
@@ -46,13 +47,15 @@ export const pull = async ({ previewKey } = {}) => {
|
|
|
46
47
|
|
|
47
48
|
if (!result.ok) {
|
|
48
49
|
spinner.stop("Download failed.", 1);
|
|
49
|
-
|
|
50
|
+
ui.log.error(result.error);
|
|
50
51
|
process.exit(1);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
spinner.message(`Extracting archive (${formatBytes(result.data.length)})...`);
|
|
54
55
|
|
|
55
|
-
const outputDir =
|
|
56
|
+
const outputDir = getDistDir();
|
|
57
|
+
await rm(outputDir, { recursive: true, force: true });
|
|
58
|
+
await mkdir(outputDir, { recursive: true });
|
|
56
59
|
const extractedFiles = await extractZip(result.data, outputDir);
|
|
57
60
|
|
|
58
61
|
const suffix = isPreviewPull ? ` from preview ${previewKey}` : "";
|
|
@@ -61,6 +64,6 @@ export const pull = async ({ previewKey } = {}) => {
|
|
|
61
64
|
);
|
|
62
65
|
|
|
63
66
|
if (previewDetails?.ok) {
|
|
64
|
-
|
|
67
|
+
ui.log.message(` ${previewDetails.data.url}`);
|
|
65
68
|
}
|
|
66
69
|
};
|
package/lib/push.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { loadConfigOrFail, isBuiltTheme, getDistDir } from "./config.mjs";
|
|
1
|
+
import { getDistDir, loadConfigOrFail } from "./config.mjs";
|
|
3
2
|
import { uploadPreviewZip } from "./api.mjs";
|
|
4
3
|
import { createZipFromDirectory } from "./archive.mjs";
|
|
5
4
|
import { build } from "./build.mjs";
|
|
@@ -8,6 +7,7 @@ import {
|
|
|
8
7
|
resolvePreviewKeyInteractively,
|
|
9
8
|
} from "./preview.mjs";
|
|
10
9
|
import { retryAsync } from "./retry.mjs";
|
|
10
|
+
import * as ui from "./ui.mjs";
|
|
11
11
|
|
|
12
12
|
/** @param {number} bytes */
|
|
13
13
|
const formatBytes = (bytes) => {
|
|
@@ -52,9 +52,7 @@ export const pushPreparedDirectoryToPreview = async ({
|
|
|
52
52
|
export const push = async ({ skipBuild = false, previewKey: previewKeyArg } = {}) => {
|
|
53
53
|
const { config, credentials } = await loadConfigOrFail();
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
if (builtTheme && !skipBuild) {
|
|
55
|
+
if (!skipBuild) {
|
|
58
56
|
const result = await build();
|
|
59
57
|
if (!result.ok) {
|
|
60
58
|
process.exit(1);
|
|
@@ -71,12 +69,12 @@ export const push = async ({ skipBuild = false, previewKey: previewKeyArg } = {}
|
|
|
71
69
|
);
|
|
72
70
|
|
|
73
71
|
if (!previewDetails.ok) {
|
|
74
|
-
|
|
72
|
+
ui.log.error(`Preview ${previewKey} not found.`);
|
|
75
73
|
process.exit(1);
|
|
76
74
|
}
|
|
77
75
|
|
|
78
|
-
const rootDir =
|
|
79
|
-
const spinner =
|
|
76
|
+
const rootDir = getDistDir();
|
|
77
|
+
const spinner = ui.spinner();
|
|
80
78
|
spinner.start("Compressing files...");
|
|
81
79
|
|
|
82
80
|
const result = await pushPreparedDirectoryToPreview({
|
|
@@ -90,10 +88,10 @@ export const push = async ({ skipBuild = false, previewKey: previewKeyArg } = {}
|
|
|
90
88
|
|
|
91
89
|
if (!result.ok) {
|
|
92
90
|
spinner.stop("Upload failed.", 1);
|
|
93
|
-
|
|
91
|
+
ui.log.error(result.error);
|
|
94
92
|
process.exit(1);
|
|
95
93
|
}
|
|
96
94
|
|
|
97
95
|
spinner.stop(`Files uploaded to preview ${previewKey}.`);
|
|
98
|
-
|
|
96
|
+
ui.log.message(` ${previewDetails.data.url}`);
|
|
99
97
|
};
|
package/lib/stores.mjs
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { fetchUserStores } from "./api.mjs";
|
|
2
|
+
import { loadConfigOrFail, writeConfig } from "./config.mjs";
|
|
3
|
+
import * as ui from "./ui.mjs";
|
|
4
|
+
|
|
5
|
+
const formatStoreLabel = (store, { active = false } = {}) =>
|
|
6
|
+
`${store.name} (ID: ${store.id})${active ? " [active]" : ""}`;
|
|
7
|
+
|
|
8
|
+
const getStoresOrFail = async () => {
|
|
9
|
+
const { config, credentials } = await loadConfigOrFail({ requireStore: false });
|
|
10
|
+
const result = await fetchUserStores(config.apiBaseUrl, credentials.apiKey);
|
|
11
|
+
if (!result.ok) {
|
|
12
|
+
ui.log.error(result.error);
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return { config, credentials, stores: result.data };
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const storesList = async () => {
|
|
20
|
+
const spinner = ui.spinner();
|
|
21
|
+
spinner.start("Fetching stores...");
|
|
22
|
+
|
|
23
|
+
const { config, stores } = await getStoresOrFail();
|
|
24
|
+
spinner.stop(`Found ${stores.length} store${stores.length === 1 ? "" : "s"}.`);
|
|
25
|
+
|
|
26
|
+
if (stores.length === 0) {
|
|
27
|
+
ui.log.warn("No stores available for this API key.");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
ui.log.message("Stores:");
|
|
32
|
+
for (const store of stores) {
|
|
33
|
+
ui.log.message(`- ${formatStoreLabel(store, { active: config.storeId === store.id })}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!config.storeId) {
|
|
37
|
+
ui.log.info("No active store selected.");
|
|
38
|
+
ui.log.info("Next step: tiendu stores set <store-id>");
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const storesSet = async (storeIdArg) => {
|
|
43
|
+
const storeId = Number(storeIdArg);
|
|
44
|
+
if (!Number.isInteger(storeId) || storeId <= 0) {
|
|
45
|
+
ui.log.error("Invalid store id. Use: tiendu stores set <store-id>");
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const spinner = ui.spinner();
|
|
50
|
+
spinner.start("Validating store...");
|
|
51
|
+
|
|
52
|
+
const { config, stores } = await getStoresOrFail();
|
|
53
|
+
const selectedStore = stores.find((store) => store.id === storeId);
|
|
54
|
+
|
|
55
|
+
if (!selectedStore) {
|
|
56
|
+
spinner.stop("Store not found.", 1);
|
|
57
|
+
ui.log.error("Store not found for this API key. Run tiendu stores list to see available stores.");
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const nextConfig = config.storeId === storeId
|
|
62
|
+
? config
|
|
63
|
+
: { apiBaseUrl: config.apiBaseUrl, storeId };
|
|
64
|
+
|
|
65
|
+
await writeConfig(nextConfig);
|
|
66
|
+
spinner.stop(`Active store set to ${selectedStore.name} (ID: ${selectedStore.id}).`);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const formatInitSummary = ({ apiBaseUrl, usedDefaultBaseUrl, stores, selectedStore }) => {
|
|
70
|
+
const lines = ["Status: Connected."];
|
|
71
|
+
|
|
72
|
+
if (usedDefaultBaseUrl) {
|
|
73
|
+
lines.push(`Base URL: ${apiBaseUrl} (default)`);
|
|
74
|
+
} else {
|
|
75
|
+
lines.push(`Base URL: ${apiBaseUrl}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (selectedStore) {
|
|
79
|
+
lines.push(`Store: ${selectedStore.name} (ID: ${selectedStore.id}) [auto-selected]`);
|
|
80
|
+
return lines.join("\n");
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
lines.push(`Possible stores to select: ${stores.length}`);
|
|
84
|
+
for (const store of stores) {
|
|
85
|
+
lines.push(`- ${store.name} (ID: ${store.id})`);
|
|
86
|
+
}
|
|
87
|
+
lines.push("No active store selected.");
|
|
88
|
+
lines.push("Continue by running: tiendu stores set <store-id>");
|
|
89
|
+
|
|
90
|
+
return lines.join("\n");
|
|
91
|
+
};
|