@korajs/store 1.0.0-beta.2 → 1.0.0-beta.5
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/adapters/better-sqlite3.cjs.map +1 -1
- package/dist/adapters/better-sqlite3.d.cts +1 -1
- package/dist/adapters/better-sqlite3.d.ts +1 -1
- package/dist/adapters/better-sqlite3.js +1 -1
- package/dist/adapters/indexeddb.cjs +161 -35
- package/dist/adapters/indexeddb.cjs.map +1 -1
- package/dist/adapters/indexeddb.d.cts +2 -2
- package/dist/adapters/indexeddb.d.ts +2 -2
- package/dist/adapters/indexeddb.js +3 -3
- package/dist/adapters/sqlite-wasm-shared-host.cjs +333 -56
- package/dist/adapters/sqlite-wasm-shared-host.cjs.map +1 -1
- package/dist/adapters/sqlite-wasm-shared-host.d.cts +81 -1
- package/dist/adapters/sqlite-wasm-shared-host.d.ts +81 -1
- package/dist/adapters/sqlite-wasm-shared-host.js +51 -56
- package/dist/adapters/sqlite-wasm-shared-host.js.map +1 -1
- package/dist/adapters/sqlite-wasm-worker.cjs +206 -208
- package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
- package/dist/adapters/sqlite-wasm-worker.js +8 -257
- package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
- package/dist/adapters/sqlite-wasm.cjs +161 -35
- package/dist/adapters/sqlite-wasm.cjs.map +1 -1
- package/dist/adapters/sqlite-wasm.d.cts +14 -3
- package/dist/adapters/sqlite-wasm.d.ts +14 -3
- package/dist/adapters/sqlite-wasm.js +3 -3
- package/dist/{backup-IQE4QC7Q.js → backup-5S7K2Z35.js} +3 -3
- package/dist/chunk-3Y2IERBF.js +255 -0
- package/dist/chunk-3Y2IERBF.js.map +1 -0
- package/dist/{chunk-IOEFNHHP.js → chunk-COJSQBXW.js} +2 -2
- package/dist/{chunk-RCTG3IJG.js → chunk-CYO3YKFL.js} +2 -2
- package/dist/{chunk-6NKLDBTY.js → chunk-GSJ7EEUC.js} +2 -1
- package/dist/chunk-GSJ7EEUC.js.map +1 -0
- package/dist/{chunk-OCOGQRHW.js → chunk-HS6DBMST.js} +2 -2
- package/dist/{chunk-QUJGXSZ6.js → chunk-Q57CHIIN.js} +154 -37
- package/dist/chunk-Q57CHIIN.js.map +1 -0
- package/dist/{chunk-2VRPF25R.js → chunk-RL43UYQZ.js} +3 -3
- package/dist/{chunk-QL2XVZRN.js → chunk-TUR7RGEP.js} +12 -1
- package/dist/{chunk-QL2XVZRN.js.map → chunk-TUR7RGEP.js.map} +1 -1
- package/dist/{export-audit-MT2LTMIN.js → export-audit-ODGQTQIK.js} +3 -3
- package/dist/index.cjs +132 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -5
- package/dist/index.d.ts +37 -5
- package/dist/index.js +131 -12
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +1 -0
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +3 -3
- package/dist/internal.d.ts +3 -3
- package/dist/internal.js +3 -3
- package/dist/{sqlite-wasm-channel-DUDPclTE.d.cts → sqlite-wasm-channel-Bx57s8Cq.d.cts} +1 -1
- package/dist/{sqlite-wasm-channel-DUDPclTE.d.ts → sqlite-wasm-channel-Bx57s8Cq.d.ts} +1 -1
- package/dist/sqlite-wasm-channel-NGSBJR44.js +12 -0
- package/dist/{types-5h4Hqcwc.d.ts → types-C1kigrWz.d.ts} +1 -1
- package/dist/{types-k6AZgiH4.d.ts → types-D1MzKVmk.d.cts} +10 -0
- package/dist/{types-k6AZgiH4.d.cts → types-D1MzKVmk.d.ts} +10 -0
- package/dist/{types-DfPiPwKr.d.cts → types-DAo9oduO.d.cts} +1 -1
- package/package.json +2 -2
- package/dist/chunk-6NKLDBTY.js.map +0 -1
- package/dist/chunk-QUJGXSZ6.js.map +0 -1
- package/dist/sqlite-wasm-channel-H43KEZLM.js +0 -12
- /package/dist/{backup-IQE4QC7Q.js.map → backup-5S7K2Z35.js.map} +0 -0
- /package/dist/{chunk-IOEFNHHP.js.map → chunk-COJSQBXW.js.map} +0 -0
- /package/dist/{chunk-RCTG3IJG.js.map → chunk-CYO3YKFL.js.map} +0 -0
- /package/dist/{chunk-OCOGQRHW.js.map → chunk-HS6DBMST.js.map} +0 -0
- /package/dist/{chunk-2VRPF25R.js.map → chunk-RL43UYQZ.js.map} +0 -0
- /package/dist/{export-audit-MT2LTMIN.js.map → export-audit-ODGQTQIK.js.map} +0 -0
- /package/dist/{sqlite-wasm-channel-H43KEZLM.js.map → sqlite-wasm-channel-NGSBJR44.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -69,6 +69,7 @@ function encodeRichtext(value) {
|
|
|
69
69
|
}
|
|
70
70
|
if (typeof value === "string") {
|
|
71
71
|
const doc = new Y.Doc();
|
|
72
|
+
doc.clientID = 0;
|
|
72
73
|
doc.getText(TEXT_KEY).insert(0, value);
|
|
73
74
|
return Y.encodeStateAsUpdate(doc);
|
|
74
75
|
}
|
|
@@ -995,6 +996,7 @@ __export(index_exports, {
|
|
|
995
996
|
InvalidStateTransitionError: () => InvalidStateTransitionError,
|
|
996
997
|
LAST_ACKED_SERVER_VECTOR_META_KEY: () => LAST_ACKED_SERVER_VECTOR_META_KEY,
|
|
997
998
|
MemoryBlobStore: () => MemoryBlobStore,
|
|
999
|
+
NoLeaderError: () => NoLeaderError,
|
|
998
1000
|
OpfsBlobStore: () => OpfsBlobStore,
|
|
999
1001
|
OptimisticLockError: () => OptimisticLockError,
|
|
1000
1002
|
PersistenceError: () => PersistenceError,
|
|
@@ -1113,6 +1115,16 @@ var WorkerTimeoutError = class extends import_core.KoraError {
|
|
|
1113
1115
|
this.name = "WorkerTimeoutError";
|
|
1114
1116
|
}
|
|
1115
1117
|
};
|
|
1118
|
+
var NoLeaderError = class extends import_core.KoraError {
|
|
1119
|
+
constructor(operation) {
|
|
1120
|
+
super(
|
|
1121
|
+
`No live leader tab is answering multi-tab storage RPC for operation "${operation}"`,
|
|
1122
|
+
"NO_LEADER",
|
|
1123
|
+
{ operation }
|
|
1124
|
+
);
|
|
1125
|
+
this.name = "NoLeaderError";
|
|
1126
|
+
}
|
|
1127
|
+
};
|
|
1116
1128
|
var PersistenceError = class extends import_core.KoraError {
|
|
1117
1129
|
constructor(message, context) {
|
|
1118
1130
|
super(`Persistence error: ${message}`, "PERSISTENCE_ERROR", context);
|
|
@@ -1187,6 +1199,7 @@ function buildDeleteSql(table, withAge) {
|
|
|
1187
1199
|
var import_core3 = require("@korajs/core");
|
|
1188
1200
|
var LAST_ACKED_SERVER_VECTOR_META_KEY = "last_acked_server_vector";
|
|
1189
1201
|
var DELTA_CURSOR_META_KEY = "delta_cursor";
|
|
1202
|
+
var DELIVERY_WATERMARK_META_KEY = "delivery_watermark";
|
|
1190
1203
|
function serializeVersionVectorToMeta(vector) {
|
|
1191
1204
|
const record = {};
|
|
1192
1205
|
for (const [nodeId, seq] of vector) {
|
|
@@ -1259,6 +1272,45 @@ async function saveDeltaCursor(adapter, cursor) {
|
|
|
1259
1272
|
cursor
|
|
1260
1273
|
]);
|
|
1261
1274
|
}
|
|
1275
|
+
function deliveryWatermarkKey(signature) {
|
|
1276
|
+
return signature === "" ? DELIVERY_WATERMARK_META_KEY : `${DELIVERY_WATERMARK_META_KEY}:${signature}`;
|
|
1277
|
+
}
|
|
1278
|
+
async function loadDeliveryWatermark(adapter, signature) {
|
|
1279
|
+
const rows = await adapter.query("SELECT value FROM _kora_meta WHERE key = ?", [
|
|
1280
|
+
deliveryWatermarkKey(signature)
|
|
1281
|
+
]);
|
|
1282
|
+
const value = rows[0]?.value;
|
|
1283
|
+
if (value === void 0 || value === null) {
|
|
1284
|
+
return 0;
|
|
1285
|
+
}
|
|
1286
|
+
const parsed = Number(value);
|
|
1287
|
+
return Number.isFinite(parsed) && parsed >= 0 ? parsed : 0;
|
|
1288
|
+
}
|
|
1289
|
+
async function saveDeliveryWatermark(adapter, signature, watermark) {
|
|
1290
|
+
await adapter.execute("INSERT OR REPLACE INTO _kora_meta (key, value) VALUES (?, ?)", [
|
|
1291
|
+
deliveryWatermarkKey(signature),
|
|
1292
|
+
String(watermark)
|
|
1293
|
+
]);
|
|
1294
|
+
}
|
|
1295
|
+
async function deleteDeliveryWatermark(adapter, signature) {
|
|
1296
|
+
await adapter.execute("DELETE FROM _kora_meta WHERE key = ?", [deliveryWatermarkKey(signature)]);
|
|
1297
|
+
}
|
|
1298
|
+
async function loadAllDeliveryWatermarks(adapter) {
|
|
1299
|
+
const rows = await adapter.query(
|
|
1300
|
+
"SELECT key, value FROM _kora_meta WHERE key = ? OR key LIKE ?",
|
|
1301
|
+
[DELIVERY_WATERMARK_META_KEY, `${DELIVERY_WATERMARK_META_KEY}:%`]
|
|
1302
|
+
);
|
|
1303
|
+
const result = {};
|
|
1304
|
+
const prefix = `${DELIVERY_WATERMARK_META_KEY}:`;
|
|
1305
|
+
for (const row of rows) {
|
|
1306
|
+
const signature = row.key === DELIVERY_WATERMARK_META_KEY ? "" : row.key.slice(prefix.length);
|
|
1307
|
+
const parsed = Number(row.value);
|
|
1308
|
+
if (Number.isFinite(parsed) && parsed >= 0) {
|
|
1309
|
+
result[signature] = parsed;
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
return result;
|
|
1313
|
+
}
|
|
1262
1314
|
|
|
1263
1315
|
// src/store/store.ts
|
|
1264
1316
|
var import_core23 = require("@korajs/core");
|
|
@@ -3880,7 +3932,8 @@ var Store = class {
|
|
|
3880
3932
|
throw new OptimisticLockError(collection, op.recordId);
|
|
3881
3933
|
}
|
|
3882
3934
|
};
|
|
3883
|
-
if (
|
|
3935
|
+
if (options?.logOnly) {
|
|
3936
|
+
} else if (op.type === "insert" && materializeSource) {
|
|
3884
3937
|
const serializedData = serializeRecord(materializeSource, definition.fields);
|
|
3885
3938
|
const existing2 = await tx.query(
|
|
3886
3939
|
`SELECT _updated_at, _version, _field_versions, _deleted FROM ${collection} WHERE id = ?`,
|
|
@@ -3902,7 +3955,7 @@ var Store = class {
|
|
|
3902
3955
|
};
|
|
3903
3956
|
const insertQuery = buildInsertQuery(collection, record);
|
|
3904
3957
|
await tx.execute(insertQuery.sql, insertQuery.params);
|
|
3905
|
-
await this.foldOrphanedOperations(tx, collection, definition, op.recordId);
|
|
3958
|
+
await this.foldOrphanedOperations(tx, collection, definition, op.recordId, op);
|
|
3906
3959
|
} else if (row._deleted === 1) {
|
|
3907
3960
|
if (isIncomingNewerThanRow(materializeTimestamp, row)) {
|
|
3908
3961
|
const fieldChanges = {
|
|
@@ -4054,7 +4107,7 @@ var Store = class {
|
|
|
4054
4107
|
* necessarily an orphan (rows are soft-deleted, never removed, so a row
|
|
4055
4108
|
* that is absent now was never materialized).
|
|
4056
4109
|
*/
|
|
4057
|
-
async foldOrphanedOperations(tx, collection, definition, recordId) {
|
|
4110
|
+
async foldOrphanedOperations(tx, collection, definition, recordId, insertOp) {
|
|
4058
4111
|
const orphanRows = await tx.query(
|
|
4059
4112
|
`SELECT * FROM ${(0, import_core23.quoteIdent)(`_kora_ops_${collection}`)} WHERE record_id = ? AND type IN ('update', 'delete')`,
|
|
4060
4113
|
[recordId]
|
|
@@ -4103,6 +4156,37 @@ var Store = class {
|
|
|
4103
4156
|
await tx.execute(query.sql, query.params);
|
|
4104
4157
|
}
|
|
4105
4158
|
}
|
|
4159
|
+
const atomicFields = /* @__PURE__ */ new Set();
|
|
4160
|
+
for (const orphan of orphans) {
|
|
4161
|
+
if (orphan.type === "update" && orphan.atomicOps) {
|
|
4162
|
+
for (const field of Object.keys(orphan.atomicOps)) {
|
|
4163
|
+
atomicFields.add(field);
|
|
4164
|
+
}
|
|
4165
|
+
}
|
|
4166
|
+
}
|
|
4167
|
+
if (atomicFields.size > 0) {
|
|
4168
|
+
const ordered = [insertOp, ...orphans].sort(
|
|
4169
|
+
(a, b) => import_core23.HybridLogicalClock.compare(a.timestamp, b.timestamp)
|
|
4170
|
+
);
|
|
4171
|
+
const folded = (0, import_core23.replayOperationsForRecord)(ordered);
|
|
4172
|
+
if (folded) {
|
|
4173
|
+
const composed = {};
|
|
4174
|
+
for (const field of atomicFields) {
|
|
4175
|
+
if (field in folded) {
|
|
4176
|
+
composed[field] = folded[field];
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
const serialized = serializeRecord(composed, definition.fields);
|
|
4180
|
+
const cols = Object.keys(serialized);
|
|
4181
|
+
if (cols.length > 0) {
|
|
4182
|
+
const setClause = cols.map((c) => `${(0, import_core23.quoteIdent)(c)} = ?`).join(", ");
|
|
4183
|
+
await tx.execute(`UPDATE ${(0, import_core23.quoteIdent)(collection)} SET ${setClause} WHERE id = ?`, [
|
|
4184
|
+
...cols.map((c) => serialized[c]),
|
|
4185
|
+
recordId
|
|
4186
|
+
]);
|
|
4187
|
+
}
|
|
4188
|
+
}
|
|
4189
|
+
}
|
|
4106
4190
|
}
|
|
4107
4191
|
/**
|
|
4108
4192
|
* Get operations from a node within a sequence number range.
|
|
@@ -4316,6 +4400,35 @@ var Store = class {
|
|
|
4316
4400
|
this.ensureOpen();
|
|
4317
4401
|
await saveDeltaCursor(this.adapter, cursor);
|
|
4318
4402
|
}
|
|
4403
|
+
/**
|
|
4404
|
+
* Load the persisted delivery watermark for a view signature (0 when none recorded).
|
|
4405
|
+
* Defaults to the unfiltered view so existing single-watermark reads keep working.
|
|
4406
|
+
*/
|
|
4407
|
+
async loadDeliveryWatermark(signature = "") {
|
|
4408
|
+
this.ensureOpen();
|
|
4409
|
+
return loadDeliveryWatermark(this.adapter, signature);
|
|
4410
|
+
}
|
|
4411
|
+
/**
|
|
4412
|
+
* Persist the delivery watermark for a view signature so it survives a client restart.
|
|
4413
|
+
*/
|
|
4414
|
+
async saveDeliveryWatermark(signature, watermark) {
|
|
4415
|
+
this.ensureOpen();
|
|
4416
|
+
await saveDeliveryWatermark(this.adapter, signature, watermark);
|
|
4417
|
+
}
|
|
4418
|
+
/**
|
|
4419
|
+
* Load every persisted view watermark, keyed by signature.
|
|
4420
|
+
*/
|
|
4421
|
+
async loadAllDeliveryWatermarks() {
|
|
4422
|
+
this.ensureOpen();
|
|
4423
|
+
return loadAllDeliveryWatermarks(this.adapter);
|
|
4424
|
+
}
|
|
4425
|
+
/**
|
|
4426
|
+
* Delete a persisted view watermark (safe: the view back-fills from 0 when next visited).
|
|
4427
|
+
*/
|
|
4428
|
+
async deleteDeliveryWatermark(signature) {
|
|
4429
|
+
this.ensureOpen();
|
|
4430
|
+
await deleteDeliveryWatermark(this.adapter, signature);
|
|
4431
|
+
}
|
|
4319
4432
|
/**
|
|
4320
4433
|
* Local operations not yet reflected on the server version vector.
|
|
4321
4434
|
*/
|
|
@@ -4406,6 +4519,21 @@ var Store = class {
|
|
|
4406
4519
|
}
|
|
4407
4520
|
return latest;
|
|
4408
4521
|
}
|
|
4522
|
+
/**
|
|
4523
|
+
* All operations for a record, sorted in HLC total order (wallTime, logical,
|
|
4524
|
+
* nodeId). Used by the apply pipeline to materialize atomic-op fields by folding
|
|
4525
|
+
* the record's log, matching the server's materialization exactly.
|
|
4526
|
+
*/
|
|
4527
|
+
async getOperationsForRecord(collection, recordId) {
|
|
4528
|
+
this.ensureOpen();
|
|
4529
|
+
const rows = await this.adapter.query(
|
|
4530
|
+
`SELECT * FROM ${(0, import_core23.quoteIdent)(`_kora_ops_${collection}`)} WHERE record_id = ?`,
|
|
4531
|
+
[recordId]
|
|
4532
|
+
);
|
|
4533
|
+
const ops = rows.map((row) => deserializeOperationWithCollection(row, collection));
|
|
4534
|
+
ops.sort((a, b) => import_core23.HybridLogicalClock.compare(a.timestamp, b.timestamp));
|
|
4535
|
+
return ops;
|
|
4536
|
+
}
|
|
4409
4537
|
/** Expose the subscription manager for direct access (e.g., by QueryBuilder) */
|
|
4410
4538
|
getSubscriptionManager() {
|
|
4411
4539
|
return this.subscriptionManager;
|
|
@@ -5700,6 +5828,7 @@ function createChunkPortPair() {
|
|
|
5700
5828
|
InvalidStateTransitionError,
|
|
5701
5829
|
LAST_ACKED_SERVER_VECTOR_META_KEY,
|
|
5702
5830
|
MemoryBlobStore,
|
|
5831
|
+
NoLeaderError,
|
|
5703
5832
|
OpfsBlobStore,
|
|
5704
5833
|
OptimisticLockError,
|
|
5705
5834
|
PersistenceError,
|