@harperfast/harper 5.3.0 → 5.3.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 (231) hide show
  1. package/bin/copyDb.ts +5 -1
  2. package/bin/deployRenderer.ts +3 -0
  3. package/components/Application.ts +66 -22
  4. package/components/DESIGN.md +89 -36
  5. package/components/componentLoader.ts +2 -0
  6. package/components/deploymentRecorder.ts +18 -1
  7. package/components/installFingerprint.ts +123 -0
  8. package/components/mcp/DESIGN.md +4 -4
  9. package/components/operations.js +29 -0
  10. package/config/DESIGN.md +5 -5
  11. package/dataLayer/DESIGN.md +37 -22
  12. package/dataLayer/backupRepository.ts +248 -0
  13. package/dataLayer/restoreMarker.ts +30 -75
  14. package/dataLayer/rocksdbBackup.ts +253 -110
  15. package/dist/bin/copyDb.js +3 -0
  16. package/dist/bin/copyDb.js.map +1 -1
  17. package/dist/bin/deployRenderer.js +3 -0
  18. package/dist/bin/deployRenderer.js.map +1 -1
  19. package/dist/components/Application.d.ts +5 -0
  20. package/dist/components/Application.js +51 -19
  21. package/dist/components/Application.js.map +1 -1
  22. package/dist/components/componentLoader.js +2 -0
  23. package/dist/components/componentLoader.js.map +1 -1
  24. package/dist/components/deploymentRecorder.js +18 -1
  25. package/dist/components/deploymentRecorder.js.map +1 -1
  26. package/dist/components/installFingerprint.d.ts +39 -0
  27. package/dist/components/installFingerprint.js +113 -0
  28. package/dist/components/installFingerprint.js.map +1 -0
  29. package/dist/components/operations.d.ts +2 -0
  30. package/dist/components/operations.js +31 -0
  31. package/dist/components/operations.js.map +1 -1
  32. package/dist/dataLayer/backupRepository.d.ts +33 -0
  33. package/dist/dataLayer/backupRepository.js +232 -0
  34. package/dist/dataLayer/backupRepository.js.map +1 -0
  35. package/dist/dataLayer/restoreMarker.d.ts +4 -1
  36. package/dist/dataLayer/restoreMarker.js +29 -68
  37. package/dist/dataLayer/restoreMarker.js.map +1 -1
  38. package/dist/dataLayer/rocksdbBackup.js +221 -112
  39. package/dist/dataLayer/rocksdbBackup.js.map +1 -1
  40. package/dist/index.d.ts +1 -0
  41. package/dist/index.js.map +1 -1
  42. package/dist/json/systemSchema.json +14 -0
  43. package/dist/resources/DatabaseTransaction.d.ts +140 -11
  44. package/dist/resources/DatabaseTransaction.js +1055 -464
  45. package/dist/resources/DatabaseTransaction.js.map +1 -1
  46. package/dist/resources/IterableEventQueue.d.ts +2 -1
  47. package/dist/resources/IterableEventQueue.js +16 -8
  48. package/dist/resources/IterableEventQueue.js.map +1 -1
  49. package/dist/resources/LMDBTransaction.d.ts +13 -2
  50. package/dist/resources/LMDBTransaction.js +219 -93
  51. package/dist/resources/LMDBTransaction.js.map +1 -1
  52. package/dist/resources/RecordEncoder.js +2 -3
  53. package/dist/resources/RecordEncoder.js.map +1 -1
  54. package/dist/resources/Resource.js +5 -6
  55. package/dist/resources/Resource.js.map +1 -1
  56. package/dist/resources/ResourceInterface.d.ts +3 -1
  57. package/dist/resources/ResourceInterface.js.map +1 -1
  58. package/dist/resources/RocksIndexStore.d.ts +2 -1
  59. package/dist/resources/RocksIndexStore.js +3 -0
  60. package/dist/resources/RocksIndexStore.js.map +1 -1
  61. package/dist/resources/Table.js +116 -12
  62. package/dist/resources/Table.js.map +1 -1
  63. package/dist/resources/analytics/write.js +6 -2
  64. package/dist/resources/analytics/write.js.map +1 -1
  65. package/dist/resources/auditStore.d.ts +71 -17
  66. package/dist/resources/auditStore.js +248 -64
  67. package/dist/resources/auditStore.js.map +1 -1
  68. package/dist/resources/branchDatabase.js +2 -0
  69. package/dist/resources/branchDatabase.js.map +1 -1
  70. package/dist/resources/databases.js +8 -2
  71. package/dist/resources/databases.js.map +1 -1
  72. package/dist/resources/indexes/fullTextNativeBinding.d.ts +6 -2
  73. package/dist/resources/indexes/fullTextNativeBinding.js +12 -2
  74. package/dist/resources/indexes/fullTextNativeBinding.js.map +1 -1
  75. package/dist/resources/indexes/fullTextQueryIndex.d.ts +4 -0
  76. package/dist/resources/indexes/fullTextQueryIndex.js +207 -10
  77. package/dist/resources/indexes/fullTextQueryIndex.js.map +1 -1
  78. package/dist/resources/models/Models.d.ts +11 -0
  79. package/dist/resources/models/Models.js +95 -4
  80. package/dist/resources/models/Models.js.map +1 -1
  81. package/dist/resources/models/backendRegistry.d.ts +8 -0
  82. package/dist/resources/models/backendRegistry.js +15 -1
  83. package/dist/resources/models/backendRegistry.js.map +1 -1
  84. package/dist/resources/models/bootstrap.d.ts +2 -0
  85. package/dist/resources/models/bootstrap.js +6 -0
  86. package/dist/resources/models/bootstrap.js.map +1 -1
  87. package/dist/resources/models/calibration.d.ts +88 -0
  88. package/dist/resources/models/calibration.js +244 -0
  89. package/dist/resources/models/calibration.js.map +1 -0
  90. package/dist/resources/models/calibrationStore.d.ts +163 -0
  91. package/dist/resources/models/calibrationStore.js +986 -0
  92. package/dist/resources/models/calibrationStore.js.map +1 -0
  93. package/dist/resources/models/decisionStore.d.ts +24 -0
  94. package/dist/resources/models/decisionStore.js +45 -0
  95. package/dist/resources/models/decisionStore.js.map +1 -1
  96. package/dist/resources/models/generativeDecision.js +44 -15
  97. package/dist/resources/models/generativeDecision.js.map +1 -1
  98. package/dist/resources/models/types.d.ts +7 -0
  99. package/dist/resources/recordLock.d.ts +8 -5
  100. package/dist/resources/recordLock.js +6 -4
  101. package/dist/resources/recordLock.js.map +1 -1
  102. package/dist/resources/replayLogs.js +8 -1
  103. package/dist/resources/replayLogs.js.map +1 -1
  104. package/dist/resources/scheduler/engine.d.ts +4 -0
  105. package/dist/resources/scheduler/engine.js +30 -2
  106. package/dist/resources/scheduler/engine.js.map +1 -1
  107. package/dist/resources/search.d.ts +16 -4
  108. package/dist/resources/search.js +117 -13
  109. package/dist/resources/search.js.map +1 -1
  110. package/dist/resources/transaction.js +85 -5
  111. package/dist/resources/transaction.js.map +1 -1
  112. package/dist/resources/transactionBroadcast.d.ts +8 -0
  113. package/dist/resources/transactionBroadcast.js +65 -0
  114. package/dist/resources/transactionBroadcast.js.map +1 -1
  115. package/dist/security/keys.d.ts +1 -1
  116. package/dist/security/keys.js +170 -120
  117. package/dist/security/keys.js.map +1 -1
  118. package/dist/server/http.js +10 -0
  119. package/dist/server/http.js.map +1 -1
  120. package/dist/server/serverHelpers/Request.js +11 -2
  121. package/dist/server/serverHelpers/Request.js.map +1 -1
  122. package/dist/server/serverHelpers/contentTypes.js +8 -3
  123. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  124. package/dist/server/serverHelpers/uwsServer.js +38 -7
  125. package/dist/server/serverHelpers/uwsServer.js.map +1 -1
  126. package/dist/server/threads/manageThreads.d.ts +3 -3
  127. package/dist/server/threads/manageThreads.js +15 -13
  128. package/dist/server/threads/manageThreads.js.map +1 -1
  129. package/dist/server/threads/threadServer.d.ts +3 -0
  130. package/dist/server/threads/threadServer.js +24 -6
  131. package/dist/server/threads/threadServer.js.map +1 -1
  132. package/dist/upgrade/directives/5-3-0.js +9 -3
  133. package/dist/upgrade/directives/5-3-0.js.map +1 -1
  134. package/dist/utility/durableFile.d.ts +36 -0
  135. package/dist/utility/durableFile.js +106 -0
  136. package/dist/utility/durableFile.js.map +1 -0
  137. package/dist/utility/errors/hdbError.d.ts +4 -0
  138. package/dist/utility/errors/hdbError.js +10 -1
  139. package/dist/utility/errors/hdbError.js.map +1 -1
  140. package/dist/utility/globalSchema.d.ts +9 -0
  141. package/dist/utility/hdbTerms.d.ts +1 -0
  142. package/dist/utility/hdbTerms.js +1 -0
  143. package/dist/utility/hdbTerms.js.map +1 -1
  144. package/dist/validation/configValidator.js +29 -0
  145. package/dist/validation/configValidator.js.map +1 -1
  146. package/index.ts +7 -0
  147. package/json/systemSchema.json +14 -0
  148. package/npm-shrinkwrap.json +22 -22
  149. package/package.json +2 -2
  150. package/resources/DESIGN.md +57 -272
  151. package/resources/DatabaseTransaction.ts +708 -87
  152. package/resources/FULL_TEXT.md +6 -2
  153. package/resources/IterableEventQueue.ts +14 -7
  154. package/resources/LMDBTransaction.ts +212 -84
  155. package/resources/RecordEncoder.ts +2 -3
  156. package/resources/Resource.ts +5 -6
  157. package/resources/ResourceInterface.ts +3 -1
  158. package/resources/RocksIndexStore.ts +5 -0
  159. package/resources/Table.ts +118 -15
  160. package/resources/analytics/write.ts +6 -2
  161. package/resources/audit-retention.md +203 -0
  162. package/resources/auditStore.ts +306 -84
  163. package/resources/branchDatabase.ts +2 -0
  164. package/resources/databases.ts +6 -2
  165. package/resources/indexes/DESIGN.md +120 -177
  166. package/resources/indexes/fullTextNativeBinding.ts +22 -3
  167. package/resources/indexes/fullTextQueryIndex.ts +274 -11
  168. package/resources/models/DESIGN.md +11 -3
  169. package/resources/models/Models.ts +127 -4
  170. package/resources/models/backendRegistry.ts +17 -0
  171. package/resources/models/bootstrap.ts +9 -1
  172. package/resources/models/calibration.ts +298 -0
  173. package/resources/models/calibrationStore.ts +1167 -0
  174. package/resources/models/decisionStore.ts +57 -0
  175. package/resources/models/generativeDecision.ts +54 -16
  176. package/resources/models/types.ts +6 -0
  177. package/resources/record-locks.md +155 -141
  178. package/resources/recordLock.ts +12 -6
  179. package/resources/replayLogs.ts +9 -2
  180. package/resources/scheduler/DESIGN.md +4 -3
  181. package/resources/scheduler/engine.ts +35 -2
  182. package/resources/search.ts +154 -16
  183. package/resources/transaction.ts +50 -5
  184. package/resources/transactionBroadcast.ts +69 -0
  185. package/security/DESIGN.md +9 -30
  186. package/security/keys.ts +175 -127
  187. package/server/DESIGN.md +70 -36
  188. package/server/http.ts +10 -0
  189. package/server/serverHelpers/Request.ts +10 -2
  190. package/server/serverHelpers/contentTypes.ts +5 -3
  191. package/server/serverHelpers/uwsServer.ts +34 -7
  192. package/server/threads/manageThreads.js +16 -14
  193. package/server/threads/threadServer.js +24 -6
  194. package/studio/web/assets/{Chat-CNEKMllJ.js → Chat-4LMxI0yB.js} +1 -1
  195. package/studio/web/assets/{FloatingChat-CEnWhHeR.js → FloatingChat-kWHApEtF.js} +3 -3
  196. package/studio/web/assets/{SearchDialog-CwhJdEdY.js → SearchDialog-B7ymna97.js} +1 -1
  197. package/studio/web/assets/{apiToken-DC5vsaaO.js → apiToken-CE3AAt9f.js} +1 -1
  198. package/studio/web/assets/{applications-DQZxDy0x.js → applications-DdN7CqsB.js} +1 -1
  199. package/studio/web/assets/{cssMode-Dz2DxF4e.js → cssMode-BpySZUSt.js} +1 -1
  200. package/studio/web/assets/{editor-B3mjd3tz.js → editor-myLCVv9C.js} +1 -1
  201. package/studio/web/assets/{html-BsRGM7tp.js → html-DGNYO55O.js} +1 -1
  202. package/studio/web/assets/{htmlMode-tUKJaA3o.js → htmlMode-BUgpQr41.js} +1 -1
  203. package/studio/web/assets/{index-BuqFjN5K.js → index-FN4KC9va.js} +39 -39
  204. package/studio/web/assets/index-QLZw6xrU.css +1 -0
  205. package/studio/web/assets/{index.lazy-CcpfQFY_.js → index.lazy-DZFuaumG.js} +1 -1
  206. package/studio/web/assets/{javascript-BZ_uRGHl.js → javascript-DiBzh4pj.js} +1 -1
  207. package/studio/web/assets/{jsonMode-DgB-aFMj.js → jsonMode-Bm312nmu.js} +1 -1
  208. package/studio/web/assets/{languageServices-BleuBw17.js → languageServices-Dz2K9rZN.js} +1 -1
  209. package/studio/web/assets/{lspLanguageFeatures-ByVYLSsQ.js → lspLanguageFeatures-CNu1L5xn.js} +1 -1
  210. package/studio/web/assets/{notifications-TuzN0IMi.js → notifications--nwFLGmM.js} +1 -1
  211. package/studio/web/assets/{notifications-D1Nf-Edn.js → notifications-BelKw5bR.js} +1 -1
  212. package/studio/web/assets/{profile-D7C7Lupz.js → profile-AU5FjOBx.js} +1 -1
  213. package/studio/web/assets/{regions-nGQCm-v8.js → regions-C0sTSEHH.js} +1 -1
  214. package/studio/web/assets/{register-C3x9Ze6P.js → register-DyNR2rGk.js} +2 -2
  215. package/studio/web/assets/{setComponentFile-DpjrRqeG.js → setComponentFile-BkWx8AN3.js} +1 -1
  216. package/studio/web/assets/{setup-CGiKLZ9e.js → setup-D6LZS78d.js} +2 -2
  217. package/studio/web/assets/{status-CGJwrNPH.js → status-BCWnn70o.js} +1 -1
  218. package/studio/web/assets/{toggleHighContrast-C-Ne6P7T.js → toggleHighContrast-pyeGJ6W1.js} +1 -1
  219. package/studio/web/assets/{tsMode-CSreYTys.js → tsMode-CGV3iaWC.js} +1 -1
  220. package/studio/web/assets/{typescript-MnxBk89W.js → typescript-Ci7Khvwo.js} +1 -1
  221. package/studio/web/assets/{useEntityRestURL-TNrh77-s.js → useEntityRestURL-DNiz3aZ0.js} +1 -1
  222. package/studio/web/assets/{workers-BfZlUNVI.js → workers-byzxjbqy.js} +1 -1
  223. package/studio/web/assets/{xml-BHd6qGH7.js → xml-DGnHQ3CX.js} +1 -1
  224. package/studio/web/assets/{yaml-C53R8OQq.js → yaml-BUoTnPWb.js} +1 -1
  225. package/studio/web/index.html +2 -2
  226. package/upgrade/directives/5-3-0.ts +10 -3
  227. package/utility/durableFile.ts +96 -0
  228. package/utility/errors/hdbError.ts +9 -0
  229. package/utility/hdbTerms.ts +1 -0
  230. package/validation/configValidator.ts +31 -0
  231. package/studio/web/assets/index-72cEH474.css +0 -1
