Skip to content

Commit be483b8

Browse files
fix(analytics): lazy-load posthog-node SDK via dynamic import
Addresses Copilot review: posthog-node declares engine >=22.22.0 but this repo allows >=22.0.0. By lazy-importing posthog-node inside init() only when an API key is configured, unconfigured deployments never load the SDK — eliminating engine mismatch warnings and startup risk on older Node 22 patch versions. - analytics.service.js: init() is now async, uses dynamic import() - analytics.init.js: awaits AnalyticsService.init() - express.js: awaits module init functions (initModulesConfiguration) - All test files updated to await init()
1 parent 583b42a commit be483b8

4 files changed

Lines changed: 26 additions & 23 deletions

File tree

lib/services/express.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ const initMiddleware = (app) => {
130130
const initModulesConfiguration = async (app) => {
131131
for (const configPath of config.files.configs) {
132132
const route = await import(path.resolve(configPath));
133-
route.default(app);
133+
await route.default(app);
134134
}
135135
};
136136

modules/analytics/analytics.init.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ import AnalyticsService from './services/analytics.service.js';
88
* Called automatically by the Express init loop (matched via the
99
* `modules/{name}/{name}.init.js` glob in config/assets.js).
1010
* @param {object} _app - Express application instance (unused)
11-
* @returns {void}
11+
* @returns {Promise<void>}
1212
*/
1313
// eslint-disable-next-line no-unused-vars
14-
export default (_app) => {
15-
AnalyticsService.init();
14+
export default async (_app) => {
15+
await AnalyticsService.init();
1616
};

modules/analytics/services/analytics.service.js

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
/**
22
* Module dependencies
33
*/
4-
import { PostHog } from 'posthog-node';
5-
64
import config from '../../../config/index.js';
75

86
/**
97
* PostHog client instance (null when not configured)
10-
* @type {PostHog|null}
8+
* @type {import('posthog-node').PostHog|null}
119
*/
1210
let client = null;
1311

@@ -16,11 +14,16 @@ let client = null;
1614
* When `posthog.apiKey` is absent the service stays in no-op mode —
1715
* every public method silently returns without side-effects so that
1816
* downstream projects that don't use PostHog are never affected.
19-
* @returns {void}
17+
*
18+
* The `posthog-node` SDK is lazy-loaded (dynamic import) so that
19+
* applications running on Node versions outside the SDK's engine
20+
* range never pay the import cost when analytics is unconfigured.
21+
* @returns {Promise<void>}
2022
*/
21-
const init = () => {
23+
const init = async () => {
2224
const { apiKey, host } = config.posthog ?? {};
2325
if (!apiKey) return;
26+
const { PostHog } = await import('posthog-node');
2427
client = new PostHog(apiKey, { host: host || 'https://us.i.posthog.com' });
2528
};
2629

modules/analytics/tests/analytics.service.unit.tests.js

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('Analytics service unit tests:', () => {
4040
const mod = await import('../services/analytics.service.js');
4141
AnalyticsService = mod.default;
4242

43-
AnalyticsService.init();
43+
await AnalyticsService.init();
4444
AnalyticsService.track('user-1', 'test-event');
4545
AnalyticsService.identify('user-1', { name: 'Alice' });
4646
AnalyticsService.groupIdentify('company', 'org-1', { name: 'Acme' });
@@ -57,7 +57,7 @@ describe('Analytics service unit tests:', () => {
5757
const mod = await import('../services/analytics.service.js');
5858
AnalyticsService = mod.default;
5959

60-
AnalyticsService.init();
60+
await AnalyticsService.init();
6161
AnalyticsService.track('user-1', 'test-event');
6262

6363
const { PostHog } = await import('posthog-node');
@@ -72,7 +72,7 @@ describe('Analytics service unit tests:', () => {
7272
const mod = await import('../services/analytics.service.js');
7373
AnalyticsService = mod.default;
7474

75-
AnalyticsService.init();
75+
await AnalyticsService.init();
7676

7777
const result = await AnalyticsService.getFeatureFlag('my-flag', 'user-1');
7878
expect(result).toBeUndefined();
@@ -86,7 +86,7 @@ describe('Analytics service unit tests:', () => {
8686
const mod = await import('../services/analytics.service.js');
8787
AnalyticsService = mod.default;
8888

89-
AnalyticsService.init();
89+
await AnalyticsService.init();
9090

9191
const result = await AnalyticsService.isFeatureEnabled('my-flag', 'user-1');
9292
expect(result).toBeUndefined();
@@ -100,7 +100,7 @@ describe('Analytics service unit tests:', () => {
100100
const mod = await import('../services/analytics.service.js');
101101
AnalyticsService = mod.default;
102102

103-
AnalyticsService.init();
103+
await AnalyticsService.init();
104104
await expect(AnalyticsService.shutdown()).resolves.toBeUndefined();
105105
});
106106
});
@@ -121,7 +121,7 @@ describe('Analytics service unit tests:', () => {
121121
const mod = await import('../services/analytics.service.js');
122122
AnalyticsService = mod.default;
123123

124-
AnalyticsService.init();
124+
await AnalyticsService.init();
125125

126126
const { PostHog } = await import('posthog-node');
127127
expect(PostHog).toHaveBeenCalledWith('phc_test_key', { host: 'https://test.posthog.com' });
@@ -145,7 +145,7 @@ describe('Analytics service unit tests:', () => {
145145
const mod = await import('../services/analytics.service.js');
146146
AnalyticsService = mod.default;
147147

148-
AnalyticsService.init();
148+
await AnalyticsService.init();
149149

150150
const { PostHog } = await import('posthog-node');
151151
expect(PostHog).toHaveBeenCalledWith('phc_test_key', { host: 'https://us.i.posthog.com' });
@@ -155,7 +155,7 @@ describe('Analytics service unit tests:', () => {
155155
const mod = await import('../services/analytics.service.js');
156156
AnalyticsService = mod.default;
157157

158-
AnalyticsService.init();
158+
await AnalyticsService.init();
159159
AnalyticsService.track('user-1', 'page_view', { path: '/' }, { company: 'org-1' });
160160

161161
expect(mockPostHogInstance.capture).toHaveBeenCalledWith({
@@ -170,7 +170,7 @@ describe('Analytics service unit tests:', () => {
170170
const mod = await import('../services/analytics.service.js');
171171
AnalyticsService = mod.default;
172172

173-
AnalyticsService.init();
173+
await AnalyticsService.init();
174174
AnalyticsService.identify('user-1', { email: 'a@b.com' });
175175

176176
expect(mockPostHogInstance.identify).toHaveBeenCalledWith({
@@ -183,7 +183,7 @@ describe('Analytics service unit tests:', () => {
183183
const mod = await import('../services/analytics.service.js');
184184
AnalyticsService = mod.default;
185185

186-
AnalyticsService.init();
186+
await AnalyticsService.init();
187187
AnalyticsService.groupIdentify('company', 'org-1', { name: 'Acme' });
188188

189189
expect(mockPostHogInstance.groupIdentify).toHaveBeenCalledWith({
@@ -197,7 +197,7 @@ describe('Analytics service unit tests:', () => {
197197
const mod = await import('../services/analytics.service.js');
198198
AnalyticsService = mod.default;
199199

200-
AnalyticsService.init();
200+
await AnalyticsService.init();
201201
const result = await AnalyticsService.getFeatureFlag('my-flag', 'user-1', { groups: {} });
202202

203203
expect(mockPostHogInstance.getFeatureFlag).toHaveBeenCalledWith('my-flag', 'user-1', { groups: {} });
@@ -208,7 +208,7 @@ describe('Analytics service unit tests:', () => {
208208
const mod = await import('../services/analytics.service.js');
209209
AnalyticsService = mod.default;
210210

211-
AnalyticsService.init();
211+
await AnalyticsService.init();
212212
const result = await AnalyticsService.isFeatureEnabled('my-flag', 'user-1', { groups: {} });
213213

214214
expect(mockPostHogInstance.isFeatureEnabled).toHaveBeenCalledWith('my-flag', 'user-1', { groups: {} });
@@ -219,7 +219,7 @@ describe('Analytics service unit tests:', () => {
219219
const mod = await import('../services/analytics.service.js');
220220
AnalyticsService = mod.default;
221221

222-
AnalyticsService.init();
222+
await AnalyticsService.init();
223223
await AnalyticsService.shutdown();
224224

225225
expect(mockPostHogInstance.shutdown).toHaveBeenCalled();
@@ -229,7 +229,7 @@ describe('Analytics service unit tests:', () => {
229229
const mod = await import('../services/analytics.service.js');
230230
AnalyticsService = mod.default;
231231

232-
AnalyticsService.init();
232+
await AnalyticsService.init();
233233
await AnalyticsService.shutdown();
234234

235235
// After shutdown, track should be a no-op (no error thrown)

0 commit comments

Comments
 (0)