tiendu 0.2.0 → 0.2.2

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/bin/tiendu.js CHANGED
@@ -7,6 +7,7 @@ import { dev } from "../lib/dev.mjs";
7
7
  import { publish } from "../lib/publish.mjs";
8
8
  import {
9
9
  previewCreate,
10
+ previewShow,
10
11
  previewList,
11
12
  previewDelete,
12
13
  previewOpen,
@@ -23,10 +24,11 @@ Usage:
23
24
  tiendu dev Start dev mode: auto-sync changes to a live preview URL
24
25
  tiendu publish Publish the active preview to the live storefront
25
26
 
27
+ tiendu preview Show the active preview details
26
28
  tiendu preview create Create a new remote preview
27
29
  tiendu preview list List previews for your store
28
30
  tiendu preview delete Delete the active preview
29
- tiendu preview open Open the preview URL in your browser
31
+ tiendu preview open Open the active preview URL in your browser
30
32
 
31
33
  tiendu help Show this help message
32
34
 
@@ -82,6 +84,10 @@ const main = async () => {
82
84
  }
83
85
 
84
86
  if (command === "preview") {
87
+ if (!subcommand) {
88
+ await previewShow();
89
+ return;
90
+ }
85
91
  if (subcommand === "create") {
86
92
  await previewCreate(args[2]);
87
93
  return;
package/lib/dev.mjs CHANGED
@@ -4,7 +4,11 @@ import path from "node:path";
4
4
  import * as p from "@clack/prompts";
5
5
  import { zipSync } from "fflate";
6
6
  import { loadConfigOrFail, writeConfig } from "./config.mjs";
7
- import { createPreview, listPreviews } from "./preview.mjs";
7
+ import {
8
+ createPreview,
9
+ listPreviews,
10
+ resolveActivePreview,
11
+ } from "./preview.mjs";
8
12
  import {
9
13
  deletePreviewFile,
10
14
  uploadPreviewFileMultipart,
@@ -54,7 +58,19 @@ export const dev = async () => {
54
58
  const { apiKey } = credentials;
55
59
  const rootDir = process.cwd();
56
60
 
57
- let previewKey = config.previewKey;
61
+ const existingPreviewsResult = await listPreviews(
62
+ apiBaseUrl,
63
+ apiKey,
64
+ storeId,
65
+ );
66
+ if (!existingPreviewsResult.ok) {
67
+ p.log.error(existingPreviewsResult.error);
68
+ process.exit(1);
69
+ }
70
+
71
+ let previewKey =
72
+ resolveActivePreview(existingPreviewsResult.data, config.previewKey)
73
+ ?.previewKey ?? config.previewKey;
58
74
  let previewUrl;
59
75
 
60
76
  if (!previewKey) {
@@ -102,15 +118,22 @@ export const dev = async () => {
102
118
  process.exit(1);
103
119
  }
104
120
 
105
- const existing = listResult.data.find((pr) => pr.previewKey === previewKey);
121
+ const existing = resolveActivePreview(listResult.data, previewKey);
106
122
  if (!existing) {
107
- spinner.stop("Preview no longer exists on the server.", 1);
123
+ spinner.stop("Could not determine the active preview.", 1);
108
124
  p.log.error(
109
- "Run tiendu preview delete to clean up, then tiendu dev again.",
125
+ listResult.data.length === 0
126
+ ? "No previews found for this store. A new preview will be created if you clear the local config and run tiendu dev again."
127
+ : "Run tiendu preview list and then set or recreate the preview.",
110
128
  );
111
129
  process.exit(1);
112
130
  }
113
131
 
132
+ previewKey = existing.previewKey;
133
+ if (config.previewKey !== previewKey) {
134
+ await writeConfig({ ...config, previewKey });
135
+ }
136
+
114
137
  previewUrl = buildPreviewUrl(apiBaseUrl, existing.previewHostname);
115
138
  spinner.stop(`Preview: ${previewUrl}`);
116
139
  }
package/lib/preview.mjs CHANGED
@@ -8,6 +8,25 @@ const buildPreviewUrl = (apiBaseUrl, previewHostname) => {
8
8
  return `${base.protocol}//${previewHostname}${!hasExplicitPort && base.port ? `:${base.port}` : ""}/`;
9
9
  };
10
10
 
11
+ /**
12
+ * @param {Array<any>} previews
13
+ * @param {string | undefined} previewKey
14
+ * @returns {any | null}
15
+ */
16
+ export const resolveActivePreview = (previews, previewKey) => {
17
+ if (previewKey) {
18
+ return (
19
+ previews.find((preview) => preview.previewKey === previewKey) ?? null
20
+ );
21
+ }
22
+
23
+ if (previews.length === 1) {
24
+ return previews[0];
25
+ }
26
+
27
+ return null;
28
+ };
29
+
11
30
  /**
12
31
  * @param {string} apiBaseUrl
13
32
  * @param {string} apiKey
@@ -196,23 +215,103 @@ export const previewList = async () => {
196
215
  `${result.data.length} preview${result.data.length === 1 ? "" : "s"}:`,
197
216
  );
198
217
 
218
+ const activePreview = resolveActivePreview(result.data, config.previewKey);
219
+
199
220
  for (const preview of result.data) {
200
- const active = config.previewKey === preview.previewKey ? " ← active" : "";
221
+ const active =
222
+ activePreview?.previewKey === preview.previewKey ? " ← active" : "";
201
223
  const url = buildPreviewUrl(config.apiBaseUrl, preview.previewHostname);
202
224
  p.log.message(` ${preview.name} ${url}${active}`);
203
225
  }
204
226
  };
205
227
 
228
+ const formatRelativeDate = (value) => {
229
+ if (!value) return "Unknown";
230
+ const date = new Date(value);
231
+ if (Number.isNaN(date.getTime())) return "Unknown";
232
+
233
+ const diffMs = Date.now() - date.getTime();
234
+ const diffMinutes = Math.floor(diffMs / (60 * 1000));
235
+ if (diffMinutes < 1) return "just now";
236
+ if (diffMinutes < 60)
237
+ return `${diffMinutes} minute${diffMinutes === 1 ? "" : "s"} ago`;
238
+ const diffHours = Math.floor(diffMinutes / 60);
239
+ if (diffHours < 24)
240
+ return `${diffHours} hour${diffHours === 1 ? "" : "s"} ago`;
241
+ const diffDays = Math.floor(diffHours / 24);
242
+ return `${diffDays} day${diffDays === 1 ? "" : "s"} ago`;
243
+ };
244
+
245
+ export const previewShow = async () => {
246
+ const { config, credentials } = await loadConfigOrFail();
247
+
248
+ const spinner = p.spinner();
249
+ spinner.start("Fetching preview details...");
250
+
251
+ const result = await listPreviews(
252
+ config.apiBaseUrl,
253
+ credentials.apiKey,
254
+ config.storeId,
255
+ );
256
+
257
+ if (!result.ok) {
258
+ spinner.stop("Failed to fetch previews.", 1);
259
+ p.log.error(result.error);
260
+ process.exit(1);
261
+ }
262
+
263
+ const preview = resolveActivePreview(result.data, config.previewKey);
264
+ if (!preview) {
265
+ spinner.stop("Could not determine the active preview.", 1);
266
+ p.log.error(
267
+ result.data.length === 0
268
+ ? "No previews found for this store."
269
+ : "Run tiendu preview list to inspect available previews.",
270
+ );
271
+ process.exit(1);
272
+ }
273
+
274
+ const url = buildPreviewUrl(config.apiBaseUrl, preview.previewHostname);
275
+ spinner.stop("Preview details loaded.");
276
+
277
+ p.note(
278
+ [
279
+ `Name: ${preview.name || "Unnamed preview"}`,
280
+ `URL: ${url}`,
281
+ `Created: ${formatRelativeDate(preview.createdAt)}`,
282
+ ].join("\n"),
283
+ "Active preview",
284
+ );
285
+ };
286
+
206
287
  export const previewDelete = async () => {
207
288
  const { config, credentials } = await loadConfigOrFail();
208
289
 
209
- if (!config.previewKey) {
210
- p.log.error("No active preview. Create one with: tiendu preview create");
290
+ const listResult = await listPreviews(
291
+ config.apiBaseUrl,
292
+ credentials.apiKey,
293
+ config.storeId,
294
+ );
295
+ if (!listResult.ok) {
296
+ p.log.error(listResult.error);
297
+ process.exit(1);
298
+ }
299
+
300
+ const activePreview = resolveActivePreview(
301
+ listResult.data,
302
+ config.previewKey,
303
+ );
304
+ if (!activePreview) {
305
+ p.log.error(
306
+ listResult.data.length === 0
307
+ ? "No previews found for this store."
308
+ : "Could not determine the active preview. Run tiendu preview list first.",
309
+ );
211
310
  process.exit(1);
212
311
  }
213
312
 
214
313
  const confirmed = await p.confirm({
215
- message: `Delete preview ${config.previewKey}?`,
314
+ message: `Delete preview ${activePreview.previewKey}?`,
216
315
  });
217
316
 
218
317
  if (p.isCancel(confirmed) || !confirmed) {
@@ -227,7 +326,7 @@ export const previewDelete = async () => {
227
326
  config.apiBaseUrl,
228
327
  credentials.apiKey,
229
328
  config.storeId,
230
- config.previewKey,
329
+ activePreview.previewKey,
231
330
  );
232
331
 
233
332
  if (!result.ok) {
@@ -245,11 +344,6 @@ export const previewDelete = async () => {
245
344
  export const previewOpen = async () => {
246
345
  const { config, credentials } = await loadConfigOrFail();
247
346
 
248
- if (!config.previewKey) {
249
- p.log.error("No active preview. Create one with: tiendu preview create");
250
- process.exit(1);
251
- }
252
-
253
347
  const spinner = p.spinner();
254
348
  spinner.start("Fetching preview URL...");
255
349
 
@@ -265,10 +359,14 @@ export const previewOpen = async () => {
265
359
  process.exit(1);
266
360
  }
267
361
 
268
- const preview = result.data.find((pr) => pr.previewKey === config.previewKey);
362
+ const preview = resolveActivePreview(result.data, config.previewKey);
269
363
  if (!preview) {
270
- spinner.stop("Active preview no longer exists on the server.", 1);
271
- p.log.error("Run tiendu preview delete to clean up the local config.");
364
+ spinner.stop("Could not determine the active preview.", 1);
365
+ p.log.error(
366
+ result.data.length === 0
367
+ ? "No previews found for this store."
368
+ : "Run tiendu preview list and then set or recreate the preview.",
369
+ );
272
370
  process.exit(1);
273
371
  }
274
372
 
package/lib/pull.mjs CHANGED
@@ -38,8 +38,4 @@ export const pull = async () => {
38
38
  p.log.success(
39
39
  `${extractedFiles.length} file${extractedFiles.length === 1 ? "" : "s"} extracted.`,
40
40
  );
41
-
42
- for (const file of extractedFiles) {
43
- p.log.message(` ${file}`);
44
- }
45
41
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tiendu",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "CLI para desarrollar y publicar temas en Tiendu",
5
5
  "type": "module",
6
6
  "bin": {