@malloy-publisher/server 0.0.91 → 0.0.93

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 (310) hide show
  1. package/.prettierignore +1 -0
  2. package/dist/app/api-doc.yaml +36 -4
  3. package/dist/app/assets/index-D1sQfWlS.js +251 -0
  4. package/dist/app/assets/{index-DJPvt7yu.js → index-Dg-zTLb3.js} +1 -1
  5. package/dist/app/assets/{index.es139-nr8meTUp.js → index.es126-CKC26ttZ.js} +1 -1
  6. package/dist/app/assets/{index.es140-f9ybcYUt.js → index.es127-CBKpWhIe.js} +1 -1
  7. package/dist/app/assets/{index.es143-BrmH4hWW.js → index.es130-5z_n6lm6.js} +1 -1
  8. package/dist/app/assets/{index.es148-KJPSzIi5.js → index.es135-aJw1Xr-9.js} +1 -1
  9. package/dist/app/assets/{index.es156-AslbYSUk.js → index.es143-CAt8G2AV.js} +1 -1
  10. package/dist/app/assets/{index.es157-CGx9vJCM.js → index.es144-CaWYrbVR.js} +1 -1
  11. package/dist/app/assets/{index.es160-B4514fRp.js → index.es147-B_4j3qbz.js} +1 -1
  12. package/dist/app/assets/{index.es164-BdfNgGb4.js → index.es151-Byg3j7QH.js} +1 -1
  13. package/dist/app/assets/{index.es167-Cytvwvs5.js → index.es154-D8PMiD5x.js} +1 -1
  14. package/dist/app/assets/{index.es170-BF0Mj3Dj.js → index.es157-qx6EMeW2.js} +1 -1
  15. package/dist/app/assets/{index.es171-bDBK09mk.js → index.es158-CJWvYXRx.js} +1 -1
  16. package/dist/app/assets/{index.es185-BECCJjWR.js → index.es172-BamsV9CN.js} +1 -1
  17. package/dist/app/assets/{index.es186-DORBN8KQ.js → index.es173-CXyGU58j.js} +1 -1
  18. package/dist/app/assets/{index.es187-C1QXB69O.js → index.es174-C2o3B3o8.js} +1 -1
  19. package/dist/app/assets/{index.es189-BxZOchW8.js → index.es176-BB-1P5bb.js} +1 -1
  20. package/dist/app/assets/{index.es194-AsRZMTWT.js → index.es181-2PlTGcYG.js} +1 -1
  21. package/dist/app/assets/{index.es196-x3jX2idz.js → index.es183-DgoVwe-N.js} +1 -1
  22. package/dist/app/assets/{index.es197-Dtz8tRxv.js → index.es184-Cmc38osU.js} +1 -1
  23. package/dist/app/assets/{index.es202-BMtZpqF-.js → index.es189-DuMu0KfI.js} +1 -1
  24. package/dist/app/assets/{index.es203-gWIQWffw.js → index.es190-C117wYiK.js} +1 -1
  25. package/dist/app/assets/{index.es205-D7Lk4BX7.js → index.es192-Bm2ZoxNH.js} +1 -1
  26. package/dist/app/assets/{index.es206-BiJF4jj3.js → index.es193-BBtnt1bZ.js} +1 -1
  27. package/dist/app/assets/{index.es207-B-UJ_bzC.js → index.es194-CB2Nn3wg.js} +1 -1
  28. package/dist/app/assets/{index.es210-Cuht9bXE.js → index.es197-D91osx4R.js} +1 -1
  29. package/dist/app/assets/{index.es212-OHX8E5rN.js → index.es199-BtpESJSS.js} +1 -1
  30. package/dist/app/assets/{index.es213-ZuPzTQ_g.js → index.es200-BeG9RRw_.js} +1 -1
  31. package/dist/app/assets/{index.es214-D803Db5p.js → index.es201-k2nuCC3B.js} +1 -1
  32. package/dist/app/assets/{index.es220-7QoCPP2j.js → index.es207-C2uQegug.js} +1 -1
  33. package/dist/app/assets/{index.es221-XNqXVFFm.js → index.es208-BTucFWqR.js} +1 -1
  34. package/dist/app/assets/{index.es222-Cdb4luVg.js → index.es209-4dK47W8o.js} +1 -1
  35. package/dist/app/assets/{index.es223-CORMGf1M.js → index.es210-DO6X3-kz.js} +1 -1
  36. package/dist/app/assets/{index.es225-Cq-kOlxp.js → index.es212-CFG-vrrr.js} +1 -1
  37. package/dist/app/assets/{index.es229-E5IGXk-q.js → index.es216-TWO3Mhor.js} +1 -1
  38. package/dist/app/assets/{index.es230-Fc3aBcCM.js → index.es217-0tHVUThG.js} +1 -1
  39. package/dist/app/assets/{index.es238-PSs3scPq.js → index.es225-DHGKCs7a.js} +1 -1
  40. package/dist/app/assets/{index.es241-CL-_GK9e.js → index.es228-Bz0ZvI5L.js} +1 -1
  41. package/dist/app/assets/{index.es244-DKPCX9xC.js → index.es231-D1HdFvsl.js} +1 -1
  42. package/dist/app/assets/{index.es247-BSAYNLHy.js → index.es234-1j9jt1V9.js} +1 -1
  43. package/dist/app/assets/{index.es251--0-h5QiV.js → index.es238-DBm7c9HR.js} +1 -1
  44. package/dist/app/assets/{index.es253-BEn9NcoZ.js → index.es240-DCqDESzD.js} +1 -1
  45. package/dist/app/assets/{index.es261-MjZ_rv4c.js → index.es248-BEYN2dFo.js} +1 -1
  46. package/dist/app/assets/{index.es262-HBJu6fHJ.js → index.es249-DKdYiZA_.js} +1 -1
  47. package/dist/app/assets/{index.es269-DLa2GgqN.js → index.es256-CgBAGaDx.js} +1 -1
  48. package/dist/app/assets/{index.es270-CalwIzIq.js → index.es257-DMI3dJGJ.js} +1 -1
  49. package/dist/app/assets/{index.es280-BmnnGhn2.js → index.es267-B5ei8o6D.js} +1 -1
  50. package/dist/app/assets/{index.es284-C2YEYS8h.js → index.es271-DDBsSlrj.js} +1 -1
  51. package/dist/app/assets/{index.es290-BHjEYaA_.js → index.es277-aI_iYBJv.js} +1 -1
  52. package/dist/app/assets/{index.es295-CLE8Egbz.js → index.es282-DZQyCurk.js} +1 -1
  53. package/dist/app/assets/{index.es296-Bsq8yJGD.js → index.es283-DRQxI154.js} +1 -1
  54. package/dist/app/assets/{index.es298-CTsw_aX6.js → index.es285-DrT3oe3D.js} +1 -1
  55. package/dist/app/assets/{index.es302-BgJs_s58.js → index.es289-CKYiktdr.js} +1 -1
  56. package/dist/app/assets/{index.es304-Y67tbm3o.js → index.es291-BRH8qn2R.js} +1 -1
  57. package/dist/app/assets/{index.es306-Dsx0880i.js → index.es293-B_x7Y2b5.js} +1 -1
  58. package/dist/app/assets/{index.es309-COa7lSGF.js → index.es296-CMhur-CK.js} +1 -1
  59. package/dist/app/assets/{index.es310-C11TmHug.js → index.es297-BY9kHC-p.js} +1 -1
  60. package/dist/app/assets/{index.es314-BExIez_V.js → index.es301-CmwjGLCv.js} +1 -1
  61. package/dist/app/assets/{index.es316-DFqBRZWD.js → index.es303-BOa5F89I.js} +1 -1
  62. package/dist/app/assets/{index.es323-BClNDw1H.js → index.es310-BF6kc0UE.js} +1 -1
  63. package/dist/app/assets/{index.es325-f5hVUVjI.js → index.es312-CoCT5fTV.js} +1 -1
  64. package/dist/app/assets/{index.es327-DsTyRd6E.js → index.es314-CMYE9lCS.js} +1 -1
  65. package/dist/app/assets/{index.es331-BLhdwz0E.js → index.es318-C2_Stmju.js} +1 -1
  66. package/dist/app/assets/{index.es341-oDRBDM86.js → index.es328-DxiHjjdV.js} +1 -1
  67. package/dist/app/assets/{index.es342-DialnTnH.js → index.es329-D4H4qJV5.js} +1 -1
  68. package/dist/app/assets/{index.es349-DrBCM5L2.js → index.es336-DMBcHb9l.js} +1 -1
  69. package/dist/app/assets/{index.es350-B1R-_5vW.js → index.es337-BOcmfyvS.js} +1 -1
  70. package/dist/app/assets/{index.es50-Dwe0S21o.js → index.es56-C6ktttDI.js} +1 -1
  71. package/dist/app/assets/mui-UpaxdnvH.js +159 -0
  72. package/dist/app/index.html +2 -2
  73. package/dist/instrumentation.js +14 -0
  74. package/dist/server.js +4862 -1294
  75. package/eslint.config.mjs +8 -0
  76. package/package.json +2 -1
  77. package/publisher.config.json +25 -5
  78. package/src/config.ts +25 -2
  79. package/src/constants.ts +21 -5
  80. package/src/controller/connection.controller.ts +32 -3
  81. package/src/controller/package.controller.ts +1 -0
  82. package/src/controller/watch-mode.controller.ts +19 -4
  83. package/src/data_styles.ts +10 -3
  84. package/src/mcp/prompts/index.ts +9 -1
  85. package/src/mcp/resources/package_resource.ts +2 -1
  86. package/src/mcp/resources/source_resource.ts +1 -0
  87. package/src/mcp/resources/view_resource.ts +1 -0
  88. package/src/server.ts +23 -9
  89. package/src/service/connection.ts +277 -7
  90. package/src/service/db_utils.ts +3 -2
  91. package/src/service/model.ts +2 -0
  92. package/src/service/package.spec.ts +76 -54
  93. package/src/service/project.ts +159 -44
  94. package/src/service/project_store.spec.ts +482 -163
  95. package/src/service/project_store.ts +317 -70
  96. package/src/service/scheduler.ts +3 -2
  97. package/src/utils.ts +0 -1
  98. package/tests/harness/e2e.ts +60 -58
  99. package/tests/harness/uris.ts +21 -24
  100. package/tests/integration/mcp/mcp_resource.integration.spec.ts +10 -0
  101. package/dist/app/assets/index-CwH8v8Pf.js +0 -251
  102. package/dist/app/assets/mui-Bcj8oDBR.js +0 -161
  103. /package/dist/app/assets/{index.es128-CG6Dc4jp.js → index.es115-CG6Dc4jp.js} +0 -0
  104. /package/dist/app/assets/{index.es136-DsBKuouk.js → index.es123-DsBKuouk.js} +0 -0
  105. /package/dist/app/assets/{index.es137-D_z4Izcz.js → index.es124-D_z4Izcz.js} +0 -0
  106. /package/dist/app/assets/{index.es138-727ZlQH0.js → index.es125-727ZlQH0.js} +0 -0
  107. /package/dist/app/assets/{index.es141-Dn00JSTd.js → index.es128-Dn00JSTd.js} +0 -0
  108. /package/dist/app/assets/{index.es142-COJ4H7py.js → index.es129-COJ4H7py.js} +0 -0
  109. /package/dist/app/assets/{index.es144-Bu5BbsvL.js → index.es131-Bu5BbsvL.js} +0 -0
  110. /package/dist/app/assets/{index.es145-7O62HKoU.js → index.es132-7O62HKoU.js} +0 -0
  111. /package/dist/app/assets/{index.es146-BPT9niGB.js → index.es133-BPT9niGB.js} +0 -0
  112. /package/dist/app/assets/{index.es147-Dhn9LcZ4.js → index.es134-Dhn9LcZ4.js} +0 -0
  113. /package/dist/app/assets/{index.es149-eg146-Ew.js → index.es136-eg146-Ew.js} +0 -0
  114. /package/dist/app/assets/{index.es150-Du268qiB.js → index.es137-Du268qiB.js} +0 -0
  115. /package/dist/app/assets/{index.es151-fje9CFhw.js → index.es138-fje9CFhw.js} +0 -0
  116. /package/dist/app/assets/{index.es152-BwXTMy5W.js → index.es139-BwXTMy5W.js} +0 -0
  117. /package/dist/app/assets/{index.es153-3xVqZejG.js → index.es140-3xVqZejG.js} +0 -0
  118. /package/dist/app/assets/{index.es154-xW4inM5L.js → index.es141-xW4inM5L.js} +0 -0
  119. /package/dist/app/assets/{index.es155-DHo0CJ0O.js → index.es142-DHo0CJ0O.js} +0 -0
  120. /package/dist/app/assets/{index.es158-C3t2pwGQ.js → index.es145-C3t2pwGQ.js} +0 -0
  121. /package/dist/app/assets/{index.es159-DNquZEk8.js → index.es146-DNquZEk8.js} +0 -0
  122. /package/dist/app/assets/{index.es161-BHOwM8T6.js → index.es148-BHOwM8T6.js} +0 -0
  123. /package/dist/app/assets/{index.es162-DxSadP1t.js → index.es149-DxSadP1t.js} +0 -0
  124. /package/dist/app/assets/{index.es163-DbXoA79R.js → index.es150-DbXoA79R.js} +0 -0
  125. /package/dist/app/assets/{index.es165-Bp6g37R7.js → index.es152-Bp6g37R7.js} +0 -0
  126. /package/dist/app/assets/{index.es166-sacFqUAJ.js → index.es153-sacFqUAJ.js} +0 -0
  127. /package/dist/app/assets/{index.es168-C7gG9l05.js → index.es155-C7gG9l05.js} +0 -0
  128. /package/dist/app/assets/{index.es169-Dsg_Bt_b.js → index.es156-Dsg_Bt_b.js} +0 -0
  129. /package/dist/app/assets/{index.es172-D9R-vmeu.js → index.es159-D9R-vmeu.js} +0 -0
  130. /package/dist/app/assets/{index.es173-BPhBrDlE.js → index.es160-BPhBrDlE.js} +0 -0
  131. /package/dist/app/assets/{index.es174-C-TU5hQ_.js → index.es161-C-TU5hQ_.js} +0 -0
  132. /package/dist/app/assets/{index.es175-DtFQj3wx.js → index.es162-DtFQj3wx.js} +0 -0
  133. /package/dist/app/assets/{index.es176-m2LEI-9-.js → index.es163-m2LEI-9-.js} +0 -0
  134. /package/dist/app/assets/{index.es177-BoXegm-a.js → index.es164-BoXegm-a.js} +0 -0
  135. /package/dist/app/assets/{index.es178-B9wLZaAG.js → index.es165-B9wLZaAG.js} +0 -0
  136. /package/dist/app/assets/{index.es179-ClGRhx96.js → index.es166-ClGRhx96.js} +0 -0
  137. /package/dist/app/assets/{index.es180-DEIpsLCJ.js → index.es167-DEIpsLCJ.js} +0 -0
  138. /package/dist/app/assets/{index.es181-BgYniUM_.js → index.es168-BgYniUM_.js} +0 -0
  139. /package/dist/app/assets/{index.es182-COcR7UxN.js → index.es169-COcR7UxN.js} +0 -0
  140. /package/dist/app/assets/{index.es183-BjQB5zDj.js → index.es170-BjQB5zDj.js} +0 -0
  141. /package/dist/app/assets/{index.es184-C-nORZOA.js → index.es171-C-nORZOA.js} +0 -0
  142. /package/dist/app/assets/{index.es188-BX77sIaO.js → index.es175-BX77sIaO.js} +0 -0
  143. /package/dist/app/assets/{index.es190-B-DoSBHF.js → index.es177-B-DoSBHF.js} +0 -0
  144. /package/dist/app/assets/{index.es191-bCA53EVm.js → index.es178-bCA53EVm.js} +0 -0
  145. /package/dist/app/assets/{index.es192-w-ucz2PV.js → index.es179-w-ucz2PV.js} +0 -0
  146. /package/dist/app/assets/{index.es193-Dayu4EKP.js → index.es180-Dayu4EKP.js} +0 -0
  147. /package/dist/app/assets/{index.es195-DKXYxT9g.js → index.es182-DKXYxT9g.js} +0 -0
  148. /package/dist/app/assets/{index.es198-DfxzS6Rs.js → index.es185-DfxzS6Rs.js} +0 -0
  149. /package/dist/app/assets/{index.es199-SKMF96pI.js → index.es186-SKMF96pI.js} +0 -0
  150. /package/dist/app/assets/{index.es200-ajMbGru0.js → index.es187-ajMbGru0.js} +0 -0
  151. /package/dist/app/assets/{index.es201--30QC5Em.js → index.es188--30QC5Em.js} +0 -0
  152. /package/dist/app/assets/{index.es204-B430Bg39.js → index.es191-B430Bg39.js} +0 -0
  153. /package/dist/app/assets/{index.es208-CM8KxXT1.js → index.es195-CM8KxXT1.js} +0 -0
  154. /package/dist/app/assets/{index.es209-B1SYOhNW.js → index.es196-B1SYOhNW.js} +0 -0
  155. /package/dist/app/assets/{index.es211-DkBy-JyN.js → index.es198-DkBy-JyN.js} +0 -0
  156. /package/dist/app/assets/{index.es215-BILxekzW.js → index.es202-BILxekzW.js} +0 -0
  157. /package/dist/app/assets/{index.es216-C5wWYbrZ.js → index.es203-C5wWYbrZ.js} +0 -0
  158. /package/dist/app/assets/{index.es217-HzYwdGDm.js → index.es204-HzYwdGDm.js} +0 -0
  159. /package/dist/app/assets/{index.es218-T-Tgc4AT.js → index.es205-T-Tgc4AT.js} +0 -0
  160. /package/dist/app/assets/{index.es219-ifBTmRxC.js → index.es206-ifBTmRxC.js} +0 -0
  161. /package/dist/app/assets/{index.es224-BMj5Y0dO.js → index.es211-BMj5Y0dO.js} +0 -0
  162. /package/dist/app/assets/{index.es226-BjABl1g7.js → index.es213-BjABl1g7.js} +0 -0
  163. /package/dist/app/assets/{index.es227-xI-RfyKK.js → index.es214-xI-RfyKK.js} +0 -0
  164. /package/dist/app/assets/{index.es228-ySlJ1b_l.js → index.es215-ySlJ1b_l.js} +0 -0
  165. /package/dist/app/assets/{index.es231-BQoSv7ci.js → index.es218-BQoSv7ci.js} +0 -0
  166. /package/dist/app/assets/{index.es232-w8dY5SsB.js → index.es219-w8dY5SsB.js} +0 -0
  167. /package/dist/app/assets/{index.es233-TU54ms6u.js → index.es220-TU54ms6u.js} +0 -0
  168. /package/dist/app/assets/{index.es234-DREVFZK8.js → index.es221-DREVFZK8.js} +0 -0
  169. /package/dist/app/assets/{index.es235-BfivnA6A.js → index.es222-BfivnA6A.js} +0 -0
  170. /package/dist/app/assets/{index.es236-P4WzXJd0.js → index.es223-P4WzXJd0.js} +0 -0
  171. /package/dist/app/assets/{index.es237-BAng5TT0.js → index.es224-BAng5TT0.js} +0 -0
  172. /package/dist/app/assets/{index.es239-B5lbUyaz.js → index.es226-B5lbUyaz.js} +0 -0
  173. /package/dist/app/assets/{index.es240-mebxcVVE.js → index.es227-mebxcVVE.js} +0 -0
  174. /package/dist/app/assets/{index.es242-XBlWyCtg.js → index.es229-XBlWyCtg.js} +0 -0
  175. /package/dist/app/assets/{index.es243-BfCpw3nA.js → index.es230-BfCpw3nA.js} +0 -0
  176. /package/dist/app/assets/{index.es245-Cc5clBb7.js → index.es232-Cc5clBb7.js} +0 -0
  177. /package/dist/app/assets/{index.es246-IuBKFhSY.js → index.es233-IuBKFhSY.js} +0 -0
  178. /package/dist/app/assets/{index.es248-Du5NY7AG.js → index.es235-Du5NY7AG.js} +0 -0
  179. /package/dist/app/assets/{index.es249-Bvotw-X0.js → index.es236-Bvotw-X0.js} +0 -0
  180. /package/dist/app/assets/{index.es250-UIAJJxZW.js → index.es237-UIAJJxZW.js} +0 -0
  181. /package/dist/app/assets/{index.es252-D9-PGadD.js → index.es239-D9-PGadD.js} +0 -0
  182. /package/dist/app/assets/{index.es254-sdHcTMYB.js → index.es241-sdHcTMYB.js} +0 -0
  183. /package/dist/app/assets/{index.es255-Ci6OQyBP.js → index.es242-Ci6OQyBP.js} +0 -0
  184. /package/dist/app/assets/{index.es256-BC5c_5Pe.js → index.es243-BC5c_5Pe.js} +0 -0
  185. /package/dist/app/assets/{index.es257-Tz6hzZYG.js → index.es244-Tz6hzZYG.js} +0 -0
  186. /package/dist/app/assets/{index.es258-DB_GagMm.js → index.es245-DB_GagMm.js} +0 -0
  187. /package/dist/app/assets/{index.es259-DLbgOhZU.js → index.es246-DLbgOhZU.js} +0 -0
  188. /package/dist/app/assets/{index.es260-B0XVJmRM.js → index.es247-B0XVJmRM.js} +0 -0
  189. /package/dist/app/assets/{index.es263-shcSOmrb.js → index.es250-shcSOmrb.js} +0 -0
  190. /package/dist/app/assets/{index.es264-D4Tzg5kh.js → index.es251-D4Tzg5kh.js} +0 -0
  191. /package/dist/app/assets/{index.es265-Deuh7S70.js → index.es252-Deuh7S70.js} +0 -0
  192. /package/dist/app/assets/{index.es266-BUEGK8hf.js → index.es253-BUEGK8hf.js} +0 -0
  193. /package/dist/app/assets/{index.es267-BNioltXt.js → index.es254-BNioltXt.js} +0 -0
  194. /package/dist/app/assets/{index.es268-JqZropPD.js → index.es255-JqZropPD.js} +0 -0
  195. /package/dist/app/assets/{index.es271-LKU2TuZ1.js → index.es258-LKU2TuZ1.js} +0 -0
  196. /package/dist/app/assets/{index.es272-BFLt1xDp.js → index.es259-BFLt1xDp.js} +0 -0
  197. /package/dist/app/assets/{index.es273-DKykz6zU.js → index.es260-DKykz6zU.js} +0 -0
  198. /package/dist/app/assets/{index.es274-B3ZDOciz.js → index.es261-B3ZDOciz.js} +0 -0
  199. /package/dist/app/assets/{index.es275-CSHBycmS.js → index.es262-CSHBycmS.js} +0 -0
  200. /package/dist/app/assets/{index.es276-BIEUsx6d.js → index.es263-BIEUsx6d.js} +0 -0
  201. /package/dist/app/assets/{index.es277-B48N-Iqd.js → index.es264-B48N-Iqd.js} +0 -0
  202. /package/dist/app/assets/{index.es278-BY-TUvya.js → index.es265-BY-TUvya.js} +0 -0
  203. /package/dist/app/assets/{index.es279-zocC4JxJ.js → index.es266-zocC4JxJ.js} +0 -0
  204. /package/dist/app/assets/{index.es281-Cza_XSSt.js → index.es268-Cza_XSSt.js} +0 -0
  205. /package/dist/app/assets/{index.es282-Bg-kzb6g.js → index.es269-Bg-kzb6g.js} +0 -0
  206. /package/dist/app/assets/{index.es283-DhUJRlN_.js → index.es270-DhUJRlN_.js} +0 -0
  207. /package/dist/app/assets/{index.es285-C8lEn-DE.js → index.es272-C8lEn-DE.js} +0 -0
  208. /package/dist/app/assets/{index.es286-DhMKtDLN.js → index.es273-DhMKtDLN.js} +0 -0
  209. /package/dist/app/assets/{index.es287-CwjWoCRV.js → index.es274-CwjWoCRV.js} +0 -0
  210. /package/dist/app/assets/{index.es288-CzouJOBO.js → index.es275-CzouJOBO.js} +0 -0
  211. /package/dist/app/assets/{index.es289-B1bQXN8T.js → index.es276-B1bQXN8T.js} +0 -0
  212. /package/dist/app/assets/{index.es291-5LuOXUq_.js → index.es278-5LuOXUq_.js} +0 -0
  213. /package/dist/app/assets/{index.es292-DWJ3fJO_.js → index.es279-DWJ3fJO_.js} +0 -0
  214. /package/dist/app/assets/{index.es293-DJlmqQ1C.js → index.es280-DJlmqQ1C.js} +0 -0
  215. /package/dist/app/assets/{index.es294-QhoSD0DR.js → index.es281-QhoSD0DR.js} +0 -0
  216. /package/dist/app/assets/{index.es297-Be6lgOlo.js → index.es284-Be6lgOlo.js} +0 -0
  217. /package/dist/app/assets/{index.es299-BJ4Li9vH.js → index.es286-BJ4Li9vH.js} +0 -0
  218. /package/dist/app/assets/{index.es300-DQVVAn-B.js → index.es287-DQVVAn-B.js} +0 -0
  219. /package/dist/app/assets/{index.es301-BJGe-b2p.js → index.es288-BJGe-b2p.js} +0 -0
  220. /package/dist/app/assets/{index.es303-BLhTXw86.js → index.es290-BLhTXw86.js} +0 -0
  221. /package/dist/app/assets/{index.es305-atvbtKCR.js → index.es292-atvbtKCR.js} +0 -0
  222. /package/dist/app/assets/{index.es307-DkLiglaE.js → index.es294-DkLiglaE.js} +0 -0
  223. /package/dist/app/assets/{index.es308-C1w2a3ep.js → index.es295-C1w2a3ep.js} +0 -0
  224. /package/dist/app/assets/{index.es311-Cf8iN4DR.js → index.es298-Cf8iN4DR.js} +0 -0
  225. /package/dist/app/assets/{index.es312-COK4E0Yg.js → index.es299-COK4E0Yg.js} +0 -0
  226. /package/dist/app/assets/{index.es313-BknIz3MU.js → index.es300-BknIz3MU.js} +0 -0
  227. /package/dist/app/assets/{index.es315-BeQkCIfX.js → index.es302-BeQkCIfX.js} +0 -0
  228. /package/dist/app/assets/{index.es317-BSxZ-RaX.js → index.es304-BSxZ-RaX.js} +0 -0
  229. /package/dist/app/assets/{index.es318-C7L56vO4.js → index.es305-C7L56vO4.js} +0 -0
  230. /package/dist/app/assets/{index.es319-CUnW07Te.js → index.es306-CUnW07Te.js} +0 -0
  231. /package/dist/app/assets/{index.es320-C1XDQQGZ.js → index.es307-C1XDQQGZ.js} +0 -0
  232. /package/dist/app/assets/{index.es321-CQjiPCtT.js → index.es308-CQjiPCtT.js} +0 -0
  233. /package/dist/app/assets/{index.es322-DQ1-QYvQ.js → index.es309-DQ1-QYvQ.js} +0 -0
  234. /package/dist/app/assets/{index.es324-BbSNqyBO.js → index.es311-BbSNqyBO.js} +0 -0
  235. /package/dist/app/assets/{index.es326-CB2ApiWb.js → index.es313-CB2ApiWb.js} +0 -0
  236. /package/dist/app/assets/{index.es328-B_m7g4N7.js → index.es315-B_m7g4N7.js} +0 -0
  237. /package/dist/app/assets/{index.es329-B6W0miNI.js → index.es316-B6W0miNI.js} +0 -0
  238. /package/dist/app/assets/{index.es330-BMR_PYu6.js → index.es317-BMR_PYu6.js} +0 -0
  239. /package/dist/app/assets/{index.es332-Dj6nwHGl.js → index.es319-Dj6nwHGl.js} +0 -0
  240. /package/dist/app/assets/{index.es333-BpWG_bgh.js → index.es320-BpWG_bgh.js} +0 -0
  241. /package/dist/app/assets/{index.es334-BVUVsWT6.js → index.es321-BVUVsWT6.js} +0 -0
  242. /package/dist/app/assets/{index.es335-CAQ2eGtk.js → index.es322-CAQ2eGtk.js} +0 -0
  243. /package/dist/app/assets/{index.es336-BFOHcciG.js → index.es323-BFOHcciG.js} +0 -0
  244. /package/dist/app/assets/{index.es337-CdO5JTpU.js → index.es324-CdO5JTpU.js} +0 -0
  245. /package/dist/app/assets/{index.es338-CJaU5se_.js → index.es325-CJaU5se_.js} +0 -0
  246. /package/dist/app/assets/{index.es339-DYoNaHQp.js → index.es326-DYoNaHQp.js} +0 -0
  247. /package/dist/app/assets/{index.es340-m4uW47V2.js → index.es327-m4uW47V2.js} +0 -0
  248. /package/dist/app/assets/{index.es343-nyqBNV6O.js → index.es330-nyqBNV6O.js} +0 -0
  249. /package/dist/app/assets/{index.es344-C6j12Q_x.js → index.es331-C6j12Q_x.js} +0 -0
  250. /package/dist/app/assets/{index.es345-7A4Fjokl.js → index.es332-7A4Fjokl.js} +0 -0
  251. /package/dist/app/assets/{index.es346-CB0Krxn9.js → index.es333-CB0Krxn9.js} +0 -0
  252. /package/dist/app/assets/{index.es347-DCE3LsBG.js → index.es334-DCE3LsBG.js} +0 -0
  253. /package/dist/app/assets/{index.es348-C3FkfJm5.js → index.es335-C3FkfJm5.js} +0 -0
  254. /package/dist/app/assets/{index.es351-CVw76BM1.js → index.es338-CVw76BM1.js} +0 -0
  255. /package/dist/app/assets/{index.es352-HnGAYVZD.js → index.es339-HnGAYVZD.js} +0 -0
  256. /package/dist/app/assets/{index.es353-BVz_zdnA.js → index.es340-BVz_zdnA.js} +0 -0
  257. /package/dist/app/assets/{index.es354-C3khCPGq.js → index.es341-C3khCPGq.js} +0 -0
  258. /package/dist/app/assets/{index.es355-D-2ljcwZ.js → index.es342-D-2ljcwZ.js} +0 -0
  259. /package/dist/app/assets/{index.es356-Cv9koXgw.js → index.es343-Cv9koXgw.js} +0 -0
  260. /package/dist/app/assets/{index.es357-CD_QflpE.js → index.es344-CD_QflpE.js} +0 -0
  261. /package/dist/app/assets/{index.es358-DRW-0cLl.js → index.es345-DRW-0cLl.js} +0 -0
  262. /package/dist/app/assets/{index.es359-C-_shW-Y.js → index.es346-C-_shW-Y.js} +0 -0
  263. /package/dist/app/assets/{index.es360-LGGdnPYs.js → index.es347-LGGdnPYs.js} +0 -0
  264. /package/dist/app/assets/{index.es361-C3mMm8J8.js → index.es348-C3mMm8J8.js} +0 -0
  265. /package/dist/app/assets/{index.es362-BzJJZx-M.js → index.es349-BzJJZx-M.js} +0 -0
  266. /package/dist/app/assets/{index.es363-BXkSAIEj.js → index.es350-BXkSAIEj.js} +0 -0
  267. /package/dist/app/assets/{index.es364-BgDCqdQA.js → index.es351-BgDCqdQA.js} +0 -0
  268. /package/dist/app/assets/{index.es365-C8M2exoo.js → index.es352-C8M2exoo.js} +0 -0
  269. /package/dist/app/assets/{index.es366-DHJKELXO.js → index.es353-DHJKELXO.js} +0 -0
  270. /package/dist/app/assets/{index.es367-Cuk6v7N8.js → index.es354-Cuk6v7N8.js} +0 -0
  271. /package/dist/app/assets/{index.es368-DH5Ifo-i.js → index.es355-DH5Ifo-i.js} +0 -0
  272. /package/dist/app/assets/{index.es369-E3gJ1_iC.js → index.es356-E3gJ1_iC.js} +0 -0
  273. /package/dist/app/assets/{index.es370-DAi9KRSo.js → index.es357-DAi9KRSo.js} +0 -0
  274. /package/dist/app/assets/{index.es371-D7oLnXFd.js → index.es358-D7oLnXFd.js} +0 -0
  275. /package/dist/app/assets/{index.es372-BfjtVDDH.js → index.es359-BfjtVDDH.js} +0 -0
  276. /package/dist/app/assets/{index.es373-DnULxvSX.js → index.es360-DnULxvSX.js} +0 -0
  277. /package/dist/app/assets/{index.es374-CkXjmgJE.js → index.es361-CkXjmgJE.js} +0 -0
  278. /package/dist/app/assets/{index.es375-CfQXZHmo.js → index.es362-CfQXZHmo.js} +0 -0
  279. /package/dist/app/assets/{index.es376-DWedfzmr.js → index.es363-DWedfzmr.js} +0 -0
  280. /package/dist/app/assets/{index.es377-DUszq2jm.js → index.es364-DUszq2jm.js} +0 -0
  281. /package/dist/app/assets/{index.es378-B7mTdjB0.js → index.es365-B7mTdjB0.js} +0 -0
  282. /package/dist/app/assets/{index.es379-D5KoaKCx.js → index.es366-D5KoaKCx.js} +0 -0
  283. /package/dist/app/assets/{index.es380-BfHTSMKl.js → index.es367-BfHTSMKl.js} +0 -0
  284. /package/dist/app/assets/{index.es381-B0m2ddpp.js → index.es368-B0m2ddpp.js} +0 -0
  285. /package/dist/app/assets/{index.es382-CyktbL80.js → index.es369-CyktbL80.js} +0 -0
  286. /package/dist/app/assets/{index.es383-Csfq5Kiy.js → index.es370-Csfq5Kiy.js} +0 -0
  287. /package/dist/app/assets/{index.es384-CafNBF8u.js → index.es371-CafNBF8u.js} +0 -0
  288. /package/dist/app/assets/{index.es385-CTRr51gU.js → index.es372-CTRr51gU.js} +0 -0
  289. /package/dist/app/assets/{index.es386-D4h5O-jR.js → index.es373-D4h5O-jR.js} +0 -0
  290. /package/dist/app/assets/{index.es387-C39BiMTA.js → index.es374-C39BiMTA.js} +0 -0
  291. /package/dist/app/assets/{index.es388-Ddv68eIx.js → index.es375-Ddv68eIx.js} +0 -0
  292. /package/dist/app/assets/{index.es389-GBQ2dnAY.js → index.es376-GBQ2dnAY.js} +0 -0
  293. /package/dist/app/assets/{index.es390-PoHY5YXO.js → index.es377-PoHY5YXO.js} +0 -0
  294. /package/dist/app/assets/{index.es391-3e1v2bzS.js → index.es378-3e1v2bzS.js} +0 -0
  295. /package/dist/app/assets/{index.es392-CS3Unz2-.js → index.es379-CS3Unz2-.js} +0 -0
  296. /package/dist/app/assets/{index.es393-bN70gL4F.js → index.es380-bN70gL4F.js} +0 -0
  297. /package/dist/app/assets/{index.es394-CmCqftbK.js → index.es381-CmCqftbK.js} +0 -0
  298. /package/dist/app/assets/{index.es395-Ds-gbosJ.js → index.es382-Ds-gbosJ.js} +0 -0
  299. /package/dist/app/assets/{index.es396-CjDtw9vr.js → index.es383-CjDtw9vr.js} +0 -0
  300. /package/dist/app/assets/{index.es397-BthQWCQV.js → index.es384-BthQWCQV.js} +0 -0
  301. /package/dist/app/assets/{index.es398-DqwNpetd.js → index.es385-DqwNpetd.js} +0 -0
  302. /package/dist/app/assets/{index.es399-Bw305WKR.js → index.es386-Bw305WKR.js} +0 -0
  303. /package/dist/app/assets/{index.es400-DXbdFlpD.js → index.es387-DXbdFlpD.js} +0 -0
  304. /package/dist/app/assets/{index.es401-L9t79GZl.js → index.es388-L9t79GZl.js} +0 -0
  305. /package/dist/app/assets/{index.es402-CbfX1IO0.js → index.es389-CbfX1IO0.js} +0 -0
  306. /package/dist/app/assets/{index.es403-DBQeEorK.js → index.es390-DBQeEorK.js} +0 -0
  307. /package/dist/app/assets/{index.es404-BEBZ7ncR.js → index.es391-BEBZ7ncR.js} +0 -0
  308. /package/dist/app/assets/{index.es405-Bkuqu6BP.js → index.es392-Bkuqu6BP.js} +0 -0
  309. /package/dist/app/assets/{index.es406-D0r3Knsf.js → index.es393-D0r3Knsf.js} +0 -0
  310. /package/dist/app/assets/{index.es407-CVO1_9PV.js → index.es394-CVO1_9PV.js} +0 -0
