storyblok 4.7.0 → 4.9.0
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 +748 -298
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -3
package/dist/index.mjs
CHANGED
|
@@ -5,21 +5,21 @@ import { dirname } from 'pathe';
|
|
|
5
5
|
import chalk from 'chalk';
|
|
6
6
|
import { Command } from 'commander';
|
|
7
7
|
import { readPackageUp } from 'read-package-up';
|
|
8
|
+
import path, { join, resolve, parse, dirname as dirname$1, extname } from 'node:path';
|
|
9
|
+
import { MultiBar, Presets } from 'cli-progress';
|
|
8
10
|
import { Spinner } from '@topcli/spinner';
|
|
11
|
+
import fs, { mkdir, writeFile, readFile as readFile$1, appendFile, access, readdir } from 'node:fs/promises';
|
|
12
|
+
import filenamify from 'filenamify';
|
|
13
|
+
import { mkdirSync, appendFileSync, existsSync, readdirSync, unlinkSync, readFileSync } from 'node:fs';
|
|
9
14
|
import { select, password, input, confirm } from '@inquirer/prompts';
|
|
10
15
|
import { ManagementApiClient } from '@storyblok/management-api-client';
|
|
11
16
|
import { RateLimit, Sema } from 'async-sema';
|
|
12
|
-
import fs, { mkdir, writeFile, readFile as readFile$1, appendFile, access, readdir } from 'node:fs/promises';
|
|
13
|
-
import path, { join, parse, resolve } from 'node:path';
|
|
14
|
-
import filenamify from 'filenamify';
|
|
15
17
|
import { exec, spawn } from 'node:child_process';
|
|
16
18
|
import { promisify } from 'node:util';
|
|
17
19
|
import { minimatch } from 'minimatch';
|
|
18
20
|
import { Readable, pipeline, Transform, Writable } from 'node:stream';
|
|
19
21
|
import { hash } from 'ohash';
|
|
20
|
-
import { MultiBar, Presets } from 'cli-progress';
|
|
21
22
|
import { compile } from 'json-schema-to-typescript';
|
|
22
|
-
import { readFileSync } from 'node:fs';
|
|
23
23
|
import open from 'open';
|
|
24
24
|
import { Octokit } from 'octokit';
|
|
25
25
|
|
|
@@ -33,7 +33,8 @@ const commands = {
|
|
|
33
33
|
MIGRATIONS: "migrations",
|
|
34
34
|
TYPES: "types",
|
|
35
35
|
DATASOURCES: "datasources",
|
|
36
|
-
CREATE: "create"
|
|
36
|
+
CREATE: "create",
|
|
37
|
+
LOGS: "logs"
|
|
37
38
|
};
|
|
38
39
|
const colorPalette = {
|
|
39
40
|
PRIMARY: "#8d60ff",
|
|
@@ -49,7 +50,8 @@ const colorPalette = {
|
|
|
49
50
|
GROUPS: "#4ade80",
|
|
50
51
|
TAGS: "#fbbf24",
|
|
51
52
|
PRESETS: "#a855f7",
|
|
52
|
-
DATASOURCES: "#4ade80"
|
|
53
|
+
DATASOURCES: "#4ade80",
|
|
54
|
+
LOGS: "#4ade80"
|
|
53
55
|
};
|
|
54
56
|
const regions = {
|
|
55
57
|
EU: "eu",
|
|
@@ -89,6 +91,9 @@ const regionNames = {
|
|
|
89
91
|
({
|
|
90
92
|
SB_Agent_Version: process.env.npm_package_version || "4.x"
|
|
91
93
|
});
|
|
94
|
+
const directories = {
|
|
95
|
+
log: "logs"
|
|
96
|
+
};
|
|
92
97
|
|
|
93
98
|
class FetchError extends Error {
|
|
94
99
|
response;
|
|
@@ -279,6 +284,51 @@ class CommandError extends Error {
|
|
|
279
284
|
}
|
|
280
285
|
}
|
|
281
286
|
|
|
287
|
+
class Logger {
|
|
288
|
+
transports = [];
|
|
289
|
+
context = {};
|
|
290
|
+
constructor(options) {
|
|
291
|
+
if (options?.transports) {
|
|
292
|
+
this.transports = options.transports;
|
|
293
|
+
}
|
|
294
|
+
if (options?.context) {
|
|
295
|
+
this.context = options.context;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
log(level, message, context) {
|
|
299
|
+
const timestamp = /* @__PURE__ */ new Date();
|
|
300
|
+
const mergedContext = context ? { ...this.context, ...context } : this.context;
|
|
301
|
+
const record = {
|
|
302
|
+
timestamp,
|
|
303
|
+
level,
|
|
304
|
+
message,
|
|
305
|
+
context: Object.keys(mergedContext).length ? mergedContext : void 0
|
|
306
|
+
};
|
|
307
|
+
for (const transport of this.transports) {
|
|
308
|
+
transport.log(record);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
error(message, context) {
|
|
312
|
+
this.log("error", message, context);
|
|
313
|
+
}
|
|
314
|
+
warn(message, context) {
|
|
315
|
+
this.log("warn", message, context);
|
|
316
|
+
}
|
|
317
|
+
info(message, context) {
|
|
318
|
+
this.log("info", message, context);
|
|
319
|
+
}
|
|
320
|
+
debug(message, context) {
|
|
321
|
+
this.log("debug", message, context);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
let loggerInstance = null;
|
|
325
|
+
function getLogger(options) {
|
|
326
|
+
if (!loggerInstance) {
|
|
327
|
+
loggerInstance = new Logger(options);
|
|
328
|
+
}
|
|
329
|
+
return loggerInstance;
|
|
330
|
+
}
|
|
331
|
+
|
|
282
332
|
const FS_ERRORS = {
|
|
283
333
|
file_not_found: "The file requested was not found",
|
|
284
334
|
permission_denied: "Permission denied while accessing the file",
|
|
@@ -359,6 +409,25 @@ class FileSystemError extends Error {
|
|
|
359
409
|
}
|
|
360
410
|
}
|
|
361
411
|
|
|
412
|
+
function hasMessage(error) {
|
|
413
|
+
return typeof error === "object" && error !== null && "message" in error && typeof error.message === "string";
|
|
414
|
+
}
|
|
415
|
+
function toError(maybeError) {
|
|
416
|
+
if (maybeError instanceof Error) {
|
|
417
|
+
return maybeError;
|
|
418
|
+
}
|
|
419
|
+
if (typeof maybeError === "string") {
|
|
420
|
+
return new Error(maybeError);
|
|
421
|
+
}
|
|
422
|
+
if (hasMessage(maybeError)) {
|
|
423
|
+
return new Error(maybeError.message);
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
return new Error(JSON.stringify(maybeError));
|
|
427
|
+
} catch {
|
|
428
|
+
return new Error(String(maybeError));
|
|
429
|
+
}
|
|
430
|
+
}
|
|
362
431
|
function handleVerboseError(error) {
|
|
363
432
|
if (error instanceof CommandError || error instanceof APIError || error instanceof FileSystemError) {
|
|
364
433
|
const errorDetails = "getInfo" in error ? error.getInfo() : {};
|
|
@@ -398,6 +467,7 @@ function handleError(error, verbose = false) {
|
|
|
398
467
|
if (!process.env.VITEST) {
|
|
399
468
|
console.log("");
|
|
400
469
|
}
|
|
470
|
+
getLogger().error(error.message, { error, errorCode: "code" in error ? String(error.code) : "UNKNOWN_ERROR" });
|
|
401
471
|
}
|
|
402
472
|
|
|
403
473
|
function requireAuthentication(state, verbose = false) {
|
|
@@ -503,6 +573,311 @@ function isRegion(value) {
|
|
|
503
573
|
}
|
|
504
574
|
const isVitest = process.env.VITEST === "true";
|
|
505
575
|
|
|
576
|
+
const noopProgressBar = {
|
|
577
|
+
increment: () => {
|
|
578
|
+
},
|
|
579
|
+
setTotal: (_n) => {
|
|
580
|
+
},
|
|
581
|
+
stop: () => {
|
|
582
|
+
}
|
|
583
|
+
};
|
|
584
|
+
const noopSpinner = {
|
|
585
|
+
failed: (_title) => {
|
|
586
|
+
},
|
|
587
|
+
succeed: (_title) => {
|
|
588
|
+
},
|
|
589
|
+
elapsedTime: 0
|
|
590
|
+
};
|
|
591
|
+
class UI {
|
|
592
|
+
console;
|
|
593
|
+
enabled;
|
|
594
|
+
multiBar;
|
|
595
|
+
constructor({ enabled }) {
|
|
596
|
+
this.console = enabled ? console : null;
|
|
597
|
+
this.enabled = enabled;
|
|
598
|
+
this.multiBar = enabled ? new MultiBar({
|
|
599
|
+
clearOnComplete: false,
|
|
600
|
+
format: `${chalk.bold(" {title} ")} ${chalk.hex(colorPalette.PRIMARY)("[{bar}]")} {percentage}% | {eta_formatted} | {value}/{total} processed`,
|
|
601
|
+
etaBuffer: 60
|
|
602
|
+
}, Presets.rect) : null;
|
|
603
|
+
}
|
|
604
|
+
title(message, color, subtitle) {
|
|
605
|
+
if (subtitle) {
|
|
606
|
+
this.console?.log(`${chalk.bgHex(color).bold(` ${capitalize(message)} `)} ${subtitle}`);
|
|
607
|
+
} else {
|
|
608
|
+
this.console?.log(chalk.bgHex(color).bold(` ${capitalize(message)} `));
|
|
609
|
+
}
|
|
610
|
+
this.br();
|
|
611
|
+
this.br();
|
|
612
|
+
}
|
|
613
|
+
br() {
|
|
614
|
+
this.console?.log("");
|
|
615
|
+
}
|
|
616
|
+
ok(message, header = false) {
|
|
617
|
+
if (header) {
|
|
618
|
+
this.br();
|
|
619
|
+
const successHeader = chalk.bgGreen.bold.white(` Success `);
|
|
620
|
+
this.console?.log(successHeader);
|
|
621
|
+
this.br();
|
|
622
|
+
}
|
|
623
|
+
this.console?.log(message ? `${chalk.green("\u2714")} ${message}` : "");
|
|
624
|
+
}
|
|
625
|
+
info(message, options = {
|
|
626
|
+
header: false,
|
|
627
|
+
margin: true
|
|
628
|
+
}) {
|
|
629
|
+
if (options.header) {
|
|
630
|
+
this.br();
|
|
631
|
+
const infoHeader = chalk.bgBlue.bold.white(` Info `);
|
|
632
|
+
this.console?.info(infoHeader);
|
|
633
|
+
}
|
|
634
|
+
this.console?.info(message ? `${chalk.blue("\u2139")} ${message}` : "");
|
|
635
|
+
if (options.margin) {
|
|
636
|
+
this.br();
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
warn(message, header = false) {
|
|
640
|
+
if (header) {
|
|
641
|
+
this.br();
|
|
642
|
+
const warnHeader = chalk.bgYellow.bold.black(` Warning `);
|
|
643
|
+
this.console?.warn(warnHeader);
|
|
644
|
+
}
|
|
645
|
+
this.console?.warn(message ? `${chalk.yellow("\u26A0\uFE0F ")} ${message}` : "");
|
|
646
|
+
}
|
|
647
|
+
error(message, info, options = {
|
|
648
|
+
header: false,
|
|
649
|
+
margin: false
|
|
650
|
+
}) {
|
|
651
|
+
if (options.header) {
|
|
652
|
+
const errorHeader = chalk.bgRed.bold.white(` Error `);
|
|
653
|
+
this.console?.error(errorHeader);
|
|
654
|
+
this.br();
|
|
655
|
+
}
|
|
656
|
+
this.console?.error(`${chalk.red.bold("\u25B2 error")} ${message}`, info || "");
|
|
657
|
+
if (options.margin) {
|
|
658
|
+
this.br();
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
list(items) {
|
|
662
|
+
for (const item of items) {
|
|
663
|
+
this.console?.log(` ${item}`);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
createProgressBar(options) {
|
|
667
|
+
return this.multiBar?.create(0, 0, options) || noopProgressBar;
|
|
668
|
+
}
|
|
669
|
+
stopAllProgressBars() {
|
|
670
|
+
this.multiBar?.stop();
|
|
671
|
+
}
|
|
672
|
+
createSpinner(title) {
|
|
673
|
+
return this.enabled ? new Spinner({
|
|
674
|
+
verbose: !isVitest
|
|
675
|
+
}).start(title) : noopSpinner;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
let uiInstance = null;
|
|
679
|
+
function getUI(options = { enabled: false }) {
|
|
680
|
+
if (!uiInstance) {
|
|
681
|
+
uiInstance = new UI(options);
|
|
682
|
+
}
|
|
683
|
+
return uiInstance;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
const getStoryblokGlobalPath = () => {
|
|
687
|
+
const homeDirectory = process.env[process.platform.startsWith("win") ? "USERPROFILE" : "HOME"] || process.cwd();
|
|
688
|
+
return join(homeDirectory, ".storyblok");
|
|
689
|
+
};
|
|
690
|
+
const saveToFile = async (filePath, data, options) => {
|
|
691
|
+
const resolvedPath = parse(filePath).dir;
|
|
692
|
+
try {
|
|
693
|
+
await mkdir(resolvedPath, { recursive: true });
|
|
694
|
+
} catch (mkdirError) {
|
|
695
|
+
handleFileSystemError("mkdir", mkdirError);
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
try {
|
|
699
|
+
await writeFile(filePath, data, options);
|
|
700
|
+
} catch (writeError) {
|
|
701
|
+
handleFileSystemError("write", writeError);
|
|
702
|
+
}
|
|
703
|
+
};
|
|
704
|
+
const appendToFile = async (filePath, data, options) => {
|
|
705
|
+
const resolvedPath = parse(filePath).dir;
|
|
706
|
+
try {
|
|
707
|
+
await mkdir(resolvedPath, { recursive: true });
|
|
708
|
+
} catch (mkdirError) {
|
|
709
|
+
handleFileSystemError("mkdir", mkdirError);
|
|
710
|
+
return;
|
|
711
|
+
}
|
|
712
|
+
try {
|
|
713
|
+
const dataWithNewline = data.endsWith("\n") ? data : `${data}
|
|
714
|
+
`;
|
|
715
|
+
await appendFile(filePath, dataWithNewline, options);
|
|
716
|
+
} catch (writeError) {
|
|
717
|
+
handleFileSystemError("write", writeError);
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
const appendToFileSync = (filePath, data, options) => {
|
|
721
|
+
const resolvedPath = parse(filePath).dir;
|
|
722
|
+
try {
|
|
723
|
+
mkdirSync(resolvedPath, { recursive: true });
|
|
724
|
+
} catch (mkdirError) {
|
|
725
|
+
handleFileSystemError("mkdir", mkdirError);
|
|
726
|
+
return;
|
|
727
|
+
}
|
|
728
|
+
try {
|
|
729
|
+
const dataWithNewline = data.endsWith("\n") ? data : `${data}
|
|
730
|
+
`;
|
|
731
|
+
appendFileSync(filePath, dataWithNewline, options);
|
|
732
|
+
} catch (writeError) {
|
|
733
|
+
handleFileSystemError("write", writeError);
|
|
734
|
+
}
|
|
735
|
+
};
|
|
736
|
+
const readFile = async (filePath) => {
|
|
737
|
+
try {
|
|
738
|
+
return await readFile$1(filePath, "utf8");
|
|
739
|
+
} catch (error) {
|
|
740
|
+
handleFileSystemError("read", error);
|
|
741
|
+
return "";
|
|
742
|
+
}
|
|
743
|
+
};
|
|
744
|
+
const resolvePath = (path, folder) => {
|
|
745
|
+
if (path) {
|
|
746
|
+
return resolve(process.cwd(), path, folder);
|
|
747
|
+
}
|
|
748
|
+
return resolve(resolve(process.cwd(), ".storyblok"), folder);
|
|
749
|
+
};
|
|
750
|
+
const getComponentNameFromFilename = (filename) => {
|
|
751
|
+
return filename.replace(/\.js$/, "");
|
|
752
|
+
};
|
|
753
|
+
const sanitizeFilename = (filename) => {
|
|
754
|
+
return filenamify(filename, {
|
|
755
|
+
replacement: "_"
|
|
756
|
+
});
|
|
757
|
+
};
|
|
758
|
+
async function readJsonFile(filePath) {
|
|
759
|
+
try {
|
|
760
|
+
const content = (await readFile(filePath)).toString();
|
|
761
|
+
if (!content) {
|
|
762
|
+
return { data: [] };
|
|
763
|
+
}
|
|
764
|
+
const parsed = JSON.parse(content);
|
|
765
|
+
return { data: Array.isArray(parsed) ? parsed : [parsed] };
|
|
766
|
+
} catch (error) {
|
|
767
|
+
return { data: [], error };
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
function importModule(filePath) {
|
|
771
|
+
return import(`file://${filePath}`);
|
|
772
|
+
}
|
|
773
|
+
function getLogsPath(logFileDir, space, baseDir) {
|
|
774
|
+
if (space) {
|
|
775
|
+
return resolvePath(baseDir, join(logFileDir, space));
|
|
776
|
+
}
|
|
777
|
+
return resolvePath(baseDir, logFileDir);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
class FileTransport {
|
|
781
|
+
filePath;
|
|
782
|
+
level;
|
|
783
|
+
maxFiles;
|
|
784
|
+
hasPruned = false;
|
|
785
|
+
constructor(options) {
|
|
786
|
+
this.filePath = options?.filePath ?? `./${Date.now()}.jsonl`;
|
|
787
|
+
this.level = options?.level ?? "info";
|
|
788
|
+
this.maxFiles = options?.maxFiles;
|
|
789
|
+
}
|
|
790
|
+
log(record) {
|
|
791
|
+
if (!this.shouldLog(record.level)) {
|
|
792
|
+
return;
|
|
793
|
+
}
|
|
794
|
+
const line = this.format(record);
|
|
795
|
+
appendToFileSync(this.filePath, line);
|
|
796
|
+
if (!this.hasPruned && this.maxFiles !== void 0) {
|
|
797
|
+
this.hasPruned = true;
|
|
798
|
+
this.pruneOldFiles();
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
pruneOldFiles() {
|
|
802
|
+
if (this.maxFiles === void 0) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
const dir = dirname$1(this.filePath);
|
|
806
|
+
const ext = extname(this.filePath);
|
|
807
|
+
FileTransport.pruneLogFiles(dir, this.maxFiles, ext);
|
|
808
|
+
}
|
|
809
|
+
static pruneLogFiles(directory, keep, extension = ".jsonl") {
|
|
810
|
+
if (!existsSync(directory)) {
|
|
811
|
+
return 0;
|
|
812
|
+
}
|
|
813
|
+
const files = readdirSync(directory).filter((file) => extname(file) === extension).sort();
|
|
814
|
+
const filesToDelete = files.length - keep;
|
|
815
|
+
if (filesToDelete <= 0) {
|
|
816
|
+
return 0;
|
|
817
|
+
}
|
|
818
|
+
for (const file of files.slice(0, filesToDelete)) {
|
|
819
|
+
unlinkSync(join(directory, file));
|
|
820
|
+
}
|
|
821
|
+
return filesToDelete;
|
|
822
|
+
}
|
|
823
|
+
static listLogFiles(directory, extension = ".jsonl") {
|
|
824
|
+
if (!existsSync(directory)) {
|
|
825
|
+
return [];
|
|
826
|
+
}
|
|
827
|
+
const files = readdirSync(directory).filter((file) => extname(file) === extension).sort();
|
|
828
|
+
return files.map((f) => join(directory, f).replace(process.cwd(), "."));
|
|
829
|
+
}
|
|
830
|
+
levelRank(level) {
|
|
831
|
+
switch (level) {
|
|
832
|
+
case "error":
|
|
833
|
+
return 0;
|
|
834
|
+
case "warn":
|
|
835
|
+
return 1;
|
|
836
|
+
case "info":
|
|
837
|
+
return 2;
|
|
838
|
+
case "debug":
|
|
839
|
+
return 3;
|
|
840
|
+
default:
|
|
841
|
+
return 3;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
shouldLog(level) {
|
|
845
|
+
return this.levelRank(level) <= this.levelRank(this.level);
|
|
846
|
+
}
|
|
847
|
+
format(record) {
|
|
848
|
+
const timestamp = (record.timestamp ?? /* @__PURE__ */ new Date()).toISOString();
|
|
849
|
+
const level = record.level.toUpperCase();
|
|
850
|
+
const message = record.message.replaceAll("\n", "\\n");
|
|
851
|
+
const contextNormalized = record.context && this.formatContext(record.context);
|
|
852
|
+
return JSON.stringify({ timestamp, level, message, context: contextNormalized });
|
|
853
|
+
}
|
|
854
|
+
formatContext(context) {
|
|
855
|
+
const contextNormalized = {};
|
|
856
|
+
for (const [key, value] of Object.entries(context)) {
|
|
857
|
+
if (value instanceof APIError) {
|
|
858
|
+
contextNormalized[key] = {
|
|
859
|
+
name: value.name,
|
|
860
|
+
message: value.message,
|
|
861
|
+
httpCode: value.code,
|
|
862
|
+
httpStatusText: value.error?.response.statusText,
|
|
863
|
+
stack: value.stack
|
|
864
|
+
};
|
|
865
|
+
continue;
|
|
866
|
+
}
|
|
867
|
+
if (value instanceof Error) {
|
|
868
|
+
contextNormalized[key] = {
|
|
869
|
+
name: value.name,
|
|
870
|
+
message: value.message,
|
|
871
|
+
stack: value.stack
|
|
872
|
+
};
|
|
873
|
+
continue;
|
|
874
|
+
}
|
|
875
|
+
contextNormalized[key] = value;
|
|
876
|
+
}
|
|
877
|
+
return contextNormalized;
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
|
|
506
881
|
let packageJson;
|
|
507
882
|
const result = await readPackageUp({
|
|
508
883
|
cwd: __dirname
|
|
@@ -521,7 +896,28 @@ let programInstance = null;
|
|
|
521
896
|
function getProgram() {
|
|
522
897
|
if (!programInstance) {
|
|
523
898
|
programInstance = new Command();
|
|
524
|
-
programInstance.name(packageJson.name).description(packageJson.description || "").version(packageJson.version)
|
|
899
|
+
programInstance.name(packageJson.name).description(packageJson.description || "").version(packageJson.version).hook("preAction", (_, actionCmd) => {
|
|
900
|
+
const options = actionCmd.optsWithGlobals();
|
|
901
|
+
const commandPieces = [];
|
|
902
|
+
for (let c = actionCmd; c; c = c.parent) {
|
|
903
|
+
commandPieces.unshift(c.name());
|
|
904
|
+
}
|
|
905
|
+
const command = commandPieces.join(" ");
|
|
906
|
+
const runId = Date.now();
|
|
907
|
+
const transports = [];
|
|
908
|
+
const logsPath = getLogsPath(directories.log, options.space, options.path);
|
|
909
|
+
const logFilename = `${commandPieces.join("-")}-${runId}.jsonl`;
|
|
910
|
+
const filePath = path.join(logsPath, logFilename);
|
|
911
|
+
transports.push(new FileTransport({
|
|
912
|
+
filePath,
|
|
913
|
+
maxFiles: 10
|
|
914
|
+
}));
|
|
915
|
+
getLogger({
|
|
916
|
+
context: { runId, command, options, cliVersion: packageJson.version },
|
|
917
|
+
transports
|
|
918
|
+
});
|
|
919
|
+
getUI({ enabled: true });
|
|
920
|
+
});
|
|
525
921
|
programInstance.configureOutput({
|
|
526
922
|
writeErr: (str) => handleError(new Error(str))
|
|
527
923
|
});
|
|
@@ -650,75 +1046,6 @@ const loginWithOtp = async (email, password, otp, region) => {
|
|
|
650
1046
|
}
|
|
651
1047
|
};
|
|
652
1048
|
|
|
653
|
-
const getStoryblokGlobalPath = () => {
|
|
654
|
-
const homeDirectory = process.env[process.platform.startsWith("win") ? "USERPROFILE" : "HOME"] || process.cwd();
|
|
655
|
-
return join(homeDirectory, ".storyblok");
|
|
656
|
-
};
|
|
657
|
-
const saveToFile = async (filePath, data, options) => {
|
|
658
|
-
const resolvedPath = parse(filePath).dir;
|
|
659
|
-
try {
|
|
660
|
-
await mkdir(resolvedPath, { recursive: true });
|
|
661
|
-
} catch (mkdirError) {
|
|
662
|
-
handleFileSystemError("mkdir", mkdirError);
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
try {
|
|
666
|
-
await writeFile(filePath, data, options);
|
|
667
|
-
} catch (writeError) {
|
|
668
|
-
handleFileSystemError("write", writeError);
|
|
669
|
-
}
|
|
670
|
-
};
|
|
671
|
-
const appendToFile = async (filePath, data, options) => {
|
|
672
|
-
const resolvedPath = parse(filePath).dir;
|
|
673
|
-
try {
|
|
674
|
-
await mkdir(resolvedPath, { recursive: true });
|
|
675
|
-
} catch (mkdirError) {
|
|
676
|
-
handleFileSystemError("mkdir", mkdirError);
|
|
677
|
-
return;
|
|
678
|
-
}
|
|
679
|
-
try {
|
|
680
|
-
const dataWithNewline = data.endsWith("\n") ? data : `${data}
|
|
681
|
-
`;
|
|
682
|
-
await appendFile(filePath, dataWithNewline, options);
|
|
683
|
-
} catch (writeError) {
|
|
684
|
-
handleFileSystemError("write", writeError);
|
|
685
|
-
}
|
|
686
|
-
};
|
|
687
|
-
const readFile = async (filePath) => {
|
|
688
|
-
try {
|
|
689
|
-
return await readFile$1(filePath, "utf8");
|
|
690
|
-
} catch (error) {
|
|
691
|
-
handleFileSystemError("read", error);
|
|
692
|
-
return "";
|
|
693
|
-
}
|
|
694
|
-
};
|
|
695
|
-
const resolvePath = (path, folder) => {
|
|
696
|
-
if (path) {
|
|
697
|
-
return resolve(process.cwd(), path, folder);
|
|
698
|
-
}
|
|
699
|
-
return resolve(resolve(process.cwd(), ".storyblok"), folder);
|
|
700
|
-
};
|
|
701
|
-
const getComponentNameFromFilename = (filename) => {
|
|
702
|
-
return filename.replace(/\.js$/, "");
|
|
703
|
-
};
|
|
704
|
-
const sanitizeFilename = (filename) => {
|
|
705
|
-
return filenamify(filename, {
|
|
706
|
-
replacement: "_"
|
|
707
|
-
});
|
|
708
|
-
};
|
|
709
|
-
async function readJsonFile(filePath) {
|
|
710
|
-
try {
|
|
711
|
-
const content = (await readFile(filePath)).toString();
|
|
712
|
-
if (!content) {
|
|
713
|
-
return { data: [] };
|
|
714
|
-
}
|
|
715
|
-
const parsed = JSON.parse(content);
|
|
716
|
-
return { data: Array.isArray(parsed) ? parsed : [parsed] };
|
|
717
|
-
} catch (error) {
|
|
718
|
-
return { data: [], error };
|
|
719
|
-
}
|
|
720
|
-
}
|
|
721
|
-
|
|
722
1049
|
const getCredentials = async (filePath = join(getStoryblokGlobalPath(), "credentials.json")) => {
|
|
723
1050
|
try {
|
|
724
1051
|
await access(filePath);
|
|
@@ -845,7 +1172,7 @@ function session() {
|
|
|
845
1172
|
return sessionInstance;
|
|
846
1173
|
}
|
|
847
1174
|
|
|
848
|
-
const program$
|
|
1175
|
+
const program$g = getProgram();
|
|
849
1176
|
const allRegionsText = Object.values(regions).join(",");
|
|
850
1177
|
const loginStrategy = {
|
|
851
1178
|
message: "How would you like to login?",
|
|
@@ -862,12 +1189,12 @@ const loginStrategy = {
|
|
|
862
1189
|
}
|
|
863
1190
|
]
|
|
864
1191
|
};
|
|
865
|
-
program$
|
|
1192
|
+
program$g.command(commands.LOGIN).description("Login to the Storyblok CLI").option("-t, --token <token>", "Token to login directly without questions, like for CI environments").option(
|
|
866
1193
|
"-r, --region <region>",
|
|
867
1194
|
`The region you would like to work in. Please keep in mind that the region must match the region of your space. This region flag will be used for the other cli's commands. You can use the values: ${allRegionsText}.`
|
|
868
1195
|
).action(async (options) => {
|
|
869
1196
|
konsola.title(`${commands.LOGIN}`, colorPalette.LOGIN);
|
|
870
|
-
const verbose = program$
|
|
1197
|
+
const verbose = program$g.opts().verbose;
|
|
871
1198
|
const { token, region } = options;
|
|
872
1199
|
const { state, updateSession, persistCredentials, initializeSession } = session();
|
|
873
1200
|
await initializeSession();
|
|
@@ -995,10 +1322,10 @@ program$i.command(commands.LOGIN).description("Login to the Storyblok CLI").opti
|
|
|
995
1322
|
konsola.br();
|
|
996
1323
|
});
|
|
997
1324
|
|
|
998
|
-
const program$
|
|
999
|
-
program$
|
|
1325
|
+
const program$f = getProgram();
|
|
1326
|
+
program$f.command(commands.LOGOUT).description("Logout from the Storyblok CLI").action(async () => {
|
|
1000
1327
|
konsola.title(`${commands.LOGOUT}`, colorPalette.LOGOUT);
|
|
1001
|
-
const verbose = program$
|
|
1328
|
+
const verbose = program$f.opts().verbose;
|
|
1002
1329
|
try {
|
|
1003
1330
|
const { state, initializeSession } = session();
|
|
1004
1331
|
await initializeSession();
|
|
@@ -1046,10 +1373,10 @@ async function openSignupInBrowser(url) {
|
|
|
1046
1373
|
}
|
|
1047
1374
|
}
|
|
1048
1375
|
|
|
1049
|
-
const program$
|
|
1050
|
-
program$
|
|
1376
|
+
const program$e = getProgram();
|
|
1377
|
+
program$e.command(commands.SIGNUP).description("Sign up for Storyblok").action(async () => {
|
|
1051
1378
|
konsola.title(`${commands.SIGNUP}`, colorPalette.SIGNUP);
|
|
1052
|
-
const verbose = program$
|
|
1379
|
+
const verbose = program$e.opts().verbose;
|
|
1053
1380
|
const { state, initializeSession } = session();
|
|
1054
1381
|
await initializeSession();
|
|
1055
1382
|
if (state.isLoggedIn && !state.envLogin) {
|
|
@@ -1071,10 +1398,10 @@ program$g.command(commands.SIGNUP).description("Sign up for Storyblok").action(a
|
|
|
1071
1398
|
konsola.br();
|
|
1072
1399
|
});
|
|
1073
1400
|
|
|
1074
|
-
const program$
|
|
1075
|
-
program$
|
|
1401
|
+
const program$d = getProgram();
|
|
1402
|
+
program$d.command(commands.USER).description("Get the current user").action(async () => {
|
|
1076
1403
|
konsola.title(`${commands.USER}`, colorPalette.USER);
|
|
1077
|
-
const verbose = program$
|
|
1404
|
+
const verbose = program$d.opts().verbose;
|
|
1078
1405
|
const { state, initializeSession } = session();
|
|
1079
1406
|
await initializeSession();
|
|
1080
1407
|
if (!requireAuthentication(state)) {
|
|
@@ -1103,8 +1430,8 @@ program$f.command(commands.USER).description("Get the current user").action(asyn
|
|
|
1103
1430
|
konsola.br();
|
|
1104
1431
|
});
|
|
1105
1432
|
|
|
1106
|
-
const program$
|
|
1107
|
-
const componentsCommand = program$
|
|
1433
|
+
const program$c = getProgram();
|
|
1434
|
+
const componentsCommand = program$c.command(commands.COMPONENTS).alias("comp").description(`Manage your space's block schema`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/components");
|
|
1108
1435
|
|
|
1109
1436
|
const fetchComponents = async (spaceId) => {
|
|
1110
1437
|
try {
|
|
@@ -1484,10 +1811,10 @@ async function readConsolidatedFiles$1(resolvedPath, suffix) {
|
|
|
1484
1811
|
};
|
|
1485
1812
|
}
|
|
1486
1813
|
|
|
1487
|
-
const program$
|
|
1814
|
+
const program$b = getProgram();
|
|
1488
1815
|
componentsCommand.command("pull [componentName]").option("-f, --filename <filename>", "custom name to be used in file(s) name instead of space id").option("--sf, --separate-files", "Argument to create a single file for each component").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. components.<suffix>.json)").description(`Download your space's components schema as json. Optionally specify a component name to pull a single component.`).action(async (componentName, options) => {
|
|
1489
1816
|
konsola.title(`${commands.COMPONENTS}`, colorPalette.COMPONENTS, componentName ? `Pulling component ${componentName}...` : "Pulling components...");
|
|
1490
|
-
const verbose = program$
|
|
1817
|
+
const verbose = program$b.opts().verbose;
|
|
1491
1818
|
const { space, path } = componentsCommand.opts();
|
|
1492
1819
|
const { separateFiles, suffix, filename = "components" } = options;
|
|
1493
1820
|
const { state, initializeSession } = session();
|
|
@@ -1625,7 +1952,7 @@ function buildDependencyGraph(context) {
|
|
|
1625
1952
|
graph.nodes.set(nodeId, node);
|
|
1626
1953
|
});
|
|
1627
1954
|
spaceState.local.groups.forEach((group) => {
|
|
1628
|
-
if (group.parent_uuid) {
|
|
1955
|
+
if (group.parent_uuid && group.parent_id && group.parent_uuid !== group.uuid) {
|
|
1629
1956
|
const childId = `group:${group.uuid}`;
|
|
1630
1957
|
const parentId = `group:${group.parent_uuid}`;
|
|
1631
1958
|
addDependency(childId, parentId);
|
|
@@ -2520,12 +2847,13 @@ async function pushWithDependencyGraph(space, spaceState, maxConcurrency = 5) {
|
|
|
2520
2847
|
return results;
|
|
2521
2848
|
}
|
|
2522
2849
|
|
|
2523
|
-
const program$
|
|
2850
|
+
const program$a = getProgram();
|
|
2524
2851
|
componentsCommand.command("push [componentName]").description(`Push your space's components schema as json`).option("-f, --from <from>", "source space id").option("--fi, --filter <filter>", "glob filter to apply to the components before pushing").option("--sf, --separate-files", "Read from separate files instead of consolidated files").option("--su, --suffix <suffix>", "Suffix to add to the component name").action(async (componentName, options) => {
|
|
2525
2852
|
konsola.title(`${commands.COMPONENTS}`, colorPalette.COMPONENTS, componentName ? `Pushing component ${componentName}...` : "Pushing components...");
|
|
2526
|
-
const verbose = program$
|
|
2853
|
+
const verbose = program$a.opts().verbose;
|
|
2527
2854
|
const { space, path } = componentsCommand.opts();
|
|
2528
|
-
const {
|
|
2855
|
+
const { filter } = options;
|
|
2856
|
+
const fromSpace = options.from || space;
|
|
2529
2857
|
const { state, initializeSession } = session();
|
|
2530
2858
|
await initializeSession();
|
|
2531
2859
|
if (!requireAuthentication(state, verbose)) {
|
|
@@ -2535,10 +2863,7 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2535
2863
|
handleError(new CommandError(`Please provide the target space as argument --space TARGET_SPACE_ID.`), verbose);
|
|
2536
2864
|
return;
|
|
2537
2865
|
}
|
|
2538
|
-
|
|
2539
|
-
options.from = space;
|
|
2540
|
-
}
|
|
2541
|
-
konsola.info(`Attempting to push components ${chalk.bold("from")} space ${chalk.hex(colorPalette.COMPONENTS)(options.from)} ${chalk.bold("to")} ${chalk.hex(colorPalette.COMPONENTS)(space)}`);
|
|
2866
|
+
konsola.info(`Attempting to push components ${chalk.bold("from")} space ${chalk.hex(colorPalette.COMPONENTS)(fromSpace)} ${chalk.bold("to")} ${chalk.hex(colorPalette.COMPONENTS)(space)}`);
|
|
2542
2867
|
konsola.br();
|
|
2543
2868
|
const { password, region } = state;
|
|
2544
2869
|
let requestCount = 0;
|
|
@@ -2556,7 +2881,7 @@ componentsCommand.command("push [componentName]").description(`Push your space's
|
|
|
2556
2881
|
const componentsData = await readComponentsFiles({
|
|
2557
2882
|
...options,
|
|
2558
2883
|
path,
|
|
2559
|
-
|
|
2884
|
+
from: fromSpace
|
|
2560
2885
|
});
|
|
2561
2886
|
const localData = {
|
|
2562
2887
|
...componentsData,
|
|
@@ -2719,11 +3044,11 @@ const saveLanguagesToFile = async (space, internationalizationOptions, options)
|
|
|
2719
3044
|
}
|
|
2720
3045
|
};
|
|
2721
3046
|
|
|
2722
|
-
const program$
|
|
2723
|
-
const languagesCommand = program$
|
|
3047
|
+
const program$9 = getProgram();
|
|
3048
|
+
const languagesCommand = program$9.command(commands.LANGUAGES).alias("lang").description(`Manage your space's languages`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/languages");
|
|
2724
3049
|
languagesCommand.command("pull").description(`Download your space's languages schema as json`).option("-f, --filename <filename>", "filename to save the file as <filename>.<suffix>.json").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. languages.<suffix>.json). By default, the space ID is used.").action(async (options) => {
|
|
2725
3050
|
konsola.title(`${commands.LANGUAGES}`, colorPalette.LANGUAGES);
|
|
2726
|
-
const verbose = program$
|
|
3051
|
+
const verbose = program$9.opts().verbose;
|
|
2727
3052
|
const { space, path } = languagesCommand.opts();
|
|
2728
3053
|
const { filename = "languages", suffix = options.space } = options;
|
|
2729
3054
|
const { state, initializeSession } = session();
|
|
@@ -2770,8 +3095,8 @@ languagesCommand.command("pull").description(`Download your space's languages sc
|
|
|
2770
3095
|
konsola.br();
|
|
2771
3096
|
});
|
|
2772
3097
|
|
|
2773
|
-
const program$
|
|
2774
|
-
const migrationsCommand = program$
|
|
3098
|
+
const program$8 = getProgram();
|
|
3099
|
+
const migrationsCommand = program$8.command(commands.MIGRATIONS).alias("mig").description(`Manage your space's migrations`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/migrations");
|
|
2775
3100
|
|
|
2776
3101
|
const getMigrationTemplate = () => {
|
|
2777
3102
|
return `export default function (block) {
|
|
@@ -2799,12 +3124,19 @@ const generateMigration = async (space, path, component, suffix) => {
|
|
|
2799
3124
|
}
|
|
2800
3125
|
};
|
|
2801
3126
|
|
|
2802
|
-
const program$9 = getProgram();
|
|
2803
3127
|
migrationsCommand.command("generate [componentName]").description("Generate a migration file").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. {component-name}.<suffix>.js)").action(async (componentName, options) => {
|
|
2804
|
-
|
|
2805
|
-
const
|
|
3128
|
+
const program = getProgram();
|
|
3129
|
+
const ui = getUI();
|
|
3130
|
+
const logger = getLogger();
|
|
3131
|
+
ui.title(`${commands.MIGRATIONS}`, colorPalette.MIGRATIONS, componentName ? `Generating migration for component ${componentName}...` : "Generating migrations...");
|
|
3132
|
+
const verbose = program.opts().verbose;
|
|
2806
3133
|
const { space, path } = migrationsCommand.opts();
|
|
2807
3134
|
const { suffix } = options;
|
|
3135
|
+
logger.info("Migration generation started", {
|
|
3136
|
+
componentName,
|
|
3137
|
+
space,
|
|
3138
|
+
suffix
|
|
3139
|
+
});
|
|
2808
3140
|
if (!componentName) {
|
|
2809
3141
|
handleError(new CommandError(`Please provide the component name as argument ${chalk.hex(colorPalette.MIGRATIONS)("storyblok migrations generate YOUR_COMPONENT_NAME.")}`), verbose);
|
|
2810
3142
|
return;
|
|
@@ -2825,9 +3157,7 @@ migrationsCommand.command("generate [componentName]").description("Generate a mi
|
|
|
2825
3157
|
},
|
|
2826
3158
|
region
|
|
2827
3159
|
});
|
|
2828
|
-
const spinner =
|
|
2829
|
-
verbose: !isVitest
|
|
2830
|
-
}).start(`Generating migration for component ${componentName}...`);
|
|
3160
|
+
const spinner = ui.createSpinner(`Generating migration for component ${componentName}...`);
|
|
2831
3161
|
try {
|
|
2832
3162
|
const component = await fetchComponent(space, componentName);
|
|
2833
3163
|
if (!component) {
|
|
@@ -2839,7 +3169,13 @@ migrationsCommand.command("generate [componentName]").description("Generate a mi
|
|
|
2839
3169
|
spinner.succeed(`Migration generated for component ${chalk.hex(colorPalette.MIGRATIONS)(componentName)} - Completed in ${spinner.elapsedTime.toFixed(2)}ms`);
|
|
2840
3170
|
const fileName = suffix ? `${component.name}.${suffix}.js` : `${component.name}.js`;
|
|
2841
3171
|
const migrationPath = path ? `${path}/migrations/${space}/${fileName}` : `.storyblok/migrations/${space}/${fileName}`;
|
|
2842
|
-
|
|
3172
|
+
ui.ok(`You can find the migration file in ${chalk.hex(colorPalette.MIGRATIONS)(migrationPath)}`);
|
|
3173
|
+
logger.info("Migration generation finished", {
|
|
3174
|
+
componentName: component.name,
|
|
3175
|
+
migrationPath,
|
|
3176
|
+
space,
|
|
3177
|
+
suffix
|
|
3178
|
+
});
|
|
2843
3179
|
} catch (error) {
|
|
2844
3180
|
spinner.failed(`Failed to generate migration for component ${componentName}`);
|
|
2845
3181
|
handleError(error, verbose);
|
|
@@ -2904,6 +3240,18 @@ const updateStory = async (spaceId, storyId, payload) => {
|
|
|
2904
3240
|
}
|
|
2905
3241
|
};
|
|
2906
3242
|
|
|
3243
|
+
const ERROR_CODES = {
|
|
3244
|
+
MIGRATION_APPLY_TO_STORY_ERROR: "MIGRATION_APPLY_TO_STORY_ERROR",
|
|
3245
|
+
MIGRATION_CREATE_STORIES_PIPELINE_ERROR: "MIGRATION_CREATE_STORIES_PIPELINE_ERROR",
|
|
3246
|
+
MIGRATION_FILE_NO_DEFAULT_EXPORT: "MIGRATION_FILE_NO_DEFAULT_EXPORT",
|
|
3247
|
+
MIGRATION_FILE_NOT_FOUND: "MIGRATION_FILE_NOT_FOUND",
|
|
3248
|
+
MIGRATION_LOAD_ERROR: "MIGRATION_LOAD_ERROR",
|
|
3249
|
+
MIGRATION_STORY_CONTENT_MISSING: "MIGRATION_STORY_CONTENT_MISSING",
|
|
3250
|
+
MIGRATION_STORY_FETCH_ERROR: "MIGRATION_STORY_FETCH_ERROR",
|
|
3251
|
+
MIGRATION_STORY_UPDATE_ERROR: "MIGRATION_STORY_UPDATE_ERROR",
|
|
3252
|
+
MIGRATION_STORY_UPDATE_NULL: "MIGRATION_STORY_UPDATE_NULL"
|
|
3253
|
+
};
|
|
3254
|
+
|
|
2907
3255
|
async function* storiesIterator(spaceId, params, onTotal) {
|
|
2908
3256
|
try {
|
|
2909
3257
|
let perPage = 500;
|
|
@@ -2924,6 +3272,7 @@ async function* storiesIterator(spaceId, params, onTotal) {
|
|
|
2924
3272
|
page: 1,
|
|
2925
3273
|
story_only: true
|
|
2926
3274
|
});
|
|
3275
|
+
getLogger().info(`Fetched stories page 1 of ${perPage}`);
|
|
2927
3276
|
if (!result) {
|
|
2928
3277
|
return;
|
|
2929
3278
|
}
|
|
@@ -2941,6 +3290,7 @@ async function* storiesIterator(spaceId, params, onTotal) {
|
|
|
2941
3290
|
page,
|
|
2942
3291
|
story_only: true
|
|
2943
3292
|
});
|
|
3293
|
+
getLogger().info(`Fetched stories page ${page} of ${perPage}`);
|
|
2944
3294
|
if (!result2) {
|
|
2945
3295
|
return;
|
|
2946
3296
|
}
|
|
@@ -2965,14 +3315,20 @@ class StoriesStream extends Transform {
|
|
|
2965
3315
|
}
|
|
2966
3316
|
semaphore;
|
|
2967
3317
|
async _transform(chunk, _encoding, callback) {
|
|
2968
|
-
|
|
2969
|
-
|
|
3318
|
+
try {
|
|
3319
|
+
await this.semaphore.acquire();
|
|
3320
|
+
const story = await fetchStory(this.spaceId, chunk.id.toString());
|
|
2970
3321
|
this.push(story);
|
|
2971
3322
|
this.onProgress?.();
|
|
2972
|
-
|
|
3323
|
+
getLogger().info("Fetched story", { storyId: chunk.id });
|
|
3324
|
+
callback();
|
|
3325
|
+
} catch (maybeError) {
|
|
3326
|
+
const error = toError(maybeError);
|
|
3327
|
+
getLogger().error(error.message, { storyId: chunk.id, error, errorCode: ERROR_CODES.MIGRATION_STORY_FETCH_ERROR });
|
|
3328
|
+
callback(error);
|
|
3329
|
+
} finally {
|
|
2973
3330
|
this.semaphore.release();
|
|
2974
|
-
}
|
|
2975
|
-
callback();
|
|
3331
|
+
}
|
|
2976
3332
|
}
|
|
2977
3333
|
_flush(callback) {
|
|
2978
3334
|
this.semaphore.drain().then(() => {
|
|
@@ -2992,37 +3348,14 @@ const createStoriesStream = async ({
|
|
|
2992
3348
|
return pipeline(listStoriesStream, new StoriesStream(spaceId, batchSize, onProgress), (err) => {
|
|
2993
3349
|
if (err) {
|
|
2994
3350
|
console.error(err);
|
|
3351
|
+
getLogger().error(err.message, { errorCode: ERROR_CODES.MIGRATION_CREATE_STORIES_PIPELINE_ERROR });
|
|
2995
3352
|
}
|
|
2996
3353
|
});
|
|
2997
3354
|
};
|
|
2998
3355
|
|
|
2999
|
-
async function readJavascriptFile(filePath) {
|
|
3000
|
-
try {
|
|
3001
|
-
const content = await readFile$1(filePath, "utf-8");
|
|
3002
|
-
if (!content) {
|
|
3003
|
-
throw new FileSystemError("invalid_argument", "read", new Error(`File ${filePath} is empty`));
|
|
3004
|
-
}
|
|
3005
|
-
return content;
|
|
3006
|
-
} catch (error) {
|
|
3007
|
-
throw new FileSystemError("file_not_found", "read", error);
|
|
3008
|
-
}
|
|
3009
|
-
}
|
|
3010
3356
|
async function readMigrationFiles(options) {
|
|
3011
3357
|
const { space, path, filter } = options;
|
|
3012
3358
|
const resolvedPath = resolvePath(path, `migrations/${space}`);
|
|
3013
|
-
try {
|
|
3014
|
-
await readdir(resolvedPath);
|
|
3015
|
-
} catch (error) {
|
|
3016
|
-
const message = `No directory found for space "${space}". Please make sure you have pulled the migrations first by running:
|
|
3017
|
-
|
|
3018
|
-
storyblok migrations pull --space ${space}`;
|
|
3019
|
-
throw new FileSystemError(
|
|
3020
|
-
"file_not_found",
|
|
3021
|
-
"read",
|
|
3022
|
-
error,
|
|
3023
|
-
message
|
|
3024
|
-
);
|
|
3025
|
-
}
|
|
3026
3359
|
try {
|
|
3027
3360
|
const dirFiles = await readdir(resolvedPath);
|
|
3028
3361
|
const migrationFiles = [];
|
|
@@ -3035,20 +3368,21 @@ async function readMigrationFiles(options) {
|
|
|
3035
3368
|
if (filterRegex && !filterRegex.test(file)) {
|
|
3036
3369
|
continue;
|
|
3037
3370
|
}
|
|
3038
|
-
const filePath = join(resolvedPath, file);
|
|
3039
|
-
const content = await readJavascriptFile(filePath);
|
|
3040
3371
|
migrationFiles.push({
|
|
3041
|
-
name: file
|
|
3042
|
-
content
|
|
3372
|
+
name: file
|
|
3043
3373
|
});
|
|
3044
3374
|
}
|
|
3045
3375
|
}
|
|
3046
3376
|
return migrationFiles;
|
|
3047
3377
|
} catch (error) {
|
|
3378
|
+
const message = `No directory found for space "${space}". Please make sure you have generated migrations first by running:
|
|
3379
|
+
|
|
3380
|
+
storyblok migrations generate YOUR_COMPONENT_NAME --space ${space}`;
|
|
3048
3381
|
throw new FileSystemError(
|
|
3049
3382
|
"file_not_found",
|
|
3050
3383
|
"read",
|
|
3051
|
-
error
|
|
3384
|
+
error,
|
|
3385
|
+
message
|
|
3052
3386
|
);
|
|
3053
3387
|
}
|
|
3054
3388
|
}
|
|
@@ -3056,14 +3390,24 @@ async function getMigrationFunction(fileName, space, basePath) {
|
|
|
3056
3390
|
try {
|
|
3057
3391
|
const resolvedPath = resolvePath(basePath, `migrations/${space}`);
|
|
3058
3392
|
const filePath = join(resolvedPath, fileName);
|
|
3059
|
-
const migrationModule = await
|
|
3393
|
+
const migrationModule = await importModule(filePath);
|
|
3060
3394
|
if (typeof migrationModule.default === "function") {
|
|
3061
3395
|
return migrationModule.default;
|
|
3062
3396
|
}
|
|
3063
|
-
|
|
3397
|
+
getUI().error(`Migration file "${fileName}" does not export a default function.`);
|
|
3398
|
+
getLogger().error("Migration file does not export a default function", {
|
|
3399
|
+
fileName,
|
|
3400
|
+
errorCode: ERROR_CODES.MIGRATION_FILE_NO_DEFAULT_EXPORT
|
|
3401
|
+
});
|
|
3064
3402
|
return null;
|
|
3065
|
-
} catch (
|
|
3066
|
-
|
|
3403
|
+
} catch (maybeError) {
|
|
3404
|
+
const error = toError(maybeError);
|
|
3405
|
+
getUI().error(`Error loading migration function from "${fileName}": ${error.message}`);
|
|
3406
|
+
getLogger().error("Couldn't load migration function", {
|
|
3407
|
+
fileName,
|
|
3408
|
+
error,
|
|
3409
|
+
errorCode: ERROR_CODES.MIGRATION_LOAD_ERROR
|
|
3410
|
+
});
|
|
3067
3411
|
return null;
|
|
3068
3412
|
}
|
|
3069
3413
|
}
|
|
@@ -3200,6 +3544,10 @@ class MigrationStream extends Transform {
|
|
|
3200
3544
|
migrationNames: relevantMigrations.map((m) => m.name),
|
|
3201
3545
|
error: new Error("Story content is missing")
|
|
3202
3546
|
});
|
|
3547
|
+
getLogger().error("Failed to process story: Content is missing", {
|
|
3548
|
+
storyId: story.id,
|
|
3549
|
+
errorCode: ERROR_CODES.MIGRATION_STORY_CONTENT_MISSING
|
|
3550
|
+
});
|
|
3203
3551
|
return [];
|
|
3204
3552
|
}
|
|
3205
3553
|
const successfulResults = [];
|
|
@@ -3218,10 +3566,14 @@ class MigrationStream extends Transform {
|
|
|
3218
3566
|
for (const migrationFile of migrationFiles) {
|
|
3219
3567
|
const migrationFunction = await this.getOrLoadMigrationFunction(migrationFile);
|
|
3220
3568
|
if (!migrationFunction) {
|
|
3569
|
+
const error = new Error(`Failed to load migration function from file "${migrationFile.name}"`);
|
|
3221
3570
|
this.results.failed.push({
|
|
3222
3571
|
storyId: story.id,
|
|
3223
3572
|
migrationNames,
|
|
3224
|
-
error
|
|
3573
|
+
error
|
|
3574
|
+
});
|
|
3575
|
+
getLogger().error(error.message, {
|
|
3576
|
+
errorCode: ERROR_CODES.MIGRATION_FILE_NOT_FOUND
|
|
3225
3577
|
});
|
|
3226
3578
|
return null;
|
|
3227
3579
|
}
|
|
@@ -3251,6 +3603,7 @@ class MigrationStream extends Transform {
|
|
|
3251
3603
|
migrationNames,
|
|
3252
3604
|
content: storyContent
|
|
3253
3605
|
});
|
|
3606
|
+
getLogger().info("Applied migration", { storyId: story.id, migrationNames });
|
|
3254
3607
|
return {
|
|
3255
3608
|
storyId: story.id,
|
|
3256
3609
|
name: story.name,
|
|
@@ -3265,6 +3618,7 @@ class MigrationStream extends Transform {
|
|
|
3265
3618
|
migrationNames,
|
|
3266
3619
|
reason: "No changes detected after migration"
|
|
3267
3620
|
});
|
|
3621
|
+
getLogger().info("Skipped migration: No changes detected", { storyId: story.id, migrationNames });
|
|
3268
3622
|
return null;
|
|
3269
3623
|
} else {
|
|
3270
3624
|
const reason = migrationFiles.map((migrationFile) => {
|
|
@@ -3278,14 +3632,22 @@ class MigrationStream extends Transform {
|
|
|
3278
3632
|
migrationNames,
|
|
3279
3633
|
reason
|
|
3280
3634
|
});
|
|
3635
|
+
getLogger().info(`Skipped migration: ${reason}`, { storyId: story.id, migrationNames });
|
|
3281
3636
|
return null;
|
|
3282
3637
|
}
|
|
3283
|
-
} catch (
|
|
3638
|
+
} catch (maybeError) {
|
|
3639
|
+
const error = toError(maybeError);
|
|
3284
3640
|
this.results.failed.push({
|
|
3285
3641
|
storyId: story.id,
|
|
3286
3642
|
migrationNames,
|
|
3287
3643
|
error
|
|
3288
3644
|
});
|
|
3645
|
+
getLogger().error(error.message, {
|
|
3646
|
+
storyId: story.id,
|
|
3647
|
+
migrationNames,
|
|
3648
|
+
error,
|
|
3649
|
+
errorCode: ERROR_CODES.MIGRATION_APPLY_TO_STORY_ERROR
|
|
3650
|
+
});
|
|
3289
3651
|
return null;
|
|
3290
3652
|
}
|
|
3291
3653
|
}
|
|
@@ -3385,16 +3747,23 @@ class UpdateStream extends Writable {
|
|
|
3385
3747
|
this.results.successful.push({ storyId, name: storyName });
|
|
3386
3748
|
this.results.totalProcessed++;
|
|
3387
3749
|
this.options.onProgress?.(this.results.totalProcessed);
|
|
3750
|
+
getLogger().info("Updated story", { storyId });
|
|
3388
3751
|
} else {
|
|
3752
|
+
const error = new Error("Update returned null");
|
|
3389
3753
|
this.results.failed.push({
|
|
3390
3754
|
storyId,
|
|
3391
3755
|
name: storyName,
|
|
3392
|
-
error
|
|
3756
|
+
error
|
|
3393
3757
|
});
|
|
3394
3758
|
this.results.totalProcessed++;
|
|
3395
3759
|
this.options.onProgress?.(this.results.totalProcessed);
|
|
3760
|
+
getLogger().error(`Failed to update story: ${error.message}`, {
|
|
3761
|
+
storyId,
|
|
3762
|
+
errorCode: ERROR_CODES.MIGRATION_STORY_UPDATE_NULL
|
|
3763
|
+
});
|
|
3396
3764
|
}
|
|
3397
|
-
} catch (
|
|
3765
|
+
} catch (maybeError) {
|
|
3766
|
+
const error = toError(maybeError);
|
|
3398
3767
|
this.results.failed.push({
|
|
3399
3768
|
storyId,
|
|
3400
3769
|
name: storyName,
|
|
@@ -3402,6 +3771,11 @@ class UpdateStream extends Writable {
|
|
|
3402
3771
|
});
|
|
3403
3772
|
this.results.totalProcessed++;
|
|
3404
3773
|
this.options.onProgress?.(this.results.totalProcessed);
|
|
3774
|
+
getLogger().error(error.message, {
|
|
3775
|
+
storyId,
|
|
3776
|
+
error,
|
|
3777
|
+
errorCode: ERROR_CODES.MIGRATION_STORY_UPDATE_ERROR
|
|
3778
|
+
});
|
|
3405
3779
|
}
|
|
3406
3780
|
}
|
|
3407
3781
|
async _destroy(error, callback) {
|
|
@@ -3437,15 +3811,18 @@ class UpdateStream extends Writable {
|
|
|
3437
3811
|
}
|
|
3438
3812
|
}
|
|
3439
3813
|
|
|
3440
|
-
const program$8 = getProgram();
|
|
3441
3814
|
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) => {
|
|
3442
|
-
|
|
3815
|
+
const program = getProgram();
|
|
3816
|
+
const ui = getUI();
|
|
3817
|
+
const logger = getLogger();
|
|
3818
|
+
ui.title(`${commands.MIGRATIONS}`, colorPalette.MIGRATIONS, componentName ? `Running migrations for component ${componentName}...` : "Running migrations...");
|
|
3819
|
+
logger.info("Migration started");
|
|
3443
3820
|
if (options.dryRun) {
|
|
3444
|
-
|
|
3821
|
+
ui.warn(`DRY RUN MODE ENABLED: No changes will be made.
|
|
3445
3822
|
`);
|
|
3823
|
+
logger.warn("Dry run mode enabled");
|
|
3446
3824
|
}
|
|
3447
|
-
const verbose = program
|
|
3448
|
-
const { filter, dryRun = false, query, startsWith, publish } = options;
|
|
3825
|
+
const verbose = program.opts().verbose;
|
|
3449
3826
|
const { space, path } = migrationsCommand.opts();
|
|
3450
3827
|
const { state, initializeSession } = session();
|
|
3451
3828
|
await initializeSession();
|
|
@@ -3456,6 +3833,7 @@ migrationsCommand.command("run [componentName]").description("Run migrations").o
|
|
|
3456
3833
|
handleError(new CommandError(`Please provide the space as argument --space YOUR_SPACE_ID.`), verbose);
|
|
3457
3834
|
return;
|
|
3458
3835
|
}
|
|
3836
|
+
const { filter, dryRun = false, query, startsWith, publish } = options;
|
|
3459
3837
|
const { password, region } = state;
|
|
3460
3838
|
mapiClient({
|
|
3461
3839
|
token: {
|
|
@@ -3464,40 +3842,25 @@ migrationsCommand.command("run [componentName]").description("Run migrations").o
|
|
|
3464
3842
|
region
|
|
3465
3843
|
});
|
|
3466
3844
|
try {
|
|
3467
|
-
const spinner =
|
|
3468
|
-
verbose: !isVitest
|
|
3469
|
-
}).start(`Fetching migration files and stories...`);
|
|
3845
|
+
const spinner = ui.createSpinner(`Fetching migration files and stories...`);
|
|
3470
3846
|
const migrationFiles = await readMigrationFiles({
|
|
3471
3847
|
space,
|
|
3472
3848
|
path,
|
|
3473
3849
|
filter
|
|
3474
3850
|
});
|
|
3475
|
-
if (migrationFiles.length === 0) {
|
|
3476
|
-
spinner.failed(`No migration files found for space "${space}"${filter ? ` matching filter "${filter}"` : ""}.`);
|
|
3477
|
-
return;
|
|
3478
|
-
}
|
|
3479
3851
|
const filteredMigrations = componentName ? migrationFiles.filter((file) => {
|
|
3480
3852
|
return file.name.match(new RegExp(`^${componentName}(\\..*)?.js$`));
|
|
3481
3853
|
}) : migrationFiles;
|
|
3482
3854
|
if (filteredMigrations.length === 0) {
|
|
3483
3855
|
spinner.failed(`No migration files found${componentName ? ` for component "${componentName}"` : ""}${filter ? ` matching filter "${filter}"` : ""} in space "${space}".`);
|
|
3856
|
+
logger.warn("No migration files found");
|
|
3857
|
+
logger.info("Migration finished");
|
|
3484
3858
|
return;
|
|
3485
3859
|
}
|
|
3486
3860
|
spinner.succeed(`Found ${filteredMigrations.length} migration files.`);
|
|
3487
|
-
const
|
|
3488
|
-
|
|
3489
|
-
|
|
3490
|
-
etaBuffer: 60
|
|
3491
|
-
}, Presets.rect);
|
|
3492
|
-
const storiesProgress = multiBar.create(0, 0, {
|
|
3493
|
-
title: "Fetching Stories...".padEnd(19)
|
|
3494
|
-
});
|
|
3495
|
-
const migrationsProgress = multiBar.create(0, 0, {
|
|
3496
|
-
title: "Applying Migrations".padEnd(19)
|
|
3497
|
-
});
|
|
3498
|
-
const updateProgress = multiBar.create(0, 0, {
|
|
3499
|
-
title: "Updating Stories...".padEnd(19)
|
|
3500
|
-
});
|
|
3861
|
+
const storiesProgress = ui.createProgressBar({ title: "Fetching Stories...".padEnd(19) });
|
|
3862
|
+
const migrationsProgress = ui.createProgressBar({ title: "Applying Migrations".padEnd(19) });
|
|
3863
|
+
const updateProgress = ui.createProgressBar({ title: "Updating Stories...".padEnd(19) });
|
|
3501
3864
|
const storiesStream = await createStoriesStream({
|
|
3502
3865
|
spaceId: space,
|
|
3503
3866
|
params: {
|
|
@@ -3535,7 +3898,7 @@ migrationsCommand.command("run [componentName]").description("Run migrations").o
|
|
|
3535
3898
|
updateProgress.increment();
|
|
3536
3899
|
}
|
|
3537
3900
|
});
|
|
3538
|
-
|
|
3901
|
+
await new Promise((resolve, reject) => {
|
|
3539
3902
|
pipeline(
|
|
3540
3903
|
storiesStream,
|
|
3541
3904
|
migrationStream,
|
|
@@ -3545,25 +3908,51 @@ migrationsCommand.command("run [componentName]").description("Run migrations").o
|
|
|
3545
3908
|
reject(err);
|
|
3546
3909
|
return;
|
|
3547
3910
|
}
|
|
3548
|
-
multiBar.stop();
|
|
3549
|
-
const migrationSummary = migrationStream.getSummary();
|
|
3550
|
-
konsola.info(migrationSummary);
|
|
3551
|
-
const updateSummary = updateStream.getSummary();
|
|
3552
|
-
konsola.info(updateSummary);
|
|
3553
3911
|
resolve();
|
|
3554
3912
|
}
|
|
3555
3913
|
);
|
|
3556
3914
|
});
|
|
3915
|
+
ui.stopAllProgressBars();
|
|
3916
|
+
const migrationSummary = migrationStream.getSummary();
|
|
3917
|
+
ui.info(migrationSummary);
|
|
3918
|
+
const updateSummary = updateStream.getSummary();
|
|
3919
|
+
ui.info(updateSummary);
|
|
3920
|
+
const migrationResults = migrationStream.getResults();
|
|
3921
|
+
const updateResults = updateStream.getResults();
|
|
3922
|
+
logger.info("Migration finished", {
|
|
3923
|
+
migrationResults: {
|
|
3924
|
+
total: migrationResults.totalProcessed,
|
|
3925
|
+
succeeded: migrationResults.successful.length,
|
|
3926
|
+
skipped: migrationResults.skipped.length,
|
|
3927
|
+
failed: migrationResults.failed.length
|
|
3928
|
+
},
|
|
3929
|
+
updateResults: {
|
|
3930
|
+
total: updateResults.totalProcessed,
|
|
3931
|
+
succeeded: updateResults.successful.length,
|
|
3932
|
+
failed: updateResults.failed.length
|
|
3933
|
+
}
|
|
3934
|
+
});
|
|
3557
3935
|
} catch (error) {
|
|
3558
3936
|
handleError(error, verbose);
|
|
3559
3937
|
}
|
|
3560
3938
|
});
|
|
3561
3939
|
|
|
3562
|
-
const program$7 = getProgram();
|
|
3563
3940
|
migrationsCommand.command("rollback [migrationFile]").description("Rollback a migration").action(async (migrationFile) => {
|
|
3564
|
-
|
|
3565
|
-
const
|
|
3941
|
+
const program = getProgram();
|
|
3942
|
+
const ui = getUI();
|
|
3943
|
+
const logger = getLogger();
|
|
3944
|
+
ui.title(
|
|
3945
|
+
`${commands.MIGRATIONS}`,
|
|
3946
|
+
colorPalette.MIGRATIONS,
|
|
3947
|
+
migrationFile ? `Rolling back migration ${chalk.hex(colorPalette.MIGRATIONS)(migrationFile)}...` : "Rolling back migration..."
|
|
3948
|
+
);
|
|
3949
|
+
const verbose = program.opts().verbose;
|
|
3566
3950
|
const { space, path } = migrationsCommand.opts();
|
|
3951
|
+
logger.info("Migration rollback started", {
|
|
3952
|
+
migrationFile,
|
|
3953
|
+
space,
|
|
3954
|
+
path
|
|
3955
|
+
});
|
|
3567
3956
|
const { state, initializeSession } = session();
|
|
3568
3957
|
await initializeSession();
|
|
3569
3958
|
if (!requireAuthentication(state, verbose)) {
|
|
@@ -3586,8 +3975,13 @@ migrationsCommand.command("rollback [migrationFile]").description("Rollback a mi
|
|
|
3586
3975
|
path,
|
|
3587
3976
|
migrationFile
|
|
3588
3977
|
});
|
|
3978
|
+
const rollbackSummary = {
|
|
3979
|
+
total: rollbackData.stories.length,
|
|
3980
|
+
succeeded: 0,
|
|
3981
|
+
failed: 0
|
|
3982
|
+
};
|
|
3589
3983
|
for (const story of rollbackData.stories) {
|
|
3590
|
-
const spinner =
|
|
3984
|
+
const spinner = ui.createSpinner(`Restoring story ${chalk.hex(colorPalette.PRIMARY)(story.name || story.storyId)}...`);
|
|
3591
3985
|
try {
|
|
3592
3986
|
const payload = {
|
|
3593
3987
|
story: {
|
|
@@ -3603,18 +3997,37 @@ migrationsCommand.command("rollback [migrationFile]").description("Rollback a mi
|
|
|
3603
3997
|
}
|
|
3604
3998
|
}
|
|
3605
3999
|
await updateStory(space, story.storyId, payload);
|
|
3606
|
-
|
|
3607
|
-
|
|
4000
|
+
rollbackSummary.succeeded += 1;
|
|
4001
|
+
spinner.succeed(`Restored story ${chalk.hex(colorPalette.PRIMARY)(story.name || story.storyId)} - Completed in ${spinner.elapsedTime.toFixed(2)}ms`);
|
|
4002
|
+
logger.info("Story restored", {
|
|
4003
|
+
storyId: story.storyId,
|
|
4004
|
+
migrationFile,
|
|
4005
|
+
space
|
|
4006
|
+
});
|
|
4007
|
+
} catch (maybeError) {
|
|
4008
|
+
const error = toError(maybeError);
|
|
4009
|
+
rollbackSummary.failed += 1;
|
|
3608
4010
|
spinner.failed(`Failed to restore story ${chalk.hex(colorPalette.PRIMARY)(story.name || story.storyId)}: ${error.message}`);
|
|
4011
|
+
logger.error("Failed to restore story", {
|
|
4012
|
+
storyId: story.storyId,
|
|
4013
|
+
migrationFile,
|
|
4014
|
+
space,
|
|
4015
|
+
error
|
|
4016
|
+
});
|
|
3609
4017
|
}
|
|
3610
4018
|
}
|
|
4019
|
+
logger.info("Migration rollback finished", {
|
|
4020
|
+
migrationFile,
|
|
4021
|
+
space,
|
|
4022
|
+
results: rollbackSummary
|
|
4023
|
+
});
|
|
3611
4024
|
} catch (error) {
|
|
3612
4025
|
handleError(new CommandError(`Failed to rollback migration: ${error.message}`), verbose);
|
|
3613
4026
|
}
|
|
3614
4027
|
});
|
|
3615
4028
|
|
|
3616
|
-
const program$
|
|
3617
|
-
const typesCommand = program$
|
|
4029
|
+
const program$7 = getProgram();
|
|
4030
|
+
const typesCommand = program$7.command(commands.TYPES).alias("ts").description(`Generate types d.ts for your component schemas`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/types");
|
|
3618
4031
|
|
|
3619
4032
|
const getAssetJSONSchema = (title) => ({
|
|
3620
4033
|
$id: "#/asset",
|
|
@@ -4454,7 +4867,7 @@ const upsertDatasourceEntry = async (space, datasourceId, entry, existingId) =>
|
|
|
4454
4867
|
}
|
|
4455
4868
|
};
|
|
4456
4869
|
const readDatasourcesFiles = async (options) => {
|
|
4457
|
-
const { from, path, separateFiles = false, suffix
|
|
4870
|
+
const { from, path, separateFiles = false, suffix } = options;
|
|
4458
4871
|
const resolvedPath = resolvePath(path, `datasources/${from}`);
|
|
4459
4872
|
try {
|
|
4460
4873
|
await readdir(resolvedPath);
|
|
@@ -4465,7 +4878,7 @@ const readDatasourcesFiles = async (options) => {
|
|
|
4465
4878
|
${chalk.cyan(`storyblok datasources pull --space ${from}`)}
|
|
4466
4879
|
|
|
4467
4880
|
2. Then try pushing again:
|
|
4468
|
-
${chalk.cyan(`storyblok datasources push --space
|
|
4881
|
+
${chalk.cyan(`storyblok datasources push --space <target_space> --from ${from}`)}`;
|
|
4469
4882
|
throw new FileSystemError(
|
|
4470
4883
|
"file_not_found",
|
|
4471
4884
|
"read",
|
|
@@ -4522,13 +4935,13 @@ async function readConsolidatedFiles(resolvedPath, suffix) {
|
|
|
4522
4935
|
};
|
|
4523
4936
|
}
|
|
4524
4937
|
|
|
4525
|
-
const program$
|
|
4938
|
+
const program$6 = getProgram();
|
|
4526
4939
|
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(
|
|
4527
4940
|
"--filename <name>",
|
|
4528
4941
|
"Base file name for all component types when generating a single declarations file (e.g. components.d.ts). Ignored when using --separate-files."
|
|
4529
4942
|
).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) => {
|
|
4530
4943
|
konsola.title(`${commands.TYPES}`, colorPalette.TYPES, "Generating types...");
|
|
4531
|
-
const verbose = program$
|
|
4944
|
+
const verbose = program$6.opts().verbose;
|
|
4532
4945
|
const { space, path } = typesCommand.opts();
|
|
4533
4946
|
const spinner = new Spinner({
|
|
4534
4947
|
verbose: !isVitest
|
|
@@ -4581,8 +4994,8 @@ typesCommand.command("generate").description("Generate types d.ts for your compo
|
|
|
4581
4994
|
}
|
|
4582
4995
|
});
|
|
4583
4996
|
|
|
4584
|
-
const program$
|
|
4585
|
-
const datasourcesCommand = program$
|
|
4997
|
+
const program$5 = getProgram();
|
|
4998
|
+
const datasourcesCommand = program$5.command(commands.DATASOURCES).alias("ds").description(`Manage your space's datasources`).option("-s, --space <space>", "space ID").option("-p, --path <path>", "path to save the file. Default is .storyblok/datasources");
|
|
4586
4999
|
|
|
4587
5000
|
async function fetchAllPages(fetchFunction, extractDataFunction, page = 1, collectedItems = []) {
|
|
4588
5001
|
const { data, response } = await fetchFunction(page);
|
|
@@ -4688,10 +5101,10 @@ const saveDatasourcesToFiles = async (space, datasources, options) => {
|
|
|
4688
5101
|
}
|
|
4689
5102
|
};
|
|
4690
5103
|
|
|
4691
|
-
const program$
|
|
5104
|
+
const program$4 = getProgram();
|
|
4692
5105
|
datasourcesCommand.command("pull [datasourceName]").option("-f, --filename <filename>", "custom name to be used in file(s) name instead of space id").option("--sf, --separate-files", "Argument to create a single file for each datasource").option("--su, --suffix <suffix>", "suffix to add to the file name (e.g. datasources.<suffix>.json)").description("Pull datasources from your space").action(async (datasourceName, options) => {
|
|
4693
5106
|
konsola.title(`${commands.DATASOURCES}`, colorPalette.DATASOURCES, datasourceName ? `Pulling datasource ${datasourceName}...` : "Pulling datasources...");
|
|
4694
|
-
const verbose = program$
|
|
5107
|
+
const verbose = program$4.opts().verbose;
|
|
4695
5108
|
const { space, path } = datasourcesCommand.opts();
|
|
4696
5109
|
const { separateFiles, suffix, filename = "datasources" } = options;
|
|
4697
5110
|
const { state, initializeSession } = session();
|
|
@@ -4760,12 +5173,13 @@ datasourcesCommand.command("pull [datasourceName]").option("-f, --filename <file
|
|
|
4760
5173
|
}
|
|
4761
5174
|
});
|
|
4762
5175
|
|
|
4763
|
-
const program$
|
|
5176
|
+
const program$3 = getProgram();
|
|
4764
5177
|
datasourcesCommand.command("push [datasourceName]").description(`Push your space's datasources schema as json`).option("-f, --from <from>", "source space id").option("--fi, --filter <filter>", "glob filter to apply to the datasources before pushing").option("--sf, --separate-files", "Read from separate files instead of consolidated files").option("--su, --suffix <suffix>", "Suffix to add to the datasource name").action(async (datasourceName, options) => {
|
|
4765
5178
|
konsola.title(`${commands.DATASOURCES}`, colorPalette.DATASOURCES, datasourceName ? `Pushing datasource ${datasourceName}...` : "Pushing datasources...");
|
|
4766
|
-
const verbose = program$
|
|
5179
|
+
const verbose = program$3.opts().verbose;
|
|
4767
5180
|
const { space, path } = datasourcesCommand.opts();
|
|
4768
|
-
const {
|
|
5181
|
+
const { filter } = options;
|
|
5182
|
+
const fromSpace = options.from || space;
|
|
4769
5183
|
const { state, initializeSession } = session();
|
|
4770
5184
|
await initializeSession();
|
|
4771
5185
|
if (!requireAuthentication(state, verbose)) {
|
|
@@ -4775,10 +5189,7 @@ datasourcesCommand.command("push [datasourceName]").description(`Push your space
|
|
|
4775
5189
|
handleError(new CommandError(`Please provide the target space as argument --space TARGET_SPACE_ID.`), verbose);
|
|
4776
5190
|
return;
|
|
4777
5191
|
}
|
|
4778
|
-
|
|
4779
|
-
options.from = space;
|
|
4780
|
-
}
|
|
4781
|
-
konsola.info(`Attempting to push datasources ${chalk.bold("from")} space ${chalk.hex(colorPalette.DATASOURCES)(options.from || space)} ${chalk.bold("to")} ${chalk.hex(colorPalette.DATASOURCES)(space)}`);
|
|
5192
|
+
konsola.info(`Attempting to push datasources ${chalk.bold("from")} space ${chalk.hex(colorPalette.DATASOURCES)(fromSpace)} ${chalk.bold("to")} ${chalk.hex(colorPalette.DATASOURCES)(space)}`);
|
|
4782
5193
|
konsola.br();
|
|
4783
5194
|
const { password, region } = state;
|
|
4784
5195
|
mapiClient({
|
|
@@ -4792,7 +5203,7 @@ datasourcesCommand.command("push [datasourceName]").description(`Push your space
|
|
|
4792
5203
|
local: await readDatasourcesFiles({
|
|
4793
5204
|
...options,
|
|
4794
5205
|
path,
|
|
4795
|
-
|
|
5206
|
+
from: fromSpace
|
|
4796
5207
|
}),
|
|
4797
5208
|
target: {
|
|
4798
5209
|
datasources: /* @__PURE__ */ new Map()
|
|
@@ -5080,11 +5491,33 @@ const fetchBlueprintRepositories = async () => {
|
|
|
5080
5491
|
}
|
|
5081
5492
|
};
|
|
5082
5493
|
|
|
5083
|
-
|
|
5084
|
-
|
|
5494
|
+
function showNextSteps(technologyTemplate, finalProjectPath) {
|
|
5495
|
+
konsola.br();
|
|
5496
|
+
konsola.ok(`Your ${chalk.hex(colorPalette.PRIMARY)(technologyTemplate)} project is ready \u{1F389} !`);
|
|
5497
|
+
konsola.br();
|
|
5498
|
+
konsola.info(`Next steps:
|
|
5499
|
+
cd ${finalProjectPath}
|
|
5500
|
+
npm install
|
|
5501
|
+
npm run dev
|
|
5502
|
+
`);
|
|
5503
|
+
konsola.info(`Or check the dedicated guide at: ${chalk.hex(colorPalette.PRIMARY)(`https://www.storyblok.com/docs/guides/${technologyTemplate}`)}`);
|
|
5504
|
+
}
|
|
5505
|
+
async function handleEnvFileCreation(resolvedPath, token) {
|
|
5506
|
+
try {
|
|
5507
|
+
await createEnvFile(resolvedPath, token);
|
|
5508
|
+
konsola.ok(`Created .env file with Storyblok access token`, true);
|
|
5509
|
+
return true;
|
|
5510
|
+
} catch (error) {
|
|
5511
|
+
konsola.warn(`Failed to create .env file: ${error.message}`);
|
|
5512
|
+
konsola.info(`You can manually add this token to your .env file: ${token}`);
|
|
5513
|
+
return false;
|
|
5514
|
+
}
|
|
5515
|
+
}
|
|
5516
|
+
const program$2 = getProgram();
|
|
5517
|
+
program$2.command(`${commands.CREATE} [project-path]`).alias("c").description(`Scaffold a new project using Storyblok`).option("-t, --template <template>", "technology starter template").option("-b, --blueprint <blueprint>", "[DEPRECATED] use --template instead").option("--skip-space", "skip space creation").option("--token <token>", "Storyblok access token (skip space creation and use this token)").action(async (projectPath, options) => {
|
|
5085
5518
|
konsola.title(`${commands.CREATE}`, colorPalette.CREATE);
|
|
5086
|
-
const verbose = program$
|
|
5087
|
-
const { template, blueprint } = options;
|
|
5519
|
+
const verbose = program$2.opts().verbose;
|
|
5520
|
+
const { template, blueprint, token } = options;
|
|
5088
5521
|
let selectedTemplate = template;
|
|
5089
5522
|
if (blueprint && !template) {
|
|
5090
5523
|
konsola.warn(`The --blueprint flag is deprecated. Please use --template instead.`);
|
|
@@ -5110,18 +5543,6 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
|
|
|
5110
5543
|
const spinnerSpace = new Spinner({
|
|
5111
5544
|
verbose: !isVitest
|
|
5112
5545
|
});
|
|
5113
|
-
let userData;
|
|
5114
|
-
try {
|
|
5115
|
-
const user = await getUser(password, region);
|
|
5116
|
-
if (!user) {
|
|
5117
|
-
throw new Error("User data is undefined");
|
|
5118
|
-
}
|
|
5119
|
-
userData = user;
|
|
5120
|
-
} catch (error) {
|
|
5121
|
-
konsola.error("Failed to fetch user info. Please login again.", error);
|
|
5122
|
-
konsola.br();
|
|
5123
|
-
return;
|
|
5124
|
-
}
|
|
5125
5546
|
try {
|
|
5126
5547
|
spinnerBlueprints.start("Fetching starter templates...");
|
|
5127
5548
|
const templates = await fetchBlueprintRepositories();
|
|
@@ -5177,91 +5598,96 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
|
|
|
5177
5598
|
await generateProject(technologyTemplate, projectName, targetDirectory);
|
|
5178
5599
|
konsola.ok(`Project ${chalk.hex(colorPalette.PRIMARY)(projectName)} created successfully in ${chalk.hex(colorPalette.PRIMARY)(finalProjectPath)}`, true);
|
|
5179
5600
|
let createdSpace;
|
|
5180
|
-
|
|
5181
|
-
{ name: "My personal account", value: "personal" }
|
|
5182
|
-
];
|
|
5183
|
-
if (userData.has_org) {
|
|
5184
|
-
choices.push({ name: `Organization (${userData?.org?.name})`, value: "org" });
|
|
5185
|
-
}
|
|
5186
|
-
if (userData.has_partner) {
|
|
5187
|
-
choices.push({ name: "Partner Portal", value: "partner" });
|
|
5188
|
-
}
|
|
5601
|
+
let userData;
|
|
5189
5602
|
let whereToCreateSpace = "personal";
|
|
5190
|
-
if (
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
});
|
|
5195
|
-
}
|
|
5196
|
-
if (region !== "eu" && userData.has_org) {
|
|
5197
|
-
whereToCreateSpace = "org";
|
|
5603
|
+
if (token) {
|
|
5604
|
+
await handleEnvFileCreation(resolvedPath, token);
|
|
5605
|
+
showNextSteps(technologyTemplate, finalProjectPath);
|
|
5606
|
+
return;
|
|
5198
5607
|
}
|
|
5199
|
-
if (
|
|
5200
|
-
|
|
5201
|
-
konsola.br();
|
|
5608
|
+
if (options.skipSpace) {
|
|
5609
|
+
showNextSteps(technologyTemplate, finalProjectPath);
|
|
5202
5610
|
return;
|
|
5203
5611
|
}
|
|
5204
|
-
|
|
5612
|
+
try {
|
|
5205
5613
|
try {
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
const spaceToCreate = {
|
|
5210
|
-
name: toHumanReadable(projectName),
|
|
5211
|
-
domain: blueprintDomain
|
|
5212
|
-
};
|
|
5213
|
-
if (whereToCreateSpace === "org") {
|
|
5214
|
-
spaceToCreate.org = userData.org;
|
|
5215
|
-
spaceToCreate.in_org = true;
|
|
5216
|
-
} else if (whereToCreateSpace === "partner") {
|
|
5217
|
-
spaceToCreate.assign_partner = true;
|
|
5614
|
+
const user = await getUser(password, region);
|
|
5615
|
+
if (!user) {
|
|
5616
|
+
throw new Error("User data is undefined");
|
|
5218
5617
|
}
|
|
5219
|
-
|
|
5220
|
-
spinnerSpace.succeed(`Space "${chalk.hex(colorPalette.PRIMARY)(toHumanReadable(projectName))}" created successfully`);
|
|
5618
|
+
userData = user;
|
|
5221
5619
|
} catch (error) {
|
|
5222
|
-
|
|
5620
|
+
konsola.error("Failed to fetch user info. Please login again.", error);
|
|
5223
5621
|
konsola.br();
|
|
5224
|
-
handleError(error, verbose);
|
|
5225
5622
|
return;
|
|
5226
5623
|
}
|
|
5227
|
-
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
} catch (error) {
|
|
5233
|
-
konsola.warn(`Failed to create .env file: ${error.message}`);
|
|
5234
|
-
konsola.info(`You can manually add this token to your .env file: ${createdSpace.first_token}`);
|
|
5624
|
+
const choices = [
|
|
5625
|
+
{ name: "My personal account", value: "personal" }
|
|
5626
|
+
];
|
|
5627
|
+
if (userData.has_org) {
|
|
5628
|
+
choices.push({ name: `Organization (${userData?.org?.name})`, value: "org" });
|
|
5235
5629
|
}
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
try {
|
|
5239
|
-
await openSpaceInBrowser(createdSpace.id, region);
|
|
5240
|
-
konsola.info(`Opened space in your browser`);
|
|
5241
|
-
} catch (error) {
|
|
5242
|
-
konsola.warn(`Failed to open browser: ${error.message}`);
|
|
5243
|
-
const spaceUrl = generateSpaceUrl(createdSpace.id, region);
|
|
5244
|
-
konsola.info(`You can manually open your space at: ${chalk.hex(colorPalette.PRIMARY)(spaceUrl)}`);
|
|
5630
|
+
if (userData.has_partner) {
|
|
5631
|
+
choices.push({ name: "Partner Portal", value: "partner" });
|
|
5245
5632
|
}
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5633
|
+
if (region === regions.EU && (userData.has_partner || userData.has_org)) {
|
|
5634
|
+
whereToCreateSpace = await select({
|
|
5635
|
+
message: `Where would you like to create this space?`,
|
|
5636
|
+
choices
|
|
5637
|
+
});
|
|
5638
|
+
}
|
|
5639
|
+
if (region !== regions.EU && userData.has_org) {
|
|
5640
|
+
whereToCreateSpace = "org";
|
|
5641
|
+
}
|
|
5642
|
+
if (region !== regions.EU && !userData.has_org) {
|
|
5643
|
+
konsola.warn(`Space creation in this region is limited to Enterprise accounts. If you're part of an organization, please ensure you have the required permissions. For more information about Enterprise access, contact our Sales Team.`);
|
|
5644
|
+
konsola.br();
|
|
5645
|
+
return;
|
|
5646
|
+
}
|
|
5647
|
+
spinnerSpace.start(`Creating space "${toHumanReadable(projectName)}"`);
|
|
5648
|
+
const selectedBlueprint = templates.find((bp) => bp.value === technologyTemplate);
|
|
5649
|
+
const blueprintDomain = selectedBlueprint?.location || "https://localhost:3000/";
|
|
5650
|
+
const spaceToCreate = {
|
|
5651
|
+
name: toHumanReadable(projectName),
|
|
5652
|
+
domain: blueprintDomain
|
|
5653
|
+
};
|
|
5250
5654
|
if (whereToCreateSpace === "org") {
|
|
5251
|
-
|
|
5655
|
+
spaceToCreate.org = userData.org;
|
|
5656
|
+
spaceToCreate.in_org = true;
|
|
5252
5657
|
} else if (whereToCreateSpace === "partner") {
|
|
5253
|
-
|
|
5254
|
-
}
|
|
5255
|
-
|
|
5658
|
+
spaceToCreate.assign_partner = true;
|
|
5659
|
+
}
|
|
5660
|
+
createdSpace = await createSpace(spaceToCreate);
|
|
5661
|
+
spinnerSpace.succeed(`Space "${chalk.hex(colorPalette.PRIMARY)(toHumanReadable(projectName))}" created successfully`);
|
|
5662
|
+
if (createdSpace?.first_token) {
|
|
5663
|
+
await handleEnvFileCreation(resolvedPath, createdSpace.first_token);
|
|
5664
|
+
}
|
|
5665
|
+
if (createdSpace?.id) {
|
|
5666
|
+
try {
|
|
5667
|
+
await openSpaceInBrowser(createdSpace.id, region);
|
|
5668
|
+
konsola.info(`Opened space in your browser`);
|
|
5669
|
+
} catch (error) {
|
|
5670
|
+
konsola.warn(`Failed to open browser: ${error.message}`);
|
|
5671
|
+
const spaceUrl = generateSpaceUrl(createdSpace.id, region);
|
|
5672
|
+
konsola.info(`You can manually open your space at: ${chalk.hex(colorPalette.PRIMARY)(spaceUrl)}`);
|
|
5673
|
+
}
|
|
5674
|
+
}
|
|
5675
|
+
showNextSteps(technologyTemplate, finalProjectPath);
|
|
5676
|
+
if (createdSpace?.first_token) {
|
|
5677
|
+
if (whereToCreateSpace === "org") {
|
|
5678
|
+
konsola.ok(`Storyblok space created in organization ${chalk.hex(colorPalette.PRIMARY)(userData?.org?.name)}, preview url and .env configured automatically. You can now open your space in the browser at ${chalk.hex(colorPalette.PRIMARY)(generateSpaceUrl(createdSpace.id, region))}`);
|
|
5679
|
+
} else if (whereToCreateSpace === "partner") {
|
|
5680
|
+
konsola.ok(`Storyblok space created in partner portal, preview url and .env configured automatically. You can now open your space in the browser at ${chalk.hex(colorPalette.PRIMARY)(generateSpaceUrl(createdSpace.id, region))}`);
|
|
5681
|
+
} else {
|
|
5682
|
+
konsola.ok(`Storyblok space created, preview url and .env configured automatically. You can now open your space in the browser at ${chalk.hex(colorPalette.PRIMARY)(generateSpaceUrl(createdSpace.id, region))}`);
|
|
5683
|
+
}
|
|
5256
5684
|
}
|
|
5685
|
+
} catch (error) {
|
|
5686
|
+
spinnerSpace.failed();
|
|
5687
|
+
konsola.br();
|
|
5688
|
+
handleError(error, verbose);
|
|
5689
|
+
return;
|
|
5257
5690
|
}
|
|
5258
|
-
konsola.br();
|
|
5259
|
-
konsola.info(`Next steps:
|
|
5260
|
-
cd ${finalProjectPath}
|
|
5261
|
-
npm install
|
|
5262
|
-
npm run dev
|
|
5263
|
-
`);
|
|
5264
|
-
konsola.info(`Or check the dedicated guide at: ${chalk.hex(colorPalette.PRIMARY)(`https://www.storyblok.com/docs/guides/${technologyTemplate}`)}`);
|
|
5265
5691
|
} catch (error) {
|
|
5266
5692
|
spinnerSpace.failed();
|
|
5267
5693
|
spinnerBlueprints.failed();
|
|
@@ -5271,7 +5697,31 @@ program$1.command(`${commands.CREATE} [project-path]`).alias("c").description(`S
|
|
|
5271
5697
|
konsola.br();
|
|
5272
5698
|
});
|
|
5273
5699
|
|
|
5274
|
-
const
|
|
5700
|
+
const program$1 = getProgram();
|
|
5701
|
+
const logsCommand = program$1.command(commands.LOGS).alias("lg").description(`Inspect and manage logs.`).option("-s, --space <space>", "The space ID.").option("-p, --path <path>", "Path to the directory containing the logs directory. Defaults to '.storyblok'.");
|
|
5702
|
+
|
|
5703
|
+
logsCommand.command("list").description("List logs").action(async () => {
|
|
5704
|
+
const { space, path } = logsCommand.opts();
|
|
5705
|
+
const ui = getUI();
|
|
5706
|
+
const logsPath = getLogsPath(directories.log, space, path);
|
|
5707
|
+
const logFiles = FileTransport.listLogFiles(logsPath);
|
|
5708
|
+
if (logFiles.length === 0) {
|
|
5709
|
+
ui.info(`No logs found for space "${space}".`);
|
|
5710
|
+
return;
|
|
5711
|
+
}
|
|
5712
|
+
ui.info(`Found ${logFiles.length} log file${logFiles.length === 1 ? "" : "s"} for space "${space}":`);
|
|
5713
|
+
ui.list(logFiles);
|
|
5714
|
+
});
|
|
5715
|
+
|
|
5716
|
+
logsCommand.command("prune").description("Prune logs").option("--keep <number>", "Max number of log files to keep (default `0`, meaning remove all)", Number.parseInt, 0).action(async ({ keep }) => {
|
|
5717
|
+
const { space, path } = logsCommand.opts();
|
|
5718
|
+
const ui = getUI();
|
|
5719
|
+
const logsPath = getLogsPath(directories.log, space, path);
|
|
5720
|
+
const deletedFilesCount = FileTransport.pruneLogFiles(logsPath, keep);
|
|
5721
|
+
ui.info(`Deleted ${deletedFilesCount} log file${deletedFilesCount === 1 ? "" : "s"}`);
|
|
5722
|
+
});
|
|
5723
|
+
|
|
5724
|
+
const version = "4.9.0";
|
|
5275
5725
|
const pkg = {
|
|
5276
5726
|
version: version};
|
|
5277
5727
|
|