@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,376 @@
1
+ import { Q as QueryDescriptor, a as SubscriptionCallback, C as CollectionRecord, S as StorageAdapter, W as WhereClause, O as OrderByDirection, b as StoreConfig, A as ApplyResult } from './types-DF-KDSK1.js';
2
+ export { M as MigrationPlan, c as OrderByClause, T as Transaction, d as WhereOperators } from './types-DF-KDSK1.js';
3
+ import { KoraError, Operation, CollectionDefinition, SchemaDefinition, OperationLog, VersionVector, HybridLogicalClock } from '@korajs/core';
4
+
5
+ /**
6
+ * Thrown when a query is invalid (bad field names, invalid operators, etc.).
7
+ */
8
+ declare class QueryError extends KoraError {
9
+ constructor(message: string, context?: Record<string, unknown>);
10
+ }
11
+ /**
12
+ * Thrown when a record is not found by ID (findById, update, delete on missing record).
13
+ */
14
+ declare class RecordNotFoundError extends KoraError {
15
+ constructor(collection: string, recordId: string);
16
+ }
17
+ /**
18
+ * Thrown when a storage adapter operation fails.
19
+ */
20
+ declare class AdapterError extends KoraError {
21
+ constructor(message: string, context?: Record<string, unknown>);
22
+ }
23
+ /**
24
+ * Thrown when an operation is attempted on a store that has not been opened.
25
+ */
26
+ declare class StoreNotOpenError extends KoraError {
27
+ constructor();
28
+ }
29
+ /**
30
+ * Thrown when the Web Worker fails to initialize (WASM load failure, OPFS unavailable, etc.).
31
+ */
32
+ declare class WorkerInitError extends KoraError {
33
+ constructor(message: string, context?: Record<string, unknown>);
34
+ }
35
+ /**
36
+ * Thrown when the Web Worker does not respond within the configured timeout.
37
+ */
38
+ declare class WorkerTimeoutError extends KoraError {
39
+ constructor(operation: string, timeoutMs: number);
40
+ }
41
+ /**
42
+ * Thrown when IndexedDB persistence operations fail (serialize/deserialize).
43
+ */
44
+ declare class PersistenceError extends KoraError {
45
+ constructor(message: string, context?: Record<string, unknown>);
46
+ }
47
+
48
+ /**
49
+ * Manages reactive subscriptions. When a mutation occurs on a collection,
50
+ * affected subscriptions are re-evaluated in a microtask batch and callbacks
51
+ * are invoked only if results actually changed.
52
+ */
53
+ declare class SubscriptionManager {
54
+ private subscriptions;
55
+ private pendingCollections;
56
+ private flushScheduled;
57
+ /**
58
+ * Register a new subscription.
59
+ *
60
+ * @param descriptor - The query descriptor defining what this subscription watches
61
+ * @param callback - Called with results whenever they change
62
+ * @param executeFn - Function to re-execute the query and get current results
63
+ * @returns An unsubscribe function
64
+ */
65
+ register(descriptor: QueryDescriptor, callback: SubscriptionCallback<CollectionRecord>, executeFn: () => Promise<CollectionRecord[]>): () => void;
66
+ /**
67
+ * Register a subscription and immediately execute the query.
68
+ * The initial results are stored as lastResults so subsequent flushes
69
+ * correctly diff against the initial state.
70
+ *
71
+ * @returns An unsubscribe function
72
+ */
73
+ registerAndFetch(descriptor: QueryDescriptor, callback: SubscriptionCallback<CollectionRecord>, executeFn: () => Promise<CollectionRecord[]>): () => void;
74
+ /**
75
+ * Notify the manager that a mutation occurred on a collection.
76
+ * Schedules a microtask flush to batch multiple mutations in the same tick.
77
+ */
78
+ notify(collection: string, _operation: Operation): void;
79
+ /**
80
+ * Immediately flush all pending notifications.
81
+ * Useful for testing. In production, flushing happens via microtask.
82
+ */
83
+ flush(): Promise<void>;
84
+ /**
85
+ * Remove all subscriptions. Called on store close.
86
+ */
87
+ clear(): void;
88
+ /** Number of active subscriptions (for testing/debugging) */
89
+ get size(): number;
90
+ private scheduleFlush;
91
+ /**
92
+ * Compare two result sets. Uses length check + JSON comparison as pragmatic approach.
93
+ * Sufficient for typical query results. Can be optimized to id-based diffing if profiling shows need.
94
+ */
95
+ private resultsEqual;
96
+ }
97
+
98
+ /**
99
+ * Fluent query builder for constructing and executing collection queries.
100
+ * Supports where, orderBy, limit, offset, include, exec, count, and subscribe.
101
+ *
102
+ * The generic parameter `T` defaults to `CollectionRecord` for backward compatibility
103
+ * but can be narrowed to a specific record type for full type inference.
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * const todos = await app.todos
108
+ * .where({ completed: false })
109
+ * .orderBy('createdAt', 'desc')
110
+ * .limit(10)
111
+ * .exec()
112
+ * ```
113
+ */
114
+ declare class QueryBuilder<T = CollectionRecord> {
115
+ private readonly collectionName;
116
+ private readonly definition;
117
+ private readonly adapter;
118
+ private readonly subscriptionManager;
119
+ private readonly schema?;
120
+ private descriptor;
121
+ constructor(collectionName: string, definition: CollectionDefinition, adapter: StorageAdapter, subscriptionManager: SubscriptionManager, initialWhere?: WhereClause, schema?: SchemaDefinition | undefined);
122
+ /**
123
+ * Add WHERE conditions (AND semantics, merged with existing conditions).
124
+ */
125
+ where(conditions: WhereClause): QueryBuilder<T>;
126
+ /**
127
+ * Add ORDER BY clause.
128
+ */
129
+ orderBy(field: string, direction?: OrderByDirection): QueryBuilder<T>;
130
+ /**
131
+ * Set result limit.
132
+ */
133
+ limit(n: number): QueryBuilder<T>;
134
+ /**
135
+ * Set result offset.
136
+ */
137
+ offset(n: number): QueryBuilder<T>;
138
+ /**
139
+ * Include related records in the query results.
140
+ * Follows relations defined in the schema to batch-fetch related data.
141
+ *
142
+ * @param targets - Relation target names (collection names or relation names)
143
+ * @returns A new QueryBuilder with include targets added
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * const todosWithProject = await app.todos
148
+ * .where({ completed: false })
149
+ * .include('project')
150
+ * .exec()
151
+ * ```
152
+ */
153
+ include(...targets: string[]): QueryBuilder<T>;
154
+ /**
155
+ * Execute the query and return results.
156
+ */
157
+ exec(): Promise<T[]>;
158
+ /**
159
+ * Execute a COUNT query and return the count.
160
+ */
161
+ count(): Promise<number>;
162
+ /**
163
+ * Subscribe to query results. Callback is called immediately with current results,
164
+ * then again whenever the results change due to mutations.
165
+ *
166
+ * @returns An unsubscribe function
167
+ */
168
+ subscribe(callback: SubscriptionCallback<T>): () => void;
169
+ /** Get the internal descriptor (for testing/debugging) */
170
+ getDescriptor(): QueryDescriptor;
171
+ private clone;
172
+ /**
173
+ * Resolve include targets to their actual collection names for subscription tracking.
174
+ */
175
+ private resolveIncludeCollections;
176
+ /**
177
+ * Resolve includes after primary query, batch-fetching related records.
178
+ */
179
+ private resolveIncludes;
180
+ /**
181
+ * Many-to-one: collect FK values from primary results, batch-fetch related, attach as singular.
182
+ */
183
+ private resolveManyToOneInclude;
184
+ /**
185
+ * One-to-many: collect primary IDs, batch-fetch children, attach as array.
186
+ */
187
+ private resolveOneToManyInclude;
188
+ /**
189
+ * Find a relation definition matching the include target.
190
+ * Searches by relation name, target collection name, and singularized/pluralized variants.
191
+ */
192
+ private findRelation;
193
+ }
194
+
195
+ /**
196
+ * Store is the main orchestrator. It owns a schema, a storage adapter,
197
+ * a clock, and a subscription manager. It creates Collection instances
198
+ * for each schema collection, and provides the sync contract via
199
+ * applyRemoteOperation and getOperationRange.
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * const store = new Store({ schema, adapter })
204
+ * await store.open()
205
+ * const todo = await store.collection('todos').insert({ title: 'Hello' })
206
+ * await store.close()
207
+ * ```
208
+ */
209
+ declare class Store implements OperationLog {
210
+ private opened;
211
+ private nodeId;
212
+ private sequenceNumber;
213
+ private versionVector;
214
+ private clock;
215
+ private collections;
216
+ private subscriptionManager;
217
+ private readonly schema;
218
+ private readonly adapter;
219
+ private readonly configNodeId;
220
+ private readonly emitter;
221
+ constructor(config: StoreConfig);
222
+ /**
223
+ * Open the store: initialize the database, load or generate a node ID,
224
+ * restore the sequence number and version vector, and create Collection instances.
225
+ */
226
+ open(): Promise<void>;
227
+ /**
228
+ * Close the store: clear subscriptions and close the adapter.
229
+ */
230
+ close(): Promise<void>;
231
+ /**
232
+ * Get a Collection instance for CRUD operations.
233
+ * @throws {StoreNotOpenError} If the store is not open
234
+ * @throws {Error} If the collection name is not in the schema
235
+ */
236
+ collection(name: string): CollectionAccessor;
237
+ /**
238
+ * Get the current version vector.
239
+ */
240
+ getVersionVector(): VersionVector;
241
+ /**
242
+ * Get the node ID for this store instance.
243
+ */
244
+ getNodeId(): string;
245
+ /**
246
+ * Apply a remote operation received from sync.
247
+ * Checks for duplicates, applies to the data table, persists the operation,
248
+ * and updates the version vector.
249
+ */
250
+ applyRemoteOperation(op: Operation): Promise<ApplyResult>;
251
+ /**
252
+ * Get operations from a node within a sequence number range.
253
+ * Implements the OperationLog interface for computeDelta.
254
+ */
255
+ getRange(nodeId: string, fromSeq: number, toSeq: number): Operation[];
256
+ /**
257
+ * Async version of getRange for use by the sync layer.
258
+ */
259
+ getOperationRange(nodeId: string, fromSeq: number, toSeq: number): Promise<Operation[]>;
260
+ /**
261
+ * Get the schema definition.
262
+ */
263
+ getSchema(): SchemaDefinition;
264
+ /** Expose the subscription manager for direct access (e.g., by QueryBuilder) */
265
+ getSubscriptionManager(): SubscriptionManager;
266
+ private nextSequenceNumber;
267
+ private loadOrGenerateNodeId;
268
+ private loadSequenceNumber;
269
+ private loadVersionVector;
270
+ private ensureOpen;
271
+ }
272
+ /**
273
+ * Public-facing collection accessor. Provides CRUD + where.
274
+ */
275
+ interface CollectionAccessor {
276
+ insert(data: Record<string, unknown>): Promise<CollectionRecord>;
277
+ findById(id: string): Promise<CollectionRecord | null>;
278
+ update(id: string, data: Record<string, unknown>): Promise<CollectionRecord>;
279
+ delete(id: string): Promise<void>;
280
+ where(conditions: Record<string, unknown>): QueryBuilder;
281
+ }
282
+
283
+ /**
284
+ * Callback invoked after a mutation so the Store can notify subscriptions.
285
+ */
286
+ type MutationCallback = (collection: string, operation: Operation) => void;
287
+ /**
288
+ * Collection provides CRUD operations on a single schema collection.
289
+ * Each mutation creates an Operation and persists both the data and the operation atomically.
290
+ */
291
+ declare class Collection {
292
+ private readonly name;
293
+ private readonly definition;
294
+ private readonly schema;
295
+ private readonly adapter;
296
+ private readonly clock;
297
+ private readonly nodeId;
298
+ private readonly getSequenceNumber;
299
+ private readonly onMutation;
300
+ constructor(name: string, definition: CollectionDefinition, schema: SchemaDefinition, adapter: StorageAdapter, clock: HybridLogicalClock, nodeId: string, getSequenceNumber: () => number, onMutation: MutationCallback);
301
+ /**
302
+ * Insert a new record into the collection.
303
+ * Generates a UUID v7 for the id, validates data, and persists atomically.
304
+ *
305
+ * @param data - The record data (auto fields and defaults are applied automatically)
306
+ * @returns The inserted record with id, createdAt, updatedAt
307
+ */
308
+ insert(data: Record<string, unknown>): Promise<CollectionRecord>;
309
+ /**
310
+ * Find a record by its ID. Returns null if not found or soft-deleted.
311
+ */
312
+ findById(id: string): Promise<CollectionRecord | null>;
313
+ /**
314
+ * Update an existing record. Only the provided fields are changed.
315
+ *
316
+ * @param id - The record ID to update
317
+ * @param data - Partial data with only the fields to change
318
+ * @returns The updated record
319
+ * @throws {RecordNotFoundError} If the record doesn't exist or is deleted
320
+ */
321
+ update(id: string, data: Record<string, unknown>): Promise<CollectionRecord>;
322
+ /**
323
+ * Soft-delete a record by its ID.
324
+ *
325
+ * @param id - The record ID to delete
326
+ * @throws {RecordNotFoundError} If the record doesn't exist or is already deleted
327
+ */
328
+ delete(id: string): Promise<void>;
329
+ /** Get the collection name */
330
+ getName(): string;
331
+ /** Get the collection definition */
332
+ getDefinition(): CollectionDefinition;
333
+ }
334
+
335
+ type RichtextInput = string | Uint8Array | ArrayBuffer | null | undefined;
336
+ /**
337
+ * Encodes richtext values into Yjs document updates.
338
+ */
339
+ declare function encodeRichtext(value: RichtextInput): Uint8Array | null;
340
+ /**
341
+ * Decodes driver-provided richtext values into Uint8Array.
342
+ */
343
+ declare function decodeRichtext(value: unknown): Uint8Array | null;
344
+ /**
345
+ * Reads plain text from a richtext Yjs state update.
346
+ */
347
+ declare function richtextToPlainText(value: RichtextInput): string;
348
+
349
+ /**
350
+ * Minimal pluralization/singularization utilities for relation name resolution.
351
+ * Handles common English patterns — not meant to be exhaustive.
352
+ */
353
+ /**
354
+ * Pluralize a word using common English rules.
355
+ *
356
+ * @example
357
+ * ```
358
+ * pluralize('project') // 'projects'
359
+ * pluralize('category') // 'categories'
360
+ * pluralize('match') // 'matches'
361
+ * ```
362
+ */
363
+ declare function pluralize(word: string): string;
364
+ /**
365
+ * Singularize a word using common English rules.
366
+ *
367
+ * @example
368
+ * ```
369
+ * singularize('projects') // 'project'
370
+ * singularize('categories') // 'category'
371
+ * singularize('matches') // 'match'
372
+ * ```
373
+ */
374
+ declare function singularize(word: string): string;
375
+
376
+ export { AdapterError, ApplyResult, Collection, type CollectionAccessor, CollectionRecord, OrderByDirection, PersistenceError, QueryBuilder, QueryDescriptor, QueryError, RecordNotFoundError, StorageAdapter, Store, StoreConfig, StoreNotOpenError, SubscriptionCallback, SubscriptionManager, WhereClause, WorkerInitError, WorkerTimeoutError, decodeRichtext, encodeRichtext, pluralize, richtextToPlainText, singularize };