@jant/core 0.3.40 → 0.3.42
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.
- package/README.md +21 -34
- package/bin/commands/assets/upload.js +247 -0
- package/bin/commands/collections.js +268 -0
- package/bin/commands/import-site.js +1169 -343
- package/bin/commands/media.js +302 -0
- package/bin/commands/migrate-text-attachments.js +183 -0
- package/bin/commands/migrate.js +263 -2
- package/bin/commands/posts.js +262 -0
- package/bin/commands/search.js +53 -0
- package/bin/commands/settings.js +93 -0
- package/bin/commands/site/export.js +34 -33
- package/bin/commands/site/{localize-media.js → pull-media.js} +17 -17
- package/bin/lib/d1-query.js +15 -3
- package/bin/lib/http-api.js +223 -0
- package/bin/lib/media-upload.js +206 -0
- package/bin/lib/migration-runner.js +64 -0
- package/bin/lib/runtime-target.js +7 -2
- package/bin/lib/site-media-parser.js +71 -16
- package/bin/lib/site-pull-media.js +698 -0
- package/bin/lib/site-snapshot.js +0 -1
- package/dist/{app-CAtsuLLh.js → app-Cu3lveYI.js} +23768 -23057
- package/dist/app-DzCB4yOp.js +5 -0
- package/dist/client/.vite/manifest.json +3485 -0
- package/dist/client/_assets/chunks/02611a045a7fe83a12014e3debc9f731-BqEDtwXC.woff2 +0 -0
- package/dist/client/_assets/chunks/033466ef683afe931f7f520cfb42d928-BluLHEh9.woff2 +0 -0
- package/dist/client/_assets/chunks/04b8bde59cff68eeee74fb1914eb09c7-D5j2CLTt.woff2 +0 -0
- package/dist/client/_assets/chunks/057a6a98bda7fe57105ddaa99ec82015-DY_AoijT.woff2 +0 -0
- package/dist/client/_assets/chunks/05ac821472e235943ed1435e4bb8ecad-CUm8GiH6.woff2 +0 -0
- package/dist/client/_assets/chunks/05da12edb9d52210581dc6ec4541031f-BXAOx2XP.woff2 +0 -0
- package/dist/client/_assets/chunks/074c1f8483d5a4d8c45c8c5f3e4cbb32-CDqdMQm8.woff2 +0 -0
- package/dist/client/_assets/chunks/0968e4861204b51f62a2f8e9f15dd5e0-D5tLgF_8.woff2 +0 -0
- package/dist/client/_assets/chunks/0acd1fe2b2ea1ad1bfee7ae1fa139d27-BKK1m412.woff2 +0 -0
- package/dist/client/_assets/chunks/0c2ab6b295e55f356f8020d4e7747522-CkCQjQw3.woff2 +0 -0
- package/dist/client/_assets/chunks/0c5f9492af03a4fa42c784de94649de1-CQlJqksa.woff2 +0 -0
- package/dist/client/_assets/chunks/0d5dec931dc885f07fe5cd5af8bed675-C74x-U_U.woff2 +0 -0
- package/dist/client/_assets/chunks/0d9a936885a4c39077438effd3779cbd-Bw0fYF4S.woff2 +0 -0
- package/dist/client/_assets/chunks/0e645da524f7cfc0e8c3c03fb2b08428-BVeI_5Th.woff2 +0 -0
- package/dist/client/_assets/chunks/0f5e1a18987dbc84ca05188c129e1936-B6_lZ6fE.woff2 +0 -0
- package/dist/client/_assets/chunks/1076f0f6f66d28d7a2f16427faad4413-ubiXnD1P.woff2 +0 -0
- package/dist/client/_assets/chunks/112743a4ab5fdd1498dfdf2b11336380-B5FzQxTK.woff2 +0 -0
- package/dist/client/_assets/chunks/1211f03d3ff5759a702631445793f60e-DDcSiyCt.woff2 +0 -0
- package/dist/client/_assets/chunks/12ef1ba76bd20b004b865266a1aa70b3-BwmaD87S.woff2 +0 -0
- package/dist/client/_assets/chunks/135e83b403475c5dc9e49b43501a5b84-C5mE_KZS.woff2 +0 -0
- package/dist/client/_assets/chunks/1380210a722ac9aeef54005ad7b015c9-CjA-MP82.woff2 +0 -0
- package/dist/client/_assets/chunks/13b2c53b30e6a3e4de7132dbc18dbdfc-B9WOB7kv.woff2 +0 -0
- package/dist/client/_assets/chunks/147b24c2f08d03bbed30887d4cb3311c-IeeFCJ13.woff2 +0 -0
- package/dist/client/_assets/chunks/152395634a207579552f8cb54db88599-C2y2JyNZ.woff2 +0 -0
- package/dist/client/_assets/chunks/15d95680dc31cc6ce20e0a5464106dc4-CpD8dqRm.woff2 +0 -0
- package/dist/client/_assets/chunks/16663e567f81f4725a1522f37e18f71f-BNoxS9pQ.woff2 +0 -0
- package/dist/client/_assets/chunks/1733f27476507ca68b68a803bc533cc2-DXL-14Ug.woff2 +0 -0
- package/dist/client/_assets/chunks/17c7f5d0a45e92ede0e5dec3a2c77efa-CNj3jQMl.woff2 +0 -0
- package/dist/client/_assets/chunks/1824321da801e6257b902f3d1d09054c-D4dgdyWB.woff2 +0 -0
- package/dist/client/_assets/chunks/188c2db794f3dd7a45889ddbc81da9bb-C9NbUBBO.woff2 +0 -0
- package/dist/client/_assets/chunks/1a08931435f885e707edb85978ea3bb6-BOiP2u7G.woff2 +0 -0
- package/dist/client/_assets/chunks/1a3a92c7c060a71a6c35819b9ebcdbb9-DFfonQfz.woff2 +0 -0
- package/dist/client/_assets/chunks/1be602ad456b0d75902d352116cb35fd-Bc-XQtQh.woff2 +0 -0
- package/dist/client/_assets/chunks/1cf737900dd49c2e88f1b3221a82a602-CfTmnns3.woff2 +0 -0
- package/dist/client/_assets/chunks/1fba7ec0e412e911bf31841de5a8a4e4-18ELMbFF.woff2 +0 -0
- package/dist/client/_assets/chunks/233d3a685ee18276b319363474599d47-BaFdJ0-G.woff2 +0 -0
- package/dist/client/_assets/chunks/2612c60f9dc5459ac42763591240a1d6-CTD69cq1.woff2 +0 -0
- package/dist/client/_assets/chunks/284b53bbefb06924cf236d24c8ed5641-CPDiuOv6.woff2 +0 -0
- package/dist/client/_assets/chunks/28a97af9ab9d38983d20f39ff09840e1-mFqcT3Jw.woff2 +0 -0
- package/dist/client/_assets/chunks/2a56eaf19d1d38a6b57e2a388f733676-CU52qBlE.woff2 +0 -0
- package/dist/client/_assets/chunks/2ab8cf1f23a5ac7939a7876054e711a7-B4T3dK7A.woff2 +0 -0
- package/dist/client/_assets/chunks/2abfbab82b6a7c04426afc054d2464da-D5PIIHde.woff2 +0 -0
- package/dist/client/_assets/chunks/2bfaadaf3479c72286248e6de0be0ec9-DEpU6O74.woff2 +0 -0
- package/dist/client/_assets/chunks/2c8c55e4cec85ce77e95cac9d330f5cf-BCTA96EZ.woff2 +0 -0
- package/dist/client/_assets/chunks/2e6f4bb71ef6b38765d51acc5a79f638-Mbs6PECD.woff2 +0 -0
- package/dist/client/_assets/chunks/2ed9981d2e8983365bd051159b976b6d-D8gg6NWM.woff2 +0 -0
- package/dist/client/_assets/chunks/2f4c633e923ba30c6ba376367379fc91-CIJCFztv.woff2 +0 -0
- package/dist/client/_assets/chunks/31194d303a67561926a544ed0e072aee-w3-eGYAi.woff2 +0 -0
- package/dist/client/_assets/chunks/31a197213ae61d7043c81013f52d10d6-DXZrrkJP.woff2 +0 -0
- package/dist/client/_assets/chunks/349965eee0a9b6c984a319ab96a4ece9-DByAVmXw.woff2 +0 -0
- package/dist/client/_assets/chunks/34e6750e00c3a911ef87dc66b336594d-DNbWesOu.woff2 +0 -0
- package/dist/client/_assets/chunks/3623d96698132c61e861f984ce11cc23-jqV9ErzX.woff2 +0 -0
- package/dist/client/_assets/chunks/36cda3eae13370b934bba4429fab9078-BPDcUlw9.woff2 +0 -0
- package/dist/client/_assets/chunks/36f3df4730cfca4fc77fe52b52e6a126-BwK10NC0.woff2 +0 -0
- package/dist/client/_assets/chunks/37887deb7a9c466cf2af6ee7ff372ea6-BrAerJB1.woff2 +0 -0
- package/dist/client/_assets/chunks/37dbd564820cce2f7b3331e442da0df3-Cql2kzIi.woff2 +0 -0
- package/dist/client/_assets/chunks/37dc8505b20b37a2477a9121b16d44a7-CoXw6XWV.woff2 +0 -0
- package/dist/client/_assets/chunks/39211b02f24c69cbd1b54f6d41b01933-4Xu7vvnq.woff2 +0 -0
- package/dist/client/_assets/chunks/3aa13f1843d7c8fa16109b6395a9a29d-BQFZUc3B.woff2 +0 -0
- package/dist/client/_assets/chunks/3aba89c4d4281553078de4622b498cbb-8QS1zDYk.woff2 +0 -0
- package/dist/client/_assets/chunks/3ac0a07878cacdc98bb889f40f5970d3-C-BaM6-y.woff2 +0 -0
- package/dist/client/_assets/chunks/3d88558097559d775231194d43745ba7-CR2aqGC7.woff2 +0 -0
- package/dist/client/_assets/chunks/3dc3e8c35524cad2d1236f3393104ccf-9sqBsF7B.woff2 +0 -0
- package/dist/client/_assets/chunks/3e24063b19abd2b051af986a6870046d-TlweM3SW.woff2 +0 -0
- package/dist/client/_assets/chunks/3e2c06bdd9dcab29aeaeb0cfb7fa608a-BaqU-dIt.woff2 +0 -0
- package/dist/client/_assets/chunks/424123f9371ba357087363263eeafcda-C8SVfEJi.woff2 +0 -0
- package/dist/client/_assets/chunks/43b3bbf43055c3e65db9cdf2c5b18c1c-CbHx6-Ki.woff2 +0 -0
- package/dist/client/_assets/chunks/4410f876d27c9821839474b6b1059fd7-ClYz91aY.woff2 +0 -0
- package/dist/client/_assets/chunks/44704471b60c18b3ecec5300a88f1102-DDmmy6-m.woff2 +0 -0
- package/dist/client/_assets/chunks/45140742dfa8686b58db2899ce89f89a-D5IMMzDM.woff2 +0 -0
- package/dist/client/_assets/chunks/46604090efbb99c2db1800928af0a239-JX2fpYX_.woff2 +0 -0
- package/dist/client/_assets/chunks/46998df85d31e629c0142f0556f5e7c5-BIkNG9Wh.woff2 +0 -0
- package/dist/client/_assets/chunks/469d2754e315e77270c12fa0ab28026c-D-y-vsfA.woff2 +0 -0
- package/dist/client/_assets/chunks/479329a39a3eb6c0047e6b0981919855-vZKCRx42.woff2 +0 -0
- package/dist/client/_assets/chunks/491bbba374093fff045f55803a22bfef-DSQXiDCd.woff2 +0 -0
- package/dist/client/_assets/chunks/49902f1c41b281f8eb3771abdd4dccb8-DxVxNg2W.woff2 +0 -0
- package/dist/client/_assets/chunks/4c0760284569b87d8f2290c324953973-Bqd-TKHH.woff2 +0 -0
- package/dist/client/_assets/chunks/4cba92c942694bdc479bc54101bb1aa4-BrfsrgrC.woff2 +0 -0
- package/dist/client/_assets/chunks/4d38e56738156625329d93bd5c8cc74a-DONkMP0y.woff2 +0 -0
- package/dist/client/_assets/chunks/4db4f31a16965baa42cb7dad667a8f04-Dt1Cy4gr.woff2 +0 -0
- package/dist/client/_assets/chunks/4e7d263c30bb48604069a3b4404d4eeb-DNPCkvKD.woff2 +0 -0
- package/dist/client/_assets/chunks/5279c7e465d9871edf51fd02e691eee7-CKhyhvVf.woff2 +0 -0
- package/dist/client/_assets/chunks/527ff336279465617aafbafe1a830632-CC3lv4pm.woff2 +0 -0
- package/dist/client/_assets/chunks/54bfdb6f21f9d6191e5100580239e14f-PGlqYvgl.woff2 +0 -0
- package/dist/client/_assets/chunks/56b40518ea0608e62826bd44ee27f5f9-BD9F9Oss.woff2 +0 -0
- package/dist/client/_assets/chunks/56be5e32fd01668fe5ba814d94905839-CSyFA-9l.woff2 +0 -0
- package/dist/client/_assets/chunks/572b9e28f1e6d89b765a16d809db84a1-BIYMjzEa.woff2 +0 -0
- package/dist/client/_assets/chunks/57e9a86651366c1ba299e47aa7e358c2-BHuZXB4W.woff2 +0 -0
- package/dist/client/_assets/chunks/57f89adeae55aa7fe2add3fc1801ce03-Cy3PNrfx.woff2 +0 -0
- package/dist/client/_assets/chunks/5921cef36750ff6092d94b83a802823b-hF-uymTh.woff2 +0 -0
- package/dist/client/_assets/chunks/59e95d8b5332dcdae894a19946ab767d-ClRfsjWZ.woff2 +0 -0
- package/dist/client/_assets/chunks/5acc7f736217259db79a42bf44241c48-FYQUuFOM.woff2 +0 -0
- package/dist/client/_assets/chunks/5c05f8b75c9d7f25b3c04ca711cf43b3-D-YWPkSq.woff2 +0 -0
- package/dist/client/_assets/chunks/5df9ce98c75f8c50fb4fff7f01a23925-DgoCENyW.woff2 +0 -0
- package/dist/client/_assets/chunks/5e0124c7265f1b4cf0fc797b94efbebc-BTBNHsSB.woff2 +0 -0
- package/dist/client/_assets/chunks/5ec349ff62b9ee5fe0c6dae867704dc4-B4dezhrz.woff2 +0 -0
- package/dist/client/_assets/chunks/60020a830d809c26970cf8e48ba1eeda-B6FDTSik.woff2 +0 -0
- package/dist/client/_assets/chunks/601f812e8b1eafecd5ba585a6a6d7962-gIVBlUU9.woff2 +0 -0
- package/dist/client/_assets/chunks/6025ddaf99d86cc8bfd781006b19fbd6-CS-ze337.woff2 +0 -0
- package/dist/client/_assets/chunks/60bcfa3ea7446eb42394aab02d28be40-C-PQfCu3.woff2 +0 -0
- package/dist/client/_assets/chunks/60f6e28b3f0400b0e8fe32c3c7116102-Dag9L2qU.woff2 +0 -0
- package/dist/client/_assets/chunks/615a7e0d63f257109d599b2a1977de68-D6Y93yoG.woff2 +0 -0
- package/dist/client/_assets/chunks/615b38e0b5bb3ea68426f44f47706e6f-B5yzIIer.woff2 +0 -0
- package/dist/client/_assets/chunks/61a2c80d0c924d5a6187c02e8d1d1642-BY3MmHbZ.woff2 +0 -0
- package/dist/client/_assets/chunks/62fc94f8d4c5a750b7f25e7387539910-3ql2_WDK.woff2 +0 -0
- package/dist/client/_assets/chunks/639c7c6b0b0c27c738702741cfa4b8c0-BCD3OGri.woff2 +0 -0
- package/dist/client/_assets/chunks/63fafcb069520613d0ea35ad3e6b1e42-DP0Rrj4o.woff2 +0 -0
- package/dist/client/_assets/chunks/64a404d675f1d726f0891b7a80794595-DU3L0I9s.woff2 +0 -0
- package/dist/client/_assets/chunks/65a60d87c64228d258a123cbe85f5f31-B4kbf-79.woff2 +0 -0
- package/dist/client/_assets/chunks/65e9c4585d71bf48a5c62f367010da16-CvzgzReN.woff2 +0 -0
- package/dist/client/_assets/chunks/68a3a4bf337f8a0722be76676e20b850-Dnaw8EPD.woff2 +0 -0
- package/dist/client/_assets/chunks/6968e889e18891d912809fe484c2e745-Bj1m5e0s.woff2 +0 -0
- package/dist/client/_assets/chunks/6abdd163d2c4b85698db8aa1ce149361-B5FVGJuo.woff2 +0 -0
- package/dist/client/_assets/chunks/6b75213eb0be40ce84241eb2bb438a2e-CqHmF-NR.woff2 +0 -0
- package/dist/client/_assets/chunks/70714891ad3fbfc3d5f10a8669dacc5a-BWk5nLFN.woff2 +0 -0
- package/dist/client/_assets/chunks/7158022889c6c177062ac85036e7af10-B3D4K22n.woff2 +0 -0
- package/dist/client/_assets/chunks/73b794d885b88f7befb7ea8ab1780a15-D0ZehnEI.woff2 +0 -0
- package/dist/client/_assets/chunks/743f290baf027d4626a86e22f3d44600-DDTDQdRC.woff2 +0 -0
- package/dist/client/_assets/chunks/7520ca6ca8c60eb1e62d50e71c8d30f1-CVRaGu2G.woff2 +0 -0
- package/dist/client/_assets/chunks/757019c01c5e2c2b0f54293cea3b5636-BTovUnoH.woff2 +0 -0
- package/dist/client/_assets/chunks/759a647b77791b1e98f99bc0ab5317a7-CrK6FIFz.woff2 +0 -0
- package/dist/client/_assets/chunks/761d84afca8d7f34eebefe538adba827-DRbiVuwX.woff2 +0 -0
- package/dist/client/_assets/chunks/76e07530323418ec723c5d7634c9bcca-DGtVJTTH.woff2 +0 -0
- package/dist/client/_assets/chunks/76e51630143b95b6322ef93ad330da7a-D-kV82C_.woff2 +0 -0
- package/dist/client/_assets/chunks/76f117a3baacda304a71965a17266a13-DFfvsFUm.woff2 +0 -0
- package/dist/client/_assets/chunks/773819b7b9b8fd404a929867c0fd677e-Bu1xCMZb.woff2 +0 -0
- package/dist/client/_assets/chunks/77747f17625a259fb405b7bbdd84ac4b-o_vNfh-6.woff2 +0 -0
- package/dist/client/_assets/chunks/7817dd16805145d8538ad57590f69f5a-DguSUP3O.woff2 +0 -0
- package/dist/client/_assets/chunks/788498548ddfb710d6ef3c7bff79fa97-3ffoQfRd.woff2 +0 -0
- package/dist/client/_assets/chunks/78ea6c40923ce95367e3517dd1e5a849-DvLDRhX9.woff2 +0 -0
- package/dist/client/_assets/chunks/79aa7c8c842c4a27cf57c0a3a1ffca5a-sDoCeoKI.woff2 +0 -0
- package/dist/client/_assets/chunks/7b59f0ec7792b18458dc5a361e37884c-DWvxkKmT.woff2 +0 -0
- package/dist/client/_assets/chunks/7c3945788a689a69356c1a622d69d48b-1JjIpss2.woff2 +0 -0
- package/dist/client/_assets/chunks/7cc72fd2c9105560422b6a67c6162945-Co3AF5_G.woff2 +0 -0
- package/dist/client/_assets/chunks/7d0ea0690e432462f4d05a23d720ec19-Dx7DK4C3.woff2 +0 -0
- package/dist/client/_assets/chunks/7dffa5c1bec57e0903fd62357401ff1a-DuQHmiiN.woff2 +0 -0
- package/dist/client/_assets/chunks/7e38ca789a9e76ac91d9256374e154f0-BtETgGRe.woff2 +0 -0
- package/dist/client/_assets/chunks/7f9a6c9286b68de9c72b0024f7beeb40-JpXdo4FC.woff2 +0 -0
- package/dist/client/_assets/chunks/815a0b797465190f60d8b1a04656c42e-Spzo94nU.woff2 +0 -0
- package/dist/client/_assets/chunks/823d1bb11097331238d103c7f72138dc-CGN6m2VF.woff2 +0 -0
- package/dist/client/_assets/chunks/82f6ccc063960eed1cdfd1d61ada8862-CsmAqx2a.woff2 +0 -0
- package/dist/client/_assets/chunks/835b6505bb9eea9678925a1fa885353d-C6mSBwUc.woff2 +0 -0
- package/dist/client/_assets/chunks/845b4b67564d62cf5cad242f7ac08ea5-DnvzrloJ.woff2 +0 -0
- package/dist/client/_assets/chunks/84c8e7bc0931008ed91f44ed12dfea94-C2Hvhh-b.woff2 +0 -0
- package/dist/client/_assets/chunks/8726558cf297cbda831cc19514897205-gSFV1vW0.woff2 +0 -0
- package/dist/client/_assets/chunks/8855472f3c02f6c7ebb3216617ebe4af-C1JsWVqq.woff2 +0 -0
- package/dist/client/_assets/chunks/887a11290ba78b1e66c6d2f67043005e-L_A1f5Fz.woff2 +0 -0
- package/dist/client/_assets/chunks/888ba53510213d5d1f6a7deb60e569b6-OP8E-ELN.woff2 +0 -0
- package/dist/client/_assets/chunks/88db1d042074fb6e66821ffc10941930-kZ6K3e62.woff2 +0 -0
- package/dist/client/_assets/chunks/892aa49b2529c89bb4076d4aa51fe30f-Bw0_Km2A.woff2 +0 -0
- package/dist/client/_assets/chunks/8b5c9ef81159f31d2ab35f45ca4373e0-Djw95sKC.woff2 +0 -0
- package/dist/client/_assets/chunks/8b91c7c2ed390f1278b9befa3aa87233-B2C3iFbQ.woff2 +0 -0
- package/dist/client/_assets/chunks/8dc18c0cebe6aa4bf4c45dbb831048ab-bUlANLit.woff2 +0 -0
- package/dist/client/_assets/chunks/8e231d707f0c4f8e3cde90a6b52a79aa-CvwbWlul.woff2 +0 -0
- package/dist/client/_assets/chunks/8fb45117a62d92dce44a80f0b729ead5-Djr4-o5-.woff2 +0 -0
- package/dist/client/_assets/chunks/9064c12fd72c7aba235d8c3881755b91-BnguxT-p.woff2 +0 -0
- package/dist/client/_assets/chunks/90b7848e9b1623b77bdcf155e90b839c-CFvPRnoM.woff2 +0 -0
- package/dist/client/_assets/chunks/911b9e53e9814de2998c60bf3115f560-DmPmj635.woff2 +0 -0
- package/dist/client/_assets/chunks/919a879bd2d580d8491a31a449390689-BY2w-8_Y.woff2 +0 -0
- package/dist/client/_assets/chunks/91da6cb174bebfb96976e2f1316be2fe-DztAJb6W.woff2 +0 -0
- package/dist/client/_assets/chunks/92fdc376bce277874e75db666f175b44-DJUnHEIN.woff2 +0 -0
- package/dist/client/_assets/chunks/930a23430f0eb64480d7fe5f82834e21-2VrDPT7E.woff2 +0 -0
- package/dist/client/_assets/chunks/934dfdbed2bb2c4b6129199d699a34fa-CtpHsjRD.woff2 +0 -0
- package/dist/client/_assets/chunks/93bed0e5f2dd5a21cf73304fcfbc149d-DJiaETX7.woff2 +0 -0
- package/dist/client/_assets/chunks/93f3ea6918533d96d9c252378b9a4af0-D-FJaGi-.woff2 +0 -0
- package/dist/client/_assets/chunks/95a0951d2a2722ff773a1a45e8c474d6-DuZg-BpP.woff2 +0 -0
- package/dist/client/_assets/chunks/95ff41191c76ef893ac70a1043e95e44-C08QDbJO.woff2 +0 -0
- package/dist/client/_assets/chunks/96c75862c8cec51a7c05ff025fea86cd-DBnRwCuW.woff2 +0 -0
- package/dist/client/_assets/chunks/97d7a17234f2b5030ae9697ae00aded2-CkUSaB-p.woff2 +0 -0
- package/dist/client/_assets/chunks/982cd1765ca6bbb3e6547e47834d5148-DkIybx1r.woff2 +0 -0
- package/dist/client/_assets/chunks/985d4d41afa0934a4eb2de35473fb9a2-CF7rgJMw.woff2 +0 -0
- package/dist/client/_assets/chunks/995c4fda62d25c3b79dd5987737df6ae-CY5ri8HW.woff2 +0 -0
- package/dist/client/_assets/chunks/9b0c3caac458c7bc9c9133e40405ddea-DhPrYOcz.woff2 +0 -0
- package/dist/client/_assets/chunks/9b21e8244f5930c48ad5073f83777b6c-CuE9-r-r.woff2 +0 -0
- package/dist/client/_assets/chunks/9bb38fd05201de666b88b82d56a386f7-nMl6lB8X.woff2 +0 -0
- package/dist/client/_assets/chunks/9d0afa53dc2f92ba42024f55f11f6779-Bozpx8bL.woff2 +0 -0
- package/dist/client/_assets/chunks/9e13a47242926f1cd7d68c08d9ef8889-DwKQjc8M.woff2 +0 -0
- package/dist/client/_assets/chunks/9eff2ab2a9d2ced47ab761bf6fdb11ec-D86-Zm1H.woff2 +0 -0
- package/dist/client/_assets/chunks/9f1963fe8d4d6878d717d872a8f3fdb5-DxjITUFq.woff2 +0 -0
- package/dist/client/_assets/chunks/9f3bdab4025dc7c5ba1a5871e62e8918-BqxDEFLJ.woff2 +0 -0
- package/dist/client/_assets/chunks/9fce02d1a09f464c36c9fcfb14a354c5-F2tMAB_j.woff2 +0 -0
- package/dist/client/_assets/chunks/a00e94d4f04df6aa659db9c4954c7efe-CRBKCj_3.woff2 +0 -0
- package/dist/client/_assets/chunks/a0a6755c7e3f9e4cf03387027dd9f16c-BAyRqF2w.woff2 +0 -0
- package/dist/client/_assets/chunks/a0d00fe4816c95a8c7dffd445ef00b03-CxcTiRLJ.woff2 +0 -0
- package/dist/client/_assets/chunks/a0f199077fa1a33bc2a1e01c64de7fe6-IdgVJmZD.woff2 +0 -0
- package/dist/client/_assets/chunks/a2b0597837e382aca19919c4b74e58c8-7iVOBjzI.woff2 +0 -0
- package/dist/client/_assets/chunks/a373e85c96aa0dc303092429fb837e51-Dvmw8mbX.woff2 +0 -0
- package/dist/client/_assets/chunks/a379ada89abd750c587ded29f77e731c-CgPBsEsv.woff2 +0 -0
- package/dist/client/_assets/chunks/a4475c442c6b259354c9eda62e64e7df-TZ0c8vK-.woff2 +0 -0
- package/dist/client/_assets/chunks/a48be56ce5d3a99dc8f8331398004412-BR_ixH1W.woff2 +0 -0
- package/dist/client/_assets/chunks/a4a39b5f048671aa37c2b2a9581ab08a-EDfDYsUd.woff2 +0 -0
- package/dist/client/_assets/chunks/a534449d67561ac9b06489f64e57ad98-C355TCrD.woff2 +0 -0
- package/dist/client/_assets/chunks/a6e044424780a57610833cc856c15bf5-x1b8FE4H.woff2 +0 -0
- package/dist/client/_assets/chunks/a7c35e42a659347e490c3cb7983b2e7f-BWpA0-oz.woff2 +0 -0
- package/dist/client/_assets/chunks/a7c58070d68dc1724e9d4e781afb78db-ql27pT-6.woff2 +0 -0
- package/dist/client/_assets/chunks/a806759e72987951d6d08b7cbdf36a1b-TvLDwB_u.woff2 +0 -0
- package/dist/client/_assets/chunks/a88481ec8bc7be11cb66e46781a79bb9-Ck-r_sGx.woff2 +0 -0
- package/dist/client/_assets/chunks/a919a8c6fec6d7e8bb21177965f2e9d7-CC5uWzwd.woff2 +0 -0
- package/dist/client/_assets/chunks/a9a016d4a93409f65278327e8a8bb38d-dylrNwF8.woff2 +0 -0
- package/dist/client/_assets/chunks/aa662092cf249707123ca4d575e2764b-CSxNsTUL.woff2 +0 -0
- package/dist/client/_assets/chunks/aae1eda193285ab817a2d1b408440326-CjyjBoRm.woff2 +0 -0
- package/dist/client/_assets/chunks/ab92d41062a7644faa45f50bf384454a-Cq0X8a9H.woff2 +0 -0
- package/dist/client/_assets/chunks/accc43762ad05edfbff66ba1380d192a-B5jIykaZ.woff2 +0 -0
- package/dist/client/_assets/chunks/ad47bcd6d4663026d648c132d969318d-BYdIbDge.woff2 +0 -0
- package/dist/client/_assets/chunks/ad67113f88b59582991cb0c5d33ea19f-DOH1vcIZ.woff2 +0 -0
- package/dist/client/_assets/chunks/adb6e613fb99c6dd660b727101f554a8-DJDw_ZZK.woff2 +0 -0
- package/dist/client/_assets/chunks/adc4a98a89870ef984ee8f4b57c8a3a5-D30vw26R.woff2 +0 -0
- package/dist/client/_assets/chunks/af57a2e8c72f9ba0efb1af771d32c124-D5Zwq9pS.woff2 +0 -0
- package/dist/client/_assets/chunks/b0ae5f374e43dac992a4a5d334cebc0b-Ccoi4dPt.woff2 +0 -0
- package/dist/client/_assets/chunks/b0cb664cb2e1371efda8943c0b7dcd1c-YzGFnelF.woff2 +0 -0
- package/dist/client/_assets/chunks/b0d1cdced482352cf0d3ae58638aacb9-IunWfc6R.woff2 +0 -0
- package/dist/client/_assets/chunks/b12379ab782468d725519cd07a7d15bc-IjbydKs2.woff2 +0 -0
- package/dist/client/_assets/chunks/b320f5d185b2cff933ac549c184031c5-IYnPrIz4.woff2 +0 -0
- package/dist/client/_assets/chunks/b326a8a9c33b554db570da94f60bc380-B6dzaCQg.woff2 +0 -0
- package/dist/client/_assets/chunks/b55486f8a459c838fe329d4e79a8c211-DW8q7oGV.woff2 +0 -0
- package/dist/client/_assets/chunks/b6117ff2993b11bb1fdc7ea3588a010c-JoMYL5W_.woff2 +0 -0
- package/dist/client/_assets/chunks/b61982951bd51b724143c30dfaaa9fe9-D3KhBKl6.woff2 +0 -0
- package/dist/client/_assets/chunks/b91234c10fd8b8c8abc88e03afe66a1f-BABNh2la.woff2 +0 -0
- package/dist/client/_assets/chunks/b9317198f118a1dfd8ddf2b82ec028f3-CiMecxeH.woff2 +0 -0
- package/dist/client/_assets/chunks/ba825ae79b1a6f7e0cce5215fcb5c96f-B6EWU_D5.woff2 +0 -0
- package/dist/client/_assets/chunks/ba9c59d7dfa4494db1bb764ada81467d-D0yPcC1m.woff2 +0 -0
- package/dist/client/_assets/chunks/bab547459d514f46206e340c4bb2dc88-YnBqlGhw.woff2 +0 -0
- package/dist/client/_assets/chunks/bb0b15492b8cdbbec57c0bcfa0aa9241-BjIFbEsN.woff2 +0 -0
- package/dist/client/_assets/chunks/bc6183ac08f0fac78c46f80c10cf7c92-B81nbRb3.woff2 +0 -0
- package/dist/client/_assets/chunks/bc8ccea5abf6598cf3cfa97eb59804bb-B_7IbluG.woff2 +0 -0
- package/dist/client/_assets/chunks/bc95a792cbca6639214c9b0da13392ff-CHQe0E-D.woff2 +0 -0
- package/dist/client/_assets/chunks/bcc39eda837bb7a7a3d37c8c60fffb81-CGd1Zk6S.woff2 +0 -0
- package/dist/client/_assets/chunks/bcfa62f35731856246c146d3a6932bf3-DMDq6w4v.woff2 +0 -0
- package/dist/client/_assets/chunks/bd73264d7f98776708d5d6f3c9b78fcc-d9QWWMym.woff2 +0 -0
- package/dist/client/_assets/chunks/bed610b217d500f5975cfc9fe6157570-B5t7s1bw.woff2 +0 -0
- package/dist/client/_assets/chunks/bedc74b423b7293b6ad0bdecc61c42cc-4Nnh7peP.woff2 +0 -0
- package/dist/client/_assets/chunks/bf8901f8f11d4f433ea17dabc9370ea6-B8dkfvGn.woff2 +0 -0
- package/dist/client/_assets/chunks/bfb00d4a4c48661bd0be99f300a0faae-OxsWZ7l-.woff2 +0 -0
- package/dist/client/_assets/chunks/c0098958e20db68cab90097b5e62516f-VpjQo3vV.woff2 +0 -0
- package/dist/client/_assets/chunks/c063897793f593eb26d6ff7b7baaba18-BWeJJ1sH.woff2 +0 -0
- package/dist/client/_assets/chunks/c1b0df29ae41d764904df84e9ac83d1e-D1N934vs.woff2 +0 -0
- package/dist/client/_assets/chunks/c4143bb9f2fe77b6ccf20088a8904650-nHfYM24e.woff2 +0 -0
- package/dist/client/_assets/chunks/c42c67070cfe99cf823df92d81c7fa6e-B_a405Ez.woff2 +0 -0
- package/dist/client/_assets/chunks/c4bfaa5e50798246e3770718b7a7c84a-B-r6Y_o0.woff2 +0 -0
- package/dist/client/_assets/chunks/c4d749e45ecd5a5aed5a0bb3ebfd355d-CkqE1fRi.woff2 +0 -0
- package/dist/client/_assets/chunks/c6c2971ad1f3221f6cf84028aa0f477e-D7BZGIcY.woff2 +0 -0
- package/dist/client/_assets/chunks/c7ff3f6bbdcd5f604b7343602ab904df-Bq7UjVrZ.woff2 +0 -0
- package/dist/client/_assets/chunks/c84598999133455503042e06f4ab79cb-DIXndJX3.woff2 +0 -0
- package/dist/client/_assets/chunks/c945c62368357d05a53206620460fb30-82gFEf6n.woff2 +0 -0
- package/dist/client/_assets/chunks/c96d83978add28b356c22c4c84916733-BL4VaPIE.woff2 +0 -0
- package/dist/client/_assets/chunks/c97f41eef722121d86f55d553c056a39-CjejgQO2.woff2 +0 -0
- package/dist/client/_assets/chunks/ca62704509932d3232d62918de97af3f-CKWF-3mX.woff2 +0 -0
- package/dist/client/_assets/chunks/ca9a533988d7019597a60d4e17127e0c-ClZ6ygZM.woff2 +0 -0
- package/dist/client/_assets/chunks/cb7ccd6494256f7a2977b7c2b0225592-Cxp8XObm.woff2 +0 -0
- package/dist/client/_assets/chunks/ce022e18a1377ac509443c3c3790b431-DpH0t6Tn.woff2 +0 -0
- package/dist/client/_assets/chunks/ce41d70ce6a069a498525c9e15c45cf2-DGtgv-_b.woff2 +0 -0
- package/dist/client/_assets/chunks/cf2b28f90f47276f7e2688a65e88a101-LVbNkXHj.woff2 +0 -0
- package/dist/client/_assets/chunks/cf9cffe56636322f62b40d61130fbc5e-DNCBMqAo.woff2 +0 -0
- package/dist/client/_assets/chunks/d1f064825fa5784b5c930652bd831cce-BU6qwjp8.woff2 +0 -0
- package/dist/client/_assets/chunks/d20d8944bc0b85f5b2aae4b24f343516-oY9l-NmC.woff2 +0 -0
- package/dist/client/_assets/chunks/d2faabcedd19f016e7b21bce073c0ec8-Dv6eFCf5.woff2 +0 -0
- package/dist/client/_assets/chunks/d320171e57480510f87dbbc7d5264b0a-CcCfjaFX.woff2 +0 -0
- package/dist/client/_assets/chunks/d36644d6502527e1fff205d0c7eca434-kVBj8zy_.woff2 +0 -0
- package/dist/client/_assets/chunks/d3a72a99d365dddfbca8d017a8011368-Bgdmhl3M.woff2 +0 -0
- package/dist/client/_assets/chunks/d40ab99c7a38026f411c8f112f742b48-D7Nm7fg6.woff2 +0 -0
- package/dist/client/_assets/chunks/d42aafa0f246ad3b4c16fe96d3a3a432-CRxTqZXU.woff2 +0 -0
- package/dist/client/_assets/chunks/d4aaf23c13ae808a4bed617afd13aa07-d_T1SKe9.woff2 +0 -0
- package/dist/client/_assets/chunks/d509ee5c8241bc7c3de2039d75564fa5-BpNFD9JE.woff2 +0 -0
- package/dist/client/_assets/chunks/d675d717cd329bfd4c0524f76ae1579c-Bd5_ctoQ.woff2 +0 -0
- package/dist/client/_assets/chunks/d689b1861d7e4377dd72ad3013482612-jPsVOU3F.woff2 +0 -0
- package/dist/client/_assets/chunks/d6bb686bddfbe8f38a36a68e609a8667-C_QduS-X.woff2 +0 -0
- package/dist/client/_assets/chunks/d9020ff69a83b2a6ee1f42ae480f7db0-DLBUPMCe.woff2 +0 -0
- package/dist/client/_assets/chunks/d9047070d72a816b3dba9d40c2d85e69-LTQ42vEp.woff2 +0 -0
- package/dist/client/_assets/chunks/da04549f3f4ed28076b01b8cd710d313-CDXOeduw.woff2 +0 -0
- package/dist/client/_assets/chunks/da7af303f8c645f9a9dbae0e6e32dd35-DCkS2jqi.woff2 +0 -0
- package/dist/client/_assets/chunks/da90a9012ab2d98f759e3fa0820ef502-D0D9d1zg.woff2 +0 -0
- package/dist/client/_assets/chunks/dc6e234ded795e91f76d6647f628fbf0-IkooXcRh.woff2 +0 -0
- package/dist/client/_assets/chunks/dc8f6256445e68199540be9ade33529f-umb5tWbZ.woff2 +0 -0
- package/dist/client/_assets/chunks/dd0cfdfdac0866e66d587e2b5a9e9961-bl4ozE0c.woff2 +0 -0
- package/dist/client/_assets/chunks/de304c8a02e45ded4f8dcc479d167198-ClRu89-4.woff2 +0 -0
- package/dist/client/_assets/chunks/df10d94bea357a43313e20da5d84bd30-CmAgh2oA.woff2 +0 -0
- package/dist/client/_assets/chunks/df9568257eb29b156449fdd4bec5ec76-BMz6aLRr.woff2 +0 -0
- package/dist/client/_assets/chunks/e067cd0ed76c90cd0a93c9339253f20b-6RxCQ4jy.woff2 +0 -0
- package/dist/client/_assets/chunks/e08b07772e7bed3cec2832d43f7fd339-CQCkeKsr.woff2 +0 -0
- package/dist/client/_assets/chunks/e12150d5a39b30be8f567968c7a527b0-DjdoWzeQ.woff2 +0 -0
- package/dist/client/_assets/chunks/e34b2b141e472dc776c86fdf8eea23b0-Cjk7H-Ev.woff2 +0 -0
- package/dist/client/_assets/chunks/e444fd88f1390636e603d0d681538ac8-Bx7CPOZh.woff2 +0 -0
- package/dist/client/_assets/chunks/e53fcb2381eee345db4f6f973dd95a3e-DmkkCUWz.woff2 +0 -0
- package/dist/client/_assets/chunks/e5bd313ef81f687d398aacb11cec3069-CwBEbcji.woff2 +0 -0
- package/dist/client/_assets/chunks/e79898628283edc27180fc39d0d769c1-By2pplQz.woff2 +0 -0
- package/dist/client/_assets/chunks/e8c15be62faf978d208925e79ea6a10d-Ct5M9lTP.woff2 +0 -0
- package/dist/client/_assets/chunks/e8c364c16daa04835bf32d293d2598db-3v4Tut_d.woff2 +0 -0
- package/dist/client/_assets/chunks/e91c4d941ed9f5c2f3e27f205a3a225e-BwOJ2Lst.woff2 +0 -0
- package/dist/client/_assets/chunks/e9c566be7a5d38a9085225f7372bc82b-BCtnCVML.woff2 +0 -0
- package/dist/client/_assets/chunks/e9ebc567a711eeb29019ddae3e0ce7fe-CnNhV49O.woff2 +0 -0
- package/dist/client/_assets/chunks/eaf332445a40942928e5f5750c1c3116-qZ5LSRrj.woff2 +0 -0
- package/dist/client/_assets/chunks/eb5afb3d952b8593782caec6026514b6-BXugZF2T.woff2 +0 -0
- package/dist/client/_assets/chunks/ec86e23683052da5cfdc3b77641bd15a-BeLcbcRc.woff2 +0 -0
- package/dist/client/_assets/chunks/ecb8875a56c7b038b35432fda41ae128-BVqgNf1o.woff2 +0 -0
- package/dist/client/_assets/chunks/eda1b0cb6d1719dd9bedcf3216a9e8de-BJTfLL5z.woff2 +0 -0
- package/dist/client/_assets/chunks/edd6a4f608d04fc0351d7688cfc321e4-d3V4U40c.woff2 +0 -0
- package/dist/client/_assets/chunks/ee6fce20b420a480714607c66d7f97e5-DEtC-G5G.woff2 +0 -0
- package/dist/client/_assets/chunks/eee3836d6ac17ebb2c450bbcbc9db121-C5a2TZjQ.woff2 +0 -0
- package/dist/client/_assets/chunks/et-book-bold-line-figures-BFJr2_zv.woff +0 -0
- package/dist/client/_assets/chunks/et-book-display-italic-old-style-figures-CoeRJAe8.woff +0 -0
- package/dist/client/_assets/chunks/et-book-roman-line-figures-CaA40oOf.woff +0 -0
- package/dist/client/_assets/chunks/f1661731474b78bdf81114daf10b254f-KnMKX_U3.woff2 +0 -0
- package/dist/client/_assets/chunks/f2c58dee206ba9355046fc23d05491f7-Cm4tCWav.woff2 +0 -0
- package/dist/client/_assets/chunks/f50ac27ea4358d67fdda403c2bb52467-WCfYHde1.woff2 +0 -0
- package/dist/client/_assets/chunks/f55334112f8e8be82d65db29887a663f-vDq1zH8k.woff2 +0 -0
- package/dist/client/_assets/chunks/f5738255e92d8dd34a46d1bcdf4c4074-tpghTzL1.woff2 +0 -0
- package/dist/client/_assets/chunks/f740e93f3c0277ecc616594103bca683-C4BgW_ko.woff2 +0 -0
- package/dist/client/_assets/chunks/f7633b5af033d76ff2fb3c3c266d77c5-BCkrZxwO.woff2 +0 -0
- package/dist/client/_assets/chunks/f7d8468cba2335a83ee414ea68291bab-CVnotkZU.woff2 +0 -0
- package/dist/client/_assets/chunks/f91cd722855f4269256eae1187df64ec-DJlAx1A_.woff2 +0 -0
- package/dist/client/_assets/chunks/f9695c6c4df2bf6bc03045ff79d4f01f-DDaYGYHT.woff2 +0 -0
- package/dist/client/_assets/chunks/fa6e58ce4b52695e7ae19bbea6336ec8-B30jEpze.woff2 +0 -0
- package/dist/client/_assets/chunks/fada6eaa68ff8816afe43d2a36c5423e-DlGAWNPb.woff2 +0 -0
- package/dist/client/_assets/chunks/fbcc4bf5367218951172bdee6f77d7a6-Dax2VSCd.woff2 +0 -0
- package/dist/client/_assets/chunks/fc895f5ce66b656f4a933097bf2a8775-DjjysNdf.woff2 +0 -0
- package/dist/client/_assets/chunks/fc9ae5b600fb711f2d67e93ce768cba4-D1lx7LBz.woff2 +0 -0
- package/dist/client/_assets/chunks/fca6720fd14c467d29a90f18ef3859b9-B3btrwq3.woff2 +0 -0
- package/dist/client/_assets/chunks/fd1bb507bcbf04856eeb7f7fd47ea579-NR1nGJTt.woff2 +0 -0
- package/dist/client/_assets/chunks/fd1bceb55d3e0183ac2454b8532fec7d-D3t-v6nn.woff2 +0 -0
- package/dist/client/_assets/chunks/fd2069e6e8588c70b4f11364093b81f2-BoXhYVgw.woff2 +0 -0
- package/dist/client/_assets/chunks/{url-CG0eolsk.js → url-pLre2DM_.js} +1 -1
- package/dist/client/_assets/client-C_kImWZj.css +2 -0
- package/dist/client/_assets/client-D95FNDg5.js +272 -0
- package/dist/client/_assets/client-auth-BRFl5zQA.js +4547 -0
- package/dist/client/_assets/client-cjk-jp-DZwrTzQC.css +1 -0
- package/dist/client/_assets/client-cjk-kr-_3ZNI2ZP.css +1 -0
- package/dist/env-wCpMcNXs.js +252 -0
- package/dist/github-api-CficQztC.js +176 -0
- package/dist/github-app-F4qZ05xk.js +275 -0
- package/dist/github-sync-zohnA9qv.js +4696 -0
- package/dist/index.js +88 -2
- package/dist/node.js +71 -30
- package/dist/url-FvvgARU9.js +305 -0
- package/package.json +16 -12
- package/src/__tests__/bin/content-cli.test.ts +179 -0
- package/src/__tests__/bin/media-cli.test.ts +192 -0
- package/src/__tests__/export-hugo-build.test.ts +178 -0
- package/src/__tests__/export-import-roundtrip.test.ts +265 -0
- package/src/__tests__/export-service.test.ts +763 -510
- package/src/__tests__/helpers/export-fixtures.ts +108 -0
- package/src/__tests__/import-site-command.test.ts +358 -350
- package/src/__tests__/site-pull-media.test.ts +256 -0
- package/src/app.tsx +60 -13
- package/src/client/__tests__/collection-form-bridge.test.ts +87 -7
- package/src/client/__tests__/collection-page-actions.test.ts +4 -4
- package/src/client/__tests__/collection-picker-order.test.ts +53 -0
- package/src/client/__tests__/collection-sort-menu.test.ts +2 -2
- package/src/client/__tests__/compose-bridge.test.ts +305 -13
- package/src/client/__tests__/compose-launch.test.ts +120 -0
- package/src/client/__tests__/compose-shortcuts.test.ts +141 -0
- package/src/client/__tests__/feed-video-player.test.ts +44 -0
- package/src/client/__tests__/site-header-nav.test.ts +250 -0
- package/src/client/__tests__/sortable-list.test.ts +6 -2
- package/src/client/__tests__/thread-context.test.ts +117 -0
- package/src/client/collection-form-bridge.ts +109 -4
- package/src/client/collection-page-actions.ts +3 -1
- package/src/client/collection-picker-order.ts +104 -0
- package/src/client/components/__tests__/{jant-collection-sidebar.test.ts → jant-collection-directory.test.ts} +91 -10
- package/src/client/components/__tests__/jant-collection-form.test.ts +40 -18
- package/src/client/components/__tests__/jant-compose-dialog.test.ts +1285 -66
- package/src/client/components/__tests__/jant-compose-editor.test.ts +254 -19
- package/src/client/components/__tests__/jant-compose-fullscreen.test.ts +127 -1
- package/src/client/components/__tests__/jant-media-lightbox.test.ts +237 -0
- package/src/client/components/__tests__/jant-nav-manager.test.ts +326 -0
- package/src/client/components/__tests__/jant-post-menu.test.ts +372 -2
- package/src/client/components/__tests__/jant-settings-avatar.test.ts +61 -5
- package/src/client/components/__tests__/jant-settings-general.test.ts +29 -28
- package/src/client/components/__tests__/jant-text-preview.test.ts +206 -0
- package/src/client/components/collection-manager-types.ts +18 -1
- package/src/client/components/compose-types.ts +48 -0
- package/src/client/components/jant-collection-directory.ts +1448 -0
- package/src/client/components/jant-collection-form.ts +65 -17
- package/src/client/components/jant-command-palette.ts +544 -0
- package/src/client/components/jant-compose-dialog.ts +2490 -375
- package/src/client/components/jant-compose-editor.ts +420 -75
- package/src/client/components/jant-compose-fullscreen.ts +59 -8
- package/src/client/components/jant-media-lightbox.ts +373 -18
- package/src/client/components/jant-nav-manager.ts +624 -286
- package/src/client/components/jant-post-menu.ts +451 -126
- package/src/client/components/jant-repo-picker-types.ts +39 -0
- package/src/client/components/jant-repo-picker.ts +799 -0
- package/src/client/components/jant-settings-avatar.ts +16 -2
- package/src/client/components/jant-settings-general.ts +137 -35
- package/src/client/components/jant-text-preview.ts +239 -55
- package/src/client/components/nav-manager-types.ts +25 -9
- package/src/client/components/settings-types.ts +13 -2
- package/src/client/compose-bridge.ts +428 -68
- package/src/client/compose-launch.ts +41 -4
- package/src/client/compose-shortcuts.ts +77 -5
- package/src/client/feed-video-player.ts +374 -0
- package/src/client/image-processor.ts +53 -127
- package/src/client/media-lightbox-events.ts +1 -0
- package/src/client/media-scroll-hint.ts +66 -0
- package/src/client/multipart-upload.ts +4 -1
- package/src/client/palette-shortcuts.ts +35 -0
- package/src/client/search-rank.ts +74 -0
- package/src/client/settings-bridge.ts +7 -0
- package/src/client/site-header-nav.d.ts +1 -0
- package/src/client/site-header-nav.js +139 -51
- package/src/client/sortable-list.ts +2 -0
- package/src/client/thread-context.ts +54 -11
- package/src/client/tiptap/__tests__/block-insertion.test.ts +65 -0
- package/src/client/tiptap/__tests__/footnotes.test.ts +692 -0
- package/src/client/tiptap/__tests__/link-toolbar.test.ts +120 -4
- package/src/client/tiptap/__tests__/markdown-clipboard.test.ts +48 -0
- package/src/client/tiptap/block-insertion.ts +36 -0
- package/src/client/tiptap/bubble-menu.ts +34 -4
- package/src/client/tiptap/create-editor.ts +49 -30
- package/src/client/tiptap/embed-dialog.ts +301 -0
- package/src/client/tiptap/embed-node.ts +373 -0
- package/src/client/tiptap/embed-paste.ts +91 -0
- package/src/client/tiptap/extensions.ts +77 -28
- package/src/client/tiptap/footnotes.ts +718 -0
- package/src/client/tiptap/html-block-node.ts +222 -0
- package/src/client/tiptap/image-node.ts +49 -6
- package/src/client/tiptap/inline-image-upload.ts +200 -40
- package/src/client/tiptap/link-input-rules.ts +50 -16
- package/src/client/tiptap/link-toolbar.ts +156 -133
- package/src/client/tiptap/markdown-clipboard.ts +120 -0
- package/src/client/tiptap/more-break.ts +36 -3
- package/src/client/tiptap/paste-media.ts +6 -0
- package/src/client/tiptap/slash-commands.ts +167 -14
- package/src/client/tiptap/tab-indent.ts +184 -0
- package/src/client/tiptap/toolbar-mode.ts +15 -2
- package/src/client/tiptap/wrapping-input-rules.ts +102 -0
- package/src/client/types/sortablejs.d.ts +9 -1
- package/src/client/upload-session.ts +2 -1
- package/src/client/video-processor.ts +177 -20
- package/src/client-auth.ts +9 -1
- package/src/client-site.ts +15 -0
- package/src/client.ts +2 -0
- package/src/db/__tests__/demo-canonical-snapshot.test.ts +1 -1
- package/src/db/__tests__/migration-rehearsal.test.ts +5 -1
- package/src/db/__tests__/migrations.test.ts +25 -2
- package/src/db/backfills/0001_strip_collection_path_c_prefix.sql +11 -0
- package/src/db/backfills/0002_strip_collection_path_c_prefix_fix.sql +12 -0
- package/src/db/backfills/0003_clear_system_nav_default_labels.sql +15 -0
- package/src/db/dialect.ts +40 -0
- package/src/db/migrations/0005_busy_the_phantom.sql +39 -0
- package/src/db/migrations/0006_adorable_magdalene.sql +3 -0
- package/src/db/migrations/0007_unusual_warstar.sql +1 -0
- package/src/db/migrations/0008_nasty_lockheed.sql +1 -0
- package/src/db/migrations/0009_clear_fixer.sql +31 -0
- package/src/db/migrations/0010_futuristic_preak.sql +31 -0
- package/src/db/migrations/0011_bizarre_smasher.sql +30 -0
- package/src/db/migrations/0012_furry_thena.sql +52 -0
- package/src/db/migrations/0013_mixed_lightspeed.sql +39 -0
- package/src/db/migrations/0014_high_the_santerians.sql +41 -0
- package/src/db/migrations/0015_skinny_shinobi_shaw.sql +1 -0
- package/src/db/migrations/0016_remarkable_nicolaos.sql +17 -0
- package/src/db/migrations/0017_powerful_moonstone.sql +14 -0
- package/src/db/migrations/meta/0005_snapshot.json +1904 -0
- package/src/db/migrations/meta/0006_snapshot.json +1925 -0
- package/src/db/migrations/meta/0007_snapshot.json +1933 -0
- package/src/db/migrations/meta/0008_snapshot.json +1940 -0
- package/src/db/migrations/meta/0009_snapshot.json +1952 -0
- package/src/db/migrations/meta/0010_snapshot.json +1952 -0
- package/src/db/migrations/meta/0011_snapshot.json +1948 -0
- package/src/db/migrations/meta/0012_snapshot.json +1955 -0
- package/src/db/migrations/meta/0013_snapshot.json +1977 -0
- package/src/db/migrations/meta/0014_snapshot.json +1988 -0
- package/src/db/migrations/meta/0015_snapshot.json +1995 -0
- package/src/db/migrations/meta/0016_snapshot.json +2104 -0
- package/src/db/migrations/meta/0017_snapshot.json +2188 -0
- package/src/db/migrations/meta/_journal.json +92 -1
- package/src/db/migrations/pg/0003_motionless_norrin_radd.sql +21 -0
- package/src/db/migrations/pg/0004_nervous_captain_midlands.sql +3 -0
- package/src/db/migrations/pg/0005_romantic_mesmero.sql +1 -0
- package/src/db/migrations/pg/0006_perpetual_bruce_banner.sql +1 -0
- package/src/db/migrations/pg/0007_nav_item_placement.sql +1 -0
- package/src/db/migrations/pg/0008_yielding_frightful_four.sql +3 -0
- package/src/db/migrations/pg/0009_outstanding_ogun.sql +1 -0
- package/src/db/migrations/pg/0010_overjoyed_gertrude_yorkes.sql +28 -0
- package/src/db/migrations/pg/0011_fixed_hulk.sql +19 -0
- package/src/db/migrations/pg/0012_cute_shockwave.sql +2 -0
- package/src/db/migrations/pg/0013_bizarre_obadiah_stane.sql +1 -0
- package/src/db/migrations/pg/0014_tearful_grim_reaper.sql +16 -0
- package/src/db/migrations/pg/0015_daffy_mikhail_rasputin.sql +14 -0
- package/src/db/migrations/pg/meta/0003_snapshot.json +2482 -0
- package/src/db/migrations/pg/meta/0004_snapshot.json +2500 -0
- package/src/db/migrations/pg/meta/0005_snapshot.json +2507 -0
- package/src/db/migrations/pg/meta/0006_snapshot.json +2513 -0
- package/src/db/migrations/pg/meta/0008_snapshot.json +2524 -0
- package/src/db/migrations/pg/meta/0009_snapshot.json +2520 -0
- package/src/db/migrations/pg/meta/0010_snapshot.json +2526 -0
- package/src/db/migrations/pg/meta/0011_snapshot.json +2563 -0
- package/src/db/migrations/pg/meta/0012_snapshot.json +2573 -0
- package/src/db/migrations/pg/meta/0013_snapshot.json +2579 -0
- package/src/db/migrations/pg/meta/0014_snapshot.json +2702 -0
- package/src/db/migrations/pg/meta/0015_snapshot.json +2803 -0
- package/src/db/migrations/pg/meta/_journal.json +92 -1
- package/src/db/pg/__tests__/node.test.ts +38 -0
- package/src/db/pg/node.ts +149 -0
- package/src/db/pg/schema.ts +130 -12
- package/src/db/rehearsal-fixtures/demo-current.sql +47 -3
- package/src/db/schema.ts +139 -12
- package/src/i18n/Trans.tsx +3 -3
- package/src/i18n/__tests__/context.test.tsx +9 -6
- package/src/i18n/__tests__/detect.test.ts +65 -48
- package/src/i18n/__tests__/fallback.test.ts +40 -0
- package/src/i18n/__tests__/message-placeholders.test.ts +43 -0
- package/src/i18n/__tests__/middleware.test.ts +83 -0
- package/src/i18n/context.tsx +9 -44
- package/src/i18n/detect.ts +95 -28
- package/src/i18n/i18n.ts +41 -17
- package/src/i18n/index.ts +7 -5
- package/src/i18n/locales/{en.po → public/en.po} +284 -1151
- package/src/i18n/locales/public/en.ts +1 -0
- package/src/i18n/locales/public/zh-Hans.po +2327 -0
- package/src/i18n/locales/public/zh-Hans.ts +1 -0
- package/src/i18n/locales/public/zh-Hant.po +2327 -0
- package/src/i18n/locales/public/zh-Hant.ts +1 -0
- package/src/i18n/locales/settings/en.po +1622 -0
- package/src/i18n/locales/settings/en.ts +1 -0
- package/src/i18n/locales/settings/zh-Hans.po +1622 -0
- package/src/i18n/locales/settings/zh-Hans.ts +1 -0
- package/src/i18n/locales/settings/zh-Hant.po +1622 -0
- package/src/i18n/locales/settings/zh-Hant.ts +1 -0
- package/src/i18n/middleware.ts +37 -24
- package/src/index.ts +4 -5
- package/src/lib/__tests__/collection-groups.test.ts +17 -0
- package/src/lib/__tests__/constants.test.ts +2 -24
- package/src/lib/__tests__/csp-builder.test.ts +68 -0
- package/src/lib/__tests__/display-text.test.ts +27 -0
- package/src/lib/__tests__/embed-providers.test.ts +104 -0
- package/src/lib/__tests__/embed-render.test.ts +60 -0
- package/src/lib/__tests__/feed.test.ts +125 -63
- package/src/lib/__tests__/hosted-signin.test.ts +11 -2
- package/src/lib/__tests__/markdown-roundtrip-embed.test.ts +88 -0
- package/src/lib/__tests__/markdown-to-tiptap.test.ts +59 -0
- package/src/lib/__tests__/markdown.test.ts +47 -5
- package/src/lib/__tests__/navigation.test.ts +148 -0
- package/src/lib/__tests__/post-display.test.ts +3 -2
- package/src/lib/__tests__/post-meta.test.ts +3 -0
- package/src/lib/__tests__/resolve-config.test.ts +30 -25
- package/src/lib/__tests__/schemas.test.ts +20 -3
- package/src/lib/__tests__/summary.test.ts +400 -1
- package/src/lib/__tests__/theme.test.ts +41 -2
- package/src/lib/__tests__/timeline.test.ts +33 -27
- package/src/lib/__tests__/tiptap-render.test.ts +224 -0
- package/src/lib/__tests__/tiptap-to-markdown.test.ts +51 -0
- package/src/lib/__tests__/video-playback.test.ts +48 -0
- package/src/lib/__tests__/view.test.ts +115 -8
- package/src/lib/__tests__/worker-response-cache.test.ts +311 -0
- package/src/lib/__tests__/youtube.test.ts +99 -0
- package/src/lib/api-media.ts +55 -0
- package/src/lib/api-posts.ts +99 -0
- package/src/lib/api-search.ts +47 -0
- package/src/lib/api-settings.ts +122 -0
- package/src/lib/asset-path.ts +20 -2
- package/src/lib/collection-groups.ts +2 -1
- package/src/lib/collection-paths.ts +40 -0
- package/src/lib/constants.ts +3 -24
- package/src/lib/csp-builder.ts +99 -0
- package/src/lib/decorative-quote-mark.ts +11 -0
- package/src/lib/display-text.ts +56 -0
- package/src/lib/embed-providers.ts +289 -0
- package/src/lib/embed-render.ts +151 -0
- package/src/lib/env.ts +70 -1
- package/src/lib/excerpt.ts +11 -2
- package/src/lib/feed.ts +89 -66
- package/src/lib/footnotes.ts +146 -0
- package/src/lib/github-api.ts +423 -0
- package/src/lib/github-app-state.ts +135 -0
- package/src/lib/github-app.ts +487 -0
- package/src/lib/github-sync-queue-handler.ts +69 -0
- package/src/lib/github-sync-site-config.ts +57 -0
- package/src/lib/github-sync-status.tsx +87 -0
- package/src/lib/github-sync-trigger.ts +199 -0
- package/src/lib/github-sync-worker.ts +72 -0
- package/src/lib/hosted-signin.ts +1 -1
- package/src/lib/hugo-markdown.ts +255 -0
- package/src/lib/job-queue-cf.ts +18 -0
- package/src/lib/job-queue-db.ts +149 -0
- package/src/lib/job-queue.ts +35 -0
- package/src/lib/markdown-manager.ts +864 -0
- package/src/lib/markdown-to-tiptap.ts +4 -323
- package/src/lib/markdown.ts +16 -24
- package/src/lib/media-helpers.ts +1 -0
- package/src/lib/navigation.ts +40 -8
- package/src/lib/post-display.ts +10 -22
- package/src/lib/public-storage.ts +6 -1
- package/src/lib/render.tsx +5 -2
- package/src/lib/resolve-config.ts +23 -11
- package/src/lib/rich-image.ts +89 -0
- package/src/lib/schemas.ts +180 -13
- package/src/lib/summary.ts +125 -18
- package/src/lib/theme.ts +119 -0
- package/src/lib/timeline.ts +63 -42
- package/src/lib/tiptap-render.ts +265 -126
- package/src/lib/tiptap-to-markdown.ts +6 -329
- package/src/lib/upload.ts +97 -6
- package/src/lib/version.ts +18 -0
- package/src/lib/video-playback.ts +73 -0
- package/src/lib/view.ts +130 -90
- package/src/lib/webhook-signature.ts +65 -0
- package/src/lib/worker-response-cache.ts +220 -0
- package/src/lib/youtube.ts +119 -0
- package/src/middleware/__tests__/onboarding.test.ts +3 -3
- package/src/middleware/__tests__/secure-headers.test.ts +28 -1
- package/src/middleware/config.ts +7 -10
- package/src/middleware/cors.ts +49 -0
- package/src/middleware/secure-headers.ts +45 -55
- package/src/node/__tests__/cli-migrate.test.ts +111 -0
- package/src/node/__tests__/cli-runtime-target.test.ts +1 -1
- package/src/node/__tests__/cli-site-token-env.test.ts +2 -2
- package/src/preset.css +316 -12
- package/src/routes/__tests__/compose.test.ts +3 -1
- package/src/routes/api/__tests__/collections.test.ts +81 -27
- package/src/routes/api/__tests__/mcp.test.ts +389 -0
- package/src/routes/api/__tests__/nav-items.test.ts +163 -2
- package/src/routes/api/__tests__/posts.test.ts +190 -0
- package/src/routes/api/__tests__/settings.test.ts +99 -3
- package/src/routes/api/__tests__/upload-multipart.test.ts +10 -2
- package/src/routes/api/__tests__/upload.test.ts +132 -0
- package/src/routes/api/__tests__/uploads.test.ts +5 -3
- package/src/routes/api/collections.ts +67 -26
- package/src/routes/api/custom-urls.ts +1 -0
- package/src/routes/api/export.ts +8 -4
- package/src/routes/api/github-sync.tsx +380 -0
- package/src/routes/api/internal/__tests__/sites.test.ts +45 -2
- package/src/routes/api/internal/sites.ts +2 -0
- package/src/routes/api/internal/text-attachments.ts +60 -0
- package/src/routes/api/internal/uploads.ts +5 -1
- package/src/routes/api/mcp.ts +30 -0
- package/src/routes/api/nav-items.ts +30 -11
- package/src/routes/api/palette.ts +17 -0
- package/src/routes/api/posts.ts +37 -106
- package/src/routes/api/public/__tests__/posts.test.ts +372 -0
- package/src/routes/api/public/posts.ts +302 -0
- package/src/routes/api/search.ts +1 -47
- package/src/routes/api/settings.ts +44 -58
- package/src/routes/api/upload-multipart.ts +15 -5
- package/src/routes/api/upload.ts +62 -49
- package/src/routes/api/uploads.ts +1 -0
- package/src/routes/auth/__tests__/hosted-sso.test.ts +33 -0
- package/src/routes/auth/__tests__/setup.test.ts +29 -4
- package/src/routes/auth/hosted-sso-expired-page.tsx +89 -0
- package/src/routes/auth/hosted-sso.ts +42 -1
- package/src/routes/auth/reset.tsx +46 -32
- package/src/routes/auth/setup.tsx +53 -43
- package/src/routes/auth/signin.tsx +34 -24
- package/src/routes/compose.tsx +114 -1
- package/src/routes/dash/__tests__/font-theme.test.ts +6 -27
- package/src/routes/dash/__tests__/settings-avatar.test.ts +2 -3
- package/src/routes/dash/custom-urls.tsx +250 -112
- package/src/routes/dash/settings.tsx +1179 -70
- package/src/routes/feed/__tests__/{rss.test.ts → feed.test.ts} +62 -123
- package/src/routes/feed/feed.ts +386 -0
- package/src/routes/feed/manifest.ts +67 -0
- package/src/routes/feed/sitemap.ts +11 -2
- package/src/routes/pages/__tests__/collection-routing.test.ts +176 -0
- package/src/routes/pages/__tests__/collections.test.ts +2 -3
- package/src/routes/pages/__tests__/featured.test.ts +2 -3
- package/src/routes/pages/archive.tsx +523 -127
- package/src/routes/pages/collection.tsx +197 -43
- package/src/routes/pages/collections.tsx +5 -1
- package/src/routes/pages/home.tsx +2 -2
- package/src/routes/pages/latest.tsx +3 -3
- package/src/routes/pages/page.tsx +221 -3
- package/src/routes/pages/partials.tsx +5 -0
- package/src/routes/pages/search.tsx +17 -1
- package/src/routes/pages/theme-sample.tsx +1 -2
- package/src/runtime/__tests__/node.test.ts +1 -1
- package/src/runtime/readiness.ts +1 -1
- package/src/runtime/site.ts +9 -1
- package/src/services/__tests__/collection.test.ts +137 -54
- package/src/services/__tests__/github-app-installations.test.ts +181 -0
- package/src/services/__tests__/github-sync-classify.test.ts +189 -0
- package/src/services/__tests__/github-sync-push.test.ts +159 -0
- package/src/services/__tests__/media.test.ts +444 -7
- package/src/services/__tests__/navigation.test.ts +271 -14
- package/src/services/__tests__/post-timeline.test.ts +31 -21
- package/src/services/__tests__/post.test.ts +83 -9
- package/src/services/__tests__/settings.test.ts +43 -21
- package/src/services/auth.ts +2 -2
- package/src/services/bootstrap.ts +10 -7
- package/src/services/collection.ts +591 -193
- package/src/services/custom-url.ts +34 -7
- package/src/services/export-theme/assets/client-site.css +2 -0
- package/src/services/export-theme/assets/client-site.js +145 -0
- package/src/services/export-theme/layouts/_default/alias.html +49 -0
- package/src/services/export-theme/layouts/_default/baseof.html +21 -0
- package/src/services/export-theme/layouts/_default/list.html +104 -0
- package/src/services/export-theme/layouts/_default/rss.xml +160 -0
- package/src/services/export-theme/layouts/_default/single.html +15 -0
- package/src/services/export-theme/layouts/archive/list.html +31 -0
- package/src/services/export-theme/layouts/collection/single.html +19 -0
- package/src/services/export-theme/layouts/collections/list.html +111 -0
- package/src/services/export-theme/layouts/featured/list.html +28 -0
- package/src/services/export-theme/layouts/index.html +41 -0
- package/src/services/export-theme/layouts/partials/feed-post-content.xml +107 -0
- package/src/services/export-theme/layouts/partials/footer.html +32 -0
- package/src/services/export-theme/layouts/partials/head.html +52 -0
- package/src/services/export-theme/layouts/partials/header.html +115 -0
- package/src/services/export-theme/layouts/partials/media-gallery.html +246 -0
- package/src/services/export-theme/layouts/partials/pagination.html +55 -0
- package/src/services/export-theme/layouts/partials/post-card.html +127 -0
- package/src/services/export-theme/layouts/partials/reply.html +91 -0
- package/src/services/export-theme/layouts/partials/thread-preview.html +82 -0
- package/src/services/export-theme/layouts/post/list.html +129 -0
- package/src/services/export-theme/styles/main.css +1981 -0
- package/src/services/export-theme/theme.toml +12 -0
- package/src/services/export.ts +1365 -2412
- package/src/services/github-app-installations.ts +302 -0
- package/src/services/github-sync.ts +769 -0
- package/src/services/index.ts +15 -0
- package/src/services/mcp.ts +1324 -0
- package/src/services/media.ts +336 -25
- package/src/services/navigation.ts +115 -23
- package/src/services/path.ts +129 -28
- package/src/services/post.ts +658 -195
- package/src/services/search.ts +3 -0
- package/src/services/settings.ts +29 -15
- package/src/services/site-admin.ts +26 -6
- package/src/services/upload-session.ts +69 -34
- package/src/style-cjk-jp.css +3 -0
- package/src/style-cjk-kr.css +3 -0
- package/src/styles/components.css +160 -59
- package/src/styles/fonts/et-book/et-book-bold-line-figures.woff +0 -0
- package/src/styles/fonts/et-book/et-book-display-italic-old-style-figures.woff +0 -0
- package/src/styles/fonts/et-book/et-book-roman-line-figures.woff +0 -0
- package/src/styles/fonts/et-book.css +31 -0
- package/src/styles/fonts/latin.css +1 -0
- package/src/styles/fonts/noto-serif-jp/400/057a6a98bda7fe57105ddaa99ec82015.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0acd1fe2b2ea1ad1bfee7ae1fa139d27.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0c2ab6b295e55f356f8020d4e7747522.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0c5f9492af03a4fa42c784de94649de1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0d9a936885a4c39077438effd3779cbd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/0e645da524f7cfc0e8c3c03fb2b08428.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/135e83b403475c5dc9e49b43501a5b84.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/13b2c53b30e6a3e4de7132dbc18dbdfc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/15d95680dc31cc6ce20e0a5464106dc4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/1733f27476507ca68b68a803bc533cc2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/188c2db794f3dd7a45889ddbc81da9bb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/1cf737900dd49c2e88f1b3221a82a602.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/233d3a685ee18276b319363474599d47.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/284b53bbefb06924cf236d24c8ed5641.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2a56eaf19d1d38a6b57e2a388f733676.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2abfbab82b6a7c04426afc054d2464da.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/2e6f4bb71ef6b38765d51acc5a79f638.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/31194d303a67561926a544ed0e072aee.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/3623d96698132c61e861f984ce11cc23.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/39211b02f24c69cbd1b54f6d41b01933.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/3e24063b19abd2b051af986a6870046d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/4410f876d27c9821839474b6b1059fd7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/479329a39a3eb6c0047e6b0981919855.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/527ff336279465617aafbafe1a830632.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/56b40518ea0608e62826bd44ee27f5f9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5acc7f736217259db79a42bf44241c48.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5c05f8b75c9d7f25b3c04ca711cf43b3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/5e0124c7265f1b4cf0fc797b94efbebc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/615a7e0d63f257109d599b2a1977de68.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/61a2c80d0c924d5a6187c02e8d1d1642.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/62fc94f8d4c5a750b7f25e7387539910.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/639c7c6b0b0c27c738702741cfa4b8c0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/68a3a4bf337f8a0722be76676e20b850.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/6abdd163d2c4b85698db8aa1ce149361.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/73b794d885b88f7befb7ea8ab1780a15.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/743f290baf027d4626a86e22f3d44600.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/757019c01c5e2c2b0f54293cea3b5636.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/76f117a3baacda304a71965a17266a13.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/77747f17625a259fb405b7bbdd84ac4b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/788498548ddfb710d6ef3c7bff79fa97.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/78ea6c40923ce95367e3517dd1e5a849.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/7f9a6c9286b68de9c72b0024f7beeb40.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/823d1bb11097331238d103c7f72138dc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/888ba53510213d5d1f6a7deb60e569b6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/892aa49b2529c89bb4076d4aa51fe30f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/95ff41191c76ef893ac70a1043e95e44.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/96c75862c8cec51a7c05ff025fea86cd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/9b21e8244f5930c48ad5073f83777b6c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/9fce02d1a09f464c36c9fcfb14a354c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a0a6755c7e3f9e4cf03387027dd9f16c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a4475c442c6b259354c9eda62e64e7df.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a4a39b5f048671aa37c2b2a9581ab08a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a534449d67561ac9b06489f64e57ad98.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/a919a8c6fec6d7e8bb21177965f2e9d7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ad67113f88b59582991cb0c5d33ea19f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/adb6e613fb99c6dd660b727101f554a8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/adc4a98a89870ef984ee8f4b57c8a3a5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/b12379ab782468d725519cd07a7d15bc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/b55486f8a459c838fe329d4e79a8c211.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bb0b15492b8cdbbec57c0bcfa0aa9241.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bedc74b423b7293b6ad0bdecc61c42cc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/bfb00d4a4c48661bd0be99f300a0faae.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c1b0df29ae41d764904df84e9ac83d1e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c42c67070cfe99cf823df92d81c7fa6e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c4d749e45ecd5a5aed5a0bb3ebfd355d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/c96d83978add28b356c22c4c84916733.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/cb7ccd6494256f7a2977b7c2b0225592.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ce41d70ce6a069a498525c9e15c45cf2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/d509ee5c8241bc7c3de2039d75564fa5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/d675d717cd329bfd4c0524f76ae1579c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/dc8f6256445e68199540be9ade33529f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e8c15be62faf978d208925e79ea6a10d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e91c4d941ed9f5c2f3e27f205a3a225e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e9c566be7a5d38a9085225f7372bc82b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/e9ebc567a711eeb29019ddae3e0ce7fe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/ecb8875a56c7b038b35432fda41ae128.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f1661731474b78bdf81114daf10b254f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f2c58dee206ba9355046fc23d05491f7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f50ac27ea4358d67fdda403c2bb52467.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f55334112f8e8be82d65db29887a663f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f740e93f3c0277ecc616594103bca683.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f7633b5af033d76ff2fb3c3c266d77c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/f7d8468cba2335a83ee414ea68291bab.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/fc9ae5b600fb711f2d67e93ce768cba4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/400/fd1bceb55d3e0183ac2454b8532fec7d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/02611a045a7fe83a12014e3debc9f731.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/0f5e1a18987dbc84ca05188c129e1936.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/112743a4ab5fdd1498dfdf2b11336380.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1211f03d3ff5759a702631445793f60e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/147b24c2f08d03bbed30887d4cb3311c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/16663e567f81f4725a1522f37e18f71f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1824321da801e6257b902f3d1d09054c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1a3a92c7c060a71a6c35819b9ebcdbb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/1fba7ec0e412e911bf31841de5a8a4e4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/28a97af9ab9d38983d20f39ff09840e1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2bfaadaf3479c72286248e6de0be0ec9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2c8c55e4cec85ce77e95cac9d330f5cf.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2ed9981d2e8983365bd051159b976b6d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/2f4c633e923ba30c6ba376367379fc91.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/349965eee0a9b6c984a319ab96a4ece9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/36cda3eae13370b934bba4429fab9078.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/3aa13f1843d7c8fa16109b6395a9a29d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/3e2c06bdd9dcab29aeaeb0cfb7fa608a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/424123f9371ba357087363263eeafcda.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/43b3bbf43055c3e65db9cdf2c5b18c1c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/44704471b60c18b3ecec5300a88f1102.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/46604090efbb99c2db1800928af0a239.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/46998df85d31e629c0142f0556f5e7c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/49902f1c41b281f8eb3771abdd4dccb8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/4db4f31a16965baa42cb7dad667a8f04.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/572b9e28f1e6d89b765a16d809db84a1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/5921cef36750ff6092d94b83a802823b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/59e95d8b5332dcdae894a19946ab767d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/5ec349ff62b9ee5fe0c6dae867704dc4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/601f812e8b1eafecd5ba585a6a6d7962.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/6025ddaf99d86cc8bfd781006b19fbd6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/60f6e28b3f0400b0e8fe32c3c7116102.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/64a404d675f1d726f0891b7a80794595.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/7158022889c6c177062ac85036e7af10.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/7e38ca789a9e76ac91d9256374e154f0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/815a0b797465190f60d8b1a04656c42e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8726558cf297cbda831cc19514897205.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8855472f3c02f6c7ebb3216617ebe4af.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/8e231d707f0c4f8e3cde90a6b52a79aa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9064c12fd72c7aba235d8c3881755b91.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/911b9e53e9814de2998c60bf3115f560.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/92fdc376bce277874e75db666f175b44.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/930a23430f0eb64480d7fe5f82834e21.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/93bed0e5f2dd5a21cf73304fcfbc149d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/93f3ea6918533d96d9c252378b9a4af0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/95a0951d2a2722ff773a1a45e8c474d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/982cd1765ca6bbb3e6547e47834d5148.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9b0c3caac458c7bc9c9133e40405ddea.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/9f3bdab4025dc7c5ba1a5871e62e8918.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a00e94d4f04df6aa659db9c4954c7efe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a2b0597837e382aca19919c4b74e58c8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a373e85c96aa0dc303092429fb837e51.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a6e044424780a57610833cc856c15bf5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/a7c58070d68dc1724e9d4e781afb78db.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/accc43762ad05edfbff66ba1380d192a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ad47bcd6d4663026d648c132d969318d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/af57a2e8c72f9ba0efb1af771d32c124.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b0ae5f374e43dac992a4a5d334cebc0b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b91234c10fd8b8c8abc88e03afe66a1f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/b9317198f118a1dfd8ddf2b82ec028f3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ba825ae79b1a6f7e0cce5215fcb5c96f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc6183ac08f0fac78c46f80c10cf7c92.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc8ccea5abf6598cf3cfa97eb59804bb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bc95a792cbca6639214c9b0da13392ff.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/bcfa62f35731856246c146d3a6932bf3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c063897793f593eb26d6ff7b7baaba18.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c6c2971ad1f3221f6cf84028aa0f477e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/c97f41eef722121d86f55d553c056a39.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/ca62704509932d3232d62918de97af3f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d1f064825fa5784b5c930652bd831cce.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d2faabcedd19f016e7b21bce073c0ec8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d320171e57480510f87dbbc7d5264b0a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d3a72a99d365dddfbca8d017a8011368.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d42aafa0f246ad3b4c16fe96d3a3a432.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d4aaf23c13ae808a4bed617afd13aa07.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d6bb686bddfbe8f38a36a68e609a8667.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/d9020ff69a83b2a6ee1f42ae480f7db0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/da90a9012ab2d98f759e3fa0820ef502.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/df10d94bea357a43313e20da5d84bd30.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e34b2b141e472dc776c86fdf8eea23b0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e444fd88f1390636e603d0d681538ac8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/e79898628283edc27180fc39d0d769c1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/eaf332445a40942928e5f5750c1c3116.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/f91cd722855f4269256eae1187df64ec.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fc895f5ce66b656f4a933097bf2a8775.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fca6720fd14c467d29a90f18ef3859b9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/700/fd1bb507bcbf04856eeb7f7fd47ea579.woff2 +0 -0
- package/src/styles/fonts/noto-serif-jp/noto-serif-jp.css +3349 -0
- package/src/styles/fonts/noto-serif-kr/400/033466ef683afe931f7f520cfb42d928.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/05da12edb9d52210581dc6ec4541031f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/0968e4861204b51f62a2f8e9f15dd5e0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/0d5dec931dc885f07fe5cd5af8bed675.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/1076f0f6f66d28d7a2f16427faad4413.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/12ef1ba76bd20b004b865266a1aa70b3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/152395634a207579552f8cb54db88599.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/1a08931435f885e707edb85978ea3bb6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/31a197213ae61d7043c81013f52d10d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/37dbd564820cce2f7b3331e442da0df3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/37dc8505b20b37a2477a9121b16d44a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/3ac0a07878cacdc98bb889f40f5970d3.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/491bbba374093fff045f55803a22bfef.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/4d38e56738156625329d93bd5c8cc74a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/57e9a86651366c1ba299e47aa7e358c2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/57f89adeae55aa7fe2add3fc1801ce03.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/5df9ce98c75f8c50fb4fff7f01a23925.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/60020a830d809c26970cf8e48ba1eeda.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/60bcfa3ea7446eb42394aab02d28be40.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/63fafcb069520613d0ea35ad3e6b1e42.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/65a60d87c64228d258a123cbe85f5f31.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/6968e889e18891d912809fe484c2e745.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/70714891ad3fbfc3d5f10a8669dacc5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/761d84afca8d7f34eebefe538adba827.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/76e07530323418ec723c5d7634c9bcca.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/773819b7b9b8fd404a929867c0fd677e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7817dd16805145d8538ad57590f69f5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7b59f0ec7792b18458dc5a361e37884c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/7c3945788a689a69356c1a622d69d48b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/835b6505bb9eea9678925a1fa885353d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8b5c9ef81159f31d2ab35f45ca4373e0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8b91c7c2ed390f1278b9befa3aa87233.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/8fb45117a62d92dce44a80f0b729ead5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/90b7848e9b1623b77bdcf155e90b839c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/985d4d41afa0934a4eb2de35473fb9a2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9bb38fd05201de666b88b82d56a386f7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9eff2ab2a9d2ced47ab761bf6fdb11ec.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/9f1963fe8d4d6878d717d872a8f3fdb5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a0d00fe4816c95a8c7dffd445ef00b03.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a0f199077fa1a33bc2a1e01c64de7fe6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/a7c35e42a659347e490c3cb7983b2e7f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/aa662092cf249707123ca4d575e2764b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/aae1eda193285ab817a2d1b408440326.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b0cb664cb2e1371efda8943c0b7dcd1c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b0d1cdced482352cf0d3ae58638aacb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/b320f5d185b2cff933ac549c184031c5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ba9c59d7dfa4494db1bb764ada81467d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bab547459d514f46206e340c4bb2dc88.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bcc39eda837bb7a7a3d37c8c60fffb81.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bd73264d7f98776708d5d6f3c9b78fcc.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bed610b217d500f5975cfc9fe6157570.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/bf8901f8f11d4f433ea17dabc9370ea6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c4143bb9f2fe77b6ccf20088a8904650.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c7ff3f6bbdcd5f604b7343602ab904df.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/c945c62368357d05a53206620460fb30.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ce022e18a1377ac509443c3c3790b431.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/cf9cffe56636322f62b40d61130fbc5e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d20d8944bc0b85f5b2aae4b24f343516.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d36644d6502527e1fff205d0c7eca434.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/d40ab99c7a38026f411c8f112f742b48.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/dc6e234ded795e91f76d6647f628fbf0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/dd0cfdfdac0866e66d587e2b5a9e9961.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/de304c8a02e45ded4f8dcc479d167198.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/e8c364c16daa04835bf32d293d2598db.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ec86e23683052da5cfdc3b77641bd15a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/eda1b0cb6d1719dd9bedcf3216a9e8de.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/ee6fce20b420a480714607c66d7f97e5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/eee3836d6ac17ebb2c450bbcbc9db121.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/f5738255e92d8dd34a46d1bcdf4c4074.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/fa6e58ce4b52695e7ae19bbea6336ec8.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/400/fd2069e6e8588c70b4f11364093b81f2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/04b8bde59cff68eeee74fb1914eb09c7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/05ac821472e235943ed1435e4bb8ecad.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/074c1f8483d5a4d8c45c8c5f3e4cbb32.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/1380210a722ac9aeef54005ad7b015c9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/17c7f5d0a45e92ede0e5dec3a2c77efa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/1be602ad456b0d75902d352116cb35fd.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/2612c60f9dc5459ac42763591240a1d6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/2ab8cf1f23a5ac7939a7876054e711a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/34e6750e00c3a911ef87dc66b336594d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/36f3df4730cfca4fc77fe52b52e6a126.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/37887deb7a9c466cf2af6ee7ff372ea6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3aba89c4d4281553078de4622b498cbb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3d88558097559d775231194d43745ba7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/3dc3e8c35524cad2d1236f3393104ccf.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/45140742dfa8686b58db2899ce89f89a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/469d2754e315e77270c12fa0ab28026c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4c0760284569b87d8f2290c324953973.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4cba92c942694bdc479bc54101bb1aa4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/4e7d263c30bb48604069a3b4404d4eeb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/5279c7e465d9871edf51fd02e691eee7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/54bfdb6f21f9d6191e5100580239e14f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/56be5e32fd01668fe5ba814d94905839.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/615b38e0b5bb3ea68426f44f47706e6f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/65e9c4585d71bf48a5c62f367010da16.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/6b75213eb0be40ce84241eb2bb438a2e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7520ca6ca8c60eb1e62d50e71c8d30f1.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/759a647b77791b1e98f99bc0ab5317a7.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/76e51630143b95b6322ef93ad330da7a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/79aa7c8c842c4a27cf57c0a3a1ffca5a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7cc72fd2c9105560422b6a67c6162945.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7d0ea0690e432462f4d05a23d720ec19.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/7dffa5c1bec57e0903fd62357401ff1a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/82f6ccc063960eed1cdfd1d61ada8862.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/845b4b67564d62cf5cad242f7ac08ea5.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/84c8e7bc0931008ed91f44ed12dfea94.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/887a11290ba78b1e66c6d2f67043005e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/88db1d042074fb6e66821ffc10941930.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/8dc18c0cebe6aa4bf4c45dbb831048ab.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/919a879bd2d580d8491a31a449390689.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/91da6cb174bebfb96976e2f1316be2fe.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/934dfdbed2bb2c4b6129199d699a34fa.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/97d7a17234f2b5030ae9697ae00aded2.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/995c4fda62d25c3b79dd5987737df6ae.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/9d0afa53dc2f92ba42024f55f11f6779.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/9e13a47242926f1cd7d68c08d9ef8889.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a379ada89abd750c587ded29f77e731c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a48be56ce5d3a99dc8f8331398004412.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a806759e72987951d6d08b7cbdf36a1b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a88481ec8bc7be11cb66e46781a79bb9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/a9a016d4a93409f65278327e8a8bb38d.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/ab92d41062a7644faa45f50bf384454a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b326a8a9c33b554db570da94f60bc380.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b6117ff2993b11bb1fdc7ea3588a010c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/b61982951bd51b724143c30dfaaa9fe9.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c0098958e20db68cab90097b5e62516f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c4bfaa5e50798246e3770718b7a7c84a.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/c84598999133455503042e06f4ab79cb.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/ca9a533988d7019597a60d4e17127e0c.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/cf2b28f90f47276f7e2688a65e88a101.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/d689b1861d7e4377dd72ad3013482612.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/d9047070d72a816b3dba9d40c2d85e69.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/da04549f3f4ed28076b01b8cd710d313.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/da7af303f8c645f9a9dbae0e6e32dd35.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/df9568257eb29b156449fdd4bec5ec76.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e067cd0ed76c90cd0a93c9339253f20b.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e08b07772e7bed3cec2832d43f7fd339.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e12150d5a39b30be8f567968c7a527b0.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e53fcb2381eee345db4f6f973dd95a3e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/e5bd313ef81f687d398aacb11cec3069.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/eb5afb3d952b8593782caec6026514b6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/edd6a4f608d04fc0351d7688cfc321e4.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/f9695c6c4df2bf6bc03045ff79d4f01f.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/fada6eaa68ff8816afe43d2a36c5423e.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/700/fbcc4bf5367218951172bdee6f77d7a6.woff2 +0 -0
- package/src/styles/fonts/noto-serif-kr/noto-serif-kr.css +2601 -0
- package/src/styles/site-media.css +823 -0
- package/src/styles/tokens.css +292 -21
- package/src/styles/ui.css +4188 -1857
- package/src/types/bindings.ts +3 -1
- package/src/types/config.ts +139 -9
- package/src/types/constants.ts +46 -11
- package/src/types/entities.ts +24 -8
- package/src/types/operations.ts +80 -1
- package/src/types/props.ts +10 -2
- package/src/types/raw-assets.d.ts +39 -0
- package/src/types/views.ts +28 -7
- package/src/ui/__tests__/color-themes.test.ts +7 -40
- package/src/ui/__tests__/font-themes.test.ts +30 -11
- package/src/ui/color-themes.ts +311 -270
- package/src/ui/compose/ComposeDialog.tsx +712 -394
- package/src/ui/compose/ComposePrompt.tsx +15 -10
- package/src/ui/dash/ActionButtons.tsx +27 -18
- package/src/ui/dash/DangerZone.tsx +15 -10
- package/src/ui/dash/FormatBadge.tsx +21 -8
- package/src/ui/dash/StatusBadge.tsx +33 -22
- package/src/ui/dash/appearance/AdvancedContent.tsx +44 -30
- package/src/ui/dash/appearance/CodeInjectionContent.tsx +164 -0
- package/src/ui/dash/appearance/ColorThemeContent.tsx +127 -160
- package/src/ui/dash/appearance/FontThemeContent.tsx +20 -15
- package/src/ui/dash/appearance/NavigationContent.tsx +368 -225
- package/src/ui/dash/appearance/__tests__/NavigationContent.test.tsx +54 -0
- package/src/ui/dash/settings/AccountContent.tsx +34 -23
- package/src/ui/dash/settings/AccountMenuContent.tsx +226 -137
- package/src/ui/dash/settings/ApiTokensContent.tsx +122 -79
- package/src/ui/dash/settings/AvatarContent.tsx +73 -45
- package/src/ui/dash/settings/DeleteAccountContent.tsx +106 -66
- package/src/ui/dash/settings/GeneralContent.tsx +274 -157
- package/src/ui/dash/settings/GitHubSyncContent.tsx +558 -0
- package/src/ui/dash/settings/SessionsContent.tsx +79 -38
- package/src/ui/dash/settings/SettingsRootContent.tsx +209 -109
- package/src/ui/dash/settings/__tests__/AccountMenuContent.test.tsx +67 -0
- package/src/ui/dash/settings/__tests__/GeneralContent.test.tsx +75 -0
- package/src/ui/feed/CuratedThreadPreview.tsx +24 -14
- package/src/ui/feed/LinkCard.tsx +116 -84
- package/src/ui/feed/LinkPreview.tsx +75 -0
- package/src/ui/feed/NoteCard.tsx +60 -16
- package/src/ui/feed/PostStatusBadges.tsx +15 -0
- package/src/ui/feed/QuoteCard.tsx +10 -3
- package/src/ui/feed/ThreadPreview.tsx +62 -36
- package/src/ui/feed/TimelineFeed.tsx +2 -1
- package/src/ui/feed/__tests__/thread-preview.test.ts +220 -11
- package/src/ui/feed/__tests__/timeline-cards.test.ts +186 -5
- package/src/ui/feed/thread-preview-state.ts +23 -10
- package/src/ui/font-themes.ts +57 -97
- package/src/ui/layouts/BaseLayout.tsx +221 -136
- package/src/ui/layouts/SiteLayout.tsx +431 -143
- package/src/ui/layouts/__tests__/BaseLayout.test.tsx +76 -6
- package/src/ui/layouts/__tests__/SiteLayout.test.tsx +76 -0
- package/src/ui/pages/ArchivePage.tsx +320 -207
- package/src/ui/pages/BrandPage.tsx +499 -341
- package/src/ui/pages/CollectionEditorPage.tsx +50 -30
- package/src/ui/pages/CollectionPage.tsx +151 -78
- package/src/ui/pages/CollectionsPage.tsx +27 -18
- package/src/ui/pages/ComposePage.tsx +15 -10
- package/src/ui/pages/FeaturedPage.tsx +16 -11
- package/src/ui/pages/HomePage.tsx +15 -10
- package/src/ui/pages/PostPage.tsx +5 -1
- package/src/ui/pages/SearchPage.tsx +93 -43
- package/src/ui/pages/ThemeSamplePage.tsx +758 -520
- package/src/ui/pages/__tests__/ArchivePage.test.tsx +72 -0
- package/src/ui/shared/CollectionDirectory.tsx +215 -34
- package/src/ui/shared/CollectionsManager.tsx +155 -75
- package/src/ui/shared/DecorativeQuoteMark.tsx +12 -9
- package/src/ui/shared/MediaGallery.tsx +350 -256
- package/src/ui/shared/Pagination.tsx +36 -25
- package/src/ui/shared/PostFooter.tsx +139 -96
- package/src/ui/shared/__tests__/media-gallery.test.ts +27 -0
- package/src/ui/shared/__tests__/navigation-labels.test.ts +79 -28
- package/src/ui/shared/__tests__/post-footer.test.ts +42 -17
- package/src/ui/shared/collection-management-labels.ts +109 -33
- package/src/ui/shared/navigation-labels.ts +78 -8
- package/bin/lib/site-localize-media.js +0 -427
- package/dist/client/_assets/client-auth.js +0 -3260
- package/dist/client/_assets/client.css +0 -2
- package/dist/client/_assets/client.js +0 -380
- package/src/__tests__/site-localize-media.test.ts +0 -150
- package/src/client/components/jant-collection-sidebar.ts +0 -816
- package/src/i18n/locales/en.ts +0 -1
- package/src/i18n/locales/zh-Hans.po +0 -3197
- package/src/i18n/locales/zh-Hans.ts +0 -1
- package/src/i18n/locales/zh-Hant.po +0 -3197
- package/src/i18n/locales/zh-Hant.ts +0 -1
- package/src/routes/feed/rss.ts +0 -216
- package/src/types/lingui-react-macro.d.ts +0 -34
- /package/dist/client/_assets/chunks/{heic-to-XcUDQvtx.js → heic-to-DUUaO23q.js} +0 -0
- /package/dist/client/_assets/chunks/{module-ChVQstFd.js → module-DcsAZQZ_.js} +0 -0
- /package/dist/client/_assets/chunks/{native-CR5HLOyf.js → native-DpcrFAPh.js} +0 -0
- /package/dist/client/_assets/{client-cjk.css → client-cjk-B7Z0snDu.css} +0 -0
- /package/dist/client/_assets/{client-cjk-tc.css → client-cjk-tc-BesJYrb2.css} +0 -0
package/src/services/post.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
import {
|
|
10
10
|
eq,
|
|
11
11
|
and,
|
|
12
|
+
or,
|
|
12
13
|
type SQL,
|
|
13
14
|
type SQLWrapper,
|
|
14
15
|
isNull,
|
|
@@ -31,9 +32,10 @@ import {
|
|
|
31
32
|
} from "../db/schema-bundle.js";
|
|
32
33
|
import { createEntityId } from "../lib/ids.js";
|
|
33
34
|
import { now } from "../lib/time.js";
|
|
34
|
-
import { renderTiptapJson } from "../lib/tiptap-render.js";
|
|
35
|
+
import { renderTiptapJson, trimTiptapBody } from "../lib/tiptap-render.js";
|
|
35
36
|
import { extractSummary, extractBodyText } from "../lib/summary.js";
|
|
36
37
|
import { markdownToTiptapJson } from "../lib/markdown-to-tiptap.js";
|
|
38
|
+
import { tiptapJsonToMarkdown } from "../lib/tiptap-to-markdown.js";
|
|
37
39
|
import { generatePostSlug } from "../lib/slug.js";
|
|
38
40
|
import { getSlugValidationIssue } from "../lib/slug-format.js";
|
|
39
41
|
import { normalizePath, slugify } from "../lib/url.js";
|
|
@@ -64,6 +66,12 @@ import {
|
|
|
64
66
|
NotFoundError,
|
|
65
67
|
} from "../lib/errors.js";
|
|
66
68
|
import { createPathService, type PathService } from "./path.js";
|
|
69
|
+
import {
|
|
70
|
+
extractYouTubeVideoId,
|
|
71
|
+
getYouTubeThumbnailUrls,
|
|
72
|
+
} from "../lib/youtube.js";
|
|
73
|
+
import { getPreviewStorageKey } from "../lib/upload.js";
|
|
74
|
+
import { generateRandomId } from "../lib/nanoid.js";
|
|
67
75
|
|
|
68
76
|
/** Dependencies for operations that coordinate with other services */
|
|
69
77
|
export interface PostDeleteDeps {
|
|
@@ -139,8 +147,18 @@ export interface CollectionFeedEntry {
|
|
|
139
147
|
collectedAt: number;
|
|
140
148
|
}
|
|
141
149
|
|
|
150
|
+
export interface PostBodyContent {
|
|
151
|
+
id: string;
|
|
152
|
+
type: "post";
|
|
153
|
+
format: Format;
|
|
154
|
+
contentFormat: "markdown";
|
|
155
|
+
content: string;
|
|
156
|
+
chars: number;
|
|
157
|
+
}
|
|
158
|
+
|
|
142
159
|
export interface PostService {
|
|
143
160
|
getById(id: string): Promise<Post | null>;
|
|
161
|
+
getBodyContent(id: string): Promise<PostBodyContent | null>;
|
|
144
162
|
getBySlug(slug: string): Promise<Post | null>;
|
|
145
163
|
suggestSlug(input: {
|
|
146
164
|
title?: string;
|
|
@@ -164,6 +182,24 @@ export interface PostService {
|
|
|
164
182
|
deps: PostAttachmentDeps,
|
|
165
183
|
summaryConfig?: SummaryConfig,
|
|
166
184
|
): Promise<Post>;
|
|
185
|
+
/**
|
|
186
|
+
* Atomically create a thread of posts. The first item is the root; each
|
|
187
|
+
* subsequent item is automatically chained as a reply to the previous one.
|
|
188
|
+
* On failure, all already-created posts are rolled back.
|
|
189
|
+
*
|
|
190
|
+
* @param items - Ordered list of (data, attachments) pairs; at least 2 required
|
|
191
|
+
* @param deps - Media/storage dependencies
|
|
192
|
+
* @param summaryConfig - Optional summary extraction config
|
|
193
|
+
* @returns Ordered list of created posts; posts[0] is the root
|
|
194
|
+
*/
|
|
195
|
+
createThreadWithAttachments(
|
|
196
|
+
items: Array<{
|
|
197
|
+
data: CreatePost;
|
|
198
|
+
attachments: PostAttachmentInput[] | undefined;
|
|
199
|
+
}>,
|
|
200
|
+
deps: PostAttachmentDeps,
|
|
201
|
+
summaryConfig?: SummaryConfig,
|
|
202
|
+
): Promise<Post[]>;
|
|
167
203
|
update(
|
|
168
204
|
id: string,
|
|
169
205
|
data: UpdatePost,
|
|
@@ -184,6 +220,11 @@ export interface PostService {
|
|
|
184
220
|
* @param deps - Media service and optional storage driver for file cleanup
|
|
185
221
|
*/
|
|
186
222
|
delete(id: string, deps?: PostDeleteDeps): Promise<boolean>;
|
|
223
|
+
/**
|
|
224
|
+
* Delete a thread draft and release its slug paths so they can be reused.
|
|
225
|
+
* Used when replacing a saved thread draft with a new version.
|
|
226
|
+
*/
|
|
227
|
+
deleteThreadDraft(id: string, deps?: PostDeleteDeps): Promise<boolean>;
|
|
187
228
|
getThread(rootId: string): Promise<Post[]>;
|
|
188
229
|
updateThreadStatusAndVisibility(
|
|
189
230
|
rootId: string,
|
|
@@ -259,24 +300,8 @@ function isValidSlug(value: string): boolean {
|
|
|
259
300
|
return SLUG_RE.test(value);
|
|
260
301
|
}
|
|
261
302
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
let current: unknown = err;
|
|
265
|
-
while (current) {
|
|
266
|
-
const msg = String(current);
|
|
267
|
-
if (
|
|
268
|
-
msg.includes("UNIQUE constraint") ||
|
|
269
|
-
msg.includes("SQLITE_CONSTRAINT")
|
|
270
|
-
) {
|
|
271
|
-
return true;
|
|
272
|
-
}
|
|
273
|
-
current =
|
|
274
|
-
current instanceof Error && current.cause !== current
|
|
275
|
-
? current.cause
|
|
276
|
-
: undefined;
|
|
277
|
-
}
|
|
278
|
-
return false;
|
|
279
|
-
}
|
|
303
|
+
// Re-export shared constraint detection — see db/dialect.ts
|
|
304
|
+
import { isUniqueConstraintError } from "../db/dialect.js";
|
|
280
305
|
|
|
281
306
|
function hasNonEmptyText(value: string | null | undefined): boolean {
|
|
282
307
|
return typeof value === "string" && value.trim().length > 0;
|
|
@@ -349,8 +374,10 @@ function ensurePostRating(
|
|
|
349
374
|
function assertPostFormatShape(data: {
|
|
350
375
|
format: Format;
|
|
351
376
|
title?: string | null;
|
|
377
|
+
body?: unknown;
|
|
352
378
|
url?: string | null;
|
|
353
379
|
quoteText?: string | null;
|
|
380
|
+
hasAttachments?: boolean;
|
|
354
381
|
}): void {
|
|
355
382
|
const hasTitle = hasNonEmptyText(data.title);
|
|
356
383
|
const hasUrl = hasNonEmptyText(data.url);
|
|
@@ -363,6 +390,18 @@ function assertPostFormatShape(data: {
|
|
|
363
390
|
if (hasQuoteText) {
|
|
364
391
|
throw new ValidationError("Notes can't include quoted text.");
|
|
365
392
|
}
|
|
393
|
+
// hasAttachments === undefined means unknown (e.g. update without
|
|
394
|
+
// attachment changes) — skip the empty-content check in that case.
|
|
395
|
+
if (
|
|
396
|
+
!hasTitle &&
|
|
397
|
+
!data.body &&
|
|
398
|
+
data.hasAttachments !== undefined &&
|
|
399
|
+
!data.hasAttachments
|
|
400
|
+
) {
|
|
401
|
+
throw new ValidationError(
|
|
402
|
+
"Notes need a title, body, or at least one attachment.",
|
|
403
|
+
);
|
|
404
|
+
}
|
|
366
405
|
return;
|
|
367
406
|
}
|
|
368
407
|
|
|
@@ -497,6 +536,87 @@ export function createPostService(
|
|
|
497
536
|
return [...new Set(collectionIds)];
|
|
498
537
|
}
|
|
499
538
|
|
|
539
|
+
/**
|
|
540
|
+
* Fetch a link preview thumbnail and store it.
|
|
541
|
+
* Silently returns without updating if the URL is not a recognized provider
|
|
542
|
+
* or if the fetch fails.
|
|
543
|
+
*/
|
|
544
|
+
async function resolveAndStorePreview(
|
|
545
|
+
postId: string,
|
|
546
|
+
format: string,
|
|
547
|
+
url: string | null | undefined,
|
|
548
|
+
storage: StorageDriver | null | undefined,
|
|
549
|
+
): Promise<void> {
|
|
550
|
+
if (!storage || format !== "link" || !url) return;
|
|
551
|
+
|
|
552
|
+
const videoId = extractYouTubeVideoId(url);
|
|
553
|
+
if (!videoId) return;
|
|
554
|
+
|
|
555
|
+
const thumbnailUrls = getYouTubeThumbnailUrls(videoId);
|
|
556
|
+
let imageBytes: Uint8Array | null = null;
|
|
557
|
+
let contentType = "image/jpeg";
|
|
558
|
+
|
|
559
|
+
for (const thumbUrl of thumbnailUrls) {
|
|
560
|
+
try {
|
|
561
|
+
const response = await fetch(thumbUrl);
|
|
562
|
+
if (response.ok) {
|
|
563
|
+
const ct = response.headers.get("content-type");
|
|
564
|
+
// YouTube returns a grey placeholder (120×90) when maxresdefault
|
|
565
|
+
// is not available. Detect it by checking content-length.
|
|
566
|
+
const cl = response.headers.get("content-length");
|
|
567
|
+
if (cl && parseInt(cl, 10) < 5000) {
|
|
568
|
+
// Too small — likely a placeholder, try next quality
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
imageBytes = new Uint8Array(await response.arrayBuffer());
|
|
572
|
+
if (ct) contentType = ct;
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
} catch {
|
|
576
|
+
// Network failure — try next URL
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (!imageBytes) return;
|
|
581
|
+
|
|
582
|
+
const ext = contentType.includes("webp")
|
|
583
|
+
? "webp"
|
|
584
|
+
: contentType.includes("png")
|
|
585
|
+
? "png"
|
|
586
|
+
: "jpg";
|
|
587
|
+
const suffix = generateRandomId(8);
|
|
588
|
+
const storageKey = getPreviewStorageKey(siteId, postId, suffix, ext);
|
|
589
|
+
|
|
590
|
+
try {
|
|
591
|
+
await storage.put(storageKey, imageBytes, { contentType });
|
|
592
|
+
await db
|
|
593
|
+
.update(posts)
|
|
594
|
+
.set({
|
|
595
|
+
previewImageKey: storageKey,
|
|
596
|
+
previewKind: "video",
|
|
597
|
+
previewProvider: "youtube",
|
|
598
|
+
})
|
|
599
|
+
.where(and(eq(posts.siteId, siteId), eq(posts.id, postId)));
|
|
600
|
+
} catch {
|
|
601
|
+
// Storage or DB failure — preview is best-effort
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/**
|
|
606
|
+
* Delete a preview image from storage if it exists.
|
|
607
|
+
*/
|
|
608
|
+
async function deletePreviewImage(
|
|
609
|
+
previewImageKey: string | null | undefined,
|
|
610
|
+
storage: StorageDriver | null | undefined,
|
|
611
|
+
): Promise<void> {
|
|
612
|
+
if (!storage || !previewImageKey) return;
|
|
613
|
+
try {
|
|
614
|
+
await storage.delete(previewImageKey);
|
|
615
|
+
} catch {
|
|
616
|
+
// Best-effort cleanup
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
500
620
|
function buildCollectionMembershipCondition(
|
|
501
621
|
collectionIds: readonly string[],
|
|
502
622
|
): SQL<unknown> {
|
|
@@ -631,6 +751,25 @@ export function createPostService(
|
|
|
631
751
|
return conditions;
|
|
632
752
|
}
|
|
633
753
|
|
|
754
|
+
async function getLastLivePostIdInThread(
|
|
755
|
+
threadId: string,
|
|
756
|
+
): Promise<string | null> {
|
|
757
|
+
const rows = await db
|
|
758
|
+
.select({ id: posts.id })
|
|
759
|
+
.from(posts)
|
|
760
|
+
.where(
|
|
761
|
+
and(
|
|
762
|
+
eq(posts.siteId, siteId),
|
|
763
|
+
eq(posts.threadId, threadId),
|
|
764
|
+
isNull(posts.deletedAt),
|
|
765
|
+
),
|
|
766
|
+
)
|
|
767
|
+
.orderBy(desc(posts.createdAt), desc(posts.id))
|
|
768
|
+
.limit(1);
|
|
769
|
+
|
|
770
|
+
return rows[0]?.id ?? null;
|
|
771
|
+
}
|
|
772
|
+
|
|
634
773
|
function getCursorSortTimestamp(row: typeof posts.$inferSelect): number {
|
|
635
774
|
return row.status === "draft" ? row.updatedAt : (row.lastActivityAt ?? -1);
|
|
636
775
|
}
|
|
@@ -790,6 +929,9 @@ export function createPostService(
|
|
|
790
929
|
quoteText: row.quoteText,
|
|
791
930
|
summary: row.summary,
|
|
792
931
|
rating: ensurePostRating(row.rating, Error),
|
|
932
|
+
previewImageKey: row.previewImageKey,
|
|
933
|
+
previewKind: row.previewKind,
|
|
934
|
+
previewProvider: row.previewProvider,
|
|
793
935
|
replyToId: row.replyToId,
|
|
794
936
|
threadId: row.threadId,
|
|
795
937
|
deletedAt: row.deletedAt,
|
|
@@ -981,6 +1123,24 @@ export function createPostService(
|
|
|
981
1123
|
return rows.map((row) => row.collectionId);
|
|
982
1124
|
}
|
|
983
1125
|
|
|
1126
|
+
async function getPostCollectionTimestamps(
|
|
1127
|
+
postId: string,
|
|
1128
|
+
): Promise<Map<string, number>> {
|
|
1129
|
+
const rows = await db
|
|
1130
|
+
.select({
|
|
1131
|
+
collectionId: postCollections.collectionId,
|
|
1132
|
+
createdAt: postCollections.createdAt,
|
|
1133
|
+
})
|
|
1134
|
+
.from(postCollections)
|
|
1135
|
+
.where(
|
|
1136
|
+
and(
|
|
1137
|
+
eq(postCollections.siteId, siteId),
|
|
1138
|
+
eq(postCollections.postId, postId),
|
|
1139
|
+
),
|
|
1140
|
+
);
|
|
1141
|
+
return new Map(rows.map((r) => [r.collectionId, r.createdAt]));
|
|
1142
|
+
}
|
|
1143
|
+
|
|
984
1144
|
function buildRollbackUpdate(
|
|
985
1145
|
post: Post,
|
|
986
1146
|
collectionIds: string[],
|
|
@@ -1018,6 +1178,20 @@ export function createPostService(
|
|
|
1018
1178
|
return hydratePost(result[0]);
|
|
1019
1179
|
},
|
|
1020
1180
|
|
|
1181
|
+
async getBodyContent(id) {
|
|
1182
|
+
const post = await this.getById(id);
|
|
1183
|
+
if (!post) return null;
|
|
1184
|
+
|
|
1185
|
+
return {
|
|
1186
|
+
id: post.id,
|
|
1187
|
+
type: "post",
|
|
1188
|
+
format: post.format,
|
|
1189
|
+
contentFormat: "markdown",
|
|
1190
|
+
content: post.body ? tiptapJsonToMarkdown(post.body) : "",
|
|
1191
|
+
chars: post.bodyText?.length ?? 0,
|
|
1192
|
+
};
|
|
1193
|
+
},
|
|
1194
|
+
|
|
1021
1195
|
async getBySlug(slug) {
|
|
1022
1196
|
const resolved = await resolvedPaths.resolve(slug);
|
|
1023
1197
|
if (!resolved || resolved.kind !== "slug" || !resolved.postId) {
|
|
@@ -1073,6 +1247,14 @@ export function createPostService(
|
|
|
1073
1247
|
ELSE 1
|
|
1074
1248
|
END`;
|
|
1075
1249
|
|
|
1250
|
+
// NULL-sort order differs between dialects: SQLite puts NULLs last for
|
|
1251
|
+
// DESC, Postgres puts them first. Wrap nullable sort keys in COALESCE
|
|
1252
|
+
// so pinned/featured posts land on top under both engines. Mirrors the
|
|
1253
|
+
// expressions used by `buildListCursorCondition` above.
|
|
1254
|
+
const pinnedSortExpr = sql<number>`coalesce(${posts.pinnedAt}, -1)`;
|
|
1255
|
+
const featuredSortExpr = sql<number>`coalesce(${posts.featuredAt}, -1)`;
|
|
1256
|
+
const sortTimestampSortExpr = sql<number>`coalesce(${sortTimestamp}, -1)`;
|
|
1257
|
+
|
|
1076
1258
|
const baseQuery = db
|
|
1077
1259
|
.select()
|
|
1078
1260
|
.from(posts)
|
|
@@ -1082,29 +1264,31 @@ export function createPostService(
|
|
|
1082
1264
|
let query =
|
|
1083
1265
|
filters.featured || filters.sortOrder === undefined
|
|
1084
1266
|
? baseQuery.orderBy(
|
|
1085
|
-
desc(
|
|
1086
|
-
filters.featured
|
|
1267
|
+
desc(pinnedSortExpr),
|
|
1268
|
+
filters.featured
|
|
1269
|
+
? desc(featuredSortExpr)
|
|
1270
|
+
: desc(sortTimestampSortExpr),
|
|
1087
1271
|
desc(posts.id),
|
|
1088
1272
|
)
|
|
1089
1273
|
: filters.sortOrder === "oldest"
|
|
1090
1274
|
? baseQuery.orderBy(
|
|
1091
|
-
desc(
|
|
1092
|
-
asc(
|
|
1275
|
+
desc(pinnedSortExpr),
|
|
1276
|
+
asc(sortTimestampSortExpr),
|
|
1093
1277
|
asc(posts.id),
|
|
1094
1278
|
)
|
|
1095
1279
|
: filters.sortOrder === "rating_desc"
|
|
1096
1280
|
? baseQuery.orderBy(
|
|
1097
|
-
desc(
|
|
1281
|
+
desc(pinnedSortExpr),
|
|
1098
1282
|
desc(ratingPresence),
|
|
1099
1283
|
desc(posts.rating),
|
|
1100
|
-
desc(
|
|
1284
|
+
desc(sortTimestampSortExpr),
|
|
1101
1285
|
desc(posts.id),
|
|
1102
1286
|
)
|
|
1103
1287
|
: baseQuery.orderBy(
|
|
1104
|
-
desc(
|
|
1288
|
+
desc(pinnedSortExpr),
|
|
1105
1289
|
desc(ratingPresence),
|
|
1106
1290
|
asc(posts.rating),
|
|
1107
|
-
desc(
|
|
1291
|
+
desc(sortTimestampSortExpr),
|
|
1108
1292
|
desc(posts.id),
|
|
1109
1293
|
);
|
|
1110
1294
|
|
|
@@ -1120,7 +1304,7 @@ export function createPostService(
|
|
|
1120
1304
|
const conditions = buildFilterConditions(filters);
|
|
1121
1305
|
|
|
1122
1306
|
const result = await db
|
|
1123
|
-
.select({ count: sql<number>`count(*)`.as("count") })
|
|
1307
|
+
.select({ count: sql<number>`CAST(count(*) AS INTEGER)`.as("count") })
|
|
1124
1308
|
.from(posts)
|
|
1125
1309
|
.where(conditions.length > 0 ? and(...conditions) : undefined);
|
|
1126
1310
|
|
|
@@ -1153,7 +1337,7 @@ export function createPostService(
|
|
|
1153
1337
|
return db
|
|
1154
1338
|
.select({
|
|
1155
1339
|
yearMonth: publishedYearMonthExpr.as("year_month"),
|
|
1156
|
-
count: sql<number>`count(*)`.as("count"),
|
|
1340
|
+
count: sql<number>`CAST(count(*) AS INTEGER)`.as("count"),
|
|
1157
1341
|
})
|
|
1158
1342
|
.from(posts)
|
|
1159
1343
|
.where(conditions.length > 0 ? and(...conditions) : undefined)
|
|
@@ -1173,22 +1357,31 @@ export function createPostService(
|
|
|
1173
1357
|
: undefined;
|
|
1174
1358
|
const rating = ensurePostRating(data.rating);
|
|
1175
1359
|
|
|
1360
|
+
const rawBody = data.bodyMarkdown
|
|
1361
|
+
? markdownToTiptapJson(data.bodyMarkdown)
|
|
1362
|
+
: (data.body ?? null);
|
|
1363
|
+
const body = rawBody ? trimTiptapBody(rawBody) : null;
|
|
1364
|
+
const title = data.title?.trim() || null;
|
|
1365
|
+
const quoteText = data.quoteText?.trim() || null;
|
|
1366
|
+
const url = data.url?.trim() || null;
|
|
1367
|
+
|
|
1176
1368
|
assertPostFormatShape({
|
|
1177
1369
|
format,
|
|
1178
|
-
title
|
|
1179
|
-
|
|
1180
|
-
|
|
1370
|
+
title,
|
|
1371
|
+
body,
|
|
1372
|
+
url,
|
|
1373
|
+
quoteText,
|
|
1374
|
+
hasAttachments: data.attachments
|
|
1375
|
+
? data.attachments.length > 0
|
|
1376
|
+
: undefined,
|
|
1181
1377
|
});
|
|
1182
1378
|
|
|
1183
|
-
const body = data.bodyMarkdown
|
|
1184
|
-
? markdownToTiptapJson(data.bodyMarkdown)
|
|
1185
|
-
: (data.body ?? null);
|
|
1186
1379
|
const bodyHtml = body ? renderTiptapJson(body) : null;
|
|
1187
1380
|
const bodyText = body ? extractBodyText(body) : null;
|
|
1188
1381
|
|
|
1189
1382
|
// Generate summary for titled notes with body content
|
|
1190
1383
|
let summary: string | null = null;
|
|
1191
|
-
if (format === "note" &&
|
|
1384
|
+
if (format === "note" && title && body && summaryConfig) {
|
|
1192
1385
|
summary = extractSummary(
|
|
1193
1386
|
body,
|
|
1194
1387
|
summaryConfig.maxParagraphs,
|
|
@@ -1201,18 +1394,45 @@ export function createPostService(
|
|
|
1201
1394
|
let status: Status = requestedStatus ?? "published";
|
|
1202
1395
|
let visibility: Visibility | null = requestedVisibility ?? "public";
|
|
1203
1396
|
|
|
1397
|
+
// Collapse the two-input (`flag` + `flagAt`) pair the DTO exposes
|
|
1398
|
+
// into the single timestamp form the DB column stores. Explicit
|
|
1399
|
+
// `*At` wins over the boolean shorthand when both are set.
|
|
1400
|
+
const resolvedPinnedAt =
|
|
1401
|
+
data.pinnedAt !== undefined
|
|
1402
|
+
? data.pinnedAt
|
|
1403
|
+
: data.pinned !== undefined
|
|
1404
|
+
? data.pinned
|
|
1405
|
+
? timestamp
|
|
1406
|
+
: null
|
|
1407
|
+
: null;
|
|
1408
|
+
const resolvedFeaturedAt =
|
|
1409
|
+
data.featuredAt !== undefined
|
|
1410
|
+
? data.featuredAt
|
|
1411
|
+
: data.featured !== undefined
|
|
1412
|
+
? data.featured
|
|
1413
|
+
? timestamp
|
|
1414
|
+
: null
|
|
1415
|
+
: null;
|
|
1416
|
+
|
|
1204
1417
|
if (data.replyToId) {
|
|
1205
1418
|
const parent = await this.getById(data.replyToId);
|
|
1206
1419
|
if (!parent) {
|
|
1207
1420
|
throw new NotFoundError("Parent post");
|
|
1208
1421
|
}
|
|
1209
1422
|
|
|
1210
|
-
if (
|
|
1423
|
+
if (resolvedPinnedAt !== null) {
|
|
1211
1424
|
throw new ConflictError(
|
|
1212
1425
|
"Cannot pin a thread reply. Pin the root post instead.",
|
|
1213
1426
|
);
|
|
1214
1427
|
}
|
|
1215
1428
|
|
|
1429
|
+
const lastLivePostId = await getLastLivePostIdInThread(parent.threadId);
|
|
1430
|
+
if (lastLivePostId && lastLivePostId !== parent.id) {
|
|
1431
|
+
throw new ConflictError(
|
|
1432
|
+
"This post is no longer the end of the thread. Reply to the latest post instead.",
|
|
1433
|
+
);
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1216
1436
|
threadId = parent.threadId;
|
|
1217
1437
|
|
|
1218
1438
|
// Replies inherit visibility from the root at read time.
|
|
@@ -1236,6 +1456,14 @@ export function createPostService(
|
|
|
1236
1456
|
let slug: string;
|
|
1237
1457
|
let aliasPath: string | null = null;
|
|
1238
1458
|
|
|
1459
|
+
// Quote `title` stores source attribution (e.g. author name), not a real
|
|
1460
|
+
// title — deriving the slug from it would produce URLs like `/basho`
|
|
1461
|
+
// that read as "a page about Basho" and collide across every quote from
|
|
1462
|
+
// the same source. Fall through to random IDs instead; users who want a
|
|
1463
|
+
// readable slug can still set one explicitly.
|
|
1464
|
+
const titleForSlug =
|
|
1465
|
+
format === "quote" ? undefined : (title ?? undefined);
|
|
1466
|
+
|
|
1239
1467
|
if (data.path) {
|
|
1240
1468
|
const normalized = normalizePath(data.path);
|
|
1241
1469
|
if (isValidSlug(normalized)) {
|
|
@@ -1250,7 +1478,7 @@ export function createPostService(
|
|
|
1250
1478
|
const slugified = slugify(normalized);
|
|
1251
1479
|
slug = await generatePostSlug({
|
|
1252
1480
|
slug: slugified || undefined,
|
|
1253
|
-
title:
|
|
1481
|
+
title: titleForSlug,
|
|
1254
1482
|
idLength: config.slugIdLength,
|
|
1255
1483
|
isAvailable: isSlugAvailable,
|
|
1256
1484
|
});
|
|
@@ -1263,13 +1491,61 @@ export function createPostService(
|
|
|
1263
1491
|
} else {
|
|
1264
1492
|
slug = await generatePostSlug({
|
|
1265
1493
|
slug: data.slug,
|
|
1266
|
-
title:
|
|
1494
|
+
title: titleForSlug,
|
|
1267
1495
|
idLength: config.slugIdLength,
|
|
1268
1496
|
isAvailable: isSlugAvailable,
|
|
1269
1497
|
});
|
|
1270
1498
|
}
|
|
1271
1499
|
|
|
1272
|
-
|
|
1500
|
+
// When structured `collectionEntries` are provided (Hugo import path),
|
|
1501
|
+
// they win over the bare `collectionIds` slug list and carry
|
|
1502
|
+
// `createdAt` / `position` / `pinnedAt` per row. Otherwise fall back to
|
|
1503
|
+
// the simple list and derive those fields from sensible defaults.
|
|
1504
|
+
const hasCollectionEntries =
|
|
1505
|
+
data.collectionEntries !== undefined &&
|
|
1506
|
+
data.collectionEntries.length > 0;
|
|
1507
|
+
const collectionInsertRows: {
|
|
1508
|
+
siteId: string;
|
|
1509
|
+
postId: string;
|
|
1510
|
+
collectionId: string;
|
|
1511
|
+
createdAt: number;
|
|
1512
|
+
position: number;
|
|
1513
|
+
pinnedAt: number | null;
|
|
1514
|
+
}[] = hasCollectionEntries
|
|
1515
|
+
? (() => {
|
|
1516
|
+
const seen = new Set<string>();
|
|
1517
|
+
const rows: {
|
|
1518
|
+
siteId: string;
|
|
1519
|
+
postId: string;
|
|
1520
|
+
collectionId: string;
|
|
1521
|
+
createdAt: number;
|
|
1522
|
+
position: number;
|
|
1523
|
+
pinnedAt: number | null;
|
|
1524
|
+
}[] = [];
|
|
1525
|
+
let fallbackPosition = 0;
|
|
1526
|
+
for (const entry of data.collectionEntries ?? []) {
|
|
1527
|
+
if (seen.has(entry.collectionId)) continue;
|
|
1528
|
+
seen.add(entry.collectionId);
|
|
1529
|
+
rows.push({
|
|
1530
|
+
siteId,
|
|
1531
|
+
postId: id,
|
|
1532
|
+
collectionId: entry.collectionId,
|
|
1533
|
+
createdAt: entry.createdAt ?? timestamp,
|
|
1534
|
+
position: entry.position ?? fallbackPosition,
|
|
1535
|
+
pinnedAt: entry.pinnedAt ?? null,
|
|
1536
|
+
});
|
|
1537
|
+
fallbackPosition++;
|
|
1538
|
+
}
|
|
1539
|
+
return rows;
|
|
1540
|
+
})()
|
|
1541
|
+
: [...new Set(data.collectionIds ?? [])].map((collectionId, index) => ({
|
|
1542
|
+
siteId,
|
|
1543
|
+
postId: id,
|
|
1544
|
+
collectionId,
|
|
1545
|
+
createdAt: timestamp,
|
|
1546
|
+
position: index,
|
|
1547
|
+
pinnedAt: null,
|
|
1548
|
+
}));
|
|
1273
1549
|
|
|
1274
1550
|
try {
|
|
1275
1551
|
if (usesBatchWrites) {
|
|
@@ -1282,14 +1558,14 @@ export function createPostService(
|
|
|
1282
1558
|
format,
|
|
1283
1559
|
status,
|
|
1284
1560
|
visibility,
|
|
1285
|
-
pinnedAt:
|
|
1286
|
-
featuredAt:
|
|
1287
|
-
title
|
|
1288
|
-
url
|
|
1289
|
-
body
|
|
1561
|
+
pinnedAt: resolvedPinnedAt,
|
|
1562
|
+
featuredAt: resolvedFeaturedAt,
|
|
1563
|
+
title,
|
|
1564
|
+
url,
|
|
1565
|
+
body,
|
|
1290
1566
|
bodyHtml,
|
|
1291
1567
|
bodyText,
|
|
1292
|
-
quoteText
|
|
1568
|
+
quoteText,
|
|
1293
1569
|
summary,
|
|
1294
1570
|
rating,
|
|
1295
1571
|
replyToId: data.replyToId ?? null,
|
|
@@ -1333,16 +1609,9 @@ export function createPostService(
|
|
|
1333
1609
|
);
|
|
1334
1610
|
}
|
|
1335
1611
|
|
|
1336
|
-
if (
|
|
1612
|
+
if (collectionInsertRows.length > 0) {
|
|
1337
1613
|
writeQueries.push(
|
|
1338
|
-
db.insert(postCollections).values(
|
|
1339
|
-
collectionIds.map((collectionId) => ({
|
|
1340
|
-
siteId,
|
|
1341
|
-
postId: id,
|
|
1342
|
-
collectionId,
|
|
1343
|
-
createdAt: timestamp,
|
|
1344
|
-
})),
|
|
1345
|
-
),
|
|
1614
|
+
db.insert(postCollections).values(collectionInsertRows),
|
|
1346
1615
|
);
|
|
1347
1616
|
}
|
|
1348
1617
|
|
|
@@ -1360,14 +1629,14 @@ export function createPostService(
|
|
|
1360
1629
|
format,
|
|
1361
1630
|
status,
|
|
1362
1631
|
visibility,
|
|
1363
|
-
pinnedAt:
|
|
1364
|
-
featuredAt:
|
|
1365
|
-
title
|
|
1366
|
-
url
|
|
1367
|
-
body
|
|
1632
|
+
pinnedAt: resolvedPinnedAt,
|
|
1633
|
+
featuredAt: resolvedFeaturedAt,
|
|
1634
|
+
title,
|
|
1635
|
+
url,
|
|
1636
|
+
body,
|
|
1368
1637
|
bodyHtml,
|
|
1369
1638
|
bodyText,
|
|
1370
|
-
quoteText
|
|
1639
|
+
quoteText,
|
|
1371
1640
|
summary,
|
|
1372
1641
|
rating,
|
|
1373
1642
|
replyToId: data.replyToId ?? null,
|
|
@@ -1406,15 +1675,8 @@ export function createPostService(
|
|
|
1406
1675
|
});
|
|
1407
1676
|
}
|
|
1408
1677
|
|
|
1409
|
-
if (
|
|
1410
|
-
await tx.insert(postCollections).values(
|
|
1411
|
-
collectionIds.map((collectionId) => ({
|
|
1412
|
-
siteId,
|
|
1413
|
-
postId: id,
|
|
1414
|
-
collectionId,
|
|
1415
|
-
createdAt: timestamp,
|
|
1416
|
-
})),
|
|
1417
|
-
);
|
|
1678
|
+
if (collectionInsertRows.length > 0) {
|
|
1679
|
+
await tx.insert(postCollections).values(collectionInsertRows);
|
|
1418
1680
|
}
|
|
1419
1681
|
});
|
|
1420
1682
|
}
|
|
@@ -1434,7 +1696,7 @@ export function createPostService(
|
|
|
1434
1696
|
}
|
|
1435
1697
|
|
|
1436
1698
|
// Bump thread root's lastActivityAt when creating a published reply
|
|
1437
|
-
if (data.replyToId && status === "published") {
|
|
1699
|
+
if (data.replyToId && status === "published" && !data.quietReply) {
|
|
1438
1700
|
await recalculateThreadLastActivity(threadId);
|
|
1439
1701
|
}
|
|
1440
1702
|
|
|
@@ -1447,13 +1709,25 @@ export function createPostService(
|
|
|
1447
1709
|
await createAttachmentMediaIds(attachmentInputs, deps);
|
|
1448
1710
|
|
|
1449
1711
|
try {
|
|
1450
|
-
const post = await this.create(
|
|
1712
|
+
const post = await this.create(
|
|
1713
|
+
{ ...data, attachments: attachmentInputs },
|
|
1714
|
+
summaryConfig,
|
|
1715
|
+
);
|
|
1451
1716
|
|
|
1452
1717
|
try {
|
|
1453
1718
|
if (orderedMediaIds.length > 0) {
|
|
1454
1719
|
await deps.media.attachToPost(post.id, orderedMediaIds);
|
|
1455
1720
|
}
|
|
1456
1721
|
await applyAttachmentAltUpdates(attachmentInputs, deps);
|
|
1722
|
+
|
|
1723
|
+
// Best-effort: fetch and store link preview thumbnail
|
|
1724
|
+
await resolveAndStorePreview(
|
|
1725
|
+
post.id,
|
|
1726
|
+
post.format,
|
|
1727
|
+
post.url,
|
|
1728
|
+
deps.storage,
|
|
1729
|
+
);
|
|
1730
|
+
|
|
1457
1731
|
return post;
|
|
1458
1732
|
} catch (error) {
|
|
1459
1733
|
await deps.media.attachToPost(post.id, []).catch(() => undefined);
|
|
@@ -1470,6 +1744,47 @@ export function createPostService(
|
|
|
1470
1744
|
}
|
|
1471
1745
|
},
|
|
1472
1746
|
|
|
1747
|
+
async createThreadWithAttachments(items, deps, summaryConfig) {
|
|
1748
|
+
if (items.length < 2) {
|
|
1749
|
+
throw new ValidationError("A thread requires at least 2 posts.");
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1752
|
+
const created: Post[] = [];
|
|
1753
|
+
|
|
1754
|
+
for (let i = 0; i < items.length; i++) {
|
|
1755
|
+
const item = items[i];
|
|
1756
|
+
if (!item) continue;
|
|
1757
|
+
const { data, attachments } = item;
|
|
1758
|
+
const prevPost = created[i - 1];
|
|
1759
|
+
const postData: CreatePost = {
|
|
1760
|
+
...data,
|
|
1761
|
+
// Chain each post as a reply to the previous one (server-side chaining)
|
|
1762
|
+
replyToId: i === 0 ? data.replyToId : prevPost?.id,
|
|
1763
|
+
};
|
|
1764
|
+
|
|
1765
|
+
try {
|
|
1766
|
+
const post = await this.createWithAttachments(
|
|
1767
|
+
postData,
|
|
1768
|
+
attachments,
|
|
1769
|
+
deps,
|
|
1770
|
+
summaryConfig,
|
|
1771
|
+
);
|
|
1772
|
+
created.push(post);
|
|
1773
|
+
} catch (error) {
|
|
1774
|
+
// Rollback: delete all already-created posts in reverse order
|
|
1775
|
+
for (const p of [...created].reverse()) {
|
|
1776
|
+
await this.delete(p.id, {
|
|
1777
|
+
media: deps.media,
|
|
1778
|
+
storage: deps.storage,
|
|
1779
|
+
}).catch(() => undefined);
|
|
1780
|
+
}
|
|
1781
|
+
throw error;
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1784
|
+
|
|
1785
|
+
return created;
|
|
1786
|
+
},
|
|
1787
|
+
|
|
1473
1788
|
async update(id, data, summaryConfig) {
|
|
1474
1789
|
const existing = await this.getById(id);
|
|
1475
1790
|
if (!existing) return null;
|
|
@@ -1479,9 +1794,12 @@ export function createPostService(
|
|
|
1479
1794
|
data.format !== undefined
|
|
1480
1795
|
? ensurePostFormat(data.format)
|
|
1481
1796
|
: existing.format;
|
|
1482
|
-
const nextUrl =
|
|
1797
|
+
const nextUrl =
|
|
1798
|
+
data.url !== undefined ? data.url?.trim() || null : existing.url;
|
|
1483
1799
|
const nextQuoteText =
|
|
1484
|
-
data.quoteText !== undefined
|
|
1800
|
+
data.quoteText !== undefined
|
|
1801
|
+
? data.quoteText?.trim() || null
|
|
1802
|
+
: existing.quoteText;
|
|
1485
1803
|
const nextStatus =
|
|
1486
1804
|
data.status !== undefined
|
|
1487
1805
|
? ensurePostStatus(data.status)
|
|
@@ -1493,11 +1811,26 @@ export function createPostService(
|
|
|
1493
1811
|
const nextRating =
|
|
1494
1812
|
data.rating !== undefined ? ensurePostRating(data.rating) : undefined;
|
|
1495
1813
|
|
|
1814
|
+
const nextTitle =
|
|
1815
|
+
data.title !== undefined ? data.title?.trim() || null : existing.title;
|
|
1816
|
+
|
|
1496
1817
|
assertPostFormatShape({
|
|
1497
1818
|
format: nextFormat,
|
|
1498
|
-
title:
|
|
1819
|
+
title: nextTitle,
|
|
1820
|
+
body:
|
|
1821
|
+
data.body !== undefined || data.bodyMarkdown !== undefined
|
|
1822
|
+
? data.bodyMarkdown
|
|
1823
|
+
? markdownToTiptapJson(data.bodyMarkdown)
|
|
1824
|
+
: (data.body ?? null)
|
|
1825
|
+
: existing.body,
|
|
1499
1826
|
url: nextUrl,
|
|
1500
1827
|
quoteText: nextQuoteText,
|
|
1828
|
+
// During update we can't cheaply resolve final attachment state;
|
|
1829
|
+
// existing posts already passed creation validation, so we only
|
|
1830
|
+
// guard against clearing all text fields here.
|
|
1831
|
+
hasAttachments: data.attachments
|
|
1832
|
+
? data.attachments.length > 0
|
|
1833
|
+
: undefined,
|
|
1501
1834
|
});
|
|
1502
1835
|
assertDraftPublishedAt(nextStatus, data.publishedAt);
|
|
1503
1836
|
|
|
@@ -1520,19 +1853,27 @@ export function createPostService(
|
|
|
1520
1853
|
}
|
|
1521
1854
|
|
|
1522
1855
|
if (data.format !== undefined) updates.format = nextFormat;
|
|
1523
|
-
if (data.title !== undefined) updates.title =
|
|
1524
|
-
if (data.url !== undefined) updates.url =
|
|
1525
|
-
if (data.quoteText !== undefined) updates.quoteText =
|
|
1856
|
+
if (data.title !== undefined) updates.title = nextTitle;
|
|
1857
|
+
if (data.url !== undefined) updates.url = nextUrl;
|
|
1858
|
+
if (data.quoteText !== undefined) updates.quoteText = nextQuoteText;
|
|
1526
1859
|
if (data.rating !== undefined) updates.rating = nextRating;
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1860
|
+
// Prefer explicit timestamp when provided; fall back to boolean shorthand.
|
|
1861
|
+
if (data.pinnedAt !== undefined) {
|
|
1862
|
+
updates.pinnedAt = data.pinnedAt;
|
|
1863
|
+
} else if (data.pinned !== undefined) {
|
|
1864
|
+
updates.pinnedAt = data.pinned ? timestamp : null;
|
|
1865
|
+
}
|
|
1866
|
+
if (data.featuredAt !== undefined) {
|
|
1867
|
+
updates.featuredAt = data.featuredAt;
|
|
1868
|
+
} else if (data.featured !== undefined) {
|
|
1869
|
+
updates.featuredAt = data.featured ? timestamp : null;
|
|
1870
|
+
}
|
|
1531
1871
|
|
|
1532
1872
|
if (data.body !== undefined || data.bodyMarkdown !== undefined) {
|
|
1533
|
-
const
|
|
1873
|
+
const rawBody = data.bodyMarkdown
|
|
1534
1874
|
? markdownToTiptapJson(data.bodyMarkdown)
|
|
1535
1875
|
: (data.body ?? null);
|
|
1876
|
+
const normalizedBody = rawBody ? trimTiptapBody(rawBody) : null;
|
|
1536
1877
|
updates.body = normalizedBody;
|
|
1537
1878
|
updates.bodyHtml = normalizedBody
|
|
1538
1879
|
? renderTiptapJson(normalizedBody)
|
|
@@ -1545,7 +1886,7 @@ export function createPostService(
|
|
|
1545
1886
|
// Recompute summary when body, title, or format change
|
|
1546
1887
|
if (summaryConfig) {
|
|
1547
1888
|
const format = nextFormat;
|
|
1548
|
-
const title =
|
|
1889
|
+
const title = nextTitle;
|
|
1549
1890
|
const body =
|
|
1550
1891
|
data.bodyMarkdown !== undefined
|
|
1551
1892
|
? data.bodyMarkdown
|
|
@@ -1572,7 +1913,10 @@ export function createPostService(
|
|
|
1572
1913
|
"Cannot change visibility of a thread reply. Update the root post instead.",
|
|
1573
1914
|
);
|
|
1574
1915
|
}
|
|
1575
|
-
if (
|
|
1916
|
+
if (
|
|
1917
|
+
(data.pinnedAt !== undefined && data.pinnedAt !== null) ||
|
|
1918
|
+
data.pinned === true
|
|
1919
|
+
) {
|
|
1576
1920
|
throw new ConflictError(
|
|
1577
1921
|
"Cannot pin a thread reply. Pin the root post instead.",
|
|
1578
1922
|
);
|
|
@@ -1631,9 +1975,9 @@ export function createPostService(
|
|
|
1631
1975
|
}
|
|
1632
1976
|
|
|
1633
1977
|
// Complex case: cascade + update + collection sync atomically
|
|
1634
|
-
const
|
|
1635
|
-
? await
|
|
1636
|
-
:
|
|
1978
|
+
const existingCollectionTimestamps = needsCollectionSync
|
|
1979
|
+
? await getPostCollectionTimestamps(id)
|
|
1980
|
+
: new Map<string, number>();
|
|
1637
1981
|
let updateResult: (typeof posts.$inferSelect)[] | undefined;
|
|
1638
1982
|
|
|
1639
1983
|
if (usesBatchWrites) {
|
|
@@ -1688,38 +2032,30 @@ export function createPostService(
|
|
|
1688
2032
|
);
|
|
1689
2033
|
|
|
1690
2034
|
if (needsCollectionSync) {
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
if (removedIds.length > 0) {
|
|
1701
|
-
writeQueries.push(
|
|
1702
|
-
db
|
|
1703
|
-
.delete(postCollections)
|
|
1704
|
-
.where(
|
|
1705
|
-
and(
|
|
1706
|
-
eq(postCollections.siteId, siteId),
|
|
1707
|
-
eq(postCollections.postId, id),
|
|
1708
|
-
inArray(postCollections.collectionId, removedIds),
|
|
1709
|
-
),
|
|
2035
|
+
// Delete all and re-insert to preserve user-specified ordering
|
|
2036
|
+
writeQueries.push(
|
|
2037
|
+
db
|
|
2038
|
+
.delete(postCollections)
|
|
2039
|
+
.where(
|
|
2040
|
+
and(
|
|
2041
|
+
eq(postCollections.siteId, siteId),
|
|
2042
|
+
eq(postCollections.postId, id),
|
|
1710
2043
|
),
|
|
1711
|
-
|
|
1712
|
-
|
|
2044
|
+
),
|
|
2045
|
+
);
|
|
1713
2046
|
|
|
1714
|
-
if (
|
|
2047
|
+
if (nextCollectionIds.length > 0) {
|
|
1715
2048
|
const collectionTimestamp = now();
|
|
1716
2049
|
writeQueries.push(
|
|
1717
2050
|
db.insert(postCollections).values(
|
|
1718
|
-
|
|
2051
|
+
nextCollectionIds.map((collectionId, index) => ({
|
|
1719
2052
|
siteId,
|
|
1720
2053
|
postId: id,
|
|
1721
2054
|
collectionId,
|
|
1722
|
-
createdAt:
|
|
2055
|
+
createdAt:
|
|
2056
|
+
existingCollectionTimestamps.get(collectionId) ??
|
|
2057
|
+
collectionTimestamp,
|
|
2058
|
+
position: index,
|
|
1723
2059
|
})),
|
|
1724
2060
|
),
|
|
1725
2061
|
);
|
|
@@ -1779,35 +2115,27 @@ export function createPostService(
|
|
|
1779
2115
|
.returning();
|
|
1780
2116
|
|
|
1781
2117
|
if (needsCollectionSync) {
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
if (removedIds.length > 0) {
|
|
1792
|
-
await tx
|
|
1793
|
-
.delete(postCollections)
|
|
1794
|
-
.where(
|
|
1795
|
-
and(
|
|
1796
|
-
eq(postCollections.siteId, siteId),
|
|
1797
|
-
eq(postCollections.postId, id),
|
|
1798
|
-
inArray(postCollections.collectionId, removedIds),
|
|
1799
|
-
),
|
|
1800
|
-
);
|
|
1801
|
-
}
|
|
2118
|
+
// Delete all and re-insert to preserve user-specified ordering
|
|
2119
|
+
await tx
|
|
2120
|
+
.delete(postCollections)
|
|
2121
|
+
.where(
|
|
2122
|
+
and(
|
|
2123
|
+
eq(postCollections.siteId, siteId),
|
|
2124
|
+
eq(postCollections.postId, id),
|
|
2125
|
+
),
|
|
2126
|
+
);
|
|
1802
2127
|
|
|
1803
|
-
if (
|
|
2128
|
+
if (nextCollectionIds.length > 0) {
|
|
1804
2129
|
const collectionTimestamp = now();
|
|
1805
2130
|
await tx.insert(postCollections).values(
|
|
1806
|
-
|
|
2131
|
+
nextCollectionIds.map((collectionId, index) => ({
|
|
1807
2132
|
siteId,
|
|
1808
2133
|
postId: id,
|
|
1809
2134
|
collectionId,
|
|
1810
|
-
createdAt:
|
|
2135
|
+
createdAt:
|
|
2136
|
+
existingCollectionTimestamps.get(collectionId) ??
|
|
2137
|
+
collectionTimestamp,
|
|
2138
|
+
position: index,
|
|
1811
2139
|
})),
|
|
1812
2140
|
);
|
|
1813
2141
|
}
|
|
@@ -1824,7 +2152,32 @@ export function createPostService(
|
|
|
1824
2152
|
|
|
1825
2153
|
async updateWithAttachments(id, data, attachments, deps, summaryConfig) {
|
|
1826
2154
|
if (attachments === undefined) {
|
|
1827
|
-
|
|
2155
|
+
const existing = await this.getById(id);
|
|
2156
|
+
const post = await this.update(id, data, summaryConfig);
|
|
2157
|
+
if (post && existing) {
|
|
2158
|
+
const urlChanged =
|
|
2159
|
+
data.url !== undefined && data.url !== existing.url;
|
|
2160
|
+
const formatChanged =
|
|
2161
|
+
data.format !== undefined && data.format !== existing.format;
|
|
2162
|
+
if (urlChanged || formatChanged) {
|
|
2163
|
+
await deletePreviewImage(existing.previewImageKey, deps.storage);
|
|
2164
|
+
await db
|
|
2165
|
+
.update(posts)
|
|
2166
|
+
.set({
|
|
2167
|
+
previewImageKey: null,
|
|
2168
|
+
previewKind: null,
|
|
2169
|
+
previewProvider: null,
|
|
2170
|
+
})
|
|
2171
|
+
.where(and(eq(posts.siteId, siteId), eq(posts.id, post.id)));
|
|
2172
|
+
await resolveAndStorePreview(
|
|
2173
|
+
post.id,
|
|
2174
|
+
post.format,
|
|
2175
|
+
post.url,
|
|
2176
|
+
deps.storage,
|
|
2177
|
+
);
|
|
2178
|
+
}
|
|
2179
|
+
}
|
|
2180
|
+
return post;
|
|
1828
2181
|
}
|
|
1829
2182
|
|
|
1830
2183
|
const existingPost = await this.getById(id);
|
|
@@ -1862,17 +2215,41 @@ export function createPostService(
|
|
|
1862
2215
|
await applyAttachmentAltUpdates(attachments, deps);
|
|
1863
2216
|
|
|
1864
2217
|
const nextAttachmentIds = new Set(orderedMediaIds);
|
|
1865
|
-
const
|
|
1866
|
-
.filter(
|
|
1867
|
-
(attachment) =>
|
|
1868
|
-
attachment.mimeType === "text/x-tiptap+json" &&
|
|
1869
|
-
!nextAttachmentIds.has(attachment.id),
|
|
1870
|
-
)
|
|
2218
|
+
const removedAttachmentIds = existingAttachments
|
|
2219
|
+
.filter((attachment) => !nextAttachmentIds.has(attachment.id))
|
|
1871
2220
|
.map((attachment) => attachment.id);
|
|
1872
2221
|
await deps.media
|
|
1873
|
-
.deleteByIds(
|
|
2222
|
+
.deleteByIds(removedAttachmentIds, deps.storage)
|
|
1874
2223
|
.catch(() => undefined);
|
|
1875
2224
|
|
|
2225
|
+
// Best-effort: update link preview when URL changes
|
|
2226
|
+
const urlChanged =
|
|
2227
|
+
data.url !== undefined && data.url !== existingPost.url;
|
|
2228
|
+
const formatChanged =
|
|
2229
|
+
data.format !== undefined && data.format !== existingPost.format;
|
|
2230
|
+
if (urlChanged || formatChanged) {
|
|
2231
|
+
// Clean up old preview image
|
|
2232
|
+
await deletePreviewImage(existingPost.previewImageKey, deps.storage);
|
|
2233
|
+
|
|
2234
|
+
// Clear preview fields first (may be re-set by resolveAndStorePreview)
|
|
2235
|
+
await db
|
|
2236
|
+
.update(posts)
|
|
2237
|
+
.set({
|
|
2238
|
+
previewImageKey: null,
|
|
2239
|
+
previewKind: null,
|
|
2240
|
+
previewProvider: null,
|
|
2241
|
+
})
|
|
2242
|
+
.where(and(eq(posts.siteId, siteId), eq(posts.id, post.id)));
|
|
2243
|
+
|
|
2244
|
+
// Fetch new preview if applicable
|
|
2245
|
+
await resolveAndStorePreview(
|
|
2246
|
+
post.id,
|
|
2247
|
+
post.format,
|
|
2248
|
+
post.url,
|
|
2249
|
+
deps.storage,
|
|
2250
|
+
);
|
|
2251
|
+
}
|
|
2252
|
+
|
|
1876
2253
|
return post;
|
|
1877
2254
|
} catch (error) {
|
|
1878
2255
|
if (replacedAttachments) {
|
|
@@ -1900,17 +2277,16 @@ export function createPostService(
|
|
|
1900
2277
|
const existing = await this.getById(id);
|
|
1901
2278
|
if (!existing) return false;
|
|
1902
2279
|
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
const thread = await this.getThread(id);
|
|
1908
|
-
postIds = thread.map((p) => p.id);
|
|
1909
|
-
} else {
|
|
1910
|
-
postIds = [id];
|
|
1911
|
-
}
|
|
2280
|
+
const isRoot = !isThreadReply(existing);
|
|
2281
|
+
const affectedPosts: Post[] = isRoot
|
|
2282
|
+
? await this.getThread(id)
|
|
2283
|
+
: [existing];
|
|
1912
2284
|
|
|
1913
|
-
|
|
2285
|
+
// Clean up media and preview images
|
|
2286
|
+
if (deps?.media) {
|
|
2287
|
+
const mediaMap = await deps.media.getByPostIds(
|
|
2288
|
+
affectedPosts.map((p) => p.id),
|
|
2289
|
+
);
|
|
1914
2290
|
const allMedia = [...mediaMap.values()].flat();
|
|
1915
2291
|
if (allMedia.length > 0) {
|
|
1916
2292
|
await deps.media.deleteByIds(
|
|
@@ -1918,18 +2294,20 @@ export function createPostService(
|
|
|
1918
2294
|
deps.storage,
|
|
1919
2295
|
);
|
|
1920
2296
|
}
|
|
2297
|
+
|
|
2298
|
+
for (const p of affectedPosts) {
|
|
2299
|
+
await deletePreviewImage(p.previewImageKey, deps.storage);
|
|
2300
|
+
}
|
|
1921
2301
|
}
|
|
1922
2302
|
|
|
1923
2303
|
const timestamp = now();
|
|
1924
2304
|
|
|
1925
|
-
|
|
1926
|
-
if (!isThreadReply(existing)) {
|
|
2305
|
+
if (isRoot) {
|
|
1927
2306
|
await db
|
|
1928
2307
|
.update(posts)
|
|
1929
2308
|
.set({ deletedAt: timestamp, updatedAt: timestamp })
|
|
1930
2309
|
.where(and(eq(posts.siteId, siteId), eq(posts.threadId, id)));
|
|
1931
2310
|
} else {
|
|
1932
|
-
// Soft-delete the single reply
|
|
1933
2311
|
await db
|
|
1934
2312
|
.update(posts)
|
|
1935
2313
|
.set({ deletedAt: timestamp, updatedAt: timestamp })
|
|
@@ -1940,6 +2318,23 @@ export function createPostService(
|
|
|
1940
2318
|
return true;
|
|
1941
2319
|
},
|
|
1942
2320
|
|
|
2321
|
+
async deleteThreadDraft(id, deps) {
|
|
2322
|
+
const deleted = await this.delete(id, deps);
|
|
2323
|
+
if (!deleted) return false;
|
|
2324
|
+
|
|
2325
|
+
// Release path_registry entries for all posts in the thread so slugs
|
|
2326
|
+
// can be reused by the replacement thread.
|
|
2327
|
+
const threadRows = await db
|
|
2328
|
+
.select({ id: posts.id })
|
|
2329
|
+
.from(posts)
|
|
2330
|
+
.where(and(eq(posts.siteId, siteId), eq(posts.threadId, id)));
|
|
2331
|
+
for (const row of threadRows) {
|
|
2332
|
+
await resolvedPaths.deleteByPostId(row.id);
|
|
2333
|
+
}
|
|
2334
|
+
|
|
2335
|
+
return true;
|
|
2336
|
+
},
|
|
2337
|
+
|
|
1943
2338
|
async getThread(rootId) {
|
|
1944
2339
|
const rows = await db
|
|
1945
2340
|
.select()
|
|
@@ -2029,7 +2424,7 @@ export function createPostService(
|
|
|
2029
2424
|
const rows = await db
|
|
2030
2425
|
.select({
|
|
2031
2426
|
threadId: posts.threadId,
|
|
2032
|
-
count: sql<number>`count(*)`.as("count"),
|
|
2427
|
+
count: sql<number>`CAST(count(*) AS INTEGER)`.as("count"),
|
|
2033
2428
|
})
|
|
2034
2429
|
.from(posts)
|
|
2035
2430
|
.where(
|
|
@@ -2058,10 +2453,10 @@ export function createPostService(
|
|
|
2058
2453
|
id: posts.id,
|
|
2059
2454
|
threadId: posts.threadId,
|
|
2060
2455
|
createdAt: posts.createdAt,
|
|
2061
|
-
previewRank: sql<number>`ROW_NUMBER() OVER (
|
|
2456
|
+
previewRank: sql<number>`CAST(ROW_NUMBER() OVER (
|
|
2062
2457
|
PARTITION BY ${posts.threadId}
|
|
2063
2458
|
ORDER BY ${posts.createdAt}, ${posts.id}
|
|
2064
|
-
)`.as("preview_rank"),
|
|
2459
|
+
) AS INTEGER)`.as("preview_rank"),
|
|
2065
2460
|
})
|
|
2066
2461
|
.from(posts)
|
|
2067
2462
|
.where(
|
|
@@ -2115,14 +2510,17 @@ export function createPostService(
|
|
|
2115
2510
|
.select({
|
|
2116
2511
|
id: posts.id,
|
|
2117
2512
|
threadId: posts.threadId,
|
|
2118
|
-
|
|
2119
|
-
|
|
2513
|
+
firstReplyRank: sql<number>`CAST(ROW_NUMBER() OVER (
|
|
2514
|
+
PARTITION BY ${posts.threadId}
|
|
2515
|
+
ORDER BY ${posts.createdAt}, ${posts.id}
|
|
2516
|
+
) AS INTEGER)`.as("first_reply_rank"),
|
|
2517
|
+
latestReplyRank: sql<number>`CAST(ROW_NUMBER() OVER (
|
|
2120
2518
|
PARTITION BY ${posts.threadId}
|
|
2121
2519
|
ORDER BY ${posts.createdAt} DESC, ${posts.id} DESC
|
|
2122
|
-
)`.as("
|
|
2123
|
-
totalReplyCount: sql<number>`COUNT(*) OVER (
|
|
2520
|
+
) AS INTEGER)`.as("latest_reply_rank"),
|
|
2521
|
+
totalReplyCount: sql<number>`CAST(COUNT(*) OVER (
|
|
2124
2522
|
PARTITION BY ${posts.threadId}
|
|
2125
|
-
)`.as("total_reply_count"),
|
|
2523
|
+
) AS INTEGER)`.as("total_reply_count"),
|
|
2126
2524
|
})
|
|
2127
2525
|
.from(posts)
|
|
2128
2526
|
.where(
|
|
@@ -2136,41 +2534,72 @@ export function createPostService(
|
|
|
2136
2534
|
)
|
|
2137
2535
|
.as("ranked_replies");
|
|
2138
2536
|
|
|
2139
|
-
const
|
|
2537
|
+
const contextRows = await db
|
|
2140
2538
|
.select({
|
|
2141
2539
|
threadId: rankedReplies.threadId,
|
|
2142
|
-
|
|
2143
|
-
|
|
2540
|
+
id: rankedReplies.id,
|
|
2541
|
+
firstReplyRank: rankedReplies.firstReplyRank,
|
|
2542
|
+
latestReplyRank: rankedReplies.latestReplyRank,
|
|
2144
2543
|
totalReplyCount: rankedReplies.totalReplyCount,
|
|
2145
2544
|
})
|
|
2146
2545
|
.from(rankedReplies)
|
|
2147
|
-
.where(
|
|
2546
|
+
.where(
|
|
2547
|
+
or(
|
|
2548
|
+
eq(rankedReplies.firstReplyRank, 1),
|
|
2549
|
+
lte(rankedReplies.latestReplyRank, 2),
|
|
2550
|
+
),
|
|
2551
|
+
);
|
|
2552
|
+
|
|
2553
|
+
const hydratedPosts = await hydratePostsById(
|
|
2554
|
+
contextRows.map((row) => row.id),
|
|
2555
|
+
);
|
|
2148
2556
|
|
|
2149
|
-
const
|
|
2150
|
-
|
|
2557
|
+
const contextByThreadId = new Map<
|
|
2558
|
+
string,
|
|
2559
|
+
{
|
|
2560
|
+
secondReplyId: string | null;
|
|
2561
|
+
penultimateReplyId: string | null;
|
|
2562
|
+
latestReplyId: string | null;
|
|
2563
|
+
totalReplyCount: number;
|
|
2564
|
+
}
|
|
2565
|
+
>();
|
|
2566
|
+
for (const row of contextRows) {
|
|
2567
|
+
const existing = contextByThreadId.get(row.threadId) ?? {
|
|
2568
|
+
secondReplyId: null,
|
|
2569
|
+
penultimateReplyId: null,
|
|
2570
|
+
latestReplyId: null,
|
|
2571
|
+
totalReplyCount: row.totalReplyCount,
|
|
2572
|
+
};
|
|
2151
2573
|
|
|
2152
|
-
if (row.
|
|
2153
|
-
|
|
2574
|
+
if (row.firstReplyRank === 1) {
|
|
2575
|
+
existing.secondReplyId = row.id;
|
|
2576
|
+
}
|
|
2577
|
+
if (row.latestReplyRank === 2) {
|
|
2578
|
+
existing.penultimateReplyId = row.id;
|
|
2579
|
+
}
|
|
2580
|
+
if (row.latestReplyRank === 1) {
|
|
2581
|
+
existing.latestReplyId = row.id;
|
|
2154
2582
|
}
|
|
2155
2583
|
|
|
2156
|
-
|
|
2157
|
-
}
|
|
2158
|
-
const hydratedPosts = await hydratePostsById(relatedPostIds);
|
|
2584
|
+
contextByThreadId.set(row.threadId, existing);
|
|
2585
|
+
}
|
|
2159
2586
|
|
|
2160
2587
|
const result = new Map<string, ThreadTimelineContext>();
|
|
2161
|
-
for (const
|
|
2162
|
-
|
|
2163
|
-
if (!latestReply) continue;
|
|
2588
|
+
for (const [threadId, context] of contextByThreadId) {
|
|
2589
|
+
if (!context.latestReplyId) continue;
|
|
2164
2590
|
|
|
2165
|
-
const
|
|
2166
|
-
|
|
2167
|
-
? (hydratedPosts.get(row.latestReplyToId) ?? null)
|
|
2168
|
-
: null;
|
|
2591
|
+
const latestReply = hydratedPosts.get(context.latestReplyId);
|
|
2592
|
+
if (!latestReply) continue;
|
|
2169
2593
|
|
|
2170
|
-
result.set(
|
|
2594
|
+
result.set(threadId, {
|
|
2595
|
+
secondReply: context.secondReplyId
|
|
2596
|
+
? (hydratedPosts.get(context.secondReplyId) ?? null)
|
|
2597
|
+
: null,
|
|
2598
|
+
penultimateReply: context.penultimateReplyId
|
|
2599
|
+
? (hydratedPosts.get(context.penultimateReplyId) ?? null)
|
|
2600
|
+
: null,
|
|
2171
2601
|
latestReply,
|
|
2172
|
-
|
|
2173
|
-
totalReplyCount: row.totalReplyCount,
|
|
2602
|
+
totalReplyCount: context.totalReplyCount,
|
|
2174
2603
|
});
|
|
2175
2604
|
}
|
|
2176
2605
|
|
|
@@ -2185,7 +2614,10 @@ export function createPostService(
|
|
|
2185
2614
|
|
|
2186
2615
|
const rows = await db
|
|
2187
2616
|
.select({
|
|
2188
|
-
count:
|
|
2617
|
+
count:
|
|
2618
|
+
sql<number>`CAST(count(distinct ${posts.threadId}) AS INTEGER)`.as(
|
|
2619
|
+
"count",
|
|
2620
|
+
),
|
|
2189
2621
|
})
|
|
2190
2622
|
.from(posts)
|
|
2191
2623
|
.where(and(...conditions));
|
|
@@ -2241,7 +2673,10 @@ export function createPostService(
|
|
|
2241
2673
|
|
|
2242
2674
|
const rows = await db
|
|
2243
2675
|
.select({
|
|
2244
|
-
count:
|
|
2676
|
+
count:
|
|
2677
|
+
sql<number>`CAST(count(distinct ${posts.threadId}) AS INTEGER)`.as(
|
|
2678
|
+
"count",
|
|
2679
|
+
),
|
|
2245
2680
|
})
|
|
2246
2681
|
.from(posts)
|
|
2247
2682
|
.innerJoin(
|
|
@@ -2272,10 +2707,10 @@ export function createPostService(
|
|
|
2272
2707
|
buildCollectionMembershipCondition(collectionIds),
|
|
2273
2708
|
];
|
|
2274
2709
|
const sortOrder = options.sortOrder ?? "newest";
|
|
2275
|
-
const
|
|
2710
|
+
const publishedAt =
|
|
2276
2711
|
sortOrder === "oldest"
|
|
2277
|
-
? sql<number>`MIN(${
|
|
2278
|
-
: sql<number>`MAX(${
|
|
2712
|
+
? sql<number>`MIN(${posts.publishedAt})`.as("published_at")
|
|
2713
|
+
: sql<number>`MAX(${posts.publishedAt})`.as("published_at");
|
|
2279
2714
|
const ratingPresence = sql<number>`MAX(
|
|
2280
2715
|
CASE
|
|
2281
2716
|
WHEN ${posts.rating} IS NULL THEN 0
|
|
@@ -2286,10 +2721,16 @@ export function createPostService(
|
|
|
2286
2721
|
"rating_value",
|
|
2287
2722
|
);
|
|
2288
2723
|
|
|
2724
|
+
const collectionPinnedAt =
|
|
2725
|
+
sql<number>`MAX(coalesce(${postCollections.pinnedAt}, -1))`.as(
|
|
2726
|
+
"collection_pinned_at",
|
|
2727
|
+
);
|
|
2728
|
+
|
|
2289
2729
|
const baseQuery = db
|
|
2290
2730
|
.select({
|
|
2291
2731
|
threadId: posts.threadId,
|
|
2292
|
-
|
|
2732
|
+
publishedAt,
|
|
2733
|
+
collectionPinnedAt,
|
|
2293
2734
|
ratingPresence,
|
|
2294
2735
|
ratingValue,
|
|
2295
2736
|
})
|
|
@@ -2306,15 +2747,24 @@ export function createPostService(
|
|
|
2306
2747
|
|
|
2307
2748
|
let query =
|
|
2308
2749
|
sortOrder === "oldest"
|
|
2309
|
-
? baseQuery.orderBy(
|
|
2750
|
+
? baseQuery.orderBy(
|
|
2751
|
+
desc(collectionPinnedAt),
|
|
2752
|
+
asc(publishedAt),
|
|
2753
|
+
asc(posts.threadId),
|
|
2754
|
+
)
|
|
2310
2755
|
: sortOrder === "rating_desc"
|
|
2311
2756
|
? baseQuery.orderBy(
|
|
2757
|
+
desc(collectionPinnedAt),
|
|
2312
2758
|
desc(ratingPresence),
|
|
2313
2759
|
desc(ratingValue),
|
|
2314
|
-
desc(
|
|
2760
|
+
desc(publishedAt),
|
|
2315
2761
|
desc(posts.threadId),
|
|
2316
2762
|
)
|
|
2317
|
-
: baseQuery.orderBy(
|
|
2763
|
+
: baseQuery.orderBy(
|
|
2764
|
+
desc(collectionPinnedAt),
|
|
2765
|
+
desc(publishedAt),
|
|
2766
|
+
desc(posts.threadId),
|
|
2767
|
+
);
|
|
2318
2768
|
|
|
2319
2769
|
if (options.limit !== undefined) {
|
|
2320
2770
|
query = query.limit(options.limit) as typeof query;
|
|
@@ -2339,14 +2789,23 @@ export function createPostService(
|
|
|
2339
2789
|
...buildThreadRootPageConditions(options),
|
|
2340
2790
|
buildCollectionMembershipCondition(collectionIds),
|
|
2341
2791
|
];
|
|
2792
|
+
const publishedAt = sql<number>`MAX(${posts.publishedAt})`.as(
|
|
2793
|
+
"published_at",
|
|
2794
|
+
);
|
|
2342
2795
|
const collectedAt = sql<number>`MAX(${postCollections.createdAt})`.as(
|
|
2343
2796
|
"collected_at",
|
|
2344
2797
|
);
|
|
2798
|
+
const collectionPinnedAt =
|
|
2799
|
+
sql<number>`MAX(coalesce(${postCollections.pinnedAt}, -1))`.as(
|
|
2800
|
+
"collection_pinned_at",
|
|
2801
|
+
);
|
|
2345
2802
|
|
|
2346
2803
|
let query = db
|
|
2347
2804
|
.select({
|
|
2348
2805
|
threadId: posts.threadId,
|
|
2806
|
+
publishedAt,
|
|
2349
2807
|
collectedAt,
|
|
2808
|
+
collectionPinnedAt,
|
|
2350
2809
|
})
|
|
2351
2810
|
.from(posts)
|
|
2352
2811
|
.innerJoin(
|
|
@@ -2358,7 +2817,11 @@ export function createPostService(
|
|
|
2358
2817
|
)
|
|
2359
2818
|
.where(and(...conditions))
|
|
2360
2819
|
.groupBy(posts.threadId)
|
|
2361
|
-
.orderBy(
|
|
2820
|
+
.orderBy(
|
|
2821
|
+
desc(collectionPinnedAt),
|
|
2822
|
+
desc(publishedAt),
|
|
2823
|
+
desc(posts.threadId),
|
|
2824
|
+
);
|
|
2362
2825
|
|
|
2363
2826
|
if (options.limit !== undefined) {
|
|
2364
2827
|
query = query.limit(options.limit) as typeof query;
|