@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
@@ -41,7 +41,6 @@ export type NativeFullTextSearchRequest = {
41
41
  mode?: NativeFullTextSearchMode;
42
42
  operator?: 'any' | 'all';
43
43
  fields?: string[];
44
- candidateIds?: string[];
45
44
  offset?: number;
46
45
  limit?: number;
47
46
  exactTotal?: boolean;
@@ -109,15 +108,20 @@ export type NativeFullTextRuntimeInfo = {
109
108
  storageBackends: ReadonlyArray<'native'>;
110
109
  limits: {
111
110
  maxCommitPayloadBytes: number;
111
+ maxRecordIdBytes: number;
112
+ maxRecordVersionBytes: number;
113
+ maxCandidateIds: number;
114
+ maxCandidateBytes: number;
112
115
  maxSearchWindow: number;
113
116
  maxAutocompleteResults: number;
117
+ maxSearchResponseBytes: number;
114
118
  maxSearchBudgetMilliseconds: number;
115
119
  maxTraceRecords: number;
116
120
  maxTraceSourceBytes: number;
117
121
  };
118
122
  };
119
123
  export type NativeFullTextQueryRuntimeInfo = Pick<NativeFullTextRuntimeInfo, 'queryClassIsolationMinimumSearchThreads'> & {
120
- limits: Pick<NativeFullTextRuntimeInfo['limits'], 'maxSearchWindow' | 'maxAutocompleteResults' | 'maxSearchBudgetMilliseconds' | 'maxTraceRecords' | 'maxTraceSourceBytes'>;
124
+ limits: Pick<NativeFullTextRuntimeInfo['limits'], 'maxSearchWindow' | 'maxAutocompleteResults' | 'maxRecordIdBytes' | 'maxRecordVersionBytes' | 'maxCandidateIds' | 'maxCandidateBytes' | 'maxSearchResponseBytes' | 'maxSearchBudgetMilliseconds' | 'maxTraceRecords' | 'maxTraceSourceBytes'>;
121
125
  };
122
126
  export interface NativeFullTextModule {
123
127
  NativeFullTextIndex: {
@@ -15,8 +15,8 @@ exports.nativeFullTextSearchThreads = nativeFullTextSearchThreads;
15
15
  exports.validateFullTextQueryRuntimeInfo = validateFullTextQueryRuntimeInfo;
16
16
  const logger_ts_1 = require("../../utility/logging/logger.js");
17
17
  const FULLTEXT_LIFECYCLE_API_VERSION = 1;
18
- const FULLTEXT_MUTATION_BATCH_API_VERSION = 4;
19
- const FULLTEXT_QUERY_API_VERSION = 2;
18
+ const FULLTEXT_MUTATION_BATCH_API_VERSION = 5;
19
+ const FULLTEXT_QUERY_API_VERSION = 3;
20
20
  const logger = (0, logger_ts_1.loggerWithTag)('fulltext-derived-index');
21
21
  let bindingPromise;
22
22
  let bindingWarningLogged = false;
@@ -102,11 +102,21 @@ function validateFullTextQueryRuntimeInfo(info) {
102
102
  !Number.isSafeInteger(runtime.queryClassIsolationMinimumSearchThreads) ||
103
103
  runtime.queryClassIsolationMinimumSearchThreads <= 0 ||
104
104
  !runtime.limits ||
105
+ !Number.isSafeInteger(runtime.limits.maxRecordIdBytes) ||
106
+ runtime.limits.maxRecordIdBytes <= 0 ||
107
+ !Number.isSafeInteger(runtime.limits.maxRecordVersionBytes) ||
108
+ runtime.limits.maxRecordVersionBytes <= 0 ||
109
+ !Number.isSafeInteger(runtime.limits.maxCandidateIds) ||
110
+ runtime.limits.maxCandidateIds <= 0 ||
111
+ !Number.isSafeInteger(runtime.limits.maxCandidateBytes) ||
112
+ runtime.limits.maxCandidateBytes <= 0 ||
105
113
  !Number.isSafeInteger(runtime.limits.maxSearchWindow) ||
106
114
  runtime.limits.maxSearchWindow <= 0 ||
107
115
  !Number.isSafeInteger(runtime.limits.maxAutocompleteResults) ||
108
116
  runtime.limits.maxAutocompleteResults <= 0 ||
109
117
  runtime.limits.maxAutocompleteResults > runtime.limits.maxSearchWindow ||
118
+ !Number.isSafeInteger(runtime.limits.maxSearchResponseBytes) ||
119
+ runtime.limits.maxSearchResponseBytes <= 0 ||
110
120
  !Number.isSafeInteger(runtime.limits.maxSearchBudgetMilliseconds) ||
111
121
  runtime.limits.maxSearchBudgetMilliseconds <= 0 ||
112
122
  !Number.isSafeInteger(runtime.limits.maxTraceRecords) ||
@@ -1 +1 @@
1
- {"version":3,"file":"fullTextNativeBinding.js","sourceRoot":"","sources":["../../../resources/indexes/fullTextNativeBinding.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fullTextNativeBinding.js","sourceRoot":"","sources":["../../../resources/indexes/fullTextNativeBinding.ts"],"names":[],"mappings":"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"}
@@ -3,6 +3,7 @@ import type { RocksTransactionLogStore } from '../RocksTransactionLogStore.ts';
3
3
  import { type NativeFullTextIndexConfiguration, type NativeFullTextModule, type NativeFullTextSearchExpression, type NativeFullTextSearchMode } from './fullTextNativeBinding.ts';
4
4
  export { FULL_TEXT_QUERY_PAUSE_OPERATION, FULL_TEXT_QUERY_RESUME_OPERATION, fullTextComparatorMode, } from './fullTextQueryProtocol.ts';
5
5
  import { type DerivedNativeIndexHost } from './hnswDerivedIndex.ts';
6
+ import type { CandidateKeyPlan } from '../search.ts';
6
7
  export type FullTextCondition = {
7
8
  attribute?: string;
8
9
  value?: string;
@@ -34,6 +35,8 @@ export type FullTextQueryIndexOptions = {
34
35
  export declare class FullTextQueryIndex {
35
36
  #private;
36
37
  readonly filteredSearch = true;
38
+ readonly candidateKeyFilter = true;
39
+ readonly candidateKeyProbe = true;
37
40
  readonly filePrimary = true;
38
41
  constructor(options: FullTextQueryIndexOptions);
39
42
  updateDefinition(definition: FullTextDefinition): void;
@@ -43,6 +46,7 @@ export declare class FullTextQueryIndex {
43
46
  minResults?: number;
44
47
  resultOffset?: number;
45
48
  assertTransactionActive?: () => void;
49
+ candidateKeys?: CandidateKeyPlan;
46
50
  }): Promise<Array<{
47
51
  key: unknown;
48
52
  $score: number;
@@ -18,8 +18,20 @@ Object.defineProperty(exports, "fullTextComparatorMode", { enumerable: true, get
18
18
  const hnswDerivedIndex_ts_1 = require("./hnswDerivedIndex.js");
19
19
  const logger_ts_1 = require("../../utility/logging/logger.js");
20
20
  const RAW_PAGE_SIZE = 256;
21
+ const MAX_FILTERED_RAW_PAGE_SIZE = 4_096;
21
22
  const MIN_RAW_PAGE_SIZE = 32;
22
23
  const RAW_PAGE_OVERFETCH_FACTOR = 2;
24
+ const RAW_PAGE_ZERO_YIELD_GROWTH_FACTOR = 4;
25
+ const RAW_PAGE_YIELD_INTERVAL = 256;
26
+ const CANDIDATE_KEYS_PER_SOURCE_READ = 8;
27
+ const MAX_CANDIDATE_KEYS = 4_096;
28
+ // Four key-only reads remain cheaper than loading and decoding the authoritative record they replace.
29
+ const MAX_POINT_PROBE_READS_PER_CANDIDATE = 4;
30
+ const REJECTED_GATE_SAMPLE_SIZE = 8;
31
+ const CANDIDATE_GATE_FAILURE_RETRY_MILLISECONDS = 1_000;
32
+ // Fulltext 0.5.0 Query API v3 uses a 13-byte response header and 13 bytes per versioned hit before string data.
33
+ const NATIVE_SEARCH_RESPONSE_HEADER_BYTES = 13;
34
+ const NATIVE_SEARCH_HIT_OVERHEAD_BYTES = 13;
23
35
  const MAX_RELOAD_FAILURES_BEFORE_REOPEN = 3;
24
36
  const HIGHLIGHT_BLOB_READ_TIMEOUT_MILLISECONDS = 5_000;
25
37
  const READER_DRAIN_GRACE_MILLISECONDS = 1_000;
@@ -33,6 +45,8 @@ const retiredReadinessIdsByPath = new Map();
33
45
  const MAX_RETIRED_READINESS_IDS_PER_PATH = 64;
34
46
  class FullTextQueryIndex {
35
47
  filteredSearch = true;
48
+ candidateKeyFilter = true;
49
+ candidateKeyProbe = true;
36
50
  filePrimary = true;
37
51
  #options;
38
52
  #nativeOptions;
@@ -44,6 +58,7 @@ class FullTextQueryIndex {
44
58
  #readerOperation;
45
59
  #retiredReaderSlots = new Set();
46
60
  #maxSearchWindow;
61
+ #maxFilteredRawPageSize;
47
62
  #maxAutocompleteResults;
48
63
  #maxSearchBudgetMilliseconds;
49
64
  #maxTraceRecords;
@@ -57,6 +72,13 @@ class FullTextQueryIndex {
57
72
  #publicationRebuildRequested = false;
58
73
  #refreshFailureWarned = false;
59
74
  #unexpectedSearchFailureWarned = false;
75
+ #incompletePageFailureLogged = false;
76
+ #candidateCollectionFailureWarned = false;
77
+ #candidateProbeFailureWarned = false;
78
+ #candidateCollectionRetryAfter = 0;
79
+ #candidateProbeRetryAfter = 0;
80
+ #candidateCollectionFailureEpoch = 0;
81
+ #candidateProbeFailureEpoch = 0;
60
82
  constructor(options) {
61
83
  this.#options = options;
62
84
  this.#definition = options.definition;
@@ -140,11 +162,13 @@ class FullTextQueryIndex {
140
162
  context?.signal?.throwIfAborted();
141
163
  const result = await this.#search(condition, context, options);
142
164
  this.#unexpectedSearchFailureWarned = false;
165
+ this.#incompletePageFailureLogged = false;
143
166
  return result;
144
167
  }
145
168
  coverage = this.#queryCoverage(maxIndexLagMilliseconds);
146
169
  const result = await this.#search(condition, context, options);
147
170
  this.#unexpectedSearchFailureWarned = false;
171
+ this.#incompletePageFailureLogged = false;
148
172
  return result;
149
173
  };
150
174
  const operation = execute().catch((error) => {
@@ -271,14 +295,22 @@ class FullTextQueryIndex {
271
295
  let moreMayExist = false;
272
296
  let staleVersionHits = 0;
273
297
  let expiredHits = 0;
298
+ let rejectedGateSampleCount = 0;
299
+ let rejectedGateKeys;
300
+ let rejectedGateVersions;
301
+ let rawPageSize = bounded
302
+ ? Math.min(this.#maxFilteredRawPageSize, RAW_PAGE_SIZE, Math.max(MIN_RAW_PAGE_SIZE, target * RAW_PAGE_OVERFETCH_FACTOR))
303
+ : Math.min(this.#maxFilteredRawPageSize, RAW_PAGE_SIZE);
274
304
  const transaction = context && this.#options.Table._readTxnForContext(context);
305
+ const candidatePlan = options.filter ? options.candidateKeys : undefined;
306
+ let candidateGate;
307
+ let candidateGatePlanned = false;
308
+ let candidateProbeFailureEpoch = 0;
275
309
  while (accepted.length < target && offset < searchWindow) {
276
310
  if (context?.signal?.aborted)
277
311
  throw context.signal.reason ?? new Error('Full-text search aborted');
278
- const desiredPageSize = bounded
279
- ? Math.max(MIN_RAW_PAGE_SIZE, (target - accepted.length) * RAW_PAGE_OVERFETCH_FACTOR)
280
- : RAW_PAGE_SIZE;
281
- const limit = autocomplete ? searchWindow : Math.min(RAW_PAGE_SIZE, desiredPageSize, searchWindow - offset);
312
+ const limit = autocomplete ? searchWindow : Math.min(rawPageSize, searchWindow - offset);
313
+ const acceptedBeforePage = accepted.length;
282
314
  const result = await reader.search({
283
315
  query,
284
316
  offset,
@@ -287,16 +319,98 @@ class FullTextQueryIndex {
287
319
  }, { remainingBudgetMilliseconds: remainingSearchBudget(deadline) });
288
320
  if (!bounded && !autocomplete && result.totalRelation === 'exact' && result.total > searchWindow)
289
321
  throw new hdbError_ts_1.ClientError(`Full-text query exceeds the ${searchWindow}-result search window; add a limit`, 400);
322
+ // Fulltext 0.5.0 Query API v3 only reports lower-bound while a full requested page remains retrievable.
323
+ if (result.totalRelation === 'lower-bound' &&
324
+ result.hits.length < limit &&
325
+ offset + result.hits.length < result.total) {
326
+ if (!this.#incompletePageFailureLogged) {
327
+ this.#incompletePageFailureLogged = true;
328
+ logger.error?.(`Full-text index '${this.#definition.name}' returned ${result.hits.length} of ${limit} hits at offset ${offset} with lower-bound total ${result.total}`);
329
+ }
330
+ throw new hdbError_ts_1.ServerError('Full-text index returned an incomplete result page', 500);
331
+ }
332
+ // An exact total can safely continue from the returned offset; only a short lower-bound page breaks the v3 contract.
290
333
  moreMayExist =
291
334
  result.totalRelation === 'exact' ? offset + result.hits.length < result.total : result.hits.length === limit;
292
335
  if (result.hits.length === 0)
293
336
  break;
294
- for (const hit of result.hits) {
337
+ if (!candidateGatePlanned) {
338
+ candidateGatePlanned = true;
339
+ if (candidatePlan) {
340
+ const assertSearchActive = () => {
341
+ if (context?.signal?.aborted)
342
+ throw context.signal.reason ?? new Error('Full-text search aborted');
343
+ options.assertTransactionActive?.();
344
+ remainingSearchBudget(deadline);
345
+ };
346
+ assertSearchActive();
347
+ const nativeHitCount = Math.min(searchWindow, Math.max(result.hits.length, result.total));
348
+ candidateGate = await this.#candidateGate(candidatePlan, target, nativeHitCount, async () => {
349
+ await new Promise((resolve) => setImmediate(resolve));
350
+ assertSearchActive();
351
+ }, assertSearchActive);
352
+ candidateProbeFailureEpoch = this.#candidateProbeFailureEpoch;
353
+ }
354
+ }
355
+ const pointReadsPerCandidate = candidateGate?.maxReadsPerCandidate ?? 0;
356
+ const workYieldInterval = Math.min(limit, pointReadsPerCandidate === 0
357
+ ? RAW_PAGE_YIELD_INTERVAL
358
+ : Math.max(1, Math.floor(RAW_PAGE_YIELD_INTERVAL / (pointReadsPerCandidate + 1))));
359
+ let nextYieldIndex = workYieldInterval;
360
+ for (let hitIndex = 0; hitIndex < result.hits.length; hitIndex++) {
361
+ if (hitIndex === nextYieldIndex) {
362
+ await new Promise((resolve) => setImmediate(resolve));
363
+ if (context?.signal?.aborted)
364
+ throw context.signal.reason ?? new Error('Full-text search aborted');
365
+ remainingSearchBudget(deadline);
366
+ nextYieldIndex += workYieldInterval;
367
+ }
368
+ const hit = result.hits[hitIndex];
295
369
  const key = decodeNativeId(hit.id, this.#options.Table.tableId);
296
- options.assertTransactionActive?.();
297
- const entry = this.#options.Table.primaryStore.getEntry(key, { transaction });
298
370
  if (typeof hit.version !== 'string')
299
371
  throw new hdbError_ts_1.ServerError('Full-text index returned a hit without a source version', 500);
372
+ options.assertTransactionActive?.();
373
+ let candidateDecides = false;
374
+ if (candidateGate) {
375
+ try {
376
+ const admitted = candidateGate.has(key);
377
+ if (candidateGate.maxReadsPerCandidate > 0 &&
378
+ candidateProbeFailureEpoch === this.#candidateProbeFailureEpoch) {
379
+ this.#candidateProbeFailureWarned = false;
380
+ this.#candidateProbeRetryAfter = 0;
381
+ }
382
+ if (!admitted) {
383
+ if (rejectedGateSampleCount < REJECTED_GATE_SAMPLE_SIZE) {
384
+ (rejectedGateKeys ??= [])[rejectedGateSampleCount] = key;
385
+ (rejectedGateVersions ??= [])[rejectedGateSampleCount] = hit.version;
386
+ rejectedGateSampleCount++;
387
+ }
388
+ continue;
389
+ }
390
+ candidateDecides = candidateGate.complete;
391
+ }
392
+ catch (error) {
393
+ const usedPointProbe = candidateGate.maxReadsPerCandidate > 0;
394
+ candidateGate = undefined;
395
+ if (usedPointProbe) {
396
+ this.#candidateProbeFailureEpoch++;
397
+ this.#candidateProbeRetryAfter = Date.now() + CANDIDATE_GATE_FAILURE_RETRY_MILLISECONDS;
398
+ if (!this.#candidateProbeFailureWarned) {
399
+ this.#candidateProbeFailureWarned = true;
400
+ logger.warn?.('could not probe the full-text companion index; using the record predicate', error);
401
+ }
402
+ }
403
+ else {
404
+ this.#candidateCollectionFailureEpoch++;
405
+ this.#candidateCollectionRetryAfter = Date.now() + CANDIDATE_GATE_FAILURE_RETRY_MILLISECONDS;
406
+ if (!this.#candidateCollectionFailureWarned) {
407
+ this.#candidateCollectionFailureWarned = true;
408
+ logger.warn?.('could not apply the full-text candidate-key set; using the record predicate; point probes may resume on later requests', error);
409
+ }
410
+ }
411
+ }
412
+ }
413
+ const entry = this.#options.Table.primaryStore.getEntry(key, { transaction });
300
414
  if (!entry?.value || hit.version !== String(entry.version)) {
301
415
  staleVersionHits++;
302
416
  continue;
@@ -309,7 +423,7 @@ class FullTextQueryIndex {
309
423
  expiredHits++;
310
424
  continue;
311
425
  }
312
- if (options.filter && !options.filter(key, entry))
426
+ if (options.filter && !candidateDecides && !options.filter(key, entry))
313
427
  continue;
314
428
  accepted.push({ key, $score: hit.score, nativeId: hit.id, record: entry.value, recordEntry: entry });
315
429
  if (accepted.length >= target)
@@ -320,11 +434,34 @@ class FullTextQueryIndex {
320
434
  break;
321
435
  if (!moreMayExist)
322
436
  break;
323
- if (accepted.length < target)
437
+ if (accepted.length < target) {
438
+ if (bounded) {
439
+ const acceptedThisPage = accepted.length - acceptedBeforePage;
440
+ rawPageSize = nextFilteredRawPageSize(limit, result.hits.length, acceptedThisPage, target - accepted.length, this.#maxFilteredRawPageSize);
441
+ }
324
442
  await new Promise((resolve) => setImmediate(resolve));
443
+ }
325
444
  }
326
445
  if (!bounded && moreMayExist)
327
446
  throw new hdbError_ts_1.ClientError(`Full-text query exceeds the ${searchWindow}-result search window; add a limit`, 400);
447
+ if (bounded &&
448
+ accepted.length < target &&
449
+ moreMayExist &&
450
+ staleVersionHits === 0 &&
451
+ rejectedGateSampleCount > 0) {
452
+ options.assertTransactionActive?.();
453
+ for (let sampleIndex = 0; sampleIndex < rejectedGateSampleCount; sampleIndex++) {
454
+ const entry = this.#options.Table.primaryStore.getEntry(rejectedGateKeys[sampleIndex], { transaction });
455
+ if (!entry?.value ||
456
+ rejectedGateVersions[sampleIndex] !== String(entry.version) ||
457
+ this.#options.Table.isFullTextSearchEntryCurrent?.(entry) === false) {
458
+ staleVersionHits++;
459
+ break;
460
+ }
461
+ if (entry.expiresAt !== undefined && entry.expiresAt < Date.now())
462
+ expiredHits++;
463
+ }
464
+ }
328
465
  if (bounded && accepted.length < target && moreMayExist && staleVersionHits > 0)
329
466
  throw new hdbError_ts_1.DerivedIndexLagError(`Full-text index '${this.#definition.name}' changed while searching; retry this query`);
330
467
  if (bounded && accepted.length < target && moreMayExist)
@@ -344,6 +481,52 @@ class FullTextQueryIndex {
344
481
  await lease.release();
345
482
  }
346
483
  }
484
+ async #candidateGate(plan, target, maxSourceReads, cooperate, assertActive) {
485
+ const store = this.#options.Table.primaryStore;
486
+ const estimatedRecords = typeof store.getEstimatedKeyCount === 'function' ? store.getEstimatedKeyCount() : store.getStats?.().entryCount;
487
+ const recordCount = Math.max(1, Number.isFinite(estimatedRecords) ? estimatedRecords : target);
488
+ const selectivity = Math.min(1, Math.max(1, plan.estimatedCount) / recordCount);
489
+ const expectedSourceReads = Math.min(maxSourceReads, Math.ceil(target / selectivity));
490
+ const expectedRejectedReads = Math.ceil(expectedSourceReads * (1 - selectivity));
491
+ const maxKeys = Math.min(MAX_CANDIDATE_KEYS, Math.ceil(expectedRejectedReads * CANDIDATE_KEYS_PER_SOURCE_READ));
492
+ const now = Date.now();
493
+ if (plan.estimatedCount <= maxKeys && now >= this.#candidateCollectionRetryAfter) {
494
+ const collectionFailureEpoch = this.#candidateCollectionFailureEpoch;
495
+ try {
496
+ const collected = await plan.collect(maxKeys, cooperate);
497
+ assertActive();
498
+ if (collectionFailureEpoch === this.#candidateCollectionFailureEpoch) {
499
+ this.#candidateCollectionFailureWarned = false;
500
+ this.#candidateCollectionRetryAfter = 0;
501
+ }
502
+ if (collected)
503
+ return {
504
+ complete: collected.complete,
505
+ maxReadsPerCandidate: 0,
506
+ has: (primaryKey) => collected.keys.has(primaryKey),
507
+ };
508
+ }
509
+ catch (error) {
510
+ assertActive();
511
+ this.#candidateCollectionFailureEpoch++;
512
+ this.#candidateCollectionRetryAfter = Date.now() + CANDIDATE_GATE_FAILURE_RETRY_MILLISECONDS;
513
+ if (!this.#candidateCollectionFailureWarned) {
514
+ this.#candidateCollectionFailureWarned = true;
515
+ logger.warn?.('could not build the full-text candidate-key set; using point probes or the record predicate', error);
516
+ }
517
+ }
518
+ }
519
+ const probe = plan.probe;
520
+ if (!probe ||
521
+ now < this.#candidateProbeRetryAfter ||
522
+ probe.maxReadsPerCandidate > MAX_POINT_PROBE_READS_PER_CANDIDATE)
523
+ return;
524
+ const probeSelectivity = Math.min(1, Math.max(1, probe.estimatedCount) / recordCount);
525
+ const expectedAvoidedSourceReadsPerCandidate = 1 - probeSelectivity;
526
+ return probe.maxReadsPerCandidate <= expectedAvoidedSourceReadsPerCandidate * CANDIDATE_KEYS_PER_SOURCE_READ
527
+ ? probe
528
+ : undefined;
529
+ }
347
530
  async #addHighlights(reader, definition, condition, entries, deadline) {
348
531
  const highlighting = definition.highlighting;
349
532
  if (!highlighting)
@@ -685,7 +868,14 @@ class FullTextQueryIndex {
685
868
  const info = (0, fullTextNativeBinding_ts_1.validateFullTextQueryRuntimeInfo)(await binding.runtimeInfo());
686
869
  this.#nativeOptions.limits.searchThreads = (0, fullTextNativeBinding_ts_1.nativeFullTextSearchThreads)(info, this.#nativeOptions.limits.searchThreads);
687
870
  this.#maxSearchWindow = info.limits.maxSearchWindow;
688
- this.#maxAutocompleteResults = info.limits.maxAutocompleteResults;
871
+ const responseHitCapacity = Math.floor((info.limits.maxSearchResponseBytes - NATIVE_SEARCH_RESPONSE_HEADER_BYTES) /
872
+ (NATIVE_SEARCH_HIT_OVERHEAD_BYTES + info.limits.maxRecordIdBytes + info.limits.maxRecordVersionBytes));
873
+ if (responseHitCapacity < 1)
874
+ throw new TypeError('@harperfast/fulltext/native search response limit cannot hold one maximum-size hit');
875
+ if (responseHitCapacity < info.limits.maxAutocompleteResults)
876
+ logger.warn?.(`Full-text index '${this.#definition.name}' limits autocomplete to ${responseHitCapacity} results because the native response envelope cannot hold its advertised maximum`);
877
+ this.#maxFilteredRawPageSize = Math.min(MAX_FILTERED_RAW_PAGE_SIZE, info.limits.maxSearchWindow, responseHitCapacity);
878
+ this.#maxAutocompleteResults = Math.min(info.limits.maxAutocompleteResults, responseHitCapacity);
689
879
  this.#maxSearchBudgetMilliseconds = info.limits.maxSearchBudgetMilliseconds;
690
880
  this.#maxTraceRecords = info.limits.maxTraceRecords;
691
881
  this.#maxTraceSourceBytes = info.limits.maxTraceSourceBytes;
@@ -707,6 +897,13 @@ class FullTextQueryIndex {
707
897
  }
708
898
  }
709
899
  exports.FullTextQueryIndex = FullTextQueryIndex;
900
+ function nextFilteredRawPageSize(currentPageSize, rawHits, acceptedHits, remainingResults, maxPageSize) {
901
+ const projected = acceptedHits === 0
902
+ ? // With no selectivity signal, grow independently of the remaining target to minimize native round trips.
903
+ currentPageSize * RAW_PAGE_ZERO_YIELD_GROWTH_FACTOR
904
+ : Math.ceil((remainingResults * rawHits * RAW_PAGE_OVERFETCH_FACTOR) / acceptedHits);
905
+ return Math.min(maxPageSize, Math.max(MIN_RAW_PAGE_SIZE, projected));
906
+ }
710
907
  async function pauseNativeFullTextQueryReaders(path, readinessId, ownerEpoch, allowUnregisteredReadiness = false) {
711
908
  const indexes = queryIndexesByPath.get(path);
712
909
  if (!indexes || indexes.size === 0)