@delali/sirannon-db 0.2.3-next.36 → 0.2.3-next.37

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.
Files changed (35) hide show
  1. package/dist/backup/index.d.ts +3 -3
  2. package/dist/backup/index.mjs +3 -3
  3. package/dist/backup-scheduler/index.d.ts +1 -1
  4. package/dist/backup-scheduler/index.mjs +2 -2
  5. package/dist/{chain-queries-BzoS648g.d.ts → chain-queries-D5aZ-8v2.d.ts} +1 -1
  6. package/dist/{change-tracker-Cl-ugzWo.d.ts → change-tracker-DXHPFe9J.d.ts} +1 -1
  7. package/dist/{chunk-T65NT3SH.mjs → chunk-BQTVNI5H.mjs} +1 -1
  8. package/dist/{chunk-TI6NWCHJ.mjs → chunk-GBM4AU24.mjs} +4 -0
  9. package/dist/{chunk-4H45GMYI.mjs → chunk-YVF2ZJTE.mjs} +52 -21
  10. package/dist/{chunk-7AVIVR72.mjs → chunk-ZKDBGX7H.mjs} +326 -70
  11. package/dist/client/index.d.ts +8 -8
  12. package/dist/client/topology.d.ts +4 -4
  13. package/dist/{client-base-zLvPr6Px.d.ts → client-base-CGBpjZUB.d.ts} +2 -2
  14. package/dist/core/index.d.ts +9 -9
  15. package/dist/core/index.mjs +5 -3
  16. package/dist/{database-CstJKKVt.d.ts → database-955qag_W.d.ts} +8 -6
  17. package/dist/driver/better-sqlite3.d.ts +1 -1
  18. package/dist/driver/better-sqlite3.mjs +4 -4
  19. package/dist/driver/bun.d.ts +1 -1
  20. package/dist/driver/expo.d.ts +1 -1
  21. package/dist/driver/node.d.ts +1 -1
  22. package/dist/driver/node.mjs +4 -4
  23. package/dist/driver/wa-sqlite.d.ts +1 -1
  24. package/dist/file-migrations/index.d.ts +1 -1
  25. package/dist/{protocol-CTbpDnFx.d.ts → protocol-BfiujBsE.d.ts} +1 -1
  26. package/dist/replication/index.d.ts +45 -10
  27. package/dist/replication/index.mjs +23 -2
  28. package/dist/server/index.d.ts +8 -8
  29. package/dist/{server-options-psYF3I5N.d.ts → server-options-D4VmfTHw.d.ts} +1 -1
  30. package/dist/{sirannon-D0WW0K-Z.d.ts → sirannon-DaGUawHs.d.ts} +2 -2
  31. package/dist/transport/grpc.d.ts +3 -3
  32. package/dist/transport/memory.d.ts +3 -3
  33. package/dist/{types-CoPT4nhu.d.ts → types-BEjS69rs.d.ts} +2 -2
  34. package/dist/{types-Bm-biDkV.d.ts → types-Xb0L3o3w.d.ts} +157 -9
  35. package/package.json +7 -7
