@datafn/client 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/client.ts","../src/events/filter.ts","../src/events/bus.ts","../src/errors.ts","../src/tables/table.ts","../src/tables/registry.ts","../src/remote/unwrap.ts","../src/offline/query.ts","../src/plugins/run-hooks.ts","../src/query.ts","../src/offline/mutate.ts","../src/mutate.ts","../src/transact.ts","../src/signals/querySignal.ts","../src/sync/apply.ts","../src/sync.ts","../src/adapters/memoryStorage.ts","../src/adapters/indexedDbStorage.ts"],"sourcesContent":["/**\n * @datafn/client public API\n */\n\nexport {\n createDatafnClient,\n type DatafnClient,\n type DatafnClientConfig,\n type DatafnRemoteAdapter,\n} from \"./client.js\";\nexport { EventBus, type EventHandler } from \"./events/bus.js\";\nexport { matchesFilter, type EventFilter } from \"./events/filter.js\";\nexport { type DatafnClientError, createClientError } from \"./errors.js\";\nexport { unwrapRemoteSuccess } from \"./remote/unwrap.js\";\nexport { type DatafnTable } from \"./tables/table.js\";\nexport {\n type DatafnStorageAdapter,\n type DatafnHydrationState,\n type DatafnChangelogEntry,\n} from \"./storage.js\";\n\n// Storage Adapters\nexport { MemoryStorageAdapter } from \"./adapters/memoryStorage.js\";\nexport { IndexedDbStorageAdapter } from \"./adapters/indexedDbStorage.js\";\n","/**\n * DataFn client factory\n */\n\nimport type { DatafnSchema, DatafnPlugin } from \"@datafn/core\";\nimport { validateSchema } from \"@datafn/core\";\nimport { EventBus, type EventHandler } from \"./events/bus.js\";\nimport type { EventFilter } from \"./events/filter.js\";\nimport { createClientError } from \"./errors.js\";\nimport { TableRegistry } from \"./tables/registry.js\";\nimport type { DatafnTable } from \"./tables/table.js\";\nimport { executeQuery } from \"./query.js\";\nimport { executeMutation } from \"./mutate.js\";\nimport { executeTransact } from \"./transact.js\";\nimport { SignalRegistry } from \"./signals/querySignal.js\";\nimport { createSyncFacade, type SyncFacade } from \"./sync.js\";\nimport type { DatafnStorageAdapter } from \"./storage.js\";\n\nexport interface DatafnRemoteAdapter {\n query(q: unknown): Promise<unknown>;\n mutation(m: unknown): Promise<unknown>;\n transact(t: unknown): Promise<unknown>;\n seed(payload: unknown): Promise<unknown>;\n clone(payload: unknown): Promise<unknown>;\n pull(payload: unknown): Promise<unknown>;\n push(payload: unknown): Promise<unknown>;\n}\n\nexport interface DatafnClientConfig {\n schema: DatafnSchema;\n remote: DatafnRemoteAdapter;\n /**\n * Optional plugins for client-side hook execution\n */\n plugins?: DatafnPlugin[];\n /**\n * Stable client/device identifier used for idempotency and offline change logs.\n * Required when `storage` is provided.\n */\n clientId?: string;\n /**\n * Local persistence adapter. When provided, sync results are applied to local storage.\n */\n storage?: DatafnStorageAdapter;\n getTimestamp?: () => number; // For testing with fake clock\n}\n\nexport interface DatafnClient {\n table<TRecord = unknown>(name: string): DatafnTable<TRecord>;\n query(q: unknown | unknown[]): Promise<unknown>;\n mutate(mutation: unknown | unknown[]): Promise<unknown>;\n transact(payload: unknown): Promise<unknown>;\n subscribe(handler: EventHandler, filter?: EventFilter): () => void;\n sync: SyncFacade;\n}\n\n/**\n * Create a DataFn client\n */\nexport function createDatafnClient(config: DatafnClientConfig): DatafnClient {\n // Validate schema at client creation (CLIENT-API-001)\n const validationResult = validateSchema(config.schema);\n if (!validationResult.ok) {\n createClientError(\n validationResult.error.code,\n validationResult.error.message,\n validationResult.error.details as {\n path: string;\n [key: string]: unknown;\n },\n );\n }\n\n const schema = validationResult.result;\n const eventBus = new EventBus();\n const getTimestamp = config.getTimestamp || (() => Date.now());\n\n // Reserved keys that should not trigger table lookup (CLIENT-REG-002)\n const RESERVED_KEYS = new Set([\"then\", \"toJSON\", \"inspect\"]);\n\n // Create the client object first (will add table() method after registry is created)\n const client: DatafnClient = {\n table: null as any, // Will be set below\n\n /**\n * Execute a query (CLIENT-QUERY-001, CLIENT-OFFLINE-QUERY-001)\n */\n async query(q: unknown | unknown[]) {\n return executeQuery(\n config.remote,\n q,\n config.storage,\n config.plugins || [],\n schema,\n );\n },\n\n /**\n * Sync facade (CLIENT-SYNC-001, CLIENT-SYNC-APPLY-001)\n */\n sync: createSyncFacade(config.remote, config.storage),\n\n /**\n * Execute a transaction (CLIENT-TX-001)\n */\n async transact(payload: unknown) {\n return executeTransact(config.remote, payload);\n },\n\n /**\n * Execute a mutation (CLIENT-MUT-001, CLIENT-OFFLINE-MUT-001)\n */\n async mutate(mutation: unknown | unknown[]) {\n return executeMutation(\n config.remote,\n eventBus,\n getTimestamp,\n mutation,\n config.storage,\n config.plugins || [],\n schema,\n );\n },\n\n /**\n * Subscribe to events\n */\n subscribe(handler: EventHandler, filter?: EventFilter) {\n return eventBus.subscribe(handler, filter);\n },\n };\n\n // Create signal registry (CLIENT-SIGNAL-001)\n const signalRegistry = new SignalRegistry(client, eventBus);\n\n // Create table registry with client and signal registry (CLIENT-REG-001)\n const registry = new TableRegistry(schema, client, signalRegistry);\n\n // Set table method now that registry exists\n client.table = (name: string) => registry.getTable(name);\n\n // Wrap client in Proxy for table property access (CLIENT-REG-001, CLIENT-REG-002)\n return new Proxy(client, {\n get(target, prop) {\n // Handle reserved keys - return undefined without throwing\n if (typeof prop === \"string\" && RESERVED_KEYS.has(prop)) {\n return undefined;\n }\n\n // If property exists on target, return it\n if (prop in target) {\n return target[prop as keyof typeof target];\n }\n\n // Check if it's a table name\n if (typeof prop === \"string\") {\n return registry.getTable(prop);\n }\n\n return undefined;\n },\n });\n}\n","/**\n * Event filtering logic\n */\n\nimport type { DatafnEvent } from \"@datafn/core\";\n\nexport interface EventFilter {\n type?: string | string[];\n resource?: string | string[];\n ids?: string | string[];\n mutationId?: string | string[];\n action?: string | string[]; // CLIENT-FILTER-001\n fields?: string | string[]; // CLIENT-FILTER-001\n contextKeys?: string[]; // CLIENT-FILTER-001\n}\n\n/**\n * Check if an event matches the filter\n */\nexport function matchesFilter(\n event: DatafnEvent,\n filter?: EventFilter,\n): boolean {\n if (!filter) return true;\n\n // Match type\n if (filter.type !== undefined) {\n if (Array.isArray(filter.type)) {\n if (!filter.type.includes(event.type)) return false;\n } else {\n if (event.type !== filter.type) return false;\n }\n }\n\n // Match resource\n if (filter.resource !== undefined && event.resource) {\n if (Array.isArray(filter.resource)) {\n if (!filter.resource.includes(event.resource)) return false;\n } else {\n if (event.resource !== filter.resource) return false;\n }\n }\n\n // Match ids (any of the event ids must match)\n if (filter.ids !== undefined && event.ids) {\n const filterIds = Array.isArray(filter.ids) ? filter.ids : [filter.ids];\n const eventIds = Array.isArray(event.ids) ? event.ids : [event.ids];\n const hasMatch = eventIds.some((id) => filterIds.includes(id));\n if (!hasMatch) return false;\n }\n\n // Match mutationId\n if (filter.mutationId !== undefined && event.mutationId) {\n if (Array.isArray(filter.mutationId)) {\n if (!filter.mutationId.includes(event.mutationId)) return false;\n } else {\n if (event.mutationId !== filter.mutationId) return false;\n }\n }\n\n // Match action (CLIENT-FILTER-001)\n if (filter.action !== undefined && event.action) {\n if (Array.isArray(filter.action)) {\n if (!filter.action.includes(event.action)) return false;\n } else {\n if (event.action !== filter.action) return false;\n }\n }\n\n // Match fields - non-empty intersection (CLIENT-FILTER-001)\n if (filter.fields !== undefined && event.fields) {\n const filterFields = Array.isArray(filter.fields)\n ? filter.fields\n : [filter.fields];\n const eventFields = Array.isArray(event.fields)\n ? event.fields\n : [event.fields];\n\n // Check for non-empty intersection\n const hasIntersection = filterFields.some((f) => eventFields.includes(f));\n if (!hasIntersection) return false;\n }\n\n // Match contextKeys - all must exist (CLIENT-FILTER-001)\n if (filter.contextKeys !== undefined && filter.contextKeys.length > 0) {\n if (!event.context || typeof event.context !== \"object\") {\n return false;\n }\n\n const ctx = event.context as Record<string, unknown>;\n const allExist = filter.contextKeys.every((key) => key in ctx);\n if (!allExist) return false;\n }\n\n return true;\n}\n","/**\n * In-process event bus\n */\n\nimport type { DatafnEvent } from \"@datafn/core\";\nimport { matchesFilter, type EventFilter } from \"./filter.js\";\n\nexport type EventHandler = (event: DatafnEvent) => void;\n\ninterface Subscription {\n id: number;\n handler: EventHandler;\n filter?: EventFilter;\n}\n\n/**\n * Simple in-process event bus\n */\nexport class EventBus {\n private subscriptions: Subscription[] = [];\n private nextId = 1;\n\n /**\n * Subscribe to events with optional filtering\n */\n subscribe(handler: EventHandler, filter?: EventFilter): () => void {\n const subscription: Subscription = {\n id: this.nextId++,\n handler,\n filter,\n };\n\n this.subscriptions.push(subscription);\n\n // Return unsubscribe function\n return () => {\n this.subscriptions = this.subscriptions.filter(\n (s) => s.id !== subscription.id\n );\n };\n }\n\n /**\n * Emit an event to all matching subscribers\n */\n emit(event: DatafnEvent): void {\n for (const subscription of this.subscriptions) {\n if (matchesFilter(event, subscription.filter)) {\n subscription.handler(event);\n }\n }\n }\n}\n","/**\n * DataFn Client Error Types\n */\n\nimport type { DatafnErrorCode } from \"@datafn/core\";\n\nexport type DatafnClientError = {\n code: DatafnErrorCode | \"TRANSPORT_ERROR\";\n message: string;\n details: { path: string; [key: string]: unknown };\n};\n\n/**\n * Create a DataFnClientError and throw it\n */\nexport function createClientError(\n code: DatafnClientError[\"code\"],\n message: string,\n details: { path: string; [key: string]: unknown },\n): never {\n const error: DatafnClientError = {\n code,\n message,\n details,\n };\n throw error;\n}\n/**\n * Check if an error is a transport/retriable error.\n * Returns true for network errors, timeouts, or explicit TRANSPORT_ERROR code.\n * Returns false for logic errors (validation, auth, missing resource, etc).\n */\nexport function isTransportError(error: unknown): boolean {\n if (typeof error !== \"object\" || error === null) return false;\n\n const err = error as any;\n\n // Explicit DataFn code\n if (err.code === \"TRANSPORT_ERROR\") return true;\n\n // Network/Fetch errors match common patterns\n // 1. fetch() throws TypeError on network failure\n // 2. AbortError on timeout\n if (err.name === \"TypeError\" && err.message.includes(\"fetch\")) return true;\n if (err.name === \"AbortError\") return true; // Timeout\n\n // Check failure status if available (e.g. 503, 504)\n // Note: DataFnRemoteAdapter usually unwraps to DataFnError, so status might not be here directly\n // unless wrapped.\n\n return false;\n}\n","/**\n * DataFn Table Handle\n *\n * Represents a table/resource from the schema with methods for query, mutation, signals, and subscriptions.\n */\n\nimport type { DatafnSignal } from \"@datafn/core\";\nimport type { EventHandler } from \"../events/bus.js\";\nimport type { EventFilter } from \"../events/filter.js\";\nimport type { SignalRegistry } from \"../signals/querySignal.js\";\n\nexport interface DatafnTable<TRecord = unknown> {\n name: string;\n version: number;\n\n query(q: unknown): Promise<unknown>;\n mutate(m: unknown): Promise<unknown>;\n transact(payload: unknown): Promise<unknown>;\n signal(q: unknown): DatafnSignal<unknown>;\n subscribe(handler: EventHandler, filter?: EventFilter): () => void;\n}\n\n// Forward declaration for client interface\ninterface ClientWithQuery {\n query(q: unknown | unknown[]): Promise<unknown>;\n mutate(m: unknown | unknown[]): Promise<unknown>;\n transact(payload: unknown): Promise<unknown>;\n subscribe(handler: EventHandler, filter?: EventFilter): () => void;\n}\n\n/**\n * Create a table handle (internal factory)\n */\nexport function createTable(\n name: string,\n version: number,\n client: ClientWithQuery,\n signalRegistry: SignalRegistry\n): DatafnTable {\n return {\n name,\n version,\n\n /**\n * Execute a query with resource/version merged (CLIENT-QUERY-001)\n */\n async query(q: unknown): Promise<unknown> {\n // Merge query fragment with table resource/version\n const fragment = (typeof q === \"object\" && q !== null ? q : {}) as Record<\n string,\n unknown\n >;\n\n // Remove resource/version from fragment if present (table is authoritative)\n const { resource: _r, version: _v, ...rest } = fragment;\n\n // Build full query\n const fullQuery = {\n resource: name,\n version,\n ...rest,\n };\n\n // Delegate to client.query and return single result\n return client.query(fullQuery);\n },\n\n /**\n * Execute a mutation with resource/version merged (CLIENT-MUT-001)\n */\n async mutate(m: unknown): Promise<unknown> {\n // Merge mutation fragment with table resource/version\n const fragment = (typeof m === \"object\" && m !== null ? m : {}) as Record<\n string,\n unknown\n >;\n\n // Remove resource/version from fragment if present (table is authoritative)\n const { resource: _r, version: _v, ...rest } = fragment;\n\n // Build full mutation\n const fullMutation = {\n resource: name,\n version,\n ...rest,\n };\n\n // Delegate to client.mutate and return single result\n return client.mutate(fullMutation);\n },\n\n /**\n * Execute a transaction (CLIENT-TX-001)\n */\n async transact(payload: unknown): Promise<unknown> {\n // Delegate directly to client.transact without modification\n return client.transact(payload);\n },\n\n /**\n * Create reactive query signal (CLIENT-SIGNAL-001)\n */\n signal(q: unknown): DatafnSignal<unknown> {\n // Merge query fragment with table resource/version\n const fragment = (typeof q === \"object\" && q !== null ? q : {}) as Record<\n string,\n unknown\n >;\n\n // Remove resource/version from fragment if present (table is authoritative)\n const { resource: _r, version: _v, ...rest } = fragment;\n\n // Build full query\n const fullQuery = {\n resource: name,\n version,\n ...rest,\n };\n\n // Get or create signal from registry (ensures caching by dfqlKey)\n return signalRegistry.getSignal(fullQuery);\n },\n\n /**\n * Subscribe to events for this table's resource (CLIENT-SUB-001)\n */\n subscribe(handler: EventHandler, filter?: EventFilter): () => void {\n // Inject resource filter (table is authoritative)\n const tableFilter: EventFilter = {\n ...filter,\n resource: name, // Always use table's resource, ignore user-provided\n };\n\n return client.subscribe(handler, tableFilter);\n },\n };\n}\n","/**\n * Table Registry\n *\n * Manages table handles with object identity caching.\n */\n\nimport type { DatafnSchema } from \"@datafn/core\";\nimport { createClientError } from \"../errors.js\";\nimport { createTable, type DatafnTable } from \"./table.js\";\nimport type { SignalRegistry } from \"../signals/querySignal.js\";\n\n// Forward declaration for client interface\ninterface ClientWithQuery {\n query(q: unknown | unknown[]): Promise<unknown>;\n mutate(m: unknown | unknown[]): Promise<unknown>;\n subscribe(handler: any, filter?: any): () => void;\n}\n\nexport class TableRegistry {\n private schema: DatafnSchema;\n private tables: Map<string, DatafnTable>;\n private client: ClientWithQuery;\n private signalRegistry: SignalRegistry;\n\n constructor(\n schema: DatafnSchema,\n client: ClientWithQuery,\n signalRegistry: SignalRegistry\n ) {\n this.schema = schema;\n this.tables = new Map();\n this.client = client;\n this.signalRegistry = signalRegistry;\n }\n\n /**\n * Get a table handle by name.\n * Caches table instances for object identity.\n * Throws DFQL_UNKNOWN_RESOURCE for unknown tables.\n */\n getTable(name: string): DatafnTable {\n // Check cache first\n const cached = this.tables.get(name);\n if (cached) {\n return cached;\n }\n\n // Find resource in schema\n const resource = this.schema.resources.find((r) => r.name === name);\n if (!resource) {\n createClientError(\"DFQL_UNKNOWN_RESOURCE\", `Unknown resource: ${name}`, {\n path: \"resource\",\n resource: name,\n });\n }\n\n // Create and cache table\n const table = createTable(\n resource.name,\n resource.version,\n this.client,\n this.signalRegistry\n );\n this.tables.set(name, table);\n return table;\n }\n\n /**\n * Get all declared table names from schema\n */\n getTableNames(): string[] {\n return this.schema.resources.map((r) => r.name);\n }\n}\n","/**\n * Remote Response Unwrapping Utilities\n *\n * Handles both wrapped (DatafnEnvelope) and unwrapped server responses.\n */\n\nimport { createClientError } from \"../errors.js\";\n\n/**\n * Unwrap a remote response that may be wrapped in DatafnEnvelope or unwrapped.\n *\n * - If wrapped success `{ ok: true, result }`, return `result`\n * - If wrapped error `{ ok: false, error }`, throw DatafnClientError\n * - If unwrapped query result `{ data, nextCursor }`, return as-is\n * - If unwrapped aggregate result `{ groups, nextCursor }`, return as-is\n * - Otherwise throw TRANSPORT_ERROR\n */\nexport function unwrapRemoteSuccess<T>(response: unknown): T {\n if (typeof response !== \"object\" || response === null) {\n createClientError(\n \"TRANSPORT_ERROR\",\n \"Transport error: unexpected response shape\",\n { path: \"$\" }\n );\n }\n\n const resp = response as Record<string, unknown>;\n\n // Check for wrapped envelope\n if (\"ok\" in resp) {\n if (resp.ok === true && \"result\" in resp) {\n // Wrapped success\n return resp.result as T;\n } else if (resp.ok === false && \"error\" in resp) {\n // Wrapped error\n const error = resp.error as {\n code?: string;\n message?: string;\n details?: { path?: string; [key: string]: unknown };\n };\n\n createClientError(\n (error.code as any) || \"INTERNAL\",\n error.message || \"Unknown error\",\n (error.details as any) || { path: \"$\" }\n );\n }\n }\n\n // Check for unwrapped query result\n if (\"data\" in resp && \"nextCursor\" in resp) {\n return response as T;\n }\n\n // Check for unwrapped aggregate result\n if (\"groups\" in resp && \"nextCursor\" in resp) {\n return response as T;\n }\n\n // Unknown shape\n createClientError(\n \"TRANSPORT_ERROR\",\n \"Transport error: unexpected response shape\",\n { path: \"$\" }\n );\n}\n","/**\n * Offline Query Execution\n *\n * Execute queries locally against storage adapter for tables in 'ready' hydration state.\n * Supports: filters (operators), sort, pagination, selection.\n */\n\nimport type { DatafnStorageAdapter } from \"../storage.js\";\n\n/**\n * Query result shape matching DFQL protocol\n */\nexport type QueryResult = {\n data: Array<Record<string, unknown>>;\n nextCursor: null | string;\n};\n\n/**\n * Execute a query locally against storage.\n *\n * Implements robust DFQL subset for local-first apps.\n */\nexport async function executeLocalQuery(\n storage: DatafnStorageAdapter,\n query: Record<string, unknown>,\n): Promise<QueryResult> {\n const resource = query.resource as string;\n const select = (query.select as string[]) || [];\n const filters = (query.filters as Record<string, unknown>) || {};\n const sort =\n (query.sort as Array<{ field: string; dir: \"asc\" | \"desc\" }>) || [];\n const limit = typeof query.limit === \"number\" ? query.limit : undefined;\n const offset = typeof query.offset === \"number\" ? query.offset : 0;\n\n // 1. Fetch all records\n // Adapters ensure deterministic id sorting by default\n let records = await storage.listRecords(resource);\n\n // 2. Apply Filters\n if (Object.keys(filters).length > 0) {\n records = records.filter((record) => matchesFilters(record, filters));\n }\n\n // 3. Apply Sort\n if (sort.length > 0) {\n records.sort(createComparator(sort));\n }\n\n // 4. Pagination\n if (offset > 0) {\n records = records.slice(offset);\n }\n\n if (limit !== undefined) {\n records = records.slice(0, limit);\n }\n\n // 5. Selection\n const data = records.map((record) => projectFields(record, select));\n\n return {\n data,\n nextCursor: null, // Local cursor logic omitted for MVP (id-based or offset based)\n };\n}\n\n/**\n * Check if a record matches filters\n */\nfunction matchesFilters(\n record: Record<string, unknown>,\n filters: Record<string, unknown>,\n): boolean {\n for (const [key, filterValue] of Object.entries(filters)) {\n const recordValue = getDotPath(record, key);\n\n // Operator Logic\n if (typeof filterValue === \"object\" && filterValue !== null) {\n if (\n !evaluateOperators(recordValue, filterValue as Record<string, unknown>)\n ) {\n return false;\n }\n } else {\n // Implicit Equality\n if (recordValue !== filterValue) {\n return false;\n }\n }\n }\n return true;\n}\n\n/**\n * Evaluate DFQL operators\n */\nfunction evaluateOperators(\n value: unknown,\n operators: Record<string, unknown>,\n): boolean {\n for (const [op, target] of Object.entries(operators)) {\n switch (op) {\n case \"$eq\":\n case \"eq\":\n if (value !== target) return false;\n break;\n case \"$neq\":\n case \"neq\":\n if (value === target) return false;\n break;\n case \"$in\":\n case \"in\":\n if (Array.isArray(target) && !target.includes(value)) return false;\n break;\n case \"$gt\":\n case \"gt\":\n if (!((value as any) > (target as any))) return false;\n break;\n case \"$lt\":\n case \"lt\":\n if (!((value as any) < (target as any))) return false;\n break;\n case \"$gte\":\n case \"gte\":\n if (!((value as any) >= (target as any))) return false;\n break;\n case \"$lte\":\n case \"lte\":\n if (!((value as any) <= (target as any))) return false;\n break;\n case \"$contains\":\n case \"contains\":\n if (typeof value === \"string\" && typeof target === \"string\") {\n if (!value.includes(target)) return false;\n } else if (Array.isArray(value)) {\n if (!value.includes(target)) return false;\n } else {\n return false;\n }\n break;\n }\n }\n return true;\n}\n\n/**\n * Create a comparator function for multi-field sorting\n */\nfunction createComparator(\n sortRules: Array<{ field: string; dir: \"asc\" | \"desc\" }>,\n) {\n return (a: Record<string, unknown>, b: Record<string, unknown>) => {\n for (const rule of sortRules) {\n const valA = getDotPath(a, rule.field);\n const valB = getDotPath(b, rule.field);\n\n if (valA === valB) continue;\n\n const comparison = valA < valB ? -1 : 1;\n return rule.dir === \"asc\" ? comparison : -comparison;\n }\n // Deterministic tie-breaker: id\n const idA = a.id as string;\n const idB = b.id as string;\n return idA < idB ? -1 : idA > idB ? 1 : 0;\n };\n}\n\n/**\n * Get value from object by dot path\n */\nfunction getDotPath(obj: any, path: string): any {\n return path.split(\".\").reduce((curr, part) => curr?.[part], obj);\n}\n\n/**\n * Project selected fields\n */\nfunction projectFields(\n record: Record<string, unknown>,\n select: string[],\n): Record<string, unknown> {\n if (!select || select.length === 0) {\n return { ...record }; // Shallow copy\n }\n\n const projected: Record<string, unknown> = { id: record.id };\n for (const field of select) {\n // Handle simple top-level fields for MVP\n // Nested projection would require recursive logic\n if (field in record) {\n projected[field] = record[field];\n }\n }\n return projected;\n}\n","/**\n * Plugin hook runner for client-side plugins\n * Implements CLIENT-PLUG-001: registration order, runsOn enforcement, fail semantics\n */\n\nimport type {\n DatafnPlugin,\n DatafnHookContext,\n DatafnSchema,\n} from \"@datafn/core\";\n\n/**\n * Filter plugins to only those that run on client\n */\nexport function getClientPlugins(plugins: DatafnPlugin[]): DatafnPlugin[] {\n return plugins.filter((p) => p.runsOn && p.runsOn.includes(\"client\"));\n}\n\n/**\n * Run beforeQuery hooks (fail-closed)\n * Throws deterministic error if any hook fails\n */\nexport async function runBeforeQuery(\n plugins: DatafnPlugin[],\n schema: DatafnSchema,\n query: unknown,\n): Promise<unknown> {\n let transformed = query;\n const ctx: DatafnHookContext = { env: \"client\", schema };\n\n for (const plugin of getClientPlugins(plugins)) {\n if (plugin.beforeQuery) {\n try {\n const result = await plugin.beforeQuery(ctx, transformed);\n if (result !== undefined) {\n transformed = result;\n }\n } catch (error: any) {\n // Fail-closed: throw with deterministic path\n throw {\n code: error.code || \"INTERNAL\",\n message: error.message || \"Plugin error\",\n details: {\n path: `plugins.${plugin.name}.beforeQuery`,\n ...(error.details || {}),\n },\n };\n }\n }\n }\n\n return transformed;\n}\n\n/**\n * Run afterQuery hooks (fail-open)\n * Logs errors but continues execution\n */\nexport async function runAfterQuery(\n plugins: DatafnPlugin[],\n schema: DatafnSchema,\n query: unknown,\n result: unknown,\n): Promise<unknown> {\n let transformed = result;\n const ctx: DatafnHookContext = { env: \"client\", schema };\n\n for (const plugin of getClientPlugins(plugins)) {\n if (plugin.afterQuery) {\n try {\n const pluginResult = await plugin.afterQuery(ctx, query, transformed);\n if (pluginResult !== undefined) {\n transformed = pluginResult;\n }\n } catch (error) {\n // Fail-open: log but continue\n console.error(`Plugin ${plugin.name}.afterQuery failed:`, error);\n }\n }\n }\n\n return transformed;\n}\n\n/**\n * Run beforeMutation hooks (fail-closed)\n */\nexport async function runBeforeMutation(\n plugins: DatafnPlugin[],\n schema: DatafnSchema,\n mutation: unknown,\n): Promise<unknown> {\n let transformed = mutation;\n const ctx: DatafnHookContext = { env: \"client\", schema };\n\n for (const plugin of getClientPlugins(plugins)) {\n if (plugin.beforeMutation) {\n try {\n const result = await plugin.beforeMutation(ctx, transformed);\n if (result !== undefined) {\n transformed = result;\n }\n } catch (error: any) {\n throw {\n code: error.code || \"INTERNAL\",\n message: error.message || \"Plugin error\",\n details: {\n path: `plugins.${plugin.name}.beforeMutation`,\n ...(error.details || {}),\n },\n };\n }\n }\n }\n\n return transformed;\n}\n\n/**\n * Run afterMutation hooks (fail-open)\n */\nexport async function runAfterMutation(\n plugins: DatafnPlugin[],\n schema: DatafnSchema,\n mutation: unknown,\n result: unknown,\n): Promise<unknown> {\n let transformed = result;\n const ctx: DatafnHookContext = { env: \"client\", schema };\n\n for (const plugin of getClientPlugins(plugins)) {\n if (plugin.afterMutation) {\n try {\n await plugin.afterMutation(ctx, mutation, transformed);\n // afterMutation returns void, no transformation\n } catch (error) {\n console.error(`Plugin ${plugin.name}.afterMutation failed:`, error);\n }\n }\n }\n\n return transformed;\n}\n\n/**\n * Run beforeSync hooks (fail-closed)\n */\nexport async function runBeforeSync(\n plugins: DatafnPlugin[],\n schema: DatafnSchema,\n phase: \"seed\" | \"clone\" | \"pull\" | \"push\",\n payload: unknown,\n): Promise<unknown> {\n let transformed = payload;\n const ctx: DatafnHookContext = { env: \"client\", schema };\n\n for (const plugin of getClientPlugins(plugins)) {\n if (plugin.beforeSync) {\n try {\n const result = await plugin.beforeSync(ctx, phase, transformed);\n if (result !== undefined) {\n transformed = result;\n }\n } catch (error: any) {\n throw {\n code: error.code || \"INTERNAL\",\n message: error.message || \"Plugin error\",\n details: {\n path: `plugins.${plugin.name}.beforeSync`,\n ...(error.details || {}),\n },\n };\n }\n }\n }\n\n return transformed;\n}\n\n/**\n * Run afterSync hooks (fail-open)\n */\nexport async function runAfterSync(\n plugins: DatafnPlugin[],\n schema: DatafnSchema,\n phase: \"seed\" | \"clone\" | \"pull\" | \"push\",\n payload: unknown,\n result: unknown,\n): Promise<unknown> {\n let transformed = result;\n const ctx: DatafnHookContext = { env: \"client\", schema };\n\n for (const plugin of getClientPlugins(plugins)) {\n if (plugin.afterSync) {\n try {\n await plugin.afterSync(ctx, phase, payload, transformed);\n // afterSync returns void, no transformation\n } catch (error) {\n console.error(`Plugin ${plugin.name}.afterSync failed:`, error);\n }\n }\n }\n\n return transformed;\n}\n","/**\n * Query Execution Utilities\n *\n * Handles query execution via remote adapter or local storage based on hydration state.\n */\n\nimport type { DatafnRemoteAdapter } from \"./client.js\";\nimport type { DatafnStorageAdapter } from \"./storage.js\";\nimport type { DatafnPlugin, DatafnSchema } from \"@datafn/core\";\nimport { unwrapRemoteSuccess } from \"./remote/unwrap.js\";\nimport { executeLocalQuery } from \"./offline/query.js\";\nimport { runBeforeQuery, runAfterQuery } from \"./plugins/run-hooks.js\";\n\n/**\n * Execute a query (single or batch) via the remote adapter or local storage.\n *\n * When storage is configured and table hydration state is 'ready', queries execute locally.\n * Otherwise, queries use remote fallback.\n *\n * @param remote - Remote adapter for server queries\n * @param q - Query or array of queries\n * @param storage - Optional storage adapter for local-first execution\n * @param plugins - Optional plugins for hook execution\n * @returns Query result(s)\n */\nexport async function executeQuery<T = unknown>(\n remote: DatafnRemoteAdapter,\n q: unknown | unknown[],\n storage?: DatafnStorageAdapter,\n plugins: DatafnPlugin[] = [],\n schema?: DatafnSchema,\n): Promise<T | T[]> {\n // Run beforeQuery hooks (fail-closed)\n const transformedQuery = schema\n ? await runBeforeQuery(plugins, schema, q)\n : q;\n\n // If no storage configured, always use remote (backward compatible)\n if (!storage) {\n const response = await remote.query(transformedQuery);\n const result = unwrapRemoteSuccess<T | T[]>(response);\n // Run afterQuery hooks (fail-open)\n return schema\n ? (runAfterQuery(plugins, schema, transformedQuery, result) as Promise<\n T | T[]\n >)\n : result;\n }\n\n // For batch queries, always use remote fallback (simplification for Phase 20)\n if (Array.isArray(transformedQuery)) {\n const response = await remote.query(transformedQuery);\n const result = unwrapRemoteSuccess<T | T[]>(response);\n return schema\n ? (runAfterQuery(plugins, schema, transformedQuery, result) as Promise<\n T | T[]\n >)\n : result;\n }\n\n // Single query: check hydration state for local-first routing\n const query = transformedQuery as Record<string, unknown>;\n const resource = query.resource as string;\n\n if (resource) {\n const hydrationState = await storage.getHydrationState(resource);\n\n // Local-first: execute against storage when table is ready\n if (hydrationState === \"ready\") {\n const result = (await executeLocalQuery(storage, query)) as T;\n return schema\n ? (runAfterQuery(plugins, schema, query, result) as Promise<T>)\n : result;\n }\n }\n\n // Remote fallback for: notStarted, hydrating, or missing resource\n const response = await remote.query(transformedQuery);\n const result = unwrapRemoteSuccess<T>(response);\n return schema\n ? (runAfterQuery(plugins, schema, transformedQuery, result) as Promise<T>)\n : result;\n}\n","/**\n * Offline Mutation Logic\n *\n * Handles offline mutations by:\n * 1. Appending to offline changelog\n * 2. Performing optimistic local write to storage\n */\n\nimport type { DatafnStorageAdapter } from \"../storage.js\";\nimport { createClientError } from \"../errors.js\";\n\n/**\n * Handle a mutation when remote is unavailable.\n *\n * @param storage Storage adapter\n * @param mutation The full mutation object (including resource, version, clientId, mutationId)\n * @param timestampMs Client timestamp\n * @returns Optimistic mutation result\n */\nexport async function handleOfflineMutation(\n storage: DatafnStorageAdapter,\n mutation: Record<string, unknown>,\n timestampMs: number,\n): Promise<any> {\n // 1. Append to changelog (handling dedupe)\n // CLIENT-CHANGELOG-001, CLIENT-OFFLINE-MUT-001\n const clientId = mutation.clientId as string;\n const mutationId = mutation.mutationId as string;\n const resource = mutation.resource as string;\n const id = mutation.id as string;\n\n try {\n await storage.changelogAppend({\n clientId,\n mutationId,\n mutation,\n timestampMs,\n });\n } catch (err) {\n // If changelog fails, the whole offline mutation fails\n // Throw as is or wrap (CLIENT-OFFLINE-MUT-002)\n throw err;\n }\n\n // 2. Optimistic local apply\n // Deterministic implementation\n const operation = mutation.operation as string;\n const record = (mutation.record || {}) as Record<string, unknown>;\n\n if (operation === \"delete\") {\n await storage.deleteRecord(resource, id);\n } else if (operation === \"merge\") {\n // Merge: Read -> Patch -> Write\n const existing = await storage.getRecord(resource, id);\n const merged = existing\n ? { ...existing, ...record } // Patch existing\n : { ...record, id }; // Create new if missing (upsert semantics)\n\n // Ensure id is present\n merged.id = id;\n\n await storage.upsertRecord(resource, merged);\n } else if (operation === \"insert\" || operation === \"replace\") {\n // Insert/Replace: Overwrite (simple upsert)\n // Ensure id matches mutation target\n const toWrite = { ...record, id };\n await storage.upsertRecord(resource, toWrite);\n }\n\n // 3. Return optimistic success result\n return {\n ok: true,\n mutationId,\n affectedIds: [id],\n deduped: false, // local apply is fresh\n };\n}\n","/**\n * Mutation Execution Utilities\n *\n * Handles mutation execution via remote adapter with event emission.\n */\n\nimport type { DatafnRemoteAdapter } from \"./client.js\";\nimport type { DatafnStorageAdapter } from \"./storage.js\";\nimport type { EventBus } from \"./events/bus.js\";\nimport type { DatafnPlugin, DatafnSchema } from \"@datafn/core\";\nimport { unwrapRemoteSuccess } from \"./remote/unwrap.js\";\nimport { isTransportError } from \"./errors.js\";\nimport { handleOfflineMutation } from \"./offline/mutate.js\";\nimport { runBeforeMutation, runAfterMutation } from \"./plugins/run-hooks.js\";\n\n/**\n * Execute a mutation (single or batch) via the remote adapter.\n * Unwraps responses, emits events, and returns mutation result(s).\n */\nexport async function executeMutation(\n remote: DatafnRemoteAdapter,\n eventBus: EventBus,\n getTimestamp: () => number,\n m: unknown | unknown[],\n storage?: DatafnStorageAdapter,\n plugins: DatafnPlugin[] = [],\n schema?: DatafnSchema,\n): Promise<unknown> {\n // Run beforeMutation hooks (fail-closed)\n const transformedMutation = schema\n ? await runBeforeMutation(plugins, schema, m)\n : m;\n\n let result: unknown;\n let fromOfflineFallback = false;\n\n try {\n const response = await remote.mutation(transformedMutation);\n result = unwrapRemoteSuccess(response);\n // Run afterMutation hooks (fail-open)\n result = schema\n ? await runAfterMutation(plugins, schema, transformedMutation, result)\n : result;\n } catch (err: unknown) {\n // CLIENT-EVENT-001: Emit mutation_rejected for thrown errors\n if (!Array.isArray(transformedMutation)) {\n emitRejectionForError(\n eventBus,\n getTimestamp,\n transformedMutation as any,\n err,\n );\n } else {\n // For batch mutations, emit rejection for each\n for (const mut of transformedMutation as any[]) {\n emitRejectionForError(eventBus, getTimestamp, mut, err);\n }\n }\n\n // Check if we can failover to offline handling\n // We only failover if:\n // 1. Storage is configured\n // 2. It's a single mutation (batch offline fallback not scope of P21)\n // 3. Error IS A TRANSPORT ERROR (logic errors should fail)\n if (storage && !Array.isArray(m) && isTransportError(err)) {\n try {\n result = await handleOfflineMutation(\n storage,\n m as Record<string, unknown>,\n getTimestamp(),\n );\n fromOfflineFallback = true;\n } catch (offlineErr) {\n // If offline fallback also fails (e.g. storage error), rethrow original error?\n // Actually vectors CLIENT-OFFLINE-MUT-002 expect storage error to be thrown if changelog fails\n throw offlineErr;\n }\n } else {\n // No fallback possible, rethrow\n throw err;\n }\n }\n\n // Handle single mutation result\n if (!Array.isArray(m)) {\n emitMutationEvents(eventBus, getTimestamp, m as any, result as any);\n return result;\n }\n\n // Handle batch mutation results\n const mutations = m as any[];\n const results = result as any[];\n\n for (let i = 0; i < mutations.length; i++) {\n emitMutationEvents(eventBus, getTimestamp, mutations[i], results[i]);\n }\n\n return results;\n}\n\n/**\n * Emit mutation events based on result\n * CLIENT-EVENT-001: Include action and fields metadata\n */\nfunction emitMutationEvents(\n eventBus: EventBus,\n getTimestamp: () => number,\n mutation: any,\n result: any,\n): void {\n // Derive action from mutation.operation (CLIENT-EVENT-001)\n const action = mutation.operation;\n\n // Derive fields from mutation.record keys, excluding 'id' (CLIENT-EVENT-001)\n let fields: string[] | undefined;\n if (mutation.operation !== \"delete\" && mutation.record) {\n fields = Object.keys(mutation.record)\n .filter((k) => k !== \"id\")\n .sort(); // deterministic order\n }\n\n const baseEvent = {\n resource: mutation.resource,\n ids: Array.isArray(mutation.id) ? mutation.id : [mutation.id],\n mutationId: mutation.mutationId,\n clientId: mutation.clientId,\n timestampMs: getTimestamp(),\n action, // NEW: CLIENT-EVENT-001\n fields, // NEW: CLIENT-EVENT-001\n };\n\n if (result.ok) {\n // Successful mutation - emit mutation_applied\n eventBus.emit({\n type: \"mutation_applied\",\n ...baseEvent,\n });\n } else {\n // Failed mutation - emit mutation_rejected with error context\n const errorContext = result.errors?.[0] || {\n code: \"UNKNOWN\",\n message: \"Mutation failed\",\n path: \"$\",\n };\n\n eventBus.emit({\n type: \"mutation_rejected\",\n ...baseEvent,\n context: errorContext,\n } as any);\n }\n}\n\n/**\n * Emit mutation_rejected for thrown errors (CLIENT-EVENT-001)\n */\nfunction emitRejectionForError(\n eventBus: EventBus,\n getTimestamp: () => number,\n mutation: any,\n error: any,\n): void {\n // Derive action and fields same as above\n const action = mutation.operation;\n let fields: string[] | undefined;\n if (mutation.operation !== \"delete\" && mutation.record) {\n fields = Object.keys(mutation.record)\n .filter((k) => k !== \"id\")\n .sort();\n }\n\n const errorContext = {\n code: error.code || \"INTERNAL\",\n message: error.message || \"Remote error\",\n path: error.path || \"$\",\n };\n\n eventBus.emit({\n type: \"mutation_rejected\",\n resource: mutation.resource,\n ids: Array.isArray(mutation.id) ? mutation.id : [mutation.id],\n mutationId: mutation.mutationId,\n clientId: mutation.clientId,\n timestampMs: getTimestamp(),\n action,\n fields,\n context: errorContext,\n } as any);\n}\n","/**\n * Transaction Execution Utilities\n *\n * Handles transaction execution via remote adapter with response unwrapping.\n */\n\nimport type { DatafnRemoteAdapter } from \"./client.js\";\nimport { unwrapRemoteSuccess } from \"./remote/unwrap.js\";\n\n/**\n * Execute a transaction via the remote adapter.\n * Unwraps response and returns transaction result.\n */\nexport async function executeTransact(\n remote: DatafnRemoteAdapter,\n payload: unknown\n): Promise<unknown> {\n const response = await remote.transact(payload);\n return unwrapRemoteSuccess(response);\n}\n","/**\n * Query Signal Implementation\n *\n * Reactive query signals with caching, lazy fetch, and auto-refresh on mutations.\n */\n\nimport type { DatafnSignal } from \"@datafn/core\";\nimport { dfqlKey } from \"@datafn/core\";\nimport type { EventBus } from \"../events/bus.js\";\n\n/**\n * Signal registry for caching signals by dfqlKey\n */\nexport class SignalRegistry {\n private signals = new Map<string, DatafnSignal<any>>();\n private client: any;\n private eventBus: EventBus;\n\n constructor(client: any, eventBus: EventBus) {\n this.client = client;\n this.eventBus = eventBus;\n }\n\n getSignal<T>(fullQuery: unknown): DatafnSignal<T> {\n const key = dfqlKey(fullQuery);\n\n // Return cached signal if exists\n if (this.signals.has(key)) {\n return this.signals.get(key) as DatafnSignal<T>;\n }\n\n // Create new signal\n const signal = createQuerySignal<T>(this.client, this.eventBus, fullQuery);\n this.signals.set(key, signal);\n return signal;\n }\n}\n\n/**\n * Create a reactive query signal\n */\nfunction createQuerySignal<T>(\n client: any,\n eventBus: EventBus,\n fullQuery: any,\n): DatafnSignal<T> {\n let currentValue: T | undefined;\n let status: \"idle\" | \"loading\" | \"error\" = \"idle\";\n const subscribers = new Set<(value: T) => void>();\n let inFlight = false;\n let queuedRefresh = false;\n const resource = fullQuery.resource;\n\n const fetchQuery = async (isRefresh = false): Promise<void> => {\n if (inFlight) {\n // Queue a refresh to happen after current fetch completes\n if (isRefresh) {\n queuedRefresh = true;\n }\n return;\n }\n\n inFlight = true;\n queuedRefresh = false;\n\n try {\n const result = await client.query(fullQuery);\n currentValue = result as T;\n status = \"idle\";\n\n // Only notify subscribers if this is not a silent refresh or it succeeded\n if (!isRefresh || currentValue !== undefined) {\n subscribers.forEach((fn) => fn(currentValue as T));\n }\n } catch (error) {\n // On refresh error, swallow and keep last value (don't notify)\n if (!isRefresh) {\n status = \"error\";\n throw error;\n }\n // Refresh errors are silent - keep old value, don't notify\n } finally {\n inFlight = false;\n\n // If a refresh was queued, execute it now\n if (queuedRefresh) {\n queuedRefresh = false;\n fetchQuery(true);\n }\n }\n };\n\n // Listen for mutation_applied events for auto-refresh\n eventBus.subscribe((event) => {\n if (event.type === \"mutation_applied\" && event.resource === resource) {\n // Trigger refresh\n fetchQuery(true);\n }\n });\n\n return {\n subscribe(fn: (value: T) => void): () => void {\n subscribers.add(fn);\n\n // Lazy fetch on first subscribe\n if (\n subscribers.size === 1 &&\n status === \"idle\" &&\n currentValue === undefined\n ) {\n fetchQuery(false).catch(() => {\n // Initial fetch errors are handled internally\n });\n } else if (currentValue !== undefined) {\n // Immediately deliver current value to new subscriber\n fn(currentValue);\n }\n\n // Return unsubscribe function\n return () => {\n subscribers.delete(fn);\n };\n },\n\n get(): T {\n // Return current value or undefined as T (signals can have undefined state)\n return currentValue as T;\n },\n };\n}\n","/**\n * Sync Apply Logic\n *\n * Apply clone/pull results into local storage with hydration state management.\n */\n\nimport type { DatafnStorageAdapter, DatafnHydrationState } from \"../storage.js\";\n\n/**\n * Clone result shape from remote\n */\nexport type CloneResult = {\n ok: boolean;\n data: Record<string, Array<Record<string, unknown>>>;\n cursors: Record<string, string>;\n};\n\n/**\n * Pull result shape from remote\n */\nexport type PullResult = {\n ok: boolean;\n records: Record<string, Array<Record<string, unknown>>>;\n deleted: Record<string, string[]>;\n cursors: Record<string, string>;\n};\n\n/**\n * Apply clone result to local storage.\n * Transitions hydration state: notStarted → hydrating → ready\n */\nexport async function applyCloneResult(\n storage: DatafnStorageAdapter,\n result: CloneResult,\n): Promise<void> {\n if (!result.ok) {\n return; // Don't apply failed results\n }\n\n const { data, cursors } = result;\n\n // Process each table\n for (const [resource, records] of Object.entries(data)) {\n // Transition to hydrating state before applying\n await storage.setHydrationState(resource, \"hydrating\");\n\n // Upsert all records by id\n for (const record of records) {\n await storage.upsertRecord(resource, record);\n }\n\n // Set cursor\n const cursor = cursors[resource];\n if (cursor !== undefined) {\n await storage.setCursor(resource, cursor);\n }\n\n // Transition to ready after applying\n await storage.setHydrationState(resource, \"ready\");\n }\n}\n\n/**\n * Apply pull result to local storage.\n * Updates records, deletes removed records, and updates cursors monotonically.\n */\nexport async function applyPullResult(\n storage: DatafnStorageAdapter,\n result: PullResult,\n): Promise<void> {\n if (!result.ok) {\n return; // Don't apply failed results\n }\n\n const { records, deleted, cursors } = result;\n\n // Process record updates\n for (const [resource, resourceRecords] of Object.entries(records)) {\n for (const record of resourceRecords) {\n await storage.upsertRecord(resource, record);\n }\n }\n\n // Process deletions\n for (const [resource, deletedIds] of Object.entries(deleted)) {\n for (const id of deletedIds) {\n await storage.deleteRecord(resource, id);\n }\n }\n\n // Update cursors monotonically (only forward)\n for (const [resource, newCursor] of Object.entries(cursors)) {\n await setCursorMonotonically(storage, resource, newCursor);\n }\n}\n\n/**\n * Set cursor only if new cursor is greater than existing cursor.\n * Cursors are base-10 integer strings representing serverSeq.\n */\nasync function setCursorMonotonically(\n storage: DatafnStorageAdapter,\n resource: string,\n newCursor: string,\n): Promise<void> {\n const existingCursor = await storage.getCursor(resource);\n\n // If no existing cursor, set the new one\n if (existingCursor === null) {\n await storage.setCursor(resource, newCursor);\n return;\n }\n\n // Parse as integers for comparison\n const existingSeq = parseInt(existingCursor, 10);\n const newSeq = parseInt(newCursor, 10);\n\n // Only update if new cursor is greater (monotonic)\n if (newSeq > existingSeq) {\n await storage.setCursor(resource, newCursor);\n }\n}\n","/**\n * Sync Facade\n *\n * Client-side sync methods that delegate to remote adapter.\n * When storage is configured, clone/pull results are applied to local storage.\n */\n\nimport type { DatafnRemoteAdapter } from \"./client.js\";\nimport type { DatafnStorageAdapter } from \"./storage.js\";\nimport { unwrapRemoteSuccess } from \"./remote/unwrap.js\";\nimport { createClientError } from \"./errors.js\";\nimport { applyCloneResult, applyPullResult } from \"./sync/apply.js\";\nimport type { CloneResult, PullResult } from \"./sync/apply.js\";\n\nexport interface SyncFacade {\n seed(payload: unknown): Promise<unknown>;\n clone(payload: unknown): Promise<unknown>;\n pull(payload: unknown): Promise<unknown>;\n push(payload: unknown): Promise<unknown>;\n}\n\n/**\n * Create sync facade that delegates to remote adapter\n */\nexport function createSyncFacade(\n remote: DatafnRemoteAdapter,\n storage?: DatafnStorageAdapter,\n): SyncFacade {\n const callSyncMethod = async (\n methodName: keyof DatafnRemoteAdapter,\n payload: unknown,\n ): Promise<unknown> => {\n const method = remote[methodName];\n\n // Check if method exists\n if (typeof method !== \"function\") {\n throw createClientError(\n \"TRANSPORT_ERROR\",\n `Transport error: remote method missing: ${methodName}`,\n { path: `sync.${methodName}` },\n );\n }\n\n // Call remote method and unwrap\n const response = await method.call(remote, payload);\n return unwrapRemoteSuccess(response);\n };\n\n return {\n async seed(payload: unknown) {\n return callSyncMethod(\"seed\", payload);\n },\n\n async clone(payload: unknown) {\n const result = await callSyncMethod(\"clone\", payload);\n\n // Apply to storage if configured (CLIENT-SYNC-APPLY-001, CLIENT-HYDRATION-001)\n if (storage) {\n await applyCloneResult(storage, result as CloneResult);\n }\n\n return result;\n },\n\n async pull(payload: unknown) {\n const result = await callSyncMethod(\"pull\", payload);\n\n // Apply to storage if configured (CLIENT-SYNC-APPLY-001)\n if (storage) {\n await applyPullResult(storage, result as PullResult);\n }\n\n return result;\n },\n\n async push(payload: unknown) {\n return callSyncMethod(\"push\", payload);\n },\n };\n}\n","/**\n * In-memory storage adapter for testing and development.\n * Implements deterministic ordering and changelog deduplication.\n */\n\nimport type {\n DatafnStorageAdapter,\n DatafnHydrationState,\n DatafnChangelogEntry,\n} from \"../storage.js\";\n\nexport class MemoryStorageAdapter implements DatafnStorageAdapter {\n private records = new Map<string, Map<string, Record<string, unknown>>>();\n private joinRows = new Map<string, Map<string, Record<string, unknown>>>();\n private cursors = new Map<string, string>();\n private hydration = new Map<string, DatafnHydrationState>();\n private changelog: DatafnChangelogEntry[] = [];\n private changelogSeq = 1;\n\n constructor() {}\n\n // --- Records ---\n\n async getRecord(\n resource: string,\n id: string,\n ): Promise<Record<string, unknown> | null> {\n const table = this.records.get(resource);\n return table?.get(id) || null;\n }\n\n async listRecords(resource: string): Promise<Record<string, unknown>[]> {\n const table = this.records.get(resource);\n if (!table) return [];\n\n // STORAGE-MEM-001: Deterministic ordering by id:asc\n return Array.from(table.values()).sort((a, b) => {\n const idA = (a.id as string) || \"\";\n const idB = (b.id as string) || \"\";\n return idA < idB ? -1 : idA > idB ? 1 : 0;\n });\n }\n\n async upsertRecord(\n resource: string,\n record: Record<string, unknown>,\n ): Promise<void> {\n if (!this.records.has(resource)) {\n this.records.set(resource, new Map());\n }\n const id = record.id as string;\n if (!id) throw new Error(\"Record missing id\");\n this.records.get(resource)!.set(id, record);\n }\n\n async deleteRecord(resource: string, id: string): Promise<void> {\n const table = this.records.get(resource);\n if (table) {\n table.delete(id);\n }\n }\n\n // --- Join Rows ---\n\n async listJoinRows(\n relationKey: string,\n ): Promise<Array<Record<string, unknown>>> {\n const table = this.joinRows.get(relationKey);\n if (!table) return [];\n\n // Deterministic sort by from, to\n return Array.from(table.values()).sort((a, b) => {\n const keyA = `${a.from}:${a.to}`;\n const keyB = `${b.from}:${b.to}`;\n return keyA < keyB ? -1 : keyA > keyB ? 1 : 0;\n });\n }\n\n async upsertJoinRow(\n relationKey: string,\n row: Record<string, unknown>,\n ): Promise<void> {\n if (!this.joinRows.has(relationKey)) {\n this.joinRows.set(relationKey, new Map());\n }\n // Composite key for storage\n const key = `${row.from}:${row.to}`;\n this.joinRows.get(relationKey)!.set(key, row);\n }\n\n async deleteJoinRow(\n relationKey: string,\n from: string,\n to: string,\n ): Promise<void> {\n const table = this.joinRows.get(relationKey);\n if (table) {\n table.delete(`${from}:${to}`);\n }\n }\n\n // --- Sync State ---\n\n async getCursor(resource: string): Promise<string | null> {\n return this.cursors.get(resource) || null;\n }\n\n async setCursor(resource: string, cursor: string): Promise<void> {\n this.cursors.set(resource, cursor);\n }\n\n async getHydrationState(resource: string): Promise<DatafnHydrationState> {\n return this.hydration.get(resource) || \"notStarted\";\n }\n\n async setHydrationState(\n resource: string,\n state: DatafnHydrationState,\n ): Promise<void> {\n this.hydration.set(resource, state);\n }\n\n // --- Changelog ---\n\n async changelogAppend(\n entry: Omit<DatafnChangelogEntry, \"seq\">,\n ): Promise<DatafnChangelogEntry> {\n // CLIENT-CHANGELOG-001: Deduplicate by (clientId, mutationId)\n const existing = this.changelog.find(\n (e) => e.clientId === entry.clientId && e.mutationId === entry.mutationId,\n );\n if (existing) {\n return existing;\n }\n\n const newEntry: DatafnChangelogEntry = {\n ...entry,\n seq: this.changelogSeq++,\n };\n this.changelog.push(newEntry);\n return newEntry;\n }\n\n async changelogList(\n options: { limit?: number } = {},\n ): Promise<DatafnChangelogEntry[]> {\n const limit = options.limit || 100;\n return this.changelog.slice(0, limit); // Already sorted by insertion/seq\n }\n\n async changelogAck(options: { throughSeq: number }): Promise<void> {\n // Remove acked entries\n this.changelog = this.changelog.filter((e) => e.seq > options.throughSeq);\n }\n\n // Test helper to clear state\n clear() {\n this.records.clear();\n this.joinRows.clear();\n this.cursors.clear();\n this.hydration.clear();\n this.changelog = [];\n this.changelogSeq = 1;\n }\n}\n","/**\n * IndexedDB storage adapter for persistent client storage.\n * Implements deterministic ordering and changelog deduplication.\n */\n\nimport type {\n DatafnStorageAdapter,\n DatafnHydrationState,\n DatafnChangelogEntry,\n} from \"../storage\";\n\nconst DB_NAME = \"datafn_client_db\";\nconst DB_VERSION = 1;\n\nexport class IndexedDbStorageAdapter implements DatafnStorageAdapter {\n private dbPromise: Promise<IDBDatabase>;\n\n constructor(dbName: string = DB_NAME) {\n this.dbPromise = new Promise((resolve, reject) => {\n const request = indexedDB.open(dbName, DB_VERSION);\n\n request.onerror = () => reject(request.error);\n request.onsuccess = () => resolve(request.result);\n\n request.onupgradeneeded = (event) => {\n const db = request.result;\n\n // Records store: Key [resource, id]\n // This effectively groups by resource and allows range queries\n if (!db.objectStoreNames.contains(\"records\")) {\n const store = db.createObjectStore(\"records\", {\n keyPath: [\"resource\", \"id\"],\n });\n store.createIndex(\"by_resource\", \"resource\", { unique: false });\n }\n\n // Join rows store: Key [relationKey, from, to]\n if (!db.objectStoreNames.contains(\"join_rows\")) {\n const store = db.createObjectStore(\"join_rows\", {\n keyPath: [\"relationKey\", \"from\", \"to\"],\n });\n store.createIndex(\"by_relation\", \"relationKey\", { unique: false });\n }\n\n // Meta store: Key [type, key] (e.g. [\"cursor\", \"task\"], [\"hydration\", \"task\"])\n if (!db.objectStoreNames.contains(\"meta\")) {\n db.createObjectStore(\"meta\", { keyPath: [\"type\", \"key\"] });\n }\n\n // Changelog store: Key seq (autoIncrement)\n if (!db.objectStoreNames.contains(\"changelog\")) {\n const store = db.createObjectStore(\"changelog\", {\n keyPath: \"seq\",\n autoIncrement: true,\n });\n // Unique index for deduplication\n store.createIndex(\"by_client_mutation\", [\"clientId\", \"mutationId\"], {\n unique: true,\n });\n }\n };\n });\n }\n\n private async getStore(\n storeName: string,\n mode: IDBTransactionMode,\n ): Promise<IDBObjectStore> {\n const db = await this.dbPromise;\n return db.transaction(storeName, mode).objectStore(storeName);\n }\n\n // --- Records ---\n\n async getRecord(\n resource: string,\n id: string,\n ): Promise<Record<string, unknown> | null> {\n const store = await this.getStore(\"records\", \"readonly\");\n return new Promise((resolve, reject) => {\n const request = store.get([resource, id]);\n request.onsuccess = () => resolve(request.result || null);\n request.onerror = () => reject(request.error);\n });\n }\n\n async listRecords(resource: string): Promise<Record<string, unknown>[]> {\n const store = await this.getStore(\"records\", \"readonly\");\n const index = store.index(\"by_resource\");\n return new Promise((resolve, reject) => {\n // Get all records for resource\n // Since keyPath is [resource, id], the natural index order for \"by_resource\" might not guarantee id sort?\n // Actually, if we use the primary key range on the store itself:\n // IDB sorts by key path. [resource, id] sorts by resource, then id.\n // So retrieving a range matching [resource, -Infinity] to [resource, Infinity]\n // from the object store directly will return them sorted by id!\n\n const range = IDBKeyRange.bound([resource, \"\"], [resource, \"\\uffff\"]);\n\n const request = store.getAll(range);\n request.onsuccess = () => resolve(request.result || []);\n request.onerror = () => reject(request.error);\n });\n }\n\n async upsertRecord(\n resource: string,\n record: Record<string, unknown>,\n ): Promise<void> {\n const store = await this.getStore(\"records\", \"readwrite\");\n return new Promise((resolve, reject) => {\n // Ensure resource is set in record for storage (though it might be redundant with key)\n // The keyPath requires 'resource' and 'id' properties on the object.\n const recordWithKey = { ...record, resource };\n const request = store.put(recordWithKey);\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n });\n }\n\n async deleteRecord(resource: string, id: string): Promise<void> {\n const store = await this.getStore(\"records\", \"readwrite\");\n return new Promise((resolve, reject) => {\n const request = store.delete([resource, id]);\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n });\n }\n\n // --- Join Rows ---\n\n async listJoinRows(\n relationKey: string,\n ): Promise<Array<Record<string, unknown>>> {\n const store = await this.getStore(\"join_rows\", \"readonly\");\n\n // Similarly, keyPath [relationKey, from, to] guarantees sorting by from, then to.\n const range = IDBKeyRange.bound(\n [relationKey, \"\", \"\"],\n [relationKey, \"\\uffff\", \"\\uffff\"],\n );\n\n return new Promise((resolve, reject) => {\n const request = store.getAll(range);\n request.onsuccess = () => resolve(request.result || []);\n request.onerror = () => reject(request.error);\n });\n }\n\n async upsertJoinRow(\n relationKey: string,\n row: Record<string, unknown>,\n ): Promise<void> {\n const store = await this.getStore(\"join_rows\", \"readwrite\");\n return new Promise((resolve, reject) => {\n const rowWithKey = { ...row, relationKey };\n const request = store.put(rowWithKey);\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n });\n }\n\n async deleteJoinRow(\n relationKey: string,\n from: string,\n to: string,\n ): Promise<void> {\n const store = await this.getStore(\"join_rows\", \"readwrite\");\n return new Promise((resolve, reject) => {\n const request = store.delete([relationKey, from, to]);\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n });\n }\n\n // --- Sync State ---\n\n async getCursor(resource: string): Promise<string | null> {\n const store = await this.getStore(\"meta\", \"readonly\");\n return new Promise((resolve, reject) => {\n const request = store.get([\"cursor\", resource]);\n request.onsuccess = () => resolve(request.result?.value || null);\n request.onerror = () => reject(request.error);\n });\n }\n\n async setCursor(resource: string, cursor: string): Promise<void> {\n const store = await this.getStore(\"meta\", \"readwrite\");\n return new Promise((resolve, reject) => {\n const request = store.put({\n type: \"cursor\",\n key: resource,\n value: cursor,\n });\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n });\n }\n\n async getHydrationState(resource: string): Promise<DatafnHydrationState> {\n const store = await this.getStore(\"meta\", \"readonly\");\n return new Promise((resolve, reject) => {\n const request = store.get([\"hydration\", resource]);\n request.onsuccess = () => resolve(request.result?.value || \"notStarted\");\n request.onerror = () => reject(request.error);\n });\n }\n\n async setHydrationState(\n resource: string,\n state: DatafnHydrationState,\n ): Promise<void> {\n const store = await this.getStore(\"meta\", \"readwrite\");\n return new Promise((resolve, reject) => {\n const request = store.put({\n type: \"hydration\",\n key: resource,\n value: state,\n });\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n });\n }\n\n // --- Changelog ---\n\n async changelogAppend(\n entry: Omit<DatafnChangelogEntry, \"seq\">,\n ): Promise<DatafnChangelogEntry> {\n const store = await this.getStore(\"changelog\", \"readwrite\");\n const index = store.index(\"by_client_mutation\");\n\n // Check for duplicate\n const existing = await new Promise<DatafnChangelogEntry | undefined>(\n (resolve, reject) => {\n const request = index.get([entry.clientId, entry.mutationId]);\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error);\n },\n );\n\n if (existing) {\n return existing;\n }\n\n // Insert new\n return new Promise((resolve, reject) => {\n // Don't pass seq, let autoIncrement handle it\n const request = store.add(entry);\n request.onsuccess = () => {\n const seq = request.result as number;\n resolve({ ...entry, seq });\n };\n request.onerror = () => reject(request.error);\n });\n }\n\n async changelogList(\n options: { limit?: number } = {},\n ): Promise<DatafnChangelogEntry[]> {\n const store = await this.getStore(\"changelog\", \"readonly\");\n return new Promise((resolve, reject) => {\n const limit = options.limit || 100;\n // getAll allows limit\n const request = store.getAll(null, limit);\n request.onsuccess = () => resolve(request.result || []);\n request.onerror = () => reject(request.error);\n });\n }\n\n async changelogAck(options: { throughSeq: number }): Promise<void> {\n const store = await this.getStore(\"changelog\", \"readwrite\");\n\n // Delete range <= throughSeq\n const range = IDBKeyRange.upperBound(options.throughSeq);\n\n return new Promise((resolve, reject) => {\n const request = store.delete(range);\n request.onsuccess = () => resolve();\n request.onerror = () => reject(request.error);\n });\n }\n}\n"],"mappings":"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","names":["import_core","response","result"]}
@@ -0,0 +1,248 @@
1
+ import { DatafnEvent, DatafnSignal, DatafnSchema, DatafnPlugin, DatafnErrorCode } from '@datafn/core';
2
+
3
+ /**
4
+ * Event filtering logic
5
+ */
6
+
7
+ interface EventFilter {
8
+ type?: string | string[];
9
+ resource?: string | string[];
10
+ ids?: string | string[];
11
+ mutationId?: string | string[];
12
+ action?: string | string[];
13
+ fields?: string | string[];
14
+ contextKeys?: string[];
15
+ }
16
+ /**
17
+ * Check if an event matches the filter
18
+ */
19
+ declare function matchesFilter(event: DatafnEvent, filter?: EventFilter): boolean;
20
+
21
+ /**
22
+ * In-process event bus
23
+ */
24
+
25
+ type EventHandler = (event: DatafnEvent) => void;
26
+ /**
27
+ * Simple in-process event bus
28
+ */
29
+ declare class EventBus {
30
+ private subscriptions;
31
+ private nextId;
32
+ /**
33
+ * Subscribe to events with optional filtering
34
+ */
35
+ subscribe(handler: EventHandler, filter?: EventFilter): () => void;
36
+ /**
37
+ * Emit an event to all matching subscribers
38
+ */
39
+ emit(event: DatafnEvent): void;
40
+ }
41
+
42
+ /**
43
+ * DataFn Table Handle
44
+ *
45
+ * Represents a table/resource from the schema with methods for query, mutation, signals, and subscriptions.
46
+ */
47
+
48
+ interface DatafnTable<TRecord = unknown> {
49
+ name: string;
50
+ version: number;
51
+ query(q: unknown): Promise<unknown>;
52
+ mutate(m: unknown): Promise<unknown>;
53
+ transact(payload: unknown): Promise<unknown>;
54
+ signal(q: unknown): DatafnSignal<unknown>;
55
+ subscribe(handler: EventHandler, filter?: EventFilter): () => void;
56
+ }
57
+
58
+ /**
59
+ * Storage adapter types for local persistence
60
+ */
61
+ type DatafnHydrationState = "notStarted" | "hydrating" | "ready";
62
+ type DatafnChangelogEntry = {
63
+ /** Monotonic local sequence (assigned by storage adapter). */
64
+ seq: number;
65
+ clientId: string;
66
+ mutationId: string;
67
+ mutation: Record<string, unknown>;
68
+ timestampMs: number;
69
+ };
70
+ interface DatafnStorageAdapter {
71
+ getRecord(resource: string, id: string): Promise<Record<string, unknown> | null>;
72
+ listRecords(resource: string): Promise<Record<string, unknown>[]>;
73
+ upsertRecord(resource: string, record: Record<string, unknown>): Promise<void>;
74
+ deleteRecord(resource: string, id: string): Promise<void>;
75
+ listJoinRows(relationKey: string): Promise<Array<Record<string, unknown>>>;
76
+ upsertJoinRow(relationKey: string, row: Record<string, unknown>): Promise<void>;
77
+ deleteJoinRow(relationKey: string, from: string, to: string): Promise<void>;
78
+ getCursor(resource: string): Promise<string | null>;
79
+ setCursor(resource: string, cursor: string): Promise<void>;
80
+ getHydrationState(resource: string): Promise<DatafnHydrationState>;
81
+ setHydrationState(resource: string, state: DatafnHydrationState): Promise<void>;
82
+ changelogAppend(entry: Omit<DatafnChangelogEntry, "seq">): Promise<DatafnChangelogEntry>;
83
+ changelogList(options?: {
84
+ limit?: number;
85
+ }): Promise<DatafnChangelogEntry[]>;
86
+ changelogAck(options: {
87
+ throughSeq: number;
88
+ }): Promise<void>;
89
+ }
90
+
91
+ /**
92
+ * Sync Facade
93
+ *
94
+ * Client-side sync methods that delegate to remote adapter.
95
+ * When storage is configured, clone/pull results are applied to local storage.
96
+ */
97
+
98
+ interface SyncFacade {
99
+ seed(payload: unknown): Promise<unknown>;
100
+ clone(payload: unknown): Promise<unknown>;
101
+ pull(payload: unknown): Promise<unknown>;
102
+ push(payload: unknown): Promise<unknown>;
103
+ }
104
+
105
+ /**
106
+ * DataFn client factory
107
+ */
108
+
109
+ interface DatafnRemoteAdapter {
110
+ query(q: unknown): Promise<unknown>;
111
+ mutation(m: unknown): Promise<unknown>;
112
+ transact(t: unknown): Promise<unknown>;
113
+ seed(payload: unknown): Promise<unknown>;
114
+ clone(payload: unknown): Promise<unknown>;
115
+ pull(payload: unknown): Promise<unknown>;
116
+ push(payload: unknown): Promise<unknown>;
117
+ }
118
+ interface DatafnClientConfig {
119
+ schema: DatafnSchema;
120
+ remote: DatafnRemoteAdapter;
121
+ /**
122
+ * Optional plugins for client-side hook execution
123
+ */
124
+ plugins?: DatafnPlugin[];
125
+ /**
126
+ * Stable client/device identifier used for idempotency and offline change logs.
127
+ * Required when `storage` is provided.
128
+ */
129
+ clientId?: string;
130
+ /**
131
+ * Local persistence adapter. When provided, sync results are applied to local storage.
132
+ */
133
+ storage?: DatafnStorageAdapter;
134
+ getTimestamp?: () => number;
135
+ }
136
+ interface DatafnClient {
137
+ table<TRecord = unknown>(name: string): DatafnTable<TRecord>;
138
+ query(q: unknown | unknown[]): Promise<unknown>;
139
+ mutate(mutation: unknown | unknown[]): Promise<unknown>;
140
+ transact(payload: unknown): Promise<unknown>;
141
+ subscribe(handler: EventHandler, filter?: EventFilter): () => void;
142
+ sync: SyncFacade;
143
+ }
144
+ /**
145
+ * Create a DataFn client
146
+ */
147
+ declare function createDatafnClient(config: DatafnClientConfig): DatafnClient;
148
+
149
+ /**
150
+ * DataFn Client Error Types
151
+ */
152
+
153
+ type DatafnClientError = {
154
+ code: DatafnErrorCode | "TRANSPORT_ERROR";
155
+ message: string;
156
+ details: {
157
+ path: string;
158
+ [key: string]: unknown;
159
+ };
160
+ };
161
+ /**
162
+ * Create a DataFnClientError and throw it
163
+ */
164
+ declare function createClientError(code: DatafnClientError["code"], message: string, details: {
165
+ path: string;
166
+ [key: string]: unknown;
167
+ }): never;
168
+
169
+ /**
170
+ * Remote Response Unwrapping Utilities
171
+ *
172
+ * Handles both wrapped (DatafnEnvelope) and unwrapped server responses.
173
+ */
174
+ /**
175
+ * Unwrap a remote response that may be wrapped in DatafnEnvelope or unwrapped.
176
+ *
177
+ * - If wrapped success `{ ok: true, result }`, return `result`
178
+ * - If wrapped error `{ ok: false, error }`, throw DatafnClientError
179
+ * - If unwrapped query result `{ data, nextCursor }`, return as-is
180
+ * - If unwrapped aggregate result `{ groups, nextCursor }`, return as-is
181
+ * - Otherwise throw TRANSPORT_ERROR
182
+ */
183
+ declare function unwrapRemoteSuccess<T>(response: unknown): T;
184
+
185
+ /**
186
+ * In-memory storage adapter for testing and development.
187
+ * Implements deterministic ordering and changelog deduplication.
188
+ */
189
+
190
+ declare class MemoryStorageAdapter implements DatafnStorageAdapter {
191
+ private records;
192
+ private joinRows;
193
+ private cursors;
194
+ private hydration;
195
+ private changelog;
196
+ private changelogSeq;
197
+ constructor();
198
+ getRecord(resource: string, id: string): Promise<Record<string, unknown> | null>;
199
+ listRecords(resource: string): Promise<Record<string, unknown>[]>;
200
+ upsertRecord(resource: string, record: Record<string, unknown>): Promise<void>;
201
+ deleteRecord(resource: string, id: string): Promise<void>;
202
+ listJoinRows(relationKey: string): Promise<Array<Record<string, unknown>>>;
203
+ upsertJoinRow(relationKey: string, row: Record<string, unknown>): Promise<void>;
204
+ deleteJoinRow(relationKey: string, from: string, to: string): Promise<void>;
205
+ getCursor(resource: string): Promise<string | null>;
206
+ setCursor(resource: string, cursor: string): Promise<void>;
207
+ getHydrationState(resource: string): Promise<DatafnHydrationState>;
208
+ setHydrationState(resource: string, state: DatafnHydrationState): Promise<void>;
209
+ changelogAppend(entry: Omit<DatafnChangelogEntry, "seq">): Promise<DatafnChangelogEntry>;
210
+ changelogList(options?: {
211
+ limit?: number;
212
+ }): Promise<DatafnChangelogEntry[]>;
213
+ changelogAck(options: {
214
+ throughSeq: number;
215
+ }): Promise<void>;
216
+ clear(): void;
217
+ }
218
+
219
+ /**
220
+ * IndexedDB storage adapter for persistent client storage.
221
+ * Implements deterministic ordering and changelog deduplication.
222
+ */
223
+
224
+ declare class IndexedDbStorageAdapter implements DatafnStorageAdapter {
225
+ private dbPromise;
226
+ constructor(dbName?: string);
227
+ private getStore;
228
+ getRecord(resource: string, id: string): Promise<Record<string, unknown> | null>;
229
+ listRecords(resource: string): Promise<Record<string, unknown>[]>;
230
+ upsertRecord(resource: string, record: Record<string, unknown>): Promise<void>;
231
+ deleteRecord(resource: string, id: string): Promise<void>;
232
+ listJoinRows(relationKey: string): Promise<Array<Record<string, unknown>>>;
233
+ upsertJoinRow(relationKey: string, row: Record<string, unknown>): Promise<void>;
234
+ deleteJoinRow(relationKey: string, from: string, to: string): Promise<void>;
235
+ getCursor(resource: string): Promise<string | null>;
236
+ setCursor(resource: string, cursor: string): Promise<void>;
237
+ getHydrationState(resource: string): Promise<DatafnHydrationState>;
238
+ setHydrationState(resource: string, state: DatafnHydrationState): Promise<void>;
239
+ changelogAppend(entry: Omit<DatafnChangelogEntry, "seq">): Promise<DatafnChangelogEntry>;
240
+ changelogList(options?: {
241
+ limit?: number;
242
+ }): Promise<DatafnChangelogEntry[]>;
243
+ changelogAck(options: {
244
+ throughSeq: number;
245
+ }): Promise<void>;
246
+ }
247
+
248
+ export { type DatafnChangelogEntry, type DatafnClient, type DatafnClientConfig, type DatafnClientError, type DatafnHydrationState, type DatafnRemoteAdapter, type DatafnStorageAdapter, type DatafnTable, EventBus, type EventFilter, type EventHandler, IndexedDbStorageAdapter, MemoryStorageAdapter, createClientError, createDatafnClient, matchesFilter, unwrapRemoteSuccess };