sb-mig 5.7.0-beta.1 → 5.7.0-beta.3

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 (33) hide show
  1. package/dist/api/components/components.sync.d.ts +1 -0
  2. package/dist/api/components/components.sync.js +50 -8
  3. package/dist/api/datasources/datasource-entries.js +15 -3
  4. package/dist/api/datasources/datasources.d.ts +2 -1
  5. package/dist/api/datasources/datasources.js +30 -1
  6. package/dist/api/datasources/datasources.sync.js +2 -2
  7. package/dist/api/datasources/datasources.types.d.ts +6 -1
  8. package/dist/api/migrate.js +50 -12
  9. package/dist/api/migrate.types.d.ts +14 -6
  10. package/dist/api/plugins/plugins.d.ts +2 -1
  11. package/dist/api/plugins/plugins.js +19 -1
  12. package/dist/api/plugins/plugins.sync.js +8 -4
  13. package/dist/api/plugins/plugins.types.d.ts +1 -0
  14. package/dist/api/roles/roles.d.ts +2 -1
  15. package/dist/api/roles/roles.js +16 -3
  16. package/dist/api/roles/roles.sync.js +2 -2
  17. package/dist/api/roles/roles.types.d.ts +6 -3
  18. package/dist/api/stories/stories.js +29 -1
  19. package/dist/api/sync/sync.types.d.ts +3 -0
  20. package/dist/api-v2/sync/index.js +4 -89
  21. package/dist/cli/cli-descriptions.d.ts +1 -1
  22. package/dist/cli/cli-descriptions.js +4 -0
  23. package/dist/cli/commands/sync.js +109 -64
  24. package/dist/cli/datasources/sync.js +5 -2
  25. package/dist/cli/roles/sync.js +4 -4
  26. package/dist-cjs/api/components/components.sync.js +50 -8
  27. package/dist-cjs/api/datasources/datasource-entries.js +15 -3
  28. package/dist-cjs/api/datasources/datasources.js +30 -1
  29. package/dist-cjs/api/plugins/plugins.js +19 -1
  30. package/dist-cjs/api/roles/roles.js +16 -3
  31. package/dist-cjs/api/stories/stories.js +29 -1
  32. package/dist-cjs/api-v2/sync/index.js +4 -89
  33. package/package.json +1 -1
@@ -4,5 +4,6 @@ export declare function syncComponentsData(args: {
4
4
  components: any[];
5
5
  presets: boolean;
6
6
  ssot?: boolean;
7
+ dryRun?: boolean;
7
8
  onProgress?: SyncProgressCallback;
8
9
  }, config: RequestBaseConfig): Promise<SyncResult>;
@@ -27,12 +27,16 @@ const defaultProgress = (event) => {
27
27
  }
28
28
  };
29
29
  import { createComponent, createComponentsGroup, getAllComponents, getAllComponentsGroups, removeComponent, removeComponentGroup, updateComponent, } from "./components.js";
