πŸš€ DAY 3 OF LAUNCH WEEK:Announcing Bun and vlt Support in Socket.Learn more β†’
Socket
Book a DemoInstallSign in
Socket

@friggframework/core

Package Overview
Dependencies
Maintainers
3
Versions
790
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@friggframework/core

Source
npmnpm
Version
2.0.0--canary.461.39e4094.0
Version published
Weekly downloads
1.8K
-80.61%
Maintainers
3
Weekly downloads
Β 
Created
Source

Frigg Core

The @friggframework/core package is the foundational layer of the Frigg Framework, implementing a hexagonal architecture pattern for building scalable, maintainable enterprise integrations. It provides the essential building blocks, domain logic, and infrastructure components that power the entire Frigg ecosystem.

Table of Contents

  • Architecture Overview
  • Installation
  • Quick Start
  • Core Components
  • Hexagonal Architecture
  • Usage Examples
  • Testing
  • Development
  • API Reference
  • Contributing

Architecture Overview

Frigg Core implements a hexagonal architecture (also known as ports and adapters) that separates business logic from external concerns:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Inbound Adapters                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚  β”‚ Express     β”‚  β”‚ Lambda      β”‚  β”‚ WebSocket   β”‚        β”‚
β”‚  β”‚ Routes      β”‚  β”‚ Handlers    β”‚  β”‚ Handlers    β”‚        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                  Application Layer                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚  β”‚ Use Cases   β”‚  β”‚ Services    β”‚  β”‚ Coordinatorsβ”‚        β”‚
β”‚  β”‚ (Business   β”‚  β”‚             β”‚  β”‚             β”‚        β”‚
β”‚  β”‚ Logic)      β”‚  β”‚             β”‚  β”‚             β”‚        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    Domain Layer                            β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚  β”‚ Integration β”‚  β”‚ Entities    β”‚  β”‚ Value       β”‚        β”‚
β”‚  β”‚ Aggregates  β”‚  β”‚             β”‚  β”‚ Objects     β”‚        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                              β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                   Outbound Adapters                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”        β”‚
β”‚  β”‚ Database    β”‚  β”‚ API Modules β”‚  β”‚ Event       β”‚        β”‚
β”‚  β”‚ Repositoriesβ”‚  β”‚             β”‚  β”‚ Publishers  β”‚        β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Installation

npm install @friggframework/core
# or
yarn add @friggframework/core

Prisma Support (Optional)

@friggframework/core supports both MongoDB and PostgreSQL via Prisma ORM. Prisma is an optional peer dependency - you only need to install it if you're using database features that require migrations or schema generation.

When you need Prisma:

  • Running database migrations (prisma migrate, prisma db push)
  • Generating Prisma clients for your application
  • Using the migration Lambda function (dbMigrate)

Installation:

# Install Prisma CLI and Client as dev dependencies
npm install --save-dev prisma @prisma/client

# Or with yarn
yarn add -D prisma @prisma/client

Generate Prisma Clients:

# From @friggframework/core directory
npm run prisma:generate:mongo      # MongoDB only
npm run prisma:generate:postgres   # PostgreSQL only
npm run prisma:generate            # Both databases

Note: The published npm package includes pre-generated Prisma clients, so you don't need to install Prisma just to use @friggframework/core in production. Prisma is only required if you're actively developing migrations or running the migration Lambda function.

Prerequisites

  • Node.js 16+
  • MongoDB 4.4+ (for data persistence)
  • AWS credentials (for SQS, KMS, Lambda deployment)

Environment Variables

# Database
MONGO_URI=mongodb://localhost:27017/frigg
FRIGG_ENCRYPTION_KEY=your-256-bit-encryption-key

# AWS (Optional - for production deployments)
AWS_REGION=us-east-1
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key

# Logging
DEBUG=frigg:*
LOG_LEVEL=info

Core Components

1. Integrations (/integrations)

The heart of the framework - manages integration lifecycle and business logic.

Key Classes:

  • IntegrationBase - Base class for all integrations
  • Integration - Domain aggregate using Proxy pattern
  • Use cases: CreateIntegration, UpdateIntegration, DeleteIntegration

Usage:

const { IntegrationBase } = require('@friggframework/core');

