From 4c36307b011ab5939bcd49d4485784c21572680b Mon Sep 17 00:00:00 2001 From: Nikolaos Karaolidis Date: Sun, 23 Feb 2025 13:24:50 +0000 Subject: [PATCH] Add source code Signed-off-by: Nikolaos Karaolidis --- .gitignore | 2 + README.md | 92 +-- bun.lockb | Bin 0 -> 58824 bytes eslint.config.js | 11 + package-lock.json | 1962 +++++++++++++++++++++++++++++++++++++++++++++ package.json | 24 + publish.lua | 133 +++ src/api.ts | 110 +++ src/exif.ts | 48 ++ src/files.ts | 66 ++ src/index.ts | 112 +++ src/lexical.ts | 50 ++ tsconfig.json | 27 + 13 files changed, 2546 insertions(+), 91 deletions(-) create mode 100644 .gitignore create mode 100644 bun.lockb create mode 100644 eslint.config.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 publish.lua create mode 100644 src/api.ts create mode 100644 src/exif.ts create mode 100644 src/files.ts create mode 100644 src/index.ts create mode 100644 src/lexical.ts create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b38db2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +node_modules/ +build/ diff --git a/README.md b/README.md index c02b96e..00d9ece 100644 --- a/README.md +++ b/README.md @@ -1,93 +1,3 @@ # darktable-ghost-cms-publish - - -## Getting started - -To make it easy for you to get started with GitLab, here's a list of recommended next steps. - -Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! - -## Add your files - -- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files -- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: - -``` -cd existing_repo -git remote add origin https://git.karaolidis.com/karaolidis/darktable-ghost-cms-publish.git -git branch -M main -git push -uf origin main -``` - -## Integrate with your tools - -- [ ] [Set up project integrations](https://git.karaolidis.com/karaolidis/darktable-ghost-cms-publish/-/settings/integrations) - -## Collaborate with your team - -- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) -- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) -- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) -- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) -- [ ] [Set auto-merge](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) - -## Test and Deploy - -Use the built-in continuous integration in GitLab. - -- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/) -- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing (SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) -- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) -- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) -- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) - -*** - -# Editing this README - -When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thanks to [makeareadme.com](https://www.makeareadme.com/) for this template. - -## Suggestions for a good README - -Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. - -## Name -Choose a self-explaining name for your project. - -## Description -Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. - -## Badges -On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. - -## Visuals -Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. - -## Installation -Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. - -## Usage -Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. - -## Support -Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. - -## Roadmap -If you have ideas for releases in the future, it is a good idea to list them in the README. - -## Contributing -State if you are open to contributions and what your requirements are for accepting them. - -For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. - -You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. - -## Authors and acknowledgment -Show your appreciation to those who have contributed to the project. - -## License -For open source projects, say how it is licensed. - -## Project status -If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. +A small utility to help publish images from [Darktable](https://www.darktable.org/) directly to [Ghost CMS](https://ghost.org/). diff --git a/bun.lockb b/bun.lockb new file mode 100644 index 0000000000000000000000000000000000000000..1df5534e36f8d17473aa96e1162eaa03ccb03fd6 GIT binary patch literal 58824 zcmY#Z)GsYA(of3F(@)JSQ%EY!<4P*c)6L0G&Q8nBN!3luFUn0U(JeFJVq#!m*eLeM zy>iC1)y^kxIGuPo>DYlj`}*#A-uKdGzMCL;U9i*QDFYbH0+S339B6a{l)q^Pl)+F? znv|1SoWan+z`(%6z|f$>$iN`Nz|bJW$iN`Lz|g=0rT;Q8Fz_=lG`xe-_o4I^1_lOh z28M>>)ZDVvA_j)6@?x;j^O+&yS>=gfz5xpZgD?X_gBS|~gA@ZpLoP%aLpcKjgCGM# z!x~nIz0+A47(^Ku8gfc2^79xN7&219+8Mf785npO7#iwWA?{3NWnhqGU}ylT;bdTF zh=rO1Qq9G{(2!c3lbKh-z%Yl4fdRz-&js;MPHGuQ3&U5a_#-I&hKqp#WKK$IYJqNM zF+(pG#J??2@%KCselRb@JXc-@20jLch6*l-dxE(b7*rV;8ZwJ@({u8Z7>@Em)Om10 z!XYy~FTW_2fx#BaPs_|p(Jd`t_`?s8{|KcEKvADql3JjflbM@Y!thZ5BCiW|f0H1@ z+_ap;l9JREhV0bJGLWD3gdpnVpzcmBE-lH-DP~A5E-Xzgs$|dC#GGsf28L8|NO)x=7VDRR!K-Y`6vOgzqBY*H>;R|p`bLcGC4mb^&BSygAfBl!$D4n{7#JiN7#h43A@-uD!!=3}`!iJ_{#dU9;U^U(CZ`rNFsP|Q#B)?3;l585 zBA=UDl%8sYNQaRs5dUvdgZSSQ%I8vt*gI7n63=Og#U+Id3=Bo7rNxq68gL+O^HLt@LH<-FvutxcbF z^2w$J7k3q04pm=IpXe@&X}VJs4-1{#e6oI$ zQ~Da!1ANa{x^GVTcvJbTYKzAk2T_5IyQ}*1X2>%=iVJ-At{`&%JJvb(-fEoet>F`A zx>^w)c*QsN)5(WNc}vEd$>F-@U&l|z?S3Jm2KEtE8E+? z#s>8KEk32CzufM?v(}_qqnyp3Po2+5Kdu?yQ2sED|KfuCSJrKlU-j>W(wFZgqBCxR`8S`Vaa4jjq5X8iR9?`DnEY}+Xvg_rLfUncIFCAO!-==r+J ziQ$h5zt5QBaOBF42#K5D`%`u;c=B=fOqG4Ulb3%iuW=B)Kf`K2M=jUOa9R5WJ-;p$@Beo-$cFPwZr}IC{-4}d=!D-2 z7J7C4{NqLgAF-Glu7{J8&#HeentUf?7jMMvQ-1n~7D=8uERs~HV(Y-oZ*ehV)dkNl z^Phh8pTg&G$IG$B{))8Mg%j>KJJkN!MR#zj^EMod(e7%~v`gwV=VOgM@zP{X>4tl6 z?BbaATzSuN|I009(S<7?+gllWhe$QvUlY}?SY^5O+tEwh&yuPq>mG#37yD#~CO9{> zim^t@`pGt*Os#uaUKIIox&PXWkOl4J{4k=7`ia!*lGh|89b$@@;D zvMu{h&xakiOE&tQ;wat|x_TWG|3_H3pEz^wl~h2SUfNCjO`*CLPgly`J#lrD>o=x5 zZ8bAGjq@hn_GH|2F`@mOsE6Z{FIg|p)1jgL64jb!L8;fdM`lMn4?G>3@aF%o35nkh zh}>Y?b@;iw?dJmCE&yQ%4PhXY^&0(69HUX9n_v_5QWgRNv zb(Y8XO4Qc(n%yZ=HtxA~@_swJ<~-lUo0eP4d&E^u^71ZwU&K+BasH*xq7zs--$B_+N8a+7ZyH$f((8%3a0kA zh2(YW*8REWHvjIbMdG`E80Mt~BsD~AF1ddG^XlBEuas zk9Dh7SF29ckLp$1YHP`vZ*%Nb;H=%J*fjTg8#I2Yev^K-L+G{A;?pxK=Vx8YWCT_2 z6kt%R5u|+z0|SEt149ERh{M3Z027CDaZ)gK4vY*80SpWcEKrqfP?``0(?5Zcfx!bw zKg=9r_1}Q%XNGEk(NHc<3TD3o69a=AlKsSnD@=bHR6opK7>(0ns02*^8mNAdzlqfg zG6JUm6B7f29n}3Gw}UW9o)CuVw`FEvFkxV5U}s=p0L2}#Zh+}8WM*K{V_;|i=>v&@ zFd@4^@-TI)m?7?Gg{D1H-Twk=KS&=a3_&!=d_ovzzcUL1gFgd9156(`A&pRZnEpvD z3=AB}q#neTVr5|PW?*QL1#w8gF#QKu z85jZ?7#et?dO>c0GVxL{{eo-^48chH@oGYngy~OVV_-0XrXNrmKvII23DZA~4HAAJ zcf-W+YJ^I{)E;7EU~s0u{jBT^43<#$gVG<&T&OiTDVRDtc1ZjYo9VpMgAD^i11Ro5We7~1So^PVFff=x>mNZv znvmpS_AKLq*bkBd)nTAG0I>;Sn0|h4Nc{ujgUUccdO`9q_2JwM3%;sONw5QKA8TkJP`MT%1w|NFmaF?d>E$g9}feAJJkOm{lw-&nEqH^MErs5 z1DOG$31OK2nY;`PE>QbHdSK!pHa-kfcb6B^eglO&DDA?;@#zK0!PLp}F)$P%*-xtb z`}rX853(O*E+`&AG$9PL|2!Wg{6Kt=-5^XzFGwDw9z=8VL)s4@Hb^hb9UwkF4Abw) z4{85^(mlxSFmZf(L2@v4wNU#(VUH{hV&lUw{k!=g;RmuGpL%pTn0|Hvi2sT8H_RNE zepje|nEPS!glL%lW&s8U3uyUINDs0+O#f~Hi2p%h4~jozHTc*t{ePhD2dO1Tzm*`Q z{0G&4ATx-?F#CH1A?X(u{=~WgrvHQ>!vCN!fQb`w2Ta{RK}h`rvLBR&Kr|t}AbFU6 zOCd=50oe@_C)NGsLXh$sq#vXgM1#yBgkkpY6@vI5#wS((XQ+OV-K3;pn0>~=koW_I z9kF(U{07sXFAQnFfX0o<(Z5!hfx!>je}kC`GM^BJsr@YsssCW^hlvxbKSG3o!I*)e z0hIpv2x&r+huPCD0;zvM`e9;7%5XAaYOjbu+z(2(DLg4*bj<3klSJ6gv@}c zD;9^ezd-Q^6C}{Fy#i#UpQ%Xtg~=GdI4_Ox zKRNLyAOk5sK<)>HDJTp35NZwBJDfCRQ)9{wi5S{sD;-YX;2zO|l68gZuz8gBT3c&n*Yh z4{|>-`e15c`n}~KAzw7#PB!?Qe3%Ur#9_`mf~Z_fukEh@-&% ztx60GAr$E6REE@lp#B3n?)O)QjAO8cOAAvOJ-gt{LVpCEUD%prtf z>iJb5_Jh(52*bpQ)nBXvX}^K|4$=b(M-WX2!|d4swI9R>*-xtbKdC_4PawS@J7F}4 zjSqwD1IZhxLfj8(|A6cUVURc>4AWnw3aNi#@kXltoX({HKJa-u zc7xmo@&||}gkkDK)e-p@CPqjvNFJtki8`eGfQ22Y>F<>~Wc&l9AJqN=(IE2)VVM20 z8j$u6Odlu>5Yh{hhv^U2psf5~sR8N#fa0B;^7E$#B>c$LZ?6f-KOp}T>vv@TcWOe~ zAE0;x>4$|2NDV#=Q+E)mAJlImr40nr|5FoEe}U31NDSl;eD=cRKx#qkLM=%AgV>-t z1SSsRbs65R699>BL1xkA$x5L(f zK$YR8Kx#o4H1;hHRY?gAa+4&~91smkPcqOxJ#2joG7XAr6{vbjXpp}(q2_>SWd;TY zALv*_5L6sQgWMMabx$Z%93Ksek1*)ErUI1E<21$Wvka#kP!@$6RjRwi5K;=O+C_X@I{Xs$?8YB-|(+}c< zXi)fMLG1^v?FWg2Xpp)b1_lO@{(Pu>0Z5X8fdQEYsROMg2T6fwka?i(ARzG)kRSsC z1BeFkOQGgKb_FmnfM`(tQ3X|pOoP-{L&b@qLH5={)g#j&bLybtAR1KWv_ttI8YB4Q2HQL z9f$_WAA<5hG>Cr~$_LRP{~U$puhUTaB2*q54I0zD4z=edR2?!6GXEA-97Kcox1sL8 z1Eudmiz4}!1ddj2~FB4YkEABG+ z8Qj3vaQxwVHjW(~@tUiIIHxPE+u7jB^@p?WP7wb@BaON?p6BbVYQObQdo;;g7Rg*t z-2ihe%R<%9nK>&t4nDEbJ-bIx%BK8_)&a{|pI=-25QwmrZa(>Y(YCVh&L(UL`}WycI?8f|~KMwk_Aejqld%(@*kB%mXn<09xJ_Cv4nm3)-1U; z??TD@asR|l#I3Kn{5L=i$y`vI6lOHbWv0Sb&yTX|J0H<-AA@u zdd(8P`s+5UJzJhip65HfRN-1^#dW^zyDIM%{F`3!eRA`XvIx(cf_}%gfc9E5GC=Yh z^14)(b#pH*_B&O0bD8GG^^J=Q9hI`*Zk*~FY3Y4Y>q`8}rCT!I`_HIc@NqJqbNOL^ z^M}nZcRWwZox0<9Q(RVF($A(?B=>^al5qE~Qo8pjKrrMDq8PX?;R!5I_u8s^7Wv-K#WlLg4$7V zbLXy5y(^}jbNkmsPC1pX+!vQ3PoFN(k^i>u&aV^qBOiV7lDTK^ILSkB*NPUVOU+Dk z=XvWTS6VV&&wLhPvoT7v5XrsB{bQE1QXw5Svi~Km7_9mYSdM-AZX5FJNs-^>%bPon zd#Lv%In1fN99kS8U44IVa$e^ol|%2n)+~3prsP$3{BpRUl?jr$pz$NPdtXEr@7!`a zCHLgb^=CsAR4Y7^&#lWi#`q~i)%IlT`-0v23&Yyg*1vf!dgArh>-MXQ-^v#)n*#DM;pm=CtAFvgWp~-=&i9H`jbqhoYd-C+3Y`GZdEmmexFb(1&G5$qu1i z^Gfv01Ge39{pQnb|8{rknx#|L&%Lz$Ou`4HBOh{|q2|^xg7?P5jAdZ?Arg4bT5j6Z zbtOwmrd<$ws;c|d-i-f+vBQ1lk|!cfB~v4_suygS=aUe)?AnF%@62Cxr2k9)JgfiX z+`lsWzZhJBnhRRQ4jMy*8O-w7fM-YGf}=C}7OQZaoy@*J@aaCDPVE8}vlR!L@)6JBCd`cGU%t0q zx_;S#KHcArNaljZZQ$mL|8<`0*uQA<#57&??g#7FXs|m^ED~V6sH>p4v$bc|wo8Ya z%Z%!OPky-c{t3>xa-U9^rMyhKen@ded5~Ykg;UZ<=8D1$1(oYU+oxTO*yGQo-`dK~ zT6&Rrc5d5Kvr~m@QX-=lD|5;yOo?sV#$;hI$M;>1aOdV6PuGy3`TPMD++S#~&KKJsnU19}D=EBBZK*qB;9IgC&b#3xz*^sX{H-BVUw&qh? zRrBgdxdgA3-cKLjdNKFpstq@8+SjJ*Y&Or{(fYV-RcTmj@3|A&*2}KQ`lg8^nJWP@ z5QUXN?DbMETlDa3aYI93g|1yzD#+ZMS)s_!aD#@o&!; z+q7xv*EU6^&HaXCE^M3(WHt+r>bn>14_BVld28mCT=;Q$&8}55BAzNm&f40;w`q=` zNMCGy3tzwMjkVVFPq#AdUa1J^-r942;rgErf{pD0 zkB*1?Y|j+XL^4+%WFQo?#Dz~;cs}ny>Z{%y@l2PSOgU!?!YV({7hukQe>_zCxsXh^ z=n4Dx+;ajRxXlc?@4e>L^;KojDx$ylWxnHje|beUlDP^{GeH!~K6hrjEs={uBz1TG zblKRyjQ`q<7OQ8g#G=I6H2>dz`YLsj+v<4Fz2{G5a;~nJ6}&3w=*knfXEeOM>aKM| zR_FRNBy$x(20}3l%iV44(`OkRvJE($ye7S}d3saAFDDyQqYFXdx!qF!b8Z|n^eb}N zc~^y5xGHR7l-QEetN8`li3fQf+DJX0P>Gz6m7r#VD3%4ypC_H1+}wX=oyM$$EY>a*#)z*Pzt{TXpP|WgI>;1#q1}B*v>pI+)+h@FN`z~#JX|2O6mbz@e z7jEv`7H$i>p;cOVIPH+gk<{J=)`Eq%V&@xdD9l^K!Su6eC_9;Hi7jaH`<$hu5-XH4IA8v8TtYw%lU9w%aXWf*{Xcj4Z zCRIg6&em2-dn9u;Kn6lF%Y{|RD@#K6Z8PFJ`gJo)eXP-zz3Zkrx$wL=obl$@wD%i| zdN?u|1#AR=oxgI=PAh2f$CfRr4r^WQZv?$4O0Zt6h-5BkoEvH^0}Jc57pGrVZ{a;B znb+E{=pN<#Q}01hf5?;jMUPLdl{&6-ck=!Jsv6sNm!H4NvaCF)_t3hj7R%VCaPN7e zu_jFG7qWY`V1|Oq$)hiWvK5ctUzQ-eEvzkK_tM-SE!WOYKfHMVq^YxeH* zG=K8#o79HQb~8)+(gaf)r+;kOvV1*D_QRJ!UP$iMMmE=T(`<&Dysv$)c6S^}`)8~B z)YGWWNSa&v>>Q0XUPeONYd73|uG$`LUAMVTu=6D&^O9#CVh(TTt@Cu3TtB_y%|aw| zb&$>YS^w?rOZ%hqF^P}Y{i-}xcV&l<$GiWr+1r~s zZH*Ojf3AOkWUfB4x$n=PkF_nBvgvT6&Ed_vl~>;=KJR^<|IPE?dyJO2@2D*?bC)to{4W$fXN^E+|~Zb1I~N z@{@fj#g12P?&ciwnXfnZRF#9&z3cyO);o4>_i1l=RpY*F+KU?-H_h6(6FFZPBb)2n zJZsL`57NwT8z+|)hTO2>`tW$3;oOR!t5RF9Zq#5rXIJR|V*19Z?Ym^Fe!RRA>|XZp z&YfjG1>t!|{lbe^ce*0E*96(zwUIjePM+nwRKG}Y&T9$&!^$;P;!Vd+bQ_=CxWoVL zzvQVarf;x&ymgk+OP>-YH&8^=qBl2mxXQBIY*~);%O)m3~sl!=`dxqTuqZg}awE-F)zSvHOl! zO?UXtbPLAuEy>h{uIZd4IY!|Fuq>{cBUDdeqIk>CVKS;A~{r?^7Cu@c= z?60d$={r+B%Vb^8s*dGI=2{|~`*3R|>zA@w%P;uM+!sFE-}d^ywAl+gx-aiwsBAtd zv$lBVQj`DAJff}%MF(p$&h1+&^!RR0O+m)Yv@dzfl(XeMk<7J1Hn+ZN!nYQqZ?+9B z;``;VI(?7fp0U7*)Bgd_%Tp$7Y|o?(tT%XT+1kpUb5FSQw;;*xw`3-dk^=9FwP9z? z8BJ;)BAIKAZ0^KpnU$3m7Ftyw)k?}dSTrMc{XenzzAqPR%E{HW{eQ2?MVf5T?hQL> z6E)>-oLhor`c;uXTmSNJ4D(8@^&HX)g7i)^mJ zaR;}jF3uLczK@(gSn#aMC~%K;bmhOp@_(h?HCa!~`*#lUef${L(d?32`z!TwYOVio z=Ih@ZeJsAnzHmDI0eKwJ4%ys`Y)l{b?vPEhIcJj2YxK)a@jmYcvH5FX7#cvaKOt*ZCVpHi9Y&xG7RwnsKMSn0Xv3*YT)Tg5~+ z^v-mj`ATJ}(9Un$jc?!hCmw%`kL^jW$dmU>GGA-%E?mtMxW!k)*kmPl{SToEwq33j z3~{rO!odOA+;!`7eoRiRcVcR@c8-@!Ro`K8tu5uLX85+b-10mPPmeD?RQFyhY2~BI zue+DARQ&#C>$h_Qw_M}aw@RANcml3BBAM%mY;IB9u5SgMz7ulv@0}Cx6miN+cqXuI zmqyBkI}vvic^Pzt6EAUxIT*=nHq5(WDCJqL{ko*ENLzlk(&;HX;v9>R+rLi8=Gs(- z1%BK$?em@c+UK9}@tZ4nU(J`FU@B9TWOik$-v5VPDN^iz^YUK3W-%?*jtI#))w+{G zRaqp1L6obq(KGrFl6#$z&DBajrC;_-`bqen|Gd|JefL|NqW_CYjPsjm>yd4X|NPne zQgc-ZbK{B*b!MA#sr5RM zGAA>`7Q3C*3l}g;;FQgecy_as|9s=FDppoz1@rl{-wL$F)%>)KTqhE{X`T1u#`b8ciPUKD*ZwXNghw4M7N zui5pXz^ZMpa51CKrXJr8LlISu`?m8hCLp=j4cXkCu@fwlzs=2gb2_bO`LT(eJCeTM zEEb>mpnXbvhP+>=Vao}VM=EnRUV5|hD(l^oX>S>d^Rin{o^_g&UA$S-y?hUnxuA30 zU`De{sL8%*KC9R9-%8iYrbr+7_={6hJ8(y3<@&fYY6ZTDz6oK8K2O&! zGr0a|=JI8FynVluZr?U{PXnDT2@3}gn4zHh`2G92V$w0kYk%H*5ViWiVfW^|BjRh> z4R)t8FPv$@F{e=W<(Ad4H(E{`te5t?`M6c0WXr-xpXu8t+nf_WK@tGrAOa+#cXl!IihH?p}dH9mUtQ|Bx^ zCM)^iD`TRIvUCEw=(4)Kn^M|}*51FrCb=;*%unLSsztYbc4ocRR{kJAUy;{kha#JD zrq^Z_Ib|esLF-&#MzbtfdAlWMqGDCZ&NqKLgw;;C?U=jchr$!1UE=nK^;44fUkmwe zpzuDFdEFkn_kt7N{kxF&%VUdQwT>;<6qDBxJ7yr6>kBg!6mP30H0+hE-E_N8?t9m! zy5n;X1@8M-a&3`R!I}$G-v86*iTVCk>o(J;{SEi`-8cDT{QTCeKDI-#d|C=IDYZXd ze{e@K*ALlTVNFi|FQ!^1*NSdsHZ&;IDsS1cG%wZTVZ*7f@4P1ON>+Wr{{D`W(A1~a zE2kPSEI8`Jc$r6!*Q#-Ux~RF(Paj((bN!LcJ-eZHjgo_Z)a^c1=lqN;jli=BdlI(! z^9IU9-f1h~FRU`V6CFE+XhCEqH#6`@uxM>8Jks z)ja&HaQ~P)=R#HF@sdDfb4%XryTd*0?%EBe+uW5NOD;86=*jL5{@tkmdv@W(TFbJ- zi;v%b@ZKnFo@2fHeDBjO3%e{=Hx|CVW^2X57RR82JZ=wK=L9pFrN8{D9{1(Nb9ZOe z%s4T*&9^C9`t|dM~DW=R2-DHv3<)k}P-9cV_4nsQqAyK~H(@23K;7^vyp5x?&5`qoH3Ds*e&Et{y=r0$S;a_aNi9go+1 z@4oq2Tzk!z&By0xosVCm-L3ZXE*p}$p!E|lqgnQLR|nXJe9f@BUa!-#+uQ29!|wOY z8}uJdiFOGoXaCJJw@}MhIG3STdG5}CJrf0^TK2gnZ~eN^@404}>(*a;#E{GlhZzcT z@AK#9Km6wrxv-B}&52*?;H{aGA|E%eWSw$u%Di1qz4zsd8lKWNZClT_inUm1PcF|7 zr(@3_wXz7l+;PC@ez7(iaz7TfUJYbC3uEW)Yb8d>{khdo0~sUK&VE0P7EWa3E&iOOFr*va6l6zt6+rZ`)@9-6h zbe#I~s82!4>2+ys>&5sprOmHcHTg>W`Kx+J-+4Yyd1=WKPurx5-*cDU`^Wr2VBf5pe0ZzQpDgkIeqQP6s@mDto}X}3j{LbZ${}s*_tP~F{uUhku=DoIR5>TxH(N=xY^c;7hN_@;8+O0l2E ztRrU>vT3MwKCX1Cu}$k1G7Z?P$+Ri|;gpP=F64P>(0V|)xhgU#f-(!{Z^-|XcJ`_C zpP;zzFwaZBCuF{qVCMe3r@Vp>q7R;oV#8(*YfU{jn&vP z=TK(mDWCesMyEJkul>BaNXn&o&XjFP;Q(5v2shVT+@fu{&NY{Jmz0?Axl8iqo|IcV z>;1-aU;Z=MFJoc;m?>ZM^rP#MTl@dKl=+)5>D0`(f4y&CdGfTkM{oLcht>Cy%muA` zgqz#6d2z_)8vBZ5wfcJsE=RBP$X-vlJD<^cvASHqq#KDhHS@W)&)Ko>;jh&`%)Q6A z9%k6JR6lpi0sV`z3i}<6?2+fCA*WYEOlEoc`OZ%{rFO3V7x!-|{hfb}nf+7n)U7G( zix%uSv2KNjY3Rb!%b1QIT4gCdZ^aBF$)1Dy&jgBIz71bE?e44TD_y#f+zVQl2{Dy{ zf#uMg$jz5Cj!x?oRB}_C@`=Z7#OWsU9H`7Vf7R`8N7#4|?|XIXE|R&Rb)g_LL6~Ll zGl^EVef%Me+LrHI9Y5wDzjZ9?Lr_glg@H8FJj*w}20AxZ+}u1#tKtTqu=3B2hSh5e z*Bo5v+{~_-?4aO!M@e~swZ#06*EGvhtfa>LNSXZZ%d?9 zK=}C=KX`1CR>!AKzHcfzGs*2yiGRwoU8(;Bw(q-mob}_f8|{x?EuH%F6tk4q70n5T z!bLB(&y?9HnTR|t2U-scHI{+J{y;L*(f6tPQx!}(u1C0YGU~r?6Mnr-qL=Rv7_vVI zUgp~&ad6Xytw`aU2{RN_Ur0TbUFEasER&_nA+ZS+`MybqPV6%6xwL)1rO?TRhVnDn zZ4B@1*gMr~;^P^|>`vGBa<9)~Sn`5xWvq{cM8%fXUy;lOt^b7?%~IFbvn?+zT4^!! zhlQsDB7ATDoRO5lbK}q(y&b!Fw=h@>@?;(OJnihgN81e-&90Ob-1UfsQO@^%QTDZI z&mPCt+aQ?>T1N~wcd^^UV?}?y$vI}e3%(z(F4kBqtv^F}#{Py(4I2)XnWnKWOCIg7 zs!=IlZ>N0Am`ioR@*`bp?Niqi`UlWDKDfCYYEC?}=kAdA3vx_JPEuGl zInqdXDJuu_tu-Iw9F?-;<}|)t*=1&*S+VzplFzCKXXaO)EDOGRh0klvMDc^4&Yneb zZ!XMGQ21^x-hOal$eN=y_Fr#_|DWkMH~ecsWOUdL_Io$ynXukiCYBNKjrq_f5ozs> zUlt2xJijYAjiGz}wDNUnxrYMQK1MEoL1(eUjAl7B-A=cB(J75JM}E3Y^ttlt^e6A| z*9vdXG)sJ~y|ddcU!vJUFI%?vPS}3_Zwszw&9<6#SNZjf*UTc*&)>R{zbFgIz4)l*7KHij$y_`tq!q#_#jAvORc{!qBZf>*LG`WZUs;`dueL3_?{odTx zlPe#kJ>T@y>W+j|d}~yRweYN0JlS9QBU&eVo&0uWzPQ=LnOAeSs&F8g3tArzG82SZ z&L1_qnq;|M=IPBczUUo$TV@n4Skx;WDHGgMeB$=0wbz_y?szg|L&nD)avhJ-nEjS? z8=ZK+{t$-%gH*^NVp+qq0N;&RU8dcSYJJ6#SwkS^?K?f4~R5c_2L)5KOJb3yCap~f<>ynDqE zbYSu8RdY?5GvyQa7^@te#=d%2zFzL%t(!|O8J9lOtnQfq@SB(V!#^(D=6vq&Y?-K< zpmOoUlV}Hi)^yE!By&q(hJxa)ySztw-xZ$&Us`7*xaw8in*Halz^+{`4cv)6rs;NH zZ_I1|znJG--qXf?CZ)eZE?kslXFrrJEPLAj%Z_O7(v9Ir=7QGO!;EH;Uh*Zi&gK}G z@$r2%@4V#Vuj$;lX7nO+{r}H@z8%!9zFK=oNNUMp*GKAJ$4a+3U6`Y%a&7AV4>fAD znJ%zKNjNS>G8eQ?A8u}30moi`Ipdd1FPCy3U!&)wBf6>PP51TkXql^bqQZOtv2n^9->&FovGAIfV1|Oi*S1EUO-%2SfRjBx^L$6U2s7!o%fFW@U5!|=T_*d_gLc=4HmwVEjW!F$ zUdS>$yz|ojmjCvKoX*k`8$4P$R+%A>BUU1ttLpo$zt(f;ESDV^~(m z&ieS9V?W!ZEfo$C6XinhW?W_Xb3xVW=X96YlI~n>J$>nA z8{VJuoa%g|TS+hK#qW*xU-H%n%sS=aExX{%sfth2i|#(yW3QomctT0fv0bx%irKgD zzE`mRSy&uUTyv@rd3*=9ZvtdIOVYuW)urt^>m`%)bR#Ux)OGLW%6wZ}Wxy}_w7l(! zgRDv6`LF5x4p9YLy6?Q@nX$I(cfj67wwjARX>GS_5#55E-)cYxLNUwh$-lepraxEI zzIsNc>5F0RCWE)0<_&LlmbB-lcOBmQ=vCWxPy0uY{?t@$SK4CY_U}f*rnwJ#)D%~( zVL0S*(`F-5IKcL8fQ)8&9I&X)!>anor>R=8Tdm@BgSGZ5xP+U$-BNwT#qP)TSLx{o zc=!97JUHV0xP;5b((CKN^=9*WQ`17&!j8rE6e7=Of%biX%miVUPwTD^*Y)oeX} z)m~rW^5qz3w%B||msR5XBWG$TTRi_?a*UtbkFz^mTEN`;wEwy3#vBR{_CNk+KWEFu zNq)%dg6csALNN=&BLz=}dCQa{4*WgfR=Dx^)O5eM?X2y^ImthsKKgL&*Y`8B?^CvK zs=S-SeIq^h@SN{oRvRl@x_r@|knxl(IGk>Ifx-|Lg=V|w(_)bsLs5e!-6rOrub7cCtDjDSY z*hZM4pnS1!?GCoJpJq=y;*{H~q4oV_!H3>+@BI!6w+cL;G`Cs%R_U%q7k?POTbF;< z(#k|5?|4F~b=54dG}|+$mgyOY|GI?~Z%xSNx_S89&E33i!2t*UZ#_5C&t2zdivIPm zJW*-ht@nKu%(L{BH{?y0(0z0C_s%;EyK5d?G1s*9)jnSudR2Yas&5uQkj!mHHuqVa z=*cNHFSgZBv5EARTfAaFgNW~>3yt0ee|ItNc%vJ@e55>){kw^zH~VkhzkweX-M#bq z!^?mjKbL1r?pa`X0XZMHAe(FUtk-kh{3%O*sxFz~Y?LwCkMpe2R}Q{eQqn)ye!6z& zhZgf=GcKt}rvj!=@=6aUrm*UZKb2a-BY5E5KE*2AQ(utW+lp-NMp5T`p;Hx07rcK3bJ}KMmsDOg|*I2?Dm<}8kxAg4$#dh5J&-tyT+xIS56BMr|y*=TZo?6h zoN@L#lDTcj<_6z9{`uJU+Vl4&D3(t5zhWPxu}Qn5U*+LDf&AED_9WJrn(Viq?_FCD~#-WX+U4SxCUEjP=j^GN1`_CLXlW|0>^zBuj6cW2AZ`TM`leK0}7 zva3@9AerjatB++#9fY_Tq)Xy_(&E+)Dh`Vqex&6=q zGZd80dN_hh)?YZD>}hj(^3f`vL&1x@HP^aleEVGT)^6z&4RQAm4|rxtOsV;+qGoQt zQql5i)vL>ii@#j6HfGuvELwoP&b$-Z+)qE2baZ^H+;!+-N^AkUb5Z8Iul#3XYZQK} z+<)v4T5B(N#Hx|g`R=LNp9`Ho3N3Nd;&;$pA$ietPJ(v&@78UdNZ|n5=LIvGMd;Fo z1@D#|QTiM4uCQxHyYI}PGm^U_w*0!iSBNYtX*ZMOcVY7W~SHTD^8N@t&mEs*{x#@X>hdzRF3 z>ExSpnUVXky)Z*T@zzlL$i2cs^<&-1=3AR6{V`N+J(7F-kj>q<%eb-N!olX~}&+<$yd z*oEWa8~Lob&aElCyYxtN+TM^3GhfBX50UfIraTT%^hxG>wzQ5>IHh&>8IHHBB{n0O z3)^o8GM;6@#Jul~7xEuXEqgM>Vc&;&Ggr<1*}dU`{Tz;}{Dth78Cn*swV&~Qdhl}d z0}HNxE>^L6c;2NtC}z==@0S>}P4?eLGIs*VKqzJ@TCef&X!|9#>+M(Fx+j0TXuUTk z{!2~h(=V<{^Y#bS8e3=YDp_b?(J{rzF)K4x*1!39kg+S1(uF1V+$!IePD7r@0qp~X z8q2^UxoKO$|May>CI!U%-mDQhwEXiF##DoA64Re76#iN#e{AoLoAnF58n5%V=+EP9 zcV2o@WbQ}tYYB7jEDYJWZaew`OoJ0~{j4%f}( zNuj!_e>vvf6L)^Xzb(NeKi)W9S6Pv#PFN(AQ~svYgtRWd)VLGA$l*H~*<4fArB4I{ zBAg#xvlH$O_SkMF`u19Q`WyZieKXrOxGm8LXq^(0;hEIppXVwmlPh^Pf8P{?1xN4g zpXQewBk^g?YNYT5-CqGSnq?Q4`Sq2{d>c}4@tdA8Oi$k~e=K_Kz5e}z>{0fcY8>t^ zNnYuBJ0s1pq3em`9hI`z(a%#jQkv}8j(wON(Z6caIwW(a!VCq4!^V?`pI^yLurEDU zq3y_YY=Zx;pp94T&AU=F5_?2u-6>ge$m~LKz1`}P48=f$ql^;|7u)sE$TVTLo5osR z7^OW6$=qqk=BfqsSU+D_;>5KouyFDs`LG6&ungn3N3GA;Km1x&vf6ybM#X0VQ~l1W z=Gw zN{n}|>Xgbhdbs-Ng)M2($z2imKNTh)i>?3Rav*zl-n=7gr@ki*@8>x<5-CPv=kHaN9$=I6}hIrZ{W15NIWPd~CE>E^_>V#+(Zj(GX3e6)4T zs;vStS<8gnmnUzJQB+>nfZUIriQ?YF_KgX_HY?b}k3R8x`MiqdqSEQ3uX@z#=h&%T zkhkWuzIiLRELV0)_KzdlRnjbi1-jEiUcYzo|B}NcP_TCb=nfGiWB<9GrN1^EqjM<3`CLl~QYHj1TT`>|j&$Uu-=Qla<$9p@Nc;SBQMC>{*~Y#{(j z_ptjL{h{|ZVpBJm#6WIdB?h_sZ;Ke@4#L6YzEQVO76PF7h25oTCd&Z6E0r=6M|F>e z!0-+MP@aL^n_8(k_{v0(`#|gQLHD?B(uS1XLnTI#&oLM(ZXR{#Xb6mkz-S1JhQMeD zjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mkz-S1JhQMeDjE2By2#kinXb6mk zz-S22HUxe^mx{JPmv(a56c;7yW#$!^BV)4r)B1(>V*`grrIeO87LHIR;A{r z=_r^eB<5tM=jErtkIw%09|Az`1+5zf&C$ZnD}?bubI!0c_CRZZL25u}yu;3$1Fhc$ ziGkJuo&y=gz`y`ndkYd{Wnf@f4iy8fn+1u1&P87VTI0&VzyR_HNNfYtCn9qoin(k` zjg9OSOmI0JqzB|KkUK#3g6sjA3pyJebdEVE19)zn0d$r(4+8@O=q?h_JtZK&fzC|? zopA^{j}YW9(0P0yzkue|L1)r|&W{6~^#(eZ4RnT@IRp6KGzQQ)VxTj>K<9IT&e8&% zdj&e93Uroqv6>|eI}qiA)tLMpgnY; zeU+d+o1p!xpuMr6a|A$p{z2z8fX?s$owor>Ly`;(44^ZILHj;HdnQ5q_d#bbY+_(w z*v!Ddu$6&)4(Q$-(A_(rdv`#4 z5kUK^Kzr^$X>tn#0|V$D9|veUJOc6rC@tYhk03XJ!V2U^Q2qd&UkvgyXkP{>EY^D1U6z-rr2+E7Fd1(Z)gc?F~n6qlg51?dOn8JHX>Kf~lf za-eht3Nz4NT~J;DnF~`7G6NJgAU=okvbX z>W_hofuUi(YJ-k;Yig1JW1Oj;ft~@R<^aivu|~@J$u^(lV2rcWGc?yTU;yPckjyDI z&Ar|RjbAvJauQ2Yi%S?7Kq*3)fuZ4!na8?StE*MPdJOc;85m&e;dh2KRIe9U@dGRa zHVu@bK`XiDC%t)-=ys6-A_H;(C~bmd{sbBPXcSEC=K#6QNY9XgAw4HQDKV#*L2us< zJtrfLOB{@GhNgOkCVB?2b!&Ix41zXk@9gDZj5F3V(KBLT0Hs<%28M=1#S3;^xA(cs z!5C+uXK0~k$N)-zoD2*N*Z3^+ghahPnHb{?jr0ul3>ZKu2c)O`%8Ias2U_@;7~_mA z!NCJclRTh%sBLT0C!Kt=z_4V?k*iy;_j`c!=ox}somCEzl3u>> z`TXzZB1|B+8G-b`c4dfqJ`LlYr1F@FG0t4iRL_Kg;R*u-sQzxq*Zti2`ry}#OpI|R zUZ&IFQK&D-77{xDo0s>g(ZL5Gn6lolEmT-;S~ zkuR?gDr3gLkW~)K_Y4eSYF)3iS`+VrLR8NLWY2tNh<_As>&L0bDL!Wc#VObYVl3dA zt)a|*QkC7$02U@jLp`trg8>Tz11MGHiR;_^-0{^6Dr3mNfZH_OGO4T#43Z2C4J(os zd9T*U-3@XZ$R-1ZI#vb-P@EpCz4^WT$o*Jw7=Y7G7b|2Jhtu_)M|#)q<^<_621f&I zpUEoaMSHl;R|-cY?>)ZW;!bagD3+-!;ZHvdo;q< zJFzetLJVVA!wLyeIXCkc(xFV;P#Hr827V53yP<*Iue!WI^PLkDBQ)_csB=L4lXla7 zQ>d=RQ-q8I2Lpow14F}!Gv{7O1;ptgWYBY?0Am6N_>K=y(T7$Z6mmdTO>=UB^LIn> zp3v3nnD{?}V;k&Kbxw#29(LR=+30tQ11tl|K@1L@5dYj$loaB!05B!&V` z1_mJphK6FF?9c?~rdF_NMxYem#0iPHsCLCF%dOvzf@KWN^voFgq5iQl^bV0~yuSt< zbKnHG7V7FRx0FQ}u6%3{)&owx2cf3f#WC%<@}A>9RF4@0!#Su6PP{Z(Q@Y{a8*mOb z&@(h;NX!D&Ees4_{ipCb-0^Z`VvIA>Gc?jOVMxwM%mLMd=N~s3_=v^a0EZj6?0vuq z$t3}GIeYGG+zfJ?xt@WZ83RK~YEmhv4()4ZFnK6->KIs$F{liA4Rv+j!cC!nYtM&* zWx%HWhlcg+nJW8yColiP1S1*B88-HBvz`df;@OT3lL^nN!SAq4hwz=D?9WaLETQ z6Xm$TE%ye=6B9Y_wr+rwE{34^(dB}~wo%UJ&!^63q=VfCc6C~2UW#sM0R!K&Og^ct zfyL00!hnInmJ1R(^V)VhulUQi3>>0hdqDlVqErTkS3$>4-bh`R1=a)hj|WtbK0im2 z{KU<#z;S8_Dq%B=bwSk$LwQTuGd=%OQ*ao7T@cL0z@W;&&=4l&aZ&!`W>5|W6%>XH z3>93E)SLIy@`rC$kTh7287LN7xEL5fb%{LFqqx9#?+U=F*AOJr3k?HNfsMPX`txRh z!_5R#9?ana*R~A`WnLSKM7$1w-DU|2ovh3fP(ziWbI!GJmN1?JB8;GR2dH=W2&!lE z$@)o7>1$NMrh)6ml+@G$-OOT!o62WZTRh%4fK3C3^&6<$4)8r+>ApDyl!FcRjPy(y z7`{SHvzVqkMe(o@$fpK+V7KL@mVpW}24fCYUr(RtF0g6fyzn0ya}_(knzLGYg5uN& zl16d64bz@Fs6Dv#yyt_DiD0)C=;mbRW|lB~jScAe zTYO3joI=2<_oD#B9{uHZ2cESi)q=ws)C^&05`?(=xMqAq`NK4RaC!rqmYrG&E?)k< zQ2O${M06%t2HbK;%SkLLNljt6xZwVkb=%}uf$cHUvt(e<6N1>2t0|YXYD$tn6C*n|VlwAv+fYOcyxWUD+L>Q9dXH0Q8a%D$^ z1lXtG5(#(df^M1sqp1j__CF2XHR@$**AlfQlUktZoD6ZQL?A+G55G4CNV_ z$)NtqncTkbi~T>jfztuF^e;#($)qW08 z{D4xt0Rw}w7^IfrdKoTjzo6$AIIO|(lUbZv0qXax&%!4 z#05!(Dz*;X{1#Aq3>g?|Bp`8mF=EvP&oA?zGBMT}>VZqX(xOb=tYQWRb>4JqXDQmU@N^xJxA5H7)L1 z6MH=eN(i7z0lAL>DqSF%8+R>pQU+2l+}pOAKRIaW0&qG2m&&j`Xni-k`F}2618NCD zV;gskaa9IVJNvJ_SpN39B&cQrr9?vphQy-ug2bZYR3`p+SHlk71GNG`DbbLD;gc*R z+*T__Ec(^@Uj^i9a6^^hzbwRUZ+;}*jTh!HL8P5tIYp=yA0lrpE zhdiVW>wTwD*_Qn$sLuq7Q&XHR6hj6E+&;xpQh<7G#+dy^0mdc;1_o&ch6b#$V611z zz~HC|@z0S+gYR5D+xo#_0Itov6d_@7PSnG3$(O7bU>R_F!(BV$X?bI9_kyxxm?8s% zECWNs#O;RTI+dopZ8zAp<;=UNrmnu zhxMI_X|1F83IrH8DMES>IP!uC1A~wfM8(A0o{XC=CbT15K#r@ml)$6A4J&2up18Wn z^&7%8a{Pna)mZz^W}tQz?iziK5(BuEW@x`ewWe863Y<^DbrzO13hMBiFfiawJLH5L zs0|0|%OJ*GVErsCc>z=^8{mr{Ow;UaA)2tXo}eWK?$(p13ZzZmoib(Po?9nDy&RAp zBL;>@sLXzy*|)4iCA`4-2i(uX(k2JB9E=zka#M@aQ;irHzAO`(!!#*v0$2|?&t(x{trrCZzu9-jiG@0Sq2OYIMR+G14CM3aS3SDuI!Q5WdpxnP;F+Y2TpIB)F9>cSsvdjQCr_@ zGBHAnLo#94 z1_p5khK7>s=Re=h+Y9P%gL;Ps3=GAUdCA$CB@AsLd7Zj-f3AVsE}*6hLy8upcK%t> z{PD5sJV_BoJ6j8I^$co#gT^{xqo5-2o?a?O=?~L`KGrF~XlDzlxR+}|@`7PrT0l}m z#AdJyz%9s<;+#y-*u2~PyQ>z7@BRVKKj5BgzBU86Osv}A*y3li5aJ(j&ovc`3n1Af zTnEyIov0txtG3nFk_qnWIXaLO&zWy?>{Z~b-As&irg}zZdL|6=x(r}@s^6ra?GSpc z1RBjV)H60`U`U3_EIvJ>a(>pOOeV%U3q4akLjwj(d+Lk~K!Ye2m@=>)vI1z@i-Cb5 z0ZOCJ4?}7VJ=cno)S|q^oZyn8%)E3v1w+I@xdqBVc}X#77K4G|z;BVIMN=k-fXI#FWpt72Qfy)Lo zE^ml)SiYpPAhkHTD6^o%Ar&0_`dP&eU_t$)(mW_Dt2jTeJT<8#KRY!KqDnV8KQApa zT^E)#prQpirRkY@a8a;1V8eAG3X=14a})DYQj1b6GSf=(^K*2|Qu9*si&9gNt;i@T zDJZtm*Dp#<&nzw}s?^IX$jvI&%P&gTM^>h&V z=~o8CLE%LxqA*G? zkf+c~G+oFH9>`jUf{gr<{Nj?L(&UoTqE!8oD&3s?{Or;KX!#1B!bwUjNzTws&M7S} z!BRfzrB->$@8fYJ*yo_U z2AaguH3p6E6VQx1W7u!w^PhKjIA zK&pCONLwJzLeJD(&p@H3Mj;MVK^uTNj23z(3O1&C7J3Gt;uO}T!IMl7-avJ~sh+tW zxXK5a1F=^RZ?Ozf14^^t#*QvzVh`+dh?FiUfJ^f-ODgrD0ucX0ISARJ)b!Mff&y5p z3ECb?EGkN@M2Hoo!q^b~MaeKeBy@DMGfNyYN^=wQ(u(!-@>5bFobrr(&^TtXer|qB zX-=wcW^O@#QAuhMnru;ONl_&v0`yahONvrcp&U>vNw+vTBQ-ZMxhTK5Shu(!u{mTqx!enBdfU6xs#S(0A_8k#N!^+9#P6FuOn0JP9iH#M)Ms8TmM zF*zd@G<2z3kXV$OSCW}qT9jK_l#y7R0kOR#F)0UTS!PrtrFS_ z0(aVTGxIWYL0K~|v81#JT;t?|OwBJS$;{77EXl`L#UK>v>*^yox(0fddf=8&K~XBG z?WdblT%KQ)qFY{+SO7{+y2%--$=P5J=A@RT<`rj@q-N&fNrUK0^>y{pg>*rEYJ$zU z)UrHKNs?NYrwbm^2Bkhc_97e$P5Tg+gY^>1{s_P8>*^yo;KGwoX%7!AeO-Mx8?4im zfQ!KiCKFVjLRq>7postiy3itDUsoSZ1l&9!!wy@7MI}b$snl$6(XQnONbtD_=C;F6Y5CTfs+|%t{QAP*dK&+!)CWZ z7DFex5kA3VD|GG~q75_)4lV(}wpHdQm*ndfmZp}b;xPb8mATy^S#hvPsQ9W;LqsZQ~kn3GrnQ3Nsz&H+aOp~MTapd>W~s<9+BMc0^!1PGe& z*VoktQMx9eHG&|wf^$8jqlL$@NUC5Z07MVCD^Jk5iNz&IZ4QvLQANOU3C{O;+yGr8 z1a9epTX5jInXs-x(3&H#E-*_MJme1YI;eq_Se9B;j4#DT3$#=XgSi1~Nko z)(2V=rE3UY1_iPjyoy0LtGEKMe{(YPN_3GMZ(yS^M0G)37ea1<+o-Rr4`+jY0j^i# zKxGi5O%EQQ1-I_O2^!Q62W!CNRIohUV94qYkT>yYfi_LS%~XW9z+7Dm&@xDn{?y{4 z)Z|h^5d>8OHU-KiYHSEw>lthaW}_OMD+vWLcsfL1S07C4nt)b8fjogH5RjVkpf&@d zVXq5{1aPynI3qPFF)tn5iAl^$!DASjO3<(os$Ot@0eb?b6x?GV!bq?>Bm=>GaMlMK zhsOkPRg;}s2`aZi^-OkZB{;K#iwscrNEf`EMo%A<%n*D$CW6(28nB>cIAAY<6EGef z;Dt1xx*D{g2CRdStHH}FK`{baW(giC1ls{>Vd@r_ROX}>7nc@*I&j6h$@w{WJ2R*X zL1BoZQy0|RCFC5~+8upeeHfFd1c~HcQ0B}l&V;%ikv+i92aN}or552E`9@I(3K@{M zVfw&f3XV~Tvb@Axd_^ik5!@h<4sh!m+`q#Umx)4I0ng0l#?a)R`Ybz$W* zxE}|RKx@(BF&wuhSe1pJ+Ob^(#iQn__70v zI&h9C0vltgN1OxTLrdV`KnfNjA{J5$>m!D)KuH5K zg}4)(Qy>H1;09Yk0XU+;gWRA*3z`=rQ1D|I1Qh`Hzu=ix7tBReLD)JI(3K~6{0}vn zi2e?EfhU2cHKevdnf8EM04eMcj)(A(W=ztH5(_d?b5n~-iggnUG9kXhVjg%H7wmN` zlE~h`A`R*2$w3b?!D z0P2tBWG3mS73&q3B$goa%M)|5b2F2R^1%9sw;m2KB5#Nfv))4_zA!@&aU)Ft`;*u=x)zH(@mfs0pu+ zQn!KH-Jq#xU1(iLT*-=~#)4GEpxjegkP6|DZ63Jd2KApo8&8Qd1>P_KoT0wact-%X9ynsM1L}~iK7jt5? zfnd%;EHZ(xS?0A2uEUaSjVjt(xhA*;9t G9|-`AytbkM literal 0 HcmV?d00001 diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..a6781a5 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,11 @@ +import globals from "globals"; +import pluginJs from "@eslint/js"; +import tseslint from "typescript-eslint"; + +/** @type {import('eslint').Linter.Config[]} */ +export default [ + { files: ["**/*.{js,mjs,cjs,ts}"] }, + { languageOptions: { globals: globals.browser } }, + pluginJs.configs.recommended, + ...tseslint.configs.recommended, +]; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0d495ea --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1962 @@ +{ + "name": "darktable-ghost-cms-publish", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "darktable-ghost-cms-publish", + "dependencies": { + "commander": "^12.1.0", + "exiftool-vendored": "^29.0.0", + "jsonwebtoken": "^9.0.2" + }, + "devDependencies": { + "@eslint/js": "^9.17.0", + "@types/bun": "latest", + "@types/jsonwebtoken": "^9.0.7", + "eslint": "^9.17.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "globals": "^15.14.0", + "prettier": "^3.4.2", + "typescript-eslint": "^8.18.1" + }, + "peerDependencies": { + "typescript": "^5.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz", + "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", + "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.5", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.1.tgz", + "integrity": "sha512-GuUdqkyyzQI5RMIWkHhvTWLCyLo1jNK3vzkSyaExH5kHPDHcuL2VOpHjmMY+y3+NC69qAKToBqldTBgYeLSr9Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", + "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.17.0.tgz", + "integrity": "sha512-Sxc4hqcs1kTu0iID3kcZDW3JHq2a77HO9P8CP6YEA/FpH3Ll8UXE2r/86Rz9YJLKme39S9vU5OWNjC6Xl0Cr3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", + "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz", + "integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", + "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@photostructure/tz-lookup": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@photostructure/tz-lookup/-/tz-lookup-11.0.0.tgz", + "integrity": "sha512-QMV5/dWtY/MdVPXZs/EApqzyhnqDq1keYEqpS+Xj2uidyaqw2Nk/fWcsszdruIXjdqp1VoWNzsgrO6bUHU1mFw==", + "license": "CC0-1.0" + }, + "node_modules/@pkgr/core": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", + "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/@types/bun": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.1.14.tgz", + "integrity": "sha512-opVYiFGtO2af0dnWBdZWlioLBoxSdDO5qokaazLhq8XQtGZbY4pY3/JxY8Zdf/hEwGubbp7ErZXoN1+h2yesxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bun-types": "1.1.37" + } + }, + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/jsonwebtoken": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz", + "integrity": "sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/luxon": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.4.2.tgz", + "integrity": "sha512-TifLZlFudklWlMBfhubvgqTXRzLDI5pCbGa4P8a3wPyUQSW+1xQ5eDsreP9DWHX3tjq1ke96uYG/nwundroWcA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.12.14", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.14.tgz", + "integrity": "sha512-scnD59RpYD91xngrQQLGkE+6UrHUPzeKZWhhjBSa3HSkwjbQc38+q3RoIVEwxQGRw3M+j5hpNAM+lgV3cVormg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ws": { + "version": "8.5.13", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.13.tgz", + "integrity": "sha512-osM/gWBTPKgHV8XkTunnegTRIsvF6owmf5w+JtAfOw472dptdm0dlGv4xCt6GwQRcC2XVOvvRE/0bAoQcL2QkA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.18.1.tgz", + "integrity": "sha512-Ncvsq5CT3Gvh+uJG0Lwlho6suwDfUXH0HztslDf5I+F2wAFAZMRwYLEorumpKLzmO2suAXZ/td1tBg4NZIi9CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.18.1", + "@typescript-eslint/type-utils": "8.18.1", + "@typescript-eslint/utils": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.18.1.tgz", + "integrity": "sha512-rBnTWHCdbYM2lh7hjyXqxk70wvon3p2FyaniZuey5TrcGBpfhVp0OxOa6gxr9Q9YhZFKyfbEnxc24ZnVbbUkCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.18.1", + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/typescript-estree": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.18.1.tgz", + "integrity": "sha512-HxfHo2b090M5s2+/9Z3gkBhI6xBH8OJCFjH9MhQ+nnoZqxU3wNxkLT+VWXWSFWc3UF3Z+CfPAyqdCTdoXtDPCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.18.1.tgz", + "integrity": "sha512-jAhTdK/Qx2NJPNOTxXpMwlOiSymtR2j283TtPqXkKBdH8OAMmhiUfP0kJjc/qSE51Xrq02Gj9NY7MwK+UxVwHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.18.1", + "@typescript-eslint/utils": "8.18.1", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.18.1.tgz", + "integrity": "sha512-7uoAUsCj66qdNQNpH2G8MyTFlgerum8ubf21s3TSM3XmKXuIn+H2Sifh/ES2nPOPiYSRJWAk0fDkW0APBWcpfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.18.1.tgz", + "integrity": "sha512-z8U21WI5txzl2XYOW7i9hJhxoKKNG1kcU4RzyNvKrdZDmbjkmLBo8bgeiOJmA06kizLI76/CCBAAGlTlEeUfyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/visitor-keys": "8.18.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^1.3.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.18.1.tgz", + "integrity": "sha512-8vikiIj2ebrC4WRdcAdDcmnu9Q/MXXwg+STf40BVfT8exDqBCUPdypvzcUPxEqRGKg9ALagZ0UWcYCtn+4W2iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.18.1", + "@typescript-eslint/types": "8.18.1", + "@typescript-eslint/typescript-estree": "8.18.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.18.1.tgz", + "integrity": "sha512-Vj0WLm5/ZsD013YeUKn+K0y8p1M0jPpxOkKdbD1wB0ns53a5piVY02zjf072TblEweAbcYiFiPoSMF3kp+VhhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.18.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/batch-cluster": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/batch-cluster/-/batch-cluster-13.0.0.tgz", + "integrity": "sha512-EreW0Vi8TwovhYUHBXXRA5tthuU2ynGsZFlboyMJHCCUXYa2AjgwnE3ubBOJs2xJLcuXFJbi6c/8pH5+FVj8Og==", + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/bun-types": { + "version": "1.1.37", + "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.1.37.tgz", + "integrity": "sha512-C65lv6eBr3LPJWFZ2gswyrGZ82ljnH8flVE03xeXxKhi2ZGtFiO4isRKTKnitbSqtRAcaqYSR6djt1whI66AbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "~20.12.8", + "@types/ws": "~8.5.10" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.17.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.17.0.tgz", + "integrity": "sha512-evtlNcpJg+cZLcnVKwsai8fExnqjGPicK7gnUtlNuzu+Fv9bI0aLpND5T44VLQtoMEnI57LoXO9XAkIXwohKrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.19.0", + "@eslint/core": "^0.9.0", + "@eslint/eslintrc": "^3.2.0", + "@eslint/js": "9.17.0", + "@eslint/plugin-kit": "^0.2.3", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.1", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.2.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.0.tgz", + "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-plugin-prettier": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.2.1.tgz", + "integrity": "sha512-gH3iR3g4JfF+yYPaJYkN7jEl9QbweL/YfkoRlNnuIEHEz1vHVlCmWOS+eGGiRuzHQXdJFCOTxRgvju9b8VUmrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "prettier-linter-helpers": "^1.0.0", + "synckit": "^0.9.1" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint-plugin-prettier" + }, + "peerDependencies": { + "@types/eslint": ">=8.0.0", + "eslint": ">=8.0.0", + "eslint-config-prettier": "*", + "prettier": ">=3.0.0" + }, + "peerDependenciesMeta": { + "@types/eslint": { + "optional": true + }, + "eslint-config-prettier": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", + "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/exiftool-vendored": { + "version": "29.0.0", + "resolved": "https://registry.npmjs.org/exiftool-vendored/-/exiftool-vendored-29.0.0.tgz", + "integrity": "sha512-BW2Fr7okYP1tN7KIIREy8gOx9WggpPsbKc3BTAS4dLgSup50LjdQttxF9kyDP+27ZayllK+d0rfMYPAixPBtQw==", + "license": "MIT", + "dependencies": { + "@photostructure/tz-lookup": "^11.0.0", + "@types/luxon": "^3.4.2", + "batch-cluster": "^13.0.0", + "he": "^1.2.0", + "luxon": "^3.5.0" + }, + "optionalDependencies": { + "exiftool-vendored.exe": "13.0.0", + "exiftool-vendored.pl": "13.0.1" + } + }, + "node_modules/exiftool-vendored.pl": { + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/exiftool-vendored.pl/-/exiftool-vendored.pl-13.0.1.tgz", + "integrity": "sha512-+BRRzjselpWudKR0ltAW5SUt9T82D+gzQN8DdOQUgnSVWWp7oLCeTGBRptbQz+436Ihn/mPzmo/xnf0cv/Qw1A==", + "license": "MIT", + "optional": true, + "os": [ + "!win32" + ] + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz", + "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==", + "dev": true, + "license": "ISC" + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.14.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", + "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonwebtoken": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", + "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", + "license": "MIT", + "dependencies": { + "jws": "^3.2.2", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", + "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", + "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "license": "MIT", + "dependencies": { + "jwa": "^1.4.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", + "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prettier-linter-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", + "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-diff": "^1.1.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/synckit": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.2.tgz", + "integrity": "sha512-vrozgXDQwYO72vHjUb/HnFbQx1exDjoKzqx23aXEg2a9VIg2TSFZ8FmeZpTjUCFMYw7mpX4BE2SFu8wI7asYsw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@pkgr/core": "^0.1.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/unts" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", + "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.18.1.tgz", + "integrity": "sha512-Mlaw6yxuaDEPQvb/2Qwu3/TfgeBHy9iTJ3mTwe7OvpPmF6KPQjVOfGyEJpPv6Ez2C34OODChhXrzYw/9phI0MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.18.1", + "@typescript-eslint/parser": "8.18.1", + "@typescript-eslint/utils": "8.18.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.8.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..47ba807 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "darktable-ghost-cms-publish", + "module": "src/index.ts", + "type": "module", + "devDependencies": { + "@eslint/js": "^9.17.0", + "@types/bun": "latest", + "@types/jsonwebtoken": "^9.0.7", + "eslint": "^9.17.0", + "eslint-config-prettier": "^9.1.0", + "eslint-plugin-prettier": "^5.2.1", + "globals": "^15.14.0", + "prettier": "^3.4.2", + "typescript-eslint": "^8.18.1" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "dependencies": { + "commander": "^12.1.0", + "exiftool-vendored": "^29.0.0", + "jsonwebtoken": "^9.0.2" + } +} diff --git a/publish.lua b/publish.lua new file mode 100644 index 0000000..65b85db --- /dev/null +++ b/publish.lua @@ -0,0 +1,133 @@ +local dt = require "darktable" +local df = require "lib/dtutils.file" +local os = require "os" + +-- Some fucking bullshit happening right here. +function os.capture(command, raw) + local f = assert(io.popen(command, 'r')) + local s = assert(f:read('*a')) + f:close() + if raw then return s end + s = string.gsub(s, '^%s+', '') + s = string.gsub(s, '%s+$', '') + s = string.gsub(s, '[\n\r]+', ' ') + return s +end + +local publish_title = dt.new_widget("entry") { + placeholder = "Post Title", + tooltip = "enter title for the post" +} + +local publish_slug = dt.new_widget("entry") { + placeholder = "post-slug", + tooltip = "enter slug for the post (URL-friendly)" +} + +local publish_keywords = dt.new_widget("entry") { + placeholder = "keywords (space-separated)", + tooltip = "enter keywords (tags) for the post" +} + +local strip_gps_checkbox = dt.new_widget("check_button") { + label = "Strip GPS data", + value = false, + tooltip = "remove GPS metadata from files before uploading" +} + +local widget = dt.new_widget("box") { + orientation = "vertical", + publish_title, + publish_slug, + publish_keywords, + strip_gps_checkbox +} + +local function initialize(_, _, images, _, extra_data) + extra_data.exported_files = {} + extra_data.cleanup_files = {} + + if publish_title.text == "" then + extra_data.title = df.get_basename(images[1].filename) + else + extra_data.title = publish_title.text + end + + if publish_slug.text == "" then + extra_data.slug = df.get_basename(images[1].filename) + else + extra_data.slug = publish_slug.text + end + + extra_data.keywords = publish_keywords.text + extra_data.strip_gps = strip_gps_checkbox.value + + return images +end + +local function store(_, image, _, filename, _, _, _, extra_data) + if extra_data.strip_gps then + local command = string.format("exiftool -gps:all= -overwrite_original '%s'", filename) + os.execute(command) + end + + if image.is_raw then + local original_path = image.path .. "/" .. image.filename + local raw_filename = original_path + + if extra_data.strip_gps then + local tmpfile = os.tmpname() + local command = string.format("exiftool -gps:all= -o '%s' '%s'", tmpfile, original_path) + os.execute(command) + table.insert(extra_data.cleanup_files, tmpfile) + raw_filename = tmpfile + end + + table.insert(extra_data.exported_files, filename .. ":" .. raw_filename) + else + table.insert(extra_data.exported_files, filename) + end +end + +local function finalize(_, _, extra_data) + local files_arg = table.concat(extra_data.exported_files, " ") + + local command = string.format( + "darktable-publish --title '%s' --slug '%s' %s", + extra_data.title, extra_data.slug, files_arg + ) + if extra_data.keywords ~= "" then + command = command .. string.format(" --keywords %s", extra_data.keywords) + end + + -- Ignore that I use an external tool (written in JavaScript god forbid) + -- I am _not_ doing JSON generation and web requests in Lua + local result = os.capture(command) + + if result and result:match("^http") then + dt.print("Post published: " .. result) + else + dt.print("Failed to publish post.") + end + + local command = string.format("xdg-open %s", result) + os.execute(command) + + for _, tmpfile in ipairs(extra_data.cleanup_files) do + os.remove(tmpfile) + end +end + +local function supported(_, _) + return true +end + +dt.register_storage( + "ghost_publish", + "publish to Ghost CMS", + store, + finalize, + supported, + initialize, + widget +) diff --git a/src/api.ts b/src/api.ts new file mode 100644 index 0000000..89eeabd --- /dev/null +++ b/src/api.ts @@ -0,0 +1,110 @@ +import { sign } from "jsonwebtoken"; +import { file } from "bun"; + +const getAdminApiKey = async () => { + const keyPath = process.env.GHOST_ADMIN_API_KEY_PATH; + if (!keyPath) { + throw new Error( + "Environment variable GHOST_ADMIN_API_KEY_PATH is not set.", + ); + } + + const keyFile = file(keyPath); + if (!(await keyFile.exists())) { + throw new Error(`Key file not found at path: ${keyPath}`); + } + + return await keyFile.text(); +}; + +const getEndpoint = () => { + const endpoint = process.env.GHOST_URL; + if (!endpoint) { + throw new Error("Environment variable GHOST_URL is not set."); + } + + return endpoint; +}; + +const createJwt = (key: string) => { + const [id, secret] = key.split(":"); + if (!id || !secret) { + throw new Error("Invalid API key format. Expected format: {id}:{secret}"); + } + + return sign({}, Buffer.from(secret, "hex"), { + keyid: id, + algorithm: "HS256", + expiresIn: "5m", + audience: `/admin/`, + }); +}; + +const upload = async ( + slug: string, + path: string, + type: string | undefined, +): Promise => { + const endpoint = getEndpoint(); + const fullEndpoint = `${endpoint}${slug}`; + + const key = await getAdminApiKey(); + const token = createJwt(key); + + const f = Bun.file(path, { type }); + const formData = new FormData(); + formData.append("file", f); + + const response = await fetch(fullEndpoint, { + method: "POST", + headers: { + Authorization: `Ghost ${token}`, + }, + body: formData, + }); + + if (!response.ok) { + throw new Error( + `Failed to upload to ${fullEndpoint}: ${response.status} ${response.statusText}`, + ); + } + + return await response.json(); +}; + +export const uploadImage = async (imagePath: string): Promise => { + const slug = `/ghost/api/admin/images/upload`; + return (await upload(slug, imagePath, "image/jpeg")).images[0].url; +}; + +export const uploadFile = async (filePath: string): Promise => { + const slug = `/ghost/api/admin/files/upload`; + return (await upload(slug, filePath, undefined)).files[0].url; +}; + +export const uploadPost = async (post: any): Promise => { + const endpoint = getEndpoint(); + const fullEndpoint = `${endpoint}/ghost/api/admin/posts`; + + const key = await getAdminApiKey(); + const token = createJwt(key); + + const response = await fetch(fullEndpoint, { + method: "POST", + headers: { + Authorization: `Ghost ${token}`, + "Content-Type": "application/json", + }, + body: JSON.stringify({ + posts: [post], + }), + }); + + if (!response.ok) { + throw new Error( + `Failed to upload to ${fullEndpoint}: ${response.status} ${response.statusText}`, + ); + } + + return (await response.json()).posts[0].url; +}; diff --git a/src/exif.ts b/src/exif.ts new file mode 100644 index 0000000..b7764da --- /dev/null +++ b/src/exif.ts @@ -0,0 +1,48 @@ +import { exiftool } from "exiftool-vendored"; +import type { FileInfo } from "./files"; + +export interface ShootingConditions { + make: string; + model: string; + lensMake: string; + lensModel: string; + focalLength: string; + focalLength35: string; + shutterSpeed: string; + fStop: string; + iso: string; + timestamp: string; +} + +export const extractShootingConditions = async ( + fileInfo: FileInfo, +): Promise => { + const path = fileInfo.rawPath ?? fileInfo.jpegPath; + + try { + const exifData = await exiftool.read(path); + + return { + make: exifData.Make ?? "Unknown", + model: exifData.Model ?? "Unknown", + lensMake: exifData.LensMake ?? "Unknown", + lensModel: exifData.LensModel ?? "Unknown", + focalLength: exifData.FocalLength ?? "Unknown", + focalLength35: exifData.FocalLengthIn35mmFormat ?? "Unknown", + shutterSpeed: exifData.ExposureTime ?? "Unknown", + fStop: exifData.FNumber?.toString() ?? "Unknown", + iso: exifData.ISO?.toString() ?? "Unknown", + timestamp: new Date( + (exifData.CreateDate?.toString() as string).replace(/\./g, ":"), + ).toISOString(), + }; + } catch (error: any) { + throw new Error( + `Failed to extract EXIF data from ${path}: ${error.message}`, + ); + } +}; + +export const createImageCaption = (exif: ShootingConditions) => { + return `${exif.make} ${exif.model}, ${exif.lensMake} ${exif.lensModel} @ ${exif.focalLength} (${exif.focalLength35}), ${exif.shutterSpeed} s, f/${exif.fStop}, ISO ${exif.iso}`; +}; diff --git a/src/files.ts b/src/files.ts new file mode 100644 index 0000000..d4e3468 --- /dev/null +++ b/src/files.ts @@ -0,0 +1,66 @@ +import { basename, extname } from "path"; + +export interface FileInfo { + jpegPath: string; + jpegSize: number; + rawPath?: string; + rawSize?: number; +} + +export const getBasenameWithoutExtension = (path: string): string => { + const base = basename(path); + const extension = extname(path); + return base.slice(0, -extension.length); +}; + +export const getBasenameWithExtension = (path: string): string => { + return basename(path); +}; + +export const prepareFiles = async (files: string[]): Promise => { + if (files.length > 10) { + throw new Error("Up to 10 files are allowed at a time."); + } + + const parsedFiles: FileInfo[] = []; + + for (const pair of files) { + const parts = pair.split(/(?", "Files to process") + .action(async (files, options) => { + if (!options.title) { + throw new Error("Please specify a title."); + } + + if (!options.slug) { + throw new Error("Please specify a slug."); + } + + const parsedFiles = await prepareFiles(files); + + const [ + shootingConditions, + uploadedJpegImages, + uploadedJpegFiles, + uploadedRawFiles, + ] = await Promise.all([ + Promise.all(parsedFiles.map(extractShootingConditions)), + Promise.all(parsedFiles.map((f) => uploadImage(f.jpegPath))), + Promise.all(parsedFiles.map((f) => uploadFile(f.jpegPath))), + Promise.all( + parsedFiles.map((f) => + f.rawPath ? uploadFile(f.rawPath) : Promise.resolve(undefined), + ), + ), + ]); + + const aggregatedFiles = parsedFiles.map((file, index) => ({ + ...file, + shootingConditions: shootingConditions[index], + uploadedJpegImage: uploadedJpegImages[index], + uploadedJpegFile: uploadedJpegFiles[index], + uploadedRawFile: uploadedRawFiles[index], + })); + + const result: any = { + root: { + children: [], + direction: "ltr", + format: "", + indent: 0, + type: "root", + version: 1, + }, + }; + + if (aggregatedFiles.length > 1) { + aggregatedFiles.slice(1).forEach((file) => + result.root.children.push( + createImageNode({ + src: file.uploadedJpegImage, + caption: createImageCaption(file.shootingConditions), + }), + ), + ); + } + + result.root.children.push(createHeadingNode("Downloads", "h2")); + + aggregatedFiles.forEach((file) => { + result.root.children.push( + createFileNode({ + src: file.uploadedJpegFile, + name: getBasenameWithExtension(file.jpegPath), + size: file.jpegSize, + }), + ); + + if (file.uploadedRawFile && file.rawPath && file.rawSize) { + result.root.children.push( + createFileNode({ + src: file.uploadedRawFile, + name: getBasenameWithExtension(file.rawPath), + size: file.rawSize, + }), + ); + } + }); + + const post = { + title: options.title, + slug: options.slug, + lexical: JSON.stringify(result), + feature_image: aggregatedFiles[0].uploadedJpegImage, + feature_image_caption: createImageCaption( + aggregatedFiles[0].shootingConditions, + ), + status: "published", + visibility: "public", + tags: options.keywords, + published_at: aggregatedFiles[0].shootingConditions.timestamp, + }; + + const url = await uploadPost(post); + console.log(url); + + process.exit(0); + }) + .parse(); diff --git a/src/lexical.ts b/src/lexical.ts new file mode 100644 index 0000000..46d013b --- /dev/null +++ b/src/lexical.ts @@ -0,0 +1,50 @@ +export const createTextNode = (text: string) => ({ + detail: 0, + format: 0, + mode: "normal", + style: "", + text, + type: "extended-text", + version: 1, +}); + +export const createHeadingNode = (text: string, level: string) => ({ + children: [createTextNode(text)], + direction: "ltr", + format: "", + indent: 0, + type: "extended-heading", + version: 1, + tag: level, +}); + +export interface ImageInput { + src: string; + caption: string; +} + +export const createImageNode = (image: ImageInput) => { + return { + type: "image", + version: 1, + cardWidth: "wide", + ...image, + }; +}; + +export interface FileInput { + src: string; + name: string; + size: number; +} + +export const createFileNode = (file: FileInput) => { + return { + type: "file", + src: file.src, + fileTitle: file.name, + fileName: file.name, + fileCaption: "", + fileSize: file.size, + }; +}; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..8b73555 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + // Enable latest features + "lib": [ + "ESNext", + "DOM" + ], + "target": "ESNext", + "module": "ESNext", + "moduleDetection": "force", + "jsx": "react-jsx", + "allowJs": true, + // Bundler mode + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "noEmit": true, + // Best practices + "strict": true, + "skipLibCheck": true, + "noFallthroughCasesInSwitch": true, + // Some stricter flags (disabled by default) + "noUnusedLocals": false, + "noUnusedParameters": false, + "noPropertyAccessFromIndexSignature": false + } +}