From 1f52edaefb34b7d4bd849979964a37fc5183cd0a Mon Sep 17 00:00:00 2001 From: DecafDev <40307803+decaf-dev@users.noreply.github.com> Date: Tue, 25 Jun 2024 19:58:35 -0600 Subject: [PATCH 1/5] Refactor migrations (#133) * refactor: move migrations into their own class * refactor: don't export old types * test: fix failing test * feat: add preform migrations function * fix: delete creationDate prop * chore: add ts-auto-guard * test: make sure migrations work --- bun.lockb | Bin 178106 -> 188341 bytes package.json | 2 + src/main.ts | 337 +-------- src/migrations/index.ts | 99 +++ src/migrations/migrate_0_4_0.ts | 20 + src/migrations/migrate_1_0_0.ts | 30 + src/migrations/migrate_1_10_0.ts | 37 + src/migrations/migrate_1_13_0.ts | 29 + src/migrations/migrate_1_14_0.ts | 38 ++ src/migrations/migrate_1_15_0.ts | 17 + src/migrations/migrate_1_17_0.ts | 44 ++ src/migrations/migrate_1_1_0.ts | 17 + src/migrations/migrate_1_2_1.ts | 17 + src/migrations/migrate_1_3_0.ts | 36 + src/migrations/migrate_1_6_0.ts | 18 + src/migrations/migrate_1_6_1.ts | 19 + src/migrations/migrate_1_9_0.ts | 36 + src/migrations/migration_interface.ts | 3 + src/migrations/types.ts | 7 + src/svelte/app/index.svelte | 4 +- src/types/index.guard.ts | 643 ++++++++++++++++++ src/types/index.ts | 1 + src/types/{types-0.3.0.ts => types-0.3.3.ts} | 2 + src/types/types-0.5.5.ts | 2 + src/types/types-1.12.1.ts | 58 +- tests/integration/preform-migration.test.ts | 189 +++++ .../unit/match-number-property-filter.test.ts | 4 +- 27 files changed, 1351 insertions(+), 358 deletions(-) create mode 100644 src/migrations/index.ts create mode 100644 src/migrations/migrate_0_4_0.ts create mode 100644 src/migrations/migrate_1_0_0.ts create mode 100644 src/migrations/migrate_1_10_0.ts create mode 100644 src/migrations/migrate_1_13_0.ts create mode 100644 src/migrations/migrate_1_14_0.ts create mode 100644 src/migrations/migrate_1_15_0.ts create mode 100644 src/migrations/migrate_1_17_0.ts create mode 100644 src/migrations/migrate_1_1_0.ts create mode 100644 src/migrations/migrate_1_2_1.ts create mode 100644 src/migrations/migrate_1_3_0.ts create mode 100644 src/migrations/migrate_1_6_0.ts create mode 100644 src/migrations/migrate_1_6_1.ts create mode 100644 src/migrations/migrate_1_9_0.ts create mode 100644 src/migrations/migration_interface.ts create mode 100644 src/migrations/types.ts create mode 100644 src/types/index.guard.ts rename src/types/{types-0.3.0.ts => types-0.3.3.ts} (95%) create mode 100644 tests/integration/preform-migration.test.ts diff --git a/bun.lockb b/bun.lockb index 48d5c37592a105fdd39b12ba19782323f2b84c8a..87174c7782e50983a0c36d24d90a27dea0ad1edd 100755 GIT binary patch delta 34146 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%0A+SGeU};V3OFna& zH+MR?ZO~cQY2Ko!>|F2EjnJ7c53R!TUIN09lG1dT%SMQ6ARFOZTm@-McLAC3ZCs@} z@Pab*GjvM?^!s3CGvHhxvX&@~V0?+cc0fT@WfOWMJ zv@$ZX73X<#b90J{wIbh)*}g(eJAzWe!8b>3|N|1U>>W{q~tpMs1>wu)QH{t0d!Vw2e1Qh2{00Pr{X&TJ462uE*t|q z4P+$`0of}L16u><19`PkN`DrO#l~9;L_?R}1B4YNrLU`QwJTBR^JW%DCy2D1#A6y(^I2{$3|p&XhuLrmslT`k@xJ+1;xU zpktl|@zU;;6m)KEATVoeL%L*#@W=fKd^Pf-$luXJakyuxm?%n@EPF-ev)!!7e=0cqza zumg1~UC>45E>QSBNM3J3S83O9AWOFg;5F!U+pVaNZIeFRnT0fO;N)er1)25_Y z*EJ(+1_lOqlV#4qSZ3^_=75^v8NVMWzPSay%wqThngjnz3!-v!X5ws;90qRh200rX7{0GihYIimMea6 znlxl9FbL_jLDHZhKz83vm9LwX4t^Vj0R1RT;g)n+Z~$~#+;XtI@B(##L(mZ{-3eq1 z75a+ub7%Xs{X^u)+zDh2Hv#VeVxrEUlc#A{hsyG5pE%EgGJAw9Fe6aLCykUfYz<^rgaGRR6GutIqJX?mD}`CUEMH!+re)@{ol$9| zFQKJxjg~dq4djICD=f?}jPlJrHbxd$3#BmK*s;>UGeAs!rN$rQfH%*zheZkYv;uzp`A1>`%bAxPmtxb z1hQhIz_a|m!1_QGIV%V0H%^q-IgD~>=QBXI{9+)lzXNHQ@0UrkTpfdl>5B3TGvPVr zkU9}1C&BR=WkA|;8EM(*7l2HgKcy%qE61C6DpPt(WR^_d49II71~@@}Pj zeKOrf*g%_nMN?+wexS5fHWuDTU3lD(h7aE zigL2&X+!Ror8QqDOUVV&i~u0hzl(g_mhA&_L$Ga;+zmYqo$bB>$aE_(rO*nsOD!q= z00cdGF%qytIQ&`A=jgUpz+zR;;-cIfF05?dDX78>U*S|=tfrk@BCBV&GbVehhhA7J z`IiyT_MWp?&VO&B++gTCfz2`dmX1K7%|R6Ureb9)(Dp8qhHL;b;q&)Oi|zKb+tF_C z&4{PxE?j1Qbw``py~^ZumzO;sQvY7(^B=p;=>c=@$^J5W$gG6go6euUr`G3j=7G>} z&9<&2b9>W*;2*X%c*J-xdv%OwYwNq028KL(@8MqCZ``qMOK_O3ziqmjdGy_82EM;G z*WogB?`~!VzL%M<<{sx8H8c(DX{l~TJdk9rZ|>3im=*ZG z*K|dAoCp0htv6!*%=6)H=M8A_&|blj%EhFC!6c>z1FP2_ot?-l}8UYGw?moT#xUjW<@KH^H~g! z?y|UkNq$gL%`@T2&R%d2(hkd-rO*P9EpGA+lX-n43GfoeeP1 z={Ysc2(({cXp^mr^=+zYc~)p2LS3y;Xc!hwE3_D)u?#sY5E^9FA8q4kjZrVnE<~;hJ`*6)H!_%I~O;VJl;YAk^E6-GWe0hMeCZguSlxa`cod-lON48Sx&+ z-UwC{zUTM~p`qrPX35U3Eo9HsFe6&J^|@w7f=54Ou21kd>a;{7S!MS_$SPZZ(9B5m z=&zaU6VXg9&C`jg&gfQ}*3qgAoWL;-nylnrgk&YXGn4$F!0XWsDW)sQqc1fx@V(z$ zpX70LX@f1Z759MY>f&+y3Vn_l)+O0d6sc+X4Cxol3d9U;t7(g^m{SPdW1c~pQSGpE zvbX~XO(Ca;n66}xV|jZ`8)JrbO)=L)8_>b-r5y+jH#fzl_#u#Pd02l`K>F26gy^Ua z%ZX1y>jJHT)zL;2Q_&CmAe3z7ydR-H7WY0v{mii8DSpwKmS#EW5`_9#O@kT6aU2@j zATrtM=!Dj{c=W$M%FOV1oDYGcOJZ(n=5|y->uQEIPj)oy3|})hp{Ir+#1yqmIIVLd zwBFDhmUa;u`@9zC5=V;|j7w{d8Ho@lFWvNFaBYCr!?H||g;#+$OfP1Q3DAZ^t7&=n zEoj{>4P`m|$4N~yHz&9qE1-3?hRP|^)yw1TjA1~VG|P%yXtW7K8Kpi44Q6pZNOC*> z1C6r?W@cF5D?!Ftb~%fnAzI~o3L0k)%wd=b&nitf&qunQO)$B!d!WI#QP4(1LmR+% zkD3*IJkADgSr?St%&o69Gx~a*SHU4E+Ln_=YqJ8}G+cs~!yc5h6&kJ6toFDJjjf4t zVL=;sz(6ZEYjrO)T7b2orQ7+M($G3+Ujv!?uzmG>v!cJpu@Ab(YVTVJv1RLqd&0DIRQSo8zCf+?dG@wE!N!BDcP9=2O4Zy&3-C}Cg*!^za)PsbT6wj zoLzcU8jtGDgvR3F;>h+fH29)wh}tMYYAiAZnp%}%yk2bdN3zQ=Tp^*COF|66g6NomT(od*!YB}!{sTa5rr z_2_+7;c?K|kEn2(+p!LsoGsr+$QmHd;PlGr088j(XmV!Vg%Bnf*z+Yq-7LrJgqD=~ z??MQ63`p@ufMXIh#oW?pi0uq$A?^_G^f*5Nmu;n>yL25YTMg;aSF@q1d3mhcc^DdJ zA35HBg~rxKN;pdDFxgmE&pGBov&OUI20|mOkv`PSnCNkCA1*6w<#K!pE!_^W$TzeAJLhGu4X zJ)rVesDi=fd>taXgLo+%zjX6m1r71KP< zkV$gjSvy*NqM4E7ajpZ$5*^kYe{3?Wu%?1AO2$Hvk$; zF;5q#I{HpQbDL*oBs&)&L|ZX0G>PBAykN_E}<*%M?ozWCnH%P$8eu;3G$7Y!hRjVhw))I* zfSW#Z!`xKIm8tZ|O>>hSG1JgUR%i}F{jJaugk)@u9Ggo+NTyqZkc|DXGB)fk?Dwp^ zIS9#8_8>Ia;%-&OrcJLb1tFQP0wGymSgy_a5R&EXK}cTrCxixC<@K0hrz=B9ru)$5 zoMCy$VKp;ugN)2PbHk!kBRbzay(rarut0Vj=0z;-|E|)oc;?MysobKyfDm0zLvtqi zL*ZaT7Oe6u3hBLJEt8!S5gG^{qdv#&+zyRPjI|dqJ}Wds7NX9OuMg&^QUmxH4#*&aH*h`3W>$9lIQi<|cF0 z9@I#iV}>kCb>0g?xiOZv*Pta@iQ&}F>(FG|V>{yPK3B$BH`&hFN`v9?ZsUcy=IMx3 z=l394H!OB7-A1Q*X2|kX=fZiiiWqd5p~|6g=wqn%aXYp7w&U|Aw=1-vh?7(FebB61 z9OISw=7triPR9aSI-)Tyx-2k5nx{Hvf@B`{uG?`8+7NR>^JF97Zu9hvRAb8BW=LMD z`!%-$C zE4Za=G4FZ9?oZJdn;ZU?>Kwg9)8Z^MynWryGH40V>X_#zC+SPf)48e6noDInV&jN5 z?hcK8DtAbWpwT(x2Ih!LDUJC>rIfoT_dS|6(%iHt*|8EKj8lY8AvD?wMJ`hz=bH#g zFX6b;@5QaIWgW-GcxW=`5rpLQdJ7@0iSjDx%gJ+Vxe6h=CTzjjKDpdHy*kxVe+BPn zacN`H3Ul0=RAc=La|7VR73S$Rsg66!tW9iMvN5R49QRqBHYm|Uz6(CqMpvxIrk zUDk(49UW%bf{w60{~Kgo`a*bZ?9{CCu%oiBjb^nzMB0L_QUfaiJkV#UUHBrRtkyKGZmkucw!i2F@#lwD_I{RyFozeLAFBb zL!O7Q3I`zkR7XGPFLFg^e;r{gKK~8sX4u0`N?4`WAvE(igr&Rz;pcY9bZ7^EVIyx=gU6RD2|G97uTpw-Bz3>yiOjzSNa{hw6B#u9x5FLH z3G2g4SpRh@n#g)T3giS>4rDQhR6LR99Z~!%Kt{dFFNJJ}zbl={{3n3q-vBb|O@2v~ zo=}aj3^=6{R7Zbgs89u*R`J!bG2*`k&kFpg(*30HXO*5vgZ=|##c$CQ__?Jb44R1# zkqK%7N%`Z84Ic<(Sq(@kWPy#8ULBdPu}a@W#aG8hi0{ZXgcipFnI>K(Br=$YFBarh zJTVyh5Fia4p?D&fk*P|rj_k5r@GNJBiYGFE$xKC5MM%$IgHv3%rBmFjlLT<;>Dm;E1=?$3~BywB2sAEDr2E7=^#7Kl*%A7V+h z1kBJv>D7^ZOU2&~8Q)696REcb(vV1{w^i6qVS6C{X(dsLh*sE%EIxmRyg+A_j>uq~ z(us_Z2hxBjUmbasT*VWq=K<>g z=P4d2eUbzXC{+oUs02i2yhrKPk%M)G;;SRm-v^!su2lI~DgAzx9#PgT8>Hd6%^gr$ zKp58=!i@BLeu&f~6;{7l0%hGi@q+)=O_Le+glzA>Zj=7HO;Qf^*KJbu+a-=^7ss1I z21D@m*KHCSB11LI?T~X&_1hz;mj2gm5*zKW+a$Ro{_8gBuiK=*Zj(4S{@HEUU$;qW zxBS;_(*O6jNjK}?w00fO-}PMFG~sBYr-_TfdOa}#Aw5RyXrqr2>s%n}MS_SE8Id6R zhk$sAM1pX(1@SY98Erwh#X%C=LqUYK1JOm~v;#4=DTud8Bnwx25dL8x7Pbe`O`Ifg zfJ9UW5FRnF1BhwOK%61bL$vD%A~+nxs*WIfi3$=YNOX$=(MOa;>0|W1;yj_B=n@U+ zFV+wSh)aZlqDLn{npjU5B)%i0ivgVhgT-dT5b+CPs2COl7$$ZQh6_CwFhXPyMv6Uz zQNkGq7%jYnG2$R$tO$$;WQZKXIB|?HUbqsFz`rd_U6=qgS1Tjg>O9U~k z9f&g|yrP{OL~wf$tK1+mMFoiyB)TPm@QJb{5OX_#xI$v8=+Xs5+m0YMbpeqhE|K_{ zM0!^c)5XCsn3F34n*nBs^<4pZ;yXgV7?2Do5Ss}z#V>?HF)Rg8Bz6#rh29M?OJopc zi#>!n!kG$~E4+kx;viwZ2=o9Jh#bP*;uv9}aCHYP5;F-U;v}I|MDzeG7V`iiI02@f z=>b!higrCgoFK8PCx~UDg2dcJ5Z!u#ST4$XfoSUnafL*g=+Ya+$0RoO2C-7)pq^qy z5{S2(fmkJ6;UJQ_fLIs~;sJ4z#6=QO%|WaY^O}QL*A>LSS%xWk^a0U78N`!)Ks+qI zBk?ndQGG!OvAHjZ?I|D}{Xnc2!}@_3+YQ8i5*vlyAB2A@2w#5?o5UUx2S_v;0OB#> z9ROmQheQ~N&00=9*yL9FWo;tGl9 zMVBEU`u7E~X$Xk@;u49UNu&=2Q7+aG1+l#!h#MphmJb-FPtqL=apUI{-(~1q%z$Qo zPW#WM!^MOV`W#mvZvXJusFXK3D-p)?@zTeD^~;fCr@<_Z>^~;PpKe9pRV1Gp9T$5> z>rXixp?D+9q=$9B)6;%zA)00A(Qu0N4E-^EUiq*b{e8W>z^mg`Juh$n1<}It^oe>c zLl!{mt}(ja%RS^D#+(2Ku-x4>$cq4RjaR=S6Pe9ica|xVL8N{^Dq^9%T4WDbJg(x6td^f97?~r>^t4%zil^*(35% zQyTgvvscwaoGQ?NGS7vzC0&kEANLce?uqi;+4@~0Yp>hSX3@0m2SuIt^zJ1$Z^;k2 z4TxeapQ!O;>+Fl!irc8tP@Su|M-<09`hF08HYtve5c^w2yjvB=J7hGQ_2HQT)=N#k zz%NkbHkFW%3GqWH>+_W2@I+OEvss_*isL(pV#V!HTp;whihEjd4WQ#!LDpxd;u=Cr zQ~OVCmm>K(C>&vao>3g%IoS_MpH&>+I;5%E?N(e9aC{8T&vT07Yo|$y+oL$XCG4(R zuw<_yLqPUY{^224lAw&xWN!M+lz{8iSTC-eqK^sD};9{V_sGqpY-lxB7BZ0 zj!%2{+P^C}3X*yF=(h{P{P0C7TaJ%`lM#*p^5rQ0Y3(2w{MMu7*A>Ud!e=2g>Nq&& z?Eu*a;l=D1sT~m>j(A?{%@UP43d%@DzNNTmaQtlxFZQkHfZK{&4rfbjL+TadRQ??Cvra1UfJkfdEQvsn)6d5GSOz zc;-VrE@ljZ>>s|K{TpO8WDUfGJOs%`+*C*oWCkQ(Ec{3hE#V}@NoPI;KN{pO{VabU z4IB%}fTTeNLHOqLJ;*5t->JR=c@=ULl8-9VP3UJF4D`o|kZ|x3kQR{okUPO~o>~f- z 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 ZLUU#!(fqe@pWk7R(W-pk$A(_({{R?oQ@;QJ diff --git a/package.json b/package.json index 699b8ae..92f8521 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "scripts": { "dev": "node esbuild.config.mjs", "build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production", + "guardgen": "ts-auto-guard", "test": "jest --config jest.config.js" }, "keywords": [], @@ -25,6 +26,7 @@ "jest": "^29.7.0", "obsidian": "latest", "svelte-preprocess": "^5.1.4", + "ts-auto-guard": "^5.0.0", "ts-jest": "^29.1.4", "tslib": "2.4.0", "typescript": "4.7.4" diff --git a/src/main.ts b/src/main.ts index 080ab7e..960b656 100644 --- a/src/main.ts +++ b/src/main.ts @@ -3,30 +3,16 @@ import { Plugin, TAbstractFile, TFile, TFolder } from 'obsidian'; import VaultExplorerView from './obsidian/vault-explorer-view'; import VaultExplorerSettingsTab from './obsidian/vault-explorer-settings-tab'; -import { FilterRuleType, TExplorerView, VaultExplorerPluginSettings } from './types'; +import { VaultExplorerPluginSettings } from './types'; import { DEFAULT_SETTINGS, HOVER_LINK_SOURCE_ID, VAULT_EXPLORER_VIEW } from './constants'; import _ from 'lodash'; import EventManager from './event/event-manager'; -import { isVersionLessThan } from './utils'; -import { VaultExplorerPluginSettings_0_3_3 } from './types/types-0.3.0'; -import { VaultExplorerPluginSettings_0_5_5 } from './types/types-0.5.5'; +import { preformMigrations } from './migrations'; import Logger from 'js-logger'; import { formatMessageForLogger, stringToLogLevel } from './logger'; -import { LOG_LEVEL_WARN } from './logger/constants'; -import { VaultExplorerPluginSettings_1_0_1 } from './types/types-1.0.1'; import { moveFocus } from './focus-utils'; -import { VaultExplorerPluginSettings_1_2_0, ViewType_1_2_0 } from './types/types-1.2.0'; -import { VaultExplorerPluginSettings_1_2_1 } from './types/types-1.2.1'; -import { PropertyFilterGroup_1_5_0, PropertyFilter_1_5_0, VaultExplorerPluginSettings_1_5_0 } from './types/types-1.5.0'; -import { VaultExplorerPluginSettings_1_6_0 } from './types/types-1.6.0'; import { loadDeviceId } from './svelte/shared/services/device-id-utils'; import License from './svelte/shared/services/license'; -import { VaultExplorerPluginSettings_1_8_1 } from './types/types-1.8.1'; -import { VaultExplorerPluginSettings_1_9_1 } from './types/types-1.9.1'; -import { VaultExplorerPluginSettings_1_12_1 } from './types/types-1.12.1'; -import { VaultExplorerPluginSettings_1_13_1 } from './types/types-1.13.1'; -import { VaultExplorerPluginSettings_1_14_2 } from './types/types-1.14.2'; -import { VaultExplorerPluginSettings_1_16_0, ViewType_1_16_0 } from './types/types-1.16.0'; export default class VaultExplorerPlugin extends Plugin { settings: VaultExplorerPluginSettings = DEFAULT_SETTINGS; @@ -133,320 +119,21 @@ export default class VaultExplorerPlugin extends Plugin { } async loadSettings() { - let data: Record | null = await this.loadData(); + const loadedData: Record | null = await this.loadData(); - if (data !== null) { - //This will be null if the settings are from a version before 0.3.0 - const settingsVersion = (data["pluginVersion"] as string) ?? null; - if (settingsVersion !== null) { - if (isVersionLessThan(settingsVersion, "0.4.0")) { - console.log("Upgrading settings from version 0.3.3 to 0.4.0"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_0_3_3; - const newData: VaultExplorerPluginSettings_0_5_5 = { - ...typedData, - filters: { - ...typedData.filters, - properties: { - ...typedData.filters.properties, - groups: [] - } - } - } - data = newData as unknown as Record; - } - - if (isVersionLessThan(settingsVersion, "1.0.0")) { - console.log("Upgrading settings from version 0.5.5 to 1.0.0"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_0_5_5; - const newData: VaultExplorerPluginSettings_1_0_1 = { - ...typedData, - logLevel: LOG_LEVEL_WARN, - filters: { - ...typedData.filters, - properties: { - ...typedData.filters.properties, - groups: typedData.filters.properties.groups.map(group => { - const { id, name, filters, isEnabled } = group; - return { - id, - name, - filters, - isEnabled - } - }) - } - } - } - data = newData as unknown as Record; - } - - if (isVersionLessThan(settingsVersion, "1.1.0")) { - console.log("Upgrading settings from version 1.0.1 to 1.1.0"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_1_0_1; - const newData: VaultExplorerPluginSettings_1_2_0 = { - ...typedData, - views: { - currentView: typedData.currentView as unknown as ViewType_1_2_0, - order: [ViewType_1_2_0.GRID, ViewType_1_2_0.LIST] - } - } - data = newData as unknown as Record; - } - - if (isVersionLessThan(settingsVersion, "1.2.1")) { - console.log("Upgrading settings from version 1.2.0 to 1.2.1"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_1_2_0; - const newData: VaultExplorerPluginSettings_1_2_1 = { - ...typedData, - views: { - ...typedData.views, - titleWrapping: "normal" - } - } - data = newData as unknown as Record; - } - - if (isVersionLessThan(settingsVersion, "1.3.0")) { - console.log("Upgrading settings from version 1.2.1 to 1.3.0"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_1_2_1; - const groups = typedData.filters.properties.groups; - - const updatedGroups: PropertyFilterGroup_1_5_0[] = groups.map(group => { - const updatedFilters: PropertyFilter_1_5_0[] = group.filters.map(filter => { - return { - ...filter, - type: filter.type as any, - matchWhenPropertyDNE: false - } - }); - return { - ...group, - filters: updatedFilters - } - }); - - const newData: VaultExplorerPluginSettings_1_5_0 = { - ...typedData, - filters: { - ...typedData.filters, - properties: { - ...typedData.filters.properties, - groups: updatedGroups, - } - } - } - data = newData as unknown as Record; - } - - if (isVersionLessThan(settingsVersion, "1.6.0")) { - console.log("Upgrading settings from version 1.5.0 to 1.6.0"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_1_5_0; - const newData: VaultExplorerPluginSettings_1_6_0 = { - ...typedData, - properties: { - ...typedData.properties, - creationDate: "", - modifiedDate: "" - } - } - data = newData as unknown as Record; - } - - if (isVersionLessThan(settingsVersion, "1.6.1")) { - console.log("Upgrading settings from version 1.6.0 to 1.6.1"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_1_6_0; - const newData: VaultExplorerPluginSettings_1_8_1 = { - ...typedData, - properties: { - ...typedData.properties, - createdDate: "", - modifiedDate: "" - } - } - data = newData as unknown as Record; - } - - if (isVersionLessThan(settingsVersion, "1.9.0")) { - console.log("Upgrading settings from version 1.8.1 to 1.9.0"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_1_8_1; - const newData: VaultExplorerPluginSettings_1_9_1 = { - ...typedData, - filters: { - ...typedData.filters, - custom: { - selectedGroupId: typedData.filters.properties.selectedGroupId, - groups: typedData.filters.properties.groups.map(group => { - const rules = group.filters.map(filter => { - return { - ...filter, - valueData: "", - type: filter.type as any - } - }); - return { - ...group, - rules - } - }) - } - } - } - delete (newData.filters as any).properties; - for (const group of newData.filters.custom.groups as any) { - delete group.filters; - } - data = newData as unknown as Record; - } - } - - if (isVersionLessThan(settingsVersion, "1.10.0")) { - console.log("Upgrading settings from version 1.9.1 to 1.10.0"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_1_9_1; - const newData: VaultExplorerPluginSettings_1_12_1 = { - ...typedData, - filters: { - ...typedData.filters, - custom: { - ...typedData.filters.custom, - groups: typedData.filters.custom.groups.map(group => { - const rules = group.rules.map(rule => { - return { - ...rule, - type: FilterRuleType.PROPERTY as any, - propertyType: rule.type as any, - } - }); - return { - ...group, - rules - } - }) - } - } - } - delete (newData.filters as any).folder; - delete (newData.filters as any).properties; - for (const group of newData.filters.custom.groups as any) { - delete group.filters; - } - data = newData as unknown as Record; - } - - if (isVersionLessThan(settingsVersion, "1.13.0")) { - console.log("Upgrading settings from version 1.12.1 to 1.13.0"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_1_12_1; - const newData: VaultExplorerPluginSettings_1_13_1 = { - ...typedData, - views: { - ...typedData.views, - enableClockUpdates: true - }, - filters: { - ...typedData.filters, - custom: { - ...typedData.filters.custom, - groups: typedData.filters.custom.groups.map(group => { - return { - ...group, - isSticky: false - } - }) - } - } - } - data = newData as unknown as Record; - } - - if (isVersionLessThan(settingsVersion, "1.14.0")) { - console.log("Upgrading settings from version 1.13.1 to 1.14.0"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_1_13_1; - const newData: VaultExplorerPluginSettings_1_14_2 = { - ...typedData, - filters: { - ...typedData.filters, - search: { - isEnabled: true, - value: typedData.filters.search - }, - favorites: { - isEnabled: true, - value: typedData.filters.onlyFavorites - }, - timestamp: { - isEnabled: true, - value: typedData.filters.timestamp - }, - sort: { - isEnabled: true, - value: typedData.filters.sort - }, - custom: { - isEnabled: true, - ...typedData.filters.custom - }, - }, - enableScrollButtons: true, - } - delete (newData.filters as any).onlyFavorites; - data = newData as unknown as Record; - } + let currentData: Record = {}; - if (isVersionLessThan(settingsVersion, "1.15.0")) { - console.log("Upgrading settings from version 1.14.2 to 1.15.0"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_1_14_2; - const newData: VaultExplorerPluginSettings_1_16_0 = { - ...typedData, - views: { - ...typedData.views, - order: [...typedData.views.order, ViewType_1_16_0.FEED], - } - } - data = newData as unknown as Record; - } - - if (isVersionLessThan(settingsVersion, "1.17.0")) { - console.log("Upgrading settings from version 1.16.0 to 1.17.0"); - const typedData = (data as unknown) as VaultExplorerPluginSettings_1_16_0; - const newData: VaultExplorerPluginSettings = { - ...typedData, - views: { - dashboard: { - isEnabled: false - }, - grid: { - isEnabled: true - }, - list: { - isEnabled: true - }, - table: { - isEnabled: false - }, - feed: { - isEnabled: true - }, - recommended: { - isEnabled: false - }, - related: { - isEnabled: false - } - }, - viewOrder: typedData.views.order as unknown as TExplorerView[], - enableClockUpdates: typedData.views.enableClockUpdates, - currentView: typedData.views.currentView as unknown as TExplorerView, - titleWrapping: typedData.views.titleWrapping - } - delete (newData as any).views.order; - delete (newData as any).views.currentView; - delete (newData as any).views.enableClockUpdates; - delete (newData as any).views.titleWrapping; - data = newData as unknown as Record; + if (loadedData !== null) { + //This will be undefined if the settings are from a version before 0.3.0 + const loadedVersion = loadedData["pluginVersion"] as string ?? null; + if (loadedVersion !== null) { + const newData = preformMigrations(loadedVersion, loadedData); + currentData = newData; } } - //Apply default settings. This will make it so we don't need to do migrations for just adding new settings - this.settings = Object.assign({}, DEFAULT_SETTINGS, data); + //Apply default settings + this.settings = Object.assign({}, DEFAULT_SETTINGS, currentData); //Update the plugin version to the current version this.settings.pluginVersion = this.manifest.version; await this.saveSettings(); diff --git a/src/migrations/index.ts b/src/migrations/index.ts new file mode 100644 index 0000000..70365ec --- /dev/null +++ b/src/migrations/index.ts @@ -0,0 +1,99 @@ +import { TMigration } from "./types"; +import Migrate_0_4_0 from "./migrate_0_4_0"; +import Migrate_1_1_0 from "./migrate_1_1_0"; +import Migrate_1_0_0 from "./migrate_1_0_0"; +import Migrate_1_2_1 from "./migrate_1_2_1"; +import Migrate_1_3_0 from "./migrate_1_3_0"; +import Migrate_1_6_0 from "./migrate_1_6_0"; +import Migrate_1_6_1 from "./migrate_1_6_1"; +import Migrate_1_9_0 from "./migrate_1_9_0"; +import Migrate_1_14_0 from "./migrate_1_14_0"; +import Migrate_1_15_0 from "./migrate_1_15_0"; +import Migrate_1_17_0 from "./migrate_1_17_0"; +import Migrate_1_13_0 from "./migrate_1_13_0"; +import Migrate_1_10_0 from "./migrate_1_10_0"; +import { isVersionLessThan } from "src/utils"; + +const migrations: TMigration[] = [ + { + from: "0.3.3", + to: "0.4.0", + migrate: Migrate_0_4_0, + }, + { + from: "0.5.5", + to: "1.0.0", + migrate: Migrate_1_0_0, + }, + { + from: "1.0.1", + to: "1.1.0", + migrate: Migrate_1_1_0, + }, + { + from: "1.2.0", + to: "1.2.1", + migrate: Migrate_1_2_1, + }, + { + from: "1.2.1", + to: "1.3.0", + migrate: Migrate_1_3_0, + }, + { + from: "1.5.0", + to: "1.6.0", + migrate: Migrate_1_6_0, + }, + { + from: "1.6.0", + to: "1.6.1", + migrate: Migrate_1_6_1, + }, + { + from: "1.8.1", + to: "1.9.0", + migrate: Migrate_1_9_0, + }, + { + from: "1.9.1", + to: "1.10.0", + migrate: Migrate_1_10_0, + }, + { + from: "1.12.1", + to: "1.13.0", + migrate: Migrate_1_13_0, + }, + { + from: "1.13.1", + to: "1.14.0", + migrate: Migrate_1_14_0, + }, + { + from: "1.14.2", + to: "1.15.0", + migrate: Migrate_1_15_0, + }, + { + from: "1.16.0", + to: "1.17.0", + migrate: Migrate_1_17_0, + }, +]; + +export const preformMigrations = (settingsVersion: string, data: Record) => { + let updatedData = structuredClone(data); + + for (const migration of migrations) { + const { from, to } = migration; + if (isVersionLessThan(settingsVersion, to)) { + console.log(`Upgrading settings from version ${from} to ${to}`); + const migrator = new migration.migrate(); + const newData = migrator.migrate(updatedData); + updatedData = newData; + } + } + + return updatedData; +} diff --git a/src/migrations/migrate_0_4_0.ts b/src/migrations/migrate_0_4_0.ts new file mode 100644 index 0000000..81960af --- /dev/null +++ b/src/migrations/migrate_0_4_0.ts @@ -0,0 +1,20 @@ +import { VaultExplorerPluginSettings_0_5_5 } from "src/types/types-0.5.5"; +import { VaultExplorerPluginSettings_0_3_3 } from "src/types/types-0.3.3"; +import MigrationInterface from "./migration_interface"; + +export default class Migrate_0_4_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_0_3_3; + const newData: VaultExplorerPluginSettings_0_5_5 = { + ...typedData, + filters: { + ...typedData.filters, + properties: { + ...typedData.filters.properties, + groups: [] + } + }, + } + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_0_0.ts b/src/migrations/migrate_1_0_0.ts new file mode 100644 index 0000000..94b7b2e --- /dev/null +++ b/src/migrations/migrate_1_0_0.ts @@ -0,0 +1,30 @@ +import { VaultExplorerPluginSettings_0_5_5 } from "src/types/types-0.5.5"; +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_0_1 } from "src/types/types-1.0.1"; +import { LOG_LEVEL_WARN } from "src/logger/constants"; + +export default class Migrate_1_0_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_0_5_5; + const newData: VaultExplorerPluginSettings_1_0_1 = { + ...typedData, + logLevel: LOG_LEVEL_WARN, + filters: { + ...typedData.filters, + properties: { + ...typedData.filters.properties, + groups: typedData.filters.properties.groups.map(group => { + const { id, name, filters, isEnabled } = group; + return { + id, + name, + filters, + isEnabled + } + }) + } + } + } + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_10_0.ts b/src/migrations/migrate_1_10_0.ts new file mode 100644 index 0000000..3c33707 --- /dev/null +++ b/src/migrations/migrate_1_10_0.ts @@ -0,0 +1,37 @@ +import { FilterRuleType_1_12_1, VaultExplorerPluginSettings_1_12_1 } from "src/types/types-1.12.1"; +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_9_1 } from "src/types/types-1.9.1"; + +export default class Migrate_1_10_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_1_9_1; + const newData: VaultExplorerPluginSettings_1_12_1 = { + ...typedData, + filters: { + ...typedData.filters, + custom: { + ...typedData.filters.custom, + groups: typedData.filters.custom.groups.map(group => { + const rules = group.rules.map(rule => { + return { + ...rule, + type: FilterRuleType_1_12_1.PROPERTY as any, + propertyType: rule.type as any, + } + }); + return { + ...group, + rules + } + }) + } + } + } + delete (newData.filters as any).folder; + delete (newData.filters as any).properties; + for (const group of newData.filters.custom.groups as any) { + delete group.filters; + } + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_13_0.ts b/src/migrations/migrate_1_13_0.ts new file mode 100644 index 0000000..80d503a --- /dev/null +++ b/src/migrations/migrate_1_13_0.ts @@ -0,0 +1,29 @@ +import { VaultExplorerPluginSettings_1_13_1 } from "src/types/types-1.13.1"; +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_12_1 } from "src/types/types-1.12.1"; + +export default class Migrate_1_13_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_1_12_1; + const newData: VaultExplorerPluginSettings_1_13_1 = { + ...typedData, + views: { + ...typedData.views, + enableClockUpdates: true + }, + filters: { + ...typedData.filters, + custom: { + ...typedData.filters.custom, + groups: typedData.filters.custom.groups.map(group => { + return { + ...group, + isSticky: false + } + }) + } + } + } + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_14_0.ts b/src/migrations/migrate_1_14_0.ts new file mode 100644 index 0000000..b5f313f --- /dev/null +++ b/src/migrations/migrate_1_14_0.ts @@ -0,0 +1,38 @@ +import { VaultExplorerPluginSettings_1_14_2 } from "src/types/types-1.14.2"; +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_13_1 } from "src/types/types-1.13.1"; + +export default class Migrate_1_14_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_1_13_1; + const newData: VaultExplorerPluginSettings_1_14_2 = { + ...typedData, + filters: { + ...typedData.filters, + search: { + isEnabled: true, + value: typedData.filters.search + }, + favorites: { + isEnabled: true, + value: typedData.filters.onlyFavorites + }, + timestamp: { + isEnabled: true, + value: typedData.filters.timestamp + }, + sort: { + isEnabled: true, + value: typedData.filters.sort + }, + custom: { + isEnabled: true, + ...typedData.filters.custom + }, + }, + enableScrollButtons: true, + } + delete (newData.filters as any).onlyFavorites; + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_15_0.ts b/src/migrations/migrate_1_15_0.ts new file mode 100644 index 0000000..6af05e0 --- /dev/null +++ b/src/migrations/migrate_1_15_0.ts @@ -0,0 +1,17 @@ +import { VaultExplorerPluginSettings_1_16_0, ViewType_1_16_0 } from "src/types/types-1.16.0"; +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_14_2 } from "src/types/types-1.14.2"; + +export default class Migrate_1_15_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_1_14_2; + const newData: VaultExplorerPluginSettings_1_16_0 = { + ...typedData, + views: { + ...typedData.views, + order: [...typedData.views.order, ViewType_1_16_0.FEED], + } + } + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_17_0.ts b/src/migrations/migrate_1_17_0.ts new file mode 100644 index 0000000..4a8b4c3 --- /dev/null +++ b/src/migrations/migrate_1_17_0.ts @@ -0,0 +1,44 @@ +import { TExplorerView, VaultExplorerPluginSettings } from "src/types"; +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_16_0 } from "src/types/types-1.16.0"; + +export default class Migrate_1_17_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_1_16_0; + const newData: VaultExplorerPluginSettings = { + ...typedData, + views: { + dashboard: { + isEnabled: false + }, + grid: { + isEnabled: true + }, + list: { + isEnabled: true + }, + table: { + isEnabled: false + }, + feed: { + isEnabled: true + }, + recommended: { + isEnabled: false + }, + related: { + isEnabled: false + } + }, + viewOrder: typedData.views.order as unknown as TExplorerView[], + enableClockUpdates: typedData.views.enableClockUpdates, + currentView: typedData.views.currentView as unknown as TExplorerView, + titleWrapping: typedData.views.titleWrapping + } + delete (newData as any).views.order; + delete (newData as any).views.currentView; + delete (newData as any).views.enableClockUpdates; + delete (newData as any).views.titleWrapping; + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_1_0.ts b/src/migrations/migrate_1_1_0.ts new file mode 100644 index 0000000..e8c541d --- /dev/null +++ b/src/migrations/migrate_1_1_0.ts @@ -0,0 +1,17 @@ +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_0_1 } from "src/types/types-1.0.1"; +import { VaultExplorerPluginSettings_1_2_0, ViewType_1_2_0 } from "src/types/types-1.2.0"; + +export default class Migrate_1_1_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_1_0_1; + const newData: VaultExplorerPluginSettings_1_2_0 = { + ...typedData, + views: { + currentView: typedData.currentView as unknown as ViewType_1_2_0, + order: [ViewType_1_2_0.GRID, ViewType_1_2_0.LIST] + } + } + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_2_1.ts b/src/migrations/migrate_1_2_1.ts new file mode 100644 index 0000000..a4c5aa2 --- /dev/null +++ b/src/migrations/migrate_1_2_1.ts @@ -0,0 +1,17 @@ +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_2_0 } from "src/types/types-1.2.0"; +import { VaultExplorerPluginSettings_1_2_1 } from "src/types/types-1.2.1"; + +export default class Migrate_1_2_1 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_1_2_0; + const newData: VaultExplorerPluginSettings_1_2_1 = { + ...typedData, + views: { + ...typedData.views, + titleWrapping: "normal" + } + } + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_3_0.ts b/src/migrations/migrate_1_3_0.ts new file mode 100644 index 0000000..c1c4b23 --- /dev/null +++ b/src/migrations/migrate_1_3_0.ts @@ -0,0 +1,36 @@ +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_2_1 } from "src/types/types-1.2.1"; +import { PropertyFilterGroup_1_5_0, PropertyFilter_1_5_0, VaultExplorerPluginSettings_1_5_0 } from "src/types/types-1.5.0"; + +export default class Migrate_1_3_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_1_2_1; + const groups = typedData.filters.properties.groups; + + const updatedGroups: PropertyFilterGroup_1_5_0[] = groups.map(group => { + const updatedFilters: PropertyFilter_1_5_0[] = group.filters.map(filter => { + return { + ...filter, + type: filter.type as any, + matchWhenPropertyDNE: false + } + }); + return { + ...group, + filters: updatedFilters + } + }); + + const newData: VaultExplorerPluginSettings_1_5_0 = { + ...typedData, + filters: { + ...typedData.filters, + properties: { + ...typedData.filters.properties, + groups: updatedGroups, + } + } + } + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_6_0.ts b/src/migrations/migrate_1_6_0.ts new file mode 100644 index 0000000..ff59330 --- /dev/null +++ b/src/migrations/migrate_1_6_0.ts @@ -0,0 +1,18 @@ +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_5_0 } from "src/types/types-1.5.0"; +import { VaultExplorerPluginSettings_1_6_0 } from "src/types/types-1.6.0"; + +export default class Migrate_1_6_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_1_5_0; + const newData: VaultExplorerPluginSettings_1_6_0 = { + ...typedData, + properties: { + ...typedData.properties, + creationDate: "", + modifiedDate: "" + } + } + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_6_1.ts b/src/migrations/migrate_1_6_1.ts new file mode 100644 index 0000000..e97092a --- /dev/null +++ b/src/migrations/migrate_1_6_1.ts @@ -0,0 +1,19 @@ +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_6_0 } from "src/types/types-1.6.0"; +import { VaultExplorerPluginSettings_1_8_1 } from "src/types/types-1.8.1"; + +export default class Migrate_1_6_1 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_1_6_0; + const newData: VaultExplorerPluginSettings_1_8_1 = { + ...typedData, + properties: { + ...typedData.properties, + createdDate: "", + modifiedDate: "" + } + } + delete (newData.properties as any).creationDate; + return newData as unknown as Record; + } +} diff --git a/src/migrations/migrate_1_9_0.ts b/src/migrations/migrate_1_9_0.ts new file mode 100644 index 0000000..55f5baa --- /dev/null +++ b/src/migrations/migrate_1_9_0.ts @@ -0,0 +1,36 @@ +import MigrationInterface from "./migration_interface"; +import { VaultExplorerPluginSettings_1_8_1 } from "src/types/types-1.8.1"; +import { VaultExplorerPluginSettings_1_9_1 } from "src/types/types-1.9.1"; + +export default class Migrate_1_9_0 implements MigrationInterface { + migrate(data: Record) { + const typedData = (data as unknown) as VaultExplorerPluginSettings_1_8_1; + const newData: VaultExplorerPluginSettings_1_9_1 = { + ...typedData, + filters: { + ...typedData.filters, + custom: { + selectedGroupId: typedData.filters.properties.selectedGroupId, + groups: typedData.filters.properties.groups.map(group => { + const rules = group.filters.map(filter => { + return { + ...filter, + valueData: "", + type: filter.type as any + } + }); + return { + ...group, + rules + } + }) + } + } + } + delete (newData.filters as any).properties; + for (const group of newData.filters.custom.groups as any) { + delete group.filters; + } + return newData as unknown as Record; + } +} diff --git a/src/migrations/migration_interface.ts b/src/migrations/migration_interface.ts new file mode 100644 index 0000000..eeb6362 --- /dev/null +++ b/src/migrations/migration_interface.ts @@ -0,0 +1,3 @@ +export default abstract class MigrationInterface { + abstract migrate(previous: Record): Record; +} diff --git a/src/migrations/types.ts b/src/migrations/types.ts new file mode 100644 index 0000000..9a478d1 --- /dev/null +++ b/src/migrations/types.ts @@ -0,0 +1,7 @@ +import MigrationInterface from "./migration_interface"; + +export interface TMigration { + from: string; + to: string; + migrate: new () => MigrationInterface; +} diff --git a/src/svelte/app/index.svelte b/src/svelte/app/index.svelte index 050d9ee..1552b55 100644 --- a/src/svelte/app/index.svelte +++ b/src/svelte/app/index.svelte @@ -313,7 +313,7 @@ if (data.length > 0 && data[0] instanceof TFile) { const file = data[0] as TFile; const content = await plugin.app.vault.cachedRead(file); - console.log(content); + const updatedContentForFiles = contentForFiles.map((entry) => { if (entry.path === file.path) { return { @@ -658,7 +658,7 @@ let filteredCustom: TFile[] = []; $: if (frontmatterCacheTime && customFilter.groups) { - console.log("Frontmatter cache time", frontmatterCacheTime); + Logger.debug(`Frontmatter cache time: ${frontmatterCacheTime}`); filteredCustom = files.filter((file) => { const { name, path } = file; const frontmatter = diff --git a/src/types/index.guard.ts b/src/types/index.guard.ts new file mode 100644 index 0000000..e0b6936 --- /dev/null +++ b/src/types/index.guard.ts @@ -0,0 +1,643 @@ +/* + * Generated type guards for "index.ts". + * WARNING: Do not manually change this file. + */ +import { FilterRuleType, TextFilterCondition, NumberFilterCondition, DateFilterCondition, CheckboxFilterCondition, ListFilterCondition, ContentFilterCondition, FolderFilterCondition, FileNameFilterCondition, PropertyType, TExplorerView, VaultExplorerPluginSettings } from "./index"; + +export function isVaultExplorerPluginSettings(obj: unknown): obj is VaultExplorerPluginSettings { + const typedObj = obj as VaultExplorerPluginSettings + return ( + (typedObj !== null && + typeof typedObj === "object" || + typeof typedObj === "function") && + (typedObj["properties"] !== null && + typeof typedObj["properties"] === "object" || + typeof typedObj["properties"] === "function") && + typeof typedObj["properties"]["favorite"] === "string" && + typeof typedObj["properties"]["url"] === "string" && + typeof typedObj["properties"]["createdDate"] === "string" && + typeof typedObj["properties"]["modifiedDate"] === "string" && + typeof typedObj["properties"]["custom1"] === "string" && + typeof typedObj["properties"]["custom2"] === "string" && + typeof typedObj["properties"]["custom3"] === "string" && + (typedObj["filters"] !== null && + typeof typedObj["filters"] === "object" || + typeof typedObj["filters"] === "function") && + (typedObj["filters"]["search"] !== null && + typeof typedObj["filters"]["search"] === "object" || + typeof typedObj["filters"]["search"] === "function") && + typeof typedObj["filters"]["search"]["isEnabled"] === "boolean" && + typeof typedObj["filters"]["search"]["value"] === "string" && + (typedObj["filters"]["favorites"] !== null && + typeof typedObj["filters"]["favorites"] === "object" || + typeof typedObj["filters"]["favorites"] === "function") && + typeof typedObj["filters"]["favorites"]["isEnabled"] === "boolean" && + typeof typedObj["filters"]["favorites"]["value"] === "boolean" && + (typedObj["filters"]["sort"] !== null && + typeof typedObj["filters"]["sort"] === "object" || + typeof typedObj["filters"]["sort"] === "function") && + typeof typedObj["filters"]["sort"]["isEnabled"] === "boolean" && + (typedObj["filters"]["sort"]["value"] === "file-name-asc" || + typedObj["filters"]["sort"]["value"] === "file-name-desc" || + typedObj["filters"]["sort"]["value"] === "modified-asc" || + typedObj["filters"]["sort"]["value"] === "modified-desc") && + (typedObj["filters"]["timestamp"] !== null && + typeof typedObj["filters"]["timestamp"] === "object" || + typeof typedObj["filters"]["timestamp"] === "function") && + typeof typedObj["filters"]["timestamp"]["isEnabled"] === "boolean" && + (typedObj["filters"]["timestamp"]["value"] === "created-today" || + typedObj["filters"]["timestamp"]["value"] === "modified-today" || + typedObj["filters"]["timestamp"]["value"] === "created-this-week" || + typedObj["filters"]["timestamp"]["value"] === "modified-this-week" || + typedObj["filters"]["timestamp"]["value"] === "created-2-weeks" || + typedObj["filters"]["timestamp"]["value"] === "modified-2-weeks" || + typedObj["filters"]["timestamp"]["value"] === "all") && + (typedObj["filters"]["custom"] !== null && + typeof typedObj["filters"]["custom"] === "object" || + typeof typedObj["filters"]["custom"] === "function") && + typeof typedObj["filters"]["custom"]["isEnabled"] === "boolean" && + typeof typedObj["filters"]["custom"]["selectedGroupId"] === "string" && + Array.isArray(typedObj["filters"]["custom"]["groups"]) && + typedObj["filters"]["custom"]["groups"].every((e: any) => + (e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["id"] === "string" && + typeof e["name"] === "string" && + Array.isArray(e["rules"]) && + e["rules"].every((e: any) => + ((e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["id"] === "string" && + (e["operator"] === "and" || + e["operator"] === "or") && + (e["type"] === FilterRuleType.PROPERTY || + e["type"] === FilterRuleType.FOLDER || + e["type"] === FilterRuleType.FILE_NAME || + e["type"] === FilterRuleType.CONTENT) && + (e["condition"] === TextFilterCondition.IS || + e["condition"] === TextFilterCondition.IS_NOT || + e["condition"] === TextFilterCondition.CONTAINS || + e["condition"] === TextFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === TextFilterCondition.STARTS_WITH || + e["condition"] === TextFilterCondition.ENDS_WITH || + e["condition"] === TextFilterCondition.EXISTS || + e["condition"] === TextFilterCondition.DOES_NOT_EXIST || + e["condition"] === NumberFilterCondition.IS_EQUAL || + e["condition"] === NumberFilterCondition.IS_NOT_EQUAL || + e["condition"] === NumberFilterCondition.IS_GREATER || + e["condition"] === NumberFilterCondition.IS_LESS || + e["condition"] === NumberFilterCondition.IS_GREATER_OR_EQUAL || + e["condition"] === NumberFilterCondition.IS_LESS_OR_EQUAL || + e["condition"] === NumberFilterCondition.EXISTS || + e["condition"] === NumberFilterCondition.DOES_NOT_EXIST || + e["condition"] === DateFilterCondition.IS || + e["condition"] === DateFilterCondition.IS_BEFORE || + e["condition"] === DateFilterCondition.IS_AFTER || + e["condition"] === DateFilterCondition.IS_ON_OR_BEFORE || + e["condition"] === DateFilterCondition.IS_ON_OR_AFTER || + e["condition"] === DateFilterCondition.EXISTS || + e["condition"] === DateFilterCondition.DOES_NOT_EXIST || + e["condition"] === CheckboxFilterCondition.IS || + e["condition"] === CheckboxFilterCondition.IS_NOT || + e["condition"] === CheckboxFilterCondition.EXISTS || + e["condition"] === CheckboxFilterCondition.DOES_NOT_EXIST || + e["condition"] === ListFilterCondition.CONTAINS || + e["condition"] === ListFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ListFilterCondition.EXISTS || + e["condition"] === ListFilterCondition.DOES_NOT_EXIST || + e["condition"] === ContentFilterCondition.CONTAINS || + e["condition"] === ContentFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ContentFilterCondition.IS_EMPTY || + e["condition"] === ContentFilterCondition.IS_NOT_EMPTY || + e["condition"] === FolderFilterCondition.IS || + e["condition"] === FolderFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.IS || + e["condition"] === FileNameFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.CONTAINS || + e["condition"] === FileNameFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === FileNameFilterCondition.STARTS_WITH || + e["condition"] === FileNameFilterCondition.ENDS_WITH) && + typeof e["isEnabled"] === "boolean" && + typeof e["value"] === "string" && + typeof e["matchWhenPropertyDNE"] === "boolean" && + e["type"] === FilterRuleType.PROPERTY && + e["propertyType"] === PropertyType.TEXT && + typeof e["propertyName"] === "string" && + (e["condition"] === TextFilterCondition.IS || + e["condition"] === TextFilterCondition.IS_NOT || + e["condition"] === TextFilterCondition.CONTAINS || + e["condition"] === TextFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === TextFilterCondition.STARTS_WITH || + e["condition"] === TextFilterCondition.ENDS_WITH || + e["condition"] === TextFilterCondition.EXISTS || + e["condition"] === TextFilterCondition.DOES_NOT_EXIST) || + (e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["id"] === "string" && + (e["operator"] === "and" || + e["operator"] === "or") && + (e["type"] === FilterRuleType.PROPERTY || + e["type"] === FilterRuleType.FOLDER || + e["type"] === FilterRuleType.FILE_NAME || + e["type"] === FilterRuleType.CONTENT) && + (e["condition"] === TextFilterCondition.IS || + e["condition"] === TextFilterCondition.IS_NOT || + e["condition"] === TextFilterCondition.CONTAINS || + e["condition"] === TextFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === TextFilterCondition.STARTS_WITH || + e["condition"] === TextFilterCondition.ENDS_WITH || + e["condition"] === TextFilterCondition.EXISTS || + e["condition"] === TextFilterCondition.DOES_NOT_EXIST || + e["condition"] === NumberFilterCondition.IS_EQUAL || + e["condition"] === NumberFilterCondition.IS_NOT_EQUAL || + e["condition"] === NumberFilterCondition.IS_GREATER || + e["condition"] === NumberFilterCondition.IS_LESS || + e["condition"] === NumberFilterCondition.IS_GREATER_OR_EQUAL || + e["condition"] === NumberFilterCondition.IS_LESS_OR_EQUAL || + e["condition"] === NumberFilterCondition.EXISTS || + e["condition"] === NumberFilterCondition.DOES_NOT_EXIST || + e["condition"] === DateFilterCondition.IS || + e["condition"] === DateFilterCondition.IS_BEFORE || + e["condition"] === DateFilterCondition.IS_AFTER || + e["condition"] === DateFilterCondition.IS_ON_OR_BEFORE || + e["condition"] === DateFilterCondition.IS_ON_OR_AFTER || + e["condition"] === DateFilterCondition.EXISTS || + e["condition"] === DateFilterCondition.DOES_NOT_EXIST || + e["condition"] === CheckboxFilterCondition.IS || + e["condition"] === CheckboxFilterCondition.IS_NOT || + e["condition"] === CheckboxFilterCondition.EXISTS || + e["condition"] === CheckboxFilterCondition.DOES_NOT_EXIST || + e["condition"] === ListFilterCondition.CONTAINS || + e["condition"] === ListFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ListFilterCondition.EXISTS || + e["condition"] === ListFilterCondition.DOES_NOT_EXIST || + e["condition"] === ContentFilterCondition.CONTAINS || + e["condition"] === ContentFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ContentFilterCondition.IS_EMPTY || + e["condition"] === ContentFilterCondition.IS_NOT_EMPTY || + e["condition"] === FolderFilterCondition.IS || + e["condition"] === FolderFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.IS || + e["condition"] === FileNameFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.CONTAINS || + e["condition"] === FileNameFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === FileNameFilterCondition.STARTS_WITH || + e["condition"] === FileNameFilterCondition.ENDS_WITH) && + typeof e["isEnabled"] === "boolean" && + typeof e["value"] === "string" && + typeof e["matchWhenPropertyDNE"] === "boolean" && + e["type"] === FilterRuleType.PROPERTY && + e["propertyType"] === PropertyType.NUMBER && + typeof e["propertyName"] === "string" && + (e["condition"] === NumberFilterCondition.IS_EQUAL || + e["condition"] === NumberFilterCondition.IS_NOT_EQUAL || + e["condition"] === NumberFilterCondition.IS_GREATER || + e["condition"] === NumberFilterCondition.IS_LESS || + e["condition"] === NumberFilterCondition.IS_GREATER_OR_EQUAL || + e["condition"] === NumberFilterCondition.IS_LESS_OR_EQUAL || + e["condition"] === NumberFilterCondition.EXISTS || + e["condition"] === NumberFilterCondition.DOES_NOT_EXIST) || + (e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["id"] === "string" && + (e["operator"] === "and" || + e["operator"] === "or") && + (e["type"] === FilterRuleType.PROPERTY || + e["type"] === FilterRuleType.FOLDER || + e["type"] === FilterRuleType.FILE_NAME || + e["type"] === FilterRuleType.CONTENT) && + (e["condition"] === TextFilterCondition.IS || + e["condition"] === TextFilterCondition.IS_NOT || + e["condition"] === TextFilterCondition.CONTAINS || + e["condition"] === TextFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === TextFilterCondition.STARTS_WITH || + e["condition"] === TextFilterCondition.ENDS_WITH || + e["condition"] === TextFilterCondition.EXISTS || + e["condition"] === TextFilterCondition.DOES_NOT_EXIST || + e["condition"] === NumberFilterCondition.IS_EQUAL || + e["condition"] === NumberFilterCondition.IS_NOT_EQUAL || + e["condition"] === NumberFilterCondition.IS_GREATER || + e["condition"] === NumberFilterCondition.IS_LESS || + e["condition"] === NumberFilterCondition.IS_GREATER_OR_EQUAL || + e["condition"] === NumberFilterCondition.IS_LESS_OR_EQUAL || + e["condition"] === NumberFilterCondition.EXISTS || + e["condition"] === NumberFilterCondition.DOES_NOT_EXIST || + e["condition"] === DateFilterCondition.IS || + e["condition"] === DateFilterCondition.IS_BEFORE || + e["condition"] === DateFilterCondition.IS_AFTER || + e["condition"] === DateFilterCondition.IS_ON_OR_BEFORE || + e["condition"] === DateFilterCondition.IS_ON_OR_AFTER || + e["condition"] === DateFilterCondition.EXISTS || + e["condition"] === DateFilterCondition.DOES_NOT_EXIST || + e["condition"] === CheckboxFilterCondition.IS || + e["condition"] === CheckboxFilterCondition.IS_NOT || + e["condition"] === CheckboxFilterCondition.EXISTS || + e["condition"] === CheckboxFilterCondition.DOES_NOT_EXIST || + e["condition"] === ListFilterCondition.CONTAINS || + e["condition"] === ListFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ListFilterCondition.EXISTS || + e["condition"] === ListFilterCondition.DOES_NOT_EXIST || + e["condition"] === ContentFilterCondition.CONTAINS || + e["condition"] === ContentFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ContentFilterCondition.IS_EMPTY || + e["condition"] === ContentFilterCondition.IS_NOT_EMPTY || + e["condition"] === FolderFilterCondition.IS || + e["condition"] === FolderFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.IS || + e["condition"] === FileNameFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.CONTAINS || + e["condition"] === FileNameFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === FileNameFilterCondition.STARTS_WITH || + e["condition"] === FileNameFilterCondition.ENDS_WITH) && + typeof e["isEnabled"] === "boolean" && + typeof e["value"] === "string" && + typeof e["matchWhenPropertyDNE"] === "boolean" && + e["type"] === FilterRuleType.PROPERTY && + e["propertyType"] === PropertyType.LIST && + typeof e["propertyName"] === "string" && + (e["condition"] === ListFilterCondition.CONTAINS || + e["condition"] === ListFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ListFilterCondition.EXISTS || + e["condition"] === ListFilterCondition.DOES_NOT_EXIST) || + (e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["id"] === "string" && + (e["operator"] === "and" || + e["operator"] === "or") && + (e["type"] === FilterRuleType.PROPERTY || + e["type"] === FilterRuleType.FOLDER || + e["type"] === FilterRuleType.FILE_NAME || + e["type"] === FilterRuleType.CONTENT) && + (e["condition"] === TextFilterCondition.IS || + e["condition"] === TextFilterCondition.IS_NOT || + e["condition"] === TextFilterCondition.CONTAINS || + e["condition"] === TextFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === TextFilterCondition.STARTS_WITH || + e["condition"] === TextFilterCondition.ENDS_WITH || + e["condition"] === TextFilterCondition.EXISTS || + e["condition"] === TextFilterCondition.DOES_NOT_EXIST || + e["condition"] === NumberFilterCondition.IS_EQUAL || + e["condition"] === NumberFilterCondition.IS_NOT_EQUAL || + e["condition"] === NumberFilterCondition.IS_GREATER || + e["condition"] === NumberFilterCondition.IS_LESS || + e["condition"] === NumberFilterCondition.IS_GREATER_OR_EQUAL || + e["condition"] === NumberFilterCondition.IS_LESS_OR_EQUAL || + e["condition"] === NumberFilterCondition.EXISTS || + e["condition"] === NumberFilterCondition.DOES_NOT_EXIST || + e["condition"] === DateFilterCondition.IS || + e["condition"] === DateFilterCondition.IS_BEFORE || + e["condition"] === DateFilterCondition.IS_AFTER || + e["condition"] === DateFilterCondition.IS_ON_OR_BEFORE || + e["condition"] === DateFilterCondition.IS_ON_OR_AFTER || + e["condition"] === DateFilterCondition.EXISTS || + e["condition"] === DateFilterCondition.DOES_NOT_EXIST || + e["condition"] === CheckboxFilterCondition.IS || + e["condition"] === CheckboxFilterCondition.IS_NOT || + e["condition"] === CheckboxFilterCondition.EXISTS || + e["condition"] === CheckboxFilterCondition.DOES_NOT_EXIST || + e["condition"] === ListFilterCondition.CONTAINS || + e["condition"] === ListFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ListFilterCondition.EXISTS || + e["condition"] === ListFilterCondition.DOES_NOT_EXIST || + e["condition"] === ContentFilterCondition.CONTAINS || + e["condition"] === ContentFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ContentFilterCondition.IS_EMPTY || + e["condition"] === ContentFilterCondition.IS_NOT_EMPTY || + e["condition"] === FolderFilterCondition.IS || + e["condition"] === FolderFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.IS || + e["condition"] === FileNameFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.CONTAINS || + e["condition"] === FileNameFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === FileNameFilterCondition.STARTS_WITH || + e["condition"] === FileNameFilterCondition.ENDS_WITH) && + typeof e["isEnabled"] === "boolean" && + typeof e["value"] === "string" && + typeof e["matchWhenPropertyDNE"] === "boolean" && + e["type"] === FilterRuleType.PROPERTY && + e["propertyType"] === PropertyType.CHECKBOX && + typeof e["propertyName"] === "string" && + (e["condition"] === CheckboxFilterCondition.IS || + e["condition"] === CheckboxFilterCondition.IS_NOT || + e["condition"] === CheckboxFilterCondition.EXISTS || + e["condition"] === CheckboxFilterCondition.DOES_NOT_EXIST) || + (e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["id"] === "string" && + (e["operator"] === "and" || + e["operator"] === "or") && + (e["type"] === FilterRuleType.PROPERTY || + e["type"] === FilterRuleType.FOLDER || + e["type"] === FilterRuleType.FILE_NAME || + e["type"] === FilterRuleType.CONTENT) && + (e["condition"] === TextFilterCondition.IS || + e["condition"] === TextFilterCondition.IS_NOT || + e["condition"] === TextFilterCondition.CONTAINS || + e["condition"] === TextFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === TextFilterCondition.STARTS_WITH || + e["condition"] === TextFilterCondition.ENDS_WITH || + e["condition"] === TextFilterCondition.EXISTS || + e["condition"] === TextFilterCondition.DOES_NOT_EXIST || + e["condition"] === NumberFilterCondition.IS_EQUAL || + e["condition"] === NumberFilterCondition.IS_NOT_EQUAL || + e["condition"] === NumberFilterCondition.IS_GREATER || + e["condition"] === NumberFilterCondition.IS_LESS || + e["condition"] === NumberFilterCondition.IS_GREATER_OR_EQUAL || + e["condition"] === NumberFilterCondition.IS_LESS_OR_EQUAL || + e["condition"] === NumberFilterCondition.EXISTS || + e["condition"] === NumberFilterCondition.DOES_NOT_EXIST || + e["condition"] === DateFilterCondition.IS || + e["condition"] === DateFilterCondition.IS_BEFORE || + e["condition"] === DateFilterCondition.IS_AFTER || + e["condition"] === DateFilterCondition.IS_ON_OR_BEFORE || + e["condition"] === DateFilterCondition.IS_ON_OR_AFTER || + e["condition"] === DateFilterCondition.EXISTS || + e["condition"] === DateFilterCondition.DOES_NOT_EXIST || + e["condition"] === CheckboxFilterCondition.IS || + e["condition"] === CheckboxFilterCondition.IS_NOT || + e["condition"] === CheckboxFilterCondition.EXISTS || + e["condition"] === CheckboxFilterCondition.DOES_NOT_EXIST || + e["condition"] === ListFilterCondition.CONTAINS || + e["condition"] === ListFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ListFilterCondition.EXISTS || + e["condition"] === ListFilterCondition.DOES_NOT_EXIST || + e["condition"] === ContentFilterCondition.CONTAINS || + e["condition"] === ContentFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ContentFilterCondition.IS_EMPTY || + e["condition"] === ContentFilterCondition.IS_NOT_EMPTY || + e["condition"] === FolderFilterCondition.IS || + e["condition"] === FolderFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.IS || + e["condition"] === FileNameFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.CONTAINS || + e["condition"] === FileNameFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === FileNameFilterCondition.STARTS_WITH || + e["condition"] === FileNameFilterCondition.ENDS_WITH) && + typeof e["isEnabled"] === "boolean" && + typeof e["value"] === "string" && + typeof e["matchWhenPropertyDNE"] === "boolean" && + e["type"] === FilterRuleType.PROPERTY && + (e["propertyType"] === PropertyType.DATE || + e["propertyType"] === PropertyType.DATETIME) && + typeof e["propertyName"] === "string" && + (e["condition"] === DateFilterCondition.IS || + e["condition"] === DateFilterCondition.IS_BEFORE || + e["condition"] === DateFilterCondition.IS_AFTER || + e["condition"] === DateFilterCondition.IS_ON_OR_BEFORE || + e["condition"] === DateFilterCondition.IS_ON_OR_AFTER || + e["condition"] === DateFilterCondition.EXISTS || + e["condition"] === DateFilterCondition.DOES_NOT_EXIST) && + typeof e["valueData"] === "string" || + (e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["id"] === "string" && + (e["operator"] === "and" || + e["operator"] === "or") && + (e["type"] === FilterRuleType.PROPERTY || + e["type"] === FilterRuleType.FOLDER || + e["type"] === FilterRuleType.FILE_NAME || + e["type"] === FilterRuleType.CONTENT) && + (e["condition"] === TextFilterCondition.IS || + e["condition"] === TextFilterCondition.IS_NOT || + e["condition"] === TextFilterCondition.CONTAINS || + e["condition"] === TextFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === TextFilterCondition.STARTS_WITH || + e["condition"] === TextFilterCondition.ENDS_WITH || + e["condition"] === TextFilterCondition.EXISTS || + e["condition"] === TextFilterCondition.DOES_NOT_EXIST || + e["condition"] === NumberFilterCondition.IS_EQUAL || + e["condition"] === NumberFilterCondition.IS_NOT_EQUAL || + e["condition"] === NumberFilterCondition.IS_GREATER || + e["condition"] === NumberFilterCondition.IS_LESS || + e["condition"] === NumberFilterCondition.IS_GREATER_OR_EQUAL || + e["condition"] === NumberFilterCondition.IS_LESS_OR_EQUAL || + e["condition"] === NumberFilterCondition.EXISTS || + e["condition"] === NumberFilterCondition.DOES_NOT_EXIST || + e["condition"] === DateFilterCondition.IS || + e["condition"] === DateFilterCondition.IS_BEFORE || + e["condition"] === DateFilterCondition.IS_AFTER || + e["condition"] === DateFilterCondition.IS_ON_OR_BEFORE || + e["condition"] === DateFilterCondition.IS_ON_OR_AFTER || + e["condition"] === DateFilterCondition.EXISTS || + e["condition"] === DateFilterCondition.DOES_NOT_EXIST || + e["condition"] === CheckboxFilterCondition.IS || + e["condition"] === CheckboxFilterCondition.IS_NOT || + e["condition"] === CheckboxFilterCondition.EXISTS || + e["condition"] === CheckboxFilterCondition.DOES_NOT_EXIST || + e["condition"] === ListFilterCondition.CONTAINS || + e["condition"] === ListFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ListFilterCondition.EXISTS || + e["condition"] === ListFilterCondition.DOES_NOT_EXIST || + e["condition"] === ContentFilterCondition.CONTAINS || + e["condition"] === ContentFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ContentFilterCondition.IS_EMPTY || + e["condition"] === ContentFilterCondition.IS_NOT_EMPTY || + e["condition"] === FolderFilterCondition.IS || + e["condition"] === FolderFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.IS || + e["condition"] === FileNameFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.CONTAINS || + e["condition"] === FileNameFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === FileNameFilterCondition.STARTS_WITH || + e["condition"] === FileNameFilterCondition.ENDS_WITH) && + typeof e["isEnabled"] === "boolean" && + typeof e["value"] === "string" && + typeof e["matchWhenPropertyDNE"] === "boolean" && + e["type"] === FilterRuleType.FOLDER && + (e["condition"] === FolderFilterCondition.IS || + e["condition"] === FolderFilterCondition.IS_NOT) && + typeof e["includeSubfolders"] === "boolean" || + (e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["id"] === "string" && + (e["operator"] === "and" || + e["operator"] === "or") && + (e["type"] === FilterRuleType.PROPERTY || + e["type"] === FilterRuleType.FOLDER || + e["type"] === FilterRuleType.FILE_NAME || + e["type"] === FilterRuleType.CONTENT) && + (e["condition"] === TextFilterCondition.IS || + e["condition"] === TextFilterCondition.IS_NOT || + e["condition"] === TextFilterCondition.CONTAINS || + e["condition"] === TextFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === TextFilterCondition.STARTS_WITH || + e["condition"] === TextFilterCondition.ENDS_WITH || + e["condition"] === TextFilterCondition.EXISTS || + e["condition"] === TextFilterCondition.DOES_NOT_EXIST || + e["condition"] === NumberFilterCondition.IS_EQUAL || + e["condition"] === NumberFilterCondition.IS_NOT_EQUAL || + e["condition"] === NumberFilterCondition.IS_GREATER || + e["condition"] === NumberFilterCondition.IS_LESS || + e["condition"] === NumberFilterCondition.IS_GREATER_OR_EQUAL || + e["condition"] === NumberFilterCondition.IS_LESS_OR_EQUAL || + e["condition"] === NumberFilterCondition.EXISTS || + e["condition"] === NumberFilterCondition.DOES_NOT_EXIST || + e["condition"] === DateFilterCondition.IS || + e["condition"] === DateFilterCondition.IS_BEFORE || + e["condition"] === DateFilterCondition.IS_AFTER || + e["condition"] === DateFilterCondition.IS_ON_OR_BEFORE || + e["condition"] === DateFilterCondition.IS_ON_OR_AFTER || + e["condition"] === DateFilterCondition.EXISTS || + e["condition"] === DateFilterCondition.DOES_NOT_EXIST || + e["condition"] === CheckboxFilterCondition.IS || + e["condition"] === CheckboxFilterCondition.IS_NOT || + e["condition"] === CheckboxFilterCondition.EXISTS || + e["condition"] === CheckboxFilterCondition.DOES_NOT_EXIST || + e["condition"] === ListFilterCondition.CONTAINS || + e["condition"] === ListFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ListFilterCondition.EXISTS || + e["condition"] === ListFilterCondition.DOES_NOT_EXIST || + e["condition"] === ContentFilterCondition.CONTAINS || + e["condition"] === ContentFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ContentFilterCondition.IS_EMPTY || + e["condition"] === ContentFilterCondition.IS_NOT_EMPTY || + e["condition"] === FolderFilterCondition.IS || + e["condition"] === FolderFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.IS || + e["condition"] === FileNameFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.CONTAINS || + e["condition"] === FileNameFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === FileNameFilterCondition.STARTS_WITH || + e["condition"] === FileNameFilterCondition.ENDS_WITH) && + typeof e["isEnabled"] === "boolean" && + typeof e["value"] === "string" && + typeof e["matchWhenPropertyDNE"] === "boolean" && + e["type"] === FilterRuleType.FILE_NAME && + (e["condition"] === FileNameFilterCondition.IS || + e["condition"] === FileNameFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.CONTAINS || + e["condition"] === FileNameFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === FileNameFilterCondition.STARTS_WITH || + e["condition"] === FileNameFilterCondition.ENDS_WITH) || + (e !== null && + typeof e === "object" || + typeof e === "function") && + typeof e["id"] === "string" && + (e["operator"] === "and" || + e["operator"] === "or") && + (e["type"] === FilterRuleType.PROPERTY || + e["type"] === FilterRuleType.FOLDER || + e["type"] === FilterRuleType.FILE_NAME || + e["type"] === FilterRuleType.CONTENT) && + (e["condition"] === TextFilterCondition.IS || + e["condition"] === TextFilterCondition.IS_NOT || + e["condition"] === TextFilterCondition.CONTAINS || + e["condition"] === TextFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === TextFilterCondition.STARTS_WITH || + e["condition"] === TextFilterCondition.ENDS_WITH || + e["condition"] === TextFilterCondition.EXISTS || + e["condition"] === TextFilterCondition.DOES_NOT_EXIST || + e["condition"] === NumberFilterCondition.IS_EQUAL || + e["condition"] === NumberFilterCondition.IS_NOT_EQUAL || + e["condition"] === NumberFilterCondition.IS_GREATER || + e["condition"] === NumberFilterCondition.IS_LESS || + e["condition"] === NumberFilterCondition.IS_GREATER_OR_EQUAL || + e["condition"] === NumberFilterCondition.IS_LESS_OR_EQUAL || + e["condition"] === NumberFilterCondition.EXISTS || + e["condition"] === NumberFilterCondition.DOES_NOT_EXIST || + e["condition"] === DateFilterCondition.IS || + e["condition"] === DateFilterCondition.IS_BEFORE || + e["condition"] === DateFilterCondition.IS_AFTER || + e["condition"] === DateFilterCondition.IS_ON_OR_BEFORE || + e["condition"] === DateFilterCondition.IS_ON_OR_AFTER || + e["condition"] === DateFilterCondition.EXISTS || + e["condition"] === DateFilterCondition.DOES_NOT_EXIST || + e["condition"] === CheckboxFilterCondition.IS || + e["condition"] === CheckboxFilterCondition.IS_NOT || + e["condition"] === CheckboxFilterCondition.EXISTS || + e["condition"] === CheckboxFilterCondition.DOES_NOT_EXIST || + e["condition"] === ListFilterCondition.CONTAINS || + e["condition"] === ListFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ListFilterCondition.EXISTS || + e["condition"] === ListFilterCondition.DOES_NOT_EXIST || + e["condition"] === ContentFilterCondition.CONTAINS || + e["condition"] === ContentFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ContentFilterCondition.IS_EMPTY || + e["condition"] === ContentFilterCondition.IS_NOT_EMPTY || + e["condition"] === FolderFilterCondition.IS || + e["condition"] === FolderFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.IS || + e["condition"] === FileNameFilterCondition.IS_NOT || + e["condition"] === FileNameFilterCondition.CONTAINS || + e["condition"] === FileNameFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === FileNameFilterCondition.STARTS_WITH || + e["condition"] === FileNameFilterCondition.ENDS_WITH) && + typeof e["isEnabled"] === "boolean" && + typeof e["value"] === "string" && + typeof e["matchWhenPropertyDNE"] === "boolean" && + e["type"] === FilterRuleType.CONTENT && + (e["condition"] === ContentFilterCondition.CONTAINS || + e["condition"] === ContentFilterCondition.DOES_NOT_CONTAIN || + e["condition"] === ContentFilterCondition.IS_EMPTY || + e["condition"] === ContentFilterCondition.IS_NOT_EMPTY)) + ) && + typeof e["isEnabled"] === "boolean" && + typeof e["isSticky"] === "boolean" + ) && + (typedObj["views"] !== null && + typeof typedObj["views"] === "object" || + typeof typedObj["views"] === "function") && + (typedObj["views"]["dashboard"] !== null && + typeof typedObj["views"]["dashboard"] === "object" || + typeof typedObj["views"]["dashboard"] === "function") && + typeof typedObj["views"]["dashboard"]["isEnabled"] === "boolean" && + (typedObj["views"]["grid"] !== null && + typeof typedObj["views"]["grid"] === "object" || + typeof typedObj["views"]["grid"] === "function") && + typeof typedObj["views"]["grid"]["isEnabled"] === "boolean" && + (typedObj["views"]["list"] !== null && + typeof typedObj["views"]["list"] === "object" || + typeof typedObj["views"]["list"] === "function") && + typeof typedObj["views"]["list"]["isEnabled"] === "boolean" && + (typedObj["views"]["table"] !== null && + typeof typedObj["views"]["table"] === "object" || + typeof typedObj["views"]["table"] === "function") && + typeof typedObj["views"]["table"]["isEnabled"] === "boolean" && + (typedObj["views"]["feed"] !== null && + typeof typedObj["views"]["feed"] === "object" || + typeof typedObj["views"]["feed"] === "function") && + typeof typedObj["views"]["feed"]["isEnabled"] === "boolean" && + (typedObj["views"]["recommended"] !== null && + typeof typedObj["views"]["recommended"] === "object" || + typeof typedObj["views"]["recommended"] === "function") && + typeof typedObj["views"]["recommended"]["isEnabled"] === "boolean" && + (typedObj["views"]["related"] !== null && + typeof typedObj["views"]["related"] === "object" || + typeof typedObj["views"]["related"] === "function") && + typeof typedObj["views"]["related"]["isEnabled"] === "boolean" && + (typedObj["titleWrapping"] === "normal" || + typedObj["titleWrapping"] === "break-word") && + typeof typedObj["enableClockUpdates"] === "boolean" && + (typedObj["currentView"] === null || + typedObj["currentView"] === TExplorerView.DASHBOARD || + typedObj["currentView"] === TExplorerView.GRID || + typedObj["currentView"] === TExplorerView.LIST || + typedObj["currentView"] === TExplorerView.FEED || + typedObj["currentView"] === TExplorerView.TABLE || + typedObj["currentView"] === TExplorerView.RECOMMENDED || + typedObj["currentView"] === TExplorerView.RELATED) && + typeof typedObj["enableScrollButtons"] === "boolean" && + typeof typedObj["pageSize"] === "number" && + (typedObj["pluginVersion"] === null || + typeof typedObj["pluginVersion"] === "string") && + Array.isArray(typedObj["viewOrder"]) && + typedObj["viewOrder"].every((e: any) => + (e === TExplorerView.DASHBOARD || + e === TExplorerView.GRID || + e === TExplorerView.LIST || + e === TExplorerView.FEED || + e === TExplorerView.TABLE || + e === TExplorerView.RECOMMENDED || + e === TExplorerView.RELATED) + ) && + typeof typedObj["logLevel"] === "string" + ) +} diff --git a/src/types/index.ts b/src/types/index.ts index 5369044..6d4a6a5 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,4 @@ +/** @see {isVaultExplorerPluginSettings} ts-auto-guard:type-guard */ export interface VaultExplorerPluginSettings { properties: { favorite: string; diff --git a/src/types/types-0.3.0.ts b/src/types/types-0.3.3.ts similarity index 95% rename from src/types/types-0.3.0.ts rename to src/types/types-0.3.3.ts index 716c695..9011b25 100644 --- a/src/types/types-0.3.0.ts +++ b/src/types/types-0.3.3.ts @@ -22,6 +22,8 @@ export interface VaultExplorerPluginSettings_0_3_3 { pluginVersion: string | null; } +export { TextFilterCondition as TextFilterCondition_0_3_3 }; + interface TextPropertyFilter extends BasePropertyFilter { condition: TextFilterCondition; value: string; diff --git a/src/types/types-0.5.5.ts b/src/types/types-0.5.5.ts index dbb990a..389d0e5 100644 --- a/src/types/types-0.5.5.ts +++ b/src/types/types-0.5.5.ts @@ -22,6 +22,8 @@ export interface VaultExplorerPluginSettings_0_5_5 { pluginVersion: string | null; } +export { TextFilterCondition as TextFilterCondition_0_5_5, PropertyFilterType as PropertyFilterType_0_5_5, NumberFilterCondition as NumberFilterCondition_0_5_5, ListFilterCondition as ListFilterCondition_0_5_5, CheckboxFilterCondition as CheckboxFilterCondition_0_5_5, DateFilterCondition as DateFilterCondition_0_5_5 }; + export type FilterOperator = "and" | "or"; export enum TextFilterCondition { diff --git a/src/types/types-1.12.1.ts b/src/types/types-1.12.1.ts index ec468fd..682c1eb 100644 --- a/src/types/types-1.12.1.ts +++ b/src/types/types-1.12.1.ts @@ -28,16 +28,18 @@ export interface VaultExplorerPluginSettings_1_12_1 { pluginVersion: string | null; } -export type WordBreak = "normal" | "break-word"; +export { FilterRuleType as FilterRuleType_1_12_1 }; -export enum ViewType { +type WordBreak = "normal" | "break-word"; + +enum ViewType { GRID = "grid", LIST = "list", } -export type FilterOperator = "and" | "or"; +type FilterOperator = "and" | "or"; -export enum TextFilterCondition { +enum TextFilterCondition { IS = "is", IS_NOT = "is-not", CONTAINS = "contains", @@ -48,14 +50,14 @@ export enum TextFilterCondition { DOES_NOT_EXIST = "does-not-exist", } -export enum ListFilterCondition { +enum ListFilterCondition { CONTAINS = "contains", DOES_NOT_CONTAIN = "does-not-contain", EXISTS = "exists", DOES_NOT_EXIST = "does-not-exist", } -export enum NumberFilterCondition { +enum NumberFilterCondition { IS_EQUAL = "is-equal", IS_NOT_EQUAL = "is-not-equal", IS_GREATER = "is-greater", @@ -66,14 +68,14 @@ export enum NumberFilterCondition { DOES_NOT_EXIST = "does-not-exist", } -export enum CheckboxFilterCondition { +enum CheckboxFilterCondition { IS = "is", IS_NOT = "is-not", EXISTS = "exists", DOES_NOT_EXIST = "does-not-exist", } -export enum DateFilterCondition { +enum DateFilterCondition { IS = "is", IS_BEFORE = "is-before", IS_AFTER = "is-after", @@ -83,19 +85,19 @@ export enum DateFilterCondition { DOES_NOT_EXIST = "does-not-exist", } -export enum ContentFilterCondition { +enum ContentFilterCondition { CONTAINS = "contains", DOES_NOT_CONTAIN = "does-not-contain", IS_EMPTY = "is-empty", IS_NOT_EMPTY = "is-not-empty", } -export enum FolderFilterCondition { +enum FolderFilterCondition { IS = "is", IS_NOT = "is-not", } -export enum FileNameFilterCondition { +enum FileNameFilterCondition { IS = "is", IS_NOT = "is-not", CONTAINS = "contains", @@ -104,10 +106,10 @@ export enum FileNameFilterCondition { ENDS_WITH = "ends-with", } -export type FilterCondition = TextFilterCondition | NumberFilterCondition | DateFilterCondition | CheckboxFilterCondition | ListFilterCondition | ContentFilterCondition | FolderFilterCondition | FileNameFilterCondition; +type FilterCondition = TextFilterCondition | NumberFilterCondition | DateFilterCondition | CheckboxFilterCondition | ListFilterCondition | ContentFilterCondition | FolderFilterCondition | FileNameFilterCondition; //This matches the Obsidian property types -export enum PropertyType { +enum PropertyType { TEXT = "text", NUMBER = "number", LIST = "list", @@ -116,14 +118,14 @@ export enum PropertyType { DATETIME = "datetime", } -export enum FilterRuleType { +enum FilterRuleType { PROPERTY = "property", FOLDER = "folder", FILE_NAME = "file-name", CONTENT = "content", } -export enum DatePropertyFilterValue { +enum DatePropertyFilterValue { TODAY = "today", TOMORROW = "tomorrow", YESTERDAY = "yesterday", @@ -144,35 +146,35 @@ interface BaseFilterRule { matchWhenPropertyDNE: boolean; } -export interface TextPropertyFilterRule extends BaseFilterRule { +interface TextPropertyFilterRule extends BaseFilterRule { type: FilterRuleType.PROPERTY; propertyType: PropertyType.TEXT; propertyName: string; condition: TextFilterCondition; } -export interface NumberPropertyFilterRule extends BaseFilterRule { +interface NumberPropertyFilterRule extends BaseFilterRule { type: FilterRuleType.PROPERTY; propertyType: PropertyType.NUMBER; propertyName: string; condition: NumberFilterCondition; } -export interface ListPropertyFilterRule extends BaseFilterRule { +interface ListPropertyFilterRule extends BaseFilterRule { type: FilterRuleType.PROPERTY; propertyType: PropertyType.LIST; propertyName: string; condition: ListFilterCondition; } -export interface CheckboxPropertyFilterRule extends BaseFilterRule { +interface CheckboxPropertyFilterRule extends BaseFilterRule { type: FilterRuleType.PROPERTY; propertyType: PropertyType.CHECKBOX; propertyName: string; condition: CheckboxFilterCondition; } -export interface DatePropertyFilterRule extends BaseFilterRule { +interface DatePropertyFilterRule extends BaseFilterRule { type: FilterRuleType.PROPERTY; propertyType: PropertyType.DATE | PropertyType.DATETIME; propertyName: string; @@ -180,32 +182,32 @@ export interface DatePropertyFilterRule extends BaseFilterRule { valueData: string; } -export interface FolderFilterRule extends BaseFilterRule { +interface FolderFilterRule extends BaseFilterRule { type: FilterRuleType.FOLDER; condition: FolderFilterCondition; includeSubfolders: boolean; } -export interface FileNameFilterRule extends BaseFilterRule { +interface FileNameFilterRule extends BaseFilterRule { type: FilterRuleType.FILE_NAME; condition: FileNameFilterCondition; } -export interface ContentFilterRule extends BaseFilterRule { +interface ContentFilterRule extends BaseFilterRule { type: FilterRuleType.CONTENT; condition: ContentFilterCondition; } -export type FilterRule = PropertyFilterRule | FolderFilterRule | FileNameFilterRule | ContentFilterRule; -export type PropertyFilterRule = TextPropertyFilterRule | NumberPropertyFilterRule | ListPropertyFilterRule | CheckboxPropertyFilterRule | DatePropertyFilterRule; +type FilterRule = PropertyFilterRule | FolderFilterRule | FileNameFilterRule | ContentFilterRule; +type PropertyFilterRule = TextPropertyFilterRule | NumberPropertyFilterRule | ListPropertyFilterRule | CheckboxPropertyFilterRule | DatePropertyFilterRule; -export interface FilterGroup { +interface FilterGroup { id: string; name: string; rules: FilterRule[]; isEnabled: boolean; } -export type SortFilter = "file-name-asc" | "file-name-desc" | "modified-asc" | "modified-desc"; +type SortFilter = "file-name-asc" | "file-name-desc" | "modified-asc" | "modified-desc"; -export type TimestampFilter = "created-today" | "modified-today" | "created-this-week" | "modified-this-week" | "created-2-weeks" | "modified-2-weeks" | "all"; +type TimestampFilter = "created-today" | "modified-today" | "created-this-week" | "modified-this-week" | "created-2-weeks" | "modified-2-weeks" | "all"; diff --git a/tests/integration/preform-migration.test.ts b/tests/integration/preform-migration.test.ts new file mode 100644 index 0000000..1edd199 --- /dev/null +++ b/tests/integration/preform-migration.test.ts @@ -0,0 +1,189 @@ +import { preformMigrations } from "src/migrations"; +import { isVaultExplorerPluginSettings } from "src/types/index.guard"; +import { TextFilterCondition_0_3_3, VaultExplorerPluginSettings_0_3_3 } from "src/types/types-0.3.3"; +import { CheckboxFilterCondition_0_5_5, DateFilterCondition_0_5_5, ListFilterCondition_0_5_5, NumberFilterCondition_0_5_5, PropertyFilterType_0_5_5, TextFilterCondition_0_5_5, VaultExplorerPluginSettings_0_5_5 } from "src/types/types-0.5.5"; + +describe("preformMigrations", () => { + it("should migrate from 0.3.3 to the latest version", () => { + //Arrange + const settingsData: VaultExplorerPluginSettings_0_3_3 = { + "properties": { + "favorite": "note-123", + "url": "https://example.com", + "custom1": "custom-value-1", + "custom2": "custom-value-2", + "custom3": "custom-value-3" + }, + "filters": { + "folder": "MyNotes", + "search": "project", + "onlyFavorites": true, + "sort": "file-name-asc", + "timestamp": "created-today", + "properties": { + "selectedGroupId": "group-1", + "groups": [ + { + "id": "group-1", + "name": "Project Filters", + "filters": [ + { + "id": "filter-1", + "propertyName": "status", + "operator": "and", + "isEnabled": true, + "condition": TextFilterCondition_0_3_3.IS, + "value": "active" + }, + { + "id": "filter-2", + "propertyName": "priority", + "operator": "or", + "isEnabled": false, + "condition": TextFilterCondition_0_3_3.CONTAINS, + "value": "high" + } + ], + "position": 0, + "isEnabled": true + }, + { + "id": "group-2", + "name": "Personal Filters", + "filters": [ + { + "id": "filter-3", + "propertyName": "tag", + "operator": "and", + "isEnabled": true, + "condition": TextFilterCondition_0_3_3.STARTS_WITH, + "value": "work" + }, + { + "id": "filter-4", + "propertyName": "deadline", + "operator": "or", + "isEnabled": true, + "condition": TextFilterCondition_0_3_3.IS_NOT_EMPTY, + "value": "" + } + ], + "position": 1, + "isEnabled": false + } + ] + } + }, + "currentView": "grid", + "pageSize": 20, + "pluginVersion": "0.3.3" + } + + //Same guard clause as in main.js + if (settingsData.pluginVersion === null) return; + + //Act + const result = preformMigrations(settingsData.pluginVersion, settingsData as unknown as Record); + + //Assert + expect(isVaultExplorerPluginSettings(result)).toBe(true); + }); + + it("should migrate from 0.4.0 to the latest version", () => { + //Arrange + const settingsData: VaultExplorerPluginSettings_0_5_5 = { + properties: { + favorite: "yes", + url: "https://example.com", + custom1: "customValue1", + custom2: "customValue2", + custom3: "customValue3" + }, + filters: { + folder: "root", + search: "example search", + onlyFavorites: true, + sort: "file-name-asc", + timestamp: "created-today", + properties: { + selectedGroupId: "group1", + groups: [ + { + id: "group1", + name: "Group 1", + filters: [ + { + id: "filter1", + propertyName: "name", + operator: "and", + type: PropertyFilterType_0_5_5.TEXT, + isEnabled: true, + value: "example", + condition: TextFilterCondition_0_5_5.CONTAINS + }, + { + id: "filter2", + propertyName: "size", + operator: "or", + type: PropertyFilterType_0_5_5.NUMBER, + isEnabled: true, + value: "100", + condition: NumberFilterCondition_0_5_5.IS_GREATER + } + ], + position: 1, + isEnabled: true + }, + { + id: "group2", + name: "Group 2", + filters: [ + { + id: "filter3", + propertyName: "tags", + operator: "and", + type: PropertyFilterType_0_5_5.LIST, + isEnabled: true, + value: "important", + condition: ListFilterCondition_0_5_5.CONTAINS + }, + { + id: "filter4", + propertyName: "isArchived", + operator: "or", + type: PropertyFilterType_0_5_5.CHECKBOX, + isEnabled: true, + value: "true", + condition: CheckboxFilterCondition_0_5_5.IS + }, + { + id: "filter5", + propertyName: "dateCreated", + operator: "and", + type: PropertyFilterType_0_5_5.DATE, + isEnabled: true, + value: "2023-01-01", + condition: DateFilterCondition_0_5_5.IS_AFTER + } + ], + position: 2, + isEnabled: true + } + ] + } + }, + currentView: "list", + pageSize: 20, + pluginVersion: "0.4.0" + }; + + //This the same guard clause as in main.js + if (settingsData.pluginVersion === null) return; + + //Act + const result = preformMigrations(settingsData.pluginVersion, settingsData as unknown as Record); + + //Assert + expect(isVaultExplorerPluginSettings(result)).toBe(true); + }); +}); diff --git a/tests/unit/match-number-property-filter.test.ts b/tests/unit/match-number-property-filter.test.ts index ae070cf..51c6738 100644 --- a/tests/unit/match-number-property-filter.test.ts +++ b/tests/unit/match-number-property-filter.test.ts @@ -156,9 +156,7 @@ describe('matchNumberPropertyFilter', () => { const matchIfNull = false; // Act & Assert - expect(() => matchNumberPropertyFilter(propertyValue, compare, condition, matchIfNull)).toThrow( - `Number filter condition not supported: ${condition}` - ); + expect(() => matchNumberPropertyFilter(propertyValue, compare, condition, matchIfNull)).toThrow() }); }); }); From 9f214fff5d5ae0eebaf29347808d536175db3b1d Mon Sep 17 00:00:00 2001 From: DecafDev <40307803+decaf-dev@users.noreply.github.com> Date: Tue, 25 Jun 2024 20:32:59 -0600 Subject: [PATCH 2/5] fix: don't block plugin UI while waiting for request --- src/main.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.ts b/src/main.ts index 960b656..ece27d7 100644 --- a/src/main.ts +++ b/src/main.ts @@ -22,9 +22,6 @@ export default class VaultExplorerPlugin extends Plugin { await this.loadSettings(); this.setupLogger(); - await loadDeviceId(); - await License.getInstance().verifyLicense(); - this.registerView( VAULT_EXPLORER_VIEW, (leaf) => new VaultExplorerView(leaf, this) @@ -50,6 +47,9 @@ export default class VaultExplorerPlugin extends Plugin { this.layoutReady = true; }); + await loadDeviceId(); + await License.getInstance().verifyLicense(); + } private registerEvents() { From 9db8b7a25bdfc24013b0c911c1e68cfaeec49c89 Mon Sep 17 00:00:00 2001 From: DecafDev <40307803+decaf-dev@users.noreply.github.com> Date: Tue, 25 Jun 2024 20:33:13 -0600 Subject: [PATCH 3/5] fix: allow offline access --- src/svelte/shared/services/license.ts | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/svelte/shared/services/license.ts b/src/svelte/shared/services/license.ts index cd6986f..48cb668 100644 --- a/src/svelte/shared/services/license.ts +++ b/src/svelte/shared/services/license.ts @@ -7,6 +7,8 @@ export const LICENSE_KEY_LENGTH = 8; const LOCAL_STORAGE_LICENSE_KEY = "vault-explorer-license-key"; +const LOCAL_STORAGE_LICENSE_LAST_VALIDATION = "vault-explorer-license-last-validation"; + export default class License { private isDeviceRegistered: boolean; private licenseKey: string; @@ -31,6 +33,7 @@ export default class License { this.isDeviceRegistered = true; this.isDeviceRegisteredStore.set(true); this.setLicenseKey(licenseKey); + this.setLastValidation(true); } return result; } @@ -44,6 +47,7 @@ export default class License { this.isDeviceRegistered = false; this.isDeviceRegisteredStore.set(false); this.setLicenseKey(""); + this.setLastValidation(false); } return result; } @@ -65,6 +69,9 @@ export default class License { if (result) { this.isDeviceRegistered = true; this.isDeviceRegisteredStore.set(true); + this.setLastValidation(true); + } else { + this.setLastValidation(false); } } @@ -88,6 +95,13 @@ export default class License { } catch (err: unknown) { const error = err as Error; Logger.error({ fileName: "license.ts", functionName: "postVerifyDevice", message: "error verifying device" }, error.message); + + if (error.message.contains("net::ERR_INTERNET_DISCONNECTED")) { + const state = License.getInstance().getLastValidationState(); + Logger.debug({ fileName: "license.ts", functionName: "postVerifyDevice", message: "returning last validation state", }, state); + return state; + + } return false; } }; @@ -172,6 +186,7 @@ export default class License { } this.responseMessage = message; + Logger.error({ fileName: "license.ts", functionName: "postUnregisterDevice", message: "error unregistering device" }, error.message); return false; } @@ -182,6 +197,15 @@ export default class License { this.licenseKey = value; } + private setLastValidation(value: boolean) { + localStorage.setItem(LOCAL_STORAGE_LICENSE_LAST_VALIDATION, value.toString()); + } + + getLastValidationState() { + const value = localStorage.getItem(LOCAL_STORAGE_LICENSE_LAST_VALIDATION) ?? ""; + return value === "true"; + } + getIsDeviceRegistered() { return this.isDeviceRegistered; } From 938d8697fd438291dc4efd5244c4cbf4957425c3 Mon Sep 17 00:00:00 2001 From: DecafDev <40307803+decaf-dev@users.noreply.github.com> Date: Tue, 25 Jun 2024 21:11:04 -0600 Subject: [PATCH 4/5] fix: use last device registered value to handle slow network --- src/migrations/migrate_1_17_2.ts | 9 +++ src/svelte/shared/services/license.ts | 81 ++++++++++++++++++--------- 2 files changed, 64 insertions(+), 26 deletions(-) create mode 100644 src/migrations/migrate_1_17_2.ts diff --git a/src/migrations/migrate_1_17_2.ts b/src/migrations/migrate_1_17_2.ts new file mode 100644 index 0000000..bfc1b35 --- /dev/null +++ b/src/migrations/migrate_1_17_2.ts @@ -0,0 +1,9 @@ +import License from "src/svelte/shared/services/license"; +import MigrationInterface from "./migration_interface"; + +export default class Migrate_1_17_2 implements MigrationInterface { + migrate(data: Record) { + License.getInstance().setStoredDeviceRegistered(false); + return data as unknown as Record; + } +} diff --git a/src/svelte/shared/services/license.ts b/src/svelte/shared/services/license.ts index 48cb668..738eb94 100644 --- a/src/svelte/shared/services/license.ts +++ b/src/svelte/shared/services/license.ts @@ -7,7 +7,7 @@ export const LICENSE_KEY_LENGTH = 8; const LOCAL_STORAGE_LICENSE_KEY = "vault-explorer-license-key"; -const LOCAL_STORAGE_LICENSE_LAST_VALIDATION = "vault-explorer-license-last-validation"; +const LOCAL_STORAGE_DEVICE_REGISTERED = "vault-explorer-device-registration"; export default class License { private isDeviceRegistered: boolean; @@ -18,10 +18,16 @@ export default class License { private static instance: License; constructor() { - this.isDeviceRegistered = false; - this.isDeviceRegisteredStore.set(false); + const storedDeviceRegistered = this.getStoredDeviceRegistered(); + this.isDeviceRegistered = storedDeviceRegistered; + this.isDeviceRegisteredStore.set(storedDeviceRegistered); + Logger.debug({ fileName: "license.ts", functionName: "constructor", message: "loaded storedDeviceRegistered", }, storedDeviceRegistered); + this.responseMessage = ""; - this.licenseKey = localStorage.getItem(LOCAL_STORAGE_LICENSE_KEY) ?? ""; + + const storedKey = this.getStoredLicenseKey(); + this.licenseKey = storedKey; + Logger.debug({ fileName: "license.ts", functionName: "constructor", message: "loaded storedKey" }, storedKey); } async registerDevice(licenseKey: string) { @@ -30,10 +36,8 @@ export default class License { const deviceId = readDeviceId(); const result = await this.postRegisterDevice(licenseKey, deviceId); if (result) { - this.isDeviceRegistered = true; - this.isDeviceRegisteredStore.set(true); - this.setLicenseKey(licenseKey); - this.setLastValidation(true); + this.updateDeviceRegistered(true); + this.updateLicenseKey(licenseKey); } return result; } @@ -44,10 +48,8 @@ export default class License { const deviceId = readDeviceId(); const result = await this.postUnregisterDevice(this.licenseKey, deviceId); if (result) { - this.isDeviceRegistered = false; - this.isDeviceRegisteredStore.set(false); - this.setLicenseKey(""); - this.setLastValidation(false); + this.updateLicenseKey(""); + this.updateDeviceRegistered(false); } return result; } @@ -67,11 +69,9 @@ export default class License { const result = await this.postVerifyDevice(this.licenseKey, deviceId); if (result) { - this.isDeviceRegistered = true; - this.isDeviceRegisteredStore.set(true); - this.setLastValidation(true); + this.updateDeviceRegistered(true); } else { - this.setLastValidation(false); + this.updateDeviceRegistered(false); } } @@ -97,9 +97,9 @@ export default class License { Logger.error({ fileName: "license.ts", functionName: "postVerifyDevice", message: "error verifying device" }, error.message); if (error.message.contains("net::ERR_INTERNET_DISCONNECTED")) { - const state = License.getInstance().getLastValidationState(); - Logger.debug({ fileName: "license.ts", functionName: "postVerifyDevice", message: "returning last validation state", }, state); - return state; + const deviceRegistered = License.getInstance().getIsDeviceRegistered(); + Logger.debug({ fileName: "license.ts", functionName: "postVerifyDevice", message: "returning last deviceRegistered state", }, deviceRegistered); + return deviceRegistered; } return false; @@ -192,18 +192,47 @@ export default class License { } } - private setLicenseKey(value: string) { - localStorage.setItem(LOCAL_STORAGE_LICENSE_KEY, value); + /** + * Sets the class licenseKey and updates local storage + * @param value - The license key + */ + private updateLicenseKey(value: string) { + Logger.trace({ filename: "license.ts", functionName: "updateLicenseKey", message: "called" }); this.licenseKey = value; + this.setStoredLicenseKey(value); } - private setLastValidation(value: boolean) { - localStorage.setItem(LOCAL_STORAGE_LICENSE_LAST_VALIDATION, value.toString()); + /** + * Sets the class registration flag and updates local storage + * @param value - The registration status of the device + */ + private updateDeviceRegistered(value: boolean) { + Logger.trace({ filename: "license.ts", functionName: "updateDeviceRegistered", message: "called" }); + this.isDeviceRegistered = value; + this.isDeviceRegisteredStore.set(value); + this.setStoredDeviceRegistered(value); + } + + private setStoredLicenseKey(value: string) { + Logger.trace({ filename: "license.ts", functionName: "setStoredLicenseKey", message: "called" }); + localStorage.setItem(LOCAL_STORAGE_LICENSE_KEY, value); + } + + private getStoredLicenseKey() { + return localStorage.getItem(LOCAL_STORAGE_LICENSE_KEY) ?? "" + } + + private getStoredDeviceRegistered() { + const value = localStorage.getItem(LOCAL_STORAGE_DEVICE_REGISTERED); + if (value) { + return value === "true"; + } + return false; } - getLastValidationState() { - const value = localStorage.getItem(LOCAL_STORAGE_LICENSE_LAST_VALIDATION) ?? ""; - return value === "true"; + setStoredDeviceRegistered(value: boolean) { + Logger.trace({ filename: "license.ts", functionName: "setStoredDeviceRegistered", message: "called" }); + localStorage.setItem(LOCAL_STORAGE_DEVICE_REGISTERED, value.toString()); } getIsDeviceRegistered() { From 135e3cd5212a6a597cdb4f692dc4234cc44eaddc Mon Sep 17 00:00:00 2001 From: DecafDev <40307803+decaf-dev@users.noreply.github.com> Date: Tue, 25 Jun 2024 21:11:09 -0600 Subject: [PATCH 5/5] chore: bump version --- manifest.json | 2 +- package.json | 2 +- versions.json | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index ebfba02..30c4268 100644 --- a/manifest.json +++ b/manifest.json @@ -1,7 +1,7 @@ { "id": "vault-explorer", "name": "Vault Explorer", - "version": "1.17.1", + "version": "1.17.2", "minAppVersion": "1.4.13", "description": "Explore your vault in visual format", "author": "DecafDev", diff --git a/package.json b/package.json index 92f8521..012def3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "obsidian-vault-explorer", - "version": "1.17.1", + "version": "1.17.2", "description": "Explore your vault in visual format", "main": "main.js", "scripts": { diff --git a/versions.json b/versions.json index 1f0c3bd..882a783 100644 --- a/versions.json +++ b/versions.json @@ -74,5 +74,6 @@ "1.15.0": "1.4.13", "1.16.0": "1.4.13", "1.17.0": "1.4.13", - "1.17.1": "1.4.13" + "1.17.1": "1.4.13", + "1.17.2": "1.4.13" }