4 Commits

Author SHA1 Message Date
cd85f90ef1 0.0.21 2025-09-01 21:25:47 +02:00
38fbb1922a 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.
2025-09-01 21:25:47 +02:00
dfcc5c0fce 0.0.20 2025-09-01 21:13:01 +02:00
089e12ac7a FINAL APPROACH: Modify collection configs at plugin config time
- This is the ONLY time we can modify collection configs before PayloadCMS finalizes them
- Directly push hooks into config.collections[].hooks.afterChange arrays
- This happens BEFORE PayloadCMS processes and freezes the configurations
- If this doesn't work, the plugin architecture is fundamentally incompatible

This is the last possible approach - modifying the actual collection config objects before they're processed by PayloadCMS.
2025-09-01 21:13:01 +02:00
4 changed files with 247 additions and 42 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!

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@xtr-dev/payload-workflows",
"version": "0.0.19",
"version": "0.0.21",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@xtr-dev/payload-workflows",
"version": "0.0.19",
"version": "0.0.21",
"license": "MIT",
"dependencies": {
"jsonpath-plus": "^10.3.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@xtr-dev/payload-automation",
"version": "0.0.19",
"version": "0.0.21",
"description": "PayloadCMS Automation Plugin - Comprehensive workflow automation system with visual workflow building, execution tracking, and step types",
"license": "MIT",
"type": "module",

View File

@@ -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 => {
@@ -52,7 +58,91 @@ export const workflowsPlugin =
applyCollectionsConfig<TSlug>(pluginOptions, config)
// Revert: Don't apply hooks in config phase - user collections don't exist yet
// CRITICAL: Modify existing collection configs BEFORE PayloadCMS processes them
// This is the ONLY time we can add hooks that will actually work
const logger = getConfigLogger()
logger.info('Attempting to modify collection configs before PayloadCMS initialization...')
if (config.collections && pluginOptions.collectionTriggers) {
for (const [triggerSlug, triggerConfig] of Object.entries(pluginOptions.collectionTriggers)) {
if (!triggerConfig) continue
// Find the collection config that matches
const collectionIndex = config.collections.findIndex(c => c.slug === triggerSlug)
if (collectionIndex === -1) {
logger.warn(`Collection '${triggerSlug}' not found in config.collections`)
continue
}
const collection = config.collections[collectionIndex]
logger.info(`Found collection '${triggerSlug}' - modifying its hooks...`)
// Initialize hooks if needed
if (!collection.hooks) {
collection.hooks = {}
}
if (!collection.hooks.afterChange) {
collection.hooks.afterChange = []
}
// 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 {
// 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) {
global.console.error('❌ Hook execution error:', error)
// Don't throw - just log
}
// 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)
logger.info(`Added automation hook to '${triggerSlug}' - hook count: ${collection.hooks.afterChange.length}`)
}
}
if (!config.jobs) {
config.jobs = {tasks: []}
@@ -103,44 +193,8 @@ export const workflowsPlugin =
// Register executor globally
setWorkflowExecutor(executor)
// DIRECT RUNTIME HOOK REGISTRATION - bypass all abstractions
logger.info('Applying hooks directly to runtime collections...')
for (const [collectionSlug, triggerConfig] of Object.entries(pluginOptions.collectionTriggers || {})) {
if (!triggerConfig) continue
const collection = payload.collections[collectionSlug as TSlug]
if (!collection) {
logger.warn(`Collection '${collectionSlug}' not found at runtime`)
continue
}
console.log(`🚨 DIRECTLY MANIPULATING ${collectionSlug} COLLECTION`)
console.log(`🚨 Current afterChange hooks:`, collection.config.hooks?.afterChange?.length || 0)
// Ensure hooks array exists
if (!collection.config.hooks) {
collection.config.hooks = {} as any // PayloadCMS hooks type is complex, bypass for direct manipulation
}
if (!collection.config.hooks.afterChange) {
collection.config.hooks.afterChange = []
}
// Add ultra-simple test hook
const ultraSimpleHook = async (change: { collection: { slug: string }, operation: string, doc?: { id?: string } }) => {
console.log('🎯 ULTRA SIMPLE HOOK EXECUTED! 🎯')
console.log('🎯 Collection:', change.collection.slug)
console.log('🎯 Operation:', change.operation)
console.log('🎯 SUCCESS - Direct runtime registration works!')
}
// Insert at beginning to ensure it runs first
collection.config.hooks.afterChange.unshift(ultraSimpleHook)
console.log(`🚨 Added hook to ${collectionSlug} - new count:`, collection.config.hooks.afterChange.length)
logger.info(`Direct hook registration completed for: ${collectionSlug}`)
}
// Hooks are now registered during config phase - just log status
logger.info('Hooks were registered during config phase - executor now available')
logger.info('Initializing global hooks...')
initGlobalHooks(payload, logger, executor)