UNPKG

@swoft/gtd-domain

Version:

Getting Things Done (GTD) productivity system - consolidated domain implementation

1,510 lines (1,490 loc) 240 kB
"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var index_exports = {}; __export(index_exports, { ActionContext: () => ActionContext, AssignmentError: () => AssignmentError, CompleteGTDProcessingCommandValidator: () => CompleteGTDProcessingCommandValidator, DomainEvent: () => DomainEvent, EnergyLevel: () => EnergyLevel, GTDAIToolsApplicationService: () => GTDAIToolsApplicationService, GTDDomainError: () => GTDDomainError, GTDDomainIntegrationError: () => GTDDomainIntegrationError, GTDFeatureError: () => GTDFeatureError, GTDNavigationHintsBuilder: () => GTDNavigationHintsBuilder, GTDProcessingWorkflowService: () => GTDProcessingWorkflowService, GTDServiceFactory: () => GTDServiceFactory, GTDValidationError: () => GTDValidationError, GTD_COLLECTIONS: () => GTD_COLLECTIONS, GTD_COLLECTION_METADATA: () => GTD_COLLECTION_METADATA, GTD_COLLECTION_MIGRATION_GUIDE: () => GTD_COLLECTION_MIGRATION_GUIDE, GTD_CONTEXTS: () => GTD_CONTEXTS, GTD_DOMAIN_PACKAGE_INFO: () => GTD_DOMAIN_PACKAGE_INFO, GTD_DOMAIN_SEMANTICS: () => GTD_DOMAIN_SEMANTICS, GTD_DOMAIN_SYMBOLS: () => GTD_DOMAIN_SYMBOLS2, GTD_ENERGY_LEVELS: () => GTD_ENERGY_LEVELS, GTD_ENTITY_RELATIONSHIPS: () => GTD_ENTITY_RELATIONSHIPS, GTD_INTEGRATION_INFO: () => GTD_INTEGRATION_INFO, GTD_PUBLISHED_LANGUAGE_VERSION: () => GTD_PUBLISHED_LANGUAGE_VERSION, GTD_WORKFLOWS: () => GTD_WORKFLOWS, GtdDomainSeeder: () => GtdDomainSeeder, InboxCaptureRequestSchema: () => InboxCaptureRequestSchema, InboxCaptureResultSchema: () => InboxCaptureResultSchema, InboxContent: () => InboxContent, InboxImportExportService: () => InboxImportExportService, InboxItem: () => InboxItem, InboxItemAggregateRepository: () => InboxItemAggregateRepository, InboxItemApplicationService: () => InboxItemApplicationService, InboxItemSummarySchema: () => InboxItemSummarySchema, InboxQueryCriteriaSchema: () => InboxQueryCriteriaSchema, InvalidActionDurationError: () => InvalidActionDurationError, InvalidProcessingStatusError: () => InvalidProcessingStatusError, NextAction: () => NextAction, NextActionAggregateRepository: () => NextActionAggregateRepository, NextActionCreated: () => NextActionCreated, NextActionReadService: () => NextActionReadService, NextActionWriteService: () => NextActionWriteService, ProcessingStatus: () => ProcessingStatus, Project: () => Project, ProjectApplicationService: () => ProjectApplicationService, ProjectCompleted: () => ProjectCompleted, ProjectIdentified: () => ProjectIdentified, ProjectReadService: () => ProjectReadService, ProjectRepository: () => ProjectRepository, ProjectStateError: () => ProjectStateError, ProjectStatus: () => ProjectStatus, ProjectWriteService: () => ProjectWriteService, TYPES: () => TYPES, ThoughtProcessingError: () => ThoughtProcessingError, WeeklyReviewApplicationService: () => WeeklyReviewApplicationService, createGTDAIToolsService: () => createGTDAIToolsService, createGTDDomainModule: () => createGTDDomainModule, createGTDDomainRules: () => createGTDDomainRules, createGTDNavigationBuilder: () => createGTDNavigationBuilder, createGTDQueryService: () => createGTDQueryService, createGTDSemanticHints: () => createGTDSemanticHints, createGtdDomainSeeder: () => createGtdDomainSeeder, createInboxTools: () => createInboxTools, getGTDCollection: () => getGTDCollection, gtdDomainApi: () => gtdDomainApi, publishedLanguage: () => publishedLanguage }); module.exports = __toCommonJS(index_exports); // src/bounded-contexts/project-management/domain/errors/GTDDomainError.ts var GTDDomainError = class extends Error { static { __name(this, "GTDDomainError"); } code; context; constructor(message, code, context) { super(message), this.code = code, this.context = context; this.name = "GTDDomainError"; } }; var ThoughtProcessingError = class extends GTDDomainError { static { __name(this, "ThoughtProcessingError"); } constructor(message, context) { super(message, "THOUGHT_PROCESSING_ERROR", context); this.name = "ThoughtProcessingError"; } }; var InvalidProcessingStatusError = class extends GTDDomainError { static { __name(this, "InvalidProcessingStatusError"); } constructor(currentStatus, requiredStatus) { super(`Cannot perform this operation. Current status: ${currentStatus}, required: ${requiredStatus}`, "INVALID_PROCESSING_STATUS", { currentStatus, requiredStatus }); this.name = "InvalidProcessingStatusError"; } }; var InvalidActionDurationError = class extends GTDDomainError { static { __name(this, "InvalidActionDurationError"); } constructor(duration) { super(`Action duration exceeds reasonable bounds. Duration: ${duration} minutes. Maximum allowed: 480 minutes (8 hours).`, "INVALID_ACTION_DURATION", { duration, maxDuration: 480 }); this.name = "InvalidActionDurationError"; } }; var ProjectStateError = class extends GTDDomainError { static { __name(this, "ProjectStateError"); } constructor(message, projectId, currentState) { super(message, "PROJECT_STATE_ERROR", { projectId, currentState }); this.name = "ProjectStateError"; } }; var AssignmentError = class extends GTDDomainError { static { __name(this, "AssignmentError"); } constructor(message, actionId) { super(message, "ASSIGNMENT_ERROR", { actionId }); this.name = "AssignmentError"; } }; // src/bounded-contexts/inbox-management/domain/value-objects/InboxContent.ts var InboxContent = class _InboxContent { static { __name(this, "InboxContent"); } _value; constructor(_value) { this._value = _value; if (!_value || _value.trim().length === 0) { throw new GTDDomainError("Inbox content cannot be empty", "EMPTY_INBOX_CONTENT"); } if (_value.trim().length < 3) { throw new GTDDomainError("Inbox content must be at least 3 characters", "INBOX_CONTENT_TOO_SHORT", { minLength: 3, actualLength: _value.trim().length }); } if (_value.length > 5e3) { throw new GTDDomainError("Inbox content cannot exceed 5000 characters", "INBOX_CONTENT_TOO_LONG", { maxLength: 5e3, actualLength: _value.length }); } } static create(content) { return new _InboxContent(content?.trim()); } get value() { return this._value; } }; // src/bounded-contexts/inbox-management/domain/aggregates/InboxItem.ts var InboxItem = class _InboxItem { static { __name(this, "InboxItem"); } id; _originalContent; capturedAt; capturedByPersonId; _clarification; _isActionable; _processingStatus; _lastRefinedAt; _refinedByPersonId; constructor(id, originalContent, capturedAt, capturedByPersonId) { this.id = id; this._originalContent = originalContent; this.capturedAt = capturedAt; this.capturedByPersonId = capturedByPersonId; this._processingStatus = "unprocessed"; } // Getters get originalContent() { return this._originalContent.value; } get clarification() { return this._clarification; } get isActionable() { return this._isActionable; } get processingStatus() { return this._processingStatus; } get lastRefinedAt() { return this._lastRefinedAt; } get refinedByPersonId() { return this._refinedByPersonId; } /** * Factory method: Capture a new inbox item * GTD Principle: "Ubiquitous Capture" */ static capture(content, capturedByPersonId) { const id = `inbox-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; const capturedAt = /* @__PURE__ */ new Date(); const inboxContent = InboxContent.create(content); return new _InboxItem(id, inboxContent, capturedAt, capturedByPersonId); } /** * Factory method: Reconstruct from repository data */ static fromRepository(data) { const inboxContent = InboxContent.create(data.originalContent); const item = new _InboxItem(data.id, inboxContent, data.capturedAt, data.capturedByPersonId); item._clarification = data.clarification; item._isActionable = data.isActionable; item._processingStatus = data.processingStatus || "unprocessed"; item._lastRefinedAt = data.lastRefinedAt; item._refinedByPersonId = data.refinedByPersonId; return item; } /** * Refine Capture: Clean up or correct the original captured content * GTD Principle: "Your system must be current and complete" * * This is NOT clarification - it's fixing typos, making the capture clearer, * or adding missing context to what was hastily captured. * * @param refinedContent The cleaned up version of the original capture * @param refinedBy Person ID who refined the content */ refineCapture(refinedContent, refinedBy) { const newContent = InboxContent.create(refinedContent); this._originalContent = newContent; this._lastRefinedAt = /* @__PURE__ */ new Date(); this._refinedByPersonId = refinedBy; } /** * GTD Clarification: "What is it? Is it actionable?" * * This is the core GTD processing step where you decide what something means * and whether it requires action. */ clarify(clarification, isActionable, _clarifiedBy) { this._clarification = clarification; this._isActionable = isActionable; this._processingStatus = "processed"; } /** * Convert to plain object for repository persistence */ toData() { return { id: this.id, originalContent: this._originalContent.value, capturedAt: this.capturedAt, capturedByPersonId: this.capturedByPersonId, clarification: this._clarification, isActionable: this._isActionable, processingStatus: this._processingStatus, lastRefinedAt: this._lastRefinedAt, refinedByPersonId: this._refinedByPersonId }; } }; // src/bounded-contexts/inbox-management/domain/services/InboxImportExportService.ts var InboxImportExportService = class { static { __name(this, "InboxImportExportService"); } repository; constructor(repository) { this.repository = repository; } /** * Export all inbox items to a portable format */ async exportItems(filters) { const items = await this.repository.findAll(); const filteredItems = filters?.status ? items.filter((item) => item.processingStatus === filters.status) : items; const limitedItems = filters?.limit ? filteredItems.slice(0, filters.limit) : filteredItems; return { exportDate: (/* @__PURE__ */ new Date()).toISOString(), version: "1.0", source: "swoft-gtd-inbox", totalItems: limitedItems.length, items: limitedItems.map((item) => ({ originalContent: item.originalContent, capturedAt: item.capturedAt.toISOString(), capturedByPersonId: item.capturedByPersonId, clarification: item.clarification || null, isActionable: item.isActionable || null, processingStatus: item.processingStatus })) }; } /** * Import items from external format * Supports migration from legacy formats */ async importItems(importData, options = {}) { this.validateImportData(importData); const results = { totalItems: importData.items.length, imported: 0, skipped: 0, errors: [] }; for (const itemData of importData.items) { try { const content = itemData.originalContent || itemData.text || itemData.content; if (!content) { results.errors.push(`Item missing content: ${JSON.stringify(itemData)}`); continue; } const item = InboxItem.capture(content, options.capturedByPersonId || itemData.capturedByPersonId || "import-system"); if (itemData.clarification && typeof itemData.isActionable === "boolean") { item.clarify(itemData.clarification, itemData.isActionable, "import-system"); } await this.repository.save(item); results.imported++; } catch (error) { if (options.skipDuplicates && error instanceof Error && error.message.includes("duplicate")) { results.skipped++; } else { results.errors.push(`Failed to import item: ${error instanceof Error ? error.message : "Unknown error"}`); } } } return results; } validateImportData(data) { if (!data.version) { throw new Error("Import data missing version"); } if (!data.items || !Array.isArray(data.items)) { throw new Error("Import data missing or invalid items array"); } if (data.items.length === 0) { throw new Error("Import data contains no items"); } } }; // src/bounded-contexts/project-management/domain/events/GTDEvents.ts var ProjectIdentified = class { static { __name(this, "ProjectIdentified"); } projectName; desiredOutcome; identifiedBy; eventId = crypto.randomUUID(); aggregateId; eventType = "gtd.ProjectIdentified"; occurredAt = /* @__PURE__ */ new Date(); eventVersion = 1; occurredOn = /* @__PURE__ */ new Date(); constructor(aggregateId, projectName, desiredOutcome, identifiedBy) { this.projectName = projectName; this.desiredOutcome = desiredOutcome; this.identifiedBy = identifiedBy; this.aggregateId = aggregateId; } getEventData() { return { projectName: this.projectName, desiredOutcome: this.desiredOutcome, identifiedBy: this.identifiedBy, identifiedAt: this.occurredOn.toISOString() }; } }; var NextActionCreated = class { static { __name(this, "NextActionCreated"); } description; context; energyLevel; createdBy; eventId = crypto.randomUUID(); aggregateId; eventType = "gtd.NextActionCreated"; occurredAt = /* @__PURE__ */ new Date(); eventVersion = 1; occurredOn = /* @__PURE__ */ new Date(); constructor(aggregateId, description, context, energyLevel, createdBy) { this.description = description; this.context = context; this.energyLevel = energyLevel; this.createdBy = createdBy; this.aggregateId = aggregateId; } getEventData() { return { description: this.description, context: this.context, energyLevel: this.energyLevel, createdBy: this.createdBy, createdAt: this.occurredOn.toISOString() }; } }; var TaskAssigned = class { static { __name(this, "TaskAssigned"); } assignedTo; roleType; assignedAt; eventId = crypto.randomUUID(); aggregateId; eventType = "gtd.TaskAssigned"; occurredAt = /* @__PURE__ */ new Date(); eventVersion = 1; occurredOn = /* @__PURE__ */ new Date(); constructor(aggregateId, assignedTo, roleType, assignedAt) { this.assignedTo = assignedTo; this.roleType = roleType; this.assignedAt = assignedAt; this.aggregateId = aggregateId; } getEventData() { return { assignedTo: this.assignedTo, roleType: this.roleType, assignedAt: this.assignedAt }; } }; var ProjectCompleted = class { static { __name(this, "ProjectCompleted"); } completedBy; completionNotes; eventId = crypto.randomUUID(); aggregateId; eventType = "gtd.ProjectCompleted"; occurredAt = /* @__PURE__ */ new Date(); eventVersion = 1; occurredOn = /* @__PURE__ */ new Date(); constructor(aggregateId, completedBy, completionNotes) { this.completedBy = completedBy; this.completionNotes = completionNotes; this.aggregateId = aggregateId; } getEventData() { return { completedBy: this.completedBy, completionNotes: this.completionNotes, completedAt: this.occurredOn.toISOString() }; } }; // src/bounded-contexts/project-management/domain/aggregates/NextAction.ts var NextAction = class _NextAction { static { __name(this, "NextAction"); } id; description; context; energyRequired; estimatedMinutes; createdBy; createdAt; projectId; domainEvents = []; _completedAt; _assignedTo; _assignedAt; _roleType; constructor(id, description, context, energyRequired, estimatedMinutes, createdBy, createdAt = /* @__PURE__ */ new Date(), projectId) { this.id = id; this.description = description; this.context = context; this.energyRequired = energyRequired; this.estimatedMinutes = estimatedMinutes; this.createdBy = createdBy; this.createdAt = createdAt; this.projectId = projectId; this.validateAction(description, estimatedMinutes); this.addDomainEvent(new NextActionCreated(id, description, context.toString(), energyRequired.toString(), createdBy)); } /** * Create a new next action with validation */ static create(description, context, energy, minutes, createdBy, projectId) { const id = this.generateId(); return new _NextAction(id, description, context, energy, minutes, createdBy, /* @__PURE__ */ new Date(), projectId); } /** * Reconstitute from persistence */ static reconstitute(id, description, context, energy, minutes, createdBy, createdAt, projectId, assignedTo, assignedAt, roleType, completedAt) { const action = new _NextAction(id, description, context, energy, minutes, createdBy, createdAt, projectId); action.clearDomainEvents(); if (assignedTo && assignedAt && roleType) { action._assignedTo = assignedTo; action._assignedAt = assignedAt; action._roleType = roleType; } if (completedAt) { action._completedAt = completedAt; } return action; } /** * Assign this action to a developer/team member * Integrates with Party Management domain */ assignTo(partyId, roleType) { if (this.isCompleted()) { throw new AssignmentError("Cannot assign completed actions", this.id); } if (this.isAssigned()) { throw new AssignmentError(`Action is already assigned to ${this._assignedTo}`, this.id); } if (!partyId?.trim()) { throw new AssignmentError("Party ID is required for assignment", this.id); } if (!roleType?.trim()) { throw new AssignmentError("Role type is required for assignment", this.id); } this._assignedTo = partyId; this._roleType = roleType; this._assignedAt = /* @__PURE__ */ new Date(); this.addDomainEvent(new TaskAssigned(this.id, partyId, roleType, this._assignedAt.toISOString())); } /** * Unassign the action (return to available pool) */ unassign() { if (!this.isAssigned()) { throw new AssignmentError("Action is not currently assigned", this.id); } if (this.isCompleted()) { throw new AssignmentError("Cannot unassign completed actions", this.id); } this._assignedTo = void 0; this._roleType = void 0; this._assignedAt = void 0; } /** * Mark action as completed */ complete() { if (this.isCompleted()) { throw new ThoughtProcessingError("Action is already completed"); } this._completedAt = /* @__PURE__ */ new Date(); } /** * Check if action can be performed given current context and energy * Core GTD principle: Match actions to available resources */ canBePerformedWith(availableContext, availableEnergy) { if (this.isCompleted()) { return false; } const contextMatch = this.context.equals(availableContext); const energyMatch = this.energyRequired.canBePerformedWhen(availableEnergy); return contextMatch && energyMatch; } /** * Get available actions that match criteria */ static getAvailableActions(actions, context, energy) { return actions.filter((action) => !action.isCompleted() && !action.isAssigned() && action.canBePerformedWith(context, energy)); } // State queries isCompleted() { return this._completedAt !== void 0; } isAssigned() { return this._assignedTo !== void 0; } isAvailable() { return !this.isCompleted() && !this.isAssigned(); } // Getters get assignedTo() { return this._assignedTo; } get roleType() { return this._roleType; } get assignedAt() { return this._assignedAt; } get completedAt() { return this._completedAt; } get status() { if (this.isCompleted()) return "completed"; if (this.isAssigned()) return "assigned"; return "available"; } // Event sourcing support getDomainEvents() { return [ ...this.domainEvents ]; } clearDomainEvents() { this.domainEvents = []; } // Private methods validateAction(description, estimatedMinutes) { if (!description?.trim()) { throw new ThoughtProcessingError("Action description is required"); } if (estimatedMinutes <= 0) { throw new ThoughtProcessingError("Estimated minutes must be positive"); } } addDomainEvent(event) { this.domainEvents.push(event); } static generateId() { return `action-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`; } }; // src/bounded-contexts/project-management/domain/value-objects/ActionContext.ts var ActionContext = class _ActionContext { static { __name(this, "ActionContext"); } context; toolsRequired; location; constructor(context, toolsRequired = [], location) { this.context = context; this.toolsRequired = toolsRequired; this.location = location; } static atComputer(tools = []) { return new _ActionContext("computer", tools); } static onPhone(tools = []) { return new _ActionContext("phone", tools); } static atCalls(tools = []) { return new _ActionContext("@calls", tools); } static atErrands(location = "", tools = []) { return new _ActionContext("@errands", tools, location); } static anywhere(tools = []) { return new _ActionContext("@anywhere", tools); } static atOffice(tools = []) { return new _ActionContext("office", tools, "office"); } static atHome(tools = []) { return new _ActionContext("home", tools, "home"); } static errands(location, tools = []) { return new _ActionContext("errands", tools, location); } static agendaFor(person) { return new _ActionContext("agenda", [], person); } static custom(context, tools = [], location) { return new _ActionContext(context, tools, location); } equals(other) { return this.context === other.context && this.location === other.location && JSON.stringify(this.toolsRequired) === JSON.stringify(other.toolsRequired); } toString() { if (this.context.startsWith("@")) { return this.context; } const base = `@${this.context}`; if (this.location) { return `${base} (${this.location})`; } return base; } }; // src/bounded-contexts/project-management/domain/value-objects/EnergyLevel.ts var EnergyLevel = class _EnergyLevel { static { __name(this, "EnergyLevel"); } level; constructor(level) { this.level = level; } static low() { return new _EnergyLevel("low"); } static medium() { return new _EnergyLevel("medium"); } static high() { return new _EnergyLevel("high"); } isLow() { return this.level === "low"; } isMedium() { return this.level === "medium"; } isHigh() { return this.level === "high"; } /** * Can this action be performed with the available energy level? * GTD principle: Match actions to available energy */ canBePerformedWhen(available) { const levels = { low: 1, medium: 2, high: 3 }; return levels[this.level] <= levels[available.level]; } equals(other) { return this.level === other.level; } toString() { return this.level; } valueOf() { return this.level; } }; // src/bounded-contexts/inbox-management/domain/services/GTDProcessingWorkflowService.ts var GTDProcessingWorkflowService = class { static { __name(this, "GTDProcessingWorkflowService"); } /** * Process a clarified inbox item according to GTD methodology * * @param inboxItem - The clarified inbox item * @param processingDecision - How the item should be processed * @returns The created work items (NextActions, Projects, etc.) */ processInboxItem(inboxItem, processingDecision) { if (!inboxItem.clarification) { throw new Error("Inbox item must be clarified before processing"); } if (inboxItem.processingStatus === "unprocessed") { throw new Error("Inbox item must be marked as processed"); } if (inboxItem.isActionable === true) { return this.handleActionableItem(inboxItem, processingDecision); } else { return this.handleNonActionableItem(inboxItem, processingDecision); } } /** * Handle actionable items (create NextActions or Projects) */ handleActionableItem(inboxItem, decision) { const result = { success: true, workflowType: "actionable", createdItems: [] }; if (decision.estimatedMinutes && decision.estimatedMinutes <= 2) { const nextAction = this.createNextAction(inboxItem, decision); result.createdItems.push({ type: "next_action", id: nextAction.id, description: nextAction.description, urgent: true, reason: "Two-minute rule: Do it now" }); } else if (decision.isProject) { result.workflowType = "project"; result.createdItems.push({ type: "project", id: `project-${Date.now()}`, description: decision.projectOutcome || inboxItem.clarification || "New project", reason: "Multi-step outcome requires project planning" }); const firstAction = this.createNextAction(inboxItem, decision); result.createdItems.push({ type: "next_action", id: firstAction.id, description: firstAction.description, urgent: false, reason: "First action for project" }); } else { const nextAction = this.createNextAction(inboxItem, decision); result.createdItems.push({ type: "next_action", id: nextAction.id, description: nextAction.description, urgent: false, reason: "Single actionable item" }); } return result; } /** * Handle non-actionable items (reference, someday/maybe, trash) */ handleNonActionableItem(inboxItem, decision) { const result = { success: true, workflowType: "non_actionable", createdItems: [] }; if (decision.isReference) { result.createdItems.push({ type: "reference_material", id: `ref-${Date.now()}`, description: inboxItem.clarification || "Reference material", reason: "Useful information for future reference" }); } else if (decision.isSomedayMaybe) { result.createdItems.push({ type: "someday_maybe", id: `someday-${Date.now()}`, description: inboxItem.clarification || "Someday/Maybe item", reason: "Potentially actionable in the future" }); } else { result.workflowType = "trash"; result.reason = "Not actionable and not worth keeping"; } return result; } /** * Create a NextAction from an inbox item */ createNextAction(inboxItem, decision) { const context = decision.context ? this.parseActionContext(decision.context) : ActionContext.atComputer(); const energyLevel = decision.energyLevel ? this.parseEnergyLevel(decision.energyLevel) : EnergyLevel.medium(); const estimatedMinutes = decision.estimatedMinutes || 1; const actionDescription = decision.nextActionDescription || this.generateActionDescription(inboxItem.clarification || ""); return NextAction.create(actionDescription, context, energyLevel, estimatedMinutes, inboxItem.capturedByPersonId, decision.projectId); } /** * Parse context string into ActionContext object */ parseActionContext(contextString) { const context = contextString.toLowerCase().replace("@", ""); switch (context) { case "computer": return ActionContext.atComputer(); case "phone": case "calls": return ActionContext.onPhone(); case "office": return ActionContext.atOffice(); case "home": return ActionContext.atHome(); case "errands": return ActionContext.errands("general"); default: return ActionContext.custom(context); } } /** * Parse energy level string into EnergyLevel object */ parseEnergyLevel(energyString) { const level = energyString.toLowerCase(); switch (level) { case "high": return EnergyLevel.high(); case "low": return EnergyLevel.low(); case "medium": default: return EnergyLevel.medium(); } } /** * Generate a proper action description from clarification * GTD Principle: Actions must be specific and physical */ generateActionDescription(clarification) { const actionVerbs = [ "Call", "Email", "Write", "Research", "Review", "Schedule", "Update" ]; const hasActionVerb = actionVerbs.some((verb) => clarification.toLowerCase().startsWith(verb.toLowerCase())); if (hasActionVerb) { return clarification; } return `Research: ${clarification}`; } }; // src/bounded-contexts/inbox-management/application/commands/CompleteGTDProcessingCommand.ts var CompleteGTDProcessingCommandValidator = class { static { __name(this, "CompleteGTDProcessingCommandValidator"); } static validate(command) { const errors = []; if (!command.itemId?.trim()) { errors.push("Item ID is required"); } if (!command.processedByPersonId?.trim()) { errors.push("Processed by person ID is required"); } if (!command.clarification?.trim()) { errors.push("Clarification is required for GTD processing"); } if (command.isActionable === void 0 || command.isActionable === null) { errors.push("Actionable decision is required (true/false)"); } if (command.isActionable) { if (command.estimatedMinutes && command.estimatedMinutes > 120) { errors.push("Actions over 2 hours should be broken into smaller steps"); } if (command.isProject && !command.projectOutcome?.trim()) { errors.push("Project outcome is required for multi-step items"); } } else { const hasDisposalMethod = command.isReference || command.isSomedayMaybe; if (!hasDisposalMethod) { errors.push("Non-actionable items must specify reference or someday/maybe"); } } if (command.context) { const validContexts = [ "@calls", "@computer", "@errands", "@home", "@office", "@anywhere" ]; const isValidContext = validContexts.includes(command.context.toLowerCase()) || command.context.startsWith("@"); if (!isValidContext) { errors.push("Context must start with @ (e.g., @calls, @computer)"); } } if (command.energyLevel) { const validEnergyLevels = [ "high", "medium", "low" ]; if (!validEnergyLevels.includes(command.energyLevel.toLowerCase())) { errors.push("Energy level must be high, medium, or low"); } } return { isValid: errors.length === 0, errors }; } }; var GTD_CONTEXTS = { CALLS: "@calls", COMPUTER: "@computer", ERRANDS: "@errands", HOME: "@home", OFFICE: "@office", ANYWHERE: "@anywhere", WAITING: "@waiting_for", READ_REVIEW: "@read_review" }; var GTD_ENERGY_LEVELS = { HIGH: "high", MEDIUM: "medium", LOW: "low" // Mindless tasks, filing, organizing }; // src/bounded-contexts/inbox-management/application/services/InboxItemApplicationService.ts var import_inversify3 = require("inversify"); // src/infrastructure/di/GTDDomainSymbols.ts var GTD_DOMAIN_SYMBOLS = { // Inbox Management Bounded Context InboxItemApplicationService: "InboxItemApplicationService", InboxItemAggregateRepository: "InboxItemAggregateRepository", GTDQueryService: "GTDQueryService", PersonLookupService: "PersonLookupService", GTDProcessingWorkflowService: "GTDProcessingWorkflowService", InboxImportExportService: "InboxImportExportService", // Project Management Bounded Context ProjectApplicationService: "GTDProjectApplicationService", ProjectReadService: "ProjectReadService", ProjectRepository: "ProjectRepository", NextActionAggregateRepository: "NextActionAggregateRepository", NextActionWriteService: "NextActionWriteService", NextActionReadService: "NextActionReadService", // Task Management Bounded Context ITaskRepository: "ITaskRepository", TaskMongoRepository: "TaskMongoRepository", GetReferenceItemService: "GetReferenceItemService", InboxFileRepository: "InboxFileRepository", NextActionsFileRepository: "NextActionsFileRepository", // Cross-cutting Services GtdDomainSeeder: "GtdDomainSeeder" }; // src/bounded-contexts/inbox-management/infrastructure/repositories/InboxItemAggregateRepository.ts var import_inversify = require("inversify"); var import_persistence2 = require("@swoft/persistence"); // src/config/collections.ts var import_persistence = require("@swoft/persistence"); var GTD_COLLECTIONS = { // Inbox Management Bounded Context INBOX_ITEMS: "gtd_inbox_items", // Project Management Bounded Context PROJECTS: "gtd_projects", NEXT_ACTIONS: "gtd_next_actions", // Task Management Bounded Context TASK_ASSIGNMENTS: "gtd_task_assignments", // Design Implementation Coordination Bounded Context DESIGN_IMPLEMENTATION_FLOWS: "gtd_design_implementation_flows", // Reference Management Bounded Context REFERENCE_ITEMS: "gtd_reference_items", AGENT_API_KEYS: "gtd_agent_api_keys", // Someday/Maybe List SOMEDAY_MAYBE: "gtd_someday_maybe" }; function getGTDCollection(collectionName) { const db = (0, import_persistence.getMongoDb)(); return db.collection(collectionName); } __name(getGTDCollection, "getGTDCollection"); var GTD_COLLECTION_METADATA = { [GTD_COLLECTIONS.INBOX_ITEMS]: { boundedContext: "inbox-management", description: "Captured thoughts, ideas, and information awaiting processing (GTD Capture phase)", primaryKey: "itemId", gtdPhase: "capture", aggregateRoot: "InboxItem" }, [GTD_COLLECTIONS.PROJECTS]: { boundedContext: "project-management", description: "Multi-step outcomes and projects with desired results (GTD Organize phase)", primaryKey: "projectId", gtdPhase: "organize", aggregateRoot: "Project" }, [GTD_COLLECTIONS.NEXT_ACTIONS]: { boundedContext: "project-management", description: "Physical next actions with context and energy requirements (GTD Organize/Engage phase)", primaryKey: "actionId", gtdPhase: "engage", aggregateRoot: "NextAction" }, [GTD_COLLECTIONS.TASK_ASSIGNMENTS]: { boundedContext: "task-management", description: "Task assignments and delegation tracking with status and metadata", primaryKey: "assignmentId", gtdPhase: "engage", aggregateRoot: "TaskAssignment" }, [GTD_COLLECTIONS.DESIGN_IMPLEMENTATION_FLOWS]: { boundedContext: "design-implementation-coordination", description: "Design-to-implementation coordination flows with artifact tracking and conformance validation", primaryKey: "flowId", gtdPhase: "organize", aggregateRoot: "DesignImplementationFlow" }, [GTD_COLLECTIONS.REFERENCE_ITEMS]: { boundedContext: "reference-management", description: "Reference materials and information for future use (GTD Reference system)", primaryKey: "referenceId", gtdPhase: "organize", aggregateRoot: "ReferenceItem" }, [GTD_COLLECTIONS.AGENT_API_KEYS]: { boundedContext: "reference-management", description: "API keys and agent configurations for external integrations", primaryKey: "keyId", gtdPhase: "organize", aggregateRoot: "AgentApiKey" } }; var GTD_DOMAIN_PACKAGE_INFO = { name: "@swoft/gtd-domain", version: "0.1.0", description: "Getting Things Done (GTD) methodology implementation with Domain-Driven Design", architecture: "Clean Architecture + Domain-Driven Design", methodology: "David Allen GTD (Getting Things Done)", boundedContexts: [ "Inbox Management", "Project Management", "Task Management", "Reference Management" ], gtdPhases: [ "Capture", "Clarify", "Organize", "Reflect", "Engage" ], collections: Object.values(GTD_COLLECTIONS), databaseDependency: "@swoft/mongo" }; var GTD_COLLECTION_MIGRATION_GUIDE = { migrations: [ { from: "gtd_inbox", to: GTD_COLLECTIONS.INBOX_ITEMS, reason: "Resolve mismatch between InboxItemAggregateRepository and GTDSystemController", affectedFiles: [ "InboxItemAggregateRepository.ts", "GTDSharedQueryRepository.ts" ] }, { from: "swoft_builder__task_assignments", to: GTD_COLLECTIONS.TASK_ASSIGNMENTS, reason: "Consistent GTD naming convention", affectedFiles: [ "TaskMongoRepository.ts" ] }, { from: "gtd_reference_store__reference_items", to: GTD_COLLECTIONS.REFERENCE_ITEMS, reason: "Simplified naming without nested prefixes", affectedFiles: [ "collectionNames.ts" ] }, { from: "gtd_reference_store__agent_api_keys", to: GTD_COLLECTIONS.AGENT_API_KEYS, reason: "Simplified naming without nested prefixes", affectedFiles: [ "collectionNames.ts" ] } ] }; // src/bounded-contexts/inbox-management/infrastructure/repositories/InboxItemAggregateRepository.ts function _ts_decorate(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } __name(_ts_decorate, "_ts_decorate"); function _ts_metadata(k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); } __name(_ts_metadata, "_ts_metadata"); var InboxItemAggregateRepository = class { static { __name(this, "InboxItemAggregateRepository"); } db = null; collection = null; constructor() { } ensureConnection() { if (!this.db) { this.db = (0, import_persistence2.getMongoDb)(); this.collection = this.db.collection(GTD_COLLECTIONS.INBOX_ITEMS); } return this.collection; } async save(item) { const collection = this.ensureConnection(); const itemData = item.toData(); const doc = { _id: item.id, originalContent: itemData.originalContent, capturedAt: item.capturedAt, capturedByPersonId: itemData.capturedByPersonId, clarification: itemData.clarification, isActionable: itemData.isActionable, processingStatus: itemData.processingStatus, lastRefinedAt: itemData.lastRefinedAt, refinedByPersonId: itemData.refinedByPersonId, updatedAt: /* @__PURE__ */ new Date() }; await collection.replaceOne({ _id: item.id }, doc, { upsert: true }); } async findById(id) { const collection = this.ensureConnection(); const doc = await collection.findOne({ _id: id }); if (!doc) return null; return this.mapDocumentToDomain(doc); } mapDocumentToDomain(doc) { return InboxItem.fromRepository({ id: doc._id, originalContent: doc.originalContent || "", capturedAt: new Date(doc.capturedAt), capturedByPersonId: doc.capturedByPersonId, clarification: doc.clarification, isActionable: doc.isActionable, processingStatus: doc.processingStatus || "unprocessed", lastRefinedAt: doc.lastRefinedAt ? new Date(doc.lastRefinedAt) : void 0, refinedByPersonId: doc.refinedByPersonId }); } async findAll() { const collection = this.ensureConnection(); const docs = await collection.find({}).toArray(); return docs.map((doc) => this.mapDocumentToDomain(doc)); } async countUnprocessed() { const collection = this.ensureConnection(); return await collection.countDocuments({ processingStatus: "unprocessed" }); } async countAll() { const collection = this.ensureConnection(); return await collection.countDocuments({}); } async delete(id) { const collection = this.ensureConnection(); await collection.deleteOne({ _id: id }); } async findByFilters(filters) { const query = {}; if (filters.status && filters.status !== "all") { query.processingStatus = filters.status === "processed" ? "processed" : "unprocessed"; } const collection = this.ensureConnection(); const docs = await collection.find(query).sort({ capturedAt: -1 }).skip(filters.offset || 0).limit(filters.limit || 20).toArray(); return docs.map((doc) => this.mapDocumentToDomain(doc)); } async countByFilters(filters) { const query = {}; if (filters.status && filters.status !== "all") { query.processingStatus = filters.status === "processed" ? "processed" : "unprocessed"; } const collection = this.ensureConnection(); return await collection.countDocuments(query); } }; InboxItemAggregateRepository = _ts_decorate([ (0, import_inversify.injectable)(), _ts_metadata("design:type", Function), _ts_metadata("design:paramtypes", []) ], InboxItemAggregateRepository); // src/bounded-contexts/project-management/infrastructure/repositories/NextActionAggregateRepository.ts var import_inversify2 = require("inversify"); var import_persistence3 = require("@swoft/persistence"); // src/bounded-contexts/project-management/domain/value-objects/ProcessingStatus.ts var ProcessingStatus = class _ProcessingStatus { static { __name(this, "ProcessingStatus"); } value; constructor(value) { this.value = value; } static new() { return new _ProcessingStatus("new"); } static clarified() { return new _ProcessingStatus("clarified"); } static processed() { return new _ProcessingStatus("processed"); } isNew() { return this.value === "new"; } isClarified() { return this.value === "clarified"; } isProcessed() { return this.value === "processed"; } equals(other) { return this.value === other.value; } toString() { return this.value; } }; // src/bounded-contexts/project-management/infrastructure/repositories/NextActionAggregateRepository.ts function _ts_decorate2(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } __name(_ts_decorate2, "_ts_decorate"); function _ts_metadata2(k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); } __name(_ts_metadata2, "_ts_metadata"); var NextActionAggregateRepository = class { static { __name(this, "NextActionAggregateRepository"); } db = null; collection = null; constructor() { } ensureConnection() { if (!this.db) { this.db = (0, import_persistence3.getMongoDb)(); this.collection = this.db.collection("gtd_next_actions"); } return this.collection; } async save(action) { const collection = this.ensureConnection(); const doc = { _id: action.id, description: action.description, context: action.context.toString(), energyRequired: action.energyRequired.toString(), estimatedMinutes: action.estimatedMinutes, createdBy: action.createdBy, createdAt: action.createdAt, projectId: action.projectId, assignedTo: action.assignedTo, assignedAt: action.assignedAt, completedAt: action.completedAt, status: action.isCompleted() ? "completed" : action.isAssigned() ? "assigned" : "available" }; await collection.replaceOne({ _id: action.id }, doc, { upsert: true }); } async findById(id) { const collection = this.ensureConnection(); const doc = await collection.findOne({ _id: id }); if (!doc) return null; const context = this.parseContext(doc.context); const energy = this.parseEnergyLevel(doc.energyRequired); return NextAction.reconstitute(doc._id.toString(), doc.description, context, energy, doc.estimatedMinutes, doc.createdBy, doc.createdAt, doc.projectId, doc.assignedTo, doc.assignedAt, doc.roleType, doc.completedAt); } async findAll() { const collection = this.ensureConnection(); const docs = await collection.find({}).toArray(); return docs.map((doc) => { const context = this.parseContext(doc.context); const energy = this.parseEnergyLevel(doc.energyRequired); return NextAction.reconstitute(doc._id.toString(), doc.description, context, energy, doc.estimatedMinutes, doc.createdBy, doc.createdAt, doc.projectId, doc.assignedTo, doc.assignedAt, doc.roleType, doc.completedAt); }); } async countAvailable() { const collection = this.ensureConnection(); return await collection.countDocuments({ status: "available" }); } async countAll() { const collection = this.ensureConnection(); return await collection.countDocuments({}); } async delete(id) { const collection = this.ensureConnection(); await collection.deleteOne({ _id: id }); } // Helper methods parseContext(contextStr) { switch (contextStr) { case "@calls": return ActionContext.atCalls(); case "@computer": return ActionContext.atComputer(); case "@errands": return ActionContext.atErrands(""); case "@home": return ActionContext.atHome(); case "@office": return ActionContext.atOffice(); case "@anywhere": return ActionContext.anywhere(); default: return ActionContext.atComputer(); } } parseEnergyLevel(energyStr) { switch (energyStr) { case "high": return EnergyLevel.high(); case "medium": return EnergyLevel.medium(); case "low": return EnergyLevel.low(); default: return EnergyLevel.medium(); } } }; NextActionAggregateRepository = _ts_decorate2([ (0, import_inversify2.injectable)(), _ts_metadata2("design:type", Function), _ts_metadata2("design:paramtypes", []) ], NextActionAggregateRepository); // src/bounded-contexts/inbox-management/application/services/InboxItemApplicationService.ts function _ts_decorate3(decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; } __name(_ts_decorate3, "_ts_decorate"); function _ts_metadata3(k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); } __name(_ts_metadata3, "_ts_metadata"); function _ts_param(paramIndex, decorator) { return function(target, key) { decorator(target, key, paramIndex); }; } __name(_ts_param, "_ts_param"); var InboxItemApplicationService = class { static { __name(this, "InboxItemApplicationService"); } repository; workflowService; constructor(repository, _nextActionRepository) { this.repository = repository; this.workflowService = new GTDProcessingWorkflowService(); } /** * Create a new inbox item - GTD Capture workflow */ async createInboxItem(command) { try { const item = InboxItem.capture(command.originalContent, command.capturedByPersonId); await this.repository.save(item); return { success: true, item