@mx-space/cli 0.14.2 → 0.15.1
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 +36 -12
- package/dist/{Lexical.dev-CtGmYDoh.mjs → Lexical.dev-BSvx45_E.mjs} +350 -402
- package/dist/{Lexical.node-8sRFmq2X.mjs → Lexical.node-kAh5GM2n.mjs} +2 -2
- package/dist/{Lexical.prod-D-VfeKDz.mjs → Lexical.prod-Bqwgb3ez.mjs} +1757 -1785
- package/dist/{LexicalClipboard.dev-CzQ15z6E.mjs → LexicalClipboard.dev-BWU3KXIa.mjs} +19 -15
- package/dist/{LexicalClipboard.node-D4gMGws6.mjs → LexicalClipboard.node-BygAdNdF.mjs} +2 -2
- package/dist/{LexicalClipboard.prod-CltkQpzS.mjs → LexicalClipboard.prod-BtMuKNyT.mjs} +11 -11
- package/dist/{LexicalCodeCore.dev-DkYBZRGB.mjs → LexicalCodeCore.dev-CD2xgvDs.mjs} +95 -89
- package/dist/{LexicalCodeCore.prod-COc3_QL8.mjs → LexicalCodeCore.prod-Bl6FFMc7.mjs} +146 -158
- package/dist/{LexicalDragon.dev-Dv5_WUae.mjs → LexicalDragon.dev-BwdYhm4K.mjs} +3 -3
- package/dist/{LexicalDragon.node-DgjrmWkd.mjs → LexicalDragon.node-CpJhkCVV.mjs} +2 -2
- package/dist/{LexicalDragon.prod-DnpbDe-_.mjs → LexicalDragon.prod-CvXmWwo1.mjs} +3 -3
- package/dist/{LexicalExtension.dev-JMH_6-OZ.mjs → LexicalExtension.dev-LcRLXI5Z.mjs} +33 -30
- package/dist/{LexicalExtension.node-BUozzx4t.mjs → LexicalExtension.node-BwJt2LXV.mjs} +2 -2
- package/dist/{LexicalExtension.prod-UiyDgmou.mjs → LexicalExtension.prod-lg8_33-H.mjs} +87 -93
- package/dist/{LexicalHeadless.dev-BfLGciiX.mjs → LexicalHeadless.dev-BeUbKBes.mjs} +2 -2
- package/dist/{LexicalHeadless.prod-CWF7edmQ.mjs → LexicalHeadless.prod-4gbIA8HM.mjs} +2 -2
- package/dist/{LexicalHtml.dev-D5EF5PGW.mjs → LexicalHtml.dev-BLdZWTCJ.mjs} +22 -16
- package/dist/{LexicalHtml.node-BYbzHNVe.mjs → LexicalHtml.node-BeuJMvvt.mjs} +2 -2
- package/dist/{LexicalHtml.prod-D_FfkvVD.mjs → LexicalHtml.prod-CHUyCocN.mjs} +6 -6
- package/dist/{LexicalLink.dev-D3Pgh6ik.mjs → LexicalLink.dev-DgsFRqw0.mjs} +41 -58
- package/dist/{LexicalLink.prod-XZoAYVYD.mjs → LexicalLink.prod-CkXkeKY2.mjs} +115 -138
- package/dist/{LexicalList.dev-B5d3ObhB.mjs → LexicalList.dev-OOms3kQi.mjs} +15 -47
- package/dist/{LexicalList.prod-OQDpVAEb.mjs → LexicalList.prod-DzMBeqHn.mjs} +208 -224
- package/dist/{LexicalMarkdown.dev-BkM8G3DG.mjs → LexicalMarkdown.dev-DJLKbZtD.mjs} +42 -30
- package/dist/{LexicalMarkdown.prod-DcVXCex6.mjs → LexicalMarkdown.prod-Cp21kDCU.mjs} +12 -8
- package/dist/{LexicalRichText.dev-DppD7IrA.mjs → LexicalRichText.dev-0rkAGjUf.mjs} +84 -86
- package/dist/{LexicalRichText.node-CPSn7dkg.mjs → LexicalRichText.node-Cd-BySPE.mjs} +8 -8
- package/dist/{LexicalRichText.prod-h7JPxFNp.mjs → LexicalRichText.prod-DTQiEr8C.mjs} +96 -100
- package/dist/{LexicalSelection.dev-CD0SLbxP.mjs → LexicalSelection.dev-Bi0VlUEv.mjs} +29 -25
- package/dist/{LexicalSelection.node-CGlJgUlZ.mjs → LexicalSelection.node-t2U46EF_.mjs} +2 -2
- package/dist/{LexicalSelection.prod-DbVZN5AV.mjs → LexicalSelection.prod-CrFG111g.mjs} +3 -3
- package/dist/{LexicalTable.dev-D0HU14UV.mjs → LexicalTable.dev-D-sFhO-o.mjs} +288 -154
- package/dist/{LexicalTable.prod-BcPNSrxP.mjs → LexicalTable.prod-DwYaa_dN.mjs} +689 -632
- package/dist/{LexicalUtils.dev-iKy_TrRT.mjs → LexicalUtils.dev-Q2q3-UDM.mjs} +9 -6
- package/dist/{LexicalUtils.node-CIgMS8cG.mjs → LexicalUtils.node-Dl7OvqJV.mjs} +2 -2
- package/dist/{LexicalUtils.prod-DIAsKJzu.mjs → LexicalUtils.prod-CBnq_iEV.mjs} +8 -7
- package/dist/bin/mxs.mjs +1 -1
- package/dist/esm-CNZ7kdTg.mjs +2 -0
- package/dist/{esm-CwXl1N0a.mjs → esm-ZXjJmLEC.mjs} +422 -391
- package/dist/index.d.mts +16 -16
- package/dist/index.mjs +1 -1
- package/dist/{mxs-iW9DgqSZ.mjs → mxs-DC9zR3f2.mjs} +2203 -2782
- package/dist/{rolldown-runtime-JspESFgx.mjs → rolldown-runtime-B4iAMlE-.mjs} +1 -1
- package/dist/src/bin/mxs.d.ts +2 -0
- package/dist/src/bin/mxs.js +310 -0
- package/dist/src/cli/ai/_poll.d.ts +12 -0
- package/dist/src/cli/ai/_poll.js +38 -0
- package/dist/src/cli/ai/_resolve.d.ts +8 -0
- package/dist/src/cli/ai/_resolve.js +27 -0
- package/dist/src/cli/ai/index.d.ts +127 -0
- package/dist/src/cli/ai/index.js +46 -0
- package/dist/src/cli/ai/insights/by-article.d.ts +10 -0
- package/dist/src/cli/ai/insights/by-article.js +21 -0
- package/dist/src/cli/ai/insights/delete.d.ts +7 -0
- package/dist/src/cli/ai/insights/delete.js +18 -0
- package/dist/src/cli/ai/insights/edit.d.ts +8 -0
- package/dist/src/cli/ai/insights/edit.js +54 -0
- package/dist/src/cli/ai/insights/get.d.ts +6 -0
- package/dist/src/cli/ai/insights/get.js +11 -0
- package/dist/src/cli/ai/insights/index.d.ts +30 -0
- package/dist/src/cli/ai/insights/index.js +17 -0
- package/dist/src/cli/ai/insights/list.d.ts +10 -0
- package/dist/src/cli/ai/insights/list.js +20 -0
- package/dist/src/cli/ai/insights/refresh.d.ts +10 -0
- package/dist/src/cli/ai/insights/refresh.js +25 -0
- package/dist/src/cli/ai/insights/translate.d.ts +10 -0
- package/dist/src/cli/ai/insights/translate.js +25 -0
- package/dist/src/cli/ai/overview/by-article.d.ts +7 -0
- package/dist/src/cli/ai/overview/by-article.js +15 -0
- package/dist/src/cli/ai/overview/index.d.ts +11 -0
- package/dist/src/cli/ai/overview/index.js +4 -0
- package/dist/src/cli/ai/overview/list.d.ts +10 -0
- package/dist/src/cli/ai/overview/list.js +20 -0
- package/dist/src/cli/ai/summary/by-article.d.ts +10 -0
- package/dist/src/cli/ai/summary/by-article.js +21 -0
- package/dist/src/cli/ai/summary/delete.d.ts +7 -0
- package/dist/src/cli/ai/summary/delete.js +18 -0
- package/dist/src/cli/ai/summary/edit.d.ts +8 -0
- package/dist/src/cli/ai/summary/edit.js +54 -0
- package/dist/src/cli/ai/summary/get.d.ts +6 -0
- package/dist/src/cli/ai/summary/get.js +11 -0
- package/dist/src/cli/ai/summary/index.d.ts +30 -0
- package/dist/src/cli/ai/summary/index.js +9 -0
- package/dist/src/cli/ai/summary/list.d.ts +10 -0
- package/dist/src/cli/ai/summary/list.js +20 -0
- package/dist/src/cli/ai/summary/regen.d.ts +10 -0
- package/dist/src/cli/ai/summary/regen.js +25 -0
- package/dist/src/cli/ai/summary/translate.d.ts +10 -0
- package/dist/src/cli/ai/summary/translate.js +25 -0
- package/dist/src/cli/ai/translate/by-article.d.ts +9 -0
- package/dist/src/cli/ai/translate/by-article.js +19 -0
- package/dist/src/cli/ai/translate/delete.d.ts +7 -0
- package/dist/src/cli/ai/translate/delete.js +18 -0
- package/dist/src/cli/ai/translate/edit.d.ts +8 -0
- package/dist/src/cli/ai/translate/edit.js +97 -0
- package/dist/src/cli/ai/translate/entries/delete.d.ts +7 -0
- package/dist/src/cli/ai/translate/entries/delete.js +18 -0
- package/dist/src/cli/ai/translate/entries/edit.d.ts +8 -0
- package/dist/src/cli/ai/translate/entries/edit.js +73 -0
- package/dist/src/cli/ai/translate/entries/generate.d.ts +7 -0
- package/dist/src/cli/ai/translate/entries/generate.js +15 -0
- package/dist/src/cli/ai/translate/entries/index.d.ts +17 -0
- package/dist/src/cli/ai/translate/entries/index.js +6 -0
- package/dist/src/cli/ai/translate/entries/list.d.ts +10 -0
- package/dist/src/cli/ai/translate/entries/list.js +20 -0
- package/dist/src/cli/ai/translate/get.d.ts +6 -0
- package/dist/src/cli/ai/translate/get.js +11 -0
- package/dist/src/cli/ai/translate/index.d.ts +40 -0
- package/dist/src/cli/ai/translate/index.js +19 -0
- package/dist/src/cli/ai/translate/languages.d.ts +7 -0
- package/dist/src/cli/ai/translate/languages.js +15 -0
- package/dist/src/cli/ai/translate/list.d.ts +9 -0
- package/dist/src/cli/ai/translate/list.js +19 -0
- package/dist/src/cli/ai/translate/run.d.ts +10 -0
- package/dist/src/cli/ai/translate/run.js +31 -0
- package/dist/src/cli/ai/tts/by-article.d.ts +7 -0
- package/dist/src/cli/ai/tts/by-article.js +15 -0
- package/dist/src/cli/ai/tts/delete.d.ts +7 -0
- package/dist/src/cli/ai/tts/delete.js +18 -0
- package/dist/src/cli/ai/tts/index.d.ts +22 -0
- package/dist/src/cli/ai/tts/index.js +7 -0
- package/dist/src/cli/ai/tts/list.d.ts +10 -0
- package/dist/src/cli/ai/tts/list.js +20 -0
- package/dist/src/cli/ai/tts/run.d.ts +10 -0
- package/dist/src/cli/ai/tts/run.js +25 -0
- package/dist/src/cli/ai/tts/voices.d.ts +7 -0
- package/dist/src/cli/ai/tts/voices.js +12 -0
- package/dist/src/cli/ai/views.d.ts +3 -0
- package/dist/src/cli/ai/views.js +45 -0
- package/dist/src/cli/auth/index.d.ts +6 -0
- package/dist/src/cli/auth/index.js +22 -0
- package/dist/src/cli/auth/login.d.ts +11 -0
- package/dist/src/cli/auth/login.js +168 -0
- package/dist/src/cli/auth/logout.d.ts +6 -0
- package/dist/src/cli/auth/logout.js +21 -0
- package/dist/src/cli/auth/status.d.ts +4 -0
- package/dist/src/cli/auth/status.js +31 -0
- package/dist/src/cli/auth/view.d.ts +25 -0
- package/dist/src/cli/auth/view.js +46 -0
- package/dist/src/cli/auth/whoami.d.ts +7 -0
- package/dist/src/cli/auth/whoami.js +44 -0
- package/dist/src/cli/category/create.d.ts +10 -0
- package/dist/src/cli/category/create.js +24 -0
- package/dist/src/cli/category/delete.d.ts +8 -0
- package/dist/src/cli/category/delete.js +21 -0
- package/dist/src/cli/category/get.d.ts +6 -0
- package/dist/src/cli/category/get.js +11 -0
- package/dist/src/cli/category/index.d.ts +20 -0
- package/dist/src/cli/category/index.js +41 -0
- package/dist/src/cli/category/list.d.ts +4 -0
- package/dist/src/cli/category/list.js +10 -0
- package/dist/src/cli/category/update.d.ts +12 -0
- package/dist/src/cli/category/update.js +34 -0
- package/dist/src/cli/comment/_flags.d.ts +28 -0
- package/dist/src/cli/comment/_flags.js +48 -0
- package/dist/src/cli/comment/approve.d.ts +9 -0
- package/dist/src/cli/comment/approve.js +23 -0
- package/dist/src/cli/comment/delete.d.ts +9 -0
- package/dist/src/cli/comment/delete.js +24 -0
- package/dist/src/cli/comment/get.d.ts +6 -0
- package/dist/src/cli/comment/get.js +12 -0
- package/dist/src/cli/comment/index.d.ts +34 -0
- package/dist/src/cli/comment/index.js +53 -0
- package/dist/src/cli/comment/list.d.ts +9 -0
- package/dist/src/cli/comment/list.js +20 -0
- package/dist/src/cli/comment/reject.d.ts +9 -0
- package/dist/src/cli/comment/reject.js +23 -0
- package/dist/src/cli/comment/reply.d.ts +9 -0
- package/dist/src/cli/comment/reply.js +31 -0
- package/dist/src/cli/comment/unread.d.ts +7 -0
- package/dist/src/cli/comment/unread.js +21 -0
- package/dist/src/cli/comment/view.d.ts +3 -0
- package/dist/src/cli/comment/view.js +197 -0
- package/dist/src/cli/config/edit.d.ts +5 -0
- package/dist/src/cli/config/edit.js +36 -0
- package/dist/src/cli/config/get.d.ts +6 -0
- package/dist/src/cli/config/get.js +11 -0
- package/dist/src/cli/config/index.d.ts +10 -0
- package/dist/src/cli/config/index.js +22 -0
- package/dist/src/cli/config/list.d.ts +4 -0
- package/dist/src/cli/config/list.js +10 -0
- package/dist/src/cli/config/set.d.ts +9 -0
- package/dist/src/cli/config/set.js +48 -0
- package/dist/src/cli/draft/_shared.d.ts +27 -0
- package/dist/src/cli/draft/_shared.js +85 -0
- package/dist/src/cli/draft/create.d.ts +21 -0
- package/dist/src/cli/draft/create.js +34 -0
- package/dist/src/cli/draft/delete.d.ts +7 -0
- package/dist/src/cli/draft/delete.js +20 -0
- package/dist/src/cli/draft/get.d.ts +6 -0
- package/dist/src/cli/draft/get.js +11 -0
- package/dist/src/cli/draft/index.d.ts +54 -0
- package/dist/src/cli/draft/index.js +42 -0
- package/dist/src/cli/draft/list.d.ts +11 -0
- package/dist/src/cli/draft/list.js +23 -0
- package/dist/src/cli/draft/publish.d.ts +8 -0
- package/dist/src/cli/draft/publish.js +64 -0
- package/dist/src/cli/draft/update.d.ts +22 -0
- package/dist/src/cli/draft/update.js +29 -0
- package/dist/src/cli/file/_shared.d.ts +6 -0
- package/dist/src/cli/file/_shared.js +5 -0
- package/dist/src/cli/file/delete.d.ts +8 -0
- package/dist/src/cli/file/delete.js +21 -0
- package/dist/src/cli/file/index.d.ts +19 -0
- package/dist/src/cli/file/index.js +33 -0
- package/dist/src/cli/file/list.d.ts +6 -0
- package/dist/src/cli/file/list.js +11 -0
- package/dist/src/cli/file/rename.d.ts +8 -0
- package/dist/src/cli/file/rename.js +16 -0
- package/dist/src/cli/file/upload.d.ts +9 -0
- package/dist/src/cli/file/upload.js +32 -0
- package/dist/src/cli/help/index.d.ts +62 -0
- package/dist/src/cli/help/index.js +276 -0
- package/dist/src/cli/help/registry.d.ts +28 -0
- package/dist/src/cli/help/registry.js +29 -0
- package/dist/src/cli/note/_flags.d.ts +66 -0
- package/dist/src/cli/note/_flags.js +73 -0
- package/dist/src/cli/note/create.d.ts +23 -0
- package/dist/src/cli/note/create.js +25 -0
- package/dist/src/cli/note/delete.d.ts +8 -0
- package/dist/src/cli/note/delete.js +21 -0
- package/dist/src/cli/note/edit.d.ts +28 -0
- package/dist/src/cli/note/edit.js +114 -0
- package/dist/src/cli/note/get.d.ts +6 -0
- package/dist/src/cli/note/get.js +29 -0
- package/dist/src/cli/note/index.d.ts +77 -0
- package/dist/src/cli/note/index.js +63 -0
- package/dist/src/cli/note/list.d.ts +10 -0
- package/dist/src/cli/note/list.js +22 -0
- package/dist/src/cli/note/publish.d.ts +7 -0
- package/dist/src/cli/note/publish.js +17 -0
- package/dist/src/cli/note/unpublish.d.ts +7 -0
- package/dist/src/cli/note/unpublish.js +17 -0
- package/dist/src/cli/note/update.d.ts +25 -0
- package/dist/src/cli/note/update.js +36 -0
- package/dist/src/cli/note/view.d.ts +3 -0
- package/dist/src/cli/note/view.js +162 -0
- package/dist/src/cli/page/create.d.ts +41 -0
- package/dist/src/cli/page/create.js +56 -0
- package/dist/src/cli/page/delete.d.ts +7 -0
- package/dist/src/cli/page/delete.js +37 -0
- package/dist/src/cli/page/edit.d.ts +19 -0
- package/dist/src/cli/page/edit.js +117 -0
- package/dist/src/cli/page/get.d.ts +6 -0
- package/dist/src/cli/page/get.js +16 -0
- package/dist/src/cli/page/index.d.ts +44 -0
- package/dist/src/cli/page/index.js +38 -0
- package/dist/src/cli/page/list.d.ts +4 -0
- package/dist/src/cli/page/list.js +10 -0
- package/dist/src/cli/page/update.d.ts +16 -0
- package/dist/src/cli/page/update.js +48 -0
- package/dist/src/cli/page/view.d.ts +2 -0
- package/dist/src/cli/page/view.js +79 -0
- package/dist/src/cli/post/_flags.d.ts +66 -0
- package/dist/src/cli/post/_flags.js +96 -0
- package/dist/src/cli/post/apply.d.ts +7 -0
- package/dist/src/cli/post/apply.js +70 -0
- package/dist/src/cli/post/create.d.ts +21 -0
- package/dist/src/cli/post/create.js +24 -0
- package/dist/src/cli/post/delete.d.ts +8 -0
- package/dist/src/cli/post/delete.js +21 -0
- package/dist/src/cli/post/edit.d.ts +26 -0
- package/dist/src/cli/post/edit.js +115 -0
- package/dist/src/cli/post/get.d.ts +7 -0
- package/dist/src/cli/post/get.js +15 -0
- package/dist/src/cli/post/index.d.ts +91 -0
- package/dist/src/cli/post/index.js +73 -0
- package/dist/src/cli/post/list.d.ts +10 -0
- package/dist/src/cli/post/list.js +23 -0
- package/dist/src/cli/post/publish.d.ts +7 -0
- package/dist/src/cli/post/publish.js +17 -0
- package/dist/src/cli/post/stage.d.ts +23 -0
- package/dist/src/cli/post/stage.js +55 -0
- package/dist/src/cli/post/unpublish.d.ts +7 -0
- package/dist/src/cli/post/unpublish.js +17 -0
- package/dist/src/cli/post/update.d.ts +23 -0
- package/dist/src/cli/post/update.js +38 -0
- package/dist/src/cli/post/view.d.ts +3 -0
- package/dist/src/cli/post/view.js +150 -0
- package/dist/src/cli/preview/index.d.ts +13 -0
- package/dist/src/cli/preview/index.js +226 -0
- package/dist/src/cli/profile/index.d.ts +14 -0
- package/dist/src/cli/profile/index.js +36 -0
- package/dist/src/cli/profile/ls.d.ts +5 -0
- package/dist/src/cli/profile/ls.js +26 -0
- package/dist/src/cli/profile/mark.d.ts +9 -0
- package/dist/src/cli/profile/mark.js +23 -0
- package/dist/src/cli/profile/rm.d.ts +12 -0
- package/dist/src/cli/profile/rm.js +50 -0
- package/dist/src/cli/profile/show.d.ts +9 -0
- package/dist/src/cli/profile/show.js +40 -0
- package/dist/src/cli/profile/use.d.ts +6 -0
- package/dist/src/cli/profile/use.js +11 -0
- package/dist/src/cli/profile/view.d.ts +8 -0
- package/dist/src/cli/profile/view.js +21 -0
- package/dist/src/cli/project/_flags.d.ts +56 -0
- package/dist/src/cli/project/_flags.js +130 -0
- package/dist/src/cli/project/create.d.ts +17 -0
- package/dist/src/cli/project/create.js +25 -0
- package/dist/src/cli/project/delete.d.ts +8 -0
- package/dist/src/cli/project/delete.js +22 -0
- package/dist/src/cli/project/edit.d.ts +11 -0
- package/dist/src/cli/project/edit.js +61 -0
- package/dist/src/cli/project/get.d.ts +7 -0
- package/dist/src/cli/project/get.js +14 -0
- package/dist/src/cli/project/index.d.ts +43 -0
- package/dist/src/cli/project/index.js +52 -0
- package/dist/src/cli/project/list.d.ts +8 -0
- package/dist/src/cli/project/list.js +19 -0
- package/dist/src/cli/project/update.d.ts +18 -0
- package/dist/src/cli/project/update.js +24 -0
- package/dist/src/cli/project/view-cmd.d.ts +7 -0
- package/dist/src/cli/project/view-cmd.js +15 -0
- package/dist/src/cli/project/view.d.ts +3 -0
- package/dist/src/cli/project/view.js +102 -0
- package/dist/src/cli/render/codehighlight.d.ts +8 -0
- package/dist/src/cli/render/codehighlight.js +264 -0
- package/dist/src/cli/render/envelope.d.ts +7 -0
- package/dist/src/cli/render/envelope.js +23 -0
- package/dist/src/cli/render/frontmatter.d.ts +8 -0
- package/dist/src/cli/render/frontmatter.js +50 -0
- package/dist/src/cli/render/helpers.d.ts +2 -0
- package/dist/src/cli/render/helpers.js +11 -0
- package/dist/src/cli/render/index.d.ts +7 -0
- package/dist/src/cli/render/index.js +10 -0
- package/dist/src/cli/render/lexical/blocks.d.ts +19 -0
- package/dist/src/cli/render/lexical/blocks.js +208 -0
- package/dist/src/cli/render/lexical/code.d.ts +2 -0
- package/dist/src/cli/render/lexical/code.js +22 -0
- package/dist/src/cli/render/lexical/embeds.d.ts +6 -0
- package/dist/src/cli/render/lexical/embeds.js +51 -0
- package/dist/src/cli/render/lexical/index.d.ts +9 -0
- package/dist/src/cli/render/lexical/index.js +25 -0
- package/dist/src/cli/render/lexical/inlines.d.ts +17 -0
- package/dist/src/cli/render/lexical/inlines.js +76 -0
- package/dist/src/cli/render/lexical/parser.d.ts +10 -0
- package/dist/src/cli/render/lexical/parser.js +64 -0
- package/dist/src/cli/render/lexical/walker.d.ts +21 -0
- package/dist/src/cli/render/lexical/walker.js +51 -0
- package/dist/src/cli/render/markdown.d.ts +18 -0
- package/dist/src/cli/render/markdown.js +196 -0
- package/dist/src/cli/render/metadata-block.d.ts +14 -0
- package/dist/src/cli/render/metadata-block.js +50 -0
- package/dist/src/cli/skill/all.d.ts +4 -0
- package/dist/src/cli/skill/all.js +11 -0
- package/dist/src/cli/skill/get.d.ts +6 -0
- package/dist/src/cli/skill/get.js +12 -0
- package/dist/src/cli/skill/index.d.ts +10 -0
- package/dist/src/cli/skill/index.js +34 -0
- package/dist/src/cli/skill/list.d.ts +4 -0
- package/dist/src/cli/skill/list.js +11 -0
- package/dist/src/cli/skill/search.d.ts +6 -0
- package/dist/src/cli/skill/search.js +12 -0
- package/dist/src/cli/skill/views.d.ts +6 -0
- package/dist/src/cli/skill/views.js +90 -0
- package/dist/src/cli/snippet/_flags.d.ts +57 -0
- package/dist/src/cli/snippet/_flags.js +123 -0
- package/dist/src/cli/snippet/_resolve.d.ts +4 -0
- package/dist/src/cli/snippet/_resolve.js +32 -0
- package/dist/src/cli/snippet/_sync.d.ts +5 -0
- package/dist/src/cli/snippet/_sync.js +54 -0
- package/dist/src/cli/snippet/create.d.ts +19 -0
- package/dist/src/cli/snippet/create.js +35 -0
- package/dist/src/cli/snippet/delete.d.ts +8 -0
- package/dist/src/cli/snippet/delete.js +28 -0
- package/dist/src/cli/snippet/edit.d.ts +7 -0
- package/dist/src/cli/snippet/edit.js +29 -0
- package/dist/src/cli/snippet/get.d.ts +6 -0
- package/dist/src/cli/snippet/get.js +12 -0
- package/dist/src/cli/snippet/index.d.ts +43 -0
- package/dist/src/cli/snippet/index.js +58 -0
- package/dist/src/cli/snippet/list.d.ts +9 -0
- package/dist/src/cli/snippet/list.js +20 -0
- package/dist/src/cli/snippet/pull.d.ts +8 -0
- package/dist/src/cli/snippet/pull.js +33 -0
- package/dist/src/cli/snippet/push.d.ts +10 -0
- package/dist/src/cli/snippet/push.js +31 -0
- package/dist/src/cli/snippet/update.d.ts +8 -0
- package/dist/src/cli/snippet/update.js +16 -0
- package/dist/src/cli/topic/create.d.ts +10 -0
- package/dist/src/cli/topic/create.js +24 -0
- package/dist/src/cli/topic/delete.d.ts +7 -0
- package/dist/src/cli/topic/delete.js +37 -0
- package/dist/src/cli/topic/get.d.ts +6 -0
- package/dist/src/cli/topic/get.js +15 -0
- package/dist/src/cli/topic/index.d.ts +20 -0
- package/dist/src/cli/topic/index.js +32 -0
- package/dist/src/cli/topic/list.d.ts +4 -0
- package/dist/src/cli/topic/list.js +10 -0
- package/dist/src/cli/topic/update.d.ts +11 -0
- package/dist/src/cli/topic/update.js +51 -0
- package/dist/src/cli/ui/index.d.ts +24 -0
- package/dist/src/cli/ui/index.js +83 -0
- package/dist/src/cli/update/index.d.ts +10 -0
- package/dist/src/cli/update/index.js +142 -0
- package/dist/src/domain/admin-link.d.ts +12 -0
- package/dist/src/domain/admin-link.js +45 -0
- package/dist/src/domain/content-spec.d.ts +20 -0
- package/dist/src/domain/content-spec.js +78 -0
- package/dist/src/domain/envelope-compat.d.ts +68 -0
- package/dist/src/domain/envelope-compat.js +121 -0
- package/dist/src/domain/envelope.d.ts +34 -0
- package/dist/src/domain/envelope.js +355 -0
- package/dist/src/domain/errors.d.ts +336 -0
- package/dist/src/domain/errors.js +239 -0
- package/dist/src/domain/gate.d.ts +14 -0
- package/dist/src/domain/gate.js +18 -0
- package/dist/src/domain/payload.d.ts +64 -0
- package/dist/src/domain/payload.js +237 -0
- package/dist/src/domain/preflight-guards.d.ts +54 -0
- package/dist/src/domain/preflight-guards.js +130 -0
- package/dist/src/domain/runtime-flags.d.ts +33 -0
- package/dist/src/domain/runtime-flags.js +166 -0
- package/dist/src/domain/schema/api-envelope.d.ts +67 -0
- package/dist/src/domain/schema/api-envelope.js +65 -0
- package/dist/src/domain/schema/category.d.ts +23 -0
- package/dist/src/domain/schema/category.js +19 -0
- package/dist/src/domain/schema/config.d.ts +13 -0
- package/dist/src/domain/schema/config.js +18 -0
- package/dist/src/domain/schema/note.d.ts +76 -0
- package/dist/src/domain/schema/note.js +35 -0
- package/dist/src/domain/schema/page.d.ts +40 -0
- package/dist/src/domain/schema/page.js +22 -0
- package/dist/src/domain/schema/post.d.ts +68 -0
- package/dist/src/domain/schema/post.js +35 -0
- package/dist/src/domain/schema/profile.d.ts +14 -0
- package/dist/src/domain/schema/profile.js +22 -0
- package/dist/src/domain/schema/topic.d.ts +24 -0
- package/dist/src/domain/schema/topic.js +16 -0
- package/dist/src/domain/version.d.ts +15 -0
- package/dist/src/domain/version.js +43 -0
- package/dist/src/index.d.ts +4 -0
- package/dist/src/index.js +8 -0
- package/dist/src/layers/App.d.ts +30 -0
- package/dist/src/layers/App.js +39 -0
- package/dist/src/services/Ai/index.d.ts +13 -0
- package/dist/src/services/Ai/index.js +127 -0
- package/dist/src/services/Ai/overview.d.ts +7 -0
- package/dist/src/services/Ai/overview.js +11 -0
- package/dist/src/services/Ai/resources.d.ts +7 -0
- package/dist/src/services/Ai/resources.js +59 -0
- package/dist/src/services/Ai/shared.d.ts +2 -0
- package/dist/src/services/Ai/shared.js +11 -0
- package/dist/src/services/Ai/tasks.d.ts +8 -0
- package/dist/src/services/Ai/tasks.js +144 -0
- package/dist/src/services/Ai/types.d.ts +130 -0
- package/dist/src/services/Ai/types.js +1 -0
- package/dist/src/services/Api.d.ts +67 -0
- package/dist/src/services/Api.js +348 -0
- package/dist/src/services/Auth.d.ts +101 -0
- package/dist/src/services/Auth.js +402 -0
- package/dist/src/services/Backup.d.ts +8 -0
- package/dist/src/services/Backup.js +4 -0
- package/dist/src/services/Cache.d.ts +8 -0
- package/dist/src/services/Cache.js +4 -0
- package/dist/src/services/Comment.d.ts +48 -0
- package/dist/src/services/Comment.js +102 -0
- package/dist/src/services/Config.d.ts +95 -0
- package/dist/src/services/Config.js +359 -0
- package/dist/src/services/Editor.d.ts +28 -0
- package/dist/src/services/Editor.js +95 -0
- package/dist/src/services/Lexical.d.ts +27 -0
- package/dist/src/services/Lexical.js +56 -0
- package/dist/src/services/Migration.d.ts +31 -0
- package/dist/src/services/Migration.js +177 -0
- package/dist/src/services/Profile.d.ts +29 -0
- package/dist/src/services/Profile.js +115 -0
- package/dist/src/services/Renderer/content.d.ts +16 -0
- package/dist/src/services/Renderer/content.js +110 -0
- package/dist/src/services/Renderer/datetime.d.ts +38 -0
- package/dist/src/services/Renderer/datetime.js +147 -0
- package/dist/src/services/Renderer/errors.d.ts +3 -0
- package/dist/src/services/Renderer/errors.js +59 -0
- package/dist/src/services/Renderer/index.d.ts +13 -0
- package/dist/src/services/Renderer/index.js +11 -0
- package/dist/src/services/Renderer/lists.d.ts +1 -0
- package/dist/src/services/Renderer/lists.js +101 -0
- package/dist/src/services/Renderer/options.d.ts +10 -0
- package/dist/src/services/Renderer/options.js +13 -0
- package/dist/src/services/Renderer/primitives.d.ts +4 -0
- package/dist/src/services/Renderer/primitives.js +12 -0
- package/dist/src/services/Renderer/service.d.ts +35 -0
- package/dist/src/services/Renderer/service.js +123 -0
- package/dist/src/services/Renderer/view.d.ts +16 -0
- package/dist/src/services/Renderer/view.js +1 -0
- package/dist/src/services/Resolver.d.ts +47 -0
- package/dist/src/services/Resolver.js +269 -0
- package/dist/src/services/Skill.d.ts +29 -0
- package/dist/src/services/Skill.js +421 -0
- package/dist/src/services/UpdateNotifier.d.ts +162 -0
- package/dist/src/services/UpdateNotifier.js +572 -0
- package/dist/test/cli/ai/ai.test.d.ts +1 -0
- package/dist/test/cli/ai/ai.test.js +507 -0
- package/dist/test/cli/ai/verbs.test.d.ts +1 -0
- package/dist/test/cli/ai/verbs.test.js +235 -0
- package/dist/test/cli/auth/login.test.d.ts +1 -0
- package/dist/test/cli/auth/login.test.js +236 -0
- package/dist/test/cli/auth/logout-whoami.test.d.ts +1 -0
- package/dist/test/cli/auth/logout-whoami.test.js +124 -0
- package/dist/test/cli/auth/status.test.d.ts +1 -0
- package/dist/test/cli/auth/status.test.js +90 -0
- package/dist/test/cli/category/category.test.d.ts +1 -0
- package/dist/test/cli/category/category.test.js +175 -0
- package/dist/test/cli/comment/comment.test.d.ts +1 -0
- package/dist/test/cli/comment/comment.test.js +445 -0
- package/dist/test/cli/config/config.test.d.ts +1 -0
- package/dist/test/cli/config/config.test.js +281 -0
- package/dist/test/cli/edit-roundtrip.test.d.ts +1 -0
- package/dist/test/cli/edit-roundtrip.test.js +259 -0
- package/dist/test/cli/file/file.test.d.ts +1 -0
- package/dist/test/cli/file/file.test.js +185 -0
- package/dist/test/cli/help.test.d.ts +1 -0
- package/dist/test/cli/help.test.js +289 -0
- package/dist/test/cli/note/create.test.d.ts +1 -0
- package/dist/test/cli/note/create.test.js +155 -0
- package/dist/test/cli/note/list.test.d.ts +1 -0
- package/dist/test/cli/note/list.test.js +115 -0
- package/dist/test/cli/note/publish.test.d.ts +1 -0
- package/dist/test/cli/note/publish.test.js +123 -0
- package/dist/test/cli/page/page.test.d.ts +1 -0
- package/dist/test/cli/page/page.test.js +185 -0
- package/dist/test/cli/post/create.test.d.ts +1 -0
- package/dist/test/cli/post/create.test.js +172 -0
- package/dist/test/cli/post/list.test.d.ts +1 -0
- package/dist/test/cli/post/list.test.js +133 -0
- package/dist/test/cli/post/publish.test.d.ts +1 -0
- package/dist/test/cli/post/publish.test.js +139 -0
- package/dist/test/cli/post/stage-apply.test.d.ts +1 -0
- package/dist/test/cli/post/stage-apply.test.js +215 -0
- package/dist/test/cli/post/update.test.d.ts +1 -0
- package/dist/test/cli/post/update.test.js +160 -0
- package/dist/test/cli/post-note-crud.test.d.ts +1 -0
- package/dist/test/cli/post-note-crud.test.js +362 -0
- package/dist/test/cli/profile/ls.test.d.ts +1 -0
- package/dist/test/cli/profile/ls.test.js +93 -0
- package/dist/test/cli/profile/mark-use.test.d.ts +1 -0
- package/dist/test/cli/profile/mark-use.test.js +216 -0
- package/dist/test/cli/project-crud.test.d.ts +1 -0
- package/dist/test/cli/project-crud.test.js +315 -0
- package/dist/test/cli/render/lexical.test.d.ts +1 -0
- package/dist/test/cli/render/lexical.test.js +216 -0
- package/dist/test/cli/render/markdown.test.d.ts +1 -0
- package/dist/test/cli/render/markdown.test.js +39 -0
- package/dist/test/cli/snippet-crud.test.d.ts +1 -0
- package/dist/test/cli/snippet-crud.test.js +293 -0
- package/dist/test/cli/topic/topic.test.d.ts +1 -0
- package/dist/test/cli/topic/topic.test.js +181 -0
- package/dist/test/cli/update.test.d.ts +1 -0
- package/dist/test/cli/update.test.js +395 -0
- package/dist/test/domain/content-spec.test.d.ts +1 -0
- package/dist/test/domain/content-spec.test.js +126 -0
- package/dist/test/domain/envelope-compat.test.d.ts +1 -0
- package/dist/test/domain/envelope-compat.test.js +126 -0
- package/dist/test/domain/envelope.test.d.ts +1 -0
- package/dist/test/domain/envelope.test.js +149 -0
- package/dist/test/domain/errors.test.d.ts +1 -0
- package/dist/test/domain/errors.test.js +127 -0
- package/dist/test/domain/gate.test.d.ts +1 -0
- package/dist/test/domain/gate.test.js +128 -0
- package/dist/test/domain/payload.test.d.ts +1 -0
- package/dist/test/domain/payload.test.js +259 -0
- package/dist/test/helper/test-fs.d.ts +24 -0
- package/dist/test/helper/test-fs.js +161 -0
- package/dist/test/helper/test-http.d.ts +31 -0
- package/dist/test/helper/test-http.js +66 -0
- package/dist/test/integration/_helpers.d.ts +5 -0
- package/dist/test/integration/_helpers.js +24 -0
- package/dist/test/integration/cli-error-envelope.test.d.ts +1 -0
- package/dist/test/integration/cli-error-envelope.test.js +137 -0
- package/dist/test/integration/cli-help-render.test.d.ts +1 -0
- package/dist/test/integration/cli-help-render.test.js +149 -0
- package/dist/test/integration/cli-help.test.d.ts +1 -0
- package/dist/test/integration/cli-help.test.js +67 -0
- package/dist/test/integration/cli-post-list.test.d.ts +1 -0
- package/dist/test/integration/cli-post-list.test.js +140 -0
- package/dist/test/integration/cli-skill.test.d.ts +1 -0
- package/dist/test/integration/cli-skill.test.js +74 -0
- package/dist/test/integration/cli-version.test.d.ts +1 -0
- package/dist/test/integration/cli-version.test.js +37 -0
- package/dist/test/services/Api.test.d.ts +1 -0
- package/dist/test/services/Api.test.js +553 -0
- package/dist/test/services/Auth.test.d.ts +1 -0
- package/dist/test/services/Auth.test.js +650 -0
- package/dist/test/services/Config.test.d.ts +1 -0
- package/dist/test/services/Config.test.js +510 -0
- package/dist/test/services/Datetime.test.d.ts +1 -0
- package/dist/test/services/Datetime.test.js +113 -0
- package/dist/test/services/Editor.test.d.ts +1 -0
- package/dist/test/services/Editor.test.js +205 -0
- package/dist/test/services/Lexical.errors.test.d.ts +1 -0
- package/dist/test/services/Lexical.errors.test.js +60 -0
- package/dist/test/services/Lexical.test.d.ts +1 -0
- package/dist/test/services/Lexical.test.js +68 -0
- package/dist/test/services/Migration.test.d.ts +1 -0
- package/dist/test/services/Migration.test.js +376 -0
- package/dist/test/services/Profile.test.d.ts +1 -0
- package/dist/test/services/Profile.test.js +277 -0
- package/dist/test/services/Renderer.test.d.ts +1 -0
- package/dist/test/services/Renderer.test.js +538 -0
- package/dist/test/services/Resolver.test.d.ts +1 -0
- package/dist/test/services/Resolver.test.js +320 -0
- package/dist/test/services/Skill.test.d.ts +1 -0
- package/dist/test/services/Skill.test.js +261 -0
- package/dist/test/services/UpdateNotifier.spawn.test.d.ts +1 -0
- package/dist/test/services/UpdateNotifier.spawn.test.js +73 -0
- package/dist/test/services/UpdateNotifier.test.d.ts +1 -0
- package/dist/test/services/UpdateNotifier.test.js +912 -0
- package/dist/vendor/litexml/cli.mjs +2481 -2561
- package/dist/vendor/litexml/litexml-html-preview-client.css +178 -60
- package/dist/vendor/litexml/litexml-html-preview-client.js +9950 -8762
- package/dist/vendor/litexml/style.css +11 -7
- package/package.json +23 -21
- package/skills/commands-ai.md +45 -12
- package/skills/overview.md +1 -1
- package/skills/safety.md +1 -1
- package/dist/esm-CH7CrOvh.mjs +0 -2
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { FileSystem } from '@effect/platform';
|
|
2
|
+
import { Context, Effect, Layer } from 'effect';
|
|
3
|
+
import { ConfigMigrationFailed } from '../domain/errors';
|
|
4
|
+
import { Config, stripLegacyConfigFields } from './Config';
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Helpers
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
const emit = (report, line) => {
|
|
9
|
+
if (report === null)
|
|
10
|
+
return;
|
|
11
|
+
if (report !== undefined) {
|
|
12
|
+
report(line);
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
process.stderr.write(`${line}\n`);
|
|
16
|
+
};
|
|
17
|
+
const isNotFound = (err) => {
|
|
18
|
+
if (typeof err === 'object' && err !== null) {
|
|
19
|
+
const e = err;
|
|
20
|
+
if (e._tag === 'SystemError' && e.reason === 'NotFound')
|
|
21
|
+
return true;
|
|
22
|
+
if (e.code === 'ENOENT')
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
};
|
|
27
|
+
const fileExists = (fs, path) => fs.stat(path).pipe(Effect.map(() => true), Effect.catchAll((err) => isNotFound(err)
|
|
28
|
+
? Effect.succeed(false)
|
|
29
|
+
: Effect.fail(new ConfigMigrationFailed({
|
|
30
|
+
message: `failed to stat ${path}: ${String(err)}`,
|
|
31
|
+
cause: err,
|
|
32
|
+
}))));
|
|
33
|
+
const dirExists = (fs, path) => fs.stat(path).pipe(Effect.map((info) => info.type === 'Directory'), Effect.catchAll((err) => isNotFound(err)
|
|
34
|
+
? Effect.succeed(false)
|
|
35
|
+
: Effect.fail(new ConfigMigrationFailed({
|
|
36
|
+
message: `failed to stat ${path}: ${String(err)}`,
|
|
37
|
+
cause: err,
|
|
38
|
+
}))));
|
|
39
|
+
const tryUnlink = (fs, path, report) => fs.remove(path, { force: true }).pipe(Effect.catchAll((err) => {
|
|
40
|
+
if (isNotFound(err))
|
|
41
|
+
return Effect.void;
|
|
42
|
+
emit(report, `mxs: warning: could not remove stale legacy file ${path}: ${err.message ?? String(err)}`);
|
|
43
|
+
return Effect.void;
|
|
44
|
+
}));
|
|
45
|
+
// `@clack/prompts` exports a `cancel` symbol; matching by `typeof v === 'symbol'`
|
|
46
|
+
// covers both the real symbol and test fixtures. Inlined here so the migration
|
|
47
|
+
// service does not pull in `@clack/prompts` (the prompt callback is injected
|
|
48
|
+
// at the call site).
|
|
49
|
+
const isCancel = (v) => typeof v === 'symbol';
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Implementation
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
const make = Effect.gen(function* () {
|
|
54
|
+
const fs = yield* FileSystem.FileSystem;
|
|
55
|
+
const config = yield* Config;
|
|
56
|
+
const runLegacyMigrationIfNeeded = (opts) => Effect.gen(function* () {
|
|
57
|
+
const report = opts?.report;
|
|
58
|
+
const legacyConfigPath = yield* config.getLegacyConfigPath;
|
|
59
|
+
const legacyCredentialsPath = yield* config.getLegacyCredentialsPath;
|
|
60
|
+
const profilesDir = yield* config.getProfilesDir;
|
|
61
|
+
const hasConfig = yield* fileExists(fs, legacyConfigPath);
|
|
62
|
+
const hasCreds = yield* fileExists(fs, legacyCredentialsPath);
|
|
63
|
+
// Fast path: nothing to migrate.
|
|
64
|
+
if (!hasConfig && !hasCreds)
|
|
65
|
+
return null;
|
|
66
|
+
const profilesExist = yield* dirExists(fs, profilesDir);
|
|
67
|
+
// Stale-cleanup branch.
|
|
68
|
+
if (profilesExist) {
|
|
69
|
+
const toRemove = [];
|
|
70
|
+
if (hasConfig)
|
|
71
|
+
toRemove.push(legacyConfigPath);
|
|
72
|
+
if (hasCreds)
|
|
73
|
+
toRemove.push(legacyCredentialsPath);
|
|
74
|
+
for (const p of toRemove) {
|
|
75
|
+
yield* tryUnlink(fs, p, report);
|
|
76
|
+
}
|
|
77
|
+
emit(report, `mxs: removed stale legacy config files at ${toRemove.join(', ')}`);
|
|
78
|
+
return {
|
|
79
|
+
profile: null,
|
|
80
|
+
production: false,
|
|
81
|
+
cleanedStaleLegacy: true,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
// Full migration branch.
|
|
85
|
+
let legacyConfig = {};
|
|
86
|
+
if (hasConfig) {
|
|
87
|
+
const raw = yield* fs.readFileString(legacyConfigPath).pipe(Effect.catchAll((err) => Effect.fail(new ConfigMigrationFailed({
|
|
88
|
+
message: `failed to read legacy config at ${legacyConfigPath}: ${String(err)}`,
|
|
89
|
+
cause: err,
|
|
90
|
+
}))));
|
|
91
|
+
try {
|
|
92
|
+
legacyConfig = JSON.parse(raw);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
return yield* Effect.fail(new ConfigMigrationFailed({
|
|
96
|
+
message: `failed to read legacy config at ${legacyConfigPath}: ${err.message}`,
|
|
97
|
+
cause: err,
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
let legacyCredentials = null;
|
|
102
|
+
if (hasCreds) {
|
|
103
|
+
const raw = yield* fs.readFileString(legacyCredentialsPath).pipe(Effect.catchAll((err) => Effect.fail(new ConfigMigrationFailed({
|
|
104
|
+
message: `failed to read legacy credentials at ${legacyCredentialsPath}: ${String(err)}`,
|
|
105
|
+
cause: err,
|
|
106
|
+
}))));
|
|
107
|
+
try {
|
|
108
|
+
legacyCredentials = JSON.parse(raw);
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
return yield* Effect.fail(new ConfigMigrationFailed({
|
|
112
|
+
message: `failed to read legacy credentials at ${legacyCredentialsPath}: ${err.message}`,
|
|
113
|
+
cause: err,
|
|
114
|
+
}));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
let production = false;
|
|
118
|
+
const tty = opts?.isTTY ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
|
|
119
|
+
const apiUrl = typeof legacyConfig.api_url === 'string'
|
|
120
|
+
? legacyConfig.api_url
|
|
121
|
+
: undefined;
|
|
122
|
+
if (tty && apiUrl && opts?.promptIsProduction) {
|
|
123
|
+
const answer = yield* Effect.tryPromise({
|
|
124
|
+
try: () => opts.promptIsProduction(apiUrl),
|
|
125
|
+
catch: (err) => new ConfigMigrationFailed({
|
|
126
|
+
message: `legacy migration prompt failed: ${String(err)}`,
|
|
127
|
+
cause: err,
|
|
128
|
+
}),
|
|
129
|
+
});
|
|
130
|
+
production = isCancel(answer) ? false : Boolean(answer);
|
|
131
|
+
}
|
|
132
|
+
// Strip legacy fields and apply production flag.
|
|
133
|
+
const migratedConfig = {
|
|
134
|
+
...stripLegacyConfigFields(legacyConfig),
|
|
135
|
+
};
|
|
136
|
+
if (production) {
|
|
137
|
+
migratedConfig.production = true;
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
delete migratedConfig.production;
|
|
141
|
+
}
|
|
142
|
+
yield* config.writeProfileConfig('default', migratedConfig).pipe(Effect.catchAll((err) => Effect.fail(new ConfigMigrationFailed({
|
|
143
|
+
message: `failed to write profile config during migration: ${String(err)}`,
|
|
144
|
+
cause: err,
|
|
145
|
+
}))));
|
|
146
|
+
if (legacyCredentials !== null) {
|
|
147
|
+
yield* config
|
|
148
|
+
.writeProfileCredentials('default', legacyCredentials)
|
|
149
|
+
.pipe(Effect.catchAll((err) => Effect.fail(new ConfigMigrationFailed({
|
|
150
|
+
message: `failed to write profile credentials during migration: ${String(err)}`,
|
|
151
|
+
cause: err,
|
|
152
|
+
}))));
|
|
153
|
+
}
|
|
154
|
+
// Point `current` at the new default profile before unlinking legacy
|
|
155
|
+
// files, so a crash mid-migration leaves the user with a recoverable
|
|
156
|
+
// state.
|
|
157
|
+
yield* config.writeCurrent('default').pipe(Effect.catchAll((err) => Effect.fail(new ConfigMigrationFailed({
|
|
158
|
+
message: `failed to set current profile to default: ${String(err)}`,
|
|
159
|
+
cause: err,
|
|
160
|
+
}))));
|
|
161
|
+
if (hasConfig)
|
|
162
|
+
yield* tryUnlink(fs, legacyConfigPath, report);
|
|
163
|
+
if (hasCreds)
|
|
164
|
+
yield* tryUnlink(fs, legacyCredentialsPath, report);
|
|
165
|
+
emit(report, `mxs: migrated single-profile config to profile 'default'.`);
|
|
166
|
+
return {
|
|
167
|
+
profile: 'default',
|
|
168
|
+
production,
|
|
169
|
+
cleanedStaleLegacy: false,
|
|
170
|
+
};
|
|
171
|
+
});
|
|
172
|
+
const svc = { runLegacyMigrationIfNeeded };
|
|
173
|
+
return svc;
|
|
174
|
+
});
|
|
175
|
+
export class Migration extends Context.Tag('Migration')() {
|
|
176
|
+
static Default = Layer.effect(Migration, make);
|
|
177
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from 'effect';
|
|
2
|
+
import { Generic, ProfileInvalidName, ProfileNotFound, ResourceNotFound } from '../domain/errors';
|
|
3
|
+
import { Config, type ConfigShape, type ResolvedConfig, type StoreOverrides } from './Config';
|
|
4
|
+
export declare const PROFILE_NAME_RE: RegExp;
|
|
5
|
+
export declare const RESERVED_PROFILE_NAMES: Set<string>;
|
|
6
|
+
export interface ProfileSummary {
|
|
7
|
+
readonly name: string;
|
|
8
|
+
readonly apiUrl?: string;
|
|
9
|
+
readonly production: boolean;
|
|
10
|
+
readonly authenticated: boolean;
|
|
11
|
+
readonly active: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface ProfileService {
|
|
14
|
+
readonly validateName: (name: string) => Effect.Effect<void, ProfileInvalidName>;
|
|
15
|
+
readonly list: Effect.Effect<readonly string[], Generic>;
|
|
16
|
+
readonly current: Effect.Effect<string | null, Generic>;
|
|
17
|
+
readonly use: (name: string) => Effect.Effect<void, ProfileInvalidName | ProfileNotFound | Generic>;
|
|
18
|
+
readonly mark: (name: string, update: {
|
|
19
|
+
readonly production?: boolean;
|
|
20
|
+
}) => Effect.Effect<ConfigShape, ProfileNotFound | Generic>;
|
|
21
|
+
readonly rm: (name: string) => Effect.Effect<void, ResourceNotFound | Generic>;
|
|
22
|
+
readonly show: (name: string) => Effect.Effect<ProfileSummary, ProfileNotFound | Generic>;
|
|
23
|
+
readonly resolve: (overrides?: StoreOverrides) => Effect.Effect<ResolvedConfig, ProfileNotFound | Generic>;
|
|
24
|
+
}
|
|
25
|
+
declare const Profile_base: Context.TagClass<Profile, "Profile", ProfileService>;
|
|
26
|
+
export declare class Profile extends Profile_base {
|
|
27
|
+
static Default: Layer.Layer<Profile, never, Config>;
|
|
28
|
+
}
|
|
29
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Context, Effect, Layer } from 'effect';
|
|
2
|
+
import { Generic, ProfileInvalidName, ProfileNotFound, ResourceNotFound, } from '../domain/errors';
|
|
3
|
+
import { Config, } from './Config';
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Name validation
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
export const PROFILE_NAME_RE = /^[\d_a-z-]{1,32}$/;
|
|
8
|
+
export const RESERVED_PROFILE_NAMES = new Set(['current']);
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
// Implementation
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
const make = Effect.gen(function* () {
|
|
13
|
+
const config = yield* Config;
|
|
14
|
+
const validateName = (name) => Effect.gen(function* () {
|
|
15
|
+
if (!name || name.length === 0) {
|
|
16
|
+
return yield* Effect.fail(new ProfileInvalidName({
|
|
17
|
+
name,
|
|
18
|
+
message: 'profile name must not be empty',
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
if (RESERVED_PROFILE_NAMES.has(name)) {
|
|
22
|
+
return yield* Effect.fail(new ProfileInvalidName({
|
|
23
|
+
name,
|
|
24
|
+
message: `profile name '${name}' is reserved`,
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
if (!PROFILE_NAME_RE.test(name)) {
|
|
28
|
+
return yield* Effect.fail(new ProfileInvalidName({
|
|
29
|
+
name,
|
|
30
|
+
message: `profile name '${name}' is invalid; must match ^[a-z0-9_-]{1,32}$`,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
const list = config.listProfileDirs;
|
|
35
|
+
const current = config.readCurrent;
|
|
36
|
+
const use = (name) => Effect.gen(function* () {
|
|
37
|
+
yield* validateName(name);
|
|
38
|
+
const exists = yield* config.profileExists(name);
|
|
39
|
+
if (!exists) {
|
|
40
|
+
return yield* Effect.fail(new ProfileNotFound({
|
|
41
|
+
name,
|
|
42
|
+
message: `profile '${name}' does not exist`,
|
|
43
|
+
}));
|
|
44
|
+
}
|
|
45
|
+
yield* config.writeCurrent(name);
|
|
46
|
+
});
|
|
47
|
+
const mark = (name, update) => Effect.gen(function* () {
|
|
48
|
+
const exists = yield* config.profileExists(name);
|
|
49
|
+
if (!exists) {
|
|
50
|
+
return yield* Effect.fail(new ProfileNotFound({
|
|
51
|
+
name,
|
|
52
|
+
message: `profile '${name}' does not exist`,
|
|
53
|
+
}));
|
|
54
|
+
}
|
|
55
|
+
return yield* config.updateProfileConfig(name, (prev) => {
|
|
56
|
+
const next = { ...prev };
|
|
57
|
+
if (update.production === undefined)
|
|
58
|
+
return next;
|
|
59
|
+
if (update.production) {
|
|
60
|
+
return { ...next, production: true };
|
|
61
|
+
}
|
|
62
|
+
const stripped = { ...next };
|
|
63
|
+
delete stripped.production;
|
|
64
|
+
return stripped;
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
const rm = (name) => Effect.gen(function* () {
|
|
68
|
+
const exists = yield* config.profileExists(name);
|
|
69
|
+
if (!exists) {
|
|
70
|
+
return yield* Effect.fail(new ResourceNotFound({
|
|
71
|
+
kind: 'profile',
|
|
72
|
+
ref: name,
|
|
73
|
+
message: `profile '${name}' does not exist`,
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
yield* config.removeProfileDir(name);
|
|
77
|
+
});
|
|
78
|
+
const show = (name) => Effect.gen(function* () {
|
|
79
|
+
const exists = yield* config.profileExists(name);
|
|
80
|
+
if (!exists) {
|
|
81
|
+
return yield* Effect.fail(new ProfileNotFound({
|
|
82
|
+
name,
|
|
83
|
+
message: `profile '${name}' does not exist`,
|
|
84
|
+
}));
|
|
85
|
+
}
|
|
86
|
+
const cfg = yield* config.readProfileConfig(name);
|
|
87
|
+
const creds = yield* config.readProfileCredentials(name);
|
|
88
|
+
const active = (yield* config.readCurrent) === name;
|
|
89
|
+
return {
|
|
90
|
+
name,
|
|
91
|
+
apiUrl: cfg.api_url,
|
|
92
|
+
production: Boolean(cfg.production),
|
|
93
|
+
authenticated: creds !== null,
|
|
94
|
+
active,
|
|
95
|
+
};
|
|
96
|
+
});
|
|
97
|
+
const resolve = (overrides) => config.resolve(overrides).pipe(Effect.catchTags({
|
|
98
|
+
ConfigMissingApiUrl: (e) => Effect.fail(new Generic({ message: e.message, cause: e })),
|
|
99
|
+
ConfigMigrationFailed: (e) => Effect.fail(new Generic({ message: e.message, cause: e })),
|
|
100
|
+
}));
|
|
101
|
+
const svc = {
|
|
102
|
+
validateName,
|
|
103
|
+
list,
|
|
104
|
+
current,
|
|
105
|
+
use,
|
|
106
|
+
mark,
|
|
107
|
+
rm,
|
|
108
|
+
show,
|
|
109
|
+
resolve,
|
|
110
|
+
};
|
|
111
|
+
return svc;
|
|
112
|
+
});
|
|
113
|
+
export class Profile extends Context.Tag('Profile')() {
|
|
114
|
+
static Default = Layer.effect(Profile, make);
|
|
115
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const asRecord: (value: unknown) => Record<string, unknown>;
|
|
2
|
+
export declare const first: (record: Record<string, unknown>, ...keys: string[]) => unknown;
|
|
3
|
+
export declare const firstString: (record: Record<string, unknown>, ...keys: string[]) => string;
|
|
4
|
+
export declare const contentFormat: (doc: Record<string, unknown>) => string;
|
|
5
|
+
export declare const publishState: (doc: Record<string, unknown>) => string | undefined;
|
|
6
|
+
export declare const relationLabel: (value: unknown) => string | undefined;
|
|
7
|
+
export declare const relationSlugOrLabel: (value: unknown) => string | undefined;
|
|
8
|
+
export declare const formatScalar: (value: unknown) => string;
|
|
9
|
+
export declare const escapeXml: (value: string) => string;
|
|
10
|
+
export declare const unwrapDocument: (data: unknown) => unknown;
|
|
11
|
+
export declare const pickArticleTranslationMeta: (payload: unknown, docId: unknown) => Record<string, unknown> | undefined;
|
|
12
|
+
export declare const tryLexicalToLitexml: (jsonStr: string) => string | null;
|
|
13
|
+
export declare const renderContent: (doc: Record<string, unknown>, llm?: boolean) => {
|
|
14
|
+
body: string;
|
|
15
|
+
format: "litexml" | "markdown" | "text";
|
|
16
|
+
};
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { serializeMxLexicalToLitexml, stripMxLitexmlDocWrapper, } from '@mx-space/editor';
|
|
2
|
+
export const asRecord = (value) => value && typeof value === 'object' && !Array.isArray(value)
|
|
3
|
+
? value
|
|
4
|
+
: {};
|
|
5
|
+
export const first = (record, ...keys) => {
|
|
6
|
+
for (const key of keys) {
|
|
7
|
+
if (record[key] !== undefined && record[key] !== null)
|
|
8
|
+
return record[key];
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
};
|
|
12
|
+
export const firstString = (record, ...keys) => {
|
|
13
|
+
const value = first(record, ...keys);
|
|
14
|
+
return typeof value === 'string' ? value : '';
|
|
15
|
+
};
|
|
16
|
+
export const contentFormat = (doc) => {
|
|
17
|
+
const value = first(doc, 'content_format', 'contentFormat');
|
|
18
|
+
return typeof value === 'string' ? value : 'markdown';
|
|
19
|
+
};
|
|
20
|
+
export const publishState = (doc) => {
|
|
21
|
+
const value = first(doc, 'is_published', 'isPublished');
|
|
22
|
+
if (typeof value === 'boolean')
|
|
23
|
+
return value ? 'published' : 'draft';
|
|
24
|
+
return undefined;
|
|
25
|
+
};
|
|
26
|
+
export const relationLabel = (value) => {
|
|
27
|
+
if (!value)
|
|
28
|
+
return undefined;
|
|
29
|
+
if (typeof value === 'string')
|
|
30
|
+
return value;
|
|
31
|
+
const obj = asRecord(value);
|
|
32
|
+
return (firstString(obj, 'name') ||
|
|
33
|
+
firstString(obj, 'slug') ||
|
|
34
|
+
firstString(obj, 'id'));
|
|
35
|
+
};
|
|
36
|
+
export const relationSlugOrLabel = (value) => {
|
|
37
|
+
if (!value)
|
|
38
|
+
return undefined;
|
|
39
|
+
if (typeof value === 'string')
|
|
40
|
+
return value;
|
|
41
|
+
const obj = asRecord(value);
|
|
42
|
+
return (firstString(obj, 'slug') ||
|
|
43
|
+
firstString(obj, 'name') ||
|
|
44
|
+
firstString(obj, 'id'));
|
|
45
|
+
};
|
|
46
|
+
export const formatScalar = (value) => {
|
|
47
|
+
if (Array.isArray(value))
|
|
48
|
+
return value.map(formatScalar).join(', ');
|
|
49
|
+
if (value instanceof Date)
|
|
50
|
+
return value.toISOString();
|
|
51
|
+
if (typeof value === 'object' && value !== null)
|
|
52
|
+
return JSON.stringify(value);
|
|
53
|
+
return String(value);
|
|
54
|
+
};
|
|
55
|
+
export const escapeXml = (value) => value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
|
|
56
|
+
export const unwrapDocument = (data) => {
|
|
57
|
+
const obj = asRecord(data);
|
|
58
|
+
const nested = obj.data;
|
|
59
|
+
if (nested && typeof nested === 'object' && !Array.isArray(nested))
|
|
60
|
+
return nested;
|
|
61
|
+
return data;
|
|
62
|
+
};
|
|
63
|
+
export const pickArticleTranslationMeta = (payload, docId) => {
|
|
64
|
+
const translation = asRecord(asRecord(asRecord(payload).meta).translation);
|
|
65
|
+
if (Object.keys(translation).length === 0)
|
|
66
|
+
return undefined;
|
|
67
|
+
const direct = asRecord(translation.article);
|
|
68
|
+
if (Object.keys(direct).length > 0)
|
|
69
|
+
return direct;
|
|
70
|
+
if (docId === undefined || docId === null)
|
|
71
|
+
return undefined;
|
|
72
|
+
const entry = asRecord(translation[String(docId)]);
|
|
73
|
+
const article = asRecord(entry.article);
|
|
74
|
+
return Object.keys(article).length > 0 ? article : undefined;
|
|
75
|
+
};
|
|
76
|
+
export const tryLexicalToLitexml = (jsonStr) => {
|
|
77
|
+
try {
|
|
78
|
+
const parsed = JSON.parse(jsonStr);
|
|
79
|
+
const xml = serializeMxLexicalToLitexml(parsed, { compact: false });
|
|
80
|
+
return stripMxLitexmlDocWrapper(xml).trim();
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
export const renderContent = (doc, llm = false) => {
|
|
87
|
+
const format = contentFormat(doc);
|
|
88
|
+
const content = firstString(doc, 'content');
|
|
89
|
+
const text = firstString(doc, 'text');
|
|
90
|
+
if (format === 'lexical' && content) {
|
|
91
|
+
// For LLM consumers, prefer the plain `text` field over litexml — easier
|
|
92
|
+
// to parse and free of markup boilerplate.
|
|
93
|
+
if (llm) {
|
|
94
|
+
if (text)
|
|
95
|
+
return { body: text, format: 'text' };
|
|
96
|
+
const xml = tryLexicalToLitexml(content);
|
|
97
|
+
return xml !== null
|
|
98
|
+
? { body: xml, format: 'litexml' }
|
|
99
|
+
: { body: content, format: 'text' };
|
|
100
|
+
}
|
|
101
|
+
const xml = tryLexicalToLitexml(content);
|
|
102
|
+
if (xml !== null)
|
|
103
|
+
return { body: xml, format: 'litexml' };
|
|
104
|
+
return { body: text || content, format: text ? 'text' : 'markdown' };
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
body: content || text || '',
|
|
108
|
+
format: format === 'markdown' ? 'markdown' : 'text',
|
|
109
|
+
};
|
|
110
|
+
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Coerce an arbitrary value (ISO string, epoch ms or s, Date) into a Date.
|
|
3
|
+
* Returns null when the value cannot be interpreted as a calendar time.
|
|
4
|
+
*/
|
|
5
|
+
export declare const toDate: (value: unknown) => Date | null;
|
|
6
|
+
/** Local-time `YYYY-MM-DD HH:mm`. Seconds are dropped for readability. */
|
|
7
|
+
export declare const formatAbsoluteDateTime: (date: Date) => string;
|
|
8
|
+
/**
|
|
9
|
+
* Relative phrase versus `now` (default = the moment of the call).
|
|
10
|
+
* Bucketing uses the same thresholds as date-fns `formatDistance` so the
|
|
11
|
+
* output reads naturally without 1-minute precision noise.
|
|
12
|
+
*/
|
|
13
|
+
export declare const formatRelativeTime: (date: Date, now?: Date) => string;
|
|
14
|
+
export type DateTimeStyle = 'abs' | 'rel' | 'both';
|
|
15
|
+
export interface FormatDateTimeOptions {
|
|
16
|
+
/** `abs` (default) → `YYYY-MM-DD HH:mm`; `rel` → relative phrase; `both` → abs (rel). */
|
|
17
|
+
readonly style?: DateTimeStyle;
|
|
18
|
+
/** Shortcut for `style: 'both'`. */
|
|
19
|
+
readonly relative?: boolean;
|
|
20
|
+
/** Override "now" — primarily for tests. */
|
|
21
|
+
readonly now?: Date;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Format an arbitrary timestamp-shaped value for a readable view.
|
|
25
|
+
*
|
|
26
|
+
* Falls back to `String(value)` when the value cannot be parsed as a date,
|
|
27
|
+
* and to `''` when the value is nullish — letting the caller filter the
|
|
28
|
+
* field out instead of rendering `null` / `undefined`.
|
|
29
|
+
*/
|
|
30
|
+
export declare const formatDateTime: (value: unknown, opts?: FormatDateTimeOptions) => string;
|
|
31
|
+
/** True when `value` is a `Date` or an ISO-8601-ish string. */
|
|
32
|
+
export declare const looksLikeTimestamp: (value: unknown) => boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Format `value` as a human-friendly timestamp when it looks like one;
|
|
35
|
+
* return `null` otherwise so the caller can fall back to its scalar
|
|
36
|
+
* formatter without committing to the timestamp shape.
|
|
37
|
+
*/
|
|
38
|
+
export declare const tryFormatTimestamp: (value: unknown, opts?: FormatDateTimeOptions) => string | null;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Human-friendly date/time formatting for readable CLI output.
|
|
3
|
+
//
|
|
4
|
+
// Why this file exists: the legacy renderers stringified API timestamps
|
|
5
|
+
// directly, which left raw ISO 8601 like `2026-05-19T14:32:00.000Z` in
|
|
6
|
+
// every `readable` field. That format is precise but hard to scan. The
|
|
7
|
+
// helpers here keep ISO as a normalization step and emit a friendlier
|
|
8
|
+
// surface form for human consumption.
|
|
9
|
+
//
|
|
10
|
+
// Two surface forms are exposed:
|
|
11
|
+
//
|
|
12
|
+
// - `abs` → `YYYY-MM-DD HH:mm` in the local timezone (default).
|
|
13
|
+
// - `rel` → "3 days ago" / "in 2 hours".
|
|
14
|
+
// - `both` → `YYYY-MM-DD HH:mm (3 days ago)`.
|
|
15
|
+
//
|
|
16
|
+
// `--json` / `--pretty-json` are unaffected: those paths stringify the raw
|
|
17
|
+
// data shape and never invoke the view layer.
|
|
18
|
+
// ---------------------------------------------------------------------------
|
|
19
|
+
const pad2 = (n) => String(n).padStart(2, '0');
|
|
20
|
+
/**
|
|
21
|
+
* Coerce an arbitrary value (ISO string, epoch ms or s, Date) into a Date.
|
|
22
|
+
* Returns null when the value cannot be interpreted as a calendar time.
|
|
23
|
+
*/
|
|
24
|
+
export const toDate = (value) => {
|
|
25
|
+
if (value === null || value === undefined)
|
|
26
|
+
return null;
|
|
27
|
+
if (value instanceof Date) {
|
|
28
|
+
return Number.isNaN(value.getTime()) ? null : value;
|
|
29
|
+
}
|
|
30
|
+
if (typeof value === 'number') {
|
|
31
|
+
if (!Number.isFinite(value) || value <= 0)
|
|
32
|
+
return null;
|
|
33
|
+
// Heuristic: values below 1e12 are seconds since epoch (covers 2001+).
|
|
34
|
+
const ms = value < 1e12 ? value * 1000 : value;
|
|
35
|
+
const date = new Date(ms);
|
|
36
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
37
|
+
}
|
|
38
|
+
if (typeof value === 'string') {
|
|
39
|
+
const trimmed = value.trim();
|
|
40
|
+
if (!trimmed)
|
|
41
|
+
return null;
|
|
42
|
+
const date = new Date(trimmed);
|
|
43
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
};
|
|
47
|
+
/** Local-time `YYYY-MM-DD HH:mm`. Seconds are dropped for readability. */
|
|
48
|
+
export const formatAbsoluteDateTime = (date) => {
|
|
49
|
+
const y = date.getFullYear();
|
|
50
|
+
const m = pad2(date.getMonth() + 1);
|
|
51
|
+
const d = pad2(date.getDate());
|
|
52
|
+
const hh = pad2(date.getHours());
|
|
53
|
+
const mm = pad2(date.getMinutes());
|
|
54
|
+
return `${y}-${m}-${d} ${hh}:${mm}`;
|
|
55
|
+
};
|
|
56
|
+
const phrase = (count, unit, past) => {
|
|
57
|
+
const plural = count === 1 ? unit : `${unit}s`;
|
|
58
|
+
return past ? `${count} ${plural} ago` : `in ${count} ${plural}`;
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Relative phrase versus `now` (default = the moment of the call).
|
|
62
|
+
* Bucketing uses the same thresholds as date-fns `formatDistance` so the
|
|
63
|
+
* output reads naturally without 1-minute precision noise.
|
|
64
|
+
*/
|
|
65
|
+
export const formatRelativeTime = (date, now = new Date()) => {
|
|
66
|
+
const diffMs = date.getTime() - now.getTime();
|
|
67
|
+
const past = diffMs < 0;
|
|
68
|
+
const seconds = Math.abs(diffMs) / 1000;
|
|
69
|
+
if (seconds < 45)
|
|
70
|
+
return past ? 'just now' : 'in a moment';
|
|
71
|
+
if (seconds < 90)
|
|
72
|
+
return phrase(1, 'minute', past);
|
|
73
|
+
const minutes = Math.round(seconds / 60);
|
|
74
|
+
if (minutes < 45)
|
|
75
|
+
return phrase(minutes, 'minute', past);
|
|
76
|
+
if (minutes < 90)
|
|
77
|
+
return phrase(1, 'hour', past);
|
|
78
|
+
const hours = Math.round(minutes / 60);
|
|
79
|
+
if (hours < 22)
|
|
80
|
+
return phrase(hours, 'hour', past);
|
|
81
|
+
if (hours < 36)
|
|
82
|
+
return phrase(1, 'day', past);
|
|
83
|
+
const days = Math.round(hours / 24);
|
|
84
|
+
if (days < 7)
|
|
85
|
+
return phrase(days, 'day', past);
|
|
86
|
+
if (days < 11)
|
|
87
|
+
return phrase(1, 'week', past);
|
|
88
|
+
const weeks = Math.round(days / 7);
|
|
89
|
+
if (weeks < 5)
|
|
90
|
+
return phrase(weeks, 'week', past);
|
|
91
|
+
if (days < 60)
|
|
92
|
+
return phrase(1, 'month', past);
|
|
93
|
+
const months = Math.round(days / 30);
|
|
94
|
+
if (months < 12)
|
|
95
|
+
return phrase(months, 'month', past);
|
|
96
|
+
const years = Math.round(days / 365.25);
|
|
97
|
+
return phrase(years, 'year', past);
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* Format an arbitrary timestamp-shaped value for a readable view.
|
|
101
|
+
*
|
|
102
|
+
* Falls back to `String(value)` when the value cannot be parsed as a date,
|
|
103
|
+
* and to `''` when the value is nullish — letting the caller filter the
|
|
104
|
+
* field out instead of rendering `null` / `undefined`.
|
|
105
|
+
*/
|
|
106
|
+
export const formatDateTime = (value, opts = {}) => {
|
|
107
|
+
const date = toDate(value);
|
|
108
|
+
if (!date) {
|
|
109
|
+
if (value === null || value === undefined)
|
|
110
|
+
return '';
|
|
111
|
+
return String(value);
|
|
112
|
+
}
|
|
113
|
+
const style = opts.style ?? (opts.relative ? 'both' : 'abs');
|
|
114
|
+
if (style === 'rel')
|
|
115
|
+
return formatRelativeTime(date, opts.now);
|
|
116
|
+
const abs = formatAbsoluteDateTime(date);
|
|
117
|
+
if (style === 'both') {
|
|
118
|
+
const rel = formatRelativeTime(date, opts.now);
|
|
119
|
+
return rel ? `${abs} (${rel})` : abs;
|
|
120
|
+
}
|
|
121
|
+
return abs;
|
|
122
|
+
};
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
124
|
+
// Auto-detection helpers (used by generic readable renderers).
|
|
125
|
+
// ---------------------------------------------------------------------------
|
|
126
|
+
// ISO-8601-ish detector: matches both `2026-05-19T14:32:00Z` and the space
|
|
127
|
+
// variant `2026-05-19 14:32:00`. Date-only strings (`2026-05-19`) are
|
|
128
|
+
// intentionally NOT matched — they often appear as opaque ids/slugs.
|
|
129
|
+
const ISO_LIKE_RE = /^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}/;
|
|
130
|
+
/** True when `value` is a `Date` or an ISO-8601-ish string. */
|
|
131
|
+
export const looksLikeTimestamp = (value) => {
|
|
132
|
+
if (value instanceof Date)
|
|
133
|
+
return !Number.isNaN(value.getTime());
|
|
134
|
+
if (typeof value === 'string')
|
|
135
|
+
return ISO_LIKE_RE.test(value);
|
|
136
|
+
return false;
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* Format `value` as a human-friendly timestamp when it looks like one;
|
|
140
|
+
* return `null` otherwise so the caller can fall back to its scalar
|
|
141
|
+
* formatter without committing to the timestamp shape.
|
|
142
|
+
*/
|
|
143
|
+
export const tryFormatTimestamp = (value, opts = {}) => {
|
|
144
|
+
if (!looksLikeTimestamp(value))
|
|
145
|
+
return null;
|
|
146
|
+
return formatDateTime(value, opts);
|
|
147
|
+
};
|