@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.
- package/dist/lib/DevModeParentInterface.js +5 -6
- package/dist/lib/DevModeUnifiedInterface.js +2 -2
- package/dist/lib/DevServerState.d.ts +14 -4
- package/dist/lib/DevServerState.js +34 -1
- package/dist/lib/__tests__/DevModeParentInterface.spec.js +42 -1
- package/dist/lib/__tests__/DevModeUnifiedInterface.spec.js +34 -0
- package/dist/lib/__tests__/DevServerState.spec.js +180 -0
- package/dist/lib/__tests__/{app-functions/errorReporter.spec.js → errorReporter.spec.js} +5 -5
- package/dist/lib/__tests__/extensionsService.spec.js +57 -9
- package/dist/lib/__tests__/factories.d.ts +1 -1
- package/dist/lib/__tests__/factories.js +1 -1
- package/dist/lib/__tests__/fixtures/extensionConfig.js +3 -0
- package/dist/lib/__tests__/plugins/devBuildPlugin.spec.js +1 -1
- package/dist/lib/__tests__/{app-functions/services → proxy}/AppProxyService.spec.js +73 -3
- package/dist/lib/__tests__/{app-functions → proxy}/signing.spec.js +1 -1
- package/dist/lib/__tests__/server.spec.js +14 -11
- package/dist/lib/__tests__/serverless/config.spec.js +61 -0
- package/dist/lib/__tests__/{app-functions → serverless}/context.spec.js +2 -2
- package/dist/lib/__tests__/serverless/executor_unsupported.spec.js +37 -0
- package/dist/lib/__tests__/{app-functions/executor_v20232.spec.js → serverless/executor_v20251.spec.js} +6 -13
- package/dist/lib/__tests__/{app-functions/executor_v20231.spec.js → serverless/executor_v202603.spec.js} +26 -11
- package/dist/lib/__tests__/{app-functions → serverless}/fixtures/constants.d.ts +4 -3
- package/dist/lib/__tests__/{app-functions → serverless}/fixtures/constants.js +9 -3
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-async-fails.cjs +5 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-async-fails.d.cts +1 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-async-succeeds.cjs +5 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-async-succeeds.d.cts +3 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-does-not-export-main.cjs +4 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-does-not-export-main.d.cts +1 -0
- package/dist/lib/__tests__/{app-functions/fixtures/v2023.1/app.functions → serverless/fixtures/v2026.03/functions}/func-echos-input.cjs +3 -3
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-echos-input.d.cts +5 -0
- package/dist/lib/__tests__/{app-functions/fixtures/v2023.1/app.functions → serverless/fixtures/v2026.03/functions}/func-logs.cjs +2 -2
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-logs.d.cts +3 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-function.cjs +4 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-function.d.cts +1 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-implicitly.cjs +7 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-implicitly.d.cts +1 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-promise-rejected.cjs +4 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-promise-rejected.d.cts +1 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-promise-resolved.cjs +4 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-promise-resolved.d.cts +3 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-text.cjs +4 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-text.d.cts +1 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-undefined.cjs +4 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-returns-undefined.d.cts +1 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-times-out.cjs +12 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-times-out.d.cts +1 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-undeclared.cjs +4 -0
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-undeclared.d.cts +1 -0
- package/dist/lib/__tests__/{app-functions/fixtures/v2023.1/app.functions → serverless/fixtures/v2026.03/functions}/func-uses-secret.cjs +3 -3
- package/dist/lib/__tests__/serverless/fixtures/v2026.03/functions/func-uses-secret.d.cts +4 -0
- package/dist/lib/__tests__/{app-functions → serverless}/secrets.spec.js +4 -4
- package/dist/lib/__tests__/{app-functions → serverless}/services/PrivateAppUserTokenManager.spec.js +4 -4
- package/dist/lib/__tests__/serverless/services/services_v20251.spec.d.ts +1 -0
- package/dist/lib/__tests__/{app-functions/services/services_v20232.spec.js → serverless/services/services_v20251.spec.js} +9 -63
- package/dist/lib/__tests__/serverless/services/services_v202603.spec.d.ts +1 -0
- package/dist/lib/__tests__/{app-functions/services/services_v20231.spec.js → serverless/services/services_v202603.spec.js} +18 -91
- package/dist/lib/__tests__/serverless/setup.d.ts +1 -0
- package/dist/lib/__tests__/utils.spec.js +26 -1
- package/dist/lib/constants.d.ts +2 -2
- package/dist/lib/constants.js +2 -2
- package/dist/lib/{app-functions/errorReporter.js → errorReporter.js} +2 -2
- package/dist/lib/extensionsService.js +6 -0
- package/dist/lib/{app-functions/services → proxy}/AppProxyService.d.ts +1 -1
- package/dist/lib/{app-functions/services → proxy}/AppProxyService.js +15 -4
- package/dist/lib/proxy/constants.d.ts +13 -0
- package/dist/lib/{app-functions → proxy}/constants.js +0 -18
- package/dist/lib/proxy/types.d.ts +44 -0
- package/dist/lib/proxy/types.js +1 -0
- package/dist/lib/server.js +2 -1
- package/dist/lib/serverless/config.js +100 -0
- package/dist/lib/serverless/constants.d.ts +7 -0
- package/dist/lib/serverless/constants.js +12 -0
- package/dist/lib/{app-functions → serverless}/errors.d.ts +1 -0
- package/dist/lib/{app-functions → serverless}/errors.js +6 -0
- package/dist/lib/{app-functions → serverless}/executor.js +19 -13
- package/dist/lib/serverless/secrets.d.ts +5 -0
- package/dist/lib/{app-functions → serverless}/services/AppFunctionExecutionService.js +1 -1
- package/dist/lib/{app-functions → serverless}/services/PrivateAppUserTokenManager.js +1 -1
- package/dist/lib/{app-functions → serverless}/types.d.ts +1 -44
- package/dist/lib/types.d.ts +12 -3
- package/dist/lib/types.js +7 -2
- package/dist/lib/utils.d.ts +2 -1
- package/dist/lib/utils.js +3 -0
- package/package.json +8 -5
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-fails.cjs +0 -8
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-fails.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-succeeds.cjs +0 -8
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-succeeds.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-rejected.cjs +0 -8
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-resolved.cjs +0 -8
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-resolved.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-does-not-export-main.cjs +0 -4
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-does-not-export-main.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-echos-input.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-logs.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-function.cjs +0 -4
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-function.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-rejected.cjs +0 -7
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-rejected.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-resolved.cjs +0 -7
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-resolved.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-text.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-undefined.cjs +0 -4
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-undefined.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-times-out.cjs +0 -10
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-times-out.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-undeclared.cjs +0 -4
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-undeclared.d.cts +0 -1
- package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-uses-secret.d.cts +0 -1
- package/dist/lib/app-functions/config.js +0 -48
- package/dist/lib/app-functions/constants.d.ts +0 -26
- package/dist/lib/app-functions/index.d.ts +0 -4
- package/dist/lib/app-functions/index.js +0 -4
- package/dist/lib/app-functions/secrets.d.ts +0 -5
- package/dist/lib/app-functions/services/index.d.ts +0 -3
- package/dist/lib/app-functions/services/index.js +0 -3
- /package/dist/lib/__tests__/{app-functions/context.spec.d.ts → DevServerState.spec.d.ts} +0 -0
- /package/dist/lib/__tests__/{app-functions/errorReporter.spec.d.ts → errorReporter.spec.d.ts} +0 -0
- /package/dist/lib/__tests__/{app-functions/services → proxy}/AppProxyService.spec.d.ts +0 -0
- /package/dist/lib/__tests__/{app-functions → proxy}/signing.spec.d.ts +0 -0
- /package/dist/lib/__tests__/{app-functions/executor_v20231.spec.d.ts → serverless/config.spec.d.ts} +0 -0
- /package/dist/lib/__tests__/{app-functions/executor_v20232.spec.d.ts → serverless/context.spec.d.ts} +0 -0
- /package/dist/lib/__tests__/{app-functions/secrets.spec.d.ts → serverless/executor_unsupported.spec.d.ts} +0 -0
- /package/dist/lib/__tests__/{app-functions/services/PrivateAppUserTokenManager.spec.d.ts → serverless/executor_v20251.spec.d.ts} +0 -0
- /package/dist/lib/__tests__/{app-functions/services/services_v20231.spec.d.ts → serverless/executor_v202603.spec.d.ts} +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-async-fails.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-async-fails.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-async-succeeds.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-async-succeeds.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-calls-callback.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-calls-callback.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-does-not-export-main.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-does-not-export-main.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-echos-input.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-echos-input.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-logs.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-logs.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-function.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-function.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-implicitly.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-implicitly.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-promise-rejected.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-promise-rejected.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-promise-resolved.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-promise-resolved.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-text.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-text.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-undefined.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-returns-undefined.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.1 → serverless/fixtures/v2025.1}/app.functions/func-throws-error.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.1 → serverless/fixtures/v2025.1}/app.functions/func-throws-error.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-times-out.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-times-out.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-undeclared.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-undeclared.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-uses-secret.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2 → serverless/fixtures/v2025.1}/app.functions/func-uses-secret.d.cts +0 -0
- /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
- /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
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2/app.functions → serverless/fixtures/v2026.03/functions}/func-throws-error.cjs +0 -0
- /package/dist/lib/__tests__/{app-functions/fixtures/v2023.2/app.functions → serverless/fixtures/v2026.03/functions}/func-throws-error.d.cts +0 -0
- /package/dist/lib/__tests__/{app-functions/services/services_v20232.spec.d.ts → serverless/secrets.spec.d.ts} +0 -0
- /package/dist/lib/__tests__/{app-functions/setup.d.ts → serverless/services/PrivateAppUserTokenManager.spec.d.ts} +0 -0
- /package/dist/lib/__tests__/{app-functions → serverless}/setup.js +0 -0
- /package/dist/lib/{app-functions/errorReporter.d.ts → errorReporter.d.ts} +0 -0
- /package/dist/lib/{app-functions → proxy}/signing.d.ts +0 -0
- /package/dist/lib/{app-functions → proxy}/signing.js +0 -0
- /package/dist/lib/{app-functions → serverless}/api/privateAppUserToken.d.ts +0 -0
- /package/dist/lib/{app-functions → serverless}/api/privateAppUserToken.js +0 -0
- /package/dist/lib/{app-functions → serverless}/config.d.ts +0 -0
- /package/dist/lib/{app-functions → serverless}/context.d.ts +0 -0
- /package/dist/lib/{app-functions → serverless}/context.js +0 -0
- /package/dist/lib/{app-functions → serverless}/executor.d.ts +0 -0
- /package/dist/lib/{app-functions → serverless}/secrets.js +0 -0
- /package/dist/lib/{app-functions → serverless}/services/AppFunctionExecutionService.d.ts +0 -0
- /package/dist/lib/{app-functions → serverless}/services/PrivateAppUserTokenManager.d.ts +0 -0
- /package/dist/lib/{app-functions → serverless}/services/constants.d.ts +0 -0
- /package/dist/lib/{app-functions → serverless}/services/constants.js +0 -0
- /package/dist/lib/{app-functions → serverless}/services/messages.d.ts +0 -0
- /package/dist/lib/{app-functions → serverless}/services/messages.js +0 -0
- /package/dist/lib/{app-functions → serverless}/types.js +0 -0
- /package/dist/lib/{app-functions → serverless}/utils.d.ts +0 -0
- /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 "../../../
|
|
5
|
-
import {
|
|
6
|
-
import * as executor from "../../../
|
|
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 "../../../
|
|
9
|
-
import { PrivateAppUserTokenManager } from "../../../
|
|
10
|
-
vi.mock('../../../
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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 =
|
|
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' });
|
package/dist/lib/constants.d.ts
CHANGED
|
@@ -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";
|
package/dist/lib/constants.js
CHANGED
|
@@ -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, '
|
|
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/
|
|
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 '
|
|
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 "
|
|
6
|
-
import { getSignatureHeaders } from "
|
|
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 {};
|
package/dist/lib/server.js
CHANGED
|
@@ -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
|
|
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) {
|