@harperfast/harper 5.3.0-beta.4 → 5.3.0

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 (314) hide show
  1. package/bin/backup.ts +6 -1
  2. package/components/Application.ts +486 -115
  3. package/components/DESIGN.md +109 -21
  4. package/components/deploymentProvenance.ts +28 -0
  5. package/components/operations.js +14 -7
  6. package/components/packageComponent.ts +5 -1
  7. package/config-root.schema.json +1 -1
  8. package/dataLayer/DESIGN.md +35 -7
  9. package/dataLayer/blobBackup.ts +71 -30
  10. package/dataLayer/harperBridge/ResourceBridge.ts +16 -2
  11. package/dataLayer/rocksdbBackup.ts +88 -34
  12. package/dataLayer/schemaDescribe.ts +45 -1
  13. package/dist/bin/backup.js +1 -1
  14. package/dist/bin/backup.js.map +1 -1
  15. package/dist/components/Application.d.ts +11 -21
  16. package/dist/components/Application.js +436 -105
  17. package/dist/components/Application.js.map +1 -1
  18. package/dist/components/deploymentProvenance.d.ts +4 -0
  19. package/dist/components/deploymentProvenance.js +32 -0
  20. package/dist/components/deploymentProvenance.js.map +1 -0
  21. package/dist/components/operations.js +14 -7
  22. package/dist/components/operations.js.map +1 -1
  23. package/dist/components/packageComponent.js +6 -1
  24. package/dist/components/packageComponent.js.map +1 -1
  25. package/dist/dataLayer/blobBackup.d.ts +30 -3
  26. package/dist/dataLayer/blobBackup.js +73 -36
  27. package/dist/dataLayer/blobBackup.js.map +1 -1
  28. package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +1 -0
  29. package/dist/dataLayer/harperBridge/ResourceBridge.js +15 -3
  30. package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
  31. package/dist/dataLayer/rocksdbBackup.d.ts +4 -1
  32. package/dist/dataLayer/rocksdbBackup.js +74 -36
  33. package/dist/dataLayer/rocksdbBackup.js.map +1 -1
  34. package/dist/dataLayer/schemaDescribe.js +45 -1
  35. package/dist/dataLayer/schemaDescribe.js.map +1 -1
  36. package/dist/resources/DatabaseTransaction.d.ts +1 -0
  37. package/dist/resources/DatabaseTransaction.js +33 -8
  38. package/dist/resources/DatabaseTransaction.js.map +1 -1
  39. package/dist/resources/ResourceInterface.d.ts +7 -3
  40. package/dist/resources/ResourceInterface.js +2 -0
  41. package/dist/resources/ResourceInterface.js.map +1 -1
  42. package/dist/resources/Table.d.ts +151 -445
  43. package/dist/resources/Table.js +209 -8
  44. package/dist/resources/Table.js.map +1 -1
  45. package/dist/resources/analytics/write.d.ts +11 -0
  46. package/dist/resources/analytics/write.js +3 -0
  47. package/dist/resources/analytics/write.js.map +1 -1
  48. package/dist/resources/blob.d.ts +30 -0
  49. package/dist/resources/blob.js +189 -1
  50. package/dist/resources/blob.js.map +1 -1
  51. package/dist/resources/databases.d.ts +1 -0
  52. package/dist/resources/databases.js +80 -15
  53. package/dist/resources/databases.js.map +1 -1
  54. package/dist/resources/derivedIndexRuntime.d.ts +13 -0
  55. package/dist/resources/derivedIndexRuntime.js +57 -5
  56. package/dist/resources/derivedIndexRuntime.js.map +1 -1
  57. package/dist/resources/derivedIndexes.d.ts +1 -0
  58. package/dist/resources/derivedIndexes.js +116 -15
  59. package/dist/resources/derivedIndexes.js.map +1 -1
  60. package/dist/resources/fullTextSchema.d.ts +5 -3
  61. package/dist/resources/fullTextSchema.js +48 -5
  62. package/dist/resources/fullTextSchema.js.map +1 -1
  63. package/dist/resources/fullTextSchemaLifecycle.d.ts +2 -0
  64. package/dist/resources/fullTextSchemaLifecycle.js +33 -0
  65. package/dist/resources/fullTextSchemaLifecycle.js.map +1 -1
  66. package/dist/resources/graphql.js +30 -12
  67. package/dist/resources/graphql.js.map +1 -1
  68. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +21 -1
  69. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +114 -5
  70. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  71. package/dist/resources/indexes/fullTextDerivedIndex.d.ts +11 -2
  72. package/dist/resources/indexes/fullTextDerivedIndex.js +187 -24
  73. package/dist/resources/indexes/fullTextDerivedIndex.js.map +1 -1
  74. package/dist/resources/indexes/fullTextNativeBinding.d.ts +99 -1
  75. package/dist/resources/indexes/fullTextNativeBinding.js +35 -1
  76. package/dist/resources/indexes/fullTextNativeBinding.js.map +1 -1
  77. package/dist/resources/indexes/fullTextQueryIndex.d.ts +58 -0
  78. package/dist/resources/indexes/fullTextQueryIndex.js +943 -0
  79. package/dist/resources/indexes/fullTextQueryIndex.js.map +1 -0
  80. package/dist/resources/indexes/fullTextQueryProtocol.d.ts +6 -0
  81. package/dist/resources/indexes/fullTextQueryProtocol.js +32 -0
  82. package/dist/resources/indexes/fullTextQueryProtocol.js.map +1 -0
  83. package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.d.ts +4 -1
  84. package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js +30 -7
  85. package/dist/resources/indexes/nativeFullTextDerivedIndexLifecycle.js.map +1 -1
  86. package/dist/resources/search.d.ts +41 -0
  87. package/dist/resources/search.js +468 -26
  88. package/dist/resources/search.js.map +1 -1
  89. package/dist/resources/transactionBroadcast.js +101 -50
  90. package/dist/resources/transactionBroadcast.js.map +1 -1
  91. package/dist/resources/typeParity.d.ts +9 -0
  92. package/dist/resources/typeParity.js +3 -0
  93. package/dist/resources/typeParity.js.map +1 -0
  94. package/dist/server/REST.js.map +1 -1
  95. package/dist/server/itc/serverHandlers.js +48 -8
  96. package/dist/server/itc/serverHandlers.js.map +1 -1
  97. package/dist/server/serverHelpers/contentTypes.d.ts +7 -0
  98. package/dist/server/serverHelpers/contentTypes.js +36 -23
  99. package/dist/server/serverHelpers/contentTypes.js.map +1 -1
  100. package/dist/server/serverHelpers/uwsServer.js +28 -5
  101. package/dist/server/serverHelpers/uwsServer.js.map +1 -1
  102. package/dist/server/threads/itc.js +19 -0
  103. package/dist/server/threads/itc.js.map +1 -1
  104. package/dist/server/threads/manageThreads.d.ts +3 -0
  105. package/dist/server/threads/manageThreads.js +90 -23
  106. package/dist/server/threads/manageThreads.js.map +1 -1
  107. package/dist/utility/operation_authorization.js +25 -7
  108. package/dist/utility/operation_authorization.js.map +1 -1
  109. package/dist/utility/signalling.js +3 -0
  110. package/dist/utility/signalling.js.map +1 -1
  111. package/dist/validation/searchValidator.js +22 -2
  112. package/dist/validation/searchValidator.js.map +1 -1
  113. package/npm-shrinkwrap.json +22 -22
  114. package/package.json +2 -4
  115. package/resources/DESIGN.md +19 -2
  116. package/resources/DatabaseTransaction.ts +40 -10
  117. package/resources/FULL_TEXT.md +97 -0
  118. package/resources/ResourceInterface.ts +8 -2
  119. package/resources/Table.ts +767 -17
  120. package/resources/analytics/write.ts +3 -3
  121. package/resources/blob.ts +183 -1
  122. package/resources/databases.ts +126 -14
  123. package/resources/derivedIndexRuntime.ts +79 -6
  124. package/resources/derivedIndexes.ts +142 -18
  125. package/resources/fullTextSchema.ts +55 -9
  126. package/resources/fullTextSchemaLifecycle.ts +45 -0
  127. package/resources/graphql.ts +33 -12
  128. package/resources/indexes/DESIGN.md +167 -15
  129. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +125 -5
  130. package/resources/indexes/fullTextDerivedIndex.ts +216 -32
  131. package/resources/indexes/fullTextNativeBinding.ts +133 -3
  132. package/resources/indexes/fullTextQueryIndex.ts +1120 -0
  133. package/resources/indexes/fullTextQueryProtocol.ts +35 -0
  134. package/resources/indexes/nativeFullTextDerivedIndexLifecycle.ts +28 -8
  135. package/resources/search.ts +513 -26
  136. package/resources/transactionBroadcast.ts +86 -47
  137. package/resources/typeParity.ts +20 -0
  138. package/schema.graphql +18 -5
  139. package/server/DESIGN.md +18 -0
  140. package/server/REST.ts +1 -1
  141. package/server/itc/serverHandlers.js +61 -8
  142. package/server/serverHelpers/contentTypes.ts +31 -26
  143. package/server/serverHelpers/uwsServer.ts +35 -7
  144. package/server/threads/itc.js +21 -0
  145. package/server/threads/manageThreads.js +95 -21
  146. package/studio/web/assets/{Chat-Bq60AO-g.js → Chat-CNEKMllJ.js} +13 -13
  147. package/studio/web/assets/FloatingChat-CEnWhHeR.js +23 -0
  148. package/studio/web/assets/SearchDialog-CwhJdEdY.js +1 -0
  149. package/studio/web/assets/{abnfDiagram-O67JEVCF-DpPZWMOf.js → abnfDiagram-O67JEVCF-raXE027J.js} +1 -1
  150. package/studio/web/assets/alertDialog-BbWD9Xxa.js +1 -0
  151. package/studio/web/assets/apiToken-DC5vsaaO.js +1 -0
  152. package/studio/web/assets/applications-DQZxDy0x.js +296 -0
  153. package/studio/web/assets/architecture-7GRP2DOG-DDmz3hpI.js +1 -0
  154. package/studio/web/assets/{architectureDiagram-NJMV4G6O-OmgLraID.js → architectureDiagram-NJMV4G6O-C8_Eet7w.js} +1 -1
  155. package/studio/web/assets/arrow-up-D2g_57vT.js +1 -0
  156. package/studio/web/assets/authStore-CJCaADw9.js +3 -0
  157. package/studio/web/assets/{blockDiagram-BEXU5L5S-Cv7CD3Ai.js → blockDiagram-BEXU5L5S-DR6S5-1D.js} +1 -1
  158. package/studio/web/assets/{button-MFP9n8Ck.js → button-DWwfgGWI.js} +2 -2
  159. package/studio/web/assets/{c4Diagram-YGBWAQC7-Ds1vKYnN.js → c4Diagram-YGBWAQC7-BAfZ1G01.js} +1 -1
  160. package/studio/web/assets/channel-D0AkPjJ8.js +1 -0
  161. package/studio/web/assets/{chevron-up-CFe8x5Bp.js → chevron-up-CFxbMGv2.js} +1 -1
  162. package/studio/web/assets/{chunk-3FUC2YCW-DpnBPH8B.js → chunk-3FUC2YCW-DuRp-j19.js} +1 -1
  163. package/studio/web/assets/{chunk-5DYCD2WN-LLLCQSzg.js → chunk-5DYCD2WN-BceBfIHT.js} +1 -1
  164. package/studio/web/assets/{chunk-742MDFTN-BL8J_pIO.js → chunk-742MDFTN-CIkt8ijt.js} +1 -1
  165. package/studio/web/assets/{chunk-7INBJB4K-C-hJHiiK.js → chunk-7INBJB4K-BpeAXyOi.js} +1 -1
  166. package/studio/web/assets/{chunk-7M6MHVWA-C6ZqkFbA.js → chunk-7M6MHVWA-h6WlaTzO.js} +1 -1
  167. package/studio/web/assets/{chunk-7PRAP22T-CUXM8BG6.js → chunk-7PRAP22T-KyqX6x3f.js} +1 -1
  168. package/studio/web/assets/{chunk-GTNCS2PH-DJ2BFvS1.js → chunk-GTNCS2PH-DGHLXyow.js} +1 -1
  169. package/studio/web/assets/{chunk-GWA4HPMP-C42bSqLL.js → chunk-GWA4HPMP-DsejB6gA.js} +1 -1
  170. package/studio/web/assets/{chunk-MBY4JIJT-C-5jz6Lo.js → chunk-MBY4JIJT-BwSGweQP.js} +1 -1
  171. package/studio/web/assets/{chunk-NETBCI7D-D-bzlN4r.js → chunk-NETBCI7D-C8UnOw_f.js} +1 -1
  172. package/studio/web/assets/{chunk-O7XYJQB3-ep14zA23.js → chunk-O7XYJQB3-CBS3nZYV.js} +1 -1
  173. package/studio/web/assets/{chunk-UA2S7LBM-BMRWRHED.js → chunk-UA2S7LBM-BHpEdH8D.js} +1 -1
  174. package/studio/web/assets/{chunk-WEXAMYUT-DdLYVdX1.js → chunk-WEXAMYUT-BAIhTwyh.js} +1 -1
  175. package/studio/web/assets/{chunk-Z7XXMR3K-BmKEDjcP.js → chunk-Z7XXMR3K-DSHbImdf.js} +1 -1
  176. package/studio/web/assets/{chunk-ZIGJFQKS-DLZgdNxA.js → chunk-ZIGJFQKS-Cs8fUn7y.js} +1 -1
  177. package/studio/web/assets/{classDiagram-v2-NBCMYWYE-DOI5vgKT.js → classDiagram-v2-NBCMYWYE-DmDPkt2u.js} +1 -1
  178. package/studio/web/assets/cn-6T8VranJ.js +1 -0
  179. package/studio/web/assets/{codicon-Brq4_Ui5.ttf → codicon-DF1abBS2.ttf} +0 -0
  180. package/studio/web/assets/{createLucideIcon-bziYxJli.js → createLucideIcon-fO5dL7D5.js} +1 -1
  181. package/studio/web/assets/{css.worker-l1psR_wG.js → css.worker-DoT-OOzx.js} +36 -36
  182. package/studio/web/assets/{cssMode-DJyc9mHj.js → cssMode-Dz2DxF4e.js} +1 -1
  183. package/studio/web/assets/{cynefin-OW5HDTMX-BSW-DB7S.js → cynefin-OW5HDTMX-DIvbXLSj.js} +1 -1
  184. package/studio/web/assets/{cynefinDiagram-VND7K2PF-CbAHEwxt.js → cynefinDiagram-VND7K2PF-BdtaXiCE.js} +1 -1
  185. package/studio/web/assets/{dagre-6A5THRUB-Csxewgs4.js → dagre-6A5THRUB-C7RUWHyz.js} +1 -1
  186. package/studio/web/assets/{diagram-22UHCM2B-a0rt8tY4.js → diagram-22UHCM2B-DJCvcaWP.js} +1 -1
  187. package/studio/web/assets/{diagram-3UASUU5V-lEj2SVNh.js → diagram-3UASUU5V-Du6VKWln.js} +1 -1
  188. package/studio/web/assets/{diagram-ATOU4E4O-HcrAeB-q.js → diagram-ATOU4E4O-dxRfeG4B.js} +1 -1
  189. package/studio/web/assets/{diagram-CDSNMT55-CYxiuyEx.js → diagram-CDSNMT55-l5Ga0u-W.js} +1 -1
  190. package/studio/web/assets/{diagram-MLGK6HIB-ChbCJTgJ.js → diagram-MLGK6HIB-CNQY47LA.js} +1 -1
  191. package/studio/web/assets/{diagram-MPIPVDR6-DaX9Y-N3.js → diagram-MPIPVDR6-7prveD1B.js} +1 -1
  192. package/studio/web/assets/{dist-Cz2Y6-6G.js → dist-BrRuMUzt.js} +1 -1
  193. package/studio/web/assets/{download-CtostjYn.js → download-Cn6tPG4L.js} +1 -1
  194. package/studio/web/assets/{ebnfDiagram-ZINNZB2B-CwAsENB1.js → ebnfDiagram-ZINNZB2B-5OnkTpfi.js} +1 -1
  195. package/studio/web/assets/{editor-DDIsQfj6.js → editor-B3mjd3tz.js} +1 -1
  196. package/studio/web/assets/editor.worker-Yd1NR0QT.js +33 -0
  197. package/studio/web/assets/{elk-276RUBZZ-Hexg9E4D.js → elk-276RUBZZ-DxDQPjQT.js} +1 -1
  198. package/studio/web/assets/{erDiagram-OPXOYQCR-CeC-wwO1.js → erDiagram-OPXOYQCR-CQlCX84D.js} +1 -1
  199. package/studio/web/assets/eventmodeling-NTZA5JFV-DONQf5aG.js +1 -0
  200. package/studio/web/assets/flowDiagram-KWPJA3E3-BoZ8_QR1.js +1 -0
  201. package/studio/web/assets/{ganttDiagram-FUAMR5RP-C2_Hhl1-.js → ganttDiagram-FUAMR5RP-bloX3Voy.js} +1 -1
  202. package/studio/web/assets/{getAnalytics-CjthMQqF.js → getAnalytics-C0FHjtdQ.js} +1 -1
  203. package/studio/web/assets/{gitGraph-4MIJSDKK-xIk5Vuew.js → gitGraph-4MIJSDKK-BrXWmJt2.js} +1 -1
  204. package/studio/web/assets/{gitGraphDiagram-X574FWY7-CMxJ34up.js → gitGraphDiagram-X574FWY7-C2KnnZeY.js} +1 -1
  205. package/studio/web/assets/{html-g1BJHxLy.js → html-BsRGM7tp.js} +1 -1
  206. package/studio/web/assets/{html.worker-DdwM4IrA.js → html.worker-B1dwY0Ay.js} +27 -27
  207. package/studio/web/assets/{htmlMode-Vh5IglCQ.js → htmlMode-tUKJaA3o.js} +1 -1
  208. package/studio/web/assets/index-72cEH474.css +1 -0
  209. package/studio/web/assets/index-BuqFjN5K.js +828 -0
  210. package/studio/web/assets/index.lazy-CcpfQFY_.js +2 -0
  211. package/studio/web/assets/{info-A6RAGUB7-o49TEbj-.js → info-A6RAGUB7-BhzBHe4U.js} +1 -1
  212. package/studio/web/assets/{infoDiagram-VRGFBTTK-Cjhh7yzZ.js → infoDiagram-VRGFBTTK-B0J1132p.js} +1 -1
  213. package/studio/web/assets/{ishikawaDiagram-OU5B5YK6-C63aXRmh.js → ishikawaDiagram-OU5B5YK6-BNk0tuQx.js} +1 -1
  214. package/studio/web/assets/{javascript-5VIolqf7.js → javascript-BZ_uRGHl.js} +1 -1
  215. package/studio/web/assets/{journeyDiagram-ZHPQQLJL-D_o0P5Mu.js → journeyDiagram-ZHPQQLJL-Be79RMQM.js} +1 -1
  216. package/studio/web/assets/json.worker-BQqXGI3X.js +65 -0
  217. package/studio/web/assets/{jsonMode-DG4zQraI.js → jsonMode-DgB-aFMj.js} +1 -1
  218. package/studio/web/assets/{kanban-definition-PNTS6WVX-CTI9SSxY.js → kanban-definition-PNTS6WVX-4uuoQP9W.js} +1 -1
  219. package/studio/web/assets/{languageServices-t5FBpX-i.js → languageServices-BleuBw17.js} +1 -1
  220. package/studio/web/assets/localStorageKeys-etzFY_xV.js +1 -0
  221. package/studio/web/assets/{lspLanguageFeatures-DgVnNPh-.js → lspLanguageFeatures-ByVYLSsQ.js} +1 -1
  222. package/studio/web/assets/{mermaid-parser.core-C2lcH_jP.js → mermaid-parser.core-V1UYMeXV.js} +3 -3
  223. package/studio/web/assets/{mermaid.core-BG5bjfIG.js → mermaid.core-1PX6R49U.js} +4 -4
  224. package/studio/web/assets/{mindmap-definition-NLK3R4M7-BK7ho6JD.js → mindmap-definition-NLK3R4M7-C70Ctm0J.js} +1 -1
  225. package/studio/web/assets/notifications-D1Nf-Edn.js +1 -0
  226. package/studio/web/assets/notifications-TuzN0IMi.js +1 -0
  227. package/studio/web/assets/{packet-AYTQ26CC-BHPy6KBV.js → packet-AYTQ26CC-DWbXHx5g.js} +1 -1
  228. package/studio/web/assets/{pegDiagram-GJSIUBJH-DzjwG3jO.js → pegDiagram-GJSIUBJH-d6z1cc7W.js} +1 -1
  229. package/studio/web/assets/{pie-WAS4IAKB-D0cP-DTU.js → pie-WAS4IAKB-DjoTm3yr.js} +1 -1
  230. package/studio/web/assets/{pieDiagram-5QR66LMP-C_hOP9Lv.js → pieDiagram-5QR66LMP-Dz3RHpDA.js} +1 -1
  231. package/studio/web/assets/profile-D7C7Lupz.js +1 -0
  232. package/studio/web/assets/{quadrantDiagram-O4NWA36T-CZW8PbV1.js → quadrantDiagram-O4NWA36T-CIF5Qkou.js} +1 -1
  233. package/studio/web/assets/{radar-RG4KPBEZ-C8fk0U81.js → radar-RG4KPBEZ-CVBgjCOj.js} +1 -1
  234. package/studio/web/assets/{railroad-74A4TZTK-B60FWm17.js → railroad-74A4TZTK-BdhXw10Q.js} +1 -1
  235. package/studio/web/assets/railroad-abnf-HS5TGJTU-BcoQPN39.js +1 -0
  236. package/studio/web/assets/railroad-ebnf-LZEXJU2U-B1Lke2JN.js +1 -0
  237. package/studio/web/assets/railroad-peg-WCYAUIDC-D-oJFkiV.js +1 -0
  238. package/studio/web/assets/{railroadDiagram-XR7U4H2S-DyyNmgbU.js → railroadDiagram-XR7U4H2S-y3Jv2U6N.js} +1 -1
  239. package/studio/web/assets/regions-nGQCm-v8.js +1 -0
  240. package/studio/web/assets/register-C3x9Ze6P.js +909 -0
  241. package/studio/web/assets/register-DjpsE8to.css +1 -0
  242. package/studio/web/assets/{requirementDiagram-PLB6GJNP-C4H-mLKu.js → requirementDiagram-PLB6GJNP-ChINZEmu.js} +1 -1
  243. package/studio/web/assets/restartGate-nHAFLDh-.js +1 -0
  244. package/studio/web/assets/{sankeyDiagram-IPEJSGJF-DHDyga4c.js → sankeyDiagram-IPEJSGJF-D9AyLce2.js} +1 -1
  245. package/studio/web/assets/{sequenceDiagram-PO4LG4MO-CdNcHXRI.js → sequenceDiagram-PO4LG4MO-hfk4aIEm.js} +1 -1
  246. package/studio/web/assets/{setComponentFile-z4-qJCbA.js → setComponentFile-DpjrRqeG.js} +1 -1
  247. package/studio/web/assets/{setup-CS50uJ4x.js → setup-CGiKLZ9e.js} +2 -2
  248. package/studio/web/assets/{stateDiagram-v2-GCMORJYK-CpKuXq15.js → stateDiagram-v2-GCMORJYK-Bs8RIb9K.js} +1 -1
  249. package/studio/web/assets/status-CGJwrNPH.js +61 -0
  250. package/studio/web/assets/{swimlanes-2SLR337P-NRHa2D8I.js → swimlanes-2SLR337P-B52xrf6t.js} +1 -1
  251. package/studio/web/assets/swimlanesDiagram-TC7HE7FX-DfN7BAP5.js +8 -0
  252. package/studio/web/assets/tabs-BCTQzoQK.js +1 -0
  253. package/studio/web/assets/{timeline-definition-EJHVYXUP-D39Y-xpq.js → timeline-definition-EJHVYXUP-DJSITpQX.js} +1 -1
  254. package/studio/web/assets/toggleHighContrast-C-Ne6P7T.js +51 -0
  255. package/studio/web/assets/toggleHighContrast-n7clI0si.css +1 -0
  256. package/studio/web/assets/{treeView-Q6P3EWNA-CRWR9OqD.js → treeView-Q6P3EWNA-C-epdxRo.js} +1 -1
  257. package/studio/web/assets/{treemap-WGGIJYW6-ZfXyJgLO.js → treemap-WGGIJYW6-DOVyp_zw.js} +1 -1
  258. package/studio/web/assets/{ts.worker-DDMUrpqh.js → ts.worker-Clm12uY6.js} +131 -131
  259. package/studio/web/assets/{tsMode-D83Gg3cq.js → tsMode-CSreYTys.js} +1 -1
  260. package/studio/web/assets/{typescript-Dfp-GrNq.js → typescript-MnxBk89W.js} +1 -1
  261. package/studio/web/assets/{useEntityRestURL-BKetlINf.js → useEntityRestURL-TNrh77-s.js} +1 -1
  262. package/studio/web/assets/{usecaseDiagram-POWQR4AR-jf-zIDZ_.js → usecaseDiagram-POWQR4AR-B6252cbq.js} +1 -1
  263. package/studio/web/assets/{vendor-core-BdkdbUek.js → vendor-core-6LhfJlRC.js} +1 -1
  264. package/studio/web/assets/vendor-datadog-ozKf-pts.js +6 -0
  265. package/studio/web/assets/{vendor-react-izWchTRL.js → vendor-react-BOliS0BL.js} +1 -1
  266. package/studio/web/assets/vendor-tanstack-Bntfka-M.js +2 -0
  267. package/studio/web/assets/vendor-ui-9EYYDgoN.js +41 -0
  268. package/studio/web/assets/{vennDiagram-UO4OBE2U-BKYAZUh_.js → vennDiagram-UO4OBE2U-DZTsDA4k.js} +1 -1
  269. package/studio/web/assets/{wardley-WFR3VGLG-BO7f36ci.js → wardley-WFR3VGLG-Cfqoe6z4.js} +1 -1
  270. package/studio/web/assets/{wardleyDiagram-VNRHLVJA-Djb0JDQc.js → wardleyDiagram-VNRHLVJA-qu_f7Tvl.js} +1 -1
  271. package/studio/web/assets/{workers-CCvmDLH3.js → workers-BfZlUNVI.js} +3 -3
  272. package/studio/web/assets/{xml-06pZp00U.js → xml-BHd6qGH7.js} +1 -1
  273. package/studio/web/assets/{xychartDiagram-PMCCYNJV-BQh9ubvb.js → xychartDiagram-PMCCYNJV-UrOKRsmR.js} +1 -1
  274. package/studio/web/assets/{yaml-iSnWMVlV.js → yaml-C53R8OQq.js} +1 -1
  275. package/studio/web/assets/{yaml.worker-DAK2o_H1.js → yaml.worker-D9JekGdp.js} +183 -183
  276. package/studio/web/index.html +15 -15
  277. package/utility/operation_authorization.ts +27 -7
  278. package/utility/signalling.ts +3 -0
  279. package/validation/searchValidator.ts +22 -13
  280. package/studio/web/assets/FloatingChat-D4560T0p.js +0 -23
  281. package/studio/web/assets/alertDialog-UrbiMgQt.js +0 -1
  282. package/studio/web/assets/apiToken-BEvbFPMc.js +0 -1
  283. package/studio/web/assets/applications-CSUVTH4E.js +0 -296
  284. package/studio/web/assets/architecture-7GRP2DOG-11l4Ny1l.js +0 -1
  285. package/studio/web/assets/authStore-5jSS5pTW.js +0 -3
  286. package/studio/web/assets/channel-BDtouPd6.js +0 -1
  287. package/studio/web/assets/dialog-DTepachC.js +0 -1
  288. package/studio/web/assets/editor.worker-CkkvdXvO.js +0 -33
  289. package/studio/web/assets/eventmodeling-NTZA5JFV-CFb03e0o.js +0 -1
  290. package/studio/web/assets/flowDiagram-KWPJA3E3-Cidi00_Y.js +0 -1
  291. package/studio/web/assets/index-CPoDzQTe.css +0 -1
  292. package/studio/web/assets/index-DGRAzvO4.js +0 -824
  293. package/studio/web/assets/index.lazy-vgSic7et.js +0 -2
  294. package/studio/web/assets/json.worker-CSi2hF-a.js +0 -65
  295. package/studio/web/assets/notifications-BeWcX04m.js +0 -1
  296. package/studio/web/assets/notifications-DUGWrWoO.js +0 -1
  297. package/studio/web/assets/profile-BvUEDBzW.js +0 -1
  298. package/studio/web/assets/railroad-abnf-HS5TGJTU-CLMVZvH0.js +0 -1
  299. package/studio/web/assets/railroad-ebnf-LZEXJU2U--tlwFoVB.js +0 -1
  300. package/studio/web/assets/railroad-peg-WCYAUIDC-Dpuslblm.js +0 -1
  301. package/studio/web/assets/regions-CB-_2m7M.js +0 -1
  302. package/studio/web/assets/register-1w1Zr3b2.js +0 -848
  303. package/studio/web/assets/register-jjEx9u7D.css +0 -1
  304. package/studio/web/assets/setLocalStorage-D_kflv4U.js +0 -1
  305. package/studio/web/assets/status-CI0wOdOE.js +0 -61
  306. package/studio/web/assets/swimlanesDiagram-TC7HE7FX-BBk4t4hO.js +0 -8
  307. package/studio/web/assets/tabs-CnQ_zenf.js +0 -1
  308. package/studio/web/assets/toggleHighContrast--ixms01u.css +0 -1
  309. package/studio/web/assets/toggleHighContrast-BsAE4jPe.js +0 -49
  310. package/studio/web/assets/useLocalStorage-BYoZF60G.js +0 -1
  311. package/studio/web/assets/vendor-datadog-fCQhBHGk.js +0 -6
  312. package/studio/web/assets/vendor-tanstack-CQ1aRjVa.js +0 -2
  313. package/studio/web/assets/vendor-ui-aE1tHcW7.js +0 -41
  314. package/studio/web/assets/x-vYntCxm4.js +0 -1
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.COERCIBLE_OPERATORS = void 0;
3
+ exports.CandidateKeySet = exports.COERCIBLE_OPERATORS = exports.fullTextComparatorMode = void 0;
4
4
  exports.executeConditions = executeConditions;