class SlackHubSpotSync extends IntegrationBase {
    static Definition = {
        name: 'slack-hubspot-sync',
        version: '2.1.0',
        modules: {
            slack: 'slack',
            hubspot: 'hubspot'
        }
    };

    async onCreate({ integrationId }) {
        // Setup webhooks, initial sync, etc.
        await this.slack.createWebhook(process.env.WEBHOOK_URL);
        await this.hubspot.setupContactSync();
        await super.onCreate({ integrationId });
    }
}

3. Database (/database)

MongoDB integration with Mongoose ODM.

Key Components:

  • Connection management
  • Pre-built models (User, Integration, Credential, etc.)
  • Schema definitions

Usage:

const { 
    connectToDatabase, 
    IntegrationModel, 
    UserModel 
} = require('@friggframework/core');

await connectToDatabase();

// Query integrations
const userIntegrations = await IntegrationModel.find({ 
    userId: 'user-123',
    status: 'ENABLED' 
});

// Create user
const user = new UserModel({
    email: 'user@example.com',
    name: 'John Doe'
});
await user.save();

4. Encryption (/encrypt)

AES-256-GCM encryption for sensitive data.

Usage:

const { Encrypt, Cryptor } = require('@friggframework/core');

// Simple encryption
const encrypted = Encrypt.encrypt('sensitive-data');
const decrypted = Encrypt.decrypt(encrypted);

// Advanced encryption with custom key
const cryptor = new Cryptor(process.env.CUSTOM_KEY);
const secureData = cryptor.encrypt(JSON.stringify({
    accessToken: 'oauth-token',
    refreshToken: 'refresh-token'
}));

5. Error Handling (/errors)

Standardized error types with proper HTTP status codes.

Usage:

const { 
    BaseError, 
    RequiredPropertyError, 
    FetchError 
} = require('@friggframework/core');

// Custom business logic error
throw new RequiredPropertyError('userId is required');

// API communication error
throw new FetchError('Failed to fetch data from external API', {
    statusCode: 404,
    response: errorResponse
});

// Base error with custom properties
throw new BaseError('Integration failed', {
    integrationId: 'int-123',
    errorCode: 'SYNC_FAILED'
});

6. Logging (/logs)

Structured logging with debug capabilities.

Usage:

const { debug, initDebugLog, flushDebugLog } = require('@friggframework/core');

// Initialize debug logging
initDebugLog('integration:slack');

// Log debug information
debug('Processing webhook payload', { 
    eventType: 'contact.created',
    payload: webhookData 
});

// Flush logs (useful in serverless environments)
await flushDebugLog();

7. User Management (/user)

Comprehensive user authentication and authorization system supporting both individual and organizational users.

Key Classes:

  • User - Domain aggregate for user entities
  • UserRepository - Data access for user operations
  • Use cases: LoginUser, CreateIndividualUser, CreateOrganizationUser, GetUserFromBearerToken

User Types:

  • Individual Users: Personal accounts with email/username authentication
  • Organization Users: Business accounts with organization-level access
  • Hybrid Mode: Support for both user types simultaneously

Authentication Methods:

  • Password-based: Traditional username/password authentication
  • Token-based: Bearer token authentication with session management
  • App-based: External app user ID authentication (passwordless)

Usage:

const { 
    LoginUser, 
    CreateIndividualUser, 
    GetUserFromBearerToken,
    UserRepository 
} = require('@friggframework/core');

// Configure user behavior in app definition
const userConfig = {
    usePassword: true,
    primary: 'individual', // or 'organization'
    individualUserRequired: true,
    organizationUserRequired: false
};

const userRepository = new UserRepository({ userConfig });

// Create individual user
const createUser = new CreateIndividualUser({ userRepository, userConfig });
const user = await createUser.execute({
    email: 'user@example.com',
    username: 'john_doe',
    password: 'secure_password',
    appUserId: 'external_user_123' // Optional external reference
});

// Login user
const loginUser = new LoginUser({ userRepository, userConfig });
const authenticatedUser = await loginUser.execute({
    username: 'john_doe',
    password: 'secure_password'
});

// Token-based authentication
const getUserFromToken = new GetUserFromBearerToken({ userRepository, userConfig });
const user = await getUserFromToken.execute('Bearer eyJhbGciOiJIUzI1NiIs...');

