@friggframework/core 2.0.0--canary.545.dba001a.0 → 2.0.0--canary.545.c40eca4.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.
@@ -25,17 +25,41 @@ const {
25
25
  } = require('../../user/use-cases/create-token-for-user-id');
26
26
  const { DeleteUser } = require('../../user/use-cases/delete-user');
27
27
 
28
- // Initialize repositories and use cases
29
- const { userConfig } = loadAppDefinition();
30
- const userRepository = createUserRepository({ userConfig });
31
- const moduleRepository = createModuleRepository();
32
-
33
- // Use cases
34
- const getModuleEntityById = new GetModuleEntityById({ moduleRepository });
35
- const updateModuleEntity = new UpdateModuleEntity({ moduleRepository });
36
- const deleteModuleEntity = new DeleteModuleEntity({ moduleRepository });
37
- const createTokenForUserId = new CreateTokenForUserId({ userRepository });
38
- const deleteUser = new DeleteUser({ userRepository });
28
+ // Lazy-initialized repositories and use cases (deferred until first request)
29
+ let _userRepository,
30
+ _moduleRepository,
31
+ _getModuleEntityById,
32
+ _updateModuleEntity,
33
+ _deleteModuleEntity,
34
+ _createTokenForUserId,
35
+ _deleteUser;
36
+
37
+ function ensureInitialized() {
38
+ if (!_userRepository) {
39
+ const { userConfig } = loadAppDefinition();
40
+ _userRepository = createUserRepository({ userConfig });
41
+ _moduleRepository = createModuleRepository();
42
+ _getModuleEntityById = new GetModuleEntityById({
43
+ moduleRepository: _moduleRepository,
44
+ });
45
+ _updateModuleEntity = new UpdateModuleEntity({
46
+ moduleRepository: _moduleRepository,
47
+ });
48
+ _deleteModuleEntity = new DeleteModuleEntity({
49
+ moduleRepository: _moduleRepository,
50
+ });
51
+ _createTokenForUserId = new CreateTokenForUserId({
52
+ userRepository: _userRepository,
53
+ });
54
+ _deleteUser = new DeleteUser({ userRepository: _userRepository });
55
+ }
56
+ }
57
+
58
+ // Lazy initialization middleware — runs once on first request
59
+ router.use((req, res, next) => {
60
+ ensureInitialized();
61
+ next();
62
+ });
39
63
 
40
64
  // Debug logging