@@ -1,200 +1,519 @@
1
- import {
2
- afterAll,
3
- beforeAll,
4
- describe,
5
- expect,
6
- it,
7
- mock,
8
- spyOn,
9
- } from "bun:test";
10
- import { rmSync } from "fs";
11
- import * as fs from "fs/promises";
12
- import path from "path";
1
+ import { afterEach, beforeEach, describe, expect, it, mock } from "bun:test";
2
+ import { existsSync, rmSync, mkdirSync, writeFileSync } from "fs";
3
+ import * as path from "path";
4
+ import * as sinon from "sinon";
5
+ import { components } from "../api";
13
6
  import { isPublisherConfigFrozen } from "../config";
14
- import { publisherPath } from "../constants";
15
- import { FrozenConfigError, ProjectNotFoundError } from "../errors";
16
- import { logger } from "../logger";
17
7
  import { ProjectStore } from "./project_store";
18
- import sinon from "sinon";
8
+ import { Project } from "./project";
9
+ import { TEMP_DIR_PATH } from "../constants";
19
10
 
20
- describe("ProjectStore", () => {
21
- const serverRoot = path.resolve(
22
- process.cwd(),
23
- process.env.SERVER_ROOT || ".",
24
- );
25
- let loggerStub: sinon.SinonStub;
11
+ type Connection = components["schemas"]["Connection"];
26
12
 
27
- beforeAll(() => {
28
- rmSync(path.resolve(publisherPath, "malloy-samples"), {
29
- recursive: true,
30
- force: true,
31
- });
32
- loggerStub = sinon.stub(logger, "info").returns(logger);
13
+ const serverRootPath = path.join(
14
+ TEMP_DIR_PATH,
15
+ "pathways-worker-publisher-project-store-test",
16
+ );
17
+ const projectName = "organizationName-projectName";
18
+ const testConnections: Connection[] = [
19
+ {
20
+ name: "testConnection",
21
+ type: "postgres",
22
+ postgresConnection: {
23
+ host: "host",
24
+ port: 1234,
25
+ databaseName: "databaseName",
26
+ userName: "userName",
27
+ password: "password",
28
+ },
29
+ },
30
+ ];
31
+
32
+ let sandbox: sinon.SinonSandbox;
33
+
34
+ describe("ProjectStore Service", () => {
35
+ let projectStore: ProjectStore;
36
+
37
+ beforeEach(async () => {
38
+ // Clean up any existing test directory
39
+ if (existsSync(serverRootPath)) {
40
+ rmSync(serverRootPath, { recursive: true, force: true });
41
+ }
42
+ mkdirSync(serverRootPath);
43
+ sandbox = sinon.createSandbox();
44
+
45
+ // Mock the configuration to prevent initialization errors
46
+ mock(isPublisherConfigFrozen).mockReturnValue(false);
47
+ mock.module("../config", () => ({
48
+ isPublisherConfigFrozen: () => false,
49
+ }));
50
+
51
+ // Create project store after mocking
52
+ projectStore = new ProjectStore(serverRootPath);
33
53
  });
34
- afterAll(() => {
35
- loggerStub.restore();
54
+
55
+ afterEach(async () => {
56
+ // Clean up the test directory after each test
57
+ if (existsSync(serverRootPath)) {
58
+ rmSync(serverRootPath, { recursive: true, force: true });
59
+ }
60
+ mkdirSync(serverRootPath);
61
+ sandbox.restore();
36
62
  });
37
63
 
38
- it("should load all projects from publisher.config.json on initialization", async () => {
39
- mock(isPublisherConfigFrozen).mockReturnValue(true);
40
- const projectStore = new ProjectStore(serverRoot);
41
- mock(projectStore.downloadGitHubDirectory).mockResolvedValue(undefined);
42
- await projectStore.finishedInitialization;
43
- expect(await projectStore.listProjects()).toEqual([
44
- {
45
- name: "malloy-samples",
46
- readme: expect.any(String),
47
- resource: "/api/v0/projects/malloy-samples",
48
- packages: expect.any(Array),
49
- connections: expect.any(Array),
50
- },
51
- ]);
64
+ it("should not load a package if the project does not exist", async () => {
65
+ await expect(
66
+ projectStore.getProject("non-existent-project"),
67
+ ).rejects.toThrow();
52
68
  });
53
69
 
54
- it("should list projects from memory by default", async () => {
55
- mock(isPublisherConfigFrozen).mockReturnValue(true);
56
- const projectStore = new ProjectStore(serverRoot);
57
- const projects = await projectStore.listProjects();
58
- expect(projects).toEqual([
59
- {
60
- name: "malloy-samples",
61
- readme: expect.any(String),
62
- resource: "/api/v0/projects/malloy-samples",
63
- packages: expect.any(Array),
64
- connections: expect.any(Array),
65
- },
66
- ]);
70
+ it("should create and manage projects with connections", async () => {
71
+ // Create a project directory
72
+ const projectPath = path.join(serverRootPath, projectName);
73
+ mkdirSync(projectPath, { recursive: true });
74
+
75
+ // Create connections file
76
+ const connectionsPath = path.join(
77
+ projectPath,
78
+ "publisher.connections.json",
79
+ );
80
+ writeFileSync(connectionsPath, JSON.stringify(testConnections));
81
+
82
+ // Create publisher config
83
+ const publisherConfigPath = path.join(
84
+ serverRootPath,
85
+ "publisher.config.json",
86
+ );
87
+ writeFileSync(
88
+ publisherConfigPath,
89
+ JSON.stringify({
90
+ projects: [
91
+ {
92
+ name: projectName,
93
+ packages: [
94
+ {
95
+ name: projectName,
96
+ location: projectPath,
97
+ },
98
+ ],
99
+ },
100
+ ],
101
+ }),
102
+ );
103
+
104
+ // Test that the project can be retrieved
105
+ const project = await projectStore.getProject(projectName);
106
+ expect(project).toBeInstanceOf(Project);
107
+ expect(project.metadata.name).toBe(projectName);
67
108
  });
68
109
 
69
- it("should list projects from disk if reload is true", async () => {
70
- // Mock fs.readFile to track calls
71
- const fs = await import("fs/promises");
72
- const readFileSpy = spyOn(fs, "readFile");
73
- mock(isPublisherConfigFrozen).mockReturnValue(true);
74
- const projectStore = new ProjectStore(serverRoot);
110
+ it("should handle multiple projects", async () => {
111
+ const projectName1 = "project1";
112
+ const projectName2 = "project2";
113
+ const projectPath1 = path.join(serverRootPath, projectName1);
114
+ const projectPath2 = path.join(serverRootPath, projectName2);
115
+
116
+ // Create project directories
117
+ mkdirSync(projectPath1, { recursive: true });
118
+ mkdirSync(projectPath2, { recursive: true });
119
+
120
+ // Create connections files
121
+ writeFileSync(
122
+ path.join(projectPath1, "publisher.connections.json"),
123
+ JSON.stringify(testConnections),
124
+ );
125
+ writeFileSync(
126
+ path.join(projectPath2, "publisher.connections.json"),
127
+ JSON.stringify(testConnections),
128
+ );
129
+
130
+ // Create publisher config
131
+ const publisherConfigPath = path.join(
132
+ serverRootPath,
133
+ "publisher.config.json",
134
+ );
135
+ writeFileSync(
136
+ publisherConfigPath,
137
+ JSON.stringify({
138
+ projects: [
139
+ {
140
+ name: projectName1,
141
+ packages: [
142
+ {
143
+ name: projectName1,
144
+ location: projectPath1,
145
+ },
146
+ ],
147
+ },
148
+ {
149
+ name: projectName2,
150
+ packages: [
151
+ {
152
+ name: projectName2,
153
+ location: projectPath2,
154
+ },
155
+ ],
156
+ },
157
+ ],
158
+ }),
159
+ );
75
160
 
76
- // Call getProject with reload=true
77
- await projectStore.getProject("malloy-samples", true);
161
+ // Create a new project store that will read the configuration
162
+ const newProjectStore = new ProjectStore(serverRootPath);
163
+ await newProjectStore.finishedInitialization;
78
164
 
79
- expect(readFileSpy).toHaveBeenCalled();
165
+ // Test that both projects can be listed
166
+ const projects = await newProjectStore.listProjects();
167
+ expect(projects).toBeInstanceOf(Array);
168
+ expect(projects.length).toBe(2);
169
+ expect(projects.map((p) => p.name)).toContain(projectName1);
170
+ expect(projects.map((p) => p.name)).toContain(projectName2);
80
171
  });
81
172
 
82
- it("should allow modifying the in-memory hashmap if config is not frozen", async () => {
83
- mock.module("../config", () => ({
84
- isPublisherConfigFrozen: () => false,
85
- }));
86
- const projectStore = new ProjectStore(serverRoot);
87
- mock(projectStore.downloadGitHubDirectory).mockResolvedValue(undefined);
88
- await projectStore.finishedInitialization;
89
- await projectStore.updateProject({
90
- name: "malloy-samples",
91
- readme: "Updated README",
92
- });
93
- let projects = await projectStore.listProjects();
94
- projects = await projectStore.listProjects();
95
- let malloySamplesProject = projects.find(
96
- (p) => p.name === "malloy-samples",
97
- );
98
- expect(malloySamplesProject).toBeDefined();
99
- expect(malloySamplesProject).toMatchObject(
100
- expect.objectContaining({
101
- name: "malloy-samples",
102
- readme: "Updated README",
103
- resource: "/api/v0/projects/malloy-samples",
173
+ it("should handle project updates", async () => {
174
+ // Create a project directory
175
+ const projectPath = path.join(serverRootPath, projectName);
176
+ mkdirSync(projectPath, { recursive: true });
177
+
178
+ // Create connections file
179
+ const connectionsPath = path.join(
180
+ projectPath,
181
+ "publisher.connections.json",
182
+ );
183
+ writeFileSync(connectionsPath, JSON.stringify(testConnections));
184
+
185
+ // Create publisher config
186
+ const publisherConfigPath = path.join(
187
+ serverRootPath,
188
+ "publisher.config.json",
189
+ );
190
+ writeFileSync(
191
+ publisherConfigPath,
192
+ JSON.stringify({
193
+ projects: [
194
+ {
195
+ name: projectName,
196
+ packages: [
197
+ {
198
+ name: projectName,
199
+ location: projectPath,
200
+ },
201
+ ],
202
+ },
203
+ ],
104
204
  }),
105
205
  );
106
- await projectStore.deleteProject("malloy-samples");
107
- expect(await projectStore.listProjects()).toEqual([]);
108
- await projectStore.addProject({
109
- name: "malloy-samples",
110
- });
111
206
 
112
- expect(
113
- await projectStore.getProject("malloy-samples", false),
114
- ).toHaveProperty("metadata", {
115
- name: "malloy-samples",
116
- resource: "/api/v0/projects/malloy-samples",
117
- location: expect.any(String),
118
- });
207
+ // Get the project
208
+ const project = await projectStore.getProject(projectName);
119
209
 
120
- projects = await projectStore.listProjects();
121
- malloySamplesProject = projects.find((p) => p.name === "malloy-samples");
122
- expect(malloySamplesProject).toBeDefined();
123
- expect(malloySamplesProject).toMatchObject({
124
- name: "malloy-samples",
125
- resource: "/api/v0/projects/malloy-samples",
210
+ // Update the project
211
+ const updatedProject = await project.update({
212
+ name: projectName,
213
+ readme: "Updated README content",
126
214
  });
215
+
216
+ expect(updatedProject.metadata.readme).toBe("Updated README content");
127
217
  });
128
218
 
129
- it("should not allow modifying the in-memory hashmap if config is frozen", async () => {
130
- mock.module("../config", () => ({
131
- isPublisherConfigFrozen: () => true,
132
- }));
133
- const projectStore = new ProjectStore(serverRoot);
134
- // Initialization should succeed
135
- await projectStore.finishedInitialization;
136
- expect(await projectStore.listProjects()).toEqual([
137
- {
138
- name: "malloy-samples",
139
- readme: expect.any(String),
140
- resource: "/api/v0/projects/malloy-samples",
141
- packages: expect.any(Array),
142
- connections: expect.any(Array),
143
- },
144
- ]);
145
- // Adding a project should fail
146
- expect(
147
- projectStore.addProject({
148
- name: "malloy-samples",
219
+ it("should handle project reload", async () => {
220
+ // Create a project directory
221
+ const projectPath = path.join(serverRootPath, projectName);
222
+ mkdirSync(projectPath, { recursive: true });
223
+
224
+ // Create connections file
225
+ const connectionsPath = path.join(
226
+ projectPath,
227
+ "publisher.connections.json",
228
+ );
229
+ writeFileSync(connectionsPath, JSON.stringify(testConnections));
230
+
231
+ // Create publisher config
232
+ const publisherConfigPath = path.join(
233
+ serverRootPath,
234
+ "publisher.config.json",
235
+ );
236
+ writeFileSync(
237
+ publisherConfigPath,
238
+ JSON.stringify({
239
+ projects: [
240
+ {
241
+ name: projectName,
242
+ packages: [
243
+ {
244
+ name: projectName,
245
+ location: projectPath,
246
+ },
247
+ ],
248
+ },
249
+ ],
149
250
  }),
150
- ).rejects.toThrow(FrozenConfigError);
251
+ );
252
+
253
+ // Get the project
254
+ const project1 = await projectStore.getProject(projectName);
151
255
 
152
- // Updating a project should fail
153
- expect(
154
- projectStore.updateProject({
155
- name: "malloy-samples",
156
- readme: "Updated README",
256
+ // Get the project again with reload=true
257
+ const project2 = await projectStore.getProject(projectName, true);
258
+
259
+ expect(project1).toBeInstanceOf(Project);
260
+ expect(project2).toBeInstanceOf(Project);
261
+ expect(project1.metadata.name).toBe(project2.metadata.name as string);
262
+ });
263
+
264
+ it("should handle missing project paths", async () => {
265
+ // Create publisher config with non-existent project path
266
+ const publisherConfigPath = path.join(
267
+ serverRootPath,
268
+ "publisher.config.json",
269
+ );
270
+ writeFileSync(
271
+ publisherConfigPath,
272
+ JSON.stringify({
273
+ projects: [
274
+ {
275
+ name: projectName,
276
+ packages: [
277
+ {
278
+ name: projectName,
279
+ location: "/non/existent/path",
280
+ },
281
+ ],
282
+ },
283
+ ],
157
284
  }),
158
- ).rejects.toThrow(FrozenConfigError);
285
+ );
286
+
287
+ // Test that getting the project throws an error
288
+ await expect(projectStore.getProject(projectName)).rejects.toThrow();
289
+ });
159
290
 
160
- // Deleting a project should fail
161
- expect(projectStore.deleteProject("malloy-samples")).rejects.toThrow(
162
- FrozenConfigError,
291
+ it("should handle invalid publisher config", async () => {
292
+ // Create invalid publisher config
293
+ const publisherConfigPath = path.join(
294
+ serverRootPath,
295
+ "publisher.config.json",
163
296
  );
297
+ writeFileSync(publisherConfigPath, "invalid json");
164
298
 
165
- // Failed methods should not modify the in-memory hashmap
166
- expect(await projectStore.listProjects()).toEqual([
167
- {
168
- name: "malloy-samples",
169
- readme: expect.any(String),
170
- resource: "/api/v0/projects/malloy-samples",
171
- packages: expect.any(Array),
172
- connections: expect.any(Array),
173
- },
174
- ]);
299
+ // Create a new project store that will read the invalid config
300
+ const newProjectStore = new ProjectStore(serverRootPath);
301
+
302
+ // Test that the project store handles invalid JSON gracefully by falling back to empty config
303
+ await newProjectStore.finishedInitialization;
304
+ const projects = await newProjectStore.listProjects();
305
+ expect(projects).toEqual([]);
175
306
  });
176
307
 
177
- it("should always try to reload a project if it's not in the hashmap", async () => {
178
- mock.module("../config", () => ({
179
- isPublisherConfigFrozen: () => false,
180
- }));
181
- const projectStore = new ProjectStore(serverRoot);
182
- await projectStore.finishedInitialization;
183
- await projectStore.deleteProject("malloy-samples");
184
- expect(await projectStore.listProjects()).toEqual([]);
185
- const readFileSpy = spyOn(fs, "readFile");
186
- await projectStore.getProject("malloy-samples", true);
187
- expect(readFileSpy).toHaveBeenCalled();
308
+ it("should handle concurrent project access", async () => {
309
+ // Create a project directory
310
+ const projectPath = path.join(serverRootPath, projectName);
311
+ mkdirSync(projectPath, { recursive: true });
312
+
313
+ // Create connections file
314
+ const connectionsPath = path.join(
315
+ projectPath,
316
+ "publisher.connections.json",
317
+ );
318
+ writeFileSync(connectionsPath, JSON.stringify(testConnections));
319
+
320
+ // Create publisher config
321
+ const publisherConfigPath = path.join(
322
+ serverRootPath,
323
+ "publisher.config.json",
324
+ );
325
+ writeFileSync(
326
+ publisherConfigPath,
327
+ JSON.stringify({
328
+ projects: [
329
+ {
330
+ name: projectName,
331
+ packages: [
332
+ {
333
+ name: projectName,
334
+ location: projectPath,
335
+ },
336
+ ],
337
+ },
338
+ ],
339
+ }),
340
+ );
341
+
342
+ // Test concurrent access to the same project
343
+ const promises = Array.from({ length: 5 }, () =>
344
+ projectStore.getProject(projectName),
345
+ );
346
+
347
+ const projects = await Promise.all(promises);
348
+
349
+ expect(projects).toHaveLength(5);
350
+ projects.forEach((project) => {
351
+ expect(project).toBeInstanceOf(Project);
352
+ expect(project.metadata.name).toBe(projectName);
353
+ });
188
354
  });
355
+ });
189
356
 
190
- it("should throw a NotFound error when reloading a project that is not in disk", async () => {
357
+ describe("Project Service Error Recovery", () => {
358
+ let sandbox: sinon.SinonSandbox;
359
+ let projectStore: ProjectStore;
360
+ const serverRootPath = path.join(
361
+ TEMP_DIR_PATH,
362
+ "pathways-worker-publisher-error-recovery-test",
363
+ );
364
+ const projectName = "organizationName-projectName-error-recovery";
365
+ const testConnections: Connection[] = [
366
+ {
367
+ name: "testConnection",
368
+ type: "postgres",
369
+ postgresConnection: {
370
+ host: "host",
371
+ port: 1234,
372
+ databaseName: "databaseName",
373
+ userName: "userName",
374
+ password: "password",
375
+ },
376
+ },
377
+ ];
378
+
379
+ beforeEach(async () => {
380
+ sandbox = sinon.createSandbox();
381
+ mkdirSync(serverRootPath, { recursive: true });
382
+
383
+ // Mock the configuration to prevent initialization errors
384
+ mock(isPublisherConfigFrozen).mockReturnValue(false);
191
385
  mock.module("../config", () => ({
192
386
  isPublisherConfigFrozen: () => false,
193
387
  }));
194
- const projectStore = new ProjectStore(serverRoot);
195
- await projectStore.finishedInitialization;
196
- expect(
197
- projectStore.getProject("this-one-does-not-exist", true),
198
- ).rejects.toThrow(ProjectNotFoundError);
388
+
389
+ // Create project store after mocking
390
+ projectStore = new ProjectStore(serverRootPath);
391
+ });
392
+
393
+ afterEach(async () => {
394
+ sandbox.restore();
395
+ if (existsSync(serverRootPath)) {
396
+ rmSync(serverRootPath, { recursive: true, force: true });
397
+ }
398
+ });
399
+
400
+ describe("Project Loading Error Recovery", () => {
401
+ it("should handle missing project directories gracefully", async () => {
402
+ // Create publisher config with missing project directory
403
+ const publisherConfigPath = path.join(
404
+ serverRootPath,
405
+ "publisher.config.json",
406
+ );
407
+ writeFileSync(
408
+ publisherConfigPath,
409
+ JSON.stringify({
410
+ projects: [
411
+ {
412
+ name: projectName,
413
+ packages: [
414
+ {
415
+ name: projectName,
416
+ location: path.join(
417
+ serverRootPath,
418
+ "missing-project",
419
+ ),
420
+ },
421
+ ],
422
+ },
423
+ ],
424
+ }),
425
+ );
426
+
427
+ // Test that the project store handles the missing directory
428
+ await expect(projectStore.getProject(projectName)).rejects.toThrow();
429
+ });
430
+
431
+ it("should handle corrupted connection files", async () => {
432
+ // Create a project directory
433
+ const projectPath = path.join(serverRootPath, projectName);
434
+ mkdirSync(projectPath, { recursive: true });
435
+
436
+ // Create corrupted connections file
437
+ const connectionsPath = path.join(
438
+ projectPath,
439
+ "publisher.connections.json",
440
+ );
441
+ writeFileSync(connectionsPath, "invalid json");
442
+
443
+ // Create publisher config
444
+ const publisherConfigPath = path.join(
445
+ serverRootPath,
446
+ "publisher.config.json",
447
+ );
448
+ writeFileSync(
449
+ publisherConfigPath,
450
+ JSON.stringify({
451
+ projects: [
452
+ {
453
+ name: projectName,
454
+ packages: [
455
+ {
456
+ name: projectName,
457
+ location: projectPath,
458
+ },
459
+ ],
460
+ },
461
+ ],
462
+ }),
463
+ );
464
+
465
+ // Test that the project store handles corrupted connection files gracefully
466
+ // (The current implementation loads the project even with corrupted connection files)
467
+ const project = await projectStore.getProject(projectName);
468
+ expect(project).toBeInstanceOf(Project);
469
+ expect(project.metadata.name).toBe(projectName);
470
+ });
471
+ });
472
+
473
+ describe("Project Store State Management", () => {
474
+ it("should maintain consistent state after errors", async () => {
475
+ // Create a valid project first
476
+ const projectPath = path.join(serverRootPath, projectName);
477
+ mkdirSync(projectPath, { recursive: true });
478
+ writeFileSync(
479
+ path.join(projectPath, "publisher.connections.json"),
480
+ JSON.stringify(testConnections),
481
+ );
482
+
483
+ const publisherConfigPath = path.join(
484
+ serverRootPath,
485
+ "publisher.config.json",
486
+ );
487
+ writeFileSync(
488
+ publisherConfigPath,
489
+ JSON.stringify({
490
+ projects: [
491
+ {
492
+ name: projectName,
493
+ packages: [
494
+ {
495
+ name: projectName,
496
+ location: projectPath,
497
+ },
498
+ ],
499
+ },
500
+ ],
501
+ }),
502
+ );
503
+
504
+ // Get the project successfully
505
+ const project = await projectStore.getProject(projectName);
506
+ expect(project).toBeInstanceOf(Project);
507
+
508
+ // Try to get a non-existent project
509
+ await expect(
510
+ projectStore.getProject("non-existent"),
511
+ ).rejects.toThrow();
512
+
513
+ // Verify the original project is still accessible
514
+ const projectAgain = await projectStore.getProject(projectName);
515
+ expect(projectAgain).toBeInstanceOf(Project);
516
+ expect(projectAgain.metadata.name).toBe(projectName);
517
+ });
199
518
  });
200
519
  });