@hubspot/ui-extensions-dev-server 0.8.15 → 0.8.17

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.d.ts CHANGED
@@ -1,3 +1,3 @@
1
1
  import { remoteBuild, buildSingleExtension } from './lib/build';
2
- import DevModeInterface from './lib/DevModeInterface';
3
- export { remoteBuild, buildSingleExtension, DevModeInterface };
2
+ import DevModeInterface, { DevModeInterfaceNonSingleton } from './lib/DevModeInterface';
3
+ export { remoteBuild, buildSingleExtension, DevModeInterface, DevModeInterfaceNonSingleton, };
package/dist/index.js CHANGED
@@ -1,11 +1,32 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
4
24
  };
5
25
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.DevModeInterface = exports.buildSingleExtension = exports.remoteBuild = void 0;
26
+ exports.DevModeInterfaceNonSingleton = exports.DevModeInterface = exports.buildSingleExtension = exports.remoteBuild = void 0;
7
27
  const build_1 = require("./lib/build");
8
28
  Object.defineProperty(exports, "remoteBuild", { enumerable: true, get: function () { return build_1.remoteBuild; } });
9
29
  Object.defineProperty(exports, "buildSingleExtension", { enumerable: true, get: function () { return build_1.buildSingleExtension; } });
10
- const DevModeInterface_1 = __importDefault(require("./lib/DevModeInterface"));
30
+ const DevModeInterface_1 = __importStar(require("./lib/DevModeInterface"));
11
31
  exports.DevModeInterface = DevModeInterface_1.default;
32
+ Object.defineProperty(exports, "DevModeInterfaceNonSingleton", { enumerable: true, get: function () { return DevModeInterface_1.DevModeInterfaceNonSingleton; } });
@@ -10,6 +10,7 @@ interface SetupArguments {
10
10
  api: string;
11
11
  web: string;
12
12
  };
13
+ setActiveApp: (appUid: string | undefined) => Promise<void>;
13
14
  }
14
15
  interface StartArguments {
15
16
  accountId?: number;
@@ -36,10 +37,12 @@ declare class DevModeInterface {
36
37
  isRunning?: boolean;
37
38
  _generateAppExtensionMappings(components: ProjectComponentMap): AppExtensionMapping[];
38
39
  _getPlatformVersion(projectConfig?: ProjectConfig): PlatformVersion;
39
- setup({ components, onUploadRequired, promptUser, logger, urls, }: SetupArguments): Promise<void>;
40
+ _reset(): void;
41
+ setup({ components, onUploadRequired, promptUser, logger, urls, setActiveApp, }: SetupArguments): Promise<void>;
40
42
  fileChange(filePath: string, __event: unknown): Promise<void>;
41
43
  start({ requestPorts, accountId, projectConfig }: StartArguments): Promise<void>;
42
44
  cleanup(): Promise<void>;
43
45
  }
46
+ export { DevModeInterface as DevModeInterfaceNonSingleton };
44
47
  declare const _default: DevModeInterface;
45
48
  export default _default;
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
11
11
  });
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.DevModeInterfaceNonSingleton = void 0;
14
15
  const dev_1 = require("./dev");
15
16
  const constants_1 = require("./constants");
16
17
  const constants_2 = require("./constants");
