@jant/core 0.3.37 → 0.3.39

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 (1177) hide show
  1. package/LICENSE +189 -189
  2. package/README.md +5 -4
  3. package/bin/commands/assets/prepare.js +55 -0
  4. package/bin/commands/db/execute-file.js +113 -0
  5. package/bin/commands/db/export.js +5 -0
  6. package/bin/commands/db/rehearse.js +65 -0
  7. package/bin/commands/deploy.js +162 -0
  8. package/bin/commands/export.js +73 -75
  9. package/bin/commands/import-site.js +1531 -267
  10. package/bin/commands/migrate.js +79 -0
  11. package/bin/commands/reset-password.js +80 -7
  12. package/bin/commands/site/export.js +415 -0
  13. package/bin/commands/site/import.js +5 -0
  14. package/bin/commands/site/localize-media.js +118 -0
  15. package/bin/commands/site/snapshot/export.js +357 -0
  16. package/bin/commands/site/snapshot/import.js +363 -0
  17. package/bin/commands/start.js +46 -0
  18. package/bin/commands/uploads/cleanup.js +119 -0
  19. package/bin/jant.js +55 -15
  20. package/bin/lib/cli-api-token.js +6 -0
  21. package/bin/lib/d1-query.js +194 -0
  22. package/bin/lib/load-node-runtime.js +10 -0
  23. package/bin/lib/migration-artifacts.js +99 -0
  24. package/bin/lib/migration-rehearsal.js +438 -0
  25. package/bin/lib/migration-runner.js +311 -0
  26. package/bin/lib/node-database.js +48 -0
  27. package/bin/lib/node-sqlite.js +158 -0
  28. package/bin/lib/public-assets.js +66 -0
  29. package/bin/lib/r2-query.js +170 -0
  30. package/bin/lib/runtime-target.js +33 -0
  31. package/bin/lib/site-localize-media.js +427 -0
  32. package/bin/lib/site-media-parser.js +1150 -0
  33. package/bin/lib/site-selection.js +222 -0
  34. package/bin/lib/site-snapshot.js +484 -0
  35. package/bin/lib/site-url.js +84 -0
  36. package/bin/lib/sql-export.js +194 -0
  37. package/bin/lib/wrangler-cli.js +41 -0
  38. package/bin/lib/wrangler-config.js +86 -0
  39. package/dist/app-BfoG98VD.js +30410 -0
  40. package/dist/client/_assets/chunks/0041f681602cc834bb5c55ced0155b8e-BNpHLJgt.woff2 +0 -0
  41. package/dist/client/_assets/chunks/008ea9091e332c639ceb18874eacd60c-CygB704Q.woff2 +0 -0
  42. package/dist/client/_assets/chunks/00c9ac960d866ffaf8a866e5939024e2-CqRrlxSi.woff2 +0 -0
  43. package/dist/client/_assets/chunks/02629e5d0a9860b7fe32ec1f0563213a-YvgSIv_V.woff2 +0 -0
  44. package/dist/client/_assets/chunks/02e48e353415a00e0f556608cab33a43-YFgu4zi3.woff2 +0 -0
  45. package/dist/client/_assets/chunks/02faf6bb0ab4d56ada037c0bbaf9b9f7-DUuUM2eo.woff2 +0 -0
  46. package/dist/client/_assets/chunks/031089da45fbfb7dc18ac827bef4c56e-D4ahCOTO.woff2 +0 -0
  47. package/dist/client/_assets/chunks/03ac785139320b7b13bac9c150bf72bf-BG7zubxs.woff2 +0 -0
  48. package/dist/client/_assets/chunks/053bd3d7aec0040d0cc50c261a1f4e3e-BdG4yI0n.woff2 +0 -0
  49. package/dist/client/_assets/chunks/057a3d44d7fc606f113d863376d0ecf0-BsEMbuKV.woff2 +0 -0
  50. package/dist/client/_assets/chunks/0713613227cc4c686c45a279f8bdc166-BzLoJcLo.woff2 +0 -0
  51. package/dist/client/_assets/chunks/0b1f83a3c7e715560a55ad9eb0fb1c94-asGrAhxW.woff2 +0 -0
  52. package/dist/client/_assets/chunks/0c055db157e7a13f3103cc2a6b67fec3-1T6huY_I.woff2 +0 -0
  53. package/dist/client/_assets/chunks/0d3f5cc265cb6c439c517f2c4cebbddf-CmkfpVHX.woff2 +0 -0
  54. package/dist/client/_assets/chunks/0e97f44ebc65384c346fe19bcc52fa20-BJTzmZzt.woff2 +0 -0
  55. package/dist/client/_assets/chunks/10f2b44b3711d3f5bdcc30d373b543d1-BRTxD_6-.woff2 +0 -0
  56. package/dist/client/_assets/chunks/1139d32ae2bdeb26c0c8f31330aa9a9f-B7k4Da0E.woff2 +0 -0
  57. package/dist/client/_assets/chunks/1259e5825b314fe2b8bb96d6e8069ee5-Ba8VoZCg.woff2 +0 -0
  58. package/dist/client/_assets/chunks/12c518ebfe62818af550c08947e359e7-B7eDp07N.woff2 +0 -0
  59. package/dist/client/_assets/chunks/145831a59caa06d894022fe60212ed21-B3xSIk4b.woff2 +0 -0
  60. package/dist/client/_assets/chunks/14b040a2dda256936bc7b3470e548394-CF7zgNWs.woff2 +0 -0
  61. package/dist/client/_assets/chunks/14c1506106d92621bafb11016735194e-cbk_paoP.woff2 +0 -0
  62. package/dist/client/_assets/chunks/154a2c266902003bd8b7449386b10776-CYgd0apt.woff2 +0 -0
  63. package/dist/client/_assets/chunks/15f8c0df47fd639d1b0d9bd5cf507c9b-B11bYwtn.woff2 +0 -0
  64. package/dist/client/_assets/chunks/1668bd859ffe15bed7d5563117d8d5fb-DJotYU-j.woff2 +0 -0
  65. package/dist/client/_assets/chunks/169a096e61d38a773216f51d1ec2cc06-CzS4CZjy.woff2 +0 -0
  66. package/dist/client/_assets/chunks/1884a2b22d314c7d57707f03aec348e0-BdBJ4_JO.woff2 +0 -0
  67. package/dist/client/_assets/chunks/189f272ea2600c74d576b7b15c014922-CylwUmhO.woff2 +0 -0
  68. package/dist/client/_assets/chunks/190e3f8632494e7c095117f26b1c811e-DT09KhZe.woff2 +0 -0
  69. package/dist/client/_assets/chunks/19ad151c22ce1befe0a9ea643fbee570-BOjbPzxL.woff2 +0 -0
  70. package/dist/client/_assets/chunks/19e39850472250bfdbce654d30859879-Db8MVQ-5.woff2 +0 -0
  71. package/dist/client/_assets/chunks/1b5d0d740450fb996749464c9b882025-BG0yepI2.woff2 +0 -0
  72. package/dist/client/_assets/chunks/1bca0a2a8840ad0ee9414940593db144-CfiRZ0Fs.woff2 +0 -0
  73. package/dist/client/_assets/chunks/1c820b5295868008ca7c78afa5b7655d-rGkaVY0Z.woff2 +0 -0
  74. package/dist/client/_assets/chunks/1cda27dcaab977ae4ef5d5ab2a10ae03-3n1CbajX.woff2 +0 -0
  75. package/dist/client/_assets/chunks/1e2640116bbba817f43c43cc69371cf1-BLnekPNy.woff2 +0 -0
  76. package/dist/client/_assets/chunks/1f4bc38a1c50f55f335f5411cae47696-3J1JFXeu.woff2 +0 -0
  77. package/dist/client/_assets/chunks/1fbccc182322b513f57cd156a9a491b0-bRVH28a5.woff2 +0 -0
  78. package/dist/client/_assets/chunks/1fbe225742c69f4ba9ea5f74922f0ca1-BHRIiKDB.woff2 +0 -0
  79. package/dist/client/_assets/chunks/2022cf097cb952d9fe75b53b4587d2c3-Btdq5ZEB.woff2 +0 -0
  80. package/dist/client/_assets/chunks/21e0a71d86be7b8a9e812e7af09dd061-SSZaQU5Z.woff2 +0 -0
  81. package/dist/client/_assets/chunks/2240a3c43ca5ef59ae3c348c7884792f-DQgT8QLe.woff2 +0 -0
  82. package/dist/client/_assets/chunks/2573703213da30d3ba18925b100b2c2b-D61uHyrJ.woff2 +0 -0
  83. package/dist/client/_assets/chunks/265e048cc9f2f8a711ba585a534d5351-geHDzSNf.woff2 +0 -0
  84. package/dist/client/_assets/chunks/26839c0e47c73514b8d8f660d24d6b19-D2gmzZl-.woff2 +0 -0
  85. package/dist/client/_assets/chunks/280e3d2b58e9ad3501816072e01b0c13-Dx5yDwjz.woff2 +0 -0
  86. package/dist/client/_assets/chunks/2874d07e228da9583b0e73646dacd498-26mUURU-.woff2 +0 -0
  87. package/dist/client/_assets/chunks/29d49891713a2785a3a383001cf58c59-DVb6JpZh.woff2 +0 -0
  88. package/dist/client/_assets/chunks/2a22e14a9ad53f2abb3c7e85017b7d12-CU_4APNf.woff2 +0 -0
  89. package/dist/client/_assets/chunks/2a7cedfcd6e4c7cec36f4fd7b0f329c2-BUp7BYdq.woff2 +0 -0
  90. package/dist/client/_assets/chunks/2acea04a920f6af31e7db97052f563c6-KdL2n7-Y.woff2 +0 -0
  91. package/dist/client/_assets/chunks/2ae2ca951489c9d50cde5b36a2a5515b-CWV12jwD.woff2 +0 -0
  92. package/dist/client/_assets/chunks/2b3e8c5703b91f39f6027f43f0da6f4b-FHYDvFqX.woff2 +0 -0
  93. package/dist/client/_assets/chunks/2ba802b14f21a58fc61606c88fa93373-BmrW6szo.woff2 +0 -0
  94. package/dist/client/_assets/chunks/2d81eb6ab0ebbc0cabfb3a3341ba8800-Ds2YcMj0.woff2 +0 -0
  95. package/dist/client/_assets/chunks/2deb444546774c3a3ab38c75eb69cdfb-Dm9USQXk.woff2 +0 -0
  96. package/dist/client/_assets/chunks/2e98b666924b8e0a09d1aeeefd24bdd2-Cmh3DrKG.woff2 +0 -0
  97. package/dist/client/_assets/chunks/2f27ee4fb2cf6a280e110e09c18ef73e-D_yYWYMZ.woff2 +0 -0
  98. package/dist/client/_assets/chunks/2fbccf9a3853eb59db1a825e044515fd-CiShE3Wo.woff2 +0 -0
  99. package/dist/client/_assets/chunks/2fd3fceb6faed5e3db768e88d7614dca-D74stxc-.woff2 +0 -0
  100. package/dist/client/_assets/chunks/2ff009fa8701505d7f3dc6c83763f019-nkOuUfA-.woff2 +0 -0
  101. package/dist/client/_assets/chunks/31342cebfa5ea7fac06b4ea372d96bc5-DdIH_TkU.woff2 +0 -0
  102. package/dist/client/_assets/chunks/31424fe5d54692e7c8b38021ccb8597c-CfNu45zJ.woff2 +0 -0
  103. package/dist/client/_assets/chunks/3179006d1c7ebfa50d27482a2859d9a0-e7PNUH9y.woff2 +0 -0
  104. package/dist/client/_assets/chunks/34c2edb3c37f71258f5c4a31091f0c6c-Cyr8VjPf.woff2 +0 -0
  105. package/dist/client/_assets/chunks/35cf5dd04315e0b906e1a413d7905a2f-A-QPUrVX.woff2 +0 -0
  106. package/dist/client/_assets/chunks/360c190344c26278bbc50e2f4d6a2b3f-B7OhzvgR.woff2 +0 -0
  107. package/dist/client/_assets/chunks/375329ba0b50b94b35006498e555867c-CDc5XB7_.woff2 +0 -0
  108. package/dist/client/_assets/chunks/387c811226f303af62f1e21aae6f5c83-BmPYUAdb.woff2 +0 -0
  109. package/dist/client/_assets/chunks/389a950f2a1211946d294716e679e381-Diuxk8JN.woff2 +0 -0
  110. package/dist/client/_assets/chunks/39b0bbc910af9d2d6dcd8bd4abd6387d-NhvZf71d.woff2 +0 -0
  111. package/dist/client/_assets/chunks/3adc8f6350cf5067bcb6dc5e44c45d41-DIO3oy0b.woff2 +0 -0
  112. package/dist/client/_assets/chunks/3b41385fc27419c19822060daa0b5cb3-Dh5JVnsu.woff2 +0 -0
  113. package/dist/client/_assets/chunks/3bed5bd57de8f738e53cddaea88983d9-BAGVbLtM.woff2 +0 -0
  114. package/dist/client/_assets/chunks/3c201fd8d1bb20abe7d06b940e83a4d9-Bz0dCLvM.woff2 +0 -0
  115. package/dist/client/_assets/chunks/3cbe4a697fd595ef42c899de7d3e5445-zWwFRRI_.woff2 +0 -0
  116. package/dist/client/_assets/chunks/3d385ea0880df7204258e290648ec012-BQqoFH8R.woff2 +0 -0
  117. package/dist/client/_assets/chunks/3d83dacbbec3d8532ae9afede21f3aab-BH06SEJj.woff2 +0 -0
  118. package/dist/client/_assets/chunks/3dc1a4f0d7af59e16b5162a2b077a442-CkXZW3CY.woff2 +0 -0
  119. package/dist/client/_assets/chunks/3dc68e473fe23bd076dd46785cd23583-Cv6HZ0Dr.woff2 +0 -0
  120. package/dist/client/_assets/chunks/427577dcb707d1d35eebd155b4222aa7-D7-mHA6o.woff2 +0 -0
  121. package/dist/client/_assets/chunks/42a74b6a625bbf0a9616ed4db3152c88-CQ58KTMS.woff2 +0 -0
  122. package/dist/client/_assets/chunks/435b7dca567809813fcb395a27ed83a0-C6E3YXBr.woff2 +0 -0
  123. package/dist/client/_assets/chunks/43693195e775d515689fa035394067fd-DLMrklI6.woff2 +0 -0
  124. package/dist/client/_assets/chunks/43fb49e5b79ee7e553869d84e6e08b1e-BgOVrd59.woff2 +0 -0
  125. package/dist/client/_assets/chunks/44dcbbc3cc8f22e613b342d691511ab6-dRyYwGGi.woff2 +0 -0
  126. package/dist/client/_assets/chunks/450a5b53be0a8a778bb0b623e86b652f-qwII2gXW.woff2 +0 -0
  127. package/dist/client/_assets/chunks/457485e72835364662dfead6281638c1-Mv279UyL.woff2 +0 -0
  128. package/dist/client/_assets/chunks/47479c470fae70f10b7c964a7ecbf274-D9VNBclD.woff2 +0 -0
  129. package/dist/client/_assets/chunks/474fac21b12b7efd71f7c321578878b0-BJRz_Yd3.woff2 +0 -0
  130. package/dist/client/_assets/chunks/477866c8396474a17317dcac3e7a014f-e90LWIFy.woff2 +0 -0
  131. package/dist/client/_assets/chunks/478ebdaadda7775c391c5dcab4e697df-RfXJJ1gl.woff2 +0 -0
  132. package/dist/client/_assets/chunks/488846410760fe128dae939836ca5423-D0YbXQOX.woff2 +0 -0
  133. package/dist/client/_assets/chunks/48d6a97a185c799be4fe67aaf7edf213-CqLkiJik.woff2 +0 -0
  134. package/dist/client/_assets/chunks/48eb0a91e50c7f026e248c64145e72af-D8LB2Dye.woff2 +0 -0
  135. package/dist/client/_assets/chunks/490edb9fc8a4356aea556eed32287464-Bxk-XtyT.woff2 +0 -0
  136. package/dist/client/_assets/chunks/4a23fe6e82fd496b5eb20401b6164efe-CfhsfLd6.woff2 +0 -0
  137. package/dist/client/_assets/chunks/4b0e79ba18b2ce424fa93e84996d7cba-B0dS4UvF.woff2 +0 -0
  138. package/dist/client/_assets/chunks/4bc743968cf1c3ce5711de67ef1ccc4d-Dmn20LlT.woff2 +0 -0
  139. package/dist/client/_assets/chunks/4c4bdd0b3f3a52e28f3b643c1c5d43be-BLqyuzyK.woff2 +0 -0
  140. package/dist/client/_assets/chunks/4c96411f3693a9a8657a9c1190f82bce-BhP7AS2j.woff2 +0 -0
  141. package/dist/client/_assets/chunks/4c9aa12aba2a6a57410eacaff7427916-CLDZgzM7.woff2 +0 -0
  142. package/dist/client/_assets/chunks/4cca7233bf8ce5dec2e5d146b993d626-Bf7OgJe6.woff2 +0 -0
  143. package/dist/client/_assets/chunks/4cf0f292f3358bd2f73b1cf4ec1476f3-CIfjw85D.woff2 +0 -0
  144. package/dist/client/_assets/chunks/4d0a9128d06ea857f203bf5d007b1ab9-BZ-BuqS9.woff2 +0 -0
  145. package/dist/client/_assets/chunks/4dc0728df0f2ba70796f45f05654c7ba-DGd3NDaT.woff2 +0 -0
  146. package/dist/client/_assets/chunks/4dc2bc2c55b47f57d13b63aa6b1c8bd4-g3WbclK2.woff2 +0 -0
  147. package/dist/client/_assets/chunks/4e1cc6aafb411b572c8d3511e925ecf1-BUf15jYj.woff2 +0 -0
  148. package/dist/client/_assets/chunks/4e5384920bbb155d9d8d74887b09ea5b-2KLteQXT.woff2 +0 -0
  149. package/dist/client/_assets/chunks/501f66f24bce8234441954de1b568403-CIe4N6pO.woff2 +0 -0
  150. package/dist/client/_assets/chunks/50cfd672bfa62512ba090420acf35c87-CibXrwCT.woff2 +0 -0
  151. package/dist/client/_assets/chunks/5227dbe9933760a48baff21ebd13fc98-v8CBITMX.woff2 +0 -0
  152. package/dist/client/_assets/chunks/526b263e72c189f4b065738aaa6d423a-Dj5Ns6Ev.woff2 +0 -0
  153. package/dist/client/_assets/chunks/53a88404451448cd2e620a0ca0e45a20-CI0P5NCv.woff2 +0 -0
  154. package/dist/client/_assets/chunks/54da934819a917f561b439bfd10f88b6-CpoNRhz7.woff2 +0 -0
  155. package/dist/client/_assets/chunks/54e301f412730f391225db59dae1c8d5-CxTAz9F1.woff2 +0 -0
  156. package/dist/client/_assets/chunks/551b1d7a0b80c8d42af09863cdca7f01-BIGKH8GS.woff2 +0 -0
  157. package/dist/client/_assets/chunks/555d990ab3fd7d3d66c6d1fa9a82fec5-BndAe6f2.woff2 +0 -0
  158. package/dist/client/_assets/chunks/557cd00c5d6827e13d72a0c71b23587b-C6gpr-g-.woff2 +0 -0
  159. package/dist/client/_assets/chunks/563fa31542d553f25abab65cf7f81e1d-Cl_1me5X.woff2 +0 -0
  160. package/dist/client/_assets/chunks/56e1c4734bbbb38af2fbc262bf6e98f2-DZ32amUx.woff2 +0 -0
  161. package/dist/client/_assets/chunks/5947f5da5da9a352a2b534ee64bfc29a-DT0Fj-SL.woff2 +0 -0
  162. package/dist/client/_assets/chunks/5979c33a7eb5963bf8e83e46931b5fb5--4f9yv1z.woff2 +0 -0
  163. package/dist/client/_assets/chunks/597d69d0710e0178b162afb0a0c20401-CpThXFdo.woff2 +0 -0
  164. package/dist/client/_assets/chunks/59966ee0b069b577510fe68c350da0ee-Bs_HDvyP.woff2 +0 -0
  165. package/dist/client/_assets/chunks/5a10741e41259e235841440394c0763d-uCZGOg0A.woff2 +0 -0
  166. package/dist/client/_assets/chunks/5bfc7a121c35ae42623ef804fb525e0e-mS7vyFFz.woff2 +0 -0
  167. package/dist/client/_assets/chunks/5cc23a76e122d0ad2f7cede41bc35b27-4T1ZVQJu.woff2 +0 -0
  168. package/dist/client/_assets/chunks/5d48855bed5f3554eff91b573d7376ac-CPzsdNDF.woff2 +0 -0
  169. package/dist/client/_assets/chunks/5ddcbe564b29ef08632e1aeb33455435-CQzDKa-z.woff2 +0 -0
  170. package/dist/client/_assets/chunks/5f90024544c2907c6c0203c6210c50be-C_1uxIky.woff2 +0 -0
  171. package/dist/client/_assets/chunks/605667a998e91e2b6a4a3cd7c31fe5a9-Co2lOjER.woff2 +0 -0
  172. package/dist/client/_assets/chunks/60a14064ed334f0155795d795e926abe-C2HuVyw5.woff2 +0 -0
  173. package/dist/client/_assets/chunks/60d8b0805a0a8c54a6cca216004beff5-CV1GX5br.woff2 +0 -0
  174. package/dist/client/_assets/chunks/611b62d5fd9698d9b5ce495ba6f14c93-CPi2xRKF.woff2 +0 -0
  175. package/dist/client/_assets/chunks/61bf4287453da4025d03fa6b2dba66ca-CMh4XzzB.woff2 +0 -0
  176. package/dist/client/_assets/chunks/638369541268ed5a10af97ad77498c73-DrpvGu-L.woff2 +0 -0
  177. package/dist/client/_assets/chunks/649b12d7cee7bb981842946e4547e6ca-EACt8KkC.woff2 +0 -0
  178. package/dist/client/_assets/chunks/653bef2ed891ae48d8ed712283080649-C3d1ZTAc.woff2 +0 -0
  179. package/dist/client/_assets/chunks/67d2a81f06ba352f17fbdc3a5e6ea59e-DC8Iostn.woff2 +0 -0
  180. package/dist/client/_assets/chunks/67f32ceea9e78e5109f87724ad886010-C_3-9sSn.woff2 +0 -0
  181. package/dist/client/_assets/chunks/68304f3229cf763465f044fccb5892c0-CTvPlN8c.woff2 +0 -0
  182. package/dist/client/_assets/chunks/687d0f0f90a9b23e40102e16ad8e9836-Bzlx1W-7.woff2 +0 -0
  183. package/dist/client/_assets/chunks/688a88911e4da17b609196a959b8b930-BgwP4zIQ.woff2 +0 -0
  184. package/dist/client/_assets/chunks/68f2fab82ec8e9291f08c3145111549c-CXqagiiH.woff2 +0 -0
  185. package/dist/client/_assets/chunks/69519ada3f3f74ca20aacb8af48ab6b4-DncDOElk.woff2 +0 -0
  186. package/dist/client/_assets/chunks/6a6e884fb2b65ec5b4a3d5ecd0d01a6a-DBNJo4jq.woff2 +0 -0
  187. package/dist/client/_assets/chunks/6db6ddf72c38a78ce44c1327701152e1-CFUWMaQQ.woff2 +0 -0
  188. package/dist/client/_assets/chunks/6e1a8b45b01939088c3a8cfcf8c10681-BemqZSaI.woff2 +0 -0
  189. package/dist/client/_assets/chunks/6e2164fad867d166de2e5b274f04a563-_YD_QMym.woff2 +0 -0
  190. package/dist/client/_assets/chunks/6e83fe0b6e708eaf1c3003d6dee11488-CRqY8z2g.woff2 +0 -0
  191. package/dist/client/_assets/chunks/6eefc9d430171c1e1e4034ecadee31c8-d3VCnQ01.woff2 +0 -0
  192. package/dist/client/_assets/chunks/70861376e5d4f92f8aa7aa1b2749b617-BqwxmSo2.woff2 +0 -0
  193. package/dist/client/_assets/chunks/70adaf50c56d5ff859c64d35e0f1e34e-BCmTRExx.woff2 +0 -0
  194. package/dist/client/_assets/chunks/7124d150570d39ced8d45507dc11ca1e-Co_mUkqP.woff2 +0 -0
  195. package/dist/client/_assets/chunks/71eafb8fbe3a734283517e230ad8b6db-CFmZ-qAS.woff2 +0 -0
  196. package/dist/client/_assets/chunks/72ee453ac0e19bd2c631c8921c44e3de-CEidyyfI.woff2 +0 -0
  197. package/dist/client/_assets/chunks/751f54dbb115140d5b645a6ba4aff5d3-DY-S96zI.woff2 +0 -0
  198. package/dist/client/_assets/chunks/753b5f6fb254bacb6618ace25af3df60-uBG6c32o.woff2 +0 -0
  199. package/dist/client/_assets/chunks/7609e7e74dd4d916a7abc7ecc7d95f7e-CzrNhght.woff2 +0 -0
  200. package/dist/client/_assets/chunks/76b9d6fe838ae4151d95ce7200aa2bf6-BfQWDykU.woff2 +0 -0
  201. package/dist/client/_assets/chunks/76d4244186d118eea245d1385a4de2ec-CEoql1pw.woff2 +0 -0
  202. package/dist/client/_assets/chunks/77a7533bd21ccd33192d142a93555aa8-BzSb8ker.woff2 +0 -0
  203. package/dist/client/_assets/chunks/78ce29fed872e44fc9014d94875d2aac-D1BDuJXp.woff2 +0 -0
  204. package/dist/client/_assets/chunks/79a7fdf7d9c722b5723ae25e6ff8e203-5rPAnIr7.woff2 +0 -0
  205. package/dist/client/_assets/chunks/79a85a253e9b3f12d2e2cb15e16b3003-DTjWGv2X.woff2 +0 -0
  206. package/dist/client/_assets/chunks/7a86b155111ba20f3e87306ff6beac77-DchSNxJ1.woff2 +0 -0
  207. package/dist/client/_assets/chunks/7b1e76975b0984e6f83e3f9f8069e784-OB_rGzsR.woff2 +0 -0
  208. package/dist/client/_assets/chunks/7b6c60131822a0e4d36d980d52509d4e-8dQVuUiW.woff2 +0 -0
  209. package/dist/client/_assets/chunks/7caa14a095a6bc313aab780fe4ff7999-BJ-80Ois.woff2 +0 -0
  210. package/dist/client/_assets/chunks/7cbda564cb2dd4799ab9e89d51286aa7-MlT7YRSz.woff2 +0 -0
  211. package/dist/client/_assets/chunks/7d138084cf03c14116b11297fce0e3e3-CdWCcyam.woff2 +0 -0
  212. package/dist/client/_assets/chunks/7d65a3d6a65050eb5e6eca43398aeba4-CYB359u-.woff2 +0 -0
  213. package/dist/client/_assets/chunks/7dfc711962c8771f97e7c8898a6bcb65-Yv69yrg4.woff2 +0 -0
  214. package/dist/client/_assets/chunks/7ef123b62d530fcba73974fa265e0aae-CD7IMlBc.woff2 +0 -0
  215. package/dist/client/_assets/chunks/7f60eefa15956d6f06dd92404887d58c-LqioDjlc.woff2 +0 -0
  216. package/dist/client/_assets/chunks/7f8c15e0ecb102738981d9fa4cb6b921-8qiw7Fmn.woff2 +0 -0
  217. package/dist/client/_assets/chunks/80466082a896fd328f30a78593c7c568-BMAVDyAy.woff2 +0 -0
  218. package/dist/client/_assets/chunks/812b5a4b87f3a7b4afc1cfebc864f413-B4_mWnLK.woff2 +0 -0
  219. package/dist/client/_assets/chunks/812dfb7f8144d01b3cc9d5ce0b472f40-DxhfKDhh.woff2 +0 -0
  220. package/dist/client/_assets/chunks/84742b1ede4f0bb6d27131298eba21b4-4_QYQ-wj.woff2 +0 -0
  221. package/dist/client/_assets/chunks/8555f0285e3d28e95e2fc0ccccd9caff-CEnfoaRy.woff2 +0 -0
  222. package/dist/client/_assets/chunks/880162ae92cd9e120eb4e4e11fae459d-Cj61LKOn.woff2 +0 -0
  223. package/dist/client/_assets/chunks/8a3c84b0df36f851f5fea75ee8757951-CfHrA-x8.woff2 +0 -0
  224. package/dist/client/_assets/chunks/8b0c8c9f8cfa9fa090d97c5a5efb1f4c-DZc3EGF4.woff2 +0 -0
  225. package/dist/client/_assets/chunks/8c8393bc875f1ee36697a2113f4421ea-CnBugeCf.woff2 +0 -0
  226. package/dist/client/_assets/chunks/8dc035a34c76e6515ca203e2df182588-B1reQPCB.woff2 +0 -0
  227. package/dist/client/_assets/chunks/8e04e64c8f68d292a18d4160fbde8671-DkT7v-sd.woff2 +0 -0
  228. package/dist/client/_assets/chunks/8e6c9bb43afb8cbbff7cf1055e67c9bd-c7wzPI7v.woff2 +0 -0
  229. package/dist/client/_assets/chunks/8eb06109812cb80be44f47b8179c2709-DOaU0xD8.woff2 +0 -0
  230. package/dist/client/_assets/chunks/8f2b960c2823670e94f5b08aa65657e6-CHT-Gunr.woff2 +0 -0
  231. package/dist/client/_assets/chunks/8f89f57230d184f92a36e241874229d7-D6P0FMCn.woff2 +0 -0
  232. package/dist/client/_assets/chunks/904324af375d5fd370af1054355a050e-CHiXmgO7.woff2 +0 -0
  233. package/dist/client/_assets/chunks/90ac4f9d2aa02afdace2843b49fc18bb-CWArmWzF.woff2 +0 -0
  234. package/dist/client/_assets/chunks/90b6f57d77847f512fd11db74fa912f1-DzYbq8hW.woff2 +0 -0
  235. package/dist/client/_assets/chunks/911a2092d64d6d6494b254d819af2b91-C0xmZ-Cu.woff2 +0 -0
  236. package/dist/client/_assets/chunks/913759e6690f9fc0746a20b96f4bdcb4-B0k9hqx0.woff2 +0 -0
  237. package/dist/client/_assets/chunks/9154e26efe532a85a27d80902f5a2d6c-DJQmzPmB.woff2 +0 -0
  238. package/dist/client/_assets/chunks/9180de34b48b325200a97e267befff32-ClJx20hD.woff2 +0 -0
  239. package/dist/client/_assets/chunks/94e7ed67f1557b76fead6b6e456a0415-CjAhhvRn.woff2 +0 -0
  240. package/dist/client/_assets/chunks/95127a92346c04fec1fa81d6295b0a28-DNbeXPGm.woff2 +0 -0
  241. package/dist/client/_assets/chunks/958efb9b2fa2ea0008ffef009885f9f8-CNOwBDS1.woff2 +0 -0
  242. package/dist/client/_assets/chunks/95df3b9f681d9df411c30aea5b24f2e0-Dv7GHFVj.woff2 +0 -0
  243. package/dist/client/_assets/chunks/975af5a496e8d87d821910aa9fe4d598-CG5KDMOI.woff2 +0 -0
  244. package/dist/client/_assets/chunks/97a874bbf55ce89a4ab7cd27c7e938b1-fhost-bL.woff2 +0 -0
  245. package/dist/client/_assets/chunks/9ca9b71010a5faeee7047ef97aeee13b-K6NP7P3o.woff2 +0 -0
  246. package/dist/client/_assets/chunks/9cd0b77920b9d6c64eb686493123fc76-Db6wX5BE.woff2 +0 -0
  247. package/dist/client/_assets/chunks/9de02d745b8e25c6411fb152fb067748-CGxZtq8G.woff2 +0 -0
  248. package/dist/client/_assets/chunks/9eb33a430058d839ebbe2af4b2e0daa9-Cjd-WSds.woff2 +0 -0
  249. package/dist/client/_assets/chunks/9ebd27835ffcbd794e67151ab046ce68-DmxtZVr5.woff2 +0 -0
  250. package/dist/client/_assets/chunks/9f5a73aa8ba417688019d628f334db07-P03uxox_.woff2 +0 -0
  251. package/dist/client/_assets/chunks/9fbc06b2e3ff16b9d705c76db563ef17-XDHSv5Te.woff2 +0 -0
  252. package/dist/client/_assets/chunks/9fd53607094e329fa8e5c785b3ff0f1a-B-9qgOdM.woff2 +0 -0
  253. package/dist/client/_assets/chunks/a077f51cfb5cffb4ff4d8e229c0e9e79-BaPgyZ5H.woff2 +0 -0
  254. package/dist/client/_assets/chunks/a0f0c06d5c7a3ffa97706178cce212a8-DN_NE4Ic.woff2 +0 -0
  255. package/dist/client/_assets/chunks/a38c1830367f784181b6f544b0b11bbd-BmPGDXn1.woff2 +0 -0
  256. package/dist/client/_assets/chunks/a397997b579d3945c9c70a979c17a8ad-DXa54wGz.woff2 +0 -0
  257. package/dist/client/_assets/chunks/a3b929542e6c5a0644b73a7c8a8b6c03-B5i2p-Hj.woff2 +0 -0
  258. package/dist/client/_assets/chunks/a578742770fcd2226e3c45b5b6efdcb0-C1QhQdHg.woff2 +0 -0
  259. package/dist/client/_assets/chunks/a68d9d5027803832bb28e78cdcd04949-D_bwWhwg.woff2 +0 -0
  260. package/dist/client/_assets/chunks/a8857f5d478f101c053ba02d2f223e90-CHOaRPpq.woff2 +0 -0
  261. package/dist/client/_assets/chunks/a904b05966368bcf90b632c7c2e5f76b-BbTXYQVh.woff2 +0 -0
  262. package/dist/client/_assets/chunks/a9cf85e27428c14351d30eac8cbc8d91-DMaVc3-8.woff2 +0 -0
  263. package/dist/client/_assets/chunks/aa0ce6740f301351761a0615cc8b2e99-4RL3pitV.woff2 +0 -0
  264. package/dist/client/_assets/chunks/aa218a2c45f3749537ce876201e5152b-DndHqA-p.woff2 +0 -0
  265. package/dist/client/_assets/chunks/aa28db16818f9eaa8c817f289e1c3270-Drm1Jwhe.woff2 +0 -0
  266. package/dist/client/_assets/chunks/aa64c9953af43ca65832f413895bb433-CR32RteD.woff2 +0 -0
  267. package/dist/client/_assets/chunks/aa96d698491c2540e2dcf7009c65c456-BXlPVR0v.woff2 +0 -0
  268. package/dist/client/_assets/chunks/ada8f0241244c60ec8d3d59ad37f20a5-BFQsznEm.woff2 +0 -0
  269. package/dist/client/_assets/chunks/ae25c41034ddc1a9e0b41f5034c9aa4b-COQJkHOc.woff2 +0 -0
  270. package/dist/client/_assets/chunks/ae289ae3f8cdb54a3a6c07174517afec-DFGIoTY6.woff2 +0 -0
  271. package/dist/client/_assets/chunks/ae401fb4db80d5ff5cd3f8d9bc811070-Byt9AwoT.woff2 +0 -0
  272. package/dist/client/_assets/chunks/b02dfa2aa52cbdb1b2f11a9f44335469-DVWsuPSL.woff2 +0 -0
  273. package/dist/client/_assets/chunks/b0ab3a7f319ce6dd3c9a4de2674e7c72-CksmA6Hr.woff2 +0 -0
  274. package/dist/client/_assets/chunks/b159deb135e9946eea0572d52778170b-BZrtOTn0.woff2 +0 -0
  275. package/dist/client/_assets/chunks/b2e326f7f9b807451bf9c745df747efe-BvlONCPB.woff2 +0 -0
  276. package/dist/client/_assets/chunks/b341de0bc0bfe194a6c28dcfb566029e-DJAj3r6U.woff2 +0 -0
  277. package/dist/client/_assets/chunks/b846c293981ca5429eabaa967f222f26-GS-TAaO6.woff2 +0 -0
  278. package/dist/client/_assets/chunks/b91450304d9ac44f5c6e0da0792e055d-vS0S7kl0.woff2 +0 -0
  279. package/dist/client/_assets/chunks/baa325551b381c5e035ef143e56d4abe-DJg3GhKJ.woff2 +0 -0
  280. package/dist/client/_assets/chunks/bc3f0cb8b55ee11d32b94ca488976f8d-Dp5KZMSD.woff2 +0 -0
  281. package/dist/client/_assets/chunks/bcb3307527d6d0033bf0f17660b91e71-BMgRW_eW.woff2 +0 -0
  282. package/dist/client/_assets/chunks/be64f9379412876e00fd3a0bfa6b6fe9-BJ3FMWFM.woff2 +0 -0
  283. package/dist/client/_assets/chunks/befed8a4fa817773fa7109db6fe07f56-3sAmDsOO.woff2 +0 -0
  284. package/dist/client/_assets/chunks/bf1acc86e17b4229c548828a9d6f455d-JNuPCGDM.woff2 +0 -0
  285. package/dist/client/_assets/chunks/c09ee2b219982f8d46ad9968b7e6e0ba-qVb8yJHb.woff2 +0 -0
  286. package/dist/client/_assets/chunks/c0c7836749e585cee24ab2f8457c5b01-Ds0003oT.woff2 +0 -0
  287. package/dist/client/_assets/chunks/c2ac4ef1860812036ca2b8c4e4089bdc-DsziO8Du.woff2 +0 -0
  288. package/dist/client/_assets/chunks/c31019c08bd22464f7a88f090281404c-CUC0jMW1.woff2 +0 -0
  289. package/dist/client/_assets/chunks/c33c59feccf391f0c5f1f5d24e36d1fe-BXTVggad.woff2 +0 -0
  290. package/dist/client/_assets/chunks/c39ec937c6a8d124e8b68cf829ea5ad4-CarrkdmQ.woff2 +0 -0
  291. package/dist/client/_assets/chunks/c3fbc1f2557c343863a10698f8c966a2-BiOC1yo2.woff2 +0 -0
  292. package/dist/client/_assets/chunks/c3fd21315345ae541f6e98067059fa19-rHtO7vK5.woff2 +0 -0
  293. package/dist/client/_assets/chunks/c568a16e3168ceb1f191b70022c492ea-BpAiyU80.woff2 +0 -0
  294. package/dist/client/_assets/chunks/c57ee3b49b7e45b995539a6b2c51f138-IHDXeVRU.woff2 +0 -0
  295. package/dist/client/_assets/chunks/c5c1c0be944ea39a3f50a02d32f5b759-DAwbrvTL.woff2 +0 -0
  296. package/dist/client/_assets/chunks/c5e66d60be3375835bbd8d6b797f6eac-DWp4tpvN.woff2 +0 -0
  297. package/dist/client/_assets/chunks/c5f1075caf6d1344ee720de85114a521-DTTbeLhd.woff2 +0 -0
  298. package/dist/client/_assets/chunks/c82fd9456d7465b5e5bd3659e9b14c55-Cs6h6hE6.woff2 +0 -0
  299. package/dist/client/_assets/chunks/c90b7b65d2b9696fbf3a506738f94d68-DsvgiuQQ.woff2 +0 -0
  300. package/dist/client/_assets/chunks/cae29b3f8951eaf20d2f61c2206e28d9-CoHXmhmG.woff2 +0 -0
  301. package/dist/client/_assets/chunks/cba6ad3981cb7861428d4be169ee8124-CBdFsPAF.woff2 +0 -0
  302. package/dist/client/_assets/chunks/cc26525aa2af1f0b929af32ce50a7fba-D55LVtW9.woff2 +0 -0
  303. package/dist/client/_assets/chunks/cd0e7b51eddb22a77a09b025c0281434-ByI_TSTq.woff2 +0 -0
  304. package/dist/client/_assets/chunks/cd10a3af2133805d8c92104d1ee6ff18-Dtr5mBXe.woff2 +0 -0
  305. package/dist/client/_assets/chunks/cd6d074f3957d58bac58437fc97e5e33-CrVZuSbw.woff2 +0 -0
  306. package/dist/client/_assets/chunks/cd75ca47da9ae4c0899e37d4c543319b-DXU1s4Ja.woff2 +0 -0
  307. package/dist/client/_assets/chunks/cef249b6d013fb0cc0d574176bc23811-BL7WfNHA.woff2 +0 -0
  308. package/dist/client/_assets/chunks/d043b8d7a48bb0ac59ee1f1477d88eee-d5CwY2I8.woff2 +0 -0
  309. package/dist/client/_assets/chunks/d0bd387fda28e58d3c9b3efa2468dd8a-CjDFowFS.woff2 +0 -0
  310. package/dist/client/_assets/chunks/d12ce1d8445213317f9163283e58a05d-4RtGQnak.woff2 +0 -0
  311. package/dist/client/_assets/chunks/d15a3317942b7d31978a759fbf2222c8-DQ9QFQdS.woff2 +0 -0
  312. package/dist/client/_assets/chunks/d28fb13acf9ced9f0657fd4012c81cd2-D8Bgk6sd.woff2 +0 -0
  313. package/dist/client/_assets/chunks/d320b000b5978c7251148a6a154741b8-CcGZjeFx.woff2 +0 -0
  314. package/dist/client/_assets/chunks/d3714e6b90de8e2085dfb2514464dd6a-8LsVAcmb.woff2 +0 -0
  315. package/dist/client/_assets/chunks/d3beff96216c8af1aa79246476b6a323-hQysoMK3.woff2 +0 -0
  316. package/dist/client/_assets/chunks/d3e311f30c811dc339c262a79a51877e-CkUTbS74.woff2 +0 -0
  317. package/dist/client/_assets/chunks/d51f4cdc83711e510f5d25f03235597e-BY3VAGvr.woff2 +0 -0
  318. package/dist/client/_assets/chunks/d5df4a8dfd4328c67d933b3912c6ad0f-A2fccswC.woff2 +0 -0
  319. package/dist/client/_assets/chunks/d740dc2e854aaa7b3dcdd3ed25455eeb-Cw8LrB06.woff2 +0 -0
  320. package/dist/client/_assets/chunks/d8325ba7ae651bc30440905bd67b95f1-Dg8M_ijr.woff2 +0 -0
  321. package/dist/client/_assets/chunks/da13b136efb1d1e4c76575af8f79a698-BbfzLGVB.woff2 +0 -0
  322. package/dist/client/_assets/chunks/da2cf0ec56bf69374ee37764c7e3ea3d-fyVBKE2g.woff2 +0 -0
  323. package/dist/client/_assets/chunks/da93ae099ff3b7aae27b3f674d3fc721-BDG5ywtd.woff2 +0 -0
  324. package/dist/client/_assets/chunks/daf62255dd60679946f28c442ca62533-BnIjYSn3.woff2 +0 -0
  325. package/dist/client/_assets/chunks/dccda6a2e2db3b530788bdfa2acd1979-ByL7eZcn.woff2 +0 -0
  326. package/dist/client/_assets/chunks/dd01a1035345f6921a48525b8ce08f06-vkP6t4fC.woff2 +0 -0
  327. package/dist/client/_assets/chunks/e2204cf85edcb96c5de5c3dcf240da9d-Ch3zKjD-.woff2 +0 -0
  328. package/dist/client/_assets/chunks/e264213b9e102dabc603adb6e4fda5e6-CyxmQBHB.woff2 +0 -0
  329. package/dist/client/_assets/chunks/e3e913e145ddcd9323b2a0972967feb6-3sjEdzck.woff2 +0 -0
  330. package/dist/client/_assets/chunks/e4fb59479cedc87ba79785590bf861ca-sx7RZ8Vu.woff2 +0 -0
  331. package/dist/client/_assets/chunks/e5d00355f73293d40b61299459d17ca5-D8Vt3D-z.woff2 +0 -0
  332. package/dist/client/_assets/chunks/e647b8d2efc501c0cc0e407249cc7135-BrSW9DU4.woff2 +0 -0
  333. package/dist/client/_assets/chunks/e6e60ffb2ebd1828628764b507060aea-Dqyf48QC.woff2 +0 -0
  334. package/dist/client/_assets/chunks/e7c7ef3669ae48c0a736f06ca471e1d7-DNCebEB-.woff2 +0 -0
  335. package/dist/client/_assets/chunks/e81a742cacef744130c40de1b90837d8-DtAIFBk_.woff2 +0 -0
  336. package/dist/client/_assets/chunks/e8b755172122d1d0a5dd453e96b0ff24-BNTSyPYU.woff2 +0 -0
  337. package/dist/client/_assets/chunks/e99280299c305402eaa5271a3e36c49b-BpqXwl7U.woff2 +0 -0
  338. package/dist/client/_assets/chunks/e9c66b085052ece66bfadf45f711d3e1-DVza3Uob.woff2 +0 -0
  339. package/dist/client/_assets/chunks/ed7c6dafaa6d8bcf015ef0ca574837df-C7bcndJl.woff2 +0 -0
  340. package/dist/client/_assets/chunks/f00eb499abb94fa7b799d6d8c9b050e9-D_XvVTI9.woff2 +0 -0
  341. package/dist/client/_assets/chunks/f2900a1d30c3a33129f4e2225669bd0e-in84LilX.woff2 +0 -0
  342. package/dist/client/_assets/chunks/f2fb1f1fbf7e44afb53c672ec286a22e-tFk0sTQc.woff2 +0 -0
  343. package/dist/client/_assets/chunks/f372129c60aaece937cf7b91ee75c9b8-DiP_JYOt.woff2 +0 -0
  344. package/dist/client/_assets/chunks/f5237486197aeff59341a1ff38b8eff8-D_uuqrNZ.woff2 +0 -0
  345. package/dist/client/_assets/chunks/f553d54ef931066712d8f3f0ce018e1b-D8K983U5.woff2 +0 -0
  346. package/dist/client/_assets/chunks/f5d7487963d43c89da63aaf10f2e6fb7-B7GIrfPU.woff2 +0 -0
  347. package/dist/client/_assets/chunks/f649cba8e14c33d6bf2265483b14b895-Nm5wSov8.woff2 +0 -0
  348. package/dist/client/_assets/chunks/f6839df1bf7cb4dc8d27e5ea55bbe633-BIjWKN84.woff2 +0 -0
  349. package/dist/client/_assets/chunks/f6b7304e028980f77a7f7007bb540abd-CIlBt6sf.woff2 +0 -0
  350. package/dist/client/_assets/chunks/f75496953a40ff241178240209f56990-MOSKd67d.woff2 +0 -0
  351. package/dist/client/_assets/chunks/f7d36ffff7a75c9c6216d576a57dd00d-CaXQA5A8.woff2 +0 -0
  352. package/dist/client/_assets/chunks/f7f3f63e7a149cd89eccab3b52171d05-oNOOSdDV.woff2 +0 -0
  353. package/dist/client/_assets/chunks/f8db8bef0a6e1178835d350ae0d384a1-ZAiKM369.woff2 +0 -0
  354. package/dist/client/_assets/chunks/f92d74d1d217d21b39075ff23f79f7fd-BlTe0_IX.woff2 +0 -0
  355. package/dist/client/_assets/chunks/f9d6d981d8b87b3e469027277f585741-DQqIv77a.woff2 +0 -0
  356. package/dist/client/_assets/chunks/fa7d3b99744d7f2dc9e00864a97a62d6-Pfww07DK.woff2 +0 -0
  357. package/dist/client/_assets/chunks/fa8ed469ef290bfeb571418fe0abb628-D5v-Z7kY.woff2 +0 -0
  358. package/dist/client/_assets/chunks/fb0e90665980954719c2eb685b130bc0-CiWRvo58.woff2 +0 -0
  359. package/dist/client/_assets/chunks/fb4649a82c50620773d79820e2e5ff13-dw14czgx.woff2 +0 -0
  360. package/dist/client/_assets/chunks/fb61b690208eff56e6d8432951270901-CUAnVvWa.woff2 +0 -0
  361. package/dist/client/_assets/chunks/fb9402d6c6357a825affc402f14d5a7e-CIp4G53L.woff2 +0 -0
  362. package/dist/client/_assets/chunks/fcc41f6a067ddd658bba5c9dff234a32-BtZTNo2r.woff2 +0 -0
  363. package/dist/client/_assets/chunks/fd6ad889fcf3583bd9b0b6db53aad434-DqiKyYmz.woff2 +0 -0
  364. package/dist/client/_assets/chunks/ff0937ad63cda71ff420945ead55ab4d-CXGPdnOz.woff2 +0 -0
  365. package/dist/client/_assets/chunks/heic-to-XcUDQvtx.js +1 -0
  366. package/dist/client/_assets/chunks/literata-cyrillic-ext-wght-normal-CGKlZYBf.woff2 +0 -0
  367. package/dist/client/_assets/chunks/literata-cyrillic-wght-normal-DLqwHbi6.woff2 +0 -0
  368. package/dist/client/_assets/chunks/literata-greek-ext-wght-normal-e3e57Shi.woff2 +0 -0
  369. package/dist/client/_assets/chunks/literata-greek-wght-normal-CO1l-giJ.woff2 +0 -0
  370. package/dist/client/_assets/chunks/literata-latin-ext-wght-normal-BnEbWgdZ.woff2 +0 -0
  371. package/dist/client/_assets/chunks/literata-latin-wght-normal-DLxlUchJ.woff2 +0 -0
  372. package/dist/client/_assets/chunks/literata-vietnamese-wght-normal-LcSrhZ7T.woff2 +0 -0
  373. package/dist/client/_assets/chunks/module-ChVQstFd.js +716 -0
  374. package/dist/client/_assets/chunks/native-CR5HLOyf.js +1 -0
  375. package/dist/client/_assets/chunks/news-cycle-cyrillic-400-normal-9BSXki1I.woff +0 -0
  376. package/dist/client/_assets/chunks/news-cycle-cyrillic-400-normal-CUYmhJri.woff2 +0 -0
  377. package/dist/client/_assets/chunks/news-cycle-cyrillic-ext-400-normal-CKyb9yaQ.woff2 +0 -0
  378. package/dist/client/_assets/chunks/news-cycle-cyrillic-ext-400-normal-Llxpm7uO.woff +0 -0
  379. package/dist/client/_assets/chunks/news-cycle-greek-400-normal-BiAZ91Zl.woff2 +0 -0
  380. package/dist/client/_assets/chunks/news-cycle-greek-400-normal-D88oNngQ.woff +0 -0
  381. package/dist/client/_assets/chunks/news-cycle-greek-ext-400-normal-Ckclqjrc.woff2 +0 -0
  382. package/dist/client/_assets/chunks/news-cycle-greek-ext-400-normal-oz-bBDWj.woff +0 -0
  383. package/dist/client/_assets/chunks/news-cycle-latin-400-normal-BLgpQ3uo.woff +0 -0
  384. package/dist/client/_assets/chunks/news-cycle-latin-400-normal-BgW97ttO.woff2 +0 -0
  385. package/dist/client/_assets/chunks/news-cycle-latin-ext-400-normal-DmDltzLi.woff2 +0 -0
  386. package/dist/client/_assets/chunks/news-cycle-latin-ext-400-normal-lewDoXxP.woff +0 -0
  387. package/dist/client/_assets/chunks/news-cycle-vietnamese-400-normal-QPSuG-pc.woff +0 -0
  388. package/dist/client/_assets/chunks/news-cycle-vietnamese-400-normal-t2mxI9x9.woff2 +0 -0
  389. package/dist/client/_assets/chunks/newsreader-latin-ext-wght-normal-C-3rgBeH.woff2 +0 -0
  390. package/dist/client/_assets/chunks/newsreader-latin-wght-normal-CCVVNp6i.woff2 +0 -0
  391. package/dist/client/_assets/chunks/newsreader-vietnamese-wght-normal-Czsa-EzN.woff2 +0 -0
  392. package/dist/client/_assets/chunks/source-sans-3-cyrillic-ext-wght-normal-DzyfIafT.woff2 +0 -0
  393. package/dist/client/_assets/chunks/source-sans-3-cyrillic-wght-normal-BMDVbyM7.woff2 +0 -0
  394. package/dist/client/_assets/chunks/source-sans-3-greek-ext-wght-normal-BWSLJLk6.woff2 +0 -0
  395. package/dist/client/_assets/chunks/source-sans-3-greek-wght-normal-C9H9m1vD.woff2 +0 -0
  396. package/dist/client/_assets/chunks/source-sans-3-latin-ext-wght-normal-C8iNium2.woff2 +0 -0
  397. package/dist/client/_assets/chunks/source-sans-3-latin-wght-normal-BqRLTx4X.woff2 +0 -0
  398. package/dist/client/_assets/chunks/source-sans-3-vietnamese-wght-normal-C1uRvKPU.woff2 +0 -0
  399. package/dist/client/_assets/chunks/url-CG0eolsk.js +1 -0
  400. package/dist/client/_assets/client-auth.js +3251 -0
  401. package/dist/client/_assets/client-cjk-tc.css +1 -0
  402. package/dist/client/_assets/client-cjk.css +1 -0
  403. package/dist/client/_assets/client.css +2 -0
  404. package/dist/client/_assets/client.js +380 -0
  405. package/dist/index.js +2 -20359
  406. package/dist/node.js +479 -0
  407. package/package.json +67 -40
  408. package/src/__tests__/bin/uploads-cleanup.test.ts +77 -0
  409. package/src/__tests__/export-service.test.ts +550 -0
  410. package/src/__tests__/helpers/app.ts +60 -27
  411. package/src/__tests__/helpers/db.ts +14 -24
  412. package/src/__tests__/helpers/lingui-core-macro-mock.ts +15 -2
  413. package/src/__tests__/import-site-command.test.ts +406 -0
  414. package/src/__tests__/site-localize-media.test.ts +150 -0
  415. package/src/__tests__/site-media-parser.test.ts +216 -0
  416. package/src/app.tsx +272 -175
  417. package/src/assets/branding/generated/README.txt +15 -0
  418. package/src/assets/branding/generated/jant-apple-touch-icon.png +0 -0
  419. package/src/assets/branding/generated/jant-brand-assets.zip +0 -0
  420. package/src/assets/branding/generated/jant-brand-tile-512.png +0 -0
  421. package/src/assets/branding/generated/jant-brand-tile.svg +1 -0
  422. package/src/assets/branding/generated/jant-circle-tile-512.png +0 -0
  423. package/src/assets/branding/generated/jant-circle-tile.svg +1 -0
  424. package/src/assets/branding/generated/jant-favicon.ico +0 -0
  425. package/src/assets/branding/generated/jant-logo-negative.svg +1 -0
  426. package/src/assets/branding/generated/jant-logo-positive-512.png +0 -0
  427. package/src/assets/branding/generated/jant-logo-positive.svg +1 -0
  428. package/src/assets/branding/generated/jant-social-preview.png +0 -0
  429. package/src/assets/branding/generated/jant-square-tile-512.png +0 -0
  430. package/src/assets/branding/generated/jant-square-tile.svg +1 -0
  431. package/src/assets/branding/jant-logo-positive.svg +3 -0
  432. package/src/auth.ts +47 -6
  433. package/src/client/__tests__/avatar-upload.test.ts +186 -0
  434. package/src/client/__tests__/collection-form-bridge.test.ts +127 -0
  435. package/src/client/__tests__/collection-page-actions.test.ts +112 -0
  436. package/src/client/__tests__/collection-sort-menu.test.ts +78 -0
  437. package/src/client/__tests__/compose-bridge.test.ts +260 -0
  438. package/src/client/__tests__/compose-discovery.test.ts +155 -0
  439. package/src/client/__tests__/compose-shortcuts.test.ts +133 -0
  440. package/src/client/__tests__/confirm.test.ts +98 -0
  441. package/src/client/__tests__/custom-url-menu.test.ts +104 -0
  442. package/src/client/__tests__/form-enter-submit.test.ts +110 -0
  443. package/src/client/__tests__/post-form-bridge.test.ts +82 -0
  444. package/src/client/__tests__/sortable-list.test.ts +91 -0
  445. package/src/client/__tests__/toast.test.ts +46 -0
  446. package/src/client/archive-nav.js +1 -3
  447. package/src/client/avatar-upload.ts +66 -25
  448. package/src/client/collection-form-bridge.ts +32 -15
  449. package/src/client/collection-page-actions.ts +160 -0
  450. package/src/client/collection-sort-menu.ts +105 -0
  451. package/src/client/components/__tests__/jant-collection-form.test.ts +199 -51
  452. package/src/client/components/__tests__/jant-collection-sidebar.test.ts +250 -0
  453. package/src/client/components/__tests__/jant-compose-dialog.test.ts +2507 -342
  454. package/src/client/components/__tests__/jant-compose-editor.test.ts +484 -20
  455. package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +298 -0
  456. package/src/client/components/__tests__/jant-post-form.test.ts +9 -8
  457. package/src/client/components/__tests__/jant-post-menu.test.ts +325 -0
  458. package/src/client/components/__tests__/jant-settings-avatar.test.ts +27 -3
  459. package/src/client/components/__tests__/jant-settings-general.test.ts +312 -84
  460. package/src/client/components/collection-manager-types.ts +51 -0
  461. package/src/client/components/collection-types.ts +12 -10
  462. package/src/client/components/compose-types.ts +80 -13
  463. package/src/client/components/jant-collection-form.ts +338 -509
  464. package/src/client/components/jant-collection-sidebar.ts +461 -463
  465. package/src/client/components/jant-compose-dialog.ts +1566 -479
  466. package/src/client/components/jant-compose-editor.ts +531 -276
  467. package/src/client/components/jant-compose-fullscreen.ts +184 -112
  468. package/src/client/components/jant-confirm-dialog.ts +249 -0
  469. package/src/client/components/jant-nav-manager.ts +79 -103
  470. package/src/client/components/jant-post-form.ts +4 -1
  471. package/src/client/components/jant-post-menu.ts +583 -306
  472. package/src/client/components/jant-settings-avatar.ts +15 -9
  473. package/src/client/components/jant-settings-general.ts +444 -137
  474. package/src/client/components/nav-manager-types.ts +6 -3
  475. package/src/client/components/post-form-template.ts +2 -9
  476. package/src/client/components/post-form-types.ts +2 -4
  477. package/src/client/components/settings-types.ts +34 -2
  478. package/src/client/compose-bridge.ts +297 -171
  479. package/src/client/compose-discovery-bridge.ts +9 -0
  480. package/src/client/compose-discovery.ts +254 -0
  481. package/src/client/compose-launch.ts +98 -0
  482. package/src/client/compose-shortcuts.ts +61 -0
  483. package/src/client/confirm.ts +40 -0
  484. package/src/client/custom-url-menu.ts +128 -0
  485. package/src/client/form-enter-submit.ts +87 -0
  486. package/src/client/json.ts +37 -0
  487. package/src/client/multipart-upload.ts +51 -18
  488. package/src/client/post-form-bridge.ts +20 -14
  489. package/src/client/runtime-paths.ts +72 -0
  490. package/src/client/settings-bridge.ts +54 -10
  491. package/src/client/site-header-nav.js +84 -0
  492. package/src/client/sortable-list.ts +152 -0
  493. package/src/client/thread-context.ts +11 -5
  494. package/src/client/tiptap/__tests__/floating-position.test.ts +74 -0
  495. package/src/client/tiptap/__tests__/link-toolbar.test.ts +94 -0
  496. package/src/client/tiptap/__tests__/paste-media.test.ts +39 -0
  497. package/src/client/tiptap/bubble-menu.ts +98 -13
  498. package/src/client/tiptap/create-editor.ts +6 -0
  499. package/src/client/tiptap/extensions.ts +15 -4
  500. package/src/client/tiptap/floating-position.ts +167 -0
  501. package/src/client/tiptap/inline-image-upload.ts +73 -0
  502. package/src/client/tiptap/link-input-rules.ts +56 -0
  503. package/src/client/tiptap/link-toolbar.ts +60 -14
  504. package/src/client/tiptap/paste-media.ts +120 -0
  505. package/src/client/tiptap/slash-commands.ts +205 -52
  506. package/src/client/tiptap/toolbar-mode.ts +31 -0
  507. package/src/client/toast.ts +136 -16
  508. package/src/client/types/sortablejs.d.ts +4 -4
  509. package/src/client/upload-session.ts +270 -0
  510. package/src/client/upload-with-metadata.ts +10 -17
  511. package/src/client-auth.ts +38 -0
  512. package/src/client.ts +10 -29
  513. package/src/db/__tests__/d1-query.test.ts +84 -0
  514. package/src/db/__tests__/demo-canonical-snapshot.test.ts +78 -0
  515. package/src/db/__tests__/demo-env-loader.test.ts +114 -0
  516. package/src/db/__tests__/dialect.test.ts +42 -0
  517. package/src/db/__tests__/migration-artifacts.test.ts +82 -0
  518. package/src/db/__tests__/migration-rehearsal.test.ts +73 -0
  519. package/src/db/__tests__/migration-runner.test.ts +72 -0
  520. package/src/db/__tests__/migrations.test.ts +293 -8
  521. package/src/db/__tests__/r2-query.test.ts +102 -0
  522. package/src/db/__tests__/wrangler-config.test.ts +86 -0
  523. package/src/db/backfills/0000_normalize_legacy_time_zone_values.sql +38 -0
  524. package/src/db/backfills/README.md +11 -0
  525. package/src/db/dialect.ts +71 -0
  526. package/src/db/index.ts +84 -2
  527. package/src/db/migrations/0000_baseline.sql +35 -57
  528. package/src/db/migrations/0002_site_aware_core.sql +354 -0
  529. package/src/db/migrations/0003_fts_site_aware.sql +38 -0
  530. package/src/db/migrations/0004_perpetual_eternity.sql +29 -0
  531. package/src/db/migrations/meta/0000_snapshot.json +134 -138
  532. package/src/db/migrations/meta/0001_snapshot.json +135 -139
  533. package/src/db/migrations/meta/0002_snapshot.json +1717 -0
  534. package/src/db/migrations/meta/0003_snapshot.json +1717 -0
  535. package/src/db/migrations/meta/0004_snapshot.json +1897 -0
  536. package/src/db/migrations/meta/_journal.json +23 -2
  537. package/src/db/migrations/pg/0000_pg_site_aware_baseline.sql +306 -0
  538. package/src/db/migrations/pg/0001_pg_search_support.sql +8 -0
  539. package/src/db/migrations/pg/0002_breezy_lockjaw.sql +29 -0
  540. package/src/db/migrations/pg/README.md +8 -0
  541. package/src/db/migrations/pg/meta/0000_snapshot.json +2195 -0
  542. package/src/db/migrations/pg/meta/0001_snapshot.json +2248 -0
  543. package/src/db/migrations/pg/meta/0002_snapshot.json +2476 -0
  544. package/src/db/migrations/pg/meta/_journal.json +27 -0
  545. package/src/db/pg/__tests__/node.test.ts +58 -0
  546. package/src/db/pg/node.ts +151 -0
  547. package/src/db/pg/schema.ts +739 -0
  548. package/src/db/raw-query.ts +15 -0
  549. package/src/db/rehearsal-fixtures/demo-current.json +25 -0
  550. package/src/db/rehearsal-fixtures/demo-current.sql +142 -0
  551. package/src/db/schema-bundle.ts +12 -0
  552. package/src/db/schema.ts +309 -128
  553. package/src/db/sqlite/schema.ts +1 -0
  554. package/src/i18n/__tests__/context.test.tsx +35 -0
  555. package/src/i18n/context.tsx +10 -2
  556. package/src/i18n/locales/en.po +1778 -231
  557. package/src/i18n/locales/en.ts +1 -1
  558. package/src/i18n/locales/glossary.zh-Hans.yml +116 -0
  559. package/src/i18n/locales/glossary.zh-Hant.yml +116 -0
  560. package/src/i18n/locales/zh-Hans.po +1819 -272
  561. package/src/i18n/locales/zh-Hans.ts +1 -1
  562. package/src/i18n/locales/zh-Hant.po +1823 -276
  563. package/src/i18n/locales/zh-Hant.ts +1 -1
  564. package/src/index.ts +4 -0
  565. package/src/lib/__tests__/asset-path.test.ts +54 -0
  566. package/src/lib/__tests__/blurhash-placeholder.test.ts +25 -1
  567. package/src/lib/__tests__/collection-sort.test.ts +45 -0
  568. package/src/lib/__tests__/constants.test.ts +1 -0
  569. package/src/lib/__tests__/favicon.test.ts +1 -1
  570. package/src/lib/__tests__/feed.test.ts +147 -0
  571. package/src/lib/__tests__/hosted-control-plane.test.ts +106 -0
  572. package/src/lib/__tests__/hosted-domain.test.ts +78 -0
  573. package/src/lib/__tests__/hosted-signin.test.ts +103 -0
  574. package/src/lib/__tests__/icons.test.ts +14 -172
  575. package/src/lib/__tests__/image.test.ts +23 -4
  576. package/src/lib/__tests__/jant-branding.test.ts +160 -0
  577. package/src/lib/__tests__/markdown-to-tiptap.test.ts +27 -0
  578. package/src/lib/__tests__/page-title.test.ts +20 -0
  579. package/src/lib/__tests__/password.test.ts +60 -0
  580. package/src/lib/__tests__/post-display.test.ts +99 -0
  581. package/src/lib/__tests__/post-meta.test.ts +126 -0
  582. package/src/lib/__tests__/public-storage.test.ts +75 -0
  583. package/src/lib/__tests__/resolve-config.test.ts +156 -28
  584. package/src/lib/__tests__/schemas.test.ts +268 -22
  585. package/src/lib/__tests__/search-snippet.test.ts +43 -0
  586. package/src/lib/__tests__/site-resolution.test.ts +39 -0
  587. package/src/lib/__tests__/slug-format.test.ts +46 -0
  588. package/src/lib/__tests__/startup-config.test.ts +87 -0
  589. package/src/lib/__tests__/storage.test.ts +218 -1
  590. package/src/lib/__tests__/theme.test.ts +50 -13
  591. package/src/lib/__tests__/time.test.ts +44 -0
  592. package/src/lib/__tests__/timeline.test.ts +410 -4
  593. package/src/lib/__tests__/timezones.test.ts +51 -18
  594. package/src/lib/__tests__/tiptap-to-markdown.test.ts +23 -0
  595. package/src/lib/__tests__/url.test.ts +79 -1
  596. package/src/lib/__tests__/view.test.ts +74 -13
  597. package/src/lib/asset-path.ts +87 -0
  598. package/src/lib/blurhash-placeholder.ts +56 -1
  599. package/src/lib/collection-sort.ts +61 -0
  600. package/src/lib/confirm.ts +37 -0
  601. package/src/lib/constants.ts +3 -6
  602. package/src/lib/crypto.ts +47 -0
  603. package/src/lib/env.ts +245 -0
  604. package/src/lib/errors.ts +25 -9
  605. package/src/lib/favicon.ts +2 -1
  606. package/src/lib/featured-icons.ts +33 -0
  607. package/src/lib/feed.ts +73 -24
  608. package/src/lib/hosted-control-plane-sync.ts +27 -0
  609. package/src/lib/hosted-control-plane.ts +99 -0
  610. package/src/lib/hosted-domain-check.ts +98 -0
  611. package/src/lib/hosted-domain.ts +61 -0
  612. package/src/lib/hosted-signin.ts +115 -0
  613. package/src/lib/hosted-sso.ts +110 -0
  614. package/src/lib/icons.ts +10 -175
  615. package/src/lib/ids.ts +54 -0
  616. package/src/lib/image.ts +18 -7
  617. package/src/lib/jant-branding-generated.ts +947 -0
  618. package/src/lib/jant-branding.ts +520 -0
  619. package/src/lib/markdown-to-tiptap.ts +57 -0
  620. package/src/lib/media-helpers.ts +6 -2
  621. package/src/lib/navigation.ts +9 -2
  622. package/src/lib/page-title.ts +23 -0
  623. package/src/lib/pagination.ts +34 -0
  624. package/src/lib/password.ts +164 -0
  625. package/src/lib/post-display.ts +162 -0
  626. package/src/lib/post-meta.ts +95 -0
  627. package/src/lib/public-storage.ts +57 -0
  628. package/src/lib/render.tsx +22 -0
  629. package/src/lib/resolve-config.ts +78 -27
  630. package/src/lib/schemas.ts +264 -73
  631. package/src/lib/search-snippet.ts +110 -8
  632. package/src/lib/site-resolution.ts +28 -0
  633. package/src/lib/slug-format.ts +84 -0
  634. package/src/lib/slug.ts +1 -1
  635. package/src/lib/startup-config.ts +244 -0
  636. package/src/lib/storage.ts +722 -28
  637. package/src/lib/theme.ts +13 -6
  638. package/src/lib/time.ts +59 -15
  639. package/src/lib/timeline.ts +386 -62
  640. package/src/lib/timezones.ts +120 -50
  641. package/src/lib/tiptap-render.ts +1 -3
  642. package/src/lib/tiptap-to-markdown.ts +49 -0
  643. package/src/lib/upload.ts +216 -11
  644. package/src/lib/url.ts +210 -0
  645. package/src/lib/view.ts +87 -31
  646. package/src/middleware/__tests__/auth.test.ts +216 -14
  647. package/src/middleware/__tests__/error-handler.test.ts +37 -0
  648. package/src/middleware/__tests__/onboarding.test.ts +9 -0
  649. package/src/middleware/__tests__/secure-headers.test.ts +111 -0
  650. package/src/middleware/auth.ts +156 -28
  651. package/src/middleware/config.ts +29 -9
  652. package/src/middleware/error-handler.ts +16 -1
  653. package/src/middleware/onboarding.ts +15 -6
  654. package/src/middleware/secure-headers.ts +136 -25
  655. package/src/node/__tests__/cli-db-execute-file.test.ts +109 -0
  656. package/src/node/__tests__/cli-deploy.test.ts +124 -0
  657. package/src/node/__tests__/cli-export.test.ts +66 -0
  658. package/src/node/__tests__/cli-reset-password.test.ts +91 -0
  659. package/src/node/__tests__/cli-runtime-target.test.ts +37 -0
  660. package/src/node/__tests__/cli-site-selection.test.ts +133 -0
  661. package/src/node/__tests__/cli-site-snapshot.test.ts +636 -0
  662. package/src/node/__tests__/cli-site-token-env.test.ts +137 -0
  663. package/src/node/__tests__/runtime.test.ts +346 -0
  664. package/src/node/index.ts +18 -0
  665. package/src/node/request-handler.ts +668 -0
  666. package/src/node/runtime.ts +88 -0
  667. package/src/preset.css +73 -16
  668. package/src/routes/__tests__/compose.test.ts +77 -4
  669. package/src/routes/api/__tests__/attachments.test.ts +92 -0
  670. package/src/routes/api/__tests__/collections.test.ts +138 -24
  671. package/src/routes/api/__tests__/nav-items.test.ts +81 -20
  672. package/src/routes/api/__tests__/posts.test.ts +315 -57
  673. package/src/routes/api/__tests__/search.test.ts +25 -1
  674. package/src/routes/api/__tests__/settings.test.ts +380 -2
  675. package/src/routes/api/__tests__/upload-multipart.test.ts +216 -26
  676. package/src/routes/api/__tests__/uploads.test.ts +420 -0
  677. package/src/routes/api/attachments.ts +24 -0
  678. package/src/routes/api/collections.ts +59 -28
  679. package/src/routes/api/custom-urls.ts +6 -5
  680. package/src/routes/api/export.ts +37 -7
  681. package/src/routes/api/internal/__tests__/api-tokens.test.ts +55 -0
  682. package/src/routes/api/internal/__tests__/sites.test.ts +472 -0
  683. package/src/routes/api/internal/__tests__/uploads.test.ts +131 -0
  684. package/src/routes/api/internal/api-tokens.ts +17 -0
  685. package/src/routes/api/internal/sites.ts +241 -0
  686. package/src/routes/api/internal/uploads.ts +37 -0
  687. package/src/routes/api/nav-items.ts +24 -15
  688. package/src/routes/api/posts.ts +222 -74
  689. package/src/routes/api/search.ts +50 -9
  690. package/src/routes/api/settings.ts +141 -7
  691. package/src/routes/api/upload-multipart.ts +105 -16
  692. package/src/routes/api/upload.ts +90 -21
  693. package/src/routes/api/uploads.ts +247 -0
  694. package/src/routes/auth/__tests__/dev.test.ts +135 -0
  695. package/src/routes/auth/__tests__/hosted-sso.test.ts +136 -0
  696. package/src/routes/auth/__tests__/setup.test.ts +73 -49
  697. package/src/routes/auth/dev.ts +72 -0
  698. package/src/routes/auth/hosted-sso.ts +64 -0
  699. package/src/routes/auth/reset.tsx +41 -7
  700. package/src/routes/auth/setup.tsx +37 -38
  701. package/src/routes/auth/signin.tsx +36 -6
  702. package/src/routes/compose.tsx +25 -38
  703. package/src/routes/dash/__tests__/font-theme.test.ts +45 -34
  704. package/src/routes/dash/__tests__/settings-avatar.test.ts +24 -11
  705. package/src/routes/dash/custom-urls.tsx +245 -67
  706. package/src/routes/dash/settings.tsx +530 -139
  707. package/src/routes/feed/__tests__/rss.test.ts +200 -51
  708. package/src/routes/feed/__tests__/sitemap.test.ts +64 -0
  709. package/src/routes/feed/rss.ts +112 -48
  710. package/src/routes/feed/sitemap.ts +16 -6
  711. package/src/routes/hosted/__tests__/domain-check.test.ts +94 -0
  712. package/src/routes/hosted/domain-check.ts +39 -0
  713. package/src/routes/pages/__tests__/collections.test.ts +27 -21
  714. package/src/routes/pages/__tests__/featured.test.ts +9 -2
  715. package/src/routes/pages/archive.tsx +79 -33
  716. package/src/routes/pages/brand.tsx +119 -0
  717. package/src/routes/pages/collection.tsx +143 -59
  718. package/src/routes/pages/collections.tsx +46 -20
  719. package/src/routes/pages/featured.tsx +31 -39
  720. package/src/routes/pages/home.tsx +50 -25
  721. package/src/routes/pages/latest.tsx +20 -4
  722. package/src/routes/pages/new.tsx +10 -6
  723. package/src/routes/pages/page.tsx +27 -53
  724. package/src/routes/pages/partials.tsx +83 -0
  725. package/src/routes/pages/search.tsx +11 -9
  726. package/src/routes/pages/theme-sample.tsx +98 -0
  727. package/src/runtime/__tests__/node.test.ts +258 -0
  728. package/src/runtime/cloudflare.ts +102 -0
  729. package/src/runtime/index.ts +14 -0
  730. package/src/runtime/node.ts +187 -0
  731. package/src/runtime/site.ts +169 -0
  732. package/src/services/__tests__/api-token.test.ts +5 -2
  733. package/src/services/__tests__/auth.test.ts +348 -0
  734. package/src/services/__tests__/collection.test.ts +154 -16
  735. package/src/services/__tests__/custom-url.test.ts +10 -3
  736. package/src/services/__tests__/hosted-handoff.test.ts +123 -0
  737. package/src/services/__tests__/media.test.ts +256 -33
  738. package/src/services/__tests__/navigation.test.ts +89 -4
  739. package/src/services/__tests__/post-timeline.test.ts +111 -2
  740. package/src/services/__tests__/post.test.ts +463 -38
  741. package/src/services/__tests__/search.test.ts +145 -14
  742. package/src/services/__tests__/settings.test.ts +162 -2
  743. package/src/services/__tests__/site-admin.test.ts +48 -0
  744. package/src/services/__tests__/site-profile.test.ts +175 -0
  745. package/src/services/api-token.ts +44 -11
  746. package/src/services/auth.ts +211 -9
  747. package/src/services/bootstrap.ts +68 -0
  748. package/src/services/collection.ts +501 -100
  749. package/src/services/custom-url.ts +42 -12
  750. package/src/services/export.ts +2269 -320
  751. package/src/services/hosted-handoff.ts +184 -0
  752. package/src/services/index.ts +110 -12
  753. package/src/services/media.ts +309 -39
  754. package/src/services/navigation.ts +141 -20
  755. package/src/services/path.ts +45 -9
  756. package/src/services/post.ts +1529 -246
  757. package/src/services/search.ts +222 -29
  758. package/src/services/settings.ts +202 -48
  759. package/src/services/site-admin.ts +825 -0
  760. package/src/services/site-member.ts +74 -0
  761. package/src/services/site-profile.ts +52 -0
  762. package/src/services/site.ts +250 -0
  763. package/src/services/upload-session.ts +707 -0
  764. package/src/style-cjk-tc.css +3 -0
  765. package/src/style-cjk.css +3 -0
  766. package/src/styles/components.css +647 -16
  767. package/src/styles/fonts/latin.css +4 -0
  768. package/src/styles/fonts/noto-serif-sc/400/02629e5d0a9860b7fe32ec1f0563213a.woff2 +0 -0
  769. package/src/styles/fonts/noto-serif-sc/400/031089da45fbfb7dc18ac827bef4c56e.woff2 +0 -0
  770. package/src/styles/fonts/noto-serif-sc/400/03ac785139320b7b13bac9c150bf72bf.woff2 +0 -0
  771. package/src/styles/fonts/noto-serif-sc/400/0b1f83a3c7e715560a55ad9eb0fb1c94.woff2 +0 -0
  772. package/src/styles/fonts/noto-serif-sc/400/14b040a2dda256936bc7b3470e548394.woff2 +0 -0
  773. package/src/styles/fonts/noto-serif-sc/400/189f272ea2600c74d576b7b15c014922.woff2 +0 -0
  774. package/src/styles/fonts/noto-serif-sc/400/1fbccc182322b513f57cd156a9a491b0.woff2 +0 -0
  775. package/src/styles/fonts/noto-serif-sc/400/21e0a71d86be7b8a9e812e7af09dd061.woff2 +0 -0
  776. package/src/styles/fonts/noto-serif-sc/400/265e048cc9f2f8a711ba585a534d5351.woff2 +0 -0
  777. package/src/styles/fonts/noto-serif-sc/400/2ae2ca951489c9d50cde5b36a2a5515b.woff2 +0 -0
  778. package/src/styles/fonts/noto-serif-sc/400/2ba802b14f21a58fc61606c88fa93373.woff2 +0 -0
  779. package/src/styles/fonts/noto-serif-sc/400/2deb444546774c3a3ab38c75eb69cdfb.woff2 +0 -0
  780. package/src/styles/fonts/noto-serif-sc/400/2fbccf9a3853eb59db1a825e044515fd.woff2 +0 -0
  781. package/src/styles/fonts/noto-serif-sc/400/2ff009fa8701505d7f3dc6c83763f019.woff2 +0 -0
  782. package/src/styles/fonts/noto-serif-sc/400/31424fe5d54692e7c8b38021ccb8597c.woff2 +0 -0
  783. package/src/styles/fonts/noto-serif-sc/400/360c190344c26278bbc50e2f4d6a2b3f.woff2 +0 -0
  784. package/src/styles/fonts/noto-serif-sc/400/39b0bbc910af9d2d6dcd8bd4abd6387d.woff2 +0 -0
  785. package/src/styles/fonts/noto-serif-sc/400/3adc8f6350cf5067bcb6dc5e44c45d41.woff2 +0 -0
  786. package/src/styles/fonts/noto-serif-sc/400/3bed5bd57de8f738e53cddaea88983d9.woff2 +0 -0
  787. package/src/styles/fonts/noto-serif-sc/400/3c201fd8d1bb20abe7d06b940e83a4d9.woff2 +0 -0
  788. package/src/styles/fonts/noto-serif-sc/400/3d385ea0880df7204258e290648ec012.woff2 +0 -0
  789. package/src/styles/fonts/noto-serif-sc/400/3dc1a4f0d7af59e16b5162a2b077a442.woff2 +0 -0
  790. package/src/styles/fonts/noto-serif-sc/400/3dc68e473fe23bd076dd46785cd23583.woff2 +0 -0
  791. package/src/styles/fonts/noto-serif-sc/400/435b7dca567809813fcb395a27ed83a0.woff2 +0 -0
  792. package/src/styles/fonts/noto-serif-sc/400/43693195e775d515689fa035394067fd.woff2 +0 -0
  793. package/src/styles/fonts/noto-serif-sc/400/43fb49e5b79ee7e553869d84e6e08b1e.woff2 +0 -0
  794. package/src/styles/fonts/noto-serif-sc/400/44dcbbc3cc8f22e613b342d691511ab6.woff2 +0 -0
  795. package/src/styles/fonts/noto-serif-sc/400/474fac21b12b7efd71f7c321578878b0.woff2 +0 -0
  796. package/src/styles/fonts/noto-serif-sc/400/4a23fe6e82fd496b5eb20401b6164efe.woff2 +0 -0
  797. package/src/styles/fonts/noto-serif-sc/400/4bc743968cf1c3ce5711de67ef1ccc4d.woff2 +0 -0
  798. package/src/styles/fonts/noto-serif-sc/400/4cf0f292f3358bd2f73b1cf4ec1476f3.woff2 +0 -0
  799. package/src/styles/fonts/noto-serif-sc/400/501f66f24bce8234441954de1b568403.woff2 +0 -0
  800. package/src/styles/fonts/noto-serif-sc/400/53a88404451448cd2e620a0ca0e45a20.woff2 +0 -0
  801. package/src/styles/fonts/noto-serif-sc/400/557cd00c5d6827e13d72a0c71b23587b.woff2 +0 -0
  802. package/src/styles/fonts/noto-serif-sc/400/563fa31542d553f25abab65cf7f81e1d.woff2 +0 -0
  803. package/src/styles/fonts/noto-serif-sc/400/56e1c4734bbbb38af2fbc262bf6e98f2.woff2 +0 -0
  804. package/src/styles/fonts/noto-serif-sc/400/5947f5da5da9a352a2b534ee64bfc29a.woff2 +0 -0
  805. package/src/styles/fonts/noto-serif-sc/400/5a10741e41259e235841440394c0763d.woff2 +0 -0
  806. package/src/styles/fonts/noto-serif-sc/400/60d8b0805a0a8c54a6cca216004beff5.woff2 +0 -0
  807. package/src/styles/fonts/noto-serif-sc/400/61bf4287453da4025d03fa6b2dba66ca.woff2 +0 -0
  808. package/src/styles/fonts/noto-serif-sc/400/638369541268ed5a10af97ad77498c73.woff2 +0 -0
  809. package/src/styles/fonts/noto-serif-sc/400/687d0f0f90a9b23e40102e16ad8e9836.woff2 +0 -0
  810. package/src/styles/fonts/noto-serif-sc/400/6e2164fad867d166de2e5b274f04a563.woff2 +0 -0
  811. package/src/styles/fonts/noto-serif-sc/400/6eefc9d430171c1e1e4034ecadee31c8.woff2 +0 -0
  812. package/src/styles/fonts/noto-serif-sc/400/71eafb8fbe3a734283517e230ad8b6db.woff2 +0 -0
  813. package/src/styles/fonts/noto-serif-sc/400/751f54dbb115140d5b645a6ba4aff5d3.woff2 +0 -0
  814. package/src/styles/fonts/noto-serif-sc/400/7609e7e74dd4d916a7abc7ecc7d95f7e.woff2 +0 -0
  815. package/src/styles/fonts/noto-serif-sc/400/76b9d6fe838ae4151d95ce7200aa2bf6.woff2 +0 -0
  816. package/src/styles/fonts/noto-serif-sc/400/78ce29fed872e44fc9014d94875d2aac.woff2 +0 -0
  817. package/src/styles/fonts/noto-serif-sc/400/79a85a253e9b3f12d2e2cb15e16b3003.woff2 +0 -0
  818. package/src/styles/fonts/noto-serif-sc/400/7caa14a095a6bc313aab780fe4ff7999.woff2 +0 -0
  819. package/src/styles/fonts/noto-serif-sc/400/7d138084cf03c14116b11297fce0e3e3.woff2 +0 -0
  820. package/src/styles/fonts/noto-serif-sc/400/880162ae92cd9e120eb4e4e11fae459d.woff2 +0 -0
  821. package/src/styles/fonts/noto-serif-sc/400/8f2b960c2823670e94f5b08aa65657e6.woff2 +0 -0
  822. package/src/styles/fonts/noto-serif-sc/400/8f89f57230d184f92a36e241874229d7.woff2 +0 -0
  823. package/src/styles/fonts/noto-serif-sc/400/9180de34b48b325200a97e267befff32.woff2 +0 -0
  824. package/src/styles/fonts/noto-serif-sc/400/95df3b9f681d9df411c30aea5b24f2e0.woff2 +0 -0
  825. package/src/styles/fonts/noto-serif-sc/400/975af5a496e8d87d821910aa9fe4d598.woff2 +0 -0
  826. package/src/styles/fonts/noto-serif-sc/400/97a874bbf55ce89a4ab7cd27c7e938b1.woff2 +0 -0
  827. package/src/styles/fonts/noto-serif-sc/400/9fd53607094e329fa8e5c785b3ff0f1a.woff2 +0 -0
  828. package/src/styles/fonts/noto-serif-sc/400/a578742770fcd2226e3c45b5b6efdcb0.woff2 +0 -0
  829. package/src/styles/fonts/noto-serif-sc/400/ae401fb4db80d5ff5cd3f8d9bc811070.woff2 +0 -0
  830. package/src/styles/fonts/noto-serif-sc/400/b0ab3a7f319ce6dd3c9a4de2674e7c72.woff2 +0 -0
  831. package/src/styles/fonts/noto-serif-sc/400/b159deb135e9946eea0572d52778170b.woff2 +0 -0
  832. package/src/styles/fonts/noto-serif-sc/400/b91450304d9ac44f5c6e0da0792e055d.woff2 +0 -0
  833. package/src/styles/fonts/noto-serif-sc/400/baa325551b381c5e035ef143e56d4abe.woff2 +0 -0
  834. package/src/styles/fonts/noto-serif-sc/400/c0c7836749e585cee24ab2f8457c5b01.woff2 +0 -0
  835. package/src/styles/fonts/noto-serif-sc/400/c2ac4ef1860812036ca2b8c4e4089bdc.woff2 +0 -0
  836. package/src/styles/fonts/noto-serif-sc/400/c31019c08bd22464f7a88f090281404c.woff2 +0 -0
  837. package/src/styles/fonts/noto-serif-sc/400/c57ee3b49b7e45b995539a6b2c51f138.woff2 +0 -0
  838. package/src/styles/fonts/noto-serif-sc/400/cd75ca47da9ae4c0899e37d4c543319b.woff2 +0 -0
  839. package/src/styles/fonts/noto-serif-sc/400/d12ce1d8445213317f9163283e58a05d.woff2 +0 -0
  840. package/src/styles/fonts/noto-serif-sc/400/d28fb13acf9ced9f0657fd4012c81cd2.woff2 +0 -0
  841. package/src/styles/fonts/noto-serif-sc/400/d3714e6b90de8e2085dfb2514464dd6a.woff2 +0 -0
  842. package/src/styles/fonts/noto-serif-sc/400/d3beff96216c8af1aa79246476b6a323.woff2 +0 -0
  843. package/src/styles/fonts/noto-serif-sc/400/d3e311f30c811dc339c262a79a51877e.woff2 +0 -0
  844. package/src/styles/fonts/noto-serif-sc/400/d51f4cdc83711e510f5d25f03235597e.woff2 +0 -0
  845. package/src/styles/fonts/noto-serif-sc/400/d5df4a8dfd4328c67d933b3912c6ad0f.woff2 +0 -0
  846. package/src/styles/fonts/noto-serif-sc/400/dccda6a2e2db3b530788bdfa2acd1979.woff2 +0 -0
  847. package/src/styles/fonts/noto-serif-sc/400/f5237486197aeff59341a1ff38b8eff8.woff2 +0 -0
  848. package/src/styles/fonts/noto-serif-sc/400/f553d54ef931066712d8f3f0ce018e1b.woff2 +0 -0
  849. package/src/styles/fonts/noto-serif-sc/400/f649cba8e14c33d6bf2265483b14b895.woff2 +0 -0
  850. package/src/styles/fonts/noto-serif-sc/400/f6839df1bf7cb4dc8d27e5ea55bbe633.woff2 +0 -0
  851. package/src/styles/fonts/noto-serif-sc/400/f6b7304e028980f77a7f7007bb540abd.woff2 +0 -0
  852. package/src/styles/fonts/noto-serif-sc/400/f8db8bef0a6e1178835d350ae0d384a1.woff2 +0 -0
  853. package/src/styles/fonts/noto-serif-sc/400/fb4649a82c50620773d79820e2e5ff13.woff2 +0 -0
  854. package/src/styles/fonts/noto-serif-sc/400/fb9402d6c6357a825affc402f14d5a7e.woff2 +0 -0
  855. package/src/styles/fonts/noto-serif-sc/700/0041f681602cc834bb5c55ced0155b8e.woff2 +0 -0
  856. package/src/styles/fonts/noto-serif-sc/700/00c9ac960d866ffaf8a866e5939024e2.woff2 +0 -0
  857. package/src/styles/fonts/noto-serif-sc/700/02e48e353415a00e0f556608cab33a43.woff2 +0 -0
  858. package/src/styles/fonts/noto-serif-sc/700/02faf6bb0ab4d56ada037c0bbaf9b9f7.woff2 +0 -0
  859. package/src/styles/fonts/noto-serif-sc/700/057a3d44d7fc606f113d863376d0ecf0.woff2 +0 -0
  860. package/src/styles/fonts/noto-serif-sc/700/10f2b44b3711d3f5bdcc30d373b543d1.woff2 +0 -0
  861. package/src/styles/fonts/noto-serif-sc/700/14c1506106d92621bafb11016735194e.woff2 +0 -0
  862. package/src/styles/fonts/noto-serif-sc/700/154a2c266902003bd8b7449386b10776.woff2 +0 -0
  863. package/src/styles/fonts/noto-serif-sc/700/19e39850472250bfdbce654d30859879.woff2 +0 -0
  864. package/src/styles/fonts/noto-serif-sc/700/1b5d0d740450fb996749464c9b882025.woff2 +0 -0
  865. package/src/styles/fonts/noto-serif-sc/700/1bca0a2a8840ad0ee9414940593db144.woff2 +0 -0
  866. package/src/styles/fonts/noto-serif-sc/700/1cda27dcaab977ae4ef5d5ab2a10ae03.woff2 +0 -0
  867. package/src/styles/fonts/noto-serif-sc/700/1f4bc38a1c50f55f335f5411cae47696.woff2 +0 -0
  868. package/src/styles/fonts/noto-serif-sc/700/2022cf097cb952d9fe75b53b4587d2c3.woff2 +0 -0
  869. package/src/styles/fonts/noto-serif-sc/700/2240a3c43ca5ef59ae3c348c7884792f.woff2 +0 -0
  870. package/src/styles/fonts/noto-serif-sc/700/280e3d2b58e9ad3501816072e01b0c13.woff2 +0 -0
  871. package/src/styles/fonts/noto-serif-sc/700/2874d07e228da9583b0e73646dacd498.woff2 +0 -0
  872. package/src/styles/fonts/noto-serif-sc/700/29d49891713a2785a3a383001cf58c59.woff2 +0 -0
  873. package/src/styles/fonts/noto-serif-sc/700/2d81eb6ab0ebbc0cabfb3a3341ba8800.woff2 +0 -0
  874. package/src/styles/fonts/noto-serif-sc/700/3179006d1c7ebfa50d27482a2859d9a0.woff2 +0 -0
  875. package/src/styles/fonts/noto-serif-sc/700/34c2edb3c37f71258f5c4a31091f0c6c.woff2 +0 -0
  876. package/src/styles/fonts/noto-serif-sc/700/389a950f2a1211946d294716e679e381.woff2 +0 -0
  877. package/src/styles/fonts/noto-serif-sc/700/42a74b6a625bbf0a9616ed4db3152c88.woff2 +0 -0
  878. package/src/styles/fonts/noto-serif-sc/700/457485e72835364662dfead6281638c1.woff2 +0 -0
  879. package/src/styles/fonts/noto-serif-sc/700/488846410760fe128dae939836ca5423.woff2 +0 -0
  880. package/src/styles/fonts/noto-serif-sc/700/48eb0a91e50c7f026e248c64145e72af.woff2 +0 -0
  881. package/src/styles/fonts/noto-serif-sc/700/4b0e79ba18b2ce424fa93e84996d7cba.woff2 +0 -0
  882. package/src/styles/fonts/noto-serif-sc/700/54e301f412730f391225db59dae1c8d5.woff2 +0 -0
  883. package/src/styles/fonts/noto-serif-sc/700/597d69d0710e0178b162afb0a0c20401.woff2 +0 -0
  884. package/src/styles/fonts/noto-serif-sc/700/5cc23a76e122d0ad2f7cede41bc35b27.woff2 +0 -0
  885. package/src/styles/fonts/noto-serif-sc/700/5d48855bed5f3554eff91b573d7376ac.woff2 +0 -0
  886. package/src/styles/fonts/noto-serif-sc/700/5ddcbe564b29ef08632e1aeb33455435.woff2 +0 -0
  887. package/src/styles/fonts/noto-serif-sc/700/605667a998e91e2b6a4a3cd7c31fe5a9.woff2 +0 -0
  888. package/src/styles/fonts/noto-serif-sc/700/67f32ceea9e78e5109f87724ad886010.woff2 +0 -0
  889. package/src/styles/fonts/noto-serif-sc/700/68f2fab82ec8e9291f08c3145111549c.woff2 +0 -0
  890. package/src/styles/fonts/noto-serif-sc/700/69519ada3f3f74ca20aacb8af48ab6b4.woff2 +0 -0
  891. package/src/styles/fonts/noto-serif-sc/700/6a6e884fb2b65ec5b4a3d5ecd0d01a6a.woff2 +0 -0
  892. package/src/styles/fonts/noto-serif-sc/700/6e1a8b45b01939088c3a8cfcf8c10681.woff2 +0 -0
  893. package/src/styles/fonts/noto-serif-sc/700/70adaf50c56d5ff859c64d35e0f1e34e.woff2 +0 -0
  894. package/src/styles/fonts/noto-serif-sc/700/72ee453ac0e19bd2c631c8921c44e3de.woff2 +0 -0
  895. package/src/styles/fonts/noto-serif-sc/700/753b5f6fb254bacb6618ace25af3df60.woff2 +0 -0
  896. package/src/styles/fonts/noto-serif-sc/700/76d4244186d118eea245d1385a4de2ec.woff2 +0 -0
  897. package/src/styles/fonts/noto-serif-sc/700/7a86b155111ba20f3e87306ff6beac77.woff2 +0 -0
  898. package/src/styles/fonts/noto-serif-sc/700/7b1e76975b0984e6f83e3f9f8069e784.woff2 +0 -0
  899. package/src/styles/fonts/noto-serif-sc/700/7b6c60131822a0e4d36d980d52509d4e.woff2 +0 -0
  900. package/src/styles/fonts/noto-serif-sc/700/7cbda564cb2dd4799ab9e89d51286aa7.woff2 +0 -0
  901. package/src/styles/fonts/noto-serif-sc/700/7f60eefa15956d6f06dd92404887d58c.woff2 +0 -0
  902. package/src/styles/fonts/noto-serif-sc/700/7f8c15e0ecb102738981d9fa4cb6b921.woff2 +0 -0
  903. package/src/styles/fonts/noto-serif-sc/700/812b5a4b87f3a7b4afc1cfebc864f413.woff2 +0 -0
  904. package/src/styles/fonts/noto-serif-sc/700/812dfb7f8144d01b3cc9d5ce0b472f40.woff2 +0 -0
  905. package/src/styles/fonts/noto-serif-sc/700/84742b1ede4f0bb6d27131298eba21b4.woff2 +0 -0
  906. package/src/styles/fonts/noto-serif-sc/700/8555f0285e3d28e95e2fc0ccccd9caff.woff2 +0 -0
  907. package/src/styles/fonts/noto-serif-sc/700/8e04e64c8f68d292a18d4160fbde8671.woff2 +0 -0
  908. package/src/styles/fonts/noto-serif-sc/700/958efb9b2fa2ea0008ffef009885f9f8.woff2 +0 -0
  909. package/src/styles/fonts/noto-serif-sc/700/9ca9b71010a5faeee7047ef97aeee13b.woff2 +0 -0
  910. package/src/styles/fonts/noto-serif-sc/700/9cd0b77920b9d6c64eb686493123fc76.woff2 +0 -0
  911. package/src/styles/fonts/noto-serif-sc/700/9ebd27835ffcbd794e67151ab046ce68.woff2 +0 -0
  912. package/src/styles/fonts/noto-serif-sc/700/a077f51cfb5cffb4ff4d8e229c0e9e79.woff2 +0 -0
  913. package/src/styles/fonts/noto-serif-sc/700/a397997b579d3945c9c70a979c17a8ad.woff2 +0 -0
  914. package/src/styles/fonts/noto-serif-sc/700/a68d9d5027803832bb28e78cdcd04949.woff2 +0 -0
  915. package/src/styles/fonts/noto-serif-sc/700/a904b05966368bcf90b632c7c2e5f76b.woff2 +0 -0
  916. package/src/styles/fonts/noto-serif-sc/700/aa218a2c45f3749537ce876201e5152b.woff2 +0 -0
  917. package/src/styles/fonts/noto-serif-sc/700/aa28db16818f9eaa8c817f289e1c3270.woff2 +0 -0
  918. package/src/styles/fonts/noto-serif-sc/700/aa96d698491c2540e2dcf7009c65c456.woff2 +0 -0
  919. package/src/styles/fonts/noto-serif-sc/700/ae25c41034ddc1a9e0b41f5034c9aa4b.woff2 +0 -0
  920. package/src/styles/fonts/noto-serif-sc/700/ae289ae3f8cdb54a3a6c07174517afec.woff2 +0 -0
  921. package/src/styles/fonts/noto-serif-sc/700/b02dfa2aa52cbdb1b2f11a9f44335469.woff2 +0 -0
  922. package/src/styles/fonts/noto-serif-sc/700/b2e326f7f9b807451bf9c745df747efe.woff2 +0 -0
  923. package/src/styles/fonts/noto-serif-sc/700/c33c59feccf391f0c5f1f5d24e36d1fe.woff2 +0 -0
  924. package/src/styles/fonts/noto-serif-sc/700/c82fd9456d7465b5e5bd3659e9b14c55.woff2 +0 -0
  925. package/src/styles/fonts/noto-serif-sc/700/c90b7b65d2b9696fbf3a506738f94d68.woff2 +0 -0
  926. package/src/styles/fonts/noto-serif-sc/700/cba6ad3981cb7861428d4be169ee8124.woff2 +0 -0
  927. package/src/styles/fonts/noto-serif-sc/700/cc26525aa2af1f0b929af32ce50a7fba.woff2 +0 -0
  928. package/src/styles/fonts/noto-serif-sc/700/cd0e7b51eddb22a77a09b025c0281434.woff2 +0 -0
  929. package/src/styles/fonts/noto-serif-sc/700/cd6d074f3957d58bac58437fc97e5e33.woff2 +0 -0
  930. package/src/styles/fonts/noto-serif-sc/700/cef249b6d013fb0cc0d574176bc23811.woff2 +0 -0
  931. package/src/styles/fonts/noto-serif-sc/700/d0bd387fda28e58d3c9b3efa2468dd8a.woff2 +0 -0
  932. package/src/styles/fonts/noto-serif-sc/700/da93ae099ff3b7aae27b3f674d3fc721.woff2 +0 -0
  933. package/src/styles/fonts/noto-serif-sc/700/e264213b9e102dabc603adb6e4fda5e6.woff2 +0 -0
  934. package/src/styles/fonts/noto-serif-sc/700/e7c7ef3669ae48c0a736f06ca471e1d7.woff2 +0 -0
  935. package/src/styles/fonts/noto-serif-sc/700/e81a742cacef744130c40de1b90837d8.woff2 +0 -0
  936. package/src/styles/fonts/noto-serif-sc/700/e8b755172122d1d0a5dd453e96b0ff24.woff2 +0 -0
  937. package/src/styles/fonts/noto-serif-sc/700/e99280299c305402eaa5271a3e36c49b.woff2 +0 -0
  938. package/src/styles/fonts/noto-serif-sc/700/e9c66b085052ece66bfadf45f711d3e1.woff2 +0 -0
  939. package/src/styles/fonts/noto-serif-sc/700/ed7c6dafaa6d8bcf015ef0ca574837df.woff2 +0 -0
  940. package/src/styles/fonts/noto-serif-sc/700/f2900a1d30c3a33129f4e2225669bd0e.woff2 +0 -0
  941. package/src/styles/fonts/noto-serif-sc/700/fa7d3b99744d7f2dc9e00864a97a62d6.woff2 +0 -0
  942. package/src/styles/fonts/noto-serif-sc/700/fb0e90665980954719c2eb685b130bc0.woff2 +0 -0
  943. package/src/styles/fonts/noto-serif-sc/noto-serif-sc.css +3615 -0
  944. package/src/styles/fonts/noto-serif-tc/400/008ea9091e332c639ceb18874eacd60c.woff2 +0 -0
  945. package/src/styles/fonts/noto-serif-tc/400/053bd3d7aec0040d0cc50c261a1f4e3e.woff2 +0 -0
  946. package/src/styles/fonts/noto-serif-tc/400/0713613227cc4c686c45a279f8bdc166.woff2 +0 -0
  947. package/src/styles/fonts/noto-serif-tc/400/0e97f44ebc65384c346fe19bcc52fa20.woff2 +0 -0
  948. package/src/styles/fonts/noto-serif-tc/400/1139d32ae2bdeb26c0c8f31330aa9a9f.woff2 +0 -0
  949. package/src/styles/fonts/noto-serif-tc/400/15f8c0df47fd639d1b0d9bd5cf507c9b.woff2 +0 -0
  950. package/src/styles/fonts/noto-serif-tc/400/1668bd859ffe15bed7d5563117d8d5fb.woff2 +0 -0
  951. package/src/styles/fonts/noto-serif-tc/400/190e3f8632494e7c095117f26b1c811e.woff2 +0 -0
  952. package/src/styles/fonts/noto-serif-tc/400/19ad151c22ce1befe0a9ea643fbee570.woff2 +0 -0
  953. package/src/styles/fonts/noto-serif-tc/400/1c820b5295868008ca7c78afa5b7655d.woff2 +0 -0
  954. package/src/styles/fonts/noto-serif-tc/400/1fbe225742c69f4ba9ea5f74922f0ca1.woff2 +0 -0
  955. package/src/styles/fonts/noto-serif-tc/400/2a7cedfcd6e4c7cec36f4fd7b0f329c2.woff2 +0 -0
  956. package/src/styles/fonts/noto-serif-tc/400/2acea04a920f6af31e7db97052f563c6.woff2 +0 -0
  957. package/src/styles/fonts/noto-serif-tc/400/2e98b666924b8e0a09d1aeeefd24bdd2.woff2 +0 -0
  958. package/src/styles/fonts/noto-serif-tc/400/2fd3fceb6faed5e3db768e88d7614dca.woff2 +0 -0
  959. package/src/styles/fonts/noto-serif-tc/400/35cf5dd04315e0b906e1a413d7905a2f.woff2 +0 -0
  960. package/src/styles/fonts/noto-serif-tc/400/387c811226f303af62f1e21aae6f5c83.woff2 +0 -0
  961. package/src/styles/fonts/noto-serif-tc/400/3b41385fc27419c19822060daa0b5cb3.woff2 +0 -0
  962. package/src/styles/fonts/noto-serif-tc/400/3cbe4a697fd595ef42c899de7d3e5445.woff2 +0 -0
  963. package/src/styles/fonts/noto-serif-tc/400/3d83dacbbec3d8532ae9afede21f3aab.woff2 +0 -0
  964. package/src/styles/fonts/noto-serif-tc/400/47479c470fae70f10b7c964a7ecbf274.woff2 +0 -0
  965. package/src/styles/fonts/noto-serif-tc/400/4dc0728df0f2ba70796f45f05654c7ba.woff2 +0 -0
  966. package/src/styles/fonts/noto-serif-tc/400/4dc2bc2c55b47f57d13b63aa6b1c8bd4.woff2 +0 -0
  967. package/src/styles/fonts/noto-serif-tc/400/4e1cc6aafb411b572c8d3511e925ecf1.woff2 +0 -0
  968. package/src/styles/fonts/noto-serif-tc/400/5227dbe9933760a48baff21ebd13fc98.woff2 +0 -0
  969. package/src/styles/fonts/noto-serif-tc/400/526b263e72c189f4b065738aaa6d423a.woff2 +0 -0
  970. package/src/styles/fonts/noto-serif-tc/400/54da934819a917f561b439bfd10f88b6.woff2 +0 -0
  971. package/src/styles/fonts/noto-serif-tc/400/5bfc7a121c35ae42623ef804fb525e0e.woff2 +0 -0
  972. package/src/styles/fonts/noto-serif-tc/400/5f90024544c2907c6c0203c6210c50be.woff2 +0 -0
  973. package/src/styles/fonts/noto-serif-tc/400/649b12d7cee7bb981842946e4547e6ca.woff2 +0 -0
  974. package/src/styles/fonts/noto-serif-tc/400/653bef2ed891ae48d8ed712283080649.woff2 +0 -0
  975. package/src/styles/fonts/noto-serif-tc/400/67d2a81f06ba352f17fbdc3a5e6ea59e.woff2 +0 -0
  976. package/src/styles/fonts/noto-serif-tc/400/68304f3229cf763465f044fccb5892c0.woff2 +0 -0
  977. package/src/styles/fonts/noto-serif-tc/400/688a88911e4da17b609196a959b8b930.woff2 +0 -0
  978. package/src/styles/fonts/noto-serif-tc/400/6db6ddf72c38a78ce44c1327701152e1.woff2 +0 -0
  979. package/src/styles/fonts/noto-serif-tc/400/77a7533bd21ccd33192d142a93555aa8.woff2 +0 -0
  980. package/src/styles/fonts/noto-serif-tc/400/7d65a3d6a65050eb5e6eca43398aeba4.woff2 +0 -0
  981. package/src/styles/fonts/noto-serif-tc/400/7dfc711962c8771f97e7c8898a6bcb65.woff2 +0 -0
  982. package/src/styles/fonts/noto-serif-tc/400/7ef123b62d530fcba73974fa265e0aae.woff2 +0 -0
  983. package/src/styles/fonts/noto-serif-tc/400/80466082a896fd328f30a78593c7c568.woff2 +0 -0
  984. package/src/styles/fonts/noto-serif-tc/400/8a3c84b0df36f851f5fea75ee8757951.woff2 +0 -0
  985. package/src/styles/fonts/noto-serif-tc/400/8b0c8c9f8cfa9fa090d97c5a5efb1f4c.woff2 +0 -0
  986. package/src/styles/fonts/noto-serif-tc/400/8dc035a34c76e6515ca203e2df182588.woff2 +0 -0
  987. package/src/styles/fonts/noto-serif-tc/400/8eb06109812cb80be44f47b8179c2709.woff2 +0 -0
  988. package/src/styles/fonts/noto-serif-tc/400/904324af375d5fd370af1054355a050e.woff2 +0 -0
  989. package/src/styles/fonts/noto-serif-tc/400/911a2092d64d6d6494b254d819af2b91.woff2 +0 -0
  990. package/src/styles/fonts/noto-serif-tc/400/9de02d745b8e25c6411fb152fb067748.woff2 +0 -0
  991. package/src/styles/fonts/noto-serif-tc/400/9eb33a430058d839ebbe2af4b2e0daa9.woff2 +0 -0
  992. package/src/styles/fonts/noto-serif-tc/400/9f5a73aa8ba417688019d628f334db07.woff2 +0 -0
  993. package/src/styles/fonts/noto-serif-tc/400/a0f0c06d5c7a3ffa97706178cce212a8.woff2 +0 -0
  994. package/src/styles/fonts/noto-serif-tc/400/a38c1830367f784181b6f544b0b11bbd.woff2 +0 -0
  995. package/src/styles/fonts/noto-serif-tc/400/a9cf85e27428c14351d30eac8cbc8d91.woff2 +0 -0
  996. package/src/styles/fonts/noto-serif-tc/400/aa0ce6740f301351761a0615cc8b2e99.woff2 +0 -0
  997. package/src/styles/fonts/noto-serif-tc/400/bc3f0cb8b55ee11d32b94ca488976f8d.woff2 +0 -0
  998. package/src/styles/fonts/noto-serif-tc/400/bcb3307527d6d0033bf0f17660b91e71.woff2 +0 -0
  999. package/src/styles/fonts/noto-serif-tc/400/bf1acc86e17b4229c548828a9d6f455d.woff2 +0 -0
  1000. package/src/styles/fonts/noto-serif-tc/400/c3fbc1f2557c343863a10698f8c966a2.woff2 +0 -0
  1001. package/src/styles/fonts/noto-serif-tc/400/c5c1c0be944ea39a3f50a02d32f5b759.woff2 +0 -0
  1002. package/src/styles/fonts/noto-serif-tc/400/c5f1075caf6d1344ee720de85114a521.woff2 +0 -0
  1003. package/src/styles/fonts/noto-serif-tc/400/cae29b3f8951eaf20d2f61c2206e28d9.woff2 +0 -0
  1004. package/src/styles/fonts/noto-serif-tc/400/d043b8d7a48bb0ac59ee1f1477d88eee.woff2 +0 -0
  1005. package/src/styles/fonts/noto-serif-tc/400/d320b000b5978c7251148a6a154741b8.woff2 +0 -0
  1006. package/src/styles/fonts/noto-serif-tc/400/da13b136efb1d1e4c76575af8f79a698.woff2 +0 -0
  1007. package/src/styles/fonts/noto-serif-tc/400/da2cf0ec56bf69374ee37764c7e3ea3d.woff2 +0 -0
  1008. package/src/styles/fonts/noto-serif-tc/400/daf62255dd60679946f28c442ca62533.woff2 +0 -0
  1009. package/src/styles/fonts/noto-serif-tc/400/dd01a1035345f6921a48525b8ce08f06.woff2 +0 -0
  1010. package/src/styles/fonts/noto-serif-tc/400/e4fb59479cedc87ba79785590bf861ca.woff2 +0 -0
  1011. package/src/styles/fonts/noto-serif-tc/400/e5d00355f73293d40b61299459d17ca5.woff2 +0 -0
  1012. package/src/styles/fonts/noto-serif-tc/400/e647b8d2efc501c0cc0e407249cc7135.woff2 +0 -0
  1013. package/src/styles/fonts/noto-serif-tc/400/e6e60ffb2ebd1828628764b507060aea.woff2 +0 -0
  1014. package/src/styles/fonts/noto-serif-tc/400/f00eb499abb94fa7b799d6d8c9b050e9.woff2 +0 -0
  1015. package/src/styles/fonts/noto-serif-tc/400/f7d36ffff7a75c9c6216d576a57dd00d.woff2 +0 -0
  1016. package/src/styles/fonts/noto-serif-tc/400/f7f3f63e7a149cd89eccab3b52171d05.woff2 +0 -0
  1017. package/src/styles/fonts/noto-serif-tc/400/fcc41f6a067ddd658bba5c9dff234a32.woff2 +0 -0
  1018. package/src/styles/fonts/noto-serif-tc/400/fd6ad889fcf3583bd9b0b6db53aad434.woff2 +0 -0
  1019. package/src/styles/fonts/noto-serif-tc/700/0c055db157e7a13f3103cc2a6b67fec3.woff2 +0 -0
  1020. package/src/styles/fonts/noto-serif-tc/700/0d3f5cc265cb6c439c517f2c4cebbddf.woff2 +0 -0
  1021. package/src/styles/fonts/noto-serif-tc/700/1259e5825b314fe2b8bb96d6e8069ee5.woff2 +0 -0
  1022. package/src/styles/fonts/noto-serif-tc/700/12c518ebfe62818af550c08947e359e7.woff2 +0 -0
  1023. package/src/styles/fonts/noto-serif-tc/700/145831a59caa06d894022fe60212ed21.woff2 +0 -0
  1024. package/src/styles/fonts/noto-serif-tc/700/169a096e61d38a773216f51d1ec2cc06.woff2 +0 -0
  1025. package/src/styles/fonts/noto-serif-tc/700/1884a2b22d314c7d57707f03aec348e0.woff2 +0 -0
  1026. package/src/styles/fonts/noto-serif-tc/700/1e2640116bbba817f43c43cc69371cf1.woff2 +0 -0
  1027. package/src/styles/fonts/noto-serif-tc/700/2573703213da30d3ba18925b100b2c2b.woff2 +0 -0
  1028. package/src/styles/fonts/noto-serif-tc/700/26839c0e47c73514b8d8f660d24d6b19.woff2 +0 -0
  1029. package/src/styles/fonts/noto-serif-tc/700/2a22e14a9ad53f2abb3c7e85017b7d12.woff2 +0 -0
  1030. package/src/styles/fonts/noto-serif-tc/700/2b3e8c5703b91f39f6027f43f0da6f4b.woff2 +0 -0
  1031. package/src/styles/fonts/noto-serif-tc/700/2f27ee4fb2cf6a280e110e09c18ef73e.woff2 +0 -0
  1032. package/src/styles/fonts/noto-serif-tc/700/31342cebfa5ea7fac06b4ea372d96bc5.woff2 +0 -0
  1033. package/src/styles/fonts/noto-serif-tc/700/375329ba0b50b94b35006498e555867c.woff2 +0 -0
  1034. package/src/styles/fonts/noto-serif-tc/700/427577dcb707d1d35eebd155b4222aa7.woff2 +0 -0
  1035. package/src/styles/fonts/noto-serif-tc/700/450a5b53be0a8a778bb0b623e86b652f.woff2 +0 -0
  1036. package/src/styles/fonts/noto-serif-tc/700/477866c8396474a17317dcac3e7a014f.woff2 +0 -0
  1037. package/src/styles/fonts/noto-serif-tc/700/478ebdaadda7775c391c5dcab4e697df.woff2 +0 -0
  1038. package/src/styles/fonts/noto-serif-tc/700/48d6a97a185c799be4fe67aaf7edf213.woff2 +0 -0
  1039. package/src/styles/fonts/noto-serif-tc/700/490edb9fc8a4356aea556eed32287464.woff2 +0 -0
  1040. package/src/styles/fonts/noto-serif-tc/700/4c4bdd0b3f3a52e28f3b643c1c5d43be.woff2 +0 -0
  1041. package/src/styles/fonts/noto-serif-tc/700/4c96411f3693a9a8657a9c1190f82bce.woff2 +0 -0
  1042. package/src/styles/fonts/noto-serif-tc/700/4c9aa12aba2a6a57410eacaff7427916.woff2 +0 -0
  1043. package/src/styles/fonts/noto-serif-tc/700/4cca7233bf8ce5dec2e5d146b993d626.woff2 +0 -0
  1044. package/src/styles/fonts/noto-serif-tc/700/4d0a9128d06ea857f203bf5d007b1ab9.woff2 +0 -0
  1045. package/src/styles/fonts/noto-serif-tc/700/4e5384920bbb155d9d8d74887b09ea5b.woff2 +0 -0
  1046. package/src/styles/fonts/noto-serif-tc/700/50cfd672bfa62512ba090420acf35c87.woff2 +0 -0
  1047. package/src/styles/fonts/noto-serif-tc/700/551b1d7a0b80c8d42af09863cdca7f01.woff2 +0 -0
  1048. package/src/styles/fonts/noto-serif-tc/700/555d990ab3fd7d3d66c6d1fa9a82fec5.woff2 +0 -0
  1049. package/src/styles/fonts/noto-serif-tc/700/5979c33a7eb5963bf8e83e46931b5fb5.woff2 +0 -0
  1050. package/src/styles/fonts/noto-serif-tc/700/59966ee0b069b577510fe68c350da0ee.woff2 +0 -0
  1051. package/src/styles/fonts/noto-serif-tc/700/60a14064ed334f0155795d795e926abe.woff2 +0 -0
  1052. package/src/styles/fonts/noto-serif-tc/700/611b62d5fd9698d9b5ce495ba6f14c93.woff2 +0 -0
  1053. package/src/styles/fonts/noto-serif-tc/700/6e83fe0b6e708eaf1c3003d6dee11488.woff2 +0 -0
  1054. package/src/styles/fonts/noto-serif-tc/700/70861376e5d4f92f8aa7aa1b2749b617.woff2 +0 -0
  1055. package/src/styles/fonts/noto-serif-tc/700/7124d150570d39ced8d45507dc11ca1e.woff2 +0 -0
  1056. package/src/styles/fonts/noto-serif-tc/700/79a7fdf7d9c722b5723ae25e6ff8e203.woff2 +0 -0
  1057. package/src/styles/fonts/noto-serif-tc/700/8c8393bc875f1ee36697a2113f4421ea.woff2 +0 -0
  1058. package/src/styles/fonts/noto-serif-tc/700/8e6c9bb43afb8cbbff7cf1055e67c9bd.woff2 +0 -0
  1059. package/src/styles/fonts/noto-serif-tc/700/90ac4f9d2aa02afdace2843b49fc18bb.woff2 +0 -0
  1060. package/src/styles/fonts/noto-serif-tc/700/90b6f57d77847f512fd11db74fa912f1.woff2 +0 -0
  1061. package/src/styles/fonts/noto-serif-tc/700/913759e6690f9fc0746a20b96f4bdcb4.woff2 +0 -0
  1062. package/src/styles/fonts/noto-serif-tc/700/9154e26efe532a85a27d80902f5a2d6c.woff2 +0 -0
  1063. package/src/styles/fonts/noto-serif-tc/700/94e7ed67f1557b76fead6b6e456a0415.woff2 +0 -0
  1064. package/src/styles/fonts/noto-serif-tc/700/95127a92346c04fec1fa81d6295b0a28.woff2 +0 -0
  1065. package/src/styles/fonts/noto-serif-tc/700/9fbc06b2e3ff16b9d705c76db563ef17.woff2 +0 -0
  1066. package/src/styles/fonts/noto-serif-tc/700/a3b929542e6c5a0644b73a7c8a8b6c03.woff2 +0 -0
  1067. package/src/styles/fonts/noto-serif-tc/700/a8857f5d478f101c053ba02d2f223e90.woff2 +0 -0
  1068. package/src/styles/fonts/noto-serif-tc/700/aa64c9953af43ca65832f413895bb433.woff2 +0 -0
  1069. package/src/styles/fonts/noto-serif-tc/700/ada8f0241244c60ec8d3d59ad37f20a5.woff2 +0 -0
  1070. package/src/styles/fonts/noto-serif-tc/700/b341de0bc0bfe194a6c28dcfb566029e.woff2 +0 -0
  1071. package/src/styles/fonts/noto-serif-tc/700/b846c293981ca5429eabaa967f222f26.woff2 +0 -0
  1072. package/src/styles/fonts/noto-serif-tc/700/be64f9379412876e00fd3a0bfa6b6fe9.woff2 +0 -0
  1073. package/src/styles/fonts/noto-serif-tc/700/befed8a4fa817773fa7109db6fe07f56.woff2 +0 -0
  1074. package/src/styles/fonts/noto-serif-tc/700/c09ee2b219982f8d46ad9968b7e6e0ba.woff2 +0 -0
  1075. package/src/styles/fonts/noto-serif-tc/700/c39ec937c6a8d124e8b68cf829ea5ad4.woff2 +0 -0
  1076. package/src/styles/fonts/noto-serif-tc/700/c3fd21315345ae541f6e98067059fa19.woff2 +0 -0
  1077. package/src/styles/fonts/noto-serif-tc/700/c568a16e3168ceb1f191b70022c492ea.woff2 +0 -0
  1078. package/src/styles/fonts/noto-serif-tc/700/c5e66d60be3375835bbd8d6b797f6eac.woff2 +0 -0
  1079. package/src/styles/fonts/noto-serif-tc/700/cd10a3af2133805d8c92104d1ee6ff18.woff2 +0 -0
  1080. package/src/styles/fonts/noto-serif-tc/700/d15a3317942b7d31978a759fbf2222c8.woff2 +0 -0
  1081. package/src/styles/fonts/noto-serif-tc/700/d740dc2e854aaa7b3dcdd3ed25455eeb.woff2 +0 -0
  1082. package/src/styles/fonts/noto-serif-tc/700/d8325ba7ae651bc30440905bd67b95f1.woff2 +0 -0
  1083. package/src/styles/fonts/noto-serif-tc/700/e2204cf85edcb96c5de5c3dcf240da9d.woff2 +0 -0
  1084. package/src/styles/fonts/noto-serif-tc/700/e3e913e145ddcd9323b2a0972967feb6.woff2 +0 -0
  1085. package/src/styles/fonts/noto-serif-tc/700/f2fb1f1fbf7e44afb53c672ec286a22e.woff2 +0 -0
  1086. package/src/styles/fonts/noto-serif-tc/700/f372129c60aaece937cf7b91ee75c9b8.woff2 +0 -0
  1087. package/src/styles/fonts/noto-serif-tc/700/f5d7487963d43c89da63aaf10f2e6fb7.woff2 +0 -0
  1088. package/src/styles/fonts/noto-serif-tc/700/f75496953a40ff241178240209f56990.woff2 +0 -0
  1089. package/src/styles/fonts/noto-serif-tc/700/f92d74d1d217d21b39075ff23f79f7fd.woff2 +0 -0
  1090. package/src/styles/fonts/noto-serif-tc/700/f9d6d981d8b87b3e469027277f585741.woff2 +0 -0
  1091. package/src/styles/fonts/noto-serif-tc/700/fa8ed469ef290bfeb571418fe0abb628.woff2 +0 -0
  1092. package/src/styles/fonts/noto-serif-tc/700/fb61b690208eff56e6d8432951270901.woff2 +0 -0
  1093. package/src/styles/fonts/noto-serif-tc/700/ff0937ad63cda71ff420945ead55ab4d.woff2 +0 -0
  1094. package/src/styles/fonts/noto-serif-tc/noto-serif-tc.css +3104 -0
  1095. package/src/styles/tokens.css +42 -7
  1096. package/src/styles/ui.css +7586 -3401
  1097. package/src/types/app-context.ts +7 -0
  1098. package/src/types/bindings.ts +77 -27
  1099. package/src/types/config.ts +104 -4
  1100. package/src/types/constants.ts +43 -4
  1101. package/src/types/entities.ts +63 -4
  1102. package/src/types/operations.ts +45 -15
  1103. package/src/types/props.ts +46 -9
  1104. package/src/types/views.ts +51 -8
  1105. package/src/ui/__tests__/color-themes.test.ts +81 -0
  1106. package/src/ui/__tests__/font-themes.test.ts +76 -7
  1107. package/src/ui/color-themes.ts +424 -238
  1108. package/src/ui/compose/ComposeDialog.tsx +149 -91
  1109. package/src/ui/compose/ComposePrompt.tsx +20 -3
  1110. package/src/ui/dash/ActionButtons.tsx +16 -2
  1111. package/src/ui/dash/DangerZone.tsx +10 -1
  1112. package/src/ui/dash/StatusBadge.tsx +3 -3
  1113. package/src/ui/dash/appearance/AdvancedContent.tsx +9 -2
  1114. package/src/ui/dash/appearance/ColorThemeContent.tsx +326 -63
  1115. package/src/ui/dash/appearance/FontThemeContent.tsx +75 -17
  1116. package/src/ui/dash/appearance/NavigationContent.tsx +48 -21
  1117. package/src/ui/dash/settings/AccountContent.tsx +7 -2
  1118. package/src/ui/dash/settings/AccountMenuContent.tsx +242 -111
  1119. package/src/ui/dash/settings/ApiTokensContent.tsx +38 -8
  1120. package/src/ui/dash/settings/AvatarContent.tsx +1 -1
  1121. package/src/ui/dash/settings/DeleteAccountContent.tsx +300 -0
  1122. package/src/ui/dash/settings/GeneralContent.tsx +120 -6
  1123. package/src/ui/dash/settings/SessionsContent.tsx +23 -5
  1124. package/src/ui/dash/settings/SettingsDirectory.tsx +99 -0
  1125. package/src/ui/dash/settings/SettingsRootContent.tsx +176 -221
  1126. package/src/ui/feed/CuratedThreadPreview.tsx +73 -0
  1127. package/src/ui/feed/LinkCard.tsx +52 -36
  1128. package/src/ui/feed/NoteCard.tsx +10 -4
  1129. package/src/ui/feed/PostStatusBadges.tsx +4 -21
  1130. package/src/ui/feed/QuoteCard.tsx +11 -3
  1131. package/src/ui/feed/ThreadPreview.tsx +11 -5
  1132. package/src/ui/feed/TimelineFeed.tsx +53 -15
  1133. package/src/ui/feed/__tests__/timeline-cards.test.ts +172 -0
  1134. package/src/ui/feed/thread-preview-state.ts +2 -4
  1135. package/src/ui/font-themes.ts +211 -43
  1136. package/src/ui/layouts/BaseLayout.tsx +161 -30
  1137. package/src/ui/layouts/SiteLayout.tsx +51 -18
  1138. package/src/ui/layouts/__tests__/BaseLayout.test.tsx +169 -0
  1139. package/src/ui/pages/ArchivePage.tsx +320 -120
  1140. package/src/ui/pages/BrandPage.tsx +927 -0
  1141. package/src/ui/pages/CollectionEditorPage.tsx +155 -0
  1142. package/src/ui/pages/CollectionPage.tsx +410 -18
  1143. package/src/ui/pages/CollectionsPage.tsx +57 -57
  1144. package/src/ui/pages/FeaturedPage.tsx +24 -3
  1145. package/src/ui/pages/HomePage.tsx +13 -0
  1146. package/src/ui/pages/PostPage.tsx +13 -4
  1147. package/src/ui/pages/SearchPage.tsx +12 -2
  1148. package/src/ui/pages/ThemeSamplePage.tsx +1160 -0
  1149. package/src/ui/shared/AdminBreadcrumb.tsx +22 -6
  1150. package/src/ui/shared/CollectionDirectory.tsx +117 -0
  1151. package/src/ui/shared/CollectionsManager.tsx +277 -0
  1152. package/src/ui/shared/DecorativeQuoteMark.tsx +32 -0
  1153. package/src/ui/shared/HomePageBranding.tsx +22 -0
  1154. package/src/ui/shared/JantBrandMark.tsx +35 -0
  1155. package/src/ui/shared/MediaGallery.tsx +106 -36
  1156. package/src/ui/shared/PaginatedPageHeader.tsx +49 -0
  1157. package/src/ui/shared/Pagination.tsx +2 -6
  1158. package/src/ui/shared/PostFooter.tsx +130 -67
  1159. package/src/ui/shared/__tests__/home-page-branding.test.tsx +14 -0
  1160. package/src/ui/shared/__tests__/media-gallery.test.ts +82 -0
  1161. package/src/ui/shared/__tests__/navigation-labels.test.ts +103 -0
  1162. package/src/ui/shared/__tests__/pagination.test.ts +18 -1
  1163. package/src/ui/shared/__tests__/post-footer.test.ts +122 -0
  1164. package/src/ui/shared/collection-management-labels.ts +161 -0
  1165. package/src/ui/shared/navigation-labels.ts +126 -0
  1166. package/src/vendor/datastar.js +1955 -7
  1167. package/dist/client/assets/heic-to-DIRPI3VF.js +0 -1
  1168. package/dist/client/assets/module-RjUF93sV.js +0 -716
  1169. package/dist/client/assets/native-48B9X9Wg.js +0 -1
  1170. package/dist/client/assets/url-FWFqPJPb.js +0 -1
  1171. package/dist/client/client.css +0 -1
  1172. package/dist/client/client.js +0 -33565
  1173. package/src/client/components/collection-sidebar-types.ts +0 -43
  1174. package/src/client/tiptap/paste-image.ts +0 -129
  1175. package/src/lib/emoji-catalog.ts +0 -963
  1176. package/src/lib/icon-catalog.ts +0 -5213
  1177. package/src/ui/shared/CollectionsSidebar.tsx +0 -294
