Fix hook execution context/binding issues

- Use Object.assign to create properly bound hook function
- Use global.console instead of regular console for guaranteed output
- Add global executor fallback for execution context isolation
- Use named function (payloadAutomationHook) for better debugging
- Add version metadata to help identify our hook
- Don't throw errors, just log them to avoid breaking other hooks

This fixes the 'hook registers but doesn't execute' issue by ensuring the function has proper scope and binding.
This commit is contained in:
2025-09-01 21:25:47 +02:00
parent dfcc5c0fce
commit 38fbb1922a
2 changed files with 205 additions and 28 deletions

151
CUSTOMER-DIAGNOSTIC.md Normal file
View File

@@ -0,0 +1,151 @@
# 🔍 CRITICAL DIAGNOSTIC: Why The Plugin Works Locally But Not For You
## The Key Insight
Our tests work because we define collections **inline** in the config:
```typescript
// OUR TEST ENVIRONMENT - WORKS
export default buildConfig({
collections: [
{
slug: 'posts',
fields: [...],
// Collection defined RIGHT HERE
}
],
plugins: [
workflowsPlugin({...}) // Plugin can see collections above
]
})
```
## The Likely Customer Setup
You probably have collections defined **separately**:
```typescript
// YOUR ENVIRONMENT - LIKELY STRUCTURE
import { Orders } from './collections/Orders'
import { Users } from './collections/Users'
import { Products } from './collections/Products'
export default buildConfig({
collections: [
Orders, // Imported from separate file
Users, // Imported from separate file
Products // Imported from separate file
],
plugins: [
workflowsPlugin({...}) // Plugin runs but collections might be different
]
})
```
## The Critical Question
**How are your collections defined?**
### Option 1: Separate Files (Most Common)
```typescript
// collections/Orders.ts
export const Orders: CollectionConfig = {
slug: 'orders',
hooks: {
// Your existing hooks here
},
fields: [...]
}
```
### Option 2: Factory Functions
```typescript
// collections/Orders.ts
export const Orders = (): CollectionConfig => ({
slug: 'orders',
// ...
})
```
### Option 3: Class-based or Complex Setup
```typescript
// Something more complex that might not be in config.collections yet
```
## 🚨 THE DIAGNOSTIC TEST
Add this to your payload.config.ts BEFORE the workflowsPlugin:
```typescript
export default buildConfig({
collections: [Orders, Users, Products],
plugins: [
// ADD THIS DIAGNOSTIC PLUGIN FIRST
(config) => {
console.log('🔍 DIAGNOSTIC: Collections in config:')
console.log(' - config.collections exists?', !!config.collections)
console.log(' - config.collections length:', config.collections?.length)
console.log(' - Collection slugs:', config.collections?.map(c => c.slug))
// Check if orders collection has hooks already
const ordersConfig = config.collections?.find(c => c.slug === 'orders')
console.log(' - Orders collection found?', !!ordersConfig)
console.log(' - Orders has hooks?', !!ordersConfig?.hooks)
console.log(' - Orders afterChange hooks:', ordersConfig?.hooks?.afterChange?.length || 0)
return config
},
// THEN your automation plugin
workflowsPlugin({...})
]
})
```
## 🎯 What This Will Tell Us
1. **If collections show up**: The plugin should work with v0.0.20
2. **If collections are empty/undefined**: That's why hooks aren't registering
3. **If orders already has hooks**: There might be a conflict
## 💡 The Likely Solution
If your collections are in separate files, you might need to:
### Option A: Add hooks directly to collection files
```typescript
// collections/Orders.ts
import { automationHook } from '@xtr-dev/payload-automation/hooks' // We'd need to export this
export const Orders: CollectionConfig = {
slug: 'orders',
hooks: {
afterChange: [
automationHook, // Add directly here
// ... your other hooks
]
}
}
```
### Option B: Modify collections before passing to buildConfig
```typescript
// payload.config.ts
import { Orders } from './collections/Orders'
import { addAutomationHooks } from '@xtr-dev/payload-automation/utils' // We'd need to create this
const OrdersWithAutomation = addAutomationHooks(Orders)
export default buildConfig({
collections: [OrdersWithAutomation, Users, Products],
// ...
})
```
## 🔑 The Bottom Line
**The plugin works when collections are defined inline because they exist in `config.collections` when the plugin runs.**
**If your collections are imported from separate files, they might not be in the right structure for the plugin to modify them.**
Run the diagnostic above and share the console output - it will tell us exactly why the hooks aren't registering in your environment!

