@friggframework/devtools 2.0.0--canary.461.ec909cf.0 → 2.0.0--canary.461.9483dbe.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/frigg-cli/__tests__/unit/commands/build.test.js +6 -6
  2. package/frigg-cli/build-command/index.js +1 -1
  3. package/frigg-cli/deploy-command/index.js +6 -6
  4. package/frigg-cli/generate-command/index.js +2 -2
  5. package/frigg-cli/generate-iam-command.js +10 -10
  6. package/frigg-cli/start-command/index.js +1 -1
  7. package/frigg-cli/start-command/start-command.test.js +3 -3
  8. package/frigg-cli/utils/database-validator.js +14 -21
  9. package/infrastructure/REFACTOR.md +532 -0
  10. package/infrastructure/TRANSFORMATION-VISUAL.md +239 -0
  11. package/infrastructure/__tests__/postgres-config.test.js +1 -1
  12. package/infrastructure/create-frigg-infrastructure.js +1 -1
  13. package/infrastructure/{DEPLOYMENT-INSTRUCTIONS.md → docs/deployment-instructions.md} +3 -3
  14. package/infrastructure/{IAM-POLICY-TEMPLATES.md → docs/iam-policy-templates.md} +9 -10
  15. package/infrastructure/domains/database/aurora-discovery.js +81 -0
  16. package/infrastructure/domains/database/aurora-discovery.test.js +188 -0
  17. package/infrastructure/domains/integration/integration-builder.js +178 -0
  18. package/infrastructure/domains/integration/integration-builder.test.js +362 -0
  19. package/infrastructure/domains/integration/websocket-builder.js +69 -0
  20. package/infrastructure/domains/integration/websocket-builder.test.js +195 -0
  21. package/infrastructure/domains/networking/vpc-discovery.test.js +257 -0
  22. package/infrastructure/domains/parameters/ssm-builder.js +79 -0
  23. package/infrastructure/domains/parameters/ssm-builder.test.js +188 -0
  24. package/infrastructure/domains/parameters/ssm-discovery.js +84 -0
  25. package/infrastructure/domains/parameters/ssm-discovery.test.js +210 -0
  26. package/infrastructure/{iam-generator.js → domains/security/iam-generator.js} +2 -2
  27. package/infrastructure/domains/security/kms-builder.js +169 -0
  28. package/infrastructure/domains/security/kms-builder.test.js +354 -0
  29. package/infrastructure/domains/security/kms-discovery.js +80 -0
  30. package/infrastructure/domains/security/kms-discovery.test.js +176 -0
  31. package/infrastructure/domains/shared/base-builder.js +112 -0
  32. package/infrastructure/domains/shared/builder-orchestrator.js +212 -0
  33. package/infrastructure/domains/shared/builder-orchestrator.test.js +213 -0
  34. package/infrastructure/domains/shared/environment-builder.js +118 -0
  35. package/infrastructure/domains/shared/environment-builder.test.js +246 -0
  36. package/infrastructure/domains/shared/providers/aws-provider-adapter.test.js +366 -0
  37. package/infrastructure/domains/shared/providers/azure-provider-adapter.stub.js +93 -0
  38. package/infrastructure/domains/shared/providers/cloud-provider-adapter.js +136 -0
  39. package/infrastructure/domains/shared/providers/gcp-provider-adapter.stub.js +82 -0
  40. package/infrastructure/domains/shared/providers/provider-factory.js +108 -0
  41. package/infrastructure/domains/shared/providers/provider-factory.test.js +170 -0
  42. package/infrastructure/domains/shared/resource-discovery.js +132 -0
  43. package/infrastructure/domains/shared/resource-discovery.test.js +410 -0
  44. package/infrastructure/domains/shared/utilities/base-definition-factory.js +2 -3
  45. package/infrastructure/domains/shared/utilities/base-definition-factory.js.bak +338 -0
  46. package/infrastructure/domains/shared/utilities/base-definition-factory.test.js +248 -0
  47. package/infrastructure/domains/shared/utilities/handler-path-resolver.test.js +259 -0
  48. package/infrastructure/domains/shared/utilities/prisma-layer-manager.js +55 -0
  49. package/infrastructure/domains/shared/utilities/prisma-layer-manager.test.js +134 -0
  50. package/infrastructure/domains/shared/validation/env-validator.test.js +173 -0
  51. package/infrastructure/esbuild.config.js +53 -0
  52. package/infrastructure/infrastructure-composer.js +85 -0
  53. package/infrastructure/scripts/build-prisma-layer.js +60 -47
  54. package/infrastructure/{build-time-discovery.test.js → scripts/build-time-discovery.test.js} +5 -4
  55. package/layers/prisma/nodejs/package.json +8 -0
  56. package/management-ui/server/utils/environment/awsParameterStore.js +29 -18
  57. package/package.json +8 -8
  58. package/infrastructure/aws-discovery.js +0 -1704
  59. package/infrastructure/aws-discovery.test.js +0 -1666
  60. package/infrastructure/serverless-template.js +0 -2804
  61. package/infrastructure/serverless-template.test.js +0 -1897
  62. /package/infrastructure/{POSTGRES-CONFIGURATION.md → docs/POSTGRES-CONFIGURATION.md} +0 -0
  63. /package/infrastructure/{WEBSOCKET-CONFIGURATION.md → docs/WEBSOCKET-CONFIGURATION.md} +0 -0
  64. /package/infrastructure/{GENERATE-IAM-DOCS.md → docs/generate-iam-command.md} +0 -0
  65. /package/infrastructure/{iam-generator.test.js → domains/security/iam-generator.test.js} +0 -0
  66. /package/infrastructure/{frigg-deployment-iam-stack.yaml → domains/security/templates/frigg-deployment-iam-stack.yaml} +0 -0
  67. /package/infrastructure/{iam-policy-basic.json → domains/security/templates/iam-policy-basic.json} +0 -0
  68. /package/infrastructure/{iam-policy-full.json → domains/security/templates/iam-policy-full.json} +0 -0
  69. /package/infrastructure/{env-validator.js → domains/shared/validation/env-validator.js} +0 -0
  70. /package/infrastructure/{build-time-discovery.js → scripts/build-time-discovery.js} +0 -0
  71. /package/infrastructure/{run-discovery.js → scripts/run-discovery.js} +0 -0
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Integration Builder
3
+ *
4
+ * Domain Layer - Hexagonal Architecture
5
+ *
6
+ * Responsible for:
7
+ * - Creating SQS queues for each integration
8
+ * - Creating queue worker Lambda functions
9
+ * - Creating webhook handler functions
10
+ * - Configuring integration-specific routes and handlers
11
+ */
12
+
13
+ const { InfrastructureBuilder, ValidationResult } = require('../shared/base-builder');
14
+
15
+ class IntegrationBuilder extends InfrastructureBuilder {
16
+ constructor() {
17
+ super();
18
+ this.name = 'IntegrationBuilder';
19
+ }
20
+
21
+ shouldExecute(appDefinition) {
22
+ return Array.isArray(appDefinition.integrations) && appDefinition.integrations.length > 0;
23
+ }
24
+
25
+ validate(appDefinition) {
26
+ const result = new ValidationResult();
27
+
28
+ if (!appDefinition.integrations) {
29
+ return result; // Not an error, just no integrations
30
+ }
31
+
32
+ if (!Array.isArray(appDefinition.integrations)) {
33
+ result.addError('integrations must be an array');
34
+ return result;
35
+ }
36
+
37
+ // Validate each integration
38
+ appDefinition.integrations.forEach((integration, index) => {
39
+ if (!integration?.Definition?.name) {
40
+ result.addError(`Integration at index ${index} is missing Definition or name`);
41
+ }
42
+ });
43
+
44
+ return result;
45
+ }
46
+
47
+ /**
48
+ * Build integration infrastructure
49
+ */
50
+ async build(appDefinition, discoveredResources) {
51
+ console.log(`\n[${this.name}] Configuring integrations...`);
52
+ console.log(` Processing ${appDefinition.integrations.length} integrations...`);
53
+
54
+ const result = {
55
+ functions: {},
56
+ resources: {},
57
+ environment: {},
58
+ custom: {},
59
+ };
60
+
61
+ const functionPackageConfig = {
62
+ exclude: [
63
+ 'node_modules/aws-sdk/**',
64
+ 'node_modules/@aws-sdk/**',
65
+ 'node_modules/@prisma/**',
66
+ 'node_modules/.prisma/**',
67
+ 'node_modules/prisma/**',
68
+ 'node_modules/@friggframework/core/generated/**',
69
+
70
+ // Exclude development/test files
71
+ 'coverage/**',
72
+ 'test/**',
73
+ 'src/**',
74
+ 'layers/**',
75
+ '**/*.test.js',
76
+ '**/*.spec.js',
77
+ ],
78
+ };
79
+
80
+ for (const integration of appDefinition.integrations) {
81
+ const integrationName = integration.Definition.name;
82
+ const queueReference = `${integrationName.charAt(0).toUpperCase() + integrationName.slice(1)}Queue`;
83
+ const queueName = `\${self:service}--\${self:provider.stage}-${queueReference}`;
84
+
85
+ console.log(` Adding integration: ${integrationName}`);
86
+
87
+ // Create HTTP API handler for integration
88
+ result.functions[integrationName] = {
89
+ handler: `node_modules/@friggframework/core/handlers/routers/integration-defined-routers.handlers.${integrationName}.handler`,
90
+ skipEsbuild: true, // Nested exports in node_modules - skip esbuild bundling
91
+ package: functionPackageConfig,
92
+ events: [
93
+ {
94
+ httpApi: {
95
+ path: `/api/${integrationName}-integration/{proxy+}`,
96
+ method: 'ANY',
97
+ },
98
+ },
99
+ ],
100
+ };
101
+
102
+ // Create SQS Queue for integration
103
+ result.resources[queueReference] = {
104
+ Type: 'AWS::SQS::Queue',
105
+ Properties: {
106
+ QueueName: `\${self:custom.${queueReference}}`,
107
+ MessageRetentionPeriod: 60,
108
+ VisibilityTimeout: 1800,
109
+ RedrivePolicy: {
110
+ maxReceiveCount: 1,
111
+ deadLetterTargetArn: {
112
+ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
113
+ },
114
+ },
115
+ },
116
+ };
117
+
118
+ // Create Queue Worker function
119
+ const queueWorkerName = `${integrationName}QueueWorker`;
120
+ result.functions[queueWorkerName] = {
121
+ handler: `node_modules/@friggframework/core/handlers/workers/integration-defined-workers.handlers.${integrationName}.queueWorker`,
122
+ skipEsbuild: true, // Nested exports in node_modules - skip esbuild bundling
123
+ package: functionPackageConfig,
124
+ reservedConcurrency: 5,
125
+ events: [
126
+ {
127
+ sqs: {
128
+ arn: { 'Fn::GetAtt': [queueReference, 'Arn'] },
129
+ batchSize: 1,
130
+ },
131
+ },
132
+ ],
133
+ timeout: 900, // 15 minutes max for queue workers (Lambda maximum)
134
+ };
135
+
136
+ // Add queue URL to environment
137
+ result.environment[`${integrationName.toUpperCase()}_QUEUE_URL`] = {
138
+ Ref: queueReference,
139
+ };
140
+
141
+ result.custom[queueReference] = queueName;
142
+
143
+ // Add webhook handler if enabled
144
+ const webhookConfig = integration.Definition.webhooks;
145
+ if (webhookConfig && (webhookConfig === true || webhookConfig.enabled === true)) {
146
+ const webhookFunctionName = `${integrationName}Webhook`;
147
+
148
+ result.functions[webhookFunctionName] = {
149
+ handler: `node_modules/@friggframework/core/handlers/routers/integration-webhook-routers.handlers.${integrationName}Webhook.handler`,
150
+ skipEsbuild: true, // Nested exports in node_modules - skip esbuild bundling
151
+ events: [
152
+ {
153
+ httpApi: {
154
+ path: `/api/${integrationName}-integration/webhooks`,
155
+ method: 'POST',
156
+ },
157
+ },
158
+ {
159
+ httpApi: {
160
+ path: `/api/${integrationName}-integration/webhooks/{integrationId}`,
161
+ method: 'POST',
162
+ },
163
+ },
164
+ ],
165
+ };
166
+ console.log(` + Webhook handler enabled`);
167
+ }
168
+ }
169
+
170
+ console.log(` ✅ Configured ${appDefinition.integrations.length} integrations`);
171
+ console.log(`[${this.name}] ✅ Integration configuration completed`);
172
+
173
+ return result;
174
+ }
175
+ }
176
+
177
+ module.exports = { IntegrationBuilder };
178
+
@@ -0,0 +1,362 @@
1
+ /**
2
+ * Tests for Integration Builder
3
+ *
4
+ * Tests integration-specific Lambda functions and SQS queues
5
+ */
6
+
7
+ const { IntegrationBuilder } = require('./integration-builder');
8
+ const { ValidationResult } = require('../shared/base-builder');
9
+
10
+ describe('IntegrationBuilder', () => {
11
+ let integrationBuilder;
12
+
13
+ beforeEach(() => {
14
+ integrationBuilder = new IntegrationBuilder();
15
+ });
16
+
17
+ describe('shouldExecute()', () => {
18
+ it('should return true when integrations array has items', () => {
19
+ const appDefinition = {
20
+ integrations: [
21
+ { Definition: { name: 'test' } },
22
+ ],
23
+ };
24
+
25
+ expect(integrationBuilder.shouldExecute(appDefinition)).toBe(true);
26
+ });
27
+
28
+ it('should return false when integrations array is empty', () => {
29
+ const appDefinition = {
30
+ integrations: [],
31
+ };
32
+
33
+ expect(integrationBuilder.shouldExecute(appDefinition)).toBe(false);
34
+ });
35
+
36
+ it('should return false when integrations is not defined', () => {
37
+ const appDefinition = {};
38
+
39
+ expect(integrationBuilder.shouldExecute(appDefinition)).toBe(false);
40
+ });
41
+
42
+ it('should return false when integrations is not an array', () => {
43
+ const appDefinition = {
44
+ integrations: { name: 'test' },
45
+ };
46
+
47
+ expect(integrationBuilder.shouldExecute(appDefinition)).toBe(false);
48
+ });
49
+ });
50
+
51
+ describe('validate()', () => {
52
+ it('should pass validation for valid integrations', () => {
53
+ const appDefinition = {
54
+ integrations: [
55
+ { Definition: { name: 'hubspot' } },
56
+ { Definition: { name: 'salesforce' } },
57
+ ],
58
+ };
59
+
60
+ const result = integrationBuilder.validate(appDefinition);
61
+
62
+ expect(result).toBeInstanceOf(ValidationResult);
63
+ expect(result.valid).toBe(true);
64
+ expect(result.errors).toEqual([]);
65
+ });
66
+
67
+ it('should pass when integrations is undefined', () => {
68
+ const appDefinition = {};
69
+
70
+ const result = integrationBuilder.validate(appDefinition);
71
+
72
+ expect(result.valid).toBe(true);
73
+ });
74
+
75
+ it('should error when integrations is not an array', () => {
76
+ const appDefinition = {
77
+ integrations: 'invalid',
78
+ };
79
+
80
+ const result = integrationBuilder.validate(appDefinition);
81
+
82
+ expect(result.valid).toBe(false);
83
+ expect(result.errors).toContain('integrations must be an array');
84
+ });
85
+
86
+ it('should error when integration is missing Definition', () => {
87
+ const appDefinition = {
88
+ integrations: [
89
+ { someOtherField: 'value' },
90
+ ],
91
+ };
92
+
93
+ const result = integrationBuilder.validate(appDefinition);
94
+
95
+ expect(result.valid).toBe(false);
96
+ expect(result.errors).toContain(
97
+ 'Integration at index 0 is missing Definition or name'
98
+ );
99
+ });
100
+
101
+ it('should error when integration Definition is missing name', () => {
102
+ const appDefinition = {
103
+ integrations: [
104
+ { Definition: {} },
105
+ ],
106
+ };
107
+
108
+ const result = integrationBuilder.validate(appDefinition);
109
+
110
+ expect(result.valid).toBe(false);
111
+ expect(result.errors).toContain(
112
+ 'Integration at index 0 is missing Definition or name'
113
+ );
114
+ });
115
+
116
+ it('should validate all integrations', () => {
117
+ const appDefinition = {
118
+ integrations: [
119
+ { Definition: { name: 'valid' } },
120
+ { Definition: {} }, // Invalid - no name
121
+ { someField: 'value' }, // Invalid - no Definition
122
+ ],
123
+ };
124
+
125
+ const result = integrationBuilder.validate(appDefinition);
126
+
127
+ expect(result.valid).toBe(false);
128
+ expect(result.errors).toHaveLength(2);
129
+ });
130
+ });
131
+
132
+ describe('build()', () => {
133
+ it('should create HTTP handler for integration', async () => {
134
+ const appDefinition = {
135
+ integrations: [
136
+ { Definition: { name: 'hubspot' } },
137
+ ],
138
+ };
139
+
140
+ const result = await integrationBuilder.build(appDefinition, {});
141
+
142
+ expect(result.functions.hubspot).toBeDefined();
143
+ expect(result.functions.hubspot.handler).toBe(
144
+ 'node_modules/@friggframework/core/handlers/routers/integration-defined-routers.handlers.hubspot.handler'
145
+ );
146
+ });
147
+
148
+ it('should configure HTTP API event for integration', async () => {
149
+ const appDefinition = {
150
+ integrations: [
151
+ { Definition: { name: 'salesforce' } },
152
+ ],
153
+ };
154
+
155
+ const result = await integrationBuilder.build(appDefinition, {});
156
+
157
+ expect(result.functions.salesforce.events).toEqual([
158
+ {
159
+ httpApi: {
160
+ path: '/api/salesforce-integration/{proxy+}',
161
+ method: 'ANY',
162
+ },
163
+ },
164
+ ]);
165
+ });
166
+
167
+ it('should create SQS queue for integration', async () => {
168
+ const appDefinition = {
169
+ integrations: [
170
+ { Definition: { name: 'slack' } },
171
+ ],
172
+ };
173
+
174
+ const result = await integrationBuilder.build(appDefinition, {});
175
+
176
+ expect(result.resources.SlackQueue).toBeDefined();
177
+ expect(result.resources.SlackQueue.Type).toBe('AWS::SQS::Queue');
178
+ });
179
+
180
+ it('should configure queue with correct retention and visibility timeout', async () => {
181
+ const appDefinition = {
182
+ integrations: [
183
+ { Definition: { name: 'test' } },
184
+ ],
185
+ };
186
+
187
+ const result = await integrationBuilder.build(appDefinition, {});
188
+
189
+ expect(result.resources.TestQueue.Properties.MessageRetentionPeriod).toBe(60);
190
+ expect(result.resources.TestQueue.Properties.VisibilityTimeout).toBe(1800);
191
+ });
192
+
193
+ it('should configure redrive policy to internal error queue', async () => {
194
+ const appDefinition = {
195
+ integrations: [
196
+ { Definition: { name: 'test' } },
197
+ ],
198
+ };
199
+
200
+ const result = await integrationBuilder.build(appDefinition, {});
201
+
202
+ expect(result.resources.TestQueue.Properties.RedrivePolicy).toEqual({
203
+ maxReceiveCount: 1,
204
+ deadLetterTargetArn: {
205
+ 'Fn::GetAtt': ['InternalErrorQueue', 'Arn'],
206
+ },
207
+ });
208
+ });
209
+
210
+ it('should create queue worker function', async () => {
211
+ const appDefinition = {
212
+ integrations: [
213
+ { Definition: { name: 'hubspot' } },
214
+ ],
215
+ };
216
+
217
+ const result = await integrationBuilder.build(appDefinition, {});
218
+
219
+ expect(result.functions.hubspotQueueWorker).toBeDefined();
220
+ });
221
+
222
+ it('should configure queue worker with SQS event', async () => {
223
+ const appDefinition = {
224
+ integrations: [
225
+ { Definition: { name: 'test' } },
226
+ ],
227
+ };
228
+
229
+ const result = await integrationBuilder.build(appDefinition, {});
230
+
231
+ expect(result.functions.testQueueWorker.events).toEqual([
232
+ {
233
+ sqs: {
234
+ arn: { 'Fn::GetAtt': ['TestQueue', 'Arn'] },
235
+ batchSize: 1,
236
+ },
237
+ },
238
+ ]);
239
+ });
240
+
241
+ it('should set queue worker timeout to 600 seconds', async () => {
242
+ const appDefinition = {
243
+ integrations: [
244
+ { Definition: { name: 'test' } },
245
+ ],
246
+ };
247
+
248
+ const result = await integrationBuilder.build(appDefinition, {});
249
+
250
+ expect(result.functions.testQueueWorker.timeout).toBe(600);
251
+ });
252
+
253
+ it('should set queue worker reserved concurrency', async () => {
254
+ const appDefinition = {
255
+ integrations: [
256
+ { Definition: { name: 'test' } },
257
+ ],
258
+ };
259
+
260
+ const result = await integrationBuilder.build(appDefinition, {});
261
+
262
+ expect(result.functions.testQueueWorker.reservedConcurrency).toBe(5);
263
+ });
264
+
265
+ it('should add queue URL to environment variables', async () => {
266
+ const appDefinition = {
267
+ integrations: [
268
+ { Definition: { name: 'slack' } },
269
+ ],
270
+ };
271
+
272
+ const result = await integrationBuilder.build(appDefinition, {});
273
+
274
+ expect(result.environment.SLACK_QUEUE_URL).toEqual({
275
+ Ref: 'SlackQueue',
276
+ });
277
+ });
278
+
279
+ it('should add queue name to custom variables', async () => {
280
+ const appDefinition = {
281
+ integrations: [
282
+ { Definition: { name: 'stripe' } },
283
+ ],
284
+ };
285
+
286
+ const result = await integrationBuilder.build(appDefinition, {});
287
+
288
+ expect(result.custom.StripeQueue).toBe('${self:service}--${self:provider.stage}-StripeQueue');
289
+ });
290
+
291
+ it('should handle multiple integrations', async () => {
292
+ const appDefinition = {
293
+ integrations: [
294
+ { Definition: { name: 'hubspot' } },
295
+ { Definition: { name: 'salesforce' } },
296
+ { Definition: { name: 'slack' } },
297
+ ],
298
+ };
299
+
300
+ const result = await integrationBuilder.build(appDefinition, {});
301
+
302
+ // Check functions
303
+ expect(result.functions.hubspot).toBeDefined();
304
+ expect(result.functions.salesforce).toBeDefined();
305
+ expect(result.functions.slack).toBeDefined();
306
+ expect(result.functions.hubspotQueueWorker).toBeDefined();
307
+ expect(result.functions.salesforceQueueWorker).toBeDefined();
308
+ expect(result.functions.slackQueueWorker).toBeDefined();
309
+
310
+ // Check queues
311
+ expect(result.resources.HubspotQueue).toBeDefined();
312
+ expect(result.resources.SalesforceQueue).toBeDefined();
313
+ expect(result.resources.SlackQueue).toBeDefined();
314
+
315
+ // Check environment vars
316
+ expect(result.environment.HUBSPOT_QUEUE_URL).toBeDefined();
317
+ expect(result.environment.SALESFORCE_QUEUE_URL).toBeDefined();
318
+ expect(result.environment.SLACK_QUEUE_URL).toBeDefined();
319
+ });
320
+
321
+ it('should capitalize integration name for queue reference', async () => {
322
+ const appDefinition = {
323
+ integrations: [
324
+ { Definition: { name: 'myIntegration' } },
325
+ ],
326
+ };
327
+
328
+ const result = await integrationBuilder.build(appDefinition, {});
329
+
330
+ // Queue name should start with capital letter
331
+ expect(result.resources.MyIntegrationQueue).toBeDefined();
332
+ });
333
+
334
+ it('should handle integration names with hyphens', async () => {
335
+ const appDefinition = {
336
+ integrations: [
337
+ { Definition: { name: 'my-integration' } },
338
+ ],
339
+ };
340
+
341
+ const result = await integrationBuilder.build(appDefinition, {});
342
+
343
+ expect(result.functions['my-integration']).toBeDefined();
344
+ expect(result.functions['my-integrationQueueWorker']).toBeDefined();
345
+ });
346
+ });
347
+
348
+ describe('getDependencies()', () => {
349
+ it('should have no dependencies', () => {
350
+ const deps = integrationBuilder.getDependencies();
351
+
352
+ expect(deps).toEqual([]);
353
+ });
354
+ });
355
+
356
+ describe('getName()', () => {
357
+ it('should return IntegrationBuilder', () => {
358
+ expect(integrationBuilder.getName()).toBe('IntegrationBuilder');
359
+ });
360
+ });
361
+ });
362
+
@@ -0,0 +1,69 @@
1
+ /**
2
+ * WebSocket Builder
3
+ *
4
+ * Domain Layer - Hexagonal Architecture
5
+ *
6
+ * Responsible for:
7
+ * - WebSocket API Gateway configuration
8
+ * - WebSocket route handlers ($connect, $default, $disconnect)
9
+ * - WebSocket function definitions
10
+ */
11
+
12
+ const { InfrastructureBuilder, ValidationResult } = require('../shared/base-builder');
13
+
14
+ class WebsocketBuilder extends InfrastructureBuilder {
15
+ constructor() {
16
+ super();
17
+ this.name = 'WebsocketBuilder';
18
+ }
19
+
20
+ shouldExecute(appDefinition) {
21
+ // Skip WebSocket in local mode (when FRIGG_SKIP_AWS_DISCOVERY is set)
22
+ // API Gateway WebSocket is an AWS-specific service that should only be created in production
23
+ if (process.env.FRIGG_SKIP_AWS_DISCOVERY === 'true') {
24
+ return false;
25
+ }
26
+
27
+ return appDefinition.websockets?.enable === true;
28
+ }
29
+
30
+ validate(appDefinition) {
31
+ const result = new ValidationResult();
32
+
33
+ if (!appDefinition.websockets) {
34
+ result.addError('WebSocket configuration is missing');
35
+ return result;
36
+ }
37
+
38
+ return result;
39
+ }
40
+
41
+ /**
42
+ * Build WebSocket infrastructure
43
+ */
44
+ async build(appDefinition, discoveredResources) {
45
+ console.log(`\n[${this.name}] Configuring WebSocket API Gateway...`);
46
+
47
+ const result = {
48
+ functions: {},
49
+ };
50
+
51
+ // Create default WebSocket handler
52
+ result.functions.defaultWebsocket = {
53
+ handler: 'node_modules/@friggframework/core/handlers/routers/websocket.handler',
54
+ events: [
55
+ { websocket: { route: '$connect' } },
56
+ { websocket: { route: '$default' } },
57
+ { websocket: { route: '$disconnect' } },
58
+ ],
59
+ };
60
+
61
+ console.log(' ✅ WebSocket functions configured ($connect, $default, $disconnect)');
62
+ console.log(`[${this.name}] ✅ WebSocket configuration completed`);
63
+
64
+ return result;
65
+ }
66
+ }
67
+
68
+ module.exports = { WebsocketBuilder };
69
+