@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
@@ -355,7 +355,7 @@ function storeTableSizeMetrics(analyticsTable: Table, dbName: string, tables: Ta
355
355
  return dbUsedSize;
356
356
  }
357
357
 
358
- function storeDBSizeMetrics(analyticsTable: Table, databases: Databases) {
358
+ export function storeDBSizeMetrics(analyticsTable: Table, databases: Databases) {
359
359
  for (const [db, tables] of Object.entries(databases)) {
360
360
  try {
361
361
  const [firstTable] = Object.values(tables);
@@ -400,7 +400,7 @@ function storeDBSizeMetrics(analyticsTable: Table, databases: Databases) {
400
400
  }
401
401
  }
402
402
 
403
- async function storeVolumeMetrics(analyticsTable: Table, databases: Databases) {
403
+ export async function storeVolumeMetrics(analyticsTable: Table, databases: Databases) {
404
404
  await Promise.all(
405
405
  Object.entries(databases).map(async ([db, tables]) => {
406
406
  try {
@@ -669,7 +669,7 @@ export function buildRocksDBTxnLogMetric(
669
669
  * @param now - The current time.
670
670
  * @param period - The period to store the metrics for.
671
671
  */
672
- function storeRocksDBStatsMetrics(
672
+ export function storeRocksDBStatsMetrics(
673
673
  analyticsTable: Table,
674
674
  databases: Databases,
675
675
  now: number,
package/resources/blob.ts CHANGED
@@ -1578,6 +1578,8 @@ function runReclamation(): void {
1578
1578
  let earliest = Infinity;
1579
1579
  for (const [filePath, pending] of pendingReclamation) {
1580
1580
  if (pending.unlinking) continue;
1581
+ // Unlinking during a restore would delete bytes it just wrote at this path.
1582
+ if (isBlobRestoreFenced(pending.fileInfo?.store)) continue;
1581
1583
  if (pending.deadline > now) {
1582
1584
  earliest = pending.deadline;
1583
1585
  break; // insertion order is deadline order; nothing behind this entry is due
@@ -1648,7 +1650,9 @@ function runReclamation(): void {
1648
1650
  const instanceStorageInfo = storageInfoForBlob.get(blob);
1649
1651
  if (instanceStorageInfo) discardStorage(instanceStorageInfo);
1650
1652
  }
1653
+ const unlinkDone = beginBlobUnlink(blobSaveDatabaseName(storageInfo?.store));
1651
1654
  unlink(filePath, (error) => {
1655
+ unlinkDone();
1652
1656
  pendingReclamation.delete(filePath);
1653
1657
  if (pendingReclamation.size === 0) queueTailDeadline = 0;
1654
1658
  // Hand the slot back: it is shared by hash, so leaving it claimed would make every later
@@ -1808,6 +1812,150 @@ export function createBlobFromStoredBody(
1808
1812
  // can be committed referencing fileIds whose blob files were never durably written.
1809
1813
  let pendingMigrationBlobSaves: Promise<void>[] | undefined;
1810
1814
 
1815
+ // Database -> the token of the restore that currently owns its fence. Restores of one database can
1816
+ // overlap, because a restore releases its lock before awaiting its `reload` broadcast. Ownership, so
1817
+ // that a superseded restore's late reload cannot unfence a database another restore is still working
1818
+ // on, and a restore that dies without reloading leaves nothing behind that a rerun cannot take over.
1819
+ const blobRestoreFences = new Map<string, string>();
1820
+ // Bumped every time a restore takes a database's fence. A check for "is it fenced right now" is not
1821
+ // enough for work that spans the whole restore: an orphan sweep can read the fence as clear, have an
1822
+ // entire restore happen while it awaits an unlink, and then keep deleting on a verdict reached
1823
+ // against the generation that restore replaced. Long-running work captures this instead.
1824
+ const blobRestoreEpochs = new Map<string, number>();
1825
+ const inFlightBlobSaves = new Map<string, Set<Promise<void>>>();
1826
+ // Count of blob-root unlinks dispatched per database, with the waiters a fence parks on until that
1827
+ // count reaches zero. A counter rather than a set of promises: this sits on the deletion path for
1828
+ // every blob, and the overwhelmingly common case has no restore running and nothing to await.
1829
+ const inFlightBlobUnlinks = new Map<string, { count: number; waiters: Array<() => void> }>();
1830
+
1831
+ function blobSaveDatabaseName(store: any): string | undefined {
1832
+ return typeof store?.databaseName === 'string' ? store.databaseName : undefined;
1833
+ }
1834
+
1835
+ function assertBlobSaveAllowed(store: any): void {
1836
+ const databaseName = blobSaveDatabaseName(store);
1837
+ if (databaseName && blobRestoreFences.has(databaseName)) {
1838
+ // 503, not a bare Error: the block is lifted by the restore's `reload` phase, so this is a
1839
+ // retry-after condition rather than a fault in the request.
1840
+ throw new BlobReadError(
1841
+ `Cannot save a blob for database '${databaseName}' while it is being restored`,
1842
+ BLOB_UNAVAILABLE_STATUS
1843
+ );
1844
+ }
1845
+ }
1846
+
1847
+ function trackBlobSave(store: any, saving?: Promise<void>): void {
1848
+ const databaseName = blobSaveDatabaseName(store);
1849
+ if (!databaseName || !saving) return;
1850
+ let saves = inFlightBlobSaves.get(databaseName);
1851
+ if (!saves) inFlightBlobSaves.set(databaseName, (saves = new Set()));
1852
+ saves.add(saving);
1853
+ const remove = () => {
1854
+ saves.delete(saving);
1855
+ if (saves.size === 0) inFlightBlobSaves.delete(databaseName);
1856
+ };
1857
+ saving.then(remove, remove);
1858
+ }
1859
+
1860
+ /**
1861
+ * Fence blob saves, deferred reclamation and orphan cleanup for a database, and wait out the work
1862
+ * already dispatched, so a restore can purge and rewrite its roots safely.
1863
+ *
1864
+ * Closing the database does not do this: a save is an asynchronous file pipeline that outlives the
1865
+ * handle it started from, a reclamation is a timer that never consults the database, and an orphan
1866
+ * sweep is dispatched fire-and-forget.
1867
+ */
1868
+ export async function blockBlobSavesForRestore(databaseName: string, restoreToken: string): Promise<void> {
1869
+ // Takes ownership whether or not another restore held it: the database never unfences in between.
1870
+ blobRestoreFences.set(databaseName, restoreToken);
1871
+ blobRestoreEpochs.set(databaseName, (blobRestoreEpochs.get(databaseName) ?? 0) + 1);
1872
+ const saves = inFlightBlobSaves.get(databaseName);
1873
+ if (saves) await Promise.allSettled([...saves]);
1874
+ await drainBlobUnlinks(databaseName);
1875
+ }
1876
+
1877
+ /**
1878
+ * Release one restore's fence. Only the current owner's token lifts it, so an earlier restore's late
1879
+ * `reload` cannot unfence a database another restore has since taken over.
1880
+ */
1881
+ export function resumeBlobSavesAfterRestore(
1882
+ databaseName: string,
1883
+ restoreToken: string,
1884
+ generationReplaced = true
1885
+ ): void {
1886
+ // Only the owner may lift the fence; a superseded restore's reload arriving late does nothing.
1887
+ if (blobRestoreFences.get(databaseName) !== restoreToken) return;
1888
+ blobRestoreFences.delete(databaseName);
1889
+ // A drain that ran while the fence was up skipped these entries without recording a next deadline,
1890
+ // so nothing is scheduled to come back for them. Whatever survives below needs a wakeup.
1891
+ if (pendingReclamation.size > 0) scheduleReclamation(Date.now());
1892
+ // A restore that aborted before it destroyed anything leaves the generation intact, so its queued
1893
+ // reclamations still condemn the files they were queued against. Discarding those would strand
1894
+ // every superseded blob the database had pending at the moment an admission check happened to fail.
1895
+ if (!generationReplaced) return;
1896
+ // Every queued reclamation for this database belongs to the generation the restore just replaced:
1897
+ // its file paths were condemned against records that no longer exist. Draining them now would
1898
+ // unlink restored bytes at the same paths, so they are dropped rather than resumed.
1899
+ discardReclamationForDatabase(databaseName);
1900
+ }
1901
+
1902
+ function isBlobRestoreFenced(store: any): boolean {
1903
+ const databaseName = blobSaveDatabaseName(store);
1904
+ return !!databaseName && blobRestoreFences.has(databaseName);
1905
+ }
1906
+
1907
+ /**
1908
+ * A token for work that outlives a single check -- an orphan sweep, say -- so it can tell whether any
1909
+ * restore has touched this database since it started, not merely whether one is running right now.
1910
+ */
1911
+ export function blobRestoreGeneration(store: any): { database?: string; epoch: number; fenced: boolean } {
1912
+ const database = blobSaveDatabaseName(store);
1913
+ return {
1914
+ database,
1915
+ epoch: database ? (blobRestoreEpochs.get(database) ?? 0) : 0,
1916
+ fenced: !!database && blobRestoreFences.has(database),
1917
+ };
1918
+ }
1919
+
1920
+ /** True once a restore has fenced this database since `since` was taken, or is fencing it now. */
1921
+ export function blobRestoreGenerationChanged(since: { database?: string; epoch: number }): boolean {
1922
+ if (!since.database) return false;
1923
+ if (blobRestoreFences.has(since.database)) return true;
1924
+ return (blobRestoreEpochs.get(since.database) ?? 0) !== since.epoch;
1925
+ }
1926
+
1927
+ function discardReclamationForDatabase(databaseName: string): void {
1928
+ for (const [filePath, pending] of [...pendingReclamation]) {
1929
+ if (pending.unlinking) continue; // already dispatched; its callback owns the entry
1930
+ if (blobSaveDatabaseName(pending.fileInfo?.store) !== databaseName) continue;
1931
+ pendingReclamation.delete(filePath);
1932
+ settleReclamationWaiters(pending);
1933
+ }
1934
+ if (pendingReclamation.size === 0) resetDrainedQueue();
1935
+ }
1936
+
1937
+ /** Register a dispatched blob-root unlink so a restore fence can wait for it to land. */
1938
+ export function beginBlobUnlink(databaseName: string | undefined): () => void {
1939
+ if (!databaseName) return () => {};
1940
+ let unlinks = inFlightBlobUnlinks.get(databaseName);
1941
+ if (!unlinks) inFlightBlobUnlinks.set(databaseName, (unlinks = { count: 0, waiters: [] }));
1942
+ unlinks.count++;
1943
+ let settled = false;
1944
+ return () => {
1945
+ if (settled) return;
1946
+ settled = true;
1947
+ if (--unlinks.count > 0) return;
1948
+ inFlightBlobUnlinks.delete(databaseName);
1949
+ for (const wake of unlinks.waiters) wake();
1950
+ };
1951
+ }
1952
+
1953
+ function drainBlobUnlinks(databaseName: string): Promise<void> | undefined {
1954
+ const unlinks = inFlightBlobUnlinks.get(databaseName);
1955
+ if (!unlinks || unlinks.count === 0) return;
1956
+ return new Promise<void>((resolve) => unlinks.waiters.push(resolve));
1957
+ }
1958
+
1811
1959
  export function saveBlob(blob: FileBackedBlob, deleteOnFailure = false) {
1812
1960
  let storageInfo = storageInfoForBlob.get(blob);
1813
1961
  if (!storageInfo) {
@@ -1826,6 +1974,7 @@ export function saveBlob(blob: FileBackedBlob, deleteOnFailure = false) {
1826
1974
  if (storageInfo.fileId) return storageInfo; // if there is any file id, we are already saving and can return the info
1827
1975
  storageInfo.store = currentStore;
1828
1976
  }
1977
+ assertBlobSaveAllowed(storageInfo.store);
1829
1978
  storageInfo.deleteOnFailure = deleteOnFailure;
1830
1979
  if (blob.saveInRecord) {
1831
1980
  if (!storageInfo.contentBuffer) {
@@ -1848,6 +1997,7 @@ export function saveBlob(blob: FileBackedBlob, deleteOnFailure = false) {
1848
1997
  // for native blobs, we have to read them from the stream
1849
1998
  writeBlobWithStream(blob as any, Readable.from(blob.stream()), storageInfo);
1850
1999
  }
2000
+ trackBlobSave(storageInfo.store, storageInfo.saving);
1851
2001
  // Track the in-flight save when a migration is collecting them. storageInfo.saving is set
1852
2002
  // by writeBlobWithStream; writeBlobWithBuffer for small blobs may not produce one. The
1853
2003
  // wrapping `.then(...)` removes resolved promises from the list so a long migration does not
@@ -2081,7 +2231,9 @@ function writeBlobWithStream(
2081
2231
  }
2082
2232
  if (storageInfo.deleteOnFailure) {
2083
2233
  store.unlock(lockKey);
2234
+ const unlinkDone = beginBlobUnlink(blobSaveDatabaseName(store));
2084
2235
  unlink(filePath, (error) => {
2236
+ unlinkDone();
2085
2237
  if (error) logger.debug?.('Error while deleting aborted blob file', error);
2086
2238
  });
2087
2239
  } else if (idleTimeoutMs > 0 && !(error as { sourceBlobUnavailable?: boolean }).sourceBlobUnavailable) {
@@ -2343,6 +2495,7 @@ export function repairBlobFile(
2343
2495
  if (!storageInfo?.fileId || !storageInfo.store) return undefined;
2344
2496
  if (storageInfo.start !== undefined || storageInfo.end !== undefined) return undefined;
2345
2497
  store = storageInfo.store;
2498
+ assertBlobSaveAllowed(store);
2346
2499
  storageInfo.filePath ??= getFilePath(storageInfo);
2347
2500
  const filePath = storageInfo.filePath;
2348
2501
  const descriptorSize = (blob as { size?: number }).size;
@@ -2396,6 +2549,7 @@ export function repairBlobFile(
2396
2549
  repairTargetPath: filePath,
2397
2550
  repairTempLockKey,
2398
2551
  });
2552
+ trackBlobSave(store, repairStorageInfo.saving);
2399
2553
  locksOwnedByWriter = true;
2400
2554
  const settled = (repairStorageInfo.saving as Promise<void>).then(() => {
2401
2555
  if (descriptorSize === undefined) (blob as { size?: number }).size = verifiedSize;
@@ -3350,6 +3504,10 @@ export async function cleanupOrphans(database: any, databaseName?: string) {
3350
3504
  auditStore = table.auditStore;
3351
3505
  if (auditStore) break;
3352
3506
  }
3507
+ // Captured before the scan, not before the deletions: the verdict this sweep acts on -- "no record
3508
+ // references this path" -- is formed by the scan below, so a restore that lands while it runs has
3509
+ // already invalidated it.
3510
+ const generation = blobRestoreGeneration(store);
3353
3511
  const pathsToCheck = new Set<string>();
3354
3512
  const rootPaths = getRootBlobPathsForDB(store);
3355
3513
  if (rootPaths) {
@@ -3452,17 +3610,41 @@ export async function cleanupOrphans(database: any, databaseName?: string) {
3452
3610
  }
3453
3611
  logger.warn?.('Deleting', pathsToCheck.size, 'orphaned blobs');
3454
3612
  orphansDeleted += pathsToCheck.size;
3613
+ let deleted = 0;
3614
+ let skipped = 0;
3615
+ let abandonedForRestore = false;
3455
3616
  for (const path of pathsToCheck) {
3617
+ // This walk is dispatched fire-and-forget and can outlive a restore's close acknowledgement, so
3618
+ // it compares generations rather than asking whether a restore is running right now: one await
3619
+ // here can span a whole restore, leaving the fence clear again by the next iteration.
3620
+ if (!abandonedForRestore && blobRestoreGenerationChanged(generation)) {
3621
+ abandonedForRestore = true;
3622
+ logger.warn?.('Stopping orphaned blob cleanup: the database has been restored since this scan began');
3623
+ }
3624
+ if (abandonedForRestore) {
3625
+ // Keep walking, but only to hand back the locks taken for every candidate up front. Leaving
3626
+ // them held would block the repair path until the process restarts, and collecting the
3627
+ // untouched paths into a second set first would double the footprint of a sweep this
3628
+ // function is explicitly built to run over hours.
3629
+ skipped++;
3630
+ const lockKey = repairTempLocks.get(path);
3631
+ if (lockKey) (store as any).unlock(lockKey);
3632
+ continue;
3633
+ }
3634
+ const unlinkDone = beginBlobUnlink(blobSaveDatabaseName(store));
3456
3635
  try {
3457
3636
  await unlinkPromised(path);
3637
+ deleted++;
3458
3638
  } catch (error) {
3459
3639
  logger.debug?.('Error deleting file', error);
3460
3640
  } finally {
3641
+ unlinkDone();
3461
3642
  const lockKey = repairTempLocks.get(path);
3462
3643
  if (lockKey) (store as any).unlock(lockKey);
3463
3644
  }
3464
3645
  }
3465
- logger.warn?.('Finished deleting', pathsToCheck.size, 'orphaned blobs');
3646
+ orphansDeleted -= skipped;
3647
+ logger.warn?.('Finished deleting', deleted, 'orphaned blobs');
3466
3648
  pathsToCheck.clear();
3467
3649
  }
3468
3650
  // check each object for any blob references and removes from the paths to check if found
@@ -95,6 +95,7 @@ import { resolveRocksMemoryConfig } from '../utility/rocksMemoryConfig.ts';
95
95
  import { isProcessRunning } from '../utility/processManagement/processManagement.js';
96
96
  import {
97
97
  compileFullTextDefinitions,
98
+ compileFullTextFields,
98
99
  persistedFullTextIndexNames,
99
100
  reconcileFullTextIndexGenerations,
100
101
  type FullTextDefinition,
@@ -104,7 +105,9 @@ import { projectAttributesToProperties } from './jsonSchemaTypes.ts';
104
105
  import {
105
106
  definitionsEqual,
106
107
  mergePeerFullTextDefinitions,
108
+ mergePeerFullTextFields,
107
109
  readPersistedFullTextDefinitions,
110
+ readPersistedFullTextFields,
108
111
  retainFullTextDefinitions,
109
112
  serializeFullTextState,
110
113
  } from './fullTextSchemaLifecycle.ts';
@@ -1443,6 +1446,12 @@ function initStores(
1443
1446
  for (const warning of warnings) logger.warn(warning);
1444
1447
  } else if (warnings.length === 0) warnedFullTextStates.delete(warningKey);
1445
1448
  }
1449
+ const fullTextFields = readPersistedFullTextFields(
1450
+ primaryAttribute.fullTextFields,
1451
+ primaryAttribute.fullTextIndexes,
1452
+ attributes,
1453
+ (message) => logger.warn(`${databaseName}.${tableName}: ${message}`)
1454
+ );
1446
1455
  const fullTextIndexGenerations = persistedFullTextIndexGenerations(
1447
1456
  primaryAttribute.fullTextIndexGenerations,
1448
1457
  fullTextIndexes
@@ -1451,6 +1460,7 @@ function initStores(
1451
1460
  if (table && !recreateTable) {
1452
1461
  if (primaryAttribute.audit === true && table.audit !== true) table.enableAuditing();
1453
1462
  table.fullTextIndexes = fullTextIndexes;
1463
+ table.fullTextFields = fullTextFields;
1454
1464
  table.fullTextIndexGenerations = fullTextIndexGenerations;
1455
1465
  table.fullTextIndexRetirements = fullTextIndexRetirements;
1456
1466
  indices = table.indices;
@@ -1605,6 +1615,7 @@ function initStores(
1605
1615
  indices,
1606
1616
  attributes,
1607
1617
  fullTextIndexes,
1618
+ fullTextFields,
1608
1619
  fullTextIndexGenerations,
1609
1620
  fullTextIndexRetirements,
1610
1621
  schemaDefined: primaryAttribute.schemaDefined,
@@ -2165,6 +2176,7 @@ interface TableDefinition {
2165
2176
  /** Internal: this declaration came from the application owned by the current dedicated worker. */
2166
2177
  isolatedApplicationOwner?: boolean;
2167
2178
  fullTextIndexes?: FullTextDefinition[];
2179
+ fullTextFields?: string[];
2168
2180
  }
2169
2181
  /**
2170
2182
  * Ensure that we have this database object (that holds a set of tables) set up
@@ -3353,6 +3365,7 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3353
3365
  cacheControl,
3354
3366
  isolatedApplicationOwner,
3355
3367
  fullTextIndexes,
3368
+ fullTextFields,
3356
3369
  } = tableDefinition;
3357
3370
  const auditExplicitlyEnabled = audit === true;
3358
3371
  const auditExplicitlyDisabled = audit === false;
@@ -3555,6 +3568,7 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3555
3568
  let published = false;
3556
3569
  let fullTextValuesForPersistence: unknown;
3557
3570
  let fullTextPersistencePending = false;
3571
+ let fullTextFieldsForPersistence: string[] | undefined;
3558
3572
  let activeFullTextIndexes: FullTextDefinition[] | undefined;
3559
3573
  let fullTextIndexGenerationMap: FullTextIndexGenerations = Object.create(null);
3560
3574
  let fullTextIndexRetirementNames: string[] | undefined;
@@ -3604,23 +3618,43 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3604
3618
  );
3605
3619
  }
3606
3620
  let persistedPrimary = persistedPrimaryDescriptor(Table.dbisDB);
3607
- let persistedFullTextValues = persistedPrimary.descriptor?.fullTextIndexes;
3621
+ const persistedFullTextState = (name: string, liveValue: unknown) =>
3622
+ persistedPrimary.descriptor === undefined ? liveValue : persistedPrimary.descriptor[name];
3623
+ let persistedFullTextValues = persistedFullTextState('fullTextIndexes', Table.fullTextIndexes);
3624
+ if (origin === 'cluster') {
3625
+ const declaredFields = new Set(
3626
+ readPersistedFullTextFields(
3627
+ persistedFullTextState('fullTextFields', Table.fullTextFields),
3628
+ persistedFullTextValues,
3629
+ catalogAttributes(Table.dbisDB),
3630
+ fullTextWarning
3631
+ )
3632
+ );
3633
+ attributes = attributes.filter((attribute) => {
3634
+ if (!declaredFields.has(attribute.name)) return true;
3635
+ fullTextWarning(`Ignoring peer attribute '${attribute.name}'; the local full-text field is authoritative`);
3636
+ return false;
3637
+ });
3638
+ }
3608
3639
  const incomingFullTextValues = fullTextIndexesExplicit ? fullTextIndexes : [];
3609
3640
  let fullTextValidationAttributes: any[] | undefined;
3610
3641
  if (
3611
3642
  (persistedFullTextValues !== undefined && (rootStore instanceof RocksDatabase || fullTextIndexesExplicit)) ||
3612
- persistedPrimary.descriptor?.fullTextIndexRetirements !== undefined ||
3643
+ persistedFullTextState('fullTextIndexRetirements', Table.fullTextIndexRetirements) !== undefined ||
3644
+ (persistedFullTextState('fullTextFields', Table.fullTextFields) !== undefined &&
3645
+ (rootStore instanceof RocksDatabase || fullTextIndexesExplicit || fullTextFields !== undefined)) ||
3646
+ (fullTextFields !== undefined && (!Array.isArray(fullTextFields) || fullTextFields.length > 0)) ||
3613
3647
  Table.fullTextIndexes?.length > 0 ||
3614
3648
  (fullTextIndexesExplicit && (!Array.isArray(incomingFullTextValues) || incomingFullTextValues.length > 0))
3615
3649
  ) {
3616
3650
  if (!(rootStore instanceof RocksDatabase)) {
3617
3651
  exclusiveLock();
3618
3652
  persistedPrimary = persistedPrimaryDescriptor(Table.dbisDB);
3619
- persistedFullTextValues = persistedPrimary.descriptor?.fullTextIndexes;
3653
+ persistedFullTextValues = persistedFullTextState('fullTextIndexes', Table.fullTextIndexes);
3620
3654
  }
3621
3655
  const originalAttributes = Table.attributes.slice();
3622
3656
  fullTextIndexRetirementNames = persistedFullTextIndexNames(
3623
- persistedPrimary.descriptor?.fullTextIndexRetirements
3657
+ persistedFullTextState('fullTextIndexRetirements', Table.fullTextIndexRetirements)
3624
3658
  );
3625
3659
  const relationshipAttributes = originalAttributes.filter((attribute: any) => attribute.relationship);
3626
3660
  const originalMetadata = {
@@ -3629,6 +3663,12 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3629
3663
  cacheControl: Table.cacheControl,
3630
3664
  schemaDefined: Table.schemaDefined,
3631
3665
  };
3666
+ const originalFullTextState = {
3667
+ indexes: Table.fullTextIndexes.slice(),
3668
+ fields: [...Table.fullTextFields],
3669
+ generations: Object.assign(Object.create(null), Table.fullTextIndexGenerations),
3670
+ retirements: [...Table.fullTextIndexRetirements],
3671
+ };
3632
3672
  armFullTextLiveStateRestore = () => {
3633
3673
  if (restoreFullTextLiveState) return;
3634
3674
  restoreFullTextLiveState = () => {
@@ -3640,16 +3680,32 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3640
3680
  Object.assign(Table, originalMetadata);
3641
3681
  Table.properties = projectAttributesToProperties(restored);
3642
3682
  const restoredPrimary = persistedPrimaryDescriptor(Table.dbisDB).descriptor;
3683
+ if (restoredPrimary === undefined) {
3684
+ Table.fullTextIndexes = originalFullTextState.indexes;
3685
+ Table.fullTextFields = originalFullTextState.fields;
3686
+ Table.fullTextIndexGenerations = originalFullTextState.generations;
3687
+ Table.fullTextIndexRetirements = originalFullTextState.retirements;
3688
+ Table.schemaVersion++;
3689
+ Table.updatedAttributes();
3690
+ refreshDerivedIndexes(Table);
3691
+ return;
3692
+ }
3643
3693
  const restoredFullTextIndexes =
3644
- rootStore instanceof RocksDatabase && restoredPrimary?.audit === true
3645
- ? readPersistedFullTextDefinitions(restoredPrimary?.fullTextIndexes, restored, fullTextWarning)
3694
+ rootStore instanceof RocksDatabase && restoredPrimary.audit === true
3695
+ ? readPersistedFullTextDefinitions(restoredPrimary.fullTextIndexes, restored, fullTextWarning)
3646
3696
  : [];
3647
3697
  Table.fullTextIndexes = restoredFullTextIndexes;
3698
+ Table.fullTextFields = readPersistedFullTextFields(
3699
+ restoredPrimary.fullTextFields,
3700
+ restoredPrimary.fullTextIndexes,
3701
+ restored,
3702
+ fullTextWarning
3703
+ );
3648
3704
  Table.fullTextIndexGenerations = persistedFullTextIndexGenerations(
3649
- restoredPrimary?.fullTextIndexGenerations,
3705
+ restoredPrimary.fullTextIndexGenerations,
3650
3706
  restoredFullTextIndexes
3651
3707
  );
3652
- Table.fullTextIndexRetirements = persistedFullTextIndexNames(restoredPrimary?.fullTextIndexRetirements);
3708
+ Table.fullTextIndexRetirements = persistedFullTextIndexNames(restoredPrimary.fullTextIndexRetirements);
3653
3709
  Table.schemaVersion++;
3654
3710
  Table.updatedAttributes();
3655
3711
  refreshDerivedIndexes(Table);
@@ -3672,8 +3728,28 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3672
3728
  // Before the full-text branches can write an interim descriptor; the check on the merged list
3673
3729
  // below stays authoritative.
3674
3730
  assertDerivedFieldOwnership(validationAttributes as any[]);
3731
+ const validateFullTextFields = (definitions: unknown) => {
3732
+ const names = persistedFullTextIndexNames(definitions).map((name) => ({ name }));
3733
+ if (origin === 'cluster')
3734
+ return mergePeerFullTextFields(
3735
+ persistedFullTextState('fullTextFields', Table.fullTextFields),
3736
+ fullTextFields,
3737
+ persistedFullTextValues,
3738
+ definitions,
3739
+ validationAttributes,
3740
+ fullTextWarning
3741
+ );
3742
+ const requestedNames = new Set(names.map(({ name }) => name));
3743
+ return compileFullTextFields(
3744
+ fullTextFields === undefined ? [...requestedNames] : fullTextFields,
3745
+ names,
3746
+ validationAttributes,
3747
+ true
3748
+ );
3749
+ };
3675
3750
 
3676
- const persistedAudit = persistedPrimary.descriptor?.audit;
3751
+ const persistedAudit =
3752
+ persistedPrimary.descriptor === undefined ? Table.audit : persistedPrimary.descriptor.audit;
3677
3753
  const durableAudit = persistedAudit === true;
3678
3754
  const finalAudit =
3679
3755
  origin === 'cluster'
@@ -3700,6 +3776,7 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3700
3776
  : [];
3701
3777
  } else if (fullTextIndexesExplicit) {
3702
3778
  const compiled = compileFullTextDefinitions(incomingFullTextValues, validationAttributes);
3779
+ fullTextFieldsForPersistence = validateFullTextFields(compiled);
3703
3780
  if (compiled.length > 0 && !(rootStore instanceof RocksDatabase))
3704
3781
  throw new ClientError(
3705
3782
  `Table '${databaseName}.${tableName}' cannot use @fullText with the LMDB storage engine`,
@@ -3710,7 +3787,7 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3710
3787
  `Table '${databaseName}.${tableName}' must enable audit logging before using @fullText because its transaction log is the derived-index recovery source`,
3711
3788
  400
3712
3789
  );
3713
- const pinAudit = compiled.length > 0 && persistedPrimary.descriptor?.audit !== true;
3790
+ const pinAudit = compiled.length > 0 && persistedAudit !== true;
3714
3791
  const requestedNames = new Set(compiled.map(({ name }) => name));
3715
3792
  const durableDefinitions = readPersistedFullTextDefinitions(
3716
3793
  persistedFullTextValues,
@@ -3739,15 +3816,25 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3739
3816
  if (interimDefinitions.length > 0) {
3740
3817
  interimPrimary.fullTextIndexes = interimDefinitions;
3741
3818
  interimPrimary.fullTextIndexGenerations = persistedFullTextIndexGenerations(
3742
- persistedPrimary.descriptor?.fullTextIndexGenerations,
3819
+ persistedFullTextState('fullTextIndexGenerations', Table.fullTextIndexGenerations),
3743
3820
  interimDefinitions
3744
3821
  );
3745
3822
  } else {
3746
3823
  delete interimPrimary.fullTextIndexes;
3747
3824
  delete interimPrimary.fullTextIndexGenerations;
3748
3825
  }
3826
+ const interimFullTextFields = readPersistedFullTextFields(
3827
+ persistedFullTextState('fullTextFields', Table.fullTextFields),
3828
+ persistedFullTextValues,
3829
+ durableAttributes,
3830
+ fullTextWarning
3831
+ ).filter((name) => requestedNames.has(name));
3832
+ if (interimFullTextFields.length > 0) interimPrimary.fullTextFields = interimFullTextFields;
3833
+ else delete interimPrimary.fullTextFields;
3749
3834
  const retirements = new Set(
3750
- persistedFullTextIndexNames(persistedPrimary.descriptor?.fullTextIndexRetirements)
3835
+ persistedFullTextIndexNames(
3836
+ persistedFullTextState('fullTextIndexRetirements', Table.fullTextIndexRetirements)
3837
+ )
3751
3838
  );
3752
3839
  for (const name of transitionRetirements) retirements.add(name);
3753
3840
  for (const name of requestedNames) if (!transitionRetirements.has(name)) retirements.delete(name);
@@ -3779,6 +3866,7 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3779
3866
  fullTextValuesForPersistence = persistedFullTextValues;
3780
3867
  activeFullTextIndexes = rootStore instanceof RocksDatabase && finalAudit ? retained : [];
3781
3868
  }
3869
+ fullTextFieldsForPersistence ??= validateFullTextFields(fullTextValuesForPersistence);
3782
3870
  }
3783
3871
  if (activeFullTextIndexes !== undefined) {
3784
3872
  const durableGenerationDefinitions = readPersistedFullTextDefinitions(
@@ -3793,7 +3881,7 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3793
3881
  );
3794
3882
  fullTextIndexGenerationMap = reconcileFullTextIndexGenerations(
3795
3883
  durableGenerationDefinitions,
3796
- persistedPrimary.descriptor?.fullTextIndexGenerations,
3884
+ persistedFullTextState('fullTextIndexGenerations', Table.fullTextIndexGenerations),
3797
3885
  finalGenerationDefinitions,
3798
3886
  createFullTextIndexGeneration
3799
3887
  );
@@ -3889,12 +3977,30 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
3889
3977
  activeFullTextIndexes = compiled;
3890
3978
  }
3891
3979
  }
3980
+ fullTextFieldsForPersistence =
3981
+ origin === 'cluster'
3982
+ ? mergePeerFullTextFields(
3983
+ undefined,
3984
+ fullTextFields,
3985
+ [],
3986
+ fullTextValuesForPersistence,
3987
+ attributes,
3988
+ fullTextWarning
3989
+ )
3990
+ : compileFullTextFields(
3991
+ fullTextFields === undefined ? persistedFullTextIndexNames(fullTextValuesForPersistence) : fullTextFields,
3992
+ persistedFullTextIndexNames(fullTextValuesForPersistence).map((name) => ({ name })),
3993
+ attributes,
3994
+ true
3995
+ );
3892
3996
  const auditStore = rootStore.auditStore;
3893
3997
  primaryKeyAttribute = attributes.find((attribute) => attribute.isPrimaryKey) || {};
3894
3998
  primaryKey = primaryKeyAttribute.name;
3895
3999
  primaryKeyAttribute.isPrimaryKey = true;
3896
4000
  primaryKeyAttribute.is_hash_attribute = true; // backward-compat: harperdb@4.x reads this field to open the DBI with correct flags
3897
4001
  primaryKeyAttribute.schemaDefined = schemaDefined;
4002
+ if (fullTextFieldsForPersistence.length > 0) primaryKeyAttribute.fullTextFields = fullTextFieldsForPersistence;
4003
+ else delete primaryKeyAttribute.fullTextFields;
3898
4004
  // Old readers treat every attribute row as live schema, so relationships stay on the ignored primary descriptor.
3899
4005
  if (relationshipDefinitions) primaryKeyAttribute.relationships = relationshipDefinitions;
3900
4006
  if (Array.isArray(fullTextValuesForPersistence) && fullTextValuesForPersistence.length > 0) {
@@ -4043,6 +4149,7 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
4043
4149
  indices: {},
4044
4150
  attributes,
4045
4151
  fullTextIndexes: activeFullTextIndexes ?? [],
4152
+ fullTextFields: fullTextFieldsForPersistence,
4046
4153
  fullTextIndexGenerations: fullTextIndexGenerationMap,
4047
4154
  fullTextIndexRetirements: fullTextIndexRetirementNames ?? [],
4048
4155
  schemaDefined,
@@ -4463,6 +4570,7 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
4463
4570
  if (
4464
4571
  !deferredPrimaryRow &&
4465
4572
  (fullTextPersistencePending ||
4573
+ fullTextFieldsForPersistence !== undefined ||
4466
4574
  (rootStore instanceof RocksDatabase &&
4467
4575
  activeFullTextIndexes !== undefined &&
4468
4576
  Array.isArray(fullTextValuesForPersistence)))
@@ -4470,10 +4578,12 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
4470
4578
  const { key, descriptor } = persistedPrimaryDescriptor(attributesDbi);
4471
4579
  if (descriptor && !tableIsDropping(descriptor, key)) {
4472
4580
  const updatedPrimary = { ...descriptor };
4581
+ if (fullTextFieldsForPersistence?.length) updatedPrimary.fullTextFields = fullTextFieldsForPersistence;
4582
+ else if (fullTextFieldsForPersistence) delete updatedPrimary.fullTextFields;
4473
4583
  if (Array.isArray(fullTextValuesForPersistence) && fullTextValuesForPersistence.length > 0) {
4474
4584
  updatedPrimary.fullTextIndexes = fullTextValuesForPersistence;
4475
4585
  updatedPrimary.fullTextIndexGenerations = fullTextIndexGenerationMap;
4476
- } else {
4586
+ } else if (Array.isArray(fullTextValuesForPersistence)) {
4477
4587
  delete updatedPrimary.fullTextIndexes;
4478
4588
  delete updatedPrimary.fullTextIndexGenerations;
4479
4589
  }
@@ -4488,6 +4598,7 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
4488
4598
  else delete updatedPrimary.fullTextIndexRetirements;
4489
4599
  if (
4490
4600
  !definitionsEqual(descriptor.fullTextIndexes, updatedPrimary.fullTextIndexes) ||
4601
+ !definitionsEqual(descriptor.fullTextFields, updatedPrimary.fullTextFields) ||
4491
4602
  JSON.stringify(descriptor.fullTextIndexGenerations ?? {}) !==
4492
4603
  JSON.stringify(updatedPrimary.fullTextIndexGenerations ?? {}) ||
4493
4604
  JSON.stringify(descriptor.fullTextIndexRetirements ?? []) !==
@@ -4560,6 +4671,7 @@ function declareTable<TableResourceType>(target: TableTarget, tableDefinition: T
4560
4671
  } finally {
4561
4672
  releaseLock();
4562
4673
  }
4674
+ if (fullTextFieldsForPersistence !== undefined) Table.fullTextFields = fullTextFieldsForPersistence;
4563
4675
  if (activeFullTextIndexes !== undefined) {
4564
4676
  Table.fullTextIndexes = activeFullTextIndexes;
4565
4677
  Table.fullTextIndexGenerations = fullTextIndexGenerationMap;