@hubspot/ui-extensions-dev-server 1.1.6 → 1.1.8

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 (131) hide show
  1. package/dist/lib/DevServerState.d.ts +1 -1
  2. package/dist/lib/ExtensionsWebSocket.js +26 -2
  3. package/dist/lib/__tests__/ExtensionsWebSocket.spec.js +42 -8
  4. package/dist/lib/__tests__/app-functions/context.spec.d.ts +1 -0
  5. package/dist/lib/__tests__/app-functions/context.spec.js +101 -0
  6. package/dist/lib/__tests__/app-functions/errorReporter.spec.d.ts +1 -0
  7. package/dist/lib/__tests__/app-functions/errorReporter.spec.js +102 -0
  8. package/dist/lib/__tests__/app-functions/executor_v20231.spec.d.ts +1 -0
  9. package/dist/lib/__tests__/app-functions/executor_v20231.spec.js +168 -0
  10. package/dist/lib/__tests__/app-functions/executor_v20232.spec.d.ts +1 -0
  11. package/dist/lib/__tests__/app-functions/executor_v20232.spec.js +190 -0
  12. package/dist/lib/__tests__/app-functions/fixtures/constants.d.ts +18 -0
  13. package/dist/lib/__tests__/app-functions/fixtures/constants.js +139 -0
  14. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-fails.cjs +8 -0
  15. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-fails.d.cts +1 -0
  16. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-succeeds.cjs +8 -0
  17. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-async-succeeds.d.cts +1 -0
  18. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-rejected.cjs +8 -0
  19. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-rejected.d.cts +1 -0
  20. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-resolved.cjs +8 -0
  21. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-callback-on-promise-resolved.d.cts +1 -0
  22. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-does-not-export-main.cjs +4 -0
  23. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-does-not-export-main.d.cts +1 -0
  24. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-echos-input.cjs +8 -0
  25. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-echos-input.d.cts +1 -0
  26. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-logs.cjs +10 -0
  27. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-logs.d.cts +1 -0
  28. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-function.cjs +4 -0
  29. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-function.d.cts +1 -0
  30. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-rejected.cjs +7 -0
  31. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-rejected.d.cts +1 -0
  32. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-resolved.cjs +7 -0
  33. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-promise-resolved.d.cts +1 -0
  34. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-text.cjs +4 -0
  35. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-text.d.cts +1 -0
  36. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-undefined.cjs +4 -0
  37. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-returns-undefined.d.cts +1 -0
  38. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-throws-error.cjs +4 -0
  39. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-throws-error.d.cts +1 -0
  40. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-times-out.cjs +10 -0
  41. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-times-out.d.cts +1 -0
  42. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-undeclared.cjs +4 -0
  43. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-undeclared.d.cts +1 -0
  44. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-uses-secret.cjs +14 -0
  45. package/dist/lib/__tests__/app-functions/fixtures/v2023.1/app.functions/func-uses-secret.d.cts +1 -0
  46. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-async-fails.cjs +5 -0
  47. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-async-fails.d.cts +1 -0
  48. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-async-succeeds.cjs +5 -0
  49. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-async-succeeds.d.cts +3 -0
  50. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-calls-callback.cjs +4 -0
  51. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-calls-callback.d.cts +1 -0
  52. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-does-not-export-main.cjs +4 -0
  53. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-does-not-export-main.d.cts +1 -0
  54. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-echos-input.cjs +8 -0
  55. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-echos-input.d.cts +5 -0
  56. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-logs.cjs +10 -0
  57. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-logs.d.cts +3 -0
  58. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-function.cjs +4 -0
  59. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-function.d.cts +1 -0
  60. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-implicitly.cjs +7 -0
  61. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-implicitly.d.cts +1 -0
  62. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-promise-rejected.cjs +4 -0
  63. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-promise-rejected.d.cts +1 -0
  64. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-promise-resolved.cjs +4 -0
  65. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-promise-resolved.d.cts +3 -0
  66. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-text.cjs +4 -0
  67. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-text.d.cts +1 -0
  68. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-undefined.cjs +4 -0
  69. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-returns-undefined.d.cts +1 -0
  70. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-throws-error.cjs +4 -0
  71. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-throws-error.d.cts +1 -0
  72. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-times-out.cjs +12 -0
  73. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-times-out.d.cts +1 -0
  74. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-undeclared.cjs +4 -0
  75. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-undeclared.d.cts +1 -0
  76. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-uses-secret.cjs +14 -0
  77. package/dist/lib/__tests__/app-functions/fixtures/v2023.2/app.functions/func-uses-secret.d.cts +4 -0
  78. package/dist/lib/__tests__/app-functions/secrets.spec.d.ts +1 -0
  79. package/dist/lib/__tests__/app-functions/secrets.spec.js +278 -0
  80. package/dist/lib/__tests__/app-functions/services/AppProxyService.spec.d.ts +1 -0
  81. package/dist/lib/__tests__/app-functions/services/AppProxyService.spec.js +667 -0
  82. package/dist/lib/__tests__/app-functions/services/PrivateAppUserTokenManager.spec.d.ts +1 -0
  83. package/dist/lib/__tests__/app-functions/services/PrivateAppUserTokenManager.spec.js +243 -0
  84. package/dist/lib/__tests__/app-functions/services/services_v20231.spec.d.ts +1 -0
  85. package/dist/lib/__tests__/app-functions/services/services_v20231.spec.js +319 -0
  86. package/dist/lib/__tests__/app-functions/services/services_v20232.spec.d.ts +1 -0
  87. package/dist/lib/__tests__/app-functions/services/services_v20232.spec.js +302 -0
  88. package/dist/lib/__tests__/app-functions/setup.d.ts +1 -0
  89. package/dist/lib/__tests__/app-functions/setup.js +7 -0
  90. package/dist/lib/__tests__/app-functions/signing.spec.d.ts +1 -0
  91. package/dist/lib/__tests__/app-functions/signing.spec.js +460 -0
  92. package/dist/lib/__tests__/server.spec.js +24 -2
  93. package/dist/lib/app-functions/api/privateAppUserToken.d.ts +16 -0
  94. package/dist/lib/app-functions/api/privateAppUserToken.js +28 -0
  95. package/dist/lib/app-functions/config.d.ts +4 -0
  96. package/dist/lib/app-functions/config.js +48 -0
  97. package/dist/lib/app-functions/constants.d.ts +26 -0
  98. package/dist/lib/app-functions/constants.js +63 -0
  99. package/dist/lib/app-functions/context.d.ts +3 -0
  100. package/dist/lib/app-functions/context.js +65 -0
  101. package/dist/lib/app-functions/errorReporter.d.ts +22 -0
  102. package/dist/lib/app-functions/errorReporter.js +42 -0
  103. package/dist/lib/app-functions/errors.d.ts +44 -0
  104. package/dist/lib/app-functions/errors.js +82 -0
  105. package/dist/lib/app-functions/executor.d.ts +3 -0
  106. package/dist/lib/app-functions/executor.js +131 -0
  107. package/dist/lib/app-functions/index.d.ts +4 -0
  108. package/dist/lib/app-functions/index.js +4 -0
  109. package/dist/lib/app-functions/secrets.d.ts +5 -0
  110. package/dist/lib/app-functions/secrets.js +56 -0
  111. package/dist/lib/app-functions/services/AppFunctionExecutionService.d.ts +2 -0
  112. package/dist/lib/app-functions/services/AppFunctionExecutionService.js +55 -0
  113. package/dist/lib/app-functions/services/AppProxyService.d.ts +5 -0
  114. package/dist/lib/app-functions/services/AppProxyService.js +196 -0
  115. package/dist/lib/app-functions/services/PrivateAppUserTokenManager.d.ts +22 -0
  116. package/dist/lib/app-functions/services/PrivateAppUserTokenManager.js +185 -0
  117. package/dist/lib/app-functions/services/constants.d.ts +4 -0
  118. package/dist/lib/app-functions/services/constants.js +4 -0
  119. package/dist/lib/app-functions/services/index.d.ts +3 -0
  120. package/dist/lib/app-functions/services/index.js +3 -0
  121. package/dist/lib/app-functions/services/messages.d.ts +14 -0
  122. package/dist/lib/app-functions/services/messages.js +36 -0
  123. package/dist/lib/app-functions/signing.d.ts +29 -0
  124. package/dist/lib/app-functions/signing.js +51 -0
  125. package/dist/lib/app-functions/types.d.ts +172 -0
  126. package/dist/lib/app-functions/types.js +6 -0
  127. package/dist/lib/app-functions/utils.d.ts +15 -0
  128. package/dist/lib/app-functions/utils.js +28 -0
  129. package/dist/lib/server.js +15 -4
  130. package/dist/lib/types.d.ts +1 -1
  131. package/package.json +11 -7
