sb-mig 6.2.0 → 6.3.0-beta.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.
Files changed (44) hide show
  1. package/README.md +15 -4
  2. package/dist/api/assets/asset-folders.d.ts +3 -0
  3. package/dist/api/assets/asset-folders.js +56 -0
  4. package/dist/api/assets/asset-folders.types.d.ts +29 -0
  5. package/dist/api/assets/asset-folders.types.js +1 -0
  6. package/dist/api/assets/assets.d.ts +4 -1
  7. package/dist/api/assets/assets.js +61 -16
  8. package/dist/api/assets/assets.types.d.ts +12 -1
  9. package/dist/api/assets/index.d.ts +4 -2
  10. package/dist/api/assets/index.js +2 -1
  11. package/dist/api/copy/assets.d.ts +12 -0
  12. package/dist/api/copy/assets.js +84 -0
  13. package/dist/api/copy/graph.d.ts +8 -0
  14. package/dist/api/copy/graph.js +27 -0
  15. package/dist/api/copy/index.d.ts +6 -0
  16. package/dist/api/copy/index.js +6 -0
  17. package/dist/api/copy/manifest.d.ts +23 -0
  18. package/dist/api/copy/manifest.js +115 -0
  19. package/dist/api/copy/reference-rewriter.d.ts +6 -0
  20. package/dist/api/copy/reference-rewriter.js +153 -0
  21. package/dist/api/copy/reference-scanner.d.ts +23 -0
  22. package/dist/api/copy/reference-scanner.js +322 -0
  23. package/dist/api/copy/types.d.ts +196 -0
  24. package/dist/api/copy/types.js +1 -0
  25. package/dist/api/data-migration/component-data-migration.d.ts +81 -0
  26. package/dist/api/data-migration/component-data-migration.js +391 -95
  27. package/dist/api/data-migration/migration-run-log.d.ts +3 -1
  28. package/dist/api/data-migration/migration-run-log.js +2 -1
  29. package/dist/api/managementApi.d.ts +6 -0
  30. package/dist/api/stories/stories.js +3 -3
  31. package/dist/api/stories/stories.types.d.ts +3 -1
  32. package/dist/api/testApi.d.ts +6 -0
  33. package/dist/api-v2/requestConfig.d.ts +17 -1
  34. package/dist/api-v2/requestConfig.js +28 -0
  35. package/dist/cli/cli-descriptions.d.ts +3 -3
  36. package/dist/cli/cli-descriptions.js +92 -18
  37. package/dist/cli/commands/copy.js +2091 -145
  38. package/dist/cli/commands/migrate.js +27 -1
  39. package/dist/cli/index.js +3 -0
  40. package/dist/utils/files.d.ts +2 -0
  41. package/dist/utils/files.js +86 -3
  42. package/dist-cjs/api/stories/stories.js +3 -3
  43. package/dist-cjs/api-v2/requestConfig.js +29 -0
  44. package/package.json +2 -2
@@ -1,5 +1,5 @@
1
1
  import path from "path";
2
- import { migrateAllComponentsDataInStories, migrateProvidedComponentsDataInStories, } from "../../api/data-migration/component-data-migration.js";
2
+ import { migrateAllComponentsDataInStories, migrateProvidedComponentsDataInStories, prepareContinueMigration, } from "../../api/data-migration/component-data-migration.js";
3
3
  import { buildStoryBackupBaseName } from "../../api/data-migration/file-naming.js";
4
4
  import { parseMigrationComponentAliasFlags, parseMigrationComponentOverrideFlags, } from "../../api/data-migration/migration-component-scope.js";
5
5
  import { managementApi } from "../../api/managementApi.js";
@@ -13,6 +13,7 @@ import { isItFactory, unpackElements, getFrom, getTo, } from "../utils/cli-utils
13
13
  const MIGRATE_COMMANDS = {
14
14
  content: "content",
15
15
  presets: "presets",
16
+ continue: "continue",
16
17
  };