@@ -1,6 +1,25 @@
1
- import { readFile, readdir, stat } from "node:fs/promises";
2
- import { resolve, join, relative } from "node:path";
1
+ import {
2
+ mkdir,
3
+ mkdtemp,
4
+ readFile,
5
+ readdir,
6
+ rm,
7
+ stat,
8
+ writeFile,
9
+ } from "node:fs/promises";
10
+ import { resolve, join, relative, extname, dirname, basename } from "node:path";
11
+ import { tmpdir } from "node:os";
3
12
  import { parseArgs } from "node:util";
13
+ import { typeidUnboxed } from "typeid-js";
14
+ import { CLI_API_TOKEN_ENV_VAR, getCliApiToken } from "../lib/cli-api-token.js";
15
+ import {
16
+ extractAttachmentBlocks,
17
+ findImageUrls,
18
+ normalizeImportedBody,
19
+ rewriteMediaReferences,
20
+ } from "../lib/site-media-parser.js";
21
+ import { openNodeDatabase } from "../lib/node-database.js";
22
+ import { loadNodeRuntime } from "../lib/load-node-runtime.js";
4
23
 
5
24
  /**
6
25
  * Parse front matter from a Markdown file.
@@ -27,6 +46,176 @@ async function parseFrontMatter(content) {
27
46
  return { frontMatter: {}, body: content };
28
47
  }
29
48
 
49
+ async function parseToml(content) {
50
+ const { parse } = await import("smol-toml");
51
+ return parse(content);
52
+ }
53
+
54
+ function resolveImportUrl(url, siteConfig) {
55
+ if (typeof url !== "string" || url.trim() === "" || url.startsWith("data:")) {
56
+ return url;
57
+ }
58
+
59
+ const baseUrl =
60
+ typeof siteConfig?.base_url === "string" ? siteConfig.base_url : "";
61
+ if (!baseUrl) {
62
+ return url;
63
+ }
64
+
65
+ try {
66
+ return new URL(url, baseUrl).toString();
67
+ } catch {
68
+ return url;
69
+ }
70
+ }
71
+
72
+ function resolveImportSiteAssetUrl(path, siteConfig) {
73
+ if (typeof path !== "string" || path.trim() === "") {
74
+ return path;
75
+ }
76
+
77
+ const baseUrl =
78
+ typeof siteConfig?.base_url === "string" ? siteConfig.base_url : "";
79
+ if (!baseUrl) {
80
+ return path;
81
+ }
82
+
83
+ try {
84
+ const parsedBaseUrl = new URL(baseUrl);
85
+ const sitePathPrefix = getImportSitePathPrefix(siteConfig);
86
+ const normalizedPath = path.startsWith("/") ? path : `/${path}`;
87
+ const publicPath =
88
+ sitePathPrefix &&
89
+ normalizedPath !== sitePathPrefix &&
90
+ !normalizedPath.startsWith(`${sitePathPrefix}/`)
91
+ ? `${sitePathPrefix}${normalizedPath}`
92
+ : normalizedPath;
93
+ return new URL(publicPath, parsedBaseUrl.origin).toString();
94
+ } catch {
95
+ return resolveImportUrl(path, siteConfig);
96
+ }
97
+ }
98
+
99
+ function getImportSitePathPrefix(siteConfig) {
100
+ const baseUrl =
101
+ typeof siteConfig?.base_url === "string" ? siteConfig.base_url : "";
102
+ if (!baseUrl) {
103
+ return "";
104
+ }
105
+
106
+ try {
107
+ const pathname = new URL(baseUrl).pathname.replace(/\/+$/, "");
108
+ return pathname === "/" ? "" : pathname;
109
+ } catch {
110
+ return "";
111
+ }
112
+ }
113
+
114
+ async function resolveImportLocalAssetPath(rawUrl, siteConfig, sourceRootDir) {
115
+ if (
116
+ !sourceRootDir ||
117
+ typeof rawUrl !== "string" ||
118
+ rawUrl.trim() === "" ||
119
+ rawUrl.startsWith("data:")
120
+ ) {
121
+ return null;
122
+ }
123
+
124
+ const resolvedUrl = resolveImportUrl(rawUrl, siteConfig);
125
+ if (typeof resolvedUrl !== "string" || resolvedUrl.trim() === "") {
126
+ return null;
127
+ }
128
+
129
+ let pathname = "";
130
+ try {
131
+ pathname = new URL(
132
+ resolvedUrl,
133
+ typeof siteConfig?.base_url === "string" && siteConfig.base_url
134
+ ? siteConfig.base_url
135
+ : "https://jant.invalid",
136
+ ).pathname;
137
+ } catch {
138
+ return null;
139
+ }
140
+
141
+ const sitePathPrefix = getImportSitePathPrefix(siteConfig);
142
+ if (sitePathPrefix && pathname.startsWith(`${sitePathPrefix}/`)) {
143
+ pathname = pathname.slice(sitePathPrefix.length + 1);
144
+ } else {
145
+ pathname = pathname.replace(/^\/+/, "");
146
+ }
147
+
148
+ if (!pathname) {
149
+ return null;
150
+ }
151
+
152
+ const fullPath = join(sourceRootDir, "static", pathname);
153
+ const fileStat = await stat(fullPath).catch(() => null);
154
+ if (!fileStat?.isFile()) {
155
+ return null;
156
+ }
157
+
158
+ return fullPath;
159
+ }
160
+
161
+ async function readImportAsset(options) {
162
+ const { sourceUrl, sourceFilePath, mimeType, originalName } = options;
163
+
164
+ if (sourceFilePath) {
165
+ const bytes = new Uint8Array(await readFile(sourceFilePath));
166
+ const filename =
167
+ originalName || basename(sourceFilePath) || getFilenameFromUrl(sourceUrl);
168
+ return {
169
+ bytes,
170
+ filename,
171
+ contentType: mimeType || guessMimeType(filename),
172
+ };
173
+ }
174
+
175
+ if (!sourceUrl) {
176
+ return null;
177
+ }
178
+
179
+ const response = await fetch(sourceUrl);
180
+ if (!response.ok) {
181
+ return null;
182
+ }
183
+
184
+ const bytes = new Uint8Array(await response.arrayBuffer());
185
+ const filename = originalName || getFilenameFromUrl(sourceUrl) || "file";
186
+ return {
187
+ bytes,
188
+ filename,
189
+ contentType:
190
+ mimeType ||
191
+ response.headers.get("content-type")?.split(";")[0] ||
192
+ guessMimeType(filename),
193
+ };
194
+ }
195
+
196
+ function toArrayBuffer(bytes) {
197
+ return bytes.buffer.slice(
198
+ bytes.byteOffset,
199
+ bytes.byteOffset + bytes.byteLength,
200
+ );
201
+ }
202
+
203
+ async function readMediaSpecAsset(media, field = "src") {
204
+ if (field === "poster") {
205
+ return readImportAsset({
206
+ sourceUrl: media.poster,
207
+ sourceFilePath: media.posterFilePath,
208
+ });
209
+ }
210
+
211
+ return readImportAsset({
212
+ sourceUrl: media.src,
213
+ sourceFilePath: media.srcFilePath,
214
+ mimeType: media.mimeType,
215
+ originalName: media.originalName,
216
+ });
217
+ }
218
+
30
219
  /**
31
220
  * Parse reply markers from post body.
32
221
  * Returns array of { attrs, body } segments where the first is the root.
@@ -45,7 +234,11 @@ function splitReplies(body) {
45
234
  while ((attrMatch = attrRegex.exec(match[1])) !== null) {
46
235
  attrs[attrMatch[1]] = attrMatch[2];
47
236
  }
48
- markers.push({ index: match.index, endIndex: match.index + match[0].length, attrs });
237
+ markers.push({
238
+ index: match.index,
239
+ endIndex: match.index + match[0].length,
240
+ attrs,
241
+ });
49
242
  }
50
243
 
51
244
  if (markers.length === 0) {
@@ -61,39 +254,126 @@ function splitReplies(body) {
61
254
  for (let i = 0; i < markers.length; i++) {
62
255
  const start = markers[i].endIndex;
63
256
  const end = i + 1 < markers.length ? markers[i + 1].index : body.length;
64
- segments.push({ attrs: markers[i].attrs, body: body.slice(start, end).trim() });
257
+ segments.push({
258
+ attrs: markers[i].attrs,
259
+ body: body.slice(start, end).trim(),
260
+ });
65
261
  }
66
262
 
67
263
  return segments;
68
264
  }
69
265
 
70
- /**
71
- * Find image URLs in markdown and return them.
72
- */
73
- function findImageUrls(markdown) {
74
- const urls = [];
75
- const regex = /!\[[^\]]*\]\(([^)\s]+)/g;
76
- let match;
77
- while ((match = regex.exec(markdown)) !== null) {
78
- urls.push(match[1]);
266
+ function normalizeImportPathKey(value) {
267
+ if (typeof value !== "string") {
268
+ return null;
269
+ }
270
+
271
+ const trimmed = value.trim();
272
+ if (!trimmed) {
273
+ return null;
274
+ }
275
+
276
+ return trimmed
277
+ .toLowerCase()
278
+ .replace(/^\/+|\/+$/g, "")
279
+ .replace(/\/+/g, "/");
280
+ }
281
+
282
+ function normalizeImportAliasPath(value) {
283
+ const pathKey = normalizeImportPathKey(value);
284
+ return pathKey ? `/${pathKey}` : null;
285
+ }
286
+
287
+ function collectReplySlugPaths(replySegments) {
288
+ const replySlugPaths = new Set();
289
+
290
+ for (const replySegment of replySegments) {
291
+ const replySlugPath = normalizeImportAliasPath(replySegment?.attrs?.slug);
292
+ if (replySlugPath) {
293
+ replySlugPaths.add(replySlugPath);
294
+ }
295
+ }
296
+
297
+ return replySlugPaths;
298
+ }
299
+
300
+ function getExportedRootAliases(frontMatter) {
301
+ const aliases = frontMatter?.extra?.jant?.root_aliases;
302
+ return Array.isArray(aliases) ? aliases : [];
303
+ }
304
+
305
+ function getRootAliasPathsForImport(aliases, postSlug, replySlugPaths) {
306
+ const rootSlugPath = normalizeImportAliasPath(postSlug);
307
+ const aliasPaths = [];
308
+ const seen = new Set();
309
+
310
+ for (const alias of Array.isArray(aliases) ? aliases : []) {
311
+ const aliasPath = normalizeImportAliasPath(alias);
312
+ if (!aliasPath) {
313
+ continue;
314
+ }
315
+ if (aliasPath === rootSlugPath) {
316
+ continue;
317
+ }
318
+ if (replySlugPaths.has(aliasPath)) {
319
+ throw new Error(
320
+ `Exported root alias "${aliasPath}" conflicts with a reply slug`,
321
+ );
322
+ }
323
+ if (seen.has(aliasPath)) {
324
+ continue;
325
+ }
326
+ seen.add(aliasPath);
327
+ aliasPaths.push(aliasPath);
328
+ }
329
+
330
+ return aliasPaths;
331
+ }
332
+
333
+ async function assertImportSlugAvailable(target, slug, label, kind) {
334
+ if (!slug) {
335
+ return;
336
+ }
337
+
338
+ const available = await target.checkPostSlugAvailability(slug);
339
+ if (!available) {
340
+ console.error(
341
+ `Import conflict: ${kind} slug "${slug}" for ${label} is already in use. Import into an empty site or remove the existing content first.`,
342
+ );
343
+ process.exit(1);
79
344
  }
80
- return urls;
81
345
  }
82
346
 
83
347
  /**
84
- * Download an image and upload it to the Jant API.
348
+ * Download a media file and upload it to the Jant API.
85
349
  * Returns the new URL, or null on failure.
86
350
  */
87
- async function uploadImage(imageUrl, apiUrl, token) {
351
+ async function uploadRemoteMedia(media, apiUrl, token) {
88
352
  try {
89
- const response = await fetch(imageUrl);
90
- if (!response.ok) return null;
353
+ const asset = await readMediaSpecAsset(media);
354
+ if (!asset) return null;
91
355
 
92
- const blob = await response.blob();
93
- const filename = imageUrl.split("/").pop() || "image.jpg";
356
+ const blob = new Blob([asset.bytes], { type: asset.contentType });
94
357
 
95
358
  const formData = new FormData();
96
- formData.append("file", blob, filename);
359
+ formData.append("file", blob, asset.filename);
360
+ if (media.alt) formData.append("alt", media.alt);
361
+ if (media.summary) formData.append("summary", media.summary);
362
+ if (media.width) formData.append("width", String(media.width));
363
+ if (media.height) formData.append("height", String(media.height));
364
+ if (media.blurhash) formData.append("blurhash", media.blurhash);
365
+ if (media.waveform) formData.append("waveform", media.waveform);
366
+
367
+ if (media.poster) {
368
+ const posterAsset = await readMediaSpecAsset(media, "poster");
369
+ if (posterAsset) {
370
+ formData.append(
371
+ "poster",
372
+ new Blob([posterAsset.bytes], { type: posterAsset.contentType }),
373
+ posterAsset.filename,
374
+ );
375
+ }
376
+ }
97
377
 
98
378
  const uploadResponse = await fetch(`${apiUrl}/api/upload`, {
99
379
  method: "POST",
@@ -109,15 +389,407 @@ async function uploadImage(imageUrl, apiUrl, token) {
109
389
  }
110
390
  }
111
391
 
112
- /**
113
- * Replace image URLs in markdown with newly uploaded URLs.
114
- */
115
- function replaceImageUrls(markdown, urlMap) {
116
- let result = markdown;
117
- for (const [oldUrl, newUrl] of urlMap) {
118
- result = result.replaceAll(oldUrl, newUrl);
392
+ function getFilenameFromUrl(fileUrl) {
393
+ try {
394
+ const pathname = new URL(fileUrl).pathname;
395
+ return pathname.split("/").pop() || "file";
396
+ } catch {
397
+ return fileUrl.split("/").pop() || "file";
119
398
  }
120
- return result;
399
+ }
400
+
401
+ function guessMimeType(filename) {
402
+ const ext = extname(filename).toLowerCase();
403
+ switch (ext) {
404
+ case ".jpg":
405
+ case ".jpeg":
406
+ return "image/jpeg";
407
+ case ".png":
408
+ return "image/png";
409
+ case ".gif":
410
+ return "image/gif";
411
+ case ".webp":
412
+ return "image/webp";
413
+ case ".svg":
414
+ return "image/svg+xml";
415
+ case ".avif":
416
+ return "image/avif";
417
+ case ".bmp":
418
+ return "image/bmp";
419
+ case ".ico":
420
+ return "image/x-icon";
421
+ case ".mp4":
422
+ return "video/mp4";
423
+ case ".mp3":
424
+ return "audio/mpeg";
425
+ case ".pdf":
426
+ return "application/pdf";
427
+ case ".json":
428
+ return "application/json";
429
+ case ".md":
430
+ return "text/markdown";
431
+ case ".csv":
432
+ return "text/csv";
433
+ case ".txt":
434
+ return "text/plain";
435
+ default:
436
+ return "application/octet-stream";
437
+ }
438
+ }
439
+
440
+ function generateImportedStorageKey(originalName) {
441
+ const id = typeidUnboxed("med");
442
+ const extension = extname(originalName) || "";
443
+ const filename = `${id}${extension}`;
444
+ return {
445
+ id,
446
+ filename,
447
+ storageKey: `media/${filename}`,
448
+ };
449
+ }
450
+
451
+ function getMediaPublicUrl(storageKey, provider, appConfig) {
452
+ const base =
453
+ provider === "s3"
454
+ ? appConfig.s3PublicUrl
455
+ : provider === "local"
456
+ ? appConfig.localPublicUrl
457
+ : appConfig.r2PublicUrl;
458
+
459
+ if (base) {
460
+ return `${base.replace(/\/+$/, "")}/${storageKey}`;
461
+ }
462
+
463
+ const prefix = appConfig.sitePathPrefix || "";
464
+ return `${prefix}/${storageKey}`.replace(/\/{2,}/g, "/");
465
+ }
466
+
467
+ function normalizeImportedBodySegment(markdown) {
468
+ const extracted = extractAttachmentBlocks(markdown);
469
+ const normalized = normalizeImportedBody(extracted.markdown);
470
+
471
+ return {
472
+ markdown: normalized.markdown,
473
+ attachments: [...extracted.attachments, ...normalized.attachments],
474
+ };
475
+ }
476
+
477
+ async function normalizeMediaSpec(spec, siteConfig, sourceRootDir) {
478
+ if (!spec || typeof spec.src !== "string" || spec.src.trim() === "") {
479
+ return null;
480
+ }
481
+
482
+ const src = resolveImportUrl(spec.src, siteConfig);
483
+ const poster =
484
+ typeof spec.poster === "string"
485
+ ? resolveImportUrl(spec.poster, siteConfig)
486
+ : null;
487
+
488
+ return {
489
+ kind: spec.kind,
490
+ src,
491
+ srcFilePath: await resolveImportLocalAssetPath(
492
+ spec.src,
493
+ siteConfig,
494
+ sourceRootDir,
495
+ ),
496
+ poster,
497
+ posterFilePath:
498
+ typeof spec.poster === "string"
499
+ ? await resolveImportLocalAssetPath(
500
+ spec.poster,
501
+ siteConfig,
502
+ sourceRootDir,
503
+ )
504
+ : null,
505
+ mimeType: spec.mimeType || undefined,
506
+ originalName: spec.originalName || undefined,
507
+ size: typeof spec.size === "number" ? spec.size : undefined,
508
+ width: typeof spec.width === "number" ? spec.width : undefined,
509
+ height: typeof spec.height === "number" ? spec.height : undefined,
510
+ alt: typeof spec.alt === "string" ? spec.alt : undefined,
511
+ position: typeof spec.position === "string" ? spec.position : undefined,
512
+ blurhash: typeof spec.blurhash === "string" ? spec.blurhash : undefined,
513
+ waveform: typeof spec.waveform === "string" ? spec.waveform : undefined,
514
+ summary: typeof spec.summary === "string" ? spec.summary : undefined,
515
+ chars: typeof spec.chars === "number" ? spec.chars : undefined,
516
+ };
517
+ }
518
+
519
+ function normalizeTextAttachmentSpec(spec) {
520
+ if (
521
+ !spec ||
522
+ spec.kind !== "text" ||
523
+ spec.contentFormat !== "markdown" ||
524
+ typeof spec.content !== "string" ||
525
+ spec.content.trim() === ""
526
+ ) {
527
+ return null;
528
+ }
529
+
530
+ return {
531
+ type: "text",
532
+ contentFormat: "markdown",
533
+ content: spec.content,
534
+ summary: typeof spec.summary === "string" ? spec.summary : undefined,
535
+ };
536
+ }
537
+
538
+ async function buildImportedAttachments(
539
+ attachmentSpecs,
540
+ target,
541
+ siteConfig,
542
+ sourceRootDir,
543
+ options = {},
544
+ ) {
545
+ if (
546
+ sourceRootDir &&
547
+ typeof sourceRootDir === "object" &&
548
+ !Array.isArray(sourceRootDir) &&
549
+ options &&
550
+ Object.keys(options).length === 0
551
+ ) {
552
+ options = sourceRootDir;
553
+ sourceRootDir = null;
554
+ }
555
+
556
+ const attachments = [];
557
+ let uploaded = 0;
558
+
559
+ for (const spec of attachmentSpecs) {
560
+ const textAttachment = normalizeTextAttachmentSpec(spec);
561
+ if (textAttachment) {
562
+ attachments.push(textAttachment);
563
+ continue;
564
+ }
565
+
566
+ if (options.skipUploads) {
567
+ continue;
568
+ }
569
+
570
+ const normalized = await normalizeMediaSpec(
571
+ spec,
572
+ siteConfig,
573
+ sourceRootDir,
574
+ );
575
+ if (!normalized || normalized.src.startsWith("data:")) continue;
576
+ const result = await target.uploadMedia(normalized);
577
+ if (!result) continue;
578
+ attachments.push({
579
+ type: "media",
580
+ mediaId: result.id,
581
+ ...(typeof normalized.alt === "string" ? { alt: normalized.alt } : {}),
582
+ });
583
+ uploaded += 1;
584
+ }
585
+
586
+ return { attachments, uploaded };
587
+ }
588
+
589
+ async function uploadMediaList(mediaSpecs, target, siteConfig, sourceRootDir) {
590
+ const urlMap = new Map();
591
+ const mediaIds = [];
592
+ let uploaded = 0;
593
+
594
+ for (const spec of mediaSpecs) {
595
+ const normalized = await normalizeMediaSpec(
596
+ spec,
597
+ siteConfig,
598
+ sourceRootDir,
599
+ );
600
+ if (!normalized || normalized.src.startsWith("data:")) continue;
601
+ const result = await target.uploadMedia(normalized);
602
+ if (!result) continue;
603
+ urlMap.set(normalized.src, result.url);
604
+ mediaIds.push(result.id);
605
+ uploaded += 1;
606
+ }
607
+
608
+ return { urlMap, mediaIds, uploaded };
609
+ }
610
+
611
+ function buildSettingsUpdatesFromConfig(siteConfig, customCss = "") {
612
+ const jant = siteConfig?.extra?.jant || {};
613
+ const themeId = String(jant.theme_id || "");
614
+ const defaultThemeId = String(jant.default_theme_id || "");
615
+ const fontThemeId = String(jant.font_theme_id || "");
616
+ const themeMode = String(jant.theme_mode || "");
617
+ const headerNavMaxVisible = Number(jant.header_nav_max_visible);
618
+
619
+ return {
620
+ SITE_NAME: String(siteConfig?.title || ""),
621
+ SITE_DESCRIPTION: String(siteConfig?.description || ""),
622
+ SITE_LANGUAGE: String(siteConfig?.default_language || "en"),
623
+ SITE_FOOTER: String(jant.site_footer_markdown || ""),
624
+ HOME_DEFAULT_VIEW:
625
+ String(jant.home_default_view || "") === "featured" ? "featured" : "",
626
+ HEADER_NAV_MAX_VISIBLE:
627
+ Number.isFinite(headerNavMaxVisible) && headerNavMaxVisible !== 2
628
+ ? String(headerNavMaxVisible)
629
+ : "",
630
+ SHOW_JANT_BRANDING_ON_HOME: jant.show_jant_branding_on_home ? "true" : "",
631
+ NOINDEX: jant.noindex ? "true" : "",
632
+ SHOW_HEADER_AVATAR: jant.show_header_avatar ? "true" : "",
633
+ THEME: themeId && themeId !== defaultThemeId ? themeId : "",
634
+ FONT_THEME: fontThemeId && fontThemeId !== "default" ? fontThemeId : "",
635
+ THEME_MODE: themeMode === "light" || themeMode === "dark" ? themeMode : "",
636
+ CUSTOM_CSS: customCss,
637
+ };
638
+ }
639
+
640
+ function normalizeImportedNavItems(siteConfig) {
641
+ const jant = siteConfig?.extra?.jant || {};
642
+ const navItems = jant.nav;
643
+ if (!Array.isArray(navItems)) {
644
+ return {
645
+ exported: Boolean(jant.nav_exported),
646
+ items: [],
647
+ };
648
+ }
649
+
650
+ return {
651
+ exported: true,
652
+ items: navItems
653
+ .map((item) => {
654
+ if (!item || typeof item !== "object") return null;
655
+ const type = item.type === "system" ? "system" : "link";
656
+ if (type === "system" && typeof item.system_key === "string") {
657
+ return { type, systemKey: item.system_key };
658
+ }
659
+ if (
660
+ type === "link" &&
661
+ typeof item.label === "string" &&
662
+ typeof item.url === "string"
663
+ ) {
664
+ return { type, label: item.label, url: item.url };
665
+ }
666
+ return null;
667
+ })
668
+ .filter(Boolean),
669
+ };
670
+ }
671
+
672
+ async function buildSiteAvatarImport(siteConfig, sourceRootDir) {
673
+ const exportInfo = siteConfig?.extra?.jant_export || {};
674
+ if (exportInfo.format !== "jant-site") {
675
+ return null;
676
+ }
677
+
678
+ const jant = siteConfig?.extra?.jant || {};
679
+ const siteAvatarMode =
680
+ jant.site_avatar_mode === "custom" ||
681
+ (typeof jant.site_avatar_url === "string" && jant.site_avatar_url.trim())
682
+ ? "custom"
683
+ : "none";
684
+ const faviconMode = jant.favicon_mode === "custom" ? "custom" : "default";
685
+ const appleTouchMode =
686
+ jant.apple_touch_mode === "custom" ||
687
+ (typeof jant.apple_touch_icon_url === "string" &&
688
+ jant.apple_touch_icon_url.trim())
689
+ ? "custom"
690
+ : "default";
691
+
692
+ if (siteAvatarMode !== "custom") {
693
+ return { mode: "remove" };
694
+ }
695
+
696
+ if (!jant.site_avatar_url || typeof jant.site_avatar_url !== "string") {
697
+ throw new Error(
698
+ 'Jant export marked site_avatar_mode="custom" but site_avatar_url is missing',
699
+ );
700
+ }
701
+
702
+ const faviconRawUrl =
703
+ faviconMode === "custom"
704
+ ? typeof jant.favicon_url === "string" && jant.favicon_url.trim()
705
+ ? jant.favicon_url
706
+ : "/favicon.ico"
707
+ : null;
708
+ const appleTouchRawUrl =
709
+ appleTouchMode === "custom"
710
+ ? typeof jant.apple_touch_icon_url === "string" &&
711
+ jant.apple_touch_icon_url.trim()
712
+ ? jant.apple_touch_icon_url
713
+ : "/apple-touch-icon.png"
714
+ : null;
715
+
716
+ return {
717
+ mode: "set",
718
+ avatarUrl: resolveImportUrl(jant.site_avatar_url, siteConfig),
719
+ avatarFilePath: await resolveImportLocalAssetPath(
720
+ jant.site_avatar_url,
721
+ siteConfig,
722
+ sourceRootDir,
723
+ ),
724
+ faviconUrl: faviconRawUrl
725
+ ? resolveImportSiteAssetUrl(faviconRawUrl, siteConfig)
726
+ : null,
727
+ faviconFilePath: faviconRawUrl
728
+ ? await resolveImportLocalAssetPath(
729
+ faviconRawUrl,
730
+ siteConfig,
731
+ sourceRootDir,
732
+ )
733
+ : null,
734
+ appleTouchUrl: appleTouchRawUrl
735
+ ? resolveImportSiteAssetUrl(appleTouchRawUrl, siteConfig)
736
+ : null,
737
+ appleTouchFilePath: appleTouchRawUrl
738
+ ? await resolveImportLocalAssetPath(
739
+ appleTouchRawUrl,
740
+ siteConfig,
741
+ sourceRootDir,
742
+ )
743
+ : null,
744
+ };
745
+ }
746
+
747
+ function buildIncompleteSetupError(targetLabel) {
748
+ return [
749
+ `${targetLabel} has not completed setup.`,
750
+ "Finish /setup first to create the site and admin account, then run the import again.",
751
+ "Until setup is finished, imports cannot write site settings, navigation, collections, or posts.",
752
+ ].join("\n");
753
+ }
754
+
755
+ async function detectRemoteSetupStatus(apiUrl) {
756
+ try {
757
+ const response = await fetch(`${apiUrl}/setup`, {
758
+ redirect: "manual",
759
+ });
760
+
761
+ if (response.status === 200) {
762
+ return false;
763
+ }
764
+
765
+ if (response.status >= 300 && response.status < 400) {
766
+ return true;
767
+ }
768
+ } catch {
769
+ return null;
770
+ }
771
+
772
+ return null;
773
+ }
774
+
775
+ async function getIncompleteSetupError(target, targetLabel) {
776
+ const isSetupComplete = await target.getSetupStatus();
777
+ if (isSetupComplete !== false) {
778
+ return null;
779
+ }
780
+
781
+ return buildIncompleteSetupError(targetLabel);
782
+ }
783
+
784
+ function createUploadFile(name, type, bytes) {
785
+ return {
786
+ name,
787
+ type,
788
+ size: bytes.byteLength,
789
+ stream() {
790
+ return new Blob([bytes], { type }).stream();
791
+ },
792
+ };
121
793
  }
122
794
 
123
795
  class ApiError extends Error {
@@ -142,14 +814,19 @@ async function apiCall(method, path, apiUrl, token, body) {
142
814
  });
143
815
  } catch (err) {
144
816
  const cause = err.cause?.code || err.cause?.message || err.message;
145
- if (cause === "UNABLE_TO_VERIFY_LEAF_SIGNATURE" || cause?.includes("certificate")) {
817
+ if (
818
+ cause === "UNABLE_TO_VERIFY_LEAF_SIGNATURE" ||
819
+ cause?.includes("certificate")
820
+ ) {
146
821
  console.error(`\nSSL certificate error connecting to ${apiUrl}`);
147
822
  console.error("If using a local/self-signed certificate, run with:");
148
823
  console.error(" NODE_TLS_REJECT_UNAUTHORIZED=0 jant import-site ...");
149
824
  console.error("Or use: node --use-system-ca bin/jant.js import-site ...");
150
825
  process.exit(1);
151
826
  }
152
- throw new Error(`Network error calling ${method} ${apiUrl}${path}: ${cause}`);
827
+ throw new Error(
828
+ `Network error calling ${method} ${apiUrl}${path}: ${cause}`,
829
+ );
153
830
  }
154
831
 
155
832
  if (!response.ok) {
@@ -160,6 +837,349 @@ async function apiCall(method, path, apiUrl, token, body) {
160
837
  return response.json();
161
838
  }
162
839
 
840
+ function toRemotePostPayload(data) {
841
+ if (data?.format !== "quote") {
842
+ return data;
843
+ }
844
+
845
+ const { title, url, ...rest } = data;
846
+ return {
847
+ ...rest,
848
+ ...(typeof title === "string" && title.trim() ? { sourceName: title } : {}),
849
+ ...(typeof url === "string" && url.trim() ? { sourceUrl: url } : {}),
850
+ };
851
+ }
852
+
853
+ function createRemoteTarget(apiUrl, token) {
854
+ return {
855
+ async close() {},
856
+ async getSetupStatus() {
857
+ return detectRemoteSetupStatus(apiUrl);
858
+ },
859
+ async updateSettings(updates) {
860
+ return apiCall("PUT", "/api/settings", apiUrl, token, updates);
861
+ },
862
+ async listNavItems() {
863
+ const result = await apiCall("GET", "/api/nav-items", apiUrl, token);
864
+ return result.navItems || [];
865
+ },
866
+ async createNavItem(data) {
867
+ return apiCall("POST", "/api/nav-items", apiUrl, token, data);
868
+ },
869
+ async deleteNavItem(id) {
870
+ return apiCall("DELETE", `/api/nav-items/${id}`, apiUrl, token);
871
+ },
872
+ async removeSiteAvatar() {
873
+ return apiCall("DELETE", "/api/settings/avatar", apiUrl, token);
874
+ },
875
+ async uploadSiteAvatar(data) {
876
+ const avatarAsset = await readImportAsset({
877
+ sourceUrl: data.avatarUrl,
878
+ sourceFilePath: data.avatarFilePath,
879
+ });
880
+ if (!avatarAsset) {
881
+ throw new Error(`Failed to read site avatar: ${data.avatarUrl}`);
882
+ }
883
+
884
+ const formData = new FormData();
885
+ formData.append(
886
+ "file",
887
+ new Blob([avatarAsset.bytes], { type: avatarAsset.contentType }),
888
+ avatarAsset.filename,
889
+ );
890
+
891
+ if (data.faviconUrl || data.faviconFilePath) {
892
+ const faviconAsset = await readImportAsset({
893
+ sourceUrl: data.faviconUrl,
894
+ sourceFilePath: data.faviconFilePath,
895
+ mimeType: "image/x-icon",
896
+ originalName: "favicon.ico",
897
+ });
898
+ if (faviconAsset) {
899
+ formData.append(
900
+ "favicon",
901
+ new Blob([faviconAsset.bytes], {
902
+ type: faviconAsset.contentType,
903
+ }),
904
+ faviconAsset.filename,
905
+ );
906
+ }
907
+ }
908
+
909
+ if (data.appleTouchUrl) {
910
+ const appleTouchAsset = await readImportAsset({
911
+ sourceUrl: data.appleTouchUrl,
912
+ sourceFilePath: data.appleTouchFilePath,
913
+ });
914
+ if (appleTouchAsset) {
915
+ formData.append(
916
+ "appleTouch",
917
+ new Blob([appleTouchAsset.bytes], {
918
+ type: appleTouchAsset.contentType,
919
+ }),
920
+ appleTouchAsset.filename,
921
+ );
922
+ }
923
+ }
924
+
925
+ const response = await fetch(`${apiUrl}/api/settings/avatar`, {
926
+ method: "POST",
927
+ headers: { Authorization: `Bearer ${token}` },
928
+ body: formData,
929
+ });
930
+
931
+ if (!response.ok) {
932
+ throw new Error(`HTTP ${response.status}: ${await response.text()}`);
933
+ }
934
+
935
+ return response.json();
936
+ },
937
+ async syncSiteAvatar(data) {
938
+ await this.removeSiteAvatar();
939
+ if (!data) {
940
+ return { success: true };
941
+ }
942
+ return this.uploadSiteAvatar(data);
943
+ },
944
+ async listCollections() {
945
+ const existing = await apiCall("GET", "/api/collections", apiUrl, token);
946
+ return existing.collections || [];
947
+ },
948
+ async createCollection(data) {
949
+ return apiCall("POST", "/api/collections", apiUrl, token, data);
950
+ },
951
+ async createPost(data) {
952
+ return apiCall(
953
+ "POST",
954
+ "/api/posts",
955
+ apiUrl,
956
+ token,
957
+ toRemotePostPayload(data),
958
+ );
959
+ },
960
+ async createAlias(path, targetSlug) {
961
+ return apiCall("POST", "/api/custom-urls", apiUrl, token, {
962
+ path,
963
+ targetType: "post",
964
+ targetId: targetSlug,
965
+ });
966
+ },
967
+ async uploadMedia(media) {
968
+ return uploadRemoteMedia(media, apiUrl, token);
969
+ },
970
+ async checkPostSlugAvailability(slug) {
971
+ const result = await apiCall(
972
+ "GET",
973
+ `/api/posts/slug?mode=check&slug=${encodeURIComponent(slug)}`,
974
+ apiUrl,
975
+ token,
976
+ );
977
+ return Boolean(result.available);
978
+ },
979
+ };
980
+ }
981
+
982
+ async function createLocalTarget(env = process.env) {
983
+ const nodeDatabase = await openNodeDatabase(env);
984
+ const { createNodeCliRuntime, resolveConfig } = await loadNodeRuntime();
985
+ const bindings = nodeDatabase.bindings;
986
+ const runtime = await createNodeCliRuntime(bindings);
987
+ const allSettings = await runtime.services.settings.getAll();
988
+ const appConfig = resolveConfig(bindings, allSettings);
989
+ const summaryConfig = {
990
+ maxParagraphs: appConfig.summaryMaxParagraphs,
991
+ maxChars: appConfig.summaryMaxChars,
992
+ };
993
+
994
+ return {
995
+ async close() {
996
+ await nodeDatabase.close();
997
+ },
998
+ async getSetupStatus() {
999
+ return runtime.services.settings.isOnboardingComplete();
1000
+ },
1001
+ async updateSettings(updates) {
1002
+ await runtime.services.settings.setMany(updates);
1003
+ return { settings: updates };
1004
+ },
1005
+ async listNavItems() {
1006
+ return runtime.services.navItems.list();
1007
+ },
1008
+ async createNavItem(data) {
1009
+ return runtime.services.navItems.create(data);
1010
+ },
1011
+ async deleteNavItem(id) {
1012
+ return runtime.services.navItems.delete(id);
1013
+ },
1014
+ async removeSiteAvatar() {
1015
+ return runtime.services.settings.removeAvatar(runtime.storage);
1016
+ },
1017
+ async uploadSiteAvatar(data) {
1018
+ if (!runtime.storage) {
1019
+ throw new Error("Local import requires configured storage.");
1020
+ }
1021
+
1022
+ const avatarAsset = await readImportAsset({
1023
+ sourceUrl: data.avatarUrl,
1024
+ sourceFilePath: data.avatarFilePath,
1025
+ });
1026
+ if (!avatarAsset) {
1027
+ throw new Error(`Failed to read site avatar: ${data.avatarUrl}`);
1028
+ }
1029
+
1030
+ let faviconIco;
1031
+ if (data.faviconUrl || data.faviconFilePath) {
1032
+ const faviconAsset = await readImportAsset({
1033
+ sourceUrl: data.faviconUrl,
1034
+ sourceFilePath: data.faviconFilePath,
1035
+ mimeType: "image/x-icon",
1036
+ originalName: "favicon.ico",
1037
+ });
1038
+ if (faviconAsset) {
1039
+ faviconIco = toArrayBuffer(faviconAsset.bytes);
1040
+ }
1041
+ }
1042
+
1043
+ let appleTouchIcon;
1044
+ if (data.appleTouchUrl) {
1045
+ const appleTouchAsset = await readImportAsset({
1046
+ sourceUrl: data.appleTouchUrl,
1047
+ sourceFilePath: data.appleTouchFilePath,
1048
+ });
1049
+ if (appleTouchAsset) {
1050
+ appleTouchIcon = toArrayBuffer(appleTouchAsset.bytes);
1051
+ }
1052
+ }
1053
+
1054
+ await runtime.services.settings.uploadAvatar(
1055
+ {
1056
+ file: createUploadFile(
1057
+ avatarAsset.filename,
1058
+ avatarAsset.contentType,
1059
+ avatarAsset.bytes,
1060
+ ),
1061
+ faviconIco,
1062
+ appleTouchIcon,
1063
+ },
1064
+ {
1065
+ media: runtime.services.media,
1066
+ storage: runtime.storage,
1067
+ storageProvider: appConfig.storageDriver,
1068
+ maxFileSizeMB: appConfig.uploadMaxFileSize,
1069
+ },
1070
+ );
1071
+
1072
+ return { success: true };
1073
+ },
1074
+ async syncSiteAvatar(data) {
1075
+ await this.removeSiteAvatar();
1076
+ if (!data) {
1077
+ return { success: true };
1078
+ }
1079
+ return this.uploadSiteAvatar(data);
1080
+ },
1081
+ async listCollections() {
1082
+ return runtime.services.collections.list();
1083
+ },
1084
+ async createCollection(data) {
1085
+ return runtime.services.collections.create(data);
1086
+ },
1087
+ async createPost(data) {
1088
+ const { attachments, ...postData } = data;
1089
+ return runtime.services.posts.createWithAttachments(
1090
+ postData,
1091
+ attachments,
1092
+ {
1093
+ media: runtime.services.media,
1094
+ storage: runtime.storage,
1095
+ storageDriver: appConfig.storageDriver,
1096
+ maxFileSizeMB: appConfig.uploadMaxFileSize,
1097
+ },
1098
+ summaryConfig,
1099
+ );
1100
+ },
1101
+ async createAlias(path, targetSlug) {
1102
+ const post = await runtime.services.posts.getBySlug(targetSlug);
1103
+ if (!post) {
1104
+ throw new Error(`Post with slug "${targetSlug}" not found`);
1105
+ }
1106
+ return runtime.services.customUrls.create({
1107
+ path,
1108
+ targetType: "post",
1109
+ targetId: post.id,
1110
+ });
1111
+ },
1112
+ async uploadMedia(mediaSpec) {
1113
+ if (!runtime.storage) {
1114
+ throw new Error("Local import requires configured storage.");
1115
+ }
1116
+
1117
+ const asset = await readMediaSpecAsset(mediaSpec);
1118
+ if (!asset) return null;
1119
+
1120
+ const originalName =
1121
+ mediaSpec.originalName ||
1122
+ asset.filename ||
1123
+ getFilenameFromUrl(mediaSpec.src) ||
1124
+ "file";
1125
+ const bytes = asset.bytes;
1126
+ const { id, filename, storageKey } =
1127
+ generateImportedStorageKey(originalName);
1128
+ const mimeType =
1129
+ mediaSpec.mimeType || asset.contentType || guessMimeType(originalName);
1130
+ let posterKey;
1131
+
1132
+ if (mediaSpec.poster) {
1133
+ const posterAsset = await readMediaSpecAsset(mediaSpec, "poster");
1134
+ if (posterAsset) {
1135
+ const posterName = posterAsset.filename || "poster.webp";
1136
+ const posterExt = extname(posterName) || ".webp";
1137
+ posterKey = storageKey.replace(/(\.[^.]+)?$/, `-poster${posterExt}`);
1138
+ await runtime.storage.put(posterKey, posterAsset.bytes, {
1139
+ contentType: posterAsset.contentType || guessMimeType(posterName),
1140
+ });
1141
+ }
1142
+ }
1143
+
1144
+ await runtime.storage.put(storageKey, bytes, {
1145
+ contentType: mimeType,
1146
+ });
1147
+
1148
+ const createdMedia = await runtime.services.media.create({
1149
+ id,
1150
+ filename,
1151
+ originalName,
1152
+ mimeType,
1153
+ size: mediaSpec.size ?? bytes.byteLength,
1154
+ storageKey,
1155
+ provider: appConfig.storageDriver,
1156
+ width: mediaSpec.width ?? undefined,
1157
+ height: mediaSpec.height ?? undefined,
1158
+ alt: mediaSpec.alt ?? undefined,
1159
+ position: mediaSpec.position ?? undefined,
1160
+ blurhash: mediaSpec.blurhash ?? undefined,
1161
+ waveform: mediaSpec.waveform ?? undefined,
1162
+ posterKey,
1163
+ summary: mediaSpec.summary ?? undefined,
1164
+ chars: mediaSpec.chars ?? undefined,
1165
+ mediaKind: mediaSpec.kind ?? undefined,
1166
+ });
1167
+
1168
+ return {
1169
+ id: createdMedia.id,
1170
+ url: getMediaPublicUrl(
1171
+ createdMedia.storageKey,
1172
+ createdMedia.provider,
1173
+ appConfig,
1174
+ ),
1175
+ };
1176
+ },
1177
+ async checkPostSlugAvailability(slug) {
1178
+ return runtime.services.posts.checkSlugAvailability(slug);
1179
+ },
1180
+ };
1181
+ }
1182
+
163
1183
  /**
164
1184
  * Recursively walk a directory's content/ folder and collect post/collection files.
165
1185
  */
@@ -180,7 +1200,11 @@ async function walkContent(rootDir, postFiles, collectionFiles) {
180
1200
  } else if (entry.name === "index.md" || entry.name === "_index.md") {
181
1201
  const relPath = relative(rootDir, fullPath).replace(/\\/g, "/");
182
1202
  const content = await readFile(fullPath, "utf-8");
183
- if (relPath.startsWith("content/c/") && relPath.endsWith("/_index.md")) {
1203
+ if (
1204
+ (relPath.startsWith("content/jant-collections/") ||
1205
+ relPath.startsWith("content/c/")) &&
1206
+ relPath.endsWith("/_index.md")
1207
+ ) {
184
1208
  collectionFiles.push({ path: relPath, content });
185
1209
  } else if (
186
1210
  relPath.startsWith("content/") &&
@@ -196,6 +1220,25 @@ async function walkContent(rootDir, postFiles, collectionFiles) {
196
1220
  await walk(contentDir);
197
1221
  }
198
1222
 
1223
+ export const __test__ = {
1224
+ resolveImportUrl,
1225
+ readMediaSpecAsset,
1226
+ normalizeMediaSpec,
1227
+ normalizeTextAttachmentSpec,
1228
+ extractAttachmentBlocks,
1229
+ buildImportedAttachments,
1230
+ buildSettingsUpdatesFromConfig,
1231
+ normalizeImportedNavItems,
1232
+ buildSiteAvatarImport,
1233
+ collectReplySlugPaths,
1234
+ getExportedRootAliases,
1235
+ getRootAliasPathsForImport,
1236
+ toRemotePostPayload,
1237
+ buildIncompleteSetupError,
1238
+ detectRemoteSetupStatus,
1239
+ getIncompleteSetupError,
1240
+ };
1241
+
199
1242
  export async function run(argv) {
200
1243
  const { values } = parseArgs({
201
1244
  args: argv,
@@ -210,39 +1253,63 @@ export async function run(argv) {
210
1253
  });
211
1254
 
212
1255
  if (values.help) {
213
- console.log("Usage: jant import-site --url <url> [options]");
1256
+ console.log("Usage: jant site import [--url <url>] [options]");
1257
+ console.log("");
1258
+ console.log("Import a Zola export directory or ZIP into a Jant instance.");
214
1259
  console.log("");
215
- console.log("Import a Zola export ZIP into a Jant instance.");
1260
+ console.log("Modes:");
1261
+ console.log(
1262
+ " Local No --url; imports into the local Node database runtime",
1263
+ );
1264
+ console.log(
1265
+ ` Remote --url requires ${CLI_API_TOKEN_ENV_VAR} or --token`,
1266
+ );
216
1267
  console.log("");
217
1268
  console.log("Options:");
218
- console.log(" --url Target Jant instance URL (required)");
219
- console.log(" --path Path to export directory or ZIP file (default: .)");
1269
+ console.log(" --url Target remote Jant instance URL");
1270
+ console.log(
1271
+ " --path Path to export directory or ZIP file (default: .)",
1272
+ );
220
1273
  console.log(" --dry-run Parse and validate without making API calls");
221
- console.log(" --skip-media Skip image download/upload");
1274
+ console.log(
1275
+ " --skip-media Skip remote media download/upload (embedded text attachments still import)",
1276
+ );
1277
+ console.log("");
1278
+ console.log(
1279
+ "Import expects an empty target site and fails on slug or alias conflicts.",
1280
+ );
222
1281
  console.log("");
223
1282
  console.log("Authentication:");
224
- console.log(" Set JANT_TOKEN env var (recommended):");
225
- console.log(" export JANT_TOKEN=jnt_your_token");
226
- console.log(" jant import-site --url https://your-site.com");
1283
+ console.log(` Set ${CLI_API_TOKEN_ENV_VAR} env var (recommended):`);
1284
+ console.log(` export ${CLI_API_TOKEN_ENV_VAR}=jnt_your_token`);
1285
+ console.log(" jant site import --url https://your-site.com");
1286
+ console.log("");
1287
+ console.log("Examples:");
1288
+ console.log(" jant site import --path ./jant-site");
1289
+ console.log(" jant site import --path ./jant-site-export.zip");
1290
+ console.log("");
1291
+ console.log("Compatibility alias: jant import-site");
227
1292
  process.exit(0);
228
1293
  }
229
1294
 
230
- if (!values.url) {
231
- console.error("Error: --url is required");
232
- process.exit(1);
233
- }
234
-
235
- const token = process.env.JANT_TOKEN || values.token;
236
- if (!token && !values["dry-run"]) {
237
- console.error("Error: JANT_TOKEN env var is required (unless using --dry-run)");
1295
+ const token = getCliApiToken(process.env, values.token);
1296
+ if (values.url && !token && !values["dry-run"]) {
1297
+ console.error(
1298
+ `Error: remote import requires ${CLI_API_TOKEN_ENV_VAR} or --token (unless using --dry-run)`,
1299
+ );
238
1300
  console.error("");
239
- console.error(" export JANT_TOKEN=jnt_your_token");
1301
+ console.error(` export ${CLI_API_TOKEN_ENV_VAR}=jnt_your_token`);
240
1302
  process.exit(1);
241
1303
  }
242
1304
 
243
- const apiUrl = values.url.replace(/\/$/, "");
1305
+ const apiUrl = values.url?.replace(/\/$/, "");
244
1306
  const dryRun = values["dry-run"];
245
1307
  const skipMedia = values["skip-media"];
1308
+ const target = dryRun
1309
+ ? null
1310
+ : values.url
1311
+ ? createRemoteTarget(apiUrl, token)
1312
+ : await createLocalTarget(process.env);
246
1313
 
247
1314
  // 1. Read source — directory or ZIP
248
1315
  const inputPath = resolve(process.cwd(), values.path);
@@ -255,275 +1322,472 @@ export async function run(argv) {
255
1322
 
256
1323
  const postFiles = [];
257
1324
  const collectionFiles = [];
1325
+ let siteConfig = null;
1326
+ let customCss = "";
1327
+ let sourceRootDir = inputPath;
1328
+ let tempSourceRootDir = null;
258
1329
 
259
1330
  if (inputStat.isDirectory()) {
260
1331
  console.log(`Reading directory ${inputPath}...`);
261
- await walkContent(inputPath, postFiles, collectionFiles);
1332
+ await walkContent(sourceRootDir, postFiles, collectionFiles);
1333
+ const configPath = join(sourceRootDir, "config.toml");
1334
+ const configContent = await readFile(configPath, "utf-8").catch(() => null);
1335
+ if (configContent) {
1336
+ siteConfig = await parseToml(configContent);
1337
+ }
1338
+ customCss = await readFile(
1339
+ join(sourceRootDir, "static", "custom.css"),
1340
+ "utf-8",
1341
+ ).catch(() => "");
262
1342
  } else {
263
1343
  console.log(`Reading ZIP ${inputPath}...`);
264
1344
  const zipData = await readFile(inputPath);
265
1345
  const { unzipSync } = await import("fflate");
266
1346
  const files = unzipSync(new Uint8Array(zipData));
267
- const decoder = new TextDecoder();
268
-
1347
+ tempSourceRootDir = await mkdtemp(join(tmpdir(), "jant-site-import-"));
1348
+ sourceRootDir = tempSourceRootDir;
269
1349
  for (const [path, data] of Object.entries(files)) {
270
- if (path.startsWith("content/c/") && path.endsWith("/_index.md")) {
271
- collectionFiles.push({ path, content: decoder.decode(data) });
272
- } else if (
273
- path.startsWith("content/") &&
274
- path.endsWith("/index.md") &&
275
- path !== "content/_index.md"
276
- ) {
277
- postFiles.push({ path, content: decoder.decode(data) });
278
- }
1350
+ const fullPath = join(sourceRootDir, path);
1351
+ await mkdir(dirname(fullPath), { recursive: true });
1352
+ await writeFile(fullPath, data);
279
1353
  }
280
- }
281
-
282
- console.log(
283
- `Found ${postFiles.length} posts and ${collectionFiles.length} collections`,
284
- );
285
-
286
- // 3. Fetch existing collections and create missing ones
287
- const collectionSlugToId = new Map();
288
1354
 
289
- if (!dryRun) {
290
- try {
291
- const existing = await apiCall("GET", "/api/collections", apiUrl, token);
292
- for (const col of existing.collections || []) {
293
- collectionSlugToId.set(col.slug, col.id);
294
- }
295
- } catch (err) {
296
- console.error(`Error fetching existing collections: ${err.message}`);
297
- process.exit(1);
1355
+ await walkContent(sourceRootDir, postFiles, collectionFiles);
1356
+ const configPath = join(sourceRootDir, "config.toml");
1357
+ const configContent = await readFile(configPath, "utf-8").catch(() => null);
1358
+ if (configContent) {
1359
+ siteConfig = await parseToml(configContent);
298
1360
  }
1361
+ customCss = await readFile(
1362
+ join(sourceRootDir, "static", "custom.css"),
1363
+ "utf-8",
1364
+ ).catch(() => "");
299
1365
  }
300
1366
 
301
- for (const { path, content } of collectionFiles) {
302
- const { frontMatter } = await parseFrontMatter(content);
303
- const slug = path.replace("content/c/", "").replace("/_index.md", "");
304
-
305
- if (collectionSlugToId.has(slug)) {
306
- console.log(`Skipped collection (exists): ${frontMatter.title || slug}`);
307
- continue;
1367
+ try {
1368
+ console.log(
1369
+ `Found ${postFiles.length} posts and ${collectionFiles.length} collections`,
1370
+ );
1371
+
1372
+ if (target) {
1373
+ const setupError = await getIncompleteSetupError(
1374
+ target,
1375
+ values.url ? `Target site at ${apiUrl}` : "Local target site",
1376
+ );
1377
+ if (setupError) {
1378
+ console.error("");
1379
+ console.error(setupError);
1380
+ console.error("");
1381
+ process.exit(1);
1382
+ }
308
1383
  }
309
1384
 
310
- if (dryRun) {
311
- console.log(`[dry-run] Would create collection: ${frontMatter.title || slug}`);
312
- collectionSlugToId.set(slug, `dry-run-${slug}`);
313
- continue;
314
- }
1385
+ if (siteConfig) {
1386
+ const settingsUpdates = buildSettingsUpdatesFromConfig(
1387
+ siteConfig,
1388
+ customCss,
1389
+ );
1390
+ const importedNav = normalizeImportedNavItems(siteConfig);
1391
+ const avatarImport = await buildSiteAvatarImport(
1392
+ siteConfig,
1393
+ sourceRootDir,
1394
+ );
315
1395
 
316
- try {
317
- const result = await apiCall("POST", "/api/collections", apiUrl, token, {
318
- title: frontMatter.title || slug,
319
- slug,
320
- description: frontMatter.description || null,
321
- });
322
- collectionSlugToId.set(slug, result.id);
323
- console.log(`Created collection: ${frontMatter.title || slug}`);
324
- } catch (err) {
325
- console.error(`Error creating collection "${slug}": ${err.message}`);
326
- process.exit(1);
327
- }
328
- }
1396
+ if (dryRun) {
1397
+ console.log("[dry-run] Would apply exported site settings");
1398
+ if (importedNav.exported) {
1399
+ console.log(
1400
+ `[dry-run] Would replace navigation with ${importedNav.items.length} items`,
1401
+ );
1402
+ }
1403
+ if (avatarImport && !skipMedia) {
1404
+ if (avatarImport.mode === "remove") {
1405
+ console.log("[dry-run] Would remove existing site avatar");
1406
+ } else {
1407
+ console.log("[dry-run] Would import exported site avatar");
1408
+ }
1409
+ }
1410
+ } else {
1411
+ try {
1412
+ const result = await target.updateSettings(settingsUpdates);
1413
+ if (result?.rejectedKeys?.length) {
1414
+ console.warn(
1415
+ `Warning: Some site settings were rejected: ${result.rejectedKeys.join(", ")}`,
1416
+ );
1417
+ }
1418
+ } catch (err) {
1419
+ console.error(
1420
+ `Error applying exported site settings: ${err.message}`,
1421
+ );
1422
+ process.exit(1);
1423
+ }
329
1424
 
330
- // 4. Process posts
331
- let postsCreated = 0;
332
- let repliesCreated = 0;
333
- let imagesUploaded = 0;
334
- let aliasesCreated = 0;
335
- let skipped = 0;
1425
+ if (importedNav.exported) {
1426
+ try {
1427
+ const existingNavItems = await target.listNavItems();
1428
+ for (const item of existingNavItems) {
1429
+ await target.deleteNavItem(item.id);
1430
+ }
1431
+ for (const item of importedNav.items) {
1432
+ await target.createNavItem(item);
1433
+ }
1434
+ } catch (err) {
1435
+ console.error(`Error importing navigation: ${err.message}`);
1436
+ process.exit(1);
1437
+ }
1438
+ }
336
1439
 
337
- for (const { path, content } of postFiles) {
338
- const { frontMatter, body } = await parseFrontMatter(content);
1440
+ if (avatarImport && !skipMedia) {
1441
+ try {
1442
+ await target.syncSiteAvatar(
1443
+ avatarImport.mode === "set" ? avatarImport : null,
1444
+ );
1445
+ } catch (err) {
1446
+ console.error(`Error importing site avatar: ${err.message}`);
1447
+ process.exit(1);
1448
+ }
1449
+ }
1450
+ }
1451
+ }
339
1452
 
340
- const segments = splitReplies(body);
341
- const rootSegment = segments[0];
342
- const replySegments = segments.slice(1);
1453
+ // 3. Fetch existing collections and create missing ones
1454
+ const collectionSlugToId = new Map();
343
1455
 
344
- // Resolve collection IDs from taxonomy slugs
345
- const collectionIds = [];
346
- const taxonomyCollections = frontMatter.taxonomies?.c || frontMatter.taxonomies?.collections || [];
347
- for (const colSlug of taxonomyCollections) {
348
- const id = collectionSlugToId.get(colSlug);
349
- if (id) collectionIds.push(id);
1456
+ if (!dryRun) {
1457
+ try {
1458
+ const existingCollections = await target.listCollections();
1459
+ for (const col of existingCollections) {
1460
+ collectionSlugToId.set(col.slug, col.id);
1461
+ }
1462
+ } catch (err) {
1463
+ console.error(`Error fetching existing collections: ${err.message}`);
1464
+ process.exit(1);
1465
+ }
350
1466
  }
351
1467
 
352
- // Process images in root body
353
- let rootBody = rootSegment?.body || "";
354
- const mediaIds = [];
355
-
356
- if (!skipMedia && !dryRun && rootBody) {
357
- const imageUrls = findImageUrls(rootBody);
358
- const urlMap = new Map();
359
-
360
- for (const imageUrl of imageUrls) {
361
- if (imageUrl.startsWith("data:")) continue;
362
- const result = await uploadImage(imageUrl, apiUrl, token);
363
- if (result) {
364
- urlMap.set(imageUrl, result.url);
365
- mediaIds.push(result.id);
366
- imagesUploaded++;
367
- }
1468
+ for (const { path, content } of collectionFiles) {
1469
+ const { frontMatter } = await parseFrontMatter(content);
1470
+ const slug = path
1471
+ .replace("content/jant-collections/", "")
1472
+ .replace("content/c/", "")
1473
+ .replace("/_index.md", "");
1474
+
1475
+ if (collectionSlugToId.has(slug)) {
1476
+ console.error(
1477
+ `Import conflict: collection slug "${slug}" is already in use. Import into an empty site or remove the existing collection first.`,
1478
+ );
1479
+ process.exit(1);
368
1480
  }
369
1481
 
370
- if (urlMap.size > 0) {
371
- rootBody = replaceImageUrls(rootBody, urlMap);
1482
+ if (dryRun) {
1483
+ console.log(
1484
+ `[dry-run] Would create collection: ${frontMatter.title || slug}`,
1485
+ );
1486
+ collectionSlugToId.set(slug, `dry-run-${slug}`);
1487
+ continue;
1488
+ }
1489
+
1490
+ try {
1491
+ const collectionExtra = frontMatter.extra || {};
1492
+ const result = await target.createCollection({
1493
+ title: frontMatter.title || slug,
1494
+ slug,
1495
+ description: frontMatter.description || null,
1496
+ sortOrder:
1497
+ collectionExtra.sort_order ||
1498
+ collectionExtra.sortOrder ||
1499
+ undefined,
1500
+ });
1501
+ collectionSlugToId.set(slug, result.id);
1502
+ console.log(`Created collection: ${frontMatter.title || slug}`);
1503
+ } catch (err) {
1504
+ console.error(`Error creating collection "${slug}": ${err.message}`);
1505
+ process.exit(1);
372
1506
  }
373
1507
  }
374
1508
 
375
- const extra = frontMatter.extra || {};
376
- const format = extra.format || "note";
377
-
378
- const postData = {
379
- format,
380
- title: frontMatter.title != null ? String(frontMatter.title) : undefined,
381
- bodyMarkdown: rootBody || undefined,
382
- slug: frontMatter.slug != null ? String(frontMatter.slug) : undefined,
383
- status: frontMatter.draft ? "draft" : "published",
384
- collectionIds: collectionIds.length > 0 ? collectionIds : undefined,
385
- mediaIds: mediaIds.length > 0 ? mediaIds : undefined,
386
- publishedAt:
387
- !frontMatter.draft && frontMatter.date
388
- ? Math.floor(new Date(frontMatter.date).getTime() / 1000)
389
- : undefined,
390
- pinned: extra.pinned || undefined,
391
- featured: extra.featured || undefined,
392
- rating: extra.rating || undefined,
393
- };
1509
+ // 4. Process posts
1510
+ let postsCreated = 0;
1511
+ let repliesCreated = 0;
1512
+ let mediaUploaded = 0;
1513
+ let aliasesCreated = 0;
1514
+
1515
+ for (const { path, content } of postFiles) {
1516
+ const { frontMatter, body } = await parseFrontMatter(content);
1517
+
1518
+ const segments = splitReplies(body);
1519
+ const rootSegment = segments[0];
1520
+ const replySegments = segments.slice(1);
1521
+ const replySlugPaths = collectReplySlugPaths(replySegments);
1522
+
1523
+ // Resolve collection IDs from taxonomy slugs
1524
+ const collectionIds = [];
1525
+ const taxonomyCollections =
1526
+ frontMatter.taxonomies?.c || frontMatter.taxonomies?.collections || [];
1527
+ for (const colSlug of taxonomyCollections) {
1528
+ const id = collectionSlugToId.get(colSlug);
1529
+ if (id) collectionIds.push(id);
1530
+ }
394
1531
 
395
- if (format === "link" && extra.link_url) {
396
- postData.url = extra.link_url;
397
- }
398
- if (format === "quote" && extra.quote_text) {
399
- postData.quoteText = extra.quote_text;
400
- }
1532
+ const extra = frontMatter.extra || {};
1533
+ const format = extra.format || "note";
1534
+ const postSlug =
1535
+ frontMatter.slug != null ? String(frontMatter.slug) : undefined;
1536
+ const postStatus =
1537
+ extra.status === "draft" || extra.status === "published"
1538
+ ? extra.status
1539
+ : frontMatter.draft
1540
+ ? "draft"
1541
+ : "published";
1542
+ const postVisibility =
1543
+ extra.visibility === "unlisted" || extra.visibility === "private"
1544
+ ? extra.visibility
1545
+ : undefined;
1546
+ const postLabel =
1547
+ (format === "quote" ? extra.source_name : frontMatter.title) ||
1548
+ postSlug ||
1549
+ "(untitled)";
1550
+
1551
+ if (!dryRun && postSlug) {
1552
+ await assertImportSlugAvailable(target, postSlug, postLabel, "post");
1553
+ }
401
1554
 
402
- if (dryRun) {
403
- console.log(
404
- `[dry-run] Would create post: ${frontMatter.title || frontMatter.slug || "(untitled)"} (${format})`,
405
- );
406
- if (replySegments.length > 0) {
407
- console.log(` [dry-run] With ${replySegments.length} replies`);
1555
+ // Process images in root body
1556
+ let rootBody = rootSegment?.body || "";
1557
+ const normalizedRootBody = normalizeImportedBodySegment(rootBody);
1558
+ rootBody = normalizedRootBody.markdown;
1559
+ let importedAttachments = [];
1560
+
1561
+ if (!skipMedia && !dryRun) {
1562
+ const imageMedia = findImageUrls(rootBody).map((src) => ({ src }));
1563
+ const uploadResult = await uploadMediaList(
1564
+ imageMedia,
1565
+ target,
1566
+ siteConfig,
1567
+ sourceRootDir,
1568
+ );
1569
+ mediaUploaded += uploadResult.uploaded;
1570
+
1571
+ if (uploadResult.urlMap.size > 0) {
1572
+ rootBody = rewriteMediaReferences(rootBody, uploadResult.urlMap);
1573
+ }
1574
+ }
1575
+ if (!dryRun) {
1576
+ const attachmentResult = await buildImportedAttachments(
1577
+ normalizedRootBody.attachments,
1578
+ target,
1579
+ siteConfig,
1580
+ sourceRootDir,
1581
+ { skipUploads: skipMedia },
1582
+ );
1583
+ importedAttachments = attachmentResult.attachments;
1584
+ mediaUploaded += attachmentResult.uploaded;
408
1585
  }
409
- postsCreated++;
410
- repliesCreated += replySegments.length;
411
- continue;
412
- }
413
1586
 
414
- const postLabel = frontMatter.title || frontMatter.slug || "(untitled)";
1587
+ const postData = {
1588
+ format,
1589
+ title:
1590
+ format === "quote"
1591
+ ? typeof extra.source_name === "string"
1592
+ ? extra.source_name
1593
+ : undefined
1594
+ : frontMatter.title != null
1595
+ ? String(frontMatter.title)
1596
+ : undefined,
1597
+ bodyMarkdown: rootBody || undefined,
1598
+ slug: postSlug,
1599
+ path: frontMatter.path != null ? String(frontMatter.path) : undefined,
1600
+ status: postStatus,
1601
+ visibility: postVisibility,
1602
+ collectionIds: collectionIds.length > 0 ? collectionIds : undefined,
1603
+ attachments:
1604
+ importedAttachments.length > 0 ? importedAttachments : undefined,
1605
+ publishedAt:
1606
+ postStatus === "published" && frontMatter.date
1607
+ ? Math.floor(new Date(frontMatter.date).getTime() / 1000)
1608
+ : undefined,
1609
+ pinned: extra.pinned || undefined,
1610
+ featured: extra.featured || undefined,
1611
+ rating: extra.rating || undefined,
1612
+ };
415
1613
 
416
- const progress = `[${postsCreated + skipped + 1}/${postFiles.length}]`;
1614
+ if (format === "link" && extra.link_url) {
1615
+ postData.url = extra.link_url;
1616
+ }
1617
+ if (format === "quote" && extra.quote_text) {
1618
+ postData.quoteText = extra.quote_text;
1619
+ if (typeof extra.source_url === "string" && extra.source_url.trim()) {
1620
+ postData.url = extra.source_url;
1621
+ }
1622
+ }
417
1623
 
418
- let post;
419
- try {
420
- post = await apiCall("POST", "/api/posts", apiUrl, token, postData);
421
- postsCreated++;
422
- const replyInfo = replySegments.length > 0 ? ` (+${replySegments.length} replies)` : "";
423
- console.log(`${progress} Created: ${postLabel}${replyInfo}`);
424
- } catch (err) {
425
- if (err.status === 409) {
426
- console.log(`${progress} Skipped: ${postLabel}`);
427
- skipped++;
428
- } else {
429
- console.error(`Error creating post "${postLabel}": ${err.message}`);
430
- process.exit(1);
1624
+ if (dryRun) {
1625
+ console.log(`[dry-run] Would create post: ${postLabel} (${format})`);
1626
+ if (replySegments.length > 0) {
1627
+ console.log(` [dry-run] With ${replySegments.length} replies`);
1628
+ }
1629
+ postsCreated++;
1630
+ repliesCreated += replySegments.length;
1631
+ continue;
431
1632
  }
432
- }
433
1633
 
434
- // Create custom URL aliases from front matter (also for skipped posts)
435
- const aliases = frontMatter.aliases || [];
436
- const postSlug = frontMatter.slug != null ? String(frontMatter.slug) : post?.slug;
437
- for (const alias of aliases) {
438
- const aliasPath = alias.startsWith("/") ? alias : `/${alias}`;
439
- if (aliasPath === `/${postSlug}`) continue; // skip self-reference
1634
+ const progress = `[${postsCreated + 1}/${postFiles.length}]`;
1635
+
1636
+ let post;
440
1637
  try {
441
- await apiCall("POST", "/api/custom-urls", apiUrl, token, {
442
- path: aliasPath,
443
- targetType: "post",
444
- targetId: postSlug,
445
- });
446
- aliasesCreated++;
1638
+ post = await target.createPost(postData);
1639
+ postsCreated++;
1640
+ const replyInfo =
1641
+ replySegments.length > 0 ? ` (+${replySegments.length} replies)` : "";
1642
+ console.log(`${progress} Created: ${postLabel}${replyInfo}`);
447
1643
  } catch (err) {
448
- if (err.status === 409) continue; // alias already exists
449
- console.warn(` Warning: Failed to create alias "${aliasPath}": ${err.message}`);
1644
+ console.error(`Error creating post "${postLabel}": ${err.message}`);
1645
+ process.exit(1);
450
1646
  }
451
- }
452
1647
 
453
- // Create replies (only for newly created posts)
454
- if (!post) continue;
455
- for (const replySegment of replySegments) {
456
- const replyAttrs = replySegment.attrs || {};
457
- let replyBody = replySegment.body || "";
458
- const replyMediaIds = [];
459
-
460
- if (!skipMedia && replyBody) {
461
- const imageUrls = findImageUrls(replyBody);
462
- const urlMap = new Map();
463
-
464
- for (const imageUrl of imageUrls) {
465
- if (imageUrl.startsWith("data:")) continue;
466
- const result = await uploadImage(imageUrl, apiUrl, token);
467
- if (result) {
468
- urlMap.set(imageUrl, result.url);
469
- replyMediaIds.push(result.id);
470
- imagesUploaded++;
1648
+ // Create replies before aliases so reply slugs can claim their own paths.
1649
+ if (!post) continue;
1650
+ for (const replySegment of replySegments) {
1651
+ const replyAttrs = replySegment.attrs || {};
1652
+ const replySlug = replyAttrs.slug || undefined;
1653
+ const replyLabel =
1654
+ replyAttrs.source_name ||
1655
+ replyAttrs.title ||
1656
+ replySlug ||
1657
+ "(untitled reply)";
1658
+ if (!dryRun && replySlug) {
1659
+ await assertImportSlugAvailable(
1660
+ target,
1661
+ replySlug,
1662
+ `${replyLabel} in ${postLabel}`,
1663
+ "reply",
1664
+ );
1665
+ }
1666
+ let replyBody = replySegment.body || "";
1667
+ const normalizedReplyBody = normalizeImportedBodySegment(replyBody);
1668
+ replyBody = normalizedReplyBody.markdown;
1669
+ let replyAttachments = [];
1670
+
1671
+ if (!skipMedia && !dryRun) {
1672
+ const imageMedia = findImageUrls(replyBody).map((src) => ({ src }));
1673
+ const uploadResult = await uploadMediaList(
1674
+ imageMedia,
1675
+ target,
1676
+ siteConfig,
1677
+ sourceRootDir,
1678
+ );
1679
+ mediaUploaded += uploadResult.uploaded;
1680
+
1681
+ if (uploadResult.urlMap.size > 0) {
1682
+ replyBody = rewriteMediaReferences(replyBody, uploadResult.urlMap);
471
1683
  }
472
1684
  }
473
-
474
- if (urlMap.size > 0) {
475
- replyBody = replaceImageUrls(replyBody, urlMap);
1685
+ if (!dryRun) {
1686
+ const attachmentResult = await buildImportedAttachments(
1687
+ normalizedReplyBody.attachments,
1688
+ target,
1689
+ siteConfig,
1690
+ sourceRootDir,
1691
+ { skipUploads: skipMedia },
1692
+ );
1693
+ replyAttachments = attachmentResult.attachments;
1694
+ mediaUploaded += attachmentResult.uploaded;
476
1695
  }
477
- }
478
1696
 
479
- const replyFormat = replyAttrs.format || "note";
480
- const replyData = {
481
- format: replyFormat,
482
- title: replyAttrs.title || undefined,
483
- bodyMarkdown: replyBody || undefined,
484
- replyToId: post.id,
485
- mediaIds: replyMediaIds.length > 0 ? replyMediaIds : undefined,
486
- publishedAt: replyAttrs.date
487
- ? Math.floor(new Date(replyAttrs.date).getTime() / 1000)
488
- : undefined,
489
- rating: replyAttrs.rating ? Number(replyAttrs.rating) : undefined,
490
- };
1697
+ const replyFormat = replyAttrs.format || "note";
1698
+ const replyStatus =
1699
+ replyAttrs.status === "draft" || replyAttrs.status === "published"
1700
+ ? replyAttrs.status
1701
+ : "published";
1702
+ const replyVisibility =
1703
+ replyAttrs.visibility === "unlisted" ||
1704
+ replyAttrs.visibility === "private"
1705
+ ? replyAttrs.visibility
1706
+ : undefined;
1707
+ const replyData = {
1708
+ format: replyFormat,
1709
+ status: replyStatus,
1710
+ title:
1711
+ replyFormat === "quote"
1712
+ ? replyAttrs.source_name || undefined
1713
+ : replyAttrs.title || undefined,
1714
+ bodyMarkdown: replyBody || undefined,
1715
+ replyToId: post.id,
1716
+ slug: replySlug,
1717
+ visibility: replyVisibility,
1718
+ attachments:
1719
+ replyAttachments.length > 0 ? replyAttachments : undefined,
1720
+ publishedAt: replyAttrs.date
1721
+ ? Math.floor(new Date(replyAttrs.date).getTime() / 1000)
1722
+ : undefined,
1723
+ rating: replyAttrs.rating ? Number(replyAttrs.rating) : undefined,
1724
+ };
1725
+
1726
+ if (replyFormat === "link" && replyAttrs.url) {
1727
+ replyData.url = replyAttrs.url;
1728
+ }
1729
+ if (replyFormat === "quote" && replyAttrs.quote_text) {
1730
+ replyData.quoteText = decodeURIComponent(replyAttrs.quote_text);
1731
+ if (replyAttrs.source_url) {
1732
+ replyData.url = replyAttrs.source_url;
1733
+ }
1734
+ }
491
1735
 
492
- if (replyFormat === "link" && replyAttrs.url) {
493
- replyData.url = replyAttrs.url;
494
- }
495
- if (replyFormat === "quote" && replyAttrs.quote_text) {
496
- replyData.quoteText = decodeURIComponent(replyAttrs.quote_text);
1736
+ try {
1737
+ await target.createPost(replyData);
1738
+ repliesCreated++;
1739
+ } catch (err) {
1740
+ console.error(` Error creating reply: ${err.message}`);
1741
+ process.exit(1);
1742
+ }
497
1743
  }
498
1744
 
1745
+ // Create exported root aliases after replies. Reply slugs are handled by
1746
+ // the thread markers; only true root aliases round-trip back into Jant.
1747
+ const rootTargetSlug = postSlug || post.slug;
1748
+ let aliasPaths;
499
1749
  try {
500
- await apiCall("POST", "/api/posts", apiUrl, token, replyData);
501
- repliesCreated++;
1750
+ aliasPaths = getRootAliasPathsForImport(
1751
+ getExportedRootAliases(frontMatter),
1752
+ rootTargetSlug,
1753
+ replySlugPaths,
1754
+ );
502
1755
  } catch (err) {
503
- if (err.status === 409) {
504
- console.log(` Skipped reply (exists)`);
505
- skipped++;
506
- continue;
507
- }
508
- console.error(` Error creating reply: ${err.message}`);
1756
+ console.error(
1757
+ `Error importing aliases for "${postLabel}": ${err.message}`,
1758
+ );
509
1759
  process.exit(1);
510
1760
  }
1761
+ for (const aliasPath of aliasPaths) {
1762
+ try {
1763
+ await target.createAlias(aliasPath, rootTargetSlug);
1764
+ aliasesCreated++;
1765
+ } catch (err) {
1766
+ console.error(
1767
+ `Error creating alias "${aliasPath}" for "${postLabel}": ${err.message}`,
1768
+ );
1769
+ process.exit(1);
1770
+ }
1771
+ }
511
1772
  }
512
- }
513
1773
 
514
- // 5. Summary
515
- console.log("");
516
- console.log("Import complete:");
517
- console.log(` Posts created: ${postsCreated}`);
518
- console.log(` Replies created: ${repliesCreated}`);
519
- console.log(` Images uploaded: ${imagesUploaded}`);
520
- if (aliasesCreated > 0) {
521
- console.log(` Aliases created: ${aliasesCreated}`);
522
- }
523
- if (skipped > 0) {
524
- console.log(` Skipped (already exist): ${skipped}`);
525
- }
526
- if (dryRun) {
527
- console.log(" (dry-run mode — no changes were made)");
1774
+ await target?.close();
1775
+
1776
+ // 5. Summary
1777
+ console.log("");
1778
+ console.log("Import complete:");
1779
+ console.log(` Posts created: ${postsCreated}`);
1780
+ console.log(` Replies created: ${repliesCreated}`);
1781
+ console.log(` Media uploaded: ${mediaUploaded}`);
1782
+ if (aliasesCreated > 0) {
1783
+ console.log(` Aliases created: ${aliasesCreated}`);
1784
+ }
1785
+ if (dryRun) {
1786
+ console.log(" (dry-run mode — no changes were made)");
1787
+ }
1788
+ } finally {
1789
+ if (tempSourceRootDir) {
1790
+ await rm(tempSourceRootDir, { recursive: true, force: true });
1791
+ }
528
1792
  }
529
1793
  }