@korajs/store 0.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.
Files changed (43) hide show
  1. package/dist/adapters/better-sqlite3.cjs +166 -0
  2. package/dist/adapters/better-sqlite3.cjs.map +1 -0
  3. package/dist/adapters/better-sqlite3.d.cts +31 -0
  4. package/dist/adapters/better-sqlite3.d.ts +31 -0
  5. package/dist/adapters/better-sqlite3.js +117 -0
  6. package/dist/adapters/better-sqlite3.js.map +1 -0
  7. package/dist/adapters/indexeddb.cjs +550 -0
  8. package/dist/adapters/indexeddb.cjs.map +1 -0
  9. package/dist/adapters/indexeddb.d.cts +52 -0
  10. package/dist/adapters/indexeddb.d.ts +52 -0
  11. package/dist/adapters/indexeddb.js +205 -0
  12. package/dist/adapters/indexeddb.js.map +1 -0
  13. package/dist/adapters/sqlite-wasm-worker.cjs +215 -0
  14. package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -0
  15. package/dist/adapters/sqlite-wasm-worker.d.cts +2 -0
  16. package/dist/adapters/sqlite-wasm-worker.d.ts +2 -0
  17. package/dist/adapters/sqlite-wasm-worker.js +191 -0
  18. package/dist/adapters/sqlite-wasm-worker.js.map +1 -0
  19. package/dist/adapters/sqlite-wasm.cjs +354 -0
  20. package/dist/adapters/sqlite-wasm.cjs.map +1 -0
  21. package/dist/adapters/sqlite-wasm.d.cts +68 -0
  22. package/dist/adapters/sqlite-wasm.d.ts +68 -0
  23. package/dist/adapters/sqlite-wasm.js +14 -0
  24. package/dist/adapters/sqlite-wasm.js.map +1 -0
  25. package/dist/chunk-DXKLAQ6P.js +111 -0
  26. package/dist/chunk-DXKLAQ6P.js.map +1 -0
  27. package/dist/chunk-LAWV6CFH.js +62 -0
  28. package/dist/chunk-LAWV6CFH.js.map +1 -0
  29. package/dist/chunk-ZP5AXQ3Z.js +179 -0
  30. package/dist/chunk-ZP5AXQ3Z.js.map +1 -0
  31. package/dist/index.cjs +1288 -0
  32. package/dist/index.cjs.map +1 -0
  33. package/dist/index.d.cts +376 -0
  34. package/dist/index.d.ts +376 -0
  35. package/dist/index.js +1194 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/sqlite-wasm-channel-46AOWNPM.js +10 -0
  38. package/dist/sqlite-wasm-channel-46AOWNPM.js.map +1 -0
  39. package/dist/sqlite-wasm-channel-Lakjuk2E.d.cts +104 -0
  40. package/dist/sqlite-wasm-channel-Lakjuk2E.d.ts +104 -0
  41. package/dist/types-DF-KDSK1.d.cts +106 -0
  42. package/dist/types-DF-KDSK1.d.ts +106 -0
  43. package/package.json +95 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/store/store.ts","../src/collection/collection.ts","../src/query/sql-builder.ts","../src/serialization/serializer.ts","../src/serialization/richtext-serializer.ts","../src/query/pluralize.ts","../src/query/query-builder.ts","../src/subscription/subscription-manager.ts"],"sourcesContent":["import { HybridLogicalClock, createVersionVector, generateUUIDv7 } from '@korajs/core'\nimport type {\n\tKoraEventEmitter,\n\tOperation,\n\tOperationLog,\n\tSchemaDefinition,\n\tVersionVector,\n} from '@korajs/core'\nimport { Collection } from '../collection/collection'\nimport { StoreNotOpenError } from '../errors'\nimport { QueryBuilder } from '../query/query-builder'\nimport { buildInsertQuery, buildSoftDeleteQuery, buildUpdateQuery } from '../query/sql-builder'\nimport {\n\tdeserializeOperationWithCollection,\n\tserializeOperation,\n\tserializeRecord,\n} from '../serialization/serializer'\nimport { SubscriptionManager } from '../subscription/subscription-manager'\nimport type {\n\tApplyResult,\n\tMetaRow,\n\tOperationRow,\n\tRawCollectionRow,\n\tStorageAdapter,\n\tStoreConfig,\n\tVersionVectorRow,\n} from '../types'\n\n/**\n * Store is the main orchestrator. It owns a schema, a storage adapter,\n * a clock, and a subscription manager. It creates Collection instances\n * for each schema collection, and provides the sync contract via\n * applyRemoteOperation and getOperationRange.\n *\n * @example\n * ```typescript\n * const store = new Store({ schema, adapter })\n * await store.open()\n * const todo = await store.collection('todos').insert({ title: 'Hello' })\n * await store.close()\n * ```\n */\nexport class Store implements OperationLog {\n\tprivate opened = false\n\tprivate nodeId = ''\n\tprivate sequenceNumber = 0\n\tprivate versionVector: VersionVector = createVersionVector()\n\tprivate clock: HybridLogicalClock | null = null\n\tprivate collections = new Map<string, Collection>()\n\tprivate subscriptionManager = new SubscriptionManager()\n\n\tprivate readonly schema: SchemaDefinition\n\tprivate readonly adapter: StorageAdapter\n\tprivate readonly configNodeId: string | undefined\n\tprivate readonly emitter: KoraEventEmitter | null\n\n\tconstructor(config: StoreConfig) {\n\t\tthis.schema = config.schema\n\t\tthis.adapter = config.adapter\n\t\tthis.configNodeId = config.nodeId\n\t\tthis.emitter = config.emitter ?? null\n\t}\n\n\t/**\n\t * Open the store: initialize the database, load or generate a node ID,\n\t * restore the sequence number and version vector, and create Collection instances.\n\t */\n\tasync open(): Promise<void> {\n\t\tawait this.adapter.open(this.schema)\n\n\t\t// Load or generate node ID\n\t\tthis.nodeId = await this.loadOrGenerateNodeId()\n\t\tthis.clock = new HybridLogicalClock(this.nodeId)\n\n\t\t// Load sequence number and version vector\n\t\tthis.sequenceNumber = await this.loadSequenceNumber()\n\t\tthis.versionVector = await this.loadVersionVector()\n\n\t\t// Create collection instances\n\t\tfor (const [name, definition] of Object.entries(this.schema.collections)) {\n\t\t\tconst col = new Collection(\n\t\t\t\tname,\n\t\t\t\tdefinition,\n\t\t\t\tthis.schema,\n\t\t\t\tthis.adapter,\n\t\t\t\tthis.clock,\n\t\t\t\tthis.nodeId,\n\t\t\t\t() => this.nextSequenceNumber(),\n\t\t\t\t(collectionName, operation) => {\n\t\t\t\t\tthis.subscriptionManager.notify(collectionName, operation)\n\t\t\t\t\tif (this.emitter) {\n\t\t\t\t\t\tthis.emitter.emit({ type: 'operation:created', operation })\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t)\n\t\t\tthis.collections.set(name, col)\n\t\t}\n\n\t\tthis.opened = true\n\t}\n\n\t/**\n\t * Close the store: clear subscriptions and close the adapter.\n\t */\n\tasync close(): Promise<void> {\n\t\tthis.subscriptionManager.clear()\n\t\tthis.collections.clear()\n\t\tthis.opened = false\n\t\tawait this.adapter.close()\n\t}\n\n\t/**\n\t * Get a Collection instance for CRUD operations.\n\t * @throws {StoreNotOpenError} If the store is not open\n\t * @throws {Error} If the collection name is not in the schema\n\t */\n\tcollection(name: string): CollectionAccessor {\n\t\tthis.ensureOpen()\n\t\tconst col = this.collections.get(name)\n\t\tif (!col) {\n\t\t\tthrow new Error(\n\t\t\t\t`Unknown collection \"${name}\". Available: ${[...this.collections.keys()].join(', ')}`,\n\t\t\t)\n\t\t}\n\n\t\tconst definition = this.schema.collections[name]\n\t\tif (!definition) {\n\t\t\tthrow new Error(`Collection definition not found for \"${name}\"`)\n\t\t}\n\n\t\treturn {\n\t\t\tinsert: (data: Record<string, unknown>) => col.insert(data),\n\t\t\tfindById: (id: string) => col.findById(id),\n\t\t\tupdate: (id: string, data: Record<string, unknown>) => col.update(id, data),\n\t\t\tdelete: (id: string) => col.delete(id),\n\t\t\twhere: (conditions) =>\n\t\t\t\tnew QueryBuilder(name, definition, this.adapter, this.subscriptionManager, conditions, this.schema),\n\t\t}\n\t}\n\n\t/**\n\t * Get the current version vector.\n\t */\n\tgetVersionVector(): VersionVector {\n\t\tthis.ensureOpen()\n\t\treturn new Map(this.versionVector)\n\t}\n\n\t/**\n\t * Get the node ID for this store instance.\n\t */\n\tgetNodeId(): string {\n\t\tthis.ensureOpen()\n\t\treturn this.nodeId\n\t}\n\n\t/**\n\t * Apply a remote operation received from sync.\n\t * Checks for duplicates, applies to the data table, persists the operation,\n\t * and updates the version vector.\n\t */\n\tasync applyRemoteOperation(op: Operation): Promise<ApplyResult> {\n\t\tthis.ensureOpen()\n\n\t\tconst collection = op.collection\n\t\tconst definition = this.schema.collections[collection]\n\t\tif (!definition) {\n\t\t\treturn 'skipped'\n\t\t}\n\n\t\t// Check for duplicate (content-addressed dedup)\n\t\tconst existing = await this.adapter.query<{ id: string }>(\n\t\t\t`SELECT id FROM _kora_ops_${collection} WHERE id = ?`,\n\t\t\t[op.id],\n\t\t)\n\t\tif (existing.length > 0) {\n\t\t\treturn 'duplicate'\n\t\t}\n\n\t\t// Update the clock with the remote timestamp\n\t\tif (this.clock) {\n\t\t\tthis.clock.receive(op.timestamp)\n\t\t}\n\n\t\t// Apply the operation to the data table\n\t\tawait this.adapter.transaction(async (tx) => {\n\t\t\tif (op.type === 'insert' && op.data) {\n\t\t\t\tconst serializedData = serializeRecord(op.data, definition.fields)\n\t\t\t\tconst now = op.timestamp.wallTime\n\t\t\t\tconst record: Record<string, unknown> = {\n\t\t\t\t\tid: op.recordId,\n\t\t\t\t\t...serializedData,\n\t\t\t\t\t_created_at: now,\n\t\t\t\t\t_updated_at: now,\n\t\t\t\t}\n\t\t\t\tconst insertQuery = buildInsertQuery(collection, record)\n\t\t\t\tawait tx.execute(insertQuery.sql, insertQuery.params)\n\t\t\t} else if (op.type === 'update' && op.data) {\n\t\t\t\tconst serializedChanges = serializeRecord(op.data, definition.fields)\n\t\t\t\tconst updateQuery = buildUpdateQuery(collection, op.recordId, {\n\t\t\t\t\t...serializedChanges,\n\t\t\t\t\t_updated_at: op.timestamp.wallTime,\n\t\t\t\t})\n\t\t\t\tawait tx.execute(updateQuery.sql, updateQuery.params)\n\t\t\t} else if (op.type === 'delete') {\n\t\t\t\tconst deleteQuery = buildSoftDeleteQuery(collection, op.recordId, op.timestamp.wallTime)\n\t\t\t\tawait tx.execute(deleteQuery.sql, deleteQuery.params)\n\t\t\t}\n\n\t\t\t// Persist the operation\n\t\t\tconst opRow = serializeOperation(op)\n\t\t\tconst opInsert = buildInsertQuery(\n\t\t\t\t`_kora_ops_${collection}`,\n\t\t\t\topRow as unknown as Record<string, unknown>,\n\t\t\t)\n\t\t\tawait tx.execute(opInsert.sql, opInsert.params)\n\n\t\t\t// Update version vector\n\t\t\tconst currentSeq = this.versionVector.get(op.nodeId) ?? 0\n\t\t\tif (op.sequenceNumber > currentSeq) {\n\t\t\t\tthis.versionVector.set(op.nodeId, op.sequenceNumber)\n\t\t\t\tawait tx.execute(\n\t\t\t\t\t'INSERT OR REPLACE INTO _kora_version_vector (node_id, sequence_number) VALUES (?, ?)',\n\t\t\t\t\t[op.nodeId, op.sequenceNumber],\n\t\t\t\t)\n\t\t\t}\n\t\t})\n\n\t\t// Notify subscriptions\n\t\tthis.subscriptionManager.notify(collection, op)\n\n\t\treturn 'applied'\n\t}\n\n\t/**\n\t * Get operations from a node within a sequence number range.\n\t * Implements the OperationLog interface for computeDelta.\n\t */\n\tgetRange(nodeId: string, fromSeq: number, toSeq: number): Operation[] {\n\t\t// This is synchronous per the OperationLog interface.\n\t\t// We can't use async here, so this must be called with data already loaded.\n\t\t// For now, this is a placeholder that the sync layer will call after awaiting.\n\t\treturn []\n\t}\n\n\t/**\n\t * Async version of getRange for use by the sync layer.\n\t */\n\tasync getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]> {\n\t\tthis.ensureOpen()\n\t\tconst allOps: Operation[] = []\n\n\t\tfor (const collectionName of Object.keys(this.schema.collections)) {\n\t\t\tconst rows = await this.adapter.query<OperationRow>(\n\t\t\t\t`SELECT * FROM _kora_ops_${collectionName} WHERE node_id = ? AND sequence_number >= ? AND sequence_number <= ? ORDER BY sequence_number ASC`,\n\t\t\t\t[nodeId, fromSeq, toSeq],\n\t\t\t)\n\t\t\tfor (const row of rows) {\n\t\t\t\tallOps.push(deserializeOperationWithCollection(row, collectionName))\n\t\t\t}\n\t\t}\n\n\t\t// Sort by sequence number across collections\n\t\tallOps.sort((a, b) => a.sequenceNumber - b.sequenceNumber)\n\t\treturn allOps\n\t}\n\n\t/**\n\t * Get the schema definition.\n\t */\n\tgetSchema(): SchemaDefinition {\n\t\treturn this.schema\n\t}\n\n\t/** Expose the subscription manager for direct access (e.g., by QueryBuilder) */\n\tgetSubscriptionManager(): SubscriptionManager {\n\t\treturn this.subscriptionManager\n\t}\n\n\tprivate nextSequenceNumber(): number {\n\t\tthis.sequenceNumber++\n\t\tthis.versionVector.set(this.nodeId, this.sequenceNumber)\n\t\treturn this.sequenceNumber\n\t}\n\n\tprivate async loadOrGenerateNodeId(): Promise<string> {\n\t\tif (this.configNodeId) {\n\t\t\t// Persist the configured node ID\n\t\t\tawait this.adapter.execute(\n\t\t\t\t\"INSERT OR REPLACE INTO _kora_meta (key, value) VALUES ('node_id', ?)\",\n\t\t\t\t[this.configNodeId],\n\t\t\t)\n\t\t\treturn this.configNodeId\n\t\t}\n\n\t\t// Try to load existing node ID\n\t\tconst rows = await this.adapter.query<MetaRow>(\n\t\t\t\"SELECT value FROM _kora_meta WHERE key = 'node_id'\",\n\t\t)\n\t\tif (rows[0]) {\n\t\t\treturn rows[0].value\n\t\t}\n\n\t\t// Generate new node ID\n\t\tconst newNodeId = generateUUIDv7()\n\t\tawait this.adapter.execute(\"INSERT INTO _kora_meta (key, value) VALUES ('node_id', ?)\", [\n\t\t\tnewNodeId,\n\t\t])\n\t\treturn newNodeId\n\t}\n\n\tprivate async loadSequenceNumber(): Promise<number> {\n\t\tconst rows = await this.adapter.query<VersionVectorRow>(\n\t\t\t'SELECT sequence_number FROM _kora_version_vector WHERE node_id = ?',\n\t\t\t[this.nodeId],\n\t\t)\n\t\treturn rows[0]?.sequence_number ?? 0\n\t}\n\n\tprivate async loadVersionVector(): Promise<VersionVector> {\n\t\tconst rows = await this.adapter.query<VersionVectorRow>(\n\t\t\t'SELECT node_id, sequence_number FROM _kora_version_vector',\n\t\t)\n\t\tconst vector = createVersionVector()\n\t\tfor (const row of rows) {\n\t\t\tvector.set(row.node_id, row.sequence_number)\n\t\t}\n\t\treturn vector\n\t}\n\n\tprivate ensureOpen(): void {\n\t\tif (!this.opened) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t}\n}\n\n/**\n * Public-facing collection accessor. Provides CRUD + where.\n */\nexport interface CollectionAccessor {\n\tinsert(data: Record<string, unknown>): Promise<import('../types').CollectionRecord>\n\tfindById(id: string): Promise<import('../types').CollectionRecord | null>\n\tupdate(id: string, data: Record<string, unknown>): Promise<import('../types').CollectionRecord>\n\tdelete(id: string): Promise<void>\n\twhere(conditions: Record<string, unknown>): QueryBuilder\n}\n","import type {\n\tCollectionDefinition,\n\tHLCTimestamp,\n\tHybridLogicalClock,\n\tOperation,\n\tSchemaDefinition,\n} from '@korajs/core'\nimport { createOperation, generateUUIDv7, validateRecord } from '@korajs/core'\nimport { RecordNotFoundError } from '../errors'\nimport { buildInsertQuery, buildSoftDeleteQuery, buildUpdateQuery } from '../query/sql-builder'\nimport { deserializeRecord, serializeOperation, serializeRecord } from '../serialization/serializer'\nimport type { CollectionRecord, RawCollectionRow, StorageAdapter } from '../types'\n\n/**\n * Callback invoked after a mutation so the Store can notify subscriptions.\n */\nexport type MutationCallback = (collection: string, operation: Operation) => void\n\n/**\n * Collection provides CRUD operations on a single schema collection.\n * Each mutation creates an Operation and persists both the data and the operation atomically.\n */\nexport class Collection {\n\tconstructor(\n\t\tprivate readonly name: string,\n\t\tprivate readonly definition: CollectionDefinition,\n\t\tprivate readonly schema: SchemaDefinition,\n\t\tprivate readonly adapter: StorageAdapter,\n\t\tprivate readonly clock: HybridLogicalClock,\n\t\tprivate readonly nodeId: string,\n\t\tprivate readonly getSequenceNumber: () => number,\n\t\tprivate readonly onMutation: MutationCallback,\n\t) {}\n\n\t/**\n\t * Insert a new record into the collection.\n\t * Generates a UUID v7 for the id, validates data, and persists atomically.\n\t *\n\t * @param data - The record data (auto fields and defaults are applied automatically)\n\t * @returns The inserted record with id, createdAt, updatedAt\n\t */\n\tasync insert(data: Record<string, unknown>): Promise<CollectionRecord> {\n\t\tconst validated = validateRecord(this.name, this.definition, data, 'insert')\n\t\tconst recordId = generateUUIDv7()\n\t\tconst now = Date.now()\n\n\t\t// Set auto timestamp fields\n\t\tfor (const [fieldName, descriptor] of Object.entries(this.definition.fields)) {\n\t\t\tif (descriptor.auto && descriptor.kind === 'timestamp') {\n\t\t\t\tvalidated[fieldName] = now\n\t\t\t}\n\t\t}\n\n\t\tconst sequenceNumber = this.getSequenceNumber()\n\t\tconst operation = await createOperation(\n\t\t\t{\n\t\t\t\tnodeId: this.nodeId,\n\t\t\t\ttype: 'insert',\n\t\t\t\tcollection: this.name,\n\t\t\t\trecordId,\n\t\t\t\tdata: { ...validated },\n\t\t\t\tpreviousData: null,\n\t\t\t\tsequenceNumber,\n\t\t\t\tcausalDeps: [],\n\t\t\t\tschemaVersion: this.schema.version,\n\t\t\t},\n\t\t\tthis.clock,\n\t\t)\n\n\t\tconst serializedData = serializeRecord(validated, this.definition.fields)\n\t\tconst record: Record<string, unknown> = {\n\t\t\tid: recordId,\n\t\t\t...serializedData,\n\t\t\t_created_at: now,\n\t\t\t_updated_at: now,\n\t\t}\n\n\t\tconst insertQuery = buildInsertQuery(this.name, record)\n\t\tconst opRow = serializeOperation(operation)\n\t\tconst opInsert = buildInsertQuery(\n\t\t\t`_kora_ops_${this.name}`,\n\t\t\topRow as unknown as Record<string, unknown>,\n\t\t)\n\n\t\tawait this.adapter.transaction(async (tx) => {\n\t\t\tawait tx.execute(insertQuery.sql, insertQuery.params)\n\t\t\tawait tx.execute(opInsert.sql, opInsert.params)\n\t\t\tawait tx.execute(\n\t\t\t\t'INSERT OR REPLACE INTO _kora_version_vector (node_id, sequence_number) VALUES (?, ?)',\n\t\t\t\t[this.nodeId, sequenceNumber],\n\t\t\t)\n\t\t})\n\n\t\tthis.onMutation(this.name, operation)\n\n\t\treturn {\n\t\t\tid: recordId,\n\t\t\t...validated,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t}\n\t}\n\n\t/**\n\t * Find a record by its ID. Returns null if not found or soft-deleted.\n\t */\n\tasync findById(id: string): Promise<CollectionRecord | null> {\n\t\tconst rows = await this.adapter.query<RawCollectionRow>(\n\t\t\t`SELECT * FROM ${this.name} WHERE id = ? AND _deleted = 0`,\n\t\t\t[id],\n\t\t)\n\n\t\tconst row = rows[0]\n\t\tif (!row) return null\n\t\treturn deserializeRecord(row, this.definition.fields)\n\t}\n\n\t/**\n\t * Update an existing record. Only the provided fields are changed.\n\t *\n\t * @param id - The record ID to update\n\t * @param data - Partial data with only the fields to change\n\t * @returns The updated record\n\t * @throws {RecordNotFoundError} If the record doesn't exist or is deleted\n\t */\n\tasync update(id: string, data: Record<string, unknown>): Promise<CollectionRecord> {\n\t\tconst currentRows = await this.adapter.query<RawCollectionRow>(\n\t\t\t`SELECT * FROM ${this.name} WHERE id = ? AND _deleted = 0`,\n\t\t\t[id],\n\t\t)\n\t\tconst currentRow = currentRows[0]\n\t\tif (!currentRow) {\n\t\t\tthrow new RecordNotFoundError(this.name, id)\n\t\t}\n\n\t\tconst validated = validateRecord(this.name, this.definition, data, 'update')\n\t\tconst now = Date.now()\n\n\t\t// Build previousData from current row for the changed fields\n\t\tconst previousData: Record<string, unknown> = {}\n\t\tconst currentRecord = deserializeRecord(currentRow, this.definition.fields)\n\t\tfor (const key of Object.keys(validated)) {\n\t\t\tpreviousData[key] = currentRecord[key]\n\t\t}\n\n\t\tconst sequenceNumber = this.getSequenceNumber()\n\t\tconst operation = await createOperation(\n\t\t\t{\n\t\t\t\tnodeId: this.nodeId,\n\t\t\t\ttype: 'update',\n\t\t\t\tcollection: this.name,\n\t\t\t\trecordId: id,\n\t\t\t\tdata: { ...validated },\n\t\t\t\tpreviousData,\n\t\t\t\tsequenceNumber,\n\t\t\t\tcausalDeps: [],\n\t\t\t\tschemaVersion: this.schema.version,\n\t\t\t},\n\t\t\tthis.clock,\n\t\t)\n\n\t\tconst serializedChanges = serializeRecord(validated, this.definition.fields)\n\t\tconst updateQuery = buildUpdateQuery(this.name, id, {\n\t\t\t...serializedChanges,\n\t\t\t_updated_at: now,\n\t\t})\n\t\tconst opRow = serializeOperation(operation)\n\t\tconst opInsert = buildInsertQuery(\n\t\t\t`_kora_ops_${this.name}`,\n\t\t\topRow as unknown as Record<string, unknown>,\n\t\t)\n\n\t\tawait this.adapter.transaction(async (tx) => {\n\t\t\tawait tx.execute(updateQuery.sql, updateQuery.params)\n\t\t\tawait tx.execute(opInsert.sql, opInsert.params)\n\t\t\tawait tx.execute(\n\t\t\t\t'INSERT OR REPLACE INTO _kora_version_vector (node_id, sequence_number) VALUES (?, ?)',\n\t\t\t\t[this.nodeId, sequenceNumber],\n\t\t\t)\n\t\t})\n\n\t\tthis.onMutation(this.name, operation)\n\n\t\t// Return the full updated record\n\t\tconst updatedRow = await this.findById(id)\n\t\tif (!updatedRow) {\n\t\t\tthrow new RecordNotFoundError(this.name, id)\n\t\t}\n\t\treturn updatedRow\n\t}\n\n\t/**\n\t * Soft-delete a record by its ID.\n\t *\n\t * @param id - The record ID to delete\n\t * @throws {RecordNotFoundError} If the record doesn't exist or is already deleted\n\t */\n\tasync delete(id: string): Promise<void> {\n\t\tconst currentRows = await this.adapter.query<RawCollectionRow>(\n\t\t\t`SELECT * FROM ${this.name} WHERE id = ? AND _deleted = 0`,\n\t\t\t[id],\n\t\t)\n\t\tif (!currentRows[0]) {\n\t\t\tthrow new RecordNotFoundError(this.name, id)\n\t\t}\n\n\t\tconst now = Date.now()\n\t\tconst sequenceNumber = this.getSequenceNumber()\n\t\tconst operation = await createOperation(\n\t\t\t{\n\t\t\t\tnodeId: this.nodeId,\n\t\t\t\ttype: 'delete',\n\t\t\t\tcollection: this.name,\n\t\t\t\trecordId: id,\n\t\t\t\tdata: null,\n\t\t\t\tpreviousData: null,\n\t\t\t\tsequenceNumber,\n\t\t\t\tcausalDeps: [],\n\t\t\t\tschemaVersion: this.schema.version,\n\t\t\t},\n\t\t\tthis.clock,\n\t\t)\n\n\t\tconst deleteQuery = buildSoftDeleteQuery(this.name, id, now)\n\t\tconst opRow = serializeOperation(operation)\n\t\tconst opInsert = buildInsertQuery(\n\t\t\t`_kora_ops_${this.name}`,\n\t\t\topRow as unknown as Record<string, unknown>,\n\t\t)\n\n\t\tawait this.adapter.transaction(async (tx) => {\n\t\t\tawait tx.execute(deleteQuery.sql, deleteQuery.params)\n\t\t\tawait tx.execute(opInsert.sql, opInsert.params)\n\t\t\tawait tx.execute(\n\t\t\t\t'INSERT OR REPLACE INTO _kora_version_vector (node_id, sequence_number) VALUES (?, ?)',\n\t\t\t\t[this.nodeId, sequenceNumber],\n\t\t\t)\n\t\t})\n\n\t\tthis.onMutation(this.name, operation)\n\t}\n\n\t/** Get the collection name */\n\tgetName(): string {\n\t\treturn this.name\n\t}\n\n\t/** Get the collection definition */\n\tgetDefinition(): CollectionDefinition {\n\t\treturn this.definition\n\t}\n}\n","import type { CollectionDefinition, FieldDescriptor } from '@korajs/core'\nimport { QueryError } from '../errors'\nimport type { QueryDescriptor, WhereOperators } from '../types'\n\n/**\n * Result of building a SQL query: the parameterized SQL string and its bound values.\n */\nexport interface SqlQuery {\n\tsql: string\n\tparams: unknown[]\n}\n\n/**\n * Build a SELECT query from a QueryDescriptor.\n * Automatically adds `WHERE _deleted = 0` to exclude soft-deleted records.\n *\n * @param descriptor - The query descriptor\n * @param fields - The field descriptors from the collection schema\n * @returns A parameterized SQL query\n */\nexport function buildSelectQuery(\n\tdescriptor: QueryDescriptor,\n\tfields: Record<string, FieldDescriptor>,\n): SqlQuery {\n\tconst params: unknown[] = []\n\tconst parts = [`SELECT * FROM ${descriptor.collection}`]\n\n\tconst whereClause = buildWhereClauseParts(descriptor.where, fields, params)\n\t// Always filter out soft-deleted records\n\tconst deletedFilter = '_deleted = 0'\n\tif (whereClause) {\n\t\tparts.push(`WHERE ${deletedFilter} AND ${whereClause}`)\n\t} else {\n\t\tparts.push(`WHERE ${deletedFilter}`)\n\t}\n\n\tif (descriptor.orderBy.length > 0) {\n\t\tconst orderParts = descriptor.orderBy.map((o) => {\n\t\t\tvalidateFieldName(o.field, fields)\n\t\t\treturn `${o.field} ${o.direction.toUpperCase()}`\n\t\t})\n\t\tparts.push(`ORDER BY ${orderParts.join(', ')}`)\n\t}\n\n\tif (descriptor.limit !== undefined) {\n\t\tparts.push(`LIMIT ${descriptor.limit}`)\n\t}\n\n\tif (descriptor.offset !== undefined) {\n\t\tparts.push(`OFFSET ${descriptor.offset}`)\n\t}\n\n\treturn { sql: parts.join(' '), params }\n}\n\n/**\n * Build a COUNT query from a QueryDescriptor.\n * Automatically adds `WHERE _deleted = 0`.\n *\n * @param descriptor - The query descriptor\n * @param fields - The field descriptors from the collection schema\n * @returns A parameterized SQL query that returns { count: number }\n */\nexport function buildCountQuery(\n\tdescriptor: QueryDescriptor,\n\tfields: Record<string, FieldDescriptor>,\n): SqlQuery {\n\tconst params: unknown[] = []\n\tconst parts = [`SELECT COUNT(*) as count FROM ${descriptor.collection}`]\n\n\tconst whereClause = buildWhereClauseParts(descriptor.where, fields, params)\n\tconst deletedFilter = '_deleted = 0'\n\tif (whereClause) {\n\t\tparts.push(`WHERE ${deletedFilter} AND ${whereClause}`)\n\t} else {\n\t\tparts.push(`WHERE ${deletedFilter}`)\n\t}\n\n\treturn { sql: parts.join(' '), params }\n}\n\n/**\n * Build an INSERT query for a collection record.\n *\n * @param collection - The collection name\n * @param record - The record data (already serialized with id, _created_at, _updated_at)\n * @returns A parameterized SQL query\n */\nexport function buildInsertQuery(collection: string, record: Record<string, unknown>): SqlQuery {\n\tconst columns = Object.keys(record)\n\tconst placeholders = columns.map(() => '?')\n\tconst params = Object.values(record)\n\n\tconst sql = `INSERT INTO ${collection} (${columns.join(', ')}) VALUES (${placeholders.join(', ')})`\n\treturn { sql, params }\n}\n\n/**\n * Build an UPDATE query for a collection record.\n *\n * @param collection - The collection name\n * @param id - The record ID\n * @param changes - The fields to update (already serialized)\n * @returns A parameterized SQL query\n */\nexport function buildUpdateQuery(\n\tcollection: string,\n\tid: string,\n\tchanges: Record<string, unknown>,\n): SqlQuery {\n\tconst setClauses = Object.keys(changes).map((col) => `${col} = ?`)\n\tconst params = [...Object.values(changes), id]\n\n\tconst sql = `UPDATE ${collection} SET ${setClauses.join(', ')} WHERE id = ?`\n\treturn { sql, params }\n}\n\n/**\n * Build a soft-delete query (SET _deleted = 1).\n *\n * @param collection - The collection name\n * @param id - The record ID\n * @param updatedAt - The timestamp to set on _updated_at\n * @returns A parameterized SQL query\n */\nexport function buildSoftDeleteQuery(collection: string, id: string, updatedAt: number): SqlQuery {\n\treturn {\n\t\tsql: `UPDATE ${collection} SET _deleted = 1, _updated_at = ? WHERE id = ?`,\n\t\tparams: [updatedAt, id],\n\t}\n}\n\n/**\n * Build a WHERE clause from conditions, validating field names against the schema.\n *\n * @param where - The where conditions\n * @param fields - The field descriptors from the collection schema\n * @returns The SQL WHERE clause string and params, or null if no conditions\n */\nexport function buildWhereClause(\n\twhere: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n): SqlQuery | null {\n\tconst params: unknown[] = []\n\tconst result = buildWhereClauseParts(where, fields, params)\n\tif (!result) return null\n\treturn { sql: result, params }\n}\n\n// --- Internal helpers ---\n\nconst VALID_OPERATORS = new Set(['$eq', '$ne', '$gt', '$gte', '$lt', '$lte', '$in'])\n\nfunction buildWhereClauseParts(\n\twhere: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n\tparams: unknown[],\n): string | null {\n\tconst conditions: string[] = []\n\n\tfor (const [fieldName, value] of Object.entries(where)) {\n\t\tvalidateFieldName(fieldName, fields)\n\t\tconst descriptor = fields[fieldName]\n\n\t\tif (value !== null && typeof value === 'object' && !Array.isArray(value)) {\n\t\t\t// Operator object: { $gt: 5, $lt: 10 }\n\t\t\tconst ops = value as WhereOperators\n\t\t\tfor (const [op, opValue] of Object.entries(ops)) {\n\t\t\t\tif (!VALID_OPERATORS.has(op)) {\n\t\t\t\t\tthrow new QueryError(`Unknown operator \"${op}\" on field \"${fieldName}\"`, {\n\t\t\t\t\t\tfield: fieldName,\n\t\t\t\t\t\toperator: op,\n\t\t\t\t\t\tvalidOperators: [...VALID_OPERATORS],\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t\tconditions.push(buildOperatorCondition(fieldName, op, opValue, descriptor, params))\n\t\t\t}\n\t\t} else {\n\t\t\t// Shorthand: { completed: false } means { completed: { $eq: false } }\n\t\t\tconditions.push(buildOperatorCondition(fieldName, '$eq', value, descriptor, params))\n\t\t}\n\t}\n\n\tif (conditions.length === 0) return null\n\treturn conditions.join(' AND ')\n}\n\nfunction buildOperatorCondition(\n\tfieldName: string,\n\toperator: string,\n\tvalue: unknown,\n\tdescriptor: FieldDescriptor | undefined,\n\tparams: unknown[],\n): string {\n\t// Serialize boolean values to 0/1 for SQL comparison\n\tconst sqlValue =\n\t\tdescriptor?.kind === 'boolean' && typeof value === 'boolean' ? (value ? 1 : 0) : value\n\n\tswitch (operator) {\n\t\tcase '$eq':\n\t\t\tif (sqlValue === null) {\n\t\t\t\treturn `${fieldName} IS NULL`\n\t\t\t}\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${fieldName} = ?`\n\t\tcase '$ne':\n\t\t\tif (sqlValue === null) {\n\t\t\t\treturn `${fieldName} IS NOT NULL`\n\t\t\t}\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${fieldName} != ?`\n\t\tcase '$gt':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${fieldName} > ?`\n\t\tcase '$gte':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${fieldName} >= ?`\n\t\tcase '$lt':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${fieldName} < ?`\n\t\tcase '$lte':\n\t\t\tparams.push(sqlValue)\n\t\t\treturn `${fieldName} <= ?`\n\t\tcase '$in': {\n\t\t\tif (!Array.isArray(sqlValue)) {\n\t\t\t\tthrow new QueryError(`$in operator requires an array value for field \"${fieldName}\"`, {\n\t\t\t\t\tfield: fieldName,\n\t\t\t\t\treceived: typeof sqlValue,\n\t\t\t\t})\n\t\t\t}\n\t\t\tconst placeholders = sqlValue.map(() => '?')\n\t\t\tfor (const item of sqlValue) {\n\t\t\t\tparams.push(\n\t\t\t\t\tdescriptor?.kind === 'boolean' && typeof item === 'boolean' ? (item ? 1 : 0) : item,\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn `${fieldName} IN (${placeholders.join(', ')})`\n\t\t}\n\t\tdefault:\n\t\t\tthrow new QueryError(`Unknown operator \"${operator}\"`, { operator })\n\t}\n}\n\nfunction validateFieldName(fieldName: string, fields: Record<string, FieldDescriptor>): void {\n\t// Allow schema fields plus metadata fields that map to query-able columns\n\tconst allowedFields = new Set([\n\t\t...Object.keys(fields),\n\t\t'id',\n\t\t'createdAt',\n\t\t'updatedAt',\n\t\t'_created_at',\n\t\t'_updated_at',\n\t])\n\tif (!allowedFields.has(fieldName)) {\n\t\tthrow new QueryError(\n\t\t\t`Unknown field \"${fieldName}\" in query. Available fields: ${[...allowedFields].join(', ')}`,\n\t\t\t{ field: fieldName },\n\t\t)\n\t}\n}\n","import { HybridLogicalClock } from '@korajs/core'\nimport type { CollectionDefinition, FieldDescriptor, Operation } from '@korajs/core'\nimport type { CollectionRecord, OperationRow, RawCollectionRow } from '../types'\nimport { decodeRichtext, encodeRichtext } from './richtext-serializer'\n\n/**\n * Serialize a JS record to SQL-compatible values for INSERT/UPDATE.\n * Transforms: boolean → 0/1, array → JSON string, richtext → Yjs binary update.\n *\n * @param data - The record data with JS-native types\n * @param fields - The field descriptors from the schema\n * @returns An object with SQL-compatible values\n */\nexport function serializeRecord(\n\tdata: Record<string, unknown>,\n\tfields: Record<string, FieldDescriptor>,\n): Record<string, unknown> {\n\tconst result: Record<string, unknown> = {}\n\tfor (const [key, value] of Object.entries(data)) {\n\t\tconst descriptor = fields[key]\n\t\tif (!descriptor) {\n\t\t\tresult[key] = value\n\t\t\tcontinue\n\t\t}\n\t\tresult[key] = serializeValue(value, descriptor)\n\t}\n\treturn result\n}\n\n/**\n * Deserialize a SQL row to JS-native types for the application layer.\n * Transforms: 0/1 → boolean, JSON string → array, strips _deleted, maps _created_at/_updated_at.\n *\n * @param row - The raw SQL row\n * @param fields - The field descriptors from the schema\n * @returns A CollectionRecord with JS-native types\n */\nexport function deserializeRecord(\n\trow: RawCollectionRow,\n\tfields: Record<string, FieldDescriptor>,\n): CollectionRecord {\n\tconst result: CollectionRecord = {\n\t\tid: row.id,\n\t\tcreatedAt: row._created_at,\n\t\tupdatedAt: row._updated_at,\n\t}\n\n\tfor (const [key, descriptor] of Object.entries(fields)) {\n\t\tconst rawValue = row[key]\n\t\tif (rawValue === undefined || rawValue === null) {\n\t\t\tresult[key] = rawValue ?? null\n\t\t\tcontinue\n\t\t}\n\t\tresult[key] = deserializeValue(rawValue, descriptor)\n\t}\n\n\treturn result\n}\n\n/**\n * Serialize an Operation to a row for the operations log table.\n *\n * @param op - The operation to serialize\n * @returns An OperationRow suitable for SQL INSERT\n */\nexport function serializeOperation(op: Operation): OperationRow {\n\treturn {\n\t\tid: op.id,\n\t\tnode_id: op.nodeId,\n\t\ttype: op.type,\n\t\trecord_id: op.recordId,\n\t\tdata: op.data ? JSON.stringify(op.data) : null,\n\t\tprevious_data: op.previousData ? JSON.stringify(op.previousData) : null,\n\t\ttimestamp: HybridLogicalClock.serialize(op.timestamp),\n\t\tsequence_number: op.sequenceNumber,\n\t\tcausal_deps: JSON.stringify(op.causalDeps),\n\t\tschema_version: op.schemaVersion,\n\t}\n}\n\n/**\n * Deserialize a row from the operations log table back to an Operation.\n *\n * @param row - The raw operation row from SQL\n * @returns The deserialized Operation object\n */\nexport function deserializeOperation(row: OperationRow): Operation {\n\treturn {\n\t\tid: row.id,\n\t\tnodeId: row.node_id,\n\t\ttype: row.type as Operation['type'],\n\t\tcollection: '', // Collection name is derived from the table name by the caller\n\t\trecordId: row.record_id,\n\t\tdata: row.data ? (JSON.parse(row.data) as Record<string, unknown>) : null,\n\t\tpreviousData: row.previous_data\n\t\t\t? (JSON.parse(row.previous_data) as Record<string, unknown>)\n\t\t\t: null,\n\t\ttimestamp: HybridLogicalClock.deserialize(row.timestamp),\n\t\tsequenceNumber: row.sequence_number,\n\t\tcausalDeps: JSON.parse(row.causal_deps) as string[],\n\t\tschemaVersion: row.schema_version,\n\t}\n}\n\n/**\n * Deserialize an operation row with collection name already known.\n */\nexport function deserializeOperationWithCollection(\n\trow: OperationRow,\n\tcollection: string,\n): Operation {\n\tconst op = deserializeOperation(row)\n\treturn { ...op, collection }\n}\n\nfunction serializeValue(value: unknown, descriptor: FieldDescriptor): unknown {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tswitch (descriptor.kind) {\n\t\tcase 'boolean':\n\t\t\treturn value ? 1 : 0\n\t\tcase 'array':\n\t\t\treturn JSON.stringify(value)\n\t\tcase 'richtext':\n\t\t\treturn encodeRichtext(value as string | Uint8Array | ArrayBuffer)\n\t\tdefault:\n\t\t\treturn value\n\t}\n}\n\nfunction deserializeValue(value: unknown, descriptor: FieldDescriptor): unknown {\n\tswitch (descriptor.kind) {\n\t\tcase 'boolean':\n\t\t\treturn value === 1 || value === true\n\t\tcase 'array':\n\t\t\tif (typeof value === 'string') {\n\t\t\t\treturn JSON.parse(value) as unknown[]\n\t\t\t}\n\t\t\treturn value\n\t\tcase 'richtext':\n\t\t\treturn decodeRichtext(value)\n\t\tdefault:\n\t\t\treturn value\n\t}\n}\n","import * as Y from 'yjs'\n\nconst TEXT_KEY = 'content'\n\nexport type RichtextInput = string | Uint8Array | ArrayBuffer | null | undefined\n\n/**\n * Encodes richtext values into Yjs document updates.\n */\nexport function encodeRichtext(value: RichtextInput): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tif (typeof value === 'string') {\n\t\tconst doc = new Y.Doc()\n\t\tdoc.getText(TEXT_KEY).insert(0, value)\n\t\treturn Y.encodeStateAsUpdate(doc)\n\t}\n\n\tif (value instanceof Uint8Array) {\n\t\treturn value\n\t}\n\n\tif (value instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\tthrow new Error('Richtext value must be a string, Uint8Array, ArrayBuffer, null, or undefined.')\n}\n\n/**\n * Decodes driver-provided richtext values into Uint8Array.\n */\nexport function decodeRichtext(value: unknown): Uint8Array | null {\n\tif (value === null || value === undefined) {\n\t\treturn null\n\t}\n\n\tif (typeof Buffer !== 'undefined' && Buffer.isBuffer(value)) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\tif (value instanceof Uint8Array) {\n\t\treturn value\n\t}\n\n\tif (value instanceof ArrayBuffer) {\n\t\treturn new Uint8Array(value)\n\t}\n\n\tthrow new Error('Richtext storage value must be Uint8Array, ArrayBuffer, Buffer, null, or undefined.')\n}\n\n/**\n * Reads plain text from a richtext Yjs state update.\n */\nexport function richtextToPlainText(value: RichtextInput): string {\n\tconst encoded = encodeRichtext(value)\n\tif (!encoded) return ''\n\n\tconst doc = new Y.Doc()\n\tY.applyUpdate(doc, encoded)\n\treturn doc.getText(TEXT_KEY).toString()\n}\n","/**\n * Minimal pluralization/singularization utilities for relation name resolution.\n * Handles common English patterns — not meant to be exhaustive.\n */\n\nfunction isVowel(char: string | undefined): boolean {\n\tif (!char) return false\n\treturn 'aeiouAEIOU'.includes(char)\n}\n\n/**\n * Pluralize a word using common English rules.\n *\n * @example\n * ```\n * pluralize('project') // 'projects'\n * pluralize('category') // 'categories'\n * pluralize('match') // 'matches'\n * ```\n */\nexport function pluralize(word: string): string {\n\tif (word.endsWith('s')) return word\n\tif (word.endsWith('y') && !isVowel(word[word.length - 2])) {\n\t\treturn `${word.slice(0, -1)}ies`\n\t}\n\tif (word.endsWith('sh') || word.endsWith('ch') || word.endsWith('x') || word.endsWith('z')) {\n\t\treturn `${word}es`\n\t}\n\treturn `${word}s`\n}\n\n/**\n * Singularize a word using common English rules.\n *\n * @example\n * ```\n * singularize('projects') // 'project'\n * singularize('categories') // 'category'\n * singularize('matches') // 'match'\n * ```\n */\nexport function singularize(word: string): string {\n\tif (word.endsWith('ies') && !isVowel(word[word.length - 4])) {\n\t\treturn `${word.slice(0, -3)}y`\n\t}\n\tif (\n\t\tword.endsWith('shes') ||\n\t\tword.endsWith('ches') ||\n\t\tword.endsWith('xes') ||\n\t\tword.endsWith('zes')\n\t) {\n\t\treturn word.slice(0, -2)\n\t}\n\tif (word.endsWith('ses')) {\n\t\treturn word.slice(0, -2)\n\t}\n\tif (word.endsWith('s') && !word.endsWith('ss')) {\n\t\treturn word.slice(0, -1)\n\t}\n\treturn word\n}\n","import type { CollectionDefinition, SchemaDefinition } from '@korajs/core'\nimport { deserializeRecord } from '../serialization/serializer'\nimport type { SubscriptionManager } from '../subscription/subscription-manager'\nimport type {\n\tCollectionRecord,\n\tOrderByDirection,\n\tQueryDescriptor,\n\tRawCollectionRow,\n\tStorageAdapter,\n\tSubscriptionCallback,\n\tWhereClause,\n} from '../types'\nimport { pluralize, singularize } from './pluralize'\nimport { buildCountQuery, buildSelectQuery } from './sql-builder'\n\n/**\n * Fluent query builder for constructing and executing collection queries.\n * Supports where, orderBy, limit, offset, include, exec, count, and subscribe.\n *\n * The generic parameter `T` defaults to `CollectionRecord` for backward compatibility\n * but can be narrowed to a specific record type for full type inference.\n *\n * @example\n * ```typescript\n * const todos = await app.todos\n * .where({ completed: false })\n * .orderBy('createdAt', 'desc')\n * .limit(10)\n * .exec()\n * ```\n */\nexport class QueryBuilder<T = CollectionRecord> {\n\tprivate descriptor: QueryDescriptor\n\n\tconstructor(\n\t\tprivate readonly collectionName: string,\n\t\tprivate readonly definition: CollectionDefinition,\n\t\tprivate readonly adapter: StorageAdapter,\n\t\tprivate readonly subscriptionManager: SubscriptionManager,\n\t\tinitialWhere: WhereClause = {},\n\t\tprivate readonly schema?: SchemaDefinition,\n\t) {\n\t\tthis.descriptor = {\n\t\t\tcollection: collectionName,\n\t\t\twhere: { ...initialWhere },\n\t\t\torderBy: [],\n\t\t}\n\t}\n\n\t/**\n\t * Add WHERE conditions (AND semantics, merged with existing conditions).\n\t */\n\twhere(conditions: WhereClause): QueryBuilder<T> {\n\t\tconst clone = this.clone()\n\t\tclone.descriptor = {\n\t\t\t...clone.descriptor,\n\t\t\twhere: { ...clone.descriptor.where, ...conditions },\n\t\t}\n\t\treturn clone\n\t}\n\n\t/**\n\t * Add ORDER BY clause.\n\t */\n\torderBy(field: string, direction: OrderByDirection = 'asc'): QueryBuilder<T> {\n\t\tconst clone = this.clone()\n\t\tclone.descriptor = {\n\t\t\t...clone.descriptor,\n\t\t\torderBy: [...clone.descriptor.orderBy, { field, direction }],\n\t\t}\n\t\treturn clone\n\t}\n\n\t/**\n\t * Set result limit.\n\t */\n\tlimit(n: number): QueryBuilder<T> {\n\t\tconst clone = this.clone()\n\t\tclone.descriptor = { ...clone.descriptor, limit: n }\n\t\treturn clone\n\t}\n\n\t/**\n\t * Set result offset.\n\t */\n\toffset(n: number): QueryBuilder<T> {\n\t\tconst clone = this.clone()\n\t\tclone.descriptor = { ...clone.descriptor, offset: n }\n\t\treturn clone\n\t}\n\n\t/**\n\t * Include related records in the query results.\n\t * Follows relations defined in the schema to batch-fetch related data.\n\t *\n\t * @param targets - Relation target names (collection names or relation names)\n\t * @returns A new QueryBuilder with include targets added\n\t *\n\t * @example\n\t * ```typescript\n\t * const todosWithProject = await app.todos\n\t * .where({ completed: false })\n\t * .include('project')\n\t * .exec()\n\t * ```\n\t */\n\tinclude(...targets: string[]): QueryBuilder<T> {\n\t\tconst clone = this.clone()\n\t\tconst existing = clone.descriptor.include ?? []\n\t\tclone.descriptor = {\n\t\t\t...clone.descriptor,\n\t\t\tinclude: [...existing, ...targets],\n\t\t}\n\t\treturn clone\n\t}\n\n\t/**\n\t * Execute the query and return results.\n\t */\n\tasync exec(): Promise<T[]> {\n\t\tconst { sql, params } = buildSelectQuery(this.descriptor, this.definition.fields)\n\t\tconst rows = await this.adapter.query<RawCollectionRow>(sql, params)\n\t\tconst records = rows.map((row) => deserializeRecord(row, this.definition.fields))\n\n\t\t// Resolve includes if any\n\t\tif (this.descriptor.include && this.descriptor.include.length > 0 && this.schema) {\n\t\t\tawait this.resolveIncludes(records)\n\t\t}\n\n\t\treturn records as T[]\n\t}\n\n\t/**\n\t * Execute a COUNT query and return the count.\n\t */\n\tasync count(): Promise<number> {\n\t\tconst { sql, params } = buildCountQuery(this.descriptor, this.definition.fields)\n\t\tconst rows = await this.adapter.query<{ count: number }>(sql, params)\n\t\treturn rows[0]?.count ?? 0\n\t}\n\n\t/**\n\t * Subscribe to query results. Callback is called immediately with current results,\n\t * then again whenever the results change due to mutations.\n\t *\n\t * @returns An unsubscribe function\n\t */\n\tsubscribe(callback: SubscriptionCallback<T>): () => void {\n\t\tconst executeFn = () => this.exec()\n\n\t\t// Resolve includeCollections for subscription tracking\n\t\tconst descriptorCopy = { ...this.descriptor }\n\t\tif (descriptorCopy.include && descriptorCopy.include.length > 0 && this.schema) {\n\t\t\tdescriptorCopy.includeCollections = this.resolveIncludeCollections(descriptorCopy.include)\n\t\t}\n\n\t\t// Use registerAndFetch to execute immediately, set lastResults,\n\t\t// and call callback — ensuring subsequent flushes diff correctly.\n\t\treturn this.subscriptionManager.registerAndFetch(\n\t\t\tdescriptorCopy,\n\t\t\tcallback as SubscriptionCallback<CollectionRecord>,\n\t\t\texecuteFn as () => Promise<CollectionRecord[]>,\n\t\t)\n\t}\n\n\t/** Get the internal descriptor (for testing/debugging) */\n\tgetDescriptor(): QueryDescriptor {\n\t\treturn { ...this.descriptor }\n\t}\n\n\tprivate clone(): QueryBuilder<T> {\n\t\tconst qb = new QueryBuilder<T>(\n\t\t\tthis.collectionName,\n\t\t\tthis.definition,\n\t\t\tthis.adapter,\n\t\t\tthis.subscriptionManager,\n\t\t\t{},\n\t\t\tthis.schema,\n\t\t)\n\t\tqb.descriptor = {\n\t\t\t...this.descriptor,\n\t\t\twhere: { ...this.descriptor.where },\n\t\t\torderBy: [...this.descriptor.orderBy],\n\t\t\tinclude: this.descriptor.include ? [...this.descriptor.include] : undefined,\n\t\t\tincludeCollections: this.descriptor.includeCollections\n\t\t\t\t? [...this.descriptor.includeCollections]\n\t\t\t\t: undefined,\n\t\t}\n\t\treturn qb\n\t}\n\n\t/**\n\t * Resolve include targets to their actual collection names for subscription tracking.\n\t */\n\tprivate resolveIncludeCollections(targets: string[]): string[] {\n\t\tif (!this.schema) return []\n\t\tconst collections: string[] = []\n\n\t\tfor (const target of targets) {\n\t\t\tconst relation = this.findRelation(target)\n\t\t\tif (relation) {\n\t\t\t\t// For many-to-one: the related collection is `relation.to`\n\t\t\t\t// For one-to-many: the related collection is `relation.from`\n\t\t\t\tif (relation.from === this.collectionName) {\n\t\t\t\t\tcollections.push(relation.to)\n\t\t\t\t} else {\n\t\t\t\t\tcollections.push(relation.from)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn collections\n\t}\n\n\t/**\n\t * Resolve includes after primary query, batch-fetching related records.\n\t */\n\tprivate async resolveIncludes(records: CollectionRecord[]): Promise<void> {\n\t\tif (!this.schema || !this.descriptor.include || records.length === 0) return\n\n\t\tfor (const target of this.descriptor.include) {\n\t\t\tconst relation = this.findRelation(target)\n\t\t\tif (!relation) {\n\t\t\t\tthrow new QueryError(\n\t\t\t\t\t`No relation found for include target \"${target}\" on collection \"${this.collectionName}\". ` +\n\t\t\t\t\t\t`Check that a relation is defined in your schema that connects \"${this.collectionName}\" to \"${target}\".`,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\tif (relation.from === this.collectionName) {\n\t\t\t\t// Many-to-one or one-to-one: primary has FK → fetch parent records\n\t\t\t\tawait this.resolveManyToOneInclude(records, relation, target)\n\t\t\t} else {\n\t\t\t\t// One-to-many: related collection has FK → fetch children\n\t\t\t\tawait this.resolveOneToManyInclude(records, relation, target)\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Many-to-one: collect FK values from primary results, batch-fetch related, attach as singular.\n\t */\n\tprivate async resolveManyToOneInclude(\n\t\trecords: CollectionRecord[],\n\t\trelation: { from: string; to: string; field: string },\n\t\ttarget: string,\n\t): Promise<void> {\n\t\tconst fkField = relation.field\n\t\tconst fkValues = records\n\t\t\t.map((r) => r[fkField])\n\t\t\t.filter((v): v is string => v !== null && v !== undefined && typeof v === 'string')\n\n\t\tif (fkValues.length === 0) {\n\t\t\t// All null FKs — set property to null on all records\n\t\t\tconst propName = singularize(target)\n\t\t\tfor (const record of records) {\n\t\t\t\t;(record as Record<string, unknown>)[propName] = null\n\t\t\t}\n\t\t\treturn\n\t\t}\n\n\t\tconst uniqueFks = [...new Set(fkValues)]\n\t\tconst relatedCollection = relation.to\n\t\tconst relatedDef = this.schema?.collections[relatedCollection]\n\t\tif (!relatedDef) return\n\n\t\t// Batch fetch: SELECT * FROM <to> WHERE id IN (...) AND _deleted = 0\n\t\tconst placeholders = uniqueFks.map(() => '?').join(', ')\n\t\tconst sql = `SELECT * FROM ${relatedCollection} WHERE id IN (${placeholders}) AND _deleted = 0`\n\t\tconst rows = await this.adapter.query<RawCollectionRow>(sql, uniqueFks)\n\t\tconst relatedRecords = rows.map((row) => deserializeRecord(row, relatedDef.fields))\n\n\t\t// Build lookup\n\t\tconst lookup = new Map<string, CollectionRecord>()\n\t\tfor (const r of relatedRecords) {\n\t\t\tlookup.set(r.id, r)\n\t\t}\n\n\t\t// Attach as singular property\n\t\tconst propName = singularize(target)\n\t\tfor (const record of records) {\n\t\t\tconst fk = record[fkField] as string | null\n\t\t\t;(record as Record<string, unknown>)[propName] = fk ? (lookup.get(fk) ?? null) : null\n\t\t}\n\t}\n\n\t/**\n\t * One-to-many: collect primary IDs, batch-fetch children, attach as array.\n\t */\n\tprivate async resolveOneToManyInclude(\n\t\trecords: CollectionRecord[],\n\t\trelation: { from: string; to: string; field: string },\n\t\ttarget: string,\n\t): Promise<void> {\n\t\tconst primaryIds = records.map((r) => r.id)\n\t\tconst relatedCollection = relation.from\n\t\tconst relatedDef = this.schema?.collections[relatedCollection]\n\t\tif (!relatedDef) return\n\n\t\tconst fkField = relation.field\n\t\tconst placeholders = primaryIds.map(() => '?').join(', ')\n\t\tconst sql = `SELECT * FROM ${relatedCollection} WHERE ${fkField} IN (${placeholders}) AND _deleted = 0`\n\t\tconst rows = await this.adapter.query<RawCollectionRow>(sql, primaryIds)\n\t\tconst relatedRecords = rows.map((row) => deserializeRecord(row, relatedDef.fields))\n\n\t\t// Group by FK\n\t\tconst grouped = new Map<string, CollectionRecord[]>()\n\t\tfor (const r of relatedRecords) {\n\t\t\tconst fk = r[fkField] as string\n\t\t\tif (!grouped.has(fk)) {\n\t\t\t\tgrouped.set(fk, [])\n\t\t\t}\n\t\t\tgrouped.get(fk)?.push(r)\n\t\t}\n\n\t\t// Attach as array property\n\t\tconst propName = pluralize(target)\n\t\tfor (const record of records) {\n\t\t\t;(record as Record<string, unknown>)[propName] = grouped.get(record.id) ?? []\n\t\t}\n\t}\n\n\t/**\n\t * Find a relation definition matching the include target.\n\t * Searches by relation name, target collection name, and singularized/pluralized variants.\n\t */\n\tprivate findRelation(\n\t\ttarget: string,\n\t): { from: string; to: string; field: string; type: string } | null {\n\t\tif (!this.schema) return null\n\n\t\tfor (const [_name, rel] of Object.entries(this.schema.relations)) {\n\t\t\t// Direct match: target is the related collection name\n\t\t\tif (rel.from === this.collectionName && rel.to === target) return rel\n\t\t\tif (rel.to === this.collectionName && rel.from === target) return rel\n\n\t\t\t// Singularized match: \"project\" matches relation to \"projects\"\n\t\t\tif (rel.from === this.collectionName && rel.to === pluralize(target)) return rel\n\t\t\tif (rel.to === this.collectionName && rel.from === pluralize(target)) return rel\n\n\t\t\t// Pluralized match: \"todos\" matches relation from \"todos\"\n\t\t\tif (rel.from === this.collectionName && rel.to === singularize(target)) return rel\n\t\t\tif (rel.to === this.collectionName && rel.from === singularize(target)) return rel\n\t\t}\n\n\t\treturn null\n\t}\n}\n\n/**\n * Error thrown when a query encounters an invalid state.\n */\nclass QueryError extends Error {\n\tconstructor(message: string) {\n\t\tsuper(message)\n\t\tthis.name = 'QueryError'\n\t}\n}\n","import type { Operation } from '@korajs/core'\nimport type {\n\tCollectionRecord,\n\tQueryDescriptor,\n\tSubscription,\n\tSubscriptionCallback,\n} from '../types'\n\nlet nextSubId = 0\n\n/**\n * Manages reactive subscriptions. When a mutation occurs on a collection,\n * affected subscriptions are re-evaluated in a microtask batch and callbacks\n * are invoked only if results actually changed.\n */\nexport class SubscriptionManager {\n\tprivate subscriptions = new Map<string, Subscription>()\n\tprivate pendingCollections = new Set<string>()\n\tprivate flushScheduled = false\n\n\t/**\n\t * Register a new subscription.\n\t *\n\t * @param descriptor - The query descriptor defining what this subscription watches\n\t * @param callback - Called with results whenever they change\n\t * @param executeFn - Function to re-execute the query and get current results\n\t * @returns An unsubscribe function\n\t */\n\tregister(\n\t\tdescriptor: QueryDescriptor,\n\t\tcallback: SubscriptionCallback<CollectionRecord>,\n\t\texecuteFn: () => Promise<CollectionRecord[]>,\n\t): () => void {\n\t\tconst id = `sub_${++nextSubId}`\n\t\tconst subscription: Subscription = {\n\t\t\tid,\n\t\t\tdescriptor,\n\t\t\tcallback,\n\t\t\texecuteFn,\n\t\t\tlastResults: [],\n\t\t}\n\t\tthis.subscriptions.set(id, subscription)\n\n\t\treturn () => {\n\t\t\tthis.subscriptions.delete(id)\n\t\t}\n\t}\n\n\t/**\n\t * Register a subscription and immediately execute the query.\n\t * The initial results are stored as lastResults so subsequent flushes\n\t * correctly diff against the initial state.\n\t *\n\t * @returns An unsubscribe function\n\t */\n\tregisterAndFetch(\n\t\tdescriptor: QueryDescriptor,\n\t\tcallback: SubscriptionCallback<CollectionRecord>,\n\t\texecuteFn: () => Promise<CollectionRecord[]>,\n\t): () => void {\n\t\tconst id = `sub_${++nextSubId}`\n\t\tconst subscription: Subscription = {\n\t\t\tid,\n\t\t\tdescriptor,\n\t\t\tcallback,\n\t\t\texecuteFn,\n\t\t\tlastResults: [],\n\t\t}\n\t\tthis.subscriptions.set(id, subscription)\n\n\t\t// Execute immediately, set lastResults, and call callback\n\t\texecuteFn().then((results) => {\n\t\t\t// Guard: subscription may have been removed before the async fetch completes\n\t\t\tif (this.subscriptions.has(id)) {\n\t\t\t\tsubscription.lastResults = results\n\t\t\t\tcallback(results)\n\t\t\t}\n\t\t})\n\n\t\treturn () => {\n\t\t\tthis.subscriptions.delete(id)\n\t\t}\n\t}\n\n\t/**\n\t * Notify the manager that a mutation occurred on a collection.\n\t * Schedules a microtask flush to batch multiple mutations in the same tick.\n\t */\n\tnotify(collection: string, _operation: Operation): void {\n\t\tthis.pendingCollections.add(collection)\n\t\tthis.scheduleFlush()\n\t}\n\n\t/**\n\t * Immediately flush all pending notifications.\n\t * Useful for testing. In production, flushing happens via microtask.\n\t */\n\tasync flush(): Promise<void> {\n\t\tif (this.pendingCollections.size === 0) return\n\n\t\tconst collections = new Set(this.pendingCollections)\n\t\tthis.pendingCollections.clear()\n\t\tthis.flushScheduled = false\n\n\t\t// Find affected subscriptions (including those with related collection includes)\n\t\tconst affected: Subscription[] = []\n\t\tfor (const sub of this.subscriptions.values()) {\n\t\t\tif (collections.has(sub.descriptor.collection)) {\n\t\t\t\taffected.push(sub)\n\t\t\t} else if (sub.descriptor.includeCollections) {\n\t\t\t\t// Re-evaluate if a mutation affects an included (related) collection\n\t\t\t\tfor (const incCol of sub.descriptor.includeCollections) {\n\t\t\t\t\tif (collections.has(incCol)) {\n\t\t\t\t\t\taffected.push(sub)\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Re-execute and diff\n\t\tfor (const sub of affected) {\n\t\t\ttry {\n\t\t\t\tconst newResults = await sub.executeFn()\n\t\t\t\tif (!this.resultsEqual(sub.lastResults, newResults)) {\n\t\t\t\t\tsub.lastResults = newResults\n\t\t\t\t\tsub.callback(newResults)\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Subscription re-execution failed — skip silently for now.\n\t\t\t\t// In future, we could emit an error event for DevTools.\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Remove all subscriptions. Called on store close.\n\t */\n\tclear(): void {\n\t\tthis.subscriptions.clear()\n\t\tthis.pendingCollections.clear()\n\t\tthis.flushScheduled = false\n\t}\n\n\t/** Number of active subscriptions (for testing/debugging) */\n\tget size(): number {\n\t\treturn this.subscriptions.size\n\t}\n\n\tprivate scheduleFlush(): void {\n\t\tif (this.flushScheduled) return\n\t\tthis.flushScheduled = true\n\t\tqueueMicrotask(() => {\n\t\t\tthis.flush()\n\t\t})\n\t}\n\n\t/**\n\t * Compare two result sets. Uses length check + JSON comparison as pragmatic approach.\n\t * Sufficient for typical query results. Can be optimized to id-based diffing if profiling shows need.\n\t */\n\tprivate resultsEqual(prev: CollectionRecord[], next: CollectionRecord[]): boolean {\n\t\tif (prev.length !== next.length) return false\n\t\t// Fast path: both empty\n\t\tif (prev.length === 0) return true\n\t\treturn JSON.stringify(prev) === JSON.stringify(next)\n\t}\n}\n"],"mappings":"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","names":["HybridLogicalClock","generateUUIDv7","QueryError","propName","HybridLogicalClock","generateUUIDv7"]}
@@ -0,0 +1,10 @@
1
+ import {
2
+ Mutex,
3
+ WebWorkerBridge
4
+ } from "./chunk-DXKLAQ6P.js";
5
+ import "./chunk-LAWV6CFH.js";
6
+ export {
7
+ Mutex,
8
+ WebWorkerBridge
9
+ };
10
+ //# sourceMappingURL=sqlite-wasm-channel-46AOWNPM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Request message sent from the main thread to the SQLite WASM worker.
3
+ * Each request carries a unique `id` for response correlation.
4
+ */
5
+ type WorkerRequest = {
6
+ id: number;
7
+ type: 'open';
8
+ ddlStatements: string[];
9
+ } | {
10
+ id: number;
11
+ type: 'close';
12
+ } | {
13
+ id: number;
14
+ type: 'execute';
15
+ sql: string;
16
+ params?: unknown[];
17
+ } | {
18
+ id: number;
19
+ type: 'query';
20
+ sql: string;
21
+ params?: unknown[];
22
+ } | {
23
+ id: number;
24
+ type: 'begin';
25
+ } | {
26
+ id: number;
27
+ type: 'commit';
28
+ } | {
29
+ id: number;
30
+ type: 'rollback';
31
+ } | {
32
+ id: number;
33
+ type: 'migrate';
34
+ from: number;
35
+ to: number;
36
+ statements: string[];
37
+ } | {
38
+ id: number;
39
+ type: 'export';
40
+ } | {
41
+ id: number;
42
+ type: 'import';
43
+ data: Uint8Array;
44
+ };
45
+ /**
46
+ * Response message sent from the worker back to the main thread.
47
+ * Matches the request `id` for correlation.
48
+ */
49
+ type WorkerResponse = {
50
+ id: number;
51
+ type: 'success';
52
+ data?: unknown;
53
+ } | {
54
+ id: number;
55
+ type: 'error';
56
+ message: string;
57
+ code: string;
58
+ context?: Record<string, unknown>;
59
+ };
60
+ /**
61
+ * Abstraction over the communication channel with the SQLite WASM worker.
62
+ * In browsers, this is backed by a real Web Worker via MessagePort.
63
+ * In Node.js tests, this is backed by better-sqlite3 via MockWorkerBridge.
64
+ */
65
+ interface WorkerBridge {
66
+ /** Send a request to the worker and wait for a response. */
67
+ send(request: WorkerRequest): Promise<WorkerResponse>;
68
+ /** Terminate the worker. Safe to call multiple times. */
69
+ terminate(): void;
70
+ }
71
+ /**
72
+ * Async mutex for serializing transaction access across the async worker boundary.
73
+ * Only one transaction may be active at a time.
74
+ */
75
+ declare class Mutex {
76
+ private locked;
77
+ private waiters;
78
+ /**
79
+ * Acquire the mutex. Returns a release function.
80
+ * If the mutex is already held, the caller waits until it's released.
81
+ */
82
+ acquire(): Promise<() => void>;
83
+ private createRelease;
84
+ }
85
+ /**
86
+ * WorkerBridge implementation for browser environments.
87
+ * Communicates with an actual Web Worker running SQLite WASM.
88
+ */
89
+ declare class WebWorkerBridge implements WorkerBridge {
90
+ private worker;
91
+ private pending;
92
+ private nextId;
93
+ private terminated;
94
+ private timeoutMs;
95
+ /**
96
+ * @param workerUrl - URL to the sqlite-wasm-worker script
97
+ * @param timeoutMs - Timeout for worker responses in milliseconds (default: 30000)
98
+ */
99
+ constructor(workerUrl: string | URL, timeoutMs?: number);
100
+ send(request: WorkerRequest): Promise<WorkerResponse>;
101
+ terminate(): void;
102
+ }
103
+
104
+ export { Mutex as M, type WorkerBridge as W, WebWorkerBridge as a, type WorkerRequest as b, type WorkerResponse as c };
@@ -0,0 +1,104 @@
1
+ /**
2
+ * Request message sent from the main thread to the SQLite WASM worker.
3
+ * Each request carries a unique `id` for response correlation.
4
+ */
5
+ type WorkerRequest = {
6
+ id: number;
7
+ type: 'open';
8
+ ddlStatements: string[];
9
+ } | {
10
+ id: number;
11
+ type: 'close';
12
+ } | {
13
+ id: number;
14
+ type: 'execute';
15
+ sql: string;
16
+ params?: unknown[];
17
+ } | {
18
+ id: number;
19
+ type: 'query';
20
+ sql: string;
21
+ params?: unknown[];
22
+ } | {
23
+ id: number;
24
+ type: 'begin';
25
+ } | {
26
+ id: number;
27
+ type: 'commit';
28
+ } | {
29
+ id: number;
30
+ type: 'rollback';
31
+ } | {
32
+ id: number;
33
+ type: 'migrate';
34
+ from: number;
35
+ to: number;
36
+ statements: string[];
37
+ } | {
38
+ id: number;
39
+ type: 'export';
40
+ } | {
41
+ id: number;
42
+ type: 'import';
43
+ data: Uint8Array;
44
+ };
45
+ /**
46
+ * Response message sent from the worker back to the main thread.
47
+ * Matches the request `id` for correlation.
48
+ */
49
+ type WorkerResponse = {
50
+ id: number;
51
+ type: 'success';
52
+ data?: unknown;
53
+ } | {
54
+ id: number;
55
+ type: 'error';
56
+ message: string;
57
+ code: string;
58
+ context?: Record<string, unknown>;
59
+ };
60
+ /**
61
+ * Abstraction over the communication channel with the SQLite WASM worker.
62
+ * In browsers, this is backed by a real Web Worker via MessagePort.
63
+ * In Node.js tests, this is backed by better-sqlite3 via MockWorkerBridge.
64
+ */
65
+ interface WorkerBridge {
66
+ /** Send a request to the worker and wait for a response. */
67
+ send(request: WorkerRequest): Promise<WorkerResponse>;
68
+ /** Terminate the worker. Safe to call multiple times. */
69
+ terminate(): void;
70
+ }
71
+ /**
72
+ * Async mutex for serializing transaction access across the async worker boundary.
73
+ * Only one transaction may be active at a time.
74
+ */
75
+ declare class Mutex {
76
+ private locked;
77
+ private waiters;
78
+ /**
79
+ * Acquire the mutex. Returns a release function.
80
+ * If the mutex is already held, the caller waits until it's released.
81
+ */
82
+ acquire(): Promise<() => void>;
83
+ private createRelease;
84
+ }
85
+ /**
86
+ * WorkerBridge implementation for browser environments.
87
+ * Communicates with an actual Web Worker running SQLite WASM.
88
+ */
89
+ declare class WebWorkerBridge implements WorkerBridge {
90
+ private worker;
91
+ private pending;
92
+ private nextId;
93
+ private terminated;
94
+ private timeoutMs;
95
+ /**
96
+ * @param workerUrl - URL to the sqlite-wasm-worker script
97
+ * @param timeoutMs - Timeout for worker responses in milliseconds (default: 30000)
98
+ */
99
+ constructor(workerUrl: string | URL, timeoutMs?: number);
100
+ send(request: WorkerRequest): Promise<WorkerResponse>;
101
+ terminate(): void;
102
+ }
103
+
104
+ export { Mutex as M, type WorkerBridge as W, WebWorkerBridge as a, type WorkerRequest as b, type WorkerResponse as c };
@@ -0,0 +1,106 @@
1
+ import { SchemaDefinition, KoraEventEmitter } from '@korajs/core';
2
+
3
+ /**
4
+ * Transaction interface for executing multiple operations atomically.
5
+ */
6
+ interface Transaction {
7
+ execute(sql: string, params?: unknown[]): Promise<void>;
8
+ query<T>(sql: string, params?: unknown[]): Promise<T[]>;
9
+ }
10
+ /**
11
+ * Migration plan containing SQL statements and optional data transforms.
12
+ */
13
+ interface MigrationPlan {
14
+ statements: string[];
15
+ transforms?: Array<(row: Record<string, unknown>) => Record<string, unknown>>;
16
+ }
17
+ /**
18
+ * Storage adapter interface. All storage backends must implement this.
19
+ * Operations are async to support both sync (better-sqlite3) and async (IndexedDB, WASM) backends.
20
+ */
21
+ interface StorageAdapter {
22
+ /** Open or create the database */
23
+ open(schema: SchemaDefinition): Promise<void>;
24
+ /** Close the database and release resources */
25
+ close(): Promise<void>;
26
+ /** Execute a write query (INSERT, UPDATE, DELETE) within a transaction */
27
+ execute(sql: string, params?: unknown[]): Promise<void>;
28
+ /** Execute a read query (SELECT) */
29
+ query<T>(sql: string, params?: unknown[]): Promise<T[]>;
30
+ /** Execute multiple operations atomically */
31
+ transaction(fn: (tx: Transaction) => Promise<void>): Promise<void>;
32
+ /** Apply a schema migration */
33
+ migrate(from: number, to: number, migration: MigrationPlan): Promise<void>;
34
+ }
35
+ /**
36
+ * Configuration for creating a Store instance.
37
+ */
38
+ interface StoreConfig {
39
+ schema: SchemaDefinition;
40
+ adapter: StorageAdapter;
41
+ /** Optional node ID. If omitted, one is generated or loaded from the database. */
42
+ nodeId?: string;
43
+ /** Optional event emitter. When provided, local mutations emit 'operation:created' events. */
44
+ emitter?: KoraEventEmitter;
45
+ }
46
+ /**
47
+ * Operators for where clause conditions.
48
+ */
49
+ interface WhereOperators {
50
+ $eq?: unknown;
51
+ $ne?: unknown;
52
+ $gt?: number | string;
53
+ $gte?: number | string;
54
+ $lt?: number | string;
55
+ $lte?: number | string;
56
+ $in?: unknown[];
57
+ }
58
+ /**
59
+ * Where clause: field name to value (shorthand for $eq) or WhereOperators.
60
+ */
61
+ type WhereClause = Record<string, unknown | WhereOperators>;
62
+ /**
63
+ * Order direction for sorting query results.
64
+ */
65
+ type OrderByDirection = 'asc' | 'desc';
66
+ /**
67
+ * Order-by clause: field name and optional direction.
68
+ */
69
+ interface OrderByClause {
70
+ field: string;
71
+ direction: OrderByDirection;
72
+ }
73
+ /**
74
+ * Internal representation of a query to be compiled to SQL.
75
+ */
76
+ interface QueryDescriptor {
77
+ collection: string;
78
+ where: WhereClause;
79
+ orderBy: OrderByClause[];
80
+ limit?: number;
81
+ offset?: number;
82
+ /** Relation names to include in results (for relational queries). */
83
+ include?: string[];
84
+ /** Resolved collection names for included relations (for subscription tracking). */
85
+ includeCollections?: string[];
86
+ }
87
+ /**
88
+ * Callback for reactive subscriptions. Receives the current result set.
89
+ */
90
+ type SubscriptionCallback<T> = (results: T[]) => void;
91
+ /**
92
+ * Record type returned from collection queries.
93
+ * Includes the id and mapped metadata fields.
94
+ */
95
+ interface CollectionRecord {
96
+ id: string;
97
+ createdAt: number;
98
+ updatedAt: number;
99
+ [key: string]: unknown;
100
+ }
101
+ /**
102
+ * Result of a remote operation application.
103
+ */
104
+ type ApplyResult = 'applied' | 'duplicate' | 'skipped';
105
+
106
+ export type { ApplyResult as A, CollectionRecord as C, MigrationPlan as M, OrderByDirection as O, QueryDescriptor as Q, StorageAdapter as S, Transaction as T, WhereClause as W, SubscriptionCallback as a, StoreConfig as b, OrderByClause as c, WhereOperators as d };
@@ -0,0 +1,106 @@
1
+ import { SchemaDefinition, KoraEventEmitter } from '@korajs/core';
2
+
3
+ /**
4
+ * Transaction interface for executing multiple operations atomically.
5
+ */
6
+ interface Transaction {
7
+ execute(sql: string, params?: unknown[]): Promise<void>;
8
+ query<T>(sql: string, params?: unknown[]): Promise<T[]>;
9
+ }
10
+ /**
11
+ * Migration plan containing SQL statements and optional data transforms.
12
+ */
13
+ interface MigrationPlan {
14
+ statements: string[];
15
+ transforms?: Array<(row: Record<string, unknown>) => Record<string, unknown>>;
16
+ }
17
+ /**
18
+ * Storage adapter interface. All storage backends must implement this.
19
+ * Operations are async to support both sync (better-sqlite3) and async (IndexedDB, WASM) backends.
20
+ */
21
+ interface StorageAdapter {
22
+ /** Open or create the database */
23
+ open(schema: SchemaDefinition): Promise<void>;
24
+ /** Close the database and release resources */
25
+ close(): Promise<void>;
26
+ /** Execute a write query (INSERT, UPDATE, DELETE) within a transaction */
27
+ execute(sql: string, params?: unknown[]): Promise<void>;
28
+ /** Execute a read query (SELECT) */
29
+ query<T>(sql: string, params?: unknown[]): Promise<T[]>;
30
+ /** Execute multiple operations atomically */
31
+ transaction(fn: (tx: Transaction) => Promise<void>): Promise<void>;
32
+ /** Apply a schema migration */
33
+ migrate(from: number, to: number, migration: MigrationPlan): Promise<void>;
34
+ }
35
+ /**
36
+ * Configuration for creating a Store instance.
37
+ */
38
+ interface StoreConfig {
39
+ schema: SchemaDefinition;
40
+ adapter: StorageAdapter;
41
+ /** Optional node ID. If omitted, one is generated or loaded from the database. */
42
+ nodeId?: string;
43
+ /** Optional event emitter. When provided, local mutations emit 'operation:created' events. */
44
+ emitter?: KoraEventEmitter;
45
+ }
46
+ /**
47
+ * Operators for where clause conditions.
48
+ */
49
+ interface WhereOperators {
50
+ $eq?: unknown;
51
+ $ne?: unknown;
52
+ $gt?: number | string;
53
+ $gte?: number | string;
54
+ $lt?: number | string;
55
+ $lte?: number | string;
56
+ $in?: unknown[];
57
+ }
58
+ /**
59
+ * Where clause: field name to value (shorthand for $eq) or WhereOperators.
60
+ */
61
+ type WhereClause = Record<string, unknown | WhereOperators>;
62
+ /**
63
+ * Order direction for sorting query results.
64
+ */
65
+ type OrderByDirection = 'asc' | 'desc';
66
+ /**
67
+ * Order-by clause: field name and optional direction.
68
+ */
69
+ interface OrderByClause {
70
+ field: string;
71
+ direction: OrderByDirection;
72
+ }
73
+ /**
74
+ * Internal representation of a query to be compiled to SQL.
75
+ */
76
+ interface QueryDescriptor {
77
+ collection: string;
78
+ where: WhereClause;
79
+ orderBy: OrderByClause[];
80
+ limit?: number;
81
+ offset?: number;
82
+ /** Relation names to include in results (for relational queries). */
83
+ include?: string[];
84
+ /** Resolved collection names for included relations (for subscription tracking). */
85
+ includeCollections?: string[];
86
+ }
87
+ /**
88
+ * Callback for reactive subscriptions. Receives the current result set.
89
+ */
90
+ type SubscriptionCallback<T> = (results: T[]) => void;
91
+ /**
92
+ * Record type returned from collection queries.
93
+ * Includes the id and mapped metadata fields.
94
+ */
95
+ interface CollectionRecord {
96
+ id: string;
97
+ createdAt: number;
98
+ updatedAt: number;
99
+ [key: string]: unknown;
100
+ }
101
+ /**
102
+ * Result of a remote operation application.
103
+ */
104
+ type ApplyResult = 'applied' | 'duplicate' | 'skipped';
105
+
106
+ export type { ApplyResult as A, CollectionRecord as C, MigrationPlan as M, OrderByDirection as O, QueryDescriptor as Q, StorageAdapter as S, Transaction as T, WhereClause as W, SubscriptionCallback as a, StoreConfig as b, OrderByClause as c, WhereOperators as d };