@friggframework/core 2.0.0--canary.698.b1a2d30.0 → 2.0.0--canary.699.0d57ee8.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.
Files changed (92) hide show
  1. package/generated/prisma-mongodb/edge.js +2 -2
  2. package/generated/prisma-mongodb/index.js +2 -2
  3. package/generated/prisma-mongodb/package.json +1 -1
  4. package/generated/prisma-mongodb/schema.prisma +2 -2
  5. package/generated/prisma-mongodb/wasm.js +2 -2
  6. package/generated/prisma-postgresql/edge.js +2 -2
  7. package/generated/prisma-postgresql/index.js +2 -2
  8. package/generated/prisma-postgresql/package.json +1 -1
  9. package/generated/prisma-postgresql/schema.prisma +2 -2
  10. package/generated/prisma-postgresql/wasm.js +2 -2
  11. package/handlers/app-handler-helpers.js +5 -2
  12. package/handlers/integration-event-dispatcher.js +1 -1
  13. package/integrations/{data-defined → definitions}/README.md +12 -12
  14. package/integrations/{data-defined → definitions}/capabilities/AUDIT.md +12 -11
  15. package/integrations/{data-defined → definitions}/capabilities/asana.capabilities.json +1 -2
  16. package/integrations/{data-defined → definitions}/capabilities/fathom.capabilities.json +1 -2
  17. package/integrations/{data-defined → definitions}/capabilities/hubspot.capabilities.json +1 -2
  18. package/integrations/{data-defined → definitions}/capabilities/linear.capabilities.json +1 -2
  19. package/integrations/{data-defined → definitions}/capabilities/phone.capabilities.json +1 -2
  20. package/integrations/{data-defined → definitions}/capabilities/pipedrive.capabilities.json +1 -2
  21. package/integrations/{data-defined → definitions}/capabilities/salesforce.capabilities.json +1 -1
  22. package/integrations/{data-defined → definitions}/capabilities/slack.capabilities.json +1 -2
  23. package/integrations/{data-defined → definitions}/capabilities/stripe.capabilities.json +1 -2
  24. package/integrations/{data-defined → definitions}/config-form.js +1 -1
  25. package/integrations/{data-defined → definitions}/examples/pipedrive-hubspot.definition.json +1 -1
  26. package/integrations/{data-defined → definitions}/examples/records/phone-hubspot.integration.json +1 -1
  27. package/integrations/{data-defined → definitions}/expressions.js +1 -1
  28. package/integrations/{data-defined → definitions}/index.js +2 -2
  29. package/integrations/{data-defined/data-defined-integration.js → definitions/integration-runtime.js} +22 -21
  30. package/integrations/{data-defined → definitions}/renderers/hubspot.js +13 -8
  31. package/integrations/{data-defined → definitions}/router.js +11 -11
  32. package/integrations/{data-defined → definitions}/runtime.js +1 -1
  33. package/integrations/{data-defined → definitions}/schema/installation.v0.1.schema.json +1 -1
  34. package/integrations/{data-defined → definitions}/schema/records.md +1 -1
  35. package/integrations/{data-defined → definitions}/scripts/validate.js +1 -1
  36. package/integrations/{data-defined → definitions}/use-cases/definition-use-cases.js +1 -1
  37. package/integrations/{data-defined → definitions}/versioning.js +1 -1
  38. package/integrations/integration-base.js +31 -1
  39. package/integrations/integration-router.js +1 -1
  40. package/package.json +6 -6
  41. package/prisma-mongodb/schema.prisma +2 -2
  42. package/prisma-postgresql/schema.prisma +2 -2
  43. /package/integrations/{data-defined → definitions}/canonical.js +0 -0
  44. /package/integrations/{data-defined → definitions}/capabilities/acmeerp.capabilities.json +0 -0
  45. /package/integrations/{data-defined → definitions}/capabilities/calendly.capabilities.json +0 -0
  46. /package/integrations/{data-defined → definitions}/capabilities/docusign.capabilities.json +0 -0
  47. /package/integrations/{data-defined → definitions}/capabilities/github.capabilities.json +0 -0
  48. /package/integrations/{data-defined → definitions}/capabilities/jira.capabilities.json +0 -0
  49. /package/integrations/{data-defined → definitions}/capabilities/klaviyo.capabilities.json +0 -0
  50. /package/integrations/{data-defined → definitions}/capabilities/netsuite.capabilities.json +0 -0
  51. /package/integrations/{data-defined → definitions}/capabilities/notion.capabilities.json +0 -0
  52. /package/integrations/{data-defined → definitions}/capabilities/quickbooks.capabilities.json +0 -0
  53. /package/integrations/{data-defined → definitions}/capabilities/shopify.capabilities.json +0 -0
  54. /package/integrations/{data-defined → definitions}/capabilities/webflow.capabilities.json +0 -0
  55. /package/integrations/{data-defined → definitions}/capabilities/zendesk.capabilities.json +0 -0
  56. /package/integrations/{data-defined → definitions}/compile.js +0 -0
  57. /package/integrations/{data-defined → definitions}/examples/acme-erp-quickbooks.definition.json +0 -0
  58. /package/integrations/{data-defined → definitions}/examples/asana-slack.definition.json +0 -0
  59. /package/integrations/{data-defined → definitions}/examples/calendly-salesforce.definition.json +0 -0
  60. /package/integrations/{data-defined → definitions}/examples/docusign-salesforce.definition.json +0 -0
  61. /package/integrations/{data-defined → definitions}/examples/fathom-hubspot-notion.definition.json +0 -0
  62. /package/integrations/{data-defined → definitions}/examples/github-linear.definition.json +0 -0
  63. /package/integrations/{data-defined → definitions}/examples/hubspot-slack.definition.json +0 -0
  64. /package/integrations/{data-defined → definitions}/examples/klaviyo-hubspot.definition.json +0 -0
  65. /package/integrations/{data-defined → definitions}/examples/phone-hubspot.definition.json +0 -0
  66. /package/integrations/{data-defined → definitions}/examples/pipedrive-phone.definition.json +0 -0
  67. /package/integrations/{data-defined → definitions}/examples/records/phone-hubspot.definition-version.json +0 -0
  68. /package/integrations/{data-defined → definitions}/examples/records/phone-hubspot.mappings.json +0 -0
  69. /package/integrations/{data-defined → definitions}/examples/records/phone-hubspot.workflow-run.json +0 -0
  70. /package/integrations/{data-defined → definitions}/examples/shopify-klaviyo.definition.json +0 -0
  71. /package/integrations/{data-defined → definitions}/examples/shopify-netsuite.definition.json +0 -0
  72. /package/integrations/{data-defined → definitions}/examples/stripe-quickbooks.definition.json +0 -0
  73. /package/integrations/{data-defined → definitions}/examples/webflow-hubspot-slack.definition.json +0 -0
  74. /package/integrations/{data-defined → definitions}/examples/zendesk-jira.definition.json +0 -0
  75. /package/integrations/{data-defined → definitions}/interpreter.js +0 -0
  76. /package/integrations/{data-defined → definitions}/manifests.js +0 -0
  77. /package/integrations/{data-defined → definitions}/renderers/common.js +0 -0
  78. /package/integrations/{data-defined → definitions}/renderers/html.js +0 -0
  79. /package/integrations/{data-defined → definitions}/renderers/preview.js +0 -0
  80. /package/integrations/{data-defined → definitions}/renderers/slack.js +0 -0
  81. /package/integrations/{data-defined → definitions}/repositories/definition-version-repository.js +0 -0
  82. /package/integrations/{data-defined → definitions}/repositories/workflow-run-repository.js +0 -0
  83. /package/integrations/{data-defined → definitions}/schema/capability-manifest.v0.1.schema.json +0 -0
  84. /package/integrations/{data-defined → definitions}/schema/integration-definition.v0.1.schema.json +0 -0
  85. /package/integrations/{data-defined → definitions}/schema-check.js +0 -0
  86. /package/integrations/{data-defined → definitions}/scripts/check-manifests.js +0 -0
  87. /package/integrations/{data-defined → definitions}/steps.js +0 -0
  88. /package/integrations/{data-defined → definitions}/sync-engine.js +0 -0
  89. /package/integrations/{data-defined → definitions}/tests/helpers-fake-crm.js +0 -0
  90. /package/integrations/{data-defined → definitions}/transforms.js +0 -0
  91. /package/integrations/{data-defined → definitions}/validate.js +0 -0
  92. /package/integrations/{data-defined → definitions}/webhooks.js +0 -0
