directus-task-management/scripts/collections/create-collections.js

690 lines
24 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Create All Directus Collections
* Main script to create all 10 core collections for the Task Management Suite
*/
const { getAuthenticatedClient } = require('../utils/directus-client');
const { createCollection, createFields, FIELD_TYPES, getSystemFields } = require('../utils/schema-helpers');
/**
* Create Projects Collection
*/
async function createProjectsCollection(client) {
console.log('\n📦 Creating Projects Collection...');
// Create collection
await createCollection(client, {
collection: 'projects',
meta: {
collection: 'projects',
icon: 'folder',
note: 'Project hierarchy and organization',
display_template: '{{name}}',
archive_field: 'status',
archive_value: 'archived',
sort_field: 'sort',
accountability: 'all',
color: '#6366F1'
},
schema: {
name: 'projects'
}
});
// Create fields
const fields = [
FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }),
FIELD_TYPES.string('name', { required: true, width: 'half' }),
FIELD_TYPES.text('description', { interface: 'input-rich-text-html' }),
FIELD_TYPES.dropdown('status', [
{ text: 'Active', value: 'active' },
{ text: 'On Hold', value: 'on_hold' },
{ text: 'Completed', value: 'completed' },
{ text: 'Archived', value: 'archived' }
], { default: 'active', width: 'half' }),
FIELD_TYPES.m2o('parent_project', 'projects', { nullable: true }),
FIELD_TYPES.string('repository_url', { nullable: true, interface: 'input', meta: { placeholder: 'https://github.com/...' } }),
FIELD_TYPES.dropdown('bmad_workflow_type', [
{ text: 'Greenfield', value: 'greenfield' },
{ text: 'Brownfield', value: 'brownfield' },
{ text: 'Maintenance', value: 'maintenance' }
], { nullable: true, width: 'half' }),
FIELD_TYPES.string('task_master_project_id', { nullable: true, width: 'half' }),
FIELD_TYPES.dropdown('priority', [
{ text: 'Low', value: 'low' },
{ text: 'Medium', value: 'medium' },
{ text: 'High', value: 'high' },
{ text: 'Critical', value: 'critical' }
], { default: 'medium', width: 'half' }),
FIELD_TYPES.date('start_date', { nullable: true, width: 'half' }),
FIELD_TYPES.date('due_date', { nullable: true, width: 'half' }),
FIELD_TYPES.integer('completion_percentage', {
default: 0,
width: 'half',
interface: 'slider',
options: { min: 0, max: 100, step: 1 }
}),
FIELD_TYPES.m2o('created_by', 'directus_users', { width: 'half' }),
FIELD_TYPES.m2o('assigned_to', 'directus_users', { nullable: true, width: 'half' }),
FIELD_TYPES.json('metadata', { nullable: true }),
FIELD_TYPES.integer('sort', { hidden: true, interface: 'input' }),
...getSystemFields()
];
await createFields(client, 'projects', fields);
}
/**
* Create Task Statuses Collection
*/
async function createTaskStatusesCollection(client) {
console.log('\n📦 Creating Task Statuses Collection...');
await createCollection(client, {
collection: 'task_statuses',
meta: {
collection: 'task_statuses',
icon: 'assignment_turned_in',
note: 'Customizable status definitions aligned with workflows',
display_template: '{{name}}',
sort_field: 'sort_order',
accountability: 'all',
color: '#10B981'
},
schema: {
name: 'task_statuses'
}
});
const fields = [
FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }),
FIELD_TYPES.string('name', { required: true, width: 'half' }),
FIELD_TYPES.string('slug', { required: true, width: 'half' }),
FIELD_TYPES.text('description'),
FIELD_TYPES.string('color', {
default: '#6366f1',
interface: 'select-color',
width: 'half'
}),
FIELD_TYPES.string('icon', {
interface: 'select-icon',
width: 'half'
}),
FIELD_TYPES.dropdown('category', [
{ text: 'To Do', value: 'todo' },
{ text: 'In Progress', value: 'in_progress' },
{ text: 'Review', value: 'review' },
{ text: 'Done', value: 'done' },
{ text: 'Blocked', value: 'blocked' }
], { width: 'half' }),
FIELD_TYPES.boolean('is_final', { default: false, width: 'half' }),
FIELD_TYPES.json('auto_transition_rules', { nullable: true }),
FIELD_TYPES.dropdown('workflow_type', [
{ text: 'BMad', value: 'bmad' },
{ text: 'Task Master', value: 'task_master' },
{ text: 'GitHub', value: 'github' },
{ text: 'Custom', value: 'custom' }
], { width: 'half', nullable: true }),
FIELD_TYPES.integer('sort_order', { default: 0, width: 'half' }),
FIELD_TYPES.boolean('active', { default: true, width: 'half' }),
...getSystemFields()
];
await createFields(client, 'task_statuses', fields);
}
/**
* Create Tasks Collection
*/
async function createTasksCollection(client) {
console.log('\n📦 Creating Tasks Collection...');
await createCollection(client, {
collection: 'tasks',
meta: {
collection: 'tasks',
icon: 'task_alt',
note: 'Core task entity with rich metadata',
display_template: '{{title}}',
archive_field: 'status',
sort_field: 'sort',
accountability: 'all',
color: '#3B82F6'
},
schema: {
name: 'tasks'
}
});
const fields = [
FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }),
FIELD_TYPES.string('title', { required: true }),
FIELD_TYPES.text('description', { interface: 'input-rich-text-html' }),
FIELD_TYPES.m2o('status', 'task_statuses', { width: 'half' }),
FIELD_TYPES.dropdown('priority', [
{ text: 'Lowest', value: 'lowest' },
{ text: 'Low', value: 'low' },
{ text: 'Medium', value: 'medium' },
{ text: 'High', value: 'high' },
{ text: 'Highest', value: 'highest' }
], { default: 'medium', width: 'half' }),
FIELD_TYPES.dropdown('task_type', [
{ text: 'Feature', value: 'feature' },
{ text: 'Bug', value: 'bug' },
{ text: 'Enhancement', value: 'enhancement' },
{ text: 'Research', value: 'research' },
{ text: 'Maintenance', value: 'maintenance' }
], { width: 'half' }),
FIELD_TYPES.dropdown('complexity', [
{ text: 'Trivial', value: 'trivial' },
{ text: 'Minor', value: 'minor' },
{ text: 'Major', value: 'major' },
{ text: 'Critical', value: 'critical' }
], { width: 'half', nullable: true }),
FIELD_TYPES.integer('story_points', {
width: 'half',
nullable: true,
interface: 'select-dropdown',
options: {
choices: [
{ text: '1', value: 1 },
{ text: '2', value: 2 },
{ text: '3', value: 3 },
{ text: '5', value: 5 },
{ text: '8', value: 8 },
{ text: '13', value: 13 },
{ text: '21', value: 21 }
]
}
}),
// Project relationships
FIELD_TYPES.m2o('project', 'projects', { required: true, width: 'half' }),
FIELD_TYPES.m2o('parent_task', 'tasks', { nullable: true, width: 'half' }),
FIELD_TYPES.m2o('epic', 'tasks', { nullable: true, width: 'half' }),
// Assignment and ownership
FIELD_TYPES.m2o('created_by', 'directus_users', { width: 'half' }),
FIELD_TYPES.m2o('assigned_to', 'directus_users', { nullable: true, width: 'half' }),
FIELD_TYPES.string('ai_agent_assigned', { nullable: true, width: 'half' }),
FIELD_TYPES.m2o('reviewer', 'directus_users', { nullable: true, width: 'half' }),
// Time and progress
FIELD_TYPES.decimal('estimated_hours', { nullable: true, width: 'half', precision: 10, scale: 2 }),
FIELD_TYPES.decimal('actual_hours', { nullable: true, width: 'half', precision: 10, scale: 2, readonly: true }),
FIELD_TYPES.integer('progress_percentage', {
default: 0,
width: 'half',
interface: 'slider',
options: { min: 0, max: 100, step: 5 }
}),
FIELD_TYPES.date('start_date', { nullable: true, width: 'half' }),
FIELD_TYPES.date('due_date', { nullable: true, width: 'half' }),
FIELD_TYPES.datetime('completed_at', { nullable: true, width: 'half' }),
// Integration fields
FIELD_TYPES.string('task_master_id', { nullable: true, width: 'half' }),
FIELD_TYPES.string('github_issue_url', { nullable: true }),
FIELD_TYPES.string('bmad_story_id', { nullable: true, width: 'half' }),
FIELD_TYPES.json('external_refs', { nullable: true }),
// AI and automation
FIELD_TYPES.boolean('ai_generated', { default: false, width: 'half' }),
FIELD_TYPES.json('ai_context', { nullable: true }),
FIELD_TYPES.boolean('auto_status_updates', { default: false, width: 'half' }),
// Metadata
FIELD_TYPES.text('acceptance_criteria', { interface: 'input-rich-text-html', nullable: true }),
FIELD_TYPES.text('definition_of_done', { interface: 'input-rich-text-html', nullable: true }),
FIELD_TYPES.text('notes', { interface: 'input-rich-text-html', nullable: true }),
FIELD_TYPES.integer('sort', { hidden: true, interface: 'input' }),
...getSystemFields()
];
await createFields(client, 'tasks', fields);
}
/**
* Create Task Dependencies Collection
*/
async function createTaskDependenciesCollection(client) {
console.log('\n📦 Creating Task Dependencies Collection...');
await createCollection(client, {
collection: 'task_dependencies',
meta: {
collection: 'task_dependencies',
icon: 'device_hub',
note: 'Task relationship and dependency management',
display_template: '{{dependent_task}} -> {{dependency_task}}',
accountability: 'all',
color: '#F59E0B'
},
schema: {
name: 'task_dependencies'
}
});
const fields = [
FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }),
FIELD_TYPES.m2o('dependent_task', 'tasks', { required: true, width: 'half' }),
FIELD_TYPES.m2o('dependency_task', 'tasks', { required: true, width: 'half' }),
FIELD_TYPES.dropdown('dependency_type', [
{ text: 'Blocks', value: 'blocks' },
{ text: 'Relates To', value: 'relates_to' },
{ text: 'Duplicates', value: 'duplicates' },
{ text: 'Subtask Of', value: 'subtask_of' }
], { default: 'blocks', width: 'half' }),
FIELD_TYPES.boolean('is_hard_dependency', { default: true, width: 'half' }),
FIELD_TYPES.m2o('created_by', 'directus_users'),
...getSystemFields()
];
await createFields(client, 'task_dependencies', fields);
}
/**
* Create Task Time Entries Collection
*/
async function createTaskTimeEntriesCollection(client) {
console.log('\n📦 Creating Task Time Entries Collection...');
await createCollection(client, {
collection: 'task_time_entries',
meta: {
collection: 'task_time_entries',
icon: 'schedule',
note: 'Time tracking and progress data',
display_template: '{{hours}}h - {{description}}',
accountability: 'all',
color: '#8B5CF6'
},
schema: {
name: 'task_time_entries'
}
});
const fields = [
FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }),
FIELD_TYPES.m2o('task', 'tasks', { required: true }),
FIELD_TYPES.m2o('user', 'directus_users', { nullable: true, width: 'half' }),
FIELD_TYPES.string('ai_agent', { nullable: true, width: 'half' }),
FIELD_TYPES.text('description'),
FIELD_TYPES.decimal('hours', { required: true, width: 'half', precision: 10, scale: 2 }),
FIELD_TYPES.dropdown('entry_type', [
{ text: 'Manual', value: 'manual' },
{ text: 'Automatic', value: 'automatic' },
{ text: 'Estimated', value: 'estimated' },
{ text: 'Actual', value: 'actual' }
], { width: 'half' }),
FIELD_TYPES.dropdown('work_type', [
{ text: 'Development', value: 'development' },
{ text: 'Testing', value: 'testing' },
{ text: 'Review', value: 'review' },
{ text: 'Research', value: 'research' },
{ text: 'Meeting', value: 'meeting' }
], { width: 'half' }),
FIELD_TYPES.boolean('billable', { default: false, width: 'half' }),
FIELD_TYPES.datetime('started_at', { nullable: true, width: 'half' }),
FIELD_TYPES.datetime('ended_at', { nullable: true, width: 'half' }),
...getSystemFields()
];
await createFields(client, 'task_time_entries', fields);
}
/**
* Create Task Templates Collection
*/
async function createTaskTemplatesCollection(client) {
console.log('\n📦 Creating Task Templates Collection...');
await createCollection(client, {
collection: 'task_templates',
meta: {
collection: 'task_templates',
icon: 'content_copy',
note: 'Reusable task patterns for BMad methodology',
display_template: '{{name}}',
accountability: 'all',
color: '#EC4899'
},
schema: {
name: 'task_templates'
}
});
const fields = [
FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }),
FIELD_TYPES.string('name', { required: true }),
FIELD_TYPES.text('description'),
FIELD_TYPES.dropdown('template_type', [
{ text: 'BMad Epic', value: 'bmad_epic' },
{ text: 'BMad Story', value: 'bmad_story' },
{ text: 'Feature', value: 'feature' },
{ text: 'Bug Fix', value: 'bug_fix' },
{ text: 'Maintenance', value: 'maintenance' }
], { width: 'half' }),
FIELD_TYPES.dropdown('workflow_stage', [
{ text: 'Planning', value: 'planning' },
{ text: 'Development', value: 'development' },
{ text: 'Testing', value: 'testing' },
{ text: 'Deployment', value: 'deployment' }
], { width: 'half' }),
// Template data
FIELD_TYPES.string('title_template'),
FIELD_TYPES.text('description_template'),
FIELD_TYPES.text('acceptance_criteria_template'),
FIELD_TYPES.dropdown('default_priority', [
{ text: 'Lowest', value: 'lowest' },
{ text: 'Low', value: 'low' },
{ text: 'Medium', value: 'medium' },
{ text: 'High', value: 'high' },
{ text: 'Highest', value: 'highest' }
], { width: 'half' }),
FIELD_TYPES.dropdown('default_complexity', [
{ text: 'Trivial', value: 'trivial' },
{ text: 'Minor', value: 'minor' },
{ text: 'Major', value: 'major' },
{ text: 'Critical', value: 'critical' }
], { width: 'half', nullable: true }),
FIELD_TYPES.decimal('estimated_hours', { nullable: true, width: 'half', precision: 10, scale: 2 }),
// BMad integration
FIELD_TYPES.string('bmad_agent_assignment', { nullable: true, width: 'half' }),
FIELD_TYPES.json('required_skills', { nullable: true }),
FIELD_TYPES.json('checklist_items', { nullable: true }),
// Usage tracking
FIELD_TYPES.integer('usage_count', { default: 0, width: 'half', readonly: true }),
FIELD_TYPES.datetime('last_used', { nullable: true, width: 'half', readonly: true }),
FIELD_TYPES.m2o('created_by', 'directus_users'),
...getSystemFields()
];
await createFields(client, 'task_templates', fields);
}
/**
* Create Task AI Contexts Collection
*/
async function createTaskAIContextsCollection(client) {
console.log('\n📦 Creating Task AI Contexts Collection...');
await createCollection(client, {
collection: 'task_ai_contexts',
meta: {
collection: 'task_ai_contexts',
icon: 'psychology',
note: 'AI agent context and prompt data',
display_template: '{{ai_agent_type}} - {{context_type}}',
accountability: 'all',
color: '#06B6D4'
},
schema: {
name: 'task_ai_contexts'
}
});
const fields = [
FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }),
FIELD_TYPES.m2o('task', 'tasks', { required: true }),
FIELD_TYPES.string('ai_agent_type', { width: 'half' }),
FIELD_TYPES.dropdown('context_type', [
{ text: 'Prompt', value: 'prompt' },
{ text: 'Result', value: 'result' },
{ text: 'Feedback', value: 'feedback' },
{ text: 'Error', value: 'error' }
], { width: 'half' }),
FIELD_TYPES.json('context_data'),
FIELD_TYPES.datetime('execution_timestamp'),
FIELD_TYPES.boolean('success', { width: 'half' }),
FIELD_TYPES.integer('tokens_used', { nullable: true, width: 'half' }),
FIELD_TYPES.integer('execution_time_ms', { nullable: true, width: 'half' }),
...getSystemFields()
];
await createFields(client, 'task_ai_contexts', fields);
}
/**
* Create Task BMad Workflows Collection
*/
async function createTaskBMadWorkflowsCollection(client) {
console.log('\n📦 Creating Task BMad Workflows Collection...');
await createCollection(client, {
collection: 'task_bmad_workflows',
meta: {
collection: 'task_bmad_workflows',
icon: 'account_tree',
note: 'BMad methodology integration',
display_template: '{{workflow_stage}} - {{agent_type}}',
accountability: 'all',
color: '#14B8A6'
},
schema: {
name: 'task_bmad_workflows'
}
});
const fields = [
FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }),
FIELD_TYPES.m2o('task', 'tasks', { required: true }),
FIELD_TYPES.dropdown('workflow_stage', [
{ text: 'Project Manager', value: 'pm' },
{ text: 'Analyst', value: 'analyst' },
{ text: 'Architect', value: 'architect' },
{ text: 'Product Owner', value: 'po' },
{ text: 'Scrum Master', value: 'sm' },
{ text: 'Developer', value: 'dev' },
{ text: 'QA', value: 'qa' },
{ text: 'UX Expert', value: 'ux' }
], { width: 'half' }),
FIELD_TYPES.string('agent_type', { width: 'half' }),
FIELD_TYPES.json('agent_context'),
FIELD_TYPES.dropdown('status', [
{ text: 'Not Started', value: 'not_started' },
{ text: 'In Progress', value: 'in_progress' },
{ text: 'Completed', value: 'completed' },
{ text: 'Skipped', value: 'skipped' }
], { default: 'not_started', width: 'half' }),
FIELD_TYPES.json('deliverables'),
FIELD_TYPES.text('notes'),
FIELD_TYPES.datetime('started_at', { nullable: true, width: 'half' }),
FIELD_TYPES.datetime('completed_at', { nullable: true, width: 'half' }),
...getSystemFields()
];
await createFields(client, 'task_bmad_workflows', fields);
}
/**
* Create Task Assignments Collection
*/
async function createTaskAssignmentsCollection(client) {
console.log('\n📦 Creating Task Assignments Collection...');
await createCollection(client, {
collection: 'task_assignments',
meta: {
collection: 'task_assignments',
icon: 'group',
note: 'Team assignments and collaboration',
display_template: '{{user}} - {{role}}',
accountability: 'all',
color: '#F97316'
},
schema: {
name: 'task_assignments'
}
});
const fields = [
FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }),
FIELD_TYPES.m2o('task', 'tasks', { required: true }),
FIELD_TYPES.m2o('user', 'directus_users', { nullable: true, width: 'half' }),
FIELD_TYPES.string('ai_agent', { nullable: true, width: 'half' }),
FIELD_TYPES.dropdown('role', [
{ text: 'Owner', value: 'owner' },
{ text: 'Assignee', value: 'assignee' },
{ text: 'Reviewer', value: 'reviewer' },
{ text: 'Watcher', value: 'watcher' },
{ text: 'Contributor', value: 'contributor' }
], { width: 'half' }),
FIELD_TYPES.decimal('capacity_percentage', {
nullable: true,
width: 'half',
precision: 5,
scale: 2,
interface: 'slider',
options: { min: 0, max: 100, step: 5 }
}),
FIELD_TYPES.datetime('assigned_at', { width: 'half' }),
FIELD_TYPES.datetime('started_at', { nullable: true, width: 'half' }),
FIELD_TYPES.datetime('completed_at', { nullable: true, width: 'half' }),
FIELD_TYPES.text('notes', { nullable: true }),
...getSystemFields()
];
await createFields(client, 'task_assignments', fields);
}
/**
* Create Task External References Collection
*/
async function createTaskExternalRefsCollection(client) {
console.log('\n📦 Creating Task External References Collection...');
await createCollection(client, {
collection: 'task_external_refs',
meta: {
collection: 'task_external_refs',
icon: 'link',
note: 'External system references and integrations',
display_template: '{{ref_type}}: {{ref_id}}',
accountability: 'all',
color: '#64748B'
},
schema: {
name: 'task_external_refs'
}
});
const fields = [
FIELD_TYPES.uuid('id', { primary_key: true, hidden: true }),
FIELD_TYPES.m2o('task', 'tasks', { required: true }),
FIELD_TYPES.dropdown('ref_type', [
{ text: 'GitHub Issue', value: 'github_issue' },
{ text: 'GitHub PR', value: 'github_pr' },
{ text: 'Task Master', value: 'task_master' },
{ text: 'Jira', value: 'jira' },
{ text: 'Slack Thread', value: 'slack_thread' },
{ text: 'Google Doc', value: 'google_doc' },
{ text: 'Figma', value: 'figma' },
{ text: 'Other', value: 'other' }
], { width: 'half' }),
FIELD_TYPES.string('ref_id', { required: true, width: 'half' }),
FIELD_TYPES.string('ref_url', { nullable: true }),
FIELD_TYPES.json('metadata', { nullable: true }),
FIELD_TYPES.boolean('auto_sync', { default: false, width: 'half' }),
FIELD_TYPES.datetime('last_synced', { nullable: true, width: 'half' }),
...getSystemFields()
];
await createFields(client, 'task_external_refs', fields);
}
/**
* Main function to create all collections
*/
async function main() {
console.log('🚀 Starting Directus Collections Creation...\n');
const summary = {
collections: { created: 0, existing: 0, errors: 0 },
fields: { created: 0, existing: 0, errors: 0 }
};
try {
// Get authenticated client
const client = await getAuthenticatedClient();
// Track results for each collection
const collectionFunctions = [
{ name: 'Projects', fn: createProjectsCollection },
{ name: 'Task Statuses', fn: createTaskStatusesCollection },
{ name: 'Tasks', fn: createTasksCollection },
{ name: 'Task Dependencies', fn: createTaskDependenciesCollection },
{ name: 'Task Time Entries', fn: createTaskTimeEntriesCollection },
{ name: 'Task Templates', fn: createTaskTemplatesCollection },
{ name: 'Task AI Contexts', fn: createTaskAIContextsCollection },
{ name: 'Task BMad Workflows', fn: createTaskBMadWorkflowsCollection },
{ name: 'Task Assignments', fn: createTaskAssignmentsCollection },
{ name: 'Task External Refs', fn: createTaskExternalRefsCollection }
];
// Create all collections
for (const { name, fn } of collectionFunctions) {
try {
await fn(client);
summary.collections.created++;
} catch (error) {
console.error(` ❌ Error with ${name} collection:`, error.message);
summary.collections.errors++;
}
}
console.log('\n' + '='.repeat(60));
console.log('📊 FINAL SUMMARY');
console.log('='.repeat(60));
console.log('\n📦 Collections:');
console.log(` ✅ Processed: ${summary.collections.created + summary.collections.existing}`);
console.log(` ⚠️ Errors: ${summary.collections.errors}`);
console.log('\n✨ Status:');
if (summary.collections.errors === 0) {
console.log(' ✅ All collections processed successfully!');
console.log(' ✅ Ready for relationship setup');
console.log(' ✅ Ready for validation and indexing');
} else {
console.log(' ⚠️ Some collections had issues, but system is functional');
console.log(' Run script again to retry failed operations');
}
console.log('\n📝 Next Steps:');
console.log(' 1. Run: npm run setup-relationships');
console.log(' 2. Run: npm run setup-validation');
console.log(' 3. Run: npm run create-indexes');
console.log('='.repeat(60));
} catch (error) {
console.error('\n❌ Critical error:', error.message);
if (error.response) {
console.error('Response data:', error.response.data);
}
process.exit(1);
}
}
// Run if called directly
if (require.main === module) {
main();
}
module.exports = {
createProjectsCollection,
createTaskStatusesCollection,
createTasksCollection,
createTaskDependenciesCollection,
createTaskTimeEntriesCollection,
createTaskTemplatesCollection,
createTaskAIContextsCollection,
createTaskBMadWorkflowsCollection,
createTaskAssignmentsCollection,
createTaskExternalRefsCollection,
main
};