korajs 0.3.2 → 0.4.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.
- package/dist/config.cjs.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/index.cjs +142 -30
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +123 -6
- package/dist/index.d.ts +123 -6
- package/dist/index.js +139 -26
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.d.cts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { KoraEventEmitter, SchemaDefinition, SchemaInput, FieldBuilder, InferRecord, InferInsertInput, InferUpdateInput } from '@korajs/core';
|
|
2
|
-
export { CollectionDefinition, ConnectionQuality, Constraint, FieldDescriptor, FieldKindToType, HLCTimestamp, HybridLogicalClock, InferFieldType, InferInsertInput, InferRecord, InferUpdateInput, KoraError, KoraEvent, KoraEventEmitter, KoraEventListener, KoraEventType, MergeStrategy, MergeTrace, Operation, SchemaDefinition, SchemaInput, TypedSchemaDefinition, VersionVector, createOperation, defineSchema, generateUUIDv7, t } from '@korajs/core';
|
|
1
|
+
import { KoraEventEmitter, SequenceConfig, Operation, SchemaDefinition, SchemaInput, FieldBuilder, InferRecord, InferInsertInput, InferUpdateInput } from '@korajs/core';
|
|
2
|
+
export { AtomicOp, AtomicOpType, CollectionDefinition, ConnectionQuality, Constraint, FieldDescriptor, FieldKindToType, HLCTimestamp, HybridLogicalClock, InferFieldType, InferInsertInput, InferRecord, InferUpdateInput, KoraError, KoraEvent, KoraEventEmitter, KoraEventListener, KoraEventType, MergeStrategy, MergeTrace, MigrationDefinition, MigrationStep, Operation, SchemaDefinition, SchemaInput, SequenceConfig, TypedSchemaDefinition, VersionVector, createOperation, defineSchema, generateUUIDv7, migrate, op, t } from '@korajs/core';
|
|
3
3
|
import * as _korajs_store from '@korajs/store';
|
|
4
|
-
import { QueryBuilder, CollectionAccessor } from '@korajs/store';
|
|
5
|
-
export { CollectionAccessor, CollectionRecord, StorageAdapter, Store, StoreConfig } from '@korajs/store';
|
|
4
|
+
import { CollectionRecord, QueryBuilder, CollectionAccessor } from '@korajs/store';
|
|
5
|
+
export { CollectionAccessor, CollectionRecord, SequenceManager, StorageAdapter, Store, StoreConfig, TransactionCollectionAccessor, TransactionContext, TransactionContextConfig } from '@korajs/store';
|
|
6
|
+
import * as _korajs_sync from '@korajs/sync';
|
|
6
7
|
import { SyncStatusInfo, SyncEngine } from '@korajs/sync';
|
|
7
|
-
export { SyncConfig, SyncEngine, SyncState, SyncStatus, SyncStatusInfo, SyncStore, WebSocketTransport } from '@korajs/sync';
|
|
8
|
+
export { SyncConfig, SyncDiagnostics, SyncEngine, SyncState, SyncStatus, SyncStatusInfo, SyncStore, WebSocketTransport } from '@korajs/sync';
|
|
8
9
|
export { MergeEngine, MergeInput, MergeResult } from '@korajs/merge';
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -40,6 +41,22 @@ interface SyncOptions {
|
|
|
40
41
|
}>;
|
|
41
42
|
/** Sync scopes per collection. */
|
|
42
43
|
scopes?: Record<string, (ctx: Record<string, unknown>) => Record<string, unknown>>;
|
|
44
|
+
/**
|
|
45
|
+
* Flat scope values. Combined with schema scope declarations to build
|
|
46
|
+
* per-collection scope filters sent to the server during handshake.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* createApp({
|
|
51
|
+
* schema,
|
|
52
|
+
* sync: {
|
|
53
|
+
* url: 'wss://server/kora',
|
|
54
|
+
* scope: { orgId: 'org-123', storeId: 'store-456' },
|
|
55
|
+
* },
|
|
56
|
+
* })
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
scope?: Record<string, unknown>;
|
|
43
60
|
/** Number of operations per batch. Defaults to 100. */
|
|
44
61
|
batchSize?: number;
|
|
45
62
|
/** Schema version of this client. */
|
|
@@ -89,6 +106,71 @@ interface SyncControl {
|
|
|
89
106
|
disconnect(): Promise<void>;
|
|
90
107
|
/** Get the current developer-facing sync status. */
|
|
91
108
|
getStatus(): SyncStatusInfo;
|
|
109
|
+
/** Force an immediate reconnection attempt. No-op if already connected. */
|
|
110
|
+
retryNow(): Promise<void>;
|
|
111
|
+
/** Export a diagnostics snapshot for debugging and support tickets. */
|
|
112
|
+
exportDiagnostics(): _korajs_sync.SyncDiagnostics;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* A transaction collection accessor providing insert, update, delete, and findById.
|
|
116
|
+
*/
|
|
117
|
+
interface TransactionCollectionProxy {
|
|
118
|
+
insert(data: Record<string, unknown>): Promise<CollectionRecord>;
|
|
119
|
+
update(id: string, data: Record<string, unknown>): Promise<CollectionRecord>;
|
|
120
|
+
delete(id: string): Promise<void>;
|
|
121
|
+
findById(id: string): Promise<CollectionRecord | null>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Transaction proxy passed to the transaction callback.
|
|
125
|
+
* Provides collection accessors as direct properties (e.g., tx.todos.insert(...)).
|
|
126
|
+
*/
|
|
127
|
+
interface TransactionProxy {
|
|
128
|
+
/** Dynamic collection accessors for transaction operations. */
|
|
129
|
+
[collection: string]: TransactionCollectionProxy;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Accessor for offline-safe sequences.
|
|
133
|
+
* Generates monotonically increasing, collision-free identifiers
|
|
134
|
+
* that work across offline devices.
|
|
135
|
+
*/
|
|
136
|
+
interface SequenceAccessor {
|
|
137
|
+
/**
|
|
138
|
+
* Get the next value in a sequence, atomically incrementing the counter.
|
|
139
|
+
*
|
|
140
|
+
* @param name - The sequence name (e.g., 'receipt', 'invoice')
|
|
141
|
+
* @param config - Optional configuration for scope, format, and starting value
|
|
142
|
+
* @returns The formatted sequence value
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* const receiptNo = await app.sequences.next('receipt', {
|
|
147
|
+
* scope: storeId,
|
|
148
|
+
* format: 'S-{date}-{node4}-{seq}',
|
|
149
|
+
* })
|
|
150
|
+
* // → "S-20260508-a1b2-0042"
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
next(name: string, config?: SequenceConfig): Promise<string>;
|
|
154
|
+
/**
|
|
155
|
+
* Get the current counter value without incrementing.
|
|
156
|
+
*
|
|
157
|
+
* @param name - The sequence name
|
|
158
|
+
* @param config - Optional scope
|
|
159
|
+
* @returns The current counter value, or 0 if never used
|
|
160
|
+
*/
|
|
161
|
+
current(name: string, config?: {
|
|
162
|
+
scope?: string;
|
|
163
|
+
}): Promise<number>;
|
|
164
|
+
/**
|
|
165
|
+
* Reset a sequence counter.
|
|
166
|
+
*
|
|
167
|
+
* @param name - The sequence name
|
|
168
|
+
* @param config - Optional scope and target value
|
|
169
|
+
*/
|
|
170
|
+
reset(name: string, config?: {
|
|
171
|
+
scope?: string;
|
|
172
|
+
to?: number;
|
|
173
|
+
}): Promise<void>;
|
|
92
174
|
}
|
|
93
175
|
/**
|
|
94
176
|
* The main application object returned by createApp().
|
|
@@ -101,12 +183,41 @@ interface KoraApp {
|
|
|
101
183
|
events: KoraEventEmitter;
|
|
102
184
|
/** Sync control (connect/disconnect/status). Null if sync not configured. */
|
|
103
185
|
sync: SyncControl | null;
|
|
186
|
+
/** Offline-safe sequence generation. */
|
|
187
|
+
sequences: SequenceAccessor;
|
|
104
188
|
/** Get the underlying Store instance (for advanced use / React integration). */
|
|
105
189
|
getStore(): _korajs_store.Store;
|
|
106
190
|
/** Get the underlying SyncEngine instance. Null if sync not configured. */
|
|
107
191
|
getSyncEngine(): SyncEngine | null;
|
|
108
192
|
/** Gracefully close the app: stop sync, close store. */
|
|
109
193
|
close(): Promise<void>;
|
|
194
|
+
/**
|
|
195
|
+
* Execute multiple mutations atomically within a transaction.
|
|
196
|
+
* All operations are committed together or rolled back on error.
|
|
197
|
+
* Subscription notifications are batched after commit.
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* await app.transaction(async (tx) => {
|
|
202
|
+
* await tx.sales.update(saleId, { status: 'completed' })
|
|
203
|
+
* await tx.payments.insert({ saleId, method: 'cash', amount: total })
|
|
204
|
+
* })
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
transaction(fn: (tx: TransactionProxy) => Promise<void>): Promise<Operation[]>;
|
|
208
|
+
/**
|
|
209
|
+
* Execute a named mutation — a transaction with a human-readable name.
|
|
210
|
+
* The mutation name is attached to all operations and visible in DevTools.
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```typescript
|
|
214
|
+
* await app.mutation('complete-sale', async (tx) => {
|
|
215
|
+
* await tx.sales.update(saleId, { status: 'completed' })
|
|
216
|
+
* await tx.payments.insert({ saleId, method: 'cash', amount: total })
|
|
217
|
+
* })
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
mutation(name: string, fn: (tx: TransactionProxy) => Promise<void>): Promise<Operation[]>;
|
|
110
221
|
/** Dynamic collection accessors (e.g., app.todos). Typed via Object.defineProperty. */
|
|
111
222
|
[collection: string]: unknown;
|
|
112
223
|
}
|
|
@@ -137,12 +248,18 @@ type TypedKoraApp<S extends SchemaInput> = {
|
|
|
137
248
|
events: KoraEventEmitter;
|
|
138
249
|
/** Sync control (connect/disconnect/status). Null if sync not configured. */
|
|
139
250
|
sync: SyncControl | null;
|
|
251
|
+
/** Offline-safe sequence generation. */
|
|
252
|
+
sequences: SequenceAccessor;
|
|
140
253
|
/** Get the underlying Store instance (for advanced use / React integration). */
|
|
141
254
|
getStore(): _korajs_store.Store;
|
|
142
255
|
/** Get the underlying SyncEngine instance. Null if sync not configured. */
|
|
143
256
|
getSyncEngine(): SyncEngine | null;
|
|
144
257
|
/** Gracefully close the app: stop sync, close store. */
|
|
145
258
|
close(): Promise<void>;
|
|
259
|
+
/** Execute multiple mutations atomically within a transaction. */
|
|
260
|
+
transaction(fn: (tx: TransactionProxy) => Promise<void>): Promise<Operation[]>;
|
|
261
|
+
/** Execute a named mutation — a transaction with a DevTools-visible name. */
|
|
262
|
+
mutation(name: string, fn: (tx: TransactionProxy) => Promise<void>): Promise<Operation[]>;
|
|
146
263
|
} & {
|
|
147
264
|
readonly [C in keyof S['collections'] & string]: S['collections'][C] extends {
|
|
148
265
|
fields: infer F extends Record<string, FieldBuilder<any, any, any>>;
|
|
@@ -190,4 +307,4 @@ declare function createApp<const S extends SchemaInput>(config: TypedKoraConfig<
|
|
|
190
307
|
*/
|
|
191
308
|
declare function createApp(config: KoraConfig): KoraApp;
|
|
192
309
|
|
|
193
|
-
export { type AdapterType, type KoraApp, type KoraConfig, type StoreOptions, type SyncControl, type SyncOptions, type TypedCollectionAccessor, type TypedKoraApp, type TypedKoraConfig, createApp };
|
|
310
|
+
export { type AdapterType, type KoraApp, type KoraConfig, type SequenceAccessor, type StoreOptions, type SyncControl, type SyncOptions, type TransactionCollectionProxy, type TransactionProxy, type TypedCollectionAccessor, type TypedKoraApp, type TypedKoraConfig, createApp };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { KoraEventEmitter, SchemaDefinition, SchemaInput, FieldBuilder, InferRecord, InferInsertInput, InferUpdateInput } from '@korajs/core';
|
|
2
|
-
export { CollectionDefinition, ConnectionQuality, Constraint, FieldDescriptor, FieldKindToType, HLCTimestamp, HybridLogicalClock, InferFieldType, InferInsertInput, InferRecord, InferUpdateInput, KoraError, KoraEvent, KoraEventEmitter, KoraEventListener, KoraEventType, MergeStrategy, MergeTrace, Operation, SchemaDefinition, SchemaInput, TypedSchemaDefinition, VersionVector, createOperation, defineSchema, generateUUIDv7, t } from '@korajs/core';
|
|
1
|
+
import { KoraEventEmitter, SequenceConfig, Operation, SchemaDefinition, SchemaInput, FieldBuilder, InferRecord, InferInsertInput, InferUpdateInput } from '@korajs/core';
|
|
2
|
+
export { AtomicOp, AtomicOpType, CollectionDefinition, ConnectionQuality, Constraint, FieldDescriptor, FieldKindToType, HLCTimestamp, HybridLogicalClock, InferFieldType, InferInsertInput, InferRecord, InferUpdateInput, KoraError, KoraEvent, KoraEventEmitter, KoraEventListener, KoraEventType, MergeStrategy, MergeTrace, MigrationDefinition, MigrationStep, Operation, SchemaDefinition, SchemaInput, SequenceConfig, TypedSchemaDefinition, VersionVector, createOperation, defineSchema, generateUUIDv7, migrate, op, t } from '@korajs/core';
|
|
3
3
|
import * as _korajs_store from '@korajs/store';
|
|
4
|
-
import { QueryBuilder, CollectionAccessor } from '@korajs/store';
|
|
5
|
-
export { CollectionAccessor, CollectionRecord, StorageAdapter, Store, StoreConfig } from '@korajs/store';
|
|
4
|
+
import { CollectionRecord, QueryBuilder, CollectionAccessor } from '@korajs/store';
|
|
5
|
+
export { CollectionAccessor, CollectionRecord, SequenceManager, StorageAdapter, Store, StoreConfig, TransactionCollectionAccessor, TransactionContext, TransactionContextConfig } from '@korajs/store';
|
|
6
|
+
import * as _korajs_sync from '@korajs/sync';
|
|
6
7
|
import { SyncStatusInfo, SyncEngine } from '@korajs/sync';
|
|
7
|
-
export { SyncConfig, SyncEngine, SyncState, SyncStatus, SyncStatusInfo, SyncStore, WebSocketTransport } from '@korajs/sync';
|
|
8
|
+
export { SyncConfig, SyncDiagnostics, SyncEngine, SyncState, SyncStatus, SyncStatusInfo, SyncStore, WebSocketTransport } from '@korajs/sync';
|
|
8
9
|
export { MergeEngine, MergeInput, MergeResult } from '@korajs/merge';
|
|
9
10
|
|
|
10
11
|
/**
|
|
@@ -40,6 +41,22 @@ interface SyncOptions {
|
|
|
40
41
|
}>;
|
|
41
42
|
/** Sync scopes per collection. */
|
|
42
43
|
scopes?: Record<string, (ctx: Record<string, unknown>) => Record<string, unknown>>;
|
|
44
|
+
/**
|
|
45
|
+
* Flat scope values. Combined with schema scope declarations to build
|
|
46
|
+
* per-collection scope filters sent to the server during handshake.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* createApp({
|
|
51
|
+
* schema,
|
|
52
|
+
* sync: {
|
|
53
|
+
* url: 'wss://server/kora',
|
|
54
|
+
* scope: { orgId: 'org-123', storeId: 'store-456' },
|
|
55
|
+
* },
|
|
56
|
+
* })
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
scope?: Record<string, unknown>;
|
|
43
60
|
/** Number of operations per batch. Defaults to 100. */
|
|
44
61
|
batchSize?: number;
|
|
45
62
|
/** Schema version of this client. */
|
|
@@ -89,6 +106,71 @@ interface SyncControl {
|
|
|
89
106
|
disconnect(): Promise<void>;
|
|
90
107
|
/** Get the current developer-facing sync status. */
|
|
91
108
|
getStatus(): SyncStatusInfo;
|
|
109
|
+
/** Force an immediate reconnection attempt. No-op if already connected. */
|
|
110
|
+
retryNow(): Promise<void>;
|
|
111
|
+
/** Export a diagnostics snapshot for debugging and support tickets. */
|
|
112
|
+
exportDiagnostics(): _korajs_sync.SyncDiagnostics;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* A transaction collection accessor providing insert, update, delete, and findById.
|
|
116
|
+
*/
|
|
117
|
+
interface TransactionCollectionProxy {
|
|
118
|
+
insert(data: Record<string, unknown>): Promise<CollectionRecord>;
|
|
119
|
+
update(id: string, data: Record<string, unknown>): Promise<CollectionRecord>;
|
|
120
|
+
delete(id: string): Promise<void>;
|
|
121
|
+
findById(id: string): Promise<CollectionRecord | null>;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Transaction proxy passed to the transaction callback.
|
|
125
|
+
* Provides collection accessors as direct properties (e.g., tx.todos.insert(...)).
|
|
126
|
+
*/
|
|
127
|
+
interface TransactionProxy {
|
|
128
|
+
/** Dynamic collection accessors for transaction operations. */
|
|
129
|
+
[collection: string]: TransactionCollectionProxy;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Accessor for offline-safe sequences.
|
|
133
|
+
* Generates monotonically increasing, collision-free identifiers
|
|
134
|
+
* that work across offline devices.
|
|
135
|
+
*/
|
|
136
|
+
interface SequenceAccessor {
|
|
137
|
+
/**
|
|
138
|
+
* Get the next value in a sequence, atomically incrementing the counter.
|
|
139
|
+
*
|
|
140
|
+
* @param name - The sequence name (e.g., 'receipt', 'invoice')
|
|
141
|
+
* @param config - Optional configuration for scope, format, and starting value
|
|
142
|
+
* @returns The formatted sequence value
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```typescript
|
|
146
|
+
* const receiptNo = await app.sequences.next('receipt', {
|
|
147
|
+
* scope: storeId,
|
|
148
|
+
* format: 'S-{date}-{node4}-{seq}',
|
|
149
|
+
* })
|
|
150
|
+
* // → "S-20260508-a1b2-0042"
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
next(name: string, config?: SequenceConfig): Promise<string>;
|
|
154
|
+
/**
|
|
155
|
+
* Get the current counter value without incrementing.
|
|
156
|
+
*
|
|
157
|
+
* @param name - The sequence name
|
|
158
|
+
* @param config - Optional scope
|
|
159
|
+
* @returns The current counter value, or 0 if never used
|
|
160
|
+
*/
|
|
161
|
+
current(name: string, config?: {
|
|
162
|
+
scope?: string;
|
|
163
|
+
}): Promise<number>;
|
|
164
|
+
/**
|
|
165
|
+
* Reset a sequence counter.
|
|
166
|
+
*
|
|
167
|
+
* @param name - The sequence name
|
|
168
|
+
* @param config - Optional scope and target value
|
|
169
|
+
*/
|
|
170
|
+
reset(name: string, config?: {
|
|
171
|
+
scope?: string;
|
|
172
|
+
to?: number;
|
|
173
|
+
}): Promise<void>;
|
|
92
174
|
}
|
|
93
175
|
/**
|
|
94
176
|
* The main application object returned by createApp().
|
|
@@ -101,12 +183,41 @@ interface KoraApp {
|
|
|
101
183
|
events: KoraEventEmitter;
|
|
102
184
|
/** Sync control (connect/disconnect/status). Null if sync not configured. */
|
|
103
185
|
sync: SyncControl | null;
|
|
186
|
+
/** Offline-safe sequence generation. */
|
|
187
|
+
sequences: SequenceAccessor;
|
|
104
188
|
/** Get the underlying Store instance (for advanced use / React integration). */
|
|
105
189
|
getStore(): _korajs_store.Store;
|
|
106
190
|
/** Get the underlying SyncEngine instance. Null if sync not configured. */
|
|
107
191
|
getSyncEngine(): SyncEngine | null;
|
|
108
192
|
/** Gracefully close the app: stop sync, close store. */
|
|
109
193
|
close(): Promise<void>;
|
|
194
|
+
/**
|
|
195
|
+
* Execute multiple mutations atomically within a transaction.
|
|
196
|
+
* All operations are committed together or rolled back on error.
|
|
197
|
+
* Subscription notifications are batched after commit.
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* ```typescript
|
|
201
|
+
* await app.transaction(async (tx) => {
|
|
202
|
+
* await tx.sales.update(saleId, { status: 'completed' })
|
|
203
|
+
* await tx.payments.insert({ saleId, method: 'cash', amount: total })
|
|
204
|
+
* })
|
|
205
|
+
* ```
|
|
206
|
+
*/
|
|
207
|
+
transaction(fn: (tx: TransactionProxy) => Promise<void>): Promise<Operation[]>;
|
|
208
|
+
/**
|
|
209
|
+
* Execute a named mutation — a transaction with a human-readable name.
|
|
210
|
+
* The mutation name is attached to all operations and visible in DevTools.
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```typescript
|
|
214
|
+
* await app.mutation('complete-sale', async (tx) => {
|
|
215
|
+
* await tx.sales.update(saleId, { status: 'completed' })
|
|
216
|
+
* await tx.payments.insert({ saleId, method: 'cash', amount: total })
|
|
217
|
+
* })
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
mutation(name: string, fn: (tx: TransactionProxy) => Promise<void>): Promise<Operation[]>;
|
|
110
221
|
/** Dynamic collection accessors (e.g., app.todos). Typed via Object.defineProperty. */
|
|
111
222
|
[collection: string]: unknown;
|
|
112
223
|
}
|
|
@@ -137,12 +248,18 @@ type TypedKoraApp<S extends SchemaInput> = {
|
|
|
137
248
|
events: KoraEventEmitter;
|
|
138
249
|
/** Sync control (connect/disconnect/status). Null if sync not configured. */
|
|
139
250
|
sync: SyncControl | null;
|
|
251
|
+
/** Offline-safe sequence generation. */
|
|
252
|
+
sequences: SequenceAccessor;
|
|
140
253
|
/** Get the underlying Store instance (for advanced use / React integration). */
|
|
141
254
|
getStore(): _korajs_store.Store;
|
|
142
255
|
/** Get the underlying SyncEngine instance. Null if sync not configured. */
|
|
143
256
|
getSyncEngine(): SyncEngine | null;
|
|
144
257
|
/** Gracefully close the app: stop sync, close store. */
|
|
145
258
|
close(): Promise<void>;
|
|
259
|
+
/** Execute multiple mutations atomically within a transaction. */
|
|
260
|
+
transaction(fn: (tx: TransactionProxy) => Promise<void>): Promise<Operation[]>;
|
|
261
|
+
/** Execute a named mutation — a transaction with a DevTools-visible name. */
|
|
262
|
+
mutation(name: string, fn: (tx: TransactionProxy) => Promise<void>): Promise<Operation[]>;
|
|
146
263
|
} & {
|
|
147
264
|
readonly [C in keyof S['collections'] & string]: S['collections'][C] extends {
|
|
148
265
|
fields: infer F extends Record<string, FieldBuilder<any, any, any>>;
|
|
@@ -190,4 +307,4 @@ declare function createApp<const S extends SchemaInput>(config: TypedKoraConfig<
|
|
|
190
307
|
*/
|
|
191
308
|
declare function createApp(config: KoraConfig): KoraApp;
|
|
192
309
|
|
|
193
|
-
export { type AdapterType, type KoraApp, type KoraConfig, type StoreOptions, type SyncControl, type SyncOptions, type TypedCollectionAccessor, type TypedKoraApp, type TypedKoraConfig, createApp };
|
|
310
|
+
export { type AdapterType, type KoraApp, type KoraConfig, type SequenceAccessor, type StoreOptions, type SyncControl, type SyncOptions, type TransactionCollectionProxy, type TransactionProxy, type TypedCollectionAccessor, type TypedKoraApp, type TypedKoraConfig, createApp };
|
package/dist/index.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
// src/create-app.ts
|
|
2
|
+
import { buildScopeMap } from "@korajs/core";
|
|
2
3
|
import { SimpleEventEmitter } from "@korajs/core/internal";
|
|
3
4
|
import { Instrumenter } from "@korajs/devtools";
|
|
4
5
|
import { MergeEngine } from "@korajs/merge";
|
|
5
6
|
import { Store } from "@korajs/store";
|
|
6
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
ConnectionMonitor,
|
|
9
|
+
ReconnectionManager,
|
|
10
|
+
SyncEngine,
|
|
11
|
+
WebSocketTransport
|
|
12
|
+
} from "@korajs/sync";
|
|
7
13
|
|
|
8
14
|
// src/adapter-resolver.ts
|
|
9
15
|
function detectAdapterType() {
|
|
@@ -24,8 +30,10 @@ function detectAdapterType() {
|
|
|
24
30
|
async function createAdapter(type, dbName, workerUrl) {
|
|
25
31
|
switch (type) {
|
|
26
32
|
case "tauri-sqlite": {
|
|
27
|
-
const
|
|
28
|
-
|
|
33
|
+
const { TauriSqliteAdapter } = await import(
|
|
34
|
+
/* @vite-ignore */
|
|
35
|
+
"@korajs/tauri"
|
|
36
|
+
);
|
|
29
37
|
return new TauriSqliteAdapter({ path: `${dbName}.db` });
|
|
30
38
|
}
|
|
31
39
|
case "better-sqlite3": {
|
|
@@ -69,23 +77,23 @@ var MergeAwareSyncStore = class {
|
|
|
69
77
|
async getOperationRange(nodeId, fromSeq, toSeq) {
|
|
70
78
|
return this.store.getOperationRange(nodeId, fromSeq, toSeq);
|
|
71
79
|
}
|
|
72
|
-
async applyRemoteOperation(
|
|
73
|
-
if (
|
|
74
|
-
return this.store.applyRemoteOperation(
|
|
80
|
+
async applyRemoteOperation(op2) {
|
|
81
|
+
if (op2.type !== "update" || !op2.data || !op2.previousData) {
|
|
82
|
+
return this.store.applyRemoteOperation(op2);
|
|
75
83
|
}
|
|
76
84
|
const schema = this.store.getSchema();
|
|
77
|
-
const collectionDef = schema.collections[
|
|
85
|
+
const collectionDef = schema.collections[op2.collection];
|
|
78
86
|
if (!collectionDef) {
|
|
79
|
-
return this.store.applyRemoteOperation(
|
|
87
|
+
return this.store.applyRemoteOperation(op2);
|
|
80
88
|
}
|
|
81
|
-
const accessor = this.store.collection(
|
|
82
|
-
const currentRecord = await accessor.findById(
|
|
89
|
+
const accessor = this.store.collection(op2.collection);
|
|
90
|
+
const currentRecord = await accessor.findById(op2.recordId);
|
|
83
91
|
if (!currentRecord) {
|
|
84
|
-
return this.store.applyRemoteOperation(
|
|
92
|
+
return this.store.applyRemoteOperation(op2);
|
|
85
93
|
}
|
|
86
94
|
let hasConflict = false;
|
|
87
|
-
for (const field of Object.keys(
|
|
88
|
-
const expectedBase =
|
|
95
|
+
for (const field of Object.keys(op2.data)) {
|
|
96
|
+
const expectedBase = op2.previousData[field];
|
|
89
97
|
const currentLocal = currentRecord[field];
|
|
90
98
|
if (!deepEqual(expectedBase, currentLocal)) {
|
|
91
99
|
hasConflict = true;
|
|
@@ -93,24 +101,24 @@ var MergeAwareSyncStore = class {
|
|
|
93
101
|
}
|
|
94
102
|
}
|
|
95
103
|
if (!hasConflict) {
|
|
96
|
-
return this.store.applyRemoteOperation(
|
|
104
|
+
return this.store.applyRemoteOperation(op2);
|
|
97
105
|
}
|
|
98
106
|
this.emitter?.emit({
|
|
99
107
|
type: "merge:started",
|
|
100
|
-
operationA:
|
|
101
|
-
operationB:
|
|
108
|
+
operationA: op2,
|
|
109
|
+
operationB: op2
|
|
102
110
|
});
|
|
103
|
-
const baseState = { ...
|
|
111
|
+
const baseState = { ...op2.previousData };
|
|
104
112
|
const localOp = {
|
|
105
|
-
...
|
|
113
|
+
...op2,
|
|
106
114
|
// The "local" operation's data is the diff between base and current local state
|
|
107
|
-
data: buildLocalDiff(
|
|
108
|
-
previousData:
|
|
115
|
+
data: buildLocalDiff(op2.previousData, currentRecord, Object.keys(op2.data)),
|
|
116
|
+
previousData: op2.previousData,
|
|
109
117
|
nodeId: this.store.getNodeId()
|
|
110
118
|
};
|
|
111
119
|
const input = {
|
|
112
120
|
local: localOp,
|
|
113
|
-
remote:
|
|
121
|
+
remote: op2,
|
|
114
122
|
baseState,
|
|
115
123
|
collectionDef
|
|
116
124
|
};
|
|
@@ -123,7 +131,7 @@ var MergeAwareSyncStore = class {
|
|
|
123
131
|
this.emitter?.emit({ type: "merge:completed", trace: firstTrace });
|
|
124
132
|
}
|
|
125
133
|
const mergedOp = {
|
|
126
|
-
...
|
|
134
|
+
...op2,
|
|
127
135
|
data: result.mergedData
|
|
128
136
|
};
|
|
129
137
|
return this.store.applyRemoteOperation(mergedOp);
|
|
@@ -245,13 +253,104 @@ function createApp(config) {
|
|
|
245
253
|
if (syncEngine) {
|
|
246
254
|
return syncEngine.getStatus();
|
|
247
255
|
}
|
|
248
|
-
return {
|
|
256
|
+
return {
|
|
257
|
+
status: "offline",
|
|
258
|
+
pendingOperations: 0,
|
|
259
|
+
lastSyncedAt: null,
|
|
260
|
+
lastSuccessfulPush: null,
|
|
261
|
+
lastSuccessfulPull: null,
|
|
262
|
+
conflicts: 0
|
|
263
|
+
};
|
|
264
|
+
},
|
|
265
|
+
async retryNow() {
|
|
266
|
+
await ready;
|
|
267
|
+
if (syncEngine) {
|
|
268
|
+
await syncEngine.retryNow();
|
|
269
|
+
}
|
|
270
|
+
},
|
|
271
|
+
exportDiagnostics() {
|
|
272
|
+
if (syncEngine) {
|
|
273
|
+
return syncEngine.exportDiagnostics();
|
|
274
|
+
}
|
|
275
|
+
return {
|
|
276
|
+
state: "disconnected",
|
|
277
|
+
status: {
|
|
278
|
+
status: "offline",
|
|
279
|
+
pendingOperations: 0,
|
|
280
|
+
lastSyncedAt: null,
|
|
281
|
+
lastSuccessfulPush: null,
|
|
282
|
+
lastSuccessfulPull: null,
|
|
283
|
+
conflicts: 0
|
|
284
|
+
},
|
|
285
|
+
nodeId: "",
|
|
286
|
+
url: config.sync?.url ?? "",
|
|
287
|
+
schemaVersion: config.schema.version,
|
|
288
|
+
lastSyncedAt: null,
|
|
289
|
+
lastSuccessfulPush: null,
|
|
290
|
+
lastSuccessfulPull: null,
|
|
291
|
+
conflicts: 0,
|
|
292
|
+
pendingOperations: 0,
|
|
293
|
+
hasInFlightBatch: false,
|
|
294
|
+
reconnecting: false,
|
|
295
|
+
timestamp: Date.now()
|
|
296
|
+
};
|
|
249
297
|
}
|
|
250
298
|
} : null;
|
|
299
|
+
async function executeTransaction(fn, mutationName) {
|
|
300
|
+
await ready;
|
|
301
|
+
if (!store) {
|
|
302
|
+
throw new Error("Store not initialized. Await app.ready before using transactions.");
|
|
303
|
+
}
|
|
304
|
+
const txContext = store.createTransaction();
|
|
305
|
+
if (mutationName !== void 0) {
|
|
306
|
+
txContext.setMutationName(mutationName);
|
|
307
|
+
}
|
|
308
|
+
const collectionNames = Object.keys(config.schema.collections);
|
|
309
|
+
const proxy = {};
|
|
310
|
+
for (const name of collectionNames) {
|
|
311
|
+
Object.defineProperty(proxy, name, {
|
|
312
|
+
get() {
|
|
313
|
+
return txContext.collection(name);
|
|
314
|
+
},
|
|
315
|
+
enumerable: true,
|
|
316
|
+
configurable: false
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
await fn(proxy);
|
|
321
|
+
const { operations } = await txContext.commit();
|
|
322
|
+
for (const op2 of operations) {
|
|
323
|
+
store.getSubscriptionManager().notify(op2.collection, op2);
|
|
324
|
+
emitter.emit({ type: "operation:created", operation: op2 });
|
|
325
|
+
}
|
|
326
|
+
return operations;
|
|
327
|
+
} catch (error) {
|
|
328
|
+
txContext.rollback();
|
|
329
|
+
throw error;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
const sequences = {
|
|
333
|
+
async next(name, config2) {
|
|
334
|
+
await ready;
|
|
335
|
+
if (!store) throw new Error("Store not initialized. Await app.ready before using sequences.");
|
|
336
|
+
return store.getSequenceManager().next(name, config2);
|
|
337
|
+
},
|
|
338
|
+
async current(name, config2) {
|
|
339
|
+
await ready;
|
|
340
|
+
if (!store) throw new Error("Store not initialized. Await app.ready before using sequences.");
|
|
341
|
+
return store.getSequenceManager().current(name, config2);
|
|
342
|
+
},
|
|
343
|
+
async reset(name, config2) {
|
|
344
|
+
await ready;
|
|
345
|
+
if (!store) throw new Error("Store not initialized. Await app.ready before using sequences.");
|
|
346
|
+
return store.getSequenceManager().reset(name, config2);
|
|
347
|
+
}
|
|
348
|
+
};
|
|
251
349
|
const app = {
|
|
252
350
|
ready,
|
|
253
351
|
events: emitter,
|
|
254
352
|
sync: syncControl,
|
|
353
|
+
sequences,
|
|
255
354
|
getStore() {
|
|
256
355
|
if (!store) {
|
|
257
356
|
throw new Error("Store not initialized. Await app.ready before accessing the store.");
|
|
@@ -261,6 +360,12 @@ function createApp(config) {
|
|
|
261
360
|
getSyncEngine() {
|
|
262
361
|
return syncEngine;
|
|
263
362
|
},
|
|
363
|
+
async transaction(fn) {
|
|
364
|
+
return executeTransaction(fn);
|
|
365
|
+
},
|
|
366
|
+
async mutation(name, fn) {
|
|
367
|
+
return executeTransaction(fn, name);
|
|
368
|
+
},
|
|
264
369
|
async close() {
|
|
265
370
|
await ready;
|
|
266
371
|
intentionalDisconnect = true;
|
|
@@ -314,6 +419,7 @@ async function initializeAsync(config, emitter, mergeEngine) {
|
|
|
314
419
|
if (config.sync) {
|
|
315
420
|
const transport = new WebSocketTransport();
|
|
316
421
|
const mergeAwareStore = new MergeAwareSyncStore(store, mergeEngine, emitter);
|
|
422
|
+
const scopeMap = config.sync.scope ? buildScopeMap(config.schema, config.sync.scope) : void 0;
|
|
317
423
|
syncEngine = new SyncEngine({
|
|
318
424
|
transport,
|
|
319
425
|
store: mergeAwareStore,
|
|
@@ -322,7 +428,8 @@ async function initializeAsync(config, emitter, mergeEngine) {
|
|
|
322
428
|
transport: config.sync.transport,
|
|
323
429
|
auth: config.sync.auth,
|
|
324
430
|
batchSize: config.sync.batchSize,
|
|
325
|
-
schemaVersion: config.sync.schemaVersion ?? config.schema.version
|
|
431
|
+
schemaVersion: config.sync.schemaVersion ?? config.schema.version,
|
|
432
|
+
scopeMap
|
|
326
433
|
},
|
|
327
434
|
emitter
|
|
328
435
|
});
|
|
@@ -415,25 +522,31 @@ function createPendingQueryBuilder(initialWhere) {
|
|
|
415
522
|
}
|
|
416
523
|
|
|
417
524
|
// src/index.ts
|
|
418
|
-
import { defineSchema, t } from "@korajs/core";
|
|
525
|
+
import { defineSchema, migrate, t } from "@korajs/core";
|
|
419
526
|
import { HybridLogicalClock } from "@korajs/core";
|
|
420
527
|
import { generateUUIDv7 } from "@korajs/core";
|
|
421
528
|
import { createOperation } from "@korajs/core";
|
|
422
529
|
import { KoraError } from "@korajs/core";
|
|
423
|
-
import {
|
|
530
|
+
import { op } from "@korajs/core";
|
|
531
|
+
import { SequenceManager, Store as Store2 } from "@korajs/store";
|
|
532
|
+
import { TransactionContext } from "@korajs/store";
|
|
424
533
|
import { MergeEngine as MergeEngine2 } from "@korajs/merge";
|
|
425
534
|
import { SyncEngine as SyncEngine2, WebSocketTransport as WebSocketTransport2 } from "@korajs/sync";
|
|
426
535
|
export {
|
|
427
536
|
HybridLogicalClock,
|
|
428
537
|
KoraError,
|
|
429
538
|
MergeEngine2 as MergeEngine,
|
|
539
|
+
SequenceManager,
|
|
430
540
|
Store2 as Store,
|
|
431
541
|
SyncEngine2 as SyncEngine,
|
|
542
|
+
TransactionContext,
|
|
432
543
|
WebSocketTransport2 as WebSocketTransport,
|
|
433
544
|
createApp,
|
|
434
545
|
createOperation,
|
|
435
546
|
defineSchema,
|
|
436
547
|
generateUUIDv7,
|
|
548
|
+
migrate,
|
|
549
|
+
op,
|
|
437
550
|
t
|
|
438
551
|
};
|
|
439
552
|
//# sourceMappingURL=index.js.map
|