@delali/sirannon-db 0.1.5 → 0.1.6

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 CHANGED
@@ -580,6 +580,8 @@ When a new node joins a running cluster, it needs the full dataset before it can
580
580
 
581
581
  The state machine is: `pending` -> `syncing` -> `catching-up` -> `ready`. You can monitor it via `engine.status().syncState`.
582
582
 
583
+ During `syncing`, `syncState.completedTables` lists the tables the joiner has finished and `syncState.totalTables` holds how many it will receive in all, so `completedTables.length / totalTables` gives you first-sync progress. A source that predates this field leaves `totalTables` at 0 until the sync finishes.
584
+
583
585
  For large databases where a network transfer is impractical, the out-of-band path lets you copy the SQLite file directly and start from a known sequence:
584
586
 
585
587
  ```ts
@@ -1,5 +1,5 @@
1
- import { C as ConflictResolver, h as ConflictContext, i as ConflictResolution, H as HLCTimestamp, R as ReplicationBatch, A as ApplyResult, j as SyncPhase, k as SyncTableManifest, I as InFlightBatch, P as PeerState, b as ForwardedTransactionResult, c as SyncBatch, d as SyncComplete, e as SyncAck, S as SyncRequest, l as ReplicationConfig, m as SyncState, n as ReplicationStatus, o as ReplicationErrorEvent, a as ReplicationAck, p as Topology, T as TopologyRole } from '../types-B2byqt0B.js';
2
- export { F as ForwardedTransaction, N as NodeInfo, q as ReplicationChange, f as ReplicationTransport, g as TransportConfig } from '../types-B2byqt0B.js';
1
+ import { C as ConflictResolver, h as ConflictContext, i as ConflictResolution, H as HLCTimestamp, R as ReplicationBatch, A as ApplyResult, j as SyncPhase, k as SyncTableManifest, I as InFlightBatch, P as PeerState, b as ForwardedTransactionResult, c as SyncBatch, d as SyncComplete, e as SyncAck, S as SyncRequest, l as ReplicationConfig, m as SyncState, n as ReplicationStatus, o as ReplicationErrorEvent, a as ReplicationAck, p as Topology, T as TopologyRole } from '../types-Lc7ywFx7.js';
2
+ export { F as ForwardedTransaction, N as NodeInfo, q as ReplicationChange, f as ReplicationTransport, g as TransportConfig } from '../types-Lc7ywFx7.js';
3
3
  import { c as ReplicationGroupState } from '../types-BEu1I_9_.js';
4
4
  export { A as AcquireControllerLeaseInput, a as AcquireControllerLeaseResult, h as AdmitNodeToInSyncSetInput, C as ClusterCoordinator, f as CompareAndAdvancePrimaryTermInput, g as CompareAndAdvancePrimaryTermResult, j as CoordinatorCompatibilityMetadata, k as CoordinatorLease, b as CoordinatorNodeSession, l as CoordinatorPrimary, e as CoordinatorWatchDisposer, P as PromoteEligibleReplicaInput, R as RegisterNodeSessionInput, d as ReplicationGroupWatcher, S as SetReplicationGroupStateInput, U as UpdateInSyncSetInput, i as UpdateNodeMaintenanceInput } from '../types-BEu1I_9_.js';
5
5
  import { EventEmitter } from 'node:events';
@@ -244,6 +244,7 @@ interface ActiveSyncSession {
244
244
  readConn: SQLiteConnection;
245
245
  snapshotSeq: bigint;
246
246
  tables: string[];
247
+ totalTables: number;
247
248
  completedTables: Set<string>;
248
249
  startedAt: number;
249
250
  timeoutTimer: ReturnType<typeof setTimeout>;
@@ -2322,6 +2322,9 @@ var SyncJoiner = class {
2322
2322
  engine.expectedBatchIndex.set(batch.table, expectedIndex + 1);
2323
2323
  try {
2324
2324
  if (batch.table === "__schema__") {
2325
+ if (typeof batch.totalTables === "number" && Number.isInteger(batch.totalTables) && batch.totalTables >= 0) {
2326
+ engine.syncState.totalTables = batch.totalTables;
2327
+ }
2325
2328
  if (batch.schema) {
2326
2329
  for (const ddl of batch.schema) {
2327
2330
  if (!isSyncSafeDdl(ddl)) {
@@ -2645,6 +2648,7 @@ var SyncServer = class {
2645
2648
  readConn,
2646
2649
  snapshotSeq,
2647
2650
  tables,
2651
+ totalTables: allTables.length,
2648
2652
  completedTables: new Set(request.completedTables),
2649
2653
  startedAt: Date.now(),
2650
2654
  timeoutTimer,
@@ -2770,7 +2774,8 @@ var SyncServer = class {
2770
2774
  rows: [],
2771
2775
  schema: schemaDdl,
2772
2776
  checksum: schemaChecksum,
2773
- isLastBatchForTable: true
2777
+ isLastBatchForTable: true,
2778
+ totalTables: session.totalTables
2774
2779
  });
2775
2780
  if (!schemaAck.success) {
2776
2781
  this.abortSyncSession(session.requestId);
@@ -1,7 +1,7 @@
1
1
  import { BinaryWriter, BinaryReader } from '@bufbuild/protobuf/wire';
2
2
  import { Client, ClientDuplexStream, CallOptions, Metadata, ServiceError, ClientUnaryCall, ChannelCredentials, ClientOptions, ServerDuplexStream, Server } from '@grpc/grpc-js';
3
3
  import { HealthImplementation } from 'grpc-health-check';
4
- import { R as ReplicationBatch, a as ReplicationAck, F as ForwardedTransaction, b as ForwardedTransactionResult, N as NodeInfo, S as SyncRequest, c as SyncBatch, d as SyncComplete, e as SyncAck, f as ReplicationTransport, T as TopologyRole, g as TransportConfig } from '../types-B2byqt0B.js';
4
+ import { R as ReplicationBatch, a as ReplicationAck, F as ForwardedTransaction, b as ForwardedTransactionResult, N as NodeInfo, S as SyncRequest, c as SyncBatch, d as SyncComplete, e as SyncAck, f as ReplicationTransport, T as TopologyRole, g as TransportConfig } from '../types-Lc7ywFx7.js';
5
5
  import '../change-tracker-CFTQ9TSn.js';
6
6
  import '../types-BFSsG77t.js';
7
7
  import '../types-D-74JiXb.js';
@@ -92,6 +92,7 @@ interface SyncBatchPayload {
92
92
  isLastBatchForTable: boolean;
93
93
  groupId: string;
94
94
  primaryTerm: bigint;
95
+ totalTables: number;
95
96
  }
96
97
  declare const SyncBatchPayload: MessageFns<SyncBatchPayload>;
97
98
  interface SyncCompletePayload {
@@ -160,7 +160,8 @@ function toSyncBatchPayload(batch) {
160
160
  checksum: batch.checksum,
161
161
  isLastBatchForTable: batch.isLastBatchForTable,
162
162
  groupId: batch.groupId ?? "",
163
- primaryTerm: batch.primaryTerm ?? 0n
163
+ primaryTerm: batch.primaryTerm ?? 0n,
164
+ totalTables: batch.totalTables ?? 0
164
165
  };
165
166
  }
166
167
  function fromSyncBatchPayload(p) {
@@ -173,7 +174,8 @@ function fromSyncBatchPayload(p) {
173
174
  checksum: p.checksum,
174
175
  isLastBatchForTable: p.isLastBatchForTable,
175
176
  groupId: p.groupId || void 0,
176
- primaryTerm: p.primaryTerm === 0n ? void 0 : p.primaryTerm
177
+ primaryTerm: p.primaryTerm === 0n ? void 0 : p.primaryTerm,
178
+ totalTables: p.totalTables === 0 ? void 0 : p.totalTables
177
179
  };
178
180
  }
179
181
  function toSyncCompletePayload(complete) {
@@ -1455,7 +1457,8 @@ function createBaseSyncBatchPayload() {
1455
1457
  checksum: "",
1456
1458
  isLastBatchForTable: false,
1457
1459
  groupId: "",
1458
- primaryTerm: 0n
1460
+ primaryTerm: 0n,
1461
+ totalTables: 0
1459
1462
  };
1460
1463
  }
1461
1464
  var SyncBatchPayload = {
@@ -1490,6 +1493,9 @@ var SyncBatchPayload = {
1490
1493
  }
1491
1494
  writer.uint32(72).int64(message.primaryTerm);
1492
1495
  }
1496
+ if (message.totalTables !== 0) {
1497
+ writer.uint32(80).int32(message.totalTables);
1498
+ }
1493
1499
  return writer;
1494
1500
  },
1495
1501
  decode(input, length) {
@@ -1562,6 +1568,13 @@ var SyncBatchPayload = {
1562
1568
  message.primaryTerm = reader.int64();
1563
1569
  continue;
1564
1570
  }
1571
+ case 10: {
1572
+ if (tag !== 80) {
1573
+ break;
1574
+ }
1575
+ message.totalTables = reader.int32();
1576
+ continue;
1577
+ }
1565
1578
  }
1566
1579
  if ((tag & 7) === 4 || tag === 0) {
1567
1580
  break;
@@ -1580,7 +1593,8 @@ var SyncBatchPayload = {
1580
1593
  checksum: isSet(object.checksum) ? globalThis.String(object.checksum) : "",
1581
1594
  isLastBatchForTable: isSet(object.isLastBatchForTable) ? globalThis.Boolean(object.isLastBatchForTable) : isSet(object.is_last_batch_for_table) ? globalThis.Boolean(object.is_last_batch_for_table) : false,
1582
1595
  groupId: isSet(object.groupId) ? globalThis.String(object.groupId) : isSet(object.group_id) ? globalThis.String(object.group_id) : "",
1583
- primaryTerm: isSet(object.primaryTerm) ? BigInt(object.primaryTerm) : isSet(object.primary_term) ? BigInt(object.primary_term) : 0n
1596
+ primaryTerm: isSet(object.primaryTerm) ? BigInt(object.primaryTerm) : isSet(object.primary_term) ? BigInt(object.primary_term) : 0n,
1597
+ totalTables: isSet(object.totalTables) ? globalThis.Number(object.totalTables) : isSet(object.total_tables) ? globalThis.Number(object.total_tables) : 0
1584
1598
  };
1585
1599
  },
1586
1600
  toJSON(message) {
@@ -1612,6 +1626,9 @@ var SyncBatchPayload = {
1612
1626
  if (message.primaryTerm !== 0n) {
1613
1627
  obj.primaryTerm = message.primaryTerm.toString();
1614
1628
  }
1629
+ if (message.totalTables !== 0) {
1630
+ obj.totalTables = Math.round(message.totalTables);
1631
+ }
1615
1632
  return obj;
1616
1633
  },
1617
1634
  create(base) {
@@ -1628,6 +1645,7 @@ var SyncBatchPayload = {
1628
1645
  message.isLastBatchForTable = object.isLastBatchForTable ?? false;
1629
1646
  message.groupId = object.groupId ?? "";
1630
1647
  message.primaryTerm = object.primaryTerm ?? 0n;
1648
+ message.totalTables = object.totalTables ?? 0;
1631
1649
  return message;
1632
1650
  }
1633
1651
  };
@@ -106,6 +106,7 @@ interface SyncBatch {
106
106
  schema?: string[];
107
107
  checksum: string;
108
108
  isLastBatchForTable: boolean;
109
+ totalTables?: number;
109
110
  groupId?: string;
110
111
  primaryTerm?: bigint;
111
112
  }
@@ -66,7 +66,7 @@ function isValidSyncRequest(req) {
66
66
  function isValidSyncBatch(batch) {
67
67
  if (typeof batch !== "object" || batch === null) return false;
68
68
  const b = batch;
69
- return typeof b.requestId === "string" && typeof b.table === "string" && typeof b.batchIndex === "number" && Array.isArray(b.rows) && typeof b.checksum === "string" && typeof b.isLastBatchForTable === "boolean" && optionalString(b.groupId) && optionalBigint(b.primaryTerm);
69
+ return typeof b.requestId === "string" && typeof b.table === "string" && typeof b.batchIndex === "number" && Array.isArray(b.rows) && typeof b.checksum === "string" && typeof b.isLastBatchForTable === "boolean" && optionalNonNegativeInteger(b.totalTables) && optionalString(b.groupId) && optionalBigint(b.primaryTerm);
70
70
  }
71
71
  function isValidSyncComplete(complete) {
72
72
  if (typeof complete !== "object" || complete === null) return false;
@@ -84,6 +84,9 @@ function optionalString(value) {
84
84
  function optionalBigint(value) {
85
85
  return value === void 0 || typeof value === "bigint";
86
86
  }
87
+ function optionalNonNegativeInteger(value) {
88
+ return value === void 0 || typeof value === "number" && Number.isInteger(value) && value >= 0;
89
+ }
87
90
 
88
91
  // src/transport/memory/transport.ts
89
92
  var InMemoryTransport = class {
@@ -238,6 +238,7 @@ interface SyncBatch {
238
238
  schema?: string[];
239
239
  checksum: string;
240
240
  isLastBatchForTable: boolean;
241
+ totalTables?: number;
241
242
  groupId?: string;
242
243
  primaryTerm?: bigint;
243
244
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@delali/sirannon-db",
3
3
  "type": "module",
4
- "version": "0.1.5",
4
+ "version": "0.1.6",
5
5
  "description": "A production-grade library that turns SQLite databases into a networked data layer with real-time subscriptions.",
6
6
  "author": "Delali (https://sondelali.com)",
7
7
  "license": "Apache-2.0",