@saluzi/saluzi-edu 0.2.9 → 0.2.11

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 (219) hide show
  1. package/dist/cli.js +336 -333
  2. package/dist/rcs-web/assets/Dashboard-BwotBWlJ.js +1 -0
  3. package/dist/rcs-web/assets/Join-4yrksbdX.js +1 -0
  4. package/dist/rcs-web/assets/Login-Cxm_yEeE.js +1 -0
  5. package/dist/rcs-web/assets/SessionDetail-DNdfPOYy.js +50 -0
  6. package/dist/rcs-web/assets/Setup-BiAAxugt.js +1 -0
  7. package/dist/rcs-web/assets/ShareLanding-CP3Z6ckr.js +1 -0
  8. package/dist/rcs-web/assets/TeamCreate-DgdCwRSb.js +1 -0
  9. package/dist/rcs-web/assets/TeamDetail-CkNTFoMB.js +11 -0
  10. package/dist/rcs-web/assets/TeamList-Bf0sQ5RQ.js +1 -0
  11. package/dist/rcs-web/assets/TeamSettings-BaNAfXU3.js +1 -0
  12. package/dist/rcs-web/assets/UserSettings-CsutQjxG.js +1 -0
  13. package/{packages/remote-control-server/web/dist/assets/arc-D90597o-.js → dist/rcs-web/assets/arc-BxxmNQO6.js} +1 -1
  14. package/dist/rcs-web/assets/architectureDiagram-3BPJPVTR-DBjkRdbd.js +36 -0
  15. package/dist/rcs-web/assets/{blockDiagram-GPEHLZMM-DHd8IbOs.js → blockDiagram-GPEHLZMM-BF6COWlM.js} +2 -2
  16. package/dist/rcs-web/assets/{c4Diagram-AAUBKEIU-CjFkQJ9h.js → c4Diagram-AAUBKEIU-DHOf390v.js} +2 -2
  17. package/dist/rcs-web/assets/channel-JsvbMQjW.js +1 -0
  18. package/dist/rcs-web/assets/{chunk-2J33WTMH-C21XWnGZ.js → chunk-2J33WTMH-CeJN8yjM.js} +1 -1
  19. package/dist/rcs-web/assets/{chunk-4BX2VUAB-B3SXiJlt.js → chunk-4BX2VUAB-jP76_P2A.js} +1 -1
  20. package/dist/rcs-web/assets/{chunk-55IACEB6-DjImv45F.js → chunk-55IACEB6-BsFA6emB.js} +1 -1
  21. package/{packages/remote-control-server/web/dist/assets/chunk-727SXJPM-DtnaqsyD.js → dist/rcs-web/assets/chunk-727SXJPM-BF4HrQjZ.js} +1 -1
  22. package/dist/rcs-web/assets/{chunk-AQP2D5EJ-BVFgH5mS.js → chunk-AQP2D5EJ-DToJ74Kc.js} +1 -1
  23. package/dist/rcs-web/assets/{chunk-FMBD7UC4-Bl8jgb57.js → chunk-FMBD7UC4-DVTARgsa.js} +1 -1
  24. package/dist/rcs-web/assets/{chunk-ND2GUHAM-BpK111zh.js → chunk-ND2GUHAM-C50Gqv9c.js} +1 -1
  25. package/dist/rcs-web/assets/{chunk-QZHKN3VN-ruhIHBim.js → chunk-QZHKN3VN-EsIIh6zV.js} +1 -1
  26. package/dist/rcs-web/assets/classDiagram-4FO5ZUOK-BXn8Uc4c.js +1 -0
  27. package/dist/rcs-web/assets/classDiagram-v2-Q7XG4LA2-BXn8Uc4c.js +1 -0
  28. package/dist/rcs-web/assets/code-block-IT6T5CEO-BnufHNTT.js +2 -0
  29. package/dist/rcs-web/assets/cose-bilkent-S5V4N54A-M6tNt14I.js +1 -0
  30. package/dist/rcs-web/assets/dagre-BM42HDAG-DaTm29qG.js +4 -0
  31. package/dist/rcs-web/assets/diagram-2AECGRRQ-fqoeOvq2.js +43 -0
  32. package/dist/rcs-web/assets/diagram-5GNKFQAL-6s2CiaGG.js +10 -0
  33. package/dist/rcs-web/assets/{diagram-KO2AKTUF-Uy3PDP5S.js → diagram-KO2AKTUF-C0aw_N95.js} +2 -2
  34. package/dist/rcs-web/assets/diagram-LMA3HP47-C8_Rv3rB.js +24 -0
  35. package/dist/rcs-web/assets/diagram-OG6HWLK6-Dae4u8Uc.js +24 -0
  36. package/{packages/remote-control-server/web/dist/assets/erDiagram-TEJ5UH35-sre_MTeK.js → dist/rcs-web/assets/erDiagram-TEJ5UH35-CosS5VWr.js} +13 -13
  37. package/dist/rcs-web/assets/{flowDiagram-I6XJVG4X-DwrWaLoe.js → flowDiagram-I6XJVG4X-D_Mxs46-.js} +2 -2
  38. package/dist/rcs-web/assets/ganttDiagram-6RSMTGT7-B0jy8Am_.js +292 -0
  39. package/dist/rcs-web/assets/gitGraphDiagram-PVQCEYII-CSdu2HVI.js +106 -0
  40. package/dist/rcs-web/assets/i18n-Dbt-vEQ3.js +1 -0
  41. package/dist/rcs-web/assets/infoDiagram-5YYISTIA-Dcbc9L1S.js +2 -0
  42. package/{packages/remote-control-server/web/dist/assets/ishikawaDiagram-YF4QCWOH-SO0nn3--.js → dist/rcs-web/assets/ishikawaDiagram-YF4QCWOH-BLMjFE4t.js} +2 -2
  43. package/dist/rcs-web/assets/{journeyDiagram-JHISSGLW-D4igFUd4.js → journeyDiagram-JHISSGLW-Dw4nh1xI.js} +2 -2
  44. package/dist/rcs-web/assets/{kanban-definition-UN3LZRKU-Cm_yKsZC.js → kanban-definition-UN3LZRKU-DkU3j_ly.js} +2 -2
  45. package/dist/rcs-web/assets/{linear-4IMGll_E.js → linear-N5Cify7M.js} +1 -1
  46. package/dist/rcs-web/assets/main-30YqUvTd.js +238 -0
  47. package/dist/rcs-web/assets/main-EP6Lac2m.css +1 -0
  48. package/{packages/remote-control-server/web/dist/assets/mermaid-VLURNSYL-D_yQ7nAd.js → dist/rcs-web/assets/mermaid-VLURNSYL-gfiJUpl_.js} +5 -5
  49. package/dist/rcs-web/assets/{mermaid.core-B1qOUdiU.js → mermaid.core-B_Xc65Bf.js} +5 -5
  50. package/dist/rcs-web/assets/{mindmap-definition-RKZ34NQL-yETom5qt.js → mindmap-definition-RKZ34NQL-DUavHfNu.js} +2 -2
  51. package/{packages/remote-control-server/web/dist/assets/pieDiagram-4H26LBE5-KIhqXGT6.js → dist/rcs-web/assets/pieDiagram-4H26LBE5-DSJaToTU.js} +2 -2
  52. package/{packages/remote-control-server/web/dist/assets/quadrantDiagram-W4KKPZXB-DISqmoA6.js → dist/rcs-web/assets/quadrantDiagram-W4KKPZXB-BFp6TnHj.js} +2 -2
  53. package/dist/rcs-web/assets/{requirementDiagram-4Y6WPE33-DsgT4ptk.js → requirementDiagram-4Y6WPE33-eil6F3-O.js} +2 -2
  54. package/{packages/remote-control-server/web/dist/assets/sankeyDiagram-5OEKKPKP-Dn67kbn0.js → dist/rcs-web/assets/sankeyDiagram-5OEKKPKP-DbGhoepK.js} +2 -2
  55. package/{packages/remote-control-server/web/dist/assets/sequenceDiagram-3UESZ5HK-CtWaXZHl.js → dist/rcs-web/assets/sequenceDiagram-3UESZ5HK-B0YrN1ud.js} +14 -14
  56. package/dist/rcs-web/assets/stateDiagram-AJRCARHV-CofFPj3N.js +1 -0
  57. package/dist/rcs-web/assets/stateDiagram-v2-BHNVJYJU-DPlcvPn1.js +1 -0
  58. package/{packages/remote-control-server/web/dist/assets/timeline-definition-PNZ67QCA-Dk5WUenC.js → dist/rcs-web/assets/timeline-definition-PNZ67QCA-Bx4dusy8.js} +2 -2
  59. package/dist/rcs-web/assets/useTeams-MDI4djXg.js +1 -0
  60. package/{packages/remote-control-server/web/dist/assets/vennDiagram-CIIHVFJN-TY8EiRrC.js → dist/rcs-web/assets/vennDiagram-CIIHVFJN-B_-3l-MF.js} +2 -2
  61. package/dist/rcs-web/assets/{wardleyDiagram-YWT4CUSO-BK3oi47X.js → wardleyDiagram-YWT4CUSO-DNSn-pGp.js} +2 -2
  62. package/dist/rcs-web/assets/workerType-OM09y7Lk.js +1 -0
  63. package/{packages/remote-control-server/web/dist/assets/xychartDiagram-2RQKCTM6-DhsxqX67.js → dist/rcs-web/assets/xychartDiagram-2RQKCTM6-TqUbNp0j.js} +2 -2
  64. package/dist/rcs-web/index.html +2 -3
  65. package/package.json +2 -1
  66. package/packages/remote-control-server/src/__tests__/acp-ws-handler.test.ts +7 -19
  67. package/packages/remote-control-server/src/__tests__/auth-login.test.ts +561 -0
  68. package/packages/remote-control-server/src/__tests__/auth-security.test.ts +547 -0
  69. package/packages/remote-control-server/src/__tests__/auth.test.ts +7 -18
  70. package/packages/remote-control-server/src/__tests__/bind-replacement.test.ts +251 -0
  71. package/packages/remote-control-server/src/__tests__/cookie.test.ts +155 -0
  72. package/packages/remote-control-server/src/__tests__/data-layer-fixes.test.ts +147 -0
  73. package/packages/remote-control-server/src/__tests__/db.test.ts +582 -0
  74. package/packages/remote-control-server/src/__tests__/design-gaps.test.ts +440 -0
  75. package/packages/remote-control-server/src/__tests__/disconnect-monitor.test.ts +2 -18
  76. package/packages/remote-control-server/src/__tests__/helpers/db.ts +43 -0
  77. package/packages/remote-control-server/src/__tests__/helpers/fixtures.ts +94 -0
  78. package/packages/remote-control-server/src/__tests__/helpers/mock-config.ts +31 -0
  79. package/packages/remote-control-server/src/__tests__/middleware.test.ts +4 -112
  80. package/packages/remote-control-server/src/__tests__/password.test.ts +76 -0
  81. package/packages/remote-control-server/src/__tests__/phase4-cleanup.test.ts +290 -0
  82. package/packages/remote-control-server/src/__tests__/phase4-integration.test.ts +224 -0
  83. package/packages/remote-control-server/src/__tests__/routes.test.ts +292 -207
  84. package/packages/remote-control-server/src/__tests__/services.test.ts +2 -18
  85. package/packages/remote-control-server/src/__tests__/session-visibility.test.ts +452 -0
  86. package/packages/remote-control-server/src/__tests__/session.test.ts +216 -0
  87. package/packages/remote-control-server/src/__tests__/shares.test.ts +1419 -0
  88. package/packages/remote-control-server/src/__tests__/sse-writer.test.ts +2 -18
  89. package/packages/remote-control-server/src/__tests__/teams.test.ts +2089 -0
  90. package/packages/remote-control-server/src/__tests__/work-dispatch.test.ts +2 -18
  91. package/packages/remote-control-server/src/__tests__/worker-token-removal.test.ts +487 -0
  92. package/packages/remote-control-server/src/__tests__/ws-handler.test.ts +2 -18
  93. package/packages/remote-control-server/src/auth/constants.ts +13 -0
  94. package/packages/remote-control-server/src/auth/cookie.ts +75 -0
  95. package/packages/remote-control-server/src/auth/middleware.ts +75 -165
  96. package/packages/remote-control-server/src/auth/password.ts +116 -0
  97. package/packages/remote-control-server/src/auth/session.ts +130 -0
  98. package/packages/remote-control-server/src/db/schema.sql +116 -0
  99. package/packages/remote-control-server/src/db/sqlite.ts +99 -0
  100. package/packages/remote-control-server/src/routes/acp/index.ts +6 -11
  101. package/packages/remote-control-server/src/routes/v1/environments.ts +111 -8
  102. package/packages/remote-control-server/src/routes/web/auth-routes.ts +634 -0
  103. package/packages/remote-control-server/src/routes/web/control.ts +5 -5
  104. package/packages/remote-control-server/src/routes/web/environments.ts +5 -27
  105. package/packages/remote-control-server/src/routes/web/sessions.ts +19 -41
  106. package/packages/remote-control-server/src/routes/web/shares.ts +371 -0
  107. package/packages/remote-control-server/src/routes/web/teams.ts +850 -0
  108. package/packages/remote-control-server/src/services/environment.ts +1 -4
  109. package/packages/remote-control-server/src/services/session-visibility.ts +79 -0
  110. package/packages/remote-control-server/src/services/session.ts +0 -13
  111. package/packages/remote-control-server/src/store.ts +150 -56
  112. package/packages/remote-control-server/src/types/api.ts +2 -8
  113. package/packages/remote-control-server/web/dist/assets/Dashboard-BwotBWlJ.js +1 -0
  114. package/packages/remote-control-server/web/dist/assets/Join-4yrksbdX.js +1 -0
  115. package/packages/remote-control-server/web/dist/assets/Login-Cxm_yEeE.js +1 -0
  116. package/packages/remote-control-server/web/dist/assets/SessionDetail-DNdfPOYy.js +50 -0
  117. package/packages/remote-control-server/web/dist/assets/Setup-BiAAxugt.js +1 -0
  118. package/packages/remote-control-server/web/dist/assets/ShareLanding-CP3Z6ckr.js +1 -0
  119. package/packages/remote-control-server/web/dist/assets/TeamCreate-DgdCwRSb.js +1 -0
  120. package/packages/remote-control-server/web/dist/assets/TeamDetail-CkNTFoMB.js +11 -0
  121. package/packages/remote-control-server/web/dist/assets/TeamList-Bf0sQ5RQ.js +1 -0
  122. package/packages/remote-control-server/web/dist/assets/TeamSettings-BaNAfXU3.js +1 -0
  123. package/packages/remote-control-server/web/dist/assets/UserSettings-CsutQjxG.js +1 -0
  124. package/{dist/rcs-web/assets/arc-D90597o-.js → packages/remote-control-server/web/dist/assets/arc-BxxmNQO6.js} +1 -1
  125. package/packages/remote-control-server/web/dist/assets/architectureDiagram-3BPJPVTR-DBjkRdbd.js +36 -0
  126. package/packages/remote-control-server/web/dist/assets/{blockDiagram-GPEHLZMM-DHd8IbOs.js → blockDiagram-GPEHLZMM-BF6COWlM.js} +2 -2
  127. package/packages/remote-control-server/web/dist/assets/{c4Diagram-AAUBKEIU-CjFkQJ9h.js → c4Diagram-AAUBKEIU-DHOf390v.js} +2 -2
  128. package/packages/remote-control-server/web/dist/assets/channel-JsvbMQjW.js +1 -0
  129. package/packages/remote-control-server/web/dist/assets/{chunk-2J33WTMH-C21XWnGZ.js → chunk-2J33WTMH-CeJN8yjM.js} +1 -1
  130. package/packages/remote-control-server/web/dist/assets/{chunk-4BX2VUAB-B3SXiJlt.js → chunk-4BX2VUAB-jP76_P2A.js} +1 -1
  131. package/packages/remote-control-server/web/dist/assets/{chunk-55IACEB6-DjImv45F.js → chunk-55IACEB6-BsFA6emB.js} +1 -1
  132. package/{dist/rcs-web/assets/chunk-727SXJPM-DtnaqsyD.js → packages/remote-control-server/web/dist/assets/chunk-727SXJPM-BF4HrQjZ.js} +1 -1
  133. package/packages/remote-control-server/web/dist/assets/{chunk-AQP2D5EJ-BVFgH5mS.js → chunk-AQP2D5EJ-DToJ74Kc.js} +1 -1
  134. package/packages/remote-control-server/web/dist/assets/{chunk-FMBD7UC4-Bl8jgb57.js → chunk-FMBD7UC4-DVTARgsa.js} +1 -1
  135. package/packages/remote-control-server/web/dist/assets/{chunk-ND2GUHAM-BpK111zh.js → chunk-ND2GUHAM-C50Gqv9c.js} +1 -1
  136. package/packages/remote-control-server/web/dist/assets/{chunk-QZHKN3VN-ruhIHBim.js → chunk-QZHKN3VN-EsIIh6zV.js} +1 -1
  137. package/packages/remote-control-server/web/dist/assets/classDiagram-4FO5ZUOK-BXn8Uc4c.js +1 -0
  138. package/packages/remote-control-server/web/dist/assets/classDiagram-v2-Q7XG4LA2-BXn8Uc4c.js +1 -0
  139. package/packages/remote-control-server/web/dist/assets/code-block-IT6T5CEO-BnufHNTT.js +2 -0
  140. package/packages/remote-control-server/web/dist/assets/cose-bilkent-S5V4N54A-M6tNt14I.js +1 -0
  141. package/packages/remote-control-server/web/dist/assets/dagre-BM42HDAG-DaTm29qG.js +4 -0
  142. package/packages/remote-control-server/web/dist/assets/diagram-2AECGRRQ-fqoeOvq2.js +43 -0
  143. package/packages/remote-control-server/web/dist/assets/diagram-5GNKFQAL-6s2CiaGG.js +10 -0
  144. package/packages/remote-control-server/web/dist/assets/{diagram-KO2AKTUF-Uy3PDP5S.js → diagram-KO2AKTUF-C0aw_N95.js} +2 -2
  145. package/packages/remote-control-server/web/dist/assets/diagram-LMA3HP47-C8_Rv3rB.js +24 -0
  146. package/packages/remote-control-server/web/dist/assets/diagram-OG6HWLK6-Dae4u8Uc.js +24 -0
  147. package/{dist/rcs-web/assets/erDiagram-TEJ5UH35-sre_MTeK.js → packages/remote-control-server/web/dist/assets/erDiagram-TEJ5UH35-CosS5VWr.js} +13 -13
  148. package/packages/remote-control-server/web/dist/assets/{flowDiagram-I6XJVG4X-DwrWaLoe.js → flowDiagram-I6XJVG4X-D_Mxs46-.js} +2 -2
  149. package/packages/remote-control-server/web/dist/assets/ganttDiagram-6RSMTGT7-B0jy8Am_.js +292 -0
  150. package/packages/remote-control-server/web/dist/assets/gitGraphDiagram-PVQCEYII-CSdu2HVI.js +106 -0
  151. package/packages/remote-control-server/web/dist/assets/i18n-Dbt-vEQ3.js +1 -0
  152. package/packages/remote-control-server/web/dist/assets/infoDiagram-5YYISTIA-Dcbc9L1S.js +2 -0
  153. package/{dist/rcs-web/assets/ishikawaDiagram-YF4QCWOH-SO0nn3--.js → packages/remote-control-server/web/dist/assets/ishikawaDiagram-YF4QCWOH-BLMjFE4t.js} +2 -2
  154. package/packages/remote-control-server/web/dist/assets/{journeyDiagram-JHISSGLW-D4igFUd4.js → journeyDiagram-JHISSGLW-Dw4nh1xI.js} +2 -2
  155. package/packages/remote-control-server/web/dist/assets/{kanban-definition-UN3LZRKU-Cm_yKsZC.js → kanban-definition-UN3LZRKU-DkU3j_ly.js} +2 -2
  156. package/packages/remote-control-server/web/dist/assets/{linear-4IMGll_E.js → linear-N5Cify7M.js} +1 -1
  157. package/packages/remote-control-server/web/dist/assets/main-30YqUvTd.js +238 -0
  158. package/packages/remote-control-server/web/dist/assets/main-EP6Lac2m.css +1 -0
  159. package/{dist/rcs-web/assets/mermaid-VLURNSYL-D_yQ7nAd.js → packages/remote-control-server/web/dist/assets/mermaid-VLURNSYL-gfiJUpl_.js} +5 -5
  160. package/packages/remote-control-server/web/dist/assets/{mermaid.core-B1qOUdiU.js → mermaid.core-B_Xc65Bf.js} +5 -5
  161. package/packages/remote-control-server/web/dist/assets/{mindmap-definition-RKZ34NQL-yETom5qt.js → mindmap-definition-RKZ34NQL-DUavHfNu.js} +2 -2
  162. package/{dist/rcs-web/assets/pieDiagram-4H26LBE5-KIhqXGT6.js → packages/remote-control-server/web/dist/assets/pieDiagram-4H26LBE5-DSJaToTU.js} +2 -2
  163. package/{dist/rcs-web/assets/quadrantDiagram-W4KKPZXB-DISqmoA6.js → packages/remote-control-server/web/dist/assets/quadrantDiagram-W4KKPZXB-BFp6TnHj.js} +2 -2
  164. package/packages/remote-control-server/web/dist/assets/{requirementDiagram-4Y6WPE33-DsgT4ptk.js → requirementDiagram-4Y6WPE33-eil6F3-O.js} +2 -2
  165. package/{dist/rcs-web/assets/sankeyDiagram-5OEKKPKP-Dn67kbn0.js → packages/remote-control-server/web/dist/assets/sankeyDiagram-5OEKKPKP-DbGhoepK.js} +2 -2
  166. package/{dist/rcs-web/assets/sequenceDiagram-3UESZ5HK-CtWaXZHl.js → packages/remote-control-server/web/dist/assets/sequenceDiagram-3UESZ5HK-B0YrN1ud.js} +14 -14
  167. package/packages/remote-control-server/web/dist/assets/stateDiagram-AJRCARHV-CofFPj3N.js +1 -0
  168. package/packages/remote-control-server/web/dist/assets/stateDiagram-v2-BHNVJYJU-DPlcvPn1.js +1 -0
  169. package/{dist/rcs-web/assets/timeline-definition-PNZ67QCA-Dk5WUenC.js → packages/remote-control-server/web/dist/assets/timeline-definition-PNZ67QCA-Bx4dusy8.js} +2 -2
  170. package/packages/remote-control-server/web/dist/assets/useTeams-MDI4djXg.js +1 -0
  171. package/{dist/rcs-web/assets/vennDiagram-CIIHVFJN-TY8EiRrC.js → packages/remote-control-server/web/dist/assets/vennDiagram-CIIHVFJN-B_-3l-MF.js} +2 -2
  172. package/packages/remote-control-server/web/dist/assets/{wardleyDiagram-YWT4CUSO-BK3oi47X.js → wardleyDiagram-YWT4CUSO-DNSn-pGp.js} +2 -2
  173. package/packages/remote-control-server/web/dist/assets/workerType-OM09y7Lk.js +1 -0
  174. package/{dist/rcs-web/assets/xychartDiagram-2RQKCTM6-DhsxqX67.js → packages/remote-control-server/web/dist/assets/xychartDiagram-2RQKCTM6-TqUbNp0j.js} +2 -2
  175. package/packages/remote-control-server/web/dist/index.html +2 -3
  176. package/dist/rcs-web/assets/Dashboard-Cyj-Bb86.js +0 -2
  177. package/dist/rcs-web/assets/SessionDetail-SA9QBkyj.js +0 -51
  178. package/dist/rcs-web/assets/architectureDiagram-3BPJPVTR-CG9q9_bk.js +0 -36
  179. package/dist/rcs-web/assets/channel-CQFGglg-.js +0 -1
  180. package/dist/rcs-web/assets/classDiagram-4FO5ZUOK-C5rBdShD.js +0 -1
  181. package/dist/rcs-web/assets/classDiagram-v2-Q7XG4LA2-C5rBdShD.js +0 -1
  182. package/dist/rcs-web/assets/code-block-IT6T5CEO-BLGFrBw3.js +0 -2
  183. package/dist/rcs-web/assets/cose-bilkent-S5V4N54A-DBKEXjqM.js +0 -1
  184. package/dist/rcs-web/assets/dagre-BM42HDAG-DB_SWlAe.js +0 -4
  185. package/dist/rcs-web/assets/diagram-2AECGRRQ-Gbm8G3Xz.js +0 -43
  186. package/dist/rcs-web/assets/diagram-5GNKFQAL-BBuWqfDc.js +0 -10
  187. package/dist/rcs-web/assets/diagram-LMA3HP47-DyUwNrlg.js +0 -24
  188. package/dist/rcs-web/assets/diagram-OG6HWLK6-BXpvRwkJ.js +0 -24
  189. package/dist/rcs-web/assets/ganttDiagram-6RSMTGT7-ByDYviDl.js +0 -292
  190. package/dist/rcs-web/assets/gitGraphDiagram-PVQCEYII-BUnOPw74.js +0 -106
  191. package/dist/rcs-web/assets/infoDiagram-5YYISTIA-B1RpwJHG.js +0 -2
  192. package/dist/rcs-web/assets/main-0A4a3FDU.css +0 -1
  193. package/dist/rcs-web/assets/main-Dom16YJk.js +0 -283
  194. package/dist/rcs-web/assets/qr-C0jP9HcD.js +0 -8
  195. package/dist/rcs-web/assets/qr-scanner-worker.min-D85Z9gVD.js +0 -98
  196. package/dist/rcs-web/assets/stateDiagram-AJRCARHV-D6xp64f3.js +0 -1
  197. package/dist/rcs-web/assets/stateDiagram-v2-BHNVJYJU-D76vZCyQ.js +0 -1
  198. package/packages/remote-control-server/web/dist/assets/Dashboard-Cyj-Bb86.js +0 -2
  199. package/packages/remote-control-server/web/dist/assets/SessionDetail-SA9QBkyj.js +0 -51
  200. package/packages/remote-control-server/web/dist/assets/architectureDiagram-3BPJPVTR-CG9q9_bk.js +0 -36
  201. package/packages/remote-control-server/web/dist/assets/channel-CQFGglg-.js +0 -1
  202. package/packages/remote-control-server/web/dist/assets/classDiagram-4FO5ZUOK-C5rBdShD.js +0 -1
  203. package/packages/remote-control-server/web/dist/assets/classDiagram-v2-Q7XG4LA2-C5rBdShD.js +0 -1
  204. package/packages/remote-control-server/web/dist/assets/code-block-IT6T5CEO-BLGFrBw3.js +0 -2
  205. package/packages/remote-control-server/web/dist/assets/cose-bilkent-S5V4N54A-DBKEXjqM.js +0 -1
  206. package/packages/remote-control-server/web/dist/assets/dagre-BM42HDAG-DB_SWlAe.js +0 -4
  207. package/packages/remote-control-server/web/dist/assets/diagram-2AECGRRQ-Gbm8G3Xz.js +0 -43
  208. package/packages/remote-control-server/web/dist/assets/diagram-5GNKFQAL-BBuWqfDc.js +0 -10
  209. package/packages/remote-control-server/web/dist/assets/diagram-LMA3HP47-DyUwNrlg.js +0 -24
  210. package/packages/remote-control-server/web/dist/assets/diagram-OG6HWLK6-BXpvRwkJ.js +0 -24
  211. package/packages/remote-control-server/web/dist/assets/ganttDiagram-6RSMTGT7-ByDYviDl.js +0 -292
  212. package/packages/remote-control-server/web/dist/assets/gitGraphDiagram-PVQCEYII-BUnOPw74.js +0 -106
  213. package/packages/remote-control-server/web/dist/assets/infoDiagram-5YYISTIA-B1RpwJHG.js +0 -2
  214. package/packages/remote-control-server/web/dist/assets/main-0A4a3FDU.css +0 -1
  215. package/packages/remote-control-server/web/dist/assets/main-Dom16YJk.js +0 -283
  216. package/packages/remote-control-server/web/dist/assets/qr-C0jP9HcD.js +0 -8
  217. package/packages/remote-control-server/web/dist/assets/qr-scanner-worker.min-D85Z9gVD.js +0 -98
  218. package/packages/remote-control-server/web/dist/assets/stateDiagram-AJRCARHV-D6xp64f3.js +0 -1
  219. package/packages/remote-control-server/web/dist/assets/stateDiagram-v2-BHNVJYJU-D76vZCyQ.js +0 -1