@@ -428,8 +428,8 @@ const config = {
428
428
  }
429
429
  }
430
430
  },
431
- "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 workflowRuns WorkflowRun[]\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\n// Data-defined integrations (ADR-063): one immutable row per published definition version.\nenum DefinitionStatus {\n DRAFT\n PUBLISHED\n DEPRECATED\n RETIRED\n}\n\nmodel IntegrationDefinitionVersion {\n id Int @id @default(autoincrement())\n definitionId String\n version String\n schemaVersion String\n status DefinitionStatus @default(DRAFT)\n checksum String\n document Json\n changeLevel String?\n previousVersion String?\n notes String?\n createdBy String?\n // The workspace an integration a user built belongs to (null: the app's own catalogue, offered to everyone).\n workspaceId String?\n publishedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([definitionId, version])\n @@index([definitionId, status])\n @@index([workspaceId, status])\n}\n\n// One row per data-defined workflow run. trace holds step ids, statuses, timings and error messages, never request or\n// response bodies.\nmodel WorkflowRun {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n definitionId String\n definitionVersion String\n workflowId String\n triggerType String\n eventId String?\n status String\n reason String?\n error String?\n retryable Boolean @default(false)\n trace Json @default(\"[]\")\n /// The runtime's run id (run_...): how routes and resume jobs address a run\n runKey String?\n /// A paused run (status waiting): which step, and what it waits for. Never request or response bodies.\n waitingFor Json?\n resumeAt DateTime?\n startedAt DateTime\n endedAt DateTime?\n createdAt DateTime @default(now())\n\n @@index([integrationId, runKey])\n @@index([integrationId, startedAt])\n @@index([integrationId, workflowId, startedAt])\n @@index([integrationId, status])\n}\n",
432
- "inlineSchemaHash": "7cbcf3d54afbe39160d69f8b640da30304af16dd1eb4ce3267842820b464be7a",
431
+ "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 workflowRuns WorkflowRun[]\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\n// Integrations published as definitions (ADR-063): one immutable row per published definition version.\nenum DefinitionStatus {\n DRAFT\n PUBLISHED\n DEPRECATED\n RETIRED\n}\n\nmodel IntegrationDefinitionVersion {\n id Int @id @default(autoincrement())\n definitionId String\n version String\n schemaVersion String\n status DefinitionStatus @default(DRAFT)\n checksum String\n document Json\n changeLevel String?\n previousVersion String?\n notes String?\n createdBy String?\n // The workspace an integration a user built belongs to (null: the app's own catalogue, offered to everyone).\n workspaceId String?\n publishedAt DateTime?\n createdAt DateTime @default(now())\n updatedAt DateTime @updatedAt\n\n @@unique([definitionId, version])\n @@index([definitionId, status])\n @@index([workspaceId, status])\n}\n\n// One row per workflow run. trace holds step ids, statuses, timings and error messages, never request or\n// response bodies.\nmodel WorkflowRun {\n id Int @id @default(autoincrement())\n integrationId Int\n integration Integration @relation(fields: [integrationId], references: [id], onDelete: Cascade)\n definitionId String\n definitionVersion String\n workflowId String\n triggerType String\n eventId String?\n status String\n reason String?\n error String?\n retryable Boolean @default(false)\n trace Json @default(\"[]\")\n /// The runtime's run id (run_...): how routes and resume jobs address a run\n runKey String?\n /// A paused run (status waiting): which step, and what it waits for. Never request or response bodies.\n waitingFor Json?\n resumeAt DateTime?\n startedAt DateTime\n endedAt DateTime?\n createdAt DateTime @default(now())\n\n @@index([integrationId, runKey])\n @@index([integrationId, startedAt])\n @@index([integrationId, workflowId, startedAt])\n @@index([integrationId, status])\n}\n",
432
+ "inlineSchemaHash": "5d11039694b44af001971b7239ab8290b642c385239660aee17e709d4fa0b509",
433
433
  "copyEngine": true
