@nmakarov/cli-toolkit 0.1.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mock-server/index.ts","../src/filedatabase/index.ts","../src/utils/os-utils.ts","../src/utils/fs-utils.ts","../src/utils/format-utils.ts","../src/utils/date-utils.ts","../src/filedatabase/serializers.ts","../src/errors.ts","../src/mock-server/catalog.ts","../src/mock-server/sanitization.ts"],"sourcesContent":["/**\n * MockServer - HTTP Mock Server with FileDatabase Integration\n *\n * A complete HTTP mock server that captures and serves API responses using FileDatabase\n * for persistent storage and retrieval. Replaces the legacy MockEngine with modern,\n * type-safe implementation.\n */\n\nimport express from 'express';\nimport http from 'http';\nimport morgan from 'morgan';\nimport { FileDatabase } from '../filedatabase/index.js';\nimport { MockCatalog } from './catalog.js';\nimport type {\n MockServerConfig,\n MockServerInstance,\n MockResponseEntry,\n MockResponseData,\n RequestMatchCriteria,\n ServerStats,\n MaintenanceResult\n} from './types.js';\nimport { sanitizeRequest } from './sanitization.js';\n\n/**\n * Default sensitive keys to mask in requests/responses\n */\nconst DEFAULT_SENSITIVE_KEYS = [\n 'client_id',\n 'client_secret',\n 'access_token',\n 'authorization',\n 'bearer',\n 'api_key',\n 'apikey',\n 'password',\n 'token'\n];\n\n/**\n * HTTP Mock Server with FileDatabase integration\n */\nexport class MockServer {\n private config: Required<MockServerConfig>;\n private app: express.Application;\n private server: http.Server | null = null;\n private fileDb: FileDatabase;\n private catalog: MockCatalog;\n private stats: ServerStats;\n private startTime: number;\n\n constructor(config: MockServerConfig) {\n this.config = {\n port: 5029,\n namespace: 'mocks',\n tableName: 'responses',\n sensitiveKeys: DEFAULT_SENSITIVE_KEYS,\n logger: console,\n debug: false,\n middleware: [],\n ...config\n };\n\n if (!this.config.basePath) {\n throw new Error('MockServer: basePath is required');\n }\n\n this.app = express();\n this.startTime = Date.now();\n this.stats = {\n totalRequests: 0,\n mockResponses: 0,\n fallbackResponses: 0,\n errors: 0,\n uptime: 0\n };\n\n this.fileDb = this.config.fileDb || new FileDatabase({\n basePath: this.config.basePath,\n namespace: this.config.namespace,\n tableName: this.config.tableName,\n versioned: false, // Mock responses are typically not versioned\n logger: this.config.logger\n });\n\n this.catalog = new MockCatalog(this.fileDb, this.config.basePath, this.config.logger);\n\n this.setupMiddleware();\n this.setupRoutes();\n }\n\n /**\n * Start the mock server\n */\n async start(): Promise<MockServerInstance> {\n return new Promise((resolve, reject) => {\n try {\n this.server = http.createServer(this.app);\n\n this.server.listen(this.config.port, () => {\n this.config.logger.info?.(`MockServer listening on port ${this.config.port}`);\n resolve({\n server: this.server!,\n port: this.config.port,\n close: () => this.stop(),\n getStats: () => this.getCurrentStats()\n });\n });\n\n this.server.on('error', (error) => {\n reject(error);\n });\n } catch (error) {\n reject(error);\n }\n });\n }\n\n /**\n * Stop the mock server\n */\n async stop(): Promise<void> {\n return new Promise((resolve) => {\n if (this.server) {\n this.server.close(() => {\n this.config.logger.info?.('MockServer stopped');\n resolve();\n });\n } else {\n resolve();\n }\n });\n }\n\n /**\n * Get current server statistics (internal)\n */\n private getCurrentStats(): ServerStats {\n return {\n ...this.stats,\n uptime: Date.now() - this.startTime\n };\n }\n\n /**\n * Setup Express middleware\n */\n private setupMiddleware(): void {\n // Body parsing\n this.app.use(express.urlencoded({ extended: true }));\n this.app.use(express.json());\n\n // Logging with decoded URLs\n morgan.token('urlDecoded', (req) => {\n return decodeURIComponent(req.url || '');\n });\n\n this.app.use(morgan(':method :urlDecoded :status :response-time', {\n skip: (req, res) => req.url === '/version' || res.statusCode === 429,\n stream: {\n write: (message) => {\n if (this.config.debug) {\n this.config.logger.debug?.(message.trim());\n }\n }\n }\n }));\n\n // Custom middleware\n for (const middleware of this.config.middleware) {\n this.app.use(middleware);\n }\n }\n\n /**\n * Setup API routes\n */\n private setupRoutes(): void {\n // Version endpoint\n this.app.get('/version', (req, res) => {\n res.json({ version: '1.0.0', server: 'MockServer' });\n });\n\n // 404 test endpoint\n this.app.get('/404', (req, res) => {\n res.status(404).json({ code: 'not_found', message: 'not found' });\n });\n\n // Test endpoint with delay and error simulation\n this.app.get('/test', async (req, res) => {\n const delay = req.get('x-axios-delay');\n const err = req.get('x-axios-err');\n\n // Handle delay\n if (delay) {\n await new Promise(resolve => setTimeout(resolve, parseInt(delay)));\n }\n\n // Handle simulated errors\n if (err) {\n if (!isNaN(Number(err))) {\n return res.status(Number(err)).json({\n code: 'error',\n message: `expected error ${err}`\n });\n }\n\n if (err === 'econnreset') {\n const connection = (req as any).connection || (req as any).socket;\n connection?.destroy();\n return;\n }\n }\n\n // Success response\n const message = delay ? `success with delay ${delay}` : 'success';\n res.status(200).json({ code: 'success', message });\n });\n\n // Catch-all route for mock responses\n this.app.all('/*', async (req, res) => {\n this.stats.totalRequests++;\n\n try {\n const mockResponse = await this.findMockResponse(req);\n\n if (mockResponse) {\n this.stats.mockResponses++;\n\n // Remove problematic headers\n const safeHeaders = { ...mockResponse.headers };\n delete safeHeaders['transfer-encoding'];\n delete safeHeaders['Transfer-Encoding'];\n\n res.set(safeHeaders);\n res.status(mockResponse.status).send(mockResponse.data);\n } else {\n this.stats.fallbackResponses++;\n res.status(200).json({ code: 'ok', message: 'catch all' });\n }\n } catch (error) {\n this.stats.errors++;\n this.config.logger.error?.('MockServer error:', error);\n res.status(500).json({ code: 'server_error', message: 'Internal server error' });\n }\n });\n }\n\n /**\n * Find mock response for a request\n */\n private async findMockResponse(req: express.Request): Promise<MockResponseData | null> {\n const headerValue = req.get('XAXIOSOrigin');\n\n if (!headerValue) {\n return null;\n }\n\n try {\n const url = new URL(headerValue);\n const method = req.method;\n const pathname = url.pathname;\n const query = url.search.slice(1); // Remove leading ?\n const requestData = this.extractRequestData(req);\n\n const criteria: RequestMatchCriteria = {\n method,\n host: url.host,\n pathname,\n query,\n requestData\n };\n\n return await this.catalog.findMock(criteria);\n } catch (error) {\n this.config.logger.error?.('Error finding mock response:', error);\n return null;\n }\n }\n\n\n /**\n * Extract request data from Express request\n */\n private extractRequestData(req: express.Request): any {\n const contentType = req.get('content-type');\n\n if (contentType?.includes('application/x-www-form-urlencoded')) {\n return req.body; // Already parsed by express.urlencoded\n } else if (contentType?.includes('application/json')) {\n return req.body; // Already parsed by express.json\n }\n\n return req.body;\n }\n\n /**\n * Store a mock response from an HTTP request/response\n */\n async storeMock(\n requestUrl: string,\n requestData: any,\n responseData: MockResponseData,\n operationId?: string,\n mockName?: string\n ): Promise<string> {\n return await this.catalog.storeMock(\n requestUrl,\n requestData,\n responseData,\n operationId,\n mockName,\n this.config.sensitiveKeys\n );\n }\n\n /**\n * List all stored mock responses\n */\n async listMocks(): Promise<MockResponseEntry[]> {\n return await this.catalog.listEntries();\n }\n\n /**\n * Remove a mock response by filename\n */\n async removeMock(filename: string): Promise<boolean> {\n return await this.catalog.removeEntry(filename);\n }\n\n /**\n * Run maintenance to clean up orphaned files\n */\n async maintenance(): Promise<{ cleaned: number }> {\n return await this.catalog.maintenance();\n }\n\n /**\n * Get current configuration\n */\n getConfig(): Readonly<MockServerConfig> {\n return { ...this.config };\n }\n\n /**\n * Get server statistics\n */\n getStats(): ServerStats {\n return {\n ...this.stats,\n uptime: Date.now() - this.startTime\n };\n }\n}\n\n/**\n * Create and start a mock server instance\n */\nexport async function createMockServer(config: MockServerConfig): Promise<MockServerInstance> {\n const server = new MockServer(config);\n return server.start();\n}\n","/**\n * FileDatabase - Versioned, file-based data storage system\n * \n * Provides organized file storage with:\n * - Timestamp-based versioning\n * - Chunked/paginated file writes for large datasets\n * - Metadata tracking\n * - Backward compatibility with legacy structures\n * - Multiple storage modes (versioned, catalog, logs)\n */\n\nimport fs from \"fs\";\nimport path from \"path\";\nimport type {\n FileDatabaseConfig,\n VersionMetadata,\n FileEntry,\n ReadOptions,\n WriteOptions,\n WriteContext,\n FileSynopsisFunction,\n VersionSynopsisFunction,\n DataType,\n} from \"./types.js\";\nimport { ensurePath, generateVersionName, getFileExtension, getFreeDiskSpace, bytesToHumanReadable, isTimestampFolder } from \"./utils.js\";\nimport { detectDataType, serializeData, deserializeData } from \"./serializers.js\";\nimport { ParamError } from \"../errors.js\";\n\n/**\n * FileDatabase Error class\n */\nexport class FileDatabaseError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FileDatabaseError\";\n }\n}\n\nexport class FileDatabase {\n private basePath: string;\n private namespace: string;\n private tableName: string | null = null;\n private versioned: boolean;\n private maxVersions: number;\n private pageSize: number;\n private useMetadata: boolean;\n private freeSpaceThreshold: number;\n private logger: any;\n\n // Current operation state\n private currentVersion: string | null = null;\n private currentVersionFolder: string | null = null;\n private currentFileNumber: number = 0;\n private currentRecord: number = 0;\n private hasReadFirstPage: boolean = false;\n private lastFileData: any = null;\n private metadata: VersionMetadata;\n\n // Synopsis calculation functions\n private fileSynopsisFunction: FileSynopsisFunction | null = null;\n private versionSynopsisFunction: VersionSynopsisFunction | null = null;\n\n constructor(config: FileDatabaseConfig) {\n // Validate required configuration\n if (!config.basePath) {\n throw new ParamError(\"[FileDatabase] basePath is required\");\n }\n\n this.basePath = config.basePath;\n this.namespace = config.namespace || \"default\";\n this.tableName = config.tableName || null;\n this.versioned = config.versioned ?? true; // Default true for backward compatibility\n this.maxVersions = config.maxVersions || 5;\n this.pageSize = config.pageSize || 5000;\n this.useMetadata = config.useMetadata !== false; // Default true\n this.freeSpaceThreshold = config.freeSpaceThreshold || 100 * 1024 * 1024; // 100MB\n this.logger = config.logger || console;\n\n // Initialize metadata\n this.metadata = this.getDefaultMetadata();\n }\n\n /**\n * Get default metadata structure\n */\n private getDefaultMetadata(): VersionMetadata {\n return {\n version: this.currentVersion || null,\n files: [],\n createdAt: new Date().toISOString(),\n modifiedAt: new Date().toISOString(),\n totalRecords: 0,\n synopsis: null,\n dataType: null,\n };\n }\n\n /**\n * Get the destination path (basePath/namespace/tableName[/version])\n */\n private getDestinationPath(version?: string): string {\n const errors = [\"basePath\", \"namespace\", \"tableName\"]\n .filter(prop => !this[prop as keyof this])\n .map(prop => `${prop} is not set`);\n if (errors.length) {\n throw new FileDatabaseError(`[FileDatabase] ${errors.join(\"; \")}`);\n }\n\n let parts = [this.basePath, this.namespace];\n if (this.tableName) {\n parts.push(...this.tableName.split('/'));\n }\n\n // Only add version folder if in versioned mode and version is specified\n if (this.versioned && version) {\n parts.push(version);\n }\n\n return path.resolve(...parts);\n }\n\n /**\n * Set current version and version folder\n */\n private async setCurrentVersion(version: string): Promise<void> {\n this.currentVersion = version;\n this.currentVersionFolder = await ensurePath(this.getDestinationPath(), version);\n }\n\n /**\n * Create a new version folder with comprehensive timestamp logic\n * Only works in versioned mode\n */\n private async makeNewVersion(): Promise<string> {\n if (!this.versioned) {\n throw new FileDatabaseError(\"makeNewVersion() only works in versioned mode\");\n }\n\n // Reset in-memory metadata when creating a new version\n this.metadata = this.getDefaultMetadata();\n\n const existingVersions = await this.getVersions();\n let versionName: string;\n\n if (existingVersions.length > 0) {\n // Find the maximum timestamp among all existing versions\n const maxTimestamp = existingVersions.reduce((max, version) => {\n const versionDate = new Date(version.replace(\"Z\", \"\"));\n const maxDate = new Date(max.replace(\"Z\", \"\"));\n return versionDate > maxDate ? version : max;\n });\n\n // Increment the maximum timestamp by 1 second\n const maxDate = new Date(maxTimestamp.replace(\"Z\", \"\"));\n const nextDate = new Date(maxDate.getTime() + 1000);\n versionName = nextDate.toISOString().split(\".\")[0] + \"Z\";\n } else {\n // No existing versions, use current timestamp\n const now = new Date();\n versionName = now.toISOString().split(\".\")[0] + \"Z\";\n }\n\n await this.setCurrentVersion(versionName);\n\n // Reset file numbering for new version\n this.currentFileNumber = 0;\n\n // Delete old versions if we exceed maxVersions\n const versions = await this.getVersions();\n while (versions.length > this.maxVersions) {\n const versionToDelete = path.resolve(this.getDestinationPath(), versions.shift()!);\n this.logger.debug?.(`[FileDatabase] Deleting old version: ${versionToDelete}`);\n await fs.promises.rm(versionToDelete, { recursive: true, force: true });\n }\n\n return versionName;\n }\n\n /**\n * Get list of all versions (sorted chronologically)\n * Only works in versioned mode\n */\n async getVersions(): Promise<string[]> {\n if (!this.versioned) {\n return []; // No versions in non-versioned mode\n }\n\n const destPath = this.getDestinationPath();\n\n try {\n await ensurePath(destPath);\n const items = await fs.promises.readdir(destPath);\n const versions = items.filter(item => {\n const itemPath = path.join(destPath, item);\n const stat = fs.statSync(itemPath);\n return stat.isDirectory() && isTimestampFolder(item);\n });\n\n return versions.sort();\n } catch (error) {\n return [];\n }\n }\n\n /**\n * Get the latest version (most recent timestamp)\n * Only works in versioned mode\n * @returns Latest version string or null if no versions\n */\n async getLatestVersion(): Promise<string | null> {\n if (!this.versioned) {\n throw new FileDatabaseError(\"getLatestVersion() only works in versioned mode\");\n }\n\n const versions = await this.getVersions();\n if (versions.length === 0) {\n return null;\n }\n\n return versions[versions.length - 1];\n }\n\n /**\n * Check if any data exists in this table\n * Works for both versioned and non-versioned modes\n * @returns true if data exists\n */\n async hasData(): Promise<boolean> {\n const tablePath = this.getDestinationPath();\n\n if (!fs.existsSync(tablePath)) {\n return false;\n }\n\n if (this.versioned) {\n // Check for version folders\n const versions = await this.getVersions();\n return versions.length > 0;\n } else {\n // Check for any data files or metadata\n const items = await fs.promises.readdir(tablePath);\n return items.some(item =>\n item === 'metadata.json' ||\n item.match(/^\\d{6}\\.(json|txt|xml)$/) ||\n item.endsWith('.json')\n );\n }\n }\n\n /**\n * Auto-detect the data format in this table\n * Used when reading existing data\n * @returns Format detection result\n */\n async detectDataFormat(): Promise<{\n versioned: boolean;\n hasMetadata: boolean;\n dataType: DataType | null;\n }> {\n const tablePath = this.getDestinationPath();\n\n if (!fs.existsSync(tablePath)) {\n return { versioned: false, hasMetadata: false, dataType: null };\n }\n\n const items = await fs.promises.readdir(tablePath);\n\n // Check for metadata.json in root (non-versioned with metadata)\n if (items.includes('metadata.json')) {\n const metadata = JSON.parse(\n await fs.promises.readFile(path.join(tablePath, 'metadata.json'), 'utf8')\n );\n return {\n versioned: false,\n hasMetadata: true,\n dataType: metadata.dataType || null\n };\n }\n\n // Check for version folders\n const versionFolders = items.filter(item => {\n const itemPath = path.join(tablePath, item);\n const stat = fs.statSync(itemPath);\n return stat.isDirectory() && isTimestampFolder(item);\n });\n\n if (versionFolders.length > 0) {\n // Check if latest version has metadata\n const latestVersion = versionFolders.sort().pop()!;\n const versionMetadataPath = path.join(tablePath, latestVersion, 'metadata.json');\n\n return {\n versioned: true,\n hasMetadata: fs.existsSync(versionMetadataPath),\n dataType: null\n };\n }\n\n // Check for sequential files (legacy non-versioned)\n const dataFiles = items.filter(f => f.match(/^\\d{6}\\.(json|txt|xml)$/));\n if (dataFiles.length > 0) {\n return {\n versioned: false,\n hasMetadata: false,\n dataType: null\n };\n }\n\n return { versioned: false, hasMetadata: false, dataType: null };\n }\n\n /**\n * Load metadata from JSON file\n */\n private async loadMetadataJson(version: string): Promise<VersionMetadata | null> {\n const metadataFile = path.join(this.getDestinationPath(), version, \"metadata.json\");\n if (fs.existsSync(metadataFile)) {\n try {\n const rawData = await fs.promises.readFile(metadataFile, \"utf8\");\n return JSON.parse(rawData);\n } catch (e) {\n throw new FileDatabaseError(`Failed to read metadata for version \"${version}\": ${(e as Error).message}`);\n }\n }\n return null;\n }\n\n /**\n * Build metadata by scanning files in a version folder (backward compatibility)\n * Reads all files to get accurate counts - used when synopsis calculation is needed\n */\n private async figureMetadataFromVersionFiles(version: string): Promise<VersionMetadata> {\n const versionPath = path.join(this.getDestinationPath(), version);\n\n if (!fs.existsSync(versionPath)) {\n return this.getDefaultMetadata();\n }\n\n const files = (await fs.promises.readdir(versionPath))\n .filter(file => file !== \"metadata.json\" && !file.startsWith(\".\"))\n .sort();\n\n const metadata = this.getDefaultMetadata();\n metadata.version = version;\n metadata.files = [];\n\n let totalRecords = 0;\n let detectedDataType: DataType | null = null;\n\n for (let i = 0; i < files.length; i++) {\n const fileName = files[i];\n const filePath = path.join(versionPath, fileName);\n\n try {\n const rawData = await fs.promises.readFile(filePath, \"utf8\");\n const extension = path.extname(fileName).toLowerCase();\n let dataType: DataType = \"text\";\n\n if (extension === \".json\") {\n dataType = \"json-array\";\n } else if (extension === \".xml\") {\n dataType = \"xml\";\n }\n\n const fileData = deserializeData(rawData, dataType);\n const recordsCount = Array.isArray(fileData) ? fileData.length : 1;\n\n if (detectedDataType === null) {\n detectedDataType = detectDataType(fileData);\n }\n\n const fileInfo: FileEntry = {\n number: i + 1,\n recordsCount,\n fileName,\n };\n\n metadata.files.push(fileInfo);\n totalRecords += recordsCount;\n } catch (error) {\n this.logger.error?.(`[FileDatabase] Failed to read file ${fileName}: ${(error as Error).message}`);\n }\n }\n\n metadata.totalRecords = totalRecords;\n metadata.dataType = detectedDataType;\n\n return metadata;\n }\n\n /**\n * Build metadata optimized - only reads first and last files\n * Assumes all middle files have the same record count as the first file\n * Much faster for large datasets with many files\n */\n private async buildMetadataOptimized(version: string): Promise<VersionMetadata> {\n const versionPath = path.join(this.getDestinationPath(), version);\n\n if (!fs.existsSync(versionPath)) {\n return this.getDefaultMetadata();\n }\n\n const files = (await fs.promises.readdir(versionPath))\n .filter(file => file !== \"metadata.json\" && !file.startsWith(\".\"))\n .sort();\n\n if (files.length === 0) {\n return this.getDefaultMetadata();\n }\n\n const metadata = this.getDefaultMetadata();\n metadata.version = version;\n metadata.files = files.map((fileName, index) => ({\n number: index + 1,\n recordsCount: 0,\n fileName,\n }));\n\n // Read first file to determine data type and standard record count\n const firstFile = metadata.files[0];\n const firstFilePath = path.join(versionPath, firstFile.fileName);\n const firstFileRaw = await fs.promises.readFile(firstFilePath, \"utf8\");\n\n let firstFileData: any;\n try {\n firstFileData = JSON.parse(firstFileRaw);\n } catch (e) {\n firstFileData = firstFileRaw;\n }\n\n metadata.dataType = detectDataType(firstFileData);\n\n // Only proceed with optimization for json-array data\n if (metadata.dataType === \"json-array\") {\n const firstFileCount = Array.isArray(firstFileData) ? firstFileData.length : 1;\n firstFile.recordsCount = firstFileCount;\n\n // Assume all middle files have the same count as the first\n for (let i = 1; i < metadata.files.length - 1; i++) {\n metadata.files[i].recordsCount = firstFileCount;\n }\n\n // Read last file to get its actual count (might be partial)\n if (files.length > 1) {\n const lastFile = metadata.files[metadata.files.length - 1];\n const lastFilePath = path.join(versionPath, lastFile.fileName);\n const lastFileRaw = await fs.promises.readFile(lastFilePath, \"utf8\");\n const lastFileData = deserializeData(lastFileRaw, metadata.dataType);\n lastFile.recordsCount = Array.isArray(lastFileData) ? lastFileData.length : 1;\n }\n\n // Calculate total records\n metadata.totalRecords = metadata.files.reduce((sum, file) => sum + file.recordsCount, 0);\n } else {\n // For non-array data, count each file as 1 record\n metadata.files.forEach(file => {\n file.recordsCount = 1;\n });\n metadata.totalRecords = files.length;\n }\n\n return metadata;\n }\n\n /**\n * Figure out metadata - tries JSON first, then builds from files\n * Uses optimized building when no synopsis calculation is needed\n */\n private async figureMetadata(version: string, useOptimized: boolean = true): Promise<VersionMetadata> {\n if (this.useMetadata) {\n const metadata = await this.loadMetadataJson(version);\n if (metadata) {\n return metadata;\n }\n }\n \n // Fallback: build from files\n // Use optimized version (only reads first+last) when no synopsis needed\n if (useOptimized && !this.fileSynopsisFunction && !this.versionSynopsisFunction) {\n return await this.buildMetadataOptimized(version);\n }\n \n // Use full version (reads all files) when synopsis calculation needed\n return await this.figureMetadataFromVersionFiles(version);\n }\n\n /**\n * Load version metadata (main entry point for loading)\n */\n private async loadVersionMetadata(version: string): Promise<VersionMetadata> {\n const metadata = await this.figureMetadata(version);\n this.metadata = metadata;\n return metadata;\n }\n\n /**\n * Save version metadata to file\n */\n private async saveVersionMetadata(metadata?: VersionMetadata): Promise<void> {\n if (!this.useMetadata) {\n return;\n }\n\n const metadataToSave = metadata || this.metadata;\n let metadataFile: string;\n\n if (this.versioned) {\n // Versioned mode: metadata in version folder\n if (!this.currentVersion) {\n return;\n }\n metadataFile = path.join(this.getDestinationPath(), this.currentVersion, \"metadata.json\");\n } else {\n // Non-versioned mode: metadata in root table folder\n metadataFile = path.join(this.getDestinationPath(), \"metadata.json\");\n }\n\n await fs.promises.writeFile(metadataFile, JSON.stringify(metadataToSave, null, 4), \"utf8\");\n }\n\n /**\n * Create a new file entry in metadata\n */\n private makeNewFile(): void {\n this.currentFileNumber = (this.currentFileNumber || 0) + 1;\n\n const dataType = this.metadata.dataType || \"json-array\";\n const fileEntry: FileEntry = {\n number: this.currentFileNumber,\n recordsCount: 0,\n fileName: `${this.currentFileNumber.toString().padStart(6, \"0\")}.${getFileExtension(dataType)}`,\n };\n\n this.metadata.files.push(fileEntry);\n this.lastFileData = null;\n\n this.logger.debug?.(`[FileDatabase] Created new file: ${fileEntry.fileName}, fileNumber: ${this.currentFileNumber}`);\n }\n\n /**\n * Figure out what data to write and which file to use (for pagination)\n */\n private figureOutDataAndFileToWrite(data: any): WriteContext {\n let dataToWrite: any;\n let dataLeftOver: any[] | null;\n\n const lastFile = this.metadata.files[this.metadata.files.length - 1];\n const lastFileRecordsCount = lastFile.recordsCount;\n\n if (Array.isArray(data)) {\n // For arrays, handle pagination\n if (lastFileRecordsCount < this.pageSize) {\n // Try to append to existing file if there's space\n dataToWrite = [...(this.lastFileData || []), ...data.slice(0, this.pageSize - lastFileRecordsCount)];\n dataLeftOver = data.slice(this.pageSize - lastFileRecordsCount);\n } else {\n // Last file is full, create a new file\n this.makeNewFile();\n dataToWrite = data.slice(0, this.pageSize);\n dataLeftOver = data.slice(this.pageSize);\n }\n this.lastFileData = dataToWrite;\n } else {\n // For non-arrays, write as-is\n dataToWrite = data;\n dataLeftOver = null;\n }\n\n const fileName = this.metadata.files[this.metadata.files.length - 1].fileName;\n\n this.logger.debug?.(\n `[FileDatabase] figureOutDataAndFileToWrite: filename=${fileName}, dataToWrite.length=${Array.isArray(dataToWrite) ? dataToWrite.length : \"N/A\"}, lastFileRecordsCount=${lastFileRecordsCount}`\n );\n\n return { dataToWrite, dataLeftOver, fileName };\n }\n\n /**\n * Calculate file-level synopsis if function is set\n */\n private calculateFileSynopsis(data: any, fileIndex: number = this.metadata.files.length - 1): void {\n if (!this.fileSynopsisFunction) {\n return;\n }\n const fileInfo = this.metadata.files[fileIndex];\n const enhancedFileInfo = this.fileSynopsisFunction(fileInfo, data);\n this.metadata.files[fileIndex] = enhancedFileInfo;\n }\n\n /**\n * Calculate version-level synopsis if function is set\n */\n private calculateVersionSynopsis(): void {\n if (!this.versionSynopsisFunction) {\n return;\n }\n const enhancedMetadata = this.versionSynopsisFunction(this.metadata);\n this.metadata = enhancedMetadata;\n }\n\n /**\n * Update metadata after writing data\n */\n private updateMetadata(dataToWrite: any, fileName?: string): void {\n let currentFile: FileEntry;\n\n if (fileName) {\n // Find the specific file by filename\n const foundFile = this.metadata.files.find(file => file.fileName === fileName);\n if (!foundFile) {\n this.logger.warn?.(`[FileDatabase] File ${fileName} not found in metadata, using last file`);\n currentFile = this.metadata.files[this.metadata.files.length - 1];\n } else {\n currentFile = foundFile;\n }\n } else {\n // Get the current file info from metadata (always the last file)\n currentFile = this.metadata.files[this.metadata.files.length - 1];\n }\n\n // Calculate the actual records count for the data being written\n const recordsCount = Array.isArray(dataToWrite) ? dataToWrite.length : 1;\n\n // Update existing file entry with the correct records count\n currentFile.recordsCount = recordsCount;\n\n // Find the file index for synopsis calculation\n const fileIndex = this.metadata.files.indexOf(currentFile);\n if (fileIndex !== -1) {\n this.calculateFileSynopsis(dataToWrite, fileIndex);\n }\n\n // Update version metadata\n this.metadata.version = this.currentVersion;\n this.metadata.modifiedAt = new Date().toISOString();\n this.metadata.dataType = detectDataType(dataToWrite);\n\n // Recalculate total records by summing all file records counts\n this.metadata.totalRecords = this.metadata.files.reduce((sum, file) => sum + (file.recordsCount || 0), 0);\n\n this.logger.debug?.(\n `[FileDatabase] Updated metadata for file ${currentFile.fileName}: recordsCount=${recordsCount}, totalRecords=${this.metadata.totalRecords}`\n );\n }\n\n /**\n * Safe write with disk space check\n */\n private async safeWrite(filePath: string, data: any): Promise<void> {\n const serializedData = serializeData(data);\n const dir = path.dirname(filePath);\n const requiredBytes = Buffer.byteLength(serializedData, \"utf8\");\n\n // Check disk space\n const freeBytes = getFreeDiskSpace(dir);\n if (freeBytes !== null) {\n if (freeBytes < requiredBytes) {\n throw new FileDatabaseError(\n `Not enough disk space. Required: ${bytesToHumanReadable(requiredBytes)}, Free: ${bytesToHumanReadable(freeBytes)}`\n );\n }\n\n if (freeBytes < this.freeSpaceThreshold) {\n this.logger.warn?.(`Low disk space warning: only ${bytesToHumanReadable(freeBytes)} left`);\n }\n }\n\n try {\n await fs.promises.writeFile(filePath, serializedData, \"utf8\");\n this.logger.debug?.(`[FileDatabase] Wrote ${bytesToHumanReadable(requiredBytes)} to ${filePath}`);\n } catch (error) {\n throw new FileDatabaseError(`Failed to write file ${filePath}: ${(error as Error).message}`);\n }\n }\n\n /**\n * Prepare the instance for read or write operations\n * This discovers state and sets up internal members based on mode and current data\n */\n private async prepare({ write, read, version }: { write?: boolean; read?: boolean; version?: string }): Promise<void> {\n if (write) {\n if (this.versioned) {\n // Versioned mode\n if (this.currentVersion === null) {\n await this.makeNewVersion();\n this.metadata = this.getDefaultMetadata();\n this.metadata.version = this.currentVersion;\n this.makeNewFile();\n } else {\n // For existing versions, load the metadata if not already loaded\n if (!this.metadata.files.length) {\n this.metadata = await this.figureMetadata(this.currentVersion);\n }\n }\n } else {\n // Non-versioned mode - ensure table directory exists\n await ensurePath(this.getDestinationPath());\n\n // Non-versioned mode - auto-detect useMetadata if not set\n if (this.useMetadata === true) {\n // Try to load existing metadata, create new if doesn't exist\n const metadataPath = path.join(this.getDestinationPath(), 'metadata.json');\n if (fs.existsSync(metadataPath)) {\n try {\n const rawData = await fs.promises.readFile(metadataPath, 'utf8');\n this.metadata = JSON.parse(rawData);\n } catch (e) {\n this.metadata = this.getDefaultMetadata();\n }\n } else {\n this.metadata = this.getDefaultMetadata();\n this.makeNewFile();\n }\n } else {\n // No metadata mode - just create default metadata\n this.metadata = this.getDefaultMetadata();\n this.makeNewFile();\n }\n }\n } else if (read) {\n if (this.versioned) {\n // Versioned mode\n const versions = await this.getVersions();\n if (versions.length === 0) {\n throw new FileDatabaseError(\"[FileDatabase] No versions found, cannot read\");\n }\n\n if (version) {\n if (!versions.includes(version)) {\n throw new FileDatabaseError(`[FileDatabase] Version \"${version}\" not found`);\n }\n await this.setCurrentVersion(version);\n } else {\n await this.setCurrentVersion(versions[versions.length - 1]);\n }\n\n if (!this.metadata.files.length) {\n this.metadata = await this.figureMetadata(this.currentVersion!);\n }\n } else {\n // Non-versioned mode\n this.currentVersion = null; // No version concept\n\n // Auto-detect useMetadata if not explicitly set\n if (this.useMetadata === undefined) {\n const format = await this.detectDataFormat();\n this.useMetadata = format.hasMetadata;\n }\n\n if (this.useMetadata) {\n // Load metadata from root\n const metadataPath = path.join(this.getDestinationPath(), 'metadata.json');\n if (fs.existsSync(metadataPath)) {\n try {\n const rawData = await fs.promises.readFile(metadataPath, 'utf8');\n this.metadata = JSON.parse(rawData);\n } catch (e) {\n throw new FileDatabaseError(`Failed to read metadata: ${(e as Error).message}`);\n }\n } else {\n throw new FileDatabaseError(\"[FileDatabase] No metadata found in non-versioned mode\");\n }\n } else {\n // Figure metadata from files\n this.metadata = await this.figureMetadataFromVersionFiles('');\n }\n }\n }\n }\n\n /**\n * Write data to the file database\n */\n async write(data: any, options: WriteOptions = {}): Promise<void> {\n // Check for forceNewVersion in non-versioned mode\n if (options.forceNewVersion && !this.versioned) {\n throw new FileDatabaseError(\"Cannot use forceNewVersion in non-versioned mode\");\n }\n\n // Prepare for writing\n await this.prepare({ write: true });\n\n // Force new version if requested (versioned mode only)\n if (options.forceNewVersion) {\n await this.makeNewVersion();\n this.metadata = this.getDefaultMetadata();\n this.metadata.version = this.currentVersion;\n this.makeNewFile();\n }\n\n let { dataToWrite, dataLeftOver, fileName } = this.figureOutDataAndFileToWrite(data);\n\n // Write first batch\n const destPath = this.getDestinationPath(this.currentVersion || undefined);\n await this.safeWrite(path.join(destPath, fileName), dataToWrite);\n this.updateMetadata(dataToWrite, fileName);\n\n // Handle pagination for remaining data (arrays only)\n while (dataLeftOver && dataLeftOver.length > 0) {\n const writeContext = this.figureOutDataAndFileToWrite(dataLeftOver);\n await this.safeWrite(path.join(destPath, writeContext.fileName), writeContext.dataToWrite);\n this.updateMetadata(writeContext.dataToWrite, writeContext.fileName);\n dataLeftOver = writeContext.dataLeftOver;\n }\n\n // Calculate version synopsis\n this.calculateVersionSynopsis();\n\n // Save metadata to file\n if (this.useMetadata) {\n await this.saveVersionMetadata(this.metadata);\n }\n }\n\n /**\n * Read data from the file database\n */\n async read(options: ReadOptions = {}): Promise<any> {\n const { version, nextPage = false, pageSize } = options;\n\n // Prepare for reading\n await this.prepare({ read: true, version });\n\n // Check for non-paginated data types\n const isNonPaginatedData =\n this.metadata.dataType === \"text\" || this.metadata.dataType === \"xml\" || this.metadata.dataType === \"json-object\";\n\n if (isNonPaginatedData) {\n // For text/xml/object data, return all content\n const file = this.metadata.files[0];\n const filePath = path.join(this.getDestinationPath(this.currentVersion || undefined), file.fileName);\n try {\n const rawData = await fs.promises.readFile(filePath, \"utf8\");\n return deserializeData(rawData, this.metadata.dataType!);\n } catch (error) {\n throw new FileDatabaseError(`Failed to read file ${file.fileName}: ${(error as Error).message}`);\n }\n }\n\n // Handle paginated data (JSON arrays)\n let effectivePageSize: number;\n\n if (nextPage && this.hasReadFirstPage) {\n // Move to next page\n effectivePageSize = pageSize || this.pageSize;\n this.currentRecord += effectivePageSize;\n } else if (!nextPage) {\n // If not paginating, read all records (unless pageSize is explicitly provided)\n effectivePageSize = pageSize !== undefined ? pageSize : this.metadata.totalRecords;\n this.currentRecord = 0;\n } else {\n // First call with nextPage=true (no previous page read)\n effectivePageSize = pageSize || this.pageSize;\n }\n\n // If beyond total records, return empty array\n if (this.currentRecord >= this.metadata.totalRecords) {\n return [];\n }\n\n const result: any[] = [];\n let recordsRead = 0;\n let currentFileIndex = 0;\n let currentFileOffset = 0;\n\n // Calculate which file and offset to start from\n let totalRecords = 0;\n for (let i = 0; i < this.metadata.files.length; i++) {\n const file = this.metadata.files[i];\n if (this.currentRecord < totalRecords + file.recordsCount) {\n currentFileIndex = i;\n currentFileOffset = totalRecords;\n break;\n }\n totalRecords += file.recordsCount;\n }\n\n // Read from files\n let cumulativeRecords = currentFileOffset;\n for (let i = currentFileIndex; i < this.metadata.files.length && recordsRead < effectivePageSize; i++) {\n const file = this.metadata.files[i];\n const filePath = path.join(this.getDestinationPath(this.currentVersion || undefined), file.fileName);\n\n try {\n const rawData = await fs.promises.readFile(filePath, \"utf8\");\n const fileData = deserializeData(rawData, this.metadata.dataType!);\n\n let startIndex = 0;\n if (i === currentFileIndex) {\n startIndex = this.currentRecord - cumulativeRecords;\n }\n\n const endIndex = Math.min(startIndex + (effectivePageSize - recordsRead), fileData.length);\n const recordsFromThisFile = fileData.slice(startIndex, endIndex);\n\n result.push(...recordsFromThisFile);\n recordsRead += recordsFromThisFile.length;\n\n cumulativeRecords += file.recordsCount;\n } catch (error) {\n throw new FileDatabaseError(`Failed to read file ${file.fileName}: ${(error as Error).message}`);\n }\n }\n\n // Mark page as read for pagination tracking\n // When nextPage=true, we're explicitly paginating\n // When nextPage=false with explicit pageSize, we're also paginating (starting from beginning)\n if (result.length > 0) {\n if (nextPage || (pageSize !== undefined && pageSize < this.metadata.totalRecords)) {\n this.hasReadFirstPage = true;\n }\n }\n\n return result;\n }\n\n /**\n * Set the starting record for pagination (1-based index)\n */\n setStartRecord(startRecord: number): void {\n this.currentRecord = startRecord - 1;\n this.hasReadFirstPage = false;\n }\n\n /**\n * Reset read pagination state\n */\n resetPagination(): void {\n this.currentRecord = 0;\n this.hasReadFirstPage = false;\n }\n\n /**\n * Set file-level synopsis calculation function\n */\n setFileSynopsisFunction(fn: FileSynopsisFunction): void {\n this.fileSynopsisFunction = fn;\n }\n\n /**\n * Set version-level synopsis calculation function\n */\n setVersionSynopsisFunction(fn: VersionSynopsisFunction): void {\n this.versionSynopsisFunction = fn;\n }\n\n /**\n * Get current version name\n */\n getCurrentVersion(): string | null {\n return this.currentVersion;\n }\n\n /**\n * Get current metadata\n */\n getMetadata(): VersionMetadata {\n return { ...this.metadata };\n }\n}\n\n// Export types\nexport type {\n FileDatabaseConfig,\n VersionMetadata,\n FileEntry,\n ReadOptions,\n WriteOptions,\n FileSynopsisFunction,\n VersionSynopsisFunction,\n DataType,\n StorageMode,\n CatalogEntry,\n} from \"./types.js\";\n\n// Export synopsis functions\nexport { defaultFileSynopsisFunction, defaultVersionSynopsisFunction } from \"./synopsis-functions.js\";\n","/**\n * Operating System Utilities\n * \n * Helper functions for interacting with the operating system\n * to get system statistics and information\n */\n\nimport fs from \"fs\";\nimport path from \"path\";\nimport { execSync } from \"child_process\";\n\n/**\n * Get free disk space for a given path (in bytes)\n * Returns null if unable to determine\n * Uses `df` command on Unix-like systems\n */\nexport function getFreeDiskSpace(targetPath: string): number | null {\n try {\n // If the target path doesn't exist, use its parent directory\n let pathToCheck = targetPath;\n if (!fs.existsSync(targetPath)) {\n const parentDir = path.dirname(targetPath);\n if (fs.existsSync(parentDir)) {\n pathToCheck = parentDir;\n } else {\n // If parent doesn't exist either, use the root directory\n pathToCheck = process.platform === \"win32\" ? \"C:\\\\\" : \"/\";\n }\n }\n\n if (process.platform === \"win32\") {\n // Windows: use wmic command\n // Note: This is a simplified implementation, may need adjustments\n return null; // TODO: Implement robust Windows support\n } else {\n // Unix-like systems: use df command\n const stdout = execSync(`df -k \"${pathToCheck}\"`, { encoding: \"utf8\" });\n const lines = stdout.trim().split(\"\\n\");\n const parts = lines[1].split(/\\s+/); // second line, split on whitespace\n const freeKb = parseInt(parts[3], 10); // 4th column is \"Available\"\n return freeKb * 1024; // Convert to bytes\n }\n } catch (error) {\n // Silently fail and return null\n return null;\n }\n}\n\n","/**\n * File System Utilities\n * \n * Helper functions for file system operations like path management,\n * directory creation, and file type detection\n */\n\nimport fs from \"fs\";\nimport path from \"path\";\n\n/**\n * Ensure a directory path exists, creating it if necessary\n * Returns the absolute path\n */\nexport async function ensurePath(...pathParts: string[]): Promise<string> {\n const fullPath = path.resolve(...pathParts);\n \n if (!fs.existsSync(fullPath)) {\n await fs.promises.mkdir(fullPath, { recursive: true });\n }\n \n return fullPath;\n}\n\n/**\n * Synchronous version of ensurePath\n */\nexport function ensurePathSync(...pathParts: string[]): string {\n const fullPath = path.resolve(...pathParts);\n \n if (!fs.existsSync(fullPath)) {\n fs.mkdirSync(fullPath, { recursive: true });\n }\n \n return fullPath;\n}\n\n/**\n * Get file extension for a given data type\n */\nexport function getFileExtension(dataType: string): string {\n switch (dataType) {\n case \"json-array\":\n case \"json-object\":\n return \"json\";\n case \"text\":\n return \"txt\";\n case \"xml\":\n return \"xml\";\n default:\n return \"json\";\n }\n}\n\n","/**\n * Formatting Utilities\n * \n * Helper functions for converting between different data formats\n * and human-readable representations\n */\n\n/**\n * Convert bytes to human-readable format (e.g., \"1.5 MB\")\n */\nexport function bytesToHumanReadable(bytes: number): string {\n if (bytes === 0) return \"0 B\";\n \n const k = 1024;\n const sizes = [\"B\", \"KB\", \"MB\", \"GB\", \"TB\", \"PB\"];\n const i = Math.floor(Math.log(bytes) / Math.log(k));\n \n return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + \" \" + sizes[i];\n}\n\n/**\n * Convert human-readable format to bytes (e.g., \"1.5 MB\" -> 1572864)\n */\nexport function humanReadableToBytes(humanString: string): number {\n const units: Record<string, number> = {\n \"B\": 1,\n \"KB\": 1024,\n \"MB\": 1024 * 1024,\n \"GB\": 1024 * 1024 * 1024,\n \"TB\": 1024 * 1024 * 1024 * 1024,\n \"PB\": 1024 * 1024 * 1024 * 1024 * 1024,\n };\n \n const match = humanString.trim().match(/^([\\d.]+)\\s*([A-Z]+)$/i);\n if (!match) {\n throw new Error(`Invalid format: ${humanString}. Expected format like \"2MB\" or \"1.5 GB\"`);\n }\n \n const value = parseFloat(match[1]);\n const unit = match[2].toUpperCase();\n \n if (!units[unit]) {\n throw new Error(`Unknown unit: ${unit}. Supported units: ${Object.keys(units).join(\", \")}`);\n }\n \n return Math.round(value * units[unit]);\n}\n\n","/**\n * Date/time utility functions for timestamp formatting and timezone conversions\n */\n\nexport interface DateFormatOptions {\n timezone?: \"UTC\" | \"user\";\n format?: \"iso\" | \"iso-date\" | \"iso-time\" | \"human\";\n}\n\n/**\n * Format a date string or Date object to ISO8601 format in specified timezone\n * Always returns UTC-based ISO strings (YYYY-MM-DDTHH:mm:ssZ) as internal representation\n * \n * @param value - Date object, ISO string, or timestamp\n * @param options - Formatting options\n * @returns ISO8601 formatted string\n */\nexport function formatDate(value: Date | string | number | undefined, options: DateFormatOptions = {}): string {\n if (value === undefined || value === null) {\n return \"\";\n }\n\n const { timezone = \"UTC\", format = \"iso\" } = options;\n \n let date: Date;\n if (value instanceof Date) {\n date = value;\n } else if (typeof value === \"string\") {\n date = new Date(value);\n } else if (typeof value === \"number\") {\n date = new Date(value);\n } else {\n return \"\";\n }\n\n if (isNaN(date.getTime())) {\n return \"\";\n }\n\n // Format based on requested format\n switch (format) {\n case \"iso\":\n // Full ISO8601: 2025-01-01T01:01:01Z\n return date.toISOString();\n \n case \"iso-date\":\n // Date only: 2025-01-01\n return date.toISOString().split(\"T\")[0];\n \n case \"iso-time\":\n // Time only: 01:01:01Z\n return date.toISOString().split(\"T\")[1];\n \n case \"human\":\n // Human-readable: Jan 1, 2025 01:01:01 UTC\n if (timezone === \"user\") {\n return date.toLocaleString();\n }\n return date.toUTCString();\n \n default:\n return date.toISOString();\n }\n}\n\n/**\n * Parse a date value and return ISO8601 string in UTC\n * This is the canonical format for internal storage\n * \n * @param value - Date object or string\n * @returns ISO8601 string in UTC timezone\n */\nexport function toISOString(value: Date | string | number | undefined): string {\n return formatDate(value, { timezone: \"UTC\", format: \"iso\" });\n}\n\n/**\n * Get current timestamp as ISO8601 string in UTC\n * @returns Current time as ISO8601 string\n */\nexport function nowISO(): string {\n return new Date().toISOString();\n}\n\n/**\n * Parse ISO8601 string to Date object\n * @param isoString - ISO8601 formatted string\n * @returns Date object\n */\nexport function fromISOString(isoString: string): Date {\n return new Date(isoString);\n}\n\n/**\n * Calculate difference between two dates in various units\n * @param start - Start date (ISO string or Date)\n * @param end - End date (ISO string or Date)\n * @returns Object with duration in multiple units\n */\nexport function dateDiff(\n start: Date | string,\n end: Date | string\n): {\n milliseconds: number;\n seconds: number;\n minutes: number;\n hours: number;\n days: number;\n} {\n const startDate = start instanceof Date ? start : new Date(start);\n const endDate = end instanceof Date ? end : new Date(end);\n \n const diffMs = endDate.getTime() - startDate.getTime();\n \n return {\n milliseconds: diffMs,\n seconds: Math.floor(diffMs / 1000),\n minutes: Math.floor(diffMs / (1000 * 60)),\n hours: Math.floor(diffMs / (1000 * 60 * 60)),\n days: Math.floor(diffMs / (1000 * 60 * 60 * 24))\n };\n}\n\n/**\n * Check if a folder name looks like an ISO8601 timestamp\n * Used to identify version folders vs regular folders\n * @param folderName - Folder name to check\n * @returns true if the folder name is a valid ISO8601 timestamp\n */\nexport function isTimestampFolder(folderName: string): boolean {\n // Match ISO8601 format: YYYY-MM-DDTHH:mm:ssZ\n const isoRegex = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(Z|\\.\\d{3}Z)$/;\n \n if (!isoRegex.test(folderName)) {\n return false;\n }\n \n const date = new Date(folderName);\n return !isNaN(date.getTime()) && date.getTime() > 0;\n}\n\n/**\n * Generate a new version name (ISO8601 timestamp)\n * If existingVersions provided, ensures the new version is later than all existing ones\n * @param existingVersions - Array of existing version timestamps\n * @returns New version timestamp that is unique and later than existing ones\n */\nexport function generateVersionName(existingVersions: string[] = []): string {\n if (existingVersions.length === 0) {\n // No existing versions, use current timestamp\n const now = new Date();\n return now.toISOString().split(\".\")[0] + \"Z\";\n }\n \n // Find the maximum timestamp among all existing versions\n const maxTimestamp = existingVersions.reduce((max, version) => {\n const versionDate = new Date(version);\n const maxDate = new Date(max);\n return versionDate > maxDate ? version : max;\n });\n \n // Increment by 1 second to ensure uniqueness\n const maxDate = new Date(maxTimestamp);\n const nextDate = new Date(maxDate.getTime() + 1000);\n \n return nextDate.toISOString().split(\".\")[0] + \"Z\";\n}\n\n","/**\n * FileStore Serializers\n * \n * Data type detection and serialization/deserialization utilities\n */\n\nimport type { DataType } from \"./types.js\";\n\n/**\n * Detect the data type of a given value\n */\nexport function detectDataType(data: any): DataType {\n if (Array.isArray(data)) {\n return \"json-array\";\n } else if (typeof data === \"object\" && data !== null) {\n return \"json-object\";\n } else if (typeof data === \"string\") {\n // Try to detect if it's XML\n const trimmed = data.trim();\n if (trimmed.startsWith(\"<?xml\") || trimmed.startsWith(\"<\")) {\n return \"xml\";\n }\n return \"text\";\n } else {\n return \"text\";\n }\n}\n\n/**\n * Serialize data to a string for file storage\n */\nexport function serializeData(data: any): string {\n const dataType = detectDataType(data);\n \n if (dataType === \"json-array\" || dataType === \"json-object\") {\n return JSON.stringify(data, null, 4);\n } else {\n // For text and XML, return as-is\n return String(data);\n }\n}\n\n/**\n * Deserialize data from a string based on data type\n */\nexport function deserializeData(rawData: string, dataType: DataType): any {\n if (dataType === \"json-array\" || dataType === \"json-object\") {\n return JSON.parse(rawData);\n } else {\n // For text and XML, return as-is\n return rawData;\n }\n}\n\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\n","/**\n * MockServer Catalog - Request/Response Matching\n *\n * Provides catalog functionality for matching HTTP requests to stored mock responses\n * using FileDatabase for response data and direct file operations for catalog entries.\n */\n\nimport { FileDatabase } from '../filedatabase/index.js';\nimport * as fs from 'fs/promises';\nimport * as path from 'path';\nimport type { MockResponseEntry, MockResponseData, RequestMatchCriteria } from './types.js';\nimport { sanitizeRequest } from './sanitization.js';\n\n/**\n * Catalog interface for mock response storage and retrieval\n */\nexport class MockCatalog {\n private fileDb: FileDatabase;\n private catalogPath: string;\n private logger: any;\n\n constructor(fileDb: FileDatabase, catalogPath: string, logger: any = console) {\n this.fileDb = fileDb;\n this.catalogPath = catalogPath;\n this.logger = logger;\n }\n\n /**\n * Store a mock response in the catalog\n */\n async storeMock(\n requestUrl: string,\n requestData: any,\n responseData: MockResponseData,\n operationId?: string,\n mockName?: string,\n sensitiveKeys: string[] = []\n ): Promise<string> {\n try {\n const url = new URL(requestUrl);\n\n // Sanitize request data\n const sanitized = sanitizeRequest({\n query: url.search.slice(1),\n requestData,\n headers: {},\n data: responseData.data\n }, sensitiveKeys);\n\n // Create catalog entry\n const entry: MockResponseEntry = {\n method: 'GET', // Will be passed from caller\n host: url.host,\n pathname: url.pathname,\n query: sanitized.query,\n timestamp: new Date().toISOString(),\n ...(sanitized.requestData !== undefined ? { requestData: sanitized.requestData } : {}),\n ...(operationId ? { operationId } : {}),\n ...(mockName ? { mockName } : {})\n };\n\n // Generate unique filename\n const filename = this.generateFilename(entry);\n const catalogFilePath = path.join(this.catalogPath, `${filename}.json`);\n const responseFilename = `response_${filename}.json`;\n\n // Store the response data first\n await this.fileDb.write(responseData, { filename: responseFilename });\n\n // Update catalog entry with response filename\n entry.file = responseFilename;\n\n // Store the catalog entry directly as a file\n await fs.mkdir(path.dirname(catalogFilePath), { recursive: true });\n await fs.writeFile(catalogFilePath, JSON.stringify(entry, null, 2), 'utf-8');\n\n return filename;\n } catch (error) {\n this.logger.error?.('Error storing mock:', error);\n throw error;\n }\n }\n\n /**\n * Find mock response for a request\n */\n async findMock(criteria: RequestMatchCriteria): Promise<MockResponseData | null> {\n try {\n // Try exact match first\n const exactMatch = await this.findExactMatch(criteria);\n if (exactMatch) {\n return exactMatch;\n }\n\n // Try fuzzy match (ignore some parameters)\n const fuzzyMatch = await this.findFuzzyMatch(criteria);\n if (fuzzyMatch) {\n return fuzzyMatch;\n }\n\n return null;\n } catch (error) {\n this.logger.error?.('Error finding mock:', error);\n return null;\n }\n }\n\n /**\n * Find exact match for request criteria\n */\n private async findExactMatch(criteria: RequestMatchCriteria): Promise<MockResponseData | null> {\n try {\n const entries = await this.listEntries();\n\n for (const entry of entries) {\n if (this.matchesCriteria(entry, criteria)) {\n return await this.loadResponseData(entry.file);\n }\n }\n\n return null;\n } catch (error) {\n this.logger.error?.('Error in exact match:', error);\n return null;\n }\n }\n\n /**\n * Find fuzzy match for request criteria\n */\n private async findFuzzyMatch(criteria: RequestMatchCriteria): Promise<MockResponseData | null> {\n try {\n // Fuzzy matching: ignore certain query parameters\n const fuzzyCriteria = {\n ...criteria,\n query: this.stripCommonParams(criteria.query)\n };\n\n return this.findExactMatch(fuzzyCriteria);\n } catch (error) {\n this.logger.error?.('Error in fuzzy match:', error);\n return null;\n }\n }\n\n /**\n * Check if a catalog entry matches the request criteria\n */\n private matchesCriteria(entry: MockResponseEntry, criteria: RequestMatchCriteria): boolean {\n return (\n entry.method === criteria.method &&\n entry.host === criteria.host &&\n entry.pathname === criteria.pathname &&\n this.queriesMatch(entry.query, criteria.query) &&\n this.requestDataMatches(entry.requestData, criteria.requestData) &&\n (!criteria.operationId || entry.operationId === criteria.operationId)\n );\n }\n\n /**\n * Check if query strings match (with sanitization)\n */\n private queriesMatch(storedQuery: string, requestQuery: string): boolean {\n // For now, exact match. Could be enhanced with parameter normalization\n return storedQuery === requestQuery;\n }\n\n /**\n * Check if request data matches\n */\n private requestDataMatches(storedData: any, requestData: any): boolean {\n if (!storedData && !requestData) {\n return true;\n }\n\n if (!storedData || !requestData) {\n return false;\n }\n\n // Simple comparison - could be enhanced\n return JSON.stringify(storedData) === JSON.stringify(requestData);\n }\n\n /**\n * Load response data from file\n */\n private async loadResponseData(filename: string): Promise<MockResponseData | null> {\n try {\n const data = await this.fileDb.read({ filename });\n return data as MockResponseData;\n } catch (error) {\n this.logger.error?.('Error loading response data:', error);\n return null;\n }\n }\n\n /**\n * Strip common query parameters for fuzzy matching\n */\n private stripCommonParams(query: string): string {\n const commonParams = ['timestamp', 'nonce', 'cache', '_'];\n const params = new URLSearchParams(query);\n\n for (const param of commonParams) {\n params.delete(param);\n }\n\n return params.toString();\n }\n\n /**\n * Generate unique filename for catalog entry\n */\n private generateFilename(entry: MockResponseEntry): string {\n const timestamp = Date.now();\n const hash = this.simpleHash(`${entry.method}${entry.host}${entry.pathname}${entry.query}${entry.operationId || ''}`);\n return `mock_${timestamp}_${hash}`;\n }\n\n /**\n * Simple hash function for filename generation\n */\n private simpleHash(str: string): string {\n let hash = 0;\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i);\n hash = ((hash << 5) - hash) + char;\n hash = hash & hash; // Convert to 32-bit integer\n }\n return Math.abs(hash).toString(36);\n }\n\n /**\n * List all catalog entries\n */\n async listEntries(): Promise<MockResponseEntry[]> {\n try {\n const entries: MockResponseEntry[] = [];\n\n // Read all .json files from the catalog directory\n const files = await fs.readdir(this.catalogPath).catch(() => []);\n const jsonFiles = files.filter(file => file.endsWith('.json') && !file.startsWith('response_'));\n\n for (const file of jsonFiles) {\n try {\n const filePath = path.join(this.catalogPath, file);\n const content = await fs.readFile(filePath, 'utf-8');\n const entry = JSON.parse(content) as MockResponseEntry;\n entries.push(entry);\n } catch (error) {\n this.logger.warn?.(`Error reading catalog entry ${file}:`, error);\n }\n }\n\n return entries;\n } catch (error) {\n this.logger.error?.('Error listing catalog entries:', error);\n return [];\n }\n }\n\n /**\n * Remove a catalog entry and its response data\n */\n async removeEntry(filename: string): Promise<boolean> {\n try {\n const catalogFilePath = path.join(this.catalogPath, `${filename}.json`);\n const responseFilename = `response_${filename}.json`;\n\n // Remove catalog entry file\n try {\n await fs.unlink(catalogFilePath);\n } catch (error) {\n this.logger.warn?.(`Could not remove catalog file ${catalogFilePath}:`, error);\n }\n\n // Remove response data (this will need to be implemented in FileDatabase)\n // For now, just try to remove the file directly\n try {\n const responseFilePath = path.join(this.catalogPath, responseFilename);\n await fs.unlink(responseFilePath);\n } catch (error) {\n this.logger.warn?.(`Could not remove response file ${responseFilename}:`, error);\n }\n\n return true;\n } catch (error) {\n this.logger.error?.('Error removing entry:', error);\n return false;\n }\n }\n\n /**\n * Clean up orphaned files and invalid catalog entries\n */\n async maintenance(): Promise<{ cleaned: number }> {\n try {\n let cleaned = 0;\n\n // Get all files in the directory\n const files = await fs.readdir(this.catalogPath).catch(() => []);\n const catalogFiles = files.filter(file => file.endsWith('.json') && !file.startsWith('response_'));\n const responseFiles = files.filter(file => file.startsWith('response_'));\n\n // Check for catalog entries with missing response files\n for (const catalogFile of catalogFiles) {\n try {\n const catalogPath = path.join(this.catalogPath, catalogFile);\n const content = await fs.readFile(catalogPath, 'utf-8');\n const entry = JSON.parse(content) as MockResponseEntry;\n\n const responseFile = entry.file;\n const responsePath = path.join(this.catalogPath, responseFile);\n\n // Check if response file exists\n try {\n await fs.access(responsePath);\n } catch {\n // Response file is missing, remove catalog entry\n await fs.unlink(catalogPath);\n cleaned++;\n this.logger.info?.(`Removed orphaned catalog entry: ${catalogFile}`);\n }\n } catch (error) {\n this.logger.warn?.(`Error processing catalog file ${catalogFile}:`, error);\n }\n }\n\n // Check for response files without catalog entries\n const catalogResponseFiles = catalogFiles.map(file =>\n `response_${file.replace('.json', '')}.json`\n );\n\n for (const responseFile of responseFiles) {\n if (!catalogResponseFiles.includes(responseFile)) {\n // Orphaned response file\n const responsePath = path.join(this.catalogPath, responseFile);\n await fs.unlink(responsePath);\n cleaned++;\n this.logger.info?.(`Removed orphaned response file: ${responseFile}`);\n }\n }\n\n return { cleaned };\n } catch (error) {\n this.logger.error?.('Error during maintenance:', error);\n return { cleaned: 0 };\n }\n }\n}\n","/**\n * MockServer Data Sanitization\n *\n * Handles masking of sensitive data in requests and responses for mock storage\n */\n\nimport { createHash } from 'crypto';\nimport { parse, stringify } from 'querystring';\n\n/**\n * Create MD5 hash of a value for masking\n */\nexport function maskValue(value: string): string {\n return `[md5:${createHash('md5').update(value).digest('hex')}]`;\n}\n\n/**\n * Sanitize URL-encoded query string by masking sensitive parameters\n */\nexport function sanitizeUrlEncodedString(input: string, keysToSanitize: string[]): string {\n const parsed = parse(input);\n const sanitized = sanitizeObject(parsed, keysToSanitize);\n return stringify(sanitized);\n}\n\n/**\n * Sanitize object by masking sensitive keys\n */\nexport function sanitizeObject(obj: Record<string, any>, keysToSanitize: string[]): Record<string, any> {\n if (Array.isArray(obj)) {\n // For arrays, return as-is since we don't sanitize array elements\n return obj;\n }\n\n const result: Record<string, any> = {};\n\n for (const [key, value] of Object.entries(obj || {})) {\n if (keysToSanitize.includes(key.toLowerCase())) {\n result[key] = maskValue(String(value));\n } else if (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n // Recursively sanitize nested objects\n result[key] = sanitizeObject(value, keysToSanitize);\n } else {\n result[key] = value;\n }\n }\n\n return result;\n}\n\n/**\n * Sanitize headers by masking authorization and other sensitive headers\n */\nexport function sanitizeHeaders(headers: Record<string, string>, additionalKeys: string[] = []): Record<string, string> {\n const sensitiveKeys = ['authorization', 'x-api-key', 'api-key', 'bearer', ...additionalKeys.map(k => k.toLowerCase())];\n const sanitized = { ...headers };\n\n for (const [key, value] of Object.entries(sanitized)) {\n if (sensitiveKeys.includes(key.toLowerCase())) {\n sanitized[key] = maskValue(value);\n }\n }\n\n return sanitized;\n}\n\n/**\n * Sanitize request data for mock storage\n */\nexport function sanitizeRequestData(data: any, keysToSanitize: string[]): any {\n if (typeof data === 'string') {\n // Only try to parse as URL-encoded if it contains '=' (key=value pattern)\n if (data.includes('=')) {\n try {\n const parsed = parse(data);\n return stringify(sanitizeObject(parsed, keysToSanitize));\n } catch {\n // If parsing fails, treat as plain string\n return data;\n }\n }\n // Plain string without '=', return as-is\n return data;\n } else if (typeof data === 'object' && data !== null) {\n return sanitizeObject(data, keysToSanitize);\n }\n\n return data;\n}\n\n/**\n * Sanitize response data for mock storage\n */\nexport function sanitizeResponseData(data: any, keysToSanitize: string[]): any {\n if (typeof data === 'object' && data !== null) {\n return sanitizeObject(data, keysToSanitize);\n }\n return data;\n}\n\n/**\n * Complete request sanitization for mock storage\n */\nexport function sanitizeRequest(params: {\n query: string;\n requestData?: any;\n headers?: Record<string, string>;\n data?: any;\n}, sensitiveKeys: string[]): {\n query: string;\n requestData?: any;\n headers: Record<string, string>;\n data: any;\n} {\n const { query, requestData, headers = {}, data } = params;\n\n return {\n query: sanitizeUrlEncodedString(query, sensitiveKeys),\n requestData: requestData ? sanitizeRequestData(requestData, sensitiveKeys) : undefined,\n headers: sanitizeHeaders(headers, sensitiveKeys),\n data: data ? sanitizeResponseData(data, sensitiveKeys) : data\n };\n}\n"],"mappings":"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","names":["fs","path","fs","path","path","maxDate","fs","fs","path","maskValue"]}
package/dist/params.cjs CHANGED
@@ -233,8 +233,6 @@ var Params = class {
233
233
  } else if (str.match(/^boolean|^bool/i)) {
234
234
  type = import_joi.default.boolean();
235
235
  } else if (str.match(/^date/i)) {
236
- type = import_joi.default.date();
237
- } else if (str.match(/^edate/i)) {
238
236
  type = import_joi.default.custom(joiEdateType);
239
237
  } else if (str.match(/^duration/i)) {
240
238
  type = import_joi.default.string().isoDuration();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/params.ts","../src/params/index.ts","../src/errors.ts","../src/params/custom-types.ts"],"sourcesContent":["// Re-export everything from the params module directory\nexport * from \"./params/index.js\";\n\n","import Joi from \"joi\";\nimport { ParamError } from \"../errors\";\nimport { joiEdateType, joiStringArrayType } from \"./custom-types\";\n\n/**\n * Parameter definition types\n */\nexport type ParamDefinition = \n | string \n | Joi.Schema \n | { \n type?: string | Joi.Schema; \n values?: any[]; \n [key: string]: any; \n };\n\nexport type ParamGetter = (key: string, definition?: any) => any;\nexport type ParamSetter = (key: string, value: any) => boolean;\n\n/**\n * Args instance interface\n */\nexport interface ArgsInstance {\n get(key: string): any;\n}\n\n/**\n * Params constructor options\n */\nexport interface ParamsOptions {\n [key: string]: any;\n}\n\n/**\n * Params class for parameter validation and type checking\n * Built on top of Args library with Joi validation\n */\nexport class Params {\n private params: Record<string, any> = {};\n private definitions: Record<string, any> = {};\n private args: ArgsInstance;\n private paramSetters: ParamSetter[] = [];\n private paramGetters: ParamGetter[] = [];\n\n constructor({ args }: { args: ArgsInstance }, opts: ParamsOptions = {}) {\n this.args = args;\n\n for (const [k, v] of Object.entries(opts)) {\n // TODO: opt values might be an object with definitions in it, so perhaps `this.set` should be used\n this.params[k] = v;\n }\n }\n\n /**\n * Assign a parameter definition\n */\n assignDefinition(key: string, definition?: ParamDefinition): any {\n if (this.definitions[key] && !definition) {\n return this.definitions[key];\n }\n\n let type: Joi.Schema;\n if (!definition) {\n type = Joi.string();\n } else if (Joi.isSchema(definition)) {\n type = definition;\n } else if (Joi.isSchema(definition.type)) {\n type = definition.type;\n } else if (typeof definition === \"string\") {\n type = this.toJoi(definition);\n } else if (typeof definition.type === \"string\") {\n type = this.toJoi(definition.type);\n } else if (!definition.type) {\n type = Joi.string();\n } else {\n type = Joi.string();\n }\n\n if (!this.definitions[key]) {\n this.definitions[key] = {};\n }\n this.definitions[key].type = type;\n\n if (definition && definition.values) {\n if (Array.isArray(definition.values)) {\n this.definitions[key].values = definition.values;\n }\n }\n return this.definitions[key];\n }\n\n /**\n * Convert string definition to Joi schema\n */\n toJoi(str: string): Joi.Schema {\n let type: Joi.Schema;\n \n if (str.match(/^string|^text/i)) {\n type = Joi.string();\n } else if (str.match(/^number|^integer|^int/i)) {\n type = Joi.number();\n } else if (str.match(/^boolean|^bool/i)) {\n type = Joi.boolean();\n } else if (str.match(/^date/i)) {\n type = Joi.date();\n } else if (str.match(/^edate/i)) {\n type = Joi.custom(joiEdateType);\n } else if (str.match(/^duration/i)) {\n type = Joi.string().isoDuration();\n } else if (str.match(/^array/i)) {\n let elementTypes = \"string\";\n const tmp = str.match(/\\((.*)\\)/);\n if (tmp && tmp[1].match(/string/i)) {\n elementTypes = \"string\";\n } else if (tmp && tmp[1].match(/number|integer|int/i)) {\n elementTypes = \"number\";\n } else if (tmp && tmp[1].match(/boolean|bool/i)) {\n elementTypes = \"boolean\";\n }\n type = Joi.custom(joiStringArrayType(elementTypes));\n } else {\n type = Joi.string();\n }\n\n // Handle default values\n const regexForDefault = /\\bdefault\\s+([^\\s]+)/;\n const matchForDefault = str.match(regexForDefault);\n if (matchForDefault) {\n const defValObj = type.validate(matchForDefault[1]);\n if (defValObj.error) {\n throw new ParamError(`default value \"${defValObj.value}\" type mismatch`);\n }\n type = type.default(defValObj.value);\n } else if (str.match(/required/)) {\n type = type.required();\n }\n\n return type;\n }\n\n /**\n * Validate a value against a definition\n */\n validate(key: string, val: any, def: any): any {\n // Pass current params as context to support cross-parameter references (e.g., @startTime+2h)\n const { value, error } = def.type.validate(val, { context: { params: this.params } });\n if (error) {\n const errs = error.details.map((el: any) => el.message).join(\", \");\n throw new ParamError(`\"${key}\" validation error: ${errs}`);\n }\n return value;\n }\n\n /**\n * Get a parameter value with validation\n */\n get(key: string, definition?: ParamDefinition): any {\n const def = this.assignDefinition(key, definition);\n let valFromGetters: any = undefined;\n \n if (def.volatile || true) {\n valFromGetters = this.runAllRegisteredGetters(key);\n }\n \n const valFromArgs = this.args.get(key);\n const valFromParams = this.params[key];\n\n const res = valFromGetters ? this.validate(key, valFromGetters, def) :\n valFromArgs ? this.validate(key, valFromArgs, def) :\n this.validate(key, valFromParams, def);\n\n if (res !== undefined && def.values && !def.values.includes(res)) {\n throw new ParamError(`key ${key} should be one of ${def.values}`);\n }\n return res;\n }\n\n /**\n * Set a parameter value with validation\n */\n set(key: string, val: any, definition?: ParamDefinition): void {\n // TODO: check if there's a test for this:\n if (val && val.type && val.value) {\n definition = val;\n val = val.value;\n }\n const def = this.assignDefinition(key, definition);\n\n if (!this.runAllRegisteredSetters(key, val)) {\n this.params[key] = val;\n }\n }\n\n /**\n * Get all parameters from definitions\n * Processes parameters left-to-right to support cross-parameter references\n */\n getAll(defs: Record<string, ParamDefinition>): Record<string, any> {\n const res: Record<string, any> = {};\n for (const [k, def] of Object.entries(defs)) {\n const value = this.get(k, def);\n res[k] = value;\n // Store validated value in params so subsequent params can reference it\n if (value !== undefined) {\n this.params[k] = value;\n }\n }\n return res;\n }\n\n /**\n * Run all registered getters for a key\n */\n runAllRegisteredGetters(key: string): any {\n let val: any = null;\n for (const getter of this.paramGetters) {\n val = getter(key, this.definitions[key]);\n if (val !== undefined) {\n break;\n }\n }\n return val;\n }\n\n /**\n * Run all registered setters for a key\n */\n runAllRegisteredSetters(key: string, value: any): boolean {\n let setterUsed: boolean = false;\n for (const setter of this.paramSetters) {\n setterUsed = setter(key, value);\n if (setterUsed) {\n break;\n }\n }\n return setterUsed;\n }\n\n /**\n * Register a parameter getter\n */\n registerParamGetter(fn: ParamGetter): void {\n this.paramGetters.push(fn);\n }\n\n /**\n * Register a parameter setter\n */\n registerParamSetter(fn: ParamSetter): void {\n this.paramSetters.push(fn);\n }\n}\n\n// Singleton instance\nlet paramsInstance: Params | null = null;\n\n/**\n * Initialize the singleton Params instance\n */\nexport const init = (fns: { args: ArgsInstance }, opts: ParamsOptions = {}): Params => {\n paramsInstance = new Params(fns, opts);\n return paramsInstance;\n};\n\n/**\n * Get the current singleton Params instance\n */\nexport const getParamsInstance = (): Params | null => paramsInstance;\n\n// Export custom types for external use\nexport { joiEdateType, joiStringArrayType };\n\n\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\n","import Joi from \"joi\";\nimport { ParamError } from \"../errors.js\";\n\n/**\n * Custom Joi type for enhanced date parsing with relative time support\n * Supports:\n * - ISO8601 strings: \"2025-01-01T01:01:01Z\"\n * - Relative time: \"-2h\", \"+1d\", \"now\"\n * - Cross-parameter references: \"@startTime+2h\", \"@endDate-30m\"\n * \n * Internal representation: UTC ISO8601 string (YYYY-MM-DDTHH:mm:ssZ)\n * \n * @param value - Date value to parse\n * @param helpers - Joi helpers (includes context with other params)\n * @returns ISO8601 string in UTC timezone\n */\nexport const joiEdateType = (value: any, helpers: Joi.CustomHelpers): string => {\n // If value is already a string in ISO format, validate and return\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z$/.test(value)) {\n const testDate = new Date(value);\n if (!isNaN(testDate.getTime())) {\n return value;\n }\n }\n\n // If value is a Date object, convert to ISO string\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n // If value is not a string, try to convert it\n if (typeof value !== \"string\") {\n value = String(value);\n }\n\n // Handle special keyword \"now\"\n if (value.toLowerCase() === \"now\") {\n return new Date().toISOString();\n }\n\n // Check for cross-parameter reference with relative time: @paramName+2h, @paramName-30m\n const referenceRegex = /^@(\\w+)([+-]\\d+[smhdwy])$/i;\n const referenceMatch = value.match(referenceRegex);\n \n if (referenceMatch) {\n const [, paramName, relativeExpr] = referenceMatch;\n \n // Get the referenced parameter from context (if available via helpers.state.ancestors)\n // For now, we'll use helpers.prefs.context which Joi provides\n const context = (helpers as any).prefs?.context;\n \n if (!context || !context.params) {\n throw new ParamError(`Cannot resolve cross-parameter reference @${paramName}: context not available. Ensure parameters are processed with proper context.`);\n }\n\n const referencedValue = context.params[paramName];\n \n if (referencedValue === undefined || referencedValue === null) {\n throw new ParamError(`Cannot resolve @${paramName}: parameter \"${paramName}\" is not defined or has no value. Parameters are evaluated left-to-right.`);\n }\n\n // Referenced value should be an ISO string or Date\n let referenceDate: Date;\n if (referencedValue instanceof Date) {\n referenceDate = referencedValue;\n } else if (typeof referencedValue === \"string\") {\n referenceDate = new Date(referencedValue);\n if (isNaN(referenceDate.getTime())) {\n throw new ParamError(`Referenced parameter @${paramName} has invalid date value: ${referencedValue}`);\n }\n } else {\n throw new ParamError(`Referenced parameter @${paramName} is not a valid date type (found: ${typeof referencedValue})`);\n }\n\n // Parse the relative expression and apply to reference date\n const relativeMatch = relativeExpr.match(/^([+-])(\\d+)([smhdwy])$/i);\n if (!relativeMatch) {\n throw new ParamError(`Invalid relative time expression in @${paramName}${relativeExpr}`);\n }\n\n const [, sign, amount, unit] = relativeMatch;\n const offset = calculateTimeOffset(parseInt(amount, 10), unit, sign);\n const resultDate = new Date(referenceDate.getTime() + offset);\n \n return resultDate.toISOString();\n }\n\n // Check for relative time expressions like \"-2h\", \"+1d\", \"-30m\", etc.\n const relativeTimeRegex = /^([+-])(\\d+)([smhdwy])$/i;\n const relativeMatch = value.match(relativeTimeRegex);\n \n if (relativeMatch) {\n const [, sign, amount, unit] = relativeMatch;\n const numAmount = parseInt(amount, 10);\n \n if (isNaN(numAmount)) {\n throw new ParamError(`Invalid relative time amount: ${amount}`);\n }\n\n const offset = calculateTimeOffset(numAmount, unit, sign);\n const resultDate = new Date(Date.now() + offset);\n \n return resultDate.toISOString();\n }\n\n // Try to parse as a regular date string\n const parsedDate = new Date(value);\n \n // Check if the parsed date is valid\n if (isNaN(parsedDate.getTime())) {\n throw new ParamError(`Invalid date format: ${value}. Expected a valid date string, \"now\", relative time expression (e.g., \"-2h\", \"+1d\"), or cross-parameter reference (e.g., \"@startTime+2h\")`);\n }\n\n return parsedDate.toISOString();\n};\n\n/**\n * Calculate time offset in milliseconds\n */\nfunction calculateTimeOffset(amount: number, unit: string, sign: string): number {\n let multiplier = 1;\n \n // Convert to milliseconds based on unit\n switch (unit.toLowerCase()) {\n case \"s\": // seconds\n multiplier = 1000;\n break;\n case \"m\": // minutes\n multiplier = 60 * 1000;\n break;\n case \"h\": // hours\n multiplier = 60 * 60 * 1000;\n break;\n case \"d\": // days\n multiplier = 24 * 60 * 60 * 1000;\n break;\n case \"w\": // weeks\n multiplier = 7 * 24 * 60 * 60 * 1000;\n break;\n case \"y\": // years (approximate)\n multiplier = 365 * 24 * 60 * 60 * 1000;\n break;\n default:\n throw new ParamError(`Invalid time unit: ${unit}. Supported units: s, m, h, d, w, y`);\n }\n\n return sign === \"+\" ? amount * multiplier : -amount * multiplier;\n}\n\n/**\n * Custom Joi type for string array parsing\n * Converts comma-separated strings to typed arrays\n */\nexport const joiStringArrayType = (type: string) => (value: any, helpers: Joi.CustomHelpers): any[] => {\n if (value === undefined || typeof value === \"function\") {\n return [];\n }\n \n const arr = value.split(/,\\s*/).map((el: string) => {\n if (type === \"number\") {\n const v = parseInt(el, 10);\n if (isNaN(v)) {\n throw new ParamError(`array element \"${el}\" should be numeric`);\n }\n return v;\n } else if (type === \"boolean\") {\n const v = el.match(/true|t|yes|1/i) ? true :\n el.match(/false|f|no|0/i) ? false : null;\n if (v === null) {\n throw new ParamError(`array element \"${el}\" should be boolean`);\n }\n return v;\n } else if (type === \"string\") {\n return el;\n } else {\n throw new ParamError(`unknown type \"${type}\" for array elements`);\n }\n });\n \n return arr;\n};\n\n"],"mappings":"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","names":["relativeMatch","Joi"]}
1
+ {"version":3,"sources":["../src/params.ts","../src/params/index.ts","../src/errors.ts","../src/params/custom-types.ts"],"sourcesContent":["// Re-export everything from the params module directory\nexport * from \"./params/index.js\";\n\n","import Joi from \"joi\";\nimport { ParamError } from \"../errors\";\nimport { joiEdateType, joiStringArrayType } from \"./custom-types\";\n\n/**\n * Parameter definition types\n */\nexport type ParamDefinition = \n | string \n | Joi.Schema \n | { \n type?: string | Joi.Schema; \n values?: any[]; \n [key: string]: any; \n };\n\nexport type ParamGetter = (key: string, definition?: any) => any;\nexport type ParamSetter = (key: string, value: any) => boolean;\n\n/**\n * Args instance interface\n */\nexport interface ArgsInstance {\n get(key: string): any;\n}\n\n/**\n * Params constructor options\n */\nexport interface ParamsOptions {\n [key: string]: any;\n}\n\n/**\n * Params class for parameter validation and type checking\n * Built on top of Args library with Joi validation\n */\nexport class Params {\n private params: Record<string, any> = {};\n private definitions: Record<string, any> = {};\n private args: ArgsInstance;\n private paramSetters: ParamSetter[] = [];\n private paramGetters: ParamGetter[] = [];\n\n constructor({ args }: { args: ArgsInstance }, opts: ParamsOptions = {}) {\n this.args = args;\n\n for (const [k, v] of Object.entries(opts)) {\n // TODO: opt values might be an object with definitions in it, so perhaps `this.set` should be used\n this.params[k] = v;\n }\n }\n\n /**\n * Assign a parameter definition\n */\n assignDefinition(key: string, definition?: ParamDefinition): any {\n if (this.definitions[key] && !definition) {\n return this.definitions[key];\n }\n\n let type: Joi.Schema;\n if (!definition) {\n type = Joi.string();\n } else if (Joi.isSchema(definition)) {\n type = definition;\n } else if (Joi.isSchema(definition.type)) {\n type = definition.type;\n } else if (typeof definition === \"string\") {\n type = this.toJoi(definition);\n } else if (typeof definition.type === \"string\") {\n type = this.toJoi(definition.type);\n } else if (!definition.type) {\n type = Joi.string();\n } else {\n type = Joi.string();\n }\n\n if (!this.definitions[key]) {\n this.definitions[key] = {};\n }\n this.definitions[key].type = type;\n\n if (definition && definition.values) {\n if (Array.isArray(definition.values)) {\n this.definitions[key].values = definition.values;\n }\n }\n return this.definitions[key];\n }\n\n /**\n * Convert string definition to Joi schema\n */\n toJoi(str: string): Joi.Schema {\n let type: Joi.Schema;\n \n if (str.match(/^string|^text/i)) {\n type = Joi.string();\n } else if (str.match(/^number|^integer|^int/i)) {\n type = Joi.number();\n } else if (str.match(/^boolean|^bool/i)) {\n type = Joi.boolean();\n } else if (str.match(/^date/i)) {\n type = Joi.custom(joiEdateType);\n } else if (str.match(/^duration/i)) {\n type = Joi.string().isoDuration();\n } else if (str.match(/^array/i)) {\n let elementTypes = \"string\";\n const tmp = str.match(/\\((.*)\\)/);\n if (tmp && tmp[1].match(/string/i)) {\n elementTypes = \"string\";\n } else if (tmp && tmp[1].match(/number|integer|int/i)) {\n elementTypes = \"number\";\n } else if (tmp && tmp[1].match(/boolean|bool/i)) {\n elementTypes = \"boolean\";\n }\n type = Joi.custom(joiStringArrayType(elementTypes));\n } else {\n type = Joi.string();\n }\n\n // Handle default values\n const regexForDefault = /\\bdefault\\s+([^\\s]+)/;\n const matchForDefault = str.match(regexForDefault);\n if (matchForDefault) {\n const defValObj = type.validate(matchForDefault[1]);\n if (defValObj.error) {\n throw new ParamError(`default value \"${defValObj.value}\" type mismatch`);\n }\n type = type.default(defValObj.value);\n } else if (str.match(/required/)) {\n type = type.required();\n }\n\n return type;\n }\n\n /**\n * Validate a value against a definition\n */\n validate(key: string, val: any, def: any): any {\n // Pass current params as context to support cross-parameter references (e.g., @startTime+2h)\n const { value, error } = def.type.validate(val, { context: { params: this.params } });\n if (error) {\n const errs = error.details.map((el: any) => el.message).join(\", \");\n throw new ParamError(`\"${key}\" validation error: ${errs}`);\n }\n return value;\n }\n\n /**\n * Get a parameter value with validation\n */\n get(key: string, definition?: ParamDefinition): any {\n const def = this.assignDefinition(key, definition);\n let valFromGetters: any = undefined;\n \n if (def.volatile || true) {\n valFromGetters = this.runAllRegisteredGetters(key);\n }\n \n const valFromArgs = this.args.get(key);\n const valFromParams = this.params[key];\n\n const res = valFromGetters ? this.validate(key, valFromGetters, def) :\n valFromArgs ? this.validate(key, valFromArgs, def) :\n this.validate(key, valFromParams, def);\n\n if (res !== undefined && def.values && !def.values.includes(res)) {\n throw new ParamError(`key ${key} should be one of ${def.values}`);\n }\n return res;\n }\n\n /**\n * Set a parameter value with validation\n */\n set(key: string, val: any, definition?: ParamDefinition): void {\n // TODO: check if there's a test for this:\n if (val && val.type && val.value) {\n definition = val;\n val = val.value;\n }\n const def = this.assignDefinition(key, definition);\n\n if (!this.runAllRegisteredSetters(key, val)) {\n this.params[key] = val;\n }\n }\n\n /**\n * Get all parameters from definitions\n * Processes parameters left-to-right to support cross-parameter references\n */\n getAll(defs: Record<string, ParamDefinition>): Record<string, any> {\n const res: Record<string, any> = {};\n for (const [k, def] of Object.entries(defs)) {\n const value = this.get(k, def);\n res[k] = value;\n // Store validated value in params so subsequent params can reference it\n if (value !== undefined) {\n this.params[k] = value;\n }\n }\n return res;\n }\n\n /**\n * Run all registered getters for a key\n */\n runAllRegisteredGetters(key: string): any {\n let val: any = null;\n for (const getter of this.paramGetters) {\n val = getter(key, this.definitions[key]);\n if (val !== undefined) {\n break;\n }\n }\n return val;\n }\n\n /**\n * Run all registered setters for a key\n */\n runAllRegisteredSetters(key: string, value: any): boolean {\n let setterUsed: boolean = false;\n for (const setter of this.paramSetters) {\n setterUsed = setter(key, value);\n if (setterUsed) {\n break;\n }\n }\n return setterUsed;\n }\n\n /**\n * Register a parameter getter\n */\n registerParamGetter(fn: ParamGetter): void {\n this.paramGetters.push(fn);\n }\n\n /**\n * Register a parameter setter\n */\n registerParamSetter(fn: ParamSetter): void {\n this.paramSetters.push(fn);\n }\n}\n\n// Singleton instance\nlet paramsInstance: Params | null = null;\n\n/**\n * Initialize the singleton Params instance\n */\nexport const init = (fns: { args: ArgsInstance }, opts: ParamsOptions = {}): Params => {\n paramsInstance = new Params(fns, opts);\n return paramsInstance;\n};\n\n/**\n * Get the current singleton Params instance\n */\nexport const getParamsInstance = (): Params | null => paramsInstance;\n\n// Export custom types for external use\nexport { joiEdateType, joiStringArrayType };\n\n\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\n","import Joi from \"joi\";\nimport { ParamError } from \"../errors.js\";\n\n/**\n * Custom Joi type for enhanced date parsing with relative time support\n * Supports:\n * - ISO8601 strings: \"2025-01-01T01:01:01Z\"\n * - Relative time: \"-2h\", \"+1d\", \"now\"\n * - Cross-parameter references: \"@startTime+2h\", \"@endDate-30m\"\n * \n * Internal representation: UTC ISO8601 string (YYYY-MM-DDTHH:mm:ssZ)\n * \n * @param value - Date value to parse\n * @param helpers - Joi helpers (includes context with other params)\n * @returns ISO8601 string in UTC timezone\n */\nexport const joiEdateType = (value: any, helpers: Joi.CustomHelpers): string => {\n // If value is already a string in ISO format, validate and return\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z$/.test(value)) {\n const testDate = new Date(value);\n if (!isNaN(testDate.getTime())) {\n return value;\n }\n }\n\n // If value is a Date object, convert to ISO string\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n // If value is not a string, try to convert it\n if (typeof value !== \"string\") {\n value = String(value);\n }\n\n // Handle special keyword \"now\"\n if (value.toLowerCase() === \"now\") {\n return new Date().toISOString();\n }\n\n // Check for cross-parameter reference with relative time: @paramName+2h, @paramName-30m\n const referenceRegex = /^@(\\w+)([+-]\\d+[smhdwy])$/i;\n const referenceMatch = value.match(referenceRegex);\n \n if (referenceMatch) {\n const [, paramName, relativeExpr] = referenceMatch;\n \n // Get the referenced parameter from context (if available via helpers.state.ancestors)\n // For now, we'll use helpers.prefs.context which Joi provides\n const context = (helpers as any).prefs?.context;\n \n if (!context || !context.params) {\n throw new ParamError(`Cannot resolve cross-parameter reference @${paramName}: context not available. Ensure parameters are processed with proper context.`);\n }\n\n const referencedValue = context.params[paramName];\n \n if (referencedValue === undefined || referencedValue === null) {\n throw new ParamError(`Cannot resolve @${paramName}: parameter \"${paramName}\" is not defined or has no value. Parameters are evaluated left-to-right.`);\n }\n\n // Referenced value should be an ISO string or Date\n let referenceDate: Date;\n if (referencedValue instanceof Date) {\n referenceDate = referencedValue;\n } else if (typeof referencedValue === \"string\") {\n referenceDate = new Date(referencedValue);\n if (isNaN(referenceDate.getTime())) {\n throw new ParamError(`Referenced parameter @${paramName} has invalid date value: ${referencedValue}`);\n }\n } else {\n throw new ParamError(`Referenced parameter @${paramName} is not a valid date type (found: ${typeof referencedValue})`);\n }\n\n // Parse the relative expression and apply to reference date\n const relativeMatch = relativeExpr.match(/^([+-])(\\d+)([smhdwy])$/i);\n if (!relativeMatch) {\n throw new ParamError(`Invalid relative time expression in @${paramName}${relativeExpr}`);\n }\n\n const [, sign, amount, unit] = relativeMatch;\n const offset = calculateTimeOffset(parseInt(amount, 10), unit, sign);\n const resultDate = new Date(referenceDate.getTime() + offset);\n \n return resultDate.toISOString();\n }\n\n // Check for relative time expressions like \"-2h\", \"+1d\", \"-30m\", etc.\n const relativeTimeRegex = /^([+-])(\\d+)([smhdwy])$/i;\n const relativeMatch = value.match(relativeTimeRegex);\n \n if (relativeMatch) {\n const [, sign, amount, unit] = relativeMatch;\n const numAmount = parseInt(amount, 10);\n \n if (isNaN(numAmount)) {\n throw new ParamError(`Invalid relative time amount: ${amount}`);\n }\n\n const offset = calculateTimeOffset(numAmount, unit, sign);\n const resultDate = new Date(Date.now() + offset);\n \n return resultDate.toISOString();\n }\n\n // Try to parse as a regular date string\n const parsedDate = new Date(value);\n \n // Check if the parsed date is valid\n if (isNaN(parsedDate.getTime())) {\n throw new ParamError(`Invalid date format: ${value}. Expected a valid date string, \"now\", relative time expression (e.g., \"-2h\", \"+1d\"), or cross-parameter reference (e.g., \"@startTime+2h\")`);\n }\n\n return parsedDate.toISOString();\n};\n\n/**\n * Calculate time offset in milliseconds\n */\nfunction calculateTimeOffset(amount: number, unit: string, sign: string): number {\n let multiplier = 1;\n \n // Convert to milliseconds based on unit\n switch (unit.toLowerCase()) {\n case \"s\": // seconds\n multiplier = 1000;\n break;\n case \"m\": // minutes\n multiplier = 60 * 1000;\n break;\n case \"h\": // hours\n multiplier = 60 * 60 * 1000;\n break;\n case \"d\": // days\n multiplier = 24 * 60 * 60 * 1000;\n break;\n case \"w\": // weeks\n multiplier = 7 * 24 * 60 * 60 * 1000;\n break;\n case \"y\": // years (approximate)\n multiplier = 365 * 24 * 60 * 60 * 1000;\n break;\n default:\n throw new ParamError(`Invalid time unit: ${unit}. Supported units: s, m, h, d, w, y`);\n }\n\n return sign === \"+\" ? amount * multiplier : -amount * multiplier;\n}\n\n/**\n * Custom Joi type for string array parsing\n * Converts comma-separated strings to typed arrays\n */\nexport const joiStringArrayType = (type: string) => (value: any, helpers: Joi.CustomHelpers): any[] => {\n if (value === undefined || typeof value === \"function\") {\n return [];\n }\n \n const arr = value.split(/,\\s*/).map((el: string) => {\n if (type === \"number\") {\n const v = parseInt(el, 10);\n if (isNaN(v)) {\n throw new ParamError(`array element \"${el}\" should be numeric`);\n }\n return v;\n } else if (type === \"boolean\") {\n const v = el.match(/true|t|yes|1/i) ? true :\n el.match(/false|f|no|0/i) ? false : null;\n if (v === null) {\n throw new ParamError(`array element \"${el}\" should be boolean`);\n }\n return v;\n } else if (type === \"string\") {\n return el;\n } else {\n throw new ParamError(`unknown type \"${type}\" for array elements`);\n }\n });\n \n return arr;\n};\n\n"],"mappings":"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","names":["relativeMatch","Joi"]}
package/dist/params.js CHANGED
@@ -193,8 +193,6 @@ var Params = class {
193
193
  } else if (str.match(/^boolean|^bool/i)) {
194
194
  type = Joi.boolean();
195
195
  } else if (str.match(/^date/i)) {
196
- type = Joi.date();
197
- } else if (str.match(/^edate/i)) {
198
196
  type = Joi.custom(joiEdateType);
199
197
  } else if (str.match(/^duration/i)) {
200
198
  type = Joi.string().isoDuration();
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/params/index.ts","../src/errors.ts","../src/params/custom-types.ts"],"sourcesContent":["import Joi from \"joi\";\nimport { ParamError } from \"../errors\";\nimport { joiEdateType, joiStringArrayType } from \"./custom-types\";\n\n/**\n * Parameter definition types\n */\nexport type ParamDefinition = \n | string \n | Joi.Schema \n | { \n type?: string | Joi.Schema; \n values?: any[]; \n [key: string]: any; \n };\n\nexport type ParamGetter = (key: string, definition?: any) => any;\nexport type ParamSetter = (key: string, value: any) => boolean;\n\n/**\n * Args instance interface\n */\nexport interface ArgsInstance {\n get(key: string): any;\n}\n\n/**\n * Params constructor options\n */\nexport interface ParamsOptions {\n [key: string]: any;\n}\n\n/**\n * Params class for parameter validation and type checking\n * Built on top of Args library with Joi validation\n */\nexport class Params {\n private params: Record<string, any> = {};\n private definitions: Record<string, any> = {};\n private args: ArgsInstance;\n private paramSetters: ParamSetter[] = [];\n private paramGetters: ParamGetter[] = [];\n\n constructor({ args }: { args: ArgsInstance }, opts: ParamsOptions = {}) {\n this.args = args;\n\n for (const [k, v] of Object.entries(opts)) {\n // TODO: opt values might be an object with definitions in it, so perhaps `this.set` should be used\n this.params[k] = v;\n }\n }\n\n /**\n * Assign a parameter definition\n */\n assignDefinition(key: string, definition?: ParamDefinition): any {\n if (this.definitions[key] && !definition) {\n return this.definitions[key];\n }\n\n let type: Joi.Schema;\n if (!definition) {\n type = Joi.string();\n } else if (Joi.isSchema(definition)) {\n type = definition;\n } else if (Joi.isSchema(definition.type)) {\n type = definition.type;\n } else if (typeof definition === \"string\") {\n type = this.toJoi(definition);\n } else if (typeof definition.type === \"string\") {\n type = this.toJoi(definition.type);\n } else if (!definition.type) {\n type = Joi.string();\n } else {\n type = Joi.string();\n }\n\n if (!this.definitions[key]) {\n this.definitions[key] = {};\n }\n this.definitions[key].type = type;\n\n if (definition && definition.values) {\n if (Array.isArray(definition.values)) {\n this.definitions[key].values = definition.values;\n }\n }\n return this.definitions[key];\n }\n\n /**\n * Convert string definition to Joi schema\n */\n toJoi(str: string): Joi.Schema {\n let type: Joi.Schema;\n \n if (str.match(/^string|^text/i)) {\n type = Joi.string();\n } else if (str.match(/^number|^integer|^int/i)) {\n type = Joi.number();\n } else if (str.match(/^boolean|^bool/i)) {\n type = Joi.boolean();\n } else if (str.match(/^date/i)) {\n type = Joi.date();\n } else if (str.match(/^edate/i)) {\n type = Joi.custom(joiEdateType);\n } else if (str.match(/^duration/i)) {\n type = Joi.string().isoDuration();\n } else if (str.match(/^array/i)) {\n let elementTypes = \"string\";\n const tmp = str.match(/\\((.*)\\)/);\n if (tmp && tmp[1].match(/string/i)) {\n elementTypes = \"string\";\n } else if (tmp && tmp[1].match(/number|integer|int/i)) {\n elementTypes = \"number\";\n } else if (tmp && tmp[1].match(/boolean|bool/i)) {\n elementTypes = \"boolean\";\n }\n type = Joi.custom(joiStringArrayType(elementTypes));\n } else {\n type = Joi.string();\n }\n\n // Handle default values\n const regexForDefault = /\\bdefault\\s+([^\\s]+)/;\n const matchForDefault = str.match(regexForDefault);\n if (matchForDefault) {\n const defValObj = type.validate(matchForDefault[1]);\n if (defValObj.error) {\n throw new ParamError(`default value \"${defValObj.value}\" type mismatch`);\n }\n type = type.default(defValObj.value);\n } else if (str.match(/required/)) {\n type = type.required();\n }\n\n return type;\n }\n\n /**\n * Validate a value against a definition\n */\n validate(key: string, val: any, def: any): any {\n // Pass current params as context to support cross-parameter references (e.g., @startTime+2h)\n const { value, error } = def.type.validate(val, { context: { params: this.params } });\n if (error) {\n const errs = error.details.map((el: any) => el.message).join(\", \");\n throw new ParamError(`\"${key}\" validation error: ${errs}`);\n }\n return value;\n }\n\n /**\n * Get a parameter value with validation\n */\n get(key: string, definition?: ParamDefinition): any {\n const def = this.assignDefinition(key, definition);\n let valFromGetters: any = undefined;\n \n if (def.volatile || true) {\n valFromGetters = this.runAllRegisteredGetters(key);\n }\n \n const valFromArgs = this.args.get(key);\n const valFromParams = this.params[key];\n\n const res = valFromGetters ? this.validate(key, valFromGetters, def) :\n valFromArgs ? this.validate(key, valFromArgs, def) :\n this.validate(key, valFromParams, def);\n\n if (res !== undefined && def.values && !def.values.includes(res)) {\n throw new ParamError(`key ${key} should be one of ${def.values}`);\n }\n return res;\n }\n\n /**\n * Set a parameter value with validation\n */\n set(key: string, val: any, definition?: ParamDefinition): void {\n // TODO: check if there's a test for this:\n if (val && val.type && val.value) {\n definition = val;\n val = val.value;\n }\n const def = this.assignDefinition(key, definition);\n\n if (!this.runAllRegisteredSetters(key, val)) {\n this.params[key] = val;\n }\n }\n\n /**\n * Get all parameters from definitions\n * Processes parameters left-to-right to support cross-parameter references\n */\n getAll(defs: Record<string, ParamDefinition>): Record<string, any> {\n const res: Record<string, any> = {};\n for (const [k, def] of Object.entries(defs)) {\n const value = this.get(k, def);\n res[k] = value;\n // Store validated value in params so subsequent params can reference it\n if (value !== undefined) {\n this.params[k] = value;\n }\n }\n return res;\n }\n\n /**\n * Run all registered getters for a key\n */\n runAllRegisteredGetters(key: string): any {\n let val: any = null;\n for (const getter of this.paramGetters) {\n val = getter(key, this.definitions[key]);\n if (val !== undefined) {\n break;\n }\n }\n return val;\n }\n\n /**\n * Run all registered setters for a key\n */\n runAllRegisteredSetters(key: string, value: any): boolean {\n let setterUsed: boolean = false;\n for (const setter of this.paramSetters) {\n setterUsed = setter(key, value);\n if (setterUsed) {\n break;\n }\n }\n return setterUsed;\n }\n\n /**\n * Register a parameter getter\n */\n registerParamGetter(fn: ParamGetter): void {\n this.paramGetters.push(fn);\n }\n\n /**\n * Register a parameter setter\n */\n registerParamSetter(fn: ParamSetter): void {\n this.paramSetters.push(fn);\n }\n}\n\n// Singleton instance\nlet paramsInstance: Params | null = null;\n\n/**\n * Initialize the singleton Params instance\n */\nexport const init = (fns: { args: ArgsInstance }, opts: ParamsOptions = {}): Params => {\n paramsInstance = new Params(fns, opts);\n return paramsInstance;\n};\n\n/**\n * Get the current singleton Params instance\n */\nexport const getParamsInstance = (): Params | null => paramsInstance;\n\n// Export custom types for external use\nexport { joiEdateType, joiStringArrayType };\n\n\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\n","import Joi from \"joi\";\nimport { ParamError } from \"../errors.js\";\n\n/**\n * Custom Joi type for enhanced date parsing with relative time support\n * Supports:\n * - ISO8601 strings: \"2025-01-01T01:01:01Z\"\n * - Relative time: \"-2h\", \"+1d\", \"now\"\n * - Cross-parameter references: \"@startTime+2h\", \"@endDate-30m\"\n * \n * Internal representation: UTC ISO8601 string (YYYY-MM-DDTHH:mm:ssZ)\n * \n * @param value - Date value to parse\n * @param helpers - Joi helpers (includes context with other params)\n * @returns ISO8601 string in UTC timezone\n */\nexport const joiEdateType = (value: any, helpers: Joi.CustomHelpers): string => {\n // If value is already a string in ISO format, validate and return\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z$/.test(value)) {\n const testDate = new Date(value);\n if (!isNaN(testDate.getTime())) {\n return value;\n }\n }\n\n // If value is a Date object, convert to ISO string\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n // If value is not a string, try to convert it\n if (typeof value !== \"string\") {\n value = String(value);\n }\n\n // Handle special keyword \"now\"\n if (value.toLowerCase() === \"now\") {\n return new Date().toISOString();\n }\n\n // Check for cross-parameter reference with relative time: @paramName+2h, @paramName-30m\n const referenceRegex = /^@(\\w+)([+-]\\d+[smhdwy])$/i;\n const referenceMatch = value.match(referenceRegex);\n \n if (referenceMatch) {\n const [, paramName, relativeExpr] = referenceMatch;\n \n // Get the referenced parameter from context (if available via helpers.state.ancestors)\n // For now, we'll use helpers.prefs.context which Joi provides\n const context = (helpers as any).prefs?.context;\n \n if (!context || !context.params) {\n throw new ParamError(`Cannot resolve cross-parameter reference @${paramName}: context not available. Ensure parameters are processed with proper context.`);\n }\n\n const referencedValue = context.params[paramName];\n \n if (referencedValue === undefined || referencedValue === null) {\n throw new ParamError(`Cannot resolve @${paramName}: parameter \"${paramName}\" is not defined or has no value. Parameters are evaluated left-to-right.`);\n }\n\n // Referenced value should be an ISO string or Date\n let referenceDate: Date;\n if (referencedValue instanceof Date) {\n referenceDate = referencedValue;\n } else if (typeof referencedValue === \"string\") {\n referenceDate = new Date(referencedValue);\n if (isNaN(referenceDate.getTime())) {\n throw new ParamError(`Referenced parameter @${paramName} has invalid date value: ${referencedValue}`);\n }\n } else {\n throw new ParamError(`Referenced parameter @${paramName} is not a valid date type (found: ${typeof referencedValue})`);\n }\n\n // Parse the relative expression and apply to reference date\n const relativeMatch = relativeExpr.match(/^([+-])(\\d+)([smhdwy])$/i);\n if (!relativeMatch) {\n throw new ParamError(`Invalid relative time expression in @${paramName}${relativeExpr}`);\n }\n\n const [, sign, amount, unit] = relativeMatch;\n const offset = calculateTimeOffset(parseInt(amount, 10), unit, sign);\n const resultDate = new Date(referenceDate.getTime() + offset);\n \n return resultDate.toISOString();\n }\n\n // Check for relative time expressions like \"-2h\", \"+1d\", \"-30m\", etc.\n const relativeTimeRegex = /^([+-])(\\d+)([smhdwy])$/i;\n const relativeMatch = value.match(relativeTimeRegex);\n \n if (relativeMatch) {\n const [, sign, amount, unit] = relativeMatch;\n const numAmount = parseInt(amount, 10);\n \n if (isNaN(numAmount)) {\n throw new ParamError(`Invalid relative time amount: ${amount}`);\n }\n\n const offset = calculateTimeOffset(numAmount, unit, sign);\n const resultDate = new Date(Date.now() + offset);\n \n return resultDate.toISOString();\n }\n\n // Try to parse as a regular date string\n const parsedDate = new Date(value);\n \n // Check if the parsed date is valid\n if (isNaN(parsedDate.getTime())) {\n throw new ParamError(`Invalid date format: ${value}. Expected a valid date string, \"now\", relative time expression (e.g., \"-2h\", \"+1d\"), or cross-parameter reference (e.g., \"@startTime+2h\")`);\n }\n\n return parsedDate.toISOString();\n};\n\n/**\n * Calculate time offset in milliseconds\n */\nfunction calculateTimeOffset(amount: number, unit: string, sign: string): number {\n let multiplier = 1;\n \n // Convert to milliseconds based on unit\n switch (unit.toLowerCase()) {\n case \"s\": // seconds\n multiplier = 1000;\n break;\n case \"m\": // minutes\n multiplier = 60 * 1000;\n break;\n case \"h\": // hours\n multiplier = 60 * 60 * 1000;\n break;\n case \"d\": // days\n multiplier = 24 * 60 * 60 * 1000;\n break;\n case \"w\": // weeks\n multiplier = 7 * 24 * 60 * 60 * 1000;\n break;\n case \"y\": // years (approximate)\n multiplier = 365 * 24 * 60 * 60 * 1000;\n break;\n default:\n throw new ParamError(`Invalid time unit: ${unit}. Supported units: s, m, h, d, w, y`);\n }\n\n return sign === \"+\" ? amount * multiplier : -amount * multiplier;\n}\n\n/**\n * Custom Joi type for string array parsing\n * Converts comma-separated strings to typed arrays\n */\nexport const joiStringArrayType = (type: string) => (value: any, helpers: Joi.CustomHelpers): any[] => {\n if (value === undefined || typeof value === \"function\") {\n return [];\n }\n \n const arr = value.split(/,\\s*/).map((el: string) => {\n if (type === \"number\") {\n const v = parseInt(el, 10);\n if (isNaN(v)) {\n throw new ParamError(`array element \"${el}\" should be numeric`);\n }\n return v;\n } else if (type === \"boolean\") {\n const v = el.match(/true|t|yes|1/i) ? true :\n el.match(/false|f|no|0/i) ? false : null;\n if (v === null) {\n throw new ParamError(`array element \"${el}\" should be boolean`);\n }\n return v;\n } else if (type === \"string\") {\n return el;\n } else {\n throw new ParamError(`unknown type \"${type}\" for array elements`);\n }\n });\n \n return arr;\n};\n\n"],"mappings":"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","names":["relativeMatch"]}
1
+ {"version":3,"sources":["../src/params/index.ts","../src/errors.ts","../src/params/custom-types.ts"],"sourcesContent":["import Joi from \"joi\";\nimport { ParamError } from \"../errors\";\nimport { joiEdateType, joiStringArrayType } from \"./custom-types\";\n\n/**\n * Parameter definition types\n */\nexport type ParamDefinition = \n | string \n | Joi.Schema \n | { \n type?: string | Joi.Schema; \n values?: any[]; \n [key: string]: any; \n };\n\nexport type ParamGetter = (key: string, definition?: any) => any;\nexport type ParamSetter = (key: string, value: any) => boolean;\n\n/**\n * Args instance interface\n */\nexport interface ArgsInstance {\n get(key: string): any;\n}\n\n/**\n * Params constructor options\n */\nexport interface ParamsOptions {\n [key: string]: any;\n}\n\n/**\n * Params class for parameter validation and type checking\n * Built on top of Args library with Joi validation\n */\nexport class Params {\n private params: Record<string, any> = {};\n private definitions: Record<string, any> = {};\n private args: ArgsInstance;\n private paramSetters: ParamSetter[] = [];\n private paramGetters: ParamGetter[] = [];\n\n constructor({ args }: { args: ArgsInstance }, opts: ParamsOptions = {}) {\n this.args = args;\n\n for (const [k, v] of Object.entries(opts)) {\n // TODO: opt values might be an object with definitions in it, so perhaps `this.set` should be used\n this.params[k] = v;\n }\n }\n\n /**\n * Assign a parameter definition\n */\n assignDefinition(key: string, definition?: ParamDefinition): any {\n if (this.definitions[key] && !definition) {\n return this.definitions[key];\n }\n\n let type: Joi.Schema;\n if (!definition) {\n type = Joi.string();\n } else if (Joi.isSchema(definition)) {\n type = definition;\n } else if (Joi.isSchema(definition.type)) {\n type = definition.type;\n } else if (typeof definition === \"string\") {\n type = this.toJoi(definition);\n } else if (typeof definition.type === \"string\") {\n type = this.toJoi(definition.type);\n } else if (!definition.type) {\n type = Joi.string();\n } else {\n type = Joi.string();\n }\n\n if (!this.definitions[key]) {\n this.definitions[key] = {};\n }\n this.definitions[key].type = type;\n\n if (definition && definition.values) {\n if (Array.isArray(definition.values)) {\n this.definitions[key].values = definition.values;\n }\n }\n return this.definitions[key];\n }\n\n /**\n * Convert string definition to Joi schema\n */\n toJoi(str: string): Joi.Schema {\n let type: Joi.Schema;\n \n if (str.match(/^string|^text/i)) {\n type = Joi.string();\n } else if (str.match(/^number|^integer|^int/i)) {\n type = Joi.number();\n } else if (str.match(/^boolean|^bool/i)) {\n type = Joi.boolean();\n } else if (str.match(/^date/i)) {\n type = Joi.custom(joiEdateType);\n } else if (str.match(/^duration/i)) {\n type = Joi.string().isoDuration();\n } else if (str.match(/^array/i)) {\n let elementTypes = \"string\";\n const tmp = str.match(/\\((.*)\\)/);\n if (tmp && tmp[1].match(/string/i)) {\n elementTypes = \"string\";\n } else if (tmp && tmp[1].match(/number|integer|int/i)) {\n elementTypes = \"number\";\n } else if (tmp && tmp[1].match(/boolean|bool/i)) {\n elementTypes = \"boolean\";\n }\n type = Joi.custom(joiStringArrayType(elementTypes));\n } else {\n type = Joi.string();\n }\n\n // Handle default values\n const regexForDefault = /\\bdefault\\s+([^\\s]+)/;\n const matchForDefault = str.match(regexForDefault);\n if (matchForDefault) {\n const defValObj = type.validate(matchForDefault[1]);\n if (defValObj.error) {\n throw new ParamError(`default value \"${defValObj.value}\" type mismatch`);\n }\n type = type.default(defValObj.value);\n } else if (str.match(/required/)) {\n type = type.required();\n }\n\n return type;\n }\n\n /**\n * Validate a value against a definition\n */\n validate(key: string, val: any, def: any): any {\n // Pass current params as context to support cross-parameter references (e.g., @startTime+2h)\n const { value, error } = def.type.validate(val, { context: { params: this.params } });\n if (error) {\n const errs = error.details.map((el: any) => el.message).join(\", \");\n throw new ParamError(`\"${key}\" validation error: ${errs}`);\n }\n return value;\n }\n\n /**\n * Get a parameter value with validation\n */\n get(key: string, definition?: ParamDefinition): any {\n const def = this.assignDefinition(key, definition);\n let valFromGetters: any = undefined;\n \n if (def.volatile || true) {\n valFromGetters = this.runAllRegisteredGetters(key);\n }\n \n const valFromArgs = this.args.get(key);\n const valFromParams = this.params[key];\n\n const res = valFromGetters ? this.validate(key, valFromGetters, def) :\n valFromArgs ? this.validate(key, valFromArgs, def) :\n this.validate(key, valFromParams, def);\n\n if (res !== undefined && def.values && !def.values.includes(res)) {\n throw new ParamError(`key ${key} should be one of ${def.values}`);\n }\n return res;\n }\n\n /**\n * Set a parameter value with validation\n */\n set(key: string, val: any, definition?: ParamDefinition): void {\n // TODO: check if there's a test for this:\n if (val && val.type && val.value) {\n definition = val;\n val = val.value;\n }\n const def = this.assignDefinition(key, definition);\n\n if (!this.runAllRegisteredSetters(key, val)) {\n this.params[key] = val;\n }\n }\n\n /**\n * Get all parameters from definitions\n * Processes parameters left-to-right to support cross-parameter references\n */\n getAll(defs: Record<string, ParamDefinition>): Record<string, any> {\n const res: Record<string, any> = {};\n for (const [k, def] of Object.entries(defs)) {\n const value = this.get(k, def);\n res[k] = value;\n // Store validated value in params so subsequent params can reference it\n if (value !== undefined) {\n this.params[k] = value;\n }\n }\n return res;\n }\n\n /**\n * Run all registered getters for a key\n */\n runAllRegisteredGetters(key: string): any {\n let val: any = null;\n for (const getter of this.paramGetters) {\n val = getter(key, this.definitions[key]);\n if (val !== undefined) {\n break;\n }\n }\n return val;\n }\n\n /**\n * Run all registered setters for a key\n */\n runAllRegisteredSetters(key: string, value: any): boolean {\n let setterUsed: boolean = false;\n for (const setter of this.paramSetters) {\n setterUsed = setter(key, value);\n if (setterUsed) {\n break;\n }\n }\n return setterUsed;\n }\n\n /**\n * Register a parameter getter\n */\n registerParamGetter(fn: ParamGetter): void {\n this.paramGetters.push(fn);\n }\n\n /**\n * Register a parameter setter\n */\n registerParamSetter(fn: ParamSetter): void {\n this.paramSetters.push(fn);\n }\n}\n\n// Singleton instance\nlet paramsInstance: Params | null = null;\n\n/**\n * Initialize the singleton Params instance\n */\nexport const init = (fns: { args: ArgsInstance }, opts: ParamsOptions = {}): Params => {\n paramsInstance = new Params(fns, opts);\n return paramsInstance;\n};\n\n/**\n * Get the current singleton Params instance\n */\nexport const getParamsInstance = (): Params | null => paramsInstance;\n\n// Export custom types for external use\nexport { joiEdateType, joiStringArrayType };\n\n\n","/**\n * Error classes for the CLI toolkit\n */\n\nexport class FrameworkError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"FrameworkError\";\n }\n}\n\nexport class ParamError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"ParamError\";\n }\n}\n\nexport class InitError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"InitError\";\n }\n}\n\nexport class CriticalRequestError extends FrameworkError {\n constructor(message: string) {\n super(message);\n this.name = \"CriticalRequestError\";\n }\n}\n\nexport class ControlFlowError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"ControlFlowError\";\n }\n}\n\n","import Joi from \"joi\";\nimport { ParamError } from \"../errors.js\";\n\n/**\n * Custom Joi type for enhanced date parsing with relative time support\n * Supports:\n * - ISO8601 strings: \"2025-01-01T01:01:01Z\"\n * - Relative time: \"-2h\", \"+1d\", \"now\"\n * - Cross-parameter references: \"@startTime+2h\", \"@endDate-30m\"\n * \n * Internal representation: UTC ISO8601 string (YYYY-MM-DDTHH:mm:ssZ)\n * \n * @param value - Date value to parse\n * @param helpers - Joi helpers (includes context with other params)\n * @returns ISO8601 string in UTC timezone\n */\nexport const joiEdateType = (value: any, helpers: Joi.CustomHelpers): string => {\n // If value is already a string in ISO format, validate and return\n if (typeof value === \"string\" && /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z$/.test(value)) {\n const testDate = new Date(value);\n if (!isNaN(testDate.getTime())) {\n return value;\n }\n }\n\n // If value is a Date object, convert to ISO string\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n // If value is not a string, try to convert it\n if (typeof value !== \"string\") {\n value = String(value);\n }\n\n // Handle special keyword \"now\"\n if (value.toLowerCase() === \"now\") {\n return new Date().toISOString();\n }\n\n // Check for cross-parameter reference with relative time: @paramName+2h, @paramName-30m\n const referenceRegex = /^@(\\w+)([+-]\\d+[smhdwy])$/i;\n const referenceMatch = value.match(referenceRegex);\n \n if (referenceMatch) {\n const [, paramName, relativeExpr] = referenceMatch;\n \n // Get the referenced parameter from context (if available via helpers.state.ancestors)\n // For now, we'll use helpers.prefs.context which Joi provides\n const context = (helpers as any).prefs?.context;\n \n if (!context || !context.params) {\n throw new ParamError(`Cannot resolve cross-parameter reference @${paramName}: context not available. Ensure parameters are processed with proper context.`);\n }\n\n const referencedValue = context.params[paramName];\n \n if (referencedValue === undefined || referencedValue === null) {\n throw new ParamError(`Cannot resolve @${paramName}: parameter \"${paramName}\" is not defined or has no value. Parameters are evaluated left-to-right.`);\n }\n\n // Referenced value should be an ISO string or Date\n let referenceDate: Date;\n if (referencedValue instanceof Date) {\n referenceDate = referencedValue;\n } else if (typeof referencedValue === \"string\") {\n referenceDate = new Date(referencedValue);\n if (isNaN(referenceDate.getTime())) {\n throw new ParamError(`Referenced parameter @${paramName} has invalid date value: ${referencedValue}`);\n }\n } else {\n throw new ParamError(`Referenced parameter @${paramName} is not a valid date type (found: ${typeof referencedValue})`);\n }\n\n // Parse the relative expression and apply to reference date\n const relativeMatch = relativeExpr.match(/^([+-])(\\d+)([smhdwy])$/i);\n if (!relativeMatch) {\n throw new ParamError(`Invalid relative time expression in @${paramName}${relativeExpr}`);\n }\n\n const [, sign, amount, unit] = relativeMatch;\n const offset = calculateTimeOffset(parseInt(amount, 10), unit, sign);\n const resultDate = new Date(referenceDate.getTime() + offset);\n \n return resultDate.toISOString();\n }\n\n // Check for relative time expressions like \"-2h\", \"+1d\", \"-30m\", etc.\n const relativeTimeRegex = /^([+-])(\\d+)([smhdwy])$/i;\n const relativeMatch = value.match(relativeTimeRegex);\n \n if (relativeMatch) {\n const [, sign, amount, unit] = relativeMatch;\n const numAmount = parseInt(amount, 10);\n \n if (isNaN(numAmount)) {\n throw new ParamError(`Invalid relative time amount: ${amount}`);\n }\n\n const offset = calculateTimeOffset(numAmount, unit, sign);\n const resultDate = new Date(Date.now() + offset);\n \n return resultDate.toISOString();\n }\n\n // Try to parse as a regular date string\n const parsedDate = new Date(value);\n \n // Check if the parsed date is valid\n if (isNaN(parsedDate.getTime())) {\n throw new ParamError(`Invalid date format: ${value}. Expected a valid date string, \"now\", relative time expression (e.g., \"-2h\", \"+1d\"), or cross-parameter reference (e.g., \"@startTime+2h\")`);\n }\n\n return parsedDate.toISOString();\n};\n\n/**\n * Calculate time offset in milliseconds\n */\nfunction calculateTimeOffset(amount: number, unit: string, sign: string): number {\n let multiplier = 1;\n \n // Convert to milliseconds based on unit\n switch (unit.toLowerCase()) {\n case \"s\": // seconds\n multiplier = 1000;\n break;\n case \"m\": // minutes\n multiplier = 60 * 1000;\n break;\n case \"h\": // hours\n multiplier = 60 * 60 * 1000;\n break;\n case \"d\": // days\n multiplier = 24 * 60 * 60 * 1000;\n break;\n case \"w\": // weeks\n multiplier = 7 * 24 * 60 * 60 * 1000;\n break;\n case \"y\": // years (approximate)\n multiplier = 365 * 24 * 60 * 60 * 1000;\n break;\n default:\n throw new ParamError(`Invalid time unit: ${unit}. Supported units: s, m, h, d, w, y`);\n }\n\n return sign === \"+\" ? amount * multiplier : -amount * multiplier;\n}\n\n/**\n * Custom Joi type for string array parsing\n * Converts comma-separated strings to typed arrays\n */\nexport const joiStringArrayType = (type: string) => (value: any, helpers: Joi.CustomHelpers): any[] => {\n if (value === undefined || typeof value === \"function\") {\n return [];\n }\n \n const arr = value.split(/,\\s*/).map((el: string) => {\n if (type === \"number\") {\n const v = parseInt(el, 10);\n if (isNaN(v)) {\n throw new ParamError(`array element \"${el}\" should be numeric`);\n }\n return v;\n } else if (type === \"boolean\") {\n const v = el.match(/true|t|yes|1/i) ? true :\n el.match(/false|f|no|0/i) ? false : null;\n if (v === null) {\n throw new ParamError(`array element \"${el}\" should be boolean`);\n }\n return v;\n } else if (type === \"string\") {\n return el;\n } else {\n throw new ParamError(`unknown type \"${type}\" for array elements`);\n }\n });\n \n return arr;\n};\n\n"],"mappings":"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","names":["relativeMatch"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nmakarov/cli-toolkit",
3
- "version": "0.1.4",
3
+ "version": "0.3.0",
4
4
  "description": "A comprehensive toolkit for building CLI applications",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -27,6 +27,16 @@
27
27
  "import": "./dist/init.js",
28
28
  "require": "./dist/init.cjs"
29
29
  },
30
+ "./http-client": {
31
+ "types": "./dist/http-client.d.ts",
32
+ "import": "./dist/http-client.js",
33
+ "require": "./dist/http-client.cjs"
34
+ },
35
+ "./mock-server": {
36
+ "types": "./dist/mock-server.d.ts",
37
+ "import": "./dist/mock-server.js",
38
+ "require": "./dist/mock-server.cjs"
39
+ },
30
40
  "./screen": {
31
41
  "types": "./dist/screen.d.ts",
32
42
  "import": "./dist/screen.js",
@@ -41,6 +51,11 @@
41
51
  "types": "./dist/logger.d.ts",
42
52
  "import": "./dist/logger.js",
43
53
  "require": "./dist/logger.cjs"
54
+ },
55
+ "./filedatabase": {
56
+ "types": "./dist/filedatabase.d.ts",
57
+ "import": "./dist/filedatabase.js",
58
+ "require": "./dist/filedatabase.cjs"
44
59
  }
45
60
  },
46
61
  "scripts": {
@@ -50,7 +65,7 @@
50
65
  "test:watch": "vitest --watch",
51
66
  "test:coverage": "vitest --coverage",
52
67
  "test:coverage:open": "vitest --coverage && open coverage/index.html",
53
- "test:ci": "vitest run src/args/tests/args.ci.test.ts src/params/tests/params.ci.test.ts src/errors/tests/errors.ci.test.ts src/screen/tests/screen.ci.test.ts src/logger/tests/logger.ci.test.ts --coverage",
68
+ "test:ci": "vitest run src/args/tests/args.ci.test.ts src/params/tests/params.ci.test.ts src/errors/tests/errors.ci.test.ts src/screen/tests/screen.ci.test.ts src/logger/tests/logger.ci.test.ts src/filedatabase/tests/filestore.ci.test.ts src/filedatabase/tests/synopsis-functions.ci.test.ts src/http-client/tests/http-client.ci.test.ts src/mock-server/tests/mock-server.ci.test.ts src/utils/tests/format-utils.ci.test.ts src/utils/tests/date-utils.ci.test.ts src/utils/tests/crypto-utils.ci.test.ts src/utils/tests/fs-utils.ci.test.ts --coverage",
54
69
  "test:args": "vitest run src/args/tests",
55
70
  "test:args:ci": "vitest run src/args/tests/args.ci.test.ts --coverage",
56
71
  "test:params": "vitest run src/params/tests",
@@ -61,6 +76,18 @@
61
76
  "test:screen:ci": "vitest run src/screen/tests/screen.ci.test.ts --coverage",
62
77
  "test:logger": "vitest run src/logger/tests/logger.cli.test.ts",
63
78
  "test:logger:ci": "vitest run src/logger/tests/logger.ci.test.ts --coverage",
79
+ "test:filedatabase": "vitest run src/filedatabase/tests",
80
+ "test:filedatabase:ci": "vitest run src/filedatabase/tests/filestore.ci.test.ts --coverage",
81
+ "test:synopsis": "vitest run src/filedatabase/tests/synopsis-functions.ci.test.ts",
82
+ "test:synopsis:ci": "vitest run src/filedatabase/tests/synopsis-functions.ci.test.ts --coverage",
83
+ "test:http-client": "vitest run src/http-client/tests",
84
+ "test:http-client:ci": "vitest run src/http-client/tests/http-client.ci.test.ts --coverage",
85
+ "test:http-client:ci:real-timeouts": "HTTP_CLIENT_REAL_TIMEOUTS=true vitest run src/http-client/tests/http-client.ci.test.ts --coverage",
86
+ "test:mock-server": "vitest run src/mock-server/tests",
87
+ "test:mock-server:ci": "vitest run src/mock-server/tests/mock-server.ci.test.ts --coverage",
88
+ "test:mock-server:coverage": "vitest run src/mock-server/tests --coverage",
89
+ "test:utils": "vitest run src/utils/tests",
90
+ "test:utils:ci": "vitest run src/utils/tests/*.ci.test.ts --coverage",
64
91
  "lint": "eslint src/",
65
92
  "type-check": "tsc --noEmit",
66
93
  "docs": "typedoc src/index.ts",
@@ -113,9 +140,12 @@
113
140
  },
114
141
  "dependencies": {
115
142
  "@types/joi": "^17.2.3",
143
+ "axios": "^1.6.0",
116
144
  "chalk": "^4.1.2",
117
145
  "dotenv": "^17.2.3",
118
- "joi": "^18.0.1"
146
+ "express": "^4.18.0",
147
+ "joi": "^18.0.1",
148
+ "morgan": "^1.10.0"
119
149
  },
120
150
  "engines": {
121
151
  "node": ">=20.0.0"