// Access user properties
console.log('User ID:', user.getId());
console.log('Primary user:', user.getPrimaryUser());
console.log('Individual user:', user.getIndividualUser());
console.log('Organization user:', user.getOrganizationUser());

8. Lambda Utilities (/lambda)

AWS Lambda-specific utilities and helpers.

Usage:

const { TimeoutCatcher } = require('@friggframework/core');

exports.handler = async (event, context) => {
    const timeoutCatcher = new TimeoutCatcher(context);
    
    try {
        // Long-running integration process
        const result = await processIntegrationSync(event);
        return { statusCode: 200, body: JSON.stringify(result) };
    } catch (error) {
        if (timeoutCatcher.isNearTimeout()) {
            // Handle graceful shutdown
            await saveProgressState(event);
            return { statusCode: 202, body: 'Processing continues...' };
        }
        throw error;
    }
};

User Management & Behavior

Frigg Core provides a flexible user management system that supports various authentication patterns and user types. The system is designed around the concept of Individual Users (personal accounts) and Organization Users (business accounts), with configurable authentication methods.

User Configuration

User behavior is configured in the app definition, allowing you to customize authentication requirements:

// App Definition with User Configuration
const appDefinition = {
    integrations: [HubSpotIntegration],
    user: {
        usePassword: true,                    // Enable password authentication
        primary: 'individual',               // Primary user type: 'individual' or 'organization'
        organizationUserRequired: true,      // Require organization user
        individualUserRequired: true,        // Require individual user
    }
};

User Domain Model

The User class provides a rich domain model with behavior:

const { User } = require('@friggframework/core');

// User instance methods
const user = new User(individualUser, organizationUser, usePassword, primary);

// Access methods
user.getId()                    // Get primary user ID
user.getPrimaryUser()          // Get primary user based on config
user.getIndividualUser()       // Get individual user
user.getOrganizationUser()     // Get organization user

// Validation methods
user.isPasswordRequired()      // Check if password is required
user.isPasswordValid(password) // Validate password
user.isIndividualUserRequired() // Check individual user requirement
user.isOrganizationUserRequired() // Check organization user requirement

// Configuration methods
user.setIndividualUser(individualUser)
user.setOrganizationUser(organizationUser)

Database Models

The user system uses MongoDB with Mongoose for data persistence:

// Individual User Schema
{
    email: String,
    username: { type: String, unique: true },
    hashword: String,           // Encrypted password
    appUserId: String,          // External app reference
    organizationUser: ObjectId  // Reference to organization
}

// Organization User Schema
{
    name: String,
    appOrgId: String,          // External organization reference
    domain: String,
    settings: Object
}

// Session Token Schema
{
    user: ObjectId,            // Reference to user
    token: String,             // Encrypted token
    expires: Date,
    created: Date
}

Security Features

  • Password Hashing: Uses bcrypt with configurable salt rounds
  • Token Management: Secure session tokens with expiration
  • Unique Constraints: Enforced username and email uniqueness
  • External References: Support for external app user/org IDs
  • Flexible Authentication: Multiple authentication methods

Hexagonal Architecture

Use Case Pattern

Each business operation is encapsulated in a use case class:

class UpdateIntegrationStatus {
    constructor({ integrationRepository }) {
        this.integrationRepository = integrationRepository;
    }

    async execute(integrationId, newStatus) {
        // Business logic validation
        if (!['ENABLED', 'DISABLED', 'ERROR'].includes(newStatus)) {
            throw new Error('Invalid status');
        }

        // Domain operation
        const integration = await this.integrationRepository.findById(integrationId);
        if (!integration) {
            throw new Error('Integration not found');
        }

        // Update and persist
        integration.status = newStatus;
        integration.updatedAt = new Date();
        
        return await this.integrationRepository.save(integration);
    }
}

Repository Pattern

Data access is abstracted through repositories:

class IntegrationRepository {
    async findById(id) {
        return await IntegrationModel.findById(id);
    }

    async findByUserId(userId) {
        return await IntegrationModel.find({ userId, deletedAt: null });
    }

    async save(integration) {
        return await integration.save();
    }