30
- async function ensureComponentGroupsExist(groupNames, config) {
30
+ async function ensureComponentGroupsExist(groupNames, config, options = {}) {
31
31
  try {
32
32
  const existing = await getAllComponentsGroups(config);
33
33
  const existingNames = new Set((existing ?? []).map((g) => g.name));
34
34
  for (const groupName of groupNames) {
35
35
  if (!existingNames.has(groupName)) {
36
+ if (options.dryRun) {
37
+ Logger.warning(`[dry-run] Would create component group '${groupName}'.`);
38
+ continue;
39
+ }
36
40
  await createComponentsGroup(groupName, config);
37
41
  }
38
42
  }
@@ -52,7 +56,7 @@ function resolveGroupUuid(component, remoteGroups) {
52
56
  return { ...component, component_group_uuid: match.uuid };
53
57
  }
54
58
  export async function syncComponentsData(args, config) {
55
- const { components, presets, ssot, onProgress } = args;
59
+ const { components, presets, ssot, dryRun, onProgress } = args;
56
60
  const progress = onProgress ?? defaultProgress;
57
61
  const result = {
58
62
  created: [],
@@ -60,23 +64,37 @@ export async function syncComponentsData(args, config) {
60
64
  skipped: [],
61
65
  errors: [],
62
66
  };
67
+ if (dryRun) {
68
+ Logger.warning("[dry-run] Component sync will only read remote data and report planned changes.");
69
+ }
63
70
  if (ssot) {
64
71
  const existingComponents = await getAllComponents(config);
65
72
  const existingGroups = await getAllComponentsGroups(config);
66
- await Promise.allSettled([
67
- ...(existingComponents ?? []).map((c) => removeComponent(c, config)),
68
- ...(existingGroups ?? []).map((g) => removeComponentGroup(g, config)),
69
- ]);
73
+ if (dryRun) {
74
+ for (const component of existingComponents ?? []) {
75
+ Logger.warning(`[dry-run] Would remove component '${component.name}'.`);
76
+ }
77
+ for (const group of existingGroups ?? []) {
78
+ Logger.warning(`[dry-run] Would remove component group '${group.name}'.`);
79
+ }
80
+ }
81
+ else {
82
+ await Promise.allSettled([
83
+ ...(existingComponents ?? []).map((c) => removeComponent(c, config)),
84
+ ...(existingGroups ?? []).map((g) => removeComponentGroup(g, config)),
85
+ ]);
86
+ }
70
87
  }
71
88
  const nonEmptyComponents = components.filter((c) => !isObjectEmpty(c));
72
89
  const groupsToCheck = uniqueValuesFrom(nonEmptyComponents
73
90
  .filter((c) => c.component_group_name)
74
91
  .map((c) => c.component_group_name));
75
- await ensureComponentGroupsExist(groupsToCheck, config);
92
+ await ensureComponentGroupsExist(groupsToCheck, config, { dryRun });
76
93
  let remoteComponents = [];
77
94
  let remoteGroups = [];
78
95
  try {
79
- remoteComponents = (await getAllComponents(config)) ?? [];
96
+ remoteComponents =
97
+ ssot && dryRun ? [] : ((await getAllComponents(config)) ?? []);
80
98
  }
81
99
  catch (error) {
82
100
  Logger.warning(`Could not fetch remote components: ${error instanceof Error ? error.message : String(error)}`);
@@ -121,6 +139,18 @@ export async function syncComponentsData(args, config) {
121
139
  action: "updating",
122
140
  });
123
141
  try {
142
+ if (dryRun) {
143
+ Logger.warning(`[dry-run] Would update component '${name}'.`);
144
+ result.updated.push(name);
145
+ progress({
146
+ type: "progress",
147
+ current: currentIndex,
148
+ total: totalComponents,
149
+ name,
150
+ action: "updated",
151
+ });
152
+ continue;
153
+ }
124
154
  await updateComponent(component, presets, config);
125
155
  result.updated.push(name);
126
156
  progress({
@@ -158,6 +188,18 @@ export async function syncComponentsData(args, config) {
158
188
  action: "creating",
159
189
  });
160
190
  try {
191
+ if (dryRun) {
192
+ Logger.warning(`[dry-run] Would create component '${name}'.`);
193
+ result.created.push(name);
194
+ progress({
195
+ type: "progress",
196
+ current: currentIndex,
197
+ total: totalComponents,
198
+ name,
199
+ action: "created",
200
+ });
201
+ continue;
202
+ }
161
203
  await createComponent(component, presets, config);
162
204
  result.created.push(name);
163
205
  progress({
@@ -48,16 +48,28 @@ export const getDatasourceEntries = async (args, config) => {
48
48
  }
49
49
  };
50
50
  export const createDatasourceEntries = (args, config) => {
51
- const { datasource_entries, remoteDatasourceEntries, data } = args;
51
+ const { datasource_entries, remoteDatasourceEntries, data, dryRun } = args;
52
+ const remoteEntries = Array.isArray(remoteDatasourceEntries?.datasource_entries)
53
+ ? remoteDatasourceEntries.datasource_entries
54
+ : [];
52
55
  return Promise.all(datasource_entries.map((datasourceEntry) => {
53
- const datasourceToBeUpdated = remoteDatasourceEntries.datasource_entries.find((remoteDatasourceEntry) => remoteDatasourceEntry.name ===
54
- Object.values(datasourceEntry)[0]);
56
+ const datasourceEntryName = datasourceEntry.name ?? Object.values(datasourceEntry)[0];
57
+ const datasourceToBeUpdated = remoteEntries.find((remoteDatasourceEntry) => remoteDatasourceEntry.name === datasourceEntryName);
58
+ if (dryRun) {
59
+ const action = datasourceToBeUpdated ? "update" : "create";
60
+ Logger.warning(`[dry-run] Would ${action} datasource entry '${datasourceEntryName}' in '${data.datasource.name}' datasource.`);
61
+ return data;
62
+ }
55
63
  if (datasourceToBeUpdated) {
56
64
  return updateDatasourceEntry({ data, datasourceEntry, datasourceToBeUpdated }, config);
57
65
  }
58
66
  return createDatasourceEntry({ data, datasourceEntry }, config);
59
67
  }))
60
68
  .then((_) => {
69
+ if (dryRun) {
70
+ Logger.warning(`[dry-run] Datasource entries for ${data.datasource.id} datasource id were planned without API writes.`);
71
+ return data;
72
+ }
61
73
  Logger.success(`Datasource entries for ${data.datasource.id} datasource id has been successfully synced.`);
62
74
  return data;
63
75
  })
@@ -4,6 +4,7 @@ export declare const getAllDatasources: GetAllDatasources;
4
4
  export declare const getDatasource: GetDatasource;
5
5
  export declare const createDatasource: CreateDatasource;
6
6
  export declare const updateDatasource: UpdateDatasource;
7
- export declare const syncDatasourcesData: ({ datasources }: {
7
+ export declare const syncDatasourcesData: ({ datasources, dryRun }: {
8
8
  datasources: any[];
9
+ dryRun?: boolean;
9
10
  }, config: any) => Promise<SyncResult>;
@@ -110,13 +110,16 @@ export const updateDatasource = (args, config) => {
110
110
  .catch((err) => Logger.error(err));
111
111
  };
112
112
  // File-based sync wrapper lives in `datasources.sync.ts` to keep this module CJS-safe.
113
- export const syncDatasourcesData = async ({ datasources }, config) => {
113
+ export const syncDatasourcesData = async ({ datasources, dryRun }, config) => {
114
114
  const result = {
115
115
  created: [],
116
116
  updated: [],
117
117
  skipped: [],
118
118
  errors: [],
119
119
  };
120
+ if (dryRun) {
121
+ Logger.warning("[dry-run] Datasource sync will only read remote data and report planned changes.");
122
+ }
120
123
  const remoteDatasourcesRaw = await getAllDatasources(config);
121
124
  const remoteDatasources = Array.isArray(remoteDatasourcesRaw)
122
125
  ? remoteDatasourcesRaw
@@ -129,6 +132,32 @@ export const syncDatasourcesData = async ({ datasources }, config) => {
129
132
  }
130
133
  try {
131
134
  const datasourceToBeUpdated = remoteDatasources.find((remoteDatasource) => datasource.name === remoteDatasource.name);
135
+ if (dryRun) {
136
+ if (datasourceToBeUpdated) {
137
+ result.updated.push(name);
138
+ Logger.warning(`[dry-run] Would update datasource '${name}'.`);
139
+ const remoteDatasourceEntries = await getDatasourceEntries({
140
+ datasourceName: name,
141
+ }, config);
142
+ await createDatasourceEntries({
143
+ data: { datasource: datasourceToBeUpdated },
144
+ datasource_entries: datasource.datasource_entries ?? [],
145
+ remoteDatasourceEntries,
146
+ dryRun,
147
+ }, config);
148
+ }
149
+ else {
150
+ const entriesCount = Array.isArray(datasource.datasource_entries)
151
+ ? datasource.datasource_entries.length
152
+ : 0;
153
+ result.created.push(name);
154
+ Logger.warning(`[dry-run] Would create datasource '${name}'.`);
155
+ if (entriesCount > 0) {
156
+ Logger.warning(`[dry-run] Would create ${entriesCount} datasource entries for '${name}' after datasource creation.`);
157
+ }
158
+ }
159
+ continue;
160
+ }
132
161
  const opResult = datasourceToBeUpdated
133
162
  ? await updateDatasource({ datasource, datasourceToBeUpdated }, config)
134
163
  : await createDatasource({ datasource }, config);
@@ -2,10 +2,10 @@ import { getFileContentWithRequire } from "../../utils/files.js";
2
2
  import Logger from "../../utils/logger.js";
3
3
  import { syncDatasourcesData } from "./datasources.js";
4
4
  export const syncDatasources = async (args, config) => {
5
- const { providedDatasources } = args;
5
+ const { providedDatasources, dryRun } = args;
6
6
  Logger.log(`Trying to sync provided datasources: `);
7
7
  const providedDatasourcesContent = await Promise.all(providedDatasources.map((datasource) => {
8
8
  return getFileContentWithRequire({ file: datasource.p });
9
9
  }));
10
- await syncDatasourcesData({ datasources: providedDatasourcesContent }, config);
10
+ await syncDatasourcesData({ datasources: providedDatasourcesContent, dryRun }, config);
11
11
  };
@@ -13,11 +13,15 @@ export type UpdateDatasource = (args: {
13
13
  }, config: RequestBaseConfig) => Promise<any>;
14
14
  export type SyncDatasources = (args: {
15
15
  providedDatasources: OneFileElement[];
16
+ dryRun?: boolean;
16
17
  }, config: RequestBaseConfig) => Promise<any>;
17
18
  export type SyncProvidedDatasources = (args: {
18
19
  datasources: string[];
20
+ dryRun?: boolean;
19
21
  }, config: RequestBaseConfig) => Promise<void>;
20
- export type SyncAllDatasources = (config: RequestBaseConfig) => Promise<void>;
22
+ export type SyncAllDatasources = (config: RequestBaseConfig, args?: {
23
+ dryRun?: boolean;
24
+ }) => Promise<void>;
21
25
  export type GetDatasourceEntries = (args: {
22
26
  datasourceName: string;
23
27
  }, config: RequestBaseConfig) => Promise<any>;
@@ -25,6 +29,7 @@ export type CreateDatasourceEntries = (args: {
25
29
  data: any;
26
30
  datasource_entries: any;
27
31
  remoteDatasourceEntries: any;
32
+ dryRun?: boolean;
28
33
  }, config: RequestBaseConfig) => Promise<any> | void;
29
34
  export type CreateDatasourceEntry = (args: {
30
35
  data: any;
@@ -53,7 +53,7 @@ const _resolveGroups = async (component, existedGroups, remoteComponentsGroups)
53
53
  return { ...component, component_group_uuid };
54
54
  }
55
55
  };
56
- export const syncComponents = async (specifiedComponents, presets, config) => {
56
+ export const syncComponents = async (specifiedComponents, presets, config, options = {}) => {
57
57
  Logger.log("sync2Components: ");
58
58
  let specifiedComponentsContent = await Promise.all(specifiedComponents.map((component) => {
59
59
  return getFileContentWithRequire({ file: component.p });
@@ -64,9 +64,14 @@ export const syncComponents = async (specifiedComponents, presets, config) => {
64
64
  * - .sb.resolvers.ts files resolvers
65
65
  */
66
66
  specifiedComponentsContent = await resolveGlobalTransformations(specifiedComponentsContent);
67
- await syncComponentsData({ components: specifiedComponentsContent, presets }, config);
67
+ await syncComponentsData({
68
+ components: specifiedComponentsContent,
69
+ presets,
70
+ ssot: options.ssot,
71
+ dryRun: options.dryRun,
72
+ }, config);
68
73
  };
69
- export const syncAllComponents = async (presets, config) => {
74
+ export const syncAllComponents = async (presets, config, options = {}) => {
70
75
  // #1: discover all external .sb.js files
71
76
  const allLocalSbComponentsSchemaFiles = await discover({
72
77
  scope: SCOPE.local,
@@ -83,9 +88,12 @@ export const syncAllComponents = async (presets, config) => {
83
88
  external: allExternalSbComponentsSchemaFiles,
84
89
  });
85
90
  // #4: sync - do all stuff already done (groups resolving, and so on)
86
- return await syncComponents([...local, ...external], presets, config);
91
+ return await syncComponents([...local, ...external], presets, config, {
92
+ dryRun: options.dryRun,
93
+ ssot: options.ssot,
94
+ });
87
95
  };
88
- export const syncProvidedComponents = async (presets, components, packageName, config) => {
96
+ export const syncProvidedComponents = async (presets, components, packageName, config, options = {}) => {
89
97
  if (!packageName) {
90
98
  // #1: discover all external .sb.js files
91
99
  const allLocalSbComponentsSchemaFiles = await discoverMany({
@@ -105,7 +113,9 @@ export const syncProvidedComponents = async (presets, components, packageName, c
105
113
  external: allExternalSbComponentsSchemaFiles,
106
114
  });
107
115
  // #4: sync - do all stuff already done (groups resolving, and so on)
108
- return await syncComponents([...local, ...external], presets, config);
116
+ return await syncComponents([...local, ...external], presets, config, {
117
+ dryRun: options.dryRun,
118
+ });
109
119
  }
110
120
  else {
111
121
  // implement discovering and syncrhonizing with packageName
@@ -125,13 +135,20 @@ export const syncProvidedComponents = async (presets, components, packageName, c
125
135
  external: allExternalSbComponentsSchemaFiles,
126
136
  });
127
137
  // #4: sync - do all stuff already done (groups resolving, and so on)
128
- return syncComponents([...local, ...external], presets, config);
138
+ return syncComponents([...local, ...external], presets, config, {
139
+ dryRun: options.dryRun,
140
+ });
129
141
  }
130
142
  };
131
- export const syncAssets = async ({ transmission: { from, to }, syncDirection }, config) => {
143
+ export const syncAssets = async ({ transmission: { from, to }, syncDirection, dryRun }, config) => {
132
144
  Logger.log(`We would try to migrate Assets data from: ${from} to: ${to}`);
133
145
  const allAssets = await getAllAssets({ spaceId: from }, config);
134
- await Promise.all(allAssets.assets.map((asset) => {
146
+ const assets = Array.isArray(allAssets?.assets) ? allAssets.assets : [];
147
+ if (dryRun) {
148
+ Logger.warning(`[dry-run] Would sync ${assets.length} assets from ${from} to ${to}.`);
149
+ return true;
150
+ }
151
+ await Promise.all(assets.map((asset) => {
135
152
  const { id, created_at, updated_at, ...newAssetPayload } = asset;
136
153
  return migrateAsset({
137
154
  migrateTo: to,
@@ -139,13 +156,18 @@ export const syncAssets = async ({ transmission: { from, to }, syncDirection },
139
156
  syncDirection,
140
157
  }, config);
141
158
  }));
159
+ return true;
142
160
  };
143
- const syncStories = async ({ transmission: { from, to }, stories, toSpaceId }, config) => {
161
+ const syncStories = async ({ transmission: { from, to }, stories, toSpaceId, dryRun }, config) => {
144
162
  Logger.log(`We would try to migrate Stories data from: ${from} to: ${to}`);
145
163
  const storiesToPass = stories
146
164
  .map((item) => item.story)
147
165
  .map((item) => item.parent_id === 0 ? { ...item, parent_id: null } : item);
148
166
  Logger.warning(`Amount of all stories to migrate: ${storiesToPass.length}`);
167
+ if (dryRun) {
168
+ Logger.warning(`[dry-run] Would sync ${storiesToPass.length} stories from ${from} to ${to}.`);
169
+ return true;
170
+ }
149
171
  const storiesToPassJson = JSON.stringify(storiesToPass, null, 2);
150
172
  if (config.debug) {
151
173
  dumpToFile("storiesToPass.json", storiesToPassJson);
@@ -156,10 +178,23 @@ const syncStories = async ({ transmission: { from, to }, stories, toSpaceId }, c
156
178
  dumpToFile("tree.json", jsonString);
157
179
  }
158
180
  await traverseAndCreate({ tree, realParentId: null, spaceId: toSpaceId }, config);
181
+ return true;
159
182
  };
160
- export const syncContent = async ({ type, transmission, syncDirection, filename }, config) => {
183
+ export const syncContent = async ({ type, transmission, syncDirection, filename, dryRun }, config) => {
184
+ if (dryRun) {
185
+ Logger.warning("[dry-run] Content sync will only read source data and report planned changes.");
186
+ }
161
187
  if (type === "stories") {
162
188
  if (syncDirection === "fromSpaceToFile") {
189
+ if (dryRun) {
190
+ const stories = await getAllStories({}, {
191
+ ...config,
192
+ spaceId: transmission.from,
193
+ sbApi: config.sbApi,
194
+ });
195
+ Logger.warning(`[dry-run] Would back up ${stories.length} stories from ${transmission.from} to '${transmission.to}'.`);
196
+ return true;
197
+ }
163
198
  await backupStories({
164
199
  filename: transmission.to,
165
200
  suffix: ".sb.stories",
@@ -176,6 +211,7 @@ export const syncContent = async ({ type, transmission, syncDirection, filename
176
211
  transmission,
177
212
  stories,
178
213
  toSpaceId: transmission.to,
214
+ dryRun,
179
215
  }, config);
180
216
  }
181
217
  if (syncDirection === "fromFileToSpace") {
@@ -191,6 +227,7 @@ export const syncContent = async ({ type, transmission, syncDirection, filename
191
227
  transmission,
192
228
  stories: storiesFileContent[0],
193
229
  toSpaceId: transmission.to,
230
+ dryRun,
194
231
  }, config);
195
232
  }
196
233
  if (syncDirection === "fromAWSToSpace") {
@@ -199,6 +236,7 @@ export const syncContent = async ({ type, transmission, syncDirection, filename
199
236
  transmission,
200
237
  stories: data.stories,
201
238
  toSpaceId: transmission.to,
239
+ dryRun,
202
240
  }, config);
203
241
  }
204
242
  return true;
@@ -209,7 +247,7 @@ export const syncContent = async ({ type, transmission, syncDirection, filename
209
247
  }
210
248
  else if (syncDirection === "fromSpaceToSpace" ||
211
249
  syncDirection === "fromSpaceToFile") {
212
- await syncAssets({ transmission, syncDirection }, config);
250
+ await syncAssets({ transmission, syncDirection, dryRun }, config);
213
251
  }
214
252
  else {
215
253
  Logger.warning(`${syncDirection} with ${type} This is not implemented yet!`);
@@ -1,13 +1,19 @@
1
1
  import type { RequestBaseConfig } from "./utils/request.js";
2
2
  import type { SyncDirection } from "../cli/sync.types.js";
3
+ import type { SyncOptions } from "./sync/sync.types.js";
3
4
  import type { OneFileElement } from "../cli/utils/discover.js";
4
- export type SyncComponents = (specifiedComponents: OneFileElement[], presets: boolean, config: RequestBaseConfig) => Promise<any>;
5
- export type SyncAllComponents = (presets: boolean, config: RequestBaseConfig) => Promise<any>;
6
- export type SyncProvidedComponents = (presets: boolean, components: string[], packageName: boolean, config: RequestBaseConfig) => Promise<any>;
7
- export type SyncStories = ({ transmission, stories, toSpaceId, }: {
5
+ export type SyncComponents = (specifiedComponents: OneFileElement[], presets: boolean, config: RequestBaseConfig, options?: SyncOptions & {
6
+ ssot?: boolean;
7
+ }) => Promise<any>;
8
+ export type SyncAllComponents = (presets: boolean, config: RequestBaseConfig, options?: SyncOptions & {
9
+ ssot?: boolean;
10
+ }) => Promise<any>;
11
+ export type SyncProvidedComponents = (presets: boolean, components: string[], packageName: boolean, config: RequestBaseConfig, options?: SyncOptions) => Promise<any>;
12
+ export type SyncStories = ({ transmission, stories, toSpaceId, dryRun, }: {
8
13
  transmission: SyncContent["transmission"];
9
14
  stories: any[];
10
15
  toSpaceId: string;
16
+ dryRun?: boolean;
11
17
  }, config: RequestBaseConfig) => Promise<any>;
12
18
  export interface SyncContent {
13
19
  type: "stories" | "assets";
@@ -17,9 +23,11 @@ export interface SyncContent {
17
23
  };
18
24
  syncDirection: SyncDirection;
19
25
  filename?: string;
26
+ dryRun?: boolean;
20
27
  }
21
- export type SyncContentFunction = ({ type, transmission, syncDirection, filename }: SyncContent, config: RequestBaseConfig) => Promise<any>;
22
- export type SyncAssets = ({ transmission, }: {
28
+ export type SyncContentFunction = ({ type, transmission, syncDirection, filename, dryRun }: SyncContent, config: RequestBaseConfig) => Promise<any>;
29
+ export type SyncAssets = ({ transmission, dryRun, }: {
23
30
  transmission: SyncContent["transmission"];
24
31
  syncDirection: SyncDirection;
32
+ dryRun?: boolean;
25
33
  }, config: RequestBaseConfig) => Promise<any>;
@@ -5,9 +5,10 @@ export declare const getPlugin: GetPlugin;
5
5
  export declare const getPluginDetails: GetPluginDetails;
6
6
  export declare const updatePlugin: UpdatePlugin;
7
7
  export declare const createPlugin: CreatePlugin;
8
- export declare const syncPluginsData: ({ plugins }: {
8
+ export declare const syncPluginsData: ({ plugins, dryRun, }: {
9
9
  plugins: {
10
10
  name: string;
11
11
  body: string;
12
12
  }[];
13
+ dryRun?: boolean;
13
14
  }, config: any) => Promise<SyncResult>;
@@ -87,13 +87,17 @@ export const createPlugin = (pluginName, config) => {
87
87
  });
88
88
  };
89
89
  // File-based sync wrapper lives in `plugins.sync.ts` to keep this module CJS-safe.
90
- export const syncPluginsData = async ({ plugins }, config) => {
90
+ export const syncPluginsData = async ({ plugins, dryRun, }, config) => {
91
91
  const result = {
92
92
  created: [],
93
93
  updated: [],
94
94
  skipped: [],
95
95
  errors: [],
96
96
  };
97
+ if (dryRun) {
98
+ Logger.warning("[dry-run] Plugin sync will only read remote data and report planned changes.");
99
+ }
100
+ const remotePlugins = dryRun ? await getAllPlugins(config) : [];
97
101
  for (const p of plugins) {
98
102
  const name = String(p?.name ?? "unknown");
99
103
  if (!p?.name) {
@@ -101,6 +105,20 @@ export const syncPluginsData = async ({ plugins }, config) => {
101
105
  continue;
102
106
  }
103
107
  try {
108
+ if (dryRun) {
109
+ const plugin = Array.isArray(remotePlugins)
110
+ ? remotePlugins.find((remotePlugin) => remotePlugin.name === name)
111
+ : undefined;
112
+ if (plugin) {
113
+ Logger.warning(`[dry-run] Would update plugin '${name}'.`);
114
+ result.updated.push(name);
115
+ }
116
+ else {
117
+ Logger.warning(`[dry-run] Would create plugin '${name}'.`);
118
+ result.created.push(name);
119
+ }
120
+ continue;
121
+ }
104
122
  const plugin = await getPlugin(name, config);
105
123
  if (plugin) {
106
124
  await updatePlugin({ plugin: plugin.field_type, body: p.body }, config);
@@ -1,11 +1,15 @@
1
1
  import { readFile } from "../../utils/files.js";
2
2
  import { syncPluginsData } from "./plugins.js";
3
- export const syncProvidedPlugins = async ({ plugins }, config) => {
4
- const body = await readFile("dist/export.js");
5
- if (!body) {
3
+ export const syncProvidedPlugins = async ({ plugins, dryRun }, config) => {
4
+ const body = dryRun ? "" : await readFile("dist/export.js");
5
+ if (!body && !dryRun) {
6
6
  throw new Error("Unable to read plugin bundle from dist/export.js");
7
7
  }
8
8
  await syncPluginsData({
9
- plugins: plugins.map((name) => ({ name: String(name), body })),
9
+ plugins: plugins.map((name) => ({
10
+ name: String(name),
11
+ body: body ?? "",
12
+ })),
13
+ dryRun,
10
14
  }, config);
11
15
  };
@@ -9,6 +9,7 @@ interface UpdatePluginDTO {
9
9
  }
10
10
  interface SyncProvidedPluginsDTO {
11
11
  plugins: string[];
12
+ dryRun?: boolean;
12
13
  }
13
14
  export type GetPlugin = (pluginName: string | undefined, config: RequestBaseConfig) => Promise<any>;
14
15
  export type GetAllPlugins = (config: RequestBaseConfig) => Promise<any>;
@@ -4,6 +4,7 @@ export declare const createRole: CreateRole;
4
4
  export declare const updateRole: UpdateRole;
5
5
  export declare const getAllRoles: GetAllRoles;
6
6
  export declare const getRole: GetRole;
7
- export declare const syncRolesData: ({ roles }: {
7
+ export declare const syncRolesData: ({ roles, dryRun }: {
8
8
  roles: any[];
9
+ dryRun?: boolean;
9
10
  }, config: any) => Promise<SyncResult>;
@@ -75,13 +75,16 @@ export const getRole = async (roleName, config) => {
75
75
  })
76
76
  .catch((err) => Logger.error(err));
77
77
  };
78
- export const syncRolesData = async ({ roles }, config) => {
78
+ export const syncRolesData = async ({ roles, dryRun }, config) => {
79
79
  const result = {
80
80
  created: [],
81
81
  updated: [],
82
82
  skipped: [],
83
83
  errors: [],
84
84
  };
85
+ if (dryRun) {
86
+ Logger.warning("[dry-run] Role sync will only read remote data and report planned changes.");
87
+ }
85
88
  const space_roles_raw = await getAllRoles(config);
86
89
  const space_roles = Array.isArray(space_roles_raw) ? space_roles_raw : [];
87
90
  const rolesToUpdate = [];
@@ -99,17 +102,27 @@ export const syncRolesData = async ({ roles }, config) => {
99
102
  rolesToCreate.push(role);
100
103
  }
101
104
  }
102
- const updateResults = await Promise.allSettled(rolesToUpdate.map((role) => updateRole(role, config)));
105
+ const updateResults = dryRun
106
+ ? rolesToUpdate.map(() => ({ status: "fulfilled" }))
107
+ : await Promise.allSettled(rolesToUpdate.map((role) => updateRole(role, config)));
103
108
  updateResults.forEach((r, idx) => {
104
109
  const name = String(rolesToUpdate[idx]?.role ?? "unknown");
110
+ if (dryRun) {
111
+ Logger.warning(`[dry-run] Would update role '${name}'.`);
112
+ }
105
113
  if (r.status === "fulfilled")
106
114
  result.updated.push(name);
107
115
  else
108
116
  result.errors.push({ name, message: String(r.reason) });
109
117
  });
110
- const createResults = await Promise.allSettled(rolesToCreate.map((role) => createRole(role, config)));
118
+ const createResults = dryRun
119
+ ? rolesToCreate.map(() => ({ status: "fulfilled" }))
120
+ : await Promise.allSettled(rolesToCreate.map((role) => createRole(role, config)));
111
121
  createResults.forEach((r, idx) => {
112
122
  const name = String(rolesToCreate[idx]?.role ?? "unknown");
123
+ if (dryRun) {
124
+ Logger.warning(`[dry-run] Would create role '${name}'.`);
125
+ }
113
126
  if (r.status === "fulfilled")
114
127
  result.created.push(name);
115
128
  else
@@ -1,6 +1,6 @@
1
1
  import { getFileContentWithRequire } from "../../utils/files.js";
2
2
  import { syncRolesData } from "./roles.js";
3
- export const syncRoles = async ({ specifiedRoles }, config) => {
3
+ export const syncRoles = async ({ specifiedRoles, dryRun }, config) => {
4
4
  const specifiedRolesContent = await Promise.all(specifiedRoles.map((roles) => getFileContentWithRequire({ file: roles.p })));
5
- await syncRolesData({ roles: specifiedRolesContent }, config);
5
+ await syncRolesData({ roles: specifiedRolesContent, dryRun }, config);
6
6
  };
@@ -1,13 +1,16 @@
1
1
  import type { OneFileElement } from "../../utils/path-utils.js";
2
+ import type { SyncOptions } from "../sync/sync.types.js";
2
3
  import type { RequestBaseConfig } from "../utils/request.js";
3
4
  export type GetRole = (roleName: string | undefined, config: RequestBaseConfig) => Promise<void>;
4
5
  export type GetAllRoles = (config: RequestBaseConfig) => Promise<any>;
5
6
  export type CreateRole = (role: any, config: RequestBaseConfig) => void;
6
7
  export type UpdateRole = (role: any, config: RequestBaseConfig) => void;
7
- export type SyncRoles = ({ specifiedRoles }: {
8
+ export type SyncRoles = ({ specifiedRoles, dryRun, }: {
8
9
  specifiedRoles: OneFileElement[];
10
+ dryRun?: boolean;
9
11
  }, config: RequestBaseConfig) => Promise<void>;
10
- export type SyncAllRoles = (config: RequestBaseConfig) => Promise<void>;
11
- export type SyncProvidedRoles = ({ roles }: {
12
+ export type SyncAllRoles = (config: RequestBaseConfig, options?: SyncOptions) => Promise<void>;
13
+ export type SyncProvidedRoles = ({ roles, dryRun }: {
12
14
  roles: string[];
15
+ dryRun?: boolean;
13
16
  }, config: RequestBaseConfig) => Promise<void>;