@@ -0,0 +1 @@
1
+ export function main(__context?: {}): Promise<void>;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ exports.main = (__context = {}) => {
3
+ return Promise.reject(new Error('fail')).then((res) => res.data);
4
+ };
@@ -0,0 +1 @@
1
+ export function main(__context?: {}): Promise<any>;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ exports.main = (__context = {}) => {
3
+ return Promise.resolve({ data: { result: 'simulated' } }).then((res) => res.data);
4
+ };
@@ -0,0 +1,3 @@
1
+ export function main(__context?: {}): Promise<{
2
+ result: string;
3
+ }>;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ exports.main = (__context = {}) => {
3
+ return 'result';
4
+ };
@@ -0,0 +1 @@
1
+ export function main(__context?: {}): string;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ exports.main = (__context = {}) => {
3
+ return undefined;
4
+ };
@@ -0,0 +1 @@
1
+ export function main(__context?: {}): undefined;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ exports.main = () => {
3
+ throw new Error('Oops');
4
+ };
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ exports.main = (context = {}) => {
3
+ const parameters = context.parameters || {};
4
+ const delayMs = parameters.delayMs || 60_000;
5
+ const startedAt = Date.now();
6
+ return new Promise((resolve, __reject) => {
7
+ setTimeout(() => resolve({
8
+ status: 'success',
9
+ elapsedMs: Date.now() - startedAt,
10
+ }), delayMs);
11
+ });
12
+ };
@@ -0,0 +1 @@
1
+ export function main(context?: {}): Promise<any>;
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ exports.main = (__context = {}) => {
3
+ return 'This function is not declared in serverless.json';
4
+ };
@@ -0,0 +1 @@
1
+ export function main(__context?: {}): string;
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ exports.main = (context = {}) => {
3
+ const parameters = context.parameters || {};
4
+ const paramPhrase = parameters['PHRASE'] || '';
5
+ const secrets = context.secrets || {};
6
+ const secretToken = secrets['PRIVATE_APP_ACCESS_TOKEN'] || '';
7
+ const secretPhrase = secrets['PHRASE'] || '';
8
+ const envToken = process.env['PRIVATE_APP_ACCESS_TOKEN'] || '';
9
+ const envPhrase = process.env['PHRASE'] || '';
10
+ return {
11
+ contextSecrets: `${secretToken}${paramPhrase}${secretPhrase}`,
12
+ processEnvs: `${envToken}${paramPhrase}${envPhrase}`,
13
+ };
14
+ };
@@ -0,0 +1,4 @@
1
+ export function main(context?: {}): {
2
+ contextSecrets: string;
3
+ processEnvs: string;
4
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,278 @@
1
+ import { describe, it, beforeEach, expect, vi, afterEach } from 'vitest';
2
+ import { vol } from 'memfs';
3
+ import { loadSecrets, setProperTokenInSecrets, } from "../../app-functions/secrets.js";
4
+ import { APP_FUNCTIONS_MANIFEST_FILENAME, PRIVATE_APP_ACCESS_TOKEN, } from "../../app-functions/constants.js";
5
+ import { SAMPLE_FUNCTION_JS, SAMPLE_SERVERLESS_JSON, } from "./fixtures/constants.js";
6
+ import { PrivateAppUserTokenManager } from "../../app-functions/services/PrivateAppUserTokenManager.js";
7
+ const SRC_DIR = '/path/to/app.functions';
8
+ const FUNCTION_NAME = 'sample-function';
9
+ const FUNCTION_FILENAME = `${FUNCTION_NAME}.cjs`;
10
+ function cloneSampleServerlessJson() {
11
+ return JSON.parse(JSON.stringify(SAMPLE_SERVERLESS_JSON));
12
+ }
13
+ function setFunctionSecrets(serverlessJson, functionName, secrets) {
14
+ if (serverlessJson.appFunctions[functionName] === undefined) {
15
+ throw new Error(`Function "${functionName}" is not declared in serverlessJson: ${JSON.stringify(serverlessJson)}`);
16
+ }
17
+ serverlessJson.appFunctions[functionName].secrets = [...secrets];
18
+ }
19
+ vi.mock('fs', async () => {
20
+ const memfs = await import('memfs');
21
+ return { default: memfs.fs, ...memfs.fs };
22
+ });
23
+ vi.mock('node:fs', async () => {
24
+ const memfs = await import('memfs');
25
+ return { default: memfs.fs, ...memfs.fs };
26
+ });
27
+ vi.mock('fs/promises', async () => {
28
+ const memfs = await import('memfs');
29
+ return { default: memfs.fs.promises, ...memfs.fs.promises };
30
+ });
31
+ vi.mock('node:fs/promises', async () => {
32
+ const memfs = await import('memfs');
33
+ return { default: memfs.fs.promises, ...memfs.fs.promises };
34
+ });
35
+ vi.mock('../../app-functions/errorReporter.ts', () => ({
36
+ reportError: vi.fn(),
37
+ }));
38
+ /**
39
+ * Validate loading of secrets from .env files into process.env
40
+ */
41
+ describe('loadSecrets', () => {
42
+ const initialEnvJson = JSON.stringify(process.env);
43
+ const serverlessJson = cloneSampleServerlessJson();
44
+ setFunctionSecrets(serverlessJson, FUNCTION_NAME, ['Token']);
45
+ afterEach(() => {
46
+ // restore process.env
47
+ process.env = JSON.parse(initialEnvJson);
48
+ vol.reset();
49
+ vi.resetAllMocks();
50
+ });
51
+ it('loads secrets from a .env file and puts them in process.env', () => {
52
+ vol.fromNestedJSON({
53
+ [SRC_DIR]: {
54
+ '.env': 'Token=tokentoken\nSomething=else',
55
+ [APP_FUNCTIONS_MANIFEST_FILENAME]: JSON.stringify(serverlessJson),
56
+ [FUNCTION_FILENAME]: SAMPLE_FUNCTION_JS,
57
+ },
58
+ });
59
+ const secrets = loadSecrets(SRC_DIR);
60
+ expect(process.env).toEqual({
61
+ ...JSON.parse(initialEnvJson),
62
+ Token: 'tokentoken',
63
+ Something: 'else',
64
+ });
65
+ expect(secrets).toEqual({});
66
+ });
67
+ it('loads secrets from a .env file, puts them in process.env and returns secrets for context', () => {
68
+ vol.fromNestedJSON({
69
+ [SRC_DIR]: {
70
+ '.env': 'Token=tokentoken\nSomething=else\nPRIVATE_APP_ACCESS_TOKEN=pat-na1-abababab',
71
+ [APP_FUNCTIONS_MANIFEST_FILENAME]: JSON.stringify(serverlessJson),
72
+ [FUNCTION_FILENAME]: SAMPLE_FUNCTION_JS,
73
+ },
74
+ });
75
+ const secrets = loadSecrets(SRC_DIR);
76
+ expect(process.env).toEqual({
77
+ ...JSON.parse(initialEnvJson),
78
+ Token: 'tokentoken',
79
+ Something: 'else',
80
+ PRIVATE_APP_ACCESS_TOKEN: 'pat-na1-abababab',
81
+ });
82
+ expect(secrets).toEqual({
83
+ PRIVATE_APP_ACCESS_TOKEN: 'pat-na1-abababab',
84
+ });
85
+ });
86
+ it('loads secrets cumulatively', () => {
87
+ vol.fromNestedJSON({
88
+ [SRC_DIR]: {
89
+ '.env': 'ALPHA=set-once\nBETA=set-once',
90
+ [APP_FUNCTIONS_MANIFEST_FILENAME]: JSON.stringify(serverlessJson),
91
+ [FUNCTION_FILENAME]: SAMPLE_FUNCTION_JS,
92
+ },
93
+ });
94
+ let secrets = loadSecrets(SRC_DIR);
95
+ expect(process.env).toEqual({
96
+ ...JSON.parse(initialEnvJson),
97
+ ALPHA: 'set-once',
98
+ BETA: 'set-once',
99
+ });
100
+ expect(secrets).toEqual({});
101
+ // Pretend the user has edited the file
102
+ vol.fromNestedJSON({
103
+ [SRC_DIR]: {
104
+ '.env': 'BETA=set-twice\nPRIVATE_APP_ACCESS_TOKEN=pat-na1-abababab',
105
+ },
106
+ });
107
+ secrets = loadSecrets(SRC_DIR);
108
+ expect(process.env).toEqual({
109
+ ...JSON.parse(initialEnvJson),
110
+ ALPHA: 'set-once', // ALPHA is still present because loadSecrets does not delete, only overrides
111
+ BETA: 'set-twice',
112
+ PRIVATE_APP_ACCESS_TOKEN: 'pat-na1-abababab',
113
+ });
114
+ expect(secrets).toEqual({
115
+ PRIVATE_APP_ACCESS_TOKEN: 'pat-na1-abababab',
116
+ });
117
+ });
118
+ it.skip('.env.[mode] > .env', () => {
119
+ vol.fromNestedJSON({
120
+ [SRC_DIR]: {
121
+ '.env': 'Level=1\nSomething=else',
122
+ '.env.test': 'Level=2',
123
+ [APP_FUNCTIONS_MANIFEST_FILENAME]: JSON.stringify(serverlessJson),
124
+ [FUNCTION_FILENAME]: SAMPLE_FUNCTION_JS,
125
+ },
126
+ });
127
+ const secrets = loadSecrets(SRC_DIR);
128
+ expect(process.env).toEqual({
129
+ ...JSON.parse(initialEnvJson),
130
+ Level: '2',
131
+ });
132
+ expect(secrets).toEqual({});
133
+ });
134
+ it(' .env.local > .env.[mode] > .env', () => {
135
+ vol.fromNestedJSON({
136
+ [SRC_DIR]: {
137
+ '.env': 'Level=1\nSomething=else',
138
+ '.env.test': 'Level=2',
139
+ '.env.local': 'Test=test\nLevel=3',
140
+ [APP_FUNCTIONS_MANIFEST_FILENAME]: JSON.stringify(serverlessJson),
141
+ [FUNCTION_FILENAME]: SAMPLE_FUNCTION_JS,
142
+ },
143
+ });
144
+ const secrets = loadSecrets(SRC_DIR);
145
+ expect(process.env).toEqual({
146
+ ...JSON.parse(initialEnvJson),
147
+ Test: 'test',
148
+ Level: '3',
149
+ });
150
+ expect(secrets).toEqual({});
151
+ });
152
+ it.skip('.env.[mode].local > .env.local > .env.[mode] > .env', () => {
153
+ vol.fromNestedJSON({
154
+ [SRC_DIR]: {
155
+ '.env': 'Level=1\nSomething=else',
156
+ '.env.test': 'Level=2',
157
+ '.env.local': 'Test=test\nLevel=3',
158
+ '.env.test.local': '',
159
+ [APP_FUNCTIONS_MANIFEST_FILENAME]: JSON.stringify(serverlessJson),
160
+ [FUNCTION_FILENAME]: SAMPLE_FUNCTION_JS,
161
+ },
162
+ });
163
+ const secrets = loadSecrets(SRC_DIR);
164
+ expect(process.env).toEqual({ ...JSON.parse(initialEnvJson) });
165
+ expect(secrets).toEqual({});
166
+ });
167
+ it('loads secrets as strings', () => {
168
+ vol.fromNestedJSON({
169
+ [SRC_DIR]: {
170
+ '.env': 'ZERO=0\nBOOL=false\nQUOTED="quoted string"\nSPACE=" "\nEMPTY=',
171
+ [APP_FUNCTIONS_MANIFEST_FILENAME]: JSON.stringify(serverlessJson),
172
+ [FUNCTION_FILENAME]: SAMPLE_FUNCTION_JS,
173
+ },
174
+ });
175
+ const secrets = loadSecrets(SRC_DIR);
176
+ expect(process.env).toEqual({
177
+ ...JSON.parse(initialEnvJson),
178
+ ZERO: '0',
179
+ BOOL: 'false',
180
+ QUOTED: 'quoted string',
181
+ SPACE: ' ',
182
+ EMPTY: '',
183
+ });
184
+ expect(secrets).toEqual({});
185
+ });
186
+ it('ignores if no .env file is found', () => {
187
+ vol.fromNestedJSON({
188
+ [SRC_DIR]: {
189
+ [APP_FUNCTIONS_MANIFEST_FILENAME]: JSON.stringify(serverlessJson),
190
+ [FUNCTION_FILENAME]: SAMPLE_FUNCTION_JS,
191
+ },
192
+ });
193
+ const secrets = loadSecrets(SRC_DIR);
194
+ expect(process.env).toEqual({ ...JSON.parse(initialEnvJson) });
195
+ expect(secrets).toEqual({});
196
+ });
197
+ it('ignores if the .env file is not valid', () => {
198
+ vol.fromNestedJSON({
199
+ [SRC_DIR]: {
200
+ '.env': 'gibberish',
201
+ [APP_FUNCTIONS_MANIFEST_FILENAME]: JSON.stringify(serverlessJson),
202
+ [FUNCTION_FILENAME]: SAMPLE_FUNCTION_JS,
203
+ },
204
+ });
205
+ const secrets = loadSecrets(SRC_DIR);
206
+ expect(process.env).toEqual({ ...JSON.parse(initialEnvJson) });
207
+ expect(secrets).toEqual({});
208
+ });
209
+ });
210
+ describe('setProperTokenInSecrets', () => {
211
+ let logger;
212
+ const initialEnvJson = JSON.stringify(process.env);
213
+ const privateAppUserToken = {
214
+ userId: 111,
215
+ portalId: 123,
216
+ appId: 345,
217
+ scopeGroups: ['crm.objects.contacts.read', 'crm.objects.contacts.write'],
218
+ userTokenKey: 'pat-na1-u-FFFFFFFF-FFFF-FFFF-FFFF-FFFFFFFFFFFF',
219
+ clientId: 'my-client-id',
220
+ expiresAt: '2024-06-01T01:00:00Z',
221
+ privateAppTokenInfo: {
222
+ userId: 111,
223
+ portalId: 123,
224
+ appId: 345,
225
+ scopeGroups: ['crm.objects.contacts.read', 'crm.objects.contacts.write'],
226
+ expiresAt: '2024-06-01T01:00:00Z',
227
+ },
228
+ };
229
+ beforeEach(() => {
230
+ logger = {
231
+ error: vi.fn(),
232
+ debug: vi.fn(),
233
+ info: vi.fn(),
234
+ warn: vi.fn(),
235
+ };
236
+ });
237
+ afterEach(() => {
238
+ // restore process.env
239
+ process.env = JSON.parse(initialEnvJson);
240
+ vi.resetAllMocks();
241
+ });
242
+ it('returns secrets unchanged if no privateAppUserToken is provided', () => {
243
+ const secrets = { [PRIVATE_APP_ACCESS_TOKEN]: 'pat-na1-557-4531' };
244
+ const result = setProperTokenInSecrets(secrets, logger);
245
+ expect(result).toEqual(secrets);
246
+ });
247
+ it('preserves the privateAppToken in PRIVATE_APP_ACCESS_TOKEN in secrets/env even if privateAppUserToken is provided', () => {
248
+ const appToken = 'pat-na1-557-4531';
249
+ process.env['PRIVATE_APP_ACCESS_TOKEN'] = appToken;
250
+ const secrets = { [PRIVATE_APP_ACCESS_TOKEN]: appToken };
251
+ const result = setProperTokenInSecrets(secrets, logger, privateAppUserToken);
252
+ expect(result).toEqual({
253
+ ...secrets,
254
+ PRIVATE_APP_ACCESS_TOKEN: appToken,
255
+ });
256
+ expect(process.env.PRIVATE_APP_ACCESS_TOKEN).toBe(appToken);
257
+ });
258
+ it('sets privateAppUserToken to PRIVATE_APP_ACCESS_TOKEN in secrets/env even no privateAppToken is provided', () => {
259
+ const secrets = {};
260
+ const result = setProperTokenInSecrets(secrets, logger, privateAppUserToken);
261
+ expect(result).toEqual({
262
+ ...secrets,
263
+ PRIVATE_APP_ACCESS_TOKEN: privateAppUserToken.userTokenKey,
264
+ });
265
+ expect(process.env.PRIVATE_APP_ACCESS_TOKEN).toBe(privateAppUserToken.userTokenKey);
266
+ });
267
+ it('logs a message if privateAppUserToken contains all scopes from privateAppToken', () => {
268
+ const secrets = { [PRIVATE_APP_ACCESS_TOKEN]: 'pat-na1-557-4531' };
269
+ setProperTokenInSecrets(secrets, logger, privateAppUserToken);
270
+ expect(logger.info).toHaveBeenCalledWith('The private app user token was overridden by the PRIVATE_APP_ACCESS_TOKEN in the environment file. For local development, you no longer need a PRIVATE_APP_ACCESS_TOKEN in the environment file. You can safely remove it.');
271
+ });
272
+ it('does not log a message if privateAppUserToken does not contain all scopes from privateAppToken', () => {
273
+ const secrets = { [PRIVATE_APP_ACCESS_TOKEN]: 'pat-na1-557-4531' };
274
+ vi.spyOn(PrivateAppUserTokenManager, 'doesUserTokenContainAppTokenScopes').mockReturnValue(false);
275
+ setProperTokenInSecrets(secrets, logger, privateAppUserToken);
276
+ expect(logger.info).not.toHaveBeenCalled();
277
+ });
278
+ });