@hubspot/ui-extensions-dev-server 1.1.8 → 2.0.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.
Files changed (184) hide show
  1. package/dist/lib/DevModeParentInterface.js +5 -6
  2. package/dist/lib/DevModeUnifiedInterface.js +2 -2
  3. package/dist/lib/DevServerState.d.ts +14 -4
  4. package/dist/lib/DevServerState.js +34 -1
  5. package/dist/lib/__tests__/DevModeParentInterface.spec.js +42 -1
  6. package/dist/lib/__tests__/DevModeUnifiedInterface.spec.js +34 -0
  7. package/dist/lib/__tests__/DevServerState.spec.js +180 -0
  8. package/dist/lib/__tests__/{app-functions/errorReporter.spec.js → errorReporter.spec.js} +5 -5
  9. package/dist/lib/__tests__/extensionsService.spec.js +57 -9
  10. package/dist/lib/__tests__/factories.d.ts +1 -1
  11. package/dist/lib/__tests__/factories.js +1 -1
  12. package/dist/lib/__tests__/fixtures/extensionConfig.js +3 -0
  13. package/dist/lib/__tests__/plugins/devBuildPlugin.spec.js +1 -1
  14. package/dist/lib/__tests__/{app-functions/services → proxy}/AppProxyService.spec.js +73 -3
  15. package/dist/lib/__tests__/{app-functions → proxy}/signing.spec.js +1 -1
  16. package/dist/lib/__tests__/server.spec.js +14 -11
  17. package/dist/lib/__tests__/serverless/config.spec.js +61 -0
  18. package/dist/lib/__tests__/{app-functions → serverless}/context.spec.js +2 -2
  19. package/dist/lib/__tests__/serverless/executor_unsupported.spec.js +37 -0
  20. package/dist/lib/__tests__/{app-functions/executor_v20232.spec.js → serverless/executor_v20251.spec.js} +6 -13
  21. package/dist/lib/__tests__/{app-functions/executor_v20231.spec.js → serverless/executor_v202603.spec.js} +26 -11
  22. package/dist/lib/__tests__/{app-functions → serverless}/fixtures/constants.d.ts +4 -3
  23. package/dist/lib/__tests__/{app-functions → serverless}/fixtures/constants.js +9 -3
  24. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-async-fails.cjs +5 -0
  25. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-async-fails.d.cts +1 -0
  26. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-async-succeeds.cjs +5 -0
  27. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-async-succeeds.d.cts +3 -0
  28. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-does-not-export-main.cjs +4 -0
  29. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-does-not-export-main.d.cts +1 -0
  30. package/dist/lib/__tests__/{app-functions/fixtures/v2023.1/app.functions → serverless/fixtures/v2026.03/functions}/func-echos-input.cjs +3 -3
  31. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-echos-input.d.cts +5 -0
  32. package/dist/lib/__tests__/{app-functions/fixtures/v2023.1/app.functions → serverless/fixtures/v2026.03/functions}/func-logs.cjs +2 -2
  33. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-logs.d.cts +3 -0
  34. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-function.cjs +4 -0
  35. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-function.d.cts +1 -0
  36. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-implicitly.cjs +7 -0
  37. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-implicitly.d.cts +1 -0
  38. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-promise-rejected.cjs +4 -0
  39. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-promise-rejected.d.cts +1 -0
  40. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-promise-resolved.cjs +4 -0
  41. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-promise-resolved.d.cts +3 -0
  42. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-text.cjs +4 -0
  43. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-text.d.cts +1 -0
  44. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-undefined.cjs +4 -0
  45. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-undefined.d.cts +1 -0
  46. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-times-out.cjs +12 -0
  47. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-times-out.d.cts +1 -0
  48. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-undeclared.cjs +4 -0
  49. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-undeclared.d.cts +1 -0
  50. package/dist/lib/__tests__/{app-functions/fixtures/v2023.1/app.functions → serverless/fixtures/v2026.03/functions}/func-uses-secret.cjs +3 -3
  51. package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-uses-secret.d.cts +4 -0
  52. package/dist/lib/__tests__/{app-functions → serverless}/secrets.spec.js +4 -4
  53. package/dist/lib/__tests__/{app-functions → serverless}/services/PrivateAppUserTokenManager.spec.js +4 -4
  54. package/dist/lib/__tests__/serverless/services/services_v20251.spec.d.ts +1 -0
  55. package/dist/lib/__tests__/{app-functions/services/services_v20232.spec.js → serverless/services/services_v20251.spec.js} +9 -63
  56. package/dist/lib/__tests__/serverless/services/services_v202603.spec.d.ts +1 -0
  57. package/dist/lib/__tests__/{app-functions/services/services_v20231.spec.js → serverless/services/services_v202603.spec.js} +18 -91
  58. package/dist/lib/__tests__/serverless/setup.d.ts +1 -0
  59. package/dist/lib/__tests__/utils.spec.js +26 -1
  60. package/dist/lib/constants.d.ts +2 -2
  61. package/dist/lib/constants.js +2 -2
  62. package/dist/lib/{app-functions/errorReporter.js → errorReporter.js} +2 -2
  63. package/dist/lib/extensionsService.js +6 -0
  64. package/dist/lib/{app-functions/services → proxy}/AppProxyService.d.ts +1 -1
  65. package/dist/lib/{app-functions/services → proxy}/AppProxyService.js +15 -4
  66. package/dist/lib/proxy/constants.d.ts +13 -0
  67. package/dist/lib/{app-functions → proxy}/constants.js +0 -18
  68. package/dist/lib/proxy/types.d.ts +44 -0
  69. package/dist/lib/proxy/types.js +1 -0
  70. package/dist/lib/server.js +2 -1
  71. package/dist/lib/serverless/config.js +100 -0
  72. package/dist/lib/serverless/constants.d.ts +7 -0
  73. package/dist/lib/serverless/constants.js +12 -0
  74. package/dist/lib/{app-functions → serverless}/errors.d.ts +1 -0
  75. package/dist/lib/{app-functions → serverless}/errors.js +6 -0
  76. package/dist/lib/{app-functions → serverless}/executor.js +19 -13
  77. package/dist/lib/serverless/secrets.d.ts +5 -0
  78. package/dist/lib/{app-functions → serverless}/services/AppFunctionExecutionService.js +1 -1
  79. package/dist/lib/{app-functions → serverless}/services/PrivateAppUserTokenManager.js +1 -1
  80. package/dist/lib/{app-functions → serverless}/types.d.ts +1 -44
  81. package/dist/lib/types.d.ts +12 -3
  82. package/dist/lib/types.js +7 -2
  83. package/dist/lib/utils.d.ts +2 -1
  84. package/dist/lib/utils.js +3 -0
  85. package/package.json +8 -5
  86. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-fails.cjs +0 -8
  87. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-fails.d.cts +0 -1
  88. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-succeeds.cjs +0 -8
  89. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-succeeds.d.cts +0 -1
  90. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-rejected.cjs +0 -8
  91. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-resolved.cjs +0 -8
  92. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-resolved.d.cts +0 -1
  93. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-does-not-export-main.cjs +0 -4
  94. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-does-not-export-main.d.cts +0 -1
  95. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-echos-input.d.cts +0 -1
  96. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-logs.d.cts +0 -1
  97. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-function.cjs +0 -4
  98. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-function.d.cts +0 -1
  99. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-rejected.cjs +0 -7
  100. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-rejected.d.cts +0 -1
  101. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-resolved.cjs +0 -7
  102. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-resolved.d.cts +0 -1
  103. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-text.d.cts +0 -1
  104. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-undefined.cjs +0 -4
  105. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-undefined.d.cts +0 -1
  106. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-times-out.cjs +0 -10
  107. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-times-out.d.cts +0 -1
  108. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-undeclared.cjs +0 -4
  109. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-undeclared.d.cts +0 -1
  110. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-uses-secret.d.cts +0 -1
  111. package/dist/lib/app-functions/config.js +0 -48
  112. package/dist/lib/app-functions/constants.d.ts +0 -26
  113. package/dist/lib/app-functions/index.d.ts +0 -4
  114. package/dist/lib/app-functions/index.js +0 -4
  115. package/dist/lib/app-functions/secrets.d.ts +0 -5
  116. package/dist/lib/app-functions/services/index.d.ts +0 -3
  117. package/dist/lib/app-functions/services/index.js +0 -3
  118. /package/dist/lib/__tests__/{app-functions/context.spec.d.ts → DevServerState.spec.d.ts} +0 -0
  119. /package/dist/lib/__tests__/{app-functions/errorReporter.spec.d.ts → errorReporter.spec.d.ts} +0 -0
  120. /package/dist/lib/__tests__/{app-functions/services → proxy}/AppProxyService.spec.d.ts +0 -0
  121. /package/dist/lib/__tests__/{app-functions → proxy}/signing.spec.d.ts +0 -0
  122. /package/dist/lib/__tests__/{app-functions/executor_v20231.spec.d.ts → serverless/config.spec.d.ts} +0 -0
  123. /package/dist/lib/__tests__/{app-functions/executor_v20232.spec.d.ts → serverless/context.spec.d.ts} +0 -0
  124. /package/dist/lib/__tests__/{app-functions/secrets.spec.d.ts → serverless/executor_unsupported.spec.d.ts} +0 -0
  125. /package/dist/lib/__tests__/{app-functions/services/PrivateAppUserTokenManager.spec.d.ts → serverless/executor_v20251.spec.d.ts} +0 -0
  126. /package/dist/lib/__tests__/{app-functions/services/services_v20231.spec.d.ts → serverless/executor_v202603.spec.d.ts} +0 -0
  127. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-async-fails.cjs +0 -0
  128. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-async-fails.d.cts +0 -0
  129. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-async-succeeds.cjs +0 -0
  130. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-async-succeeds.d.cts +0 -0
  131. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-calls-callback.cjs +0 -0
  132. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-calls-callback.d.cts +0 -0
  133. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-does-not-export-main.cjs +0 -0
  134. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-does-not-export-main.d.cts +0 -0
  135. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-echos-input.cjs +0 -0
  136. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-echos-input.d.cts +0 -0
  137. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-logs.cjs +0 -0
  138. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-logs.d.cts +0 -0
  139. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-function.cjs +0 -0
  140. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-function.d.cts +0 -0
  141. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-implicitly.cjs +0 -0
  142. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-implicitly.d.cts +0 -0
  143. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-promise-rejected.cjs +0 -0
  144. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-promise-rejected.d.cts +0 -0
  145. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-promise-resolved.cjs +0 -0
  146. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-promise-resolved.d.cts +0 -0
  147. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-text.cjs +0 -0
  148. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-text.d.cts +0 -0
  149. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-undefined.cjs +0 -0
  150. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-undefined.d.cts +0 -0
  151. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.1 → serverless/fixtures/v2025.1}/app.functions/func-throws-error.cjs +0 -0
  152. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.1 → serverless/fixtures/v2025.1}/app.functions/func-throws-error.d.cts +0 -0
  153. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-times-out.cjs +0 -0
  154. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-times-out.d.cts +0 -0
  155. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-undeclared.cjs +0 -0
  156. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-undeclared.d.cts +0 -0
  157. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-uses-secret.cjs +0 -0
  158. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-uses-secret.d.cts +0 -0
  159. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.1/app.functions/func-returns-text.cjs → serverless/fixtures/v2026.03/functions/func-calls-callback.cjs} +0 -0
  160. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-rejected.d.cts → serverless/fixtures/v2026.03/functions/func-calls-callback.d.cts} +0 -0
  161. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2/app.functions → serverless/fixtures/v2026.03/functions}/func-throws-error.cjs +0 -0
  162. /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2/app.functions → serverless/fixtures/v2026.03/functions}/func-throws-error.d.cts +0 -0
  163. /package/dist/lib/__tests__/{app-functions/services/services_v20232.spec.d.ts → serverless/secrets.spec.d.ts} +0 -0
  164. /package/dist/lib/__tests__/{app-functions/setup.d.ts → serverless/services/PrivateAppUserTokenManager.spec.d.ts} +0 -0
  165. /package/dist/lib/__tests__/{app-functions → serverless}/setup.js +0 -0
  166. /package/dist/lib/{app-functions/errorReporter.d.ts → errorReporter.d.ts} +0 -0
  167. /package/dist/lib/{app-functions → proxy}/signing.d.ts +0 -0
  168. /package/dist/lib/{app-functions → proxy}/signing.js +0 -0
  169. /package/dist/lib/{app-functions → serverless}/api/privateAppUserToken.d.ts +0 -0
  170. /package/dist/lib/{app-functions → serverless}/api/privateAppUserToken.js +0 -0
  171. /package/dist/lib/{app-functions → serverless}/config.d.ts +0 -0
  172. /package/dist/lib/{app-functions → serverless}/context.d.ts +0 -0
  173. /package/dist/lib/{app-functions → serverless}/context.js +0 -0
  174. /package/dist/lib/{app-functions → serverless}/executor.d.ts +0 -0
  175. /package/dist/lib/{app-functions → serverless}/secrets.js +0 -0
  176. /package/dist/lib/{app-functions → serverless}/services/AppFunctionExecutionService.d.ts +0 -0
  177. /package/dist/lib/{app-functions → serverless}/services/PrivateAppUserTokenManager.d.ts +0 -0
  178. /package/dist/lib/{app-functions → serverless}/services/constants.d.ts +0 -0
  179. /package/dist/lib/{app-functions → serverless}/services/constants.js +0 -0
  180. /package/dist/lib/{app-functions → serverless}/services/messages.d.ts +0 -0
  181. /package/dist/lib/{app-functions → serverless}/services/messages.js +0 -0
  182. /package/dist/lib/{app-functions → serverless}/types.js +0 -0
  183. /package/dist/lib/{app-functions → serverless}/utils.d.ts +0 -0
  184. /package/dist/lib/{app-functions → serverless}/utils.js +0 -0
