@coraltravelcenter/b2c-landing-builder 2.12.0 → 2.12.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/README.md CHANGED
@@ -33,7 +33,8 @@ b2c-landing-vite update
33
33
  - `dev` запускает Vite и отдельный userscript с названием проекта для
34
34
  Tampermonkey.
35
35
  - `build` создаёт CMS-файлы в `@CMS/` и переписывает пути ассетов на CDN.
36
- - `deploy` собирает проект, проверяет ассеты на CDN и разворачивает CMS-виджеты.
36
+ - `deploy` собирает проект, синхронизирует ассеты с CDN и после успешной
37
+ синхронизации разворачивает CMS-виджеты.
37
38
  - `deploy --section <key>` разворачивает только выбранную CMS-секцию, не изменяя
38
39
  и не удаляя соседние секции.
39
40
  - `deploy assets` собирает проект и синхронизирует ассеты с B2C CDN.
@@ -255,9 +256,11 @@ Deploy-state обновляется атомарно только после у
255
256
  В итоговом блоке команда также выводит корневой URL `CDN folder`, например
256
257
  `https://b2ccdn.coral.ru/content/landing-pages/uae-promo/`.
257
258
 
258
- Обычный `deploy` не загружает ассеты. Он проверяет наличие всех файлов build
259
- manifest на CDN и останавливается до изменения CMS, если сначала требуется
260
- выполнить `deploy assets`.
259
+ Обычный `deploy` выполняет этот же план синхронизации ассетов перед изменением
260
+ CMS. Если нужны новые файлы, он запрашивает подтверждение загрузки и продолжает
261
+ CMS-deploy только после её успешного завершения. При отказе CMS не изменяется.
262
+ Отдельная команда `deploy assets` полезна, когда нужно загрузить файлы заранее,
263
+ не меняя CMS.
261
264
 
262
265
  Перед изменением CMS команда показывает план добавления, обновления, удаления и
263
266
  перестановки управляемых виджетов. Подтверждение и последующее предложение
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@coraltravelcenter/b2c-landing-builder",
3
- "version": "2.12.0",
3
+ "version": "2.12.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@coraltravelcenter/b2c-landing-builder",
9
- "version": "2.12.0",
9
+ "version": "2.12.2",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@babel/parser": "7.28.6",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coraltravelcenter/b2c-landing-builder",
3
- "version": "2.12.0",
3
+ "version": "2.12.2",
4
4
  "description": "CLI and build toolkit for B2C landing projects",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli/index.mjs CHANGED
@@ -100,14 +100,24 @@ export async function runCli(args) {
100
100
  },
101
101
  });
102
102
  }
103
- await runCli(["build"]);
104
103
  const {getBackofficeToken} = await import("../deploy/auth.mjs");
105
104
  const token = await getBackofficeToken({brand: preset.id});
105
+ const {BackofficeClient} = await import("../deploy/backoffice-client.mjs");
106
+ const client = new BackofficeClient({brand: preset.id, token});
107
+ const {deployAssetsCommand} = await import("../deploy/assets-command.mjs");
108
+ const assets = await deployAssetsCommand({
109
+ config,
110
+ project,
111
+ preset,
112
+ build: () => runCli(["build"]),
113
+ client,
114
+ });
115
+ if (assets.cancelled) return assets;
106
116
  const {deployProject} = await import("../deploy/deploy.mjs");
107
117
  return deployProject({
108
118
  config,
109
119
  preset,
110
- token,
120
+ client,
111
121
  section: sectionOnly ? rest[1] : null,
112
122
  });
113
123
  });
@@ -36,16 +36,24 @@ export async function planAssetChanges({manifest, state, listFiles, remoteRoot =
36
36
  };
37
37
  const upload = [];
38
38
  const skip = [];
39
- await Promise.all(pending.map(async (asset) => {
39
+ let unchanged = 0;
40
+ const pendingPaths = new Set(pending.map((asset) => asset.path));
41
+ await Promise.all(manifest.assets.map(async (asset) => {
40
42
  const remotePath = path.posix.join("/", remoteRoot, manifest.assetsPrefix, asset.path);
41
43
  const names = await namesInDirectory(path.posix.dirname(remotePath));
42
- (names.has(path.posix.basename(remotePath)) ? skip : upload).push(asset);
44
+ const exists = names.has(path.posix.basename(remotePath));
45
+ if (!pendingPaths.has(asset.path)) {
46
+ if (exists) unchanged += 1;
47
+ else upload.push(asset);
48
+ return;
49
+ }
50
+ (exists ? skip : upload).push(asset);
43
51
  }));
44
52
  const byPath = (left, right) => left.path.localeCompare(right.path);
45
53
  return {
46
54
  upload: upload.sort(byPath),
47
55
  skip: skip.sort(byPath),
48
- unchanged: manifest.assets.length - pending.length,
56
+ unchanged,
49
57
  remove: prune ? obsoleteAssets(manifest, state) : [],
50
58
  };
51
59
  }
@@ -195,10 +195,25 @@ export class BackofficeClient {
195
195
  }
196
196
 
197
197
  async listFiles(remoteDirectory) {
198
- const result = await this.request("GET", "/FileManagement/ListFiles", {
199
- query: {path: remoteDirectory.replace(/^\/+/, "")},
200
- });
201
- return result?.result || result;
198
+ const remotePath = remoteDirectory.replace(/^\/+/, "");
199
+ const files = [];
200
+ const seenTokens = new Set();
201
+ let pageToken;
202
+ let baseUrl;
203
+ do {
204
+ const result = await this.request("GET", "/FileManagement/ListFiles", {
205
+ query: {path: remotePath, ...(pageToken ? {pageToken} : {})},
206
+ });
207
+ const page = result?.result || result || {};
208
+ if (Array.isArray(page.files)) files.push(...page.files);
209
+ baseUrl ||= page.baseUrl;
210
+ pageToken = page.pageToken;
211
+ if (pageToken && seenTokens.has(pageToken)) {
212
+ throw new Error("Backoffice file listing returned a repeated page token");
213
+ }
214
+ if (pageToken) seenTokens.add(pageToken);
215
+ } while (pageToken);
216
+ return {baseUrl, files};
202
217
  }
203
218
 
204
219
  async uploadFile(remotePath, localPath) {