@@ -1,6 +1,6 @@
1
- import { j as BackupDestination, k as BackupRunReport, a as SQLiteDriver } from '../types-Bm-biDkV.js';
2
- export { g as BackupChain, e as BackupChainBase, f as BackupChainChange, l as BackupChainPosition, h as BackupChainRecord, m as BackupCycle, n as BackupCycleOptions, o as BackupPiece, p as BackupProgress, q as BackupToDestinationOptions, r as DEFAULT_CHAIN_NAME, s as chainLogName, t as readBackupChains } from '../types-Bm-biDkV.js';
3
- export { B as BackupCapabilities, a as BackupRestorePlan, b as BackupSafeToDeleteOptions, c as backupPiecesSafeToDelete, p as planBackupRestore } from '../chain-queries-BzoS648g.js';
1
+ import { j as BackupDestination, k as BackupRunReport, a as SQLiteDriver } from '../types-Xb0L3o3w.js';
2
+ export { g as BackupChain, e as BackupChainBase, f as BackupChainChange, l as BackupChainPosition, h as BackupChainRecord, m as BackupCycle, n as BackupCycleOptions, o as BackupGroupMembership, p as BackupGroupSource, q as BackupNodePreference, r as BackupPiece, s as BackupProgress, t as BackupSkip, u as BackupSkipReason, v as BackupToDestinationOptions, w as DEFAULT_CHAIN_NAME, x as chainLogName, y as readBackupChains } from '../types-Xb0L3o3w.js';
3
+ export { B as BackupCapabilities, a as BackupRestorePlan, b as BackupSafeToDeleteOptions, c as backupPiecesSafeToDelete, p as planBackupRestore } from '../chain-queries-D5aZ-8v2.js';
4
4
  import '../query-types-Cv8N7pXj.js';
5
5
 