@@ -1,13 +1,13 @@
1
1
  import { describe, it, expect, vi, afterEach, beforeEach, } from 'vitest';
2
2
  import httpMocks from 'node-mocks-http';
3
3
  import { EventEmitter } from 'node:events';
4
- import { AppFunctionExecutionService } from "../../../app-functions/services/index.js";
5
- import { TEST_CONFIG_V20231 as TEST_CONFIG } from "../fixtures/constants.js";
6
- import * as executor from "../../../app-functions/executor.js";
4
+ import { AppFunctionExecutionService } from "../../../serverless/services/AppFunctionExecutionService.js";
5
+ import { TEST_CONFIG_V202603 as TEST_CONFIG } from "../fixtures/constants.js";
6
+ import * as executor from "../../../serverless/executor.js";
7
7
  import { scopesOnAccessToken as __scopesOnAccessToken } from '@hubspot/local-dev-lib/personalAccessKey';
8
- import { USER_TOKEN_READ, USER_TOKEN_WRITE, } from "../../../app-functions/services/constants.js";
9
- import { PrivateAppUserTokenManager } from "../../../app-functions/services/PrivateAppUserTokenManager.js";
10
- vi.mock('../../../app-functions/services/PrivateAppUserTokenManager.ts');
8
+ import { USER_TOKEN_READ, USER_TOKEN_WRITE, } from "../../../serverless/services/constants.js";
9
+ import { PrivateAppUserTokenManager } from "../../../serverless/services/PrivateAppUserTokenManager.js";
10
+ vi.mock('../../../serverless/services/PrivateAppUserTokenManager.ts');
11
11
  vi.mock('@hubspot/local-dev-lib/personalAccessKey');
