@friggframework/devtools 2.0.0-next.87 → 2.0.0-next.88

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.
@@ -347,13 +347,46 @@ class IntegrationBuilder extends InfrastructureBuilder {
347
347
  console.log(` ✓ Webhook handler function defined`);
348
348
  }
349
349
 
350
- // Create HTTP API handler for integration (catch-all route AFTER webhooks)
350
+ // Tier 3 Integration Extension routes (e.g. hubspot.extensions.webhooks).
351
+ // The integration-defined-routers handler already mounts these at
352
+ // runtime; emitting explicit httpApi events here registers them as
353
+ // dedicated routes (visible at startup, and matched ahead of the
354
+ // catch-all) instead of silently relying on {proxy+}. They point at
355
+ // the same handler as the catch-all. Route shape mirrors core's
356
+ // getExtensionRoutes; we read it directly to avoid coupling the
357
+ // build-time generator to a core runtime import.
358
+ const extensionBindings = Object.values(
359
+ integration.Definition.extensions || {}
360
+ );
361
+ const extensionRouteEvents = [];
362
+ for (const binding of extensionBindings) {
363
+ const routes =
364
+ (binding && binding.extension && binding.extension.routes) ||
365
+ [];
366
+ for (const route of routes) {
367
+ extensionRouteEvents.push({
368
+ httpApi: {
369
+ path: `/api/${integrationName}-integration${route.path}`,
370
+ method: route.method,
371
+ },
372
+ });
373
+ }
374
+ }
375
+ if (extensionRouteEvents.length > 0) {
376
+ console.log(
377
+ ` ✓ ${extensionRouteEvents.length} extension route(s) defined`
378
+ );
379
+ }
380
+
381
+ // Create HTTP API handler for integration (catch-all route AFTER
382
+ // webhooks and extension routes)
351
383
  result.functions[integrationName] = {
352
384
  handler: `node_modules/@friggframework/core/handlers/routers/integration-defined-routers.handlers.${integrationName}.handler`,
353
385
  skipEsbuild: true, // Nested exports in node_modules - skip esbuild bundling
354
386
  package: functionPackageConfig,
355
387
  ...(usePrismaLayer && { layers: [{ Ref: 'PrismaLambdaLayer' }] }), // HTTP handlers need Prisma for integration queries
356
388
  events: [
389
+ ...extensionRouteEvents,
357
390
  {
358
391
  httpApi: {
359
392
  path: `/api/${integrationName}-integration/{proxy+}`,
@@ -568,6 +568,75 @@ describe('IntegrationBuilder', () => {
568
568
  ]);
569
569
  });
570
570
 
571
+ it('should emit dedicated httpApi events for Tier 3 extension routes, before the catch-all', async () => {
572
+ const appDefinition = {
573
+ integrations: [
574
+ {
575
+ Definition: {
576
+ name: 'hubspot',
577
+ extensions: {
578
+ hubspotWebhooks: {
579
+ extension: {
580
+ name: 'hubspot-webhooks',
581
+ routes: [
582
+ {
583
+ path: '/webhooks',
584
+ method: 'POST',
585
+ event: 'HUBSPOT_WEBHOOK_RECEIVED',
586
+ },
587
+ ],
588
+ events: {
589
+ HUBSPOT_WEBHOOK_RECEIVED: {
590
+ type: 'LIFE_CYCLE_EVENT',
591
+ handler: () => {},
592
+ },
593
+ },
594
+ },
595
+ handlers: {},
596
+ },
597
+ },
598
+ },
599
+ },
600
+ ],
601
+ };
602
+
603
+ const result = await integrationBuilder.build(appDefinition, {});
604
+
605
+ // The extension route is registered on the same handler as the
606
+ // catch-all, and ordered before {proxy+}.
607
+ expect(result.functions.hubspot.events).toEqual([
608
+ {
609
+ httpApi: {
610
+ path: '/api/hubspot-integration/webhooks',
611
+ method: 'POST',
612
+ },
613
+ },
614
+ {
615
+ httpApi: {
616
+ path: '/api/hubspot-integration/{proxy+}',
617
+ method: 'ANY',
618
+ },
619
+ },
620
+ ]);
621
+ });
622
+
623
+ it('should only have the catch-all proxy route when no extensions are declared', async () => {
624
+ const appDefinition = {
625
+ integrations: [{ Definition: { name: 'plain' } }],
626
+ };
627
+
628
+ const result = await integrationBuilder.build(appDefinition, {});
629
+
630
+ expect(result.functions.plain.events).toEqual([
631
+ {
632
+ httpApi: {
633
+ path: '/api/plain-integration/{proxy+}',
634
+ method: 'ANY',
635
+ },
636
+ },
637
+ ]);
638
+ });
639
+
571
640
  it('should define webhook handler BEFORE catch-all proxy route (ordering bug fix)', async () => {
572
641
  const appDefinition = {
573
642
  integrations: [
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@friggframework/devtools",
3
3
  "prettier": "@friggframework/prettier-config",
4
- "version": "2.0.0-next.87",
4
+ "version": "2.0.0-next.88",
5
5
  "bin": {
6
6
  "frigg": "./frigg-cli/index.js"
7
7
  },
@@ -25,9 +25,9 @@
25
25
  "@babel/eslint-parser": "^7.18.9",
26
26
  "@babel/parser": "^7.25.3",
27
27
  "@babel/traverse": "^7.25.3",
28
- "@friggframework/core": "2.0.0-next.87",
29
- "@friggframework/schemas": "2.0.0-next.87",
30
- "@friggframework/test": "2.0.0-next.87",
28
+ "@friggframework/core": "2.0.0-next.88",
29
+ "@friggframework/schemas": "2.0.0-next.88",
30
+ "@friggframework/test": "2.0.0-next.88",
31
31
  "@hapi/boom": "^10.0.1",
32
32
  "@inquirer/prompts": "^5.3.8",
33
33
  "axios": "^1.7.2",
@@ -55,8 +55,8 @@
55
55
  "validate-npm-package-name": "^5.0.0"
56
56
  },
57
57
  "devDependencies": {
58
- "@friggframework/eslint-config": "2.0.0-next.87",
59
- "@friggframework/prettier-config": "2.0.0-next.87",
58
+ "@friggframework/eslint-config": "2.0.0-next.88",
59
+ "@friggframework/prettier-config": "2.0.0-next.88",
60
60
  "aws-sdk-client-mock": "^4.1.0",
61
61
  "aws-sdk-client-mock-jest": "^4.1.0",
62
62
  "jest": "^30.1.3",
@@ -88,5 +88,5 @@
88
88
  "publishConfig": {
89
89
  "access": "public"
90
90
  },
91
- "gitHead": "ee72b6f5349ee764da595f67cc6f184a42f763f1"
91
+ "gitHead": "613b1c6f878086fadaf5a400c4227e4911a06d3e"
92
92
  }