chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
import { generateMockPayloadFromOutputsDefinition } from '@/lib/workflows/triggers/trigger-utils'
|
||||
import type { SubBlockConfig } from '@/blocks/types'
|
||||
import { TRIGGER_REGISTRY } from '@/triggers/registry'
|
||||
import type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* IDs that should NOT be namespaced because they are shared across triggers
|
||||
* or are the control mechanism for trigger selection
|
||||
*/
|
||||
const SHARED_SUBBLOCK_IDS = new Set(['selectedTriggerId'])
|
||||
|
||||
/**
|
||||
* Checks if a subBlock is display-only (not user-editable).
|
||||
* Display-only subBlocks should be namespaced to avoid conflicts when
|
||||
* multiple triggers show different content for the same conceptual field.
|
||||
*/
|
||||
function isDisplayOnlySubBlock(subBlock: SubBlockConfig): boolean {
|
||||
// Text type is always display-only
|
||||
if (subBlock.type === 'text') {
|
||||
return true
|
||||
}
|
||||
|
||||
// ReadOnly inputs are display-only
|
||||
if (subBlock.readOnly === true) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespaces a subBlock ID with the trigger ID to avoid conflicts when
|
||||
* multiple triggers are merged into a single block.
|
||||
*
|
||||
* Only namespaces display-only subBlocks (readOnly or text type) that have
|
||||
* a condition on selectedTriggerId. User-input fields are NOT namespaced
|
||||
* so their values persist when switching between triggers.
|
||||
*/
|
||||
function namespaceSubBlockId(subBlock: SubBlockConfig, triggerId: string): SubBlockConfig {
|
||||
// Don't namespace shared IDs
|
||||
if (SHARED_SUBBLOCK_IDS.has(subBlock.id)) {
|
||||
return subBlock
|
||||
}
|
||||
|
||||
// Only namespace display-only subBlocks to avoid content conflicts
|
||||
// User-input fields should remain shared so values persist across trigger switches
|
||||
if (!isDisplayOnlySubBlock(subBlock)) {
|
||||
return subBlock
|
||||
}
|
||||
|
||||
// Only namespace if the subBlock has a condition on selectedTriggerId
|
||||
// These are the ones that are trigger-specific and will conflict when merged
|
||||
const condition =
|
||||
typeof subBlock.condition === 'function' ? subBlock.condition() : subBlock.condition
|
||||
if (condition?.field === 'selectedTriggerId') {
|
||||
return {
|
||||
...subBlock,
|
||||
id: `${subBlock.id}_${triggerId}`,
|
||||
}
|
||||
}
|
||||
|
||||
return subBlock
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a trigger config and injects samplePayload subblock with condition.
|
||||
* Also namespaces subBlock IDs to avoid conflicts when multiple triggers
|
||||
* are merged into a single block (e.g., ...getTrigger('a').subBlocks, ...getTrigger('b').subBlocks).
|
||||
*/
|
||||
export function getTrigger(triggerId: string): TriggerConfig {
|
||||
const trigger = TRIGGER_REGISTRY[triggerId]
|
||||
if (!trigger) {
|
||||
throw new Error(`Trigger not found: ${triggerId}`)
|
||||
}
|
||||
|
||||
// Filter out deprecated trigger-save subblocks from legacy stored data
|
||||
const subBlocks = trigger.subBlocks
|
||||
.filter(
|
||||
(subBlock) => subBlock.id !== 'triggerSave' && (subBlock.type as string) !== 'trigger-save'
|
||||
)
|
||||
.map((subBlock) => namespaceSubBlockId(subBlock, triggerId))
|
||||
|
||||
const clonedTrigger = { ...trigger, subBlocks }
|
||||
|
||||
// Inject samplePayload for webhooks/pollers with condition
|
||||
if (trigger.webhook || trigger.id.includes('webhook') || trigger.id.includes('poller')) {
|
||||
const samplePayloadExists = clonedTrigger.subBlocks.some(
|
||||
(sb) => sb.id === 'samplePayload' || sb.id === `samplePayload_${triggerId}`
|
||||
)
|
||||
|
||||
if (!samplePayloadExists && trigger.outputs) {
|
||||
const mockPayload = generateMockPayloadFromOutputsDefinition(trigger.outputs)
|
||||
const generatedPayload = JSON.stringify(mockPayload, null, 2)
|
||||
|
||||
const samplePayloadSubBlock: SubBlockConfig = {
|
||||
id: `samplePayload_${triggerId}`,
|
||||
title: 'Event Payload Example',
|
||||
type: 'code',
|
||||
language: 'json',
|
||||
defaultValue: generatedPayload,
|
||||
readOnly: true,
|
||||
collapsible: true,
|
||||
defaultCollapsed: true,
|
||||
hideFromPreview: true,
|
||||
mode: 'trigger',
|
||||
condition: {
|
||||
field: 'selectedTriggerId',
|
||||
value: trigger.id,
|
||||
},
|
||||
}
|
||||
|
||||
clonedTrigger.subBlocks.push(samplePayloadSubBlock)
|
||||
}
|
||||
}
|
||||
|
||||
return clonedTrigger
|
||||
}
|
||||
|
||||
export function getTriggersByProvider(provider: string): TriggerConfig[] {
|
||||
return Object.values(TRIGGER_REGISTRY)
|
||||
.filter((trigger) => trigger.provider === provider)
|
||||
.map((trigger) => getTrigger(trigger.id))
|
||||
}
|
||||
|
||||
export function getAllTriggers(): TriggerConfig[] {
|
||||
return Object.keys(TRIGGER_REGISTRY).map((triggerId) => getTrigger(triggerId))
|
||||
}
|
||||
|
||||
export function getTriggerIds(): string[] {
|
||||
return Object.keys(TRIGGER_REGISTRY)
|
||||
}
|
||||
|
||||
export function isTriggerValid(triggerId: string): boolean {
|
||||
return triggerId in TRIGGER_REGISTRY
|
||||
}
|
||||
|
||||
export type { TriggerConfig } from '@/triggers/types'
|
||||
|
||||
/**
|
||||
* Options for building trigger subBlocks
|
||||
*/
|
||||
export interface BuildTriggerSubBlocksOptions {
|
||||
/** The trigger ID (e.g., 'lemlist_email_replied') */
|
||||
triggerId: string
|
||||
/** Dropdown options for selecting trigger type */
|
||||
triggerOptions: Array<{ label: string; id: string }>
|
||||
/** Whether to include the trigger type dropdown (only for primary trigger) */
|
||||
includeDropdown?: boolean
|
||||
/** HTML setup instructions to display */
|
||||
setupInstructions: string
|
||||
/** Additional fields to insert before the save button (e.g., campaign filters) */
|
||||
extraFields?: SubBlockConfig[]
|
||||
/** Webhook URL placeholder text */
|
||||
webhookPlaceholder?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic builder for trigger subBlocks.
|
||||
* Creates a consistent structure: [dropdown?] -> webhookUrl -> extraFields -> instructions
|
||||
*
|
||||
* Usage:
|
||||
* - Primary trigger: `buildTriggerSubBlocks({ ...options, includeDropdown: true })`
|
||||
* - Secondary triggers: `buildTriggerSubBlocks({ ...options })` (no dropdown)
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Primary trigger (with dropdown)
|
||||
* subBlocks: buildTriggerSubBlocks({
|
||||
* triggerId: 'service_event_a',
|
||||
* triggerOptions: serviceTriggerOptions,
|
||||
* includeDropdown: true,
|
||||
* setupInstructions: serviceSetupInstructions('eventA'),
|
||||
* })
|
||||
*
|
||||
* // Secondary trigger (no dropdown)
|
||||
* subBlocks: buildTriggerSubBlocks({
|
||||
* triggerId: 'service_event_b',
|
||||
* triggerOptions: serviceTriggerOptions,
|
||||
* setupInstructions: serviceSetupInstructions('eventB'),
|
||||
* })
|
||||
* ```
|
||||
*/
|
||||
export function buildTriggerSubBlocks(options: BuildTriggerSubBlocksOptions): SubBlockConfig[] {
|
||||
const {
|
||||
triggerId,
|
||||
triggerOptions,
|
||||
includeDropdown = false,
|
||||
setupInstructions,
|
||||
extraFields = [],
|
||||
webhookPlaceholder = 'Webhook URL will be generated',
|
||||
} = options
|
||||
|
||||
const blocks: SubBlockConfig[] = []
|
||||
|
||||
// Only the primary trigger includes the dropdown
|
||||
if (includeDropdown) {
|
||||
blocks.push({
|
||||
id: 'selectedTriggerId',
|
||||
title: 'Trigger Type',
|
||||
type: 'dropdown',
|
||||
mode: 'trigger',
|
||||
options: triggerOptions,
|
||||
value: () => triggerId,
|
||||
required: true,
|
||||
})
|
||||
}
|
||||
|
||||
// Webhook URL display (common to all triggers)
|
||||
// ID will be namespaced by getTrigger() when merged into blocks
|
||||
blocks.push({
|
||||
id: 'webhookUrlDisplay',
|
||||
title: 'Webhook URL',
|
||||
type: 'short-input',
|
||||
readOnly: true,
|
||||
showCopyButton: true,
|
||||
useWebhookUrl: true,
|
||||
placeholder: webhookPlaceholder,
|
||||
mode: 'trigger',
|
||||
condition: { field: 'selectedTriggerId', value: triggerId },
|
||||
})
|
||||
|
||||
// Insert any extra fields (campaign filters, event types, etc.)
|
||||
if (extraFields.length > 0) {
|
||||
blocks.push(...extraFields)
|
||||
}
|
||||
|
||||
// Setup instructions
|
||||
// ID will be namespaced by getTrigger() when merged into blocks
|
||||
blocks.push({
|
||||
id: 'triggerInstructions',
|
||||
title: 'Setup Instructions',
|
||||
hideFromPreview: true,
|
||||
type: 'text',
|
||||
defaultValue: setupInstructions,
|
||||
mode: 'trigger',
|
||||
condition: { field: 'selectedTriggerId', value: triggerId },
|
||||
})
|
||||
|
||||
return blocks
|
||||
}
|
||||
Reference in New Issue
Block a user