@@ -1,24 +1,8 @@
1
1
  import { describe, test, expect, beforeEach, mock } from 'bun:test'
2
+ import { mockConfigModule } from './helpers/mock-config'
2
3
 
3
4
  // Mock config before imports
4
- const mockConfig = {
5
- port: 3000,
6
- host: '0.0.0.0',
7
- apiKeys: ['test-api-key'],
8
- baseUrl: 'http://localhost:3000',
9
- pollTimeout: 1, // Short timeout for tests
10
- heartbeatInterval: 20,
11
- jwtExpiresIn: 3600,
12
- disconnectTimeout: 300,
13
- webCorsOrigins: [],
14
- wsIdleTimeout: 30,
15
- wsKeepaliveInterval: 20,
16
- }
17
-
18
- mock.module('../config', () => ({
19
- config: mockConfig,
20
- getBaseUrl: () => 'http://localhost:3000',
21
- }))
5
+ mock.module('../config', () => mockConfigModule({ pollTimeout: 1 })) // Short timeout for tests
22
6
 
23
7
  import {
24
8
  storeReset,
@@ -0,0 +1,487 @@
1
+ import { describe, test, expect, beforeEach, mock } from 'bun:test'
2
+ import { readFileSync } from 'node:fs'
3
+ import { resolve } from 'node:path'
4
+ import { randomUUID } from 'node:crypto'
5
+ import { tmpdir } from 'node:os'
6
+ import { mockConfigModule } from './helpers/mock-config'
7
+
8
+ // Mock config before any module imports
9
+ mock.module('../config', () => mockConfigModule())
10
+
11
+ import { Hono } from 'hono'
12
+ import { Database } from 'bun:sqlite'
13
+
14
+ // Dynamic imports — modules may or may not exist
15
+ let migrateDatabase: ((db: Database) => void) | undefined
16
+ let initDatabase: ((path?: string) => Database) | undefined
17
+ let resetDbSingleton: (() => void) | undefined
18
+ let getDb: (() => Database) | undefined
19
+
20
+ try {
21
+ const dbMod = await import('../db/sqlite')
22
+ migrateDatabase = dbMod.migrateDatabase
23
+ getDb = dbMod.getDb
24
+ initDatabase = dbMod.initDatabase
25
+ resetDbSingleton = dbMod.resetDbSingleton
26
+ } catch {
27
+ // Module not implemented yet
28
+ }
29
+
30
+ let authRoutes: Hono | undefined
31
+ try {
32
+ const mod = await import('../routes/web/auth-routes')
33
+ authRoutes = mod.default
34
+ } catch {
35
+ // Module not implemented yet
36
+ }
37
+
38
+ let sessionRoutes: Hono | undefined
39
+ try {
40
+ const mod = await import('../routes/web/sessions')
41
+ sessionRoutes = mod.default
42
+ } catch {
43
+ // Module not implemented yet
44
+ }
45
+
46
+ let controlRoutes: Hono | undefined
47
+ try {
48
+ const mod = await import('../routes/web/control')
49
+ controlRoutes = mod.default
50
+ } catch {
51
+ // Module not implemented yet
52
+ }
53
+
54
+ // ---------------------------------------------------------------------------
55
+ // Helpers
56
+ // ---------------------------------------------------------------------------
57
+
58
+ const PKG_ROOT = resolve(import.meta.dir, '../..')
59
+ const RCS_SRC = resolve(PKG_ROOT, 'src')
60
+ const BRIDGE_SRC = resolve(PKG_ROOT, '..', '..', '..', 'src', 'bridge')
61
+ const WEB_SRC = resolve(PKG_ROOT, 'web', 'src')
62
+
63
+ function readFileSafe(p: string): string | null {
64
+ try {
65
+ return readFileSync(p, 'utf-8')
66
+ } catch {
67
+ return null
68
+ }
69
+ }
70
+
71
+ function freshDb(): Database {
72
+ if (resetDbSingleton) {
73
+ try {
74
+ resetDbSingleton()
75
+ } catch {
76
+ // ignore
77
+ }
78
+ }
79
+ if (initDatabase) {
80
+ const uniquePath = resolve(
81
+ tmpdir(),
82
+ `rcs-test-${randomUUID().replace(/-/g, '').slice(0, 8)}.db`,
83
+ )
84
+ return initDatabase(uniquePath)
85
+ }
86
+ const db = new Database(':memory:')
87
+ db.exec('PRAGMA foreign_keys = ON')
88
+ if (migrateDatabase) migrateDatabase(db)
89
+ return db
90
+ }
91
+
92
+ interface TestUser {
93
+ id: string
94
+ username: string
95
+ role: string
96
+ accessToken: string
97
+ }
98
+
99
+ function createUserDirectly(
100
+ db: Database,
101
+ opts: { username: string; role: string },
102
+ ): TestUser {
103
+ const id = `usr_${randomUUID().replace(/-/g, '')}`
104
+ const now = new Date().toISOString()
105
+ const passwordHash =
106
+ '$argon2id$v=19$m=65536,t=3,p=1$dGVzdHNhbHR0ZXN0$' +
107
+ 'dGVzdGhhc2h0ZXN0aGFzaHRlc3RoYXNodGVzdGhhc2g'
108
+ try {
109
+ db.query(
110
+ `INSERT INTO users (id, username, password_hash, role, created_at)
111
+ VALUES ($id, $username, $hash, $role, $now)`,
112
+ ).run({
113
+ $id: id,
114
+ $username: opts.username,
115
+ $hash: passwordHash,
116
+ $role: opts.role,
117
+ $now: now,
118
+ })
119
+ } catch {
120
+ // table may not exist
121
+ }
122
+ return { id, username: opts.username, role: opts.role, accessToken: '' }
123
+ }
124
+
125
+ function issueTokenForUser(db: Database, userId: string): string {
126
+ try {
127
+ const { issueSessionToken } = require('../auth/session') as {
128
+ issueSessionToken: (
129
+ userId: string,
130
+ db: Database,
131
+ ) => { accessToken: string; refreshToken: string }
132
+ }
133
+ const { accessToken } = issueSessionToken(userId, db)
134
+ return accessToken
135
+ } catch {
136
+ const { createHash } =
137
+ require('node:crypto') as typeof import('node:crypto')
138
+ const token = `rct_${randomUUID().replace(/-/g, '')}`
139
+ const tokenHash = createHash('sha256').update(token).digest('hex')
140
+ const now = new Date().toISOString()
141
+ const expiresAt = new Date(Date.now() + 3600_000).toISOString()
142
+ try {
143
+ db.query(
144
+ `INSERT INTO session_tokens (token_hash, user_id, kind, expires_at, created_at)
145
+ VALUES ($hash, $userId, 'access', $exp, $now)`,
146
+ ).run({ $hash: tokenHash, $userId: userId, $exp: expiresAt, $now: now })
147
+ } catch {
148
+ // table may not exist
149
+ }
150
+ return token
151
+ }
152
+ }
153
+
154
+ async function createAndLoginUser(
155
+ db: Database,
156
+ opts: { username: string; role: string },
157
+ ): Promise<TestUser> {
158
+ const user = createUserDirectly(db, opts)
159
+ user.accessToken = issueTokenForUser(db, user.id)
160
+ return user
161
+ }
162
+
163
+ function authHeader(token: string): Record<string, string> {
164
+ return { Authorization: `Bearer ${token}` }
165
+ }
166
+
167
+ function createApp(): Hono {
168
+ const app = new Hono()
169
+ if (authRoutes) app.route('/web/auth', authRoutes)
170
+ if (sessionRoutes) app.route('/web', sessionRoutes)
171
+ if (controlRoutes) app.route('/web', controlRoutes)
172
+ return app
173
+ }
174
+
175
+ // ===========================================================================
176
+ // TESTS — RCS_WORKER_TOKENS env removed
177
+ // ===========================================================================
178
+
179
+ describe('RCS_WORKER_TOKENS env removed', () => {
180
+ let db: Database
181
+
182
+ beforeEach(() => {
183
+ db = freshDb()
184
+ })
185
+
186
+ test('EnvironmentRecord type does not contain workerTokenHash field', async () => {
187
+ // Read store.ts and check that EnvironmentRecord no longer has workerTokenHash
188
+ const storeContent = readFileSafe(resolve(RCS_SRC, 'store.ts'))
189
+ if (!storeContent) return
190
+
191
+ // Check the EnvironmentRecord interface/type definition
192
+ // After Phase 3, workerTokenHash should not appear in the type
193
+ const envRecordMatch = storeContent.match(
194
+ /(?:interface|type)\s+EnvironmentRecord[^}]*}/s,
195
+ )
196
+ if (envRecordMatch) {
197
+ expect(envRecordMatch[0]).not.toContain('workerTokenHash')
198
+ }
199
+ })
200
+
201
+ test('storeCreateEnvironment does not accept workerTokenHash parameter', async () => {
202
+ const storeContent = readFileSafe(resolve(RCS_SRC, 'store.ts'))
203
+ if (!storeContent) return
204
+
205
+ // After Phase 3, the StoreEnvironmentRequest type should not include workerTokenHash
206
+ const reqTypeMatch = storeContent.match(
207
+ /(?:interface|type)\s+Store\w*Request[^}]*}/s,
208
+ )
209
+ if (reqTypeMatch) {
210
+ expect(reqTypeMatch[0]).not.toContain('workerTokenHash')
211
+ }
212
+
213
+ // Also check the function signature doesn't reference workerTokenHash
214
+ const createFnMatch = storeContent.match(
215
+ /function\s+storeCreateEnvironment[^{]*\{[^}]*\}/s,
216
+ )
217
+ if (createFnMatch) {
218
+ expect(createFnMatch[0]).not.toContain('workerTokenHash')
219
+ }
220
+ })
221
+ })
222
+
223
+ // ===========================================================================
224
+ // TESTS — requireWorkerToken middleware removed
225
+ // ===========================================================================
226
+
227
+ describe('requireWorkerToken middleware removed', () => {
228
+ test('requireWorkerToken does not exist in auth/middleware.ts', () => {
229
+ const middlewareContent = readFileSafe(
230
+ resolve(RCS_SRC, 'auth', 'middleware.ts'),
231
+ )
232
+ if (!middlewareContent) return
233
+ // After Phase 3, requireWorkerToken should not be exported
234
+ const hasExport =
235
+ /export\s+(?:async\s+)?function\s+requireWorkerToken/.test(
236
+ middlewareContent,
237
+ )
238
+ expect(hasExport).toBe(false)
239
+ })
240
+
241
+ test('verifyWorkerTokenForEnv does not exist in auth/middleware.ts', () => {
242
+ const middlewareContent = readFileSafe(
243
+ resolve(RCS_SRC, 'auth', 'middleware.ts'),
244
+ )
245
+ if (!middlewareContent) return
246
+ const hasExport =
247
+ /export\s+(?:async\s+)?function\s+verifyWorkerTokenForEnv/.test(
248
+ middlewareContent,
249
+ )
250
+ expect(hasExport).toBe(false)
251
+ })
252
+
253
+ test('POST /web/sessions/:id/events does not require X-Worker-Token', async () => {
254
+ const app = createApp()
255
+ const db = freshDb()
256
+ const user = await createAndLoginUser(db, {
257
+ username: 'worker-test',
258
+ role: 'member',
259
+ })
260
+
261
+ const sessionId = `ses_${randomUUID().replace(/-/g, '')}`
262
+ try {
263
+ const now = new Date().toISOString()
264
+ db.query(
265
+ `INSERT INTO sessions (id, created_at, updated_at) VALUES ($id, $now, $now)`,
266
+ ).run({ $id: sessionId, $now: now })
267
+ db.query(
268
+ `INSERT INTO session_owners (session_id, owner_type, owner_id) VALUES ($sid, 'user', $uid)`,
269
+ ).run({ $sid: sessionId, $uid: user.id })
270
+ } catch {
271
+ // tables may not exist
272
+ }
273
+
274
+ // Access without X-Worker-Token — should not get a worker_token_required error
275
+ const res = await app.request(`/web/sessions/${sessionId}/events`, {
276
+ method: 'POST',
277
+ headers: {
278
+ 'Content-Type': 'application/json',
279
+ ...authHeader(user.accessToken),
280
+ },
281
+ body: JSON.stringify({ type: 'message', content: 'test' }),
282
+ })
283
+
284
+ // Should NOT return worker_token_required error
285
+ if (res.status === 401) {
286
+ const body = (await res.json()) as Record<string, unknown>
287
+ const errorType =
288
+ (body.error as Record<string, unknown>)?.type || body.type
289
+ expect(errorType).not.toBe('worker_token_required')
290
+ }
291
+ })
292
+
293
+ test('POST /web/sessions/:id/control does not require X-Worker-Token', async () => {
294
+ const app = createApp()
295
+ const db = freshDb()
296
+ const user = await createAndLoginUser(db, {
297
+ username: 'control-test',
298
+ role: 'member',
299
+ })
300
+
301
+ const sessionId = `ses_${randomUUID().replace(/-/g, '')}`
302
+ try {
303
+ const now = new Date().toISOString()
304
+ db.query(
305
+ `INSERT INTO sessions (id, created_at, updated_at) VALUES ($id, $now, $now)`,
306
+ ).run({ $id: sessionId, $now: now })
307
+ db.query(
308
+ `INSERT INTO session_owners (session_id, owner_type, owner_id) VALUES ($sid, 'user', $uid)`,
309
+ ).run({ $sid: sessionId, $uid: user.id })
310
+ } catch {
311
+ // tables may not exist
312
+ }
313
+
314
+ const res = await app.request(`/web/sessions/${sessionId}/control`, {
315
+ method: 'POST',
316
+ headers: {
317
+ 'Content-Type': 'application/json',
318
+ ...authHeader(user.accessToken),
319
+ },
320
+ body: JSON.stringify({ action: 'interrupt' }),
321
+ })
322
+
323
+ if (res.status === 401) {
324
+ const body = (await res.json()) as Record<string, unknown>
325
+ const errorType =
326
+ (body.error as Record<string, unknown>)?.type || body.type
327
+ expect(errorType).not.toBe('worker_token_required')
328
+ }
329
+ })
330
+
331
+ test('POST /web/sessions/:id/interrupt does not require X-Worker-Token', async () => {
332
+ const app = createApp()
333
+ const db = freshDb()
334
+ const user = await createAndLoginUser(db, {
335
+ username: 'interrupt-test',
336
+ role: 'member',
337
+ })
338
+
339
+ const sessionId = `ses_${randomUUID().replace(/-/g, '')}`
340
+ try {
341
+ const now = new Date().toISOString()
342
+ db.query(
343
+ `INSERT INTO sessions (id, created_at, updated_at) VALUES ($id, $now, $now)`,
344
+ ).run({ $id: sessionId, $now: now })
345
+ db.query(
346
+ `INSERT INTO session_owners (session_id, owner_type, owner_id) VALUES ($sid, 'user', $uid)`,
347
+ ).run({ $sid: sessionId, $uid: user.id })
348
+ } catch {
349
+ // tables may not exist
350
+ }
351
+
352
+ const res = await app.request(`/web/sessions/${sessionId}/interrupt`, {
353
+ method: 'POST',
354
+ headers: {
355
+ 'Content-Type': 'application/json',
356
+ ...authHeader(user.accessToken),
357
+ },
358
+ body: JSON.stringify({}),
359
+ })
360
+
361
+ if (res.status === 401) {
362
+ const body = (await res.json()) as Record<string, unknown>
363
+ const errorType =
364
+ (body.error as Record<string, unknown>)?.type || body.type
365
+ expect(errorType).not.toBe('worker_token_required')
366
+ }
367
+ })
368
+
369
+ test('existing routes do not call worker token verification', () => {
370
+ // Check sessions.ts does not import or use requireWorkerToken/verifyWorkerTokenForEnv
371
+ const sessionsContent = readFileSafe(
372
+ resolve(RCS_SRC, 'routes', 'web', 'sessions.ts'),
373
+ )
374
+ if (sessionsContent) {
375
+ expect(sessionsContent).not.toContain('requireWorkerToken')
376
+ expect(sessionsContent).not.toContain('verifyWorkerTokenForEnv')
377
+ }
378
+
379
+ // Check control.ts does not import or use them either
380
+ const controlContent = readFileSafe(
381
+ resolve(RCS_SRC, 'routes', 'web', 'control.ts'),
382
+ )
383
+ if (controlContent) {
384
+ expect(controlContent).not.toContain('requireWorkerToken')
385
+ expect(controlContent).not.toContain('verifyWorkerTokenForEnv')
386
+ }
387
+ })
388
+ })
389
+
390
+ // ===========================================================================
391
+ // TESTS — worker token related code removed
392
+ // ===========================================================================
393
+
394
+ describe('worker token related code removed', () => {
395
+ test('src/bridge/bridgeConfig.ts does not read RCS_WORKER_TOKENS', () => {
396
+ const bridgeConfigContent = readFileSafe(
397
+ resolve(BRIDGE_SRC, 'bridgeConfig.ts'),
398
+ )
399
+ if (!bridgeConfigContent) return
400
+ expect(bridgeConfigContent).not.toContain('RCS_WORKER_TOKENS')
401
+ })
402
+
403
+ test('src/bridge/bridgeConfig.ts does not fall back to SALUZI_BRIDGE_OAUTH_TOKEN as worker token', () => {
404
+ const bridgeConfigContent = readFileSafe(
405
+ resolve(BRIDGE_SRC, 'bridgeConfig.ts'),
406
+ )
407
+ if (!bridgeConfigContent) return
408
+ // getWorkerToken() should be removed entirely, or at least not reference
409
+ // SALUZI_BRIDGE_OAUTH_TOKEN as a worker token fallback
410
+ const hasWorkerTokenFn = /function\s+getWorkerToken\b/.test(
411
+ bridgeConfigContent,
412
+ )
413
+ if (hasWorkerTokenFn) {
414
+ // If the function still exists, it should not reference SALUZI_BRIDGE_OAUTH_TOKEN
415
+ // as a worker token fallback
416
+ const fnBody = bridgeConfigContent.match(
417
+ /function\s+getWorkerToken\b[^}]*\}/s,
418
+ )
419
+ if (fnBody) {
420
+ expect(fnBody[0]).not.toContain('SALUZI_BRIDGE_OAUTH_TOKEN')
421
+ }
422
+ }
423
+ })
424
+
425
+ test('Web UI client.ts does not send X-Worker-Token header', () => {
426
+ const clientContent = readFileSafe(resolve(WEB_SRC, 'api', 'client.ts'))
427
+ if (!clientContent) return
428
+ expect(clientContent).not.toContain('X-Worker-Token')
429
+ })
430
+
431
+ test('Web UI no longer has WorkerTokenPrompt or it is not referenced', () => {
432
+ // Check if WorkerTokenPrompt.tsx exists and is referenced
433
+ const workerTokenPromptPath = resolve(
434
+ WEB_SRC,
435
+ 'components',
436
+ 'WorkerTokenPrompt.tsx',
437
+ )
438
+ const promptContent = readFileSafe(workerTokenPromptPath)
439
+
440
+ if (promptContent) {
441
+ // File exists — check it's not imported anywhere
442
+ const pagesDir = resolve(WEB_SRC, 'pages')
443
+ const dashboardContent = readFileSafe(resolve(pagesDir, 'Dashboard.tsx'))
444
+ const sessionDetailContent = readFileSafe(
445
+ resolve(pagesDir, 'SessionDetail.tsx'),
446
+ )
447
+
448
+ if (dashboardContent) {
449
+ expect(dashboardContent).not.toContain('WorkerTokenPrompt')
450
+ }
451
+ if (sessionDetailContent) {
452
+ expect(sessionDetailContent).not.toContain('WorkerTokenPrompt')
453
+ }
454
+ }
455
+ // If file doesn't exist, test passes
456
+ })
457
+
458
+ test('useWorkerTokens hook is not imported in active components', () => {
459
+ // Check if useWorkerTokens is still referenced in active components
460
+ const dashboardContent = readFileSafe(
461
+ resolve(WEB_SRC, 'pages', 'Dashboard.tsx'),
462
+ )
463
+ const sessionDetailContent = readFileSafe(
464
+ resolve(WEB_SRC, 'pages', 'SessionDetail.tsx'),
465
+ )
466
+
467
+ if (dashboardContent) {
468
+ expect(dashboardContent).not.toMatch(/import.*useWorkerTokens/)
469
+ }
470
+ if (sessionDetailContent) {
471
+ expect(sessionDetailContent).not.toMatch(/import.*useWorkerTokens/)
472
+ }
473
+ })
474
+
475
+ test('getWorkerToken and getWorkerTokenHash are removed from bridgeConfig', () => {
476
+ const bridgeConfigContent = readFileSafe(
477
+ resolve(BRIDGE_SRC, 'bridgeConfig.ts'),
478
+ )
479
+ if (!bridgeConfigContent) return
480
+ expect(bridgeConfigContent).not.toMatch(
481
+ /export\s+function\s+getWorkerToken\b/,
482
+ )
483
+ expect(bridgeConfigContent).not.toMatch(
484
+ /export\s+function\s+getWorkerTokenHash\b/,
485
+ )
486
+ })
487
+ })
@@ -1,24 +1,8 @@
1
1
  import { describe, test, expect, beforeEach, mock } from 'bun:test'
