tiendu 0.3.1 → 0.5.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 +64 -14
- package/bin/tiendu.js +6 -4
- package/bin/tiendu.mjs +1 -134
- package/lib/api.mjs +18 -50
- package/lib/archive.mjs +30 -0
- package/lib/assets.mjs +245 -0
- package/lib/build.mjs +299 -41
- package/lib/dev.mjs +239 -136
- package/lib/fs-utils.mjs +35 -0
- package/lib/local-preview.mjs +350 -0
- package/lib/postcss.mjs +166 -0
- package/lib/preview.mjs +19 -9
- package/lib/publish.mjs +12 -2
- package/lib/push.mjs +51 -52
- package/lib/retry.mjs +69 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Official CLI for [Tiendu](https://tiendu.uy) — develop and publish storefront themes from your local machine.
|
|
4
4
|
|
|
5
|
-
Download your store's theme, edit files locally, preview changes live with a
|
|
5
|
+
Download your store's theme, edit files locally, preview changes live with a local auto-reloading URL plus a sharable preview URL, and publish when you're ready — all from the terminal.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -44,7 +44,7 @@ tiendu init
|
|
|
44
44
|
tiendu dev
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
`tiendu dev` creates a remote preview, builds your source files,
|
|
47
|
+
`tiendu dev` creates a remote preview, builds your source files, runs an initial push from the prepared output, and then watches for changes. It prints a local live-preview URL first, plus a sharable preview URL like:
|
|
48
48
|
|
|
49
49
|
```
|
|
50
50
|
http://preview-xxxxxxxxxxxx.tiendu.uy/
|
|
@@ -52,6 +52,9 @@ http://preview-xxxxxxxxxxxx.tiendu.uy/
|
|
|
52
52
|
|
|
53
53
|
The preview renders with the real Tiendu engine — same output as production.
|
|
54
54
|
|
|
55
|
+
When `tiendu dev` starts, it always re-syncs your current local files to the active preview before watching for changes.
|
|
56
|
+
It also starts a local live-preview URL that proxies the preview and auto-reloads after successful syncs.
|
|
57
|
+
|
|
55
58
|
---
|
|
56
59
|
|
|
57
60
|
## Commands
|
|
@@ -91,9 +94,11 @@ tiendu build
|
|
|
91
94
|
|
|
92
95
|
The build:
|
|
93
96
|
|
|
94
|
-
1. Copies theme files
|
|
95
|
-
2.
|
|
96
|
-
3.
|
|
97
|
+
1. Copies theme files from `src/layout/`, `src/templates/`, and `src/snippets/` to `dist/`
|
|
98
|
+
2. Flattens static files from `src/assets/` into `dist/assets/`
|
|
99
|
+
3. Discovers entry points in `src/layout/` and `src/templates/`
|
|
100
|
+
4. Bundles JS/TS and CSS into `dist/assets/`
|
|
101
|
+
5. Runs project PostCSS plugins for CSS entries when available (for example Tailwind v4)
|
|
97
102
|
|
|
98
103
|
Entry naming convention:
|
|
99
104
|
|
|
@@ -115,7 +120,10 @@ tiendu dev
|
|
|
115
120
|
```
|
|
116
121
|
|
|
117
122
|
- Prints the preview URL on start
|
|
123
|
+
- Re-syncs the full local theme to the preview on startup
|
|
118
124
|
- Syncs file creates, edits and deletes
|
|
125
|
+
- Retries failed file sync operations up to 3 times before giving up
|
|
126
|
+
- Starts a local live-preview URL on `localhost` that refreshes after successful uploads
|
|
119
127
|
- Handles both text and binary files (images, fonts, etc.)
|
|
120
128
|
- Press `Ctrl+C` to stop
|
|
121
129
|
|
|
@@ -126,22 +134,31 @@ tiendu dev
|
|
|
126
134
|
Zips and uploads files to the active preview, replacing its content entirely.
|
|
127
135
|
|
|
128
136
|
- **Buildless themes:** uploads from the current directory.
|
|
129
|
-
- **Built themes:** uploads from `dist/`.
|
|
137
|
+
- **Built themes:** runs `tiendu build` first, then uploads from `dist/`.
|
|
130
138
|
|
|
131
139
|
```bash
|
|
132
140
|
tiendu push
|
|
141
|
+
tiendu push --skip-build
|
|
133
142
|
```
|
|
134
143
|
|
|
144
|
+
Use `--skip-build` to upload the existing `dist/` output without rebuilding.
|
|
145
|
+
|
|
135
146
|
---
|
|
136
147
|
|
|
137
148
|
### `tiendu publish`
|
|
138
149
|
|
|
139
150
|
Publishes the active preview to the live storefront. Visitors will see the new theme immediately. All previews for the store are removed after publishing.
|
|
140
151
|
|
|
152
|
+
- **Buildless themes:** publishes the active preview as-is.
|
|
153
|
+
- **Built themes:** builds the theme, uploads the latest `dist/` output to the preview, then publishes it.
|
|
154
|
+
|
|
141
155
|
```bash
|
|
142
156
|
tiendu publish
|
|
157
|
+
tiendu publish --skip-build
|
|
143
158
|
```
|
|
144
159
|
|
|
160
|
+
Use `--skip-build` to publish after uploading the existing `dist/` output without rebuilding.
|
|
161
|
+
|
|
145
162
|
---
|
|
146
163
|
|
|
147
164
|
### `tiendu check-updates`
|
|
@@ -263,26 +280,59 @@ my-theme/
|
|
|
263
280
|
├── .gitignore
|
|
264
281
|
├── src/
|
|
265
282
|
│ ├── layout/
|
|
283
|
+
│ │ ├── theme.liquid # copied to dist/layout/theme.liquid
|
|
266
284
|
│ │ ├── theme.ts # layout TS entry → layout-theme.bundle.js
|
|
267
285
|
│ │ └── theme.css # layout CSS entry → layout-theme.bundle.css
|
|
268
286
|
│ ├── templates/
|
|
287
|
+
│ │ ├── product.liquid # copied to dist/templates/product.liquid
|
|
269
288
|
│ │ ├── product.ts # template TS entry → template-product.bundle.js
|
|
270
289
|
│ │ └── product.css # template CSS entry → template-product.bundle.css
|
|
290
|
+
│ ├── snippets/ # Liquid snippets copied to dist/snippets/
|
|
291
|
+
│ ├── assets/ # source assets → flattened into dist/assets/
|
|
271
292
|
│ ├── lib/ # shared modules (bundled into entries, not served)
|
|
272
293
|
│ └── css/ # shared CSS (imported by entry CSS)
|
|
273
|
-
├── layout/ # Liquid layouts
|
|
274
|
-
├── templates/ # Liquid templates
|
|
275
|
-
├── snippets/ # Liquid snippets
|
|
276
|
-
├── assets/ # static assets (SVGs, images, fonts)
|
|
277
294
|
└── dist/ # build output (gitignored, uploaded to Tiendu)
|
|
278
295
|
```
|
|
279
296
|
|
|
280
297
|
### How it works
|
|
281
298
|
|
|
282
|
-
1. Source
|
|
283
|
-
2.
|
|
284
|
-
3.
|
|
285
|
-
4. Liquid
|
|
299
|
+
1. Source assets in `src/assets/` are flattened into `dist/assets/` (`payment-methods/visa.svg` becomes `payment-methods___visa.svg`)
|
|
300
|
+
2. Source JS/TS/CSS in `src/` is bundled by esbuild into `dist/assets/`
|
|
301
|
+
3. CSS entries also run through your local PostCSS pipeline when configured
|
|
302
|
+
4. Liquid files are copied from `src/` to `dist/`
|
|
303
|
+
5. `dist/` is what gets uploaded — it looks like a normal Tiendu theme
|
|
304
|
+
6. Liquid templates reference bundles and assets via `asset_url` (e.g. `{{ 'layout-theme.bundle.js' | asset_url | script_tag }}` or `{{ 'payment-methods/visa.svg' | asset_url }}`)
|
|
305
|
+
|
|
306
|
+
### Tailwind v4
|
|
307
|
+
|
|
308
|
+
Built themes can use Tailwind v4 in CSS entry files.
|
|
309
|
+
|
|
310
|
+
Install it in your theme project:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
npm install -D tailwindcss @tailwindcss/postcss postcss
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Then import Tailwind from a CSS entry such as `src/layout/theme.css`:
|
|
317
|
+
|
|
318
|
+
```css
|
|
319
|
+
@import "tailwindcss";
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
You can either:
|
|
323
|
+
|
|
324
|
+
- rely on Tiendu CLI's automatic Tailwind detection when `@tailwindcss/postcss` is installed, or
|
|
325
|
+
- add a local `postcss.config.mjs` / `postcss.config.js` / `postcss.config.cjs` / `postcss.config.json`
|
|
326
|
+
|
|
327
|
+
Example `postcss.config.mjs`:
|
|
328
|
+
|
|
329
|
+
```js
|
|
330
|
+
export default {
|
|
331
|
+
plugins: {
|
|
332
|
+
"@tailwindcss/postcss": {},
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
```
|
|
286
336
|
|
|
287
337
|
### tiendu.config.json
|
|
288
338
|
|
package/bin/tiendu.js
CHANGED
|
@@ -26,9 +26,10 @@ Usage:
|
|
|
26
26
|
tiendu init [dir] Set up a theme project (optionally in a new directory)
|
|
27
27
|
tiendu pull Download the live theme from your store
|
|
28
28
|
tiendu build Build a theme (requires tiendu.config.json)
|
|
29
|
-
tiendu push
|
|
29
|
+
tiendu push [--skip-build] Upload local files to the active preview (full replace)
|
|
30
30
|
tiendu dev Start dev mode: auto-sync changes to a live preview URL
|
|
31
|
-
tiendu publish
|
|
31
|
+
tiendu publish [--skip-build]
|
|
32
|
+
Publish the active preview to the live storefront
|
|
32
33
|
|
|
33
34
|
tiendu preview Show the active preview details
|
|
34
35
|
tiendu preview create Create a new remote preview
|
|
@@ -55,6 +56,7 @@ const main = async () => {
|
|
|
55
56
|
const args = process.argv.slice(2);
|
|
56
57
|
const command = args[0];
|
|
57
58
|
const subcommand = args[1];
|
|
59
|
+
const skipBuild = args.includes("--skip-build");
|
|
58
60
|
|
|
59
61
|
if (
|
|
60
62
|
command === "version" ||
|
|
@@ -100,7 +102,7 @@ const main = async () => {
|
|
|
100
102
|
}
|
|
101
103
|
|
|
102
104
|
if (command === "push") {
|
|
103
|
-
await push();
|
|
105
|
+
await push({ skipBuild });
|
|
104
106
|
return;
|
|
105
107
|
}
|
|
106
108
|
|
|
@@ -110,7 +112,7 @@ const main = async () => {
|
|
|
110
112
|
}
|
|
111
113
|
|
|
112
114
|
if (command === "publish") {
|
|
113
|
-
await publish();
|
|
115
|
+
await publish({ skipBuild });
|
|
114
116
|
return;
|
|
115
117
|
}
|
|
116
118
|
|
package/bin/tiendu.mjs
CHANGED
|
@@ -1,136 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import { pull } from "../lib/pull.mjs";
|
|
5
|
-
import { push } from "../lib/push.mjs";
|
|
6
|
-
import { dev } from "../lib/dev.mjs";
|
|
7
|
-
import { publish } from "../lib/publish.mjs";
|
|
8
|
-
import {
|
|
9
|
-
previewCreate,
|
|
10
|
-
previewList,
|
|
11
|
-
previewDelete,
|
|
12
|
-
previewOpen,
|
|
13
|
-
} from "../lib/preview.mjs";
|
|
14
|
-
import {
|
|
15
|
-
checkForUpdates,
|
|
16
|
-
checkForUpdatesNow,
|
|
17
|
-
getCurrentVersion,
|
|
18
|
-
} from "../lib/update-check.mjs";
|
|
19
|
-
|
|
20
|
-
const HELP = `
|
|
21
|
-
tiendu — CLI para desarrollar temas de Tiendu
|
|
22
|
-
|
|
23
|
-
Uso:
|
|
24
|
-
tiendu init Inicializar un tema en el directorio actual
|
|
25
|
-
tiendu pull Descargar el tema live desde Tiendu
|
|
26
|
-
tiendu push Subir archivos locales al preview activo (ZIP)
|
|
27
|
-
tiendu dev Modo desarrollo: watch + sync automático
|
|
28
|
-
tiendu publish Publicar el preview activo al storefront live
|
|
29
|
-
|
|
30
|
-
tiendu preview create Crear un preview remoto
|
|
31
|
-
tiendu preview list Listar previews de la tienda
|
|
32
|
-
tiendu preview delete Eliminar el preview activo
|
|
33
|
-
tiendu preview open Abrir la URL del preview en el navegador
|
|
34
|
-
|
|
35
|
-
tiendu check-updates Buscar una nueva version del CLI
|
|
36
|
-
tiendu version Mostrar la version actual del CLI
|
|
37
|
-
|
|
38
|
-
tiendu help Mostrar esta ayuda
|
|
39
|
-
|
|
40
|
-
Opciones:
|
|
41
|
-
--help, -h Mostrar esta ayuda
|
|
42
|
-
--version, -v Mostrar la version actual del CLI
|
|
43
|
-
`;
|
|
44
|
-
|
|
45
|
-
const main = async () => {
|
|
46
|
-
const args = process.argv.slice(2);
|
|
47
|
-
const command = args[0];
|
|
48
|
-
const subcommand = args[1];
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
command === "version" ||
|
|
52
|
-
command === "--version" ||
|
|
53
|
-
command === "-v"
|
|
54
|
-
) {
|
|
55
|
-
console.log(getCurrentVersion());
|
|
56
|
-
process.exit(0);
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (
|
|
60
|
-
!command ||
|
|
61
|
-
command === "help" ||
|
|
62
|
-
command === "--help" ||
|
|
63
|
-
command === "-h"
|
|
64
|
-
) {
|
|
65
|
-
console.log(HELP.trim());
|
|
66
|
-
process.exit(0);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
if (command === "check-updates") {
|
|
70
|
-
await checkForUpdatesNow();
|
|
71
|
-
return;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
await checkForUpdates();
|
|
75
|
-
|
|
76
|
-
if (command === "init") {
|
|
77
|
-
await init();
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (command === "pull") {
|
|
82
|
-
await pull();
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (command === "push") {
|
|
87
|
-
await push();
|
|
88
|
-
return;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (command === "dev") {
|
|
92
|
-
await dev();
|
|
93
|
-
return;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
if (command === "publish") {
|
|
97
|
-
await publish();
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (command === "preview") {
|
|
102
|
-
if (subcommand === "create") {
|
|
103
|
-
const name = args[2];
|
|
104
|
-
await previewCreate(name);
|
|
105
|
-
return;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
if (subcommand === "list") {
|
|
109
|
-
await previewList();
|
|
110
|
-
return;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (subcommand === "delete") {
|
|
114
|
-
await previewDelete();
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (subcommand === "open") {
|
|
119
|
-
await previewOpen();
|
|
120
|
-
return;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
console.error(`Subcomando desconocido: preview ${subcommand ?? "(vacío)"}`);
|
|
124
|
-
console.log(HELP.trim());
|
|
125
|
-
process.exit(1);
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
console.error(`Comando desconocido: ${command}`);
|
|
129
|
-
console.log(HELP.trim());
|
|
130
|
-
process.exit(1);
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
main().catch((error) => {
|
|
134
|
-
console.error(error.message || error);
|
|
135
|
-
process.exit(1);
|
|
136
|
-
});
|
|
3
|
+
import "./tiendu.js";
|
package/lib/api.mjs
CHANGED
|
@@ -2,9 +2,14 @@
|
|
|
2
2
|
* @param {string} apiBaseUrl
|
|
3
3
|
* @param {string} apiKey
|
|
4
4
|
* @param {string} path
|
|
5
|
-
* @param {{ method?: string, body?: string | Buffer | Uint8Array, contentType?: string }} [options]
|
|
5
|
+
* @param {{ method?: string, body?: string | Buffer | Uint8Array, contentType?: string, signal?: AbortSignal }} [options]
|
|
6
6
|
* @returns {Promise<Response>}
|
|
7
7
|
*/
|
|
8
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
9
|
+
|
|
10
|
+
const isRetriableStatus = (status) =>
|
|
11
|
+
status === 408 || status === 425 || status === 429 || status >= 500;
|
|
12
|
+
|
|
8
13
|
export const apiFetch = (apiBaseUrl, apiKey, path, options = {}) => {
|
|
9
14
|
const url = `${apiBaseUrl}${path}`;
|
|
10
15
|
/** @type {Record<string, string>} */
|
|
@@ -22,6 +27,7 @@ export const apiFetch = (apiBaseUrl, apiKey, path, options = {}) => {
|
|
|
22
27
|
method: options.method ?? "GET",
|
|
23
28
|
headers,
|
|
24
29
|
body: options.body,
|
|
30
|
+
signal: options.signal ?? AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
25
31
|
});
|
|
26
32
|
};
|
|
27
33
|
|
|
@@ -126,7 +132,7 @@ export const downloadStorefrontArchive = async (
|
|
|
126
132
|
* @param {number} storeId
|
|
127
133
|
* @param {string} previewKey
|
|
128
134
|
* @param {Buffer} zipBuffer
|
|
129
|
-
* @returns {Promise<{ ok: true } | { ok: false, error: string }>}
|
|
135
|
+
* @returns {Promise<{ ok: true } | { ok: false, error: string, retriable?: boolean }>}
|
|
130
136
|
*/
|
|
131
137
|
export const uploadPreviewZip = async (
|
|
132
138
|
apiBaseUrl,
|
|
@@ -152,51 +158,7 @@ export const uploadPreviewZip = async (
|
|
|
152
158
|
return {
|
|
153
159
|
ok: false,
|
|
154
160
|
error: `Error del servidor: ${response.status}${body ? ` — ${body}` : ""}`,
|
|
155
|
-
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return { ok: true };
|
|
159
|
-
} catch (error) {
|
|
160
|
-
return { ok: false, error: `No se pudo subir: ${error.message}` };
|
|
161
|
-
}
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Upload a single file to a preview.
|
|
166
|
-
*
|
|
167
|
-
* @param {string} apiBaseUrl
|
|
168
|
-
* @param {string} apiKey
|
|
169
|
-
* @param {number} storeId
|
|
170
|
-
* @param {string} previewKey
|
|
171
|
-
* @param {string} filePath - relative path within the preview
|
|
172
|
-
* @param {string} content - file content
|
|
173
|
-
* @returns {Promise<{ ok: true } | { ok: false, error: string }>}
|
|
174
|
-
*/
|
|
175
|
-
export const uploadPreviewFile = async (
|
|
176
|
-
apiBaseUrl,
|
|
177
|
-
apiKey,
|
|
178
|
-
storeId,
|
|
179
|
-
previewKey,
|
|
180
|
-
filePath,
|
|
181
|
-
content,
|
|
182
|
-
) => {
|
|
183
|
-
try {
|
|
184
|
-
const query = new URLSearchParams({ path: filePath }).toString();
|
|
185
|
-
const response = await apiFetch(
|
|
186
|
-
apiBaseUrl,
|
|
187
|
-
apiKey,
|
|
188
|
-
`/api/v2/stores/${storeId}/theme-previews/${previewKey}/file?${query}`,
|
|
189
|
-
{
|
|
190
|
-
method: "PUT",
|
|
191
|
-
body: JSON.stringify({ content }),
|
|
192
|
-
},
|
|
193
|
-
);
|
|
194
|
-
|
|
195
|
-
if (!response.ok) {
|
|
196
|
-
const body = await response.text().catch(() => "");
|
|
197
|
-
return {
|
|
198
|
-
ok: false,
|
|
199
|
-
error: `Error subiendo ${filePath}: ${response.status}${body ? ` — ${body}` : ""}`,
|
|
161
|
+
retriable: isRetriableStatus(response.status),
|
|
200
162
|
};
|
|
201
163
|
}
|
|
202
164
|
|
|
@@ -204,7 +166,8 @@ export const uploadPreviewFile = async (
|
|
|
204
166
|
} catch (error) {
|
|
205
167
|
return {
|
|
206
168
|
ok: false,
|
|
207
|
-
error: `
|
|
169
|
+
error: `No se pudo subir: ${error.message}`,
|
|
170
|
+
retriable: true,
|
|
208
171
|
};
|
|
209
172
|
}
|
|
210
173
|
};
|
|
@@ -219,7 +182,7 @@ export const uploadPreviewFile = async (
|
|
|
219
182
|
* @param {string} previewKey
|
|
220
183
|
* @param {string} relativePath
|
|
221
184
|
* @param {Buffer} fileBuffer
|
|
222
|
-
* @returns {Promise<{ ok: true } | { ok: false, error: string }>}
|
|
185
|
+
* @returns {Promise<{ ok: true } | { ok: false, error: string, retriable?: boolean }>}
|
|
223
186
|
*/
|
|
224
187
|
export const uploadPreviewFileMultipart = async (
|
|
225
188
|
apiBaseUrl,
|
|
@@ -249,6 +212,7 @@ export const uploadPreviewFileMultipart = async (
|
|
|
249
212
|
Authorization: `Bearer ${apiKey}`,
|
|
250
213
|
},
|
|
251
214
|
body: formData,
|
|
215
|
+
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
|
252
216
|
},
|
|
253
217
|
);
|
|
254
218
|
|
|
@@ -257,6 +221,7 @@ export const uploadPreviewFileMultipart = async (
|
|
|
257
221
|
return {
|
|
258
222
|
ok: false,
|
|
259
223
|
error: `Error subiendo ${relativePath}: ${response.status}${body ? ` — ${body}` : ""}`,
|
|
224
|
+
retriable: isRetriableStatus(response.status),
|
|
260
225
|
};
|
|
261
226
|
}
|
|
262
227
|
|
|
@@ -265,6 +230,7 @@ export const uploadPreviewFileMultipart = async (
|
|
|
265
230
|
return {
|
|
266
231
|
ok: false,
|
|
267
232
|
error: `Error subiendo ${relativePath}: ${error.message}`,
|
|
233
|
+
retriable: true,
|
|
268
234
|
};
|
|
269
235
|
}
|
|
270
236
|
};
|
|
@@ -277,7 +243,7 @@ export const uploadPreviewFileMultipart = async (
|
|
|
277
243
|
* @param {number} storeId
|
|
278
244
|
* @param {string} previewKey
|
|
279
245
|
* @param {string} filePath
|
|
280
|
-
* @returns {Promise<{ ok: true } | { ok: false, error: string }>}
|
|
246
|
+
* @returns {Promise<{ ok: true } | { ok: false, error: string, retriable?: boolean }>}
|
|
281
247
|
*/
|
|
282
248
|
export const deletePreviewFile = async (
|
|
283
249
|
apiBaseUrl,
|
|
@@ -302,6 +268,7 @@ export const deletePreviewFile = async (
|
|
|
302
268
|
return {
|
|
303
269
|
ok: false,
|
|
304
270
|
error: `Error eliminando ${filePath}: ${response.status}${body ? ` — ${body}` : ""}`,
|
|
271
|
+
retriable: isRetriableStatus(response.status),
|
|
305
272
|
};
|
|
306
273
|
}
|
|
307
274
|
|
|
@@ -310,6 +277,7 @@ export const deletePreviewFile = async (
|
|
|
310
277
|
return {
|
|
311
278
|
ok: false,
|
|
312
279
|
error: `Error eliminando ${filePath}: ${error.message}`,
|
|
280
|
+
retriable: true,
|
|
313
281
|
};
|
|
314
282
|
}
|
|
315
283
|
};
|
package/lib/archive.mjs
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { zipSync } from "fflate";
|
|
4
|
+
import { listFilesRecursive } from "./fs-utils.mjs";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} rootDir
|
|
8
|
+
* @returns {Promise<string[]>}
|
|
9
|
+
*/
|
|
10
|
+
export const listAllFiles = async (rootDir) => listFilesRecursive(rootDir);
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} rootDir
|
|
14
|
+
* @returns {Promise<Buffer>}
|
|
15
|
+
*/
|
|
16
|
+
export const createZipFromDirectory = async (rootDir) => {
|
|
17
|
+
const absoluteFiles = await listAllFiles(rootDir);
|
|
18
|
+
/** @type {Record<string, Uint8Array>} */
|
|
19
|
+
const entries = {};
|
|
20
|
+
|
|
21
|
+
for (const absolutePath of absoluteFiles) {
|
|
22
|
+
const relativePath = path
|
|
23
|
+
.relative(rootDir, absolutePath)
|
|
24
|
+
.split(path.sep)
|
|
25
|
+
.join("/");
|
|
26
|
+
entries[relativePath] = new Uint8Array(await readFile(absolutePath));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return Buffer.from(zipSync(entries, { level: 6 }));
|
|
30
|
+
};
|