UNPKG

@memory-bank/mcp

Version:

Memory-enabled Co-Pilot (MCP) server for managing project documentation and context

158 lines 5.79 kB
import { DomainError, DomainErrorCodes } from '../../shared/errors/DomainError.js'; /** * Value object representing a document path */ export class DocumentPath { _value; constructor(_value) { this._value = _value; } /** * Factory method to create a new DocumentPath * @param value Raw path string * @returns DocumentPath instance * @throws DomainError if path is invalid */ static create(value) { if (!value) { throw new DomainError(DomainErrorCodes.INVALID_DOCUMENT_PATH, 'Document path cannot be empty'); } // ★★★ 不正なパス区切り文字チェックを追加 ★★★ if (value.includes('\\')) { throw new DomainError(DomainErrorCodes.INVALID_DOCUMENT_PATH, 'Document path cannot contain backslashes (\\). Use forward slashes (/) instead.'); } const normalizedPath = value; // replace は不要になる if (normalizedPath.includes('..')) { throw new DomainError(DomainErrorCodes.INVALID_DOCUMENT_PATH, 'Document path cannot contain ".."'); } // ★★★ 無効な文字チェックを追加 ★★★ const invalidChars = /[<>:"|?*]/; // : はWindowsドライブレターと区別が必要だが、絶対パスは既に弾いている if (invalidChars.test(normalizedPath)) { throw new DomainError(DomainErrorCodes.INVALID_DOCUMENT_PATH, 'Document path contains invalid characters (<, >, :, ", |, ?, *)'); } if (normalizedPath.startsWith('/') || /^[a-zA-Z]:/.test(normalizedPath)) { throw new DomainError(DomainErrorCodes.INVALID_DOCUMENT_PATH, 'Document path cannot be absolute'); } // ★★★ 末尾スラッシュのチェックを追加 ★★★ if (normalizedPath.endsWith('/')) { throw new DomainError(DomainErrorCodes.INVALID_DOCUMENT_PATH, 'Document path cannot end with a slash'); } return new DocumentPath(normalizedPath); } /** * Get the raw path value */ get value() { return this._value; } /** * Get the directory part of the path */ get directory() { const lastSlashIndex = this._value.lastIndexOf('/'); return lastSlashIndex === -1 ? '' : this._value.substring(0, lastSlashIndex); } /** * Get the filename part of the path */ get filename() { const lastSlashIndex = this._value.lastIndexOf('/'); return lastSlashIndex === -1 ? this._value : this._value.substring(lastSlashIndex + 1); } /** * Get the extension part of the path */ get extension() { const filename = this.filename; const lastDotIndex = filename.lastIndexOf('.'); return lastDotIndex === -1 ? '' : filename.substring(lastDotIndex + 1); } /** * Check if this document is a JSON file */ get isJSON() { return this.extension.toLowerCase() === 'json'; } /** * Checks if two DocumentPath instances are equal * @param other Other DocumentPath instance to compare with * @returns true if both paths have the same value */ equals(other) { if (!other) { // Check if other is null or undefined return false; } return this._value === other._value; } /** * Convert to string * @returns Raw path value */ toString() { return this._value; } /** * Get the basename (filename without extension) */ get basename() { const filename = this.filename; const lastDotIndex = filename.lastIndexOf('.'); return lastDotIndex === -1 ? filename : filename.substring(0, lastDotIndex); } inferDocumentType() { const lcBasename = this.basename.toLowerCase(); if (lcBasename.includes('branchcontext') || lcBasename.includes('branch-context')) { return 'branch_context'; } else if (lcBasename.includes('activecontext') || lcBasename.includes('active-context')) { return 'active_context'; } else if (lcBasename.includes('progress')) { return 'progress'; } else if (lcBasename.includes('systempatterns') || lcBasename.includes('system-patterns')) { return 'system_patterns'; } else { return 'generic'; } } /** * Create a new path with different extension * @param newExtension New extension (without dot) * @returns New DocumentPath instance */ withExtension(newExtension) { if (!newExtension) { throw new DomainError(DomainErrorCodes.INVALID_DOCUMENT_PATH, 'Extension cannot be empty'); } const directory = this.directory; const basename = this.basename; const newPath = directory ? `${directory}/${basename}.${newExtension}` : `${basename}.${newExtension}`; return DocumentPath.create(newPath); } /** * Get the path in the alternate format (.md <-> .json conversion) * @returns DocumentPath in the alternate format */ toAlternateFormat() { if (this.extension.toLowerCase() === 'md') { return this.withExtension('json'); } else if (this.extension.toLowerCase() === 'json') { return this.withExtension('md'); } // If not a convertible format, clone the original path return DocumentPath.create(this._value); } /** * Create a clone of the current DocumentPath * @returns A new DocumentPath with the same path value */ clone() { return DocumentPath.create(this._value); } } //# sourceMappingURL=DocumentPath.js.map