Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
188110e
feat: add Netlify adapter and queue provider abstraction for multi-pl…
claude Feb 28, 2026
9513a42
feat: complete Netlify adapter with split functions, scheduler, DB su…
claude Feb 28, 2026
992ea0c
docs: add provider plugin architecture plan
claude Feb 28, 2026
2427eba
docs: add Vercel, GCP, and Docker/local provider specs to plan
claude Feb 28, 2026
7dc47e9
docs: add deploy story, difficulty assessment, and tier 3 analysis
claude Feb 28, 2026
35013d9
feat: complete Netlify provider to full plugin interface
claude Mar 1, 2026
9c6a13d
refactor: rename @friggframework/netlify-adapter to @friggframework/p…
claude Mar 1, 2026
0430c59
fix: address PR review feedback on deploy.js and scheduled-sync.js
claude Mar 1, 2026
72e5463
fix: use netlify-background queue provider in worker-background.js
claude Mar 1, 2026
88cf983
feat: add provider resolver to bridge appDefinition.provider to provi…
claude Mar 1, 2026
c870571
feat: wire CLI commands to use appDefinition.provider for provider di…
claude Mar 1, 2026
7b93757
test: add provider dispatch tests for CLI commands
claude Mar 1, 2026
a2a8f01
fix(scheduler): prevent duplicate dispatch via PROCESSING state guard
claude Mar 2, 2026
97831cd
docs: add ADR for multi-provider support architecture
claude Mar 2, 2026
1176a00
fix(scheduler): lazy-load adapters to avoid eager AWS SDK import
claude Mar 3, 2026
302ab9b
fix: route non-AWS providers through provider build in infrastructure.js
claude Mar 3, 2026
9237e31
refactor: extract AWS adapters to provider-aws, move provider-netlify…
claude Mar 3, 2026
023b098
refactor: introduce hexagonal architecture interfaces for AWS decoupling
claude Mar 3, 2026
5949712
refactor!: remove lazy-loading backward compat, require explicit adap…
claude Mar 3, 2026
3d89eac
fix: address review findings from AWS decoupling branch review
claude Mar 3, 2026
21381ca
fix(provider-netlify): move @friggframework/core to peerDependencies
claude Mar 3, 2026
3de4267
chore: update package-lock.json after provider-netlify dep change
claude Mar 3, 2026
079aa2d
fix(providers): use wildcard peer dep for @friggframework/core
claude Mar 3, 2026
29ef032
fix(core): remove provider-aws from core's peerDependencies
claude Mar 3, 2026
a8d08b4
refactor(core): eliminate hardcoded require('@friggframework/provider…
claude Mar 3, 2026
74db90f
refactor(core): remove mongoose dependency, use Prisma and mongodb dr…
d-klotz Mar 3, 2026
e34b741
chore(deps): remove lodash.get, upgrade supertest to v7
claude Mar 3, 2026
077502a
fix(provider-netlify): generate lib/ shims alongside function entry p…
claude Mar 3, 2026
f95ba19
fix(core): address code review findings from PR #546
d-klotz Mar 3, 2026
3dadab6
fix(provider-netlify): fix ONGOING_SYNC detection and webhook route r…
claude Mar 4, 2026
2eed07a
fix(provider-netlify): add @friggframework packages to esbuild externals
claude Mar 4, 2026
8642b58
refactor(core): remove unused expectShallowEqualDbObject
d-klotz Mar 5, 2026
ce82650
refactor(tests): remove password encryption tests
d-klotz Mar 5, 2026
45e06e5
refactor(core): remove unused Entity import
d-klotz Mar 5, 2026
c17a357
feat(core): add AWS SDK client-scheduler dependency
d-klotz Mar 5, 2026
a987d94
Merge pull request #546 from friggframework/refactor/remove-mongoose-…
d-klotz Mar 5, 2026
e7becd9
feat(core, provider-netlify): make app definition statically traceabl…
claude Mar 6, 2026
5ec2975
fix(core): lazy-load MongoDB/DocumentDB implementations in all reposi…
claude Mar 6, 2026
8db2e0d
fix(core): lazy-load Entity (Mongoose model) and DocumentDB implement…
claude Mar 6, 2026
f995374
chore: remove stale build artifact
claude Mar 6, 2026
17e3f18
chore: remove stale prisma layer build artifact
claude Mar 6, 2026
c459392
chore: remove stale prisma layer build artifact
claude Mar 6, 2026
4f3ba52
refactor(core): replace mongodb ObjectId with bson
d-klotz Mar 6, 2026
f9c9ccc
Merge branch 'next' into refactor/remove-mongoose-dependency
d-klotz Mar 6, 2026
67ebb53
refactor(core): remove unused AWS SDK client-scheduler dependency
d-klotz Mar 6, 2026
aac4f40
Merge pull request #547 from friggframework/refactor/remove-mongoose-…
d-klotz Mar 6, 2026
0c9482d
fix(infra): self-heal VPC subnet-route table association drift
d-klotz Mar 6, 2026
1a22160
fix(infra): improve self-healing for VPC subnet associations
d-klotz Mar 6, 2026
90eefb0
test(infra): add self-heal tests for VPC subnet associations
d-klotz Mar 6, 2026
1435f81
test(infra): enhance fallback path tests for VPC subnets
d-klotz Mar 6, 2026
c8ae0ca
style(tests): format test cases for better readability
d-klotz Mar 6, 2026
9740ac8
Merge pull request #548 from friggframework/fix/vpc-subnet-route-tabl…
d-klotz Mar 6, 2026
9660c3e
merge: pull from next branch and resolve all conflicts
claude Mar 7, 2026
d336d2a
fix(core): route backend discovery through loadAppDefinition() for Ne…
claude Mar 9, 2026
2b4f642
chore: sync package-lock.json
claude Mar 10, 2026
b75e1e2
fix: regenerate package-lock.json with missing encoding and iconv-lit…
claude Mar 10, 2026
cd04c85
fix: regenerate package-lock.json from next branch base
claude Mar 10, 2026
dba001a
fix: regenerate package-lock.json with npm 11 for CI compatibility
claude Mar 10, 2026
c40eca4
feat(core): lazy router initialization to eliminate require-time side…
claude Mar 12, 2026
60108f0
fix(core,netlify): resolve Prisma client loading on Netlify deployments
claude Mar 13, 2026
ae2019f
fix(core,netlify): reduce Netlify function bundle size
claude Mar 13, 2026
676b219
fix(core): remove redundant debian-openssl binaryTarget from Prisma s…
claude Mar 13, 2026
69acce5
feat(core): add extension system for DB-backed OAuth credentials
claude Mar 20, 2026
4f9d12a
feat(schemas,devtools): add extensions to app definition schema and v…
claude Mar 20, 2026
ccb5010
feat(extensions): add @friggframework/extension-db-credentials package
claude Mar 20, 2026
6f62ce6
fix: add packages/extensions/* to workspaces and lerna packages
claude Mar 20, 2026
5a3df8f
fix(db-credentials): allow prerelease versions of @friggframework/core
claude Mar 20, 2026
e256e95
fix: regenerate package-lock.json to include db-credentials workspace
claude Mar 20, 2026
b4ca16d
fix(db-credentials): add publishConfig and repo metadata for npm publish
claude Mar 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
248 changes: 248 additions & 0 deletions docs/architecture-decisions/010-decouple-aws-from-core.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
# ADR-010: Decouple AWS SDK Dependencies from @friggframework/core

**Status**: Accepted
**Date**: 2026-03-03
**Deciders**: Sean, Frigg Team

## Context

`@friggframework/core` directly imported multiple AWS SDK v3 packages:

- `@aws-sdk/client-sqs` — in `Worker.js`, `queuer-util.js`
- `@aws-sdk/client-kms` — in `Cryptor.js`
- `@aws-sdk/client-apigatewaymanagementapi` — in WebSocket connection repositories and the Mongoose `WebsocketConnection` model
- AWS-specific health check logic (VPC detection, KMS capability) — in `health.js`

This tight coupling meant that **any** consumer of `@friggframework/core` pulled in all AWS SDKs at install time, even if deploying to a non-AWS platform (e.g., Netlify, Vercel, or Docker on GCP/Azure). Bundle size on Netlify was unnecessarily large, and esbuild would fail or produce warnings when tree-shaking unused AWS SDK code.

The recent DDD/hexagonal architecture work established clean layer boundaries (handlers → use cases → repositories), but the infrastructure layer itself was still AWS-native throughout.

## Decision

**Extract all AWS SDK dependencies from `@friggframework/core` into `@friggframework/provider-aws`.**

This is a **breaking change**. Instead of using lazy-loading with backward-compatible auto-discovery of AWS adapters, we require consumers to explicitly install `@friggframework/provider-aws` and inject the appropriate adapters via constructor options or setter methods.

### Why breaking change instead of backward compatibility?

1. **Explicit is better than implicit** — Lazy-loading hid a hard dependency behind a `require()` call that would fail at runtime with a confusing error if `@friggframework/provider-aws` wasn't installed. An explicit constructor error at initialization time is clearer.

2. **No phantom dependencies** — With lazy-loading, `@friggframework/core` still had a runtime dependency on `@friggframework/provider-aws` for AWS users, but this wasn't declared in `package.json`. Forgetting to install it would cause cryptic failures deep in the call stack.

3. **Clean architecture** — Hexagonal architecture requires that adapters are explicitly wired at the composition root (app startup), not auto-discovered at call time. Lazy-loading violated this principle.

4. **Bundle safety** — Even with lazy `require()`, some bundlers (esbuild, webpack) will follow the require path and include the AWS SDK in the bundle. Removing the `require()` entirely guarantees zero AWS SDK code in non-AWS bundles.

### Architecture

```
@friggframework/core (ports/interfaces)
├── QueueClientInterface — port for queue messaging
├── EncryptionKeyProviderInterface — port for envelope encryption keys
├── WebSocketMessageSenderInterface — port for WebSocket message sending
├── AesEncryptionKeyProvider — built-in AES adapter (no AWS)
└── StaleConnectionError — shared error type

@friggframework/provider-aws (adapters)
├── SqsQueueClient — implements QueueClientInterface
├── KmsEncryptionKeyProvider — implements EncryptionKeyProviderInterface
├── ApiGatewayMessageSender — implements WebSocketMessageSenderInterface
├── EventBridgeSchedulerAdapter — scheduler adapter
└── health/kms-health-check — KMS + VPC health checks

@friggframework/provider-netlify (adapters)
├── NetlifyBackgroundProvider — implements QueueProvider
└── QStashQueueProvider — implements QueueProvider
```

## Migration Guide

### 1. Install the AWS provider package

```bash
npm install @friggframework/provider-aws
```

### 2. Worker — inject queueClient

**Before (v2):**
```javascript
const { Worker } = require('@friggframework/core');

class MyWorker extends Worker {
// queueClient was auto-loaded from SQS
}
const worker = new MyWorker();
```

**After (v3):**
```javascript
const { Worker } = require('@friggframework/core');
const { SqsQueueClient } = require('@friggframework/provider-aws');

class MyWorker extends Worker {
// Same _run() implementation — no changes needed
}
const worker = new MyWorker({ queueClient: new SqsQueueClient() });
```

### 3. Cryptor — inject keyProvider for KMS

**Before (v2):**
```javascript
const { Cryptor } = require('@friggframework/core');
const cryptor = new Cryptor({ shouldUseAws: true });
```

**After (v3):**
```javascript
const { Cryptor } = require('@friggframework/core');
const { KmsEncryptionKeyProvider } = require('@friggframework/provider-aws');

const cryptor = new Cryptor({
shouldUseAws: true,
keyProvider: new KmsEncryptionKeyProvider(),
});
```

**AES mode (unchanged):**
```javascript
// AES mode still works without provider-aws — no changes needed
const cryptor = new Cryptor({ shouldUseAws: false });
```

### 4. QueuerUtil — call setQueueClient() at startup

**Before (v2):**
```javascript
const { QueuerUtil } = require('@friggframework/core');
await QueuerUtil.send(message, queueUrl); // auto-loaded SQS
```

**After (v3):**
```javascript
const { QueuerUtil } = require('@friggframework/core');
const { SqsQueueClient } = require('@friggframework/provider-aws');

// At application startup:
QueuerUtil.setQueueClient(new SqsQueueClient());

// Then use as before:
await QueuerUtil.send(message, queueUrl);
```

### 5. WebSocket Connection Repositories — inject messageSender

**Before (v2):**
```javascript
const repo = new WebsocketConnectionRepository(prisma);
// messageSender was auto-loaded from API Gateway
```

**After (v3):**
```javascript
const { ApiGatewayMessageSender } = require('@friggframework/provider-aws');

const repo = new WebsocketConnectionRepository(
prisma,
new ApiGatewayMessageSender()
);
```

### 6. WebsocketConnection Mongoose model — call setMessageSender()

**Before (v2):**
```javascript
const { WebsocketConnection } = require('@friggframework/core');
const connections = await WebsocketConnection.getActiveConnections();
// messageSender was auto-loaded from API Gateway
```

**After (v3):**
```javascript
const { WebsocketConnection } = require('@friggframework/core');
const { ApiGatewayMessageSender } = require('@friggframework/provider-aws');

// At application startup:
WebsocketConnection.setMessageSender(new ApiGatewayMessageSender());

// Then use as before:
const connections = await WebsocketConnection.getActiveConnections();
```

### 7. Health checks — no changes needed

The health router (`health.js`) uses a try/catch around `require('@friggframework/provider-aws')`. If the package is installed, AWS health checks (KMS, VPC) run as before. If not, they return `{ status: 'skipped' }`. No migration needed.

### 8. Recommended: wire adapters at the composition root

For clean architecture, wire all adapters at your application's entry point:

```javascript
// app.js or handler.js — composition root
const { SqsQueueClient, KmsEncryptionKeyProvider, ApiGatewayMessageSender } =
require('@friggframework/provider-aws');
const { QueuerUtil } = require('@friggframework/core');

// Wire queue adapter
QueuerUtil.setQueueClient(new SqsQueueClient());

// Wire encryption adapter (in prisma.js or wherever Cryptor is instantiated)
const cryptor = new Cryptor({
shouldUseAws: true,
keyProvider: new KmsEncryptionKeyProvider(),
});

// Wire WebSocket adapter (in WebSocket handler setup)
const wsRepo = new WebsocketConnectionRepository(
prisma,
new ApiGatewayMessageSender()
);
```

## Consequences

### Positive

- **Zero AWS SDK in non-AWS bundles** — `@friggframework/core` has no AWS SDK imports at all
- **Explicit dependencies** — consumers declare which provider they use in `package.json`
- **Clean hexagonal architecture** — ports in core, adapters in provider packages, wired at composition root
- **Platform flexibility** — same core works on AWS, Netlify, Vercel, Docker, or any other platform
- **Better error messages** — clear errors at initialization time instead of cryptic failures deep in the call stack
- **Testability** — easy to inject mock adapters in tests without mocking AWS SDK internals

### Negative

- **Breaking change** — all existing AWS consumers must update their wiring code
- **More boilerplate at startup** — a few extra lines to instantiate and inject adapters
- **Two packages to install** — AWS users need both `@friggframework/core` and `@friggframework/provider-aws`

### Risks

- **Missed injection** — if a consumer forgets to inject an adapter, they get a clear error message pointing to this ADR and showing exactly what code to add
- **Version drift** — core and provider-aws must be compatible; managed via monorepo versioning

## Affected Files

### Core (ports/interfaces created)
- `packages/core/queues/queue-client-interface.js`
- `packages/core/encrypt/encryption-key-provider-interface.js`
- `packages/core/websocket/websocket-message-sender-interface.js`
- `packages/core/encrypt/aes-encryption-key-provider.js`

### Core (lazy-loading removed, explicit injection required)
- `packages/core/core/Worker.js`
- `packages/core/encrypt/Cryptor.js`
- `packages/core/queues/queuer-util.js`
- `packages/core/websocket/repositories/websocket-connection-repository.js`
- `packages/core/websocket/repositories/websocket-connection-repository-mongo.js`
- `packages/core/websocket/repositories/websocket-connection-repository-postgres.js`
- `packages/core/websocket/repositories/websocket-connection-repository-documentdb.js`
- `packages/core/database/models/WebsocketConnection.js`
- `packages/core/handlers/routers/health.js` (graceful try/catch, not strict)

### Provider-AWS (adapters created)
- `packages/providers/aws/queues/sqs-queue-client.js`
- `packages/providers/aws/encryption/kms-encryption-key-provider.js`
- `packages/providers/aws/websocket/api-gateway-message-sender.js`
- `packages/providers/aws/health/kms-health-check.js`
Loading
Loading