@malloy-publisher/server 0.0.91 → 0.0.92

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