@@ -49,6 +50,9 @@ class DevModeInterface {
49
50
  // Loop over the loaded extension configs and generate the list of choices to use to prompt the user for input
50
51
  extensionConfigKeys.forEach((extensionKey) => {
51
52
  const extensionConfig = extensionsConfigForApp[extensionKey];
53
+ if (extensionConfig.appConfig) {
54
+ extensionConfig.appConfig.isPublicApp = component.type === constants_1.PUBLIC_APP;
55
+ }
52
56
  appExtensionMappings.push({
53
57
  name: `${componentName}/${extensionConfig.data.title}`,
54
58
  value: extensionConfig,
@@ -71,7 +75,18 @@ class DevModeInterface {
71
75
  return (0, utils_1.throwUnhandledPlatformVersionError)(platformVersion);
72
76
  }
73
77
  }
74
- setup({ components, onUploadRequired, promptUser, logger, urls, }) {
78
+ _reset() {
79
+ this.configs = undefined;
80
+ this.devServerState = undefined;
81
+ this.onUploadRequired = undefined;
82
+ this.shutdown = undefined;
83
+ this.logger = defaultLogger;
84
+ this.urls = undefined;
85
+ this.isConfigured = false;
86
+ this.isRunning = false;
87
+ }
88
+ setup({ components, onUploadRequired, promptUser, logger, urls, setActiveApp, }) {
89
+ var _a, _b, _c;
75
90
  return __awaiter(this, void 0, void 0, function* () {
76
91
  logger.debug('Setup function was invoked', { components, urls });
77
92
  if (this.isConfigured) {
@@ -108,6 +123,9 @@ class DevModeInterface {
108
123
  this.configs = answers.extensions;
109
124
  }
110
125
  this.isConfigured = true;
126
+ if (typeof setActiveApp === 'function') {
127
+ yield setActiveApp((_c = (_b = (_a = this.configs) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.appConfig) === null || _c === void 0 ? void 0 : _c.uid);
128
+ }
111
129
  });
112
130
  }
113
131
  // The contract is for this to be async, so eslint can chill
@@ -149,7 +167,7 @@ class DevModeInterface {
149
167
  this.logger.debug('Call to port manager failed, using default ports');
150
168
  }
151
169
  }
152
- const { proxy: localDevUrlMapping } = (0, config_1.loadLocalConfig)(((_b = (_a = this.configs) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.path) || '') || {};
170
+ const { proxy: localDevUrlMapping } = (0, config_1.loadLocalConfig)(((_b = (_a = this.configs) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.path) || '', this.logger) || {};
153
171
  this.devServerState = new DevServerState_1.DevServerState({
154
172
  localDevUrlMapping,
155
173
  extensionConfigs: this.configs,
@@ -172,11 +190,13 @@ class DevModeInterface {
172
190
  }
173
191
  cleanup() {
174
192
  return __awaiter(this, void 0, void 0, function* () {
175
- if (this.shutdown && this.isRunning) {
193
+ if (this.shutdown) {
176
194
  yield this.shutdown();
177
- this.isRunning = false;
178
195
  }
196
+ // Since the DevModeInterface is a singleton, we need to wipe out the state when we shutdown
197
+ this._reset();
179
198
  });
180
199
  }
181
200
  }
201
+ exports.DevModeInterfaceNonSingleton = DevModeInterface;
182
202
  exports.default = new DevModeInterface();
@@ -35,5 +35,6 @@ export declare class DevServerState {
35
35
  get outputDir(): string;
36
36
  get appPath(): string;
37
37
  get localDevUrlMapping(): DevServerStateLocalDevUrlMapping;
38
+ isPublicApp(): boolean;
38
39
  }
39
40
  export {};
@@ -68,5 +68,9 @@ class DevServerState {
68
68
  get localDevUrlMapping() {
69
69
  return this._localDevUrlMapping;
70
70
  }
71
+ isPublicApp() {
72
+ var _a;
73
+ return !!((_a = this.appConfig) === null || _a === void 0 ? void 0 : _a.isPublicApp);
74
+ }
71
75
  }
72
76
  exports.DevServerState = DevServerState;
@@ -1,11 +1,14 @@
1
+ import { InlineConfig } from 'vite';
1
2
  interface BuildSingleExtensionArgs {
2
3
  file: string;
3
4
  outputDir?: string;
4
5
  emptyOutDir?: boolean;
5
6
  minify?: boolean;
6
7
  root?: string;
8
+ logLevel?: InlineConfig['logLevel'];
7
9
  }
8
10
  export declare const extensionErrorBaseMessage: string;
9
- export declare function buildSingleExtension({ file, outputDir, emptyOutDir, minify, root, }: BuildSingleExtensionArgs): Promise<void>;
10
- export declare function remoteBuild(root: string, entryPoint: string, outputDir?: string): Promise<void>;
11
+ export declare function buildSingleExtension({ file, outputDir, emptyOutDir, minify, root, // This is the vite default, so using that as our default
12
+ logLevel, }: BuildSingleExtensionArgs): Promise<void>;
13
+ export declare function remoteBuild(root: string, entryPoint: string, outputDir?: string, logLevel?: BuildSingleExtensionArgs['logLevel']): Promise<void>;
11
14
  export {};
package/dist/lib/build.js CHANGED
@@ -23,10 +23,11 @@ const friendlyLoggingPlugin_1 = __importDefault(require("./plugins/friendlyLoggi
23
23
  const allowedExtensions = ['.js', '.ts', '.tsx', '.jsx'];
24
24
  exports.extensionErrorBaseMessage = `Supported file extensions are [${allowedExtensions.join(', ')}], received:`;
25
25
  function buildSingleExtension({ file, outputDir = constants_1.OUTPUT_DIR, emptyOutDir = true, minify = false, root = process.cwd(), // This is the vite default, so using that as our default
26
- }) {
26
+ logLevel = 'info', }) {
27
27
  return __awaiter(this, void 0, void 0, function* () {
28
28
  const output = (0, utils_1.getUrlSafeFileName)(file);
29
29
  yield (0, vite_1.build)({
30
+ logLevel,
30
31
  root,
31
32
  define: {
32
33
  'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'production'),
@@ -51,7 +52,7 @@ function buildSingleExtension({ file, outputDir = constants_1.OUTPUT_DIR, emptyO
51
52
  });
52
53
  }
53
54
  exports.buildSingleExtension = buildSingleExtension;
54
- function remoteBuild(root, entryPoint, outputDir = constants_1.OUTPUT_DIR) {
55
+ function remoteBuild(root, entryPoint, outputDir = constants_1.OUTPUT_DIR, logLevel) {
55
56
  return __awaiter(this, void 0, void 0, function* () {
56
57
  const fileInfo = path_1.default.parse(entryPoint);
57
58
  if (!allowedExtensions.includes(fileInfo.ext)) {
@@ -62,6 +63,7 @@ function remoteBuild(root, entryPoint, outputDir = constants_1.OUTPUT_DIR) {
62
63
  outputDir,
63
64
  minify: true,
64
65
  root,
66
+ logLevel,
65
67
  });
66
68
  });
67
69
  }
@@ -1,4 +1,6 @@
1
- import { AppConfig, ExtensionConfigMap, LocalAppConfig } from './types';
1
+ import { AppConfig, ExtensionConfigMap, LocalAppConfig, Logger } from './types';
2
2
  export declare function loadConfigByPath<T = unknown>(configPath: string): T;
3
3
  export declare function loadExtensionConfig(appConfig: AppConfig, appPath: string): ExtensionConfigMap;
4
- export declare function loadLocalConfig(appPath: string): LocalAppConfig | undefined;
4
+ export declare function validateProxyConfigKey(urlKey: string, logger: Logger, localConfigPath: string): void;
5
+ export declare function validateProxyConfigValue(value: string, key: string, logger: Logger, localConfigPath: string): void;
6
+ export declare function loadLocalConfig(appPath: string, logger: Logger): LocalAppConfig | undefined;
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  return (mod && mod.__esModule) ? mod : { "default": mod };
6
6
  };
7
7
  Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.loadLocalConfig = exports.loadExtensionConfig = exports.loadConfigByPath = void 0;
8
+ exports.loadLocalConfig = exports.validateProxyConfigValue = exports.validateProxyConfigKey = exports.loadExtensionConfig = exports.loadConfigByPath = void 0;
9
9
  const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
11
  const utils_1 = require("./utils");
@@ -17,6 +17,9 @@ exports.loadConfigByPath = loadConfigByPath;
17
17
  function loadExtensionConfig(appConfig, appPath) {
18
18
  var _a, _b;
19
19
  const crmCardsSubConfigFiles = (_b = (_a = appConfig === null || appConfig === void 0 ? void 0 : appConfig.extensions) === null || _a === void 0 ? void 0 : _a.crm) === null || _b === void 0 ? void 0 : _b.cards;
20
+ if (!crmCardsSubConfigFiles) {
21
+ throw new Error("Unable to find extensions files, make sure the 'extensions.crm.cards' array is defined in the application configuration file");
22
+ }
20
23
  const outputConfig = {};
21
24
  crmCardsSubConfigFiles.forEach((card) => {
22
25
  var _a, _b;
@@ -38,16 +41,47 @@ function loadExtensionConfig(appConfig, appPath) {
38
41
  return outputConfig;
39
42
  }
40
43
  exports.loadExtensionConfig = loadExtensionConfig;
41
- function loadLocalConfig(appPath) {
44
+ function validateProxyConfigKey(urlKey, logger, localConfigPath) {
42
45
  try {
43
- const localConfig = path_1.default.join(appPath, 'local.json');
44
- if (fs_1.default.existsSync(localConfig)) {
45
- return JSON.parse(fs_1.default.readFileSync(localConfig).toString());
46
+ const url = new URL(urlKey);
47
+ if (url.pathname !== '/') {
48
+ logger.warn(`The key "${urlKey}" in "${localConfigPath}" is invalid, paths are not supported for keys`);
46
49
  }
47
50
  }
48
51
  catch (e) {
52
+ logger.warn(`The key "${urlKey}" in "${localConfigPath}" is an invalid url`);
53
+ }
54
+ }
55
+ exports.validateProxyConfigKey = validateProxyConfigKey;
56
+ function validateProxyConfigValue(value, key, logger, localConfigPath) {
57
+ try {
58
+ // eslint-disable-next-line no-new
59
+ new URL(value);
60
+ }
61
+ catch (e) {
62
+ logger.warn(`The value "${value}" for key "${key}" in "${localConfigPath}" is an invalid url`);
63
+ }
64
+ }
65
+ exports.validateProxyConfigValue = validateProxyConfigValue;
66
+ function loadLocalConfig(appPath, logger) {
67
+ const localConfigFilename = 'local.json';
68
+ const localConfigPath = path_1.default.join(appPath, localConfigFilename);
69
+ if (!fs_1.default.existsSync(localConfigPath)) {
70
+ return undefined;
71
+ }
72
+ try {
73
+ const localConfig = JSON.parse(fs_1.default.readFileSync(localConfigPath).toString());
74
+ const { proxy = {} } = localConfig;
75
+ Object.entries(proxy).forEach((entry) => {
76
+ const [key, value] = entry;
77
+ validateProxyConfigKey(key, logger, localConfigFilename);
78
+ validateProxyConfigValue(value, key, logger, localConfigFilename);
79
+ });
80
+ return localConfig;
81
+ }
82
+ catch (e) {
83
+ logger.error(`Error loading and parsing ${localConfigPath}, ${e}`);
49
84
  return undefined;
50
85
  }
51
- return undefined;
52
86
  }
53
87
  exports.loadLocalConfig = loadLocalConfig;
@@ -39,9 +39,11 @@ function startDevServer({ devServerState, viteDevServer, }) {
39
39
  app.use((0, cors_1.default)());
40
40
  app.use(express_1.default.static(devServerState.outputDir));
41
41
  const capabilities = [...constants_1.SERVER_CAPABILITIES];
42
- const middlewares = [
43
- (0, app_functions_dev_server_1.AppFunctionExecutionService)(Object.assign(Object.assign({}, devServerState.functionsConfig), { logger: devServerState.logger })),
44
- ];
42
+ const middlewares = [];
43
+ if (!devServerState.isPublicApp()) {
44
+ middlewares.push((0, app_functions_dev_server_1.AppFunctionExecutionService)(Object.assign(Object.assign({}, devServerState.functionsConfig), { logger: devServerState.logger })));
45
+ devServerState.logger.info(`Serving app functions locally (platform version ${devServerState.functionsConfig.platformVersion})`);
46
+ }
45
47
  if (devServerState.localDevUrlMapping) {
46
48
  devServerState.logger.info('Proxy config discovered, enabling local proxy mode');
47
49
  middlewares.push((0, app_functions_dev_server_1.AppProxyService)({
@@ -52,8 +54,9 @@ function startDevServer({ devServerState, viteDevServer, }) {
52
54
  }));
53
55
  capabilities.push(constants_1.PROXY_CAPABILITY);
54
56
  }
55
- app.use('/api/crm-extensibility/execution/internal/v3', ...middlewares);
56
- devServerState.logger.info(`Serving app functions locally (platform version ${devServerState.functionsConfig.platformVersion})`);
57
+ if (middlewares.length > 0) {
58
+ app.use('/api/crm-extensibility/execution/internal/v3', ...middlewares);
59
+ }
57
60
  const endpointsAdded = extensionsService_1.default.add(app, devServerState, capabilities);
58
61
  const { expressPort } = devServerState;
59
62
  endpointsAdded.forEach((endpoint) => {
@@ -77,9 +80,15 @@ function startDevServer({ devServerState, viteDevServer, }) {
77
80
  });
78
81
  return function shutdown() {
79
82
  return __awaiter(this, void 0, void 0, function* () {
80
- yield viteDevServer.pluginContainer.close();
81
- // Stop new connections to express server
82
- server.close(() => { });
83
+ try {
84
+ yield viteDevServer.pluginContainer.close();
85
+ // Stop new connections to express server
86
+ server.close(() => { });
87
+ yield viteDevServer.close();
88
+ }
89
+ catch (e) {
90
+ devServerState.logger.debug(`Error shutting down ${e}`);
91
+ }
83
92
  devServerState.logger.info('Extension dev server done cleaning up');
84
93
  });
85
94
  };
@@ -74,7 +74,10 @@ export interface PublicAppConfig {
74
74
  };
75
75
  };
76
76
  }
77
- export type AppConfig = PrivateAppConfig | PublicAppConfig;
77
+ export interface NonCanonicalAppMetadata {
78
+ isPublicApp?: boolean;
79
+ }
80
+ export type AppConfig = (PrivateAppConfig | PublicAppConfig) & NonCanonicalAppMetadata;
78
81
  export interface ProjectComponent {
79
82
  type: typeof PUBLIC_APP | typeof PRIVATE_APP;
80
83
  config: AppConfig;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hubspot/ui-extensions-dev-server",
3
- "version": "0.8.15",
3
+ "version": "0.8.17",
4
4
  "description": "",
5
5
  "scripts": {
6
6
  "test": "jest",
@@ -10,9 +10,8 @@
10
10
  "prepare": "npm run build",
11
11
  "lint": "echo 'no lint step for @hubspot/ui-extensions-dev-server'",
12
12
  "jest": "jest --watch",
13
- "integration-test-private-app": "npm run integration-test-private-app --prefix ./integrationTests/fixtures",
14
- "integration-test-public-app": "npm run integration-test-public-app --prefix ./integrationTests/fixtures",
15
- "integration-test": "npm run build && npm run integration-test-private-app && npm run integration-test-public-app"
13
+ "integration-test": "npm run --prefix 'integrationTests' integration-test",
14
+ "integration-test-no-setup": "npm run --prefix 'integrationTests' integration-test-no-setup"
16
15
  },
17
16
  "publishConfig": {
18
17
  "access": "public"
@@ -21,12 +20,11 @@
21
20
  ".": "./dist/index.js"
22
21
  },
23
22
  "files": [
24
- "dist",
25
- "README.md"
23
+ "dist"
26
24
  ],
27
25
  "license": "MIT",
28
26
  "dependencies": {
29
- "@hubspot/app-functions-dev-server": "^0.8.15",
27
+ "@hubspot/app-functions-dev-server": "0.8.17",
30
28
  "cors": "^2.8.5",
31
29
  "detect-port": "1.5.1",
32
30
  "estraverse": "^5.3.0",
@@ -40,7 +38,8 @@
40
38
  "@types/inquirer": "^9.0.3",
41
39
  "@types/jest": "^29.5.4",
42
40
  "acorn": "^8.11.2",
43
- "axios": "^1.4.0",
41
+ "ava": "4.3.3",
42
+ "axios": "^1.6.8",
44
43
  "jest": "^29.5.0",
45
44
  "ts-jest": "^29.1.1",
46
45
  "typescript": "^5.1.6",
@@ -64,5 +63,5 @@
64
63
  "optional": true
65
64
  }
66
65
  },
67
- "gitHead": "48b7ed5d22520fb76d9ea2e8d97c9645cf3f8419"
66
+ "gitHead": "c056fcf4f48454c6f4cd7986fd1f5b5f619d1605"
68
67
  }