@bsv/overlay 2.3.1 → 2.6.1

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 (201) hide show
  1. package/README.md +166 -0
  2. package/dist/cjs/mod.d.ts +3 -0
  3. package/dist/cjs/mod.d.ts.map +1 -1
  4. package/dist/cjs/mod.js +8 -1
  5. package/dist/cjs/mod.js.map +1 -1
  6. package/dist/cjs/package.json +10 -4
  7. package/dist/cjs/src/BASM.d.ts +6 -0
  8. package/dist/cjs/src/BASM.d.ts.map +1 -1
  9. package/dist/cjs/src/BASM.js +21 -8
  10. package/dist/cjs/src/BASM.js.map +1 -1
  11. package/dist/cjs/src/BASMRemote.d.ts +8 -1
  12. package/dist/cjs/src/BASMRemote.d.ts.map +1 -1
  13. package/dist/cjs/src/BASMRemote.js +179 -13
  14. package/dist/cjs/src/BASMRemote.js.map +1 -1
  15. package/dist/cjs/src/BASMValidation.d.ts +27 -0
  16. package/dist/cjs/src/BASMValidation.d.ts.map +1 -0
  17. package/dist/cjs/src/BASMValidation.js +108 -0
  18. package/dist/cjs/src/BASMValidation.js.map +1 -0
  19. package/dist/cjs/src/DiscoveryAdvertisementValidation.d.ts +11 -0
  20. package/dist/cjs/src/DiscoveryAdvertisementValidation.d.ts.map +1 -0
  21. package/dist/cjs/src/DiscoveryAdvertisementValidation.js +60 -0
  22. package/dist/cjs/src/DiscoveryAdvertisementValidation.js.map +1 -0
  23. package/dist/cjs/src/Engine.d.ts +37 -13
  24. package/dist/cjs/src/Engine.d.ts.map +1 -1
  25. package/dist/cjs/src/Engine.js +1713 -336
  26. package/dist/cjs/src/Engine.js.map +1 -1
  27. package/dist/cjs/src/EngineAdmission.d.ts +70 -0
  28. package/dist/cjs/src/EngineAdmission.d.ts.map +1 -0
  29. package/dist/cjs/src/EngineAdmission.js +297 -0
  30. package/dist/cjs/src/EngineAdmission.js.map +1 -0
  31. package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts +4 -3
  32. package/dist/cjs/src/GASP/OverlayGASPRemote.d.ts.map +1 -1
  33. package/dist/cjs/src/GASP/OverlayGASPRemote.js +27 -43
  34. package/dist/cjs/src/GASP/OverlayGASPRemote.js.map +1 -1
  35. package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts +22 -16
  36. package/dist/cjs/src/GASP/OverlayGASPStorage.d.ts.map +1 -1
  37. package/dist/cjs/src/GASP/OverlayGASPStorage.js +206 -57
  38. package/dist/cjs/src/GASP/OverlayGASPStorage.js.map +1 -1
  39. package/dist/cjs/src/RemoteSecurity.d.ts +50 -0
  40. package/dist/cjs/src/RemoteSecurity.d.ts.map +1 -0
  41. package/dist/cjs/src/RemoteSecurity.js +301 -0
  42. package/dist/cjs/src/RemoteSecurity.js.map +1 -0
  43. package/dist/cjs/src/storage/AdmissionStorage.d.ts +185 -0
  44. package/dist/cjs/src/storage/AdmissionStorage.d.ts.map +1 -0
  45. package/dist/cjs/src/storage/AdmissionStorage.js +93 -0
  46. package/dist/cjs/src/storage/AdmissionStorage.js.map +1 -0
  47. package/dist/cjs/src/storage/RecoveryContract.d.ts +50 -0
  48. package/dist/cjs/src/storage/RecoveryContract.d.ts.map +1 -0
  49. package/dist/cjs/src/storage/RecoveryContract.js +35 -0
  50. package/dist/cjs/src/storage/RecoveryContract.js.map +1 -0
  51. package/dist/cjs/src/storage/Storage.d.ts +32 -19
  52. package/dist/cjs/src/storage/Storage.d.ts.map +1 -1
  53. package/dist/cjs/src/storage/Storage.js +5 -0
  54. package/dist/cjs/src/storage/Storage.js.map +1 -1
  55. package/dist/cjs/src/storage/knex/KnexStorage.d.ts +8 -8
  56. package/dist/cjs/src/storage/knex/KnexStorage.d.ts.map +1 -1
  57. package/dist/cjs/src/storage/knex/KnexStorage.js +98 -61
  58. package/dist/cjs/src/storage/knex/KnexStorage.js.map +1 -1
  59. package/dist/cjs/src/storage/knex/all-migrations.d.ts.map +1 -1
  60. package/dist/cjs/src/storage/knex/all-migrations.js +5 -1
  61. package/dist/cjs/src/storage/knex/all-migrations.js.map +1 -1
  62. package/dist/cjs/src/storage/knex/migrations/2026-09-17-001-topical-uniqueness.d.ts +4 -0
  63. package/dist/cjs/src/storage/knex/migrations/2026-09-17-001-topical-uniqueness.d.ts.map +1 -0
  64. package/dist/cjs/src/storage/knex/migrations/2026-09-17-001-topical-uniqueness.js +36 -0
  65. package/dist/cjs/src/storage/knex/migrations/2026-09-17-001-topical-uniqueness.js.map +1 -0
  66. package/dist/cjs/src/storage/knex/migrations/2026-09-20-001-spent-by.d.ts +5 -0
  67. package/dist/cjs/src/storage/knex/migrations/2026-09-20-001-spent-by.d.ts.map +1 -0
  68. package/dist/cjs/src/storage/knex/migrations/2026-09-20-001-spent-by.js +16 -0
  69. package/dist/cjs/src/storage/knex/migrations/2026-09-20-001-spent-by.js.map +1 -0
  70. package/dist/cjs/src/storage/mongo/MongoAdmissionPlan.d.ts +18 -0
  71. package/dist/cjs/src/storage/mongo/MongoAdmissionPlan.d.ts.map +1 -0
  72. package/dist/cjs/src/storage/mongo/MongoAdmissionPlan.js +229 -0
  73. package/dist/cjs/src/storage/mongo/MongoAdmissionPlan.js.map +1 -0
  74. package/dist/cjs/src/storage/mongo/MongoAdmissionStorage.d.ts +112 -0
  75. package/dist/cjs/src/storage/mongo/MongoAdmissionStorage.d.ts.map +1 -0
  76. package/dist/cjs/src/storage/mongo/MongoAdmissionStorage.js +675 -0
  77. package/dist/cjs/src/storage/mongo/MongoAdmissionStorage.js.map +1 -0
  78. package/dist/cjs/src/storage/mongo/MongoOverlayStorage.d.ts +71 -0
  79. package/dist/cjs/src/storage/mongo/MongoOverlayStorage.d.ts.map +1 -0
  80. package/dist/cjs/src/storage/mongo/MongoOverlayStorage.js +453 -0
  81. package/dist/cjs/src/storage/mongo/MongoOverlayStorage.js.map +1 -0
  82. package/dist/cjs/src/storage/mongo/MongoPayloadStore.d.ts +134 -0
  83. package/dist/cjs/src/storage/mongo/MongoPayloadStore.d.ts.map +1 -0
  84. package/dist/cjs/src/storage/mongo/MongoPayloadStore.js +756 -0
  85. package/dist/cjs/src/storage/mongo/MongoPayloadStore.js.map +1 -0
  86. package/dist/cjs/src/storage/mongo/MongoReadGuards.d.ts +44 -0
  87. package/dist/cjs/src/storage/mongo/MongoReadGuards.d.ts.map +1 -0
  88. package/dist/cjs/src/storage/mongo/MongoReadGuards.js +135 -0
  89. package/dist/cjs/src/storage/mongo/MongoReadGuards.js.map +1 -0
  90. package/dist/cjs/src/storage/mongo/MongoSchema.d.ts +67 -0
  91. package/dist/cjs/src/storage/mongo/MongoSchema.d.ts.map +1 -0
  92. package/dist/cjs/src/storage/mongo/MongoSchema.js +743 -0
  93. package/dist/cjs/src/storage/mongo/MongoSchema.js.map +1 -0
  94. package/dist/cjs/src/storage/mongo/MongoTransactionRunner.d.ts +69 -0
  95. package/dist/cjs/src/storage/mongo/MongoTransactionRunner.d.ts.map +1 -0
  96. package/dist/cjs/src/storage/mongo/MongoTransactionRunner.js +368 -0
  97. package/dist/cjs/src/storage/mongo/MongoTransactionRunner.js.map +1 -0
  98. package/dist/cjs/src/storage/mongo.d.ts +9 -0
  99. package/dist/cjs/src/storage/mongo.d.ts.map +1 -0
  100. package/dist/cjs/src/storage/mongo.js +25 -0
  101. package/dist/cjs/src/storage/mongo.js.map +1 -0
  102. package/dist/esm/mod.js +3 -0
  103. package/dist/esm/mod.js.map +1 -1
  104. package/dist/esm/src/BASM.js +21 -8
  105. package/dist/esm/src/BASM.js.map +1 -1
  106. package/dist/esm/src/BASMRemote.js +176 -12
  107. package/dist/esm/src/BASMRemote.js.map +1 -1
  108. package/dist/esm/src/BASMValidation.js +95 -0
  109. package/dist/esm/src/BASMValidation.js.map +1 -0
  110. package/dist/esm/src/DiscoveryAdvertisementValidation.js +57 -0
  111. package/dist/esm/src/DiscoveryAdvertisementValidation.js.map +1 -0
  112. package/dist/esm/src/Engine.js +1698 -326
  113. package/dist/esm/src/Engine.js.map +1 -1
  114. package/dist/esm/src/EngineAdmission.js +280 -0
  115. package/dist/esm/src/EngineAdmission.js.map +1 -0
  116. package/dist/esm/src/GASP/OverlayGASPRemote.js +27 -43
  117. package/dist/esm/src/GASP/OverlayGASPRemote.js.map +1 -1
  118. package/dist/esm/src/GASP/OverlayGASPStorage.js +200 -56
  119. package/dist/esm/src/GASP/OverlayGASPStorage.js.map +1 -1
  120. package/dist/esm/src/RemoteSecurity.js +279 -0
  121. package/dist/esm/src/RemoteSecurity.js.map +1 -0
  122. package/dist/esm/src/storage/AdmissionStorage.js +85 -0
  123. package/dist/esm/src/storage/AdmissionStorage.js.map +1 -0
  124. package/dist/esm/src/storage/RecoveryContract.js +31 -0
  125. package/dist/esm/src/storage/RecoveryContract.js.map +1 -0
  126. package/dist/esm/src/storage/Storage.js +4 -1
  127. package/dist/esm/src/storage/Storage.js.map +1 -1
  128. package/dist/esm/src/storage/knex/KnexStorage.js +98 -61
  129. package/dist/esm/src/storage/knex/KnexStorage.js.map +1 -1
  130. package/dist/esm/src/storage/knex/all-migrations.js +5 -1
  131. package/dist/esm/src/storage/knex/all-migrations.js.map +1 -1
  132. package/dist/esm/src/storage/knex/migrations/2026-09-17-001-topical-uniqueness.js +32 -0
  133. package/dist/esm/src/storage/knex/migrations/2026-09-17-001-topical-uniqueness.js.map +1 -0
  134. package/dist/esm/src/storage/knex/migrations/2026-09-20-001-spent-by.js +12 -0
  135. package/dist/esm/src/storage/knex/migrations/2026-09-20-001-spent-by.js.map +1 -0
  136. package/dist/esm/src/storage/mongo/MongoAdmissionPlan.js +217 -0
  137. package/dist/esm/src/storage/mongo/MongoAdmissionPlan.js.map +1 -0
  138. package/dist/esm/src/storage/mongo/MongoAdmissionStorage.js +673 -0
  139. package/dist/esm/src/storage/mongo/MongoAdmissionStorage.js.map +1 -0
  140. package/dist/esm/src/storage/mongo/MongoOverlayStorage.js +451 -0
  141. package/dist/esm/src/storage/mongo/MongoOverlayStorage.js.map +1 -0
  142. package/dist/esm/src/storage/mongo/MongoPayloadStore.js +747 -0
  143. package/dist/esm/src/storage/mongo/MongoPayloadStore.js.map +1 -0
  144. package/dist/esm/src/storage/mongo/MongoReadGuards.js +129 -0
  145. package/dist/esm/src/storage/mongo/MongoReadGuards.js.map +1 -0
  146. package/dist/esm/src/storage/mongo/MongoSchema.js +730 -0
  147. package/dist/esm/src/storage/mongo/MongoSchema.js.map +1 -0
  148. package/dist/esm/src/storage/mongo/MongoTransactionRunner.js +371 -0
  149. package/dist/esm/src/storage/mongo/MongoTransactionRunner.js.map +1 -0
  150. package/dist/esm/src/storage/mongo.js +9 -0
  151. package/dist/esm/src/storage/mongo.js.map +1 -0
  152. package/dist/types/mod.d.ts +3 -0
  153. package/dist/types/mod.d.ts.map +1 -1
  154. package/dist/types/src/BASM.d.ts +6 -0
  155. package/dist/types/src/BASM.d.ts.map +1 -1
  156. package/dist/types/src/BASMRemote.d.ts +8 -1
  157. package/dist/types/src/BASMRemote.d.ts.map +1 -1
  158. package/dist/types/src/BASMValidation.d.ts +27 -0
  159. package/dist/types/src/BASMValidation.d.ts.map +1 -0
  160. package/dist/types/src/DiscoveryAdvertisementValidation.d.ts +11 -0
  161. package/dist/types/src/DiscoveryAdvertisementValidation.d.ts.map +1 -0
  162. package/dist/types/src/Engine.d.ts +37 -13
  163. package/dist/types/src/Engine.d.ts.map +1 -1
  164. package/dist/types/src/EngineAdmission.d.ts +70 -0
  165. package/dist/types/src/EngineAdmission.d.ts.map +1 -0
  166. package/dist/types/src/GASP/OverlayGASPRemote.d.ts +4 -3
  167. package/dist/types/src/GASP/OverlayGASPRemote.d.ts.map +1 -1
  168. package/dist/types/src/GASP/OverlayGASPStorage.d.ts +22 -16
  169. package/dist/types/src/GASP/OverlayGASPStorage.d.ts.map +1 -1
  170. package/dist/types/src/RemoteSecurity.d.ts +50 -0
  171. package/dist/types/src/RemoteSecurity.d.ts.map +1 -0
  172. package/dist/types/src/storage/AdmissionStorage.d.ts +185 -0
  173. package/dist/types/src/storage/AdmissionStorage.d.ts.map +1 -0
  174. package/dist/types/src/storage/RecoveryContract.d.ts +50 -0
  175. package/dist/types/src/storage/RecoveryContract.d.ts.map +1 -0
  176. package/dist/types/src/storage/Storage.d.ts +32 -19
  177. package/dist/types/src/storage/Storage.d.ts.map +1 -1
  178. package/dist/types/src/storage/knex/KnexStorage.d.ts +8 -8
  179. package/dist/types/src/storage/knex/KnexStorage.d.ts.map +1 -1
  180. package/dist/types/src/storage/knex/all-migrations.d.ts.map +1 -1
  181. package/dist/types/src/storage/knex/migrations/2026-09-17-001-topical-uniqueness.d.ts +4 -0
  182. package/dist/types/src/storage/knex/migrations/2026-09-17-001-topical-uniqueness.d.ts.map +1 -0
  183. package/dist/types/src/storage/knex/migrations/2026-09-20-001-spent-by.d.ts +5 -0
  184. package/dist/types/src/storage/knex/migrations/2026-09-20-001-spent-by.d.ts.map +1 -0
  185. package/dist/types/src/storage/mongo/MongoAdmissionPlan.d.ts +18 -0
  186. package/dist/types/src/storage/mongo/MongoAdmissionPlan.d.ts.map +1 -0
  187. package/dist/types/src/storage/mongo/MongoAdmissionStorage.d.ts +112 -0
  188. package/dist/types/src/storage/mongo/MongoAdmissionStorage.d.ts.map +1 -0
  189. package/dist/types/src/storage/mongo/MongoOverlayStorage.d.ts +71 -0
  190. package/dist/types/src/storage/mongo/MongoOverlayStorage.d.ts.map +1 -0
  191. package/dist/types/src/storage/mongo/MongoPayloadStore.d.ts +134 -0
  192. package/dist/types/src/storage/mongo/MongoPayloadStore.d.ts.map +1 -0
  193. package/dist/types/src/storage/mongo/MongoReadGuards.d.ts +44 -0
  194. package/dist/types/src/storage/mongo/MongoReadGuards.d.ts.map +1 -0
  195. package/dist/types/src/storage/mongo/MongoSchema.d.ts +67 -0
  196. package/dist/types/src/storage/mongo/MongoSchema.d.ts.map +1 -0
  197. package/dist/types/src/storage/mongo/MongoTransactionRunner.d.ts +69 -0
  198. package/dist/types/src/storage/mongo/MongoTransactionRunner.d.ts.map +1 -0
  199. package/dist/types/src/storage/mongo.d.ts +9 -0
  200. package/dist/types/src/storage/mongo.d.ts.map +1 -0
  201. package/package.json +11 -5
