@objectstack/driver-memory 4.0.5 → 4.1.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/persistence/local-storage-adapter.ts","../src/persistence/file-adapter.ts","../src/memory-driver.ts","../src/memory-matcher.ts","../src/index.ts","../src/memory-analytics.ts","../src/in-memory-strategy.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * LocalStoragePersistenceAdapter\n *\n * Persists the in-memory database to browser localStorage.\n * Synchronous storage with a ~5MB size limit warning.\n *\n * Browser only — will throw if used in non-browser environments.\n */\nexport class LocalStoragePersistenceAdapter {\n private readonly storageKey: string;\n private static readonly SIZE_WARNING_BYTES = 4.5 * 1024 * 1024; // 4.5MB warning threshold\n\n constructor(options?: { key?: string }) {\n this.storageKey = options?.key || 'objectstack:memory-db';\n }\n\n /**\n * Load persisted data from localStorage.\n * Returns null if no data exists.\n */\n async load(): Promise<Record<string, any[]> | null> {\n try {\n const raw = localStorage.getItem(this.storageKey);\n if (!raw) return null;\n return JSON.parse(raw) as Record<string, any[]>;\n } catch {\n return null;\n }\n }\n\n /**\n * Save data to localStorage.\n * Warns if data size approaches the ~5MB localStorage limit.\n */\n async save(db: Record<string, any[]>): Promise<void> {\n const json = JSON.stringify(db);\n\n if (json.length > LocalStoragePersistenceAdapter.SIZE_WARNING_BYTES) {\n console.warn(\n `[ObjectStack] localStorage persistence data size (${(json.length / 1024 / 1024).toFixed(2)}MB) ` +\n `is approaching the ~5MB limit. Consider using a different persistence strategy.`\n );\n }\n\n try {\n localStorage.setItem(this.storageKey, json);\n } catch (e: any) {\n console.error('[ObjectStack] Failed to persist data to localStorage:', e?.message || e);\n }\n }\n\n /**\n * Flush is a no-op for localStorage (writes are synchronous).\n */\n async flush(): Promise<void> {\n // localStorage writes are synchronous, no flushing needed\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n/**\n * FileSystemPersistenceAdapter\n *\n * Persists the in-memory database to a JSON file on disk.\n * Supports atomic writes (write to temp file then rename) and auto-save with dirty tracking.\n *\n * Node.js only — will throw if used in non-Node.js environments.\n */\nexport class FileSystemPersistenceAdapter {\n private readonly filePath: string;\n private readonly autoSaveInterval: number;\n private dirty = false;\n private timer: ReturnType<typeof setInterval> | null = null;\n private currentDb: Record<string, any[]> | null = null;\n\n constructor(options?: { path?: string; autoSaveInterval?: number }) {\n this.filePath = options?.path || path.join('.objectstack', 'data', 'memory-driver.json');\n this.autoSaveInterval = options?.autoSaveInterval ?? 2000;\n }\n\n /**\n * Load persisted data from disk.\n * Returns null if no file exists.\n */\n async load(): Promise<Record<string, any[]> | null> {\n try {\n if (!fs.existsSync(this.filePath)) {\n return null;\n }\n const raw = fs.readFileSync(this.filePath, 'utf-8');\n const data = JSON.parse(raw);\n return data as Record<string, any[]>;\n } catch {\n return null;\n }\n }\n\n /**\n * Save data to disk using atomic write (temp file + rename).\n */\n async save(db: Record<string, any[]>): Promise<void> {\n this.currentDb = db;\n this.dirty = true;\n }\n\n /**\n * Flush pending writes to disk immediately.\n */\n async flush(): Promise<void> {\n if (!this.dirty || !this.currentDb) return;\n await this.writeToDisk(this.currentDb);\n this.dirty = false;\n }\n\n /**\n * Start the auto-save timer.\n */\n startAutoSave(): void {\n if (this.timer) return;\n this.timer = setInterval(async () => {\n if (this.dirty && this.currentDb) {\n await this.writeToDisk(this.currentDb);\n this.dirty = false;\n }\n }, this.autoSaveInterval);\n\n // Allow process to exit even if timer is running\n if (this.timer) {\n this.timer.unref();\n }\n }\n\n /**\n * Stop the auto-save timer and flush pending writes.\n */\n async stopAutoSave(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n /**\n * Atomic write: write to temp file, then rename.\n */\n private async writeToDisk(db: Record<string, any[]>): Promise<void> {\n const dir = path.dirname(this.filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n const tmpPath = this.filePath + '.tmp';\n const json = JSON.stringify(db, null, 2);\n fs.writeFileSync(tmpPath, json, 'utf-8');\n fs.renameSync(tmpPath, this.filePath);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { QueryAST, QueryInput, DriverOptions } from '@objectstack/spec/data';\nimport type { IDataDriver } from '@objectstack/spec/contracts';\nimport { Logger, createLogger } from '@objectstack/core';\nimport { Query, Aggregator } from 'mingo';\nimport { getValueByPath } from './memory-matcher.js';\n\n/**\n * Persistence adapter interface.\n * Matches the PersistenceAdapterSchema contract from @objectstack/spec.\n */\nexport interface PersistenceAdapterInterface {\n load(): Promise<Record<string, any[]> | null>;\n save(db: Record<string, any[]>): Promise<void>;\n flush(): Promise<void>;\n /** Optional: Start periodic auto-save (used by FileSystemPersistenceAdapter). */\n startAutoSave?(): void;\n /** Optional: Stop auto-save timer and flush pending writes. */\n stopAutoSave?(): Promise<void>;\n}\n\n/**\n * Configuration options for the InMemory driver.\n * Aligned with @objectstack/spec MemoryConfigSchema.\n */\nexport interface InMemoryDriverConfig {\n /** Optional: Initial data to populate the store */\n initialData?: Record<string, Record<string, unknown>[]>;\n /** Optional: Enable strict mode (throw on missing records) */\n strictMode?: boolean;\n /** Optional: Logger instance */\n logger?: Logger;\n /**\n * Persistence configuration. Defaults to `'auto'`.\n * - `'auto'` (default) — Auto-detect environment (browser → localStorage, Node.js → file, serverless → disabled)\n * - `'file'` — File-system persistence with defaults (Node.js only)\n * - `'local'` — localStorage persistence with defaults (Browser only)\n * - `{ type: 'file', path?: string, autoSaveInterval?: number }` — File-system with options\n * - `{ type: 'local', key?: string }` — localStorage with options\n * - `{ type: 'auto', path?: string, key?: string, autoSaveInterval?: number }` — Auto-detect with options\n * - `{ adapter: PersistenceAdapterInterface }` — Custom adapter\n * - `false` — Disable persistence (pure in-memory)\n *\n * ⚠️ In serverless environments (Vercel, AWS Lambda, Netlify, etc.),\n * auto mode disables file persistence to prevent silent data loss.\n * Use `persistence: false` or supply a custom adapter for serverless deployments.\n */\n persistence?: string | false | {\n type?: 'file' | 'local' | 'auto';\n path?: string;\n key?: string;\n autoSaveInterval?: number;\n adapter?: PersistenceAdapterInterface;\n };\n}\n\n/**\n * Snapshot for in-memory transactions.\n */\ninterface MemoryTransaction {\n id: string;\n snapshot: Record<string, any[]>;\n}\n\n/**\n * In-Memory Driver for ObjectStack\n * \n * A production-ready implementation of the ObjectStack Driver Protocol\n * powered by Mingo — a MongoDB-compatible query and aggregation engine.\n * \n * Features:\n * - MongoDB-compatible query engine (Mingo) for filtering, projection, aggregation\n * - Full CRUD and bulk operations\n * - Aggregation pipeline support ($match, $group, $sort, $project, $unwind, etc.)\n * - Snapshot-based transactions (begin/commit/rollback)\n * - Field projection and distinct values\n * - Strict mode and initial data loading\n * \n * Reference: objectql/packages/drivers/memory\n */\nexport class InMemoryDriver implements IDataDriver {\n readonly name = 'com.objectstack.driver.memory';\n type = 'driver';\n readonly version = '1.0.0';\n private config: InMemoryDriverConfig;\n private logger: Logger;\n private idCounters: Map<string, number> = new Map();\n private transactions: Map<string, MemoryTransaction> = new Map();\n private persistenceAdapter: PersistenceAdapterInterface | null = null;\n\n constructor(config?: InMemoryDriverConfig) {\n this.config = config || {};\n this.logger = config?.logger || createLogger({ level: 'info', format: 'pretty' });\n this.logger.debug('InMemory driver instance created');\n }\n\n // Duck-typed RuntimePlugin hook\n install(ctx: any) {\n this.logger.debug('Installing InMemory driver via plugin hook');\n if (ctx.engine && ctx.engine.ql && typeof ctx.engine.ql.registerDriver === 'function') {\n ctx.engine.ql.registerDriver(this);\n this.logger.info('InMemory driver registered with ObjectQL engine');\n } else {\n this.logger.warn('Could not register driver - ObjectQL engine not found in context');\n }\n }\n \n readonly supports = {\n // Basic CRUD Operations\n create: true,\n read: true,\n update: true,\n delete: true,\n\n // Bulk Operations\n bulkCreate: true,\n bulkUpdate: true,\n bulkDelete: true,\n\n // Transaction & Connection Management\n transactions: true, // Snapshot-based transactions\n savepoints: false,\n \n // Query Operations\n queryFilters: true, // Implemented via memory-matcher\n queryAggregations: true, // Implemented\n querySorting: true, // Implemented via JS sort\n queryPagination: true, // Implemented\n queryWindowFunctions: false, // @planned: Window functions (ROW_NUMBER, RANK, etc.)\n querySubqueries: false, // @planned: Subquery execution\n queryCTE: false,\n joins: false, // @planned: In-memory join operations\n \n // Advanced Features\n fullTextSearch: false, // @planned: Text tokenization + matching\n jsonQuery: false,\n geospatialQuery: false,\n streaming: true, // Implemented via findStream()\n jsonFields: true, // Native JS object support\n arrayFields: true, // Native JS array support\n vectorSearch: false, // @planned: Cosine similarity search\n\n // Schema Management\n schemaSync: true, // Implemented via syncSchema()\n batchSchemaSync: false,\n migrations: false,\n indexes: false,\n\n // Performance & Optimization\n connectionPooling: false,\n preparedStatements: false,\n queryCache: false,\n };\n\n /**\n * The \"Database\": A map of TableName -> Array of Records\n */\n private db: Record<string, any[]> = {};\n\n // ===================================\n // Lifecycle\n // ===================================\n\n async connect() {\n // Initialize persistence adapter if configured\n await this.initPersistence();\n\n // Load persisted data if available\n if (this.persistenceAdapter) {\n const persisted = await this.persistenceAdapter.load();\n if (persisted) {\n for (const [objectName, records] of Object.entries(persisted)) {\n this.db[objectName] = records;\n // Update ID counters based on persisted data\n for (const record of records) {\n if (record.id && typeof record.id === 'string') {\n // ID format: {objectName}-{timestamp}-{counter}\n const parts = record.id.split('-');\n const lastPart = parts[parts.length - 1];\n const counter = parseInt(lastPart, 10);\n if (!isNaN(counter)) {\n const current = this.idCounters.get(objectName) || 0;\n if (counter > current) {\n this.idCounters.set(objectName, counter);\n }\n }\n }\n }\n }\n this.logger.info('InMemory Database restored from persistence', {\n tables: Object.keys(persisted).length,\n });\n }\n }\n\n // Load initial data if provided\n if (this.config.initialData) {\n for (const [objectName, records] of Object.entries(this.config.initialData)) {\n const table = this.getTable(objectName);\n for (const record of records) {\n const id = (record as any).id || this.generateId(objectName);\n table.push({ ...record, id });\n }\n }\n this.logger.info('InMemory Database Connected with initial data', {\n tables: Object.keys(this.config.initialData).length,\n });\n } else {\n this.logger.info('InMemory Database Connected (Virtual)');\n }\n\n // Start auto-save if using file adapter\n if (this.persistenceAdapter?.startAutoSave) {\n this.persistenceAdapter.startAutoSave();\n }\n }\n\n async disconnect() {\n // Stop auto-save and flush pending writes\n if (this.persistenceAdapter) {\n if (this.persistenceAdapter.stopAutoSave) {\n await this.persistenceAdapter.stopAutoSave();\n }\n await this.persistenceAdapter.flush();\n }\n\n const tableCount = Object.keys(this.db).length;\n const recordCount = Object.values(this.db).reduce((sum, table) => sum + table.length, 0);\n \n this.db = {};\n this.logger.info('InMemory Database Disconnected & Cleared', { \n tableCount, \n recordCount \n });\n }\n\n async checkHealth() {\n this.logger.debug('Health check performed', { \n tableCount: Object.keys(this.db).length,\n status: 'healthy' \n });\n return true; \n }\n\n // ===================================\n // Execution\n // ===================================\n\n async execute(command: any, params?: any[]) {\n this.logger.warn('Raw execution not supported in InMemory driver', { command });\n return null;\n }\n\n // ===================================\n // CRUD\n // ===================================\n\n async find(object: string, query: QueryAST, options?: DriverOptions) {\n this.logger.debug('Find operation', { object, query });\n \n const table = this.getTable(object);\n let results = [...table]; // Work on copy\n\n // 1. Filter using Mingo\n if (query.where) {\n const mongoQuery = this.convertToMongoQuery(query.where);\n if (mongoQuery && Object.keys(mongoQuery).length > 0) {\n const mingoQuery = new Query(mongoQuery);\n results = mingoQuery.find(results).all();\n }\n }\n\n // 1.5 Aggregation & Grouping\n if (query.groupBy || (query.aggregations && query.aggregations.length > 0)) {\n results = this.performAggregation(results, query);\n }\n\n // 2. Sort\n if (query.orderBy) {\n const sortFields = Array.isArray(query.orderBy) ? query.orderBy : [query.orderBy];\n results = this.applySort(results, sortFields);\n }\n\n // 3. Pagination (Offset)\n if (query.offset) {\n results = results.slice(query.offset);\n }\n\n // 4. Pagination (Limit)\n if (query.limit) {\n results = results.slice(0, query.limit);\n }\n\n // 5. Field Projection\n if (query.fields && Array.isArray(query.fields) && query.fields.length > 0) {\n results = results.map(record => this.projectFields(record, query.fields as string[]));\n }\n\n this.logger.debug('Find completed', { object, resultCount: results.length });\n return results;\n }\n\n async *findStream(object: string, query: QueryAST, options?: DriverOptions) {\n this.logger.debug('FindStream operation', { object });\n \n const results = await this.find(object, query, options);\n for (const record of results) {\n yield record;\n }\n }\n\n async findOne(object: string, query: QueryAST, options?: DriverOptions) {\n this.logger.debug('FindOne operation', { object, query });\n \n const results = await this.find(object, { ...query, limit: 1 }, options);\n const result = results[0] || null;\n \n this.logger.debug('FindOne completed', { object, found: !!result });\n return result;\n }\n\n async create(object: string, data: Record<string, any>, options?: DriverOptions) {\n this.logger.debug('Create operation', { object, hasData: !!data });\n \n const table = this.getTable(object);\n \n const newRecord = {\n id: data.id || this.generateId(object),\n ...data,\n created_at: data.created_at || new Date().toISOString(),\n updated_at: data.updated_at || new Date().toISOString(),\n };\n\n table.push(newRecord);\n this.markDirty();\n this.logger.debug('Record created', { object, id: newRecord.id, tableSize: table.length });\n return { ...newRecord };\n }\n\n async update(object: string, id: string | number, data: Record<string, any>, options?: DriverOptions) {\n this.logger.debug('Update operation', { object, id });\n \n const table = this.getTable(object);\n const index = table.findIndex(r => r.id == id);\n \n if (index === -1) {\n if (this.config.strictMode) {\n this.logger.warn('Record not found for update', { object, id });\n throw new Error(`Record with ID ${id} not found in ${object}`);\n }\n return null;\n }\n\n const updatedRecord = {\n ...table[index],\n ...data,\n id: table[index].id, // Preserve original ID\n created_at: table[index].created_at, // Preserve created_at\n updated_at: new Date().toISOString(),\n };\n \n table[index] = updatedRecord;\n this.markDirty();\n this.logger.debug('Record updated', { object, id });\n return { ...updatedRecord };\n }\n\n async upsert(object: string, data: Record<string, any>, conflictKeys?: string[], options?: DriverOptions) {\n this.logger.debug('Upsert operation', { object, conflictKeys });\n \n const table = this.getTable(object);\n let existingRecord: any = null;\n\n if (data.id) {\n existingRecord = table.find(r => r.id === data.id);\n } else if (conflictKeys && conflictKeys.length > 0) {\n existingRecord = table.find(r => conflictKeys.every(key => r[key] === data[key]));\n }\n\n if (existingRecord) {\n this.logger.debug('Record exists, updating', { object, id: existingRecord.id });\n return this.update(object, existingRecord.id, data, options);\n } else {\n this.logger.debug('Record does not exist, creating', { object });\n return this.create(object, data, options);\n }\n }\n\n async delete(object: string, id: string | number, options?: DriverOptions) {\n this.logger.debug('Delete operation', { object, id });\n \n const table = this.getTable(object);\n const index = table.findIndex(r => r.id == id);\n \n if (index === -1) {\n if (this.config.strictMode) {\n throw new Error(`Record with ID ${id} not found in ${object}`);\n }\n this.logger.warn('Record not found for deletion', { object, id });\n return false;\n }\n\n table.splice(index, 1);\n this.markDirty();\n this.logger.debug('Record deleted', { object, id, tableSize: table.length });\n return true;\n }\n\n async count(object: string, query?: QueryAST, options?: DriverOptions) {\n let records = this.getTable(object);\n if (query?.where) {\n const mongoQuery = this.convertToMongoQuery(query.where);\n if (mongoQuery && Object.keys(mongoQuery).length > 0) {\n const mingoQuery = new Query(mongoQuery);\n records = mingoQuery.find(records).all();\n }\n }\n const count = records.length;\n this.logger.debug('Count operation', { object, count });\n return count;\n }\n\n // ===================================\n // Bulk Operations\n // ===================================\n\n async bulkCreate(object: string, dataArray: Record<string, any>[], options?: DriverOptions) {\n this.logger.debug('BulkCreate operation', { object, count: dataArray.length });\n const results = await Promise.all(dataArray.map(data => this.create(object, data, options)));\n this.logger.debug('BulkCreate completed', { object, count: results.length });\n return results;\n }\n \n async updateMany(object: string, query: QueryAST, data: Record<string, any>, options?: DriverOptions): Promise<number> {\n this.logger.debug('UpdateMany operation', { object, query });\n \n const table = this.getTable(object);\n let targetRecords = table;\n \n if (query && query.where) {\n const mongoQuery = this.convertToMongoQuery(query.where);\n if (mongoQuery && Object.keys(mongoQuery).length > 0) {\n const mingoQuery = new Query(mongoQuery);\n targetRecords = mingoQuery.find(targetRecords).all();\n }\n }\n \n const count = targetRecords.length;\n \n for (const record of targetRecords) {\n const index = table.findIndex(r => r.id === record.id);\n if (index !== -1) {\n const updated = {\n ...table[index],\n ...data,\n updated_at: new Date().toISOString()\n };\n table[index] = updated;\n }\n }\n \n if (count > 0) this.markDirty();\n this.logger.debug('UpdateMany completed', { object, count });\n return count;\n }\n\n async deleteMany(object: string, query: QueryAST, options?: DriverOptions): Promise<number> {\n this.logger.debug('DeleteMany operation', { object, query });\n \n const table = this.getTable(object);\n const initialLength = table.length;\n \n if (query && query.where) {\n const mongoQuery = this.convertToMongoQuery(query.where);\n if (mongoQuery && Object.keys(mongoQuery).length > 0) {\n const mingoQuery = new Query(mongoQuery);\n const matched = mingoQuery.find(table).all();\n const matchedIds = new Set(matched.map((r: any) => r.id));\n this.db[object] = table.filter(r => !matchedIds.has(r.id));\n } else {\n // Empty query = delete all\n this.db[object] = [];\n }\n } else {\n // No where clause = delete all\n this.db[object] = [];\n }\n \n const count = initialLength - this.db[object].length;\n if (count > 0) this.markDirty();\n this.logger.debug('DeleteMany completed', { object, count });\n return count;\n }\n\n // Compatibility aliases\n async bulkUpdate(object: string, updates: { id: string | number, data: Record<string, any> }[], options?: DriverOptions) {\n this.logger.debug('BulkUpdate operation', { object, count: updates.length });\n const results = await Promise.all(updates.map(u => this.update(object, u.id, u.data, options)));\n this.logger.debug('BulkUpdate completed', { object, count: results.length });\n return results;\n }\n\n async bulkDelete(object: string, ids: (string | number)[], options?: DriverOptions) {\n this.logger.debug('BulkDelete operation', { object, count: ids.length });\n await Promise.all(ids.map(id => this.delete(object, id, options)));\n this.logger.debug('BulkDelete completed', { object, count: ids.length });\n }\n\n // ===================================\n // Transaction Management\n // ===================================\n\n async beginTransaction() {\n const txId = `tx_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n // Deep-clone current database state as a snapshot\n const snapshot: Record<string, any[]> = {};\n for (const [table, records] of Object.entries(this.db)) {\n snapshot[table] = records.map(r => ({ ...r }));\n }\n\n const transaction: MemoryTransaction = { id: txId, snapshot };\n this.transactions.set(txId, transaction);\n this.logger.debug('Transaction started', { txId });\n return { id: txId };\n }\n\n async commit(txHandle?: unknown) {\n const txId = (txHandle as any)?.id;\n if (!txId || !this.transactions.has(txId)) {\n this.logger.warn('Commit called with unknown transaction');\n return;\n }\n // Data is already in the store; just remove the snapshot\n this.transactions.delete(txId);\n this.logger.debug('Transaction committed', { txId });\n }\n\n async rollback(txHandle?: unknown) {\n const txId = (txHandle as any)?.id;\n if (!txId || !this.transactions.has(txId)) {\n this.logger.warn('Rollback called with unknown transaction');\n return;\n }\n const tx = this.transactions.get(txId)!;\n // Restore the snapshot\n this.db = tx.snapshot;\n this.transactions.delete(txId);\n this.markDirty();\n this.logger.debug('Transaction rolled back', { txId });\n }\n\n // ===================================\n // Utility Methods\n // ===================================\n\n /**\n * Remove all data from the store.\n */\n async clear() {\n this.db = {};\n this.idCounters.clear();\n this.markDirty();\n this.logger.debug('All data cleared');\n }\n\n /**\n * Get total number of records across all tables.\n */\n getSize(): number {\n return Object.values(this.db).reduce((sum, table) => sum + table.length, 0);\n }\n\n /**\n * Get distinct values for a field, optionally filtered.\n */\n async distinct(object: string, field: string, query?: QueryInput): Promise<any[]> {\n let records = this.getTable(object);\n if (query?.where) {\n const mongoQuery = this.convertToMongoQuery(query.where);\n if (mongoQuery && Object.keys(mongoQuery).length > 0) {\n const mingoQuery = new Query(mongoQuery);\n records = mingoQuery.find(records).all();\n }\n }\n const values = new Set<any>();\n for (const record of records) {\n const value = getValueByPath(record, field);\n if (value !== undefined && value !== null) {\n values.add(value);\n }\n }\n return Array.from(values);\n }\n\n /**\n * Execute a MongoDB-style aggregation pipeline using Mingo.\n * \n * Supports all standard MongoDB pipeline stages:\n * - $match, $group, $sort, $project, $unwind, $limit, $skip\n * - $addFields, $replaceRoot, $lookup (limited), $count\n * - Accumulator operators: $sum, $avg, $min, $max, $first, $last, $push, $addToSet\n * \n * @example\n * // Group by status and count\n * const results = await driver.aggregate('orders', [\n * { $match: { status: 'completed' } },\n * { $group: { _id: '$customer', totalAmount: { $sum: '$amount' } } }\n * ]);\n * \n * @example\n * // Calculate average with filter\n * const results = await driver.aggregate('products', [\n * { $match: { category: 'electronics' } },\n * { $group: { _id: null, avgPrice: { $avg: '$price' } } }\n * ]);\n */\n async aggregate(object: string, pipeline: Record<string, any>[], options?: DriverOptions): Promise<any[]> {\n this.logger.debug('Aggregate operation', { object, stageCount: pipeline.length });\n \n const records = this.getTable(object).map(r => ({ ...r }));\n const aggregator = new Aggregator(pipeline);\n const results = aggregator.run(records);\n \n this.logger.debug('Aggregate completed', { object, resultCount: results.length });\n return results;\n }\n\n // ===================================\n // Query Conversion (ObjectQL → MongoDB)\n // ===================================\n\n /**\n * Convert ObjectQL filter format to MongoDB query format for Mingo.\n * \n * Supports:\n * 1. AST Comparison Node: { type: 'comparison', field, operator, value }\n * 2. AST Logical Node: { type: 'logical', operator: 'and'|'or', conditions: [...] }\n * 3. Legacy Array Format: [['field', 'op', value], 'and', ['field2', 'op', value2]]\n * 4. MongoDB Format: { field: value } or { field: { $eq: value } } (passthrough)\n */\n private convertToMongoQuery(filters?: any): Record<string, any> {\n if (!filters) return {};\n\n // AST node format (ObjectQL QueryAST)\n if (!Array.isArray(filters) && typeof filters === 'object') {\n if (filters.type === 'comparison') {\n return this.convertConditionToMongo(filters.field, filters.operator, filters.value) || {};\n }\n if (filters.type === 'logical') {\n const conditions = filters.conditions?.map((c: any) => this.convertToMongoQuery(c)) || [];\n if (conditions.length === 0) return {};\n if (conditions.length === 1) return conditions[0];\n const op = filters.operator === 'or' ? '$or' : '$and';\n return { [op]: conditions };\n }\n // MongoDB/FilterCondition format: { field: value } or { field: { $op: value } }\n // Translate non-standard operators ($contains, $notContains, etc.) to Mingo-compatible format\n return this.normalizeFilterCondition(filters);\n }\n\n // Legacy array format\n if (!Array.isArray(filters) || filters.length === 0) return {};\n\n const logicGroups: { logic: 'and' | 'or'; conditions: Record<string, any>[] }[] = [\n { logic: 'and', conditions: [] },\n ];\n let currentLogic: 'and' | 'or' = 'and';\n\n for (const item of filters) {\n if (typeof item === 'string') {\n const newLogic = item.toLowerCase() as 'and' | 'or';\n if (newLogic !== currentLogic) {\n currentLogic = newLogic;\n logicGroups.push({ logic: currentLogic, conditions: [] });\n }\n } else if (Array.isArray(item)) {\n const [field, operator, value] = item;\n const cond = this.convertConditionToMongo(field, operator, value);\n if (cond) logicGroups[logicGroups.length - 1].conditions.push(cond);\n }\n }\n\n const allConditions: Record<string, any>[] = [];\n for (const group of logicGroups) {\n if (group.conditions.length === 0) continue;\n if (group.conditions.length === 1) {\n allConditions.push(group.conditions[0]);\n } else {\n const op = group.logic === 'or' ? '$or' : '$and';\n allConditions.push({ [op]: group.conditions });\n }\n }\n\n if (allConditions.length === 0) return {};\n if (allConditions.length === 1) return allConditions[0];\n return { $and: allConditions };\n }\n\n /**\n * Convert a single ObjectQL condition to MongoDB operator format.\n */\n private convertConditionToMongo(field: string, operator: string, value: any): Record<string, any> | null {\n switch (operator) {\n case '=': case '==':\n return { [field]: value };\n case '!=': case '<>':\n return { [field]: { $ne: value } };\n case '>':\n return { [field]: { $gt: value } };\n case '>=':\n return { [field]: { $gte: value } };\n case '<':\n return { [field]: { $lt: value } };\n case '<=':\n return { [field]: { $lte: value } };\n case 'in':\n return { [field]: { $in: value } };\n case 'nin': case 'not in':\n return { [field]: { $nin: value } };\n case 'contains': case 'like':\n return { [field]: { $regex: new RegExp(this.escapeRegex(value), 'i') } };\n case 'notcontains': case 'not_contains':\n return { [field]: { $not: { $regex: new RegExp(this.escapeRegex(value), 'i') } } };\n case 'startswith': case 'starts_with':\n return { [field]: { $regex: new RegExp(`^${this.escapeRegex(value)}`, 'i') } };\n case 'endswith': case 'ends_with':\n return { [field]: { $regex: new RegExp(`${this.escapeRegex(value)}$`, 'i') } };\n case 'between':\n if (Array.isArray(value) && value.length === 2) {\n return { [field]: { $gte: value[0], $lte: value[1] } };\n }\n return null;\n default:\n return null;\n }\n }\n\n /**\n * Normalize a FilterCondition object by converting non-standard $-prefixed\n * operators ($contains, $notContains, $startsWith, $endsWith, $between, $null)\n * to Mingo-compatible equivalents ($regex, $gte/$lte, null checks).\n */\n private normalizeFilterCondition(filter: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n const extraAndConditions: Record<string, any>[] = [];\n\n for (const key of Object.keys(filter)) {\n const value = filter[key];\n // Recurse into logical operators\n if (key === '$and' || key === '$or') {\n result[key] = Array.isArray(value)\n ? value.map((child: any) => this.normalizeFilterCondition(child))\n : value;\n continue;\n }\n if (key === '$not') {\n result[key] = value && typeof value === 'object'\n ? this.normalizeFilterCondition(value)\n : value;\n continue;\n }\n // Skip $-prefixed keys that aren't field names (already handled or unknown)\n if (key.startsWith('$')) {\n result[key] = value;\n continue;\n }\n // Field-level: value may be primitive (implicit eq) or operator object\n if (value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof RegExp)) {\n const normalized = this.normalizeFieldOperators(value);\n // Handle multiple regex conditions on the same field (e.g. $startsWith + $endsWith)\n if (normalized._multiRegex) {\n const regexConditions: Record<string, any>[] = normalized._multiRegex;\n delete normalized._multiRegex;\n // Each regex becomes its own { field: { $regex: ... } } inside $and\n for (const rc of regexConditions) {\n extraAndConditions.push({ [key]: { ...normalized, ...rc } });\n }\n } else {\n result[key] = normalized;\n }\n } else {\n result[key] = value;\n }\n }\n\n // Merge extra $and conditions from multi-regex fields\n if (extraAndConditions.length > 0) {\n const existing = result.$and;\n const andArray = Array.isArray(existing) ? existing : [];\n // Include the rest of result as a condition too\n if (Object.keys(result).filter(k => k !== '$and').length > 0) {\n const rest = { ...result };\n delete rest.$and;\n andArray.push(rest);\n }\n andArray.push(...extraAndConditions);\n return { $and: andArray };\n }\n\n return result;\n }\n\n /**\n * Convert non-standard field operators to Mingo-compatible format.\n * When multiple regex-producing operators appear on the same field\n * (e.g. $startsWith + $endsWith), they are combined via $and.\n */\n private normalizeFieldOperators(ops: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n const regexConditions: Record<string, any>[] = [];\n\n for (const op of Object.keys(ops)) {\n const val = ops[op];\n switch (op) {\n case '$contains':\n regexConditions.push({ $regex: new RegExp(this.escapeRegex(val), 'i') });\n break;\n case '$notContains':\n result.$not = { $regex: new RegExp(this.escapeRegex(val), 'i') };\n break;\n case '$startsWith':\n regexConditions.push({ $regex: new RegExp(`^${this.escapeRegex(val)}`, 'i') });\n break;\n case '$endsWith':\n regexConditions.push({ $regex: new RegExp(`${this.escapeRegex(val)}$`, 'i') });\n break;\n case '$between':\n if (Array.isArray(val) && val.length === 2) {\n result.$gte = val[0];\n result.$lte = val[1];\n }\n break;\n case '$null':\n // $null: true → field is null, $null: false → field is not null\n // Use $eq/$ne null for Mingo compatibility\n if (val === true) {\n result.$eq = null;\n } else {\n result.$ne = null;\n }\n break;\n default:\n result[op] = val;\n break;\n }\n }\n\n // Merge regex conditions: single → inline, multiple → wrap with $and\n if (regexConditions.length === 1) {\n Object.assign(result, regexConditions[0]);\n } else if (regexConditions.length > 1) {\n // Cannot have multiple $regex on one object; promote to top-level $and.\n // _multiRegex is an internal sentinel consumed by normalizeFilterCondition().\n result._multiRegex = regexConditions;\n }\n\n return result;\n }\n\n /**\n * Escape special regex characters for safe literal matching.\n */\n private escapeRegex(str: string): string {\n return String(str).replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n\n // ===================================\n // Aggregation Logic\n // ===================================\n\n private performAggregation(records: any[], query: QueryInput): any[] {\n const { groupBy, aggregations } = query;\n const groups: Map<string, any[]> = new Map();\n\n // 1. Group records\n if (groupBy && groupBy.length > 0) {\n for (const record of records) {\n // Create a composite key from group values\n const keyParts = groupBy.map(field => {\n const val = getValueByPath(record, field);\n return val === undefined || val === null ? 'null' : String(val);\n });\n const key = JSON.stringify(keyParts);\n \n if (!groups.has(key)) {\n groups.set(key, []);\n }\n groups.get(key)!.push(record);\n }\n } else {\n groups.set('all', records);\n }\n\n // 2. Compute aggregates for each group\n const resultRows: any[] = [];\n \n for (const [_key, groupRecords] of groups.entries()) {\n const row: any = {};\n \n // A. Add Group fields to row (if groupBy exists)\n if (groupBy && groupBy.length > 0) {\n if (groupRecords.length > 0) {\n const firstRecord = groupRecords[0];\n for (const field of groupBy) {\n this.setValueByPath(row, field, getValueByPath(firstRecord, field));\n }\n }\n }\n \n // B. Compute Aggregations\n if (aggregations) {\n for (const agg of aggregations) {\n const value = this.computeAggregate(groupRecords, agg);\n row[agg.alias] = value;\n }\n }\n \n resultRows.push(row);\n }\n \n return resultRows;\n }\n \n private computeAggregate(records: any[], agg: any): any {\n const { function: func, field } = agg;\n \n const values = field ? records.map(r => getValueByPath(r, field)) : [];\n \n switch (func) {\n case 'count':\n if (!field || field === '*') return records.length;\n return values.filter(v => v !== null && v !== undefined).length;\n \n case 'sum':\n case 'avg': {\n const nums = values.filter(v => typeof v === 'number');\n const sum = nums.reduce((a, b) => a + b, 0);\n if (func === 'sum') return sum;\n return nums.length > 0 ? sum / nums.length : null;\n }\n \n case 'min': {\n // Handle comparable values\n const valid = values.filter(v => v !== null && v !== undefined);\n if (valid.length === 0) return null;\n // Works for numbers and strings\n return valid.reduce((min, v) => (v < min ? v : min), valid[0]);\n }\n\n case 'max': {\n const valid = values.filter(v => v !== null && v !== undefined);\n if (valid.length === 0) return null;\n return valid.reduce((max, v) => (v > max ? v : max), valid[0]);\n }\n\n default:\n return null;\n }\n }\n\n private setValueByPath(obj: any, path: string, value: any) {\n const parts = path.split('.');\n let current = obj;\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (!current[part]) current[part] = {};\n current = current[part];\n }\n current[parts[parts.length - 1]] = value;\n }\n\n // ===================================\n // Schema Management\n // ===================================\n\n async syncSchema(object: string, schema: any, options?: DriverOptions) {\n if (!this.db[object]) {\n this.db[object] = [];\n this.logger.info('Created in-memory table', { object });\n }\n }\n\n async dropTable(object: string, options?: DriverOptions) {\n if (this.db[object]) {\n const recordCount = this.db[object].length;\n delete this.db[object];\n this.logger.info('Dropped in-memory table', { object, recordCount });\n }\n }\n\n // ===================================\n // Helpers\n // ===================================\n\n /**\n * Apply manual sorting (Mingo sort has CJS build issues).\n */\n private applySort(records: any[], sortFields: any[]): any[] {\n const sorted = [...records];\n for (let i = sortFields.length - 1; i >= 0; i--) {\n const sortItem = sortFields[i];\n let field: string;\n let direction: string;\n if (typeof sortItem === 'object' && !Array.isArray(sortItem)) {\n field = sortItem.field;\n direction = sortItem.order || sortItem.direction || 'asc';\n } else if (Array.isArray(sortItem)) {\n [field, direction] = sortItem;\n } else {\n continue;\n }\n sorted.sort((a, b) => {\n const aVal = getValueByPath(a, field);\n const bVal = getValueByPath(b, field);\n if (aVal == null && bVal == null) return 0;\n if (aVal == null) return 1;\n if (bVal == null) return -1;\n if (aVal < bVal) return direction === 'desc' ? 1 : -1;\n if (aVal > bVal) return direction === 'desc' ? -1 : 1;\n return 0;\n });\n }\n return sorted;\n }\n\n /**\n * Project specific fields from a record.\n */\n private projectFields(record: any, fields: string[]): any {\n const result: any = {};\n for (const field of fields) {\n const value = getValueByPath(record, field);\n if (value !== undefined) {\n result[field] = value;\n }\n }\n // Always include id if not explicitly listed\n if (!fields.includes('id') && record.id !== undefined) {\n result.id = record.id;\n }\n return result;\n }\n\n private getTable(name: string) {\n if (!this.db[name]) {\n this.db[name] = [];\n }\n return this.db[name];\n }\n\n private generateId(objectName?: string) {\n const key = objectName || '_global';\n const counter = (this.idCounters.get(key) || 0) + 1;\n this.idCounters.set(key, counter);\n const timestamp = Date.now();\n return `${key}-${timestamp}-${counter}`;\n }\n\n // ===================================\n // Persistence\n // ===================================\n\n /**\n * Mark the database as dirty, triggering persistence save.\n */\n private markDirty(): void {\n if (this.persistenceAdapter) {\n this.persistenceAdapter.save(this.db);\n }\n }\n\n /**\n * Flush pending persistence writes to ensure data is safely stored.\n */\n async flush(): Promise<void> {\n if (this.persistenceAdapter) {\n await this.persistenceAdapter.flush();\n }\n }\n\n /**\n * Detect whether the current runtime is a browser environment.\n * Checks for window, document AND a functional localStorage to avoid\n * false positives in Node.js runtimes that partially polyfill globals.\n */\n private isBrowserEnvironment(): boolean {\n const g = globalThis as any;\n return typeof g.window !== 'undefined'\n && typeof g.document !== 'undefined'\n && typeof g.localStorage?.setItem === 'function';\n }\n\n /**\n * Detect whether the current runtime is a serverless/edge environment.\n *\n * Checks well-known environment variables set by serverless platforms:\n * - `VERCEL` / `VERCEL_ENV` — Vercel Functions / Edge\n * - `AWS_LAMBDA_FUNCTION_NAME` — AWS Lambda\n * - `NETLIFY` — Netlify Functions\n * - `FUNCTIONS_WORKER_RUNTIME` — Azure Functions\n * - `K_SERVICE` — Google Cloud Run / Cloud Functions\n * - `FUNCTION_TARGET` — Google Cloud Functions (Node.js)\n * - `DENO_DEPLOYMENT_ID` — Deno Deploy\n *\n * Returns `false` when `process` or `process.env` is unavailable\n * (e.g. browser or edge runtimes without a Node.js process object).\n */\n private isServerlessEnvironment(): boolean {\n if (typeof globalThis.process === 'undefined' || !globalThis.process.env) {\n return false;\n }\n const env = globalThis.process.env;\n return !!(\n env.VERCEL ||\n env.VERCEL_ENV ||\n env.AWS_LAMBDA_FUNCTION_NAME ||\n env.NETLIFY ||\n env.FUNCTIONS_WORKER_RUNTIME ||\n env.K_SERVICE ||\n env.FUNCTION_TARGET ||\n env.DENO_DEPLOYMENT_ID\n );\n }\n\n private static readonly SERVERLESS_PERSISTENCE_WARNING =\n 'Serverless environment detected — file-system persistence is disabled in auto mode. ' +\n 'Data will NOT be persisted across function invocations. ' +\n 'Set persistence: false to silence this warning, or provide a custom adapter ' +\n '(e.g. Upstash Redis, Vercel KV) via persistence: { adapter: yourAdapter }.';\n\n /**\n * Initialize the persistence adapter based on configuration.\n * Defaults to 'auto' when persistence is not specified.\n * Use `persistence: false` to explicitly disable persistence.\n *\n * In serverless environments (Vercel, AWS Lambda, etc.), auto mode disables\n * file-system persistence and emits a warning. Use `persistence: false` or\n * supply a custom adapter for serverless-safe operation.\n */\n private async initPersistence(): Promise<void> {\n const persistence = this.config.persistence === undefined ? 'auto' : this.config.persistence;\n if (persistence === false) return;\n\n if (typeof persistence === 'string') {\n if (persistence === 'auto') {\n if (this.isBrowserEnvironment()) {\n const { LocalStoragePersistenceAdapter } = await import('./persistence/local-storage-adapter.js');\n this.persistenceAdapter = new LocalStoragePersistenceAdapter();\n this.logger.debug('Auto-detected browser environment, using localStorage persistence');\n } else if (this.isServerlessEnvironment()) {\n this.logger.warn(InMemoryDriver.SERVERLESS_PERSISTENCE_WARNING);\n } else {\n const { FileSystemPersistenceAdapter } = await import('./persistence/file-adapter.js');\n this.persistenceAdapter = new FileSystemPersistenceAdapter();\n this.logger.debug('Auto-detected Node.js environment, using file persistence');\n }\n } else if (persistence === 'file') {\n const { FileSystemPersistenceAdapter } = await import('./persistence/file-adapter.js');\n this.persistenceAdapter = new FileSystemPersistenceAdapter();\n } else if (persistence === 'local') {\n const { LocalStoragePersistenceAdapter } = await import('./persistence/local-storage-adapter.js');\n this.persistenceAdapter = new LocalStoragePersistenceAdapter();\n } else {\n throw new Error(`Unknown persistence type: \"${persistence}\". Use 'file', 'local', or 'auto'.`);\n }\n } else if ('adapter' in persistence && persistence.adapter) {\n this.persistenceAdapter = persistence.adapter;\n } else if ('type' in persistence) {\n if (persistence.type === 'auto') {\n if (this.isBrowserEnvironment()) {\n const { LocalStoragePersistenceAdapter } = await import('./persistence/local-storage-adapter.js');\n this.persistenceAdapter = new LocalStoragePersistenceAdapter({\n key: persistence.key,\n });\n this.logger.debug('Auto-detected browser environment, using localStorage persistence');\n } else if (this.isServerlessEnvironment()) {\n this.logger.warn(InMemoryDriver.SERVERLESS_PERSISTENCE_WARNING);\n } else {\n const { FileSystemPersistenceAdapter } = await import('./persistence/file-adapter.js');\n this.persistenceAdapter = new FileSystemPersistenceAdapter({\n path: persistence.path,\n autoSaveInterval: persistence.autoSaveInterval,\n });\n this.logger.debug('Auto-detected Node.js environment, using file persistence');\n }\n } else if (persistence.type === 'file') {\n const { FileSystemPersistenceAdapter } = await import('./persistence/file-adapter.js');\n this.persistenceAdapter = new FileSystemPersistenceAdapter({\n path: persistence.path,\n autoSaveInterval: persistence.autoSaveInterval,\n });\n } else if (persistence.type === 'local') {\n const { LocalStoragePersistenceAdapter } = await import('./persistence/local-storage-adapter.js');\n this.persistenceAdapter = new LocalStoragePersistenceAdapter({\n key: persistence.key,\n });\n }\n }\n\n if (this.persistenceAdapter) {\n this.logger.debug('Persistence adapter initialized');\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n\n/**\n * Simple In-Memory Query Matcher\n * \n * Implements a subset of the ObjectStack Filter Protocol (MongoDB-compatible)\n * for evaluating conditions against in-memory JavaScript objects.\n */\n\ntype RecordType = Record<string, any>;\n\n/**\n * matches - Check if a record matches a filter criteria\n * @param record The data record to check\n * @param filter The filter condition (where clause)\n */\nexport function match(record: RecordType, filter: any): boolean {\n if (!filter || Object.keys(filter).length === 0) return true;\n \n // 1. Handle Top-Level Logical Operators ($and, $or, $not)\n // These usually appear at the root or nested.\n \n // $and: [ { ... }, { ... } ]\n if (Array.isArray(filter.$and)) {\n if (!filter.$and.every((f: any) => match(record, f))) {\n return false;\n }\n }\n \n // $or: [ { ... }, { ... } ]\n if (Array.isArray(filter.$or)) {\n if (!filter.$or.some((f: any) => match(record, f))) {\n return false;\n }\n }\n \n // $not: { ... }\n if (filter.$not) {\n if (match(record, filter.$not)) {\n return false;\n }\n }\n \n // 2. Iterate over field constraints\n for (const key of Object.keys(filter)) {\n // Skip logical operators we already handled (or future ones)\n if (key.startsWith('$')) continue;\n \n const condition = filter[key];\n const value = getValueByPath(record, key);\n \n if (!checkCondition(value, condition)) {\n return false;\n }\n }\n \n return true;\n}\n\n/**\n * Access nested properties via dot-notation (e.g. \"user.name\")\n */\nexport function getValueByPath(obj: any, path: string): any {\n if (!path.includes('.')) return obj[path];\n return path.split('.').reduce((o, i) => (o ? o[i] : undefined), obj);\n}\n\n/**\n * Evaluate a specific condition against a value\n */\nfunction checkCondition(value: any, condition: any): boolean {\n // Case A: Implicit Equality (e.g. status: 'active')\n // If condition is a primitive or Date/Array (exact match), treat as equality.\n if (\n typeof condition !== 'object' || \n condition === null || \n condition instanceof Date ||\n Array.isArray(condition)\n ) {\n // Loose equality to handle undefined/null mismatch or string/number coercion if desired.\n // But stick to == for JS loose equality which is often convenient in weakly typed queries.\n return value == condition;\n }\n \n // Case B: Operator Object (e.g. { $gt: 10, $lt: 20 })\n const keys = Object.keys(condition);\n const isOperatorObject = keys.some(k => k.startsWith('$'));\n \n if (!isOperatorObject) {\n // It's just a nested object comparison or implicit equality against an object\n // Simplistic check:\n return JSON.stringify(value) === JSON.stringify(condition);\n }\n\n // Iterate operators\n for (const op of keys) {\n const target = condition[op];\n \n // Handle undefined values\n if (value === undefined && op !== '$exists' && op !== '$ne' && op !== '$null') {\n return false; \n }\n\n switch (op) {\n case '$eq': \n if (value != target) return false; \n break;\n case '$ne': \n if (value == target) return false; \n break;\n \n // Numeric / Date\n case '$gt': \n if (!(value > target)) return false; \n break;\n case '$gte': \n if (!(value >= target)) return false; \n break;\n case '$lt': \n if (!(value < target)) return false; \n break;\n case '$lte': \n if (!(value <= target)) return false; \n break;\n case '$between':\n // target should be [min, max]\n if (Array.isArray(target) && (value < target[0] || value > target[1])) return false;\n break;\n\n // Sets\n case '$in': \n if (!Array.isArray(target) || !target.includes(value)) return false; \n break;\n case '$nin': \n if (Array.isArray(target) && target.includes(value)) return false; \n break;\n \n // Existence\n case '$exists':\n const exists = value !== undefined && value !== null;\n if (exists !== !!target) return false;\n break;\n\n // Strings\n case '$contains': \n if (typeof value !== 'string' || !value.includes(target)) return false; \n break;\n case '$notContains':\n if (typeof value !== 'string' || value.includes(target)) return false;\n break;\n case '$startsWith': \n if (typeof value !== 'string' || !value.startsWith(target)) return false; \n break;\n case '$endsWith': \n if (typeof value !== 'string' || !value.endsWith(target)) return false; \n break;\n case '$null':\n // $null: true → value must be null/undefined; $null: false → value must not be null/undefined\n if (target === true && value != null) return false;\n if (target === false && value == null) return false;\n break;\n case '$regex':\n try {\n const re = new RegExp(target, condition.$options || '');\n if (!re.test(String(value))) return false;\n } catch (e) { return false; }\n break;\n\n default: \n // Unknown operator, ignore or fail. Ignoring safe for optional features.\n break;\n }\n }\n \n return true;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { InMemoryDriver } from './memory-driver.js';\n\nexport { InMemoryDriver }; // Export class for direct usage\nexport type { InMemoryDriverConfig, PersistenceAdapterInterface } from './memory-driver.js';\n\nexport { FileSystemPersistenceAdapter } from './persistence/file-adapter.js';\nexport { LocalStoragePersistenceAdapter } from './persistence/local-storage-adapter.js';\n\nexport { MemoryAnalyticsService } from './memory-analytics.js';\nexport type { MemoryAnalyticsConfig } from './memory-analytics.js';\n\nexport { InMemoryStrategy } from './in-memory-strategy.js';\n\nexport default {\n id: 'com.objectstack.driver.memory',\n version: '1.0.0',\n\n onEnable: async (context: any) => {\n const { logger, config, drivers } = context;\n logger.info('[Memory Driver] Initializing...');\n\n if (drivers) {\n const driver = new InMemoryDriver(config);\n drivers.register(driver);\n logger.info(`[Memory Driver] Registered driver: ${driver.name}`);\n } else {\n logger.warn('[Memory Driver] No driver registry found in context.');\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { IAnalyticsService, AnalyticsResult, CubeMeta } from '@objectstack/spec/contracts';\nimport type { Cube, AnalyticsQuery } from '@objectstack/spec/data';\nimport type { InMemoryDriver } from './memory-driver.js';\nimport { Logger, createLogger } from '@objectstack/core';\n\n/**\n * Configuration for MemoryAnalyticsService\n */\nexport interface MemoryAnalyticsConfig {\n /** The data driver instance to use for queries */\n driver: InMemoryDriver;\n /** Cube definitions for the semantic layer */\n cubes: Cube[];\n /** Optional logger */\n logger?: Logger;\n}\n\n/**\n * Memory-Based Analytics Service\n * \n * Implements IAnalyticsService using InMemoryDriver's aggregation capabilities.\n * Provides a semantic layer (Cubes, Metrics, Dimensions) on top of in-memory data.\n * \n * Features:\n * - Cube-based semantic modeling\n * - Measure calculations (count, sum, avg, min, max, count_distinct)\n * - Dimension grouping\n * - Filter support\n * - Time dimension handling\n * - SQL generation (for debugging/transparency)\n * \n * This implementation is suitable for:\n * - Development and testing\n * - Local-first analytics\n * - Small to medium datasets\n * - Prototyping BI applications\n */\nexport class MemoryAnalyticsService implements IAnalyticsService {\n private driver: InMemoryDriver;\n private cubes: Map<string, Cube>;\n private logger: Logger;\n\n constructor(config: MemoryAnalyticsConfig) {\n this.driver = config.driver;\n this.cubes = new Map(config.cubes.map(c => [c.name, c]));\n this.logger = config.logger || createLogger({ level: 'info', format: 'pretty' });\n this.logger.debug('MemoryAnalyticsService initialized', { cubeCount: this.cubes.size });\n }\n\n /**\n * Execute an analytical query using the memory driver's aggregation pipeline\n */\n async query(query: AnalyticsQuery): Promise<AnalyticsResult> {\n this.logger.debug('Executing analytics query', { cube: query.cube, measures: query.measures });\n\n // Get cube definition\n if (!query.cube) {\n throw new Error('Cube name is required');\n }\n const cube = this.cubes.get(query.cube);\n if (!cube) {\n throw new Error(`Cube not found: ${query.cube}`);\n }\n\n // Build MongoDB aggregation pipeline\n const pipeline: Record<string, any>[] = [];\n\n // Stage 1: $match for filters\n if (query.filters && query.filters.length > 0) {\n const matchStage: Record<string, any> = {};\n for (const filter of query.filters) {\n const mongoOp = this.convertOperatorToMongo(filter.operator);\n const fieldPath = this.resolveFieldPath(cube, filter.member);\n \n if (filter.values && filter.values.length > 0) {\n if (mongoOp === '$in') {\n matchStage[fieldPath] = { $in: filter.values };\n } else if (mongoOp === '$nin') {\n matchStage[fieldPath] = { $nin: filter.values };\n } else {\n matchStage[fieldPath] = { [mongoOp]: filter.values[0] };\n }\n } else if (mongoOp === '$exists') {\n matchStage[fieldPath] = { $exists: filter.operator === 'set' };\n }\n }\n if (Object.keys(matchStage).length > 0) {\n pipeline.push({ $match: matchStage });\n }\n }\n\n // Stage 2: Time dimension filters\n if (query.timeDimensions && query.timeDimensions.length > 0) {\n for (const timeDim of query.timeDimensions) {\n const fieldPath = this.resolveFieldPath(cube, timeDim.dimension);\n if (timeDim.dateRange) {\n const range = Array.isArray(timeDim.dateRange) \n ? timeDim.dateRange \n : this.parseDateRangeString(timeDim.dateRange);\n \n if (range.length === 2) {\n pipeline.push({\n $match: {\n [fieldPath]: {\n $gte: new Date(range[0]),\n $lte: new Date(range[1])\n }\n }\n });\n }\n }\n }\n }\n\n // Stage 3: $group for measures and dimensions\n const groupStage: Record<string, any> = { _id: {} };\n \n // Add dimensions to _id\n if (query.dimensions && query.dimensions.length > 0) {\n for (const dim of query.dimensions) {\n const fieldPath = this.resolveFieldPath(cube, dim);\n const dimName = this.getShortName(dim);\n groupStage._id[dimName] = `$${fieldPath}`;\n }\n } else {\n groupStage._id = null; // No grouping, aggregate all\n }\n\n // Add measures as computed fields\n if (query.measures && query.measures.length > 0) {\n for (const measure of query.measures) {\n const measureDef = this.resolveMeasure(cube, measure);\n const measureName = this.getShortName(measure);\n \n if (measureDef) {\n const aggregator = this.buildAggregator(measureDef);\n groupStage[measureName] = aggregator;\n }\n }\n }\n\n pipeline.push({ $group: groupStage });\n\n // Stage 4: $project to reshape results (use short names, we'll fix them later)\n const projectStage: Record<string, any> = { _id: 0 };\n if (query.dimensions && query.dimensions.length > 0) {\n for (const dim of query.dimensions) {\n const dimName = this.getShortName(dim);\n projectStage[dimName] = `$_id.${dimName}`;\n }\n }\n if (query.measures && query.measures.length > 0) {\n for (const measure of query.measures) {\n const measureName = this.getShortName(measure);\n projectStage[measureName] = `$${measureName}`;\n }\n }\n pipeline.push({ $project: projectStage });\n\n // Stage 5: $sort (use short names)\n if (query.order && Object.keys(query.order).length > 0) {\n const sortStage: Record<string, any> = {};\n for (const [field, direction] of Object.entries(query.order)) {\n const shortName = this.getShortName(field);\n sortStage[shortName] = direction === 'asc' ? 1 : -1;\n }\n pipeline.push({ $sort: sortStage });\n }\n\n // Stage 6: $limit and $skip\n if (query.offset) {\n pipeline.push({ $skip: query.offset });\n }\n if (query.limit) {\n pipeline.push({ $limit: query.limit });\n }\n\n // Execute the aggregation pipeline\n const tableName = this.extractTableName(cube.sql);\n const rawRows = await this.driver.aggregate(tableName, pipeline);\n\n // Rename fields from short names to full cube.field names\n const rows = rawRows.map(row => {\n const renamedRow: Record<string, unknown> = {};\n \n // Rename dimensions\n if (query.dimensions) {\n for (const dim of query.dimensions) {\n const shortName = this.getShortName(dim);\n if (shortName in row) {\n renamedRow[dim] = row[shortName];\n }\n }\n }\n \n // Rename measures\n if (query.measures) {\n for (const measure of query.measures) {\n const shortName = this.getShortName(measure);\n if (shortName in row) {\n renamedRow[measure] = row[shortName];\n }\n }\n }\n \n return renamedRow;\n });\n\n // Build field metadata\n const fields: Array<{ name: string; type: string }> = [];\n \n if (query.dimensions) {\n for (const dim of query.dimensions) {\n const dimension = this.resolveDimension(cube, dim);\n fields.push({\n name: dim,\n type: dimension?.type || 'string'\n });\n }\n }\n \n if (query.measures) {\n for (const measure of query.measures) {\n const measureDef = this.resolveMeasure(cube, measure);\n fields.push({\n name: measure,\n type: this.measureTypeToFieldType(measureDef?.type || 'count')\n });\n }\n }\n\n this.logger.debug('Analytics query completed', { rowCount: rows.length });\n\n return {\n rows,\n fields,\n sql: this.generateSqlFromPipeline(tableName, pipeline) // For debugging\n };\n }\n\n /**\n * Get available cube metadata for discovery\n */\n async getMeta(cubeName?: string): Promise<CubeMeta[]> {\n const cubes = cubeName \n ? [this.cubes.get(cubeName)].filter(Boolean) as Cube[]\n : Array.from(this.cubes.values());\n\n return cubes.map(cube => ({\n name: cube.name,\n title: cube.title,\n measures: Object.entries(cube.measures).map(([key, measure]) => ({\n name: `${cube.name}.${key}`,\n type: measure.type,\n title: measure.label\n })),\n dimensions: Object.entries(cube.dimensions).map(([key, dimension]) => ({\n name: `${cube.name}.${key}`,\n type: dimension.type,\n title: dimension.label\n }))\n }));\n }\n\n /**\n * Generate SQL representation for debugging/transparency\n */\n async generateSql(query: AnalyticsQuery): Promise<{ sql: string; params: unknown[] }> {\n if (!query.cube) {\n throw new Error('Cube name is required');\n }\n const cube = this.cubes.get(query.cube);\n if (!cube) {\n throw new Error(`Cube not found: ${query.cube}`);\n }\n\n const tableName = this.extractTableName(cube.sql);\n const selectClauses: string[] = [];\n const groupByClauses: string[] = [];\n\n // Build SELECT for dimensions\n if (query.dimensions && query.dimensions.length > 0) {\n for (const dim of query.dimensions) {\n const fieldPath = this.resolveFieldPath(cube, dim);\n selectClauses.push(`${fieldPath} AS \"${dim}\"`);\n groupByClauses.push(fieldPath);\n }\n }\n\n // Build SELECT for measures\n if (query.measures && query.measures.length > 0) {\n for (const measure of query.measures) {\n const measureDef = this.resolveMeasure(cube, measure);\n if (measureDef) {\n const aggSql = this.measureToSql(measureDef);\n selectClauses.push(`${aggSql} AS \"${measure}\"`);\n }\n }\n }\n\n // Build WHERE clause\n const whereClauses: string[] = [];\n if (query.filters && query.filters.length > 0) {\n for (const filter of query.filters) {\n const fieldPath = this.resolveFieldPath(cube, filter.member);\n const sqlOp = this.operatorToSql(filter.operator);\n if (filter.values && filter.values.length > 0) {\n whereClauses.push(`${fieldPath} ${sqlOp} '${filter.values[0]}'`);\n }\n }\n }\n\n let sql = `SELECT ${selectClauses.join(', ')} FROM ${tableName}`;\n if (whereClauses.length > 0) {\n sql += ` WHERE ${whereClauses.join(' AND ')}`;\n }\n if (groupByClauses.length > 0) {\n sql += ` GROUP BY ${groupByClauses.join(', ')}`;\n }\n if (query.order) {\n const orderClauses = Object.entries(query.order).map(([field, dir]) => \n `\"${field}\" ${dir.toUpperCase()}`\n );\n sql += ` ORDER BY ${orderClauses.join(', ')}`;\n }\n if (query.limit) {\n sql += ` LIMIT ${query.limit}`;\n }\n if (query.offset) {\n sql += ` OFFSET ${query.offset}`;\n }\n\n return { sql, params: [] };\n }\n\n // ===================================\n // Helper Methods\n // ===================================\n\n private resolveFieldPath(cube: Cube, member: string): string {\n // Handle both \"cube.field\" and \"field\" formats\n const parts = member.split('.');\n const fieldName = parts.length > 1 ? parts[1] : parts[0];\n\n // Check if it's a dimension\n const dimension = cube.dimensions[fieldName];\n if (dimension) {\n // Extract field path from SQL expression\n return dimension.sql.replace(/^\\$/, ''); // Remove $ prefix if present\n }\n\n // Check if it's a measure (for filters)\n const measure = cube.measures[fieldName];\n if (measure) {\n return measure.sql.replace(/^\\$/, '');\n }\n\n return fieldName;\n }\n\n private resolveMeasure(cube: Cube, measureName: string) {\n const parts = measureName.split('.');\n const fieldName = parts.length > 1 ? parts[1] : parts[0];\n const direct = cube.measures[fieldName];\n if (direct) return direct;\n\n // Accept `${field}_${type}` aliases (e.g. 'amount_sum') for measures whose\n // canonical name is just `${field}` (e.g. measure 'amount' of type 'sum').\n // This matches the convention used by the data-objectstack adapter and\n // other clients that build measure names from (field, function) pairs.\n const aggTypes = ['count', 'sum', 'avg', 'min', 'max', 'count_distinct'];\n for (const type of aggTypes) {\n const suffix = `_${type}`;\n if (fieldName.endsWith(suffix)) {\n const baseField = fieldName.slice(0, -suffix.length);\n const candidate = cube.measures[baseField];\n if (candidate && candidate.type === type) {\n return candidate;\n }\n }\n }\n return undefined;\n }\n\n private resolveDimension(cube: Cube, dimensionName: string) {\n const parts = dimensionName.split('.');\n const fieldName = parts.length > 1 ? parts[1] : parts[0];\n return cube.dimensions[fieldName];\n }\n\n private getShortName(fullName: string): string {\n const parts = fullName.split('.');\n return parts.length > 1 ? parts[1] : parts[0];\n }\n\n private buildAggregator(measure: { type: string; sql: string; filters?: any[] }): any {\n const fieldPath = measure.sql.replace(/^\\$/, '');\n\n switch (measure.type) {\n case 'count':\n return { $sum: 1 };\n case 'sum':\n return { $sum: `$${fieldPath}` };\n case 'avg':\n return { $avg: `$${fieldPath}` };\n case 'min':\n return { $min: `$${fieldPath}` };\n case 'max':\n return { $max: `$${fieldPath}` };\n case 'count_distinct':\n return { $addToSet: `$${fieldPath}` }; // Will need post-processing for count\n default:\n return { $sum: 1 }; // Default to count\n }\n }\n\n private measureTypeToFieldType(measureType: string): string {\n switch (measureType) {\n case 'count':\n case 'sum':\n case 'count_distinct':\n return 'number';\n case 'avg':\n case 'min':\n case 'max':\n return 'number';\n case 'string':\n return 'string';\n case 'boolean':\n return 'boolean';\n default:\n return 'number';\n }\n }\n\n private convertOperatorToMongo(operator: string): string {\n const opMap: Record<string, string> = {\n 'equals': '$eq',\n 'notEquals': '$ne',\n 'contains': '$regex',\n 'notContains': '$not',\n 'gt': '$gt',\n 'gte': '$gte',\n 'lt': '$lt',\n 'lte': '$lte',\n 'set': '$exists',\n 'notSet': '$exists',\n 'inDateRange': '$gte', // Will need special handling\n };\n return opMap[operator] || '$eq';\n }\n\n private operatorToSql(operator: string): string {\n const opMap: Record<string, string> = {\n 'equals': '=',\n 'notEquals': '!=',\n 'contains': 'LIKE',\n 'notContains': 'NOT LIKE',\n 'gt': '>',\n 'gte': '>=',\n 'lt': '<',\n 'lte': '<=',\n };\n return opMap[operator] || '=';\n }\n\n private measureToSql(measure: { type: string; sql: string }): string {\n const fieldPath = measure.sql.replace(/^\\$/, '');\n \n switch (measure.type) {\n case 'count':\n return 'COUNT(*)';\n case 'sum':\n return `SUM(${fieldPath})`;\n case 'avg':\n return `AVG(${fieldPath})`;\n case 'min':\n return `MIN(${fieldPath})`;\n case 'max':\n return `MAX(${fieldPath})`;\n case 'count_distinct':\n return `COUNT(DISTINCT ${fieldPath})`;\n default:\n return 'COUNT(*)';\n }\n }\n\n private extractTableName(sql: string): string {\n // For simple table names, return as-is\n // For complex SQL, this would need more sophisticated parsing\n return sql.trim();\n }\n\n private parseDateRangeString(range: string): string[] {\n // Simple parser for common date range strings\n // In production, this would use a proper date range parser\n const now = new Date();\n const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n \n if (range === 'today') {\n return [today.toISOString(), new Date(today.getTime() + 86400000).toISOString()];\n } else if (range.startsWith('last ')) {\n const parts = range.split(' ');\n const num = parseInt(parts[1]);\n const unit = parts[2];\n const start = new Date(today);\n \n if (unit.startsWith('day')) {\n start.setDate(start.getDate() - num);\n } else if (unit.startsWith('week')) {\n start.setDate(start.getDate() - num * 7);\n } else if (unit.startsWith('month')) {\n start.setMonth(start.getMonth() - num);\n } else if (unit.startsWith('year')) {\n start.setFullYear(start.getFullYear() - num);\n }\n \n return [start.toISOString(), now.toISOString()];\n }\n \n return [range, range]; // Fallback\n }\n\n private generateSqlFromPipeline(table: string, pipeline: Record<string, any>[]): string {\n // Simplified SQL generation for debugging\n // This is a basic representation of the aggregation pipeline\n const stages = pipeline.map((stage, idx) => {\n const op = Object.keys(stage)[0];\n return `/* Stage ${idx + 1}: ${op} */ ${JSON.stringify(stage[op])}`;\n }).join('\\n');\n \n return `-- MongoDB Aggregation Pipeline on table: ${table}\\n${stages}`;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { AnalyticsQuery, AnalyticsResult, AnalyticsStrategy, StrategyContext } from '@objectstack/spec/contracts';\n\n/**\n * InMemoryStrategy — Priority 3\n *\n * Delegates to an existing `IAnalyticsService` instance that was registered\n * as a fallback (typically `MemoryAnalyticsService` from this package).\n *\n * This is the lowest-priority strategy, used in:\n * - `dev` / `test` environments\n * - Any runtime where the backing driver is in-memory\n */\nexport class InMemoryStrategy implements AnalyticsStrategy {\n readonly name = 'InMemoryStrategy';\n readonly priority = 30;\n\n canHandle(query: AnalyticsQuery, ctx: StrategyContext): boolean {\n if (!query.cube) return false;\n // Can handle when a fallback service exists\n if (ctx.fallbackService) return true;\n // Or when the driver is flagged as in-memory\n const caps = ctx.queryCapabilities(query.cube);\n return caps.inMemory;\n }\n\n async execute(query: AnalyticsQuery, ctx: StrategyContext): Promise<AnalyticsResult> {\n if (!ctx.fallbackService) {\n throw new Error(\n `[InMemoryStrategy] No fallback analytics service available for cube \"${query.cube}\". ` +\n 'Register a MemoryAnalyticsService or configure a driver with analytics support.'\n );\n }\n return ctx.fallbackService.query(query);\n }\n\n async generateSql(query: AnalyticsQuery, ctx: StrategyContext): Promise<{ sql: string; params: unknown[] }> {\n if (ctx.fallbackService?.generateSql) {\n return ctx.fallbackService.generateSql(query);\n }\n return {\n sql: `-- InMemoryStrategy: SQL generation not supported for cube \"${query.cube}\"`,\n params: [],\n };\n }\n}\n"],"mappings":"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","names":["path","path","LocalStoragePersistenceAdapter","FileSystemPersistenceAdapter","createLogger"]}
1
+ {"version":3,"sources":["../src/persistence/local-storage-adapter.ts","../src/persistence/file-adapter.ts","../src/memory-driver.ts","../src/memory-matcher.ts","../src/index.ts","../src/memory-analytics.ts","../src/in-memory-strategy.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * LocalStoragePersistenceAdapter\n *\n * Persists the in-memory database to browser localStorage.\n * Synchronous storage with a ~5MB size limit warning.\n *\n * Browser only — will throw if used in non-browser environments.\n */\nexport class LocalStoragePersistenceAdapter {\n private readonly storageKey: string;\n private static readonly SIZE_WARNING_BYTES = 4.5 * 1024 * 1024; // 4.5MB warning threshold\n\n constructor(options?: { key?: string }) {\n this.storageKey = options?.key || 'objectstack:memory-db';\n }\n\n /**\n * Load persisted data from localStorage.\n * Returns null if no data exists.\n */\n async load(): Promise<Record<string, any[]> | null> {\n try {\n const raw = localStorage.getItem(this.storageKey);\n if (!raw) return null;\n return JSON.parse(raw) as Record<string, any[]>;\n } catch {\n return null;\n }\n }\n\n /**\n * Save data to localStorage.\n * Warns if data size approaches the ~5MB localStorage limit.\n */\n async save(db: Record<string, any[]>): Promise<void> {\n const json = JSON.stringify(db);\n\n if (json.length > LocalStoragePersistenceAdapter.SIZE_WARNING_BYTES) {\n console.warn(\n `[ObjectStack] localStorage persistence data size (${(json.length / 1024 / 1024).toFixed(2)}MB) ` +\n `is approaching the ~5MB limit. Consider using a different persistence strategy.`\n );\n }\n\n try {\n localStorage.setItem(this.storageKey, json);\n } catch (e: any) {\n console.error('[ObjectStack] Failed to persist data to localStorage:', e?.message || e);\n }\n }\n\n /**\n * Flush is a no-op for localStorage (writes are synchronous).\n */\n async flush(): Promise<void> {\n // localStorage writes are synchronous, no flushing needed\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\n\n/**\n * FileSystemPersistenceAdapter\n *\n * Persists the in-memory database to a JSON file on disk.\n * Supports atomic writes (write to temp file then rename) and auto-save with dirty tracking.\n *\n * Node.js only — will throw if used in non-Node.js environments.\n */\nexport class FileSystemPersistenceAdapter {\n private readonly filePath: string;\n private readonly autoSaveInterval: number;\n private dirty = false;\n private timer: ReturnType<typeof setInterval> | null = null;\n private currentDb: Record<string, any[]> | null = null;\n\n constructor(options?: { path?: string; autoSaveInterval?: number }) {\n this.filePath = options?.path || path.join('.objectstack', 'data', 'memory-driver.json');\n this.autoSaveInterval = options?.autoSaveInterval ?? 2000;\n }\n\n /**\n * Load persisted data from disk.\n * Returns null if no file exists.\n */\n async load(): Promise<Record<string, any[]> | null> {\n try {\n if (!fs.existsSync(this.filePath)) {\n return null;\n }\n const raw = fs.readFileSync(this.filePath, 'utf-8');\n const data = JSON.parse(raw);\n return data as Record<string, any[]>;\n } catch {\n return null;\n }\n }\n\n /**\n * Save data to disk using atomic write (temp file + rename).\n */\n async save(db: Record<string, any[]>): Promise<void> {\n this.currentDb = db;\n this.dirty = true;\n }\n\n /**\n * Flush pending writes to disk immediately.\n */\n async flush(): Promise<void> {\n if (!this.dirty || !this.currentDb) return;\n await this.writeToDisk(this.currentDb);\n this.dirty = false;\n }\n\n /**\n * Start the auto-save timer.\n */\n startAutoSave(): void {\n if (this.timer) return;\n this.timer = setInterval(async () => {\n if (this.dirty && this.currentDb) {\n await this.writeToDisk(this.currentDb);\n this.dirty = false;\n }\n }, this.autoSaveInterval);\n\n // Allow process to exit even if timer is running\n if (this.timer) {\n this.timer.unref();\n }\n }\n\n /**\n * Stop the auto-save timer and flush pending writes.\n */\n async stopAutoSave(): Promise<void> {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n await this.flush();\n }\n\n /**\n * Atomic write: write to temp file, then rename.\n */\n private async writeToDisk(db: Record<string, any[]>): Promise<void> {\n const dir = path.dirname(this.filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n const tmpPath = this.filePath + '.tmp';\n const json = JSON.stringify(db, null, 2);\n fs.writeFileSync(tmpPath, json, 'utf-8');\n fs.renameSync(tmpPath, this.filePath);\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { QueryAST, QueryInput, DriverOptions } from '@objectstack/spec/data';\nimport type { IDataDriver } from '@objectstack/spec/contracts';\nimport { Logger, createLogger } from '@objectstack/core';\nimport { Query, Aggregator } from 'mingo';\nimport { getValueByPath } from './memory-matcher.js';\n\n/**\n * Persistence adapter interface.\n * Matches the PersistenceAdapterSchema contract from @objectstack/spec.\n */\nexport interface PersistenceAdapterInterface {\n load(): Promise<Record<string, any[]> | null>;\n save(db: Record<string, any[]>): Promise<void>;\n flush(): Promise<void>;\n /** Optional: Start periodic auto-save (used by FileSystemPersistenceAdapter). */\n startAutoSave?(): void;\n /** Optional: Stop auto-save timer and flush pending writes. */\n stopAutoSave?(): Promise<void>;\n}\n\n/**\n * Configuration options for the InMemory driver.\n * Aligned with @objectstack/spec MemoryConfigSchema.\n */\nexport interface InMemoryDriverConfig {\n /** Optional: Initial data to populate the store */\n initialData?: Record<string, Record<string, unknown>[]>;\n /** Optional: Enable strict mode (throw on missing records) */\n strictMode?: boolean;\n /** Optional: Logger instance */\n logger?: Logger;\n /**\n * Persistence configuration. Defaults to `'auto'`.\n * - `'auto'` (default) — Auto-detect environment (browser → localStorage, Node.js → file, serverless → disabled)\n * - `'file'` — File-system persistence with defaults (Node.js only)\n * - `'local'` — localStorage persistence with defaults (Browser only)\n * - `{ type: 'file', path?: string, autoSaveInterval?: number }` — File-system with options\n * - `{ type: 'local', key?: string }` — localStorage with options\n * - `{ type: 'auto', path?: string, key?: string, autoSaveInterval?: number }` — Auto-detect with options\n * - `{ adapter: PersistenceAdapterInterface }` — Custom adapter\n * - `false` — Disable persistence (pure in-memory)\n *\n * ⚠️ In serverless environments (Vercel, AWS Lambda, Netlify, etc.),\n * auto mode disables file persistence to prevent silent data loss.\n * Use `persistence: false` or supply a custom adapter for serverless deployments.\n */\n persistence?: string | false | {\n type?: 'file' | 'local' | 'auto';\n path?: string;\n key?: string;\n autoSaveInterval?: number;\n adapter?: PersistenceAdapterInterface;\n };\n}\n\n/**\n * Snapshot for in-memory transactions.\n */\ninterface MemoryTransaction {\n id: string;\n snapshot: Record<string, any[]>;\n}\n\n/**\n * In-Memory Driver for ObjectStack\n * \n * A production-ready implementation of the ObjectStack Driver Protocol\n * powered by Mingo — a MongoDB-compatible query and aggregation engine.\n * \n * Features:\n * - MongoDB-compatible query engine (Mingo) for filtering, projection, aggregation\n * - Full CRUD and bulk operations\n * - Aggregation pipeline support ($match, $group, $sort, $project, $unwind, etc.)\n * - Snapshot-based transactions (begin/commit/rollback)\n * - Field projection and distinct values\n * - Strict mode and initial data loading\n * \n * Reference: objectql/packages/drivers/memory\n */\nexport class InMemoryDriver implements IDataDriver {\n readonly name = 'com.objectstack.driver.memory';\n type = 'driver';\n readonly version = '1.0.0';\n private config: InMemoryDriverConfig;\n private logger: Logger;\n private idCounters: Map<string, number> = new Map();\n private transactions: Map<string, MemoryTransaction> = new Map();\n private persistenceAdapter: PersistenceAdapterInterface | null = null;\n\n constructor(config?: InMemoryDriverConfig) {\n this.config = config || {};\n this.logger = config?.logger || createLogger({ level: 'info', format: 'pretty' });\n this.logger.debug('InMemory driver instance created');\n }\n\n // Duck-typed RuntimePlugin hook\n install(ctx: any) {\n this.logger.debug('Installing InMemory driver via plugin hook');\n if (ctx.engine && ctx.engine.ql && typeof ctx.engine.ql.registerDriver === 'function') {\n ctx.engine.ql.registerDriver(this);\n this.logger.info('InMemory driver registered with ObjectQL engine');\n } else {\n this.logger.warn('Could not register driver - ObjectQL engine not found in context');\n }\n }\n \n readonly supports = {\n // Basic CRUD Operations\n create: true,\n read: true,\n update: true,\n delete: true,\n\n // Bulk Operations\n bulkCreate: true,\n bulkUpdate: true,\n bulkDelete: true,\n\n // Transaction & Connection Management\n transactions: true, // Snapshot-based transactions\n savepoints: false,\n \n // Query Operations\n queryFilters: true, // Implemented via memory-matcher\n queryAggregations: true, // Implemented\n querySorting: true, // Implemented via JS sort\n queryPagination: true, // Implemented\n queryWindowFunctions: false, // @planned: Window functions (ROW_NUMBER, RANK, etc.)\n querySubqueries: false, // @planned: Subquery execution\n queryCTE: false,\n joins: false, // @planned: In-memory join operations\n \n // Advanced Features\n fullTextSearch: false, // @planned: Text tokenization + matching\n jsonQuery: false,\n geospatialQuery: false,\n streaming: true, // Implemented via findStream()\n jsonFields: true, // Native JS object support\n arrayFields: true, // Native JS array support\n vectorSearch: false, // @planned: Cosine similarity search\n\n // Schema Management\n schemaSync: true, // Implemented via syncSchema()\n batchSchemaSync: false,\n migrations: false,\n indexes: false,\n\n // Performance & Optimization\n connectionPooling: false,\n preparedStatements: false,\n queryCache: false,\n };\n\n /**\n * The \"Database\": A map of TableName -> Array of Records\n */\n private db: Record<string, any[]> = {};\n\n // ===================================\n // Lifecycle\n // ===================================\n\n async connect() {\n // Initialize persistence adapter if configured\n await this.initPersistence();\n\n // Load persisted data if available\n if (this.persistenceAdapter) {\n const persisted = await this.persistenceAdapter.load();\n if (persisted) {\n for (const [objectName, records] of Object.entries(persisted)) {\n this.db[objectName] = records;\n // Update ID counters based on persisted data\n for (const record of records) {\n if (record.id && typeof record.id === 'string') {\n // ID format: {objectName}-{timestamp}-{counter}\n const parts = record.id.split('-');\n const lastPart = parts[parts.length - 1];\n const counter = parseInt(lastPart, 10);\n if (!isNaN(counter)) {\n const current = this.idCounters.get(objectName) || 0;\n if (counter > current) {\n this.idCounters.set(objectName, counter);\n }\n }\n }\n }\n }\n this.logger.info('InMemory Database restored from persistence', {\n tables: Object.keys(persisted).length,\n });\n }\n }\n\n // Load initial data if provided\n if (this.config.initialData) {\n for (const [objectName, records] of Object.entries(this.config.initialData)) {\n const table = this.getTable(objectName);\n for (const record of records) {\n const id = (record as any).id || this.generateId(objectName);\n table.push({ ...record, id });\n }\n }\n this.logger.info('InMemory Database Connected with initial data', {\n tables: Object.keys(this.config.initialData).length,\n });\n } else {\n this.logger.info('InMemory Database Connected (Virtual)');\n }\n\n // Start auto-save if using file adapter\n if (this.persistenceAdapter?.startAutoSave) {\n this.persistenceAdapter.startAutoSave();\n }\n }\n\n async disconnect() {\n // Stop auto-save and flush pending writes\n if (this.persistenceAdapter) {\n if (this.persistenceAdapter.stopAutoSave) {\n await this.persistenceAdapter.stopAutoSave();\n }\n await this.persistenceAdapter.flush();\n }\n\n const tableCount = Object.keys(this.db).length;\n const recordCount = Object.values(this.db).reduce((sum, table) => sum + table.length, 0);\n \n this.db = {};\n this.logger.info('InMemory Database Disconnected & Cleared', { \n tableCount, \n recordCount \n });\n }\n\n async checkHealth() {\n this.logger.debug('Health check performed', { \n tableCount: Object.keys(this.db).length,\n status: 'healthy' \n });\n return true; \n }\n\n // ===================================\n // Execution\n // ===================================\n\n async execute(command: any, params?: any[]) {\n this.logger.warn('Raw execution not supported in InMemory driver', { command });\n return null;\n }\n\n // ===================================\n // CRUD\n // ===================================\n\n async find(object: string, query: QueryAST, options?: DriverOptions) {\n this.logger.debug('Find operation', { object, query });\n \n const table = this.getTable(object);\n let results = [...table]; // Work on copy\n\n // 1. Filter using Mingo\n if (query.where) {\n const mongoQuery = this.convertToMongoQuery(query.where);\n if (mongoQuery && Object.keys(mongoQuery).length > 0) {\n const mingoQuery = new Query(mongoQuery);\n results = mingoQuery.find(results).all();\n }\n }\n\n // 1.5 Aggregation & Grouping\n if (query.groupBy || (query.aggregations && query.aggregations.length > 0)) {\n results = this.performAggregation(results, query);\n }\n\n // 2. Sort\n if (query.orderBy) {\n const sortFields = Array.isArray(query.orderBy) ? query.orderBy : [query.orderBy];\n results = this.applySort(results, sortFields);\n }\n\n // 3. Pagination (Offset)\n if (query.offset) {\n results = results.slice(query.offset);\n }\n\n // 4. Pagination (Limit)\n if (query.limit) {\n results = results.slice(0, query.limit);\n }\n\n // 5. Field Projection\n if (query.fields && Array.isArray(query.fields) && query.fields.length > 0) {\n results = results.map(record => this.projectFields(record, query.fields as string[]));\n }\n\n this.logger.debug('Find completed', { object, resultCount: results.length });\n return results;\n }\n\n async *findStream(object: string, query: QueryAST, options?: DriverOptions) {\n this.logger.debug('FindStream operation', { object });\n \n const results = await this.find(object, query, options);\n for (const record of results) {\n yield record;\n }\n }\n\n async findOne(object: string, query: QueryAST, options?: DriverOptions) {\n this.logger.debug('FindOne operation', { object, query });\n \n const results = await this.find(object, { ...query, limit: 1 }, options);\n const result = results[0] || null;\n \n this.logger.debug('FindOne completed', { object, found: !!result });\n return result;\n }\n\n async create(object: string, data: Record<string, any>, options?: DriverOptions) {\n this.logger.debug('Create operation', { object, hasData: !!data });\n \n const table = this.getTable(object);\n \n const newRecord = {\n id: data.id || this.generateId(object),\n ...data,\n created_at: data.created_at || new Date().toISOString(),\n updated_at: data.updated_at || new Date().toISOString(),\n };\n\n table.push(newRecord);\n this.markDirty();\n this.logger.debug('Record created', { object, id: newRecord.id, tableSize: table.length });\n return { ...newRecord };\n }\n\n async update(object: string, id: string | number, data: Record<string, any>, options?: DriverOptions) {\n this.logger.debug('Update operation', { object, id });\n \n const table = this.getTable(object);\n const index = table.findIndex(r => r.id == id);\n \n if (index === -1) {\n if (this.config.strictMode) {\n this.logger.warn('Record not found for update', { object, id });\n throw new Error(`Record with ID ${id} not found in ${object}`);\n }\n return null;\n }\n\n const updatedRecord = {\n ...table[index],\n ...data,\n id: table[index].id, // Preserve original ID\n created_at: table[index].created_at, // Preserve created_at\n updated_at: new Date().toISOString(),\n };\n \n table[index] = updatedRecord;\n this.markDirty();\n this.logger.debug('Record updated', { object, id });\n return { ...updatedRecord };\n }\n\n async upsert(object: string, data: Record<string, any>, conflictKeys?: string[], options?: DriverOptions) {\n this.logger.debug('Upsert operation', { object, conflictKeys });\n \n const table = this.getTable(object);\n let existingRecord: any = null;\n\n if (data.id) {\n existingRecord = table.find(r => r.id === data.id);\n } else if (conflictKeys && conflictKeys.length > 0) {\n existingRecord = table.find(r => conflictKeys.every(key => r[key] === data[key]));\n }\n\n if (existingRecord) {\n this.logger.debug('Record exists, updating', { object, id: existingRecord.id });\n return this.update(object, existingRecord.id, data, options);\n } else {\n this.logger.debug('Record does not exist, creating', { object });\n return this.create(object, data, options);\n }\n }\n\n async delete(object: string, id: string | number, options?: DriverOptions) {\n this.logger.debug('Delete operation', { object, id });\n \n const table = this.getTable(object);\n const index = table.findIndex(r => r.id == id);\n \n if (index === -1) {\n if (this.config.strictMode) {\n throw new Error(`Record with ID ${id} not found in ${object}`);\n }\n this.logger.warn('Record not found for deletion', { object, id });\n return false;\n }\n\n table.splice(index, 1);\n this.markDirty();\n this.logger.debug('Record deleted', { object, id, tableSize: table.length });\n return true;\n }\n\n async count(object: string, query?: QueryAST, options?: DriverOptions) {\n let records = this.getTable(object);\n if (query?.where) {\n const mongoQuery = this.convertToMongoQuery(query.where);\n if (mongoQuery && Object.keys(mongoQuery).length > 0) {\n const mingoQuery = new Query(mongoQuery);\n records = mingoQuery.find(records).all();\n }\n }\n const count = records.length;\n this.logger.debug('Count operation', { object, count });\n return count;\n }\n\n // ===================================\n // Bulk Operations\n // ===================================\n\n async bulkCreate(object: string, dataArray: Record<string, any>[], options?: DriverOptions) {\n this.logger.debug('BulkCreate operation', { object, count: dataArray.length });\n const results = await Promise.all(dataArray.map(data => this.create(object, data, options)));\n this.logger.debug('BulkCreate completed', { object, count: results.length });\n return results;\n }\n \n async updateMany(object: string, query: QueryAST, data: Record<string, any>, options?: DriverOptions): Promise<number> {\n this.logger.debug('UpdateMany operation', { object, query });\n \n const table = this.getTable(object);\n let targetRecords = table;\n \n if (query && query.where) {\n const mongoQuery = this.convertToMongoQuery(query.where);\n if (mongoQuery && Object.keys(mongoQuery).length > 0) {\n const mingoQuery = new Query(mongoQuery);\n targetRecords = mingoQuery.find(targetRecords).all();\n }\n }\n \n const count = targetRecords.length;\n \n for (const record of targetRecords) {\n const index = table.findIndex(r => r.id === record.id);\n if (index !== -1) {\n const updated = {\n ...table[index],\n ...data,\n updated_at: new Date().toISOString()\n };\n table[index] = updated;\n }\n }\n \n if (count > 0) this.markDirty();\n this.logger.debug('UpdateMany completed', { object, count });\n return count;\n }\n\n async deleteMany(object: string, query: QueryAST, options?: DriverOptions): Promise<number> {\n this.logger.debug('DeleteMany operation', { object, query });\n \n const table = this.getTable(object);\n const initialLength = table.length;\n \n if (query && query.where) {\n const mongoQuery = this.convertToMongoQuery(query.where);\n if (mongoQuery && Object.keys(mongoQuery).length > 0) {\n const mingoQuery = new Query(mongoQuery);\n const matched = mingoQuery.find(table).all();\n const matchedIds = new Set(matched.map((r: any) => r.id));\n this.db[object] = table.filter(r => !matchedIds.has(r.id));\n } else {\n // Empty query = delete all\n this.db[object] = [];\n }\n } else {\n // No where clause = delete all\n this.db[object] = [];\n }\n \n const count = initialLength - this.db[object].length;\n if (count > 0) this.markDirty();\n this.logger.debug('DeleteMany completed', { object, count });\n return count;\n }\n\n // Compatibility aliases\n async bulkUpdate(object: string, updates: { id: string | number, data: Record<string, any> }[], options?: DriverOptions) {\n this.logger.debug('BulkUpdate operation', { object, count: updates.length });\n const results = await Promise.all(updates.map(u => this.update(object, u.id, u.data, options)));\n this.logger.debug('BulkUpdate completed', { object, count: results.length });\n return results;\n }\n\n async bulkDelete(object: string, ids: (string | number)[], options?: DriverOptions) {\n this.logger.debug('BulkDelete operation', { object, count: ids.length });\n await Promise.all(ids.map(id => this.delete(object, id, options)));\n this.logger.debug('BulkDelete completed', { object, count: ids.length });\n }\n\n // ===================================\n // Transaction Management\n // ===================================\n\n async beginTransaction() {\n const txId = `tx_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;\n\n // Deep-clone current database state as a snapshot\n const snapshot: Record<string, any[]> = {};\n for (const [table, records] of Object.entries(this.db)) {\n snapshot[table] = records.map(r => ({ ...r }));\n }\n\n const transaction: MemoryTransaction = { id: txId, snapshot };\n this.transactions.set(txId, transaction);\n this.logger.debug('Transaction started', { txId });\n return { id: txId };\n }\n\n async commit(txHandle?: unknown) {\n const txId = (txHandle as any)?.id;\n if (!txId || !this.transactions.has(txId)) {\n this.logger.warn('Commit called with unknown transaction');\n return;\n }\n // Data is already in the store; just remove the snapshot\n this.transactions.delete(txId);\n this.logger.debug('Transaction committed', { txId });\n }\n\n async rollback(txHandle?: unknown) {\n const txId = (txHandle as any)?.id;\n if (!txId || !this.transactions.has(txId)) {\n this.logger.warn('Rollback called with unknown transaction');\n return;\n }\n const tx = this.transactions.get(txId)!;\n // Restore the snapshot\n this.db = tx.snapshot;\n this.transactions.delete(txId);\n this.markDirty();\n this.logger.debug('Transaction rolled back', { txId });\n }\n\n // ===================================\n // Utility Methods\n // ===================================\n\n /**\n * Remove all data from the store.\n */\n async clear() {\n this.db = {};\n this.idCounters.clear();\n this.markDirty();\n this.logger.debug('All data cleared');\n }\n\n /**\n * Get total number of records across all tables.\n */\n getSize(): number {\n return Object.values(this.db).reduce((sum, table) => sum + table.length, 0);\n }\n\n /**\n * Get distinct values for a field, optionally filtered.\n */\n async distinct(object: string, field: string, query?: QueryInput): Promise<any[]> {\n let records = this.getTable(object);\n if (query?.where) {\n const mongoQuery = this.convertToMongoQuery(query.where);\n if (mongoQuery && Object.keys(mongoQuery).length > 0) {\n const mingoQuery = new Query(mongoQuery);\n records = mingoQuery.find(records).all();\n }\n }\n const values = new Set<any>();\n for (const record of records) {\n const value = getValueByPath(record, field);\n if (value !== undefined && value !== null) {\n values.add(value);\n }\n }\n return Array.from(values);\n }\n\n /**\n * Execute a MongoDB-style aggregation pipeline using Mingo.\n * \n * Supports all standard MongoDB pipeline stages:\n * - $match, $group, $sort, $project, $unwind, $limit, $skip\n * - $addFields, $replaceRoot, $lookup (limited), $count\n * - Accumulator operators: $sum, $avg, $min, $max, $first, $last, $push, $addToSet\n * \n * @example\n * // Group by status and count\n * const results = await driver.aggregate('orders', [\n * { $match: { status: 'completed' } },\n * { $group: { _id: '$customer', totalAmount: { $sum: '$amount' } } }\n * ]);\n * \n * @example\n * // Calculate average with filter\n * const results = await driver.aggregate('products', [\n * { $match: { category: 'electronics' } },\n * { $group: { _id: null, avgPrice: { $avg: '$price' } } }\n * ]);\n */\n async aggregate(object: string, pipeline: Record<string, any>[], options?: DriverOptions): Promise<any[]> {\n this.logger.debug('Aggregate operation', { object, stageCount: pipeline.length });\n \n const records = this.getTable(object).map(r => ({ ...r }));\n const aggregator = new Aggregator(pipeline);\n const results = aggregator.run(records);\n \n this.logger.debug('Aggregate completed', { object, resultCount: results.length });\n return results;\n }\n\n // ===================================\n // Query Conversion (ObjectQL → MongoDB)\n // ===================================\n\n /**\n * Convert ObjectQL filter format to MongoDB query format for Mingo.\n * \n * Supports:\n * 1. AST Comparison Node: { type: 'comparison', field, operator, value }\n * 2. AST Logical Node: { type: 'logical', operator: 'and'|'or', conditions: [...] }\n * 3. Legacy Array Format: [['field', 'op', value], 'and', ['field2', 'op', value2]]\n * 4. MongoDB Format: { field: value } or { field: { $eq: value } } (passthrough)\n */\n private convertToMongoQuery(filters?: any): Record<string, any> {\n if (!filters) return {};\n\n // AST node format (ObjectQL QueryAST)\n if (!Array.isArray(filters) && typeof filters === 'object') {\n if (filters.type === 'comparison') {\n return this.convertConditionToMongo(filters.field, filters.operator, filters.value) || {};\n }\n if (filters.type === 'logical') {\n const conditions = filters.conditions?.map((c: any) => this.convertToMongoQuery(c)) || [];\n if (conditions.length === 0) return {};\n if (conditions.length === 1) return conditions[0];\n const op = filters.operator === 'or' ? '$or' : '$and';\n return { [op]: conditions };\n }\n // MongoDB/FilterCondition format: { field: value } or { field: { $op: value } }\n // Translate non-standard operators ($contains, $notContains, etc.) to Mingo-compatible format\n return this.normalizeFilterCondition(filters);\n }\n\n // Legacy array format\n if (!Array.isArray(filters) || filters.length === 0) return {};\n\n const logicGroups: { logic: 'and' | 'or'; conditions: Record<string, any>[] }[] = [\n { logic: 'and', conditions: [] },\n ];\n let currentLogic: 'and' | 'or' = 'and';\n\n for (const item of filters) {\n if (typeof item === 'string') {\n const newLogic = item.toLowerCase() as 'and' | 'or';\n if (newLogic !== currentLogic) {\n currentLogic = newLogic;\n logicGroups.push({ logic: currentLogic, conditions: [] });\n }\n } else if (Array.isArray(item)) {\n const [field, operator, value] = item;\n const cond = this.convertConditionToMongo(field, operator, value);\n if (cond) logicGroups[logicGroups.length - 1].conditions.push(cond);\n }\n }\n\n const allConditions: Record<string, any>[] = [];\n for (const group of logicGroups) {\n if (group.conditions.length === 0) continue;\n if (group.conditions.length === 1) {\n allConditions.push(group.conditions[0]);\n } else {\n const op = group.logic === 'or' ? '$or' : '$and';\n allConditions.push({ [op]: group.conditions });\n }\n }\n\n if (allConditions.length === 0) return {};\n if (allConditions.length === 1) return allConditions[0];\n return { $and: allConditions };\n }\n\n /**\n * Convert a single ObjectQL condition to MongoDB operator format.\n */\n private convertConditionToMongo(field: string, operator: string, value: any): Record<string, any> | null {\n switch (operator) {\n case '=': case '==':\n return { [field]: value };\n case '!=': case '<>':\n return { [field]: { $ne: value } };\n case '>':\n return { [field]: { $gt: value } };\n case '>=':\n return { [field]: { $gte: value } };\n case '<':\n return { [field]: { $lt: value } };\n case '<=':\n return { [field]: { $lte: value } };\n case 'in':\n return { [field]: { $in: value } };\n case 'nin': case 'not in':\n return { [field]: { $nin: value } };\n case 'contains': case 'like':\n return { [field]: { $regex: new RegExp(this.escapeRegex(value), 'i') } };\n case 'notcontains': case 'not_contains':\n return { [field]: { $not: { $regex: new RegExp(this.escapeRegex(value), 'i') } } };\n case 'startswith': case 'starts_with':\n return { [field]: { $regex: new RegExp(`^${this.escapeRegex(value)}`, 'i') } };\n case 'endswith': case 'ends_with':\n return { [field]: { $regex: new RegExp(`${this.escapeRegex(value)}$`, 'i') } };\n case 'between':\n if (Array.isArray(value) && value.length === 2) {\n return { [field]: { $gte: value[0], $lte: value[1] } };\n }\n return null;\n default:\n return null;\n }\n }\n\n /**\n * Normalize a FilterCondition object by converting non-standard $-prefixed\n * operators ($contains, $notContains, $startsWith, $endsWith, $between, $null)\n * to Mingo-compatible equivalents ($regex, $gte/$lte, null checks).\n */\n private normalizeFilterCondition(filter: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n const extraAndConditions: Record<string, any>[] = [];\n\n for (const key of Object.keys(filter)) {\n const value = filter[key];\n // Recurse into logical operators\n if (key === '$and' || key === '$or') {\n result[key] = Array.isArray(value)\n ? value.map((child: any) => this.normalizeFilterCondition(child))\n : value;\n continue;\n }\n if (key === '$not') {\n result[key] = value && typeof value === 'object'\n ? this.normalizeFilterCondition(value)\n : value;\n continue;\n }\n // Skip $-prefixed keys that aren't field names (already handled or unknown)\n if (key.startsWith('$')) {\n result[key] = value;\n continue;\n }\n // Field-level: value may be primitive (implicit eq) or operator object\n if (value && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date) && !(value instanceof RegExp)) {\n const normalized = this.normalizeFieldOperators(value);\n // Handle multiple regex conditions on the same field (e.g. $startsWith + $endsWith)\n if (normalized._multiRegex) {\n const regexConditions: Record<string, any>[] = normalized._multiRegex;\n delete normalized._multiRegex;\n // Each regex becomes its own { field: { $regex: ... } } inside $and\n for (const rc of regexConditions) {\n extraAndConditions.push({ [key]: { ...normalized, ...rc } });\n }\n } else {\n result[key] = normalized;\n }\n } else {\n result[key] = value;\n }\n }\n\n // Merge extra $and conditions from multi-regex fields\n if (extraAndConditions.length > 0) {\n const existing = result.$and;\n const andArray = Array.isArray(existing) ? existing : [];\n // Include the rest of result as a condition too\n if (Object.keys(result).filter(k => k !== '$and').length > 0) {\n const rest = { ...result };\n delete rest.$and;\n andArray.push(rest);\n }\n andArray.push(...extraAndConditions);\n return { $and: andArray };\n }\n\n return result;\n }\n\n /**\n * Convert non-standard field operators to Mingo-compatible format.\n * When multiple regex-producing operators appear on the same field\n * (e.g. $startsWith + $endsWith), they are combined via $and.\n */\n private normalizeFieldOperators(ops: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n const regexConditions: Record<string, any>[] = [];\n\n for (const op of Object.keys(ops)) {\n const val = ops[op];\n switch (op) {\n case '$contains':\n regexConditions.push({ $regex: new RegExp(this.escapeRegex(val), 'i') });\n break;\n case '$notContains':\n result.$not = { $regex: new RegExp(this.escapeRegex(val), 'i') };\n break;\n case '$startsWith':\n regexConditions.push({ $regex: new RegExp(`^${this.escapeRegex(val)}`, 'i') });\n break;\n case '$endsWith':\n regexConditions.push({ $regex: new RegExp(`${this.escapeRegex(val)}$`, 'i') });\n break;\n case '$between':\n if (Array.isArray(val) && val.length === 2) {\n result.$gte = val[0];\n result.$lte = val[1];\n }\n break;\n case '$null':\n // $null: true → field is null, $null: false → field is not null\n // Use $eq/$ne null for Mingo compatibility\n if (val === true) {\n result.$eq = null;\n } else {\n result.$ne = null;\n }\n break;\n default:\n result[op] = val;\n break;\n }\n }\n\n // Merge regex conditions: single → inline, multiple → wrap with $and\n if (regexConditions.length === 1) {\n Object.assign(result, regexConditions[0]);\n } else if (regexConditions.length > 1) {\n // Cannot have multiple $regex on one object; promote to top-level $and.\n // _multiRegex is an internal sentinel consumed by normalizeFilterCondition().\n result._multiRegex = regexConditions;\n }\n\n return result;\n }\n\n /**\n * Escape special regex characters for safe literal matching.\n */\n private escapeRegex(str: string): string {\n return String(str).replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n }\n\n // ===================================\n // Aggregation Logic\n // ===================================\n\n private performAggregation(records: any[], query: QueryInput): any[] {\n const { groupBy, aggregations } = query;\n const groups: Map<string, any[]> = new Map();\n\n const normalizeGroupBy = (node: any): { field: string; alias: string } => {\n if (typeof node === 'string') return { field: node, alias: node };\n return { field: node.field, alias: node.alias ?? node.field };\n };\n\n // 1. Group records\n if (groupBy && groupBy.length > 0) {\n for (const record of records) {\n // Create a composite key from group values\n const keyParts = groupBy.map(node => {\n const { field } = normalizeGroupBy(node);\n const val = getValueByPath(record, field);\n return val === undefined || val === null ? 'null' : String(val);\n });\n const key = JSON.stringify(keyParts);\n \n if (!groups.has(key)) {\n groups.set(key, []);\n }\n groups.get(key)!.push(record);\n }\n } else {\n groups.set('all', records);\n }\n\n // 2. Compute aggregates for each group\n const resultRows: any[] = [];\n \n for (const [_key, groupRecords] of groups.entries()) {\n const row: any = {};\n \n // A. Add Group fields to row (if groupBy exists)\n if (groupBy && groupBy.length > 0) {\n if (groupRecords.length > 0) {\n const firstRecord = groupRecords[0];\n for (const node of groupBy) {\n const { field, alias } = normalizeGroupBy(node);\n this.setValueByPath(row, alias, getValueByPath(firstRecord, field));\n }\n }\n }\n \n // B. Compute Aggregations\n if (aggregations) {\n for (const agg of aggregations) {\n const value = this.computeAggregate(groupRecords, agg);\n row[agg.alias] = value;\n }\n }\n \n resultRows.push(row);\n }\n \n return resultRows;\n }\n \n private computeAggregate(records: any[], agg: any): any {\n const { function: func, field } = agg;\n \n const values = field ? records.map(r => getValueByPath(r, field)) : [];\n \n switch (func) {\n case 'count':\n if (!field || field === '*') return records.length;\n return values.filter(v => v !== null && v !== undefined).length;\n \n case 'sum':\n case 'avg': {\n const nums = values.filter(v => typeof v === 'number');\n const sum = nums.reduce((a, b) => a + b, 0);\n if (func === 'sum') return sum;\n return nums.length > 0 ? sum / nums.length : null;\n }\n \n case 'min': {\n // Handle comparable values\n const valid = values.filter(v => v !== null && v !== undefined);\n if (valid.length === 0) return null;\n // Works for numbers and strings\n return valid.reduce((min, v) => (v < min ? v : min), valid[0]);\n }\n\n case 'max': {\n const valid = values.filter(v => v !== null && v !== undefined);\n if (valid.length === 0) return null;\n return valid.reduce((max, v) => (v > max ? v : max), valid[0]);\n }\n\n default:\n return null;\n }\n }\n\n private setValueByPath(obj: any, path: string, value: any) {\n const parts = path.split('.');\n let current = obj;\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (!current[part]) current[part] = {};\n current = current[part];\n }\n current[parts[parts.length - 1]] = value;\n }\n\n // ===================================\n // Schema Management\n // ===================================\n\n async syncSchema(object: string, schema: any, options?: DriverOptions) {\n if (!this.db[object]) {\n this.db[object] = [];\n this.logger.info('Created in-memory table', { object });\n }\n }\n\n async dropTable(object: string, options?: DriverOptions) {\n if (this.db[object]) {\n const recordCount = this.db[object].length;\n delete this.db[object];\n this.logger.info('Dropped in-memory table', { object, recordCount });\n }\n }\n\n // ===================================\n // Helpers\n // ===================================\n\n /**\n * Apply manual sorting (Mingo sort has CJS build issues).\n */\n private applySort(records: any[], sortFields: any[]): any[] {\n const sorted = [...records];\n for (let i = sortFields.length - 1; i >= 0; i--) {\n const sortItem = sortFields[i];\n let field: string;\n let direction: string;\n if (typeof sortItem === 'object' && !Array.isArray(sortItem)) {\n field = sortItem.field;\n direction = sortItem.order || sortItem.direction || 'asc';\n } else if (Array.isArray(sortItem)) {\n [field, direction] = sortItem;\n } else {\n continue;\n }\n sorted.sort((a, b) => {\n const aVal = getValueByPath(a, field);\n const bVal = getValueByPath(b, field);\n if (aVal == null && bVal == null) return 0;\n if (aVal == null) return 1;\n if (bVal == null) return -1;\n if (aVal < bVal) return direction === 'desc' ? 1 : -1;\n if (aVal > bVal) return direction === 'desc' ? -1 : 1;\n return 0;\n });\n }\n return sorted;\n }\n\n /**\n * Project specific fields from a record.\n */\n private projectFields(record: any, fields: string[]): any {\n const result: any = {};\n for (const field of fields) {\n const value = getValueByPath(record, field);\n if (value !== undefined) {\n result[field] = value;\n }\n }\n // Always include id if not explicitly listed\n if (!fields.includes('id') && record.id !== undefined) {\n result.id = record.id;\n }\n return result;\n }\n\n private getTable(name: string) {\n if (!this.db[name]) {\n this.db[name] = [];\n }\n return this.db[name];\n }\n\n private generateId(objectName?: string) {\n const key = objectName || '_global';\n const counter = (this.idCounters.get(key) || 0) + 1;\n this.idCounters.set(key, counter);\n const timestamp = Date.now();\n return `${key}-${timestamp}-${counter}`;\n }\n\n // ===================================\n // Persistence\n // ===================================\n\n /**\n * Mark the database as dirty, triggering persistence save.\n */\n private markDirty(): void {\n if (this.persistenceAdapter) {\n this.persistenceAdapter.save(this.db);\n }\n }\n\n /**\n * Flush pending persistence writes to ensure data is safely stored.\n */\n async flush(): Promise<void> {\n if (this.persistenceAdapter) {\n await this.persistenceAdapter.flush();\n }\n }\n\n /**\n * Detect whether the current runtime is a browser environment.\n * Checks for window, document AND a functional localStorage to avoid\n * false positives in Node.js runtimes that partially polyfill globals.\n */\n private isBrowserEnvironment(): boolean {\n const g = globalThis as any;\n return typeof g.window !== 'undefined'\n && typeof g.document !== 'undefined'\n && typeof g.localStorage?.setItem === 'function';\n }\n\n /**\n * Detect whether the current runtime is a serverless/edge environment.\n *\n * Checks well-known environment variables set by serverless platforms:\n * - `VERCEL` / `VERCEL_ENV` — Vercel Functions / Edge\n * - `AWS_LAMBDA_FUNCTION_NAME` — AWS Lambda\n * - `NETLIFY` — Netlify Functions\n * - `FUNCTIONS_WORKER_RUNTIME` — Azure Functions\n * - `K_SERVICE` — Google Cloud Run / Cloud Functions\n * - `FUNCTION_TARGET` — Google Cloud Functions (Node.js)\n * - `DENO_DEPLOYMENT_ID` — Deno Deploy\n *\n * Returns `false` when `process` or `process.env` is unavailable\n * (e.g. browser or edge runtimes without a Node.js process object).\n */\n private isServerlessEnvironment(): boolean {\n if (typeof globalThis.process === 'undefined' || !globalThis.process.env) {\n return false;\n }\n const env = globalThis.process.env;\n return !!(\n env.VERCEL ||\n env.VERCEL_ENV ||\n env.AWS_LAMBDA_FUNCTION_NAME ||\n env.NETLIFY ||\n env.FUNCTIONS_WORKER_RUNTIME ||\n env.K_SERVICE ||\n env.FUNCTION_TARGET ||\n env.DENO_DEPLOYMENT_ID\n );\n }\n\n private static readonly SERVERLESS_PERSISTENCE_WARNING =\n 'Serverless environment detected — file-system persistence is disabled in auto mode. ' +\n 'Data will NOT be persisted across function invocations. ' +\n 'Set persistence: false to silence this warning, or provide a custom adapter ' +\n '(e.g. Upstash Redis, Vercel KV) via persistence: { adapter: yourAdapter }.';\n\n /**\n * Initialize the persistence adapter based on configuration.\n * Defaults to 'auto' when persistence is not specified.\n * Use `persistence: false` to explicitly disable persistence.\n *\n * In serverless environments (Vercel, AWS Lambda, etc.), auto mode disables\n * file-system persistence and emits a warning. Use `persistence: false` or\n * supply a custom adapter for serverless-safe operation.\n */\n private async initPersistence(): Promise<void> {\n const persistence = this.config.persistence === undefined ? 'auto' : this.config.persistence;\n if (persistence === false) return;\n\n if (typeof persistence === 'string') {\n if (persistence === 'auto') {\n if (this.isBrowserEnvironment()) {\n const { LocalStoragePersistenceAdapter } = await import('./persistence/local-storage-adapter.js');\n this.persistenceAdapter = new LocalStoragePersistenceAdapter();\n this.logger.debug('Auto-detected browser environment, using localStorage persistence');\n } else if (this.isServerlessEnvironment()) {\n this.logger.warn(InMemoryDriver.SERVERLESS_PERSISTENCE_WARNING);\n } else {\n const { FileSystemPersistenceAdapter } = await import('./persistence/file-adapter.js');\n this.persistenceAdapter = new FileSystemPersistenceAdapter();\n this.logger.debug('Auto-detected Node.js environment, using file persistence');\n }\n } else if (persistence === 'file') {\n const { FileSystemPersistenceAdapter } = await import('./persistence/file-adapter.js');\n this.persistenceAdapter = new FileSystemPersistenceAdapter();\n } else if (persistence === 'local') {\n const { LocalStoragePersistenceAdapter } = await import('./persistence/local-storage-adapter.js');\n this.persistenceAdapter = new LocalStoragePersistenceAdapter();\n } else {\n throw new Error(`Unknown persistence type: \"${persistence}\". Use 'file', 'local', or 'auto'.`);\n }\n } else if ('adapter' in persistence && persistence.adapter) {\n this.persistenceAdapter = persistence.adapter;\n } else if ('type' in persistence) {\n if (persistence.type === 'auto') {\n if (this.isBrowserEnvironment()) {\n const { LocalStoragePersistenceAdapter } = await import('./persistence/local-storage-adapter.js');\n this.persistenceAdapter = new LocalStoragePersistenceAdapter({\n key: persistence.key,\n });\n this.logger.debug('Auto-detected browser environment, using localStorage persistence');\n } else if (this.isServerlessEnvironment()) {\n this.logger.warn(InMemoryDriver.SERVERLESS_PERSISTENCE_WARNING);\n } else {\n const { FileSystemPersistenceAdapter } = await import('./persistence/file-adapter.js');\n this.persistenceAdapter = new FileSystemPersistenceAdapter({\n path: persistence.path,\n autoSaveInterval: persistence.autoSaveInterval,\n });\n this.logger.debug('Auto-detected Node.js environment, using file persistence');\n }\n } else if (persistence.type === 'file') {\n const { FileSystemPersistenceAdapter } = await import('./persistence/file-adapter.js');\n this.persistenceAdapter = new FileSystemPersistenceAdapter({\n path: persistence.path,\n autoSaveInterval: persistence.autoSaveInterval,\n });\n } else if (persistence.type === 'local') {\n const { LocalStoragePersistenceAdapter } = await import('./persistence/local-storage-adapter.js');\n this.persistenceAdapter = new LocalStoragePersistenceAdapter({\n key: persistence.key,\n });\n }\n }\n\n if (this.persistenceAdapter) {\n this.logger.debug('Persistence adapter initialized');\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n\n/**\n * Simple In-Memory Query Matcher\n * \n * Implements a subset of the ObjectStack Filter Protocol (MongoDB-compatible)\n * for evaluating conditions against in-memory JavaScript objects.\n */\n\ntype RecordType = Record<string, any>;\n\n/**\n * matches - Check if a record matches a filter criteria\n * @param record The data record to check\n * @param filter The filter condition (where clause)\n */\nexport function match(record: RecordType, filter: any): boolean {\n if (!filter || Object.keys(filter).length === 0) return true;\n \n // 1. Handle Top-Level Logical Operators ($and, $or, $not)\n // These usually appear at the root or nested.\n \n // $and: [ { ... }, { ... } ]\n if (Array.isArray(filter.$and)) {\n if (!filter.$and.every((f: any) => match(record, f))) {\n return false;\n }\n }\n \n // $or: [ { ... }, { ... } ]\n if (Array.isArray(filter.$or)) {\n if (!filter.$or.some((f: any) => match(record, f))) {\n return false;\n }\n }\n \n // $not: { ... }\n if (filter.$not) {\n if (match(record, filter.$not)) {\n return false;\n }\n }\n \n // 2. Iterate over field constraints\n for (const key of Object.keys(filter)) {\n // Skip logical operators we already handled (or future ones)\n if (key.startsWith('$')) continue;\n \n const condition = filter[key];\n const value = getValueByPath(record, key);\n \n if (!checkCondition(value, condition)) {\n return false;\n }\n }\n \n return true;\n}\n\n/**\n * Access nested properties via dot-notation (e.g. \"user.name\")\n */\nexport function getValueByPath(obj: any, path: string): any {\n if (!path.includes('.')) return obj[path];\n return path.split('.').reduce((o, i) => (o ? o[i] : undefined), obj);\n}\n\n/**\n * Evaluate a specific condition against a value\n */\nfunction checkCondition(value: any, condition: any): boolean {\n // Case A: Implicit Equality (e.g. status: 'active')\n // If condition is a primitive or Date/Array (exact match), treat as equality.\n if (\n typeof condition !== 'object' || \n condition === null || \n condition instanceof Date ||\n Array.isArray(condition)\n ) {\n // Loose equality to handle undefined/null mismatch or string/number coercion if desired.\n // But stick to == for JS loose equality which is often convenient in weakly typed queries.\n return value == condition;\n }\n \n // Case B: Operator Object (e.g. { $gt: 10, $lt: 20 })\n const keys = Object.keys(condition);\n const isOperatorObject = keys.some(k => k.startsWith('$'));\n \n if (!isOperatorObject) {\n // It's just a nested object comparison or implicit equality against an object\n // Simplistic check:\n return JSON.stringify(value) === JSON.stringify(condition);\n }\n\n // Iterate operators\n for (const op of keys) {\n const target = condition[op];\n \n // Handle undefined values\n if (value === undefined && op !== '$exists' && op !== '$ne' && op !== '$null') {\n return false; \n }\n\n switch (op) {\n case '$eq': \n if (value != target) return false; \n break;\n case '$ne': \n if (value == target) return false; \n break;\n \n // Numeric / Date\n case '$gt': \n if (!(value > target)) return false; \n break;\n case '$gte': \n if (!(value >= target)) return false; \n break;\n case '$lt': \n if (!(value < target)) return false; \n break;\n case '$lte': \n if (!(value <= target)) return false; \n break;\n case '$between':\n // target should be [min, max]\n if (Array.isArray(target) && (value < target[0] || value > target[1])) return false;\n break;\n\n // Sets\n case '$in': \n if (!Array.isArray(target) || !target.includes(value)) return false; \n break;\n case '$nin': \n if (Array.isArray(target) && target.includes(value)) return false; \n break;\n \n // Existence\n case '$exists':\n const exists = value !== undefined && value !== null;\n if (exists !== !!target) return false;\n break;\n\n // Strings\n case '$contains': \n if (typeof value !== 'string' || !value.includes(target)) return false; \n break;\n case '$notContains':\n if (typeof value !== 'string' || value.includes(target)) return false;\n break;\n case '$startsWith': \n if (typeof value !== 'string' || !value.startsWith(target)) return false; \n break;\n case '$endsWith': \n if (typeof value !== 'string' || !value.endsWith(target)) return false; \n break;\n case '$null':\n // $null: true → value must be null/undefined; $null: false → value must not be null/undefined\n if (target === true && value != null) return false;\n if (target === false && value == null) return false;\n break;\n case '$regex':\n try {\n const re = new RegExp(target, condition.$options || '');\n if (!re.test(String(value))) return false;\n } catch (e) { return false; }\n break;\n\n default: \n // Unknown operator, ignore or fail. Ignoring safe for optional features.\n break;\n }\n }\n \n return true;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport { InMemoryDriver } from './memory-driver.js';\n\nexport { InMemoryDriver }; // Export class for direct usage\nexport type { InMemoryDriverConfig, PersistenceAdapterInterface } from './memory-driver.js';\n\nexport { FileSystemPersistenceAdapter } from './persistence/file-adapter.js';\nexport { LocalStoragePersistenceAdapter } from './persistence/local-storage-adapter.js';\n\nexport { MemoryAnalyticsService } from './memory-analytics.js';\nexport type { MemoryAnalyticsConfig } from './memory-analytics.js';\n\nexport { InMemoryStrategy } from './in-memory-strategy.js';\n\nexport default {\n id: 'com.objectstack.driver.memory',\n version: '1.0.0',\n\n onEnable: async (context: any) => {\n const { logger, config, drivers } = context;\n logger.info('[Memory Driver] Initializing...');\n\n if (drivers) {\n const driver = new InMemoryDriver(config);\n drivers.register(driver);\n logger.info(`[Memory Driver] Registered driver: ${driver.name}`);\n } else {\n logger.warn('[Memory Driver] No driver registry found in context.');\n }\n }\n};\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { IAnalyticsService, AnalyticsResult, CubeMeta } from '@objectstack/spec/contracts';\nimport type { Cube, AnalyticsQuery } from '@objectstack/spec/data';\nimport type { InMemoryDriver } from './memory-driver.js';\nimport { Logger, createLogger } from '@objectstack/core';\n\n/**\n * Configuration for MemoryAnalyticsService\n */\nexport interface MemoryAnalyticsConfig {\n /** The data driver instance to use for queries */\n driver: InMemoryDriver;\n /** Cube definitions for the semantic layer */\n cubes: Cube[];\n /** Optional logger */\n logger?: Logger;\n}\n\n/**\n * Memory-Based Analytics Service\n * \n * Implements IAnalyticsService using InMemoryDriver's aggregation capabilities.\n * Provides a semantic layer (Cubes, Metrics, Dimensions) on top of in-memory data.\n * \n * Features:\n * - Cube-based semantic modeling\n * - Measure calculations (count, sum, avg, min, max, count_distinct)\n * - Dimension grouping\n * - Filter support\n * - Time dimension handling\n * - SQL generation (for debugging/transparency)\n * \n * This implementation is suitable for:\n * - Development and testing\n * - Local-first analytics\n * - Small to medium datasets\n * - Prototyping BI applications\n */\nexport class MemoryAnalyticsService implements IAnalyticsService {\n private driver: InMemoryDriver;\n private cubes: Map<string, Cube>;\n private logger: Logger;\n\n constructor(config: MemoryAnalyticsConfig) {\n this.driver = config.driver;\n this.cubes = new Map(config.cubes.map(c => [c.name, c]));\n this.logger = config.logger || createLogger({ level: 'info', format: 'pretty' });\n this.logger.debug('MemoryAnalyticsService initialized', { cubeCount: this.cubes.size });\n }\n\n /**\n * Execute an analytical query using the memory driver's aggregation pipeline\n */\n async query(query: AnalyticsQuery): Promise<AnalyticsResult> {\n this.logger.debug('Executing analytics query', { cube: query.cube, measures: query.measures });\n\n // Get cube definition\n if (!query.cube) {\n throw new Error('Cube name is required');\n }\n const cube = this.cubes.get(query.cube);\n if (!cube) {\n throw new Error(`Cube not found: ${query.cube}`);\n }\n\n // Build MongoDB aggregation pipeline\n const pipeline: Record<string, any>[] = [];\n\n // Stage 1: $match for filters\n // Filters can arrive in two shapes (per spec/data/analytics.zod.ts):\n // - Array of { member, operator, values } (cube-style, legacy)\n // - FilterCondition (MongoDB-style — canonical spec shape, used by\n // dashboard widget metadata directly).\n // Normalize both into the cube-style array before processing so the\n // existing pipeline logic stays untouched.\n const normalizedFilters = this.normalizeFilters(query);\n if (normalizedFilters.length > 0) {\n const matchStage: Record<string, any> = {};\n for (const filter of normalizedFilters) {\n const mongoOp = this.convertOperatorToMongo(filter.operator);\n const fieldPath = this.resolveFieldPath(cube, filter.member);\n\n if (filter.values && filter.values.length > 0) {\n // Coerce each filter value to a sensible runtime type so\n // `$eq` against in-memory numeric/boolean records still\n // matches. The cube spec serialises values as `string[]`,\n // but the in-memory driver compares with strict equality.\n const coerced = filter.values.map(v => this.coerceFilterValue(v));\n if (mongoOp === '$in') {\n matchStage[fieldPath] = { $in: coerced };\n } else if (mongoOp === '$nin') {\n matchStage[fieldPath] = { $nin: coerced };\n } else {\n matchStage[fieldPath] = { [mongoOp]: coerced[0] };\n }\n } else if (mongoOp === '$exists') {\n matchStage[fieldPath] = { $exists: filter.operator === 'set' };\n }\n }\n if (Object.keys(matchStage).length > 0) {\n pipeline.push({ $match: matchStage });\n }\n }\n\n // Stage 2: Time dimension filters\n if (query.timeDimensions && query.timeDimensions.length > 0) {\n for (const timeDim of query.timeDimensions) {\n const fieldPath = this.resolveFieldPath(cube, timeDim.dimension);\n if (timeDim.dateRange) {\n const range = Array.isArray(timeDim.dateRange) \n ? timeDim.dateRange \n : this.parseDateRangeString(timeDim.dateRange);\n \n if (range.length === 2) {\n pipeline.push({\n $match: {\n [fieldPath]: {\n $gte: new Date(range[0]),\n $lte: new Date(range[1])\n }\n }\n });\n }\n }\n }\n }\n\n // Stage 3: $group for measures and dimensions\n const groupStage: Record<string, any> = { _id: {} };\n \n // Add dimensions to _id\n if (query.dimensions && query.dimensions.length > 0) {\n for (const dim of query.dimensions) {\n const fieldPath = this.resolveFieldPath(cube, dim);\n const dimName = this.getShortName(dim);\n groupStage._id[dimName] = `$${fieldPath}`;\n }\n } else {\n groupStage._id = null; // No grouping, aggregate all\n }\n\n // Add measures as computed fields\n if (query.measures && query.measures.length > 0) {\n for (const measure of query.measures) {\n const measureDef = this.resolveMeasure(cube, measure);\n const measureName = this.getShortName(measure);\n \n if (measureDef) {\n const aggregator = this.buildAggregator(measureDef);\n groupStage[measureName] = aggregator;\n }\n }\n }\n\n pipeline.push({ $group: groupStage });\n\n // Stage 4: $project to reshape results (use short names, we'll fix them later)\n const projectStage: Record<string, any> = { _id: 0 };\n if (query.dimensions && query.dimensions.length > 0) {\n for (const dim of query.dimensions) {\n const dimName = this.getShortName(dim);\n projectStage[dimName] = `$_id.${dimName}`;\n }\n }\n if (query.measures && query.measures.length > 0) {\n for (const measure of query.measures) {\n const measureName = this.getShortName(measure);\n projectStage[measureName] = `$${measureName}`;\n }\n }\n pipeline.push({ $project: projectStage });\n\n // Stage 5: $sort (use short names)\n if (query.order && Object.keys(query.order).length > 0) {\n const sortStage: Record<string, any> = {};\n for (const [field, direction] of Object.entries(query.order)) {\n const shortName = this.getShortName(field);\n sortStage[shortName] = direction === 'asc' ? 1 : -1;\n }\n pipeline.push({ $sort: sortStage });\n }\n\n // Stage 6: $limit and $skip\n if (query.offset) {\n pipeline.push({ $skip: query.offset });\n }\n if (query.limit) {\n pipeline.push({ $limit: query.limit });\n }\n\n // Execute the aggregation pipeline\n const tableName = this.extractTableName(cube.sql);\n const rawRows = await this.driver.aggregate(tableName, pipeline);\n\n // Rename fields from short names to full cube.field names\n const rows = rawRows.map(row => {\n const renamedRow: Record<string, unknown> = {};\n \n // Rename dimensions\n if (query.dimensions) {\n for (const dim of query.dimensions) {\n const shortName = this.getShortName(dim);\n if (shortName in row) {\n renamedRow[dim] = row[shortName];\n }\n }\n }\n \n // Rename measures\n if (query.measures) {\n for (const measure of query.measures) {\n const shortName = this.getShortName(measure);\n if (shortName in row) {\n renamedRow[measure] = row[shortName];\n }\n }\n }\n \n return renamedRow;\n });\n\n // Build field metadata\n const fields: Array<{ name: string; type: string }> = [];\n \n if (query.dimensions) {\n for (const dim of query.dimensions) {\n const dimension = this.resolveDimension(cube, dim);\n fields.push({\n name: dim,\n type: dimension?.type || 'string'\n });\n }\n }\n \n if (query.measures) {\n for (const measure of query.measures) {\n const measureDef = this.resolveMeasure(cube, measure);\n fields.push({\n name: measure,\n type: this.measureTypeToFieldType(measureDef?.type || 'count')\n });\n }\n }\n\n this.logger.debug('Analytics query completed', { rowCount: rows.length });\n\n return {\n rows,\n fields,\n sql: this.generateSqlFromPipeline(tableName, pipeline) // For debugging\n };\n }\n\n /**\n * Get available cube metadata for discovery\n */\n async getMeta(cubeName?: string): Promise<CubeMeta[]> {\n const cubes = cubeName \n ? [this.cubes.get(cubeName)].filter(Boolean) as Cube[]\n : Array.from(this.cubes.values());\n\n return cubes.map(cube => ({\n name: cube.name,\n title: cube.title,\n measures: Object.entries(cube.measures).map(([key, measure]) => ({\n name: `${cube.name}.${key}`,\n type: measure.type,\n title: measure.label\n })),\n dimensions: Object.entries(cube.dimensions).map(([key, dimension]) => ({\n name: `${cube.name}.${key}`,\n type: dimension.type,\n title: dimension.label\n }))\n }));\n }\n\n /**\n * Generate SQL representation for debugging/transparency\n */\n async generateSql(query: AnalyticsQuery): Promise<{ sql: string; params: unknown[] }> {\n if (!query.cube) {\n throw new Error('Cube name is required');\n }\n const cube = this.cubes.get(query.cube);\n if (!cube) {\n throw new Error(`Cube not found: ${query.cube}`);\n }\n\n const tableName = this.extractTableName(cube.sql);\n const selectClauses: string[] = [];\n const groupByClauses: string[] = [];\n\n // Build SELECT for dimensions\n if (query.dimensions && query.dimensions.length > 0) {\n for (const dim of query.dimensions) {\n const fieldPath = this.resolveFieldPath(cube, dim);\n selectClauses.push(`${fieldPath} AS \"${dim}\"`);\n groupByClauses.push(fieldPath);\n }\n }\n\n // Build SELECT for measures\n if (query.measures && query.measures.length > 0) {\n for (const measure of query.measures) {\n const measureDef = this.resolveMeasure(cube, measure);\n if (measureDef) {\n const aggSql = this.measureToSql(measureDef);\n selectClauses.push(`${aggSql} AS \"${measure}\"`);\n }\n }\n }\n\n // Build WHERE clause\n const whereClauses: string[] = [];\n const normalizedFilters = this.normalizeFilters(query);\n if (normalizedFilters.length > 0) {\n for (const filter of normalizedFilters) {\n const fieldPath = this.resolveFieldPath(cube, filter.member);\n const sqlOp = this.operatorToSql(filter.operator);\n if (filter.values && filter.values.length > 0) {\n const literal = this.toSqlLiteral(filter.values[0]);\n whereClauses.push(`${fieldPath} ${sqlOp} ${literal}`);\n }\n }\n }\n\n let sql = `SELECT ${selectClauses.join(', ')} FROM ${tableName}`;\n if (whereClauses.length > 0) {\n sql += ` WHERE ${whereClauses.join(' AND ')}`;\n }\n if (groupByClauses.length > 0) {\n sql += ` GROUP BY ${groupByClauses.join(', ')}`;\n }\n if (query.order) {\n const orderClauses = Object.entries(query.order).map(([field, dir]) => \n `\"${field}\" ${dir.toUpperCase()}`\n );\n sql += ` ORDER BY ${orderClauses.join(', ')}`;\n }\n if (query.limit) {\n sql += ` LIMIT ${query.limit}`;\n }\n if (query.offset) {\n sql += ` OFFSET ${query.offset}`;\n }\n\n return { sql, params: [] };\n }\n\n // ===================================\n // Helper Methods\n // ===================================\n\n /**\n * Normalize filters into a cube-style array regardless of input shape.\n *\n * Accepts:\n * - undefined / null → []\n * - cube-style array `[{member, operator, values}]` → returned as-is\n * - MongoDB FilterCondition object (per spec/data/filter.zod.ts):\n * * implicit equality: `{is_active: true}`\n * * operator wrapper: `{stage: {$nin: [...]}}`\n * * mixed: `{stage: 'won', amount: {$gte: 100}}`\n * → flattened into one cube-style entry per (field, operator) pair\n *\n * Logical combinators (`$and`, `$or`, `$not`) are not yet expanded into\n * the cube pipeline; for current dashboard widget metadata the implicit\n * top-level AND of fields is sufficient. `$and` clauses are flattened\n * into the same AND list.\n */\n private normalizeFilters(query: unknown): Array<{ member: string; operator: string; values: string[] }> {\n if (!query || typeof query !== 'object') return [];\n\n const out: Array<{ member: string; operator: string; values: string[] }> = [];\n const where = (query as { where?: unknown }).where;\n\n if (where && typeof where === 'object' && !Array.isArray(where)) {\n this.flattenFilterCondition(where as Record<string, unknown>, out);\n }\n\n return out;\n }\n\n private flattenFilterCondition(\n cond: Record<string, unknown>,\n out: Array<{ member: string; operator: string; values: string[] }>,\n ): void {\n for (const [key, raw] of Object.entries(cond)) {\n if (raw == null) continue;\n\n // Logical combinators\n if (key === '$and' && Array.isArray(raw)) {\n for (const sub of raw) {\n if (sub && typeof sub === 'object') {\n this.flattenFilterCondition(sub as Record<string, unknown>, out);\n }\n }\n continue;\n }\n // $or / $not are not yet supported in the cube pipeline; ignore so\n // a partial query still runs rather than failing entirely.\n if (key === '$or' || key === '$not') continue;\n\n // Operator wrapper: { field: { $op: value, ... } }\n if (typeof raw === 'object' && !Array.isArray(raw) && !(raw instanceof Date)) {\n const wrapper = raw as Record<string, unknown>;\n const opEntries = Object.keys(wrapper).filter(k => k.startsWith('$'));\n if (opEntries.length > 0) {\n for (const opKey of opEntries) {\n const cubeOp = this.mongoOperatorToCubeOperator(opKey);\n if (!cubeOp) continue;\n const v = wrapper[opKey];\n const values = Array.isArray(v)\n ? v.map(x => this.stringifyForCube(x))\n : [this.stringifyForCube(v)];\n out.push({ member: key, operator: cubeOp, values });\n }\n continue;\n }\n // Otherwise treat as nested relation (e.g. {profile: {verified: true}}).\n // Flatten with dot-prefixed keys.\n for (const [nestedKey, nestedVal] of Object.entries(wrapper)) {\n this.flattenFilterCondition({ [`${key}.${nestedKey}`]: nestedVal }, out);\n }\n continue;\n }\n\n // Implicit equality: { field: scalar | array }\n const values = Array.isArray(raw)\n ? raw.map(x => this.stringifyForCube(x))\n : [this.stringifyForCube(raw)];\n out.push({\n member: key,\n operator: Array.isArray(raw) ? 'in' : 'equals',\n values,\n });\n }\n }\n\n /**\n * Map MongoDB-style `$op` keys (from FilterCondition) to the cube-style\n * operator names accepted by `convertOperatorToMongo` / `operatorToSql`.\n */\n private mongoOperatorToCubeOperator(op: string): string | null {\n switch (op) {\n case '$eq': return 'equals';\n case '$ne': return 'notEquals';\n case '$gt': return 'gt';\n case '$gte': return 'gte';\n case '$lt': return 'lt';\n case '$lte': return 'lte';\n case '$in': return 'in';\n case '$nin': return 'notIn';\n case '$contains': return 'contains';\n case '$notContains': return 'notContains';\n case '$exists': return 'set';\n default: return null;\n }\n }\n\n /**\n * Stringify a filter value for cube-style storage. Booleans become\n * `'1'/'0'` so that downstream consumers expecting SQLite-style\n * numeric booleans match correctly. The in-memory pipeline uses\n * {@link coerceFilterValue} to recover real JS types from these\n * strings.\n */\n private stringifyForCube(v: unknown): string {\n if (v == null) return '';\n if (typeof v === 'boolean') return v ? '1' : '0';\n if (v instanceof Date) return v.toISOString();\n if (typeof v === 'object') return JSON.stringify(v);\n return String(v);\n }\n\n /**\n * Recover a runtime value from its cube-stringified form for in-memory\n * comparison. Booleans, integers, floats and ISO-date-like strings are\n * coerced; everything else stays as a string.\n */\n private coerceFilterValue(s: string): unknown {\n if (s === 'true') return true;\n if (s === 'false') return false;\n if (s === 'null') return null;\n // Numeric strings: integer or float (no leading zeros except '0')\n if (/^-?\\d+$/.test(s)) {\n const n = Number(s);\n if (Number.isFinite(n)) return n;\n }\n if (/^-?\\d+\\.\\d+$/.test(s)) {\n const n = Number(s);\n if (Number.isFinite(n)) return n;\n }\n return s;\n }\n\n /**\n * Type-aware SQL literal formatter. Booleans and numbers are emitted\n * unquoted; everything else is single-quoted with embedded quotes\n * escaped.\n */\n private toSqlLiteral(s: string): string {\n if (s === 'true') return '1';\n if (s === 'false') return '0';\n if (s === 'null') return 'NULL';\n if (/^-?\\d+(\\.\\d+)?$/.test(s)) return s;\n return `'${s.replace(/'/g, \"''\")}'`;\n }\n\n private resolveFieldPath(cube: Cube, member: string): string {\n // Handle both \"cube.field\" and \"field\" formats\n const parts = member.split('.');\n const fieldName = parts.length > 1 ? parts[1] : parts[0];\n\n // Check if it's a dimension\n const dimension = cube.dimensions[fieldName];\n if (dimension) {\n // Extract field path from SQL expression\n return dimension.sql.replace(/^\\$/, ''); // Remove $ prefix if present\n }\n\n // Check if it's a measure (for filters)\n const measure = cube.measures[fieldName];\n if (measure) {\n return measure.sql.replace(/^\\$/, '');\n }\n\n return fieldName;\n }\n\n private resolveMeasure(cube: Cube, measureName: string) {\n const parts = measureName.split('.');\n const fieldName = parts.length > 1 ? parts[1] : parts[0];\n const direct = cube.measures[fieldName];\n if (direct) return direct;\n\n // Accept `${field}_${type}` aliases (e.g. 'amount_sum') for measures whose\n // canonical name is just `${field}` (e.g. measure 'amount' of type 'sum').\n // This matches the convention used by the data-objectstack adapter and\n // other clients that build measure names from (field, function) pairs.\n const aggTypes = ['count', 'sum', 'avg', 'min', 'max', 'count_distinct'];\n for (const type of aggTypes) {\n const suffix = `_${type}`;\n if (fieldName.endsWith(suffix)) {\n const baseField = fieldName.slice(0, -suffix.length);\n const candidate = cube.measures[baseField];\n if (candidate && candidate.type === type) {\n return candidate;\n }\n }\n }\n return undefined;\n }\n\n private resolveDimension(cube: Cube, dimensionName: string) {\n const parts = dimensionName.split('.');\n const fieldName = parts.length > 1 ? parts[1] : parts[0];\n return cube.dimensions[fieldName];\n }\n\n private getShortName(fullName: string): string {\n const parts = fullName.split('.');\n return parts.length > 1 ? parts[1] : parts[0];\n }\n\n private buildAggregator(measure: { type: string; sql: string; filters?: any[] }): any {\n const fieldPath = measure.sql.replace(/^\\$/, '');\n\n switch (measure.type) {\n case 'count':\n return { $sum: 1 };\n case 'sum':\n return { $sum: `$${fieldPath}` };\n case 'avg':\n return { $avg: `$${fieldPath}` };\n case 'min':\n return { $min: `$${fieldPath}` };\n case 'max':\n return { $max: `$${fieldPath}` };\n case 'count_distinct':\n return { $addToSet: `$${fieldPath}` }; // Will need post-processing for count\n default:\n return { $sum: 1 }; // Default to count\n }\n }\n\n private measureTypeToFieldType(measureType: string): string {\n switch (measureType) {\n case 'count':\n case 'sum':\n case 'count_distinct':\n return 'number';\n case 'avg':\n case 'min':\n case 'max':\n return 'number';\n case 'string':\n return 'string';\n case 'boolean':\n return 'boolean';\n default:\n return 'number';\n }\n }\n\n private convertOperatorToMongo(operator: string): string {\n const opMap: Record<string, string> = {\n 'equals': '$eq',\n 'notEquals': '$ne',\n 'contains': '$regex',\n 'notContains': '$not',\n 'gt': '$gt',\n 'gte': '$gte',\n 'lt': '$lt',\n 'lte': '$lte',\n 'in': '$in',\n 'notIn': '$nin',\n 'set': '$exists',\n 'notSet': '$exists',\n 'inDateRange': '$gte', // Will need special handling\n };\n return opMap[operator] || '$eq';\n }\n\n private operatorToSql(operator: string): string {\n const opMap: Record<string, string> = {\n 'equals': '=',\n 'notEquals': '!=',\n 'contains': 'LIKE',\n 'notContains': 'NOT LIKE',\n 'gt': '>',\n 'gte': '>=',\n 'lt': '<',\n 'lte': '<=',\n };\n return opMap[operator] || '=';\n }\n\n private measureToSql(measure: { type: string; sql: string }): string {\n const fieldPath = measure.sql.replace(/^\\$/, '');\n \n switch (measure.type) {\n case 'count':\n return 'COUNT(*)';\n case 'sum':\n return `SUM(${fieldPath})`;\n case 'avg':\n return `AVG(${fieldPath})`;\n case 'min':\n return `MIN(${fieldPath})`;\n case 'max':\n return `MAX(${fieldPath})`;\n case 'count_distinct':\n return `COUNT(DISTINCT ${fieldPath})`;\n default:\n return 'COUNT(*)';\n }\n }\n\n private extractTableName(sql: string): string {\n // For simple table names, return as-is\n // For complex SQL, this would need more sophisticated parsing\n return sql.trim();\n }\n\n private parseDateRangeString(range: string): string[] {\n // Simple parser for common date range strings\n // In production, this would use a proper date range parser\n const now = new Date();\n const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());\n \n if (range === 'today') {\n return [today.toISOString(), new Date(today.getTime() + 86400000).toISOString()];\n } else if (range.startsWith('last ')) {\n const parts = range.split(' ');\n const num = parseInt(parts[1]);\n const unit = parts[2];\n const start = new Date(today);\n \n if (unit.startsWith('day')) {\n start.setDate(start.getDate() - num);\n } else if (unit.startsWith('week')) {\n start.setDate(start.getDate() - num * 7);\n } else if (unit.startsWith('month')) {\n start.setMonth(start.getMonth() - num);\n } else if (unit.startsWith('year')) {\n start.setFullYear(start.getFullYear() - num);\n }\n \n return [start.toISOString(), now.toISOString()];\n }\n \n return [range, range]; // Fallback\n }\n\n private generateSqlFromPipeline(table: string, pipeline: Record<string, any>[]): string {\n // Simplified SQL generation for debugging\n // This is a basic representation of the aggregation pipeline\n const stages = pipeline.map((stage, idx) => {\n const op = Object.keys(stage)[0];\n return `/* Stage ${idx + 1}: ${op} */ ${JSON.stringify(stage[op])}`;\n }).join('\\n');\n \n return `-- MongoDB Aggregation Pipeline on table: ${table}\\n${stages}`;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { AnalyticsQuery, AnalyticsResult, AnalyticsStrategy, StrategyContext } from '@objectstack/spec/contracts';\n\n/**\n * InMemoryStrategy — Priority 3\n *\n * Delegates to an existing `IAnalyticsService` instance that was registered\n * as a fallback (typically `MemoryAnalyticsService` from this package).\n *\n * This is the lowest-priority strategy, used in:\n * - `dev` / `test` environments\n * - Any runtime where the backing driver is in-memory\n */\nexport class InMemoryStrategy implements AnalyticsStrategy {\n readonly name = 'InMemoryStrategy';\n readonly priority = 30;\n\n canHandle(query: AnalyticsQuery, ctx: StrategyContext): boolean {\n if (!query.cube) return false;\n // Can handle when a fallback service exists\n if (ctx.fallbackService) return true;\n // Or when the driver is flagged as in-memory\n const caps = ctx.queryCapabilities(query.cube);\n return caps.inMemory;\n }\n\n async execute(query: AnalyticsQuery, ctx: StrategyContext): Promise<AnalyticsResult> {\n if (!ctx.fallbackService) {\n throw new Error(\n `[InMemoryStrategy] No fallback analytics service available for cube \"${query.cube}\". ` +\n 'Register a MemoryAnalyticsService or configure a driver with analytics support.'\n );\n }\n return ctx.fallbackService.query(query);\n }\n\n async generateSql(query: AnalyticsQuery, ctx: StrategyContext): Promise<{ sql: string; params: unknown[] }> {\n if (ctx.fallbackService?.generateSql) {\n return ctx.fallbackService.generateSql(query);\n }\n return {\n sql: `-- InMemoryStrategy: SQL generation not supported for cube \"${query.cube}\"`,\n params: [],\n };\n }\n}\n"],"mappings":"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","names":["path","path","LocalStoragePersistenceAdapter","FileSystemPersistenceAdapter","createLogger","values"]}