@hubspot/ui-extensions-dev-server 1.1.0 → 1.1.1

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.
Files changed (95) hide show
  1. package/dist/index.d.ts +4 -0
  2. package/dist/index.js +4 -0
  3. package/dist/lib/DevModeInterface.d.ts +9 -0
  4. package/dist/lib/DevModeInterface.js +36 -0
  5. package/dist/lib/DevModeParentInterface.d.ts +19 -0
  6. package/dist/lib/DevModeParentInterface.js +181 -0
  7. package/dist/lib/DevModeUnifiedInterface.d.ts +9 -0
  8. package/dist/lib/DevModeUnifiedInterface.js +118 -0
  9. package/dist/lib/DevServerState.d.ts +44 -0
  10. package/dist/lib/DevServerState.js +95 -0
  11. package/dist/lib/ExtensionsWebSocket.d.ts +25 -0
  12. package/dist/lib/ExtensionsWebSocket.js +110 -0
  13. package/dist/lib/__mocks__/config.d.ts +2 -0
  14. package/dist/lib/__mocks__/config.js +5 -0
  15. package/dist/lib/__mocks__/isExtensionFile.d.ts +5 -0
  16. package/dist/lib/__mocks__/isExtensionFile.js +11 -0
  17. package/dist/lib/__tests__/DevModeInterface.spec.d.ts +1 -0
  18. package/dist/lib/__tests__/DevModeInterface.spec.js +155 -0
  19. package/dist/lib/__tests__/DevModeParentInterface.spec.d.ts +1 -0
  20. package/dist/lib/__tests__/DevModeParentInterface.spec.js +179 -0
  21. package/dist/lib/__tests__/DevModeUnifiedInterface.spec.d.ts +1 -0
  22. package/dist/lib/__tests__/DevModeUnifiedInterface.spec.js +236 -0
  23. package/dist/lib/__tests__/ExtensionsWebSocket.spec.d.ts +1 -0
  24. package/dist/lib/__tests__/ExtensionsWebSocket.spec.js +304 -0
  25. package/dist/lib/__tests__/ast.spec.d.ts +1 -0
  26. package/dist/lib/__tests__/ast.spec.js +737 -0
  27. package/dist/lib/__tests__/build.spec.d.ts +1 -0
  28. package/dist/lib/__tests__/build.spec.js +159 -0
  29. package/dist/lib/__tests__/config.spec.d.ts +1 -0
  30. package/dist/lib/__tests__/config.spec.js +291 -0
  31. package/dist/lib/__tests__/dev.spec.d.ts +1 -0
  32. package/dist/lib/__tests__/dev.spec.js +80 -0
  33. package/dist/lib/__tests__/extensionsService.spec.d.ts +1 -0
  34. package/dist/lib/__tests__/extensionsService.spec.js +150 -0
  35. package/dist/lib/__tests__/factories.d.ts +48 -0
  36. package/dist/lib/__tests__/factories.js +32 -0
  37. package/dist/lib/__tests__/fixtures/extensionConfig.d.ts +182 -0
  38. package/dist/lib/__tests__/fixtures/extensionConfig.js +304 -0
  39. package/dist/lib/__tests__/fixtures/urls.d.ts +4 -0
  40. package/dist/lib/__tests__/fixtures/urls.js +4 -0
  41. package/dist/lib/__tests__/parsing-utils.spec.d.ts +1 -0
  42. package/dist/lib/__tests__/parsing-utils.spec.js +467 -0
  43. package/dist/lib/__tests__/plugins/codeBlockingPlugin.spec.d.ts +1 -0
  44. package/dist/lib/__tests__/plugins/codeBlockingPlugin.spec.js +112 -0
  45. package/dist/lib/__tests__/plugins/codeCheckingPlugin.spec.d.ts +1 -0
  46. package/dist/lib/__tests__/plugins/codeCheckingPlugin.spec.js +111 -0
  47. package/dist/lib/__tests__/plugins/devBuildPlugin.spec.d.ts +1 -0
  48. package/dist/lib/__tests__/plugins/devBuildPlugin.spec.js +345 -0
  49. package/dist/lib/__tests__/plugins/friendlyLoggingPlugin.spec.d.ts +1 -0
  50. package/dist/lib/__tests__/plugins/friendlyLoggingPlugin.spec.js +65 -0
  51. package/dist/lib/__tests__/plugins/manifestPlugin.spec.d.ts +1 -0
  52. package/dist/lib/__tests__/plugins/manifestPlugin.spec.js +455 -0
  53. package/dist/lib/__tests__/plugins/relevantModulesPlugin.spec.d.ts +1 -0
  54. package/dist/lib/__tests__/plugins/relevantModulesPlugin.spec.js +81 -0
  55. package/dist/lib/__tests__/server.spec.d.ts +1 -0
  56. package/dist/lib/__tests__/server.spec.js +152 -0
  57. package/dist/lib/__tests__/test-utils/ast.d.ts +1 -0
  58. package/dist/lib/__tests__/test-utils/ast.js +4 -0
  59. package/dist/lib/__tests__/utils.spec.d.ts +1 -0
  60. package/dist/lib/__tests__/utils.spec.js +176 -0
  61. package/dist/lib/ast.d.ts +16 -0
  62. package/dist/lib/ast.js +281 -0
  63. package/dist/lib/bin/cli.d.ts +2 -0
  64. package/dist/lib/bin/cli.js +143 -0
  65. package/dist/lib/build.d.ts +24 -0
  66. package/dist/lib/build.js +73 -0
  67. package/dist/lib/config.d.ts +7 -0
  68. package/dist/lib/config.js +124 -0
  69. package/dist/lib/constants.d.ts +32 -0
  70. package/dist/lib/constants.js +43 -0
  71. package/dist/lib/dev.d.ts +2 -0
  72. package/dist/lib/dev.js +58 -0
  73. package/dist/lib/extensionsService.d.ts +10 -0
  74. package/dist/lib/extensionsService.js +45 -0
  75. package/dist/lib/parsing-utils.d.ts +31 -0
  76. package/dist/lib/parsing-utils.js +289 -0
  77. package/dist/lib/plugins/codeBlockingPlugin.d.ts +8 -0
  78. package/dist/lib/plugins/codeBlockingPlugin.js +45 -0
  79. package/dist/lib/plugins/codeCheckingPlugin.d.ts +8 -0
  80. package/dist/lib/plugins/codeCheckingPlugin.js +89 -0
  81. package/dist/lib/plugins/devBuildPlugin.d.ts +8 -0
  82. package/dist/lib/plugins/devBuildPlugin.js +201 -0
  83. package/dist/lib/plugins/friendlyLoggingPlugin.d.ts +14 -0
  84. package/dist/lib/plugins/friendlyLoggingPlugin.js +36 -0
  85. package/dist/lib/plugins/manifestPlugin.d.ts +12 -0
  86. package/dist/lib/plugins/manifestPlugin.js +158 -0
  87. package/dist/lib/plugins/relevantModulesPlugin.d.ts +13 -0
  88. package/dist/lib/plugins/relevantModulesPlugin.js +25 -0
  89. package/dist/lib/server.d.ts +13 -0
  90. package/dist/lib/server.js +99 -0
  91. package/dist/lib/types.d.ts +290 -0
  92. package/dist/lib/types.js +12 -0
  93. package/dist/lib/utils.d.ts +25 -0
  94. package/dist/lib/utils.js +113 -0
  95. package/package.json +1 -1