434
434
  }
435
435
  config.dirname = '/'
@@ -12,8 +12,11 @@ const log = getLogger('frigg.http');
12
12
  const createApp = (applyMiddleware) => {
13
13
  const app = express();
14
14
 
15
- app.use(bodyParser.json({ limit: '10mb' }));
16
- app.use(bodyParser.urlencoded({ extended: true }));
15
+ // Keep the bytes as received: webhook signatures (HubSpot, Slack, Stripe, Linear…) are computed over the exact
16
+ // raw body, which re-serialising the parsed body does not reproduce. A reference to the buffer, not a copy.
17
+ const keepRaw = (req, res, buf) => { req.rawBody = buf; };
18
+ app.use(bodyParser.json({ limit: '10mb', verify: keepRaw }));
19
+ app.use(bodyParser.urlencoded({ extended: true, verify: keepRaw }));
17
20
  app.use(
18
21
  cors({
19
22
  origin: '*',
@@ -21,7 +21,7 @@ class IntegrationEventDispatcher {
21
21
  async dispatchJob({ event, data, context, delivery }) {
22
22
  return this._dispatch(event, (instance, handler) => {
23
23
  const invoke = () => handler.call(instance, { data, context, delivery });
24
- // An integration that keeps run history records each queued event as a run (data-defined runtime).
24
+ // An integration that keeps run history records each queued event as a run (integration runtime).
25
25
  return typeof instance.runEvent === 'function'
26
26
  ? instance.runEvent(event, invoke)
27
27
  : invoke();
@@ -1,4 +1,4 @@
1
- # Data-defined integrations (v0.1)
1
+ # Integrations published as definitions (v0.1)
2
2
 
3
3
  ADR-063, implemented to the point where a definition can be validated, compiled into Frigg events, executed, rendered
4
4
  and versioned. An integration becomes a **document** (what it connects, what it syncs, which workflows and UI it has)
@@ -17,7 +17,7 @@ installation (Integration.config) ─┘ │
17
17
  |---|---|
18
18
  | `schema/integration-definition.v0.1.schema.json` | The definition format (JSON Schema 2020-12) |
19
19
  | `schema/capability-manifest.v0.1.schema.json` | What an API module declares it can do |
20
- | `schema/installation.v0.1.schema.json` | `Integration.config` for a data-defined installation |
20
+ | `schema/installation.v0.1.schema.json` | `Integration.config` for an installation |
21
21
  | `schema/records.md` | Database records: definition versions, installation, mappings, workflow runs |
22
22
  | `capabilities/*.capabilities.json` | Capability manifests. Nine are checked against the real Api classes (`capabilities/AUDIT.md`); the other twelve are still mock-shaped |
23
23
  | `scripts/check-manifests.js` | Checks every manifest `method` exists on the real module's Api class, with the arguments it takes |
@@ -30,7 +30,7 @@ installation (Integration.config) ─┘ │
30
30
  | `sync-engine.js` | Syncs: link → identity → search → create/update, master rules, backfill pages, polls |
31
31
  | `runtime.js` | Ports (Api calls, link store), `runWorkflow`, `runQuery`, `renderSurface`, `runLifecycle`, `resolveOptions`, `installContext` |
32
32
  | `versioning.js` | `diff`, `checkBump`, `planUpgrade` |
33
- | `data-defined-integration.js` | `defineIntegrationClass`: the IntegrationBase subclass that runs it all |
33
+ | `integration-runtime.js` | `defineIntegrationClass`: the IntegrationBase subclass that runs it all |
34
34
  | `use-cases/definition-use-cases.js` | Publish, deprecate, catalogue, history, upgrade plan; `createDefinitionCatalog()` (the store the runtime reads) |
35
35
  | `repositories/` | `IntegrationDefinitionVersion` and `WorkflowRun` rows (MongoDB, PostgreSQL, DocumentDB) |
36
36
  | `router.js` | The publish, catalogue, upgrade and run-history routes |
@@ -107,9 +107,9 @@ Definitions live in the `IntegrationDefinitionVersion` table (Prisma models in b
107
107
  `20261018000000_add_data_defined_integrations`), and runs in `WorkflowRun`. Point the class at the store:
108
108
 
109
109
  ```js
110
- const { defineSharedIntegrationClass, createDefinitionCatalog } = require('@friggframework/core/integrations/data-defined');
110
+ const { defineSharedIntegrationClass, createDefinitionCatalog } = require('@friggframework/core/integrations/definitions');
111
111
  // A database store is read asynchronously, so it goes with the shared class (one class, every definition).
112
- const DataDefined = defineSharedIntegrationClass({ definitions: createDefinitionCatalog(), manifests, modules });
112
+ const IntegrationRuntime = defineSharedIntegrationClass({ definitions: createDefinitionCatalog(), manifests, modules });
113
113
  ```
114
114
 
115
115
  | Route | Auth | |
@@ -118,8 +118,8 @@ const DataDefined = defineSharedIntegrationClass({ definitions: createDefinition
118
118
  | `GET /admin/integrations/:id/versions` | admin key | Version history |
119
119
  | `POST /admin/integrations/:id/versions/:v/deprecate` | admin key | Stop new installs of a version |
120
120
  | `GET /api/integrations` | user | What can be installed: code and published integrations in one list of options |
121
- | `GET /api/data-defined/definitions` | user | The published ones, with their counts |
122
- | `POST /api/integrations` with `config: { type: '<id>' }` | user | Install; pins the latest published version and its checksum (`{ type: 'data-defined', definition: { id } }` still works) |
121
+ | `GET /api/integration-definitions` | user | The published ones, with their counts |
122
+ | `POST /api/integrations` with `config: { type: '<id>' }` | user | Install; pins the latest published version and its checksum |
123
123
  | `GET` / `POST /api/integrations/:id/upgrade` `{ to? }` | user | Plan / apply an upgrade (409 with the plan while something is needed) |
124
124
  | `GET /api/integrations/:id/runs` | user | Run history, newest first |
125
125
  | `GET /api/integrations/:id/syncs` | user | Each sync's backfill progress and poll marks |
@@ -153,7 +153,7 @@ code production runs (`renderers/`):
153
153
 
154
154
  | Function | Does |
155
155
  |---|---|
156
- | `previewSurface({ def, installation, surfaceId, record, ports, host? })` | `{ surface, view, html, slack?, hubspot? }`. `slack` is Block Kit (`{ blocks, text, truncated }`), `hubspot` a data description of `@hubspot/ui-extensions` components, `html` the same card framed as its host looks. `host` defaults to the surface's own |
156
+ | `previewSurface({ def, installation, surfaceId, record, ports, host? })` | `{ surface, view, html, slack?, hubspot? }`. `slack` is Block Kit (`{ blocks, text, truncated }`), `hubspot` a data description of `@hubspot/ui-extensions` components (names and props checked against 0.17.1; `props` are real component props, a userAction button carries our own node-level `action` for the card to post, an `open_url` button is a real `href`), `html` the same card framed as its host looks. `host` defaults to the surface's own |
157
157
  | `previewWorkflow({ def, installation, workflowId, event?, record?, manifests, fixtures? })` | Dry run: `{ status, sends, calls, trace, waiting? }`. Each call is `{ stepId, module, capability, kind, args, output, rendered }`; `rendered` is `{ html, slack? }` for writes (a Slack post renders as the message, anything else as a "would call" card) |
158
158
  | `recordingPorts({ def, manifests, fixtures })` | Ports that record and answer instead of calling an API; give them to `previewSurface` for sample data |
159
159
  | `viewToSlack`, `viewToHubSpot`, `viewToHtml`, `slackMessageToHtml` | The renderers on their own. `decodeAction(value)` reads a Slack button click back to `{ integrationId?, workflow, surface }` |
@@ -231,14 +231,14 @@ they were routed under and finish on it.
231
231
  ## Running the tests
232
232
 
233
233
  ```bash
234
- npx jest --config '{}' --rootDir packages/core/integrations/data-defined tests
234
+ npx jest --config '{}' --rootDir packages/core/integrations/definitions tests
235
235
  ```
236
236
 
237
237
  `tests/persistence.test.js` runs the repositories and the publish rules against real databases. Each leg runs only
238
238
  when its URL is set. Point them at a migrated PostgreSQL database and a MongoDB replica set with the schema pushed:
239
239
 
240
240
  ```bash
241
- DATA_DEFINED_POSTGRES_URL=postgresql://… DATA_DEFINED_MONGO_URL=mongodb://… npx jest … tests
241
+ INTEGRATION_DEFINITIONS_POSTGRES_URL=postgresql://… INTEGRATION_DEFINITIONS_MONGO_URL=mongodb://… npx jest … tests
242
242
  ```
243
243
 
244
244
  ## Validating
@@ -251,8 +251,8 @@ Validation is two layers, and a document is valid only when both pass (`schema-c
251
251
 
252
252
  ```bash
253
253
  npm run validate:definitions --workspace @friggframework/core # bundled manifests + examples
254
- node packages/core/integrations/data-defined/scripts/validate.js my.definition.json --strict
255
- node packages/core/integrations/data-defined/scripts/validate.js --manifests ./capabilities --json defs/
254
+ node packages/core/integrations/definitions/scripts/validate.js my.definition.json --strict
255
+ node packages/core/integrations/definitions/scripts/validate.js --manifests ./capabilities --json defs/
256
256
  ```
257
257
 
258
258
  Exit codes: `0` valid, `1` invalid (or a warning with `--strict`), `2` could not run (unreadable JSON, missing file,
@@ -7,7 +7,7 @@ Api class and every call shape was read against the method's real signature.
7
7
  Re-run it (no network, nothing instantiated):
8
8
 
9
9
  ```bash
10
- node packages/core/integrations/data-defined/scripts/check-manifests.js --stub-missing \
10
+ node packages/core/integrations/definitions/scripts/check-manifests.js --stub-missing \
11
11
  ~/phone-app/backend/src/api-modules ~/phone-app/backend/node_modules/@friggframework \
12
12
  ~/api-module-library/packages/v1-ready ~/api-module-library/packages/needs-updating
13
13
  ```
@@ -29,7 +29,8 @@ the Api's methods can be read. Roots are searched in order and the first module
29
29
  `runtime.js` `argumentsFor` spreads them. An input that is declared but never passed is refused by the manifest check,
30
30
  because it would be accepted and silently dropped.
31
31
  - **Webhook verification.** Every manifest names `verifyWebhookSignature`. No real module has it (only clio, which has no
32
- manifest). Each real module's `webhook` block is marked `"status": "unimplemented"`, and a workflow that triggers on
32
+ manifest). Each real module's `webhook` block was marked `"status": "unimplemented"`; since api-module-library
33
+ 659b90d eight of them verify signatures (Salesforce has no signed webhook and stays unimplemented), and a workflow that triggers on
33
34
  its events validates with a warning: every delivery would fail signature verification.
34
35
 
35
36
  ## Summary
@@ -81,7 +82,7 @@ Checked against `~/api-module-library/packages/v1-ready/asana` (@friggframework/
81
82
  | `workspace.list` | `listWorkspaces` | ok | `listWorkspaces()` | (method was `getWorkspaces`) |
82
83
  | `webhook.create` | `createWebhook` | unimplemented | none | Api has no webhook methods. |
83
84
  | `webhook.delete` | `deleteWebhook` | unimplemented | none | Api has no webhook methods. |
84
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The asana Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
85
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
85
86
 
86
87
  ## Calendly (`calendly`)
87
88
 
@@ -121,7 +122,7 @@ Checked against `~/api-module-library/packages/v1-ready/fathom` (@friggframework
121
122
  | `meeting.transcript` | `getTranscript` | ok | `getTranscript(recordingId, params = {})` | (method was `getRecordingTranscript`; called with one object; now positional) |
122
123
  | `webhook.create` | `createWebhook` | ok | `createWebhook(data)` | |
123
124
  | `webhook.delete` | `deleteWebhook` | unimplemented | none | Api has createWebhook but no deleteWebhook. |
124
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The fathom Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
125
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
125
126
 
126
127
  ## GitHub (`github`)
127
128
 
@@ -166,7 +167,7 @@ Checked against `~/api-module-library/packages/v1-ready/hubspot` (@friggframewor
166
167
  | `owner.findByEmail` | `getOwnerByEmail` | unimplemented | none | Api has getOwnerById only. |
167
168
  | `subscription.definitions` | `listSubscriptionDefinitions` | unimplemented | none | Api has no email subscription methods. |
168
169
  | `subscription.unsubscribe` | `unsubscribeEmail` | unimplemented | none | Api has no email subscription methods. |
169
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The hubspot Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
170
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
170
171
 
171
172
  ## Jira Cloud (`jira`)
172
173
 
@@ -225,7 +226,7 @@ Checked against `~/api-module-library/packages/v1-ready/linear` (@friggframework
225
226
  | `attachment.link` | `attachmentLinkURL` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
226
227
  | `team.list` | `teams` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
227
228
  | `workflowState.list` | `workflowStates` | unimplemented | none | Api exposes getClient() (a @linear/sdk LinearClient) plus getUsers, getProjects and getUserIssues; issues, comments, attachments, teams and workflow states are reached through the SDK client, not Api methods. |
228
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The linear Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
229
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
229
230
 
230
231
  ## NetSuite (`netsuite`)
231
232
 
@@ -280,7 +281,7 @@ Checked against `~/phone-app/backend/node_modules/@friggframework/api-module-pip
280
281
  | `user.list` | `listUsers` | ok | `listUsers()` | (method was `getUsers`) |
281
282
  | `webhook.create` | `createWebhook` | ok | `createWebhook(params)` | (method was `addWebhook`) |
282
283
  | `webhook.delete` | `deleteWebhook` | ok | `deleteWebhook(webhookId)` | (called with one object; now positional) |
283
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The pipedrive Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
284
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
284
285
 
285
286
  ## QuickBooks Online (`quickbooks`)
286
287
 
@@ -323,7 +324,7 @@ Checked against `~/phone-app/backend/src/api-modules/phone` (@acme/api-module-ph
323
324
  | `user.list` | `listUsers` | ok | `listUsers(params = {})` | |
324
325
  | `webhook.create` | `createUnifiedWebhook` | ok | `createUnifiedWebhook(data)` | (method was `createWebhook`) |
325
326
  | `webhook.delete` | `deleteWebhook` | ok | `deleteWebhook(id)` | (called with one object; now positional) |
326
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The phone Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
327
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
327
328
 
328
329
  ## Salesforce (`salesforce`)
329
330
 
@@ -343,7 +344,7 @@ Checked against `~/api-module-library/packages/v1-ready/salesforce` (@friggframe
343
344
  | `file.attach` | `createContentVersion` | unimplemented | none | Api has the generic create(object, data) only; ContentVersion field names differ from this capability's inputs. |
344
345
  | `note.create` | `createContentNote` | unimplemented | none | Api has the generic create(object, data) only; ContentNote field names differ from this capability's inputs. |
345
346
  | `user.list` | `listUsers` | unimplemented | none | Api has find(object, filter) but no user listing with an activeOnly filter. |
346
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The salesforce Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
347
+ | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | Not a webhook: CDC arrives over the Pub/Sub or CometD stream; Outbound Messages are unsigned (OrganizationId + IP allowlist). Deliveries are rejected. |
347
348
 
348
349
  ## Shopify (`shopify`)
349
350
 
@@ -381,7 +382,7 @@ Checked against `~/api-module-library/packages/needs-updating/slack` (@friggfram
381
382
  | `channel.list` | `listChannels` | ok | `listChannels(query)` | (method was `listConversations`) |
382
383
  | `user.list` | `listUsers` | unimplemented | none | Api has lookupUserByEmail, getUserById and getUserProfileById, but no user listing. |
383
384
  | `user.lookupByEmail` | `lookupUserByEmail` | ok | `lookupUserByEmail(email)` | (called with one object; now positional) |
384
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The slack Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
385
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
385
386
 
386
387
  ## Stripe (`stripe`)
387
388
 
@@ -398,7 +399,7 @@ Checked against `~/api-module-library/packages/v1-ready/stripe` (@friggframework
398
399
  | `refund.get` | `retrieveRefund` | unimplemented | none | Api wraps balance transactions, charges, accounts and webhook endpoints only; customers, payment intents, refunds and payouts are not exposed. |
399
400
  | `payout.list` | `listPayouts` | unimplemented | none | Api wraps balance transactions, charges, accounts and webhook endpoints only; customers, payment intents, refunds and payouts are not exposed. |
400
401
  | `balanceTransaction.list` | `getBalanceTransactions` | ok | `getBalanceTransactions(params)` | (method was `listBalanceTransactions`) |
401
- | `webhook.verify` | `verifyWebhookSignature` | unimplemented | | The stripe Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one. |
402
+ | `webhook.verify` | `verifyWebhookSignature` | ok (unreleased) | `verifyWebhookSignature({ headers, body, rawBody, method?, url?, now? })` | Implemented in api-module-library 659b90d per the vendor's signing docs; needs the release that includes it. |
402
403
 
403
404
  ## Acme ERP (`acmeerp`)
404
405
 
@@ -152,8 +152,7 @@
152
152
  "X-Hook-Secret": "$.headers.x-hook-secret"
153
153
  }
154
154
  },
155
- "status": "unimplemented",
156
- "note": "The asana Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one."
155
+ "note": "X-Hook-Signature: HMAC-SHA256 of the raw body keyed with the handshake secret (per installation). Implemented in api-module-library 659b90d; needs the module release that includes it."
157
156
  },
158
157
  "capabilities": {
159
158
  "task.get": {
@@ -143,8 +143,7 @@
143
143
  "topic": "meeting.ready",
144
144
  "eventId": "{{body.recording_id}}",
145
145
  "verify": "verifyWebhookSignature",
146
- "status": "unimplemented",
147
- "note": "The fathom Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one."
146
+ "note": "Standard Webhooks: webhook-id, webhook-timestamp, webhook-signature (v1), 300 s. Implemented in api-module-library 659b90d; needs the module release that includes it."
148
147
  },
149
148
  "capabilities": {
150
149
  "meeting.list": {
@@ -388,8 +388,7 @@
388
388
  "eventId": "{{item.eventId}}",
389
389
  "account": "{{item.portalId}}",
390
390
  "verify": "verifyWebhookSignature",
391
- "status": "unimplemented",
392
- "note": "The hubspot Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one."
391
+ "note": "HubSpot v3: X-HubSpot-Signature-v3 over method + URL + raw body + timestamp, 5 minutes. Needs the public URL and method (the runtime passes them). Implemented in api-module-library 659b90d; needs the module release that includes it."
393
392
  },
394
393
  "capabilities": {
395
394
  "contact.get": {
@@ -183,8 +183,7 @@
183
183
  ],
184
184
  "account": "{{body.organizationId}}",
185
185
  "verify": "verifyWebhookSignature",
186
- "status": "unimplemented",
187
- "note": "The linear Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one."
186
+ "note": "Linear-Signature HMAC of the raw body, webhookTimestamp within 60 s. Implemented in api-module-library 659b90d; needs the module release that includes it."
188
187
  },
189
188
  "capabilities": {
190
189
  "issue.get": {
@@ -306,8 +306,7 @@
306
306
  "topic": "{{body.type}}",
307
307
  "eventId": "{{body.id}}",
308
308
  "verify": "verifyWebhookSignature",
309
- "status": "unimplemented",
310
- "note": "The phone Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one."
309
+ "note": "Standard Webhooks (current) or a legacy signature header, chosen by header. Implemented in api-module-library 659b90d; needs the module release that includes it."
311
310
  },
312
311
  "capabilities": {
313
312
  "contact.get": {
@@ -242,8 +242,7 @@
242
242
  "{{body.meta.entity}}:{{body.data.id}}:{{body.meta.timestamp}}"
243
243
  ],
244
244
  "verify": "verifyWebhookSignature",
245
- "status": "unimplemented",
246
- "note": "The pipedrive Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one."
245
+ "note": "Pipedrive does not sign bodies: HTTP Basic credentials configured on the webhook are checked. Implemented in api-module-library 659b90d; needs the module release that includes it."
247
246
  },
248
247
  "capabilities": {
249
248
  "person.get": {
@@ -323,7 +323,7 @@
323
323
  "eventId": "{{body.id}}",
324
324
  "verify": "verifyWebhookSignature",
325
325
  "status": "unimplemented",
326
- "note": "The salesforce Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one."
326
+ "note": "Not a webhook: change-data-capture events arrive over the Pub/Sub or CometD stream the module opens itself, and Salesforce Outbound Messages are unsigned (check OrganizationId plus an IP allowlist). Stays unimplemented, so a delivery here is rejected, never accepted unchecked."
327
327
  },
328
328
  "capabilities": {
329
329
  "lead.get": {
@@ -207,8 +207,7 @@
207
207
  "challenge": "$.body.challenge"
208
208
  }
209
209
  },
210
- "status": "unimplemented",
211
- "note": "The slack Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one."
210
+ "note": "Slack signing secret: v0 HMAC over v0:timestamp:raw body, 5 minutes. Implemented in api-module-library 659b90d; needs the module release that includes it."
212
211
  },
213
212
  "capabilities": {
214
213
  "message.post": {
@@ -472,8 +472,7 @@
472
472
  "eventId": "{{body.id}}",
473
473
  "account": "{{body.account}}",
474
474
  "verify": "verifyWebhookSignature",
475
- "status": "unimplemented",
476
- "note": "The stripe Api has no verifyWebhookSignature method; webhook signatures cannot be checked until the module adds one."
475
+ "note": "Stripe-Signature t=…,v1=… over t.raw body, 300 s tolerance; rolling secrets accepted. Implemented in api-module-library 659b90d; needs the module release that includes it."
477
476
  },
478
477
  "capabilities": {
479
478
  "customer.get": {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * The configure form for a data-defined installation, generated from its definition: what GET_CONFIG_OPTIONS and
2
+ * The configure form for an installation, generated from its definition: what GET_CONFIG_OPTIONS and
3
3
  * REFRESH_CONFIG_OPTIONS return on Frigg's existing, authenticated /api/integrations/:id/config/options routes.
4
4
  *
5
5
  * The form's data has exactly the shape of Integration.config, so what the management UI PATCHes back is the config:
@@ -5,7 +5,7 @@
5
5
  "version": "1.0.0",
6
6
  "display": {
7
7
  "label": "Pipedrive \u21c4 HubSpot people",
8
- "description": "Keeps Pipedrive people and HubSpot contacts the same person on both sides, matched by email then phone. Sync only: no workflows, no UI.",
8
+ "description": "Keeps Pipedrive people and HubSpot contacts the same person on both sides, matched by email, then by phone.",
9
9
  "category": "CRM",
10
10
  "tags": [
11
11
  "crm",
@@ -69,7 +69,7 @@
69
69
  "installed": {
70
70
  "hook": {
71
71
  "id": "WHk2c9",
72
- "url": "https://api.example.com/api/data-defined-integration/webhooks/int_7f3a9c?module=phone",
72
+ "url": "https://api.example.com/api/integration-runtime-integration/webhooks/int_7f3a9c?module=phone",
73
73
  "events": [
74
74
  "call.completed",
75
75
  "call.ringing",
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Expressions for data-defined integrations (v0.1).
2
+ * Expressions for integrations published as definitions (v0.1).
3
3
  *
4
4
  * Three forms, and no others (nothing is ever passed to eval):
5
5
  * 1. Reference "$.event.contact.id" A string that is exactly a path. Resolves to the value, keeping its type.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Data-defined integrations, v0.1 (ADR-063). An integration as a document: modules, syncs, workflows, queries,
2
+ * Integrations published as definitions, v0.1 (ADR-063). An integration as a document: modules, syncs, workflows, queries,
3
3
  * UI surfaces and templates, run by one runtime class on the existing Frigg infrastructure.
4
4
  */
5
5
  const fs = require('fs');
@@ -9,7 +9,7 @@ const { compile, routeWebhook, eventName } = require('./compile');
9
9
  const { execute } = require('./interpreter');
10
10
  const { makePorts, runWorkflow, runQuery, renderSurface, resolveView, runLifecycle, resolveOptions } = require('./runtime');
11
11
  const { diff, checkBump, planUpgrade } = require('./versioning');
12
- const { defineIntegrationClass, defineSharedIntegrationClass, extendIntegrationClass, memoryDefinitions } = require('./data-defined-integration');
12
+ const { defineIntegrationClass, defineSharedIntegrationClass, extendIntegrationClass, memoryDefinitions } = require('./integration-runtime');
13
13
  const { BUILTINS } = require('./steps');
14
14
  const { TRANSFORMS, applyMapping } = require('./transforms');
15
15
  const { FILTERS } = require('./expressions');