    async createIntegration(entities, userId, config) {
        const integration = new IntegrationModel({
            entitiesIds: entities,
            userId,
            config,
            status: 'NEW',
            createdAt: new Date()
        });
        return await integration.save();
    }
}

Domain Aggregates

Complex business objects with behavior:

const Integration = new Proxy(class {}, {
    construct(target, args) {
        const [params] = args;
        const instance = new params.integrationClass(params);
        
        // Attach domain properties
        Object.assign(instance, {
            id: params.id,
            userId: params.userId,
            entities: params.entities,
            config: params.config,
            status: params.status,
            modules: params.modules
        });

        return instance;
    }
});

Usage Examples

Real-World HubSpot Integration Example

Here's a complete, production-ready HubSpot integration that demonstrates advanced Frigg features:

const {
    get,
    IntegrationBase,
    WebsocketConnection,
} = require('@friggframework/core');
const FriggConstants = require('../utils/constants');
const hubspot = require('@friggframework/api-module-hubspot');
const testRouter = require('../testRouter');
const extensions = require('../extensions');

class HubSpotIntegration extends IntegrationBase {
    static Definition = {
        name: 'hubspot',
        version: '1.0.0',
        supportedVersions: ['1.0.0'],
        hasUserConfig: true,

        display: {
            label: 'HubSpot',
            description: hubspot.Config.description,
            category: 'Sales & CRM, Marketing',
            detailsUrl: 'https://hubspot.com',
            icon: hubspot.Config.logoUrl,
        },
        modules: {
            hubspot: {
                definition: hubspot.Definition,
            },
        },
        // Express routes for webhook endpoints and custom APIs
        routes: [
            {
                path: '/hubspot/webhooks',
                method: 'POST',
                event: 'HUBSPOT_WEBHOOK',
            },
            testRouter,
        ],
    };

    constructor() {
        super();
        
        // Define event handlers for various integration actions
        this.events = {
            // Webhook handler with real-time WebSocket broadcasting
            HUBSPOT_WEBHOOK: {
                handler: async ({ data, context }) => {
                    console.log('Received HubSpot webhook:', data);

                    // Broadcast to all connected WebSocket clients
                    const activeConnections = await WebsocketConnection.getActiveConnections();
                    const message = JSON.stringify({
                        type: 'HUBSPOT_WEBHOOK',
                        data,
                    });

                    activeConnections.forEach((connection) => {
                        connection.send(message);
                    });
                },
            },
            
            // User action: Get sample data with formatted table output
            [FriggConstants.defaultEvents.GET_SAMPLE_DATA]: {
                type: FriggConstants.eventTypes.USER_ACTION,
                handler: this.getSampleData,
                title: 'Get Sample Data',
                description: 'Get sample data from HubSpot and display in a formatted table',
                userActionType: 'QUICK_ACTION',
            },
            
            // User action: List available objects
            GET_OBJECT_LIST: {
                type: FriggConstants.eventTypes.USER_ACTION,
                handler: this.getObjectList,
                title: 'Get Object List',
                description: 'Get list of available HubSpot objects',
                userActionType: 'DATA',
            },
            
            // User action: Create records with dynamic forms
            CREATE_RECORD: {
                type: FriggConstants.eventTypes.USER_ACTION,
                handler: this.createRecord,
                title: 'Create Record',
                description: 'Create a new record in HubSpot',
                userActionType: 'DATA',
            },
        };
        
        // Extension system for modular functionality
        this.extensions = {
            hubspotWebhooks: {
                extension: extensions.hubspotWebhooks,
                handlers: {
                    WEBHOOK_EVENT: this.handleWebhookEvent,
                },
            },
        };
    }