@@ -0,0 +1,36 @@
1
+ import path from 'path';
2
+ const unfriendlyCodeMapper = Object.freeze({
3
+ UNRESOLVED_IMPORT: (loggable) => {
4
+ const { exporter, id } = loggable;
5
+ const { base: extension } = path.parse(id);
6
+ return {
7
+ message: `${exporter} is imported by ${extension}, but ${exporter} cannot be resolved. Make sure ${exporter} is installed by running \`hs project install-deps\`.`,
8
+ level: 'error',
9
+ };
10
+ },
11
+ MISSING_GLOBAL_NAME: () => {
12
+ return {};
13
+ },
14
+ });
15
+ const unfriendlyCodeList = Object.freeze(Object.keys(unfriendlyCodeMapper));
16
+ function friendlyLoggingPlugin({ logger }) {
17
+ return {
18
+ name: 'ui-extensions-friendly-logging-plugin',
19
+ enforce: 'post',
20
+ onLog(_level, log) {
21
+ if (unfriendlyCodeList.includes(log.code || '')) {
22
+ const { message, level } = _mapMessageToFriendlyVersion(log);
23
+ if (message && level) {
24
+ logger[level](message);
25
+ }
26
+ return false; // Filter the log message
27
+ }
28
+ return true; // We don't have a friendly log message, let it through
29
+ },
30
+ };
31
+ }
32
+ function _mapMessageToFriendlyVersion(loggable) {
33
+ const { code } = loggable;
34
+ return unfriendlyCodeMapper[code]?.(loggable) ?? {};
35
+ }
36
+ export default friendlyLoggingPlugin;
@@ -0,0 +1,12 @@
1
+ import { Rollup } from 'vite';
2
+ import { Logger, ManifestConfig } from '../types.ts';
3
+ export interface ManifestPluginOptions {
4
+ output: string;
5
+ minify?: boolean;
6
+ extensionPath?: string;
7
+ logger: Logger;
8
+ manifestConfig?: ManifestConfig;
9
+ }
10
+ export type ManifestPlugin = (options: ManifestPluginOptions) => Rollup.Plugin;
11
+ declare const manifestPlugin: ManifestPlugin;
12
+ export default manifestPlugin;
@@ -0,0 +1,158 @@
1
+ import { readFileSync } from 'fs';
2
+ import { normalize } from 'path';
3
+ import { MANIFEST_FILE } from "../constants.js";
4
+ import path from 'path';
5
+ import { isExtensionFile, isNodeModule } from "../utils.js";
6
+ import { traverseAbstractSyntaxTree } from "../ast.js";
7
+ const PACKAGE_LOCK_FILE = 'package-lock.json';
8
+ const PACKAGE_FILE = 'package.json';
9
+ /**
10
+ * Checks if a given filename is a valid module file path. This is necessary because
11
+ * Rollup sometimes passes in a filename that starts with a null byte, which is not a valid
12
+ * module file path. From the Rollup documentation:
13
+ *
14
+ * > If your plugin uses 'virtual modules' (e.g. for helper functions), prefix the module ID with \0.
15
+ * > This prevents other plugins from trying to process it.
16
+ * Source: https://rollupjs.org/plugin-development/#conventions
17
+ *
18
+ * @param filename The filename to check.
19
+ * @returns Whether the filename is a valid module file path.
20
+ */
21
+ const isRealModuleFile = (filename) => typeof filename === 'string' && !filename.startsWith('\0');
22
+ const manifestPlugin = (options) => {
23
+ let allDataDependencies;
24
+ return {
25
+ name: 'ui-extensions-manifest-generation-plugin',
26
+ enforce: 'post', // run after default rollup plugins
27
+ buildStart() {
28
+ // Reset the source metadata for the new build
29
+ allDataDependencies = [];
30
+ },
31
+ transform(code, filename) {
32
+ const { logger } = options;
33
+ // We only need to parse the AST for extension files, so check that first.
34
+ const isExtension = isRealModuleFile(filename) &&
35
+ isExtensionFile(filename, options.extensionPath || '');
36
+ if (!isExtension) {
37
+ return { code, map: null };
38
+ }
39
+ try {
40
+ const ast = this.parse(code);
41
+ const sourceCodeMetadata = traverseAbstractSyntaxTree(ast, [], options.extensionPath || process.cwd(), logger);
42
+ allDataDependencies = [
43
+ ...allDataDependencies,
44
+ ...sourceCodeMetadata.dataDependencies.dependencies,
45
+ ];
46
+ }
47
+ catch (e) {
48
+ logger.error(`Unable to parse source code for ${filename}, ${e}`);
49
+ }
50
+ // We don't need to actually transform anything, just collect data, so return the original code
51
+ return { code, map: null };
52
+ },
53
+ generateBundle(_rollupOptions, bundle) {
54
+ const { output, minify = false, extensionPath = process.cwd(), logger, } = options;
55
+ try {
56
+ const filename = path.parse(output).name;
57
+ const manifest = _generateManifestContents(bundle, extensionPath, allDataDependencies, options?.manifestConfig);
58
+ this.emitFile({
59
+ type: 'asset',
60
+ source: minify
61
+ ? JSON.stringify(manifest)
62
+ : JSON.stringify(manifest, null, 2),
63
+ fileName: normalize(`${filename}-${MANIFEST_FILE}`),
64
+ });
65
+ }
66
+ catch (e) {
67
+ logger.warn(`\nUnable to write manifest file in ${output}, ${e}`);
68
+ }
69
+ },
70
+ };
71
+ };
72
+ function _generateManifestContents(bundle, extensionPath, allDataDependencies, appConfig) {
73
+ const baseManifest = {
74
+ package: _loadPackageFile(extensionPath),
75
+ };
76
+ const dataDependencies = {
77
+ dataDeps: allDataDependencies ?? [],
78
+ };
79
+ const variables = {
80
+ variables: {},
81
+ };
82
+ if (appConfig &&
83
+ 'variables' in appConfig &&
84
+ typeof appConfig.variables === 'object' &&
85
+ appConfig.variables !== null) {
86
+ variables.variables = appConfig.variables;
87
+ }
88
+ // The keys to bundle are the filename without any path information
89
+ const bundles = Object.keys(bundle).filter((cur) => cur.endsWith('.js'));
90
+ if (bundles.length === 1) {
91
+ return {
92
+ ..._generateManifestEntry(bundle[bundles[0]], extensionPath),
93
+ ...dataDependencies,
94
+ ...baseManifest,
95
+ ...variables,
96
+ };
97
+ }
98
+ const manifest = bundles.reduce((acc, current) => {
99
+ return {
100
+ ...acc,
101
+ [current]: _generateManifestEntry(bundle[current], extensionPath),
102
+ };
103
+ }, {});
104
+ return {
105
+ ...manifest,
106
+ ...dataDependencies,
107
+ ...baseManifest,
108
+ ...variables,
109
+ };
110
+ }
111
+ function _generateManifestEntry(subBundle, extensionPath) {
112
+ const { facadeModuleId, moduleIds, modules } = subBundle;
113
+ return {
114
+ entry: _stripPathPriorToExtDir(facadeModuleId, extensionPath),
115
+ modules: _buildModulesInfo(moduleIds, modules, extensionPath),
116
+ };
117
+ }
118
+ function _loadJsonFileSafely(extensionPath, filename) {
119
+ try {
120
+ return JSON.parse(readFileSync(path.join(extensionPath, filename)).toString());
121
+ }
122
+ catch (e) {
123
+ return undefined;
124
+ }
125
+ }
126
+ function _loadPackageFile(extensionPath) {
127
+ // Look for package-lock.json then fallback to package.json
128
+ return (_loadJsonFileSafely(extensionPath, PACKAGE_LOCK_FILE) ||
129
+ _loadJsonFileSafely(extensionPath, PACKAGE_FILE));
130
+ }
131
+ function _stripPathPriorToExtDir(filepath, extensionPath) {
132
+ // Before stripping the path, make sure the given extensionPath ends with a slash.
133
+ extensionPath = extensionPath.endsWith('/')
134
+ ? extensionPath
135
+ : `${extensionPath}/`;
136
+ return filepath?.split(extensionPath).pop();
137
+ }
138
+ function _buildModulesInfo(moduleIds, modules, extensionPath) {
139
+ const accumulator = {
140
+ internal: [],
141
+ external: [],
142
+ };
143
+ return moduleIds.reduce((acc, mod) => {
144
+ const { renderedExports } = modules[mod];
145
+ const moduleData = {
146
+ module: _stripPathPriorToExtDir(mod, extensionPath),
147
+ renderedExports,
148
+ };
149
+ if (isNodeModule(moduleData.module)) {
150
+ acc.external.push(moduleData);
151
+ }
152
+ else {
153
+ acc.internal.push(moduleData);
154
+ }
155
+ return acc;
156
+ }, accumulator);
157
+ }
158
+ export default manifestPlugin;
@@ -0,0 +1,13 @@
1
+ import * as Vite from 'vite';
2
+ import { Logger } from '../types.ts';
3
+ export interface RelevantModules {
4
+ [key: string]: string[];
5
+ }
6
+ export declare function getRelevantModules(output: string): string[];
7
+ export interface RelevantModulesPluginOptions {
8
+ output: string;
9
+ logger: Logger;
10
+ }
11
+ export type RelevantModulesPlugin = (options: RelevantModulesPluginOptions) => Vite.Plugin;
12
+ declare const relevantModulesPlugin: RelevantModulesPlugin;
13
+ export default relevantModulesPlugin;
@@ -0,0 +1,25 @@
1
+ import { isNodeModule } from "../utils.js";
2
+ const relevantModules = {};
3
+ export function getRelevantModules(output) {
4
+ return relevantModules[output] || [];
5
+ }
6
+ const relevantModulesPlugin = ({ output, logger }) => {
7
+ return {
8
+ name: 'ui-extensions-relevant-modules-plugin',
9
+ generateBundle(_options, bundle) {
10
+ const subBundle = bundle[output];
11
+ if (!subBundle || !subBundle.moduleIds) {
12
+ // If this happens, something has gone horribly wrong
13
+ logger.error('Invalid bundle format, please try saving the extension again. If the problem persists try restarting `hs project dev`');
14
+ return;
15
+ }
16
+ const updatedRelevantModules = subBundle.moduleIds.filter((moduleId) => !isNodeModule(moduleId));
17
+ if (updatedRelevantModules.length === 0) {
18
+ logger.error('Unable to determine relevant files to watch, please try saving the extension again. If the problem persists try restarting `hs project dev`');
19
+ return;
20
+ }
21
+ relevantModules[output] = updatedRelevantModules;
22
+ },
23
+ };
24
+ };
25
+ export default relevantModulesPlugin;
@@ -0,0 +1,13 @@
1
+ import { Server } from 'http';
2
+ import { ViteDevServer } from 'vite';
3
+ import { DevServerState } from './DevServerState.ts';
4
+ interface StartDevServerArgs {
5
+ devServerState: DevServerState;
6
+ viteDevServer: ViteDevServer;
7
+ }
8
+ interface StartDevServerReturn {
9
+ httpServer: Server;
10
+ shutdown: () => Promise<void>;
11
+ }
12
+ declare function startDevServer({ devServerState, viteDevServer, }: StartDevServerArgs): Promise<StartDevServerReturn>;
13
+ export default startDevServer;
@@ -0,0 +1,99 @@
1
+ import express from 'express';
2
+ import cors from 'cors';
3
+ import { PROXY_CAPABILITY, SERVER_CAPABILITIES } from "./constants.js";
4
+ import extensionsService from "./extensionsService.js";
5
+ import { AppFunctionExecutionService, AppProxyService, } from '@hubspot/app-functions-dev-server';
6
+ import { extractAllowedUrls } from "./utils.js";
7
+ import { ExtensionsWebSocket } from "./ExtensionsWebSocket.js";
8
+ function listen(app, port) {
9
+ return new Promise((resolve, reject) => {
10
+ const server = app
11
+ .listen({ port }, () => {
12
+ resolve(server);
13
+ })
14
+ .on('error', (err) => {
15
+ reject(err);
16
+ });
17
+ });
18
+ }
19
+ async function startDevServer({ devServerState, viteDevServer, }) {
20
+ const app = express();
21
+ // Setup middleware
22
+ app.use(cors());
23
+ app.use(express.static(devServerState.outputDir));
24
+ const capabilities = [...SERVER_CAPABILITIES];
25
+ const middlewares = [];
26
+ if (!devServerState.isPublicApp()) {
27
+ middlewares.push(AppFunctionExecutionService({
28
+ ...devServerState.functionsConfig,
29
+ logger: devServerState.logger,
30
+ }));
31
+ devServerState.logger.info(`Serving app functions locally (platform version ${devServerState.functionsConfig.platformVersion})`);
32
+ }
33
+ if (devServerState.localDevUrlMapping &&
34
+ Object.keys(devServerState.localDevUrlMapping).length > 0) {
35
+ devServerState.logger.info('Proxy config discovered, enabling local proxy mode');
36
+ middlewares.push(AppProxyService({
37
+ localDevUrlMapping: devServerState.localDevUrlMapping,
38
+ logger: devServerState.logger,
39
+ accountId: devServerState.functionsConfig.accountId,
40
+ allowedUrls: extractAllowedUrls(devServerState.appConfig),
41
+ }));
42
+ capabilities.push(PROXY_CAPABILITY);
43
+ }
44
+ if (middlewares.length > 0) {
45
+ app.use('/api/crm-extensibility/execution/internal/v3', ...middlewares);
46
+ }
47
+ const endpointsAdded = extensionsService.add(app, devServerState, capabilities);
48
+ const { expressPort } = devServerState;
49
+ endpointsAdded.forEach((endpoint) => {
50
+ devServerState.logger.debug(`Listening at http://hslocal.net:${expressPort}${endpoint}`);
51
+ });
52
+ // Vite middlewares needs to go last because it's greedy and will block other middleware
53
+ app.use(viteDevServer.middlewares);
54
+ let server;
55
+ try {
56
+ server = await listen(app, devServerState.expressPort);
57
+ }
58
+ catch (e) {
59
+ if (e.code === 'EADDRINUSE') {
60
+ throw new Error(`Port ${devServerState.expressPort} is already in use.`);
61
+ }
62
+ throw new Error(e);
63
+ }
64
+ /**
65
+ * We use a custom WebSocket server instead of Vite's built-in one because:
66
+ * 1. We send HubSpot-specific extension metadata (not standard HMR messages)
67
+ * 2. We need cross-origin connections from HubSpot iframes
68
+ * 3. Our message format { event: 'start', ...metadata } doesn't match Vite's protocol
69
+ */
70
+ const wss = new ExtensionsWebSocket(server, devServerState);
71
+ devServerState.extensionsWebSocket = wss;
72
+ // Trigger the WebSocket setup that was deferred from the plugin's configureServer hook
73
+ // This must happen after the WebSocket is initialized to avoid race conditions
74
+ devServerState.triggerWebSocketSetup();
75
+ devServerState.extensionsMetadata?.forEach((metadata) => {
76
+ const { baseMessage } = metadata;
77
+ devServerState.logger.debug(`Listening at ${baseMessage.callback}`);
78
+ });
79
+ return {
80
+ httpServer: server,
81
+ shutdown: async function shutdown() {
82
+ try {
83
+ await viteDevServer.pluginContainer.close();
84
+ // Close WebSocket server first to reject new connections
85
+ await devServerState.getExtensionsWebSocket().close();
86
+ // Close HTTP server
87
+ await new Promise((resolve) => {
88
+ server.close(() => resolve());
89
+ });
90
+ await viteDevServer.close();
91
+ }
92
+ catch (e) {
93
+ devServerState.logger.debug(`Error shutting down ${e}`);
94
+ }
95
+ devServerState.logger.info('Extension dev server done cleaning up');
96
+ },
97
+ };
98
+ }
99
+ export default startDevServer;
@@ -0,0 +1,290 @@
1
+ import { PLATFORM_VERSION, PRIVATE_APP, PUBLIC_APP } from './constants.ts';
2
+ import { LocalDevUrlMapping } from '@hubspot/app-functions-dev-server';
3
+ export interface ObjectTypes {
4
+ name: string;
5
+ }
6
+ export interface ExtensionConfig {
7
+ type: string;
8
+ data: {
9
+ title: string;
10
+ location: string;
11
+ module: {
12
+ file: string;
13
+ };
14
+ objectTypes: ObjectTypes[];
15
+ uid?: string;
16
+ };
17
+ }
18
+ export interface ExtensionOutputDataConfig {
19
+ appName: string;
20
+ title: string;
21
+ location: string;
22
+ module: {
23
+ file: string;
24
+ };
25
+ objectTypes: ObjectTypes[];
26
+ uid?: string;
27
+ sourceId?: string | null;
28
+ }
29
+ export interface ExtensionOutputConfig extends ExtensionConfig {
30
+ type: string;
31
+ output: string;
32
+ path: string;
33
+ extensionPath: string;
34
+ extensionConfigPath: string;
35
+ data: ExtensionOutputDataConfig;
36
+ appConfig?: AppConfig;
37
+ }
38
+ export interface ExtensionConfigMap {
39
+ [key: string]: ExtensionOutputConfig;
40
+ }
41
+ interface CardConfig {
42
+ file: string;
43
+ }
44
+ export interface ProjectConfig {
45
+ name: string;
46
+ srcDir: string;
47
+ platformVersion?: PlatformVersion;
48
+ }
49
+ export interface PrivateAppConfig {
50
+ name: string;
51
+ description: string;
52
+ scopes: string[];
53
+ public: boolean;
54
+ extensions: {
55
+ crm: {
56
+ cards: CardConfig[];
57
+ };
58
+ };
59
+ uid?: string;
60
+ }
61
+ export interface LocalAppConfig {
62
+ proxy: LocalDevUrlMapping;
63
+ }
64
+ export interface PublicAppAuth {
65
+ preventAdditionalScopes: boolean;
66
+ preventOptionalScopes: boolean;
67
+ redirectUrls: string[];
68
+ requiredScopes: string[];
69
+ optionalScopes: string[];
70
+ additionalScopes: string[];
71
+ }
72
+ export declare enum UnifiedAppAuthTypes {
73
+ OAUTH = "OAUTH",
74
+ STATIC = "STATIC"
75
+ }
76
+ export interface UnifiedAppAuth {
77
+ type: UnifiedAppAuthTypes;
78
+ redirectUrls?: string[];
79
+ requiredScopes?: string[];
80
+ optionalScopes?: string[];
81
+ conditionallyRequiredScopes?: string[];
82
+ }
83
+ export type AppAuth = PublicAppAuth | UnifiedAppAuth;
84
+ export interface PublicAppConfig {
85
+ name: string;
86
+ uid: string;
87
+ logo?: string;
88
+ description?: string;
89
+ allowedUrls?: string[];
90
+ auth?: AppAuth;
91
+ support?: {
92
+ supportUrl: string;
93
+ supportEmail: string;
94
+ documentationUrl: string;
95
+ supportPhone: string;
96
+ };
97
+ extensions: {
98
+ crm: {
99
+ cards: CardConfig[];
100
+ };
101
+ };
102
+ }
103
+ export interface NonCanonicalAppMetadata {
104
+ isPublicApp?: boolean;
105
+ }
106
+ export type AppConfig = (PrivateAppConfig | PublicAppConfig) & NonCanonicalAppMetadata;
107
+ export interface ProjectComponent {
108
+ type: typeof PUBLIC_APP | typeof PRIVATE_APP;
109
+ config: AppConfig;
110
+ path: string;
111
+ }
112
+ export interface ProjectComponentMap {
113
+ [key: string]: ProjectComponent;
114
+ }
115
+ export type UnifiedCardConfig = {
116
+ uid: string;
117
+ type: 'card';
118
+ config: {
119
+ name: string;
120
+ description?: string;
121
+ location: string;
122
+ entrypoint: string;
123
+ objectTypes?: string[];
124
+ previewImage?: {
125
+ file: string;
126
+ altText: string;
127
+ };
128
+ };
129
+ };
130
+ export declare enum UnifiedComponentTypes {
131
+ CARD = "CARD",
132
+ APPLICATION = "APPLICATION",
133
+ SETTINGS = "SETTINGS",
134
+ PAGE = "PAGE"
135
+ }
136
+ export type UnifiedExtensionComponent = Omit<UnifiedCardConfig, 'type'> & {
137
+ componentType: UnifiedComponentTypes.CARD | UnifiedComponentTypes.SETTINGS | UnifiedComponentTypes.PAGE;
138
+ componentDeps: {
139
+ app: string;
140
+ };
141
+ metaFilePath: string;
142
+ files: {};
143
+ localDev: {
144
+ componentRoot: string;
145
+ componentConfigPath: string;
146
+ };
147
+ };
148
+ export interface UnifiedAppComponent {
149
+ uid: string;
150
+ config: {
151
+ description: string;
152
+ name: string;
153
+ logo?: string;
154
+ auth: UnifiedAppAuth;
155
+ permittedUrls?: {
156
+ fetch: string[];
157
+ iframe: string[];
158
+ img: string[];
159
+ };
160
+ support?: {
161
+ supportEmail: string;
162
+ documentationUrl: string;
163
+ supportUrl: string;
164
+ supportPhone: string;
165
+ };
166
+ variables?: {
167
+ [key: string]: string | number | boolean;
168
+ };
169
+ };
170
+ componentType: UnifiedComponentTypes.APPLICATION;
171
+ componentDeps: {};
172
+ metaFilePath: string;
173
+ files: {};
174
+ localDev: {
175
+ componentRoot: string;
176
+ componentConfigPath: string;
177
+ };
178
+ }
179
+ export type UnifiedAppConfig = Omit<PublicAppConfig, 'auth'> & {
180
+ auth: UnifiedAppAuth;
181
+ isPublicApp: boolean;
182
+ variables?: {
183
+ [key: string]: string | number | boolean;
184
+ };
185
+ };
186
+ export type UnifiedComponent = UnifiedExtensionComponent | UnifiedAppComponent;
187
+ export interface UnifiedProjectComponentMap {
188
+ [key: string]: UnifiedComponent;
189
+ }
190
+ export interface BaseMessage {
191
+ appName: string;
192
+ title: string;
193
+ callback: string;
194
+ sourceId?: string | null;
195
+ portalId?: number;
196
+ }
197
+ export interface ExtensionMetadata {
198
+ baseMessage: BaseMessage;
199
+ config: ExtensionOutputConfig;
200
+ }
201
+ export type PlatformVersion = (typeof PLATFORM_VERSION)[keyof typeof PLATFORM_VERSION];
202
+ export interface FunctionMetadata {
203
+ scope?: 'Global' | 'Local';
204
+ defined?: boolean;
205
+ invoked?: boolean;
206
+ }
207
+ export type DataDependency = {
208
+ referenceId: string;
209
+ properties: {
210
+ type: 'CrmRecordProperties';
211
+ recordProperties: string[];
212
+ options: {
213
+ [key: string]: any;
214
+ };
215
+ } | {
216
+ type: 'CrmRecordAssociationProperties';
217
+ toObjectTypeId: string;
218
+ requestProperties: string[];
219
+ paginationOptions: {
220
+ [key: string]: any;
221
+ };
222
+ options: {
223
+ [key: string]: any;
224
+ };
225
+ };
226
+ };
227
+ export type NodeValue = string | number | boolean | null | undefined | RegExp | bigint | symbol | NodeValue[] | NodeObject;
228
+ export type NodeObject = {
229
+ [key: string]: NodeValue;
230
+ };
231
+ export interface SourceCodeMetadata {
232
+ functions: {
233
+ [functionName: string]: FunctionMetadata;
234
+ };
235
+ badImports: string[];
236
+ dataDependencies: {
237
+ importedHooks: {
238
+ [hookName: string]: string;
239
+ };
240
+ dependencies: DataDependency[];
241
+ };
242
+ variableDeclarations: Map<string, NodeValue>;
243
+ }
244
+ export interface FunctionInvocationCheck {
245
+ functionName: string;
246
+ }
247
+ export type SourceCodeChecks = FunctionInvocationCheck[];
248
+ export interface Logger {
249
+ info: (...args: any[]) => void;
250
+ debug: (...args: any[]) => void;
251
+ warn: (...args: any[]) => void;
252
+ error: (...args: any[]) => void;
253
+ }
254
+ export interface AppExtensionMapping {
255
+ name: string;
256
+ value: ExtensionOutputConfig;
257
+ }
258
+ export interface DevModeStartArguments {
259
+ accountId?: number;
260
+ requestPorts?: (requestPortsData: Array<{
261
+ instanceId: string;
262
+ port?: number;
263
+ }>) => Promise<{
264
+ [instanceId: string]: number;
265
+ }>;
266
+ projectConfig?: ProjectConfig;
267
+ }
268
+ export interface DevModeBaseSetupArguments {
269
+ onUploadRequired?: VoidFunction;
270
+ logger: Logger;
271
+ urls: {
272
+ api: string;
273
+ web: string;
274
+ };
275
+ setActiveApp: (appUid: string | undefined) => Promise<void>;
276
+ choices?: AppExtensionMapping[];
277
+ }
278
+ export interface DevModeSetupArguments extends DevModeBaseSetupArguments {
279
+ components: ProjectComponentMap;
280
+ }
281
+ export interface UnifiedDevModeSetupArguments extends DevModeBaseSetupArguments {
282
+ components: UnifiedProjectComponentMap;
283
+ profileData?: ProfileVariables;
284
+ }
285
+ export type ProfileVariableValue = string | number | boolean;
286
+ export type ProfileVariables = Record<string, ProfileVariableValue>;
287
+ export interface ManifestConfig {
288
+ variables?: ProfileVariables;
289
+ }
290
+ export {};
@@ -0,0 +1,12 @@
1
+ export var UnifiedAppAuthTypes;
2
+ (function (UnifiedAppAuthTypes) {
3
+ UnifiedAppAuthTypes["OAUTH"] = "OAUTH";
4
+ UnifiedAppAuthTypes["STATIC"] = "STATIC";
5
+ })(UnifiedAppAuthTypes || (UnifiedAppAuthTypes = {}));
6
+ export var UnifiedComponentTypes;
7
+ (function (UnifiedComponentTypes) {
8
+ UnifiedComponentTypes["CARD"] = "CARD";
9
+ UnifiedComponentTypes["APPLICATION"] = "APPLICATION";
10
+ UnifiedComponentTypes["SETTINGS"] = "SETTINGS";
11
+ UnifiedComponentTypes["PAGE"] = "PAGE";
12
+ })(UnifiedComponentTypes || (UnifiedComponentTypes = {}));
@@ -0,0 +1,25 @@
1
+ import { AppConfig, ExtensionConfig } from './types.ts';
2
+ export declare function getUrlSafeFileName(filePath: string): string;
3
+ export declare function stripAnsiColorCodes(stringWithColorCodes: string | undefined | null): string | null;
4
+ export declare function loadManifest(outputDir: string, output: string): any;
5
+ export declare function buildSourceId(appConfig: AppConfig, extensionConfig: ExtensionConfig): string | null;
6
+ export declare function isNodeModule(filepath: string | undefined): boolean;
7
+ /**
8
+ * Check if a given file is within the extension path
9
+ */
10
+ export declare function isExtensionFile(filepath: string | undefined, extensionPath: string): boolean;
11
+ export declare class UnhandledPlatformVersionError extends Error {
12
+ constructor(platformVersion: never);
13
+ }
14
+ export declare function throwUnhandledPlatformVersionError(platformVersion: never): never;
15
+ export declare function extractAllowedUrls(appConfig?: AppConfig): string[];
16
+ /**
17
+ * This function generates a deterministic hash from any number of arguments
18
+ * Arrays and objects are stringified to ensure it works for all types.
19
+ * Uses the same simple hash algorithm as the browser version for consistency.
20
+ */
21
+ export declare function generateHash(...args: unknown[]): string;
22
+ /**
23
+ * Check if a given URL is an image (of a type we support)
24
+ */
25
+ export declare function isImage(url: string): boolean;