From 38fbb1922a0eb30cd19a6029bed71726fa9bc6d7 Mon Sep 17 00:00:00 2001 From: Bas van den Aakster Date: Mon, 1 Sep 2025 21:25:47 +0200 Subject: [PATCH] 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. --- CUSTOMER-DIAGNOSTIC.md | 151 +++++++++++++++++++++++++++++++++++++++++ src/plugin/index.ts | 82 ++++++++++++++-------- 2 files changed, 205 insertions(+), 28 deletions(-) create mode 100644 CUSTOMER-DIAGNOSTIC.md diff --git a/CUSTOMER-DIAGNOSTIC.md b/CUSTOMER-DIAGNOSTIC.md new file mode 100644 index 0000000..a3b346b --- /dev/null +++ b/CUSTOMER-DIAGNOSTIC.md @@ -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! \ No newline at end of file diff --git a/src/plugin/index.ts b/src/plugin/index.ts index ecaf6aa..b44098b 100644 --- a/src/plugin/index.ts +++ b/src/plugin/index.ts @@ -21,6 +21,12 @@ let globalExecutor: WorkflowExecutor | null = null const setWorkflowExecutor = (executor: WorkflowExecutor) => { console.log('🚨 SETTING GLOBAL 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 => { @@ -79,38 +85,58 @@ export const workflowsPlugin = collection.hooks.afterChange = [] } - // Add our hook DIRECTLY to the collection config - // This happens BEFORE PayloadCMS processes the config - const automationHook = async (args: any) => { - console.log('🔥🔥🔥 AUTOMATION HOOK FROM CONFIG PHASE! 🔥🔥🔥') - 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!') + // Create a properly bound hook function that doesn't rely on closures + // Use a simple function that PayloadCMS can definitely execute + const automationHook = Object.assign( + async function payloadAutomationHook(args: any) { try { - await executor.executeTriggeredWorkflows( - args.collection.slug, - args.operation, - args.doc, - args.previousDoc, - args.req - ) - console.log('✅ Workflow execution completed!') + // Use global console to ensure output + global.console.log('🔥🔥🔥 AUTOMATION HOOK EXECUTED! 🔥🔥🔥') + global.console.log('Collection:', args?.collection?.slug) + global.console.log('Operation:', args?.operation) + global.console.log('Doc ID:', args?.doc?.id) + + // Try multiple ways to get the executor + 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) { - 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 collection.hooks.afterChange.push(automationHook)