@superblocksteam/cli 0.0.19 → 0.0.21

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.
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const node_child_process_1 = require("node:child_process");
5
+ const node_path_1 = tslib_1.__importDefault(require("node:path"));
6
+ const node_util_1 = tslib_1.__importDefault(require("node:util"));
7
+ const util_1 = require("@superblocksteam/util");
8
+ const colorette_1 = require("colorette");
9
+ const fs = tslib_1.__importStar(require("fs-extra"));
10
+ const listr2_1 = require("listr2");
11
+ const semver_1 = tslib_1.__importDefault(require("semver"));
12
+ const authenticated_command_1 = require("../common/authenticated-command");
13
+ const version_control_1 = require("../common/version-control");
14
+ class Migrate extends authenticated_command_1.AuthenticatedCommand {
15
+ async run() {
16
+ const tasks = this.createTasks();
17
+ await tasks.run();
18
+ }
19
+ createTasks() {
20
+ const tasks = new listr2_1.Listr([
21
+ {
22
+ title: "Checking for existing Superblocks project...",
23
+ task: async (ctx) => {
24
+ try {
25
+ [
26
+ ctx.existingSuperblocksRootConfig,
27
+ ctx.superblocksRootConfigPath,
28
+ ] = await (0, util_1.getSuperblocksMonorepoConfigJson)(true);
29
+ ctx.existingSuperblocksResourceConfig =
30
+ await (0, util_1.getSuperblocksResourceConfigIfExists)();
31
+ }
32
+ catch {
33
+ // no existing superblocks config
34
+ this.error(`No Superblocks project found in the current folder hierarchy. Run ${(0, colorette_1.cyan)("superblocks init")} to initialize a new project.`);
35
+ }
36
+ },
37
+ },
38
+ {
39
+ title: "Migrating resources...",
40
+ task: async (ctx, task) => {
41
+ var _a;
42
+ const resourceIdsToMigrate = await this.getResourceIdsToMigrate(ctx, task);
43
+ const subtasks = [];
44
+ const superblocksRootPath = node_path_1.default.resolve(node_path_1.default.dirname(ctx.superblocksRootConfigPath), "..");
45
+ for (const resourceId of resourceIdsToMigrate) {
46
+ const resource = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources[resourceId];
47
+ switch (resource === null || resource === void 0 ? void 0 : resource.resourceType) {
48
+ case "APPLICATION": {
49
+ subtasks.push({
50
+ title: `Migrating application ${resource.location}...`,
51
+ task: async (_ctx, task) => {
52
+ await this.migrateApplication(resource, superblocksRootPath);
53
+ task.title += `: done`;
54
+ },
55
+ });
56
+ break;
57
+ }
58
+ case "BACKEND": {
59
+ subtasks.push({
60
+ title: `Migrating backend ${resource.location}...`,
61
+ task: async (_ctx, task) => {
62
+ task.title += `: done`;
63
+ },
64
+ });
65
+ break;
66
+ }
67
+ default: {
68
+ this.error(`Unsupported resource type, resource: ${JSON.stringify(resource)}
69
+ `);
70
+ }
71
+ }
72
+ }
73
+ return task.newListr(subtasks, {
74
+ concurrent: true,
75
+ });
76
+ },
77
+ },
78
+ ], {
79
+ concurrent: false,
80
+ });
81
+ return tasks;
82
+ }
83
+ async getResourceIdsToMigrate(ctx, task) {
84
+ var _a, _b, _c, _d, _e;
85
+ const choices = [];
86
+ const initialSelections = [];
87
+ choices.push({
88
+ name: "All Resources",
89
+ message: "",
90
+ });
91
+ let counter = 1;
92
+ for (const [resourceId, resource] of Object.entries((_b = (_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.resources) !== null && _b !== void 0 ? _b : {})) {
93
+ choices.push({
94
+ name: resourceId,
95
+ message: resource.location,
96
+ });
97
+ if (((_c = ctx.existingSuperblocksResourceConfig) === null || _c === void 0 ? void 0 : _c.id) === resourceId) {
98
+ initialSelections.push(counter);
99
+ }
100
+ counter++;
101
+ }
102
+ const resourceIdsToMigrate = choices.length === 1
103
+ ? [choices[0].name]
104
+ : await task.prompt([
105
+ {
106
+ type: "MultiSelect",
107
+ name: "resourceIdsToMigrate",
108
+ message: `Select resources to migrate (${version_control_1.MULTI_SELECT_PROMPT_HELP})`,
109
+ choices: choices,
110
+ initial: initialSelections,
111
+ validate: version_control_1.atLeastOneSelection,
112
+ // @ts-expect-error listr2 types are wrong for prefix
113
+ prefix: "▸",
114
+ indicator: "◉",
115
+ },
116
+ ]);
117
+ if (resourceIdsToMigrate[0] === "All Resources") {
118
+ return Object.entries((_e = (_d = ctx.existingSuperblocksRootConfig) === null || _d === void 0 ? void 0 : _d.resources) !== null && _e !== void 0 ? _e : {}).map(([id]) => id);
119
+ }
120
+ return resourceIdsToMigrate;
121
+ }
122
+ async migrateApplication(applicationResource, superblocksRootPath) {
123
+ var _a;
124
+ this.log("Checking CLI version compatibility...");
125
+ try {
126
+ const packageJson = await fs.readJson(node_path_1.default.join(superblocksRootPath, applicationResource.location, "package.json"));
127
+ const versionStr = (_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a["@superblocksteam/custom-components"];
128
+ if (!semver_1.default.satisfies(this.config.version, versionStr)) {
129
+ this.log("Migrating application dependencies...");
130
+ await node_util_1.default.promisify(node_child_process_1.exec)(`npm install @superblocksteam/custom-components@${this.config.version}`, {
131
+ cwd: node_path_1.default.join(superblocksRootPath, applicationResource.location),
132
+ });
133
+ }
134
+ else {
135
+ this.log("CLI version matches package.json version");
136
+ }
137
+ }
138
+ catch (e) {
139
+ this.error(e.message, {
140
+ exit: 1,
141
+ });
142
+ }
143
+ }
144
+ }
145
+ Migrate.description = "Migrate files to use the current CLI version";
146
+ Migrate.examples = ["<%= config.bin %> <%= command.id %>"];
147
+ exports.default = Migrate;
@@ -87,20 +87,12 @@ class Pull extends authenticated_command_1.AuthenticatedCommand {
87
87
  {
88
88
  title: "Updating Superblocks project file...",
89
89
  task: async (ctx) => {
90
- var _a;
91
- const superblocksRootConfig = {
92
- configType: "ROOT",
93
- resources: ctx.existingSuperblocksRootConfig.resources,
94
- metadata: {
95
- ...(_a = ctx.existingSuperblocksRootConfig) === null || _a === void 0 ? void 0 : _a.metadata,
96
- lastUpdated: ctx.now,
97
- },
98
- };
90
+ const [superblocksRootConfig, rootConfigPath] = await (0, util_1.getSuperblocksMonorepoConfigJson)(true);
99
91
  for (const [resourceId, resource] of Object.entries(ctx.writtenResources)) {
100
92
  superblocksRootConfig.resources[resourceId] = resource;
101
93
  }
102
94
  // update superblocks.json file
103
- await fs.writeFile(ctx.superblocksRootConfigPath, JSON.stringify((0, version_control_1.sortByKey)(superblocksRootConfig), null, 2));
95
+ await fs.writeFile(rootConfigPath, JSON.stringify((0, version_control_1.sortByKey)(superblocksRootConfig), null, 2));
104
96
  },
105
97
  },
106
98
  ], {
@@ -128,7 +120,7 @@ class Pull extends authenticated_command_1.AuthenticatedCommand {
128
120
  for (const [resourceId, resource] of Object.entries((_f = (_e = ctx.existingSuperblocksRootConfig) === null || _e === void 0 ? void 0 : _e.resources) !== null && _f !== void 0 ? _f : {})) {
129
121
  choices.push({
130
122
  name: resourceId,
131
- message: `${resource.location} (Last updated at ${new Date(resource.lastUpdated).toLocaleTimeString()})`,
123
+ message: resource.location,
132
124
  });
133
125
  if (((_g = ctx.existingSuperblocksResourceConfig) === null || _g === void 0 ? void 0 : _g.id) === resourceId) {
134
126
  initialSelections.push(counter);
@@ -6,6 +6,7 @@ export declare abstract class AuthenticatedCommand extends Command {
6
6
  protected init(): Promise<void>;
7
7
  protected getBaseUrl(): Promise<string>;
8
8
  protected getSdk(): SuperblocksSdk;
9
+ protected runAutomatedDotfileUpdates(): Promise<void>;
9
10
  }
10
11
  export declare abstract class AuthenticatedApplicationCommand extends AuthenticatedCommand {
11
12
  applicationConfig: SuperblocksApplicationConfig;
@@ -2,10 +2,17 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.AuthenticatedApplicationCommand = exports.AuthenticatedCommand = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const node_path_1 = tslib_1.__importDefault(require("node:path"));
5
6
  const core_1 = require("@oclif/core");
6
7
  const sdk_1 = require("@superblocksteam/sdk");
7
8
  const util_1 = require("@superblocksteam/util");
9
+ const colorette_1 = require("colorette");
8
10
  const fs_extra_1 = tslib_1.__importDefault(require("fs-extra"));
11
+ const semver_1 = tslib_1.__importDefault(require("semver"));
12
+ const yaml_1 = require("yaml");
13
+ const migrationWarningsForApplications_1 = require("../util/migrationWarningsForApplications");
14
+ const migrationsForDotfiles_1 = require("../util/migrationsForDotfiles");
15
+ const version_control_1 = require("./version-control");
9
16
  class AuthenticatedCommand extends core_1.Command {
10
17
  async init() {
11
18
  await super.init();
@@ -16,8 +23,10 @@ class AuthenticatedCommand extends core_1.Command {
16
23
  }
17
24
  const { token, superblocksBaseUrl } = result;
18
25
  this.sdk = new sdk_1.SuperblocksSdk(token, superblocksBaseUrl);
26
+ await this.runAutomatedDotfileUpdates();
19
27
  }
20
- catch {
28
+ catch (e) {
29
+ console.log(e.message);
21
30
  this.error("Please run 'superblocks login' first", { exit: 1 });
22
31
  }
23
32
  }
@@ -32,6 +41,62 @@ class AuthenticatedCommand extends core_1.Command {
32
41
  }
33
42
  return this.sdk;
34
43
  }
44
+ async runAutomatedDotfileUpdates() {
45
+ let previousConfig;
46
+ let rootConfigPath;
47
+ try {
48
+ [previousConfig, rootConfigPath] = await (0, util_1.getSuperblocksMonorepoConfigJson)(true);
49
+ }
50
+ catch {
51
+ // This might fail for a brand-new project folder, which is deliberately ignored
52
+ return;
53
+ }
54
+ if (previousConfig &&
55
+ rootConfigPath &&
56
+ (0, migrationsForDotfiles_1.isDotfileMigrationNeeded)(previousConfig.metadata.cliVersion, this.config.version)) {
57
+ this.log(`Migrating Superblocks settings from ${previousConfig.metadata.cliVersion} to ${this.config.version}...`);
58
+ try {
59
+ const newConfig = await (0, migrationsForDotfiles_1.migrateSuperblocksMonorepoConfig)(previousConfig, {
60
+ previousVersion: previousConfig.metadata.cliVersion,
61
+ currentVersion: this.config.version,
62
+ });
63
+ await fs_extra_1.default.writeFile(rootConfigPath, JSON.stringify((0, version_control_1.sortByKey)(newConfig), null, 2));
64
+ await Promise.all(Object.entries(newConfig.resources).map(async ([, resource]) => {
65
+ this.log(`Migrating ${resource.location}`);
66
+ try {
67
+ const resourcePath = node_path_1.default.join(rootConfigPath, "../..", resource.location, ".superblocks/superblocks.json");
68
+ const resourceConfig = await fs_extra_1.default.readJSON(resourcePath);
69
+ const newConfig = await (0, migrationsForDotfiles_1.migrateSuperblocksEntityConfig)(resourceConfig, {
70
+ previousVersion: previousConfig
71
+ .metadata.cliVersion,
72
+ currentVersion: this.config.version,
73
+ });
74
+ await fs_extra_1.default.writeFile(resourcePath, JSON.stringify((0, version_control_1.sortByKey)(newConfig), null, 2));
75
+ }
76
+ catch (e) {
77
+ this.error(e);
78
+ }
79
+ }));
80
+ this.log(`Finalizing migration`);
81
+ this.log(`After upgrading the CLI version, you may need to perform additional migrations using
82
+ ${(0, colorette_1.cyan)("superblocks migrate")}`);
83
+ }
84
+ catch (e) {
85
+ this.error(e);
86
+ }
87
+ }
88
+ if (previousConfig.metadata.cliVersion) {
89
+ const warningMessage = (0, migrationWarningsForApplications_1.getWarningsForApplicationMigration)(previousConfig.metadata.cliVersion, this.config.version);
90
+ if (warningMessage) {
91
+ this.log(warningMessage);
92
+ }
93
+ }
94
+ // Always upgrade the root config even if there's no migration
95
+ if (rootConfigPath) {
96
+ previousConfig.metadata.cliVersion = this.config.version;
97
+ await fs_extra_1.default.writeFile(rootConfigPath, JSON.stringify((0, version_control_1.sortByKey)(previousConfig), null, 2));
98
+ }
99
+ }
35
100
  }
36
101
  exports.AuthenticatedCommand = AuthenticatedCommand;
37
102
  class AuthenticatedApplicationCommand extends AuthenticatedCommand {
@@ -50,6 +115,7 @@ class AuthenticatedApplicationCommand extends AuthenticatedCommand {
50
115
  return new URL(`/applications/${this.applicationConfig.id}/pages/${this.applicationConfig.defaultPageId}/edit`, baseUrl).toString();
51
116
  }
52
117
  async init() {
118
+ var _a;
53
119
  await super.init();
54
120
  try {
55
121
  this.applicationConfig = await (0, util_1.getSuperblocksApplicationConfigJson)();
@@ -59,8 +125,44 @@ class AuthenticatedApplicationCommand extends AuthenticatedCommand {
59
125
  exit: 1,
60
126
  });
61
127
  }
128
+ try {
129
+ const appYaml = await fs_extra_1.default.readFile("application.yaml", "utf-8");
130
+ const appJson = await (0, yaml_1.parse)(appYaml);
131
+ if ((_a = appJson === null || appJson === void 0 ? void 0 : appJson.settings) === null || _a === void 0 ? void 0 : _a.cliVersion) {
132
+ const warningMessage = (0, migrationWarningsForApplications_1.getWarningsForApplicationMigration)(appJson.settings.cliVersion, this.config.version);
133
+ if (warningMessage) {
134
+ this.log(warningMessage);
135
+ }
136
+ }
137
+ }
138
+ catch (e) {
139
+ this.warn(`Could not read page.yaml to determine version compatibility: ${e.message}`);
140
+ }
62
141
  }
63
142
  async registerComponents() {
143
+ var _a;
144
+ core_1.ux.action.start("Checking CLI version compatibility...");
145
+ try {
146
+ const packageJson = await fs_extra_1.default.readJson("package.json");
147
+ const versionStr = (_a = packageJson.dependencies) === null || _a === void 0 ? void 0 : _a["@superblocksteam/custom-components"];
148
+ if (!versionStr) {
149
+ throw new Error(`You must install the @superblocksteam/custom-components library as a dependency in your package.json file. Run:
150
+
151
+ ${(0, colorette_1.cyan)("superblocks migrate")}`);
152
+ }
153
+ if (!semver_1.default.satisfies(this.config.version, versionStr)) {
154
+ throw new Error(`You must upgrade the @superblocksteam/custom-components library. You are using ${versionStr} but the CLI is ${this.config.version}. Run:
155
+
156
+ ${(0, colorette_1.cyan)("superblocks migrate")}`);
157
+ }
158
+ }
159
+ catch (e) {
160
+ core_1.ux.action.stop();
161
+ this.error(e.message, {
162
+ exit: 1,
163
+ });
164
+ }
165
+ core_1.ux.action.stop();
64
166
  core_1.ux.action.start("Scanning for Superblocks components...");
65
167
  const exists = await fs_extra_1.default.pathExists(util_1.CUSTOM_COMPONENTS_PATH);
66
168
  if (!exists) {
@@ -1,9 +1,9 @@
1
- import { StatefulProperty } from "../types";
2
- export declare const getDefaultConfigTs: ({ id, name, displayName, statefulPropsRendered, eventHandlersRendered, }: {
1
+ import { type Property } from "@superblocksteam/util";
2
+ export declare const getDefaultConfigTs: ({ id, name, displayName, propertiesRendered, eventHandlersRendered, }: {
3
3
  id: string;
4
4
  name: string;
5
5
  displayName: string;
6
- statefulPropsRendered: string;
6
+ propertiesRendered: string;
7
7
  eventHandlersRendered: string;
8
8
  }) => string;
9
- export declare const getDefaultComponentTsx: (statefulProps: StatefulProperty[], eventHandlers: string[]) => string;
9
+ export declare const getDefaultComponentTsx: (properties: Property[], eventHandlers: string[]) => string;
@@ -8,7 +8,7 @@ const indent = (str, spaces) => {
8
8
  .map((line) => `${spacesString}${line}`)
9
9
  .join("\n");
10
10
  };
11
- const getDefaultConfigTs = ({ id, name, displayName, statefulPropsRendered, eventHandlersRendered, }) => `import { type ComponentConfig } from "@superblocksteam/cli";
11
+ const getDefaultConfigTs = ({ id, name, displayName, propertiesRendered, eventHandlersRendered, }) => `import { type ComponentConfig } from "@superblocksteam/custom-components";
12
12
 
13
13
  export default {
14
14
  // DO NOT CHANGE THE ID ONCE THE COMPONENT HAS BEEN REGISTERED!
@@ -16,21 +16,23 @@ export default {
16
16
  name: "${name}",
17
17
  displayName: "${displayName}",
18
18
  componentPath: "components/${name}/component.tsx",
19
- statefulProperties: [${indent(statefulPropsRendered, 4).trim()}],
20
- eventHandlers: [${indent(eventHandlersRendered, 4).trim()}],
19
+ properties: [${indent(propertiesRendered, 4).trim()}],
20
+ events: [${indent(eventHandlersRendered, 4).trim()}],
21
21
  } satisfies ComponentConfig;
22
22
  `;
23
23
  exports.getDefaultConfigTs = getDefaultConfigTs;
24
- const getDefaultComponentTsx = (statefulProps, eventHandlers) => `import React from "react";
25
- import { Props } from "./types";
24
+ const getDefaultComponentTsx = (properties, eventHandlers) => `import React from "react";
25
+ import { useSuperblocksContext } from "@superblocksteam/custom-components";
26
+ import { type Props, type EventTriggers } from "./types";
27
+
28
+ export default function Component({${properties.length === 0
29
+ ? ""
30
+ : "\n" + properties.map((v) => indent(v.path, 2) + ",\n").join("")}}: Props) {
31
+ const {
32
+ updateProperties,
33
+ events: {${"\n" + eventHandlers.map((v) => indent(v, 6) + ",\n").join("")} },
34
+ } = useSuperblocksContext<Props, EventTriggers>();
26
35
 
27
- export default function Component({
28
- updateStatefulProperties${statefulProps.length > 0
29
- ? ",\n" + statefulProps.map((v) => indent(v.path, 2)).join(",\n")
30
- : ""}${eventHandlers.length > 0
31
- ? ",\n" + eventHandlers.map((v) => indent(v, 2)).join(",\n")
32
- : ""}
33
- }: Props) {
34
36
  return (
35
37
  <div
36
38
  style={{
@@ -1,11 +1,5 @@
1
1
  import { ViewMode } from "@superblocksteam/sdk";
2
2
  import { VersionedResourceConfig } from "@superblocksteam/util";
3
- export type SuperblocksMetadata = {
4
- cliVersion: string;
5
- remote: string;
6
- lastUpdated: number;
7
- created: number;
8
- };
9
3
  export declare const LATEST_EDITS_MODE = "latest-edits";
10
4
  export declare const MOST_RECENT_COMMIT_MODE = "most-recent-commit";
11
5
  export declare const DEPLOYED_MODE = "deployed";
@@ -2,7 +2,9 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.sortByKey = exports.getMode = exports.writeResourceToDisk = exports.atLeastOneSelection = exports.MULTI_SELECT_PROMPT_HELP = exports.SELECT_PROMPT_HELP = exports.modeFlagToViewMode = exports.modeFlagValuesMap = exports.DEPLOYED_MODE = exports.MOST_RECENT_COMMIT_MODE = exports.LATEST_EDITS_MODE = void 0;
4
4
  const tslib_1 = require("tslib");
5
+ const https = tslib_1.__importStar(require("https"));
5
6
  const node_path_1 = tslib_1.__importDefault(require("node:path"));
7
+ const path_1 = require("path");
6
8
  const util_1 = require("@superblocksteam/util");
7
9
  const colorette_1 = require("colorette");
8
10
  const fs = tslib_1.__importStar(require("fs-extra"));
@@ -46,8 +48,47 @@ function slugifyName(originalName) {
46
48
  lower: true,
47
49
  });
48
50
  }
51
+ // resolves a true promise when download is complete. If there's an error, resolve false
52
+ async function downloadFile(rootDirectory, filepath, url) {
53
+ const fullPath = `${rootDirectory}/${filepath}`;
54
+ // eslint-disable-next-line no-async-promise-executor
55
+ const result = await new Promise(async (resolve) => {
56
+ try {
57
+ // create directory path if it doesn't exist yet
58
+ if (!(await fs.pathExists(fullPath))) {
59
+ await fs.mkdir((0, path_1.dirname)(fullPath), { recursive: true });
60
+ }
61
+ const file = fs.createWriteStream(fullPath);
62
+ https.get(url, (resp) => {
63
+ resp.pipe(file);
64
+ file
65
+ .on("finish", () => {
66
+ file.end(() => resolve(true));
67
+ })
68
+ .on("error", () => {
69
+ fs.unlink(filepath);
70
+ resolve(false);
71
+ });
72
+ });
73
+ }
74
+ catch (e) {
75
+ return resolve(false);
76
+ }
77
+ });
78
+ // failed to download correctly, attempt to clean up file
79
+ if (!result) {
80
+ try {
81
+ await fs.unlink(fullPath);
82
+ }
83
+ catch (e) {
84
+ console.log("Failed to delete file", fullPath);
85
+ }
86
+ return Promise.resolve(fullPath);
87
+ }
88
+ return Promise.resolve("");
89
+ }
49
90
  async function writeResourceToDisk(resourceType, resourceId, resource, now, rootPath, existingRelativeLocation) {
50
- var _a, _b, _c, _d, _e, _f, _g;
91
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
51
92
  switch (resourceType) {
52
93
  case "APPLICATION": {
53
94
  const parentDirName = "apps";
@@ -94,15 +135,23 @@ async function writeResourceToDisk(resourceType, resourceId, resource, now, root
94
135
  }
95
136
  await fs.ensureDir(`${appDirName}/${util_1.SUPERBLOCKS_HOME_FOLDER_NAME}`);
96
137
  await fs.writeFile(`${appDirName}/${util_1.RESOURCE_CONFIG_PATH}`, JSON.stringify(sortByKey(applicationConfig), null, 2));
138
+ const createdFiles = await Promise.resolve(
139
+ // Defensive check for when application settings are missing componentFiles
140
+ (_f = (_e = resource.componentFiles) === null || _e === void 0 ? void 0 : _e.map((file) => downloadFile(appDirName, file.filename, file.url))) !== null && _f !== void 0 ? _f : []);
141
+ // print out failed downloads synchronously here
142
+ createdFiles
143
+ .filter((createdFiles) => createdFiles.length)
144
+ .forEach((createdFile) => {
145
+ console.log(`Unable to download ${createdFile}`);
146
+ });
97
147
  return {
98
148
  location: relativeLocation,
99
- lastUpdated: now,
100
149
  resourceType: "APPLICATION",
101
150
  };
102
151
  }
103
152
  case "BACKEND": {
104
153
  const parentDirName = "backends";
105
- const apiName = slugifyName((_g = (_f = (_e = resource.apiPb) === null || _e === void 0 ? void 0 : _e.metadata) === null || _f === void 0 ? void 0 : _f.name) !== null && _g !== void 0 ? _g : resource.actions.name);
154
+ const apiName = slugifyName((_j = (_h = (_g = resource.apiPb) === null || _g === void 0 ? void 0 : _g.metadata) === null || _h === void 0 ? void 0 : _h.name) !== null && _j !== void 0 ? _j : resource.actions.name);
106
155
  // server is still sending actions for a backwards compatibility
107
156
  delete resource.actions;
108
157
  const newRelativeLocation = `${parentDirName}/${apiName}`;
@@ -123,7 +172,6 @@ async function writeResourceToDisk(resourceType, resourceId, resource, now, root
123
172
  await fs.writeFile(`${backendDirName}/${util_1.RESOURCE_CONFIG_PATH}`, JSON.stringify(sortByKey(backendConfig), null, 2));
124
173
  return {
125
174
  location: relativeLocation,
126
- lastUpdated: now,
127
175
  resourceType: "BACKEND",
128
176
  };
129
177
  }
package/dist/index.d.ts CHANGED
@@ -1,2 +1 @@
1
1
  export { run } from "@oclif/core";
2
- export * from "./exportedTypes";
package/dist/index.js CHANGED
@@ -1,7 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.run = void 0;
4
- const tslib_1 = require("tslib");
5
4
  var core_1 = require("@oclif/core");
6
5
  Object.defineProperty(exports, "run", { enumerable: true, get: function () { return core_1.run; } });
7
- tslib_1.__exportStar(require("./exportedTypes"), exports);
@@ -0,0 +1 @@
1
+ export declare function getWarningsForApplicationMigration(previousVersion: string, newVersion: string): string | undefined;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getWarningsForApplicationMigration = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const colorette_1 = require("colorette");
6
+ const semver_1 = tslib_1.__importDefault(require("semver"));
7
+ // If you are changing how application files work on disk, for example changing the directory layout,
8
+ // you need to tell the user how to upgrade manually.
9
+ const BREAKING_APPLICATION_VERSIONS = [
10
+ {
11
+ version: "0.0.20",
12
+ // This is an example message because 0.0.20 does not have a docs page for any migrations. This will be added
13
+ // in a future release
14
+ message: `${(0, colorette_1.red)("Warning")}: Your code must be updated due to a breaking change in custom component definitions. See docs.`,
15
+ },
16
+ {
17
+ version: "0.0.21",
18
+ // This is an example message because 0.0.21 does not have a docs page for any migrations. This will be added
19
+ // in a future release
20
+ message: `${(0, colorette_1.red)("Warning")}: You will not be able to use CLI commands on custom components until you manually migrate your components,
21
+ due to breaking changes in the config.ts format and custom component API.
22
+
23
+ ${(0, colorette_1.bold)("Your existing components are safe.")}
24
+
25
+ To manually migrate:
26
+
27
+ 1. Rename "eventHandlers" to "events".
28
+ 2. Update your properties to use the new format: ${(0, colorette_1.magenta)("https://docs.superblocks.com/applications/custom-components/development-lifecycle#configts")}
29
+ `,
30
+ },
31
+ ];
32
+ function getWarningsForApplicationMigration(previousVersion, newVersion) {
33
+ const firstBreakingChange = BREAKING_APPLICATION_VERSIONS.find(({ version }) => {
34
+ return (semver_1.default.lt(previousVersion, version) && semver_1.default.lte(version, newVersion));
35
+ });
36
+ return firstBreakingChange === null || firstBreakingChange === void 0 ? void 0 : firstBreakingChange.message;
37
+ }
38
+ exports.getWarningsForApplicationMigration = getWarningsForApplicationMigration;
@@ -0,0 +1,10 @@
1
+ import { SuperblocksApplicationConfig, SuperblocksBackendConfig, SuperblocksMonorepoConfig } from "@superblocksteam/util";
2
+ export declare function migrateSuperblocksMonorepoConfig(config: SuperblocksMonorepoConfig, options: {
3
+ previousVersion: string;
4
+ currentVersion: string;
5
+ }): Promise<SuperblocksMonorepoConfig>;
6
+ export declare function migrateSuperblocksEntityConfig(config: SuperblocksApplicationConfig | SuperblocksBackendConfig, options: {
7
+ previousVersion: string;
8
+ currentVersion: string;
9
+ }): Promise<SuperblocksApplicationConfig | SuperblocksBackendConfig>;
10
+ export declare function isDotfileMigrationNeeded(previousVersion: string, currentVersion: string): boolean;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isDotfileMigrationNeeded = exports.migrateSuperblocksEntityConfig = exports.migrateSuperblocksMonorepoConfig = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const semver_1 = tslib_1.__importDefault(require("semver"));
6
+ // These migrations are used to upgrade the Superblocks config files as needed.
7
+ // For example if we add a new required field
8
+ const MonorepoMigratorsFromOldVersion = [
9
+ {
10
+ migrateWhenUpgradingTo: "0.0.19",
11
+ migrator: (config) => config,
12
+ },
13
+ ];
14
+ // Recursively migrate configs to the latest version
15
+ async function migrateSuperblocksMonorepoConfig(config, options) {
16
+ return await sharedMigrator(MonorepoMigratorsFromOldVersion, config, options);
17
+ }
18
+ exports.migrateSuperblocksMonorepoConfig = migrateSuperblocksMonorepoConfig;
19
+ // These migrations are used to upgrade the Superblocks config files as needed.
20
+ // For example if we add a new required field
21
+ const EntityMigratorsFromOldVersion = [
22
+ {
23
+ migrateWhenUpgradingTo: "0.0.19",
24
+ migrator: (config) => config,
25
+ },
26
+ ];
27
+ async function migrateSuperblocksEntityConfig(config, options) {
28
+ return await sharedMigrator(EntityMigratorsFromOldVersion, config, options);
29
+ }
30
+ exports.migrateSuperblocksEntityConfig = migrateSuperblocksEntityConfig;
31
+ function isDotfileMigrationNeeded(previousVersion, currentVersion) {
32
+ return (previousVersion !== currentVersion &&
33
+ (MonorepoMigratorsFromOldVersion.some(({ migrateWhenUpgradingTo }) => semver_1.default.gt(migrateWhenUpgradingTo, previousVersion) &&
34
+ semver_1.default.lte(migrateWhenUpgradingTo, currentVersion)) ||
35
+ EntityMigratorsFromOldVersion.some(({ migrateWhenUpgradingTo }) => semver_1.default.gt(migrateWhenUpgradingTo, previousVersion) &&
36
+ semver_1.default.lte(migrateWhenUpgradingTo, currentVersion))));
37
+ }
38
+ exports.isDotfileMigrationNeeded = isDotfileMigrationNeeded;
39
+ async function sharedMigrator(migratorObject, config, { previousVersion, currentVersion, }) {
40
+ let newConfig = JSON.parse(JSON.stringify(config));
41
+ // Find all migrators in the range between previousVersion and currentVersion
42
+ const migratorFns = migratorObject.filter(({ migrateWhenUpgradingTo }) => semver_1.default.gt(migrateWhenUpgradingTo, previousVersion) &&
43
+ semver_1.default.lte(migrateWhenUpgradingTo, currentVersion));
44
+ migratorFns.forEach(({ migrator, migrateWhenUpgradingTo }) => {
45
+ try {
46
+ newConfig = migrator(newConfig);
47
+ }
48
+ catch (e) {
49
+ // Migrations fail if our preconditions aren't met
50
+ console.warn(`Error while migrating config to version ${migrateWhenUpgradingTo}: ${e.message}`);
51
+ throw e;
52
+ }
53
+ });
54
+ return newConfig;
55
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.0.19",
2
+ "version": "0.0.21",
3
3
  "commands": {
4
4
  "init": {
5
5
  "id": "init",
@@ -37,7 +37,7 @@
37
37
  },
38
38
  "login": {
39
39
  "id": "login",
40
- "description": "Authenticates with Superblocks cloud",
40
+ "description": "Authenticate with Superblocks cloud",
41
41
  "strict": true,
42
42
  "pluginName": "@superblocksteam/cli",
43
43
  "pluginAlias": "@superblocksteam/cli",
@@ -54,6 +54,20 @@
54
54
  },
55
55
  "args": {}
56
56
  },
57
+ "migrate": {
58
+ "id": "migrate",
59
+ "description": "Migrate files to use the current CLI version",
60
+ "strict": true,
61
+ "pluginName": "@superblocksteam/cli",
62
+ "pluginAlias": "@superblocksteam/cli",
63
+ "pluginType": "core",
64
+ "aliases": [],
65
+ "examples": [
66
+ "<%= config.bin %> <%= command.id %>"
67
+ ],
68
+ "flags": {},
69
+ "args": {}
70
+ },
57
71
  "pull": {
58
72
  "id": "pull",
59
73
  "description": "Download objects from Superblocks and save them locally",