    // Business logic: Fetch and format sample data
    async getSampleData({ objectName }) {
        let res;
        switch (objectName) {
            case 'deals':
                res = await this.hubspot.api.searchDeals({
                    properties: ['dealname,amount,closedate'],
                });
                break;
            case 'contacts':
                res = await this.hubspot.api.listContacts({
                    after: 0,
                    properties: 'firstname,lastname,email',
                });
                break;
            case 'companies':
                res = await this.hubspot.api.searchCompanies({
                    properties: ['name,website,email'],
                    limit: 100,
                });
                break;
            default:
                throw new Error(`Unsupported object type: ${objectName}`);
        }

        const portalId = this.hubspot.entity.externalId;

        // Format data with HubSpot record links
        const formatted = res.results.map((item) => {
            const formattedItem = {
                linkToRecord: `https://app.hubspot.com/contacts/${portalId}/${objectName}/${item.id}/`,
                id: item.id,
            };

            // Clean and format properties
            for (const [key, value] of Object.entries(item.properties)) {
                if (value !== null && value !== undefined && value !== '') {
                    formattedItem[key] = value;
                }
            }
            delete formattedItem.hs_object_id;

            return formattedItem;
        });

        return { label: objectName, data: formatted };
    }

    // Return available HubSpot object types
    async getObjectList() {
        return [
            { key: 'deals', label: 'Deals' },
            { key: 'contacts', label: 'Contacts' },
            { key: 'companies', label: 'Companies' },
        ];
    }

    // Create records based on object type
    async createRecord(args) {
        let res;
        const objectType = args.objectType;
        delete args.objectType;
        
        switch (objectType.toLowerCase()) {
            case 'deal':
                res = await this.hubspot.api.createDeal({ ...args });
                break;
            case 'company':
                res = await this.hubspot.api.createCompany({ ...args });
                break;
            case 'contact':
                res = await this.hubspot.api.createContact({ ...args });
                break;
            default:
                throw new Error(`Unsupported object type: ${objectType}`);
        }
        return { data: res };
    }

    // Dynamic form generation based on action and context
    async getActionOptions({ actionId, data }) {
        switch (actionId) {
            case 'CREATE_RECORD':
                let jsonSchema = {
                    type: 'object',
                    properties: {
                        objectType: {
                            type: 'string',
                            title: 'Object Type',
                        },
                    },
                    required: [],
                };
                
                let uiSchema = {
                    type: 'HorizontalLayout',
                    elements: [
                        {
                            type: 'Control',
                            scope: '#/properties/objectType',
                            rule: { effect: 'HIDE', condition: {} },
                        },
                    ],
                };

                // Generate form fields based on object type
                switch (data.name.toLowerCase()) {
                    case 'deal':
                        jsonSchema.properties = {
                            ...jsonSchema.properties,
                            dealname: { type: 'string', title: 'Deal Name' },
                            amount: { type: 'number', title: 'Amount' },
                        };
                        jsonSchema.required = ['dealname', 'amount'];
                        uiSchema.elements.push(
                            { type: 'Control', scope: '#/properties/dealname' },
                            { type: 'Control', scope: '#/properties/amount' }
                        );
                        break;
                        
                    case 'company':
                        jsonSchema.properties = {
                            ...jsonSchema.properties,
                            name: { type: 'string', title: 'Company Name' },
                            website: { type: 'string', title: 'Website URL' },
                        };
                        jsonSchema.required = ['name', 'website'];
                        uiSchema.elements.push(
                            { type: 'Control', scope: '#/properties/name' },
                            { type: 'Control', scope: '#/properties/website' }
                        );
                        break;
                        
                    case 'contact':
                        jsonSchema.properties = {
                            ...jsonSchema.properties,
                            firstname: { type: 'string', title: 'First Name' },
                            lastname: { type: 'string', title: 'Last Name' },
                            email: { type: 'string', title: 'Email Address' },
                        };
                        jsonSchema.required = ['firstname', 'lastname', 'email'];
                        uiSchema.elements.push(
                            { type: 'Control', scope: '#/properties/firstname' },
                            { type: 'Control', scope: '#/properties/lastname' },
                            { type: 'Control', scope: '#/properties/email' }
                        );
                        break;
                        
                    default:
                        throw new Error(`Unsupported object type: ${data.name}`);
                }

                return {
                    jsonSchema,
                    uiSchema,
                    data: { objectType: data.name },
                };
        }
        return null;
    }

    async getConfigOptions() {
        // Return configuration options for the integration
        return {};
    }
}

module.exports = HubSpotIntegration;

index.js

const HubSpotIntegration = require('./src/integrations/HubSpotIntegration');

const appDefinition = {
    integrations: [
        HubSpotIntegration,
    ],
    user: {
        usePassword: true,
        primary: 'individual',
        organizationUserRequired: true,
        individualUserRequired: true,
    }
}