12
12
  const scopesOnAccessToken = __scopesOnAccessToken;
13
13
  const callAppFunction = async (functionName, parameters) => {
@@ -27,8 +27,6 @@ const callAppFunction = async (functionName, parameters) => {
27
27
  eventEmitter: EventEmitter,
28
28
  });
29
29
  const handler = AppFunctionExecutionService(TEST_CONFIG);
30
- // Hold response until the handler finishes writing to it. THis must
31
- // be set up before calling the handler or it may miss the `end` event.
32
30
  const responsePromised = new Promise((resolve) => {
33
31
  response.on('end', () => {
34
32
  resolve(response);
@@ -37,16 +35,12 @@ const callAppFunction = async (functionName, parameters) => {
37
35
  await handler(request, response);
38
36
  return await responsePromised;
39
37
  };
40
- /**
41
- * Validate the service for executing app functions
42
- */
43
- describe('app function dev server', () => {
38
+ describe('app function dev server (v2026.03)', () => {
44
39
  const initialEnvJson = JSON.stringify(process.env);
45
40
  beforeEach(() => {
46
41
  scopesOnAccessToken.mockResolvedValue([USER_TOKEN_READ, USER_TOKEN_WRITE]);
47
42
  });
48
43
  afterEach(() => {
49
- // restore process.env
50
44
  process.env = JSON.parse(initialEnvJson);
51
45
  vi.resetAllMocks();
52
46
  vi.restoreAllMocks();
@@ -54,184 +48,132 @@ describe('app function dev server', () => {
54
48
  it('returns "200 OK" response if function execution succeeded', async () => {
55
49
  const getTokenspy = vi.spyOn(PrivateAppUserTokenManager.prototype, 'getPrivateAppUserToken');
56
50
  const response = await callAppFunction('returns-text');
57
- // Validate response status and body
58
51
  expect(response.statusCode).toEqual(200);
59
52
  expect(response._getJSONData()).toEqual({
60
53
  logId: 'n/a',
61
54
  response: 'result',
62
55
  });
63
- // Validate log output
64
56
  expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "returns-text" execution succeeded'));
65
57
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
66
58
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
67
- // Validate process.env is put back to what it was before the call
68
59
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
69
- // get the token
70
60
  expect(getTokenspy).toHaveBeenCalledTimes(1);
71
61
  });
72
62
  it('returns "400 Bad Request" response if function execution failed', async () => {
73
63
  const response = await callAppFunction('throws-error');
74
- // Validate response status and body
75
64
  expect(response.statusCode).toEqual(400);
76
65
  const body = response._getJSONData();
77
66
  expect(body).toHaveProperty('status', 'error');
78
67
  expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
79
68
  const exception = body.errors?.[0]?.context?.exception?.[0];
80
69
  expect(exception).toEqual('Error: Oops');
81
- // Validate log output
82
70
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "throws-error" execution failed'));
83
71
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith('App function encountered an uncaught error.', expect.anything());
84
- // Validate process.env is put back to what it was before the call
85
72
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
86
73
  });
87
74
  describe('handles asynchronous functions (promise chaining)', () => {
88
75
  it('returns "200 OK" response if function returns a promise that resolves', async () => {
89
76
  const response = await callAppFunction('returns-promise-resolved');
90
- // Validate response status and body
91
77
  expect(response.statusCode).toEqual(200);
92
78
  expect(response._getJSONData()).toEqual({
93
79
  logId: 'n/a',
94
80
  response: { result: 'simulated' },
95
81
  });
96
- // Validate log output
97
82
  expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "returns-promise-resolved" execution succeeded'));
98
83
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
99
84
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
100
- // Validate process.env is put back to what it was before the call
101
85
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
102
86
  });
103
87
  it('returns "400 Bad Request" response if function returns a promise that rejects', async () => {
104
88
  const response = await callAppFunction('returns-promise-rejected');
105
- // Validate response status and body
106
89
  expect(response.statusCode).toEqual(400);
107
90
  const body = response._getJSONData();
108
91
  expect(body).toHaveProperty('status', 'error');
109
92
  expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
110
93
  const exception = body.errors?.[0]?.context?.exception?.[0];
111
94
  expect(exception).toEqual('Error: fail');
112
- // Validate log output
113
95
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "returns-promise-rejected" execution failed'));
114
96
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith('App function encountered an uncaught error.', expect.anything());
115
- // Validate process.env is put back to what it was before the call
116
97
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
117
98
  });
118
99
  });
119
100
  describe('handles asynchronous functions (async/await)', () => {
120
101
  it('returns "200 OK" response if an async function execution succeeded', async () => {
121
102
  const response = await callAppFunction('async-succeeds');
122
- // Validate response status and body
123
103
  expect(response.statusCode).toEqual(200);
124
104
  expect(response._getJSONData()).toEqual({
125
105
  logId: 'n/a',
126
106
  response: { result: 'simulated' },
127
107
  });
128
- // Validate log output
129
108
  expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "async-succeeds" execution succeeded'));
130
109
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
131
110
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
132
- // Validate process.env is put back to what it was before the call
133
111
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
134
112
  });
135
113
  it('returns "400 Bad Request" response if an async function execution failed', async () => {
136
114
  const response = await callAppFunction('async-fails');
137
- // Validate response status and body
138
115
  expect(response.statusCode).toEqual(400);
139
116
  const body = response._getJSONData();
140
117
  expect(body).toHaveProperty('status', 'error');
141
118
  expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
142
119
  const exception = body.errors?.[0]?.context?.exception?.[0];
143
120
  expect(exception).toEqual('Error: fail');
144
- // Validate log output
145
121
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "async-fails" execution failed'));
146
122
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith('App function encountered an uncaught error.', expect.anything());
147
- // Validate process.env is put back to what it was before the call
148
123
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
149
124
  });
150
125
  });
151
- describe('handles functions with floating promises (only 2023.1)', () => {
152
- it('returns "200 OK" response if function makes a promise that resolves and calls callback', async () => {
153
- const response = await callAppFunction('callback-on-promise-resolved');
154
- // Validate response status and body
155
- expect(response.statusCode).toEqual(200);
156
- expect(response._getJSONData()).toEqual({
157
- logId: 'n/a',
158
- response: { result: 'simulated' },
159
- });
160
- // Validate log output
161
- expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "callback-on-promise-resolved" execution succeeded'));
162
- expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
163
- expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
164
- // Validate process.env is put back to what it was before the call
165
- expect(process.env).toEqual(JSON.parse(initialEnvJson));
166
- });
167
- it('returns "200 OK" response if function makes a promise that rejects and calls callback', async () => {
168
- const response = await callAppFunction('callback-on-promise-rejected');
169
- // Validate response status and body
170
- expect(response.statusCode).toEqual(200);
171
- expect(response._getJSONData()).toEqual({
172
- logId: 'n/a',
173
- response: { error: 'Error: fail' },
174
- });
175
- // Validate log output
176
- expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "callback-on-promise-rejected" execution succeeded'));
177
- expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
178
- expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
179
- // Validate process.env is put back to what it was before the call
180
- expect(process.env).toEqual(JSON.parse(initialEnvJson));
126
+ it('returns "200 OK" response if function returns implicitly', async () => {
127
+ const response = await callAppFunction('returns-implicitly');
128
+ expect(response.statusCode).toEqual(200);
129
+ expect(response._getJSONData()).toEqual({
130
+ logId: 'n/a',
131
+ response: null,
181
132
  });
133
+ expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "returns-implicitly" execution succeeded'));
134
+ expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
135
+ expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
136
+ expect(process.env).toEqual(JSON.parse(initialEnvJson));
182
137
  });
183
138
  it('returns "400 Bad Request" response if function not found', async () => {
184
139
  const response = await callAppFunction('does-not-exist');
185
- // Validate response status and body
186
140
  expect(response.statusCode).toEqual(400);
187
141
  const body = response._getJSONData();
188
142
  expect(body).toHaveProperty('status', 'error');
189
143
  expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
190
144
  const exception = body.errors?.[0]?.context?.exception?.[0];
191
145
  expect(exception).toMatch(/doesn't exist in this project/);
192
- // Validate log output
193
146
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "does-not-exist" execution failed'));
194
147
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith(expect.stringContaining('Could not find file'));
195
- // Validate process.env is put back to what it was before the call
196
148
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
197
149
  });
198
150
  it('returns "400 Bad Request" response if function does not export main', async () => {
199
- // TEST_CONFIG.functionTimeoutMs = 250
200
151
  const response = await callAppFunction('does-not-export-main');
201
- // Validate response status and body
202
152
  expect(response.statusCode).toEqual(400);
203
153
  const body = response._getJSONData();
204
154
  expect(body).toHaveProperty('status', 'error');
205
155
  expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
206
156
  const exception = body.errors?.[0]?.context?.exception?.[0];
207
157
  expect(exception).toMatch(/customerPayload\.main is not a function/);
208
- // Validate log output
209
158
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "does-not-export-main" execution failed'));
210
159
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith(expect.stringContaining('Could not find "main" export in'));
211
- // Validate process.env is put back to what it was before the call
212
160
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
213
161
  });
214
162
  it('returns "400 Bad Request" response if function returns invalid json', async () => {
215
- // TEST_CONFIG.functionTimeoutMs = 250
216
163
  const response = await callAppFunction('returns-function');
217
- // Validate response status and body
218
164
  expect(response.statusCode).toEqual(400);
219
165
  const body = response._getJSONData();
220
166
  expect(body).toHaveProperty('status', 'error');
221
167
  expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
222
168
  const exception = body.errors?.[0]?.context?.exception?.[0];
223
169
  expect(exception).toMatch(/Wrong arguments/);
224
- // Validate log output
225
170
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "returns-function" execution failed'));
226
171
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith('App function reponse is not valid JSON.');
227
- // Validate process.env is put back to what it was before the call
228
172
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
229
173
  });
230
174
  it('returns "200 OK" response if function returns before timeout', async () => {
231
- // TEST_CONFIG.functionTimeoutMs = 250
232
175
  const response = await callAppFunction('times-out', { delayMs: 200 });
233
- const variance = 5; // milliseconds
234
- // Validate response status and body
176
+ const variance = 10;
235
177
  expect(response.statusCode).toEqual(200);
236
178
  const body = response._getJSONData();
237
179
  expect(body).toEqual({
@@ -240,27 +182,21 @@ describe('app function dev server', () => {
240
182
  });
241
183
  expect(body.response.elapsedMs).toBeGreaterThanOrEqual(200 - variance);
242
184
  expect(body.response.elapsedMs).toBeLessThanOrEqual(200 + variance);
243
- // Validate log output
244
185
  expect(TEST_CONFIG.logger.debug).toHaveBeenCalledWith(expect.stringContaining('App function "times-out" execution succeeded'));
245
186
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
246
187
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
247
- // Validate process.env is put back to what it was before the call
248
188
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
249
189
  });
250
190
  it('returns "400 Bad Request" response if function returns after timeout', async () => {
251
- // TEST_CONFIG.functionTimeoutMs = 250
252
191
  const response = await callAppFunction('times-out', { delayMs: 300 });
253
- // Validate response status and body
254
192
  expect(response.statusCode).toEqual(400);
255
193
  const body = response._getJSONData();
256
194
  expect(body).toHaveProperty('status', 'error');
257
195
  expect(body).toHaveProperty('category', 'VALIDATION_ERROR');
258
196
  const exception = body.errors?.[0]?.context?.exception?.[0];
259
197
  expect(exception).toMatch(/Task timed out after/);
260
- // Validate log output
261
198
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "times-out" execution failed'));
262
199
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith('App function failed to callback within 0.25 second.');
263
- // Validate process.env is put back to what it was before the call
264
200
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
265
201
  });
266
202
  it('returns "500 Internal Server Error" response if something went wrong', async () => {
@@ -268,18 +204,14 @@ describe('app function dev server', () => {
268
204
  vi.spyOn(executor, 'executeFunction').mockImplementation(() => {
269
205
  throw error;
270
206
  });
271
- // Act
272
207
  const response = await callAppFunction('returns-text');
273
- // Validate response status and body
274
208
  expect(response.statusCode).toEqual(500);
275
209
  const body = response._getJSONData();
276
210
  expect(body).toHaveProperty('status', 'error');
277
211
  expect(body).not.toHaveProperty('category', 'INTERNAL_ERROR');
278
212
  expect(body).toHaveProperty('message', 'internal error');
279
- // Validate log output
280
213
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledWith(expect.stringContaining('App function "returns-text" execution failed due to server internal error'));
281
214
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledWith(error);
282
- // Validate process.env is put back to what it was before the call
283
215
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
284
216
  });
285
217
  it('logs to console from inside the function', async () => {
@@ -288,15 +220,12 @@ describe('app function dev server', () => {
288
220
  const info = vi.spyOn(console, 'info').mockImplementation(() => { });
289
221
  const warn = vi.spyOn(console, 'warn').mockImplementation(() => { });
290
222
  const error = vi.spyOn(console, 'error').mockImplementation(() => { });
291
- // Act
292
223
  const response = await callAppFunction('logs');
293
- // Validate response status and body
294
224
  expect(response.statusCode).toEqual(200);
295
225
  expect(response._getJSONData()).toEqual({
296
226
  logId: 'n/a',
297
227
  response: { status: 'success' },
298
228
  });
299
- // Validate function logging
300
229
  expect(log).toHaveBeenCalledTimes(1);
301
230
  expect(log).toHaveBeenCalledWith('log line');
302
231
  expect(debug).toHaveBeenCalledTimes(1);
@@ -308,12 +237,10 @@ describe('app function dev server', () => {
308
237
  expect(warn).toHaveBeenCalledWith('warn line');
309
238
  expect(error).toHaveBeenCalledTimes(1);
310
239
  expect(error).toHaveBeenCalledWith('error line');
311
- // Validate service logging, which is separate from function logging
312
240
  expect(TEST_CONFIG.logger.debug).toHaveBeenCalledTimes(1);
313
241
  expect(TEST_CONFIG.logger.debug).toHaveBeenNthCalledWith(1, expect.stringContaining('App function "logs" execution succeeded'));
314
242
  expect(TEST_CONFIG.logger.warn).toHaveBeenCalledTimes(0);
315
243
  expect(TEST_CONFIG.logger.error).toHaveBeenCalledTimes(0);
316
- // Validate process.env is put back to what it was before the call
317
244
  expect(process.env).toEqual(JSON.parse(initialEnvJson));
318
245
  });
319
246
  });
@@ -0,0 +1 @@
1
+ export {};
@@ -1,8 +1,9 @@
1
1
  import { describe, it, expect, vi, beforeAll, afterAll, beforeEach, afterEach, } from 'vitest';
2
2
  import { OUTPUT_DIR } from "../constants.js";
3
- import { getUrlSafeFileName, stripAnsiColorCodes, loadManifest, buildSourceId, isNodeModule, isExtensionFile, generateHash, } from "../utils.js";
3
+ import { getUrlSafeFileName, stripAnsiColorCodes, loadManifest, buildSourceId, isNodeModule, isExtensionFile, generateHash, isUnifiedAppConfig, } from "../utils.js";
4
4
  import fs from 'fs';
5
5
  import path from 'path';
6
+ import { UnifiedAppAuthTypes, } from "../types.js";
6
7
  describe('utils', () => {
7
8
  describe('getUrlSafeFileName', () => {
8
9
  it('should uri encode the filename', () => {
@@ -128,6 +129,30 @@ describe('utils', () => {
128
129
  process.chdir(originalCwd);
129
130
  });
130
131
  });
132
+ describe('isUnifiedAppConfig', () => {
133
+ it('should return true for a unified app config', () => {
134
+ const config = {
135
+ unified: true,
136
+ name: 'Test',
137
+ uid: 'test-uid',
138
+ auth: { type: UnifiedAppAuthTypes.OAUTH },
139
+ distribution: 'private',
140
+ isPublicApp: true,
141
+ extensions: { crm: { cards: [] } },
142
+ };
143
+ expect(isUnifiedAppConfig(config)).toBe(true);
144
+ });
145
+ it('should return false for a legacy app config', () => {
146
+ const config = {
147
+ name: 'Test',
148
+ uid: 'test-uid',
149
+ scopes: [],
150
+ public: false,
151
+ extensions: { crm: { cards: [] } },
152
+ };
153
+ expect(isUnifiedAppConfig(config)).toBe(false);
154
+ });
155
+ });
131
156
  describe('generateHash', () => {
132
157
  it('should generate consistent hashes for same inputs', () => {
133
158
  const hash1 = generateHash('test', ['a', 'b'], { foo: 'bar' });
@@ -17,10 +17,10 @@ export declare const WEBSOCKET_MESSAGE_VERSION = 1;
17
17
  export declare const PROXY_CAPABILITY = "app-backend-proxy-server";
18
18
  export declare const SERVER_CAPABILITIES: string[];
19
19
  export declare const PLATFORM_VERSION: {
20
- readonly V20231: "2023.1";
21
- readonly V20232: "2023.2";
22
20
  readonly V20251: "2025.1";
23
21
  readonly V20252: "2025.2";
22
+ readonly V202603: "2026.03";
23
+ readonly V202603BETA: "2026.03-beta";
24
24
  readonly UNSTABLE: "unstable";
25
25
  };
26
26
  export declare const PUBLIC_APP = "public-app";
@@ -24,10 +24,10 @@ export const SERVER_CAPABILITIES = [
24
24
  'account-id',
25
25
  ];
26
26
  export const PLATFORM_VERSION = {
27
- V20231: '2023.1',
28
- V20232: '2023.2',
29
27
  V20251: '2025.1',
30
28
  V20252: '2025.2',
29
+ V202603: '2026.03',
30
+ V202603BETA: '2026.03-beta',
31
31
  UNSTABLE: 'unstable',
32
32
  };
33
33
  export const PUBLIC_APP = 'public-app';
@@ -1,12 +1,12 @@
1
1
  import * as Sentry from '@sentry/node';
2
2
  import { readFileSync } from 'node:fs';
3
3
  import { join } from 'node:path';
4
- const version = JSON.parse(readFileSync(join(import.meta.dirname, '../../../package.json'), 'utf-8')).version;
4
+ const version = JSON.parse(readFileSync(join(import.meta.dirname, '../../package.json'), 'utf-8')).version;
5
5
  const DSN_KEY = 'fbfd0619a2c1af3be058700da25fe9f1';
6
6
  const LOGFETCH_DSN = `https://${DSN_KEY}@exceptions.hubspot.com/v2/1`;
7
7
  Sentry.init({
8
8
  dsn: LOGFETCH_DSN,
9
- release: `@hubspot/app-functions-dev-server@${version}`,
9
+ release: `@hubspot/ui-extensions-dev-server@${version}`,
10
10
  initialScope: {
11
11
  tags: {
12
12
  devServerVersion: version,
@@ -30,6 +30,12 @@ class ExtensionsService {
30
30
  ...(devServerState.functionsConfig.platformVersion && {
31
31
  platformVersion: devServerState.functionsConfig.platformVersion,
32
32
  }),
33
+ ...(devServerState.authType && {
34
+ authType: devServerState.authType,
35
+ }),
36
+ ...(devServerState.distribution && {
37
+ distribution: devServerState.distribution,
38
+ }),
33
39
  };
34
40
  res.status(200).json(response);
35
41
  }
@@ -1,5 +1,5 @@
1
1
  import { AxiosError } from 'axios';
2
- import { LocalDevUrlMapping, ProxyServiceConfig, ProxyServerError } from '../types.ts';
2
+ import { LocalDevUrlMapping, ProxyServiceConfig, ProxyServerError } from './types.ts';
3
3
  export declare const mapToLocalUrl: (localDevUrlMapping: LocalDevUrlMapping, requestUri: string, allowedUrls: string[], logger: ProxyServiceConfig["logger"]) => string;
4
4
  export declare const extractErrorData: (e: unknown | AxiosError) => ProxyServerError;
5
5
  export declare const AppProxyService: ({ localDevUrlMapping, logger, accountId, allowedUrls, }: ProxyServiceConfig) => import("express-serve-static-core").Express;
@@ -2,8 +2,8 @@ import express from 'express';
2
2
  import cors from 'cors';
3
3
  import axios, { isAxiosError } from 'axios';
4
4
  import crypto from 'node:crypto';
5
- import { axiosErrorMappings, defaultServerError, localProxyErrorMappings, } from "../constants.js";
6
- import { getSignatureHeaders } from "../signing.js";
5
+ import { axiosErrorMappings, defaultServerError, localProxyErrorMappings, } from "./constants.js";
6
+ import { getSignatureHeaders } from "./signing.js";
7
7
  import { reportError } from "../errorReporter.js";
8
8
  export const mapToLocalUrl = (localDevUrlMapping, requestUri, allowedUrls, logger) => {
9
9
  const url = new URL(requestUri);
@@ -21,7 +21,7 @@ export const mapToLocalUrl = (localDevUrlMapping, requestUri, allowedUrls, logge
21
21
  if (parsedLocalMapping.pathname !== '/') {
22
22
  // If the local pathname ends with '/', remove it to avoid '//' in the path
23
23
  const localPathname = parsedLocalMapping.pathname.endsWith('/')
24
- ? parsedLocalMapping.pathname.slice(0, 1)
24
+ ? parsedLocalMapping.pathname.slice(0, -1)
25
25
  : parsedLocalMapping.pathname;
26
26
  url.pathname = `${localPathname}${url.pathname}`;
27
27
  }
@@ -55,6 +55,7 @@ export const AppProxyService = ({ localDevUrlMapping, logger, accountId, allowed
55
55
  const { requestUri, method, requestTimeoutMillis, requestBody, requestHeaders, } =
56
56
  /* eslint-disable-next-line no-unsafe-optional-chaining */
57
57
  req?.body;
58
+ let requestFailed = false;
58
59
  try {
59
60
  logger.info(`Request to ${requestUri} started, method=${method}, correlationId=${correlationId}`);
60
61
  const url = mapToLocalUrl(localDevUrlMapping, requestUri, allowedUrls, logger);
@@ -139,7 +140,17 @@ export const AppProxyService = ({ localDevUrlMapping, logger, accountId, allowed
139
140
  });
140
141
  }
141
142
  catch (e) {
143
+ requestFailed = true;
142
144
  const { status, message, category, data: responseBody, } = extractErrorData(e);
145
+ // If it's an Axios error with a response, it's a server error from the app server and we simply warn about it.
146
+ if (isAxiosError(e) && e.response) {
147
+ logger.warn(`Request to ${requestUri} returned HTTP ${status}, correlationId=${correlationId}`);
148
+ }
149
+ else {
150
+ // Other failures are likely related to the local proxy itself, such as network errors, CORS issues, or misconfiguration. We log these as errors for visibility.
151
+ const errorMessage = e instanceof Error ? e.message : 'Unknown error';
152
+ logger.error(`Request to ${requestUri} failed: ${errorMessage}, correlationId=${correlationId}`);
153
+ }
143
154
  // Report non-Axios errors (dev server bugs)
144
155
  if (!isAxiosError(e)) {
145
156
  reportError(e, {
@@ -189,7 +200,7 @@ export const AppProxyService = ({ localDevUrlMapping, logger, accountId, allowed
189
200
  });
190
201
  }
191
202
  finally {
192
- logger.info(`Request completed, correlationId=${correlationId}`);
203
+ logger.info(`Request ${requestFailed ? 'failed' : 'completed'}, correlationId=${correlationId}`);
193
204
  }
194
205
  });
195
206
  return app;
@@ -0,0 +1,13 @@
1
+ import { ProxyServerError } from './types.ts';
2
+ export declare const defaultServerError: ProxyServerError;
3
+ export declare const validationError: Omit<ProxyServerError, 'message' | 'data'>;
4
+ export declare const axiosErrorMappings: {
5
+ [key: string]: ProxyServerError;
6
+ };
7
+ export declare const localProxyErrorMappings: {
8
+ BAD_GATEWAY: {
9
+ status: number;
10
+ category: string;
11
+ message: string;
12
+ };
13
+ };
@@ -1,22 +1,4 @@
1
1
  import { AxiosError, HttpStatusCode } from 'axios';
2
- export const APP_FUNCTIONS_DIRNAME = 'app.functions';
3
- export const APP_FUNCTIONS_MANIFEST_FILENAME = 'serverless.json';
4
- export const DEV_SERVER_DEFAULT_PORT = 6789;
5
- export const EXECUTION_TIMEOUT_MS = 15_000;
6
- export const PRIVATE_APP_ACCESS_TOKEN = 'PRIVATE_APP_ACCESS_TOKEN';
7
- export const SECRETS_IN_CONTEXT = [
8
- PRIVATE_APP_ACCESS_TOKEN,
9
- 'HS_ENVIRONMENT',
10
- 'HS_HUBLET',
11
- 'HS_SERVERLESS_FUNCTION_ID',
12
- ];
13
- export const PLATFORM_VERSION = {
14
- V20231: '2023.1',
15
- V20232: '2023.2',
16
- V20251: '2025.1',
17
- V20252: '2025.2',
18
- UNSTABLE: 'unstable',
19
- };
20
2
  export const defaultServerError = {
21
3
  status: HttpStatusCode.InternalServerError,
22
4
  message: 'Failure observed in UI Extensions Proxy Server. Contact Hubspot Support if the problem persists',
@@ -0,0 +1,44 @@
1
+ export interface LocalDevUrlMapping {
2
+ [hostUrl: string]: string;
3
+ }
4
+ export interface ProxyServiceConfig {
5
+ logger: Pick<typeof console, 'debug' | 'info' | 'warn' | 'error'>;
6
+ localDevUrlMapping: LocalDevUrlMapping;
7
+ accountId: number;
8
+ allowedUrls: string[];
9
+ }
10
+ interface Context {
11
+ correlationId: string[];
12
+ httpMethod: string[];
13
+ appServerStatusCode?: string[];
14
+ portalId: string[];
15
+ requestUri: string[];
16
+ }
17
+ export interface AppProxyServiceSuccessResponse {
18
+ status: 'success';
19
+ responseBody: unknown;
20
+ context: Context;
21
+ }
22
+ export interface AppProxyServiceErrorResponse {
23
+ status: 'error';
24
+ category: string;
25
+ message: string;
26
+ context: Context;
27
+ errors?: unknown[];
28
+ responseBody?: unknown;
29
+ }
30
+ export type AppProxyServiceResponse = AppProxyServiceSuccessResponse | AppProxyServiceErrorResponse;
31
+ export interface ProxyServerError {
32
+ status: number;
33
+ data?: unknown;
34
+ message: string;
35
+ category: string;
36
+ }
37
+ export interface ProxyRequest {
38
+ requestUri: string;
39
+ method: string;
40
+ requestTimeoutMillis: number;
41
+ requestBody: unknown;
42
+ requestHeaders?: Record<string, string>;
43
+ }
44
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -2,7 +2,8 @@ import express from 'express';
2
2
  import cors from 'cors';
3
3
  import { PROXY_CAPABILITY, SERVER_CAPABILITIES, WEBSOCKET_MESSAGE_VERSION, } from "./constants.js";
4
4
  import extensionsService from "./extensionsService.js";
5
- import { AppFunctionExecutionService, AppProxyService, } from "./app-functions/services/index.js";
5
+ import { AppFunctionExecutionService } from "./serverless/services/AppFunctionExecutionService.js";
6
+ import { AppProxyService } from "./proxy/AppProxyService.js";
6
7
  import { extractAllowedUrls } from "./utils.js";
7
8
  import { ExtensionsWebSocket } from "./ExtensionsWebSocket.js";
8
9
  function listen(app, port) {