17
18
  const normalizeMigrationFlags = (migrationFlag) => {
18
19
  if (Array.isArray(migrationFlag)) {
@@ -280,6 +281,31 @@ export const migrate = async (props) => {
280
281
  }
281
282
  break;
282
283
  }
284
+ case MIGRATE_COMMANDS.continue: {
285
+ const manifestFileName = flags["manifest"];
286
+ const plan = await prepareContinueMigration({ manifestFileName }, apiConfig);
287
+ const { summary } = plan;
288
+ Logger.log("Found dry-run to continue:");
289
+ console.log(` manifest: ${summary.manifestFileName}`);
290
+ console.log(` space (from→to): ${summary.from} → ${summary.to}`);
291
+ console.log(` publication mode: ${summary.publicationMode}`);
292
+ console.log(` migrations: ${summary.migrationConfigNames.join(", ") || "(none recorded)"}`);
293
+ if (summary.resolvedPublishLanguages.length > 0) {
294
+ console.log(` languages: ${summary.resolvedPublishLanguages.join(", ")}`);
295
+ }
296
+ console.log(` ${summary.itemType}s to write: ${summary.changedCount} changed${summary.dirtyPublishedCount > 0
297
+ ? ` (${summary.dirtyPublishedCount} dirty-published dual-layer write(s))`
298
+ : ""}`);
299
+ console.log(" using artifacts:");
300
+ summary.artifactFiles.forEach((file) => console.log(` - ${file}`));
301
+ console.log(" ");
302
+ await askForConfirmation(`Continue and write these to Storyblok space ${summary.to}? (it will overwrite ${summary.itemType}s)`, async () => {
303
+ await plan.run();
304
+ }, () => {
305
+ Logger.warning("Continue not started, exiting the program...");
306
+ }, flags["yes"]);
307
+ break;
308
+ }
283
309
  default:
284
310
  console.log(`no command like that: ${command}`);
285
311
  }
package/dist/cli/index.js CHANGED
@@ -83,6 +83,9 @@ app.migrate = () => ({
83
83
  fileName: {
84
84
  type: "string",
85
85
  },
86
+ manifest: {
87
+ type: "string",
88
+ },
86
89
  },
87
90
  }),
