types: Abstract inline interfaces to @plunk/types

This commit is contained in:
Dries Augustyns
2026-01-01 09:09:03 +01:00
parent 519b131792
commit 38da58e5e9
50 changed files with 639 additions and 462 deletions
+45
View File
@@ -0,0 +1,45 @@
/**
* Activity tracking types
*/
/**
* Activity types that can be tracked
*/
export enum ActivityType {
EVENT_TRIGGERED = 'event.triggered',
EMAIL_SENT = 'email.sent',
EMAIL_DELIVERED = 'email.delivered',
EMAIL_OPENED = 'email.opened',
EMAIL_CLICKED = 'email.clicked',
EMAIL_BOUNCED = 'email.bounced',
CAMPAIGN_SENT = 'campaign.sent',
CAMPAIGN_SCHEDULED = 'campaign.scheduled',
WORKFLOW_STARTED = 'workflow.started',
WORKFLOW_COMPLETED = 'workflow.completed',
WORKFLOW_EMAIL_SCHEDULED = 'workflow.email.scheduled',
}
/**
* Unified activity item
*/
export interface Activity {
id: string;
type: ActivityType;
timestamp: Date;
contactEmail?: string;
contactId?: string;
metadata: Record<string, unknown>;
}
/**
* Activity statistics for dashboard
*/
export interface ActivityStats {
totalEvents: number;
totalEmailsSent: number;
totalEmailsOpened: number;
totalEmailsClicked: number;
totalWorkflowsStarted: number;
openRate: number;
clickRate: number;
}
+36
View File
@@ -0,0 +1,36 @@
/**
* Billing and usage limit types
*/
/**
* Usage information for a specific email category
*/
export interface CategoryUsage {
limit: number | null; // null = unlimited
usage: number;
percentage: number; // 0-100
isWarning: boolean; // true if >= 80%
isBlocked: boolean; // true if >= 100%
}
/**
* Complete billing limits and usage for a project
*/
export interface BillingLimitsResponse {
workflows: CategoryUsage;
campaigns: CategoryUsage;
transactional: CategoryUsage;
currency: string | null;
}
/**
* Result of limit check
*/
export interface LimitCheckResult {
allowed: boolean;
warning: boolean; // true if >= 80% but < 100%
usage: number;
limit: number | null;
percentage: number;
message?: string;
}
+38
View File
@@ -0,0 +1,38 @@
/**
* Campaign service types
*/
import type {CampaignAudienceType} from '@plunk/db';
import type {FilterCondition} from '../segments/index.js';
/**
* Data for creating a new campaign
*/
export interface CreateCampaignData {
name: string;
description?: string;
subject: string;
body: string;
from: string;
fromName?: string | null;
replyTo?: string | null;
audienceType: CampaignAudienceType;
audienceCondition?: FilterCondition;
segmentId?: string;
}
/**
* Data for updating an existing campaign
*/
export interface UpdateCampaignData {
name?: string;
description?: string;
subject?: string;
body?: string;
from?: string;
fromName?: string | null;
replyTo?: string | null;
audienceType?: CampaignAudienceType;
audienceCondition?: FilterCondition;
segmentId?: string;
}
+10
View File
@@ -0,0 +1,10 @@
/**
* API service types
* Response and data structures for API endpoints
*/
export * from './activity.js';
export * from './billing.js';
export * from './campaign.js';
export * from './membership.js';
export * from './verification.js';
+31
View File
@@ -0,0 +1,31 @@
/**
* Project membership types
*/
import type {Role} from '@plunk/db';
/**
* Project member with user email
*/
export interface MemberWithEmail {
userId: string;
email: string;
role: Role;
createdAt: Date;
}
/**
* Project owner information
*/
export interface OwnerInfo {
userId: string;
email: string;
}
/**
* Information about disabled projects for a user
*/
export interface DisabledProjectInfo {
hasDisabledProject: boolean;
disabledProjectNames: string[];
}
+18
View File
@@ -0,0 +1,18 @@
/**
* Email verification types
*/
/**
* Result of email address verification
*/
export interface EmailVerificationResult {
email: string;
valid: boolean;
isDisposable: boolean;
isTypo: boolean;
isPlusAddressed: boolean;
domainExists: boolean;
hasMxRecords: boolean;
suggestedEmail?: string;
reasons: string[];
}
+1
View File
@@ -0,0 +1 @@
export * from './pagination.js';
+33
View File
@@ -0,0 +1,33 @@
/**
* Generic pagination types for the Plunk platform
*/
/**
* Offset-based pagination response
* Used for: Templates, Workflows, and other paginated lists with fixed page sizes
*
* @template T - The type of items in the paginated response
*/
export interface PaginatedResponse<T> {
data: T[];
total: number;
page: number;
pageSize: number;
totalPages: number;
}
/**
* Cursor-based pagination response
* Used for: Contacts, Activities, and other large datasets requiring efficient pagination
*
* Cursor pagination is more efficient for large datasets (1M+ rows) as it doesn't
* require offset calculations and provides stable pagination when data changes.
*
* @template T - The type of items in the paginated response
*/
export interface CursorPaginatedResponse<T> {
data: T[];
cursor?: string;
hasMore: boolean;
total?: number; // Optional: computed on first page only for performance
}
+18 -87
View File
@@ -1,94 +1,25 @@
// Segment filter types
export type SegmentFilterOperator =
// Standard operators (for contact fields)
| 'equals'
| 'notEquals'
| 'contains'
| 'notContains'
| 'greaterThan'
| 'lessThan'
| 'greaterThanOrEqual'
| 'lessThanOrEqual'
| 'exists'
| 'notExists'
| 'within'
// Event-based operators
| 'triggered' // Event/email activity occurred (any time)
| 'triggeredWithin' // Event/email activity occurred within timeframe
| 'notTriggered'; // Event/email activity never occurred
/**
* @plunk/types
* Centralized type definitions for the Plunk platform
*/
export type SegmentFilterLogic = 'AND' | 'OR';
// Common utility types
export * from './common/index.js';
export interface SegmentFilter {
field: string;
operator: SegmentFilterOperator;
value?: any;
unit?: 'days' | 'hours' | 'minutes';
}
// Job queue types
export * from './jobs/index.js';
export interface FilterGroup {
filters: SegmentFilter[];
conditions?: FilterCondition;
}
// API service types
export * from './api/index.js';
export interface FilterCondition {
logic: SegmentFilterLogic;
groups: FilterGroup[];
}
// Notification types
export * from './notifications/index.js';
export interface CreateSegmentData {
name: string;
description?: string;
condition: FilterCondition;
trackMembership?: boolean;
}
// Extended Prisma types
export * from './prisma/index.js';
export interface UpdateSegmentData {
name?: string;
description?: string;
condition?: FilterCondition;
trackMembership?: boolean;
}
// Segment and filter types
export * from './segments/index.js';
export interface SegmentMembershipComputeResult {
added: number;
removed: number;
total: number;
}
// Security status types
export interface SecurityRateData {
total: number;
bounces: number;
complaints: number;
bounceRate: number;
complaintRate: number;
}
export interface SecurityStatus {
projectId: string;
isHealthy: boolean;
shouldDisable: boolean;
sevenDay: SecurityRateData;
allTime: SecurityRateData;
violations: string[];
warnings: string[];
}
export interface SecurityThresholds {
MIN_EMAILS_FOR_ENFORCEMENT: number;
BOUNCE_7DAY_WARNING: number;
BOUNCE_7DAY_CRITICAL: number;
BOUNCE_ALLTIME_WARNING: number;
BOUNCE_ALLTIME_CRITICAL: number;
COMPLAINT_7DAY_WARNING: number;
COMPLAINT_7DAY_CRITICAL: number;
COMPLAINT_ALLTIME_WARNING: number;
COMPLAINT_ALLTIME_CRITICAL: number;
}
export interface ProjectSecurityMetrics {
status: SecurityStatus;
thresholds: SecurityThresholds;
isDisabled: boolean;
}
// Security types
export * from './security/index.js';
+23
View File
@@ -0,0 +1,23 @@
/**
* Campaign queue job data types
*/
/**
* Job data for processing a batch of campaign recipients
* Used by: campaignQueue worker
*/
export interface CampaignBatchJobData {
campaignId: string;
batchNumber: number;
offset: number;
limit: number;
cursor?: string; // For cursor-based pagination
}
/**
* Job data for sending a scheduled campaign
* Used by: scheduledQueue worker
*/
export interface ScheduledCampaignJobData {
campaignId: string;
}
+11
View File
@@ -0,0 +1,11 @@
/**
* Email queue job data types
*/
/**
* Job data for sending a single email
* Used by: emailQueue worker
*/
export interface SendEmailJobData {
emailId: string;
}
+23
View File
@@ -0,0 +1,23 @@
/**
* Import and bulk operation queue job data types
*/
/**
* Job data for importing contacts from CSV
* Used by: importQueue worker
*/
export interface ContactImportJobData {
projectId: string;
csvData: string; // Base64 encoded CSV content
filename: string;
}
/**
* Job data for bulk contact actions (subscribe, unsubscribe, delete)
* Used by: bulkContactQueue worker
*/
export interface BulkContactActionJobData {
projectId: string;
contactIds: string[];
operation: 'subscribe' | 'unsubscribe' | 'delete';
}
+10
View File
@@ -0,0 +1,10 @@
/**
* Queue job data types
* Centralized type definitions for all BullMQ job payloads
*/
export * from './email.js';
export * from './campaign.js';
export * from './workflow.js';
export * from './import.js';
export * from './maintenance.js';
+29
View File
@@ -0,0 +1,29 @@
/**
* Maintenance and background task queue job data types
*/
/**
* Job data for updating segment membership counts
* Used by: segmentCountQueue worker
*/
export interface SegmentCountJobData {
projectId?: string; // Optional: if provided, only update this project's segments
}
/**
* Job data for domain verification checks
* Used by: domainVerificationQueue worker
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface DomainVerificationJobData {
// Empty for now - processes all domains
}
/**
* Job data for cleaning up old API request logs
* Used by: apiRequestCleanupQueue worker
*/
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface ApiRequestCleanupJobData {
// Empty - cleans up old API request logs
}
+14
View File
@@ -0,0 +1,14 @@
/**
* Workflow queue job data types
*/
/**
* Job data for executing a workflow step
* Used by: workflowQueue worker
*/
export interface WorkflowStepJobData {
executionId: string;
stepId: string;
type?: 'process-step' | 'timeout'; // Job type for different handling
stepExecutionId?: string; // For timeout jobs, reference to the step execution
}
@@ -0,0 +1 @@
export * from './ntfy.js';
+41
View File
@@ -0,0 +1,41 @@
/**
* Notification types for ntfy.sh integration
*/
/**
* Priority levels for ntfy notifications
* Based on ntfy.sh documentation
*/
export enum NtfyPriority {
MIN = 1, // No vibration/sound, relegated to "Other notifications"
LOW = 2, // No vibration/sound, hidden until drawer opened
DEFAULT = 3, // Short vibration and sound (standard)
HIGH = 4, // Long vibration, pop-over notification
MAX = 5, // Long vibration bursts, pop-over notification
}
/**
* Tags for ntfy notifications (emoji shortcuts)
*/
export enum NtfyTag {
WARNING = 'warning',
ERROR = 'rotating_light',
SUCCESS = 'white_check_mark',
MONEY = 'money_with_wings',
SHIELD = 'shield',
ROCKET = 'rocket',
BELL = 'bell',
CHART = 'chart_with_upwards_trend',
SKULL = 'skull',
INFO = 'information_source',
}
/**
* Notification payload for ntfy
*/
export interface NtfyNotification {
title: string;
message: string;
priority?: NtfyPriority;
tags?: NtfyTag[];
}
+82
View File
@@ -0,0 +1,82 @@
/**
* Extended Prisma types
* Types that extend Prisma models with additional relations or computed fields
*/
import type {
Workflow,
WorkflowExecution,
WorkflowStep,
WorkflowStepExecution,
WorkflowTransition,
Template,
Contact,
Prisma,
} from '@plunk/db';
/**
* Workflow with all steps, transitions, and template details
* Used for workflow editor and detailed workflow views
*/
export interface WorkflowWithDetails extends Workflow {
steps: Array<
WorkflowStep & {
template?: {id: string; name: string} | null;
outgoingTransitions: WorkflowTransition[];
incomingTransitions: WorkflowTransition[];
}
>;
}
/**
* Workflow execution with full context
* Used for execution details and monitoring
*/
export interface WorkflowExecutionWithDetails extends WorkflowExecution {
workflow: Workflow;
contact: {id: string; email: string};
currentStep?: WorkflowStep | null;
stepExecutions: WorkflowStepExecution[];
}
/**
* Workflow execution with all relations loaded
* Used internally by workflow execution engine
*/
export type WorkflowExecutionWithRelations = WorkflowExecution & {
contact: Contact;
workflow: Workflow;
};
/**
* Workflow step with optional template
* Used by step execution logic
*/
export type WorkflowStepWithTemplate = WorkflowStep & {
template?: Template | null;
};
/**
* Workflow step with outgoing transitions loaded
* Used for flow control and navigation
*/
export type WorkflowStepWithTransitions = WorkflowStep & {
outgoingTransitions?: Array<{
id: string;
condition: Prisma.JsonValue;
priority: number;
toStep: WorkflowStep;
}>;
};
/**
* Step configuration (JSON value)
* Type-safe alias for workflow step config
*/
export type StepConfig = Prisma.JsonValue;
/**
* Step execution result
* Generic key-value result from step execution
*/
export type StepResult = Record<string, unknown>;
+1
View File
@@ -0,0 +1 @@
export * from './extended.js';
+39
View File
@@ -0,0 +1,39 @@
/**
* Security and project health types
*/
export interface SecurityRateData {
total: number;
bounces: number;
complaints: number;
bounceRate: number;
complaintRate: number;
}
export interface SecurityStatus {
projectId: string;
isHealthy: boolean;
shouldDisable: boolean;
sevenDay: SecurityRateData;
allTime: SecurityRateData;
violations: string[];
warnings: string[];
}
export interface SecurityThresholds {
MIN_EMAILS_FOR_ENFORCEMENT: number;
BOUNCE_7DAY_WARNING: number;
BOUNCE_7DAY_CRITICAL: number;
BOUNCE_ALLTIME_WARNING: number;
BOUNCE_ALLTIME_CRITICAL: number;
COMPLAINT_7DAY_WARNING: number;
COMPLAINT_7DAY_CRITICAL: number;
COMPLAINT_ALLTIME_WARNING: number;
COMPLAINT_ALLTIME_CRITICAL: number;
}
export interface ProjectSecurityMetrics {
status: SecurityStatus;
thresholds: SecurityThresholds;
isDisabled: boolean;
}
+61
View File
@@ -0,0 +1,61 @@
/**
* Segment and filter types
*/
// Segment filter types
export type SegmentFilterOperator =
// Standard operators (for contact fields)
| 'equals'
| 'notEquals'
| 'contains'
| 'notContains'
| 'greaterThan'
| 'lessThan'
| 'greaterThanOrEqual'
| 'lessThanOrEqual'
| 'exists'
| 'notExists'
| 'within'
// Event-based operators
| 'triggered' // Event/email activity occurred (any time)
| 'triggeredWithin' // Event/email activity occurred within timeframe
| 'notTriggered'; // Event/email activity never occurred
export type SegmentFilterLogic = 'AND' | 'OR';
export interface SegmentFilter {
field: string;
operator: SegmentFilterOperator;
value?: any;
unit?: 'days' | 'hours' | 'minutes';
}
export interface FilterGroup {
filters: SegmentFilter[];
conditions?: FilterCondition;
}
export interface FilterCondition {
logic: SegmentFilterLogic;
groups: FilterGroup[];
}
export interface CreateSegmentData {
name: string;
description?: string;
condition: FilterCondition;
trackMembership?: boolean;
}
export interface UpdateSegmentData {
name?: string;
description?: string;
condition?: FilterCondition;
trackMembership?: boolean;
}
export interface SegmentMembershipComputeResult {
added: number;
removed: number;
total: number;
}