sb-mig 6.0.0-beta.4 → 6.0.0-beta.5
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/dist/api/data-migration/component-data-migration.d.ts +6 -3
- package/dist/api/data-migration/component-data-migration.js +26 -5
- package/dist/api/data-migration/migration-run-log.d.ts +11 -2
- package/dist/api/data-migration/migration-run-log.js +18 -3
- package/dist/api/data-migration/write-summary.d.ts +2 -0
- package/dist/api/managementApi.d.ts +13 -0
- package/dist/api/stories/index.d.ts +1 -1
- package/dist/api/stories/index.js +1 -1
- package/dist/api/stories/stories.d.ts +14 -1
- package/dist/api/stories/stories.js +145 -2
- package/dist/api/stories/stories.types.d.ts +2 -0
- package/dist/api/testApi.d.ts +13 -0
- package/dist/cli/cli-descriptions.d.ts +1 -1
- package/dist/cli/cli-descriptions.js +3 -0
- package/dist/cli/commands/migrate.js +15 -0
- package/dist/cli/index.js +3 -0
- package/dist-cjs/api/stories/stories.js +149 -3
- package/package.json +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { PublishLanguagesOption } from "../stories/stories.types.js";
|
|
1
2
|
import type { RequestBaseConfig } from "../utils/request.js";
|
|
2
3
|
import { type MigrationComponentAliasesByMigration, type MigrationComponentOverridesByMigration } from "./migration-component-scope.js";
|
|
3
4
|
import { type PreparedMigrationValidator } from "./migration-validation.js";
|
|
@@ -44,6 +45,7 @@ interface MigrateItems {
|
|
|
44
45
|
};
|
|
45
46
|
dryRun?: boolean;
|
|
46
47
|
publish?: boolean;
|
|
48
|
+
publishLanguages?: PublishLanguagesOption;
|
|
47
49
|
fromFilePath?: string;
|
|
48
50
|
fileName?: string;
|
|
49
51
|
preparedMigrationConfigs?: PreparedMigrationConfig[];
|
|
@@ -68,8 +70,8 @@ export declare const runMigrationPipelineInMemory: ({ itemType, itemsToMigrate,
|
|
|
68
70
|
itemsToMigrate: any[];
|
|
69
71
|
preparedMigrationConfigs: PreparedMigrationConfig[];
|
|
70
72
|
}) => MigrationPipelineResult;
|
|
71
|
-
export declare const migrateAllComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, filters, dryRun, publish, fromFilePath, fileName, migrationComponentAliases, migrationComponentOverrides, }: Omit<MigrateItems, "componentsToMigrate" | "preparedMigrationConfigs">, config: RequestBaseConfig) => Promise<void>;
|
|
72
|
-
export declare const doTheMigration: ({ itemType, from, itemsToMigrate, migrationConfig, migrationConfigs, to, dryRun, publish, migrateFrom, fromFilePath, fileName, }: {
|
|
73
|
+
export declare const migrateAllComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, filters, dryRun, publish, publishLanguages, fromFilePath, fileName, migrationComponentAliases, migrationComponentOverrides, }: Omit<MigrateItems, "componentsToMigrate" | "preparedMigrationConfigs">, config: RequestBaseConfig) => Promise<void>;
|
|
74
|
+
export declare const doTheMigration: ({ itemType, from, itemsToMigrate, migrationConfig, migrationConfigs, to, dryRun, publish, publishLanguages, migrateFrom, fromFilePath, fileName, }: {
|
|
73
75
|
itemType?: "story" | "preset";
|
|
74
76
|
from: string;
|
|
75
77
|
itemsToMigrate: any[];
|
|
@@ -78,9 +80,10 @@ export declare const doTheMigration: ({ itemType, from, itemsToMigrate, migratio
|
|
|
78
80
|
to: string;
|
|
79
81
|
dryRun?: boolean;
|
|
80
82
|
publish?: boolean;
|
|
83
|
+
publishLanguages?: PublishLanguagesOption;
|
|
81
84
|
migrateFrom: MigrateFrom;
|
|
82
85
|
fromFilePath?: string;
|
|
83
86
|
fileName?: string;
|
|
84
87
|
}, config: RequestBaseConfig) => Promise<void>;
|
|
85
|
-
export declare const migrateProvidedComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, dryRun, publish, fromFilePath, fileName, preparedMigrationConfigs, migrationComponentAliases, migrationComponentOverrides, }: MigrateItems, config: RequestBaseConfig) => Promise<void>;
|
|
88
|
+
export declare const migrateProvidedComponentsDataInStories: ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, dryRun, publish, publishLanguages, fromFilePath, fileName, preparedMigrationConfigs, migrationComponentAliases, migrationComponentOverrides, }: MigrateItems, config: RequestBaseConfig) => Promise<void>;
|
|
86
89
|
export {};
|
|
@@ -296,7 +296,7 @@ export const runMigrationPipelineInMemory = ({ itemType, itemsToMigrate, prepare
|
|
|
296
296
|
totalItems: workingItems.length,
|
|
297
297
|
};
|
|
298
298
|
};
|
|
299
|
-
const savePipelineSummary = async ({ artifactBaseName, useDatestamp, from, itemType, dryRun, publish, migrateFrom, fromFilePath, pipelineResult, }, config) => {
|
|
299
|
+
const savePipelineSummary = async ({ artifactBaseName, useDatestamp, from, itemType, dryRun, publish, publishLanguages, migrateFrom, fromFilePath, pipelineResult, }, config) => {
|
|
300
300
|
await createAndSaveToFile({
|
|
301
301
|
datestamp: useDatestamp,
|
|
302
302
|
ext: "json",
|
|
@@ -310,6 +310,11 @@ const savePipelineSummary = async ({ artifactBaseName, useDatestamp, from, itemT
|
|
|
310
310
|
fromFilePath: fromFilePath || null,
|
|
311
311
|
},
|
|
312
312
|
writeMode: itemType === "story" && publish ? "publish" : "save",
|
|
313
|
+
publishLanguages: itemType === "story" && publish
|
|
314
|
+
? {
|
|
315
|
+
requested: publishLanguages,
|
|
316
|
+
}
|
|
317
|
+
: null,
|
|
313
318
|
totalItems: pipelineResult.totalItems,
|
|
314
319
|
totalChangedItems: pipelineResult.changedItems.length,
|
|
315
320
|
steps: pipelineResult.stepReports,
|
|
@@ -380,7 +385,7 @@ const loadItemsToMigrate = async ({ itemType, migrateFrom, from, filters, fromFi
|
|
|
380
385
|
spaceId: from,
|
|
381
386
|
});
|
|
382
387
|
};
|
|
383
|
-
export const migrateAllComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, filters, dryRun, publish, fromFilePath, fileName, migrationComponentAliases, migrationComponentOverrides, }, config) => {
|
|
388
|
+
export const migrateAllComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, filters, dryRun, publish, publishLanguages, fromFilePath, fileName, migrationComponentAliases, migrationComponentOverrides, }, config) => {
|
|
384
389
|
Logger.warning(`Trying to migrate all ${itemType} from ${migrateFrom}, ${from} to ${to}...`);
|
|
385
390
|
const preparedMigrationConfigs = prepareMigrationConfigs({
|
|
386
391
|
migrationConfig,
|
|
@@ -400,12 +405,13 @@ export const migrateAllComponentsDataInStories = async ({ itemType, migrationCon
|
|
|
400
405
|
filters,
|
|
401
406
|
dryRun,
|
|
402
407
|
publish,
|
|
408
|
+
publishLanguages,
|
|
403
409
|
fromFilePath,
|
|
404
410
|
fileName,
|
|
405
411
|
preparedMigrationConfigs,
|
|
406
412
|
}, config);
|
|
407
413
|
};
|
|
408
|
-
export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate, migrationConfig, migrationConfigs, to, dryRun, publish, migrateFrom, fromFilePath, fileName, }, config) => {
|
|
414
|
+
export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate, migrationConfig, migrationConfigs, to, dryRun, publish, publishLanguages, migrateFrom, fromFilePath, fileName, }, config) => {
|
|
409
415
|
const preparedMigrationConfigs = migrationConfigs ||
|
|
410
416
|
prepareMigrationConfigs({
|
|
411
417
|
migrationConfig: migrationConfig || [],
|
|
@@ -476,6 +482,7 @@ export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate,
|
|
|
476
482
|
itemType,
|
|
477
483
|
dryRun,
|
|
478
484
|
publish,
|
|
485
|
+
publishLanguages,
|
|
479
486
|
migrateFrom,
|
|
480
487
|
fromFilePath,
|
|
481
488
|
pipelineResult,
|
|
@@ -493,11 +500,22 @@ export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate,
|
|
|
493
500
|
return;
|
|
494
501
|
}
|
|
495
502
|
let writeResults = [];
|
|
503
|
+
let resolvedPublishLanguages;
|
|
496
504
|
if (itemType === "story") {
|
|
505
|
+
if (publish && publishLanguages !== undefined) {
|
|
506
|
+
resolvedPublishLanguages =
|
|
507
|
+
await managementApi.stories.resolvePublishLanguageCodes(publishLanguages, {
|
|
508
|
+
...config,
|
|
509
|
+
spaceId: to,
|
|
510
|
+
});
|
|
511
|
+
}
|
|
497
512
|
writeResults = await managementApi.stories.updateStories({
|
|
498
513
|
stories: pipelineResult.changedItems,
|
|
499
514
|
spaceId: to,
|
|
500
|
-
options: {
|
|
515
|
+
options: {
|
|
516
|
+
publish: Boolean(publish),
|
|
517
|
+
publishLanguages: resolvedPublishLanguages,
|
|
518
|
+
},
|
|
501
519
|
}, config);
|
|
502
520
|
}
|
|
503
521
|
else if (itemType === "preset") {
|
|
@@ -517,6 +535,8 @@ export const doTheMigration = async ({ itemType = "story", from, itemsToMigrate,
|
|
|
517
535
|
itemType,
|
|
518
536
|
dryRun,
|
|
519
537
|
publish,
|
|
538
|
+
publishLanguages,
|
|
539
|
+
resolvedPublishLanguages,
|
|
520
540
|
migrateFrom,
|
|
521
541
|
fromFilePath,
|
|
522
542
|
pipelineResult,
|
|
@@ -550,7 +570,7 @@ const saveBackupToFile = async ({ itemType, res, folder, filename }, config) =>
|
|
|
550
570
|
res: res,
|
|
551
571
|
}, config);
|
|
552
572
|
};
|
|
553
|
-
export const migrateProvidedComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, dryRun, publish, fromFilePath, fileName, preparedMigrationConfigs, migrationComponentAliases, migrationComponentOverrides, }, config) => {
|
|
573
|
+
export const migrateProvidedComponentsDataInStories = async ({ itemType, migrationConfig, migrateFrom, from, to, componentsToMigrate, filters, dryRun, publish, publishLanguages, fromFilePath, fileName, preparedMigrationConfigs, migrationComponentAliases, migrationComponentOverrides, }, config) => {
|
|
554
574
|
const resolvedMigrationConfigs = preparedMigrationConfigs ||
|
|
555
575
|
prepareMigrationConfigs({
|
|
556
576
|
migrationConfig,
|
|
@@ -586,6 +606,7 @@ export const migrateProvidedComponentsDataInStories = async ({ itemType, migrati
|
|
|
586
606
|
to,
|
|
587
607
|
dryRun,
|
|
588
608
|
publish,
|
|
609
|
+
publishLanguages,
|
|
589
610
|
migrateFrom,
|
|
590
611
|
fromFilePath,
|
|
591
612
|
fileName,
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { MigrateFrom, MigrationPipelineResult } from "./component-data-migration.js";
|
|
2
2
|
import type { MutationWriteResult, MutationWriteSummary } from "./write-summary.js";
|
|
3
|
+
import type { PublishLanguagesOption } from "../stories/stories.types.js";
|
|
3
4
|
import type { RequestBaseConfig } from "../utils/request.js";
|
|
4
|
-
type MigrationRunLogEvent = "update_success" | "update_failed" | "migration_write_summary";
|
|
5
|
+
type MigrationRunLogEvent = "update_success" | "update_failed" | "publish_success" | "publish_failed" | "migration_write_summary";
|
|
5
6
|
export interface MigrationRunLogRecord {
|
|
6
7
|
timestamp: string;
|
|
7
8
|
event: MigrationRunLogEvent;
|
|
@@ -16,6 +17,10 @@ export interface MigrationRunLogRecord {
|
|
|
16
17
|
to: string;
|
|
17
18
|
};
|
|
18
19
|
writeMode: "publish" | "save";
|
|
20
|
+
publishLanguages?: {
|
|
21
|
+
requested?: PublishLanguagesOption;
|
|
22
|
+
resolved?: string[];
|
|
23
|
+
};
|
|
19
24
|
dryRun: boolean;
|
|
20
25
|
migrationConfigs: string[];
|
|
21
26
|
totalItems: number;
|
|
@@ -31,6 +36,7 @@ export interface MigrationRunLogRecord {
|
|
|
31
36
|
spaceId?: string;
|
|
32
37
|
status?: number | string;
|
|
33
38
|
response?: string | null;
|
|
39
|
+
stage?: "update" | "publish";
|
|
34
40
|
}>;
|
|
35
41
|
};
|
|
36
42
|
item?: {
|
|
@@ -42,6 +48,7 @@ export interface MigrationRunLogRecord {
|
|
|
42
48
|
};
|
|
43
49
|
status?: number | string | null;
|
|
44
50
|
response?: string | null;
|
|
51
|
+
stage?: "update" | "publish";
|
|
45
52
|
error?: unknown;
|
|
46
53
|
}
|
|
47
54
|
interface SaveMigrationRunLogArgs {
|
|
@@ -52,13 +59,15 @@ interface SaveMigrationRunLogArgs {
|
|
|
52
59
|
itemType: "story" | "preset";
|
|
53
60
|
dryRun?: boolean;
|
|
54
61
|
publish?: boolean;
|
|
62
|
+
publishLanguages?: PublishLanguagesOption;
|
|
63
|
+
resolvedPublishLanguages?: string[];
|
|
55
64
|
migrateFrom: MigrateFrom;
|
|
56
65
|
fromFilePath?: string;
|
|
57
66
|
pipelineResult: MigrationPipelineResult;
|
|
58
67
|
writeResults: PromiseSettledResult<MutationWriteResult>[];
|
|
59
68
|
writeSummary: MutationWriteSummary;
|
|
60
69
|
}
|
|
61
|
-
export declare const buildMigrationRunLogRecords: ({ from, to, itemType, dryRun, publish, migrateFrom, fromFilePath, pipelineResult, writeResults, writeSummary, }: Omit<SaveMigrationRunLogArgs, "artifactBaseName" | "useDatestamp">) => MigrationRunLogRecord[];
|
|
70
|
+
export declare const buildMigrationRunLogRecords: ({ from, to, itemType, dryRun, publish, publishLanguages, resolvedPublishLanguages, migrateFrom, fromFilePath, pipelineResult, writeResults, writeSummary, }: Omit<SaveMigrationRunLogArgs, "artifactBaseName" | "useDatestamp">) => MigrationRunLogRecord[];
|
|
62
71
|
export declare const recordsToJsonl: (records: MigrationRunLogRecord[]) => string;
|
|
63
72
|
export declare const saveMigrationRunLog: (args: SaveMigrationRunLogArgs, config: RequestBaseConfig) => Promise<void>;
|
|
64
73
|
export {};
|
|
@@ -31,7 +31,7 @@ const resolveWriteResultValue = (result) => {
|
|
|
31
31
|
error: result.reason,
|
|
32
32
|
};
|
|
33
33
|
};
|
|
34
|
-
export const buildMigrationRunLogRecords = ({ from, to, itemType, dryRun, publish, migrateFrom, fromFilePath, pipelineResult, writeResults, writeSummary, }) => {
|
|
34
|
+
export const buildMigrationRunLogRecords = ({ from, to, itemType, dryRun, publish, publishLanguages, resolvedPublishLanguages, migrateFrom, fromFilePath, pipelineResult, writeResults, writeSummary, }) => {
|
|
35
35
|
const timestamp = new Date().toISOString();
|
|
36
36
|
const runId = `${itemType}-${timestamp}`;
|
|
37
37
|
const baseRecord = {
|
|
@@ -47,6 +47,14 @@ export const buildMigrationRunLogRecords = ({ from, to, itemType, dryRun, publis
|
|
|
47
47
|
to,
|
|
48
48
|
},
|
|
49
49
|
writeMode: itemType === "story" && publish ? "publish" : "save",
|
|
50
|
+
...(publish
|
|
51
|
+
? {
|
|
52
|
+
publishLanguages: {
|
|
53
|
+
requested: publishLanguages,
|
|
54
|
+
resolved: resolvedPublishLanguages,
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
: {}),
|
|
50
58
|
dryRun: Boolean(dryRun),
|
|
51
59
|
migrationConfigs: pipelineResult.stepReports.map((step) => step.migrationConfig),
|
|
52
60
|
totalItems: pipelineResult.totalItems,
|
|
@@ -55,9 +63,14 @@ export const buildMigrationRunLogRecords = ({ from, to, itemType, dryRun, publis
|
|
|
55
63
|
const updateRecords = writeResults.map((result, index) => {
|
|
56
64
|
const value = resolveWriteResultValue(result);
|
|
57
65
|
const changedItem = resolveChangedItemPayload(pipelineResult.changedItems[index]);
|
|
66
|
+
const stage = value.stage || "update";
|
|
58
67
|
const event = value.ok
|
|
59
|
-
? "
|
|
60
|
-
|
|
68
|
+
? stage === "publish"
|
|
69
|
+
? "publish_success"
|
|
70
|
+
: "update_success"
|
|
71
|
+
: stage === "publish"
|
|
72
|
+
? "publish_failed"
|
|
73
|
+
: "update_failed";
|
|
61
74
|
return {
|
|
62
75
|
...baseRecord,
|
|
63
76
|
event,
|
|
@@ -72,6 +85,7 @@ export const buildMigrationRunLogRecords = ({ from, to, itemType, dryRun, publis
|
|
|
72
85
|
},
|
|
73
86
|
status: value.status || null,
|
|
74
87
|
response: value.response || null,
|
|
88
|
+
stage,
|
|
75
89
|
...(value.ok ? {} : { error: serializeError(value.error) }),
|
|
76
90
|
};
|
|
77
91
|
});
|
|
@@ -89,6 +103,7 @@ export const buildMigrationRunLogRecords = ({ from, to, itemType, dryRun, publis
|
|
|
89
103
|
spaceId: item.spaceId,
|
|
90
104
|
status: item.status,
|
|
91
105
|
response: item.response || null,
|
|
106
|
+
stage: item.stage,
|
|
92
107
|
})),
|
|
93
108
|
},
|
|
94
109
|
};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export interface MutationWriteResult {
|
|
2
2
|
ok: boolean;
|
|
3
|
+
stage?: "update" | "publish";
|
|
3
4
|
id?: number | string;
|
|
4
5
|
name?: string;
|
|
5
6
|
slug?: string;
|
|
6
7
|
spaceId?: string;
|
|
7
8
|
status?: number | string;
|
|
8
9
|
response?: string | null;
|
|
10
|
+
publishLanguages?: string[];
|
|
9
11
|
error?: unknown;
|
|
10
12
|
}
|
|
11
13
|
export interface MutationWriteSummary {
|
|
@@ -58,6 +58,19 @@ export declare const managementApi: {
|
|
|
58
58
|
removeStory: import("./stories/stories.types.js").RemoveStory;
|
|
59
59
|
getStoryBySlug: import("./stories/stories.types.js").GetStoryBySlug;
|
|
60
60
|
updateStories: import("./stories/stories.types.js").UpdateStories;
|
|
61
|
+
publishStoryLanguages: ({ storyId, story, languages, }: {
|
|
62
|
+
storyId: string | number;
|
|
63
|
+
story?: Record<string, any>;
|
|
64
|
+
languages: string[];
|
|
65
|
+
}, config: {
|
|
66
|
+
spaceId: string;
|
|
67
|
+
sbApi: any;
|
|
68
|
+
}) => Promise<any>;
|
|
69
|
+
parsePublishLanguagesOption: (publishLanguages?: string) => import("./stories/stories.types.js").PublishLanguagesOption;
|
|
70
|
+
resolvePublishLanguageCodes: (publishLanguages: import("./stories/stories.types.js").PublishLanguagesOption | undefined, config: {
|
|
71
|
+
spaceId: string;
|
|
72
|
+
sbApi: any;
|
|
73
|
+
}) => Promise<string[]>;
|
|
61
74
|
getAllStories: import("./stories/stories.types.js").GetAllStories;
|
|
62
75
|
removeAllStories: import("./stories/stories.types.js").RemoveAllStories;
|
|
63
76
|
upsertStory: import("./stories/stories.types.js").UpsertStory;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createStory, updateStory, getStoryById, removeStory, getStoryBySlug, updateStories, getAllStories, removeAllStories, upsertStory, } from "./stories.js";
|
|
1
|
+
export { createStory, updateStory, getStoryById, removeStory, getStoryBySlug, updateStories, publishStoryLanguages, parsePublishLanguagesOption, resolvePublishLanguageCodes, getAllStories, removeAllStories, upsertStory, } from "./stories.js";
|
|
2
2
|
export { backupStories } from "./backup.js";
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createStory, updateStory, getStoryById, removeStory, getStoryBySlug, updateStories, getAllStories, removeAllStories, upsertStory, } from "./stories.js";
|
|
1
|
+
export { createStory, updateStory, getStoryById, removeStory, getStoryBySlug, updateStories, publishStoryLanguages, parsePublishLanguagesOption, resolvePublishLanguageCodes, getAllStories, removeAllStories, upsertStory, } from "./stories.js";
|
|
2
2
|
export { backupStories } from "./backup.js";
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type { GetAllStories, GetStoryById, RemoveStory, CreateStory, UpdateStory, UpdateStories, RemoveAllStories, UpsertStory, DeepUpsertStory, GetStoryBySlug } from "./stories.types.js";
|
|
1
|
+
import type { GetAllStories, GetStoryById, RemoveStory, CreateStory, UpdateStory, UpdateStories, RemoveAllStories, UpsertStory, DeepUpsertStory, GetStoryBySlug, PublishLanguagesOption } from "./stories.types.js";
|
|
2
|
+
export declare const parsePublishLanguagesOption: (publishLanguages?: string) => PublishLanguagesOption;
|
|
3
|
+
export declare const resolvePublishLanguageCodes: (publishLanguages: PublishLanguagesOption | undefined, config: {
|
|
4
|
+
spaceId: string;
|
|
5
|
+
sbApi: any;
|
|
6
|
+
}) => Promise<string[]>;
|
|
2
7
|
export declare const removeStory: RemoveStory;
|
|
3
8
|
export declare const removeAllStories: RemoveAllStories;
|
|
4
9
|
export declare const getAllStories: GetAllStories;
|
|
@@ -6,6 +11,14 @@ export declare const getStoryById: GetStoryById;
|
|
|
6
11
|
export declare const getStoryBySlug: GetStoryBySlug;
|
|
7
12
|
export declare const createStory: CreateStory;
|
|
8
13
|
export declare const updateStory: UpdateStory;
|
|
14
|
+
export declare const publishStoryLanguages: ({ storyId, story, languages, }: {
|
|
15
|
+
storyId: string | number;
|
|
16
|
+
story?: Record<string, any>;
|
|
17
|
+
languages: string[];
|
|
18
|
+
}, config: {
|
|
19
|
+
spaceId: string;
|
|
20
|
+
sbApi: any;
|
|
21
|
+
}) => Promise<any>;
|
|
9
22
|
export declare const updateStories: UpdateStories;
|
|
10
23
|
export declare const upsertStory: UpsertStory;
|
|
11
24
|
export declare const deepUpsertStory: DeepUpsertStory;
|
|
@@ -3,6 +3,81 @@ import Logger from "../../utils/logger.js";
|
|
|
3
3
|
import { notNullish } from "../../utils/object-utils.js";
|
|
4
4
|
import { getAllItemsWithPagination } from "../utils/request.js";
|
|
5
5
|
const resolveStoryLabel = (content, storyId) => content?.full_slug || content?.slug || content?.name || String(storyId);
|
|
6
|
+
const DEFAULT_PUBLISH_LANGUAGE = "[default]";
|
|
7
|
+
const isDefaultLanguageToken = (language) => language.toLowerCase() === "default" ||
|
|
8
|
+
language === DEFAULT_PUBLISH_LANGUAGE;
|
|
9
|
+
const normalizePublishLanguageCodes = (languages) => {
|
|
10
|
+
const normalized = languages.map((language) => {
|
|
11
|
+
const trimmed = language.trim();
|
|
12
|
+
return isDefaultLanguageToken(trimmed)
|
|
13
|
+
? DEFAULT_PUBLISH_LANGUAGE
|
|
14
|
+
: trimmed;
|
|
15
|
+
});
|
|
16
|
+
const cleanLanguages = normalized.filter((language) => language.length > 0);
|
|
17
|
+
if (cleanLanguages.length === 0) {
|
|
18
|
+
throw new Error("Publish languages cannot be empty.");
|
|
19
|
+
}
|
|
20
|
+
return Array.from(new Set(cleanLanguages));
|
|
21
|
+
};
|
|
22
|
+
export const parsePublishLanguagesOption = (publishLanguages) => {
|
|
23
|
+
if (!publishLanguages) {
|
|
24
|
+
return "default";
|
|
25
|
+
}
|
|
26
|
+
const trimmed = publishLanguages.trim();
|
|
27
|
+
if (trimmed.length === 0) {
|
|
28
|
+
return "default";
|
|
29
|
+
}
|
|
30
|
+
if (trimmed.toLowerCase() === "all") {
|
|
31
|
+
return "all";
|
|
32
|
+
}
|
|
33
|
+
const languages = normalizePublishLanguageCodes(trimmed.split(","));
|
|
34
|
+
if (languages.length === 1 && languages[0] === DEFAULT_PUBLISH_LANGUAGE) {
|
|
35
|
+
return "default";
|
|
36
|
+
}
|
|
37
|
+
return languages;
|
|
38
|
+
};
|
|
39
|
+
const readSpaceLanguageCodes = (spaceResponse) => {
|
|
40
|
+
const space = spaceResponse?.data?.space || spaceResponse?.space || {};
|
|
41
|
+
const languageSources = [
|
|
42
|
+
space.languages,
|
|
43
|
+
space.language_codes,
|
|
44
|
+
space.options?.languages,
|
|
45
|
+
];
|
|
46
|
+
for (const source of languageSources) {
|
|
47
|
+
if (!Array.isArray(source)) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
return source
|
|
51
|
+
.map((language) => {
|
|
52
|
+
if (typeof language === "string") {
|
|
53
|
+
return language;
|
|
54
|
+
}
|
|
55
|
+
if (typeof language?.code === "string") {
|
|
56
|
+
return language.code;
|
|
57
|
+
}
|
|
58
|
+
return "";
|
|
59
|
+
})
|
|
60
|
+
.filter((language) => language.trim().length > 0);
|
|
61
|
+
}
|
|
62
|
+
return [];
|
|
63
|
+
};
|
|
64
|
+
export const resolvePublishLanguageCodes = async (publishLanguages, config) => {
|
|
65
|
+
if (!publishLanguages || publishLanguages === "default") {
|
|
66
|
+
return [DEFAULT_PUBLISH_LANGUAGE];
|
|
67
|
+
}
|
|
68
|
+
if (Array.isArray(publishLanguages)) {
|
|
69
|
+
return normalizePublishLanguageCodes(publishLanguages);
|
|
70
|
+
}
|
|
71
|
+
const spaceResponse = await config.sbApi.get(`spaces/${config.spaceId}`);
|
|
72
|
+
const spaceLanguageCodes = readSpaceLanguageCodes(spaceResponse);
|
|
73
|
+
if (spaceLanguageCodes.length === 0) {
|
|
74
|
+
Logger.warning(`No configured Storyblok languages were found for space '${config.spaceId}'. Publishing only ${DEFAULT_PUBLISH_LANGUAGE}.`);
|
|
75
|
+
}
|
|
76
|
+
return normalizePublishLanguageCodes([
|
|
77
|
+
DEFAULT_PUBLISH_LANGUAGE,
|
|
78
|
+
...spaceLanguageCodes,
|
|
79
|
+
]);
|
|
80
|
+
};
|
|
6
81
|
const resolveStoryblokErrorResponse = (err) => {
|
|
7
82
|
if (typeof err?.response === "string" && err.response.trim().length > 0) {
|
|
8
83
|
return err.response.trim();
|
|
@@ -139,6 +214,7 @@ export const updateStory = (content, storyId, options, config) => {
|
|
|
139
214
|
console.log(`${chalk.green(res.data.story.full_slug)} updated.`);
|
|
140
215
|
return {
|
|
141
216
|
ok: true,
|
|
217
|
+
stage: "update",
|
|
142
218
|
id: res.data.story.id,
|
|
143
219
|
name: res.data.story.name,
|
|
144
220
|
slug: res.data.story.full_slug,
|
|
@@ -155,6 +231,7 @@ export const updateStory = (content, storyId, options, config) => {
|
|
|
155
231
|
Logger.error(`Failed to update story '${storyLabel}' in space '${spaceId}' (${statusLabel}).${responseLabel}`);
|
|
156
232
|
return {
|
|
157
233
|
ok: false,
|
|
234
|
+
stage: "update",
|
|
158
235
|
id: storyId,
|
|
159
236
|
name: content?.name,
|
|
160
237
|
slug: content?.full_slug || content?.slug,
|
|
@@ -165,12 +242,78 @@ export const updateStory = (content, storyId, options, config) => {
|
|
|
165
242
|
};
|
|
166
243
|
});
|
|
167
244
|
};
|
|
168
|
-
export const
|
|
245
|
+
export const publishStoryLanguages = async ({ storyId, story, languages, }, config) => {
|
|
246
|
+
const { spaceId, sbApi } = config;
|
|
247
|
+
const lang = languages.join(",");
|
|
248
|
+
const storyLabel = resolveStoryLabel(story, String(storyId));
|
|
249
|
+
Logger.log(`Publishing story '${storyLabel}' in space '${spaceId}' for languages: ${lang}`);
|
|
250
|
+
return sbApi
|
|
251
|
+
.get(`spaces/${spaceId}/stories/${storyId}/publish`, { lang })
|
|
252
|
+
.then((res) => {
|
|
253
|
+
const publishedStory = res?.data?.story || story || {};
|
|
254
|
+
Logger.success(`Published story '${storyLabel}' for languages: ${lang}`);
|
|
255
|
+
return {
|
|
256
|
+
ok: true,
|
|
257
|
+
stage: "publish",
|
|
258
|
+
id: publishedStory.id || storyId,
|
|
259
|
+
name: publishedStory.name || story?.name,
|
|
260
|
+
slug: publishedStory.full_slug ||
|
|
261
|
+
publishedStory.slug ||
|
|
262
|
+
story?.full_slug ||
|
|
263
|
+
story?.slug,
|
|
264
|
+
spaceId,
|
|
265
|
+
publishLanguages: languages,
|
|
266
|
+
data: res?.data,
|
|
267
|
+
};
|
|
268
|
+
})
|
|
269
|
+
.catch((err) => {
|
|
270
|
+
const status = err?.status || err?.response?.status;
|
|
271
|
+
const responseMessage = resolveStoryblokErrorResponse(err);
|
|
272
|
+
const statusLabel = status ? `status ${status}` : "unknown status";
|
|
273
|
+
const responseLabel = responseMessage
|
|
274
|
+
? ` Response: ${responseMessage}`
|
|
275
|
+
: "";
|
|
276
|
+
Logger.error(`Failed to publish story '${storyLabel}' in space '${spaceId}' (${statusLabel}).${responseLabel}`);
|
|
277
|
+
return {
|
|
278
|
+
ok: false,
|
|
279
|
+
stage: "publish",
|
|
280
|
+
id: storyId,
|
|
281
|
+
name: story?.name,
|
|
282
|
+
slug: story?.full_slug || story?.slug,
|
|
283
|
+
spaceId,
|
|
284
|
+
status,
|
|
285
|
+
response: responseMessage,
|
|
286
|
+
publishLanguages: languages,
|
|
287
|
+
error: err,
|
|
288
|
+
};
|
|
289
|
+
});
|
|
290
|
+
};
|
|
291
|
+
export const updateStories = async (args, config) => {
|
|
169
292
|
const { stories, options, spaceId } = args;
|
|
293
|
+
const shouldPublishLanguages = options.publish && options.publishLanguages !== undefined;
|
|
294
|
+
const publishLanguages = shouldPublishLanguages
|
|
295
|
+
? await resolvePublishLanguageCodes(options.publishLanguages, {
|
|
296
|
+
...config,
|
|
297
|
+
spaceId,
|
|
298
|
+
})
|
|
299
|
+
: undefined;
|
|
170
300
|
return Promise.allSettled(
|
|
171
301
|
// Run through stories, and update the space with migrated version of stories
|
|
172
302
|
stories.map(async (stories) => {
|
|
173
|
-
|
|
303
|
+
const story = stories.story;
|
|
304
|
+
const updateResult = await updateStory(story, story.id, {
|
|
305
|
+
publish: options.publish && !shouldPublishLanguages,
|
|
306
|
+
}, { ...config, spaceId });
|
|
307
|
+
if (!shouldPublishLanguages ||
|
|
308
|
+
!publishLanguages ||
|
|
309
|
+
!updateResult?.ok) {
|
|
310
|
+
return updateResult;
|
|
311
|
+
}
|
|
312
|
+
return publishStoryLanguages({
|
|
313
|
+
storyId: story.id,
|
|
314
|
+
story,
|
|
315
|
+
languages: publishLanguages,
|
|
316
|
+
}, { ...config, spaceId });
|
|
174
317
|
}));
|
|
175
318
|
};
|
|
176
319
|
export const upsertStory = async (args, config) => {
|
|
@@ -6,7 +6,9 @@ export interface ExtendedISbStoriesParams extends ISbStoriesParams {
|
|
|
6
6
|
interface ModifyStoryOptions {
|
|
7
7
|
publish?: boolean;
|
|
8
8
|
force_update?: boolean;
|
|
9
|
+
publishLanguages?: PublishLanguagesOption;
|
|
9
10
|
}
|
|
11
|
+
export type PublishLanguagesOption = "default" | "all" | string[];
|
|
10
12
|
export type RemoveStory = (args: {
|
|
11
13
|
storyId: string;
|
|
12
14
|
}, config: RequestBaseConfig) => Promise<any>;
|
package/dist/api/testApi.d.ts
CHANGED
|
@@ -58,6 +58,19 @@ export declare const testApi: {
|
|
|
58
58
|
removeStory: import("./stories/stories.types.js").RemoveStory;
|
|
59
59
|
getStoryBySlug: import("./stories/stories.types.js").GetStoryBySlug;
|
|
60
60
|
updateStories: import("./stories/stories.types.js").UpdateStories;
|
|
61
|
+
publishStoryLanguages: ({ storyId, story, languages, }: {
|
|
62
|
+
storyId: string | number;
|
|
63
|
+
story?: Record<string, any>;
|
|
64
|
+
languages: string[];
|
|
65
|
+
}, config: {
|
|
66
|
+
spaceId: string;
|
|
67
|
+
sbApi: any;
|
|
68
|
+
}) => Promise<any>;
|
|
69
|
+
parsePublishLanguagesOption: (publishLanguages?: string) => import("./stories/stories.types.js").PublishLanguagesOption;
|
|
70
|
+
resolvePublishLanguageCodes: (publishLanguages: import("./stories/stories.types.js").PublishLanguagesOption | undefined, config: {
|
|
71
|
+
spaceId: string;
|
|
72
|
+
sbApi: any;
|
|
73
|
+
}) => Promise<string[]>;
|
|
61
74
|
getAllStories: import("./stories/stories.types.js").GetAllStories;
|
|
62
75
|
removeAllStories: import("./stories/stories.types.js").RemoveAllStories;
|
|
63
76
|
upsertStory: import("./stories/stories.types.js").UpsertStory;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const mainDescription = "\n USAGE\n $ sb-mig [command]\n \n COMMANDS\n sync Synchronize components, datasources, roles, stories, assets with Storyblok space.\n discover Discover components, migration configs and write to file or stdout.\n backup Command for backing up anything related to Storyblok\n migrate Migrate content from space to space, or from file to space.\n debug Output extra debugging information\n help This screen\n \n Examples\n $ sb-mig sync components --all\n $ sb-mig debug \n";
|
|
2
2
|
export declare const syncDescription = "\n Usage\n $ sb-mig sync [components|roles|datasources|plugins|content] [space separated file names] or --all\n \n Description\n Synchronize components, roles, datasources, plugins, content with Storyblok space.\n \n COMMANDS\n components - sync components\n roles - sync roles\n datasources - sync datasources\n plugins - sync plugins\n content - sync content (stories, assets) - ! right now destructive, it will move content from 1 space to another, completelly overwriting it\n \n FLAGS\n --all - Sync all components, roles, datasources [components, roles, datasources]\n --presets - Pass it, if u want to sync also with presets (will take longer) [components only]\n --dry-run - Preview planned changes without making writes [components, roles, datasources, plugins, content]\n \n --yes - Skip ask for confirmation (dangerous, but useful in CI/CD) [content only]\n --from - Space ID from which you want to sync content [content only]\n --to - Space ID to which you want to sync content [content only]\n --syncDirection [fromSpaceToFile|fromFileToSpace|fromSpaceToSpace|fromAWStoSpace] \n - Sync direction (from, to) [content only]\n \n EXAMPLES\n $ sb-mig sync components --all\n $ sb-mig sync components --all --dry-run\n $ sb-mig sync components --all --presets\n $ sb-mig sync components accordion accordion-item\n $ sb-mig sync components accordion accordion-item --presets\n \n $ sb-mig sync roles --all\n $ sb-mig sync roles --all --dry-run\n \n $ sb-mig sync datasources --all\n $ sb-mig sync datasources --all --dry-run\n \n $ sb-mig sync plugins my-awesome-plugin - (you have to be in catalog which has ./dist/export.js file with compiled plugin)\n \n $ sb-mig sync content --all --from 12345 --to 12345\n $ sb-mig sync content --stories --from 12345 --to 12345\n $ sb-mig sync content --assets --from 12345 --to 12345\n";
|
|
3
3
|
export declare const copyDescription = "\n Usage\n $ sb-mig copy\n \n Description\n Copy stuff\n \n COMMANDS\n ?\n \n FLAGS\n ?\n \n EXAMPLES\n $ sb-mig copy ?\n";
|
|
4
|
-
export declare const migrateDescription = "\n Usage\n $ sb-mig migrate [content] [space separated file names] or --all --from [spaceId] --to [spaceId] --migration [migration-config-filename]\n $ sb-mig migrate content --all --migration migration-a --migration migration-b --migration migration-c\n \n Description\n Migrate content from space to space, or from file to space. It's potentially dangerous command, so it will ask for confirmation.\n Use with care.\n \n COMMANDS\n content - migrate content \n \n FLAGS\n --from - Space ID from which you want to migrate / or file name if passed '--migrate-from file'\n --fromFilePath - Direct path to stories JSON file when using '--migrate-from file'\n --to - Space ID to which you want to migrate\n --migrate-from - Migrate from (space, file) default: space\n --migration - File name of migration file (without extension). Can be repeated for ordered pipeline in content migration.\n --migrationComponentAlias - Add extra component aliases for a migration. Repeatable. Format: <migration>:<source>=<alias1>,<alias2>\n --migrationComponents - Override the exact component scope for a migration. Repeatable. Format: <migration>:<component1>,<component2>\n --withSlug - Filter stories by full slug (can be repeated)\n --startsWith - Filter stories by starts_with prefix\n --yes - Skip ask for confirmation (dangerous, but useful in CI/CD)\n --dry-run - Preview what would be migrated without making any API changes\n --publish - Publish changed stories immediately after migration. Default: save draft. [content only]\n --fileName - Stable base name for migration output files (disables timestamp suffix for migration artifacts)\n\n EXAMPLES\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration migration-a --migration migration-b --migration migration-c\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration colorPickerModeValues --migrationComponentAlias colorPickerModeValues:sb-button=sb-open-drift-button\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration colorPickerModeValues --migrationComponentAlias colorPickerModeValues:sb-section=sb-tour-page-section --migrationComponents colorPickerModeValues:sb-section,sb-tour-page-section\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --withSlug blog/home --withSlug docs/getting-started\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --startsWith blog/\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --publish --yes\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration v3toV4AllMigrations --dry-run --fileName brand-hub-v3-v4-run\n $ sb-mig migrate content --all --migrate-from file --from file-with-stories --to 12345 --migration file-with-migration\n $ sb-mig migrate content --all --migrate-from file --fromFilePath sbmig/migrations/dry-run--123---story-to-migrate__2026-2-9_20-51.json --to 12345 --migration migration-a --migration migration-b\n $ sb-mig migrate content my-component-1 my-component-2 --from 12345 --to 12345 --migration file-with-migration\n $ sb-mig migrate content my-component-1 my-component-2 --migrate-from file --from file-with-stories --to 12345 --migration file-with-migration \n";
|
|
4
|
+
export declare const migrateDescription = "\n Usage\n $ sb-mig migrate [content] [space separated file names] or --all --from [spaceId] --to [spaceId] --migration [migration-config-filename]\n $ sb-mig migrate content --all --migration migration-a --migration migration-b --migration migration-c\n \n Description\n Migrate content from space to space, or from file to space. It's potentially dangerous command, so it will ask for confirmation.\n Use with care.\n \n COMMANDS\n content - migrate content \n \n FLAGS\n --from - Space ID from which you want to migrate / or file name if passed '--migrate-from file'\n --fromFilePath - Direct path to stories JSON file when using '--migrate-from file'\n --to - Space ID to which you want to migrate\n --migrate-from - Migrate from (space, file) default: space\n --migration - File name of migration file (without extension). Can be repeated for ordered pipeline in content migration.\n --migrationComponentAlias - Add extra component aliases for a migration. Repeatable. Format: <migration>:<source>=<alias1>,<alias2>\n --migrationComponents - Override the exact component scope for a migration. Repeatable. Format: <migration>:<component1>,<component2>\n --withSlug - Filter stories by full slug (can be repeated)\n --startsWith - Filter stories by starts_with prefix\n --yes - Skip ask for confirmation (dangerous, but useful in CI/CD)\n --dry-run - Preview what would be migrated without making any API changes\n --publish - Publish changed stories immediately after migration. Default: save draft. [content only]\n --publishLanguages - Languages to publish when --publish is set. Values: default, all, or comma-separated Storyblok language codes. [content only]\n --fileName - Stable base name for migration output files (disables timestamp suffix for migration artifacts)\n\n EXAMPLES\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration migration-a --migration migration-b --migration migration-c\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration colorPickerModeValues --migrationComponentAlias colorPickerModeValues:sb-button=sb-open-drift-button\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration colorPickerModeValues --migrationComponentAlias colorPickerModeValues:sb-section=sb-tour-page-section --migrationComponents colorPickerModeValues:sb-section,sb-tour-page-section\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --withSlug blog/home --withSlug docs/getting-started\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --startsWith blog/\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --publish --yes\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --publish --publishLanguages all --yes\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --publish --publishLanguages default,fr,de --yes\n $ sb-mig migrate content --all --from 12345 --to 12345 --migration v3toV4AllMigrations --dry-run --fileName brand-hub-v3-v4-run\n $ sb-mig migrate content --all --migrate-from file --from file-with-stories --to 12345 --migration file-with-migration\n $ sb-mig migrate content --all --migrate-from file --fromFilePath sbmig/migrations/dry-run--123---story-to-migrate__2026-2-9_20-51.json --to 12345 --migration migration-a --migration migration-b\n $ sb-mig migrate content my-component-1 my-component-2 --from 12345 --to 12345 --migration file-with-migration\n $ sb-mig migrate content my-component-1 my-component-2 --migrate-from file --from file-with-stories --to 12345 --migration file-with-migration \n";
|
|
5
5
|
export declare const revertDescription = "\n Usage\n $ sb-mig revert [content] --migration\n \n Description\n Revert content migration\n \n COMMANDS\n content - revert content migration \n \n FLAGS\n --migration - ???\n --yes - Skip ask for confirmation (dangerous, but useful in CI/CD) \n \n EXAMPLES\n $ sb-mig revert content --migration \n";
|
|
6
6
|
export declare const discoverDescription = "\n Usage\n $ sb-mig discover [components|migrations] --all --write\n\n Description\n Discover all components or migration configs and write to file or stdout\n\n COMMANDS\n components - discover components\n migrations - discover migration config files\n\n FLAGS\n --all - Discover all components or migration configs\n --write - Write to file\n\n EXAMPLES\n $ sb-mig discover components --all\n $ sb-mig discover components --all --write\n $ sb-mig discover migrations --all\n";
|
|
7
7
|
export declare const migrationsDescription = "\n Usage\n $ sb-mig migrations recognize\n \n Description\n Recognize migrations you need to apply\n \n COMMANDS\n recognize - recognize migrations\n \n FLAGS \n \n EXAMPLES\n $ sb-mig migrations recognize\n\n";
|
|
@@ -99,6 +99,7 @@ export const migrateDescription = `
|
|
|
99
99
|
--yes - Skip ask for confirmation (dangerous, but useful in CI/CD)
|
|
100
100
|
--dry-run - Preview what would be migrated without making any API changes
|
|
101
101
|
--publish - Publish changed stories immediately after migration. Default: save draft. [content only]
|
|
102
|
+
--publishLanguages - Languages to publish when --publish is set. Values: default, all, or comma-separated Storyblok language codes. [content only]
|
|
102
103
|
--fileName - Stable base name for migration output files (disables timestamp suffix for migration artifacts)
|
|
103
104
|
|
|
104
105
|
EXAMPLES
|
|
@@ -109,6 +110,8 @@ export const migrateDescription = `
|
|
|
109
110
|
$ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --withSlug blog/home --withSlug docs/getting-started
|
|
110
111
|
$ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --startsWith blog/
|
|
111
112
|
$ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --publish --yes
|
|
113
|
+
$ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --publish --publishLanguages all --yes
|
|
114
|
+
$ sb-mig migrate content --all --from 12345 --to 12345 --migration file-with-migration --publish --publishLanguages default,fr,de --yes
|
|
112
115
|
$ sb-mig migrate content --all --from 12345 --to 12345 --migration v3toV4AllMigrations --dry-run --fileName brand-hub-v3-v4-run
|
|
113
116
|
$ sb-mig migrate content --all --migrate-from file --from file-with-stories --to 12345 --migration file-with-migration
|
|
114
117
|
$ sb-mig migrate content --all --migrate-from file --fromFilePath sbmig/migrations/dry-run--123---story-to-migrate__2026-2-9_20-51.json --to 12345 --migration migration-a --migration migration-b
|
|
@@ -4,6 +4,7 @@ import { buildStoryBackupBaseName } from "../../api/data-migration/file-naming.j
|
|
|
4
4
|
import { parseMigrationComponentAliasFlags, parseMigrationComponentOverrideFlags, } from "../../api/data-migration/migration-component-scope.js";
|
|
5
5
|
import { managementApi } from "../../api/managementApi.js";
|
|
6
6
|
import { backupStories } from "../../api/stories/backup.js";
|
|
7
|
+
import { parsePublishLanguagesOption } from "../../api/stories/stories.js";
|
|
7
8
|
import { createAndSaveToFile } from "../../utils/files.js";
|
|
8
9
|
import Logger from "../../utils/logger.js";
|
|
9
10
|
import { apiConfig } from "../api-config.js";
|
|
@@ -42,6 +43,7 @@ export const migrate = async (props) => {
|
|
|
42
43
|
"startsWith",
|
|
43
44
|
"dryRun",
|
|
44
45
|
"publish",
|
|
46
|
+
"publishLanguages",
|
|
45
47
|
"fileName",
|
|
46
48
|
]);
|
|
47
49
|
Logger.warning(`This feature is in BETA. Use it at your own risk. The API might change in the future. (Probably in a standard Prisma like migration way)`);
|
|
@@ -62,6 +64,10 @@ export const migrate = async (props) => {
|
|
|
62
64
|
const migrationComponentOverrides = parseMigrationComponentOverrideFlags(flags["migrationComponents"]);
|
|
63
65
|
const dryRun = flags["dryRun"];
|
|
64
66
|
const publish = Boolean(flags["publish"]);
|
|
67
|
+
const publishLanguagesFlag = flags["publishLanguages"];
|
|
68
|
+
const publishLanguages = publishLanguagesFlag
|
|
69
|
+
? parsePublishLanguagesOption(publishLanguagesFlag)
|
|
70
|
+
: undefined;
|
|
65
71
|
const fileName = flags["fileName"];
|
|
66
72
|
const withSlugFlag = flags["withSlug"];
|
|
67
73
|
const withSlug = Array.isArray(withSlugFlag)
|
|
@@ -73,6 +79,9 @@ export const migrate = async (props) => {
|
|
|
73
79
|
if (migrationConfigs.length === 0) {
|
|
74
80
|
throw new Error("Missing migration config. Pass at least one --migration value.");
|
|
75
81
|
}
|
|
82
|
+
if (!publish && publishLanguagesFlag) {
|
|
83
|
+
throw new Error("--publishLanguages requires --publish for 'migrate content'.");
|
|
84
|
+
}
|
|
76
85
|
if (isIt("empty")) {
|
|
77
86
|
const componentsToMigrate = unpackElements(input) || [""];
|
|
78
87
|
const migrateFrom = "space";
|
|
@@ -100,6 +109,7 @@ export const migrate = async (props) => {
|
|
|
100
109
|
filters: { withSlug, startsWith },
|
|
101
110
|
dryRun,
|
|
102
111
|
publish,
|
|
112
|
+
publishLanguages,
|
|
103
113
|
fromFilePath,
|
|
104
114
|
fileName,
|
|
105
115
|
}, apiConfig);
|
|
@@ -128,6 +138,7 @@ export const migrate = async (props) => {
|
|
|
128
138
|
filters: { withSlug, startsWith },
|
|
129
139
|
dryRun,
|
|
130
140
|
publish,
|
|
141
|
+
publishLanguages,
|
|
131
142
|
fromFilePath,
|
|
132
143
|
fileName,
|
|
133
144
|
}, apiConfig);
|
|
@@ -163,12 +174,16 @@ export const migrate = async (props) => {
|
|
|
163
174
|
getFrom(flags, apiConfig);
|
|
164
175
|
const to = getTo(flags, apiConfig);
|
|
165
176
|
const publish = Boolean(flags["publish"]);
|
|
177
|
+
const publishLanguagesFlag = flags["publishLanguages"];
|
|
166
178
|
if (migrationConfigs.length === 0) {
|
|
167
179
|
throw new Error("Missing migration config. Pass exactly one --migration value for presets.");
|
|
168
180
|
}
|
|
169
181
|
if (publish) {
|
|
170
182
|
throw new Error("--publish is only supported for 'migrate content'. Presets cannot be published.");
|
|
171
183
|
}
|
|
184
|
+
if (publishLanguagesFlag) {
|
|
185
|
+
throw new Error("--publishLanguages is only supported for 'migrate content'. Presets cannot be published.");
|
|
186
|
+
}
|
|
172
187
|
if (migrationConfigs.length > 1) {
|
|
173
188
|
throw new Error("Multiple --migration values are currently supported only for 'migrate content'. Presets support a single migration config.");
|
|
174
189
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -3,12 +3,89 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.deepUpsertStory = exports.upsertStory = exports.updateStories = exports.updateStory = exports.createStory = exports.getStoryBySlug = exports.getStoryById = exports.getAllStories = exports.removeAllStories = exports.removeStory = void 0;
|
|
6
|
+
exports.deepUpsertStory = exports.upsertStory = exports.updateStories = exports.publishStoryLanguages = exports.updateStory = exports.createStory = exports.getStoryBySlug = exports.getStoryById = exports.getAllStories = exports.removeAllStories = exports.removeStory = exports.resolvePublishLanguageCodes = exports.parsePublishLanguagesOption = void 0;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const logger_js_1 = __importDefault(require("../../utils/logger.js"));
|
|
9
9
|
const object_utils_js_1 = require("../../utils/object-utils.js");
|
|
10
10
|
const request_js_1 = require("../utils/request.js");
|
|
11
11
|
const resolveStoryLabel = (content, storyId) => content?.full_slug || content?.slug || content?.name || String(storyId);
|
|
12
|
+
const DEFAULT_PUBLISH_LANGUAGE = "[default]";
|
|
13
|
+
const isDefaultLanguageToken = (language) => language.toLowerCase() === "default" ||
|
|
14
|
+
language === DEFAULT_PUBLISH_LANGUAGE;
|
|
15
|
+
const normalizePublishLanguageCodes = (languages) => {
|
|
16
|
+
const normalized = languages.map((language) => {
|
|
17
|
+
const trimmed = language.trim();
|
|
18
|
+
return isDefaultLanguageToken(trimmed)
|
|
19
|
+
? DEFAULT_PUBLISH_LANGUAGE
|
|
20
|
+
: trimmed;
|
|
21
|
+
});
|
|
22
|
+
const cleanLanguages = normalized.filter((language) => language.length > 0);
|
|
23
|
+
if (cleanLanguages.length === 0) {
|
|
24
|
+
throw new Error("Publish languages cannot be empty.");
|
|
25
|
+
}
|
|
26
|
+
return Array.from(new Set(cleanLanguages));
|
|
27
|
+
};
|
|
28
|
+
const parsePublishLanguagesOption = (publishLanguages) => {
|
|
29
|
+
if (!publishLanguages) {
|
|
30
|
+
return "default";
|
|
31
|
+
}
|
|
32
|
+
const trimmed = publishLanguages.trim();
|
|
33
|
+
if (trimmed.length === 0) {
|
|
34
|
+
return "default";
|
|
35
|
+
}
|
|
36
|
+
if (trimmed.toLowerCase() === "all") {
|
|
37
|
+
return "all";
|
|
38
|
+
}
|
|
39
|
+
const languages = normalizePublishLanguageCodes(trimmed.split(","));
|
|
40
|
+
if (languages.length === 1 && languages[0] === DEFAULT_PUBLISH_LANGUAGE) {
|
|
41
|
+
return "default";
|
|
42
|
+
}
|
|
43
|
+
return languages;
|
|
44
|
+
};
|
|
45
|
+
exports.parsePublishLanguagesOption = parsePublishLanguagesOption;
|
|
46
|
+
const readSpaceLanguageCodes = (spaceResponse) => {
|
|
47
|
+
const space = spaceResponse?.data?.space || spaceResponse?.space || {};
|
|
48
|
+
const languageSources = [
|
|
49
|
+
space.languages,
|
|
50
|
+
space.language_codes,
|
|
51
|
+
space.options?.languages,
|
|
52
|
+
];
|
|
53
|
+
for (const source of languageSources) {
|
|
54
|
+
if (!Array.isArray(source)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
return source
|
|
58
|
+
.map((language) => {
|
|
59
|
+
if (typeof language === "string") {
|
|
60
|
+
return language;
|
|
61
|
+
}
|
|
62
|
+
if (typeof language?.code === "string") {
|
|
63
|
+
return language.code;
|
|
64
|
+
}
|
|
65
|
+
return "";
|
|
66
|
+
})
|
|
67
|
+
.filter((language) => language.trim().length > 0);
|
|
68
|
+
}
|
|
69
|
+
return [];
|
|
70
|
+
};
|
|
71
|
+
const resolvePublishLanguageCodes = async (publishLanguages, config) => {
|
|
72
|
+
if (!publishLanguages || publishLanguages === "default") {
|
|
73
|
+
return [DEFAULT_PUBLISH_LANGUAGE];
|
|
74
|
+
}
|
|
75
|
+
if (Array.isArray(publishLanguages)) {
|
|
76
|
+
return normalizePublishLanguageCodes(publishLanguages);
|
|
77
|
+
}
|
|
78
|
+
const spaceResponse = await config.sbApi.get(`spaces/${config.spaceId}`);
|
|
79
|
+
const spaceLanguageCodes = readSpaceLanguageCodes(spaceResponse);
|
|
80
|
+
if (spaceLanguageCodes.length === 0) {
|
|
81
|
+
logger_js_1.default.warning(`No configured Storyblok languages were found for space '${config.spaceId}'. Publishing only ${DEFAULT_PUBLISH_LANGUAGE}.`);
|
|
82
|
+
}
|
|
83
|
+
return normalizePublishLanguageCodes([
|
|
84
|
+
DEFAULT_PUBLISH_LANGUAGE,
|
|
85
|
+
...spaceLanguageCodes,
|
|
86
|
+
]);
|
|
87
|
+
};
|
|
88
|
+
exports.resolvePublishLanguageCodes = resolvePublishLanguageCodes;
|
|
12
89
|
const resolveStoryblokErrorResponse = (err) => {
|
|
13
90
|
if (typeof err?.response === "string" && err.response.trim().length > 0) {
|
|
14
91
|
return err.response.trim();
|
|
@@ -151,6 +228,7 @@ const updateStory = (content, storyId, options, config) => {
|
|
|
151
228
|
console.log(`${chalk_1.default.green(res.data.story.full_slug)} updated.`);
|
|
152
229
|
return {
|
|
153
230
|
ok: true,
|
|
231
|
+
stage: "update",
|
|
154
232
|
id: res.data.story.id,
|
|
155
233
|
name: res.data.story.name,
|
|
156
234
|
slug: res.data.story.full_slug,
|
|
@@ -167,6 +245,7 @@ const updateStory = (content, storyId, options, config) => {
|
|
|
167
245
|
logger_js_1.default.error(`Failed to update story '${storyLabel}' in space '${spaceId}' (${statusLabel}).${responseLabel}`);
|
|
168
246
|
return {
|
|
169
247
|
ok: false,
|
|
248
|
+
stage: "update",
|
|
170
249
|
id: storyId,
|
|
171
250
|
name: content?.name,
|
|
172
251
|
slug: content?.full_slug || content?.slug,
|
|
@@ -178,12 +257,79 @@ const updateStory = (content, storyId, options, config) => {
|
|
|
178
257
|
});
|
|
179
258
|
};
|
|
180
259
|
exports.updateStory = updateStory;
|
|
181
|
-
const
|
|
260
|
+
const publishStoryLanguages = async ({ storyId, story, languages, }, config) => {
|
|
261
|
+
const { spaceId, sbApi } = config;
|
|
262
|
+
const lang = languages.join(",");
|
|
263
|
+
const storyLabel = resolveStoryLabel(story, String(storyId));
|
|
264
|
+
logger_js_1.default.log(`Publishing story '${storyLabel}' in space '${spaceId}' for languages: ${lang}`);
|
|
265
|
+
return sbApi
|
|
266
|
+
.get(`spaces/${spaceId}/stories/${storyId}/publish`, { lang })
|
|
267
|
+
.then((res) => {
|
|
268
|
+
const publishedStory = res?.data?.story || story || {};
|
|
269
|
+
logger_js_1.default.success(`Published story '${storyLabel}' for languages: ${lang}`);
|
|
270
|
+
return {
|
|
271
|
+
ok: true,
|
|
272
|
+
stage: "publish",
|
|
273
|
+
id: publishedStory.id || storyId,
|
|
274
|
+
name: publishedStory.name || story?.name,
|
|
275
|
+
slug: publishedStory.full_slug ||
|
|
276
|
+
publishedStory.slug ||
|
|
277
|
+
story?.full_slug ||
|
|
278
|
+
story?.slug,
|
|
279
|
+
spaceId,
|
|
280
|
+
publishLanguages: languages,
|
|
281
|
+
data: res?.data,
|
|
282
|
+
};
|
|
283
|
+
})
|
|
284
|
+
.catch((err) => {
|
|
285
|
+
const status = err?.status || err?.response?.status;
|
|
286
|
+
const responseMessage = resolveStoryblokErrorResponse(err);
|
|
287
|
+
const statusLabel = status ? `status ${status}` : "unknown status";
|
|
288
|
+
const responseLabel = responseMessage
|
|
289
|
+
? ` Response: ${responseMessage}`
|
|
290
|
+
: "";
|
|
291
|
+
logger_js_1.default.error(`Failed to publish story '${storyLabel}' in space '${spaceId}' (${statusLabel}).${responseLabel}`);
|
|
292
|
+
return {
|
|
293
|
+
ok: false,
|
|
294
|
+
stage: "publish",
|
|
295
|
+
id: storyId,
|
|
296
|
+
name: story?.name,
|
|
297
|
+
slug: story?.full_slug || story?.slug,
|
|
298
|
+
spaceId,
|
|
299
|
+
status,
|
|
300
|
+
response: responseMessage,
|
|
301
|
+
publishLanguages: languages,
|
|
302
|
+
error: err,
|
|
303
|
+
};
|
|
304
|
+
});
|
|
305
|
+
};
|
|
306
|
+
exports.publishStoryLanguages = publishStoryLanguages;
|
|
307
|
+
const updateStories = async (args, config) => {
|
|
182
308
|
const { stories, options, spaceId } = args;
|
|
309
|
+
const shouldPublishLanguages = options.publish && options.publishLanguages !== undefined;
|
|
310
|
+
const publishLanguages = shouldPublishLanguages
|
|
311
|
+
? await (0, exports.resolvePublishLanguageCodes)(options.publishLanguages, {
|
|
312
|
+
...config,
|
|
313
|
+
spaceId,
|
|
314
|
+
})
|
|
315
|
+
: undefined;
|
|
183
316
|
return Promise.allSettled(
|
|
184
317
|
// Run through stories, and update the space with migrated version of stories
|
|
185
318
|
stories.map(async (stories) => {
|
|
186
|
-
|
|
319
|
+
const story = stories.story;
|
|
320
|
+
const updateResult = await (0, exports.updateStory)(story, story.id, {
|
|
321
|
+
publish: options.publish && !shouldPublishLanguages,
|
|
322
|
+
}, { ...config, spaceId });
|
|
323
|
+
if (!shouldPublishLanguages ||
|
|
324
|
+
!publishLanguages ||
|
|
325
|
+
!updateResult?.ok) {
|
|
326
|
+
return updateResult;
|
|
327
|
+
}
|
|
328
|
+
return (0, exports.publishStoryLanguages)({
|
|
329
|
+
storyId: story.id,
|
|
330
|
+
story,
|
|
331
|
+
languages: publishLanguages,
|
|
332
|
+
}, { ...config, spaceId });
|
|
187
333
|
}));
|
|
188
334
|
};
|
|
189
335
|
exports.updateStories = updateStories;
|
package/package.json
CHANGED