88
91
  action: async (cli) => {
@@ -38,6 +38,7 @@ export declare const getPackageJson: () => any;
38
38
  export declare const isDirectoryExists: (path: string) => boolean;
39
39
  export declare const createDir: (dirPath: string) => Promise<void>;
40
40
  export declare const createJsonFile: (content: string, pathWithFilename: string) => Promise<void>;
41
+ export declare const writeJsonArrayStreamed: (items: any[], pathWithFilename: string) => Promise<void>;
41
42
  export declare const createJSAllComponentsFile: (content: string, pathWithFilename: string, timestamp?: boolean) => Promise<void>;
42
43
  export declare const copyFolder: (src: string, dest: string) => Promise<unknown>;
43
44
  export declare const copyFile: (src: string, dest: string) => Promise<void>;
@@ -59,6 +60,7 @@ type CreateAndSaveToFile = (args: {
59
60
  }, config: RequestBaseConfig) => void;
60
61
  export declare const createAndSaveToFile: CreateAndSaveToFile;
61
62
  export declare const createAndSaveComponentListToFile: CreateAndSaveComponentListToFile;
63
+ export declare const listFilesInDir: (dirPath: string) => Promise<string[]>;
62
64
  export declare const readFile: (pathToFile: string) => Promise<string | undefined>;
63
65
  export declare const dumpToFile: (path: string, content: string) => Promise<void>;
64
66
  export declare const getConsumerPackageJson: () => Promise<any>;
@@ -84,6 +84,75 @@ export const createDir = async (dirPath) => {
84
84
  export const createJsonFile = async (content, pathWithFilename) => {
85
85
  await fs.promises.writeFile(pathWithFilename, content, { flag: "w" });
86
86
  };
87
+ /*
88
+ * Streams an array to disk as a pretty-printed (2-space) JSON array,
89
+ * one element at a time. Avoids building the whole document as a single
90
+ * string, which overflows V8's max string length (~512 MB) for large
91
+ * migration sets. Output is byte-for-byte identical to
92
+ * JSON.stringify(items, null, 2).
93
+ * */
94
+ export const writeJsonArrayStreamed = (items, pathWithFilename) => {
95
+ return new Promise((resolve, reject) => {
96
+ const stream = fs.createWriteStream(pathWithFilename, { flags: "w" });
97
+ let settled = false;
98
+ const fail = (err) => {
99
+ if (settled)
100
+ return;
101
+ settled = true;
102
+ reject(err);
103
+ };
104
+ stream.on("error", fail);
105
+ stream.on("finish", () => {
106
+ if (settled)
107
+ return;
108
+ settled = true;
109
+ resolve();
110
+ });
111
+ const indentElement = (item) => {
112
+ const serialized = JSON.stringify(item, null, 2);
113
+ const body = serialized === undefined ? "null" : serialized;
114
+ return ` ${body.replace(/\n/g, "\n ")}`;
115
+ };
116
+ const writeChunk = (chunk) => new Promise((res, rej) => {
117
+ const onErr = (err) => rej(err);
118
+ const ok = stream.write(chunk, (err) => {
119
+ if (err) {
120
+ rej(err);
121
+ return;
122
+ }
123
+ if (ok) {
124
+ stream.removeListener("error", onErr);
125
+ res();
126
+ }
127
+ });
128
+ if (!ok) {
129
+ stream.once("drain", () => {
130
+ stream.removeListener("error", onErr);
131
+ res();
132
+ });
133
+ }
134
+ stream.once("error", onErr);
135
+ });
136
+ const writeChunks = async () => {
137
+ if (items.length === 0) {
138
+ stream.write("[]");
139
+ return;
140
+ }
141
+ await writeChunk("[\n");
142
+ for (let i = 0; i < items.length; i++) {
143
+ const prefix = i === 0 ? "" : ",\n";
144
+ await writeChunk(`${prefix}${indentElement(items[i])}`);
145
+ }
146
+ await writeChunk("\n]");
147
+ };
148
+ writeChunks()
149
+ .then(() => stream.end())
150
+ .catch((err) => {
151
+ stream.destroy();
152
+ fail(err);
153
+ });
154
+ });
155
+ };
87
156
  export const createJSAllComponentsFile = async (content, pathWithFilename, timestamp = false) => {
88
157
  const datestamp = new Date();
89
158
  const finalContent = `/*
@@ -146,8 +215,8 @@ export const createAndSaveToFile = async (args, config) => {
146
215
  const finalFilename = `${prefix}${filename}${datestamp ? `__${timestamp}` : ""}`;
147
216
  const fullPath = `${sbmigWorkingDirectory}/${folder}/${finalFilename}${suffix}.${ext}`;
148
217
  await createDir(`${sbmigWorkingDirectory}/${folder}/`);
149
- if (ext === "json") {
150
- await createJsonFile(JSON.stringify(res, undefined, 2), fullPath);
218
+ if (Array.isArray(res)) {
219
+ await writeJsonArrayStreamed(res, fullPath);
151
220
  }
152
221
  else {
153
222
  await createJsonFile(JSON.stringify(res, undefined, 2), fullPath);
@@ -157,7 +226,12 @@ export const createAndSaveToFile = async (args, config) => {
157
226
  if (path) {
158
227
  const folderPath = nodePath.dirname(path);
159
228
  await createDir(folderPath);
160
- await createJsonFile(JSON.stringify(res, undefined, 2), path);
229
+ if (Array.isArray(res)) {
230
+ await writeJsonArrayStreamed(res, path);
231
+ }
232
+ else {
233
+ await createJsonFile(JSON.stringify(res, undefined, 2), path);
234
+ }
161
235
  Logger.success(`All response written to a file: ${path}`);
162
236
  }
163
237
  };
@@ -179,6 +253,15 @@ export const createAndSaveComponentListToFile = async ({ file, folder, res, time
179
253
  : `${sbmigWorkingDirectory}/${filename}.ts`, timestamp);
180
254
  Logger.success(`All components written to a file: ${filename}`);
181
255
  };
256
+ export const listFilesInDir = async (dirPath) => {
257
+ const absolutePath = resolveFromCwd(dirPath);
258
+ try {
259
+ return await fs.promises.readdir(absolutePath);
260
+ }
261
+ catch {
262
+ return [];
263
+ }
264
+ };
182
265
  export const readFile = async (pathToFile) => {
183
266
  const absolutePath = resolveFromCwd(pathToFile);
184
267
  try {
@@ -354,13 +354,13 @@ const getStoryVersions = async ({ storyId, showContent = true, page = 1, perPage
354
354
  };
355
355
  exports.getStoryVersions = getStoryVersions;
356
356
  // CREATE
357
- const createStory = (content, config) => {
357
+ const createStory = (content, config, options) => {
358
358
  const { spaceId, sbApi } = config;
359
359
  logger_js_1.default.log(`Creating story with name: ${content.name} in space: ${spaceId}`);
360
360
  return sbApi
361
361
  .post(`spaces/${spaceId}/stories/`, {
362
362
  story: content,
363
- publish: true,
363
+ publish: options?.publish ?? true,
364
364
  })
365
365
  .then((res) => res.data)
366
366
  .catch((err) => console.error(err));
@@ -371,7 +371,7 @@ const updateStory = (content, storyId, options, config) => {
371
371
  const { spaceId, sbApi } = config;
372
372
  const storyLabel = resolveStoryLabel(content, storyId);
373
373
  logger_js_1.default.warning("Trying to update Story...");
374
- logger_js_1.default.log(`Updating story with name: ${content.name} in space: ${spaceId}`);
374
+ logger_js_1.default.log(`Updating story '${storyLabel}' in space: ${spaceId}`);
375
375
  // console.log("THis is content to update: ");
376
376
  // console.log(JSON.stringify(content, null, 2));
377
377
  return sbApi
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.toRequestConfig = toRequestConfig;
4
+ exports.configToClient = configToClient;
4
5
  function toRequestConfig(client, overrides) {
5
6
  return {
6
7
  spaceId: overrides?.spaceId ?? client.spaceId,
@@ -35,3 +36,31 @@ function toRequestConfig(client, overrides) {
35
36
  storyblokComponentsLocalDirectory: overrides?.storyblokComponentsLocalDirectory,
36
37
  };
37
38
  }
39
+ /**
40
+ * Inverse of `toRequestConfig`: adapt a legacy `RequestBaseConfig` into an
41
+ * `ApiClient`.
42
+ *
43
+ * This is the primitive used by the Strategy-B shims (see SDK-REFACTOR.md §3): a
44
+ * legacy `api/<x>` function receives a `RequestBaseConfig` and delegates to the
45
+ * moved `api-v2/<x>` function, which takes an `ApiClient`. The live `sbApi`
46
+ * instance is reused (not recreated) so rate-limit and cache state are preserved.
47
+ *
48
+ * Note: `ApiClient` intentionally carries only the data-API essentials (`sbApi`,
49
+ * `spaceId`, tokens). The remaining `IStoryblokConfig` fields on
50
+ * `RequestBaseConfig` (directories, extensions, resolvers, …) are node-tier /
51
+ * file-discovery concerns handled separately — they are not represented on
52
+ * `ApiClient`. See SDK-REFACTOR.md §4 and ticket F5.
53
+ */
54
+ function configToClient(config, overrides) {
55
+ const clientConfig = {
56
+ oauthToken: overrides?.oauthToken ?? config.oauthToken ?? "",
57
+ spaceId: overrides?.spaceId ?? config.spaceId,
58
+ accessToken: overrides?.accessToken ?? config.accessToken,
59
+ rateLimit: overrides?.rateLimit ?? config.rateLimit,
60
+ };
61
+ return {
62
+ config: clientConfig,
63
+ sbApi: config.sbApi,
64
+ spaceId: clientConfig.spaceId,
65
+ };
66
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sb-mig",
3
- "version": "6.2.0",
3
+ "version": "6.3.0-beta.2",
4
4
  "description": "CLI to rule the world. (and handle stuff related to Storyblok CMS)",
5
5
  "author": "Marcin Krawczyk <marckraw@icloud.com>",
6
6
  "license": "MIT",
@@ -15,7 +15,7 @@
15
15
  "access": "public"
16
16
  },
17
17
  "bugs": "https://github.com/sb-mig/sb-mig/issues",
18
- "homepage": "https://github.com/sb-mig/sb-mig",
18
+ "homepage": "https://sb-mig.vercel.app",
19
19
  "keywords": [
20
20
  "cli",
21
21
  "storyblok",