41
65
  router.use((req, res, next) => {
@@ -72,14 +96,14 @@ router.get(
72
96
  sort[sortBy] = sortOrder === 'desc' ? -1 : 1;
73
97
 
74
98
  // Use repository to get users
75
- const users = await userRepository.findAllUsers({
99
+ const users = await _userRepository.findAllUsers({
76
100
  skip,
77
101
  limit: parseInt(limit),
78
102
  sort,
79
103
  excludeFields: ['-hashword'], // Exclude password hash
80
104
  });
81
105
 
82
- const totalCount = await userRepository.countUsers();
106
+ const totalCount = await _userRepository.countUsers();
83
107
 
84
108
  res.json({
85
109
  users,
@@ -122,7 +146,7 @@ router.get(
122
146
  sort[sortBy] = sortOrder === 'desc' ? -1 : 1;
123
147
 
124
148
  // Use repository to search users
125
- const users = await userRepository.searchUsers({
149
+ const users = await _userRepository.searchUsers({
126
150
  query: q,
127
151
  skip,
128
152
  limit: parseInt(limit),
@@ -130,7 +154,7 @@ router.get(
130
154
  excludeFields: ['-hashword'],
131
155
  });
132
156
 
133
- const totalCount = await userRepository.countUsersBySearchQuery(q);
157
+ const totalCount = await _userRepository.countUsersBySearchQuery(q);
134
158
 
135
159
  res.json({
136
160
  users,
@@ -175,7 +199,7 @@ router.post(
175
199
  }
176
200
 
177
201
  // Check if user already exists
178
- const existingUser = await userRepository.findIndividualUserByUsername(
202
+ const existingUser = await _userRepository.findIndividualUserByUsername(
179
203
  username
180
204
  );
181
205
  if (existingUser) {
@@ -185,7 +209,7 @@ router.post(
185
209
  });
186
210
  }
187
211
 
188
- const existingEmail = await userRepository.findIndividualUserByEmail(
212
+ const existingEmail = await _userRepository.findIndividualUserByEmail(
189
213
  email
190
214
  );
191
215
  if (existingEmail) {
@@ -210,7 +234,7 @@ router.post(
210
234
  if (appUserId) userData.appUserId = appUserId;
211
235
  if (organizationId) userData.organizationId = organizationId;
212
236
 
213
- const user = await userRepository.createIndividualUser(userData);
237
+ const user = await _userRepository.createIndividualUser(userData);
214
238
 
215
239
  // Remove sensitive fields
216
240
  const userObj = user.toObject ? user.toObject() : user;
@@ -232,7 +256,7 @@ router.get(
232
256
  catchAsyncError(async (req, res) => {
233
257
  const { userId } = req.params;
234
258
 
235
- const user = await userRepository.findUserById(userId);
259
+ const user = await _userRepository.findUserById(userId);
236
260
 
237
261
  if (!user) {
238
262
  return res.status(404).json({
@@ -261,7 +285,7 @@ router.post(
261
285
  const { expiresInMinutes = 120 } = req.body;
262
286
 
263
287
  // Find the user
264
- const user = await userRepository.findUserById(userId);
288
+ const user = await _userRepository.findUserById(userId);
265
289
 
266
290
  if (!user) {
267
291
  return res.status(404).json({
@@ -271,7 +295,7 @@ router.post(
271
295
  }
272
296
 
273
297
  // Generate token without password verification
274
- const token = await createTokenForUserId.execute(
298
+ const token = await _createTokenForUserId.execute(
275
299
  userId,
276
300
  expiresInMinutes
277
301
  );
@@ -295,7 +319,7 @@ router.delete(
295
319
  const { userId } = req.params;
296
320
 
297
321
  // Execute delete user use case
298
- await deleteUser.execute(userId);
322
+ await _deleteUser.execute(userId);
299
323
 
300
324
  res.status(204).send();
301
325
  })
@@ -318,7 +342,7 @@ router.get(
318
342
  if (type) query.type = type;
319
343
  if (status) query.status = status;
320
344
 
321
- const entities = await moduleRepository.findEntitiesBy(query);
345
+ const entities = await _moduleRepository.findEntitiesBy(query);
322
346
 
323
347
  res.json({ entities });
324
348
  })
@@ -333,7 +357,7 @@ router.get(
333
357
  catchAsyncError(async (req, res) => {
334
358
  const { entityId } = req.params;
335
359
 
336
- const entity = await getModuleEntityById.execute(entityId);
360
+ const entity = await _getModuleEntityById.execute(entityId);
337
361
 
338
362
  if (!entity || !entity.isGlobal) {
339
363
  return res.status(404).json({
@@ -363,7 +387,7 @@ router.post(
363
387
  }
364
388
 
365
389
  // Create entity with isGlobal flag
366
- const entity = await moduleRepository.createEntity({
390
+ const entity = await _moduleRepository.createEntity({
367
391
  ...entityData,
368
392
  type,
369
393
  isGlobal: true,
@@ -383,7 +407,7 @@ router.put(
383
407
  catchAsyncError(async (req, res) => {
384
408
  const { entityId } = req.params;
385
409
 
386
- const entity = await updateModuleEntity.execute(entityId, req.body);
410
+ const entity = await _updateModuleEntity.execute(entityId, req.body);
387
411
 
388
412
  if (!entity) {
389
413
  return res.status(404).json({
@@ -405,7 +429,7 @@ router.delete(
405
429
  catchAsyncError(async (req, res) => {
406
430
  const { entityId } = req.params;
407
431
 
408
- await deleteModuleEntity.execute(entityId);
432
+ await _deleteModuleEntity.execute(entityId);
409
433
 
410
434
  res.status(204).send();
411
435
  })
@@ -420,7 +444,7 @@ router.post(
420
444
  catchAsyncError(async (req, res) => {
421
445
  const { entityId } = req.params;
422
446
 
423
- const entity = await getModuleEntityById.execute(entityId);
447
+ const entity = await _getModuleEntityById.execute(entityId);
424
448
 
425
449
  if (!entity || !entity.isGlobal) {
426
450
  return res.status(404).json({
@@ -3,34 +3,55 @@ const { createAppHandler } = require('./../app-handler-helpers');
3
3
  const { requireLoggedInUser } = require('./middleware/requireLoggedInUser');
4
4
  const { loadAppDefinition } = require('../app-definition-loader');
5
5
 
6
- const router = createIntegrationRouter();
6
+ let _router;
7
+ let _handler;
7
8
 
8
- router.route('/api/integrations/redirect/:appId').get((req, res) => {
9
- res.redirect(
10
- `${process.env.FRONTEND_URI}/redirect/${
11
- req.params.appId
12
- }?${new URLSearchParams(req.query)}`
13
- );
14
- });
9
+ function ensureRouter() {
10
+ if (!_router) {
11
+ _router = createIntegrationRouter();
15
12
 
16
- // Integration settings endpoint
17
- router
18
- .route('/config/integration-settings')
19
- .get(requireLoggedInUser, (req, res) => {
20
- const appDefinition = loadAppDefinition();
13
+ _router
14
+ .route('/api/integrations/redirect/:appId')
15
+ .get((req, res) => {
16
+ res.redirect(
17
+ `${process.env.FRONTEND_URI}/redirect/${
18
+ req.params.appId
19
+ }?${new URLSearchParams(req.query)}`
20
+ );
21
+ });
21
22
 
22
- const settings = {
23
- autoProvisioningEnabled:
24
- appDefinition.integration?.autoProvisioningEnabled ?? true,
25
- credentialReuseStrategy:
26
- appDefinition.integration?.credentialReuseStrategy ?? 'shared',
27
- allowUserManagedEntities:
28
- appDefinition.integration?.allowUserManagedEntities ?? true,
29
- };
23
+ // Integration settings endpoint
24
+ _router
25
+ .route('/config/integration-settings')
26
+ .get(requireLoggedInUser, (req, res) => {
27
+ const appDefinition = loadAppDefinition();
30
28
 
31
- res.json(settings);
32
- });
29
+ const settings = {
30
+ autoProvisioningEnabled:
31
+ appDefinition.integration
32
+ ?.autoProvisioningEnabled ?? true,
33
+ credentialReuseStrategy:
34
+ appDefinition.integration
35
+ ?.credentialReuseStrategy ?? 'shared',
36
+ allowUserManagedEntities:
37
+ appDefinition.integration
38
+ ?.allowUserManagedEntities ?? true,
39
+ };
33
40
 
34
- const handler = createAppHandler('HTTP Event: Auth', router);
41
+ res.json(settings);
42
+ });
43
+ }
44
+ return _router;
45
+ }
35
46
 
36
- module.exports = { handler, router };
47
+ module.exports = {
48
+ get router() {
49
+ return ensureRouter();
50
+ },
51
+ get handler() {
52
+ if (!_handler) {
53
+ _handler = createAppHandler('HTTP Event: Auth', ensureRouter());
54
+ }
55
+ return _handler;
56
+ },
57
+ };
@@ -29,49 +29,58 @@ const {
29
29
  } = require('../use-cases/check-integrations-health-use-case');
30
30
 
31
31
  const router = Router();
32
- const healthCheckRepository = createHealthCheckRepository({
33
- prismaClient: prisma,
34
- });
35
32
 
36
- // Load integrations and create factories just like auth router does
37
- // This verifies the system can properly load integrations
38
- let moduleFactory, integrationClasses;
39
- try {
40
- const appDef = loadAppDefinition();
41
- integrationClasses = appDef.integrations || [];
33
+ // Lazy-initialized use cases (deferred until first request)
34
+ let _checkDatabaseHealthUseCase,
35
+ _checkEncryptionHealthUseCase,
36
+ _checkExternalApisHealthUseCase,
37
+ _checkIntegrationsHealthUseCase;
42
38
 
43
- const moduleRepository = createModuleRepository();
44
- const moduleDefinitions =
45
- getModulesDefinitionFromIntegrationClasses(integrationClasses);
39
+ function ensureInitialized() {
40
+ if (!_checkDatabaseHealthUseCase) {
41
+ const healthCheckRepository = createHealthCheckRepository({
42
+ prismaClient: prisma,
43
+ });
46
44
 
47
- moduleFactory = new ModuleFactory({
48
- moduleRepository,
49
- moduleDefinitions,
50
- });
51
- } catch (error) {
52
- console.error(
53
- 'Failed to load integrations for health check:',
54
- error.message
55
- );
56
- // Factories will be undefined, health check will report unhealthy
57
- moduleFactory = undefined;
58
- integrationClasses = [];
59
- }
45
+ let moduleFactory, integrationClasses;
46
+ try {
47
+ const appDef = loadAppDefinition();
48
+ integrationClasses = appDef.integrations || [];
49
+
50
+ const moduleRepository = createModuleRepository();
51
+ const moduleDefinitions =
52
+ getModulesDefinitionFromIntegrationClasses(integrationClasses);
53
+
54
+ moduleFactory = new ModuleFactory({
55
+ moduleRepository,
56
+ moduleDefinitions,
57
+ });
58
+ } catch (error) {
59
+ console.error(
60
+ 'Failed to load integrations for health check:',
61
+ error.message
62
+ );
63
+ moduleFactory = undefined;
64
+ integrationClasses = [];
65
+ }
60
66
 
61
- const testEncryptionUseCase = new TestEncryptionUseCase({
62
- healthCheckRepository,
63
- });
64
- const checkDatabaseHealthUseCase = new CheckDatabaseHealthUseCase({
65
- healthCheckRepository,
66
- });
67
- const checkEncryptionHealthUseCase = new CheckEncryptionHealthUseCase({
68
- testEncryptionUseCase,
69
- });
70
- const checkExternalApisHealthUseCase = new CheckExternalApisHealthUseCase();
71
- const checkIntegrationsHealthUseCase = new CheckIntegrationsHealthUseCase({
72
- moduleFactory,
73
- integrationClasses,
74
- });
67
+ const testEncryptionUseCase = new TestEncryptionUseCase({
68
+ healthCheckRepository,
69
+ });
70
+ _checkDatabaseHealthUseCase = new CheckDatabaseHealthUseCase({
71
+ healthCheckRepository,
72
+ });
73
+ _checkEncryptionHealthUseCase = new CheckEncryptionHealthUseCase({
74
+ testEncryptionUseCase,
75
+ });
76
+ _checkExternalApisHealthUseCase =
77
+ new CheckExternalApisHealthUseCase();
78
+ _checkIntegrationsHealthUseCase = new CheckIntegrationsHealthUseCase({
79
+ moduleFactory,
80
+ integrationClasses,
81
+ });
82
+ }
83
+ }
75
84
 
76
85
  const validateApiKey = (req, res, next) => {
77
86
  const apiKey = req.headers['x-frigg-health-api-key'];
@@ -132,6 +141,7 @@ router.get('/health', async (_req, res) => {
132
141
  });
133
142
 
134
143
  router.get('/health/detailed', async (_req, res) => {
144
+ ensureInitialized();
135
145
  console.log('Starting detailed health check');
136
146
  const startTime = Date.now();
137
147
 
@@ -197,7 +207,7 @@ router.get('/health/detailed', async (_req, res) => {
197
207
  }
198
208
 
199
209
  try {
200
- response.checks.database = await checkDatabaseHealthUseCase.execute();
210
+ response.checks.database = await _checkDatabaseHealthUseCase.execute();
201
211
  if (response.checks.database.status === 'unhealthy') {
202
212
  response.status = 'unhealthy';
203
213
  }
@@ -213,7 +223,7 @@ router.get('/health/detailed', async (_req, res) => {
213
223
 
214
224
  try {
215
225
  response.checks.encryption =
216
- await checkEncryptionHealthUseCase.execute();
226
+ await _checkEncryptionHealthUseCase.execute();
217
227
  if (response.checks.encryption.status === 'unhealthy') {
218
228
  response.status = 'unhealthy';
219
229
  }
@@ -229,7 +239,7 @@ router.get('/health/detailed', async (_req, res) => {
229
239
 
230
240
  try {
231
241
  const { apiStatuses, allReachable } =
232
- await checkExternalApisHealthUseCase.execute();
242
+ await _checkExternalApisHealthUseCase.execute();
233
243
  response.checks.externalApis = apiStatuses;
234
244
  if (!allReachable) {
235
245
  response.status = 'unhealthy';
@@ -248,7 +258,7 @@ router.get('/health/detailed', async (_req, res) => {
248
258
  }
249
259
 
250
260
  try {
251
- response.checks.integrations = checkIntegrationsHealthUseCase.execute();
261
+ response.checks.integrations = _checkIntegrationsHealthUseCase.execute();
252
262
  console.log(
253
263
  'Integrations check completed:',
254
264
  response.checks.integrations
@@ -283,10 +293,11 @@ router.get('/health/live', (_req, res) => {
283
293
  });
284
294
 
285
295
  router.get('/health/ready', async (_req, res) => {
286
- const dbHealth = await checkDatabaseHealthUseCase.execute();
296
+ ensureInitialized();
297
+ const dbHealth = await _checkDatabaseHealthUseCase.execute();
287
298
  const isDbReady = dbHealth.status === 'healthy';
288
299
 
289
- const integrationsHealth = checkIntegrationsHealthUseCase.execute();
300
+ const integrationsHealth = _checkIntegrationsHealthUseCase.execute();
290
301
  const areModulesReady = integrationsHealth.modules.count > 0;
291
302
 
292
303
  const isReady = isDbReady && areModulesReady;
@@ -3,43 +3,54 @@ const { loadAppDefinition } = require('../app-definition-loader');
3
3
  const { Router } = require('express');
4
4
  const { loadRouterFromObject } = require('../backend-utils');
5
5
 
6
- const handlers = {};
7
- const { integrations: integrationClasses } = loadAppDefinition();
6
+ let _handlers;
8
7
 
9
- //todo: this should be in a use case class
10
- for (const IntegrationClass of integrationClasses) {
11
- const router = Router();
12
- const basePath = `/api/${IntegrationClass.Definition.name}-integration`;
8
+ function ensureHandlers() {
9
+ if (!_handlers) {
10
+ _handlers = {};
11
+ const { integrations: integrationClasses } = loadAppDefinition();
13
12
 
14
- console.log(
15
- `\n│ Configuring routes for ${IntegrationClass.Definition.name} Integration:`
16
- );
13
+ //todo: this should be in a use case class
14
+ for (const IntegrationClass of integrationClasses) {
15
+ const router = Router();
16
+ const basePath = `/api/${IntegrationClass.Definition.name}-integration`;
17
17
 
18
- for (const routeDef of IntegrationClass.Definition.routes) {
19
- if (typeof routeDef === 'function') {
20
- router.use(basePath, routeDef(IntegrationClass));
21
- console.log(`│ ANY ${basePath}/* (function handler)`);
22
- } else if (typeof routeDef === 'object') {
23
- router.use(
24
- basePath,
25
- loadRouterFromObject(IntegrationClass, routeDef)
18
+ console.log(
19
+ `\n│ Configuring routes for ${IntegrationClass.Definition.name} Integration:`
26
20
  );
27
- const method = (routeDef.method || 'ANY').toUpperCase();
28
- const fullPath = `${basePath}${routeDef.path}`;
29
- console.log(`│ ${method} ${fullPath}`);
30
- } else if (routeDef instanceof express.Router) {
31
- router.use(basePath, routeDef);
32
- console.log(`│ ANY ${basePath}/* (express router)`);
21
+
22
+ for (const routeDef of IntegrationClass.Definition.routes) {
23
+ if (typeof routeDef === 'function') {
24
+ router.use(basePath, routeDef(IntegrationClass));
25
+ console.log(`│ ANY ${basePath}/* (function handler)`);
26
+ } else if (typeof routeDef === 'object') {
27
+ router.use(
28
+ basePath,
29
+ loadRouterFromObject(IntegrationClass, routeDef)
30
+ );
31
+ const method = (routeDef.method || 'ANY').toUpperCase();
32
+ const fullPath = `${basePath}${routeDef.path}`;
33
+ console.log(`│ ${method} ${fullPath}`);
34
+ } else if (routeDef instanceof express.Router) {
35
+ router.use(basePath, routeDef);
36
+ console.log(`│ ANY ${basePath}/* (express router)`);
37
+ }
38
+ }
39
+ console.log('│');
40
+
41
+ _handlers[`${IntegrationClass.Definition.name}`] = {
42
+ handler: createAppHandler(
43
+ `HTTP Event: ${IntegrationClass.Definition.name}`,
44
+ router
45
+ ),
46
+ };
33
47
  }
34
48
  }
35
- console.log('│');
36
-
37
- handlers[`${IntegrationClass.Definition.name}`] = {
38
- handler: createAppHandler(
39
- `HTTP Event: ${IntegrationClass.Definition.name}`,
40
- router
41
- ),
42
- };
49
+ return _handlers;
43
50
  }
44
51
 
45
- module.exports = { handlers };
52
+ module.exports = {
53
+ get handlers() {
54
+ return ensureHandlers();
55
+ },
56
+ };
@@ -5,73 +5,84 @@ const {
5
5
  IntegrationEventDispatcher,
6
6
  } = require('../integration-event-dispatcher');
7
7
 
8
- const handlers = {};
9
- const { integrations: integrationClasses } = loadAppDefinition();
8
+ let _handlers;
10
9
 
11
- for (const IntegrationClass of integrationClasses) {
12
- const webhookConfig = IntegrationClass.Definition.webhooks;
10
+ function ensureHandlers() {
11
+ if (!_handlers) {
12
+ _handlers = {};
13
+ const { integrations: integrationClasses } = loadAppDefinition();
13
14
 
14
- // Skip if webhooks not enabled
15
- if (
16
- !webhookConfig ||
17
- (typeof webhookConfig === 'object' && !webhookConfig.enabled)
18
- ) {
19
- continue;
20
- }
15
+ for (const IntegrationClass of integrationClasses) {
16
+ const webhookConfig = IntegrationClass.Definition.webhooks;
21
17
 
22
- const router = Router();
23
- const basePath = `/api/${IntegrationClass.Definition.name}-integration/webhooks`;
18
+ // Skip if webhooks not enabled
19
+ if (
20
+ !webhookConfig ||
21
+ (typeof webhookConfig === 'object' && !webhookConfig.enabled)
22
+ ) {
23
+ continue;
24
+ }
24
25
 
25
- console.log(
26
- `\n│ Configuring webhook routes for ${IntegrationClass.Definition.name}:`
27
- );
26
+ const router = Router();
27
+ const basePath = `/api/${IntegrationClass.Definition.name}-integration/webhooks`;
28
28
 
29
- // General webhook route (no integration ID)
30
- router.post(basePath, async (req, res, next) => {
31
- try {
32
- const integrationInstance = new IntegrationClass();
33
- const dispatcher = new IntegrationEventDispatcher(
34
- integrationInstance
29
+ console.log(
30
+ `\n│ Configuring webhook routes for ${IntegrationClass.Definition.name}:`
35
31
  );
36
- await dispatcher.dispatchHttp({
37
- event: 'WEBHOOK_RECEIVED',
38
- req,
39
- res,
40
- next,
32
+
33
+ // General webhook route (no integration ID)
34
+ router.post(basePath, async (req, res, next) => {
35
+ try {
36
+ const integrationInstance = new IntegrationClass();
37
+ const dispatcher = new IntegrationEventDispatcher(
38
+ integrationInstance
39
+ );
40
+ await dispatcher.dispatchHttp({
41
+ event: 'WEBHOOK_RECEIVED',
42
+ req,
43
+ res,
44
+ next,
45
+ });
46
+ } catch (error) {
47
+ next(error);
48
+ }
41
49
  });
42
- } catch (error) {
43
- next(error);
44
- }
45
- });
46
- console.log(`│ POST ${basePath}`);
50
+ console.log(`│ POST ${basePath}`);
47
51
 
48
- // Integration-specific webhook route (with integration ID)
49
- router.post(`${basePath}/:integrationId`, async (req, res, next) => {
50
- try {
51
- const integrationInstance = new IntegrationClass();
52
- const dispatcher = new IntegrationEventDispatcher(
53
- integrationInstance
54
- );
55
- await dispatcher.dispatchHttp({
56
- event: 'WEBHOOK_RECEIVED',
57
- req,
58
- res,
59
- next,
52
+ // Integration-specific webhook route (with integration ID)
53
+ router.post(`${basePath}/:integrationId`, async (req, res, next) => {
54
+ try {
55
+ const integrationInstance = new IntegrationClass();
56
+ const dispatcher = new IntegrationEventDispatcher(
57
+ integrationInstance
58
+ );
59
+ await dispatcher.dispatchHttp({
60
+ event: 'WEBHOOK_RECEIVED',
61
+ req,
62
+ res,
63
+ next,
64
+ });
65
+ } catch (error) {
66
+ next(error);
67
+ }
60
68
  });
61
- } catch (error) {
62
- next(error);
63
- }
64
- });
65
- console.log(`│ POST ${basePath}/:integrationId`);
66
- console.log('│');
69
+ console.log(`│ POST ${basePath}/:integrationId`);
70
+ console.log('│');
67
71
 
68
- handlers[`${IntegrationClass.Definition.name}Webhook`] = {
69
- handler: createAppHandler(
70
- `HTTP Event: ${IntegrationClass.Definition.name} Webhook`,
71
- router,
72
- false // shouldUseDatabase = false
73
- ),
74
- };
72
+ _handlers[`${IntegrationClass.Definition.name}Webhook`] = {
73
+ handler: createAppHandler(
74
+ `HTTP Event: ${IntegrationClass.Definition.name} Webhook`,
75
+ router,
76
+ false // shouldUseDatabase = false
77
+ ),
78
+ };
79
+ }
80
+ }
81
+ return _handlers;
75
82
  }
76
83
 
77
- module.exports = { handlers };
84
+ module.exports = {
85
+ get handlers() {
86
+ return ensureHandlers();
87
+ },
88
+ };
@@ -15,17 +15,33 @@ const catchAsyncError = require('express-async-handler');
15
15
  const { loadAppDefinition } = require('../app-definition-loader');
16
16
 
17
17
  const router = express();
18
- const { userConfig } = loadAppDefinition();
19
- const userRepository = createUserRepository();
20
- const createIndividualUser = new CreateIndividualUser({
21
- userRepository,
22
- userConfig,
23
- });
24
- const loginUser = new LoginUser({
25
- userRepository,
26
- userConfig,
18
+
19
+ // Lazy-initialized repositories and use cases (deferred until first request)
20
+ let _userRepository, _createIndividualUser, _loginUser, _createTokenForUserId;
21
+
22
+ function ensureInitialized() {
23
+ if (!_userRepository) {
24
+ const { userConfig } = loadAppDefinition();
25
+ _userRepository = createUserRepository();
26
+ _createIndividualUser = new CreateIndividualUser({
27
+ userRepository: _userRepository,
28
+ userConfig,
29
+ });
30
+ _loginUser = new LoginUser({
31
+ userRepository: _userRepository,
32
+ userConfig,
33
+ });
34
+ _createTokenForUserId = new CreateTokenForUserId({
35
+ userRepository: _userRepository,
36
+ });
37
+ }
38
+ }
39
+
40
+ // Lazy initialization middleware — runs once on first request
41
+ router.use((req, res, next) => {
42
+ ensureInitialized();
43
+ next();
27
44
  });
28
- const createTokenForUserId = new CreateTokenForUserId({ userRepository });
29
45
 
30
46
  // define the login endpoint
31
47
  router.route('/user/login').post(
@@ -34,8 +50,8 @@ router.route('/user/login').post(
34
50
  'username',
35
51
  'password',
36
52
  ]);
37
- const user = await loginUser.execute({ username, password });
38
- const token = await createTokenForUserId.execute(user.getId(), 120);
53
+ const user = await _loginUser.execute({ username, password });
54
+ const token = await _createTokenForUserId.execute(user.getId(), 120);
39
55
  res.status(201);
40
56
  res.json({ token });
41
57
  })
@@ -48,11 +64,11 @@ router.route('/user/create').post(
48
64
  'password',
49
65
  ]);
50
66
 
51
- const user = await createIndividualUser.execute({
67
+ const user = await _createIndividualUser.execute({
52
68
  username,
53
69
  password,
54
70
  });
55
- const token = await createTokenForUserId.execute(user.getId(), 120);
71
+ const token = await _createTokenForUserId.execute(user.getId(), 120);
56
72
  res.status(201);
57
73
  res.json({ token });
58
74
  })
@@ -3,7 +3,15 @@ const {
3
3
  createWebsocketConnectionRepository,
4
4
  } = require('../../database/websocket-connection-repository-factory');
5
5
 
6
- const websocketConnectionRepository = createWebsocketConnectionRepository();
6
+ let _websocketConnectionRepository;
7
+
8
+ function getRepository() {
9
+ if (!_websocketConnectionRepository) {
10
+ _websocketConnectionRepository =
11
+ createWebsocketConnectionRepository();
12
+ }
13
+ return _websocketConnectionRepository;
14
+ }
7
15
 
8
16
  const handleWebSocketConnection = async (event, context) => {
9
17
  // Handle different WebSocket events
@@ -12,7 +20,7 @@ const handleWebSocketConnection = async (event, context) => {
12
20
  // Handle new connection
13
21
  try {
14
22
  const connectionId = event.requestContext.connectionId;
15
- await websocketConnectionRepository.createConnection(
23
+ await getRepository().createConnection(
16
24
  connectionId
17
25
  );
18
26
  console.log(`Stored new connection: ${connectionId}`);
@@ -26,7 +34,7 @@ const handleWebSocketConnection = async (event, context) => {
26
34
  // Handle disconnection
27
35
  try {
28
36
  const connectionId = event.requestContext.connectionId;
29
- await websocketConnectionRepository.deleteConnection(
37
+ await getRepository().deleteConnection(
30
38
  connectionId
31
39
  );
32
40
  console.log(`Removed connection: ${connectionId}`);
@@ -2,26 +2,37 @@ const { createHandler } = require('@friggframework/core');
2
2
  const { loadAppDefinition } = require('../app-definition-loader');
3
3
  const { createQueueWorker } = require('../backend-utils');
4
4
 
5
- const handlers = {};
6
- const { integrations: integrationClasses } = loadAppDefinition();
5
+ let _handlers;
7
6
 
8
- integrationClasses.forEach((IntegrationClass) => {
9
- const defaultQueueWorker = createQueueWorker(IntegrationClass);
7
+ function ensureHandlers() {
8
+ if (!_handlers) {
9
+ _handlers = {};
10
+ const { integrations: integrationClasses } = loadAppDefinition();
10
11
 
11
- handlers[`${IntegrationClass.Definition.name}`] = {
12
- queueWorker: createHandler({
13
- eventName: `Queue Worker for ${IntegrationClass.Definition.name}`,
14
- isUserFacingResponse: false,
15
- method: async (event, context) => {
16
- const worker = new defaultQueueWorker();
17
- await worker.run(event, context);
18
- return {
19
- message: 'Successfully processed the Generic Queue Worker',
20
- input: event,
21
- };
22
- },
23
- }),
24
- };
25
- });
12
+ integrationClasses.forEach((IntegrationClass) => {
13
+ const defaultQueueWorker = createQueueWorker(IntegrationClass);
26
14
 
27
- module.exports = { handlers };
15
+ _handlers[`${IntegrationClass.Definition.name}`] = {
16
+ queueWorker: createHandler({
17
+ eventName: `Queue Worker for ${IntegrationClass.Definition.name}`,
18
+ isUserFacingResponse: false,
19
+ method: async (event, context) => {
20
+ const worker = new defaultQueueWorker();
21
+ await worker.run(event, context);
22
+ return {
23
+ message: 'Successfully processed the Generic Queue Worker',
24
+ input: event,
25
+ };
26
+ },
27
+ }),
28
+ };
29
+ });
30
+ }
31
+ return _handlers;
32
+ }
33
+
34
+ module.exports = {
35
+ get handlers() {
36
+ return ensureHandlers();
37
+ },
38
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/core",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0--canary.545.dba001a.0",
4
+ "version": "2.0.0--canary.545.c40eca4.0",
5
5
  "dependencies": {
6
6
  "@hapi/boom": "^10.0.1",
7
7
  "bcryptjs": "^2.4.3",
@@ -35,9 +35,9 @@
35
35
  }
36
36
  },
37
37
  "devDependencies": {
38
- "@friggframework/eslint-config": "2.0.0--canary.545.dba001a.0",
39
- "@friggframework/prettier-config": "2.0.0--canary.545.dba001a.0",
40
- "@friggframework/test": "2.0.0--canary.545.dba001a.0",
38
+ "@friggframework/eslint-config": "2.0.0--canary.545.c40eca4.0",
39
+ "@friggframework/prettier-config": "2.0.0--canary.545.c40eca4.0",
40
+ "@friggframework/test": "2.0.0--canary.545.c40eca4.0",
41
41
  "@prisma/client": "^6.17.0",
42
42
  "@types/lodash": "4.17.15",
43
43
  "@typescript-eslint/eslint-plugin": "^8.0.0",
@@ -78,5 +78,5 @@
78
78
  "publishConfig": {
79
79
  "access": "public"
80
80
  },
81
- "gitHead": "dba001affb699d55b0684fa236af8a5f67ddf852"
81
+ "gitHead": "c40eca49e72472f0499b24ce709a655a7503727b"
82
82
  }