@friggframework/core 2.0.0--canary.622.fe9da2d.0 → 2.0.0--canary.623.ee3bde8.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.
- package/README.md +0 -40
- package/application/index.js +9 -10
- package/core/create-handler.js +10 -112
- package/generated/prisma-mongodb/edge.js +4 -16
- package/generated/prisma-mongodb/index-browser.js +1 -13
- package/generated/prisma-mongodb/index.d.ts +96 -1494
- package/generated/prisma-mongodb/index.js +4 -16
- package/generated/prisma-mongodb/package.json +1 -1
- package/generated/prisma-mongodb/schema.prisma +0 -23
- package/generated/prisma-mongodb/wasm.js +4 -16
- package/generated/prisma-postgresql/edge.js +4 -16
- package/generated/prisma-postgresql/index-browser.js +1 -13
- package/generated/prisma-postgresql/index.d.ts +91 -1540
- package/generated/prisma-postgresql/index.js +4 -16
- package/generated/prisma-postgresql/package.json +1 -1
- package/generated/prisma-postgresql/schema.prisma +0 -22
- package/generated/prisma-postgresql/wasm.js +4 -16
- package/handlers/app-definition-loader.js +3 -26
- package/handlers/integration-event-dispatcher.js +15 -29
- package/handlers/routers/integration-webhook-routers.js +7 -20
- package/index.js +9 -16
- package/integrations/integration-base.js +14 -65
- package/modules/module.js +15 -2
- package/modules/requester/requester.js +60 -105
- package/package.json +5 -12
- package/prisma-mongodb/schema.prisma +0 -23
- package/prisma-postgresql/schema.prisma +0 -22
- package/reporting/README.md +1 -8
- package/reporting/reporting-router.js +1 -8
- package/reporting/use-cases/list-integrations-report.js +6 -53
- package/application/commands/usage-commands.js +0 -56
- package/prisma-postgresql/migrations/20260705000000_create_usage_counter/migration.sql +0 -26
- package/telemetry/README.md +0 -331
- package/telemetry/bind-telemetry-context.js +0 -73
- package/telemetry/canonical-counters.js +0 -52
- package/telemetry/exporters.js +0 -85
- package/telemetry/index.js +0 -26
- package/telemetry/instrument-handler.js +0 -87
- package/telemetry/no-op-telemetry.js +0 -67
- package/telemetry/north-star.js +0 -103
- package/telemetry/otel-telemetry.js +0 -213
- package/telemetry/plugin-subscribers.js +0 -77
- package/telemetry/telemetry-config.js +0 -120
- package/telemetry/telemetry-context.js +0 -40
- package/telemetry/telemetry-event-bus.js +0 -58
- package/telemetry/telemetry-runtime.js +0 -147
- package/telemetry/telemetry-service.js +0 -51
- package/telemetry/usage-rollup-subscriber.js +0 -116
- package/usage/README.md +0 -54
- package/usage/index.js +0 -17
- package/usage/repositories/usage-repository-documentdb.js +0 -194
- package/usage/repositories/usage-repository-factory.js +0 -25
- package/usage/repositories/usage-repository-interface.js +0 -37
- package/usage/repositories/usage-repository-prisma.js +0 -146
- package/usage/tracked-metrics.js +0 -38
- package/usage/usage-windows.js +0 -24
|
@@ -242,17 +242,6 @@ exports.Prisma.ScriptScheduleScalarFieldEnum = {
|
|
|
242
242
|
updatedAt: 'updatedAt'
|
|
243
243
|
};
|
|
244
244
|
|
|
245
|
-
exports.Prisma.UsageCounterScalarFieldEnum = {
|
|
246
|
-
id: 'id',
|
|
247
|
-
integrationId: 'integrationId',
|
|
248
|
-
integrationType: 'integrationType',
|
|
249
|
-
metric: 'metric',
|
|
250
|
-
window: 'window',
|
|
251
|
-
value: 'value',
|
|
252
|
-
createdAt: 'createdAt',
|
|
253
|
-
updatedAt: 'updatedAt'
|
|
254
|
-
};
|
|
255
|
-
|
|
256
245
|
exports.Prisma.SortOrder = {
|
|
257
246
|
asc: 'asc',
|
|
258
247
|
desc: 'desc'
|
|
@@ -325,8 +314,7 @@ exports.Prisma.ModelName = {
|
|
|
325
314
|
State: 'State',
|
|
326
315
|
WebsocketConnection: 'WebsocketConnection',
|
|
327
316
|
AdminScriptExecution: 'AdminScriptExecution',
|
|
328
|
-
ScriptSchedule: 'ScriptSchedule'
|
|
329
|
-
UsageCounter: 'UsageCounter'
|
|
317
|
+
ScriptSchedule: 'ScriptSchedule'
|
|
330
318
|
};
|
|
331
319
|
/**
|
|
332
320
|
* Create the Client
|
|
@@ -380,13 +368,13 @@ const config = {
|
|
|
380
368
|
}
|
|
381
369
|
}
|
|
382
370
|
},
|
|
383
|
-
"inlineSchema": "// Frigg Framework - Prisma Schema (PostgreSQL)\n// PostgreSQL database schema for enterprise integration platform\n// Converted from MongoDB schema for relational database support\n\ngenerator client {\n provider = \"prisma-client-js\"\n output = \"../generated/prisma-postgresql\"\n binaryTargets = [\"native\", \"rhel-openssl-3.0.x\"] // native for local dev, rhel for Lambda deployment\n // Library engine (default since Prisma 3.x): Rust query engine loads as a\n // Node-API addon inside the same process. The binary engine forks a child\n // query-engine subprocess and communicates over a local HTTP/IPC pipe with\n // NO client-side timeout — a zombied child wedges the Node process until\n // Lambda's 900s cap. Switching to library eliminates that entire class of\n // silent hangs. See friggframework/frigg#580 for the investigation.\n engineType = \"library\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\n// ============================================================================\n// USER MODELS\n// ============================================================================\n\n/// User model with discriminator pattern support\n/// Replaces Mongoose discriminators (IndividualUser, OrganizationUser)\nmodel User {\n id Int @id @default(autoincrement())\n type UserType\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // IndividualUser fields (nullable for organizations)\n email String?\n username String?\n hashword String? // Bcrypt hashed password (handled in application layer)\n appUserId String?\n organizationId Int?\n\n // Self-referential relation for organization membership\n organization User? @relation(\"OrgMembers\", fields: [organizationId], references: [id], onDelete: NoAction, onUpdate: NoAction)\n members User[] @relation(\"OrgMembers\")\n\n // OrganizationUser fields (nullable for individuals)\n appOrgId String?\n name String?\n\n // Relations\n tokens Token[]\n credentials Credential[]\n entities Entity[]\n integrations Integration[]\n processes Process[]\n\n @@unique([username, appUserId])\n @@index([type])\n @@index([appUserId])\n @@index([organizationId])\n}\n\nenum UserType {\n INDIVIDUAL\n ORGANIZATION\n}\n\n// ============================================================================\n// AUTHENTICATION MODELS\n// ============================================================================\n\n/// Authentication tokens with expiration\n/// Bcrypt hashed tokens stored (handled in application layer)\nmodel Token {\n id Int @id @default(autoincrement())\n token String // Bcrypt hashed\n created DateTime @default(now())\n expires DateTime?\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@index([userId])\n @@index([expires])\n}\n\n// ============================================================================\n// CREDENTIAL & ENTITY MODELS\n// ============================================================================\n\n/// OAuth credentials and API tokens\n/// All sensitive data encrypted with KMS at rest\nmodel Credential {\n id Int @id @default(autoincrement())\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n authIsValid Boolean?\n externalId String?\n\n // Dynamic OAuth fields stored as JSON (encrypted via Prisma middleware)\n // Contains: access_token, refresh_token, domain, expires_in, token_type, etc.\n data Json @default(\"{}\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n entities Entity[]\n\n @@index([userId])\n @@index([externalId])\n}\n\n/// External service entities (API connections)\nmodel Entity {\n id Int @id @default(autoincrement())\n credentialId Int?\n credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull)\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n name String?\n moduleName String?\n externalId String?\n\n data Json @default(\"{}\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations - many-to-many with implicit join tables\n integrations Integration[]\n syncs Sync[]\n\n dataIdentifiers DataIdentifier[]\n associationObjects AssociationObject[]\n\n @@index([userId])\n @@index([externalId])\n @@index([moduleName])\n @@index([credentialId])\n}\n\n// ============================================================================\n// INTEGRATION MODELS\n// ============================================================================\n\n/// Main integration configuration and state\nmodel Integration {\n id Int @id @default(autoincrement())\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n status IntegrationStatus @default(IN_CREATION)\n\n // Configuration and version\n config Json? // Integration configuration object\n version String?\n\n // Entity references (many-to-many via implicit join table)\n entities Entity[]\n\n // Message arrays (stored as JSON)\n errors Json @default(\"[]\")\n warnings Json @default(\"[]\")\n info Json @default(\"[]\")\n logs Json @default(\"[]\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n associations Association[]\n syncs Sync[]\n mappings IntegrationMapping[]\n processes Process[]\n\n @@index([userId])\n @@index([status])\n}\n\nenum IntegrationStatus {\n ENABLED\n NEEDS_CONFIG\n PROCESSING\n DISABLED\n ERROR\n IN_CREATION\n IN_DELETION\n}\n\n/// Integration-specific data mappings\n/// All mapping data encrypted with KMS\nmodel IntegrationMapping {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n sourceId String?\n\n // Encrypted mapping data (handled via Prisma middleware)\n mapping Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([integrationId, sourceId])\n @@index([integrationId])\n @@index([sourceId])\n}\n\n// ============================================================================\n// SYNC MODELS\n// ============================================================================\n\n/// Bidirectional data synchronization tracking\nmodel Sync {\n id Int @id @default(autoincrement())\n integrationId Int?\n integration Integration? @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n\n // Entity references (many-to-many via implicit join table)\n entities Entity[]\n\n hash String\n name String\n\n // Data identifiers (extracted to separate model)\n dataIdentifiers DataIdentifier[]\n\n @@index([integrationId])\n @@index([hash])\n @@index([name])\n}\n\n/// Data identifier for sync operations\n/// Replaces nested array structure in Mongoose\nmodel DataIdentifier {\n id Int @id @default(autoincrement())\n syncId Int?\n sync Sync? @relation(fields: [syncId], references: [id], onDelete: Cascade)\n entityId Int\n entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)\n\n // Identifier data (can be any structure)\n idData Json\n\n hash String\n\n @@index([syncId])\n @@index([entityId])\n @@index([hash])\n}\n\n// ============================================================================\n// ASSOCIATION MODELS\n// ============================================================================\n\n/// Entity associations with cardinality tracking\nmodel Association {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n name String\n type AssociationType\n primaryObject String\n\n // Associated objects (extracted to separate model)\n objects AssociationObject[]\n\n @@index([integrationId])\n @@index([name])\n}\n\n/// Association object entry\n/// Replaces nested array structure in Mongoose\nmodel AssociationObject {\n id Int @id @default(autoincrement())\n associationId Int\n association Association @relation(fields: [associationId], references: [id], onDelete: Cascade)\n entityId Int\n entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)\n objectType String\n objId String\n metadata Json? // Optional metadata\n\n @@index([associationId])\n @@index([entityId])\n}\n\nenum AssociationType {\n ONE_TO_MANY\n ONE_TO_ONE\n MANY_TO_ONE\n}\n\n// ============================================================================\n// PROCESS MODELS\n// ============================================================================\n\n/// Generic Process Model - tracks any long-running operation\n/// Used for: CRM syncs, data migrations, bulk operations, etc.\nmodel Process {\n id Int @id @default(autoincrement())\n\n // Core references\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n\n // Process identification\n name String // e.g., \"zoho-crm-contact-sync\", \"pipedrive-lead-sync\"\n type String // e.g., \"CRM_SYNC\", \"DATA_MIGRATION\", \"BULK_OPERATION\"\n\n // State machine\n state String // Current state (integration-defined states)\n\n // Flexible storage\n context Json @default(\"{}\") // Process-specific data (pagination, metadata, etc.)\n results Json @default(\"{}\") // Process results and metrics\n\n // Hierarchy support - self-referential relation\n parentProcessId Int?\n parentProcess Process? @relation(\"ProcessHierarchy\", fields: [parentProcessId], references: [id], onDelete: SetNull)\n childProcesses Process[] @relation(\"ProcessHierarchy\")\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@index([integrationId])\n @@index([type])\n @@index([state])\n @@index([name])\n @@index([parentProcessId])\n}\n\n// ============================================================================\n// UTILITY MODELS\n// ============================================================================\n\n/// Generic state storage\nmodel State {\n id Int @id @default(autoincrement())\n state Json?\n}\n\n/// AWS API Gateway WebSocket connection tracking\nmodel WebsocketConnection {\n id Int @id @default(autoincrement())\n connectionId String?\n\n @@index([connectionId])\n}\n\n// ============================================================================\n// ADMIN PROCESS MODELS\n// ============================================================================\n\n/// Admin process state machine\nenum AdminScriptExecutionState {\n PENDING\n RUNNING\n COMPLETED\n FAILED\n}\n\n/// Admin process tracking (like Process but without user/integration FK)\n/// Used for: admin scripts, db migrations, system maintenance tasks\nmodel AdminScriptExecution {\n id Int @id @default(autoincrement())\n name String // e.g., \"oauth-token-refresh\", \"db-migration-xyz\"\n type String // e.g., \"ADMIN_SCRIPT\", \"DB_MIGRATION\"\n\n // State machine\n state AdminScriptExecutionState @default(PENDING)\n\n // Flexible storage (mirrors Process model pattern)\n context Json @default(\"{}\") // input, trigger, audit info, script version\n results Json @default(\"{}\") // output, logs, metrics, errors\n\n // Hierarchy support - self-referential relation\n parentExecutionId Int?\n parentExecution AdminScriptExecution? @relation(\"AdminScriptExecutionHierarchy\", fields: [parentExecutionId], references: [id], onDelete: SetNull)\n childExecutions AdminScriptExecution[] @relation(\"AdminScriptExecutionHierarchy\")\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([name, createdAt(sort: Desc)])\n @@index([state])\n @@index([type])\n}\n\n/// Script scheduling configuration for hybrid scheduling (SQS + EventBridge)\nmodel ScriptSchedule {\n id Int @id @default(autoincrement())\n scriptName String @unique\n enabled Boolean @default(false)\n cronExpression String?\n timezone String @default(\"UTC\")\n lastTriggeredAt DateTime?\n nextTriggerAt DateTime?\n\n // External Scheduler (e.g., AWS EventBridge Scheduler)\n externalScheduleId String?\n externalScheduleName String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([enabled])\n}\n\n/// Durable usage counters, deliberately isolated: no userId and no foreign key\n/// to Integration, so a user-scoped query can never return a usage row and usage\n/// history survives integration deletion.\nmodel UsageCounter {\n id Int @id @default(autoincrement())\n\n integrationId String\n integrationType String\n metric String\n window String // e.g. \"day:2026-07-05\" or \"hour:2026-07-05T14\"\n value BigInt @default(0) // BigInt: a busy day/hour counter can exceed 32-bit\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([integrationId, integrationType, metric, window])\n // Covers totals() (metric + window prefix/range, no integrationType filter).\n @@index([metric, window])\n // Covers series() (metric + integrationType + window range).\n @@index([metric, integrationType, window])\n}\n",
|
|
384
|
-
"inlineSchemaHash": "
|
|
371
|
+
"inlineSchema": "// Frigg Framework - Prisma Schema (PostgreSQL)\n// PostgreSQL database schema for enterprise integration platform\n// Converted from MongoDB schema for relational database support\n\ngenerator client {\n provider = \"prisma-client-js\"\n output = \"../generated/prisma-postgresql\"\n binaryTargets = [\"native\", \"rhel-openssl-3.0.x\"] // native for local dev, rhel for Lambda deployment\n // Library engine (default since Prisma 3.x): Rust query engine loads as a\n // Node-API addon inside the same process. The binary engine forks a child\n // query-engine subprocess and communicates over a local HTTP/IPC pipe with\n // NO client-side timeout — a zombied child wedges the Node process until\n // Lambda's 900s cap. Switching to library eliminates that entire class of\n // silent hangs. See friggframework/frigg#580 for the investigation.\n engineType = \"library\"\n}\n\ndatasource db {\n provider = \"postgresql\"\n url = env(\"DATABASE_URL\")\n}\n\n// ============================================================================\n// USER MODELS\n// ============================================================================\n\n/// User model with discriminator pattern support\n/// Replaces Mongoose discriminators (IndividualUser, OrganizationUser)\nmodel User {\n id Int @id @default(autoincrement())\n type UserType\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // IndividualUser fields (nullable for organizations)\n email String?\n username String?\n hashword String? // Bcrypt hashed password (handled in application layer)\n appUserId String?\n organizationId Int?\n\n // Self-referential relation for organization membership\n organization User? @relation(\"OrgMembers\", fields: [organizationId], references: [id], onDelete: NoAction, onUpdate: NoAction)\n members User[] @relation(\"OrgMembers\")\n\n // OrganizationUser fields (nullable for individuals)\n appOrgId String?\n name String?\n\n // Relations\n tokens Token[]\n credentials Credential[]\n entities Entity[]\n integrations Integration[]\n processes Process[]\n\n @@unique([username, appUserId])\n @@index([type])\n @@index([appUserId])\n @@index([organizationId])\n}\n\nenum UserType {\n INDIVIDUAL\n ORGANIZATION\n}\n\n// ============================================================================\n// AUTHENTICATION MODELS\n// ============================================================================\n\n/// Authentication tokens with expiration\n/// Bcrypt hashed tokens stored (handled in application layer)\nmodel Token {\n id Int @id @default(autoincrement())\n token String // Bcrypt hashed\n created DateTime @default(now())\n expires DateTime?\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n\n @@index([userId])\n @@index([expires])\n}\n\n// ============================================================================\n// CREDENTIAL & ENTITY MODELS\n// ============================================================================\n\n/// OAuth credentials and API tokens\n/// All sensitive data encrypted with KMS at rest\nmodel Credential {\n id Int @id @default(autoincrement())\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n authIsValid Boolean?\n externalId String?\n\n // Dynamic OAuth fields stored as JSON (encrypted via Prisma middleware)\n // Contains: access_token, refresh_token, domain, expires_in, token_type, etc.\n data Json @default(\"{}\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n entities Entity[]\n\n @@index([userId])\n @@index([externalId])\n}\n\n/// External service entities (API connections)\nmodel Entity {\n id Int @id @default(autoincrement())\n credentialId Int?\n credential Credential? @relation(fields: [credentialId], references: [id], onDelete: SetNull)\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n name String?\n moduleName String?\n externalId String?\n\n data Json @default(\"{}\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations - many-to-many with implicit join tables\n integrations Integration[]\n syncs Sync[]\n\n dataIdentifiers DataIdentifier[]\n associationObjects AssociationObject[]\n\n @@index([userId])\n @@index([externalId])\n @@index([moduleName])\n @@index([credentialId])\n}\n\n// ============================================================================\n// INTEGRATION MODELS\n// ============================================================================\n\n/// Main integration configuration and state\nmodel Integration {\n id Int @id @default(autoincrement())\n userId Int?\n user User? @relation(fields: [userId], references: [id], onDelete: Cascade)\n status IntegrationStatus @default(IN_CREATION)\n\n // Configuration and version\n config Json? // Integration configuration object\n version String?\n\n // Entity references (many-to-many via implicit join table)\n entities Entity[]\n\n // Message arrays (stored as JSON)\n errors Json @default(\"[]\")\n warnings Json @default(\"[]\")\n info Json @default(\"[]\")\n logs Json @default(\"[]\")\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n // Relations\n associations Association[]\n syncs Sync[]\n mappings IntegrationMapping[]\n processes Process[]\n\n @@index([userId])\n @@index([status])\n}\n\nenum IntegrationStatus {\n ENABLED\n NEEDS_CONFIG\n PROCESSING\n DISABLED\n ERROR\n IN_CREATION\n IN_DELETION\n}\n\n/// Integration-specific data mappings\n/// All mapping data encrypted with KMS\nmodel IntegrationMapping {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n sourceId String?\n\n // Encrypted mapping data (handled via Prisma middleware)\n mapping Json?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([integrationId, sourceId])\n @@index([integrationId])\n @@index([sourceId])\n}\n\n// ============================================================================\n// SYNC MODELS\n// ============================================================================\n\n/// Bidirectional data synchronization tracking\nmodel Sync {\n id Int @id @default(autoincrement())\n integrationId Int?\n integration Integration? @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n\n // Entity references (many-to-many via implicit join table)\n entities Entity[]\n\n hash String\n name String\n\n // Data identifiers (extracted to separate model)\n dataIdentifiers DataIdentifier[]\n\n @@index([integrationId])\n @@index([hash])\n @@index([name])\n}\n\n/// Data identifier for sync operations\n/// Replaces nested array structure in Mongoose\nmodel DataIdentifier {\n id Int @id @default(autoincrement())\n syncId Int?\n sync Sync? @relation(fields: [syncId], references: [id], onDelete: Cascade)\n entityId Int\n entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)\n\n // Identifier data (can be any structure)\n idData Json\n\n hash String\n\n @@index([syncId])\n @@index([entityId])\n @@index([hash])\n}\n\n// ============================================================================\n// ASSOCIATION MODELS\n// ============================================================================\n\n/// Entity associations with cardinality tracking\nmodel Association {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n name String\n type AssociationType\n primaryObject String\n\n // Associated objects (extracted to separate model)\n objects AssociationObject[]\n\n @@index([integrationId])\n @@index([name])\n}\n\n/// Association object entry\n/// Replaces nested array structure in Mongoose\nmodel AssociationObject {\n id Int @id @default(autoincrement())\n associationId Int\n association Association @relation(fields: [associationId], references: [id], onDelete: Cascade)\n entityId Int\n entity Entity @relation(fields: [entityId], references: [id], onDelete: Cascade)\n objectType String\n objId String\n metadata Json? // Optional metadata\n\n @@index([associationId])\n @@index([entityId])\n}\n\nenum AssociationType {\n ONE_TO_MANY\n ONE_TO_ONE\n MANY_TO_ONE\n}\n\n// ============================================================================\n// PROCESS MODELS\n// ============================================================================\n\n/// Generic Process Model - tracks any long-running operation\n/// Used for: CRM syncs, data migrations, bulk operations, etc.\nmodel Process {\n id Int @id @default(autoincrement())\n\n // Core references\n userId Int\n user User @relation(fields: [userId], references: [id], onDelete: Cascade)\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n\n // Process identification\n name String // e.g., \"zoho-crm-contact-sync\", \"pipedrive-lead-sync\"\n type String // e.g., \"CRM_SYNC\", \"DATA_MIGRATION\", \"BULK_OPERATION\"\n\n // State machine\n state String // Current state (integration-defined states)\n\n // Flexible storage\n context Json @default(\"{}\") // Process-specific data (pagination, metadata, etc.)\n results Json @default(\"{}\") // Process results and metrics\n\n // Hierarchy support - self-referential relation\n parentProcessId Int?\n parentProcess Process? @relation(\"ProcessHierarchy\", fields: [parentProcessId], references: [id], onDelete: SetNull)\n childProcesses Process[] @relation(\"ProcessHierarchy\")\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([userId])\n @@index([integrationId])\n @@index([type])\n @@index([state])\n @@index([name])\n @@index([parentProcessId])\n}\n\n// ============================================================================\n// UTILITY MODELS\n// ============================================================================\n\n/// Generic state storage\nmodel State {\n id Int @id @default(autoincrement())\n state Json?\n}\n\n/// AWS API Gateway WebSocket connection tracking\nmodel WebsocketConnection {\n id Int @id @default(autoincrement())\n connectionId String?\n\n @@index([connectionId])\n}\n\n// ============================================================================\n// ADMIN PROCESS MODELS\n// ============================================================================\n\n/// Admin process state machine\nenum AdminScriptExecutionState {\n PENDING\n RUNNING\n COMPLETED\n FAILED\n}\n\n/// Admin process tracking (like Process but without user/integration FK)\n/// Used for: admin scripts, db migrations, system maintenance tasks\nmodel AdminScriptExecution {\n id Int @id @default(autoincrement())\n name String // e.g., \"oauth-token-refresh\", \"db-migration-xyz\"\n type String // e.g., \"ADMIN_SCRIPT\", \"DB_MIGRATION\"\n\n // State machine\n state AdminScriptExecutionState @default(PENDING)\n\n // Flexible storage (mirrors Process model pattern)\n context Json @default(\"{}\") // input, trigger, audit info, script version\n results Json @default(\"{}\") // output, logs, metrics, errors\n\n // Hierarchy support - self-referential relation\n parentExecutionId Int?\n parentExecution AdminScriptExecution? @relation(\"AdminScriptExecutionHierarchy\", fields: [parentExecutionId], references: [id], onDelete: SetNull)\n childExecutions AdminScriptExecution[] @relation(\"AdminScriptExecutionHierarchy\")\n\n // Timestamps\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([name, createdAt(sort: Desc)])\n @@index([state])\n @@index([type])\n}\n\n/// Script scheduling configuration for hybrid scheduling (SQS + EventBridge)\nmodel ScriptSchedule {\n id Int @id @default(autoincrement())\n scriptName String @unique\n enabled Boolean @default(false)\n cronExpression String?\n timezone String @default(\"UTC\")\n lastTriggeredAt DateTime?\n nextTriggerAt DateTime?\n\n // External Scheduler (e.g., AWS EventBridge Scheduler)\n externalScheduleId String?\n externalScheduleName String?\n\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@index([enabled])\n}\n",
|
|
372
|
+
"inlineSchemaHash": "f61bdd340fe2a680795a08432905ff7bded4fdb9d9284e12da04215013cc2b20",
|
|
385
373
|
"copyEngine": true
|
|
386
374
|
}
|
|
387
375
|
config.dirname = '/'
|
|
388
376
|
|
|
389
|
-
config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"type\",\"kind\":\"enum\",\"type\":\"UserType\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"username\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"hashword\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appUserId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organizationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"organization\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"OrgMembers\"},{\"name\":\"members\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"OrgMembers\"},{\"name\":\"appOrgId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"tokens\",\"kind\":\"object\",\"type\":\"Token\",\"relationName\":\"TokenToUser\"},{\"name\":\"credentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToUser\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"EntityToUser\"},{\"name\":\"integrations\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToUser\"},{\"name\":\"processes\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"ProcessToUser\"}],\"dbName\":null},\"Token\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"created\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"expires\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"TokenToUser\"}],\"dbName\":null},\"Credential\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"CredentialToUser\"},{\"name\":\"authIsValid\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"externalId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"data\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"CredentialToEntity\"}],\"dbName\":null},\"Entity\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"credentialId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"credential\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToEntity\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"EntityToUser\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"moduleName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"externalId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"data\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"integrations\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"EntityToIntegration\"},{\"name\":\"syncs\",\"kind\":\"object\",\"type\":\"Sync\",\"relationName\":\"EntityToSync\"},{\"name\":\"dataIdentifiers\",\"kind\":\"object\",\"type\":\"DataIdentifier\",\"relationName\":\"DataIdentifierToEntity\"},{\"name\":\"associationObjects\",\"kind\":\"object\",\"type\":\"AssociationObject\",\"relationName\":\"AssociationObjectToEntity\"}],\"dbName\":null},\"Integration\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"IntegrationToUser\"},{\"name\":\"status\",\"kind\":\"enum\",\"type\":\"IntegrationStatus\"},{\"name\":\"config\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"version\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"EntityToIntegration\"},{\"name\":\"errors\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"warnings\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"info\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"logs\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"associations\",\"kind\":\"object\",\"type\":\"Association\",\"relationName\":\"AssociationToIntegration\"},{\"name\":\"syncs\",\"kind\":\"object\",\"type\":\"Sync\",\"relationName\":\"IntegrationToSync\"},{\"name\":\"mappings\",\"kind\":\"object\",\"type\":\"IntegrationMapping\",\"relationName\":\"IntegrationToIntegrationMapping\"},{\"name\":\"processes\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"IntegrationToProcess\"}],\"dbName\":null},\"IntegrationMapping\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToIntegrationMapping\"},{\"name\":\"sourceId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"mapping\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Sync\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToSync\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"EntityToSync\"},{\"name\":\"hash\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"dataIdentifiers\",\"kind\":\"object\",\"type\":\"DataIdentifier\",\"relationName\":\"DataIdentifierToSync\"}],\"dbName\":null},\"DataIdentifier\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"syncId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"sync\",\"kind\":\"object\",\"type\":\"Sync\",\"relationName\":\"DataIdentifierToSync\"},{\"name\":\"entityId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"entity\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"DataIdentifierToEntity\"},{\"name\":\"idData\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"hash\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null},\"Association\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"AssociationToIntegration\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"enum\",\"type\":\"AssociationType\"},{\"name\":\"primaryObject\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"objects\",\"kind\":\"object\",\"type\":\"AssociationObject\",\"relationName\":\"AssociationToAssociationObject\"}],\"dbName\":null},\"AssociationObject\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"associationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"association\",\"kind\":\"object\",\"type\":\"Association\",\"relationName\":\"AssociationToAssociationObject\"},{\"name\":\"entityId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"entity\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"AssociationObjectToEntity\"},{\"name\":\"objectType\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"objId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"metadata\",\"kind\":\"scalar\",\"type\":\"Json\"}],\"dbName\":null},\"Process\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ProcessToUser\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToProcess\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"state\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"context\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"results\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"parentProcessId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"parentProcess\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"ProcessHierarchy\"},{\"name\":\"childProcesses\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"ProcessHierarchy\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"State\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"state\",\"kind\":\"scalar\",\"type\":\"Json\"}],\"dbName\":null},\"WebsocketConnection\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"connectionId\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null},\"AdminScriptExecution\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"state\",\"kind\":\"enum\",\"type\":\"AdminScriptExecutionState\"},{\"name\":\"context\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"results\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"parentExecutionId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"parentExecution\",\"kind\":\"object\",\"type\":\"AdminScriptExecution\",\"relationName\":\"AdminScriptExecutionHierarchy\"},{\"name\":\"childExecutions\",\"kind\":\"object\",\"type\":\"AdminScriptExecution\",\"relationName\":\"AdminScriptExecutionHierarchy\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ScriptSchedule\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"scriptName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"enabled\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"cronExpression\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"timezone\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastTriggeredAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"nextTriggerAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"externalScheduleId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"externalScheduleName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"UsageCounter\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"integrationType\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"metric\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"window\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"value\",\"kind\":\"scalar\",\"type\":\"BigInt\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
|
|
377
|
+
config.runtimeDataModel = JSON.parse("{\"models\":{\"User\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"type\",\"kind\":\"enum\",\"type\":\"UserType\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"email\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"username\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"hashword\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"appUserId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"organizationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"organization\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"OrgMembers\"},{\"name\":\"members\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"OrgMembers\"},{\"name\":\"appOrgId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"tokens\",\"kind\":\"object\",\"type\":\"Token\",\"relationName\":\"TokenToUser\"},{\"name\":\"credentials\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToUser\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"EntityToUser\"},{\"name\":\"integrations\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToUser\"},{\"name\":\"processes\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"ProcessToUser\"}],\"dbName\":null},\"Token\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"token\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"created\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"expires\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"TokenToUser\"}],\"dbName\":null},\"Credential\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"CredentialToUser\"},{\"name\":\"authIsValid\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"externalId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"data\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"CredentialToEntity\"}],\"dbName\":null},\"Entity\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"credentialId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"credential\",\"kind\":\"object\",\"type\":\"Credential\",\"relationName\":\"CredentialToEntity\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"EntityToUser\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"moduleName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"externalId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"data\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"integrations\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"EntityToIntegration\"},{\"name\":\"syncs\",\"kind\":\"object\",\"type\":\"Sync\",\"relationName\":\"EntityToSync\"},{\"name\":\"dataIdentifiers\",\"kind\":\"object\",\"type\":\"DataIdentifier\",\"relationName\":\"DataIdentifierToEntity\"},{\"name\":\"associationObjects\",\"kind\":\"object\",\"type\":\"AssociationObject\",\"relationName\":\"AssociationObjectToEntity\"}],\"dbName\":null},\"Integration\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"IntegrationToUser\"},{\"name\":\"status\",\"kind\":\"enum\",\"type\":\"IntegrationStatus\"},{\"name\":\"config\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"version\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"EntityToIntegration\"},{\"name\":\"errors\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"warnings\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"info\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"logs\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"associations\",\"kind\":\"object\",\"type\":\"Association\",\"relationName\":\"AssociationToIntegration\"},{\"name\":\"syncs\",\"kind\":\"object\",\"type\":\"Sync\",\"relationName\":\"IntegrationToSync\"},{\"name\":\"mappings\",\"kind\":\"object\",\"type\":\"IntegrationMapping\",\"relationName\":\"IntegrationToIntegrationMapping\"},{\"name\":\"processes\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"IntegrationToProcess\"}],\"dbName\":null},\"IntegrationMapping\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToIntegrationMapping\"},{\"name\":\"sourceId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"mapping\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"Sync\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToSync\"},{\"name\":\"entities\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"EntityToSync\"},{\"name\":\"hash\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"dataIdentifiers\",\"kind\":\"object\",\"type\":\"DataIdentifier\",\"relationName\":\"DataIdentifierToSync\"}],\"dbName\":null},\"DataIdentifier\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"syncId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"sync\",\"kind\":\"object\",\"type\":\"Sync\",\"relationName\":\"DataIdentifierToSync\"},{\"name\":\"entityId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"entity\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"DataIdentifierToEntity\"},{\"name\":\"idData\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"hash\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null},\"Association\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"AssociationToIntegration\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"enum\",\"type\":\"AssociationType\"},{\"name\":\"primaryObject\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"objects\",\"kind\":\"object\",\"type\":\"AssociationObject\",\"relationName\":\"AssociationToAssociationObject\"}],\"dbName\":null},\"AssociationObject\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"associationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"association\",\"kind\":\"object\",\"type\":\"Association\",\"relationName\":\"AssociationToAssociationObject\"},{\"name\":\"entityId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"entity\",\"kind\":\"object\",\"type\":\"Entity\",\"relationName\":\"AssociationObjectToEntity\"},{\"name\":\"objectType\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"objId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"metadata\",\"kind\":\"scalar\",\"type\":\"Json\"}],\"dbName\":null},\"Process\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"userId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"user\",\"kind\":\"object\",\"type\":\"User\",\"relationName\":\"ProcessToUser\"},{\"name\":\"integrationId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"integration\",\"kind\":\"object\",\"type\":\"Integration\",\"relationName\":\"IntegrationToProcess\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"state\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"context\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"results\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"parentProcessId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"parentProcess\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"ProcessHierarchy\"},{\"name\":\"childProcesses\",\"kind\":\"object\",\"type\":\"Process\",\"relationName\":\"ProcessHierarchy\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"State\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"state\",\"kind\":\"scalar\",\"type\":\"Json\"}],\"dbName\":null},\"WebsocketConnection\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"connectionId\",\"kind\":\"scalar\",\"type\":\"String\"}],\"dbName\":null},\"AdminScriptExecution\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"name\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"type\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"state\",\"kind\":\"enum\",\"type\":\"AdminScriptExecutionState\"},{\"name\":\"context\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"results\",\"kind\":\"scalar\",\"type\":\"Json\"},{\"name\":\"parentExecutionId\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"parentExecution\",\"kind\":\"object\",\"type\":\"AdminScriptExecution\",\"relationName\":\"AdminScriptExecutionHierarchy\"},{\"name\":\"childExecutions\",\"kind\":\"object\",\"type\":\"AdminScriptExecution\",\"relationName\":\"AdminScriptExecutionHierarchy\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null},\"ScriptSchedule\":{\"fields\":[{\"name\":\"id\",\"kind\":\"scalar\",\"type\":\"Int\"},{\"name\":\"scriptName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"enabled\",\"kind\":\"scalar\",\"type\":\"Boolean\"},{\"name\":\"cronExpression\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"timezone\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"lastTriggeredAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"nextTriggerAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"externalScheduleId\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"externalScheduleName\",\"kind\":\"scalar\",\"type\":\"String\"},{\"name\":\"createdAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"},{\"name\":\"updatedAt\",\"kind\":\"scalar\",\"type\":\"DateTime\"}],\"dbName\":null}},\"enums\":{},\"types\":{}}")
|
|
390
378
|
defineDmmfProperty(exports.Prisma, config.runtimeDataModel)
|
|
391
379
|
config.engineWasm = {
|
|
392
380
|
getRuntime: async () => require('./query_engine_bg.js'),
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
const { findNearestBackendPackageJson } = require('@friggframework/core/utils');
|
|
2
2
|
const path = require('node:path');
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
|
-
const { resolveTelemetryConfig } = require('../telemetry/telemetry-config');
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Loads the App definition from the nearest backend package
|
|
8
7
|
* @function loadAppDefinition
|
|
9
8
|
* @description Searches for the nearest backend package.json, loads the corresponding index.js file,
|
|
10
9
|
* and extracts the application definition containing integrations and user configuration.
|
|
11
|
-
* @returns {{integrations: Array<object>, userConfig: object | null, adminScripts: Array<object>, admin: object
|
|
10
|
+
* @returns {{integrations: Array<object>, userConfig: object | null, adminScripts: Array<object>, admin: object}} An object containing the application definition.
|
|
12
11
|
* @throws {Error} Throws error if backend package.json cannot be found.
|
|
13
12
|
* @throws {Error} Throws error if index.js file cannot be found in the backend directory.
|
|
14
13
|
* @example
|
|
15
|
-
* const { integrations, userConfig
|
|
14
|
+
* const { integrations, userConfig } = loadAppDefinition();
|
|
16
15
|
* console.log(`Found ${integrations.length} integrations`);
|
|
17
16
|
*/
|
|
18
17
|
function loadAppDefinition() {
|
|
@@ -36,29 +35,7 @@ function loadAppDefinition() {
|
|
|
36
35
|
adminScripts = [],
|
|
37
36
|
admin = {},
|
|
38
37
|
} = appDefinition;
|
|
39
|
-
|
|
40
|
-
// Degrade consistently: an invalid telemetry block must never take down a
|
|
41
|
-
// router bundle that loads the app definition at module scope (telemetry is
|
|
42
|
-
// never allowed to break a handler). The singletons apply the same
|
|
43
|
-
// fall-back, so all consumers behave identically.
|
|
44
|
-
let telemetry;
|
|
45
|
-
try {
|
|
46
|
-
telemetry = resolveTelemetryConfig(appDefinition);
|
|
47
|
-
} catch (error) {
|
|
48
|
-
console.warn(
|
|
49
|
-
`[Frigg][telemetry] invalid telemetry config, defaulting to disabled: ${
|
|
50
|
-
error && error.message
|
|
51
|
-
}`
|
|
52
|
-
);
|
|
53
|
-
telemetry = {
|
|
54
|
-
exporter: { type: 'none' },
|
|
55
|
-
northStar: null,
|
|
56
|
-
sampleRatio: 1,
|
|
57
|
-
subscribers: [],
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
return { integrations, userConfig, adminScripts, admin, telemetry };
|
|
38
|
+
return { integrations, userConfig, adminScripts, admin };
|
|
62
39
|
}
|
|
63
40
|
|
|
64
41
|
module.exports = {
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
const { instrumentHandler } = require('../telemetry/instrument-handler');
|
|
2
|
-
|
|
3
1
|
/**
|
|
4
2
|
* Lightweight dispatcher that executes integration event handlers.
|
|
5
3
|
* @param {import('../integrations/integration-base')} integrationInstance Pre-instantiated integration.
|
|
@@ -13,24 +11,22 @@ class IntegrationEventDispatcher {
|
|
|
13
11
|
}
|
|
14
12
|
|
|
15
13
|
async dispatchHttp({ event, req, res, next }) {
|
|
16
|
-
|
|
17
|
-
handler.call(instance, { req, res, next })
|
|
18
|
-
);
|
|
19
|
-
}
|
|
14
|
+
const instance = this.integrationInstance;
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
16
|
+
const handler = this.findEventHandler(instance, event);
|
|
17
|
+
|
|
18
|
+
if (!handler) {
|
|
19
|
+
const name =
|
|
20
|
+
instance.constructor?.Definition?.name || 'integration';
|
|
21
|
+
throw new Error(`Event ${event} not registered for ${name}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return await handler.call(instance, { req, res, next });
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
* Resolve + invoke a handler, auto-instrumented. This
|
|
29
|
-
* is the seam for queue/webhook/defined-route dispatch; the `this.on` path
|
|
30
|
-
* (user actions, lifecycle) is instrumented in IntegrationBase.send().
|
|
31
|
-
*/
|
|
32
|
-
async _dispatch(event, invoke) {
|
|
27
|
+
async dispatchJob({ event, data, context }) {
|
|
33
28
|
const instance = this.integrationInstance;
|
|
29
|
+
|
|
34
30
|
const handler = this.findEventHandler(instance, event);
|
|
35
31
|
|
|
36
32
|
if (!handler) {
|
|
@@ -39,26 +35,16 @@ class IntegrationEventDispatcher {
|
|
|
39
35
|
throw new Error(`Event ${event} not registered for ${name}`);
|
|
40
36
|
}
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return instrumentHandler(
|
|
45
|
-
instance.telemetry,
|
|
46
|
-
{ event, eventType: eventDef?.type },
|
|
47
|
-
() => invoke(instance, handler)
|
|
48
|
-
);
|
|
38
|
+
return await handler.call(instance, { data, context });
|
|
49
39
|
}
|
|
50
40
|
|
|
51
41
|
findEventHandler(integration, event) {
|
|
52
|
-
return this.findEventDef(integration, event)?.handler || null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
findEventDef(integration, event) {
|
|
56
42
|
if (integration.events && integration.events[event]) {
|
|
57
|
-
return integration.events[event];
|
|
43
|
+
return integration.events[event].handler;
|
|
58
44
|
}
|
|
59
45
|
|
|
60
46
|
if (integration.defaultEvents && integration.defaultEvents[event]) {
|
|
61
|
-
return integration.defaultEvents[event];
|
|
47
|
+
return integration.defaultEvents[event].handler;
|
|
62
48
|
}
|
|
63
49
|
|
|
64
50
|
return null;
|
|
@@ -1,41 +1,29 @@
|
|
|
1
1
|
const { createAppHandler } = require('./../app-handler-helpers');
|
|
2
2
|
const { loadAppDefinition } = require('../app-definition-loader');
|
|
3
3
|
const { Router } = require('express');
|
|
4
|
-
const {
|
|
5
|
-
IntegrationEventDispatcher,
|
|
6
|
-
} = require('../integration-event-dispatcher');
|
|
4
|
+
const { IntegrationEventDispatcher } = require('../integration-event-dispatcher');
|
|
7
5
|
|
|
8
6
|
const handlers = {};
|
|
9
7
|
const { integrations: integrationClasses } = loadAppDefinition();
|
|
10
8
|
|
|
11
|
-
// `webhooks.received` is counted at the DB-connected ON_WEBHOOK dispatch (see
|
|
12
|
-
// usage-rollup-subscriber), not in this DB-free receipt handler.
|
|
13
|
-
|
|
14
9
|
for (const IntegrationClass of integrationClasses) {
|
|
15
10
|
const webhookConfig = IntegrationClass.Definition.webhooks;
|
|
16
11
|
|
|
17
12
|
// Skip if webhooks not enabled
|
|
18
|
-
if (
|
|
19
|
-
!webhookConfig ||
|
|
20
|
-
(typeof webhookConfig === 'object' && !webhookConfig.enabled)
|
|
21
|
-
) {
|
|
13
|
+
if (!webhookConfig || (typeof webhookConfig === 'object' && !webhookConfig.enabled)) {
|
|
22
14
|
continue;
|
|
23
15
|
}
|
|
24
16
|
|
|
25
17
|
const router = Router();
|
|
26
18
|
const basePath = `/api/${IntegrationClass.Definition.name}-integration/webhooks`;
|
|
27
19
|
|
|
28
|
-
console.log(
|
|
29
|
-
`\n│ Configuring webhook routes for ${IntegrationClass.Definition.name}:`
|
|
30
|
-
);
|
|
20
|
+
console.log(`\n│ Configuring webhook routes for ${IntegrationClass.Definition.name}:`);
|
|
31
21
|
|
|
32
22
|
// General webhook route (no integration ID)
|
|
33
23
|
router.post(basePath, async (req, res, next) => {
|
|
34
24
|
try {
|
|
35
25
|
const integrationInstance = new IntegrationClass();
|
|
36
|
-
const dispatcher = new IntegrationEventDispatcher(
|
|
37
|
-
integrationInstance
|
|
38
|
-
);
|
|
26
|
+
const dispatcher = new IntegrationEventDispatcher(integrationInstance);
|
|
39
27
|
await dispatcher.dispatchHttp({
|
|
40
28
|
event: 'WEBHOOK_RECEIVED',
|
|
41
29
|
req,
|
|
@@ -52,9 +40,7 @@ for (const IntegrationClass of integrationClasses) {
|
|
|
52
40
|
router.post(`${basePath}/:integrationId`, async (req, res, next) => {
|
|
53
41
|
try {
|
|
54
42
|
const integrationInstance = new IntegrationClass();
|
|
55
|
-
const dispatcher = new IntegrationEventDispatcher(
|
|
56
|
-
integrationInstance
|
|
57
|
-
);
|
|
43
|
+
const dispatcher = new IntegrationEventDispatcher(integrationInstance);
|
|
58
44
|
await dispatcher.dispatchHttp({
|
|
59
45
|
event: 'WEBHOOK_RECEIVED',
|
|
60
46
|
req,
|
|
@@ -72,9 +58,10 @@ for (const IntegrationClass of integrationClasses) {
|
|
|
72
58
|
handler: createAppHandler(
|
|
73
59
|
`HTTP Event: ${IntegrationClass.Definition.name} Webhook`,
|
|
74
60
|
router,
|
|
75
|
-
false
|
|
61
|
+
false // shouldUseDatabase = false
|
|
76
62
|
),
|
|
77
63
|
};
|
|
78
64
|
}
|
|
79
65
|
|
|
80
66
|
module.exports = { handlers };
|
|
67
|
+
|
package/index.js
CHANGED
|
@@ -30,7 +30,9 @@ const {
|
|
|
30
30
|
const {
|
|
31
31
|
GetUserFromAdopterJwt,
|
|
32
32
|
} = require('./user/use-cases/get-user-from-adopter-jwt');
|
|
33
|
-
const {
|
|
33
|
+
const {
|
|
34
|
+
AuthenticateUser,
|
|
35
|
+
} = require('./user/use-cases/authenticate-user');
|
|
34
36
|
|
|
35
37
|
const {
|
|
36
38
|
CredentialRepository,
|
|
@@ -41,14 +43,18 @@ const {
|
|
|
41
43
|
const {
|
|
42
44
|
IntegrationMappingRepository,
|
|
43
45
|
} = require('./integrations/repositories/integration-mapping-repository');
|
|
44
|
-
const {
|
|
46
|
+
const {
|
|
47
|
+
CreateProcess,
|
|
48
|
+
} = require('./integrations/use-cases/create-process');
|
|
45
49
|
const {
|
|
46
50
|
UpdateProcessState,
|
|
47
51
|
} = require('./integrations/use-cases/update-process-state');
|
|
48
52
|
const {
|
|
49
53
|
UpdateProcessMetrics,
|
|
50
54
|
} = require('./integrations/use-cases/update-process-metrics');
|
|
51
|
-
const {
|
|
55
|
+
const {
|
|
56
|
+
GetProcess,
|
|
57
|
+
} = require('./integrations/use-cases/get-process');
|
|
52
58
|
const { Cryptor } = require('./encrypt');
|
|
53
59
|
const {
|
|
54
60
|
BaseError,
|
|
@@ -69,12 +75,6 @@ const {
|
|
|
69
75
|
createReportingRouter,
|
|
70
76
|
createReportingRepository,
|
|
71
77
|
} = require('./reporting/index');
|
|
72
|
-
const {
|
|
73
|
-
createTelemetry,
|
|
74
|
-
getTelemetry,
|
|
75
|
-
CANONICAL_COUNTERS,
|
|
76
|
-
} = require('./telemetry/index');
|
|
77
|
-
const { createUsageRepository } = require('./usage/index');
|
|
78
78
|
const { TimeoutCatcher } = require('./lambda/index');
|
|
79
79
|
const { debug, initDebugLog, flushDebugLog } = require('./logs/index');
|
|
80
80
|
const {
|
|
@@ -147,12 +147,6 @@ module.exports = {
|
|
|
147
147
|
createReportingRouter,
|
|
148
148
|
createReportingRepository,
|
|
149
149
|
|
|
150
|
-
// telemetry
|
|
151
|
-
createTelemetry,
|
|
152
|
-
getTelemetry,
|
|
153
|
-
CANONICAL_COUNTERS,
|
|
154
|
-
createUsageRepository,
|
|
155
|
-
|
|
156
150
|
// application - Command factories for integration developers
|
|
157
151
|
application,
|
|
158
152
|
createFriggCommands: application.createFriggCommands,
|
|
@@ -162,7 +156,6 @@ module.exports = {
|
|
|
162
156
|
createCredentialCommands: application.createCredentialCommands,
|
|
163
157
|
createProcessCommands: application.createProcessCommands,
|
|
164
158
|
createSchedulerCommands: application.createSchedulerCommands,
|
|
165
|
-
createUsageCommands: application.createUsageCommands,
|
|
166
159
|
findIntegrationContextByExternalEntityId:
|
|
167
160
|
application.findIntegrationContextByExternalEntityId,
|
|
168
161
|
integrationCommands: application.integrationCommands,
|
|
@@ -18,9 +18,6 @@ const {
|
|
|
18
18
|
UpdateIntegrationConfig,
|
|
19
19
|
} = require('./use-cases/update-integration-config');
|
|
20
20
|
const { validateExtensionBinding } = require('./extension');
|
|
21
|
-
const { getTelemetry } = require('../telemetry/telemetry-runtime');
|
|
22
|
-
const { instrumentHandler } = require('../telemetry/instrument-handler');
|
|
23
|
-
const { bindTelemetryContext } = require('../telemetry/bind-telemetry-context');
|
|
24
21
|
|
|
25
22
|
const constantsToBeMigrated = {
|
|
26
23
|
defaultEvents: {
|
|
@@ -59,11 +56,6 @@ class IntegrationBase {
|
|
|
59
56
|
integrationRepository: this.integrationRepository,
|
|
60
57
|
});
|
|
61
58
|
|
|
62
|
-
// this.telemetry.count('records.synced', n, { entity: 'contact' })
|
|
63
|
-
telemetry = bindTelemetryContext(getTelemetry(), () =>
|
|
64
|
-
this.getTelemetryContext()
|
|
65
|
-
);
|
|
66
|
-
|
|
67
59
|
static getOptionDetails() {
|
|
68
60
|
const options = new Options({
|
|
69
61
|
module: Object.values(this.Definition.modules)[0], // This is a placeholder until we revamp the frontend
|
|
@@ -84,8 +76,6 @@ class IntegrationBase {
|
|
|
84
76
|
// Tier 3 Integration Extensions — see packages/core/integrations/EXTENSIONS.md
|
|
85
77
|
// Shape: { [bindingName]: { extension, handlers?: { [eventName]: methodName } } }
|
|
86
78
|
extensions: {},
|
|
87
|
-
// usage: { canonical: ['records.synced'], custom: { 'deals.enriched': { unit, label } } }
|
|
88
|
-
usage: {},
|
|
89
79
|
display: {
|
|
90
80
|
name: 'Integration Name',
|
|
91
81
|
logo: '',
|
|
@@ -214,20 +204,6 @@ class IntegrationBase {
|
|
|
214
204
|
};
|
|
215
205
|
|
|
216
206
|
this._isHydrated = Boolean(this.id);
|
|
217
|
-
|
|
218
|
-
// Log the instance-open exactly once per hydrated instance, so an
|
|
219
|
-
// integration is visible in telemetry even on a path that never
|
|
220
|
-
// dispatches a handler. integration_type + the full id context are
|
|
221
|
-
// attached automatically by the bound telemetry service.
|
|
222
|
-
if (this._isHydrated && !this._instantiationLogged) {
|
|
223
|
-
this._instantiationLogged = true;
|
|
224
|
-
try {
|
|
225
|
-
this.telemetry.event('frigg.integration.instantiated');
|
|
226
|
-
} catch (_) {
|
|
227
|
-
// Telemetry must never break integration hydration.
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
207
|
return this;
|
|
232
208
|
}
|
|
233
209
|
|
|
@@ -235,25 +211,6 @@ class IntegrationBase {
|
|
|
235
211
|
return this._isHydrated;
|
|
236
212
|
}
|
|
237
213
|
|
|
238
|
-
/**
|
|
239
|
-
* Standard telemetry identifier set. Assembled from the
|
|
240
|
-
* hydrated record (integrationId, userId, version), the static Definition
|
|
241
|
-
* (integrationType, version fallback), and the environment (stage, appName).
|
|
242
|
-
* High-cardinality ids (integrationId, userId) ride span baggage only — never
|
|
243
|
-
* metric labels.
|
|
244
|
-
*/
|
|
245
|
-
getTelemetryContext() {
|
|
246
|
-
const Definition = this.constructor.Definition || {};
|
|
247
|
-
return {
|
|
248
|
-
integrationId: this.id ?? null,
|
|
249
|
-
integrationType: Definition.name ?? null,
|
|
250
|
-
userId: this.userId ?? null,
|
|
251
|
-
version: this.version ?? Definition.version ?? null,
|
|
252
|
-
stage: process.env.STAGE || process.env.NODE_ENV || null,
|
|
253
|
-
appName: process.env.FRIGG_STACK || null,
|
|
254
|
-
};
|
|
255
|
-
}
|
|
256
|
-
|
|
257
214
|
assertHydrated(message = 'Integration instance is not hydrated') {
|
|
258
215
|
if (!this.isHydrated) {
|
|
259
216
|
throw new Error(message);
|
|
@@ -279,16 +236,13 @@ class IntegrationBase {
|
|
|
279
236
|
// e.g., 'quo-attio' → 'quo', 'attio' → 'attio'
|
|
280
237
|
const moduleNameToKey = {};
|
|
281
238
|
if (this.constructor.Definition?.modules) {
|
|
282
|
-
for (const [key, moduleConfig] of Object.entries(
|
|
283
|
-
this.constructor.Definition.modules
|
|
284
|
-
)) {
|
|
239
|
+
for (const [key, moduleConfig] of Object.entries(this.constructor.Definition.modules)) {
|
|
285
240
|
const definition = moduleConfig.definition;
|
|
286
241
|
if (definition) {
|
|
287
242
|
// Use getName() if available, fallback to moduleName
|
|
288
|
-
const definitionName =
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
: definition.moduleName;
|
|
243
|
+
const definitionName = typeof definition.getName === 'function'
|
|
244
|
+
? definition.getName()
|
|
245
|
+
: definition.moduleName;
|
|
292
246
|
if (definitionName) {
|
|
293
247
|
moduleNameToKey[definitionName] = key;
|
|
294
248
|
}
|
|
@@ -373,9 +327,7 @@ class IntegrationBase {
|
|
|
373
327
|
try {
|
|
374
328
|
const authPassed = await this[module].testAuth();
|
|
375
329
|
if (!authPassed) {
|
|
376
|
-
throw new Error(
|
|
377
|
-
`testAuth returned false for module ${module}`
|
|
378
|
-
);
|
|
330
|
+
throw new Error(`testAuth returned false for module ${module}`);
|
|
379
331
|
}
|
|
380
332
|
} catch {
|
|
381
333
|
didAuthPass = false;
|
|
@@ -632,9 +584,7 @@ class IntegrationBase {
|
|
|
632
584
|
async persistStatus(status) {
|
|
633
585
|
await this.updateIntegrationStatus.execute(this.id, status);
|
|
634
586
|
this.status = status;
|
|
635
|
-
console.log(
|
|
636
|
-
`[Frigg] Integration ${this.id} status changed to ${status}`
|
|
637
|
-
);
|
|
587
|
+
console.log(`[Frigg] Integration ${this.id} status changed to ${status}`);
|
|
638
588
|
}
|
|
639
589
|
|
|
640
590
|
/**
|
|
@@ -799,14 +749,7 @@ class IntegrationBase {
|
|
|
799
749
|
`Event ${event} is not defined in the Integration event object`
|
|
800
750
|
);
|
|
801
751
|
}
|
|
802
|
-
|
|
803
|
-
// actions, config-options, and lifecycle events dispatched via `this.on`
|
|
804
|
-
// (the queue/webhook/route paths go through IntegrationEventDispatcher).
|
|
805
|
-
return instrumentHandler(
|
|
806
|
-
this.telemetry,
|
|
807
|
-
{ event, eventType: this.on[event].type },
|
|
808
|
-
() => this.on[event].handler.call(this, object)
|
|
809
|
-
);
|
|
752
|
+
return this.on[event].handler.call(this, object);
|
|
810
753
|
}
|
|
811
754
|
|
|
812
755
|
getOptionDetails() {
|
|
@@ -848,12 +791,18 @@ class IntegrationBase {
|
|
|
848
791
|
if (!this.id) return;
|
|
849
792
|
|
|
850
793
|
if (delegateString === 'CREDENTIAL_INVALIDATED') {
|
|
794
|
+
const detail =
|
|
795
|
+
object?.reason || object?.statusCode
|
|
796
|
+
? ` (status ${object?.statusCode ?? '?'}: ${
|
|
797
|
+
object?.reason ?? 'no reason given'
|
|
798
|
+
})`
|
|
799
|
+
: '';
|
|
851
800
|
console.log(
|
|
852
801
|
`[Frigg] Module ${
|
|
853
802
|
notifier?.name || '?'
|
|
854
803
|
} reported invalid credentials for integration ${
|
|
855
804
|
this.id
|
|
856
|
-
} — marking ERROR`
|
|
805
|
+
} — marking ERROR${detail}`
|
|
857
806
|
);
|
|
858
807
|
await this.persistStatus('ERROR');
|
|
859
808
|
return;
|