module.exports = {
    Definition: appDefinition,
}

Key Features Demonstrated

This real-world example showcases:

πŸ”„ Webhook Integration: Real-time event processing with WebSocket broadcasting πŸ“Š User Actions: Interactive data operations with dynamic form generation
🎯 API Module Integration: Direct use of @friggframework/api-module-hubspot πŸ›  Extension System: Modular functionality through extensions πŸ“ Dynamic Forms: JSON Schema-based form generation for different object types πŸ”— Deep Linking: Direct links to HubSpot records in formatted data ⚑ Real-time Updates: WebSocket connections for live data streaming

Testing

Running Tests

# Run all tests
npm test

# Run specific test file
npm test -- --testPathPattern="integration.test.js"

Test Structure

The core package uses a comprehensive testing approach:

// Example test structure
describe('CreateIntegration Use-Case', () => {
    let integrationRepository;
    let moduleFactory;
    let useCase;

    beforeEach(() => {
        integrationRepository = new TestIntegrationRepository();
        moduleFactory = new TestModuleFactory();
        useCase = new CreateIntegration({
            integrationRepository,
            integrationClasses: [TestIntegration],
            moduleFactory
        });
    });

    describe('happy path', () => {
        it('creates an integration and returns DTO', async () => {
            const result = await useCase.execute(['entity-1'], 'user-1', { type: 'test' });
            expect(result.id).toBeDefined();
            expect(result.status).toBe('NEW');
        });
    });

    describe('error cases', () => {
        it('throws error for unknown integration type', async () => {
            await expect(useCase.execute(['entity-1'], 'user-1', { type: 'unknown' }))
                .rejects.toThrow('No integration class found for type: unknown');
        });
    });
});

Test Doubles

The framework provides test doubles for external dependencies:

const { TestIntegrationRepository, TestModuleFactory } = require('@friggframework/core/test');

// Mock repository for testing
const testRepo = new TestIntegrationRepository();
testRepo.addMockIntegration({ id: 'test-123', userId: 'user-1' });

// Mock module factory
const testFactory = new TestModuleFactory();
testFactory.addMockModule('hubspot', mockHubSpotModule);

Development

Project Structure

packages/core/
β”œβ”€β”€ integrations/           # Integration domain logic
β”‚   β”œβ”€β”€ use-cases/         # Business use cases
β”‚   β”œβ”€β”€ tests/             # Integration tests
β”‚   └── integration-base.js # Base integration class
β”œβ”€β”€ modules/               # API module system
β”‚   β”œβ”€β”€ requester/         # HTTP clients
β”‚   └── use-cases/         # Module management
β”œβ”€β”€ database/              # Data persistence
β”œβ”€β”€ encrypt/               # Encryption utilities
β”œβ”€β”€ errors/                # Error definitions
β”œβ”€β”€ logs/                  # Logging system
└── lambda/                # Serverless utilities

Adding New Components

  • Create the component: Follow the established patterns
  • Add tests: Comprehensive test coverage required
  • Export from index.js: Make it available to consumers
  • Update documentation: Keep README current

Code Style

# Format code
npm run lint:fix

# Check linting
npm run lint

API Reference

Core Exports

const {
    // Integrations
    IntegrationBase,
    IntegrationModel,
    CreateIntegration,
    UpdateIntegration,
    DeleteIntegration,
    
    // Modules
    OAuth2Requester,
    ApiKeyRequester,
    Credential,
    Entity,
    // Database
    connectToDatabase,
    mongoose,
    UserModel,
    
    // Utilities
    Encrypt,
    Cryptor,
    BaseError,
    debug,
    TimeoutCatcher
} = require('@friggframework/core');

Environment Configuration

VariableRequiredDescription
MONGO_URIYesMongoDB connection string
FRIGG_ENCRYPTION_KEYYes256-bit encryption key
AWS_REGIONNoAWS region for services
DEBUGNoDebug logging pattern
LOG_LEVELNoLogging level (debug, info, warn, error)

License

This project is licensed under the MIT License - see the LICENSE.md file for details.

Support

Built with ❀️ by the Frigg Framework team.

FAQs

Package last updated on 23 Oct 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts