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