sb-mig 5.5.4-beta.1 → 5.6.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 (175) hide show
  1. package/README.md +20 -19
  2. package/dist/api/assets/assets.js +1 -14
  3. package/dist/api/components/components.js +2 -1
  4. package/dist/api/components/components.sync.d.ts +8 -0
  5. package/dist/api/components/components.sync.js +193 -0
  6. package/dist/api/data-migration/component-data-migration.d.ts +6 -2
  7. package/dist/api/data-migration/component-data-migration.js +35 -11
  8. package/dist/api/datasources/datasource-entries.js +4 -5
  9. package/dist/api/datasources/datasources.d.ts +5 -2
  10. package/dist/api/datasources/datasources.js +42 -35
  11. package/dist/api/datasources/datasources.sync.d.ts +2 -0
  12. package/dist/api/datasources/datasources.sync.js +11 -0
  13. package/dist/api/datasources/datasources.types.d.ts +1 -1
  14. package/dist/api/datasources/index.d.ts +2 -1
  15. package/dist/api/datasources/index.js +2 -1
  16. package/dist/api/managementApi.d.ts +2 -2
  17. package/dist/api/migrate.d.ts +1 -1
  18. package/dist/api/migrate.js +3 -48
  19. package/dist/api/plugins/index.d.ts +2 -1
  20. package/dist/api/plugins/index.js +2 -1
  21. package/dist/api/plugins/plugins.d.ts +7 -2
  22. package/dist/api/plugins/plugins.js +28 -15
  23. package/dist/api/plugins/plugins.sync.d.ts +2 -0
  24. package/dist/api/plugins/plugins.sync.js +11 -0
  25. package/dist/api/roles/index.d.ts +2 -1
  26. package/dist/api/roles/index.js +2 -1
  27. package/dist/api/roles/roles.d.ts +5 -2
  28. package/dist/api/roles/roles.js +34 -11
  29. package/dist/api/roles/roles.sync.d.ts +2 -0
  30. package/dist/api/roles/roles.sync.js +6 -0
  31. package/dist/api/roles/roles.types.d.ts +1 -1
  32. package/dist/api/stories/stories.js +5 -13
  33. package/dist/api/sync/sync.types.d.ts +30 -0
  34. package/dist/api/sync/sync.types.js +1 -0
  35. package/dist/api/testApi.d.ts +2 -2
  36. package/dist/api/utils/helper-functions.d.ts +5 -1
  37. package/dist/api/utils/helper-functions.js +6 -1
  38. package/dist/api/utils/request.d.ts +1 -1
  39. package/dist/api/utils/request.js +11 -2
  40. package/dist/api/utils/resolverTransformations.js +2 -57
  41. package/dist/api-v2/assets/index.d.ts +13 -0
  42. package/dist/api-v2/assets/index.js +25 -0
  43. package/dist/api-v2/auth/index.d.ts +3 -0
  44. package/dist/api-v2/auth/index.js +8 -0
  45. package/dist/api-v2/client.d.ts +13 -0
  46. package/dist/api-v2/client.js +17 -0
  47. package/dist/api-v2/components/index.d.ts +10 -0
  48. package/dist/api-v2/components/index.js +29 -0
  49. package/dist/api-v2/datasources/index.d.ts +8 -0
  50. package/dist/api-v2/datasources/index.js +58 -0
  51. package/dist/api-v2/discover/discover.d.ts +36 -0
  52. package/dist/api-v2/discover/discover.js +281 -0
  53. package/dist/api-v2/discover/index.d.ts +2 -0
  54. package/dist/api-v2/discover/index.js +1 -0
  55. package/dist/api-v2/index.d.ts +19 -0
  56. package/dist/api-v2/index.js +21 -0
  57. package/dist/api-v2/plugins/index.d.ts +9 -0
  58. package/dist/api-v2/plugins/index.js +42 -0
  59. package/dist/api-v2/precompile/index.d.ts +2 -0
  60. package/dist/api-v2/precompile/index.js +1 -0
  61. package/dist/api-v2/precompile/precompile.d.ts +65 -0
  62. package/dist/api-v2/precompile/precompile.js +127 -0
  63. package/dist/api-v2/presets/index.d.ts +13 -0
  64. package/dist/api-v2/presets/index.js +25 -0
  65. package/dist/api-v2/requestConfig.d.ts +5 -0
  66. package/dist/api-v2/requestConfig.js +34 -0
  67. package/dist/api-v2/roles/index.d.ts +5 -0
  68. package/dist/api-v2/roles/index.js +35 -0
  69. package/dist/api-v2/spaces/index.d.ts +7 -0
  70. package/dist/api-v2/spaces/index.js +11 -0
  71. package/dist/api-v2/stories/index.d.ts +34 -0
  72. package/dist/api-v2/stories/index.js +172 -0
  73. package/dist/api-v2/stories/types.d.ts +28 -0
  74. package/dist/api-v2/stories/types.js +1 -0
  75. package/dist/api-v2/sync/index.d.ts +24 -0
  76. package/dist/api-v2/sync/index.js +109 -0
  77. package/dist/api-v2/sync/types.d.ts +1 -0
  78. package/dist/api-v2/sync/types.js +1 -0
  79. package/dist/api-v2/test.d.ts +15 -0
  80. package/dist/api-v2/test.js +21 -0
  81. package/dist/cli/cli-descriptions.d.ts +1 -1
  82. package/dist/cli/cli-descriptions.js +4 -0
  83. package/dist/cli/commands/backup.js +7 -3
  84. package/dist/cli/commands/copy.js +2 -2
  85. package/dist/cli/commands/migrate.js +13 -2
  86. package/dist/cli/commands/migrations.js +2 -2
  87. package/dist/cli/commands/remove.js +1 -1
  88. package/dist/cli/commands/revert.js +2 -2
  89. package/dist/cli/commands/sync.js +1 -2
  90. package/dist/cli/index.js +8 -1
  91. package/dist/cli/utils/cli-utils.d.ts +69 -0
  92. package/dist/cli/utils/cli-utils.js +100 -0
  93. package/dist/cli/utils/discover.d.ts +3 -22
  94. package/dist/cli/utils/discover.js +4 -51
  95. package/dist/config/config.d.ts +2 -39
  96. package/dist/config/config.types.d.ts +40 -0
  97. package/dist/config/config.types.js +1 -0
  98. package/dist/config/defaultConfig.d.ts +1 -1
  99. package/dist/config/defaultConfig.js +2 -2
  100. package/dist/utils/array-utils.d.ts +20 -0
  101. package/dist/utils/array-utils.js +20 -0
  102. package/dist/utils/async-utils.d.ts +13 -0
  103. package/dist/utils/async-utils.js +13 -0
  104. package/dist/utils/date-utils.d.ts +14 -0
  105. package/dist/utils/date-utils.js +21 -0
  106. package/dist/utils/files.d.ts +35 -0
  107. package/dist/utils/files.js +57 -2
  108. package/dist/utils/main.d.ts +8 -18
  109. package/dist/utils/main.js +12 -104
  110. package/dist/utils/migrations.d.ts +9 -3
  111. package/dist/utils/object-utils.d.ts +46 -0
  112. package/dist/utils/object-utils.js +71 -0
  113. package/dist/utils/others.d.ts +6 -9
  114. package/dist/utils/others.js +8 -15
  115. package/dist/utils/path-utils.d.ts +89 -0
  116. package/dist/utils/path-utils.js +106 -0
  117. package/dist/utils/pkg.d.ts +16 -2
  118. package/dist/utils/pkg.js +16 -3
  119. package/dist/utils/string-utils.d.ts +33 -0
  120. package/dist/utils/string-utils.js +45 -0
  121. package/dist/utils/transform-utils.d.ts +62 -0
  122. package/dist/utils/transform-utils.js +113 -0
  123. package/dist-cjs/api/auth/auth.js +28 -0
  124. package/dist-cjs/api/auth/auth.types.js +2 -0
  125. package/dist-cjs/api/components/components.js +202 -0
  126. package/dist-cjs/api/components/components.sync.js +199 -0
  127. package/dist-cjs/api/components/components.types.js +2 -0
  128. package/dist-cjs/api/datasources/datasource-entries.js +166 -0
  129. package/dist-cjs/api/datasources/datasources.js +166 -0
  130. package/dist-cjs/api/datasources/datasources.types.js +2 -0
  131. package/dist-cjs/api/plugins/plugins.js +132 -0
  132. package/dist-cjs/api/plugins/plugins.types.js +2 -0
  133. package/dist-cjs/api/presets/componentPresets.js +25 -0
  134. package/dist-cjs/api/presets/presets.js +92 -0
  135. package/dist-cjs/api/presets/presets.types.js +2 -0
  136. package/dist-cjs/api/presets/resolvePresets.js +49 -0
  137. package/dist-cjs/api/roles/roles.js +131 -0
  138. package/dist-cjs/api/roles/roles.types.js +2 -0
  139. package/dist-cjs/api/spaces/spaces.js +34 -0
  140. package/dist-cjs/api/spaces/spaces.types.js +2 -0
  141. package/dist-cjs/api/stories/stories.js +214 -0
  142. package/dist-cjs/api/stories/stories.types.js +2 -0
  143. package/dist-cjs/api/sync/sync.types.js +2 -0
  144. package/dist-cjs/api/utils/request.js +48 -0
  145. package/dist-cjs/api/utils/resolvers.types.js +2 -0
  146. package/dist-cjs/api-v2/assets/index.js +30 -0
  147. package/dist-cjs/api-v2/auth/index.js +12 -0
  148. package/dist-cjs/api-v2/client.js +23 -0
  149. package/dist-cjs/api-v2/components/index.js +40 -0
  150. package/dist-cjs/api-v2/datasources/index.js +64 -0
  151. package/dist-cjs/api-v2/discover/discover.js +321 -0
  152. package/dist-cjs/api-v2/discover/index.js +9 -0
  153. package/dist-cjs/api-v2/index.js +60 -0
  154. package/dist-cjs/api-v2/plugins/index.js +49 -0
  155. package/dist-cjs/api-v2/precompile/index.js +7 -0
  156. package/dist-cjs/api-v2/precompile/precompile.js +136 -0
  157. package/dist-cjs/api-v2/presets/index.js +33 -0
  158. package/dist-cjs/api-v2/requestConfig.js +37 -0
  159. package/dist-cjs/api-v2/roles/index.js +41 -0
  160. package/dist-cjs/api-v2/spaces/index.js +16 -0
  161. package/dist-cjs/api-v2/stories/index.js +180 -0
  162. package/dist-cjs/api-v2/stories/types.js +2 -0
  163. package/dist-cjs/api-v2/sync/index.js +115 -0
  164. package/dist-cjs/api-v2/sync/types.js +2 -0
  165. package/dist-cjs/api-v2/test.js +25 -0
  166. package/dist-cjs/config/config.types.js +2 -0
  167. package/dist-cjs/config/constants.js +29 -0
  168. package/dist-cjs/package.json +3 -0
  169. package/dist-cjs/utils/array-utils.js +24 -0
  170. package/dist-cjs/utils/logger.js +32 -0
  171. package/dist-cjs/utils/object-utils.js +77 -0
  172. package/dist-cjs/utils/path-utils.js +115 -0
  173. package/package.json +37 -20
  174. package/dist/utils/pkg-require.d.ts +0 -2
  175. package/dist/utils/pkg-require.js +0 -4
