@xnetjs/data 0.1.1 → 0.1.2
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.
|
@@ -3413,6 +3413,7 @@ var DEFAULT_QUERY_VERIFICATION = {
|
|
|
3413
3413
|
logFailures: true
|
|
3414
3414
|
};
|
|
3415
3415
|
var QUERY_TELEMETRY_FLUSH_THRESHOLD = 50;
|
|
3416
|
+
var CHANGE_ENVELOPE_KEY = "__xnetChangeEnvelopeV1";
|
|
3416
3417
|
var COMPILED_QUERY_DIAGNOSTICS_MEMO_LIMIT = 512;
|
|
3417
3418
|
var SQLITE_BIND_PARAMETER_BATCH_SIZE = 900;
|
|
3418
3419
|
var SQLITE_HYDRATE_NODE_BATCH_SIZE = Math.floor(SQLITE_BIND_PARAMETER_BATCH_SIZE / 2);
|
|
@@ -3581,7 +3582,7 @@ var SQLiteNodeStorageAdapter = class {
|
|
|
3581
3582
|
await this.enqueueWrite(() => this.appendChangeInternal(change));
|
|
3582
3583
|
}
|
|
3583
3584
|
async appendChangeInternal(change) {
|
|
3584
|
-
const payload = this.
|
|
3585
|
+
const payload = this.serializeChangeRecord(change);
|
|
3585
3586
|
await this.db.run(
|
|
3586
3587
|
`INSERT OR IGNORE INTO changes
|
|
3587
3588
|
(hash, node_id, payload, lamport_time, lamport_peer, wall_time, author, parent_hash, batch_id, signature)
|
|
@@ -3842,7 +3843,11 @@ var SQLiteNodeStorageAdapter = class {
|
|
|
3842
3843
|
lamport_time = excluded.lamport_time,
|
|
3843
3844
|
updated_by = excluded.updated_by,
|
|
3844
3845
|
updated_at = excluded.updated_at
|
|
3845
|
-
WHERE excluded.lamport_time > node_properties.lamport_time
|
|
3846
|
+
WHERE excluded.lamport_time > node_properties.lamport_time
|
|
3847
|
+
OR (excluded.lamport_time = node_properties.lamport_time
|
|
3848
|
+
AND (excluded.updated_at > node_properties.updated_at
|
|
3849
|
+
OR (excluded.updated_at = node_properties.updated_at
|
|
3850
|
+
AND excluded.updated_by > node_properties.updated_by)))`,
|
|
3846
3851
|
[
|
|
3847
3852
|
node.id,
|
|
3848
3853
|
key,
|
|
@@ -4335,7 +4340,7 @@ var SQLiteNodeStorageAdapter = class {
|
|
|
4335
4340
|
changes: input.changes.map((change) => ({
|
|
4336
4341
|
hash: change.hash,
|
|
4337
4342
|
nodeId: change.payload.nodeId,
|
|
4338
|
-
payload: this.
|
|
4343
|
+
payload: this.serializeChangeRecord(change),
|
|
4339
4344
|
lamportTime: change.lamport,
|
|
4340
4345
|
lamportPeer: change.authorDID,
|
|
4341
4346
|
wallTime: change.wallTime,
|
|
@@ -4488,6 +4493,8 @@ var SQLiteNodeStorageAdapter = class {
|
|
|
4488
4493
|
const timestamp = node.timestamps[key];
|
|
4489
4494
|
if (!timestamp) continue;
|
|
4490
4495
|
operations.push({
|
|
4496
|
+
// Full LWW ordering triple — keep in lockstep with the setNode
|
|
4497
|
+
// upsert above and `shouldReplace` in ./store.ts (0272).
|
|
4491
4498
|
sql: `INSERT INTO node_properties
|
|
4492
4499
|
(node_id, property_key, value, lamport_time, updated_by, updated_at)
|
|
4493
4500
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
@@ -4496,7 +4503,11 @@ var SQLiteNodeStorageAdapter = class {
|
|
|
4496
4503
|
lamport_time = excluded.lamport_time,
|
|
4497
4504
|
updated_by = excluded.updated_by,
|
|
4498
4505
|
updated_at = excluded.updated_at
|
|
4499
|
-
WHERE excluded.lamport_time > node_properties.lamport_time
|
|
4506
|
+
WHERE excluded.lamport_time > node_properties.lamport_time
|
|
4507
|
+
OR (excluded.lamport_time = node_properties.lamport_time
|
|
4508
|
+
AND (excluded.updated_at > node_properties.updated_at
|
|
4509
|
+
OR (excluded.updated_at = node_properties.updated_at
|
|
4510
|
+
AND excluded.updated_by > node_properties.updated_by)))`,
|
|
4500
4511
|
params: [
|
|
4501
4512
|
node.id,
|
|
4502
4513
|
key,
|
|
@@ -4529,7 +4540,7 @@ var SQLiteNodeStorageAdapter = class {
|
|
|
4529
4540
|
params: [
|
|
4530
4541
|
change.hash,
|
|
4531
4542
|
change.payload.nodeId,
|
|
4532
|
-
this.
|
|
4543
|
+
this.serializeChangeRecord(change),
|
|
4533
4544
|
change.lamport,
|
|
4534
4545
|
change.authorDID,
|
|
4535
4546
|
change.wallTime,
|
|
@@ -6470,8 +6481,28 @@ WHERE schema_id = ${this.quoteSqlLiteral(schemaId)}
|
|
|
6470
6481
|
serializePayload(payload) {
|
|
6471
6482
|
return new TextEncoder().encode(JSON.stringify(payload));
|
|
6472
6483
|
}
|
|
6473
|
-
|
|
6474
|
-
|
|
6484
|
+
/**
|
|
6485
|
+
* Serialize a change for the `changes` table (exploration 0272).
|
|
6486
|
+
*
|
|
6487
|
+
* The table has no columns for `id`, `type`, `protocolVersion`,
|
|
6488
|
+
* `batchIndex`, or `batchSize`, yet all of them are part of the signed
|
|
6489
|
+
* content hash — a change re-read without them can never pass
|
|
6490
|
+
* `verifyChangeHash`, so the reload-resync push (getChangesSince → hub)
|
|
6491
|
+
* was structurally rejected as INVALID_HASH and tripped the 0224 breaker,
|
|
6492
|
+
* stranding offline edits. The hub's own `node_changes` table persists
|
|
6493
|
+
* every one of these fields; the client log now does too, by wrapping the
|
|
6494
|
+
* payload BLOB in an envelope instead of a schema migration (applySchema
|
|
6495
|
+
* re-runs idempotent DDL and never executes ALTERs, so new columns would
|
|
6496
|
+
* not reach existing databases).
|
|
6497
|
+
*/
|
|
6498
|
+
serializeChangeRecord(change) {
|
|
6499
|
+
const meta = { id: change.id, type: change.type };
|
|
6500
|
+
if (change.protocolVersion !== void 0) meta.protocolVersion = change.protocolVersion;
|
|
6501
|
+
if (change.batchIndex !== void 0) meta.batchIndex = change.batchIndex;
|
|
6502
|
+
if (change.batchSize !== void 0) meta.batchSize = change.batchSize;
|
|
6503
|
+
return new TextEncoder().encode(
|
|
6504
|
+
JSON.stringify({ [CHANGE_ENVELOPE_KEY]: meta, payload: change.payload })
|
|
6505
|
+
);
|
|
6475
6506
|
}
|
|
6476
6507
|
serializeValue(value) {
|
|
6477
6508
|
return new TextEncoder().encode(JSON.stringify(value));
|
|
@@ -6481,13 +6512,16 @@ WHERE schema_id = ${this.quoteSqlLiteral(schemaId)}
|
|
|
6481
6512
|
return JSON.parse(new TextDecoder().decode(data));
|
|
6482
6513
|
}
|
|
6483
6514
|
deserializeChange(row) {
|
|
6484
|
-
const
|
|
6485
|
-
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
//
|
|
6489
|
-
|
|
6490
|
-
//
|
|
6515
|
+
const parsed = JSON.parse(new TextDecoder().decode(row.payload));
|
|
6516
|
+
const envelope = parsed !== null && typeof parsed === "object" && CHANGE_ENVELOPE_KEY in parsed ? parsed[CHANGE_ENVELOPE_KEY] : null;
|
|
6517
|
+
const payload = envelope ? parsed.payload : parsed;
|
|
6518
|
+
const change = {
|
|
6519
|
+
// Envelope rows (exploration 0272) round-trip the hashed identity
|
|
6520
|
+
// fields exactly, so verifyChangeHash holds after a re-read. Legacy
|
|
6521
|
+
// rows predate the envelope: their id/type/protocolVersion are gone
|
|
6522
|
+
// for good, so we keep the historical fabricated values.
|
|
6523
|
+
id: envelope?.id ?? row.hash,
|
|
6524
|
+
type: envelope?.type ?? "node",
|
|
6491
6525
|
hash: row.hash,
|
|
6492
6526
|
payload,
|
|
6493
6527
|
lamport: row.lamport_time,
|
|
@@ -6497,6 +6531,10 @@ WHERE schema_id = ${this.quoteSqlLiteral(schemaId)}
|
|
|
6497
6531
|
batchId: row.batch_id ?? void 0,
|
|
6498
6532
|
signature: row.signature
|
|
6499
6533
|
};
|
|
6534
|
+
if (envelope?.protocolVersion !== void 0) change.protocolVersion = envelope.protocolVersion;
|
|
6535
|
+
if (envelope?.batchIndex !== void 0) change.batchIndex = envelope.batchIndex;
|
|
6536
|
+
if (envelope?.batchSize !== void 0) change.batchSize = envelope.batchSize;
|
|
6537
|
+
return change;
|
|
6500
6538
|
}
|
|
6501
6539
|
};
|
|
6502
6540
|
function chunkItems(items, size) {
|
package/dist/index.js
CHANGED
package/dist/store/index.d.ts
CHANGED
|
@@ -514,7 +514,21 @@ declare class SQLiteNodeStorageAdapter implements NodeStorageAdapter {
|
|
|
514
514
|
private buildSqlOrderBy;
|
|
515
515
|
private hasOnlySystemOrdering;
|
|
516
516
|
private serializePayload;
|
|
517
|
-
|
|
517
|
+
/**
|
|
518
|
+
* Serialize a change for the `changes` table (exploration 0272).
|
|
519
|
+
*
|
|
520
|
+
* The table has no columns for `id`, `type`, `protocolVersion`,
|
|
521
|
+
* `batchIndex`, or `batchSize`, yet all of them are part of the signed
|
|
522
|
+
* content hash — a change re-read without them can never pass
|
|
523
|
+
* `verifyChangeHash`, so the reload-resync push (getChangesSince → hub)
|
|
524
|
+
* was structurally rejected as INVALID_HASH and tripped the 0224 breaker,
|
|
525
|
+
* stranding offline edits. The hub's own `node_changes` table persists
|
|
526
|
+
* every one of these fields; the client log now does too, by wrapping the
|
|
527
|
+
* payload BLOB in an envelope instead of a schema migration (applySchema
|
|
528
|
+
* re-runs idempotent DDL and never executes ALTERs, so new columns would
|
|
529
|
+
* not reach existing databases).
|
|
530
|
+
*/
|
|
531
|
+
private serializeChangeRecord;
|
|
518
532
|
private serializeValue;
|
|
519
533
|
private deserializeValue;
|
|
520
534
|
private deserializeChange;
|
package/dist/store/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xnetjs/data",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -57,12 +57,12 @@
|
|
|
57
57
|
"nanoid": "^5.1.6",
|
|
58
58
|
"y-protocols": "^1.0.6",
|
|
59
59
|
"yjs": "^13.6.24",
|
|
60
|
-
"@xnetjs/core": "0.1.
|
|
61
|
-
"@xnetjs/crypto": "0.1.
|
|
62
|
-
"@xnetjs/identity": "0.1.
|
|
63
|
-
"@xnetjs/sqlite": "0.1.
|
|
64
|
-
"@xnetjs/storage": "0.1.
|
|
65
|
-
"@xnetjs/sync": "0.1.
|
|
60
|
+
"@xnetjs/core": "0.1.2",
|
|
61
|
+
"@xnetjs/crypto": "0.1.2",
|
|
62
|
+
"@xnetjs/identity": "0.1.2",
|
|
63
|
+
"@xnetjs/sqlite": "0.1.2",
|
|
64
|
+
"@xnetjs/storage": "0.1.2",
|
|
65
|
+
"@xnetjs/sync": "0.1.2"
|
|
66
66
|
},
|
|
67
67
|
"devDependencies": {
|
|
68
68
|
"tsup": "^8.0.0",
|