@swoft/gtd-domain
Version:
Getting Things Done (GTD) productivity system - consolidated domain implementation
1,510 lines (1,490 loc) • 240 kB
JavaScript
"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