@@ -561,6 +561,15 @@ function scopeViolation(handle, resolved, databaseName) {
561
561
  return undefined;
562
562
  return new hdbError_ts_1.ClientError('This transaction already holds a node-scoped lock on this record, so a cluster-scoped lock cannot be taken on top of it', 409);
563
563
  }
564
+ function rescope(resolved, tableReplicates) {
565
+ if (resolved.scopeRequested)
566
+ return resolved;
567
+ const scope = tableReplicates ? 'cluster' : 'node';
568
+ return scope === resolved.scope ? resolved : { ...resolved, scope };
569
+ }
570
+ function transportUnavailable(databaseName) {
571
+ return new hdbError_ts_1.LockUnavailableError(`Cluster-scoped record locks are not available on ${databaseName}: no record lock transport is registered`);
572
+ }
564
573
  /** Distinguishes bare lock options from a record target (id, URL, {id:...}). */
565
574
  function isPlainOptions(value) {
566
575
  return (typeof value === 'object' &&
@@ -577,6 +586,26 @@ function isPlainOptions(value) {
577
586
  return undefined;
578
587
  return (entry) => Table.writeLockControlEntry(entry);
579
588
  });
589
+ // Valid for one synchronous notify pass, in which a key's subscribers on this thread all receive the same
590
+ // freshly decoded audit record; cleared after it so it pins no store or record once delivery is done.
591
+ let memoizedEntryAuditRecord;
592
+ let memoizedEntryStore;
593
+ let memoizedEntryId;
594
+ let memoizedEntry;
595
+ function clearEntryMemo() {
596
+ memoizedEntryAuditRecord = memoizedEntryStore = memoizedEntryId = memoizedEntry = undefined;
597
+ }
598
+ function currentEntryForAudit(store, id, auditRecord) {
599
+ if (auditRecord !== memoizedEntryAuditRecord || store !== memoizedEntryStore || id !== memoizedEntryId) {
600
+ if (memoizedEntryAuditRecord === undefined)
601
+ queueMicrotask(clearEntryMemo);
602
+ memoizedEntry = store.getEntry(id);
603
+ memoizedEntryAuditRecord = auditRecord;
604
+ memoizedEntryStore = store;
605
+ memoizedEntryId = id;
606
+ }
607
+ return memoizedEntry;
608
+ }
580
609
  function makeTable(options) {
581
610
  var _a;
582
611
  const { primaryKey, indices, tableId, tableName, primaryStore, databasePath, databaseName, auditStore, schemaDefined, dbisDB: dbisDb, sealed, splitSegments, replicate, description, hidden, cacheControl, isBranch, fullTextIndexes = [], fullTextFields = [], fullTextIndexGenerations = Object.create(null), fullTextIndexRetirements = [], } = options;
@@ -865,12 +894,26 @@ function makeTable(options) {
865
894
  return;
866
895
  return { key, descriptor };
867
896
  }
897
+ const COMMIT_BASE_METHODS = new Set(['put', 'patch', 'delete']);
898
+ function entryBeforeWrite(loadedEntry, id, transaction, reloadsCommitBase) {
899
+ if (loadedEntry != null)
900
+ return loadedEntry;
901
+ if (isRocksDB && reloadsCommitBase) {
902
+ // save() reads this write's base from the staging snapshot (harper#2259); the read handle is still opened
903
+ // here because it is what gives the staged write coordinated conflict retries
904
+ transaction.getReadTxn();
905
+ return undefined;
906
+ }
907
+ return primaryStore.getEntry(id, { transaction: transaction.getReadTxn() });
908
+ }
868
909
  class TableResource extends Resource_ts_1.Resource {
869
910
  #record; // the stored/frozen record from the database and stored in the cache (should not be modified directly)
870
911
  #changes; // the changes to the record that have been made (should not be modified directly)
871
912
  #version; // version of the record
872
913
  #entry; // the entry from the database
873
914
  #savingOperation; // operation for the record is currently being saved
915
+ #baseReadTxn; // staging handle this instance's uncached pre-load read through
916
+ #baseReadEntry; // what that read returned, reusable as the commit base only while it is still #entry
874
917
  #lockHandle; // the record lock acquired by lock() — scoped or hold
875
918
  #lockWritable; // set by #reloadLocked to let save() stage lock-writable updates
876
919
  #writeGeneration;
@@ -1611,12 +1654,25 @@ function makeTable(options) {
1611
1654
  if (readTxn?.isDone) {
1612
1655
  throw new Error('You can not read from a transaction that has already been committed/aborted');
1613
1656
  }
1614
- return loadLocalRecord(id, request, { transaction: readTxn, ensureLoaded: resourceOptions?.ensureLoaded }, sync, (entry) => {
1657
+ const readsCommitBase = isRocksDB &&
1658
+ COMMIT_BASE_METHODS.has(resourceOptions?.method) &&
1659
+ !resourceOptions?.ensureLoaded &&
1660
+ readTxn &&
1661
+ !readTxn.snapshotDisabled;
1662
+ return loadLocalRecord(id, request, {
1663
+ transaction: readTxn,
1664
+ ensureLoaded: resourceOptions?.ensureLoaded,
1665
+ uncachedRead: readsCommitBase || undefined,
1666
+ }, sync, (entry) => {
1615
1667
  if (entry) {
1616
1668
  _a._updateResource(this, entry);
1617
1669
  }
1618
1670
  else
1619
1671
  this.#record = null;
1672
+ if (readsCommitBase) {
1673
+ this.#baseReadTxn = readTxn;
1674
+ this.#baseReadEntry = entry;
1675
+ }
1620
1676
  if (request.onlyIfCached) {
1621
1677
  // don't go into the loading from source condition, but HTTP spec says to
1622
1678
  // return 504 (rather than 404) if there is no content and the cache-control header
@@ -1647,6 +1703,16 @@ function makeTable(options) {
1647
1703
  throw error;
1648
1704
  }
1649
1705
  }
1706
+ // Reusable only for the key this instance read and while #entry is the entry that read returned: a source fill
1707
+ // or retry replaces #entry, and ensureLoaded() can evict it in place, so ensureLoaded() drops the receipt.
1708
+ #commitBaseTxn(id) {
1709
+ const baseReadTxn = this.#baseReadTxn;
1710
+ if (!baseReadTxn || this.#baseReadEntry !== this.#entry)
1711
+ return;
1712
+ const receiverId = this.getId();
1713
+ if (Object.is(id, receiverId) || (0, DatabaseTransaction_ts_1.writeKeyId)(id) === (0, DatabaseTransaction_ts_1.writeKeyId)(receiverId))
1714
+ return baseReadTxn;
1715
+ }
1650
1716
  static _updateResource(resource, entry) {
1651
1717
  resource.#entry = entry;
1652
1718
  resource.#record = entry?.value ?? null;
@@ -1658,6 +1724,7 @@ function makeTable(options) {
1658
1724
  * @returns
1659
1725
  */
1660
1726
  ensureLoaded() {
1727
+ this.#baseReadTxn = undefined;
1661
1728
  const loadedFromSource = ensureLoadedFromSource(this.constructor.source, this.getId(), this.#entry, this.getContext());
1662
1729
  if (loadedFromSource) {
1663
1730
  return (0, when_ts_1.when)(loadedFromSource, (entry) => {
@@ -2848,6 +2915,7 @@ function makeTable(options) {
2848
2915
  additionalAuditRefs: isRocksDB && audit && txnLogKey !== txnTime
2849
2916
  ? [{ version: txnLogKey, nodeId: options?.nodeId }]
2850
2917
  : undefined,
2918
+ localOnly: options?.localOnly,
2851
2919
  }, 'invalidate');
2852
2920
  if (write.trackRecordVersion)
2853
2921
  write.recordVersionApplied = true;
@@ -2906,6 +2974,7 @@ function makeTable(options) {
2906
2974
  additionalAuditRefs: isRocksDB && audit && txnLogKey !== txnTime
2907
2975
  ? [{ version: txnLogKey, nodeId: options?.nodeId }]
2908
2976
  : undefined,
2977
+ localOnly: options?.localOnly,
2909
2978
  }, 'relocate', false, null);
2910
2979
  if (write.trackRecordVersion)
2911
2980
  write.recordVersionApplied = true;
@@ -3078,7 +3147,7 @@ function makeTable(options) {
3078
3147
  const id = target != null ? requestTargetToId(target) : this.getId();
3079
3148
  checkValidId(id);
3080
3149
  this.#assertLiveHandle(id);
3081
- const resolved = (0, recordLock_ts_1.resolveLockOptions)(options);
3150
+ const resolved = (0, recordLock_ts_1.resolveLockOptions)(options, _a.replicate !== false);
3082
3151
  const context = this.getContext();
3083
3152
  const link = txnForContext(context);
3084
3153
  const keyId = (0, DatabaseTransaction_ts_1.writeKeyId)(id);
@@ -3088,7 +3157,7 @@ function makeTable(options) {
3088
3157
  if (resolved.scope === 'cluster' &&
3089
3158
  (resolved.scopeRequested || (0, recordLockCoordinator_ts_1.isClusterLockRequired)(databaseName)) &&
3090
3159
  !(0, recordLockCoordinator_ts_1.getClusterLockTransport)(databaseName))
3091
- return Promise.reject(new hdbError_ts_1.LockUnavailableError(`Cluster-scoped record locks are not available on ${databaseName}: no record lock transport is registered`));
3160
+ return Promise.reject(transportUnavailable(databaseName));
3092
3161
  const held = this.#lockHandle;
3093
3162
  if (held && !held.isExpired() && held.keyId === keyId) {
3094
3163
  // Re-entrant: upgrade to hold if requested, then preserve staged changes.
@@ -3181,7 +3250,7 @@ function makeTable(options) {
3181
3250
  clearTimeout(followerTimer);
3182
3251
  const acquired = link.recordLockFor(primaryStore, keyId);
3183
3252
  if (acquired && !acquired.isExpired()) {
3184
- const violation = scopeViolation(acquired, resolved, databaseName);
3253
+ const violation = scopeViolation(acquired, rescope(resolved, _a.replicate !== false), databaseName);
3185
3254
  if (violation)
3186
3255
  throw violation;
3187
3256
  if (resolved.hold && !acquired.hold) {
@@ -3223,9 +3292,9 @@ function makeTable(options) {
3223
3292
  // caller's whole timeout, long enough for harper-pro to register the transport on this
3224
3293
  // worker. Using the snapshot would take the native key alone and hand back a node-scoped
3225
3294
  // handle while a peer that already had the transport is granted the same key.
3295
+ const current = rescope(resolved, _a.replicate !== false);
3226
3296
  try {
3227
- if (resolved.scope !== 'node')
3228
- coordinator = _a.lockCoordinator ?? coordinator;
3297
+ coordinator = current.scope === 'cluster' ? (_a.lockCoordinator ?? coordinator) : undefined;
3229
3298
  }
3230
3299
  catch (error) {
3231
3300
  // The getter fails closed on an unusable node identity, and that has to reach the caller
@@ -3235,6 +3304,13 @@ function makeTable(options) {
3235
3304
  handle.release();
3236
3305
  throw error;
3237
3306
  }
3307
+ // The entry check cannot cover this: the wait is where the call became cluster-scoped.
3308
+ if (current.scope === 'cluster' &&
3309
+ !coordinator &&
3310
+ (current.scopeRequested || (0, recordLockCoordinator_ts_1.isClusterLockRequired)(databaseName))) {
3311
+ handle.release();
3312
+ throw transportUnavailable(databaseName);
3313
+ }
3238
3314
  if (coordinator) {
3239
3315
  try {
3240
3316
  // Not a 423 when the budget is gone, and not a skip either: the native wait can consume
@@ -3538,7 +3614,9 @@ function makeTable(options) {
3538
3614
  }
3539
3615
  };
3540
3616
  }
3541
- const entry = this.#entry ?? primaryStore.getEntry(id, { transaction: transaction.getReadTxn() });
3617
+ const reloadsCommitBase = options?.isCopyApply !== true;
3618
+ const entry = entryBeforeWrite(this.#entry, id, transaction, reloadsCommitBase);
3619
+ const baseReadTxn = this.#commitBaseTxn(id);
3542
3620
  const writeToSource = () => {
3543
3621
  if (!this.constructor.source || context?.source)
3544
3622
  return;
@@ -3567,11 +3645,12 @@ function makeTable(options) {
3567
3645
  key: id,
3568
3646
  store: primaryStore,
3569
3647
  entry,
3648
+ baseReadTxn,
3570
3649
  nodeName: context?.nodeName,
3571
3650
  fullUpdate,
3572
3651
  chainsStagedState: true,
3573
3652
  // copy-apply rows keep their pre-read base: one read per row, healed by the post-copy replay
3574
- reloadCommitBase: options?.isCopyApply !== true,
3653
+ reloadCommitBase: reloadsCommitBase,
3575
3654
  deferSave: true,
3576
3655
  // the origin's record version on an applied write; absent for a locally-originated one
3577
3656
  recordVersion: options?.version,
@@ -4373,11 +4452,13 @@ function makeTable(options) {
4373
4452
  const transaction = txnForContext(context);
4374
4453
  assertDerivedIndexAdmission(options, transaction);
4375
4454
  checkValidId(id);
4376
- const entry = this.#entry ?? primaryStore.getEntry(id, { transaction: transaction.getReadTxn() });
4455
+ const entry = entryBeforeWrite(this.#entry, id, transaction, true);
4456
+ const baseReadTxn = this.#commitBaseTxn(id);
4377
4457
  const write = {
4378
4458
  key: id,
4379
4459
  store: primaryStore,
4380
4460
  entry,
4461
+ baseReadTxn,
4381
4462
  chainsStagedState: true,
4382
4463
  reloadCommitBase: true,
4383
4464
  nodeName: context?.nodeName,
@@ -4428,6 +4509,7 @@ function makeTable(options) {
4428
4509
  additionalAuditRefs: isRocksDB && audit && txnLogKey !== txnTime
4429
4510
  ? [{ version: txnLogKey, nodeId: options?.nodeId }]
4430
4511
  : undefined,
4512
+ localOnly: options?.localOnly,
4431
4513
  }, 'delete');
4432
4514
  if (!audit || isRocksDB)
4433
4515
  scheduleCleanup();
@@ -5027,8 +5109,12 @@ function makeTable(options) {
5027
5109
  results = results.slice(offset, end);
5028
5110
  results.onDone = () => {
5029
5111
  results.onDone = null; // ensure that it isn't called twice
5112
+ txn.unregisterReadIterator(results);
5030
5113
  txn.doneReadTxn();
5031
5114
  };
5115
+ // Recorded ownership: if the request dies before anything consumes these results, the
5116
+ // transaction closes them itself rather than leaving its read snapshot pinned.
5117
+ txn.registerReadIterator(results);
5032
5118
  results.selectApplied = true;
5033
5119
  results.getColumns = getColumns;
5034
5120
  return results;
@@ -5869,7 +5955,12 @@ function makeTable(options) {
5869
5955
  if (subscription.closed)
5870
5956
  return;
5871
5957
  harper_logger_ts_1.default.error?.('Error in real-time subscription:', error);
5872
- subscription.close(error);
5958
+ try {
5959
+ subscription.close(error);
5960
+ }
5961
+ catch (listenerError) {
5962
+ harper_logger_ts_1.default.error?.('Error in real-time subscription listener:', listenerError);
5963
+ }
5873
5964
  }
5874
5965
  function eventFromAudit(id, auditRecord, localTime, beginTxn) {
5875
5966
  let type = auditRecord.type;
@@ -5878,7 +5969,7 @@ function makeTable(options) {
5878
5969
  if (isMutation && !includeSuperseded) {
5879
5970
  if (id === undefined)
5880
5971
  return;
5881
- const entry = primaryStore.getEntry(id);
5972
+ const entry = currentEntryForAudit(primaryStore, id, auditRecord);
5882
5973
  if (!entry || entry.version !== auditRecord.version)
5883
5974
  return;
5884
5975
  if (getFullRecord) {
@@ -6076,7 +6167,7 @@ function makeTable(options) {
6076
6167
  logger_ts_1.logger.trace?.(`Publishing message to id: ${id}, timestamp: ${new Date(txnTime).toISOString()}`);
6077
6168
  // always audit this, but don't change existing version
6078
6169
  // TODO: Use direct writes in the future (copying binary data is hard because it invalidates the cache)
6079
- return updateRecord(id, existingEntry?.value ?? null, existingEntry, txnTime, 0, true, {
6170
+ return updateRecord(id, existingEntry?.value ?? null, existingEntry, txnTime, (existingEntry?.metadataFlags ?? 0) & auditStore_ts_1.LOCAL_ONLY, true, {
6080
6171
  user: context?.user,
6081
6172
  residencyId: options?.residencyId,
6082
6173
  expiresAt: context?.expiresAt,
@@ -6084,6 +6175,7 @@ function makeTable(options) {
6084
6175
  viaNodeId: options?.viaNodeId,
6085
6176
  transaction,
6086
6177
  tableToTrack: tableName,
6178
+ auditLocalOnly: options?.localOnly,
6087
6179
  }, 'message', false, message);
6088
6180
  },
6089
6181
  };
@@ -7712,6 +7804,16 @@ function makeTable(options) {
7712
7804
  // regardless of this state.
7713
7805
  transaction.next.open = DatabaseTransaction_ts_1.TRANSACTION_STATE.CLOSED;
7714
7806
  }
7807
+ // A poison flag must travel with `open`, or a link created after the poisoning (a
7808
+ // handler touching this database for the first time post-poison) sees CLOSED but not
7809
+ // the reason, takes save()'s immediateCommit path, and commits on behalf of a request
7810
+ // that was supposed to have been cut off.
7811
+ if (transaction.timedOut)
7812
+ transaction.next.timedOut = true;
7813
+ if (transaction.disconnected)
7814
+ transaction.next.disconnected = true;
7815
+ if (transaction.postSubmitPoisoned)
7816
+ transaction.next.postSubmitPoisoned = true;
7715
7817
  transaction = transaction.next;
7716
7818
  transaction.db = primaryStore;
7717
7819
  return transaction;
@@ -7725,6 +7827,8 @@ function makeTable(options) {
7725
7827
  context.transaction = transaction;
7726
7828
  if (context.timestamp)
7727
7829
  transaction.timestamp = context.timestamp;
7830
+ if (!context.sourceApply)
7831
+ transaction.requestSignal = context.signal;
7728
7832
  }
7729
7833
  return transaction;
7730
7834
  }