2
+ import { mockConfigModule } from './helpers/mock-config'
2
3
 
3
4
  // Mock config before imports
4
- const mockConfig = {
5
- port: 3000,
6
- host: '0.0.0.0',
7
- apiKeys: ['test-api-key'],
8
- baseUrl: 'http://localhost:3000',
9
- pollTimeout: 8,
10
- heartbeatInterval: 20,
11
- jwtExpiresIn: 3600,
12
- disconnectTimeout: 300,
13
- webCorsOrigins: [],
14
- wsIdleTimeout: 30,
15
- wsKeepaliveInterval: 20,
16
- }
17
-
18
- mock.module('../config', () => ({
19
- config: mockConfig,
20
- getBaseUrl: () => 'http://localhost:3000',
21
- }))
5
+ mock.module('../config', () => mockConfigModule())
22
6
 
23
7
  import { storeReset } from '../store'
24
8
  import {
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Identity constants for the RCS auth system.
3
+ *
4
+ * SYSTEM_USER_ID is the identity assigned to API-key-authenticated operations.
5
+ * These operations are not attributable to any specific human user.
6
+ *
7
+ * ADMIN_FALLBACK_USER_ID is kept only for backward compatibility in code paths
8
+ * that previously relied on the literal string. New code should use SYSTEM_USER_ID.
9
+ */
10
+ export const SYSTEM_USER_ID = '__system__'
11
+
12
+ /** @deprecated Use SYSTEM_USER_ID for new code. Kept for backward compat only. */
13
+ export const ADMIN_FALLBACK_USER_ID = '__admin__'
@@ -0,0 +1,75 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto'
2
+
3
+ /**
4
+ * Sign cookie data with HMAC-SHA256.
5
+ * Returns `data.signature` format.
6
+ *
7
+ * Note: Currently unused by production code — token verification relies on
8
+ * database lookup. Kept as utility for potential future use.
9
+ */
10
+ export function signCookie(data: string, secret: string): string {
11
+ const sig = createHmac('sha256', secret).update(data).digest('hex')
12
+ return `${data}.${sig}`
13
+ }
14
+
15
+ /**
16
+ * Parse and verify a signed cookie value.
17
+ * Returns the original data if signature is valid, null otherwise.
18
+ *
19
+ * Note: Currently unused by production code — token verification relies on
20
+ * database lookup. Kept as utility for potential future use.
21
+ */
22
+ export function parseCookie(
23
+ signedValue: string,
24
+ secret: string,
25
+ ): string | null {
26
+ if (!signedValue) return null
27
+
28
+ const lastDot = signedValue.lastIndexOf('.')
29
+ if (lastDot === -1) return null
30
+
31
+ const data = signedValue.slice(0, lastDot)
32
+ const sig = signedValue.slice(lastDot + 1)
33
+
34
+ const expectedSig = createHmac('sha256', secret).update(data).digest('hex')
35
+
36
+ const a = Buffer.from(sig, 'utf8')
37
+ const b = Buffer.from(expectedSig, 'utf8')
38
+ if (a.length !== b.length) return null
39
+ if (!timingSafeEqual(a, b)) return null
40
+
41
+ return data
42
+ }
43
+
44
+ /**
45
+ * Build Set-Cookie header values for access and refresh tokens.
46
+ *
47
+ * Access token cookie: HttpOnly, Secure (default true), SameSite=Strict,
48
+ * Path=/, Max-Age=3600 (1 hour)
49
+ * Refresh token cookie: HttpOnly, Secure (default true), SameSite=Strict,
50
+ * Path=/, Max-Age=2592000 (30 days)
51
+ */
52
+ export function buildSessionCookies(
53
+ accessToken: string,
54
+ refreshToken: string,
55
+ options?: { secure?: boolean },
56
+ ): string[] {
57
+ const secure = options?.secure ?? true
58
+ const secureFlag = secure ? '; Secure' : ''
59
+
60
+ const accessCookie = `rcs_access=${accessToken}; Path=/; HttpOnly; SameSite=Strict; Max-Age=3600${secureFlag}`
61
+
62
+ const refreshCookie = `rcs_refresh=${refreshToken}; Path=/; HttpOnly; SameSite=Strict; Max-Age=2592000${secureFlag}`
63
+
64
+ return [accessCookie, refreshCookie]
65
+ }
66
+
67
+ /**
68
+ * Build Set-Cookie headers to clear session cookies.
69
+ */
70
+ export function buildClearCookies(): string[] {
71
+ return [
72
+ 'rcs_access=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0',
73
+ 'rcs_refresh=; Path=/; HttpOnly; SameSite=Strict; Max-Age=0',
74
+ ]
75
+ }