storyblok 4.16.1 → 4.16.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.
- package/dist/index.mjs +316 -166
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -681,6 +681,7 @@ const API_ACTIONS = {
|
|
|
681
681
|
push_datasource: "Failed to push datasource",
|
|
682
682
|
update_datasource: "Failed to update datasource",
|
|
683
683
|
delete_datasource: "Failed to delete datasource",
|
|
684
|
+
delete_datasource_entry: "Failed to delete datasource entry",
|
|
684
685
|
create_space: "Failed to create space",
|
|
685
686
|
pull_spaces: "Failed to pull spaces",
|
|
686
687
|
fetch_blueprints: "Failed to fetch blueprints from GitHub"
|
|
@@ -3995,6 +3996,8 @@ generateCmd$1.action(async (componentName, options, command) => {
|
|
|
3995
3996
|
}
|
|
3996
3997
|
});
|
|
3997
3998
|
|
|
3999
|
+
const normalizeFullSlug = (slug) => slug.replace(/\/$/, "");
|
|
4000
|
+
|
|
3998
4001
|
const fetchStories = async (spaceId, params) => {
|
|
3999
4002
|
try {
|
|
4000
4003
|
const client = getMapiClient();
|
|
@@ -4096,9 +4099,15 @@ const prefetchTargetStories = async (spaceId, options) => {
|
|
|
4096
4099
|
options?.onTotal?.(total);
|
|
4097
4100
|
}
|
|
4098
4101
|
for (const story of response.stories) {
|
|
4099
|
-
const ref = { id: story.id, uuid: story.uuid };
|
|
4102
|
+
const ref = { id: story.id, uuid: story.uuid, is_folder: story.is_folder };
|
|
4100
4103
|
if (story.full_slug) {
|
|
4101
|
-
|
|
4104
|
+
const key = normalizeFullSlug(story.full_slug);
|
|
4105
|
+
const existing = result.bySlug.get(key);
|
|
4106
|
+
if (existing) {
|
|
4107
|
+
existing.push(ref);
|
|
4108
|
+
} else {
|
|
4109
|
+
result.bySlug.set(key, [ref]);
|
|
4110
|
+
}
|
|
4102
4111
|
}
|
|
4103
4112
|
result.byId.set(story.id, ref);
|
|
4104
4113
|
}
|
|
@@ -5874,7 +5883,7 @@ const upsertDatasource = async (space, datasource, existingId) => {
|
|
|
5874
5883
|
return await pushDatasource(space, datasource);
|
|
5875
5884
|
}
|
|
5876
5885
|
};
|
|
5877
|
-
const pushDatasourceEntry = async (spaceId, datasourceId, entry) => {
|
|
5886
|
+
const pushDatasourceEntry = async (spaceId, datasourceId, entry, position) => {
|
|
5878
5887
|
try {
|
|
5879
5888
|
const client = getMapiClient();
|
|
5880
5889
|
const { data } = await client.datasourceEntries.create({
|
|
@@ -5884,7 +5893,8 @@ const pushDatasourceEntry = async (spaceId, datasourceId, entry) => {
|
|
|
5884
5893
|
body: {
|
|
5885
5894
|
datasource_entry: {
|
|
5886
5895
|
...entry,
|
|
5887
|
-
datasource_id: datasourceId
|
|
5896
|
+
datasource_id: datasourceId,
|
|
5897
|
+
...position != null && { position }
|
|
5888
5898
|
}
|
|
5889
5899
|
},
|
|
5890
5900
|
throwOnError: true
|
|
@@ -5894,7 +5904,7 @@ const pushDatasourceEntry = async (spaceId, datasourceId, entry) => {
|
|
|
5894
5904
|
handleAPIError("push_datasource", error, `Failed to push datasource entry ${entry.name}`);
|
|
5895
5905
|
}
|
|
5896
5906
|
};
|
|
5897
|
-
const updateDatasourceEntry = async (spaceId, entryId, entry) => {
|
|
5907
|
+
const updateDatasourceEntry = async (spaceId, entryId, entry, position) => {
|
|
5898
5908
|
try {
|
|
5899
5909
|
const client = getMapiClient();
|
|
5900
5910
|
await client.datasourceEntries.updateDatasourceEntry({
|
|
@@ -5903,7 +5913,10 @@ const updateDatasourceEntry = async (spaceId, entryId, entry) => {
|
|
|
5903
5913
|
datasource_entry_id: entryId
|
|
5904
5914
|
},
|
|
5905
5915
|
body: {
|
|
5906
|
-
datasource_entry:
|
|
5916
|
+
datasource_entry: {
|
|
5917
|
+
...entry,
|
|
5918
|
+
...position != null && { position }
|
|
5919
|
+
}
|
|
5907
5920
|
},
|
|
5908
5921
|
throwOnError: true
|
|
5909
5922
|
});
|
|
@@ -5911,12 +5924,26 @@ const updateDatasourceEntry = async (spaceId, entryId, entry) => {
|
|
|
5911
5924
|
handleAPIError("update_datasource", error, `Failed to update datasource entry ${entry.name}`);
|
|
5912
5925
|
}
|
|
5913
5926
|
};
|
|
5914
|
-
const upsertDatasourceEntry = async (space, datasourceId, entry, existingId) => {
|
|
5927
|
+
const upsertDatasourceEntry = async (space, datasourceId, entry, existingId, position) => {
|
|
5915
5928
|
if (existingId) {
|
|
5916
|
-
await updateDatasourceEntry(space, existingId, entry);
|
|
5929
|
+
await updateDatasourceEntry(space, existingId, entry, position);
|
|
5917
5930
|
return void 0;
|
|
5918
5931
|
} else {
|
|
5919
|
-
return await pushDatasourceEntry(space, datasourceId, entry);
|
|
5932
|
+
return await pushDatasourceEntry(space, datasourceId, entry, position);
|
|
5933
|
+
}
|
|
5934
|
+
};
|
|
5935
|
+
const deleteDatasourceEntry = async (spaceId, entryId) => {
|
|
5936
|
+
try {
|
|
5937
|
+
const client = getMapiClient();
|
|
5938
|
+
await client.datasourceEntries.delete({
|
|
5939
|
+
path: {
|
|
5940
|
+
space_id: spaceId,
|
|
5941
|
+
datasource_entry_id: entryId
|
|
5942
|
+
},
|
|
5943
|
+
throwOnError: true
|
|
5944
|
+
});
|
|
5945
|
+
} catch (error) {
|
|
5946
|
+
handleAPIError("delete_datasource_entry", error, `Failed to delete datasource entry ${entryId}`);
|
|
5920
5947
|
}
|
|
5921
5948
|
};
|
|
5922
5949
|
const readDatasourcesFiles = async (options) => {
|
|
@@ -5994,16 +6021,17 @@ const generateCmd = typesCommand.command("generate").description("Generate types
|
|
|
5994
6021
|
).option("--sf, --separate-files", "Generate one .d.ts file per component instead of a single combined file").option("--strict", "strict mode, no loose typing").option("--type-prefix <prefix>", "prefix to be prepended to all generated component type names").option("--type-suffix <suffix>", "suffix to be appended to all generated component type names").option("--suffix <suffix>", "Components suffix").option("--custom-fields-parser <path>", "Path to the parser file for Custom Field Types").option("--compiler-options <options>", "path to the compiler options from json-schema-to-typescript").option("-s, --space <space>", "space ID").option("-p, --path <path>", "path for file storage");
|
|
5995
6022
|
generateCmd.action(async (options, command) => {
|
|
5996
6023
|
konsola.title(`${commands.TYPES}`, colorPalette.TYPES, "Generating types...");
|
|
5997
|
-
const { space, path, verbose } = command.optsWithGlobals();
|
|
6024
|
+
const { space, path, verbose, suffix, filename, separateFiles } = command.optsWithGlobals();
|
|
5998
6025
|
const spinner = new Spinner({
|
|
5999
|
-
verbose
|
|
6026
|
+
verbose
|
|
6000
6027
|
});
|
|
6001
6028
|
try {
|
|
6002
6029
|
spinner.start(`Generating types...`);
|
|
6003
6030
|
const componentsData = await readComponentsFiles({
|
|
6004
6031
|
from: space,
|
|
6005
6032
|
path,
|
|
6006
|
-
|
|
6033
|
+
separateFiles,
|
|
6034
|
+
suffix,
|
|
6007
6035
|
verbose
|
|
6008
6036
|
});
|
|
6009
6037
|
let dataSourceData;
|
|
@@ -6011,7 +6039,8 @@ generateCmd.action(async (options, command) => {
|
|
|
6011
6039
|
dataSourceData = await readDatasourcesFiles({
|
|
6012
6040
|
from: space,
|
|
6013
6041
|
path,
|
|
6014
|
-
|
|
6042
|
+
separateFiles,
|
|
6043
|
+
suffix,
|
|
6015
6044
|
verbose
|
|
6016
6045
|
});
|
|
6017
6046
|
} catch (error) {
|
|
@@ -6029,18 +6058,17 @@ generateCmd.action(async (options, command) => {
|
|
|
6029
6058
|
...dataSourceData
|
|
6030
6059
|
};
|
|
6031
6060
|
const typedefData = await generateTypes(spaceDataWithComponentsAndDatasources, {
|
|
6032
|
-
...options
|
|
6033
|
-
path
|
|
6061
|
+
...options
|
|
6034
6062
|
});
|
|
6035
6063
|
if (typedefData) {
|
|
6036
6064
|
await saveTypesToComponentsFile(space, typedefData, {
|
|
6037
|
-
filename
|
|
6065
|
+
filename,
|
|
6038
6066
|
path,
|
|
6039
|
-
separateFiles
|
|
6067
|
+
separateFiles
|
|
6040
6068
|
});
|
|
6041
6069
|
}
|
|
6042
6070
|
spinner.succeed();
|
|
6043
|
-
if (
|
|
6071
|
+
if (separateFiles && filename) {
|
|
6044
6072
|
konsola.warn(`The --filename option is ignored when using --separate-files`);
|
|
6045
6073
|
}
|
|
6046
6074
|
konsola.ok(`Successfully generated types for space ${space}`, true);
|
|
@@ -6178,23 +6206,23 @@ pullCmd$2.action(async (datasourceName, options, command) => {
|
|
|
6178
6206
|
handleError(new CommandError(`Please provide the space as argument --space YOUR_SPACE_ID.`), verbose);
|
|
6179
6207
|
return;
|
|
6180
6208
|
}
|
|
6181
|
-
const
|
|
6182
|
-
|
|
6183
|
-
});
|
|
6209
|
+
const ui = getUI();
|
|
6210
|
+
const logger = getLogger();
|
|
6211
|
+
logger.info("Pulling datasources started", { space, datasourceName });
|
|
6212
|
+
const spinnerDatasources = ui.createSpinner(`Fetching ${chalk.hex(colorPalette.DATASOURCES)("datasources")}`);
|
|
6184
6213
|
try {
|
|
6185
|
-
spinnerDatasources.start(`Fetching ${chalk.hex(colorPalette.DATASOURCES)("datasources")}`);
|
|
6186
6214
|
let datasources;
|
|
6187
6215
|
if (datasourceName) {
|
|
6188
6216
|
const datasource = await fetchDatasource(space, datasourceName);
|
|
6189
6217
|
if (!datasource) {
|
|
6190
|
-
|
|
6218
|
+
spinnerDatasources.failed(`No datasource found with name "${datasourceName}"`);
|
|
6191
6219
|
return;
|
|
6192
6220
|
}
|
|
6193
6221
|
datasources = [datasource];
|
|
6194
6222
|
} else {
|
|
6195
6223
|
datasources = await fetchDatasources(space);
|
|
6196
6224
|
if (!datasources || datasources.length === 0) {
|
|
6197
|
-
|
|
6225
|
+
spinnerDatasources.failed(`No datasources found in the space ${space}`);
|
|
6198
6226
|
return;
|
|
6199
6227
|
}
|
|
6200
6228
|
}
|
|
@@ -6228,6 +6256,8 @@ pullCmd$2.action(async (datasourceName, options, command) => {
|
|
|
6228
6256
|
spinnerDatasources.failed(`Fetching ${chalk.hex(colorPalette.DATASOURCES)("Datasources")} - Failed`);
|
|
6229
6257
|
konsola.br();
|
|
6230
6258
|
handleError(error, verbose);
|
|
6259
|
+
} finally {
|
|
6260
|
+
logger.info("Pulling datasources finished", { space, datasourceName });
|
|
6231
6261
|
}
|
|
6232
6262
|
});
|
|
6233
6263
|
|
|
@@ -6245,6 +6275,8 @@ pushCmd$2.action(async (datasourceName, options, command) => {
|
|
|
6245
6275
|
handleError(new CommandError(`Please provide the target space as argument --space TARGET_SPACE_ID.`), verbose);
|
|
6246
6276
|
return;
|
|
6247
6277
|
}
|
|
6278
|
+
const logger = getLogger();
|
|
6279
|
+
logger.info("Pushing datasources started", { space, fromSpace, datasourceName, filter });
|
|
6248
6280
|
konsola.info(`Attempting to push datasources ${chalk.bold("from")} space ${chalk.hex(colorPalette.DATASOURCES)(fromSpace)} ${chalk.bold("to")} ${chalk.hex(colorPalette.DATASOURCES)(space)}`);
|
|
6249
6281
|
konsola.br();
|
|
6250
6282
|
try {
|
|
@@ -6288,26 +6320,44 @@ pushCmd$2.action(async (datasourceName, options, command) => {
|
|
|
6288
6320
|
successful: [],
|
|
6289
6321
|
failed: []
|
|
6290
6322
|
};
|
|
6323
|
+
const ui = getUI();
|
|
6291
6324
|
for (const datasource of spaceState.local.datasources) {
|
|
6292
|
-
const spinner =
|
|
6293
|
-
verbose: !isVitest
|
|
6294
|
-
});
|
|
6295
|
-
spinner.start(`Pushing ${chalk.hex(colorPalette.DATASOURCES)(datasource.name)}`);
|
|
6325
|
+
const spinner = ui.createSpinner(`Pushing ${chalk.hex(colorPalette.DATASOURCES)(datasource.name)}`);
|
|
6296
6326
|
const existingDatasource = spaceState.target.datasources.get(datasource.name);
|
|
6297
6327
|
const existingId = existingDatasource?.id;
|
|
6298
6328
|
const { entries, ...datasourceDefinition } = datasource;
|
|
6299
6329
|
const result = await upsertDatasource(space, datasourceDefinition, existingId);
|
|
6300
6330
|
if (result) {
|
|
6301
6331
|
results.successful.push(datasource.name);
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
6309
|
-
|
|
6310
|
-
|
|
6332
|
+
const localEntries = entries ?? [];
|
|
6333
|
+
const existingEntries = existingDatasource?.entries ?? [];
|
|
6334
|
+
const existingEntryMap = new Map(existingEntries.map((e, idx) => [e.name, { entry: e, position: idx + 1 }]));
|
|
6335
|
+
for (let i = 0; i < localEntries.length; i++) {
|
|
6336
|
+
const entry = localEntries[i];
|
|
6337
|
+
const existing = existingEntryMap.get(entry.name);
|
|
6338
|
+
const existingEntryId = existing?.entry.id;
|
|
6339
|
+
const targetPosition = i + 1;
|
|
6340
|
+
if (existing && existing.entry.value === entry.value && existing.entry.dimension_value === entry.dimension_value && existing.position === targetPosition) {
|
|
6341
|
+
logger.info("Skipped datasource entry (unchanged)", { datasource: datasource.name, entry: entry.name, position: targetPosition });
|
|
6342
|
+
continue;
|
|
6343
|
+
}
|
|
6344
|
+
try {
|
|
6345
|
+
await upsertDatasourceEntry(space, result.id, entry, existingEntryId, i + 1);
|
|
6346
|
+
logger.info(existingEntryId ? "Updated datasource entry" : "Created datasource entry", { datasource: datasource.name, entry: entry.name, position: i + 1 });
|
|
6347
|
+
} catch (entryError) {
|
|
6348
|
+
results.failed.push({ name: datasource.name, error: entryError });
|
|
6349
|
+
spinner.failed(`${chalk.hex(colorPalette.DATASOURCES)(datasource.name)} - Failed in ${spinner.elapsedTime.toFixed(2)}ms`);
|
|
6350
|
+
}
|
|
6351
|
+
}
|
|
6352
|
+
const localEntryNames = new Set(localEntries.map((e) => e.name));
|
|
6353
|
+
const staleEntries = existingEntries.filter((e) => !localEntryNames.has(e.name));
|
|
6354
|
+
for (const stale of staleEntries) {
|
|
6355
|
+
try {
|
|
6356
|
+
await deleteDatasourceEntry(space, stale.id);
|
|
6357
|
+
logger.info("Deleted datasource entry", { datasource: datasource.name, entry: stale.name, entryId: stale.id });
|
|
6358
|
+
} catch (entryError) {
|
|
6359
|
+
results.failed.push({ name: datasource.name, error: entryError });
|
|
6360
|
+
spinner.failed(`${chalk.hex(colorPalette.DATASOURCES)(datasource.name)} - Failed in ${spinner.elapsedTime.toFixed(2)}ms`);
|
|
6311
6361
|
}
|
|
6312
6362
|
}
|
|
6313
6363
|
spinner.succeed(`${chalk.hex(colorPalette.DATASOURCES)(datasource.name)} - Completed in ${spinner.elapsedTime.toFixed(2)}ms`);
|
|
@@ -6328,6 +6378,8 @@ pushCmd$2.action(async (datasourceName, options, command) => {
|
|
|
6328
6378
|
}
|
|
6329
6379
|
} catch (error) {
|
|
6330
6380
|
handleError(error, verbose);
|
|
6381
|
+
} finally {
|
|
6382
|
+
logger.info("Pushing datasources finished", { space, fromSpace });
|
|
6331
6383
|
}
|
|
6332
6384
|
});
|
|
6333
6385
|
|
|
@@ -6367,14 +6419,14 @@ deleteCmd.action(async (name, options, command) => {
|
|
|
6367
6419
|
handleError(new CommandError("Please provide the space as argument --space YOUR_SPACE_ID."), verbose);
|
|
6368
6420
|
return;
|
|
6369
6421
|
}
|
|
6370
|
-
const
|
|
6371
|
-
|
|
6372
|
-
});
|
|
6422
|
+
const ui = getUI();
|
|
6423
|
+
const logger = getLogger();
|
|
6424
|
+
logger.info("Deleting datasource started", { space, name, id: options.id });
|
|
6373
6425
|
try {
|
|
6374
6426
|
if (options.id) {
|
|
6375
|
-
spinner.
|
|
6427
|
+
const spinner = ui.createSpinner(`Deleting datasource...`);
|
|
6376
6428
|
await deleteDatasource(space, options.id);
|
|
6377
|
-
spinner.succeed();
|
|
6429
|
+
spinner.succeed(`Datasource deleted`);
|
|
6378
6430
|
konsola.ok(`Datasource ${chalk.hex(colorPalette.DATASOURCES)(options.id)} deleted successfully from space ${space}.`);
|
|
6379
6431
|
} else {
|
|
6380
6432
|
const datasource = await fetchDatasource(space, name);
|
|
@@ -6395,21 +6447,19 @@ deleteCmd.action(async (name, options, command) => {
|
|
|
6395
6447
|
default: false
|
|
6396
6448
|
});
|
|
6397
6449
|
if (!confirmed) {
|
|
6398
|
-
spinner.failed("Deletion aborted by user.");
|
|
6399
6450
|
konsola.warn("Deletion aborted by user.");
|
|
6400
6451
|
return;
|
|
6401
6452
|
}
|
|
6402
6453
|
}
|
|
6403
|
-
spinner.
|
|
6454
|
+
const spinner = ui.createSpinner(`Deleting datasource...`);
|
|
6404
6455
|
await deleteDatasource(space, datasource.id.toString());
|
|
6405
|
-
spinner.succeed();
|
|
6456
|
+
spinner.succeed(`Datasource deleted`);
|
|
6406
6457
|
konsola.ok(`Datasource ${chalk.hex(colorPalette.DATASOURCES)(name)} deleted successfully from space ${space}.`);
|
|
6407
6458
|
}
|
|
6408
6459
|
} catch (error) {
|
|
6409
|
-
spinner.failed(
|
|
6410
|
-
`Failed to delete datasource ${chalk.hex(colorPalette.DATASOURCES)(options.id ? options.id : name)}`
|
|
6411
|
-
);
|
|
6412
6460
|
handleError(error, verbose);
|
|
6461
|
+
} finally {
|
|
6462
|
+
logger.info("Deleting datasource finished", { space, name, id: options.id });
|
|
6413
6463
|
}
|
|
6414
6464
|
});
|
|
6415
6465
|
|
|
@@ -8216,87 +8266,172 @@ const mapReferencesStream = ({
|
|
|
8216
8266
|
}
|
|
8217
8267
|
});
|
|
8218
8268
|
};
|
|
8219
|
-
const
|
|
8220
|
-
const { id: _id, uuid: _uuid, parent_id: _parentId, is_startpage: _isStartpage, content, ...newStoryData } = localStory;
|
|
8221
|
-
if (!localStory.is_folder && !content?.component) {
|
|
8222
|
-
throw new Error(`Story "${localStory.slug}" is missing a content type (content.component). Every story must define a content field with a valid component.`);
|
|
8223
|
-
}
|
|
8224
|
-
const remoteStory = await createStory(spaceId, {
|
|
8225
|
-
story: {
|
|
8226
|
-
...newStoryData,
|
|
8227
|
-
...content?.component ? { content: { _uid: "", component: "__migration_artifact__" } } : {}
|
|
8228
|
-
},
|
|
8229
|
-
publish: 0
|
|
8230
|
-
});
|
|
8231
|
-
if (!remoteStory) {
|
|
8232
|
-
throw new Error("No response!");
|
|
8233
|
-
}
|
|
8234
|
-
return remoteStory;
|
|
8235
|
-
};
|
|
8236
|
-
const makeAppendToManifestFSTransport = ({ manifestFile }) => async (localStory, remoteStory) => {
|
|
8269
|
+
const makeAppendToManifestFSTransport = ({ manifestFile }) => async (entry, remoteStory) => {
|
|
8237
8270
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
8238
8271
|
await appendToFile(manifestFile, JSON.stringify({
|
|
8239
|
-
old_id:
|
|
8272
|
+
old_id: entry.uuid,
|
|
8240
8273
|
new_id: remoteStory.uuid,
|
|
8241
8274
|
created_at: createdAt
|
|
8242
8275
|
}));
|
|
8243
8276
|
await appendToFile(manifestFile, JSON.stringify({
|
|
8244
|
-
old_id:
|
|
8277
|
+
old_id: entry.id,
|
|
8245
8278
|
new_id: remoteStory.id,
|
|
8246
8279
|
created_at: createdAt
|
|
8247
8280
|
}));
|
|
8248
8281
|
};
|
|
8249
|
-
const
|
|
8282
|
+
const scanLocalStoryIndex = async ({
|
|
8283
|
+
directoryPath,
|
|
8284
|
+
setTotalStories,
|
|
8285
|
+
onIncrement,
|
|
8286
|
+
onError
|
|
8287
|
+
}) => {
|
|
8288
|
+
const files = (await readDirectory(directoryPath)).filter((f) => extname(f) === ".json");
|
|
8289
|
+
setTotalStories?.(files.length);
|
|
8290
|
+
const entries = [];
|
|
8291
|
+
for (const file of files) {
|
|
8292
|
+
try {
|
|
8293
|
+
const filePath = join(directoryPath, file);
|
|
8294
|
+
const fileContent = await readFile$1(filePath, "utf-8");
|
|
8295
|
+
const story = JSON.parse(fileContent);
|
|
8296
|
+
entries.push({
|
|
8297
|
+
filename: file,
|
|
8298
|
+
id: story.id,
|
|
8299
|
+
uuid: story.uuid ?? "",
|
|
8300
|
+
slug: story.slug ?? "",
|
|
8301
|
+
name: story.name ?? "",
|
|
8302
|
+
full_slug: story.full_slug ?? "",
|
|
8303
|
+
is_folder: story.is_folder ?? false,
|
|
8304
|
+
is_startpage: story.is_startpage === true,
|
|
8305
|
+
parent_id: story.parent_id ?? null,
|
|
8306
|
+
component: story.content?.component
|
|
8307
|
+
});
|
|
8308
|
+
} catch (maybeError) {
|
|
8309
|
+
onError?.(toError(maybeError), file);
|
|
8310
|
+
} finally {
|
|
8311
|
+
onIncrement?.();
|
|
8312
|
+
}
|
|
8313
|
+
}
|
|
8314
|
+
return entries;
|
|
8315
|
+
};
|
|
8316
|
+
const groupStoriesByDepth = (entries) => {
|
|
8317
|
+
const depthMap = /* @__PURE__ */ new Map();
|
|
8318
|
+
for (const entry of entries) {
|
|
8319
|
+
const slug = normalizeFullSlug(entry.full_slug || "");
|
|
8320
|
+
const depth = slug === "" ? 0 : slug.split("/").length - 1;
|
|
8321
|
+
if (!depthMap.has(depth)) {
|
|
8322
|
+
depthMap.set(depth, []);
|
|
8323
|
+
}
|
|
8324
|
+
depthMap.get(depth).push(entry);
|
|
8325
|
+
}
|
|
8326
|
+
const maxDepth = depthMap.size > 0 ? Math.max(...depthMap.keys()) : 0;
|
|
8327
|
+
const levels = [];
|
|
8328
|
+
for (let d = 0; d <= maxDepth; d++) {
|
|
8329
|
+
const level = depthMap.get(d);
|
|
8330
|
+
if (!level || level.length === 0) {
|
|
8331
|
+
continue;
|
|
8332
|
+
}
|
|
8333
|
+
level.sort((a, b) => {
|
|
8334
|
+
if (a.is_folder && !b.is_folder) {
|
|
8335
|
+
return -1;
|
|
8336
|
+
}
|
|
8337
|
+
if (!a.is_folder && b.is_folder) {
|
|
8338
|
+
return 1;
|
|
8339
|
+
}
|
|
8340
|
+
return 0;
|
|
8341
|
+
});
|
|
8342
|
+
levels.push(level);
|
|
8343
|
+
}
|
|
8344
|
+
return levels;
|
|
8345
|
+
};
|
|
8346
|
+
const findSlugMatch = ({
|
|
8347
|
+
entry,
|
|
8348
|
+
existingTargetStories,
|
|
8349
|
+
claimedRemoteIds
|
|
8350
|
+
}) => {
|
|
8351
|
+
const normalizedSlug = entry.full_slug ? normalizeFullSlug(entry.full_slug) : void 0;
|
|
8352
|
+
const slugCandidates = normalizedSlug ? existingTargetStories.bySlug.get(normalizedSlug) : void 0;
|
|
8353
|
+
if (!slugCandidates) {
|
|
8354
|
+
return void 0;
|
|
8355
|
+
}
|
|
8356
|
+
const unclaimed = slugCandidates.filter((ref) => !claimedRemoteIds.has(ref.id));
|
|
8357
|
+
return unclaimed.find((ref) => ref.is_folder === entry.is_folder) ?? unclaimed[0];
|
|
8358
|
+
};
|
|
8359
|
+
const createStoriesForLevel = async ({
|
|
8360
|
+
level,
|
|
8361
|
+
spaceId,
|
|
8250
8362
|
maps,
|
|
8251
8363
|
existingTargetStories,
|
|
8364
|
+
claimedRemoteIds,
|
|
8252
8365
|
isCrossSpace,
|
|
8253
|
-
|
|
8254
|
-
|
|
8366
|
+
dryRun,
|
|
8367
|
+
appendToManifest,
|
|
8255
8368
|
onStorySuccess,
|
|
8256
8369
|
onStorySkipped,
|
|
8257
8370
|
onStoryError
|
|
8258
8371
|
}) => {
|
|
8259
|
-
const
|
|
8260
|
-
|
|
8261
|
-
|
|
8262
|
-
|
|
8263
|
-
|
|
8264
|
-
|
|
8265
|
-
|
|
8266
|
-
|
|
8267
|
-
|
|
8268
|
-
|
|
8269
|
-
|
|
8270
|
-
|
|
8271
|
-
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
onStorySkipped?.(localStory, existingBySlug);
|
|
8278
|
-
return;
|
|
8279
|
-
}
|
|
8280
|
-
}
|
|
8281
|
-
const newRemoteStory = await transports.createStory(localStory);
|
|
8282
|
-
await transports.appendStoryManifest(localStory, newRemoteStory);
|
|
8283
|
-
onStorySuccess?.(localStory, newRemoteStory);
|
|
8284
|
-
} catch (maybeError) {
|
|
8285
|
-
onStoryError?.(toError(maybeError), localStory);
|
|
8372
|
+
const processEntry = async (entry) => {
|
|
8373
|
+
await apiConcurrencyLock.acquire();
|
|
8374
|
+
try {
|
|
8375
|
+
const mappedStoryId = maps.stories?.get(entry.id);
|
|
8376
|
+
const mappedRemoteStory = mappedStoryId ? existingTargetStories.byId.get(Number(mappedStoryId)) : void 0;
|
|
8377
|
+
if (mappedRemoteStory) {
|
|
8378
|
+
claimedRemoteIds.add(mappedRemoteStory.id);
|
|
8379
|
+
onStorySkipped?.(entry, mappedRemoteStory, "matched by manifest mapping from a previous push");
|
|
8380
|
+
return;
|
|
8381
|
+
}
|
|
8382
|
+
const match = findSlugMatch({ entry, existingTargetStories, claimedRemoteIds });
|
|
8383
|
+
if (match) {
|
|
8384
|
+
const isMatchConfirmed = isCrossSpace || match.uuid === entry.uuid;
|
|
8385
|
+
if (isMatchConfirmed) {
|
|
8386
|
+
claimedRemoteIds.add(match.id);
|
|
8387
|
+
await appendToManifest(entry, match);
|
|
8388
|
+
onStorySkipped?.(entry, match, "matched by slug in target space");
|
|
8389
|
+
return;
|
|
8286
8390
|
}
|
|
8287
|
-
}
|
|
8288
|
-
|
|
8289
|
-
|
|
8290
|
-
|
|
8291
|
-
|
|
8292
|
-
|
|
8391
|
+
}
|
|
8392
|
+
if (!entry.is_folder && !entry.component) {
|
|
8393
|
+
throw new Error(`Story "${entry.slug}" (${entry.filename}) is missing a content type (content.component). Every story must define a content field with a valid component.`);
|
|
8394
|
+
}
|
|
8395
|
+
const resolvedParentId = entry.parent_id != null ? maps.stories?.get(entry.parent_id) : void 0;
|
|
8396
|
+
if (dryRun) {
|
|
8397
|
+
const fakeRemote = { id: entry.id, uuid: entry.uuid };
|
|
8398
|
+
onStorySuccess?.(entry, fakeRemote);
|
|
8399
|
+
return;
|
|
8400
|
+
}
|
|
8401
|
+
const remoteStory = await createStory(spaceId, {
|
|
8402
|
+
story: {
|
|
8403
|
+
slug: entry.slug,
|
|
8404
|
+
name: entry.name,
|
|
8405
|
+
is_folder: entry.is_folder,
|
|
8406
|
+
...resolvedParentId != null ? { parent_id: Number(resolvedParentId) } : {},
|
|
8407
|
+
...entry.is_startpage && resolvedParentId != null ? { is_startpage: true } : {},
|
|
8408
|
+
...entry.component ? { content: { _uid: "", component: entry.component } } : {}
|
|
8409
|
+
},
|
|
8410
|
+
publish: 0
|
|
8293
8411
|
});
|
|
8294
|
-
|
|
8295
|
-
|
|
8296
|
-
|
|
8297
|
-
|
|
8412
|
+
if (!remoteStory) {
|
|
8413
|
+
throw new Error("No response!");
|
|
8414
|
+
}
|
|
8415
|
+
await appendToManifest(entry, remoteStory);
|
|
8416
|
+
onStorySuccess?.(entry, remoteStory);
|
|
8417
|
+
} catch (maybeError) {
|
|
8418
|
+
onStoryError?.(toError(maybeError), entry);
|
|
8419
|
+
} finally {
|
|
8420
|
+
apiConcurrencyLock.release();
|
|
8298
8421
|
}
|
|
8299
|
-
}
|
|
8422
|
+
};
|
|
8423
|
+
const folders = level.filter((e) => e.is_folder);
|
|
8424
|
+
const nonFolders = level.filter((e) => !e.is_folder);
|
|
8425
|
+
const folderTasks = [];
|
|
8426
|
+
for (const entry of folders) {
|
|
8427
|
+
folderTasks.push(processEntry(entry));
|
|
8428
|
+
}
|
|
8429
|
+
await Promise.all(folderTasks);
|
|
8430
|
+
const storyTasks = [];
|
|
8431
|
+
for (const entry of nonFolders) {
|
|
8432
|
+
storyTasks.push(processEntry(entry));
|
|
8433
|
+
}
|
|
8434
|
+
await Promise.all(storyTasks);
|
|
8300
8435
|
};
|
|
8301
8436
|
const makeWriteStoryFSTransport = ({ directoryPath }) => async (story) => {
|
|
8302
8437
|
await saveToFile(resolve(directoryPath, getStoryFilename(story)), JSON.stringify(story, null, 2));
|
|
@@ -8306,15 +8441,25 @@ const makeWriteStoryAPITransport = ({ spaceId, publish }) => (mappedLocalStory)
|
|
|
8306
8441
|
story: mappedLocalStory,
|
|
8307
8442
|
publish: publish ?? (isStoryPublishedWithoutChanges(mappedLocalStory) ? 1 : 0)
|
|
8308
8443
|
});
|
|
8309
|
-
const makeCleanupStoryFSTransport = ({ directoryPath, maps }) =>
|
|
8310
|
-
const
|
|
8311
|
-
|
|
8312
|
-
|
|
8313
|
-
|
|
8314
|
-
|
|
8315
|
-
|
|
8316
|
-
|
|
8317
|
-
|
|
8444
|
+
const makeCleanupStoryFSTransport = ({ directoryPath, maps }) => {
|
|
8445
|
+
const reverseUuidMap = /* @__PURE__ */ new Map();
|
|
8446
|
+
if (maps.stories) {
|
|
8447
|
+
for (const [key, value] of maps.stories.entries()) {
|
|
8448
|
+
if (typeof key === "string") {
|
|
8449
|
+
reverseUuidMap.set(value, key);
|
|
8450
|
+
}
|
|
8451
|
+
}
|
|
8452
|
+
}
|
|
8453
|
+
return async (mappedStory) => {
|
|
8454
|
+
const uuid = mappedStory.uuid ?? "";
|
|
8455
|
+
const originalUuid = reverseUuidMap.get(uuid) ?? uuid;
|
|
8456
|
+
const storyFilename = getStoryFilename({
|
|
8457
|
+
slug: mappedStory.slug,
|
|
8458
|
+
uuid: originalUuid
|
|
8459
|
+
});
|
|
8460
|
+
const storyFilePath = resolve(directoryPath, storyFilename);
|
|
8461
|
+
await unlink(storyFilePath);
|
|
8462
|
+
};
|
|
8318
8463
|
};
|
|
8319
8464
|
const writeStoryStream = ({
|
|
8320
8465
|
transports,
|
|
@@ -8921,75 +9066,80 @@ pushCmd.action(async (options, command) => {
|
|
|
8921
9066
|
});
|
|
8922
9067
|
fetchProgress.stop();
|
|
8923
9068
|
const storiesDirectoryPath = resolveCommandPath(directories.stories, fromSpace, basePath);
|
|
9069
|
+
const scanProgress = ui.createProgressBar({ title: "Scanning Stories...".padEnd(21) });
|
|
9070
|
+
const storyIndex = await scanLocalStoryIndex({
|
|
9071
|
+
directoryPath: storiesDirectoryPath,
|
|
9072
|
+
setTotalStories(total) {
|
|
9073
|
+
scanProgress.setTotal(total);
|
|
9074
|
+
},
|
|
9075
|
+
onIncrement() {
|
|
9076
|
+
scanProgress.increment();
|
|
9077
|
+
},
|
|
9078
|
+
onError(error, filename) {
|
|
9079
|
+
summary.creationResults.failed += 1;
|
|
9080
|
+
handleError(error, verbose, { storyFile: filename });
|
|
9081
|
+
}
|
|
9082
|
+
});
|
|
9083
|
+
const levels = groupStoriesByDepth(storyIndex);
|
|
9084
|
+
scanProgress.stop();
|
|
8924
9085
|
const creationProgress = ui.createProgressBar({ title: "Creating Stories...".padEnd(21) });
|
|
8925
9086
|
const processProgress = ui.createProgressBar({ title: "Processing Stories...".padEnd(21) });
|
|
8926
9087
|
const updateProgress = ui.createProgressBar({ title: "Updating Stories...".padEnd(21) });
|
|
8927
|
-
|
|
8928
|
-
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
8934
|
-
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
8938
|
-
|
|
8939
|
-
|
|
8940
|
-
summary.creationResults.failed += 1;
|
|
8941
|
-
summary.processResults.total -= 1;
|
|
8942
|
-
summary.updateResults.total -= 1;
|
|
8943
|
-
processProgress.setTotal(summary.processResults.total);
|
|
8944
|
-
updateProgress.setTotal(summary.updateResults.total);
|
|
8945
|
-
creationProgress.increment();
|
|
8946
|
-
handleError(error, verbose, { storyFile: filename });
|
|
8947
|
-
}
|
|
8948
|
-
}),
|
|
8949
|
-
// Create remote stories.
|
|
8950
|
-
createStoryPlaceholderStream({
|
|
9088
|
+
const totalStories = storyIndex.length + summary.creationResults.failed;
|
|
9089
|
+
summary.creationResults.total = totalStories;
|
|
9090
|
+
summary.processResults.total = totalStories;
|
|
9091
|
+
summary.updateResults.total = totalStories;
|
|
9092
|
+
creationProgress.setTotal(totalStories);
|
|
9093
|
+
processProgress.setTotal(totalStories);
|
|
9094
|
+
updateProgress.setTotal(totalStories);
|
|
9095
|
+
const appendToManifest = options.dryRun ? (() => Promise.resolve()) : makeAppendToManifestFSTransport({ manifestFile });
|
|
9096
|
+
const claimedRemoteIds = /* @__PURE__ */ new Set();
|
|
9097
|
+
for (const level of levels) {
|
|
9098
|
+
await createStoriesForLevel({
|
|
9099
|
+
level,
|
|
9100
|
+
spaceId: space,
|
|
8951
9101
|
maps,
|
|
8952
9102
|
existingTargetStories,
|
|
9103
|
+
claimedRemoteIds,
|
|
8953
9104
|
isCrossSpace: fromSpace !== space,
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
appendStoryManifest: options.dryRun ? () => Promise.resolve() : makeAppendToManifestFSTransport({
|
|
8959
|
-
manifestFile
|
|
8960
|
-
})
|
|
8961
|
-
},
|
|
8962
|
-
onStorySuccess(localStory, remoteStory) {
|
|
8963
|
-
if (!localStory.uuid || !remoteStory.uuid) {
|
|
9105
|
+
dryRun: options.dryRun ?? false,
|
|
9106
|
+
appendToManifest,
|
|
9107
|
+
onStorySuccess(entry, remoteStory) {
|
|
9108
|
+
if (!entry.uuid || !remoteStory.uuid) {
|
|
8964
9109
|
throw new Error("Invalid story provided!");
|
|
8965
9110
|
}
|
|
8966
|
-
maps.stories.set(
|
|
8967
|
-
maps.stories.set(
|
|
9111
|
+
maps.stories.set(entry.id, remoteStory.id);
|
|
9112
|
+
maps.stories.set(entry.uuid, remoteStory.uuid);
|
|
8968
9113
|
logger.info("Created story", { storyId: remoteStory.uuid });
|
|
8969
9114
|
summary.creationResults.succeeded += 1;
|
|
9115
|
+
creationProgress.increment();
|
|
8970
9116
|
},
|
|
8971
|
-
onStorySkipped(
|
|
8972
|
-
if (!
|
|
9117
|
+
onStorySkipped(entry, remoteStory, reason) {
|
|
9118
|
+
if (!entry.uuid || !remoteStory.uuid) {
|
|
8973
9119
|
throw new Error("Invalid story provided!");
|
|
8974
9120
|
}
|
|
8975
|
-
maps.stories.set(
|
|
8976
|
-
maps.stories.set(
|
|
8977
|
-
logger.info(
|
|
9121
|
+
maps.stories.set(entry.id, remoteStory.id);
|
|
9122
|
+
maps.stories.set(entry.uuid, remoteStory.uuid);
|
|
9123
|
+
logger.info(`Skipped creating story: ${reason}`, { storyId: entry.uuid });
|
|
8978
9124
|
summary.creationResults.skipped += 1;
|
|
9125
|
+
creationProgress.increment();
|
|
8979
9126
|
},
|
|
8980
|
-
onStoryError(error,
|
|
9127
|
+
onStoryError(error, entry) {
|
|
8981
9128
|
summary.creationResults.failed += 1;
|
|
8982
9129
|
summary.processResults.total -= 1;
|
|
8983
9130
|
summary.updateResults.total -= 1;
|
|
8984
9131
|
processProgress.setTotal(summary.processResults.total);
|
|
8985
9132
|
updateProgress.setTotal(summary.updateResults.total);
|
|
8986
|
-
handleError(error, verbose, { storyId: localStory?.uuid });
|
|
8987
|
-
},
|
|
8988
|
-
onIncrement() {
|
|
8989
9133
|
creationProgress.increment();
|
|
9134
|
+
handleError(error, verbose, { storyId: entry?.uuid });
|
|
8990
9135
|
}
|
|
8991
|
-
})
|
|
8992
|
-
|
|
9136
|
+
});
|
|
9137
|
+
}
|
|
9138
|
+
if (summary.creationResults.failed > 0) {
|
|
9139
|
+
const message = `${summary.creationResults.failed} ${summary.creationResults.failed === 1 ? "story" : "stories"} failed to create. References to these stories will be left unmapped.`;
|
|
9140
|
+
ui.warn(message);
|
|
9141
|
+
logger.warn(message);
|
|
9142
|
+
}
|
|
8993
9143
|
await pipeline$1(
|
|
8994
9144
|
// Read local stories from `.json` files.
|
|
8995
9145
|
readLocalStoriesStream({
|
|
@@ -9055,7 +9205,7 @@ pushCmd.action(async (options, command) => {
|
|
|
9055
9205
|
})
|
|
9056
9206
|
);
|
|
9057
9207
|
} catch (maybeError) {
|
|
9058
|
-
handleError(toError(maybeError));
|
|
9208
|
+
handleError(toError(maybeError), verbose);
|
|
9059
9209
|
} finally {
|
|
9060
9210
|
logger.info("Pushing stories finished", summary);
|
|
9061
9211
|
ui.stopAllProgressBars();
|