5
5
  exports.searchByIndex = searchByIndex;
6
6
  exports.findAttribute = findAttribute;
@@ -22,6 +22,8 @@ const DatabaseTransaction_ts_1 = require("./DatabaseTransaction.js");
22
22
  const write_1 = require("./analytics/write");
23
23
  const rocksdb_js_1 = require("@harperfast/rocksdb-js");
24
24
  const Headers_ts_1 = require("../server/serverHelpers/Headers.js");
25
+ const fullTextQueryProtocol_ts_1 = require("./indexes/fullTextQueryProtocol.js");
26
+ Object.defineProperty(exports, "fullTextComparatorMode", { enumerable: true, get: function () { return fullTextQueryProtocol_ts_1.fullTextComparatorMode; } });
25
27
  // these are ratios/percentages of overall table size
26
28
  const OPEN_RANGE_ESTIMATE = 0.3;
27
29
  const BETWEEN_ESTIMATE = 0.1;
@@ -70,7 +72,12 @@ exports.COERCIBLE_OPERATORS = {
70
72
  eq: true,
71
73
  };
72
74
  function executeConditions(conditions, operator, table, txn, request, context, transformToEntries, filtered, recordAccess) {
75
+ conditions = combineFullTextConditions(conditions, operator, table);
73
76
  const firstSearch = conditions[0];
77
+ // A same-index full-text OR is already one native expression. Route that single traversal through
78
+ // the AND path so row and vector guards are pushed down before the requested page is filled.
79
+ if (operator === 'or' && conditions.length === 1 && firstSearch.fullTextQuery)
80
+ operator = 'and';
74
81
  // Record-level guards (caller-supplied vectorFilter + rowFilter) apply to every record
75
82
  // the query returns, independent of which condition leads (#1241). `recordAccess` is supplied only on
76
83
  // the top-level executeConditions call (Table.search) and deliberately NOT threaded into the recursive
@@ -104,18 +111,27 @@ function executeConditions(conditions, operator, table, txn, request, context, t
104
111
  }
105
112
  else {
106
113
  // AND: use the indexed query for the first condition and filter by all subsequent conditions.
107
- if (isFilterablePushdown(firstSearch, table)) {
114
+ const pushdownIndex = filterablePushdownIndex(firstSearch, table);
115
+ if (pushdownIndex) {
108
116
  // The lead is a filterable custom index (HNSW). It doesn't populate the join map (`filtered`)
109
117
  // that sibling filters read, so — unlike the general case below — we can build the sibling
110
118
  // filters BEFORE executing the lead and push them into the traversal, so filtering happens
111
119
  // during the graph walk instead of post-filtering an under-filled candidate set (#1241). They
112
120
  // also stay in the post-filter chain as a deterministic, harmless re-check.
113
- const filters = mapConditionsToFilters(conditions.slice(1), true, firstSearch.estimated_count);
121
+ const siblings = conditions.slice(1);
122
+ const filters = mapConditionsToFilters(siblings, true, firstSearch.estimated_count);
114
123
  const recordFilters = recordGuards ? filters.concat(recordGuards) : filters;
124
+ // The sibling conditions a secondary-index range scan answers exactly can seed an allow-set
125
+ // the index admits from, instead of loading and decoding a record at every visited node.
126
+ // The post-filter chain below stays whole, so the set only has to be a SUPERSET of the true
127
+ // matches — the index decides whether building it beats evaluating the predicate.
128
+ const candidateKeys = pushdownIndex.candidateKeyFilter && siblings.length > 0
129
+ ? planCandidateKeys(siblings, table, txn, !recordGuards)
130
+ : undefined;
115
131
  // Execute a COPY carrying the pushed-down predicate, never mutating the caller's condition
116
132
  // object (which may be reused across requests with different users / guards).
117
133
  const lead = recordFilters.length > 0
118
- ? { ...firstSearch, recordFilter: composeRecordFilter(recordFilters, table, context) }
134
+ ? { ...firstSearch, recordFilter: composeRecordFilter(recordFilters, table, context), candidateKeys }
119
135
  : firstSearch;
120
136
  const results = executeCondition(lead);
121
137
  return recordFilters.length > 0 ? transformToEntries(results, recordFilters) : results;
@@ -137,6 +153,7 @@ function executeConditions(conditions, operator, table, txn, request, context, t
137
153
  filtered,
138
154
  context,
139
155
  minResults: request.limit === 0 ? 0 : request.limit !== undefined ? (request.offset || 0) + request.limit : undefined,
156
+ resultOffset: request.offset || 0,
140
157
  });
141
158
  }
142
159
  function mapConditionsToFilters(conditions, intersection, estimatedIncomingCount) {
@@ -181,13 +198,141 @@ function buildRecordGuards(recordAccess) {
181
198
  guards.push(recordAccess.rowFilter);
182
199
  return guards.length > 0 ? guards : undefined;
183
200
  }
184
- /** True when a condition's index is a custom index that participates in predicate-aware traversal (HNSW). */
185
- function isFilterablePushdown(condition, table) {
201
+ function conditionIndex(table, attributeName, preferFullText = false) {
202
+ if (Array.isArray(attributeName) && attributeName.length === 1)
203
+ attributeName = attributeName[0];
204
+ if (typeof attributeName !== 'string')
205
+ return;
206
+ return preferFullText ? table.fullTextQueryIndexes?.[attributeName] : table.indices?.[attributeName];
207
+ }
208
+ function combineFullTextConditions(conditions, operator, table) {
209
+ if (table.hasFullTextQueryIndexes === false || !containsFullTextCondition(conditions, table))
210
+ return conditions;
211
+ let indexName;
212
+ let positive = false;
213
+ const expressions = [];
214
+ const leaves = [];
215
+ const remaining = [];
216
+ let includeHighlights = false;
217
+ let maxIndexLagMilliseconds;
218
+ let waitForIndexMilliseconds;
219
+ for (const condition of conditions) {
220
+ const compiled = compileFullTextExpression(condition, table);
221
+ if (!compiled) {
222
+ remaining.push(condition);
223
+ continue;
224
+ }
225
+ if (operator === 'or' && compiled.remaining?.length)
226
+ throw new hdbError_ts_1.ClientError('A full-text OR group cannot mix full-text and record conditions', 400);
227
+ if (indexName && indexName !== compiled.indexName)
228
+ throw new hdbError_ts_1.ClientError('One query cannot combine conditions from different full-text indexes', 400);
229
+ indexName = compiled.indexName;
230
+ expressions.push(compiled.expression);
231
+ leaves.push(...compiled.leaves);
232
+ positive ||= compiled.positive;
233
+ includeHighlights ||= compiled.includeHighlights;
234
+ maxIndexLagMilliseconds = mergeFullTextOption('maxIndexLagMilliseconds', maxIndexLagMilliseconds, compiled.maxIndexLagMilliseconds);
235
+ waitForIndexMilliseconds = mergeFullTextOption('waitForIndexMilliseconds', waitForIndexMilliseconds, compiled.waitForIndexMilliseconds);
236
+ if (compiled.remaining)
237
+ remaining.push(...compiled.remaining);
238
+ }
239
+ if (!indexName)
240
+ return conditions;
241
+ if (operator === 'or' && remaining.length > 0)
242
+ throw new hdbError_ts_1.ClientError('A full-text OR group can only contain conditions on the same full-text index', 400);
243
+ if (!positive)
244
+ throw new hdbError_ts_1.ClientError('A full-text query requires at least one non-negated condition', 400);
245
+ const expression = expressions.length === 1 ? expressions[0] : { operator: operator === 'or' ? 'or' : 'and', clauses: expressions };
246
+ const combined = {
247
+ attribute: indexName,
248
+ comparator: 'matches',
249
+ value: '',
250
+ fullTextQuery: expression,
251
+ fullTextLeaves: leaves,
252
+ includeHighlights,
253
+ ...(maxIndexLagMilliseconds === undefined ? null : { maxIndexLagMilliseconds }),
254
+ ...(waitForIndexMilliseconds === undefined ? null : { waitForIndexMilliseconds }),
255
+ };
256
+ return operator === 'or' ? [combined] : [combined, ...remaining];
257
+ }
258
+ function containsFullTextCondition(conditions, table) {
259
+ for (const condition of conditions) {
260
+ if (condition.conditions) {
261
+ if (containsFullTextCondition(condition.conditions, table))
262
+ return true;
263
+ continue;
264
+ }
265
+ const indexName = condition[0] ?? condition.attribute;
266
+ if (typeof indexName === 'string' &&
267
+ table.fullTextQueryIndexes?.[indexName] &&
268
+ (0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(condition.comparator))
269
+ return true;
270
+ }
271
+ return false;
272
+ }
273
+ function compileFullTextExpression(condition, table) {
274
+ if (condition.conditions) {
275
+ const children = condition.conditions.map((child) => compileFullTextExpression(child, table));
276
+ const fullTextChildren = children.filter(Boolean);
277
+ if (fullTextChildren.length === 0)
278
+ return;
279
+ const groupOperator = condition.operator === 'or' ? 'or' : 'and';
280
+ const remaining = condition.conditions.filter((_child, index) => !children[index]);
281
+ for (const child of fullTextChildren)
282
+ if (child.remaining)
283
+ remaining.push(...child.remaining);
284
+ if (groupOperator === 'or' && remaining.length > 0)
285
+ throw new hdbError_ts_1.ClientError('A full-text OR group cannot mix full-text and record conditions', 400);
286
+ const indexName = fullTextChildren[0].indexName;
287
+ if (fullTextChildren.some((child) => child.indexName !== indexName))
288
+ throw new hdbError_ts_1.ClientError('One query cannot combine conditions from different full-text indexes', 400);
289
+ return {
290
+ indexName,
291
+ expression: { operator: groupOperator, clauses: fullTextChildren.map((child) => child.expression) },
292
+ leaves: fullTextChildren.flatMap((child) => child.leaves),
293
+ positive: fullTextChildren.some((child) => child.positive),
294
+ includeHighlights: fullTextChildren.some((child) => child.includeHighlights),
295
+ maxIndexLagMilliseconds: fullTextChildren.reduce((value, child) => mergeFullTextOption('maxIndexLagMilliseconds', value, child.maxIndexLagMilliseconds), undefined),
296
+ waitForIndexMilliseconds: fullTextChildren.reduce((value, child) => mergeFullTextOption('waitForIndexMilliseconds', value, child.waitForIndexMilliseconds), undefined),
297
+ ...(remaining.length > 0 ? { remaining } : null),
298
+ };
299
+ }
300
+ const indexName = condition[0] ?? condition.attribute;
301
+ if (typeof indexName !== 'string' || !table.fullTextQueryIndexes?.[indexName])
302
+ return;
303
+ const mode = (0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(condition.comparator);
304
+ if (!mode)
305
+ return;
306
+ if (typeof (condition[1] ?? condition.value) !== 'string')
307
+ throw new hdbError_ts_1.ClientError(`Full-text index '${indexName}' requires a full-text comparator and string value`, 400);
308
+ const text = condition[1] ?? condition.value;
309
+ const leaf = { text, mode, ...(condition.fields ? { fields: condition.fields } : null) };
310
+ return {
311
+ indexName,
312
+ expression: condition.negated ? { operator: 'not', clause: leaf } : leaf,
313
+ leaves: condition.negated ? [] : [leaf],
314
+ positive: !condition.negated,
315
+ includeHighlights: condition.includeHighlights === true,
316
+ maxIndexLagMilliseconds: condition.maxIndexLagMilliseconds,
317
+ waitForIndexMilliseconds: condition.waitForIndexMilliseconds,
318
+ };
319
+ }
320
+ function mergeFullTextOption(name, current, next) {
321
+ if (next === undefined)
322
+ return current;
323
+ if (current !== undefined && current !== next)
324
+ throw new hdbError_ts_1.ClientError(`Combined full-text conditions must use the same ${name}`, 400);
325
+ return next;
326
+ }
327
+ function filterablePushdownIndex(condition, table) {
186
328
  const attributeName = condition?.attribute ?? condition?.[0];
187
329
  if (attributeName == null || table == null)
188
- return false;
189
- const index = attributeName === table.primaryKey ? table.primaryStore : table.indices?.[attributeName];
190
- return Boolean(index?.customIndex?.filteredSearch);
330
+ return undefined;
331
+ const fullTextMode = Boolean((0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(condition?.comparator));
332
+ const index = attributeName === table.primaryKey && !fullTextMode
333
+ ? table.primaryStore
334
+ : conditionIndex(table, attributeName, fullTextMode);
335
+ return index?.customIndex?.filteredSearch ? index.customIndex : undefined;
191
336
  }
192
337
  /**
193
338
  * Compose a set of record predicates into the single `(primaryKey) => boolean` the custom index calls
@@ -198,12 +343,12 @@ function isFilterablePushdown(condition, table) {
198
343
  function composeRecordFilter(recordFilters, table, context) {
199
344
  const memo = new Map();
200
345
  const transaction = context && table._readTxnForContext ? table._readTxnForContext(context) : undefined;
201
- return (primaryKey) => {
346
+ return (primaryKey, suppliedEntry) => {
202
347
  const cached = memo.get(primaryKey);
203
348
  if (cached !== undefined)
204
349
  return cached;
205
350
  let verdict = false;
206
- const entry = table.primaryStore.getEntry(primaryKey, { transaction });
351
+ const entry = suppliedEntry ?? table.primaryStore.getEntry(primaryKey, { transaction });
207
352
  const record = entry?.value;
208
353
  if (record != null) {
209
354
  (0, Table_ts_1.freezeRecord)(record);
@@ -219,6 +364,263 @@ function composeRecordFilter(recordFilters, table, context) {
219
364
  return verdict;
220
365
  };
221
366
  }
367
+ /**
368
+ * Primary keys by storage identity. Strings and non-zero numbers are their own identity, which spares
369
+ * encoding the common key shapes, and the stores encode -0 and 0 apart where Set membership does not.
370
+ * The rest go by writeKeyId, the encoder the stores index by — flattenKey joins composite keys with
371
+ * NUL and collapses ['a', 'b\0c'] and ['a\0b', 'c'] — kept apart from the values because an encoded
372
+ * identity is a string that can equal a string key: writeKeyId(['a', 'b']) is 'a\0b'. An index scan
373
+ * decodes an int64 as the number it encodes as when that is exact, while a traversal may report the
374
+ * BigInt, so the two are the same key.
375
+ */
376
+ class CandidateKeySet {
377
+ #byValue = new Set();
378
+ #encoded = new Set();
379
+ #zero = false;
380
+ #negativeZero = false;
381
+ #holdsBigInt = false;
382
+ add(primaryKey) {
383
+ if (typeof primaryKey === 'string' || (typeof primaryKey === 'number' && primaryKey !== 0))
384
+ this.#byValue.add(primaryKey);
385
+ else if (primaryKey === 0) {
386
+ if (Object.is(primaryKey, -0))
387
+ this.#negativeZero = true;
388
+ else
389
+ this.#zero = true;
390
+ }
391
+ else {
392
+ if (typeof primaryKey === 'bigint')
393
+ this.#holdsBigInt = true;
394
+ this.#encoded.add((0, DatabaseTransaction_ts_1.writeKeyId)(primaryKey));
395
+ }
396
+ }
397
+ has(primaryKey) {
398
+ if (typeof primaryKey === 'string')
399
+ return this.#byValue.has(primaryKey);
400
+ if (typeof primaryKey === 'number') {
401
+ if (primaryKey === 0)
402
+ return Object.is(primaryKey, -0) ? this.#negativeZero : this.#zero;
403
+ return this.#byValue.has(primaryKey) || (this.#holdsBigInt && this.#encoded.has((0, DatabaseTransaction_ts_1.writeKeyId)(primaryKey)));
404
+ }
405
+ if (typeof primaryKey === 'bigint') {
406
+ const number = Number(primaryKey);
407
+ if (BigInt(number) === primaryKey && (number === 0 ? this.#zero : this.#byValue.has(number)))
408
+ return true;
409
+ }
410
+ return this.#encoded.has((0, DatabaseTransaction_ts_1.writeKeyId)(primaryKey));
411
+ }
412
+ get size() {
413
+ return this.#byValue.size + this.#encoded.size + (this.#zero ? 1 : 0) + (this.#negativeZero ? 1 : 0);
414
+ }
415
+ }
416
+ exports.CandidateKeySet = CandidateKeySet;
417
+ // Comparators whose secondary-index range is EXACTLY the condition over the values an index holds,
418
+ // so scanning that range yields every indexed match and nothing else. `ne`, `in`, `contains`,
419
+ // `ends_with` and any negated condition are excluded because searchByIndex answers them with a scan
420
+ // plus a record filter.
421
+ const CANDIDATE_KEY_COMPARATORS = new Set([
422
+ 'equals',
423
+ 'lt',
424
+ 'le',
425
+ 'gt',
426
+ 'ge',
427
+ 'between',
428
+ 'gele',
429
+ 'gelt',
430
+ 'gtlt',
431
+ 'gtle',
432
+ ]);
433
+ /**
434
+ * Plan one leaf condition as an index range scan, or undefined when its matches cannot be read off
435
+ * an index exactly, or when the scan's size cannot be estimated.
436
+ */
437
+ function planCandidateKeyScan(condition, table) {
438
+ if (condition.negated)
439
+ return undefined;
440
+ const attributeName = condition.attribute ?? condition[0];
441
+ if (typeof attributeName !== 'string' || attributeName === table.primaryKey)
442
+ return undefined;
443
+ const index = usableIndex(table, attributeName);
444
+ if (!index || index.customIndex)
445
+ return undefined;
446
+ const comparator = ALTERNATE_COMPARATOR_NAMES[condition.comparator] ?? condition.comparator ?? 'equals';
447
+ if (!CANDIDATE_KEY_COMPARATORS.has(comparator))
448
+ return undefined;
449
+ // The scan has to hold every key the record predicate admits, or results would depend on whether
450
+ // the plan was built. The index cannot supply a record lacking the attribute, a null one unless it
451
+ // indexes nulls, or one written as -0: ordered-binary encodes -0 past every number, and that entry's
452
+ // primary key decodes wrong. compareKeys ranks undefined below every scalar, so `rank < 100` admits
453
+ // a record with no rank at all; a condition admitting any of these is left to the predicate.
454
+ const predicate = filterByType(condition, table, undefined, null);
455
+ if (predicate({}) || predicate({ [attributeName]: 0 }) || (!index.indexNulls && predicate({ [attributeName]: null })))
456
+ return undefined;
457
+ let value = condition[1] ?? condition.value;
458
+ if (value instanceof Date)
459
+ value = value.getTime();
460
+ let start;
461
+ let end;
462
+ let inclusiveEnd = false;
463
+ let exclusiveStart = false;
464
+ switch (comparator) {
465
+ case 'lt':
466
+ // null, not searchByIndex's `true`: `true` sorts ABOVE null and false, so starting there
467
+ // would drop indexed rows the record predicate's compareKeys admits — an omission the
468
+ // post-filter cannot undo.
469
+ start = null;
470
+ end = value;
471
+ break;
472
+ case 'le':
473
+ start = null;
474
+ end = value;
475
+ inclusiveEnd = true;
476
+ break;
477
+ case 'gt':
478
+ start = value;
479
+ exclusiveStart = true;
480
+ break;
481
+ case 'ge':
482
+ start = value;
483
+ break;
484
+ case 'between':
485
+ case 'gele':
486
+ case 'gelt':
487
+ case 'gtlt':
488
+ case 'gtle':
489
+ if (!Array.isArray(value))
490
+ return undefined;
491
+ start = value[0] instanceof Date ? value[0].getTime() : value[0];
492
+ end = value[1] instanceof Date ? value[1].getTime() : value[1];
493
+ inclusiveEnd = comparator === 'gele' || comparator === 'gtle' || comparator === 'between';
494
+ exclusiveStart = comparator === 'gtlt' || comparator === 'gtle';
495
+ break;
496
+ default:
497
+ start = value;
498
+ end = value;
499
+ inclusiveEnd = true;
500
+ }
501
+ // ordered-binary encodes -0 above every number, where the predicate's compareKeys calls it 0.
502
+ if (start === 0)
503
+ start = 0;
504
+ if (end === 0)
505
+ end = 0;
506
+ // An over-length string key is stored truncated behind an overflow marker, which makes the range
507
+ // wider than the condition and needs a record filter to narrow again.
508
+ if ((typeof start === 'string' && start.length > terms_ts_1.MAX_SEARCH_KEY_LENGTH) ||
509
+ (typeof end === 'string' && end.length > terms_ts_1.MAX_SEARCH_KEY_LENGTH))
510
+ return undefined;
511
+ if (condition.estimated_count === undefined)
512
+ estimateCondition(table)(condition);
513
+ let estimatedCount = condition.estimated_count;
514
+ if (!(estimatedCount >= 0) || !Number.isFinite(estimatedCount))
515
+ return undefined;
516
+ // estimateRangeCondition measures the range searchByIndex would run, which starts at `true`. Only
517
+ // lt/le widen below that, and the block they add is read FIRST and can dwarf the rest of the
518
+ // range, so it has to be counted or the scan overruns a budget sized without it. A statistical
519
+ // estimate, never getValuesCount: that one steps the whole block, which is the unbounded
520
+ // per-query cost this is here to avoid. The other comparators' own estimates already cover
521
+ // whatever they start at.
522
+ if (comparator === 'lt' || comparator === 'le') {
523
+ let belowTrue;
524
+ try {
525
+ belowTrue = index.estimateCount?.({ start: null, end: true })?.count;
526
+ }
527
+ catch {
528
+ return undefined;
529
+ }
530
+ if (!(belowTrue >= 0))
531
+ return undefined;
532
+ estimatedCount += belowTrue;
533
+ }
534
+ return { index, range: { start, end, inclusiveEnd, exclusiveStart }, estimatedCount };
535
+ }
536
+ function planCandidateKeys(conditions, table, transaction, guardFree) {
537
+ const terms = [];
538
+ const planned = collectCandidateKeyTerms(conditions, table, terms) && guardFree;
539
+ if (terms.length === 0)
540
+ return undefined;
541
+ const estimatedCount = terms.reduce((narrowest, term) => Math.min(narrowest, term.estimatedCount), Infinity);
542
+ return {
543
+ estimatedCount,
544
+ collect: (maxKeys) => collectCandidateKeys(terms, transaction, maxKeys, planned),
545
+ };
546
+ }
547
+ /** Returns whether every condition was covered. */
548
+ function collectCandidateKeyTerms(conditions, table, terms) {
549
+ let complete = true;
550
+ for (const condition of conditions) {
551
+ if (condition.conditions) {
552
+ if (condition.operator === 'or') {
553
+ // A union is only sound in full: dropping one branch would exclude the records only
554
+ // that branch matches, and admission may over-admit but never omit.
555
+ const scans = [];
556
+ for (const child of condition.conditions) {
557
+ const scan = child.conditions ? undefined : planCandidateKeyScan(child, table);
558
+ if (!scan) {
559
+ complete = false;
560
+ break;
561
+ }
562
+ scans.push(scan);
563
+ }
564
+ if (scans.length === condition.conditions.length) {
565
+ terms.push({ scans, estimatedCount: scans.reduce((total, scan) => total + scan.estimatedCount, 0) });
566
+ }
567
+ }
568
+ else if (!collectCandidateKeyTerms(condition.conditions, table, terms))
569
+ complete = false;
570
+ continue;
571
+ }
572
+ const scan = planCandidateKeyScan(condition, table);
573
+ if (scan)
574
+ terms.push({ scans: [scan], estimatedCount: scan.estimatedCount });
575
+ else
576
+ complete = false;
577
+ }
578
+ return complete;
579
+ }
580
+ /**
581
+ * Run the planned scans narrowest-estimate first, intersecting on storage key identity. `maxKeys`
582
+ * bounds index entries READ, not keys retained — a wide later scan whose intersection is tiny still
583
+ * reads its whole range, and that is the cost the caller budgeted against. A term whose estimate
584
+ * exceeds what is left of that budget is dropped instead: fewer AND terms is a wider superset, which
585
+ * the predicate then narrows, and spending the budget on it only to abandon the set is pure loss.
586
+ */
587
+ function collectCandidateKeys(terms, transaction, maxKeys, planned) {
588
+ const ordered = terms.length > 1 ? terms.slice().sort((a, b) => a.estimatedCount - b.estimatedCount) : terms;
589
+ let matched;
590
+ let complete = planned;
591
+ let scanned = 0;
592
+ for (const term of ordered) {
593
+ if (matched && term.estimatedCount > maxKeys - scanned) {
594
+ complete = false;
595
+ continue;
596
+ }
597
+ const next = new CandidateKeySet();
598
+ let overran = false;
599
+ for (const scan of term.scans) {
600
+ for (const { value: primaryKey } of scan.index.getRange({ ...scan.range, values: true, transaction })) {
601
+ if (++scanned > maxKeys) {
602
+ overran = true;
603
+ break;
604
+ }
605
+ if (!matched || matched.has(primaryKey))
606
+ next.add(primaryKey);
607
+ }
608
+ if (overran)
609
+ break;
610
+ }
611
+ // A half-scanned term is not a superset of its own matches, so it is dropped whole.
612
+ if (overran) {
613
+ if (!matched)
614
+ return null;
615
+ complete = false;
616
+ break;
617
+ }
618
+ matched = next;
619
+ if (matched.size === 0)
620
+ break;
621
+ }
622
+ return { keys: matched, complete };
623
+ }
222
624
  /**
223
625
  * A record's index entries are not adjacent — the composite `[indexedValue, primaryKey]` key sorts
224
626
  * on the indexed value first — so comparing neighbours cannot collapse them and the scan has to
@@ -269,10 +671,15 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
269
671
  // fail loud rather than silently reading every option as undefined.
270
672
  if (typeof options !== 'object' || options === null)
271
673
  throw new TypeError('searchByIndex: the 5th argument is an options object (#2165), not a positional value');
272
- const { allowFullScan, filtered, context, minResults } = options;
674
+ const { allowFullScan, filtered, context, minResults, resultOffset } = options;
273
675
  let attribute_name = searchCondition[0] ?? searchCondition.attribute;
274
676
  let value = searchCondition[1] ?? searchCondition.value;
275
677
  const comparator = searchCondition.comparator;
678
+ const fullTextMode = (0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(comparator);
679
+ if (fullTextMode && typeof attribute_name !== 'string')
680
+ throw new hdbError_ts_1.ClientError('Full-text comparator requires an index directly on the queried table', 400);
681
+ if (!fullTextMode)
682
+ Table.assertFullTextRecordField?.(attribute_name);
276
683
  if (value === undefined && comparator !== 'sort') {
277
684
  throw new hdbError_ts_1.ClientError(`Search condition for ${attribute_name} must have a value`);
278
685
  }
@@ -332,8 +739,14 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
332
739
  needFullScan = true;
333
740
  }
334
741
  }
335
- const isPrimaryKey = attribute_name === Table.primaryKey || attribute_name == null;
336
- const index = isPrimaryKey ? Table.primaryStore : Table.indices[attribute_name];
742
+ if (fullTextMode) {
743
+ if (typeof attribute_name !== 'string' || !Table.fullTextIndexes?.some(({ name }) => name === attribute_name))
744
+ throw new hdbError_ts_1.ClientError('Full-text comparator requires a declared @fullText index', 400);
745
+ if (!Table.fullTextQueryIndexes?.[attribute_name]?.customIndex)
746
+ throw new hdbError_ts_1.IndexRebuildingError(`Full-text index '${attribute_name}' is not ready`);
747
+ }
748
+ const isPrimaryKey = !fullTextMode && (attribute_name === Table.primaryKey || attribute_name == null);
749
+ const index = isPrimaryKey ? Table.primaryStore : conditionIndex(Table, attribute_name, Boolean(fullTextMode));
337
750
  let start;
338
751
  let end, inclusiveEnd, exclusiveStart, stringPrefix;
339
752
  if (value instanceof Date)
@@ -345,6 +758,9 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
345
758
  start = true;
346
759
  needFullScan = true;
347
760
  }
761
+ else if (fullTextMode) {
762
+ start = true;
763
+ }
348
764
  else
349
765
  switch (ALTERNATE_COMPARATOR_NAMES[comparator] || comparator) {
350
766
  case 'lt':
@@ -521,6 +937,7 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
521
937
  // exploring until it has enough MATCHING results, rather than post-filtering an under-filled
522
938
  // candidate set. Only indexes that opt in (filteredSearch) receive it; others post-filter as before.
523
939
  const recordFilter = index.customIndex.filteredSearch ? searchCondition.recordFilter : undefined;
940
+ const candidateKeys = index.customIndex.candidateKeyFilter ? searchCondition.candidateKeys : undefined;
524
941
  const waiting = index.customIndex.filePrimary && searchCondition.waitForIndexMilliseconds > 0;
525
942
  const start = waiting ? Promise.withResolvers() : undefined;
526
943
  const controller = waiting ? new AbortController() : undefined;
@@ -530,22 +947,27 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
530
947
  ? AbortSignal.any([context.signal, controller.signal])
531
948
  : controller.signal
532
949
  : undefined;
950
+ const indexSearchStart = waiting ? start.promise.then(() => checkActive?.()) : undefined;
951
+ indexSearchStart?.catch(() => { });
533
952
  const searchContext = waiting
534
953
  ? Object.create(context, {
535
954
  signal: { value: signal },
536
- indexSearchStart: { value: start.promise.then(() => checkActive?.()) },
955
+ indexSearchStart: { value: indexSearchStart },
537
956
  })
538
957
  : context;
539
958
  const searched = index.customIndex.search(searchCondition, searchContext, {
540
959
  filter: waiting && recordFilter
541
- ? (id) => {
960
+ ? (id, entry) => {
542
961
  if (signal.aborted)
543
962
  return false;
544
963
  checkActive?.();
545
- return recordFilter(id);
964
+ return recordFilter(id, entry);
546
965
  }
547
966
  : recordFilter,
548
967
  minResults,
968
+ resultOffset,
969
+ assertTransactionActive: checkActive,
970
+ candidateKeys,
549
971
  });
550
972
  const coverage = searched.indexCoverage;
551
973
  if (!waiting && coverage && context?.responseHeaders) {
@@ -557,12 +979,13 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
557
979
  .map((entry) => {
558
980
  // if the custom index returns an entry with metadata, merge it with the loaded entry
559
981
  if (typeof entry === 'object' && entry) {
560
- const { key, ...otherProps } = entry;
982
+ const { key, loadedEntry: suppliedEntry, ...otherProps } = entry;
561
983
  if (key == null)
562
984
  return extended_iterable_1.SKIP; // primaryKey missing from HNSW node — skip rather than crash
563
- const loadedEntry = Table.primaryStore.getEntry(key, {
564
- transaction: waiting ? transaction : context && Table._readTxnForContext(context),
565
- });
985
+ const loadedEntry = suppliedEntry ??
986
+ Table.primaryStore.getEntry(key, {
987
+ transaction: waiting ? transaction : context && Table._readTxnForContext(context),
988
+ });
566
989
  if (!loadedEntry)
567
990
  return extended_iterable_1.SKIP; // record was deleted/expired or not yet visible
568
991
  (0, Table_ts_1.freezeRecord)(loadedEntry?.value);
@@ -620,8 +1043,10 @@ function searchByIndex(searchCondition, transaction, reverse, Table, options = {
620
1043
  },
621
1044
  return(value) {
622
1045
  closed = true;
623
- if (!settled)
1046
+ if (!settled) {
1047
+ start?.resolve();
624
1048
  controller?.abort();
1049
+ }
625
1050
  iteratorPromise.then((inner) => inner.return?.(value), () => { });
626
1051
  return Promise.resolve({ done: true, value });
627
1052
  },
@@ -894,7 +1319,15 @@ const ALTERNATE_COMPARATOR_NAMES = {
894
1319
  const LIST_VALUE_COMPARATORS = new Set(['in', 'between']);
895
1320
  // Base comparators that accept the `not_` prefix to produce a negated form.
896
1321
  // `not_equal` is an existing alias for `ne` and keeps its existing semantics.
897
- const NEGATABLE_BASE_COMPARATORS = new Set(['in', 'between', 'starts_with', 'ends_with', 'contains', 'equals']);
1322
+ const NEGATABLE_BASE_COMPARATORS = new Set([
1323
+ 'in',
1324
+ 'between',
1325
+ 'starts_with',
1326
+ 'ends_with',
1327
+ 'contains',
1328
+ 'equals',
1329
+ ...fullTextQueryProtocol_ts_1.FULL_TEXT_POSITIVE_COMPARATORS,
1330
+ ]);
898
1331
  /**
899
1332
  * Resolve a comparator name to a (possibly stripped) base comparator and a
900
1333
  * `negated` flag. Existing aliases are preserved as-is — the execution layer
@@ -1311,7 +1744,7 @@ function estimateRangeCondition(table, condition, searchType, fraction) {
1311
1744
  }
1312
1745
  /** The index a condition can be driven by; searchByIndex refuses a rebuilding one, so it reads as absent. */
1313
1746
  function usableIndex(table, attributeName) {
1314
- const index = attributeName == null ? undefined : table.indices[attributeName];
1747
+ const index = attributeName == null ? undefined : conditionIndex(table, attributeName);
1315
1748
  return index?.isIndexing ? undefined : index;
1316
1749
  }
1317
1750
  /**
@@ -1320,7 +1753,7 @@ function usableIndex(table, attributeName) {
1320
1753
  */
1321
1754
  function drivesRebuildingIndex(table, attributeName, relationshipOffset = 0) {
1322
1755
  if (!Array.isArray(attributeName))
1323
- return attributeName != null && attributeName !== table.primaryKey && !!table.indices[attributeName]?.isIndexing;
1756
+ return (attributeName != null && attributeName !== table.primaryKey && !!conditionIndex(table, attributeName)?.isIndexing);
1324
1757
  if (relationshipOffset >= attributeName.length - 1)
1325
1758
  return drivesRebuildingIndex(table, attributeName[relationshipOffset]);
1326
1759
  const attribute = findAttribute(table.attributes, attributeName[relationshipOffset]);
@@ -1406,6 +1839,13 @@ function estimateCondition(table) {
1406
1839
  condition.estimated_count = index ? index.getValuesCount(condition[1] ?? condition.value) : Infinity;
1407
1840
  }
1408
1841
  }
1842
+ else if ((0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(searchType)) {
1843
+ const attributeName = condition[0] ?? condition.attribute;
1844
+ const index = conditionIndex(table, attributeName, true);
1845
+ condition.estimated_count = index?.customIndex?.estimateCount
1846
+ ? index.customIndex.estimateCount(condition.value)
1847
+ : Infinity;
1848
+ }
1409
1849
  else if (searchType === 'contains' || searchType === 'ends_with' || searchType === 'ne') {
1410
1850
  const attribute_name = condition[0] ?? condition.attribute;
1411
1851
  const index = usableIndex(table, attribute_name);
@@ -1554,7 +1994,9 @@ function parseBlock(query, expectedEnd) {
1554
1994
  comparator = value;
1555
1995
  else
1556
1996
  recordError(`invalid FIQL operator ${value}`);
1557
- valueDecoder = typedDecoding; // use typed/auto-cast decoding for FIQL operators
1997
+ valueDecoder = (0, fullTextQueryProtocol_ts_1.fullTextComparatorMode)(resolveComparator(comparator).comparator)
1998
+ ? decodeURIComponent
1999
+ : typedDecoding;
1558
2000
  }
1559
2001
  else {
1560
2002
  // standard equal comparison