@korajs/store 1.0.0-beta.12 → 1.0.0-beta.13
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/README.md +29 -37
- package/dist/adapters/better-sqlite3.cjs +90 -4
- package/dist/adapters/better-sqlite3.cjs.map +1 -1
- package/dist/adapters/better-sqlite3.d.cts +21 -12
- package/dist/adapters/better-sqlite3.d.ts +21 -12
- package/dist/adapters/better-sqlite3.js +69 -5
- package/dist/adapters/better-sqlite3.js.map +1 -1
- package/dist/adapters/indexeddb.cjs +1316 -610
- package/dist/adapters/indexeddb.cjs.map +1 -1
- package/dist/adapters/indexeddb.d.cts +29 -3
- package/dist/adapters/indexeddb.d.ts +29 -3
- package/dist/adapters/indexeddb.js +247 -221
- package/dist/adapters/indexeddb.js.map +1 -1
- package/dist/adapters/sqlite-wasm-worker.cjs +803 -62
- package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
- package/dist/adapters/sqlite-wasm-worker.js +480 -62
- package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
- package/dist/adapters/sqlite-wasm.cjs +1032 -344
- package/dist/adapters/sqlite-wasm.cjs.map +1 -1
- package/dist/adapters/sqlite-wasm.d.cts +239 -18
- package/dist/adapters/sqlite-wasm.d.ts +239 -18
- package/dist/adapters/sqlite-wasm.js +111 -5
- package/dist/adapters/sqlite-wasm.js.map +1 -1
- package/dist/backup-4NUPDEMB.js +25 -0
- package/dist/chunk-32EDKWMI.js +876 -0
- package/dist/chunk-32EDKWMI.js.map +1 -0
- package/dist/{chunk-CYO3YKFL.js → chunk-5ZRRF6R2.js} +2 -2
- package/dist/chunk-A6GFH5IG.js +36 -0
- package/dist/chunk-A6GFH5IG.js.map +1 -0
- package/dist/chunk-DUKTWVZW.js +175 -0
- package/dist/chunk-DUKTWVZW.js.map +1 -0
- package/dist/chunk-HZTDBLDB.js +607 -0
- package/dist/chunk-HZTDBLDB.js.map +1 -0
- package/dist/{chunk-GSJ7EEUC.js → chunk-J5AGXF3D.js} +58 -9
- package/dist/chunk-J5AGXF3D.js.map +1 -0
- package/dist/chunk-MCQKEHOD.js +112 -0
- package/dist/chunk-MCQKEHOD.js.map +1 -0
- package/dist/chunk-P4V375FY.js +810 -0
- package/dist/chunk-P4V375FY.js.map +1 -0
- package/dist/chunk-QDUSJMFV.js +1095 -0
- package/dist/chunk-QDUSJMFV.js.map +1 -0
- package/dist/chunk-SJP2HTYZ.js +1003 -0
- package/dist/chunk-SJP2HTYZ.js.map +1 -0
- package/dist/chunk-W5F2LWG6.js +591 -0
- package/dist/chunk-W5F2LWG6.js.map +1 -0
- package/dist/chunk-Y5LRH7KI.js +5341 -0
- package/dist/chunk-Y5LRH7KI.js.map +1 -0
- package/dist/{export-audit-ODGQTQIK.js → export-audit-J2QXRHD2.js} +3 -3
- package/dist/index.cjs +9371 -4240
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1053 -110
- package/dist/index.d.ts +1053 -110
- package/dist/index.js +249 -3158
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +580 -264
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +15 -85
- package/dist/internal.d.ts +15 -85
- package/dist/internal.js +10 -8
- package/dist/restore-FR7XIKT2.js +299 -0
- package/dist/restore-FR7XIKT2.js.map +1 -0
- package/dist/sql-builder-C4u55jKW.d.cts +608 -0
- package/dist/sql-builder-CGckdBDL.d.ts +608 -0
- package/dist/sqlite-wasm-channel-DVX93Uiq.d.cts +141 -0
- package/dist/sqlite-wasm-channel-DVX93Uiq.d.ts +141 -0
- package/dist/sqlite-wasm-channel-MLHLKEED.js +12 -0
- package/dist/store-IMWKZLWB.js +16 -0
- package/dist/store-IMWKZLWB.js.map +1 -0
- package/dist/{types-BslDOTk0.d.ts → types-Dh-xyUjP.d.cts} +99 -26
- package/dist/{types-BslDOTk0.d.cts → types-Dh-xyUjP.d.ts} +99 -26
- package/package.json +3 -2
- package/dist/backup-5S7K2Z35.js +0 -14
- package/dist/chunk-GSJ7EEUC.js.map +0 -1
- package/dist/chunk-HS6DBMST.js +0 -383
- package/dist/chunk-HS6DBMST.js.map +0 -1
- package/dist/chunk-RJIEKEN4.js +0 -751
- package/dist/chunk-RJIEKEN4.js.map +0 -1
- package/dist/chunk-TUR7RGEP.js +0 -84
- package/dist/chunk-TUR7RGEP.js.map +0 -1
- package/dist/chunk-VDBJQ75K.js +0 -78
- package/dist/chunk-VDBJQ75K.js.map +0 -1
- package/dist/chunk-YT7MRFCP.js +0 -687
- package/dist/chunk-YT7MRFCP.js.map +0 -1
- package/dist/sqlite-wasm-channel-GGHA2BXU.js +0 -12
- package/dist/sqlite-wasm-channel-L6o3bWQK.d.cts +0 -92
- package/dist/sqlite-wasm-channel-L6o3bWQK.d.ts +0 -92
- package/dist/types-BOO9EPt1.d.ts +0 -170
- package/dist/types-C_GDavG-.d.cts +0 -170
- /package/dist/{backup-5S7K2Z35.js.map → backup-4NUPDEMB.js.map} +0 -0
- /package/dist/{chunk-CYO3YKFL.js.map → chunk-5ZRRF6R2.js.map} +0 -0
- /package/dist/{export-audit-ODGQTQIK.js.map → export-audit-J2QXRHD2.js.map} +0 -0
- /package/dist/{sqlite-wasm-channel-GGHA2BXU.js.map → sqlite-wasm-channel-MLHLKEED.js.map} +0 -0
package/dist/internal.d.cts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Operation, FieldDescriptor, HLCTimestamp } from '@korajs/core';
|
|
2
|
-
import { O as OperationRow, R as RawCollectionRow, C as CollectionRecord
|
|
3
|
-
export { b as MetaRow, c as Subscription, V as VersionVectorRow } from './types-
|
|
4
|
-
import { L as LocalMutationContext } from './
|
|
2
|
+
import { O as OperationRow, R as RawCollectionRow, C as CollectionRecord } from './types-Dh-xyUjP.cjs';
|
|
3
|
+
export { b as MetaRow, c as Subscription, V as VersionVectorRow } from './types-Dh-xyUjP.cjs';
|
|
4
|
+
import { L as LocalMutationContext } from './sql-builder-C4u55jKW.cjs';
|
|
5
|
+
export { D as DatabaseQuery, S as SqlQuery, b as buildCountQuery, a as buildInsertQuery, c as buildLwwSoftDeleteQuery, d as buildLwwUpdateQuery, e as buildSelectQuery, f as buildSoftDeleteQuery, g as buildUpdateQuery, h as buildWhereClause, i as hasUnsyncedOwnOperations } from './sql-builder-C4u55jKW.cjs';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Serialize a JS record to SQL-compatible values for INSERT/UPDATE.
|
|
@@ -40,75 +41,6 @@ declare function deserializeOperation(row: OperationRow): Operation;
|
|
|
40
41
|
*/
|
|
41
42
|
declare function deserializeOperationWithCollection(row: OperationRow, collection: string): Operation;
|
|
42
43
|
|
|
43
|
-
/**
|
|
44
|
-
* Result of building a SQL query: the parameterized SQL string and its bound values.
|
|
45
|
-
*/
|
|
46
|
-
interface SqlQuery {
|
|
47
|
-
sql: string;
|
|
48
|
-
params: unknown[];
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Build a SELECT query from a QueryDescriptor.
|
|
52
|
-
* Automatically adds `WHERE _deleted = 0` to exclude soft-deleted records.
|
|
53
|
-
*
|
|
54
|
-
* @param descriptor - The query descriptor
|
|
55
|
-
* @param fields - The field descriptors from the collection schema
|
|
56
|
-
* @returns A parameterized SQL query
|
|
57
|
-
*/
|
|
58
|
-
declare function buildSelectQuery(descriptor: QueryDescriptor, fields: Record<string, FieldDescriptor>): SqlQuery;
|
|
59
|
-
/**
|
|
60
|
-
* Build a COUNT query from a QueryDescriptor.
|
|
61
|
-
* Automatically adds `WHERE _deleted = 0`.
|
|
62
|
-
*
|
|
63
|
-
* @param descriptor - The query descriptor
|
|
64
|
-
* @param fields - The field descriptors from the collection schema
|
|
65
|
-
* @returns A parameterized SQL query that returns { count: number }
|
|
66
|
-
*/
|
|
67
|
-
declare function buildCountQuery(descriptor: QueryDescriptor, fields: Record<string, FieldDescriptor>): SqlQuery;
|
|
68
|
-
/**
|
|
69
|
-
* Build an INSERT query for a collection record.
|
|
70
|
-
*
|
|
71
|
-
* @param collection - The collection name
|
|
72
|
-
* @param record - The record data (already serialized with id, _created_at, _updated_at)
|
|
73
|
-
* @returns A parameterized SQL query
|
|
74
|
-
*/
|
|
75
|
-
declare function buildInsertQuery(collection: string, record: Record<string, unknown>): SqlQuery;
|
|
76
|
-
/**
|
|
77
|
-
* Build an UPDATE query for a collection record.
|
|
78
|
-
*
|
|
79
|
-
* @param collection - The collection name
|
|
80
|
-
* @param id - The record ID
|
|
81
|
-
* @param changes - The fields to update (already serialized)
|
|
82
|
-
* @returns A parameterized SQL query
|
|
83
|
-
*/
|
|
84
|
-
declare function buildUpdateQuery(collection: string, id: string, changes: Record<string, unknown>): SqlQuery;
|
|
85
|
-
/**
|
|
86
|
-
* Build a soft-delete query (SET _deleted = 1).
|
|
87
|
-
*
|
|
88
|
-
* @param collection - The collection name
|
|
89
|
-
* @param id - The record ID
|
|
90
|
-
* @param updatedAt - The timestamp to set on _updated_at
|
|
91
|
-
* @returns A parameterized SQL query
|
|
92
|
-
*/
|
|
93
|
-
declare function buildSoftDeleteQuery(collection: string, id: string, updatedAt: number, version?: string): SqlQuery;
|
|
94
|
-
/**
|
|
95
|
-
* Build an UPDATE that applies only when the row is missing or older than `remoteVersion`
|
|
96
|
-
* (serialized HLC). Prevents stale remote sync from overwriting newer local materialized state.
|
|
97
|
-
*/
|
|
98
|
-
declare function buildLwwUpdateQuery(collection: string, id: string, changes: Record<string, unknown>, remoteVersion: string): SqlQuery;
|
|
99
|
-
/**
|
|
100
|
-
* Build a soft-delete that applies only when the row is older than `remoteVersion`.
|
|
101
|
-
*/
|
|
102
|
-
declare function buildLwwSoftDeleteQuery(collection: string, id: string, updatedAt: number, version: string): SqlQuery;
|
|
103
|
-
/**
|
|
104
|
-
* Build a WHERE clause from conditions, validating field names against the schema.
|
|
105
|
-
*
|
|
106
|
-
* @param where - The where conditions
|
|
107
|
-
* @param fields - The field descriptors from the collection schema
|
|
108
|
-
* @returns The SQL WHERE clause string and params, or null if no conditions
|
|
109
|
-
*/
|
|
110
|
-
declare function buildWhereClause(where: Record<string, unknown>, fields: Record<string, FieldDescriptor>): SqlQuery | null;
|
|
111
|
-
|
|
112
44
|
/**
|
|
113
45
|
* Serialize an HLC timestamp for storage in the `_version` column.
|
|
114
46
|
* Lexicographic order matches {@link HybridLogicalClock.compare}.
|
|
@@ -125,30 +57,28 @@ declare function rowVersionFromRecord(row: Pick<RawCollectionRow, '_updated_at'
|
|
|
125
57
|
declare function isIncomingNewerThanRow(incoming: HLCTimestamp, row: Pick<RawCollectionRow, '_updated_at' | '_version'>): boolean;
|
|
126
58
|
|
|
127
59
|
/**
|
|
128
|
-
* Insert a record and persist
|
|
60
|
+
* Insert a record and persist its operation, atomically, through the single
|
|
61
|
+
* local write path (sequence reserved and operation built inside the write
|
|
62
|
+
* transaction).
|
|
129
63
|
*/
|
|
130
64
|
declare function executeInsert(ctx: LocalMutationContext, data: Record<string, unknown>): Promise<CollectionRecord>;
|
|
131
65
|
|
|
132
66
|
/**
|
|
133
|
-
* Update a record and persist
|
|
67
|
+
* Update a record and persist its operation, atomically. The row read, the
|
|
68
|
+
* state-machine check and atomic-op resolution all happen inside the write
|
|
69
|
+
* transaction, so concurrent increments compose (STORE-9).
|
|
134
70
|
*/
|
|
135
71
|
declare function executeUpdate(ctx: LocalMutationContext, id: string, data: Record<string, unknown>): Promise<CollectionRecord>;
|
|
136
72
|
|
|
137
73
|
interface ExecuteDeleteOptions {
|
|
138
|
-
/** When true, skip
|
|
74
|
+
/** When true, skip referential checks and cascades (caller already enforced them). */
|
|
139
75
|
skipReferentialEnforcement?: boolean;
|
|
140
|
-
/** Pre-built delete operation (avoids duplicate sequence allocation). */
|
|
141
|
-
operation?: Operation;
|
|
142
76
|
}
|
|
143
77
|
/**
|
|
144
|
-
* Soft-delete a record and persist the operation log entry atomically
|
|
145
|
-
*
|
|
78
|
+
* Soft-delete a record and persist the operation log entry atomically, together
|
|
79
|
+
* with any referential side effects (cascades / set-null), in one transaction.
|
|
80
|
+
* Returns the primary delete operation followed by the side-effect operations.
|
|
146
81
|
*/
|
|
147
82
|
declare function executeDelete(ctx: LocalMutationContext, id: string, options?: ExecuteDeleteOptions): Promise<Operation[]>;
|
|
148
83
|
|
|
149
|
-
|
|
150
|
-
* Merge explicit parent deps (e.g. referential cascade) with tracker-derived deps.
|
|
151
|
-
*/
|
|
152
|
-
declare function resolveCausalDeps(ctx: LocalMutationContext): string[];
|
|
153
|
-
|
|
154
|
-
export { type ExecuteDeleteOptions, LocalMutationContext, OperationRow, RawCollectionRow, type SqlQuery, buildCountQuery, buildInsertQuery, buildLwwSoftDeleteQuery, buildLwwUpdateQuery, buildSelectQuery, buildSoftDeleteQuery, buildUpdateQuery, buildWhereClause, deserializeOperation, deserializeOperationWithCollection, deserializeRecord, executeDelete, executeInsert, executeUpdate, isIncomingNewerThanRow, resolveCausalDeps, rowVersionFromRecord, serializeOperation, serializeRecord, serializeRowVersion };
|
|
84
|
+
export { type ExecuteDeleteOptions, LocalMutationContext, OperationRow, RawCollectionRow, deserializeOperation, deserializeOperationWithCollection, deserializeRecord, executeDelete, executeInsert, executeUpdate, isIncomingNewerThanRow, rowVersionFromRecord, serializeOperation, serializeRecord, serializeRowVersion };
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Operation, FieldDescriptor, HLCTimestamp } from '@korajs/core';
|
|
2
|
-
import { O as OperationRow, R as RawCollectionRow, C as CollectionRecord
|
|
3
|
-
export { b as MetaRow, c as Subscription, V as VersionVectorRow } from './types-
|
|
4
|
-
import { L as LocalMutationContext } from './
|
|
2
|
+
import { O as OperationRow, R as RawCollectionRow, C as CollectionRecord } from './types-Dh-xyUjP.js';
|
|
3
|
+
export { b as MetaRow, c as Subscription, V as VersionVectorRow } from './types-Dh-xyUjP.js';
|
|
4
|
+
import { L as LocalMutationContext } from './sql-builder-CGckdBDL.js';
|
|
5
|
+
export { D as DatabaseQuery, S as SqlQuery, b as buildCountQuery, a as buildInsertQuery, c as buildLwwSoftDeleteQuery, d as buildLwwUpdateQuery, e as buildSelectQuery, f as buildSoftDeleteQuery, g as buildUpdateQuery, h as buildWhereClause, i as hasUnsyncedOwnOperations } from './sql-builder-CGckdBDL.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Serialize a JS record to SQL-compatible values for INSERT/UPDATE.
|
|
@@ -40,75 +41,6 @@ declare function deserializeOperation(row: OperationRow): Operation;
|
|
|
40
41
|
*/
|
|
41
42
|
declare function deserializeOperationWithCollection(row: OperationRow, collection: string): Operation;
|
|
42
43
|
|
|
43
|
-
/**
|
|
44
|
-
* Result of building a SQL query: the parameterized SQL string and its bound values.
|
|
45
|
-
*/
|
|
46
|
-
interface SqlQuery {
|
|
47
|
-
sql: string;
|
|
48
|
-
params: unknown[];
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Build a SELECT query from a QueryDescriptor.
|
|
52
|
-
* Automatically adds `WHERE _deleted = 0` to exclude soft-deleted records.
|
|
53
|
-
*
|
|
54
|
-
* @param descriptor - The query descriptor
|
|
55
|
-
* @param fields - The field descriptors from the collection schema
|
|
56
|
-
* @returns A parameterized SQL query
|
|
57
|
-
*/
|
|
58
|
-
declare function buildSelectQuery(descriptor: QueryDescriptor, fields: Record<string, FieldDescriptor>): SqlQuery;
|
|
59
|
-
/**
|
|
60
|
-
* Build a COUNT query from a QueryDescriptor.
|
|
61
|
-
* Automatically adds `WHERE _deleted = 0`.
|
|
62
|
-
*
|
|
63
|
-
* @param descriptor - The query descriptor
|
|
64
|
-
* @param fields - The field descriptors from the collection schema
|
|
65
|
-
* @returns A parameterized SQL query that returns { count: number }
|
|
66
|
-
*/
|
|
67
|
-
declare function buildCountQuery(descriptor: QueryDescriptor, fields: Record<string, FieldDescriptor>): SqlQuery;
|
|
68
|
-
/**
|
|
69
|
-
* Build an INSERT query for a collection record.
|
|
70
|
-
*
|
|
71
|
-
* @param collection - The collection name
|
|
72
|
-
* @param record - The record data (already serialized with id, _created_at, _updated_at)
|
|
73
|
-
* @returns A parameterized SQL query
|
|
74
|
-
*/
|
|
75
|
-
declare function buildInsertQuery(collection: string, record: Record<string, unknown>): SqlQuery;
|
|
76
|
-
/**
|
|
77
|
-
* Build an UPDATE query for a collection record.
|
|
78
|
-
*
|
|
79
|
-
* @param collection - The collection name
|
|
80
|
-
* @param id - The record ID
|
|
81
|
-
* @param changes - The fields to update (already serialized)
|
|
82
|
-
* @returns A parameterized SQL query
|
|
83
|
-
*/
|
|
84
|
-
declare function buildUpdateQuery(collection: string, id: string, changes: Record<string, unknown>): SqlQuery;
|
|
85
|
-
/**
|
|
86
|
-
* Build a soft-delete query (SET _deleted = 1).
|
|
87
|
-
*
|
|
88
|
-
* @param collection - The collection name
|
|
89
|
-
* @param id - The record ID
|
|
90
|
-
* @param updatedAt - The timestamp to set on _updated_at
|
|
91
|
-
* @returns A parameterized SQL query
|
|
92
|
-
*/
|
|
93
|
-
declare function buildSoftDeleteQuery(collection: string, id: string, updatedAt: number, version?: string): SqlQuery;
|
|
94
|
-
/**
|
|
95
|
-
* Build an UPDATE that applies only when the row is missing or older than `remoteVersion`
|
|
96
|
-
* (serialized HLC). Prevents stale remote sync from overwriting newer local materialized state.
|
|
97
|
-
*/
|
|
98
|
-
declare function buildLwwUpdateQuery(collection: string, id: string, changes: Record<string, unknown>, remoteVersion: string): SqlQuery;
|
|
99
|
-
/**
|
|
100
|
-
* Build a soft-delete that applies only when the row is older than `remoteVersion`.
|
|
101
|
-
*/
|
|
102
|
-
declare function buildLwwSoftDeleteQuery(collection: string, id: string, updatedAt: number, version: string): SqlQuery;
|
|
103
|
-
/**
|
|
104
|
-
* Build a WHERE clause from conditions, validating field names against the schema.
|
|
105
|
-
*
|
|
106
|
-
* @param where - The where conditions
|
|
107
|
-
* @param fields - The field descriptors from the collection schema
|
|
108
|
-
* @returns The SQL WHERE clause string and params, or null if no conditions
|
|
109
|
-
*/
|
|
110
|
-
declare function buildWhereClause(where: Record<string, unknown>, fields: Record<string, FieldDescriptor>): SqlQuery | null;
|
|
111
|
-
|
|
112
44
|
/**
|
|
113
45
|
* Serialize an HLC timestamp for storage in the `_version` column.
|
|
114
46
|
* Lexicographic order matches {@link HybridLogicalClock.compare}.
|
|
@@ -125,30 +57,28 @@ declare function rowVersionFromRecord(row: Pick<RawCollectionRow, '_updated_at'
|
|
|
125
57
|
declare function isIncomingNewerThanRow(incoming: HLCTimestamp, row: Pick<RawCollectionRow, '_updated_at' | '_version'>): boolean;
|
|
126
58
|
|
|
127
59
|
/**
|
|
128
|
-
* Insert a record and persist
|
|
60
|
+
* Insert a record and persist its operation, atomically, through the single
|
|
61
|
+
* local write path (sequence reserved and operation built inside the write
|
|
62
|
+
* transaction).
|
|
129
63
|
*/
|
|
130
64
|
declare function executeInsert(ctx: LocalMutationContext, data: Record<string, unknown>): Promise<CollectionRecord>;
|
|
131
65
|
|
|
132
66
|
/**
|
|
133
|
-
* Update a record and persist
|
|
67
|
+
* Update a record and persist its operation, atomically. The row read, the
|
|
68
|
+
* state-machine check and atomic-op resolution all happen inside the write
|
|
69
|
+
* transaction, so concurrent increments compose (STORE-9).
|
|
134
70
|
*/
|
|
135
71
|
declare function executeUpdate(ctx: LocalMutationContext, id: string, data: Record<string, unknown>): Promise<CollectionRecord>;
|
|
136
72
|
|
|
137
73
|
interface ExecuteDeleteOptions {
|
|
138
|
-
/** When true, skip
|
|
74
|
+
/** When true, skip referential checks and cascades (caller already enforced them). */
|
|
139
75
|
skipReferentialEnforcement?: boolean;
|
|
140
|
-
/** Pre-built delete operation (avoids duplicate sequence allocation). */
|
|
141
|
-
operation?: Operation;
|
|
142
76
|
}
|
|
143
77
|
/**
|
|
144
|
-
* Soft-delete a record and persist the operation log entry atomically
|
|
145
|
-
*
|
|
78
|
+
* Soft-delete a record and persist the operation log entry atomically, together
|
|
79
|
+
* with any referential side effects (cascades / set-null), in one transaction.
|
|
80
|
+
* Returns the primary delete operation followed by the side-effect operations.
|
|
146
81
|
*/
|
|
147
82
|
declare function executeDelete(ctx: LocalMutationContext, id: string, options?: ExecuteDeleteOptions): Promise<Operation[]>;
|
|
148
83
|
|
|
149
|
-
|
|
150
|
-
* Merge explicit parent deps (e.g. referential cascade) with tracker-derived deps.
|
|
151
|
-
*/
|
|
152
|
-
declare function resolveCausalDeps(ctx: LocalMutationContext): string[];
|
|
153
|
-
|
|
154
|
-
export { type ExecuteDeleteOptions, LocalMutationContext, OperationRow, RawCollectionRow, type SqlQuery, buildCountQuery, buildInsertQuery, buildLwwSoftDeleteQuery, buildLwwUpdateQuery, buildSelectQuery, buildSoftDeleteQuery, buildUpdateQuery, buildWhereClause, deserializeOperation, deserializeOperationWithCollection, deserializeRecord, executeDelete, executeInsert, executeUpdate, isIncomingNewerThanRow, resolveCausalDeps, rowVersionFromRecord, serializeOperation, serializeRecord, serializeRowVersion };
|
|
84
|
+
export { type ExecuteDeleteOptions, LocalMutationContext, OperationRow, RawCollectionRow, deserializeOperation, deserializeOperationWithCollection, deserializeRecord, executeDelete, executeInsert, executeUpdate, isIncomingNewerThanRow, rowVersionFromRecord, serializeOperation, serializeRecord, serializeRowVersion };
|
package/dist/internal.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
executeDelete,
|
|
3
|
+
executeInsert,
|
|
4
|
+
executeUpdate
|
|
5
|
+
} from "./chunk-HZTDBLDB.js";
|
|
1
6
|
import {
|
|
2
7
|
buildCountQuery,
|
|
3
8
|
buildInsertQuery,
|
|
@@ -7,22 +12,19 @@ import {
|
|
|
7
12
|
buildSoftDeleteQuery,
|
|
8
13
|
buildUpdateQuery,
|
|
9
14
|
buildWhereClause,
|
|
10
|
-
|
|
11
|
-
executeInsert,
|
|
12
|
-
executeUpdate,
|
|
15
|
+
hasUnsyncedOwnOperations,
|
|
13
16
|
isIncomingNewerThanRow,
|
|
14
|
-
resolveCausalDeps,
|
|
15
17
|
rowVersionFromRecord,
|
|
16
18
|
serializeRowVersion
|
|
17
|
-
} from "./chunk-
|
|
18
|
-
import "./chunk-TUR7RGEP.js";
|
|
19
|
+
} from "./chunk-SJP2HTYZ.js";
|
|
19
20
|
import {
|
|
20
21
|
deserializeOperation,
|
|
21
22
|
deserializeOperationWithCollection,
|
|
22
23
|
deserializeRecord,
|
|
23
24
|
serializeOperation,
|
|
24
25
|
serializeRecord
|
|
25
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-J5AGXF3D.js";
|
|
27
|
+
import "./chunk-DUKTWVZW.js";
|
|
26
28
|
export {
|
|
27
29
|
buildCountQuery,
|
|
28
30
|
buildInsertQuery,
|
|
@@ -38,8 +40,8 @@ export {
|
|
|
38
40
|
executeDelete,
|
|
39
41
|
executeInsert,
|
|
40
42
|
executeUpdate,
|
|
43
|
+
hasUnsyncedOwnOperations,
|
|
41
44
|
isIncomingNewerThanRow,
|
|
42
|
-
resolveCausalDeps,
|
|
43
45
|
rowVersionFromRecord,
|
|
44
46
|
serializeOperation,
|
|
45
47
|
serializeRecord,
|
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COMPACTED_THROUGH_TABLE,
|
|
3
|
+
FOLD_BASE_TABLE,
|
|
4
|
+
FOLD_SNAPSHOT_TABLE,
|
|
5
|
+
FOLD_STATE_TABLE,
|
|
6
|
+
PROVISIONAL_OPS_TABLE,
|
|
7
|
+
SEQ_CONFLICTS_TABLE,
|
|
8
|
+
insertConflictRow
|
|
9
|
+
} from "./chunk-32EDKWMI.js";
|
|
10
|
+
import {
|
|
11
|
+
DELIVERY_WATERMARK_META_KEY,
|
|
12
|
+
DELTA_CURSOR_META_KEY,
|
|
13
|
+
TERMINAL_REJECTIONS_TABLE,
|
|
14
|
+
ensureLocalSyncRecordTables,
|
|
15
|
+
loadLastAckedServerVector,
|
|
16
|
+
loadOwnAckedThrough
|
|
17
|
+
} from "./chunk-SJP2HTYZ.js";
|
|
18
|
+
import {
|
|
19
|
+
deserializeOperationWithCollection,
|
|
20
|
+
serializeOperation
|
|
21
|
+
} from "./chunk-J5AGXF3D.js";
|
|
22
|
+
import "./chunk-DUKTWVZW.js";
|
|
23
|
+
|
|
24
|
+
// src/backup/restore.ts
|
|
25
|
+
import { HybridLogicalClock, quoteIdent } from "@korajs/core";
|
|
26
|
+
async function restoreMerge(host, backup, filtered) {
|
|
27
|
+
await importTerminalRejections(host.adapter, backup);
|
|
28
|
+
let restored = 0;
|
|
29
|
+
let skipped = 0;
|
|
30
|
+
const authority = (nodeId) => host.isServerAuthority?.(nodeId) ?? false;
|
|
31
|
+
for (const operation of backup.operations) {
|
|
32
|
+
if (!host.schema.collections[operation.collection]) continue;
|
|
33
|
+
if (authority(operation.nodeId)) {
|
|
34
|
+
skipped++;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (await host.applyOperation(operation) === "applied") restored++;
|
|
38
|
+
}
|
|
39
|
+
if (!filtered && backup.versionVector.size > 0) {
|
|
40
|
+
await host.adapter.transaction(async (tx) => {
|
|
41
|
+
for (const [nodeId, sequence] of backup.versionVector) {
|
|
42
|
+
if (authority(nodeId)) continue;
|
|
43
|
+
await maxVector(tx, nodeId, sequence);
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return {
|
|
48
|
+
operationsRestored: restored,
|
|
49
|
+
recordsRestored: 0,
|
|
50
|
+
unsyncedWritesKept: 0,
|
|
51
|
+
serverOperationsSkipped: skipped
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
async function restoreReplace(host, backup, options) {
|
|
55
|
+
const { adapter, schema } = host;
|
|
56
|
+
const targets = Object.keys(schema.collections).filter(
|
|
57
|
+
(name) => !options.collections || options.collections.includes(name)
|
|
58
|
+
);
|
|
59
|
+
const rawRecords = backup.manifest.includesRecords;
|
|
60
|
+
const localNodes = await host.listLocalNodes();
|
|
61
|
+
const localIds = new Set(localNodes.map((node) => node.nodeId));
|
|
62
|
+
const acked = await loadAckedThrough(adapter, localNodes);
|
|
63
|
+
const kept = [];
|
|
64
|
+
const dropped = /* @__PURE__ */ new Set();
|
|
65
|
+
for (const node of localNodes) {
|
|
66
|
+
const keep = node.accepted || options.keepUnsyncedWrites;
|
|
67
|
+
const floor = acked.get(node.nodeId) ?? 0;
|
|
68
|
+
for (const collection of targets) {
|
|
69
|
+
const rows = await adapter.query(
|
|
70
|
+
`SELECT * FROM ${quoteIdent(`_kora_ops_${collection}`)} WHERE node_id = ? AND sequence_number > ?`,
|
|
71
|
+
[node.nodeId, floor]
|
|
72
|
+
);
|
|
73
|
+
for (const row of rows) {
|
|
74
|
+
if (keep) kept.push(deserializeOperationWithCollection(row, collection));
|
|
75
|
+
else dropped.add(node.nodeId);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
kept.sort((a, b) => HybridLogicalClock.compare(a.timestamp, b.timestamp));
|
|
80
|
+
const restartable = new Set(
|
|
81
|
+
localNodes.filter((node) => !node.accepted && !options.keepUnsyncedWrites).map((node) => node.nodeId)
|
|
82
|
+
);
|
|
83
|
+
await ensureLocalSyncRecordTables(adapter);
|
|
84
|
+
let recordsRestored = 0;
|
|
85
|
+
const hasConflicts = await tableExists(adapter, SEQ_CONFLICTS_TABLE);
|
|
86
|
+
await adapter.transaction(async (tx) => {
|
|
87
|
+
for (const collection of targets) {
|
|
88
|
+
await tx.execute(`DELETE FROM ${quoteIdent(`_kora_ops_${collection}`)}`);
|
|
89
|
+
await tx.execute(`DELETE FROM ${quoteIdent(collection)}`);
|
|
90
|
+
await tx.execute("DELETE FROM _kora_scope_retractions WHERE collection = ?", [collection]);
|
|
91
|
+
await tx.execute(`DELETE FROM ${FOLD_STATE_TABLE} WHERE collection = ?`, [collection]);
|
|
92
|
+
await tx.execute(`DELETE FROM ${FOLD_BASE_TABLE} WHERE collection = ?`, [collection]);
|
|
93
|
+
await tx.execute(`DELETE FROM ${FOLD_SNAPSHOT_TABLE} WHERE collection = ?`, [collection]);
|
|
94
|
+
await tx.execute(`DELETE FROM ${PROVISIONAL_OPS_TABLE} WHERE collection = ?`, [collection]);
|
|
95
|
+
if (hasConflicts) {
|
|
96
|
+
await tx.execute(
|
|
97
|
+
`DELETE FROM ${SEQ_CONFLICTS_TABLE} WHERE collection = ? AND reemitted_as IS NULL`,
|
|
98
|
+
[collection]
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
const current = await tx.query(
|
|
103
|
+
"SELECT node_id, sequence_number FROM _kora_version_vector"
|
|
104
|
+
);
|
|
105
|
+
if (!options.collections) {
|
|
106
|
+
for (const row of current) {
|
|
107
|
+
if (!localIds.has(row.node_id)) {
|
|
108
|
+
await tx.execute("DELETE FROM _kora_version_vector WHERE node_id = ?", [row.node_id]);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const [nodeId, sequence] of backup.versionVector) {
|
|
113
|
+
if (restartable.has(nodeId)) continue;
|
|
114
|
+
await maxVector(tx, nodeId, sequence);
|
|
115
|
+
}
|
|
116
|
+
await tx.execute("UPDATE _kora_meta SET value = ? WHERE key = ? OR key LIKE ?", [
|
|
117
|
+
"0",
|
|
118
|
+
DELIVERY_WATERMARK_META_KEY,
|
|
119
|
+
`${DELIVERY_WATERMARK_META_KEY}:%`
|
|
120
|
+
]);
|
|
121
|
+
await tx.execute("DELETE FROM _kora_meta WHERE key = ?", [DELTA_CURSOR_META_KEY]);
|
|
122
|
+
for (const entry of backup.terminalRejections) {
|
|
123
|
+
await tx.execute(
|
|
124
|
+
`INSERT OR IGNORE INTO ${TERMINAL_REJECTIONS_TABLE} (operation_id, node_id, sequence_number, code, rejected_at) VALUES (?, ?, ?, ?, ?)`,
|
|
125
|
+
[entry.operationId, entry.nodeId, entry.sequenceNumber, entry.code, entry.rejectedAt]
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
for (const row of backup.foldBases) {
|
|
129
|
+
if (!targets.includes(row.collection)) continue;
|
|
130
|
+
await tx.execute(
|
|
131
|
+
`INSERT OR REPLACE INTO ${FOLD_BASE_TABLE} (collection, record_id, state) VALUES (?, ?, ?)`,
|
|
132
|
+
[row.collection, row.recordId, row.state]
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
for (const row of backup.foldSnapshots) {
|
|
136
|
+
if (!targets.includes(row.collection)) continue;
|
|
137
|
+
await tx.execute(
|
|
138
|
+
`INSERT OR REPLACE INTO ${FOLD_SNAPSHOT_TABLE} (collection, record_id, state) VALUES (?, ?, ?)`,
|
|
139
|
+
[row.collection, row.recordId, row.state]
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
if (!options.collections) {
|
|
143
|
+
await tx.execute(`DELETE FROM ${COMPACTED_THROUGH_TABLE}`);
|
|
144
|
+
for (const [nodeId, sequence] of backup.compactedThrough) {
|
|
145
|
+
await tx.execute(
|
|
146
|
+
`INSERT INTO ${COMPACTED_THROUGH_TABLE} (node_id, sequence_number) VALUES (?, ?)`,
|
|
147
|
+
[nodeId, sequence]
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (rawRecords) {
|
|
152
|
+
for (const operation of backup.operations) {
|
|
153
|
+
if (!targets.includes(operation.collection)) continue;
|
|
154
|
+
await insertOperationRow(tx, operation);
|
|
155
|
+
}
|
|
156
|
+
for (const collection of targets) {
|
|
157
|
+
const rows = backup.records.get(collection) ?? [];
|
|
158
|
+
if (rows.length === 0) continue;
|
|
159
|
+
const columns = new Set(
|
|
160
|
+
(await tx.query(`PRAGMA table_info(${quoteIdent(collection)})`)).map(
|
|
161
|
+
(column) => column.name
|
|
162
|
+
)
|
|
163
|
+
);
|
|
164
|
+
for (const row of rows) {
|
|
165
|
+
const keys = Object.keys(row).filter((key) => columns.has(key));
|
|
166
|
+
await tx.execute(
|
|
167
|
+
`INSERT OR REPLACE INTO ${quoteIdent(collection)} (${keys.map((k) => quoteIdent(k)).join(", ")}) VALUES (${keys.map(() => "?").join(", ")})`,
|
|
168
|
+
keys.map((key) => toSqlValue(row[key]))
|
|
169
|
+
);
|
|
170
|
+
recordsRestored++;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
let restored = rawRecords ? backup.operations.filter((operation) => targets.includes(operation.collection)).length : 0;
|
|
176
|
+
if (!rawRecords) {
|
|
177
|
+
for (const operation of backup.operations) {
|
|
178
|
+
if (!targets.includes(operation.collection)) continue;
|
|
179
|
+
if (await host.applyOperation(operation) === "applied") restored++;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
let unsyncedWritesKept = 0;
|
|
183
|
+
for (const operation of kept) {
|
|
184
|
+
if (await host.applyOperation(operation) === "applied") unsyncedWritesKept++;
|
|
185
|
+
}
|
|
186
|
+
await adapter.transaction(async (tx) => {
|
|
187
|
+
for (const nodeId of restartable) {
|
|
188
|
+
const highest = await highestSequence(tx, targets, nodeId);
|
|
189
|
+
await tx.execute("DELETE FROM _kora_version_vector WHERE node_id = ?", [nodeId]);
|
|
190
|
+
if (highest > 0) {
|
|
191
|
+
await tx.execute(
|
|
192
|
+
"INSERT INTO _kora_version_vector (node_id, sequence_number) VALUES (?, ?)",
|
|
193
|
+
[nodeId, highest]
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (dropped.size > 0 || !options.keepUnsyncedWrites) {
|
|
198
|
+
const union = targets.map((c) => `SELECT id FROM ${quoteIdent(`_kora_ops_${c}`)}`).join(" UNION ");
|
|
199
|
+
if (union) {
|
|
200
|
+
await tx.execute(`DELETE FROM _kora_sync_queue WHERE id NOT IN (${union})`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
return { operationsRestored: restored, recordsRestored, unsyncedWritesKept };
|
|
205
|
+
}
|
|
206
|
+
async function importTerminalRejections(adapter, backup) {
|
|
207
|
+
if (backup.terminalRejections.length === 0) return;
|
|
208
|
+
await ensureLocalSyncRecordTables(adapter);
|
|
209
|
+
await adapter.transaction(async (tx) => {
|
|
210
|
+
for (const entry of backup.terminalRejections) {
|
|
211
|
+
await tx.execute(
|
|
212
|
+
`INSERT OR IGNORE INTO ${TERMINAL_REJECTIONS_TABLE} (operation_id, node_id, sequence_number, code, rejected_at) VALUES (?, ?, ?, ?, ?)`,
|
|
213
|
+
[entry.operationId, entry.nodeId, entry.sequenceNumber, entry.code, entry.rejectedAt]
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
async function insertOperationRow(tx, operation) {
|
|
219
|
+
const table = quoteIdent(`_kora_ops_${operation.collection}`);
|
|
220
|
+
const row = serializeOperation(operation);
|
|
221
|
+
const existing = await tx.query(`SELECT id FROM ${table} WHERE id = ?`, [row.id]);
|
|
222
|
+
if (existing.length > 0) return;
|
|
223
|
+
const holder = await tx.query(
|
|
224
|
+
`SELECT id FROM ${table} WHERE node_id = ? AND sequence_number = ?`,
|
|
225
|
+
[row.node_id, row.sequence_number]
|
|
226
|
+
);
|
|
227
|
+
if (holder.length > 0) {
|
|
228
|
+
await insertConflictRow(
|
|
229
|
+
tx,
|
|
230
|
+
operation.collection,
|
|
231
|
+
row,
|
|
232
|
+
"backup-sequence-conflict",
|
|
233
|
+
null,
|
|
234
|
+
null,
|
|
235
|
+
Date.now()
|
|
236
|
+
);
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
await tx.execute(
|
|
240
|
+
`INSERT INTO ${table} (id, node_id, type, record_id, data, previous_data, timestamp, sequence_number, causal_deps, schema_version) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
241
|
+
[
|
|
242
|
+
row.id,
|
|
243
|
+
row.node_id,
|
|
244
|
+
row.type,
|
|
245
|
+
row.record_id,
|
|
246
|
+
row.data,
|
|
247
|
+
row.previous_data,
|
|
248
|
+
row.timestamp,
|
|
249
|
+
row.sequence_number,
|
|
250
|
+
row.causal_deps,
|
|
251
|
+
row.schema_version
|
|
252
|
+
]
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
async function maxVector(tx, nodeId, sequence) {
|
|
256
|
+
await tx.execute(
|
|
257
|
+
`INSERT INTO _kora_version_vector (node_id, sequence_number) VALUES (?, ?)
|
|
258
|
+
ON CONFLICT(node_id) DO UPDATE SET sequence_number = MAX(sequence_number, excluded.sequence_number)`,
|
|
259
|
+
[nodeId, sequence]
|
|
260
|
+
);
|
|
261
|
+
}
|
|
262
|
+
async function highestSequence(tx, collections, nodeId) {
|
|
263
|
+
let highest = 0;
|
|
264
|
+
for (const collection of collections) {
|
|
265
|
+
const rows = await tx.query(
|
|
266
|
+
`SELECT MAX(sequence_number) AS m FROM ${quoteIdent(`_kora_ops_${collection}`)} WHERE node_id = ?`,
|
|
267
|
+
[nodeId]
|
|
268
|
+
);
|
|
269
|
+
highest = Math.max(highest, rows[0]?.m ?? 0);
|
|
270
|
+
}
|
|
271
|
+
return highest;
|
|
272
|
+
}
|
|
273
|
+
async function loadAckedThrough(adapter, nodes) {
|
|
274
|
+
const lastAcked = await loadLastAckedServerVector(adapter);
|
|
275
|
+
const result = /* @__PURE__ */ new Map();
|
|
276
|
+
for (const node of nodes) {
|
|
277
|
+
const prefix = await loadOwnAckedThrough(adapter, node.nodeId);
|
|
278
|
+
result.set(node.nodeId, prefix ?? lastAcked.get(node.nodeId) ?? 0);
|
|
279
|
+
}
|
|
280
|
+
return result;
|
|
281
|
+
}
|
|
282
|
+
async function tableExists(adapter, name) {
|
|
283
|
+
const rows = await adapter.query(
|
|
284
|
+
"SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?",
|
|
285
|
+
[name]
|
|
286
|
+
);
|
|
287
|
+
return rows.length > 0;
|
|
288
|
+
}
|
|
289
|
+
function toSqlValue(value) {
|
|
290
|
+
if (typeof value === "boolean") return value ? 1 : 0;
|
|
291
|
+
if (value instanceof Uint8Array) return value;
|
|
292
|
+
if (value !== null && typeof value === "object") return JSON.stringify(value);
|
|
293
|
+
return value;
|
|
294
|
+
}
|
|
295
|
+
export {
|
|
296
|
+
restoreMerge,
|
|
297
|
+
restoreReplace
|
|
298
|
+
};
|
|
299
|
+
//# sourceMappingURL=restore-FR7XIKT2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/backup/restore.ts"],"sourcesContent":["import { HybridLogicalClock, quoteIdent } from '@korajs/core'\nimport type { Operation, SchemaDefinition } from '@korajs/core'\nimport {\n\tCOMPACTED_THROUGH_TABLE,\n\tFOLD_BASE_TABLE,\n\tFOLD_SNAPSHOT_TABLE,\n\tFOLD_STATE_TABLE,\n\tPROVISIONAL_OPS_TABLE,\n} from '../fold/record-folder'\nimport { deserializeOperationWithCollection, serializeOperation } from '../serialization/serializer'\nimport { SEQ_CONFLICTS_TABLE, insertConflictRow } from '../store/sequence-repair'\nimport {\n\ttype LocalNodeRecord,\n\tTERMINAL_REJECTIONS_TABLE,\n\tensureLocalSyncRecordTables,\n} from '../sync/local-sync-records'\nimport { loadOwnAckedThrough } from '../sync/sync-durability'\nimport {\n\tDELIVERY_WATERMARK_META_KEY,\n\tDELTA_CURSOR_META_KEY,\n\tloadLastAckedServerVector,\n} from '../sync/sync-state'\nimport type { ApplyResult, OperationRow, StorageAdapter, Transaction } from '../types'\nimport type { ParsedBackup } from './backup'\n\n/** What a restore needs from the store it restores into. */\nexport interface RestoreHost {\n\treadonly adapter: StorageAdapter\n\treadonly schema: SchemaDefinition\n\t/** The normal remote-apply path (sync's): dedup by id, per-field merge, version vector. */\n\tapplyOperation(operation: Operation): Promise<ApplyResult>\n\t/** This database's own nodes (RT-38/40), never taken from a backup. */\n\tlistLocalNodes(): Promise<LocalNodeRecord[]>\n\t/**\n\t * Whether a node's writes would be authoritative here (Kora's reserved `kora:`\n\t * nodes, and the explicit server authorities this device learned). A merge import\n\t * never applies their operations: only the sync server delivers them.\n\t */\n\tisServerAuthority?(nodeId: string): boolean\n}\n\n/** Outcome counts of a restore. */\nexport interface RestoreCounts {\n\toperationsRestored: number\n\trecordsRestored: number\n\tunsyncedWritesKept: number\n\t/** Merge mode: operations of server-authority nodes left out (see `RestoreHost.isServerAuthority`). */\n\tserverOperationsSkipped?: number\n}\n\n/**\n * Merge a backup into the database (STORE-5): every operation goes through the normal\n * remote-apply path in HLC (causal) order, the version vector advances by MAX, the\n * terminal-rejection markers are added. Never imported: the node id, node tokens, local\n * node registry and principal bindings, sync watermarks, acknowledged prefixes.\n */\nexport async function restoreMerge(\n\thost: RestoreHost,\n\tbackup: ParsedBackup,\n\tfiltered: boolean,\n): Promise<RestoreCounts> {\n\tawait importTerminalRejections(host.adapter, backup)\n\tlet restored = 0\n\tlet skipped = 0\n\tconst authority = (nodeId: string): boolean => host.isServerAuthority?.(nodeId) ?? false\n\tfor (const operation of backup.operations) {\n\t\tif (!host.schema.collections[operation.collection]) continue\n\t\t// A server decision is applied only as the sync server delivers it: from a file,\n\t\t// an operation under a server node would win `merge('server-authoritative')`\n\t\t// fields on this device (and is never uploaded to be checked). One the server\n\t\t// already delivered is in the log anyway; the rest arrive by sync.\n\t\tif (authority(operation.nodeId)) {\n\t\t\tskipped++\n\t\t\tcontinue\n\t\t}\n\t\tif ((await host.applyOperation(operation)) === 'applied') restored++\n\t}\n\tif (!filtered && backup.versionVector.size > 0) {\n\t\t// With a collection filter the vector would claim other collections' operations.\n\t\tawait host.adapter.transaction(async (tx) => {\n\t\t\tfor (const [nodeId, sequence] of backup.versionVector) {\n\t\t\t\tif (authority(nodeId)) continue\n\t\t\t\tawait maxVector(tx, nodeId, sequence)\n\t\t\t}\n\t\t})\n\t}\n\treturn {\n\t\toperationsRestored: restored,\n\t\trecordsRestored: 0,\n\t\tunsyncedWritesKept: 0,\n\t\tserverOperationsSkipped: skipped,\n\t}\n}\n\n/**\n * Replace the database's data with a backup's (STORE-5), keeping this device's\n * identity:\n *\n * - The node id, node tokens, local node registry, principal bindings and acknowledged\n * prefixes are untouched; the exporting device's are never imported.\n * - This database's own nodes never move their sequence counters backwards (numbers the\n * server may hold are never reused). A node the server never accepted, whose unsynced\n * writes are not kept, restarts after its highest restored number (its numbers never\n * left the device), so its log stays contiguous.\n * - Unacknowledged writes of an accepted node, and with `keepUnsyncedWrites` every\n * unsynced own write, are re-applied on top of the restored data.\n * - Delivery watermarks restart at 0 and the delta cursor is dropped, so the sync server\n * re-sends everything in scope (deduplicated by id).\n * - Rows, operations, version vector and sync bookkeeping change in one transaction;\n * re-applied writes (and a backup without records, materialized through the\n * remote-apply path) follow it.\n */\nexport async function restoreReplace(\n\thost: RestoreHost,\n\tbackup: ParsedBackup,\n\toptions: { collections: string[] | null; keepUnsyncedWrites: boolean },\n): Promise<RestoreCounts> {\n\tconst { adapter, schema } = host\n\tconst targets = Object.keys(schema.collections).filter(\n\t\t(name) => !options.collections || options.collections.includes(name),\n\t)\n\tconst rawRecords = backup.manifest.includesRecords\n\tconst localNodes = await host.listLocalNodes()\n\tconst localIds = new Set(localNodes.map((node) => node.nodeId))\n\tconst acked = await loadAckedThrough(adapter, localNodes)\n\n\t// Writes that exist only on this device: re-applied after the replace.\n\tconst kept: Operation[] = []\n\tconst dropped = new Set<string>()\n\tfor (const node of localNodes) {\n\t\tconst keep = node.accepted || options.keepUnsyncedWrites\n\t\tconst floor = acked.get(node.nodeId) ?? 0\n\t\tfor (const collection of targets) {\n\t\t\tconst rows = await adapter.query<OperationRow>(\n\t\t\t\t`SELECT * FROM ${quoteIdent(`_kora_ops_${collection}`)} WHERE node_id = ? AND sequence_number > ?`,\n\t\t\t\t[node.nodeId, floor],\n\t\t\t)\n\t\t\tfor (const row of rows) {\n\t\t\t\tif (keep) kept.push(deserializeOperationWithCollection(row, collection))\n\t\t\t\telse dropped.add(node.nodeId)\n\t\t\t}\n\t\t}\n\t}\n\tkept.sort((a, b) => HybridLogicalClock.compare(a.timestamp, b.timestamp))\n\t// Counters that may restart: nodes the server never accepted whose writes are dropped.\n\tconst restartable = new Set(\n\t\tlocalNodes\n\t\t\t.filter((node) => !node.accepted && !options.keepUnsyncedWrites)\n\t\t\t.map((node) => node.nodeId),\n\t)\n\n\tawait ensureLocalSyncRecordTables(adapter)\n\tlet recordsRestored = 0\n\tconst hasConflicts = await tableExists(adapter, SEQ_CONFLICTS_TABLE)\n\tawait adapter.transaction(async (tx) => {\n\t\tfor (const collection of targets) {\n\t\t\tawait tx.execute(`DELETE FROM ${quoteIdent(`_kora_ops_${collection}`)}`)\n\t\t\tawait tx.execute(`DELETE FROM ${quoteIdent(collection)}`)\n\t\t\tawait tx.execute('DELETE FROM _kora_scope_retractions WHERE collection = ?', [collection])\n\t\t\t// W7: the records' fold states go with their rows and log; the store\n\t\t\t// re-materializes the restored collections afterwards.\n\t\t\tawait tx.execute(`DELETE FROM ${FOLD_STATE_TABLE} WHERE collection = ?`, [collection])\n\t\t\tawait tx.execute(`DELETE FROM ${FOLD_BASE_TABLE} WHERE collection = ?`, [collection])\n\t\t\tawait tx.execute(`DELETE FROM ${FOLD_SNAPSHOT_TABLE} WHERE collection = ?`, [collection])\n\t\t\tawait tx.execute(`DELETE FROM ${PROVISIONAL_OPS_TABLE} WHERE collection = ?`, [collection])\n\t\t\tif (hasConflicts) {\n\t\t\t\tawait tx.execute(\n\t\t\t\t\t`DELETE FROM ${SEQ_CONFLICTS_TABLE} WHERE collection = ? AND reemitted_as IS NULL`,\n\t\t\t\t\t[collection],\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// Version vector. Without a collection filter, remote nodes take the backup's\n\t\t// values; this database's own nodes never go backwards. With a filter, MAX only.\n\t\tconst current = await tx.query<{ node_id: string; sequence_number: number }>(\n\t\t\t'SELECT node_id, sequence_number FROM _kora_version_vector',\n\t\t)\n\t\tif (!options.collections) {\n\t\t\tfor (const row of current) {\n\t\t\t\tif (!localIds.has(row.node_id)) {\n\t\t\t\t\tawait tx.execute('DELETE FROM _kora_version_vector WHERE node_id = ?', [row.node_id])\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor (const [nodeId, sequence] of backup.versionVector) {\n\t\t\tif (restartable.has(nodeId)) continue\n\t\t\tawait maxVector(tx, nodeId, sequence)\n\t\t}\n\n\t\t// The server must re-send everything in scope: this database no longer holds what\n\t\t// it delivered before.\n\t\tawait tx.execute('UPDATE _kora_meta SET value = ? WHERE key = ? OR key LIKE ?', [\n\t\t\t'0',\n\t\t\tDELIVERY_WATERMARK_META_KEY,\n\t\t\t`${DELIVERY_WATERMARK_META_KEY}:%`,\n\t\t])\n\t\tawait tx.execute('DELETE FROM _kora_meta WHERE key = ?', [DELTA_CURSOR_META_KEY])\n\n\t\tfor (const entry of backup.terminalRejections) {\n\t\t\tawait tx.execute(\n\t\t\t\t`INSERT OR IGNORE INTO ${TERMINAL_REJECTIONS_TABLE} (operation_id, node_id, sequence_number, code, rejected_at) VALUES (?, ?, ?, ?, ?)`,\n\t\t\t\t[entry.operationId, entry.nodeId, entry.sequenceNumber, entry.code, entry.rejectedAt],\n\t\t\t)\n\t\t}\n\n\t\t// W7 (RT-66): the exporting device's compacted history (base states), its row\n\t\t// snapshots and compacted prefixes, so records rebuild from base + log exactly.\n\t\tfor (const row of backup.foldBases) {\n\t\t\tif (!targets.includes(row.collection)) continue\n\t\t\tawait tx.execute(\n\t\t\t\t`INSERT OR REPLACE INTO ${FOLD_BASE_TABLE} (collection, record_id, state) VALUES (?, ?, ?)`,\n\t\t\t\t[row.collection, row.recordId, row.state],\n\t\t\t)\n\t\t}\n\t\tfor (const row of backup.foldSnapshots) {\n\t\t\tif (!targets.includes(row.collection)) continue\n\t\t\tawait tx.execute(\n\t\t\t\t`INSERT OR REPLACE INTO ${FOLD_SNAPSHOT_TABLE} (collection, record_id, state) VALUES (?, ?, ?)`,\n\t\t\t\t[row.collection, row.recordId, row.state],\n\t\t\t)\n\t\t}\n\t\tif (!options.collections) {\n\t\t\t// Per node and global; a collection filter would claim other collections' ops.\n\t\t\tawait tx.execute(`DELETE FROM ${COMPACTED_THROUGH_TABLE}`)\n\t\t\tfor (const [nodeId, sequence] of backup.compactedThrough) {\n\t\t\t\tawait tx.execute(\n\t\t\t\t\t`INSERT INTO ${COMPACTED_THROUGH_TABLE} (node_id, sequence_number) VALUES (?, ?)`,\n\t\t\t\t\t[nodeId, sequence],\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tif (rawRecords) {\n\t\t\tfor (const operation of backup.operations) {\n\t\t\t\tif (!targets.includes(operation.collection)) continue\n\t\t\t\tawait insertOperationRow(tx, operation)\n\t\t\t}\n\t\t\tfor (const collection of targets) {\n\t\t\t\tconst rows = backup.records.get(collection) ?? []\n\t\t\t\tif (rows.length === 0) continue\n\t\t\t\tconst columns = new Set(\n\t\t\t\t\t(await tx.query<{ name: string }>(`PRAGMA table_info(${quoteIdent(collection)})`)).map(\n\t\t\t\t\t\t(column) => column.name,\n\t\t\t\t\t),\n\t\t\t\t)\n\t\t\t\tfor (const row of rows) {\n\t\t\t\t\tconst keys = Object.keys(row).filter((key) => columns.has(key))\n\t\t\t\t\tawait tx.execute(\n\t\t\t\t\t\t`INSERT OR REPLACE INTO ${quoteIdent(collection)} (${keys.map((k) => quoteIdent(k)).join(', ')}) VALUES (${keys.map(() => '?').join(', ')})`,\n\t\t\t\t\t\tkeys.map((key) => toSqlValue(row[key])),\n\t\t\t\t\t)\n\t\t\t\t\trecordsRestored++\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n\n\tlet restored = rawRecords\n\t\t? backup.operations.filter((operation) => targets.includes(operation.collection)).length\n\t\t: 0\n\tif (!rawRecords) {\n\t\t// No materialized rows in the backup: the remote-apply path rebuilds them.\n\t\tfor (const operation of backup.operations) {\n\t\t\tif (!targets.includes(operation.collection)) continue\n\t\t\tif ((await host.applyOperation(operation)) === 'applied') restored++\n\t\t}\n\t}\n\tlet unsyncedWritesKept = 0\n\tfor (const operation of kept) {\n\t\tif ((await host.applyOperation(operation)) === 'applied') unsyncedWritesKept++\n\t}\n\n\tawait adapter.transaction(async (tx) => {\n\t\t// Restartable counters continue after the highest number now in the log.\n\t\tfor (const nodeId of restartable) {\n\t\t\tconst highest = await highestSequence(tx, targets, nodeId)\n\t\t\tawait tx.execute('DELETE FROM _kora_version_vector WHERE node_id = ?', [nodeId])\n\t\t\tif (highest > 0) {\n\t\t\t\tawait tx.execute(\n\t\t\t\t\t'INSERT INTO _kora_version_vector (node_id, sequence_number) VALUES (?, ?)',\n\t\t\t\t\t[nodeId, highest],\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t\t// Nothing dropped may still wait to upload.\n\t\tif (dropped.size > 0 || !options.keepUnsyncedWrites) {\n\t\t\tconst union = targets\n\t\t\t\t.map((c) => `SELECT id FROM ${quoteIdent(`_kora_ops_${c}`)}`)\n\t\t\t\t.join(' UNION ')\n\t\t\tif (union) {\n\t\t\t\tawait tx.execute(`DELETE FROM _kora_sync_queue WHERE id NOT IN (${union})`)\n\t\t\t}\n\t\t}\n\t})\n\n\treturn { operationsRestored: restored, recordsRestored, unsyncedWritesKept }\n}\n\nasync function importTerminalRejections(\n\tadapter: StorageAdapter,\n\tbackup: ParsedBackup,\n): Promise<void> {\n\tif (backup.terminalRejections.length === 0) return\n\tawait ensureLocalSyncRecordTables(adapter)\n\tawait adapter.transaction(async (tx) => {\n\t\tfor (const entry of backup.terminalRejections) {\n\t\t\tawait tx.execute(\n\t\t\t\t`INSERT OR IGNORE INTO ${TERMINAL_REJECTIONS_TABLE} (operation_id, node_id, sequence_number, code, rejected_at) VALUES (?, ?, ?, ?, ?)`,\n\t\t\t\t[entry.operationId, entry.nodeId, entry.sequenceNumber, entry.code, entry.rejectedAt],\n\t\t\t)\n\t\t}\n\t})\n}\n\n/** Append a canonical operation row; a taken `(node, sequence)` goes to the conflicts table. */\nasync function insertOperationRow(tx: Transaction, operation: Operation): Promise<void> {\n\tconst table = quoteIdent(`_kora_ops_${operation.collection}`)\n\tconst row = serializeOperation(operation)\n\tconst existing = await tx.query<{ id: string }>(`SELECT id FROM ${table} WHERE id = ?`, [row.id])\n\tif (existing.length > 0) return\n\tconst holder = await tx.query<{ id: string }>(\n\t\t`SELECT id FROM ${table} WHERE node_id = ? AND sequence_number = ?`,\n\t\t[row.node_id, row.sequence_number],\n\t)\n\tif (holder.length > 0) {\n\t\tawait insertConflictRow(\n\t\t\ttx,\n\t\t\toperation.collection,\n\t\t\trow,\n\t\t\t'backup-sequence-conflict',\n\t\t\tnull,\n\t\t\tnull,\n\t\t\tDate.now(),\n\t\t)\n\t\treturn\n\t}\n\tawait tx.execute(\n\t\t`INSERT INTO ${table} (id, node_id, type, record_id, data, previous_data, timestamp, sequence_number, causal_deps, schema_version) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n\t\t[\n\t\t\trow.id,\n\t\t\trow.node_id,\n\t\t\trow.type,\n\t\t\trow.record_id,\n\t\t\trow.data,\n\t\t\trow.previous_data,\n\t\t\trow.timestamp,\n\t\t\trow.sequence_number,\n\t\t\trow.causal_deps,\n\t\t\trow.schema_version,\n\t\t],\n\t)\n}\n\nasync function maxVector(tx: Transaction, nodeId: string, sequence: number): Promise<void> {\n\tawait tx.execute(\n\t\t`INSERT INTO _kora_version_vector (node_id, sequence_number) VALUES (?, ?)\n ON CONFLICT(node_id) DO UPDATE SET sequence_number = MAX(sequence_number, excluded.sequence_number)`,\n\t\t[nodeId, sequence],\n\t)\n}\n\nasync function highestSequence(\n\ttx: Transaction,\n\tcollections: string[],\n\tnodeId: string,\n): Promise<number> {\n\tlet highest = 0\n\tfor (const collection of collections) {\n\t\tconst rows = await tx.query<{ m: number | null }>(\n\t\t\t`SELECT MAX(sequence_number) AS m FROM ${quoteIdent(`_kora_ops_${collection}`)} WHERE node_id = ?`,\n\t\t\t[nodeId],\n\t\t)\n\t\thighest = Math.max(highest, rows[0]?.m ?? 0)\n\t}\n\treturn highest\n}\n\n/**\n * Per own node, how far the sync server acknowledged its operations: the W3 contiguous\n * acknowledged prefix, else the last acknowledged server vector, else 0.\n */\nasync function loadAckedThrough(\n\tadapter: StorageAdapter,\n\tnodes: readonly LocalNodeRecord[],\n): Promise<Map<string, number>> {\n\tconst lastAcked = await loadLastAckedServerVector(adapter)\n\tconst result = new Map<string, number>()\n\tfor (const node of nodes) {\n\t\tconst prefix = await loadOwnAckedThrough(adapter, node.nodeId)\n\t\tresult.set(node.nodeId, prefix ?? lastAcked.get(node.nodeId) ?? 0)\n\t}\n\treturn result\n}\n\nasync function tableExists(adapter: StorageAdapter, name: string): Promise<boolean> {\n\tconst rows = await adapter.query<{ name: string }>(\n\t\t\"SELECT name FROM sqlite_master WHERE type = 'table' AND name = ?\",\n\t\t[name],\n\t)\n\treturn rows.length > 0\n}\n\nfunction toSqlValue(value: unknown): unknown {\n\tif (typeof value === 'boolean') return value ? 1 : 0\n\tif (value instanceof Uint8Array) return value\n\tif (value !== null && typeof value === 'object') return JSON.stringify(value)\n\treturn value\n}\n"],"mappings":"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","names":[]}
|