package/README.md CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  <p align="center">
3
2
  <img width="250" height="250" src="./sb-mig-logo.png" alt="Logo" />
4
3
  </p>
@@ -10,17 +9,16 @@ If you've found an issue or you have feature request - <a href="https://github.c
10
9
  [![GitHub issues](https://img.shields.io/github/issues/sb-mig/sb-mig.svg?style=flat-square&v=1)](https://github.com/sb-mig/sb-mig/issues?q=is%3Aopen+is%3Aissue)
11
10
  ![npm](https://img.shields.io/npms-io/maintenance-score/sb-mig)
12
11
 
13
-
14
-
15
12
  # Requirements:
16
13
 
17
- | | |
18
- | ------------- | ------------- |
19
- | Node | LTS (18.x.x) |
14
+ | | |
15
+ | ---- | ------------ |
16
+ | Node | LTS (18.x.x) |
20
17
 
21
18
  # 5.x.x version released!
22
19
 
23
20
  ## Important Updates
21
+
24
22
  - Complete codebase overhaul to facilitate the utilization of features and requests to Storyblok. This development decreases the tight coupling with CLI, while improving folder and file structure.
25
23
  - New feature: Content synchronization (including stories and assets) in various directions, ranging from space to space, from space to file, and from file to space.
26
24
  - New feature: Introduced support for TypeScript Schema, with the added ability to precompile them on-the-fly before synchronization, improving usage with sb-mig.
@@ -33,12 +31,12 @@ If you've found an issue or you have feature request - <a href="https://github.c
33
31
  - Expanded test coverage (with more additions anticipated).
34
32
 
35
33
  ## Breaking changes
34
+
36
35
  - Please note that sb-mig no longer extends support for Node versions older than 18.x.x, as part of its adoption of native ESM support.
37
36
  - The sb-mig backup command has now been aligned with all other commands, causing minor changes in its execution (although functionalities have been preserved).
38
37
 
39
38
  Do not hesitate to get in touch if you encounter any issues or require further clarification on any points.
40
39
 
41
-
42
40
  # 4.x.x version released!
43
41
 
44
42
  - Whole deployment now, is handled by [semantic-release](https://github.com/semantic-release/semantic-release). And is just normal repository, instead of Lerna monorepo which is not needed anymore, and it was recently unmaintained (now it was passed to `nrwl` to maintain (https://github.com/lerna/lerna/issues/3121) will see what will happen in future with it :)
@@ -57,10 +55,10 @@ Do not hesitate to get in touch if you encounter any issues or require further c
57
55
 
58
56
  - [How to install and configure](#how-to-install-and-configure)
59
57
  - [Schema documentation:](#schema-documentation)
60
- - [Basics](#basics)
61
- - [Syncing components](#syncing-components)
62
- - [Syncing datasources](#syncing-datasources)
63
- - [Presets support](#presets-support)
58
+ - [Basics](#basics)
59
+ - [Syncing components](#syncing-components)
60
+ - [Syncing datasources](#syncing-datasources)
61
+ - [Presets support](#presets-support)
64
62
  - [Development](#development)
65
63
  - [Roadmap](#roadmap)
66
64
 
@@ -189,7 +187,6 @@ sb-mig sync components row column
189
187
 
190
188
  This command will look for `row.sb.js` and `column.sb.js` files inside a directories mentioned in `componentDirectories` field. (You can change directories name mapping by modifying `componentDirectories` inside `storyblok.config.js`). You can also change the extension searched by changing `schemaFileExt`. [How to install and configure](#how-to-install-and-configure))
191
189
 
192
-
193
190
  ## Syncing datasources
194
191
 
195
192
  You can also sync your `datasources`.
@@ -269,7 +266,6 @@ You can also sync all datasources, and that's the command we strongly recommend.
269
266
  sb-mig sync datasources --all
270
267
  ```
271
268
 
272
-
273
269
  ## Presets support
274
270
 
275
271
  Writing your own predefined data (presets) for components can be a pain, so with `sb-mig` you can create presets for your components in the storyblok gui, and then export them to a schema based `.sb.js` file to be picked up while syncing.
@@ -342,36 +338,41 @@ git clone git@github.com:sb-mig/sb-mig.git
342
338
  Install packages
343
339
 
344
340
  ```
345
- yarn
341
+ npm install
346
342
  ```
347
343
 
348
344
  Run development command
345
+
349
346
  ```bash
350
- yarn build:dev
347
+ npm run build:dev
351
348
  ```
352
349
 
353
350
  It will watch a file change, and on every change, will rebuild typescript and build the whole lib/cli.
354
351
 
355
- The you can use
352
+ The you can use
353
+
356
354
  ```
357
- node dist/index.js debug
355
+ node dist/index.js debug
358
356
  ```
357
+
359
358
  to access `sb-mig`
360
359
 
361
360
  For your conveniece, you can also, link it to proper `sb-mi` name:
361
+
362
362
  ```
363
- yarn link
363
+ npm link
364
364
  ```
365
365
 
366
366
  And then you can use it like that:
367
+
367
368
  ```
368
369
  sb-mig debug
369
370
  ```
370
371
 
371
-
372
372
  # Releasing
373
373
 
374
374
  ## Flow of branching out and merging
375
+
375
376
  - Before creating feature branch from beta, make sure you have newest beta. (by `git pull origin beta`).
376
377
  - Create feature branch from beta
377
378
  - Do your changes
@@ -4,6 +4,7 @@ import path from "path";
4
4
  import FormData from "form-data";
5
5
  import { createDir, isDirectoryExists } from "../../utils/files.js";
6
6
  import Logger from "../../utils/logger.js";
7
+ import { getFileName, getSizeFromURL } from "../../utils/string-utils.js";
7
8
  // GET
8
9
  export const getAllAssets = async (args, config) => {
9
10
  const { spaceId, search } = args;
@@ -77,20 +78,6 @@ const uploadFile = ({ signedResponseObject, pathToFile }) => {
77
78
  }
78
79
  });
79
80
  };
80
- const getFileName = (fileUrl) => {
81
- const fileName = fileUrl.split("/").pop();
82
- if (fileName) {
83
- return fileName;
84
- }
85
- else {
86
- throw Error("File name couldn't be extracted from URL.");
87
- }
88
- };
89
- const getSizeFromURL = (fileUrl) => {
90
- const data = fileUrl.split("/");
91
- const sizePos = data.length - 3;
92
- return data[sizePos];
93
- };
94
81
  const downloadAsset = async (args, config) => {
95
82
  const { debug, sbmigWorkingDirectory } = config;
96
83
  const { payload } = args;
@@ -55,7 +55,7 @@ export const createComponent = (component, presets, config) => {
55
55
  Logger.log(`Trying to create '${component.name}'`);
56
56
  const componentWithPresets = component;
57
57
  const { all_presets, ...componentWithoutPresets } = componentWithPresets;
58
- sbApi
58
+ return sbApi
59
59
  .post(`spaces/${spaceId}/components/`, {
60
60
  component: componentWithoutPresets,
61
61
  })
@@ -67,6 +67,7 @@ export const createComponent = (component, presets, config) => {
67
67
  })
68
68
  .catch((err) => {
69
69
  Logger.error(`${err.message} in migration of ${component.name} in createComponent function`);
70
+ throw err;
70
71
  });
71
72
  };
72
73
  /*
@@ -0,0 +1,8 @@
1
+ import type { SyncProgressCallback, SyncResult } from "../sync/sync.types.js";
2
+ import type { RequestBaseConfig } from "../utils/request.js";
3
+ export declare function syncComponentsData(args: {
4
+ components: any[];
5
+ presets: boolean;
6
+ ssot?: boolean;
7
+ onProgress?: SyncProgressCallback;
8
+ }, config: RequestBaseConfig): Promise<SyncResult>;
@@ -0,0 +1,193 @@
1
+ import { uniqueValuesFrom } from "../../utils/array-utils.js";
2
+ import Logger from "../../utils/logger.js";
3
+ import { isObjectEmpty } from "../../utils/object-utils.js";
4
+ /**
5
+ * Default progress callback that logs to console
6
+ */
7
+ const defaultProgress = (event) => {
8
+ if (event.type === "start") {
9
+ Logger.log(`Starting sync of ${event.total} components...`);
10
+ }
11
+ else if (event.type === "progress" && event.name) {
12
+ const status = event.action === "creating"
13
+ ? "Creating"
14
+ : event.action === "updating"
15
+ ? "Updating"
16
+ : event.action === "created"
17
+ ? "✓ Created"
18
+ : event.action === "updated"
19
+ ? "✓ Updated"
20
+ : event.action === "skipped"
21
+ ? "⏭ Skipped"
22
+ : "✘ Error";
23
+ Logger.log(`[${event.current}/${event.total}] ${status}: ${event.name}`);
24
+ }
25
+ else if (event.type === "complete") {
26
+ Logger.success(`Sync complete: ${event.message ?? "done"}`);
27
+ }
28
+ };
29
+ import { createComponent, createComponentsGroup, getAllComponents, getAllComponentsGroups, removeComponent, removeComponentGroup, updateComponent, } from "./components.js";
30
+ async function ensureComponentGroupsExist(groupNames, config) {
31
+ try {
32
+ const existing = await getAllComponentsGroups(config);
33
+ const existingNames = new Set((existing ?? []).map((g) => g.name));
34
+ for (const groupName of groupNames) {
35
+ if (!existingNames.has(groupName)) {
36
+ await createComponentsGroup(groupName, config);
37
+ }
38
+ }
39
+ }
40
+ catch (error) {
41
+ // Log but don't fail - component groups are optional
42
+ Logger.warning(`Could not fetch component groups: ${error instanceof Error ? error.message : String(error)}`);
43
+ }
44
+ }
45
+ function resolveGroupUuid(component, remoteGroups) {
46
+ if (!component.component_group_name) {
47
+ return { ...component, component_group_uuid: null };
48
+ }
49
+ const match = remoteGroups.find((g) => g.name === component.component_group_name);
50
+ if (!match)
51
+ return { ...component, component_group_uuid: null };
52
+ return { ...component, component_group_uuid: match.uuid };
53
+ }
54
+ export async function syncComponentsData(args, config) {
55
+ const { components, presets, ssot, onProgress } = args;
56
+ const progress = onProgress ?? defaultProgress;
57
+ const result = {
58
+ created: [],
59
+ updated: [],
60
+ skipped: [],
61
+ errors: [],
62
+ };
63
+ if (ssot) {
64
+ const existingComponents = await getAllComponents(config);
65
+ const existingGroups = await getAllComponentsGroups(config);
66
+ await Promise.allSettled([
67
+ ...(existingComponents ?? []).map((c) => removeComponent(c, config)),
68
+ ...(existingGroups ?? []).map((g) => removeComponentGroup(g, config)),
69
+ ]);
70
+ }
71
+ const nonEmptyComponents = components.filter((c) => !isObjectEmpty(c));
72
+ const groupsToCheck = uniqueValuesFrom(nonEmptyComponents
73
+ .filter((c) => c.component_group_name)
74
+ .map((c) => c.component_group_name));
75
+ await ensureComponentGroupsExist(groupsToCheck, config);
76
+ let remoteComponents = [];
77
+ let remoteGroups = [];
78
+ try {
79
+ remoteComponents = (await getAllComponents(config)) ?? [];
80
+ }
81
+ catch (error) {
82
+ Logger.warning(`Could not fetch remote components: ${error instanceof Error ? error.message : String(error)}`);
83
+ }
84
+ try {
85
+ remoteGroups = (await getAllComponentsGroups(config)) ?? [];
86
+ }
87
+ catch (error) {
88
+ Logger.warning(`Could not fetch remote groups: ${error instanceof Error ? error.message : String(error)}`);
89
+ }
90
+ const componentsToUpdate = [];
91
+ const componentsToCreate = [];
92
+ for (const component of nonEmptyComponents) {
93
+ if (!component?.name) {
94
+ result.skipped.push("unknown");
95
+ continue;
96
+ }
97
+ const remote = remoteComponents.find((rc) => rc.name === component.name);
98
+ if (remote) {
99
+ componentsToUpdate.push({ id: remote.id, ...component });
100
+ }
101
+ else {
102
+ componentsToCreate.push(component);
103
+ }
104
+ }
105
+ // Resolve group uuids after ensureComponentGroupsExist
106
+ const updatePayloads = componentsToUpdate.map((c) => resolveGroupUuid(c, remoteGroups));
107
+ const createPayloads = componentsToCreate.map((c) => resolveGroupUuid(c, remoteGroups));
108
+ const totalComponents = updatePayloads.length + createPayloads.length;
109
+ let currentIndex = 0;
110
+ // Report start
111
+ progress({ type: "start", total: totalComponents });
112
+ // Process updates sequentially for progress reporting
113
+ for (const component of updatePayloads) {
114
+ const name = String(component?.name ?? "unknown");
115
+ currentIndex++;
116
+ progress({
117
+ type: "progress",
118
+ current: currentIndex,
119
+ total: totalComponents,
120
+ name,
121
+ action: "updating",
122
+ });
123
+ try {
124
+ await updateComponent(component, presets, config);
125
+ result.updated.push(name);
126
+ progress({
127
+ type: "progress",
128
+ current: currentIndex,
129
+ total: totalComponents,
130
+ name,
131
+ action: "updated",
132
+ });
133
+ }
134
+ catch (error) {
135
+ result.errors.push({
136
+ name,
137
+ message: error instanceof Error ? error.message : String(error),
138
+ });
139
+ progress({
140
+ type: "progress",
141
+ current: currentIndex,
142
+ total: totalComponents,
143
+ name,
144
+ action: "error",
145
+ message: error instanceof Error ? error.message : String(error),
146
+ });
147
+ }
148
+ }
149
+ // Process creates sequentially for progress reporting
150
+ for (const component of createPayloads) {
151
+ const name = String(component?.name ?? "unknown");
152
+ currentIndex++;
153
+ progress({
154
+ type: "progress",
155
+ current: currentIndex,
156
+ total: totalComponents,
157
+ name,
158
+ action: "creating",
159
+ });
160
+ try {
161
+ await createComponent(component, presets, config);
162
+ result.created.push(name);
163
+ progress({
164
+ type: "progress",
165
+ current: currentIndex,
166
+ total: totalComponents,
167
+ name,
168
+ action: "created",
169
+ });
170
+ }
171
+ catch (error) {
172
+ result.errors.push({
173
+ name,
174
+ message: error instanceof Error ? error.message : String(error),
175
+ });
176
+ progress({
177
+ type: "progress",
178
+ current: currentIndex,
179
+ total: totalComponents,
180
+ name,
181
+ action: "error",
182
+ message: error instanceof Error ? error.message : String(error),
183
+ });
184
+ }
185
+ }
186
+ // Report completion
187
+ progress({
188
+ type: "complete",
189
+ total: totalComponents,
190
+ message: `${result.created.length} created, ${result.updated.length} updated, ${result.errors.length} errors`,
191
+ });
192
+ return result;
193
+ }
@@ -7,11 +7,15 @@ interface MigrateItems {
7
7
  migrateFrom: MigrateFrom;
8
8
  migrationConfig: string;
9
9
  componentsToMigrate: string[];
10
+ filters?: {
11
+ withSlug?: string[];
12
+ startsWith?: string;
13
+ };
10
14
  }
11
15
  export type MapperDefinition = (data: any) => any;
12
16
  export declare const prepareStoriesFromLocalFile: ({ from }: any) => any;
13
17
  export declare const prepareMigrationConfig: ({ migrationConfig }: any) => any;
14
- export declare const migrateAllComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, }: Omit<MigrateItems, "componentsToMigrate">, config: RequestBaseConfig) => Promise<void>;
18
+ export declare const migrateAllComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, filters, }: Omit<MigrateItems, "componentsToMigrate">, config: RequestBaseConfig) => Promise<void>;
15
19
  export declare const doTheMigration: ({ itemType, from, itemsToMigrate, componentsToMigrate, migrationConfigFileContent, migrationConfig, to, }: any, config: RequestBaseConfig) => Promise<void>;
16
- export declare const migrateProvidedComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, }: MigrateItems, config: RequestBaseConfig) => Promise<void>;
20
+ export declare const migrateProvidedComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, }: MigrateItems, config: RequestBaseConfig) => Promise<void>;
17
21
  export {};
@@ -2,10 +2,10 @@ import path from "path";
2
2
  import chalk from "chalk";
3
3
  import { discoverMigrationConfig, discoverStories, LOOKUP_TYPE, SCOPE, } from "../../cli/utils/discover.js";
4
4
  import storyblokConfig from "../../config/config.js";
5
- import { createAndSaveToFile } from "../../utils/files.js";
5
+ import { createAndSaveToFile, getFilesContentWithRequire, } from "../../utils/files.js";
6
6
  import Logger from "../../utils/logger.js";
7
- import { getFilesContentWithRequire, isObjectEmpty } from "../../utils/main.js";
8
7
  import { modifyOrCreateAppliedMigrationsFile } from "../../utils/migrations.js";
8
+ import { isObjectEmpty } from "../../utils/object-utils.js";
9
9
  import { managementApi } from "../managementApi.js";
10
10
  function replaceComponentData({ parent, key, components, mapper, depth, maxDepth, sumOfReplacing, }) {
11
11
  let currentMaxDepth = depth;
@@ -101,7 +101,7 @@ export const prepareMigrationConfig = ({ migrationConfig }) => {
101
101
  Logger.success(`Migration config loaded.`);
102
102
  return migrationConfigFileContent;
103
103
  };
104
- export const migrateAllComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, }, config) => {
104
+ export const migrateAllComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, filters, }, config) => {
105
105
  Logger.warning(`Trying to migrate all ${itemType} from ${migrateFrom}, ${from} to ${to}...`);
106
106
  const migrationConfigFileContent = prepareMigrationConfig({
107
107
  migrationConfig,
@@ -119,6 +119,7 @@ export const migrateAllComponentsDataInStories = async ({ itemType, migrationCon
119
119
  from,
120
120
  to,
121
121
  componentsToMigrate,
122
+ filters,
122
123
  }, config);
123
124
  };
124
125
  export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate, componentsToMigrate, migrationConfigFileContent, migrationConfig, to, }, config) => {
@@ -128,9 +129,10 @@ export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate,
128
129
  if (storyblokConfig.debug) {
129
130
  Logger.success(`# ${index} #`);
130
131
  }
131
- const json = itemType === "story" ? item[itemType].content : item[itemType];
132
+ let json = itemType === "story" ? item[itemType].content : item[itemType];
133
+ const rootWrapper = { root: json };
132
134
  const maxDepth = replaceComponentData({
133
- parent: { root: json },
135
+ parent: rootWrapper,
134
136
  key: "root",
135
137
  components: componentsToMigrate,
136
138
  mapper: migrationConfigFileContent,
@@ -138,6 +140,8 @@ export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate,
138
140
  maxDepth: 0,
139
141
  sumOfReplacing,
140
142
  });
143
+ // After replacement, take potentially new root (root may be reassigned inside)
144
+ json = rootWrapper.root;
141
145
  arrayOfMaxDepths.push(maxDepth);
142
146
  if (Object.keys(sumOfReplacing).length > 0) {
143
147
  console.log(" ");
@@ -220,14 +224,17 @@ const saveBackupToFile = async ({ itemType, res, folder, filename }, config) =>
220
224
  res: res,
221
225
  }, config);
222
226
  };
223
- export const migrateProvidedComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, }, config) => {
227
+ export const migrateProvidedComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, }, config) => {
224
228
  const migrationConfigFileContent = prepareMigrationConfig({
225
229
  migrationConfig,
226
230
  });
227
231
  if (migrateFrom === "file") {
228
232
  Logger.log("Migrating using file....");
229
233
  // Get all stories to be migrated from file
230
- const itemsToMigrate = prepareStoriesFromLocalFile({ from });
234
+ const itemsFromFile = prepareStoriesFromLocalFile({ from });
235
+ const itemsToMigrate = Array.isArray(itemsFromFile)
236
+ ? itemsFromFile.filter((it) => !(it?.story?.is_folder === true))
237
+ : itemsFromFile;
231
238
  await doTheMigration({
232
239
  itemsToMigrate,
233
240
  componentsToMigrate,
@@ -239,10 +246,27 @@ export const migrateProvidedComponentsDataInStories = async ({ itemType, migrati
239
246
  // Get all stories to be migrated from storyblok space
240
247
  let itemsToMigrate = [];
241
248
  if (itemType === "story") {
242
- itemsToMigrate = await managementApi.stories.getAllStories({}, {
243
- ...config,
244
- spaceId: from,
245
- });
249
+ if (filters?.withSlug && filters.withSlug.length > 0) {
250
+ const results = await Promise.all(filters.withSlug.map((slug) => managementApi.stories.getStoryBySlug(slug, {
251
+ ...config,
252
+ spaceId: from,
253
+ })));
254
+ itemsToMigrate = results.filter(Boolean).filter((it) => !(it?.story?.is_folder === true));
255
+ }
256
+ else if (filters?.startsWith) {
257
+ itemsToMigrate = await managementApi.stories.getAllStories({ options: { starts_with: filters.startsWith } }, {
258
+ ...config,
259
+ spaceId: from,
260
+ });
261
+ itemsToMigrate = itemsToMigrate.filter((it) => !(it?.story?.is_folder === true));
262
+ }
263
+ else {
264
+ itemsToMigrate = await managementApi.stories.getAllStories({}, {
265
+ ...config,
266
+ spaceId: from,
267
+ });
268
+ itemsToMigrate = itemsToMigrate.filter((it) => !(it?.story?.is_folder === true));
269
+ }
246
270
  }
247
271
  else if (itemType === "preset") {
248
272
  itemsToMigrate = await managementApi.presets.getAllPresets({
@@ -1,7 +1,6 @@
1
1
  import chalk from "chalk";
2
- import storyblokConfig from "../../config/config.js";
3
2
  import Logger from "../../utils/logger.js";
4
- import { isObjectEmpty } from "../../utils/main.js";
3
+ import { isObjectEmpty } from "../../utils/object-utils.js";
5
4
  import { getDatasource } from "./datasources.js";
6
5
  const _decorateWithDimensions = async (args, config) => {
7
6
  const { currentDatasource, dimensionsData, _callback } = args;
@@ -67,7 +66,7 @@ export const createDatasourceEntries = (args, config) => {
67
66
  const _createDatasourceEntry = (args, config) => {
68
67
  const { currentDatasource, finalDatasource_entry } = args;
69
68
  const { spaceId, sbApi } = config;
70
- if (storyblokConfig.debug) {
69
+ if (config.debug) {
71
70
  console.log("############# Entity to Create: ");
72
71
  console.log(finalDatasource_entry);
73
72
  console.log("################################");
@@ -81,7 +80,7 @@ const _createDatasourceEntry = (args, config) => {
81
80
  return data;
82
81
  })
83
82
  .catch((err) => {
84
- if (storyblokConfig.debug) {
83
+ if (config.debug) {
85
84
  console.log("Full Create error: ");
86
85
  console.log(err);
87
86
  }
@@ -122,7 +121,7 @@ const _updateDatasourceEntry = (args, config) => {
122
121
  return true;
123
122
  })
124
123
  .catch((err) => {
125
- if (storyblokConfig.debug) {
124
+ if (config.debug) {
126
125
  console.log("Full update error: ");
127
126
  console.log(err);
128
127
  }
@@ -1,6 +1,9 @@
1
- import type { CreateDatasource, GetAllDatasources, GetDatasource, SyncDatasources, UpdateDatasource } from "./datasources.types.js";
1
+ import type { CreateDatasource, GetAllDatasources, GetDatasource, UpdateDatasource } from "./datasources.types.js";
2
+ import type { SyncResult } from "../sync/sync.types.js";
2
3
  export declare const getAllDatasources: GetAllDatasources;
3
4
  export declare const getDatasource: GetDatasource;
4
5
  export declare const createDatasource: CreateDatasource;
5
6
  export declare const updateDatasource: UpdateDatasource;
6
- export declare const syncDatasources: SyncDatasources;
7
+ export declare const syncDatasourcesData: ({ datasources }: {
8
+ datasources: any[];
9
+ }, config: any) => Promise<SyncResult>;
@@ -1,5 +1,4 @@
1
1
  import Logger from "../../utils/logger.js";
2
- import { getFileContentWithRequire, } from "../../utils/main.js";
3
2
  import { getAllItemsWithPagination } from "../utils/request.js";
4
3
  import { createDatasourceEntries, getDatasourceEntries, } from "./datasource-entries.js";
5
4
  // GET
@@ -110,39 +109,47 @@ export const updateDatasource = (args, config) => {
110
109
  })
111
110
  .catch((err) => Logger.error(err));
112
111
  };
113
- export const syncDatasources = async (args, config) => {
114
- const { providedDatasources } = args;
115
- Logger.log(`Trying to sync provided datasources: `);
116
- const providedDatasourcesContent = await Promise.all(providedDatasources.map((datasource) => {
117
- return getFileContentWithRequire({ file: datasource.p });
118
- }));
119
- const remoteDatasources = await getAllDatasources(config);
120
- Promise.all(providedDatasourcesContent.map((datasource) => {
121
- const datasourceToBeUpdated = remoteDatasources.find((remoteDatasource) => datasource.name === remoteDatasource.name);
122
- if (datasourceToBeUpdated) {
123
- return updateDatasource({ datasource: datasource, datasourceToBeUpdated }, config);
112
+ // File-based sync wrapper lives in `datasources.sync.ts` to keep this module CJS-safe.
113
+ export const syncDatasourcesData = async ({ datasources }, config) => {
114
+ const result = {
115
+ created: [],
116
+ updated: [],
117
+ skipped: [],
118
+ errors: [],
119
+ };
120
+ const remoteDatasourcesRaw = await getAllDatasources(config);
121
+ const remoteDatasources = Array.isArray(remoteDatasourcesRaw)
122
+ ? remoteDatasourcesRaw
123
+ : [];
124
+ for (const datasource of datasources) {
125
+ const name = String(datasource?.name ?? "unknown");
126
+ if (!datasource || typeof datasource !== "object" || !datasource.name) {
127
+ result.skipped.push(name);
128
+ continue;
124
129
  }
125
- return createDatasource({ datasource: datasource }, config);
126
- }))
127
- .then((res) => {
128
- // After create or after update datasource
129
- res.map(async ({ data, datasource_entries }) => {
130
- const remoteDatasourceEntries = await getDatasourceEntries({
131
- datasourceName: data.datasource.name,
132
- }, config);
133
- console.log(" ");
134
- Logger.warning(`Start async syncing of '${data.datasource.name}' datasource entries.`);
135
- createDatasourceEntries({
136
- data,
137
- datasource_entries,
138
- remoteDatasourceEntries,
139
- }, config);
140
- });
141
- return res;
142
- })
143
- .catch((err) => {
144
- console.log(err);
145
- Logger.warning("There is error inside promise.all from datasource");
146
- return false;
147
- });
130
+ try {
131
+ const datasourceToBeUpdated = remoteDatasources.find((remoteDatasource) => datasource.name === remoteDatasource.name);
132
+ const opResult = datasourceToBeUpdated
133
+ ? await updateDatasource({ datasource, datasourceToBeUpdated }, config)
134
+ : await createDatasource({ datasource }, config);
135
+ if (datasourceToBeUpdated)
136
+ result.updated.push(name);
137
+ else
138
+ result.created.push(name);
139
+ if (opResult?.data?.datasource && opResult?.datasource_entries) {
140
+ const remoteDatasourceEntries = await getDatasourceEntries({
141
+ datasourceName: opResult.data.datasource.name,
142
+ }, config);
143
+ await createDatasourceEntries({
144
+ data: opResult.data,
145
+ datasource_entries: opResult.datasource_entries,
146
+ remoteDatasourceEntries,
147
+ }, config);
148
+ }
149
+ }
150
+ catch (e) {
151
+ result.errors.push({ name, message: String(e) });
152
+ }
153
+ }
154
+ return result;
148
155
  };
@@ -0,0 +1,2 @@
1
+ import type { SyncDatasources } from "./datasources.types.js";
2
+ export declare const syncDatasources: SyncDatasources;
@@ -0,0 +1,11 @@
1
+ import { getFileContentWithRequire } from "../../utils/files.js";
2
+ import Logger from "../../utils/logger.js";
3
+ import { syncDatasourcesData } from "./datasources.js";
4
+ export const syncDatasources = async (args, config) => {
5
+ const { providedDatasources } = args;
6
+ Logger.log(`Trying to sync provided datasources: `);
7
+ const providedDatasourcesContent = await Promise.all(providedDatasources.map((datasource) => {
8
+ return getFileContentWithRequire({ file: datasource.p });
9
+ }));
10
+ await syncDatasourcesData({ datasources: providedDatasourcesContent }, config);
11
+ };
@@ -1,4 +1,4 @@
1
- import type { OneFileElement } from "../../cli/utils/discover.js";
1
+ import type { OneFileElement } from "../../utils/path-utils.js";
2
2
  import type { RequestBaseConfig } from "../utils/request.js";
3
3
  export type GetAllDatasources = (config: RequestBaseConfig) => Promise<any>;
4
4
  export type GetDatasource = (args: {
@@ -1 +1,2 @@
1
- export { createDatasource, getDatasource, getAllDatasources, syncDatasources, updateDatasource, } from "./datasources.js";
1
+ export { createDatasource, getDatasource, getAllDatasources, updateDatasource, } from "./datasources.js";
2
+ export { syncDatasources } from "./datasources.sync.js";
@@ -1 +1,2 @@
1
- export { createDatasource, getDatasource, getAllDatasources, syncDatasources, updateDatasource, } from "./datasources.js";
1
+ export { createDatasource, getDatasource, getAllDatasources, updateDatasource, } from "./datasources.js";
2
+ export { syncDatasources } from "./datasources.sync.js";