Skip to content

Commit f651ce3

Browse files
authored
feat: stream based migrations (#295)
This PR replaces the batch based migration command with a streaming pipeline that provides progress reporting and improved performance. The previous process relied on loading all the stories into memory whereas now we process the stories in flight. This improves both memory usage and throughput. resolves wdx-67
1 parent 36a42a1 commit f651ce3

File tree

8 files changed

+883
-1557
lines changed

8 files changed

+883
-1557
lines changed

packages/cli/package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@
4343
"@storyblok/management-api-client": "workspace:*",
4444
"@storyblok/region-helper": "workspace:*",
4545
"@topcli/spinner": "^2.1.2",
46+
"async-sema": "^3.1.1",
4647
"chalk": "^5.4.1",
48+
"cli-progress": "^3.12.0",
4749
"commander": "^13.1.0",
4850
"dotenv": "^16.5.0",
4951
"filenamify": "^6.0.0",
@@ -59,6 +61,7 @@
5961
"devDependencies": {
6062
"@release-it/conventional-changelog": "10.0.0",
6163
"@storyblok/eslint-config": "workspace:*",
64+
"@types/cli-progress": "^3.11.6",
6265
"@types/inquirer": "^9.0.8",
6366
"@types/node": "^22.15.18",
6467
"@vitest/coverage-v8": "^3.1.3",

packages/cli/src/api.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { ManagementApiClient, type ManagementApiClientConfig } from '@storyblok/management-api-client';
2+
import { RateLimit } from 'async-sema';
23

34
let instance: ManagementApiClient | null = null;
45
let storedConfig: ManagementApiClientConfig | null = null;
6+
const lim = RateLimit(6, {
7+
uniformDistribution: true,
8+
});
59

610
function configsAreEqual(config1: ManagementApiClientConfig, config2: ManagementApiClientConfig): boolean {
711
return JSON.stringify(config1) === JSON.stringify(config2);
@@ -10,6 +14,10 @@ function configsAreEqual(config1: ManagementApiClientConfig, config2: Management
1014
export function mapiClient(options?: ManagementApiClientConfig) {
1115
if (!instance && options) {
1216
instance = new ManagementApiClient(options);
17+
instance.interceptors.request.use(async (request) => {
18+
await lim();
19+
return request;
20+
});
1321
storedConfig = options;
1422
}
1523
else if (!instance) {
@@ -18,6 +26,10 @@ export function mapiClient(options?: ManagementApiClientConfig) {
1826
else if (options && storedConfig && !configsAreEqual(options, storedConfig)) {
1927
// Create new instance if options are different from stored config
2028
instance = new ManagementApiClient(options);
29+
instance.interceptors.request.use(async (request) => {
30+
await lim();
31+
return request;
32+
});
2133
storedConfig = options;
2234
}
2335
return instance;

0 commit comments

Comments
 (0)