@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
@@ -18,6 +18,7 @@ import { toBufferKey } from 'ordered-binary';
18
18
  import { loggerWithTag } from '../../utility/logging/logger.ts';
19
19
 
20
20
  const logger = loggerWithTag('fulltext-derived-index');
21
+ const UTF8_DECODER = new TextDecoder('utf-8', { fatal: true });
21
22
 
22
23
  const DEFAULT_MAX_QUEUED_BATCHES = 16;
23
24
  export const HARPER_FULLTEXT_DEFAULT_MAX_QUEUED_BYTES = 64 * 1024 * 1024;
@@ -27,7 +28,9 @@ const DEFAULT_OPEN_RETRY_MILLISECONDS = 10;
27
28
  const DEFAULT_MAX_OPEN_RETRY_MILLISECONDS = 5_000;
28
29
  const DEFAULT_CLOSE_TIMEOUT_MILLISECONDS = 35_000;
29
30
  const DEFAULT_MAX_APPLY_SLICE_RECORDS = 256;
31
+ const DEFAULT_BLOB_READ_TIMEOUT_MILLISECONDS = 5_000;
30
32
  const APPLY_SLICE_MILLISECONDS = 5;
33
+ const MAX_FULL_TEXT_VALUE_BYTES = 1 << 20;
31
34
  const TERMINAL_OPEN_ERROR_CODES = new Set([
32
35
  'E_IDENTITY_MISMATCH',
33
36
  'E_INCOMPLETE_CREATE',
@@ -61,11 +64,11 @@ export type FullTextDerivedIndexInspection =
61
64
  export interface FullTextDerivedIndexLifecycle {
62
65
  inspect(): FullTextDerivedIndexInspection;
63
66
  open(): Promise<FullTextDerivedIndexEngine>;
64
- reset(): Promise<void>;
67
+ reset(ownerEpoch: bigint): Promise<void>;
65
68
  }
66
69
 
67
70
  export type FullTextMutationBatch = {
68
- upserts: Array<{ id: string; fields: Record<string, string | string[]> }>;
71
+ upserts: Array<{ id: string; version?: string; fields: Record<string, string | string[]> }>;
69
72
  deletes: string[];
70
73
  };
71
74
 
@@ -81,6 +84,12 @@ export type FullTextDerivedIndexBackendOptions = {
81
84
  maxOpenRetryMilliseconds?: number;
82
85
  closeTimeoutMilliseconds?: number;
83
86
  shutdownTimeoutMilliseconds?: number;
87
+ blobReadTimeoutMilliseconds?: number;
88
+ hasBlobSources?: boolean;
89
+ };
90
+
91
+ export type FullTextPublication = {
92
+ cursor?: DerivedIndexCursor;
84
93
  };
85
94
 
86
95
  type ApplyCommand = {
@@ -97,6 +106,7 @@ type BarrierCommand = {
97
106
  epoch: bigint;
98
107
  horizon: number;
99
108
  cursor?: DerivedIndexCursor;
109
+ coverageOnly?: true;
100
110
  };
101
111
 
102
112
  type Command = ApplyCommand | BarrierCommand;
@@ -123,6 +133,7 @@ class FullTextDerivedIndexConfigurationError extends FullTextDerivedIndexError {
123
133
 
124
134
  export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
125
135
  readonly id: string;
136
+ readonly publishesQueryRevisions = true;
126
137
  #lifecycle: FullTextDerivedIndexLifecycle;
127
138
  #maxQueuedBatches: number;
128
139
  #maxQueuedBytes: number;
@@ -133,6 +144,9 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
133
144
  #maxOpenRetryMilliseconds: number;
134
145
  #closeTimeoutMilliseconds: number;
135
146
  #shutdownTimeoutMilliseconds: number;
147
+ #blobReadTimeoutMilliseconds: number;
148
+ #maxBlobApplySliceMilliseconds: number;
149
+ #hasBlobSources: boolean;
136
150
  #host?: DerivedIndexBackendHost;
137
151
  #wake?: (change?: DerivedIndexBackendStateChange) => void;
138
152
  #engine?: FullTextDerivedIndexEngine;
@@ -225,6 +239,15 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
225
239
  );
226
240
  if (this.#shutdownTimeoutMilliseconds < this.#closeTimeoutMilliseconds * 2)
227
241
  throw new RangeError('shutdownTimeoutMilliseconds must be at least twice closeTimeoutMilliseconds');
242
+ this.#blobReadTimeoutMilliseconds = positiveInteger(
243
+ options.blobReadTimeoutMilliseconds ?? DEFAULT_BLOB_READ_TIMEOUT_MILLISECONDS,
244
+ 'blobReadTimeoutMilliseconds'
245
+ );
246
+ this.#maxBlobApplySliceMilliseconds = Math.max(
247
+ APPLY_SLICE_MILLISECONDS,
248
+ Math.floor((this.#shutdownTimeoutMilliseconds - this.#closeTimeoutMilliseconds) / 2)
249
+ );
250
+ this.#hasBlobSources = options.hasBlobSources !== false;
228
251
  this.#openRetryDelayMilliseconds = Math.max(1, this.#openRetryMilliseconds);
229
252
  this.#writerRetryDelayMilliseconds = Math.min(
230
253
  this.#maxOpenRetryMilliseconds,
@@ -241,13 +264,14 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
241
264
  getDurableCursor(): DerivedIndexCursor | undefined {
242
265
  this.#assertAttached();
243
266
  if (!this.#cursorInspectedForAcquisition) {
244
- this.#durableCursor = this.#inspectDurableCursor();
267
+ const publication = this.#inspectPublication();
268
+ this.#durableCursor = publication.cursor;
245
269
  this.#cursorInspectedForAcquisition = true;
246
270
  }
247
271
  return this.#durableCursor;
248
272
  }
249
273
 
250
- #inspectDurableCursor(): DerivedIndexCursor | undefined {
274
+ #inspectPublication(): FullTextPublication {
251
275
  let inspection: FullTextDerivedIndexInspection;
252
276
  try {
253
277
  inspection = this.#lifecycle.inspect();
@@ -258,12 +282,13 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
258
282
  if (inspection.state !== 'checkpointed') {
259
283
  if (inspection.state === 'incompatible')
260
284
  logWarning(`Full-text derived index '${this.id}' is incompatible (${inspection.code}); rebuilding`);
261
- return;
285
+ return {};
262
286
  }
263
287
  try {
264
- return decodeFullTextCursorPayload(inspection.committedPayload);
288
+ return decodeFullTextPublication(inspection.committedPayload);
265
289
  } catch {
266
290
  logWarning(`Full-text derived index '${this.id}' has an invalid committed cursor; rebuilding`);
291
+ return {};
267
292
  }
268
293
  }
269
294
 
@@ -323,6 +348,34 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
323
348
  this.#queueBarrier(this.#activeEpoch);
324
349
  }
325
350
 
351
+ publishCoverage(positions: DerivedIndexPositions, ownerEpoch: bigint): void {
352
+ this.#assertSharedEpoch(ownerEpoch);
353
+ if (!this.#durableCursor) throw new FullTextDerivedIndexError('Cannot publish coverage without a durable cursor');
354
+ if (sameDerivedIndexPositions(this.#durableCursor.coverage, positions)) return;
355
+ let pending: BarrierCommand | undefined;
356
+ for (let index = this.#commands.length - 1; index >= 0; index--) {
357
+ if (this.#commands[index].type === 'barrier') {
358
+ pending = this.#commands[index] as BarrierCommand;
359
+ break;
360
+ }
361
+ }
362
+ const horizon = this.#lastAcceptedSequence;
363
+ const cursor = {
364
+ ...cloneCursor(this.#lastAcceptedCursor ?? this.#durableCursor)!,
365
+ coverage: clonePositions(positions),
366
+ };
367
+ if (pending?.epoch === ownerEpoch && pending.horizon === horizon) pending.cursor = cursor;
368
+ else
369
+ this.#commands.push({
370
+ type: 'barrier',
371
+ epoch: ownerEpoch,
372
+ horizon,
373
+ cursor,
374
+ coverageOnly: true,
375
+ });
376
+ this.#scheduleDrain();
377
+ }
378
+
326
379
  shutdown(ownerEpoch: bigint): Promise<void> {
327
380
  if (this.#shutdown?.epoch === ownerEpoch) {
328
381
  this.#cursorInspectedForAcquisition = false;
@@ -368,9 +421,15 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
368
421
  this.#assertAttached();
369
422
  if (this.#resetOperation) {
370
423
  this.#assertSharedEpoch(ownerEpoch);
371
- if (this.#resetEpoch !== ownerEpoch)
372
- throw new FullTextDerivedIndexError('Full-text reset belongs to another owner epoch');
373
- return this.#boundedReset(this.#resetOperation);
424
+ if (this.#resetEpoch === ownerEpoch) return this.#boundedReset(this.#resetOperation);
425
+ const priorReset = this.#resetOperation;
426
+ try {
427
+ await this.#boundedReset(priorReset);
428
+ } catch (error) {
429
+ if (this.#resetOperation === priorReset) throw error;
430
+ }
431
+ this.#assertSharedEpoch(ownerEpoch);
432
+ return this.reset(ownerEpoch);
374
433
  }
375
434
  if (
376
435
  this.#engine ||
@@ -406,7 +465,7 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
406
465
 
407
466
  async #performReset(ownerEpoch: bigint): Promise<void> {
408
467
  try {
409
- await this.#lifecycle.reset();
468
+ await this.#lifecycle.reset(ownerEpoch);
410
469
  } catch (error) {
411
470
  logNativeError('Full-text native reset failed', error);
412
471
  throw new FullTextDerivedIndexError('Full-text native reset failed');
@@ -509,18 +568,18 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
509
568
  if (epoch === undefined) throw new FullTextDerivedIndexError('Full-text owner epoch is unavailable');
510
569
  const engine = await this.#open(epoch);
511
570
  if (!engine) return false;
512
- let actual: DerivedIndexCursor | undefined;
571
+ let actual: FullTextPublication;
513
572
  try {
514
573
  this.#assertCommandEpoch(epoch);
515
- actual = decodeFullTextCursorPayload(engine.committedPayload);
574
+ actual = decodeFullTextPublication(engine.committedPayload);
516
575
  } catch (error) {
517
576
  await this.#closeUninstalledEngine(engine);
518
577
  throw error;
519
578
  }
520
- if (!sameCursor(actual, this.#durableCursor)) {
579
+ if (!sameCursor(actual.cursor, this.#durableCursor)) {
521
580
  this.#lossPendingEpoch = epoch;
522
581
  await this.#closeUninstalledEngine(engine);
523
- this.#durableCursor = actual;
582
+ this.#durableCursor = actual.cursor;
524
583
  this.#cursorInspectedForAcquisition = true;
525
584
  this.#resetWriterRetryBackoff();
526
585
  this.#discardCommands();
@@ -551,13 +610,34 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
551
610
  this.#lastAppliedSequence = command.sequence;
552
611
  return true;
553
612
  }
554
- const slice = toFullTextMutationSlice(
555
- command.batch.records,
556
- command.position,
557
- this.#maxApplySliceRecords,
558
- performance.now() + APPLY_SLICE_MILLISECONDS
559
- );
560
- const { batch: logical, end } = slice;
613
+ let slice: Awaited<ReturnType<typeof toFullTextMutationSlice>>;
614
+ try {
615
+ const startedAt = performance.now();
616
+ const deadline = startedAt + APPLY_SLICE_MILLISECONDS;
617
+ slice = this.#hasBlobSources
618
+ ? await toFullTextMutationSlice(
619
+ command.batch.records,
620
+ command.position,
621
+ this.#maxApplySliceRecords,
622
+ deadline,
623
+ startedAt + this.#maxBlobApplySliceMilliseconds,
624
+ this.#blobReadTimeoutMilliseconds
625
+ )
626
+ : {
627
+ ...toFullTextMutationSliceSync(
628
+ command.batch.records,
629
+ command.position,
630
+ this.#maxApplySliceRecords,
631
+ deadline
632
+ ),
633
+ rejected: 0,
634
+ };
635
+ } catch (error) {
636
+ await this.#loseAcceptedWork(command.epoch, error, { retry: true });
637
+ return false;
638
+ }
639
+ const { batch: logical, end, rejected: projectionRejected } = slice;
640
+ this.#stagedUnindexableRecords += projectionRejected;
561
641
  const mutationCount = logical.upserts.length + logical.deletes.length;
562
642
  if (mutationCount > 0) {
563
643
  this.#hasStagedMutations = true;
@@ -602,7 +682,7 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
602
682
  }
603
683
 
604
684
  async #publish(command: BarrierCommand): Promise<boolean> {
605
- if (command.horizon <= this.#lastPublishedSequence) return true;
685
+ if (!command.coverageOnly && command.horizon <= this.#lastPublishedSequence) return true;
606
686
  this.#assertCommandEpoch(command.epoch);
607
687
  if (this.#lastAppliedSequence < command.horizon)
608
688
  throw new FullTextDerivedIndexError('Full-text publication barrier passed unapplied work');
@@ -620,14 +700,15 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
620
700
  return false;
621
701
  }
622
702
  this.#assertCommandEpoch(command.epoch);
623
- this.#lastPublishedSequence = command.horizon;
703
+ if (command.coverageOnly) this.#host!.publicationChanged();
704
+ this.#lastPublishedSequence = Math.max(this.#lastPublishedSequence, command.horizon);
624
705
  this.#hasStagedMutations = false;
625
706
  const rejected = this.#stagedUnindexableRecords;
626
707
  this.#unindexableRecords += rejected;
627
708
  this.#stagedUnindexableRecords = 0;
628
709
  if (rejected > 0)
629
710
  logWarning(
630
- `Full-text derived index '${this.id}' removed ${rejected} records rejected by native record or frame limits`
711
+ `Full-text derived index '${this.id}' removed ${rejected} records with invalid projections or native record limits`
631
712
  );
632
713
  this.#durableCursor = cloneCursor(cursor);
633
714
  this.#cursorInspectedForAcquisition = true;
@@ -895,10 +976,53 @@ export class FullTextDerivedIndexBackend implements DerivedIndexBackend {
895
976
  }
896
977
 
897
978
  export function toFullTextMutationBatch(batch: DerivedIndexBatch): FullTextMutationBatch {
898
- return toFullTextMutationSlice(batch.records, 0, batch.records.length, Number.POSITIVE_INFINITY).batch;
979
+ return toFullTextMutationSliceSync(batch.records, 0, batch.records.length, Number.POSITIVE_INFINITY).batch;
980
+ }
981
+
982
+ async function toFullTextMutationSlice(
983
+ records: DerivedIndexBatch['records'],
984
+ start: number,
985
+ maxRecords: number,
986
+ deadline: number,
987
+ wallDeadline: number,
988
+ readTimeoutMilliseconds: number
989
+ ): Promise<{ batch: FullTextMutationBatch; end: number; rejected: number }> {
990
+ const upserts: FullTextMutationBatch['upserts'] = [];
991
+ const deletes: string[] = [];
992
+ let rejected = 0;
993
+ const limit = Math.min(start + maxRecords, records.length);
994
+ let index = start;
995
+ let previousTableId: number | undefined;
996
+ let tablePrefix = '';
997
+ let yieldDeadline = deadline;
998
+ for (; index < limit; index++) {
999
+ const record = records[index];
1000
+ if (record.recordId == null || typeof record.recordId === 'symbol') continue;
1001
+ if (record.tableId !== previousTableId) {
1002
+ previousTableId = record.tableId;
1003
+ tablePrefix = `${record.tableId}.`;
1004
+ }
1005
+ const id = tablePrefix + toBufferKey(record.recordId).toString('base64url');
1006
+ const recordState = record.state.kind === 'record' ? record.state : undefined;
1007
+ const resolved = recordState
1008
+ ? await resolvedFullTextFields(recordState.projection, readTimeoutMilliseconds)
1009
+ : undefined;
1010
+ yieldDeadline += resolved?.waitMilliseconds ?? 0;
1011
+ if (resolved?.error) throw resolved.error;
1012
+ if (resolved?.rejected) rejected++;
1013
+ if (resolved?.fields && !resolved.rejected)
1014
+ upserts.push({ id, version: String(recordState!.version), fields: resolved.fields });
1015
+ else deletes.push(id);
1016
+ const now = performance.now();
1017
+ if (now >= yieldDeadline || now >= wallDeadline) {
1018
+ index++;
1019
+ break;
1020
+ }
1021
+ }
1022
+ return { batch: { upserts, deletes }, end: index, rejected };
899
1023
  }
900
1024
 
901
- function toFullTextMutationSlice(
1025
+ function toFullTextMutationSliceSync(
902
1026
  records: DerivedIndexBatch['records'],
903
1027
  start: number,
904
1028
  maxRecords: number,
@@ -918,8 +1042,9 @@ function toFullTextMutationSlice(
918
1042
  tablePrefix = `${record.tableId}.`;
919
1043
  }
920
1044
  const id = tablePrefix + toBufferKey(record.recordId).toString('base64url');
921
- const fields = record.state.kind === 'record' ? fullTextFields(record.state.projection) : undefined;
922
- if (fields) upserts.push({ id, fields });
1045
+ const recordState = record.state.kind === 'record' ? record.state : undefined;
1046
+ const fields = recordState ? fullTextFields(recordState.projection) : undefined;
1047
+ if (fields) upserts.push({ id, version: String(recordState!.version), fields });
923
1048
  else deletes.push(id);
924
1049
  if ((index - start + 1) % 16 === 0 && performance.now() >= deadline) {
925
1050
  index++;
@@ -1001,7 +1126,14 @@ export function decodeFullTextCursorPayload(
1001
1126
  payload: string | null | undefined,
1002
1127
  maxBytes = HARPER_FULLTEXT_MAX_CURSOR_PAYLOAD_BYTES
1003
1128
  ): DerivedIndexCursor | undefined {
1004
- if (payload == null) return;
1129
+ return decodeFullTextPublication(payload, maxBytes).cursor;
1130
+ }
1131
+
1132
+ export function decodeFullTextPublication(
1133
+ payload: string | null | undefined,
1134
+ maxBytes = HARPER_FULLTEXT_MAX_CURSOR_PAYLOAD_BYTES
1135
+ ): FullTextPublication {
1136
+ if (payload == null) return {};
1005
1137
  if (typeof payload !== 'string' || Buffer.byteLength(payload) > maxBytes)
1006
1138
  throw new FullTextDerivedIndexError('Full-text cursor payload is invalid or too large');
1007
1139
  let decoded: unknown;
@@ -1014,10 +1146,10 @@ export function decodeFullTextCursorPayload(
1014
1146
  !plainObject(decoded) ||
1015
1147
  decoded.format !== 1 ||
1016
1148
  !Object.hasOwn(decoded, 'cursor') ||
1017
- Object.keys(decoded).some((name) => name !== 'format' && name !== 'cursor')
1149
+ Object.keys(decoded).some((name) => name !== 'format' && name !== 'cursor' && name !== 'dataRevision')
1018
1150
  )
1019
1151
  throw new FullTextDerivedIndexError('Full-text cursor payload has an unsupported format');
1020
- if (decoded.cursor === null) return;
1152
+ if (decoded.cursor === null) return {};
1021
1153
  if (
1022
1154
  !plainObject(decoded.cursor) ||
1023
1155
  decoded.cursor.format !== 1 ||
@@ -1025,7 +1157,7 @@ export function decodeFullTextCursorPayload(
1025
1157
  Object.keys(decoded.cursor).some((name) => name !== 'format' && name !== 'logs' && name !== 'coverage')
1026
1158
  )
1027
1159
  throw new FullTextDerivedIndexError('Full-text cursor payload contains an invalid cursor');
1028
- return normalizedCursor(decoded.cursor as DerivedIndexCursor);
1160
+ return { cursor: normalizedCursor(decoded.cursor as DerivedIndexCursor) };
1029
1161
  }
1030
1162
 
1031
1163
  function fullTextFields(projection: unknown): Record<string, string | string[]> | undefined {
@@ -1040,6 +1172,58 @@ function fullTextFields(projection: unknown): Record<string, string | string[]>
1040
1172
  return fields;
1041
1173
  }
1042
1174
 
1175
+ async function resolvedFullTextFields(
1176
+ projection: unknown,
1177
+ readTimeoutMilliseconds: number
1178
+ ): Promise<{
1179
+ fields?: Record<string, string | string[]>;
1180
+ rejected: boolean;
1181
+ error?: unknown;
1182
+ waitMilliseconds: number;
1183
+ }> {
1184
+ let waitMilliseconds = 0;
1185
+ let fields = fullTextFields(projection);
1186
+ if (!projection || typeof projection !== 'object' || Array.isArray(projection))
1187
+ return { fields, rejected: false, waitMilliseconds };
1188
+ const deadline = performance.now() + readTimeoutMilliseconds;
1189
+ for (const name in projection) {
1190
+ if (!Object.hasOwn(projection, name)) continue;
1191
+ const value = (projection as Record<string, unknown>)[name];
1192
+ if (!(value instanceof Blob)) continue;
1193
+ if (value.size > MAX_FULL_TEXT_VALUE_BYTES) return { rejected: true, waitMilliseconds };
1194
+ let bytes: ArrayBuffer;
1195
+ try {
1196
+ const remaining = deadline - performance.now();
1197
+ if (remaining <= 0) throw new FullTextBlobReadTimeout();
1198
+ const reading = withTimeout(value.arrayBuffer(), remaining, () => new FullTextBlobReadTimeout());
1199
+ const waitStartedAt = performance.now();
1200
+ try {
1201
+ bytes = await reading;
1202
+ } finally {
1203
+ waitMilliseconds += performance.now() - waitStartedAt;
1204
+ }
1205
+ } catch (error) {
1206
+ return { rejected: false, error, waitMilliseconds };
1207
+ }
1208
+ let text: string;
1209
+ try {
1210
+ text = UTF8_DECODER.decode(bytes);
1211
+ } catch (error) {
1212
+ if (error instanceof TypeError) return { rejected: true, waitMilliseconds };
1213
+ throw error;
1214
+ }
1215
+ if (Buffer.byteLength(text) > MAX_FULL_TEXT_VALUE_BYTES) return { rejected: true, waitMilliseconds };
1216
+ (fields ??= Object.create(null))[name] = text;
1217
+ }
1218
+ return { fields, rejected: false, waitMilliseconds };
1219
+ }
1220
+
1221
+ class FullTextBlobReadTimeout extends Error {
1222
+ constructor() {
1223
+ super('Full-text Blob read timed out');
1224
+ }
1225
+ }
1226
+
1043
1227
  function isTerminalOpenError(error: unknown): boolean {
1044
1228
  const code = nativeErrorCode(error);
1045
1229
  return code !== undefined && TERMINAL_OPEN_ERROR_CODES.has(code);
@@ -2,15 +2,17 @@ import type { FullTextDerivedIndexEngine } from './fullTextDerivedIndex.ts';
2
2
  import { loggerWithTag } from '../../utility/logging/logger.ts';
3
3
 
4
4
  const FULLTEXT_LIFECYCLE_API_VERSION = 1;
5
- const FULLTEXT_MUTATION_BATCH_API_VERSION = 3;
5
+ const FULLTEXT_MUTATION_BATCH_API_VERSION = 4;
6
+ const FULLTEXT_QUERY_API_VERSION = 2;
6
7
  const logger = loggerWithTag('fulltext-derived-index');
7
8
 
8
9
  export interface NativeFullTextIndexConfiguration {
9
10
  fields: Array<{ name: string; weight?: number }>;
10
- analyzer: 'english@1';
11
+ analyzer: 'english@2';
11
12
  stopWords?: boolean;
12
13
  positions?: boolean;
13
14
  surfaceTerms?: boolean;
15
+ synonyms?: NativeFullTextSynonymRule[];
14
16
  limits: {
15
17
  indexingThreads: number;
16
18
  searchThreads: number;
@@ -21,14 +23,97 @@ export interface NativeFullTextIndexConfiguration {
21
23
  };
22
24
  }
23
25
 
26
+ export type NativeFullTextSynonymRule = { source: string; replacements: string[] };
27
+
28
+ export type NativeFullTextSearchMode = 'any' | 'all' | 'phrase' | 'prefix' | 'fuzzy' | 'fuzzy-prefix';
29
+
30
+ export type NativeFullTextSearchExpression =
31
+ | { text: string; mode?: NativeFullTextSearchMode; operator?: 'any' | 'all'; fields?: string[] }
32
+ | { operator: 'and' | 'or'; clauses: NativeFullTextSearchExpression[] }
33
+ | { operator: 'not'; clause: NativeFullTextSearchExpression };
34
+
35
+ export type NativeFullTextSearchRequest = {
36
+ text?: string;
37
+ query?: NativeFullTextSearchExpression;
38
+ mode?: NativeFullTextSearchMode;
39
+ operator?: 'any' | 'all';
40
+ fields?: string[];
41
+ candidateIds?: string[];
42
+ offset?: number;
43
+ limit?: number;
44
+ exactTotal?: boolean;
45
+ };
46
+
47
+ export type NativeFullTextSearchResult = {
48
+ total: number;
49
+ totalRelation: 'exact' | 'lower-bound';
50
+ hits: Array<{ id: string; score: number; version?: string }>;
51
+ };
52
+
53
+ export type NativeFullTextTraceResult = {
54
+ complete: boolean;
55
+ records: Array<{
56
+ id: string;
57
+ values: Array<{
58
+ field: string;
59
+ valueIndex: number;
60
+ spans: Array<{ start: number; end: number }>;
61
+ fragments?: Array<{ text: string; start: number; spans: Array<{ start: number; end: number }> }>;
62
+ }>;
63
+ }>;
64
+ };
65
+
66
+ export interface NativeFullTextReader {
67
+ readonly committedPayload?: string;
68
+ reload(): Promise<void>;
69
+ search(
70
+ request: NativeFullTextSearchRequest,
71
+ options?: { remainingBudgetMilliseconds?: number }
72
+ ): Promise<NativeFullTextSearchResult>;
73
+ traceMatches(
74
+ request: Omit<NativeFullTextSearchRequest, 'query' | 'offset' | 'limit' | 'exactTotal'> & { text: string },
75
+ records: Array<{ id: string; fields: Record<string, string | string[]> }>,
76
+ options?: {
77
+ remainingBudgetMilliseconds?: number;
78
+ snippets?: boolean;
79
+ fragmentLength?: number;
80
+ maxFragmentsPerValue?: number;
81
+ }
82
+ ): Promise<NativeFullTextTraceResult>;
83
+ close(): Promise<{ cleanupError?: unknown }>;
84
+ }
85
+
24
86
  export type NativeFullTextRuntimeInfo = {
25
87
  packageVersion: string;
26
88
  tantivyVersion: string;
27
89
  nativeAbiVersion: number;
90
+ queryClassIsolationMinimumSearchThreads: number;
28
91
  lifecycleApiVersion: number;
29
92
  mutationBatchApiVersion: number;
93
+ queryApiVersion: number;
30
94
  storageBackends: ReadonlyArray<'native'>;
31
- limits: { maxCommitPayloadBytes: number };
95
+ limits: {
96
+ maxCommitPayloadBytes: number;
97
+ maxSearchWindow: number;
98
+ maxAutocompleteResults: number;
99
+ maxSearchBudgetMilliseconds: number;
100
+ maxTraceRecords: number;
101
+ maxTraceSourceBytes: number;
102
+ };
103
+ };
104
+
105
+ export type NativeFullTextQueryRuntimeInfo = Pick<
106
+ NativeFullTextRuntimeInfo,
107
+ 'queryClassIsolationMinimumSearchThreads'
108
+ > & {
109
+ limits: Pick<
110
+ NativeFullTextRuntimeInfo['limits'],
111
+ | 'maxSearchWindow'
112
+ | 'maxAutocompleteResults'
113
+ | 'maxSearchBudgetMilliseconds'
114
+ | 'maxTraceRecords'
115
+ | 'maxTraceSourceBytes'
116
+ >;
32
117
  };
33
118
 
34
119
  export interface NativeFullTextModule {
@@ -50,6 +135,13 @@ export interface NativeFullTextModule {
50
135
  generation: string;
51
136
  }
52
137
  ): Promise<FullTextDerivedIndexEngine>;
138
+ openNativeFullTextReader(
139
+ options: NativeFullTextIndexConfiguration & {
140
+ path: string;
141
+ indexId: string;
142
+ generation: string;
143
+ }
144
+ ): Promise<NativeFullTextReader>;
53
145
  inspectNativeFullTextIndex(
54
146
  options: Omit<NativeFullTextIndexConfiguration, 'limits'> & {
55
147
  path: string;
@@ -109,6 +201,8 @@ export async function validateFullTextNativeBinding(module: unknown): Promise<Na
109
201
  typeof prototype.close !== 'function' ||
110
202
  !('openNativeFullTextIndex' in module) ||
111
203
  typeof module.openNativeFullTextIndex !== 'function' ||
204
+ !('openNativeFullTextReader' in module) ||
205
+ typeof module.openNativeFullTextReader !== 'function' ||
112
206
  !('validateNativeFullTextIndexOptions' in module) ||
113
207
  typeof module.validateNativeFullTextIndexOptions !== 'function' ||
114
208
  !('inspectNativeFullTextIndex' in module) ||
@@ -121,6 +215,7 @@ export async function validateFullTextNativeBinding(module: unknown): Promise<Na
121
215
  throw new TypeError('@harperfast/fulltext/native does not implement the required Harper binding contract');
122
216
  const binding = module as NativeFullTextModule;
123
217
  const info = await binding.runtimeInfo();
218
+ validateFullTextQueryRuntimeInfo(info);
124
219
  if (
125
220
  !info ||
126
221
  typeof info.packageVersion !== 'string' ||
@@ -128,6 +223,7 @@ export async function validateFullTextNativeBinding(module: unknown): Promise<Na
128
223
  !Number.isSafeInteger(info.nativeAbiVersion) ||
129
224
  info.lifecycleApiVersion !== FULLTEXT_LIFECYCLE_API_VERSION ||
130
225
  info.mutationBatchApiVersion !== FULLTEXT_MUTATION_BATCH_API_VERSION ||
226
+ info.queryApiVersion !== FULLTEXT_QUERY_API_VERSION ||
131
227
  !Array.isArray(info.storageBackends) ||
132
228
  !info.storageBackends.includes('native') ||
133
229
  !info.limits ||
@@ -144,3 +240,37 @@ export function getValidatedFullTextRuntimeInfo(binding: NativeFullTextModule):
144
240
  if (!info) throw new Error('Full-text native binding has not been validated');
145
241
  return info;
146
242
  }
243
+
244
+ export function nativeFullTextSearchThreads(
245
+ info: Pick<NativeFullTextRuntimeInfo, 'queryClassIsolationMinimumSearchThreads'>,
246
+ configured: number
247
+ ): number {
248
+ const minimum = info.queryClassIsolationMinimumSearchThreads;
249
+ if (!Number.isSafeInteger(minimum) || minimum <= 0)
250
+ throw new TypeError('@harperfast/fulltext/native reported an invalid query-class isolation threshold');
251
+ return Math.max(configured, minimum);
252
+ }
253
+
254
+ export function validateFullTextQueryRuntimeInfo(info: unknown): NativeFullTextQueryRuntimeInfo {
255
+ const runtime = info as NativeFullTextQueryRuntimeInfo;
256
+ if (
257
+ !runtime ||
258
+ typeof runtime !== 'object' ||
259
+ !Number.isSafeInteger(runtime.queryClassIsolationMinimumSearchThreads) ||
260
+ runtime.queryClassIsolationMinimumSearchThreads <= 0 ||
261
+ !runtime.limits ||
262
+ !Number.isSafeInteger(runtime.limits.maxSearchWindow) ||
263
+ runtime.limits.maxSearchWindow <= 0 ||
264
+ !Number.isSafeInteger(runtime.limits.maxAutocompleteResults) ||
265
+ runtime.limits.maxAutocompleteResults <= 0 ||
266
+ runtime.limits.maxAutocompleteResults > runtime.limits.maxSearchWindow ||
267
+ !Number.isSafeInteger(runtime.limits.maxSearchBudgetMilliseconds) ||
268
+ runtime.limits.maxSearchBudgetMilliseconds <= 0 ||
269
+ !Number.isSafeInteger(runtime.limits.maxTraceRecords) ||
270
+ runtime.limits.maxTraceRecords <= 0 ||
271
+ !Number.isSafeInteger(runtime.limits.maxTraceSourceBytes) ||
272
+ runtime.limits.maxTraceSourceBytes <= 0
273
+ )
274
+ throw new TypeError('@harperfast/fulltext/native reported incompatible runtime capabilities');
275
+ return runtime;
276
+ }