storyblok 4.6.4 → 4.6.7

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 CHANGED
@@ -856,7 +856,7 @@ const loginStrategy = {
856
856
  short: "Email"
857
857
  },
858
858
  {
859
- name: "With Token (SSO)",
859
+ name: "With Token (Personal Access Token \u2013 works also for SSO accounts)",
860
860
  value: "login-with-token",
861
861
  short: "Token"
862
862
  }
@@ -915,8 +915,13 @@ program$i.command(commands.LOGIN).description("Login to the Storyblok CLI").opti
915
915
  try {
916
916
  const strategy = await select(loginStrategy);
917
917
  if (strategy === "login-with-token") {
918
+ konsola.info([
919
+ "\u{1F511} You can use a Personal Access Token to log in.",
920
+ "This works for all accounts, including SSO accounts.",
921
+ `Generate one in your Storyblok account settings: ${chalk.underline.blue("https://app.storyblok.com/#/me/account?tab=token")}`
922
+ ].join("\n"));
918
923
  const userToken = await password({
919
- message: "Please enter your token:",
924
+ message: "Please enter your Personal Access Token:",
920
925
  validate: (value) => {
921
926
  return value.length > 0;
922
927
  }
@@ -1572,6 +1577,7 @@ componentsCommand.command("pull [componentName]").option("-f, --filename <filena
1572
1577
  }
1573
1578
  });
1574
1579
 
1580
+ const fieldTypesWithDependencies = ["bloks", "richtext"];
1575
1581
  function buildDependencyGraph(context) {
1576
1582
  const { spaceState } = context;
1577
1583
  const graph = { nodes: /* @__PURE__ */ new Map() };
@@ -1676,7 +1682,7 @@ function collectWhitelistDependencies(schema) {
1676
1682
  const componentNames = /* @__PURE__ */ new Set();
1677
1683
  const datasourceNames = /* @__PURE__ */ new Set();
1678
1684
  function traverseField(field) {
1679
- if (field.type === "bloks") {
1685
+ if (fieldTypesWithDependencies.includes(field.type)) {
1680
1686
  if (field.component_group_whitelist && Array.isArray(field.component_group_whitelist)) {
1681
1687
  field.component_group_whitelist.forEach((uuid) => groupUuids.add(uuid));
1682
1688
  }
@@ -2024,7 +2030,7 @@ class ComponentNode extends GraphNode {
2024
2030
  return field.map(resolveField);
2025
2031
  }
2026
2032
  const resolvedField = { ...field };
2027
- if (resolvedField.type === "bloks") {
2033
+ if (fieldTypesWithDependencies.includes(resolvedField.type)) {
2028
2034
  if (resolvedField.component_group_whitelist && Array.isArray(resolvedField.component_group_whitelist)) {
2029
2035
  resolvedField.component_group_whitelist = resolvedField.component_group_whitelist.map((groupUuid) => {
2030
2036
  const groupNodeId = `group:${groupUuid}`;
@@ -3074,7 +3080,8 @@ function applyMigrationToAllBlocks(content, migrationFunction, targetComponent)
3074
3080
  migratedContent = migrationFunction({ ...content });
3075
3081
  processed = true;
3076
3082
  }
3077
- for (const key of Object.keys(content)) {
3083
+ const uniqueKeys = /* @__PURE__ */ new Set([...Object.keys(content), ...Object.keys(migratedContent || {})]);
3084
+ for (const key of uniqueKeys) {
3078
3085
  if (migratedContent) {
3079
3086
  if (!(key in migratedContent)) {
3080
3087
  delete content[key];
@@ -3102,7 +3109,7 @@ async function saveRollbackData({
3102
3109
  path,
3103
3110
  story,
3104
3111
  migrationTimestamp,
3105
- migrationFile
3112
+ migrationNames
3106
3113
  }) {
3107
3114
  const rollbackData = {
3108
3115
  storyId: story.id,
@@ -3110,7 +3117,9 @@ async function saveRollbackData({
3110
3117
  content: story.content
3111
3118
  };
3112
3119
  const rollbacksPath = resolvePath(path, `migrations/${space}/rollbacks`);
3113
- const rollbackFileName = `${migrationFile.replace(".js", "")}.${migrationTimestamp}.jsonl`;
3120
+ const componentNames = migrationNames.map((n) => getComponentNameFromFilename(n));
3121
+ const rollbackName = [...new Set(componentNames)].join("~");
3122
+ const rollbackFileName = `${rollbackName}.${migrationTimestamp}.jsonl`;
3114
3123
  const rollbackFilePath = join(rollbacksPath, rollbackFileName);
3115
3124
  await appendToFile(
3116
3125
  rollbackFilePath,
@@ -3168,67 +3177,71 @@ class MigrationStream extends Transform {
3168
3177
  callback(error);
3169
3178
  }
3170
3179
  }
3171
- async processStory(story) {
3172
- if (!story.content) {
3173
- for (const migrationFile of this.options.migrationFiles) {
3174
- this.results.failed.push({
3175
- storyId: story.id,
3176
- migrationName: migrationFile.name,
3177
- error: new Error("Story content is missing")
3178
- });
3179
- }
3180
- return [];
3180
+ async getOrLoadMigrationFunction(migrationFile) {
3181
+ if (this.migrationFunctions.has(migrationFile.name)) {
3182
+ return this.migrationFunctions.get(migrationFile.name);
3181
3183
  }
3184
+ const migrationFunction = await getMigrationFunction(
3185
+ migrationFile.name,
3186
+ this.options.space,
3187
+ this.options.path
3188
+ );
3189
+ this.migrationFunctions.set(migrationFile.name, migrationFunction);
3190
+ return migrationFunction;
3191
+ }
3192
+ async processStory(story) {
3182
3193
  const relevantMigrations = this.options.componentName ? this.options.migrationFiles.filter((file) => {
3183
3194
  const targetComponent = getComponentNameFromFilename(file.name);
3184
3195
  return targetComponent.split(".")[0] === this.options.componentName;
3185
3196
  }) : this.options.migrationFiles;
3197
+ if (!story.content) {
3198
+ this.results.failed.push({
3199
+ storyId: story.id,
3200
+ migrationNames: relevantMigrations.map((m) => m.name),
3201
+ error: new Error("Story content is missing")
3202
+ });
3203
+ return [];
3204
+ }
3186
3205
  const successfulResults = [];
3187
- for (const migrationFile of relevantMigrations) {
3188
- const result = await this.applyMigrationToStory(story, migrationFile);
3189
- if (result) {
3190
- successfulResults.push(result);
3191
- }
3206
+ const result = await this.applyMigrationsToStory(story, relevantMigrations);
3207
+ if (result) {
3208
+ successfulResults.push(result);
3192
3209
  }
3193
3210
  return successfulResults;
3194
3211
  }
3195
- async applyMigrationToStory(story, migrationFile) {
3212
+ async applyMigrationsToStory(story, migrationFiles) {
3213
+ const migrationNames = migrationFiles.map((f) => f.name);
3196
3214
  try {
3197
- let migrationFunction = this.migrationFunctions.get(migrationFile.name);
3198
- if (!migrationFunction) {
3199
- migrationFunction = await getMigrationFunction(
3200
- migrationFile.name,
3201
- this.options.space,
3202
- this.options.path
3203
- );
3204
- this.migrationFunctions.set(migrationFile.name, migrationFunction);
3205
- }
3206
- if (!migrationFunction) {
3207
- this.results.failed.push({
3208
- storyId: story.id,
3209
- migrationName: migrationFile.name,
3210
- error: new Error(`Failed to load migration function from file "${migrationFile.name}"`)
3211
- });
3212
- return null;
3213
- }
3214
- await saveRollbackData({
3215
- space: this.options.space,
3216
- path: this.options.path,
3217
- story: { id: story.id, name: story.name || "", content: story.content },
3218
- migrationTimestamp: this.timestamp,
3219
- migrationFile: migrationFile.name
3220
- });
3221
3215
  const storyContent = structuredClone(story.content);
3222
3216
  const originalContentHash = hash(storyContent);
3223
- const targetComponent = this.options.componentName || getComponentNameFromFilename(migrationFile.name);
3224
- const processed = applyMigrationToAllBlocks(storyContent, migrationFunction, targetComponent);
3217
+ let processed = false;
3218
+ for (const migrationFile of migrationFiles) {
3219
+ const migrationFunction = await this.getOrLoadMigrationFunction(migrationFile);
3220
+ if (!migrationFunction) {
3221
+ this.results.failed.push({
3222
+ storyId: story.id,
3223
+ migrationNames,
3224
+ error: new Error(`Failed to load migration function from file "${migrationFile.name}"`)
3225
+ });
3226
+ return null;
3227
+ }
3228
+ const targetComponent = this.options.componentName || getComponentNameFromFilename(migrationFile.name);
3229
+ processed = applyMigrationToAllBlocks(storyContent, migrationFunction, targetComponent);
3230
+ }
3225
3231
  const newContentHash = hash(storyContent);
3226
3232
  const contentChanged = originalContentHash !== newContentHash;
3227
3233
  if (processed && contentChanged) {
3234
+ await saveRollbackData({
3235
+ space: this.options.space,
3236
+ path: this.options.path,
3237
+ story: { id: story.id, name: story.name || "", content: story.content },
3238
+ migrationTimestamp: this.timestamp,
3239
+ migrationNames
3240
+ });
3228
3241
  this.results.successful.push({
3229
3242
  storyId: story.id,
3230
3243
  name: story.name,
3231
- migrationName: migrationFile.name,
3244
+ migrationNames,
3232
3245
  content: storyContent
3233
3246
  });
3234
3247
  return {
@@ -3240,24 +3253,28 @@ class MigrationStream extends Transform {
3240
3253
  this.results.skipped.push({
3241
3254
  storyId: story.id,
3242
3255
  name: story.name,
3243
- migrationName: migrationFile.name,
3256
+ migrationNames,
3244
3257
  reason: "No changes detected after migration"
3245
3258
  });
3246
3259
  return null;
3247
3260
  } else {
3248
- const baseComponent = targetComponent.split(".")[0];
3261
+ const reason = migrationFiles.map((migrationFile) => {
3262
+ const targetComponent = this.options.componentName || getComponentNameFromFilename(migrationFile.name);
3263
+ const baseComponent = targetComponent.split(".")[0];
3264
+ return baseComponent === this.options.componentName ? `No matching components found for ${migrationFile.name}` : `Different component target ${migrationFile.name}`;
3265
+ }).join("\n");
3249
3266
  this.results.skipped.push({
3250
3267
  storyId: story.id,
3251
3268
  name: story.name,
3252
- migrationName: migrationFile.name,
3253
- reason: baseComponent === this.options.componentName ? "No matching components found" : "Different component target"
3269
+ migrationNames,
3270
+ reason
3254
3271
  });
3255
3272
  return null;
3256
3273
  }
3257
3274
  } catch (error) {
3258
3275
  this.results.failed.push({
3259
3276
  storyId: story.id,
3260
- migrationName: migrationFile.name,
3277
+ migrationNames,
3261
3278
  error
3262
3279
  });
3263
3280
  return null;
@@ -3355,8 +3372,9 @@ class UpdateStream extends Writable {
3355
3372
  } else if (this.options.publish === "all") {
3356
3373
  payload.publish = 1;
3357
3374
  }
3358
- const updatedStory = await updateStory(this.options.space, storyId, payload);
3359
- if (updatedStory) {
3375
+ const updatedStory = !this.options.dryRun && await updateStory(this.options.space, storyId, payload);
3376
+ const isStoryUpdated = Boolean(updatedStory);
3377
+ if (isStoryUpdated || this.options.dryRun) {
3360
3378
  this.results.successful.push({ storyId, name: storyName });
3361
3379
  this.results.totalProcessed++;
3362
3380
  this.options.onProgress?.(this.results.totalProcessed);
@@ -3415,6 +3433,10 @@ class UpdateStream extends Writable {
3415
3433
  const program$8 = getProgram();
3416
3434
  migrationsCommand.command("run [componentName]").description("Run migrations").option("--fi, --filter <filter>", "glob filter to apply to the components before pushing").option("-d, --dry-run", "Preview changes without applying them to Storyblok").option("-q, --query <query>", 'Filter stories by content attributes using Storyblok filter query syntax. Example: --query="[highlighted][in]=true"').option("--starts-with <path>", 'Filter stories by path. Example: --starts-with="/en/blog/"').option("--publish <publish>", "Options for publication mode: all | published | published-with-changes").action(async (componentName, options) => {
3417
3435
  konsola.title(`${commands.MIGRATIONS}`, colorPalette.MIGRATIONS, componentName ? `Running migrations for component ${componentName}...` : "Running migrations...");
3436
+ if (options.dryRun) {
3437
+ konsola.warn(`DRY RUN MODE ENABLED: No changes will be made.
3438
+ `);
3439
+ }
3418
3440
  const verbose = program$8.opts().verbose;
3419
3441
  const { filter, dryRun = false, query, startsWith, publish } = options;
3420
3442
  const { space, path } = migrationsCommand.opts();
@@ -4001,6 +4023,7 @@ const storyblokSchemas = /* @__PURE__ */ new Map([
4001
4023
  ]);
4002
4024
 
4003
4025
  const STORY_TYPE = "ISbStoryData";
4026
+ const DEFAULT_COMPONENT_FILENAME = "storyblok-components";
4004
4027
  const DEFAULT_TYPEDEFS_HEADER = [
4005
4028
  "// This file was generated by the storyblok CLI.",
4006
4029
  "// DO NOT MODIFY THIS FILE BY HAND."
@@ -4263,8 +4286,8 @@ const generateTypes = async (spaceData, options = {
4263
4286
  handleError(error);
4264
4287
  }
4265
4288
  };
4266
- const saveTypesToFile = async (space, typedefString, options) => {
4267
- const { filename = "storyblok-components", path } = options;
4289
+ const saveTypesToComponentsFile = async (space, typedefString, options) => {
4290
+ const { filename = DEFAULT_COMPONENT_FILENAME, path } = options;
4268
4291
  const resolvedPath = path ? resolve(process.cwd(), path, "types", space) : resolvePath(path, `types/${space}`);
4269
4292
  try {
4270
4293
  await saveToFile(join(resolvedPath, `${filename}.d.ts`), typedefString);
@@ -4273,7 +4296,7 @@ const saveTypesToFile = async (space, typedefString, options) => {
4273
4296
  }
4274
4297
  };
4275
4298
  const generateStoryblokTypes = async (options = {}) => {
4276
- const { filename = "storyblok", path } = options;
4299
+ const { path } = options;
4277
4300
  try {
4278
4301
  const storyblokTypesPath = resolve(__dirname, "./index.d.ts");
4279
4302
  const storyblokTypesContent = readFileSync(storyblokTypesPath, "utf-8");
@@ -4284,7 +4307,7 @@ const generateStoryblokTypes = async (options = {}) => {
4284
4307
  storyblokTypesContent
4285
4308
  ].join("\n");
4286
4309
  const resolvedPath = path ? resolve(process.cwd(), path, "types") : resolvePath(path, "types");
4287
- await saveToFile(join(resolvedPath, `${filename}.d.ts`), typeDefs);
4310
+ await saveToFile(join(resolvedPath, `storyblok.d.ts`), typeDefs);
4288
4311
  return true;
4289
4312
  } catch (error) {
4290
4313
  handleFileSystemError("read", error);
@@ -4293,7 +4316,10 @@ const generateStoryblokTypes = async (options = {}) => {
4293
4316
  };
4294
4317
 
4295
4318
  const program$5 = getProgram();
4296
- typesCommand.command("generate").description("Generate types d.ts for your component schemas").option("--sf, --separate-files", "").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").action(async (options) => {
4319
+ typesCommand.command("generate").description("Generate types d.ts for your component schemas").option("--sf, --separate-files", "Generate one .d.ts file per component instead of a single combined file").option(
4320
+ "--filename <name>",
4321
+ "Base file name for all component types when generating a single declarations file (e.g. components.d.ts). Ignored when using --separate-files."
4322
+ ).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").action(async (options) => {
4297
4323
  konsola.title(`${commands.TYPES}`, colorPalette.TYPES, "Generating types...");
4298
4324
  const verbose = program$5.opts().verbose;
4299
4325
  const { space, path } = typesCommand.opts();
@@ -4308,7 +4334,6 @@ typesCommand.command("generate").description("Generate types d.ts for your compo
4308
4334
  path
4309
4335
  });
4310
4336
  await generateStoryblokTypes({
4311
- ...options,
4312
4337
  path
4313
4338
  });
4314
4339
  const spaceDataWithDatasources = {
@@ -4320,8 +4345,8 @@ typesCommand.command("generate").description("Generate types d.ts for your compo
4320
4345
  path
4321
4346
  });
4322
4347
  if (typedefString) {
4323
- await saveTypesToFile(space, typedefString, {
4324
- ...options,
4348
+ await saveTypesToComponentsFile(space, typedefString, {
4349
+ filename: options.filename,
4325
4350
  path
4326
4351
  });
4327
4352
  }
@@ -5157,7 +5182,7 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
5157
5182
  konsola.br();
5158
5183
  });
5159
5184
 
5160
- const version = "4.6.4";
5185
+ const version = "4.6.7";
5161
5186
  const pkg = {
5162
5187
  version: version};
5163
5188