@@ -0,0 +1,730 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { asStorageUint64, parseStorageOutputIndex, parseStorageUint64 } from '../AdmissionStorage.js';
3
+ export const MongoCollectionNames = {
4
+ schema: 'overlay_schema',
5
+ payloads: 'overlay_payloads',
6
+ payloadReferences: 'overlay_payload_references',
7
+ submissionOperations: 'overlay_submission_operations',
8
+ readGuards: 'overlay_read_guards',
9
+ transactions: 'overlay_transactions',
10
+ transactionProofs: 'overlay_transaction_proofs',
11
+ outputs: 'overlay_outputs',
12
+ consumptionEdges: 'overlay_consumption_edges',
13
+ appliedTransactions: 'overlay_applied_transactions',
14
+ topicGenerations: 'overlay_topic_generations',
15
+ topicAnchors: 'overlay_topic_anchors',
16
+ topicAnchorTips: 'overlay_topic_anchor_tips',
17
+ basmRecoveryJobs: 'overlay_basm_recovery_jobs',
18
+ basmComparisons: 'overlay_basm_comparisons',
19
+ gaspCursors: 'overlay_gasp_cursors',
20
+ gaspGraphs: 'overlay_gasp_graphs',
21
+ gaspNodes: 'overlay_gasp_nodes',
22
+ manifestComponents: 'overlay_manifest_components',
23
+ lookupOutbox: 'overlay_lookup_outbox',
24
+ propagationOutbox: 'overlay_propagation_outbox',
25
+ shipRecords: 'overlay_ship_records',
26
+ slapRecords: 'overlay_slap_records',
27
+ bannedRecords: 'overlay_banned_records'
28
+ };
29
+ /** GridFS bucket for payload bytes that cannot be stored inline. */
30
+ export const MongoGridFsBucketName = 'overlayPayloads';
31
+ const schemaVersion = 1;
32
+ const maxUint64 = '18446744073709551615';
33
+ const maxUint32 = 4294967295;
34
+ const paddedUint64Pattern = String.raw `^\d{20}$`;
35
+ const hashPattern = '^[0-9a-f]{64}$';
36
+ const uuidPattern = '^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$';
37
+ const safeStringPattern = String.raw `^[^\x00]+$`;
38
+ function validPart(value) {
39
+ if (typeof value !== 'string' ||
40
+ value.length === 0 ||
41
+ Buffer.byteLength(value, 'utf8') > 1024 ||
42
+ value.includes('\u0000') ||
43
+ !value.isWellFormed()) {
44
+ throw new Error('Invalid Mongo record key component');
45
+ }
46
+ return value;
47
+ }
48
+ /**
49
+ * A length-framed, UTF-8 tuple. It remains unambiguous when values contain
50
+ * dots, colons, or separators and is safe to use only as a BSON value, never
51
+ * as a dynamic BSON field name.
52
+ */
53
+ export function mongoRecordKey(...parts) {
54
+ if (parts.length === 0 || parts.length > 32)
55
+ throw new Error('Invalid Mongo record key');
56
+ const encoded = parts.map(part => {
57
+ const valid = validPart(part);
58
+ return `${Buffer.byteLength(valid, 'utf8')}:${valid}`;
59
+ });
60
+ const result = `v1|${encoded.join('|')}`;
61
+ if (Buffer.byteLength(result, 'utf8') > 4096)
62
+ throw new Error('Mongo record key is too large');
63
+ return result;
64
+ }
65
+ function validChainScope(scope) {
66
+ if (typeof scope !== 'object' ||
67
+ scope === null ||
68
+ typeof scope.network !== 'string' ||
69
+ typeof scope.genesisHash !== 'string' ||
70
+ Buffer.byteLength(validPart(scope.network), 'utf8') > 128 ||
71
+ !new RegExp(hashPattern).test(scope.genesisHash)) {
72
+ throw new Error('Invalid Mongo chain scope');
73
+ }
74
+ return scope;
75
+ }
76
+ export function mongoChainKey(scope) {
77
+ const valid = validChainScope(scope);
78
+ return mongoRecordKey('chain', valid.network, valid.genesisHash);
79
+ }
80
+ export function mongoNodeKey(scope) {
81
+ const valid = validChainScope(scope);
82
+ return mongoRecordKey('node', valid.network, valid.genesisHash, validPart(scope.nodeId));
83
+ }
84
+ /** Stores a canonical S01 uint64 in an exact, lexically sortable BSON string. */
85
+ export function encodeMongoUint64(value) {
86
+ const parsed = parseStorageUint64(value);
87
+ return parsed.toString(10).padStart(20, '0');
88
+ }
89
+ /** Strict inverse of encodeMongoUint64; rejects non-canonical padded values. */
90
+ export function decodeMongoUint64(value) {
91
+ if (typeof value !== 'string' || !new RegExp(paddedUint64Pattern).test(value))
92
+ throw new Error('Invalid Mongo uint64');
93
+ if (value > maxUint64)
94
+ throw new Error('Invalid Mongo uint64');
95
+ const decoded = BigInt(value).toString(10);
96
+ if (encodeMongoUint64(decoded) !== value)
97
+ throw new Error('Invalid Mongo uint64');
98
+ return asStorageUint64(decoded);
99
+ }
100
+ /** Validates the wire uint32 domain and returns its canonical decimal spelling. */
101
+ export function encodeMongoOutputIndex(value) {
102
+ return String(parseStorageOutputIndex(value));
103
+ }
104
+ const string = (maxLength = 1024) => ({
105
+ bsonType: 'string',
106
+ pattern: safeStringPattern,
107
+ minLength: 1,
108
+ maxLength
109
+ });
110
+ const date = { bsonType: 'date' };
111
+ const hash = { bsonType: 'string', pattern: hashPattern };
112
+ const uint64 = { bsonType: 'string', pattern: paddedUint64Pattern };
113
+ const uint32 = { bsonType: 'string', pattern: '^(0|[1-9][0-9]{0,9})$' };
114
+ const schemaV1 = { bsonType: 'int', enum: [schemaVersion] };
115
+ const chainFields = { network: string(128), genesisHash: hash };
116
+ const nodeFields = { ...chainFields, nodeId: string(256) };
117
+ const commonFields = {
118
+ schemaVersion: schemaV1,
119
+ createdAt: date,
120
+ updatedAt: date
121
+ };
122
+ function scopedValidator(fields, required, options = {}) {
123
+ const scopeFields = options.node ? nodeFields : chainFields;
124
+ const uint64Bounds = Object.entries(fields)
125
+ .filter(([, definition]) => definition === uint64)
126
+ .map(([field]) => ({
127
+ $or: [{ $eq: [{ $type: `$${field}` }, 'missing'] }, { $lte: [`$${field}`, maxUint64] }]
128
+ }));
129
+ // Unpadded canonical uint32 strings are not lexicographically ordered; compare as integers.
130
+ const uint32Bounds = Object.entries(fields)
131
+ .filter(([, definition]) => definition === uint32)
132
+ .map(([field]) => ({
133
+ $or: [
134
+ { $eq: [{ $type: `$${field}` }, 'missing'] },
135
+ { $lte: [{ $toLong: `$${field}` }, maxUint32] }
136
+ ]
137
+ }));
138
+ return {
139
+ $and: [
140
+ {
141
+ $jsonSchema: {
142
+ bsonType: 'object',
143
+ additionalProperties: options.allowAdditional ?? false,
144
+ required: [
145
+ '_id',
146
+ 'schemaVersion',
147
+ 'createdAt',
148
+ 'updatedAt',
149
+ ...Object.keys(scopeFields),
150
+ ...required
151
+ ],
152
+ properties: { _id: string(4096), ...commonFields, ...scopeFields, ...fields }
153
+ }
154
+ },
155
+ ...uint64Bounds.map(bound => ({ $expr: bound })),
156
+ ...uint32Bounds.map(bound => ({ $expr: bound })),
157
+ ...(options.extra === undefined ? [] : [options.extra])
158
+ ]
159
+ };
160
+ }
161
+ const index = (key, name, options = {}) => ({
162
+ key,
163
+ name,
164
+ ...options
165
+ });
166
+ const definitions = [
167
+ {
168
+ name: MongoCollectionNames.schema,
169
+ validator: scopedValidator({ schemaFingerprint: hash, lastTransactionProbeAt: date }, ['schemaFingerprint'], { node: true }),
170
+ indexes: [index({ network: 1, genesisHash: 1, nodeId: 1 }, 'scope_unique', { unique: true })]
171
+ },
172
+ {
173
+ name: MongoCollectionNames.payloads,
174
+ validator: scopedValidator({
175
+ kind: string(64),
176
+ digest: hash,
177
+ byteLength: uint64,
178
+ state: { bsonType: 'string', enum: ['uploading', 'ready', 'deleting', 'deleted'] },
179
+ guard: string(256),
180
+ ownerNodeId: string(256),
181
+ ownerId: string(1024),
182
+ fencingToken: uint64,
183
+ leaseUntil: date,
184
+ fileId: { bsonType: 'objectId' },
185
+ inlineData: { bsonType: 'binData' },
186
+ retiredFileId: { bsonType: 'objectId' },
187
+ uploadId: string(256)
188
+ }, [
189
+ 'kind',
190
+ 'digest',
191
+ 'byteLength',
192
+ 'state',
193
+ 'guard',
194
+ 'ownerNodeId',
195
+ 'ownerId',
196
+ 'fencingToken',
197
+ 'leaseUntil'
198
+ ]),
199
+ indexes: [
200
+ index({ network: 1, genesisHash: 1, kind: 1, digest: 1 }, 'chain_kind_digest_unique', {
201
+ unique: true
202
+ }),
203
+ index({ network: 1, genesisHash: 1, state: 1, updatedAt: 1 }, 'chain_state_updated')
204
+ ]
205
+ },
206
+ {
207
+ name: MongoCollectionNames.payloadReferences,
208
+ validator: scopedValidator({
209
+ payloadId: string(4096),
210
+ ownerKind: {
211
+ bsonType: 'string',
212
+ enum: [
213
+ 'transaction',
214
+ 'applied-history',
215
+ 'output',
216
+ 'gasp-graph',
217
+ 'gasp-node',
218
+ 'basm-job',
219
+ 'lookup-outbox',
220
+ 'propagation-outbox',
221
+ 'manifest',
222
+ 'pin'
223
+ ]
224
+ },
225
+ ownerId: string(4096),
226
+ slot: string(256),
227
+ expiresAt: date
228
+ }, ['payloadId', 'ownerKind', 'ownerId', 'slot'], {
229
+ node: true,
230
+ extra: {
231
+ $expr: {
232
+ $eq: [{ $eq: ['$ownerKind', 'pin'] }, { $ne: [{ $type: '$expiresAt' }, 'missing'] }]
233
+ }
234
+ }
235
+ }),
236
+ indexes: [
237
+ index({ network: 1, genesisHash: 1, nodeId: 1, ownerKind: 1, ownerId: 1, slot: 1 }, 'owner_slot_unique', {
238
+ unique: true
239
+ }),
240
+ index({ payloadId: 1 }, 'payload_reference'),
241
+ index({ expiresAt: 1 }, 'pin_expiry', { partialFilterExpression: { ownerKind: 'pin' } })
242
+ ]
243
+ },
244
+ {
245
+ name: MongoCollectionNames.submissionOperations,
246
+ validator: scopedValidator({
247
+ operationId: string(1024),
248
+ semanticDigest: hash,
249
+ txid: hash,
250
+ state: { bsonType: 'string', enum: ['pending', 'committed', 'aborted'] },
251
+ attemptId: { bsonType: 'string', pattern: uuidPattern },
252
+ leaseOwner: { bsonType: 'string', pattern: uuidPattern },
253
+ leaseToken: uint64,
254
+ leaseUntil: date,
255
+ guard: { bsonType: 'string', pattern: uuidPattern },
256
+ receipt: { bsonType: 'binData' }
257
+ }, [
258
+ 'operationId',
259
+ 'semanticDigest',
260
+ 'txid',
261
+ 'state',
262
+ 'attemptId',
263
+ 'leaseOwner',
264
+ 'leaseToken',
265
+ 'leaseUntil',
266
+ 'guard'
267
+ ], {
268
+ node: true,
269
+ extra: {
270
+ $expr: {
271
+ $cond: [
272
+ { $eq: [{ $type: '$receipt' }, 'missing'] },
273
+ true,
274
+ { $lte: [{ $binarySize: '$receipt' }, 1048576] }
275
+ ]
276
+ }
277
+ }
278
+ }),
279
+ indexes: [
280
+ index({ network: 1, genesisHash: 1, nodeId: 1, operationId: 1 }, 'operation_unique', {
281
+ unique: true
282
+ }),
283
+ index({ network: 1, genesisHash: 1, nodeId: 1, txid: 1, state: 1 }, 'scope_tx_state')
284
+ ]
285
+ },
286
+ {
287
+ name: MongoCollectionNames.readGuards,
288
+ validator: scopedValidator({
289
+ key: string(4096),
290
+ version: { bsonType: ['string', 'null'], maxLength: 256 },
291
+ guard: { bsonType: 'string', pattern: uuidPattern }
292
+ }, ['key', 'version', 'guard'], { node: true }),
293
+ indexes: [
294
+ index({ network: 1, genesisHash: 1, nodeId: 1, key: 1, guard: 1 }, 'read_guard_unique', {
295
+ unique: true
296
+ }),
297
+ index({ guard: 1 }, 'guard_lookup')
298
+ ]
299
+ },
300
+ {
301
+ name: MongoCollectionNames.transactions,
302
+ validator: scopedValidator({
303
+ txid: hash,
304
+ rawPayloadId: string(4096),
305
+ manifestPayloadId: string(4096),
306
+ blockHash: hash,
307
+ blockHeight: uint64
308
+ }, ['txid']),
309
+ indexes: [
310
+ index({ network: 1, genesisHash: 1, txid: 1 }, 'chain_txid_unique', { unique: true }),
311
+ index({ network: 1, genesisHash: 1, blockHash: 1, blockHeight: 1 }, 'chain_block')
312
+ ]
313
+ },
314
+ {
315
+ name: MongoCollectionNames.transactionProofs,
316
+ validator: scopedValidator({
317
+ txid: hash,
318
+ proofPayloadId: string(4096),
319
+ blockHash: hash,
320
+ blockHeight: uint64,
321
+ variantDigest: hash
322
+ }, ['txid', 'proofPayloadId', 'variantDigest']),
323
+ indexes: [
324
+ index({ network: 1, genesisHash: 1, txid: 1, variantDigest: 1 }, 'transaction_variant_unique', { unique: true })
325
+ ]
326
+ },
327
+ {
328
+ name: MongoCollectionNames.outputs,
329
+ validator: scopedValidator({
330
+ topic: string(256),
331
+ txid: hash,
332
+ outputIndex: uint32,
333
+ satoshis: uint64,
334
+ score: uint64,
335
+ scriptPayloadId: string(4096),
336
+ scriptOffset: uint64,
337
+ scriptByteLength: uint64,
338
+ state: { bsonType: 'string', enum: ['unspent', 'spent', 'evicted'] },
339
+ spender: string(4096),
340
+ version: string(256)
341
+ }, [
342
+ 'topic',
343
+ 'txid',
344
+ 'outputIndex',
345
+ 'satoshis',
346
+ 'score',
347
+ 'scriptPayloadId',
348
+ 'scriptOffset',
349
+ 'scriptByteLength',
350
+ 'state',
351
+ 'version'
352
+ ], { node: true }),
353
+ indexes: [
354
+ index({ network: 1, genesisHash: 1, nodeId: 1, topic: 1, txid: 1, outputIndex: 1 }, 'outpoint_unique', { unique: true }),
355
+ index({ network: 1, genesisHash: 1, nodeId: 1, topic: 1, state: 1, score: 1, _id: 1 }, 'gasp_order')
356
+ ]
357
+ },
358
+ {
359
+ name: MongoCollectionNames.consumptionEdges,
360
+ validator: scopedValidator({
361
+ topic: string(256),
362
+ sourceTxid: hash,
363
+ sourceOutputIndex: uint32,
364
+ consumerTxid: hash,
365
+ consumerOutputIndex: uint32
366
+ }, ['topic', 'sourceTxid', 'sourceOutputIndex', 'consumerTxid', 'consumerOutputIndex'], { node: true }),
367
+ indexes: [
368
+ index({
369
+ network: 1,
370
+ genesisHash: 1,
371
+ nodeId: 1,
372
+ topic: 1,
373
+ sourceTxid: 1,
374
+ sourceOutputIndex: 1,
375
+ consumerTxid: 1,
376
+ consumerOutputIndex: 1
377
+ }, 'edge_unique', { unique: true }),
378
+ index({ network: 1, genesisHash: 1, nodeId: 1, consumerTxid: 1, consumerOutputIndex: 1 }, 'consumer_lookup')
379
+ ]
380
+ },
381
+ {
382
+ name: MongoCollectionNames.appliedTransactions,
383
+ validator: scopedValidator({
384
+ topic: string(256),
385
+ txid: hash,
386
+ state: { bsonType: 'string', enum: ['active', 'unproven', 'evicted'] },
387
+ firstSeenHeight: uint64,
388
+ proofPayloadId: string(4096),
389
+ admissionId: string(4096)
390
+ }, ['topic', 'txid', 'state', 'admissionId'], { node: true }),
391
+ indexes: [
392
+ index({ network: 1, genesisHash: 1, nodeId: 1, topic: 1, txid: 1 }, 'topic_txid_unique', {
393
+ unique: true
394
+ }),
395
+ index({ network: 1, genesisHash: 1, nodeId: 1, topic: 1, state: 1, firstSeenHeight: 1 }, 'topic_state_height')
396
+ ]
397
+ }
398
+ ];
399
+ function stateDefinition(name, fields, required, node, indexes, extra) {
400
+ definitions.push({ name, validator: scopedValidator(fields, required, { node, extra }), indexes });
401
+ }
402
+ stateDefinition(MongoCollectionNames.topicGenerations, { topic: string(256), chainEpoch: uint64, topicHistoryGeneration: uint64, policyId: string(256) }, ['topic', 'chainEpoch', 'topicHistoryGeneration', 'policyId'], true, [index({ network: 1, genesisHash: 1, nodeId: 1, topic: 1 }, 'topic_unique', { unique: true })]);
403
+ stateDefinition(MongoCollectionNames.topicAnchors, {
404
+ topic: string(256),
405
+ chainEpoch: uint64,
406
+ topicHistoryGeneration: uint64,
407
+ height: uint64,
408
+ blockHash: hash,
409
+ blockIndex: uint64,
410
+ basmRoot: hash,
411
+ tac: hash,
412
+ previousTac: hash,
413
+ state: { bsonType: 'string', enum: ['canonical', 'noncanonical'] }
414
+ }, [
415
+ 'topic',
416
+ 'chainEpoch',
417
+ 'topicHistoryGeneration',
418
+ 'height',
419
+ 'blockHash',
420
+ 'blockIndex',
421
+ 'basmRoot',
422
+ 'tac',
423
+ 'previousTac',
424
+ 'state'
425
+ ], true, [
426
+ index({
427
+ network: 1,
428
+ genesisHash: 1,
429
+ nodeId: 1,
430
+ topic: 1,
431
+ chainEpoch: 1,
432
+ topicHistoryGeneration: 1,
433
+ height: 1,
434
+ blockHash: 1
435
+ }, 'anchor_unique', { unique: true })
436
+ ]);
437
+ stateDefinition(MongoCollectionNames.topicAnchorTips, {
438
+ topic: string(256),
439
+ chainEpoch: uint64,
440
+ topicHistoryGeneration: uint64,
441
+ height: uint64,
442
+ blockHash: hash,
443
+ anchorId: string(4096)
444
+ }, ['topic', 'chainEpoch', 'topicHistoryGeneration', 'height', 'blockHash', 'anchorId'], true, [index({ network: 1, genesisHash: 1, nodeId: 1, topic: 1 }, 'tip_unique', { unique: true })]);
445
+ stateDefinition(MongoCollectionNames.basmRecoveryJobs, {
446
+ topic: string(256),
447
+ peerId: string(1024),
448
+ jobId: string(1024),
449
+ chainEpoch: uint64,
450
+ topicHistoryGeneration: uint64,
451
+ leaseToken: uint64,
452
+ leaseUntil: date,
453
+ state: string(64),
454
+ checkpoint: string(4096)
455
+ }, [
456
+ 'topic',
457
+ 'peerId',
458
+ 'jobId',
459
+ 'chainEpoch',
460
+ 'topicHistoryGeneration',
461
+ 'leaseToken',
462
+ 'leaseUntil',
463
+ 'state',
464
+ 'checkpoint'
465
+ ], true, [
466
+ index({ network: 1, genesisHash: 1, nodeId: 1, topic: 1, peerId: 1, jobId: 1 }, 'job_unique', {
467
+ unique: true
468
+ }),
469
+ index({ state: 1, leaseUntil: 1 }, 'lease_state')
470
+ ]);
471
+ stateDefinition(MongoCollectionNames.basmComparisons, {
472
+ topic: string(256),
473
+ peerId: string(1024),
474
+ chainEpoch: uint64,
475
+ topicHistoryGeneration: uint64,
476
+ commonHeight: uint64,
477
+ commonHash: hash,
478
+ state: string(64)
479
+ }, [
480
+ 'topic',
481
+ 'peerId',
482
+ 'chainEpoch',
483
+ 'topicHistoryGeneration',
484
+ 'commonHeight',
485
+ 'commonHash',
486
+ 'state'
487
+ ], true, [
488
+ index({
489
+ network: 1,
490
+ genesisHash: 1,
491
+ nodeId: 1,
492
+ topic: 1,
493
+ chainEpoch: 1,
494
+ topicHistoryGeneration: 1,
495
+ updatedAt: 1
496
+ }, 'comparison_lookup')
497
+ ]);
498
+ stateDefinition(MongoCollectionNames.gaspCursors, {
499
+ remoteHost: string(2048),
500
+ topic: string(256),
501
+ state: string(64),
502
+ score: uint64,
503
+ cursorId: string(4096)
504
+ }, ['remoteHost', 'topic', 'state'], true, [
505
+ index({ network: 1, genesisHash: 1, nodeId: 1, remoteHost: 1, topic: 1 }, 'cursor_unique', {
506
+ unique: true
507
+ })
508
+ ]);
509
+ stateDefinition(MongoCollectionNames.gaspGraphs, {
510
+ graphId: string(4096),
511
+ state: { bsonType: 'string', enum: ['receiving', 'validated', 'finalized', 'discarded'] },
512
+ reason: string(1024)
513
+ }, ['graphId', 'state'], true, [
514
+ index({ network: 1, genesisHash: 1, nodeId: 1, graphId: 1 }, 'graph_unique', { unique: true }),
515
+ index({ state: 1, updatedAt: 1 }, 'graph_state')
516
+ ]);
517
+ stateDefinition(MongoCollectionNames.gaspNodes, {
518
+ graphId: string(4096),
519
+ txid: hash,
520
+ outputIndex: uint32,
521
+ payloadId: string(4096),
522
+ state: string(64)
523
+ }, ['graphId', 'txid', 'outputIndex', 'state'], true, [
524
+ index({ network: 1, genesisHash: 1, nodeId: 1, graphId: 1, txid: 1, outputIndex: 1 }, 'graph_node_unique', { unique: true }),
525
+ index({ graphId: 1 }, 'graph_lookup')
526
+ ]);
527
+ stateDefinition(MongoCollectionNames.manifestComponents, { manifestId: string(4096), ordinal: uint64, payloadId: string(4096), kind: string(64) }, ['manifestId', 'ordinal', 'payloadId', 'kind'], true, [
528
+ index({ network: 1, genesisHash: 1, nodeId: 1, manifestId: 1, ordinal: 1 }, 'manifest_component_unique', { unique: true })
529
+ ]);
530
+ for (const name of [
531
+ MongoCollectionNames.lookupOutbox,
532
+ MongoCollectionNames.propagationOutbox
533
+ ]) {
534
+ stateDefinition(name, {
535
+ eventId: string(1024),
536
+ target: string(2048),
537
+ state: string(64),
538
+ nextAttemptAt: date,
539
+ leaseUntil: date
540
+ }, ['eventId', 'target', 'state'], true, [
541
+ index({ network: 1, genesisHash: 1, nodeId: 1, eventId: 1 }, 'event_unique', {
542
+ unique: true
543
+ }),
544
+ index({ state: 1, nextAttemptAt: 1 }, 'outbox_ready')
545
+ ]);
546
+ }
547
+ for (const name of [MongoCollectionNames.shipRecords, MongoCollectionNames.slapRecords]) {
548
+ stateDefinition(name, {
549
+ txid: hash,
550
+ outputIndex: uint32,
551
+ domain: string(255),
552
+ topic: string(256),
553
+ service: string(256),
554
+ state: string(64)
555
+ }, ['txid', 'outputIndex', 'domain', 'state'], true, [
556
+ index({ network: 1, genesisHash: 1, nodeId: 1, txid: 1, outputIndex: 1 }, 'discovery_unique', { unique: true }),
557
+ index({ domain: 1, topic: 1, createdAt: 1 }, 'discovery_lookup')
558
+ ]);
559
+ }
560
+ stateDefinition(MongoCollectionNames.bannedRecords, { type: string(64), value: string(2048), bannedAt: date, state: string(64) }, ['type', 'value', 'bannedAt', 'state'], true, [
561
+ index({ network: 1, genesisHash: 1, nodeId: 1, type: 1, value: 1 }, 'ban_unique', {
562
+ unique: true
563
+ }),
564
+ index({ bannedAt: 1 }, 'ban_time')
565
+ ]);
566
+ export const MongoCollectionDefinitions = definitions;
567
+ function stable(value) {
568
+ if (Array.isArray(value))
569
+ return `[${value.map(stable).join(',')}]`;
570
+ if (value !== null && typeof value === 'object') {
571
+ const record = value;
572
+ return `{${Object.keys(record)
573
+ .sort((left, right) => left.localeCompare(right, 'en'))
574
+ .map(key => `${JSON.stringify(key)}:${stable(record[key])}`)
575
+ .join(',')}}`;
576
+ }
577
+ return JSON.stringify(value);
578
+ }
579
+ const schemaFingerprint = () => {
580
+ const source = stable(MongoCollectionDefinitions.map(({ name, validator, indexes }) => ({ name, validator, indexes })));
581
+ return createHash('sha256').update(source, 'utf8').digest('hex');
582
+ };
583
+ /**
584
+ * Creates only empty Overlay-owned collections and refuses all incompatible
585
+ * pre-existing schema. It never drops, migrates, or relaxes validation.
586
+ */
587
+ export async function bootstrapMongoOverlay(db, scope) {
588
+ validChainScope(scope);
589
+ validPart(scope.nodeId);
590
+ const hello = await db.command({ hello: 1 });
591
+ if (hello.msg === 'isdbgrid' || typeof hello.setName !== 'string' || hello.setName.length === 0)
592
+ throw new Error('Mongo Overlay storage requires an unsharded replica set');
593
+ if (hello.isWritablePrimary !== true)
594
+ throw new Error('Mongo Overlay storage requires the writable primary');
595
+ const fingerprint = schemaFingerprint();
596
+ for (const definition of MongoCollectionDefinitions)
597
+ await ensureCollection(db, definition);
598
+ await ensureGridFs(db);
599
+ await ensureLedger(db, scope, fingerprint);
600
+ await transactionalProbe(db, scope);
601
+ return {
602
+ topology: 'replica-set',
603
+ schemaVersion,
604
+ scope: { ...scope },
605
+ collections: Object.values(MongoCollectionNames)
606
+ };
607
+ }
608
+ async function ensureCollection(db, definition) {
609
+ await createCollectionIfMissing(db, definition);
610
+ const actual = await db.listCollections({ name: definition.name }).next();
611
+ if (!collectionMatchesDefinition(actual, definition))
612
+ throw new Error(`Incompatible Mongo Overlay validator for ${definition.name}`);
613
+ await ensureCollectionIndexes(db, definition);
614
+ }
615
+ async function createCollectionIfMissing(db, definition) {
616
+ const existing = await db.listCollections({ name: definition.name }).next();
617
+ if (existing !== null)
618
+ return;
619
+ try {
620
+ await db.createCollection(definition.name, {
621
+ validator: definition.validator,
622
+ validationLevel: 'strict',
623
+ validationAction: 'error',
624
+ collation: { locale: 'simple' }
625
+ });
626
+ }
627
+ catch (error) {
628
+ if (error.code !== 48)
629
+ throw error;
630
+ }
631
+ }
632
+ function collectionMatchesDefinition(actual, definition) {
633
+ return (actual !== null &&
634
+ stable(actual.options?.validator) === stable(definition.validator) &&
635
+ (actual.options?.collation === undefined ||
636
+ stable(actual.options.collation) === stable({ locale: 'simple' })));
637
+ }
638
+ async function ensureCollectionIndexes(db, definition) {
639
+ const collection = db.collection(definition.name);
640
+ for (const expected of definition.indexes) {
641
+ const { key, ...options } = expected;
642
+ try {
643
+ await collection.createIndex(key, options);
644
+ }
645
+ catch (error) {
646
+ if (![68, 85, 86].includes(error.code ?? -1))
647
+ throw error;
648
+ }
649
+ }
650
+ const actualIndexes = await collection.listIndexes().toArray();
651
+ for (const expected of definition.indexes) {
652
+ if (!indexMatchesDefinition(actualIndexes.find(candidate => candidate.name === expected.name), expected))
653
+ throw new Error(`Incompatible Mongo Overlay index for ${definition.name}:${expected.name}`);
654
+ }
655
+ }
656
+ function indexMatchesDefinition(actualIndex, expected) {
657
+ return (actualIndex !== undefined &&
658
+ stable(actualIndex.key) === stable(expected.key) &&
659
+ Boolean(actualIndex.unique) === Boolean(expected.unique) &&
660
+ stable(actualIndex.partialFilterExpression) === stable(expected.partialFilterExpression) &&
661
+ Boolean(actualIndex.sparse) === Boolean(expected.sparse) &&
662
+ actualIndex.expireAfterSeconds === expected.expireAfterSeconds);
663
+ }
664
+ async function ensureGridFs(db) {
665
+ const filesName = `${MongoGridFsBucketName}.files`;
666
+ const chunksName = `${MongoGridFsBucketName}.chunks`;
667
+ for (const name of [filesName, chunksName]) {
668
+ const existing = await db.listCollections({ name }).next();
669
+ if (existing === null) {
670
+ try {
671
+ await db.createCollection(name);
672
+ }
673
+ catch (error) {
674
+ if (error.code !== 48)
675
+ throw error;
676
+ }
677
+ }
678
+ }
679
+ await db
680
+ .collection(filesName)
681
+ .createIndex({ filename: 1, uploadDate: 1 }, { name: 'filename_1_uploadDate_1' });
682
+ await db
683
+ .collection(chunksName)
684
+ .createIndex({ files_id: 1, n: 1 }, { unique: true, name: 'files_id_1_n_1' });
685
+ }
686
+ async function ensureLedger(db, scope, fingerprint) {
687
+ const collection = db.collection(MongoCollectionNames.schema);
688
+ const id = mongoNodeKey(scope);
689
+ const now = new Date();
690
+ try {
691
+ await collection.insertOne({
692
+ _id: id,
693
+ schemaVersion,
694
+ network: scope.network,
695
+ genesisHash: scope.genesisHash,
696
+ nodeId: scope.nodeId,
697
+ schemaFingerprint: fingerprint,
698
+ createdAt: now,
699
+ updatedAt: now
700
+ });
701
+ }
702
+ catch (error) {
703
+ if (error.code !== 11000)
704
+ throw error;
705
+ }
706
+ const ledger = await collection.findOne({ _id: id });
707
+ if (ledger === null ||
708
+ ledger.schemaVersion !== schemaVersion ||
709
+ ledger.network !== scope.network ||
710
+ ledger.genesisHash !== scope.genesisHash ||
711
+ ledger.nodeId !== scope.nodeId ||
712
+ ledger.schemaFingerprint !== fingerprint)
713
+ throw new Error('Incompatible Mongo Overlay schema ledger');
714
+ }
715
+ async function transactionalProbe(db, scope) {
716
+ const session = db.client.startSession();
717
+ try {
718
+ await session.withTransaction(async () => {
719
+ const result = await db
720
+ .collection(MongoCollectionNames.schema)
721
+ .updateOne({ _id: mongoNodeKey(scope), schemaVersion, schemaFingerprint: schemaFingerprint() }, { $set: { lastTransactionProbeAt: new Date(), updatedAt: new Date() } }, { session });
722
+ if (result.matchedCount !== 1)
723
+ throw new Error('Mongo Overlay schema ledger changed during bootstrap');
724
+ }, { readConcern: { level: 'majority' }, writeConcern: { w: 'majority', j: true } });
725
+ }
726
+ finally {
727
+ await session.endSession();
728
+ }
729
+ }
730
+ //# sourceMappingURL=MongoSchema.js.map