@superblocksteam/cli 1.6.0 → 1.8.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/README.md CHANGED
@@ -12,7 +12,7 @@ $ npm install -g @superblocksteam/cli
12
12
  $ superblocks COMMAND
13
13
  running command...
14
14
  $ superblocks (--version)
15
- @superblocksteam/cli/1.6.0 linux-x64 node-v18.20.2
15
+ @superblocksteam/cli/1.8.0 linux-x64 node-v18.20.2
16
16
  $ superblocks --help [COMMAND]
17
17
  USAGE
18
18
  $ superblocks COMMAND
@@ -7,7 +7,7 @@
7
7
  "lint:fix": "npx eslint . --fix"
8
8
  },
9
9
  "dependencies": {
10
- "@superblocksteam/custom-components": "1.6.0",
10
+ "@superblocksteam/custom-components": "1.8.0",
11
11
  "react": "^18",
12
12
  "react-dom": "^18"
13
13
  },
@@ -39,7 +39,7 @@ class Login extends core_1.Command {
39
39
  this.log((0, colorette_1.green)(`Welcome to the Superblocks 🐨 CLI ${user.user.name}!`));
40
40
  }
41
41
  catch (error) {
42
- if (error.name === util_1.ERROR_FILE_ACCESS) {
42
+ if (error instanceof util_1.FileAccessError) {
43
43
  this.log((0, colorette_1.red)("Could not save token, ensure the Superblocks CLI has access to create folders in your home directory."));
44
44
  return;
45
45
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  const node_path_1 = tslib_1.__importDefault(require("node:path"));
5
5
  const core_1 = require("@oclif/core");
6
+ const sdk_1 = require("@superblocksteam/sdk");
6
7
  const util_1 = require("@superblocksteam/util");
7
8
  const listr2_1 = require("listr2");
8
9
  const authenticated_command_1 = require("../common/authenticated-command");
@@ -128,14 +129,26 @@ Would you like to also delete these resources from your filesystem?`,
128
129
  var _a, _b;
129
130
  task.title = `Validating git configuration...`;
130
131
  const subtasks = [];
132
+ ctx.resourceIdsToSkip = new Set();
131
133
  for (const resourceId of ctx.resourceIdsToPull) {
132
134
  const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
133
135
  const resourceTitle = `${((_b = resource.resourceType) !== null && _b !== void 0 ? _b : "").toLowerCase()} ${resourceId}`;
134
136
  subtasks.push({
135
137
  title: `Checking ${resourceTitle}...`,
136
138
  task: async () => {
137
- const { branchName } = await this.validateGitSetup(resource === null || resource === void 0 ? void 0 : resource.resourceType, resourceId, util_1.ComponentEvent.PULL, ctx.localBranchName);
138
- ctx.branchToPullFrom.set(resourceId, branchName);
139
+ try {
140
+ const { branchName } = await this.validateGitSetup(resource === null || resource === void 0 ? void 0 : resource.resourceType, resourceId, util_1.ComponentEvent.PULL, ctx.localBranchName);
141
+ ctx.branchToPullFrom.set(resourceId, branchName);
142
+ }
143
+ catch (error) {
144
+ if ((0, version_control_1.isCI)() && error instanceof sdk_1.ValidateGitSetupError) {
145
+ this.log(`WARN: Failed to validate git setup for ${resourceTitle}. Skipping pull.\n\n${error.message}.`);
146
+ }
147
+ else {
148
+ throw error;
149
+ }
150
+ ctx.resourceIdsToSkip.add(resourceId);
151
+ }
139
152
  },
140
153
  });
141
154
  }
@@ -149,6 +162,8 @@ Would you like to also delete these resources from your filesystem?`,
149
162
  var _a, _b;
150
163
  task.title = `Pulling resources from branch ${ctx.localBranchName}...`;
151
164
  const viewMode = await (0, version_control_1.getMode)(task, mode);
165
+ // Remove resources to skip from list of resources to push
166
+ ctx.resourceIdsToPull = ctx.resourceIdsToPull.filter((id) => !ctx.resourceIdsToSkip.has(id));
152
167
  const subtasks = [];
153
168
  for (const resourceId of ctx.resourceIdsToPull) {
154
169
  const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
@@ -135,6 +135,7 @@ Would you like to also delete these resources from your filesystem?`,
135
135
  var _a, _b;
136
136
  task.title = `Validating git configuration...`;
137
137
  const subtasks = [];
138
+ ctx.resourceIdsToSkip = new Set();
138
139
  for (const resourceId of ctx.resourceIdsToPush) {
139
140
  const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
140
141
  // for user messages:
@@ -142,8 +143,43 @@ Would you like to also delete these resources from your filesystem?`,
142
143
  subtasks.push({
143
144
  title: `Checking ${resourceTitle}...`,
144
145
  task: async () => {
145
- const { branchName } = await this.validateGitSetup(resource === null || resource === void 0 ? void 0 : resource.resourceType, resourceId, util_1.ComponentEvent.PUSH, ctx.localBranchName);
146
- ctx.branchToPushTo.set(resourceId, branchName);
146
+ try {
147
+ const { branchName } = await this.validateGitSetup(resource === null || resource === void 0 ? void 0 : resource.resourceType, resourceId, util_1.ComponentEvent.PUSH, ctx.localBranchName);
148
+ ctx.branchToPushTo.set(resourceId, branchName);
149
+ }
150
+ catch (error) {
151
+ if ((0, version_control_1.isCI)() && error instanceof sdk_1.ValidateGitSetupError) {
152
+ this.log(`WARN: Failed to validate git setup for ${resourceTitle}. Skipping push.\n\n${error.message}.`);
153
+ }
154
+ else {
155
+ throw error;
156
+ }
157
+ ctx.resourceIdsToSkip.add(resourceId);
158
+ }
159
+ },
160
+ });
161
+ }
162
+ return task.newListr(subtasks, {
163
+ concurrent: true,
164
+ });
165
+ },
166
+ },
167
+ {
168
+ task: async (ctx, task) => {
169
+ var _a, _b;
170
+ task.title = `Validating project structure...`;
171
+ const subtasks = [];
172
+ const superblocksRootPath = node_path_1.default.resolve(node_path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
173
+ for (const resourceId of ctx.resourceIdsToPush) {
174
+ const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
175
+ const resourceTitle = `${((_b = resource.resourceType) !== null && _b !== void 0 ? _b : "").toLowerCase()} at ${resource.location}`;
176
+ subtasks.push({
177
+ title: `Validating ${resourceTitle}...`,
178
+ task: async () => {
179
+ const validationError = await (0, version_control_1.validateLocalResource)(superblocksRootPath, resource);
180
+ if (validationError) {
181
+ this.error(`Push failed for resource '${resource.location}' (id: ${resourceId}). ${validationError}`);
182
+ }
147
183
  },
148
184
  });
149
185
  }
@@ -156,6 +192,8 @@ Would you like to also delete these resources from your filesystem?`,
156
192
  task: async (ctx, task) => {
157
193
  var _a;
158
194
  task.title = `Pushing resources to branch ${ctx.localBranchName}...`;
195
+ // Remove resources to skip from list of resources to push
196
+ ctx.resourceIdsToPush = ctx.resourceIdsToPush.filter((id) => !ctx.resourceIdsToSkip.has(id));
159
197
  const subtasks = [];
160
198
  const superblocksRootPath = node_path_1.default.resolve(node_path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
161
199
  for (const resourceId of ctx.resourceIdsToPush) {
@@ -171,16 +209,19 @@ Would you like to also delete these resources from your filesystem?`,
171
209
  (await this.getSdk().fetchCurrentUser()).flagBootstrap["server.multipage.enabled"]) {
172
210
  this.error(`Application files at ${resource.location} are in single page format, but the multi-page feature is enabled for your account. Please run \`superblocks migrate\` to convert your application to multi-page format and commit the changes before pushing.`);
173
211
  }
212
+ const localGitRepoState = await (0, version_control_1.getLocalGitRepoState)(ctx.localBranchName);
174
213
  const applicationConfig = fileStructureType === version_control_1.FileStructureType.SINGLE_PAGE
175
214
  ? {
176
215
  ...(await (0, version_control_1.readApplicationFromDisk)(superblocksRootPath, resource.location)),
177
216
  commitId: ctx.headCommitId,
178
217
  commitMessage: ctx.headCommitMessage,
218
+ gitState: localGitRepoState,
179
219
  }
180
220
  : {
181
221
  ...(await (0, version_control_1.readMultiPageApplicationFromDisk)(superblocksRootPath, resource.location)),
182
222
  commitId: ctx.headCommitId,
183
223
  commitMessage: ctx.headCommitMessage,
224
+ gitState: localGitRepoState,
184
225
  };
185
226
  task.title += `: read from disk`;
186
227
  try {
@@ -215,10 +256,12 @@ Would you like to also delete these resources from your filesystem?`,
215
256
  title: `Pushing workflow/scheduled job ${resource.location}...`,
216
257
  task: async (_ctx, task) => {
217
258
  var _a;
259
+ const localGitRepoState = await (0, version_control_1.getLocalGitRepoState)(ctx.localBranchName);
218
260
  const apiConfig = {
219
261
  ...(await (0, version_control_1.readApiFromDisk)(superblocksRootPath, resource.location)),
220
262
  commitId: ctx.headCommitId,
221
263
  commitMessage: ctx.headCommitMessage,
264
+ gitState: localGitRepoState,
222
265
  };
223
266
  task.title += `: read from disk`;
224
267
  try {
@@ -231,7 +274,7 @@ Would you like to also delete these resources from your filesystem?`,
231
274
  });
232
275
  }
233
276
  catch (error) {
234
- if ((error === null || error === void 0 ? void 0 : error.name) === "BranchNotCheckedOutError") {
277
+ if (error instanceof sdk_1.BranchNotCheckedOutError) {
235
278
  this.log(`WARN: Workflow/Scheduled Job ${(0, version_control_1.extractApiName)(apiConfig)} failed to push, please check branch out in the Superblocks UI first in order to check this branch out for this resource.`);
236
279
  }
237
280
  else if (error instanceof sdk_1.CommitAlreadyExistsError) {
@@ -297,11 +340,12 @@ Would you like to also delete these resources from your filesystem?`,
297
340
  ? [choices[0].name]
298
341
  : await task.prompt([
299
342
  {
300
- type: "MultiSelect",
343
+ type: "AutoComplete",
301
344
  name: "resourceIdsToPush",
302
345
  message: `Select resources to push (${version_control_1.MULTI_SELECT_PROMPT_HELP})`,
303
346
  choices: choices,
304
347
  initial: initialSelections,
348
+ multiple: true,
305
349
  validate: version_control_1.atLeastOneSelection,
306
350
  prefix: "▸",
307
351
  indicator: "◉",
@@ -36,6 +36,8 @@ export declare function getCurrentGitBranchIfGit(): Promise<string | null>;
36
36
  */
37
37
  export declare function getCurrentGitBranch(): Promise<string>;
38
38
  export declare function getHeadCommit(branch: string): Promise<[string, string]>;
39
+ export declare function isCI(): boolean;
39
40
  export declare function isGitRepoDirty(): Promise<boolean>;
40
41
  export declare function extractApiName(api: ApiWrapper): string;
42
+ export declare function validateLocalResource(superblocksRootPath: string, resource: VersionedResourceConfig): Promise<string | undefined>;
41
43
  export declare function deleteResourcesAndUpdateRootConfig(removedResourceIds: string[], existingSuperblocksRootConfig: SuperblocksMonorepoConfig, superblocksRootPath: string, superblocksRootConfigPath: string): Promise<void>;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.deleteResourcesAndUpdateRootConfig = exports.extractApiName = exports.isGitRepoDirty = exports.getHeadCommit = exports.getCurrentGitBranch = exports.getCurrentGitBranchIfGit = exports.getLocalGitRepoState = exports.sortByKey = exports.getMode = exports.removeResourceFromDisk = exports.writeMultiPageApplicationToDisk = exports.writeResourceToDisk = exports.readApiFromDisk = exports.readMultiPageApplicationFromDisk = exports.readApplicationFromDisk = exports.getFileStructureType = exports.atLeastOneSelection = exports.MULTI_SELECT_PROMPT_HELP = exports.SELECT_PROMPT_HELP = exports.modeFlagToViewMode = exports.FileStructureType = exports.modeFlagValuesMap = exports.DEFAULT_BRANCH = exports.DEPLOYED_MODE = exports.MOST_RECENT_COMMIT_MODE = exports.LATEST_EDITS_MODE = void 0;
3
+ exports.deleteResourcesAndUpdateRootConfig = exports.validateLocalResource = exports.extractApiName = exports.isGitRepoDirty = exports.isCI = exports.getHeadCommit = exports.getCurrentGitBranch = exports.getCurrentGitBranchIfGit = exports.getLocalGitRepoState = exports.sortByKey = exports.getMode = exports.removeResourceFromDisk = exports.writeMultiPageApplicationToDisk = exports.writeResourceToDisk = exports.readApiFromDisk = exports.readMultiPageApplicationFromDisk = exports.readApplicationFromDisk = exports.getFileStructureType = exports.atLeastOneSelection = exports.MULTI_SELECT_PROMPT_HELP = exports.SELECT_PROMPT_HELP = exports.modeFlagToViewMode = exports.FileStructureType = exports.modeFlagValuesMap = exports.DEFAULT_BRANCH = exports.DEPLOYED_MODE = exports.MOST_RECENT_COMMIT_MODE = exports.LATEST_EDITS_MODE = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const https = tslib_1.__importStar(require("https"));
6
6
  const node_path_1 = tslib_1.__importDefault(require("node:path"));
@@ -574,8 +574,12 @@ async function getHeadCommit(branch) {
574
574
  return [headCommitId, headCommitMessage];
575
575
  }
576
576
  exports.getHeadCommit = getHeadCommit;
577
+ function isCI() {
578
+ return process.env.CI === "true";
579
+ }
580
+ exports.isCI = isCI;
577
581
  async function isGitRepoDirty() {
578
- if (process.env.CI === "true") {
582
+ if (isCI()) {
579
583
  // Skip dirtiness check in CI environments
580
584
  return false;
581
585
  }
@@ -600,6 +604,123 @@ function writeApi(api, originalApiName, appDirName) {
600
604
  };
601
605
  return handleApi();
602
606
  }
607
+ function validateSinglePageApplication(applicationConfig, superblocksRootPath, location) {
608
+ // validate app level APIs
609
+ for (const apiName of Object.values(applicationConfig.apis)) {
610
+ const apiPath = node_path_1.default.resolve(superblocksRootPath, location, "apis", `${slugifyName(apiName)}.yaml`);
611
+ const validateApiError = validateYamlFile(apiPath);
612
+ if (validateApiError) {
613
+ return validateApiError;
614
+ }
615
+ }
616
+ // validate page
617
+ const pagePath = node_path_1.default.resolve(superblocksRootPath, location, "page.yaml");
618
+ const validatePageError = validateYamlFile(pagePath);
619
+ if (validatePageError) {
620
+ return validatePageError;
621
+ }
622
+ }
623
+ async function validateMultiPageApplication(applicationConfig, superblocksRootPath, location) {
624
+ var _a;
625
+ // validate app level APIs
626
+ for (const apiName of Object.values(applicationConfig.apis)) {
627
+ const apiPath = node_path_1.default.resolve(superblocksRootPath, location, "apis", `${slugifyName(apiName)}.yaml`);
628
+ const validateApiError = await validateYamlFile(apiPath);
629
+ if (validateApiError) {
630
+ return validateApiError;
631
+ }
632
+ }
633
+ // validate pages
634
+ for (const page of Object.values((_a = applicationConfig.pages) !== null && _a !== void 0 ? _a : {})) {
635
+ const pagePath = node_path_1.default.resolve(superblocksRootPath, location, "pages", slugifyName(page.name), "page.yaml");
636
+ const validatePageError = await validateYamlFile(pagePath);
637
+ if (validatePageError) {
638
+ return validatePageError;
639
+ }
640
+ // validate page level APIs
641
+ for (const apiName of Object.values(page.apis)) {
642
+ const apiPath = node_path_1.default.resolve(superblocksRootPath, location, "pages", slugifyName(page.name), "apis", `${slugifyName(apiName)}.yaml`);
643
+ const validateApiError = await validateYamlFile(apiPath);
644
+ if (validateApiError) {
645
+ return validateApiError;
646
+ }
647
+ }
648
+ }
649
+ return undefined;
650
+ }
651
+ async function validateLocalResource(superblocksRootPath, resource) {
652
+ switch (resource.resourceType) {
653
+ case "APPLICATION": {
654
+ // make sure application config exists
655
+ const applicationConfigPath = node_path_1.default.resolve(superblocksRootPath, resource.location, util_2.RESOURCE_CONFIG_PATH);
656
+ if (!(await fs.pathExists(applicationConfigPath))) {
657
+ return `File ${relativeToCurrentDir(applicationConfigPath)} not found. Superblocks CLI commands cannot function without it.`;
658
+ }
659
+ let fileStructureType = undefined;
660
+ let applicationConfig = undefined;
661
+ try {
662
+ // make sure it's a well-formed application config
663
+ applicationConfig = await fs.readJSON(applicationConfigPath);
664
+ if (!applicationConfig) {
665
+ throw new Error();
666
+ }
667
+ fileStructureType =
668
+ getFileStructureTypeFromResourceConfig(applicationConfig);
669
+ }
670
+ catch {
671
+ return `File ${relativeToCurrentDir(applicationConfigPath)} is not a valid JSON file. Please be sure it's valid JSON and rerun the command.`;
672
+ }
673
+ const applicationYamlPath = node_path_1.default.resolve(superblocksRootPath, resource.location, "application.yaml");
674
+ // make sure application.yaml is a well-formed yaml file
675
+ try {
676
+ await readYamlFile(applicationYamlPath);
677
+ }
678
+ catch {
679
+ return `File ${relativeToCurrentDir(applicationYamlPath)} is not a valid YAML file. Please be sure it's valid YAML and rerun the command.`;
680
+ }
681
+ switch (fileStructureType) {
682
+ case FileStructureType.SINGLE_PAGE: {
683
+ validateSinglePageApplication(applicationConfig, superblocksRootPath, resource.location);
684
+ break;
685
+ }
686
+ case FileStructureType.MULTI_PAGE: {
687
+ const validationError = validateMultiPageApplication(applicationConfig, superblocksRootPath, resource.location);
688
+ if (validationError) {
689
+ return validationError;
690
+ }
691
+ break;
692
+ }
693
+ default: {
694
+ return `Invalid file structure type ${fileStructureType}`;
695
+ }
696
+ }
697
+ break;
698
+ }
699
+ case "BACKEND": {
700
+ // make sure the backend config exists
701
+ const backendConfigPath = node_path_1.default.resolve(superblocksRootPath, resource.location, util_2.RESOURCE_CONFIG_PATH);
702
+ if (!(await fs.pathExists(backendConfigPath))) {
703
+ return `File ${relativeToCurrentDir(backendConfigPath)} not found. Superblocks CLI commands cannot function without it.`;
704
+ }
705
+ // make sure it's a well-formed backend config
706
+ try {
707
+ await fs.readJSON(backendConfigPath);
708
+ }
709
+ catch {
710
+ return `File ${relativeToCurrentDir(backendConfigPath)} is not a valid JSON file. Please be sure it's valid JSON and rerun the command.`;
711
+ }
712
+ // make sure that api.yaml exists
713
+ const apiYamlPath = node_path_1.default.resolve(superblocksRootPath, resource.location, "api.yaml");
714
+ const validateYamlFileError = await validateYamlFile(apiYamlPath);
715
+ if (validateYamlFileError) {
716
+ return validateYamlFileError;
717
+ }
718
+ break;
719
+ }
720
+ }
721
+ return undefined;
722
+ }
723
+ exports.validateLocalResource = validateLocalResource;
603
724
  async function deleteResourcesAndUpdateRootConfig(removedResourceIds, existingSuperblocksRootConfig, superblocksRootPath, superblocksRootConfigPath) {
604
725
  for (const resourceId of removedResourceIds) {
605
726
  const resource = existingSuperblocksRootConfig === null || existingSuperblocksRootConfig === void 0 ? void 0 : existingSuperblocksRootConfig.resources[resourceId];
@@ -612,3 +733,15 @@ async function deleteResourcesAndUpdateRootConfig(removedResourceIds, existingSu
612
733
  await fs.writeFile(superblocksRootConfigPath, JSON.stringify(sortByKey(existingSuperblocksRootConfig), null, 2));
613
734
  }
614
735
  exports.deleteResourcesAndUpdateRootConfig = deleteResourcesAndUpdateRootConfig;
736
+ async function validateYamlFile(yamlPath) {
737
+ // make sure a yaml file is well-formed
738
+ try {
739
+ await readYamlFile(yamlPath);
740
+ }
741
+ catch {
742
+ return `File ${relativeToCurrentDir(yamlPath)} is not a valid YAML file. Please be sure it's valid YAML and rerun the command.`;
743
+ }
744
+ }
745
+ function relativeToCurrentDir(applicationConfigPath) {
746
+ return `./${node_path_1.default.relative(process.cwd(), applicationConfigPath)}`;
747
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.6.0",
2
+ "version": "1.8.0",
3
3
  "commands": {
4
4
  "init": {
5
5
  "id": "init",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superblocksteam/cli",
3
- "version": "1.6.0",
3
+ "version": "1.8.0",
4
4
  "description": "Official Superblocks CLI",
5
5
  "bin": {
6
6
  "superblocks": "bin/run"
@@ -18,11 +18,11 @@
18
18
  "@oclif/core": "^2.11.7",
19
19
  "@oclif/plugin-help": "^5.2.16",
20
20
  "@oclif/plugin-plugins": "^3.1.10",
21
- "@superblocksteam/css-plugin": "1.6.0",
22
- "@superblocksteam/react-shim": "1.6.0",
23
- "@superblocksteam/sdk": "1.6.0",
24
- "@superblocksteam/util": "1.6.0",
25
- "@superblocksteam/vite-custom-component-reload-plugin": "1.6.0",
21
+ "@superblocksteam/css-plugin": "1.8.0",
22
+ "@superblocksteam/react-shim": "1.8.0",
23
+ "@superblocksteam/sdk": "1.8.0",
24
+ "@superblocksteam/util": "1.8.0",
25
+ "@superblocksteam/vite-custom-component-reload-plugin": "1.8.0",
26
26
  "@vitejs/plugin-react": "^4.1.0",
27
27
  "colorette": "^2.0.19",
28
28
  "enquirer": "^2.3.6",