6
6
  /** What one assembled file took to build.
@@ -1,9 +1,9 @@
1
1
  import { planBackupRestore } from '../chunk-YNTDOFTN.mjs';
2
2
  export { backupPiecesSafeToDelete, planBackupRestore } from '../chunk-YNTDOFTN.mjs';
3
3
  import { checkpointLog, LOG_HEADER_BYTES, LOG_FRAME_HEADER_BYTES, readLogHeader, foldLogChecksum, MAGIC_WITH_LITTLE_ENDIAN_CONTENT, LOG_FORMAT_VERSION } from '../chunk-72I5UI5C.mjs';
4
- import { destinationWithDeadline, DEFAULT_DESTINATION_TIMEOUT_MS } from '../chunk-TI6NWCHJ.mjs';
5
- import { readBackupChains, DEFAULT_CHAIN_NAME } from '../chunk-4H45GMYI.mjs';
6
- export { DEFAULT_CHAIN_NAME, chainLogName, readBackupChains } from '../chunk-4H45GMYI.mjs';
4
+ import { destinationWithDeadline, DEFAULT_DESTINATION_TIMEOUT_MS } from '../chunk-GBM4AU24.mjs';
5
+ import { readBackupChains, DEFAULT_CHAIN_NAME } from '../chunk-YVF2ZJTE.mjs';
6
+ export { DEFAULT_CHAIN_NAME, chainLogName, readBackupChains } from '../chunk-YVF2ZJTE.mjs';
7
7
  import { randomHex } from '../chunk-V5FUJEYY.mjs';
8
8
  import { SirannonError } from '../chunk-PBRXXISQ.mjs';
9
9
  import { rename, open, rm, stat } from 'fs/promises';
@@ -1,4 +1,4 @@
1
- import { i as SQLiteConnection, u as BackupRunRequest, k as BackupRunReport, v as BackupScheduleOptions } from '../types-Bm-biDkV.js';
1
+ import { i as SQLiteConnection, z as BackupRunRequest, k as BackupRunReport, E as BackupScheduleOptions } from '../types-Xb0L3o3w.js';
2
2
  import '../query-types-Cv8N7pXj.js';
3
3
 
4
4
  /** What one runtime needs before a copy can reach a destination without a local file.
@@ -1,5 +1,5 @@
1
- export { BackupScheduler } from '../chunk-T65NT3SH.mjs';
2
- import '../chunk-TI6NWCHJ.mjs';
1
+ export { BackupScheduler } from '../chunk-BQTVNI5H.mjs';
2
+ import '../chunk-GBM4AU24.mjs';
3
3
  import '../chunk-W67A4H2E.mjs';
4
4
  import '../chunk-V5FUJEYY.mjs';
5
5
  import '../chunk-PBRXXISQ.mjs';
@@ -1,4 +1,4 @@
1
- import { e as BackupChainBase, f as BackupChainChange, g as BackupChain, h as BackupChainRecord } from './types-Bm-biDkV.js';
1
+ import { e as BackupChainBase, f as BackupChainChange, g as BackupChain, h as BackupChainRecord } from './types-Xb0L3o3w.js';
2
2
 
3
3
  /**
4
4
  * What one runtime supports of the backup operations, so a caller learns
@@ -1,4 +1,4 @@
1
- import { i as SQLiteConnection } from './types-Bm-biDkV.js';
1
+ import { i as SQLiteConnection } from './types-Xb0L3o3w.js';
2
2
  import { C as ChangeEvent } from './query-types-Cv8N7pXj.js';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { destinationWithDeadline, DEFAULT_DESTINATION_TIMEOUT_MS } from './chunk-TI6NWCHJ.mjs';
1
+ import { destinationWithDeadline, DEFAULT_DESTINATION_TIMEOUT_MS } from './chunk-GBM4AU24.mjs';
2
2
  import { startCopyWithoutHoldingWriter } from './chunk-W67A4H2E.mjs';
3
3
  import { randomHex } from './chunk-V5FUJEYY.mjs';
4
4
  import { BackupError, SirannonError } from './chunk-PBRXXISQ.mjs';
@@ -28,8 +28,12 @@ function destinationWithDeadline(destination, timeoutMs) {
28
28
  );
29
29
  }
30
30
  if (timeoutMs === 0) return destination;
31
+ const claim = destination.writePieceIfAbsent?.bind(destination);
31
32
  return {
32
33
  writePiece: (name, index, bytes) => withinDeadline(destination.writePiece(name, index, bytes), `store piece ${index} of '${name}'`, timeoutMs),
34
+ ...claim ? {
35
+ writePieceIfAbsent: (name, index, bytes) => withinDeadline(claim(name, index, bytes), `claim piece ${index} of '${name}'`, timeoutMs)
36
+ } : {},
33
37
  readPiece: (name, index) => withinDeadline(destination.readPiece(name, index), `return piece ${index} of '${name}'`, timeoutMs),
34
38
  listPieces: (name) => withinDeadline(destination.listPieces(name), `list the pieces of '${name}'`, timeoutMs)
35
39
  };
@@ -29,6 +29,7 @@ function isBackupChainChange(value) {
29
29
 
30
30
  // src/core/backup/chain.ts
31
31
  var DEFAULT_CHAIN_NAME = "sirannon-backup-chain";
32
+ var HEAD_APPEND_ATTEMPTS = 8;
32
33
  var encoder = new TextEncoder();
33
34
  var decoder = new TextDecoder();
34
35
  function destinationError(message, err) {
@@ -39,34 +40,44 @@ function destinationError(message, err) {
39
40
  function chainLogName(chainName, chainId) {
40
41
  return `${chainName}.${chainId}`;
41
42
  }
42
- async function appendRecord(destination, name, index, record) {
43
+ async function storeRecord(store, name, index, record) {
43
44
  try {
44
- await destination.writePiece(name, index, encoder.encode(JSON.stringify(record)));
45
+ return await store(encoder.encode(JSON.stringify(record)));
45
46
  } catch (err) {
46
47
  throw destinationError(`The destination refused record ${index} of '${name}'`, err);
47
48
  }
48
49
  }
49
- async function readRecords(destination, name) {
50
- let listed;
50
+ function appendRecord(destination, name, index, record) {
51
+ return storeRecord((bytes) => destination.writePiece(name, index, bytes), name, index, record);
52
+ }
53
+ function claimRecord(claim, name, index, record) {
54
+ return storeRecord((bytes) => claim(name, index, bytes), name, index, record);
55
+ }
56
+ async function listRecordIndices(destination, name) {
51
57
  try {
52
- listed = await destination.listPieces(name);
58
+ return (await destination.listPieces(name)).map((piece) => piece.index);
53
59
  } catch (err) {
54
60
  throw destinationError(`The destination could not list the records of '${name}'`, err);
55
61
  }
56
- const ordered = [...listed].sort((left, right) => left.index - right.index);
62
+ }
63
+ async function readRecord(destination, name, index) {
64
+ let bytes;
65
+ try {
66
+ bytes = await destination.readPiece(name, index);
67
+ } catch (err) {
68
+ throw destinationError(`The destination could not return record ${index} of '${name}'`, err);
69
+ }
70
+ try {
71
+ return JSON.parse(decoder.decode(bytes));
72
+ } catch (err) {
73
+ throw destinationError(`Record ${index} of '${name}' is not a record Sirannon wrote`, err);
74
+ }
75
+ }
76
+ async function readRecords(destination, name) {
77
+ const ordered = (await listRecordIndices(destination, name)).sort((left, right) => left - right);
57
78
  const records = [];
58
- for (const piece of ordered) {
59
- let bytes;
60
- try {
61
- bytes = await destination.readPiece(name, piece.index);
62
- } catch (err) {
63
- throw destinationError(`The destination could not return record ${piece.index} of '${name}'`, err);
64
- }
65
- try {
66
- records.push(JSON.parse(decoder.decode(bytes)));
67
- } catch (err) {
68
- throw destinationError(`Record ${piece.index} of '${name}' is not a record Sirannon wrote`, err);
69
- }
79
+ for (const index of ordered) {
80
+ records.push(await readRecord(destination, name, index));
70
81
  }
71
82
  return records;
72
83
  }
@@ -86,12 +97,32 @@ function chainRecords(records, name) {
86
97
  }
87
98
  return kept;
88
99
  }
89
- async function appendChainHead(destination, chainName, head, index) {
90
- await appendRecord(destination, chainName, index, head);
100
+ async function appendChainHead(destination, chainName, head) {
101
+ const taken = await listRecordIndices(destination, chainName);
102
+ let index = taken.reduce((next, piece) => Math.max(next, piece + 1), 0);
103
+ const claim = destination.writePieceIfAbsent?.bind(destination);
104
+ for (let attempt = 0; attempt < HEAD_APPEND_ATTEMPTS; attempt++) {
105
+ if (claim) {
106
+ if (await claimRecord(claim, chainName, index, head)) return index;
107
+ index++;
108
+ continue;
109
+ }
110
+ await appendRecord(destination, chainName, index, head);
111
+ const stored = await readRecord(destination, chainName, index);
112
+ if (isBackupChainHead(stored) && stored.chainId === head.chainId) return index;
113
+ index++;
114
+ }
115
+ throw destinationError(
116
+ `Another chain took every one of the ${HEAD_APPEND_ATTEMPTS} places Sirannon tried for chain '${head.chainId}' in '${chainName}'. Point one replication group at this chain name, and give any other group a chainName of its own.`
117
+ );
91
118
  }
92
119
  async function appendChainRecord(destination, chainName, record, index) {
93
120
  await appendRecord(destination, chainLogName(chainName, record.chainId), index, record);
94
121
  }
122
+ async function readChainHeadAt(destination, chainName, index) {
123
+ const record = await readRecord(destination, chainName, index);
124
+ return isBackupChainHead(record) ? record : null;
125
+ }
95
126
  async function readChainHeads(destination, chainName) {
96
127
  const records = await readRecords(destination, chainName);
97
128
  return records.filter(isBackupChainHead).reverse();
@@ -115,4 +146,4 @@ async function readBackupChains(destination, chainName = DEFAULT_CHAIN_NAME) {
115
146
  return chains;
116
147
  }
117
148
 
118
- export { DEFAULT_CHAIN_NAME, appendChainHead, appendChainRecord, chainLogName, isBackupChainPosition, readBackupChains, readChainHeads };
149
+ export { DEFAULT_CHAIN_NAME, appendChainHead, appendChainRecord, chainLogName, isBackupChainPosition, readBackupChains, readChainHeadAt, readChainHeads };