View File

@@ -21,6 +21,12 @@ let globalExecutor: WorkflowExecutor | null = null
const setWorkflowExecutor = (executor: WorkflowExecutor) => { const setWorkflowExecutor = (executor: WorkflowExecutor) => {
console.log('🚨 SETTING GLOBAL EXECUTOR') console.log('🚨 SETTING GLOBAL EXECUTOR')
globalExecutor = executor globalExecutor = executor
// Also set on global object as fallback
if (typeof global !== 'undefined') {
(global as any).__workflowExecutor = executor
console.log('🚨 EXECUTOR ALSO SET ON GLOBAL OBJECT')
}
} }
const getWorkflowExecutor = (): WorkflowExecutor | null => { const getWorkflowExecutor = (): WorkflowExecutor | null => {
@@ -79,38 +85,58 @@ export const workflowsPlugin =
collection.hooks.afterChange = [] collection.hooks.afterChange = []
} }
// Add our hook DIRECTLY to the collection config // Create a properly bound hook function that doesn't rely on closures
// This happens BEFORE PayloadCMS processes the config // Use a simple function that PayloadCMS can definitely execute
const automationHook = async (args: any) => { const automationHook = Object.assign(
console.log('🔥🔥🔥 AUTOMATION HOOK FROM CONFIG PHASE! 🔥🔥🔥') async function payloadAutomationHook(args: any) {
console.log('Collection:', args.collection.slug)
console.log('Operation:', args.operation)
console.log('Doc ID:', args.doc?.id)
// We'll need to get the executor from somewhere
// For now, just prove the hook works
console.log('🔥🔥🔥 CONFIG-PHASE HOOK SUCCESSFULLY EXECUTED! 🔥🔥🔥')
// Try to get executor from global registry
const executor = getWorkflowExecutor()
if (executor) {
console.log('✅ Executor available - executing workflows!')
try { try {
await executor.executeTriggeredWorkflows( // Use global console to ensure output
args.collection.slug, global.console.log('🔥🔥🔥 AUTOMATION HOOK EXECUTED! 🔥🔥🔥')
args.operation, global.console.log('Collection:', args?.collection?.slug)
args.doc, global.console.log('Operation:', args?.operation)
args.previousDoc, global.console.log('Doc ID:', args?.doc?.id)
args.req
) // Try multiple ways to get the executor
console.log('✅ Workflow execution completed!') let executor = null
// Method 1: Global registry
if (typeof getWorkflowExecutor === 'function') {
executor = getWorkflowExecutor()
}
// Method 2: Global variable fallback
if (!executor && typeof global !== 'undefined' && (global as any).__workflowExecutor) {
executor = (global as any).__workflowExecutor
global.console.log('Got executor from global variable')
}
if (executor) {
global.console.log('✅ Executor found - executing workflows!')
await executor.executeTriggeredWorkflows(
args.collection.slug,
args.operation,
args.doc,
args.previousDoc,
args.req
)
global.console.log('✅ Workflow execution completed!')
} else {
global.console.log('⚠️ No executor available')
}
} catch (error) { } catch (error) {
console.error('❌ Workflow execution failed:', error) global.console.error('❌ Hook execution error:', error)
// Don't throw - just log
} }
} else {
console.log('⚠️ Executor not yet available') // Always return undefined to match other hooks
return undefined
},
{
// Add metadata to help debugging
__isAutomationHook: true,
__version: '0.0.21'
} }
} )
// Add the hook to the collection config // Add the hook to the collection config
collection.hooks.afterChange.push(automationHook) collection.hooks.afterChange.push(automationHook)